diff --git a/bumpversion.sh b/bumpversion.sh index 87728603c..4681e72be 100644 --- a/bumpversion.sh +++ b/bumpversion.sh @@ -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. diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 402021513..31da38b48 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -1067,7 +1067,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv) break; case 0x44: - if (dev->local <= VIA_PIPC_586B) + if (dev->local < VIA_PIPC_586B) pic_mouse_latch(val & 0x01); break; diff --git a/src/device/keyboard.c b/src/device/keyboard.c index 197281992..cc6469f9d 100644 --- a/src/device/keyboard.c +++ b/src/device/keyboard.c @@ -300,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? */ diff --git a/src/device/keyboard_at.c b/src/device/keyboard_at.c index 5cc5f012a..a0c2f3504 100644 --- a/src/device/keyboard_at.c +++ b/src/device/keyboard_at.c @@ -55,8 +55,6 @@ #define STAT_IFULL 0x02 #define STAT_OFULL 0x01 -#define RESET_DELAY_TIME (100 * 10) /* 600ms */ - #define CCB_UNUSED 0x80 #define CCB_TRANSLATE 0x40 #define CCB_PCMODE 0x20 @@ -108,15 +106,12 @@ enum { #define KBC_STATE_SCAN_MOUSE KBC_STATE_MOUSE enum { - DEV_STATE_RESET = 0, - DEV_STATE_MAIN_1, + DEV_STATE_MAIN_1 = 0, DEV_STATE_MAIN_2, DEV_STATE_MAIN_CMD, DEV_STATE_MAIN_OUT, DEV_STATE_MAIN_WANT_IN, - DEV_STATE_MAIN_IN, - DEV_STATE_MAIN_WANT_RESET, - DEV_STATE_RESET_OUT + DEV_STATE_MAIN_IN }; typedef struct { @@ -155,10 +150,10 @@ typedef struct { uint8_t mouse_queue[16]; /* Keyboard. */ - int out_new, reset_delay; + int out_new; /* Mouse. */ - int out_new_mouse, mouse_reset_delay; + int out_new_mouse; /* Controller. */ uint32_t flags; @@ -180,8 +175,8 @@ uint8_t keyboard_set3_all_repeat; uint8_t keyboard_set3_all_break; /* Global keyboard mode: - Bits 0 - 1 = scan code set, bit 6 = translate or not. */ -uint8_t keyboard_mode = 0x42; + Bits 0 - 1 = scan code set. */ +uint8_t keyboard_mode = 0x02; static void (*mouse_write)(uint8_t val, void *priv) = NULL; static void *mouse_p = NULL; @@ -223,7 +218,6 @@ static const uint8_t nont_to_t[256] = { 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; -#ifdef USE_SET1 static const scancode scancode_set1[512] = { // clang-format off { { 0},{ 0} }, { { 0x01,0},{ 0x81,0} }, { { 0x02,0},{ 0x82,0} }, { { 0x03,0},{ 0x83,0} }, /*000*/ @@ -355,7 +349,6 @@ static const scancode scancode_set1[512] = { { { 0},{ 0} }, { { 0},{ 0} }, { {0xe0,0xfe,0},{ 0} }, { {0xe0,0xff,0},{ 0} } /*1fc*/ // clang-format on }; -#endif static const scancode scancode_set2[512] = { // clang-format off @@ -643,15 +636,11 @@ kbd_log(const char *fmt, ...) static void set_scancode_map(atkbd_t *dev) { - switch (keyboard_mode & 3) { -#ifdef USE_SET1 + switch (keyboard_mode) { case 1: default: keyboard_set_table(scancode_set1); break; -#else - default: -#endif case 2: keyboard_set_table(scancode_set2); break; @@ -660,13 +649,6 @@ set_scancode_map(atkbd_t *dev) keyboard_set_table(scancode_set3); break; } - - if (keyboard_mode & 0x20) -#ifdef USE_SET1 - keyboard_set_table(scancode_set1); -#else - keyboard_set_table(scancode_set2); -#endif } static void @@ -734,14 +716,11 @@ kbc_queue_add(atkbd_t *dev, uint8_t val, uint8_t channel) static int kbc_translate(atkbd_t *dev, uint8_t val) { - int xt_mode = (keyboard_mode & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF); - int translate = (keyboard_mode & 0x40); + int xt_mode = (dev->mem[0x20] & 0x20) && ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF); + int translate = (dev->mem[0x20] & 0x40) || xt_mode || ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2); uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; int ret = - 1; - translate = translate || (keyboard_mode & 0x40) || xt_mode; - translate = translate || ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2); - /* Allow for scan code translation. */ if (translate && (val == 0xf0)) { kbd_log("ATkbd: translate is on, F0 prefix detected\n"); @@ -867,8 +846,8 @@ add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_ static void add_data_kbd_cmd_queue(atkbd_t *dev, uint8_t val) { - if ((dev->reset_delay > 0) || (dev->key_cmd_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", (dev->reset_delay > 0), (dev->key_cmd_queue_end >= 16)); + if (dev->key_cmd_queue_end >= 16) { + kbd_log("ATkbc: Unable to add to queue, dev->key_cmd_queue_end >= 16\n"); return; } kbd_log("ATkbc: dev->key_cmd_queue[%02X] = %02X;\n", dev->key_cmd_queue_end, val); @@ -879,8 +858,8 @@ add_data_kbd_cmd_queue(atkbd_t *dev, uint8_t val) static void add_data_kbd_queue(atkbd_t *dev, uint8_t val) { - if (!keyboard_scan || (dev->reset_delay > 0) || (dev->key_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i, %i\n", !keyboard_scan, (dev->reset_delay > 0), (dev->key_queue_end >= 16)); + if (!keyboard_scan || (dev->key_queue_end >= 16)) { + kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", !keyboard_scan, (dev->key_queue_end >= 16)); return; } kbd_log("ATkbc: key_queue[%02X] = %02X;\n", dev->key_queue_end, val); @@ -891,9 +870,6 @@ add_data_kbd_queue(atkbd_t *dev, uint8_t val) static void add_data_kbd_front(atkbd_t *dev, uint8_t val) { - if (dev->reset_delay) - return; - add_data_kbd_cmd_queue(dev, val); } @@ -1082,7 +1058,6 @@ kbc_poll_ps2(atkbd_t *dev) { switch (dev->kbc_state) { case KBC_STATE_RESET: - // pclog("KBC_STATE_RESET\n"); if (dev->status & STAT_IFULL) { dev->status = ((dev->status & 0x0f) | 0x10) & ~STAT_IFULL; if ((dev->status & STAT_CD) && (dev->ib == 0xaa)) @@ -1091,7 +1066,6 @@ kbc_poll_ps2(atkbd_t *dev) break; case KBC_STATE_MAIN_IBF: default: - // pclog("KBC_STATE_MAIN_IBF\n"); if (dev->status & STAT_IFULL) kbc_ibf_process(dev); else if (!(dev->status & STAT_OFULL)) { @@ -1112,7 +1086,6 @@ kbc_poll_ps2(atkbd_t *dev) } break; case KBC_STATE_MAIN_KBD: - // pclog("KBC_STATE_MAIN_KBD\n"); if (dev->status & STAT_IFULL) kbc_ibf_process(dev); else { @@ -1121,7 +1094,6 @@ kbc_poll_ps2(atkbd_t *dev) } break; case KBC_STATE_MAIN_MOUSE: - // pclog("KBC_STATE_MAIN_MOUSE\n"); if (dev->status & STAT_IFULL) kbc_ibf_process(dev); else { @@ -1130,14 +1102,12 @@ kbc_poll_ps2(atkbd_t *dev) } break; case KBC_STATE_MAIN_BOTH: - // pclog("KBC_STATE_MAIN_BOTH\n"); if (kbc_scan_kbd_ps2(dev)) dev->kbc_state = KBC_STATE_MAIN_IBF; else dev->kbc_state = KBC_STATE_MAIN_MOUSE; break; case KBC_STATE_KBC_OUT: - // pclog("KBC_STATE_KBC_OUT\n"); /* Keyboard controller command want to output multiple bytes. */ if (dev->status & STAT_IFULL) { /* Data from host aborts dumping. */ @@ -1154,7 +1124,6 @@ kbc_poll_ps2(atkbd_t *dev) } break; case KBC_STATE_KBC_PARAM: - // pclog("KBC_STATE_KBC_PARAM\n"); /* Keyboard controller command wants data, wait for said data. */ if (dev->status & STAT_IFULL) { /* Command written, abort current command. */ @@ -1170,7 +1139,6 @@ kbc_poll_ps2(atkbd_t *dev) dev->kbc_state = KBC_STATE_SCAN_KBD; break; case KBC_STATE_SCAN_KBD: - // pclog("KBC_STATE_SCAN_KBD\n"); (void) kbc_scan_kbd_ps2(dev); break; case KBC_STATE_SEND_MOUSE: @@ -1178,7 +1146,6 @@ kbc_poll_ps2(atkbd_t *dev) dev->kbc_state = KBC_STATE_SCAN_MOUSE; break; case KBC_STATE_SCAN_MOUSE: - // pclog("KBC_STATE_SCAN_MOUSE\n"); (void) kbc_scan_aux_ps2(dev); break; } @@ -1188,21 +1155,10 @@ static void kbc_poll_kbd(atkbd_t *dev) { switch (dev->kbd_state) { - case DEV_STATE_RESET: - /* Reset state. */ - if (dev->reset_delay) { - dev->reset_delay--; - if (!dev->reset_delay) { - kbd_log("ATkbc: Sending AA on keyboard reset...\n"); - add_data_kbd_front(dev, 0xaa); - dev->kbd_state = DEV_STATE_RESET_OUT; - } - } - break; case DEV_STATE_MAIN_1: /* Process the command if needed and then return to main loop #2. */ if (dev->key_wantcmd) { - kbd_log("ATkbc: Processing keyboard command...\n"); + kbd_log("ATkbc: Processing keyboard command %02X...\n", dev->key_dat); kbc_queue_reset(dev, 4); // dev->out_new = -1; kbd_process_cmd(dev); @@ -1221,7 +1177,6 @@ kbc_poll_kbd(atkbd_t *dev) dev->kbd_state = DEV_STATE_MAIN_1; break; case DEV_STATE_MAIN_OUT: - case DEV_STATE_RESET_OUT: /* Output command response and then return to main loop #2. */ if ((dev->out_new == -1) && (dev->key_cmd_queue_start != dev->key_cmd_queue_end)) { kbd_log("ATkbc: %02X (CMD ) on channel 1\n", dev->key_cmd_queue[dev->key_cmd_queue_start]); @@ -1229,7 +1184,7 @@ kbc_poll_kbd(atkbd_t *dev) dev->key_cmd_queue_start = (dev->key_cmd_queue_start + 1) & 0xf; } if (dev->key_cmd_queue_start == dev->key_cmd_queue_end) - dev->kbd_state = (dev->kbd_state == DEV_STATE_RESET_OUT) ? DEV_STATE_MAIN_1 : DEV_STATE_MAIN_2; + dev->kbd_state = DEV_STATE_MAIN_2; break; case DEV_STATE_MAIN_WANT_IN: /* Output command response and then wait for host data. */ @@ -1244,23 +1199,13 @@ kbc_poll_kbd(atkbd_t *dev) case DEV_STATE_MAIN_IN: /* Wait for host data. */ if (dev->key_wantcmd) { - kbd_log("ATkbc: Processing keyboard command...\n"); + kbd_log("ATkbc: Processing keyboard command %02X parameter %02X...\n", dev->key_command, dev->key_dat); kbc_queue_reset(dev, 4); // dev->out_new = -1; kbd_process_cmd(dev); dev->key_wantcmd = 0; } break; - case DEV_STATE_MAIN_WANT_RESET: - /* Output command response and then go to the reset state. */ - if ((dev->out_new == -1) && (dev->key_cmd_queue_start != dev->key_cmd_queue_end)) { - kbd_log("ATkbc: %02X (CMD ) on channel 1\n", dev->key_cmd_queue[dev->key_cmd_queue_start]); - dev->out_new = dev->key_cmd_queue[dev->key_cmd_queue_start]; - dev->key_cmd_queue_start = (dev->key_cmd_queue_start + 1) & 0xf; - } - if (dev->key_cmd_queue_start == dev->key_cmd_queue_end) - dev->kbd_state = DEV_STATE_RESET; - break; } } @@ -1268,24 +1213,10 @@ static void kbc_poll_aux(atkbd_t *dev) { switch (dev->mouse_state) { -#if 0 - case DEV_STATE_RESET: - /* Reset state. */ - if (dev->mouse_reset_delay) { - dev->mouse_reset_delay--; - if (!dev->mouse_reset_delay) { - kbd_log("ATkbc: Sending AA 00 on mouse reset...\n"); - keyboard_at_adddata_mouse_cmd(0xaa); - keyboard_at_adddata_mouse_cmd(0x00); - dev->mouse_state = DEV_STATE_RESET_OUT; - } - } - break; -#endif case DEV_STATE_MAIN_1: /* Process the command if needed and then return to main loop #2. */ if (dev->mouse_wantcmd) { - kbd_log("ATkbc: Processing mouse command...\n"); + kbd_log("ATkbc: Processing mouse command %02X...\n", dev->mouse_dat); kbc_queue_reset(dev, 3); // dev->out_new_mouse = -1; dev->mouse_state = DEV_STATE_MAIN_OUT; @@ -1307,7 +1238,6 @@ kbc_poll_aux(atkbd_t *dev) dev->mouse_state = DEV_STATE_MAIN_1; break; case DEV_STATE_MAIN_OUT: - case DEV_STATE_RESET_OUT: /* Output command response and then return to main loop #2. */ if ((dev->out_new_mouse == -1) && (dev->mouse_cmd_queue_start != dev->mouse_cmd_queue_end)) { kbd_log("ATkbc: %02X (CMD ) on channel 2\n", dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]); @@ -1315,7 +1245,7 @@ kbc_poll_aux(atkbd_t *dev) dev->mouse_cmd_queue_start = (dev->mouse_cmd_queue_start + 1) & 0xf; } if (dev->mouse_cmd_queue_start == dev->mouse_cmd_queue_end) - dev->mouse_state = (dev->mouse_state == DEV_STATE_RESET_OUT) ? DEV_STATE_MAIN_1 : DEV_STATE_MAIN_2; + dev->mouse_state = DEV_STATE_MAIN_2; break; case DEV_STATE_MAIN_WANT_IN: /* Output command response and then wait for host data. */ @@ -1330,7 +1260,7 @@ kbc_poll_aux(atkbd_t *dev) case DEV_STATE_MAIN_IN: /* Wait for host data. */ if (dev->mouse_wantcmd) { - kbd_log("ATkbc: Processing mouse command...\n"); + kbd_log("ATkbc: Processing mouse command parameter %02X...\n", dev->mouse_dat); kbc_queue_reset(dev, 3); // dev->out_new_mouse = -1; dev->mouse_state = DEV_STATE_MAIN_OUT; @@ -1338,16 +1268,6 @@ kbc_poll_aux(atkbd_t *dev) dev->mouse_wantcmd = 0; } break; - case DEV_STATE_MAIN_WANT_RESET: - /* Output command response and then go to the reset state. */ - if ((dev->out_new_mouse == -1) && (dev->mouse_cmd_queue_start != dev->mouse_cmd_queue_end)) { - kbd_log("ATkbc: %02X (CMD ) on channel 2\n", dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]); - dev->out_new_mouse = dev->mouse_cmd_queue[dev->mouse_cmd_queue_start]; - dev->mouse_cmd_queue_start = (dev->mouse_cmd_queue_start + 1) & 0xf; - } - if (dev->mouse_cmd_queue_start == dev->mouse_cmd_queue_end) - dev->mouse_state = DEV_STATE_RESET; - break; } } @@ -1377,9 +1297,6 @@ add_data_vals(atkbd_t *dev, uint8_t *val, uint8_t len) { int i; - if (dev->reset_delay) - return; - for (i = 0; i < len; i++) add_data_kbd_queue(dev, val[i]); } @@ -1391,9 +1308,6 @@ add_data_kbd(uint16_t val) uint8_t fake_shift[4]; uint8_t num_lock = 0, shift_states = 0; - if (dev->reset_delay) - return; - keyboard_get_states(NULL, &num_lock, NULL); shift_states = keyboard_get_shift() & STATE_SHIFT_MASK; @@ -1557,6 +1471,7 @@ write_output(atkbd_t *dev, uint8_t val) uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; +#if 0 /* PS/2: Handle IRQ's. */ if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) { /* IRQ 12 */ @@ -1565,6 +1480,7 @@ write_output(atkbd_t *dev, uint8_t val) /* IRQ 1 */ picint_common(1 << 1, 0, val & 0x10); } +#endif /* AT, PS/2: Handle A20. */ if ((old ^ val) & 0x02) { /* A20 enable change */ @@ -1621,7 +1537,6 @@ write_output_fast_a20(atkbd_t *dev, uint8_t val) static void write_cmd(atkbd_t *dev, uint8_t val) { - uint8_t kbc_ven = dev->flags & KBC_VEN_MASK; kbd_log("ATkbc: write command byte: %02X (old: %02X)\n", val, dev->mem[0x20]); /* PS/2 type 2 keyboard controllers always force the XLAT bit to 0. */ @@ -1633,22 +1548,8 @@ write_cmd(atkbd_t *dev, uint8_t val) dev->mem[0x2e] = 0x01; } - /* Scan code translate ON/OFF. */ - keyboard_mode &= 0x93; - keyboard_mode |= (val & MODE_MASK); - kbd_log("ATkbc: keyboard interrupt is now %s\n", (val & 0x01) ? "enabled" : "disabled"); - /* ISA AT keyboard controllers use bit 5 for keyboard mode (1 = PC/XT, 2 = AT); - PS/2 (and EISA/PCI) keyboard controllers use it as the PS/2 mouse enable switch. - The AMIKEY firmware apparently uses this bit for something else. */ - if ((kbc_ven == KBC_VEN_AMI) || (kbc_ven == KBC_VEN_TG) || - (kbc_ven == KBC_VEN_TG_GREEN) || ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)) { - keyboard_mode &= ~CCB_PCMODE; - - kbd_log("ATkbc: mouse interrupt is now %s\n", (val & 0x02) ? "enabled" : "disabled"); - } - if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF) { /* Update the output port to mirror the IBF and OBF bits, if active. */ write_output(dev, (dev->output_port & 0x0f) | ((val & 0x03) << 4) | ((val & 0x20) ? 0xc0 : 0x00)); @@ -2265,22 +2166,20 @@ kbd_key_reset(atkbd_t *dev, int do_fa) dev->kbd_last_scan_code = 0x00; /* Set scan code set to 2. */ - keyboard_mode = (keyboard_mode & 0xfc) | 0x02; + keyboard_mode = 0x02; set_scancode_map(dev); keyboard_scan = 1; dev->sc_or = 0; - if (do_fa) + if (do_fa) { add_data_kbd_front(dev, 0xfa); + add_data_kbd_front(dev, 0xaa); + } - dev->reset_delay = RESET_DELAY_TIME; - - if (do_fa) - dev->kbd_state = DEV_STATE_MAIN_WANT_RESET; - else - dev->kbd_state = DEV_STATE_RESET; + if (!do_fa) + dev->kbd_state = DEV_STATE_MAIN_1; } static void @@ -2334,13 +2233,12 @@ kbd_process_cmd(void *priv) case 0xf0: /* get/set scancode set */ add_data_kbd_front(dev, 0xfa); if (dev->key_dat == 0) { - kbd_log("Get scan code set: %02X\n", keyboard_mode & 3); - add_data_kbd_front(dev, keyboard_mode & 3); + kbd_log("Get scan code set: %02X\n", keyboard_mode); + add_data_kbd_front(dev, keyboard_mode); } else { - if ((dev->key_dat <= 3) && (dev->key_dat != 1)) { - keyboard_mode &= 0xfc; - keyboard_mode |= (dev->key_dat & 3); - kbd_log("Scan code set now: %02X\n", dev->key_dat); + if (dev->key_dat <= 3) { + keyboard_mode = dev->key_dat; + kbd_log("Scan code set now: %02X\n", keyboard_mode); } set_scancode_map(dev); } @@ -2358,7 +2256,6 @@ kbd_process_cmd(void *priv) /* Keyboard command is now done. */ dev->key_command = 0x00; - /* Do not process command if the existing command is outputting bytes. */ } else { /* No keyboard command in progress. */ dev->key_command = 0x00; @@ -2426,7 +2323,7 @@ kbd_process_cmd(void *priv) keyboard_set3_all_break = 0; keyboard_set3_all_repeat = 0; memset(keyboard_set3_flags, 0, 512); - keyboard_mode = (keyboard_mode & 0xfc) | 0x02; + keyboard_mode = 0x02; set_scancode_map(dev); break; @@ -2634,7 +2531,7 @@ kbc_process_cmd(void *priv) case 0xdd: /* disable A20 address line */ case 0xdf: /* enable A20 address line */ kbd_log("ATkbc: %sable A20\n", (dev->ib == 0xdd) ? "dis" : "en"); - write_output(dev, (dev->output_port & 0xfd) | (dev->ib & 0x02)); + write_output_fast_a20(dev, (dev->output_port & 0xfd) | (dev->ib & 0x02)); break; case 0xe0: /* read test inputs */ @@ -2837,8 +2734,6 @@ kbd_reset(void *priv) dev->input_port = video_is_mda() ? 0xf0 : 0xb0; kbd_log("ATkbc: input port = %02x\n", dev->input_port); - keyboard_mode = 0x02 | (dev->mem[0x20] & CCB_TRANSLATE); - /* Enable keyboard, disable mouse. */ set_enable_kbd(dev, 1); keyboard_scan = 1; @@ -2852,6 +2747,8 @@ kbd_reset(void *priv) dev->sc_or = 0; + keyboard_mode = 0x02; + memset(keyboard_set3_flags, 0, 512); set_scancode_map(dev); @@ -2869,8 +2766,6 @@ kbd_reset(void *priv) /* Stage 1. */ dev->status = (dev->status & 0x0f) | (dev->input_port & 0xf0); - /* Wait for command AA. */ - dev->kbc_state = KBC_STATE_RESET; /* Reset the keyboard. */ kbd_key_reset(dev, 0); @@ -3293,8 +3188,8 @@ keyboard_at_adddata_mouse(uint8_t val) { atkbd_t *dev = SavedKbd; - if (!mouse_scan || (dev->mouse_reset_delay > 0) || (dev->mouse_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i, %i\n", !mouse_scan, (dev->mouse_reset_delay > 0), (dev->mouse_queue_end >= 16)); + if (!mouse_scan || (dev->mouse_queue_end >= 16)) { + kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", !mouse_scan, (dev->mouse_queue_end >= 16)); return; } kbc_queue_add(dev, val, 2); @@ -3305,8 +3200,8 @@ keyboard_at_adddata_mouse_cmd(uint8_t val) { atkbd_t *dev = SavedKbd; - if ((dev->mouse_reset_delay > 0) || (dev->mouse_cmd_queue_end >= 16)) { - kbd_log("ATkbc: Unable to add to queue, conditions: %i, %i\n", (dev->mouse_reset_delay > 0), (dev->mouse_cmd_queue_end >= 16)); + if (dev->mouse_cmd_queue_end >= 16) { + kbd_log("ATkbc: Unable to add to queue, dev->mouse_cmd_queue_end >= 16\n"); return; } kbc_queue_add(dev, val, 3); diff --git a/src/disk/hdc_esdi_mca.c b/src/disk/hdc_esdi_mca.c index f9af1e864..47fcdb1b7 100644 --- a/src/disk/hdc_esdi_mca.c +++ b/src/disk/hdc_esdi_mca.c @@ -62,6 +62,7 @@ */ #include +#include #include #include #include diff --git a/src/disk/hdc_ide.c b/src/disk/hdc_ide.c index d4b44a13e..cad5ce111 100644 --- a/src/disk/hdc_ide.c +++ b/src/disk/hdc_ide.c @@ -18,6 +18,7 @@ * Copyright 2016-2020 Miran Grca. */ #include +#include #include #include #include diff --git a/src/disk/hdd_image.c b/src/disk/hdd_image.c index ba7cf18ba..4007f13f3 100644 --- a/src/disk/hdd_image.c +++ b/src/disk/hdd_image.c @@ -18,6 +18,7 @@ */ #define _GNU_SOURCE #include +#include #include #include #include @@ -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 diff --git a/src/disk/minivhd/CMakeLists.txt b/src/disk/minivhd/CMakeLists.txt index ec6caff81..324acf81b 100644 --- a/src/disk/minivhd/CMakeLists.txt +++ b/src/disk/minivhd/CMakeLists.txt @@ -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) diff --git a/src/disk/minivhd/CREDITS.md b/src/disk/minivhd/CREDITS.md index c494d4e43..266e9f0e4 100644 --- a/src/disk/minivhd/CREDITS.md +++ b/src/disk/minivhd/CREDITS.md @@ -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. diff --git a/src/disk/minivhd/LICENSE b/src/disk/minivhd/LICENSE index af7299185..2997be44a 100644 --- a/src/disk/minivhd/LICENSE +++ b/src/disk/minivhd/LICENSE @@ -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. \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_convert.c b/src/disk/minivhd/convert.c similarity index 57% rename from src/disk/minivhd/minivhd_convert.c rename to src/disk/minivhd/convert.c index 3ae1d084f..1b7d10c87 100644 --- a/src/disk/minivhd/minivhd_convert.c +++ b/src/disk/minivhd/convert.c @@ -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, + * Fred N. van Kempen, + * + * 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 #include #include #include #include -#include "minivhd_create.h" -#include "minivhd_internal.h" -#include "minivhd_util.h" +#include #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; } diff --git a/src/disk/minivhd/minivhd_create.c b/src/disk/minivhd/create.c similarity index 71% rename from src/disk/minivhd/minivhd_create.c rename to src/disk/minivhd/create.c index b56437c28..ebfbb69a2 100644 --- a/src/disk/minivhd/minivhd_create.c +++ b/src/disk/minivhd/create.c @@ -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, + * Fred N. van Kempen, + * + * 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 +#include #include #include #include #include -#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 #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; +} diff --git a/src/disk/minivhd/cwalk.c b/src/disk/minivhd/cwalk.c index f0c48427c..02964af25 100644 --- a/src/disk/minivhd/cwalk.c +++ b/src/disk/minivhd/cwalk.c @@ -1,12 +1,49 @@ +/* + * libCWALK Path library for C/C++ + * + * Version: @(#)cwalk.c 1.0.2 2021/03/16 + * + * Authors: Sherman Perry, + * Leonard Ikl, + * + * 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 #include +#include #include #include #include #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. diff --git a/src/disk/minivhd/cwalk.h b/src/disk/minivhd/cwalk.h index baa5d432d..380f6fa2c 100644 --- a/src/disk/minivhd/cwalk.h +++ b/src/disk/minivhd/cwalk.h @@ -1,10 +1,40 @@ -#pragma once - +/* + * libCWALK path library for C/C++ + * + * Version: @(#)cwalk.h 1.0.3 2021/03/22 + * + * Authors: Sherman Perry, + * Leonard Ikl, + * + * 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 -#include /** * 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*/ diff --git a/src/disk/minivhd/internal.h b/src/disk/minivhd/internal.h new file mode 100644 index 000000000..d3f930110 --- /dev/null +++ b/src/disk/minivhd/internal.h @@ -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, + * + * 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*/ diff --git a/src/disk/minivhd/libxml2_encoding.h b/src/disk/minivhd/libxml2_encoding.h deleted file mode 100644 index d86770b86..000000000 --- a/src/disk/minivhd/libxml2_encoding.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef LIBXML2_ENCODING_H -#define LIBXML2_ENCODING_H - -#include -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 diff --git a/src/disk/minivhd/minivhd_manage.c b/src/disk/minivhd/manage.c similarity index 71% rename from src/disk/minivhd/minivhd_manage.c rename to src/disk/minivhd/manage.c index ce0f31f60..053acc40c 100644 --- a/src/disk/minivhd/minivhd_manage.c +++ b/src/disk/minivhd/manage.c @@ -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, + * Fred N. van Kempen, + * + * 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 #include #include #include #include -#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 #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; +} diff --git a/src/disk/minivhd/minivhd.h b/src/disk/minivhd/minivhd.h index df3a24bb3..929ea5b27 100644 --- a/src/disk/minivhd/minivhd.h +++ b/src/disk/minivhd/minivhd.h @@ -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, + * Fred N. van Kempen, + * + * 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 -#include -#include - -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*/ diff --git a/src/disk/minivhd/minivhd_create.h b/src/disk/minivhd/minivhd_create.h deleted file mode 100644 index 203834a71..000000000 --- a/src/disk/minivhd/minivhd_create.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef MINIVHD_CREATE_H -#define MINIVHD_CREATE_H -#include -#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 diff --git a/src/disk/minivhd/minivhd_internal.h b/src/disk/minivhd/minivhd_internal.h deleted file mode 100644 index 54b304830..000000000 --- a/src/disk/minivhd/minivhd_internal.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef MINIVHD_INTERNAL_H -#define MINIVHD_INTERNAL_H -#include -#include -#include - -#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 diff --git a/src/disk/minivhd/minivhd_io.c b/src/disk/minivhd/minivhd_io.c index 63017bbf8..ff86a8337 100644 --- a/src/disk/minivhd/minivhd_io.c +++ b/src/disk/minivhd/minivhd_io.c @@ -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, + * + * 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 +#include #include +#include #include -#include "minivhd_internal.h" -#include "minivhd_util.h" +#include +#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; } diff --git a/src/disk/minivhd/minivhd_io.h b/src/disk/minivhd/minivhd_io.h deleted file mode 100644 index 7ffd10f49..000000000 --- a/src/disk/minivhd/minivhd_io.h +++ /dev/null @@ -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 diff --git a/src/disk/minivhd/minivhd_struct_rw.c b/src/disk/minivhd/minivhd_struct_rw.c deleted file mode 100644 index 5285f8a68..000000000 --- a/src/disk/minivhd/minivhd_struct_rw.c +++ /dev/null @@ -1,167 +0,0 @@ -/** - * \file - * \brief Header and footer serialize/deserialize functions - */ -#ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 64 -#endif -#include -#include -#include -#include -#include -#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); -} diff --git a/src/disk/minivhd/minivhd_struct_rw.h b/src/disk/minivhd/minivhd_struct_rw.h deleted file mode 100644 index 39441fb39..000000000 --- a/src/disk/minivhd/minivhd_struct_rw.h +++ /dev/null @@ -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 diff --git a/src/disk/minivhd/minivhd_util.c b/src/disk/minivhd/minivhd_util.c index 5bfc59915..dd3244322 100644 --- a/src/disk/minivhd/minivhd_util.c +++ b/src/disk/minivhd/minivhd_util.c @@ -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, + * + * 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 #include #include #include +#include #include #include #include #include -#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; diff --git a/src/disk/minivhd/minivhd_util.h b/src/disk/minivhd/minivhd_util.h deleted file mode 100644 index 227570ce2..000000000 --- a/src/disk/minivhd/minivhd_util.h +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef MINIVHD_UTIL_H -#define MINIVHD_UTIL_H - -#include -#include -#include -#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 diff --git a/src/disk/minivhd/struct_rw.c b/src/disk/minivhd/struct_rw.c new file mode 100644 index 000000000..ceb98253a --- /dev/null +++ b/src/disk/minivhd/struct_rw.c @@ -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, + * + * 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 +#include +#include +#include +#include +#include +#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); +} diff --git a/src/disk/minivhd/version.h b/src/disk/minivhd/version.h new file mode 100644 index 000000000..fcedc1be2 --- /dev/null +++ b/src/disk/minivhd/version.h @@ -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, + * + * 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*/ diff --git a/src/disk/minivhd/libxml2_encoding.c b/src/disk/minivhd/xml2_encoding.c similarity index 92% rename from src/disk/minivhd/libxml2_encoding.c rename to src/disk/minivhd/xml2_encoding.c index 48c291f2f..6c39cb7f6 100644 --- a/src/disk/minivhd/libxml2_encoding.c +++ b/src/disk/minivhd/xml2_encoding.c @@ -22,9 +22,19 @@ * Adapted and abridged for MiniVHD by Sherman Perry */ #include +#include +#include +#include +#include +#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); } diff --git a/src/disk/minivhd/xml2_encoding.h b/src/disk/minivhd/xml2_encoding.h new file mode 100644 index 000000000..68c85390d --- /dev/null +++ b/src/disk/minivhd/xml2_encoding.h @@ -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, + * + * 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*/ diff --git a/src/include/86box/keyboard.h b/src/include/86box/keyboard.h index b7f2a67bc..7f599e371 100644 --- a/src/include/86box/keyboard.h +++ b/src/include/86box/keyboard.h @@ -194,7 +194,10 @@ 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); diff --git a/src/io.c b/src/io.c index 0cd7cd87b..87cceae62 100644 --- a/src/io.c +++ b/src/io.c @@ -56,6 +56,7 @@ typedef struct { int initialized = 0; io_t *io[NPORTS], *io_last[NPORTS]; +// #define ENABLE_IO_LOG 1 #ifdef ENABLE_IO_LOG int io_do_log = ENABLE_IO_LOG; @@ -310,7 +311,9 @@ inb(uint16_t port) /* if (port == 0x1ed) ret = 0xfe; */ - io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); + if (port == 0x92) { + io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); + } return (ret); } @@ -341,7 +344,9 @@ outb(uint16_t port, uint8_t val) #endif } - io_log("[%04X:%08X] (%i, %i, %04i) outb(%04X, %02X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val); + if (port == 0x92) { + io_log("[%04X:%08X] (%i, %i, %04i) outb(%04X, %02X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val); + } return; } @@ -395,7 +400,9 @@ inw(uint16_t port) if (!found) cycles -= io_delay; - io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); + if (port == 0x92) { + io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); + } return ret; } @@ -440,7 +447,9 @@ outw(uint16_t port, uint16_t val) #endif } - io_log("[%04X:%08X] (%i, %i, %04i) outw(%04X, %04X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val); + if (port == 0x92) { + io_log("[%04X:%08X] (%i, %i, %04i) outw(%04X, %04X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val); + } return; } @@ -522,7 +531,9 @@ inl(uint16_t port) if (!found) cycles -= io_delay; - io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); + if (port == 0x92) { + io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); + } return ret; } @@ -582,7 +593,9 @@ outl(uint16_t port, uint32_t val) #endif } - io_log("[%04X:%08X] (%i, %i, %04i) outl(%04X, %08X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val); + if (port == 0x92) { + io_log("[%04X:%08X] (%i, %i, %04i) outl(%04X, %08X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val); + } return; } diff --git a/src/pic.c b/src/pic.c index cb5fcb0c5..984d346fa 100644 --- a/src/pic.c +++ b/src/pic.c @@ -395,7 +395,7 @@ pic_latch_read(uint16_t addr, void *priv) { uint8_t ret = 0xff; - pic_log("pic_latch_read(): %02X%02X\n", pic2.lines & 0x10, pic.lines & 0x02); + 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); @@ -541,11 +541,12 @@ void pic_kbd_latch(int enable) { pic_log("PIC keyboard latch now %sabled\n", enable ? "en" : "dis"); + pclog("PIC keyboard latch now %sabled\n", enable ? "en" : "dis"); - if ((enable | mouse_latch) != (kbd_latch | mouse_latch)) { - kbd_latch = enable; - io_handler(kbd_latch | mouse_latch, 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL); - } + 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); @@ -555,11 +556,12 @@ void pic_mouse_latch(int enable) { pic_log("PIC mouse latch now %sabled\n", enable ? "en" : "dis"); + pclog("PIC mouse latch now %sabled\n", enable ? "en" : "dis"); - if ((kbd_latch | enable) != (kbd_latch | mouse_latch)) { - mouse_latch = enable; - io_handler(kbd_latch | mouse_latch, 0x0060, 0x0001, pic_latch_read, NULL, NULL, NULL, NULL, NULL, NULL); - } + 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); diff --git a/src/qt/qt_harddiskdialog.cpp b/src/qt/qt_harddiskdialog.cpp index e8ccccd6f..088ef0413 100644 --- a/src/qt/qt_harddiskdialog.cpp +++ b/src/qt/qt_harddiskdialog.cpp @@ -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 diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 8f230ec5a..8df0dba3d 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -580,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 @@ -1241,13 +1240,14 @@ MainWindow::keyPressEvent(QKeyEvent *event) #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)) @@ -1279,6 +1279,17 @@ 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; diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 826f75475..cd0bd695b 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -167,6 +167,10 @@ private: bool resizableonce = false; bool vnc_enabled = false; + /* Full screen ON and OFF signals */ + static bool fs_on_signal = false; + static bool fs_off_signal = false; + friend class SpecifyDimensions; friend class ProgSettings; friend class RendererCommon; diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 5cfaea14c..b61a974c6 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -362,12 +362,6 @@ &Fullscreen - - Ctrl+Alt+PgUp - - - false - diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index 3f7c36199..b5ae84d71 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -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) diff --git a/src/qt/qt_winrawinputfilter.cpp b/src/qt/qt_winrawinputfilter.cpp index ccaea1f56..3703f47d8 100644 --- a/src/qt/qt_winrawinputfilter.cpp +++ b/src/qt/qt_winrawinputfilter.cpp @@ -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 diff --git a/src/sound/ymfm/ymfm.h b/src/sound/ymfm/ymfm.h index ae13faedd..bc0cf8b6c 100644 --- a/src/sound/ymfm/ymfm.h +++ b/src/sound/ymfm/ymfm.h @@ -40,11 +40,11 @@ #include #include #include +#include #include #include #include #include -#include 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 +template 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 - void add(ymfm_output<_Outputs> output) + template + void add(ymfm_output 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 - void add(ymfm_output<_Outputs> output, ymfm_output<_Outputs> const &ref) + template + void add(ymfm_output output, ymfm_output 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]); } diff --git a/src/sound/ymfm/ymfm_fm.h b/src/sound/ymfm/ymfm_fm.h index 7c92c0f82..81795f8fe 100644 --- a/src/sound/ymfm/ymfm_fm.h +++ b/src/sound/ymfm/ymfm_fm.h @@ -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> m_channel[CHANNELS]; // channel pointers std::unique_ptr> m_operator[OPERATORS]; // operator pointers -#if (DEBUG_LOG_WAVFILES) +#if (YMFM_DEBUG_LOG_WAVFILES) mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging #endif }; diff --git a/src/sound/ymfm/ymfm_fm.ipp b/src/sound/ymfm/ymfm_fm.ipp index 7e5109d59..14c1aa965 100644 --- a/src/sound/ymfm/ymfm_fm.ipp +++ b/src/sound/ymfm/ymfm_fm.ipp @@ -1185,6 +1185,7 @@ fm_engine_base::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::fm_engine_base(ymfm_interface &intf) : for (uint32_t opnum = 0; opnum < OPERATORS; opnum++) m_operator[opnum] = std::make_unique>(*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::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::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::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::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 } diff --git a/src/sound/ymfm/ymfm_opl.cpp b/src/sound/ymfm/ymfm_opl.cpp index 86215c5b2..499bfceef 100644 --- a/src/sound/ymfm/ymfm_opl.cpp +++ b/src/sound/ymfm/ymfm_opl.cpp @@ -100,6 +100,11 @@ opl_registers_base::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; } diff --git a/src/sound/ymfm/ymfm_pcm.cpp b/src/sound/ymfm/ymfm_pcm.cpp index 50595133b..34417490c 100644 --- a/src/sound/ymfm/ymfm_pcm.cpp +++ b/src/sound/ymfm/ymfm_pcm.cpp @@ -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; } diff --git a/src/unix/assets/86Box.spec b/src/unix/assets/86Box.spec index e994ca4a7..ede0d5bb6 100644 --- a/src/unix/assets/86Box.spec +++ b/src/unix/assets/86Box.spec @@ -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 diff --git a/src/unix/unix.c b/src/unix/unix.c index 478b5309f..23390fae9 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -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 diff --git a/src/win/86Box.rc b/src/win/86Box.rc index e180873ff..2932b7d62 100644 --- a/src/win/86Box.rc +++ b/src/win/86Box.rc @@ -42,7 +42,7 @@ BEGIN #ifdef MTR_ENABLED "T", IDM_ACTION_TRACE, CONTROL, VIRTKEY #endif - VK_PRIOR,IDM_VID_FULLSCREEN, VIRTKEY, CONTROL , ALT + // VK_PRIOR,IDM_VID_FULLSCREEN, VIRTKEY, CONTROL , ALT VK_F11, IDM_ACTION_SCREENSHOT, VIRTKEY, CONTROL VK_F12, IDM_ACTION_RESET_CAD, VIRTKEY, CONTROL VK_PAUSE,IDM_ACTION_PAUSE, VIRTKEY diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 3f6bf5cdb..76d402a83 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -631,9 +631,8 @@ HDDOBJ := hdd.o \ hdc_ide_cmd640.o hdc_ide_cmd646.o \ hdc_ide_sff8038i.o -MINIVHDOBJ := cwalk.o libxml2_encoding.o minivhd_convert.o \ - minivhd_create.o minivhd_io.o minivhd_manage.o \ - minivhd_struct_rw.o minivhd_util.o +MINIVHDOBJ := cwalk.o xml2_encoding.o convert.o \ + create.o minivhd_io.o manage.o struct_rw.o minivhd_util.o CDROMOBJ := cdrom.o \ cdrom_image_backend.o cdrom_image_viso.o cdrom_image.o cdrom_mitsumi.o diff --git a/src/win/win_keyboard.c b/src/win/win_keyboard.c index 6b7e00b57..010da5c81 100644 --- a/src/win/win_keyboard.c +++ b/src/win/win_keyboard.c @@ -133,7 +133,7 @@ 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. */ if (recv_lalt) { @@ -152,14 +152,14 @@ keyboard_handle(PRAWINPUT raw) 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); + keyboard_input(0, 0x00f); recv_tab = 0; } else { switch (scancode) { - case 0x00F: + case 0x00f: recv_tab = !(rawKB.Flags & RI_KEY_BREAK); break; case 0x038: @@ -172,7 +172,7 @@ 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 diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 129fd2d5f..7129230a3 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -73,7 +73,6 @@ #include <86box/win.h> #include <86box/serial_passthrough.h> #include "../disk/minivhd/minivhd.h" -#include "../disk/minivhd/minivhd_util.h" /* Icon, Bus, File, C, H, S, Size, Speed */ #define C_COLUMNS_HARD_DISKS 7 diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 4c19f5a7f..7deaf7738 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -1183,6 +1183,7 @@ ui_init(int nCmdShow) {IDCANCEL, MAKEINTRESOURCE(IDS_2120)} }; uint32_t helper_lang; + static int fs_on_signal = 0, fs_off_signal = 0; /* Load DPI related Windows 10 APIs */ user32_handle = dynld_module("user32.dll", user32_imports); @@ -1461,9 +1462,20 @@ ui_init(int nCmdShow) plat_mouse_capture(0); } - if (video_fullscreen && keyboard_isfsexit()) { + if (!fs_off_signal && video_fullscreen && keyboard_isfsexit()) { /* Signal "exit fullscreen mode". */ + fs_off_signal = 1; + } else if (fs_off_signal && video_fullscreen && keyboard_isfsexit_down()) { plat_setfullscreen(0); + fs_off_signal = 0; + } + + if (!fs_on_signal && !video_fullscreen && keyboard_isfsenter()) { + /* Signal "enter fullscreen mode". */ + fs_on_signal = 1; + } else if (fs_on_signal && !video_fullscreen && keyboard_isfsenter_down()) { + plat_setfullscreen(1); + fs_on_signal = 0; } #ifdef DISCORD