mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-22 02:39:05 +02:00
POPS: Boot converted PBPs with firmware 5.55
5.55's manager is an older design: the kernel hands it the version key (there's no license probe to replace), and the emulator opens the game through a manager export that first looks up its license. A converted PBP has none, so that lookup now returns success, as the MAC checks do. Store games still need 6.00 or later, so the 5.55 layout only matches for converted PBPs. Its code changed too much to map every import by call site, so its NID aliases are only the ones used here, each checked by its call site (and the sysreg ones by lowio.prx's code). The emulator's disc read call is now found by the block size next to it, which 5.55's pops.prx passes from the other side of the jal: the same single site as before in every 6.x emulator. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d008453255
commit
5460e75265
2 files changed
+59
-18
No files matched your search
@@ -348,6 +348,28 @@ static const HLENidAlias hleNidAliases[] = {
|
||||
{ "SysMemForKernel", 0xB9796F69, 0xB4F00CB5 }, // sceKernelGetCompiledSdkVersion
|
||||
{ "SysMemForKernel", 0xF5780DAA, 0xC90B0992 }, // sceKernelGetUIDcontrolBlock
|
||||
{ "SysMemForKernel", 0xFEB5C72B, 0xF12A62F7 },
|
||||
// 5.55, from popsman.prx, whose code changed too much to match every import: only the ones used here,
|
||||
// each checked by its call site.
|
||||
{ "LoadExecForKernel", 0x9C9BB2D8, 0xC3474C2A }, // sceKernelExitVSHKernel
|
||||
{ "ModuleMgrForKernel", 0xBB8C8FDF, 0x3FF74DF1 }, // sceKernelStartModule
|
||||
{ "ModuleMgrForKernel", 0xC5A281C5, 0x939E4270 }, // sceKernelLoadModuleForKernel
|
||||
{ "SysMemForKernel", 0x2CFF6F90, 0xB4F00CB5 }, // sceKernelGetCompiledSdkVersion
|
||||
{ "SysMemForKernel", 0x950BCB31, 0xF12A62F7 },
|
||||
{ "SysMemForKernel", 0xD7E24299, 0xC90B0992 }, // sceKernelGetUIDcontrolBlock
|
||||
{ "sceGe_driver", 0x1F226BA9, 0xD8633888 }, // sceGeEdramSetSize
|
||||
{ "sceImpose_driver", 0xA516434B, 0x5557F4E2 }, // sceImposeGetBatteryIconStatus
|
||||
{ "sceSysreg_driver", 0x08E0EE7E, 0x38527743 }, // sceSysregMeBusClockEnable660
|
||||
{ "sceSysreg_driver", 0x0E6301D0, 0xDB97C70E }, // sceSysregSemaUnlock660
|
||||
{ "sceSysreg_driver", 0x20C9E2C4, 0x457FEBA9 }, // sceSysregMeResetEnable660
|
||||
{ "sceSysreg_driver", 0x410C74F7, 0x4634C9DC }, // sceSysreg_driver_4634C9DC
|
||||
{ "sceSysreg_driver", 0x461B8077, 0x0143E8A8 }, // sceSysregSemaTryLock660
|
||||
{ "sceSysreg_driver", 0x48869494, 0x41B0337B }, // sceSysreg_driver_41B0337B
|
||||
{ "sceSysreg_driver", 0x5F190A4E, 0x48F1C4AD }, // sceSysregMeResetDisable660
|
||||
{ "sceSysreg_driver", 0x663D5471, 0x0AE8E549 }, // sceSysregAvcResetEnable660
|
||||
{ "sceSysreg_driver", 0x823FFD7A, 0xB1751B06 }, // sceSysreg_driver_B1751B06
|
||||
{ "sceSysreg_driver", 0x923509A1, 0xC36775AD }, // sceSysreg_driver_C36775AD
|
||||
{ "sceSysreg_driver", 0xC7FC7669, 0x8B1DD83A }, // sceSysreg_driver_8B1DD83A
|
||||
{ "sceSysreg_driver", 0xE46A0890, 0x4B4CCE80 }, // sceSysreg_driver_4B4CCE80
|
||||
};
|
||||
|
||||
u32 GetHLENidAlias(std::string_view moduleName, u32 nid) {
|
||||
|
||||
+37
-18
@@ -64,7 +64,7 @@ struct PopsManLayout {
|
||||
u32 loadModule;
|
||||
u32 loadModuleFrame; // Its first instruction, addiu sp, sp, -frame.
|
||||
// The license check (path, keyOut, fingerprintOut). Its first success latches the version key and
|
||||
// the content ID fingerprint (6.60's +0124).
|
||||
// the content ID fingerprint (6.60's +0124). 5.x has none: the kernel hands the key to the manager.
|
||||
u32 keyProbe;
|
||||
u32 versionKey;
|
||||
u32 keyLatched;
|
||||
@@ -73,15 +73,19 @@ struct PopsManLayout {
|
||||
// sceMeAudio_F6637A72).
|
||||
u32 checkMac;
|
||||
u32 checkMacExport;
|
||||
// 5.x: the license lookup in the export the emulator opens the game with (5.55's
|
||||
// sceMeAudio_A1457825 calls scePspNpDrm_driver_4478C033 on the content ID).
|
||||
u32 licenseCall;
|
||||
};
|
||||
|
||||
// The managers have the same code at these places, and older ones import kernel functions by older
|
||||
// NIDs (see GetHLENidAlias()).
|
||||
static const PopsManLayout POPSMAN_LAYOUTS[] = {
|
||||
{ 0x1E20, 0x120, 0x266C, 0x49E0, 0x4A18, 0x4A1C, 0x0A28, 0x0AB8 }, // 6.35-6.61
|
||||
{ 0x1E48, 0x120, 0x2694, 0x4AE0, 0x4B18, 0x4B1C, 0x0A28, 0x0AB8 }, // 6.30-6.31
|
||||
{ 0x1E48, 0x130, 0x26F8, 0x4B30, 0x4B68, 0x4B6C, 0x0A28, 0x0AB8 }, // 6.20
|
||||
{ 0x19E8, 0x130, 0x2270, 0x46D0, 0x4704, 0x4708, 0x07B8, 0x0848 }, // 6.00-6.10
|
||||
{ 0x1E20, 0x120, 0x266C, 0x49E0, 0x4A18, 0x4A1C, 0x0A28, 0x0AB8, 0 }, // 6.35-6.61
|
||||
{ 0x1E48, 0x120, 0x2694, 0x4AE0, 0x4B18, 0x4B1C, 0x0A28, 0x0AB8, 0 }, // 6.30-6.31
|
||||
{ 0x1E48, 0x130, 0x26F8, 0x4B30, 0x4B68, 0x4B6C, 0x0A28, 0x0AB8, 0 }, // 6.20
|
||||
{ 0x19E8, 0x130, 0x2270, 0x46D0, 0x4704, 0x4708, 0x07B8, 0x0848, 0 }, // 6.00-6.10
|
||||
{ 0x155C, 0x130, 0, 0x3550, 0x3570, 0x3578, 0x0500, 0x058C, 0x0144 }, // 5.55
|
||||
};
|
||||
static int g_popsManLayout = 0;
|
||||
|
||||
@@ -153,7 +157,7 @@ static u32 PopsManBase() {
|
||||
}
|
||||
|
||||
// Picks the layout of the loaded manager, by the code at its places. False if none matches.
|
||||
static bool FindPopsManLayout() {
|
||||
static bool FindPopsManLayout(bool converted) {
|
||||
const u32 loadModule = KernelFindModuleExport("scePopsMan", SCE_POPSMAN_LOAD_MODULE_NID);
|
||||
if (!loadModule) {
|
||||
return false;
|
||||
@@ -168,13 +172,18 @@ static bool FindPopsManLayout() {
|
||||
{ layout.loadModule, 0x27BD0000 | ((0x10000 - layout.loadModuleFrame) & 0xFFFF) },
|
||||
{ layout.checkMac, 0x27BDFFC0 }, // addiu sp, sp, -0x40
|
||||
{ layout.checkMacExport, 0x27BDFFB0 },
|
||||
{ layout.keyProbe, 0x27BDFEF0 },
|
||||
{ layout.keyProbe + 4, 0x2CA70001 },
|
||||
};
|
||||
bool match = true;
|
||||
for (const auto &c : code) {
|
||||
match = match && Memory::ReadUnchecked_U32(base + c.offset) == c.original;
|
||||
}
|
||||
if (layout.keyProbe != 0) {
|
||||
match = match && Memory::ReadUnchecked_U32(base + layout.keyProbe) == 0x27BDFEF0;
|
||||
match = match && Memory::ReadUnchecked_U32(base + layout.keyProbe + 4) == 0x2CA70001;
|
||||
} else {
|
||||
// Without the probe, only converted PBPs: a store game's key would come from the kernel.
|
||||
match = match && converted && (Memory::ReadUnchecked_U32(base + layout.licenseCall) >> 26) == 3;
|
||||
}
|
||||
if (match) {
|
||||
g_popsManLayout = i;
|
||||
return true;
|
||||
@@ -367,28 +376,31 @@ void __PopsBootPatchEmulator(u32 textAddr, u32 textSize, u32 decodeStub) {
|
||||
ERROR_LOG(Log::sceKernel, "POPS: the emulator doesn't import scePopsMan_0090B2C8, the disc won't read");
|
||||
return;
|
||||
}
|
||||
// A converted PBP's disc blocks are raw DEFLATE. As CFW's popcorn does, send the decoder calls
|
||||
// (the jal two words after each "lw a2, 0xC(s3)") to the scePopsMan_0090B2C8 import, which
|
||||
// inflates.
|
||||
// A converted PBP's disc blocks are raw DEFLATE. As CFW's popcorn does, send the disc read's
|
||||
// decoder call to the scePopsMan_0090B2C8 import, which inflates. That's the jal next to
|
||||
// "li a0, 0x9300" (a block of 16 raw sectors), whichever side 5.x and 6.x put it on.
|
||||
int sites = 0;
|
||||
for (u32 addr = textAddr; addr + 8 < textAddr + textSize; addr += 4) {
|
||||
int trackBranches = 0;
|
||||
for (u32 addr = textAddr + 4; addr + 8 < textAddr + textSize; addr += 4) {
|
||||
const u32 op = Memory::ReadUnchecked_U32(addr);
|
||||
const u32 next = Memory::ReadUnchecked_U32(addr + 4);
|
||||
if (op == 0x8E66000C) {
|
||||
Memory::WriteUnchecked_U32(MIPS_MAKE_JAL(decodeStub), addr + 8);
|
||||
const u32 prev = Memory::ReadUnchecked_U32(addr - 4);
|
||||
if ((op >> 26) == 3 && (next == 0x34049300 || prev == 0x34049300)) {
|
||||
Memory::WriteUnchecked_U32(MIPS_MAKE_JAL(decodeStub), addr);
|
||||
sites++;
|
||||
} else if ((op == 0x14C00014 && next == 0x24E2FFFF) || (op == 0x14A00014 && next == 0x24C2FFFF)) {
|
||||
// Also popcorn's: the track index's length doesn't count the CD audio tracks a converted
|
||||
// image adds, so always take the full-length branch (b +0x14; addiu v0, aN, 0).
|
||||
Memory::WriteUnchecked_U32(0x10000014, addr);
|
||||
Memory::WriteUnchecked_U32(next & 0xFFFF0000, addr + 4);
|
||||
trackBranches++;
|
||||
}
|
||||
}
|
||||
Memory::WriteUnchecked_U32(MIPS_MAKE_JR_RA(), decodeStub);
|
||||
PatchCall(decodeStub + 4, "__PopsDeflateDecompress");
|
||||
currentMIPS->InvalidateICacheRangeDeferred(textAddr, textSize);
|
||||
currentMIPS->InvalidateICacheRangeDeferred(decodeStub, 8);
|
||||
INFO_LOG(Log::sceKernel, "POPS: converted PBP, %d disc decoder calls now inflate", sites);
|
||||
INFO_LOG(Log::sceKernel, "POPS: converted PBP, %d disc decoder calls now inflate, %d track index branches taken", sites, trackBranches);
|
||||
}
|
||||
|
||||
// After the game path, a word for the version key function the manager registers.
|
||||
@@ -422,9 +434,9 @@ static bool IsUsableIcon0(FileLoader *loader) {
|
||||
}
|
||||
|
||||
bool __PopsBootStartInit(FileLoader *pbp, const std::string &gamePath, std::string *error) {
|
||||
if (!FindPopsManLayout()) {
|
||||
if (!FindPopsManLayout(IsConvertedPBP(pbp))) {
|
||||
auto er = GetI18NCategory(I18NCat::ERRORS);
|
||||
*error = er->T("PS1FirmwareVersion", "So far, PS1 games only run with firmware 6.00 to 6.61, and a different version is installed. Install one of those from an official PSP update file in Settings > Tools > PSP Firmware.");
|
||||
*error = er->T("PS1FirmwareVersion", "So far, PS1 games only run with firmware 6.00 to 6.61 (converted ones also with 5.55), and a different version is installed. Install one of those from an official PSP update file in Settings > Tools > PSP Firmware.");
|
||||
return false;
|
||||
}
|
||||
const u32 base = PopsManBase();
|
||||
@@ -432,11 +444,18 @@ bool __PopsBootStartInit(FileLoader *pbp, const std::string &gamePath, std::stri
|
||||
g_popsConvertedPBP = IsConvertedPBP(pbp);
|
||||
g_popsIcon0Usable = IsUsableIcon0(pbp);
|
||||
const u32 loadModule = base + PopsMan().loadModule;
|
||||
PatchReturn(base + PopsMan().keyProbe, MIPS_MAKE_SYSCALL("PopsBootCalls", "__PopsGetVersionKey"));
|
||||
if (PopsMan().keyProbe != 0) {
|
||||
PatchReturn(base + PopsMan().keyProbe, MIPS_MAKE_SYSCALL("PopsBootCalls", "__PopsGetVersionKey"));
|
||||
}
|
||||
if (g_popsConvertedPBP) {
|
||||
// A converted PBP has no MACs. As popcorn does, take its data as authentic.
|
||||
PatchReturn(base + PopsMan().checkMac, 0x24020001); // li v0, 1
|
||||
PatchReturn(base + PopsMan().checkMacExport, 0x24020001);
|
||||
if (PopsMan().licenseCall != 0) {
|
||||
// It has no license, so the lookup can only fail.
|
||||
Memory::WriteUnchecked_U32(0x24020000, base + PopsMan().licenseCall); // li v0, 0
|
||||
currentMIPS->InvalidateICacheRangeDeferred(base + PopsMan().licenseCall, 4);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 pathSize = (u32)gamePath.size() + 1;
|
||||
|
||||
Reference in new issue
Block a user