From f61a77a31fa3ea9690edf997df570c4aad287d9a Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Mon, 7 Sep 2026 17:44:54 -0400 Subject: [PATCH] ps1: bundle plain-ELF POPS loader + KEYS.BIN (iPoPS, MIT) Fix 80020148 'game could not be started' on ARK-5: the base PBP's encrypted ~PSP loader fails to link on modern CFW. Use the bundled plain-ELF loader (data/loader/DATA.PSP) as the DATA.PSP section instead, and write the 16-byte NPDRM version key (data/loader/KEYS.BIN) alongside the EBOOT so CFW can decrypt the STARTDAT PGD. Both assets are from iPoPS (MIT, attributed). - load_data_psp: prefer the bundled ELF, fall back to the base ~PSP loader. - load_keys_bin: return the bundled 16-byte key when present. - convert: write KEYS.BIN next to the output EBOOT. - README + data/loader/ATTRIBUTION.md: document the loader and its license. --- README.md | 27 +++++++++++--------- app/ps1_convert.py | 50 +++++++++++++++++++++++++++++++------ data/loader/ATTRIBUTION.md | 30 ++++++++++++++++++++++ data/loader/DATA.PSP | Bin 0 -> 19002 bytes data/loader/KEYS.BIN | 1 + 5 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 data/loader/ATTRIBUTION.md create mode 100644 data/loader/DATA.PSP create mode 100644 data/loader/KEYS.BIN diff --git a/README.md b/README.md index 2642c53..b0525b5 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,10 @@ Discs are matched by hash first (most precise), then serial, then filename. - `data/covers/` — checked-in assets: the UMD video/game cover templates, `libretro-index.json` + `openvgdb-index.json` catalogs, `libretro-covers.zip` + `openvgdb-covers.zip` offline cover bundles, and the legacy cover cache. -- `data/base.PBP` — a PSN PS1 classic EBOOT (any game) used as the encrypted - loader when converting PS1 discs to `EBOOT.PBP` (git-ignored, copyrighted). - Prefer a 6.60/6.61-era classic; 3.xx-era loaders can fail on modern CFW. +- `data/base.PBP` — a PSN PS1 classic EBOOT (any game) supplying `STARTDAT` + + a base `PARAM.SFO`/icon for PS1 conversion (git-ignored, copyrighted). +- `data/loader/DATA.PSP` + `data/loader/KEYS.BIN` — bundled plain-ELF POPS + loader and NPDRM version key (from iPoPS, MIT). - State dir (`~/.local/state/psp-preserve/` by default, or `$XDG_STATE_HOME/psp-preserve`): - `library.sqlite` — the catalog + library database. - `covers/` — downloaded cover cache. @@ -189,16 +190,18 @@ PS1 discs (pending) and converted EBOOTs also show up in the **Library** tab - **Serial detection** — reads `SYSTEM.CNF` to derive the game ID (e.g. `SLUS-00001`) and writes it into `PARAM.SFO` + the PSAR header. - **base.pbp** — conversion needs a PSN PS1 classic `EBOOT.PBP` (any game) to - supply Sony's encrypted `DATA.PSP` loader (POPS emulator) and `STARTDAT`. + supply the `STARTDAT` block (boot logo + PGD) and a base `PARAM.SFO`/icon. Copy one to `data/base.PBP` and point **Settings → General → PS1 base.pbp** - at it. **Prefer a 6.60/6.61-era classic**: very old 3.xx-era classics ship a - POPS loader that fails to link on modern CFW (`80020148` "game could not be - started"). Any PS1 classic works structurally — the converter finds - `STARTDAT` at `PSAR + p1` regardless of PSAR alignment. -- **Output** — a folder `//` containing a single `EBOOT.PBP` - (the popstation way: raw-DEFLATE-compressed PSAR, no `KEYS.BIN`/`DOCUMENT.DAT`), - ready to copy to `/PSP/GAME//`. Compatible with CFW POPS - (ARK-4/ARK-5). + at it. Any PS1 classic works structurally — the converter finds `STARTDAT` + at `PSAR + p1` regardless of PSAR alignment. +- **POPS loader** — the `DATA.PSP` section uses a bundled plain-ELF loader + (`data/loader/DATA.PSP`, from iPoPS, MIT) so the EBOOT boots on modern CFW + without the base PBP's encrypted loader failing to link (`80020148`). The + base PBP's own loader is only used as a fallback when the bundle is missing. +- **Output** — a folder `//` containing `EBOOT.PBP` (raw-DEFLATE + compressed PSAR) plus a `KEYS.BIN` (NPDRM version key used to decrypt the + `STARTDAT` PGD), ready to copy to `/PSP/GAME//`. Compatible + with CFW POPS (ARK-4/ARK-5). Configure the folders in **Settings → General** (or `PSP_PS1_ROOTS` / `PSP_PS1_OUTPUT_DIR`). Current limits: single-disc games only (no multi-disc diff --git a/app/ps1_convert.py b/app/ps1_convert.py index fe05fb4..a23b18a 100644 --- a/app/ps1_convert.py +++ b/app/ps1_convert.py @@ -15,11 +15,13 @@ Format reference (offsets are byte-exact): b"PSISOIMG0000", game id at +0x400, TOC at +0x800, ISO block index at +0x4000, disc data at +0x100000. -A ``base.pbp`` (any PSN PS1 classic ``EBOOT.PBP``) is REQUIRED to supply two -blobs that we cannot synthesise: +A ``base.pbp`` (any PSN PS1 classic ``EBOOT.PBP``) supplies the blobs that we +cannot synthesise: - * DATA.PSP - the encrypted POPS loader (header section 8). * STARTDAT - the trailing encrypted block (boot.png + PGD). + * DATA.PSP - fallback only: the encrypted POPS loader, used when the + bundled plain-ELF loader (``data/loader/DATA.PSP``, from + iPoPS/MIT) is unavailable. Without a base.pbp the resulting EBOOT will not boot on real hardware; the converter refuses to run without one. @@ -82,6 +84,14 @@ INDEX_ENTRY_SIZE = 32 # offset u32 + length u32 + 6 unused u32 SYNC_PATTERN = b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00" +# Bundled plain-ELF POPS loader + NPDRM key (from iPoPS, MIT). Modern CFW +# (ARK/PRO/ME) loads this ELF directly as the DATA.PSP section, sidestepping +# the base PBP's encrypted ~PSP loader which frequently fails to link on +# 6.60/6.61 (error 80020148 "game could not be started"). +_LOADER_DIR = Path(__file__).resolve().parent.parent / "data" / "loader" +BUNDLED_DATA_PSP = _LOADER_DIR / "DATA.PSP" +BUNDLED_KEYS_BIN = _LOADER_DIR / "KEYS.BIN" + # SFO field formats (PSP PARAM.SFO). FMT_U32 = 0x0004 FMT_STRING = 0x0204 @@ -518,6 +528,25 @@ def extract_data_psp(base_pbp: Path) -> bytes: return section[:prx_size] +def load_data_psp(base_pbp: Path) -> bytes: + """Return the POPS loader (DATA.PSP) to embed. + + Prefers the bundled plain-ELF loader (MIT, from iPoPS), which modern CFW + can load without ~PSP decryption. Falls back to the base PBP's encrypted + ~PSP loader when the bundle is absent. + """ + if BUNDLED_DATA_PSP.is_file(): + return BUNDLED_DATA_PSP.read_bytes() + return extract_data_psp(base_pbp) + + +def load_keys_bin() -> Optional[bytes]: + """Return the bundled NPDRM version key (KEYS.BIN), if available.""" + if BUNDLED_KEYS_BIN.is_file(): + return BUNDLED_KEYS_BIN.read_bytes() + return None + + def extract_startdat(base_pbp: Path) -> bytes: """Extract the trailing STARTDAT block (boot.png + PGD) from a base PBP. @@ -865,7 +894,7 @@ def convert( disc = DiscImage(None, source) # Resources required from the base PBP. - data_psp = extract_data_psp(base_pbp) + data_psp = load_data_psp(base_pbp) startdat = extract_startdat(base_pbp) _warn_old_base(base_pbp) @@ -933,6 +962,13 @@ def convert( except OSError: pass raise + + # Write the NPDRM version key next to the EBOOT (the popstation/iPoPS + # convention; ARK and other CFW read it to decrypt the STARTDAT PGD). + keys = load_keys_bin() + if keys is not None: + out_path.with_name("KEYS.BIN").write_bytes(keys) + return out_path @@ -948,9 +984,9 @@ def convert_to_folder( ) -> Path: """Convert a PS1 disc into a game folder ready for /PSP/GAME. - Writes ``//EBOOT.PBP`` — the same single-file output the - popstation tool produces. No KEYS.BIN or DOCUMENT.DAT is written: - popstation doesn't emit them and CFW (ARK-4 POPS) doesn't need them. + Writes ``//EBOOT.PBP`` plus a ``KEYS.BIN`` (the NPDRM + version key modern CFW uses to decrypt the STARTDAT PGD). No + DOCUMENT.DAT is written; CFW (ARK POPS) doesn't need one. """ folder_name = re.sub(r"[^A-Za-z0-9]", "", game_id).upper() or "PS1GAME" folder = Path(out_dir) / folder_name diff --git a/data/loader/ATTRIBUTION.md b/data/loader/ATTRIBUTION.md new file mode 100644 index 0000000..408e986 --- /dev/null +++ b/data/loader/ATTRIBUTION.md @@ -0,0 +1,30 @@ +# Loader assets + +The files in this directory are bundled from [iPoPS](https://github.com/julianxhokaxhiu/iPoPS) +by Julian Xhokaxhiu, licensed under the MIT License: + +- `DATA.PSP` — a plain-ELF PSP POPS loader (relocatable PRX, 19002 bytes). +- `KEYS.BIN` — a 16-byte NPDRM version key used by CFW to decrypt the + `STARTDAT` PGD. + +MIT License + +Copyright (c) 2020 Julian Xhokaxhiu + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/data/loader/DATA.PSP b/data/loader/DATA.PSP new file mode 100644 index 0000000000000000000000000000000000000000..e62969355c3972232a9fbd2eaa2ada8dad2027f1 GIT binary patch literal 19002 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 literal 0 HcmV?d00001 diff --git a/data/loader/KEYS.BIN b/data/loader/KEYS.BIN new file mode 100644 index 0000000..395a034 --- /dev/null +++ b/data/loader/KEYS.BIN @@ -0,0 +1 @@ +MB\Ω:òÂ ûñ ¾" \ No newline at end of file