From 152924e13fe851c0824579989b8f59d4dbd5358f Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 25 Apr 2020 22:48:48 +0300 Subject: [PATCH] Add support to the ISX format, including symbols --- Cocoa/Document.m | 9 +- Cocoa/Info.plist | 47 +++++++- Core/debugger.c | 22 ++-- Core/debugger.h | 3 + Core/gb.c | 213 ++++++++++++++++++++++++++++++++++ Core/gb.h | 3 +- OpenDialog/cocoa.m | 2 +- OpenDialog/gtk.c | 1 + OpenDialog/windows.c | 2 +- QuickLook/Info.plist | 1 + QuickLook/get_image_for_rom.c | 18 ++- SDL/main.c | 18 ++- 12 files changed, 320 insertions(+), 19 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index ed3eaaf..ed52efb 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -647,11 +647,16 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample) - (void) loadROM { NSString *rom_warnings = [self captureOutputForBlock:^{ - GB_load_rom(&gb, [self.fileName UTF8String]); + GB_debugger_clear_symbols(&gb); + if ([[self.fileType pathExtension] isEqualToString:@"isx"]) { + GB_load_isx(&gb, [self.fileName UTF8String]); + } + else { + GB_load_rom(&gb, [self.fileName UTF8String]); + } GB_load_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]); GB_load_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]); [self.cheatWindowController cheatsUpdated]; - GB_debugger_clear_symbols(&gb); GB_debugger_load_symbol_file(&gb, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]); GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]); }]; diff --git a/Cocoa/Info.plist b/Cocoa/Info.plist index 1c7bdb5..44a21f0 100644 --- a/Cocoa/Info.plist +++ b/Cocoa/Info.plist @@ -16,7 +16,7 @@ CFBundleTypeIconFile Cartridge CFBundleTypeName - GameBoy Game + Game Boy Game CFBundleTypeRole Viewer LSItemContentTypes @@ -36,7 +36,7 @@ CFBundleTypeIconFile ColorCartridge CFBundleTypeName - GameBoy Color Game + Game Boy Color Game CFBundleTypeRole Viewer LSItemContentTypes @@ -48,6 +48,26 @@ NSDocumentClass Document + + CFBundleTypeExtensions + + gbc + + CFBundleTypeIconFile + ColorCartridge + CFBundleTypeName + Game Boy ISX File + CFBundleTypeRole + Viewer + LSItemContentTypes + + com.github.liji32.sameboy.isx + + LSTypeIsPackage + 0 + NSDocumentClass + Document + CFBundleExecutable SameBoy @@ -85,7 +105,7 @@ public.data UTTypeDescription - GameBoy Game + Game Boy Game UTTypeIconFile Cartridge UTTypeIdentifier @@ -104,7 +124,7 @@ public.data UTTypeDescription - GameBoy Color Game + Game Boy Color Game UTTypeIconFile ColorCartridge UTTypeIdentifier @@ -117,6 +137,25 @@ + + UTTypeConformsTo + + public.data + + UTTypeDescription + Game Boy ISX File + UTTypeIconFile + ColorCartridge + UTTypeIdentifier + com.github.liji32.sameboy.isx + UTTypeTagSpecification + + public.filename-extension + + isx + + + NSCameraUsageDescription SameBoy needs to access your camera to emulate the Game Boy Camera diff --git a/Core/debugger.c b/Core/debugger.c index ee27e88..caf0af1 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -2160,6 +2160,19 @@ void GB_debugger_handle_async_commands(GB_gameboy_t *gb) } } +void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol) +{ + bank &= 0x1FF; + + if (!gb->bank_symbols[bank]) { + gb->bank_symbols[bank] = GB_map_alloc(); + } + GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol); + if (allocated_symbol) { + GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol); + } +} + void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path) { FILE *f = fopen(path, "r"); @@ -2182,14 +2195,7 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path) char symbol[length]; if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) { - bank &= 0x1FF; - if (!gb->bank_symbols[bank]) { - gb->bank_symbols[bank] = GB_map_alloc(); - } - GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol); - if (allocated_symbol) { - GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol); - } + GB_debugger_add_symbol(gb, bank, address, symbol); } } free(line); diff --git a/Core/debugger.h b/Core/debugger.h index 4868df3..b6a12d9 100644 --- a/Core/debugger.h +++ b/Core/debugger.h @@ -14,6 +14,8 @@ #define GB_debugger_call_hook(gb, addr) (void)addr #define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value) #define GB_debugger_test_read_watchpoint(gb, addr) (void)addr +#define GB_debugger_add_symbol(gb, bank, address, symbol) ((void)bank, (void)address, (void)symbol) + #else void GB_debugger_run(GB_gameboy_t *gb); void GB_debugger_handle_async_commands(GB_gameboy_t *gb); @@ -22,6 +24,7 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb); void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value); void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr); const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr); +void GB_debugger_add_symbol(GB_gameboy_t *gb, uint16_t bank, uint16_t address, const char *symbol); #endif /* GB_DISABLE_DEBUGGER */ #endif diff --git a/Core/gb.c b/Core/gb.c index 2b22f9d..f1eae90 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -296,6 +296,219 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path) return 0; } +int GB_load_isx(GB_gameboy_t *gb, const char *path) +{ + FILE *f = fopen(path, "rb"); + if (!f) { + GB_log(gb, "Could not open ISX file: %s.\n", strerror(errno)); + return errno; + } + char magic[4]; +#define READ(x) if (fread(&x, sizeof(x), 1, f) != 1) goto error + fread(magic, 1, sizeof(magic), f); + + bool extended = *(uint32_t *)&magic == htonl('ISX '); + + fseek(f, extended? 0x20 : 0, SEEK_SET); + + + uint8_t *old_rom = gb->rom; + uint32_t old_size = gb->rom_size; + gb->rom = NULL; + gb->rom_size = 0; + + while (true) { + uint8_t record_type = 0; + if (fread(&record_type, sizeof(record_type), 1, f) != 1) break; + switch (record_type) { + case 0x01: { // Binary + uint16_t bank; + uint16_t address; + uint16_t length; + uint8_t byte; + READ(byte); + bank = byte; + if (byte >= 0x80) { + READ(byte); + bank |= byte << 8; + } + + READ(address); +#ifdef GB_BIG_ENDIAN + address = __builtin_bswap16(address); +#endif + address &= 0x3FFF; + + READ(length); +#ifdef GB_BIG_ENDIAN + length = __builtin_bswap16(length); +#endif + + size_t needed_size = bank * 0x4000 + address + length; + if (needed_size > 1024 * 1024 * 32) + goto error; + + if (gb->rom_size < needed_size) { + gb->rom = realloc(gb->rom, needed_size); + memset(gb->rom + gb->rom_size, 0, needed_size - gb->rom_size); + gb->rom_size = needed_size; + } + + if (fread(gb->rom + (bank * 0x4000 + address), length, 1, f) != 1) + goto error; + + break; + } + + case 0x11: { // Extended Binary + uint32_t address; + uint32_t length; + + READ(address); +#ifdef GB_BIG_ENDIAN + address = __builtin_bswap32(address); +#endif + + READ(length); +#ifdef GB_BIG_ENDIAN + length = __builtin_bswap32(length); +#endif + size_t needed_size = address + length; + if (needed_size > 1024 * 1024 * 32) + goto error; + if (gb->rom_size < needed_size) { + gb->rom = realloc(gb->rom, needed_size); + memset(gb->rom + gb->rom_size, 0, needed_size - gb->rom_size); + gb->rom_size = needed_size; + } + + if (fread(gb->rom + address, length, 1, f) != 1) + goto error; + + break; + } + + case 0x04: { // Symbol + uint16_t count; + uint8_t length; + char name[257]; + uint8_t flag; + uint16_t bank; + uint16_t address; + uint8_t byte; + READ(count); +#ifdef GB_BIG_ENDIAN + count = __builtin_bswap16(count); +#endif + while (count--) { + READ(length); + if (fread(name, length, 1, f) != 1) + goto error; + name[length] = 0; + READ(flag); // unused + + READ(byte); + bank = byte; + if (byte >= 0x80) { + READ(byte); + bank |= byte << 8; + } + + READ(address); +#ifdef GB_BIG_ENDIAN + address = __builtin_bswap16(address); +#endif + GB_debugger_add_symbol(gb, bank, address, name); + } + break; + } + + case 0x14: { // Extended Binary + uint16_t count; + uint8_t length; + char name[257]; + uint8_t flag; + uint32_t address; + READ(count); +#ifdef GB_BIG_ENDIAN + count = __builtin_bswap16(count); +#endif + while (count--) { + READ(length); + if (fread(name, length + 1, 1, f) != 1) + goto error; + name[length] = 0; + READ(flag); // unused + + READ(address); +#ifdef GB_BIG_ENDIAN + address = __builtin_bswap32(address); +#endif + // TODO: How to convert 32-bit addresses to Bank:Address? Needs to tell RAM and ROM apart + } + break; + } + + default: + goto done; + } + } +done:; +#undef READ + if (gb->rom_size == 0) goto error; + + size_t needed_size = (gb->rom_size + 0x3FFF) & ~0x3FFF; /* Round to bank */ + + /* And then round to a power of two */ + while (needed_size & (needed_size - 1)) { + /* I promise this works. */ + needed_size |= needed_size >> 1; + needed_size++; + } + + if (needed_size < 0x8000) { + needed_size = 0x8000; + } + + if (gb->rom_size < needed_size) { + gb->rom = realloc(gb->rom, needed_size); + memset(gb->rom + gb->rom_size, 0, needed_size - gb->rom_size); + gb->rom_size = needed_size; + } + + GB_configure_cart(gb); + + // Fix a common wrong MBC error + if (gb->rom[0x147] == 3) { // MBC1 + RAM + Battery + if (gb->rom_size == 64 * 0x4000) { + for (unsigned i = 63 * 0x4000; i < 64 * 0x4000; i++) { + if (gb->rom[i]) { + gb->rom[0x147] = 0x10; // MBC3 + RTC + RAM + Battery + GB_configure_cart(gb); + gb->rom[0x147] = 0x3; + GB_log(gb, "ROM uses MBC1 but appears to use all 64 banks, assuming MBC3\n"); + break; + } + } + } + } + + if (old_rom) { + free(old_rom); + } + + return 0; +error: + GB_log(gb, "Invalid or unsupported ISX file.\n"); + if (gb->rom) { + free(gb->rom); + gb->rom = old_rom; + gb->rom_size = old_size; + } + fclose(f); + return -1; +} + void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size) { gb->rom_size = (size + 0x3fff) & ~0x3fff; diff --git a/Core/gb.h b/Core/gb.h index 703a489..a83bb09 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -721,7 +721,8 @@ int GB_load_boot_rom(GB_gameboy_t *gb, const char *path); void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size); int GB_load_rom(GB_gameboy_t *gb, const char *path); void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size); - +int GB_load_isx(GB_gameboy_t *gb, const char *path); + int GB_save_battery_size(GB_gameboy_t *gb); int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size); int GB_save_battery(GB_gameboy_t *gb, const char *path); diff --git a/OpenDialog/cocoa.m b/OpenDialog/cocoa.m index 29a722c..76b9606 100644 --- a/OpenDialog/cocoa.m +++ b/OpenDialog/cocoa.m @@ -8,7 +8,7 @@ char *do_open_rom_dialog(void) NSWindow *key = [NSApp keyWindow]; NSOpenPanel *dialog = [NSOpenPanel openPanel]; dialog.title = @"Open ROM"; - dialog.allowedFileTypes = @[@"gb", @"gbc", @"sgb"]; + dialog.allowedFileTypes = @[@"gb", @"gbc", @"sgb", @"isx"]; [dialog runModal]; [key makeKeyAndOrderFront:nil]; NSString *ret = [[[dialog URLs] firstObject] path]; diff --git a/OpenDialog/gtk.c b/OpenDialog/gtk.c index d9163fc..5b1caa3 100644 --- a/OpenDialog/gtk.c +++ b/OpenDialog/gtk.c @@ -82,6 +82,7 @@ char *do_open_rom_dialog(void) gtk_file_filter_add_pattern(filter, "*.gb"); gtk_file_filter_add_pattern(filter, "*.gbc"); gtk_file_filter_add_pattern(filter, "*.sgb"); + gtk_file_filter_add_pattern(filter, "*.isx"); gtk_file_filter_set_name(filter, "Game Boy ROMs"); gtk_file_chooser_add_filter(dialog, filter); diff --git a/OpenDialog/windows.c b/OpenDialog/windows.c index 6bf9b89..52e281d 100644 --- a/OpenDialog/windows.c +++ b/OpenDialog/windows.c @@ -10,7 +10,7 @@ char *do_open_rom_dialog(void) dialog.lStructSize = sizeof(dialog); dialog.lpstrFile = filename; dialog.nMaxFile = sizeof(filename); - dialog.lpstrFilter = L"Game Boy ROMs\0*.gb;*.gbc;*.sgb\0All files\0*.*\0\0"; + dialog.lpstrFilter = L"Game Boy ROMs\0*.gb;*.gbc;*.sgb;*.isx\0All files\0*.*\0\0"; dialog.nFilterIndex = 1; dialog.lpstrFileTitle = NULL; dialog.nMaxFileTitle = 0; diff --git a/QuickLook/Info.plist b/QuickLook/Info.plist index 2cff196..b01aae1 100644 --- a/QuickLook/Info.plist +++ b/QuickLook/Info.plist @@ -13,6 +13,7 @@ com.github.liji32.sameboy.gb com.github.liji32.sameboy.gbc + com.github.liji32.sameboy.isx diff --git a/QuickLook/get_image_for_rom.c b/QuickLook/get_image_for_rom.c index 3950dac..b9f87ed 100755 --- a/QuickLook/get_image_for_rom.c +++ b/QuickLook/get_image_for_rom.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "get_image_for_rom.h" @@ -60,7 +61,22 @@ int get_image_for_rom(const char *filename, const char *boot_path, uint32_t *out GB_set_log_callback(&gb, log_callback); GB_set_color_correction_mode(&gb, GB_COLOR_CORRECTION_EMULATE_HARDWARE); - if (GB_load_rom(&gb, filename)) { + size_t length = strlen(filename); + char extension[4] = {0,}; + if (length > 4) { + if (filename[length - 4] == '.') { + extension[0] = tolower(filename[length - 3]); + extension[1] = tolower(filename[length - 2]); + extension[2] = tolower(filename[length - 1]); + } + } + if (strcmp(extension, "isx") == 0) { + if (GB_load_isx(&gb, filename)) { + GB_free(&gb); + return 1; + } + } + else if (GB_load_rom(&gb, filename)) { GB_free(&gb); return 1; } diff --git a/SDL/main.c b/SDL/main.c index 4ce2e59..e09630b 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -526,8 +527,23 @@ restart: } bool error = false; + GB_debugger_clear_symbols(&gb); start_capturing_logs(); - error = GB_load_rom(&gb, filename); + size_t length = strlen(filename); + char extension[4] = {0,}; + if (length > 4) { + if (filename[length - 4] == '.') { + extension[0] = tolower(filename[length - 3]); + extension[1] = tolower(filename[length - 2]); + extension[2] = tolower(filename[length - 1]); + } + } + if (strcmp(extension, "isx") == 0) { + error = GB_load_isx(&gb, filename); + } + else { + GB_load_rom(&gb, filename); + } end_capturing_logs(true, error); size_t path_length = strlen(filename);