Repository navigation
feat(psp): a generic runtime that reads app.pocket, and a TypeScript repack into a Memory Stick zip - #561
Merged
Conversation
doodlewind
force-pushed
the
feat/runtime-psp-vita
branch
2 times, most recently
from
October 6, 2026 17:56
58852c0 to
ef0db4a
Compare
…repack into a Memory Stick zip
The PSP runtime (cargo feature `runtime`, `bun tools/runtime.ts psp`) is an
EBOOT with no game in it. At boot, on the module's main thread and before the
first allocation, it reads app.pocket from its own folder into one kernel
block, checks the footer hash, the psp variant and the host ABI, and runs the
game; a missing or refused file shows one line on screen.
`bun tools/repack.ts --target psp` turns that runtime and a game's .pocket
into PSP/GAME/Studio<Name>/{EBOOT.PBP, app.pocket}: PARAM.SFO written again
with the game's TITLE and MEMSIZE = 1, ICON0 144x80 from the identity's icon,
DATA.PSP unchanged, the .pocket thinned to its psp variant. It uses the shared
runtime and .pocket admission, PNG and scale helpers the 3DS repack added, and
adds a deterministic zip writer and a PARAM.SFO writer to tools/repack/shared/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
doodlewind
force-pushed
the
feat/runtime-psp-vita
branch
from
October 6, 2026 18:04
ef0db4a to
30e6e24
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 of per-device runtimes for Pocket Studio (plan approved by Evan 2026-10-07): one prebuilt PocketJS runtime per device, repacked with a game's files and identity, with no native compile per game. This PR is the PSP part and the shared repack pieces; the PS Vita follows in a second PR.
What changes
Runtime (
bun tools/runtime.ts psp→dist/runtime/psp/EBOOT.PBP+runtime.json):tools/psp.ts --runtimebuildshosts/pspwith the new cargo featureruntimeandPOCKETJS_EMBED_APP=0. At boot, on the module's main thread (rust-psp has changed to the EBOOT's folder, argv[0] up to its last/) and before the first allocation,src/package_file.rsreadsapp.pocketinto one kernel block, thenpocketjs_core::package::select_guestchecks the footer hash, thepspvariant and host ABI 1. The arena reserves the rest at its first allocation, as before. A missing or refused file shows one line on the debug screen (No app.pocket in this game's folder (next to EBOOT.PBP).,app.pocket is damaged (its footer hash does not match).,app.pocket has no psp variant.,app.pocket is built for host ABI 2; this runtime is host ABI 1., …) and parks; HOME exits. The embedded build path is unchanged;build.rsrefusesruntimetogether with an embedded app or a launcher registry.ui.debugStats()gains amemoryobject (arena capacity, largest free block at arena reservation, bump, tail,appFilebytes, current largest free block).Repack (
tools/repack/psp.ts,repackPsp(input)), TypeScript only, no file system, no native tool, on the shared admission the 3DS repack (#562) added (readRuntime,admitPocket,checkIdentity: runtime files againstruntime.jsonSHA-256, footer, variant, host ABI, app id = identity id):.zipwithPSP/,PSP/GAME/,PSP/GAME/Studio<Name>/,EBOOT.PBP,app.pocket(folder from pocket-studio's rule:Studio+ the title's ASCII words, then the id's last segment).EBOOT.PBP: PARAM.SFO written again with the runtime's keys, the game'sTITLE(room 128, cut to 127 bytes of UTF-8) andMEMSIZE = 1(52 MB user partition on a PSP-2000/3000 started from the XMB; the runtime holds the whole game file); ICON0 =identity.iconfitted into 144×80 on transparency;DATA.PSPunchanged.app.pocket= the.pocketthinned to itspspvariant (variant hash unchanged, footer recomputed).pspvariant, a host ABI that differs fromruntime.json, a runtime file that differs fromruntime.json, and a plan that asks forio.offload(the runtime has no offload slot).Shared pieces:
tools/repack.tsandtools/runtime.ts(from #562) gain apspline beside3dsandandroid. The zip comes from #563'stools/repack/shared/zip.ts. New:tools/repack/shared/sfo.ts(PSF 1.1 writer/reader with per-key room, used by the Vita PR too);scale.tsgainsfitRgba.tools/pocket-pack.ts buildgains--project-rootso a game outside the PocketJS checkout (a Studio starter) packs.Reproduce
Starter: a copy of pocket-studio's
starters/twenty48(2-variant.pocket, 2,923,336 B; psp variant js 217,804 B + pak 502,912 B).Results
EBOOT.PBPb2d61455ba452ec9…dba2e7db87eb3d5fe; the same bytes at ef0db4a (hardware run) and at 30e6e24 (this head, rebased on #563); two builds byte-identicalc9cdba49…8b743c290); the EBOOT inside is the one run on the PSPtests/repack-psp.test.ts(structure, SFO fields, ICON0 size, thinned package, determinism, 6 refusals, folder naming, title cut),tests/repack-sfo.test.ts; with #562's and #563's repack tests 56 pass. Added totools/test.tsunit stage; the newRuntime repackworkflow runstests/repack-*.test.tsMemory, game loaded from the file vs embedded (arena's largest free block when it reserves,
ui.debugStats().memory.arenaInitFree):ldstart, 55 MB partition (ef0db4a)The arena bump after boot is the same in both (3,680,736 B). The 10 KB are the package's manifest, plan, identity and tables plus block rounding.
What ran where
~/ppsspp-srcbuild, software GE, debugger WebSocket for VRAM reads and buttons): the repacked zip unpacked, twenty48 started with ○ and played (score 4), on both the first and the final build. Fault screens checked for a missing file, a damaged footer, a vita-only package, host ABI 2 and a non-package file.psp:usb+psp:usb:transport:DATA.PSP(byte-identical to the runtime PRX) withapp.pocketbeside it,ldstart host0:/runtime/DATA.PSP: title card, ○ to start, five moves by DevTools replay tape, score 20 (first build) and 16 (final build ef0db4a), screenshots through the mailbox.ms0:/PSP/GAME/StudioTwenty48Runtime/(repacked with title "Twenty48 Runtime" so pocket-studio'sStudioTwenty48stays), read back and compared byte for byte;DATA.PSPcopied beside it,ldstart ms0:/…/DATA.PSPreadapp.pocketfrom the Memory Stick and showed the title card;DATA.PSPremoved afterwards. The final build's EBOOT.PBP + app.pocket remain installed (read back identical).DATA.PSPwithoutapp.pocket(scrshot):No app.pocket in this game's folder (next to EBOOT.PBP).ldstartrefuses a PBP). Evan can start "Twenty48 Runtime" from Game → Memory Stick to check the XMB path, MEMSIZE and ICON0.Evidence (not committed):
.pocket-build/validation/runtime-psp/in the worktree.🤖 Generated with Claude Code