From a738fe9b86753a3fe67313d6b3ca02b340493965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Oct 2026 11:22:06 +0200 Subject: [PATCH] Scan modules that only exist in a savestate for functions A module the game loaded before the state was saved, but that this run never loaded, was never scanned, so loading such a state left all of its code without function replacements and hooks (and without functions in the debugger). Scan any loaded module with no analyzed functions in its text range before rehashing. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/sceKernelModule.cpp | 13 +++++++++++++ Core/MIPS/MIPSAnalyst.cpp | 10 ++++++++++ Core/MIPS/MIPSAnalyst.h | 2 ++ 3 files changed, 25 insertions(+) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 5b0e59f195..f411ce1b69 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -587,6 +587,19 @@ void __KernelModuleDoState(PointerWrap &p) { } } } + // A module that was only loaded in the state was never scanned, so nothing in it could be + // replaced or hooked (or show up as functions in the debugger.) + for (SceUID moduleId : loadedModules) { + PSPModule *module = kernelObjects.Get(moduleId, error); + if (!module || module->isFake || module->textEnd <= module->textStart) { + continue; + } + // textEnd is inclusive. + if (Memory::IsValid4AlignedRange(module->textStart, module->textEnd + 4 - module->textStart) && + !MIPSAnalyst::HasFunctionsInRange(module->textStart, module->textEnd + 4)) { + MIPSAnalyst::ScanForFunctions(module->textStart, module->textEnd, true); + } + } // The functions were found in memory from before the load, where other code may have been // (an overlay module, say.) Hash them from what's there now, so a hook only goes where it matches. MIPSAnalyst::RehashFunctions(); diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index bb6abd4cf2..19d98fd4e3 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -1295,6 +1295,16 @@ namespace MIPSAnalyst { return false; } + bool HasFunctionsInRange(u32 startAddr, u32 endAddr) { + std::lock_guard guard(functions_lock); + for (const AnalyzedFunction &f : functions) { + if (f.start >= startAddr && f.start < endAddr) { + return true; + } + } + return false; + } + void RehashFunctions() { std::lock_guard guard(functions_lock); HashFunctions(); diff --git a/Core/MIPS/MIPSAnalyst.h b/Core/MIPS/MIPSAnalyst.h index 1c1a679a78..fda9908b56 100644 --- a/Core/MIPS/MIPSAnalyst.h +++ b/Core/MIPS/MIPSAnalyst.h @@ -109,6 +109,8 @@ namespace MIPSAnalyst { void ForgetFunctions(u32 startAddr, u32 endAddr); bool GetAnalyzedFunctionAt(u32 addr, AnalyzedFunction *out); + // endAddr is exclusive. + bool HasFunctionsInRange(u32 startAddr, u32 endAddr); void LoadBuiltinHashMap(); void LoadHashMap(const Path &filename);