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);