/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ #include #include "snes9x.h" #include "memory.h" #include "apu.h" #include "controls.h" #include "hacks.h" CMemory Memory; uint32 OpenBus = 0; #define match_nn(str) (strncmp(Memory.ROMName, (str), strlen((str))) == 0) static int First512BytesCountZeroes(void) { int zeroCount = 0; for (int i = 0; i < 512; i++) { if (Memory.ROM[i] == 0) zeroCount++; } return zeroCount; } static void sanitize(char *b, int size) { for (int pos = 0; pos < size; pos++) { if (b[pos] == 0) continue; if (b[pos] < 32 || b[pos] > 126) b[pos] = '_'; } b[size - 1] = 0; } static bool8 allASCII (uint8 *b, int size) { for (int i = 0; i < size; i++) { if (b[i] < 32 || b[i] > 126) return (FALSE); } return (TRUE); } static void ShowInfoString (void) { const char *contents[3] = { "ROM", "ROM+RAM", "ROM+RAM+BAT" }; char contentstr[20], sizestr[20], sramstr[20]; bool8 isChecksumOK = (Memory.ROMChecksum + Memory.ROMComplementChecksum == 0xffff) & (Memory.ROMChecksum == Memory.CalculatedChecksum); if (!isChecksumOK || ((uint32) Memory.CalculatedSize > (uint32) (((1 << (Memory.ROMSize - 7)) * 128) * 1024))) { // Use yellow tint to indicate corrupted ROM. Settings.DisplayColor = BUILD_PIXEL(31, 31, 0); } else if (Memory.ROMIsPatched) { // Use slight blue tint to indicate ROM was patched. Settings.DisplayColor = BUILD_PIXEL(26, 26, 31); } else { Settings.DisplayColor = BUILD_PIXEL(31, 31, 31); } strcpy(contentstr, contents[(Memory.ROMType & 0xf) % 3]); if (Settings.DSP == 1) strcat(contentstr, "+DSP-1"); else if (Settings.DSP == 2) strcat(contentstr, "+DSP-2"); if (Memory.ROMSize < 7 || Memory.ROMSize - 7 > 23) strcpy(sizestr, "Corrupt"); else sprintf(sizestr, "%dMbits", 1 << (Memory.ROMSize - 7)); if (Memory.SRAMSize > 16) strcpy(sramstr, "Corrupt"); else sprintf(sramstr, "%dKbits", 8 * (Memory.SRAMMask + 1) / 1024); sprintf(String, "\"%s\" [%s] %s, %s, %s, %s, SRAM:%s, ID:%s, CRC32:%08X", Memory.ROMName, isChecksumOK ? "checksum ok" : "bad checksum", Memory.HiROM ? "HiROM" : "LoROM", sizestr, contentstr, (Settings.Region == S9X_PAL) ? "PAL" : "NTSC", sramstr, Memory.ROMId, Memory.ROMCRC32); S9xMessage(S9X_INFO, S9X_ROM_INFO, String); } static uint32 map_mirror (uint32 size, uint32 pos) { // from bsnes if (size == 0) return (0); if (pos < size) return (pos); uint32 mask = 1 << 31; while (!(pos & mask)) mask >>= 1; if (size <= (pos & mask)) return (map_mirror(size, pos - mask)); else return (mask + map_mirror(size - mask, pos - mask)); } static void map_lorom (uint32 bank_s, uint32 bank_e, uint32 addr_s, uint32 addr_e, uint32 size) { uint32 c, i, p, addr; for (c = bank_s; c <= bank_e; c++) { for (i = addr_s; i <= addr_e; i += 0x1000) { p = (c << 4) | (i >> 12); addr = (c & 0x7f) * 0x8000; Memory.ReadMap[p] = Memory.ROM + map_mirror(size, addr) - (i & 0x8000); } } } static void map_hirom (uint32 bank_s, uint32 bank_e, uint32 addr_s, uint32 addr_e, uint32 size) { uint32 c, i, p, addr; for (c = bank_s; c <= bank_e; c++) { for (i = addr_s; i <= addr_e; i += 0x1000) { p = (c << 4) | (i >> 12); addr = c << 16; Memory.ReadMap[p] = Memory.ROM + map_mirror(size, addr); } } } static void map_space (uint32 bank_s, uint32 bank_e, uint32 addr_s, uint32 addr_e, uint8 *data) { uint32 c, i, p; for (c = bank_s; c <= bank_e; c++) { for (i = addr_s; i <= addr_e; i += 0x1000) { p = (c << 4) | (i >> 12); Memory.ReadMap[p] = data; Memory.WriteMap[p] = data; } } } static void map_index (uint32 bank_s, uint32 bank_e, uint32 addr_s, uint32 addr_e, int index, int type) { uint32 c, i, p; for (c = bank_s; c <= bank_e; c++) { for (i = addr_s; i <= addr_e; i += 0x1000) { p = (c << 4) | (i >> 12); Memory.ReadMap[p] = (uint8 *) (pint) index; Memory.WriteMap[p] = (uint8 *) (pint) index; } } } static void Map_Initialize (void) { for (int c = 0; c < MEMMAP_NUM_BLOCKS; c++) { Memory.ReadMap[c] = (uint8 *) MAP_NONE; Memory.WriteMap[c] = (uint8 *) MAP_NONE; } // will be overwritten map_space(0x00, 0x3f, 0x0000, 0x1fff, Memory.RAM); map_index(0x00, 0x3f, 0x2000, 0x3fff, MAP_PPU, MAP_TYPE_I_O); map_index(0x00, 0x3f, 0x4000, 0x5fff, MAP_CPU, MAP_TYPE_I_O); map_space(0x80, 0xbf, 0x0000, 0x1fff, Memory.RAM); map_index(0x80, 0xbf, 0x2000, 0x3fff, MAP_PPU, MAP_TYPE_I_O); map_index(0x80, 0xbf, 0x4000, 0x5fff, MAP_CPU, MAP_TYPE_I_O); } static void Map_Finalize (void) { // Map Work RAM map_space(0x7e, 0x7e, 0x0000, 0xffff, Memory.RAM); map_space(0x7f, 0x7f, 0x0000, 0xffff, Memory.RAM + 0x10000); } static void Map_DSP (void) { switch (DSP0.maptype) { case M_DSP1_LOROM_S: map_index(0x20, 0x3f, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O); map_index(0xa0, 0xbf, 0x8000, 0xffff, MAP_DSP, MAP_TYPE_I_O); break; case M_DSP1_LOROM_L: map_index(0x60, 0x6f, 0x0000, 0x7fff, MAP_DSP, MAP_TYPE_I_O); map_index(0xe0, 0xef, 0x0000, 0x7fff, MAP_DSP, MAP_TYPE_I_O); break; case M_DSP1_HIROM: map_index(0x00, 0x1f, 0x6000, 0x7fff, MAP_DSP, MAP_TYPE_I_O); map_index(0x80, 0x9f, 0x6000, 0x7fff, MAP_DSP, MAP_TYPE_I_O); break; case M_DSP2_LOROM: map_index(0x20, 0x3f, 0x6000, 0x6fff, MAP_DSP, MAP_TYPE_I_O); map_index(0x20, 0x3f, 0x8000, 0xbfff, MAP_DSP, MAP_TYPE_I_O); map_index(0xa0, 0xbf, 0x6000, 0x6fff, MAP_DSP, MAP_TYPE_I_O); map_index(0xa0, 0xbf, 0x8000, 0xbfff, MAP_DSP, MAP_TYPE_I_O); break; } } static void Map_LoROMMap (void) { Map_Initialize(); map_lorom(0x00, 0x3f, 0x8000, 0xffff, Memory.CalculatedSize); map_lorom(0x40, 0x7f, 0x0000, 0xffff, Memory.CalculatedSize); map_lorom(0x80, 0xbf, 0x8000, 0xffff, Memory.CalculatedSize); map_lorom(0xc0, 0xff, 0x0000, 0xffff, Memory.CalculatedSize); if (Settings.DSP) Map_DSP(); if (Memory.SRAMSize > 0) { uint32 hi = (Memory.ROMSize > 11 || Memory.SRAMSize > 5) ? 0x7fff : 0xffff; map_index(0x70, 0x7d, 0x0000, hi, MAP_LOROM_SRAM, MAP_TYPE_RAM); map_index(0xf0, 0xff, 0x0000, hi, MAP_LOROM_SRAM, MAP_TYPE_RAM); } Map_Finalize(); } static void Map_HiROMMap (void) { Map_Initialize(); map_hirom(0x00, 0x3f, 0x8000, 0xffff, Memory.CalculatedSize); map_hirom(0x40, 0x7f, 0x0000, 0xffff, Memory.CalculatedSize); map_hirom(0x80, 0xbf, 0x8000, 0xffff, Memory.CalculatedSize); map_hirom(0xc0, 0xff, 0x0000, 0xffff, Memory.CalculatedSize); if (Settings.DSP) Map_DSP(); map_index(0x20, 0x3f, 0x6000, 0x7fff, MAP_HIROM_SRAM, MAP_TYPE_RAM); map_index(0xa0, 0xbf, 0x6000, 0x7fff, MAP_HIROM_SRAM, MAP_TYPE_RAM); Map_Finalize(); } static uint16 checksum_calc_sum (uint8 *data, uint32 length) { uint16 sum = 0; for (size_t i = 0; i < length; i++) sum += data[i]; return (sum); } static uint16 checksum_mirror_sum (uint8 *start, uint32 *length, uint32 mask) { // from NSRT while (!(*length & mask) && mask) mask >>= 1; uint16 part1 = checksum_calc_sum(start, mask); uint16 part2 = 0; uint32 next_length = *length - mask; if (next_length) { part2 = checksum_mirror_sum(start + mask, &next_length, mask >> 1); while (next_length < mask) { next_length += next_length; part2 += part2; } *length = mask + mask; } return (part1 + part2); } static uint16 CalcChecksum (uint8 *data, uint32 length) { if (length & 0x7fff) return checksum_calc_sum(data, length); else return checksum_mirror_sum(data, &length, 0x800000); } static int ScoreHiROM (void) { uint8 *buf = Memory.ROM + 0xff00; int score = 0; if (buf[0xd5] & 0x1) score += 2; // Mode23 is SA-1 if (buf[0xd5] == 0x23) score -= 2; if (buf[0xd4] == 0x20) score += 2; if ((buf[0xdc] + (buf[0xdd] << 8)) + (buf[0xde] + (buf[0xdf] << 8)) == 0xffff) { score += 2; if (0 != (buf[0xde] + (buf[0xdf] << 8))) score++; } if (buf[0xda] == 0x33) score += 2; if ((buf[0xd5] & 0xf) < 4) score += 2; if (!(buf[0xfd] & 0x80)) score -= 6; if ((buf[0xfc] + (buf[0xfd] << 8)) > 0xffb0) score -= 2; // reduced after looking at a scan by Cowering if (Memory.CalculatedSize > 1024 * 1024 * 3) score += 4; if ((1 << (buf[0xd7] - 7)) > 48) score -= 1; if (!allASCII(&buf[0xb0], 6)) score -= 1; if (!allASCII(&buf[0xc0], ROM_NAME_LEN - 1)) score -= 1; return (score); } static int ScoreLoROM (void) { uint8 *buf = Memory.ROM + 0x7f00; int score = 0; if (!(buf[0xd5] & 0x1)) score += 3; // Mode23 is SA-1 if (buf[0xd5] == 0x23) score += 2; if ((buf[0xdc] + (buf[0xdd] << 8)) + (buf[0xde] + (buf[0xdf] << 8)) == 0xffff) { score += 2; if (0 != (buf[0xde] + (buf[0xdf] << 8))) score++; } if (buf[0xda] == 0x33) score += 2; if ((buf[0xd5] & 0xf) < 4) score += 2; if (!(buf[0xfd] & 0x80)) score -= 6; if ((buf[0xfc] + (buf[0xfd] << 8)) > 0xffb0) score -= 2; // reduced per Cowering suggestion if (Memory.CalculatedSize <= 1024 * 1024 * 16) score += 2; if ((1 << (buf[0xd7] - 7)) > 48) score -= 1; if (!allASCII(&buf[0xb0], 6)) score -= 1; if (!allASCII(&buf[0xc0], ROM_NAME_LEN - 1)) score -= 1; return (score); } int ApplyGameSpecificHacks (void) { int applied = 0; for (const s9x_hack_t *hack = &GameHacks[0]; hack->checksum; hack++) { if (hack->checksum == Memory.ROMCRC32) { printf("Applying patch %s: '%s'\n", hack->name, hack->patch); char *ptr = (char*)hack->patch; while (ptr && *ptr) { int offset = strtol(ptr, &ptr, 16); int value = strtol(ptr + 1, &ptr, 16); if ((ptr = strchr(ptr, ','))) ptr++; // We only care about opcode 0x42 for now if ((value != 0x42) && ((value >> 8) != 0x42)) { printf(" - Warning: Ignoring patch %X=%X...\n", offset, value); } else if (offset < 1 || offset >= Memory.CalculatedSize) { printf(" - Warning: Offset %d (%X) is out of range...\n", offset, offset); } else if (value & 0xFF00) // 16 bit replacement { printf(" - Applying 16bit patch %X=%X\n", offset, value); // Memory.ROM[offset] = (value >> 8) & 0xFF; // Memory.ROM[offset + 1] = value & 0xFF; applied++; } else // 8 bit replacement { printf(" - Applying 8bit patch %X=%X\n", offset, value); // Memory.ROM[offset] = value; applied++; } } } } return applied; } static bool8 InitROM () { if (!Memory.ROM || Memory.ROM_SIZE < 0x200) return (FALSE); if ((Memory.ROM_SIZE & 0x7FF) == 512 || First512BytesCountZeroes() > 400) { emu_printf("Found ROM file header (and ignored it).\n"); memmove(Memory.ROM, Memory.ROM + 512, Memory.ROM_SIZE - 512); Memory.ROM_SIZE -= 512; } Memory.CalculatedSize = ((Memory.ROM_SIZE + 0x1fff) / 0x2000) * 0x2000; //// these two games fail to be detected if (strncmp((char *) &Memory.ROM[0x7fc0], "YUYU NO QUIZ DE GO!GO!", 22) == 0 || (strncmp((char *) &Memory.ROM[0xffc0], "BATMAN--REVENGE JOKER", 21) == 0)) { Memory.LoROM = TRUE; Memory.HiROM = FALSE; } else { Memory.LoROM = (ScoreLoROM() >= ScoreHiROM()); Memory.HiROM = !Memory.LoROM; } //// Parse ROM header and read ROM informatoin uint8 *RomHeader = Memory.ROM + (Memory.HiROM ? 0xFFB0 : 0x7FB0); strncpy(Memory.ROMName, (char *) &RomHeader[0x10], ROM_NAME_LEN - 1); sanitize(Memory.ROMName, ROM_NAME_LEN); Memory.ROMName[ROM_NAME_LEN - 1] = 0; if (strlen(Memory.ROMName)) { char *p = Memory.ROMName + strlen(Memory.ROMName); if (p > Memory.ROMName + 21 && Memory.ROMName[20] == ' ') p = Memory.ROMName + 21; while (p > Memory.ROMName && *(p - 1) == ' ') p--; *p = 0; } Memory.ROMSize = RomHeader[0x27]; Memory.SRAMSize = RomHeader[0x28]; Memory.ROMSpeed = RomHeader[0x25]; Memory.ROMType = RomHeader[0x26]; Memory.ROMRegion = RomHeader[0x29]; Memory.ROMChecksum = RomHeader[0x2E] + (RomHeader[0x2F] << 8); Memory.ROMComplementChecksum = RomHeader[0x2C] + (RomHeader[0x2D] << 8); memmove(Memory.ROMId, &RomHeader[0x02], 4); sanitize(Memory.ROMId, 4); //// Detect DSP 1 & 2 if (Memory.ROMType == 0x03 || (Memory.ROMType == 0x05 && Memory.ROMSpeed != 0x20)) { Settings.DSP = 1; if (Memory.HiROM) { DSP0.boundary = 0x7000; DSP0.maptype = M_DSP1_HIROM; } else if (Memory.CalculatedSize > 0x100000) { DSP0.boundary = 0x4000; DSP0.maptype = M_DSP1_LOROM_L; } else { DSP0.boundary = 0xc000; DSP0.maptype = M_DSP1_LOROM_S; } SetDSP = &DSP1SetByte; GetDSP = &DSP1GetByte; } else if (Memory.ROMType == 0x05 && Memory.ROMSpeed == 0x20) { Settings.DSP = 2; DSP0.boundary = 0x10000; DSP0.maptype = M_DSP2_LOROM; SetDSP = &DSP2SetByte; GetDSP = &DSP2GetByte; } else { Settings.DSP = 0; SetDSP = NULL; GetDSP = NULL; } //// SRAM size if (strncmp("HITOMI3 ", Memory.ROMName, 8) == 0) { Memory.SRAMSize = 1; } Memory.SRAMMask = Memory.SRAMSize ? ((1 << (Memory.SRAMSize + 3)) * 128) - 1 : 0; Memory.SRAMBytes = Memory.SRAMSize ? ((1 << (Memory.SRAMSize + 3)) * 128) : 0; if (Memory.SRAMBytes > 0x8000) { emu_printf("\n\nWARNING: Default SRAM size too small!, need bytes"); emu_printi(Memory.SRAMBytes); } //// Map memory if (Memory.HiROM) { Map_HiROMMap(); } else { Map_LoROMMap(); } //// Checksums Memory.ROMCRC32 = crc32_le(0, Memory.ROM, Memory.CalculatedSize); Memory.CalculatedChecksum = CalcChecksum(Memory.ROM, Memory.CalculatedSize); //// ROM Region if ((Memory.ROMRegion >= 2) && (Memory.ROMRegion <= 12)) { Settings.Region = S9X_PAL; Settings.FrameRate = 50; } else { Settings.Region = S9X_NTSC; Settings.FrameRate = 60; } // Setup APU timings S9xAPUTimingSetSpeedup(0); //// Apply Game hacks if (!Settings.DisableGameSpecificHacks) { ApplyGameSpecificHacks(); } //// Show ROM information ShowInfoString(); //// Reset system, then we're ready S9xReset(); return (TRUE); } // Public functions bool8 S9xMemoryInit (void) { memset(&Memory, 0, sizeof(Memory)); Memory.RAM = (uint8 *) emu_Malloc(0x20000); // calloc(1, 0x20000); // 128k Memory.VRAM = (uint8 *) emu_SMalloc(0x10000); //calloc(1, 0x10000); // 64k Malloc? Memory.SRAM = (uint8 *) emu_Malloc(0x8000); //calloc(1, 0x8000); // 32k Memory.ROM_MAX_SIZE = 0x600000; //Memory.ROM = (uint8 *) calloc(1, Memory.ROM_MAX_SIZE); // Note: we don't care if ROM alloc fails. It's just to grab a large heap block // before it gets fragmented. The actual useful alloc is done in S9xLoadROM() if (!Memory.RAM || !Memory.SRAM || !Memory.VRAM) { S9xMemoryDeinit(); return (FALSE); } return (TRUE); } void S9xMemoryDeinit (void) { emu_Free(Memory.RAM); //free(Memory.RAM); emu_Free(Memory.SRAM); //free(Memory.SRAM); emu_Free(Memory.VRAM); // free(Memory.VRAM); emu_SFree(Memory.ROM); //free(Memory.ROM); Memory.RAM = NULL; Memory.SRAM = NULL; Memory.VRAM = NULL; Memory.ROM = NULL; } bool8 S9xLoadROM (const char *filename) { //FILE *stream = fopen(filename, "rb"); //if (!stream) // return (FALSE); //fseek(stream, 0, SEEK_END); //Memory.ROM_SIZE = ftell(stream); Memory.ROM_SIZE = emu_FileSize((char*)filename); Memory.ROM = (uint8 *) emu_SMalloc(Memory.ROM_SIZE); // We shrink if possible because we need all the memory we can get for savestates and buffers //uint8 *temp = (uint8 *)realloc(Memory.ROM, Memory.ROM_SIZE); //if (temp) //{ // Memory.ROM_MAX_SIZE = Memory.ROM_SIZE; // Memory.ROM = temp; //} if (!Memory.ROM || Memory.ROM_MAX_SIZE < Memory.ROM_SIZE) { //fclose(stream); return (FALSE); } //fseek(stream, 0, SEEK_SET); //fread(Memory.ROM, Memory.ROM_SIZE, 1, stream); //fclose(stream); emu_LoadFile((char*)filename, (char*)Memory.ROM, Memory.ROM_SIZE); emu_printf("loaded"); return InitROM(); } // BPS % UPS % IPS #if 0 static long ReadInt (Stream *r, unsigned nbytes) { long v = 0; while (nbytes--) { int c = r->get_char(); if (c == EOF) return (-1); v = (v << 8) | (c & 0xFF); } return (v); } static bool8 ReadIPSPatch (Stream *r, long offset, int32 &rom_size) { const int32 IPS_EOF = 0x00454F46l; int32 ofs; char fname[6]; fname[5] = 0; for (int i = 0; i < 5; i++) { int c = r->get_char(); if (c == EOF) return (0); fname[i] = (char) c; } if (strncmp(fname, "PATCH", 5)) return (0); for (;;) { long len, rlen; int rchar; ofs = ReadInt(r, 3); if (ofs == -1) return (0); if (ofs == IPS_EOF) break; ofs -= offset; len = ReadInt(r, 2); if (len == -1) return (0); if (len) { if (ofs + len > Memory.ROM_SIZE) return (0); while (len--) { rchar = r->get_char(); if (rchar == EOF) return (0); Memory.ROM[ofs++] = (uint8) rchar; } if (ofs > rom_size) rom_size = ofs; } else { rlen = ReadInt(r, 2); if (rlen == -1) return (0); rchar = r->get_char(); if (rchar == EOF) return (0); if (ofs + rlen > Memory.ROM_SIZE) return (0); while (rlen--) Memory.ROM[ofs++] = (uint8) rchar; if (ofs > rom_size) rom_size = ofs; } } ofs = ReadInt(r, 3); if (ofs != -1 && ofs - offset < rom_size) rom_size = ofs - offset; ROMIsPatched = 1; return (1); } /* void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &rom_size) { ROMIsPatched = false; FSTREAM patch_file = NULL; uint32 i; long offset = header ? 512 : 0; int ret; bool flag; char dir[_MAX_DIR + 1], drive[_MAX_DRIVE + 1], name[_MAX_FNAME + 1], ext[_MAX_EXT + 1], ips[_MAX_EXT + 3], fname[PATH_MAX + 1]; const char *n; _splitpath(rom_filename, drive, dir, name, ext); // IPS _makepath(fname, drive, dir, name, "ips"); if ((patch_file = OPEN_FSTREAM(fname, "rb")) != NULL) { printf("Using IPS patch %s", fname); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); return; } else printf(" failed!\n"); } if (_MAX_EXT > 6) { i = 0; flag = false; do { snprintf(ips, 8, "%03d.ips", i); _makepath(fname, drive, dir, name, ips); if (!(patch_file = OPEN_FSTREAM(fname, "rb"))) break; printf("Using IPS patch %s", fname); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); flag = true; } else { printf(" failed!\n"); break; } } while (++i < 1000); if (flag) return; } if (_MAX_EXT > 3) { i = 0; flag = false; do { snprintf(ips, _MAX_EXT + 2, "ips%d", i); if (strlen(ips) > _MAX_EXT) break; _makepath(fname, drive, dir, name, ips); if (!(patch_file = OPEN_FSTREAM(fname, "rb"))) break; printf("Using IPS patch %s", fname); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); flag = true; } else { printf(" failed!\n"); break; } } while (++i != 0); if (flag) return; } if (_MAX_EXT > 2) { i = 0; flag = false; do { snprintf(ips, 4, "ip%d", i); _makepath(fname, drive, dir, name, ips); if (!(patch_file = OPEN_FSTREAM(fname, "rb"))) break; printf("Using IPS patch %s", fname); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); flag = true; } else { printf(" failed!\n"); break; } } while (++i < 10); if (flag) return; } n = S9xGetFilename(".ips", PATCH_DIR); if ((patch_file = OPEN_FSTREAM(n, "rb")) != NULL) { printf("Using IPS patch %s", n); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); return; } else printf(" failed!\n"); } if (_MAX_EXT > 6) { i = 0; flag = false; do { snprintf(ips, 9, ".%03d.ips", i); n = S9xGetFilename(ips, PATCH_DIR); if (!(patch_file = OPEN_FSTREAM(n, "rb"))) break; printf("Using IPS patch %s", n); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); flag = true; } else { printf(" failed!\n"); break; } } while (++i < 1000); if (flag) return; } if (_MAX_EXT > 3) { i = 0; flag = false; do { snprintf(ips, _MAX_EXT + 3, ".ips%d", i); if (strlen(ips) > _MAX_EXT + 1) break; n = S9xGetFilename(ips, PATCH_DIR); if (!(patch_file = OPEN_FSTREAM(n, "rb"))) break; printf("Using IPS patch %s", n); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); flag = true; } else { printf(" failed!\n"); break; } } while (++i != 0); if (flag) return; } if (_MAX_EXT > 2) { i = 0; flag = false; do { snprintf(ips, 5, ".ip%d", i); n = S9xGetFilename(ips, PATCH_DIR); if (!(patch_file = OPEN_FSTREAM(n, "rb"))) break; printf("Using IPS patch %s", n); Stream *s = new fStream(patch_file); ret = ReadIPSPatch(s, offset, rom_size); s->closeStream(); if (ret) { printf("!\n"); flag = true; } else { printf(" failed!\n"); break; } } while (++i < 10); if (flag) return; } } */ #endif