Files
PS5_RetroArch/docs/PLAN.md
T
Mihawk 6b54bbe862 Link the title and register the PS5 video driver
The frontend now builds into a signed title, and the PS5 driver is in
RetroArch's table in the image that ships, not only in the source.

  bash tools/build-title.sh
    -> 225 of 225 sources compiled, archived into build/ra/libretroarch.a
    -> dist/PPSA99169/eboot.bin, 8,026,239 bytes
       container: signed, plaintext; 12 segments; integrity: valid
       digest 71c88975040535a02b462618dd394a7a378034272c9bffa43ce0ca4b717ab9b7
       (identical across two consecutive builds)

  readelf -r build/ra/obj/gfx_video_driver.c.o
    -> .rela.data.video_drivers holds exactly two entries: video_ps5, video_null
  nm build/llvm-pie.elf
    -> video_ps5, video_null, rarch_main and a single main

  bash tools/check-manifest.sh
    -> 7 files match their digests, none extra, eboot.bin magic 4f153d1d, and it
       fails as it should when one byte of the image is changed

  tools/verify.sh
    -> PASS (format unit build integration evidence)

Registration and entry point. The port's changes are applied by
tools/apply-port-patches.py to the configured copy under build/ra-conf and never
to vendor/retroarch; patches/series names both. RetroArch's own main is compiled
out with -DHAVE_MAIN, the flag its desktop build passes, so src/main.cpp supplies
the only one and calls rarch_main.

Five faults fixed on the way, each recorded in docs/PHASE_LOG.md: the build read
sources from vendor/ and headers from build/ra-conf, so the driver table in the
image had no ps5 entry; 36 hand-written -DHAVE_* flags included six features
configure had turned off; config.h alone is not enough because a source can test a
feature before it is reached (libchdr without zlib); five Linux-only subsystems
were in the object list and are now off at their own configure switches; and two
link-time stubs the pipeline refuses needed -DNDEBUG and -DZSTD_TRACE=0.

The abandoned route is removed: deploy.py, console-launch.sh, check-payload.sh,
scaffold-native.sh, build-native-app.sh, install-title.sh and deploy.sh. Every
tool named by another tool now exists - tools/fetch-retroarch.sh was named by
three tools and three documents and had never been committed, and it now pins
RetroArch at v1.22.2 (69a4f0e) and drops the history so upstream cannot be
committed into. The gate scripts that had never been written are written:
lint-shell, lint-format, check-manifest. The template's unit test, which built a
test for a file that does not exist here, is replaced by tests/test_frontend.py.

The title has not run yet. It is built, signed and manifested; the folder has to
reach /data/homebrew/PPSA99169, and writes from this machine do not take on that
console.
2026-09-18 14:37:57 -04:00

6.0 KiB

PS5 RetroArch: the plan

A native RetroArch homebrew application for jailbroken PlayStation 5 consoles. This file is the top-level plan: the gates that decide whether a milestone is done, the milestone map, the invariants that constrain the code, and the index of the documents that hold the detail. It is read every session, so it stays short and never records progress — the state is in docs/ACTIVE.md and the runs are in docs/PHASE_LOG.md.

The gates

A milestone is done when all four hold for the step that closes it. They are ordered: a later gate never excuses an earlier one.

  1. It runs. tools/verify.sh is green on this host, in order: the sources are formatted and linted, the host unit tests pass, and the PS5 target builds from a clean tree.
  2. It is correct. The cross-built artifact is structurally what the console's loader and RetroArch's own dynamic loader require: PS5 objects that import libkernel_web.sprx and never libkernel_sys.sprx, libretro cores that export retro_api_version, retro_init, retro_run, retro_load_game and retro_get_system_info in the dynamic symbol table, and no host Linux object staged by mistake.
  3. It works on the target. The staged tree is uploaded to the console, launched from the console's own homebrew launcher, and the run reaches the step's definition of working — the menu drawn, the core loaded, the frame presented — with the console's klog captured for the whole run.
  4. It is provable. The console capture is distilled into a committed artifact under evidence/<step>/, and tools/verify.sh evidence replays it against the committed expectation on this host and exits non-zero on a difference. A claim with no artifact is not a pass.

Milestone map

Milestone What it delivers Detail
M0 The contract: filled documentation, the pinned environment, a green gate runner, and the bootstrapper that fetches and cross-builds the pinned upstream RetroArch docs/PHASE_LOG.md
M1 The console shell: a RetroArch payload ELF that loads on the console, takes over the screen, shows its identity and exits cleanly docs/REFERENCE.md
M2 Video and input: RetroArch draws through our own platform context driver onto the graphics backend, and the controller reaches its input driver docs/REFERENCE.md
M3 Cores and storage: core scan and load from the application's own directories, content loading, saves, save states and configuration persistence docs/REFERENCE.md
M4 Audio, mapping and release: audio output, input mapping, menus usable at 1080p, and a release ZIP with an evidence-backed acceptance run docs/REFERENCE.md

Each milestone is a sequence of steps, one commit each, in docs/REFERENCE.md. A step that turns out to be two steps is split before it is started, not after it is half-built.

Invariants

These constrain every change. Breaking one is a design decision, and a design decision is written down in docs/REFERENCE.md and docs/FINDINGS.md before the code that depends on it.

  • Upstream stays upstream. RetroArch is fetched at a pinned version and digest into vendor/, which is never committed and never hand-edited; every change to it is a file in patches/, ordered in patches/series, and regenerated by tools/fetch-retroarch.sh. A fix that cannot be expressed as a patch belongs in platform/, not in vendor/.
  • The toolchain is named everywhere. Every compilation of PS5 code goes through $PS5_PAYLOAD_SDK/bin/prospero-* — in the environment and as make variables — so a makefile that hardcodes CC = gcc cannot silently produce a host Linux object that stages cleanly and loads nowhere.
  • The loader contract is checked, not assumed. Every shipped ELF and core is verified after the build: it imports libkernel_web.sprx, it does not import libkernel_sys.sprx, and its exported libretro entry points are present in the dynamic symbol table. Linking is not loading.
  • No direct hardware access. RetroArch code talks to the console through the public payload SDK, and to the GPU only through a released graphics backend: the OpenGL 3.3 Core stack in ../ps5-opengl-sdk-0.2.0 until the Vulkan driver in ../PS5_Vulkan reaches its 1.0 rung, then that driver. No AGC register writes, no sceGnm* command submission, no firmware offsets, and no private GPU structures in this repository.

Where the truth lives

Question Document
What is being built, and why this file
What is happening right now docs/ACTIVE.md
What a step is, and how it is accepted docs/REFERENCE.md
What was measured, and what it forces docs/FINDINGS.md
What happened, run by run docs/PHASE_LOG.md
How to verify, ship and debug docs/TESTING.md, docs/DEPLOYMENT.md, docs/TROUBLESHOOTING.md

If a question has no home, add it to the table and create the document; do not answer it in two places.

What is deliberately not planned

  • The exploit, the kernel payload and the console's configuration. This project builds an application; it does not jailbreak a console, and it never writes to a console it was not pointed at.
  • The GPU driver. Graphics on this console are separate projects in ../PS5_Vulkan and ../ps5-opengl-sdk-0.2.0; this project consumes their released artifacts and reports what RetroArch needs from them.
  • The homebrew launcher, the payload loader and the title registration flow. The console's own loader starts this application; this repository does not install or configure a console.
  • Retail-style packaging: signing keys, sce_sys beyond a homebrew title's own metadata, and store submission.
  • A software-rendered fallback as the product. The headless and null drivers exist to prove the frontend underneath the graphics work; the shipped application renders through a graphics backend named in docs/REFERENCE.md.

Scope grows by changing this file, in its own commit, with the reason in the message. It never grows by a step quietly doing more than its milestone says.