From 15e5b4bbb8263f486dd9a5e035bb6a6702553a1c Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 2 Feb 2018 14:35:19 -1000 Subject: [PATCH] NESEmulator save fixes --- NESEmulator/CMakeLists.txt | 4 +- NESEmulator/CNESEmulator.cpp | 206 ++++++++++++++++++++++++++++---- NESEmulator/CNESEmulator.hpp | 31 +++-- NESEmulator/apu.c | 172 ++++++++++++++++++++++++++ NESEmulator/fixNES | 2 +- NESEmulator/ppu.c | 8 ++ Runtime/Input/CFinalInput.hpp | 4 +- Runtime/MP1/CFrontEndUI.cpp | 48 ++++---- Runtime/MP1/CFrontEndUI.hpp | 2 + Runtime/MP1/CQuitGameScreen.cpp | 1 + hecl | 2 +- 11 files changed, 414 insertions(+), 66 deletions(-) create mode 100644 NESEmulator/apu.c create mode 100644 NESEmulator/ppu.c diff --git a/NESEmulator/CMakeLists.txt b/NESEmulator/CMakeLists.txt index eb83fd9e8..6ff49c966 100644 --- a/NESEmulator/CMakeLists.txt +++ b/NESEmulator/CMakeLists.txt @@ -1,6 +1,6 @@ include_directories(${CMAKE_SOURCE_DIR}/DataSpec ${CMAKE_SOURCE_DIR}/Runtime ${CMAKE_CURRENT_SOURCE_DIR}) file(GLOB MAPPER_SRCS fixNES/mapper/*.c) add_library(NESEmulator CNESEmulator.hpp CNESEmulator.cpp CNESShader.hpp CNESShader.cpp malloc.h - fixNES/apu.c fixNES/audio_fds.c fixNES/audio_mmc5.c fixNES/audio_vrc6.c fixNES/audio_vrc7.c - fixNES/audio_n163.c fixNES/audio_s5b.c fixNES/cpu.c fixNES/ppu.c fixNES/mem.c fixNES/input.c + apu.c fixNES/audio_fds.c fixNES/audio_mmc5.c fixNES/audio_vrc6.c fixNES/audio_vrc7.c + fixNES/audio_n163.c fixNES/audio_s5b.c fixNES/cpu.c ppu.c fixNES/mem.c fixNES/input.c fixNES/mapper.c fixNES/mapperList.c fixNES/fm2play.c fixNES/vrc_irq.c ${MAPPER_SRCS}) diff --git a/NESEmulator/CNESEmulator.cpp b/NESEmulator/CNESEmulator.cpp index 2398902e4..c5a4e4777 100644 --- a/NESEmulator/CNESEmulator.cpp +++ b/NESEmulator/CNESEmulator.cpp @@ -31,7 +31,7 @@ extern "C" #include "fixNES/mapper_h/nsf.h" /* - * Copyright (C) 2017 FIX94 + * Portions Copyright (C) 2017 FIX94 * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -77,7 +77,7 @@ static uint32_t emuPrgRAMsize = 0; uint32_t textureImage[0xF000]; bool nesPause = false; bool ppuDebugPauseFrame = false; -bool doOverscan = true; +bool doOverscan = false; bool nesPAL = false; bool nesEmuNSFPlayback = false; @@ -139,6 +139,9 @@ static uint16_t vrc7Clock = 1; extern bool fdsSwitch; +bool apuCycleURDE(); +uint8_t* ppuGetVRAM(); + int audioUpdate() { if (!EmulatorInst) @@ -256,6 +259,7 @@ void CNESEmulator::InitializeEmulator() CGraphics::CommitResources([this](boo::IGraphicsDataFactory::Context& ctx) { + // Nearest-neighbor FTW! m_texture = ctx.newDynamicTexture(VISIBLE_DOTS, linesToDraw, boo::TextureFormat::RGBA8, boo::TextureClampMode::ClampToEdgeNearest); @@ -393,8 +397,9 @@ size_t CNESEmulator::supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t static int catchupFrames = 0; #endif -void CNESEmulator::NesEmuMainLoop() +bool CNESEmulator::NesEmuMainLoop() { + bool drawFrame = false; int start = GetTickCount(); int loopCount = 0; do @@ -406,6 +411,7 @@ void CNESEmulator::NesEmuMainLoop() #endif //printf("LC RENDER: %d\n", loopCount); m_texture->load(textureImage, visibleImg); + drawFrame = true; emuRenderFrame = false; #if CATCHUP_SKIP if (catchupFrames) @@ -417,26 +423,36 @@ void CNESEmulator::NesEmuMainLoop() ++loopCount; if(mainClock == cpuCycleTimer) { - //runs every 8th cpu clock - if(apuClock == 8) + //URDE uses this loop to pre-fill audio buffers, + //rather than executing multiple frame loops + bool breakout = false; + do { - if(!apuCycle()) + //runs every 8th cpu clock + if(apuClock == 8) { + if(!apuCycleURDE()) + { #if (WINDOWS_BUILD && DEBUG_MAIN_CALLS) - emuMainTimesSkipped++; + emuMainTimesSkipped++; #endif #if CATCHUP_SKIP - catchupFrames = 0; + catchupFrames = 0; #endif - //printf("LC SKIP\n"); - break; + //printf("LC SKIP\n"); + breakout = true; + break; + } + apuClock = 1; } - apuClock = 1; - } - else - apuClock++; - //runs every cpu cycle - apuClockTimers(); + else + apuClock++; + //runs every cpu cycle + apuClockTimers(); + } while (emuSkipVsync); + if (breakout) + break; + //main CPU clock if(!cpuCycle()) exit(EXIT_SUCCESS); @@ -499,7 +515,7 @@ void CNESEmulator::NesEmuMainLoop() } #if 1 - if ((loopCount % 5000) == 0 && GetTickCount() - start >= 14) + if ((loopCount % 10000) == 0 && GetTickCount() - start >= 14) { #if CATCHUP_SKIP if (catchupFrames < 50) @@ -531,6 +547,8 @@ void CNESEmulator::NesEmuMainLoop() } emuMainFrameStart = end; #endif + + return drawFrame; } static void nesEmuFdsSetup(uint8_t *src, uint8_t *dst) @@ -755,16 +773,142 @@ void CNESEmulator::DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen, u8 des void CNESEmulator::ProcessUserInput(const CFinalInput& input, int) { + if (input.ControllerIdx() != 0) + return; + + if (GetPasswordEntryState() != EPasswordEntryState::NotPasswordScreen) + { + // Don't swap A/B + inValReads[BUTTON_A] = input.DA(); + inValReads[BUTTON_B] = input.DB(); + } + else + { + // Prime controls (B jumps, A shoots) + inValReads[BUTTON_B] = input.DA() | input.DY(); + inValReads[BUTTON_A] = input.DB() | input.DX(); + } + inValReads[BUTTON_UP] = input.DDPUp() | input.DLAUp(); inValReads[BUTTON_DOWN] = input.DDPDown() | input.DLADown(); inValReads[BUTTON_LEFT] = input.DDPLeft() | input.DLALeft(); inValReads[BUTTON_RIGHT] = input.DDPRight() | input.DLARight(); - inValReads[BUTTON_A] = input.DA(); - inValReads[BUTTON_B] = input.DB(); inValReads[BUTTON_SELECT] = input.DZ(); inValReads[BUTTON_START] = input.DStart(); - if (input.PL()) - x20_wantsQuit = true; +} + +bool CNESEmulator::CheckForGameOver(const u8* vram, u8* passwordOut) +{ + // "PASS WORD" + if (memcmp(vram + 0x14B, "\x19\xa\x1c\x1c\xff\x20\x18\x1b\xd", 9)) + return false; + + int chOff = 0; + int encOff = 0; + u8 pwOut[18]; + for (int i=0 ; i<24 ; ++i) + { + u8 chName = vram[0x1A9 + chOff]; + ++chOff; + if (chOff == 0x6 || chOff == 0x46) + ++chOff; // mid-line space + else if (chOff == 0xd) + chOff = 64; // 2nd line + + if (chName > 0x3f) + return false; + + switch (i & 0x3) + { + case 0: + pwOut[encOff] = chName; + break; + case 1: + pwOut[encOff] |= chName << 6; + ++encOff; + pwOut[encOff] = chName >> 2; + break; + case 2: + pwOut[encOff] |= chName << 4; + ++encOff; + pwOut[encOff] = chName >> 4; + break; + case 3: + pwOut[encOff] |= chName << 2; + ++encOff; + break; + default: + break; + } + } + + if (passwordOut) + memmove(passwordOut, pwOut, 18); + return true; +} + +CNESEmulator::EPasswordEntryState CNESEmulator::CheckForPasswordEntryScreen(const u8* vram) +{ + // "PASS WORD PLEASE" + if (memcmp(vram + 0x88, "\x19\xa\x1c\x1c\xff\x20\x18\x1b\xd\xff\x19\x15\xe\xa\x1c\xe", 16)) + return EPasswordEntryState::NotPasswordScreen; + + for (int i=0 ; i<13 ; ++i) + if (vram[0x109 + i] < 0x40 || vram[0x149 + i] < 0x40) + return EPasswordEntryState::Entered; + + return EPasswordEntryState::NotEntered; +} + +bool CNESEmulator::SetPasswordIntoEntryScreen(u8* vram, u8* wram, const u8* password) +{ + if (CheckForPasswordEntryScreen(vram) != EPasswordEntryState::NotEntered) + return false; + + int i; + for (i=0 ; i<18 ; ++i) + if (password[i]) + break; + if (i == 18) + return false; + + int encOff = 0; + int chOff = 0; + u32 lastWord = 0; + for (i=0 ; i<24 ; ++i) + { + switch (i & 0x3) + { + case 0: + lastWord = password[encOff]; + ++encOff; + break; + case 1: + lastWord = (lastWord >> 6) | (password[encOff] << 2); + ++encOff; + break; + case 2: + lastWord = (lastWord >> 4) | (password[encOff] << 4); + ++encOff; + break; + case 3: + lastWord = (lastWord >> 6); + break; + default: + break; + } + + u8 name = u8(lastWord & 0x3f); + wram[0x99a + i] = name; + vram[0x109 + chOff] = name; + ++chOff; + if (chOff == 0x6 || chOff == 0x46) + ++chOff; // mid-line space + else if (chOff == 0xd) + chOff = 64; // 2nd line + } + + return true; } void CNESEmulator::Update() @@ -779,7 +923,19 @@ void CNESEmulator::Update() } else { - NesEmuMainLoop(); + bool gameOver = CheckForGameOver(ppuGetVRAM(), x21_passwordFromNES); + x34_passwordEntryState = CheckForPasswordEntryScreen(ppuGetVRAM()); + if (x34_passwordEntryState == EPasswordEntryState::NotEntered && x38_passwordPending) + { + SetPasswordIntoEntryScreen(ppuGetVRAM(), emuPrgRAM, x39_passwordToNES); + x38_passwordPending = false; + } + if (gameOver && !x20_gameOver) + for (int i=0 ; i<3 ; ++i) // Three draw loops to ensure password display + while (!NesEmuMainLoop()) {} + else + NesEmuMainLoop(); + x20_gameOver = gameOver; } } @@ -801,10 +957,10 @@ void CNESEmulator::Draw(const zeus::CColor& mulColor, bool filtering) CGraphics::DrawArray(0, 4); } -void CNESEmulator::LoadState(const u8* state) +void CNESEmulator::LoadPassword(const u8* state) { - memmove(x39_loadState, state, 18); - x38_stateLoaded = true; + memmove(x39_passwordToNES, state, 18); + x38_passwordPending = true; } } diff --git a/NESEmulator/CNESEmulator.hpp b/NESEmulator/CNESEmulator.hpp index 046226cab..ac14b89f4 100644 --- a/NESEmulator/CNESEmulator.hpp +++ b/NESEmulator/CNESEmulator.hpp @@ -19,6 +19,14 @@ namespace MP1 class CNESEmulator : public boo::IAudioVoiceCallback { +public: + enum class EPasswordEntryState + { + NotPasswordScreen, + NotEntered, + Entered + }; +private: static bool EmulatorConstructed; std::unique_ptr m_nesEmuPBuf; @@ -49,26 +57,29 @@ class CNESEmulator : public boo::IAudioVoiceCallback size_t m_posInBuf = 0; boo::ObjToken m_booVoice; - bool x20_wantsQuit = false; - u8 x21_saveState[18]; - bool x34_wantsLoad = false; - bool x38_stateLoaded = false; - u8 x39_loadState[18]; + bool x20_gameOver = false; + u8 x21_passwordFromNES[18]; + EPasswordEntryState x34_passwordEntryState = EPasswordEntryState::NotPasswordScreen; + bool x38_passwordPending = false; + u8 x39_passwordToNES[18]; static void DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen = 0x20000, u8 descrambleSeed = 0xe9, u32 checkDataLen = 0x1FFFC, u32 checksumMagic = 0xA663); void InitializeEmulator(); void DeinitializeEmulator(); - void NesEmuMainLoop(); + bool NesEmuMainLoop(); + static bool CheckForGameOver(const u8* vram, u8* passwordOut = nullptr); + static EPasswordEntryState CheckForPasswordEntryScreen(const uint8_t* vram); + static bool SetPasswordIntoEntryScreen(u8* vram, u8* wram, const u8* password); public: CNESEmulator(); ~CNESEmulator(); void ProcessUserInput(const CFinalInput& input, int); void Update(); void Draw(const zeus::CColor& mulColor, bool filtering); - void LoadState(const u8* state); - const u8* GetSaveState() const { return x21_saveState; } - bool WantsQuit() const { return x20_wantsQuit; } - bool WantsLoad() const { return x34_wantsLoad; } + void LoadPassword(const u8* state); + const u8* GetPassword() const { return x21_passwordFromNES; } + bool IsGameOver() const { return x20_gameOver; } + EPasswordEntryState GetPasswordEntryState() const { return x34_passwordEntryState; } int audioUpdate(); void preSupplyAudio(boo::IAudioVoice& voice, double dt) {} diff --git a/NESEmulator/apu.c b/NESEmulator/apu.c new file mode 100644 index 000000000..2fd4e64da --- /dev/null +++ b/NESEmulator/apu.c @@ -0,0 +1,172 @@ +#include "fixNES/apu.c" + +/* + * Alternate apuCycle implementation to avoid processing multiple + * NES frames per URDE frame (costly and jarring to player). + * + * This implementation nominally fills 6/10 buffers, allowing + * emulation to "catch up" by having more buffer headroom available + * (and also reducing audio latency somewhat). + * + * URDE's NesEmuMainLoop uses emuSkipVsync as a signal to proceed + * with the emulation, allowing audio buffers to be pre-filled with + * generated tones independent of the emulated CPU. Granted, this + * compromises accuracy, but doesn't affect NEStroid's behavior and + * reduces audio discontinuities. + */ + +bool apuCycleURDE() +{ + if(curBufPos == apuBufSize) + { + int updateRes = audioUpdate(); + if(updateRes == 0) + { + emuSkipFrame = false; + emuSkipVsync = false; + return false; + } + if(updateRes > 6) + { + emuSkipVsync = true; + emuSkipFrame = true; + } + else + { + emuSkipFrame = false; + if(updateRes > 4) // 6 buffers filled, stop here + emuSkipVsync = true; + else + emuSkipVsync = false; + } + curBufPos = 0; + } + uint8_t p1Out = lastP1Out, p2Out = lastP2Out, + triOut = lastTriOut, noiseOut = lastNoiseOut; + if(p1LengthCtr && (APU_IO_Reg[0x15] & P1_ENABLE)) + { + if(p1seq[p1Cycle] && !p1Sweep.mute && freq1 >= 8 && freq1 < 0x7FF) + lastP1Out = p1Out = (p1Env.constant ? p1Env.vol : p1Env.decay); + else + p1Out = 0; + } + if(p2LengthCtr && (APU_IO_Reg[0x15] & P2_ENABLE)) + { + if(p2seq[p2Cycle] && !p2Sweep.mute && freq2 >= 8 && freq2 < 0x7FF) + lastP2Out = p2Out = (p2Env.constant ? p2Env.vol : p2Env.decay); + else + p2Out = 0; + } + if(triLengthCtr && triCurLinearCtr && (APU_IO_Reg[0x15] & TRI_ENABLE)) + { + if(triSeq[triCycle] && triFreq >= 2) + lastTriOut = triOut = triSeq[triCycle]; + else + triOut = 0; + } + if(noiseLengthCtr && (APU_IO_Reg[0x15] & NOISE_ENABLE)) + { + if((noiseShiftReg&1) == 0 && noiseFreq > 0) + lastNoiseOut = noiseOut = (noiseEnv.constant ? noiseEnv.vol : noiseEnv.decay); + else + noiseOut = 0; + } +#if AUDIO_FLOAT + float curIn = pulseLookupTbl[p1Out + p2Out] + tndLookupTbl[(3*triOut) + (2*noiseOut) + dmcVol]; + //very rough still + if(vrc6enabled) + { + vrc6AudioCycle(); + curIn += ((float)vrc6Out)*0.008f; + curIn *= 0.6667f; + } + if(fdsEnabled) + { + fdsAudioCycle(); + curIn += ((float)fdsOut)*0.00617f; + curIn *= 0.75f; + } + if(mmc5enabled) + { + mmc5AudioCycle(); + curIn += pulseLookupTbl[mmc5Out]+(mmc5pcm*0.002f); + curIn *= 0.75f; + } + if(vrc7enabled) + { + curIn += (((float)(vrc7Out>>7))/32768.f); + curIn *= 0.75f; + } + if(n163enabled) + { + curIn += ((float)n163Out)*0.0008f; + curIn *= 0.6667f; + } + if(s5Benabled) + { + s5BAudioCycle(); + curIn += ((float)s5BOut)/32768.f; + curIn *= 0.6667f; + } + //amplify input + curIn *= 3.0f; + float curLPout = lastLPOut+(lpVal*(curIn-lastLPOut)); + float curHPOut = hpVal*(lastHPOut+lastLPOut-curLPout); + //set output + apuOutBuf[curBufPos] = curHPOut; + lastLPOut = curLPout; + lastHPOut = curHPOut; +#else + int32_t curIn = pulseLookupTbl[p1Out + p2Out] + tndLookupTbl[(3*triOut) + (2*noiseOut) + dmcVol]; + //very rough still + if(vrc6enabled) + { + vrc6AudioCycle(); + curIn += ((int32_t)vrc6Out)*262; + curIn <<= 1; curIn /= 3; + } + if(fdsEnabled) + { + fdsAudioCycle(); + curIn += ((int32_t)fdsOut)*202; + curIn *= 3; curIn >>= 2; + } + if(mmc5enabled) + { + mmc5AudioCycle(); + curIn += pulseLookupTbl[mmc5Out]+(mmc5pcm<<6); + curIn *= 3; curIn >>= 2; + } + if(vrc7enabled) + { + curIn += vrc7Out>>7; + curIn *= 3; curIn >>= 2; + } + if(n163enabled) + { + curIn += n163Out*26; + curIn <<= 1; curIn /= 3; + } + if(s5Benabled) + { + s5BAudioCycle(); + curIn += s5BOut; + curIn <<= 1; curIn /= 3; + } + //amplify input + curIn *= 3; + int32_t curOut; + //gen output + curOut = lastLPOut+((lpVal*(curIn-lastLPOut))>>15); //Set Lowpass Output + curIn = (lastHPOut+lastLPOut-curOut); //Set Highpass Input + curIn += (curIn>>31)&1; //Add Sign Bit for proper Downshift later + lastLPOut = curOut; //Save Lowpass Output + curOut = (hpVal*curIn)>>15; //Set Highpass Output + lastHPOut = curOut; //Save Highpass Output + //Save Clipped Highpass Output + apuOutBuf[curBufPos] = (curOut > 32767)?(32767):((curOut < -32768)?(-32768):curOut); +#endif + apuOutBuf[curBufPos+1] = apuOutBuf[curBufPos]; + curBufPos+=2; + return true; +} diff --git a/NESEmulator/fixNES b/NESEmulator/fixNES index c0a679921..01b5bde49 160000 --- a/NESEmulator/fixNES +++ b/NESEmulator/fixNES @@ -1 +1 @@ -Subproject commit c0a679921ddb0e104aa35202bc9cdf0be82850b8 +Subproject commit 01b5bde49a730eb287b18def7ed5e5f510cda7b5 diff --git a/NESEmulator/ppu.c b/NESEmulator/ppu.c new file mode 100644 index 000000000..7305e027a --- /dev/null +++ b/NESEmulator/ppu.c @@ -0,0 +1,8 @@ +#include "fixNES/ppu.c" + +/* Non-invasive way to access VRAM buffer directly */ + +uint8_t* ppuGetVRAM() +{ + return PPU_VRAM; +} diff --git a/Runtime/Input/CFinalInput.hpp b/Runtime/Input/CFinalInput.hpp index 04346cc15..86eca47bf 100644 --- a/Runtime/Input/CFinalInput.hpp +++ b/Runtime/Input/CFinalInput.hpp @@ -8,9 +8,8 @@ namespace urde { -class CFinalInput +struct CFinalInput { - friend class CStateManager; float x0_dt; u32 x4_controllerIdx; float x8_anaLeftX; @@ -63,7 +62,6 @@ class CFinalInput bool x2e_b30_PDPLeft:1; bool x2e_b31_PStart:1; -public: CFinalInput(); CFinalInput(int cIdx, float dt, const boo::DolphinControllerState& data, diff --git a/Runtime/MP1/CFrontEndUI.cpp b/Runtime/MP1/CFrontEndUI.cpp index 3df877ee3..82b5de22d 100644 --- a/Runtime/MP1/CFrontEndUI.cpp +++ b/Runtime/MP1/CFrontEndUI.cpp @@ -1036,8 +1036,8 @@ void CFrontEndUI::SFusionBonusFrame::Update(float dt, CSaveGameScreen* saveUI) else if (x24_loadedFrame) x24_loadedFrame->Update(dt); - bool showFusionSuit = g_GameState->SystemOptions().GetPlayerLinkedFusion() && - g_GameState->SystemOptions().GetPlayerBeatNormalMode(); + bool showFusionSuit = (g_GameState->SystemOptions().GetPlayerLinkedFusion() && + g_GameState->SystemOptions().GetPlayerBeatNormalMode()) || m_gbaOverride; bool showFusionSuitProceed = showFusionSuit && x28_tablegroup_options->GetUserSelection() == 1; x2c_tablegroup_fusionsuit->SetIsActive(showFusionSuitProceed); x2c_tablegroup_fusionsuit->SetIsVisible(showFusionSuitProceed); @@ -1060,11 +1060,6 @@ void CFrontEndUI::SFusionBonusFrame::Update(float dt, CSaveGameScreen* saveUI) instructionStr = g_MainStringTable->GetString(79); // You have not completed fusion else if (!g_GameState->SystemOptions().GetPlayerBeatFusion()) instructionStr = g_MainStringTable->GetString(77); // To play NES Metroid - else - { - instructionStr = u"NES Emulator currently unsupported"; - x30_textpane_instructions.x0_panes[0]->TextSupport().SetFontColor(zeus::CColor::skYellow); - } } x30_textpane_instructions.SetPairText(instructionStr); @@ -1102,15 +1097,22 @@ CFrontEndUI::SFusionBonusFrame::ProcessUserInput(const CFinalInput& input, CSave } else if (x24_loadedFrame) { - bool showFusionSuit = g_GameState->SystemOptions().GetPlayerLinkedFusion() && - g_GameState->SystemOptions().GetPlayerBeatNormalMode(); + CFinalInput useInput = input; + if (input.PZ()) + { + useInput.x2d_b28_PA = true; + m_gbaOverride = true; + } + + bool showFusionSuit = (g_GameState->SystemOptions().GetPlayerLinkedFusion() && + g_GameState->SystemOptions().GetPlayerBeatNormalMode()) || m_gbaOverride; if (m_touchBar.GetPhase() != CFrontEndUITouchBar::EPhase::FusionBonus) { m_touchBar.SetFusionBonusPhase(showFusionSuit && g_GameState->SystemOptions().GetPlayerFusionSuitActive()); } - x24_loadedFrame->ProcessUserInput(input); + x24_loadedFrame->ProcessUserInput(useInput); switch (tbAction) { @@ -1196,7 +1198,7 @@ void CFrontEndUI::SFusionBonusFrame::DoAdvance(CGuiTableGroup* caller) { case 1: /* Fusion Suit */ - if (x3a_mpNotComplete) + if (x3a_mpNotComplete || m_gbaOverride) { x3a_mpNotComplete = false; PlayAdvanceSfx(); @@ -1216,12 +1218,12 @@ void CFrontEndUI::SFusionBonusFrame::DoAdvance(CGuiTableGroup* caller) break; case 0: /* NES Metroid */ - if (x39_fusionNotComplete) + if (x39_fusionNotComplete && !m_gbaOverride) { x39_fusionNotComplete = false; PlayAdvanceSfx(); } - else if (g_GameState->SystemOptions().GetPlayerBeatFusion()) + else if (g_GameState->SystemOptions().GetPlayerBeatFusion() || m_gbaOverride) { //x8_action = EAction::None; CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); @@ -1427,6 +1429,9 @@ CFrontEndUI::SNesEmulatorFrame::SNesEmulatorFrame() xc_textSupport = std::make_unique(deface->id, props, zeus::CColor::skWhite, zeus::CColor::skBlack, zeus::CColor::skWhite, 0, 0, g_SimplePool, CGuiWidget::EGuiModelDrawFlags::Alpha); + xc_textSupport->SetText(g_MainStringTable->GetString(103)); + xc_textSupport->AutoSetExtent(); + xc_textSupport->ClearRenderBuffer(); } void CFrontEndUI::SNesEmulatorFrame::SetMode(EMode mode) @@ -1498,9 +1503,9 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi) case EMode::Emulator: { x4_nesEmu->Update(); - if (!x4_nesEmu->WantsQuit()) + if (!x4_nesEmu->IsGameOver()) x14_emulationSuspended = false; - if (x4_nesEmu->WantsQuit() && !x14_emulationSuspended) + if (x4_nesEmu->IsGameOver() && !x14_emulationSuspended) { x14_emulationSuspended = true; if (saveUi && !saveUi->IsSavingDisabled()) @@ -1511,8 +1516,8 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi) SetMode(EMode::ContinuePlaying); break; } - if (x4_nesEmu->WantsLoad() && saveUi) - x4_nesEmu->LoadState(g_GameState->SystemOptions().GetNESState()); + if (x4_nesEmu->GetPasswordEntryState() == CNESEmulator::EPasswordEntryState::NotEntered && saveUi) + x4_nesEmu->LoadPassword(g_GameState->SystemOptions().GetNESState()); break; } @@ -1523,7 +1528,7 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi) EQuitAction action = x8_quitScreen->Update(dt); if (action == EQuitAction::Yes) { - memmove(g_GameState->SystemOptions().GetNESState(), x4_nesEmu->GetSaveState(), 18); + memmove(g_GameState->SystemOptions().GetNESState(), x4_nesEmu->GetPassword(), 18); saveUi->SaveNESState(); SetMode(EMode::ContinuePlaying); } @@ -2533,7 +2538,7 @@ static const float AudioFadeTimeB[] = CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue) { - if (xdc_saveUI && (x50_curScreen >= EScreen::FileSelect || true)) + if (xdc_saveUI && x50_curScreen >= EScreen::FileSelect) { switch (xdc_saveUI->Update(dt)) { @@ -2636,11 +2641,6 @@ CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue) } break; } - else - { - xec_emuFrme = std::make_unique(); - xdc_saveUI->ResetCardDriver(); - } if (xd2_deferSlideShow) { diff --git a/Runtime/MP1/CFrontEndUI.hpp b/Runtime/MP1/CFrontEndUI.hpp index 16f43ab35..e8c5a037e 100644 --- a/Runtime/MP1/CFrontEndUI.hpp +++ b/Runtime/MP1/CFrontEndUI.hpp @@ -249,6 +249,8 @@ public: CFrontEndUITouchBar& m_touchBar; + bool m_gbaOverride = false; + SFusionBonusFrame(CFrontEndUITouchBar& touchBar); void FinishedLoading(); bool PumpLoad(); diff --git a/Runtime/MP1/CQuitGameScreen.cpp b/Runtime/MP1/CQuitGameScreen.cpp index b6e0ea1d2..e9d5643e9 100644 --- a/Runtime/MP1/CQuitGameScreen.cpp +++ b/Runtime/MP1/CQuitGameScreen.cpp @@ -36,6 +36,7 @@ void CQuitGameScreen::SetColors() void CQuitGameScreen::FinishedLoading() { x10_loadedFrame = x4_frame.GetObj(); + x10_loadedFrame->SetMaxAspect(1.33f); x14_tablegroup_quitgame = static_cast( x10_loadedFrame->FindWidget("tablegroup_quitgame")); diff --git a/hecl b/hecl index 3454e81f9..e46ab0523 160000 --- a/hecl +++ b/hecl @@ -1 +1 @@ -Subproject commit 3454e81f97ae951b85cd4a9e18bb800f6123163e +Subproject commit e46ab0523f0b46992465f2b541d93f7cb49c4c77