Code cleanup for save state notification, add metadata field

This commit is contained in:
Henrik Rydgård
2026-05-19 11:07:07 +02:00
parent 87a74546bc
commit 231aefe476
9 changed files with 44 additions and 48 deletions
+14 -13
View File
@@ -414,7 +414,7 @@ int g_screenshotFailures;
Path backup = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
std::string prefix(gamePrefix);
auto saveCallback = [prefix, fn, backup, slot, callback](Status status, std::string_view message) {
auto saveCallback = [prefix, fn, backup, slot, callback](Status status, std::string_view message, std::string_view metadata) {
if (status != Status::FAILURE) {
DeleteIfExists(backup);
File::Rename(backup.WithExtraExtension(".tmp"), backup);
@@ -438,7 +438,7 @@ int g_screenshotFailures;
} else {
if (callback) {
auto sy = GetI18NCategory(I18NCat::SYSTEM);
callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."));
callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."), "");
}
}
}
@@ -451,7 +451,7 @@ int g_screenshotFailures;
if (g_Config.sStateLoadUndoGame != gamePrefix) {
if (callback) {
auto sy = GetI18NCategory(I18NCat::SYSTEM);
callback(Status::FAILURE, sy->T("Error: load undo state is from a different game"));
callback(Status::FAILURE, sy->T("Error: load undo state is from a different game"), "");
}
return false;
}
@@ -463,7 +463,7 @@ int g_screenshotFailures;
} else {
if (callback) {
auto sy = GetI18NCategory(I18NCat::SYSTEM);
callback(Status::FAILURE, sy->T("Failed to load state for load undo. Error in the file system."));
callback(Status::FAILURE, sy->T("Failed to load state for load undo. Error in the file system."), "");
}
return false;
}
@@ -480,7 +480,7 @@ int g_screenshotFailures;
Path shot = GenerateSaveSlotPath(gamePrefix, slot, SCREENSHOT_EXTENSION);
std::string prefix(gamePrefix);
auto renameCallback = [fn, fnUndo, prefix, slot, callback](Status status, std::string_view message) {
auto renameCallback = [fn, fnUndo, prefix, slot, callback](Status status, std::string_view message, std::string_view metadata) {
if (status != Status::FAILURE) {
if (g_Config.bEnableStateUndo) {
DeleteIfExists(fnUndo);
@@ -494,7 +494,7 @@ int g_screenshotFailures;
File::Rename(fn.WithExtraExtension(".tmp"), fn);
}
if (callback) {
callback(status, message);
callback(status, message, "");
}
};
// Let's also create a screenshot.
@@ -508,7 +508,7 @@ int g_screenshotFailures;
} else {
if (callback) {
auto sy = GetI18NCategory(I18NCat::SYSTEM);
callback(Status::FAILURE, sy->T("Failed to save state. Error in the file system."));
callback(Status::FAILURE, sy->T("Failed to save state. Error in the file system."), "");
}
}
Rescan(gamePrefix);
@@ -714,7 +714,7 @@ int g_screenshotFailures;
return copy;
}
bool HandleLoadFailure(bool wasRewinding) {
static bool HandleLoadFailure(bool wasRewinding, std::string *metadata) {
if (wasRewinding) {
WARN_LOG(Log::SaveState, "HandleLoadFailure - trying a rewind state.");
// Okay, first, let's give the next rewind state a shot - maybe we can at least not reset entirely.
@@ -722,7 +722,7 @@ int g_screenshotFailures;
CChunkFileReader::Error result;
do {
std::string errorString;
result = rewindStates.Restore(&errorString);
result = rewindStates.Restore(&errorString, metadata);
} while (result == CChunkFileReader::ERROR_BROKEN_STATE);
if (result == CChunkFileReader::ERROR_NONE) {
@@ -813,6 +813,7 @@ int g_screenshotFailures;
CChunkFileReader::Error result;
Status callbackResult;
std::string callbackMessage;
std::string callbackMetadata;
std::string title;
auto sc = GetI18NCategory(I18NCat::SCREEN);
@@ -849,7 +850,7 @@ int g_screenshotFailures;
#endif
g_lastSaveTime = time_now_d();
} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
HandleLoadFailure(false);
HandleLoadFailure(false, &callbackMetadata);
callbackMessage = std::string(i18nLoadFailure) + ": " + errorString;
ERROR_LOG(Log::SaveState, "Load state failure: %s", errorString.c_str());
callbackResult = Status::FAILURE;
@@ -909,7 +910,7 @@ int g_screenshotFailures;
case OperationType::Rewind:
INFO_LOG(Log::SaveState, "Rewinding to recent savestate snapshot");
result = rewindStates.Restore(&errorString);
result = rewindStates.Restore(&errorString, &callbackMetadata);
if (result == CChunkFileReader::ERROR_NONE) {
callbackMessage = sc->T("Loaded State");
callbackResult = Status::SUCCESS;
@@ -917,7 +918,7 @@ int g_screenshotFailures;
Core_ResetException();
} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
// Cripes. Good news is, we might have more. Let's try those too, better than a reset.
if (HandleLoadFailure(true)) {
if (HandleLoadFailure(true, &callbackMetadata)) {
// Well, we did rewind, even if too much...
callbackMessage = sc->T("Loaded State");
callbackResult = Status::SUCCESS;
@@ -940,7 +941,7 @@ int g_screenshotFailures;
}
if (op.callback) {
op.callback(callbackResult, callbackMessage);
op.callback(callbackResult, callbackMessage, callbackMetadata);
}
}
if (operations.size()) {
+1 -1
View File
@@ -34,7 +34,7 @@ namespace SaveState {
WARNING,
SUCCESS,
};
typedef std::function<void(Status status, std::string_view message)> Callback;
typedef std::function<void(Status status, std::string_view message, std::string_view metadata)> Callback;
static const char * const SCREENSHOT_EXTENSION = "jpg";
+5 -1
View File
@@ -1,4 +1,5 @@
#include "Common/Thread/ThreadUtil.h"
#include "Common/Data/Text/I18n.h"
#include "Core/SaveState.h"
#include "Core/SaveStateRewind.h"
#include "Core/Core.h"
@@ -42,7 +43,7 @@ CChunkFileReader::Error StateRingbuffer::Save() {
return err;
}
CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString) {
CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString, std::string *metadata) {
std::lock_guard<std::mutex> guard(lock_);
// No valid states left.
@@ -53,9 +54,12 @@ CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString) {
if (states_[n].empty())
return CChunkFileReader::ERROR_BAD_FILE;
auto pa = GetI18NCategory(I18NCat::PAUSE);
static std::vector<u8> buffer;
LockedDecompress(buffer, states_[n], bases_[baseMapping_[n]]);
CChunkFileReader::Error error = LoadFromRam(buffer, errorString);
*metadata = pa->T("Rewind");
rewindLastTime_ = time_now_d();
return error;
}
+1 -1
View File
@@ -28,7 +28,7 @@ public:
}
CChunkFileReader::Error Save();
CChunkFileReader::Error Restore(std::string *errorString);
CChunkFileReader::Error Restore(std::string *errorString, std::string *metadata);
void ScheduleCompress(std::vector<u8> *result, const std::vector<u8> *state, const std::vector<u8> *base);
void Compress(std::vector<u8> &result, const std::vector<u8> &state, const std::vector<u8> &base);
void LockedDecompress(std::vector<u8> &result, const std::vector<u8> &compressed, const std::vector<u8> &base);
+6 -13
View File
@@ -74,7 +74,6 @@ using namespace std::placeholders;
#include "Core/Screenshot.h"
#include "UI/ImDebugger/ImDebugger.h"
#include "Core/HLE/__sceAudio.h"
// #include "Core/HLE/proAdhoc.h"
#include "Core/HW/Display.h"
#include "UI/BackgroundAudio.h"
@@ -533,12 +532,6 @@ void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) {
lastImguiEnabled_ = false;
}
static void AfterSaveStateAction(SaveState::Status status, std::string_view message) {
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
}
}
void EmuScreen::focusChanged(ScreenFocusChange focusChange) {
Screen::focusChanged(focusChange);
@@ -609,7 +602,7 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) {
if (newGamePath.GetFileExtension() == ".ppst") {
// TODO: Should verify that it's for the correct game....
INFO_LOG(Log::Loader, "New game is a save state - just load it.");
SaveState::Load(newGamePath, -1, [](SaveState::Status status, std::string_view message) {
SaveState::Load(newGamePath, -1, [](SaveState::Status status, std::string_view message, std::string_view metadata) {
Core_Resume();
System_Notify(SystemNotification::DISASSEMBLY);
});
@@ -965,7 +958,7 @@ void EmuScreen::ProcessVKey(VirtKey virtKey) {
case VIRTKEY_REWIND:
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
if (SaveState::CanRewind()) {
SaveState::Rewind(&AfterSaveStateAction);
SaveState::Rewind(&ShowMessageAfterSaveStateAction);
} else {
g_OSD.Show(OSDType::MESSAGE_WARNING, sc->T("norewind", "No rewind save states available"), 2.0);
}
@@ -1058,12 +1051,12 @@ void EmuScreen::ProcessVKey(VirtKey virtKey) {
case VIRTKEY_SAVE_STATE:
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
SaveState::SaveSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &AfterSaveStateAction);
SaveState::SaveSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &ShowMessageAfterSaveStateAction);
}
break;
case VIRTKEY_LOAD_STATE:
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate() && !bootPending_) {
SaveState::LoadSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &AfterSaveStateAction);
SaveState::LoadSlot(SaveState::GetGamePrefix(g_paramSFO), g_Config.iCurrentStateSlot, &ShowMessageAfterSaveStateAction);
}
break;
case VIRTKEY_PREVIOUS_SLOT:
@@ -2053,8 +2046,8 @@ void EmuScreen::AutoLoadSaveState() {
}
if (g_Config.iAutoLoadSaveState && autoSlot != -1) {
SaveState::LoadSlot(gamePrefix, autoSlot, [this, autoSlot](SaveState::Status status, std::string_view message) {
AfterSaveStateAction(status, message);
SaveState::LoadSlot(gamePrefix, autoSlot, [this, autoSlot](SaveState::Status status, std::string_view message, std::string_view metadata) {
ShowMessageAfterSaveStateAction(status, message, metadata);
auto sy = GetI18NCategory(I18NCat::SYSTEM);
if (status == SaveState::Status::FAILURE) {
autoLoadFailed_ = true;
+2 -6
View File
@@ -133,6 +133,7 @@
#include "UI/OnScreenDisplay.h"
#include "UI/RemoteISOScreen.h"
#include "UI/Theme.h"
#include "UI/PauseScreen.h"
#include "UI/UIAtlas.h"
#if PPSSPP_PLATFORM(UWP)
@@ -693,12 +694,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
g_BackgroundAudio.SFX().Init();
if (!boot_filename.empty() && stateToLoad.Valid()) {
SaveState::Load(stateToLoad, -1, [](SaveState::Status status, std::string_view message) {
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR,
message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
}
});
SaveState::Load(stateToLoad, -1, &ShowMessageAfterSaveStateAction);
}
if (g_Config.bAchievementsEnable) {
+9 -9
View File
@@ -76,10 +76,10 @@
void copyDeepLinkForPath(std::string_view filePath);
#endif
static void AfterSaveStateAction(SaveState::Status status, std::string_view message) {
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
void ShowMessageAfterSaveStateAction(SaveState::Status status, std::string_view message, std::string_view metadata) {
if (!message.empty()) {
g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR,
message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
message, metadata, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
}
}
@@ -153,7 +153,7 @@ private:
void ScreenshotViewScreen::OnSaveState(UI::EventParams &e) {
if (!NetworkWarnUserIfOnlineAndCantSavestate()) {
g_Config.iCurrentStateSlot = slot_;
SaveState::SaveSlot(saveStatePrefix_, slot_, &AfterSaveStateAction);
SaveState::SaveSlot(saveStatePrefix_, slot_, &ShowMessageAfterSaveStateAction);
TriggerFinish(DR_OK); //OK will close the pause screen as well
}
}
@@ -161,7 +161,7 @@ void ScreenshotViewScreen::OnSaveState(UI::EventParams &e) {
void ScreenshotViewScreen::OnLoadState(UI::EventParams &e) {
if (!NetworkWarnUserIfOnlineAndCantSavestate()) {
g_Config.iCurrentStateSlot = slot_;
SaveState::LoadSlot(saveStatePrefix_, slot_, &AfterSaveStateAction);
SaveState::LoadSlot(saveStatePrefix_, slot_, &ShowMessageAfterSaveStateAction);
TriggerFinish(DR_OK);
}
}
@@ -303,7 +303,7 @@ void SaveSlotView::Draw(UIContext &dc) {
void SaveSlotView::OnLoadState(UI::EventParams &e) {
if (!NetworkWarnUserIfOnlineAndCantSavestate()) {
g_Config.iCurrentStateSlot = slot_;
SaveState::LoadSlot(saveStatePrefix_, slot_, &AfterSaveStateAction);
SaveState::LoadSlot(saveStatePrefix_, slot_, &ShowMessageAfterSaveStateAction);
UI::EventParams e2{};
e2.v = this;
OnStateLoaded.Trigger(e2);
@@ -313,7 +313,7 @@ void SaveSlotView::OnLoadState(UI::EventParams &e) {
void SaveSlotView::OnSaveState(UI::EventParams &e) {
if (!NetworkWarnUserIfOnlineAndCantSavestate()) {
g_Config.iCurrentStateSlot = slot_;
SaveState::SaveSlot(saveStatePrefix_, slot_, &AfterSaveStateAction);
SaveState::SaveSlot(saveStatePrefix_, slot_, &ShowMessageAfterSaveStateAction);
UI::EventParams e2{};
e2.v = this;
OnStateSaved.Trigger(e2);
@@ -426,7 +426,7 @@ void GamePauseScreen::CreateSavestateControls(UI::LinearLayout *leftColumnItems,
UI::Choice *loadUndoButton = buttonRow->Add(new Choice(pa->T("Undo last load"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)));
loadUndoButton->SetEnabled(SaveState::HasUndoLoad(saveStatePrefix_));
loadUndoButton->OnClick.Add([this](UI::EventParams &e) {
SaveState::UndoLoad(saveStatePrefix_, &AfterSaveStateAction);
SaveState::UndoLoad(saveStatePrefix_, &ShowMessageAfterSaveStateAction);
TriggerFinish(DR_CANCEL);
});
}
@@ -435,7 +435,7 @@ void GamePauseScreen::CreateSavestateControls(UI::LinearLayout *leftColumnItems,
UI::Choice *rewindButton = buttonRow->Add(new Choice(pa->T("Rewind"), ImageID("I_REWIND"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)));
rewindButton->SetEnabled(SaveState::CanRewind());
rewindButton->OnClick.Add([this](UI::EventParams &e) {
SaveState::Rewind(&AfterSaveStateAction);
SaveState::Rewind(&ShowMessageAfterSaveStateAction);
TriggerFinish(DR_CANCEL);
});
}
+2
View File
@@ -23,6 +23,7 @@
#include "Common/File/Path.h"
#include "Common/UI/UIScreen.h"
#include "Common/UI/ViewGroup.h"
#include "Core/SaveState.h"
#include "Core/ControlMapper.h"
#include "UI/BaseScreens.h"
#include "UI/Screen.h"
@@ -85,3 +86,4 @@ private:
};
std::string GetConfirmExitMessage();
void ShowMessageAfterSaveStateAction(SaveState::Status status, std::string_view message, std::string_view metadata);
+4 -4
View File
@@ -45,6 +45,7 @@
#include "Core/SaveState.h"
#include "Core/Core.h"
#include "Core/RetroAchievements.h"
#include "UI/PauseScreen.h"
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
#include "ext/rcheevos/include/rc_client_raintegration.h"
@@ -394,10 +395,9 @@ namespace MainWindow {
});
}
static void SaveStateActionFinished(SaveState::Status status, std::string_view message) {
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
}
static void SaveStateActionFinished(SaveState::Status status, std::string_view message, std::string_view metadata) {
// Reuse the message from the pause screen.
ShowMessageAfterSaveStateAction(status, message, metadata);
PostMessage(MainWindow::GetHWND(), WM_USER_SAVESTATE_FINISH, 0, 0);
}