From bebc3feee110a7dd6b58ee2bb72f02968b6ca0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 3 Jul 2023 09:18:25 +0200 Subject: [PATCH] Safety: Make sure we're not executing game code while waiting for RetroAchievements identification. --- Core/RetroAchievements.cpp | 11 ++++++ Core/RetroAchievements.h | 3 ++ UI/EmuScreen.cpp | 80 +++++++++++++++++++------------------- 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 4995a5ab35..9387618a56 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -104,6 +104,8 @@ std::string s_game_hash; bool g_challengeMode = true; +bool g_isIdentifying = false; + // rc_client implementation static rc_client_t *g_rcClient; @@ -132,6 +134,10 @@ bool IsActive() { return GetGameID() != 0; } +bool IsBlockingExecution() { + return g_isIdentifying; +} + u32 GetGameID() { if (!g_rcClient) { return 0; @@ -561,6 +567,8 @@ void identify_and_load_callback(int result, const char *error_message, rc_client ERROR_LOG(ACHIEVEMENTS, "Failed to identify/load game: %d (%s)", result, error_message); break; } + + g_isIdentifying = false; } void SetGame(const Path &path) { @@ -580,6 +588,9 @@ void SetGame(const Path &path) { rc_filereader.read = [](void *file_handle, void *buffer, size_t requested_bytes) -> size_t { return fread(buffer, 1, requested_bytes, (FILE *)file_handle); }; rc_filereader.close = [](void *file_handle) { fclose((FILE *)file_handle); }; + // The caller should hold off on executing game code until this turns false, checking with IsBlockingExecution() + g_isIdentifying = true; + // Apply pre-load settings. rc_client_set_encore_mode_enabled(g_rcClient, g_Config.bAchievementsEncoreMode ? 1 : 0); diff --git a/Core/RetroAchievements.h b/Core/RetroAchievements.h index 3c495313e7..c4d359dbf9 100644 --- a/Core/RetroAchievements.h +++ b/Core/RetroAchievements.h @@ -51,6 +51,9 @@ bool IsLoggedIn(); // Returns true if in a game, and achievements are active in the current game. bool IsActive(); +// Returns true if the emulator should hold off on executing game code, such as during game identification. +bool IsBlockingExecution(); + /// Returns true if features such as save states should be disabled. bool ChallengeModeActive(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 46102be5bd..60c9e4a331 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1495,50 +1495,52 @@ void EmuScreen::render() { Core_UpdateDebugStats(g_Config.bShowDebugStats || g_Config.bLogFrameDrops); - PSP_BeginHostFrame(); + bool blockedExecution = Achievements::IsBlockingExecution(); + if (!blockedExecution) { + PSP_BeginHostFrame(); + PSP_RunLoopWhileState(); - PSP_RunLoopWhileState(); - - // Hopefully coreState is now CORE_NEXTFRAME - switch (coreState) { - case CORE_NEXTFRAME: - // Reached the end of the frame, all good. Set back to running for the next frame - coreState = CORE_RUNNING; - break; - case CORE_STEPPING: - case CORE_RUNTIME_ERROR: - { - // If there's an exception, display information. - const MIPSExceptionInfo &info = Core_GetExceptionInfo(); - if (info.type != MIPSExceptionType::NONE) { - // Clear to blue background screen - bool dangerousSettings = !Reporting::IsSupported(); - uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000; - thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError"); - // The info is drawn later in renderUI - } else { - // If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output. - // This won't work in non-buffered, but that's fine. - thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping"); - // Just to make sure. - if (PSP_IsInited()) { - gpu->CopyDisplayToOutput(true); + // Hopefully coreState is now CORE_NEXTFRAME + switch (coreState) { + case CORE_NEXTFRAME: + // Reached the end of the frame, all good. Set back to running for the next frame + coreState = CORE_RUNNING; + break; + case CORE_STEPPING: + case CORE_RUNTIME_ERROR: + { + // If there's an exception, display information. + const MIPSExceptionInfo &info = Core_GetExceptionInfo(); + if (info.type != MIPSExceptionType::NONE) { + // Clear to blue background screen + bool dangerousSettings = !Reporting::IsSupported(); + uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000; + thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError"); + // The info is drawn later in renderUI + } else { + // If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output. + // This won't work in non-buffered, but that's fine. + thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping"); + // Just to make sure. + if (PSP_IsInited()) { + gpu->CopyDisplayToOutput(true); + } } + break; + } + default: + // Didn't actually reach the end of the frame, ran out of the blockTicks cycles. + // In this case we need to bind and wipe the backbuffer, at least. + // It's possible we never ended up outputted anything - make sure we have the backbuffer cleared + thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame"); + break; } - break; - } - default: - // Didn't actually reach the end of the frame, ran out of the blockTicks cycles. - // In this case we need to bind and wipe the backbuffer, at least. - // It's possible we never ended up outputted anything - make sure we have the backbuffer cleared - thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame"); - break; - } - PSP_EndHostFrame(); + PSP_EndHostFrame(); - // This must happen after PSP_EndHostFrame so that things like push buffers are end-frame'd before we start destroying stuff. - checkPowerDown(); + // This must happen after PSP_EndHostFrame so that things like push buffers are end-frame'd before we start destroying stuff. + checkPowerDown(); + } if (invalid_) return;