Load game patches installed on a PSP, given its Fuse ID

- Fixes #12343

Killzone: Liberation's Chapter 5 DLC is an installer that the PSP turns
into ms0:/PSP/GAME/<id>/KZL.PRX, which the game loads with
sceKernelLoadModuleDNAS. That file is a PGD around a type 3 PRX (decrypt
mode 18), both keyed to the console that installed it. Its Fuse ID
gives everything needed:

- libkirk: the per-console key derivation, and KIRK commands 3, 5 and 8
  built on it (KIRK ROM per uofw, key mesh per psdevwiki).
- PrxDecrypter: type 3, which is type 5 with the first step through
  KIRK command 3 (JPCSP's PRX.java), and the DNAS PGD unwrap.
- sceKernelLoadModuleDNAS uses the new FuseID setting (or --fuse-id).
  Without one, or with the wrong one, the game falls back to its stock
  module and an OSD message says why.
- Headless --re-decrypt takes --re-decrypt-key and --fuse-id, to decrypt
  such a file offline.

Verified with a patch installed on a real PSP: the v1.20 module loads,
starts and reads CHAPTER5.BIN. Type 5 output is unchanged (an NPJH50521
PBOOT.PBP decrypts byte-identically). How to install one over PSPLink is
in docs/dnas-patches.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-14 18:10:08 +02:00
1 parent 121b67604c
commit edef36d97e
14 files changed
+459 -81

No files matched your search

+6
View File
@@ -232,6 +232,8 @@ static const CommandLineParam g_autoParams[] = {
{POFF(reRawBase), CmdParamType::String, "re-raw-base", '\0', "Treat --re-module as a raw code image loaded at this address, e.g. 0x08300000", CmdLineMode::Headless},
{POFF(reDecrypt), CmdParamType::String, "re-decrypt", '\0', "Decrypt one encrypted PSP file (PRX or ME image) and exit", CmdLineMode::Headless},
{POFF(reDecryptOut), CmdParamType::String, "re-decrypt-out", '\0', "Output file for --re-decrypt (default: decrypted.bin)", CmdLineMode::Headless},
{POFF(reDecryptKey), CmdParamType::String, "re-decrypt-key", '\0', "Secure install ID (32 hex digits) for --re-decrypt of a game patch installed on a PSP; also needs --fuse-id", CmdLineMode::Headless},
{POFF(fuseId), CmdParamType::String, "fuse-id", '\0', "Fuse ID (12 hex digits) of the PSP a game patch was installed on"},
{POFF(dumpFile), CmdParamType::String, "dump-file", '\0', "Copy one file out of the disc image, e.g. disc0:/PSP_GAME/USRDIR/MOVIE.PMF, or list a directory, and exit", CmdLineMode::Headless},
{POFF(dumpFileOut), CmdParamType::String, "dump-file-out", '\0', "Output file for --dump-file (default: the file's own name)", CmdLineMode::Headless},
{POFF(odsLog), CmdParamType::Bool, "odslog", 'o', "Also log through OutputDebugString (Windows)", CmdLineMode::Headless},
@@ -532,6 +534,10 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C
}
void CommandLineOptions::ApplyToConfig() const {
if (fuseId.has_value()) {
g_Config.sFuseID = fuseId.value();
g_Config.DoNotSaveSetting(&g_Config.sFuseID);
}
if (fullscreen.has_value()) {
g_Config.bFullScreen = fullscreen.value();
g_Config.DoNotSaveSetting(&g_Config.bFullScreen);