From 039ffc682d6a7729b4aff46dae1573f0031f4275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 11 Sep 2026 10:47:25 -0600 Subject: [PATCH] HLE: graduate the leaf libraries games carry on the disc A survey of disc images found these shipped on a lot of them: LIBDEFLT and LIBMT19937, LIBADLER, LIBMD5 on about 1/6, LIBSHA256 on 1/7, LIBHEAP on 1/12, LIBSFMT19937 on 1/24. None appears in any firmware dump and none is loadable through sceUtility, so a game that imports one has to carry it - which makes running the game's own module the safe default, the same argument psmfplayer graduated on. Read off real discs with --re-module disc0:/..., all but sceHeap import nothing at all, and sceHeap only Kernel_Library and ThreadManForUser. sceFont joins them with a condition. Its module is on the disc like the others - the most-shipped of all, on a third of the discs - but it reads its fonts from flash0:/font and has nothing to fall back on, so it is only usable with a firmware dump installed (our own fallback fonts are just not good enough anyway, it chokes on them). CheckDisableHLEAvailability puts the HLE back when those fonts are missing, since ours does have a fallback. sceParseUri and sceParseHttp look like candidates and aren't: they are in the firmware and sceUtility can load them, so a game may import them without shipping one. Their headers say so. The HLE implementations stay - old savestates index these tables by syscall opcode - and are marked legacy the way scePsmf is. Also: Move HLEInit after the filesystems are mounted It ran before MountFileSystems, so the checks in it deciding whether a library can run its real module instead of our HLE couldn't ask the PSP filesystem and had to reach for host paths under the NAND directory. Nothing between CoreTiming::Init and here touches HLE, and the kernel and the game's modules are both loaded later, so it can simply move. Two things fall out. The checks now spell their paths flash0:/... like the rest of the emulator. And they run after AutoInstallFirmwareFromDisc, so a disc that carries a firmware counts on the launch that installs it rather than the next one - verified with an empty NAND and a disc carrying 3.11: the fonts land and sceFont runs the game's own module the same boot. Co-Authored-By: Claude Opus 5 (1M context) --- Core/ConfigValues.h | 13 ++++++++++- Core/HLE/HLE.cpp | 51 +++++++++++++++++++++++++++++++++++++---- Core/HLE/sceAdler.h | 6 +++++ Core/HLE/sceDeflt.h | 6 +++++ Core/HLE/sceFont.cpp | 2 +- Core/HLE/sceFont.h | 6 +++++ Core/HLE/sceHeap.h | 6 +++++ Core/HLE/sceMd5.h | 6 +++++ Core/HLE/sceMt19937.h | 6 +++++ Core/HLE/sceParseHttp.h | 6 +++++ Core/HLE/sceParseUri.h | 6 +++++ Core/HLE/sceSfmt19937.h | 6 +++++ Core/HLE/sceSha256.h | 6 +++++ Core/System.cpp | 10 +++++--- 14 files changed, 126 insertions(+), 10 deletions(-) diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 3f76185ec8..e1caa22745 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -166,7 +166,18 @@ enum class DisableHLEFlags : int { sceCcc = (1 << 7), // character conversion library. // Swaps in flash0:/kd/libmp4.prx and mp4msv.prx, which then decode through our sceAudiocodec. sceMp4 = (1 << 8), - Count = 9, + // Small leaf libraries games carry on the disc themselves - see AlwaysDisableHLEFlags. None of + // them is in any firmware dump, so the game's own copy is the only one there is, and none + // imports anything we don't already have. + sceDeflt = (1 << 9), + sceAdler = (1 << 10), + sceMd5 = (1 << 11), + sceSha256 = (1 << 12), + sceMt19937 = (1 << 13), + sceSfmt19937 = (1 << 14), + sceHeap = (1 << 15), + sceParseUri = (1 << 16), + Count = 17, // TODO: Some of the networking libraries may be interesting candidates, like HTTP. }; ENUM_CLASS_BITOPS(DisableHLEFlags); diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 206b9054f7..ec60ecf29a 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -43,6 +43,8 @@ #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceKernelModule.h" +#include "Core/HLE/sceFont.h" +#include "Core/FileSystems/MetaFileSystem.h" #include "Core/HLE/HLE.h" enum { @@ -141,8 +143,6 @@ static const HLEModuleMeta g_moduleMeta[] = { {"sceNetAdhocctl_Library"}, {"sceNetIfhandle_Service"}, {"sceSsl_Module"}, - {"sceDEFLATE_Library"}, - {"sceMD5_Library"}, {"sceMemab"}, // Underlying AdHoc crypto library {"sceAvcodec_driver"}, {"sceAudiocodec_Driver"}, @@ -160,7 +160,17 @@ static const HLEModuleMeta g_moduleMeta[] = { {"sceMp4_library", "sceMp4", DisableHLEFlags::sceMp4}, {"mp4msv_module", "mp4msv", DisableHLEFlags::sceMp4}, {"SceParseHTTPheader_Library", "sceParseHttp", DisableHLEFlags::sceParseHttp}, - {"SceParseURI_Library"}, + {"SceParseURI_Library", "sceParseUri", DisableHLEFlags::sceParseUri}, + // Leaf libraries games carry on the disc. Module names and export library names read off the + // copies on real discs with --re-module disc0:/...; all but sceHeap import nothing at all, and + // sceHeap only Kernel_Library and ThreadManForUser. + {"sceDEFLATE_Library", "sceDeflt", DisableHLEFlags::sceDeflt}, + {"sceADLER32_Library", "sceAdler", DisableHLEFlags::sceAdler}, + {"sceMD5_Library", "sceMd5", DisableHLEFlags::sceMd5}, + {"sceSHA256_Library", "sceSha256", DisableHLEFlags::sceSha256}, + {"sceMT19937_Library", "sceMt19937", DisableHLEFlags::sceMt19937}, + {"sceSfmt19937_Library", "sceSfmt19937", DisableHLEFlags::sceSfmt19937}, + {"sceHeap_Library", "sceHeap", DisableHLEFlags::sceHeap}, // Guessing these names {"sceJpeg", "sceJpeg"}, {"sceJpeg_library", "sceJpeg"}, @@ -200,7 +210,25 @@ DisableHLEFlags AlwaysDisableHLEFlags() { // // PSMF testing issue: #20200 // sceCcc is simply a character conversion library, zero deps. If available we just load it. - return DisableHLEFlags::scePsmf | DisableHLEFlags::scePsmfPlayer | DisableHLEFlags::sceCcc; + // sceDeflt is zip format decompression. + // sceSmft19937 and sceMt19937 are random number generation. + // sceAdler, sceSha256, sceMd5 are hashes. + // sceHeap is a memory allocator wrapper. + // + // All these are found in game discs, and are basically dependency-less libraries that we + // can just run as-is, no need for HLE. Games always ship these if they use them. + // + // sceFont is the odd one out: the module is on the disc like the others, but it reads its fonts + // from flash0:/font, so it is only usable with a firmware dump installed - see + // CheckDisableHLEAvailability, which puts the HLE back when those fonts aren't there. + // + // sceParseUri and sceParseHttp are not here - those two are also in the firmware, and + // sceUtility can load them (modules 0x103 and 0x104), so unlike the rest a game may import them + // without carrying a copy. + return DisableHLEFlags::scePsmf | DisableHLEFlags::scePsmfPlayer | DisableHLEFlags::sceCcc | + DisableHLEFlags::sceDeflt | DisableHLEFlags::sceAdler | DisableHLEFlags::sceMd5 | + DisableHLEFlags::sceSha256 | DisableHLEFlags::sceMt19937 | DisableHLEFlags::sceSfmt19937 | + DisableHLEFlags::sceHeap | DisableHLEFlags::sceFont; } // Which modules we're HLE-ing is part of the machine's state, not a live setting: it's decided @@ -314,9 +342,22 @@ static void hleDelayResultFinish(u64 userdata, int cycleslate) { static void CheckDisableHLEAvailability() { g_unavailableDisableFlags = (DisableHLEFlags)0; + // The real libfont.prx a disc ships reads its fonts from flash0:/font and has nothing to fall + // back on, so without them it would render nothing at all. Our HLE does have a fallback - the + // fonts in assets - so keep it when the NAND set isn't there. Same question the HLE font loader + // asks itself, so the same answer: "the fonts this game's firmware would have had", not every + // font we know of, since an older game's firmware never had the later ones. + if (AlwaysDisableHLEFlags() & DisableHLEFlags::sceFont) { + if (!NandFontsComplete()) { + g_unavailableDisableFlags |= DisableHLEFlags::sceFont; + INFO_LOG(Log::HLE, "flash0:/font doesn't have this firmware's fonts - using the HLE sceFont rather than the disc's."); + } + } + if ((DisableHLEFlags)g_Config.iDisableHLE & DisableHLEFlags::sceMp4) { const Path kd = g_Config.nandRootDirectory / "flash0" / "kd"; - if (!File::Exists(kd / "libmp4.prx") || !File::Exists(kd / "mp4msv.prx")) { + if (!pspFileSystem.GetFileInfo("flash0:/kd/libmp4.prx").exists || + !pspFileSystem.GetFileInfo("flash0:/kd/mp4msv.prx").exists) { g_unavailableDisableFlags |= DisableHLEFlags::sceMp4; ERROR_LOG(Log::HLE, "Asked to run the real sceMp4, but %s doesn't have libmp4.prx and " "mp4msv.prx - keeping the HLE.", kd.c_str()); diff --git a/Core/HLE/sceAdler.h b/Core/HLE/sceAdler.h index 9acd6a2042..7bdc8b8c7e 100644 --- a/Core/HLE/sceAdler.h +++ b/Core/HLE/sceAdler.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceAdler is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once void Register_sceAdler(); diff --git a/Core/HLE/sceDeflt.h b/Core/HLE/sceDeflt.h index 8196ab8869..8c56066f21 100644 --- a/Core/HLE/sceDeflt.h +++ b/Core/HLE/sceDeflt.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceDeflt is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once void Register_sceDeflt(); diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index c6c89fdc0c..0c83bb0d45 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -907,7 +907,7 @@ static int GameFirmwareVersion() { // // One directory listing rather than a stat per font, since on Android's scoped storage the // individual checks are slow. -static bool NandFontsComplete() { +bool NandFontsComplete() { const int firmware = GameFirmwareVersion(); std::string_view fontDir(g_nandFontPath); diff --git a/Core/HLE/sceFont.h b/Core/HLE/sceFont.h index 9291029807..7a7a0a433a 100644 --- a/Core/HLE/sceFont.h +++ b/Core/HLE/sceFont.h @@ -5,6 +5,12 @@ class PointerWrap; void Register_sceFont(); void Register_sceLibFttt(); +// Whether flash0:/font holds the fonts this game's firmware would have had. Used two ways: the +// HLE font loader asks so it knows whether to unpack them off the disc's updater first, and +// CheckDisableHLEAvailability asks because the real libfont.prx a disc ships reads its fonts from +// there and has nothing to fall back on - so it can only replace our HLE when they're present. +bool NandFontsComplete(); + void __FontInit(); void __FontShutdown(); void __FontDoState(PointerWrap &p); \ No newline at end of file diff --git a/Core/HLE/sceHeap.h b/Core/HLE/sceHeap.h index 68e1c808d8..06d02929c2 100644 --- a/Core/HLE/sceHeap.h +++ b/Core/HLE/sceHeap.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceHeap is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once void Register_sceHeap(); diff --git a/Core/HLE/sceMd5.h b/Core/HLE/sceMd5.h index 311103d28c..23837626de 100644 --- a/Core/HLE/sceMd5.h +++ b/Core/HLE/sceMd5.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceMd5 is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once #include "Common/CommonTypes.h" diff --git a/Core/HLE/sceMt19937.h b/Core/HLE/sceMt19937.h index 71ed7afc5a..981eb6216f 100644 --- a/Core/HLE/sceMt19937.h +++ b/Core/HLE/sceMt19937.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceMt19937 is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once void Register_sceMt19937(); diff --git a/Core/HLE/sceParseHttp.h b/Core/HLE/sceParseHttp.h index 6c67a246b2..154a6328e0 100644 --- a/Core/HLE/sceParseHttp.h +++ b/Core/HLE/sceParseHttp.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: a graduation candidate, but not graduated. sceParseHttp is a small library with no +// dependencies beyond the kernel, and most games that use it ship their own copy - but it is also +// in the firmware, and sceUtility can load it (module 0x104), so a game may import it without +// carrying one. Letting the game's module win would hand that game unresolved imports. It can +// follow once sceUtility loads the firmware copy the way it does for sceMp3 and sceMp4. + #pragma once void Register_sceParseHttp(); \ No newline at end of file diff --git a/Core/HLE/sceParseUri.h b/Core/HLE/sceParseUri.h index 9b3bb7d06b..5d7b677b2c 100644 --- a/Core/HLE/sceParseUri.h +++ b/Core/HLE/sceParseUri.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: a graduation candidate, but not graduated. sceParseUri is a small library with no +// dependencies beyond the kernel, and most games that use it ship their own copy - but it is also +// in the firmware, and sceUtility can load it (module 0x103), so a game may import it without +// carrying one. Letting the game's module win would hand that game unresolved imports. It can +// follow once sceUtility loads the firmware copy the way it does for sceMp3 and sceMp4. + #pragma once #ifdef _MSC_VER diff --git a/Core/HLE/sceSfmt19937.h b/Core/HLE/sceSfmt19937.h index 31b62663f1..0cd16f0cf1 100644 --- a/Core/HLE/sceSfmt19937.h +++ b/Core/HLE/sceSfmt19937.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceSfmt19937 is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once void Register_sceSfmt19937(); diff --git a/Core/HLE/sceSha256.h b/Core/HLE/sceSha256.h index eefb687f48..05d34c284d 100644 --- a/Core/HLE/sceSha256.h +++ b/Core/HLE/sceSha256.h @@ -15,6 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// NOTE: This is now unmaintained legacy code. sceSha256 is a small leaf library that games ship +// on the disc themselves and that appears in no firmware dump, so we let the game's own module run +// and this HLE is only a fallback for the rare game that imports it without shipping it, and for +// old savestates - whose syscall opcodes index these tables, which is why nothing here is removed. +// See AlwaysDisableHLEFlags in Core/HLE/HLE.cpp. + #pragma once void Register_sceSha256(); diff --git a/Core/System.cpp b/Core/System.cpp index 740d6ddbee..62f75b570c 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -506,9 +506,6 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin DisplayHWInit(); - // Init all the HLE modules - HLEInit(); - // TODO: Put this somewhere better? if (!g_CoreParameter.mountIso.empty()) { g_CoreParameter.mountIsoLoader = ConstructFileLoader(g_CoreParameter.mountIso); @@ -521,6 +518,13 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin MountFileSystems(); + // Init all the HLE modules. After the mount, and after the firmware install above, so that the + // checks in HLEInit deciding whether a library can run its real module instead of our HLE can + // ask the PSP filesystem the way everything else does - and see a firmware this very boot just + // installed off the disc. Nothing between CoreTiming::Init above and here touches HLE, and the + // kernel and the game's modules both come later, in the switch below. + HLEInit(); + // Game-specific settings are load from for example Load_PSP_ISO (which calls g_Config.LoadGameConfig). // We can't do things that depend on these before the below switch. So for example, the adjustment of the GPU core // to software has now been moved below it.