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 0000000..e629693 Binary files /dev/null and b/data/loader/DATA.PSP differ 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