mirror of
https://github.com/86Box/86Box.git
synced 2026-02-23 01:48:21 -07:00
Merge remote-tracking branch 'upstream/master' into feature/ich2
This commit is contained in:
@@ -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
15
.ci/Jenkinsfile
vendored
@@ -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
11
.ci/build.sh
@@ -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.
|
||||
|
||||
@@ -11,3 +11,5 @@ vulkan-headers
|
||||
MoltenVK
|
||||
qt5
|
||||
wget
|
||||
fluidsynth
|
||||
ghostscript
|
||||
|
||||
3
.github/workflows/cmake.yml
vendored
3
.github/workflows/cmake.yml
vendored
@@ -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
2
.gitignore
vendored
@@ -34,6 +34,8 @@ Makefile
|
||||
*.tar.*
|
||||
*.AppImage
|
||||
/appimage-builder-cache
|
||||
/appimage-build
|
||||
/AppImageBuilder-generated.yml
|
||||
|
||||
# Visual Studio Code
|
||||
/.vs
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -302,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:
|
||||
@@ -426,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,6 +478,9 @@ ali6117_init(const device_t *info)
|
||||
}
|
||||
}
|
||||
|
||||
if (!(dev->local & 0x08))
|
||||
pic_kbd_latch(0x01);
|
||||
|
||||
ali6117_reset(dev);
|
||||
|
||||
if (!(dev->local & 0x08))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -496,10 +496,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*/
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
@@ -45,6 +47,8 @@ int mouse_x,
|
||||
double mouse_x_abs,
|
||||
mouse_y_abs;
|
||||
|
||||
pc_timer_t mouse_timer; /* mouse event timer */
|
||||
|
||||
static const device_t mouse_none_device = {
|
||||
.name = "None",
|
||||
.internal_name = "none",
|
||||
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* Copyright 2016-2020 Miran Grca.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
429
src/disk/minivhd/internal.h
Normal 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*/
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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*/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
232
src/disk/minivhd/struct_rw.c
Normal file
232
src/disk/minivhd/struct_rw.c
Normal 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);
|
||||
}
|
||||
68
src/disk/minivhd/version.h
Normal file
68
src/disk/minivhd/version.h
Normal 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*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
62
src/disk/minivhd/xml2_encoding.h
Normal file
62
src/disk/minivhd/xml2_encoding.h
Normal 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*/
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
@@ -158,7 +207,7 @@ 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,6 +227,7 @@ 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;
|
||||
extern const device_t keyboard_ps2_tg_ami_pci_device;
|
||||
#endif /*EMU_DEVICE_H*/
|
||||
|
||||
extern void keyboard_init(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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
25
src/io.c
25
src/io.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3775,7 +3775,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,
|
||||
@@ -3814,7 +3814,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,
|
||||
@@ -3895,7 +3895,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,
|
||||
@@ -7037,7 +7037,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,
|
||||
|
||||
@@ -2313,6 +2313,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
|
||||
@@ -2530,11 +2580,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();
|
||||
@@ -2577,7 +2628,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));
|
||||
|
||||
@@ -2615,7 +2667,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) {
|
||||
@@ -2821,6 +2873,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;
|
||||
|
||||
76
src/pic.c
76
src/pic.c
@@ -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)
|
||||
{
|
||||
@@ -519,10 +543,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);
|
||||
}
|
||||
@@ -530,7 +591,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);
|
||||
@@ -587,6 +648,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);
|
||||
}
|
||||
|
||||
@@ -594,6 +659,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 {
|
||||
@@ -601,11 +669,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ()
|
||||
@@ -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
112
src/qt/be_keyboard.hpp
Normal 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
129
src/qt/cocoa_keyboard.hpp
Normal 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
163
src/qt/evdev_keyboard.cpp
Normal 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
20
src/qt/evdev_keyboard.hpp
Normal 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
|
||||
@@ -710,7 +710,7 @@ msgid "Floppy & CD-ROM drives"
|
||||
msgstr "軟碟/光碟機"
|
||||
|
||||
msgid "Other removable devices"
|
||||
msgstr "其他可移除裝置"
|
||||
msgstr "其他卸除式裝置"
|
||||
|
||||
msgid "Other peripherals"
|
||||
msgstr "其他周邊裝置"
|
||||
@@ -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"
|
||||
@@ -1037,7 +1037,7 @@ 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 "磁碟映像太大"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -882,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
|
||||
@@ -1545,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)
|
||||
@@ -1585,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
|
||||
@@ -1733,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))
|
||||
@@ -1791,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -362,12 +362,6 @@
|
||||
<property name="text">
|
||||
<string>&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">
|
||||
|
||||
@@ -572,7 +572,8 @@ MediaMenu::cdromUpdateMenu(int i)
|
||||
auto childs = menu->children();
|
||||
|
||||
auto *muteMenu = dynamic_cast<QAction *>(childs[cdromMutePos]);
|
||||
muteMenu->setChecked(cdrom[i].sound_on == 0);
|
||||
muteMenu->setIcon(QApplication::style()->standardIcon((cdrom[i].sound_on == 0) ? QStyle::SP_MediaVolume : QStyle::SP_MediaVolumeMuted));
|
||||
muteMenu->setText((cdrom[i].sound_on == 0) ? tr("&Unmute") : tr("&Mute"));
|
||||
|
||||
auto *imageMenu = dynamic_cast<QAction *>(childs[cdromImagePos]);
|
||||
imageMenu->setEnabled(!name.isEmpty());
|
||||
|
||||
@@ -195,7 +195,7 @@ int ignoreNextMouseEvent = 1;
|
||||
void
|
||||
RendererStack::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->geometry().contains(event->pos()) && event->button() == Qt::LeftButton && !mouse_capture && (isMouseDown & 1) && (mouse_get_buttons() != 0) && mouse_mode == 0) {
|
||||
if (this->geometry().contains(event->pos()) && (event->button() == Qt::LeftButton) && !mouse_capture && (isMouseDown & 1) && (kbd_req_capture || (mouse_get_buttons() != 0)) && (mouse_mode == 0)) {
|
||||
plat_mouse_capture(1);
|
||||
this->setCursor(Qt::BlankCursor);
|
||||
if (!ignoreNextMouseEvent)
|
||||
|
||||
@@ -198,13 +198,13 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
|
||||
We use scan code 0xFFFF to mean a mapping that
|
||||
has a prefix other than E0 and that is not E1 1D,
|
||||
which is, for our purposes, invalid. */
|
||||
if ((scancode == 0x00F) && !(rawKB.Flags & RI_KEY_BREAK) && (recv_lalt || recv_ralt) && !mouse_capture) {
|
||||
if ((scancode == 0x00f) && !(rawKB.Flags & RI_KEY_BREAK) && (recv_lalt || recv_ralt) && (!kbd_req_capture || mouse_capture)) {
|
||||
/* We received a TAB while ALT was pressed, while the mouse
|
||||
is not captured, suppress the TAB and send an ALT key up. */
|
||||
is not captured, suppress the TAB and send an ALT key up. */
|
||||
if (recv_lalt) {
|
||||
keyboard_input(0, 0x038);
|
||||
/* Extra key press and release so the guest is not stuck in the
|
||||
menu bar. */
|
||||
menu bar. */
|
||||
keyboard_input(1, 0x038);
|
||||
keyboard_input(0, 0x038);
|
||||
recv_lalt = 0;
|
||||
@@ -212,19 +212,19 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
|
||||
if (recv_ralt) {
|
||||
keyboard_input(0, 0x138);
|
||||
/* Extra key press and release so the guest is not stuck in the
|
||||
menu bar. */
|
||||
menu bar. */
|
||||
keyboard_input(1, 0x138);
|
||||
keyboard_input(0, 0x138);
|
||||
recv_ralt = 0;
|
||||
}
|
||||
} else if (((scancode == 0x038) || (scancode == 0x138)) && !(rawKB.Flags & RI_KEY_BREAK) && recv_tab && !mouse_capture) {
|
||||
} else if (((scancode == 0x038) || (scancode == 0x138)) && !(rawKB.Flags & RI_KEY_BREAK) && recv_tab && (!kbd_req_capture || mouse_capture)) {
|
||||
/* We received an ALT while TAB was pressed, while the mouse
|
||||
is not captured, suppress the ALT and send a TAB key up. */
|
||||
keyboard_input(0, 0x00F);
|
||||
is not captured, suppress the ALT and send a TAB key up. */
|
||||
keyboard_input(0, 0x00f);
|
||||
recv_tab = 0;
|
||||
} else {
|
||||
switch (scancode) {
|
||||
case 0x00F:
|
||||
case 0x00f:
|
||||
recv_tab = !(rawKB.Flags & RI_KEY_BREAK);
|
||||
break;
|
||||
case 0x038:
|
||||
@@ -237,7 +237,7 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
|
||||
|
||||
/* Translate right CTRL to left ALT if the user has so
|
||||
chosen. */
|
||||
if ((scancode == 0x11D) && rctrl_is_lalt)
|
||||
if ((scancode == 0x11d) && rctrl_is_lalt)
|
||||
scancode = 0x038;
|
||||
|
||||
/* Normal scan code pass through, pass it through as is if
|
||||
|
||||
234
src/qt/xkbcommon_keyboard.cpp
Normal file
234
src/qt/xkbcommon_keyboard.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* xkbcommon keyboard input module.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
extern "C" {
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
};
|
||||
|
||||
#include <unordered_map>
|
||||
#include <QtDebug>
|
||||
#include "evdev_keyboard.hpp"
|
||||
|
||||
#define IS_DEC_DIGIT(c) (((c) >= '0') && ((c) <= '9'))
|
||||
#define IS_HEX_DIGIT(c) (IS_DEC_DIGIT(c) || (((c) >= 'A') && ((c) <= 'F')) || (((c) >= 'a') && ((c) <= 'f')))
|
||||
|
||||
static std::unordered_map<std::string, uint16_t> xkb_keycodes = {
|
||||
{"ESC", 0x01},
|
||||
{"AE01", 0x02},
|
||||
{"AE02", 0x03},
|
||||
{"AE03", 0x04},
|
||||
{"AE04", 0x05},
|
||||
{"AE05", 0x06},
|
||||
{"AE06", 0x07},
|
||||
{"AE07", 0x08},
|
||||
{"AE08", 0x09},
|
||||
{"AE09", 0x0a},
|
||||
{"AE10", 0x0b},
|
||||
{"AE11", 0x0c},
|
||||
{"AE12", 0x0d},
|
||||
{"BKSP", 0x0e},
|
||||
|
||||
{"TAB", 0x0f},
|
||||
{"AD01", 0x10},
|
||||
{"AD02", 0x11},
|
||||
{"AD03", 0x12},
|
||||
{"AD04", 0x13},
|
||||
{"AD05", 0x14},
|
||||
{"AD06", 0x15},
|
||||
{"AD07", 0x16},
|
||||
{"AD08", 0x17},
|
||||
{"AD09", 0x18},
|
||||
{"AD10", 0x19},
|
||||
{"AD11", 0x1a},
|
||||
{"AD12", 0x1b},
|
||||
{"RTRN", 0x1c},
|
||||
{"LNFD", 0x1c}, /* linefeed => Enter */
|
||||
|
||||
{"LCTL", 0x1d},
|
||||
{"AC01", 0x1e},
|
||||
{"AC02", 0x1f},
|
||||
{"AC03", 0x20},
|
||||
{"AC04", 0x21},
|
||||
{"AC05", 0x22},
|
||||
{"AC06", 0x23},
|
||||
{"AC07", 0x24},
|
||||
{"AC08", 0x25},
|
||||
{"AC09", 0x26},
|
||||
{"AC10", 0x27},
|
||||
{"AC11", 0x28},
|
||||
|
||||
{"TLDE", 0x29},
|
||||
{"LFSH", 0x2a},
|
||||
{"BKSL", 0x2b},
|
||||
{"AB01", 0x2c},
|
||||
{"AB02", 0x2d},
|
||||
{"AB03", 0x2e},
|
||||
{"AB04", 0x2f},
|
||||
{"AB05", 0x30},
|
||||
{"AB06", 0x31},
|
||||
{"AB07", 0x32},
|
||||
{"AB08", 0x33},
|
||||
{"AB09", 0x34},
|
||||
{"AB10", 0x35},
|
||||
{"RTSH", 0x36},
|
||||
|
||||
{"KPMU", 0x37},
|
||||
{"LALT", 0x38},
|
||||
{"SPCE", 0x39},
|
||||
{"CAPS", 0x3a},
|
||||
{"FK01", 0x3b},
|
||||
{"FK02", 0x3c},
|
||||
{"FK03", 0x3d},
|
||||
{"FK04", 0x3e},
|
||||
{"FK05", 0x3f},
|
||||
{"FK06", 0x40},
|
||||
{"FK07", 0x41},
|
||||
{"FK08", 0x42},
|
||||
{"FK09", 0x43},
|
||||
{"FK10", 0x44},
|
||||
|
||||
{"NMLK", 0x45},
|
||||
{"SCLK", 0x46},
|
||||
{"FK14", 0x46}, /* F14 => Scroll Lock (for Apple keyboards) */
|
||||
{"KP7", 0x47},
|
||||
{"KP8", 0x48},
|
||||
{"KP9", 0x49},
|
||||
{"KPSU", 0x4a},
|
||||
{"KP4", 0x4b},
|
||||
{"KP5", 0x4c},
|
||||
{"KP6", 0x4d},
|
||||
{"KPAD", 0x4e},
|
||||
{"KP1", 0x4f},
|
||||
{"KP2", 0x50},
|
||||
{"KP3", 0x51},
|
||||
{"KP0", 0x52},
|
||||
{"KPDL", 0x53},
|
||||
|
||||
{"LSGT", 0x56},
|
||||
{"FK11", 0x57},
|
||||
{"FK12", 0x58},
|
||||
{"FK16", 0x5d}, /* F16 => F13 */
|
||||
{"FK17", 0x5e}, /* F17 => F14 */
|
||||
{"FK18", 0x5f}, /* F18 => F15 */
|
||||
|
||||
/* Japanese keys. */
|
||||
{"JPCM", 0x5c}, /* Num, */
|
||||
{"KPDC", 0x5c},
|
||||
{"HKTG", 0x70}, /* hiragana-katakana toggle */
|
||||
{"AB11", 0x73}, /* \_ and Brazilian /? */
|
||||
{"HZTG", 0x76}, /* hankaku-zenkaku toggle */
|
||||
{"HIRA", 0x77},
|
||||
{"KATA", 0x78},
|
||||
{"HENK", 0x79},
|
||||
{"KANA", 0x79}, /* kana => henkan (for Apple keyboards) */
|
||||
{"MUHE", 0x7b},
|
||||
{"EISU", 0x7b}, /* eisu => muhenkan (for Apple keyboards) */
|
||||
{"AE13", 0x7d}, /* \| */
|
||||
{"KPPT", 0x7e}, /* Brazilian Num. */
|
||||
{"I06", 0x7e}, /* alias of KPPT on keycodes/xfree86 (i.e. X11 forwarding) */
|
||||
|
||||
/* Korean keys. */
|
||||
{"HJCV", 0xf1}, /* hancha toggle */
|
||||
{"HNGL", 0xf2}, /* latin toggle */
|
||||
|
||||
{"KPEN", 0x11c},
|
||||
{"RCTL", 0x11d},
|
||||
{"KPDV", 0x135},
|
||||
{"PRSC", 0x137},
|
||||
{"SYRQ", 0x137},
|
||||
{"FK13", 0x137}, /* F13 => SysRq (for Apple keyboards) */
|
||||
{"RALT", 0x138},
|
||||
{"ALGR", 0x138},
|
||||
{"LVL3", 0x138}, /* observed on TigerVNC with AltGr-enabled layout */
|
||||
{"PAUS", 0x145},
|
||||
{"FK15", 0x145}, /* F15 => Pause (for Apple keyboards) */
|
||||
{"BRK", 0x145},
|
||||
{"HOME", 0x147},
|
||||
{"UP", 0x148},
|
||||
{"PGUP", 0x149},
|
||||
{"LEFT", 0x14b},
|
||||
{"RGHT", 0x14d},
|
||||
{"END", 0x14f},
|
||||
{"DOWN", 0x150},
|
||||
{"PGDN", 0x151},
|
||||
{"INS", 0x152},
|
||||
{"DELE", 0x153},
|
||||
|
||||
{"LWIN", 0x15b},
|
||||
{"LMTA", 0x15b},
|
||||
{"RWIN", 0x15c},
|
||||
{"RMTA", 0x15c},
|
||||
{"MENU", 0x15d},
|
||||
{"COMP", 0x15d}, /* Compose as Menu */
|
||||
|
||||
/* Multimedia keys. Same notes as evdev_keyboard apply here. */
|
||||
{"KPEQ", 0x59}, /* Num= */
|
||||
{"FRNT", 0x101}, /* # Logitech Task Select */
|
||||
{"UNDO", 0x108}, /* # */
|
||||
{"PAST", 0x10a}, /* # Paste */
|
||||
{"FIND", 0x112}, /* # Logitech */
|
||||
{"CUT", 0x117}, /* # */
|
||||
{"COPY", 0x118}, /* # */
|
||||
{"MUTE", 0x120},
|
||||
{"VOL-", 0x12e},
|
||||
{"VOL+", 0x130},
|
||||
{"HELP", 0x13b},
|
||||
{"OPEN", 0x13f},
|
||||
{"POWR", 0x15e},
|
||||
{"STOP", 0x168},
|
||||
};
|
||||
struct xkb_keymap *xkbcommon_keymap = nullptr;
|
||||
|
||||
void
|
||||
xkbcommon_init(struct xkb_keymap *keymap)
|
||||
{
|
||||
if (keymap)
|
||||
xkbcommon_keymap = keymap;
|
||||
}
|
||||
|
||||
void
|
||||
xkbcommon_close()
|
||||
{
|
||||
xkbcommon_keymap = nullptr;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
xkbcommon_translate(uint32_t keycode)
|
||||
{
|
||||
const char *key_name = xkb_keymap_key_get_name(xkbcommon_keymap, keycode);
|
||||
|
||||
/* If XKB doesn't know the key name for this keycode, assume an unnamed Ixxx key.
|
||||
This is useful for older XKB versions with an incomplete evdev keycode map. */
|
||||
auto key_name_s = key_name ? std::string(key_name) : QString("I%1").arg(keycode).toStdString();
|
||||
auto ret = xkb_keycodes[key_name_s];
|
||||
|
||||
/* Observed with multimedia keys on Windows VcXsrv. */
|
||||
if (!ret && (key_name_s.length() == 3) && (key_name_s[0] == 'I') && IS_HEX_DIGIT(key_name_s[1]) && IS_HEX_DIGIT(key_name_s[2]))
|
||||
ret = 0x100 | stoi(key_name_s.substr(1), nullptr, 16);
|
||||
|
||||
/* Translate unnamed evdev-specific keycodes. */
|
||||
if (!ret && (key_name_s.length() >= 2) && (key_name_s[0] == 'I') && IS_DEC_DIGIT(key_name_s[1]))
|
||||
ret = evdev_translate(stoi(key_name_s.substr(1)) - 8);
|
||||
|
||||
if (!ret)
|
||||
qWarning() << "XKB Keyboard: Unknown key" << QString::number(keycode, 16) << QString::fromStdString(key_name_s);
|
||||
#if 0
|
||||
else
|
||||
qInfo() << "XKB Keyboard: Key" << QString::number(keycode, 16) << QString::fromStdString(key_name_s) << "scancode" << QString::number(ret, 16);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
20
src/qt/xkbcommon_keyboard.hpp
Normal file
20
src/qt/xkbcommon_keyboard.hpp
Normal 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 xkbcommon keyboard input module.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
extern void *xkbcommon_keymap;
|
||||
void xkbcommon_init(struct xkb_keymap *keymap);
|
||||
void xkbcommon_close();
|
||||
uint16_t xkbcommon_translate(uint32_t keycode);
|
||||
241
src/qt/xkbcommon_wl_keyboard.cpp
Normal file
241
src/qt/xkbcommon_wl_keyboard.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* xkbcommon Wayland keyboard input module.
|
||||
*
|
||||
* Heavily inspired by libxkbcommon interactive-wayland.c
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
extern "C" {
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <86box/86box.h>
|
||||
};
|
||||
#include "xkbcommon_keyboard.hpp"
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <QtDebug>
|
||||
#include <QGuiApplication>
|
||||
|
||||
typedef struct {
|
||||
struct wl_seat *wl_seat;
|
||||
struct wl_keyboard *wl_kbd;
|
||||
uint32_t version;
|
||||
|
||||
struct xkb_keymap *keymap;
|
||||
|
||||
struct wl_list link;
|
||||
} seat_t;
|
||||
|
||||
static bool wl_init_ok = false;
|
||||
static struct wl_list seats;
|
||||
static struct xkb_context *ctx;
|
||||
|
||||
static void
|
||||
xkbcommon_wl_set_keymap()
|
||||
{
|
||||
/* Grab keymap from the first seat with one. */
|
||||
seat_t *seat, *tmp;
|
||||
wl_list_for_each_safe(seat, tmp, &seats, link) {
|
||||
if (seat->keymap) {
|
||||
xkbcommon_init(seat->keymap);
|
||||
return;
|
||||
}
|
||||
}
|
||||
xkbcommon_close(); /* none found */
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format,
|
||||
int fd, uint32_t size)
|
||||
{
|
||||
seat_t *seat = (seat_t *) data;
|
||||
|
||||
char *buf = (char *) mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (!buf) {
|
||||
qWarning() << "XKB Keyboard: Failed to mmap keymap with error" << errno;
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat->keymap) {
|
||||
struct xkb_keymap *keymap = seat->keymap;
|
||||
seat->keymap = NULL;
|
||||
xkbcommon_wl_set_keymap();
|
||||
xkb_keymap_unref(keymap);
|
||||
}
|
||||
|
||||
seat->keymap = xkb_keymap_new_from_buffer(ctx, buf, size - 1,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
munmap(buf, size);
|
||||
close(fd);
|
||||
if (!seat->keymap)
|
||||
qWarning() << "XKB Keyboard: Keymap compilation failed";
|
||||
|
||||
xkbcommon_wl_set_keymap();
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
|
||||
struct wl_surface *surf, struct wl_array *keys)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
|
||||
struct wl_surface *surf)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
|
||||
uint32_t key, uint32_t state)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
|
||||
int32_t delay)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener kbd_listener = {
|
||||
kbd_keymap,
|
||||
kbd_enter,
|
||||
kbd_leave,
|
||||
kbd_key,
|
||||
kbd_modifiers,
|
||||
kbd_repeat_info
|
||||
};
|
||||
|
||||
static void
|
||||
seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
|
||||
{
|
||||
seat_t *seat = (seat_t *) data;
|
||||
|
||||
if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
|
||||
wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
|
||||
} else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
|
||||
wl_keyboard_release(seat->wl_kbd);
|
||||
else
|
||||
wl_keyboard_destroy(seat->wl_kbd);
|
||||
|
||||
struct xkb_keymap *keymap = seat->keymap;
|
||||
seat->keymap = NULL;
|
||||
xkbcommon_wl_set_keymap();
|
||||
xkb_keymap_unref(keymap);
|
||||
|
||||
seat->wl_kbd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_name(void *data, struct wl_seat *wl_seat, const char *name)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
seat_capabilities,
|
||||
seat_name
|
||||
};
|
||||
|
||||
static void
|
||||
display_handle_global(void *data, struct wl_registry *wl_registry, uint32_t id,
|
||||
const char *interface, uint32_t version)
|
||||
{
|
||||
if (!strcmp(interface, "wl_seat")) {
|
||||
seat_t *seat = (seat_t *) malloc(sizeof(seat_t));
|
||||
memset(seat, 0, sizeof(seat_t));
|
||||
|
||||
seat->wl_seat = (wl_seat *) wl_registry_bind(wl_registry, id, &wl_seat_interface, MIN(version, 5));
|
||||
wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
|
||||
wl_list_insert(&seats, &seat->link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
display_global_remove(void *data, struct wl_registry *wl_registry, uint32_t id)
|
||||
{
|
||||
xkbcommon_close();
|
||||
|
||||
seat_t *seat, *tmp;
|
||||
wl_list_for_each_safe(seat, tmp, &seats, link) {
|
||||
if (seat->wl_kbd) {
|
||||
if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
|
||||
wl_keyboard_release(seat->wl_kbd);
|
||||
else
|
||||
wl_keyboard_destroy(seat->wl_kbd);
|
||||
|
||||
xkb_keymap_unref(seat->keymap);
|
||||
}
|
||||
|
||||
if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
|
||||
wl_seat_release(seat->wl_seat);
|
||||
else
|
||||
wl_seat_destroy(seat->wl_seat);
|
||||
|
||||
wl_list_remove(&seat->link);
|
||||
free(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
display_handle_global,
|
||||
display_global_remove
|
||||
};
|
||||
|
||||
void
|
||||
xkbcommon_wl_init()
|
||||
{
|
||||
if (wl_init_ok)
|
||||
return;
|
||||
|
||||
wl_list_init(&seats);
|
||||
|
||||
ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!ctx) {
|
||||
qWarning() << "XKB Keyboard: XKB context creation failed";
|
||||
return;
|
||||
}
|
||||
|
||||
wl_display *display = (wl_display *) QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_display");
|
||||
if (display) {
|
||||
auto registry = wl_display_get_registry(display);
|
||||
if (registry) {
|
||||
wl_registry_add_listener(registry, ®istry_listener, nullptr);
|
||||
wl_display_roundtrip(display);
|
||||
wl_display_roundtrip(display);
|
||||
} else {
|
||||
goto err_ctx;
|
||||
}
|
||||
} else {
|
||||
goto err_ctx;
|
||||
}
|
||||
wl_init_ok = true;
|
||||
return;
|
||||
|
||||
err_ctx:
|
||||
xkb_context_unref(ctx);
|
||||
}
|
||||
17
src/qt/xkbcommon_wl_keyboard.hpp
Normal file
17
src/qt/xkbcommon_wl_keyboard.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 xkbcommon Wayland keyboard input module.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
void xkbcommon_wl_init();
|
||||
86
src/qt/xkbcommon_x11_keyboard.cpp
Normal file
86
src/qt/xkbcommon_x11_keyboard.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* xkbcommon-x11 keyboard input module.
|
||||
*
|
||||
* Heavily inspired by libxkbcommon interactive-x11.c
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
extern "C" {
|
||||
/* xkb.h has identifiers named "explicit", which is a C++ keyword now... */
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wkeyword-macro"
|
||||
#endif
|
||||
#define explicit explicit_
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#include <xcb/xkb.h>
|
||||
#undef explicit
|
||||
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
};
|
||||
#include "xkbcommon_keyboard.hpp"
|
||||
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <QtDebug>
|
||||
#include <QGuiApplication>
|
||||
|
||||
void
|
||||
xkbcommon_x11_init()
|
||||
{
|
||||
xcb_connection_t *conn;
|
||||
struct xkb_context *ctx;
|
||||
int32_t core_kbd_device_id;
|
||||
struct xkb_keymap *keymap;
|
||||
|
||||
conn = (xcb_connection_t *) QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("connection");
|
||||
if (!conn) {
|
||||
qWarning() << "XKB Keyboard: X server connection failed";
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = xkb_x11_setup_xkb_extension(conn,
|
||||
XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION,
|
||||
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (!ret) {
|
||||
qWarning() << "XKB Keyboard: XKB extension setup failed";
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!ctx) {
|
||||
qWarning() << "XKB Keyboard: XKB context creation failed";
|
||||
return;
|
||||
}
|
||||
|
||||
core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn);
|
||||
if (core_kbd_device_id == -1) {
|
||||
qWarning() << "XKB Keyboard: Core keyboard device not found";
|
||||
goto err_ctx;
|
||||
}
|
||||
|
||||
keymap = xkb_x11_keymap_new_from_device(ctx, conn, core_kbd_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
qWarning() << "XKB Keyboard: Keymap loading failed";
|
||||
goto err_ctx;
|
||||
}
|
||||
|
||||
xkbcommon_init(keymap);
|
||||
return;
|
||||
|
||||
err_ctx:
|
||||
xkb_context_unref(ctx);
|
||||
}
|
||||
17
src/qt/xkbcommon_x11_keyboard.hpp
Normal file
17
src/qt/xkbcommon_x11_keyboard.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 xkbcommon-x11 keyboard input module.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
void xkbcommon_x11_init();
|
||||
@@ -675,7 +675,7 @@ x54x_rd_sge(x54x_t *dev, int Is24bit, uint32_t Address, SGE32 *SG)
|
||||
memcpy((uint8_t *) &SGE24, bytes, sizeof(SGE));
|
||||
} else {
|
||||
/* 16-bit device, special handling not needed. */
|
||||
dma_bm_read(Address, (uint8_t *) &SGE24, 8, dev->transfer_size);
|
||||
dma_bm_read(Address, (uint8_t *) &SGE24, 6, dev->transfer_size);
|
||||
}
|
||||
x54x_add_to_period(dev, sizeof(SGE));
|
||||
|
||||
|
||||
@@ -85,6 +85,12 @@ if(RTMIDI)
|
||||
endif()
|
||||
|
||||
if(FLUIDSYNTH)
|
||||
if(APPLE)
|
||||
find_library(FLUIDSYNTH_LIB fluidsynth)
|
||||
if (NOT FLUIDSYNTH_LIB)
|
||||
message(WARNING "Could not find fluid synth. The library will not be bundled and any related features will not work.")
|
||||
endif()
|
||||
endif ()
|
||||
target_compile_definitions(snd PRIVATE USE_FLUIDSYNTH)
|
||||
target_sources(snd PRIVATE midi_fluidsynth.c)
|
||||
endif()
|
||||
|
||||
@@ -178,6 +178,7 @@ ac97_via_update_codec(ac97_via_t *dev)
|
||||
/* Update volumes according to codec registers. */
|
||||
ac97_codec_getattn(codec, 0x02, &dev->master_vol_l, &dev->master_vol_r);
|
||||
ac97_codec_getattn(codec, 0x18, &dev->sgd[0].vol_l, &dev->sgd[0].vol_r);
|
||||
ac97_codec_getattn(codec, 0x18, &dev->sgd[2].vol_l, &dev->sgd[2].vol_r); /* VIAFMTSR sets Master, CD and PCM volumes to 0 dB */
|
||||
ac97_codec_getattn(codec, 0x12, &dev->cd_vol_l, &dev->cd_vol_r);
|
||||
|
||||
/* Update sample rate according to codec registers and the variable sample rate flag. */
|
||||
@@ -315,13 +316,11 @@ ac97_via_sgd_write(uint16_t addr, uint8_t val, void *priv)
|
||||
dev->sgd_regs[addr & 0xf0] |= 0x08;
|
||||
} else {
|
||||
/* Start SGD immediately. */
|
||||
dev->sgd_regs[addr & 0xf0] |= 0x80;
|
||||
dev->sgd_regs[addr & 0xf0] &= ~0x44;
|
||||
dev->sgd_regs[addr & 0xf0] = (dev->sgd_regs[addr & 0xf0] & ~0x47) | 0x80;
|
||||
|
||||
/* Start at the specified entry pointer. */
|
||||
dev->sgd[addr >> 4].sample_ptr = 0;
|
||||
dev->sgd[addr >> 4].entry_ptr = *((uint32_t *) &dev->sgd_regs[(addr & 0xf0) | 0x4]) & 0xfffffffe;
|
||||
dev->sgd[addr >> 4].restart = 1;
|
||||
dev->sgd[addr >> 4].restart = 2;
|
||||
|
||||
/* Start the actual SGD process. */
|
||||
ac97_via_sgd_process(&dev->sgd[addr >> 4]);
|
||||
@@ -530,15 +529,14 @@ ac97_via_sgd_process(void *priv)
|
||||
timer_on_auto(&sgd->dma_timer, 10.0);
|
||||
|
||||
/* Process SGD if it's active, and the FIFO has room or is disabled. */
|
||||
if ((sgd_status == 0x80) && (sgd->always_run || ((sgd->fifo_end - sgd->fifo_pos) <= (sizeof(sgd->fifo) - 4)))) {
|
||||
if (((sgd_status & 0xc7) == 0x80) && (sgd->always_run || ((sgd->fifo_end - sgd->fifo_pos) <= (sizeof(sgd->fifo) - 4)))) {
|
||||
/* Move on to the next block if no entry is present. */
|
||||
if (sgd->restart) {
|
||||
/* (Re)load entry pointer if required. */
|
||||
if (sgd->restart & 2)
|
||||
sgd->entry_ptr = *((uint32_t *) &dev->sgd_regs[sgd->id | 0x4]) & 0xfffffffe; /* TODO: probe real hardware - does "even addr" actually mean dword aligned? */
|
||||
sgd->restart = 0;
|
||||
|
||||
/* Start at first entry if no pointer is present. */
|
||||
if (!sgd->entry_ptr)
|
||||
sgd->entry_ptr = *((uint32_t *) &dev->sgd_regs[sgd->id | 0x4]) & 0xfffffffe;
|
||||
|
||||
/* Read entry. */
|
||||
sgd->sample_ptr = mem_readl_phys(sgd->entry_ptr);
|
||||
sgd->entry_ptr += 4;
|
||||
@@ -573,6 +571,9 @@ ac97_via_sgd_process(void *priv)
|
||||
if (sgd->sample_count <= 0) {
|
||||
ac97_via_log("AC97 VIA: Ending SGD %d block", sgd->id >> 4);
|
||||
|
||||
/* Move on to the next block on the next run, unless overridden below. */
|
||||
sgd->restart = 1;
|
||||
|
||||
if (sgd->entry_flags & 0x20) {
|
||||
ac97_via_log(" with STOP");
|
||||
|
||||
@@ -583,8 +584,8 @@ ac97_via_sgd_process(void *priv)
|
||||
if (sgd->entry_flags & 0x40) {
|
||||
ac97_via_log(" with FLAG");
|
||||
|
||||
/* Raise FLAG and STOP. */
|
||||
dev->sgd_regs[sgd->id] |= 0x05;
|
||||
/* Raise FLAG to pause SGD. */
|
||||
dev->sgd_regs[sgd->id] |= 0x01;
|
||||
|
||||
#ifdef ENABLE_AC97_VIA_LOG
|
||||
if (dev->sgd_regs[sgd->id | 0x2] & 0x01)
|
||||
@@ -610,8 +611,8 @@ ac97_via_sgd_process(void *priv)
|
||||
/* Un-queue trigger. */
|
||||
dev->sgd_regs[sgd->id] &= ~0x08;
|
||||
|
||||
/* Go back to the starting block. */
|
||||
sgd->entry_ptr = 0; /* ugly, but Windows XP plays too fast if the pointer is reloaded now */
|
||||
/* Go back to the starting block on the next run. */
|
||||
sgd->restart = 2;
|
||||
} else {
|
||||
ac97_via_log(" finish");
|
||||
|
||||
@@ -623,9 +624,6 @@ ac97_via_sgd_process(void *priv)
|
||||
|
||||
/* Fire any requested status interrupts. */
|
||||
ac97_via_update_irqs(dev);
|
||||
|
||||
/* Move on to a new block on the next run. */
|
||||
sgd->restart = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,7 +747,7 @@ ac97_via_speed_changed(void *priv)
|
||||
freq = (double) SOUND_FREQ;
|
||||
|
||||
dev->sgd[0].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / freq));
|
||||
dev->sgd[2].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / 24000.0));
|
||||
dev->sgd[2].timer_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / 24000.0)); /* FM operates at a fixed 24 KHz */
|
||||
}
|
||||
|
||||
static void *
|
||||
@@ -775,10 +773,6 @@ ac97_via_init(const device_t *info)
|
||||
if ((i != 0) && (i != 2))
|
||||
dev->sgd[i].always_run = 1;
|
||||
|
||||
/* No volume control on FM SGD that I know of. */
|
||||
if (i == 2)
|
||||
dev->sgd[i].vol_l = dev->sgd[i].vol_r = 32767;
|
||||
|
||||
timer_add(&dev->sgd[i].dma_timer, ac97_via_sgd_process, &dev->sgd[i], 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
@@ -329,7 +329,7 @@ struct ymfm_output
|
||||
// ======================> ymfm_wavfile
|
||||
|
||||
// this class is a debugging helper that accumulates data and writes it to wav files
|
||||
template<int _Channels>
|
||||
template<int Channels>
|
||||
class ymfm_wavfile
|
||||
{
|
||||
public:
|
||||
@@ -361,10 +361,10 @@ public:
|
||||
memcpy(&header[12], "fmt ", 4);
|
||||
*(uint32_t *)&header[16] = 16;
|
||||
*(uint16_t *)&header[20] = 1;
|
||||
*(uint16_t *)&header[22] = _Channels;
|
||||
*(uint16_t *)&header[22] = Channels;
|
||||
*(uint32_t *)&header[24] = m_samplerate;
|
||||
*(uint32_t *)&header[28] = m_samplerate * 2 * _Channels;
|
||||
*(uint16_t *)&header[32] = 2 * _Channels;
|
||||
*(uint32_t *)&header[28] = m_samplerate * 2 * Channels;
|
||||
*(uint16_t *)&header[32] = 2 * Channels;
|
||||
*(uint16_t *)&header[34] = 16;
|
||||
memcpy(&header[36], "data", 4);
|
||||
*(uint32_t *)&header[40] = m_buffer.size() * 2 + 44 - 44;
|
||||
@@ -377,24 +377,24 @@ public:
|
||||
}
|
||||
|
||||
// add data to the file
|
||||
template<int _Outputs>
|
||||
void add(ymfm_output<_Outputs> output)
|
||||
template<int Outputs>
|
||||
void add(ymfm_output<Outputs> output)
|
||||
{
|
||||
int16_t sum[_Channels] = { 0 };
|
||||
for (int index = 0; index < _Outputs; index++)
|
||||
sum[index % _Channels] += output.data[index];
|
||||
for (int index = 0; index < _Channels; index++)
|
||||
int16_t sum[Channels] = { 0 };
|
||||
for (int index = 0; index < Outputs; index++)
|
||||
sum[index % Channels] += output.data[index];
|
||||
for (int index = 0; index < Channels; index++)
|
||||
m_buffer.push_back(sum[index]);
|
||||
}
|
||||
|
||||
// add data to the file, using a reference
|
||||
template<int _Outputs>
|
||||
void add(ymfm_output<_Outputs> output, ymfm_output<_Outputs> const &ref)
|
||||
template<int Outputs>
|
||||
void add(ymfm_output<Outputs> output, ymfm_output<Outputs> const &ref)
|
||||
{
|
||||
int16_t sum[_Channels] = { 0 };
|
||||
for (int index = 0; index < _Outputs; index++)
|
||||
sum[index % _Channels] += output.data[index] - ref.data[index];
|
||||
for (int index = 0; index < _Channels; index++)
|
||||
int16_t sum[Channels] = { 0 };
|
||||
for (int index = 0; index < Outputs; index++)
|
||||
sum[index % Channels] += output.data[index] - ref.data[index];
|
||||
for (int index = 0; index < Channels; index++)
|
||||
m_buffer.push_back(sum[index]);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define DEBUG_LOG_WAVFILES (0)
|
||||
#define YMFM_DEBUG_LOG_WAVFILES (0)
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
@@ -401,7 +401,7 @@ public:
|
||||
// compute sample rate
|
||||
uint32_t sample_rate(uint32_t baseclock) const
|
||||
{
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
m_wavfile[chnum].set_samplerate(baseclock / (m_clock_prescale * OPERATORS));
|
||||
#endif
|
||||
@@ -453,7 +453,7 @@ protected:
|
||||
RegisterType m_regs; // register accessor
|
||||
std::unique_ptr<fm_channel<RegisterType>> m_channel[CHANNELS]; // channel pointers
|
||||
std::unique_ptr<fm_operator<RegisterType>> m_operator[OPERATORS]; // operator pointers
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1185,6 +1185,7 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
|
||||
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
|
||||
m_irq_state(0),
|
||||
m_timer_running{0,0},
|
||||
m_total_clocks(0),
|
||||
m_active_channels(ALL_CHANNELS),
|
||||
m_modified_channels(ALL_CHANNELS),
|
||||
m_prepare_count(0)
|
||||
@@ -1200,7 +1201,7 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
|
||||
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
|
||||
m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
|
||||
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
m_wavfile[chnum].set_index(chnum);
|
||||
#endif
|
||||
@@ -1332,7 +1333,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
chanmask &= debug::GLOBAL_FM_CHANNEL_MASK;
|
||||
|
||||
// mask out inactive channels
|
||||
if (!DEBUG_LOG_WAVFILES)
|
||||
if (!YMFM_DEBUG_LOG_WAVFILES)
|
||||
chanmask &= m_active_channels;
|
||||
|
||||
// handle the rhythm case, where some of the operators are dedicated
|
||||
@@ -1351,7 +1352,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
if (bitfield(chanmask, chnum))
|
||||
{
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
auto reference = output;
|
||||
#endif
|
||||
if (chnum == 6)
|
||||
@@ -1364,7 +1365,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
m_channel[chnum]->output_4op(output, rshift, clipmax);
|
||||
else
|
||||
m_channel[chnum]->output_2op(output, rshift, clipmax);
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
m_wavfile[chnum].add(output, reference);
|
||||
#endif
|
||||
}
|
||||
@@ -1375,14 +1376,14 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
if (bitfield(chanmask, chnum))
|
||||
{
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
auto reference = output;
|
||||
#endif
|
||||
if (m_channel[chnum]->is4op())
|
||||
m_channel[chnum]->output_4op(output, rshift, clipmax);
|
||||
else
|
||||
m_channel[chnum]->output_2op(output, rshift, clipmax);
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
m_wavfile[chnum].add(output, reference);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -100,6 +100,11 @@ opl_registers_base<Revision>::opl_registers_base() :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OPL3/OPL4 have dynamic operators, so initialize the fourop_enable value here
|
||||
// since operator_map() is called right away, prior to reset()
|
||||
if (Revision > 2)
|
||||
m_regdata[0x104 % REGISTERS] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1710,9 +1715,15 @@ uint8_t ymf278b::read_status()
|
||||
|
||||
uint8_t ymf278b::read_data_pcm()
|
||||
{
|
||||
// write to FM
|
||||
// read from PCM
|
||||
if (bitfield(m_address, 9) != 0)
|
||||
return m_pcm.read(m_address & 0xff);
|
||||
{
|
||||
uint8_t result = m_pcm.read(m_address & 0xff);
|
||||
if ((m_address & 0xff) == 0x02)
|
||||
result |= 0x20;
|
||||
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace ymfm
|
||||
void pcm_registers::reset()
|
||||
{
|
||||
std::fill_n(&m_regdata[0], REGISTERS, 0);
|
||||
m_regdata[0x02] = 0x20;
|
||||
m_regdata[0xf8] = 0x1b;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# After a successful build, you can install the RPMs as follows:
|
||||
# sudo dnf install RPMS/$(uname -m)/86Box-3* RPMS/noarch/86Box-roms*
|
||||
|
||||
%global romver v3.11
|
||||
%global romver 3.11
|
||||
|
||||
Name: 86Box
|
||||
Version: 4.0
|
||||
@@ -21,8 +21,8 @@ Summary: Classic PC emulator
|
||||
License: GPLv2+
|
||||
URL: https://86box.net
|
||||
|
||||
Source0: https://github.com/86Box/86Box/archive/refs/tags/v%%{version}.tar.gz
|
||||
Source1: https://github.com/86Box/roms/archive/refs/tags/%{romver}.zip
|
||||
Source0: https://github.com/86Box/86Box/archive/refs/tags/v%{version}.tar.gz
|
||||
Source1: https://github.com/86Box/roms/archive/refs/tags/v%{romver}.zip
|
||||
|
||||
BuildRequires: cmake
|
||||
BuildRequires: desktop-file-utils
|
||||
@@ -32,6 +32,7 @@ BuildRequires: gcc-c++
|
||||
BuildRequires: libFAudio-devel
|
||||
BuildRequires: libappstream-glib
|
||||
BuildRequires: libevdev-devel
|
||||
BuildRequires: libxkbcommon-x11-devel
|
||||
BuildRequires: libXi-devel
|
||||
BuildRequires: ninja-build
|
||||
BuildRequires: openal-soft-devel
|
||||
@@ -98,7 +99,7 @@ cp src/unix/assets/net.86box.86Box.metainfo.xml %{buildroot}%{_metainfodir}
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/net.86box.86Box.metainfo.xml
|
||||
|
||||
# install roms
|
||||
pushd roms-%{version}
|
||||
pushd roms-%{romver}
|
||||
mkdir -p %{buildroot}%{_datadir}/%{name}/roms
|
||||
cp -a * %{buildroot}%{_datadir}/%{name}/roms/
|
||||
popd
|
||||
|
||||
@@ -814,7 +814,7 @@ void
|
||||
plat_get_global_config_dir(char *strptr)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
char *prefPath = SDL_GetPrefPath(NULL, "net.86Box.86Box")
|
||||
char *prefPath = SDL_GetPrefPath(NULL, "net.86Box.86Box");
|
||||
#else
|
||||
char *prefPath = SDL_GetPrefPath(NULL, "86Box");
|
||||
#endif
|
||||
|
||||
@@ -3945,7 +3945,10 @@ static void
|
||||
}
|
||||
|
||||
if (info->flags & DEVICE_MCA) {
|
||||
vram = 1024;
|
||||
if (id == CIRRUS_ID_CLGD5428)
|
||||
vram = 1024;
|
||||
else
|
||||
vram = device_get_config_int("memory");
|
||||
gd54xx->vram_size = vram << 10;
|
||||
} else {
|
||||
if (id <= CIRRUS_ID_CLGD5428) {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <86box/86box.h>
|
||||
#include <86box/i2c.h>
|
||||
|
||||
#define PIXEL_MM(px) ((uint16_t) (((px) *25.4) / 96))
|
||||
#define PIXEL_MM(px) (((px) * 25.4) / 96.0)
|
||||
#define STANDARD_TIMING(slot, width, aspect_ratio, refresh) \
|
||||
do { \
|
||||
edid->slot.horiz_pixels = ((width) >> 3) - 31; \
|
||||
@@ -44,9 +44,9 @@
|
||||
edid->slot.h_sync_pulse_lsb = (hsp) &0xff; \
|
||||
edid->slot.v_front_porch_sync_pulse_lsb = (((vfp) &0x0f) << 4) | ((vsp) &0x0f); \
|
||||
edid->slot.hv_front_porch_sync_pulse_msb = (((hfp) >> 2) & 0xc0) | (((hsp) >> 4) & 0x30) | (((vfp) >> 2) & 0x0c) | (((vsp) >> 4) & 0x03); \
|
||||
edid->slot.h_size_lsb = horiz_mm & 0xff; \
|
||||
edid->slot.v_size_lsb = vert_mm & 0xff; \
|
||||
edid->slot.hv_size_msb = ((horiz_mm >> 4) & 0xf0) | ((vert_mm >> 8) & 0x0f); \
|
||||
edid->slot.h_size_lsb = (uint8_t) horiz_mm; \
|
||||
edid->slot.v_size_lsb = (uint8_t) vert_mm; \
|
||||
edid->slot.hv_size_msb = ((((uint16_t) horiz_mm) >> 4) & 0xf0) | ((((uint16_t) vert_mm) >> 8) & 0x0f); \
|
||||
} while (0)
|
||||
|
||||
enum {
|
||||
@@ -133,7 +133,7 @@ ddc_init(void *i2c)
|
||||
memset(edid, 0, sizeof(edid_t));
|
||||
|
||||
uint8_t *edid_bytes = (uint8_t *) edid;
|
||||
uint16_t horiz_mm = PIXEL_MM(1366), vert_mm = PIXEL_MM(768);
|
||||
double horiz_mm = PIXEL_MM(800), vert_mm = PIXEL_MM(600);
|
||||
|
||||
memset(&edid->magic[1], 0xff, sizeof(edid->magic) - 2);
|
||||
|
||||
@@ -142,11 +142,11 @@ ddc_init(void *i2c)
|
||||
edid->mfg_week = 48;
|
||||
edid->mfg_year = 2020 - 1990;
|
||||
edid->edid_version = 0x01;
|
||||
edid->edid_rev = 0x03; /* EDID 1.3 */
|
||||
edid->edid_rev = 0x04; /* EDID 1.4, required for Xorg on Linux to use the preferred mode timing */
|
||||
|
||||
edid->input_params = 0x0e; /* analog input; separate sync; composite sync; sync on green */
|
||||
edid->horiz_size = horiz_mm / 10;
|
||||
edid->vert_size = vert_mm / 10;
|
||||
edid->horiz_size = round(horiz_mm / 10.0);
|
||||
edid->vert_size = round(vert_mm / 10.0);
|
||||
edid->features = 0xeb; /* DPMS standby/suspend/active-off; RGB color; first timing is preferred; GTF/CVT */
|
||||
|
||||
edid->red_green_lsb = 0x81;
|
||||
|
||||
@@ -57,7 +57,7 @@ static uint8_t ega_rotate[8][256];
|
||||
static uint32_t pallook16[256], pallook64[256];
|
||||
static int ega_type = 0, old_overscan_color = 0;
|
||||
|
||||
extern uint8_t edatlookup[4][4];
|
||||
uint8_t egaremap2bpp[256];
|
||||
|
||||
/* 3C2 controls default mode on EGA. On VGA, it determines monitor type (mono or colour):
|
||||
7=CGA mode (200 lines), 9=EGA mode (350 lines), 8=EGA mode (200 lines). */
|
||||
@@ -312,8 +312,25 @@ ega_in(uint16_t addr, void *p)
|
||||
break;
|
||||
case 0x3d1:
|
||||
case 0x3d5:
|
||||
if (ega_type)
|
||||
ret = ega->crtc[ega->crtcreg];
|
||||
switch(ega->crtcreg) {
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
ret = ega->crtc[ega->crtcreg];
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
// TODO: Return light pen address once implemented
|
||||
if (ega_type)
|
||||
ret = ega->crtc[ega->crtcreg];
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ega_type)
|
||||
ret = ega->crtc[ega->crtcreg];
|
||||
}
|
||||
break;
|
||||
case 0x3da:
|
||||
ega->attrff = 0;
|
||||
@@ -409,32 +426,16 @@ ega_recalctimings(ega_t *ega)
|
||||
ega->render = ega_render_blank;
|
||||
if (!ega->scrblank && ega->attr_palette_enable) {
|
||||
if (!(ega->gdcreg[6] & 1)) {
|
||||
if (ega->seqregs[1] & 8) {
|
||||
ega->render = ega_render_text_40;
|
||||
if (ega->seqregs[1] & 8)
|
||||
ega->hdisp *= (ega->seqregs[1] & 1) ? 16 : 18;
|
||||
} else {
|
||||
ega->render = ega_render_text_80;
|
||||
else
|
||||
ega->hdisp *= (ega->seqregs[1] & 1) ? 8 : 9;
|
||||
}
|
||||
ega->render = ega_render_text;
|
||||
ega->hdisp_old = ega->hdisp;
|
||||
} else {
|
||||
ega->hdisp *= (ega->seqregs[1] & 8) ? 16 : 8;
|
||||
ega->render = ega_render_graphics;
|
||||
ega->hdisp_old = ega->hdisp;
|
||||
|
||||
switch (ega->gdcreg[5] & 0x20) {
|
||||
case 0x00:
|
||||
if (ega->seqregs[1] & 8)
|
||||
ega->render = ega_render_4bpp_lowres;
|
||||
else
|
||||
ega->render = ega_render_4bpp_highres;
|
||||
break;
|
||||
case 0x20:
|
||||
if (ega->seqregs[1] & 8)
|
||||
ega->render = ega_render_2bpp_lowres;
|
||||
else
|
||||
ega->render = ega_render_2bpp_highres;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,6 +753,73 @@ ega_doblit(int wx, int wy, ega_t *ega)
|
||||
ega->y_add >>= 1;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ega_remap_cpu_addr(uint32_t inaddr, ega_t *ega)
|
||||
{
|
||||
int a0mux;
|
||||
uint32_t addr = inaddr;
|
||||
|
||||
// The CPU A0 line is multiplexed via a 3-to-8 mux.
|
||||
// Input bits are:
|
||||
// bit 0: 1 = 64K, 0 = 128K+ (from memory expansion connector)
|
||||
// bit 1: 1 = Odd/Even mode, 0 = normal mode (from GC reg 6 bit 1)
|
||||
// bit 2: 1 = 128K mapping, 0 = other mapping (from memory decode PROM)
|
||||
a0mux = 0;
|
||||
|
||||
if (ega->gdcreg[6] & 2) {
|
||||
a0mux |= 2;
|
||||
}
|
||||
if (ega->vram_limit <= 64*1024) {
|
||||
a0mux |= 1;
|
||||
}
|
||||
|
||||
switch ((ega->gdcreg[6] & 0xC)) {
|
||||
case 0x0: // 128K A000
|
||||
addr &= 0xFFFF;
|
||||
// TODO: Confirm the behaviour of this on actual hardware
|
||||
a0mux |= 4;
|
||||
break;
|
||||
case 0x4: // 64K A000
|
||||
addr &= 0xFFFF;
|
||||
break;
|
||||
case 0x8: // 32K B000
|
||||
addr &= 0x7FFF;
|
||||
break;
|
||||
case 0xC: // 32K B800
|
||||
addr &= 0x7FFF;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (a0mux) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 4:
|
||||
case 5:
|
||||
case 7: // A0 becomes A0
|
||||
break;
|
||||
case 2:
|
||||
// A0 becomes the inversion of PGSEL (reg 0x3C2, miscout, bit 5)
|
||||
// That is, 1 selects the "low" 64k, and 0 selects the "high" 64k.
|
||||
addr &= ~1;
|
||||
addr |= (~ega->miscout>>5)&1;
|
||||
break;
|
||||
case 3: // A0 becomes A14
|
||||
addr &= ~1;
|
||||
addr |= (inaddr>>14)&1;
|
||||
break;
|
||||
case 6: // A0 becomes A16
|
||||
addr &= ~1;
|
||||
addr |= (inaddr>>16)&1;
|
||||
break;
|
||||
}
|
||||
|
||||
// In 64k mode, only select the first 16Kword/64KB bank
|
||||
if (!(ega->seqregs[4] & 2)) {
|
||||
addr &= 0x3FFF;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
void
|
||||
ega_write(uint32_t addr, uint8_t val, void *p)
|
||||
{
|
||||
@@ -761,21 +829,14 @@ ega_write(uint32_t addr, uint8_t val, void *p)
|
||||
|
||||
cycles -= video_timing_write_b;
|
||||
|
||||
if (addr >= 0xB0000)
|
||||
addr &= 0x7fff;
|
||||
else
|
||||
addr &= 0xffff;
|
||||
|
||||
if (ega->chain2_write) {
|
||||
writemask2 &= ~0xa;
|
||||
if (addr & 1)
|
||||
writemask2 <<= 1;
|
||||
addr &= ~1;
|
||||
if (addr & 0x4000)
|
||||
addr |= 1;
|
||||
addr &= ~0x4000;
|
||||
}
|
||||
|
||||
addr = ega_remap_cpu_addr(addr, ega);
|
||||
|
||||
addr <<= 2;
|
||||
|
||||
if (addr >= ega->vram_limit)
|
||||
@@ -939,19 +1000,13 @@ ega_read(uint32_t addr, void *p)
|
||||
int readplane = ega->readplane;
|
||||
|
||||
cycles -= video_timing_read_b;
|
||||
if (addr >= 0xb0000)
|
||||
addr &= 0x7fff;
|
||||
else
|
||||
addr &= 0xffff;
|
||||
|
||||
if (ega->chain2_read) {
|
||||
readplane = (readplane & 2) | (addr & 1);
|
||||
addr &= ~1;
|
||||
if (addr & 0x4000)
|
||||
addr |= 1;
|
||||
addr &= ~0x4000;
|
||||
}
|
||||
|
||||
addr = ega_remap_cpu_addr(addr, ega);
|
||||
|
||||
addr <<= 2;
|
||||
|
||||
if (addr >= ega->vram_limit)
|
||||
@@ -1009,6 +1064,18 @@ ega_init(ega_t *ega, int monitor_type, int is_mono)
|
||||
}
|
||||
}
|
||||
|
||||
for (c = 0; c < 256; c++) {
|
||||
egaremap2bpp[c] = 0;
|
||||
if (c & 0x01)
|
||||
egaremap2bpp[c] |= 0x01;
|
||||
if (c & 0x04)
|
||||
egaremap2bpp[c] |= 0x02;
|
||||
if (c & 0x10)
|
||||
egaremap2bpp[c] |= 0x04;
|
||||
if (c & 0x40)
|
||||
egaremap2bpp[c] |= 0x08;
|
||||
}
|
||||
|
||||
if (is_mono) {
|
||||
for (c = 0; c < 256; c++) {
|
||||
if (((c >> 3) & 3) == 0)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
@@ -105,16 +106,8 @@ ega_render_overscan_right(ega_t *ega)
|
||||
}
|
||||
|
||||
void
|
||||
ega_render_text_40(ega_t *ega)
|
||||
ega_render_text(ega_t *ega)
|
||||
{
|
||||
uint32_t *p;
|
||||
int x, xx;
|
||||
int drawcursor, xinc;
|
||||
uint8_t chr, attr, dat;
|
||||
uint32_t charaddr;
|
||||
int fg, bg;
|
||||
uint32_t addr;
|
||||
|
||||
if ((ega->displine + ega->y_add) < 0)
|
||||
return;
|
||||
|
||||
@@ -123,25 +116,36 @@ ega_render_text_40(ega_t *ega)
|
||||
ega->lastline_draw = ega->displine;
|
||||
|
||||
if (ega->fullchange) {
|
||||
p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
xinc = (ega->seqregs[1] & 1) ? 16 : 18;
|
||||
const bool doublewidth = ((ega->seqregs[1] & 8) != 0);
|
||||
const bool attrblink = ((ega->attrregs[0x10] & 8) != 0);
|
||||
const bool attrlinechars = (ega->attrregs[0x10] & 4);
|
||||
const bool crtcreset = ((ega->crtc[0x17] & 0x80) == 0);
|
||||
const bool seq9dot = ((ega->seqregs[1] & 1) == 0);
|
||||
const int dwshift = doublewidth ? 1 : 0;
|
||||
const int dotwidth = 1 << dwshift;
|
||||
const int charwidth = dotwidth*(seq9dot ? 9 : 8);
|
||||
const bool blinked = ega->blink & 0x10;
|
||||
uint32_t *p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
|
||||
for (x = 0; x < (ega->hdisp + ega->scrollcache); x += xinc) {
|
||||
addr = ega->remap_func(ega, ega->ma) & ega->vrammask;
|
||||
for (int x = 0; x < (ega->hdisp + ega->scrollcache); x += charwidth) {
|
||||
uint32_t addr = ega->remap_func(ega, ega->ma) & ega->vrammask;
|
||||
|
||||
drawcursor = ((ega->ma == ega->ca) && ega->con && ega->cursoron);
|
||||
int drawcursor = ((ega->ma == ega->ca) && ega->con && ega->cursoron);
|
||||
|
||||
if (ega->crtc[0x17] & 0x80) {
|
||||
uint32_t chr, attr;
|
||||
if (!crtcreset) {
|
||||
chr = ega->vram[addr];
|
||||
attr = ega->vram[addr + 1];
|
||||
} else
|
||||
chr = attr = 0;
|
||||
|
||||
uint32_t charaddr;
|
||||
if (attr & 8)
|
||||
charaddr = ega->charsetb + ((chr * 0x80));
|
||||
else
|
||||
charaddr = ega->charseta + ((chr * 0x80));
|
||||
|
||||
int fg, bg;
|
||||
if (drawcursor) {
|
||||
bg = ega->pallook[ega->egapal[attr & 0x0f]];
|
||||
fg = ega->pallook[ega->egapal[attr >> 4]];
|
||||
@@ -149,43 +153,31 @@ ega_render_text_40(ega_t *ega)
|
||||
fg = ega->pallook[ega->egapal[attr & 0x0f]];
|
||||
bg = ega->pallook[ega->egapal[attr >> 4]];
|
||||
|
||||
if ((attr & 0x80) && ega->attrregs[0x10] & 8) {
|
||||
if ((attr & 0x80) && attrblink) {
|
||||
bg = ega->pallook[ega->egapal[(attr >> 4) & 7]];
|
||||
if (ega->blink & 0x10)
|
||||
if (blinked)
|
||||
fg = bg;
|
||||
}
|
||||
}
|
||||
|
||||
dat = ega->vram[charaddr + (ega->sc << 2)];
|
||||
if (ega->seqregs[1] & 1) {
|
||||
for (xx = 0; xx < 16; xx += 2)
|
||||
p[xx] = p[xx + 1] = (dat & (0x80 >> (xx >> 1))) ? fg : bg;
|
||||
} else {
|
||||
for (xx = 0; xx < 16; xx += 2)
|
||||
p[xx] = p[xx + 1] = (dat & (0x80 >> (xx >> 1))) ? fg : bg;
|
||||
if ((chr & ~0x1f) != 0xc0 || !(ega->attrregs[0x10] & 4))
|
||||
p[16] = p[17] = bg;
|
||||
else
|
||||
p[16] = p[17] = (dat & 1) ? fg : bg;
|
||||
}
|
||||
uint32_t dat = ega->vram[charaddr + (ega->sc << 2)];
|
||||
dat <<= 1;
|
||||
if ((chr & ~0x1F) == 0xC0 && attrlinechars)
|
||||
dat |= (dat >> 1) & 1;
|
||||
|
||||
for (int xx = 0; xx < charwidth; xx++)
|
||||
p[xx] = (dat & (0x100 >> (xx>>dwshift))) ? fg : bg;
|
||||
|
||||
ega->ma += 4;
|
||||
p += xinc;
|
||||
p += charwidth;
|
||||
}
|
||||
ega->ma &= ega->vrammask;
|
||||
ega->ma &= 0x3ffff;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ega_render_text_80(ega_t *ega)
|
||||
ega_render_graphics(ega_t *ega)
|
||||
{
|
||||
uint32_t *p;
|
||||
int x, xx;
|
||||
int drawcursor, xinc;
|
||||
uint8_t chr, attr, dat;
|
||||
uint32_t charaddr;
|
||||
int fg, bg;
|
||||
uint32_t addr;
|
||||
|
||||
if ((ega->displine + ega->y_add) < 0)
|
||||
return;
|
||||
|
||||
@@ -193,246 +185,66 @@ ega_render_text_80(ega_t *ega)
|
||||
ega->firstline_draw = ega->displine;
|
||||
ega->lastline_draw = ega->displine;
|
||||
|
||||
if (ega->fullchange) {
|
||||
p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
xinc = (ega->seqregs[1] & 1) ? 8 : 9;
|
||||
const bool doublewidth = ((ega->seqregs[1] & 8) != 0);
|
||||
const bool cga2bpp = ((ega->gdcreg[5] & 0x20) != 0);
|
||||
const bool attrblink = ((ega->attrregs[0x10] & 8) != 0);
|
||||
const bool blinked = ega->blink & 0x10;
|
||||
const bool crtcreset = ((ega->crtc[0x17] & 0x80) == 0);
|
||||
const bool seqoddeven = ((ega->seqregs[1] & 4) != 0);
|
||||
const uint8_t blinkmask = (attrblink && blinked ? 0x8 : 0x0);
|
||||
uint32_t *p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
const int dwshift = doublewidth ? 1 : 0;
|
||||
const int dotwidth = 1 << dwshift;
|
||||
const int charwidth = dotwidth*8;
|
||||
int secondcclk = 0;
|
||||
for (int x = 0; x <= (ega->hdisp + ega->scrollcache); x += charwidth) {
|
||||
uint32_t addr = ega->remap_func(ega, ega->ma) & ega->vrammask;
|
||||
|
||||
for (x = 0; x < (ega->hdisp + ega->scrollcache); x += xinc) {
|
||||
addr = ega->remap_func(ega, ega->ma) & ega->vrammask;
|
||||
|
||||
drawcursor = ((ega->ma == ega->ca) && ega->con && ega->cursoron);
|
||||
|
||||
if (ega->crtc[0x17] & 0x80) {
|
||||
chr = ega->vram[addr];
|
||||
attr = ega->vram[addr + 1];
|
||||
} else
|
||||
chr = attr = 0;
|
||||
|
||||
if (attr & 0x08)
|
||||
charaddr = ega->charsetb + (chr * 0x80);
|
||||
else
|
||||
charaddr = ega->charseta + (chr * 0x80);
|
||||
|
||||
if (drawcursor) {
|
||||
bg = ega->pallook[ega->egapal[attr & 0x0f]];
|
||||
fg = ega->pallook[ega->egapal[attr >> 4]];
|
||||
} else {
|
||||
fg = ega->pallook[ega->egapal[attr & 0x0f]];
|
||||
bg = ega->pallook[ega->egapal[attr >> 4]];
|
||||
if ((attr & 0x80) && ega->attrregs[0x10] & 8) {
|
||||
bg = ega->pallook[ega->egapal[(attr >> 4) & 7]];
|
||||
if (ega->blink & 16)
|
||||
fg = bg;
|
||||
}
|
||||
}
|
||||
|
||||
dat = ega->vram[charaddr + (ega->sc << 2)];
|
||||
if (ega->seqregs[1] & 1) {
|
||||
for (xx = 0; xx < 8; xx++)
|
||||
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
|
||||
} else {
|
||||
for (xx = 0; xx < 8; xx++)
|
||||
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
|
||||
if ((chr & ~0x1F) != 0xC0 || !(ega->attrregs[0x10] & 4))
|
||||
p[8] = bg;
|
||||
else
|
||||
p[8] = (dat & 1) ? fg : bg;
|
||||
}
|
||||
ega->ma += 4;
|
||||
p += xinc;
|
||||
}
|
||||
ega->ma &= ega->vrammask;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ega_render_2bpp_lowres(ega_t *ega)
|
||||
{
|
||||
int x;
|
||||
uint8_t dat[2];
|
||||
uint32_t addr, *p;
|
||||
|
||||
if ((ega->displine + ega->y_add) < 0)
|
||||
return;
|
||||
|
||||
p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
|
||||
if (ega->firstline_draw == 2000)
|
||||
ega->firstline_draw = ega->displine;
|
||||
ega->lastline_draw = ega->displine;
|
||||
|
||||
for (x = 0; x <= (ega->hdisp + ega->scrollcache); x += 16) {
|
||||
addr = ega->remap_func(ega, ega->ma);
|
||||
|
||||
dat[0] = ega->vram[addr];
|
||||
dat[1] = ega->vram[addr | 0x1];
|
||||
if (ega->seqregs[1] & 4)
|
||||
ega->ma += 2;
|
||||
else
|
||||
ega->ma += 4;
|
||||
|
||||
ega->ma &= ega->vrammask;
|
||||
|
||||
if (ega->crtc[0x17] & 0x80) {
|
||||
p[0] = p[1] = ega->pallook[ega->egapal[(dat[0] >> 6) & 3]];
|
||||
p[2] = p[3] = ega->pallook[ega->egapal[(dat[0] >> 4) & 3]];
|
||||
p[4] = p[5] = ega->pallook[ega->egapal[(dat[0] >> 2) & 3]];
|
||||
p[6] = p[7] = ega->pallook[ega->egapal[dat[0] & 3]];
|
||||
p[8] = p[9] = ega->pallook[ega->egapal[(dat[1] >> 6) & 3]];
|
||||
p[10] = p[11] = ega->pallook[ega->egapal[(dat[1] >> 4) & 3]];
|
||||
p[12] = p[13] = ega->pallook[ega->egapal[(dat[1] >> 2) & 3]];
|
||||
p[14] = p[15] = ega->pallook[ega->egapal[dat[1] & 3]];
|
||||
} else
|
||||
memset(p, 0x00, 16 * sizeof(uint32_t));
|
||||
|
||||
p += 16;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ega_render_2bpp_highres(ega_t *ega)
|
||||
{
|
||||
int x;
|
||||
uint8_t dat[2];
|
||||
uint32_t addr, *p;
|
||||
|
||||
if ((ega->displine + ega->y_add) < 0)
|
||||
return;
|
||||
|
||||
p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
|
||||
if (ega->firstline_draw == 2000)
|
||||
ega->firstline_draw = ega->displine;
|
||||
ega->lastline_draw = ega->displine;
|
||||
|
||||
for (x = 0; x <= (ega->hdisp + ega->scrollcache); x += 8) {
|
||||
addr = ega->remap_func(ega, ega->ma);
|
||||
|
||||
dat[0] = ega->vram[addr];
|
||||
dat[1] = ega->vram[addr | 0x1];
|
||||
if (ega->seqregs[1] & 4)
|
||||
ega->ma += 2;
|
||||
else
|
||||
ega->ma += 4;
|
||||
|
||||
ega->ma &= ega->vrammask;
|
||||
|
||||
if (ega->crtc[0x17] & 0x80) {
|
||||
p[0] = ega->pallook[ega->egapal[(dat[0] >> 6) & 3]];
|
||||
p[1] = ega->pallook[ega->egapal[(dat[0] >> 4) & 3]];
|
||||
p[2] = ega->pallook[ega->egapal[(dat[0] >> 2) & 3]];
|
||||
p[3] = ega->pallook[ega->egapal[dat[0] & 3]];
|
||||
p[4] = ega->pallook[ega->egapal[(dat[1] >> 6) & 3]];
|
||||
p[5] = ega->pallook[ega->egapal[(dat[1] >> 4) & 3]];
|
||||
p[6] = ega->pallook[ega->egapal[(dat[1] >> 2) & 3]];
|
||||
p[7] = ega->pallook[ega->egapal[dat[1] & 3]];
|
||||
} else
|
||||
memset(p, 0x00, 8 * sizeof(uint32_t));
|
||||
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ega_render_4bpp_lowres(ega_t *ega)
|
||||
{
|
||||
int x, oddeven;
|
||||
uint8_t dat, edat[4];
|
||||
uint32_t addr, *p;
|
||||
|
||||
if ((ega->displine + ega->y_add) < 0)
|
||||
return;
|
||||
|
||||
p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
|
||||
if (ega->firstline_draw == 2000)
|
||||
ega->firstline_draw = ega->displine;
|
||||
ega->lastline_draw = ega->displine;
|
||||
|
||||
for (x = 0; x <= (ega->hdisp + ega->scrollcache); x += 16) {
|
||||
addr = ega->remap_func(ega, ega->ma);
|
||||
oddeven = 0;
|
||||
|
||||
if (ega->seqregs[1] & 4) {
|
||||
oddeven = (addr & 4) ? 1 : 0;
|
||||
edat[0] = ega->vram[addr | oddeven];
|
||||
edat[2] = ega->vram[addr | oddeven | 0x2];
|
||||
edat[1] = edat[3] = 0;
|
||||
ega->ma += 2;
|
||||
uint8_t edat[4];
|
||||
if (seqoddeven) {
|
||||
// FIXME: Verify the behaviour of planes 1,3 on actual hardware
|
||||
edat[0] = ega->vram[(addr | 0) ^ secondcclk];
|
||||
edat[1] = ega->vram[(addr | 1) ^ secondcclk];
|
||||
edat[2] = ega->vram[(addr | 2) ^ secondcclk];
|
||||
edat[3] = ega->vram[(addr | 3) ^ secondcclk];
|
||||
secondcclk = (secondcclk + 1) & 1;
|
||||
if (secondcclk == 0)
|
||||
ega->ma += 4;
|
||||
} else {
|
||||
*(uint32_t *) (&edat[0]) = *(uint32_t *) (&ega->vram[addr]);
|
||||
ega->ma += 4;
|
||||
}
|
||||
ega->ma &= ega->vrammask;
|
||||
ega->ma &= 0x3ffff;
|
||||
|
||||
if (ega->crtc[0x17] & 0x80) {
|
||||
dat = edatlookup[edat[0] >> 6][edat[1] >> 6] | (edatlookup[edat[2] >> 6][edat[3] >> 6] << 2);
|
||||
p[0] = p[1] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[2] = p[3] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
dat = edatlookup[(edat[0] >> 4) & 3][(edat[1] >> 4) & 3] | (edatlookup[(edat[2] >> 4) & 3][(edat[3] >> 4) & 3] << 2);
|
||||
p[4] = p[5] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[6] = p[7] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
dat = edatlookup[(edat[0] >> 2) & 3][(edat[1] >> 2) & 3] | (edatlookup[(edat[2] >> 2) & 3][(edat[3] >> 2) & 3] << 2);
|
||||
p[8] = p[9] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[10] = p[11] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
dat = edatlookup[edat[0] & 3][edat[1] & 3] | (edatlookup[edat[2] & 3][edat[3] & 3] << 2);
|
||||
p[12] = p[13] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[14] = p[15] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
} else
|
||||
memset(p, 0x00, 16 * sizeof(uint32_t));
|
||||
|
||||
p += 16;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ega_render_4bpp_highres(ega_t *ega)
|
||||
{
|
||||
int x, oddeven;
|
||||
uint8_t dat, edat[4];
|
||||
uint32_t addr, *p;
|
||||
|
||||
if ((ega->displine + ega->y_add) < 0)
|
||||
return;
|
||||
|
||||
p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
|
||||
|
||||
if (ega->firstline_draw == 2000)
|
||||
ega->firstline_draw = ega->displine;
|
||||
ega->lastline_draw = ega->displine;
|
||||
|
||||
for (x = 0; x <= (ega->hdisp + ega->scrollcache); x += 8) {
|
||||
addr = ega->remap_func(ega, ega->ma);
|
||||
oddeven = 0;
|
||||
|
||||
if (ega->seqregs[1] & 4) {
|
||||
oddeven = (addr & 4) ? 1 : 0;
|
||||
edat[0] = ega->vram[addr | oddeven];
|
||||
edat[2] = ega->vram[addr | oddeven | 0x2];
|
||||
edat[1] = edat[3] = 0;
|
||||
ega->ma += 2;
|
||||
} else {
|
||||
*(uint32_t *) (&edat[0]) = *(uint32_t *) (&ega->vram[addr]);
|
||||
ega->ma += 4;
|
||||
if (cga2bpp) {
|
||||
// Remap CGA 2bpp-chunky data into fully planar data
|
||||
uint8_t dat0 = egaremap2bpp[edat[1] ] | (egaremap2bpp[edat[0] ] << 4);
|
||||
uint8_t dat1 = egaremap2bpp[edat[1]>>1] | (egaremap2bpp[edat[0]>>1] << 4);
|
||||
uint8_t dat2 = egaremap2bpp[edat[3] ] | (egaremap2bpp[edat[2] ] << 4);
|
||||
uint8_t dat3 = egaremap2bpp[edat[3]>>1] | (egaremap2bpp[edat[2]>>1] << 4);
|
||||
edat[0] = dat0;
|
||||
edat[1] = dat1;
|
||||
edat[2] = dat2;
|
||||
edat[3] = dat3;
|
||||
}
|
||||
ega->ma &= ega->vrammask;
|
||||
|
||||
if (ega->crtc[0x17] & 0x80) {
|
||||
dat = edatlookup[edat[0] >> 6][edat[1] >> 6] | (edatlookup[edat[2] >> 6][edat[3] >> 6] << 2);
|
||||
p[0] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[1] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
dat = edatlookup[(edat[0] >> 4) & 3][(edat[1] >> 4) & 3] | (edatlookup[(edat[2] >> 4) & 3][(edat[3] >> 4) & 3] << 2);
|
||||
p[2] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[3] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
dat = edatlookup[(edat[0] >> 2) & 3][(edat[1] >> 2) & 3] | (edatlookup[(edat[2] >> 2) & 3][(edat[3] >> 2) & 3] << 2);
|
||||
p[4] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[5] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
dat = edatlookup[edat[0] & 3][edat[1] & 3] | (edatlookup[edat[2] & 3][edat[3] & 3] << 2);
|
||||
p[6] = ega->pallook[ega->egapal[(dat >> 4) & ega->plane_mask]];
|
||||
p[7] = ega->pallook[ega->egapal[dat & ega->plane_mask]];
|
||||
if (!crtcreset) {
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
const int outoffs = i << dwshift;
|
||||
const int inshift = 6 - i;
|
||||
uint8_t dat = (edatlookup[(edat[0] >> inshift) & 3][(edat[1] >> inshift) & 3] )
|
||||
| (edatlookup[(edat[2] >> inshift) & 3][(edat[3] >> inshift) & 3] << 2);
|
||||
// FIXME: Confirm blink behaviour is actually XOR on real hardware
|
||||
uint32_t p0 = ega->pallook[ega->egapal[((dat >> 4) & ega->plane_mask) ^ blinkmask]];
|
||||
uint32_t p1 = ega->pallook[ega->egapal[((dat ) & ega->plane_mask) ^ blinkmask]];
|
||||
for (int subx = 0; subx < dotwidth; subx++)
|
||||
p[outoffs + subx] = p0;
|
||||
for (int subx = 0; subx < dotwidth; subx++)
|
||||
p[outoffs + subx + dotwidth] = p1;
|
||||
}
|
||||
} else
|
||||
memset(p, 0x00, 8 * sizeof(uint32_t));
|
||||
memset(p, 0x00, charwidth * sizeof(uint32_t));
|
||||
|
||||
p += 8;
|
||||
p += charwidth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,12 +96,12 @@ typedef struct et4000w32p_t {
|
||||
uint8_t suspend_terminate, osr;
|
||||
uint8_t status;
|
||||
uint16_t x_count, y_count;
|
||||
uint16_t cpu_x_cnt, cpu_x_cnt_back, cpu_y_cnt;
|
||||
|
||||
int pattern_x, source_x, pattern_x_back, source_x_back,
|
||||
pattern_y, source_y, cpu_dat_pos, pix_pos,
|
||||
cpu_input_num, fifo_queue;
|
||||
int pattern_x_diff, pattern_y_diff, pattern_x_diff2, pattern_y_diff2;
|
||||
int patcnt, mmu_start;
|
||||
int mmu_start;
|
||||
|
||||
uint32_t pattern_addr, source_addr, dest_addr, mix_addr,
|
||||
pattern_back, source_back, dest_back, mix_back,
|
||||
@@ -121,7 +121,7 @@ typedef struct et4000w32p_t {
|
||||
static int et4000w32_vbus[4] = { 1, 2, 4, 4 };
|
||||
|
||||
static int et4000w32_max_x[8] = { 0, 0, 4, 8, 0x10, 0x20, 0x40, 0x70000000 };
|
||||
static int et4000w32_wrap_x[8] = { 0, 0, 3, 7, 0x0F, 0x1F, 0x3F, ~0 };
|
||||
static int et4000w32_wrap_x[8] = { 0, 0, 3, 7, 0x0f, 0x1f, 0x3f, ~0 };
|
||||
static int et4000w32_wrap_y[8] = { 1, 2, 4, 8, ~0, ~0, ~0, ~0 };
|
||||
|
||||
static video_timings_t timing_et4000w32_vlb = { .type = VIDEO_BUS, .write_b = 4, .write_w = 4, .write_l = 4, .read_b = 10, .read_w = 10, .read_l = 10 };
|
||||
@@ -272,14 +272,20 @@ et4000w32p_out(uint16_t addr, uint8_t val, void *p)
|
||||
case 0x216b:
|
||||
case 0x217b:
|
||||
et4000->regs[et4000->index] = val;
|
||||
svga->hwcursor.cur_xsize = svga->hwcursor.cur_ysize = ((et4000->regs[0xEF] & 4) || (et4000->type == ET4000W32)) ? 128 : 64;
|
||||
svga->hwcursor.x = et4000->regs[0xE0] | ((et4000->regs[0xE1] & 7) << 8);
|
||||
svga->hwcursor.y = et4000->regs[0xE4] | ((et4000->regs[0xE5] & 7) << 8);
|
||||
svga->hwcursor.ena = !!(et4000->regs[0xF7] & 0x80);
|
||||
svga->hwcursor.xoff = et4000->regs[0xE2];
|
||||
svga->hwcursor.yoff = et4000->regs[0xE6];
|
||||
svga->hwcursor.cur_xsize = svga->hwcursor.cur_ysize = ((et4000->regs[0xEF] & 4) || ((et4000->type == ET4000W32) && et4000->regs[0xe2] && et4000->regs[0xe6])) ? 128 : 64;
|
||||
|
||||
if (et4000->type == ET4000W32) {
|
||||
if ((svga->bpp == 15) || (svga->bpp == 16)) {
|
||||
svga->hwcursor.cur_xsize = svga->hwcursor.cur_ysize = 128;
|
||||
}
|
||||
}
|
||||
|
||||
if (((et4000->type == ET4000W32) && (svga->hwcursor.cur_xsize == 128))) {
|
||||
switch (svga->bpp) {
|
||||
case 8:
|
||||
svga->hwcursor.xoff += 32;
|
||||
@@ -297,7 +303,7 @@ et4000w32p_out(uint16_t addr, uint8_t val, void *p)
|
||||
}
|
||||
} else {
|
||||
if (et4000->type > ET4000W32P_REVC) {
|
||||
if (svga->bpp == 24 && et4000->adjust_cursor) {
|
||||
if ((svga->bpp == 24) && et4000->adjust_cursor) {
|
||||
et4000->adjust_cursor = 0;
|
||||
}
|
||||
}
|
||||
@@ -674,40 +680,46 @@ et4000w32p_accel_write_fifo(et4000w32p_t *et4000, uint32_t addr, uint8_t val)
|
||||
et4000->acl.fifo_queue++;
|
||||
switch (addr & 0xff) {
|
||||
case 0x80:
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0x3fff00) | val;
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0xffffff00) | val;
|
||||
break;
|
||||
case 0x81:
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0x3f00ff) | (val << 8);
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0xffff00ff) | (val << 8);
|
||||
break;
|
||||
case 0x82:
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0x00ffff) | ((val & 0x3f) << 16);
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0xff00ffff) | (val << 16);
|
||||
break;
|
||||
case 0x83:
|
||||
et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0x00ffffff) | (val << 24);
|
||||
break;
|
||||
case 0x84:
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0x3fff00) | val;
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0xffffff00) | val;
|
||||
break;
|
||||
case 0x85:
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0x3f00ff) | (val << 8);
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0xffff00ff) | (val << 8);
|
||||
break;
|
||||
case 0x86:
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0x00ffff) | ((val & 0x3f) << 16);
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0xff00ffff) | (val << 16);
|
||||
break;
|
||||
case 0x87:
|
||||
et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0x00ffffff) | (val << 24);
|
||||
break;
|
||||
case 0x88:
|
||||
et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0x0f00) | val;
|
||||
et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0xff00) | val;
|
||||
break;
|
||||
case 0x89:
|
||||
et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0x00ff) | ((val & 0x0f) << 8);
|
||||
et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case 0x8a:
|
||||
et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0x0f00) | val;
|
||||
et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0xff00) | val;
|
||||
break;
|
||||
case 0x8b:
|
||||
et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0x00ff) | ((val & 0x0f) << 8);
|
||||
et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case 0x8c:
|
||||
et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0x0f00) | val;
|
||||
et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0xff00) | val;
|
||||
break;
|
||||
case 0x8d:
|
||||
et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0x00ff) | ((val & 0x0f) << 8);
|
||||
et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case 0x8e:
|
||||
if (et4000->type >= ET4000W32P_REVC)
|
||||
@@ -728,16 +740,16 @@ et4000w32p_accel_write_fifo(et4000w32p_t *et4000, uint32_t addr, uint8_t val)
|
||||
et4000->acl.queued.source_wrap = val & 0x77;
|
||||
break;
|
||||
case 0x98:
|
||||
et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0x0f00) | val;
|
||||
et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0xff00) | val;
|
||||
break;
|
||||
case 0x99:
|
||||
et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0x00ff) | ((val & 0x0f) << 8);
|
||||
et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case 0x9a:
|
||||
et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0x0f00) | val;
|
||||
et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0xff00) | val;
|
||||
break;
|
||||
case 0x9b:
|
||||
et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0x00ff) | ((val & 0x0f) << 8);
|
||||
et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0x00ff) | (val << 8);
|
||||
break;
|
||||
case 0x9c:
|
||||
if (et4000->type >= ET4000W32P_REVC)
|
||||
@@ -755,15 +767,16 @@ et4000w32p_accel_write_fifo(et4000w32p_t *et4000, uint32_t addr, uint8_t val)
|
||||
et4000->acl.queued.rop_fg = val;
|
||||
break;
|
||||
case 0xa0:
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0x3fff00) | val;
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0xffffff00) | val;
|
||||
break;
|
||||
case 0xa1:
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0x3f00ff) | (val << 8);
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0xffff00ff) | (val << 8);
|
||||
break;
|
||||
case 0xa2:
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0x00ffff) | ((val & 0x3f) << 16);
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0xff00ffff) | (val << 16);
|
||||
break;
|
||||
case 0xa3:
|
||||
et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0x00ffffff) | (val << 24);
|
||||
et4000->acl.internal = et4000->acl.queued;
|
||||
if (et4000->type >= ET4000W32P_REVC) {
|
||||
et4000w32p_blit_start(et4000);
|
||||
@@ -778,9 +791,10 @@ et4000w32p_accel_write_fifo(et4000w32p_t *et4000, uint32_t addr, uint8_t val)
|
||||
et4000w32_blit_start(et4000);
|
||||
et4000->acl.cpu_input_num = 0;
|
||||
if (!(et4000->acl.queued.ctrl_routing & 0x37)) {
|
||||
et4000->acl.mmu_start = 0;
|
||||
et4000->acl.mmu_start = 1;
|
||||
et4000w32_blit(-1, 0, 0, 0xffffffff, et4000);
|
||||
}
|
||||
} else
|
||||
et4000->acl.mmu_start = 0;
|
||||
}
|
||||
break;
|
||||
case 0xa4:
|
||||
@@ -843,11 +857,13 @@ et4000w32p_accel_write_mmu(et4000w32p_t *et4000, uint32_t addr, uint8_t val, uin
|
||||
et4000->acl.queued.dest_addr = ((addr & 0x1fff) + et4000->mmu.base[bank]);
|
||||
et4000->acl.internal = et4000->acl.queued;
|
||||
et4000w32_blit_start(et4000);
|
||||
et4000w32_log("ET4000W32 Accelerated MMU aperture start XY Block (Implicit): bank = %i, patx = %i, paty = %i, wrap y = %i\n", et4000->bank, et4000->acl.pattern_x, et4000->acl.pattern_y, et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7]);
|
||||
et4000w32_log("ET4000W32 Accelerated MMU aperture start XY Block (Implicit): bank = %i, patx = %i, paty = %i, wrap x = %i, wrap y = %i\n", et4000->bank, et4000->acl.pattern_x, et4000->acl.pattern_y, et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7], et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7]);
|
||||
et4000->acl.cpu_input_num = 0;
|
||||
if (!(et4000->acl.queued.ctrl_routing & 0x37)) {
|
||||
et4000->acl.mmu_start = 1;
|
||||
et4000w32_blit(-1, 0, 0, 0xffffffff, et4000);
|
||||
} else {
|
||||
et4000->acl.mmu_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,10 +881,19 @@ et4000w32p_accel_write_mmu(et4000w32p_t *et4000, uint32_t addr, uint8_t val, uin
|
||||
et4000->acl.cpu_input_num = 0;
|
||||
}
|
||||
|
||||
if ((et4000->acl.internal.ctrl_routing & 7) == 4) /*CPU data is X Count*/
|
||||
et4000w32_blit(val | (et4000->acl.queued.count_x << 8), 0, 0, 0xffffffff, et4000);
|
||||
if ((et4000->acl.internal.ctrl_routing & 7) == 5) /*CPU data is Y Count*/
|
||||
et4000w32_blit(val | (et4000->acl.queued.count_y << 8), 0, 0, 0xffffffff, et4000);
|
||||
if (et4000w32_vbus[et4000->acl.internal.vbus] == 1) {
|
||||
if ((et4000->acl.internal.ctrl_routing & 7) == 4) { /*CPU data is X Count*/
|
||||
et4000w32_log("ET4000W32 Accelerated MMU aperture routing = %02x: val = %02x, cx = %02x.\n", et4000->acl.internal.ctrl_routing, val, et4000->acl.internal.count_x);
|
||||
et4000->acl.cpu_x_cnt = val + 1;
|
||||
et4000->acl.cpu_x_cnt |= ((et4000->acl.queued.count_x >> 8) << 8);
|
||||
et4000w32_blit(et4000->acl.cpu_x_cnt, 3, 0, 0xffffffff, et4000);
|
||||
} else if ((et4000->acl.internal.ctrl_routing & 7) == 5) { /*CPU data is Y Count*/
|
||||
et4000w32_log("ET4000W32 Accelerated MMU aperture routing = %02x: val = %02x, cy = %02x.\n", et4000->acl.internal.ctrl_routing, val, et4000->acl.internal.count_y);
|
||||
et4000->acl.cpu_y_cnt = val + 1;
|
||||
et4000->acl.cpu_y_cnt |= ((et4000->acl.queued.count_y >> 8) << 8);
|
||||
et4000w32_blit(et4000->acl.cpu_y_cnt, 4, 0, 0xffffffff, et4000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -899,31 +924,40 @@ et4000w32p_mmu_write(uint32_t addr, uint8_t val, void *p)
|
||||
} else {
|
||||
switch (addr & 0xff) {
|
||||
case 0x00:
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0x3fff00) | val;
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0xffffff00) | val;
|
||||
break;
|
||||
case 0x01:
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0x3f00ff) | (val << 8);
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0xffff00ff) | (val << 8);
|
||||
break;
|
||||
case 0x02:
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0x00ffff) | ((val & 0x3f) << 16);
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0xff00ffff) | (val << 16);
|
||||
break;
|
||||
case 0x03:
|
||||
et4000->mmu.base[0] = (et4000->mmu.base[0] & 0x00ffffff) | (val << 24);
|
||||
break;
|
||||
case 0x04:
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0x3fff00) | val;
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0xffffff00) | val;
|
||||
break;
|
||||
case 0x05:
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0x3f00ff) | (val << 8);
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0xffff00ff) | (val << 8);
|
||||
break;
|
||||
case 0x06:
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0x00ffff) | ((val & 0x3f) << 16);
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0xff00ffff) | (val << 16);
|
||||
break;
|
||||
case 0x07:
|
||||
et4000->mmu.base[1] = (et4000->mmu.base[1] & 0x00ffffff) | (val << 24);
|
||||
break;
|
||||
case 0x08:
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0x3fff00) | val;
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0xffffff00) | val;
|
||||
break;
|
||||
case 0x09:
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0x3f00ff) | (val << 8);
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0xffff00ff) | (val << 8);
|
||||
break;
|
||||
case 0x0a:
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0x00ffff) | ((val & 0x3f) << 16);
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0xff00ffff) | (val << 16);
|
||||
break;
|
||||
case 0x0b:
|
||||
et4000->mmu.base[2] = (et4000->mmu.base[2] & 0x00ffffff) | (val << 24);
|
||||
break;
|
||||
case 0x13:
|
||||
et4000->mmu.ctrl = val;
|
||||
@@ -1127,6 +1161,26 @@ et4000w32_blit_start(et4000w32p_t *et4000)
|
||||
et4000->acl.source_back &= ~(((et4000w32_wrap_x[et4000->acl.internal.source_wrap & 7] + 1) * et4000w32_wrap_y[(et4000->acl.internal.source_wrap >> 4) & 7]) - 1);
|
||||
}
|
||||
et4000->acl.source_x_back = et4000->acl.source_x;
|
||||
|
||||
if ((et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] == 7) && !(et4000->acl.internal.ctrl_routing & 0x37) && (et4000->acl.internal.rop_fg == 0x5a)) {
|
||||
if ((et4000->acl.internal.count_y > 0) && (et4000->acl.pattern_y > 0)) {
|
||||
if (et4000->acl.pattern_addr == et4000->acl.pattern_back)
|
||||
et4000->acl.pattern_y = 0;
|
||||
else {
|
||||
et4000->acl.pattern_y = (et4000->acl.pattern_addr - et4000->acl.pattern_back) & 0x70;
|
||||
et4000->acl.pattern_y >>= 4;
|
||||
}
|
||||
}
|
||||
} else if ((et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] == 15) && !(et4000->acl.internal.ctrl_routing & 0x37) && (et4000->acl.internal.rop_fg == 0x5a)) {
|
||||
if ((et4000->acl.internal.count_y > 0) && (et4000->acl.pattern_y > 0)) {
|
||||
if (et4000->acl.pattern_addr == et4000->acl.pattern_back)
|
||||
et4000->acl.pattern_y = 0;
|
||||
else {
|
||||
et4000->acl.pattern_y = (et4000->acl.pattern_addr - et4000->acl.pattern_back) & 0xf0;
|
||||
et4000->acl.pattern_y >>= 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1137,6 +1191,7 @@ et4000w32p_blit_start(et4000w32p_t *et4000)
|
||||
|
||||
if (!(et4000->acl.queued.xy_dir & 0x20))
|
||||
et4000->acl.internal.error = et4000->acl.internal.dmaj / 2;
|
||||
|
||||
et4000->acl.pattern_addr = et4000->acl.internal.pattern_addr;
|
||||
et4000->acl.source_addr = et4000->acl.internal.source_addr;
|
||||
et4000->acl.mix_addr = et4000->acl.internal.mix_addr;
|
||||
@@ -1236,12 +1291,12 @@ et4000w32_decy(et4000w32p_t *et4000)
|
||||
et4000->acl.mix_addr -= et4000->acl.internal.mix_off + 1;
|
||||
et4000->acl.dest_addr -= et4000->acl.internal.dest_off + 1;
|
||||
et4000->acl.pattern_y--;
|
||||
if (et4000->acl.pattern_y < 0 && !(et4000->acl.internal.pattern_wrap & 0x40)) {
|
||||
if ((et4000->acl.pattern_y < 0) && !(et4000->acl.internal.pattern_wrap & 0x40)) {
|
||||
et4000->acl.pattern_y = et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7] - 1;
|
||||
et4000->acl.pattern_addr = et4000->acl.pattern_back + (et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] * (et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7] - 1));
|
||||
}
|
||||
et4000->acl.source_y--;
|
||||
if (et4000->acl.source_y < 0 && !(et4000->acl.internal.source_wrap & 0x40)) {
|
||||
if ((et4000->acl.source_y < 0) && !(et4000->acl.internal.source_wrap & 0x40)) {
|
||||
et4000->acl.source_y = et4000w32_wrap_y[(et4000->acl.internal.source_wrap >> 4) & 7] - 1;
|
||||
et4000->acl.source_addr = et4000->acl.source_back + (et4000w32_wrap_x[et4000->acl.internal.source_wrap & 7] * (et4000w32_wrap_y[(et4000->acl.internal.source_wrap >> 4) & 7] - 1));
|
||||
}
|
||||
@@ -2030,99 +2085,168 @@ et4000w32_blit(int count, int cpu_input, uint32_t src_dat, uint32_t mix_dat, et4
|
||||
uint8_t out;
|
||||
int mixmap;
|
||||
|
||||
while (count-- && et4000->acl.y_count >= 0) {
|
||||
pattern = svga->vram[(et4000->acl.pattern_addr + et4000->acl.pattern_x) & et4000->vram_mask];
|
||||
if (!(et4000->acl.status & ACL_XYST) && !et4000->acl.mmu_start) {
|
||||
et4000w32_log("XY Block not started\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_input == 1) {
|
||||
source = src_dat & 0xff;
|
||||
src_dat >>= 8;
|
||||
} else /*The source data is from the display memory if the Control Routing register is not set to 1*/
|
||||
if (cpu_input == 3) {
|
||||
while (1) {
|
||||
pattern = svga->vram[(et4000->acl.pattern_addr + et4000->acl.pattern_x) & et4000->vram_mask];
|
||||
source = svga->vram[(et4000->acl.source_addr + et4000->acl.source_x) & et4000->vram_mask];
|
||||
|
||||
dest = svga->vram[et4000->acl.dest_addr & et4000->vram_mask];
|
||||
mixmap = mix_dat & 1;
|
||||
dest = svga->vram[et4000->acl.dest_addr & et4000->vram_mask];
|
||||
mixmap = mix_dat & 1;
|
||||
|
||||
/*Now determine the Raster Operation*/
|
||||
rop = mixmap ? et4000->acl.internal.rop_fg : et4000->acl.internal.rop_bg;
|
||||
mix_dat >>= 1;
|
||||
mix_dat |= 0x80000000;
|
||||
rop = mixmap ? et4000->acl.internal.rop_fg : et4000->acl.internal.rop_bg;
|
||||
mix_dat >>= 1;
|
||||
mix_dat |= 0x80000000;
|
||||
|
||||
ROPMIX(rop, dest, pattern, source, out);
|
||||
ROPMIX(rop, dest, pattern, source, out);
|
||||
|
||||
/*Write the data*/
|
||||
svga->vram[et4000->acl.dest_addr & et4000->vram_mask] = out;
|
||||
svga->changedvram[(et4000->acl.dest_addr & et4000->vram_mask) >> 12] = changeframecount;
|
||||
/*Write the data*/
|
||||
svga->vram[et4000->acl.dest_addr & et4000->vram_mask] = out;
|
||||
svga->changedvram[(et4000->acl.dest_addr & et4000->vram_mask) >> 12] = changeframecount;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 1) {
|
||||
et4000->acl.dest_addr--;
|
||||
et4000->acl.pattern_x--;
|
||||
et4000->acl.source_x--;
|
||||
if (et4000->acl.pattern_x < 0)
|
||||
et4000->acl.pattern_x += (et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] + 1);
|
||||
if (et4000->acl.source_x < 0)
|
||||
et4000->acl.source_x += (et4000w32_wrap_x[et4000->acl.internal.source_wrap & 7] + 1);
|
||||
} else {
|
||||
et4000->acl.dest_addr++;
|
||||
et4000->acl.pattern_x++;
|
||||
et4000->acl.source_x++;
|
||||
if (et4000->acl.pattern_x >= (et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] + 1))
|
||||
et4000->acl.pattern_x -= (et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] + 1);
|
||||
if (et4000->acl.source_x >= (et4000w32_wrap_x[et4000->acl.internal.source_wrap & 7] + 1))
|
||||
et4000->acl.source_x -= (et4000w32_wrap_x[et4000->acl.internal.source_wrap & 7] + 1);
|
||||
if (et4000->acl.internal.xy_dir & 1)
|
||||
et4000w32_decx(1, et4000);
|
||||
else
|
||||
et4000w32_incx(1, et4000);
|
||||
|
||||
count--;
|
||||
if (!count) {
|
||||
count = et4000->acl.cpu_x_cnt;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 2) {
|
||||
et4000w32_decy(et4000);
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back - (et4000->acl.internal.dest_off + 1);
|
||||
} else {
|
||||
et4000w32_incy(et4000);
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back + et4000->acl.internal.dest_off + 1;
|
||||
}
|
||||
|
||||
et4000->acl.pattern_x = et4000->acl.pattern_x_back;
|
||||
et4000->acl.source_x = et4000->acl.source_x_back;
|
||||
|
||||
et4000->acl.y_count--;
|
||||
if (et4000->acl.y_count == 0xffff) {
|
||||
et4000->acl.status &= ~ACL_XYST;
|
||||
if (!(et4000->acl.internal.ctrl_routing & 7) || (et4000->acl.internal.ctrl_routing & 4)) {
|
||||
et4000w32_log("W32i: end blit, xcount = %i\n", et4000->acl.x_count);
|
||||
et4000->acl.status &= ~ACL_SSO;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (cpu_input == 4) {
|
||||
while (1) {
|
||||
pattern = svga->vram[(et4000->acl.pattern_addr + et4000->acl.pattern_x) & et4000->vram_mask];
|
||||
source = svga->vram[(et4000->acl.source_addr + et4000->acl.source_x) & et4000->vram_mask];
|
||||
|
||||
et4000->acl.x_count--;
|
||||
if (et4000->acl.x_count == 0xffff) {
|
||||
et4000->acl.x_count = et4000->acl.internal.count_x;
|
||||
dest = svga->vram[et4000->acl.dest_addr & et4000->vram_mask];
|
||||
mixmap = mix_dat & 1;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 2) {
|
||||
et4000->acl.pattern_addr -= (et4000->acl.internal.pattern_off + 1);
|
||||
et4000->acl.source_addr -= (et4000->acl.internal.source_off + 1);
|
||||
et4000->acl.dest_addr -= (et4000->acl.internal.dest_off + 1);
|
||||
et4000->acl.pattern_y--;
|
||||
if ((et4000->acl.pattern_y < 0) && !(et4000->acl.internal.pattern_wrap & 0x40)) {
|
||||
et4000->acl.pattern_y = et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7] - 1;
|
||||
et4000->acl.pattern_addr = et4000->acl.pattern_back + (et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7] * (et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7] - 1));
|
||||
rop = mixmap ? et4000->acl.internal.rop_fg : et4000->acl.internal.rop_bg;
|
||||
mix_dat >>= 1;
|
||||
mix_dat |= 0x80000000;
|
||||
|
||||
ROPMIX(rop, dest, pattern, source, out);
|
||||
|
||||
/*Write the data*/
|
||||
svga->vram[et4000->acl.dest_addr & et4000->vram_mask] = out;
|
||||
svga->changedvram[(et4000->acl.dest_addr & et4000->vram_mask) >> 12] = changeframecount;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 1)
|
||||
et4000w32_decx(1, et4000);
|
||||
else
|
||||
et4000w32_incx(1, et4000);
|
||||
|
||||
et4000->acl.x_count--;
|
||||
if (et4000->acl.x_count == 0xffff) {
|
||||
et4000->acl.x_count = et4000->acl.internal.count_x;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 2) {
|
||||
et4000w32_decy(et4000);
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back - (et4000->acl.internal.dest_off + 1);
|
||||
} else {
|
||||
et4000w32_incy(et4000);
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back + et4000->acl.internal.dest_off + 1;
|
||||
}
|
||||
et4000->acl.source_y--;
|
||||
if ((et4000->acl.source_y < 0) && !(et4000->acl.internal.source_wrap & 0x40)) {
|
||||
et4000->acl.source_y = et4000w32_wrap_y[(et4000->acl.internal.source_wrap >> 4) & 7] - 1;
|
||||
et4000->acl.source_addr = et4000->acl.source_back + (et4000w32_wrap_x[et4000->acl.internal.source_wrap & 7] * (et4000w32_wrap_y[(et4000->acl.internal.source_wrap >> 4) & 7] - 1));
|
||||
|
||||
et4000->acl.pattern_x = et4000->acl.pattern_x_back;
|
||||
et4000->acl.source_x = et4000->acl.source_x_back;
|
||||
|
||||
count--;
|
||||
if (!count) {
|
||||
et4000->acl.status &= ~ACL_XYST;
|
||||
if (!(et4000->acl.internal.ctrl_routing & 7) || (et4000->acl.internal.ctrl_routing & 4)) {
|
||||
et4000w32_log("W32i: end blit, xcount = %i\n", et4000->acl.x_count);
|
||||
et4000->acl.status &= ~ACL_SSO;
|
||||
}
|
||||
return;
|
||||
}
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back - (et4000->acl.internal.dest_off + 1);
|
||||
} else {
|
||||
et4000->acl.pattern_addr += (et4000->acl.internal.pattern_off + 1);
|
||||
et4000->acl.source_addr += (et4000->acl.internal.source_off + 1);
|
||||
et4000->acl.dest_addr += (et4000->acl.internal.dest_off + 1);
|
||||
et4000->acl.pattern_y++;
|
||||
if (et4000->acl.pattern_y == et4000w32_wrap_y[(et4000->acl.internal.pattern_wrap >> 4) & 7]) {
|
||||
et4000->acl.pattern_y = 0;
|
||||
et4000->acl.pattern_addr = et4000->acl.pattern_back;
|
||||
}
|
||||
et4000->acl.source_y++;
|
||||
if (et4000->acl.source_y == et4000w32_wrap_y[(et4000->acl.internal.source_wrap >> 4) & 7]) {
|
||||
et4000->acl.source_y = 0;
|
||||
et4000->acl.source_addr = et4000->acl.source_back;
|
||||
}
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back + (et4000->acl.internal.dest_off + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (count-- && (et4000->acl.y_count >= 0)) {
|
||||
pattern = svga->vram[(et4000->acl.pattern_addr + et4000->acl.pattern_x) & et4000->vram_mask];
|
||||
|
||||
et4000->acl.pattern_x = et4000->acl.pattern_x_back;
|
||||
et4000->acl.source_x = et4000->acl.source_x_back;
|
||||
if (cpu_input == 1) {
|
||||
source = src_dat & 0xff;
|
||||
src_dat >>= 8;
|
||||
} else /*The source data is from the display memory if the Control Routing register is not set to 1*/
|
||||
source = svga->vram[(et4000->acl.source_addr + et4000->acl.source_x) & et4000->vram_mask];
|
||||
|
||||
et4000->acl.y_count--;
|
||||
if (et4000->acl.y_count == 0xffff) {
|
||||
et4000->acl.status &= ~ACL_XYST;
|
||||
if (!(et4000->acl.internal.ctrl_routing & 7) || (et4000->acl.internal.ctrl_routing & 4)) {
|
||||
et4000w32_log("W32i: end blit, xcount = %i\n", et4000->acl.x_count);
|
||||
et4000->acl.status &= ~ACL_SSO;
|
||||
dest = svga->vram[et4000->acl.dest_addr & et4000->vram_mask];
|
||||
mixmap = mix_dat & 1;
|
||||
|
||||
/*Now determine the Raster Operation*/
|
||||
rop = mixmap ? et4000->acl.internal.rop_fg : et4000->acl.internal.rop_bg;
|
||||
mix_dat >>= 1;
|
||||
mix_dat |= 0x80000000;
|
||||
|
||||
ROPMIX(rop, dest, pattern, source, out);
|
||||
|
||||
/*Write the data*/
|
||||
svga->vram[et4000->acl.dest_addr & et4000->vram_mask] = out;
|
||||
svga->changedvram[(et4000->acl.dest_addr & et4000->vram_mask) >> 12] = changeframecount;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 1)
|
||||
et4000w32_decx(1, et4000);
|
||||
else
|
||||
et4000w32_incx(1, et4000);
|
||||
|
||||
et4000->acl.x_count--;
|
||||
if (et4000->acl.x_count == 0xffff) {
|
||||
et4000->acl.x_count = et4000->acl.internal.count_x;
|
||||
|
||||
if (et4000->acl.internal.xy_dir & 2) {
|
||||
et4000w32_decy(et4000);
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back - (et4000->acl.internal.dest_off + 1);
|
||||
} else {
|
||||
et4000w32_incy(et4000);
|
||||
et4000->acl.dest_back = et4000->acl.dest_addr = et4000->acl.dest_back + et4000->acl.internal.dest_off + 1;
|
||||
}
|
||||
et4000->acl.cpu_input_num = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_input)
|
||||
return;
|
||||
et4000->acl.pattern_x = et4000->acl.pattern_x_back;
|
||||
et4000->acl.source_x = et4000->acl.source_x_back;
|
||||
|
||||
et4000->acl.y_count--;
|
||||
if (et4000->acl.y_count == 0xffff) {
|
||||
et4000->acl.status &= ~ACL_XYST;
|
||||
if (!(et4000->acl.internal.ctrl_routing & 7) || (et4000->acl.internal.ctrl_routing & 4)) {
|
||||
et4000w32_log("W32i: end blit, xcount = %i\n", et4000->acl.x_count);
|
||||
et4000->acl.status &= ~ACL_SSO;
|
||||
}
|
||||
et4000->acl.cpu_input_num = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_input) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2334,7 +2458,7 @@ et4000w32p_hwcursor_draw(svga_t *svga, int displine)
|
||||
uint8_t dat;
|
||||
offset = svga->hwcursor_latch.xoff;
|
||||
|
||||
if (et4000->type == ET4000W32) {
|
||||
if ((et4000->type == ET4000W32) && (pitch == 32)) {
|
||||
switch (svga->bpp) {
|
||||
case 8:
|
||||
minus_width = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user