Merge pull request #14434 from unknownbrackets/savestate

SaveState: Show a warning when loading if saved after savestate
This commit is contained in:
Henrik Rydgård 2021-08-07 17:23:26 +02:00 committed by GitHub
commit 229e18420a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 19 deletions

View File

@ -25,23 +25,23 @@
#include <ctime>
#include <thread>
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/Data/Text/I18n.h"
#include "Common/File/FileUtil.h"
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/StringUtils.h"
#include "Common/Thread/ThreadUtil.h"
#include "Core/Dialog/PSPSaveDialog.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/Util/PPGeDraw.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/HLE/sceUtility.h"
#include "Core/HW/MemoryStick.h"
#include "Core/MemMapHelpers.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/HW/MemoryStick.h"
#include "Core/Dialog/PSPSaveDialog.h"
#include "Core/SaveState.h"
const static float FONT_SCALE = 0.55f;
@ -1048,6 +1048,7 @@ void PSPSaveDialog::ExecuteIOAction() {
}
break;
case DS_SAVE_SAVING:
SaveState::NotifySaveData();
if (param.Save(param.GetPspParam(), GetSelectedSaveDirName()) == 0) {
display = DS_SAVE_DONE;
} else {
@ -1085,6 +1086,7 @@ void PSPSaveDialog::ExecuteNotVisibleIOAction() {
break;
case SCE_UTILITY_SAVEDATA_TYPE_SAVE: // Only save and exit
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
SaveState::NotifySaveData();
result = param.Save(param.GetPspParam(), GetSelectedSaveDirName());
break;
case SCE_UTILITY_SAVEDATA_TYPE_SIZES:
@ -1129,6 +1131,7 @@ void PSPSaveDialog::ExecuteNotVisibleIOAction() {
// TODO: Should reset the directory's other files.
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
SaveState::NotifySaveData();
result = param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE);
if (result == SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE) {
result = SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL;
@ -1136,6 +1139,7 @@ void PSPSaveDialog::ExecuteNotVisibleIOAction() {
break;
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
SaveState::NotifySaveData();
result = param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE);
break;
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:

View File

@ -260,6 +260,8 @@ namespace SaveState
// 4 hours of total gameplay since the virtual PSP started the game.
static const u64 STALE_STATE_TIME = 4 * 3600 * 1000000ULL;
static int saveStateGeneration = 0;
static int saveDataGeneration = 0;
static int lastSaveDataGeneration = 0;
static std::string saveStateInitialGitVersion = "";
// TODO: Should this be configurable?
@ -289,6 +291,12 @@ namespace SaveState
} else {
saveStateGeneration = 1;
}
if (s >= 3) {
// Keep track of savedata (not save states) too.
Do(p, saveDataGeneration);
} else {
saveDataGeneration = 0;
}
// Gotta do CoreTiming first since we'll restore into it.
CoreTiming::DoState(p);
@ -779,6 +787,37 @@ namespace SaveState
return state < gitVer;
}
static Status TriggerLoadWarnings(std::string &callbackMessage) {
auto sc = GetI18NCategory("Screen");
if (g_Config.bHideStateWarnings)
return Status::SUCCESS;
if (IsStale()) {
// For anyone wondering why (too long to put on the screen in an osm):
// Using save states instead of saves simulates many hour play sessions.
// Sometimes this exposes game bugs that were rarely seen on real devices,
// because few people played on a real PSP for 10 hours straight.
callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs.");
return Status::WARNING;
}
if (IsOldVersion()) {
// Save states also preserve bugs from old PPSSPP versions, so warn.
callbackMessage = sc->T("Loaded. Save in game, restart, and load for less bugs.");
return Status::WARNING;
}
// If the loaded state (saveDataGeneration) is older, the game may prevent saving again.
// This can happen with newer too, but ignore to/from 0 as a common likely safe case.
if (saveDataGeneration != lastSaveDataGeneration && saveDataGeneration != 0 && lastSaveDataGeneration != 0) {
if (saveDataGeneration < lastSaveDataGeneration)
callbackMessage = sc->T("Loaded. Game may refuse to save over newer savedata.");
else
callbackMessage = sc->T("Loaded. Game may refuse to save over different savedata.");
return Status::WARNING;
}
return Status::SUCCESS;
}
void Process()
{
if (g_Config.iRewindFlipFrequency != 0 && gpuStats.numFlips != 0)
@ -824,22 +863,12 @@ namespace SaveState
// Use the state's latest version as a guess for saveStateInitialGitVersion.
result = CChunkFileReader::Load(op.filename, &saveStateInitialGitVersion, state, &errorString);
if (result == CChunkFileReader::ERROR_NONE) {
callbackMessage = op.slot != LOAD_UNDO_SLOT ? slot_prefix + sc->T("Loaded State") : sc->T("State load undone");
callbackResult = Status::SUCCESS;
callbackMessage = op.slot != LOAD_UNDO_SLOT ? sc->T("Loaded State") : sc->T("State load undone");
callbackResult = TriggerLoadWarnings(callbackMessage);
hasLoadedState = true;
if (!g_Config.bHideStateWarnings && IsStale()) {
// For anyone wondering why (too long to put on the screen in an osm):
// Using save states instead of saves simulates many hour play sessions.
// Sometimes this exposes game bugs that were rarely seen on real devices,
// because few people played on a real PSP for 10 hours straight.
callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs.");
callbackResult = Status::WARNING;
} else if (!g_Config.bHideStateWarnings && IsOldVersion()) {
// Save states also preserve bugs from old PPSSPP versions, so warn.
callbackMessage = slot_prefix + sc->T("Loaded. Save in game, restart, and load for less bugs.");
callbackResult = Status::WARNING;
}
if (!slot_prefix.empty())
callbackMessage = slot_prefix + callbackMessage;
#ifndef MOBILE_DEVICE
if (g_Config.bSaveLoadResetsAVdumping) {
@ -963,6 +992,11 @@ namespace SaveState
}
}
void NotifySaveData() {
saveDataGeneration++;
lastSaveDataGeneration = saveDataGeneration;
}
void Cleanup() {
if (needsRestart) {
PSP_Shutdown();
@ -989,6 +1023,8 @@ namespace SaveState
hasLoadedState = false;
saveStateGeneration = 0;
saveDataGeneration = 0;
lastSaveDataGeneration = 0;
saveStateInitialGitVersion.clear();
}

View File

@ -102,6 +102,9 @@ namespace SaveState
// Check if there's any save stating needing to be done. Normally called once per frame.
void Process();
// Notify save state code that new save data has been written.
void NotifySaveData();
// Cleanup by triggering a restart if needed.
void Cleanup();
};