Merge pull request #22236 from hrydgard/savestate-hardcore-race

Fix savestate loads slipping past hardcore mode during boot
This commit is contained in:
Henrik Rydgård
2026-09-05 14:07:40 -06:00
committed by GitHub
3 changed files with 38 additions and 17 deletions
+25 -15
View File
@@ -197,16 +197,16 @@ int g_screenshotFailures;
}
void Enqueue(const SaveState::Operation &op) {
if (!NetworkAllowSaveState()) {
// These two both refuse the operation, and both say so - dropping it silently just
// looks like the hotkey didn't work. Callers that ask the same questions themselves
// return before getting here, so nothing shows a message twice.
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return;
}
if (Achievements::HardcoreModeActive()) {
if (g_Config.bAchievementsSaveStateInHardcoreMode && ((op.type == SaveState::OperationType::Save))) {
// We allow saving in hardcore mode if this setting is on.
} else {
// Operation not allowed
return;
}
// Hardcore mode bans loading savestates outright, and saving too unless the user opted
// back into that.
if (Achievements::WarnUserIfHardcoreModeActive(op.type == OperationType::Save)) {
return;
}
std::lock_guard<std::mutex> guard(mutex);
@@ -218,7 +218,7 @@ int g_screenshotFailures;
}
void Load(const Path &filename, int slot, Callback callback) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return;
}
@@ -229,7 +229,7 @@ int g_screenshotFailures;
}
void Save(const Path &filename, int slot, Callback callback) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return;
}
@@ -399,7 +399,7 @@ int g_screenshotFailures;
}
void LoadSlot(std::string_view gamePrefix, int slot, Callback callback) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return;
}
@@ -440,7 +440,7 @@ int g_screenshotFailures;
}
bool UndoLoad(std::string_view gamePrefix, Callback callback) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return false;
}
@@ -466,7 +466,7 @@ int g_screenshotFailures;
}
void SaveSlot(std::string_view gamePrefix, int slot, Callback callback) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return;
}
@@ -511,7 +511,7 @@ int g_screenshotFailures;
}
bool UndoSaveSlot(std::string_view gamePrefix, int slot) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return false;
}
@@ -544,7 +544,7 @@ int g_screenshotFailures;
}
bool UndoLastSave(std::string_view gamePrefix) {
if (!NetworkAllowSaveState()) {
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
return false;
}
@@ -810,6 +810,16 @@ int g_screenshotFailures;
SaveStart state;
for (const auto &op : operations) {
// Re-check here, and not just in Enqueue: during boot, RetroAchievements is still
// identifying the game asynchronously, and until it's done hardcore mode doesn't
// read as active yet. An operation queued in that window (a hotkey press, --state,
// auto-load) passed the check in Enqueue and would otherwise be applied here, after
// identification has finished and hardcore mode has come up.
if (Achievements::WarnUserIfHardcoreModeActive(op.type == OperationType::Save)) {
WARN_LOG(Log::SaveState, "Dropping queued savestate operation - hardcore mode is active");
continue;
}
CChunkFileReader::Error result;
Status callbackResult;
std::string callbackMessage;
+5
View File
@@ -58,6 +58,7 @@
#include "Core/ConfigValues.h"
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/RetroAchievements.h"
#include "Core/CoreParameter.h"
#include "Core/HLE/sceKernel.h" // GPI/GPO
#include "Core/MIPS/MIPSTables.h"
@@ -187,6 +188,10 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
});
items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Add([](UI::EventParams &e) {
// Freezing restores a savestate every frame, so it's not allowed in hardcore mode.
if (Achievements::WarnUserIfHardcoreModeActive(false)) {
return;
}
if (PSP_CoreParameter().frozen) {
PSP_CoreParameter().frozen = false;
} else {
+8 -2
View File
@@ -1709,8 +1709,14 @@ ScreenRenderFlags EmuScreen::RunEmulation(bool skipBufferEffects) {
gpu->BeginHostFrame(displayLayoutConfig);
}
// Freeze-frame functionality (loads a savestate on every frame).
if (PSP_CoreParameter().freezeNext) {
// Freeze-frame functionality (loads a savestate on every frame). It's a savestate load
// like any other, so it's banned in hardcore mode - and checked here rather than only
// where it's toggled, since hardcore mode can come up after the fact, once the game
// has been identified.
if (Achievements::HardcoreModeActive()) {
PSP_CoreParameter().freezeNext = false;
PSP_CoreParameter().frozen = false;
} else if (PSP_CoreParameter().freezeNext) {
PSP_CoreParameter().frozen = true;
PSP_CoreParameter().freezeNext = false;
SaveState::SaveToRam(freezeState_);