Merge branch 'master' of https://github.com/86Box/86Box
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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? */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* Copyright 2016-2020 Miran Grca.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -32,7 +33,7 @@
|
||||
#include <86box/random.h>
|
||||
#include <86box/hdd.h>
|
||||
#include "minivhd/minivhd.h"
|
||||
#include "minivhd/minivhd_internal.h"
|
||||
#include "minivhd/internal.h"
|
||||
|
||||
#define HDD_IMAGE_RAW 0
|
||||
#define HDD_IMAGE_HDI 1
|
||||
|
||||
@@ -13,6 +13,5 @@
|
||||
# Copyright 2020,2021 David Hrdlička.
|
||||
#
|
||||
|
||||
add_library(minivhd STATIC cwalk.c libxml2_encoding.c minivhd_convert.c
|
||||
minivhd_create.c minivhd_io.c minivhd_manage.c minivhd_struct_rw.c
|
||||
minivhd_util.c)
|
||||
add_library(minivhd STATIC cwalk.c xml2_encoding.c convert.c
|
||||
create.c minivhd_io.c manage.c struct_rw.c minivhd_util.c)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Credits
|
||||
MiniVHD Copyright (c) 2019 Sherman Perry
|
||||
MiniVHD Copyright 2019-2021 Sherman Perry.
|
||||
|
||||
MiniVHD was made possible with the help of the following projects
|
||||
MiniVHD was made possible with the help of the following projects:
|
||||
|
||||
### libxml2
|
||||
**Project Home:** http://www.xmlsoft.org/
|
||||
@@ -10,3 +10,8 @@ MiniVHD was made possible with the help of the following projects
|
||||
### cwalk
|
||||
**Project Home:** https://likle.github.io/cwalk/
|
||||
**Licence:** MIT (https://github.com/likle/cwalk/blob/master/LICENSE.md)
|
||||
|
||||
### VARCem
|
||||
The MiniVHD was rewritten into a standalone library (both shared as well
|
||||
as static) by Fred N. van Kempen for use with the VARCem PC Systems
|
||||
emulator - see https://www.varcem.com/ for more info.
|
||||
|
||||
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
@@ -1,28 +1,66 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Version: @(#)convert.c 1.0.2 2021/04/16
|
||||
*
|
||||
* Authors: Sherman Perry, <shermperry@gmail.com>
|
||||
* Fred N. van Kempen, <waltje@varcem.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
* Copyright 2021 Fred N. van Kempen.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "minivhd_create.h"
|
||||
#include "minivhd_internal.h"
|
||||
#include "minivhd_util.h"
|
||||
#include <time.h>
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
|
||||
static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err);
|
||||
|
||||
static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err) {
|
||||
static FILE*
|
||||
open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err)
|
||||
{
|
||||
if (geom == NULL) {
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *raw_img = mvhd_fopen(utf8_raw_path, "rb", err);
|
||||
if (raw_img == NULL) {
|
||||
*err = MVHD_ERR_FILE;
|
||||
return NULL;
|
||||
}
|
||||
if (geom == NULL) {
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mvhd_fseeko64(raw_img, 0, SEEK_END);
|
||||
uint64_t size_bytes = (uint64_t)mvhd_ftello64(raw_img);
|
||||
MVHDGeom new_geom = mvhd_calculate_geometry(size_bytes);
|
||||
@@ -34,37 +72,52 @@ static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geo
|
||||
geom->heads = new_geom.heads;
|
||||
geom->spt = new_geom.spt;
|
||||
mvhd_fseeko64(raw_img, 0, SEEK_SET);
|
||||
|
||||
return raw_img;
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) {
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err)
|
||||
{
|
||||
MVHDGeom geom;
|
||||
FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err);
|
||||
|
||||
FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err);
|
||||
if (raw_img == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
|
||||
MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, size_in_bytes, &geom, err, NULL);
|
||||
if (vhdm == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vhdm;
|
||||
}
|
||||
MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) {
|
||||
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err)
|
||||
{
|
||||
MVHDGeom geom;
|
||||
MVHDMeta *vhdm = NULL;
|
||||
FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err);
|
||||
|
||||
FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err);
|
||||
if (raw_img == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vhdm = mvhd_create_sparse(utf8_vhd_path, geom, err);
|
||||
if (vhdm == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
uint8_t buff[4096] = {0}; // 8 sectors
|
||||
uint8_t empty_buff[4096] = {0};
|
||||
int total_sectors = mvhd_calc_size_sectors(&geom);
|
||||
int copy_sect = 0;
|
||||
|
||||
for (int i = 0; i < total_sectors; i += 8) {
|
||||
copy_sect = 8;
|
||||
if ((i + 8) >= total_sectors) {
|
||||
@@ -72,6 +125,7 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
|
||||
memset(buff, 0, sizeof buff);
|
||||
}
|
||||
(void) !fread(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img);
|
||||
|
||||
/* Only write data if there's data to write, to take advantage of the sparse VHD format */
|
||||
if (memcmp(buff, empty_buff, sizeof buff) != 0) {
|
||||
mvhd_write_sectors(vhdm, i, copy_sect, buff);
|
||||
@@ -79,18 +133,25 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
|
||||
}
|
||||
end:
|
||||
fclose(raw_img);
|
||||
|
||||
return vhdm;
|
||||
}
|
||||
FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err) {
|
||||
|
||||
|
||||
MVHDAPI FILE*
|
||||
mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err)
|
||||
{
|
||||
FILE *raw_img = mvhd_fopen(utf8_raw_path, "wb", err);
|
||||
if (raw_img == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MVHDMeta *vhdm = mvhd_open(utf8_vhd_path, true, err);
|
||||
if (vhdm == NULL) {
|
||||
fclose(raw_img);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t buff[4096] = {0}; // 8 sectors
|
||||
int total_sectors = mvhd_calc_size_sectors((MVHDGeom*)&vhdm->footer.geom);
|
||||
int copy_sect = 0;
|
||||
@@ -104,5 +165,6 @@ FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path,
|
||||
}
|
||||
mvhd_close(vhdm);
|
||||
mvhd_fseeko64(raw_img, 0, SEEK_SET);
|
||||
|
||||
return raw_img;
|
||||
}
|
||||
@@ -1,30 +1,60 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Version: @(#)create.c 1.0.3 2021/04/16
|
||||
*
|
||||
* Authors: Sherman Perry, <shermperry@gmail.com>
|
||||
* Fred N. van Kempen, <waltje@varcem.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
* Copyright 2021 Fred N. van Kempen.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "cwalk.h"
|
||||
#include "libxml2_encoding.h"
|
||||
#include "minivhd_internal.h"
|
||||
#include "minivhd_util.h"
|
||||
#include "minivhd_struct_rw.h"
|
||||
#include "minivhd_io.h"
|
||||
#include "minivhd_create.h"
|
||||
#include <time.h>
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
#include "cwalk.h"
|
||||
#include "xml2_encoding.h"
|
||||
|
||||
|
||||
static const char MVHD_CONECTIX_COOKIE[] = "conectix";
|
||||
static const char MVHD_CREATOR[] = "mVHD";
|
||||
static const char MVHD_CREATOR_HOST_OS[] = "Wi2k";
|
||||
static const char MVHD_CXSPARSE_COOKIE[] = "cxsparse";
|
||||
|
||||
static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off);
|
||||
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors);
|
||||
static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
const char* child_path,
|
||||
const char* par_path,
|
||||
uint64_t start_offset,
|
||||
mvhd_utf16* w2ku_path_buff,
|
||||
mvhd_utf16* w2ru_path_buff,
|
||||
MVHDError* err);
|
||||
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err);
|
||||
|
||||
/**
|
||||
* \brief Populate a VHD footer
|
||||
@@ -35,24 +65,29 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
* \param [in] type of HVD that is being created
|
||||
* \param [in] sparse_header_off, an absolute file offset to the sparse header. Not used for fixed VHD images
|
||||
*/
|
||||
static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) {
|
||||
memcpy(footer->cookie, "conectix", sizeof footer->cookie);
|
||||
static void
|
||||
gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off)
|
||||
{
|
||||
memcpy(footer->cookie, MVHD_CONECTIX_COOKIE, sizeof footer->cookie);
|
||||
footer->features = 0x00000002;
|
||||
footer->fi_fmt_vers = 0x00010000;
|
||||
footer->data_offset = (type == MVHD_TYPE_DIFF || type == MVHD_TYPE_DYNAMIC) ? sparse_header_off : 0xffffffffffffffff;
|
||||
footer->timestamp = vhd_calc_timestamp();
|
||||
memcpy(footer->cr_app, "mvhd", sizeof footer->cr_app);
|
||||
memcpy(footer->cr_app, MVHD_CREATOR, sizeof footer->cr_app);
|
||||
footer->cr_vers = 0x000e0000;
|
||||
memcpy(footer->cr_host_os, "Wi2k", sizeof footer->cr_host_os);
|
||||
memcpy(footer->cr_host_os, MVHD_CREATOR_HOST_OS, sizeof footer->cr_host_os);
|
||||
footer->orig_sz = footer->curr_sz = size_in_bytes;
|
||||
footer->geom.cyl = geom->cyl;
|
||||
footer->geom.heads = geom->heads;
|
||||
footer->geom.spt = geom->spt;
|
||||
footer->disk_type = type;
|
||||
|
||||
mvhd_generate_uuid(footer->uuid);
|
||||
|
||||
footer->checksum = mvhd_gen_footer_checksum(footer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Populate a VHD sparse header
|
||||
*
|
||||
@@ -61,8 +96,10 @@ static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom
|
||||
* \param [in] bat_offset is the absolute file offset for start of the Block Allocation Table
|
||||
* \param [in] block_size_in_sectors is the block size in sectors.
|
||||
*/
|
||||
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors) {
|
||||
memcpy(header->cookie, "cxsparse", sizeof header->cookie);
|
||||
static void
|
||||
gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors)
|
||||
{
|
||||
memcpy(header->cookie, MVHD_CXSPARSE_COOKIE, sizeof header->cookie);
|
||||
header->data_offset = 0xffffffffffffffff;
|
||||
header->bat_offset = bat_offset;
|
||||
header->head_vers = 0x00010000;
|
||||
@@ -71,6 +108,7 @@ static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks,
|
||||
header->checksum = mvhd_gen_sparse_checksum(header);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Generate parent locators for differencing VHD images
|
||||
*
|
||||
@@ -85,13 +123,12 @@ static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks,
|
||||
* \retval 0 if success
|
||||
* \retval < 0 if an error occurrs. Check value of *err for actual error
|
||||
*/
|
||||
static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
const char* child_path,
|
||||
const char* par_path,
|
||||
uint64_t start_offset,
|
||||
mvhd_utf16* w2ku_path_buff,
|
||||
mvhd_utf16* w2ru_path_buff,
|
||||
MVHDError* err) {
|
||||
static int
|
||||
gen_par_loc(MVHDSparseHeader* header, const char* child_path,
|
||||
const char* par_path, uint64_t start_offset,
|
||||
mvhd_utf16* w2ku_path_buff, mvhd_utf16* w2ru_path_buff,
|
||||
MVHDError* err)
|
||||
{
|
||||
/* Get our paths to store in the differencing VHD. We want both the absolute path to the parent,
|
||||
as well as the relative path from the child VHD */
|
||||
int rv = 0;
|
||||
@@ -100,6 +137,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
char rel_path[MVHD_MAX_PATH_BYTES] = {0};
|
||||
char child_dir[MVHD_MAX_PATH_BYTES] = {0};
|
||||
size_t child_dir_len;
|
||||
|
||||
if (strlen(child_path) < sizeof child_dir) {
|
||||
strcpy(child_dir, child_path);
|
||||
} else {
|
||||
@@ -107,6 +145,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
rv = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
cwk_path_get_basename(par_path, (const char**)&par_filename, &par_fn_len);
|
||||
cwk_path_get_dirname(child_dir, &child_dir_len);
|
||||
child_dir[child_dir_len] = '\0';
|
||||
@@ -116,6 +155,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
rv = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* We have our paths, now store the parent filename directly in the sparse header. */
|
||||
int outlen = sizeof header->par_utf16_name;
|
||||
int utf_ret;
|
||||
@@ -144,6 +184,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
goto end;
|
||||
}
|
||||
int w2ru_len = utf_ret;
|
||||
|
||||
/**
|
||||
* Finally populate the parent locaters in the sparse header.
|
||||
* This is the information needed to find the paths saved elsewhere
|
||||
@@ -169,11 +210,16 @@ end:
|
||||
return rv;
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback) {
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback)
|
||||
{
|
||||
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
|
||||
|
||||
return mvhd_create_fixed_raw(path, NULL, size_in_bytes, &geom, err, progress_callback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief internal function that implements public mvhd_create_fixed() functionality
|
||||
*
|
||||
@@ -182,27 +228,35 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_prog
|
||||
*
|
||||
* \param [in] raw_image file handle to a raw disk image to populate VHD
|
||||
*/
|
||||
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback) {
|
||||
MVHDMeta*
|
||||
mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback)
|
||||
{
|
||||
uint8_t img_data[MVHD_SECTOR_SIZE] = {0};
|
||||
uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0};
|
||||
|
||||
if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) {
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MVHDMeta* vhdm = calloc(1, sizeof *vhdm);
|
||||
if (vhdm == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
goto end;
|
||||
}
|
||||
if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) {
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
|
||||
FILE* f = mvhd_fopen(path, "wb+", err);
|
||||
if (f == NULL) {
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
mvhd_fseeko64(f, 0, SEEK_SET);
|
||||
|
||||
uint32_t size_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE);
|
||||
uint32_t s;
|
||||
|
||||
if (progress_callback)
|
||||
progress_callback(0, size_sectors);
|
||||
|
||||
if (raw_img != NULL) {
|
||||
mvhd_fseeko64(raw_img, 0, SEEK_END);
|
||||
uint64_t raw_size = (uint64_t)mvhd_ftello64(raw_img);
|
||||
@@ -211,7 +265,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
|
||||
*err = MVHD_ERR_CONV_SIZE;
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
mvhd_gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0);
|
||||
gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0);
|
||||
mvhd_fseeko64(raw_img, 0, SEEK_SET);
|
||||
for (s = 0; s < size_sectors; s++) {
|
||||
(void) !fread(img_data, sizeof img_data, 1, raw_img);
|
||||
@@ -220,7 +274,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
|
||||
progress_callback(s + 1, size_sectors);
|
||||
}
|
||||
} else {
|
||||
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0);
|
||||
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0);
|
||||
for (s = 0; s < size_sectors; s++) {
|
||||
fwrite(img_data, sizeof img_data, 1, f);
|
||||
if (progress_callback)
|
||||
@@ -238,10 +292,12 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
|
||||
cleanup_vhdm:
|
||||
free(vhdm);
|
||||
vhdm = NULL;
|
||||
|
||||
end:
|
||||
return vhdm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Create sparse or differencing VHD image.
|
||||
*
|
||||
@@ -254,7 +310,9 @@ end:
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err) {
|
||||
static MVHDMeta*
|
||||
create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err)
|
||||
{
|
||||
uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0};
|
||||
uint8_t sparse_buff[MVHD_SPARSE_SIZE] = {0};
|
||||
uint8_t bat_sect[MVHD_SECTOR_SIZE];
|
||||
@@ -265,6 +323,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
mvhd_utf16* w2ku_path_buff = NULL;
|
||||
mvhd_utf16* w2ru_path_buff = NULL;
|
||||
uint32_t par_mod_timestamp = 0;
|
||||
|
||||
if (par_path != NULL) {
|
||||
par_mod_timestamp = mvhd_file_mod_timestamp(par_path, err);
|
||||
if (*err != 0) {
|
||||
@@ -275,6 +334,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
vhdm = calloc(1, sizeof *vhdm);
|
||||
if (vhdm == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
@@ -297,15 +357,18 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
mvhd_fseeko64(f, 0, SEEK_SET);
|
||||
|
||||
/* Note, the sparse header follows the footer copy at the beginning of the file */
|
||||
if (par_path == NULL) {
|
||||
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
|
||||
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
|
||||
} else {
|
||||
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
|
||||
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
|
||||
}
|
||||
mvhd_footer_to_buffer(&vhdm->footer, footer_buff);
|
||||
|
||||
/* As mentioned, start with a copy of the footer */
|
||||
fwrite(footer_buff, sizeof footer_buff, 1, f);
|
||||
|
||||
/**
|
||||
* Calculate the number of (2MB or 512KB) data blocks required to store the entire
|
||||
* contents of the disk image, followed by the number of sectors the
|
||||
@@ -347,43 +410,51 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
}
|
||||
memcpy(vhdm->sparse.par_uuid, par_vhdm->footer.uuid, sizeof vhdm->sparse.par_uuid);
|
||||
par_loc_offset = bat_offset + ((uint64_t)num_bat_sect * MVHD_SECTOR_SIZE) + (5 * MVHD_SECTOR_SIZE);
|
||||
if (mvhd_gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) {
|
||||
if (gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) {
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
vhdm->sparse.par_timestamp = par_mod_timestamp;
|
||||
}
|
||||
mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors);
|
||||
gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors);
|
||||
mvhd_header_to_buffer(&vhdm->sparse, sparse_buff);
|
||||
fwrite(sparse_buff, sizeof sparse_buff, 1, f);
|
||||
|
||||
/* The BAT sectors need to be filled with 0xffffffff */
|
||||
for (uint32_t i = 0; i < num_bat_sect; i++) {
|
||||
for (uint32_t k = 0; k < num_bat_sect; k++) {
|
||||
fwrite(bat_sect, sizeof bat_sect, 1, f);
|
||||
}
|
||||
mvhd_write_empty_sectors(f, 5);
|
||||
|
||||
/**
|
||||
* If creating a differencing VHD, the paths to the parent image need to be written
|
||||
* tp the file. Both absolute and relative paths are written
|
||||
* */
|
||||
if (par_vhdm != NULL) {
|
||||
uint64_t curr_pos = (uint64_t)mvhd_ftello64(f);
|
||||
|
||||
/* Double check my sums... */
|
||||
assert(curr_pos == par_loc_offset);
|
||||
|
||||
/* Fill the space required for location data with zero */
|
||||
uint8_t empty_sect[MVHD_SECTOR_SIZE] = {0};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (uint32_t j = 0; j < (vhdm->sparse.par_loc_entry[i].plat_data_space / MVHD_SECTOR_SIZE); j++) {
|
||||
fwrite(empty_sect, sizeof empty_sect, 1, f);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now write the location entries */
|
||||
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[0].plat_data_offset, SEEK_SET);
|
||||
fwrite(w2ku_path_buff, vhdm->sparse.par_loc_entry[0].plat_data_len, 1, f);
|
||||
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset, SEEK_SET);
|
||||
fwrite(w2ru_path_buff, vhdm->sparse.par_loc_entry[1].plat_data_len, 1, f);
|
||||
|
||||
/* and reset the file position to continue */
|
||||
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset + vhdm->sparse.par_loc_entry[1].plat_data_space, SEEK_SET);
|
||||
mvhd_write_empty_sectors(f, 5);
|
||||
}
|
||||
|
||||
/* And finish with the footer */
|
||||
fwrite(footer_buff, sizeof footer_buff, 1, f);
|
||||
fclose(f);
|
||||
@@ -395,91 +466,112 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
cleanup_vhdm:
|
||||
free(vhdm);
|
||||
vhdm = NULL;
|
||||
|
||||
cleanup_par_vhdm:
|
||||
if (par_vhdm != NULL) {
|
||||
mvhd_close(par_vhdm);
|
||||
}
|
||||
|
||||
end:
|
||||
free(w2ku_path_buff);
|
||||
free(w2ru_path_buff);
|
||||
|
||||
return vhdm;
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) {
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_create_sparse(const char* path, MVHDGeom geom, int* err)
|
||||
{
|
||||
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
|
||||
return mvhd_create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err);
|
||||
|
||||
return create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err);
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err) {
|
||||
return mvhd_create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err);
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_create_diff(const char* path, const char* par_path, int* err)
|
||||
{
|
||||
return create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err);
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err) {
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_create_ex(MVHDCreationOptions options, int* err)
|
||||
{
|
||||
uint32_t geom_sector_size;
|
||||
switch (options.type)
|
||||
{
|
||||
case MVHD_TYPE_FIXED:
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
geom_sector_size = mvhd_calc_size_sectors(&(options.geometry));
|
||||
if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0)
|
||||
|| (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES)
|
||||
|| (options.size_in_bytes == 0 && geom_sector_size == 0))
|
||||
{
|
||||
*err = MVHD_ERR_INVALID_SIZE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes)
|
||||
{
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
return NULL;
|
||||
}
|
||||
switch (options.type) {
|
||||
case MVHD_TYPE_FIXED:
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
geom_sector_size = mvhd_calc_size_sectors(&(options.geometry));
|
||||
if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0)
|
||||
|| (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES)
|
||||
|| (options.size_in_bytes == 0 && geom_sector_size == 0)) {
|
||||
*err = MVHD_ERR_INVALID_SIZE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.size_in_bytes == 0)
|
||||
options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE;
|
||||
if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes) {
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (geom_sector_size == 0)
|
||||
options.geometry = mvhd_calculate_geometry(options.size_in_bytes);
|
||||
break;
|
||||
case MVHD_TYPE_DIFF:
|
||||
if (options.parent_path == NULL)
|
||||
{
|
||||
*err = MVHD_ERR_FILE;
|
||||
if (options.size_in_bytes == 0)
|
||||
options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE;
|
||||
|
||||
if (geom_sector_size == 0)
|
||||
options.geometry = mvhd_calculate_geometry(options.size_in_bytes);
|
||||
break;
|
||||
|
||||
case MVHD_TYPE_DIFF:
|
||||
if (options.parent_path == NULL) {
|
||||
*err = MVHD_ERR_FILE;
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
*err = MVHD_ERR_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*err = MVHD_ERR_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.path == NULL)
|
||||
{
|
||||
if (options.path == NULL) {
|
||||
*err = MVHD_ERR_FILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.type != MVHD_TYPE_FIXED)
|
||||
{
|
||||
if (options.type != MVHD_TYPE_FIXED) {
|
||||
if (options.block_size_in_sectors == MVHD_BLOCK_DEFAULT)
|
||||
options.block_size_in_sectors = MVHD_BLOCK_LARGE;
|
||||
|
||||
if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL)
|
||||
{
|
||||
if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL) {
|
||||
*err = MVHD_ERR_INVALID_BLOCK_SIZE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (options.type)
|
||||
{
|
||||
case MVHD_TYPE_FIXED:
|
||||
return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback);
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
return mvhd_create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err);
|
||||
case MVHD_TYPE_DIFF:
|
||||
return mvhd_create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err);
|
||||
switch (options.type) {
|
||||
case MVHD_TYPE_FIXED:
|
||||
return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback);
|
||||
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
return create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err);
|
||||
|
||||
case MVHD_TYPE_DIFF:
|
||||
return create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err);
|
||||
}
|
||||
|
||||
return NULL; /* Make the compiler happy */
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
mvhd_is_conectix_str(const void* buffer)
|
||||
{
|
||||
if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,12 +1,49 @@
|
||||
/*
|
||||
* libCWALK Path library for C/C++
|
||||
*
|
||||
* Version: @(#)cwalk.c 1.0.2 2021/03/16
|
||||
*
|
||||
* Authors: Sherman Perry, <shermperry@gmail.com>
|
||||
* Leonard Ikl<6B>, <https://github.com/likle>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
* Copyright 2020 Leonard Ikl<6B>.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "cwalk.h"
|
||||
|
||||
|
||||
/**
|
||||
* We try to default to a different path style depending on the operating
|
||||
* system. So this should detect whether we should use windows or unix paths.
|
||||
|
||||
@@ -1,10 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* libCWALK path library for C/C++
|
||||
*
|
||||
* Version: @(#)cwalk.h 1.0.3 2021/03/22
|
||||
*
|
||||
* Authors: Sherman Perry, <shermperry@gmail.com>
|
||||
* Leonard Ikl<6B>, <https://github.com/likle>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
* Copyright 2020 Leonard Ikl<6B>.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CWK_LIBRARY_H
|
||||
#define CWK_LIBRARY_H
|
||||
# define CWK_LIBRARY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* A segment represents a single component of a path. For instance, on linux a
|
||||
@@ -45,6 +75,11 @@ enum cwk_path_style
|
||||
CWK_STYLE_UNIX
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Generates an absolute path based on a base.
|
||||
*
|
||||
@@ -454,4 +489,9 @@ void cwk_path_set_style(enum cwk_path_style style);
|
||||
*/
|
||||
enum cwk_path_style cwk_path_get_style(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*CWK_LIBRARY_H*/
|
||||
|
||||
429
src/disk/minivhd/internal.h
Normal file
429
src/disk/minivhd/internal.h
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Internal definitions.
|
||||
*
|
||||
* Version: @(#)internal.h 1.0.1 2021/03/15
|
||||
*
|
||||
* Author: Sherman Perry, <shermperry@gmail.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MINIVHD_INTERNAL_H
|
||||
# define MINIVHD_INTERNAL_H
|
||||
|
||||
|
||||
#define MVHD_FOOTER_SIZE 512
|
||||
#define MVHD_SPARSE_SIZE 1024
|
||||
|
||||
#define MVHD_SECTOR_SIZE 512
|
||||
#define MVHD_BAT_ENT_PER_SECT 128
|
||||
|
||||
#define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000
|
||||
|
||||
#define MVHD_SPARSE_BLK 0xffffffff
|
||||
|
||||
/* For simplicity, we don't handle paths longer than this
|
||||
* Note, this is the max path in characters, as that is what
|
||||
* Windows uses
|
||||
*/
|
||||
#define MVHD_MAX_PATH_CHARS 260
|
||||
#define MVHD_MAX_PATH_BYTES 1040
|
||||
|
||||
#define MVHD_DIF_LOC_W2RU 0x57327275
|
||||
#define MVHD_DIF_LOC_W2KU 0x57326B75
|
||||
|
||||
#define MVHD_START_TS 946684800
|
||||
|
||||
|
||||
typedef struct MVHDSectorBitmap {
|
||||
uint8_t* curr_bitmap;
|
||||
int sector_count;
|
||||
int curr_block;
|
||||
} MVHDSectorBitmap;
|
||||
|
||||
typedef struct MVHDFooter {
|
||||
uint8_t cookie[8];
|
||||
uint32_t features;
|
||||
uint32_t fi_fmt_vers;
|
||||
uint64_t data_offset;
|
||||
uint32_t timestamp;
|
||||
uint8_t cr_app[4];
|
||||
uint32_t cr_vers;
|
||||
uint8_t cr_host_os[4];
|
||||
uint64_t orig_sz;
|
||||
uint64_t curr_sz;
|
||||
struct {
|
||||
uint16_t cyl;
|
||||
uint8_t heads;
|
||||
uint8_t spt;
|
||||
} geom;
|
||||
uint32_t disk_type;
|
||||
uint32_t checksum;
|
||||
uint8_t uuid[16];
|
||||
uint8_t saved_st;
|
||||
uint8_t reserved[427];
|
||||
} MVHDFooter;
|
||||
|
||||
typedef struct MVHDSparseHeader {
|
||||
uint8_t cookie[8];
|
||||
uint64_t data_offset;
|
||||
uint64_t bat_offset;
|
||||
uint32_t head_vers;
|
||||
uint32_t max_bat_ent;
|
||||
uint32_t block_sz;
|
||||
uint32_t checksum;
|
||||
uint8_t par_uuid[16];
|
||||
uint32_t par_timestamp;
|
||||
uint32_t reserved_1;
|
||||
uint8_t par_utf16_name[512];
|
||||
struct {
|
||||
uint32_t plat_code;
|
||||
uint32_t plat_data_space;
|
||||
uint32_t plat_data_len;
|
||||
uint32_t reserved;
|
||||
uint64_t plat_data_offset;
|
||||
} par_loc_entry[8];
|
||||
uint8_t reserved_2[256];
|
||||
} MVHDSparseHeader;
|
||||
|
||||
struct MVHDMeta {
|
||||
FILE* f;
|
||||
bool readonly;
|
||||
char filename[MVHD_MAX_PATH_BYTES];
|
||||
struct MVHDMeta* parent;
|
||||
MVHDFooter footer;
|
||||
MVHDSparseHeader sparse;
|
||||
uint32_t* block_offset;
|
||||
int sect_per_block;
|
||||
MVHDSectorBitmap bitmap;
|
||||
int (*read_sectors)(struct MVHDMeta*, uint32_t, int, void*);
|
||||
int (*write_sectors)(struct MVHDMeta*, uint32_t, int, void*);
|
||||
struct {
|
||||
uint8_t* zero_data;
|
||||
int sector_count;
|
||||
} format_buffer;
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Functions to deal with endian issues
|
||||
*/
|
||||
uint16_t mvhd_from_be16(uint16_t val);
|
||||
uint32_t mvhd_from_be32(uint32_t val);
|
||||
uint64_t mvhd_from_be64(uint64_t val);
|
||||
uint16_t mvhd_to_be16(uint16_t val);
|
||||
uint32_t mvhd_to_be32(uint32_t val);
|
||||
uint64_t mvhd_to_be64(uint64_t val);
|
||||
|
||||
/**
|
||||
* \brief Check if provided buffer begins with the string "conectix"
|
||||
*
|
||||
* \param [in] buffer The buffer to compare. Must be at least 8 bytes in length
|
||||
*
|
||||
* \return true if the buffer begins with "conectix"
|
||||
* \return false if the buffer does not begin with "conectix"
|
||||
*/
|
||||
bool mvhd_is_conectix_str(const void* buffer);
|
||||
|
||||
/**
|
||||
* \brief Generate a raw 16 byte UUID
|
||||
*
|
||||
* \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to
|
||||
*/
|
||||
void mvhd_generate_uuid(uint8_t *uuid);
|
||||
|
||||
/**
|
||||
* \brief Calculate a VHD formatted timestamp from the current time
|
||||
*/
|
||||
uint32_t vhd_calc_timestamp(void);
|
||||
|
||||
/**
|
||||
* \brief Convert an epoch timestamp to a VHD timestamp
|
||||
*
|
||||
* \param [in] ts epoch timestamp to convert.
|
||||
*
|
||||
* \return The adjusted timestamp, or 0 if the input timestamp is
|
||||
* earlier that 1 Janurary 2000
|
||||
*/
|
||||
uint32_t mvhd_epoch_to_vhd_ts(time_t ts);
|
||||
|
||||
/**
|
||||
* \brief Return the created time from a VHD image
|
||||
*
|
||||
* \param [in] vhdm Pointer to the MiniVHD metadata structure
|
||||
*
|
||||
* \return The created time, as a Unix timestamp
|
||||
*/
|
||||
time_t vhd_get_created_time(struct MVHDMeta *vhdm);
|
||||
|
||||
/**
|
||||
* \brief Cross platform, unicode filepath opening
|
||||
*
|
||||
* This function accounts for the fact that fopen() handles file paths differently compared to other
|
||||
* operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8.
|
||||
*
|
||||
* Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE
|
||||
* encoded path and modestring.
|
||||
*
|
||||
* \param [in] path The filepath to open as a UTF-8 string
|
||||
* \param [in] mode The mode string to use (eg: "rb+"")
|
||||
* \param [out] err The error value, if an error occurrs
|
||||
*
|
||||
* \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err
|
||||
*/
|
||||
FILE* mvhd_fopen(const char* path, const char* mode, int* err);
|
||||
|
||||
void mvhd_set_encoding_err(int encoding_retval, int* err);
|
||||
|
||||
/**
|
||||
* \brief Generate VHD footer checksum
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer);
|
||||
|
||||
/**
|
||||
* \brief Generate VHD sparse header checksum
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header);
|
||||
|
||||
uint32_t mvhd_crc32_for_byte(uint32_t r);
|
||||
|
||||
/**
|
||||
* \brief Get current position in file stream
|
||||
*
|
||||
* This is a portable version of the POSIX ftello64(). *
|
||||
*/
|
||||
int64_t mvhd_ftello64(FILE* stream);
|
||||
|
||||
/**
|
||||
* \brief Reposition the file stream's position
|
||||
*
|
||||
* This is a portable version of the POSIX fseeko64(). *
|
||||
*/
|
||||
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin);
|
||||
|
||||
/**
|
||||
* \brief Calculate the CRC32 of a data buffer.
|
||||
*
|
||||
* This function can be used for verifying data integrity.
|
||||
*
|
||||
* \param [in] data The data buffer
|
||||
* \param [in] n_bytes The size of the data buffer in bytes
|
||||
*
|
||||
* \return The CRC32 of the data buffer
|
||||
*/
|
||||
uint32_t mvhd_crc32(const void* data, size_t n_bytes);
|
||||
|
||||
/**
|
||||
* \brief Calculate the file modification timestamp.
|
||||
*
|
||||
* This function is primarily to help protect differencing VHD's
|
||||
*
|
||||
* \param [in] path the UTF-8 file path
|
||||
* \param [out] err The error value, if an error occurrs
|
||||
*
|
||||
* \return The file modified timestamp, in VHD compatible timestamp.
|
||||
* 'err' will be set to non-zero on error
|
||||
*/
|
||||
uint32_t mvhd_file_mod_timestamp(const char* path, int *err);
|
||||
|
||||
struct MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback);
|
||||
|
||||
/**
|
||||
* \brief Write zero filled sectors to file.
|
||||
*
|
||||
* Note, the caller should set the file position before calling this
|
||||
* function for correct operation.
|
||||
*
|
||||
* \param [in] f File to write sectors to
|
||||
* \param [in] sector_count The number of sectors to write
|
||||
*/
|
||||
void mvhd_write_empty_sectors(FILE* f, int sector_count);
|
||||
|
||||
/**
|
||||
* \brief Read a fixed VHD image
|
||||
*
|
||||
* Fixed VHD images are essentially raw image files with a footer tacked on
|
||||
* the end. They are therefore straightforward to write
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to read from
|
||||
* \param [in] num_sectors The desired number of sectors to read
|
||||
* \param [out] out_buff An output buffer to store read sectors. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_fixed_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Read a sparse VHD image
|
||||
*
|
||||
* Sparse, or dynamic images are VHD images that grow as data is written to them.
|
||||
*
|
||||
* This function implements the logic to read sectors from the file, taking into
|
||||
* account the fact that blocks may be stored on disk in any order, and that the
|
||||
* read could cross block boundaries.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to read from
|
||||
* \param [in] num_sectors The desired number of sectors to read
|
||||
* \param [out] out_buff An output buffer to store read sectors. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_sparse_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Read a differencing VHD image
|
||||
*
|
||||
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
|
||||
* properties of sparse images, but also reference a parent image. Data is read from the
|
||||
* child image only if it is newer than the data stored in the parent image.
|
||||
*
|
||||
* This function implements the logic to read sectors from the child, or a parent image.
|
||||
* Differencing images may have a differencing image as a parent, creating a chain of images.
|
||||
* There is no theoretical chain length limit, although I do not consider long chains to be
|
||||
* advisable. Verifying the parent-child relationship is not very robust.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to read from
|
||||
* \param [in] num_sectors The desired number of sectors to read
|
||||
* \param [out] out_buff An output buffer to store read sectors. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_diff_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Write to a fixed VHD image
|
||||
*
|
||||
* Fixed VHD images are essentially raw image files with a footer tacked on
|
||||
* the end. They are therefore straightforward to write
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to write to
|
||||
* \param [in] num_sectors The desired number of sectors to write
|
||||
* \param [in] in_buff A source buffer to write sectors from. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_fixed_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief Write to a sparse or differencing VHD image
|
||||
*
|
||||
* Sparse, or dynamic images are VHD images that grow as data is written to them.
|
||||
*
|
||||
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
|
||||
* properties of sparse images, but also reference a parent image. Data is always written
|
||||
* to the child image. This makes writing to differencing images essentially identical to
|
||||
* writing to sparse images, hence they use the same function.
|
||||
*
|
||||
* This function implements the logic to write sectors to the file, taking into
|
||||
* account the fact that blocks may be stored on disk in any order, and that the
|
||||
* write operation could cross block boundaries.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to write to
|
||||
* \param [in] num_sectors The desired number of sectors to write
|
||||
* \param [in] in_buff A source buffer to write sectors from. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_sparse_diff_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief A no-op function to "write" to read-only VHD images
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to write to
|
||||
* \param [in] num_sectors The desired number of sectors to write
|
||||
* \param [in] in_buff A source buffer to write sectors from. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_noop_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD footer from a buffer to a struct
|
||||
*
|
||||
* \param [out] footer save contents of buffer into footer
|
||||
* \param [in] buffer VHD footer in raw bytes
|
||||
*/
|
||||
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD sparse header from a buffer to a struct
|
||||
*
|
||||
* \param [out] header save contents of buffer into header
|
||||
* \param [in] buffer VHD header in raw bytes
|
||||
*/
|
||||
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD footer struct to a buffer
|
||||
*
|
||||
* \param [in] footer save contents of struct into buffer
|
||||
* \param [out] buffer VHD footer in raw bytes
|
||||
*/
|
||||
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD sparse header struct to a buffer
|
||||
*
|
||||
* \param [in] header save contents of struct into buffer
|
||||
* \param [out] buffer VHD sparse header in raw bytes
|
||||
*/
|
||||
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*MINIVHD_INTERNAL_H*/
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef LIBXML2_ENCODING_H
|
||||
#define LIBXML2_ENCODING_H
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint16_t mvhd_utf16;
|
||||
|
||||
void xmlEncodingInit(void);
|
||||
int UTF16LEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb);
|
||||
int UTF8ToUTF16LE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen);
|
||||
int UTF16BEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb);
|
||||
int UTF8ToUTF16BE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen);
|
||||
#endif
|
||||
@@ -1,66 +1,105 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief VHD management functions (open, close, read write etc)
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* VHD management functions (open, close, read write etc)
|
||||
*
|
||||
* Version: @(#)manage.c 1.0.4 2021/04/16
|
||||
*
|
||||
* Authors: Sherman Perry, <shermperry@gmail.com>
|
||||
* Fred N. van Kempen, <waltje@varcem.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
* Copyright 2021 Fred N. van Kempen.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "cwalk.h"
|
||||
#include "libxml2_encoding.h"
|
||||
#include "minivhd_internal.h"
|
||||
#include "minivhd_io.h"
|
||||
#include "minivhd_util.h"
|
||||
#include "minivhd_struct_rw.h"
|
||||
#include <time.h>
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
#include "version.h"
|
||||
#include "cwalk.h"
|
||||
#include "xml2_encoding.h"
|
||||
|
||||
|
||||
int mvhd_errno = 0;
|
||||
static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0};
|
||||
struct MVHDPaths {
|
||||
char dir_path[MVHD_MAX_PATH_BYTES];
|
||||
char file_name[MVHD_MAX_PATH_BYTES];
|
||||
char w2ku_path[MVHD_MAX_PATH_BYTES];
|
||||
char w2ru_path[MVHD_MAX_PATH_BYTES];
|
||||
char joined_path[MVHD_MAX_PATH_BYTES];
|
||||
char dir_path[MVHD_MAX_PATH_BYTES];
|
||||
char file_name[MVHD_MAX_PATH_BYTES];
|
||||
char w2ku_path[MVHD_MAX_PATH_BYTES];
|
||||
char w2ru_path[MVHD_MAX_PATH_BYTES];
|
||||
char joined_path[MVHD_MAX_PATH_BYTES];
|
||||
uint16_t tmp_src_path[MVHD_MAX_PATH_CHARS];
|
||||
};
|
||||
|
||||
static void mvhd_read_footer(MVHDMeta* vhdm);
|
||||
static void mvhd_read_sparse_header(MVHDMeta* vhdm);
|
||||
static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm);
|
||||
static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm);
|
||||
static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err);
|
||||
static void mvhd_calc_sparse_values(MVHDMeta* vhdm);
|
||||
static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err);
|
||||
|
||||
int mvhd_errno = 0;
|
||||
|
||||
|
||||
static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Populate data stuctures with content from a VHD footer
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static void mvhd_read_footer(MVHDMeta* vhdm) {
|
||||
static void
|
||||
read_footer(MVHDMeta* vhdm)
|
||||
{
|
||||
uint8_t buffer[MVHD_FOOTER_SIZE];
|
||||
|
||||
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
|
||||
(void) !fread(buffer, sizeof buffer, 1, vhdm->f);
|
||||
mvhd_buffer_to_footer(&vhdm->footer, buffer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Populate data stuctures with content from a VHD sparse header
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static void mvhd_read_sparse_header(MVHDMeta* vhdm) {
|
||||
static void
|
||||
read_sparse_header(MVHDMeta* vhdm)
|
||||
{
|
||||
uint8_t buffer[MVHD_SPARSE_SIZE];
|
||||
|
||||
mvhd_fseeko64(vhdm->f, vhdm->footer.data_offset, SEEK_SET);
|
||||
(void) !fread(buffer, sizeof buffer, 1, vhdm->f);
|
||||
mvhd_buffer_to_header(&vhdm->sparse, buffer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Validate VHD footer checksum
|
||||
*
|
||||
@@ -68,10 +107,13 @@ static void mvhd_read_sparse_header(MVHDMeta* vhdm) {
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) {
|
||||
static bool
|
||||
footer_checksum_valid(MVHDMeta* vhdm)
|
||||
{
|
||||
return vhdm->footer.checksum == mvhd_gen_footer_checksum(&vhdm->footer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Validate VHD sparse header checksum
|
||||
*
|
||||
@@ -79,10 +121,13 @@ static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) {
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) {
|
||||
static bool
|
||||
sparse_checksum_valid(MVHDMeta* vhdm)
|
||||
{
|
||||
return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(&vhdm->sparse);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Read BAT into MiniVHD data structure
|
||||
*
|
||||
@@ -96,13 +141,17 @@ static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) {
|
||||
* \retval -1 if an error occurrs. Check value of err in this case
|
||||
* \retval 0 if the function call succeeds
|
||||
*/
|
||||
static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) {
|
||||
static int
|
||||
read_bat(MVHDMeta *vhdm, MVHDError* err)
|
||||
{
|
||||
vhdm->block_offset = calloc(vhdm->sparse.max_bat_ent, sizeof *vhdm->block_offset);
|
||||
if (vhdm->block_offset == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mvhd_fseeko64(vhdm->f, vhdm->sparse.bat_offset, SEEK_SET);
|
||||
|
||||
for (uint32_t i = 0; i < vhdm->sparse.max_bat_ent; i++) {
|
||||
(void) !fread(&vhdm->block_offset[i], sizeof *vhdm->block_offset, 1, vhdm->f);
|
||||
vhdm->block_offset[i] = mvhd_from_be32(vhdm->block_offset[i]);
|
||||
@@ -110,20 +159,25 @@ static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Perform a one-time calculation of some sparse VHD values
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static void mvhd_calc_sparse_values(MVHDMeta* vhdm) {
|
||||
static void
|
||||
calc_sparse_values(MVHDMeta* vhdm)
|
||||
{
|
||||
vhdm->sect_per_block = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE;
|
||||
int bm_bytes = vhdm->sect_per_block / 8;
|
||||
vhdm->bitmap.sector_count = bm_bytes / MVHD_SECTOR_SIZE;
|
||||
|
||||
if (bm_bytes % MVHD_SECTOR_SIZE > 0) {
|
||||
vhdm->bitmap.sector_count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Allocate memory for a sector bitmap.
|
||||
*
|
||||
@@ -137,16 +191,21 @@ static void mvhd_calc_sparse_values(MVHDMeta* vhdm) {
|
||||
* \retval -1 if an error occurrs. Check value of err in this case
|
||||
* \retval 0 if the function call succeeds
|
||||
*/
|
||||
static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) {
|
||||
static int
|
||||
init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err)
|
||||
{
|
||||
vhdm->bitmap.curr_bitmap = calloc(vhdm->bitmap.sector_count, MVHD_SECTOR_SIZE);
|
||||
if (vhdm->bitmap.curr_bitmap == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
vhdm->bitmap.curr_block = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Check if the path for a given platform code exists
|
||||
*
|
||||
@@ -163,13 +222,19 @@ static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) {
|
||||
* \retval true if a file is found
|
||||
* \retval false if a file is not found
|
||||
*/
|
||||
static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code) {
|
||||
memset(paths->joined_path, 0, sizeof paths->joined_path);
|
||||
static bool
|
||||
mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
|
||||
{
|
||||
FILE* f;
|
||||
int cwk_ret, ferr;
|
||||
enum cwk_path_style style = cwk_path_guess_style((const char*)paths->dir_path);
|
||||
int ferr;
|
||||
size_t cwk_ret;
|
||||
enum cwk_path_style style;
|
||||
|
||||
memset(paths->joined_path, 0, sizeof paths->joined_path);
|
||||
style = cwk_path_guess_style((const char*)paths->dir_path);
|
||||
cwk_path_set_style(style);
|
||||
cwk_ret = 1;
|
||||
|
||||
if (plat_code == MVHD_DIF_LOC_W2RU && *paths->w2ru_path) {
|
||||
cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->w2ru_path, paths->joined_path, sizeof paths->joined_path);
|
||||
} else if (plat_code == MVHD_DIF_LOC_W2KU && *paths->w2ku_path) {
|
||||
@@ -181,6 +246,7 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
|
||||
if (cwk_ret > MVHD_MAX_PATH_BYTES) {
|
||||
return false;
|
||||
}
|
||||
|
||||
f = mvhd_fopen((const char*)paths->joined_path, "rb", &ferr);
|
||||
if (f != NULL) {
|
||||
/* We found a file at the requested path! */
|
||||
@@ -188,11 +254,12 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
|
||||
tmp_open_path[sizeof tmp_open_path - 1] = '\0';
|
||||
fclose(f);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief attempt to obtain a file path to a file that may be a valid VHD image
|
||||
*
|
||||
@@ -208,27 +275,33 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
|
||||
* \return a pointer to the global string `tmp_open_path`, or NULL if a path could
|
||||
* not be found, or some error occurred
|
||||
*/
|
||||
static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
|
||||
static char*
|
||||
get_diff_parent_path(MVHDMeta* vhdm, int* err)
|
||||
{
|
||||
int utf_outlen, utf_inlen, utf_ret;
|
||||
char* par_fp = NULL;
|
||||
char *par_fp = NULL;
|
||||
struct MVHDPaths *paths;
|
||||
size_t dirlen;
|
||||
|
||||
/* We can't resolve relative paths if we don't have an absolute
|
||||
path to work with */
|
||||
if (!cwk_path_is_absolute((const char*)vhdm->filename)) {
|
||||
*err = MVHD_ERR_PATH_REL;
|
||||
goto end;
|
||||
}
|
||||
struct MVHDPaths* paths = calloc(1, sizeof *paths);
|
||||
|
||||
paths = calloc(1, sizeof *paths);
|
||||
if (paths == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
goto end;
|
||||
}
|
||||
size_t dirlen;
|
||||
cwk_path_get_dirname((const char*)vhdm->filename, &dirlen);
|
||||
if (dirlen >= sizeof paths->dir_path) {
|
||||
*err = MVHD_ERR_PATH_LEN;
|
||||
goto paths_cleanup;
|
||||
}
|
||||
memcpy(paths->dir_path, vhdm->filename, dirlen);
|
||||
|
||||
/* Get the filename field from the sparse header. */
|
||||
utf_outlen = (int)sizeof paths->file_name;
|
||||
utf_inlen = (int)sizeof vhdm->sparse.par_utf16_name;
|
||||
@@ -237,8 +310,10 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
|
||||
mvhd_set_encoding_err(utf_ret, err);
|
||||
goto paths_cleanup;
|
||||
}
|
||||
|
||||
/* Now read the parent locator entries, both relative and absolute, if they exist */
|
||||
unsigned char* loc_path;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
utf_outlen = MVHD_MAX_PATH_BYTES - 1;
|
||||
if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2RU) {
|
||||
@@ -248,6 +323,7 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
utf_inlen = vhdm->sparse.par_loc_entry[i].plat_data_len;
|
||||
if (utf_inlen > MVHD_MAX_PATH_BYTES) {
|
||||
*err = MVHD_ERR_PATH_LEN;
|
||||
@@ -255,6 +331,7 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
|
||||
}
|
||||
mvhd_fseeko64(vhdm->f, vhdm->sparse.par_loc_entry[i].plat_data_offset, SEEK_SET);
|
||||
(void) !fread(paths->tmp_src_path, sizeof (uint8_t), utf_inlen, vhdm->f);
|
||||
|
||||
/* Note, the W2*u parent locators are UTF-16LE, unlike the filename field previously obtained,
|
||||
which is UTF-16BE */
|
||||
utf_ret = UTF16LEToUTF8(loc_path, &utf_outlen, (const unsigned char*)paths->tmp_src_path, &utf_inlen);
|
||||
@@ -263,22 +340,26 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
|
||||
goto paths_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have paths in UTF-8. We should have enough info to try and find the parent VHD */
|
||||
/* Does the relative path exist? */
|
||||
if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2RU)) {
|
||||
par_fp = tmp_open_path;
|
||||
goto paths_cleanup;
|
||||
}
|
||||
|
||||
/* What about trying the child directory? */
|
||||
if (mvhd_parent_path_exists(paths, 0)) {
|
||||
par_fp = tmp_open_path;
|
||||
goto paths_cleanup;
|
||||
}
|
||||
|
||||
/* Well, all else fails, try the stored absolute path, if it exists */
|
||||
if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2KU)) {
|
||||
par_fp = tmp_open_path;
|
||||
goto paths_cleanup;
|
||||
}
|
||||
|
||||
/* If we reach this point, we could not find a path with a valid file */
|
||||
par_fp = NULL;
|
||||
*err = MVHD_ERR_PAR_NOT_FOUND;
|
||||
@@ -286,10 +367,12 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
|
||||
paths_cleanup:
|
||||
free(paths);
|
||||
paths = NULL;
|
||||
|
||||
end:
|
||||
return par_fp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Attach the read/write function pointers to read/write functions
|
||||
*
|
||||
@@ -298,44 +381,90 @@ end:
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static void mvhd_assign_io_funcs(MVHDMeta* vhdm) {
|
||||
static void
|
||||
assign_io_funcs(MVHDMeta* vhdm)
|
||||
{
|
||||
switch (vhdm->footer.disk_type) {
|
||||
case MVHD_TYPE_FIXED:
|
||||
vhdm->read_sectors = mvhd_fixed_read;
|
||||
vhdm->write_sectors = mvhd_fixed_write;
|
||||
break;
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
vhdm->read_sectors = mvhd_sparse_read;
|
||||
vhdm->write_sectors = mvhd_sparse_diff_write;
|
||||
break;
|
||||
case MVHD_TYPE_DIFF:
|
||||
vhdm->read_sectors = mvhd_diff_read;
|
||||
vhdm->write_sectors = mvhd_sparse_diff_write;
|
||||
break;
|
||||
case MVHD_TYPE_FIXED:
|
||||
vhdm->read_sectors = mvhd_fixed_read;
|
||||
vhdm->write_sectors = mvhd_fixed_write;
|
||||
break;
|
||||
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
vhdm->read_sectors = mvhd_sparse_read;
|
||||
vhdm->write_sectors = mvhd_sparse_diff_write;
|
||||
break;
|
||||
|
||||
case MVHD_TYPE_DIFF:
|
||||
vhdm->read_sectors = mvhd_diff_read;
|
||||
vhdm->write_sectors = mvhd_sparse_diff_write;
|
||||
break;
|
||||
}
|
||||
if (vhdm->readonly) {
|
||||
|
||||
if (vhdm->readonly)
|
||||
vhdm->write_sectors = mvhd_noop_write;
|
||||
}
|
||||
}
|
||||
|
||||
bool mvhd_file_is_vhd(FILE* f) {
|
||||
if (f) {
|
||||
uint8_t con_str[8];
|
||||
mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END);
|
||||
(void) !fread(con_str, sizeof con_str, 1, f);
|
||||
return mvhd_is_conectix_str(con_str);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the library version as a string
|
||||
*/
|
||||
MVHDAPI const char *
|
||||
mvhd_version(void)
|
||||
{
|
||||
return LIB_VERSION_4;
|
||||
}
|
||||
|
||||
MVHDGeom mvhd_calculate_geometry(uint64_t size) {
|
||||
|
||||
/**
|
||||
* \brief Return the library version as a number
|
||||
*/
|
||||
MVHDAPI uint32_t
|
||||
mvhd_version_id(void)
|
||||
{
|
||||
return (LIB_VER_MAJOR << 24) | (LIB_VER_MINOR << 16) |
|
||||
(LIB_VER_REV << 16) | LIB_VER_PATCH;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief A simple test to see if a given file is a VHD
|
||||
*
|
||||
* \param [in] f file to test
|
||||
*
|
||||
* \retval 1 if f is a VHD
|
||||
* \retval 0 if f is not a VHD
|
||||
*/
|
||||
MVHDAPI int
|
||||
mvhd_file_is_vhd(FILE* f)
|
||||
{
|
||||
uint8_t con_str[8];
|
||||
|
||||
if (f == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END);
|
||||
fread(con_str, sizeof con_str, 1, f);
|
||||
if (mvhd_is_conectix_str(con_str)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
MVHDAPI MVHDGeom
|
||||
mvhd_calculate_geometry(uint64_t size)
|
||||
{
|
||||
MVHDGeom chs;
|
||||
uint32_t ts = (uint32_t)(size / MVHD_SECTOR_SIZE);
|
||||
uint32_t spt, heads, cyl, cth;
|
||||
|
||||
if (ts > 65535 * 16 * 255) {
|
||||
ts = 65535 * 16 * 255;
|
||||
}
|
||||
|
||||
if (ts >= 65535 * 16 * 63) {
|
||||
spt = 255;
|
||||
heads = 16;
|
||||
@@ -358,77 +487,95 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size) {
|
||||
cth = ts / spt;
|
||||
}
|
||||
}
|
||||
|
||||
cyl = cth / heads;
|
||||
chs.heads = heads;
|
||||
chs.spt = spt;
|
||||
chs.cyl = cyl;
|
||||
|
||||
return chs;
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) {
|
||||
|
||||
MVHDAPI MVHDMeta*
|
||||
mvhd_open(const char* path, int readonly, int* err)
|
||||
{
|
||||
MVHDError open_err;
|
||||
|
||||
MVHDMeta *vhdm = calloc(sizeof *vhdm, 1);
|
||||
if (vhdm == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strlen(path) >= sizeof vhdm->filename) {
|
||||
*err = MVHD_ERR_PATH_LEN;
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
|
||||
//This is safe, as we've just checked for potential overflow above
|
||||
strcpy(vhdm->filename, path);
|
||||
vhdm->f = readonly ? mvhd_fopen((const char*)vhdm->filename, "rb", err) : mvhd_fopen((const char*)vhdm->filename, "rb+", err);
|
||||
|
||||
if (readonly) {
|
||||
vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb", err);
|
||||
} else {
|
||||
vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb+", err);
|
||||
}
|
||||
if (vhdm->f == NULL) {
|
||||
/* note, mvhd_fopen sets err for us */
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
vhdm->readonly = readonly;
|
||||
|
||||
if (!mvhd_file_is_vhd(vhdm->f)) {
|
||||
*err = MVHD_ERR_NOT_VHD;
|
||||
goto cleanup_file;
|
||||
}
|
||||
mvhd_read_footer(vhdm);
|
||||
if (!mvhd_footer_checksum_valid(vhdm)) {
|
||||
|
||||
read_footer(vhdm);
|
||||
if (!footer_checksum_valid(vhdm)) {
|
||||
*err = MVHD_ERR_FOOTER_CHECKSUM;
|
||||
goto cleanup_file;
|
||||
}
|
||||
if (vhdm->footer.disk_type == MVHD_TYPE_DIFF || vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) {
|
||||
mvhd_read_sparse_header(vhdm);
|
||||
if (!mvhd_sparse_checksum_valid(vhdm)) {
|
||||
read_sparse_header(vhdm);
|
||||
if (!sparse_checksum_valid(vhdm)) {
|
||||
*err = MVHD_ERR_SPARSE_CHECKSUM;
|
||||
goto cleanup_file;
|
||||
}
|
||||
if (mvhd_read_bat(vhdm, &open_err) == -1) {
|
||||
if (read_bat(vhdm, &open_err) == -1) {
|
||||
*err = open_err;
|
||||
goto cleanup_file;
|
||||
}
|
||||
mvhd_calc_sparse_values(vhdm);
|
||||
if (mvhd_init_sector_bitmap(vhdm, &open_err) == -1) {
|
||||
calc_sparse_values(vhdm);
|
||||
if (init_sector_bitmap(vhdm, &open_err) == -1) {
|
||||
*err = open_err;
|
||||
goto cleanup_bat;
|
||||
}
|
||||
|
||||
} else if (vhdm->footer.disk_type != MVHD_TYPE_FIXED) {
|
||||
*err = MVHD_ERR_TYPE;
|
||||
goto cleanup_bitmap;
|
||||
}
|
||||
mvhd_assign_io_funcs(vhdm);
|
||||
assign_io_funcs(vhdm);
|
||||
|
||||
vhdm->format_buffer.zero_data = calloc(64, MVHD_SECTOR_SIZE);
|
||||
if (vhdm->format_buffer.zero_data == NULL) {
|
||||
*err = MVHD_ERR_MEM;
|
||||
goto cleanup_bitmap;
|
||||
}
|
||||
|
||||
vhdm->format_buffer.sector_count = 64;
|
||||
if (vhdm->footer.disk_type == MVHD_TYPE_DIFF) {
|
||||
char* par_path = mvhd_get_diff_parent_path(vhdm, err);
|
||||
char* par_path = get_diff_parent_path(vhdm, err);
|
||||
if (par_path == NULL) {
|
||||
goto cleanup_format_buff;
|
||||
}
|
||||
|
||||
uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err);
|
||||
if (*err != 0) {
|
||||
goto cleanup_format_buff;
|
||||
}
|
||||
|
||||
if (vhdm->sparse.par_timestamp != par_mod_ts) {
|
||||
/* The last-modified timestamp is to fragile to make this a fatal error.
|
||||
Instead, we inform the caller of the potential problem. */
|
||||
@@ -438,57 +585,78 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) {
|
||||
if (vhdm->parent == NULL) {
|
||||
goto cleanup_format_buff;
|
||||
}
|
||||
|
||||
if (memcmp(vhdm->sparse.par_uuid, vhdm->parent->footer.uuid, sizeof vhdm->sparse.par_uuid) != 0) {
|
||||
*err = MVHD_ERR_INVALID_PAR_UUID;
|
||||
goto cleanup_format_buff;
|
||||
}
|
||||
}
|
||||
/* If we've reached this point, we are good to go, so skip the cleanup steps */
|
||||
|
||||
/*
|
||||
* If we've reached this point, we are good to go,
|
||||
* so skip the cleanup steps.
|
||||
*/
|
||||
goto end;
|
||||
|
||||
cleanup_format_buff:
|
||||
free(vhdm->format_buffer.zero_data);
|
||||
vhdm->format_buffer.zero_data = NULL;
|
||||
|
||||
cleanup_bitmap:
|
||||
free(vhdm->bitmap.curr_bitmap);
|
||||
vhdm->bitmap.curr_bitmap = NULL;
|
||||
|
||||
cleanup_bat:
|
||||
free(vhdm->block_offset);
|
||||
vhdm->block_offset = NULL;
|
||||
|
||||
cleanup_file:
|
||||
fclose(vhdm->f);
|
||||
vhdm->f = NULL;
|
||||
|
||||
cleanup_vhdm:
|
||||
free(vhdm);
|
||||
vhdm = NULL;
|
||||
|
||||
end:
|
||||
return vhdm;
|
||||
}
|
||||
|
||||
void mvhd_close(MVHDMeta* vhdm) {
|
||||
if (vhdm != NULL) {
|
||||
if (vhdm->parent != NULL) {
|
||||
mvhd_close(vhdm->parent);
|
||||
}
|
||||
fclose(vhdm->f);
|
||||
if (vhdm->block_offset != NULL) {
|
||||
free(vhdm->block_offset);
|
||||
vhdm->block_offset = NULL;
|
||||
}
|
||||
if (vhdm->bitmap.curr_bitmap != NULL) {
|
||||
free(vhdm->bitmap.curr_bitmap);
|
||||
vhdm->bitmap.curr_bitmap = NULL;
|
||||
}
|
||||
if (vhdm->format_buffer.zero_data != NULL) {
|
||||
free(vhdm->format_buffer.zero_data);
|
||||
vhdm->format_buffer.zero_data = NULL;
|
||||
}
|
||||
free(vhdm);
|
||||
vhdm = NULL;
|
||||
|
||||
MVHDAPI void
|
||||
mvhd_close(MVHDMeta* vhdm)
|
||||
{
|
||||
if (vhdm == NULL)
|
||||
return;
|
||||
|
||||
if (vhdm->parent != NULL) {
|
||||
mvhd_close(vhdm->parent);
|
||||
}
|
||||
|
||||
fclose(vhdm->f);
|
||||
|
||||
if (vhdm->block_offset != NULL) {
|
||||
free(vhdm->block_offset);
|
||||
vhdm->block_offset = NULL;
|
||||
}
|
||||
if (vhdm->bitmap.curr_bitmap != NULL) {
|
||||
free(vhdm->bitmap.curr_bitmap);
|
||||
vhdm->bitmap.curr_bitmap = NULL;
|
||||
}
|
||||
if (vhdm->format_buffer.zero_data != NULL) {
|
||||
free(vhdm->format_buffer.zero_data);
|
||||
vhdm->format_buffer.zero_data = NULL;
|
||||
}
|
||||
|
||||
free(vhdm);
|
||||
}
|
||||
|
||||
int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
|
||||
|
||||
MVHDAPI int
|
||||
mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err)
|
||||
{
|
||||
uint8_t sparse_buff[1024];
|
||||
|
||||
if (vhdm == NULL || err == NULL) {
|
||||
*err = MVHD_ERR_INVALID_PARAMS;
|
||||
return -1;
|
||||
@@ -497,7 +665,7 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
|
||||
*err = MVHD_ERR_TYPE;
|
||||
return -1;
|
||||
}
|
||||
char* par_path = mvhd_get_diff_parent_path(vhdm, err);
|
||||
char* par_path = get_diff_parent_path(vhdm, err);
|
||||
if (par_path == NULL) {
|
||||
return -1;
|
||||
}
|
||||
@@ -505,31 +673,53 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
|
||||
if (*err != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update the timestamp and sparse header checksum */
|
||||
vhdm->sparse.par_timestamp = par_mod_ts;
|
||||
vhdm->sparse.checksum = mvhd_gen_sparse_checksum(&vhdm->sparse);
|
||||
|
||||
/* Generate and write the updated sparse header */
|
||||
mvhd_header_to_buffer(&vhdm->sparse, sparse_buff);
|
||||
mvhd_fseeko64(vhdm->f, (int64_t)vhdm->footer.data_offset, SEEK_SET);
|
||||
fwrite(sparse_buff, sizeof sparse_buff, 1, vhdm->f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
|
||||
MVHDAPI int
|
||||
mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff)
|
||||
{
|
||||
return vhdm->read_sectors(vhdm, offset, num_sectors, out_buff);
|
||||
}
|
||||
|
||||
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
|
||||
MVHDAPI int
|
||||
mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
|
||||
{
|
||||
return vhdm->write_sectors(vhdm, offset, num_sectors, in_buff);
|
||||
}
|
||||
|
||||
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors) {
|
||||
|
||||
MVHDAPI int
|
||||
mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors)
|
||||
{
|
||||
int num_full = num_sectors / vhdm->format_buffer.sector_count;
|
||||
int remain = num_sectors % vhdm->format_buffer.sector_count;
|
||||
|
||||
for (int i = 0; i < num_full; i++) {
|
||||
vhdm->write_sectors(vhdm, offset, vhdm->format_buffer.sector_count, vhdm->format_buffer.zero_data);
|
||||
offset += vhdm->format_buffer.sector_count;
|
||||
}
|
||||
|
||||
vhdm->write_sectors(vhdm, offset, remain, vhdm->format_buffer.zero_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
MVHDAPI MVHDType
|
||||
mvhd_get_type(MVHDMeta* vhdm)
|
||||
{
|
||||
return vhdm->footer.disk_type;
|
||||
}
|
||||
@@ -1,11 +1,49 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
* MiniVHD is a minimalist implementation of read/write/creation
|
||||
* of VHD files. It is designed to read and write to VHD files
|
||||
* at a sector level. It does not enable file access, or provide
|
||||
* mounting options. Those features are left to more advanced
|
||||
* libraries and/or the operating system.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Definitions for the MiniVHD library.
|
||||
*
|
||||
* Version: @(#)minivhd.h 1.0.3 2021/04/16
|
||||
*
|
||||
* Authors: Sherman Perry, <shermperry@gmail.com>
|
||||
* Fred N. van Kempen, <waltje@varcem.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
* Copyright 2021 Fred N. van Kempen.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MINIVHD_H
|
||||
#define MINIVHD_H
|
||||
# define MINIVHD_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern int mvhd_errno;
|
||||
|
||||
typedef enum MVHDError {
|
||||
MVHD_ERR_MEM = -128,
|
||||
@@ -46,6 +84,11 @@ typedef struct MVHDGeom {
|
||||
uint8_t spt;
|
||||
} MVHDGeom;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*mvhd_progress_callback)(uint32_t current_sector, uint32_t total_sectors);
|
||||
|
||||
typedef struct MVHDCreationOptions {
|
||||
@@ -60,6 +103,42 @@ typedef struct MVHDCreationOptions {
|
||||
|
||||
typedef struct MVHDMeta MVHDMeta;
|
||||
|
||||
|
||||
extern int mvhd_errno;
|
||||
|
||||
|
||||
/* Shared-library madness. */
|
||||
//#if defined(_WIN32)
|
||||
//# ifdef STATIC
|
||||
# define MVHDAPI /*nothing*/
|
||||
//# else
|
||||
//# ifdef BUILDING_LIBRARY
|
||||
//# define MVHDAPI __declspec(dllexport)
|
||||
//# else
|
||||
//# define MVHDAPI __declspec(dllimport)
|
||||
//# endif
|
||||
//# endif
|
||||
//#elif defined(__GNUC__)
|
||||
//# ifdef BUILDING_LIBRARY
|
||||
//# define MVHDAPI __attribute__((visibility("default")))
|
||||
//# else
|
||||
//# define MVHDAPI /*nothing*/
|
||||
//# endif
|
||||
//#else
|
||||
//# define MVHDAPI /*nothing*/
|
||||
//#endif
|
||||
|
||||
|
||||
/**
|
||||
* \brief Return the library version as a string
|
||||
*/
|
||||
MVHDAPI const char *mvhd_version(void);
|
||||
|
||||
/**
|
||||
* \brief Return the library version as a number
|
||||
*/
|
||||
MVHDAPI uint32_t mvhd_version_id(void);
|
||||
|
||||
/**
|
||||
* \brief Output a string from a MiniVHD error number
|
||||
*
|
||||
@@ -67,17 +146,26 @@ typedef struct MVHDMeta MVHDMeta;
|
||||
*
|
||||
* \return Error string
|
||||
*/
|
||||
const char* mvhd_strerr(MVHDError err);
|
||||
MVHDAPI const char* mvhd_strerr(MVHDError err);
|
||||
|
||||
/**
|
||||
* \brief A simple test to see if a given file is a VHD
|
||||
*
|
||||
* \param [in] f file to test
|
||||
*
|
||||
* \retval true if f is a VHD
|
||||
* \retval false if f is not a VHD
|
||||
* \retval 1 if f is a VHD
|
||||
* \retval 0 if f is not a VHD
|
||||
*/
|
||||
bool mvhd_file_is_vhd(FILE* f);
|
||||
MVHDAPI int mvhd_file_is_vhd(FILE* f);
|
||||
|
||||
/**
|
||||
* \brief Return the file type of the given file
|
||||
*
|
||||
* \param [in] vhdm VHD to check.
|
||||
*
|
||||
* \retval one of the defined MVHDType values
|
||||
*/
|
||||
MVHDAPI MVHDType mvhd_get_type(MVHDMeta* vhdm);
|
||||
|
||||
/**
|
||||
* \brief Open a VHD image for reading and/or writing
|
||||
@@ -89,7 +177,7 @@ bool mvhd_file_is_vhd(FILE* f);
|
||||
*
|
||||
* \param [in] Absolute path to VHD file. Relative path will cause issues when opening
|
||||
* a differencing VHD file
|
||||
* \param [in] readonly set this to true to open the VHD in a read only manner
|
||||
* \param [in] readonly set this to 1 to open the VHD in a read only manner
|
||||
* \param [out] err will be set if the VHD fails to open. Value could be one of
|
||||
* MVHD_ERR_MEM, MVHD_ERR_FILE, MVHD_ERR_NOT_VHD, MVHD_ERR_FOOTER_CHECKSUM, MVHD_ERR_SPARSE_CHECKSUM,
|
||||
* MVHD_ERR_TYPE, MVHD_ERR_TIMESTAMP
|
||||
@@ -98,7 +186,7 @@ bool mvhd_file_is_vhd(FILE* f);
|
||||
* \return MVHDMeta pointer. If NULL, check err. err may also be set to MVHD_ERR_TIMESTAMP if
|
||||
* opening a differencing VHD.
|
||||
*/
|
||||
MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
|
||||
MVHDAPI MVHDMeta* mvhd_open(const char* path, int readonly, int* err);
|
||||
|
||||
/**
|
||||
* \brief Update the parent modified timestamp in the VHD file
|
||||
@@ -116,7 +204,7 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
|
||||
*
|
||||
* \return non-zero on error, 0 on success
|
||||
*/
|
||||
int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
|
||||
MVHDAPI int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
|
||||
|
||||
/**
|
||||
* \brief Create a fixed VHD image
|
||||
@@ -128,7 +216,7 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
|
||||
*
|
||||
* \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback);
|
||||
MVHDAPI MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback);
|
||||
|
||||
/**
|
||||
* \brief Create sparse (dynamic) VHD image.
|
||||
@@ -139,7 +227,7 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_prog
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
|
||||
MVHDAPI MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
|
||||
|
||||
/**
|
||||
* \brief Create differencing VHD imagee.
|
||||
@@ -150,7 +238,7 @@ MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
|
||||
MVHDAPI MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
|
||||
|
||||
/**
|
||||
* \brief Create a VHD using the provided options
|
||||
@@ -162,14 +250,14 @@ MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
|
||||
*
|
||||
* \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err);
|
||||
MVHDAPI MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err);
|
||||
|
||||
/**
|
||||
* \brief Safely close a VHD image
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure to close
|
||||
*/
|
||||
void mvhd_close(MVHDMeta* vhdm);
|
||||
MVHDAPI void mvhd_close(MVHDMeta* vhdm);
|
||||
|
||||
/**
|
||||
* \brief Calculate hard disk geometry from a provided size
|
||||
@@ -189,7 +277,47 @@ void mvhd_close(MVHDMeta* vhdm);
|
||||
*
|
||||
* \return MVHDGeom the calculated geometry. This can be used in the appropriate create functions.
|
||||
*/
|
||||
MVHDGeom mvhd_calculate_geometry(uint64_t size);
|
||||
MVHDAPI MVHDGeom mvhd_calculate_geometry(uint64_t size);
|
||||
|
||||
/**
|
||||
* \brief Get the CHS geometry from the image
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*
|
||||
* \return The CHS geometry as stored in the image
|
||||
*/
|
||||
MVHDAPI MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm);
|
||||
|
||||
/**
|
||||
* \brief Get the 'current_size' value from the image
|
||||
*
|
||||
* Note that the size returned may not match the size calculated from the
|
||||
* CHS geometry. It is up to the caller to decide how best to handle this.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*
|
||||
* \return The 'current_size' value in bytes, as stored in the image.
|
||||
* Note, this may not match the CHS geometry.
|
||||
*/
|
||||
MVHDAPI uint64_t mvhd_get_current_size(MVHDMeta* vhdm);
|
||||
|
||||
/**
|
||||
* \brief Calculate CHS geometry size in bytes
|
||||
*
|
||||
* \param [in] geom the CHS geometry to calculate
|
||||
*
|
||||
* \return the size in bytes
|
||||
*/
|
||||
MVHDAPI uint64_t mvhd_calc_size_bytes(MVHDGeom *geom);
|
||||
|
||||
/**
|
||||
* \brief Calculate CHS geometry size in sectors
|
||||
*
|
||||
* \param [in] geom the CHS geometry to calculate
|
||||
*
|
||||
* \return the size in sectors
|
||||
*/
|
||||
MVHDAPI uint32_t mvhd_calc_size_sectors(MVHDGeom *geom);
|
||||
|
||||
/**
|
||||
* \brief Convert a raw disk image to a fixed VHD image
|
||||
@@ -200,7 +328,7 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size);
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
|
||||
MVHDAPI MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
|
||||
|
||||
/**
|
||||
* \brief Convert a raw disk image to a sparse VHD image
|
||||
@@ -211,7 +339,7 @@ MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
|
||||
MVHDAPI MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err);
|
||||
|
||||
/**
|
||||
* \brief Convert a VHD image to a raw disk image
|
||||
@@ -222,7 +350,7 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns the raw disk image FILE pointer
|
||||
*/
|
||||
FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err);
|
||||
MVHDAPI FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err);
|
||||
|
||||
/**
|
||||
* \brief Read sectors from VHD file
|
||||
@@ -236,7 +364,7 @@ FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path,
|
||||
*
|
||||
* \return the number of sectors that were not read, or zero
|
||||
*/
|
||||
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
MVHDAPI int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Write sectors to VHD file
|
||||
@@ -250,7 +378,7 @@ int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* ou
|
||||
*
|
||||
* \return the number of sectors that were not written, or zero
|
||||
*/
|
||||
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
MVHDAPI int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief Write zeroed sectors to VHD file
|
||||
@@ -265,5 +393,11 @@ int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* i
|
||||
*
|
||||
* \return the number of sectors that were not written, or zero
|
||||
*/
|
||||
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
|
||||
MVHDAPI int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*MINIVHD_H*/
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef MINIVHD_CREATE_H
|
||||
#define MINIVHD_CREATE_H
|
||||
#include <stdio.h>
|
||||
#include "minivhd.h"
|
||||
|
||||
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback);
|
||||
|
||||
#endif
|
||||
@@ -1,96 +0,0 @@
|
||||
#ifndef MINIVHD_INTERNAL_H
|
||||
#define MINIVHD_INTERNAL_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MVHD_FOOTER_SIZE 512
|
||||
#define MVHD_SPARSE_SIZE 1024
|
||||
|
||||
#define MVHD_SECTOR_SIZE 512
|
||||
#define MVHD_BAT_ENT_PER_SECT 128
|
||||
|
||||
#define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000
|
||||
|
||||
#define MVHD_SPARSE_BLK 0xffffffff
|
||||
/* For simplicity, we don't handle paths longer than this
|
||||
* Note, this is the max path in characters, as that is what
|
||||
* Windows uses
|
||||
*/
|
||||
#define MVHD_MAX_PATH_CHARS 260
|
||||
#define MVHD_MAX_PATH_BYTES 1040
|
||||
|
||||
#define MVHD_DIF_LOC_W2RU 0x57327275
|
||||
#define MVHD_DIF_LOC_W2KU 0x57326B75
|
||||
|
||||
typedef struct MVHDSectorBitmap {
|
||||
uint8_t* curr_bitmap;
|
||||
int sector_count;
|
||||
int curr_block;
|
||||
} MVHDSectorBitmap;
|
||||
|
||||
typedef struct MVHDFooter {
|
||||
uint8_t cookie[8];
|
||||
uint32_t features;
|
||||
uint32_t fi_fmt_vers;
|
||||
uint64_t data_offset;
|
||||
uint32_t timestamp;
|
||||
uint8_t cr_app[4];
|
||||
uint32_t cr_vers;
|
||||
uint8_t cr_host_os[4];
|
||||
uint64_t orig_sz;
|
||||
uint64_t curr_sz;
|
||||
struct {
|
||||
uint16_t cyl;
|
||||
uint8_t heads;
|
||||
uint8_t spt;
|
||||
} geom;
|
||||
uint32_t disk_type;
|
||||
uint32_t checksum;
|
||||
uint8_t uuid[16];
|
||||
uint8_t saved_st;
|
||||
uint8_t reserved[427];
|
||||
} MVHDFooter;
|
||||
|
||||
typedef struct MVHDSparseHeader {
|
||||
uint8_t cookie[8];
|
||||
uint64_t data_offset;
|
||||
uint64_t bat_offset;
|
||||
uint32_t head_vers;
|
||||
uint32_t max_bat_ent;
|
||||
uint32_t block_sz;
|
||||
uint32_t checksum;
|
||||
uint8_t par_uuid[16];
|
||||
uint32_t par_timestamp;
|
||||
uint32_t reserved_1;
|
||||
uint8_t par_utf16_name[512];
|
||||
struct {
|
||||
uint32_t plat_code;
|
||||
uint32_t plat_data_space;
|
||||
uint32_t plat_data_len;
|
||||
uint32_t reserved;
|
||||
uint64_t plat_data_offset;
|
||||
} par_loc_entry[8];
|
||||
uint8_t reserved_2[256];
|
||||
} MVHDSparseHeader;
|
||||
|
||||
typedef struct MVHDMeta MVHDMeta;
|
||||
struct MVHDMeta {
|
||||
FILE* f;
|
||||
bool readonly;
|
||||
char filename[MVHD_MAX_PATH_BYTES];
|
||||
struct MVHDMeta* parent;
|
||||
MVHDFooter footer;
|
||||
MVHDSparseHeader sparse;
|
||||
uint32_t* block_offset;
|
||||
int sect_per_block;
|
||||
MVHDSectorBitmap bitmap;
|
||||
int (*read_sectors)(MVHDMeta*, uint32_t, int, void*);
|
||||
int (*write_sectors)(MVHDMeta*, uint32_t, int, void*);
|
||||
struct {
|
||||
uint8_t* zero_data;
|
||||
int sector_count;
|
||||
} format_buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,28 +1,61 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief Sector reading and writing implementations
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Sector reading and writing implementations.
|
||||
*
|
||||
* Version: @(#)io.c 1.0.3 2021/04/16
|
||||
*
|
||||
* Author: Sherman Perry, <shermperry@gmail.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "minivhd_internal.h"
|
||||
#include "minivhd_util.h"
|
||||
#include <time.h>
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* The following bit array macros adapted from
|
||||
http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html */
|
||||
|
||||
#define VHD_SETBIT(A,k) ( A[(k/8)] |= (0x80 >> (k%8)) )
|
||||
#define VHD_CLEARBIT(A,k) ( A[(k/8)] &= ~(0x80 >> (k%8)) )
|
||||
#define VHD_TESTBIT(A,k) ( A[(k/8)] & (0x80 >> (k%8)) )
|
||||
/*
|
||||
* The following bit array macros adapted from:
|
||||
*
|
||||
* http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html
|
||||
*/
|
||||
#define VHD_SETBIT(A,k) ( A[(k>>3)] |= (0x80 >> (k&7)) )
|
||||
#define VHD_CLEARBIT(A,k) ( A[(k>>3)] &= ~(0x80 >> (k&7)) )
|
||||
#define VHD_TESTBIT(A,k) ( A[(k>>3)] & (0x80 >> (k&7)) )
|
||||
|
||||
static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect);
|
||||
static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk);
|
||||
static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk);
|
||||
static void mvhd_create_block(MVHDMeta* vhdm, int blk);
|
||||
static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm);
|
||||
|
||||
/**
|
||||
* \brief Check that we will not be overflowing buffers
|
||||
@@ -34,22 +67,30 @@ static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm);
|
||||
* This may be lower than num_sectors if offset + num_sectors >= total_sectors
|
||||
* \param [out] trunc_sectors The number of sectors truncated if transfer_sectors < num_sectors
|
||||
*/
|
||||
static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect) {
|
||||
static inline void
|
||||
check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect)
|
||||
{
|
||||
*transfer_sect = num_sectors;
|
||||
*trunc_sect = 0;
|
||||
|
||||
if ((total_sectors - offset) < (uint32_t)*transfer_sect) {
|
||||
*transfer_sect = total_sectors - offset;
|
||||
*trunc_sect = num_sectors - *transfer_sect;
|
||||
}
|
||||
}
|
||||
|
||||
void mvhd_write_empty_sectors(FILE* f, int sector_count) {
|
||||
|
||||
void
|
||||
mvhd_write_empty_sectors(FILE* f, int sector_count)
|
||||
{
|
||||
uint8_t zero_bytes[MVHD_SECTOR_SIZE] = {0};
|
||||
|
||||
for (int i = 0; i < sector_count; i++) {
|
||||
fwrite(zero_bytes, sizeof zero_bytes, 1, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Read the sector bitmap for a block.
|
||||
*
|
||||
@@ -59,22 +100,28 @@ void mvhd_write_empty_sectors(FILE* f, int sector_count) {
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] blk The block for which to read the sector bitmap from
|
||||
*/
|
||||
static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk) {
|
||||
static void
|
||||
read_sect_bitmap(MVHDMeta* vhdm, int blk)
|
||||
{
|
||||
if (vhdm->block_offset[blk] != MVHD_SPARSE_BLK) {
|
||||
mvhd_fseeko64(vhdm->f, (uint64_t)vhdm->block_offset[blk] * MVHD_SECTOR_SIZE, SEEK_SET);
|
||||
(void) !fread(vhdm->bitmap.curr_bitmap, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE, 1, vhdm->f);
|
||||
} else {
|
||||
memset(vhdm->bitmap.curr_bitmap, 0, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
vhdm->bitmap.curr_block = blk;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Write the current sector bitmap in memory to file
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) {
|
||||
static void
|
||||
write_curr_sect_bitmap(MVHDMeta* vhdm)
|
||||
{
|
||||
if (vhdm->bitmap.curr_block >= 0) {
|
||||
int64_t abs_offset = (int64_t)vhdm->block_offset[vhdm->bitmap.curr_block] * MVHD_SECTOR_SIZE;
|
||||
mvhd_fseeko64(vhdm->f, abs_offset, SEEK_SET);
|
||||
@@ -82,19 +129,24 @@ static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Write block offset from memory into file
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] blk The block for which to write the offset for
|
||||
*/
|
||||
static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) {
|
||||
static void
|
||||
write_bat_entry(MVHDMeta* vhdm, int blk)
|
||||
{
|
||||
uint64_t table_offset = vhdm->sparse.bat_offset + ((uint64_t)blk * sizeof *vhdm->block_offset);
|
||||
uint32_t offset = mvhd_to_be32(vhdm->block_offset[blk]);
|
||||
|
||||
mvhd_fseeko64(vhdm->f, table_offset, SEEK_SET);
|
||||
fwrite(&offset, sizeof offset, 1, vhdm->f);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Create an empty block in a sparse or differencing VHD image
|
||||
*
|
||||
@@ -109,18 +161,23 @@ static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) {
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] blk The block number to create
|
||||
*/
|
||||
static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
|
||||
static void
|
||||
create_block(MVHDMeta* vhdm, int blk)
|
||||
{
|
||||
uint8_t footer[MVHD_FOOTER_SIZE];
|
||||
|
||||
/* Seek to where the footer SHOULD be */
|
||||
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
|
||||
(void) !fread(footer, sizeof footer, 1, vhdm->f);
|
||||
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
|
||||
|
||||
if (!mvhd_is_conectix_str(footer)) {
|
||||
/* Oh dear. We use the header instead, since something has gone wrong at the footer */
|
||||
mvhd_fseeko64(vhdm->f, 0, SEEK_SET);
|
||||
(void) !fread(footer, sizeof footer, 1, vhdm->f);
|
||||
mvhd_fseeko64(vhdm->f, 0, SEEK_END);
|
||||
}
|
||||
|
||||
int64_t abs_offset = mvhd_ftello64(vhdm->f);
|
||||
if (abs_offset % MVHD_SECTOR_SIZE != 0) {
|
||||
/* Yikes! We're supposed to be on a sector boundary. Add some padding */
|
||||
@@ -131,52 +188,68 @@ static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
|
||||
}
|
||||
abs_offset += padding_amount;
|
||||
}
|
||||
|
||||
uint32_t sect_offset = (uint32_t)(abs_offset / MVHD_SECTOR_SIZE);
|
||||
int blk_size_sectors = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE;
|
||||
mvhd_write_empty_sectors(vhdm->f, vhdm->bitmap.sector_count + blk_size_sectors);
|
||||
|
||||
/* Add a bit of padding. That's what Windows appears to do, although it's not strictly necessary... */
|
||||
mvhd_write_empty_sectors(vhdm->f, 5);
|
||||
|
||||
/* And we finish with the footer */
|
||||
fwrite(footer, sizeof footer, 1, vhdm->f);
|
||||
|
||||
/* We no longer have a sparse block. Update that BAT! */
|
||||
vhdm->block_offset[blk] = sect_offset;
|
||||
mvhd_write_bat_entry(vhdm, blk);
|
||||
write_bat_entry(vhdm, blk);
|
||||
}
|
||||
|
||||
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
|
||||
int
|
||||
mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
int64_t addr;
|
||||
int transfer_sectors, truncated_sectors;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
addr = (int64_t)offset * MVHD_SECTOR_SIZE;
|
||||
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
|
||||
(void) !fread(out_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f);
|
||||
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
|
||||
int
|
||||
mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff)
|
||||
{
|
||||
int transfer_sectors, truncated_sectors;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
uint8_t* buff = (uint8_t*)out_buff;
|
||||
int64_t addr;
|
||||
uint32_t s, ls;
|
||||
int blk, prev_blk, sib;
|
||||
ls = offset + transfer_sectors;
|
||||
prev_blk = -1;
|
||||
|
||||
for (s = offset; s < ls; s++) {
|
||||
blk = s / vhdm->sect_per_block;
|
||||
sib = s % vhdm->sect_per_block;
|
||||
if (blk != prev_blk) {
|
||||
prev_blk = blk;
|
||||
if (vhdm->bitmap.curr_block != blk) {
|
||||
mvhd_read_sect_bitmap(vhdm, blk);
|
||||
read_sect_bitmap(vhdm, blk);
|
||||
mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR);
|
||||
} else {
|
||||
addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE;
|
||||
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
if (VHD_TESTBIT(vhdm->bitmap.curr_bitmap, sib)) {
|
||||
(void) !fread(buff, MVHD_SECTOR_SIZE, 1, vhdm->f);
|
||||
} else {
|
||||
@@ -185,29 +258,37 @@ int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out
|
||||
}
|
||||
buff += MVHD_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
|
||||
int
|
||||
mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff)
|
||||
{
|
||||
int transfer_sectors, truncated_sectors;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
uint8_t* buff = (uint8_t*)out_buff;
|
||||
MVHDMeta* curr_vhdm = vhdm;
|
||||
uint32_t s, ls;
|
||||
int blk, sib;
|
||||
ls = offset + transfer_sectors;
|
||||
|
||||
for (s = offset; s < ls; s++) {
|
||||
while (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF) {
|
||||
blk = s / curr_vhdm->sect_per_block;
|
||||
sib = s % curr_vhdm->sect_per_block;
|
||||
if (curr_vhdm->bitmap.curr_block != blk) {
|
||||
mvhd_read_sect_bitmap(curr_vhdm, blk);
|
||||
read_sect_bitmap(curr_vhdm, blk);
|
||||
}
|
||||
if (!VHD_TESTBIT(curr_vhdm->bitmap.curr_bitmap, sib)) {
|
||||
curr_vhdm = curr_vhdm->parent;
|
||||
} else { break; }
|
||||
}
|
||||
|
||||
/* We handle actual sector reading using the fixed or sparse functions,
|
||||
as a differencing VHD is also a sparse VHD */
|
||||
if (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF || curr_vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) {
|
||||
@@ -215,49 +296,65 @@ int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_b
|
||||
} else {
|
||||
mvhd_fixed_read(curr_vhdm, s, 1, buff);
|
||||
}
|
||||
|
||||
curr_vhdm = vhdm;
|
||||
buff += MVHD_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
|
||||
int
|
||||
mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
|
||||
{
|
||||
int64_t addr;
|
||||
int transfer_sectors, truncated_sectors;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
addr = (int64_t)offset * MVHD_SECTOR_SIZE;
|
||||
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
|
||||
fwrite(in_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f);
|
||||
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
|
||||
int
|
||||
mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
|
||||
{
|
||||
int transfer_sectors, truncated_sectors;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
|
||||
uint8_t* buff = (uint8_t*)in_buff;
|
||||
int64_t addr;
|
||||
uint32_t s, ls;
|
||||
int blk, prev_blk, sib;
|
||||
ls = offset + transfer_sectors;
|
||||
prev_blk = -1;
|
||||
|
||||
for (s = offset; s < ls; s++) {
|
||||
blk = s / vhdm->sect_per_block;
|
||||
sib = s % vhdm->sect_per_block;
|
||||
if (vhdm->bitmap.curr_block != blk && prev_blk >= 0) {
|
||||
/* Write the sector bitmap for the previous block, before we replace it. */
|
||||
mvhd_write_curr_sect_bitmap(vhdm);
|
||||
write_curr_sect_bitmap(vhdm);
|
||||
}
|
||||
|
||||
if (vhdm->block_offset[blk] == MVHD_SPARSE_BLK) {
|
||||
/* "read" the sector bitmap first, before creating a new block, as the bitmap will be
|
||||
zero either way */
|
||||
mvhd_read_sect_bitmap(vhdm, blk);
|
||||
mvhd_create_block(vhdm, blk);
|
||||
read_sect_bitmap(vhdm, blk);
|
||||
create_block(vhdm, blk);
|
||||
}
|
||||
|
||||
if (blk != prev_blk) {
|
||||
if (vhdm->bitmap.curr_block != blk) {
|
||||
mvhd_read_sect_bitmap(vhdm, blk);
|
||||
read_sect_bitmap(vhdm, blk);
|
||||
mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR);
|
||||
} else {
|
||||
addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE;
|
||||
@@ -265,15 +362,26 @@ int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, voi
|
||||
}
|
||||
prev_blk = blk;
|
||||
}
|
||||
|
||||
fwrite(buff, MVHD_SECTOR_SIZE, 1, vhdm->f);
|
||||
VHD_SETBIT(vhdm->bitmap.curr_bitmap, sib);
|
||||
buff += MVHD_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* And write the sector bitmap for the last block we visited to disk */
|
||||
mvhd_write_curr_sect_bitmap(vhdm);
|
||||
write_curr_sect_bitmap(vhdm);
|
||||
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
|
||||
int
|
||||
mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
|
||||
{
|
||||
(void)vhdm;
|
||||
(void)offset;
|
||||
(void)num_sectors;
|
||||
(void)in_buff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
#ifndef MINIVHD_IO_H
|
||||
#define MINIVHD_IO_H
|
||||
#include "minivhd.h"
|
||||
|
||||
/**
|
||||
* \brief Write zero filled sectors to file.
|
||||
*
|
||||
* Note, the caller should set the file position before calling this
|
||||
* function for correct operation.
|
||||
*
|
||||
* \param [in] f File to write sectors to
|
||||
* \param [in] sector_count The number of sectors to write
|
||||
*/
|
||||
void mvhd_write_empty_sectors(FILE* f, int sector_count);
|
||||
|
||||
/**
|
||||
* \brief Read a fixed VHD image
|
||||
*
|
||||
* Fixed VHD images are essentially raw image files with a footer tacked on
|
||||
* the end. They are therefore straightforward to write
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to read from
|
||||
* \param [in] num_sectors The desired number of sectors to read
|
||||
* \param [out] out_buff An output buffer to store read sectors. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Read a sparse VHD image
|
||||
*
|
||||
* Sparse, or dynamic images are VHD images that grow as data is written to them.
|
||||
*
|
||||
* This function implements the logic to read sectors from the file, taking into
|
||||
* account the fact that blocks may be stored on disk in any order, and that the
|
||||
* read could cross block boundaries.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to read from
|
||||
* \param [in] num_sectors The desired number of sectors to read
|
||||
* \param [out] out_buff An output buffer to store read sectors. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Read a differencing VHD image
|
||||
*
|
||||
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
|
||||
* properties of sparse images, but also reference a parent image. Data is read from the
|
||||
* child image only if it is newer than the data stored in the parent image.
|
||||
*
|
||||
* This function implements the logic to read sectors from the child, or a parent image.
|
||||
* Differencing images may have a differencing image as a parent, creating a chain of images.
|
||||
* There is no theoretical chain length limit, although I do not consider long chains to be
|
||||
* advisable. Verifying the parent-child relationship is not very robust.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to read from
|
||||
* \param [in] num_sectors The desired number of sectors to read
|
||||
* \param [out] out_buff An output buffer to store read sectors. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Write to a fixed VHD image
|
||||
*
|
||||
* Fixed VHD images are essentially raw image files with a footer tacked on
|
||||
* the end. They are therefore straightforward to write
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to write to
|
||||
* \param [in] num_sectors The desired number of sectors to write
|
||||
* \param [in] in_buff A source buffer to write sectors from. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief Write to a sparse or differencing VHD image
|
||||
*
|
||||
* Sparse, or dynamic images are VHD images that grow as data is written to them.
|
||||
*
|
||||
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
|
||||
* properties of sparse images, but also reference a parent image. Data is always written
|
||||
* to the child image. This makes writing to differencing images essentially identical to
|
||||
* writing to sparse images, hence they use the same function.
|
||||
*
|
||||
* This function implements the logic to write sectors to the file, taking into
|
||||
* account the fact that blocks may be stored on disk in any order, and that the
|
||||
* write operation could cross block boundaries.
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to write to
|
||||
* \param [in] num_sectors The desired number of sectors to write
|
||||
* \param [in] in_buff A source buffer to write sectors from. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief A no-op function to "write" to read-only VHD images
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
* \param [in] offset Sector offset to write to
|
||||
* \param [in] num_sectors The desired number of sectors to write
|
||||
* \param [in] in_buff A source buffer to write sectors from. Must be
|
||||
* large enough to hold num_sectors worth of sectors.
|
||||
*
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
#endif
|
||||
@@ -1,167 +0,0 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief Header and footer serialize/deserialize functions
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "minivhd_util.h"
|
||||
#include "minivhd_internal.h"
|
||||
|
||||
/* Read data from footer into the struct members, swapping endian where necessary
|
||||
Note: order matters here! We must read each field in the order the struct is in.
|
||||
Doing this may be less elegant than performing a memcpy to a packed struct, but
|
||||
it avoids potential data alignment issues, and the endian swapping allows us to
|
||||
use the fields directly. */
|
||||
|
||||
static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer);
|
||||
static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer);
|
||||
|
||||
/**
|
||||
* \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary
|
||||
*
|
||||
* \param [out] struct_memb struct member to save the field to
|
||||
* \param [in] memb_size the size of struct_memb, in bytes
|
||||
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
|
||||
* \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call
|
||||
*/
|
||||
static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) {
|
||||
memcpy(struct_memb, *buffer, memb_size);
|
||||
if (req_endian) {
|
||||
switch (memb_size) {
|
||||
case 2:
|
||||
*(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb));
|
||||
break;
|
||||
case 4:
|
||||
*(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb));
|
||||
break;
|
||||
case 8:
|
||||
*(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb));
|
||||
break;
|
||||
}
|
||||
}
|
||||
*buffer += memb_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Save a struct member into a buffer, converting endian if necessary
|
||||
*
|
||||
* \param [in] struct_memb struct member read from
|
||||
* \param [in] memb_size the size of struct_memb, in bytes
|
||||
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
|
||||
* \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call
|
||||
*/
|
||||
static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) {
|
||||
uint8_t *buf_ptr = *buffer;
|
||||
memcpy(buf_ptr, struct_memb, memb_size);
|
||||
if (req_endian) {
|
||||
switch (memb_size) {
|
||||
case 2:
|
||||
*((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb));
|
||||
break;
|
||||
case 4:
|
||||
*((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb));
|
||||
break;
|
||||
case 8:
|
||||
*((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb));
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf_ptr += memb_size;
|
||||
*buffer = buf_ptr;
|
||||
}
|
||||
|
||||
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer) {
|
||||
uint8_t* buff_ptr = buffer;
|
||||
mvhd_next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
|
||||
}
|
||||
|
||||
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer) {
|
||||
uint8_t* buff_ptr = buffer;
|
||||
mvhd_next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
|
||||
}
|
||||
|
||||
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer) {
|
||||
uint8_t* buff_ptr = buffer;
|
||||
mvhd_next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
|
||||
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
|
||||
}
|
||||
mvhd_next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
|
||||
}
|
||||
|
||||
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer) {
|
||||
uint8_t* buff_ptr = buffer;
|
||||
mvhd_next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
|
||||
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
|
||||
}
|
||||
mvhd_next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef MINIVHD_STRUCT_RW_H
|
||||
#define MINIVHD_STRUCT_RW_H
|
||||
|
||||
#include "minivhd_internal.h"
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD footer from a buffer to a struct
|
||||
*
|
||||
* \param [out] footer save contents of buffer into footer
|
||||
* \param [in] buffer VHD footer in raw bytes
|
||||
*/
|
||||
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD sparse header from a buffer to a struct
|
||||
*
|
||||
* \param [out] header save contents of buffer into header
|
||||
* \param [in] buffer VHD header in raw bytes
|
||||
*/
|
||||
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD footer struct to a buffer
|
||||
*
|
||||
* \param [in] footer save contents of struct into buffer
|
||||
* \param [out] buffer VHD footer in raw bytes
|
||||
*/
|
||||
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer);
|
||||
|
||||
/**
|
||||
* \brief Save the contents of a VHD sparse header struct to a buffer
|
||||
*
|
||||
* \param [in] header save contents of struct into buffer
|
||||
* \param [out] buffer VHD sparse header in raw bytes
|
||||
*/
|
||||
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer);
|
||||
|
||||
#endif
|
||||
@@ -1,46 +1,90 @@
|
||||
/**
|
||||
* \file
|
||||
* \brief Utility functions
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Utility functions.
|
||||
*
|
||||
* Version: @(#)util.c 1.0.4 2021/04/16
|
||||
*
|
||||
* Author: Sherman Perry, <shermperry@gmail.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "libxml2_encoding.h"
|
||||
#include "minivhd_internal.h"
|
||||
#include "minivhd_util.h"
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
#include "xml2_encoding.h"
|
||||
|
||||
const char MVHD_CONECTIX_COOKIE[] = "conectix";
|
||||
const char MVHD_CREATOR[] = "pcem";
|
||||
const char MVHD_CREATOR_HOST_OS[] = "Wi2k";
|
||||
const char MVHD_CXSPARSE_COOKIE[] = "cxsparse";
|
||||
|
||||
uint16_t mvhd_from_be16(uint16_t val) {
|
||||
uint16_t
|
||||
mvhd_from_be16(uint16_t val)
|
||||
{
|
||||
uint8_t *tmp = (uint8_t*)&val;
|
||||
uint16_t ret = 0;
|
||||
|
||||
ret |= (uint16_t)tmp[0] << 8;
|
||||
ret |= (uint16_t)tmp[1] << 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
uint32_t mvhd_from_be32(uint32_t val) {
|
||||
|
||||
|
||||
uint32_t
|
||||
mvhd_from_be32(uint32_t val)
|
||||
{
|
||||
uint8_t *tmp = (uint8_t*)&val;
|
||||
uint32_t ret = 0;
|
||||
|
||||
ret = (uint32_t)tmp[0] << 24;
|
||||
ret |= (uint32_t)tmp[1] << 16;
|
||||
ret |= (uint32_t)tmp[2] << 8;
|
||||
ret |= (uint32_t)tmp[3] << 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
uint64_t mvhd_from_be64(uint64_t val) {
|
||||
|
||||
|
||||
uint64_t
|
||||
mvhd_from_be64(uint64_t val)
|
||||
{
|
||||
uint8_t *tmp = (uint8_t*)&val;
|
||||
uint64_t ret = 0;
|
||||
|
||||
ret = (uint64_t)tmp[0] << 56;
|
||||
ret |= (uint64_t)tmp[1] << 48;
|
||||
ret |= (uint64_t)tmp[2] << 40;
|
||||
@@ -49,27 +93,45 @@ uint64_t mvhd_from_be64(uint64_t val) {
|
||||
ret |= (uint64_t)tmp[5] << 16;
|
||||
ret |= (uint64_t)tmp[6] << 8;
|
||||
ret |= (uint64_t)tmp[7] << 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
uint16_t mvhd_to_be16(uint16_t val) {
|
||||
|
||||
|
||||
uint16_t
|
||||
mvhd_to_be16(uint16_t val)
|
||||
{
|
||||
uint16_t ret = 0;
|
||||
uint8_t *tmp = (uint8_t*)&ret;
|
||||
|
||||
tmp[0] = (val & 0xff00) >> 8;
|
||||
tmp[1] = (val & 0x00ff) >> 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
uint32_t mvhd_to_be32(uint32_t val) {
|
||||
|
||||
|
||||
uint32_t
|
||||
mvhd_to_be32(uint32_t val)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
uint8_t *tmp = (uint8_t*)&ret;
|
||||
|
||||
tmp[0] = (val & 0xff000000) >> 24;
|
||||
tmp[1] = (val & 0x00ff0000) >> 16;
|
||||
tmp[2] = (val & 0x0000ff00) >> 8;
|
||||
tmp[3] = (val & 0x000000ff) >> 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
uint64_t mvhd_to_be64(uint64_t val) {
|
||||
|
||||
|
||||
uint64_t
|
||||
mvhd_to_be64(uint64_t val)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
uint8_t *tmp = (uint8_t*)&ret;
|
||||
|
||||
tmp[0] = (uint8_t)((val & 0xff00000000000000) >> 56);
|
||||
tmp[1] = (uint8_t)((val & 0x00ff000000000000) >> 48);
|
||||
tmp[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40);
|
||||
@@ -78,21 +140,17 @@ uint64_t mvhd_to_be64(uint64_t val) {
|
||||
tmp[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16);
|
||||
tmp[6] = (uint8_t)((val & 0x000000000000ff00) >> 8);
|
||||
tmp[7] = (uint8_t)((val & 0x00000000000000ff) >> 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mvhd_is_conectix_str(const void* buffer) {
|
||||
if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void mvhd_generate_uuid(uint8_t* uuid)
|
||||
void
|
||||
mvhd_generate_uuid(uint8_t* uuid)
|
||||
{
|
||||
/* We aren't doing crypto here, so using system time as seed should be good enough */
|
||||
srand((unsigned int)time(0));
|
||||
|
||||
for (int n = 0; n < 16; n++) {
|
||||
uuid[n] = rand();
|
||||
}
|
||||
@@ -102,34 +160,50 @@ void mvhd_generate_uuid(uint8_t* uuid)
|
||||
uuid[8] |= 0x80; /* Variant 1 */
|
||||
}
|
||||
|
||||
uint32_t vhd_calc_timestamp(void)
|
||||
{
|
||||
time_t start_time;
|
||||
time_t curr_time;
|
||||
double vhd_time;
|
||||
start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */
|
||||
curr_time = time(NULL);
|
||||
vhd_time = difftime(curr_time, start_time);
|
||||
return (uint32_t)vhd_time;
|
||||
}
|
||||
|
||||
uint32_t mvhd_epoch_to_vhd_ts(time_t ts) {
|
||||
time_t start_time = MVHD_START_TS;
|
||||
if (ts < start_time) {
|
||||
return start_time;
|
||||
}
|
||||
double vhd_time = difftime(ts, start_time);
|
||||
uint32_t
|
||||
vhd_calc_timestamp(void)
|
||||
{
|
||||
time_t start_time;
|
||||
time_t curr_time;
|
||||
double vhd_time;
|
||||
|
||||
start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */
|
||||
curr_time = time(NULL);
|
||||
vhd_time = difftime(curr_time, start_time);
|
||||
|
||||
return (uint32_t)vhd_time;
|
||||
}
|
||||
|
||||
time_t vhd_get_created_time(MVHDMeta *vhdm)
|
||||
|
||||
uint32_t
|
||||
mvhd_epoch_to_vhd_ts(time_t ts)
|
||||
{
|
||||
time_t vhd_time = (time_t)vhdm->footer.timestamp;
|
||||
time_t vhd_time_unix = MVHD_START_TS + vhd_time;
|
||||
return vhd_time_unix;
|
||||
time_t start_time = MVHD_START_TS;
|
||||
double vhd_time;
|
||||
|
||||
if (ts < start_time)
|
||||
return (uint32_t)start_time;
|
||||
|
||||
vhd_time = difftime(ts, start_time);
|
||||
|
||||
return (uint32_t)vhd_time;
|
||||
}
|
||||
|
||||
FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
|
||||
|
||||
time_t
|
||||
vhd_get_created_time(MVHDMeta *vhdm)
|
||||
{
|
||||
time_t vhd_time = (time_t)vhdm->footer.timestamp;
|
||||
time_t vhd_time_unix = MVHD_START_TS + vhd_time;
|
||||
|
||||
return vhd_time_unix;
|
||||
}
|
||||
|
||||
|
||||
FILE*
|
||||
mvhd_fopen(const char* path, const char* mode, int* err)
|
||||
{
|
||||
FILE* f = NULL;
|
||||
#ifdef _WIN32
|
||||
size_t path_len = strlen(path);
|
||||
@@ -140,6 +214,7 @@ FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
|
||||
int new_mode_len = (sizeof mode_str) - 2;
|
||||
int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len);
|
||||
int mode_res = UTF8ToUTF16LE((unsigned char*)mode_str, &new_mode_len, (const unsigned char*)mode, (int*)&mode_len);
|
||||
|
||||
if (path_res > 0 && mode_res > 0) {
|
||||
f = _wfopen(new_path, mode_str);
|
||||
if (f == NULL) {
|
||||
@@ -160,10 +235,14 @@ FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
|
||||
*err = MVHD_ERR_FILE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void mvhd_set_encoding_err(int encoding_retval, int* err) {
|
||||
|
||||
void
|
||||
mvhd_set_encoding_err(int encoding_retval, int* err)
|
||||
{
|
||||
if (encoding_retval == -1) {
|
||||
*err = MVHD_ERR_UTF_SIZE;
|
||||
} else if (encoding_retval == -2) {
|
||||
@@ -171,87 +250,162 @@ void mvhd_set_encoding_err(int encoding_retval, int* err) {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t mvhd_calc_size_bytes(MVHDGeom *geom) {
|
||||
|
||||
uint64_t
|
||||
mvhd_calc_size_bytes(MVHDGeom *geom)
|
||||
{
|
||||
uint64_t img_size = (uint64_t)geom->cyl * (uint64_t)geom->heads * (uint64_t)geom->spt * (uint64_t)MVHD_SECTOR_SIZE;
|
||||
|
||||
return img_size;
|
||||
}
|
||||
|
||||
uint32_t mvhd_calc_size_sectors(MVHDGeom *geom) {
|
||||
|
||||
uint32_t
|
||||
mvhd_calc_size_sectors(MVHDGeom *geom)
|
||||
{
|
||||
uint32_t sector_size = (uint32_t)geom->cyl * (uint32_t)geom->heads * (uint32_t)geom->spt;
|
||||
|
||||
return sector_size;
|
||||
}
|
||||
|
||||
MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm) {
|
||||
MVHDGeom geometry = { .cyl = vhdm->footer.geom.cyl, .heads = vhdm->footer.geom.heads, .spt = vhdm->footer.geom.spt };
|
||||
|
||||
MVHDAPI MVHDGeom
|
||||
mvhd_get_geometry(MVHDMeta* vhdm)
|
||||
{
|
||||
MVHDGeom geometry = {
|
||||
.cyl = vhdm->footer.geom.cyl,
|
||||
.heads = vhdm->footer.geom.heads,
|
||||
.spt = vhdm->footer.geom.spt
|
||||
};
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer) {
|
||||
|
||||
MVHDAPI uint64_t
|
||||
mvhd_get_current_size(MVHDMeta* vhdm)
|
||||
{
|
||||
return vhdm->footer.curr_sz;
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
mvhd_gen_footer_checksum(MVHDFooter* footer)
|
||||
{
|
||||
uint32_t new_chk = 0;
|
||||
uint32_t orig_chk = footer->checksum;
|
||||
footer->checksum = 0;
|
||||
uint8_t* footer_bytes = (uint8_t*)footer;
|
||||
for (size_t i = 0; i < sizeof *footer; i++) {
|
||||
|
||||
for (size_t i = 0; i < sizeof *footer; i++)
|
||||
new_chk += footer_bytes[i];
|
||||
}
|
||||
footer->checksum = orig_chk;
|
||||
|
||||
return ~new_chk;
|
||||
}
|
||||
|
||||
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header) {
|
||||
|
||||
uint32_t
|
||||
mvhd_gen_sparse_checksum(MVHDSparseHeader* header)
|
||||
{
|
||||
uint32_t new_chk = 0;
|
||||
uint32_t orig_chk = header->checksum;
|
||||
header->checksum = 0;
|
||||
uint8_t* sparse_bytes = (uint8_t*)header;
|
||||
|
||||
for (size_t i = 0; i < sizeof *header; i++) {
|
||||
new_chk += sparse_bytes[i];
|
||||
}
|
||||
header->checksum = orig_chk;
|
||||
|
||||
return ~new_chk;
|
||||
}
|
||||
|
||||
const char* mvhd_strerr(MVHDError err) {
|
||||
|
||||
MVHDAPI const char*
|
||||
mvhd_strerr(MVHDError err)
|
||||
{
|
||||
const char *s = "unknown error";
|
||||
|
||||
switch (err) {
|
||||
case MVHD_ERR_MEM:
|
||||
return "memory allocation error";
|
||||
case MVHD_ERR_FILE:
|
||||
return "file error";
|
||||
case MVHD_ERR_NOT_VHD:
|
||||
return "file is not a VHD image";
|
||||
case MVHD_ERR_TYPE:
|
||||
return "unsupported VHD image type";
|
||||
case MVHD_ERR_FOOTER_CHECKSUM:
|
||||
return "invalid VHD footer checksum";
|
||||
case MVHD_ERR_SPARSE_CHECKSUM:
|
||||
return "invalid VHD sparse header checksum";
|
||||
case MVHD_ERR_UTF_TRANSCODING_FAILED:
|
||||
return "error converting path encoding";
|
||||
case MVHD_ERR_UTF_SIZE:
|
||||
return "buffer size mismatch when converting path encoding";
|
||||
case MVHD_ERR_PATH_REL:
|
||||
return "relative path detected where absolute path expected";
|
||||
case MVHD_ERR_PATH_LEN:
|
||||
return "path length exceeds MVHD_MAX_PATH";
|
||||
case MVHD_ERR_PAR_NOT_FOUND:
|
||||
return "parent VHD image not found";
|
||||
case MVHD_ERR_INVALID_PAR_UUID:
|
||||
return "UUID mismatch between child and parent VHD";
|
||||
case MVHD_ERR_INVALID_GEOM:
|
||||
return "invalid geometry detected";
|
||||
case MVHD_ERR_INVALID_SIZE:
|
||||
return "invalid size";
|
||||
case MVHD_ERR_INVALID_BLOCK_SIZE:
|
||||
return "invalid block size";
|
||||
case MVHD_ERR_INVALID_PARAMS:
|
||||
return "invalid parameters passed to function";
|
||||
case MVHD_ERR_CONV_SIZE:
|
||||
return "error converting image. Size mismatch detechted";
|
||||
default:
|
||||
return "unknown error";
|
||||
case MVHD_ERR_MEM:
|
||||
s = "memory allocation error";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_FILE:
|
||||
s = "file error";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_NOT_VHD:
|
||||
s = "file is not a VHD image";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_TYPE:
|
||||
s = "unsupported VHD image type";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_FOOTER_CHECKSUM:
|
||||
s = "invalid VHD footer checksum";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_SPARSE_CHECKSUM:
|
||||
s = "invalid VHD sparse header checksum";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_UTF_TRANSCODING_FAILED:
|
||||
s = "error converting path encoding";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_UTF_SIZE:
|
||||
s = "buffer size mismatch when converting path encoding";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_PATH_REL:
|
||||
s = "relative path detected where absolute path expected";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_PATH_LEN:
|
||||
s = "path length exceeds MVHD_MAX_PATH";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_PAR_NOT_FOUND:
|
||||
s = "parent VHD image not found";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_INVALID_PAR_UUID:
|
||||
s = "UUID mismatch between child and parent VHD";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_INVALID_GEOM:
|
||||
s = "invalid geometry detected";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_INVALID_SIZE:
|
||||
s = "invalid size";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_INVALID_BLOCK_SIZE:
|
||||
s = "invalid block size";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_INVALID_PARAMS:
|
||||
s = "invalid parameters passed to function";
|
||||
break;
|
||||
|
||||
case MVHD_ERR_CONV_SIZE:
|
||||
s = "error converting image. Size mismatch detected";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int64_t mvhd_ftello64(FILE* stream)
|
||||
|
||||
int64_t
|
||||
mvhd_ftello64(FILE* stream)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _ftelli64(stream);
|
||||
@@ -262,7 +416,9 @@ int64_t mvhd_ftello64(FILE* stream)
|
||||
#endif
|
||||
}
|
||||
|
||||
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
|
||||
|
||||
int
|
||||
mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _fseeki64(stream, offset, origin);
|
||||
@@ -273,17 +429,25 @@ int mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t mvhd_crc32_for_byte(uint32_t r) {
|
||||
|
||||
uint32_t
|
||||
mvhd_crc32_for_byte(uint32_t r)
|
||||
{
|
||||
for (int j = 0; j < 8; ++j)
|
||||
r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1;
|
||||
|
||||
return r ^ (uint32_t)0xFF000000L;
|
||||
}
|
||||
|
||||
uint32_t mvhd_crc32(const void* data, size_t n_bytes) {
|
||||
|
||||
uint32_t
|
||||
mvhd_crc32(const void* data, size_t n_bytes)
|
||||
{
|
||||
static uint32_t table[0x100];
|
||||
|
||||
if (!*table)
|
||||
for (size_t i = 0; i < 0x100; ++i)
|
||||
table[i] = mvhd_crc32_for_byte(i);
|
||||
table[i] = mvhd_crc32_for_byte((uint32_t)i);
|
||||
|
||||
uint32_t crc = 0;
|
||||
for (size_t i = 0; i < n_bytes; ++i)
|
||||
@@ -292,7 +456,10 @@ uint32_t mvhd_crc32(const void* data, size_t n_bytes) {
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
|
||||
|
||||
uint32_t
|
||||
mvhd_file_mod_timestamp(const char* path, int *err)
|
||||
{
|
||||
*err = 0;
|
||||
#ifdef _WIN32
|
||||
struct _stat file_stat;
|
||||
@@ -300,6 +467,7 @@ uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
|
||||
mvhd_utf16 new_path[260] = {0};
|
||||
int new_path_len = (sizeof new_path) - 2;
|
||||
int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len);
|
||||
|
||||
if (path_res > 0) {
|
||||
int stat_res = _wstat(new_path, &file_stat);
|
||||
if (stat_res != 0) {
|
||||
@@ -319,6 +487,7 @@ uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
|
||||
#else
|
||||
struct stat file_stat;
|
||||
int stat_res = stat(path, &file_stat);
|
||||
|
||||
if (stat_res != 0) {
|
||||
mvhd_errno = errno;
|
||||
*err = MVHD_ERR_FILE;
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
#ifndef MINIVHD_UTIL_H
|
||||
#define MINIVHD_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "minivhd_internal.h"
|
||||
#include "minivhd.h"
|
||||
#define MVHD_START_TS 946684800
|
||||
|
||||
/**
|
||||
* Functions to deal with endian issues
|
||||
*/
|
||||
uint16_t mvhd_from_be16(uint16_t val);
|
||||
uint32_t mvhd_from_be32(uint32_t val);
|
||||
uint64_t mvhd_from_be64(uint64_t val);
|
||||
uint16_t mvhd_to_be16(uint16_t val);
|
||||
uint32_t mvhd_to_be32(uint32_t val);
|
||||
uint64_t mvhd_to_be64(uint64_t val);
|
||||
|
||||
/**
|
||||
* \brief Check if provided buffer begins with the string "conectix"
|
||||
*
|
||||
* \param [in] buffer The buffer to compare. Must be at least 8 bytes in length
|
||||
*
|
||||
* \return true if the buffer begins with "conectix"
|
||||
* \return false if the buffer does not begin with "conectix"
|
||||
*/
|
||||
bool mvhd_is_conectix_str(const void* buffer);
|
||||
|
||||
/**
|
||||
* \brief Generate a raw 16 byte UUID
|
||||
*
|
||||
* \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to
|
||||
*/
|
||||
void mvhd_generate_uuid(uint8_t *uuid);
|
||||
|
||||
/**
|
||||
* \brief Calculate a VHD formatted timestamp from the current time
|
||||
*/
|
||||
uint32_t vhd_calc_timestamp(void);
|
||||
|
||||
/**
|
||||
* \brief Convert an epoch timestamp to a VHD timestamp
|
||||
*
|
||||
* \param [in] ts epoch timestamp to convert.
|
||||
*
|
||||
* \return The adjusted timestamp, or 0 if the input timestamp is
|
||||
* earlier that 1 Janurary 2000
|
||||
*/
|
||||
uint32_t mvhd_epoch_to_vhd_ts(time_t ts);
|
||||
|
||||
/**
|
||||
* \brief Return the created time from a VHD image
|
||||
*
|
||||
* \param [in] vhdm Pointer to the MiniVHD metadata structure
|
||||
*
|
||||
* \return The created time, as a Unix timestamp
|
||||
*/
|
||||
time_t vhd_get_created_time(MVHDMeta *vhdm);
|
||||
|
||||
/**
|
||||
* \brief Cross platform, unicode filepath opening
|
||||
*
|
||||
* This function accounts for the fact that fopen() handles file paths differently compared to other
|
||||
* operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8.
|
||||
*
|
||||
* Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE
|
||||
* encoded path and modestring.
|
||||
*
|
||||
* \param [in] path The filepath to open as a UTF-8 string
|
||||
* \param [in] mode The mode string to use (eg: "rb+"")
|
||||
* \param [out] err The error value, if an error occurrs
|
||||
*
|
||||
* \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err
|
||||
*/
|
||||
FILE* mvhd_fopen(const char* path, const char* mode, int* err);
|
||||
|
||||
void mvhd_set_encoding_err(int encoding_retval, int* err);
|
||||
uint64_t mvhd_calc_size_bytes(MVHDGeom *geom);
|
||||
uint32_t mvhd_calc_size_sectors(MVHDGeom *geom);
|
||||
MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm);
|
||||
|
||||
/**
|
||||
* \brief Generate VHD footer checksum
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer);
|
||||
|
||||
/**
|
||||
* \brief Generate VHD sparse header checksum
|
||||
*
|
||||
* \param [in] vhdm MiniVHD data structure
|
||||
*/
|
||||
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header);
|
||||
|
||||
/**
|
||||
* \brief Get current position in file stream
|
||||
*
|
||||
* This is a portable version of the POSIX ftello64(). *
|
||||
*/
|
||||
int64_t mvhd_ftello64(FILE* stream);
|
||||
|
||||
/**
|
||||
* \brief Reposition the file stream's position
|
||||
*
|
||||
* This is a portable version of the POSIX fseeko64(). *
|
||||
*/
|
||||
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin);
|
||||
|
||||
/**
|
||||
* \brief Calculate the CRC32 of a data buffer.
|
||||
*
|
||||
* This function can be used for verifying data integrity.
|
||||
*
|
||||
* \param [in] data The data buffer
|
||||
* \param [in] n_bytes The size of the data buffer in bytes
|
||||
*
|
||||
* \return The CRC32 of the data buffer
|
||||
*/
|
||||
uint32_t mvhd_crc32(const void* data, size_t n_bytes);
|
||||
|
||||
/**
|
||||
* \brief Calculate the file modification timestamp.
|
||||
*
|
||||
* This function is primarily to help protect differencing VHD's
|
||||
*
|
||||
* \param [in] path the UTF-8 file path
|
||||
* \param [out] err The error value, if an error occurrs
|
||||
*
|
||||
* \return The file modified timestamp, in VHD compatible timestamp.
|
||||
* 'err' will be set to non-zero on error
|
||||
*/
|
||||
uint32_t mvhd_file_mod_timestamp(const char* path, int *err);
|
||||
#endif
|
||||
232
src/disk/minivhd/struct_rw.c
Normal file
232
src/disk/minivhd/struct_rw.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Header and footer serialize/deserialize functions.
|
||||
*
|
||||
* Read data from footer into the struct members, swapping
|
||||
* endian where necessary.
|
||||
*
|
||||
* NOTE: Order matters here!
|
||||
* We must read each field in the order the struct is in.
|
||||
* Doing this may be less elegant than performing a memcpy
|
||||
* to a packed struct, but it avoids potential data alignment
|
||||
* issues, and the endian swapping allows us to use the fields
|
||||
* directly.
|
||||
*
|
||||
* Version: @(#)struct_rw.c 1.0.2 2021/04/16
|
||||
*
|
||||
* Author: Sherman Perry, <shermperry@gmail.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
/**
|
||||
* \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary
|
||||
*
|
||||
* \param [out] struct_memb struct member to save the field to
|
||||
* \param [in] memb_size the size of struct_memb, in bytes
|
||||
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
|
||||
* \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call
|
||||
*/
|
||||
static void
|
||||
next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer)
|
||||
{
|
||||
memcpy(struct_memb, *buffer, memb_size);
|
||||
|
||||
if (req_endian) switch (memb_size) {
|
||||
case 2:
|
||||
*(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb));
|
||||
break;
|
||||
|
||||
case 8:
|
||||
*(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb));
|
||||
break;
|
||||
}
|
||||
|
||||
*buffer += memb_size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Save a struct member into a buffer, converting endian if necessary
|
||||
*
|
||||
* \param [in] struct_memb struct member read from
|
||||
* \param [in] memb_size the size of struct_memb, in bytes
|
||||
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
|
||||
* \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call
|
||||
*/
|
||||
static void
|
||||
next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer)
|
||||
{
|
||||
uint8_t *buf_ptr = *buffer;
|
||||
|
||||
memcpy(buf_ptr, struct_memb, memb_size);
|
||||
|
||||
if (req_endian) switch (memb_size) {
|
||||
case 2:
|
||||
*((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb));
|
||||
break;
|
||||
|
||||
case 8:
|
||||
*((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb));
|
||||
break;
|
||||
}
|
||||
|
||||
buf_ptr += memb_size;
|
||||
*buffer = buf_ptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer)
|
||||
{
|
||||
uint8_t* buff_ptr = buffer;
|
||||
|
||||
next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
|
||||
next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer)
|
||||
{
|
||||
uint8_t* buff_ptr = buffer;
|
||||
|
||||
next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
|
||||
next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer)
|
||||
{
|
||||
uint8_t* buff_ptr = buffer;
|
||||
|
||||
next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr);
|
||||
next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
|
||||
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
|
||||
}
|
||||
|
||||
next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer)
|
||||
{
|
||||
uint8_t* buff_ptr = buffer;
|
||||
|
||||
next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr);
|
||||
next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
|
||||
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
|
||||
}
|
||||
|
||||
next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
|
||||
}
|
||||
68
src/disk/minivhd/version.h
Normal file
68
src/disk/minivhd/version.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Define library version and build info.
|
||||
*
|
||||
* Version: @(#)version.h 1.034 2021/04/16
|
||||
*
|
||||
* Author: Fred N. van Kempen, <waltje@varcem.com>
|
||||
*
|
||||
* Copyright 2021 Fred N. van Kempen.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MINIVHD_VERSION_H
|
||||
# define MINIVHD_VERSION_H
|
||||
|
||||
|
||||
/* Library name. */
|
||||
#define LIB_NAME "MiniVHD"
|
||||
|
||||
/* Version info. */
|
||||
#define LIB_VER_MAJOR 1
|
||||
#define LIB_VER_MINOR 0
|
||||
#define LIB_VER_REV 3
|
||||
#define LIB_VER_PATCH 0
|
||||
|
||||
|
||||
/* Standard C preprocessor macros. */
|
||||
#define STR_STRING(x) #x
|
||||
#define STR(x) STR_STRING(x)
|
||||
#define STR_RC(a,e) a ## , ## e
|
||||
|
||||
|
||||
/* These are used in the application. */
|
||||
#define LIB_VER_NUM LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV
|
||||
#if defined(LIB_VER_PATCH) && LIB_VER_PATCH > 0
|
||||
# define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.LIB_VER_PATCH
|
||||
#else
|
||||
# define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.0
|
||||
#endif
|
||||
#define LIB_VERSION STR(LIB_VER_NUM)
|
||||
#define LIB_VERSION_4 STR(LIB_VER_NUM_4)
|
||||
|
||||
|
||||
#endif /*MINIVHD_VERSION_H*/
|
||||
@@ -22,9 +22,19 @@
|
||||
* Adapted and abridged for MiniVHD by Sherman Perry
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#define BUILDING_LIBRARY
|
||||
#include "minivhd.h"
|
||||
#include "internal.h"
|
||||
#include "xml2_encoding.h"
|
||||
|
||||
|
||||
static int xmlLittleEndian = 1;
|
||||
|
||||
|
||||
/* Note: extracted from original 'void xmlInitCharEncodingHandlers(void)' function */
|
||||
void xmlEncodingInit(void)
|
||||
{
|
||||
@@ -96,8 +106,8 @@ int UTF16LEToUTF8(unsigned char* out, int *outlen,
|
||||
c += 0x10000;
|
||||
}
|
||||
else {
|
||||
*outlen = out - outstart;
|
||||
*inlenb = processed - inb;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlenb = (int)(processed - inb);
|
||||
return(-2);
|
||||
}
|
||||
}
|
||||
@@ -117,8 +127,8 @@ int UTF16LEToUTF8(unsigned char* out, int *outlen,
|
||||
}
|
||||
processed = (const unsigned char*) in;
|
||||
}
|
||||
*outlen = out - outstart;
|
||||
*inlenb = processed - inb;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlenb = (int)(processed - inb);
|
||||
return(*outlen);
|
||||
}
|
||||
|
||||
@@ -163,16 +173,16 @@ int UTF8ToUTF16LE(unsigned char* outb, int *outlen,
|
||||
if (d < 0x80) { c= d; trailing= 0; }
|
||||
else if (d < 0xC0) {
|
||||
/* trailing byte in leading position */
|
||||
*outlen = (out - outstart) * 2;
|
||||
*inlen = processed - instart;
|
||||
*outlen = (int)((out - outstart) * 2);
|
||||
*inlen = (int)(processed - instart);
|
||||
return(-2);
|
||||
} else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
|
||||
else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
|
||||
else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
|
||||
else {
|
||||
/* no chance for this in UTF-16 */
|
||||
*outlen = (out - outstart) * 2;
|
||||
*inlen = processed - instart;
|
||||
*outlen = (int)((out - outstart) * 2);
|
||||
*inlen = (int)(processed - instart);
|
||||
return(-2);
|
||||
}
|
||||
|
||||
@@ -225,8 +235,8 @@ int UTF8ToUTF16LE(unsigned char* outb, int *outlen,
|
||||
break;
|
||||
processed = in;
|
||||
}
|
||||
*outlen = (out - outstart) * 2;
|
||||
*inlen = processed - instart;
|
||||
*outlen = (int)((out - outstart) * 2);
|
||||
*inlen = (int)(processed - instart);
|
||||
return(*outlen);
|
||||
}
|
||||
|
||||
@@ -275,8 +285,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
|
||||
}
|
||||
if ((c & 0xFC00) == 0xD800) { /* surrogates */
|
||||
if (in >= inend) { /* (in > inend) shouldn't happens */
|
||||
*outlen = out - outstart;
|
||||
*inlenb = processed - inb;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlenb = (int)(processed - inb);
|
||||
return(-2);
|
||||
}
|
||||
if (xmlLittleEndian) {
|
||||
@@ -295,8 +305,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
|
||||
c += 0x10000;
|
||||
}
|
||||
else {
|
||||
*outlen = out - outstart;
|
||||
*inlenb = processed - inb;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlenb = (int)(processed - inb);
|
||||
return(-2);
|
||||
}
|
||||
}
|
||||
@@ -316,8 +326,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
|
||||
}
|
||||
processed = (const unsigned char*) in;
|
||||
}
|
||||
*outlen = out - outstart;
|
||||
*inlenb = processed - inb;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlenb = (int)(processed - inb);
|
||||
return(*outlen);
|
||||
}
|
||||
|
||||
@@ -362,16 +372,16 @@ int UTF8ToUTF16BE(unsigned char* outb, int *outlen,
|
||||
if (d < 0x80) { c= d; trailing= 0; }
|
||||
else if (d < 0xC0) {
|
||||
/* trailing byte in leading position */
|
||||
*outlen = out - outstart;
|
||||
*inlen = processed - instart;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlen = (int)(processed - instart);
|
||||
return(-2);
|
||||
} else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
|
||||
else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
|
||||
else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
|
||||
else {
|
||||
/* no chance for this in UTF-16 */
|
||||
*outlen = out - outstart;
|
||||
*inlen = processed - instart;
|
||||
*outlen = (int)(out - outstart);
|
||||
*inlen = (int)(processed - instart);
|
||||
return(-2);
|
||||
}
|
||||
|
||||
@@ -421,8 +431,8 @@ int UTF8ToUTF16BE(unsigned char* outb, int *outlen,
|
||||
break;
|
||||
processed = in;
|
||||
}
|
||||
*outlen = (out - outstart) * 2;
|
||||
*inlen = processed - instart;
|
||||
*outlen = (int)((out - outstart) * 2);
|
||||
*inlen = (int)(processed - instart);
|
||||
return(*outlen);
|
||||
}
|
||||
|
||||
62
src/disk/minivhd/xml2_encoding.h
Normal file
62
src/disk/minivhd/xml2_encoding.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MiniVHD Minimalist VHD implementation in C.
|
||||
*
|
||||
* This file is part of the MiniVHD Project.
|
||||
*
|
||||
* Version: @(#)xml2_encoding.h 1.0.1 2021/03/15
|
||||
*
|
||||
* Author: Sherman Perry, <shermperry@gmail.com>
|
||||
*
|
||||
* Copyright 2019-2021 Sherman Perry.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documenta-
|
||||
* tion files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom
|
||||
* the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall
|
||||
* be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef XML2_ENCODING_H
|
||||
# define XML2_ENCODING_H
|
||||
|
||||
|
||||
typedef uint16_t mvhd_utf16;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void xmlEncodingInit(void);
|
||||
|
||||
int UTF16LEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb,
|
||||
int *inlenb);
|
||||
int UTF8ToUTF16LE(uint8_t *outb, int *outlen, const uint8_t *in,
|
||||
int *inlen);
|
||||
int UTF16BEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb,
|
||||
int *inlenb);
|
||||
int UTF8ToUTF16BE(uint8_t *outb, int *outlen, const uint8_t *in,
|
||||
int *inlen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*XML2_ENCODING_H*/
|
||||
@@ -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);
|
||||
|
||||
|
||||
25
src/io.c
25
src/io.c
@@ -56,6 +56,7 @@ typedef struct {
|
||||
int initialized = 0;
|
||||
io_t *io[NPORTS], *io_last[NPORTS];
|
||||
|
||||
// #define ENABLE_IO_LOG 1
|
||||
#ifdef ENABLE_IO_LOG
|
||||
int io_do_log = ENABLE_IO_LOG;
|
||||
|
||||
@@ -310,7 +311,9 @@ inb(uint16_t port)
|
||||
/* if (port == 0x1ed)
|
||||
ret = 0xfe; */
|
||||
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
|
||||
if (port == 0x92) {
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
@@ -341,7 +344,9 @@ outb(uint16_t port, uint8_t val)
|
||||
#endif
|
||||
}
|
||||
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) outb(%04X, %02X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
|
||||
if (port == 0x92) {
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) outb(%04X, %02X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -395,7 +400,9 @@ inw(uint16_t port)
|
||||
if (!found)
|
||||
cycles -= io_delay;
|
||||
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
|
||||
if (port == 0x92) {
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -440,7 +447,9 @@ outw(uint16_t port, uint16_t val)
|
||||
#endif
|
||||
}
|
||||
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) outw(%04X, %04X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
|
||||
if (port == 0x92) {
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) outw(%04X, %04X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -522,7 +531,9 @@ inl(uint16_t port)
|
||||
if (!found)
|
||||
cycles -= io_delay;
|
||||
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
|
||||
if (port == 0x92) {
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -582,7 +593,9 @@ outl(uint16_t port, uint32_t val)
|
||||
#endif
|
||||
}
|
||||
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) outl(%04X, %08X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
|
||||
if (port == 0x92) {
|
||||
io_log("[%04X:%08X] (%i, %i, %04i) outl(%04X, %08X)\n", CS, cpu_state.pc, in_smm, found, qfound, port, val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
20
src/pic.c
20
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);
|
||||
|
||||
@@ -23,7 +23,6 @@ extern "C" {
|
||||
#include <86box/86box.h>
|
||||
#include <86box/hdd.h>
|
||||
#include "../disk/minivhd/minivhd.h"
|
||||
#include "../disk/minivhd/minivhd_util.h"
|
||||
}
|
||||
|
||||
#include <thread>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -362,12 +362,6 @@
|
||||
<property name="text">
|
||||
<string>&Fullscreen</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+PgUp</string>
|
||||
</property>
|
||||
<property name="shortcutVisibleInContextMenu">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSoftware_Renderer">
|
||||
<property name="checkable">
|
||||
|
||||
@@ -195,7 +195,7 @@ int ignoreNextMouseEvent = 1;
|
||||
void
|
||||
RendererStack::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->geometry().contains(event->pos()) && event->button() == Qt::LeftButton && !mouse_capture && (isMouseDown & 1) && (mouse_get_buttons() != 0) && mouse_mode == 0) {
|
||||
if (this->geometry().contains(event->pos()) && (event->button() == Qt::LeftButton) && !mouse_capture && (isMouseDown & 1) && (kbd_req_capture || (mouse_get_buttons() != 0)) && (mouse_mode == 0)) {
|
||||
plat_mouse_capture(1);
|
||||
this->setCursor(Qt::BlankCursor);
|
||||
if (!ignoreNextMouseEvent)
|
||||
|
||||
@@ -198,13 +198,13 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
|
||||
We use scan code 0xFFFF to mean a mapping that
|
||||
has a prefix other than E0 and that is not E1 1D,
|
||||
which is, for our purposes, invalid. */
|
||||
if ((scancode == 0x00F) && !(rawKB.Flags & RI_KEY_BREAK) && (recv_lalt || recv_ralt) && !mouse_capture) {
|
||||
if ((scancode == 0x00f) && !(rawKB.Flags & RI_KEY_BREAK) && (recv_lalt || recv_ralt) && (!kbd_req_capture || mouse_capture)) {
|
||||
/* We received a TAB while ALT was pressed, while the mouse
|
||||
is not captured, suppress the TAB and send an ALT key up. */
|
||||
is not captured, suppress the TAB and send an ALT key up. */
|
||||
if (recv_lalt) {
|
||||
keyboard_input(0, 0x038);
|
||||
/* Extra key press and release so the guest is not stuck in the
|
||||
menu bar. */
|
||||
menu bar. */
|
||||
keyboard_input(1, 0x038);
|
||||
keyboard_input(0, 0x038);
|
||||
recv_lalt = 0;
|
||||
@@ -212,19 +212,19 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
|
||||
if (recv_ralt) {
|
||||
keyboard_input(0, 0x138);
|
||||
/* Extra key press and release so the guest is not stuck in the
|
||||
menu bar. */
|
||||
menu bar. */
|
||||
keyboard_input(1, 0x138);
|
||||
keyboard_input(0, 0x138);
|
||||
recv_ralt = 0;
|
||||
}
|
||||
} else if (((scancode == 0x038) || (scancode == 0x138)) && !(rawKB.Flags & RI_KEY_BREAK) && recv_tab && !mouse_capture) {
|
||||
} else if (((scancode == 0x038) || (scancode == 0x138)) && !(rawKB.Flags & RI_KEY_BREAK) && recv_tab && (!kbd_req_capture || mouse_capture)) {
|
||||
/* We received an ALT while TAB was pressed, while the mouse
|
||||
is not captured, suppress the ALT and send a TAB key up. */
|
||||
keyboard_input(0, 0x00F);
|
||||
is not captured, suppress the ALT and send a TAB key up. */
|
||||
keyboard_input(0, 0x00f);
|
||||
recv_tab = 0;
|
||||
} else {
|
||||
switch (scancode) {
|
||||
case 0x00F:
|
||||
case 0x00f:
|
||||
recv_tab = !(rawKB.Flags & RI_KEY_BREAK);
|
||||
break;
|
||||
case 0x038:
|
||||
@@ -237,7 +237,7 @@ WindowsRawInputFilter::keyboard_handle(PRAWINPUT raw)
|
||||
|
||||
/* Translate right CTRL to left ALT if the user has so
|
||||
chosen. */
|
||||
if ((scancode == 0x11D) && rctrl_is_lalt)
|
||||
if ((scancode == 0x11d) && rctrl_is_lalt)
|
||||
scancode = 0x038;
|
||||
|
||||
/* Normal scan code pass through, pass it through as is if
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
@@ -329,7 +329,7 @@ struct ymfm_output
|
||||
// ======================> ymfm_wavfile
|
||||
|
||||
// this class is a debugging helper that accumulates data and writes it to wav files
|
||||
template<int _Channels>
|
||||
template<int Channels>
|
||||
class ymfm_wavfile
|
||||
{
|
||||
public:
|
||||
@@ -361,10 +361,10 @@ public:
|
||||
memcpy(&header[12], "fmt ", 4);
|
||||
*(uint32_t *)&header[16] = 16;
|
||||
*(uint16_t *)&header[20] = 1;
|
||||
*(uint16_t *)&header[22] = _Channels;
|
||||
*(uint16_t *)&header[22] = Channels;
|
||||
*(uint32_t *)&header[24] = m_samplerate;
|
||||
*(uint32_t *)&header[28] = m_samplerate * 2 * _Channels;
|
||||
*(uint16_t *)&header[32] = 2 * _Channels;
|
||||
*(uint32_t *)&header[28] = m_samplerate * 2 * Channels;
|
||||
*(uint16_t *)&header[32] = 2 * Channels;
|
||||
*(uint16_t *)&header[34] = 16;
|
||||
memcpy(&header[36], "data", 4);
|
||||
*(uint32_t *)&header[40] = m_buffer.size() * 2 + 44 - 44;
|
||||
@@ -377,24 +377,24 @@ public:
|
||||
}
|
||||
|
||||
// add data to the file
|
||||
template<int _Outputs>
|
||||
void add(ymfm_output<_Outputs> output)
|
||||
template<int Outputs>
|
||||
void add(ymfm_output<Outputs> output)
|
||||
{
|
||||
int16_t sum[_Channels] = { 0 };
|
||||
for (int index = 0; index < _Outputs; index++)
|
||||
sum[index % _Channels] += output.data[index];
|
||||
for (int index = 0; index < _Channels; index++)
|
||||
int16_t sum[Channels] = { 0 };
|
||||
for (int index = 0; index < Outputs; index++)
|
||||
sum[index % Channels] += output.data[index];
|
||||
for (int index = 0; index < Channels; index++)
|
||||
m_buffer.push_back(sum[index]);
|
||||
}
|
||||
|
||||
// add data to the file, using a reference
|
||||
template<int _Outputs>
|
||||
void add(ymfm_output<_Outputs> output, ymfm_output<_Outputs> const &ref)
|
||||
template<int Outputs>
|
||||
void add(ymfm_output<Outputs> output, ymfm_output<Outputs> const &ref)
|
||||
{
|
||||
int16_t sum[_Channels] = { 0 };
|
||||
for (int index = 0; index < _Outputs; index++)
|
||||
sum[index % _Channels] += output.data[index] - ref.data[index];
|
||||
for (int index = 0; index < _Channels; index++)
|
||||
int16_t sum[Channels] = { 0 };
|
||||
for (int index = 0; index < Outputs; index++)
|
||||
sum[index % Channels] += output.data[index] - ref.data[index];
|
||||
for (int index = 0; index < Channels; index++)
|
||||
m_buffer.push_back(sum[index]);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define DEBUG_LOG_WAVFILES (0)
|
||||
#define YMFM_DEBUG_LOG_WAVFILES (0)
|
||||
|
||||
namespace ymfm
|
||||
{
|
||||
@@ -401,7 +401,7 @@ public:
|
||||
// compute sample rate
|
||||
uint32_t sample_rate(uint32_t baseclock) const
|
||||
{
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
m_wavfile[chnum].set_samplerate(baseclock / (m_clock_prescale * OPERATORS));
|
||||
#endif
|
||||
@@ -453,7 +453,7 @@ protected:
|
||||
RegisterType m_regs; // register accessor
|
||||
std::unique_ptr<fm_channel<RegisterType>> m_channel[CHANNELS]; // channel pointers
|
||||
std::unique_ptr<fm_operator<RegisterType>> m_operator[OPERATORS]; // operator pointers
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
mutable ymfm_wavfile<1> m_wavfile[CHANNELS]; // for debugging
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1185,6 +1185,7 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
|
||||
m_irq_mask(STATUS_TIMERA | STATUS_TIMERB),
|
||||
m_irq_state(0),
|
||||
m_timer_running{0,0},
|
||||
m_total_clocks(0),
|
||||
m_active_channels(ALL_CHANNELS),
|
||||
m_modified_channels(ALL_CHANNELS),
|
||||
m_prepare_count(0)
|
||||
@@ -1200,7 +1201,7 @@ fm_engine_base<RegisterType>::fm_engine_base(ymfm_interface &intf) :
|
||||
for (uint32_t opnum = 0; opnum < OPERATORS; opnum++)
|
||||
m_operator[opnum] = std::make_unique<fm_operator<RegisterType>>(*this, RegisterType::operator_offset(opnum));
|
||||
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
m_wavfile[chnum].set_index(chnum);
|
||||
#endif
|
||||
@@ -1332,7 +1333,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
chanmask &= debug::GLOBAL_FM_CHANNEL_MASK;
|
||||
|
||||
// mask out inactive channels
|
||||
if (!DEBUG_LOG_WAVFILES)
|
||||
if (!YMFM_DEBUG_LOG_WAVFILES)
|
||||
chanmask &= m_active_channels;
|
||||
|
||||
// handle the rhythm case, where some of the operators are dedicated
|
||||
@@ -1351,7 +1352,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
if (bitfield(chanmask, chnum))
|
||||
{
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
auto reference = output;
|
||||
#endif
|
||||
if (chnum == 6)
|
||||
@@ -1364,7 +1365,7 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
m_channel[chnum]->output_4op(output, rshift, clipmax);
|
||||
else
|
||||
m_channel[chnum]->output_2op(output, rshift, clipmax);
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
m_wavfile[chnum].add(output, reference);
|
||||
#endif
|
||||
}
|
||||
@@ -1375,14 +1376,14 @@ void fm_engine_base<RegisterType>::output(output_data &output, uint32_t rshift,
|
||||
for (uint32_t chnum = 0; chnum < CHANNELS; chnum++)
|
||||
if (bitfield(chanmask, chnum))
|
||||
{
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
auto reference = output;
|
||||
#endif
|
||||
if (m_channel[chnum]->is4op())
|
||||
m_channel[chnum]->output_4op(output, rshift, clipmax);
|
||||
else
|
||||
m_channel[chnum]->output_2op(output, rshift, clipmax);
|
||||
#if (DEBUG_LOG_WAVFILES)
|
||||
#if (YMFM_DEBUG_LOG_WAVFILES)
|
||||
m_wavfile[chnum].add(output, reference);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -100,6 +100,11 @@ opl_registers_base<Revision>::opl_registers_base() :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OPL3/OPL4 have dynamic operators, so initialize the fourop_enable value here
|
||||
// since operator_map() is called right away, prior to reset()
|
||||
if (Revision > 2)
|
||||
m_regdata[0x104 % REGISTERS] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1710,9 +1715,15 @@ uint8_t ymf278b::read_status()
|
||||
|
||||
uint8_t ymf278b::read_data_pcm()
|
||||
{
|
||||
// write to FM
|
||||
// read from PCM
|
||||
if (bitfield(m_address, 9) != 0)
|
||||
return m_pcm.read(m_address & 0xff);
|
||||
{
|
||||
uint8_t result = m_pcm.read(m_address & 0xff);
|
||||
if ((m_address & 0xff) == 0x02)
|
||||
result |= 0x20;
|
||||
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace ymfm
|
||||
void pcm_registers::reset()
|
||||
{
|
||||
std::fill_n(&m_regdata[0], REGISTERS, 0);
|
||||
m_regdata[0x02] = 0x20;
|
||||
m_regdata[0xf8] = 0x1b;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# After a successful build, you can install the RPMs as follows:
|
||||
# sudo dnf install RPMS/$(uname -m)/86Box-3* RPMS/noarch/86Box-roms*
|
||||
|
||||
%global romver v3.11
|
||||
%global romver 3.11
|
||||
|
||||
Name: 86Box
|
||||
Version: 4.0
|
||||
@@ -21,8 +21,8 @@ Summary: Classic PC emulator
|
||||
License: GPLv2+
|
||||
URL: https://86box.net
|
||||
|
||||
Source0: https://github.com/86Box/86Box/archive/refs/tags/v%%{version}.tar.gz
|
||||
Source1: https://github.com/86Box/roms/archive/refs/tags/%{romver}.zip
|
||||
Source0: https://github.com/86Box/86Box/archive/refs/tags/v%{version}.tar.gz
|
||||
Source1: https://github.com/86Box/roms/archive/refs/tags/v%{romver}.zip
|
||||
|
||||
BuildRequires: cmake
|
||||
BuildRequires: desktop-file-utils
|
||||
@@ -32,6 +32,7 @@ BuildRequires: gcc-c++
|
||||
BuildRequires: libFAudio-devel
|
||||
BuildRequires: libappstream-glib
|
||||
BuildRequires: libevdev-devel
|
||||
BuildRequires: libxkbcommon-x11-devel
|
||||
BuildRequires: libXi-devel
|
||||
BuildRequires: ninja-build
|
||||
BuildRequires: openal-soft-devel
|
||||
@@ -98,7 +99,7 @@ cp src/unix/assets/net.86box.86Box.metainfo.xml %{buildroot}%{_metainfodir}
|
||||
appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/net.86box.86Box.metainfo.xml
|
||||
|
||||
# install roms
|
||||
pushd roms-%{version}
|
||||
pushd roms-%{romver}
|
||||
mkdir -p %{buildroot}%{_datadir}/%{name}/roms
|
||||
cp -a * %{buildroot}%{_datadir}/%{name}/roms/
|
||||
popd
|
||||
|
||||
@@ -814,7 +814,7 @@ void
|
||||
plat_get_global_config_dir(char *strptr)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
char *prefPath = SDL_GetPrefPath(NULL, "net.86Box.86Box")
|
||||
char *prefPath = SDL_GetPrefPath(NULL, "net.86Box.86Box");
|
||||
#else
|
||||
char *prefPath = SDL_GetPrefPath(NULL, "86Box");
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user