Automatic model selection in the Cocoa and SDL frontends, closes #648

This commit is contained in:
Lior Halphon
2024-08-25 14:36:48 +03:00
parent f0bab07f26
commit b6c6b9ed54
8 changed files with 189 additions and 106 deletions

View File

@@ -6,6 +6,18 @@
#import "GBOSDView.h"
#import "GBDebuggerButton.h"
enum model {
MODEL_NONE,
MODEL_DMG,
MODEL_CGB,
MODEL_AGB,
MODEL_SGB,
MODEL_MGB,
MODEL_AUTO,
MODEL_QUICK_RESET = -1,
};
@class GBCheatWindowController;
@class GBPaletteView;
@class GBObjectView;

View File

@@ -48,16 +48,6 @@
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
/* Todo: Split into category files! This is so messy!!! */
enum model {
MODEL_NONE,
MODEL_DMG,
MODEL_CGB,
MODEL_AGB,
MODEL_SGB,
MODEL_MGB,
MODEL_QUICK_RESET = -1,
};
@interface Document ()
@property GBAudioClient *audioClient;
@@ -100,6 +90,7 @@ enum model {
bool _logToSideView;
bool _shouldClearSideView;
enum model _currentModel;
bool _usesAutoModel;
bool _rewind;
bool _modelsChanging;
@@ -263,6 +254,7 @@ static void debuggerReloadCallback(GB_gameboy_t *gb)
case MODEL_NONE:
case MODEL_QUICK_RESET:
case MODEL_AUTO:
case MODEL_CGB:
return (GB_model_t)[[NSUserDefaults standardUserDefaults] integerForKey:@"GBCGBModel"];
@@ -677,15 +669,46 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
GB_load_boot_rom(&_gb, [path UTF8String]);
}
- (enum model)bestModelForROM
{
uint8_t *rom = GB_get_direct_access(&_gb, GB_DIRECT_ACCESS_ROM, NULL, NULL);
if (!rom) return MODEL_CGB;
if (rom[0x143] & 0x80) { // Has CGB features
return MODEL_CGB;
}
if (rom[0x146] == 3) { // Has SGB features
return MODEL_SGB;
}
if (rom[0x14B] == 1) { // Nintendo-licensed (most likely has boot ROM palettes)
return MODEL_CGB;
}
if (rom[0x14B] == 0x33 &&
rom[0x144] == '0' &&
rom[0x145] == '1') { // Ditto
return MODEL_CGB;
}
return MODEL_DMG;
}
- (IBAction)reset:(id)sender
{
[self stop];
size_t old_width = GB_get_screen_width(&_gb);
if ([sender tag] != MODEL_NONE) {
if ([sender tag] > MODEL_NONE) {
/* User explictly selected a model, save the preference */
_currentModel = (enum model)[sender tag];
_usesAutoModel = _currentModel == MODEL_AUTO;
[[NSUserDefaults standardUserDefaults] setInteger:_currentModel forKey:@"GBEmulatedModel"];
}
/* Reload the ROM, SAV and SYM files */
[self loadROM];
if ([sender tag] == MODEL_QUICK_RESET) {
GB_quick_reset(&_gb);
}
@@ -699,16 +722,6 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
[self updateMinSize];
if ([sender tag] > MODEL_NONE) {
/* User explictly selected a model, save the preference */
[[NSUserDefaults standardUserDefaults] setBool:_currentModel == MODEL_DMG forKey:@"EmulateDMG"];
[[NSUserDefaults standardUserDefaults] setBool:_currentModel == MODEL_SGB forKey:@"EmulateSGB"];
[[NSUserDefaults standardUserDefaults] setBool:_currentModel == MODEL_AGB forKey:@"EmulateAGB"];
[[NSUserDefaults standardUserDefaults] setBool:_currentModel == MODEL_MGB forKey:@"EmulateMGB"];
}
/* Reload the ROM, SAV and SYM files */
[self loadROM];
[self start];
@@ -871,19 +884,10 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
[self observeStandardDefaultsKey:@"GBVolume" withBlock:^(id newValue) {
weakSelf->_volume = [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBVolume"];
}];
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateDMG"]) {
_currentModel = MODEL_DMG;
}
else if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateSGB"]) {
_currentModel = MODEL_SGB;
}
else if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateMGB"]) {
_currentModel = MODEL_MGB;
}
else {
_currentModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateAGB"]? MODEL_AGB : MODEL_CGB;
}
_currentModel = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBEmulatedModel"];
_usesAutoModel = _currentModel == MODEL_AUTO;
[self initCommon];
self.view.gb = &_gb;
@@ -1125,7 +1129,7 @@ static bool is_path_writeable(const char *path)
return true;
}
- (int) loadROM
- (int)loadROM
{
__block int ret = 0;
NSString *fileName = self.romPath;
@@ -1177,6 +1181,9 @@ static bool is_path_writeable(const char *path)
[GBWarningPopover popoverWithContents:rom_warnings onWindow:self.mainWindow];
}
_fileModificationTime = [[NSFileManager defaultManager] attributesOfItemAtPath:fileName error:nil][NSFileModificationDate];
if (_usesAutoModel) {
_currentModel = [self bestModelForROM];
}
return ret;
}
@@ -1250,7 +1257,7 @@ static bool is_path_writeable(const char *path)
return !GB_debugger_is_stopped(&_gb);
}
else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE && anItem.tag != MODEL_QUICK_RESET) {
[(NSMenuItem *)anItem setState:anItem.tag == _currentModel];
[(NSMenuItem *)anItem setState:(anItem.tag == _currentModel) || (anItem.tag == MODEL_AUTO && _usesAutoModel)];
}
else if ([anItem action] == @selector(interrupt:)) {
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {

View File

@@ -83,6 +83,8 @@ static uint32_t color_to_int(NSColor *color)
@"GBJoyConAutoPair": @YES,
@"GBJoyConsDefaultsToHorizontal": @YES,
@"GBEmulatedModel": @(MODEL_AUTO),
// Default themes
@"GBThemes": @{
@"Desert": @{

View File

@@ -218,6 +218,50 @@
<action selector="togglePause:" target="-1" id="osW-wt-QAa"/>
</connections>
</menuItem>
<menuItem title="Emulated Model" id="Cip-1S-4Zp">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Emulated Model" id="MtH-V4-vUi">
<items>
<menuItem title="Pick Automatically" tag="6" id="GXg-yH-3dl">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="ao2-FI-kk1"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="Nyl-zW-Uez"/>
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
</connections>
</menuItem>
<menuItem title="Game Boy Pocket/Light" tag="5" id="1bM-CT-hoW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="U7l-BM-kB1"/>
</connections>
</menuItem>
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>
</connections>
</menuItem>
<menuItem title="Game Boy Color" tag="2" id="hdG-Bl-8nJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xAz-cr-0u2"/>
</connections>
</menuItem>
<menuItem title="Game Boy Advance" tag="3" id="7jw-B1-tf5">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xQk-4e-kd7"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="QIS-av-Byy"/>
<menuItem title="Save State" id="Hdz-ut-okE">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -360,37 +404,6 @@
<action selector="copyScreenshot:" target="-1" id="XJC-EB-HNl"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="zk7-gf-LXN"/>
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
</connections>
</menuItem>
<menuItem title="Game Boy Pocket/Light" tag="5" id="1bM-CT-hoW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="U7l-BM-kB1"/>
</connections>
</menuItem>
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>
</connections>
</menuItem>
<menuItem title="Game Boy Color" tag="2" id="hdG-Bl-8nJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xAz-cr-0u2"/>
</connections>
</menuItem>
<menuItem title="Game Boy Advance" tag="3" id="7jw-B1-tf5">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xQk-4e-kd7"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="DPb-Sh-5tg"/>
<menuItem title="Start Audio Recording…" keyEquivalent="A" id="1UK-8n-QPP">
<connections>

View File

@@ -43,7 +43,7 @@ configuration_t configuration =
.scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR,
.blending_mode = GB_FRAME_BLENDING_MODE_ACCURATE,
.rewind_length = 60 * 2,
.model = MODEL_CGB,
.model = MODEL_AUTO,
.volume = 100,
.rumble_mode = GB_RUMBLE_CARTRIDGE_ONLY,
.default_scale = 2,

View File

@@ -83,6 +83,7 @@ typedef struct {
MODEL_AGB,
MODEL_SGB,
MODEL_MGB,
MODEL_AUTO,
MODEL_MAX,
} model;

View File

@@ -933,26 +933,35 @@ static void enter_help_menu(unsigned index)
static void cycle_model(unsigned index)
{
configuration.model++;
if (configuration.model == MODEL_MAX) {
configuration.model = 0;
switch (configuration.model) {
case MODEL_DMG: configuration.model = MODEL_MGB; break;
case MODEL_MGB: configuration.model = MODEL_SGB; break;
case MODEL_SGB: configuration.model = MODEL_CGB; break;
case MODEL_CGB: configuration.model = MODEL_AGB; break;
case MODEL_AGB: configuration.model = MODEL_AUTO; break;
case MODEL_AUTO: configuration.model = MODEL_DMG; break;
default: configuration.model = MODEL_AUTO;
}
pending_command = GB_SDL_RESET_COMMAND;
}
static void cycle_model_backwards(unsigned index)
{
if (configuration.model == 0) {
configuration.model = MODEL_MAX;
switch (configuration.model) {
case MODEL_MGB: configuration.model = MODEL_DMG; break;
case MODEL_SGB: configuration.model = MODEL_MGB; break;
case MODEL_CGB: configuration.model = MODEL_SGB; break;
case MODEL_AGB: configuration.model = MODEL_CGB; break;
case MODEL_AUTO: configuration.model = MODEL_AGB; break;
case MODEL_DMG: configuration.model = MODEL_AUTO; break;
default: configuration.model = MODEL_AUTO;
}
configuration.model--;
pending_command = GB_SDL_RESET_COMMAND;
}
static const char *current_model_string(unsigned index)
{
return GB_inline_const(const char *[], {"Game Boy", "Game Boy Color", "Game Boy Advance", "Super Game Boy", "Game Boy Pocket"})
return GB_inline_const(const char *[], {"Game Boy", "Game Boy Color", "Game Boy Advance", "Super Game Boy", "Game Boy Pocket", "Pick Automatically"})
[configuration.model];
}

View File

@@ -134,24 +134,21 @@ static void log_capture_callback(GB_gameboy_t *gb, const char *string, GB_log_at
captured_log[current_len + len_to_add] = 0;
}
static void start_capturing_logs(void)
static void *start_capturing_logs(void)
{
if (captured_log != NULL) {
free(captured_log);
}
void *previous = captured_log;
captured_log = malloc(1);
captured_log[0] = 0;
GB_set_log_callback(&gb, log_capture_callback);
return previous;
}
static const char *end_capturing_logs(bool show_popup, bool should_exit, uint32_t popup_flags, const char *title)
static void end_capturing_logs(bool show_popup, bool should_exit, uint32_t popup_flags, const char *title, void *previous)
{
GB_set_log_callback(&gb, console_supported? log_callback : NULL);
if (captured_log[0] == 0) {
free(captured_log);
captured_log = NULL;
if (!previous) {
GB_set_log_callback(&gb, console_supported? log_callback : NULL);
}
else {
if (captured_log[0] != 0) {
if (show_popup) {
SDL_ShowSimpleMessageBox(popup_flags, title, captured_log, window);
}
@@ -159,7 +156,8 @@ static const char *end_capturing_logs(bool show_popup, bool should_exit, uint32_
exit(1);
}
}
return captured_log;
free(captured_log);
captured_log = previous;
}
static void update_palette(void)
@@ -620,7 +618,7 @@ static bool handle_pending_command(void)
save_extension[2] += command_parameter;
replace_extension(filename, strlen(filename), save_path, save_extension);
start_capturing_logs();
void *previous = start_capturing_logs();
bool success;
if (pending_command == GB_SDL_LOAD_STATE_COMMAND) {
int result = GB_load_state(&gb, save_path);
@@ -639,25 +637,28 @@ static bool handle_pending_command(void)
end_capturing_logs(true,
false,
success? SDL_MESSAGEBOX_INFORMATION : SDL_MESSAGEBOX_ERROR,
success? "Notice" : "Error");
success? "Notice" : "Error",
previous);
if (success) {
show_osd_text(pending_command == GB_SDL_LOAD_STATE_COMMAND? "State loaded" : "State saved");
}
return false;
}
case GB_SDL_LOAD_STATE_FROM_FILE_COMMAND:
start_capturing_logs();
case GB_SDL_LOAD_STATE_FROM_FILE_COMMAND: {
void *previous = start_capturing_logs();
bool success = GB_load_state(&gb, dropped_state_file) == 0;
end_capturing_logs(true,
false,
success? SDL_MESSAGEBOX_INFORMATION : SDL_MESSAGEBOX_ERROR,
success? "Notice" : "Error");
success? "Notice" : "Error",
previous);
SDL_free(dropped_state_file);
if (success) {
show_osd_text("State loaded");
}
return false;
}
case GB_SDL_NO_COMMAND:
return false;
@@ -697,18 +698,20 @@ static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type)
use_built_in = GB_load_boot_rom(gb, path);
}
if (use_built_in) {
start_capturing_logs();
void *previous = start_capturing_logs();
if (GB_load_boot_rom(gb, resource_path(names[type]))) {
if (type == GB_BOOT_ROM_CGB_E) {
end_capturing_logs(false, false, 0, NULL, previous);
load_boot_rom(gb, GB_BOOT_ROM_CGB);
return;
}
if (type == GB_BOOT_ROM_AGB_0) {
end_capturing_logs(false, false, 0, NULL, previous);
load_boot_rom(gb, GB_BOOT_ROM_AGB);
return;
}
}
end_capturing_logs(true, false, SDL_MESSAGEBOX_ERROR, "Error");
end_capturing_logs(true, false, SDL_MESSAGEBOX_ERROR, "Error", previous);
}
}
@@ -752,13 +755,34 @@ static void debugger_reload_callback(GB_gameboy_t *gb)
GB_reset(gb);
}
static void run(void)
static GB_model_t model_to_use(void)
{
SDL_ShowCursor(SDL_DISABLE);
GB_model_t model;
pending_command = GB_SDL_NO_COMMAND;
restart:
model = (GB_model_t [])
typeof(configuration.model) gui_model = configuration.model;
if (gui_model == MODEL_AUTO) {
uint8_t *rom = GB_get_direct_access(&gb, GB_DIRECT_ACCESS_ROM, NULL, NULL);
if (!rom) {
gui_model = MODEL_CGB;
}
else if (rom[0x143] & 0x80) { // Has CGB features
gui_model = MODEL_CGB;
}
else if (rom[0x146] == 3) { // Has SGB features
gui_model = MODEL_SGB;
}
else if (rom[0x14B] == 1) { // Nintendo-licensed (most likely has boot ROM palettes)
gui_model = MODEL_CGB;
}
else if (rom[0x14B] == 0x33 &&
rom[0x144] == '0' &&
rom[0x145] == '1') { // Ditto
gui_model = MODEL_CGB;
}
else {
gui_model = MODEL_DMG;
}
}
return (GB_model_t [])
{
[MODEL_DMG] = GB_MODEL_DMG_B,
[MODEL_CGB] = GB_MODEL_CGB_0 + configuration.cgb_revision,
@@ -770,7 +794,16 @@ restart:
[SGB_PAL] = GB_MODEL_SGB_PAL,
[SGB_2] = GB_MODEL_SGB2,
}[configuration.sgb_revision],
}[configuration.model];
}[gui_model];
}
static void run(void)
{
SDL_ShowCursor(SDL_DISABLE);
GB_model_t model;
pending_command = GB_SDL_NO_COMMAND;
restart:;
model = model_to_use();
if (GB_is_inited(&gb)) {
if (doing_hot_swap) {
@@ -819,7 +852,7 @@ restart:
bool error = false;
GB_debugger_clear_symbols(&gb);
start_capturing_logs();
void *previous = start_capturing_logs();
size_t path_length = strlen(filename);
char extension[4] = {0,};
if (path_length > 4) {
@@ -840,6 +873,11 @@ restart:
else {
GB_load_rom(&gb, filename);
}
GB_model_t updated_model = model_to_use(); // Could change after loading ROM with auto setting
if (model != updated_model) {
model = updated_model;
GB_switch_model_and_reset(&gb, model);
}
/* Configure battery */
char battery_save_path[path_length + 5]; /* At the worst case, size is strlen(path) + 4 bytes for .sav + NULL */
@@ -856,7 +894,7 @@ restart:
replace_extension(filename, path_length, cheat_path, ".cht");
GB_load_cheats(&gb, cheat_path);
end_capturing_logs(true, error, SDL_MESSAGEBOX_WARNING, "Warning");
end_capturing_logs(true, error, SDL_MESSAGEBOX_WARNING, "Warning", previous);
static char start_text[64];
static char title[17];
@@ -958,14 +996,15 @@ static void handle_model_option(const char *model_string)
GB_model_t model;
const char *description;
} name_to_model[] = {
{"auto", -1, "Pick automatically"},
{"dmg-b", GB_MODEL_DMG_B, "Game Boy, DMG-CPU B"},
{"dmg", GB_MODEL_DMG_B, "Alias of dmg-b"},
{"sgb-ntsc", GB_MODEL_SGB_NTSC, "Super Game Boy (NTSC)"},
{"sgb-pal", GB_MODEL_SGB_PAL, "Super Game Boy (PAL"},
{"sgb-pal", GB_MODEL_SGB_PAL, "Super Game Boy (PAL)"},
{"sgb2", GB_MODEL_SGB2, "Super Game Boy 2"},
{"sgb", GB_MODEL_SGB, "Alias of sgb-ntsc"},
{"mgb", GB_MODEL_MGB, "Game Boy Pocket/Light"},
{"cgb-0", GB_MODEL_CGB_0, "Game Boy Color, CPU CGB 0"},
{"cgb-0", GB_MODEL_CGB_0, "Game Boy Color, CPU CGB"},
{"cgb-a", GB_MODEL_CGB_A, "Game Boy Color, CPU CGB A"},
{"cgb-b", GB_MODEL_CGB_B, "Game Boy Color, CPU CGB B"},
{"cgb-c", GB_MODEL_CGB_C, "Game Boy Color, CPU CGB C"},
@@ -1028,6 +1067,7 @@ static void handle_model_option(const char *model_string)
break;
default:
configuration.model = MODEL_AUTO;
break;
}
}
@@ -1093,7 +1133,6 @@ int main(int argc, char **argv)
configuration.default_scale %= GB_SDL_DEFAULT_SCALE_MAX + 1;
configuration.blending_mode %= GB_FRAME_BLENDING_MODE_ACCURATE + 1;
configuration.highpass_mode %= GB_HIGHPASS_MAX;
configuration.model %= MODEL_MAX;
configuration.sgb_revision %= SGB_MAX;
configuration.dmg_palette %= 5;
if (configuration.dmg_palette) {