From d913e3b264dca1d5d457177941ae70f43b214527 Mon Sep 17 00:00:00 2001 From: Fabio Cavallo Date: Sat, 30 Dec 2023 13:48:57 +0100 Subject: [PATCH] Rewritten FDS emulation. Old diff files (.dif) are no longer compatible. Now the diff files are in IPS format and have the extension ".ipf" for FDS files and ".ipq" for Quick Disk files. --- src/core/cpu_inline.h | 161 ++--- src/core/fds.c | 1002 ++++++++++++++++++++++---------- src/core/fds.h | 99 ++-- src/core/mappers/mapper_FDS.c | 289 ++++----- src/core/patcher.c | 6 +- src/core/recording.c | 6 +- src/core/save_slot.c | 25 +- src/core/unicode_def.h | 2 + src/gui/designer/mainWindow.ui | 14 +- src/gui/mainWindow.cpp | 31 +- src/gui/wdgOverlayUi.cpp | 2 + 11 files changed, 1010 insertions(+), 627 deletions(-) diff --git a/src/core/cpu_inline.h b/src/core/cpu_inline.h index 10a1396c3..a879aad20 100644 --- a/src/core/cpu_inline.h +++ b/src/core/cpu_inline.h @@ -633,25 +633,23 @@ INLINE static BYTE fds_rd_mem(BYTE nidx, WORD address, BYTE made_tick) { // |||+------ CRC control (0: CRC passed; 1: CRC error) // |+-------- End of Head (1 when disk head is on the most inner track) // +--------- Disk Data Read/Write Enable (1 when disk is readable/writable) - - // azzero - nes[nidx].c.cpu.openbus = 0; // bit 0 (timer irq) - nes[nidx].c.cpu.openbus |= fds.drive.irq_timer_high; + nes[nidx].c.cpu.openbus = fds.drive.irq_timer_high; // bit 1 (trasfer flag) nes[nidx].c.cpu.openbus |= fds.drive.transfer_flag; // bit 2 e 3 non settati // TODO : bit 4 (CRC control : 0 passato, 1 errore) + nes[nidx].c.cpu.openbus |= (fds.drive.crc ? 0x10 : 0x00); // bit 5 non settato - // TODO : bit 6 (end of head) + // bit 6 (end of head) nes[nidx].c.cpu.openbus |= fds.drive.end_of_head; - //fds.drive.end_of_head = FALSE; - // TODO : bit 7 (disk data read/write enable (1 when disk is readable/writable) - fds.drive.transfer_flag = FALSE; + // bit 7 (disk data read/write enable (1 when disk is readable/writable)) + nes[nidx].c.cpu.openbus |= fds.drive.data_available; // devo disabilitare sia il timer IRQ ... fds.drive.irq_timer_high = FALSE; nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; // che il disk IRQ + fds.drive.transfer_flag = FALSE; nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; #if !defined (RELEASE) //printf("0x%04X 0x%02X %d\n", address, nes[nidx].c.cpu.openbus, nes[nidx].c.irq.high); @@ -659,15 +657,16 @@ INLINE static BYTE fds_rd_mem(BYTE nidx, WORD address, BYTE made_tick) { return (TRUE); } if (address == 0x4031) { - nes[nidx].c.cpu.openbus = fds.drive.data_readed; + nes[nidx].c.cpu.openbus = fds.drive.data_io; + fds.drive.data_available = FALSE; + // devo disabilitare il disk IRQ + fds.drive.transfer_flag = FALSE; + nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; #if !defined (RELEASE) //printf("0x%04X 0x%02X [0x%04X] 0x%04X %d %d\n", address, nes[nidx].c.cpu.openbus, // fds.info.sides[fds.drive.side_inserted].data[fds.drive.disk_position], nes[nidx].c.cpu.opcode_PC, // fds.drive.disk_position, nes[nidx].c.irq.high); #endif - fds.drive.transfer_flag = FALSE; - // devo disabilitare il disk IRQ - nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; return (TRUE); } if (address == 0x4032) { @@ -685,33 +684,42 @@ INLINE static BYTE fds_rd_mem(BYTE nidx, WORD address, BYTE made_tick) { } else if (fds.drive.end_of_head | fds.drive.transfer_reset) { nes[nidx].c.cpu.openbus |= 0x02; } + nes[nidx].c.cpu.openbus |= fds.info.write_protected; + #if !defined (RELEASE) //printf("%5d - %3d - %3d : 0x%04X 0x%02X %d %d %d %d\n", // nes[nidx].p.ppu.frames, // nes[nidx].p.ppu.frame_y, // nes[nidx].p.ppu.frame_x, // address, nes[nidx].c.cpu.openbus, fds.drive.disk_ejected, fds.drive.scan, - // fds.drive.delay, fds.drive.disk_position); + // fds.drive.delay_insert, fds.drive.disk_position); #endif - if (fds_auto_insert_enabled()) { - if ((nes[nidx].p.ppu.frames - fds.auto_insert.r4032.frames) < 100) { - if ((++fds.auto_insert.r4032.checks > FDS_AUTOINSERT_R4032_MAX_CHECKS) && - (fds.auto_insert.delay.side == -1) && - (fds.auto_insert.delay.eject == -1) && - (fds.auto_insert.delay.dummy == -1)) { - // FDS interessati : - // - Ao no Senritsu (1987)(Gakken)(J).fds - // - Bishojou SF Alien Battle (19xx)(Hacker International)(J)(Unl)[b].fds - // senza questo controllo entrambi gli fds potrebbero rimanere in attesa - // del cambio side senza che l'auto insert avvenga visto che l'END_OF_HEAD - // e' stato raggiunto prima. - fds.auto_insert.r4032.checks = 0; - fds.auto_insert.delay.eject = FDS_OP_SIDE_DELAY; + if (fds_auto_insert_enabled() && !fds.auto_insert.r4032.disabled && !fds.auto_insert.delay.dummy) { + if (!fds.auto_insert.r4032.frames) { + fds.auto_insert.r4032.frames = nes[nidx].p.ppu.frames; + } + { + int diff = (nes[nidx].p.ppu.frames - fds.auto_insert.r4032.frames); + + if (diff > 10) { + if (diff < 70) { + if (!fds.drive.scan && (++fds.auto_insert.r4032.checks > FDS_AUTOINSERT_R4032_MAX_CHECKS)) { + // - 19 Neunzehn (1988)(Soft Pro)(J).fds (il controllo del r4032 e' mooooolto lento) + // - Dandy (19xx)(Pony Canyon)(J).fds + // - Zelda no Densetsu - The Hyrule Fantasy (1986)(Nintendo)(J).fds + // - Ao no Senritsu (1987)(Gakken)(J).fds + // - Bishojou SF Alien Battle (19xx)(Hacker International)(J)(Unl)[b].fds + fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + gui_update_fds_menu(); + fds.auto_insert.delay.dummy = fds.info.cycles_dummy_delay; + } + fds.auto_insert.r4032.frames = nes[nidx].p.ppu.frames; + } else { + fds.auto_insert.r4032.frames = 0; + fds.auto_insert.r4032.checks = 0; + } } - } else { - fds.auto_insert.r4032.checks = 0; } - fds.auto_insert.r4032.frames = nes[nidx].p.ppu.frames; } return (TRUE); } @@ -1808,15 +1816,6 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { if ((address >= 0x4020) && (address <= 0x4026)) { // eseguo un tick hardware tick_hw(nidx, 1); -#if !defined (RELEASE) - //if (address == 0x4025) { - // printf("0x%04X 0x%02X %d\n", address, value, fds.drive.enabled_dsk_reg); - //} else { - // if (fds.drive.disk_position) - // printf("0x%04X 0x%02X 0x%04X %d 0x%02X %d\n", address, value, nes[nidx].c.cpu.opcode_PC, - // fds.drive.disk_position - 1, fds.side.data[fds.drive.disk_position - 1], nes[nidx].p.ppu.frames); - //} -#endif if (address == 0x4020) { // 7 bit 0 // --------- @@ -1832,7 +1831,7 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { // LLLL LLLL // |||| |||| // ++++-++++- 8 MSB of IRQ timer - fds.drive.irq_timer_reload = (value << 8) | (fds.drive.irq_timer_reload & 0x00FF); + fds.drive.irq_timer_reload = (fds.drive.irq_timer_reload & 0x00FF) | (value << 8); return (TRUE); } if (address == 0x4022) { @@ -1845,12 +1844,12 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { if (fds.drive.enabled_dsk_reg) { fds.drive.irq_timer_reload_enabled = value & 0x01; fds.drive.irq_timer_enabled = value & 0x02; - } - if (fds.drive.irq_timer_enabled) { - fds.drive.irq_timer_counter = fds.drive.irq_timer_reload; - } else { - fds.drive.irq_timer_high = FALSE; - nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; + if (fds.drive.irq_timer_enabled) { + fds.drive.irq_timer_counter = fds.drive.irq_timer_reload; + } else { + fds.drive.irq_timer_high = FALSE; + nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; + } } return (TRUE); } @@ -1866,7 +1865,6 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { #endif fds.drive.enabled_dsk_reg = value & 0x01; fds.drive.enabled_snd_reg = value & 0x02; - if (!fds.drive.enabled_dsk_reg) { fds.drive.irq_timer_high = FALSE; nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; @@ -1874,9 +1872,19 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { return (TRUE); } if (address == 0x4024) { - fds.drive.data_to_write = value; + if (fds.drive.enabled_dsk_reg) { + fds.drive.data_io = value; + fds.drive.data_available = FALSE; + } fds.drive.transfer_flag = FALSE; nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; +#if !defined (RELEASE) + //printf("%5d - %3d - %3d : 0x%04X 0x%02X %d %d\n", + // nes[nidx].p.ppu.frames, + // nes[nidx].p.ppu.frame_y, + // nes[nidx].p.ppu.frame_x, + // address, value, fds.drive.disk_position, fds.drive.io_mode); +#endif return (TRUE); } if (address == 0x4025) { @@ -1904,32 +1912,43 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { // nes[nidx].p.ppu.frames, // nes[nidx].p.ppu.frame_y, // nes[nidx].p.ppu.frame_x, - // address, value, fds.drive.disk_ejected, fds.drive.scan, - // fds.drive.delay, fds.drive.disk_position); + // address, value, fds.drive.motor_on, fds.drive.scan, + // fds.drive.delay_insert, fds.drive.disk_position); #endif - if (!fds.drive.enabled_dsk_reg) { - return (TRUE); - } - - fds.drive.motor_on = value & 0x01; - fds.drive.transfer_reset = value & 0x02; + if (fds.drive.enabled_dsk_reg) { + fds.drive.motor_on = value & 0x01; + if (!fds.drive.transfer_reset && (value & 0x02)) { + fds.drive.motor_started = TRUE; + } + fds.drive.transfer_reset = value & 0x02; + if (!fds.drive.transfer_reset || !fds.drive.motor_on) { + fds.drive.motor_started = FALSE; + } + fds.drive.io_mode = value & 0x04; + fds.drive.mirroring = value & 0x08; + if (fds.drive.mirroring) { + mirroring_H(nidx); + } else { + mirroring_V(nidx); + } + // If we are writing and a CRC value is about to be written (rising edge on bit 4), + // feed two empty bytes to the CRC routine to yield the correct value. + if (!fds.drive.crc_control && (value & 0x10) && !fds.drive.io_mode) { + fds.drive.crc = fds_crc_byte(fds.drive.crc, FDS_DISK_GAP); + fds.drive.crc = fds_crc_byte(fds.drive.crc, FDS_DISK_GAP); + } + fds.drive.crc_control = value & 0x10; + fds.drive.unknow = value & 0x20; + fds.drive.drive_ready = value & 0x40; + if (!fds.drive.drive_ready) { + fds.drive.mark_finded = FALSE; + } + fds.drive.irq_disk_enabled = value & 0x80; -// TODO : penso che sia corretto -// if (fds.drive.read_mode != (value & 0x04)) { -// fds.drive.gap_ended = FALSE; -// } - fds.drive.read_mode = value & 0x04; - fds.drive.mirroring = value & 0x08; - if (fds.drive.mirroring) { - mirroring_H(nidx); - } else { - mirroring_V(nidx); + // "Akuu Senki Raijin (Japan) (Disk Writer)" (sporcare lo screen). + fds.drive.delay_8bit = fds.info.cycles_8bit_delay; } - fds.drive.crc_control = value & 0x10; - fds.drive.unknow = value & 0x20; - fds.drive.drive_ready = value & 0x40; - fds.drive.irq_disk_enabled = value & 0x80; - + fds.drive.transfer_flag = FALSE; nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; return (TRUE); } diff --git a/src/core/fds.c b/src/core/fds.c index 2f1d23a43..47126ef64 100644 --- a/src/core/fds.c +++ b/src/core/fds.c @@ -28,7 +28,6 @@ #include "../../c++/crc/crc.h" #define BIOSFILE "disksys.rom" -#define DIFFVERSION 2 typedef struct _fds_sinfo { struct _fds_side_block1 { @@ -59,18 +58,46 @@ typedef struct _fds_sinfo { } _fds_sinfo; typedef struct _fds_diff_ele { BYTE side; - WORD value; + BYTE value; uint32_t position; } _fds_diff_ele; - -void fds_to_image(void); +typedef struct _fds_control_autoinsert { + char name[4]; + uint32_t crc32prg[4]; + BYTE disable_autoinsert; + BYTE disable_r4032; + BYTE disable_end_of_head; +} _fds_control_autoinsert; +typedef struct _fds_info_block { + uint32_t blength; + WORD faddress; + WORD flength; + WORD flength_new; + WORD flength_cpu; + WORD flength_ppu; + BYTE ftype; + BYTE files; + BYTE bl1; + BYTE stop; + int count2000; + BYTE magic_card_trainer; + BYTE quick_hunter; + BYTE ouji; + BYTE kgk; +} _fds_info_block; + +BYTE fds_to_image(void); +BYTE fds_examine_block(const BYTE *src, uint32_t position, _fds_info_block *fb); void fds_control_autoinsert(_fds_sinfo *sinfo); void fds_diff_file_name(uTCHAR *dst, size_t lenght); void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo); -void fds_image_memset(WORD *dst, WORD value, uint32_t lenght); -void fds_image_memcpy(const BYTE *src, WORD *dst, uint32_t lenght); -void fds_image_memcpy_ASCII(const WORD *src, BYTE *dst, size_t lenght); -WORD fds_block_crc(const WORD *src, uint32_t lenght); +void fds_image_memset(BYTE *dst, BYTE value, uint32_t lenght); +void fds_image_memcpy(const BYTE *src, BYTE *dst, uint32_t lenght); +void fds_image_memcpy_ASCII(const BYTE *src, BYTE *dst, size_t lenght); +WORD fds_crc_block(const BYTE *src, uint32_t lenght); +BYTE *fds_from_image_to_mem(BYTE format, BYTE type, size_t *size); +BYTE *fds_read_ips(BYTE *data, const size_t size, const char *file); +BYTE fds_create_ips(const BYTE *d1, const size_t size1, const BYTE *d2, const size_t size2, const char *file); _fds fds; @@ -89,20 +116,32 @@ void fds_init(void) { void fds_quit(void) { if (fds.info.image) { // se richiesto, sovrascrivo il file originale - if ((cfg->fds_write_mode == FDS_WR_ORIGINAL_FILE) && fds.info.writings_occurred) { - fds_from_image(info.rom.file, info.format, fds.info.type); + if (fds.info.writings_occurred) { + uTCHAR file[LENGTH_FILE_NAME_LONG]; + + fds_diff_file_name(&file[0], usizeof(file)); + if (cfg->fds_write_mode == FDS_WR_ORIGINAL_FILE) { + fds_from_image_to_file(info.rom.file, fds.info.format, fds.info.type); + } else { + size_t size = 0; + BYTE *mfds = fds_from_image_to_mem(fds.info.format, fds.info.type, &size); + + if (!mfds || fds_create_ips(fds.info.data, fds.info.total_size, mfds, size, file)) { + log_error(uL("FDS;error on writing diff file")); + } + if (mfds) { + free(mfds); + } + } } free(fds.info.image); } if (fds.info.data) { free(fds.info.data); } - if (fds.info.diff) { - fclose(fds.info.diff); - } fds_init(); } -BYTE fds_load_rom(BYTE type) { +BYTE fds_load_rom(BYTE format) { _rom_mem rom; unsigned int i = 0; @@ -111,7 +150,7 @@ BYTE fds_load_rom(BYTE type) { uTCHAR rom_ext[2][10] = { uL(".fds\0"), uL(".FDS\0") }; FILE *fp = NULL; - if (type == QD_FORMAT) { + if (format == QD_FORMAT) { ustrncpy(&rom_ext[0][0], uL(".qd\0"), usizeof(rom_ext[0])); ustrncpy(&rom_ext[0][0], uL(".QD\0"), usizeof(rom_ext[0])); } @@ -195,16 +234,21 @@ BYTE fds_load_rom(BYTE type) { rom.position = 0; } - info.format = type; + info.format = fds.info.format = format; info.number_of_nes = 1; - fds.info.expcted_side = fds.info.total_sides; + fds.info.expcted_sides = fds.info.total_sides; + fds.info.write_protected = 0x00; + fds.info.cycles_8bit_delay = emu_ms_to_cpu_cycles(FDS_8BIT_MS_DELAY); + fds.info.cycles_dummy_delay = emu_ms_to_cpu_cycles(FDS_OP_SIDE_MS_DELAY); // converto nel mio formato immagine - fds_to_image(); + if (fds_to_image()) { + return (EXIT_ERROR); + } // inserisco il primo fds.info.frame_insert = nes[0].p.ppu.frames; - fds.info.bios_first_run = !cfg->fds_disk1sideA_at_reset; + fds.info.bios.first_run = !cfg->fds_disk1sideA_at_reset; fds_disk_op(cfg->fds_disk1sideA_at_reset ? FDS_DISK_SELECT_AND_INSERT : FDS_DISK_SELECT, 0, FALSE); info.cpu_rw_extern = TRUE; @@ -230,49 +274,54 @@ BYTE fds_load_rom(BYTE type) { return (EXIT_OK); } BYTE fds_load_bios(void) { - uTCHAR bios_file[LENGTH_FILE_NAME_LONG], *lastSlash = NULL; + uTCHAR *lastSlash = NULL; FILE *bios = NULL; // ordine di ricerca: // 1) file specificato dall'utente - usnprintf(bios_file, usizeof(bios_file), uL("" uPs("")), cfg->fds_bios_file); - bios = ufopen(bios_file, uL("rb")); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + usnprintf(fds.info.bios.file, usizeof(fds.info.bios.file), uL("" uPs("")), cfg->fds_bios_file); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } // 2) directory di lavoro - ustrncpy(bios_file, uL("" BIOSFILE), usizeof(bios_file)); - bios = ufopen(bios_file, uL("rb")); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + ustrncpy(fds.info.bios.file, uL("" BIOSFILE), usizeof(fds.info.bios.file)); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } // 3) directory contenente il file fds - ustrncpy(bios_file, info.rom.file, usizeof(bios_file)); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + ustrncpy(fds.info.bios.file, info.rom.file, usizeof(fds.info.bios.file)); // rintraccio l'ultimo '.' nel nome #if defined (_WIN32) - lastSlash = ustrrchr(bios_file, uL('\\')); + lastSlash = ustrrchr(fds.info.bios.file, uL('\\')); if (lastSlash) { (*(lastSlash + 1)) = 0x00; } #else - lastSlash = ustrrchr(bios_file, uL('/')); + lastSlash = ustrrchr(fds.info.bios.file, uL('/')); if (lastSlash) { (*(lastSlash + 1)) = 0x00; } #endif // aggiungo il nome del file - ustrcat(bios_file, uL("" BIOSFILE)); - bios = ufopen(bios_file, uL("rb")); + ustrcat(fds.info.bios.file, uL("" BIOSFILE)); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } // 4) directory puNES/bios - usnprintf(bios_file, usizeof(bios_file), uL("" uPs("") BIOS_FOLDER "/" BIOSFILE), gui_data_folder()); - bios = ufopen(bios_file, uL("rb")); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + usnprintf(fds.info.bios.file, usizeof(fds.info.bios.file), + uL("" uPs("") BIOS_FOLDER "/" BIOSFILE), gui_data_folder()); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } @@ -295,33 +344,50 @@ BYTE fds_load_bios(void) { fclose(bios); + fds.info.bios.crc32 = emu_crc32((void *)prgrom_pnt(), prgrom_size()); + return (EXIT_OK); } void fds_info(void) { - log_info_box_open(uL("sides;")); - if (fds.info.expcted_side != fds.info.total_sides) { - log_close_box(uL("expected %d, finded %d"), fds.info.expcted_side, fds.info.total_sides); + uTCHAR buffer[LENGTH_FILE_NAME_LONG]; + + log_info_open(uL("bios;")); + umemset(buffer, 0x00, usizeof(buffer)); + gui_utf_basename((uTCHAR *)fds.info.bios.file, buffer, usizeof(buffer) - 1); + log_close(uL("" uPs("") ", crc32 : 0x%08X"), buffer, fds.info.bios.crc32); + + log_info_box_open(uL("folder;")); + umemset(buffer, 0x00, usizeof(buffer)); + gui_utf_dirname((uTCHAR *)fds.info.bios.file, buffer, usizeof(buffer) - 1); + log_close_box(uL("" uPs("")), buffer); + + log_info_open(uL("sides;")); + if (fds.info.expcted_sides != fds.info.total_sides) { + log_close(uL("expected %d, finded %d"), fds.info.expcted_sides, fds.info.total_sides); } else { - log_close_box(uL("%d"), fds.info.total_sides); + log_close(uL("%d"), fds.info.total_sides); } for (int side = 0; side < fds.info.total_sides; side++) { _fds_sinfo sinfo = { 0 }; fds_image_sinfo(side, &sinfo); - log_info_box(uL("FDS side %d;prg crc 0x%08X, vrt size %d, files %3d, counted %3d"), side, + log_info_box(uL("FDS side %d;prg crc32 0x%08X, vrt size %d, files %3d, counted %3d"), side, sinfo.crc32prg, fds.info.sides[side].size, sinfo.block2.files, sinfo.counted_files); // controllo se disabilitare l'autoinsert fds_control_autoinsert(&sinfo); +#if !defined (RELEASE) + fds_info_side(side); +#endif } } void fds_info_side(BYTE side) { _fds_sinfo sinfo = { 0 }; fds_image_sinfo(side, &sinfo); - log_info(uL("FDS side %d;disk %d, side %X, name %3s, version %d, vsize %d, prg crc 0x%08X"), side, + log_info(uL("FDS side %d;disk %d, side %X, name %3s, version %d, vsize %d, prg crc32 0x%08X"), side, sinfo.block1.dnumber, sinfo.block1.snumber + 0x0A, sinfo.block1.name, @@ -334,7 +400,7 @@ void fds_info_side(BYTE side) { sinfo.block2.files, sinfo.counted_files); for (uint32_t i = 0; i < sinfo.counted_files; i++) { - log_info_box(uL("file %d;name %8s, size %5d, crc 0x%08X, 0x%04X (b3 : %5d) (b4 : %5d)"), i, + log_info_box(uL("file %d;name %8s, size %5d, crc32 0x%08X, 0x%04X (b3 : %5d) (b4 : %5d)"), i, sinfo.file[i].block3.name, sinfo.file[i].block3.length, sinfo.file[i].block4.crc32, @@ -352,6 +418,8 @@ void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet) { switch (type) { case FDS_DISK_EJECT: fds.drive.disk_ejected = TRUE; + fds.drive.scan = FALSE; + fds.drive.motor_started = FALSE; fds.auto_insert.r4032.frames = 0; fds.auto_insert.r4032.checks = 0; if (!quiet) { @@ -365,11 +433,13 @@ void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet) { } return; } - fds.info.bios_first_run = FALSE; + fds.info.bios.first_run = FALSE; fds.drive.disk_position = 0; - fds.drive.gap_ended = FALSE; + fds.drive.mark_finded = FALSE; fds.drive.disk_ejected = FALSE; - fds.drive.at_least_one_scan = FALSE; + fds.drive.scan = FALSE; + fds.drive.motor_started = FALSE; + fds.drive.delay_insert = 32768; fds.auto_insert.r4032.frames = 0; fds.auto_insert.r4032.checks = 0; if (!quiet) { @@ -415,90 +485,14 @@ void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet) { break; } } -void fds_diff_op(BYTE side, BYTE mode, uint32_t position, WORD value) { - if (!fds.info.diff) { - uTCHAR file[LENGTH_FILE_NAME_LONG]; - uint32_t version = 0; - - fds_diff_file_name(&file[0], usizeof(file)); - fds.info.diff = ufopen(file, uL("r+b")); - if ((mode == FDS_OP_WRITE) && !fds.info.diff) { - // creo il file - fds.info.diff = ufopen(file, uL("a+b")); - if (fds.info.diff) { - // lo chiudo - fclose(fds.info.diff); - // lo riapro in modalita' rb+ - fds.info.diff = ufopen(file, uL("r+b")); - } - } - if (fds.info.diff) { - // leggo la versione del file - if ((fread(&version, sizeof(uint32_t), 1, fds.info.diff) < 1) || (version < DIFFVERSION)) { - fclose(fds.info.diff); - fds.info.diff = ufopen(file, uL("w+b")); - } - } - } - - if (!fds.info.diff) { - return; - } - - rewind(fds.info.diff); - fds.info.writings_occurred = TRUE; - - if (mode == FDS_OP_WRITE) { - _fds_diff_ele in = { 0 }, out = { 0 }; - uint32_t version = DIFFVERSION; - - // salvo la versione - if (fwrite(&version, sizeof(uint32_t), 1, fds.info.diff) < 1) { - fprintf(stderr, "error on write version fds diff file\n"); - } - // senza questo in windows non funziona correttamente - fflush(fds.info.diff); - - out.side = side; - out.position = position; - out.value = value; - - while (fread(&in, sizeof(_fds_diff_ele), 1, fds.info.diff)) { - if ((in.position == out.position) && (in.side == out.side)) { - fseek(fds.info.diff, ftell(fds.info.diff) - (long)sizeof(_fds_diff_ele), SEEK_SET); - break; - } - } - - if (fwrite(&out, sizeof(_fds_diff_ele), 1, fds.info.diff) < 1) { - log_error(uL("FDS;error on writing diff file")); - } - // senza questo in windows non funziona correttamente - fflush(fds.info.diff); - } else if (mode == FDS_OP_READ) { - WORD *dst = &fds.info.image[side * fds_image_side_size()]; - _fds_diff_ele ele = { 0 }; - uint32_t version = 0; - - // leggo la versione del file - if (fread(&version, sizeof(uint32_t), 1, fds.info.diff) < 1) { - log_error(uL("FDS;error on reading version diff file")); - } - - while (fread(&ele, sizeof(_fds_diff_ele), 1, fds.info.diff)) { - if (ele.side == side) { - dst[ele.position] = ele.value; - } - } - - fclose(fds.info.diff); - fds.info.diff = NULL; - } -} -BYTE fds_from_image(uTCHAR *file, BYTE format, BYTE type) { - uint32_t flength = 0; +BYTE fds_from_image_to_file(uTCHAR *file, BYTE format, BYTE type) { + size_t size = 0; + BYTE *mfds = fds_from_image_to_mem(format, type, &size); FILE *fp = NULL; + if (!mfds) { + return (EXIT_ERROR); + } // creo il file fp = ufopen(file, uL("a+b")); if (fp) { @@ -507,92 +501,19 @@ BYTE fds_from_image(uTCHAR *file, BYTE format, BYTE type) { // lo riapro in modalita' rb+ fp = ufopen(file, uL("w+b")); } else { + free(mfds); return (EXIT_ERROR); } - - if (type == FDS_FORMAT_FDS) { - fwrite("FDS", 3, 1, fp); - fputc(0x1A, fp); - fputc(fds.info.total_sides, fp); - for (int i = 0; i < 11; i++) { - fputc(0, fp); - } - } - - for (BYTE side = 0; side < fds.info.total_sides; side++) { - _fds_info_side *is = &fds.info.sides[side]; - const WORD *src = is->data; - uint32_t position = 0, size = 0; - WORD crc = 0; - - for (position = 0; position < fds_disk_side_size_format(format);) { - WORD block = src[position]; - uint32_t blength = 0; - BYTE stop = FALSE; - - switch (block) { - case BL_DISK_INFO: - // le info sul disco - blength = 56; - crc = fds_block_crc(&src[position], blength); - break; - case BL_FILE_AMOUNT: - // il numero dei file immagazzinati nel disco - blength = 2; - crc = fds_block_crc(&src[position], blength); - break; - case BL_FILE_HEADER: - // l'header del file - flength = (src[position + 14] << 8) | src[position + 13]; - blength = 16; - crc = fds_block_crc(&src[position], blength); - break; - case BL_FILE_DATA: - // il contenuto del file - blength = flength + 1; - crc = fds_block_crc(&src[position], blength); - break; - case FDS_DISK_GAP: - case FDS_DISK_BLOCK_MARK: - case FDS_DISK_CRC_CHAR1: - case FDS_DISK_CRC_CHAR2: - position++; - continue; - default: - stop = TRUE; - break; - } - - if (stop) { - break; - } - - if (block) { - for (unsigned int i = 0; i < blength; i++) { - fputc(src[position] & 0xFF, fp); - position++; - size++; - } - if (format == QD_FORMAT) { - fputc(((crc & 0x00FF) >> 0), fp); - fputc(((crc & 0xFF00) >> 8), fp); - size += 2; - } - } - } - while (size < fds_disk_side_size_format(format)) { - fputc(0x00, fp); - size++; - } - } + if (fwrite(mfds, size, 1, fp) < 1) { + fclose(fp); + free(mfds); + return (EXIT_ERROR); + }; fclose(fp); + free(mfds); { uTCHAR diff[LENGTH_FILE_NAME_LONG]; - if (fds.info.diff) { - fclose(fds.info.diff); - fds.info.diff = NULL; - } fds_diff_file_name(&diff[0], usizeof(diff)); uremove(diff); } @@ -611,48 +532,90 @@ BYTE fds_image_to_file(uTCHAR *file) { } else { return (EXIT_ERROR); } - fwrite(fds.info.image, fds.info.total_sides * fds_image_side_bytes(), 1, fp); + fwrite(fds.info.image, fds.info.total_sides * fds_image_side_size(), 1, fp); fclose(fp); return (EXIT_OK); } +WORD fds_crc_byte(WORD base, BYTE data) { + for (unsigned i = 0; i < 8; i++) { + BYTE bit = (data >> i) & 0x01; + BYTE carry = base & 0x01; + + base = (base >> 1) | (bit << 15); + if (carry) { + base ^= 0x8408; + } + } + return (base); +} uint32_t fds_disk_side_size_format(BYTE format) { return (format == QD_FORMAT ? DISK_QD_SIDE_SIZE : DISK_FDS_SIDE_SIZE); } uint32_t fds_disk_side_size(void) { - return (fds_disk_side_size_format(info.format)); + return (fds_disk_side_size_format(fds.info.format)); } uint32_t fds_image_side_size(void) { - return (fds_disk_side_size() + FDS_GAP_END); -} -uint32_t fds_image_side_bytes(void) { - return (fds_image_side_size() * sizeof(WORD)); + return (FDS_IMAGE_SIDE_SIZE); } -void fds_to_image(void) { - uint32_t flength = 0; +BYTE fds_to_image(void) { + BYTE *mfds = NULL, *pointer = fds.info.data; if (fds.info.image) { free(fds.info.image); fds.info.image = NULL; } + // applico la diff + { + uTCHAR file[LENGTH_FILE_NAME_LONG]; + + fds_diff_file_name(&file[0], usizeof(file)); + if (emu_file_exist(file) == EXIT_OK) { + mfds = malloc(fds.info.total_size); + if (mfds) { + memcpy(mfds, fds.info.data, fds.info.total_size); + mfds = fds_read_ips(mfds, fds.info.total_size, file); + } + if (!mfds) { + log_error(uL("FDS;error on reading diff file")); + } else { + pointer = mfds; + fds.info.writings_occurred = TRUE; + } + } + } + for (BYTE side = 0; side < fds.info.total_sides; side++) { uint32_t position = 0, size = 0; + _fds_info_block fib = { 0 }; _fds_info_side *is = NULL; const BYTE *src = NULL; - WORD *dst = NULL; - BYTE bl1 = FALSE; + BYTE *dst = NULL; + uint32_t header = 0; if (fds.info.type == FDS_FORMAT_FDS) { - position = (side * fds_disk_side_size()) + 16; - } else { - position = side * fds_disk_side_size(); + header = 16; } - fds.info.image = realloc((void * )fds.info.image, (side + 1) * fds_image_side_bytes()); - src = &fds.info.data[position]; + fds.info.protection.autodetect = TRUE; + fds.info.protection.magic_card_trainer = FALSE; + fds.info.protection.quick_hunter = FALSE; + fds.info.protection.ouji = FALSE; + fds.info.protection.kgk = FALSE; + position = side * fds_disk_side_size() + header; + + // "Bishoujo Mahjong Club (Japan) (Unl).fds" + // "Bodycon Quest I - Girls Exposed (Japan) (Unl) [T-En by DvD Translations Rev A] [n].fds" + // "Dead Zone (Japan) [T-En by Stardust Crusaders v1.00].fds" + // "Game no Tatsujin - Money Wars (Japan) (Unl).fds" + // "Golf, The - Bishoujo Classic (Japan) (Unl).fds" + // "Otocky (Japan) (Beta) (1986-04-15).fds" + // la dimensione dell'immeagine finale e' superiore a 65500 + fds.info.image = realloc((void *)fds.info.image, (side + 1) * fds_image_side_size()); + src = &pointer[position]; dst = &fds.info.image[side * fds_image_side_size()]; - memset(dst, 0x00, fds_image_side_bytes()); + memset(dst, FDS_DISK_GAP, fds_image_side_size()); is = &fds.info.sides[side]; position = 0; @@ -660,34 +623,15 @@ void fds_to_image(void) { size += FDS_GAP_START; for (position = 0; position < fds_disk_side_size();) { - BYTE block = src[position], stop = FALSE; - uint32_t blength = 1; - - switch (block) { - case BL_DISK_INFO: - // le info sul disco - blength = 56; - bl1 = TRUE; - break; - case BL_FILE_AMOUNT: - // il numero dei file immagazzinati nel disco - blength = 2; - break; - case BL_FILE_HEADER: - // l'header del file - flength = (src[position + 14] << 8) | src[position + 13]; - blength = 16; - break; - case BL_FILE_DATA: - // il contenuto del file - blength = flength + 1; - break; - default: - // nel caso il disco sia "sporco" - stop = TRUE; - break; + fds_examine_block(src, position, &fib); + fds.info.protection.magic_card_trainer = fib.magic_card_trainer; + fds.info.protection.quick_hunter = fib.quick_hunter; + fds.info.protection.ouji = fib.ouji; + fds.info.protection.kgk = fib.kgk; + + if (fds.info.protection.quick_hunter | fds.info.protection.kgk | fds.info.protection.ouji) { + fds.info.write_protected = 0x04; } - // in "Tobidase Daisakusen (1987)(Square)(J).fds" esiste un file nascosto // esattamente dopo l'ultimo file "riconosciuto" dal file system. // Il vecchio controllo che facevo per riconoscere i dischi "sporchi" @@ -698,73 +642,249 @@ void fds_to_image(void) { // blocchi riconosciuti allora considero l'analisi del disco completa e // tralascio tutto quello che sta dopo (in questo modo funziona anche // "Akumajou Dracula v1.02 (1986)(Konami)(J).fds" il cui disco e' "sporco"). - if (stop) { + if (fib.stop) { + // gestione trainer e protezioni (blocco aggiuntivo) + if (fds.info.protection.magic_card_trainer || fds.info.protection.kgk || fds.info.protection.quick_hunter) { + BYTE btype = fds.info.protection.magic_card_trainer + ? 0x05 + : fds.info.protection.kgk ? 0x12 : fds.info.protection.quick_hunter ? 0x00 : 0x00; + uint32_t cpos = 0, cblength = 0; + WORD crc = 0; + + fib.blength = fds.info.protection.magic_card_trainer + ? 0x200 + : fds.info.protection.kgk ? 0x500 : fds.info.protection.quick_hunter ? 0x3000 : 0; + // indico l'inizio del blocco + fds_image_memset(&dst[size], FDS_DISK_BLOCK_MARK, 1); + size += 1; + // inizilizzo le variabili per il calcolo del crc + cpos = size; + cblength = fib.blength; + // nell'fds originale manca il tipo di blocco (0x00) + if (fds.info.protection.quick_hunter) { + fds_image_memset(&dst[size], btype, 1); + size += 1; + cblength += 1; + } + // copio il blocco + fds_image_memcpy(&src[position], &dst[size], fib.blength); + size += fib.blength; + // crc + crc = fds_crc_block(&dst[cpos], cblength); + fds_image_memset(&dst[size], (crc >> 0), 1); + size += 1; + fds_image_memset(&dst[size], (crc >> 8), 1); + size += 1; + } break; } - if (block) { + if (src[position]) { + uint32_t cpos = 0, cblength = 0; + WORD crc = 0; + // indico l'inizio del blocco fds_image_memset(&dst[size], FDS_DISK_BLOCK_MARK, 1); size += 1; - // copio i dati - fds_image_memcpy(&src[position], &dst[size], blength); - size += blength; - // dummy CRC - fds_image_memset(&dst[size], FDS_DISK_CRC_CHAR1, 1); + // inizilizzo le variabili per il calcolo del crc + cpos = size; + cblength = fib.blength; + // copio il blocco + fds_image_memcpy(&src[position], &dst[size], fib.blength); + size += fib.blength; + // crc + crc = fds_crc_block(&dst[cpos], cblength); + fds_image_memset(&dst[size], (crc >> 0), 1); size += 1; - fds_image_memset(&dst[size], FDS_DISK_CRC_CHAR2, 1); + fds_image_memset(&dst[size], (crc >> 8), 1); size += 1; - // 1016 bit di gap alla fine di ogni blocco. - // Note : con 976 funziona correttamente la read del disco ma non e' - // sufficiente per la write. + // inizializzo il blocco di gap fds_image_memset(&dst[size], FDS_DISK_GAP, FDS_GAP_BLOCK); size += FDS_GAP_BLOCK; + + // in caso di write di un file questo altro blocco di gap serve per ottenere il + // il giusto posizionamento (nell'immagine) al blocco dei dati del file (0x04): + // - scrittura blocco header (0x03) + // - scrittura crc + // - il bios scrive altri 4 byte (0x00) dopo il crc + // - scrive 121 byte di gap + // - scrive il blocco dei dati (0x04) + // - scrittura crc + // - il bios scrive altri 4 byte (0x00) dopo il crc + // - scrive 121 byte di gap + if (src[position] == BL_FILE_HEADER) { + fds_image_memset(&dst[size], FDS_DISK_GAP, FDS_GAP_FILE_BLOCK); + size += FDS_GAP_FILE_BLOCK; + } } - position += (blength + (info.format == QD_FORMAT ? 2 : 0)); + position += (fib.blength + (fds.info.format == QD_FORMAT ? 2 : 0)); } - if (!bl1) { + if (!fib.bl1) { fds.info.total_sides = side; if (!fds.info.total_sides) { free(fds.info.image); fds.info.image = NULL; } else { - fds.info.image = realloc((void * )fds.info.image, fds.info.total_sides * fds_image_side_bytes()); + fds.info.image = realloc((void *)fds.info.image, fds.info.total_sides * fds_image_side_size()); } continue; } is->side = side; - is->last_position = size; - if (size < fds_disk_side_size()) { - fds_image_memset(&dst[size], 0x0000, fds_disk_side_size() - size); - size += (fds_disk_side_size() - size); + if (size < fds_image_side_size()) { + fds_image_memset(&dst[size], FDS_DISK_GAP, fds_image_side_size() - size); + size += (fds_image_side_size() - size); } - fds_image_memset(&dst[size], FDS_DISK_GAP, FDS_GAP_END); - size += FDS_GAP_END; - is->size = size; - fds_diff_op(side, FDS_OP_READ, 0, 0); } // ultimo passaggio for (BYTE side = 0; side < fds.info.total_sides; side++) { fds.info.sides[side].data = &fds.info.image[side * fds_image_side_size()]; } + if (mfds) { + free(mfds); + } + return (EXIT_OK); +} +BYTE fds_examine_block(const BYTE *src, uint32_t position, _fds_info_block *fib) { + switch (src[position]) { + case BL_DISK_INFO: + // informazioni sul disco + // "Jingorou (Japan) (Unl).fds" e "Graphic Editor Hokusai - Ver 1.2 (Japan) (Unl).fds" + fib->ouji = fds.info.protection.autodetect && !strncmp((void *)&src[position + 0x10], "OUJI", 4); + fib->kgk = fds.info.protection.autodetect && !strncmp((void *)&src[position + 0x10], "KGK ", 4); + fib->magic_card_trainer = FALSE; + fib->count2000 = 0; + fib->blength = 56; + fib->bl1 = TRUE; + return (TRUE); + case BL_FILE_AMOUNT: + // il numero dei file nel disco + fib->files = src[position + 1]; + fib->blength = 2; + return (TRUE); + case BL_FILE_HEADER: + // header del file + fib->faddress = (src[position + 0x0C] << 8) | src[position + 0x0B]; + fib->flength = (src[position + 0x0E] << 8) | src[position + 0x0D]; + fib->ftype = src[position + 0x0F] & 0x03; + if (fib->flength_new && fib->files) { + fib->flength = fib->ftype != 0 ? (fib->flength_ppu ? fib->flength_ppu : fib->flength) : fib->flength_cpu; + } else if (fib->ouji && (fib->ftype == 0) && (fib->faddress == 0x2000) && (fib->flength == 1) && + (++fib->count2000 >= 3)) { + fib->flength = fib->files ? 0xC000 : 0xE000; + } + if (fib->files) { + fib->files--; + } + // "Quick Hunter (Japan) (Unl).fds" + if (!fib->files && (fib->faddress == 0x2000) && (fib->flength == 0x900) && (fib->ftype == 0) && + !strncmp((void *)&src[position + 0x03], "KIYONO.", 7)) { + fib->quick_hunter = TRUE; + } + fib->blength = 16; + return (TRUE); + case BL_FILE_DATA: + // il contenuto del file + if (fds.info.protection.autodetect && (fib->ftype == 0) && (fib->faddress == 0x4FFF) && (fib->flength == 8)) { + fib->flength_new = src[position + 0x01] & 0x80; + fib->magic_card_trainer = (src[position + 0x01] != 0xFE) && ((src[position + 0x01] & 0xC0) == 0xC0) && + (src[position + 0x08] == 0x00); + fib->flength_cpu = 0x8000; + switch (src[position + 0x02] >> 5) { + case 4: + case 5: + fib->flength_ppu = 32768; + break; + case 6: + fib->flength_ppu = 16384; + break; + case 7: + fib->flength_ppu = 8192; + break; + default: + fib->flength_ppu = 0; + break; + } + } + fib->blength = fib->flength + 1; + return (TRUE); + default: + fib->blength = 0; + fib->stop = TRUE; + return (FALSE); + } } void fds_control_autoinsert(_fds_sinfo *sinfo) { - // NB : il crc32prg da controllare deve essere sempre quello - // del disco dove non vengono scritti i dati di salvataggio - if (!strncmp((char *)&sinfo->block1.name[0], "GAL", 3) && (sinfo->crc32prg == 0xC5D1EC5D)) { - // Gall Force - Eternal Story (Japan) - disk 0 side A + _fds_control_autoinsert images[] = { + // --- auto insert disabilitato + // Gall Force - Eternal Story (Japan) + { "GAL", { 0x1E9969AC, 0xED380FA3, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + // Koneko Monogatari - The Adventures of Chatran (Japan) + { "KOM", { 0xE9A457BF, 0x7791A2DD, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + //Koneko Monogatari - The Adventures of Chatran (Japan) (Sample) (1986-07-03) + { "KOM", { 0x8C457E04, 0xCB25E05C, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + // Samurai Sword (Japan) + { "SMU", { 0xD0B342F5, 0xE46A6E3E, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + // Quick Hunter (Japan) (Unl) + { "OUJ", { 0x2AD12F7F, 0xBFA73716, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + + // --- auto insert r4032 disabilitato e auto insert end_of_head abilitato + // Egger Land (Japan) + { "EGL", { 0x8EE0B051, 0x71119427, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Eggerland (1987)(Hal Laboratory)(J)[tr En] + { "EGL", { 0xCE21494E, 0x5FD1F4EB, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Egger Land (Japan) (Rev 1) (Possible Proto) + { "EGL", { 0xBB887262, 0xC9915867, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Igo - Kyuu Roban Taikyoku (Japan) + { "IGO", { 0x1E8B0151, 0xF7130E20, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Puzzle Boys (Japan) (Disk Writer) + { "PUZ", { 0x2385D83D, 0x64488AFD, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Time Twist - Rekishi no Katasumi de (1991)(Nintendo)(J) + { "TT1", { 0x6D6014C1, 0x145A90B2, 0xBFB019B9, 0x636083C1 }, FALSE, TRUE, FALSE }, + }; + + fds.auto_insert.r4032.disabled = FALSE; + fds.auto_insert.end_of_head.disabled = TRUE; + + if (fds.info.total_sides == 1) { + // auto insert disabilitato + fds.auto_insert.disabled = TRUE; + gui_overlay_info_append_msg_precompiled(40, NULL); + return; + } + if (fds.info.protection.quick_hunter) { + // auto insert disabilitato fds.auto_insert.disabled = TRUE; gui_overlay_info_append_msg_precompiled(39, NULL); - } else if (!strncmp((char *)&sinfo->block1.name[0], "KOM", 3) && - ((sinfo->crc32prg == 0x2B24787F) || (sinfo->crc32prg == 0x14F219C5))) { - // Koneko Monogatari - The Adventures of Chatran (Japan) - disk 0 side A oppure disk 0 side B + return; + } + if (ustrstr(info.rom.file, uL(" Zenpen ")) || ustrstr(info.rom.file, uL(" Kouhen "))) { + // auto insert disabilitato fds.auto_insert.disabled = TRUE; gui_overlay_info_append_msg_precompiled(39, NULL); + return; + } + for (unsigned int i = 0; i < LENGTH(images); i++) { + _fds_control_autoinsert *fca = &images[i]; + + if (!strncmp((char *)&sinfo->block1.name[0], &fca->name[0], 3) && + ((fca->crc32prg[0] && (fca->crc32prg[0] == sinfo->crc32prg)) || + (fca->crc32prg[1] && (fca->crc32prg[1] == sinfo->crc32prg)) || + (fca->crc32prg[2] && (fca->crc32prg[2] == sinfo->crc32prg)) || + (fca->crc32prg[3] && (fca->crc32prg[3] == sinfo->crc32prg)))) { + if (fca->disable_autoinsert) { + fds.auto_insert.disabled = TRUE; + gui_overlay_info_append_msg_precompiled(39, NULL); + continue; + } + fds.auto_insert.r4032.disabled = fca->disable_r4032; + fds.auto_insert.end_of_head.disabled = fca->disable_end_of_head; + break; + } } } void fds_diff_file_name(uTCHAR *dst, size_t lenght) { @@ -773,7 +893,7 @@ void fds_diff_file_name(uTCHAR *dst, size_t lenght) { umemset(dst, 0x00, lenght); gui_utf_basename(info.rom.file, basename, usizeof(basename)); usnprintf(dst, lenght, uL("" uPs("") DIFF_FOLDER "/" uPs("")), gui_data_folder(), basename); - usnprintf(ext, usizeof(ext), uL("" uPs("")), (info.format == QD_FORMAT ? uL(".diq") : uL(".dif"))); + usnprintf(ext, usizeof(ext), uL("" uPs("")), (fds.info.format == QD_FORMAT ? uL(".ipq") : uL(".ipf"))); // rintraccio l'ultimo '.' nel nome last_dot = ustrrchr(dst, uL('.')); @@ -787,12 +907,12 @@ void fds_diff_file_name(uTCHAR *dst, size_t lenght) { void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { _fds_info_side *is = &fds.info.sides[side]; uint32_t size = 0, position = 0, flength = 0; - const WORD *src = is->data; + const BYTE *src = is->data; memset(sinfo, 0x00, sizeof(_fds_sinfo)); for (position = 0; position < fds_disk_side_size();) { - WORD block = src[position]; + BYTE block = src[position]; uint32_t blength = 1; BYTE stop = FALSE; @@ -807,7 +927,7 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { break; case BL_FILE_HEADER: // l'header del file - flength = (src[position + 14] << 8) | src[position + 13]; + flength = (src[position + 0x0E] << 8) | src[position + 0x0D]; blength = 16; break; case BL_FILE_DATA: @@ -816,15 +936,10 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { break; case FDS_DISK_GAP: case FDS_DISK_BLOCK_MARK: - case FDS_DISK_CRC_CHAR1: - case FDS_DISK_CRC_CHAR2: size++; position++; continue; default: - if (is->last_position < position) { - is->last_position = position; - } stop = TRUE; break; } @@ -840,8 +955,7 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { case 1: sinfo->block1.position = size; // name - fds_image_memcpy_ASCII(&src[position + 0x10], &sinfo->block1.name[0], - sizeof(sinfo->block1.name)); + fds_image_memcpy_ASCII(&src[position + 0x10], &sinfo->block1.name[0], sizeof(sinfo->block1.name)); // game version sinfo->block1.gversion = src[position + 0x14]; // side number @@ -862,12 +976,12 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { sizeof(sinfo->file[file].block3.name)); // type sinfo->file[file].block3.type = src[position + 0x0F]; - sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength * sizeof(WORD), sinfo->crc32prg); + sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength, sinfo->crc32prg); break; case 4: sinfo->file[file].block4.position = size; - sinfo->file[file].block4.crc32 = emu_crc32((void *)&src[position + 1], flength * sizeof(WORD)); - sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength * sizeof(WORD), sinfo->crc32prg); + sinfo->file[file].block4.crc32 = emu_crc32((void *)&src[position + 1], flength); + sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength, sinfo->crc32prg); sinfo->counted_files++; break; default: @@ -875,20 +989,23 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { } size += blength; position += blength; + // crc + size += 2; + position += 2; } } } -void fds_image_memset(WORD *dst, WORD value, uint32_t lenght) { +void fds_image_memset(BYTE *dst, BYTE value, uint32_t lenght) { for (uint32_t i = 0; i < lenght; i++) { (*dst++) = value; } } -void fds_image_memcpy(const BYTE *src, WORD *dst, uint32_t lenght) { +void fds_image_memcpy(const BYTE *src, BYTE *dst, uint32_t lenght) { for (uint32_t i = 0; i < lenght; i++) { (*dst++) = (*src++); } } -void fds_image_memcpy_ASCII(const WORD *src, BYTE *dst, size_t lenght) { +void fds_image_memcpy_ASCII(const BYTE *src, BYTE *dst, size_t lenght) { memset(dst, 0x00, lenght); for (size_t i = 0; i < (lenght - 1); i++) { BYTE ch = src[i] & 0xFF; @@ -896,25 +1013,304 @@ void fds_image_memcpy_ASCII(const WORD *src, BYTE *dst, size_t lenght) { dst[i] = ((ch >= 0x20) && (ch <= 0x7E)) ? ch : 0x20; } } -WORD fds_block_crc(const WORD *src, uint32_t lenght) { +WORD fds_crc_block(const BYTE *src, uint32_t lenght) { // Do not include any existing checksum, not even the blank checksums 00 00 or FF FF. // The formula will automatically count 2 0x00 bytes without the programmer adding them manually. // Also, do not include the gap terminator (0x80) in the data. // If you wish to do so, change sum to 0x0000. - WORD sum = 0x8000; + WORD crc = 0x8000; + + for (uint32_t i = 0; i < (lenght + 2); i++) { + crc = fds_crc_byte(crc, (i < lenght ? src[i]: 0x00)); + } + return (crc); +} +BYTE *fds_from_image_to_mem(BYTE format, BYTE type, size_t *size) { + BYTE *mfds = NULL; + + (*size) = (type == FDS_FORMAT_FDS ? 16 : 0) + fds_disk_side_size_format(format) * fds.info.total_sides; + + // alloco la zona di memoria + mfds = malloc((*size)); + if (!mfds) { + (*size) = 0; + return (mfds); + } + memset(mfds, 0x00, (*size)); + + if (type == FDS_FORMAT_FDS) { + memcpy(&mfds[0], "FDS", 3); + mfds[3] = 0x1A; + mfds[4] = fds.info.total_sides; + } + + for (BYTE side = 0; side < fds.info.total_sides; side++) { + uint32_t length = 0, position = 0, total_size = 0; + _fds_info_side *is = &fds.info.sides[side]; + const BYTE *src = is->data; + _fds_info_block fib = { 0 }; + WORD crc = 0; + + length = (type == FDS_FORMAT_FDS ? 16 : 0) + (fds_disk_side_size_format(format) * side); + + for (position = 0; position < fds_image_side_size();) { + if ((src[position] == FDS_DISK_GAP) || (src[position] == FDS_DISK_BLOCK_MARK)) { + position++; + continue; + } + if (fds_examine_block(src, position, &fib)) { + crc = fds_crc_block(&src[position], fib.blength); + } else { + break; + } + + if (src[position]) { + if ((total_size + fib.blength + (format == QD_FORMAT ? 2 : 0)) < fds_disk_side_size_format(format)) { + // troncato + for (unsigned int i = 0; i < fib.blength; i++) { + mfds[length] = src[position]; + length++; + position++; + total_size++; + } + if (format == QD_FORMAT) { + mfds[length] = (crc >> 0); + mfds[length + 1] = (crc >> 8); + length += 2; + total_size += 2; + } + position += 2; + } else { + break; + } + } + } + } + return (mfds); +} +BYTE *fds_read_ips(BYTE *data, size_t size, const char *file) { + size_t fsize = 0, fpos = 0, mpos = 0; + FILE *fp = fopen(file, "rb"); + uint32_t counter = 0; + BYTE rc = EXIT_OK; + char header[5]; + + if (!fp || !data) { + return (NULL); + } + + fseek(fp, 0L, SEEK_END); + fsize = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + if (fsize >= 0x1000000) { + fclose(fp); + free(data); + return (NULL); + } + if ((fread(&header[0], 1, sizeof(header), fp) < sizeof(header)) || strncmp((void *)header, "PATCH", 5)) { + fclose(fp); + free(data); + return (NULL); + } + fpos += sizeof(header); + + // Itera attraverso le patch + while ((fpos < fsize) && (mpos < size)) { + size_t offset = 0, length = 0; + BYTE address[3], len[2]; + BYTE rle = FALSE, ch = 0; + + // Leggo l'offset dalla patch corrente + if (fread(&address[0], 1, sizeof(address), fp) < sizeof(address)) { + rc = EXIT_ERROR; + break; + } + fpos += sizeof(address); + offset = (address[0] << 16) | (address[1] << 8) | address[2]; + + // EOF + if (offset == 0x454F46) { + break; + } - for (uint32_t byte_index = 0; byte_index < (lenght + 2); byte_index++) { - BYTE byte = byte_index < lenght ? src[byte_index] & 0x00FF: 0x00; + // Leggo la lunghezza dalla patch corrente + if (fread(&len[0], 1, sizeof(len), fp) < sizeof(len)) { + rc = EXIT_ERROR; + break; + } + fpos += sizeof(len); + length = (len[0] << 8) | len[1]; - for(unsigned bit_index = 0; bit_index < 8; bit_index++) { - BYTE bit = (byte >> bit_index) & 0x01; - BYTE carry = sum & 0x01; + // Se la lunghezza e' 0, e' un blocco RLE + if (!length) { + rle = TRUE; + + // Leggo la lunghezza RLE a 16 bit + if (fread(&len[0], 1, sizeof(len), fp) < sizeof(len)) { + rc = EXIT_ERROR; + break; + } + fpos += sizeof(len); + length = (len[0] << 8) | len[1]; - sum = (sum >> 1) | (bit << 15); - if (carry) { - sum ^= 0x8408; + // Leggo il byte da ripetere + if (fread(&ch, 1, sizeof(ch), fp) < sizeof(ch)) { + rc = EXIT_ERROR; + break; } + fpos += sizeof(ch); } + + if ((offset + length) >= size) { + size_t old_size = size; + BYTE *new_blk = NULL; + + size = (offset + length); + new_blk = (BYTE *)realloc(data, size); + if (!new_blk) { + rc = EXIT_ERROR; + break; + } + memset(new_blk + old_size, 0x00, size - old_size); + data = new_blk; + } + + if (rle) { + // Applico la ripetizione dei dati in memoria + for (size_t i = 0; i < length; ++i) { + data[offset + i] = ch; + } + } else { + // Leggo i dati da applicare dalla patch + if (fread(&data[offset], 1, length, fp) < length) { + rc = EXIT_ERROR; + break; + } + fpos += length; + } + counter++; + mpos = offset + length; + } + fclose(fp); + if ((rc == EXIT_ERROR) || !counter) { + free(data); + data = NULL; + } + return (data); +} +BYTE fds_create_ips(const BYTE *d1, const size_t size1, const BYTE *d2, const size_t size2, const char *file) { + FILE *fp = fopen(file, "w+b"); + uint32_t counter = 0; + size_t i = 0, fsize = 0; + + if (!d1 || !d2 || !fp) { + return (EXIT_ERROR); } - return (sum); + fwrite("PATCH", 5, 1, fp); + fsize += 5; + + while ((i < size1) || (i < size2)) { + size_t length = 0; + BYTE rle = TRUE; + + if (i < size1) { + if (d1[i] == d2[i]) { + i++; + continue; + } + while (((i + length) < size1) && ((i + length) < size2) && (length < 65535)) { + if (d1[i + length] == d2[i + length]) { + break; + } + if (!rle && (length >= 3) && ((length + 3) < size1)) { + if ((d1[i + length + 0] != d2[i + length + 0]) && + (d1[i + length + 1] != d2[i + length + 1]) && + (d1[i + length + 2] != d2[i + length + 2]) && + (d2[i + length + 0] == d2[i + length + 1]) && + (d2[i + length + 1] == d2[i + length + 2])) { + break; + } + } + if (rle && (length >= 1) && (d2[i + length] != d2[i])) { + if (length < 3) { + rle = FALSE; + continue; + } + break; + } + length++; + } + } + + if (!length) { + while (((i + length) < size2) && (length < 65535)) { + if (!rle && (length >= 3) && ((length + 3) < size2)) { + if ((d2[i + length + 0] == d2[i + length + 1]) && + (d2[i + length + 1] == d2[i + length + 2])) { + break; + } + } + if (rle && (length >= 1) && (d2[i + length] != d2[i])) { + if (length < 3) { + rle = FALSE; + continue; + } + break; + } + length++; + } + } + + if (length == 1) { + rle = FALSE; + } + + { + // Big Endian + BYTE address[3] = { i >> 16, i >> 8, i & 0xFF }; + BYTE len[2] = { length >> 8, length & 0xFF }; + BYTE rlen[2] = { 0x00, 0x00 }; + + if (rle) { + rlen[0] = len[0]; + rlen[1] = len[1]; + len[0] = 0x00; + len[1] = 0x00; + } + // Scrivo le istruzioni di patching nel file IPS + fputc(address[0], fp); + fputc(address[1], fp); + fputc(address[2], fp); + fputc(len[0], fp); + fputc(len[1], fp); + fsize += 5; + if (rle) { + fputc(rlen[0], fp); + fputc(rlen[1], fp); + fputc(d2[i], fp); + fsize += 3; + } else { + fwrite(&d2[i], 1, length, fp); + fsize += length; + } + + // forzo la scrittura del file + fflush(fp); + + if (fsize >= 0x1000000) { + log_error(uL("ips;error on writing file, too large (max 16MB)")); + break; + } + } + + counter++; + i += length; + } + fclose(fp); + + if (!counter) { + remove(file); + } + return (EXIT_OK); } diff --git a/src/core/fds.h b/src/core/fds.h index 3e6f46856..4e05dcd38 100644 --- a/src/core/fds.h +++ b/src/core/fds.h @@ -19,7 +19,6 @@ #ifndef FDS_H_ #define FDS_H_ -#include #include "common.h" enum fds_formats { FDS_FORMAT_RAW, FDS_FORMAT_FDS }; @@ -28,65 +27,76 @@ enum fds_operations { FDS_OP_NONE, FDS_OP_READ, FDS_OP_WRITE }; enum fds_disk_operations { FDS_DISK_INSERT, FDS_DISK_EJECT, - // e' importante che tutte le modalita' - // SELECT siano dopo la FDS_DISK_SELECT. + // e' importante che tutte le modalita' SELECT siano dopo la FDS_DISK_SELECT. FDS_DISK_SELECT, FDS_DISK_SELECT_AND_INSERT, FDS_DISK_SELECT_FROM_REWIND }; enum fds_gaps { FDS_GAP_START = 28300 / 8, - // 1016 bit di gap alla fine di ogni blocco. - // Note : con 976 funziona correttamente la read del disco ma non e' - // sufficiente per la write. - //FDS_GAP_BLOCK = 1016 / 8, //976 / 8, FDS_GAP_BLOCK = 976 / 8, - FDS_GAP_END = 0 + FDS_GAP_FILE_BLOCK = 32 / 8 }; enum fds_block_type { BL_DISK_INFO = 1, BL_FILE_AMOUNT, BL_FILE_HEADER, BL_FILE_DATA, - DISK_FDS_SIDE_SIZE = 65500, - DISK_QD_SIDE_SIZE = 65536 }; enum fds_misc { - //FDS_8BIT_DELAY = 149, //20 * 8, - FDS_8BIT_DELAY = 22 * 8, - FDS_DISK_GAP = 0x0100, - FDS_DISK_BLOCK_MARK = 0x0180, - FDS_DISK_CRC_CHAR1 = 0x0155, - FDS_DISK_CRC_CHAR2 = 0x01AA, - FDS_OP_SIDE_DELAY = 2800000, - FDS_AUTOINSERT_OP_SIDE_DELAY = 100, - FDS_AUTOINSERT_R4032_MAX_CHECKS = 150 + FDS_DISK_GAP = 0x00, + FDS_DISK_BLOCK_MARK = 0x80, + // Aspic (1988)(Bothtec)(J) necessita di almeno 1500 ms + // Pulsar no Hikari - Space Wars Simulation (Japan) di almeno 1600 ms + FDS_OP_SIDE_MS_DELAY = 1600, + FDS_AUTOINSERT_R4032_MAX_CHECKS = 7, + FDS_MIN_LAG_FRAMES = 20, + FDS_IMAGE_SIDE_SIZE = 75500, + DISK_FDS_SIDE_SIZE = 65500, + DISK_QD_SIDE_SIZE = 65536 }; -#define fds_auto_insert_enabled() (cfg->fds_switch_side_automatically & !fds.auto_insert.disabled & !fds.info.bios_first_run) +// 147 cicli (secondo vari test è questo il numero preciso) +#define FDS_8BIT_MS_DELAY 0.0825f +#define fds_auto_insert_enabled() (cfg->fds_switch_side_automatically & !fds.auto_insert.disabled & !fds.info.bios.first_run) #define fds_reset_envelope_counter(env) (fds.snd.envelope.speed << 3) * (fds.snd.env.speed + 1) #define fds_sweep_bias(val) (SBYTE)((val & 0x7F) << 1) / 2; typedef struct _fds_info_side { BYTE side; - WORD *data; + BYTE *data; uint32_t size; - uint32_t last_position; } _fds_info_side; +typedef struct _fds_info_bios { + uTCHAR file[LENGTH_FILE_NAME_LONG]; + uint32_t crc32; + BYTE first_run; +} _fds_info_bios; +typedef struct _fds_info_protection { + BYTE autodetect; + BYTE magic_card_trainer; + BYTE quick_hunter; + BYTE ouji; + BYTE kgk; +} _fds_info_protection; typedef struct _fds { struct _fds_info { BYTE enabled; BYTE *data; - WORD *image; - FILE *diff; + BYTE *image; + BYTE write_protected; BYTE writings_occurred; BYTE total_sides; - BYTE expcted_side; + BYTE expcted_sides; + BYTE format; BYTE type; uint32_t total_size; BYTE last_operation; - BYTE bios_first_run; BYTE frame_insert; + uint32_t cycles_8bit_delay; + uint32_t cycles_dummy_delay; + _fds_info_protection protection; + _fds_info_bios bios; _fds_info_side sides[20]; } info; struct _fds_side { @@ -99,30 +109,28 @@ typedef struct _fds { // le variabili da salvare nei savestate struct _fds_drive { uint32_t disk_position; - uint32_t delay; + uint32_t delay_insert; + uint32_t delay_8bit; BYTE disk_ejected; BYTE side_inserted; - BYTE gap_ended; + BYTE mark_finded; BYTE end_of_head; BYTE scan; - BYTE crc_char; + BYTE crc_control; + WORD crc; BYTE enabled_dsk_reg; BYTE enabled_snd_reg; - BYTE data_readed; - BYTE data_to_write; - // anche se continuo a salvarlo nel save_slot.c, questa - // variabile non e' piu' utilizzata. quindi se servisse - // potrebbe essere riciclata per qualche altra cosa. + BYTE data_io; + BYTE data_available; BYTE transfer_flag; - BYTE motor_on; BYTE transfer_reset; - BYTE read_mode; + BYTE motor_on; + BYTE motor_started; + BYTE io_mode; BYTE mirroring; - BYTE crc_control; BYTE unknow; BYTE drive_ready; BYTE irq_disk_enabled; - BYTE at_least_one_scan; BYTE irq_timer_enabled; BYTE irq_timer_reload_enabled; BYTE irq_timer_high; @@ -189,20 +197,20 @@ typedef struct _fds { // auto insert struct _fds_auto_insert { struct _fds_auto_insert_r4032 { + BYTE disabled; uint32_t frames; uint32_t checks; } r4032; + struct _fds_auto_insert_end_of_head { + BYTE disabled; + } end_of_head; struct _fds_auto_insert_delay { - int32_t eject; int32_t dummy; - int32_t side; } delay; struct _fds_auto_insert_rE445 { BYTE in_run; - BYTE count; } rE445; BYTE disabled; - BYTE new_side; BYTE in_game; } auto_insert; } _fds; @@ -217,18 +225,17 @@ extern _fds fds; EXTERNC void fds_init(void); EXTERNC void fds_quit(void); -EXTERNC BYTE fds_load_rom(BYTE type); +EXTERNC BYTE fds_load_rom(BYTE format); EXTERNC BYTE fds_load_bios(void); EXTERNC void fds_info(void); EXTERNC void fds_info_side(BYTE side); EXTERNC void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet); -EXTERNC void fds_diff_op(BYTE side, BYTE mode, uint32_t position, WORD value); -EXTERNC BYTE fds_from_image(uTCHAR *file, BYTE format, BYTE type); +EXTERNC BYTE fds_from_image_to_file(uTCHAR *file, BYTE format, BYTE type); EXTERNC BYTE fds_image_to_file(uTCHAR *file); +EXTERNC WORD fds_crc_byte(WORD base, BYTE data); EXTERNC uint32_t fds_disk_side_size_format(BYTE format); EXTERNC uint32_t fds_disk_side_size(void); EXTERNC uint32_t fds_image_side_size(void); -EXTERNC uint32_t fds_image_side_bytes(void); #undef EXTERNC diff --git a/src/core/mappers/mapper_FDS.c b/src/core/mappers/mapper_FDS.c index 8d05a6fa9..6c06ee9a7 100644 --- a/src/core/mappers/mapper_FDS.c +++ b/src/core/mappers/mapper_FDS.c @@ -28,8 +28,6 @@ #include "conf.h" #include "gui.h" -enum { TRANSFERED_8BIT = 0x02, END_OF_HEAD = 0x40, MIN_LAG_FRAMES = 20 }; - static const SBYTE modulation_table[8] = { 0, 1, 2, 4, 8, -4, -2, -1 }; static const BYTE volume_wave[4] = { 36, 24, 17, 14 }; @@ -40,9 +38,9 @@ void map_init_FDS(void) { EXTCL_APU_TICK(FDS); memset (&fds.auto_insert, 0x00, sizeof(fds.auto_insert)); - fds.auto_insert.delay.eject = -1; - fds.auto_insert.delay.dummy = -1; - fds.auto_insert.delay.side = -1; + fds.drive.transfer_reset = 0x02; + fds.drive.io_mode = 0x04; + fds.drive.drive_ready = 0x40; if (cfg->fds_disk1sideA_at_reset) { fds_disk_op(FDS_DISK_EJECT, 0, TRUE); @@ -76,22 +74,24 @@ void extcl_after_mapper_init_FDS(void) { memmap_prgrom_8k(0, MMCPU(0xE000), 0); } BYTE extcl_cpu_rd_mem_FDS(BYTE nidx, WORD address, UNUSED(BYTE openbus)) { - // 0xE18B : NMI entry point - // [$0100]: PC action on NMI. set to $C0 on reset - // When NMI occurs while $100 & $C0 != 0, it typically means that the game is starting. - if ((address == 0xE18B) & !fds.auto_insert.in_game & ((cpu_rd_mem_dbg(nidx, 0x100) & 0xC0) != 0)) { - fds.auto_insert.in_game = TRUE; - } else if (address == 0xE445) { - // Address : 0xE445 - // Name : CheckDiskHeader - // Input parameters : Pointer to 10 byte string at $00 - // Description : Compares the first 10 bytes on the disk coming after the FDS string, to 10 - // bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the - // equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error - // will be generated. - if (fds_auto_insert_enabled() & !fds.auto_insert.rE445.in_run) { - // i primi due passaggi sono del bios e li ignoro - if (fds.auto_insert.rE445.count > 1) { + switch (address) { + case 0xE188: + // 0xE18B : NMI entry point + // [$0100]: PC action on NMI. set to $C0 on reset + // When NMI occurs while $100 & $C0 != 0, it typically means that the game is starting. + if (!fds.auto_insert.in_game & ((cpu_rd_mem_dbg(nidx, 0x100) & 0xC0) != 0)) { + fds.auto_insert.in_game = TRUE; + } + break; + case 0xE445: + // Address : 0xE445 + // Name : CheckDiskHeader + // Input parameters : Pointer to 10 byte string at $00 + // Description : Compares the first 10 bytes on the disk coming after the FDS string, to 10 + // bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the + // equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error + // will be generated. + if (fds_auto_insert_enabled() & !fds.auto_insert.rE445.in_run) { WORD adr = cpu_rd_mem_dbg(nidx, 0) | (cpu_rd_mem_dbg(nidx, 1) << 8); BYTE string[10], side = 0xFF; uint32_t position = 0; @@ -108,7 +108,6 @@ BYTE extcl_cpu_rd_mem_FDS(BYTE nidx, WORD address, UNUSED(BYTE openbus)) { BYTE finded = TRUE; position = (a * fds_disk_side_size()); - if (fds.info.type == FDS_FORMAT_FDS) { position += 16; } @@ -145,61 +144,63 @@ BYTE extcl_cpu_rd_mem_FDS(BYTE nidx, WORD address, UNUSED(BYTE openbus)) { } else if (count == 1) { if ((side != fds.drive.side_inserted) || fds.drive.disk_ejected) { fds.auto_insert.rE445.in_run = TRUE; - fds.auto_insert.new_side = side; - fds.auto_insert.delay.side = FDS_AUTOINSERT_OP_SIDE_DELAY; + fds.side.change.new_side = side; + fds.side.change.delay = emu_ms_to_cpu_cycles(1); + fds.auto_insert.delay.dummy = 0; + if (!fds.drive.disk_ejected) { + fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + gui_update_fds_menu(); + } } if (side > 0) { fds.auto_insert.in_game = TRUE; } - fds.auto_insert.delay.eject = -1; - fds.auto_insert.delay.dummy = -1; + fds.auto_insert.delay.dummy = 0; } - } else { - fds.auto_insert.rE445.count++; } - } + break; + case 0xEF44: + // Wait after disk insertion + if (cfg->fds_fast_forward) { + nes[nidx].c.cpu.PC.w += 2; + address = nes[nidx].c.cpu.PC.w - 1; + } + break; + case 0xEFAF: + // Wait license + if (cfg->fds_fast_forward) { + nes[nidx].c.cpu.AR = 0; + nes[nidx].c.cpu.PC.w += 2; + address = nes[nidx].c.cpu.PC.w - 1; + } + break; + case 0xF46E: + // License check + if (cfg->fds_fast_forward) { + nes[nidx].c.cpu.PC.w += 2; + address = nes[nidx].c.cpu.PC.w - 1; + } + break; } return (prgrom_rd(nidx, address)); } void extcl_cpu_every_cycle_FDS(BYTE nidx) { BYTE max_speed = cfg->fds_fast_forward & - ((fds.drive.scan & (info.lag_frame.consecutive > MIN_LAG_FRAMES)) | !fds.auto_insert.in_game); - WORD data = 0; + ((fds.side.change.delay | fds.drive.delay_insert) || !fds.auto_insert.in_game || + (fds.drive.scan & (info.lag_frame.consecutive > FDS_MIN_LAG_FRAMES)) || + (fds.auto_insert.r4032.checks > 5)); // auto insert - if (fds_auto_insert_enabled()) { -#define df_max_speed (cfg->fds_fast_forward & (info.lag_frame.consecutive > MIN_LAG_FRAMES)) - if (fds.auto_insert.delay.eject > 0) { - fds.auto_insert.delay.eject--; - max_speed = df_max_speed & (fds.auto_insert.delay.eject > 0); - } else if (fds.auto_insert.delay.dummy > 0) { - if (--fds.auto_insert.delay.dummy == 0) { - fds.auto_insert.delay.dummy = -1; - fds_disk_op(FDS_DISK_INSERT, 0, TRUE); - gui_update_fds_menu(); - } - max_speed = df_max_speed & (fds.auto_insert.delay.dummy > 0); - } else if (fds.auto_insert.delay.side > 0) { - if (--fds.auto_insert.delay.side == 0) { - fds.auto_insert.delay.side = -1; - fds.side.change.new_side = fds.auto_insert.new_side; - fds.side.change.delay = FDS_AUTOINSERT_OP_SIDE_DELAY; - fds_disk_op(FDS_DISK_EJECT, 0, TRUE); - gui_update_fds_menu(); - } - max_speed = df_max_speed & (fds.auto_insert.delay.side > 0); - } - if (!fds.auto_insert.delay.eject & (fds.auto_insert.delay.dummy == -1) & (fds.auto_insert.r4032.checks > 20)) { - fds.auto_insert.delay.eject = -1; - fds.auto_insert.delay.dummy = FDS_OP_SIDE_DELAY; - fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + if (fds_auto_insert_enabled() && (fds.auto_insert.delay.dummy > 0)) { + if (!(--fds.auto_insert.delay.dummy)) { + fds_disk_op(FDS_DISK_INSERT, fds.drive.side_inserted, TRUE); gui_update_fds_menu(); - max_speed = df_max_speed; + } else { + max_speed = cfg->fds_fast_forward && fds.auto_insert.end_of_head.disabled; } -#undef df_max_speed } - if (max_speed & !fds.info.bios_first_run) { + if (max_speed) { gui_max_speed_start(); } else { gui_max_speed_stop(); @@ -235,149 +236,101 @@ void extcl_cpu_every_cycle_FDS(BYTE nidx) { // no disco, no party if (fds.drive.disk_ejected) { - fds.drive.at_least_one_scan = FALSE; - if (fds.drive.delay < FDS_8BIT_DELAY) { - fds.drive.delay = FDS_8BIT_DELAY; - } return; } - // il motore non e' avviato - if (!fds.drive.motor_on) { - fds.drive.at_least_one_scan = FALSE; + if (fds.drive.delay_insert) { + fds.drive.delay_insert--; + return; + } + + if (!fds.drive.motor_on && !fds.drive.motor_started) { fds.drive.disk_position = 0; - fds.drive.gap_ended = FALSE; - // "Akuu Senki Raijin (Japan) (Disk Writer)" ne ha bisogno - // per non sporcare lo screen. - if (fds.drive.delay < FDS_8BIT_DELAY) { - fds.drive.delay = FDS_8BIT_DELAY; - } + fds.drive.mark_finded = FALSE; return; } // se c'e' un delay aspetto - if ((fds.drive.delay > 0) && --fds.drive.delay) { + if ((fds.drive.delay_8bit > 0) && --fds.drive.delay_8bit) { return; } fds.drive.scan = !fds.drive.transfer_reset; fds.info.last_operation = FDS_OP_NONE; + fds.drive.data_available = FALSE; if (fds.drive.scan) { - // se c'e' una richiesta di invio crc i prossimi due bytes lo saranno - if (!fds.drive.crc_char && fds.drive.crc_control) { - fds.drive.crc_char = 2; - } + BYTE data = 0, transfer = FALSE; + + data = fds.side.info->data[fds.drive.disk_position]; - if (fds.drive.read_mode) { + if (fds.drive.io_mode) { // read - data = fds.side.info->data[fds.drive.disk_position]; + if (fds.drive.drive_ready) { + if (fds.drive.mark_finded) { + transfer = TRUE; + fds.drive.crc = fds_crc_byte(fds.drive.crc, data); + } else if (data == FDS_DISK_BLOCK_MARK) { + fds.drive.mark_finded = TRUE; + fds.drive.crc = 0; + fds.drive.crc = fds_crc_byte(fds.drive.crc, data); + } + } } else { - // write if (!fds.drive.drive_ready) { data = FDS_DISK_GAP; - } else if (fds.drive.crc_char) { - data = FDS_DISK_CRC_CHAR1; + fds.drive.crc = 0; } else { - data = fds.side.info->data[fds.drive.disk_position]; + if (fds.drive.crc_control) { + data = fds.drive.crc >> 0; + fds.drive.crc >>= 8; + } else { + data = fds.drive.data_io; + fds.drive.crc = fds_crc_byte(fds.drive.crc, data); + } } + transfer = TRUE; } - // se non sono piu' nel gap vuol dire che ho trasferito - // 8 bit di dati quindi setto il flag corrispondente e - // se e' abilitato l'irq del disco, lo setto. - if (fds.drive.gap_ended) { - fds.drive.transfer_flag = 0x02; - fds.drive.at_least_one_scan = TRUE; + fds.auto_insert.r4032.frames = 0; + fds.auto_insert.r4032.checks = 0; + + if (transfer) { + fds.drive.data_available = 0x80; if (fds.drive.irq_disk_enabled) { + fds.drive.transfer_flag = 0x02; nes[nidx].c.irq.high |= FDS_DISK_IRQ; } - - if (fds.drive.read_mode) { - fds.drive.data_readed = data; + if (fds.drive.io_mode) { + fds.drive.data_io = data; fds.info.last_operation = FDS_OP_READ; } else { - uint32_t position = (fds.drive.disk_position - 2); - WORD *dst = &fds.side.info->data[position]; - - // quando inizia la scrittura il bios scrive sempre - // prima un GAP, seguito da un MARK seguito dal blocco che verrà chiuso dai CRC. - // il last_position devo aggiornarlo solo con i CRC e i GAP che seguono. - if (((*dst) == 0x0100) && (fds.drive.data_to_write == 0x00)) { - (*dst) = 0x0100; - } else if ((fds.drive.data_to_write == 0x80) && - (((*dst) == 0x0180) || (position == fds.side.info->last_position))) { - (*dst) = 0x0180; - if (position == fds.side.info->last_position) { - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, position, (*dst)); - } - } else if (fds.drive.crc_char) { - if (fds.drive.crc_char == 2) { - (*dst) = FDS_DISK_CRC_CHAR1; - } else { - (*dst) = FDS_DISK_CRC_CHAR2; - } - if (position >= fds.side.info->last_position) { - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, position, (*dst)); - fds.info.sides[fds.side.info->side].last_position = position + 1; - if ((*dst) == FDS_DISK_CRC_CHAR2) { - for (uint32_t i = 0; i < FDS_GAP_BLOCK; i++) { - uint32_t p = position + 1 + i; - - if (p < fds_disk_side_size()) { - fds.side.info->data[p] = FDS_DISK_GAP; - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, p, FDS_DISK_GAP); - fds.info.sides[fds.side.info->side].last_position = p + 1; - } - } - } - } - } else { - (*dst) = fds.drive.data_to_write; - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, position, fds.drive.data_to_write); - } - data = (*dst); + fds.side.info->data[fds.drive.disk_position] = data; + fds.info.writings_occurred = TRUE; fds.info.last_operation = FDS_OP_WRITE; } } - - if (data != FDS_DISK_GAP) { - fds.drive.gap_ended = TRUE; - } - - if (fds.drive.crc_char && !(--fds.drive.crc_char)) { - fds.drive.gap_ended = fds.drive.crc_control = FALSE; - } - - if (!fds.drive.drive_ready) { - fds.drive.gap_ended = FALSE; - } } - if (++fds.drive.disk_position >= fds.info.sides[fds.drive.side_inserted].size) { - fds.drive.end_of_head = END_OF_HEAD; - fds.drive.disk_position = 0; - fds.drive.gap_ended = FALSE; - fds.drive.delay = 65536 * 8; - // FDS interessati : - // - 19 Neunzehn (1988)(Soft Pro)(J).fds - // visto che il controllo del r4032 e' mooooolto lento, l'eject forzato alla fine del disco - // costringe la rom al richiamo della funzione del bios $E445. - // - Dandy (19xx)(Pony Canyon)(J).fds - // dopo aver selezionato il nome del personaggio, puo' capitare che il disco sia disinserito a causa di - // di eject e che dia un "error 01" che comunque verra' subito corretto dall'insert automatico seguente. - // - Zelda no Densetsu - The Hyrule Fantasy (1986)(Nintendo)(J).fds - // stesso discorso fatto per Dandy (19xx)(Pony Canyon)(J).fds. - if (fds_auto_insert_enabled() && !fds.drive.at_least_one_scan && - (fds.auto_insert.delay.eject == -1) && !fds.side.change.delay && - (fds.auto_insert.delay.dummy == -1)) { - fds.auto_insert.delay.eject = FDS_AUTOINSERT_OP_SIDE_DELAY; + if (fds.drive.scan || fds.drive.motor_started) { + if (++fds.drive.disk_position >= fds.info.sides[fds.drive.side_inserted].size) { + fds.drive.end_of_head = 0x40; + fds.drive.disk_position = 0; + fds.drive.transfer_reset = FALSE; + if (fds.drive.motor_started) { + fds.drive.motor_on = FALSE; + fds.drive.motor_started = FALSE; + if (fds_auto_insert_enabled() && !fds.auto_insert.end_of_head.disabled && !fds.auto_insert.delay.dummy) { + fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + gui_update_fds_menu(); + fds.auto_insert.delay.dummy = fds.info.cycles_dummy_delay; + } + } + } else { + fds.drive.end_of_head = FALSE; + // il delay per riuscire a leggere i prossimi 8 bit + fds.drive.delay_8bit = fds.info.cycles_8bit_delay; } - fds.drive.at_least_one_scan = FALSE; - } else { - fds.drive.end_of_head = FALSE; - // il delay per riuscire a leggere i prossimi 8 bit - fds.drive.delay = FDS_8BIT_DELAY; } } void extcl_apu_tick_FDS(void) { diff --git a/src/core/patcher.c b/src/core/patcher.c index 68c61bc0b..4861e2253 100644 --- a/src/core/patcher.c +++ b/src/core/patcher.c @@ -312,7 +312,7 @@ static BYTE patcher_ips(_rom_mem *patch, _rom_mem *rom) { BYTE ch = 0; address = patcher_3byte(patch); - if ((address == -1) || (address == 0x454f46)) { + if ((address == -1) || (address == 0x454F46)) { break; } @@ -352,9 +352,7 @@ static BYTE patcher_ips(_rom_mem *patch, _rom_mem *rom) { } if (rle) { - SDBWORD i = 0; - - for (i = 0; i < len; i++) { + for (SDBWORD i = 0; i < len; i++) { blk[address + i] = ch; } } else { diff --git a/src/core/recording.c b/src/core/recording.c index e55ff9484..e1959d405 100644 --- a/src/core/recording.c +++ b/src/core/recording.c @@ -1412,7 +1412,7 @@ static int ffmpeg_audio_select_samplerate(const AVCodec *codec) { const int *p = NULL; if (!codec->supported_samplerates) { - switch(codec->id) { + switch (codec->id) { case AV_CODEC_ID_PCM_S16LE: case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16LE_PLANAR: @@ -1474,7 +1474,7 @@ static uint64_t ffmpeg_audio_select_channel_layout(const AVCodec *codec) { int best_nb_channels = 0; if (!codec->channel_layouts) { - switch(codec->id) { + switch (codec->id) { case AV_CODEC_ID_PCM_S16LE: case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16LE_PLANAR: @@ -1549,7 +1549,7 @@ static int ffmpeg_audio_select_channel_layout(const AVCodec *codec, AVChannelLay int best_nb_channels = 0; if (!codec->ch_layouts) { - switch(codec->id) { + switch (codec->id) { case AV_CODEC_ID_PCM_S16LE: case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16LE_PLANAR: diff --git a/src/core/save_slot.c b/src/core/save_slot.c index 6f9fb2a66..e46ed466a 100644 --- a/src/core/save_slot.c +++ b/src/core/save_slot.c @@ -511,31 +511,32 @@ BYTE save_slot_operation(BYTE mode, BYTE slot, FILE *fp) { save_slot_ele(mode, slot, dipswitch.value); if (fds.info.enabled) { - // libero la zona di memoria gia' occupata BYTE old_side_inserted = fds.drive.side_inserted; // salvo, leggo o conto quello che serve save_slot_ele(mode, slot, fds.drive.disk_position); - save_slot_ele(mode, slot, fds.drive.delay); + save_slot_ele(mode, slot, fds.drive.delay_insert); + save_slot_ele(mode, slot, fds.drive.delay_8bit); save_slot_ele(mode, slot, fds.drive.disk_ejected); save_slot_ele(mode, slot, fds.drive.side_inserted); - save_slot_ele(mode, slot, fds.drive.gap_ended); + save_slot_ele(mode, slot, fds.drive.mark_finded); + save_slot_ele(mode, slot, fds.drive.end_of_head); save_slot_ele(mode, slot, fds.drive.scan); - save_slot_ele(mode, slot, fds.drive.crc_char); + save_slot_ele(mode, slot, fds.drive.crc_control); + save_slot_ele(mode, slot, fds.drive.crc); save_slot_ele(mode, slot, fds.drive.enabled_dsk_reg); save_slot_ele(mode, slot, fds.drive.enabled_snd_reg); - save_slot_ele(mode, slot, fds.drive.data_readed); - save_slot_ele(mode, slot, fds.drive.data_to_write); + save_slot_ele(mode, slot, fds.drive.data_io); + save_slot_ele(mode, slot, fds.drive.data_available); save_slot_ele(mode, slot, fds.drive.transfer_flag); - save_slot_ele(mode, slot, fds.drive.motor_on); save_slot_ele(mode, slot, fds.drive.transfer_reset); - save_slot_ele(mode, slot, fds.drive.read_mode); + save_slot_ele(mode, slot, fds.drive.motor_on); + save_slot_ele(mode, slot, fds.drive.motor_started); + save_slot_ele(mode, slot, fds.drive.io_mode); save_slot_ele(mode, slot, fds.drive.mirroring); - save_slot_ele(mode, slot, fds.drive.crc_control); save_slot_ele(mode, slot, fds.drive.unknow); save_slot_ele(mode, slot, fds.drive.drive_ready); save_slot_ele(mode, slot, fds.drive.irq_disk_enabled); - save_slot_ele(mode, slot, fds.drive.at_least_one_scan); save_slot_ele(mode, slot, fds.drive.irq_timer_enabled); save_slot_ele(mode, slot, fds.drive.irq_timer_reload_enabled); save_slot_ele(mode, slot, fds.drive.irq_timer_high); @@ -581,15 +582,11 @@ BYTE save_slot_operation(BYTE mode, BYTE slot, FILE *fp) { save_slot_ele(mode, slot, fds.auto_insert.r4032.frames); save_slot_ele(mode, slot, fds.auto_insert.r4032.checks); - save_slot_ele(mode, slot, fds.auto_insert.delay.eject); save_slot_ele(mode, slot, fds.auto_insert.delay.dummy); - save_slot_ele(mode, slot, fds.auto_insert.delay.side); save_slot_ele(mode, slot, fds.auto_insert.rE445.in_run); - save_slot_ele(mode, slot, fds.auto_insert.rE445.count); save_slot_ele(mode, slot, fds.auto_insert.disabled); - save_slot_ele(mode, slot, fds.auto_insert.new_side); save_slot_ele(mode, slot, fds.auto_insert.in_game); save_slot_ele(mode, slot, info.lag_frame.consecutive); diff --git a/src/core/unicode_def.h b/src/core/unicode_def.h index 4a6992373..99bf94115 100644 --- a/src/core/unicode_def.h +++ b/src/core/unicode_def.h @@ -71,6 +71,7 @@ typedef wchar_t uTCHAR; #define ustrchr wcschr #define ustrdup _wcsdup #define usscanf swscanf_s +#define ustrstr wcsstr // linux, bsd #else @@ -119,6 +120,7 @@ typedef char uTCHAR; #define ustrchr strchr #define ustrdup strdup #define usscanf sscanf +#define ustrstr strstr #endif diff --git a/src/gui/designer/mainWindow.ui b/src/gui/designer/mainWindow.ui index 76d37ee80..7a56b8a07 100644 --- a/src/gui/designer/mainWindow.ui +++ b/src/gui/designer/mainWindow.ui @@ -99,7 +99,8 @@ :/icon/icons/fds_export.svgz:/icon/icons/fds_export.svgz - + + @@ -788,14 +789,14 @@ Detach &Barcode - + - to &FDS Format + to &FDS Format (with Header) - to Quick Disk Format + to &Quick Disk Format @@ -803,6 +804,11 @@ to puNES image + + + to F&DS Format (without Header) + + diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index cff0d18bf..0d5bb07f4 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -885,9 +885,10 @@ void mainWindow::connect_menu_signals(void) { connect_action(action_Disk_4_side_B, 7, SLOT(s_disk_side())); connect_action(action_Switch_sides, 0xFFF, SLOT(s_disk_side())); connect_action(action_Eject_Insert_Disk, SLOT(s_eject_disk())); - connect_action(action_Current_state_to_FDS, 0, SLOT(s_export_fds_image())); - connect_action(action_Current_state_to_Quick_Disk, 1, SLOT(s_export_fds_image())); - connect_action(action_Current_state_to_puNES_image, 2, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_FDS_with_Header, 0, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_FDS_without_Header, 1, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_Quick_Disk, 2, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_puNES_image, 3, SLOT(s_export_fds_image())); connect_action(action_Tape_Play, SLOT(s_tape_play())); connect_action(action_Tape_Record, SLOT(s_tape_record())); connect_action(action_Tape_Stop, SLOT(s_tape_stop())); @@ -1554,7 +1555,8 @@ void mainWindow::s_disk_side(void) { fds_disk_op(FDS_DISK_SELECT, side, FALSE); } else { fds.side.change.new_side = side; - fds.side.change.delay = FDS_OP_SIDE_DELAY; + fds.side.change.delay = fds.info.cycles_dummy_delay; + fds.auto_insert.delay.dummy = 0; fds_disk_op(FDS_DISK_EJECT, 0, FALSE); } @@ -1579,18 +1581,18 @@ void mainWindow::s_export_fds_image(void) { emu_thread_pause(); - switch(format) { + switch (format) { default: case 0: - format = 0; + case 1: filters.append(tr("FDS Format Disk")); filters[0].append(" (*.fds *.FDS)"); break; - case 1: + case 2: filters.append(tr("Quick Disk Format Disk")); filters[0].append(" (*.qd *.QD)"); break; - case 2: + case 3: filters.append(tr("puNES image")); filters[0].append(" (*.image)"); break; @@ -1607,22 +1609,23 @@ void mainWindow::s_export_fds_image(void) { BYTE rc = EXIT_ERROR; if (fileinfo.suffix().isEmpty()) { - switch(format) { + switch (format) { default: case 0: + case 1: fileinfo.setFile(QString(file) + ".fds"); break; - case 1: + case 2: fileinfo.setFile(QString(file) + ".qd"); break; - case 2: + case 3: fileinfo.setFile(QString(file) + ".image"); break; } } - if (format < 2) { - rc = fds_from_image(uQStringCD(fileinfo.absoluteFilePath()), - format == 0 ? FDS_FORMAT : QD_FORMAT, + if (format < 3) { + rc = fds_from_image_to_file(uQStringCD(fileinfo.absoluteFilePath()), + format == 2 ? QD_FORMAT : FDS_FORMAT, format == 0 ? FDS_FORMAT_FDS : FDS_FORMAT_RAW); } else { rc = fds_image_to_file(uQStringCD(fileinfo.absoluteFilePath())); diff --git a/src/gui/wdgOverlayUi.cpp b/src/gui/wdgOverlayUi.cpp index 4116df553..17d14cf65 100644 --- a/src/gui/wdgOverlayUi.cpp +++ b/src/gui/wdgOverlayUi.cpp @@ -122,6 +122,8 @@ static const char *info_messages_precompiled[] = { /* 38 */ QT_TRANSLATE_NOOP("overlayWidgetInfo", "[red]error[normal] on write save state"), //: Do not translate the words contained between parentheses (example: [red] or [normal]) are tags that have a specific meaning and do not traslate %1 and %2 /* 39 */ QT_TRANSLATE_NOOP("overlayWidgetInfo", "auto switch [red]disabled[normal], game not compatible"), +//: Do not translate the words contained between parentheses (example: [red] or [normal]) are tags that have a specific meaning and do not traslate %1 and %2 +/* 40 */ QT_TRANSLATE_NOOP("overlayWidgetInfo", "auto switch [red]disabled[normal], single-sided disk"), }; typedef struct _overlay_info_message {