From d91056586e03100698ff15d89ca4b56a49fca804 Mon Sep 17 00:00:00 2001 From: Stephen McKinney Date: Tue, 17 Nov 2020 23:31:38 -0600 Subject: [PATCH] Add VHD support. --- src/config.c | 5 + src/disk/hdd_image.c | 635 +++++++++++++++++++---------------- src/include/86box/language.h | 8 +- src/include/86box/resource.h | 4 + src/include/86box/win.h | 11 +- src/win/86Box.rc | 18 +- src/win/win_devconf.c | 2 +- src/win/win_dialog.c | 35 +- src/win/win_media_menu.c | 10 +- src/win/win_new_floppy.c | 2 +- src/win/win_settings.c | 365 ++++++++++++++++++-- 11 files changed, 763 insertions(+), 332 deletions(-) diff --git a/src/config.c b/src/config.c index 292c461d1..37de7b7b2 100644 --- a/src/config.c +++ b/src/config.c @@ -981,6 +981,11 @@ load_hard_disks(void) * files. We should remove this before * finalizing this release! --FvK */ + /* + * ANOTHER NOTE: + * When loading differencing VHDs, the absolute path is required. + * So we should not convert absolute paths to relative. -sards + */ if (! wcsnicmp(wp, usr_path, wcslen(usr_path))) { /* * Yep, its absolute and prefixed diff --git a/src/disk/hdd_image.c b/src/disk/hdd_image.c index 621e2f86c..fda699970 100644 --- a/src/disk/hdd_image.c +++ b/src/disk/hdd_image.c @@ -32,14 +32,21 @@ #include <86box/plat.h> #include <86box/random.h> #include <86box/hdd.h> +#include "minivhd/minivhd.h" +#include "minivhd/minivhd_internal.h" +#define HDD_IMAGE_RAW 0 +#define HDD_IMAGE_HDI 1 +#define HDD_IMAGE_HDX 2 +#define HDD_IMAGE_VHD 3 typedef struct { - FILE *file; + FILE *file; /* Used for HDD_IMAGE_RAW, HDD_IMAGE_HDI, and HDD_IMAGE_HDX. */ + MVHDMeta* vhd; /* Used for HDD_IMAGE_VHD. */ uint32_t base; uint32_t pos, last_sector; - uint8_t type; + uint8_t type; /* HDD_IMAGE_RAW, HDD_IMAGE_HDI, HDD_IMAGE_HDX, or HDD_IMAGE_VHD */ uint8_t loaded; } hdd_image_t; @@ -59,16 +66,15 @@ hdd_image_log(const char *fmt, ...) va_list ap; if (hdd_image_do_log) { - va_start(ap, fmt); - pclog_ex(fmt, ap); - va_end(ap); + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); } } #else #define hdd_image_log(fmt, ...) #endif - int image_is_hdi(const wchar_t *s) { @@ -77,12 +83,12 @@ image_is_hdi(const wchar_t *s) char *ws = (char *) s; len = wcslen(s); if ((len < 4) || (s[0] == L'.')) - return 0; + return 0; memcpy(ext, ws + ((len - 4) << 1), 8); if (! wcscasecmp(ext, L".HDI")) - return 1; + return 1; else - return 0; + return 0; } @@ -97,34 +103,34 @@ image_is_hdx(const wchar_t *s, int check_signature) wchar_t ext[5] = { 0, 0, 0, 0, 0 }; len = wcslen(s); if ((len < 4) || (s[0] == L'.')) - return 0; + return 0; memcpy(ext, ws + ((len - 4) << 1), 8); if (wcscasecmp(ext, L".HDX") == 0) { - if (check_signature) { - f = plat_fopen((wchar_t *)s, L"rb"); - if (!f) - return 0; - if (fseeko64(f, 0, SEEK_END)) - fatal("image_is_hdx(): Error while seeking"); - filelen = ftello64(f); - if (fseeko64(f, 0, SEEK_SET)) - fatal("image_is_hdx(): Error while seeking"); - if (filelen < 44) { - if (f != NULL) - fclose(f); - return 0; - } - if (fread(&signature, 1, 8, f) != 8) - fatal("image_is_hdx(): Error reading signature\n"); - fclose(f); - if (signature == 0xD778A82044445459ll) - return 1; - else - return 0; - } else - return 1; + if (check_signature) { + f = plat_fopen((wchar_t *)s, L"rb"); + if (!f) + return 0; + if (fseeko64(f, 0, SEEK_END)) + fatal("image_is_hdx(): Error while seeking"); + filelen = ftello64(f); + if (fseeko64(f, 0, SEEK_SET)) + fatal("image_is_hdx(): Error while seeking"); + if (filelen < 44) { + if (f != NULL) + fclose(f); + return 0; + } + if (fread(&signature, 1, 8, f) != 8) + fatal("image_is_hdx(): Error reading signature\n"); + fclose(f); + if (signature == 0xD778A82044445459ll) + return 1; + else + return 0; + } else + return 1; } else - return 0; + return 0; } @@ -132,22 +138,26 @@ int image_is_vhd(const wchar_t *s, int check_signature) { int len; + FILE* f; char *ws = (char *) s; wchar_t ext[5] = { 0, 0, 0, 0, 0 }; len = wcslen(s); if ((len < 4) || (s[0] == L'.')) - return 0; + return 0; memcpy(ext, ws + ((len - 4) << 1), 8); if (wcscasecmp(ext, L".VHD") == 0) { - if (check_signature) { - // if is VHD - return 1; - // else - // return 0; - } else - return 1; + if (check_signature) { + f = plat_fopen((wchar_t*)s, L"rb"); + if (!f) + return 0; + + bool is_vhd = mvhd_file_is_vhd(f); + fclose(f); + return is_vhd ? 1 : 0; + } else + return 1; } else - return 0; + return 0; } void @@ -158,28 +168,28 @@ hdd_image_calc_chs(uint32_t *c, uint32_t *h, uint32_t *s, uint32_t size) uint64_t ts = ((uint64_t) size) << 11LL; uint32_t spt, heads, cyl, cth; if (ts > 65535 * 16 * 255) - ts = 65535 * 16 * 255; + ts = 65535 * 16 * 255; if (ts >= 65535 * 16 * 63) { - spt = 255; - heads = 16; - cth = (uint32_t) (ts / spt); + spt = 255; + heads = 16; + cth = (uint32_t) (ts / spt); } else { - spt = 17; - cth = (uint32_t) (ts / spt); - heads = (cth +1023) / 1024; - if (heads < 4) - heads = 4; - if ((cth >= (heads * 1024)) || (heads > 16)) { - spt = 31; - heads = 16; - cth = (uint32_t) (ts / spt); - } - if (cth >= (heads * 1024)) { - spt = 63; - heads = 16; - cth = (uint32_t) (ts / spt); - } + spt = 17; + cth = (uint32_t) (ts / spt); + heads = (cth +1023) / 1024; + if (heads < 4) + heads = 4; + if ((cth >= (heads * 1024)) || (heads > 16)) { + spt = 31; + heads = 16; + cth = (uint32_t) (ts / spt); + } + if (cth >= (heads * 1024)) { + spt = 63; + heads = 16; + cth = (uint32_t) (ts / spt); + } } cyl = cth / heads; *c = cyl; @@ -209,18 +219,18 @@ prepare_new_hard_disk(uint8_t id, uint64_t full_size) /* First, write all the 1 MB blocks. */ if (t > 0) { - for (i = 0; i < t; i++) { - fseek(hdd_images[id].file, 0, SEEK_END); - fwrite(empty_sector_1mb, 1, 1048576, hdd_images[id].file); - pclog("#"); - } + for (i = 0; i < t; i++) { + fseek(hdd_images[id].file, 0, SEEK_END); + fwrite(empty_sector_1mb, 1, 1048576, hdd_images[id].file); + pclog("#"); + } } /* Then, write the remainder. */ if (size > 0) { - fseek(hdd_images[id].file, 0, SEEK_END); - fwrite(empty_sector_1mb, 1, size, hdd_images[id].file); - pclog("#"); + fseek(hdd_images[id].file, 0, SEEK_END); + fwrite(empty_sector_1mb, 1, size, hdd_images[id].file); + pclog("#"); } pclog("]\n"); /* Switch the suppression of seen messages back on. */ @@ -242,7 +252,7 @@ hdd_image_init(void) int i; for (i = 0; i < HDD_NUM; i++) - memset(&hdd_images[i], 0, sizeof(hdd_image_t)); + memset(&hdd_images[i], 0, sizeof(hdd_image_t)); } int @@ -256,19 +266,25 @@ hdd_image_load(int id) int c, ret; uint64_t s = 0; wchar_t *fn = hdd[id].fn; + char fn_multibyte_buf[1200]; int is_hdx[2] = { 0, 0 }; - int is_vhd[2] = { 0, 0 }; + int is_vhd[2] = { 0, 0 }; + int vhd_error = 0; memset(empty_sector, 0, sizeof(empty_sector)); hdd_images[id].base = 0; if (hdd_images[id].loaded) { - if (hdd_images[id].file) { - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; - } - hdd_images[id].loaded = 0; + if (hdd_images[id].file) { + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + } + else if (hdd_images[id].vhd) { + mvhd_close(hdd_images[id].vhd); + hdd_images[id].vhd = NULL; + } + hdd_images[id].loaded = 0; } is_hdx[0] = image_is_hdx(fn, 0); @@ -281,172 +297,199 @@ hdd_image_load(int id) /* Try to open existing hard disk image */ if (fn[0] == '.') { - hdd_image_log("File name starts with .\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; + hdd_image_log("File name starts with .\n"); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; } hdd_images[id].file = plat_fopen(fn, L"rb+"); if (hdd_images[id].file == NULL) { - /* Failed to open existing hard disk image */ - if (errno == ENOENT) { - /* Failed because it does not exist, - so try to create new file */ - if (hdd[id].wp) { - hdd_image_log("A write-protected image must exist\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } + /* Failed to open existing hard disk image */ + if (errno == ENOENT) { + /* Failed because it does not exist, + so try to create new file */ + if (hdd[id].wp) { + hdd_image_log("A write-protected image must exist\n"); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } - hdd_images[id].file = plat_fopen(fn, L"wb+"); - if (hdd_images[id].file == NULL) { - hdd_image_log("Unable to open image\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } else { - if (image_is_hdi(fn)) { - full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - hdd_images[id].base = 0x1000; - fwrite(&zero, 1, 4, hdd_images[id].file); - fwrite(&zero, 1, 4, hdd_images[id].file); - fwrite(&(hdd_images[id].base), 1, 4, hdd_images[id].file); - fwrite(&full_size, 1, 4, hdd_images[id].file); - fwrite(§or_size, 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); - for (c = 0; c < 0x3f8; c++) - fwrite(&zero, 1, 4, hdd_images[id].file); - hdd_images[id].type = 1; - } else if (is_hdx[0]) { - full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - hdd_images[id].base = 0x28; - fwrite(&signature, 1, 8, hdd_images[id].file); - fwrite(&full_size, 1, 8, hdd_images[id].file); - fwrite(§or_size, 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); - fwrite(&zero, 1, 4, hdd_images[id].file); - fwrite(&zero, 1, 4, hdd_images[id].file); - hdd_images[id].type = 2; - } - else - hdd_images[id].type = 0; - hdd_images[id].last_sector = 0; - } + hdd_images[id].file = plat_fopen(fn, L"wb+"); + if (hdd_images[id].file == NULL) { + hdd_image_log("Unable to open image\n"); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } else { + if (image_is_hdi(fn)) { + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].base = 0x1000; + fwrite(&zero, 1, 4, hdd_images[id].file); + fwrite(&zero, 1, 4, hdd_images[id].file); + fwrite(&(hdd_images[id].base), 1, 4, hdd_images[id].file); + fwrite(&full_size, 1, 4, hdd_images[id].file); + fwrite(§or_size, 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); + for (c = 0; c < 0x3f8; c++) + fwrite(&zero, 1, 4, hdd_images[id].file); + hdd_images[id].type = HDD_IMAGE_HDI; + } else if (is_hdx[0]) { + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].base = 0x28; + fwrite(&signature, 1, 8, hdd_images[id].file); + fwrite(&full_size, 1, 8, hdd_images[id].file); + fwrite(§or_size, 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); + fwrite(&zero, 1, 4, hdd_images[id].file); + fwrite(&zero, 1, 4, hdd_images[id].file); + hdd_images[id].type = HDD_IMAGE_HDX; + } else if (is_vhd[0]) { + fclose(hdd_images[id].file); + MVHDGeom geometry; + geometry.cyl = hdd[id].tracks; + geometry.heads = hdd[id].hpc; + geometry.spt = hdd[id].spt; + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; - s = full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; + wcstombs(fn_multibyte_buf, fn, sizeof fn_multibyte_buf); + hdd_images[id].vhd = mvhd_create_fixed(fn_multibyte_buf, geometry, &vhd_error, NULL); + if (hdd_images[id].vhd == NULL) + fatal("hdd_image_load(): VHD: Could not create VHD : %s\n", mvhd_strerr(vhd_error)); - ret = prepare_new_hard_disk(id, full_size); + hdd_images[id].type = HDD_IMAGE_VHD; + return 1; + } else { + hdd_images[id].type = HDD_IMAGE_RAW; + } + hdd_images[id].last_sector = 0; + } - if (is_vhd[0]) { - /* VHD image. */ - hdd_images[id].type = 3; - } + s = full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; - return ret; - } else { - /* Failed for another reason */ - hdd_image_log("Failed for another reason\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } + ret = prepare_new_hard_disk(id, full_size); + return ret; + } else { + /* Failed for another reason */ + hdd_image_log("Failed for another reason\n"); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } } else { - if (image_is_hdi(fn)) { - if (fseeko64(hdd_images[id].file, 0x8, SEEK_SET) == -1) - fatal("hdd_image_load(): HDI: Error seeking to offset 0x8\n"); - if (fread(&(hdd_images[id].base), 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading base offset\n"); - if (fseeko64(hdd_images[id].file, 0xC, SEEK_SET) == -1) - fatal("hdd_image_load(): HDI: Error seeking to offest 0xC\n"); - full_size = 0LL; - if (fread(&full_size, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading full size\n"); - if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) - fatal("hdd_image_load(): HDI: Error seeking to offset 0x10\n"); - if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading sector size\n"); - if (sector_size != 512) { - /* Sector size is not 512 */ - hdd_image_log("HDI: Sector size is not 512\n"); - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } - if (fread(&spt, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); - if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); - if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading number of tracks\n"); - hdd[id].spt = spt; - hdd[id].hpc = hpc; - hdd[id].tracks = tracks; - hdd_images[id].type = 1; - } else if (is_hdx[1]) { - hdd_images[id].base = 0x28; - if (fseeko64(hdd_images[id].file, 8, SEEK_SET) == -1) - fatal("hdd_image_load(): HDX: Error seeking to offset 0x8\n"); - if (fread(&full_size, 1, 8, hdd_images[id].file) != 8) - fatal("hdd_image_load(): HDX: Error reading full size\n"); - if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) - fatal("hdd_image_load(): HDX: Error seeking to offset 0x10\n"); - if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDX: Error reading sector size\n"); - if (sector_size != 512) { - /* Sector size is not 512 */ - hdd_image_log("HDX: Sector size is not 512\n"); - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } - if (fread(&spt, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); - if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); - if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDX: Error reading number of tracks\n"); - hdd[id].spt = spt; - hdd[id].hpc = hpc; - hdd[id].tracks = tracks; - hdd_images[id].type = 2; - } else if (is_vhd[1]) { - // full_size = VHD size in bytes - // hdd[id].tracks = VHD cylinders - // hdd[id].hpc = VHD heads - // hdd[id].spt = VHD spt - hdd_images[id].type = 3; - /* If we're here, this means there is a valid VHD footer in the - image, which means that by definition, all valid sectors - are there. */ - hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; - hdd_images[id].loaded = 1; - return 1; - } else { - full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - hdd_images[id].type = 0; - } + if (image_is_hdi(fn)) { + if (fseeko64(hdd_images[id].file, 0x8, SEEK_SET) == -1) + fatal("hdd_image_load(): HDI: Error seeking to offset 0x8\n"); + if (fread(&(hdd_images[id].base), 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading base offset\n"); + if (fseeko64(hdd_images[id].file, 0xC, SEEK_SET) == -1) + fatal("hdd_image_load(): HDI: Error seeking to offest 0xC\n"); + full_size = 0LL; + if (fread(&full_size, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading full size\n"); + if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) + fatal("hdd_image_load(): HDI: Error seeking to offset 0x10\n"); + if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading sector size\n"); + if (sector_size != 512) { + /* Sector size is not 512 */ + hdd_image_log("HDI: Sector size is not 512\n"); + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } + if (fread(&spt, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); + if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); + if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading number of tracks\n"); + hdd[id].spt = spt; + hdd[id].hpc = hpc; + hdd[id].tracks = tracks; + hdd_images[id].type = HDD_IMAGE_HDI; + } else if (is_hdx[1]) { + hdd_images[id].base = 0x28; + if (fseeko64(hdd_images[id].file, 8, SEEK_SET) == -1) + fatal("hdd_image_load(): HDX: Error seeking to offset 0x8\n"); + if (fread(&full_size, 1, 8, hdd_images[id].file) != 8) + fatal("hdd_image_load(): HDX: Error reading full size\n"); + if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) + fatal("hdd_image_load(): HDX: Error seeking to offset 0x10\n"); + if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDX: Error reading sector size\n"); + if (sector_size != 512) { + /* Sector size is not 512 */ + hdd_image_log("HDX: Sector size is not 512\n"); + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } + if (fread(&spt, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); + if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); + if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDX: Error reading number of tracks\n"); + hdd[id].spt = spt; + hdd[id].hpc = hpc; + hdd[id].tracks = tracks; + hdd_images[id].type = HDD_IMAGE_HDX; + } else if (is_vhd[1]) { + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + wcstombs(fn_multibyte_buf, fn, sizeof fn_multibyte_buf); + hdd_images[id].vhd = mvhd_open(fn_multibyte_buf, (bool)0, &vhd_error); + if (hdd_images[id].vhd == NULL) + { + if (vhd_error == MVHD_ERR_FILE) + fatal("hdd_image_load(): VHD: Error opening VHD file '%s': %s\n", fn_multibyte_buf, strerror(mvhd_errno)); + else + fatal("hdd_image_load(): VHD: Error opening VHD file '%s': %s\n", fn_multibyte_buf, mvhd_strerr(vhd_error)); + } + else if (vhd_error == MVHD_ERR_TIMESTAMP) + { + fatal("hdd_image_load(): VHD: Parent/child timestamp mismatch for VHD file '%s'\n", fn_multibyte_buf); + } + + full_size = hdd_images[id].vhd->footer.curr_sz; + hdd[id].tracks = hdd_images[id].vhd->footer.geom.cyl; + hdd[id].hpc = hdd_images[id].vhd->footer.geom.heads; + hdd[id].spt = hdd_images[id].vhd->footer.geom.spt; + hdd_images[id].type = HDD_IMAGE_VHD; + /* If we're here, this means there is a valid VHD footer in the + image, which means that by definition, all valid sectors + are there. */ + hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; + hdd_images[id].loaded = 1; + return 1; + } else { + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].type = HDD_IMAGE_RAW; + } } if (fseeko64(hdd_images[id].file, 0, SEEK_END) == -1) - fatal("hdd_image_load(): Error seeking to the end of file\n"); + fatal("hdd_image_load(): Error seeking to the end of file\n"); s = ftello64(hdd_images[id].file); if (s < (full_size + hdd_images[id].base)) - ret = prepare_new_hard_disk(id, full_size); + ret = prepare_new_hard_disk(id, full_size); else { - hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; - hdd_images[id].loaded = 1; - ret = 1; + hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; + hdd_images[id].loaded = 1; + ret = 1; } return ret; @@ -460,36 +503,47 @@ hdd_image_seek(uint8_t id, uint32_t sector) addr = (uint64_t)sector << 9LL; hdd_images[id].pos = sector; - if (fseeko64(hdd_images[id].file, addr + hdd_images[id].base, SEEK_SET) == -1) - fatal("hdd_image_seek(): Error seeking\n"); + if (hdd_images[id].type != HDD_IMAGE_VHD) { + if (fseeko64(hdd_images[id].file, addr + hdd_images[id].base, SEEK_SET) == -1) + fatal("hdd_image_seek(): Error seeking\n"); + } } void hdd_image_read(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) { - int i; + if (hdd_images[id].type == HDD_IMAGE_VHD) { + int non_transferred_sectors = mvhd_read_sectors(hdd_images[id].vhd, sector, count, buffer); + hdd_images[id].pos = sector + count - non_transferred_sectors - 1; + } else { + int i; - if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { - fatal("Hard disk image %i: Read error during seek\n", id); - return; - } + if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { + fatal("Hard disk image %i: Read error during seek\n", id); + return; + } - for (i = 0; i < count; i++) { - if (feof(hdd_images[id].file)) - break; + for (i = 0; i < count; i++) { + if (feof(hdd_images[id].file)) + break; - hdd_images[id].pos = sector + i; - fread(buffer + (i << 9), 1, 512, hdd_images[id].file); - } + hdd_images[id].pos = sector + i; + fread(buffer + (i << 9), 1, 512, hdd_images[id].file); + } + } } uint32_t hdd_sectors(uint8_t id) { - fseeko64(hdd_images[id].file, 0, SEEK_END); - return (uint32_t) ((ftello64(hdd_images[id].file) - hdd_images[id].base) >> 9); + if (hdd_images[id].type == HDD_IMAGE_VHD) { + return (uint32_t) (hdd_images[id].vhd->footer.curr_sz >> 9); + } else { + fseeko64(hdd_images[id].file, 0, SEEK_END); + return (uint32_t)((ftello64(hdd_images[id].file) - hdd_images[id].base) >> 9); + } } @@ -500,12 +554,12 @@ hdd_image_read_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) uint32_t sectors = hdd_sectors(id); if ((sectors - sector) < transfer_sectors) - transfer_sectors = sectors - sector; + transfer_sectors = sectors - sector; hdd_image_read(id, sector, transfer_sectors, buffer); if (count != transfer_sectors) - return 1; + return 1; return 0; } @@ -513,20 +567,25 @@ hdd_image_read_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) void hdd_image_write(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) { - int i; + if (hdd_images[id].type == HDD_IMAGE_VHD) { + int non_transferred_sectors = mvhd_write_sectors(hdd_images[id].vhd, sector, count, buffer); + hdd_images[id].pos = sector + count - non_transferred_sectors - 1; + } else { + int i; - if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { - fatal("Hard disk image %i: Write error during seek\n", id); - return; - } + if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { + fatal("Hard disk image %i: Write error during seek\n", id); + return; + } - for (i = 0; i < count; i++) { - if (feof(hdd_images[id].file)) - break; + for (i = 0; i < count; i++) { + if (feof(hdd_images[id].file)) + break; - hdd_images[id].pos = sector + i; - fwrite(buffer + (i << 9), 512, 1, hdd_images[id].file); - } + hdd_images[id].pos = sector + i; + fwrite(buffer + (i << 9), 512, 1, hdd_images[id].file); + } + } } @@ -537,12 +596,12 @@ hdd_image_write_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) uint32_t sectors = hdd_sectors(id); if ((sectors - sector) < transfer_sectors) - transfer_sectors = sectors - sector; + transfer_sectors = sectors - sector; hdd_image_write(id, sector, transfer_sectors, buffer); if (count != transfer_sectors) - return 1; + return 1; return 0; } @@ -550,22 +609,27 @@ hdd_image_write_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) void hdd_image_zero(uint8_t id, uint32_t sector, uint32_t count) { - uint32_t i = 0; + if (hdd_images[id].type == HDD_IMAGE_VHD) { + int non_transferred_sectors = mvhd_format_sectors(hdd_images[id].vhd, sector, count); + hdd_images[id].pos = sector + count - non_transferred_sectors - 1; + } else { + uint32_t i = 0; - memset(empty_sector, 0, 512); + memset(empty_sector, 0, 512); - if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { - fatal("Hard disk image %i: Zero error during seek\n", id); - return; - } + if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { + fatal("Hard disk image %i: Zero error during seek\n", id); + return; + } - for (i = 0; i < count; i++) { - if (feof(hdd_images[id].file)) - break; + for (i = 0; i < count; i++) { + if (feof(hdd_images[id].file)) + break; - hdd_images[id].pos = sector + i; - fwrite(empty_sector, 512, 1, hdd_images[id].file); - } + hdd_images[id].pos = sector + i; + fwrite(empty_sector, 512, 1, hdd_images[id].file); + } + } } @@ -576,12 +640,12 @@ hdd_image_zero_ex(uint8_t id, uint32_t sector, uint32_t count) uint32_t sectors = hdd_sectors(id); if ((sectors - sector) < transfer_sectors) - transfer_sectors = sectors - sector; + transfer_sectors = sectors - sector; hdd_image_zero(id, sector, transfer_sectors); if (count != transfer_sectors) - return 1; + return 1; return 0; } @@ -611,21 +675,24 @@ void hdd_image_unload(uint8_t id, int fn_preserve) { if (wcslen(hdd[id].fn) == 0) - return; + return; if (hdd_images[id].loaded) { - if (hdd_images[id].file != NULL) { - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; - } - hdd_images[id].loaded = 0; + if (hdd_images[id].file != NULL) { + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + } else if (hdd_images[id].vhd != NULL) { + mvhd_close(hdd_images[id].vhd); + hdd_images[id].vhd = NULL; + } + hdd_images[id].loaded = 0; } hdd_images[id].last_sector = -1; memset(hdd[id].prev_fn, 0, sizeof(hdd[id].prev_fn)); if (fn_preserve) - wcscpy(hdd[id].prev_fn, hdd[id].fn); + wcscpy(hdd[id].prev_fn, hdd[id].fn); memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); } @@ -636,12 +703,16 @@ hdd_image_close(uint8_t id) hdd_image_log("hdd_image_close(%i)\n", id); if (!hdd_images[id].loaded) - return; + return; if (hdd_images[id].file != NULL) { - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + } else if (hdd_images[id].vhd != NULL) { + mvhd_close(hdd_images[id].vhd); + hdd_images[id].vhd = NULL; } + memset(&hdd_images[id], 0, sizeof(hdd_image_t)); hdd_images[id].loaded = 0; } diff --git a/src/include/86box/language.h b/src/include/86box/language.h index a16f2f0ac..c4a4b64a5 100644 --- a/src/include/86box/language.h +++ b/src/include/86box/language.h @@ -140,6 +140,12 @@ #define IDS_4119 4119 // "Unsupported disk image" #define IDS_4120 4120 // "Overwrite" #define IDS_4121 4121 // "Don't Overwrite" +#define IDS_4122 4122 // "Raw image (.img)" +#define IDS_4123 4123 // "HDI image (.hdi)" +#define IDS_4124 4124 // "HDX image (.hdx)" +#define IDS_4125 4125 // "Fixed-size VHD (.vhd)" +#define IDS_4126 4126 // "Dynamic-size VHD (.vhd)" +#define IDS_4127 4127 // "Differencing VHD (.vhd)" #define IDS_4352 4352 // "MFM/RLL" #define IDS_4353 4353 // "XT IDE" @@ -209,7 +215,7 @@ #define STR_NUM_2048 92 #define STR_NUM_3072 11 -#define STR_NUM_4096 18 +#define STR_NUM_4096 32 #define STR_NUM_4352 6 #define STR_NUM_4608 6 #define STR_NUM_5120 1 diff --git a/src/include/86box/resource.h b/src/include/86box/resource.h index b0422d18b..b0e194bb8 100644 --- a/src/include/86box/resource.h +++ b/src/include/86box/resource.h @@ -105,6 +105,8 @@ #define IDT_1771 1771 /* ID: */ #define IDT_1772 1772 /* Channel */ #define IDT_1773 1773 /* Type: */ +#define IDT_1774 1774 /* Image Format: */ +#define IDT_1775 1775 /* Block Size: */ /* @@ -218,6 +220,8 @@ #define IDC_EDIT_HD_SIZE 1164 #define IDC_COMBO_HD_TYPE 1165 #define IDC_PBAR_IMG_CREATE 1166 +#define IDC_COMBO_HD_IMG_FORMAT 1167 +#define IDC_COMBO_HD_BLOCK_SIZE 1168 #define IDC_REMOV_DEVICES 1170 /* floppy and cd-rom drives config */ #define IDC_LIST_FLOPPY_DRIVES 1171 diff --git a/src/include/86box/win.h b/src/include/86box/win.h index a181dad06..aae82adac 100644 --- a/src/include/86box/win.h +++ b/src/include/86box/win.h @@ -193,11 +193,12 @@ extern int MediaMenuHandler(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPara /* Functions in win_dialog.c: */ -extern int file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save); -extern int file_dlg(HWND hwnd, WCHAR *f, char *fn, int save); -extern int file_dlg_mb(HWND hwnd, char *f, char *fn, int save); -extern int file_dlg_w_st(HWND hwnd, int i, WCHAR *fn, int save); -extern int file_dlg_st(HWND hwnd, int i, char *fn, int save); +/* Pass NULL in the title param to use the default title. */ +extern int file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, WCHAR *title, int save); +extern int file_dlg(HWND hwnd, WCHAR *f, char *fn, char *title, int save); +extern int file_dlg_mb(HWND hwnd, char *f, char *fn, char *title, int save); +extern int file_dlg_w_st(HWND hwnd, int i, WCHAR *fn, char *title, int save); +extern int file_dlg_st(HWND hwnd, int i, char *fn, char *title, int save); extern wchar_t *BrowseFolder(wchar_t *saved_path, wchar_t *title); diff --git a/src/win/86Box.rc b/src/win/86Box.rc index 7216bc391..873cb9e1f 100644 --- a/src/win/86Box.rc +++ b/src/win/86Box.rc @@ -575,13 +575,13 @@ BEGIN WS_VSCROLL | WS_TABSTOP END -DLG_CFG_HARD_DISKS_ADD DIALOG DISCARDABLE 0, 0, 219, 111 +DLG_CFG_HARD_DISKS_ADD DIALOG DISCARDABLE 0, 0, 219, 149 STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Add Hard Disk" FONT 9, "Segoe UI" BEGIN - DEFPUSHBUTTON "OK",IDOK,55,89,50,14 - PUSHBUTTON "Cancel",IDCANCEL,112,89,50,14 + DEFPUSHBUTTON "OK",IDOK,55,127,50,14 + PUSHBUTTON "Cancel",IDCANCEL,112,127,50,14 EDITTEXT IDC_EDIT_HD_FILE_NAME,7,16,153,12 PUSHBUTTON "&Specify...",IDC_CFILE,167,16,44,12 EDITTEXT IDC_EDIT_HD_SPT,183,34,28,12 @@ -607,6 +607,12 @@ BEGIN LTEXT "ID:",IDT_1723,99,73,34,8 COMBOBOX IDC_COMBO_HD_CHANNEL_IDE,134,71,77,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Image Format:",IDT_1774,7,92,50,12 + COMBOBOX IDC_COMBO_HD_IMG_FORMAT,58,90,153,12,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Block Size:",IDT_1775,7,111,50,12 + COMBOBOX IDC_COMBO_HD_BLOCK_SIZE,58,109,153,12,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP LTEXT "Progress:",IDT_1752,7,7,204,9 CONTROL "IMGCreateProgress",IDC_PBAR_IMG_CREATE,"msctls_progress32",PBS_SMOOTH | WS_BORDER,7,16,204,12 @@ -1055,6 +1061,12 @@ BEGIN IDS_4119 "Unsupported disk image" IDS_4120 "Overwrite" IDS_4121 "Don't Overwrite" + IDS_4122 "Raw image (.img)" + IDS_4123 "HDI image (.hdi)" + IDS_4124 "HDX image (.hdx)" + IDS_4125 "Fixed-size VHD (.vhd)" + IDS_4126 "Dynamic-size VHD (.vhd)" + IDS_4127 "Differencing VHD (.vhd)" IDS_4352 "MFM/RLL" IDS_4353 "XTA" diff --git a/src/win/win_devconf.c b/src/win/win_devconf.c index 280dce484..18b567d79 100644 --- a/src/win/win_devconf.c +++ b/src/win/win_devconf.c @@ -412,7 +412,7 @@ deviceconfig_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) ws[c] = 0; } - if (!file_dlg(hdlg, ws, s, 0)) + if (!file_dlg(hdlg, ws, s, NULL, 0)) SendMessage(h, WM_SETTEXT, 0, (LPARAM)wopenfilestring); } break; diff --git a/src/win/win_dialog.c b/src/win/win_dialog.c index 563920cc3..16b1a2cc3 100644 --- a/src/win/win_dialog.c +++ b/src/win/win_dialog.c @@ -152,7 +152,7 @@ ui_msgbox_ex(int flags, void *header, void *message, void *btn1, void *btn2, voi int -file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save) +file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, WCHAR *title, int save) { OPENFILENAME ofn; BOOL r; @@ -177,7 +177,9 @@ file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save) ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST; if (! save) - ofn.Flags |= OFN_FILEMUSTEXIST; + ofn.Flags |= OFN_FILEMUSTEXIST; + if (title) + ofn.lpstrTitle = title; /* Display the Open dialog box. */ if (save) @@ -199,37 +201,44 @@ file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save) int -file_dlg(HWND hwnd, WCHAR *f, char *fn, int save) +file_dlg(HWND hwnd, WCHAR *f, char *fn, char *title, int save) { - WCHAR ufn[512]; + WCHAR ufn[512], title_buf[512]; mbstowcs(ufn, fn, strlen(fn) + 1); + if (title) + mbstowcs(title_buf, title, sizeof title_buf); - return(file_dlg_w(hwnd, f, ufn, save)); + return(file_dlg_w(hwnd, f, ufn, title ? title_buf : NULL, save)); } int -file_dlg_mb(HWND hwnd, char *f, char *fn, int save) +file_dlg_mb(HWND hwnd, char *f, char *fn, char *title, int save) { - WCHAR uf[512], ufn[512]; + WCHAR uf[512], ufn[512], title_buf[512]; mbstowcs(uf, f, strlen(fn) + 1); mbstowcs(ufn, fn, strlen(fn) + 1); + if (title) + mbstowcs(title_buf, title, sizeof title_buf); - return(file_dlg_w(hwnd, uf, ufn, save)); + return(file_dlg_w(hwnd, uf, ufn, title ? title_buf : NULL, save)); } int -file_dlg_w_st(HWND hwnd, int id, WCHAR *fn, int save) +file_dlg_w_st(HWND hwnd, int id, WCHAR *fn, char *title, int save) { - return(file_dlg_w(hwnd, plat_get_string(id), fn, save)); + WCHAR title_buf[512]; + if (title) + mbstowcs(title_buf, title, sizeof title_buf); + return(file_dlg_w(hwnd, plat_get_string(id), fn, title ? title_buf : NULL, save)); } int -file_dlg_st(HWND hwnd, int id, char *fn, int save) -{ - return(file_dlg(hwnd, plat_get_string(id), fn, save)); +file_dlg_st(HWND hwnd, int id, char *fn, char *title, int save) +{ + return(file_dlg(hwnd, plat_get_string(id), fn, title, save)); } diff --git a/src/win/win_media_menu.c b/src/win/win_media_menu.c index 45963c15a..d7e6b6cba 100644 --- a/src/win/win_media_menu.c +++ b/src/win/win_media_menu.c @@ -413,7 +413,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) wp = 1; /* FALLTHROUGH */ case IDM_FLOPPY_IMAGE_EXISTING: - ret = file_dlg_w_st(hwnd, IDS_2109, floppyfns[id], 0); + ret = file_dlg_w_st(hwnd, IDS_2109, floppyfns[id], NULL, 0); if (! ret) { floppy_mount(id, wopenfilestring, wp); } @@ -424,7 +424,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_FLOPPY_EXPORT_TO_86F: - ret = file_dlg_w_st(hwnd, IDS_2076, floppyfns[id], 1); + ret = file_dlg_w_st(hwnd, IDS_2076, floppyfns[id], NULL, 1); if (! ret) { plat_pause(1); ret = d86f_export(id, wopenfilestring); @@ -450,7 +450,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_CDROM_IMAGE: - if (!file_dlg_w_st(hwnd, IDS_2075, cdrom[id].image_path, 0)) { + if (!file_dlg_w_st(hwnd, IDS_2075, cdrom[id].image_path, NULL, 0)) { cdrom_mount(id, wopenfilestring); } break; @@ -463,7 +463,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) wp = 1; /* FALLTHROUGH */ case IDM_ZIP_IMAGE_EXISTING: - ret = file_dlg_w_st(hwnd, IDS_2058, zip_drives[id].image_path, 0); + ret = file_dlg_w_st(hwnd, IDS_2058, zip_drives[id].image_path, NULL, 0); if (! ret) zip_mount(id, wopenfilestring, wp); break; @@ -484,7 +484,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) wp = 1; /* FALLTHROUGH */ case IDM_MO_IMAGE_EXISTING: - ret = file_dlg_w_st(hwnd, IDS_2116, mo_drives[id].image_path, 0); + ret = file_dlg_w_st(hwnd, IDS_2116, mo_drives[id].image_path, NULL, 0); if (! ret) mo_mount(id, wopenfilestring, wp); break; diff --git a/src/win/win_new_floppy.c b/src/win/win_new_floppy.c index 9845c77cf..090bb9df4 100644 --- a/src/win/win_new_floppy.c +++ b/src/win/win_new_floppy.c @@ -777,7 +777,7 @@ NewFloppyDialogProcedure(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) return TRUE; case IDC_CFILE: - if (!file_dlg_w(hdlg, plat_get_string(is_mo ? IDS_2139 : (is_zip ? IDS_2055 : IDS_2062)), L"", 1)) { + if (!file_dlg_w(hdlg, plat_get_string(is_mo ? IDS_2139 : (is_zip ? IDS_2055 : IDS_2062)), L"", NULL, 1)) { if (!wcschr(wopenfilestring, L'.')) { if (wcslen(wopenfilestring) && (wcslen(wopenfilestring) <= 256)) { twcs = &wopenfilestring[wcslen(wopenfilestring)]; diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 17cf4acf8..a23690b1d 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -64,6 +64,8 @@ #include <86box/plat_midi.h> #include <86box/ui.h> #include <86box/win.h> +#include "../disk/minivhd/minivhd.h" +#include "../disk/minivhd/minivhd_util.h" /* Icon, Bus, File, C, H, S, Size */ @@ -2344,6 +2346,17 @@ set_edit_box_contents(HWND hdlg, int id, uint32_t val) SendMessage(h, WM_SETTEXT, (WPARAM) wcslen(szText), (LPARAM) szText); } +static void set_edit_box_text_contents(HWND hdlg, int id, WCHAR* text) +{ + HWND h = GetDlgItem(hdlg, id); + SendMessage(h, WM_SETTEXT, (WPARAM) wcslen(text), (LPARAM) text); +} + +static void get_edit_box_text_contents(HWND hdlg, int id, WCHAR* text_buffer, int buffer_size) +{ + HWND h = GetDlgItem(hdlg, id); + SendMessage(h, WM_GETTEXT, (WPARAM) buffer_size, (LPARAM) text_buffer); +} static int hdconf_initialize_hdt_combo(HWND hdlg) { @@ -2387,6 +2400,161 @@ recalc_selection(HWND hdlg) settings_set_cur_sel(hdlg, IDC_COMBO_HD_TYPE, selection); } +HWND vhd_progress_hdlg; + +static void vhd_progress_callback(uint32_t current_sector, uint32_t total_sectors) +{ + MSG msg; + HWND h = GetDlgItem(vhd_progress_hdlg, IDC_PBAR_IMG_CREATE); + SendMessage(h, PBM_SETPOS, (WPARAM) current_sector, (LPARAM) 0); + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +/* If the disk geometry requested in the 86Box GUI is not compatible with the internal VHD geometry, + * we adjust it to the next-largest size that is compatible. On average, this will be a difference + * of about 21 MB, and should only be necessary for VHDs larger than 31.5 GB, so should never be more + * than a tenth of a percent change in size. + */ +static void adjust_86box_geometry_for_vhd(MVHDGeom *_86box_geometry, MVHDGeom *vhd_geometry) +{ + if (_86box_geometry->cyl <= 65535) { + vhd_geometry->cyl = _86box_geometry->cyl; + vhd_geometry->heads = _86box_geometry->heads; + vhd_geometry->spt = _86box_geometry->spt; + return; + } + + int desired_sectors = _86box_geometry->cyl * _86box_geometry->heads * _86box_geometry->spt; + if (desired_sectors > 267321600) + desired_sectors = 267321600; + + int remainder = desired_sectors % 85680; /* 8560 is the LCM of 1008 (63*16) and 4080 (255*16) */ + if (remainder > 0) + desired_sectors += (85680 - remainder); + + _86box_geometry->cyl = desired_sectors / (16 * 63); + _86box_geometry->heads = 16; + _86box_geometry->spt = 63; + + vhd_geometry->cyl = desired_sectors / (16 * 255); + vhd_geometry->heads = 16; + vhd_geometry->spt = 255; +} + +static void adjust_vhd_geometry_for_86box(MVHDGeom *vhd_geometry) +{ + if (vhd_geometry->spt <= 63) + return; + + int desired_sectors = vhd_geometry->cyl * vhd_geometry->heads * vhd_geometry->spt; + if (desired_sectors > 267321600) + desired_sectors = 267321600; + + int remainder = desired_sectors % 85680; /* 8560 is the LCM of 1008 (63*16) and 4080 (255*16) */ + if (remainder > 0) + desired_sectors -= remainder; + + vhd_geometry->cyl = desired_sectors / (16 * 63); + vhd_geometry->heads = 16; + vhd_geometry->spt = 63; +} + +static MVHDGeom create_drive_vhd_fixed(char* filename, int cyl, int heads, int spt) +{ + MVHDGeom _86box_geometry = { .cyl = cyl, .heads = heads, .spt = spt }; + MVHDGeom vhd_geometry; + adjust_86box_geometry_for_vhd(&_86box_geometry, &vhd_geometry); + + HWND h = GetDlgItem(vhd_progress_hdlg, IDC_PBAR_IMG_CREATE); + settings_show_window(vhd_progress_hdlg, IDT_1731, FALSE); + settings_show_window(vhd_progress_hdlg, IDC_EDIT_HD_FILE_NAME, FALSE); + settings_show_window(vhd_progress_hdlg, IDC_CFILE, FALSE); + settings_show_window(vhd_progress_hdlg, IDC_PBAR_IMG_CREATE, TRUE); + settings_enable_window(vhd_progress_hdlg, IDT_1752, TRUE); + SendMessage(h, PBM_SETRANGE32, (WPARAM) 0, (LPARAM) vhd_geometry.cyl * vhd_geometry.heads * vhd_geometry.spt); + SendMessage(h, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0); + + int vhd_error = 0; + MVHDMeta *vhd = mvhd_create_fixed(filename, vhd_geometry, &vhd_error, vhd_progress_callback); + if (vhd == NULL) + { + _86box_geometry.cyl = 0; + _86box_geometry.heads = 0; + _86box_geometry.spt = 0; + } + else + { + mvhd_close(vhd); + } + + return _86box_geometry; +} + +static MVHDGeom create_drive_vhd_dynamic(char* filename, int cyl, int heads, int spt, int blocksize) +{ + MVHDGeom _86box_geometry = { .cyl = cyl, .heads = heads, .spt = spt }; + MVHDGeom vhd_geometry; + adjust_86box_geometry_for_vhd(&_86box_geometry, &vhd_geometry); + int vhd_error = 0; + MVHDCreationOptions options; + options.block_size_in_sectors = blocksize; + options.path = filename; + options.size_in_bytes = 0; + options.geometry = vhd_geometry; + options.type = MVHD_TYPE_DYNAMIC; + + MVHDMeta *vhd = mvhd_create_ex(options, &vhd_error); + if (vhd == NULL) + { + _86box_geometry.cyl = 0; + _86box_geometry.heads = 0; + _86box_geometry.spt = 0; + } + else + { + mvhd_close(vhd); + } + + return _86box_geometry; +} + +static MVHDGeom create_drive_vhd_diff(char* filename, char* parent_filename, int blocksize) +{ + int vhd_error = 0; + MVHDCreationOptions options; + options.block_size_in_sectors = blocksize; + options.path = filename; + options.parent_path = parent_filename; + options.type = MVHD_TYPE_DIFF; + + MVHDMeta *vhd = mvhd_create_ex(options, &vhd_error); + MVHDGeom vhd_geometry; + if (vhd == NULL) + { + vhd_geometry.cyl = 0; + vhd_geometry.heads = 0; + vhd_geometry.spt = 0; + } + else + { + vhd_geometry = mvhd_get_geometry(vhd); + + if (vhd_geometry.spt > 63) + { + vhd_geometry.cyl = mvhd_calc_size_sectors(&vhd_geometry) / (16 * 63); + vhd_geometry.heads = 16; + vhd_geometry.spt = 63; + } + + mvhd_close(vhd); + } + + return vhd_geometry; +} + #if defined(__amd64__) || defined(__aarch64__) static LRESULT CALLBACK @@ -2400,19 +2568,26 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM uint32_t temp, i = 0, sector_size = 512; uint32_t zero = 0, base = 0x1000; uint64_t signature = 0xD778A82044445459ll; - uint64_t temp_size, r = 0; + uint64_t r = 0; char *big_buf; + char hd_file_name_multibyte[1200]; int b = 0; + int vhd_error = 0; uint8_t channel = 0; uint8_t id = 0; wchar_t *twcs; MSG msg; + int img_format, block_size; + WCHAR text_buf[256]; + RECT rect; + POINT point; + int dlg_height_adjust; switch (message) { case WM_INITDIALOG: memset(hd_file_name, 0, sizeof(hd_file_name)); - hdd_ptr = &(temp_hdd[next_free_id]); + hdd_ptr = &(temp_hdd[next_free_id]); SetWindowText(hdlg, plat_get_string((existing & 1) ? IDS_4103 : IDS_4102)); @@ -2426,15 +2601,61 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM size = (tracks * hpc * spt) << 9; set_edit_box_contents(hdlg, IDC_EDIT_HD_SIZE, (uint32_t) (size >> 20LL)); hdconf_initialize_hdt_combo(hdlg); + + // TODO: Why is it crashing when I try to use string resources here? + // settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4122)); + // settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4123)); + // settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4124)); + // settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4125)); + // settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4126)); + // settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4127)); + + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, (LPARAM) L"Raw image (.img)"); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, (LPARAM) L"HDI image (.hdi)"); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, (LPARAM) L"HDX image (.hdx)"); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, (LPARAM) L"Fixed-size VHD (.vhd)"); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, (LPARAM) L"Dynamic-size VHD (.vhd)"); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, (LPARAM) L"Differencing VHD (.vhd)"); + settings_set_cur_sel(hdlg, IDC_COMBO_HD_IMG_FORMAT, 0); + + settings_add_string(hdlg, IDC_COMBO_HD_BLOCK_SIZE, (LPARAM) L"Large blocks (2 MB)"); + settings_add_string(hdlg, IDC_COMBO_HD_BLOCK_SIZE, (LPARAM) L"Small blocks (512 KB)"); + settings_set_cur_sel(hdlg, IDC_COMBO_HD_BLOCK_SIZE, 0); + + settings_show_window(hdlg, IDC_COMBO_HD_BLOCK_SIZE, FALSE); + settings_show_window(hdlg, IDT_1775, FALSE); + if (existing & 1) { settings_enable_window(hdlg, IDC_EDIT_HD_SPT, FALSE); settings_enable_window(hdlg, IDC_EDIT_HD_HPC, FALSE); settings_enable_window(hdlg, IDC_EDIT_HD_CYL, FALSE); settings_enable_window(hdlg, IDC_EDIT_HD_SIZE, FALSE); settings_enable_window(hdlg, IDC_COMBO_HD_TYPE, FALSE); + settings_show_window(hdlg, IDC_COMBO_HD_IMG_FORMAT, FALSE); + settings_show_window(hdlg, IDT_1774, FALSE); + + /* adjust window size */ + GetWindowRect(hdlg, &rect); + OffsetRect(&rect, -rect.left, -rect.top); + dlg_height_adjust = rect.bottom / 5; + SetWindowPos(hdlg, NULL, 0, 0, rect.right, rect.bottom - dlg_height_adjust, SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOZORDER); + h = GetDlgItem(hdlg, IDOK); + GetWindowRect(h, &rect); + point.x = rect.left; + point.y = rect.top; + ScreenToClient(hdlg, &point); + SetWindowPos(h, NULL, point.x, point.y - dlg_height_adjust, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_NOZORDER); + h = GetDlgItem(hdlg, IDCANCEL); + GetWindowRect(h, &rect); + point.x = rect.left; + point.y = rect.top; + ScreenToClient(hdlg, &point); + SetWindowPos(h, NULL, point.x, point.y - dlg_height_adjust, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_NOZORDER); + chs_enabled = 0; } else chs_enabled = 1; + add_locations(hdlg); hdd_ptr->bus = HDD_BUS_IDE; max_spt = 63; @@ -2502,19 +2723,22 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM memset(hdd_ptr->fn, 0, sizeof(hdd_ptr->fn)); wcscpy(hdd_ptr->fn, hd_file_name); + wcstombs(hd_file_name_multibyte, hd_file_name, sizeof hd_file_name_multibyte); sector_size = 512; if (!(existing & 1) && (wcslen(hd_file_name) > 0)) { - f = _wfopen(hd_file_name, L"wb"); - - if (size > 0x1FFFFFFE00ll) { - fclose(f); + if (size > 0x1FFFFFFE00ll) { settings_msgbox_header(MBX_ERROR, (wchar_t *) IDS_4116, (wchar_t *) IDS_4105); return TRUE; } - if (image_is_hdi(hd_file_name)) { + img_format = settings_get_cur_sel(hdlg, IDC_COMBO_HD_IMG_FORMAT); + if (img_format < 3) { + f = _wfopen(hd_file_name, L"wb"); + } + + if (img_format == 1) { /* HDI file */ if (size >= 0x100000000ll) { fclose(f); settings_msgbox_header(MBX_ERROR, (wchar_t *) IDS_4116, (wchar_t *) IDS_4104); @@ -2532,7 +2756,7 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM for (i = 0; i < 0x3f8; i++) fwrite(&zero, 1, 4, f); - } else if (image_is_hdx(hd_file_name, 0)) { + } else if (img_format == 2) { /* HDX file */ fwrite(&signature, 1, 8, f); /* 00000000: Signature */ fwrite(&size, 1, 8, f); /* 00000008: Full size of the data (64-bit) */ fwrite(§or_size, 1, 4, f); /* 00000010: Sector size in bytes */ @@ -2541,12 +2765,39 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM fwrite(&tracks, 1, 4, f); /* 0000001C: Cylinders */ fwrite(&zero, 1, 4, f); /* 00000020: [Translation] Sectors per cylinder */ fwrite(&zero, 1, 4, f); /* 00000004: [Translation] Heads per cylinder */ - } + } else if (img_format >= 3) { /* VHD file */ + MVHDGeom _86box_geometry; + block_size = settings_get_cur_sel(hdlg, IDC_COMBO_HD_BLOCK_SIZE) == 0 ? MVHD_BLOCK_LARGE : MVHD_BLOCK_SMALL; + switch (img_format) { + case 3: + vhd_progress_hdlg = hdlg; + _86box_geometry = create_drive_vhd_fixed(hd_file_name_multibyte, tracks, hpc, spt); + break; + case 4: + _86box_geometry = create_drive_vhd_dynamic(hd_file_name_multibyte, tracks, hpc, spt, block_size); + break; + case 5: + if (file_dlg_w(hdlg, L"VHD files (*.VHD)\0*.VHD\0All files (*.*)\0*.*\0", L"", L"Select the parent VHD",0)) { + return TRUE; + } + _86box_geometry = create_drive_vhd_diff(hd_file_name_multibyte, openfilestring, block_size); + break; + } + + if (img_format != 5) + settings_msgbox_header(MBX_INFO, (wchar_t *) IDS_4113, (wchar_t *) IDS_4117); + + hdd_ptr->tracks = _86box_geometry.cyl; + hdd_ptr->hpc = _86box_geometry.heads; + hdd_ptr->spt = _86box_geometry.spt; + + hard_disk_added = 1; + EndDialog(hdlg, 0); + return TRUE; + } big_buf = (char *) malloc(1048576); - memset(big_buf, 0, 1048576); - - temp_size = size; + memset(big_buf, 0, 1048576); r = size >> 20; size &= 0xfffff; @@ -2580,12 +2831,7 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM DispatchMessage(&msg); } } - } - - if (image_is_vhd(hd_file_name, 0)) { - /* VHD image. */ - // Create VHD image here of size temp_size bytes. - } + } free(big_buf); @@ -2603,8 +2849,8 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM EndDialog(hdlg, 0); return TRUE; - case IDC_CFILE: - if (!file_dlg_w(hdlg, plat_get_string(IDS_4106), L"", !(existing & 1))) { + case IDC_CFILE: + if (!file_dlg_w(hdlg, plat_get_string(IDS_4106), L"", NULL, !(existing & 1))) { if (!wcschr(wopenfilestring, L'.')) { if (wcslen(wopenfilestring) && (wcslen(wopenfilestring) <= 256)) { twcs = &wopenfilestring[wcslen(wopenfilestring)]; @@ -2645,7 +2891,39 @@ hdd_add_file_open_error: fread(&hpc, 1, 4, f); fread(&tracks, 1, 4, f); } else if (image_is_vhd(wopenfilestring, 1)) { - // Read VHD geometry + fclose(f); + wcstombs(hd_file_name_multibyte, wopenfilestring, sizeof hd_file_name_multibyte); + MVHDMeta* vhd = mvhd_open(hd_file_name_multibyte, 0, &vhd_error); + if (vhd == NULL) { + settings_msgbox_header(MBX_ERROR, (existing & 1) ? (wchar_t *) IDS_4114 : (wchar_t *) IDS_4115, (existing & 1) ? (wchar_t *) IDS_4107 : (wchar_t *) IDS_4108); + return TRUE; + } else if (vhd_error == MVHD_ERR_TIMESTAMP) { + wchar_t* ts_warning = + L"WARNING: VHD PARENT/CHILD TIMESTAMPS DO NOT MATCH!\n\n" + "This could indicate that the parent image was modified after this VHD was created.\n\n" + "This could also happen if the VHD files were moved/copied, or the differencing VHD was created with DiskPart.\n\n" + "Do you wish to fix this error after a file copy or DiskPart creation?"; + if (settings_msgbox_ex(MBX_QUESTION_YN, L"VHD Timestamp Mismatch", ts_warning, NULL, NULL, NULL) != 0) { + int ts_res = mvhd_diff_update_par_timestamp(vhd, &vhd_error); + if (ts_res != 0) + { + settings_msgbox_header(MBX_ERROR, L"Error", L"Could not fix VHD timestamp."); + mvhd_close(vhd); + return TRUE; + } + } else { + mvhd_close(vhd); + return TRUE; + } + } + + MVHDGeom vhd_geom = mvhd_get_geometry(vhd); + adjust_vhd_geometry_for_86box(&vhd_geom); + tracks = vhd_geom.cyl; + hpc = vhd_geom.heads; + spt = vhd_geom.spt; + size = (uint64_t)tracks * hpc * spt * 512; + mvhd_close(vhd); } else { fseeko64(f, 0, SEEK_END); size = ftello64(f); @@ -2951,6 +3229,51 @@ hdd_add_file_open_error: no_update = 0; break; + case IDC_COMBO_HD_IMG_FORMAT: + img_format = settings_get_cur_sel(hdlg, IDC_COMBO_HD_IMG_FORMAT); + + no_update = 1; + if (img_format == 5) { /* They switched to a diff VHD; disable the geometry fields. */ + settings_enable_window(hdlg, IDC_EDIT_HD_SPT, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_SPT, L"(N/A)"); + settings_enable_window(hdlg, IDC_EDIT_HD_HPC, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_HPC, L"(N/A)"); + settings_enable_window(hdlg, IDC_EDIT_HD_CYL, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_CYL, L"(N/A)"); + settings_enable_window(hdlg, IDC_EDIT_HD_SIZE, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_SIZE, L"(N/A)"); + settings_enable_window(hdlg, IDC_COMBO_HD_TYPE, FALSE); + settings_reset_content(hdlg, IDC_COMBO_HD_TYPE); + settings_add_string(hdlg, IDC_COMBO_HD_TYPE, (LPARAM) L"(use parent)"); + settings_set_cur_sel(hdlg, IDC_COMBO_HD_TYPE, 0); + } else { + get_edit_box_text_contents(hdlg, IDC_EDIT_HD_SPT, text_buf, 256); + if (!wcscmp(text_buf, L"(N/A)")) { + settings_enable_window(hdlg, IDC_EDIT_HD_SPT, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_SPT, 17); + settings_enable_window(hdlg, IDC_EDIT_HD_HPC, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_HPC, 15); + settings_enable_window(hdlg, IDC_EDIT_HD_CYL, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_CYL, 1023); + settings_enable_window(hdlg, IDC_EDIT_HD_SIZE, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_SIZE, (uint32_t) ((uint64_t)17 * 15 * 1023 * 512 >> 20)); + settings_enable_window(hdlg, IDC_COMBO_HD_TYPE, TRUE); + hdconf_initialize_hdt_combo(hdlg); + } + } + no_update = 0; + + if (img_format == 4 || img_format == 5) /* For dynamic and diff VHDs, show the block size dropdown. */ + { + settings_show_window(hdlg, IDC_COMBO_HD_BLOCK_SIZE, TRUE); + settings_show_window(hdlg, IDT_1775, TRUE); + } + else /* Hide it otherwise. */ + { + settings_show_window(hdlg, IDC_COMBO_HD_BLOCK_SIZE, FALSE); + settings_show_window(hdlg, IDT_1775, FALSE); + } + break; } return FALSE;