mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 23:10:33 +02:00
VSH: Patch vsh_module so the XMB can launch unsigned homebrew
Picking homebrew in the XMB gave "The game cannot be started. The data is corrupted." without ever calling LoadExec: vshmain.prx rejects a PARAM.SFO without a valid DISC_ID, and a DATA.PSP that's a plain ELF rather than ~PSP. NOP the three branches that do that, the same patches ProCFW's vshctrl makes and JPCSP applies. They're for 6.61's build, and are only applied if all three instructions are what we expect. With it, Kosmodrones launches from the XMB, through the PSP boot logo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dcb1df6930
commit
24a2f6110d
1 file changed
+34
@@ -1303,6 +1303,38 @@ static void LoadAndStartVshKernelModules() {
|
||||
// filename is only used for dumping/metadata.
|
||||
// prxSeed is the extra key a module that came out of an NPDRM container needs to decrypt - see
|
||||
// NpDrmDeriveModuleKey(). Null for everything else, which is the overwhelming majority.
|
||||
struct ModulePatch {
|
||||
u32 offset; // From the module base.
|
||||
u32 expected;
|
||||
u32 replacement;
|
||||
};
|
||||
|
||||
// Lets the XMB launch unsigned homebrew, which it otherwise refuses with "The game cannot be
|
||||
// started. The data is corrupted." before ever calling LoadExec. These are ProCFW's vshctrl
|
||||
// patches, as JPCSP applies them (Loader.patchModule). In 6.61's vshmain.prx:
|
||||
// - +0x12058 and +0x12060: the bne's that reject a PARAM.SFO without a valid DISC_ID.
|
||||
// - +0x122B0: the beql that rejects the DATA.PSP read from the PBP, a plain ELF rather than ~PSP.
|
||||
static const ModulePatch vshHomebrewPatches661[] = {
|
||||
{ 0x12058, 0x1440003B, 0 },
|
||||
{ 0x12060, 0x14400039, 0 },
|
||||
{ 0x122B0, 0x506000E0, 0 },
|
||||
};
|
||||
|
||||
// Only patches if every expected instruction is there, since the offsets move between firmware builds.
|
||||
static void ApplyVshHomebrewPatches(u32 base) {
|
||||
for (const ModulePatch &patch : vshHomebrewPatches661) {
|
||||
const u32 addr = base + patch.offset;
|
||||
if (!Memory::IsValid4AlignedAddress(addr) || Memory::ReadUnchecked_U32(addr) != patch.expected) {
|
||||
WARN_LOG(Log::sceModule, "vsh_module isn't a build the homebrew patches are known for (+%x), leaving it alone", patch.offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const ModulePatch &patch : vshHomebrewPatches661) {
|
||||
Memory::WriteUnchecked_U32(patch.replacement, base + patch.offset);
|
||||
}
|
||||
INFO_LOG(Log::sceModule, "Patched vsh_module to launch unsigned homebrew");
|
||||
}
|
||||
|
||||
static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAddress, bool fromTop, std::string *error_string, u32 *magic, std::string_view filename, u32 &error, const u8 *prxSeed = nullptr) {
|
||||
// The magic reads below need four bytes, and the ~SCE branch another four after that. Everything
|
||||
// downstream checks its own sizes; this is just so we can look at the magic at all. The PBP path
|
||||
@@ -1710,6 +1742,8 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
|
||||
} else {
|
||||
Memory::WriteUnchecked_U32(0, patchAddr);
|
||||
}
|
||||
|
||||
ApplyVshHomebrewPatches(module->memoryBlockAddr);
|
||||
}
|
||||
|
||||
// Let's also get a truncated version.
|
||||
|
||||
Reference in new issue
Block a user