From d28b79960b919d84fac4873ba30fd35db234068f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 25 Sep 2026 18:36:49 -0600 Subject: [PATCH] Savestate: Fix loading kernel heaps and pre-exit-callback states KernelHeap was missing from CreateByIDType, and an unknown type returned without an error, desyncing the rest of the load. States from before exit callbacks kept the boot-time action slot, which sceMpeg's restore took over. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/sceKernel.cpp | 8 ++++++-- Core/HLE/sceKernelHeap.cpp | 4 ++++ Core/HLE/sceKernelHeap.h | 3 +++ Core/HLE/sceKernelThread.cpp | 4 ++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 197fe28021..0667a9d9e3 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -593,9 +593,11 @@ void KernelObjectPool::DoState(PointerWrap &p) { Do(p, type); pool[i] = CreateByIDType(type); - // Already logged an error. - if (pool[i] == nullptr) + // Already logged an error. The rest of the stream can't be read without knowing its size. + if (pool[i] == nullptr) { + p.SetError(p.ERROR_FAILURE); return; + } pool[i]->uid = i + handleOffset; } else { @@ -648,6 +650,8 @@ KernelObject *KernelObjectPool::CreateByIDType(int type) { return __KernelDirListingObject(); case SCE_KERNEL_TMID_ThreadEventHandler: return __KernelThreadEventHandlerObject(); + case PPSSPP_KERNEL_TMID_Heap: + return __KernelHeapObject(); default: ERROR_LOG(Log::SaveState, "Unable to load state: could not find object type %d.", type); diff --git a/Core/HLE/sceKernelHeap.cpp b/Core/HLE/sceKernelHeap.cpp index dd73a78584..c452366fba 100644 --- a/Core/HLE/sceKernelHeap.cpp +++ b/Core/HLE/sceKernelHeap.cpp @@ -45,6 +45,10 @@ struct KernelHeap : public KernelObject { } }; +KernelObject *__KernelHeapObject() { + return new KernelHeap; +} + static int sceKernelCreateHeap(int partitionId, int size, int flags, const char *Name) { // Everything below is recorded by pspautotests sysmem/kernel/heap, which is the first test // this API has ever had - these used to be guesses. diff --git a/Core/HLE/sceKernelHeap.h b/Core/HLE/sceKernelHeap.h index 1279c74962..1ae8ccae03 100644 --- a/Core/HLE/sceKernelHeap.h +++ b/Core/HLE/sceKernelHeap.h @@ -1,3 +1,6 @@ #pragma once +class KernelObject; + void Register_SysMemForKernel(); +KernelObject *__KernelHeapObject(); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 260c349f61..da1ee11e43 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -849,6 +849,10 @@ void __KernelThreadingDoState(PointerWrap &p) if (s >= 5) { Do(p, actionAfterExitCallback); __KernelRestoreActionType(actionAfterExitCallback, ActionAfterExitCallback::Create); + } else { + // Older states numbered the action types without this one, so the slot it got at boot may + // now be restored to another type (sceMpeg's). Give it a new one after all of those. + actionAfterExitCallback = __KernelRegisterActionType(ActionAfterExitCallback::Create); } Do(p, pausedDelays);