mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 06:50:29 +02:00
HLE: Send the VSH's LoadExec calls to HLE on every firmware version
The VSH launches games through the VSH variants of LoadExecForKernel, whose NIDs most firmware versions changed. Where HLE doesn't know the NID, the firmware's own loadexec.prx ran instead, and the launch got nowhere, so homebrew launched from the XMB only on firmware 3.73 and older, and 6.60/6.61. Each variant is the same small wrapper, loadExecVSH(apiType, file, opt, 0x10000), so when loadexec.prx exports one we don't know, look at the API type it loads into a0, and for Disc (0x120) and MS2 (0x141), overwrite it with a call to our HLE implementation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2072e0d568
commit
ba4a95d8ff
1 file changed
+42
@@ -804,6 +804,44 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char
|
||||
}
|
||||
}
|
||||
|
||||
// The VSH launches games through the VSH variants of LoadExecForKernel, whose NIDs most firmware
|
||||
// versions changed. Where we don't know the NID, the firmware's own loadexec.prx would run, and that
|
||||
// doesn't get anywhere. But each variant is the same small wrapper,
|
||||
// loadExecVSH(apiType, file, opt, 0x10000), so spot the ones we implement by the API type they pass
|
||||
// (li a0, apiType, in the delay slot of the call), and send them to HLE.
|
||||
static void RedirectLoadExecVSHExport(const FuncSymbolExport &func) {
|
||||
static const struct {
|
||||
u16 apiType;
|
||||
u32 nid;
|
||||
} redirects[] = {
|
||||
{ 0x120, 0xD8320A28 }, // sceKernelLoadExecVSHDisc
|
||||
{ 0x141, 0xD940C83C }, // sceKernelLoadExecVSHMs2
|
||||
};
|
||||
const u32 addr = func.symAddr;
|
||||
if (!Memory::IsValidRange(addr, 10 * 4)) {
|
||||
return;
|
||||
}
|
||||
bool hasFlags = false;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
const u32 op = Memory::ReadUnchecked_U32(addr + i * 4);
|
||||
if (op == 0x3C070001) { // lui a3, 1
|
||||
hasFlags = true;
|
||||
} else if ((op >> 16) == 0x2404 && hasFlags) { // li a0, imm
|
||||
for (const auto &redirect : redirects) {
|
||||
if ((op & 0xFFFF) == redirect.apiType) {
|
||||
INFO_LOG(Log::Loader, "LoadExecForKernel/%08x passes API type %03x, sending it to HLE as %08x", func.nid, redirect.apiType, redirect.nid);
|
||||
WriteHLESyscall("LoadExecForKernel", redirect.nid, addr);
|
||||
currentMIPS->InvalidateICacheRangeDeferred(addr, 8);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (op == MIPS_MAKE_JR_RA()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExportFuncSymbol(const FuncSymbolExport &func) {
|
||||
if (FuncImportIsHLE(func.moduleName, func.nid)) {
|
||||
// HLE covers this already - let's ignore the function.
|
||||
@@ -812,6 +850,10 @@ void ExportFuncSymbol(const FuncSymbolExport &func) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equals(func.moduleName, "LoadExecForKernel")) {
|
||||
RedirectLoadExecVSHExport(func);
|
||||
}
|
||||
|
||||
u32 error;
|
||||
for (SceUID moduleId : loadedModules) {
|
||||
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
|
||||
|
||||
Reference in new issue
Block a user