GBA: Ability to boot directly into the BIOS
@@ -20,6 +20,7 @@ - Support varible speed (PWM) rumble
- Ability to cap fast forward speed - Finer control over FPS target - Holdable shortcut for rewinding one frame at a time + - Ability to boot directly into the BIOS Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit
@@ -633,10 +633,18 @@ return true;
} void GBAGetGameCode(struct GBA* gba, char* out) { + if (!gba->memory.rom) { + out[0] = '\0'; + return; + } memcpy(out, &((struct GBACartridge*) gba->memory.rom)->id, 4); } void GBAGetGameTitle(struct GBA* gba, char* out) { + if (!gba->memory.rom) { + strncpy(out, "(BIOS)", 12); + return; + } memcpy(out, &((struct GBACartridge*) gba->memory.rom)->title, 12); }
@@ -47,6 +47,7 @@ gba->memory.fullBios = 0;
gba->memory.wram = 0; gba->memory.iwram = 0; gba->memory.rom = 0; + gba->memory.romSize = 0; gba->memory.hw.p = gba; int i;
@@ -29,8 +29,13 @@ state->versionMagic = GBA_SAVESTATE_MAGIC;
state->biosChecksum = gba->biosChecksum; state->romCrc32 = gba->romCrc32; - state->id = ((struct GBACartridge*) gba->memory.rom)->id; - memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)); + if (gba->memory.rom) { + state->id = ((struct GBACartridge*) gba->memory.rom)->id; + memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)); + } else { + state->id = 0; + memset(state->title, 0, sizeof(state->title)); + } memcpy(state->cpu.gprs, gba->cpu->gprs, sizeof(state->cpu.gprs)); state->cpu.cpsr = gba->cpu->cpsr;@@ -67,8 +72,11 @@ if (state->cpu.gprs[ARM_PC] < SIZE_BIOS && state->cpu.gprs[ARM_PC] >= 0x20) {
return; } } - if (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title))) { + if (gba->memory.rom && state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title))) { GBALog(gba, GBA_LOG_WARN, "Savestate is for a different game"); + return; + } else if (!gba->memory.rom && state->id != 0) { + GBALog(gba, GBA_LOG_WARN, "Savestate is for a game, but no game loaded"); return; } if (state->romCrc32 != gba->romCrc32) {
@@ -167,13 +167,13 @@ if (threadContext->hasOverride) {
GBAOverrideApply(&gba, &threadContext->override); } - if (threadContext->bios && GBAIsBIOS(threadContext->bios)) { - GBALoadBIOS(&gba, threadContext->bios); - } - if (threadContext->patch && loadPatch(threadContext->patch, &patch)) { GBAApplyPatch(&gba, &patch); } + } + + if (threadContext->bios && GBAIsBIOS(threadContext->bios)) { + GBALoadBIOS(&gba, threadContext->bios); } if (threadContext->movie) {@@ -400,7 +400,9 @@ if (!threadContext->fpsTarget) {
threadContext->fpsTarget = _defaultFPSTarget; } - if (threadContext->rom && !GBAIsROM(threadContext->rom)) { + bool bootBios = threadContext->bootBios && threadContext->bios; + + if (threadContext->rom && (!GBAIsROM(threadContext->rom) || bootBios)) { threadContext->rom->close(threadContext->rom); threadContext->rom = 0; }@@ -427,7 +429,7 @@ }
} - if (!threadContext->rom) { + if (!threadContext->rom && !bootBios) { threadContext->state = THREAD_SHUTDOWN; return false; }
@@ -72,6 +72,7 @@ int activeKeys;
struct GBAAVStream* stream; struct Configuration* overrides; enum GBAIdleLoopOptimization idleOptimization; + bool bootBios; bool hasOverride; struct GBACartridgeOverride override;
@@ -247,7 +247,18 @@ m_dirmode = dirmode;
openGame(); } -void GameController::openGame() { +void GameController::bootBIOS() { + closeGame(); + m_fname = QString(); + m_dirmode = false; + openGame(true); +} + +void GameController::openGame(bool biosOnly) { + if (biosOnly && (!m_useBios || m_bios.isNull())) { + return; + } + m_gameOpen = true; m_pauseAfterFrame = false;@@ -261,25 +272,30 @@ m_threadContext.sync.audioWait = m_audioSync;
} m_threadContext.gameDir = 0; - m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData()); - if (m_dirmode) { - m_threadContext.gameDir = VDirOpen(m_threadContext.fname); - m_threadContext.stateDir = m_threadContext.gameDir; + m_threadContext.bootBios = biosOnly; + if (biosOnly) { + m_threadContext.fname = nullptr; } else { - m_threadContext.rom = VFileOpen(m_threadContext.fname, O_RDONLY); + m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData()); + if (m_dirmode) { + m_threadContext.gameDir = VDirOpen(m_threadContext.fname); + m_threadContext.stateDir = m_threadContext.gameDir; + } else { + m_threadContext.rom = VFileOpen(m_threadContext.fname, O_RDONLY); #if USE_LIBZIP - if (!m_threadContext.gameDir) { - m_threadContext.gameDir = VDirOpenZip(m_threadContext.fname, 0); - } + if (!m_threadContext.gameDir) { + m_threadContext.gameDir = VDirOpenZip(m_threadContext.fname, 0); + } #endif #if USE_LZMA - if (!m_threadContext.gameDir) { - m_threadContext.gameDir = VDirOpen7z(m_threadContext.fname, 0); - } + if (!m_threadContext.gameDir) { + m_threadContext.gameDir = VDirOpen7z(m_threadContext.fname, 0); + } #endif + } } - if (!m_bios.isNull() &&m_useBios) { + if (!m_bios.isNull() && m_useBios) { m_threadContext.bios = VFileDevice::open(m_bios, O_RDONLY); } else { m_threadContext.bios = nullptr;
@@ -102,7 +102,7 @@ void setUseBIOS(bool);
void loadPatch(const QString& path); void importSharkport(const QString& path); void exportSharkport(const QString& path); - void openGame(); + void bootBIOS(); void closeGame(); void setPaused(bool paused); void reset();@@ -148,6 +148,7 @@ void enableLogLevel(int);
void disableLogLevel(int); private slots: + void openGame(bool bios = false); void crashGame(const QString& crashMessage); void pollEvents();
@@ -482,7 +482,9 @@ MutexUnlock(&context->stateMutex);
foreach (QAction* action, m_gameActions) { action->setDisabled(false); } - appendMRU(context->fname); + if (context->fname) { + appendMRU(context->fname); + } updateTitle(); attachWidget(m_display);@@ -609,6 +611,7 @@ installEventFilter(m_shortcutController);
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open), "loadROM"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())), "loadBIOS"); addControlledAction(fileMenu, fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())), "loadPatch"); + addControlledAction(fileMenu, fileMenu->addAction(tr("Boot BIOS"), m_controller, SLOT(bootBIOS())), "bootBIOS"); m_mruMenu = fileMenu->addMenu(tr("Recent"));