diff --git a/README.md b/README.md index 10d801f12..46a7f08bb 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,22 @@ Features * MIDI output to Windows built-in MIDI support, FluidSynth, or emulated Roland synthesizers * Supports running MS-DOS, older Windows versions, OS/2, many Linux distributions, or vintage systems such as BeOS or NEXTSTEP, and applications for these systems -System requirements and recommendations ---------------------------------------- +Minimum system requirements and recommendations +----------------------------------------------- * Intel Core 2 or AMD Athlon 64 processor * Windows version: Windows 7 Service Pack 1, Windows 8.1 or Windows 10 * Linux version: Ubuntu 16.04, Debian 9.0 or other distributions from 2016 onwards +* macOS version: macOS High Sierra 10.13 * 4 GB of RAM -Performance may vary depending on both host and guest configuration. Most emulation logic is executed in a single thread, therefore generally systems with better IPC (instructions per clock) should be able to emulate higher clock speeds. +Performance may vary depending on both host and guest configuration. Most emulation logic is executed in a single thread; therefore, systems with better IPC (instructions per clock) generally should be able to emulate higher clock speeds. It is also recommended to use a manager application with 86Box for easier handling of multiple virtual machines. * [86Box Manager](https://github.com/86Box/86BoxManager) by [Overdoze](https://github.com/daviunic) (Windows only) * [86Box Manager Lite](https://github.com/insanemal/86box_manager_py) by [Insanemal](https://github.com/insanemal) * [WinBox for 86Box](https://github.com/86Box/WinBox-for-86Box) by Laci bá' (Windows only) -However, it is also possible to use 86Box on its own with the `--vmpath`/`-P` command line option. +It is also possible to use 86Box on its own with the `--vmpath`/`-P` command line option. Getting started --------------- diff --git a/src/cdrom/CMakeLists.txt b/src/cdrom/CMakeLists.txt index ecd0d934e..347a0e19d 100644 --- a/src/cdrom/CMakeLists.txt +++ b/src/cdrom/CMakeLists.txt @@ -13,4 +13,4 @@ # Copyright 2020,2021 David Hrdlička. # -add_library(cdrom OBJECT cdrom.c cdrom_image_backend.c cdrom_image.c) +add_library(cdrom OBJECT cdrom.c cdrom_image_backend.c cdrom_image_viso.c cdrom_image.c) diff --git a/src/cdrom/cdrom_image.c b/src/cdrom/cdrom_image.c index a327adad2..11a53aacc 100644 --- a/src/cdrom/cdrom_image.c +++ b/src/cdrom/cdrom_image.c @@ -268,14 +268,16 @@ cdrom_image_open(cdrom_t *dev, const char *fn) dev->image = img; /* Open the image. */ - if (!cdi_set_device(img, fn)) + int i = cdi_set_device(img, fn); + if (!i) return image_open_abort(dev); /* All good, reset state. */ - if (!strcasecmp(path_get_extension((char *) fn), "ISO")) + if (i >= 2) dev->cd_status = CD_STATUS_DATA_ONLY; else dev->cd_status = CD_STATUS_STOPPED; + dev->is_dir = (i == 3); dev->seek_pos = 0; dev->cd_buflen = 0; dev->cdrom_capacity = image_get_capacity(dev); diff --git a/src/cdrom/cdrom_image_backend.c b/src/cdrom/cdrom_image_backend.c index 0fe12869c..19968fe2b 100644 --- a/src/cdrom/cdrom_image_backend.c +++ b/src/cdrom/cdrom_image_backend.c @@ -1,36 +1,37 @@ /* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. * - * This file is part of the 86Box distribution. + * This file is part of the 86Box distribution. * - * CD-ROM image file handling module, translated to C from - * cdrom_dosbox.cpp. + * CD-ROM image file handling module, translated to C from + * cdrom_dosbox.cpp. * - * Authors: Miran Grca, - * Fred N. van Kempen, - * The DOSBox Team, + * Authors: Miran Grca, + * Fred N. van Kempen, + * The DOSBox Team, * - * Copyright 2016-2020 Miran Grca. - * Copyright 2017-2020 Fred N. van Kempen. - * Copyright 2002-2020 The DOSBox Team. + * Copyright 2016-2020 Miran Grca. + * Copyright 2017-2020 Fred N. van Kempen. + * Copyright 2002-2020 The DOSBox Team. */ #define __STDC_FORMAT_MACROS -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #ifdef _WIN32 # include +# include #else # include #endif -#include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/path.h> @@ -132,6 +133,7 @@ static track_file_t * bin_init(const char *filename, int *error) { track_file_t *tf = (track_file_t *) malloc(sizeof(track_file_t)); + struct stat stats; if (tf == NULL) { *error = 1; @@ -143,7 +145,11 @@ bin_init(const char *filename, int *error) tf->file = plat_fopen64(tf->fn, "rb"); cdrom_image_backend_log("CDROM: binary_open(%s) = %08lx\n", tf->fn, tf->file); - *error = (tf->file == NULL); + if (stat(tf->fn, &stats) != 0) { + /* Use a blank structure if stat failed. */ + memset(&stats, 0, sizeof(struct stat)); + } + *error = ((tf->file == NULL) || ((stats.st_mode & S_IFMT) == S_IFDIR)); /* Set the function pointers. */ if (!*error) { @@ -222,11 +228,13 @@ cdi_close(cd_img_t *cdi) int cdi_set_device(cd_img_t *cdi, const char *path) { - if (cdi_load_cue(cdi, path)) - return 1; + int ret; - if (cdi_load_iso(cdi, path)) - return 1; + if ((ret = cdi_load_cue(cdi, path))) + return ret; + + if ((ret = cdi_load_iso(cdi, path))) + return ret; return 0; } @@ -532,7 +540,7 @@ cdi_track_push_back(cd_img_t *cdi, track_t *trk) int cdi_load_iso(cd_img_t *cdi, const char *filename) { - int error; + int error, ret = 2; track_t trk; cdi->tracks = NULL; @@ -545,7 +553,13 @@ cdi_load_iso(cd_img_t *cdi, const char *filename) if (error) { if ((trk.file != NULL) && (trk.file->close != NULL)) trk.file->close(trk.file); - return 0; + ret = 3; + trk.file = viso_init(filename, &error); + if (error) { + if ((trk.file != NULL) && (trk.file->close != NULL)) + trk.file->close(trk.file); + return 0; + } } trk.number = 1; trk.track_number = 1; @@ -585,7 +599,7 @@ cdi_load_iso(cd_img_t *cdi, const char *filename) trk.file = NULL; cdi_track_push_back(cdi, &trk); - return 1; + return ret; } static int diff --git a/src/cdrom/cdrom_image_viso.c b/src/cdrom/cdrom_image_viso.c new file mode 100644 index 000000000..7d40deb26 --- /dev/null +++ b/src/cdrom/cdrom_image_viso.c @@ -0,0 +1,1489 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Virtual ISO CD-ROM image back-end. + * + * Authors: RichardG + * + * Copyright 2022 RichardG. + */ +// clang-format off +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/bswap.h> +#include <86box/cdrom_image_backend.h> +#include <86box/path.h> +#include <86box/plat.h> +#include <86box/plat_dir.h> +#include <86box/version.h> +#include <86box/timer.h> +#include <86box/nvr.h> +// clang-format on + +#ifndef S_ISDIR +# define S_ISDIR(m) (((m) &S_IFMT) == S_IFDIR) +#endif + +#define VISO_SKIP(p, n) \ + { \ + memset(p, 0x00, n); \ + p += n; \ + } +#define VISO_TIME_VALID(t) ((t) > 0) + +/* ISO 9660 defines "both endian" data formats, which + are stored as little endian followed by big endian. */ +#define VISO_LBE_16(p, x) \ + { \ + *((uint16_t *) p) = cpu_to_le16(x); \ + p += 2; \ + *((uint16_t *) p) = cpu_to_be16(x); \ + p += 2; \ + } +#define VISO_LBE_32(p, x) \ + { \ + *((uint32_t *) p) = cpu_to_le32(x); \ + p += 4; \ + *((uint32_t *) p) = cpu_to_be32(x); \ + p += 4; \ + } + +#define VISO_SECTOR_SIZE COOKED_SECTOR_SIZE +#define VISO_OPEN_FILES 32 + +enum { + VISO_CHARSET_D = 0, + VISO_CHARSET_A, + VISO_CHARSET_FN, + VISO_CHARSET_ANY +}; + +enum { + VISO_DIR_CURRENT = 0, + VISO_DIR_CURRENT_ROOT, + VISO_DIR_PARENT, + VISO_DIR_REGULAR, + VISO_DIR_JOLIET +}; + +enum { + VISO_FORMAT_HSF = 0, /* High Sierra */ + VISO_FORMAT_ISO, /* ISO 9660 */ + VISO_FORMAT_ISO_LFN /* ISO 9660 with Joliet and Rock Ridge */ +}; + +typedef struct _viso_entry_ { + union { /* save some memory */ + uint64_t pt_offsets[4]; + FILE *file; + }; + union { + char name_short[13]; + uint64_t dr_offsets[2]; + uint64_t data_offset; + }; + uint16_t pt_idx; + + struct stat stats; + + struct _viso_entry_ *parent, *next, *next_dir, *first_child; + + char *basename, path[]; +} viso_entry_t; + +typedef struct { + uint64_t vol_size_offsets[2], pt_meta_offsets[2]; + int format; + size_t metadata_sectors, all_sectors, entry_map_size, sector_size, file_fifo_pos; + uint8_t *metadata; + + track_file_t tf; + viso_entry_t *root_dir, **entry_map, *file_fifo[VISO_OPEN_FILES]; +} viso_t; + +static const char rr_eid[] = "RRIP_1991A"; /* identifiers used in ER field for Rock Ridge */ +static const char rr_edesc[] = "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."; +static int8_t tz_offset = 0; + +#ifdef ENABLE_CDROM_IMAGE_VISO_LOG +int cdrom_image_viso_do_log = ENABLE_CDROM_IMAGE_VISO_LOG; + +void +cdrom_image_viso_log(const char *fmt, ...) +{ + va_list ap; + + if (cdrom_image_viso_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define cdrom_image_viso_log(fmt, ...) +#endif + +static size_t +viso_pread(void *ptr, uint64_t offset, size_t size, size_t count, FILE *stream) +{ + uint64_t cur_pos = ftello64(stream); + size_t ret = 0; + if (fseeko64(stream, offset, SEEK_SET) != -1) + ret = fread(ptr, size, count, stream); + fseeko64(stream, cur_pos, SEEK_SET); + return ret; +} + +static size_t +viso_pwrite(const void *ptr, uint64_t offset, size_t size, size_t count, FILE *stream) +{ + uint64_t cur_pos = ftello64(stream); + size_t ret = 0; + if (fseeko64(stream, offset, SEEK_SET) != -1) + ret = fwrite(ptr, size, count, stream); + fseeko64(stream, cur_pos, SEEK_SET); + return ret; +} + +static size_t +viso_convert_utf8(wchar_t *dest, const char *src, ssize_t buf_size) +{ + uint32_t c; + wchar_t *p = dest; + size_t next; + + while (buf_size-- > 0) { + /* Interpret source codepoint. */ + c = *src; + if (!c) { + /* Terminator. */ + *p = 0; + break; + } else if (c & 0x80) { + /* Convert UTF-8 sequence into a codepoint. */ + next = 0; + while (c & 0x40) { + next++; + c <<= 1; + } + c = *src++ & (0x3f >> next); + while ((next-- > 0) && ((*src & 0xc0) == 0x80)) + c = (c << 6) | (*src++ & 0x3f); + + /* Convert codepoints >= U+10000 to UTF-16 surrogate pairs. + This has to be done here because wchar_t on some platforms + (Windows) is not wide enough to store such high codepoints. */ + if (c >= 0x10000) { + if ((c <= 0x10ffff) && (buf_size-- > 0)) { + /* Encode surrogate pair. */ + c -= 0x10000; + *p++ = 0xd800 | (c >> 10); + c = 0xdc00 | (c & 0x3ff); + } else { + /* Codepoint overflow or no room for a pair. */ + c = '?'; + } + } + } else { + /* Pass through sub-UTF-8 codepoints. */ + src++; + } + + /* Write destination codepoint. */ + *p++ = c; + } + + return p - dest; +} + +#define VISO_WRITE_STR_FUNC(func, dst_type, src_type, converter) \ + static void \ + func(dst_type *dest, const src_type *src, ssize_t buf_size, int charset) \ + { \ + src_type c; \ + while (buf_size-- > 0) { \ + /* Interpret source codepoint. */ \ + c = *src++; \ + switch (c) { \ + case 0x00: \ + /* Terminator, apply space padding. */ \ + while (buf_size-- >= 0) \ + *dest++ = converter(' '); \ + return; \ + \ + case 'A' ... 'Z': \ + case '0' ... '9': \ + case '_': \ + /* Valid on all sets. */ \ + break; \ + \ + case 'a' ... 'z': \ + /* Convert to uppercase on D and A. */ \ + if (charset <= VISO_CHARSET_A) \ + c -= 'a' - 'A'; \ + break; \ + \ + case ' ': \ + case '!': \ + case '"': \ + case '%': \ + case '&': \ + case '\'': \ + case '(': \ + case ')': \ + case '+': \ + case ',': \ + case '-': \ + case '.': \ + case '<': \ + case '=': \ + case '>': \ + /* Valid for A and filenames but not for D. */ \ + if (charset < VISO_CHARSET_A) \ + c = '_'; \ + break; \ + \ + case '*': \ + case '/': \ + case ':': \ + case ';': \ + case '?': \ + /* Valid for A but not for filenames or D. */ \ + if ((charset < VISO_CHARSET_A) || (charset == VISO_CHARSET_FN)) \ + c = '_'; \ + break; \ + \ + case 0x01 ... 0x1f: \ + case '\\': \ + /* Not valid for D, A or filenames. */ \ + if (charset <= VISO_CHARSET_FN) \ + c = '_'; \ + break; \ + \ + default: \ + /* Not valid for D or A, but valid for filenames. */ \ + if ((charset < VISO_CHARSET_FN) || (c > 0xffff)) \ + c = '_'; \ + break; \ + } \ + \ + /* Write destination codepoint with conversion function applied. */ \ + *dest++ = converter(c); \ + } \ + } +VISO_WRITE_STR_FUNC(viso_write_string, uint8_t, char, ) +VISO_WRITE_STR_FUNC(viso_write_wstring, uint16_t, wchar_t, cpu_to_be16) + +static int +viso_fill_fn_short(char *data, const viso_entry_t *entry, viso_entry_t **entries) +{ + /* Get name and extension length. */ + const char *ext_pos = S_ISDIR(entry->stats.st_mode) ? NULL : strrchr(entry->basename, '.'); + int name_len, ext_len; + if (ext_pos) { + name_len = ext_pos - entry->basename; + ext_len = strlen(ext_pos); + } else { + name_len = strlen(entry->basename); + ext_len = 0; + } + + /* Copy name. */ + int name_copy_len = MIN(8, name_len); + viso_write_string((uint8_t *) data, entry->basename, name_copy_len, VISO_CHARSET_D); + data[name_copy_len] = '\0'; + + /* Copy extension to temporary buffer. */ + char ext[5] = { 0 }; + int force_tail = (name_len > 8) || (ext_len == 1); + if (ext_len > 1) { + ext[0] = '.'; + if (ext_len > 4) { + ext_len = 4; + force_tail = 1; + } + viso_write_string((uint8_t *) &ext[1], &ext_pos[1], ext_len - 1, VISO_CHARSET_D); + } + + /* Check if this filename is unique, and add a tail if required, while also adding the extension. */ + char tail[8]; + for (int i = force_tail; i <= 999999; i++) { + /* Add tail to the filename if this is not the first run. */ + int tail_len = -1; + if (i) { + tail_len = sprintf(tail, "~%d", i); + strcpy(&data[MIN(name_copy_len, 8 - tail_len)], tail); + } + + /* Add extension to the filename if present. */ + if (ext[0]) + strcat(data, ext); + + /* Go through files in this directory to make sure this filename is unique. */ + for (size_t j = 0; entries[j] != entry; j++) { + /* Flag and stop if this filename was seen. */ + if (!strcmp(data, entries[j]->name_short)) { + tail_len = 0; + break; + } + } + + /* Stop if this is an unique name. */ + if (tail_len) + return 0; + } + return 1; +} + +static size_t +viso_fill_fn_rr(uint8_t *data, const viso_entry_t *entry, size_t max_len) +{ + /* Trim filename to max_len if needed. */ + size_t len = strlen(entry->basename); + if (len > max_len) { + viso_write_string(data, entry->basename, max_len, VISO_CHARSET_FN); + + /* Relocate extension if the original name exceeds the maximum length. */ + if (!S_ISDIR(entry->stats.st_mode)) { /* do this on files only */ + char *ext = strrchr(entry->basename, '.'); + if (ext > entry->basename) { + len = strlen(ext); + if (len >= max_len) + len = max_len - 1; /* don't create a dotfile where there isn't one */ + viso_write_string(data + (max_len - len), ext, len, VISO_CHARSET_FN); + } + } + + return max_len; + } else { + viso_write_string(data, entry->basename, len, VISO_CHARSET_FN); + return len; + } +} + +static size_t +viso_fill_fn_joliet(uint8_t *data, const viso_entry_t *entry, size_t max_len) /* note: receives and returns byte sizes */ +{ + /* Decode filename as UTF-8. */ + size_t len = strlen(entry->basename); + wchar_t utf8dec[len + 1]; + len = viso_convert_utf8(utf8dec, entry->basename, len + 1); + + /* Trim decoded filename to max_len if needed. */ + max_len /= 2; + if (len > max_len) { + viso_write_wstring((uint16_t *) data, utf8dec, max_len, VISO_CHARSET_FN); + + /* Relocate extension if the original name exceeds the maximum length. */ + if (!S_ISDIR(entry->stats.st_mode)) { /* do this on files only */ + wchar_t *ext = wcsrchr(utf8dec, L'.'); + if (ext > utf8dec) { + len = wcslen(ext); + if (len > max_len) + len = max_len; + else if ((len < max_len) && ((((uint16_t *) data)[max_len - len] & be16_to_cpu(0xfc00)) == be16_to_cpu(0xdc00))) + max_len--; /* don't break an UTF-16 pair */ + viso_write_wstring(((uint16_t *) data) + (max_len - len), ext, len, VISO_CHARSET_FN); + } + } + + return max_len * 2; + } else { + viso_write_wstring((uint16_t *) data, utf8dec, len, VISO_CHARSET_FN); + return len * 2; + } +} + +static int +viso_fill_time(uint8_t *data, time_t time, int format, int longform) +{ + uint8_t *p = data; + struct tm *time_s = localtime(&time); + if (!time_s) + fatal("VISO: localtime(%d) = NULL\n", time); + + if (longform) { + p += sprintf((char *) p, "%04u%02u%02u%02u%02u%02u00", + 1900 + time_s->tm_year, 1 + time_s->tm_mon, time_s->tm_mday, + time_s->tm_hour, time_s->tm_min, time_s->tm_sec); + } else { + *p++ = time_s->tm_year; /* year since 1900 */ + *p++ = 1 + time_s->tm_mon; /* month */ + *p++ = time_s->tm_mday; /* day */ + *p++ = time_s->tm_hour; /* hour */ + *p++ = time_s->tm_min; /* minute */ + *p++ = time_s->tm_sec; /* second */ + } + if (format >= VISO_FORMAT_ISO) + *p++ = tz_offset; /* timezone (ISO only) */ + + return p - data; +} + +static int +viso_fill_dir_record(uint8_t *data, viso_entry_t *entry, int format, int type) +{ + uint8_t *p = data, *q, *r; + + *p++ = 0; /* size (filled in later) */ + *p++ = 0; /* extended attribute length */ + VISO_SKIP(p, 8); /* sector offset */ + VISO_LBE_32(p, entry->stats.st_size); /* size (filled in later if this is a directory) */ + p += viso_fill_time(p, entry->stats.st_mtime, format, 0); /* time */ + *p++ = S_ISDIR(entry->stats.st_mode) ? 0x02 : 0x00; /* flags */ + + VISO_SKIP(p, 2 + (format <= VISO_FORMAT_HSF)); /* file unit size (reserved on HSF), interleave gap size (HSF/ISO) and skip factor (HSF only) */ + VISO_LBE_16(p, 1); /* volume sequence number */ + + switch (type) { + case VISO_DIR_CURRENT: + case VISO_DIR_CURRENT_ROOT: + case VISO_DIR_PARENT: + *p++ = 1; /* file ID length */ + *p++ = (type == VISO_DIR_PARENT) ? 1 : 0; /* magic value corresponding to . or .. */ + + /* Fill Rock Ridge Extension Record for the root directory's . entry. */ + if ((type == VISO_DIR_CURRENT_ROOT) && (format >= VISO_FORMAT_ISO_LFN)) { + *p++ = 'E'; + *p++ = 'R'; + *p++ = 8 + (sizeof(rr_eid) - 1) + (sizeof(rr_edesc) - 1); /* length */ + *p++ = 1; /* version */ + + *p++ = sizeof(rr_eid) - 1; /* ID length */ + *p++ = sizeof(rr_edesc) - 1; /* description length */ + *p++ = 0; /* source length (source is recommended but won't fit here) */ + *p++ = 1; /* extension version */ + + memcpy(p, rr_eid, sizeof(rr_eid) - 1); /* ID */ + p += sizeof(rr_eid) - 1; + memcpy(p, rr_edesc, sizeof(rr_edesc) - 1); /* description */ + p += sizeof(rr_edesc) - 1; + + goto pad_susp; + } + break; + + case VISO_DIR_REGULAR: + q = p++; /* save file ID length location for later */ + + *q = strlen(entry->name_short); + memcpy(p, entry->name_short, *q); /* file ID */ + p += *q; + if ((format >= VISO_FORMAT_ISO) && !S_ISDIR(entry->stats.st_mode)) { + *p++ = ';'; /* version suffix for files (ISO only?) */ + *p++ = '1'; + *q += 2; + } + + if (!(*q & 1)) /* padding for even file ID lengths */ + *p++ = 0; + + /* Fill Rock Ridge data. */ + if (format >= VISO_FORMAT_ISO_LFN) { + *p++ = 'R'; /* RR = present Rock Ridge entries (only documented by RRIP revision 1.09!) */ + *p++ = 'R'; + *p++ = 5; /* length */ + *p++ = 1; /* version */ + + q = p++; /* save Rock Ridge flags location for later */ + +#ifndef _WIN32 /* attributes reported by MinGW don't really make sense because it's Windows */ + *q |= 0x01; /* PX = POSIX attributes */ + *p++ = 'P'; + *p++ = 'X'; + *p++ = 36; /* length */ + *p++ = 1; /* version */ + + VISO_LBE_32(p, entry->stats.st_mode); /* mode */ + VISO_LBE_32(p, entry->stats.st_nlink); /* number of links */ + VISO_LBE_32(p, entry->stats.st_uid); /* owner UID */ + VISO_LBE_32(p, entry->stats.st_gid); /* owner GID */ + +# ifndef S_ISCHR +# define S_ISCHR(x) 0 +# endif +# ifndef S_ISBLK +# define S_ISBLK(x) 0 +# endif + if (S_ISCHR(entry->stats.st_mode) || S_ISBLK(entry->stats.st_mode)) { + *q |= 0x02; /* PN = POSIX device */ + *p++ = 'P'; + *p++ = 'N'; + *p++ = 20; /* length */ + *p++ = 1; /* version */ + + uint64_t dev = entry->stats.st_rdev; /* avoid warning if <= 32 bits */ + VISO_LBE_32(p, dev >> 32); /* device number (high 32 bits) */ + VISO_LBE_32(p, dev); /* device number (low 32 bits) */ + } +#endif + int times = +#ifdef st_birthtime + (VISO_TIME_VALID(entry->stats.st_birthtime) << 0) | /* creation (hack: assume the platform remaps st_birthtime at header level) */ +#endif + (VISO_TIME_VALID(entry->stats.st_mtime) << 1) | /* modify */ + (VISO_TIME_VALID(entry->stats.st_atime) << 2) | /* access */ + (VISO_TIME_VALID(entry->stats.st_ctime) << 3); /* attributes */ + if (times) { + *q |= 0x80; /* TF = timestamps */ + *p++ = 'T'; + *p++ = 'F'; + r = p; /* save length location for later */ + *p++ = 2; /* length (added to later) */ + *p++ = 1; /* version */ + + *p++ = times; /* flags */ +#ifdef st_birthtime + if (times & (1 << 0)) + p += viso_fill_time(p, entry->stats.st_birthtime, format, 0); /* creation */ +#endif + if (times & (1 << 1)) + p += viso_fill_time(p, entry->stats.st_mtime, format, 0); /* modify */ + if (times & (1 << 2)) + p += viso_fill_time(p, entry->stats.st_atime, format, 0); /* access */ + if (times & (1 << 3)) + p += viso_fill_time(p, entry->stats.st_ctime, format, 0); /* attributes */ + + *r += p - r; /* add to length */ + } + + *q |= 0x08; /* NM = alternate name */ + *p++ = 'N'; + *p++ = 'M'; + r = p; /* save length location for later */ + *p++ = 2; /* length (added to later) */ + *p++ = 1; /* version */ + + *p++ = 0; /* flags */ + p += viso_fill_fn_rr(p, entry, 254 - (p - data)); /* name */ + + *r += p - r; /* add to length */ +pad_susp: + if ((p - data) & 1) /* padding for odd SUSP section lengths */ + *p++ = 0; + } + break; + + case VISO_DIR_JOLIET: + q = p++; /* save file ID length location for later */ + + *q = viso_fill_fn_joliet(p, entry, 254 - (p - data)); + p += *q; + + if (!(*q & 1)) /* padding for even file ID lengths */ + *p++ = 0; + break; + } + + if ((p - data) > 255) + fatal("VISO: Directory record overflow (%d) on entry %08X\n", p - data, entry); + + data[0] = p - data; /* length */ + return data[0]; +} + +static int +viso_compare_entries(const void *a, const void *b) +{ + return strcmp((*((viso_entry_t **) a))->name_short, (*((viso_entry_t **) b))->name_short); +} + +int +viso_read(void *p, uint8_t *buffer, uint64_t seek, size_t count) +{ + track_file_t *tf = (track_file_t *) p; + viso_t *viso = (viso_t *) tf->priv; + + /* Handle reads in a sector by sector basis. */ + while (count > 0) { + /* Determine the current sector, offset and remainder. */ + uint32_t sector = seek / viso->sector_size, + sector_offset = seek % viso->sector_size, + sector_remain = MIN(count, viso->sector_size - sector_offset); + + /* Handle sector. */ + if (sector < viso->metadata_sectors) { + /* Copy metadata. */ + memcpy(buffer, viso->metadata + seek, sector_remain); + } else { + size_t read = 0; + + /* Get the file entry corresponding to this sector. */ + viso_entry_t *entry = viso->entry_map[sector - viso->metadata_sectors]; + if (entry) { + /* Open file if it's not already open. */ + if (!entry->file) { + /* Close any existing FIFO entry's file. */ + viso_entry_t *other_entry = viso->file_fifo[viso->file_fifo_pos]; + if (other_entry && other_entry->file) { + cdrom_image_viso_log("VISO: Closing [%s]", other_entry->path); + fclose(other_entry->file); + other_entry->file = NULL; + cdrom_image_viso_log("\n"); + } + + /* Open file. */ + cdrom_image_viso_log("VISO: Opening [%s]", entry->path); + if ((entry->file = fopen(entry->path, "rb"))) { + cdrom_image_viso_log("\n"); + + /* Add this entry to the FIFO. */ + viso->file_fifo[viso->file_fifo_pos++] = entry; + viso->file_fifo_pos &= (sizeof(viso->file_fifo) / sizeof(viso->file_fifo[0])) - 1; + } else { + cdrom_image_viso_log(" => failed\n"); + + /* Clear any existing FIFO entry. */ + viso->file_fifo[viso->file_fifo_pos] = NULL; + } + } + + /* Read data. */ + if (entry->file && (fseeko64(entry->file, seek - entry->data_offset, SEEK_SET) != -1)) + read = fread(buffer, 1, sector_remain, entry->file); + } + + /* Fill remainder with 00 bytes if needed. */ + if (read < sector_remain) + memset(buffer + read, 0x00, sector_remain - read); + } + + /* Move on to the next sector. */ + buffer += sector_remain; + seek += sector_remain; + count -= sector_remain; + } + + return 1; +} + +uint64_t +viso_get_length(void *p) +{ + track_file_t *tf = (track_file_t *) p; + viso_t *viso = (viso_t *) tf->priv; + return ((uint64_t) viso->all_sectors) * viso->sector_size; +} + +void +viso_close(void *p) +{ + track_file_t *tf = (track_file_t *) p; + viso_t *viso = (viso_t *) tf->priv; + + if (viso == NULL) + return; + + cdrom_image_viso_log("VISO: close()\n"); + + /* De-allocate everything. */ + if (tf->file) + fclose(tf->file); +#ifndef ENABLE_CDROM_IMAGE_VISO_LOG + remove(nvr_path(viso->tf.fn)); +#endif + + viso_entry_t *entry = viso->root_dir, *next_entry; + while (entry) { + if (entry->file) + fclose(entry->file); + next_entry = entry->next; + free(entry); + entry = next_entry; + } + + if (viso->metadata) + free(viso->metadata); + if (viso->entry_map) + free(viso->entry_map); + + free(viso); +} + +track_file_t * +viso_init(const char *dirname, int *error) +{ + cdrom_image_viso_log("VISO: init()\n"); + + /* Initialize our data structure. */ + viso_t *viso = (viso_t *) calloc(1, sizeof(viso_t)); + uint8_t *data = NULL, *p; + *error = 1; + if (viso == NULL) + goto end; + viso->sector_size = VISO_SECTOR_SIZE; + viso->format = VISO_FORMAT_ISO_LFN; + + /* Prepare temporary data buffers. */ + data = calloc(2, viso->sector_size); + if (!data) + goto end; + + /* Open temporary file. */ +#ifdef ENABLE_CDROM_IMAGE_VISO_LOG + strcpy(viso->tf.fn, "viso-debug.iso"); +#else + plat_tempfile(viso->tf.fn, "viso", ".tmp"); +#endif + viso->tf.file = plat_fopen64(nvr_path(viso->tf.fn), "w+b"); + if (!viso->tf.file) + goto end; + + /* Set up directory traversal. */ + cdrom_image_viso_log("VISO: Traversing directories:\n"); + viso_entry_t *entry, *last_entry, *dir, *last_dir, *eltorito_dir = NULL, *eltorito_entry = NULL; + struct dirent *readdir_entry; + int len, eltorito_others_present = 0; + size_t dir_path_len; + uint64_t eltorito_offset = 0; + uint8_t eltorito_type = 0; + + /* Fill root directory entry. */ + dir_path_len = strlen(dirname); + last_entry = dir = last_dir = viso->root_dir = (viso_entry_t *) calloc(1, sizeof(viso_entry_t) + dir_path_len + 1); + if (!dir) + goto end; + strcpy(dir->path, dirname); + strcpy(dir->name_short, "[root]"); + if (stat(dirname, &dir->stats) != 0) { + /* Use a blank structure if stat failed. */ + memset(&dir->stats, 0x00, sizeof(struct stat)); + } + if (!S_ISDIR(dir->stats.st_mode)) /* root is not a directory */ + goto end; + dir->parent = dir; /* for the root's path table and .. entries */ + cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, dir->name_short); + + /* Traverse directories, starting with the root. */ + viso_entry_t **dir_entries = NULL; + size_t dir_entries_len = 0; + while (dir) { + /* Open directory for listing. */ + DIR *dirp = opendir(dir->path); + if (!dirp) + goto next_dir; + + /* Iterate through this directory's children to determine the entry array size. */ + size_t children_count = 3; /* include terminator, . and .. */ + while ((readdir_entry = readdir(dirp))) { + /* Ignore . and .. pseudo-directories. */ + if ((readdir_entry->d_name[0] == '.') && ((readdir_entry->d_name[1] == '\0') || ((readdir_entry->d_name[1] == '.') && (readdir_entry->d_name[2] == '\0')))) + continue; + children_count++; + } + + /* Grow array if needed. */ + if (children_count > dir_entries_len) { + viso_entry_t **new_dir_entries = (viso_entry_t **) realloc(dir_entries, children_count * sizeof(viso_entry_t *)); + if (new_dir_entries) { + dir_entries = new_dir_entries; + dir_entries_len = children_count; + } else { + goto next_dir; + } + } + + /* Add . and .. pseudo-directories. */ + dir_path_len = strlen(dir->path); + for (children_count = 0; children_count < 2; children_count++) { + entry = dir_entries[children_count] = (viso_entry_t *) calloc(1, sizeof(viso_entry_t) + 1); + if (!entry) + goto next_dir; + entry->parent = dir; + if (!children_count) + dir->first_child = entry; + + /* Stat the current directory or parent directory. */ + if (stat(children_count ? dir->parent->path : dir->path, &entry->stats) != 0) { + /* Use a blank structure if stat failed. */ + memset(&entry->stats, 0x00, sizeof(struct stat)); + } + + /* Set basename. */ + strcpy(entry->name_short, children_count ? ".." : "."); + + cdrom_image_viso_log("[%08X] %s => %s\n", entry, dir->path, entry->name_short); + } + + /* Iterate through this directory's children again, making the entries. */ + rewinddir(dirp); + while ((readdir_entry = readdir(dirp))) { + /* Ignore . and .. pseudo-directories. */ + if ((readdir_entry->d_name[0] == '.') && ((readdir_entry->d_name[1] == '\0') || ((readdir_entry->d_name[1] == '.') && (readdir_entry->d_name[2] == '\0')))) + continue; + + /* Add and fill entry. */ + entry = dir_entries[children_count++] = (viso_entry_t *) calloc(1, sizeof(viso_entry_t) + dir_path_len + strlen(readdir_entry->d_name) + 2); + if (!entry) + break; + entry->parent = dir; + strcpy(entry->path, dir->path); + path_slash(&entry->path[dir_path_len]); + entry->basename = &entry->path[dir_path_len + 1]; + strcpy(entry->basename, readdir_entry->d_name); + + /* Stat this child. */ + if (stat(entry->path, &entry->stats) != 0) { + /* Use a blank structure if stat failed. */ + memset(&entry->stats, 0x00, sizeof(struct stat)); + } + + /* Handle file size and El Torito boot code. */ + if (!S_ISDIR(entry->stats.st_mode)) { + /* Limit to 4 GB - 1 byte. */ + if (entry->stats.st_size > ((uint32_t) -1)) + entry->stats.st_size = (uint32_t) -1; + + /* Increase entry map size. */ + viso->entry_map_size += entry->stats.st_size / viso->sector_size; + if (entry->stats.st_size % viso->sector_size) + viso->entry_map_size++; /* round up to the next sector */ + + /* Detect El Torito boot code file and set it accordingly. */ + if (dir == eltorito_dir) { + if (!stricmp(readdir_entry->d_name, "Boot-NoEmul.img")) { + eltorito_type = 0x00; +have_eltorito_entry: + if (eltorito_entry) + eltorito_others_present = 1; /* flag that the boot code directory contains other files */ + eltorito_entry = entry; + } else if (!stricmp(readdir_entry->d_name, "Boot-1.2M.img")) { + eltorito_type = 0x01; + goto have_eltorito_entry; + } else if (!stricmp(readdir_entry->d_name, "Boot-1.44M.img")) { + eltorito_type = 0x02; + goto have_eltorito_entry; + } else if (!stricmp(readdir_entry->d_name, "Boot-2.88M.img")) { + eltorito_type = 0x03; + goto have_eltorito_entry; + } else if (!stricmp(readdir_entry->d_name, "Boot-HardDisk.img")) { + eltorito_type = 0x04; + goto have_eltorito_entry; + } else { + eltorito_others_present = 1; /* flag that the boot code directory contains other files */ + } + } + } else if ((dir == viso->root_dir) && !stricmp(readdir_entry->d_name, "[BOOT]")) { + /* Set this as the directory containing El Torito boot code. */ + eltorito_dir = entry; + eltorito_others_present = 0; + } + + /* Set short filename. */ + if (viso_fill_fn_short(entry->name_short, entry, dir_entries)) { + free(entry); + children_count--; + continue; + } + + cdrom_image_viso_log("[%08X] %s => [%-12s] %s\n", entry, dir->path, entry->name_short, entry->basename); + } + + /* Add terminator. */ + dir_entries[children_count] = NULL; + + /* Sort directory entries and create the linked list. */ + qsort(&dir_entries[2], children_count - 2, sizeof(viso_entry_t *), viso_compare_entries); + for (size_t i = 0; dir_entries[i]; i++) { + /* Add link. */ + last_entry->next = dir_entries[i]; + last_entry = dir_entries[i]; + + /* If this is a directory, add it to the traversal list. */ + if ((i >= 2) && S_ISDIR(dir_entries[i]->stats.st_mode)) { + last_dir->next_dir = dir_entries[i]; + last_dir = dir_entries[i]; + } + } + +next_dir: + /* Move on to the next directory. */ + dir = dir->next_dir; + } + if (dir_entries) + free(dir_entries); + + /* Write 16 blank sectors. */ + for (int i = 0; i < 16; i++) + fwrite(data, viso->sector_size, 1, viso->tf.file); + + /* Get current time for the volume descriptors, and calculate + the timezone offset for descriptors and file times to use. */ + tzset(); + time_t now = time(NULL); + tz_offset = (now - mktime(gmtime(&now))) / (3600 / 4); + + /* Get root directory basename for the volume ID. */ + char *basename = path_get_filename(viso->root_dir->path); + if (!basename || (basename[0] == '\0')) + basename = EMU_NAME; + + /* Write volume descriptors. */ + for (int i = 0; i <= (viso->format >= VISO_FORMAT_ISO_LFN); i++) { + /* Fill volume descriptor. */ + p = data; + if (viso->format <= VISO_FORMAT_HSF) + VISO_LBE_32(p, ftello64(viso->tf.file) / viso->sector_size); /* sector offset (HSF only) */ + *p++ = 1 + i; /* type */ + memcpy(p, (viso->format <= VISO_FORMAT_HSF) ? "CDROM" : "CD001", 5); /* standard ID */ + p += 5; + *p++ = 1; /* version */ + *p++ = 0; /* unused */ + + if (i) { + viso_write_wstring((uint16_t *) p, EMU_NAME_W, 16, VISO_CHARSET_A); /* system ID */ + p += 32; + wchar_t wtemp[16]; + viso_convert_utf8(wtemp, basename, 16); + viso_write_wstring((uint16_t *) p, wtemp, 16, VISO_CHARSET_D); /* volume ID */ + p += 32; + } else { + viso_write_string(p, EMU_NAME, 32, VISO_CHARSET_A); /* system ID */ + p += 32; + viso_write_string(p, basename, 32, VISO_CHARSET_D); /* volume ID */ + p += 32; + } + + VISO_SKIP(p, 8); /* unused */ + + viso->vol_size_offsets[i] = ftello64(viso->tf.file) + (p - data); + VISO_LBE_32(p, 0); /* volume space size (filled in later) */ + + if (i) { + *p++ = 0x25; /* escape sequence (indicates our Joliet names are UCS-2 Level 3) */ + *p++ = 0x2f; + *p++ = 0x45; + VISO_SKIP(p, 32 - 3); /* unused */ + } else { + VISO_SKIP(p, 32); /* unused */ + } + + VISO_LBE_16(p, 1); /* volume set size */ + VISO_LBE_16(p, 1); /* volume sequence number */ + VISO_LBE_16(p, viso->sector_size); /* logical block size */ + + /* Path table metadata is filled in later. */ + viso->pt_meta_offsets[i] = ftello64(viso->tf.file) + (p - data); + VISO_SKIP(p, 24 + (16 * (viso->format <= VISO_FORMAT_HSF))); /* PT size, LE PT offset, optional LE PT offset (three on HSF), BE PT offset, optional BE PT offset (three on HSF) */ + + viso->root_dir->dr_offsets[i] = ftello64(viso->tf.file) + (p - data); + p += viso_fill_dir_record(p, viso->root_dir, viso->format, VISO_DIR_CURRENT); /* root directory */ + + if (i) { + viso_write_wstring((uint16_t *) p, L"", 64, VISO_CHARSET_D); /* volume set ID */ + p += 128; + viso_write_wstring((uint16_t *) p, L"", 64, VISO_CHARSET_A); /* publisher ID */ + p += 128; + viso_write_wstring((uint16_t *) p, L"", 64, VISO_CHARSET_A); /* data preparer ID */ + p += 128; + viso_write_wstring((uint16_t *) p, EMU_NAME_W L" " EMU_VERSION_W L" VIRTUAL ISO", 64, VISO_CHARSET_A); /* application ID */ + p += 128; + viso_write_wstring((uint16_t *) p, L"", (viso->format <= VISO_FORMAT_HSF) ? 16 : 18, VISO_CHARSET_D); /* copyright file ID */ + p += (viso->format <= VISO_FORMAT_HSF) ? 32 : 37; + viso_write_wstring((uint16_t *) p, L"", (viso->format <= VISO_FORMAT_HSF) ? 16 : 18, VISO_CHARSET_D); /* abstract file ID */ + p += (viso->format <= VISO_FORMAT_HSF) ? 32 : 37; + if (viso->format >= VISO_FORMAT_ISO) { + viso_write_wstring((uint16_t *) p, L"", 18, VISO_CHARSET_D); /* bibliography file ID (ISO only) */ + p += 37; + } + } else { + viso_write_string(p, "", 128, VISO_CHARSET_D); /* volume set ID */ + p += 128; + viso_write_string(p, "", 128, VISO_CHARSET_A); /* publisher ID */ + p += 128; + viso_write_string(p, "", 128, VISO_CHARSET_A); /* data preparer ID */ + p += 128; + viso_write_string(p, EMU_NAME " " EMU_VERSION " VIRTUAL ISO", 128, VISO_CHARSET_A); /* application ID */ + p += 128; + viso_write_string(p, "", (viso->format <= VISO_FORMAT_HSF) ? 32 : 37, VISO_CHARSET_D); /* copyright file ID */ + p += (viso->format <= VISO_FORMAT_HSF) ? 32 : 37; + viso_write_string(p, "", (viso->format <= VISO_FORMAT_HSF) ? 32 : 37, VISO_CHARSET_D); /* abstract file ID */ + p += (viso->format <= VISO_FORMAT_HSF) ? 32 : 37; + if (viso->format >= VISO_FORMAT_ISO) { + viso_write_string(p, "", 37, VISO_CHARSET_D); /* bibliography file ID (ISO only) */ + p += 37; + } + } + + len = viso_fill_time(p, now, viso->format, 1); /* volume created */ + memcpy(p + len, p, len); /* volume modified */ + p += len * 2; + VISO_SKIP(p, len * 2); /* volume expires/effective */ + + *p++ = 1; /* file structure version */ + *p++ = 0; /* unused */ + + /* Blank the rest of the working sector. */ + memset(p, 0x00, viso->sector_size - (p - data)); + + /* Write volume descriptor. */ + fwrite(data, viso->sector_size, 1, viso->tf.file); + + /* Write El Torito boot descriptor. This is an awkward spot for + that, but the spec requires it to be the second descriptor. */ + if (!i && eltorito_entry) { + cdrom_image_viso_log("VISO: Writing El Torito boot descriptor for entry [%08X]\n", eltorito_entry); + + p = data; + if (viso->format <= VISO_FORMAT_HSF) + VISO_LBE_32(p, ftello64(viso->tf.file) / viso->sector_size); /* sector offset (HSF only) */ + *p++ = 0; /* type */ + memcpy(p, (viso->format <= VISO_FORMAT_HSF) ? "CDROM" : "CD001", 5); /* standard ID */ + p += 5; + *p++ = 1; /* version */ + + memcpy(p, "EL TORITO SPECIFICATION", 24); /* identifier */ + p += 24; + VISO_SKIP(p, 40); + + /* Save the boot catalog pointer's offset for later. */ + eltorito_offset = ftello64(viso->tf.file) + (p - data); + + /* Blank the rest of the working sector. */ + memset(p, 0x00, viso->sector_size - (p - data)); + + /* Write boot descriptor. */ + fwrite(data, viso->sector_size, 1, viso->tf.file); + } + } + + /* Fill terminator. */ + p = data; + if (viso->format <= VISO_FORMAT_HSF) + VISO_LBE_32(p, ftello64(viso->tf.file) / viso->sector_size); /* sector offset (HSF only) */ + *p++ = 0xff; /* type */ + memcpy(p, (viso->format <= VISO_FORMAT_HSF) ? "CDROM" : "CD001", 5); /* standard ID */ + p += 5; + *p++ = 1; /* version */ + + /* Blank the rest of the working sector. */ + memset(p, 0x00, viso->sector_size - (p - data)); + + /* Write terminator. */ + fwrite(data, viso->sector_size, 1, viso->tf.file); + + /* We start seeing a pattern of padding to even sectors here. + mkisofs does this, presumably for a very good reason... */ + int write = ftello64(viso->tf.file) % (viso->sector_size * 2); + if (write) { + write = (viso->sector_size * 2) - write; + memset(data, 0x00, write); + fwrite(data, write, 1, viso->tf.file); + } + + /* Handle El Torito boot catalog. */ + if (eltorito_entry) { + /* Write a pointer to this boot catalog to the boot descriptor. */ + *((uint32_t *) data) = cpu_to_le32(ftello64(viso->tf.file) / viso->sector_size); + viso_pwrite(data, eltorito_offset, 4, 1, viso->tf.file); + + /* Fill boot catalog validation entry. */ + p = data; + *p++ = 0x01; /* header ID */ + *p++ = 0x00; /* platform */ + *p++ = 0x00; /* reserved */ + *p++ = 0x00; + VISO_SKIP(p, 24); + strncpy((char *) (p - 24), EMU_NAME, 24); /* ID string */ + *p++ = 0x00; /* checksum */ + *p++ = 0x00; + *p++ = 0x55; /* key bytes */ + *p++ = 0xaa; + + /* Calculate checksum. */ + uint16_t eltorito_checksum = 0; + for (int i = 0; i < (p - data); i += 2) + eltorito_checksum -= le16_to_cpu(*((uint16_t *) &data[i])); + *((uint16_t *) &data[28]) = cpu_to_le16(eltorito_checksum); + + /* Now fill the default boot entry. */ + *p++ = 0x88; /* bootable flag */ + *p++ = eltorito_type; /* boot media type */ + *p++ = 0x00; /* load segment */ + *p++ = 0x00; + *p++ = 0x00; /* system type (is this even relevant?) */ + *p++ = 0x00; /* reserved */ + + /* Save offsets to the boot catalog entry's offset and size fields for later. */ + eltorito_offset = ftello64(viso->tf.file) + (p - data); + + /* Blank the rest of the working sector. This includes the sector count, + ISO sector offset and 20-byte selection criteria fields at the end. */ + memset(p, 0x00, viso->sector_size - (p - data)); + + /* Write boot catalog. */ + fwrite(data, viso->sector_size, 1, viso->tf.file); + + /* Pad to the next even sector. */ + write = ftello64(viso->tf.file) % (viso->sector_size * 2); + if (write) { + write = (viso->sector_size * 2) - write; + memset(data, 0x00, write); + fwrite(data, write, 1, viso->tf.file); + } + + /* Flag that we shouldn't hide the boot code directory if it contains other files. */ + if (eltorito_others_present) + eltorito_dir = NULL; + } + + /* Write each path table. */ + for (int i = 0; i <= ((viso->format >= VISO_FORMAT_ISO_LFN) ? 3 : 1); i++) { + cdrom_image_viso_log("VISO: Generating path table #%d:\n", i); + + /* Save this path table's start offset. */ + uint64_t pt_start = ftello64(viso->tf.file); + + /* Write this table's sector offset to the corresponding volume descriptor. */ + uint32_t pt_temp = pt_start / viso->sector_size; + *((uint32_t *) data) = (i & 1) ? cpu_to_be32(pt_temp) : cpu_to_le32(pt_temp); + viso_pwrite(data, viso->pt_meta_offsets[i >> 1] + 8 + (8 * (i & 1)), 4, 1, viso->tf.file); + + /* Go through directories. */ + dir = viso->root_dir; + uint16_t pt_idx = 1; + while (dir) { + /* Ignore . and .. pseudo-directories, and hide the El Torito + boot code directory if no other files are present in it. */ + if ((dir->name_short[0] == '.' && (dir->name_short[1] == '\0' || (dir->name_short[1] == '.' && dir->name_short[2] == '\0'))) || (dir == eltorito_dir)) { + dir = dir->next_dir; + continue; + } + + cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, ((i & 2) || (dir == viso->root_dir)) ? dir->basename : dir->name_short); + + /* Save this directory's path table index and offset. */ + dir->pt_idx = pt_idx; + dir->pt_offsets[i] = ftello64(viso->tf.file); + + /* Fill path table entry. */ + p = data; + if (viso->format <= VISO_FORMAT_HSF) { + *((uint32_t *) p) = 0; /* extent location (filled in later) */ + p += 4; + *p++ = 0; /* extended attribute length */ + p++; /* skip ID length for now */ + } else { + p++; /* skip ID length for now */ + *p++ = 0; /* extended attribute length */ + dir->pt_offsets[i] += p - data; + *((uint32_t *) p) = 0; /* extent location (filled in later) */ + p += 4; + } + + *((uint16_t *) p) = (i & 1) ? cpu_to_be16(dir->parent->pt_idx) : cpu_to_le16(dir->parent->pt_idx); /* parent directory number */ + p += 2; + + if (dir == viso->root_dir) { /* directory ID and length */ + data[5 * (viso->format <= VISO_FORMAT_HSF)] = 1; + *p = 0x00; + } else if (i & 2) { + data[5 * (viso->format <= VISO_FORMAT_HSF)] = viso_fill_fn_joliet(p, dir, 255); + } else { + data[5 * (viso->format <= VISO_FORMAT_HSF)] = strlen(dir->name_short); + memcpy(p, dir->name_short, data[5 * (viso->format <= VISO_FORMAT_HSF)]); + } + p += data[5 * (viso->format <= VISO_FORMAT_HSF)]; + + if ((p - data) & 1) /* padding for odd directory ID lengths */ + *p++ = 0x00; + + /* Write path table entry. */ + fwrite(data, p - data, 1, viso->tf.file); + + /* Increment path table index and stop if it overflows. */ + if (++pt_idx == 0) + break; + + /* Move on to the next directory. */ + dir = dir->next_dir; + } + + /* Write this table's size to the corresponding volume descriptor. */ + pt_temp = ftello64(viso->tf.file) - pt_start; + p = data; + VISO_LBE_32(p, pt_temp); + viso_pwrite(data, viso->pt_meta_offsets[i >> 1], 8, 1, viso->tf.file); + + /* Pad to the next even sector. */ + write = ftello64(viso->tf.file) % (viso->sector_size * 2); + if (write) { + write = (viso->sector_size * 2) - write; + memset(data, 0x00, write); + fwrite(data, write, 1, viso->tf.file); + } + } + + /* Write directory records for each type. */ + int dir_type = VISO_DIR_CURRENT_ROOT; + for (int i = 0; i <= (viso->format >= VISO_FORMAT_ISO_LFN); i++) { + cdrom_image_viso_log("VISO: Generating directory record set #%d:\n", i); + + /* Go through directories. */ + dir = viso->root_dir; + while (dir) { + /* Hide the El Torito boot code directory if no other files are present in it. */ + if (dir == eltorito_dir) { + dir = dir->next_dir; + continue; + } + + /* Pad to the next sector if required. */ + write = ftello64(viso->tf.file) % viso->sector_size; + if (write) { + write = viso->sector_size - write; + memset(data, 0x00, write); + fwrite(data, write, 1, viso->tf.file); + } + + /* Save this directory's child record array's start offset. */ + uint64_t dir_start = ftello64(viso->tf.file); + + /* Write this directory's child record array's sector offset to its record... */ + uint32_t dir_temp = dir_start / viso->sector_size; + p = data; + VISO_LBE_32(p, dir_temp); + viso_pwrite(data, dir->dr_offsets[i] + 2, 8, 1, viso->tf.file); + + /* ...and to its path table entries. */ + viso_pwrite(data, dir->pt_offsets[i << 1], 4, 1, viso->tf.file); /* little endian */ + viso_pwrite(data + 4, dir->pt_offsets[(i << 1) | 1], 4, 1, viso->tf.file); /* big endian */ + + if (i == (viso->format >= VISO_FORMAT_ISO_LFN)) /* overwrite pt_offsets in the union if we no longer need them */ + dir->file = NULL; + + /* Go through entries in this directory. */ + entry = dir->first_child; + while (entry) { + /* Skip the El Torito boot code entry if present, or hide the + boot code directory if no other files are present in it. */ + if ((entry == eltorito_entry) || (entry == eltorito_dir)) + goto next_entry; + + cdrom_image_viso_log("[%08X] %s => %s\n", entry, dir->path, + ((dir_type == VISO_DIR_PARENT) ? ".." : ((dir_type < VISO_DIR_PARENT) ? "." : (i ? entry->basename : entry->name_short)))); + + /* Fill directory record. */ + viso_fill_dir_record(data, entry, viso->format, dir_type); + + /* Entries cannot cross sector boundaries, so pad to the next sector if needed. */ + write = viso->sector_size - (ftello64(viso->tf.file) % viso->sector_size); + if (write < data[0]) { + p = data + (viso->sector_size * 2) - write; + memset(p, 0x00, write); + fwrite(p, write, 1, viso->tf.file); + } + + /* Save this entry's record's offset. This overwrites name_short in the union. */ + entry->dr_offsets[i] = ftello64(viso->tf.file); + + /* Write data related to the . and .. pseudo-subdirectories, + while advancing the current directory type. */ + if (dir_type < VISO_DIR_PARENT) { + /* Write a self-referential pointer to this entry. */ + p = data + 2; + VISO_LBE_32(p, dir_temp); + + dir_type = VISO_DIR_PARENT; + } else if (dir_type == VISO_DIR_PARENT) { + /* Copy the parent directory's offset and size. The root directory's + parent size is a special, self-referential case handled later. */ + viso_pread(data + 2, dir->parent->dr_offsets[i] + 2, 16, 1, viso->tf.file); + + dir_type = i ? VISO_DIR_JOLIET : VISO_DIR_REGULAR; + } + + /* Write entry. */ + fwrite(data, data[0], 1, viso->tf.file); +next_entry: + /* Move on to the next entry, and stop if the end of this directory was reached. */ + entry = entry->next; + if (entry && (entry->parent != dir)) + break; + } + + /* Write this directory's child record array's size to its parent and . records. */ + dir_temp = ftello64(viso->tf.file) - dir_start; + p = data; + VISO_LBE_32(p, dir_temp); + viso_pwrite(data, dir->dr_offsets[i] + 10, 8, 1, viso->tf.file); + viso_pwrite(data, dir->first_child->dr_offsets[i] + 10, 8, 1, viso->tf.file); + if (dir->parent == dir) /* write size to .. on root directory as well */ + viso_pwrite(data, dir->first_child->next->dr_offsets[i] + 10, 8, 1, viso->tf.file); + + /* Move on to the next directory. */ + dir_type = VISO_DIR_CURRENT; + dir = dir->next_dir; + } + + /* Pad to the next even sector. */ + write = ftello64(viso->tf.file) % (viso->sector_size * 2); + if (write) { + write = (viso->sector_size * 2) - write; + memset(data, 0x00, write); + fwrite(data, write, 1, viso->tf.file); + } + } + + /* Allocate entry map for sector->file lookups. */ + size_t orig_sector_size = viso->sector_size; + while (1) { + cdrom_image_viso_log("VISO: Allocating entry map for %d %d-byte sectors\n", viso->entry_map_size, viso->sector_size); + viso->entry_map = (viso_entry_t **) calloc(viso->entry_map_size, sizeof(viso_entry_t *)); + if (viso->entry_map) { + /* Successfully allocated. */ + break; + } else { + /* Blank data buffer for padding if this is the first run. */ + if (orig_sector_size == viso->sector_size) + memset(data, 0x00, orig_sector_size); + + /* If we don't have enough memory, double the sector size. */ + viso->sector_size *= 2; + if (viso->sector_size == 0) /* give up if sectors become too large */ + goto end; + + /* Go through files, recalculating the entry map size. */ + size_t orig_entry_map_size = viso->entry_map_size; + viso->entry_map_size = 0; + entry = viso->root_dir; + while (entry) { + if (!S_ISDIR(entry->stats.st_mode)) { + viso->entry_map_size += entry->stats.st_size / viso->sector_size; + if (entry->stats.st_size % viso->sector_size) + viso->entry_map_size++; /* round up to the next sector */ + } + entry = entry->next; + } + if (viso->entry_map_size == orig_entry_map_size) /* give up if there was no change in map size */ + goto end; + + /* Pad metadata to the new size's next sector. */ + while (ftello64(viso->tf.file) % viso->sector_size) + fwrite(data, orig_sector_size, 1, viso->tf.file); + } + } + + /* Start sector counts. */ + viso->metadata_sectors = ftello64(viso->tf.file) / viso->sector_size; + viso->all_sectors = viso->metadata_sectors; + + /* Go through files, assigning sectors to them. */ + cdrom_image_viso_log("VISO: Assigning sectors to files:\n"); + size_t base_factor = viso->sector_size / orig_sector_size; + viso_entry_t *prev_entry = viso->root_dir, + **entry_map_p = viso->entry_map; + entry = prev_entry->next; + while (entry) { + /* Skip this entry if it corresponds to a directory. */ + if (S_ISDIR(entry->stats.st_mode)) { + /* Deallocate directory entries to save some memory. */ + prev_entry->next = entry->next; + free(entry); + entry = prev_entry->next; + continue; + } + + /* Write this file's base sector offset to its directory + entries, unless this is the El Torito boot code entry, + in which case, write offset and size to the boot entry. */ + if (entry == eltorito_entry) { + /* Load the entire file if not emulating, or just the first virtual + sector (which usually contains all the boot code) if emulating. */ + if (eltorito_type == 0x00) { /* non-emulation */ + uint32_t boot_size = entry->stats.st_size; + if (boot_size % 512) /* round up */ + boot_size += 512 - (boot_size % 512); + *((uint16_t *) &data[0]) = cpu_to_le16(boot_size / 512); + } else { /* emulation */ + *((uint16_t *) &data[0]) = cpu_to_le16(1); + } + *((uint32_t *) &data[2]) = cpu_to_le32(viso->all_sectors * base_factor); + viso_pwrite(data, eltorito_offset, 6, 1, viso->tf.file); + } else { + p = data; + VISO_LBE_32(p, viso->all_sectors * base_factor); + for (int i = 0; i <= (viso->format >= VISO_FORMAT_ISO_LFN); i++) + viso_pwrite(data, entry->dr_offsets[i] + 2, 8, 1, viso->tf.file); + } + + /* Save this file's base offset. This overwrites dr_offsets in the union. */ + entry->data_offset = ((uint64_t) viso->all_sectors) * viso->sector_size; + + /* Determine how many sectors this file will take. */ + uint32_t size = entry->stats.st_size / viso->sector_size; + if (entry->stats.st_size % viso->sector_size) + size++; /* round up to the next sector */ + cdrom_image_viso_log("[%08X] %s => %" PRIu32 " + %" PRIu32 " sectors\n", entry, entry->path, viso->all_sectors, size); + + /* Allocate sectors to this file. */ + viso->all_sectors += size; + while (size-- > 0) + *entry_map_p++ = entry; + + /* Move on to the next entry. */ + prev_entry = entry; + entry = entry->next; + } + + /* Write final volume size to all volume descriptors. */ + p = data; + VISO_LBE_32(p, viso->all_sectors); + for (int i = 0; i < (sizeof(viso->vol_size_offsets) / sizeof(viso->vol_size_offsets[0])); i++) + viso_pwrite(data, viso->vol_size_offsets[i], 8, 1, viso->tf.file); + + /* Metadata processing is finished, read it back to memory. */ + cdrom_image_viso_log("VISO: Reading back %d %d-byte sectors of metadata\n", viso->metadata_sectors, viso->sector_size); + viso->metadata = (uint8_t *) calloc(viso->metadata_sectors, viso->sector_size); + if (!viso->metadata) + goto end; + fseeko64(viso->tf.file, 0, SEEK_SET); + uint64_t metadata_size = viso->metadata_sectors * viso->sector_size, metadata_remain = metadata_size; + while (metadata_remain > 0) + metadata_remain -= fread(viso->metadata + (metadata_size - metadata_remain), 1, MIN(metadata_remain, viso->sector_size), viso->tf.file); + + /* We no longer need the temporary file; close and delete it. */ + fclose(viso->tf.file); + viso->tf.file = NULL; +#ifndef ENABLE_CDROM_IMAGE_VISO_LOG + remove(nvr_path(viso->tf.fn)); +#endif + + /* All good. */ + *error = 0; + +end: + /* Set the function pointers. */ + viso->tf.priv = viso; + if (!*error) { + cdrom_image_viso_log("VISO: Initialized\n"); + viso->tf.read = viso_read; + viso->tf.get_length = viso_get_length; + viso->tf.close = viso_close; + return &viso->tf; + } else { + cdrom_image_viso_log("VISO: Initialization failed\n"); + if (data) + free(data); + viso_close(&viso->tf); + return NULL; + } +} diff --git a/src/codegen/codegen_accumulate_x86-64.c b/src/codegen/codegen_accumulate_x86-64.c index 05a728ae5..d2f1e0c3f 100644 --- a/src/codegen/codegen_accumulate_x86-64.c +++ b/src/codegen/codegen_accumulate_x86-64.c @@ -47,17 +47,17 @@ void codegen_accumulate(int acc_reg, int delta) void codegen_accumulate_flush(void) { + intptr_t rip; + if (acc_regs[0].count) { - addbyte(0x55); /*push rbp*/ - addbyte(0x48); /*mov rbp,val*/ - addbyte(0xbd); - addlong((uint32_t) (acc_regs[0].dest_reg & 0xffffffffULL)); - addlong((uint32_t) (acc_regs[0].dest_reg >> 32ULL)); - addbyte(0x81); /* add d,[rbp][0],val */ - addbyte(0x45); - addbyte(0x00); + /* To reduce the size of the generated code, we take advantage of + the fact that the target offset points to _cycles within cpu_state, + so we can just use our existing infrastracture for variables + relative to cpu_state. */ + addbyte(0x81); /*ADDL $acc_regs[0].count,(_cycles)*/ + addbyte(0x45); + addbyte((uint8_t)cpu_state_offset(_cycles)); addlong(acc_regs[0].count); - addbyte(0x5d); /*pop rbp*/ } acc_regs[0].count = 0; diff --git a/src/codegen/codegen_accumulate_x86.c b/src/codegen/codegen_accumulate_x86.c index 424cc45ab..b47c643d2 100644 --- a/src/codegen/codegen_accumulate_x86.c +++ b/src/codegen/codegen_accumulate_x86.c @@ -45,9 +45,13 @@ void codegen_accumulate(int acc_reg, int delta) void codegen_accumulate_flush(void) { if (acc_regs[0].count) { - addbyte(0x81); /*ADD $acc_regs[0].count,acc_regs[0].dest*/ - addbyte(0x05); - addlong((uint32_t) acc_regs[0].dest_reg); + /* To reduce the size of the generated code, we take advantage of + the fact that the target offset points to _cycles within cpu_state, + so we can just use our existing infrastracture for variables + relative to cpu_state. */ + addbyte(0x81); /*MOVL $acc_regs[0].count,(_cycles)*/ + addbyte(0x45); + addbyte((uint8_t)cpu_state_offset(_cycles)); addlong(acc_regs[0].count); } diff --git a/src/config.c b/src/config.c index 6212871be..702f41fe4 100644 --- a/src/config.c +++ b/src/config.c @@ -1291,6 +1291,14 @@ load_floppy_and_cdrom_drives(void) sprintf(temp, "fdd_%02i_check_bpb", c + 1); ini_section_delete_var(cat, temp); } + for (int i = 0; i < MAX_PREV_IMAGES; i++) { + fdd_image_history[c][i] = (char *) calloc(MAX_IMAGE_PATH_LEN + 1, sizeof(char)); + sprintf(temp, "fdd_%02i_image_history_%02i", c + 1, i + 1); + p = ini_section_get_string(cat, temp, NULL); + if (p) { + sprintf(fdd_image_history[c][i], "%s", p); + } + } } memset(temp, 0x00, sizeof(temp)); @@ -2680,6 +2688,15 @@ save_floppy_and_cdrom_drives(void) ini_section_delete_var(cat, temp); else ini_section_set_int(cat, temp, fdd_get_check_bpb(c)); + + for (int i = 0; i < MAX_PREV_IMAGES; i++) { + sprintf(temp, "fdd_%02i_image_history_%02i", c + 1, i + 1); + if ((fdd_image_history[c][i] == 0) || strlen(fdd_image_history[c][i]) == 0) { + ini_section_delete_var(cat, temp); + } else { + ini_section_set_string(cat, temp, fdd_image_history[c][i]); + } + } } for (c = 0; c < CDROM_NUM; c++) { diff --git a/src/cpu/codegen_timing_p6.c b/src/cpu/codegen_timing_p6.c index 0fb5359fe..00f5bfb24 100644 --- a/src/cpu/codegen_timing_p6.c +++ b/src/cpu/codegen_timing_p6.c @@ -158,10 +158,13 @@ static const macro_op_t lods_op = }; static const macro_op_t loop_op = { - .nr_uops = 2, + .nr_uops = 5, .decode_type = DECODE_COMPLEX, .uop[0] = {.type = UOP_ALU, .latency = 1}, - .uop[1] = {.type = UOP_BRANCH, .latency = 2} + .uop[1] = {.type = UOP_ALU, .latency = 1}, + .uop[2] = {.type = UOP_ALU, .latency = 1}, + .uop[3] = {.type = UOP_ALU, .latency = 1}, + .uop[4] = {.type = UOP_BRANCH, .latency = 1} }; static const macro_op_t mov_reg_seg_op = { diff --git a/src/disk/hdd.c b/src/disk/hdd.c index 2ba59eb93..ee731b046 100644 --- a/src/disk/hdd.c +++ b/src/disk/hdd.c @@ -442,16 +442,16 @@ hdd_preset_get_num() return sizeof(hdd_speed_presets) / sizeof(hdd_preset_t); } -char * +const char * hdd_preset_getname(int preset) { - return (char *) hdd_speed_presets[preset].name; + return hdd_speed_presets[preset].name; } -char * +const char * hdd_preset_get_internal_name(int preset) { - return (char *) hdd_speed_presets[preset].internal_name; + return hdd_speed_presets[preset].internal_name; } int diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index ae1c5658a..7f8649cb5 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -76,6 +76,7 @@ typedef struct { fdd_t fdd[FDD_NUM]; char floppyfns[FDD_NUM][512]; +char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; pc_timer_t fdd_poll_time[FDD_NUM]; diff --git a/src/include/86box/cdrom.h b/src/include/86box/cdrom.h index cd4eb5442..3be2910c8 100644 --- a/src/include/86box/cdrom.h +++ b/src/include/86box/cdrom.h @@ -102,7 +102,7 @@ typedef struct cdrom { media status. */ speed, cur_speed; - FILE *img_fp; + int is_dir; void *priv; char image_path[1024], diff --git a/src/include/86box/cdrom_image_backend.h b/src/include/86box/cdrom_image_backend.h index 64bd807b4..2a581624d 100644 --- a/src/include/86box/cdrom_image_backend.h +++ b/src/include/86box/cdrom_image_backend.h @@ -52,6 +52,7 @@ typedef struct { char fn[260]; FILE *file; + void *priv; } track_file_t; typedef struct { @@ -88,4 +89,10 @@ extern int cdi_load_cue(cd_img_t *cdi, const char *cuefile); extern int cdi_has_data_track(cd_img_t *cdi); extern int cdi_has_audio_track(cd_img_t *cdi); +/* Virtual ISO functions. */ +extern int viso_read(void *p, uint8_t *buffer, uint64_t seek, size_t count); +extern uint64_t viso_get_length(void *p); +extern void viso_close(void *p); +extern track_file_t *viso_init(const char *dirname, int *error); + #endif /*CDROM_IMAGE_BACKEND_H*/ diff --git a/src/include/86box/fdd.h b/src/include/86box/fdd.h index d2eab842b..2bfe290bb 100644 --- a/src/include/86box/fdd.h +++ b/src/include/86box/fdd.h @@ -22,6 +22,7 @@ #define EMU_FDD_H #define FDD_NUM 4 +#define FLOPPY_IMAGE_HISTORY 4 #define SEEK_RECALIBRATE -999 #ifdef __cplusplus @@ -84,6 +85,7 @@ typedef struct { extern DRIVE drives[FDD_NUM]; extern char floppyfns[FDD_NUM][512]; +extern char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; extern pc_timer_t fdd_poll_time[FDD_NUM]; extern int ui_writeprot[FDD_NUM]; diff --git a/src/include/86box/hdd.h b/src/include/86box/hdd.h index 905a1c294..d993d5c32 100644 --- a/src/include/86box/hdd.h +++ b/src/include/86box/hdd.h @@ -203,13 +203,13 @@ extern int image_is_hdi(const char *s); extern int image_is_hdx(const char *s, int check_signature); extern int image_is_vhd(const char *s, int check_signature); -extern double hdd_timing_write(hard_disk_t *hdd, uint32_t addr, uint32_t len); -extern double hdd_timing_read(hard_disk_t *hdd, uint32_t addr, uint32_t len); -extern double hdd_seek_get_time(hard_disk_t *hdd, uint32_t dst_addr, uint8_t operation, uint8_t continuous, double max_seek_time); -int hdd_preset_get_num(); -char *hdd_preset_getname(int preset); -extern char *hdd_preset_get_internal_name(int preset); -extern int hdd_preset_get_from_internal_name(char *s); -extern void hdd_preset_apply(int hdd_id); +extern double hdd_timing_write(hard_disk_t *hdd, uint32_t addr, uint32_t len); +extern double hdd_timing_read(hard_disk_t *hdd, uint32_t addr, uint32_t len); +extern double hdd_seek_get_time(hard_disk_t *hdd, uint32_t dst_addr, uint8_t operation, uint8_t continuous, double max_seek_time); +int hdd_preset_get_num(); +const char *hdd_preset_getname(int preset); +extern const char *hdd_preset_get_internal_name(int preset); +extern int hdd_preset_get_from_internal_name(char *s); +extern void hdd_preset_apply(int hdd_id); #endif /*EMU_HDD_H*/ diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index c85d3c1d4..3810b8ac3 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -787,6 +787,7 @@ extern int machine_xt_sansx16_init(const machine_t *); extern int machine_xt_bw230_init(const machine_t *); extern int machine_xt_iskra3104_init(const machine_t *); +extern int machine_xt_pravetz16_imko4_init(const machine_t *); /* m_xt_compaq.c */ extern int machine_xt_compaq_deskpro_init(const machine_t *); diff --git a/src/include/86box/plat.h b/src/include/86box/plat.h index 1c17d50bd..aa01ac129 100644 --- a/src/include/86box/plat.h +++ b/src/include/86box/plat.h @@ -76,6 +76,10 @@ extern "C" { # define atomic_bool_t atomic_bool #endif +#if defined(_MSC_VER) +# define ssize_t intptr_t +#endif + /* Global variables residing in the platform module. */ extern int dopause, /* system is paused */ mouse_capture; /* mouse is captured in app */ diff --git a/src/include/86box/plat_dir.h b/src/include/86box/plat_dir.h index 73c33eebf..7a7876ebb 100644 --- a/src/include/86box/plat_dir.h +++ b/src/include/86box/plat_dir.h @@ -17,6 +17,8 @@ #ifndef PLAT_DIR_H #define PLAT_DIR_H +/* Windows needs the POSIX re-implementations */ +#if defined(_WIN32) #ifdef _MAX_FNAME # define MAXNAMLEN _MAX_FNAME #else @@ -63,5 +65,10 @@ extern void seekdir(DIR *, long); extern int closedir(DIR *); #define rewinddir(dirp) seekdir(dirp, 0L) +#else +/* On linux and macOS, use the standard functions and types */ +#include +#endif + #endif /*PLAT_DIR_H*/ diff --git a/src/include/86box/resource.h b/src/include/86box/resource.h index af7fc1c46..27207a157 100644 --- a/src/include/86box/resource.h +++ b/src/include/86box/resource.h @@ -445,7 +445,8 @@ #define IDM_CDROM_EMPTY 0x4300 #define IDM_CDROM_RELOAD 0x4400 #define IDM_CDROM_IMAGE 0x4500 -#define IDM_CDROM_HOST_DRIVE 0x4600 +#define IDM_CDROM_DIR 0x4600 +#define IDM_CDROM_HOST_DRIVE 0x4700 #define IDM_ZIP_IMAGE_NEW 0x5200 #define IDM_ZIP_IMAGE_EXISTING 0x5300 diff --git a/src/include/86box/video.h b/src/include/86box/video.h index 4e5e426eb..62a424c6a 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -509,6 +509,7 @@ extern const device_t ibm_ps1_2121_device; extern const device_t tvga8900b_device; extern const device_t tvga8900d_device; extern const device_t tvga9000b_device; +extern const device_t nec_sv9000_device; /* IBM VGA */ extern const device_t vga_device; diff --git a/src/machine/m_xt.c b/src/machine/m_xt.c index cd9a78df6..17229238f 100644 --- a/src/machine/m_xt.c +++ b/src/machine/m_xt.c @@ -328,6 +328,24 @@ machine_xt_iskra3104_init(const machine_t *model) return ret; } +int +machine_xt_pravetz16_imko4_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/pravetz16/BIOS_IMKO4_FE00.bin", + 0x000fe000, 8192, 0); + + if (bios_only || !ret) + return ret; + + device_add(&keyboard_at_device); + + machine_xt_common_init(model); + + return ret; +} + int machine_xt_pc4i_init(const machine_t *model) { diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index e80b7858a..eca85c4bd 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -1610,6 +1610,42 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + { + .name = "[8088] Pravetz 16 / IMKO-4", + .internal_name = "pravetz16", + .type = MACHINE_TYPE_8088, + .chipset = MACHINE_CHIPSET_DISCRETE, + .init = machine_xt_pravetz16_imko4_init, + .pad = 0, + .pad0 = 0, + .pad1 = MACHINE_AVAILABLE, + .pad2 = 0, + .cpu = { + .package = CPU_PKG_8088, + .block = CPU_BLOCK_NONE, + .min_bus = 0, + .max_bus = 0, + .min_voltage = 0, + .max_voltage = 0, + .min_multi = 0, + .max_multi = 0 + }, + .bus_flags = MACHINE_PC, + .flags = MACHINE_FLAGS_NONE, + .ram = { + .min = 64, + .max = 640, + .step = 64 + }, + .nvrmask = 0, + .kbc = KBC_IBM_PC_XT, + .kbc_p1 = 0xff00, + .gpio = 0xffffffff, + .device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, /* 8086 Machines */ { diff --git a/src/qt/languages/cs-CZ.po b/src/qt/languages/cs-CZ.po index 6f7ecc69e..4b5c8d30d 100644 --- a/src/qt/languages/cs-CZ.po +++ b/src/qt/languages/cs-CZ.po @@ -229,6 +229,9 @@ msgstr "&Načíst znova předchozí obraz" msgid "&Image" msgstr "&Obraz..." +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Cílová snímková frekvence" diff --git a/src/qt/languages/de-DE.po b/src/qt/languages/de-DE.po index 2951c7b85..45f344a9e 100644 --- a/src/qt/languages/de-DE.po +++ b/src/qt/languages/de-DE.po @@ -229,6 +229,9 @@ msgstr "&Voriges Image neu laden" msgid "&Image" msgstr "&Image" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Ziel&framerate" diff --git a/src/qt/languages/en-GB.po b/src/qt/languages/en-GB.po index 90820a569..9ea707546 100644 --- a/src/qt/languages/en-GB.po +++ b/src/qt/languages/en-GB.po @@ -229,6 +229,9 @@ msgstr "&Reload previous image" msgid "&Image" msgstr "&Image" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Target &framerate" diff --git a/src/qt/languages/en-US.po b/src/qt/languages/en-US.po index b11e1cdea..916619b02 100644 --- a/src/qt/languages/en-US.po +++ b/src/qt/languages/en-US.po @@ -229,6 +229,9 @@ msgstr "&Reload previous image" msgid "&Image" msgstr "&Image" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Target &framerate" diff --git a/src/qt/languages/es-ES.po b/src/qt/languages/es-ES.po index c7b306e1c..0923b9948 100644 --- a/src/qt/languages/es-ES.po +++ b/src/qt/languages/es-ES.po @@ -229,6 +229,9 @@ msgstr "&Recargar imagen previa" msgid "&Image" msgstr "&Imagen..." +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Tasa de refresco objetivo" diff --git a/src/qt/languages/fi-FI.po b/src/qt/languages/fi-FI.po index 991b13205..ea250563d 100644 --- a/src/qt/languages/fi-FI.po +++ b/src/qt/languages/fi-FI.po @@ -229,6 +229,9 @@ msgstr "&Lataa edellinen levykuva uudelleen" msgid "&Image" msgstr "L&evykuva" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Kuvataajuustavoite" diff --git a/src/qt/languages/fr-FR.po b/src/qt/languages/fr-FR.po index 7fa029045..98a5f60fa 100644 --- a/src/qt/languages/fr-FR.po +++ b/src/qt/languages/fr-FR.po @@ -229,6 +229,9 @@ msgstr "&Recharger image précedente" msgid "&Image" msgstr "&Image" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Taux de rafraîchissement cible" diff --git a/src/qt/languages/hr-HR.po b/src/qt/languages/hr-HR.po index 1d3ab11a2..7b9cd71ea 100644 --- a/src/qt/languages/hr-HR.po +++ b/src/qt/languages/hr-HR.po @@ -229,6 +229,9 @@ msgstr "&Ponovo učitaj prethodnu sliku" msgid "&Image" msgstr "&Slika" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Ciljni broj okvira u sekundi" diff --git a/src/qt/languages/hu-HU.po b/src/qt/languages/hu-HU.po index 716ad22fc..022523ff9 100644 --- a/src/qt/languages/hu-HU.po +++ b/src/qt/languages/hu-HU.po @@ -229,6 +229,9 @@ msgstr "Előző képfájl &újratöltése" msgid "&Image" msgstr "&Meglévő képfájl &megnyitása..." +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Cél &képkockasebesség" diff --git a/src/qt/languages/it-IT.po b/src/qt/languages/it-IT.po index ee5e2a01a..a32efc320 100644 --- a/src/qt/languages/it-IT.po +++ b/src/qt/languages/it-IT.po @@ -229,6 +229,9 @@ msgstr "&Ricarica l'immagine precedente" msgid "&Image" msgstr "&Immagine" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Imposta obiettivo &fotogrammi" diff --git a/src/qt/languages/ja-JP.po b/src/qt/languages/ja-JP.po index d1da289c4..146275c74 100644 --- a/src/qt/languages/ja-JP.po +++ b/src/qt/languages/ja-JP.po @@ -229,6 +229,9 @@ msgstr "前のイメージを再読み込み(&R)" msgid "&Image" msgstr "イメージ(&I)" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "目標フレームレート(&F)" diff --git a/src/qt/languages/ko-KR.po b/src/qt/languages/ko-KR.po index bd5cd3e37..13f5be067 100644 --- a/src/qt/languages/ko-KR.po +++ b/src/qt/languages/ko-KR.po @@ -229,6 +229,9 @@ msgstr "이전 이미지 다시 불러오기(&R)" msgid "&Image" msgstr "이미지(&I)" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "목표 프레임 레이트(&F)" diff --git a/src/qt/languages/pl-PL.po b/src/qt/languages/pl-PL.po index 5c60b1eda..1a17217d6 100644 --- a/src/qt/languages/pl-PL.po +++ b/src/qt/languages/pl-PL.po @@ -229,6 +229,9 @@ msgstr "&Przeładuj poprzedni obraz" msgid "&Image" msgstr "&Obraz" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Docelowa &liczba klatek na sekundę" diff --git a/src/qt/languages/pt-BR.po b/src/qt/languages/pt-BR.po index b7f325a9a..44c03cfec 100644 --- a/src/qt/languages/pt-BR.po +++ b/src/qt/languages/pt-BR.po @@ -229,6 +229,9 @@ msgstr "&Recarregar imagem anterior" msgid "&Image" msgstr "&Imagem" +msgid "&Folder..." +msgstr "&Pasta..." + msgid "Target &framerate" msgstr "&Taxa de quadro pretendida" diff --git a/src/qt/languages/pt-PT.po b/src/qt/languages/pt-PT.po index 8497b935d..1cf187a0c 100644 --- a/src/qt/languages/pt-PT.po +++ b/src/qt/languages/pt-PT.po @@ -229,6 +229,9 @@ msgstr "&Recarregar imagem anterior" msgid "&Image" msgstr "&Imagem" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Taxa de quadros de destino" diff --git a/src/qt/languages/ru-RU.po b/src/qt/languages/ru-RU.po index a33cc81dd..b483fabb3 100644 --- a/src/qt/languages/ru-RU.po +++ b/src/qt/languages/ru-RU.po @@ -229,6 +229,9 @@ msgstr "&Снова загрузить предыдущий образ" msgid "&Image" msgstr "&Образ..." +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Целевая &частота кадров" diff --git a/src/qt/languages/sl-SI.po b/src/qt/languages/sl-SI.po index 5a396c855..6ea64a7c2 100644 --- a/src/qt/languages/sl-SI.po +++ b/src/qt/languages/sl-SI.po @@ -229,6 +229,9 @@ msgstr "&Naloži zadnjo sliko" msgid "&Image" msgstr "&Slika" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "&Ciljno št. sličic na sekundo" diff --git a/src/qt/languages/tr-TR.po b/src/qt/languages/tr-TR.po index 2a12b3d85..58f2c08bb 100644 --- a/src/qt/languages/tr-TR.po +++ b/src/qt/languages/tr-TR.po @@ -229,6 +229,9 @@ msgstr "&Önceki imajı seç" msgid "&Image" msgstr "&İmaj seç" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Hedef &kare oranı" diff --git a/src/qt/languages/uk-UA.po b/src/qt/languages/uk-UA.po index 76382a9c7..5b16fa84c 100644 --- a/src/qt/languages/uk-UA.po +++ b/src/qt/languages/uk-UA.po @@ -229,6 +229,9 @@ msgstr "&Знову завантажити попередній образ" msgid "&Image" msgstr "&Образ..." +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "Цільова &частота кадрів" diff --git a/src/qt/languages/zh-CN.po b/src/qt/languages/zh-CN.po index b9c6f6c98..b83ab1d46 100644 --- a/src/qt/languages/zh-CN.po +++ b/src/qt/languages/zh-CN.po @@ -229,6 +229,9 @@ msgstr "载入上一个镜像(&R)" msgid "&Image" msgstr "镜像(&I)" +msgid "&Folder..." +msgstr "&Folder..." + msgid "Target &framerate" msgstr "目标帧率(&F)" diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 0ee5a3161..fd2294415 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -202,7 +202,8 @@ MainWindow::MainWindow(QWidget *parent) : } } #endif - ui->actionPause->setChecked(dopause); + ui->actionPause->setChecked(false); + ui->actionPause->setCheckable(false); }); connect(this, &MainWindow::getTitleForNonQtThread, this, &MainWindow::getTitle_, Qt::BlockingQueuedConnection); @@ -753,6 +754,10 @@ void MainWindow::on_actionCtrl_Alt_Esc_triggered() { void MainWindow::on_actionPause_triggered() { plat_pause(dopause ^ 1); + auto pause_icon = dopause ? QIcon(":/menuicons/win/icons/run.ico") : QIcon(":/menuicons/win/icons/pause.ico"); + auto tooltip_text = dopause ? QString(tr("Resume execution")) : QString(tr("Pause execution")); + ui->actionPause->setIcon(pause_icon); + ui->actionPause->setToolTip(tooltip_text); } void MainWindow::on_actionExit_triggered() { diff --git a/src/qt/qt_mediahistorymanager.cpp b/src/qt/qt_mediahistorymanager.cpp index 884a13de5..19025d210 100644 --- a/src/qt/qt_mediahistorymanager.cpp +++ b/src/qt/qt_mediahistorymanager.cpp @@ -21,10 +21,15 @@ #include #include #include - -#include "86box/cdrom.h" #include "qt_mediahistorymanager.hpp" +extern "C" +{ +#include <86box/timer.h> +#include <86box/cdrom.h> +#include <86box/fdd.h> +} + namespace ui { MediaHistoryManager::MediaHistoryManager() { @@ -158,6 +163,9 @@ void MediaHistoryManager::initialDeduplication() case ui::MediaType::Optical: current_image = cdrom[device_index].image_path; break; + case ui::MediaType::Floppy: + current_image = floppyfns[device_index]; + break; default: continue; break; @@ -180,6 +188,8 @@ char ** MediaHistoryManager::getEmuHistoryVarForType(ui::MediaType type, int ind switch (type) { case ui::MediaType::Optical: return &cdrom[index].image_history[0]; + case ui::MediaType::Floppy: + return &fdd_image_history[index][0]; default: return nullptr; diff --git a/src/qt/qt_mediahistorymanager.hpp b/src/qt/qt_mediahistorymanager.hpp index 0a69aa100..c628ce793 100644 --- a/src/qt/qt_mediahistorymanager.hpp +++ b/src/qt/qt_mediahistorymanager.hpp @@ -59,7 +59,8 @@ namespace ui { // Used to iterate over all supported types when preparing data structures // Also useful to indicate which types support history static const MediaType AllSupportedMediaHistoryTypes[] = { - MediaType::Optical + MediaType::Optical, + MediaType::Floppy, }; class MediaHistoryManager { @@ -87,7 +88,7 @@ namespace ui { // Main hash of hash of vector of strings master_list_t master_list; - const master_list_t &getMasterList() const; + [[nodiscard]] const master_list_t &getMasterList() const; void setMasterList(const master_list_t &masterList); device_index_list_t index_list, empty_device_index_list; diff --git a/src/qt/qt_mediamenu.cpp b/src/qt/qt_mediamenu.cpp index ba52074cf..d11a14c96 100644 --- a/src/qt/qt_mediamenu.cpp +++ b/src/qt/qt_mediamenu.cpp @@ -105,6 +105,11 @@ void MediaMenu::refresh(QMenu *parentMenu) { menu->addAction(tr("&Existing image..."), [this, i]() { floppySelectImage(i, false); }); menu->addAction(tr("Existing image (&Write-protected)..."), [this, i]() { floppySelectImage(i, true); }); menu->addSeparator(); + for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) { + floppyImageHistoryPos[slot] = menu->children().count(); + menu->addAction(QString::asprintf(tr("Image %i").toUtf8().constData(), slot), [this, i, slot]() { floppyMenuSelect(i, slot); })->setCheckable(false); + } + menu->addSeparator(); floppyExportPos = menu->children().count(); menu->addAction(tr("E&xport to 86F..."), [this, i]() { floppyExportTo86f(i); }); menu->addSeparator(); @@ -120,7 +125,8 @@ void MediaMenu::refresh(QMenu *parentMenu) { cdromMutePos = menu->children().count(); menu->addAction(tr("&Mute"), [this, i]() { cdromMute(i); })->setCheckable(true); menu->addSeparator(); - menu->addAction(tr("&Image..."), [this, i]() { cdromMount(i); })->setCheckable(false); + menu->addAction(tr("&Image..."), [this, i]() { cdromMount(i, 0); })->setCheckable(false); + menu->addAction(tr("&Folder..."), [this, i]() { cdromMount(i, 1); })->setCheckable(false); menu->addSeparator(); for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) { cdromImageHistoryPos[slot] = menu->children().count(); @@ -128,6 +134,7 @@ void MediaMenu::refresh(QMenu *parentMenu) { } menu->addSeparator(); cdromImagePos = menu->children().count(); + cdromDirPos = menu->children().count(); menu->addAction(tr("E&ject"), [this, i]() { cdromEject(i); })->setCheckable(false); cdromMenus[i] = menu; cdromUpdateMenu(i); @@ -328,6 +335,7 @@ void MediaMenu::floppySelectImage(int i, bool wp) { } void MediaMenu::floppyMount(int i, const QString &filename, bool wp) { + auto previous_image = QFileInfo(floppyfns[i]); fdd_close(i); ui_writeprot[i] = wp ? 1 : 0; if (! filename.isEmpty()) { @@ -335,12 +343,14 @@ void MediaMenu::floppyMount(int i, const QString &filename, bool wp) { fdd_load(i, filenameBytes.data()); } ui_sb_update_icon_state(SB_FLOPPY | i, filename.isEmpty() ? 1 : 0); + mhm.addImageToHistory(i, ui::MediaType::Floppy, previous_image.filePath(), filename); floppyUpdateMenu(i); ui_sb_update_tip(SB_FLOPPY | i); config_save(); } void MediaMenu::floppyEject(int i) { + mhm.addImageToHistory(i, ui::MediaType::Floppy, floppyfns[i], QString()); fdd_close(i); ui_sb_update_icon_state(SB_FLOPPY | i, 1); floppyUpdateMenu(i); @@ -376,11 +386,22 @@ void MediaMenu::floppyUpdateMenu(int i) { ejectMenu->setText(QString::asprintf(tr("Eject %s").toUtf8().constData(), name.isEmpty() ? QString().toUtf8().constData() : fi.fileName().toUtf8().constData())); exportMenu->setEnabled(!name.isEmpty()); + for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) { + updateImageHistory(i, slot, ui::MediaType::Floppy); + } + int type = fdd_get_type(i); //floppyMenus[i]->setTitle(tr("Floppy %1 (%2): %3").arg(QString::number(i+1), fdd_getname(type), name.isEmpty() ? tr("(empty)") : name)); floppyMenus[i]->setTitle(QString::asprintf(tr("Floppy %i (%s): %ls").toUtf8().constData(), i + 1, fdd_getname(type), name.isEmpty() ? tr("(empty)").toStdU16String().data() : name.toStdU16String().data())); } +void MediaMenu::floppyMenuSelect(int index, int slot) { + QString filename = mhm.getImageForSlot(index, slot, ui::MediaType::Floppy); + floppyMount(index, filename.toUtf8().constData(), false); + floppyUpdateMenu(index); + ui_sb_update_tip(SB_FLOPPY | index); +} + void MediaMenu::cdromMute(int i) { cdrom[i].sound_on ^= 1; config_save(); @@ -415,16 +436,23 @@ void MediaMenu::cdromMount(int i, const QString &filename) config_save(); } -void MediaMenu::cdromMount(int i) { +void MediaMenu::cdromMount(int i, int dir) { + QString filename; + QFileInfo fi(cdrom[i].image_path); - auto filename = QFileDialog::getOpenFileName( - parentWidget, - QString(), - getMediaOpenDirectory(), - tr("CD-ROM images") % - util::DlgFilter({ "iso","cue" }) % - tr("All files") % - util::DlgFilter({ "*" }, true)); + if (dir) { + filename = QFileDialog::getExistingDirectory( + parentWidget); + } else { + filename = QFileDialog::getOpenFileName( + parentWidget, + QString(), + QString(), + tr("CD-ROM images") % + util::DlgFilter({ "iso","cue" }) % + tr("All files") % + util::DlgFilter({ "*" }, true)); + } if (filename.isEmpty()) { return; @@ -501,8 +529,9 @@ void MediaMenu::cdromUpdateMenu(int i) { imageMenu->setEnabled(!name.isEmpty()); imageMenu->setText(QString::asprintf(tr("Eject %s").toUtf8().constData(), name.isEmpty() ? QString().toUtf8().constData() : fi.fileName().toUtf8().constData())); - for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) + for (int slot = 0; slot < MAX_PREV_IMAGES; slot++) { updateImageHistory(i, slot, ui::MediaType::Optical); + } QString busName = tr("Unknown Bus"); switch (cdrom[i].bus_type) { diff --git a/src/qt/qt_mediamenu.hpp b/src/qt/qt_mediamenu.hpp index 4503c1b93..4cb177797 100644 --- a/src/qt/qt_mediamenu.hpp +++ b/src/qt/qt_mediamenu.hpp @@ -37,11 +37,12 @@ public: void floppySelectImage(int i, bool wp); void floppyMount(int i, const QString& filename, bool wp); void floppyEject(int i); + void floppyMenuSelect(int index, int slot); void floppyExportTo86f(int i); void floppyUpdateMenu(int i); void cdromMute(int i); - void cdromMount(int i); + void cdromMount(int i, int dir); void cdromMount(int i, const QString& filename); void cdromEject(int i); void cdromReload(int index, int slot); @@ -94,6 +95,7 @@ private: int cdromMutePos; int cdromReloadPos; int cdromImagePos; + int cdromDirPos; int cdromImageHistoryPos[MAX_PREV_IMAGES]; int floppyImageHistoryPos[MAX_PREV_IMAGES]; diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index 019d38cf4..10a6654c0 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -161,7 +161,13 @@ plat_timer_read(void) FILE * plat_fopen(const char *path, const char *mode) { +#if defined(Q_OS_MACOS) or defined(Q_OS_LINUX) + QFileInfo fi(path); + QString filename = (fi.isRelative() && !fi.filePath().isEmpty()) ? usr_path + fi.filePath() : fi.filePath(); + return fopen(filename.toUtf8().constData(), mode); +#else return fopen(QString::fromUtf8(path).toLocal8Bit(), mode); +#endif } FILE * @@ -169,7 +175,7 @@ plat_fopen64(const char *path, const char *mode) { #if defined(Q_OS_MACOS) or defined(Q_OS_LINUX) QFileInfo fi(path); - QString filename = fi.isRelative() ? usr_path + fi.filePath() : fi.filePath(); + QString filename = (fi.isRelative() && !fi.filePath().isEmpty()) ? usr_path + fi.filePath() : fi.filePath(); return fopen(filename.toUtf8().constData(), mode); #else return fopen(QString::fromUtf8(path).toLocal8Bit(), mode); diff --git a/src/qt/qt_settingsfloppycdrom.cpp b/src/qt/qt_settingsfloppycdrom.cpp index bf26c5162..6bf9859ce 100644 --- a/src/qt/qt_settingsfloppycdrom.cpp +++ b/src/qt/qt_settingsfloppycdrom.cpp @@ -154,7 +154,7 @@ void SettingsFloppyCDROM::save() { /* Removable devices category */ model = ui->tableViewCDROM->model(); for (int i = 0; i < CDROM_NUM; i++) { - cdrom[i].img_fp = NULL; + cdrom[i].is_dir = 0; cdrom[i].priv = NULL; cdrom[i].ops = NULL; cdrom[i].image = NULL; diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 8a44ac075..ed34e7914 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -139,6 +139,7 @@ video_cards[] = { { &tvga8900b_device }, { &tvga8900d_device }, { &tvga9000b_device }, + { &nec_sv9000_device }, { &et4000k_isa_device }, { &et2000_device }, { &et4000_isa_device }, diff --git a/src/video/vid_tvga.c b/src/video/vid_tvga.c index 0af37ac94..96931ac6f 100644 --- a/src/video/vid_tvga.c +++ b/src/video/vid_tvga.c @@ -35,9 +35,10 @@ #define TVGA9000B_ID 0x23 #define TVGA8900CLD_ID 0x33 -#define ROM_TVGA_8900B "roms/video/tvga/tvga8900b.vbi" -#define ROM_TVGA_8900CLD "roms/video/tvga/trident.bin" -#define ROM_TVGA_9000B "roms/video/tvga/tvga9000b.bin" +#define ROM_TVGA_8900B "roms/video/tvga/tvga8900b.vbi" +#define ROM_TVGA_8900CLD "roms/video/tvga/trident.bin" +#define ROM_TVGA_9000B "roms/video/tvga/tvga9000b.bin" +#define ROM_TVGA_9000B_NEC_SV9000 "roms/video/tvga/SV9000.VBI" typedef struct tvga_t { mem_mapping_t linear_mapping; @@ -389,7 +390,9 @@ tvga_init(const device_t *info) tvga_t *tvga = malloc(sizeof(tvga_t)); memset(tvga, 0, sizeof(tvga_t)); - if (info->local == TVGA9000B_ID) { + tvga->card_id = info->local & 0xFF; + + if (tvga->card_id == TVGA9000B_ID) { video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_tvga9000); tvga->vram_size = 512 << 10; } else { @@ -399,9 +402,7 @@ tvga_init(const device_t *info) tvga->vram_mask = tvga->vram_size - 1; - tvga->card_id = info->local; - - switch (info->local) { + switch (tvga->card_id) { case TVGA8900B_ID: bios_fn = ROM_TVGA_8900B; break; @@ -409,7 +410,7 @@ tvga_init(const device_t *info) bios_fn = ROM_TVGA_8900CLD; break; case TVGA9000B_ID: - bios_fn = ROM_TVGA_9000B; + bios_fn = (info->local & 0x100) ? ROM_TVGA_9000B_NEC_SV9000 : ROM_TVGA_9000B; break; default: free(tvga); @@ -424,7 +425,7 @@ tvga_init(const device_t *info) NULL, NULL); - if (info->local != TVGA9000B_ID) + if (tvga->card_id != TVGA9000B_ID) tvga->svga.ramdac = device_add(&tkd8001_ramdac_device); io_sethandler(0x03c0, 0x0020, tvga_in, NULL, NULL, tvga_out, NULL, NULL, tvga); @@ -450,6 +451,12 @@ tvga9000b_available(void) return rom_present(ROM_TVGA_9000B); } +static int +tvga9000b_nec_sv9000_available(void) +{ + return rom_present(ROM_TVGA_9000B_NEC_SV9000); +} + void tvga_close(void *p) { @@ -549,3 +556,17 @@ const device_t tvga9000b_device = { .force_redraw = tvga_force_redraw, .config = NULL }; + +const device_t nec_sv9000_device = { + .name = "NEC SV9000 (Trident TVGA 9000B)", + .internal_name = "nec_sv9000", + .flags = DEVICE_ISA, + .local = TVGA9000B_ID | 0x100, + .init = tvga_init, + .close = tvga_close, + .reset = NULL, + { .available = tvga9000b_nec_sv9000_available }, + .speed_changed = tvga_speed_changed, + .force_redraw = tvga_force_redraw, + .config = NULL +}; diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index b1c298970..e8eb299a4 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -632,7 +632,7 @@ MINIVHDOBJ := cwalk.o libxml2_encoding.o minivhd_convert.o \ minivhd_struct_rw.o minivhd_util.o CDROMOBJ := cdrom.o \ - cdrom_image_backend.o cdrom_image.o + cdrom_image_backend.o cdrom_image_viso.o cdrom_image.o ZIPOBJ := zip.o diff --git a/src/win/languages/cs-CZ.rc b/src/win/languages/cs-CZ.rc index c7bdb3cb1..63279611c 100644 --- a/src/win/languages/cs-CZ.rc +++ b/src/win/languages/cs-CZ.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Načíst znova předchozí obraz", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Obraz...", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/de-DE.rc b/src/win/languages/de-DE.rc index d99095d56..a2436fb3e 100644 --- a/src/win/languages/de-DE.rc +++ b/src/win/languages/de-DE.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Voriges Image neu laden", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Image", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/en-GB.rc b/src/win/languages/en-GB.rc index 9c1e8acd7..3eed04ed1 100644 --- a/src/win/languages/en-GB.rc +++ b/src/win/languages/en-GB.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Reload previous image", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Image", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/en-US.rc b/src/win/languages/en-US.rc index 5a5fa4fd2..ac8b1f6ee 100644 --- a/src/win/languages/en-US.rc +++ b/src/win/languages/en-US.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Reload previous image", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Image", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/es-ES.rc b/src/win/languages/es-ES.rc index 3dc9757ea..0aa8b3533 100644 --- a/src/win/languages/es-ES.rc +++ b/src/win/languages/es-ES.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Recargar imagen previa", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Imagen...", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/fi-FI.rc b/src/win/languages/fi-FI.rc index d1b390bcd..43ddbc6da 100644 --- a/src/win/languages/fi-FI.rc +++ b/src/win/languages/fi-FI.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Lataa edellinen levykuva uudelleen", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "L&evykuva", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/fr-FR.rc b/src/win/languages/fr-FR.rc index eab96bf05..ddcb22341 100644 --- a/src/win/languages/fr-FR.rc +++ b/src/win/languages/fr-FR.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Recharger image précedente", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Image", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/hr-HR.rc b/src/win/languages/hr-HR.rc index 3cb8eb6d3..d608a079a 100644 --- a/src/win/languages/hr-HR.rc +++ b/src/win/languages/hr-HR.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Ponovo učitaj prethodnu sliku", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Slika", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/hu-HU.rc b/src/win/languages/hu-HU.rc index 7c2e1cf5e..a35209d1f 100644 --- a/src/win/languages/hu-HU.rc +++ b/src/win/languages/hu-HU.rc @@ -182,6 +182,7 @@ BEGIN MENUITEM "Előző képfájl &újratöltése", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Meglévő képfájl &megnyitása...", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/it-IT.rc b/src/win/languages/it-IT.rc index ca24daed4..fb3f36b66 100644 --- a/src/win/languages/it-IT.rc +++ b/src/win/languages/it-IT.rc @@ -178,6 +178,7 @@ BEGIN MENUITEM "&Ricarica l'immagine precedente", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Immagine", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/ja-JP.rc b/src/win/languages/ja-JP.rc index 18017bfb4..f66055707 100644 --- a/src/win/languages/ja-JP.rc +++ b/src/win/languages/ja-JP.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "前のイメージを再読み込み(&R)", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "イメージ(&I)", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/ko-KR.rc b/src/win/languages/ko-KR.rc index 961b00748..2cc4fdf7b 100644 --- a/src/win/languages/ko-KR.rc +++ b/src/win/languages/ko-KR.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "이전 이미지 다시 불러오기(&R)", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "이미지(&I)", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/pl-PL.rc b/src/win/languages/pl-PL.rc index 5405778d3..9fb82438c 100644 --- a/src/win/languages/pl-PL.rc +++ b/src/win/languages/pl-PL.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Przeładuj poprzedni obraz", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Obraz", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/pt-BR.rc b/src/win/languages/pt-BR.rc index 538472293..3a5af87f8 100644 --- a/src/win/languages/pt-BR.rc +++ b/src/win/languages/pt-BR.rc @@ -180,6 +180,7 @@ BEGIN MENUITEM "&Recarregar imagem anterior", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Imagem", IDM_CDROM_IMAGE + MENUITEM "&Pasta", IDM_CDROM_DIR END END diff --git a/src/win/languages/pt-PT.rc b/src/win/languages/pt-PT.rc index 314f7ed56..81b27665e 100644 --- a/src/win/languages/pt-PT.rc +++ b/src/win/languages/pt-PT.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Recarregar imagem anterior", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Imagem", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/ru-RU.rc b/src/win/languages/ru-RU.rc index e88d9668e..868f9c4b3 100644 --- a/src/win/languages/ru-RU.rc +++ b/src/win/languages/ru-RU.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Снова загрузить предыдущий образ", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Образ...", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/sl-SI.rc b/src/win/languages/sl-SI.rc index e8672235f..b6713d3da 100644 --- a/src/win/languages/sl-SI.rc +++ b/src/win/languages/sl-SI.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Naloži zadnjo sliko", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Slika", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/tr-TR.rc b/src/win/languages/tr-TR.rc index 3d1cffc97..2317d5a6e 100644 --- a/src/win/languages/tr-TR.rc +++ b/src/win/languages/tr-TR.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Önceki imajı seç", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&İmaj seç", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/uk-UA.rc b/src/win/languages/uk-UA.rc index 5a741a5c9..dd4171611 100644 --- a/src/win/languages/uk-UA.rc +++ b/src/win/languages/uk-UA.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "&Знову завантажити попередній образ", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "&Образ...", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/languages/zh-CN.rc b/src/win/languages/zh-CN.rc index 3151b7143..d2566ac15 100644 --- a/src/win/languages/zh-CN.rc +++ b/src/win/languages/zh-CN.rc @@ -177,6 +177,7 @@ BEGIN MENUITEM "载入上一个镜像(&R)", IDM_CDROM_RELOAD MENUITEM SEPARATOR MENUITEM "镜像(&I)", IDM_CDROM_IMAGE + MENUITEM "&Folder", IDM_CDROM_DIR END END diff --git a/src/win/win_dialog.c b/src/win/win_dialog.c index a15974b01..fa48e067a 100644 --- a/src/win/win_dialog.c +++ b/src/win/win_dialog.c @@ -172,7 +172,8 @@ file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, WCHAR *title, int save) * not use the contents of szFile to initialize itself. */ memset(ofn.lpstrFile, 0x00, 512 * sizeof(WCHAR)); - memcpy(ofn.lpstrFile, fn, (wcslen(fn) << 1) + 2); + if (fn) + memcpy(ofn.lpstrFile, fn, (wcslen(fn) << 1) + 2); ofn.nMaxFile = sizeof_w(wopenfilestring); ofn.lpstrFilter = f; ofn.nFilterIndex = 1; @@ -211,11 +212,12 @@ file_dlg(HWND hwnd, WCHAR *f, char *fn, char *title, int save) { WCHAR ufn[512], title_buf[512]; - mbstoc16s(ufn, fn, strlen(fn) + 1); + if (fn) + mbstoc16s(ufn, fn, strlen(fn) + 1); if (title) mbstoc16s(title_buf, title, sizeof title_buf); - return (file_dlg_w(hwnd, f, ufn, title ? title_buf : NULL, save)); + return (file_dlg_w(hwnd, f, fn ? ufn : NULL, title ? title_buf : NULL, save)); } int diff --git a/src/win/win_media_menu.c b/src/win/win_media_menu.c index 1a37e2ce4..7695f28b2 100644 --- a/src/win/win_media_menu.c +++ b/src/win/win_media_menu.c @@ -3,6 +3,7 @@ #include #include #include +#include #include <86box/86box.h> #include <86box/cdrom.h> #include <86box/config.h> @@ -294,11 +295,13 @@ media_menu_update_cdrom(int id) CheckMenuItem(menus[i], IDM_CDROM_MUTE | id, MF_BYCOMMAND | MF_UNCHECKED); if (cdrom[id].host_drive == 200) { - CheckMenuItem(menus[i], IDM_CDROM_IMAGE | id, MF_BYCOMMAND | MF_CHECKED); + CheckMenuItem(menus[i], IDM_CDROM_IMAGE | id, MF_BYCOMMAND | (cdrom[id].is_dir ? MF_UNCHECKED : MF_CHECKED)); + CheckMenuItem(menus[i], IDM_CDROM_DIR | id, MF_BYCOMMAND | (cdrom[id].is_dir ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(menus[i], IDM_CDROM_EMPTY | id, MF_BYCOMMAND | MF_UNCHECKED); } else { cdrom[id].host_drive = 0; CheckMenuItem(menus[i], IDM_CDROM_IMAGE | id, MF_BYCOMMAND | MF_UNCHECKED); + CheckMenuItem(menus[i], IDM_CDROM_DIR | id, MF_BYCOMMAND | MF_UNCHECKED); CheckMenuItem(menus[i], IDM_CDROM_EMPTY | id, MF_BYCOMMAND | MF_CHECKED); } @@ -629,11 +632,32 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_CDROM_IMAGE: - if (!file_dlg_st(hwnd, IDS_2140, cdrom[id].image_path, NULL, 0)) { + if (!file_dlg_st(hwnd, IDS_2140, cdrom[id].is_dir ? NULL : cdrom[id].image_path, NULL, 0)) { cdrom_mount(id, openfilestring); } break; + case IDM_CDROM_DIR: + BROWSEINFO bi = { + .hwndOwner = hwnd, + .ulFlags = BIF_EDITBOX + }; + OleInitialize(NULL); + int old_dopause = dopause; + plat_pause(1); + LPITEMIDLIST pidl = SHBrowseForFolder(&bi); + plat_pause(old_dopause); + plat_chdir(usr_path); + if (pidl) { + wchar_t wbuf[MAX_PATH + 1]; + if (SHGetPathFromIDList(pidl, wbuf)) { + char buf[MAX_PATH + 1]; + c16stombs(buf, wbuf, sizeof(buf) - 1); + cdrom_mount(id, buf); + } + } + break; + case IDM_ZIP_IMAGE_NEW: NewFloppyDialogCreate(hwnd, id | 0x80, 0); /* NewZIPDialogCreate */ break; diff --git a/src/win/win_new_floppy.c b/src/win/win_new_floppy.c index e1cf3bcbd..95cde3176 100644 --- a/src/win/win_new_floppy.c +++ b/src/win/win_new_floppy.c @@ -51,27 +51,27 @@ typedef struct { } disk_size_t; static const disk_size_t disk_sizes[14] = { -// { 1, 1, 2, 1, 1, 77, 26, 0, 0, 4, 2, 6, 68 }, /* 250k 8" */ -// { 1, 2, 2, 1, 1, 77, 26, 0, 0, 4, 2, 6, 68 }, /* 500k 8" */ -// { 1, 1, 2, 1, 1, 77, 8, 3, 0, 1, 2, 2, 192 }, /* 616k 8" */ -// { 1, 2, 0, 1, 1, 77, 8, 3, 0, 1, 2, 2, 192 }, /* 1232k 8" */ - { 0, 1, 2, 1, 0, 40, 8, 2, 0xfe, 1, 2, 1, 64 }, /* 160k */ - { 0, 1, 2, 1, 0, 40, 9, 2, 0xfc, 1, 2, 2, 64 }, /* 180k */ - { 0, 2, 2, 1, 0, 40, 8, 2, 0xff, 2, 2, 1, 112 }, /* 320k */ - { 0, 2, 2, 1, 0, 40, 9, 2, 0xfd, 2, 2, 2, 112 }, /* 360k */ - { 0, 2, 2, 1, 0, 80, 8, 2, 0xfb, 2, 2, 2, 112 }, /* 640k */ - { 0, 2, 2, 1, 0, 80, 9, 2, 0xf9, 2, 2, 3, 112 }, /* 720k */ - { 1, 2, 0, 1, 1, 80, 15, 2, 0xf9, 1, 2, 7, 224 }, /* 1.2M */ - { 1, 2, 0, 1, 1, 77, 8, 3, 0xfe, 1, 2, 2, 192 }, /* 1.25M */ - { 1, 2, 0, 1, 0, 80, 18, 2, 0xf0, 1, 2, 9, 224 }, /* 1.44M */ - { 1, 2, 0, 1, 0, 80, 21, 2, 0xf0, 2, 2, 5, 16 }, /* DMF cluster 1024 */ - { 1, 2, 0, 1, 0, 80, 21, 2, 0xf0, 4, 2, 3, 16 }, /* DMF cluster 2048 */ - { 2, 2, 3, 1, 0, 80, 36, 2, 0xf0, 2, 2, 9, 240 }, /* 2.88M */ - { 0, 64, 0, 0, 0, 96, 32, 2, 0, 0, 0, 0, 0 }, /* ZIP 100 */ - { 0, 64, 0, 0, 0, 239, 32, 2, 0, 0, 0, 0, 0 } }; /* ZIP 250 */ - -static unsigned char *empty; + // { 1, 1, 2, 1, 1, 77, 26, 0, 0, 4, 2, 6, 68 }, /* 250k 8" */ + // { 1, 2, 2, 1, 1, 77, 26, 0, 0, 4, 2, 6, 68 }, /* 500k 8" */ + // { 1, 1, 2, 1, 1, 77, 8, 3, 0, 1, 2, 2, 192 }, /* 616k 8" */ + // { 1, 2, 0, 1, 1, 77, 8, 3, 0, 1, 2, 2, 192 }, /* 1232k 8" */ + {0, 1, 2, 1, 0, 40, 8, 2, 0xfe, 1, 2, 1, 64 }, /* 160k */ + { 0, 1, 2, 1, 0, 40, 9, 2, 0xfc, 1, 2, 2, 64 }, /* 180k */ + { 0, 2, 2, 1, 0, 40, 8, 2, 0xff, 2, 2, 1, 112}, /* 320k */ + { 0, 2, 2, 1, 0, 40, 9, 2, 0xfd, 2, 2, 2, 112}, /* 360k */ + { 0, 2, 2, 1, 0, 80, 8, 2, 0xfb, 2, 2, 2, 112}, /* 640k */ + { 0, 2, 2, 1, 0, 80, 9, 2, 0xf9, 2, 2, 3, 112}, /* 720k */ + { 1, 2, 0, 1, 1, 80, 15, 2, 0xf9, 1, 2, 7, 224}, /* 1.2M */ + { 1, 2, 0, 1, 1, 77, 8, 3, 0xfe, 1, 2, 2, 192}, /* 1.25M */ + { 1, 2, 0, 1, 0, 80, 18, 2, 0xf0, 1, 2, 9, 224}, /* 1.44M */ + { 1, 2, 0, 1, 0, 80, 21, 2, 0xf0, 2, 2, 5, 16 }, /* DMF cluster 1024 */ + { 1, 2, 0, 1, 0, 80, 21, 2, 0xf0, 4, 2, 3, 16 }, /* DMF cluster 2048 */ + { 2, 2, 3, 1, 0, 80, 36, 2, 0xf0, 2, 2, 9, 240}, /* 2.88M */ + { 0, 64, 0, 0, 0, 96, 32, 2, 0, 0, 0, 0, 0 }, /* ZIP 100 */ + { 0, 64, 0, 0, 0, 239, 32, 2, 0, 0, 0, 0, 0 } +}; /* ZIP 250 */ +static unsigned char *empty; static int create_86f(char *file_name, disk_size_t disk_size, uint8_t rpm_mode) diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 83a181c42..ff4eaf3b0 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -595,7 +595,7 @@ win_settings_save(void) /* Removable devices category */ memcpy(cdrom, temp_cdrom, CDROM_NUM * sizeof(cdrom_t)); for (i = 0; i < CDROM_NUM; i++) { - cdrom[i].img_fp = NULL; + cdrom[i].is_dir = 0; cdrom[i].priv = NULL; cdrom[i].ops = NULL; cdrom[i].image = NULL;