all repos — mgba @ 466916729e8b89cb0a24ed62d6ddc90d69ca54f4

mGBA Game Boy Advance Emulator

GBA: Configurable game overrides
Jeffrey Pfau jeffrey@endrift.com
Tue, 13 Jan 2015 01:18:07 -0800
commit

466916729e8b89cb0a24ed62d6ddc90d69ca54f4

parent

eced06bc2abf974dafb5001526092369f6f44511

M CHANGESCHANGES

@@ -18,6 +18,7 @@ - Implemented BIOS routines SoftReset, RegisterRamReset, Diff8bitUnFilterWram, Diff8bitUnFilterVram, and Diff16bitUnFilter

- Support IPv6 - Save directory of last loaded file - Support BPS patches + - Configurable game overrides Bugfixes: - Qt: Fix issue with set frame sizes being the wrong height - Qt: Fix emulator crashing when full screen if a game is not running
A src/gba/gba-overrides.c

@@ -0,0 +1,199 @@

+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gba-overrides.h" + +#include "gba.h" +#include "gba-gpio.h" + + #include "util/configuration.h" + +static const struct GBACartridgeOverride _overrides[] = { + // Boktai: The Sun is in Your Hand + { "U3IJ", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + { "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + { "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + + // Boktai 2: Solar Boy Django + { "U32J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + { "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + { "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + + // Drill Dozer + { "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, + { "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, + + // Final Fantasy Tactics Advance + { "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 }, + + // Koro Koro Puzzle - Happy Panechu! + { "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 }, + + // Mega Man Battle Network + { "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E }, + + // Pokemon Ruby + { "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + + // Pokemon Sapphire + { "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + + // Pokemon Emerald + { "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + + // Pokemon Mystery Dungeon + { "B24J", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "B24E", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "B24P", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "B24U", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + + // Pokemon FireRed + { "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + + // Pokemon LeafGreen + { "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + + // RockMan EXE 4.5 - Real Operation + { "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 }, + + // Shin Bokura no Taiyou: Gyakushuu no Sabata + { "U33J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + + // Super Mario Advance 4 + { "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + + // Top Gun - Combat Zones + { "A2YE", SAVEDATA_FORCE_NONE, GPIO_NONE, -1 }, + + // Wario Ware Twisted + { "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + + // Yoshi's Universal Gravitation + { "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, -1 }, + { "KYGE", SAVEDATA_EEPROM, GPIO_TILT, -1 }, + { "KYGP", SAVEDATA_EEPROM, GPIO_TILT, -1 }, + + { { 0, 0, 0, 0 }, 0, 0, -1 } +}; + +bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOverride* override) { + override->savetype = SAVEDATA_AUTODETECT; + override->hardware = GPIO_NONE; + override->idleLoop = -1; + bool found; + + if (override->id[0] == 'F') { + // Classic NES Series + override->savetype = SAVEDATA_EEPROM; + found = true; + } else { + int i; + for (i = 0; _overrides[i].id[0]; ++i) { + if (memcmp(override->id, _overrides[i].id, sizeof(override->id)) == 0) { + *override = _overrides[i]; + found = true; + break; + } + } + } + + if (config) { + char sectionName[16]; + snprintf(sectionName, sizeof(sectionName), "override.%c%c%c%c", override->id[0], override->id[1], override->id[2], override->id[3]); + const char* savetype = ConfigurationGetValue(config, sectionName, "savetype"); + const char* hardware = ConfigurationGetValue(config, sectionName, "hardware"); + const char* idleLoop = ConfigurationGetValue(config, sectionName, "idleLoop"); + + if (savetype) { + if (strcasecmp(savetype, "SRAM") == 0) { + found = true; + override->savetype = SAVEDATA_SRAM; + } else if (strcasecmp(savetype, "EEPROM") == 0) { + found = true; + override->savetype = SAVEDATA_EEPROM; + } else if (strcasecmp(savetype, "FLASH512") == 0) { + found = true; + override->savetype = SAVEDATA_FLASH512; + } else if (strcasecmp(savetype, "FLASH1M") == 0) { + found = true; + override->savetype = SAVEDATA_FLASH1M; + } else if (strcasecmp(savetype, "NONE") == 0) { + found = true; + override->savetype = SAVEDATA_FORCE_NONE; + } + } + + if (hardware) { + char* end; + long type = strtoul(hardware, &end, 0); + if (end && !*end) { + override->hardware = type; + found = true; + } + } + + if (idleLoop) { + char* end; + uint32_t address = strtoul(idleLoop, &end, 16); + if (end && !*end) { + override->idleLoop = address; + found = true; + } + } + } + return found; +} + +void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* override) { + GBASavedataForceType(&gba->memory.savedata, override->savetype); + + if (override->hardware & GPIO_RTC) { + GBAGPIOInitRTC(&gba->memory.gpio); + } + + if (override->hardware & GPIO_GYRO) { + GBAGPIOInitGyro(&gba->memory.gpio); + } + + if (override->hardware & GPIO_RUMBLE) { + GBAGPIOInitRumble(&gba->memory.gpio); + } + + if (override->hardware & GPIO_LIGHT_SENSOR) { + GBAGPIOInitLightSensor(&gba->memory.gpio); + } + + if (override->hardware & GPIO_TILT) { + GBAGPIOInitTilt(&gba->memory.gpio); + } + + gba->busyLoop = override->idleLoop; +}
A src/gba/gba-overrides.h

@@ -0,0 +1,27 @@

+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GBA_OVERRIDES_H +#define GBA_OVERRIDES_H + +#include "util/common.h" + +#include "gba-savedata.h" + +struct GBACartridgeOverride { + char id[4]; + enum SavedataType savetype; + int hardware; + uint32_t idleLoop; +}; + +struct Configuration; +bool GBAOverrideFind(const struct Configuration*, struct GBACartridgeOverride* override); +bool GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* override); + +struct GBA; +void GBAOverrideApply(struct GBA*, const struct GBACartridgeOverride*); + +#endif
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -8,6 +8,7 @@

#include "arm.h" #include "gba.h" #include "gba-config.h" +#include "gba-overrides.h" #include "gba-serialize.h" #include "debugger/debugger.h"

@@ -141,6 +142,14 @@ }

if (threadContext->rom) { GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname); + + struct GBACartridgeOverride override; + const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom; + memcpy(override.id, &cart->id, sizeof(override.id)); + if (GBAOverrideFind(threadContext->overrides, &override)) { + GBAOverrideApply(&gba, &override); + } + if (threadContext->bios && GBAIsBIOS(threadContext->bios)) { GBALoadBIOS(&gba, threadContext->bios); }
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -70,6 +70,7 @@ struct VFile* patch;

const char* fname; int activeKeys; struct GBAAVStream* stream; + struct Configuration* overrides; // Run-time options int frameskip;
M src/gba/gba.csrc/gba/gba.c

@@ -24,115 +24,12 @@

static const size_t GBA_ROM_MAGIC_OFFSET = 2; static const uint8_t GBA_ROM_MAGIC[] = { 0x00, 0xEA }; -struct GBACartridgeOverride { - const char id[4]; - enum SavedataType type; - int gpio; - uint32_t busyLoop; -}; - -static const struct GBACartridgeOverride _overrides[] = { - // Boktai: The Sun is in Your Hand - { "U3IJ", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - { "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - { "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - - // Boktai 2: Solar Boy Django - { "U32J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - { "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - { "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - - // Drill Dozer - { "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, - { "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, - - // Final Fantasy Tactics Advance - { "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000418 }, - - // Koro Koro Puzzle - Happy Panechu! - { "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, -1 }, - - // Mega Man Battle Network - { "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E }, - - // Pokemon Ruby - { "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - - // Pokemon Sapphire - { "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - - // Pokemon Emerald - { "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - { "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, - - // Pokemon Mystery Dungeon - { "B24J", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "B24E", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "B24P", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "B24U", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - - // Pokemon FireRed - { "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - - // Pokemon LeafGreen - { "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - - // RockMan EXE 4.5 - Real Operation - { "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 }, - - // Shin Bokura no Taiyou: Gyakushuu no Sabata - { "U33J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, - - // Super Mario Advance 4 - { "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - { "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, - - // Top Gun - Combat Zones - { "A2YE", SAVEDATA_FORCE_NONE, GPIO_NONE, -1 }, - - // Wario Ware Twisted - { "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, - { "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, - { "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, - - // Yoshi's Universal Gravitation - { "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, -1 }, - { "KYGE", SAVEDATA_EEPROM, GPIO_TILT, -1 }, - { "KYGP", SAVEDATA_EEPROM, GPIO_TILT, -1 }, - - { { 0, 0, 0, 0 }, 0, 0, -1 } -}; - static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component); static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh); static void GBAProcessEvents(struct ARMCore* cpu); static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles); static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode); static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode); - -static void _checkOverrides(struct GBA* gba, uint32_t code); void GBACreate(struct GBA* gba) { gba->d.id = GBA_COMPONENT_MAGIC;

@@ -464,7 +361,6 @@ gba->memory.romSize = gba->pristineRomSize;

gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); GBASavedataInit(&gba->memory.savedata, sav); GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); - _checkOverrides(gba, ((struct GBACartridge*) gba->memory.rom)->id); // TODO: error check }

@@ -739,43 +635,3 @@ if (gba->debugger) {

ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP); } } - -void _checkOverrides(struct GBA* gba, uint32_t id) { - int i; - gba->busyLoop = -1; - if ((id & 0xFF) == 'F') { - GBALog(gba, GBA_LOG_DEBUG, "Found Classic NES Series game, using EEPROM saves"); - GBASavedataInitEEPROM(&gba->memory.savedata); - return; - } - for (i = 0; _overrides[i].id[0]; ++i) { - const uint32_t* overrideId = (const uint32_t*) _overrides[i].id; - if (*overrideId == id) { - GBALog(gba, GBA_LOG_DEBUG, "Found override for game %s!", _overrides[i].id); - GBASavedataForceType(&gba->memory.savedata, _overrides[i].type); - - if (_overrides[i].gpio & GPIO_RTC) { - GBAGPIOInitRTC(&gba->memory.gpio); - } - - if (_overrides[i].gpio & GPIO_GYRO) { - GBAGPIOInitGyro(&gba->memory.gpio); - } - - if (_overrides[i].gpio & GPIO_RUMBLE) { - GBAGPIOInitRumble(&gba->memory.gpio); - } - - if (_overrides[i].gpio & GPIO_LIGHT_SENSOR) { - GBAGPIOInitLightSensor(&gba->memory.gpio); - } - - if (_overrides[i].gpio & GPIO_TILT) { - GBAGPIOInitTilt(&gba->memory.gpio); - } - - gba->busyLoop = _overrides[i].busyLoop; - return; - } - } -}
M src/platform/perf-main.csrc/platform/perf-main.c

@@ -76,6 +76,7 @@ context.renderer = &renderer.d;

} context.debugger = createDebugger(&args, &context); + context.overrides = &config.configTable; char gameCode[5] = { 0 }; GBAConfigMap(&config, &opts);
M src/platform/qt/ConfigController.hsrc/platform/qt/ConfigController.h

@@ -75,6 +75,8 @@

QList<QString> getMRU() const; void setMRU(const QList<QString>& mru); + Configuration* overrides() { return &m_config.configTable; } // TODO: Make this not return the whole table + public slots: void setOption(const char* key, bool value); void setOption(const char* key, int value);
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -22,6 +22,7 @@ }

struct GBAAudio; struct GBAVideoSoftwareRenderer; +struct Configuration; class QThread;

@@ -53,6 +54,7 @@ bool audioSync() const { return m_audioSync; }

bool videoSync() const { return m_videoSync; } void setInputController(InputController* controller) { m_inputController = controller; } + void setOverrides(Configuration* overrides) { m_threadContext.overrides = overrides; } #ifdef USE_GDB_STUB ARMDebugger* debugger();
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -56,6 +56,7 @@ setWindowTitle(PROJECT_NAME);

setFocusPolicy(Qt::StrongFocus); m_controller = new GameController(this); m_controller->setInputController(&m_inputController); + m_controller->setOverrides(m_config->overrides()); QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer)); format.setSwapInterval(1);
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -103,6 +103,7 @@ renderer.events.bindings = &inputMap;

GBASDLInitBindings(&inputMap); GBASDLInitEvents(&renderer.events); GBASDLEventsLoadConfig(&renderer.events, &config.configTable); // TODO: Don't use this directly + context.overrides = &config.configTable; GBAThreadStart(&context);