Add support to the ISX format, including symbols

This commit is contained in:
Lior Halphon 2020-04-25 22:48:48 +03:00
parent 36aa3f31b9
commit 152924e13f
12 changed files with 320 additions and 19 deletions

View File

@ -647,11 +647,16 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
- (void) loadROM - (void) loadROM
{ {
NSString *rom_warnings = [self captureOutputForBlock:^{ 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_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
GB_load_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]); GB_load_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]);
[self.cheatWindowController cheatsUpdated]; [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, [[[NSBundle mainBundle] pathForResource:@"registers" ofType:@"sym"] UTF8String]);
GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]); GB_debugger_load_symbol_file(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sym"] UTF8String]);
}]; }];

View File

@ -16,7 +16,7 @@
<key>CFBundleTypeIconFile</key> <key>CFBundleTypeIconFile</key>
<string>Cartridge</string> <string>Cartridge</string>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string>GameBoy Game</string> <string>Game Boy Game</string>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Viewer</string> <string>Viewer</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
@ -36,7 +36,7 @@
<key>CFBundleTypeIconFile</key> <key>CFBundleTypeIconFile</key>
<string>ColorCartridge</string> <string>ColorCartridge</string>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string>GameBoy Color Game</string> <string>Game Boy Color Game</string>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Viewer</string> <string>Viewer</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
@ -48,6 +48,26 @@
<key>NSDocumentClass</key> <key>NSDocumentClass</key>
<string>Document</string> <string>Document</string>
</dict> </dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>gbc</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>ColorCartridge</string>
<key>CFBundleTypeName</key>
<string>Game Boy ISX File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>com.github.liji32.sameboy.isx</string>
</array>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>Document</string>
</dict>
</array> </array>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>SameBoy</string> <string>SameBoy</string>
@ -85,7 +105,7 @@
<string>public.data</string> <string>public.data</string>
</array> </array>
<key>UTTypeDescription</key> <key>UTTypeDescription</key>
<string>GameBoy Game</string> <string>Game Boy Game</string>
<key>UTTypeIconFile</key> <key>UTTypeIconFile</key>
<string>Cartridge</string> <string>Cartridge</string>
<key>UTTypeIdentifier</key> <key>UTTypeIdentifier</key>
@ -104,7 +124,7 @@
<string>public.data</string> <string>public.data</string>
</array> </array>
<key>UTTypeDescription</key> <key>UTTypeDescription</key>
<string>GameBoy Color Game</string> <string>Game Boy Color Game</string>
<key>UTTypeIconFile</key> <key>UTTypeIconFile</key>
<string>ColorCartridge</string> <string>ColorCartridge</string>
<key>UTTypeIdentifier</key> <key>UTTypeIdentifier</key>
@ -117,6 +137,25 @@
</array> </array>
</dict> </dict>
</dict> </dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Game Boy ISX File</string>
<key>UTTypeIconFile</key>
<string>ColorCartridge</string>
<key>UTTypeIdentifier</key>
<string>com.github.liji32.sameboy.isx</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>isx</string>
</array>
</dict>
</dict>
</array> </array>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string> <string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>

View File

@ -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) void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
{ {
FILE *f = fopen(path, "r"); 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]; char symbol[length];
if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) { if (sscanf(line, "%x:%x %s", &bank, &address, symbol) == 3) {
bank &= 0x1FF; GB_debugger_add_symbol(gb, bank, address, symbol);
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);
}
} }
} }
free(line); free(line);

View File

@ -14,6 +14,8 @@
#define GB_debugger_call_hook(gb, addr) (void)addr #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_write_watchpoint(gb, addr, value) ((void)addr, (void)value)
#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr #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 #else
void GB_debugger_run(GB_gameboy_t *gb); void GB_debugger_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(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_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); 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); 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 /* GB_DISABLE_DEBUGGER */
#endif #endif

213
Core/gb.c
View File

@ -296,6 +296,219 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
return 0; 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) void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size)
{ {
gb->rom_size = (size + 0x3fff) & ~0x3fff; gb->rom_size = (size + 0x3fff) & ~0x3fff;

View File

@ -721,6 +721,7 @@ 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); 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); 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); 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_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_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);

View File

@ -8,7 +8,7 @@ char *do_open_rom_dialog(void)
NSWindow *key = [NSApp keyWindow]; NSWindow *key = [NSApp keyWindow];
NSOpenPanel *dialog = [NSOpenPanel openPanel]; NSOpenPanel *dialog = [NSOpenPanel openPanel];
dialog.title = @"Open ROM"; dialog.title = @"Open ROM";
dialog.allowedFileTypes = @[@"gb", @"gbc", @"sgb"]; dialog.allowedFileTypes = @[@"gb", @"gbc", @"sgb", @"isx"];
[dialog runModal]; [dialog runModal];
[key makeKeyAndOrderFront:nil]; [key makeKeyAndOrderFront:nil];
NSString *ret = [[[dialog URLs] firstObject] path]; NSString *ret = [[[dialog URLs] firstObject] path];

View File

@ -82,6 +82,7 @@ char *do_open_rom_dialog(void)
gtk_file_filter_add_pattern(filter, "*.gb"); gtk_file_filter_add_pattern(filter, "*.gb");
gtk_file_filter_add_pattern(filter, "*.gbc"); gtk_file_filter_add_pattern(filter, "*.gbc");
gtk_file_filter_add_pattern(filter, "*.sgb"); 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_filter_set_name(filter, "Game Boy ROMs");
gtk_file_chooser_add_filter(dialog, filter); gtk_file_chooser_add_filter(dialog, filter);

View File

@ -10,7 +10,7 @@ char *do_open_rom_dialog(void)
dialog.lStructSize = sizeof(dialog); dialog.lStructSize = sizeof(dialog);
dialog.lpstrFile = filename; dialog.lpstrFile = filename;
dialog.nMaxFile = sizeof(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.nFilterIndex = 1;
dialog.lpstrFileTitle = NULL; dialog.lpstrFileTitle = NULL;
dialog.nMaxFileTitle = 0; dialog.nMaxFileTitle = 0;

View File

@ -13,6 +13,7 @@
<array> <array>
<string>com.github.liji32.sameboy.gb</string> <string>com.github.liji32.sameboy.gb</string>
<string>com.github.liji32.sameboy.gbc</string> <string>com.github.liji32.sameboy.gbc</string>
<string>com.github.liji32.sameboy.isx</string>
</array> </array>
</dict> </dict>
</array> </array>

View File

@ -2,6 +2,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include <ctype.h>
#include <Core/gb.h> #include <Core/gb.h>
#include "get_image_for_rom.h" #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_log_callback(&gb, log_callback);
GB_set_color_correction_mode(&gb, GB_COLOR_CORRECTION_EMULATE_HARDWARE); 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); GB_free(&gb);
return 1; return 1;
} }

View File

@ -1,6 +1,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#include <ctype.h>
#include <OpenDialog/open_dialog.h> #include <OpenDialog/open_dialog.h>
#include <SDL.h> #include <SDL.h>
#include <Core/gb.h> #include <Core/gb.h>
@ -526,8 +527,23 @@ restart:
} }
bool error = false; bool error = false;
GB_debugger_clear_symbols(&gb);
start_capturing_logs(); 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); end_capturing_logs(true, error);
size_t path_length = strlen(filename); size_t path_length = strlen(filename);