mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-20 17:59:08 +02:00
SaveState: Report failed writes, and call back on network refusals
- A failed final flush (a full disk) or a failed rename into place was reported as "Saved State". A failed load undo rename still pointed load undo at the current game, while the file was another game's state. - Refusing a save or load for being online didn't call the callback, unlike every other refusal. - A rewind dropped after its decode started stayed active, and stopped rewind snapshots until the next clear. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f45f4d5c85
commit
3cc61d3f8e
5 files changed
+33
-4
No files matched your search
@@ -601,6 +601,11 @@ CChunkFileReader::Error CChunkFileReader::SaveFile(const Path &filename, const s
|
||||
INFO_LOG(Log::SaveState, "Savestate: Compressed %i bytes into %i", (int)sz, (int)writeLen);
|
||||
}
|
||||
free(buffer);
|
||||
// Buffered writes can still fail here (a full disk.)
|
||||
if (!pFile.Close() && success) {
|
||||
ERROR_LOG(Log::SaveState, "ChunkReader: Failed closing file");
|
||||
success = false;
|
||||
}
|
||||
if (!success) {
|
||||
return ERROR_BAD_FILE;
|
||||
}
|
||||
|
||||
+14
-2
@@ -414,6 +414,7 @@ int g_screenshotFailures;
|
||||
|
||||
static bool QueueLoad(const Operation &op) {
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
CallbackRefused(op.callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -425,6 +426,7 @@ int g_screenshotFailures;
|
||||
|
||||
static bool QueueSave(const Operation &op) {
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
CallbackRefused(op.callback);
|
||||
return false;
|
||||
}
|
||||
if (AttachToPendingSave(op.path, op.callback)) {
|
||||
@@ -646,6 +648,7 @@ int g_screenshotFailures;
|
||||
|
||||
bool UndoLoad(std::string_view gamePrefix, Callback callback) {
|
||||
if (NetworkWarnUserIfOnlineAndCantSavestate()) {
|
||||
CallbackRefused(callback);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1159,7 +1162,10 @@ int g_screenshotFailures;
|
||||
} else {
|
||||
DeleteIfExists(job.path);
|
||||
}
|
||||
File::Rename(job.writePath, job.path);
|
||||
if (!File::Rename(job.writePath, job.path)) {
|
||||
ERROR_LOG(Log::SaveState, "Failed to move '%s' into place", job.writePath.c_str());
|
||||
result = CChunkFileReader::ERROR_BAD_FILE;
|
||||
}
|
||||
}
|
||||
PostCompletion([job, result](bool callCallbacks) {
|
||||
FinishSave(job, result, callCallbacks);
|
||||
@@ -1193,7 +1199,10 @@ int g_screenshotFailures;
|
||||
g_saveSnapshots--;
|
||||
if (result == CChunkFileReader::ERROR_NONE) {
|
||||
DeleteIfExists(backup);
|
||||
File::Rename(temp, backup);
|
||||
if (!File::Rename(temp, backup)) {
|
||||
// The undo would load whatever was there before, maybe another game's state.
|
||||
result = CChunkFileReader::ERROR_BAD_FILE;
|
||||
}
|
||||
}
|
||||
// Not a callback, so it runs at shutdown too.
|
||||
PostCompletion([result, gamePrefix](bool) {
|
||||
@@ -1404,6 +1413,9 @@ int g_screenshotFailures;
|
||||
needsProcess = false;
|
||||
lock.unlock();
|
||||
for (const Operation &op : operations) {
|
||||
if (op.type == OperationType::Rewind && op.started) {
|
||||
rewindStates.CancelRestore();
|
||||
}
|
||||
RefuseOperation(op);
|
||||
}
|
||||
return;
|
||||
|
||||
+2
-2
@@ -109,11 +109,11 @@ namespace SaveState {
|
||||
CChunkFileReader::Error LoadFromRam(std::vector<u8> &state, std::string *errorString);
|
||||
|
||||
// For testing / automated tests. Runs a save state verification pass (async.)
|
||||
// Warning: callback will be called on a different thread.
|
||||
// The callback runs from Process().
|
||||
bool Verify(Callback callback = Callback());
|
||||
|
||||
// To go back to a previous snapshot (only if enabled.)
|
||||
// Warning: callback will be called on a different thread.
|
||||
// The callback runs from Process(), once the snapshot has been applied.
|
||||
bool Rewind(Callback callback = Callback());
|
||||
|
||||
// Returns true if there are rewind snapshots available.
|
||||
|
||||
@@ -161,6 +161,16 @@ CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString, std::
|
||||
return FinishRestore(errorString, metadata);
|
||||
}
|
||||
|
||||
void StateRingbuffer::CancelRestore() {
|
||||
if (!restoreActive_) {
|
||||
return;
|
||||
}
|
||||
// The decode may still be running into restoreBuffer_.
|
||||
WaitForWorker();
|
||||
restoreActive_ = false;
|
||||
std::vector<u8>().swap(restoreBuffer_);
|
||||
}
|
||||
|
||||
// Only moves the ring back: the state's buffer may still be in use by a worker job, and is reused
|
||||
// by the next snapshot anyway.
|
||||
void StateRingbuffer::DropNewest() {
|
||||
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
CChunkFileReader::Error FinishRestore(std::string *errorString, std::string *metadata);
|
||||
// Both halves, waiting for the worker in between.
|
||||
CChunkFileReader::Error Restore(std::string *errorString, std::string *metadata);
|
||||
// For a rewind that was started and then dropped.
|
||||
void CancelRestore();
|
||||
void Clear();
|
||||
|
||||
bool Empty() const {
|
||||
|
||||
Reference in new issue
Block a user