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

This commit is contained in:
Jasmine Iwanek
2023-04-17 23:48:04 -04:00
127 changed files with 9494 additions and 7111 deletions

View File

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

15
.ci/Jenkinsfile vendored
View File

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

View File

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

View File

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

View File

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

2
.gitignore vendored
View File

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

View File

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

View File

@@ -8,8 +8,6 @@
*
* Main emulator module where most things are controlled.
*
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
@@ -1265,9 +1263,9 @@ pc_run(void)
startblit();
cpu_exec(cpu_s->rspeed / 100);
#ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */
if (gdbstub_step == GDBSTUB_EXEC)
// if (gdbstub_step == GDBSTUB_EXEC)
#endif
mouse_process();
// mouse_process();
joystick_process();
endblit();

View File

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

View File

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

View File

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

View File

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

View File

@@ -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))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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*/

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,8 @@
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/gdbstub.h>
#include <86box/mouse.h>
typedef struct {
@@ -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. */

View File

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

View File

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

View File

@@ -18,6 +18,7 @@
* Copyright 2016-2020 Miran Grca.
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,52 @@
#ifndef EMU_KEYBOARD_H
#define EMU_KEYBOARD_H
enum {
DEV_KBD = 0,
DEV_AUX
};
/* Used by the AT / PS/2 keyboard controller, common device, keyboard, and mouse. */
typedef struct {
uint8_t wantcmd, dat, pad, pad0;
int out_new;
void *priv;
void (*poll)(void *priv);
} kbc_port_t;
/* Used by the AT / PS/2 common device, keyboard, and mouse. */
typedef struct {
const char *name; /* name of this device */
uint8_t type, inst, command, wantdata,
last_scan_code, state, resolution, rate,
cmd_queue_start, cmd_queue_end, queue_start, queue_end;
/* 6 bytes needed for mouse */
uint8_t last_data[6];
uint16_t flags;
/* Internal FIFO, not present on real devices, needed for commands that
output multiple bytes. */
uint8_t cmd_queue[16];
uint8_t queue[16];
int mode,
x, y, z, b;
int *scan;
void (*process_cmd)(void *priv);
void (*execute_bat)(void *priv);
kbc_port_t *port;
} atkbc_dev_t;
typedef struct {
const uint8_t mk[4];
const uint8_t brk[4];
@@ -139,8 +185,11 @@ extern uint8_t keyboard_set3_flags[512];
extern uint8_t keyboard_set3_all_repeat;
extern uint8_t keyboard_set3_all_break;
extern int mouse_queue_start, mouse_queue_end;
extern int mouse_cmd_queue_start, mouse_cmd_queue_end;
extern int mouse_scan;
extern kbc_port_t *kbc_ports[2];
#ifdef EMU_DEVICE_H
extern const device_t keyboard_pc_device;
extern const device_t keyboard_pc82_device;
@@ -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
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -220,7 +220,7 @@ machine_at_spc6000a_init(const machine_t *model)
if (fdc_type == FDC_INTERNAL)
device_add(&fdc_at_device);
device_add(&keyboard_at_samsung_device);
device_add(&keyboard_at_ami_device);
return ret;
}
@@ -404,7 +404,7 @@ machine_at_acerv10_init(const machine_t *model)
machine_at_common_init(model);
device_add(&sis_85c461_device);
device_add(&keyboard_ps2_ami_pci_device);
device_add(&keyboard_ps2_acer_pci_device);
device_add(&ide_isa_2ch_device);
if (fdc_type == FDC_INTERNAL)
@@ -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;
}

View File

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

View File

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

View File

@@ -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,

View File

@@ -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;

View File

@@ -51,7 +51,8 @@ static pc_timer_t pic_timer;
static int shadow = 0, elcr_enabled = 0,
tmr_inited = 0, latched = 0,
pic_pci = 0;
pic_pci = 0, kbd_latch = 0,
mouse_latch = 0;
static uint16_t smi_irq_mask = 0x0000,
smi_irq_status = 0x0000;
@@ -284,6 +285,12 @@ pic_set_shadow(int sh)
shadow = sh;
}
int
pic_get_pci_flag(void)
{
return pic_pci;
}
void
pic_set_pci_flag(int pci)
{
@@ -383,6 +390,23 @@ pic_command(pic_t *dev)
dev->auto_eoi_rotate = !!(dev->ocw2 & 0x80);
}
uint8_t
pic_latch_read(uint16_t addr, void *priv)
{
uint8_t ret = 0xff;
pic_log("pic_latch_read(%i, %i): %02X%02X\n", kbd_latch, mouse_latch, pic2.lines & 0x10, pic.lines & 0x02);
if (kbd_latch && (pic.lines & 0x02))
picintc(0x0002);
if (mouse_latch && (pic2.lines & 0x10))
picintc(0x1000);
/* Return FF - we just lower IRQ 1 and IRQ 12. */
return ret;
}
uint8_t
pic_read(uint16_t addr, void *priv)
{
@@ -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);
}
}

View File

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

View File

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

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

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

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

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

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

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

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

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

View File

@@ -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 "磁碟映像太大"

View File

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

View File

@@ -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
}

View File

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

View File

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

View File

@@ -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());

View File

@@ -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)

View File

@@ -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

View 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;
}

View File

@@ -0,0 +1,20 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Definitions for 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);

View 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, &registry_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);
}

View 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();

View 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);
}

View 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();

View File

@@ -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));

View File

@@ -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()

View File

@@ -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);
}

View File

@@ -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]);
}

View File

@@ -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
};

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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