[GB] Add support for per-game overrides (#1370)

* [GB] Add support for per-game overrides

Alleyway is a buggy game that does not work properly when a Game Boy
Printer is connected. In order to work around this issue, this adds
upport for built-in per-software Game Boy overrides. In addition, this
renames various variables to make their meaning clearer.

* This only supports built-in overrides. External INI files are not
  supported.
* Only the Game Boy Printer option is supported, this only takes effect
  on game startup and the general configuration option is restored when
  the game is unloaded.
* As a result, it is possible to override the per-game override by
  manually setting the option while the game is running, this is working
  as intended.
* Future refactors of the option handling will manage overrides better.
* Switch `gbPrinterEnabled` default to off.

Fixes #1368
This commit is contained in:
Fabrice de Gans 2024-11-24 12:15:26 -08:00 committed by GitHub
parent 611f3a3409
commit a8ec85d536
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 57 additions and 27 deletions

View File

@ -70,7 +70,7 @@ extern struct CoreOptions {
int skipSaveGameBattery = 1; int skipSaveGameBattery = 1;
int skipSaveGameCheats = 0; int skipSaveGameCheats = 0;
int useBios = 0; int useBios = 0;
int winGbPrinterEnabled = 1; int gbPrinterEnabled = 0;
uint32_t speedup_throttle = 100; uint32_t speedup_throttle = 100;
uint32_t speedup_frame_skip = 9; uint32_t speedup_frame_skip = 9;
uint32_t throttle = 100; uint32_t throttle = 100;

View File

@ -1416,7 +1416,7 @@ void gbWriteMemory(uint16_t address, uint8_t value)
EmuReseted = false; EmuReseted = false;
gbMemory[0xff02] = value; gbMemory[0xff02] = value;
if (gbSerialOn && (GetLinkMode() == LINK_GAMEBOY_IPC || GetLinkMode() == LINK_GAMEBOY_SOCKET if (gbSerialOn && (GetLinkMode() == LINK_GAMEBOY_IPC || GetLinkMode() == LINK_GAMEBOY_SOCKET
|| GetLinkMode() == LINK_DISCONNECTED || coreOptions.winGbPrinterEnabled)) { || GetLinkMode() == LINK_DISCONNECTED || coreOptions.gbPrinterEnabled)) {
gbSerialTicks = GBSERIAL_CLOCK_TICKS; gbSerialTicks = GBSERIAL_CLOCK_TICKS;

4
src/gb-over.ini Normal file
View File

@ -0,0 +1,4 @@
# Game Boy Overrrides
[ALLEY WAY]
gbPrinter = 0

View File

@ -590,7 +590,7 @@ void retro_init(void)
coreOptions.parseDebug = true; coreOptions.parseDebug = true;
coreOptions.cheatsEnabled = 0; coreOptions.cheatsEnabled = 0;
coreOptions.skipSaveGameBattery = 0; coreOptions.skipSaveGameBattery = 0;
coreOptions.winGbPrinterEnabled = 0; coreOptions.gbPrinterEnabled = 0;
struct retro_log_callback log; struct retro_log_callback log;
struct retro_rumble_interface rumble; struct retro_rumble_interface rumble;

View File

@ -189,7 +189,7 @@ struct option argOptions[] = {
{ "gb-emulator-type", required_argument, 0, OPT_GB_EMULATOR_TYPE }, { "gb-emulator-type", required_argument, 0, OPT_GB_EMULATOR_TYPE },
{ "gb-frame-skip", required_argument, 0, OPT_GB_FRAME_SKIP }, { "gb-frame-skip", required_argument, 0, OPT_GB_FRAME_SKIP },
{ "gb-palette-option", required_argument, 0, OPT_GB_PALETTE_OPTION }, { "gb-palette-option", required_argument, 0, OPT_GB_PALETTE_OPTION },
{ "gb-printer", no_argument, &coreOptions.winGbPrinterEnabled, 1 }, { "gb-printer", no_argument, &coreOptions.gbPrinterEnabled, 1 },
{ "gdb", required_argument, 0, 'G' }, { "gdb", required_argument, 0, 'G' },
{ "help", no_argument, &optPrintUsage, 1 }, { "help", no_argument, &optPrintUsage, 1 },
{ "ifb-filter", required_argument, 0, 'I' }, { "ifb-filter", required_argument, 0, 'I' },
@ -236,7 +236,7 @@ struct option argOptions[] = {
{ "no-speedup-mute", no_argument, 0, OPT_NO_SPEEDUP_MUTE }, { "no-speedup-mute", no_argument, 0, OPT_NO_SPEEDUP_MUTE },
{ "use-bios", no_argument, &coreOptions.useBios, 1 }, { "use-bios", no_argument, &coreOptions.useBios, 1 },
{ "verbose", required_argument, 0, 'v' }, { "verbose", required_argument, 0, 'v' },
{ "win-gb-printer-enabled", no_argument, &coreOptions.winGbPrinterEnabled, 1 }, { "win-gb-printer-enabled", no_argument, &coreOptions.gbPrinterEnabled, 1 },
{ NULL, no_argument, NULL, 0 } { NULL, no_argument, NULL, 0 }
@ -351,7 +351,7 @@ void LoadConfig()
coreOptions.speedup_throttle_frame_skip = ReadPref("speedupThrottleFrameSkip", 0); coreOptions.speedup_throttle_frame_skip = ReadPref("speedupThrottleFrameSkip", 0);
coreOptions.speedup_mute = ReadPref("speedupMute", 1); coreOptions.speedup_mute = ReadPref("speedupMute", 1);
coreOptions.useBios = ReadPrefHex("useBiosGBA"); coreOptions.useBios = ReadPrefHex("useBiosGBA");
coreOptions.winGbPrinterEnabled = ReadPref("gbPrinter", 0); coreOptions.gbPrinterEnabled = ReadPref("gbPrinter", 0);
int soundQuality = (ReadPrefHex("soundQuality", 1)); int soundQuality = (ReadPrefHex("soundQuality", 1));
switch (soundQuality) { switch (soundQuality) {

View File

@ -59,6 +59,7 @@ set(VBAM_WX_COMMON
${VBAM_GENERATED_DIR}/wx/builtin-over.h ${VBAM_GENERATED_DIR}/wx/builtin-over.h
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h ${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h ${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
${VBAM_GENERATED_DIR}/wx/gb-builtin-over.h
) )
if(NOT ZIP_PROGRAM) if(NOT ZIP_PROGRAM)
@ -642,6 +643,12 @@ add_custom_command(
DEPENDS ${CMAKE_SOURCE_DIR}/src/vba-over.ini ${BIN2C} DEPENDS ${CMAKE_SOURCE_DIR}/src/vba-over.ini ${BIN2C}
) )
add_custom_command(
OUTPUT ${VBAM_GENERATED_DIR}/wx/gb-builtin-over.h
COMMAND ${BIN2C} ${CMAKE_SOURCE_DIR}/src/gb-over.ini ${VBAM_GENERATED_DIR}/wx/gb-builtin-over.h gb_builtin_over
DEPENDS ${CMAKE_SOURCE_DIR}/src/gb-over.ini ${BIN2C}
)
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON} ${VBAM_LOCALIZABLE_WX_CONFIG_FILES}) set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON} ${VBAM_LOCALIZABLE_WX_CONFIG_FILES})
list(APPEND VBAM_LOCALIZABLE_FILES list(APPEND VBAM_LOCALIZABLE_FILES
audio/internal/dsound.cpp audio/internal/dsound.cpp

View File

@ -1990,7 +1990,7 @@ EVT_HANDLER(GameBoyAdvanceConfigure, "Game Boy Advance options...")
XRCCTRL(*dlg, "GameCode", wxControl) XRCCTRL(*dlg, "GameCode", wxControl)
->SetLabel(s); ->SetLabel(s);
cmt = wxString((const char*)&g_rom[0xa0], wxConvLibc, 12); cmt = wxString((const char*)&g_rom[0xa0], wxConvLibc, 12);
wxFileConfig* cfg = wxGetApp().overrides; wxFileConfig* cfg = wxGetApp().overrides_.get();
if (cfg->HasGroup(s)) { if (cfg->HasGroup(s)) {
cfg->SetPath(s); cfg->SetPath(s);
@ -2024,7 +2024,7 @@ EVT_HANDLER(GameBoyAdvanceConfigure, "Game Boy Advance options...")
if (panel->game_type() == IMAGE_GBA) { if (panel->game_type() == IMAGE_GBA) {
agbPrintEnable(OPTION(kPrefAgbPrint)); agbPrintEnable(OPTION(kPrefAgbPrint));
wxString s = wxString((const char*)&g_rom[0xac], wxConvLibc, 4); wxString s = wxString((const char*)&g_rom[0xac], wxConvLibc, 4);
wxFileConfig* cfg = wxGetApp().overrides; wxFileConfig* cfg = wxGetApp().overrides_.get();
bool chg; bool chg;
if (cfg->HasGroup(s)) { if (cfg->HasGroup(s)) {
@ -2297,7 +2297,7 @@ EVT_HANDLER(RetainAspect, "Retain aspect ratio when resizing")
EVT_HANDLER(Printer, "Enable printer emulation") EVT_HANDLER(Printer, "Enable printer emulation")
{ {
GetMenuOptionInt("Printer", &coreOptions.winGbPrinterEnabled, 1); GetMenuOptionInt("Printer", &coreOptions.gbPrinterEnabled, 1);
#if (defined __WIN32__ || defined _WIN32) #if (defined __WIN32__ || defined _WIN32)
#ifndef NO_LINK #ifndef NO_LINK
gbSerialFunction = gbStartLink; gbSerialFunction = gbStartLink;
@ -2305,7 +2305,7 @@ EVT_HANDLER(Printer, "Enable printer emulation")
gbSerialFunction = NULL; gbSerialFunction = NULL;
#endif #endif
#endif #endif
if (coreOptions.winGbPrinterEnabled) if (coreOptions.gbPrinterEnabled)
gbSerialFunction = gbPrinterSend; gbSerialFunction = gbPrinterSend;
update_opts(); update_opts();

View File

@ -312,7 +312,7 @@ std::array<Option, kNbOptions>& Option::All() {
Option(OptionID::kPrefFlashSize, &g_owned_opts.flash_size, 0, 1), Option(OptionID::kPrefFlashSize, &g_owned_opts.flash_size, 0, 1),
Option(OptionID::kPrefFrameSkip, &g_owned_opts.frame_skip, -1, 9), Option(OptionID::kPrefFrameSkip, &g_owned_opts.frame_skip, -1, 9),
Option(OptionID::kPrefGBPaletteOption, &gbPaletteOption, 0, 2), Option(OptionID::kPrefGBPaletteOption, &gbPaletteOption, 0, 2),
Option(OptionID::kPrefGBPrinter, &coreOptions.winGbPrinterEnabled, 0, 1), Option(OptionID::kPrefGBPrinter, &coreOptions.gbPrinterEnabled, 0, 1),
Option(OptionID::kPrefGDBBreakOnLoad, &g_owned_opts.gdb_break_on_load), Option(OptionID::kPrefGDBBreakOnLoad, &g_owned_opts.gdb_break_on_load),
Option(OptionID::kPrefGDBPort, &gopts.gdb_port, 0, 65535), Option(OptionID::kPrefGDBPort, &gopts.gdb_port, 0, 65535),
#ifndef NO_LINK #ifndef NO_LINK

View File

@ -287,6 +287,15 @@ void GameArea::LoadGame(const wxString& name)
gbApplyPatch(UTF8(pfn.GetFullPath())); gbApplyPatch(UTF8(pfn.GetFullPath()));
} }
// Apply overrides.
wxFileConfig* cfg = wxGetApp().gb_overrides_.get();
const std::string& title = g_gbCartData.title();
if (cfg->HasGroup(title)) {
cfg->SetPath(title);
coreOptions.gbPrinterEnabled = cfg->Read("gbPrinter", coreOptions.gbPrinterEnabled);
cfg->SetPath("/");
}
// start sound; this must happen before CPU stuff // start sound; this must happen before CPU stuff
gb_effects_config.enabled = OPTION(kSoundGBEnableEffects); gb_effects_config.enabled = OPTION(kSoundGBEnableEffects);
gb_effects_config.surround = OPTION(kSoundGBSurround); gb_effects_config.surround = OPTION(kSoundGBSurround);
@ -359,7 +368,7 @@ void GameArea::LoadGame(const wxString& name)
gbaUpdateRomSize(size); gbaUpdateRomSize(size);
} }
wxFileConfig* cfg = wxGetApp().overrides; wxFileConfig* cfg = wxGetApp().overrides_.get();
wxString id = wxString((const char*)&g_rom[0xac], wxConvLibc, 4); wxString id = wxString((const char*)&g_rom[0xac], wxConvLibc, 4);
if (cfg->HasGroup(id)) { if (cfg->HasGroup(id)) {
@ -456,7 +465,7 @@ void GameArea::LoadGame(const wxString& name)
SuspendScreenSaver(); SuspendScreenSaver();
// probably only need to do this for GB carts // probably only need to do this for GB carts
if (coreOptions.winGbPrinterEnabled) if (coreOptions.gbPrinterEnabled)
gbSerialFunction = gbPrinterSend; gbSerialFunction = gbPrinterSend;
// probably only need to do this for GBA carts // probably only need to do this for GBA carts
@ -666,6 +675,9 @@ void GameArea::UnloadGame(bool destruct)
if (loaded == IMAGE_GB) { if (loaded == IMAGE_GB) {
gbCleanUp(); gbCleanUp();
gbCheatRemoveAll(); gbCheatRemoveAll();
// Reset overrides.
coreOptions.gbPrinterEnabled = OPTION(kPrefGBPrinter);
} else if (loaded == IMAGE_GBA) { } else if (loaded == IMAGE_GBA) {
CPUCleanUp(); CPUCleanUp();
cheatsDeleteAll(false); cheatsDeleteAll(false);

View File

@ -31,6 +31,7 @@
#include "components/user_config/user_config.h" #include "components/user_config/user_config.h"
#include "core/gba/gbaSound.h" #include "core/gba/gbaSound.h"
#include "wx/gb-builtin-over.h"
#include "wx/builtin-over.h" #include "wx/builtin-over.h"
#include "wx/builtin-xrc.h" #include "wx/builtin-xrc.h"
#include "wx/config/cmdtab.h" #include "wx/config/cmdtab.h"
@ -470,13 +471,17 @@ bool wxvbamApp::OnInit() {
} }
} }
// load gb-over.ini
wxMemoryInputStream stream(gb_builtin_over, sizeof(gb_builtin_over));
gb_overrides_ = std::make_unique<wxFileConfig>(stream);
// load vba-over.ini // load vba-over.ini
// rather than dealing with wxConfig's broken search path, just use // rather than dealing with wxConfig's broken search path, just use
// the same one that the xrc overrides use // the same one that the xrc overrides use
// this also allows us to override a group at a time, add commments, and // this also allows us to override a group at a time, add commments, and
// add the file from which the group came // add the file from which the group came
wxMemoryInputStream mis(builtin_over, sizeof(builtin_over)); wxMemoryInputStream mis(builtin_over, sizeof(builtin_over));
overrides = new wxFileConfig(mis); overrides_ = std::make_unique<wxFileConfig>(mis);
wxRegEx cmtre; wxRegEx cmtre;
// not the most efficient thing to do: read entire file into a string // not the most efficient thing to do: read entire file into a string
// just to parse the comments out // just to parse the comments out
@ -486,8 +491,8 @@ bool wxvbamApp::OnInit() {
long grp_idx; long grp_idx;
#define CMT_RE_START wxT("(^|[\n\r])# ?([^\n\r]*)(\r?\n|\r)\\[") #define CMT_RE_START wxT("(^|[\n\r])# ?([^\n\r]*)(\r?\n|\r)\\[")
for (cont = overrides->GetFirstGroup(s, grp_idx); cont; for (cont = overrides_->GetFirstGroup(s, grp_idx); cont;
cont = overrides->GetNextGroup(s, grp_idx)) { cont = overrides_->GetNextGroup(s, grp_idx)) {
// apparently even MacOSX sometimes uses the old \r by itself // apparently even MacOSX sometimes uses the old \r by itself
wxString cmt(CMT_RE_START); wxString cmt(CMT_RE_START);
cmt += s + wxT("\\]"); cmt += s + wxT("\\]");
@ -497,7 +502,7 @@ bool wxvbamApp::OnInit() {
else else
cmt = wxEmptyString; cmt = wxEmptyString;
overrides->Write(s + wxT("/comment"), cmt); overrides_->Write(s + wxT("/comment"), cmt);
} }
if (vba_over.FileExists()) { if (vba_over.FileExists()) {
@ -513,10 +518,10 @@ bool wxvbamApp::OnInit() {
for (cont = ov.GetFirstGroup(s, grp_idx); cont; for (cont = ov.GetFirstGroup(s, grp_idx); cont;
cont = ov.GetNextGroup(s, grp_idx)) { cont = ov.GetNextGroup(s, grp_idx)) {
overrides->DeleteGroup(s); overrides_->DeleteGroup(s);
overrides->SetPath(s); overrides_->SetPath(s);
ov.SetPath(s); ov.SetPath(s);
overrides->Write(wxT("path"), GetConfigurationPath()); overrides_->Write(wxT("path"), GetConfigurationPath());
// apparently even MacOSX sometimes uses \r by itself // apparently even MacOSX sometimes uses \r by itself
wxString cmt(CMT_RE_START); wxString cmt(CMT_RE_START);
cmt += s + wxT("\\]"); cmt += s + wxT("\\]");
@ -526,15 +531,15 @@ bool wxvbamApp::OnInit() {
else else
cmt = wxEmptyString; cmt = wxEmptyString;
overrides->Write(wxT("comment"), cmt); overrides_->Write(wxT("comment"), cmt);
long ent_idx; long ent_idx;
for (cont = ov.GetFirstEntry(s, ent_idx); cont; for (cont = ov.GetFirstEntry(s, ent_idx); cont;
cont = ov.GetNextEntry(s, ent_idx)) cont = ov.GetNextEntry(s, ent_idx))
overrides->Write(s, ov.Read(s, wxEmptyString)); overrides_->Write(s, ov.Read(s, wxEmptyString));
ov.SetPath(wxT("/")); ov.SetPath(wxT("/"));
overrides->SetPath(wxT("/")); overrides_->SetPath(wxT("/"));
} }
} }
@ -822,7 +827,6 @@ wxvbamApp::~wxvbamApp() {
free(home); free(home);
home = NULL; home = NULL;
} }
delete overrides;
#ifndef NO_ONLINEUPDATES #ifndef NO_ONLINEUPDATES
shutdownAutoupdater(); shutdownAutoupdater();

View File

@ -1,11 +1,13 @@
#ifndef VBAM_WX_WXVBAM_H_ #ifndef VBAM_WX_WXVBAM_H_
#define VBAM_WX_WXVBAM_H_ #define VBAM_WX_WXVBAM_H_
#include <cstdio>
#include <ctime>
#include <list> #include <list>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <iostream>
#include <stdio.h>
#include <time.h>
#include <wx/log.h> #include <wx/log.h>
#include <wx/propdlg.h> #include <wx/propdlg.h>
#include <wx/datetime.h> #include <wx/datetime.h>
@ -106,7 +108,8 @@ public:
widgets::SdlPoller* sdl_poller() { return &sdl_poller_; } widgets::SdlPoller* sdl_poller() { return &sdl_poller_; }
// vba-over.ini // vba-over.ini
wxFileConfig* overrides = nullptr; std::unique_ptr<wxFileConfig> overrides_;
std::unique_ptr<wxFileConfig> gb_overrides_;
wxFileName rom_database; wxFileName rom_database;
wxFileName rom_database_scene; wxFileName rom_database_scene;