From 490f08d220910048d9af71026bf924e0011b9e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 25 Sep 2026 20:27:28 -0600 Subject: [PATCH] Savestate: Fix action and exit callback handling on load Delete the old HLE mips call actions instead of leaking them or keeping stale ones, fail the load on an unknown action type instead of crashing, and derive the exit-callback-pending flag from the loaded state. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/HLE.cpp | 20 +++++++++++++++++--- Core/HLE/sceKernelThread.cpp | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 19a18a96d4..e66e489806 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -486,16 +486,30 @@ void HLEDoState(PointerWrap &p) { if (s >= 2) { int actions = (int)mipsCallActions.size(); Do(p, actions); - if (actions != (int)mipsCallActions.size()) { - mipsCallActions.resize(actions); + if (p.mode == p.MODE_READ) { + for (PSPAction *action : mipsCallActions) { + delete action; + } + mipsCallActions.clear(); + if (actions < 0) { + p.SetError(p.ERROR_FAILURE); + return; + } + mipsCallActions.resize(actions, nullptr); } for (auto &action : mipsCallActions) { int actionTypeID = action != nullptr ? action->actionTypeID : -1; Do(p, actionTypeID); if (actionTypeID != -1) { - if (p.mode == p.MODE_READ) + if (p.mode == p.MODE_READ) { action = __KernelCreateAction(actionTypeID); + if (!action) { + ERROR_LOG(Log::SaveState, "Unable to load state: unknown action type %d", actionTypeID); + p.SetError(p.ERROR_FAILURE); + return; + } + } action->DoState(p); } } diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index da1ee11e43..8bafc3b1cc 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -223,6 +223,10 @@ public: { if (p.mode == p.MODE_READ) chainedAction = __KernelCreateAction(chainedActionType); + if (!chainedAction) { + p.SetError(p.ERROR_FAILURE); + return; + } chainedAction->DoState(p); } } @@ -286,6 +290,10 @@ public: auto s = p.Section("ActionAfterExitCallback", 1); if (!s) return; + if (p.mode == p.MODE_READ) { + // The dispatch is still in flight in the loaded state (see __KernelThreadingDoState.) + g_exitCallbackPending = true; + } } }; @@ -570,6 +578,10 @@ void MipsCall::DoState(PointerWrap &p) { if (p.mode == p.MODE_READ) doAfter = __KernelCreateAction(actionTypeID); + if (!doAfter) { + p.SetError(p.ERROR_FAILURE); + return; + } doAfter->DoState(p); } } @@ -810,6 +822,11 @@ void __KernelThreadingInit() { void __KernelThreadingDoState(PointerWrap &p) { + if (p.mode == p.MODE_READ) { + // Set again if an ActionAfterExitCallback is loaded (with the mips calls, later.) + g_exitCallbackPending = false; + } + auto s = p.Section("sceKernelThread", 1, 5); if (!s) return;