Merge remote-tracking branch 'upstream/master' into feature/mtrr

This commit is contained in:
Jasmine Iwanek
2023-04-17 23:49:35 -04:00
185 changed files with 16142 additions and 13388 deletions

View File

@@ -67,6 +67,8 @@ AppDir:
- libxcb-shape0 # if QT:BOOL=ON
- libxcb-shm0 # if QT:BOOL=ON
- libxcb-xfixes0 # if QT:BOOL=ON
- libxkbcommon-x11-0 # if QT:BOOL=ON
- qtwayland5 # if QT:BOOL=ON
- zlib1g
files:
exclude:

15
.ci/Jenkinsfile vendored
View File

@@ -78,8 +78,7 @@ def dynarecSlugs = [
]
def presets = [
'Regular',
'Debug'
'Regular'
]
def presetSlugs = [
@@ -284,13 +283,19 @@ pipeline {
def archName = archNames[archSlug]
if (os == 'macOS')
archName = archNamesMac[archSlug]
dir("${dynarecNames[dynarec]}/$os - $archName") {
ret = runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} $buildFlags")
dir(dynarecNames[dynarec]) {
dir("$os - $archName") {
ret = runBuild("-b \"$packageName\" \"$arch\" ${presetFlags[preset]} ${dynarecFlags[dynarec]} ${osFlags[os]} $buildFlags")
if (presets.size() == 1)
writeFile file: '.forcedir', text: ''
}
if ((osArchs.size() == 1) && (thisOsArchs.size() == 1))
writeFile file: '.forcedir', text: ''
}
if (ret == 0) {
/* Archive resulting artifacts. */
archiveArtifacts artifacts: "**/**/$packageName*"
archiveArtifacts artifacts: "**/$packageName*, **/.forcedir", defaultExcludes: false
} else {
/* Fail this stage. */
failStage()

11
.ci/build.sh Normal file → Executable file
View File

@@ -316,6 +316,9 @@ then
pacman -S --needed --noconfirm "$pkg"
done
fi
# Clean pacman cache when running under Jenkins to save disk space.
[ "$CI" = "true" ] && rm -rf /var/cache/pacman/pkg
# Generate a new freetype DLL for this architecture.
rm -f "$freetype_dll"
@@ -584,7 +587,7 @@ else
# ...and the ones we do want listed. Non-dev packages fill missing spots on the list.
libpkgs=""
longest_libpkg=0
for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev
for pkg in libc6-dev libstdc++6 libopenal-dev libfreetype6-dev libx11-dev libsdl2-dev libpng-dev librtmidi-dev qtdeclarative5-dev libwayland-dev libevdev-dev libxkbcommon-x11-dev libglib2.0-dev libslirp-dev libfaudio-dev libaudio-dev libjack-jackd2-dev libpipewire-0.3-dev libsamplerate0-dev libsndio-dev
do
libpkgs="$libpkgs $pkg:$arch_deb"
length=$(echo -n $pkg | sed 's/-dev$//' | sed "s/qtdeclarative/qt/" | wc -c)
@@ -629,7 +632,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(ENV{PKG_CONFIG_PATH} "")
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig")
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/$libdir/pkgconfig:/usr/share/$libdir/pkgconfig:/usr/share/pkgconfig")
include("$(realpath "$toolchain_file")")
EOF
@@ -948,7 +951,6 @@ else
-S "$prefix" -B "$prefix_build" || exit 99
cmake --build "$prefix_build" -j$(nproc) || exit 99
cmake --install "$prefix_build" || exit 99
cp -p "$cwd_root/archive_tmp/usr/bin/fluidsynth" fluidsynth
# Build SDL2 for joystick and FAudio support, with most components
# disabled to remove the dependencies on PulseAudio and libdrm.
@@ -1014,7 +1016,7 @@ else
mkdir -p "$icon_dir"
cp -rp "$icon_size" "$icon_dir/apps"
done
project_icon=$(ls "$icon_base/"[0-9]*x[0-9]*/* | head -1 | grep -oP '/\K([^/]+)(?=\.[^\.]+$)')
project_icon=$(find "$icon_base/"[0-9]*x[0-9]*/* -type f -name '*.png' -o -name '*.svg' | head -1 | grep -oP '/\K([^/]+)(?=\.[^\.]+$)')
# Archive executable, while also stripping it if requested.
mkdir -p archive_tmp/usr/local/bin
@@ -1139,6 +1141,7 @@ EOF
--recipe AppImageBuilder-generated.yml --appdir "$(grep -oP '^\s+path: \K(.+)' AppImageBuilder-generated.yml)"
status=$?
[ $status -eq 0 ] && break
[ $status -eq 127 ] && rm -rf /tmp/appimage_extracted_*
done
# Remove appimage-builder binary on failure, just in case it's corrupted.

View File

@@ -11,3 +11,5 @@ vulkan-headers
MoltenVK
qt5
wget
fluidsynth
ghostscript

0
.ci/static2dll.sh Normal file → Executable file
View File

View File

@@ -252,7 +252,10 @@ jobs:
slug: -Qt
packages: >-
qtbase5-dev
qtbase5-private-dev
qttools5-dev
libevdev-dev
libxkbcommon-x11-dev
steps:
- name: Install dependencies

2
.gitignore vendored
View File

@@ -34,6 +34,8 @@ Makefile
*.tar.*
*.AppImage
/appimage-builder-cache
/appimage-build
/AppImageBuilder-generated.yml
# Visual Studio Code
/.vs

View File

@@ -36,7 +36,7 @@ if [ -z "${romversion}" ]; then
# Get the latest ROM release from the GitHub API.
romversion=$(curl --silent "https://api.github.com/repos/86Box/roms/releases/latest" |
grep '"tag_name":' |
sed -E 's/.*"([^"]+)".*/\1/')
sed -E 's/.*"v([^"]+)".*/\1/')
fi
# Switch to the repository root directory.

View File

@@ -8,8 +8,6 @@
*
* Main emulator module where most things are controlled.
*
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
@@ -150,47 +148,47 @@ uint64_t instru_run_ms = 0;
/* Configuration values. */
int window_remember;
int vid_resize; /* (C) allow resizing */
int invert_display = 0; /* (C) invert the display */
int suppress_overscan = 0; /* (C) suppress overscans */
int scale = 0; /* (C) screen scale factor */
int dpi_scale = 0; /* (C) DPI scaling of the emulated screen */
int vid_api = 0; /* (C) video renderer */
int vid_cga_contrast = 0; /* (C) video */
int video_fullscreen = 0; /* (C) video */
int video_fullscreen_scale = 0; /* (C) video */
int video_fullscreen_first = 0; /* (C) video */
int enable_overscan = 0; /* (C) video */
int force_43 = 0; /* (C) video */
int video_filter_method = 1; /* (C) video */
int video_vsync = 0; /* (C) video */
int video_framerate = -1; /* (C) video */
char video_shader[512] = { '\0' }; /* (C) video */
int vid_resize; /* (C) allow resizing */
int invert_display = 0; /* (C) invert the display */
int suppress_overscan = 0; /* (C) suppress overscans */
int scale = 0; /* (C) screen scale factor */
int dpi_scale = 0; /* (C) DPI scaling of the emulated screen */
int vid_api = 0; /* (C) video renderer */
int vid_cga_contrast = 0; /* (C) video */
int video_fullscreen = 0; /* (C) video */
int video_fullscreen_scale = 0; /* (C) video */
int video_fullscreen_first = 0; /* (C) video */
int enable_overscan = 0; /* (C) video */
int force_43 = 0; /* (C) video */
int video_filter_method = 1; /* (C) video */
int video_vsync = 0; /* (C) video */
int video_framerate = -1; /* (C) video */
char video_shader[512] = { '\0' }; /* (C) video */
bool serial_passthrough_enabled[SERIAL_MAX] = { 0, 0, 0, 0 }; /* (C) activation and kind of pass-through for serial ports */
int bugger_enabled = 0; /* (C) enable ISAbugger */
int postcard_enabled = 0; /* (C) enable POST card */
int isamem_type[ISAMEM_MAX] = { 0, 0, 0, 0 }; /* (C) enable ISA mem cards */
int isartc_type = 0; /* (C) enable ISA RTC card */
int gfxcard[2] = { 0, 0 }; /* (C) graphics/video card */
int show_second_monitors = 1; /* (C) show non-primary monitors */
int sound_is_float = 1; /* (C) sound uses FP values */
int voodoo_enabled = 0; /* (C) video option */
int ibm8514_enabled = 0; /* (C) video option */
int xga_enabled = 0; /* (C) video option */
uint32_t mem_size = 0; /* (C) memory size (Installed on system board)*/
uint32_t isa_mem_size = 0; /* (C) memory size (ISA Memory Cards) */
int cpu_use_dynarec = 0; /* (C) cpu uses/needs Dyna */
int cpu = 0; /* (C) cpu type */
int fpu_type = 0; /* (C) fpu type */
int time_sync = 0; /* (C) enable time sync */
int confirm_reset = 1; /* (C) enable reset confirmation */
int confirm_exit = 1; /* (C) enable exit confirmation */
int confirm_save = 1; /* (C) enable save confirmation */
int enable_discord = 0; /* (C) enable Discord integration */
int pit_mode = -1; /* (C) force setting PIT mode */
int fm_driver = 0; /* (C) select FM sound driver */
int open_dir_usr_path = 0; /* default file open dialog directory of usr_path */
int video_fullscreen_scale_maximized = 0; /* (C) Whether fullscreen scaling settings also apply when maximized. */
int bugger_enabled = 0; /* (C) enable ISAbugger */
int postcard_enabled = 0; /* (C) enable POST card */
int isamem_type[ISAMEM_MAX] = { 0, 0, 0, 0 }; /* (C) enable ISA mem cards */
int isartc_type = 0; /* (C) enable ISA RTC card */
int gfxcard[2] = { 0, 0 }; /* (C) graphics/video card */
int show_second_monitors = 1; /* (C) show non-primary monitors */
int sound_is_float = 1; /* (C) sound uses FP values */
int voodoo_enabled = 0; /* (C) video option */
int ibm8514_enabled = 0; /* (C) video option */
int xga_enabled = 0; /* (C) video option */
uint32_t mem_size = 0; /* (C) memory size (Installed on system board)*/
uint32_t isa_mem_size = 0; /* (C) memory size (ISA Memory Cards) */
int cpu_use_dynarec = 0; /* (C) cpu uses/needs Dyna */
int cpu = 0; /* (C) cpu type */
int fpu_type = 0; /* (C) fpu type */
int time_sync = 0; /* (C) enable time sync */
int confirm_reset = 1; /* (C) enable reset confirmation */
int confirm_exit = 1; /* (C) enable exit confirmation */
int confirm_save = 1; /* (C) enable save confirmation */
int enable_discord = 0; /* (C) enable Discord integration */
int pit_mode = -1; /* (C) force setting PIT mode */
int fm_driver = 0; /* (C) select FM sound driver */
int open_dir_usr_path = 0; /* default file open dialog directory of usr_path */
int video_fullscreen_scale_maximized = 0; /* (C) Whether fullscreen scaling settings also apply when maximized. */
/* Statistics. */
extern int mmuflush;
@@ -898,7 +896,7 @@ pc_init_modules(void)
if (!video_card_available(gfxcard[1])) {
char tempc[512] = { 0 };
device_get_name(video_card_getdevice(gfxcard[1]), 0, tempc);
swprintf(temp, sizeof_w(temp), (wchar_t *) "Video card #2 \"%hs\" is not available due to missing ROMs in the roms/video directory. Disabling the second video card.", tempc);
swprintf(temp, sizeof_w(temp), plat_get_string(IDS_2163), tempc);
ui_msgbox_header(MBX_INFO, (wchar_t *) IDS_2129, temp);
gfxcard[1] = 0;
}
@@ -1265,9 +1263,9 @@ pc_run(void)
startblit();
cpu_exec(cpu_s->rspeed / 100);
#ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */
if (gdbstub_step == GDBSTUB_EXEC)
// if (gdbstub_step == GDBSTUB_EXEC)
#endif
mouse_process();
// mouse_process();
joystick_process();
endblit();

View File

@@ -40,9 +40,9 @@
#include <86box/i2c.h>
#include <86box/video.h>
int acpi_rtc_status = 0;
int acpi_rtc_status = 0;
atomic_int acpi_pwrbut_pressed = 0;
int acpi_enabled = 0;
int acpi_enabled = 0;
static double cpu_to_acpi;
@@ -1520,7 +1520,7 @@ acpi_ali_soft_smi_status_write(acpi_t *dev, uint8_t soft_smi)
}
void
acpi_pwrbtn_timer(void* priv)
acpi_pwrbtn_timer(void *priv)
{
acpi_t *dev = (acpi_t *) priv;

View File

@@ -159,7 +159,15 @@ bin_init(const char *filename, int *error)
tf->get_length = bin_get_length;
tf->close = bin_close;
} else {
free(tf);
/* From the check above, error may still be non-zero if opening a directory.
* The error is set for viso to try and open the directory following this function.
* However, we need to make sure the descriptor is closed. */
if ((tf->file != NULL) && ((stats.st_mode & S_IFMT) == S_IFDIR)) {
/* tf is freed by bin_close */
bin_close(tf);
} else {
free(tf);
}
tf = NULL;
}

View File

@@ -218,7 +218,7 @@ viso_convert_utf8(wchar_t *dest, const char *src, ssize_t buf_size)
return p - dest;
}
#define VISO_WRITE_STR_FUNC(func, dst_type, src_type, converter) \
#define VISO_WRITE_STR_FUNC(func, dst_type, src_type, converter, bounds_chk) \
static void \
func(dst_type *dest, const src_type *src, ssize_t buf_size, int charset) \
{ \
@@ -284,7 +284,7 @@ viso_convert_utf8(wchar_t *dest, const char *src, ssize_t buf_size)
\
default: \
/* Not valid for D or A, but valid for filenames. */ \
if ((charset < VISO_CHARSET_FN) || (c > 0xffff)) \
if ((charset < VISO_CHARSET_FN) || (bounds_chk)) \
c = '_'; \
break; \
} \
@@ -293,8 +293,8 @@ viso_convert_utf8(wchar_t *dest, const char *src, ssize_t buf_size)
*dest++ = converter(c); \
} \
}
VISO_WRITE_STR_FUNC(viso_write_string, uint8_t, char, )
VISO_WRITE_STR_FUNC(viso_write_wstring, uint16_t, wchar_t, cpu_to_be16)
VISO_WRITE_STR_FUNC(viso_write_string, uint8_t, char, , 0)
VISO_WRITE_STR_FUNC(viso_write_wstring, uint16_t, wchar_t, cpu_to_be16, c > 0xffff)
static int
viso_fill_fn_short(char *data, const viso_entry_t *entry, viso_entry_t **entries)

View File

@@ -1,21 +1,23 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
* This file is part of the 86Box distribution.
*
* Implementation of the ALi M1429 chipset.
* Implementation of the ALi M1429 chipset.
*
* Note: This chipset has no datasheet, everything were done via
* reverse engineering the BIOS of various machines using it.
* Note: This chipset has no datasheet, everything were done via
* reverse engineering the BIOS of various machines using it.
*
* Authors: Tiseno100,
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2020,2021 Tiseno100.
* Copyright 2021,2021 Miran Grca.
*
* Authors: Tiseno100,
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2020-2021 Tiseno100.
* Copyright 2021 Miran Grca.
*/
/*
@@ -64,15 +66,14 @@
Register 20h:
Bits 2-1-0: Bus Clock Speed
0 0 0: 7.1519Mhz (ATCLK2)
0 0 1: CLK2IN/4
0 1 0: CLK2IN/5
0 1 1: CLK2IN/6
1 0 0: CLK2IN/8
1 0 1: CLK2IN/10
1 1 0: CLK2IN/12
0 0 1: CLK2IN/4
0 1 0: CLK2IN/5
0 1 1: CLK2IN/6
1 0 0: CLK2IN/8
1 0 1: CLK2IN/10
1 1 0: CLK2IN/12
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -94,13 +95,11 @@
#include <86box/smram.h>
#include <86box/chipset.h>
#define GREEN dev->is_g /* Is G Variant */
#define GREEN dev->is_g /* Is G Variant */
#ifdef ENABLE_ALI1429_LOG
int ali1429_do_log = ENABLE_ALI1429_LOG;
static void
ali1429_log(const char *fmt, ...)
{
@@ -113,27 +112,25 @@ ali1429_log(const char *fmt, ...)
}
}
#else
#define ali1429_log(fmt, ...)
# define ali1429_log(fmt, ...)
#endif
typedef struct
{
uint8_t is_g, index, cfg_locked, reg_57h,
regs[90];
uint8_t is_g, index, cfg_locked, reg_57h,
regs[90];
} ali1429_t;
static void
ali1429_shadow_recalc(ali1429_t *dev)
{
uint32_t base, i, can_write, can_read;
shadowbios = (dev->regs[0x13] & 0x40) && (dev->regs[0x14] & 0x01);
shadowbios = (dev->regs[0x13] & 0x40) && (dev->regs[0x14] & 0x01);
shadowbios_write = (dev->regs[0x13] & 0x40) && (dev->regs[0x14] & 0x02);
can_write = (dev->regs[0x14] & 0x02) ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY;
can_read = (dev->regs[0x14] & 0x01) ? MEM_READ_INTERNAL : MEM_READ_EXTANY;
can_read = (dev->regs[0x14] & 0x01) ? MEM_READ_INTERNAL : MEM_READ_EXTANY;
for (i = 0; i < 8; i++) {
base = 0xc0000 + (i << 15);
@@ -147,149 +144,151 @@ ali1429_shadow_recalc(ali1429_t *dev)
flushmmucache_nopc();
}
static void
ali1429_write(uint16_t addr, uint8_t val, void *priv)
{
ali1429_t *dev = (ali1429_t *)priv;
ali1429_t *dev = (ali1429_t *) priv;
switch (addr) {
case 0x22:
dev->index = val;
break;
case 0x22:
dev->index = val;
break;
case 0x23:
case 0x23:
#ifdef ENABLE_ALI1429_LOG
if (dev->index != 0x03)
ali1429_log("M1429: dev->regs[%02x] = %02x\n", dev->index, val);
if (dev->index != 0x03)
ali1429_log("M1429: dev->regs[%02x] = %02x\n", dev->index, val);
#endif
if (dev->index == 0x03)
dev->cfg_locked = (val != 0xc5);
if (dev->index == 0x03)
dev->cfg_locked = (val != 0xc5);
if (!dev->cfg_locked) {
pclog("M1429: dev->regs[%02x] = %02x\n", dev->index, val);
if (!dev->cfg_locked) {
pclog("M1429: dev->regs[%02x] = %02x\n", dev->index, val);
/* Common M1429 Registers */
switch (dev->index) {
case 0x10: case 0x11:
dev->regs[dev->index] = val;
break;
/* Common M1429 Registers */
switch (dev->index) {
case 0x10:
case 0x11:
dev->regs[dev->index] = val;
break;
case 0x12:
dev->regs[dev->index] = val;
if(val & 4)
mem_remap_top(128);
else
mem_remap_top(0);
break;
case 0x12:
dev->regs[dev->index] = val;
if (val & 4)
mem_remap_top(128);
else
mem_remap_top(0);
break;
case 0x13: case 0x14:
dev->regs[dev->index] = val;
ali1429_shadow_recalc(dev);
break;
case 0x13:
case 0x14:
dev->regs[dev->index] = val;
ali1429_shadow_recalc(dev);
break;
case 0x15: case 0x16:
case 0x17:
dev->regs[dev->index] = val;
break;
case 0x15:
case 0x16:
case 0x17:
dev->regs[dev->index] = val;
break;
case 0x18:
dev->regs[dev->index] = (val & 0x8f) | 0x20;
cpu_cache_ext_enabled = !!(val & 2);
cpu_update_waitstates();
break;
case 0x18:
dev->regs[dev->index] = (val & 0x8f) | 0x20;
cpu_cache_ext_enabled = !!(val & 2);
cpu_update_waitstates();
break;
case 0x19: case 0x1a:
case 0x1e:
dev->regs[dev->index] = val;
break;
case 0x19:
case 0x1a:
case 0x1e:
dev->regs[dev->index] = val;
break;
case 0x20:
dev->regs[dev->index] = val;
case 0x20:
dev->regs[dev->index] = val;
switch(val & 7) {
case 0: case 7: /* Illegal */
cpu_set_isa_speed(7159091);
break;
switch (val & 7) {
case 0:
case 7: /* Illegal */
cpu_set_isa_speed(7159091);
break;
case 1:
cpu_set_isa_speed(cpu_busspeed / 4);
break;
case 1:
cpu_set_isa_speed(cpu_busspeed / 4);
break;
case 2:
cpu_set_isa_speed(cpu_busspeed / 5);
break;
case 2:
cpu_set_isa_speed(cpu_busspeed / 5);
break;
case 3:
cpu_set_isa_speed(cpu_busspeed / 6);
break;
case 3:
cpu_set_isa_speed(cpu_busspeed / 6);
break;
case 4:
cpu_set_isa_speed(cpu_busspeed / 8);
break;
case 4:
cpu_set_isa_speed(cpu_busspeed / 8);
break;
case 5:
cpu_set_isa_speed(cpu_busspeed / 10);
break;
case 5:
cpu_set_isa_speed(cpu_busspeed / 10);
break;
case 6:
cpu_set_isa_speed(cpu_busspeed / 12);
break;
}
break;
case 6:
cpu_set_isa_speed(cpu_busspeed / 12);
break;
}
break;
case 0x21 ... 0x27:
dev->regs[dev->index] = val;
break;
}
case 0x21 ... 0x27:
dev->regs[dev->index] = val;
break;
}
/* M1429G Only Registers */
if (GREEN) {
switch (dev->index) {
case 0x30 ... 0x41:
case 0x43: case 0x45:
case 0x4a:
dev->regs[dev->index] = val;
break;
/* M1429G Only Registers */
if (GREEN) {
switch (dev->index) {
case 0x30 ... 0x41:
case 0x43:
case 0x45:
case 0x4a:
dev->regs[dev->index] = val;
break;
case 0x57:
dev->reg_57h = val;
break;
}
}
}
break;
case 0x57:
dev->reg_57h = val;
break;
}
}
}
break;
}
}
static uint8_t
ali1429_read(uint16_t addr, void *priv)
{
ali1429_t *dev = (ali1429_t *)priv;
uint8_t ret = 0xff;
ali1429_t *dev = (ali1429_t *) priv;
uint8_t ret = 0xff;
if ((addr == 0x23) && (dev->index >= 0x10) && (dev->index <= 0x4a))
ret = dev->regs[dev->index];
ret = dev->regs[dev->index];
else if ((addr == 0x23) && (dev->index == 0x57))
ret = dev->reg_57h;
ret = dev->reg_57h;
else if (addr == 0x22)
ret = dev->index;
ret = dev->index;
return ret;
}
static void
ali1429_close(void *priv)
{
ali1429_t *dev = (ali1429_t *)priv;
ali1429_t *dev = (ali1429_t *) priv;
free(dev);
}
static void
ali1429_defaults(ali1429_t *dev)
{
@@ -308,28 +307,27 @@ ali1429_defaults(ali1429_t *dev)
/* M1429G Default Registers */
if (GREEN) {
dev->regs[0x31] = 0x88;
dev->regs[0x32] = 0xc0;
dev->regs[0x38] = 0xe5;
dev->regs[0x40] = 0xe3;
dev->regs[0x41] = 2;
dev->regs[0x45] = 0x80;
dev->regs[0x31] = 0x88;
dev->regs[0x32] = 0xc0;
dev->regs[0x38] = 0xe5;
dev->regs[0x40] = 0xe3;
dev->regs[0x41] = 2;
dev->regs[0x45] = 0x80;
}
}
static void *
ali1429_init(const device_t *info)
{
ali1429_t *dev = (ali1429_t *)malloc(sizeof(ali1429_t));
ali1429_t *dev = (ali1429_t *) malloc(sizeof(ali1429_t));
memset(dev, 0, sizeof(ali1429_t));
dev->cfg_locked = 1;
GREEN = info->local;
GREEN = info->local;
/* M1429 Ports:
22h Index Port
23h Data Port
22h Index Port
23h Data Port
*/
io_sethandler(0x0022, 0x0002, ali1429_read, NULL, NULL, ali1429_write, NULL, NULL, dev);
@@ -341,29 +339,29 @@ ali1429_init(const device_t *info)
}
const device_t ali1429_device = {
.name = "ALi M1429",
.name = "ALi M1429",
.internal_name = "ali1429",
.flags = 0,
.local = 0,
.init = ali1429_init,
.close = ali1429_close,
.reset = NULL,
.flags = 0,
.local = 0,
.init = ali1429_init,
.close = ali1429_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
.force_redraw = NULL,
.config = NULL
};
const device_t ali1429g_device = {
.name = "ALi M1429G",
.name = "ALi M1429G",
.internal_name = "ali1429g",
.flags = 0,
.local = 1,
.init = ali1429_init,
.close = ali1429_close,
.reset = NULL,
.flags = 0,
.local = 1,
.init = ali1429_init,
.close = ali1429_close,
.reset = NULL,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
.force_redraw = NULL,
.config = NULL
};

View File

@@ -38,138 +38,132 @@
#include <86box/chipset.h>
#include <86box/spd.h>
#define MEM_STATE_SHADOW_R 0x01
#define MEM_STATE_SHADOW_W 0x02
#define MEM_STATE_SMRAM 0x04
#define MEM_STATE_SHADOW_R 0x01
#define MEM_STATE_SHADOW_W 0x02
#define MEM_STATE_SMRAM 0x04
typedef struct
{
uint8_t index, cfg_locked,
regs[16], pci_regs[256];
uint8_t index, cfg_locked,
regs[16], pci_regs[256];
} ali1435_t;
#define ENABLE_ALI1435_LOG 1
#ifdef ENABLE_ALI1435_LOG
int ali1435_do_log = ENABLE_ALI1435_LOG;
static void
ali1435_log(const char *fmt, ...)
{
va_list ap;
if (ali1435_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define ali1435_log(fmt, ...)
# define ali1435_log(fmt, ...)
#endif
/* NOTE: We cheat here. The real ALi M1435 uses a level to edge triggered IRQ converter
when the most siginificant bit is set. We work around that by manipulating the
emulated PIC's ELCR register. */
when the most siginificant bit is set. We work around that by manipulating the
emulated PIC's ELCR register. */
static void
ali1435_update_irqs(ali1435_t *dev, int set)
{
uint8_t val;
int i, reg;
int shift, irq;
int irq_map[8] = { -1, 5, 9, 10, 11, 12, 14, 15 };
pic_t *temp_pic;
int i, reg;
int shift, irq;
int irq_map[8] = { -1, 5, 9, 10, 11, 12, 14, 15 };
pic_t *temp_pic;
for (i = 0; i < 4; i++) {
reg = 0x80 + (i >> 1);
shift = (i & 1) << 2;
val = (dev->pci_regs[reg] >> shift) & 0x0f;
irq = irq_map[val & 0x07];
if (irq == -1)
continue;
temp_pic = (irq >= 8) ? &pic2 : &pic;
irq &= 7;
if (set && (val & 0x08))
temp_pic->elcr |= (1 << irq);
else
temp_pic->elcr &= ~(1 << irq);
reg = 0x80 + (i >> 1);
shift = (i & 1) << 2;
val = (dev->pci_regs[reg] >> shift) & 0x0f;
irq = irq_map[val & 0x07];
if (irq == -1)
continue;
temp_pic = (irq >= 8) ? &pic2 : &pic;
irq &= 7;
if (set && (val & 0x08))
temp_pic->elcr |= (1 << irq);
else
temp_pic->elcr &= ~(1 << irq);
}
}
static void
ali1435_pci_write(int func, int addr, uint8_t val, void *priv)
{
ali1435_t *dev = (ali1435_t *) priv;
int irq, irq_map[8] = { -1, 5, 9, 10, 11, 12, 14, 15 };
int irq, irq_map[8] = { -1, 5, 9, 10, 11, 12, 14, 15 };
ali1435_log("ali1435_write(%02X, %02X, %02X)\n", func, addr, val);
if (func > 0)
return;
return;
if ((addr < 0x04) || (addr == 0x06) || ((addr >= 0x08) && (addr <= 0x0b)))
return;
return;
if ((addr >= 0x0f) && (addr < 0x30))
return;
return;
if ((addr >= 0x34) && (addr < 0x40))
return;
return;
switch (addr) {
/* Dummy PCI Config */
case 0x04:
dev->pci_regs[addr] = (val & 0x7f) | 0x07;
break;
/* Dummy PCI Config */
case 0x04:
dev->pci_regs[addr] = (val & 0x7f) | 0x07;
break;
case 0x05:
dev->pci_regs[addr] = (val & 0x01);
break;
case 0x05:
dev->pci_regs[addr] = (val & 0x01);
break;
/* Dummy PCI Status */
case 0x07:
dev->pci_regs[addr] &= ~(val & 0xb8);
break;
/* Dummy PCI Status */
case 0x07:
dev->pci_regs[addr] &= ~(val & 0xb8);
break;
case 0x80: case 0x81:
dev->pci_regs[addr] = val;
ali1435_update_irqs(dev, 0);
irq = irq_map[val & 0x07];
if (irq >= 0) {
ali1435_log("Set IRQ routing: INT %c -> %02X\n", 0x41 + ((addr & 0x01) << 1), irq);
pci_set_irq_routing(PCI_INTA + ((addr & 0x01) << 1), irq);
} else {
ali1435_log("Set IRQ routing: INT %c -> FF\n", 0x41 + ((addr & 0x01) << 1));
pci_set_irq_routing(PCI_INTA + ((addr & 0x01) << 1), PCI_IRQ_DISABLED);
}
irq = irq_map[(val >> 4) & 0x07];
if (irq >= 0) {
ali1435_log("Set IRQ routing: INT %c -> %02X\n", 0x42 + ((addr & 0x01) << 1), irq);
pci_set_irq_routing(PCI_INTB + ((addr & 0x01) << 1), irq);
} else {
ali1435_log("Set IRQ routing: INT %c -> FF\n", 0x42 + ((addr & 0x01) << 1));
pci_set_irq_routing(PCI_INTB + ((addr & 0x01) << 1), PCI_IRQ_DISABLED);
}
ali1435_update_irqs(dev, 1);
break;
case 0x80:
case 0x81:
dev->pci_regs[addr] = val;
ali1435_update_irqs(dev, 0);
irq = irq_map[val & 0x07];
if (irq >= 0) {
ali1435_log("Set IRQ routing: INT %c -> %02X\n", 0x41 + ((addr & 0x01) << 1), irq);
pci_set_irq_routing(PCI_INTA + ((addr & 0x01) << 1), irq);
} else {
ali1435_log("Set IRQ routing: INT %c -> FF\n", 0x41 + ((addr & 0x01) << 1));
pci_set_irq_routing(PCI_INTA + ((addr & 0x01) << 1), PCI_IRQ_DISABLED);
}
irq = irq_map[(val >> 4) & 0x07];
if (irq >= 0) {
ali1435_log("Set IRQ routing: INT %c -> %02X\n", 0x42 + ((addr & 0x01) << 1), irq);
pci_set_irq_routing(PCI_INTB + ((addr & 0x01) << 1), irq);
} else {
ali1435_log("Set IRQ routing: INT %c -> FF\n", 0x42 + ((addr & 0x01) << 1));
pci_set_irq_routing(PCI_INTB + ((addr & 0x01) << 1), PCI_IRQ_DISABLED);
}
ali1435_update_irqs(dev, 1);
break;
default:
dev->pci_regs[addr] = val;
break;
default:
dev->pci_regs[addr] = val;
break;
}
}
static uint8_t
ali1435_pci_read(int func, int addr, void *priv)
{
ali1435_t *dev = (ali1435_t *) priv;
uint8_t ret;
uint8_t ret;
ret = 0xff;
@@ -181,68 +175,65 @@ ali1435_pci_read(int func, int addr, void *priv)
return ret;
}
static void
ali1435_write(uint16_t addr, uint8_t val, void *priv)
{
ali1435_t *dev = (ali1435_t *)priv;
ali1435_t *dev = (ali1435_t *) priv;
switch (addr) {
case 0x22:
dev->index = val;
break;
case 0x22:
dev->index = val;
break;
case 0x23:
/* #ifdef ENABLE_ALI1435_LOG
if (dev->index != 0x03)
ali1435_log("M1435: dev->regs[%02x] = %02x\n", dev->index, val);
#endif */
case 0x23:
/* #ifdef ENABLE_ALI1435_LOG
if (dev->index != 0x03)
ali1435_log("M1435: dev->regs[%02x] = %02x\n", dev->index, val);
#endif */
if (dev->index == 0x03)
dev->cfg_locked = (val != 0x69);
if (dev->index == 0x03)
dev->cfg_locked = (val != 0x69);
if (!dev->cfg_locked) {
pclog("M1435: dev->regs[%02x] = %02x\n", dev->index, val);
if (!dev->cfg_locked) {
pclog("M1435: dev->regs[%02x] = %02x\n", dev->index, val);
switch (dev->index) {
/* PCI Mechanism select? */
case 0x00:
dev->regs[dev->index] = val;
pclog("PMC = %i\n", val != 0xc8);
pci_set_pmc(val != 0xc8);
break;
switch (dev->index) {
/* PCI Mechanism select? */
case 0x00:
dev->regs[dev->index] = val;
pclog("PMC = %i\n", val != 0xc8);
pci_set_pmc(val != 0xc8);
break;
/* ???? */
case 0x06:
dev->regs[dev->index] = val;
break;
/* ???? */
case 0x06:
dev->regs[dev->index] = val;
break;
/* ???? */
case 0x07:
dev->regs[dev->index] = val;
break;
}
}
break;
/* ???? */
case 0x07:
dev->regs[dev->index] = val;
break;
}
}
break;
}
}
static uint8_t
ali1435_read(uint16_t addr, void *priv)
{
ali1435_t *dev = (ali1435_t *)priv;
uint8_t ret = 0xff;
ali1435_t *dev = (ali1435_t *) priv;
uint8_t ret = 0xff;
if ((addr == 0x23) && (dev->index < 0x10))
ret = dev->regs[dev->index];
ret = dev->regs[dev->index];
else if (addr == 0x22)
ret = dev->index;
ret = dev->index;
return ret;
}
static void
ali1435_reset(void *priv)
{
@@ -258,13 +249,16 @@ ali1435_reset(void *priv)
memset(dev->pci_regs, 0, 256);
dev->pci_regs[0x00] = 0x25; dev->pci_regs[0x01] = 0x10; /*ALi*/
dev->pci_regs[0x02] = 0x35; dev->pci_regs[0x03] = 0x14; /*M1435*/
dev->pci_regs[0x00] = 0x25;
dev->pci_regs[0x01] = 0x10; /*ALi*/
dev->pci_regs[0x02] = 0x35;
dev->pci_regs[0x03] = 0x14; /*M1435*/
dev->pci_regs[0x04] = 0x07;
dev->pci_regs[0x07] = 0x04;
dev->pci_regs[0x0b] = 0x06;
dev->pci_regs[0x80] = 0x80; dev->pci_regs[0x81] = 0x00;
dev->pci_regs[0x80] = 0x80;
dev->pci_regs[0x81] = 0x00;
pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED);
@@ -272,16 +266,14 @@ ali1435_reset(void *priv)
pci_set_irq_routing(PCI_INTD, PCI_IRQ_DISABLED);
}
static void
ali1435_close(void *p)
{
ali1435_t *dev = (ali1435_t *)p;
ali1435_t *dev = (ali1435_t *) p;
free(dev);
}
static void *
ali1435_init(const device_t *info)
{
@@ -291,8 +283,8 @@ ali1435_init(const device_t *info)
dev->cfg_locked = 1;
/* M1435 Ports:
22h Index Port
23h Data Port
22h Index Port
23h Data Port
*/
io_sethandler(0x0022, 0x0002, ali1435_read, NULL, NULL, ali1435_write, NULL, NULL, dev);
@@ -309,15 +301,15 @@ ali1435_init(const device_t *info)
}
const device_t ali1435_device = {
.name = "Intel ALi M1435",
.name = "Intel ALi M1435",
.internal_name = "ali1435",
.flags = DEVICE_PCI,
.local = 0x00,
.init = ali1435_init,
.close = ali1435_close,
.reset = ali1435_reset,
.flags = DEVICE_PCI,
.local = 0x00,
.init = ali1435_init,
.close = ali1435_close,
.reset = ali1435_reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
.force_redraw = NULL,
.config = NULL
};

View File

@@ -180,6 +180,9 @@ ali1489_defaults(ali1489_t *dev)
dev->regs[0x3d] = 0x01;
dev->regs[0x40] = 0x03;
pic_kbd_latch(0x01);
pic_mouse_latch(0x00);
ali1489_shadow_recalc(dev);
cpu_cache_int_enabled = 0;
cpu_cache_ext_enabled = 0;
@@ -295,6 +298,7 @@ ali1489_write(uint16_t addr, uint8_t val, void *priv)
case 0x2a: /* I/O Recovery Register */
dev->regs[dev->index] = val;
pic_mouse_latch(val & 0x80);
break;
case 0x2b: /* Turbo Function Register */

View File

@@ -153,7 +153,8 @@ ali1533_write(int func, int addr, uint8_t val, void *priv)
case 0x41:
/* TODO: Bit 7 selects keyboard controller type:
0 = AT, 1 = PS/2 */
keyboard_at_set_mouse_scan((val & 0x40) ? 1 : 0);
pic_kbd_latch(!!(val & 0x80));
pic_mouse_latch(!!(val & 0x40));
dev->pci_conf[addr] = val & 0xbf;
break;
@@ -454,9 +455,7 @@ ali1533_read(int func, int addr, void *priv)
ret = 0x00;
else {
ret = dev->pci_conf[addr];
if (addr == 0x41)
ret |= (keyboard_at_get_mouse_scan() << 2);
else if (addr == 0x58)
if (addr == 0x58)
ret = (ret & 0xbf) | (dev->ide_dev_enable ? 0x40 : 0x00);
else if ((dev->type == 1) && ((addr >= 0x7c) && (addr <= 0xff)) && !dev->pmu_dev_enable) {
dev->pmu_dev_enable = 1;
@@ -1510,7 +1509,8 @@ ali1543_reset(void *priv)
dev->pci_conf[0x0a] = 0x01;
dev->pci_conf[0x0b] = 0x06;
ali1533_write(0, 0x48, 0x00, dev); // Disables all IRQ's
ali1533_write(0, 0x41, 0x00, dev); /* Disables the keyboard and mouse IRQ latch. */
ali1533_write(0, 0x48, 0x00, dev); /* Disables all IRQ's. */
ali1533_write(0, 0x44, 0x00, dev);
ali1533_write(0, 0x4d, 0x00, dev);
ali1533_write(0, 0x53, 0x00, dev);

View File

@@ -46,38 +46,40 @@ typedef struct ali6117_t {
/* Total size, Bank 0 size, Bank 1 size, Bank 2 size, Bank 3 size. */
static uint32_t ali6117_modes[32][5] = {
{ 1024, 512, 512, 0, 0 },
{ 2048, 512, 512, 512, 512 },
{ 3072, 512, 512, 2048, 0 },
{ 5120, 512, 512, 2048, 2048 },
{ 9216, 512, 512, 8192, 0 },
{ 1024, 1024, 0, 0, 0 },
{ 2048, 1024, 1024, 0, 0 },
{ 4096, 1024, 1024, 2048, 0 },
{ 6144, 1024, 1024, 2048, 2048 },
{ 10240, 1024, 1024, 8192, 0 },
{ 18432, 1024, 1024, 8192, 8192 },
{ 3072, 1024, 2048, 0, 0 },
{ 5120, 1024, 2048, 2048, 0 },
{ 9216, 1024, 8192, 0, 0 },
{ 2048, 2048, 0, 0, 0 },
{ 4096, 2048, 2048, 0, 0 },
{ 6144, 2048, 2048, 2048, 0 },
{ 8192, 2048, 2048, 2048, 2048 },
{ 12288, 2048, 2048, 8192, 0 },
{ 20480, 2048, 2048, 8192, 8192 },
{ 10240, 2048, 8192, 0, 0 },
{ 18432, 2048, 8192, 8192, 0 },
{ 26624, 2048, 8192, 8192, 8192 },
{ 4096, 4096, 0, 0, 0 },
{ 8192, 4096, 4096, 0, 0 },
{ 24576, 4096, 4096, 8192, 8192 },
{ 12288, 4096, 8192, 0, 0 },
{ 8192, 8192, 0, 0, 0 },
{ 16384, 8192, 8192, 0, 0 },
{ 24576, 8192, 8192, 8192, 0 },
{ 32768, 8192, 8192, 8192, 8192 },
{ 65536, 32768, 32768, 0, 0 }
// clang-format off
{ 1024, 512, 512, 0, 0 },
{ 2048, 512, 512, 512, 512 },
{ 3072, 512, 512, 2048, 0 },
{ 5120, 512, 512, 2048, 2048 },
{ 9216, 512, 512, 8192, 0 },
{ 1024, 1024, 0, 0, 0 },
{ 2048, 1024, 1024, 0, 0 },
{ 4096, 1024, 1024, 2048, 0 },
{ 6144, 1024, 1024, 2048, 2048 },
{ 10240, 1024, 1024, 8192, 0 },
{ 18432, 1024, 1024, 8192, 8192 },
{ 3072, 1024, 2048, 0, 0 },
{ 5120, 1024, 2048, 2048, 0 },
{ 9216, 1024, 8192, 0, 0 },
{ 2048, 2048, 0, 0, 0 },
{ 4096, 2048, 2048, 0, 0 },
{ 6144, 2048, 2048, 2048, 0 },
{ 8192, 2048, 2048, 2048, 2048 },
{ 12288, 2048, 2048, 8192, 0 },
{ 20480, 2048, 2048, 8192, 8192 },
{ 10240, 2048, 8192, 0, 0 },
{ 18432, 2048, 8192, 8192, 0 },
{ 26624, 2048, 8192, 8192, 8192 },
{ 4096, 4096, 0, 0, 0 },
{ 8192, 4096, 4096, 0, 0 },
{ 24576, 4096, 4096, 8192, 8192 },
{ 12288, 4096, 8192, 0, 0 },
{ 8192, 8192, 0, 0, 0 },
{ 16384, 8192, 8192, 0, 0 },
{ 24576, 8192, 8192, 8192, 0 },
{ 32768, 8192, 8192, 8192, 8192 },
{ 65536, 32768, 32768, 0, 0 }
// clang-format on
};
#ifdef ENABLE_ALI6117_LOG
@@ -300,6 +302,7 @@ ali6117_reg_write(uint16_t addr, uint8_t val, void *priv)
case 0x36:
val &= 0xf0;
val |= dev->regs[dev->reg_offset];
pic_mouse_latch(val & 0x40);
break;
case 0x37:
@@ -424,6 +427,8 @@ ali6117_reset(void *priv)
/* On-board memory 15-16M is enabled by default. */
mem_set_mem_state_both(0x00f00000, 0x00100000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
ali6117_bank_recalc(dev);
pic_mouse_latch(0x00);
}
}
@@ -473,6 +478,9 @@ ali6117_init(const device_t *info)
}
}
if (!(dev->local & 0x08))
pic_kbd_latch(0x01);
ali6117_reset(dev);
if (!(dev->local & 0x08))

View File

@@ -30,6 +30,7 @@
#include <86box/mem.h>
#include <86box/smram.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/port_92.h>
#include <86box/chipset.h>
@@ -388,6 +389,9 @@ ims8848_init(const device_t *info)
ims8848_reset(dev);
pic_kbd_latch(0x01);
pic_mouse_latch(0x01);
return dev;
}

View File

@@ -31,6 +31,7 @@
#include <86box/mem.h>
#include <86box/smram.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/timer.h>
#include <86box/pit.h>
#include <86box/port_92.h>
@@ -217,6 +218,7 @@ i420ex_write(int func, int addr, uint8_t val, void *priv)
break;
case 0x4e:
dev->regs[addr] = (val & 0xf7);
pic_mouse_latch(!!(val & 0x10));
break;
case 0x50:
dev->regs[addr] = (val & 0x0f);
@@ -387,7 +389,8 @@ i420ex_reset_hard(void *priv)
dev->regs[0x4c] = 0x4d;
dev->regs[0x4e] = 0x03;
/* Bits 2:1 of register 50h are 00 is 25 MHz, and 01 if 33 MHz, 10 and 11 are reserved. */
pic_mouse_latch(0x00);
/* Bits 2:1 of register 50h are 00 is 25 MHz, and 01 if 33 MHz, 10 and 11 are reserved. */
if (cpu_busspeed >= 33333333)
dev->regs[0x50] |= 0x02;
dev->regs[0x51] = 0x80;
@@ -436,6 +439,9 @@ i420ex_reset(void *p)
i420ex_write(0, 0x48, 0x00, p);
/* Disable the PIC mouse latch. */
i420ex_write(0, 0x4e, 0x03, p);
for (i = 0; i < 7; i++)
i420ex_write(0, 0x59 + i, 0x00, p);
@@ -520,6 +526,8 @@ i420ex_init(const device_t *info)
i420ex_reset_hard(dev);
pic_kbd_latch(0x01);
return dev;
}

View File

@@ -512,7 +512,7 @@ piix_write(int func, int addr, uint8_t val, void *priv)
break;
case 0x4e:
fregs[0x4e] = val;
keyboard_at_set_mouse_scan((val & 0x10) ? 1 : 0);
pic_mouse_latch(!!(val & 0x10));
if (dev->type >= 4)
kbc_alias_update_io_mapping(dev);
break;
@@ -1159,9 +1159,7 @@ piix_read(int func, int addr, void *priv)
if ((func <= dev->max_func) || ((func == 1) && (dev->max_func == 0))) {
fregs = (uint8_t *) dev->regs[func];
ret = fregs[addr];
if ((func == 0) && (addr == 0x4e))
ret |= keyboard_at_get_mouse_scan();
else if ((func == 2) && (addr == 0xff))
if ((func == 2) && (addr == 0xff))
ret |= 0xef;
piix_log("PIIX function %i read: %02X from %02X\n", func, ret, addr);
@@ -1277,6 +1275,7 @@ piix_reset_hard(piix_t *dev)
fregs[0x0e] = ((dev->type > 1) || (dev->rev != 2)) ? 0x80 : 0x00;
fregs[0x4c] = 0x4d;
fregs[0x4e] = 0x03;
pic_mouse_latch(0x00);
fregs[0x60] = fregs[0x61] = fregs[0x62] = fregs[0x63] = 0x80;
fregs[0x64] = (dev->type > 3) ? 0x10 : 0x00;
fregs[0x69] = 0x02;
@@ -1446,6 +1445,9 @@ piix_reset(void *p)
piix_write(0, 0xa8, 0x0f, p);
}
/* Disable the PIC mouse latch. */
piix_write(0, 0x4e, 0x03, p);
if (dev->type == 5)
piix_write(0, 0xe1, 0x40, p);
piix_write(1, 0x04, 0x00, p);
@@ -1532,9 +1534,8 @@ piix_speed_changed(void *priv)
timer_on_auto(&dev->fast_off_timer, ((double) cpu_fast_off_val + 1) * dev->fast_off_period);
}
static void
*
piix_init(const device_t *info)
static void *
piix_init(const device_t *info)
{
piix_t *dev = (piix_t *) malloc(sizeof(piix_t));
memset(dev, 0, sizeof(piix_t));
@@ -1680,6 +1681,8 @@ static void
// device_add(&i8254_sec_device);
pic_kbd_latch(0x01);
return dev;
}

View File

@@ -27,6 +27,7 @@
#include <86box/dma.h>
#include <86box/mem.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/timer.h>
#include <86box/pit.h>
#include <86box/port_92.h>
@@ -201,6 +202,7 @@ sio_write(int func, int addr, uint8_t val, void *priv)
case 0x4c:
case 0x4d:
dev->regs[addr] = (val & 0x7f);
pic_mouse_latch(!!(val & 0x10));
break;
case 0x4f:
dev->regs[addr] = val;
@@ -392,6 +394,7 @@ sio_reset_hard(void *priv)
dev->regs[0x4b] = 0x0f;
dev->regs[0x4c] = 0x56;
dev->regs[0x4d] = 0x40;
pic_mouse_latch(0x00);
dev->regs[0x4e] = 0x07;
dev->regs[0x4f] = 0x4f;
dev->regs[0x57] = 0x04;
@@ -444,6 +447,9 @@ sio_reset(void *p)
{
sio_t *dev = (sio_t *) p;
/* Disable the PIC mouse latch. */
sio_write(0, 0x4d, 0x40, p);
sio_write(0, 0x57, 0x04, p);
dma_set_params(1, 0xffffffff);
@@ -538,6 +544,8 @@ sio_init(const device_t *info)
// device_add(&i8254_sec_device);
pic_kbd_latch(0x01);
return dev;
}

View File

@@ -84,16 +84,20 @@ typedef struct scat_t {
} scat_t;
static const uint8_t max_map[32] = {
// clang-format off
0, 1, 1, 1, 2, 3, 4, 8,
4, 8, 12, 16, 20, 24, 28, 32,
0, 5, 9, 13, 6, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
// clang-format om
};
static const uint8_t max_map_sx[32] = {
0, 1, 2, 1, 3, 4, 6, 10,
5, 9, 13, 4, 8, 12, 16, 14,
18, 22, 26, 20, 24, 28, 32, 18,
20, 32, 0, 0, 0, 0, 0, 0
// clang-format off
0, 1, 2, 1, 3, 4, 6, 10,
5, 9, 13, 4, 8, 12, 16, 14,
18, 22, 26, 20, 24, 28, 32, 18,
20, 32, 0, 0, 0, 0, 0, 0
// clang-format om
};
static const uint8_t scatsx_external_is_RAS[33] = {
0, 0, 0, 0, 0, 0, 0, 0,

View File

@@ -29,6 +29,7 @@
#include <86box/dma.h>
#include <86box/mem.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/port_92.h>
#include <86box/hdc_ide.h>
#include <86box/hdc_ide_sff8038i.h>
@@ -725,6 +726,9 @@ sis_5571_init(const device_t *info)
sis_5571_reset(dev);
pic_kbd_latch(0x01);
pic_mouse_latch(0x01);
return dev;
}

View File

@@ -135,89 +135,90 @@ sis_85c50x_smm_recalc(sis_85c50x_t *dev)
static void
sis_85c50x_write(int func, int addr, uint8_t val, void *priv)
{
sis_85c50x_t *dev = (sis_85c50x_t *) priv;
sis_85c50x_t *dev = (sis_85c50x_t *) priv;
sis_85c50x_log("85C501: [W] (%02X, %02X) = %02X\n", func, addr, val);
if (func == 0x00) switch (addr) {
case 0x04: /* Command - low byte */
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0xb4) | (val & 0x4b);
break;
case 0x07: /* Status - high byte */
dev->pci_conf[addr] = ((dev->pci_conf[addr] & 0xf9) & ~(val & 0xf8)) | (val & 0x06);
break;
case 0x50:
dev->pci_conf[addr] = val;
break;
case 0x51: /* Cache */
dev->pci_conf[addr] = val;
cpu_cache_ext_enabled = (val & 0x40);
cpu_update_waitstates();
break;
case 0x52:
dev->pci_conf[addr] = val;
break;
case 0x53: /* Shadow RAM */
case 0x54:
case 0x55:
case 0x56:
dev->pci_conf[addr] = val;
sis_85c50x_shadow_recalc(dev);
if (addr == 0x54)
if (func == 0x00)
switch (addr) {
case 0x04: /* Command - low byte */
dev->pci_conf[addr] = (dev->pci_conf[addr] & 0xb4) | (val & 0x4b);
break;
case 0x07: /* Status - high byte */
dev->pci_conf[addr] = ((dev->pci_conf[addr] & 0xf9) & ~(val & 0xf8)) | (val & 0x06);
break;
case 0x50:
dev->pci_conf[addr] = val;
break;
case 0x51: /* Cache */
dev->pci_conf[addr] = val;
cpu_cache_ext_enabled = (val & 0x40);
cpu_update_waitstates();
break;
case 0x52:
dev->pci_conf[addr] = val;
break;
case 0x53: /* Shadow RAM */
case 0x54:
case 0x55:
case 0x56:
dev->pci_conf[addr] = val;
sis_85c50x_shadow_recalc(dev);
if (addr == 0x54)
sis_85c50x_smm_recalc(dev);
break;
case 0x57:
case 0x58:
case 0x59:
case 0x5a:
case 0x5c:
case 0x5d:
case 0x5e:
case 0x61:
case 0x62:
case 0x63:
case 0x67:
case 0x68:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f:
dev->pci_conf[addr] = val;
break;
case 0x5f:
dev->pci_conf[addr] = val & 0xfe;
break;
case 0x5b:
dev->pci_conf[addr] = val;
break;
case 0x60: /* SMI */
if ((dev->pci_conf[0x68] & 0x01) && !(dev->pci_conf[addr] & 0x02) && (val & 0x02)) {
dev->pci_conf[0x69] |= 0x01;
smi_raise();
}
dev->pci_conf[addr] = val & 0x3e;
break;
case 0x64: /* SMRAM */
case 0x65:
dev->pci_conf[addr] = val;
sis_85c50x_smm_recalc(dev);
break;
case 0x57:
case 0x58:
case 0x59:
case 0x5a:
case 0x5c:
case 0x5d:
case 0x5e:
case 0x61:
case 0x62:
case 0x63:
case 0x67:
case 0x68:
case 0x6a:
case 0x6b:
case 0x6c:
case 0x6d:
case 0x6e:
case 0x6f:
dev->pci_conf[addr] = val;
break;
case 0x5f:
dev->pci_conf[addr] = val & 0xfe;
break;
case 0x5b:
dev->pci_conf[addr] = val;
break;
case 0x60: /* SMI */
if ((dev->pci_conf[0x68] & 0x01) && !(dev->pci_conf[addr] & 0x02) && (val & 0x02)) {
dev->pci_conf[0x69] |= 0x01;
smi_raise();
}
dev->pci_conf[addr] = val & 0x3e;
break;
case 0x64: /* SMRAM */
case 0x65:
dev->pci_conf[addr] = val;
sis_85c50x_smm_recalc(dev);
break;
case 0x66:
dev->pci_conf[addr] = (val & 0x7f);
break;
case 0x69:
dev->pci_conf[addr] &= ~(val);
break;
}
break;
case 0x66:
dev->pci_conf[addr] = (val & 0x7f);
break;
case 0x69:
dev->pci_conf[addr] &= ~(val);
break;
}
}
static uint8_t
sis_85c50x_read(int func, int addr, void *priv)
{
sis_85c50x_t *dev = (sis_85c50x_t *) priv;
uint8_t ret = 0xff;
uint8_t ret = 0xff;
if (func == 0x00)
ret = dev->pci_conf[addr];
@@ -234,41 +235,42 @@ sis_85c50x_sb_write(int func, int addr, uint8_t val, void *priv)
sis_85c50x_log("85C503: [W] (%02X, %02X) = %02X\n", func, addr, val);
if (func == 0x00) switch (addr) {
case 0x04: /* Command */
dev->pci_conf_sb[addr] = val & 0x0f;
break;
case 0x07: /* Status */
dev->pci_conf_sb[addr] &= ~(val & 0x30);
break;
case 0x40: /* BIOS Control Register */
dev->pci_conf_sb[addr] = val & 0x3f;
break;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
/* INTA/B/C/D# Remapping Control Register */
dev->pci_conf_sb[addr] = val & 0x8f;
if (val & 0x80)
pci_set_irq_routing(PCI_INTA + (addr - 0x41), PCI_IRQ_DISABLED);
else
pci_set_irq_routing(PCI_INTA + (addr - 0x41), val & 0xf);
break;
case 0x48: /* ISA Master/DMA Memory Cycle Control Register 1 */
case 0x49: /* ISA Master/DMA Memory Cycle Control Register 2 */
case 0x4a: /* ISA Master/DMA Memory Cycle Control Register 3 */
case 0x4b: /* ISA Master/DMA Memory Cycle Control Register 4 */
dev->pci_conf_sb[addr] = val;
break;
}
if (func == 0x00)
switch (addr) {
case 0x04: /* Command */
dev->pci_conf_sb[addr] = val & 0x0f;
break;
case 0x07: /* Status */
dev->pci_conf_sb[addr] &= ~(val & 0x30);
break;
case 0x40: /* BIOS Control Register */
dev->pci_conf_sb[addr] = val & 0x3f;
break;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
/* INTA/B/C/D# Remapping Control Register */
dev->pci_conf_sb[addr] = val & 0x8f;
if (val & 0x80)
pci_set_irq_routing(PCI_INTA + (addr - 0x41), PCI_IRQ_DISABLED);
else
pci_set_irq_routing(PCI_INTA + (addr - 0x41), val & 0xf);
break;
case 0x48: /* ISA Master/DMA Memory Cycle Control Register 1 */
case 0x49: /* ISA Master/DMA Memory Cycle Control Register 2 */
case 0x4a: /* ISA Master/DMA Memory Cycle Control Register 3 */
case 0x4b: /* ISA Master/DMA Memory Cycle Control Register 4 */
dev->pci_conf_sb[addr] = val;
break;
}
}
static uint8_t
sis_85c50x_sb_read(int func, int addr, void *priv)
{
sis_85c50x_t *dev = (sis_85c50x_t *) priv;
uint8_t ret = 0xff;
uint8_t ret = 0xff;
if (func == 0x00)
ret = dev->pci_conf_sb[addr];

View File

@@ -374,6 +374,9 @@ umc_8886_init(const device_t *info)
umc_8886_reset(dev);
pic_kbd_latch(0x01);
pic_mouse_latch(0x01);
return dev;
}

View File

@@ -59,13 +59,15 @@
/* Most revision numbers (PCI-ISA bridge or otherwise) were lifted from PCI device
listings on forums, as VIA's datasheets are not very helpful regarding those. */
#define VIA_PIPC_586A 0x05862500
#define VIA_PIPC_586B 0x05864700
#define VIA_PIPC_596A 0x05960900
#define VIA_PIPC_596B 0x05962300
#define VIA_PIPC_686A 0x06861400
#define VIA_PIPC_686B 0x06864000
#define VIA_PIPC_8231 0x82311000
#define VIA_PIPC_586A 0x05862500
#define VIA_PIPC_586B 0x05864700
#define VIA_PIPC_596A 0x05960900
#define VIA_PIPC_596B 0x05962300
#define VIA_PIPC_686A 0x06861400
#define VIA_PIPC_686B 0x06864000
#define VIA_PIPC_8231 0x82311000
#define VIA_PIPC_FM_EMULATION 1
enum {
TRAP_DRQ = 0,
@@ -118,7 +120,7 @@ typedef struct _pipc_ {
ide_regs[256],
usb_regs[2][256],
power_regs[256],
ac97_regs[2][256], fmnmi_regs[4];
ac97_regs[2][256], fmnmi_regs[4], fmnmi_status;
sff8038i_t *bm[2];
nvr_t *nvr;
@@ -220,6 +222,9 @@ pipc_reset_hard(void *priv)
dev->pci_isa_regs[0x0b] = 0x06;
dev->pci_isa_regs[0x0e] = 0x80;
pic_kbd_latch(0x01);
pic_mouse_latch(dev->local >= VIA_PIPC_586B);
dev->pci_isa_regs[0x48] = 0x01;
dev->pci_isa_regs[0x4a] = 0x04;
dev->pci_isa_regs[0x4f] = 0x03;
@@ -760,10 +765,10 @@ pipc_fmnmi_handlers(pipc_t *dev, uint8_t modem)
static uint8_t
pipc_fm_read(uint16_t addr, void *priv)
{
#ifdef VIA_PIPC_FM_EMULATION
uint8_t ret = 0x00;
#else
pipc_t *dev = (pipc_t *) priv;
#ifdef VIA_PIPC_FM_EMULATION
uint8_t ret = ((addr & 0x03) == 0x00) ? dev->fmnmi_status : 0x00;
#else
uint8_t ret = dev->sb->opl.read(addr, dev->sb->opl.priv);
#endif
@@ -784,12 +789,26 @@ pipc_fm_write(uint16_t addr, uint8_t val, void *priv)
index port, and only fires NMI/SMI when writing to the data port. */
if (!(addr & 0x01)) {
dev->fmnmi_regs[0x00] = (addr & 0x02) ? 0x02 : 0x01;
dev->fmnmi_regs[0x01] = val;
} else {
dev->fmnmi_regs[0x02] = val;
} else {
dev->fmnmi_regs[0x01] = val;
/* TODO: Probe how real hardware handles OPL timers. This assumed implementation
just sets the relevant interrupt flags as soon as a timer is started. */
if (!(addr & 0x02) && (dev->fmnmi_regs[0x02] == 0x04)) {
if (val & 0x80)
dev->fmnmi_status = 0x00;
if ((val & 0x41) == 0x01)
dev->fmnmi_status |= 0x40;
if ((val & 0x22) == 0x02)
dev->fmnmi_status |= 0x20;
if (dev->fmnmi_status & 0x60)
dev->fmnmi_status |= 0x80;
}
/* Fire NMI/SMI if enabled. */
if (dev->ac97_regs[0][0x48] & 0x01) {
pipc_log("PIPC: Raising %s\n", (dev->ac97_regs[0][0x48] & 0x04) ? "SMI" : "NMI");
if (dev->ac97_regs[0][0x48] & 0x04)
smi_raise();
else
@@ -1047,6 +1066,11 @@ pipc_write(int func, int addr, uint8_t val, void *priv)
break;
case 0x44:
if (dev->local < VIA_PIPC_586B)
pic_mouse_latch(val & 0x01);
break;
case 0x47:
if (val & 0x01)
trc_write(0x0047, (val & 0x80) ? 0x06 : 0x04, NULL);
@@ -1569,6 +1593,9 @@ pipc_reset(void *p)
else
pipc_write(1, 0x40, 0x00, p);
if (dev->local < VIA_PIPC_586B)
pipc_write(0, 0x44, 0x00, p);
pipc_write(0, 0x77, 0x00, p);
}

View File

@@ -857,7 +857,7 @@ load_ports(void)
serial_passthrough_enabled[c] = !!ini_section_get_int(cat, temp, 0);
if (serial_passthrough_enabled[c])
config_log("Serial Port %d: passthrough enabled.\n\n", c+1);
config_log("Serial Port %d: passthrough enabled.\n\n", c + 1);
}
for (c = 0; c < PARALLEL_MAX; c++) {
@@ -2345,7 +2345,7 @@ save_input_devices(void)
} else {
ini_section_delete_var(cat, "tablet_tool_type");
}
ini_delete_section_if_empty(config, cat);
}
@@ -2540,7 +2540,7 @@ save_storage_controllers(void)
ini_section_delete_var(cat, "cdrom_interface");
else
ini_section_set_string(cat, "cdrom_interface",
cdrom_interface_get_internal_name(cdrom_interface_current));
cdrom_interface_get_internal_name(cdrom_interface_current));
if (ide_ter_enabled == 0)
ini_section_delete_var(cat, "ide_ter");

View File

@@ -114,7 +114,7 @@ int isa_cycles, cpu_inited,
cpu_waitstates, cpu_cache_int_enabled, cpu_cache_ext_enabled,
cpu_isa_speed, cpu_pci_speed, cpu_isa_pci_div, cpu_agp_speed, cpu_alt_reset,
cpu_override, cpu_effective, cpu_multi, cpu_16bitbus, cpu_64bitbus, cpu_busspeed,
cpu_override, cpu_effective, cpu_multi, cpu_16bitbus, cpu_64bitbus,
cpu_cyrix_alignment, CPUID,
is186, is_nec,
@@ -138,7 +138,7 @@ uint8_t _cache[2048];
uint64_t cpu_CR4_mask, tsc = 0;
uint64_t pmc[2] = { 0, 0 };
double cpu_dmulti;
double cpu_dmulti, cpu_busspeed;
msr_t msr;

View File

@@ -490,10 +490,11 @@ extern int cpu_override;
extern int cpu_isintel;
extern int cpu_iscyrix;
extern int cpu_16bitbus, cpu_64bitbus;
extern int cpu_busspeed, cpu_pci_speed;
extern int cpu_pci_speed;
extern int cpu_multi;
extern double cpu_dmulti;
extern double fpu_multi;
extern double cpu_busspeed;
extern int cpu_cyrix_alignment; /*Cyrix 5x86/6x86 only has data misalignment
penalties when crossing 8-byte boundaries*/

View File

@@ -29,6 +29,7 @@
#include <86box/device.h>
#include <86box/dma.h>
#include <86box/io.h>
#include <86box/keyboard.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/nmi.h>
@@ -265,9 +266,12 @@ reset_common(int hard)
if (is286) {
loadcs(0xF000);
cpu_state.pc = 0xFFF0;
rammask = cpu_16bitbus ? 0xFFFFFF : 0xFFFFFFFF;
if (is6117)
rammask |= 0x03000000;
if (hard) {
rammask = cpu_16bitbus ? 0xFFFFFF : 0xFFFFFFFF;
if (is6117)
rammask |= 0x03000000;
mem_a20_key = mem_a20_alt = mem_a20_state = 0;
}
}
idt.base = 0;
cpu_state.flags = 2;
@@ -315,6 +319,10 @@ reset_common(int hard)
cache_index = 0;
memset(_tr, 0x00, sizeof(_tr));
memset(_cache, 0x00, sizeof(_cache));
/* If we have an AT or PS/2 keyboard controller, make sure the A20 state
is correct. */
kbc_at_a20_reset();
}
if (!is286)

View File

@@ -92,7 +92,7 @@ device_set_context(device_context_t *c, const device_t *d, int inst)
void *sec, *single_sec;
memset(c, 0, sizeof(device_context_t));
c->dev = d;
c->dev = d;
c->instance = inst;
if (inst) {
sprintf(c->name, "%s #%i", d->name, inst);
@@ -135,7 +135,7 @@ device_context_restore(void)
}
static void *
device_add_common(const device_t *d, const device_t *cd, void *p, void* params, int inst)
device_add_common(const device_t *d, const device_t *cd, void *p, void *params, int inst)
{
void *priv = NULL;
int c;
@@ -203,7 +203,7 @@ device_add(const device_t *d)
}
void *
device_add_parameters(const device_t *d, void* params)
device_add_parameters(const device_t *d, void *params)
{
return device_add_common(d, d, NULL, params, 0);
}
@@ -216,7 +216,7 @@ device_add_ex(const device_t *d, void *priv)
}
void
device_add_ex_parameters(const device_t *d, void* priv, void *params)
device_add_ex_parameters(const device_t *d, void *priv, void *params)
{
device_add_common(d, d, priv, params, 0);
}
@@ -293,7 +293,7 @@ device_cadd_inst_ex(const device_t *d, const device_t *cd, void *priv, int inst)
}
void
device_cadd_inst_ex_parameters(const device_t *d, const device_t *cd, void *priv, int inst, void* params)
device_cadd_inst_ex_parameters(const device_t *d, const device_t *cd, void *priv, int inst, void *params)
{
device_add_common(d, cd, priv, params, inst);
}

View File

@@ -88,7 +88,7 @@
#define ISARTC_A6PAK 3
#define ISARTC_VENDEX 4
#define ISARTC_DEBUG 0
#define ISARTC_DEBUG 0
typedef struct {
const char *name; /* board name */
@@ -572,7 +572,7 @@ isartc_init(const device_t *info)
dev->f_rd, NULL, NULL, dev->f_wr, NULL, NULL, dev);
/* Hook into the NVR backend. */
dev->nvr.fn = (char *)info->internal_name;
dev->nvr.fn = (char *) info->internal_name;
dev->nvr.irq = dev->irq;
if (!is_at)
nvr_init(&dev->nvr);

View File

@@ -66,6 +66,7 @@ static uint8_t
fake_shift_needed(uint16_t scan)
{
switch (scan) {
case 0x137: /* Yes, Print Screen requires the fake shifts. */
case 0x147:
case 0x148:
case 0x149:
@@ -125,9 +126,13 @@ key_process(uint16_t scan, int down)
void
keyboard_input(int down, uint16_t scan)
{
/* Special case for E1 1D, translate it to 0100 - special case. */
if ((scan >> 8) == 0xe1) {
if ((scan & 0xff) == 0x1d)
scan = 0x0100;
/* Translate E0 xx scan codes to 01xx because we use 512-byte arrays for states
and scan code sets. */
if ((scan >> 8) == 0xe0) {
} else if ((scan >> 8) == 0xe0) {
scan &= 0x00ff;
scan |= 0x0100; /* extended key code */
} else if ((scan >> 8) != 0x01)
@@ -295,11 +300,30 @@ keyboard_recv(uint16_t key)
return recv_key[key];
}
/* Do we have Control-Alt-PgDn in the keyboard buffer? */
int
keyboard_isfsenter(void)
{
return ((recv_key[0x01d] || recv_key[0x11d]) && (recv_key[0x038] || recv_key[0x138]) && (recv_key[0x049] || recv_key[0x149]));
}
int
keyboard_isfsenter_down(void)
{
return (!recv_key[0x01d] && !recv_key[0x11d] && !recv_key[0x038] && !recv_key[0x138] && !recv_key[0x049] && !recv_key[0x149]);
}
/* Do we have Control-Alt-PgDn in the keyboard buffer? */
int
keyboard_isfsexit(void)
{
return ((recv_key[0x01D] || recv_key[0x11D]) && (recv_key[0x038] || recv_key[0x138]) && (recv_key[0x051] || recv_key[0x151]));
return ((recv_key[0x01d] || recv_key[0x11d]) && (recv_key[0x038] || recv_key[0x138]) && (recv_key[0x051] || recv_key[0x151]));
}
int
keyboard_isfsexit_down(void)
{
return (!recv_key[0x01d] && !recv_key[0x11d] && !recv_key[0x038] && !recv_key[0x138] && !recv_key[0x051] && !recv_key[0x151]);
}
/* Do we have F8-F12 in the keyboard buffer? */

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,8 @@
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/gdbstub.h>
#include <86box/mouse.h>
typedef struct {
@@ -40,11 +42,12 @@ int mouse_x,
mouse_buttons,
mouse_mode,
mouse_tablet_in_proximity = 0,
tablet_tool_type = 1; /* 0 = Puck/Cursor, 1 = Pen */
tablet_tool_type = 1; /* 0 = Puck/Cursor, 1 = Pen */
double mouse_x_abs,
mouse_y_abs;
mouse_y_abs;
pc_timer_t mouse_timer; /* mouse event timer */
static const device_t mouse_none_device = {
.name = "None",
@@ -76,19 +79,20 @@ static const device_t mouse_internal_device = {
static mouse_t mouse_devices[] = {
// clang-format off
{ &mouse_none_device },
{ &mouse_internal_device },
{ &mouse_logibus_device },
{ &mouse_msinport_device },
{ &mouse_none_device },
{ &mouse_internal_device },
{ &mouse_logibus_device },
{ &mouse_msinport_device },
#if 0
{ &mouse_genibus_device },
{ &mouse_genibus_device },
#endif
{ &mouse_mssystems_device },
{ &mouse_msserial_device },
{ &mouse_ltserial_device },
{ &mouse_ps2_device },
{ &mouse_wacom_device },
{ NULL }
{ &mouse_mssystems_device },
{ &mouse_msserial_device },
{ &mouse_ltserial_device },
{ &mouse_ps2_device },
{ &mouse_wacom_device },
{ &mouse_wacom_artpad_device },
{ NULL }
// clang-format on
};
@@ -141,6 +145,20 @@ mouse_close(void)
mouse_priv = NULL;
mouse_nbut = 0;
mouse_dev_poll = NULL;
timer_stop(&mouse_timer);
}
static void
mouse_timer_poll(void *priv)
{
/* Poll at 255 Hz, maximum supported by PS/2 mic. */
timer_on_auto(&mouse_timer, 1000000.0 / 255.0);
#ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */
if (gdbstub_step == GDBSTUB_EXEC)
#endif
mouse_process();
}
void
@@ -165,6 +183,11 @@ mouse_reset(void)
if (mouse_curr != NULL)
mouse_priv = device_add(mouse_curr);
timer_add(&mouse_timer, mouse_timer_poll, NULL, 0);
/* Poll at 255 Hz, maximum supported by PS/2 mic. */
timer_on_auto(&mouse_timer, 1000000.0 / 255.0);
}
/* Callback from the hardware driver. */

View File

@@ -82,18 +82,27 @@ mouse_clear_data(void *priv)
}
static void
ps2_report_coordinates(mouse_t *dev)
ps2_report_coordinates(mouse_t *dev, int cmd)
{
uint8_t buff[3] = { 0x08, 0x00, 0x00 };
int temp_z;
if (dev->x > 255)
if (dev->x > 255) {
dev->x = 255;
if (dev->x < -256)
buff[0] |= 0x40;
}
if (dev->x < -256) {
dev->x = -256;
if (dev->y > 255)
buff[0] |= 0x40;
}
if (dev->y > 255) {
dev->y = 255;
if (dev->y < -256)
buff[0] |= 0x80;
}
if (dev->y < -256) {
dev->y = -256;
buff[0] |= 0x80;
}
if (dev->z < -8)
dev->z = -8;
if (dev->z > 7)
@@ -114,19 +123,31 @@ ps2_report_coordinates(mouse_t *dev)
buff[1] = (dev->x & 0xff);
buff[2] = (dev->y & 0xff);
keyboard_at_adddata_mouse(buff[0]);
keyboard_at_adddata_mouse(buff[1]);
keyboard_at_adddata_mouse(buff[2]);
if (cmd) {
keyboard_at_adddata_mouse_cmd(buff[0]);
keyboard_at_adddata_mouse_cmd(buff[1]);
keyboard_at_adddata_mouse_cmd(buff[2]);
} else {
keyboard_at_adddata_mouse(buff[0]);
keyboard_at_adddata_mouse(buff[1]);
keyboard_at_adddata_mouse(buff[2]);
}
if (dev->flags & FLAG_INTMODE) {
int temp_z = dev->z;
temp_z = dev->z & 0x0f;
if ((dev->flags & FLAG_5BTN)) {
temp_z &= 0xF;
if (mouse_buttons & 8)
temp_z |= 0x10;
if (mouse_buttons & 16)
temp_z |= 0x20;
} else {
/* The wheel coordinate is sign-extended. */
if (temp_z & 0x08)
temp_z |= 0xf0;
}
keyboard_at_adddata_mouse(temp_z);
if (cmd)
keyboard_at_adddata_mouse_cmd(temp_z);
else
keyboard_at_adddata_mouse(temp_z);
}
dev->x = dev->y = dev->z = 0;
@@ -147,16 +168,16 @@ ps2_write(uint8_t val, void *priv)
switch (dev->command) {
case 0xe8: /* set mouse resolution */
dev->resolution = val;
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
break;
case 0xf3: /* set sample rate */
dev->sample_rate = val;
keyboard_at_adddata_mouse(0xfa); /* Command response */
keyboard_at_adddata_mouse_cmd(0xfa); /* Command response */
break;
default:
keyboard_at_adddata_mouse(0xfc);
keyboard_at_adddata_mouse_cmd(0xfc);
}
} else {
dev->command = val;
@@ -164,21 +185,21 @@ ps2_write(uint8_t val, void *priv)
switch (dev->command) {
case 0xe6: /* set scaling to 1:1 */
dev->flags &= ~FLAG_SCALED;
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
break;
case 0xe7: /* set scaling to 2:1 */
dev->flags |= FLAG_SCALED;
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
break;
case 0xe8: /* set mouse resolution */
dev->flags |= FLAG_CTRLDAT;
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
break;
case 0xe9: /* status request */
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
temp = (dev->flags & 0x30);
if (mouse_buttons & 1)
temp |= 4;
@@ -186,46 +207,46 @@ ps2_write(uint8_t val, void *priv)
temp |= 1;
if ((mouse_buttons & 4) && (dev->flags & FLAG_INTELLI))
temp |= 2;
keyboard_at_adddata_mouse(temp);
keyboard_at_adddata_mouse(dev->resolution);
keyboard_at_adddata_mouse(dev->sample_rate);
keyboard_at_adddata_mouse_cmd(temp);
keyboard_at_adddata_mouse_cmd(dev->resolution);
keyboard_at_adddata_mouse_cmd(dev->sample_rate);
break;
case 0xea: /* set stream */
dev->flags &= ~FLAG_CTRLDAT;
mouse_scan = 1;
keyboard_at_adddata_mouse(0xfa); /* ACK for command byte */
keyboard_at_adddata_mouse_cmd(0xfa); /* ACK for command byte */
break;
case 0xeb: /* Get mouse data */
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
ps2_report_coordinates(dev);
ps2_report_coordinates(dev, 1);
break;
case 0xf2: /* read ID */
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
if (dev->flags & FLAG_INTMODE)
keyboard_at_adddata_mouse((dev->flags & FLAG_5BTN) ? 0x04 : 0x03);
keyboard_at_adddata_mouse_cmd((dev->flags & FLAG_5BTN) ? 0x04 : 0x03);
else
keyboard_at_adddata_mouse(0x00);
keyboard_at_adddata_mouse_cmd(0x00);
break;
case 0xf3: /* set command mode */
dev->flags |= FLAG_CTRLDAT;
keyboard_at_adddata_mouse(0xfa); /* ACK for command byte */
keyboard_at_adddata_mouse_cmd(0xfa); /* ACK for command byte */
break;
case 0xf4: /* enable */
dev->flags |= FLAG_ENABLED;
mouse_scan = 1;
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
break;
case 0xf5: /* disable */
dev->flags &= ~FLAG_ENABLED;
mouse_scan = 0;
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
break;
case 0xf6: /* set defaults */
@@ -235,15 +256,15 @@ mouse_reset:
dev->flags &= 0x88;
mouse_scan = 1;
keyboard_at_mouse_reset();
keyboard_at_adddata_mouse(0xfa);
keyboard_at_adddata_mouse_cmd(0xfa);
if (dev->command == 0xff) {
keyboard_at_adddata_mouse(0xaa);
keyboard_at_adddata_mouse(0x00);
keyboard_at_adddata_mouse_cmd(0xaa);
keyboard_at_adddata_mouse_cmd(0x00);
}
break;
default:
keyboard_at_adddata_mouse(0xfe);
keyboard_at_adddata_mouse_cmd(0xfe);
}
}
@@ -253,16 +274,15 @@ mouse_reset:
dev->last_data[5] = val;
if (dev->last_data[0] == 0xf3 && dev->last_data[1] == 0xc8
&& dev->last_data[2] == 0xf3 && dev->last_data[3] == 0xc8
&& dev->last_data[4] == 0xf3 && dev->last_data[5] == 0x50
&& mouse_get_buttons() == 5) {
dev->flags |= FLAG_INTMODE | FLAG_5BTN;
} else if (dev->last_data[0] == 0xf3 && dev->last_data[1] == 0xc8
&& dev->last_data[2] == 0xf3 && dev->last_data[3] == 0x64
&& dev->last_data[4] == 0xf3 && dev->last_data[5] == 0x50) {
if ((dev->last_data[0] == 0xf3) && (dev->last_data[1] == 0xc8) &&
(dev->last_data[2] == 0xf3) && (dev->last_data[3] == 0x64) &&
(dev->last_data[4] == 0xf3) && (dev->last_data[5] == 0x50))
dev->flags |= FLAG_INTMODE;
}
if ((dev->flags & FLAG_INTMODE) && (dev->last_data[0] == 0xf3) && (dev->last_data[1] == 0xc8) &&
(dev->last_data[2] == 0xf3) && (dev->last_data[3] == 0xc8) &&
(dev->last_data[4] == 0xf3) && (dev->last_data[5] == 0x50))
dev->flags |= FLAG_5BTN;
}
}
@@ -285,10 +305,14 @@ ps2_poll(int x, int y, int z, int b, double abs_x, double abs_y, void *priv)
dev->x += x;
dev->y -= y;
dev->z -= z;
#if 0
if ((dev->mode == MODE_STREAM) && (dev->flags & FLAG_ENABLED) && (keyboard_at_mouse_pos() < 13)) {
#else
if ((dev->mode == MODE_STREAM) && (keyboard_at_mouse_pos() < 13)) {
#endif
dev->b = b;
ps2_report_coordinates(dev);
ps2_report_coordinates(dev, 0);
}
return (0);

View File

@@ -511,7 +511,7 @@ sermouse_command_timer(void *priv)
}
static int
sermouse_poll(int x, int y, int z, int b, double abs_x, double abs_y, void *priv)
sermouse_poll(int x, int y, int z, int b, double abs_x, double abs_y, void *priv)
{
mouse_t *dev = (mouse_t *) priv;

View File

@@ -9,45 +9,123 @@
#include <86box/mouse.h>
#include <86box/serial.h>
#include <86box/plat.h>
#include <86box/fifo8.h>
#define FLAG_3BTN 0x20 /* enable 3-button mode */
#define FLAG_3BTN 0x20 /* enable 3-button mode */
enum wacom_modes
{
enum wacom_modes {
WACOM_MODE_SUPPRESSED = 0,
WACOM_MODE_POINT = 1,
WACOM_MODE_STREAM = 2,
WACOM_MODE_SWITCH = 3,
};
enum wacom_handshake_modes {
WACOM_HANDSHAKE_NONE = 0,
WACOM_HANDSHAKE_CTS = 1,
WACOM_HANDSHAKE_DTS = 2,
WACOM_HANDSHAKE_BOTH = 3,
};
enum wacom_cmd_set {
WACOM_CMDSET_BITPAD = 0,
WACOM_CMDSET_MM1201 = 1,
WACOM_CMDSET_IIS = 2,
WACOM_CMDSET_IV = 3
};
enum wacom_tablet_type {
WACOM_TYPE_IISONLY = 0,
WACOM_TYPE_IV,
};
enum {
REPORT_PHASE_PREPARE,
REPORT_PHASE_TRANSMIT
};
typedef struct wacom_tablet_id {
char id[64];
int type;
} wacom_tablet_id;
static const wacom_tablet_id sd510_id = {
.id = "~#SD51C V3.2.1.01\r",
.type = WACOM_TYPE_IISONLY
};
static const wacom_tablet_id artpad_id = {
.id = "~#KT-0405-R00 V1.1-0\r",
.type = WACOM_TYPE_IV
};
static const uint32_t wacom_resolution_values[4] = {
500,
508,
1000,
1270
};
typedef struct {
const char *name; /* name of this device */
int8_t type, /* type of this device */
port;
uint8_t flags, but, /* device flags */
status, format,
data_len, data[64],
status, bits,
data_rec[0x200];
int abs_x, abs_y,
rel_x, rel_y,
oldb, b;
int data_pos, data_rec_pos, mode, transmission_ongoing, transmission_format, interval;
Fifo8 data;
int data_rec_pos, mode, interval;
int increment, suppressed_increment;
int transmission_stopped;
int reset;
int transmit_id, transmit_id_pending;
int pressure_mode;
int suppressed, measurement, always_report;
int remote_req, remote_mode;
int suppressed, measurement;
int remote_req;
uint32_t x_res, y_res;
const wacom_tablet_id* tablet_type;
int last_abs_x, last_abs_y; /* Suppressed/Increment Mode. */
uint32_t settings; /* Settings DWORD */
union {
uint32_t settings; /* Settings DWORD */
/* We don't target any architectures except x86/x64/ARM32/ARM64.
(The ABIs for those are explicit in little-endian bit ordering) */
struct {
uint8_t remote_mode : 1;
uint8_t bitpad_two_cursor_data : 1;
uint8_t mm961_orientation : 1;
uint8_t mm_command_set : 1;
uint8_t tilt : 1;
uint8_t multi_device : 1;
uint8_t reading_height : 1;
uint8_t pressure_sensitivity : 1;
uint8_t pnp : 1; /* Unused. */
uint8_t dummy : 1;
uint8_t terminator : 2;
uint8_t out_of_range_data : 1;
uint8_t origin_location : 1;
uint8_t resolution : 2;
uint8_t transfer_rate : 2;
uint8_t coord_sys : 1;
uint8_t output_format : 1;
uint8_t transfer_mode : 2;
uint8_t handshake : 2;
uint8_t stop_bits_conf : 1;
uint8_t data_bits_conf : 1;
uint8_t parity : 2;
uint8_t baud_rate : 2;
uint8_t cmd_set : 2;
} settings_bits;
};
double transmit_period;
double old_tsc, reset_tsc;
@@ -56,12 +134,22 @@ typedef struct {
serial_t *serial;
} mouse_wacom_t;
static unsigned int
reverse(register unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return ((x >> 16) | (x << 16));
}
static double
wacom_transmit_period(mouse_wacom_t *dev, int bps, int rps)
{
double dbps = (double) bps;
double temp = 0.0;
int word_len = 10;
double dbps = (double) bps;
double temp = 0.0;
int word_len = dev->bits;
if (rps == -1)
temp = (double) word_len;
@@ -76,32 +164,105 @@ wacom_transmit_period(mouse_wacom_t *dev, int bps, int rps)
}
static void
wacom_reset(mouse_wacom_t* wacom)
wacom_process_settings_dword(mouse_wacom_t *wacom, uint32_t dword)
{
wacom->transmit_period = wacom_transmit_period(wacom, 9600, -1);
wacom->mode = WACOM_MODE_POINT;
wacom->data_pos = 0;
wacom->transmission_ongoing = 0;
wacom->mode = 0;
wacom->transmission_stopped = 0;
wacom->interval = 0;
wacom->transmit_id = 0;
wacom->format = 0; /* ASCII */
wacom->measurement = 1;
wacom->increment = wacom->suppressed_increment = 0;
wacom->reset_tsc = tsc;
wacom->remote_mode = wacom->remote_req = 0;
wacom->always_report = 0;
wacom->settings = dword;
wacom->mode = wacom->settings_bits.transfer_mode;
wacom->bits = 1 + 7 + wacom->settings_bits.data_bits_conf;
wacom->bits += 1 + wacom->settings_bits.stop_bits_conf;
if (wacom->settings_bits.parity == 2 && !(wacom->bits % 2)) {
wacom->bits++;
} else if (wacom->settings_bits.parity == 3 && (wacom->bits % 2)) {
wacom->bits++;
}
switch(wacom->settings_bits.baud_rate) {
case 0:
wacom->transmit_period = wacom_transmit_period(wacom, 2400, -1);
break;
case 1:
wacom->transmit_period = wacom_transmit_period(wacom, 4800, -1);
break;
case 2:
wacom->transmit_period = wacom_transmit_period(wacom, 9600, -1);
break;
case 3:
wacom->transmit_period = wacom_transmit_period(wacom, 19200, -1);
break;
}
mouse_mode = !wacom->settings_bits.coord_sys;
wacom->x_res = wacom->y_res = wacom_resolution_values[wacom->settings_bits.resolution];
}
static void
wacom_reset(mouse_wacom_t *wacom)
{
wacom->transmit_period = wacom_transmit_period(wacom, 9600, -1);
wacom->mode = WACOM_MODE_POINT;
wacom->transmission_stopped = 0;
wacom->interval = 0;
wacom->transmit_id = 0;
wacom->settings_bits.output_format = 1; /* ASCII */
wacom->settings_bits.cmd_set = 1;
wacom->measurement = 1;
wacom->increment = wacom->suppressed_increment = 0;
wacom->reset_tsc = tsc;
wacom->settings_bits.remote_mode = wacom->remote_req = 0;
wacom->settings_bits.out_of_range_data = 0;
mouse_mode = 1;
wacom_process_settings_dword(wacom, 0xA21BC800);
}
static void
wacom_reset_artpad(mouse_wacom_t *wacom)
{
wacom->transmit_period = wacom_transmit_period(wacom, 9600, -1);
wacom->mode = WACOM_MODE_SUPPRESSED;
wacom->transmission_stopped = 0;
wacom->interval = 0;
wacom->transmit_id = 0;
wacom->settings_bits.output_format = 0; /* Binary */
wacom->measurement = 1;
wacom->increment = 0;
wacom->suppressed_increment = 1;
wacom->reset_tsc = tsc;
wacom->settings_bits.remote_mode = 0;
wacom->remote_req = 0;
wacom->settings_bits.out_of_range_data = 0;
wacom_process_settings_dword(wacom, 0xE203C000);
mouse_mode = 1;
}
static void
wacom_callback(struct serial_s *serial, void *priv)
{
mouse_wacom_t* wacom = (mouse_wacom_t*)priv;
mouse_wacom_t *wacom = (mouse_wacom_t *) priv;
wacom->transmit_period = wacom_transmit_period(wacom, 9600, -1);
switch(wacom->settings_bits.baud_rate) {
case 0:
wacom->transmit_period = wacom_transmit_period(wacom, 2400, -1);
break;
case 1:
wacom->transmit_period = wacom_transmit_period(wacom, 4800, -1);
break;
case 2:
wacom->transmit_period = wacom_transmit_period(wacom, 9600, -1);
break;
case 3:
wacom->transmit_period = wacom_transmit_period(wacom, 19200, -1);
break;
}
timer_stop(&wacom->report_timer);
timer_on_auto(&wacom->report_timer, wacom->transmit_period);
}
@@ -109,8 +270,8 @@ wacom_callback(struct serial_s *serial, void *priv)
static void
wacom_write(struct serial_s *serial, void *priv, uint8_t data)
{
mouse_wacom_t* wacom = (mouse_wacom_t*)priv;
static int special_command = 0;
mouse_wacom_t *wacom = (mouse_wacom_t *) priv;
static int special_command = 0;
if (data == '~') {
special_command = 1;
@@ -119,18 +280,34 @@ wacom_write(struct serial_s *serial, void *priv, uint8_t data)
if (special_command) {
switch (data) {
case '#':
{
if (!wacom->transmission_ongoing) wacom->transmit_id++;
break;
}
{
wacom->transmit_id = 1;
break;
}
case 'C':
case '*':
case 'R':
{
wacom->data_rec[wacom->data_rec_pos++] = '~';
wacom->data_rec[wacom->data_rec_pos++] = data;
break;
}
}
special_command = 0;
return;
}
if (data == '@') {
wacom->remote_req = 1;
wacom->remote_mode = 1;
wacom->remote_req = 1;
wacom->settings_bits.remote_mode = 1;
return;
}
if (data == '#' && wacom->tablet_type->type == WACOM_TYPE_IV) {
wacom_reset_artpad(wacom);
return;
}
if (data == '$') {
wacom_reset(wacom);
return;
}
if (data == 0x13) {
@@ -139,48 +316,84 @@ wacom_write(struct serial_s *serial, void *priv, uint8_t data)
}
if (data == 0x11) {
wacom->transmission_stopped = 0;
wacom->remote_mode = wacom->remote_req = 0;
wacom->settings_bits.remote_mode = wacom->remote_req = 0;
return;
}
wacom->data_rec[wacom->data_rec_pos++] = data;
if (data == '\r' || data == '\n') {
wacom->data_rec[wacom->data_rec_pos] = 0;
wacom->data_rec_pos = 0;
wacom->data_rec[wacom->data_rec_pos] = 0;
wacom->data_rec_pos = 0;
if (data == '\n') pclog("Wacom: written %s", wacom->data_rec);
else pclog("Wacom: written %s\n", wacom->data_rec);
if (!memcmp(wacom->data_rec, "AS", 2)) {
wacom->format = (wacom->data_rec[2] == '1');
wacom->transmission_ongoing = 0;
if (data == '\n')
pclog("Wacom: written %s", wacom->data_rec);
else
pclog("Wacom: written %s\n", wacom->data_rec);
if (!memcmp(wacom->data_rec, "AS", 2) && wacom->settings_bits.cmd_set == WACOM_CMDSET_IIS) {
wacom->settings_bits.output_format = !(wacom->data_rec[2] == '1');
} else if (!memcmp(wacom->data_rec, "SR", 2)) {
wacom->mode = WACOM_MODE_STREAM;
wacom->suppressed_increment = 0;
wacom->mode = WACOM_MODE_STREAM;
} else if (!memcmp(wacom->data_rec, "IN", 2)) {
sscanf((const char*)wacom->data_rec, "IN%d", &wacom->increment);
} else if (!memcmp(wacom->data_rec, "RE", 2) || wacom->data_rec[0] == '$' || wacom->data_rec[0] == '#') {
wacom_reset(wacom);
sscanf((const char *) wacom->data_rec, "IN%d", &wacom->increment);
} else if (!memcmp(wacom->data_rec, "RE", 2)) {
if (wacom->tablet_type->type == WACOM_TYPE_IV) wacom_reset_artpad(wacom);
else wacom_reset(wacom);
} else if (!memcmp(wacom->data_rec, "IT", 2)) {
sscanf((const char*)wacom->data_rec, "IT%d", &wacom->interval);
} else if (!memcmp(wacom->data_rec, "DE", 2)) {
sscanf((const char*)wacom->data_rec, "DE%d", &mouse_mode);
sscanf((const char *) wacom->data_rec, "IT%d", &wacom->interval);
} else if (!memcmp(wacom->data_rec, "DE", 2) && wacom->settings_bits.cmd_set == WACOM_CMDSET_IIS) {
sscanf((const char *) wacom->data_rec, "DE%d", &mouse_mode);
mouse_mode = !mouse_mode;
plat_mouse_capture(0);
} else if (!memcmp(wacom->data_rec, "SU", 2)) {
sscanf((const char*)wacom->data_rec, "SU%d", &wacom->suppressed_increment);
} else if (!memcmp(wacom->data_rec, "PH", 2)) {
sscanf((const char*)wacom->data_rec, "PH%d", &wacom->pressure_mode);
sscanf((const char *) wacom->data_rec, "SU%d", &wacom->suppressed_increment);
wacom->settings_bits.transfer_mode = wacom->mode = WACOM_MODE_SUPPRESSED;
} else if (!memcmp(wacom->data_rec, "PH", 2) && wacom->settings_bits.cmd_set == WACOM_CMDSET_IIS) {
sscanf((const char *) wacom->data_rec, "PH%d", &wacom->pressure_mode);
} else if (!memcmp(wacom->data_rec, "IC", 2)) {
sscanf((const char*)wacom->data_rec, "IC%d", &wacom->measurement);
sscanf((const char *) wacom->data_rec, "IC%d", &wacom->measurement);
} else if (!memcmp(wacom->data_rec, "SW", 2)) {
wacom->mode = WACOM_MODE_SWITCH;
} else if (!memcmp(wacom->data_rec, "AL", 2)) {
sscanf((const char*)wacom->data_rec, "AL%d", &wacom->always_report);
uint8_t out_of_range_data = wacom->settings_bits.out_of_range_data;
wacom->settings_bits.out_of_range_data = !!out_of_range_data;
} else if (!memcmp(wacom->data_rec, "RQ", 2)) {
sscanf((const char*)wacom->data_rec, "RQ%d", &wacom->remote_mode);
if (wacom->remote_mode) wacom->remote_req = 1;
uint8_t remote_mode = 0;
sscanf((const char *) wacom->data_rec, "RQ%d", &remote_mode);
wacom->settings_bits.remote_mode = !!remote_mode;
if (wacom->settings_bits.remote_mode)
wacom->remote_req = 1;
} else if (!memcmp(wacom->data_rec, "SP", 2)) {
wacom->transmission_stopped = 1;
} else if (!memcmp(wacom->data_rec, "ST", 2)){
} else if (!memcmp(wacom->data_rec, "ST", 2)) {
wacom->transmission_stopped = 0;
wacom->remote_mode = wacom->remote_req = 0;
wacom->settings_bits.remote_mode = wacom->remote_req = 0;
} else if (!memcmp(wacom->data_rec, "NR", 2)) {
sscanf((const char *) wacom->data_rec, "NR%d", &wacom->x_res);
wacom->y_res = wacom->x_res;
} else if (wacom->tablet_type->type == WACOM_TYPE_IV && wacom->data_rec[0] == '~') {
if (!memcmp(wacom->data_rec, "~*", 2)) {
uint32_t settings_dword = wacom->settings;
if (strstr(wacom->data_rec, ",")) {
uint32_t x_res = wacom->x_res, y_res = wacom->y_res;
uint32_t increment = wacom->increment;
uint32_t interval = wacom->interval;
sscanf("~*%08X,%d,%d,%d,%d", wacom->data_rec, &settings_dword, &increment, &interval, &x_res, &y_res);
wacom->interval = interval;
wacom->increment = increment;
wacom->x_res = x_res;
wacom->y_res = y_res;
} else {
sscanf("~*%X", wacom->data_rec, &settings_dword);
}
wacom_process_settings_dword(wacom, settings_dword);
} else if (!memcmp(wacom->data_rec, "~C", 2)) {
fifo8_push_all(&wacom->data, "~C5039,3779\r", sizeof("~C5039,3779\r") - 1);
} else if (!memcmp(wacom->data_rec, "~R", 2)) {
uint8_t data[256] = { 0 };
snprintf(data, sizeof(data), "~*%08X,%d,%d,%d,%d\r", wacom->settings, wacom->increment, wacom->interval, wacom->x_res, wacom->y_res);
fifo8_push_all(&wacom->data, data, strlen(data));
}
}
}
}
@@ -188,16 +401,27 @@ wacom_write(struct serial_s *serial, void *priv, uint8_t data)
static int
wacom_poll(int x, int y, int z, int b, double abs_x, double abs_y, void *priv)
{
mouse_wacom_t* wacom = (mouse_wacom_t*)priv;
wacom->abs_x = abs_x * (wacom->measurement ? 4566. : 5800.);
wacom->abs_y = abs_y * (wacom->measurement ? 2972. : 3774.);
if (wacom->abs_x > (wacom->measurement ? 4566 : 5800)) wacom->abs_x = (wacom->measurement ? 4566 : 5800);
if (wacom->abs_y > (wacom->measurement ? 2972 : 3774)) wacom->abs_x = (wacom->measurement ? 2972 : 3774);
if (wacom->abs_x < 0) wacom->abs_x = 0;
if (wacom->abs_y < 0) wacom->abs_y = 0;
wacom->rel_x = x;
wacom->rel_y = y;
if (wacom->b != b) wacom->oldb = wacom->b;
mouse_wacom_t *wacom = (mouse_wacom_t *) priv;
if (wacom->settings_bits.cmd_set == WACOM_CMDSET_IV) {
wacom->abs_x = abs_x * 5039. * (wacom->x_res / 1000.);
wacom->abs_y = abs_y * 3779. * (wacom->y_res / 1000.);
} else {
wacom->abs_x = abs_x * (wacom->measurement ? 4566. : 5800.);
wacom->abs_y = abs_y * (wacom->measurement ? 2972. : 3774.);
if (wacom->abs_x > (wacom->measurement ? 4566 : 5800))
wacom->abs_x = (wacom->measurement ? 4566 : 5800);
if (wacom->abs_y > (wacom->measurement ? 2972 : 3774))
wacom->abs_x = (wacom->measurement ? 2972 : 3774);
if (wacom->abs_x < 0)
wacom->abs_x = 0;
if (wacom->abs_y < 0)
wacom->abs_y = 0;
wacom->rel_x = x;
wacom->rel_y = y;
}
if (wacom->b != b)
wacom->oldb = wacom->b;
wacom->b = b;
return (0);
}
@@ -205,9 +429,12 @@ wacom_poll(int x, int y, int z, int b, double abs_x, double abs_y, void *priv)
static int
wacom_switch_off_to_on(int b, int oldb)
{
if (!(oldb & 0x1) && (b & 1)) return 1;
if (!(oldb & 0x2) && (b & 2)) return 1;
if (!(oldb & 0x4) && (b & 4)) return 1;
if (!(oldb & 0x1) && (b & 1))
return 1;
if (!(oldb & 0x2) && (b & 2))
return 1;
if (!(oldb & 0x4) && (b & 4))
return 1;
return 0;
}
@@ -215,64 +442,85 @@ wacom_switch_off_to_on(int b, int oldb)
static uint8_t
wacom_get_switch(int b)
{
if (b & 0x4) return 0x23;
if (b & 0x2) return 0x22;
if (b & 0x1) return 0x21;
if (b & 0x4)
return 0x23;
if (b & 0x2)
return 0x22;
if (b & 0x1)
return 0x21;
return 0x00;
}
static void
wacom_transmit_prepare(mouse_wacom_t* wacom, int x, int y)
wacom_transmit_prepare(mouse_wacom_t *wacom, int x, int y)
{
wacom->transmission_ongoing = 1;
wacom->data_pos = 0;
memset(wacom->data, 0, sizeof(wacom->data));
if (wacom->transmit_id) {
wacom->transmission_format = 0;
snprintf((char*)wacom->data, sizeof(wacom->data), "~#SD51C V3.2.1.01\r");
uint8_t data[128] = { 0 };
snprintf((char *) data, sizeof(data), "%s", wacom->tablet_type->id);
fifo8_push_all(&wacom->data, data, strlen(data));
wacom->transmit_id = 0;
return;
}
wacom->transmission_format = wacom->format;
wacom->last_abs_x = wacom->abs_x;
wacom->last_abs_y = wacom->abs_y;
wacom->remote_req = 0;
wacom->last_abs_x = wacom->abs_x;
wacom->last_abs_y = wacom->abs_y;
wacom->remote_req = 0;
wacom->oldb = wacom->b;
if (wacom->format == 1) {
wacom->data[0] = 0xC0;
wacom->data[6] = wacom->pressure_mode ? ((wacom->b & 0x1) ? (uint8_t)31 : (uint8_t)-31) : wacom_get_switch(wacom->b);
if (wacom->settings_bits.output_format == 0) {
uint8_t data[7];
data[0] = 0xC0;
if (wacom->settings_bits.cmd_set == WACOM_CMDSET_IV) {
if (tablet_tool_type == 0)
data[6] = ((wacom->b & 0x1) ? (uint8_t) 31 : (uint8_t) -1);
else
data[6] = ((wacom->b & 0x1) ? (uint8_t) 63 : (uint8_t) -63);
}
else
data[6] = (wacom->pressure_mode || wacom->settings_bits.cmd_set == WACOM_CMDSET_IV) ? ((wacom->b & 0x1) ? (uint8_t) 31 : (uint8_t) -31) : wacom_get_switch(wacom->b);
wacom->data[5] = (y & 0x7F);
wacom->data[4] = ((y & 0x3F80) >> 7) & 0x7F;
wacom->data[3] = (((y & 0xC000) >> 14) & 3);
data[5] = (y & 0x7F);
data[4] = ((y & 0x3F80) >> 7) & 0x7F;
data[3] = (((y & 0xC000) >> 14) & 3);
wacom->data[2] = (x & 0x7F);
wacom->data[1] = ((x & 0x3F80) >> 7) & 0x7F;
wacom->data[0] |= (((x & 0xC000) >> 14) & 3);
data[2] = (x & 0x7F);
data[1] = ((x & 0x3F80) >> 7) & 0x7F;
data[0] |= (((x & 0xC000) >> 14) & 3);
if (mouse_mode == 0) {
wacom->data[0] |= (!!(x < 0)) << 2;
wacom->data[3] |= (!!(y < 0)) << 2;
if (mouse_mode == 0 && wacom->settings_bits.cmd_set == WACOM_CMDSET_IIS) {
data[0] |= (!!(x < 0)) << 2;
data[3] |= (!!(y < 0)) << 2;
}
if (wacom->pressure_mode) {
wacom->data[0] |= 0x10;
wacom->data[6] &= 0x7F;
if (wacom->settings_bits.cmd_set == WACOM_CMDSET_IV) {
data[6] &= 0x7F;
data[3] &= 0x3;
if (wacom_get_switch(wacom->b) != 0x21) {
data[3] |= (wacom_get_switch(wacom->b) & 0xF) << 3;
data[0] |= 0x8;
}
}
if (wacom->pressure_mode && wacom->settings_bits.cmd_set == WACOM_CMDSET_IIS) {
data[0] |= 0x10;
data[6] &= 0x7F;
}
if (tablet_tool_type == 1) {
wacom->data[0] |= 0x20;
data[0] |= 0x20;
}
if (!mouse_tablet_in_proximity) {
wacom->data[0] &= ~0x40;
data[0] &= ~0x40;
}
fifo8_push_all(&wacom->data, data, 7);
} else {
wacom->data[0] = 0;
snprintf((char*)wacom->data, sizeof(wacom->data), "*,%05d,%05d,%d\r\n",
wacom->abs_x, wacom->abs_y,
wacom->pressure_mode ? ((wacom->b & 0x1) ? (uint8_t)-31 : (uint8_t)15) : ((wacom->b & 0x1) ? 21 : 00));
uint8_t data[128];
data[0] = 0;
snprintf((char *) data, sizeof(data), "*,%05d,%05d,%d\r\n",
wacom->abs_x, wacom->abs_y,
wacom->pressure_mode ? ((wacom->b & 0x1) ? (uint8_t) -31 : (uint8_t) 15) : ((wacom->b & 0x1) ? 21 : 00));
fifo8_push_all(&wacom->data, data, strlen(data));
}
}
@@ -280,57 +528,62 @@ extern double cpuclock;
static void
wacom_report_timer(void *priv)
{
mouse_wacom_t* wacom = (mouse_wacom_t*)priv;
double milisecond_diff = ((double)(tsc - wacom->old_tsc)) / cpuclock * 1000.0;
int relative_mode = (mouse_mode == 0);
int x = (relative_mode ? wacom->rel_x : wacom->abs_x);
int y = (relative_mode ? wacom->rel_y : wacom->abs_y);
int x_diff = abs(relative_mode ? wacom->rel_x : (wacom->abs_x - wacom->last_abs_x));
int y_diff = abs(relative_mode ? wacom->rel_y : (wacom->abs_y - wacom->last_abs_y));
int increment = wacom->suppressed_increment ? wacom->suppressed_increment : wacom->increment;
mouse_wacom_t *wacom = (mouse_wacom_t *) priv;
double milisecond_diff = ((double) (tsc - wacom->old_tsc)) / cpuclock * 1000.0;
int relative_mode = (mouse_mode == 0);
int x = (relative_mode ? wacom->rel_x : wacom->abs_x);
int y = (relative_mode ? wacom->rel_y : wacom->abs_y);
int x_diff = abs(relative_mode ? wacom->rel_x : (wacom->abs_x - wacom->last_abs_x));
int y_diff = abs(relative_mode ? wacom->rel_y : (wacom->abs_y - wacom->last_abs_y));
int increment = wacom->suppressed_increment ? wacom->suppressed_increment : wacom->increment;
timer_on_auto(&wacom->report_timer, wacom->transmit_period);
if ((((double)(tsc - wacom->reset_tsc)) / cpuclock * 1000.0) <= 10)
if ((((double) (tsc - wacom->reset_tsc)) / cpuclock * 1000.0) <= 10)
return;
if (wacom->transmit_id && !wacom->transmission_ongoing)
if (wacom->transmit_id)
goto transmit_prepare;
if (wacom->transmission_ongoing)
if (fifo8_num_used(&wacom->data))
goto transmit;
else if (wacom->remote_mode && !wacom->remote_req)
else if (wacom->settings_bits.remote_mode && !wacom->remote_req)
return;
else {
if (wacom->remote_mode && wacom->remote_req) {
if (wacom->settings_bits.remote_mode && wacom->remote_req) {
goto transmit_prepare;
}
if (wacom->transmission_stopped || (!mouse_tablet_in_proximity && !wacom->always_report))
if (wacom->transmission_stopped || (!mouse_tablet_in_proximity && !wacom->settings_bits.out_of_range_data))
return;
if (milisecond_diff >= (wacom->interval * 5)) {
wacom->old_tsc = tsc;
} else
return;
switch (wacom->mode) {
case WACOM_MODE_STREAM:
default:
break;
case WACOM_MODE_POINT:
{
if (!(wacom_switch_off_to_on(wacom->b, wacom->oldb)))
return;
break;
}
{
if (wacom->suppressed_increment)
break;
if (!(wacom_switch_off_to_on(wacom->b, wacom->oldb)))
return;
break;
}
case WACOM_MODE_SWITCH:
{
if (!wacom->b)
return;
break;
}
{
if (!wacom->b)
return;
break;
}
}
if (increment && !mouse_tablet_in_proximity)
return;
if (increment && !(x_diff > increment || y_diff > increment)) {
if (wacom->suppressed_increment && (wacom->b == wacom->oldb))
return;
@@ -344,13 +597,9 @@ transmit_prepare:
wacom_transmit_prepare(wacom, x, y);
transmit:
serial_write_fifo(wacom->serial, wacom->data[wacom->data_pos++]);
if ((wacom->transmission_format == 0 && wacom->data[wacom->data_pos] == 0)
|| (wacom->transmission_format == 1 && wacom->data_pos == 7)) {
wacom->transmission_ongoing = 0;
wacom->transmit_id = 0;
wacom->data_pos = 0;
wacom->old_tsc = tsc;
serial_write_fifo(wacom->serial, fifo8_pop(&wacom->data));
if (fifo8_num_used(&wacom->data) == 0) {
wacom->old_tsc = tsc;
}
return;
}
@@ -360,9 +609,16 @@ wacom_init(const device_t *info)
{
mouse_wacom_t *dev;
dev = (mouse_wacom_t *) calloc(1, sizeof(mouse_wacom_t));
dev = (mouse_wacom_t *) calloc(1, sizeof(mouse_wacom_t));
dev->name = info->name;
dev->but = 3;
dev->bits = 10;
if (info->local == 0) {
dev->tablet_type = &sd510_id;
} else
dev->tablet_type = (wacom_tablet_id*)info->local;
fifo8_create(&dev->data, 512);
dev->port = device_get_config_int("port");
@@ -370,7 +626,11 @@ wacom_init(const device_t *info)
timer_add(&dev->report_timer, wacom_report_timer, dev, 0);
mouse_set_buttons(dev->but);
wacom_reset(dev);
if (dev->tablet_type->type == WACOM_TYPE_IV) {
wacom_reset_artpad(dev);
wacom_process_settings_dword(dev, 0xE2018000);
}
else wacom_reset(dev);
return dev;
}
@@ -388,6 +648,8 @@ wacom_close(void *priv)
{
mouse_wacom_t *dev = (mouse_wacom_t *) priv;
fifo8_destroy(&dev->data);
/* Detach serial port from the mouse. */
if (dev && dev->serial && dev->serial->sd)
memset(dev->serial->sd, 0, sizeof(serial_device_t));
@@ -421,7 +683,21 @@ const device_t mouse_wacom_device = {
.name = "Wacom SD-510C",
.internal_name = "wacom_serial",
.flags = DEVICE_COM,
.local = MOUSE_TYPE_WACOM,
.local = 0,
.init = wacom_init,
.close = wacom_close,
.reset = NULL,
{ .poll = wacom_poll },
.speed_changed = wacom_speed_changed,
.force_redraw = NULL,
.config = wacom_config
};
const device_t mouse_wacom_artpad_device = {
.name = "Wacom ArtPad",
.internal_name = "wacom_serial_artpad",
.flags = DEVICE_COM,
.local = (uintptr_t)&artpad_id,
.init = wacom_init,
.close = wacom_close,
.reset = NULL,

View File

@@ -152,7 +152,7 @@ serial_clear_timeout(serial_t *dev)
static void
serial_receive_timer(void *priv)
{
serial_t *dev = (serial_t *) priv;
serial_t *dev = (serial_t *) priv;
// serial_log("serial_receive_timer()\n");
@@ -211,8 +211,7 @@ write_fifo(serial_t *dev, uint8_t dat)
{
serial_log("write_fifo(%08X, %02X, %i, %i)\n", dev, dat,
(dev->type >= SERIAL_16550) && dev->fifo_enabled,
((dev->type >= SERIAL_16550) && dev->fifo_enabled) ?
(dev->rcvr_fifo_pos % dev->rcvr_fifo_len) : 0);
((dev->type >= SERIAL_16550) && dev->fifo_enabled) ? (dev->rcvr_fifo_pos % dev->rcvr_fifo_len) : 0);
if ((dev->type >= SERIAL_16550) && dev->fifo_enabled) {
/* FIFO mode. */
@@ -560,7 +559,7 @@ serial_write(uint16_t addr, uint8_t val, void *p)
dev->rcvr_fifo_len = 14;
break;
}
dev->out_new = 0xffff;
dev->out_new = 0xffff;
serial_log("FIFO now %sabled, receive FIFO length = %i\n", dev->fifo_enabled ? "en" : "dis", dev->rcvr_fifo_len);
}
break;
@@ -665,7 +664,7 @@ serial_read(uint16_t addr, void *p)
if (dev->rcvr_fifo_full || (dev->rcvr_fifo_pos != dev->rcvr_fifo_end)) {
/* There is data in the FIFO. */
ret = dev->rcvr_fifo[dev->rcvr_fifo_pos];
ret = dev->rcvr_fifo[dev->rcvr_fifo_pos];
dev->rcvr_fifo_pos = (dev->rcvr_fifo_pos + 1) & 0x0f;
/* Make sure to clear the FIFO full condition. */
@@ -690,7 +689,7 @@ serial_read(uint16_t addr, void *p)
} else {
/* Non-FIFO mode. */
ret = (uint8_t) (dev->out_new & 0xffff);
ret = (uint8_t) (dev->out_new & 0xffff);
dev->out_new = 0xffff;
/* Always clear Data Ready interrupt. */
@@ -857,8 +856,8 @@ serial_reset(void *priv)
serial_reset_port(dev);
dev->dlab = 96;
dev->fcr = 0x06;
dev->dlab = 96;
dev->fcr = 0x06;
serial_transmit_period(dev);
serial_update_speed(dev);
@@ -889,8 +888,8 @@ serial_init(const device_t *info)
serial_setup(dev, COM1_ADDR, COM1_IRQ);
/* Default to 1200,N,7. */
dev->dlab = 96;
dev->fcr = 0x06;
dev->dlab = 96;
dev->fcr = 0x06;
if (info->local == SERIAL_8250_PCJR)
dev->clock_src = 1789500.0;
else

View File

@@ -88,7 +88,7 @@ discord_update_activity(int paused)
*(paren - 1) = '\0';
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined (__clang__)
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
if (strlen(vm_name) < 100) {

View File

@@ -62,6 +62,7 @@
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

View File

@@ -18,6 +18,7 @@
* Copyright 2016-2020 Miran Grca.
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -737,7 +738,7 @@ ide_set_signature(ide_t *ide)
ide->secount = ide->sc->phase;
ide->cylinder = ide->sc->request_length;
} else {
ide->secount = 1;
ide->secount = 1;
// ide->cylinder = ((ide->type == IDE_HDD) ? 0 : 0xFFFF);
ide->cylinder = ((ide->type == IDE_HDD) ? 0 : 0x7F7F);
if (ide->type == IDE_HDD)
@@ -1919,9 +1920,9 @@ ide_readb(uint16_t addr, void *priv)
addr &= 0xFFF7;
if ((ide->type == IDE_NONE) && ((ide_drives[ch ^ 1]->type == IDE_NONE) || !(ch & 1)))
absent = 1; /* Absent and is master or both are absent. */
absent = 1; /* Absent and is master or both are absent. */
else if ((ide->type == IDE_NONE) && (ch & 1))
absent = 2; /* Absent and is slave and master is present. */
absent = 2; /* Absent and is slave and master is present. */
switch (addr & 0x7) {
case 0x0: /* Data */

View File

@@ -102,7 +102,7 @@
#include <86box/hdc.h>
#include <86box/hdd.h>
#define HDC_TIME (50 * TIMER_USEC)
#define HDC_TIME (50 * TIMER_USEC)
#define WD_REV_1_BIOS_FILE "roms/hdd/xta/idexywd2.bin"
#define WD_REV_2_BIOS_FILE "roms/hdd/xta/infowdbios.rom"
@@ -968,7 +968,7 @@ xta_init(const device_t *info)
{
drive_t *drive;
char *bios_rev = NULL;
char *fn = NULL;
char *fn = NULL;
hdc_t *dev;
int c, i;
int max = XTA_NUM;

View File

@@ -18,6 +18,7 @@
*/
#define _GNU_SOURCE
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
@@ -32,7 +33,7 @@
#include <86box/random.h>
#include <86box/hdd.h>
#include "minivhd/minivhd.h"
#include "minivhd/minivhd_internal.h"
#include "minivhd/internal.h"
#define HDD_IMAGE_RAW 0
#define HDD_IMAGE_HDI 1

View File

@@ -13,6 +13,5 @@
# Copyright 2020,2021 David Hrdlička.
#
add_library(minivhd STATIC cwalk.c libxml2_encoding.c minivhd_convert.c
minivhd_create.c minivhd_io.c minivhd_manage.c minivhd_struct_rw.c
minivhd_util.c)
add_library(minivhd STATIC cwalk.c xml2_encoding.c convert.c
create.c minivhd_io.c manage.c struct_rw.c minivhd_util.c)

View File

@@ -1,7 +1,7 @@
# Credits
MiniVHD Copyright (c) 2019 Sherman Perry
MiniVHD Copyright 2019-2021 Sherman Perry.
MiniVHD was made possible with the help of the following projects
MiniVHD was made possible with the help of the following projects:
### libxml2
**Project Home:** http://www.xmlsoft.org/
@@ -10,3 +10,8 @@ MiniVHD was made possible with the help of the following projects
### cwalk
**Project Home:** https://likle.github.io/cwalk/
**Licence:** MIT (https://github.com/likle/cwalk/blob/master/LICENSE.md)
### VARCem
The MiniVHD was rewritten into a standalone library (both shared as well
as static) by Fred N. van Kempen for use with the VARCem PC Systems
emulator - see https://www.varcem.com/ for more info.

View File

@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@@ -1,28 +1,66 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Version: @(#)convert.c 1.0.2 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "minivhd_create.h"
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include <time.h>
#include "minivhd.h"
#include "internal.h"
static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err);
static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err) {
static FILE*
open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err)
{
if (geom == NULL) {
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
FILE *raw_img = mvhd_fopen(utf8_raw_path, "rb", err);
if (raw_img == NULL) {
*err = MVHD_ERR_FILE;
return NULL;
}
if (geom == NULL) {
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
mvhd_fseeko64(raw_img, 0, SEEK_END);
uint64_t size_bytes = (uint64_t)mvhd_ftello64(raw_img);
MVHDGeom new_geom = mvhd_calculate_geometry(size_bytes);
@@ -34,37 +72,52 @@ static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geo
geom->heads = new_geom.heads;
geom->spt = new_geom.spt;
mvhd_fseeko64(raw_img, 0, SEEK_SET);
return raw_img;
}
MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) {
MVHDAPI MVHDMeta*
mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err)
{
MVHDGeom geom;
FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err);
FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err);
if (raw_img == NULL) {
return NULL;
}
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, size_in_bytes, &geom, err, NULL);
if (vhdm == NULL) {
return NULL;
}
return vhdm;
}
MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) {
MVHDAPI MVHDMeta*
mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err)
{
MVHDGeom geom;
MVHDMeta *vhdm = NULL;
FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err);
FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err);
if (raw_img == NULL) {
return NULL;
}
vhdm = mvhd_create_sparse(utf8_vhd_path, geom, err);
if (vhdm == NULL) {
goto end;
}
uint8_t buff[4096] = {0}; // 8 sectors
uint8_t empty_buff[4096] = {0};
int total_sectors = mvhd_calc_size_sectors(&geom);
int copy_sect = 0;
for (int i = 0; i < total_sectors; i += 8) {
copy_sect = 8;
if ((i + 8) >= total_sectors) {
@@ -72,6 +125,7 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
memset(buff, 0, sizeof buff);
}
(void) !fread(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img);
/* Only write data if there's data to write, to take advantage of the sparse VHD format */
if (memcmp(buff, empty_buff, sizeof buff) != 0) {
mvhd_write_sectors(vhdm, i, copy_sect, buff);
@@ -79,18 +133,25 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
}
end:
fclose(raw_img);
return vhdm;
}
FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err) {
MVHDAPI FILE*
mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err)
{
FILE *raw_img = mvhd_fopen(utf8_raw_path, "wb", err);
if (raw_img == NULL) {
return NULL;
}
MVHDMeta *vhdm = mvhd_open(utf8_vhd_path, true, err);
if (vhdm == NULL) {
fclose(raw_img);
return NULL;
}
uint8_t buff[4096] = {0}; // 8 sectors
int total_sectors = mvhd_calc_size_sectors((MVHDGeom*)&vhdm->footer.geom);
int copy_sect = 0;
@@ -104,5 +165,6 @@ FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path,
}
mvhd_close(vhdm);
mvhd_fseeko64(raw_img, 0, SEEK_SET);
return raw_img;
}

View File

@@ -1,30 +1,60 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Version: @(#)create.c 1.0.3 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "cwalk.h"
#include "libxml2_encoding.h"
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include "minivhd_struct_rw.h"
#include "minivhd_io.h"
#include "minivhd_create.h"
#include <time.h>
#include "minivhd.h"
#include "internal.h"
#include "cwalk.h"
#include "xml2_encoding.h"
static const char MVHD_CONECTIX_COOKIE[] = "conectix";
static const char MVHD_CREATOR[] = "mVHD";
static const char MVHD_CREATOR_HOST_OS[] = "Wi2k";
static const char MVHD_CXSPARSE_COOKIE[] = "cxsparse";
static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off);
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors);
static int mvhd_gen_par_loc(MVHDSparseHeader* header,
const char* child_path,
const char* par_path,
uint64_t start_offset,
mvhd_utf16* w2ku_path_buff,
mvhd_utf16* w2ru_path_buff,
MVHDError* err);
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err);
/**
* \brief Populate a VHD footer
@@ -35,24 +65,29 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
* \param [in] type of HVD that is being created
* \param [in] sparse_header_off, an absolute file offset to the sparse header. Not used for fixed VHD images
*/
static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) {
memcpy(footer->cookie, "conectix", sizeof footer->cookie);
static void
gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off)
{
memcpy(footer->cookie, MVHD_CONECTIX_COOKIE, sizeof footer->cookie);
footer->features = 0x00000002;
footer->fi_fmt_vers = 0x00010000;
footer->data_offset = (type == MVHD_TYPE_DIFF || type == MVHD_TYPE_DYNAMIC) ? sparse_header_off : 0xffffffffffffffff;
footer->timestamp = vhd_calc_timestamp();
memcpy(footer->cr_app, "mvhd", sizeof footer->cr_app);
memcpy(footer->cr_app, MVHD_CREATOR, sizeof footer->cr_app);
footer->cr_vers = 0x000e0000;
memcpy(footer->cr_host_os, "Wi2k", sizeof footer->cr_host_os);
memcpy(footer->cr_host_os, MVHD_CREATOR_HOST_OS, sizeof footer->cr_host_os);
footer->orig_sz = footer->curr_sz = size_in_bytes;
footer->geom.cyl = geom->cyl;
footer->geom.heads = geom->heads;
footer->geom.spt = geom->spt;
footer->disk_type = type;
mvhd_generate_uuid(footer->uuid);
footer->checksum = mvhd_gen_footer_checksum(footer);
}
/**
* \brief Populate a VHD sparse header
*
@@ -61,8 +96,10 @@ static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom
* \param [in] bat_offset is the absolute file offset for start of the Block Allocation Table
* \param [in] block_size_in_sectors is the block size in sectors.
*/
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors) {
memcpy(header->cookie, "cxsparse", sizeof header->cookie);
static void
gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors)
{
memcpy(header->cookie, MVHD_CXSPARSE_COOKIE, sizeof header->cookie);
header->data_offset = 0xffffffffffffffff;
header->bat_offset = bat_offset;
header->head_vers = 0x00010000;
@@ -71,6 +108,7 @@ static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks,
header->checksum = mvhd_gen_sparse_checksum(header);
}
/**
* \brief Generate parent locators for differencing VHD images
*
@@ -85,13 +123,12 @@ static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks,
* \retval 0 if success
* \retval < 0 if an error occurrs. Check value of *err for actual error
*/
static int mvhd_gen_par_loc(MVHDSparseHeader* header,
const char* child_path,
const char* par_path,
uint64_t start_offset,
mvhd_utf16* w2ku_path_buff,
mvhd_utf16* w2ru_path_buff,
MVHDError* err) {
static int
gen_par_loc(MVHDSparseHeader* header, const char* child_path,
const char* par_path, uint64_t start_offset,
mvhd_utf16* w2ku_path_buff, mvhd_utf16* w2ru_path_buff,
MVHDError* err)
{
/* Get our paths to store in the differencing VHD. We want both the absolute path to the parent,
as well as the relative path from the child VHD */
int rv = 0;
@@ -100,6 +137,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
char rel_path[MVHD_MAX_PATH_BYTES] = {0};
char child_dir[MVHD_MAX_PATH_BYTES] = {0};
size_t child_dir_len;
if (strlen(child_path) < sizeof child_dir) {
strcpy(child_dir, child_path);
} else {
@@ -107,6 +145,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
rv = -1;
goto end;
}
cwk_path_get_basename(par_path, (const char**)&par_filename, &par_fn_len);
cwk_path_get_dirname(child_dir, &child_dir_len);
child_dir[child_dir_len] = '\0';
@@ -116,6 +155,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
rv = -1;
goto end;
}
/* We have our paths, now store the parent filename directly in the sparse header. */
int outlen = sizeof header->par_utf16_name;
int utf_ret;
@@ -144,6 +184,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
goto end;
}
int w2ru_len = utf_ret;
/**
* Finally populate the parent locaters in the sparse header.
* This is the information needed to find the paths saved elsewhere
@@ -169,11 +210,16 @@ end:
return rv;
}
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback) {
MVHDAPI MVHDMeta*
mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback)
{
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
return mvhd_create_fixed_raw(path, NULL, size_in_bytes, &geom, err, progress_callback);
}
/**
* \brief internal function that implements public mvhd_create_fixed() functionality
*
@@ -182,27 +228,35 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_prog
*
* \param [in] raw_image file handle to a raw disk image to populate VHD
*/
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback) {
MVHDMeta*
mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback)
{
uint8_t img_data[MVHD_SECTOR_SIZE] = {0};
uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0};
if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) {
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
MVHDMeta* vhdm = calloc(1, sizeof *vhdm);
if (vhdm == NULL) {
*err = MVHD_ERR_MEM;
goto end;
}
if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) {
*err = MVHD_ERR_INVALID_GEOM;
goto cleanup_vhdm;
}
FILE* f = mvhd_fopen(path, "wb+", err);
if (f == NULL) {
goto cleanup_vhdm;
}
mvhd_fseeko64(f, 0, SEEK_SET);
uint32_t size_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE);
uint32_t s;
if (progress_callback)
progress_callback(0, size_sectors);
if (raw_img != NULL) {
mvhd_fseeko64(raw_img, 0, SEEK_END);
uint64_t raw_size = (uint64_t)mvhd_ftello64(raw_img);
@@ -211,7 +265,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
*err = MVHD_ERR_CONV_SIZE;
goto cleanup_vhdm;
}
mvhd_gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0);
gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0);
mvhd_fseeko64(raw_img, 0, SEEK_SET);
for (s = 0; s < size_sectors; s++) {
(void) !fread(img_data, sizeof img_data, 1, raw_img);
@@ -220,7 +274,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
progress_callback(s + 1, size_sectors);
}
} else {
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0);
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0);
for (s = 0; s < size_sectors; s++) {
fwrite(img_data, sizeof img_data, 1, f);
if (progress_callback)
@@ -238,10 +292,12 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
cleanup_vhdm:
free(vhdm);
vhdm = NULL;
end:
return vhdm;
}
/**
* \brief Create sparse or differencing VHD image.
*
@@ -254,7 +310,9 @@ end:
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err) {
static MVHDMeta*
create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err)
{
uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0};
uint8_t sparse_buff[MVHD_SPARSE_SIZE] = {0};
uint8_t bat_sect[MVHD_SECTOR_SIZE];
@@ -265,6 +323,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
mvhd_utf16* w2ku_path_buff = NULL;
mvhd_utf16* w2ru_path_buff = NULL;
uint32_t par_mod_timestamp = 0;
if (par_path != NULL) {
par_mod_timestamp = mvhd_file_mod_timestamp(par_path, err);
if (*err != 0) {
@@ -275,6 +334,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
goto end;
}
}
vhdm = calloc(1, sizeof *vhdm);
if (vhdm == NULL) {
*err = MVHD_ERR_MEM;
@@ -297,15 +357,18 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
goto cleanup_vhdm;
}
mvhd_fseeko64(f, 0, SEEK_SET);
/* Note, the sparse header follows the footer copy at the beginning of the file */
if (par_path == NULL) {
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
} else {
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
}
mvhd_footer_to_buffer(&vhdm->footer, footer_buff);
/* As mentioned, start with a copy of the footer */
fwrite(footer_buff, sizeof footer_buff, 1, f);
/**
* Calculate the number of (2MB or 512KB) data blocks required to store the entire
* contents of the disk image, followed by the number of sectors the
@@ -347,43 +410,51 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
}
memcpy(vhdm->sparse.par_uuid, par_vhdm->footer.uuid, sizeof vhdm->sparse.par_uuid);
par_loc_offset = bat_offset + ((uint64_t)num_bat_sect * MVHD_SECTOR_SIZE) + (5 * MVHD_SECTOR_SIZE);
if (mvhd_gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) {
if (gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) {
goto cleanup_vhdm;
}
vhdm->sparse.par_timestamp = par_mod_timestamp;
}
mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors);
gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors);
mvhd_header_to_buffer(&vhdm->sparse, sparse_buff);
fwrite(sparse_buff, sizeof sparse_buff, 1, f);
/* The BAT sectors need to be filled with 0xffffffff */
for (uint32_t i = 0; i < num_bat_sect; i++) {
for (uint32_t k = 0; k < num_bat_sect; k++) {
fwrite(bat_sect, sizeof bat_sect, 1, f);
}
mvhd_write_empty_sectors(f, 5);
/**
* If creating a differencing VHD, the paths to the parent image need to be written
* tp the file. Both absolute and relative paths are written
* */
if (par_vhdm != NULL) {
uint64_t curr_pos = (uint64_t)mvhd_ftello64(f);
/* Double check my sums... */
assert(curr_pos == par_loc_offset);
/* Fill the space required for location data with zero */
uint8_t empty_sect[MVHD_SECTOR_SIZE] = {0};
for (int i = 0; i < 2; i++) {
for (uint32_t j = 0; j < (vhdm->sparse.par_loc_entry[i].plat_data_space / MVHD_SECTOR_SIZE); j++) {
fwrite(empty_sect, sizeof empty_sect, 1, f);
}
}
/* Now write the location entries */
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[0].plat_data_offset, SEEK_SET);
fwrite(w2ku_path_buff, vhdm->sparse.par_loc_entry[0].plat_data_len, 1, f);
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset, SEEK_SET);
fwrite(w2ru_path_buff, vhdm->sparse.par_loc_entry[1].plat_data_len, 1, f);
/* and reset the file position to continue */
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset + vhdm->sparse.par_loc_entry[1].plat_data_space, SEEK_SET);
mvhd_write_empty_sectors(f, 5);
}
/* And finish with the footer */
fwrite(footer_buff, sizeof footer_buff, 1, f);
fclose(f);
@@ -395,91 +466,112 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
cleanup_vhdm:
free(vhdm);
vhdm = NULL;
cleanup_par_vhdm:
if (par_vhdm != NULL) {
mvhd_close(par_vhdm);
}
end:
free(w2ku_path_buff);
free(w2ru_path_buff);
return vhdm;
}
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) {
MVHDAPI MVHDMeta*
mvhd_create_sparse(const char* path, MVHDGeom geom, int* err)
{
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
return mvhd_create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err);
return create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err);
}
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err) {
return mvhd_create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err);
MVHDAPI MVHDMeta*
mvhd_create_diff(const char* path, const char* par_path, int* err)
{
return create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err);
}
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err) {
MVHDAPI MVHDMeta*
mvhd_create_ex(MVHDCreationOptions options, int* err)
{
uint32_t geom_sector_size;
switch (options.type)
{
case MVHD_TYPE_FIXED:
case MVHD_TYPE_DYNAMIC:
geom_sector_size = mvhd_calc_size_sectors(&(options.geometry));
if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0)
|| (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES)
|| (options.size_in_bytes == 0 && geom_sector_size == 0))
{
*err = MVHD_ERR_INVALID_SIZE;
return NULL;
}
if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes)
{
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
switch (options.type) {
case MVHD_TYPE_FIXED:
case MVHD_TYPE_DYNAMIC:
geom_sector_size = mvhd_calc_size_sectors(&(options.geometry));
if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0)
|| (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES)
|| (options.size_in_bytes == 0 && geom_sector_size == 0)) {
*err = MVHD_ERR_INVALID_SIZE;
return NULL;
}
if (options.size_in_bytes == 0)
options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE;
if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes) {
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
if (geom_sector_size == 0)
options.geometry = mvhd_calculate_geometry(options.size_in_bytes);
break;
case MVHD_TYPE_DIFF:
if (options.parent_path == NULL)
{
*err = MVHD_ERR_FILE;
if (options.size_in_bytes == 0)
options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE;
if (geom_sector_size == 0)
options.geometry = mvhd_calculate_geometry(options.size_in_bytes);
break;
case MVHD_TYPE_DIFF:
if (options.parent_path == NULL) {
*err = MVHD_ERR_FILE;
return NULL;
}
break;
default:
*err = MVHD_ERR_TYPE;
return NULL;
}
break;
default:
*err = MVHD_ERR_TYPE;
return NULL;
}
if (options.path == NULL)
{
if (options.path == NULL) {
*err = MVHD_ERR_FILE;
return NULL;
}
if (options.type != MVHD_TYPE_FIXED)
{
if (options.type != MVHD_TYPE_FIXED) {
if (options.block_size_in_sectors == MVHD_BLOCK_DEFAULT)
options.block_size_in_sectors = MVHD_BLOCK_LARGE;
if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL)
{
if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL) {
*err = MVHD_ERR_INVALID_BLOCK_SIZE;
return NULL;
}
}
switch (options.type)
{
case MVHD_TYPE_FIXED:
return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback);
case MVHD_TYPE_DYNAMIC:
return mvhd_create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err);
case MVHD_TYPE_DIFF:
return mvhd_create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err);
switch (options.type) {
case MVHD_TYPE_FIXED:
return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback);
case MVHD_TYPE_DYNAMIC:
return create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err);
case MVHD_TYPE_DIFF:
return create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err);
}
return NULL; /* Make the compiler happy */
}
bool
mvhd_is_conectix_str(const void* buffer)
{
if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) {
return true;
}
return false;
}

View File

@@ -1,12 +1,49 @@
/*
* libCWALK Path library for C/C++
*
* Version: @(#)cwalk.c 1.0.2 2021/03/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Leonard Iklé, <https://github.com/likle>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2020 Leonard Iklé.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "cwalk.h"
/**
* We try to default to a different path style depending on the operating
* system. So this should detect whether we should use windows or unix paths.

View File

@@ -1,10 +1,40 @@
#pragma once
/*
* libCWALK path library for C/C++
*
* Version: @(#)cwalk.h 1.0.3 2021/03/22
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Leonard Iklé, <https://github.com/likle>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2020 Leonard Iklé.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef CWK_LIBRARY_H
#define CWK_LIBRARY_H
# define CWK_LIBRARY_H
#include <stdbool.h>
#include <stddef.h>
/**
* A segment represents a single component of a path. For instance, on linux a
@@ -45,6 +75,11 @@ enum cwk_path_style
CWK_STYLE_UNIX
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generates an absolute path based on a base.
*
@@ -454,4 +489,9 @@ void cwk_path_set_style(enum cwk_path_style style);
*/
enum cwk_path_style cwk_path_get_style(void);
#ifdef __cplusplus
}
#endif
#endif /*CWK_LIBRARY_H*/

429
src/disk/minivhd/internal.h Normal file
View File

@@ -0,0 +1,429 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Internal definitions.
*
* Version: @(#)internal.h 1.0.1 2021/03/15
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MINIVHD_INTERNAL_H
# define MINIVHD_INTERNAL_H
#define MVHD_FOOTER_SIZE 512
#define MVHD_SPARSE_SIZE 1024
#define MVHD_SECTOR_SIZE 512
#define MVHD_BAT_ENT_PER_SECT 128
#define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000
#define MVHD_SPARSE_BLK 0xffffffff
/* For simplicity, we don't handle paths longer than this
* Note, this is the max path in characters, as that is what
* Windows uses
*/
#define MVHD_MAX_PATH_CHARS 260
#define MVHD_MAX_PATH_BYTES 1040
#define MVHD_DIF_LOC_W2RU 0x57327275
#define MVHD_DIF_LOC_W2KU 0x57326B75
#define MVHD_START_TS 946684800
typedef struct MVHDSectorBitmap {
uint8_t* curr_bitmap;
int sector_count;
int curr_block;
} MVHDSectorBitmap;
typedef struct MVHDFooter {
uint8_t cookie[8];
uint32_t features;
uint32_t fi_fmt_vers;
uint64_t data_offset;
uint32_t timestamp;
uint8_t cr_app[4];
uint32_t cr_vers;
uint8_t cr_host_os[4];
uint64_t orig_sz;
uint64_t curr_sz;
struct {
uint16_t cyl;
uint8_t heads;
uint8_t spt;
} geom;
uint32_t disk_type;
uint32_t checksum;
uint8_t uuid[16];
uint8_t saved_st;
uint8_t reserved[427];
} MVHDFooter;
typedef struct MVHDSparseHeader {
uint8_t cookie[8];
uint64_t data_offset;
uint64_t bat_offset;
uint32_t head_vers;
uint32_t max_bat_ent;
uint32_t block_sz;
uint32_t checksum;
uint8_t par_uuid[16];
uint32_t par_timestamp;
uint32_t reserved_1;
uint8_t par_utf16_name[512];
struct {
uint32_t plat_code;
uint32_t plat_data_space;
uint32_t plat_data_len;
uint32_t reserved;
uint64_t plat_data_offset;
} par_loc_entry[8];
uint8_t reserved_2[256];
} MVHDSparseHeader;
struct MVHDMeta {
FILE* f;
bool readonly;
char filename[MVHD_MAX_PATH_BYTES];
struct MVHDMeta* parent;
MVHDFooter footer;
MVHDSparseHeader sparse;
uint32_t* block_offset;
int sect_per_block;
MVHDSectorBitmap bitmap;
int (*read_sectors)(struct MVHDMeta*, uint32_t, int, void*);
int (*write_sectors)(struct MVHDMeta*, uint32_t, int, void*);
struct {
uint8_t* zero_data;
int sector_count;
} format_buffer;
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* Functions to deal with endian issues
*/
uint16_t mvhd_from_be16(uint16_t val);
uint32_t mvhd_from_be32(uint32_t val);
uint64_t mvhd_from_be64(uint64_t val);
uint16_t mvhd_to_be16(uint16_t val);
uint32_t mvhd_to_be32(uint32_t val);
uint64_t mvhd_to_be64(uint64_t val);
/**
* \brief Check if provided buffer begins with the string "conectix"
*
* \param [in] buffer The buffer to compare. Must be at least 8 bytes in length
*
* \return true if the buffer begins with "conectix"
* \return false if the buffer does not begin with "conectix"
*/
bool mvhd_is_conectix_str(const void* buffer);
/**
* \brief Generate a raw 16 byte UUID
*
* \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to
*/
void mvhd_generate_uuid(uint8_t *uuid);
/**
* \brief Calculate a VHD formatted timestamp from the current time
*/
uint32_t vhd_calc_timestamp(void);
/**
* \brief Convert an epoch timestamp to a VHD timestamp
*
* \param [in] ts epoch timestamp to convert.
*
* \return The adjusted timestamp, or 0 if the input timestamp is
* earlier that 1 Janurary 2000
*/
uint32_t mvhd_epoch_to_vhd_ts(time_t ts);
/**
* \brief Return the created time from a VHD image
*
* \param [in] vhdm Pointer to the MiniVHD metadata structure
*
* \return The created time, as a Unix timestamp
*/
time_t vhd_get_created_time(struct MVHDMeta *vhdm);
/**
* \brief Cross platform, unicode filepath opening
*
* This function accounts for the fact that fopen() handles file paths differently compared to other
* operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8.
*
* Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE
* encoded path and modestring.
*
* \param [in] path The filepath to open as a UTF-8 string
* \param [in] mode The mode string to use (eg: "rb+"")
* \param [out] err The error value, if an error occurrs
*
* \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err
*/
FILE* mvhd_fopen(const char* path, const char* mode, int* err);
void mvhd_set_encoding_err(int encoding_retval, int* err);
/**
* \brief Generate VHD footer checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer);
/**
* \brief Generate VHD sparse header checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header);
uint32_t mvhd_crc32_for_byte(uint32_t r);
/**
* \brief Get current position in file stream
*
* This is a portable version of the POSIX ftello64(). *
*/
int64_t mvhd_ftello64(FILE* stream);
/**
* \brief Reposition the file stream's position
*
* This is a portable version of the POSIX fseeko64(). *
*/
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin);
/**
* \brief Calculate the CRC32 of a data buffer.
*
* This function can be used for verifying data integrity.
*
* \param [in] data The data buffer
* \param [in] n_bytes The size of the data buffer in bytes
*
* \return The CRC32 of the data buffer
*/
uint32_t mvhd_crc32(const void* data, size_t n_bytes);
/**
* \brief Calculate the file modification timestamp.
*
* This function is primarily to help protect differencing VHD's
*
* \param [in] path the UTF-8 file path
* \param [out] err The error value, if an error occurrs
*
* \return The file modified timestamp, in VHD compatible timestamp.
* 'err' will be set to non-zero on error
*/
uint32_t mvhd_file_mod_timestamp(const char* path, int *err);
struct MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback);
/**
* \brief Write zero filled sectors to file.
*
* Note, the caller should set the file position before calling this
* function for correct operation.
*
* \param [in] f File to write sectors to
* \param [in] sector_count The number of sectors to write
*/
void mvhd_write_empty_sectors(FILE* f, int sector_count);
/**
* \brief Read a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_fixed_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a sparse VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* This function implements the logic to read sectors from the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* read could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_sparse_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a differencing VHD image
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is read from the
* child image only if it is newer than the data stored in the parent image.
*
* This function implements the logic to read sectors from the child, or a parent image.
* Differencing images may have a differencing image as a parent, creating a chain of images.
* There is no theoretical chain length limit, although I do not consider long chains to be
* advisable. Verifying the parent-child relationship is not very robust.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_diff_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Write to a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_fixed_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Write to a sparse or differencing VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is always written
* to the child image. This makes writing to differencing images essentially identical to
* writing to sparse images, hence they use the same function.
*
* This function implements the logic to write sectors to the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* write operation could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_sparse_diff_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief A no-op function to "write" to read-only VHD images
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_noop_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Save the contents of a VHD footer from a buffer to a struct
*
* \param [out] footer save contents of buffer into footer
* \param [in] buffer VHD footer in raw bytes
*/
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header from a buffer to a struct
*
* \param [out] header save contents of buffer into header
* \param [in] buffer VHD header in raw bytes
*/
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer);
/**
* \brief Save the contents of a VHD footer struct to a buffer
*
* \param [in] footer save contents of struct into buffer
* \param [out] buffer VHD footer in raw bytes
*/
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header struct to a buffer
*
* \param [in] header save contents of struct into buffer
* \param [out] buffer VHD sparse header in raw bytes
*/
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer);
#ifdef __cplusplus
}
#endif
#endif /*MINIVHD_INTERNAL_H*/

View File

@@ -1,12 +0,0 @@
#ifndef LIBXML2_ENCODING_H
#define LIBXML2_ENCODING_H
#include <stdint.h>
typedef uint16_t mvhd_utf16;
void xmlEncodingInit(void);
int UTF16LEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb);
int UTF8ToUTF16LE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen);
int UTF16BEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb);
int UTF8ToUTF16BE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen);
#endif

View File

@@ -1,66 +1,105 @@
/**
* \file
* \brief VHD management functions (open, close, read write etc)
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* VHD management functions (open, close, read write etc)
*
* Version: @(#)manage.c 1.0.4 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "cwalk.h"
#include "libxml2_encoding.h"
#include "minivhd_internal.h"
#include "minivhd_io.h"
#include "minivhd_util.h"
#include "minivhd_struct_rw.h"
#include <time.h>
#include "minivhd.h"
#include "internal.h"
#include "version.h"
#include "cwalk.h"
#include "xml2_encoding.h"
int mvhd_errno = 0;
static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0};
struct MVHDPaths {
char dir_path[MVHD_MAX_PATH_BYTES];
char file_name[MVHD_MAX_PATH_BYTES];
char w2ku_path[MVHD_MAX_PATH_BYTES];
char w2ru_path[MVHD_MAX_PATH_BYTES];
char joined_path[MVHD_MAX_PATH_BYTES];
char dir_path[MVHD_MAX_PATH_BYTES];
char file_name[MVHD_MAX_PATH_BYTES];
char w2ku_path[MVHD_MAX_PATH_BYTES];
char w2ru_path[MVHD_MAX_PATH_BYTES];
char joined_path[MVHD_MAX_PATH_BYTES];
uint16_t tmp_src_path[MVHD_MAX_PATH_CHARS];
};
static void mvhd_read_footer(MVHDMeta* vhdm);
static void mvhd_read_sparse_header(MVHDMeta* vhdm);
static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm);
static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm);
static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err);
static void mvhd_calc_sparse_values(MVHDMeta* vhdm);
static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err);
int mvhd_errno = 0;
static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0};
/**
* \brief Populate data stuctures with content from a VHD footer
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_read_footer(MVHDMeta* vhdm) {
static void
read_footer(MVHDMeta* vhdm)
{
uint8_t buffer[MVHD_FOOTER_SIZE];
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
(void) !fread(buffer, sizeof buffer, 1, vhdm->f);
mvhd_buffer_to_footer(&vhdm->footer, buffer);
}
/**
* \brief Populate data stuctures with content from a VHD sparse header
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_read_sparse_header(MVHDMeta* vhdm) {
static void
read_sparse_header(MVHDMeta* vhdm)
{
uint8_t buffer[MVHD_SPARSE_SIZE];
mvhd_fseeko64(vhdm->f, vhdm->footer.data_offset, SEEK_SET);
(void) !fread(buffer, sizeof buffer, 1, vhdm->f);
mvhd_buffer_to_header(&vhdm->sparse, buffer);
}
/**
* \brief Validate VHD footer checksum
*
@@ -68,10 +107,13 @@ static void mvhd_read_sparse_header(MVHDMeta* vhdm) {
*
* \param [in] vhdm MiniVHD data structure
*/
static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) {
static bool
footer_checksum_valid(MVHDMeta* vhdm)
{
return vhdm->footer.checksum == mvhd_gen_footer_checksum(&vhdm->footer);
}
/**
* \brief Validate VHD sparse header checksum
*
@@ -79,10 +121,13 @@ static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) {
*
* \param [in] vhdm MiniVHD data structure
*/
static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) {
static bool
sparse_checksum_valid(MVHDMeta* vhdm)
{
return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(&vhdm->sparse);
}
/**
* \brief Read BAT into MiniVHD data structure
*
@@ -96,13 +141,17 @@ static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) {
* \retval -1 if an error occurrs. Check value of err in this case
* \retval 0 if the function call succeeds
*/
static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) {
static int
read_bat(MVHDMeta *vhdm, MVHDError* err)
{
vhdm->block_offset = calloc(vhdm->sparse.max_bat_ent, sizeof *vhdm->block_offset);
if (vhdm->block_offset == NULL) {
*err = MVHD_ERR_MEM;
return -1;
}
mvhd_fseeko64(vhdm->f, vhdm->sparse.bat_offset, SEEK_SET);
for (uint32_t i = 0; i < vhdm->sparse.max_bat_ent; i++) {
(void) !fread(&vhdm->block_offset[i], sizeof *vhdm->block_offset, 1, vhdm->f);
vhdm->block_offset[i] = mvhd_from_be32(vhdm->block_offset[i]);
@@ -110,20 +159,25 @@ static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) {
return 0;
}
/**
* \brief Perform a one-time calculation of some sparse VHD values
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_calc_sparse_values(MVHDMeta* vhdm) {
static void
calc_sparse_values(MVHDMeta* vhdm)
{
vhdm->sect_per_block = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE;
int bm_bytes = vhdm->sect_per_block / 8;
vhdm->bitmap.sector_count = bm_bytes / MVHD_SECTOR_SIZE;
if (bm_bytes % MVHD_SECTOR_SIZE > 0) {
vhdm->bitmap.sector_count++;
}
}
/**
* \brief Allocate memory for a sector bitmap.
*
@@ -137,16 +191,21 @@ static void mvhd_calc_sparse_values(MVHDMeta* vhdm) {
* \retval -1 if an error occurrs. Check value of err in this case
* \retval 0 if the function call succeeds
*/
static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) {
static int
init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err)
{
vhdm->bitmap.curr_bitmap = calloc(vhdm->bitmap.sector_count, MVHD_SECTOR_SIZE);
if (vhdm->bitmap.curr_bitmap == NULL) {
*err = MVHD_ERR_MEM;
return -1;
}
vhdm->bitmap.curr_block = -1;
return 0;
}
/**
* \brief Check if the path for a given platform code exists
*
@@ -163,13 +222,19 @@ static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) {
* \retval true if a file is found
* \retval false if a file is not found
*/
static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code) {
memset(paths->joined_path, 0, sizeof paths->joined_path);
static bool
mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
{
FILE* f;
int cwk_ret, ferr;
enum cwk_path_style style = cwk_path_guess_style((const char*)paths->dir_path);
int ferr;
size_t cwk_ret;
enum cwk_path_style style;
memset(paths->joined_path, 0, sizeof paths->joined_path);
style = cwk_path_guess_style((const char*)paths->dir_path);
cwk_path_set_style(style);
cwk_ret = 1;
if (plat_code == MVHD_DIF_LOC_W2RU && *paths->w2ru_path) {
cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->w2ru_path, paths->joined_path, sizeof paths->joined_path);
} else if (plat_code == MVHD_DIF_LOC_W2KU && *paths->w2ku_path) {
@@ -181,6 +246,7 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
if (cwk_ret > MVHD_MAX_PATH_BYTES) {
return false;
}
f = mvhd_fopen((const char*)paths->joined_path, "rb", &ferr);
if (f != NULL) {
/* We found a file at the requested path! */
@@ -188,11 +254,12 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
tmp_open_path[sizeof tmp_open_path - 1] = '\0';
fclose(f);
return true;
} else {
return false;
}
return false;
}
/**
* \brief attempt to obtain a file path to a file that may be a valid VHD image
*
@@ -208,27 +275,33 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
* \return a pointer to the global string `tmp_open_path`, or NULL if a path could
* not be found, or some error occurred
*/
static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
static char*
get_diff_parent_path(MVHDMeta* vhdm, int* err)
{
int utf_outlen, utf_inlen, utf_ret;
char* par_fp = NULL;
char *par_fp = NULL;
struct MVHDPaths *paths;
size_t dirlen;
/* We can't resolve relative paths if we don't have an absolute
path to work with */
if (!cwk_path_is_absolute((const char*)vhdm->filename)) {
*err = MVHD_ERR_PATH_REL;
goto end;
}
struct MVHDPaths* paths = calloc(1, sizeof *paths);
paths = calloc(1, sizeof *paths);
if (paths == NULL) {
*err = MVHD_ERR_MEM;
goto end;
}
size_t dirlen;
cwk_path_get_dirname((const char*)vhdm->filename, &dirlen);
if (dirlen >= sizeof paths->dir_path) {
*err = MVHD_ERR_PATH_LEN;
goto paths_cleanup;
}
memcpy(paths->dir_path, vhdm->filename, dirlen);
/* Get the filename field from the sparse header. */
utf_outlen = (int)sizeof paths->file_name;
utf_inlen = (int)sizeof vhdm->sparse.par_utf16_name;
@@ -237,8 +310,10 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
mvhd_set_encoding_err(utf_ret, err);
goto paths_cleanup;
}
/* Now read the parent locator entries, both relative and absolute, if they exist */
unsigned char* loc_path;
for (int i = 0; i < 8; i++) {
utf_outlen = MVHD_MAX_PATH_BYTES - 1;
if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2RU) {
@@ -248,6 +323,7 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
} else {
continue;
}
utf_inlen = vhdm->sparse.par_loc_entry[i].plat_data_len;
if (utf_inlen > MVHD_MAX_PATH_BYTES) {
*err = MVHD_ERR_PATH_LEN;
@@ -255,6 +331,7 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
}
mvhd_fseeko64(vhdm->f, vhdm->sparse.par_loc_entry[i].plat_data_offset, SEEK_SET);
(void) !fread(paths->tmp_src_path, sizeof (uint8_t), utf_inlen, vhdm->f);
/* Note, the W2*u parent locators are UTF-16LE, unlike the filename field previously obtained,
which is UTF-16BE */
utf_ret = UTF16LEToUTF8(loc_path, &utf_outlen, (const unsigned char*)paths->tmp_src_path, &utf_inlen);
@@ -263,22 +340,26 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
goto paths_cleanup;
}
}
/* We have paths in UTF-8. We should have enough info to try and find the parent VHD */
/* Does the relative path exist? */
if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2RU)) {
par_fp = tmp_open_path;
goto paths_cleanup;
}
/* What about trying the child directory? */
if (mvhd_parent_path_exists(paths, 0)) {
par_fp = tmp_open_path;
goto paths_cleanup;
}
/* Well, all else fails, try the stored absolute path, if it exists */
if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2KU)) {
par_fp = tmp_open_path;
goto paths_cleanup;
}
/* If we reach this point, we could not find a path with a valid file */
par_fp = NULL;
*err = MVHD_ERR_PAR_NOT_FOUND;
@@ -286,10 +367,12 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
paths_cleanup:
free(paths);
paths = NULL;
end:
return par_fp;
}
/**
* \brief Attach the read/write function pointers to read/write functions
*
@@ -298,44 +381,90 @@ end:
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_assign_io_funcs(MVHDMeta* vhdm) {
static void
assign_io_funcs(MVHDMeta* vhdm)
{
switch (vhdm->footer.disk_type) {
case MVHD_TYPE_FIXED:
vhdm->read_sectors = mvhd_fixed_read;
vhdm->write_sectors = mvhd_fixed_write;
break;
case MVHD_TYPE_DYNAMIC:
vhdm->read_sectors = mvhd_sparse_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
case MVHD_TYPE_DIFF:
vhdm->read_sectors = mvhd_diff_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
case MVHD_TYPE_FIXED:
vhdm->read_sectors = mvhd_fixed_read;
vhdm->write_sectors = mvhd_fixed_write;
break;
case MVHD_TYPE_DYNAMIC:
vhdm->read_sectors = mvhd_sparse_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
case MVHD_TYPE_DIFF:
vhdm->read_sectors = mvhd_diff_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
}
if (vhdm->readonly) {
if (vhdm->readonly)
vhdm->write_sectors = mvhd_noop_write;
}
}
bool mvhd_file_is_vhd(FILE* f) {
if (f) {
uint8_t con_str[8];
mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END);
(void) !fread(con_str, sizeof con_str, 1, f);
return mvhd_is_conectix_str(con_str);
} else {
return false;
}
/**
* \brief Return the library version as a string
*/
MVHDAPI const char *
mvhd_version(void)
{
return LIB_VERSION_4;
}
MVHDGeom mvhd_calculate_geometry(uint64_t size) {
/**
* \brief Return the library version as a number
*/
MVHDAPI uint32_t
mvhd_version_id(void)
{
return (LIB_VER_MAJOR << 24) | (LIB_VER_MINOR << 16) |
(LIB_VER_REV << 16) | LIB_VER_PATCH;
}
/**
* \brief A simple test to see if a given file is a VHD
*
* \param [in] f file to test
*
* \retval 1 if f is a VHD
* \retval 0 if f is not a VHD
*/
MVHDAPI int
mvhd_file_is_vhd(FILE* f)
{
uint8_t con_str[8];
if (f == NULL) {
return 0;
}
mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END);
fread(con_str, sizeof con_str, 1, f);
if (mvhd_is_conectix_str(con_str)) {
return 1;
}
return 0;
}
MVHDAPI MVHDGeom
mvhd_calculate_geometry(uint64_t size)
{
MVHDGeom chs;
uint32_t ts = (uint32_t)(size / MVHD_SECTOR_SIZE);
uint32_t spt, heads, cyl, cth;
if (ts > 65535 * 16 * 255) {
ts = 65535 * 16 * 255;
}
if (ts >= 65535 * 16 * 63) {
spt = 255;
heads = 16;
@@ -358,77 +487,95 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size) {
cth = ts / spt;
}
}
cyl = cth / heads;
chs.heads = heads;
chs.spt = spt;
chs.cyl = cyl;
return chs;
}
MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) {
MVHDAPI MVHDMeta*
mvhd_open(const char* path, int readonly, int* err)
{
MVHDError open_err;
MVHDMeta *vhdm = calloc(sizeof *vhdm, 1);
if (vhdm == NULL) {
*err = MVHD_ERR_MEM;
goto end;
}
if (strlen(path) >= sizeof vhdm->filename) {
*err = MVHD_ERR_PATH_LEN;
goto cleanup_vhdm;
}
//This is safe, as we've just checked for potential overflow above
strcpy(vhdm->filename, path);
vhdm->f = readonly ? mvhd_fopen((const char*)vhdm->filename, "rb", err) : mvhd_fopen((const char*)vhdm->filename, "rb+", err);
if (readonly) {
vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb", err);
} else {
vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb+", err);
}
if (vhdm->f == NULL) {
/* note, mvhd_fopen sets err for us */
goto cleanup_vhdm;
}
vhdm->readonly = readonly;
if (!mvhd_file_is_vhd(vhdm->f)) {
*err = MVHD_ERR_NOT_VHD;
goto cleanup_file;
}
mvhd_read_footer(vhdm);
if (!mvhd_footer_checksum_valid(vhdm)) {
read_footer(vhdm);
if (!footer_checksum_valid(vhdm)) {
*err = MVHD_ERR_FOOTER_CHECKSUM;
goto cleanup_file;
}
if (vhdm->footer.disk_type == MVHD_TYPE_DIFF || vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) {
mvhd_read_sparse_header(vhdm);
if (!mvhd_sparse_checksum_valid(vhdm)) {
read_sparse_header(vhdm);
if (!sparse_checksum_valid(vhdm)) {
*err = MVHD_ERR_SPARSE_CHECKSUM;
goto cleanup_file;
}
if (mvhd_read_bat(vhdm, &open_err) == -1) {
if (read_bat(vhdm, &open_err) == -1) {
*err = open_err;
goto cleanup_file;
}
mvhd_calc_sparse_values(vhdm);
if (mvhd_init_sector_bitmap(vhdm, &open_err) == -1) {
calc_sparse_values(vhdm);
if (init_sector_bitmap(vhdm, &open_err) == -1) {
*err = open_err;
goto cleanup_bat;
}
} else if (vhdm->footer.disk_type != MVHD_TYPE_FIXED) {
*err = MVHD_ERR_TYPE;
goto cleanup_bitmap;
}
mvhd_assign_io_funcs(vhdm);
assign_io_funcs(vhdm);
vhdm->format_buffer.zero_data = calloc(64, MVHD_SECTOR_SIZE);
if (vhdm->format_buffer.zero_data == NULL) {
*err = MVHD_ERR_MEM;
goto cleanup_bitmap;
}
vhdm->format_buffer.sector_count = 64;
if (vhdm->footer.disk_type == MVHD_TYPE_DIFF) {
char* par_path = mvhd_get_diff_parent_path(vhdm, err);
char* par_path = get_diff_parent_path(vhdm, err);
if (par_path == NULL) {
goto cleanup_format_buff;
}
uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err);
if (*err != 0) {
goto cleanup_format_buff;
}
if (vhdm->sparse.par_timestamp != par_mod_ts) {
/* The last-modified timestamp is to fragile to make this a fatal error.
Instead, we inform the caller of the potential problem. */
@@ -438,57 +585,78 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) {
if (vhdm->parent == NULL) {
goto cleanup_format_buff;
}
if (memcmp(vhdm->sparse.par_uuid, vhdm->parent->footer.uuid, sizeof vhdm->sparse.par_uuid) != 0) {
*err = MVHD_ERR_INVALID_PAR_UUID;
goto cleanup_format_buff;
}
}
/* If we've reached this point, we are good to go, so skip the cleanup steps */
/*
* If we've reached this point, we are good to go,
* so skip the cleanup steps.
*/
goto end;
cleanup_format_buff:
free(vhdm->format_buffer.zero_data);
vhdm->format_buffer.zero_data = NULL;
cleanup_bitmap:
free(vhdm->bitmap.curr_bitmap);
vhdm->bitmap.curr_bitmap = NULL;
cleanup_bat:
free(vhdm->block_offset);
vhdm->block_offset = NULL;
cleanup_file:
fclose(vhdm->f);
vhdm->f = NULL;
cleanup_vhdm:
free(vhdm);
vhdm = NULL;
end:
return vhdm;
}
void mvhd_close(MVHDMeta* vhdm) {
if (vhdm != NULL) {
if (vhdm->parent != NULL) {
mvhd_close(vhdm->parent);
}
fclose(vhdm->f);
if (vhdm->block_offset != NULL) {
free(vhdm->block_offset);
vhdm->block_offset = NULL;
}
if (vhdm->bitmap.curr_bitmap != NULL) {
free(vhdm->bitmap.curr_bitmap);
vhdm->bitmap.curr_bitmap = NULL;
}
if (vhdm->format_buffer.zero_data != NULL) {
free(vhdm->format_buffer.zero_data);
vhdm->format_buffer.zero_data = NULL;
}
free(vhdm);
vhdm = NULL;
MVHDAPI void
mvhd_close(MVHDMeta* vhdm)
{
if (vhdm == NULL)
return;
if (vhdm->parent != NULL) {
mvhd_close(vhdm->parent);
}
fclose(vhdm->f);
if (vhdm->block_offset != NULL) {
free(vhdm->block_offset);
vhdm->block_offset = NULL;
}
if (vhdm->bitmap.curr_bitmap != NULL) {
free(vhdm->bitmap.curr_bitmap);
vhdm->bitmap.curr_bitmap = NULL;
}
if (vhdm->format_buffer.zero_data != NULL) {
free(vhdm->format_buffer.zero_data);
vhdm->format_buffer.zero_data = NULL;
}
free(vhdm);
}
int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
MVHDAPI int
mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err)
{
uint8_t sparse_buff[1024];
if (vhdm == NULL || err == NULL) {
*err = MVHD_ERR_INVALID_PARAMS;
return -1;
@@ -497,7 +665,7 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
*err = MVHD_ERR_TYPE;
return -1;
}
char* par_path = mvhd_get_diff_parent_path(vhdm, err);
char* par_path = get_diff_parent_path(vhdm, err);
if (par_path == NULL) {
return -1;
}
@@ -505,31 +673,53 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
if (*err != 0) {
return -1;
}
/* Update the timestamp and sparse header checksum */
vhdm->sparse.par_timestamp = par_mod_ts;
vhdm->sparse.checksum = mvhd_gen_sparse_checksum(&vhdm->sparse);
/* Generate and write the updated sparse header */
mvhd_header_to_buffer(&vhdm->sparse, sparse_buff);
mvhd_fseeko64(vhdm->f, (int64_t)vhdm->footer.data_offset, SEEK_SET);
fwrite(sparse_buff, sizeof sparse_buff, 1, vhdm->f);
return 0;
}
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
MVHDAPI int
mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff)
{
return vhdm->read_sectors(vhdm, offset, num_sectors, out_buff);
}
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
MVHDAPI int
mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
{
return vhdm->write_sectors(vhdm, offset, num_sectors, in_buff);
}
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors) {
MVHDAPI int
mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors)
{
int num_full = num_sectors / vhdm->format_buffer.sector_count;
int remain = num_sectors % vhdm->format_buffer.sector_count;
for (int i = 0; i < num_full; i++) {
vhdm->write_sectors(vhdm, offset, vhdm->format_buffer.sector_count, vhdm->format_buffer.zero_data);
offset += vhdm->format_buffer.sector_count;
}
vhdm->write_sectors(vhdm, offset, remain, vhdm->format_buffer.zero_data);
return 0;
}
MVHDAPI MVHDType
mvhd_get_type(MVHDMeta* vhdm)
{
return vhdm->footer.disk_type;
}

View File

@@ -1,11 +1,49 @@
/*
* MiniVHD Minimalist VHD implementation in C.
* MiniVHD is a minimalist implementation of read/write/creation
* of VHD files. It is designed to read and write to VHD files
* at a sector level. It does not enable file access, or provide
* mounting options. Those features are left to more advanced
* libraries and/or the operating system.
*
* This file is part of the MiniVHD Project.
*
* Definitions for the MiniVHD library.
*
* Version: @(#)minivhd.h 1.0.3 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MINIVHD_H
#define MINIVHD_H
# define MINIVHD_H
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
extern int mvhd_errno;
typedef enum MVHDError {
MVHD_ERR_MEM = -128,
@@ -46,6 +84,11 @@ typedef struct MVHDGeom {
uint8_t spt;
} MVHDGeom;
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*mvhd_progress_callback)(uint32_t current_sector, uint32_t total_sectors);
typedef struct MVHDCreationOptions {
@@ -60,6 +103,42 @@ typedef struct MVHDCreationOptions {
typedef struct MVHDMeta MVHDMeta;
extern int mvhd_errno;
/* Shared-library madness. */
//#if defined(_WIN32)
//# ifdef STATIC
# define MVHDAPI /*nothing*/
//# else
//# ifdef BUILDING_LIBRARY
//# define MVHDAPI __declspec(dllexport)
//# else
//# define MVHDAPI __declspec(dllimport)
//# endif
//# endif
//#elif defined(__GNUC__)
//# ifdef BUILDING_LIBRARY
//# define MVHDAPI __attribute__((visibility("default")))
//# else
//# define MVHDAPI /*nothing*/
//# endif
//#else
//# define MVHDAPI /*nothing*/
//#endif
/**
* \brief Return the library version as a string
*/
MVHDAPI const char *mvhd_version(void);
/**
* \brief Return the library version as a number
*/
MVHDAPI uint32_t mvhd_version_id(void);
/**
* \brief Output a string from a MiniVHD error number
*
@@ -67,17 +146,26 @@ typedef struct MVHDMeta MVHDMeta;
*
* \return Error string
*/
const char* mvhd_strerr(MVHDError err);
MVHDAPI const char* mvhd_strerr(MVHDError err);
/**
* \brief A simple test to see if a given file is a VHD
*
* \param [in] f file to test
*
* \retval true if f is a VHD
* \retval false if f is not a VHD
* \retval 1 if f is a VHD
* \retval 0 if f is not a VHD
*/
bool mvhd_file_is_vhd(FILE* f);
MVHDAPI int mvhd_file_is_vhd(FILE* f);
/**
* \brief Return the file type of the given file
*
* \param [in] vhdm VHD to check.
*
* \retval one of the defined MVHDType values
*/
MVHDAPI MVHDType mvhd_get_type(MVHDMeta* vhdm);
/**
* \brief Open a VHD image for reading and/or writing
@@ -89,7 +177,7 @@ bool mvhd_file_is_vhd(FILE* f);
*
* \param [in] Absolute path to VHD file. Relative path will cause issues when opening
* a differencing VHD file
* \param [in] readonly set this to true to open the VHD in a read only manner
* \param [in] readonly set this to 1 to open the VHD in a read only manner
* \param [out] err will be set if the VHD fails to open. Value could be one of
* MVHD_ERR_MEM, MVHD_ERR_FILE, MVHD_ERR_NOT_VHD, MVHD_ERR_FOOTER_CHECKSUM, MVHD_ERR_SPARSE_CHECKSUM,
* MVHD_ERR_TYPE, MVHD_ERR_TIMESTAMP
@@ -98,7 +186,7 @@ bool mvhd_file_is_vhd(FILE* f);
* \return MVHDMeta pointer. If NULL, check err. err may also be set to MVHD_ERR_TIMESTAMP if
* opening a differencing VHD.
*/
MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
MVHDAPI MVHDMeta* mvhd_open(const char* path, int readonly, int* err);
/**
* \brief Update the parent modified timestamp in the VHD file
@@ -116,7 +204,7 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
*
* \return non-zero on error, 0 on success
*/
int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
MVHDAPI int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
/**
* \brief Create a fixed VHD image
@@ -128,7 +216,7 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
*
* \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback);
MVHDAPI MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback);
/**
* \brief Create sparse (dynamic) VHD image.
@@ -139,7 +227,7 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_prog
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
MVHDAPI MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
/**
* \brief Create differencing VHD imagee.
@@ -150,7 +238,7 @@ MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
MVHDAPI MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
/**
* \brief Create a VHD using the provided options
@@ -162,14 +250,14 @@ MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
*
* \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err);
MVHDAPI MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err);
/**
* \brief Safely close a VHD image
*
* \param [in] vhdm MiniVHD data structure to close
*/
void mvhd_close(MVHDMeta* vhdm);
MVHDAPI void mvhd_close(MVHDMeta* vhdm);
/**
* \brief Calculate hard disk geometry from a provided size
@@ -189,7 +277,47 @@ void mvhd_close(MVHDMeta* vhdm);
*
* \return MVHDGeom the calculated geometry. This can be used in the appropriate create functions.
*/
MVHDGeom mvhd_calculate_geometry(uint64_t size);
MVHDAPI MVHDGeom mvhd_calculate_geometry(uint64_t size);
/**
* \brief Get the CHS geometry from the image
*
* \param [in] vhdm MiniVHD data structure
*
* \return The CHS geometry as stored in the image
*/
MVHDAPI MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm);
/**
* \brief Get the 'current_size' value from the image
*
* Note that the size returned may not match the size calculated from the
* CHS geometry. It is up to the caller to decide how best to handle this.
*
* \param [in] vhdm MiniVHD data structure
*
* \return The 'current_size' value in bytes, as stored in the image.
* Note, this may not match the CHS geometry.
*/
MVHDAPI uint64_t mvhd_get_current_size(MVHDMeta* vhdm);
/**
* \brief Calculate CHS geometry size in bytes
*
* \param [in] geom the CHS geometry to calculate
*
* \return the size in bytes
*/
MVHDAPI uint64_t mvhd_calc_size_bytes(MVHDGeom *geom);
/**
* \brief Calculate CHS geometry size in sectors
*
* \param [in] geom the CHS geometry to calculate
*
* \return the size in sectors
*/
MVHDAPI uint32_t mvhd_calc_size_sectors(MVHDGeom *geom);
/**
* \brief Convert a raw disk image to a fixed VHD image
@@ -200,7 +328,7 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size);
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
MVHDAPI MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
/**
* \brief Convert a raw disk image to a sparse VHD image
@@ -211,7 +339,7 @@ MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
MVHDAPI MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
/**
* \brief Convert a VHD image to a raw disk image
@@ -222,7 +350,7 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns the raw disk image FILE pointer
*/
FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err);
MVHDAPI FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err);
/**
* \brief Read sectors from VHD file
@@ -236,7 +364,7 @@ FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path,
*
* \return the number of sectors that were not read, or zero
*/
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
MVHDAPI int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Write sectors to VHD file
@@ -250,7 +378,7 @@ int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* ou
*
* \return the number of sectors that were not written, or zero
*/
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
MVHDAPI int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Write zeroed sectors to VHD file
@@ -265,5 +393,11 @@ int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* i
*
* \return the number of sectors that were not written, or zero
*/
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
MVHDAPI int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
#ifdef __cplusplus
}
#endif
#endif /*MINIVHD_H*/

View File

@@ -1,8 +0,0 @@
#ifndef MINIVHD_CREATE_H
#define MINIVHD_CREATE_H
#include <stdio.h>
#include "minivhd.h"
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback);
#endif

View File

@@ -1,96 +0,0 @@
#ifndef MINIVHD_INTERNAL_H
#define MINIVHD_INTERNAL_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#define MVHD_FOOTER_SIZE 512
#define MVHD_SPARSE_SIZE 1024
#define MVHD_SECTOR_SIZE 512
#define MVHD_BAT_ENT_PER_SECT 128
#define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000
#define MVHD_SPARSE_BLK 0xffffffff
/* For simplicity, we don't handle paths longer than this
* Note, this is the max path in characters, as that is what
* Windows uses
*/
#define MVHD_MAX_PATH_CHARS 260
#define MVHD_MAX_PATH_BYTES 1040
#define MVHD_DIF_LOC_W2RU 0x57327275
#define MVHD_DIF_LOC_W2KU 0x57326B75
typedef struct MVHDSectorBitmap {
uint8_t* curr_bitmap;
int sector_count;
int curr_block;
} MVHDSectorBitmap;
typedef struct MVHDFooter {
uint8_t cookie[8];
uint32_t features;
uint32_t fi_fmt_vers;
uint64_t data_offset;
uint32_t timestamp;
uint8_t cr_app[4];
uint32_t cr_vers;
uint8_t cr_host_os[4];
uint64_t orig_sz;
uint64_t curr_sz;
struct {
uint16_t cyl;
uint8_t heads;
uint8_t spt;
} geom;
uint32_t disk_type;
uint32_t checksum;
uint8_t uuid[16];
uint8_t saved_st;
uint8_t reserved[427];
} MVHDFooter;
typedef struct MVHDSparseHeader {
uint8_t cookie[8];
uint64_t data_offset;
uint64_t bat_offset;
uint32_t head_vers;
uint32_t max_bat_ent;
uint32_t block_sz;
uint32_t checksum;
uint8_t par_uuid[16];
uint32_t par_timestamp;
uint32_t reserved_1;
uint8_t par_utf16_name[512];
struct {
uint32_t plat_code;
uint32_t plat_data_space;
uint32_t plat_data_len;
uint32_t reserved;
uint64_t plat_data_offset;
} par_loc_entry[8];
uint8_t reserved_2[256];
} MVHDSparseHeader;
typedef struct MVHDMeta MVHDMeta;
struct MVHDMeta {
FILE* f;
bool readonly;
char filename[MVHD_MAX_PATH_BYTES];
struct MVHDMeta* parent;
MVHDFooter footer;
MVHDSparseHeader sparse;
uint32_t* block_offset;
int sect_per_block;
MVHDSectorBitmap bitmap;
int (*read_sectors)(MVHDMeta*, uint32_t, int, void*);
int (*write_sectors)(MVHDMeta*, uint32_t, int, void*);
struct {
uint8_t* zero_data;
int sector_count;
} format_buffer;
};
#endif

View File

@@ -1,28 +1,61 @@
/**
* \file
* \brief Sector reading and writing implementations
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Sector reading and writing implementations.
*
* Version: @(#)io.c 1.0.3 2021/04/16
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include <time.h>
#include "minivhd.h"
#include "internal.h"
/* The following bit array macros adapted from
http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html */
#define VHD_SETBIT(A,k) ( A[(k/8)] |= (0x80 >> (k%8)) )
#define VHD_CLEARBIT(A,k) ( A[(k/8)] &= ~(0x80 >> (k%8)) )
#define VHD_TESTBIT(A,k) ( A[(k/8)] & (0x80 >> (k%8)) )
/*
* The following bit array macros adapted from:
*
* http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html
*/
#define VHD_SETBIT(A,k) ( A[(k>>3)] |= (0x80 >> (k&7)) )
#define VHD_CLEARBIT(A,k) ( A[(k>>3)] &= ~(0x80 >> (k&7)) )
#define VHD_TESTBIT(A,k) ( A[(k>>3)] & (0x80 >> (k&7)) )
static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect);
static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk);
static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk);
static void mvhd_create_block(MVHDMeta* vhdm, int blk);
static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm);
/**
* \brief Check that we will not be overflowing buffers
@@ -34,22 +67,30 @@ static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm);
* This may be lower than num_sectors if offset + num_sectors >= total_sectors
* \param [out] trunc_sectors The number of sectors truncated if transfer_sectors < num_sectors
*/
static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect) {
static inline void
check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect)
{
*transfer_sect = num_sectors;
*trunc_sect = 0;
if ((total_sectors - offset) < (uint32_t)*transfer_sect) {
*transfer_sect = total_sectors - offset;
*trunc_sect = num_sectors - *transfer_sect;
}
}
void mvhd_write_empty_sectors(FILE* f, int sector_count) {
void
mvhd_write_empty_sectors(FILE* f, int sector_count)
{
uint8_t zero_bytes[MVHD_SECTOR_SIZE] = {0};
for (int i = 0; i < sector_count; i++) {
fwrite(zero_bytes, sizeof zero_bytes, 1, f);
}
}
/**
* \brief Read the sector bitmap for a block.
*
@@ -59,22 +100,28 @@ void mvhd_write_empty_sectors(FILE* f, int sector_count) {
* \param [in] vhdm MiniVHD data structure
* \param [in] blk The block for which to read the sector bitmap from
*/
static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk) {
static void
read_sect_bitmap(MVHDMeta* vhdm, int blk)
{
if (vhdm->block_offset[blk] != MVHD_SPARSE_BLK) {
mvhd_fseeko64(vhdm->f, (uint64_t)vhdm->block_offset[blk] * MVHD_SECTOR_SIZE, SEEK_SET);
(void) !fread(vhdm->bitmap.curr_bitmap, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE, 1, vhdm->f);
} else {
memset(vhdm->bitmap.curr_bitmap, 0, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE);
}
vhdm->bitmap.curr_block = blk;
}
/**
* \brief Write the current sector bitmap in memory to file
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) {
static void
write_curr_sect_bitmap(MVHDMeta* vhdm)
{
if (vhdm->bitmap.curr_block >= 0) {
int64_t abs_offset = (int64_t)vhdm->block_offset[vhdm->bitmap.curr_block] * MVHD_SECTOR_SIZE;
mvhd_fseeko64(vhdm->f, abs_offset, SEEK_SET);
@@ -82,19 +129,24 @@ static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) {
}
}
/**
* \brief Write block offset from memory into file
*
* \param [in] vhdm MiniVHD data structure
* \param [in] blk The block for which to write the offset for
*/
static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) {
static void
write_bat_entry(MVHDMeta* vhdm, int blk)
{
uint64_t table_offset = vhdm->sparse.bat_offset + ((uint64_t)blk * sizeof *vhdm->block_offset);
uint32_t offset = mvhd_to_be32(vhdm->block_offset[blk]);
mvhd_fseeko64(vhdm->f, table_offset, SEEK_SET);
fwrite(&offset, sizeof offset, 1, vhdm->f);
}
/**
* \brief Create an empty block in a sparse or differencing VHD image
*
@@ -109,18 +161,23 @@ static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) {
* \param [in] vhdm MiniVHD data structure
* \param [in] blk The block number to create
*/
static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
static void
create_block(MVHDMeta* vhdm, int blk)
{
uint8_t footer[MVHD_FOOTER_SIZE];
/* Seek to where the footer SHOULD be */
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
(void) !fread(footer, sizeof footer, 1, vhdm->f);
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
if (!mvhd_is_conectix_str(footer)) {
/* Oh dear. We use the header instead, since something has gone wrong at the footer */
mvhd_fseeko64(vhdm->f, 0, SEEK_SET);
(void) !fread(footer, sizeof footer, 1, vhdm->f);
mvhd_fseeko64(vhdm->f, 0, SEEK_END);
}
int64_t abs_offset = mvhd_ftello64(vhdm->f);
if (abs_offset % MVHD_SECTOR_SIZE != 0) {
/* Yikes! We're supposed to be on a sector boundary. Add some padding */
@@ -131,52 +188,68 @@ static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
}
abs_offset += padding_amount;
}
uint32_t sect_offset = (uint32_t)(abs_offset / MVHD_SECTOR_SIZE);
int blk_size_sectors = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE;
mvhd_write_empty_sectors(vhdm->f, vhdm->bitmap.sector_count + blk_size_sectors);
/* Add a bit of padding. That's what Windows appears to do, although it's not strictly necessary... */
mvhd_write_empty_sectors(vhdm->f, 5);
/* And we finish with the footer */
fwrite(footer, sizeof footer, 1, vhdm->f);
/* We no longer have a sparse block. Update that BAT! */
vhdm->block_offset[blk] = sect_offset;
mvhd_write_bat_entry(vhdm, blk);
write_bat_entry(vhdm, blk);
}
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
int
mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
int64_t addr;
int transfer_sectors, truncated_sectors;
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
addr = (int64_t)offset * MVHD_SECTOR_SIZE;
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
(void) !fread(out_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f);
return truncated_sectors;
}
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
int
mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff)
{
int transfer_sectors, truncated_sectors;
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
uint8_t* buff = (uint8_t*)out_buff;
int64_t addr;
uint32_t s, ls;
int blk, prev_blk, sib;
ls = offset + transfer_sectors;
prev_blk = -1;
for (s = offset; s < ls; s++) {
blk = s / vhdm->sect_per_block;
sib = s % vhdm->sect_per_block;
if (blk != prev_blk) {
prev_blk = blk;
if (vhdm->bitmap.curr_block != blk) {
mvhd_read_sect_bitmap(vhdm, blk);
read_sect_bitmap(vhdm, blk);
mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR);
} else {
addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE;
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
}
}
if (VHD_TESTBIT(vhdm->bitmap.curr_bitmap, sib)) {
(void) !fread(buff, MVHD_SECTOR_SIZE, 1, vhdm->f);
} else {
@@ -185,29 +258,37 @@ int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out
}
buff += MVHD_SECTOR_SIZE;
}
return truncated_sectors;
}
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
int
mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff)
{
int transfer_sectors, truncated_sectors;
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
uint8_t* buff = (uint8_t*)out_buff;
MVHDMeta* curr_vhdm = vhdm;
uint32_t s, ls;
int blk, sib;
ls = offset + transfer_sectors;
for (s = offset; s < ls; s++) {
while (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF) {
blk = s / curr_vhdm->sect_per_block;
sib = s % curr_vhdm->sect_per_block;
if (curr_vhdm->bitmap.curr_block != blk) {
mvhd_read_sect_bitmap(curr_vhdm, blk);
read_sect_bitmap(curr_vhdm, blk);
}
if (!VHD_TESTBIT(curr_vhdm->bitmap.curr_bitmap, sib)) {
curr_vhdm = curr_vhdm->parent;
} else { break; }
}
/* We handle actual sector reading using the fixed or sparse functions,
as a differencing VHD is also a sparse VHD */
if (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF || curr_vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) {
@@ -215,49 +296,65 @@ int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_b
} else {
mvhd_fixed_read(curr_vhdm, s, 1, buff);
}
curr_vhdm = vhdm;
buff += MVHD_SECTOR_SIZE;
}
return truncated_sectors;
}
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
int
mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
{
int64_t addr;
int transfer_sectors, truncated_sectors;
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
addr = (int64_t)offset * MVHD_SECTOR_SIZE;
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
fwrite(in_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f);
return truncated_sectors;
}
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
int
mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
{
int transfer_sectors, truncated_sectors;
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
uint8_t* buff = (uint8_t*)in_buff;
int64_t addr;
uint32_t s, ls;
int blk, prev_blk, sib;
ls = offset + transfer_sectors;
prev_blk = -1;
for (s = offset; s < ls; s++) {
blk = s / vhdm->sect_per_block;
sib = s % vhdm->sect_per_block;
if (vhdm->bitmap.curr_block != blk && prev_blk >= 0) {
/* Write the sector bitmap for the previous block, before we replace it. */
mvhd_write_curr_sect_bitmap(vhdm);
write_curr_sect_bitmap(vhdm);
}
if (vhdm->block_offset[blk] == MVHD_SPARSE_BLK) {
/* "read" the sector bitmap first, before creating a new block, as the bitmap will be
zero either way */
mvhd_read_sect_bitmap(vhdm, blk);
mvhd_create_block(vhdm, blk);
read_sect_bitmap(vhdm, blk);
create_block(vhdm, blk);
}
if (blk != prev_blk) {
if (vhdm->bitmap.curr_block != blk) {
mvhd_read_sect_bitmap(vhdm, blk);
read_sect_bitmap(vhdm, blk);
mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR);
} else {
addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE;
@@ -265,15 +362,26 @@ int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, voi
}
prev_blk = blk;
}
fwrite(buff, MVHD_SECTOR_SIZE, 1, vhdm->f);
VHD_SETBIT(vhdm->bitmap.curr_bitmap, sib);
buff += MVHD_SECTOR_SIZE;
}
/* And write the sector bitmap for the last block we visited to disk */
mvhd_write_curr_sect_bitmap(vhdm);
write_curr_sect_bitmap(vhdm);
return truncated_sectors;
}
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
int
mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
{
(void)vhdm;
(void)offset;
(void)num_sectors;
(void)in_buff;
return 0;
}

View File

@@ -1,132 +0,0 @@
#ifndef MINIVHD_IO_H
#define MINIVHD_IO_H
#include "minivhd.h"
/**
* \brief Write zero filled sectors to file.
*
* Note, the caller should set the file position before calling this
* function for correct operation.
*
* \param [in] f File to write sectors to
* \param [in] sector_count The number of sectors to write
*/
void mvhd_write_empty_sectors(FILE* f, int sector_count);
/**
* \brief Read a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a sparse VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* This function implements the logic to read sectors from the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* read could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a differencing VHD image
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is read from the
* child image only if it is newer than the data stored in the parent image.
*
* This function implements the logic to read sectors from the child, or a parent image.
* Differencing images may have a differencing image as a parent, creating a chain of images.
* There is no theoretical chain length limit, although I do not consider long chains to be
* advisable. Verifying the parent-child relationship is not very robust.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Write to a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Write to a sparse or differencing VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is always written
* to the child image. This makes writing to differencing images essentially identical to
* writing to sparse images, hence they use the same function.
*
* This function implements the logic to write sectors to the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* write operation could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief A no-op function to "write" to read-only VHD images
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
#endif

View File

@@ -1,167 +0,0 @@
/**
* \file
* \brief Header and footer serialize/deserialize functions
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "minivhd_util.h"
#include "minivhd_internal.h"
/* Read data from footer into the struct members, swapping endian where necessary
Note: order matters here! We must read each field in the order the struct is in.
Doing this may be less elegant than performing a memcpy to a packed struct, but
it avoids potential data alignment issues, and the endian swapping allows us to
use the fields directly. */
static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer);
static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer);
/**
* \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary
*
* \param [out] struct_memb struct member to save the field to
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call
*/
static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) {
memcpy(struct_memb, *buffer, memb_size);
if (req_endian) {
switch (memb_size) {
case 2:
*(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb));
break;
}
}
*buffer += memb_size;
}
/**
* \brief Save a struct member into a buffer, converting endian if necessary
*
* \param [in] struct_memb struct member read from
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call
*/
static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) {
uint8_t *buf_ptr = *buffer;
memcpy(buf_ptr, struct_memb, memb_size);
if (req_endian) {
switch (memb_size) {
case 2:
*((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb));
break;
}
}
buf_ptr += memb_size;
*buffer = buf_ptr;
}
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr);
mvhd_next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
mvhd_next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr);
mvhd_next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
mvhd_next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}

View File

@@ -1,38 +0,0 @@
#ifndef MINIVHD_STRUCT_RW_H
#define MINIVHD_STRUCT_RW_H
#include "minivhd_internal.h"
/**
* \brief Save the contents of a VHD footer from a buffer to a struct
*
* \param [out] footer save contents of buffer into footer
* \param [in] buffer VHD footer in raw bytes
*/
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header from a buffer to a struct
*
* \param [out] header save contents of buffer into header
* \param [in] buffer VHD header in raw bytes
*/
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer);
/**
* \brief Save the contents of a VHD footer struct to a buffer
*
* \param [in] footer save contents of struct into buffer
* \param [out] buffer VHD footer in raw bytes
*/
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header struct to a buffer
*
* \param [in] header save contents of struct into buffer
* \param [out] buffer VHD sparse header in raw bytes
*/
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer);
#endif

View File

@@ -1,46 +1,90 @@
/**
* \file
* \brief Utility functions
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Utility functions.
*
* Version: @(#)util.c 1.0.4 2021/04/16
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "libxml2_encoding.h"
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include "minivhd.h"
#include "internal.h"
#include "xml2_encoding.h"
const char MVHD_CONECTIX_COOKIE[] = "conectix";
const char MVHD_CREATOR[] = "pcem";
const char MVHD_CREATOR_HOST_OS[] = "Wi2k";
const char MVHD_CXSPARSE_COOKIE[] = "cxsparse";
uint16_t mvhd_from_be16(uint16_t val) {
uint16_t
mvhd_from_be16(uint16_t val)
{
uint8_t *tmp = (uint8_t*)&val;
uint16_t ret = 0;
ret |= (uint16_t)tmp[0] << 8;
ret |= (uint16_t)tmp[1] << 0;
return ret;
}
uint32_t mvhd_from_be32(uint32_t val) {
uint32_t
mvhd_from_be32(uint32_t val)
{
uint8_t *tmp = (uint8_t*)&val;
uint32_t ret = 0;
ret = (uint32_t)tmp[0] << 24;
ret |= (uint32_t)tmp[1] << 16;
ret |= (uint32_t)tmp[2] << 8;
ret |= (uint32_t)tmp[3] << 0;
return ret;
}
uint64_t mvhd_from_be64(uint64_t val) {
uint64_t
mvhd_from_be64(uint64_t val)
{
uint8_t *tmp = (uint8_t*)&val;
uint64_t ret = 0;
ret = (uint64_t)tmp[0] << 56;
ret |= (uint64_t)tmp[1] << 48;
ret |= (uint64_t)tmp[2] << 40;
@@ -49,27 +93,45 @@ uint64_t mvhd_from_be64(uint64_t val) {
ret |= (uint64_t)tmp[5] << 16;
ret |= (uint64_t)tmp[6] << 8;
ret |= (uint64_t)tmp[7] << 0;
return ret;
}
uint16_t mvhd_to_be16(uint16_t val) {
uint16_t
mvhd_to_be16(uint16_t val)
{
uint16_t ret = 0;
uint8_t *tmp = (uint8_t*)&ret;
tmp[0] = (val & 0xff00) >> 8;
tmp[1] = (val & 0x00ff) >> 0;
return ret;
}
uint32_t mvhd_to_be32(uint32_t val) {
uint32_t
mvhd_to_be32(uint32_t val)
{
uint32_t ret = 0;
uint8_t *tmp = (uint8_t*)&ret;
tmp[0] = (val & 0xff000000) >> 24;
tmp[1] = (val & 0x00ff0000) >> 16;
tmp[2] = (val & 0x0000ff00) >> 8;
tmp[3] = (val & 0x000000ff) >> 0;
return ret;
}
uint64_t mvhd_to_be64(uint64_t val) {
uint64_t
mvhd_to_be64(uint64_t val)
{
uint64_t ret = 0;
uint8_t *tmp = (uint8_t*)&ret;
tmp[0] = (uint8_t)((val & 0xff00000000000000) >> 56);
tmp[1] = (uint8_t)((val & 0x00ff000000000000) >> 48);
tmp[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40);
@@ -78,21 +140,17 @@ uint64_t mvhd_to_be64(uint64_t val) {
tmp[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16);
tmp[6] = (uint8_t)((val & 0x000000000000ff00) >> 8);
tmp[7] = (uint8_t)((val & 0x00000000000000ff) >> 0);
return ret;
}
bool mvhd_is_conectix_str(const void* buffer) {
if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) {
return true;
} else {
return false;
}
}
void mvhd_generate_uuid(uint8_t* uuid)
void
mvhd_generate_uuid(uint8_t* uuid)
{
/* We aren't doing crypto here, so using system time as seed should be good enough */
srand((unsigned int)time(0));
for (int n = 0; n < 16; n++) {
uuid[n] = rand();
}
@@ -102,34 +160,50 @@ void mvhd_generate_uuid(uint8_t* uuid)
uuid[8] |= 0x80; /* Variant 1 */
}
uint32_t vhd_calc_timestamp(void)
{
time_t start_time;
time_t curr_time;
double vhd_time;
start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */
curr_time = time(NULL);
vhd_time = difftime(curr_time, start_time);
return (uint32_t)vhd_time;
}
uint32_t mvhd_epoch_to_vhd_ts(time_t ts) {
time_t start_time = MVHD_START_TS;
if (ts < start_time) {
return start_time;
}
double vhd_time = difftime(ts, start_time);
uint32_t
vhd_calc_timestamp(void)
{
time_t start_time;
time_t curr_time;
double vhd_time;
start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */
curr_time = time(NULL);
vhd_time = difftime(curr_time, start_time);
return (uint32_t)vhd_time;
}
time_t vhd_get_created_time(MVHDMeta *vhdm)
uint32_t
mvhd_epoch_to_vhd_ts(time_t ts)
{
time_t vhd_time = (time_t)vhdm->footer.timestamp;
time_t vhd_time_unix = MVHD_START_TS + vhd_time;
return vhd_time_unix;
time_t start_time = MVHD_START_TS;
double vhd_time;
if (ts < start_time)
return (uint32_t)start_time;
vhd_time = difftime(ts, start_time);
return (uint32_t)vhd_time;
}
FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
time_t
vhd_get_created_time(MVHDMeta *vhdm)
{
time_t vhd_time = (time_t)vhdm->footer.timestamp;
time_t vhd_time_unix = MVHD_START_TS + vhd_time;
return vhd_time_unix;
}
FILE*
mvhd_fopen(const char* path, const char* mode, int* err)
{
FILE* f = NULL;
#ifdef _WIN32
size_t path_len = strlen(path);
@@ -140,6 +214,7 @@ FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
int new_mode_len = (sizeof mode_str) - 2;
int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len);
int mode_res = UTF8ToUTF16LE((unsigned char*)mode_str, &new_mode_len, (const unsigned char*)mode, (int*)&mode_len);
if (path_res > 0 && mode_res > 0) {
f = _wfopen(new_path, mode_str);
if (f == NULL) {
@@ -160,10 +235,14 @@ FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
*err = MVHD_ERR_FILE;
}
#endif
return f;
}
void mvhd_set_encoding_err(int encoding_retval, int* err) {
void
mvhd_set_encoding_err(int encoding_retval, int* err)
{
if (encoding_retval == -1) {
*err = MVHD_ERR_UTF_SIZE;
} else if (encoding_retval == -2) {
@@ -171,87 +250,162 @@ void mvhd_set_encoding_err(int encoding_retval, int* err) {
}
}
uint64_t mvhd_calc_size_bytes(MVHDGeom *geom) {
uint64_t
mvhd_calc_size_bytes(MVHDGeom *geom)
{
uint64_t img_size = (uint64_t)geom->cyl * (uint64_t)geom->heads * (uint64_t)geom->spt * (uint64_t)MVHD_SECTOR_SIZE;
return img_size;
}
uint32_t mvhd_calc_size_sectors(MVHDGeom *geom) {
uint32_t
mvhd_calc_size_sectors(MVHDGeom *geom)
{
uint32_t sector_size = (uint32_t)geom->cyl * (uint32_t)geom->heads * (uint32_t)geom->spt;
return sector_size;
}
MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm) {
MVHDGeom geometry = { .cyl = vhdm->footer.geom.cyl, .heads = vhdm->footer.geom.heads, .spt = vhdm->footer.geom.spt };
MVHDAPI MVHDGeom
mvhd_get_geometry(MVHDMeta* vhdm)
{
MVHDGeom geometry = {
.cyl = vhdm->footer.geom.cyl,
.heads = vhdm->footer.geom.heads,
.spt = vhdm->footer.geom.spt
};
return geometry;
}
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer) {
MVHDAPI uint64_t
mvhd_get_current_size(MVHDMeta* vhdm)
{
return vhdm->footer.curr_sz;
}
uint32_t
mvhd_gen_footer_checksum(MVHDFooter* footer)
{
uint32_t new_chk = 0;
uint32_t orig_chk = footer->checksum;
footer->checksum = 0;
uint8_t* footer_bytes = (uint8_t*)footer;
for (size_t i = 0; i < sizeof *footer; i++) {
for (size_t i = 0; i < sizeof *footer; i++)
new_chk += footer_bytes[i];
}
footer->checksum = orig_chk;
return ~new_chk;
}
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header) {
uint32_t
mvhd_gen_sparse_checksum(MVHDSparseHeader* header)
{
uint32_t new_chk = 0;
uint32_t orig_chk = header->checksum;
header->checksum = 0;
uint8_t* sparse_bytes = (uint8_t*)header;
for (size_t i = 0; i < sizeof *header; i++) {
new_chk += sparse_bytes[i];
}
header->checksum = orig_chk;
return ~new_chk;
}
const char* mvhd_strerr(MVHDError err) {
MVHDAPI const char*
mvhd_strerr(MVHDError err)
{
const char *s = "unknown error";
switch (err) {
case MVHD_ERR_MEM:
return "memory allocation error";
case MVHD_ERR_FILE:
return "file error";
case MVHD_ERR_NOT_VHD:
return "file is not a VHD image";
case MVHD_ERR_TYPE:
return "unsupported VHD image type";
case MVHD_ERR_FOOTER_CHECKSUM:
return "invalid VHD footer checksum";
case MVHD_ERR_SPARSE_CHECKSUM:
return "invalid VHD sparse header checksum";
case MVHD_ERR_UTF_TRANSCODING_FAILED:
return "error converting path encoding";
case MVHD_ERR_UTF_SIZE:
return "buffer size mismatch when converting path encoding";
case MVHD_ERR_PATH_REL:
return "relative path detected where absolute path expected";
case MVHD_ERR_PATH_LEN:
return "path length exceeds MVHD_MAX_PATH";
case MVHD_ERR_PAR_NOT_FOUND:
return "parent VHD image not found";
case MVHD_ERR_INVALID_PAR_UUID:
return "UUID mismatch between child and parent VHD";
case MVHD_ERR_INVALID_GEOM:
return "invalid geometry detected";
case MVHD_ERR_INVALID_SIZE:
return "invalid size";
case MVHD_ERR_INVALID_BLOCK_SIZE:
return "invalid block size";
case MVHD_ERR_INVALID_PARAMS:
return "invalid parameters passed to function";
case MVHD_ERR_CONV_SIZE:
return "error converting image. Size mismatch detechted";
default:
return "unknown error";
case MVHD_ERR_MEM:
s = "memory allocation error";
break;
case MVHD_ERR_FILE:
s = "file error";
break;
case MVHD_ERR_NOT_VHD:
s = "file is not a VHD image";
break;
case MVHD_ERR_TYPE:
s = "unsupported VHD image type";
break;
case MVHD_ERR_FOOTER_CHECKSUM:
s = "invalid VHD footer checksum";
break;
case MVHD_ERR_SPARSE_CHECKSUM:
s = "invalid VHD sparse header checksum";
break;
case MVHD_ERR_UTF_TRANSCODING_FAILED:
s = "error converting path encoding";
break;
case MVHD_ERR_UTF_SIZE:
s = "buffer size mismatch when converting path encoding";
break;
case MVHD_ERR_PATH_REL:
s = "relative path detected where absolute path expected";
break;
case MVHD_ERR_PATH_LEN:
s = "path length exceeds MVHD_MAX_PATH";
break;
case MVHD_ERR_PAR_NOT_FOUND:
s = "parent VHD image not found";
break;
case MVHD_ERR_INVALID_PAR_UUID:
s = "UUID mismatch between child and parent VHD";
break;
case MVHD_ERR_INVALID_GEOM:
s = "invalid geometry detected";
break;
case MVHD_ERR_INVALID_SIZE:
s = "invalid size";
break;
case MVHD_ERR_INVALID_BLOCK_SIZE:
s = "invalid block size";
break;
case MVHD_ERR_INVALID_PARAMS:
s = "invalid parameters passed to function";
break;
case MVHD_ERR_CONV_SIZE:
s = "error converting image. Size mismatch detected";
break;
default:
break;
}
return s;
}
int64_t mvhd_ftello64(FILE* stream)
int64_t
mvhd_ftello64(FILE* stream)
{
#ifdef _MSC_VER
return _ftelli64(stream);
@@ -262,7 +416,9 @@ int64_t mvhd_ftello64(FILE* stream)
#endif
}
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
int
mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
{
#ifdef _MSC_VER
return _fseeki64(stream, offset, origin);
@@ -273,17 +429,25 @@ int mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
#endif
}
uint32_t mvhd_crc32_for_byte(uint32_t r) {
uint32_t
mvhd_crc32_for_byte(uint32_t r)
{
for (int j = 0; j < 8; ++j)
r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1;
return r ^ (uint32_t)0xFF000000L;
}
uint32_t mvhd_crc32(const void* data, size_t n_bytes) {
uint32_t
mvhd_crc32(const void* data, size_t n_bytes)
{
static uint32_t table[0x100];
if (!*table)
for (size_t i = 0; i < 0x100; ++i)
table[i] = mvhd_crc32_for_byte(i);
table[i] = mvhd_crc32_for_byte((uint32_t)i);
uint32_t crc = 0;
for (size_t i = 0; i < n_bytes; ++i)
@@ -292,7 +456,10 @@ uint32_t mvhd_crc32(const void* data, size_t n_bytes) {
return crc;
}
uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
uint32_t
mvhd_file_mod_timestamp(const char* path, int *err)
{
*err = 0;
#ifdef _WIN32
struct _stat file_stat;
@@ -300,6 +467,7 @@ uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
mvhd_utf16 new_path[260] = {0};
int new_path_len = (sizeof new_path) - 2;
int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len);
if (path_res > 0) {
int stat_res = _wstat(new_path, &file_stat);
if (stat_res != 0) {
@@ -319,6 +487,7 @@ uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
#else
struct stat file_stat;
int stat_res = stat(path, &file_stat);
if (stat_res != 0) {
mvhd_errno = errno;
*err = MVHD_ERR_FILE;

View File

@@ -1,136 +0,0 @@
#ifndef MINIVHD_UTIL_H
#define MINIVHD_UTIL_H
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include "minivhd_internal.h"
#include "minivhd.h"
#define MVHD_START_TS 946684800
/**
* Functions to deal with endian issues
*/
uint16_t mvhd_from_be16(uint16_t val);
uint32_t mvhd_from_be32(uint32_t val);
uint64_t mvhd_from_be64(uint64_t val);
uint16_t mvhd_to_be16(uint16_t val);
uint32_t mvhd_to_be32(uint32_t val);
uint64_t mvhd_to_be64(uint64_t val);
/**
* \brief Check if provided buffer begins with the string "conectix"
*
* \param [in] buffer The buffer to compare. Must be at least 8 bytes in length
*
* \return true if the buffer begins with "conectix"
* \return false if the buffer does not begin with "conectix"
*/
bool mvhd_is_conectix_str(const void* buffer);
/**
* \brief Generate a raw 16 byte UUID
*
* \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to
*/
void mvhd_generate_uuid(uint8_t *uuid);
/**
* \brief Calculate a VHD formatted timestamp from the current time
*/
uint32_t vhd_calc_timestamp(void);
/**
* \brief Convert an epoch timestamp to a VHD timestamp
*
* \param [in] ts epoch timestamp to convert.
*
* \return The adjusted timestamp, or 0 if the input timestamp is
* earlier that 1 Janurary 2000
*/
uint32_t mvhd_epoch_to_vhd_ts(time_t ts);
/**
* \brief Return the created time from a VHD image
*
* \param [in] vhdm Pointer to the MiniVHD metadata structure
*
* \return The created time, as a Unix timestamp
*/
time_t vhd_get_created_time(MVHDMeta *vhdm);
/**
* \brief Cross platform, unicode filepath opening
*
* This function accounts for the fact that fopen() handles file paths differently compared to other
* operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8.
*
* Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE
* encoded path and modestring.
*
* \param [in] path The filepath to open as a UTF-8 string
* \param [in] mode The mode string to use (eg: "rb+"")
* \param [out] err The error value, if an error occurrs
*
* \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err
*/
FILE* mvhd_fopen(const char* path, const char* mode, int* err);
void mvhd_set_encoding_err(int encoding_retval, int* err);
uint64_t mvhd_calc_size_bytes(MVHDGeom *geom);
uint32_t mvhd_calc_size_sectors(MVHDGeom *geom);
MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm);
/**
* \brief Generate VHD footer checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer);
/**
* \brief Generate VHD sparse header checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header);
/**
* \brief Get current position in file stream
*
* This is a portable version of the POSIX ftello64(). *
*/
int64_t mvhd_ftello64(FILE* stream);
/**
* \brief Reposition the file stream's position
*
* This is a portable version of the POSIX fseeko64(). *
*/
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin);
/**
* \brief Calculate the CRC32 of a data buffer.
*
* This function can be used for verifying data integrity.
*
* \param [in] data The data buffer
* \param [in] n_bytes The size of the data buffer in bytes
*
* \return The CRC32 of the data buffer
*/
uint32_t mvhd_crc32(const void* data, size_t n_bytes);
/**
* \brief Calculate the file modification timestamp.
*
* This function is primarily to help protect differencing VHD's
*
* \param [in] path the UTF-8 file path
* \param [out] err The error value, if an error occurrs
*
* \return The file modified timestamp, in VHD compatible timestamp.
* 'err' will be set to non-zero on error
*/
uint32_t mvhd_file_mod_timestamp(const char* path, int *err);
#endif

View File

@@ -0,0 +1,232 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Header and footer serialize/deserialize functions.
*
* Read data from footer into the struct members, swapping
* endian where necessary.
*
* NOTE: Order matters here!
* We must read each field in the order the struct is in.
* Doing this may be less elegant than performing a memcpy
* to a packed struct, but it avoids potential data alignment
* issues, and the endian swapping allows us to use the fields
* directly.
*
* Version: @(#)struct_rw.c 1.0.2 2021/04/16
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include "minivhd.h"
#include "internal.h"
/**
* \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary
*
* \param [out] struct_memb struct member to save the field to
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call
*/
static void
next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer)
{
memcpy(struct_memb, *buffer, memb_size);
if (req_endian) switch (memb_size) {
case 2:
*(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb));
break;
}
*buffer += memb_size;
}
/**
* \brief Save a struct member into a buffer, converting endian if necessary
*
* \param [in] struct_memb struct member read from
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call
*/
static void
next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer)
{
uint8_t *buf_ptr = *buffer;
memcpy(buf_ptr, struct_memb, memb_size);
if (req_endian) switch (memb_size) {
case 2:
*((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb));
break;
}
buf_ptr += memb_size;
*buffer = buf_ptr;
}
void
mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr);
next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void
mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr);
next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void
mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr);
next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr);
next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}
void
mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr);
next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr);
next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}

View File

@@ -0,0 +1,68 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Define library version and build info.
*
* Version: @(#)version.h 1.034 2021/04/16
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MINIVHD_VERSION_H
# define MINIVHD_VERSION_H
/* Library name. */
#define LIB_NAME "MiniVHD"
/* Version info. */
#define LIB_VER_MAJOR 1
#define LIB_VER_MINOR 0
#define LIB_VER_REV 3
#define LIB_VER_PATCH 0
/* Standard C preprocessor macros. */
#define STR_STRING(x) #x
#define STR(x) STR_STRING(x)
#define STR_RC(a,e) a ## , ## e
/* These are used in the application. */
#define LIB_VER_NUM LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV
#if defined(LIB_VER_PATCH) && LIB_VER_PATCH > 0
# define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.LIB_VER_PATCH
#else
# define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.0
#endif
#define LIB_VERSION STR(LIB_VER_NUM)
#define LIB_VERSION_4 STR(LIB_VER_NUM_4)
#endif /*MINIVHD_VERSION_H*/

View File

@@ -22,9 +22,19 @@
* Adapted and abridged for MiniVHD by Sherman Perry
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#define BUILDING_LIBRARY
#include "minivhd.h"
#include "internal.h"
#include "xml2_encoding.h"
static int xmlLittleEndian = 1;
/* Note: extracted from original 'void xmlInitCharEncodingHandlers(void)' function */
void xmlEncodingInit(void)
{
@@ -96,8 +106,8 @@ int UTF16LEToUTF8(unsigned char* out, int *outlen,
c += 0x10000;
}
else {
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(-2);
}
}
@@ -117,8 +127,8 @@ int UTF16LEToUTF8(unsigned char* out, int *outlen,
}
processed = (const unsigned char*) in;
}
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(*outlen);
}
@@ -163,16 +173,16 @@ int UTF8ToUTF16LE(unsigned char* outb, int *outlen,
if (d < 0x80) { c= d; trailing= 0; }
else if (d < 0xC0) {
/* trailing byte in leading position */
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(-2);
} else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
else {
/* no chance for this in UTF-16 */
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(-2);
}
@@ -225,8 +235,8 @@ int UTF8ToUTF16LE(unsigned char* outb, int *outlen,
break;
processed = in;
}
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(*outlen);
}
@@ -275,8 +285,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
}
if ((c & 0xFC00) == 0xD800) { /* surrogates */
if (in >= inend) { /* (in > inend) shouldn't happens */
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(-2);
}
if (xmlLittleEndian) {
@@ -295,8 +305,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
c += 0x10000;
}
else {
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(-2);
}
}
@@ -316,8 +326,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
}
processed = (const unsigned char*) in;
}
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(*outlen);
}
@@ -362,16 +372,16 @@ int UTF8ToUTF16BE(unsigned char* outb, int *outlen,
if (d < 0x80) { c= d; trailing= 0; }
else if (d < 0xC0) {
/* trailing byte in leading position */
*outlen = out - outstart;
*inlen = processed - instart;
*outlen = (int)(out - outstart);
*inlen = (int)(processed - instart);
return(-2);
} else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
else {
/* no chance for this in UTF-16 */
*outlen = out - outstart;
*inlen = processed - instart;
*outlen = (int)(out - outstart);
*inlen = (int)(processed - instart);
return(-2);
}
@@ -421,8 +431,8 @@ int UTF8ToUTF16BE(unsigned char* outb, int *outlen,
break;
processed = in;
}
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(*outlen);
}

View File

@@ -0,0 +1,62 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Version: @(#)xml2_encoding.h 1.0.1 2021/03/15
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef XML2_ENCODING_H
# define XML2_ENCODING_H
typedef uint16_t mvhd_utf16;
#ifdef __cplusplus
extern "C" {
#endif
void xmlEncodingInit(void);
int UTF16LEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb,
int *inlenb);
int UTF8ToUTF16LE(uint8_t *outb, int *outlen, const uint8_t *in,
int *inlen);
int UTF16BEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb,
int *inlenb);
int UTF8ToUTF16BE(uint8_t *outb, int *outlen, const uint8_t *in,
int *inlen);
#ifdef __cplusplus
}
#endif
#endif /*XML2_ENCODING_H*/

View File

@@ -33,12 +33,12 @@
#include <86box/fdc.h>
#include <86box/fdc_ext.h>
#define BIOS_ADDR (uint32_t)(device_get_config_hex20("bios_addr") & 0x000fffff)
#define BIOS_ADDR (uint32_t)(device_get_config_hex20("bios_addr") & 0x000fffff)
#define ROM_MONSTER_FDC "roms/floppy/monster-fdc/floppy_bios.bin"
typedef struct
{
rom_t bios_rom;
rom_t bios_rom;
fdc_t *fdc_pri;
fdc_t *fdc_sec;
} monster_fdc_t;
@@ -46,7 +46,7 @@ typedef struct
static void
monster_fdc_close(void *priv)
{
monster_fdc_t *dev = (monster_fdc_t *)priv;
monster_fdc_t *dev = (monster_fdc_t *) priv;
free(dev);
}
@@ -56,7 +56,7 @@ monster_fdc_init(const device_t *info)
{
monster_fdc_t *dev;
dev = (monster_fdc_t *)malloc(sizeof(monster_fdc_t));
dev = (monster_fdc_t *) malloc(sizeof(monster_fdc_t));
memset(dev, 0, sizeof(monster_fdc_t));
#if 0
@@ -86,13 +86,14 @@ monster_fdc_init(const device_t *info)
return dev;
}
static int monster_fdc_available(void)
static int
monster_fdc_available(void)
{
return rom_present(ROM_MONSTER_FDC);
}
static const device_config_t monster_fdc_config[] = {
// clang-format off
// clang-format off
#if 0
{
.name = "sec_enabled",
@@ -205,19 +206,19 @@ static const device_config_t monster_fdc_config[] = {
},
#endif
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format on
// clang-format on
};
const device_t fdc_monster_device = {
.name = "Monster FDC Floppy Drive Controller",
.name = "Monster FDC Floppy Drive Controller",
.internal_name = "monster_fdc",
.flags = DEVICE_ISA,
.local = 0,
.init = monster_fdc_init,
.close = monster_fdc_close,
.reset = NULL,
.flags = DEVICE_ISA,
.local = 0,
.init = monster_fdc_init,
.close = monster_fdc_close,
.reset = NULL,
{ .available = monster_fdc_available },
.speed_changed = NULL,
.force_redraw = NULL,
.config =monster_fdc_config
.force_redraw = NULL,
.config = monster_fdc_config
};

View File

@@ -1520,7 +1520,7 @@ struct pulse_sample {
static int pulse_limitval = 15; /* tolerance of 15% */
static struct pulse_sample psarray[FDI_MAX_ARRAY];
static int array_index;
static uint32_t total;
static uint32_t total;
static int totaldiv;
static void
@@ -2051,7 +2051,7 @@ decode_lowlevel_track(FDI *fdi, int track, struct fdi_cache *cache)
}
static unsigned char fdiid[] = { "Formatted Disk Image file" };
static int bit_rate_table[16] = { 125, 150, 250, 300, 500, 1000 };
static int bit_rate_table[16] = { 125, 150, 250, 300, 500, 1000 };
void
fdi2raw_header_free(FDI *fdi)

View File

@@ -427,16 +427,16 @@ tmacm_init(const device_t *info)
port = device_get_config_hex16("port2_addr");
switch (port) {
case 0x201:
case 0x209:
dev = gameport_add(&gameport_209_device);
break;
case 0x203:
case 0x20b:
dev = gameport_add(&gameport_20b_device);
break;
case 0x205:
case 0x20d:
dev = gameport_add(&gameport_20d_device);
break;
case 0x207:
case 0x20f:
dev = gameport_add(&gameport_20f_device);
break;
default:

View File

@@ -22,6 +22,52 @@
#ifndef EMU_KEYBOARD_H
#define EMU_KEYBOARD_H
enum {
DEV_KBD = 0,
DEV_AUX
};
/* Used by the AT / PS/2 keyboard controller, common device, keyboard, and mouse. */
typedef struct {
uint8_t wantcmd, dat, pad, pad0;
int out_new;
void *priv;
void (*poll)(void *priv);
} kbc_port_t;
/* Used by the AT / PS/2 common device, keyboard, and mouse. */
typedef struct {
const char *name; /* name of this device */
uint8_t type, inst, command, wantdata,
last_scan_code, state, resolution, rate,
cmd_queue_start, cmd_queue_end, queue_start, queue_end;
/* 6 bytes needed for mouse */
uint8_t last_data[6];
uint16_t flags;
/* Internal FIFO, not present on real devices, needed for commands that
output multiple bytes. */
uint8_t cmd_queue[16];
uint8_t queue[16];
int mode,
x, y, z, b;
int *scan;
void (*process_cmd)(void *priv);
void (*execute_bat)(void *priv);
kbc_port_t *port;
} atkbc_dev_t;
typedef struct {
const uint8_t mk[4];
const uint8_t brk[4];
@@ -139,8 +185,11 @@ extern uint8_t keyboard_set3_flags[512];
extern uint8_t keyboard_set3_all_repeat;
extern uint8_t keyboard_set3_all_break;
extern int mouse_queue_start, mouse_queue_end;
extern int mouse_cmd_queue_start, mouse_cmd_queue_end;
extern int mouse_scan;
extern kbc_port_t *kbc_ports[2];
#ifdef EMU_DEVICE_H
extern const device_t keyboard_pc_device;
extern const device_t keyboard_pc82_device;
@@ -152,13 +201,13 @@ extern const device_t keyboard_xt_t1x00_device;
extern const device_t keyboard_tandy_device;
# if defined(DEV_BRANCH) && defined(USE_LASERXT)
extern const device_t keyboard_xt_lxt3_device;
# endif
# endif /*defined(DEV_BRANCH) && defined(USE_LASERXT) */
extern const device_t keyboard_xt_olivetti_device;
extern const device_t keyboard_xt_zenith_device;
extern const device_t keyboard_xtclone_device;
extern const device_t keyboard_at_device;
extern const device_t keyboard_at_ami_device;
extern const device_t keyboard_at_samsung_device;
extern const device_t keyboard_at_tg_ami_device;
extern const device_t keyboard_at_toshiba_device;
extern const device_t keyboard_at_olivetti_device;
extern const device_t keyboard_at_ncr_device;
@@ -167,6 +216,8 @@ extern const device_t keyboard_ps2_ps1_device;
extern const device_t keyboard_ps2_ps1_pci_device;
extern const device_t keyboard_ps2_xi8088_device;
extern const device_t keyboard_ps2_ami_device;
extern const device_t keyboard_ps2_tg_ami_device;
extern const device_t keyboard_ps2_tg_ami_green_device;
extern const device_t keyboard_ps2_olivetti_device;
extern const device_t keyboard_ps2_mca_device;
extern const device_t keyboard_ps2_mca_2_device;
@@ -176,7 +227,8 @@ extern const device_t keyboard_ps2_ami_pci_device;
extern const device_t keyboard_ps2_intel_ami_pci_device;
extern const device_t keyboard_ps2_acer_pci_device;
extern const device_t keyboard_ps2_ali_pci_device;
#endif
extern const device_t keyboard_ps2_tg_ami_pci_device;
#endif /*EMU_DEVICE_H*/
extern void keyboard_init(void);
extern void keyboard_close(void);
@@ -190,22 +242,21 @@ extern uint8_t keyboard_get_shift(void);
extern void keyboard_get_states(uint8_t *cl, uint8_t *nl, uint8_t *sl);
extern void keyboard_set_states(uint8_t cl, uint8_t nl, uint8_t sl);
extern int keyboard_recv(uint16_t key);
extern int keyboard_isfsenter(void);
extern int keyboard_isfsenter_down(void);
extern int keyboard_isfsexit(void);
extern int keyboard_isfsexit_down(void);
extern int keyboard_ismsexit(void);
extern void keyboard_set_is_amstrad(int ams);
extern void keyboard_at_adddata_mouse(uint8_t val);
extern void keyboard_at_adddata_mouse_direct(uint8_t val);
extern void keyboard_at_adddata_mouse_cmd(uint8_t val);
extern void keyboard_at_mouse_reset(void);
extern uint8_t keyboard_at_mouse_pos(void);
extern int keyboard_at_fixed_channel(void);
extern void keyboard_at_set_mouse(void (*mouse_write)(uint8_t val, void *), void *);
extern void keyboard_at_set_a20_key(int state);
extern void keyboard_at_set_mode(int ps2);
extern uint8_t keyboard_at_get_mouse_scan(void);
extern void keyboard_at_set_mouse_scan(uint8_t val);
extern void keyboard_at_reset(void);
extern void kbc_at_a20_reset(void);
#ifdef __cplusplus
}

View File

@@ -162,6 +162,7 @@
#else
# define IDS_DYNAREC IDS_2163
#endif
#define IDS_2166 2166 // "Video card #2 ""%hs"" is not..."
#define IDS_4096 4096 // "Hard disk (%s)"
#define IDS_4097 4097 // "%01i:%01i"

View File

@@ -35,6 +35,7 @@
#define MOUSE_TYPE_LT3BUTTON 10 /* Logitech 3-button Serial Mouse */
#define MOUSE_TYPE_PS2 11 /* PS/2 series Bus Mouse */
#define MOUSE_TYPE_WACOM 12 /* WACOM tablet */
#define MOUSE_TYPE_WACOMARTP 13 /* WACOM tablet (ArtPad) */
#define MOUSE_TYPE_ONBOARD 0x80 /* Mouse is an on-board version of one of the above. */
@@ -65,6 +66,7 @@ extern const device_t mouse_msserial_device;
extern const device_t mouse_ltserial_device;
extern const device_t mouse_ps2_device;
extern const device_t mouse_wacom_device;
extern const device_t mouse_wacom_artpad_device;
#endif
extern void mouse_init(void);

View File

@@ -43,8 +43,11 @@ extern void pic_elcr_write(uint16_t port, uint8_t val, void *priv);
extern uint8_t pic_elcr_read(uint16_t port, void *priv);
extern void pic_set_shadow(int sh);
extern int pic_get_pci_flag(void);
extern void pic_set_pci_flag(int pci);
extern void pic_set_pci(void);
extern void pic_kbd_latch(int enable);
extern void pic_mouse_latch(int enable);
extern void pic_init(void);
extern void pic_init_pcjr(void);
extern void pic2_init(void);

View File

@@ -26,7 +26,7 @@
#define MPU401_VERSION 0x15
#define MPU401_REVISION 0x01
#define MPU401_QUEUE 64
#define MPU401_QUEUE 1024
#define MPU401_INPUT_QUEUE 1024
#define MPU401_TIMECONSTANT (60000000 / 1000.0f)
#define MPU401_RESETBUSY 27.0f

View File

@@ -24,9 +24,16 @@
extern int sound_gain;
#define SOUNDBUFLEN (48000 / 50)
#define FREQ_44100 44100
#define FREQ_48000 48000
#define FREQ_49716 49716
#define FREQ_88200 88200
#define FREQ_96000 96000
#define CD_FREQ 44100
#define SOUND_FREQ FREQ_48000
#define SOUNDBUFLEN (SOUND_FREQ / 50)
#define CD_FREQ FREQ_44100
#define CD_BUFLEN (CD_FREQ / 10)
enum {

View File

@@ -113,6 +113,7 @@ extern int con, cursoron, cgablink;
extern int scrollcache;
extern uint8_t edatlookup[4][4];
extern uint8_t egaremap2bpp[256];
#if defined(EMU_MEM_H) && defined(EMU_ROM_H)
void ega_render_blank(ega_t *ega);
@@ -120,14 +121,8 @@ void ega_render_blank(ega_t *ega);
void ega_render_overscan_left(ega_t *ega);
void ega_render_overscan_right(ega_t *ega);
void ega_render_text_40(ega_t *ega);
void ega_render_text_80(ega_t *ega);
void ega_render_2bpp_lowres(ega_t *ega);
void ega_render_2bpp_highres(ega_t *ega);
void ega_render_4bpp_lowres(ega_t *ega);
void ega_render_4bpp_highres(ega_t *ega);
void ega_render_text(ega_t *ega);
void ega_render_graphics(ega_t *ega);
#endif
#endif /*VIDEO_EGA_H*/

View File

@@ -20,11 +20,11 @@
break; \
\
case VAR_WORD_MODE_MA13: \
out_addr = ((in_addr << 1) & 0x1fff8) | ((in_addr >> 13) & 0x4) | (in_addr & ~0x1ffff); \
out_addr = ((in_addr << 1) & 0x3fff8) | ((in_addr >> 13) & 0x4) | (in_addr & ~0x3ffff); \
break; \
\
case VAR_WORD_MODE_MA15: \
out_addr = ((in_addr << 1) & 0x1fff8) | ((in_addr >> 15) & 0x4) | (in_addr & ~0x1ffff); \
out_addr = ((in_addr << 1) & 0x3fff8) | ((in_addr >> 15) & 0x4) | (in_addr & ~0x3ffff); \
break; \
\
case VAR_DWORD_MODE: \
@@ -85,7 +85,7 @@ ega_recalc_remap_func(ega_t *ega)
func_nr = VAR_DWORD_MODE;
else if (ega->crtc[0x17] & 0x40)
func_nr = VAR_BYTE_MODE;
else if (ega->crtc[0x17] & 0x20)
else if ((ega->crtc[0x17] & 0x20) && ega->vram_limit > 64*1024)
func_nr = VAR_WORD_MODE_MA15;
else
func_nr = VAR_WORD_MODE_MA13;

View File

@@ -56,6 +56,7 @@ typedef struct {
int initialized = 0;
io_t *io[NPORTS], *io_last[NPORTS];
// #define ENABLE_IO_LOG 1
#ifdef ENABLE_IO_LOG
int io_do_log = ENABLE_IO_LOG;
@@ -310,7 +311,9 @@ inb(uint16_t port)
/* if (port == 0x1ed)
ret = 0xfe; */
io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
if (port == 0x92) {
io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
}
return (ret);
}
@@ -341,7 +344,9 @@ outb(uint16_t port, uint8_t val)
#endif
}
io_log("[%04X:%08X] (%i, %i, %04i) outb(%04X, %02X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
if (port == 0x92) {
io_log("[%04X:%08X] (%i, %i, %04i) outb(%04X, %02X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
}
return;
}
@@ -395,7 +400,9 @@ inw(uint16_t port)
if (!found)
cycles -= io_delay;
io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
if (port == 0x92) {
io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
}
return ret;
}
@@ -440,7 +447,9 @@ outw(uint16_t port, uint16_t val)
#endif
}
io_log("[%04X:%08X] (%i, %i, %04i) outw(%04X, %04X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
if (port == 0x92) {
io_log("[%04X:%08X] (%i, %i, %04i) outw(%04X, %04X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
}
return;
}
@@ -522,7 +531,9 @@ inl(uint16_t port)
if (!found)
cycles -= io_delay;
io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
if (port == 0x92) {
io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
}
return ret;
}
@@ -582,7 +593,9 @@ outl(uint16_t port, uint32_t val)
#endif
}
io_log("[%04X:%08X] (%i, %i, %04i) outl(%04X, %08X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
if (port == 0x92) {
io_log("[%04X:%08X] (%i, %i, %04i) outl(%04X, %08X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
}
return;
}

View File

@@ -220,7 +220,7 @@ machine_at_spc6000a_init(const machine_t *model)
if (fdc_type == FDC_INTERNAL)
device_add(&fdc_at_device);
device_add(&keyboard_at_samsung_device);
device_add(&keyboard_at_ami_device);
return ret;
}
@@ -404,7 +404,7 @@ machine_at_acerv10_init(const machine_t *model)
machine_at_common_init(model);
device_add(&sis_85c461_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&keyboard_ps2_acer_pci_device);
device_add(&ide_isa_2ch_device);
if (fdc_type == FDC_INTERNAL)
@@ -1066,7 +1066,7 @@ machine_at_486sp3_init(const machine_t *model)
pci_register_slot(0x05, PCI_CARD_NORMAL, 3, 4, 1, 2); /* 05 = Slot 3 */
pci_register_slot(0x06, PCI_CARD_NORMAL, 4, 1, 2, 3); /* 06 = Slot 4 */
pci_register_slot(0x02, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0);
device_add(&keyboard_ps2_ami_pci_device); /* Uses the AMIKEY KBC */
device_add(&keyboard_ps2_ami_pci_device); /* Uses the AMIKEY KBC */
device_add(&sio_device);
device_add(&fdc37c663_ide_device);
device_add(&sst_flash_29ee010_device);
@@ -1657,7 +1657,7 @@ machine_at_actionpc2600_init(const machine_t *model)
device_add(&umc_8886af_device);
device_add(&um8669f_device);
device_add(&intel_flash_bxt_device);
device_add(&keyboard_at_ami_device);
device_add(&keyboard_ps2_tg_ami_device);
return ret;
}
@@ -1725,11 +1725,11 @@ machine_at_ms4134_init(const machine_t *model)
{
int ret;
ret = bios_load_linear("roms/machines/ms4134/4alm001.bin",
ret = bios_load_linear("roms/machines/ms4134/4alm001.bin",
0x000e0000, 131072, 0);
if (bios_only || !ret)
return ret;
return ret;
machine_at_common_ide_init(model);
@@ -1760,10 +1760,10 @@ machine_at_tg486gp_init(const machine_t *model)
int ret;
ret = bios_load_linear("roms/machines/tg486gp/tg486gp.bin",
0x000e0000, 131072, 0);
0x000e0000, 131072, 0);
if (bios_only || !ret)
return ret;
return ret;
machine_at_common_ide_init(model);
@@ -1782,7 +1782,7 @@ machine_at_tg486gp_init(const machine_t *model)
device_add(&ali1435_device);
device_add(&sst_flash_29ee010_device);
device_add(&keyboard_ps2_ami_device);
device_add(&keyboard_ps2_tg_ami_device);
return ret;
}
@@ -1793,10 +1793,10 @@ machine_at_tg486g_init(const machine_t *model)
int ret;
ret = bios_load_linear("roms/machines/tg486g/tg486g.bin",
0x000c0000, 262144, 0);
0x000c0000, 262144, 0);
if (bios_only || !ret)
return ret;
return ret;
else {
mem_mapping_set_addr(&bios_mapping, 0x0c0000, 0x40000);
mem_mapping_set_exec(&bios_mapping, rom);
@@ -1806,7 +1806,7 @@ machine_at_tg486g_init(const machine_t *model)
device_add(&sis_85c471_device);
device_add(&ide_isa_device);
device_add(&fdc37c651_ide_device);
device_add(&keyboard_ps2_intel_ami_pci_device);
device_add(&keyboard_ps2_tg_ami_pci_device);
return ret;
}

View File

@@ -237,7 +237,7 @@ machine_at_hawk_init(const machine_t *model)
pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 4, 1);
pci_register_slot(0x12, PCI_CARD_NORMAL, 3, 4, 1, 2);
pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&keyboard_ps2_tg_ami_pci_device);
device_add(&i430fx_device);
device_add(&piix_device);
device_add(&fdc37c665_device);

View File

@@ -1343,6 +1343,9 @@ machine_ps2_common_init(const machine_t *model)
nmi_mask = 0x80;
ps2.uart = device_add_inst(&ns16550_device, 1);
pic_kbd_latch(0x01);
pic_mouse_latch(0x01);
}
int

View File

@@ -136,6 +136,22 @@ laserxt_init(int is_lxt3)
laserxt_is_lxt3 = is_lxt3;
}
static void
machine_xt_laserxt_common_init(const machine_t *model,int is_lxt3)
{
machine_common_init(model);
pit_devs[0].set_out_func(pit_devs[0].data, 1, pit_refresh_timer_xt);
if (fdc_type == FDC_INTERNAL)
device_add(&fdc_xt_device);
nmi_init();
standalone_gameport_type = &gameport_device;
laserxt_init(is_lxt3);
}
int
machine_xt_laserxt_init(const machine_t *model)
{
@@ -147,9 +163,9 @@ machine_xt_laserxt_init(const machine_t *model)
if (bios_only || !ret)
return ret;
machine_xt_init(model);
device_add(&keyboard_xt_device);
laserxt_init(0);
machine_xt_laserxt_common_init(model, 0);
return ret;
}
@@ -165,18 +181,9 @@ machine_xt_lxt3_init(const machine_t *model)
if (bios_only || !ret)
return ret;
machine_common_init(model);
pit_devs[0].set_out_func(pit_devs[0].data, 1, pit_refresh_timer_xt);
device_add(&keyboard_xt_lxt3_device);
if (fdc_type == FDC_INTERNAL)
device_add(&fdc_xt_device);
nmi_init();
standalone_gameport_type = &gameport_device;
laserxt_init(1);
machine_xt_laserxt_common_init(model, 1);
return ret;
}

View File

@@ -3774,7 +3774,7 @@ const machine_t machines[] = {
.min_multi = 0,
.max_multi = 0
},
.bus_flags = MACHINE_PS2,
.bus_flags = MACHINE_AT,
.flags = MACHINE_IDE,
.ram = {
.min = 1024,
@@ -3813,7 +3813,7 @@ const machine_t machines[] = {
.min_multi = 0,
.max_multi = 0
},
.bus_flags = MACHINE_PS2,
.bus_flags = MACHINE_AT,
.flags = MACHINE_IDE,
.ram = {
.min = 1024,
@@ -3894,7 +3894,7 @@ const machine_t machines[] = {
.min_multi = 0,
.max_multi = 0
},
.bus_flags = MACHINE_PS2,
.bus_flags = MACHINE_AT,
.flags = MACHINE_IDE,
.ram = {
.min = 1024,
@@ -7036,7 +7036,7 @@ const machine_t machines[] = {
.min_multi = 0,
.max_multi = 0
},
.bus_flags = MACHINE_PCI,
.bus_flags = MACHINE_PS2_PCI,
.flags = MACHINE_IDE_DUAL | MACHINE_APM,
.ram = {
.min = 1024,
@@ -13005,7 +13005,6 @@ machine_get_kbc_device(int m)
return (NULL);
}
const device_t *
machine_get_device(int m)
{

View File

@@ -2445,6 +2445,56 @@ mem_mapping_recalc(uint64_t base, uint64_t size)
}
flushmmucache_cr3();
#ifdef ENABLE_MEM_LOG
pclog("\nMemory map:\n");
mem_mapping_t *write = (mem_mapping_t *) -1, *read = (mem_mapping_t *) -1, *write_bus = (mem_mapping_t *) -1, *read_bus = (mem_mapping_t *) -1;
for (c = 0; c < (sizeof(write_mapping) / sizeof(write_mapping[0])); c++) {
if ((write_mapping[c] == write) && (read_mapping[c] == read) && (write_mapping_bus[c] == write_bus) && (read_mapping_bus[c] == read_bus))
continue;
write = write_mapping[c];
read = read_mapping[c];
write_bus = write_mapping_bus[c];
read_bus = read_mapping_bus[c];
pclog("%08X | ", c << MEM_GRANULARITY_BITS);
if (read) {
pclog("R%c%c%c %08X+% 8X",
read->read_b ? 'b' : ' ', read->read_w ? 'w' : ' ', read->read_l ? 'l' : ' ',
read->base, read->size);
} else {
pclog(" ");
}
if (write) {
pclog(" | W%c%c%c %08X+% 8X",
write->write_b ? 'b' : ' ', write->write_w ? 'w' : ' ', write->write_l ? 'l' : ' ',
write->base, write->size);
} else {
pclog(" | ");
}
pclog(" | %c\n", _mem_exec[c] ? 'X' : ' ');
if ((write != write_bus) || (read != read_bus)) {
pclog(" ^ bus | ");
if (read_bus) {
pclog("R%c%c%c %08X+% 8X",
read_bus->read_b ? 'b' : ' ', read_bus->read_w ? 'w' : ' ', read_bus->read_l ? 'l' : ' ',
read_bus->base, read_bus->size);
} else {
pclog(" ");
}
if (write_bus) {
pclog(" | W%c%c%c %08X+% 8X",
write_bus->write_b ? 'b' : ' ', write_bus->write_w ? 'w' : ' ', write_bus->write_l ? 'l' : ' ',
write_bus->base, write_bus->size);
} else {
pclog(" | ");
}
pclog(" |\n");
}
}
pclog("\n");
#endif
}
void
@@ -2662,11 +2712,12 @@ void
mem_a20_init(void)
{
if (is286) {
rammask = cpu_16bitbus ? 0xefffff : 0xffefffff;
mem_a20_key = mem_a20_alt = mem_a20_state = 0;
rammask = cpu_16bitbus ? 0xffffff : 0xffffffff;
if (is6117)
rammask |= 0x03000000;
flushmmucache();
mem_a20_state = mem_a20_key | mem_a20_alt;
// mem_a20_state = mem_a20_key | mem_a20_alt;
} else {
rammask = 0xfffff;
flushmmucache();
@@ -2709,7 +2760,8 @@ mem_init_ram_mapping(mem_mapping_t *mapping, uint32_t base, uint32_t size)
void
mem_reset(void)
{
uint32_t c, m;
uint32_t c;
size_t m;
memset(page_ff, 0xff, sizeof(page_ff));
@@ -2747,7 +2799,7 @@ mem_reset(void)
mem_size = 2097152;
#endif
m = 1024UL * mem_size;
m = 1024UL * (size_t) mem_size;
#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64))
if (mem_size > 1048576) {
@@ -2963,6 +3015,10 @@ mem_remap_top(int kb)
sis_mode = 1;
}
/* Do not remap if we're have more than (16 MB - RAM) memory. */
if ((kb != 0) && (mem_size >= (16384 - kb)))
return;
if (kb == 0) {
kb = old_kb;
set = 0;

View File

@@ -60,43 +60,42 @@
#include <86box/bswap.h>
/* Maximum number of times we report a link down to the guest (failure to send frame) */
#define ELNK_MAX_LINKDOWN_REPORTED 3
#define ELNK_MAX_LINKDOWN_REPORTED 3
/* Maximum number of times we postpone restoring a link that is temporarily down. */
#define ELNK_MAX_LINKRST_POSTPONED 3
#define ELNK_MAX_LINKRST_POSTPONED 3
/* Maximum frame size we handle */
#define MAX_FRAME 1536
#define MAX_FRAME 1536
/* Size of the packet buffer. */
#define ELNK_BUF_SIZE 2048
#define ELNK_BUF_SIZE 2048
/* The packet buffer address mask. */
#define ELNK_BUF_ADR_MASK (ELNK_BUF_SIZE - 1)
#define ELNK_BUF_ADR_MASK (ELNK_BUF_SIZE - 1)
/* The GP buffer pointer address within the buffer. */
#define ELNK_GP(dev) (dev->uGPBufPtr & ELNK_BUF_ADR_MASK)
#define ELNK_GP(dev) (dev->uGPBufPtr & ELNK_BUF_ADR_MASK)
/* The GP buffer pointer mask.
* NB: The GP buffer pointer is internally a 12-bit counter. When addressing into the
* packet buffer, bit 11 is ignored. Required to pass 3C501 diagnostics.
*/
#define ELNK_GP_MASK 0xfff
#define ELNK_GP_MASK 0xfff
/*********************************************************************************************************************************
* Structures and Typedefs *
*********************************************************************************************************************************/
* Structures and Typedefs *
*********************************************************************************************************************************/
/**
* EtherLink Transmit Command Register.
*/
typedef struct ELNK_XMIT_CMD {
uint8_t det_ufl : 1; /* Detect underflow. */
uint8_t det_coll : 1; /* Detect collision. */
uint8_t det_16col : 1; /* Detect collision 16. */
uint8_t det_succ : 1; /* Detect successful xmit. */
uint8_t unused : 4;
uint8_t det_ufl : 1; /* Detect underflow. */
uint8_t det_coll : 1; /* Detect collision. */
uint8_t det_16col : 1; /* Detect collision 16. */
uint8_t det_succ : 1; /* Detect successful xmit. */
uint8_t unused : 4;
} EL_XMT_CMD;
/**
@@ -107,151 +106,151 @@ typedef struct ELNK_XMIT_CMD {
* (something the 3C501 does not have a concept of).
*/
typedef struct ELNK_XMIT_STAT {
uint8_t uflow : 1; /* Underflow on transmit. */
uint8_t coll : 1; /* Collision on transmit. */
uint8_t coll16 : 1; /* 16 collisions on transmit. */
uint8_t ready : 1; /* Ready for a new frame. */
uint8_t undef : 4;
uint8_t uflow : 1; /* Underflow on transmit. */
uint8_t coll : 1; /* Collision on transmit. */
uint8_t coll16 : 1; /* 16 collisions on transmit. */
uint8_t ready : 1; /* Ready for a new frame. */
uint8_t undef : 4;
} EL_XMT_STAT;
/** Address match (adr_match) modes. */
typedef enum {
EL_ADRM_DISABLED = 0, /* Receiver disabled. */
EL_ADRM_PROMISC = 1, /* Receive all addresses. */
EL_ADRM_BCAST = 2, /* Receive station + broadcast. */
EL_ADRM_MCAST = 3 /* Receive station + multicast. */
EL_ADRM_DISABLED = 0, /* Receiver disabled. */
EL_ADRM_PROMISC = 1, /* Receive all addresses. */
EL_ADRM_BCAST = 2, /* Receive station + broadcast. */
EL_ADRM_MCAST = 3 /* Receive station + multicast. */
} EL_ADDR_MATCH;
/**
* EtherLink Receive Command Register.
*/
typedef struct ELNK_RECV_CMD {
uint8_t det_ofl : 1; /* Detect overflow errors. */
uint8_t det_fcs : 1; /* Detect FCS errors. */
uint8_t det_drbl : 1; /* Detect dribble error. */
uint8_t det_runt : 1; /* Detect short frames. */
uint8_t det_eof : 1; /* Detect EOF (frames without overflow). */
uint8_t acpt_good : 1; /* Accept good frames. */
uint8_t adr_match : 2; /* Address match mode. */
uint8_t det_ofl : 1; /* Detect overflow errors. */
uint8_t det_fcs : 1; /* Detect FCS errors. */
uint8_t det_drbl : 1; /* Detect dribble error. */
uint8_t det_runt : 1; /* Detect short frames. */
uint8_t det_eof : 1; /* Detect EOF (frames without overflow). */
uint8_t acpt_good : 1; /* Accept good frames. */
uint8_t adr_match : 2; /* Address match mode. */
} EL_RCV_CMD;
/**
* EtherLink Receive Status Register.
*/
typedef struct ELNK_RECV_STAT {
uint8_t oflow : 1; /* Overflow on receive. */
uint8_t fcs : 1; /* FCS error. */
uint8_t dribble : 1; /* Dribble error. */
uint8_t runt : 1; /* Short frame. */
uint8_t no_ovf : 1; /* Received packet w/o overflow. */
uint8_t good : 1; /* Received good packet. */
uint8_t undef : 1;
uint8_t stale : 1; /* Stale receive status. */
uint8_t oflow : 1; /* Overflow on receive. */
uint8_t fcs : 1; /* FCS error. */
uint8_t dribble : 1; /* Dribble error. */
uint8_t runt : 1; /* Short frame. */
uint8_t no_ovf : 1; /* Received packet w/o overflow. */
uint8_t good : 1; /* Received good packet. */
uint8_t undef : 1;
uint8_t stale : 1; /* Stale receive status. */
} EL_RCV_STAT;
/** Buffer control (buf_ctl) modes. */
typedef enum {
EL_BCTL_SYSTEM = 0, /* Host has buffer access. */
EL_BCTL_XMT_RCV = 1, /* Transmit, then receive. */
EL_BCTL_RECEIVE = 2, /* Receive. */
EL_BCTL_LOOPBACK = 3 /* Loopback. */
EL_BCTL_SYSTEM = 0, /* Host has buffer access. */
EL_BCTL_XMT_RCV = 1, /* Transmit, then receive. */
EL_BCTL_RECEIVE = 2, /* Receive. */
EL_BCTL_LOOPBACK = 3 /* Loopback. */
} EL_BUFFER_CONTROL;
/**
* EtherLink Auxiliary Status Register.
*/
typedef struct ELNK_AUX_CMD {
uint8_t ire : 1; /* Interrupt Request Enable. */
uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
uint8_t buf_ctl : 2; /* Packet buffer control. */
uint8_t unused : 1;
uint8_t dma_req : 1; /* DMA request. */
uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
uint8_t reset : 1; /* Card in reset while set. */
uint8_t ire : 1; /* Interrupt Request Enable. */
uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
uint8_t buf_ctl : 2; /* Packet buffer control. */
uint8_t unused : 1;
uint8_t dma_req : 1; /* DMA request. */
uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
uint8_t reset : 1; /* Card in reset while set. */
} EL_AUX_CMD;
/**
* EtherLink Auxiliary Status Register.
*/
typedef struct ELNK_AUX_STAT {
uint8_t recv_bsy : 1; /* Receive busy. */
uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
uint8_t buf_ctl : 2; /* Packet buffer control. */
uint8_t dma_done : 1; /* DMA done. */
uint8_t dma_req : 1; /* DMA request. */
uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
uint8_t xmit_bsy : 1; /* Transmit busy. */
uint8_t recv_bsy : 1; /* Receive busy. */
uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
uint8_t buf_ctl : 2; /* Packet buffer control. */
uint8_t dma_done : 1; /* DMA done. */
uint8_t dma_req : 1; /* DMA request. */
uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
uint8_t xmit_bsy : 1; /* Transmit busy. */
} EL_AUX_STAT;
/**
* Internal interrupt status.
*/
typedef struct ELNK_INTR_STAT {
uint8_t recv_intr : 1; /* Receive interrupt status. */
uint8_t xmit_intr : 1; /* Transmit interrupt status. */
uint8_t dma_intr : 1; /* DMA interrupt status. */
uint8_t unused : 5;
uint8_t recv_intr : 1; /* Receive interrupt status. */
uint8_t xmit_intr : 1; /* Transmit interrupt status. */
uint8_t dma_intr : 1; /* DMA interrupt status. */
uint8_t unused : 5;
} EL_INTR_STAT;
typedef struct {
uint32_t base_address;
int base_irq;
uint32_t bios_addr;
uint8_t maclocal[6]; /* configured MAC (local) address. */
bool fISR; /* Internal interrupt flag. */
int fDMA; /* Internal DMA active flag. */
int fInReset; /* Internal in-reset flag. */
uint8_t aPROM[8]; /* The PROM contents. Only 8 bytes addressable, R/O. */
uint8_t aStationAddr[6]; /* The station address programmed by the guest, W/O. */
uint16_t uGPBufPtr; /* General Purpose (GP) Buffer Pointer, R/W. */
uint16_t uRCVBufPtr; /* Receive (RCV) Buffer Pointer, R/W. */
uint32_t base_address;
int base_irq;
uint32_t bios_addr;
uint8_t maclocal[6]; /* configured MAC (local) address. */
bool fISR; /* Internal interrupt flag. */
int fDMA; /* Internal DMA active flag. */
int fInReset; /* Internal in-reset flag. */
uint8_t aPROM[8]; /* The PROM contents. Only 8 bytes addressable, R/O. */
uint8_t aStationAddr[6]; /* The station address programmed by the guest, W/O. */
uint16_t uGPBufPtr; /* General Purpose (GP) Buffer Pointer, R/W. */
uint16_t uRCVBufPtr; /* Receive (RCV) Buffer Pointer, R/W. */
/** Transmit Command Register, W/O. */
union {
uint8_t XmitCmdReg;
EL_XMT_CMD XmitCmd;
uint8_t XmitCmdReg;
EL_XMT_CMD XmitCmd;
};
/** Transmit Status Register, R/O. */
union {
uint8_t XmitStatReg;
EL_XMT_STAT XmitStat;
uint8_t XmitStatReg;
EL_XMT_STAT XmitStat;
};
/** Receive Command Register, W/O. */
union {
uint8_t RcvCmdReg;
EL_RCV_CMD RcvCmd;
uint8_t RcvCmdReg;
EL_RCV_CMD RcvCmd;
};
/** Receive Status Register, R/O. */
union {
uint8_t RcvStatReg;
EL_RCV_STAT RcvStat;
uint8_t RcvStatReg;
EL_RCV_STAT RcvStat;
};
/** Auxiliary Command Register, W/O. */
union {
uint8_t AuxCmdReg;
EL_AUX_CMD AuxCmd;
uint8_t AuxCmdReg;
EL_AUX_CMD AuxCmd;
};
/** Auxiliary Status Register, R/O. */
union {
uint8_t AuxStatReg;
EL_AUX_STAT AuxStat;
uint8_t AuxStatReg;
EL_AUX_STAT AuxStat;
};
int fLinkUp; /* If set the link is currently up. */
int fLinkTempDown; /* If set the link is temporarily down because of a saved state load. */
uint16_t cLinkDownReported; /* Number of times we've reported the link down. */
uint16_t cLinkRestorePostponed; /* Number of times we've postponed the link restore. */
int fLinkUp; /* If set the link is currently up. */
int fLinkTempDown; /* If set the link is temporarily down because of a saved state load. */
uint16_t cLinkDownReported; /* Number of times we've reported the link down. */
uint16_t cLinkRestorePostponed; /* Number of times we've postponed the link restore. */
/* Internal interrupt state. */
union {
uint8_t IntrStateReg;
EL_INTR_STAT IntrState;
uint8_t IntrStateReg;
EL_INTR_STAT IntrState;
};
uint32_t cMsLinkUpDelay; /* MS to wait before we enable the link. */
int dma_channel;
uint8_t abLoopBuf[ELNK_BUF_SIZE]; /* The loopback transmit buffer (avoid stack allocations). */
uint8_t abRuntBuf[64]; /* The runt pad buffer (only really needs 60 bytes). */
uint8_t abPacketBuf[ELNK_BUF_SIZE]; /* The packet buffer. */
int dma_pos;
pc_timer_t timer_restore;
netcard_t *netcard;
uint32_t cMsLinkUpDelay; /* MS to wait before we enable the link. */
int dma_channel;
uint8_t abLoopBuf[ELNK_BUF_SIZE]; /* The loopback transmit buffer (avoid stack allocations). */
uint8_t abRuntBuf[64]; /* The runt pad buffer (only really needs 60 bytes). */
uint8_t abPacketBuf[ELNK_BUF_SIZE]; /* The packet buffer. */
int dma_pos;
pc_timer_t timer_restore;
netcard_t *netcard;
} threec501_t;
#ifdef ENABLE_3COM501_LOG
@@ -280,7 +279,7 @@ static void elnkR3HardReset(threec501_t *dev);
#endif
#define ETHER_ADDR_LEN ETH_ALEN
#define ETH_ALEN 6
#define ETH_ALEN 6
#pragma pack(1)
struct ether_header /** @todo Use RTNETETHERHDR? */
{
@@ -325,8 +324,8 @@ static void
elnkTempLinkDown(threec501_t *dev)
{
if (dev->fLinkUp) {
dev->fLinkTempDown = 1;
dev->cLinkDownReported = 0;
dev->fLinkTempDown = 1;
dev->cLinkDownReported = 0;
dev->cLinkRestorePostponed = 0;
timer_set_delay_u64(&dev->timer_restore, (dev->cMsLinkUpDelay * 1000) * TIMER_USEC);
}
@@ -341,7 +340,7 @@ elnkR3Reset(void *priv)
threec501_t *dev = (threec501_t *) priv;
if (dev->fLinkTempDown) {
dev->cLinkDownReported = 0x1000;
dev->cLinkDownReported = 0x1000;
dev->cLinkRestorePostponed = 0x1000;
timer_disable(&dev->timer_restore);
}
@@ -367,15 +366,14 @@ elnkR3HardReset(threec501_t *dev)
elnkSoftReset(dev);
}
/**
* Check if incoming frame matches the station address.
*/
static __inline int
padr_match(threec501_t *dev, const uint8_t *buf)
{
struct ether_header *hdr = (struct ether_header *)buf;
int result;
struct ether_header *hdr = (struct ether_header *) buf;
int result;
/* Checks own + broadcast as well as own + multicast. */
result = (dev->RcvCmd.adr_match >= EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, dev->aStationAddr, 6);
@@ -389,21 +387,20 @@ padr_match(threec501_t *dev, const uint8_t *buf)
static __inline int
padr_bcast(threec501_t *dev, const uint8_t *buf)
{
static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct ether_header *hdr = (struct ether_header *)buf;
int result = (dev->RcvCmd.adr_match == EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, aBCAST, 6);
static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct ether_header *hdr = (struct ether_header *) buf;
int result = (dev->RcvCmd.adr_match == EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, aBCAST, 6);
return result;
}
/**
* Check if incoming frame is an accepted multicast frame.
*/
static __inline int
padr_mcast(threec501_t *dev, const uint8_t *buf)
{
struct ether_header *hdr = (struct ether_header *)buf;
int result = (dev->RcvCmd.adr_match == EL_ADRM_MCAST) && ETHER_IS_MULTICAST(hdr->ether_dhost);
struct ether_header *hdr = (struct ether_header *) buf;
int result = (dev->RcvCmd.adr_match == EL_ADRM_MCAST) && ETHER_IS_MULTICAST(hdr->ether_dhost);
return result;
}
@@ -475,9 +472,9 @@ elnkSoftReset(threec501_t *dev)
static int
elnkReceiveLocked(void *priv, uint8_t *src, int size)
{
threec501_t *dev = (threec501_t *) priv;
int is_padr = 0, is_bcast = 0, is_mcast = 0;
bool fLoopback = dev->RcvCmd.adr_match == EL_BCTL_LOOPBACK;
threec501_t *dev = (threec501_t *) priv;
int is_padr = 0, is_bcast = 0, is_mcast = 0;
bool fLoopback = dev->RcvCmd.adr_match == EL_BCTL_LOOPBACK;
union {
uint8_t RcvStatNewReg;
@@ -513,8 +510,8 @@ elnkReceiveLocked(void *priv, uint8_t *src, int size)
* filter are always ignored.
*/
/// @todo cbToRecv must be 6 or more (complete address)
if ((dev->RcvCmd.adr_match == EL_ADRM_PROMISC) /* promiscuous enabled */
|| (is_padr = padr_match(dev, src))
if ((dev->RcvCmd.adr_match == EL_ADRM_PROMISC) /* promiscuous enabled */
|| (is_padr = padr_match(dev, src))
|| (is_bcast = padr_bcast(dev, src))
|| (is_mcast = padr_mcast(dev, src))) {
uint8_t *dst = dev->abPacketBuf + dev->uRCVBufPtr;
@@ -524,9 +521,9 @@ elnkReceiveLocked(void *priv, uint8_t *src, int size)
#endif
/* Receive status is evaluated from scratch. The stale bit must remain set until we know better. */
rcvstatnew.RcvStatNewReg = 0;
rcvstatnew.RcvStatNewReg = 0;
rcvstatnew.RcvStatNew.stale = 1;
dev->RcvStatReg = 0x80;
dev->RcvStatReg = 0x80;
/* Detect errors: Runts, overflow, and FCS errors.
* NB: Dribble errors can not happen because we can only receive an
@@ -549,7 +546,7 @@ elnkReceiveLocked(void *priv, uint8_t *src, int size)
memset(dev->abRuntBuf, 0, sizeof(dev->abRuntBuf));
memcpy(dev->abRuntBuf, src, size);
size = 60;
src = dev->abRuntBuf;
src = dev->abRuntBuf;
} else {
#ifdef ENABLE_3COM501_LOG
threec501_log("3Com501 runt, size=%d\n", size);
@@ -582,7 +579,7 @@ elnkReceiveLocked(void *priv, uint8_t *src, int size)
if (rcvstatnew.RcvStatNew.no_ovf && !rcvstatnew.RcvStatNew.fcs && !rcvstatnew.RcvStatNew.runt)
rcvstatnew.RcvStatNew.good = 1;
uint16_t cbCopy = (uint16_t)MIN(ELNK_BUF_SIZE - dev->uRCVBufPtr, size);
uint16_t cbCopy = (uint16_t) MIN(ELNK_BUF_SIZE - dev->uRCVBufPtr, size);
/* All packets that passed the address filter are copied to the buffer. */
@@ -604,15 +601,15 @@ elnkReceiveLocked(void *priv, uint8_t *src, int size)
* NB: The precise receive logic is not very well described in the EtherLink
* documentation. It was refined using the 3C501.EXE diagnostic utility.
*/
if ( (rcvstatnew.RcvStatNew.good && dev->RcvCmd.acpt_good)
|| (rcvstatnew.RcvStatNew.no_ovf && dev->RcvCmd.det_eof)
|| (rcvstatnew.RcvStatNew.runt && dev->RcvCmd.det_runt)
if ((rcvstatnew.RcvStatNew.good && dev->RcvCmd.acpt_good)
|| (rcvstatnew.RcvStatNew.no_ovf && dev->RcvCmd.det_eof)
|| (rcvstatnew.RcvStatNew.runt && dev->RcvCmd.det_runt)
|| (rcvstatnew.RcvStatNew.dribble && dev->RcvCmd.det_drbl)
|| (rcvstatnew.RcvStatNew.fcs && dev->RcvCmd.det_fcs)
|| (rcvstatnew.RcvStatNew.oflow && dev->RcvCmd.det_ofl)) {
dev->AuxStat.recv_bsy = 0;
dev->IntrState.recv_intr = 1;
rcvstatnew.RcvStatNew.stale = 0; /* Prevents further receive until set again. */
|| (rcvstatnew.RcvStatNew.fcs && dev->RcvCmd.det_fcs)
|| (rcvstatnew.RcvStatNew.oflow && dev->RcvCmd.det_ofl)) {
dev->AuxStat.recv_bsy = 0;
dev->IntrState.recv_intr = 1;
rcvstatnew.RcvStatNew.stale = 0; /* Prevents further receive until set again. */
}
/* Finally update the receive status. */
@@ -715,7 +712,7 @@ elnkAsyncTransmit(threec501_t *dev)
/* NB: The buffer control does *not* change to Receive and stays the way it was. */
if (!fLoopback) {
dev->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
dev->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
}
} while (0); /* No loop, because there isn't ever more than one packet to transmit. */
@@ -725,14 +722,14 @@ elnkAsyncTransmit(threec501_t *dev)
static void
elnkCsrWrite(threec501_t *dev, uint8_t data)
{
bool fTransmit = false;
bool fReceive = false;
bool fDMAR;
int mode;
bool fTransmit = false;
bool fReceive = false;
bool fDMAR;
int mode;
union {
uint8_t reg;
EL_AUX_CMD val;
uint8_t reg;
EL_AUX_CMD val;
} auxcmd;
auxcmd.reg = data;
@@ -759,7 +756,7 @@ elnkCsrWrite(threec501_t *dev, uint8_t data)
#endif
elnkSoftReset(dev);
}
dev->AuxCmd.reset = auxcmd.val.reset; /* Update the reset bit, if nothing else. */
dev->AuxCmd.reset = auxcmd.val.reset; /* Update the reset bit, if nothing else. */
}
/* If the card is in reset, stop right here. */
@@ -788,14 +785,14 @@ elnkCsrWrite(threec501_t *dev, uint8_t data)
}
} else {
while (dev->dma_pos < (ELNK_BUF_SIZE - ELNK_GP(dev))) {
int dma_data = dma_channel_read(dev->dma_channel);
int dma_data = dma_channel_read(dev->dma_channel);
dev->abPacketBuf[ELNK_GP(dev) + dev->dma_pos] = dma_data & 0xff;
dev->dma_pos++;
}
}
dev->uGPBufPtr = (dev->uGPBufPtr + dev->dma_pos) & ELNK_GP_MASK;
dma_set_drq(dev->dma_channel, 0);
dev->dma_pos = 0;
dev->dma_pos = 0;
dev->IntrState.dma_intr = 1;
dev->AuxStat.dma_done = 1;
elnkUpdateIrq(dev);
@@ -808,7 +805,7 @@ elnkCsrWrite(threec501_t *dev, uint8_t data)
/* Interrupt enable changes. */
if ((dev->AuxCmd.ire != auxcmd.val.ire) || (dev->AuxCmd.ride != auxcmd.val.ride)) {
dev->AuxStat.ride = dev->AuxCmd.ride = auxcmd.val.ride;
dev->AuxCmd.ire = auxcmd.val.ire; /* NB: IRE is not visible in the aux status register. */
dev->AuxCmd.ire = auxcmd.val.ire; /* NB: IRE is not visible in the aux status register. */
}
/* DMA Request changes. */
@@ -824,15 +821,15 @@ elnkCsrWrite(threec501_t *dev, uint8_t data)
/* Packet buffer control changes. */
if (dev->AuxCmd.buf_ctl != auxcmd.val.buf_ctl) {
#ifdef ENABLE_3COM501_LOG
static const char *apszBuffCntrl[4] = { "System", "Xmit then Recv", "Receive", "Loopback" };
static const char *apszBuffCntrl[4] = { "System", "Xmit then Recv", "Receive", "Loopback" };
threec501_log("3Com501: Packet buffer control `%s' -> `%s'\n", apszBuffCntrl[dev->AuxCmd.buf_ctl], apszBuffCntrl[auxcmd.val.buf_ctl]);
#endif
if (auxcmd.val.buf_ctl == EL_BCTL_XMT_RCV) {
/* Transmit, then receive. */
fTransmit = true;
fTransmit = true;
dev->AuxStat.recv_bsy = 0;
} else if (auxcmd.val.buf_ctl == EL_BCTL_SYSTEM) {
dev->AuxStat.xmit_bsy = 1; /* Transmit Busy is set here and cleared once actual transmit completes. */
dev->AuxStat.xmit_bsy = 1; /* Transmit Busy is set here and cleared once actual transmit completes. */
dev->AuxStat.recv_bsy = 0;
} else if (auxcmd.val.buf_ctl == EL_BCTL_RECEIVE) {
/* Special case: If going from xmit-then-receive mode to receive mode, and we received
@@ -845,8 +842,8 @@ elnkCsrWrite(threec501_t *dev, uint8_t data)
/* For loopback, we go through the regular transmit and receive path. That may be an
* overkill but the receive path is too complex for a special loopback-only case.
*/
fTransmit = true;
dev->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
fTransmit = true;
dev->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
}
dev->AuxStat.buf_ctl = dev->AuxCmd.buf_ctl = auxcmd.val.buf_ctl;
}
@@ -863,7 +860,7 @@ elnkCsrWrite(threec501_t *dev, uint8_t data)
if (fTransmit)
elnkAsyncTransmit(dev);
else if (fReceive) {
dev->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
dev->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
}
}
@@ -874,52 +871,52 @@ threec501_read(uint16_t addr, void *priv)
uint8_t retval = 0xff;
switch (addr & 0x0f) {
case 0x00: /* Receive status register aliases. The SEEQ 8001 */
case 0x02: /* EDLC clearly only decodes one bit for reads. */
case 0x00: /* Receive status register aliases. The SEEQ 8001 */
case 0x02: /* EDLC clearly only decodes one bit for reads. */
case 0x04:
case 0x06: /* Receive status register. */
retval = dev->RcvStatReg;
dev->RcvStat.stale = 1; /* Allows further reception. */
case 0x06: /* Receive status register. */
retval = dev->RcvStatReg;
dev->RcvStat.stale = 1; /* Allows further reception. */
dev->IntrState.recv_intr = 0; /* Reading clears receive interrupt. */
elnkUpdateIrq(dev);
break;
case 0x01: /* Transmit status register aliases. */
case 0x01: /* Transmit status register aliases. */
case 0x03:
case 0x05:
case 0x07: /* Transmit status register. */
retval = dev->XmitStatReg;
case 0x07: /* Transmit status register. */
retval = dev->XmitStatReg;
dev->IntrState.xmit_intr = 0; /* Reading clears transmit interrupt. */
elnkUpdateIrq(dev);
break;
case 0x08: /* GP Buffer pointer LSB. */
case 0x08: /* GP Buffer pointer LSB. */
retval = (dev->uGPBufPtr & 0xff);
break;
case 0x09: /* GP Buffer pointer MSB. */
case 0x09: /* GP Buffer pointer MSB. */
retval = (dev->uGPBufPtr >> 8);
break;
case 0x0a: /* RCV Buffer pointer LSB. */
case 0x0a: /* RCV Buffer pointer LSB. */
retval = (dev->uRCVBufPtr & 0xff);
break;
case 0x0b: /* RCV Buffer pointer MSB. */
case 0x0b: /* RCV Buffer pointer MSB. */
retval = (dev->uRCVBufPtr >> 8);
break;
case 0x0c: /* Ethernet address PROM window. */
case 0x0d: /* Alias. */
case 0x0c: /* Ethernet address PROM window. */
case 0x0d: /* Alias. */
/* Reads use low 3 bits of GP buffer pointer, no auto-increment. */
retval = dev->aPROM[dev->uGPBufPtr & 7];
break;
case 0x0e: /* Auxiliary status register. */
case 0x0e: /* Auxiliary status register. */
retval = dev->AuxStatReg;
break;
case 0x0f: /* Buffer window. */
case 0x0f: /* Buffer window. */
/* Reads use low 11 bits of GP buffer pointer, auto-increment. */
retval = dev->abPacketBuf[ELNK_GP(dev)];
retval = dev->abPacketBuf[ELNK_GP(dev)];
dev->uGPBufPtr = (dev->uGPBufPtr + 1) & ELNK_GP_MASK;
break;
}
@@ -973,40 +970,40 @@ threec501_write(uint16_t addr, uint8_t value, void *priv)
#endif
break;
case 0x08: /* GP Buffer pointer LSB. */
case 0x08: /* GP Buffer pointer LSB. */
dev->uGPBufPtr = (dev->uGPBufPtr & 0xff00) | value;
break;
case 0x09: /* GP Buffer pointer MSB. */
case 0x09: /* GP Buffer pointer MSB. */
dev->uGPBufPtr = (dev->uGPBufPtr & 0x00ff) | (value << 8);
break;
case 0x0a: /* RCV Buffer pointer clear. */
case 0x0a: /* RCV Buffer pointer clear. */
dev->uRCVBufPtr = 0;
#ifdef ENABLE_3COM501_LOG
threec501_log("3Com501: RCV Buffer Pointer cleared (%02X)\n", value);
#endif
break;
case 0x0b: /* RCV buffer pointer MSB. */
case 0x0c: /* Ethernet address PROM window. */
case 0x0d: /* Undocumented. */
case 0x0b: /* RCV buffer pointer MSB. */
case 0x0c: /* Ethernet address PROM window. */
case 0x0d: /* Undocumented. */
#ifdef ENABLE_3COM501_LOG
threec501_log("3Com501: Writing read-only register %02X!\n", reg);
#endif
break;
case 0x0e: /* Auxiliary Command (CSR). */
case 0x0e: /* Auxiliary Command (CSR). */
elnkCsrWrite(dev, value);
break;
case 0x0f: /* Buffer window. */
case 0x0f: /* Buffer window. */
/* Writes use low 11 bits of GP buffer pointer, auto-increment. */
if (dev->AuxCmd.buf_ctl != EL_BCTL_SYSTEM) {
/// @todo Does this still increment GPBufPtr?
break;
}
dev->abPacketBuf[ELNK_GP(dev)] = value;
dev->uGPBufPtr = (dev->uGPBufPtr + 1) & ELNK_GP_MASK;
dev->uGPBufPtr = (dev->uGPBufPtr + 1) & ELNK_GP_MASK;
break;
}
@@ -1042,12 +1039,12 @@ elnkSetLinkState(void *priv, uint32_t link_state)
if (dev->fLinkUp != link_up) {
dev->fLinkUp = link_up;
if (link_up) {
dev->fLinkTempDown = 1;
dev->cLinkDownReported = 0;
dev->fLinkTempDown = 1;
dev->cLinkDownReported = 0;
dev->cLinkRestorePostponed = 0;
timer_set_delay_u64(&dev->timer_restore, (dev->cMsLinkUpDelay * 1000) * TIMER_USEC);
} else {
dev->cLinkDownReported = 0;
dev->cLinkDownReported = 0;
dev->cLinkRestorePostponed = 0;
}
}
@@ -1060,8 +1057,7 @@ elnkR3TimerRestore(void *priv)
{
threec501_t *dev = (threec501_t *) priv;
if ((dev->cLinkDownReported <= ELNK_MAX_LINKDOWN_REPORTED) &&
(dev->cLinkRestorePostponed <= ELNK_MAX_LINKRST_POSTPONED)) {
if ((dev->cLinkDownReported <= ELNK_MAX_LINKDOWN_REPORTED) && (dev->cLinkRestorePostponed <= ELNK_MAX_LINKRST_POSTPONED)) {
timer_advance_u64(&dev->timer_restore, 1500000 * TIMER_USEC);
dev->cLinkRestorePostponed++;
} else {
@@ -1117,7 +1113,7 @@ threec501_nic_init(const device_t *info)
/* Initialize the PROM */
memcpy(dev->aPROM, dev->maclocal, sizeof(dev->maclocal));
dev->aPROM[6] = dev->aPROM[7] = 0; /* The two padding bytes. */
dev->aPROM[6] = dev->aPROM[7] = 0; /* The two padding bytes. */
#ifdef ENABLE_3COM501_LOG
threec501_log("I/O=%04x, IRQ=%d, DMA=%d, MAC=%02x:%02x:%02x:%02x:%02x:%02x\n",
@@ -1210,15 +1206,15 @@ static const device_config_t threec501_config[] = {
};
const device_t threec501_device = {
.name = "3Com EtherLink (3c500/3c501)",
.name = "3Com EtherLink (3c500/3c501)",
.internal_name = "3c501",
.flags = DEVICE_ISA,
.local = 0,
.init = threec501_nic_init,
.close = threec501_nic_close,
.reset = elnkR3Reset,
.flags = DEVICE_ISA,
.local = 0,
.init = threec501_nic_init,
.close = threec501_nic_close,
.reset = elnkR3Reset,
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = threec501_config
.force_redraw = NULL,
.config = threec501_config
};

View File

@@ -32,7 +32,7 @@ static uint8_t
pci_dummy_read(uint16_t Port, void *p)
{
pci_dummy_t *dev = (pci_dummy_t *) p;
uint8_t ret = 0xff;
uint8_t ret = 0xff;
switch (Port & 0x20) {
case 0x00:
@@ -120,51 +120,57 @@ static uint8_t
pci_dummy_pci_read(int func, int addr, void *priv)
{
pci_dummy_t *dev = (pci_dummy_t *) priv;
uint8_t ret = 0xff;
uint8_t ret = 0xff;
if (func == 0x00) switch (addr) {
case 0x00: case 0x2c:
ret = 0x1a;
break;
case 0x01: case 0x2d:
ret = 0x07;
break;
if (func == 0x00)
switch (addr) {
case 0x00:
case 0x2c:
ret = 0x1a;
break;
case 0x01:
case 0x2d:
ret = 0x07;
break;
case 0x02: case 0x2e:
ret = 0x0b;
break;
case 0x03: case 0x2f:
ret = 0xab;
break;
case 0x02:
case 0x2e:
ret = 0x0b;
break;
case 0x03:
case 0x2f:
ret = 0xab;
break;
case 0x04: /* PCI_COMMAND_LO */
case 0x05: /* PCI_COMMAND_HI */
case 0x06: /* PCI_STATUS_LO */
case 0x07: /* PCI_STATUS_HI */
case 0x0a: case 0x0b:
case 0x3c: /* PCI_ILR */
ret = dev->pci_regs[addr];
break;
case 0x04: /* PCI_COMMAND_LO */
case 0x05: /* PCI_COMMAND_HI */
case 0x06: /* PCI_STATUS_LO */
case 0x07: /* PCI_STATUS_HI */
case 0x0a:
case 0x0b:
case 0x3c: /* PCI_ILR */
ret = dev->pci_regs[addr];
break;
case 0x08: /* Techncially, revision, but we return the card (slot) here. */
ret = dev->card;
break;
case 0x08: /* Techncially, revision, but we return the card (slot) here. */
ret = dev->card;
break;
case 0x10: /* PCI_BAR 7:5 */
ret = (dev->pci_bar[0].addr_regs[0] & 0xe0) | 0x01;
break;
case 0x11: /* PCI_BAR 15:8 */
ret = dev->pci_bar[0].addr_regs[1];
break;
case 0x10: /* PCI_BAR 7:5 */
ret = (dev->pci_bar[0].addr_regs[0] & 0xe0) | 0x01;
break;
case 0x11: /* PCI_BAR 15:8 */
ret = dev->pci_bar[0].addr_regs[1];
break;
case 0x3d: /* PCI_IPR */
ret = PCI_INTA;
break;
case 0x3d: /* PCI_IPR */
ret = PCI_INTA;
break;
default:
ret = 0x00;
break;
}
default:
ret = 0x00;
break;
}
// pclog("AB0B:071A: PCI_Read(%d, %04X) = %02X\n", func, addr, ret);
@@ -175,50 +181,51 @@ static void
pci_dummy_pci_write(int func, int addr, uint8_t val, void *priv)
{
pci_dummy_t *dev = (pci_dummy_t *) priv;
uint8_t valxor;
uint8_t valxor;
// pclog("AB0B:071A: PCI_Write(%d, %04X, %02X)\n", func, addr, val);
if (func == 0x00) switch (addr) {
case 0x04: /* PCI_COMMAND_LO */
valxor = (val & 0x03) ^ dev->pci_regs[addr];
if (valxor & PCI_COMMAND_IO) {
if (func == 0x00)
switch (addr) {
case 0x04: /* PCI_COMMAND_LO */
valxor = (val & 0x03) ^ dev->pci_regs[addr];
if (valxor & PCI_COMMAND_IO) {
pci_dummy_io_remove(dev);
if ((dev->pci_bar[0].addr != 0) && (val & PCI_COMMAND_IO))
pci_dummy_io_set(dev);
}
dev->pci_regs[addr] = val & 0x03;
break;
case 0x10: /* PCI_BAR */
val &= 0xe0; /* 0xe0 acc to RTL DS */
/*FALLTHROUGH*/
case 0x11: /* PCI_BAR */
/* Remove old I/O. */
pci_dummy_io_remove(dev);
if ((dev->pci_bar[0].addr != 0) && (val & PCI_COMMAND_IO))
pci_dummy_io_set(dev);
}
dev->pci_regs[addr] = val & 0x03;
break;
case 0x10: /* PCI_BAR */
val &= 0xe0; /* 0xe0 acc to RTL DS */
/*FALLTHROUGH*/
/* Set new I/O as per PCI request. */
dev->pci_bar[0].addr_regs[addr & 3] = val;
case 0x11: /* PCI_BAR */
/* Remove old I/O. */
pci_dummy_io_remove(dev);
/* Then let's calculate the new I/O base. */
dev->pci_bar[0].addr &= 0xffe0;
/* Set new I/O as per PCI request. */
dev->pci_bar[0].addr_regs[addr & 3] = val;
/* Log the new base. */
// pclog("AB0B:071A: PCI: new I/O base is %04X\n", dev->pci_bar[0].addr);
/* Then let's calculate the new I/O base. */
dev->pci_bar[0].addr &= 0xffe0;
/* We're done, so get out of the here. */
if (dev->pci_regs[4] & PCI_COMMAND_IO) {
if ((dev->pci_bar[0].addr) != 0)
pci_dummy_io_set(dev);
}
break;
/* Log the new base. */
// pclog("AB0B:071A: PCI: new I/O base is %04X\n", dev->pci_bar[0].addr);
/* We're done, so get out of the here. */
if (dev->pci_regs[4] & PCI_COMMAND_IO) {
if ((dev->pci_bar[0].addr) != 0)
pci_dummy_io_set(dev);
}
break;
case 0x3c: /* PCI_ILR */
pclog("AB0B:071A Device %02X: IRQ now: %i\n", dev->card, val);
dev->pci_regs[addr] = val;
return;
}
case 0x3c: /* PCI_ILR */
pclog("AB0B:071A Device %02X: IRQ now: %i\n", dev->card, val);
dev->pci_regs[addr] = val;
return;
}
}
static void
@@ -275,7 +282,7 @@ pci_dummy_init(int min_slot, int max_slot, int nb_slot, int sb_slot)
for (i = min_slot; i <= max_slot; i++) {
if ((i != nb_slot) && (i != sb_slot)) {
pci_register_slot(i, PCI_CARD_NORMAL, 1, 3, 2, 4);
pci_register_slot(i, PCI_CARD_NORMAL, 1, 3, 2, 4);
device_add_inst(&pci_dummy_device, j);
j++;
}

View File

@@ -51,7 +51,8 @@ static pc_timer_t pic_timer;
static int shadow = 0, elcr_enabled = 0,
tmr_inited = 0, latched = 0,
pic_pci = 0;
pic_pci = 0, kbd_latch = 0,
mouse_latch = 0;
static uint16_t smi_irq_mask = 0x0000,
smi_irq_status = 0x0000;
@@ -284,6 +285,12 @@ pic_set_shadow(int sh)
shadow = sh;
}
int
pic_get_pci_flag(void)
{
return pic_pci;
}
void
pic_set_pci_flag(int pci)
{
@@ -383,6 +390,23 @@ pic_command(pic_t *dev)
dev->auto_eoi_rotate = !!(dev->ocw2 & 0x80);
}
uint8_t
pic_latch_read(uint16_t addr, void *priv)
{
uint8_t ret = 0xff;
pic_log("pic_latch_read(%i, %i): %02X%02X\n", kbd_latch, mouse_latch, pic2.lines & 0x10, pic.lines & 0x02);
if (kbd_latch && (pic.lines & 0x02))
picintc(0x0002);
if (mouse_latch && (pic2.lines & 0x10))
picintc(0x1000);
/* Return FF - we just lower IRQ 1 and IRQ 12. */
return ret;
}
uint8_t
pic_read(uint16_t addr, void *priv)
{
@@ -514,10 +538,47 @@ pic_set_pci(void)
}
void
pic_init(void)
pic_kbd_latch(int enable)
{
pic_log("PIC keyboard latch now %sabled\n", enable ? "en" : "dis");
if (!!(enable | mouse_latch) != !!(kbd_latch | mouse_latch))
io_handler(!!(enable | mouse_latch), 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL);
kbd_latch = !!enable;
if (!enable)
picintc(0x0002);
}
void
pic_mouse_latch(int enable)
{
pic_log("PIC mouse latch now %sabled\n", enable ? "en" : "dis");
if (!!(kbd_latch | enable) != !!(kbd_latch | mouse_latch))
io_handler(!!(kbd_latch | enable), 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL);
mouse_latch = !!enable;
if (!enable)
picintc(0x1000);
}
static void
pic_reset_hard(void)
{
pic_reset();
pic_kbd_latch(0x00);
pic_mouse_latch(0x00);
}
void
pic_init(void)
{
pic_reset_hard();
shadow = 0;
io_sethandler(0x0020, 0x0002, pic_read, NULL, NULL, pic_write, NULL, NULL, &pic);
}
@@ -525,7 +586,7 @@ pic_init(void)
void
pic_init_pcjr(void)
{
pic_reset();
pic_reset_hard();
shadow = 0;
io_sethandler(0x0020, 0x0008, pic_read, NULL, NULL, pic_write, NULL, NULL, &pic);
@@ -582,6 +643,10 @@ picint_common(uint16_t num, int level, int set)
if (level)
pic2.lines |= (num >> 8);
/* Latch IRQ 12 if the mouse latch is enabled. */
if (mouse_latch && (num & 0x1000))
pic2.lines |= 0x10;
pic2.irr |= (num >> 8);
}
@@ -589,6 +654,9 @@ picint_common(uint16_t num, int level, int set)
if (level)
pic.lines |= (num & 0x00ff);
if (kbd_latch && (num & 0x0002))
pic.lines |= 0x02;
pic.irr |= (num & 0x00ff);
}
} else {
@@ -596,11 +664,13 @@ picint_common(uint16_t num, int level, int set)
if (num & 0xff00) {
pic2.lines &= ~(num >> 8);
pic2.irr &= ~(num >> 8);
}
if (num & 0x00ff) {
pic.lines &= ~(num & 0x00ff);
pic.irr &= ~(num & 0x00ff);
}
}

View File

@@ -14,3 +14,10 @@
#
add_library(print OBJECT png.c prt_cpmap.c prt_escp.c prt_text.c prt_ps.c)
if(APPLE)
find_library(GHOSTSCRIPT_LIB gs)
if (NOT GHOSTSCRIPT_LIB)
message(WARNING "Could not find ghostscript. The library will not be bundled and any related features will not work.")
endif()
endif ()

View File

@@ -299,7 +299,7 @@ endif()
# loads a macro to install Qt5 plugins on macOS
# based on https://stackoverflow.com/questions/35612687/cmake-macos-x-bundle-with-bundleutiliies-for-qt-application
macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var _prefix)
macro(install_qt5_plugin _qt_plugin_name _runtime_plugins_var _prefix)
get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION)
if(EXISTS "${_qt_plugin_path}")
get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME)
@@ -307,7 +307,7 @@ macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var _prefix)
get_filename_component(_qt_plugin_type "${_qt_plugin_type}" NAME)
set(_qt_plugin_dest "${_prefix}/PlugIns/${_qt_plugin_type}")
install(FILES "${_qt_plugin_path}" DESTINATION "${_qt_plugin_dest}")
list(APPEND ${_qt_plugins_var} "\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${_qt_plugin_dest}/${_qt_plugin_file}")
list(APPEND ${_runtime_plugins_var} "\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${_qt_plugin_dest}/${_qt_plugin_file}")
else()
message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found")
endif()
@@ -320,10 +320,25 @@ if (APPLE AND CMAKE_MACOSX_BUNDLE)
set(INSTALL_LIB_DIR "${prefix}/Frameworks")
# using the install_qt5_plugin to add Qt plugins into the macOS app bundle
install_qt5_plugin("Qt${QT_MAJOR}::QCocoaIntegrationPlugin" QT_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QMacStylePlugin" QT_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QICOPlugin" QT_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QICNSPlugin" QT_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QCocoaIntegrationPlugin" RUNTIME_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QMacStylePlugin" RUNTIME_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QICOPlugin" RUNTIME_PLUGINS ${prefix})
install_qt5_plugin("Qt${QT_MAJOR}::QICNSPlugin" RUNTIME_PLUGINS ${prefix})
# Install libraries that are loaded at runtime and not linked
if (GHOSTSCRIPT_LIB)
set(GS_LIBRARY_NAME "libgs.dylib")
file(REAL_PATH ${GHOSTSCRIPT_LIB} GS_LIB_RESOLVED)
install(FILES ${GS_LIB_RESOLVED} DESTINATION ${INSTALL_LIB_DIR} RENAME ${GS_LIBRARY_NAME})
list(APPEND RUNTIME_PLUGINS "\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${INSTALL_LIB_DIR}/${GS_LIBRARY_NAME}")
endif ()
if (FLUIDSYNTH_LIB)
set(FLUIDSYNTH_LIBRARY_NAME "libfluidsynth.dylib")
file(REAL_PATH ${FLUIDSYNTH_LIB} FLUIDSYNTH_LIB_RESOLVED)
install(FILES ${FLUIDSYNTH_LIB_RESOLVED} DESTINATION ${INSTALL_LIB_DIR} RENAME ${FLUIDSYNTH_LIBRARY_NAME})
list(APPEND RUNTIME_PLUGINS "\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${INSTALL_LIB_DIR}/${FLUIDSYNTH_LIBRARY_NAME}")
endif ()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf"
"[Paths]\nPlugins = PlugIns\n")
@@ -345,7 +360,7 @@ if (APPLE AND CMAKE_MACOSX_BUNDLE)
install(CODE "
include(BundleUtilities)
get_filename_component(CMAKE_INSTALL_PREFIX_ABSOLUTE \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX} ABSOLUTE)
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/86Box.app\" \"${QT_PLUGINS}\" \"${DIRS}\")
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/86Box.app\" \"${RUNTIME_PLUGINS}\" \"${DIRS}\")
execute_process(
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\"
\"\${CMAKE_INSTALL_PREFIX_ABSOLUTE}/${INSTALL_RUNTIME_DIR}/86Box\")
@@ -365,7 +380,7 @@ endif()
if (UNIX AND NOT APPLE AND NOT HAIKU)
find_package(X11 REQUIRED)
target_link_libraries(ui PRIVATE X11::X11 X11::Xi)
target_sources(ui PRIVATE xinput2_mouse.cpp)
target_sources(ui PRIVATE evdev_keyboard.cpp xinput2_mouse.cpp)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBEVDEV IMPORTED_TARGET libevdev)
if (LIBEVDEV_FOUND)
@@ -373,6 +388,22 @@ if (UNIX AND NOT APPLE AND NOT HAIKU)
target_link_libraries(ui PUBLIC PkgConfig::LIBEVDEV)
target_sources(ui PRIVATE evdev_mouse.cpp)
endif()
pkg_check_modules(XKBCOMMON IMPORTED_TARGET xkbcommon)
if (XKBCOMMON_FOUND)
target_compile_definitions(ui PRIVATE XKBCOMMON)
target_link_libraries(ui PUBLIC PkgConfig::XKBCOMMON)
target_sources(ui PRIVATE xkbcommon_keyboard.cpp)
if (X11_xcb_FOUND)
pkg_check_modules(XKBCOMMON_X11 IMPORTED_TARGET xkbcommon-x11)
if (XKBCOMMON_X11_FOUND)
target_compile_definitions(ui PRIVATE XKBCOMMON_X11)
target_link_libraries(ui PRIVATE X11::xcb PUBLIC PkgConfig::XKBCOMMON_X11)
target_sources(ui PRIVATE xkbcommon_x11_keyboard.cpp)
set(QT5_PRIVATE_HEADERS ON)
endif()
endif()
endif()
find_package(ECM NO_MODULE)
if (ECM_FOUND)
@@ -387,10 +418,22 @@ if (UNIX AND NOT APPLE AND NOT HAIKU)
ecm_add_wayland_client_protocol(WL_SOURCE_VAR PROTOCOL ${CMAKE_SOURCE_DIR}/wl_protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1)
target_include_directories(ui PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${Qt${QT_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
target_sources(ui PRIVATE ${WL_SOURCE_VAR} wl_mouse.cpp)
if (XKBCOMMON_FOUND)
target_sources(ui PRIVATE xkbcommon_wl_keyboard.cpp)
endif()
target_compile_definitions(ui PRIVATE WAYLAND)
set(QT5_PRIVATE_HEADERS ON)
endif()
endif()
endif()
# Add private headers for Qt5 if required.
if (NOT USE_QT6 AND DEFINED QT5_PRIVATE_HEADERS)
find_package(Qt${QT_MAJOR}Gui)
if (Qt${QT_MAJOR}Gui_FOUND)
include_directories(${Qt${QT_MAJOR}Gui_PRIVATE_INCLUDE_DIRS})
endif()
endif()
endif()
set(QM_FILES)
file(GLOB po_files "${CMAKE_CURRENT_SOURCE_DIR}/languages/*.po")

112
src/qt/be_keyboard.hpp Normal file
View File

@@ -0,0 +1,112 @@
static std::unordered_map<uint8_t, uint16_t> be_keycodes = {
{B_F1_KEY, 0x3b},
{B_F2_KEY, 0x3c},
{B_F3_KEY, 0x3d},
{B_F4_KEY, 0x3e},
{B_F5_KEY, 0x3f},
{B_F6_KEY, 0x40},
{B_F7_KEY, 0x41},
{B_F8_KEY, 0x42},
{B_F9_KEY, 0x43},
{B_F10_KEY, 0x44},
{B_F11_KEY, 0x57},
{B_F12_KEY, 0x58},
{B_PRINT_KEY, 0x137},
{B_SCROLL_KEY, 0x46},
{B_PAUSE_KEY, 0x145},
{B_KATAKANA_HIRAGANA, 0x70},
{B_HANKAKU_ZENKAKU, 0x76},
{0x01, 0x01}, /* Escape */
{0x11, 0x29},
{0x12, 0x02},
{0x13, 0x03},
{0x14, 0x04},
{0x15, 0x05},
{0x16, 0x06},
{0x17, 0x07},
{0x18, 0x08},
{0x19, 0x09},
{0x1a, 0x0a},
{0x1b, 0x0b},
{0x1c, 0x0c},
{0x1d, 0x0d},
{0x1e, 0x0e}, /* Backspace */
{0x1f, 0x152}, /* Insert */
{0x20, 0x147}, /* Home */
{0x21, 0x149}, /* Page Up */
{0x22, 0x45},
{0x23, 0x135},
{0x24, 0x37},
{0x25, 0x4a},
{0x26, 0x0f}, /* Tab */
{0x27, 0x10},
{0x28, 0x11},
{0x29, 0x12},
{0x2a, 0x13},
{0x2b, 0x14},
{0x2c, 0x15},
{0x2d, 0x16},
{0x2e, 0x17},
{0x2f, 0x18},
{0x30, 0x19},
{0x31, 0x1a},
{0x32, 0x1b},
{0x33, 0x2b},
{0x34, 0x153}, /* Delete */
{0x35, 0x14f}, /* End */
{0x36, 0x151}, /* Page Down */
{0x37, 0x47},
{0x38, 0x48},
{0x39, 0x49},
{0x3a, 0x4e},
{0x3b, 0x3a},
{0x3c, 0x1e},
{0x3d, 0x1f},
{0x3e, 0x20},
{0x3f, 0x21},
{0x40, 0x22},
{0x41, 0x23},
{0x42, 0x24},
{0x43, 0x25},
{0x44, 0x26},
{0x45, 0x27},
{0x46, 0x28},
{0x47, 0x1c}, /* Enter */
{0x48, 0x4b},
{0x49, 0x4c},
{0x4a, 0x4d},
{0x4b, 0x2a},
{0x4c, 0x2c},
{0x4d, 0x2d},
{0x4e, 0x2e},
{0x4f, 0x2f},
{0x50, 0x30},
{0x51, 0x31},
{0x52, 0x32},
{0x53, 0x33},
{0x54, 0x34},
{0x55, 0x35},
{0x56, 0x36},
{0x57, 0x148}, /* up arrow */
{0x58, 0x51},
{0x59, 0x50},
{0x5a, 0x4f},
{0x5b, 0x11c},
{0x5c, 0x1d},
{0x5d, 0x38},
{0x5e, 0x39}, /* space bar */
{0x5f, 0x138},
{0x60, 0x11d},
{0x61, 0x14b}, /* left arrow */
{0x62, 0x150}, /* down arrow */
{0x63, 0x14d}, /* right arrow */
{0x64, 0x52},
{0x65, 0x53},
{0x66, 0x15b},
{0x67, 0x15c},
{0x68, 0x15d},
{0x69, 0x56},
{0x7e, 0x137}, /* System Request */
{0x7f, 0x145}, /* Break */
};

129
src/qt/cocoa_keyboard.hpp Normal file
View File

@@ -0,0 +1,129 @@
static std::array<uint32_t, 127> cocoa_keycodes = { /* key names in parentheses are not declared by Apple headers */
0x1e, /* ANSI_A */
0x1f, /* ANSI_S */
0x20, /* ANSI_D */
0x21, /* ANSI_F */
0x23, /* ANSI_H */
0x22, /* ANSI_G */
0x2c, /* ANSI_Z */
0x2d, /* ANSI_X */
0x2e, /* ANSI_C */
0x2f, /* ANSI_V */
0x56, /* ISO_Section */
0x30, /* ANSI_B */
0x10, /* ANSI_Q */
0x11, /* ANSI_W */
0x12, /* ANSI_E */
0x13, /* ANSI_R */
0x15, /* ANSI_Y */
0x14, /* ANSI_T */
0x02, /* ANSI_1 */
0x03, /* ANSI_2 */
0x04, /* ANSI_3 */
0x05, /* ANSI_4 */
0x07, /* ANSI_6 */
0x06, /* ANSI_5 */
0x0d, /* ANSI_Equal */
0x0a, /* ANSI_9 */
0x08, /* ANSI_7 */
0x0c, /* ANSI_Minus */
0x09, /* ANSI_8 */
0x0b, /* ANSI_0 */
0x1b, /* ANSI_RightBracket */
0x18, /* ANSI_O */
0x16, /* ANSI_U */
0x1a, /* ANSI_LeftBracket */
0x17, /* ANSI_I */
0x19, /* ANSI_P */
0x1c, /* Return */
0x26, /* ANSI_L */
0x24, /* ANSI_J */
0x28, /* ANSI_Quote */
0x25, /* ANSI_K */
0x27, /* ANSI_Semicolon */
0x2b, /* ANSI_Backslash */
0x33, /* ANSI_Comma */
0x35, /* ANSI_Slash */
0x31, /* ANSI_N */
0x32, /* ANSI_M */
0x34, /* ANSI_Period */
0x0f, /* Tab */
0x39, /* Space */
0x29, /* ANSI_Grave */
0x0e, /* Delete => Backspace */
0x11c, /* (ANSI_KeypadEnter) */
0x01, /* Escape */
0x15c, /* (RightCommand) => Right Windows */
0x15b, /* (Left)Command => Left Windows */
0x2a, /* Shift */
0x3a, /* CapsLock */
0x38, /* Option */
0x1d, /* Control */
0x36, /* RightShift */
0x138, /* RightOption */
0x11d, /* RightControl */
0x15c, /* Function */
0x5e, /* F17 => F14 */
0x53, /* ANSI_KeypadDecimal */
0,
0x37, /* ANSI_KeypadMultiply */
0,
0x4e, /* ANSI_KeypadPlus */
0,
0x45, /* ANSI_KeypadClear => Num Lock (location equivalent) */
0x130, /* VolumeUp */
0x12e, /* VolumeDown */
0x120, /* Mute */
0x135, /* ANSI_KeypadDivide */
0x11c, /* ANSI_KeypadEnter */
0,
0x4a, /* ANSI_KeypadMinus */
0x5f, /* F18 => F15 */
0, /* F19 */
0x59, /* ANSI_KeypadEquals */
0x52, /* ANSI_Keypad0 */
0x4f, /* ANSI_Keypad1 */
0x50, /* ANSI_Keypad2 */
0x51, /* ANSI_Keypad3 */
0x4b, /* ANSI_Keypad4 */
0x4c, /* ANSI_Keypad5 */
0x4d, /* ANSI_Keypad6 */
0x47, /* ANSI_Keypad7 */
0, /* F20 */
0x48, /* ANSI_Keypad8 */
0x49, /* ANSI_Keypad9 */
0x7d, /* JIS_Yen */
0x73, /* JIS_Underscore */
0x5c, /* JIS_KeypadComma */
0x3f, /* F5 */
0x40, /* F6 */
0x41, /* F7 */
0x3d, /* F3 */
0x42, /* F8 */
0x43, /* F9 */
0x7b, /* JIS_Eisu => muhenkan (location equivalent) */
0x57, /* F11 */
0x79, /* JIS_Kana => henkan (location equivalent) */
0x137, /* F13 => SysRq (location equivalent) */
0x5d, /* F16 => F13 */
0x46, /* F14 => Scroll Lock (location equivalent) */
0,
0x44, /* F10 */
0x15d, /* (Menu) */
0x58, /* F12 */
0,
0x145, /* F15 => Pause (location equivalent) */
0x152, /* Help => Insert (location equivalent) */
0x147, /* Home */
0x149, /* PageUp */
0x153, /* ForwardDelete */
0x3e, /* F4 */
0x14f, /* End */
0x3c, /* F2 */
0x151, /* PageDown */
0x3b, /* F1 */
0x14b, /* LeftArrow */
0x14d, /* RightArrow */
0x150, /* DownArrow */
0x148, /* UpArrow */
};

163
src/qt/evdev_keyboard.cpp Normal file
View File

@@ -0,0 +1,163 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* evdev keyboard input module.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2023 RichardG.
*/
#include <unordered_map>
#include <QtDebug>
static std::unordered_map<uint32_t, uint16_t> evdev_keycodes = {
{184, 0x46}, /* F14 => Scroll Lock (for Apple keyboards) */
{86, 0x56}, /* 102ND */
{87, 0x57}, /* F11 */
{88, 0x58}, /* F12 */
{186, 0x5d}, /* F16 => F13 */
{187, 0x5e}, /* F17 => F14 */
{188, 0x5f}, /* F18 => F15 */
/* Japanese keys. */
{95, 0x5c}, /* KPJPCOMMA */
{93, 0x70}, /* KATAKANAHIRAGANA */
{89, 0x73}, /* RO */
{85, 0x76}, /* ZENKAKUHANKAKU */
{91, 0x77}, /* HIRAGANA */
{90, 0x78}, /* KATAKANA */
{92, 0x79}, /* HENKAN */
{94, 0x7b}, /* MUHENKAN */
{124, 0x7d}, /* YEN */
{121, 0x7e}, /* KPCOMMA */
/* Korean keys. */
{123, 0xf1}, /* HANJA */
{122, 0xf2}, /* HANGUL */
{96, 0x11c}, /* KPENTER */
{97, 0x11d}, /* RIGHTCTRL */
{98, 0x135}, /* KPSLASH */
{99, 0x137}, /* SYSRQ */
{183, 0x137}, /* F13 => SysRq (for Apple keyboards) */
{100, 0x138}, /* RIGHTALT */
{119, 0x145}, /* PAUSE */
{411, 0x145}, /* BREAK */
{185, 0x145}, /* F15 => Pause (for Apple keyboards) */
{102, 0x147}, /* HOME */
{103, 0x148}, /* UP */
{104, 0x149}, /* PAGEUP */
{105, 0x14b}, /* LEFT */
{106, 0x14d}, /* RIGHT */
{107, 0x14f}, /* END */
{108, 0x150}, /* DOWN */
{109, 0x151}, /* PAGEDOWN */
{110, 0x152}, /* INSERT */
{111, 0x153}, /* DELETE */
{125, 0x15b}, /* LEFTMETA */
{126, 0x15c}, /* RIGHTMETA */
{127, 0x15d}, /* COMPOSE => Menu */
/* Multimedia keys. Guideline is to try and follow the Microsoft standard, then
fill in remaining scancodes with OEM-specific keys for redundancy sake. Keys
marked with # are not translated into evdev codes by the standard atkbd driver. */
{634, 0x54}, /* SELECTIVE_SCREENSHOT# => Alt+SysRq */
{117, 0x59}, /* KPEQUAL */
{418, 0x6a}, /* ZOOMIN# => Logitech */
{420, 0x6b}, /* ZOOMRESET# => Logitech */
{223, 0x6d}, /* CANCEL# => Logitech */
{132, 0x101}, /* # Logitech Task Select */
{148, 0x102}, /* PROG1# => Samsung */
{149, 0x103}, /* PROG2# => Samsung */
{419, 0x104}, /* ZOOMOUT# => Logitech */
{144, 0x105}, /* FILE# => Messenger/Files */
{216, 0x105}, /* CHAT# => Messenger/Files */
{430, 0x105}, /* MESSENGER# */
{182, 0x107}, /* REDO# */
{131, 0x108}, /* UNDO# */
{135, 0x10a}, /* PASTE# */
{177, 0x10b}, /* SCROLLUP# => normal speed */
{165, 0x110}, /* PREVIOUSSONG */
{136, 0x112}, /* FIND# => Logitech */
{421, 0x113}, /* WORDPROCESSOR# => Word */
{423, 0x114}, /* SPREADSHEET# => Excel */
{397, 0x115}, /* CALENDAR# */
{433, 0x116}, /* LOGOFF# */
{137, 0x117}, /* CUT# */
{133, 0x118}, /* COPY# */
{163, 0x119}, /* NEXTSONG */
{154, 0x11e}, /* CYCLEWINDOWS => Application Right (no left counterpart) */
{113, 0x120}, /* MUTE */
{140, 0x121}, /* CALC */
{164, 0x122}, /* PLAYPAUSE */
{432, 0x123}, /* SPELLCHECK# */
{166, 0x124}, /* STOPCD */
{139, 0x126}, /* MENU# => Shortcut/Menu/Help for a few OEMs */
{114, 0x12e}, /* VOL- */
{160, 0x12f}, /* CLOSECD# => Logitech Eject */
{161, 0x12f}, /* EJECTCD# => Logitech */
{162, 0x12f}, /* EJECTCLOSECD# => Logitech */
{115, 0x130}, /* VOL+ */
{150, 0x132}, /* WWW# */
{172, 0x132}, /* HOMEPAGE */
{138, 0x13b}, /* HELP# */
{213, 0x13c}, /* SOUND# => My Music/Office Home */
{360, 0x13c}, /* VENDOR# => My Music/Office Home */
{204, 0x13d}, /* DASHBOARD# => Task Pane */
{181, 0x13e}, /* NEW# */
{134, 0x13f}, /* OPEN# */
{206, 0x140}, /* CLOSE# */
{232, 0x141}, /* REPLY# */
{233, 0x142}, /* FORWARDMAIL# */
{231, 0x143}, /* SEND# */
{151, 0x144}, /* MSDOS# */
{112, 0x14c}, /* MACRO */
{179, 0x14c}, /* KPLEFTPAREN# */
{118, 0x14e}, /* KPPLUSMINUS */
{235, 0x155}, /* DOCUMENTS# => Logitech */
{234, 0x157}, /* SAVE# */
{210, 0x158}, /* PRINT# */
{116, 0x15e}, /* POWER */
{142, 0x15f}, /* SLEEP */
{143, 0x163}, /* WAKEUP */
{180, 0x164}, /* KPRIGHTPAREN# */
{212, 0x164}, /* CAMERA# => My Pictures */
{217, 0x165}, /* SEARCH */
{156, 0x166}, /* BOOKMARKS => Favorites */
{364, 0x166}, /* FAVORITES# */
{173, 0x167}, /* REFRESH */
{128, 0x168}, /* STOP */
{159, 0x169}, /* FORWARD */
{158, 0x16a}, /* BACK */
{157, 0x16b}, /* COMPUTER */
{155, 0x16c}, /* MAIL */
{215, 0x16c}, /* EMAIL# */
{226, 0x16d}, /* MEDIA */
{167, 0x178}, /* RECORD# => Logitech */
{152, 0x17a}, /* COFFEE/SCREENLOCK# */
{178, 0x18b}, /* SCROLLDOWN# => normal speed */
};
uint16_t
evdev_translate(uint32_t keycode)
{
/* "for 1-83 (0x01-0x53) scancode equals keycode" */
auto ret = (keycode <= 0x53) ? keycode : evdev_keycodes[keycode];
if (!ret)
qWarning() << "Evdev Keyboard: Unknown key" << keycode;
#if 0
else
qInfo() << "Evdev Keyboard: Key" << keycode << "scancode" << QString::number(ret, 16);
#endif
return ret;
}

20
src/qt/evdev_keyboard.hpp Normal file
View File

@@ -0,0 +1,20 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Definitions for evdev keyboard input module.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2023 RichardG.
*/
#ifndef EVDEV_KEYBOARD_HPP
#define EVDEV_KEYBOARD_HPP
uint16_t evdev_translate(uint32_t keycode);
#endif

View File

@@ -206,13 +206,13 @@ msgid "&About 86Box..."
msgstr "關於 86Box(&A)..."
msgid "&New image..."
msgstr "新增像(&N)..."
msgstr "新增像(&N)..."
msgid "&Existing image..."
msgstr "開啟已存在的像(&E)..."
msgstr "開啟已存在的像(&E)..."
msgid "Existing image (&Write-protected)..."
msgstr "開啟已存在的像並寫保護(&W)..."
msgstr "開啟已存在的像並寫保護(&W)..."
msgid "&Record"
msgstr "錄製(&R)"
@@ -227,10 +227,10 @@ msgid "&Fast forward to the end"
msgstr "快進至終點(&F)"
msgid "E&ject"
msgstr "出(&J)"
msgstr "退出(&J)"
msgid "&Image..."
msgstr "像(&I)..."
msgstr "像(&I)..."
msgid "E&xport to 86F..."
msgstr "匯出為 86F 格式(&x)..."
@@ -242,7 +242,7 @@ msgid "E&mpty"
msgstr "空置光碟機(&M)"
msgid "&Reload previous image"
msgstr "載入上一個像(&R)"
msgstr "載入上一個像(&R)"
msgid "&Folder..."
msgstr "資料夾(&F)..."
@@ -272,7 +272,7 @@ msgid "&VSync"
msgstr "垂直同步(&V)"
msgid "&Select shader..."
msgstr "選著色器(&S)..."
msgstr "選著色器(&S)..."
msgid "&Remove shader"
msgstr "移除著色器(&R)"
@@ -284,7 +284,7 @@ msgid "Sound Gain"
msgstr "音量增益"
msgid "New Image"
msgstr "新增像"
msgstr "新增像"
msgid "Settings"
msgstr "設定"
@@ -533,7 +533,7 @@ msgid "&New..."
msgstr "新增(&N)..."
msgid "&Existing..."
msgstr "已有像(&E)..."
msgstr "已有像(&E)..."
msgid "&Remove"
msgstr "移除(&R)"
@@ -566,10 +566,10 @@ msgid "Type:"
msgstr "類型:"
msgid "Image Format:"
msgstr "像格式:"
msgstr "像格式:"
msgid "Block Size:"
msgstr "塊大小:"
msgstr "塊大小:"
msgid "Floppy drives:"
msgstr "軟碟機:"
@@ -647,10 +647,10 @@ msgid "ZIP %03i %i (%s): %ls"
msgstr "ZIP %03i %i (%s): %ls"
msgid "ZIP images"
msgstr "ZIP 像"
msgstr "ZIP 像"
msgid "86Box could not find any usable ROM images.\n\nPlease <a href=\"https://github.com/86Box/roms/releases/latest\">download</a> a ROM set and extract it into the \"roms\" directory."
msgstr "86Box 找不到任何可用的 ROM 像。\n\n請<a href=\"https://github.com/86Box/roms/releases/latest\">下載</a>ROM 包並將其解壓到 \"roms\" 資料夾。"
msgstr "86Box 找不到任何可用的 ROM 像。\n\n請<a href=\"https://github.com/86Box/roms/releases/latest\">下載</a>ROM 包並將其解壓到 \"roms\" 資料夾。"
msgid "(empty)"
msgstr "(空)"
@@ -668,13 +668,13 @@ msgid "Off"
msgstr "關"
msgid "All images"
msgstr "所有像"
msgstr "所有像"
msgid "Basic sector images"
msgstr "基本磁區像"
msgstr "基本磁區像"
msgid "Surface images"
msgstr "表面像"
msgstr "表面像"
msgid "Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine."
msgstr "由於 roms/machines 資料夾中缺少合適的 ROM機型 \"%hs\" 不可用。將切換到其他可用機型。"
@@ -710,7 +710,7 @@ msgid "Floppy & CD-ROM drives"
msgstr "軟碟/光碟機"
msgid "Other removable devices"
msgstr "其他可移除裝置"
msgstr "其他卸除式裝置"
msgid "Other peripherals"
msgstr "其他周邊裝置"
@@ -806,10 +806,10 @@ msgid "Floppy %i (%s): %ls"
msgstr "軟碟 %i (%s): %ls"
msgid "Advanced sector images"
msgstr "進階磁區像"
msgstr "進階磁區像"
msgid "Flux images"
msgstr "Flux 像"
msgstr "Flux 像"
msgid "Unable to initialize FreeType"
msgstr "無法初始化 FreeType"
@@ -830,7 +830,7 @@ msgid "MO %i (%ls): %ls"
msgstr "磁光碟 %i (%ls): %ls"
msgid "MO images"
msgstr "磁光碟像"
msgstr "磁光碟像"
msgid "Welcome to 86Box!"
msgstr "歡迎使用 86Box"
@@ -845,13 +845,13 @@ msgid "No ROMs found"
msgstr "找不到 ROM"
msgid "Do you want to save the settings?"
msgstr "要存設定嗎?"
msgstr "要存設定嗎?"
msgid "This will hard reset the emulated machine."
msgstr "此操作將硬重設模擬器。"
msgid "Save"
msgstr "存"
msgstr "存"
msgid "About 86Box"
msgstr "關於 86Box"
@@ -893,7 +893,7 @@ msgid "libgs"
msgstr "libgs"
msgid " is required for automatic conversion of PostScript files to PDF.\n\nAny documents sent to the generic PostScript printer will be saved as PostScript (.ps) files."
msgstr " 是將 PostScript 檔案轉換為 PDF 所需要的庫。\n\n使用通用 PostScript 印表機列印的文件將被存為 PostScript (.ps) 檔案。"
msgstr " 是將 PostScript 檔案轉換為 PDF 所需要的庫。\n\n使用通用 PostScript 印表機列印的文件將被存為 PostScript (.ps) 檔案。"
msgid "libfluidsynth.dll"
msgstr "libfluidsynth.dll"
@@ -920,7 +920,7 @@ msgid "Don't reset"
msgstr "不重設"
msgid "CD-ROM images"
msgstr "光碟像"
msgstr "光碟像"
msgid "%hs Device Configuration"
msgstr "%hs 裝置配置"
@@ -947,13 +947,13 @@ msgid "Cassette: %s"
msgstr "磁帶: %s"
msgid "Cassette images"
msgstr "磁帶像"
msgstr "磁帶像"
msgid "Cartridge %i: %ls"
msgstr "卡帶 %i: %ls"
msgid "Cartridge images"
msgstr "卡帶像"
msgstr "卡帶像"
msgid "Error initializing renderer"
msgstr "初始化渲染器時出錯"
@@ -1004,13 +1004,13 @@ msgid "Add Existing Hard Disk"
msgstr "添加已存在的硬碟"
msgid "HDI disk images cannot be larger than 4 GB."
msgstr "HDI 磁碟像不能超過 4 GB。"
msgstr "HDI 磁碟像不能超過 4 GB。"
msgid "Disk images cannot be larger than 127 GB."
msgstr "磁碟像不能超過 127 GB。"
msgstr "磁碟像不能超過 127 GB。"
msgid "Hard disk images"
msgstr "硬碟像"
msgstr "硬碟像"
msgid "Unable to read file"
msgstr "無法讀取檔案"
@@ -1019,37 +1019,37 @@ msgid "Unable to write file"
msgstr "無法寫入檔案"
msgid "HDI or HDX images with a sector size other than 512 are not supported."
msgstr "不支援非 512 位元組磁區大小的 HDI 或 HDX 像。"
msgstr "不支援非 512 位元組磁區大小的 HDI 或 HDX 像。"
msgid "USB is not yet supported"
msgstr "尚未支援 USB"
msgid "Disk image file already exists"
msgstr "磁碟像檔案已存在"
msgstr "磁碟像檔案已存在"
msgid "Please specify a valid file name."
msgstr "請指定有效的檔案名。"
msgid "Disk image created"
msgstr "已創建磁碟像"
msgstr "已創建磁碟像"
msgid "Make sure the file exists and is readable."
msgstr "請確定此檔案已存在並可讀取。"
msgid "Make sure the file is being saved to a writable directory."
msgstr "請確定此檔案存在可寫目錄中。"
msgstr "請確定此檔案存在可寫目錄中。"
msgid "Disk image too large"
msgstr "磁碟像太大"
msgstr "磁碟像太大"
msgid "Remember to partition and format the newly-created drive."
msgstr "請記得為新創建的像分區並格式化。"
msgstr "請記得為新創建的像分區並格式化。"
msgid "The selected file will be overwritten. Are you sure you want to use it?"
msgstr "選定的檔案將被覆蓋。確定繼續使用此檔案嗎?"
msgid "Unsupported disk image"
msgstr "不支援的磁碟像"
msgstr "不支援的磁碟像"
msgid "Overwrite"
msgstr "覆蓋"
@@ -1058,13 +1058,13 @@ msgid "Don't overwrite"
msgstr "不覆蓋"
msgid "Raw image (.img)"
msgstr "原始像 (.img)"
msgstr "原始像 (.img)"
msgid "HDI image (.hdi)"
msgstr "HDI 像 (.hdi)"
msgstr "HDI 像 (.hdi)"
msgid "HDX image (.hdx)"
msgstr "HDX 像 (.hdx)"
msgstr "HDX 像 (.hdx)"
msgid "Fixed-size VHD (.vhd)"
msgstr "固定大小 VHD (.vhd)"
@@ -1076,19 +1076,19 @@ msgid "Differencing VHD (.vhd)"
msgstr "差分 VHD (.vhd)"
msgid "Large blocks (2 MB)"
msgstr "大塊 (2 MB)"
msgstr "大塊 (2 MB)"
msgid "Small blocks (512 KB)"
msgstr "小塊 (512 KB)"
msgstr "小塊 (512 KB)"
msgid "VHD files"
msgstr "VHD 檔案"
msgid "Select the parent VHD"
msgstr "選父 VHD 檔案"
msgstr "選父 VHD 檔案"
msgid "This could mean that the parent image was modified after the differencing image was created.\n\nIt can also happen if the image files were moved or copied, or by a bug in the program that created this disk.\n\nDo you want to fix the timestamps?"
msgstr "父映像可能在創建差異像後被修改。\n\n如果像檔案被移動或複製,或創建此磁碟的程式中存在錯誤,也可能發生這種情況。\n\n是否需要修復時間戳"
msgstr "父映像可能在創建差異像後被修改。\n\n如果像檔案被移動或複製,或創建此磁碟的程式中存在錯誤,也可能發生這種情況。\n\n是否需要修復時間戳"
msgid "Parent and child disk timestamps do not match"
msgstr "父碟與子碟的時間戳不匹配"
@@ -1175,34 +1175,34 @@ msgid "ZIP 100"
msgstr "ZIP 100"
msgid "3.5\" 128 MB (ISO 10090)"
msgstr "3.5 英 128 MB (ISO 10090)"
msgstr "3.5 英 128 MB (ISO 10090)"
msgid "3.5\" 230 MB (ISO 13963)"
msgstr "3.5 英 230 MB (ISO 13963)"
msgstr "3.5 英 230 MB (ISO 13963)"
msgid "3.5\" 540 MB (ISO 15498)"
msgstr "3.5 英 540 MB (ISO 15498)"
msgstr "3.5 英 540 MB (ISO 15498)"
msgid "3.5\" 640 MB (ISO 15498)"
msgstr "3.5 英 640 MB (ISO 15498)"
msgstr "3.5 英 640 MB (ISO 15498)"
msgid "3.5\" 1.3 GB (GigaMO)"
msgstr "3.5 英 1.3 GB (GigaMO)"
msgstr "3.5 英 1.3 GB (GigaMO)"
msgid "3.5\" 2.3 GB (GigaMO 2)"
msgstr "3.5 英 2.3 GB (GigaMO 2)"
msgstr "3.5 英 2.3 GB (GigaMO 2)"
msgid "5.25\" 600 MB"
msgstr "5.25 英 600 MB"
msgstr "5.25 英 600 MB"
msgid "5.25\" 650 MB"
msgstr "5.25 英 650 MB"
msgstr "5.25 英 650 MB"
msgid "5.25\" 1 GB"
msgstr "5.25 英 1 GB"
msgstr "5.25 英 1 GB"
msgid "5.25\" 1.3 GB"
msgstr "5.25 英 1.3 GB"
msgstr "5.25 英 1.3 GB"
msgid "Perfect RPM"
msgstr "標準轉速 (RPM)"
@@ -1218,4 +1218,3 @@ msgstr "低於標準轉速的 2%"
msgid "(System Default)"
msgstr "(系統預設)"

View File

@@ -23,7 +23,6 @@ extern "C" {
#include <86box/86box.h>
#include <86box/hdd.h>
#include "../disk/minivhd/minivhd.h"
#include "../disk/minivhd/minivhd_util.h"
}
#include <thread>

View File

@@ -138,7 +138,11 @@ main_thread_fn()
}
is_quit = 1;
QTimer::singleShot(0, QApplication::instance(), []() { QApplication::instance()->quit(); });
if (gfxcard[1]) {
ui_deinit_monitor(1);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
QTimer::singleShot(0, QApplication::instance(), []() { QApplication::processEvents(); QApplication::instance()->quit(); });
}
static std::thread *main_thread;

View File

@@ -96,8 +96,17 @@ extern int qt_nvr_save(void);
#include "qt_util.hpp"
#if defined __unix__ && !defined __HAIKU__
# ifdef WAYLAND
# include "wl_mouse.hpp"
# ifndef Q_OS_MACOS
# include "evdev_keyboard.hpp"
# endif
# ifdef XKBCOMMON
# include "xkbcommon_keyboard.hpp"
# ifdef XKBCOMMON_X11
# include "xkbcommon_x11_keyboard.hpp"
# endif
# ifdef WAYLAND
# include "xkbcommon_wl_keyboard.hpp"
# endif
# endif
# include <X11/Xlib.h>
# include <X11/keysym.h>
@@ -106,6 +115,7 @@ extern int qt_nvr_save(void);
#endif
#ifdef Q_OS_MACOS
# include "cocoa_keyboard.hpp"
// The namespace is required to avoid clashing typedefs; we only use this
// header for its #defines anyway.
namespace IOKit {
@@ -116,6 +126,7 @@ namespace IOKit {
#ifdef __HAIKU__
# include <os/AppKit.h>
# include <os/InterfaceKit.h>
# include "be_keyboard.hpp"
extern MainWindow *main_window;
@@ -569,7 +580,6 @@ MainWindow::MainWindow(QWidget *parent)
}
#ifdef Q_OS_MACOS
ui->actionFullscreen->setShortcutVisibleInContextMenu(true);
ui->actionCtrl_Alt_Del->setShortcutVisibleInContextMenu(true);
ui->actionTake_screenshot->setShortcutVisibleInContextMenu(true);
#endif
@@ -648,6 +658,20 @@ MainWindow::MainWindow(QWidget *parent)
} else {
ui->actionCursor_Puck->setChecked(true);
}
#ifdef XKBCOMMON
# ifdef XKBCOMMON_X11
if (QApplication::platformName().contains("xcb"))
xkbcommon_x11_init();
else
# endif
# ifdef WAYLAND
if (QApplication::platformName().contains("wayland"))
xkbcommon_wl_init();
else
# endif
{}
#endif
}
void
@@ -733,7 +757,9 @@ MainWindow::initRendererMonitorSlot(int monitor_index)
secondaryRenderer->showMaximized();
}
secondaryRenderer->switchRenderer((RendererStack::Renderer) vid_api);
secondaryRenderer->setMouseTracking(true);
}
connect(this, &MainWindow::pollMouse, secondaryRenderer.get(), &RendererStack::mousePoll, Qt::DirectConnection);
}
}
@@ -880,652 +906,76 @@ MainWindow::on_actionSettings_triggered()
plat_pause(currentPause);
}
#if defined(__unix__) && !defined(__HAIKU__)
std::array<uint32_t, 256> x11_to_xt_base {
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x1C,
0x1D,
0x1E,
0x1F,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x2A,
0x2B,
0x2C,
0x2D,
0x2E,
0x2F,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43,
0x44,
0x45,
0x46,
0x47,
0x48,
0x49,
0x4A,
0x4B,
0x4C,
0x4D,
0x4E,
0x4F,
0x50,
0x51,
0x52,
0x53,
0x54,
0x55,
0x56,
0x57,
0x58,
0x147,
0x148,
0x149,
0,
0x14B,
0,
0x14D,
0x14F,
0x150,
0x151,
0x152,
0x153,
0x11C,
0x11D,
0, // Pause/Break key.
0x137,
0x135,
0x138,
0, // Ditto as above comment.
0x15B,
0x15C,
0x15D,
};
std::array<uint32_t, 256> x11_to_xt_2 {
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x1C,
0x1D,
0x1E,
0x1F,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x2A,
0x2B,
0x2C,
0x2D,
0x2E,
0x2F,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43,
0x44,
0x45,
0x46,
0x47,
0x48,
0x49,
0x4A,
0x4B,
0x4C,
0x4D,
0x4E,
0x4F,
0x50,
0x51,
0x52,
0x53,
0x138,
0x55,
0x56,
0x57,
0x58,
0x56,
0x70,
0x7B,
0x7D,
0x2B,
0x7E,
0,
0x11C,
0x11D,
0x135,
0x137,
0x138,
0,
0x147,
0x148,
0x149,
0x14B,
0x14D,
0x14F,
0x150,
0x151,
0x152,
0x153,
0,
0, /* Mute */
0, /* Volume Down */
0, /* Volume Up */
0, /* Power Off */
0,
0,
0,
0,
0,
0x70,
0x7B,
0x73,
0x15B,
0x15C,
0x15D
};
std::array<uint32_t, 256> x11_to_xt_vnc {
0,
0,
0,
0,
0,
0,
0,
0,
0x1D,
0x11D,
0x2A,
0x36,
0,
0,
0x38,
0x138,
0x39,
0x0B,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0C,
0x0D,
0x1A,
0x1B,
0x27,
0x28,
0x29,
0x33,
0x34,
0x35,
0x2B,
0x1E,
0x30,
0x2E,
0x20,
0x12,
0x21,
0x22,
0x23,
0x17,
0x24,
0x25,
0x26,
0x32,
0x31,
0x18,
0x19,
0x10,
0x13,
0x1F,
0x14,
0x16,
0x2F,
0x11,
0x2D,
0x15,
0x2C,
0x0E,
0x1C,
0x0F,
0x01,
0x153,
0x147,
0x14F,
0x149,
0x151,
0x148,
0x150,
0x14B,
0x14D,
};
#endif
#ifdef Q_OS_MACOS
std::array<uint32_t, 256> darwin_to_xt {
0x1E,
0x1F,
0x20,
0x21,
0x23,
0x22,
0x2C,
0x2D,
0x2E,
0x2F,
0x2B,
0x30,
0x10,
0x11,
0x12,
0x13,
0x15,
0x14,
0x02,
0x03,
0x04,
0x05,
0x07,
0x06,
0x0D,
0x0A,
0x08,
0x0C,
0x09,
0x0B,
0x1B,
0x18,
0x16,
0x1A,
0x17,
0x19,
0x1C,
0x26,
0x24,
0x28,
0x25,
0x27,
0x2B,
0x33,
0x35,
0x31,
0x32,
0x34,
0x0F,
0x39,
0x29,
0x0E,
0x11C,
0x01,
0x15C,
0x15B,
0x2A,
0x3A,
0x38,
0x1D,
0x36,
0x138,
0x11D,
0x15C,
0,
0x53,
0,
0x37,
0,
0x4E,
0,
0x45,
0x130,
0x12E,
0x120,
0x135,
0x11C,
0,
0x4A,
0,
0,
0,
0x52,
0x4F,
0x50,
0x51,
0x4B,
0x4C,
0x4D,
0x47,
0,
0x48,
0x49,
0,
0,
0,
0x3F,
0x40,
0x41,
0x3D,
0x42,
0x43,
0,
0x57,
0,
0x137,
0,
0x46,
0,
0x44,
0x15D,
0x58,
0,
0, // Pause/Break key.
0x152,
0x147,
0x149,
0x153,
0x3E,
0x14F,
0x3C,
0x151,
0x3B,
0x14B,
0x14D,
0x150,
0x148,
0,
};
#endif
#if defined(__unix__) && !defined(__HAIKU__)
static std::unordered_map<uint32_t, uint16_t> evdev_to_xt = {
{96, 0x11C},
{ 97, 0x11D},
{ 98, 0x135},
{ 99, 0x71 },
{ 100, 0x138},
{ 101, 0x1C },
{ 102, 0x147},
{ 103, 0x148},
{ 104, 0x149},
{ 105, 0x14B},
{ 106, 0x14D},
{ 107, 0x14F},
{ 108, 0x150},
{ 109, 0x151},
{ 110, 0x152},
{ 111, 0x153}
};
#endif
#ifdef __HAIKU__
static std::unordered_map<uint8_t, uint16_t> be_to_xt = {
{0x01, 0x01 },
{ B_F1_KEY, 0x3B },
{ B_F2_KEY, 0x3C },
{ B_F3_KEY, 0x3D },
{ B_F4_KEY, 0x3E },
{ B_F5_KEY, 0x3F },
{ B_F6_KEY, 0x40 },
{ B_F7_KEY, 0x41 },
{ B_F8_KEY, 0x42 },
{ B_F9_KEY, 0x43 },
{ B_F10_KEY, 0x44 },
{ B_F11_KEY, 0x57 },
{ B_F12_KEY, 0x58 },
{ 0x11, 0x29 },
{ 0x12, 0x02 },
{ 0x13, 0x03 },
{ 0x14, 0x04 },
{ 0x15, 0x05 },
{ 0x16, 0x06 },
{ 0x17, 0x07 },
{ 0x18, 0x08 },
{ 0x19, 0x09 },
{ 0x1A, 0x0A },
{ 0x1B, 0x0B },
{ 0x1C, 0x0C },
{ 0x1D, 0x0D },
{ 0x1E, 0x0E },
{ 0x1F, 0x152},
{ 0x20, 0x147},
{ 0x21, 0x149},
{ 0x22, 0x45 },
{ 0x23, 0x135},
{ 0x24, 0x37 },
{ 0x25, 0x4A },
{ 0x26, 0x0F },
{ 0x27, 0x10 },
{ 0x28, 0x11 },
{ 0x29, 0x12 },
{ 0x2A, 0x13 },
{ 0x2B, 0x14 },
{ 0x2C, 0x15 },
{ 0x2D, 0x16 },
{ 0x2E, 0x17 },
{ 0x2F, 0x18 },
{ 0x30, 0x19 },
{ 0x31, 0x1A },
{ 0x32, 0x1B },
{ 0x33, 0x2B },
{ 0x34, 0x153},
{ 0x35, 0x14F},
{ 0x36, 0x151},
{ 0x37, 0x47 },
{ 0x38, 0x48 },
{ 0x39, 0x49 },
{ 0x3A, 0x4E },
{ 0x3B, 0x3A },
{ 0x3C, 0x1E },
{ 0x3D, 0x1F },
{ 0x3E, 0x20 },
{ 0x3F, 0x21 },
{ 0x40, 0x22 },
{ 0x41, 0x23 },
{ 0x42, 0x24 },
{ 0x43, 0x25 },
{ 0x44, 0x26 },
{ 0x45, 0x27 },
{ 0x46, 0x28 },
{ 0x47, 0x1C },
{ 0x48, 0x4B },
{ 0x49, 0x4C },
{ 0x4A, 0x4D },
{ 0x4B, 0x2A },
{ 0x4C, 0x2C },
{ 0x4D, 0x2D },
{ 0x4E, 0x2E },
{ 0x4F, 0x2F },
{ 0x50, 0x30 },
{ 0x51, 0x31 },
{ 0x52, 0x32 },
{ 0x53, 0x33 },
{ 0x54, 0x34 },
{ 0x55, 0x35 },
{ 0x56, 0x36 },
{ 0x57, 0x148},
{ 0x58, 0x51 },
{ 0x59, 0x50 },
{ 0x5A, 0x4F },
{ 0x5B, 0x11C},
{ 0x5C, 0x1D },
{ 0x5D, 0x38 },
{ 0x5E, 0x39 },
{ 0x5F, 0x138},
{ 0x60, 0x11D},
{ 0x61, 0x14B},
{ 0x62, 0x150},
{ 0x63, 0x14D},
{ 0x64, 0x52 },
{ 0x65, 0x53 },
{ 0x0e, 0x137},
{ 0x0f, 0x46 },
{ 0x66, 0x15B},
{ 0x67, 0x15C},
{ 0x68, 0x15D},
{ 0x69, 0x56 }
};
#endif
#if defined(__unix__) && !defined(__HAIKU__)
static std::array<uint32_t, 256> &selected_keycode = x11_to_xt_base;
#endif
uint16_t
x11_keycode_to_keysym(uint32_t keycode)
void
MainWindow::processKeyboardInput(bool down, uint32_t keycode)
{
uint16_t finalkeycode = 0;
#if defined(Q_OS_WINDOWS)
finalkeycode = (keycode & 0xFFFF);
#if defined(Q_OS_WINDOWS) /* non-raw input */
keycode &= 0xffff;
#elif defined(Q_OS_MACOS)
finalkeycode = darwin_to_xt[keycode];
keycode = (keycode < 127) ? cocoa_keycodes[keycode] : 0;
#elif defined(__HAIKU__)
finalkeycode = be_to_xt[keycode];
keycode = be_keycodes[keycode];
#else
static Display *x11display = nullptr;
if (QApplication::platformName().contains("wayland")) {
selected_keycode = x11_to_xt_2;
} else if (QApplication::platformName().contains("eglfs")) {
keycode -= 8;
if (keycode <= 88)
finalkeycode = keycode;
else
finalkeycode = evdev_to_xt[keycode];
} else if (!x11display) {
x11display = XOpenDisplay(nullptr);
if (XKeysymToKeycode(x11display, XK_Home) == 110) {
selected_keycode = x11_to_xt_2;
} else if (XKeysymToKeycode(x11display, XK_Home) == 69) {
selected_keycode = x11_to_xt_vnc;
}
}
if (!QApplication::platformName().contains("eglfs"))
finalkeycode = selected_keycode[keycode];
# ifdef XKBCOMMON
if (xkbcommon_keymap)
keycode = xkbcommon_translate(keycode);
else
# endif
# ifdef EVDEV_KEYBOARD_HPP
keycode = evdev_translate(keycode - 8);
# else
keycode = 0;
# endif
#endif
if (rctrl_is_lalt && finalkeycode == 0x11D) {
finalkeycode = 0x38;
/* Apply special cases. */
switch (keycode) {
case 0x54: /* Alt + Print Screen (special case, i.e. evdev SELECTIVE_SCREENSHOT) */
/* Send Alt as well. */
if (down) {
keyboard_input(down, 0x38);
} else {
keyboard_input(down, keycode);
keycode = 0x38;
}
break;
case 0x10b: /* Microsoft scroll up normal */
case 0x18b: /* Microsoft scroll down normal */
/* This abuses make/break codes. Send them manually, only on press. */
if (down) {
keyboard_send(0xe0);
keyboard_send(keycode & 0xff);
}
return;
case 0x11d: /* Right Ctrl */
if (rctrl_is_lalt)
keycode = 0x38; /* map to Left Alt */
break;
case 0x137: /* Print Screen */
if (keyboard_recv(0x38) || keyboard_recv(0x138)) { /* Alt+ */
keycode = 0x54;
} else if (down) {
keyboard_input(down, 0x12a);
} else {
keyboard_input(down, keycode);
keycode = 0x12a;
}
break;
case 0x145: /* Pause */
if (keyboard_recv(0x1d) || keyboard_recv(0x11d)) { /* Ctrl+ */
keycode = 0x146;
} else {
keyboard_input(down, 0xe11d);
keycode &= 0x00ff;
}
break;
}
return finalkeycode;
keyboard_input(down, keycode);
}
#ifdef Q_OS_MACOS
@@ -1543,6 +993,7 @@ static std::unordered_map<uint32_t, uint16_t> mac_modifiers_to_xt = {
{ NX_DEVICE_ALPHASHIFT_STATELESS_MASK, 0x3A },
{ NX_DEVICERCTLKEYMASK, 0x11D},
};
static bool mac_iso_swap = false;
void
MainWindow::processMacKeyboardInput(bool down, const QKeyEvent *event)
@@ -1583,11 +1034,62 @@ MainWindow::processMacKeyboardInput(bool down, const QKeyEvent *event)
// It's possible that other lock keys get delivered in this way, but
// standard Apple keyboards don't have them, so this is untested.
if (event->key() == Qt::Key_CapsLock) {
keyboard_input(1, 0x3A);
keyboard_input(0, 0x3A);
keyboard_input(1, 0x3a);
keyboard_input(0, 0x3a);
}
} else {
keyboard_input(down, x11_keycode_to_keysym(event->nativeVirtualKey()));
/* Apple ISO keyboards are notorious for swapping ISO_Section and ANSI_Grave
on *some* layouts and/or models. While macOS can sort this mess out at
keymap level, it still provides applications with unfiltered, ambiguous
keycodes, so we have to disambiguate them by making some bold assumptions
about the user's keyboard layout based on the OS-provided key mappings. */
auto nvk = event->nativeVirtualKey();
if ((nvk == 0x0a) || (nvk == 0x32)) {
/* Flaws:
- Layouts with `~ on ISO_Section are partially detected due to a conflict with ANSI
- Czech and Slovak are not detected as they have <> ANSI_Grave and \| ISO_Section (differing from PC actually)
- Italian is partially detected due to \| conflicting with Brazilian
- Romanian third level ANSI_Grave is unknown
- Russian clusters <>, plusminus and paragraph into a four-level ANSI_Grave, with the aforementioned `~ on ISO_Section */
auto key = event->key();
if ((nvk == 0x32) && ( /* system reports ANSI_Grave for ISO_Section keys: */
(key == Qt::Key_Less) || (key == Qt::Key_Greater) || /* Croatian, French, German, Icelandic, Italian, Norwegian, Portuguese, Spanish, Spanish Latin America, Turkish Q */
(key == Qt::Key_Ugrave) || /* French Canadian */
(key == Qt::Key_Icircumflex) || /* Romanian */
(key == Qt::Key_Iacute) || /* Hungarian */
(key == Qt::Key_BracketLeft) || (key == Qt::Key_BracketRight) || /* Russian upper two levels */
(key == Qt::Key_W) /* Turkish F */
))
mac_iso_swap = true;
else if ((nvk == 0x0a) && ( /* system reports ISO_Section for ANSI_Grave keys: */
(key == Qt::Key_paragraph) || (key == Qt::Key_plusminus) || /* Arabic, British, Bulgarian, Danish shifted, Dutch, Greek, Hebrew, Hungarian shifted, International English, Norwegian shifted, Portuguese, Russian lower two levels, Swiss unshifted, Swedish unshifted, Turkish F */
(key == Qt::Key_At) || (key == Qt::Key_NumberSign) || /* Belgian, French */
(key == Qt::Key_Apostrophe) || /* Brazilian unshifted */
(key == Qt::Key_QuoteDbl) || /* Brazilian shifted, Turkish Q unshifted */
(key == Qt::Key_QuoteLeft) || /* Croatian (right quote unknown) */
(key == Qt::Key_Dollar) || /* Danish unshifted */
(key == Qt::Key_AsciiCircum) || (key == 0x1ffffff) || /* German unshifted (0x1ffffff according to one tester), Polish unshifted */
(key == Qt::Key_degree) || /* German shifted, Icelandic unshifted, Spanish Latin America shifted, Swiss shifted, Swedish shifted */
(key == Qt::Key_0) || /* Hungarian unshifted */
(key == Qt::Key_diaeresis) || /* Icelandic shifted */
(key == Qt::Key_acute) || /* Norwegian unshifted */
(key == Qt::Key_Asterisk) || /* Polish shifted */
(key == Qt::Key_masculine) || (key == Qt::Key_ordfeminine) || /* Spanish (masculine unconfirmed) */
(key == Qt::Key_Eacute) || /* Turkish Q shifted */
(key == Qt::Key_Slash) /* French Canadian unshifted, Ukrainian shifted */
))
mac_iso_swap = true;
#if 0
if (down) {
QMessageBox questionbox(QMessageBox::Icon::Information, QString("Mac key swap test"), QString("nativeVirtualKey 0x%1\nnativeScanCode 0x%2\nkey 0x%3\nmac_iso_swap %4").arg(nvk, 0, 16).arg(event->nativeScanCode(), 0, 16).arg(key, 0, 16).arg(mac_iso_swap ? "yes" : "no"), QMessageBox::Ok, this);
questionbox.exec();
}
#endif
if (mac_iso_swap)
nvk = (nvk == 0x0a) ? 0x32 : 0x0a;
}
processKeyboardInput(down, nvk);
}
}
#endif
@@ -1731,33 +1233,21 @@ void
MainWindow::keyPressEvent(QKeyEvent *event)
{
if (send_keyboard_input && !(kbd_req_capture && !mouse_capture)) {
// Windows keys in Qt have one-to-one mapping.
if (event->key() == Qt::Key_Pause && !keyboard_recv(0x38) && !keyboard_recv(0x138)) {
if ((keyboard_recv(0x1D) || keyboard_recv(0x11D))) {
keyboard_input(1, 0x46);
} else {
keyboard_input(0, 0xE1);
keyboard_input(0, 0x1D);
keyboard_input(0, 0x45);
keyboard_input(0, 0xE1);
keyboard_input(1, 0x1D);
keyboard_input(1, 0x45);
}
} else
#ifdef Q_OS_MACOS
processMacKeyboardInput(true, event);
processMacKeyboardInput(true, event);
#else
keyboard_input(1, x11_keycode_to_keysym(event->nativeScanCode()));
processKeyboardInput(true, event->nativeScanCode());
#endif
}
if ((video_fullscreen > 0) && keyboard_isfsexit()) {
ui->actionFullscreen->trigger();
}
if (!fs_off_signal && (video_fullscreen > 0) && keyboard_isfsexit())
fs_off_signal = true;
if (keyboard_ismsexit()) {
if (!fs_on_signal && (video_fullscreen == 0) && keyboard_isfsenter())
fs_on_signal = true;
if (keyboard_ismsexit())
plat_mouse_capture(0);
}
if ((video_fullscreen > 0) && (keyboard_recv(0x1D) || keyboard_recv(0x11D))) {
if (keyboard_recv(0x57))
@@ -1789,13 +1279,24 @@ MainWindow::keyReleaseEvent(QKeyEvent *event)
plat_pause(dopause ^ 1);
}
}
if (fs_off_signal && (video_fullscreen > 0) && keyboard_isfsexit_down()) {
ui->actionFullscreen->trigger();
fs_off_signal = false;
}
if (fs_on_signal && (video_fullscreen == 0) && keyboard_isfsenter_down()) {
ui->actionFullscreen->trigger();
fs_on_signal = false;
}
if (!send_keyboard_input)
return;
#ifdef Q_OS_MACOS
processMacKeyboardInput(false, event);
#else
keyboard_input(0, x11_keycode_to_keysym(event->nativeScanCode()));
processKeyboardInput(false, event->nativeScanCode());
#endif
}

View File

@@ -155,6 +155,7 @@ private:
std::unique_ptr<MachineStatus> status;
std::shared_ptr<MediaMenu> mm;
void processKeyboardInput(bool down, uint32_t keycode);
#ifdef Q_OS_MACOS
uint32_t last_modifiers = 0;
void processMacKeyboardInput(bool down, const QKeyEvent *event);
@@ -166,6 +167,10 @@ private:
bool resizableonce = false;
bool vnc_enabled = false;
/* Full screen ON and OFF signals */
bool fs_on_signal = false;
bool fs_off_signal = false;
friend class SpecifyDimensions;
friend class ProgSettings;
friend class RendererCommon;

View File

@@ -362,12 +362,6 @@
<property name="text">
<string>&amp;Fullscreen</string>
</property>
<property name="shortcut">
<string>Ctrl+Alt+PgUp</string>
</property>
<property name="shortcutVisibleInContextMenu">
<bool>false</bool>
</property>
</action>
<action name="actionSoftware_Renderer">
<property name="checkable">

Some files were not shown because too many files have changed in this diff Show More