Add a developer setting to disable individual HLE modules, allowing them to be loaded properly.

Some games survive with a loaded sceAtrac, and start talking to
sceAudioCodec instead, the underlying library, though unsuccessfully
since it's not properly implemented yet.
This commit is contained in:
Henrik Rydgård
2025-04-02 10:19:24 +02:00
parent 5e18576f59
commit 343ca2600a
9 changed files with 210 additions and 128 deletions
+59 -115
View File
@@ -52,12 +52,11 @@
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/Util/BlockAllocator.h"
#include "Core/CoreTiming.h"
#include "Core/ConfigValues.h"
#include "Core/PSPLoaders.h"
#include "Core/System.h"
#include "Core/MemMapHelpers.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelModule.h"
#include "Core/HLE/sceKernelThread.h"
@@ -103,32 +102,6 @@ static const char * const lieAboutSuccessModules[] = {
"flash0:/kd/pspnet_resolver.prx",
};
// Modules to not load. TODO: Look into loosening this a little (say sceFont).
static const char * const blacklistedModules[] = {
"sceATRAC3plus_Library",
"sceFont_Library",
"SceFont_Library",
"SceHttp_Library",
"sceMpeg_library",
"sceNetAdhocctl_Library",
"sceNetAdhocDownload_Library",
"sceNetAdhocMatching_Library",
"sceNetApDialogDummy_Library",
"sceNetAdhoc_Library",
"sceNetApctl_Library",
"sceNetInet_Library",
"sceNetResolver_Library",
"sceNet_Library",
"sceNetAdhoc_Library",
"sceNetAdhocAuth_Service",
"sceNetAdhocctl_Library",
"sceNetIfhandle_Service",
"sceSsl_Module",
"sceDEFLATE_Library",
"sceMD5_Library",
"sceMemab",
};
const char *NativeModuleStatusToString(NativeModuleStatus status) {
switch (status) {
case MODULE_STATUS_STARTING: return "STARTING";
@@ -167,6 +140,34 @@ struct ModuleInfo {
char name[28];
};
struct PspLibStubEntry {
u32_le name;
u16_le version;
u16_le flags;
u8 size;
u8 numVars;
u16_le numFuncs;
// each symbol has an associated nid; nidData is a pointer
// (in .rodata.sceNid section) to an array of longs, one
// for each function, which identifies the function whose
// address is to be inserted.
//
// The hash is the first 4 bytes of a SHA-1 hash of the function
// name. (Represented as a little-endian long, so the order
// of the bytes is reversed.)
u32_le nidData;
// the address of the function stubs where the function address jumps
// should be filled in
u32_le firstSymAddr;
// Optional, this is where var relocations are.
// They use the format: u32 addr, u32 nid, ...
// WARNING: May have garbage if size < 6.
u32_le varData;
// Not sure what this is yet, assume garbage for now.
// TODO: Tales of the World: Radiant Mythology 2 has something here?
u32_le extra;
};
PSPModule::~PSPModule() {
if (memoryBlockAddr) {
// If it's either below user memory, or using a high kernel bit, it's in kernel.
@@ -639,14 +640,26 @@ void UnexportVarSymbol(const VarSymbolExport &var) {
}
}
static bool FuncImportIsHLE(std::string_view module, u32 nid) {
// TODO: Take into account whether HLE is enabled for the module.
// Also, this needs to be more efficient.
if (!ShouldHLEModuleByImportName(module)) {
return false;
}
return GetHLEFunc(module, nid) != nullptr;
}
void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char *importingModule) {
// Prioritize HLE implementations.
// TODO: Or not?
if (FuncImportIsSyscall(func.moduleName, func.nid)) {
bool shouldHLE = ShouldHLEModuleByImportName(func.moduleName);
// Prioritize HLE implementations, if we should HLE this.
if (shouldHLE && GetHLEFunc(func.moduleName, func.nid)) {
if (reimporting && Memory::Read_Instruction(func.stubAddr + 4) != GetSyscallOp(func.moduleName, func.nid)) {
WARN_LOG(Log::Loader, "Reimporting updated syscall %s", GetHLEFuncName(func.moduleName, func.nid));
}
WriteSyscall(func.moduleName, func.nid, func.stubAddr);
// TODO: There's some double lookup going on here (we already did the lookup in GetHLEFunc above).
WriteHLESyscall(func.moduleName, func.nid, func.stubAddr);
currentMIPS->InvalidateICache(func.stubAddr, 8);
if (g_Config.bPreloadFunctions) {
MIPSAnalyst::PrecompileFunction(func.stubAddr, 8);
@@ -678,21 +691,21 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char
}
// It hasn't been exported yet, but hopefully it will later. Check if we know about it through HLE.
const bool isKnownHLEModule = GetHLEModuleIndex(func.moduleName) != -1;
if (isKnownHLEModule) {
if (shouldHLE) {
// We used to report this, but I don't think it's very interesting anymore.
WARN_LOG(Log::Loader, "Unknown syscall from known HLE module '%s': 0x%08x (import for '%s')", func.moduleName, func.nid, importingModule);
} else {
INFO_LOG(Log::Loader, "Function (%s,%08x) unresolved in '%s', storing for later resolving", func.moduleName, func.nid, importingModule);
}
if (isKnownHLEModule || !reimporting) {
if (shouldHLE || !reimporting) {
WriteFuncMissingStub(func.stubAddr, func.nid);
currentMIPS->InvalidateICache(func.stubAddr, 8);
}
}
void ExportFuncSymbol(const FuncSymbolExport &func) {
if (FuncImportIsSyscall(func.moduleName, func.nid)) {
if (FuncImportIsHLE(func.moduleName, func.nid)) {
// HLE covers this already - let's ignore the function.
WARN_LOG(Log::Loader, "Ignoring func export %s/%08x, already implemented in HLE.", func.moduleName, func.nid);
return;
@@ -720,7 +733,7 @@ void ExportFuncSymbol(const FuncSymbolExport &func) {
}
void UnexportFuncSymbol(const FuncSymbolExport &func) {
if (FuncImportIsSyscall(func.moduleName, func.nid)) {
if (FuncImportIsHLE(func.moduleName, func.nid)) {
// Oops, HLE covers this.
return;
}
@@ -769,72 +782,7 @@ void PSPModule::Cleanup() {
}
}
static bool IsHLEVersionedModule(const char *name) {
// TODO: Only some of these are currently known to be versioned.
// Potentially only sceMpeg_library matters.
// For now, we're just reporting version numbers.
for (size_t i = 0; i < ARRAY_SIZE(blacklistedModules); i++) {
if (!strncmp(name, blacklistedModules[i], 28)) {
return true;
}
}
static const char *otherModules[] = {
"sceAvcodec_driver",
"sceAudiocodec_Driver",
"sceAudiocodec",
"sceVideocodec_Driver",
"sceVideocodec",
"sceMpegbase_Driver",
"sceMpegbase",
"scePsmf_library",
"scePsmfP_library",
"scePsmfPlayer",
"sceSAScore",
"sceCcc_Library",
"SceParseHTTPheader_Library",
"SceParseURI_Library",
// Guessing.
"sceJpeg",
"sceJpeg_library",
"sceJpeg_Library",
};
for (size_t i = 0; i < ARRAY_SIZE(otherModules); i++) {
if (!strncmp(name, otherModules[i], 28)) {
return true;
}
}
return false;
}
static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, bool reimporting) {
struct PspLibStubEntry {
u32_le name;
u16_le version;
u16_le flags;
u8 size;
u8 numVars;
u16_le numFuncs;
// each symbol has an associated nid; nidData is a pointer
// (in .rodata.sceNid section) to an array of longs, one
// for each function, which identifies the function whose
// address is to be inserted.
//
// The hash is the first 4 bytes of a SHA-1 hash of the function
// name. (Represented as a little-endian long, so the order
// of the bytes is reversed.)
u32_le nidData;
// the address of the function stubs where the function address jumps
// should be filled in
u32_le firstSymAddr;
// Optional, this is where var relocations are.
// They use the format: u32 addr, u32 nid, ...
// WARNING: May have garbage if size < 6.
u32_le varData;
// Not sure what this is yet, assume garbage for now.
// TODO: Tales of the World: Radiant Mythology 2 has something here?
u32_le extra;
};
// Can't run - we didn't keep track of the libstub entry.
if (module->libstub == 0) {
return false;
@@ -1099,7 +1047,8 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
head = (const PSP_Header *)ptr;
devkitVersion = head->devkitversion;
if (IsHLEVersionedModule(head->modname)) {
bool wasDisabled;
if (ShouldHLEModule(head->modname, &wasDisabled)) {
int ver = (head->module_ver_hi << 8) | head->module_ver_lo;
INFO_LOG(Log::sceModule, "Loading module %s with version %04x, devkit %08x, crc %x", head->modname, ver, head->devkitversion, module->crc);
@@ -1108,6 +1057,9 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
fakeLoadedModule = true;
}
if (wasDisabled) {
g_OSD.Show(OSDType::MESSAGE_WARNING, StringFromFormat("HLE for %s has been manually disabled", head->modname));
}
const u8 *in = ptr;
const auto isGzip = head->comp_attribute & 1;
// Kind of odd.
@@ -1310,16 +1262,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
char moduleName[29] = {0};
strncpy(moduleName, modinfo->name, ARRAY_SIZE(module->nm.name));
// Check for module blacklist - we don't allow games to load these modules from disc
// as we have HLE implementations and the originals won't run in the emu because they
// directly access hardware or for other reasons.
for (u32 i = 0; i < ARRAY_SIZE(blacklistedModules); i++) {
if (strncmp(modinfo->name, blacklistedModules[i], ARRAY_SIZE(modinfo->name)) == 0) {
module->isFake = true;
}
}
if (!module->isFake && module->memoryBlockAddr != 0) {
if (module->memoryBlockAddr != 0) {
g_symbolMap->AddModule(moduleName, module->memoryBlockAddr, module->memoryBlockSize);
}
@@ -1415,6 +1358,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
}
// Look at the exports, too.
// TODO: Add them to the symbol map!
struct PspLibEntEntry {
u32_le name; /* ent's name (module name) address */
@@ -1500,7 +1444,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
func.nid = nid;
func.symAddr = exportAddr;
if (ent->name == 0) {
WARN_LOG_REPORT(Log::HLE, "Exporting func from syslib export: %08x", nid);
WARN_LOG(Log::HLE, "Exporting func from syslib export: %08x", nid);
}
module->ExportFunc(func);
}
@@ -1587,7 +1531,7 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
delete [] newptr;
if (!reportedModule && IsHLEVersionedModule(modinfo->name)) {
if (!reportedModule && ShouldHLEModule(modinfo->name)) {
INFO_LOG(Log::sceModule, "Loading module %s with version %04x, devkit %08x", modinfo->name, modinfo->moduleVersion, devkitVersion);
if (!strcmp(modinfo->name, "sceMpeg_library")) {