PlayStation 5 decomp guide
The game's own PC-type code runs as is. A shared tool rebuilds the console around it.
What this system is like
The PlayStation 5 uses the same kind of chip as your PC. It is an x86-64 chip. So the game's code can run on your PC with no translation. A tool called AnyPS5 makes this work. Its relinker rewrites the game file into a Windows program. A relinker is a tool that changes how a program is packed, not what it does. AnyPS5 also brings new copies of the console's system parts, such as files, pads, sound and saves.
Graphics are the big job. The PS5 graphics chip gets its own kind of commands and shaders. A shader is a small program that runs on the graphics chip. AnyPS5 turns both into Vulkan, a PC graphics system. One project swapped in the graphics part of KytyPS5 instead, because it ran faster for that game.
This method is very new. AnyPS5 started in August 2026. By mid-November, only a handful of games had their own port repos. RecompLauncher lists one PS5 game: Dead Cells. Most of the work happens inside the shared tool, not in each game's repo.
A full decomp of a PS5 game has never been done. Nobody has the exact Sony compiler, so byte matching is not possible. Path B shows the only real route. You swap the game's code for your own C, one function at a time, while the game keeps running. It is a very long job.
No CPU translation at all
The PS5 chip and your PC chip speak the same machine code. So the game's code runs as it is. The relinker only repacks the file for Windows. Your work is the system parts and the graphics.
One shared tool does most of the work
Every PC port here is built on AnyPS5. A game's own repo is small: a launcher, patches and notes. Fixes for missing system functions go into AnyPS5 itself. So you often work in someone else's code.
The work list comes from the import audit
An import is a system function the game asks for by NID. tools/import_audit.py lists each import as done, partly done, stub or absent. The absent and stub rows are your to-do list.
Missing parts must fail loudly
AnyPS5 has a strict rule. Each function does its real job or throws an error. A silent fake success is not allowed. This makes crashes point straight at the missing piece.
Very new and moving fast
The tools appeared between July and October 2026. AnyPS5 had about 40,000 commits by mid-November. Pin the exact commit you build on. Read the change notes before you update.
You need your own decrypted dump
No tool here decrypts game files or holds keys. You must dump your own copy. Every project keeps game files, shaders, saves and frames out of its repo.
Two paths
Get it running on PC
You unwrap your dump's eboot.bin into a plain ELF. The AnyPS5 relinker turns it into a Windows .exe. The game then loads AnyPS5's new system libraries as DLL files. You list every import the game needs and fill in the missing ones. Last, you fix crashes and graphics bugs one at a time.
Full decomp for porting
No one has decompiled a PS5 game. Byte matching is not possible, because Sony's exact compiler is not public. The real route is the one OpenRCT2 used on PC. You keep the original game running inside your Path A port. You replace one function at a time with your own C. A test runs the same scene with and without your function and compares the results.
Path A: Get it running on PC
You unwrap your dump's eboot.bin into a plain ELF. The AnyPS5 relinker turns it into a Windows .exe. The game then loads AnyPS5's new system libraries as DLL files. You list every import the game needs and fill in the missing ones. Last, you fix crashes and graphics bugs one at a time.
Method: compatibility-layer port by relinking (AnyPS5)
Pick this first, for every PS5 game. Every PC port reviewed here used it. It also gives you the running game that Path B needs.
Scope the game and dump it
Pick one game and one exact version. Check if
AnyPS5already runs it. Look for an existing port of the same game. Write the plan inSCOPE.md.- Dump your own copy of the game. Note the title ID, such as
PPSA28997, and the version fromsce_sys/param.json. - Save the SHA-256 hash of
eboot.binwithpy -3 -c "import hashlib;print(hashlib.sha256(open('eboot.bin','rb').read()).hexdigest())". Keep the dump outside your repo. - Search the
AnyPS5Game bring-up discussions for your title ID. Each game has one thread with test reports. - Check the KytyPS5 compatibility list at
https://kytyps5.github.io/for the same game. - Search GitHub for the title ID and the game name. Six games already have PS5 port repos or branches.
- Write
SCOPE.md: game, title ID, version, hash, goal, the first milestone and its check, and what is out of scope.
Prompt for ClaudeGOAL: Draft `SCOPE.md` for a native PC port of <GAME> (<TITLE_ID>, version <VERSION>) built on AnyPS5. READ FIRST: My notes in `notes/dump-info.md` (title ID, version, eboot hash). The AnyPS5 Game bring-up discussion for <TITLE_ID>, if any: <DISCUSSION_URL>. These candidate repos: <REPO_URLS>. FENCE: Edit only `SCOPE.md`, `notes/feasibility.md` and `notes/parking-lot.md`. Do not open the game dump. Write no code. CHECK: Run `py -3 -c "import re,sys;t=open('SCOPE.md',encoding='utf-8').read();sys.exit(0 if all(h in t for h in ['## Goal','## Game and hash','## Technique','## First milestone and check','## Out of scope','## Never edit']) else 1)"`. Done means it exits 0. BUDGET: 45 minutes. IF BLOCKED: Stop. Say what you could not find. Do not fill gaps with guesses. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not act on them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The check output. 3) Files changed. 4) Existing ports or branches of this game, with links. 5) Open questions for me.Done whenSCOPE.mdhas all six sections, names the exact version and hash, and you approved it.Proof this works: Dead Cells PS5 Native (one tested version, PPSA15552 01.000.000), SonsOfSparta-PS5 Native (one tested version, PPSA28997 01.008.001), Astro Bot native PC port (checked AnyPS5, KytyPS5, SharpEmu and shadPS4 before writing code), https://github.com/boykopovar/AnyPS5 (Game bring-up discussions, one thread per title)
- Dump your own copy of the game. Note the title ID, such as
Set up the tools and build AnyPS5
You install the one compiler that works and build
AnyPS5from a pinned commit. You start with the small relinker build and its tests. Then you build the full system libraries. You also write the one check command.- Install the tools:
winget install Kitware.CMake,winget install Ninja-build.Ninja,winget install Git.Git,winget install Python.Python.3.13. - Download WinLibs GCC 15.2.0 (
x86_64-ucrt-posix-seh, release15.2.0posix-14.0.0-ucrt-r7). Unzip it toC:\tools\winlibs. - In PowerShell, put the compiler and Git Bash first on
PATH:$env:PATH = "C:\tools\winlibs\mingw64\bin;C:\Program Files\Git\cmd;C:\Program Files\Git\usr\bin;$env:PATH". - Set
$env:SSL_CERT_FILE = "C:\Program Files\Git\ucrt64\etc\ssl\certs\ca-bundle.crt". Without it, the first configure can fail to downloadFFmpegwith status 60. - Clone to a short path:
git clone --recursive https://github.com/boykopovar/AnyPS5.git C:\a5. Long paths break the build. Keep your own repo, with your scripts and notes, in a separate folder. - In
C:\a5, build the relinker alone:cmake -S . -B build-relinker -G Ninja -DCMAKE_BUILD_TYPE=Release -DANYPS5_RELINKER_ONLY=ON -DBUILD_TESTING=ON, thencmake --build build-relinker --parallel. - Run its tests:
ctest --test-dir build-relinker --output-on-failure --no-tests=error.ctest --test-dir build-relinker -Nmust listinput_magic. If not, CMake did not find Python. - Build everything:
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DBUILD_TESTING=ON, thencmake --build build --parallelandcmake --build build --target libs --parallel. This makesbuild\core\relinker\relinker.exeand the system libraries. - Copy
libgcc_s_seh-1.dll,libstdc++-6.dllandlibwinpthread-1.dllfrom the WinLibsbinfolder intoC:\a5\build\core\libs\libs. - In your repo, write
tools/check.py. It rebuildslibs, runsctest --test-dir C:\a5\build --no-tests=error -R <TEST_PATTERN>, and runs the import audit once step 3 has made registry files. WriteCLAUDE.mdanddocs/PROGRESS.md.
Prompt for ClaudeGOAL: Make `py -3 tools/check.py` build AnyPS5's relinker and libs at commit <ANYPS5_COMMIT> and run its tests, with one exit code. READ FIRST: `SCOPE.md`, AnyPS5 `docs/dev/BUILD.md` (sections Relinker only, Requirements, Commands, Windows setup and troubleshooting), AnyPS5 `AGENTS.md`. AnyPS5 is cloned at `C:\a5`. FENCE: Edit only `tools/check.py`, `CLAUDE.md`, `docs/PROGRESS.md` and `notes/parking-lot.md`. Do not edit AnyPS5 source. Do not install anything; tell me what is missing. CHECK: Run `py -3 tools/check.py`. Done means exit code 0, the output shows the ctest test count (run with `--no-tests=error`), and `C:\a5\build\core\relinker\relinker.exe` and the `.prx` files in `C:\a5\build\core\libs\libs` exist. BUDGET: Stop after 3 failed builds with the same error, or 60 minutes. IF BLOCKED: Stop. Paste the first error line and the compiler path from `where g++`. Do not switch compilers on your own. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The last 20 lines of the check output. 3) Files changed. 4) Tool versions found. 5) New parking-lot items.
Done whenpy -3 tools/check.pyexits 0 on a clean clone, and the test count it prints is above zero.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/docs/dev/BUILD.md (WinLibs 15.2.0, relinker-only build, runtime DLLs), Dead Cells PS5 Native (only MinGW-w64 GCC 15.2.0 works for libc.prx), Astro Bot native PC port (full AnyPS5 build about 1 hour, pinned at revision 2199ee0), https://github.com/boykopovar/AnyPS5/blob/main/docs/dev/BUILD.md (Requirements: Python 3, Git Bash, SSL_CERT_FILE for the FFmpeg download)
- Install the tools:
Unwrap and relink the eboot
You turn your dump into a plain ELF and relink it into a Windows program. A dry run first shows what will be written. You also ask the relinker for registry files, which list every import.
relinkerbelow meansC:\a5\build\core\relinker\relinker.exe.- Unwrap the main file:
py -3 C:\a5\tools\unwrap_self.py <DUMP>\eboot.bin <WORK>\source\eboot.elf. It refuses files that still look encrypted. - Unwrap each SELF module in the dump's
sce_modulefolder the same way, into<WORK>\source\sce_module\. The relinker skips SELF modules with a warning. - If you need game patches, put them in
<WORK>\source\relinker-patches.tomlbefore you relink. The relinker loads that file from the input folder. Each patch lists theexpectedbytes, so a wrong game version fails safely. - Do a dry run:
relinker --dry-run --windows --registry <WORK>\source\eboot.elf <WORK>\out\game.exe. It writes nothing. On an Intel CPU, add--to-intelto this and every relink. - Relink for real:
relinker --windows --registry <WORK>\source\eboot.elf <WORK>\out\game.exe. It writesgame.registry.json, one registry per module, and the converted modules as.guest.prxfiles underout\app0\. - Lay out the run folder. Put
game.exeat the top. Put the built.prxfiles and the three DLLs inlibs\. Never put the game's own.prxfiles there. Copy the game's data files, its originaleboot.binandsce_sys\param.jsonintoapp0\.
Prompt for ClaudeGOAL: Produce `<WORK>/out/game.exe` and its registry files from my dump of <GAME>, with a script I can re-run. READ FIRST: `SCOPE.md`, AnyPS5 `docs/user/USAGE.md` (Input and conversion, Options, Runtime layout, Game patches). The relinker is `C:\a5\build\core\relinker\relinker.exe`. My CPU is <AMD_OR_INTEL>; on Intel, relink with `--to-intel`. FENCE: Edit only `tools/prepare.py`, `docs/PROGRESS.md` and `notes/parking-lot.md`. The dump at <DUMP> is read-only. Write outputs only under <WORK>. Never copy game files into the repo. CHECK: Run `py -3 tools/prepare.py --dump <DUMP> --work <WORK>`. Done means it exits 0, `<WORK>/out/game.exe` exists, and `<WORK>/out/game.registry.json` exists. BUDGET: Stop after 3 failed tries, or 45 minutes. IF BLOCKED: Stop. Paste the relinker's error line and exit code. If unwrap says the file looks encrypted, stop and tell me; do not try to decrypt anything. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The script output. 3) Files changed. 4) Modules unwrapped and skipped. 5) New parking-lot items.
Done whentools/prepare.pyexits 0 and makesgame.exeplus its registry files from the dump, with the dump unchanged.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/docs/user/USAGE.md (unwrap_self.py, --dry-run, --registry, runtime layout), Astro Bot native PC port (relinker --windows --registry into AstroBot.exe; SELF segments signed, not encrypted), SonsOfSparta-PS5 Native (launcher prepares the game from the user's files on first start)
Map the imports into a ledger
You list every import the game needs and who answers it. The audit sorts them into five groups. You save the result as your work plan. You also trace which imports the game really calls.
- Run the audit:
py -3 C:\a5\tools\import_audit.py <WORK>\out\game.registry.json <WORK>\out\game.<MODULE>.registry.json --libs C:\a5\build\core\libs\libs --modules <WORK>\source\sce_module --json docs\imports.json. Pass every module registry the relinker wrote. - Read the five groups:
implemented,partial,stub,absentandmodule. Exit code 1 means something blocks loading, such as an absent import. - Look up an unknown NID with
py -3 C:\a5\tools\nid_names.py --nid <NID>. It downloads a public name list,aerolib.csv, to a temp folder once. Pass that file to the audit with--names.ps5rs nid <name>turns a name into its NID. - Relink with
--trace-imports. Run withAPS5_TRACE_CALLS=allandAPS5_TRACE_CALLS_FILE=calls.txtto see which imports the game calls most. - Save a table in
docs/imports.md: library, name, group, and how often it is called.
Prompt for ClaudeGOAL: Produce `docs/imports.md`, a table of every import <GAME> needs, grouped as implemented, partial, stub, absent or module. READ FIRST: `SCOPE.md`, `docs/imports.json` from the audit, `<WORK>/calls.txt` from a traced run, AnyPS5 `docs/user/USAGE.md` section Import audit. FENCE: Edit only `docs/imports.md`, `tools/count_imports.py` and `notes/parking-lot.md`. The dump and AnyPS5 source are read-only. CHECK: Run `py -3 tools/count_imports.py`. Done means the five group totals in `docs/imports.md` match the audit's own summary, and every absent or stub row names its library. BUDGET: 60 minutes. IF BLOCKED: Stop. List the NIDs with no name and where you looked. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The five totals and the check output. 3) Files changed. 4) Absent and stub imports grouped by library, most called first. 5) New parking-lot items.
Done whendocs/imports.mdtotals match the audit's summary, and every absent or stub import names its library.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/docs/user/USAGE.md (Import audit and Tracing system library calls), https://github.com/claimore22/ps5rs (NID search and
ps5rs nid), Mewgenics-android (address and hash checked hooks into app.elf, analysis in docs/app-elf-arm64-analysis.md)- Run the audit:
First light: reach the first frame
You start the game and get real frames on screen. A repeatable run proves it. A window that opens black does not count.
- Start it once by hand with a log. In the run folder, run
$env:ANYPS5_LOG_FILE = "run.log", then.\game.exe. - If the game ends through its own exit,
anyps5-report.jsonappears next togame.exe. Look forfirstDraw,firstPresentandframesPresented. A crashed or killed run writes no report. - If it stops, read the last lines of
run.log. A function that is not built names itself in the error. - Make the run repeatable. In your repo, make
tools\my-titles.jsonwith one entry, such as{"<TITLE_ID>": {"seconds": 60}}. On an Intel CPU, add"relink": ["--to-intel"]to the entry. - Run
py -3 C:\a5\tools\title_run.py <TITLE_ID> --games <DUMP_PARENT> --build C:\a5\build --titles tools\my-titles.json --runs 2. It unwraps and relinks the dump by itself, then runs the game twice. - Read the lines
<TITLE_ID> run1: <outcome>. You wantrendering: frames were shown and the last one is not one flat colour. The script always exits 0, so read the outcome, not the exit code.
Prompt for ClaudeGOAL: Get <GAME> to show its first real frame in the relinked build, proven by the run report. READ FIRST: `SCOPE.md`, `docs/imports.md`, the last `run.log`, AnyPS5 `docs/user/USAGE.md` sections Run report and Saving the log to a file, `C:\a5\tools\title_run.py --help`. FENCE: Edit only the AnyPS5 library files for the one function that stops the boot, plus `docs/PROGRESS.md` and `notes/parking-lot.md`. Never special-case my title ID, a hash or a path. A function you cannot build calls `NotImplemented_nid_no_patch(__func__)`. CHECK: Run `py -3 tools/check.py`, then `py -3 C:\a5\tools\title_run.py <TITLE_ID> --games <DUMP_PARENT> --build C:\a5\build --titles tools\my-titles.json --runs 2`. Done means the check exits 0 and both runs print outcome `rendering`. `title_run.py` always exits 0, so read the outcome lines. BUDGET: One blocking function per session. Stop after 3 failed tries on it. IF BLOCKED: Stop. Paste the last 30 log lines and say which function blocks and what you need. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The title_run outcome lines, and the report fields if a report was written. 3) Files changed. 4) The next blocking function. 5) New parking-lot items.
Done whentitle_run.pyprints outcomerenderingfor both runs.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/tools/title_run.py (outcomes relink-failed, crashed, exited, stalled, frame-unreadable, blank, rendering; it relinks the dump itself), Astro Bot native PC port (run harness saving stdout, stderr and screenshots), Dead Cells PS5 Native (logs folder; game.err shows what failed)
- Start it once by hand with a log. In the run folder, run
Grind: one missing function or crash at a time
You fix the game one blocker at a time. Each unit is one missing system function or one crash. Each fix gets a test in
AnyPS5's test suite. The import ledger and the golden run show your progress.- Before you start a function, run
py -3 C:\a5\tools\pr_overlap.py --names <FUNCTION>. It shows whether an open pull request already covers it. - Pick the next absent or stub import the game really calls, from
docs/imports.md. - Find how nearby functions in
core/libs/prx/<library>/are declared, and copy that pattern. - Build the behaviour from what the game does in your dump and from open code, never from Sony SDK files.
- Rebuild with
cmake --build build --target libs --parallelinC:\a5. The game loads the.prxfiles frombuild\core\libs\libs. - For a crash, read the Windows crash report in the log. Set
APS5_CRASH_STACK_BYTES=4096for more stack. - Write known gaps in
docs/dev/debt/<library>.md, asAnyPS5asks. Before you push a fix upstream, runpy -3 C:\a5\tools\check_conventions.py --base origin/main. Send fixes as small pull requests.
Prompt for ClaudeGOAL: Implement <FUNCTION> in AnyPS5's <LIBRARY> so <GAME> gets past <SCENE>. READ FIRST: `docs/imports.md` row for <FUNCTION>, the crash or stop lines in `run.log`, `core/libs/prx/<LIBRARY>/` neighbours, AnyPS5 `AGENTS.md`. FENCE: Edit only `core/libs/prx/<LIBRARY>/`, its test, `docs/dev/debt/<LIBRARY>.md`, `docs/PROGRESS.md` and `notes/parking-lot.md`. Never special-case a title ID, value or path. Never return a made-up success. Use no Sony SDK material. CHECK: Run `py -3 tools/check.py`, then `py -3 C:\a5\tools\title_run.py <TITLE_ID> --games <DUMP_PARENT> --build C:\a5\build --titles tools\my-titles.json --runs 2`. Done means the check exits 0 with the new test counted, and the run gets past <SCENE> twice. BUDGET: Stop after 3 failed tries or 90 minutes. IF BLOCKED: Stop. Say what the game passes in and what you cannot work out. Do not stub around it. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output and run outcome. 3) Files changed. 4) What the game hits next. 5) New parking-lot items.
Done whenThe check exits 0 with a new test for the function, and two golden runs get past the scene that stopped before.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/AGENTS.md (NotImplemented rule, no per-title values, debt files), https://github.com/ymatrix1/PortPS5 (implement-prx-function skill: copy neighbours, real error codes, a unit test), Astro Bot native PC port (0xC0000409 exit meant AnyPS5 threw; read stderr)
- Before you start a function, run
Fix graphics and speed
Graphics bugs and low frame rates take the longest.
AnyPS5can record a few frames and play them back with no game running. That gives Claude a test it can run alone. Speed claims need measured numbers from the same scene.- Record frames: set
APS5_FRAME_CAPTUREto an empty folder before you start the game. - Build the replay with
cmake --build build --target agc_frame_replay. PutC:\a5\build\core\libs\libs\unpatchedfirst onPATH. Then runagc_frame_replay.exe <capture> --frames <dir>. For a capture with reference images, it prints the share of identical pixels. - For a GPU hang, set
APS5_DUMP_SUBMITSto a folder before you start the game. Buildagc_pm4_replayand runagc_pm4_replay.exe <folder>, with the samePATH. It reports waits that never release. - Turn on validation with
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation,ANYPS5_REQUIRE_VULKAN=1andANYPS5_NO_SHADER_CACHE=1. Use a layer of version 1.4.335 or newer. Look for newVUID-errors, becausectestcan pass while they are logged. - Measure speed with
APS5_TRACE_SUMMARY_EVERY=30andAPS5_TRACE_BENCH=1. Then runpy -3 C:\a5\tools\trace_digest.py aps5-summary.jsonl. Compare the same scene on the old and the new build. For detail, build with-DAPS5_ENABLE_TRACY=ONand connectTracy0.14.1. - If
AnyPS5's renderer is too slow for your game, try theKytyPS5graphics stack as Astro Bot did. Keep it as a patch series.
Prompt for ClaudeGOAL: Fix <VISUAL_BUG> in <SCENE> of <GAME>, proven by a frame replay. READ FIRST: `docs/PROGRESS.md` section Ruled out, my capture at <CAPTURE_DIR>, the reference image <REFERENCE_PNG>, AnyPS5 `docs/dev/BUILD.md` sections Frame capture and replay and PM4 submission replay. FENCE: Edit only the shader recompiler or AGC driver files that the evidence points to, plus their tests, `docs/PROGRESS.md` and `notes/parking-lot.md`. Never skip a draw or dispatch to hide the bug. Never match on a shader hash or title ID. CHECK: Run `py -3 tools/check.py`, then `agc_frame_replay.exe <CAPTURE_DIR> --frames <OUT_DIR>`. Done means the check exits 0, the replay's matching-pixel share rises above <TARGET> and validation logs no new `VUID-` errors. BUDGET: Stop after 3 failed hypotheses. Write each one in Ruled out. IF BLOCKED: Stop. Show the replay numbers before and after each try, and say what you would need next. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Replay output before and after. 3) Files changed. 4) Ruled-out ideas added. 5) New parking-lot items.
Done whenThe frame replay of the bad scene shows the target share of matching pixels, with no new validation errors.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/docs/dev/BUILD.md (frame capture and replay, PM4 replay, Tracy), https://github.com/boykopovar/AnyPS5/blob/main/AGENTS.md (validation layer 1.4.335 or newer; speed claims use trace_digest.py from the same machine and scene), Astro Bot native PC port (AnyPS5 renderer about 9 FPS with black tiles; swapped to kyty_gpu.dll), Ghost of Yotei branch (moves forward only when a build renders the 60-frame title reference exactly)
- Record frames: set
Harden: golden runs, patches and release
You build a set of golden runs so old fixes stay fixed. A golden run plays a set route with recorded pad input and must reach the same point. Then you package a launcher that builds the game from the player's own dump.
- Make one titles file per scene, such as
tools\scenes\boss.json. Each holds your title ID with apadtimeline, in the formatC:\a5\tools\titles.jsonuses. One file can hold only one entry per title ID. - Run each scene after every change:
py -3 C:\a5\tools\title_run.py <TITLE_ID> --games <DUMP_PARENT> --build C:\a5\build --titles tools\scenes\<SCENE>.json --runs 2. - Timelines run on wall-clock time, so a slow run can miss a press. Run a failed scene again before you call it broken.
- Compare with an earlier run: add
--compare <OLD_RUN>\summary.json. Runs are saved underC:\a5\build\title-runs\. - Check every byte patch with
relinker --verify-patches relinker-patches.toml <WORK>\source\eboot.elf. A wrong version must fail, not corrupt. - Ship a launcher that builds the game from the player's own dump, as Dead Cells and SonsOfSparta do. Model your packaging script on Dead Cells'
tools\package_release.ps1. Ship no game files. - Tell players to add an antivirus exclusion only for the build folder. Relinked programs get flagged.
Prompt for ClaudeGOAL: Make `py -3 tools/golden.py` run every golden scene of <GAME> and fail if any outcome is not `rendering`. READ FIRST: `SCOPE.md`, `docs/PROGRESS.md`, my scene list in `docs/golden-scenes.md`, `C:\a5\tools\title_run.py --help`, AnyPS5 `docs/user/INPUT_MAPPING.md` section Controller timelines. FENCE: Edit only `tools/golden.py`, my scene files in `tools/scenes/`, `docs/golden-scenes.md` and `notes/parking-lot.md`. Do not change game code or libraries in this task. Do not loosen a scene's goal to make it pass. CHECK: Run `py -3 tools/golden.py`. Done means it exits 0 on the current build, and exits 1 when I break one scene on purpose (rename one data file in the run folder). BUDGET: 90 minutes. IF BLOCKED: Stop. Say which scene cannot be reached by pad input alone. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Output of the passing run and the broken run. 3) Files changed. 4) Scenes covered. 5) New parking-lot items.
Done whentools/golden.pypasses on the current build and fails when one scene is broken on purpose.Proof this works: https://github.com/boykopovar/AnyPS5/blob/main/tools/titles.json (pad timelines per title), Astro Bot native PC port (A/B benchmarks in fixed scenes, byte patches that switch off on other versions), Dead Cells PS5 Native (package_release.ps1, launcher builds from the user's own files), https://github.com/boykopovar/AnyPS5/blob/main/docs/user/INPUT_MAPPING.md (Controller timelines run on wall-clock time)
- Make one titles file per scene, such as
Path B: Full decomp for porting
No one has decompiled a PS5 game. Byte matching is not possible, because Sony's exact compiler is not public. The real route is the one OpenRCT2 used on PC. You keep the original game running inside your Path A port. You replace one function at a time with your own C. A test runs the same scene with and without your function and compares the results.
Method: non-matching decompilation by function replacement
Pick this only after Path A works, and only if you want the game on a system that is not x86-64. Expect years, not months. To play on a phone or a Mac sooner, look at the box64 and ps5-native-mac routes first; neither is a decomp.
Scope: decide if a decomp is worth it
You measure how big the job is before you start. You count the game's functions and find the engine. If the game uses a known engine, much of the code is not the game's own. Write the numbers in
SCOPE.md.- Run
ps5rs strings <WORK>/source/eboot.elf --detect. It names the engine and middleware it finds. - Run
ps5rs middleware <DUMP_PARENT>to list third-party modules, such asFMODorWwise. You do not rewrite those. - Count functions in
Ghidraafter the next step. Note the count and the total code size inSCOPE.md. - Check for faster ways to reach your target system first:
box64for Android, orps5-native-macfor Apple silicon Macs. - Write the stop rule: what share of functions you must replace before you try a build without the eboot.
Prompt for ClaudeGOAL: Write `notes/decomp-feasibility.md`: function count, code size, engine and middleware, and a yes or no on starting Path B for <GAME>. READ FIRST: `SCOPE.md`, output of `ps5rs strings <WORK>/source/eboot.elf --detect` saved in `notes/detect.txt`, `docs/ledger.csv` if it exists. FENCE: Edit only `notes/decomp-feasibility.md` and `notes/parking-lot.md`. Write no code. Do not open the dump directly. CHECK: Run `py -3 -c "import sys;t=open('notes/decomp-feasibility.md',encoding='utf-8').read();sys.exit(0 if all(h in t for h in ['## Size','## Engine','## Faster routes','## Verdict']) else 1)"`. Done means it exits 0 and Size gives numbers, not words. BUDGET: 45 minutes. IF BLOCKED: Stop. Say which number you could not measure. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The numbers. 3) Files changed. 4) Verdict and the main risk. 5) Questions for me.Done whennotes/decomp-feasibility.mdgives a measured function count, the engine, and a verdict you approved.Proof this works: https://github.com/claimore22/ps5rs (engine and middleware detection), https://github.com/error9098x/ps5-native-mac (Sons of Sparta runs on Apple M5 without a decomp), Mewgenics-android (Android through box64, with some routines made native)
- Run
Load the eboot into Ghidra
You open the unwrapped ELF in
Ghidrawith a PS5 loader. The loader names imports by NID and marks function edges. A good load is the base for every later step.- Install
Ghidra12.1.3 for the PS5 loader plugin. It needs JDK 21:winget install EclipseAdoptium.Temurin.21.JDK. The newestGhidra, 12.2, needs JDK 25 instead. - Clone the plugin. In its folder, set
GHIDRA_INSTALL_DIRingradle.propertiesto your Ghidra 12.1.3 folder. Then run<GHIDRA_DIR>\support\gradle\gradlew.bat buildExtension. The plugin has nogradlew.batof its own. - In Ghidra use File, Install Extensions, and pick the zip. The analyzer is called
ps5-elf-analyzer. - Import
<WORK>\source\eboot.elf(the unwrapped file, not the relinked.exe). Run auto analysis. - Check the import count against the audit's count from Path A step 4.
GhidraMCPneeds Ghidra 12.1.4. To use both, keep two Ghidra folders, or try to build the plugin for 12.1.4. Nobody here has tested that.
Prompt for ClaudeGOAL: Confirm my Ghidra project of <GAME>'s eboot.elf is loaded well enough to build a function ledger. READ FIRST: `notes/ghidra-export.txt` (my export of the function list and import labels), `docs/imports.md` from Path A. FENCE: Edit only `tools/compare_imports.py`, `notes/ghidra-check.md` and `notes/parking-lot.md`. Do not change the Ghidra project. Do not open the dump. CHECK: Run `py -3 tools/compare_imports.py notes/ghidra-export.txt docs/imports.json`. Done means every import in the audit appears as a named label in the export. BUDGET: 30 minutes. IF BLOCKED: Stop. List the imports that are missing in Ghidra and the addresses where they should be. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The compare output. 3) Files changed. 4) Functions found and any odd regions. 5) New parking-lot items.
Done whenEvery import from the audit appears as a named label in Ghidra, and the function count is saved.Proof this works: https://github.com/boazvdwansem/ghidra_ps5_elf_plugin (PS5 ELF analyzer for Ghidra 12.1.3), Astro Bot native PC port (Ghidra 12.1.4, self2elf.py and dyninfo.py helpers), https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.3_build/README.md (Ghidra 12.1.3 needs JDK 21)
- Install
Map the binary into a ledger
You make one file that lists every function with its address, size and status. You mark library and engine code so nobody rewrites it by hand. This file is how you and Claude track the whole job.
- Export functions with the
ghidra-headlessskill or a Ghidra script. Save address, name, size and callers. - Mark code from the C library, the C++ library and the engine as
library. Do not rewrite it. - Use the executable load ranges, not only the
.textsection. Mewgenics found game code outside.text. - Save
docs/ledger.csvwith columns: address, name, size, kind, status, owner, test. - Sort the game's own functions by size and by how many other functions call them.
Prompt for ClaudeGOAL: Build `docs/ledger.csv` with one row per function in <GAME>'s eboot.elf. READ FIRST: `notes/ghidra-functions.csv` (headless export), `notes/detect.txt`, `SCOPE.md`. FENCE: Edit only `docs/ledger.csv`, `tools/ledger.py` and `notes/parking-lot.md`. Do not open the dump. Do not write any C yet. CHECK: Run `py -3 tools/ledger.py --check`. Done means the row count equals the Ghidra function count, every row has a kind (game, library or engine), and sizes add up to the executable code size. BUDGET: 60 minutes. IF BLOCKED: Stop. List the address ranges you could not classify. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output with counts per kind. 3) Files changed. 4) The 20 smallest game leaf functions. 5) New parking-lot items.
Done whentools/ledger.py --checkpasses: one row per function, every row has a kind, sizes add up.Proof this works: Mewgenics-android (use executable PT_LOAD ranges; .text alone misses game code), https://www.skills.sh/trailofbits/skills-curated/ghidra-headless, research/topics/ai-decomp-practice.md C1 (a script picks the task; small units first)
- Export functions with the
Build the swap-and-compare harness
You make a way to run one of your C functions in place of the game's own. The hook checks the original bytes first, so it only fires on the right game version. Then a test runs the same scene twice and compares. No PS5 project has built this harness yet. The hash-guarded hooks of
Mewgenics-androidare the closest.- Add a hook layer in your patch series. Guard each hook by a hash of the function's first 64 bytes, as Mewgenics does.
- Give each hook a switch so you can turn it off for the compare run.
- Drive both runs with the same pad timeline. Set
APS5_PAD_INPUT_FILEand queue the timeline withC:\a5\tools\pad_timeline.py. - Compare the run report, the traced calls from
APS5_TRACE_CALLS_FILE, and frames fromAPS5_FRAME_CAPTURE. - Prove the harness first: plant a wrong version of one function and check that the compare fails.
Prompt for ClaudeGOAL: Make `py -3 tools/swaptest.py <FUNCTION_ADDR>` run <SCENE> with the hook on and off, and fail on any difference. READ FIRST: `SCOPE.md`, `docs/ledger.csv`, Mewgenics-android's hook design notes in my `notes/hooks.md`, AnyPS5 `docs/user/USAGE.md` sections Controller input for test runs and Tracing system library calls. FENCE: Edit only `patches/`, `tools/swaptest.py`, `docs/PROGRESS.md` and `notes/parking-lot.md`. The dump is read-only. Every hook checks the original bytes before it fires. CHECK: Run `py -3 tools/swaptest.py --selftest`. Done means a correct copy of a small function passes, and a planted wrong copy fails. BUDGET: 2 hours. Plan first; wait for my okay before you write code. IF BLOCKED: Stop. Say which difference you cannot control, such as timing or random seeds. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Self-test output for both cases. 3) Files changed. 4) Known sources of noise. 5) New parking-lot items.
Done whenThe self-test passes a correct swap and fails a planted wrong one, on the same scene and pad timeline.Proof this works: Mewgenics-android (FNV-1a code-signature guards; hooks fire only on the matching executable), https://github.com/benpye/OpenRCT2 (procedures rewritten in C one by one inside the running original), research/topics/ai-decomp-practice.md C3 (calibrate the check first: break a file and confirm it fails)
First light: replace one small function
You pick one small leaf function and write it in C. A leaf function calls no other game function. It passes the swap test and the golden runs. This proves the whole loop works end to end.
- Pick the smallest game leaf function in
docs/ledger.csv. - Read it in Ghidra. Write the C in
src/game/<address>.c. - Build, then run
py -3 tools/swaptest.py <ADDRESS>and the golden runs from Path A. - Mark the row
replacedin the ledger with the test name.
Prompt for ClaudeGOAL: Replace the function at <ADDRESS> in <GAME> with readable C that passes the swap test. READ FIRST: `docs/ledger.csv` row <ADDRESS>, my Ghidra export of it in `notes/fn/<ADDRESS>.txt`, `docs/PROGRESS.md` section Ruled out. FENCE: Edit only `src/game/<ADDRESS>.c`, its hook entry in `patches/`, the ledger row and `notes/parking-lot.md`. No inline assembly, no copied byte arrays, no calls back into the original function. CHECK: Run `py -3 tools/swaptest.py <ADDRESS>`, then `py -3 tools/golden.py`. Done means both exit 0. BUDGET: Stop after 5 failed tries. IF BLOCKED: Stop. Paste the first difference the swap test reports. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Both check outputs. 3) Files changed. 4) Anything the C had to guess. 5) New parking-lot items.
Done whenOne function runs as your C, the swap test passes, and every golden run still passes.Proof this works: Mewgenics-android (replaced the inner LZ4 block decoder, kept the wrapper; checked with test vectors), https://github.com/benpye/OpenRCT2 (each procedure re-written in C and called from the patched original)
- Pick the smallest game leaf function in
Grind: many workers, one function each
Now you scale up. A lead session hands out small functions, one per worker. Each worker has its own worktree and its own build folder. Only verified work lands.
- Have a script pick the next units: small leaf functions first, then functions whose callees are all replaced.
- Give each worker one card from the Path B unit loop prompt.
- Run each worker in its own git worktree with absolute paths and its own build folder.
- Merge only after a fresh verify session re-runs the swap test and the golden runs on the merged branch.
- Update
docs/ledger.csvanddocs/PROGRESS.mdafter each merge.
Prompt for ClaudeGOAL: Hand out the next <N> functions from `docs/ledger.csv` to <N> workers and merge only verified results. READ FIRST: `docs/ledger.csv`, `docs/PROGRESS.md`, the Path B unit loop card in `notes/cards/unit-b.md`. FENCE: As lead, edit only `docs/ledger.csv`, `docs/PROGRESS.md` and `notes/parking-lot.md`. Workers edit only their own `src/game/<ADDRESS>.c` and hook entry. One owner per file. CHECK: After merging, run `py -3 tools/swaptest.py --all-replaced`, then `py -3 tools/golden.py`. Done means both exit 0 on the merged branch. BUDGET: <N> units, each with at most 5 tries. Stop the batch if two merges fail in a row. IF BLOCKED: Stop. List each unit as merged, failed or skipped, with the reason. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Merged check output. 3) Files changed. 4) Ledger counts before and after. 5) New parking-lot items.
Done whenThe ledger's replaced count went up, and the swap tests and golden runs pass on the merged branch.Proof this works: https://github.com/mattias800/prosper (agents work in their own worktrees; independent review before merge), research/topics/ai-decomp-practice.md C1 and C2 (worktrees, one owner per file, a match in a worktree is not a match on the merged branch)
Hard units: classes and data layouts
Big C++ classes and shared data are the slow part. Claude must work out the layout of each structure first. A wrong layout breaks many functions at once. Write each layout down before any code uses it.
- Write each structure in
include/game/<name>.hwith its size checked bystatic_assert. - Find layouts from how the code reads memory, from the run traces, and from engine headers if the engine is open.
- Mark guesses with a confidence tag, such as
CONFIDENCE: LOW, asprosperdoes. - Send units that failed three times in bulk to a hard-unit session.
Prompt for ClaudeGOAL: Write the layout of <STRUCT> used by the functions <ADDRESS_LIST> in <GAME>, and replace <ADDRESS> using it. READ FIRST: `docs/ledger.csv`, the Ghidra exports in `notes/fn/`, `docs/PROGRESS.md` section Ruled out, `include/game/`. FENCE: Edit only `include/game/<STRUCT>.h`, `src/game/<ADDRESS>.c`, its hook entry and `notes/parking-lot.md`. Every field has an offset comment and a confidence tag. CHECK: Run `py -3 tools/check.py`, `py -3 tools/swaptest.py <ADDRESS>` and `py -3 tools/golden.py`. Done means all exit 0, and `static_assert` checks the struct size. BUDGET: Stop after 3 failed layouts. Plan first; wait for my okay. IF BLOCKED: Stop. List the fields you are not sure of and why. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check outputs. 3) Files changed. 4) Fields marked LOW. 5) New parking-lot items.
Done whenThe struct header has a size check, and the function that uses it passes its swap test and the golden runs.Proof this works: https://github.com/mattias800/prosper (CONFIDENCE: HIGH/MED/LOW tags; evidence order), https://github.com/ymatrix1/PortPS5 (static_assert on the size and offsets of any struct the guest can see)
- Write each structure in
Harden: all swaps on, many scenes
You turn every replaced function on at once. You run every golden scene and longer play sessions. Bugs that only show when many swaps mix appear here.
- Run
py -3 tools/golden.pywith all hooks on, after every merge. - Add longer scenes with pad timelines, such as a full level.
- Compare frame replays of key scenes with all hooks on and all off.
- Bisect any new failure by turning hooks off in halves.
Prompt for ClaudeGOAL: Find which replaced function breaks <SCENE> when all hooks are on. READ FIRST: `docs/ledger.csv`, the failing golden output in `notes/golden-fail.txt`, `docs/PROGRESS.md` section Ruled out. FENCE: Edit only `tools/bisect_hooks.py`, `docs/PROGRESS.md` and `notes/parking-lot.md`. Do not change any replaced function in this task. CHECK: Run `py -3 tools/bisect_hooks.py <SCENE>`. Done means it names one hook, and the scene passes with only that hook off. BUDGET: 2 hours. IF BLOCKED: Stop. Say if the failure needs two hooks together to appear. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The bisect log. 3) Files changed. 4) The guilty hook and its ledger row. 5) New parking-lot items.
Done whenEvery golden scene passes with all hooks on, on a clean checkout.Proof this works: https://github.com/benpye/OpenRCT2 (the project stays a DLL until all procedures are re-written), Astro Bot native PC port (A/B runs on the same scene; kept and rejected results logged)
- Run
Port: drop the eboot
Once every game function is your C, the original eboot is no longer needed. You build your C as a normal program for the new system. Then you swap the PS5 system parts for that system's own.
- Build a version that loads no eboot code. Every call must land in your C.
- Put the PS5 system calls behind your own interface: files, pads, sound, saves and graphics.
- Fix 64-bit and pointer-size problems for the new system.
- Build for the new system and run the same golden scenes there.
Prompt for ClaudeGOAL: Build <GAME> from my C alone, with no eboot code loaded, and pass the golden scenes on <TARGET_SYSTEM>. READ FIRST: `docs/ledger.csv` (all rows replaced or library), `SCOPE.md`, `docs/PROGRESS.md`. FENCE: Edit only `src/platform/`, the build files and `notes/parking-lot.md`. Do not change game logic in `src/game/` in this task. CHECK: Run `py -3 tools/golden.py --target <TARGET_SYSTEM>`. Done means every golden scene passes there. BUDGET: Plan first; wait for my okay. Then one system part per session. IF BLOCKED: Stop. Name the system part that has no match on the new system. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Golden output on the target. 3) Files changed. 4) System parts still missing. 5) New parking-lot items.
Done whenThe game runs on the new system from your C alone and passes the same golden scenes.Proof this works: https://github.com/error9098x/ps5-native-mac (Metal backend and own system runtime on Apple silicon), Mewgenics-android (native Vulkan backend replaced the game's PS5 Agc backend)
Tools you need
| Tool | What it does | How to get it | How we know |
|---|---|---|---|
| AnyPS5 Required both | The relinker that turns a PS5 game file into a Windows or Linux program, plus new copies of the PS5 system libraries. Every PC port here is built on it. Path A Path B | git clone --recursive https://github.com/boykopovar/AnyPS5.git C:\a5 (or download relinker-v0.1.5.exe and prx-windows-v0.1.5.zip from the v0.1.5 release to try it without building) | README, docs/dev/BUILD.md, docs/user/USAGE.md, AGENTS.md, tools/title_run.py source and v0.1.5 release assets (read 2026-11-13); used by Dead Cells PS5 Native, SonsOfSparta-PS5 Native, Astro Bot native PC port, Mewgenics-android |
| WinLibs GCC 15.2.0 (UCRT, POSIX threads) Required Windows | The C++ compiler that AnyPS5's Windows builds use. MSYS2 UCRT64 GCC also works. Scoop's GCC 15.2.0 cannot build the libraries.Path A Path B | Download winlibs-x86_64-posix-seh-gcc-15.2.0-mingw-w64ucrt-14.0.0-r7.zip from the release page, check it against its .sha256 file, unzip to C:\tools\winlibs, and put C:\tools\winlibs\mingw64\bin first on PATH. Its own cmake needs SSL_CERT_FILE set (Path A step 2) | release page and asset list (read 2026-11-13); AnyPS5 BUILD.md names this release and MSYS2 UCRT64 GCC 16.2.0-6 as the other choice; used by Dead Cells PS5 Native and Astro Bot native PC port |
| CMake Required Windows | Sets up the build. AnyPS5 needs version 3.21 or newer.Path A Path B | winget install Kitware.CMake | winget-pkgs manifest Kitware.CMake, newest 4.4.4 (read 2026-11-13); AnyPS5 BUILD.md requirements |
| Ninja Required Windows | Runs the build steps fast. Every reviewed build uses -G Ninja.Path A Path B | winget install Ninja-build.Ninja | winget-pkgs manifest Ninja-build.Ninja, newest 1.13.2 (read 2026-11-13); build steps of Dead Cells PS5 Native and SonsOfSparta-PS5 Native |
| Git for Windows (with Git Bash) Required Windows | Clones the repos. The full AnyPS5 build also runs one Bash script, so a Git install without Bash is not enough. Put its cmd and usr\bin folders on PATH.Path A Path B | winget install Git.Git | winget-pkgs manifest Git.Git 2.56.0.x (read 2026-11-13); AnyPS5 BUILD.md Windows setup section |
| Python 3 Required Windows | Runs every AnyPS5 helper script and its Python tests. The full build also needs it for glslang. Run the tools with py -3.Path A Path B | winget install Python.Python.3.13 | AnyPS5 BUILD.md (Python 3 for glslang; 3.11 or newer for validate_patch_catalog; use py -3 with the python.org installer) and winget-pkgs manifest Python.Python.3.13, newest 3.13.9 (read 2026-11-13) |
| Vulkan SDK Helpful Windows | Gives you the Vulkan validation layer and spirv-val. They catch graphics mistakes. AnyPS5 wants layer version 1.4.335 or newer.Path A Path B | winget install KhronosGroup.VulkanSDK | winget-pkgs manifest KhronosGroup.VulkanSDK, newest 1.4.363.0 (read 2026-11-13); AnyPS5 AGENTS.md validation rule |
| Tracy profiler 0.14.1 Helpful Windows | Shows where each frame's time goes. AnyPS5 builds the matching client when you add -DAPS5_ENABLE_TRACY=ON.Path A | Download windows-0.14.1.zip from the v0.14.1 release page and unzip it | v0.14.1 release assets (read 2026-11-13); AnyPS5 BUILD.md Tracy section; Astro Bot native PC port TODO item |
| KytyPS5 Helpful both | A PS5 translation layer with its own graphics stack. Use it to test a game, or as a faster graphics part when AnyPS5's renderer is too slow.Path A | Download KytyPS5-<date>-Windows-x64.zip from the latest release page (to build it you need Visual Studio 2022 Build Tools with clang-cl and Qt 6) | README and latest release assets KytyPS5-2026-11-12-58ca8d1 (read 2026-11-13); used by Astro Bot native PC port as kyty_gpu.dll |
| Ghidra Required Windows | A free tool that shows a program's machine code and a rough C view. You use it to read crash sites and to map every function for Path B. Path A Path B | Install a JDK (winget install EclipseAdoptium.Temurin.21.JDK for Ghidra 12.1.3, or EclipseAdoptium.Temurin.25.JDK for 12.2), download the release zip, unzip it, and run ghidraRun.bat. Use 12.1.3 for the PS5 loader plugin and 12.1.4 for GhidraMCP | README at tags Ghidra_12.1.3_build (JDK 21) and Ghidra_12.2_build (JDK 25); releases 12.1.3 (2026-08-18), 12.1.4 (2026-09-21), 12.2 (2026-10-22) (read 2026-11-13); winget-pkgs EclipseAdoptium.Temurin.21.JDK and .25.JDK; Astro Bot native PC port used Ghidra 12.1.4 |
| ghidra_ps5_elf_plugin Helpful Windows | Adds PS5 file support to Ghidra. It names imports by NID and uses the file's unwind data to find where functions start and end.Path B | Clone the repo, set GHIDRA_INSTALL_DIR in its gradle.properties to your Ghidra 12.1.3 folder, run <GHIDRA_DIR>\support\gradle\gradlew.bat buildExtension in the repo folder, then File > Install Extensions in Ghidra | README, file list and build.bat (read 2026-11-13; the repo has no gradlew.bat, build.bat calls Ghidra's own); port of flatz/ida_ps5_elf_plugin (159 stars). Not used by a reviewed project |
| ps5rs Helpful both | A Rust tool that reads PS5 game files. It finds the engine and middleware, and turns function names into NIDs. Path A Path B | Install Rust 1.85 or newer, clone the repo, then run cargo build --release | README Building and Quick Start sections (read 2026-11-13). Not used by a reviewed project |
| box64 Helpful WSL/Linux | Runs x86-64 Linux code on ARM64 devices by translating it on the fly. The Android ports run the game's code through it. Path B | Build from source on Linux by following docs/COMPILE.md in the repo (Mewgenics-android builds it with -DANDROID=ON in WSL) | repo metadata and README compile link (read 2026-11-13); used by Mewgenics-android and PvZ: Replanted Android |
| ps5-native-mac Helpful macOS (Apple silicon) | A static recompiler that turns the game's x86-64 code into C, then into Apple silicon code. It has its own system runtime and Metal graphics. Path B | Needs a Mac with Apple silicon, macOS 26 and Xcode with the Metal toolchain. Clone the repo and follow the Building section of its README. It does not run on Windows or WSL | README (read 2026-11-13): Sons of Sparta runs end to end on an Apple M5. Tested on one machine only |
Skills and add-ons
Read any skill before you install it. A skill is a set of instructions that Claude will follow. See Tools and Skills for the full list and safety notes.
reverse-engineer-binary
Rules for reading the eboot in Ghidra: read the code first, and give an address with every claim. It may suggest an MCP bridge; pick and install that yourself.
npx skills add xsyetopz/skills --skill reverse-engineer-binary -a claude-code
ghidra-headless
Runs Ghidra with no window and saves functions and strings to files. Set GHIDRA_HOME yourself on Windows. Good for the Path B ledger.
npx skills add trailofbits/skills-curated --skill ghidra-headless -a claude-code
systematic-debugging
No fix until the cause is found. On PS5 a black screen has many causes, so this rule saves days.
npx skills add obra/superpowers --skill systematic-debugging -a claude-code
verification-before-completion
Claude may not say done without fresh proof. On PS5 that means a rendering outcome or a run report, not a window that opened.
npx skills add obra/superpowers --skill verification-before-completion -a claude-code
handoff
Writes a short note so a fresh session can carry on. Tell it to save inside the repo, next to docs/STATUS.md.
npx skills add mattpocock/skills --skill handoff -a claude-code
dispatching-parallel-agents
Shows Claude how to brief each worker and when not to split. Use it for the Path B grind and for batches of missing functions.
npx skills add obra/superpowers --skill dispatching-parallel-agents -a claude-code
GhidraMCP
Lets Claude ask Ghidra for code, names and cross references at a crash address, instead of reading big text exports. It needs Ghidra 12.1.4, while the PS5 loader plugin targets 12.1.3. The uvx line fetches the newest code each time; it is not pinned.
Install `uv`. Download release 2.0 and add its `ghidra_12.1.4_PUBLIC_<date>_GhidraMCP.zip` in Ghidra 12.1.4: File, Install Extensions, `+`. Restart, then turn on GhidraMCPPlugin in File, Configure, Developer. Then run `claude mcp add ghidra -- uvx --from git+https://github.com/LaurieWired/GhidraMCP ghidra-mcp --ghidra-server http://127.0.0.1:8080/`
implement-prx-function
PortPS5 ships a skill like this with seven steps. Its last step updates a spec file; here you update the AnyPS5 debt file instead. It keeps every missing-function fix the same shape and stops fake successes.
Make `.claude/skills/implement-prx-function/SKILL.md` with seven steps: find the library folder, copy the neighbours' export pattern, build behaviour only from your dump and open code, use real error codes, throw on unknown cases, add a unit test, and update the debt file.
title-run gate
One fixed proof for every Path A unit. It also follows Mewgenics' rule against launch loops.
Make `.claude/skills/title-run/SKILL.md` and `run.ps1`. It runs `C:\a5\tools\title_run.py` for your title ID with two runs, prints the two outcome lines, and exits 1 unless both are `rendering`, since `title_run.py` itself always exits 0. It never starts the game more than once per build.
log-reader (subagent)
PS5 run logs are very long. This keeps them out of the main session.
Make `.claude/agents/log-reader.md`: a read-only subagent that reads `run.log` and `anyps5-report.json` and returns the first error, the function it names, and the last 10 traced calls. It never opens the dump and never builds or runs anything.
Claude setup for this system
Cut Path A into one missing function, one crash, or one graphics bug per session. Each unit has a check. The audit loses one absent row, the golden run gets further, or the frame replay matches better. Cut Path B into one function per session, checked by the swap test and the golden runs. Keep a ledger file for each: docs/imports.md, docs/PROGRESS.md and docs/ledger.csv. Never hand Claude the whole game at once.
Which model and effort for each job
The model and effort names come from one shared table, so they stay the same on every guide. See Claude Setup for the reasons.
| Job | What it means on this system | Max 20x (now called Ultra) | Max 5x (now called Max) |
|---|---|---|---|
| Plan the project | Pick the game and version. Read the AnyPS5 bring-up thread and any port of the same game. Write SCOPE.md. For Path B, measure the function count first. | Fable 5.2 effort highOne session. This choice shapes months of work. | Opus 5.5 effort highUse Fable 5.2 for this one session if you have weekly room. |
| Set up the project | Install WinLibs GCC, CMake, Ninja, Python 3 and Git. Build AnyPS5 at a pinned commit in C:\a5. Write tools/check.py, tools/prepare.py and CLAUDE.md. | Opus 5.5 effort medium | Sonnet 5.5 effort medium |
| Study the game file | Run the import audit and trace which imports the game calls. Read crash sites in Ghidra. For Path B, build the function ledger. | Opus 5.5 effort high | Opus 5.5 effort mediumRaise to high for function edges and data layouts. A mistake there spreads. |
| Build the runtime | Write missing system functions in core/libs/prx/<library>/, with a test each. Fix files, threads, memory, pads, sound and saves. | Opus 5.5 effort high | Opus 5.5 effort mediumRaise to high for timing and graphics bugs. |
| Find a bug | Find why the game stops, hangs or draws wrong. Use the crash report, traced calls, the PM4 replay and the frame replay. | Opus 5.5 effort highAfter two failed tries with full evidence, move to Fable 5.2. | Opus 5.5 effort highOne bug at a time. |
| Bulk units | Path A: simple missing functions in different libraries, one per worker. Path B: small leaf functions, one per worker, each with its swap test. | Sonnet 5.5 effort mediumHaiku 5.5 for the plainest units. | Sonnet 5.5 effort mediumFewer workers at once. Haiku 5.5 for the plainest units. |
| Hard units | Shader recompiler bugs, GPU waits that never release, and large class-heavy functions in Path B. | Fable 5.2 effort highGo to xhigh only after a failed try at high. | Opus 5.5 effort highSend only the last few that still fail to Fable 5.2. |
| Check the work | A fresh session re-runs tools/check.py and the golden runs. It hunts for fake successes, title-specific code and skipped draws. | Opus 5.5 effort mediumAlways a new session, never the one that did the work. | Sonnet 5.5 effort highAlways a new session, never the one that did the work. |
| Keep the records | Update docs/STATUS.md, docs/PROGRESS.md, the import table and the ledger after each unit. | Haiku 5.5 effort medium | Haiku 5.5 effort medium |
How many sessions at once
Max 20x (now called Ultra)
Path A: one lead and two or three workers, each in its own worktree on a different library or bug. Let only one session run the game at a time, so speed and timing results stay comparable. Path B: one lead and four to six workers on leaf functions, each with its own build folder.
Max 5x (now called Max)
Path A: one session at a time, plus a read-only subagent for logs. Path B: one lead and one or two workers. Run long golden-run batches at night.
Lines for this project's CLAUDE.md
- Read `SCOPE.md`, `docs/STATUS.md` and the newest entry in `docs/PROGRESS.md` before any change. - The one check is `py -3 tools/check.py`. Work is not done until it exits 0. Paste its output. - The dump at <DUMP> is read-only. Never copy `eboot.bin`, `.prx` files, `sce_sys`, shaders, saves, frames or recorded input into the repo, an issue or a cloud session. - A system function you cannot build calls `NotImplemented_nid_no_patch(__func__)`. Never return a made-up success. - Never special-case a title ID, hash, value or path in shared library code. Per-game changes go only in `patches/` or the game's patch TOML. - In `AnyPS5` code, write no comments. Put known gaps in `docs/dev/debt/<library>.md`. (AnyPS5 `AGENTS.md`) - After any library change, rebuild with `cmake --build build --target libs`. The game loads its `.prx` files from `build/core/libs/libs`. - Quote test results from `ctest --no-tests=error` with the test count. A green run with zero tests is not a pass. - A byte patch lists the `expected` bytes and is checked with `relinker --verify-patches`. Never write to a fixed address without that check. - Start the game at most once per build, through `C:\a5\tools\title_run.py`. Never launch it in a loop. It always exits 0, so read its outcome lines. - Never skip a draw, a dispatch or a system call to get past a bug. Log it and fail visibly. - Write every dead end in the `## Ruled out` section of `docs/PROGRESS.md`, with the evidence, in the same commit. - In a worktree, use absolute paths and your own build folder. Never build in the main checkout. - A pipeline's exit code is its last stage's. Save the real exit code before you pipe to `tail` or `grep`. - Never write this machine's user name or local paths into committed files.
Keep the context small
- Send big run logs to a read-only subagent. Get back only the first error and the function it names.
- Give Claude a function name or an address and a short Ghidra export, not a wall of machine code.
- Do not let Claude read the whole
AnyPS5tree. Name the one library folder it needs. - Keep
docs/STATUS.mdto one page. Put history indocs/PROGRESS.md. - Clear the chat between units. The ledger files carry the state.
More prompts for this system
Each step above has its own prompt. These extra prompts cover the rest of the work. Fill in the parts in <ANGLE_BRACKETS>.
Scope and feasibility
Before any work on a new PS5 game. It tells you if a port exists and what will be hard.
GOAL: Tell me if <GAME> (<TITLE_ID>, version <VERSION>) can get a native PC port on AnyPS5, and what blocks it today. Write it in `notes/feasibility.md`.
READ FIRST: The AnyPS5 Game bring-up thread for <TITLE_ID>: <DISCUSSION_URL>. The KytyPS5 compatibility entry, if any. These repos: <REPO_URLS>.
FENCE: Edit only `notes/feasibility.md` and `notes/parking-lot.md`. Do not open the game dump. Write no code.
CHECK: Run `py -3 -c "import sys;t=open('notes/feasibility.md',encoding='utf-8').read();sys.exit(0 if all(h in t for h in ['## Existing ports','## Furthest point reported','## Known blockers','## First milestone and its check','## Out of scope']) else 1)"`. Done means it exits 0 and each existing port has a link.
BUDGET: 45 minutes.
IF BLOCKED: Stop. Say what you could not find. Do not fill gaps with guesses.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) A one-line verdict: start, join an existing effort, or wait. 3) Files changed. 4) Risks. 5) Questions for me.A good answer: Starts with DONE. Gives a clear verdict and names any port or branch of the same game. Says how far others got, with links, and what blocked them.
Map the binary: import audit
Right after the first relink, and again after each AnyPS5 update.
GOAL: Update `docs/imports.md` from a fresh import audit of <GAME> and list what changed since the last audit. READ FIRST: `docs/imports.json` (new audit), `docs/imports-prev.json` (last audit), `<WORK>/calls.txt`. FENCE: Edit only `docs/imports.md`, `docs/PROGRESS.md` and `notes/parking-lot.md`. Do not change code. CHECK: Run `py -3 tools/count_imports.py`. Done means the five totals match the audit's summary. BUDGET: 30 minutes. IF BLOCKED: Stop. Say which audit file is missing or unreadable. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Totals now and before. 3) Files changed. 4) Imports that moved between groups. 5) New parking-lot items.
A good answer: Shows the five totals before and after, and lists every import that moved, such as from absent to implemented.
Unit loop, Path A: one missing function
For each absent or stub import that the game really calls.
GOAL: Implement <FUNCTION> (<NID>) in AnyPS5's <LIBRARY> with a unit test. READ FIRST: `docs/imports.md` row for <FUNCTION>, how the game calls it in `notes/fn/<FUNCTION>.txt`, neighbours in `core/libs/prx/<LIBRARY>/`, AnyPS5 `AGENTS.md`. FENCE: Edit only `core/libs/prx/<LIBRARY>/`, its test, `docs/dev/debt/<LIBRARY>.md` and `notes/parking-lot.md`. No Sony SDK material. No title-specific branches. Unknown cases throw. CHECK: Run `py -3 tools/check.py`. Done means it exits 0, the new test is in the count, and the audit shows <FUNCTION> as implemented or partial. BUDGET: Stop after 3 failed builds or 60 minutes. IF BLOCKED: Stop. Say which argument or return value you cannot work out. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output and audit line. 3) Files changed. 4) Cases that still throw. 5) New parking-lot items.
A good answer: Starts with DONE. Shows the test count went up and the audit line for the function. Lists the input cases that still throw on purpose.
Unit loop, Path A: one crash
For each crash or hang during play, once the golden runs exist.
GOAL: Find and fix the cause of the crash in <SCENE> of <GAME>. READ FIRST: The crash report in `logs/<RUN>.log`, the last traced calls, `docs/PROGRESS.md` section Ruled out. FENCE: Edit only the files the evidence points to, their tests, `docs/PROGRESS.md` and `notes/parking-lot.md`. No fix until the cause is proven. Never catch and hide the error. CHECK: Run `py -3 tools/check.py`, then `py -3 tools/golden.py`. Done means both exit 0 and the golden scene passes <SCENE>. BUDGET: Stop after 3 failed hypotheses. Write each one in Ruled out. IF BLOCKED: Stop. Show the evidence you have and what you need, such as a capture or a longer stack dump. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The cause, with the log lines that prove it. 3) Files changed. 4) Check and golden output. 5) New parking-lot items.
A good answer: Names the cause with log lines before any fix. The golden scene passes after the fix, and the test count went up.
Unit loop, Path B: one function
For each function in the Path B grind.
GOAL: Replace the function at <ADDRESS> in <GAME> with readable C that passes its swap test. READ FIRST: `docs/ledger.csv` row <ADDRESS>, `notes/fn/<ADDRESS>.txt`, the headers it needs in `include/game/`. FENCE: Edit only `src/game/<ADDRESS>.c`, its hook entry in `patches/`, its ledger row and `notes/parking-lot.md`. No inline assembly. No byte arrays copied from the game. No calls back into the original. CHECK: Run `py -3 tools/swaptest.py <ADDRESS>`. Done means it exits 0 on the scene in the ledger row. BUDGET: Stop after 5 failed tries. IF BLOCKED: Stop. Paste the first difference and say what you think causes it. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Swap test output. 3) Files changed. 4) Guesses in the C. 5) New parking-lot items.
A good answer: Starts with DONE and the swap test output. The C reads like normal code, with no copied bytes and no assembly.
Batch dispatch
When you have a list of small, separate units: missing functions in different libraries, or leaf functions.
GOAL: Run the next <N> units from <LEDGER_FILE> in parallel, one worker each, and merge only verified ones. READ FIRST: <LEDGER_FILE>, the unit card in `notes/cards/<CARD>.md`, `docs/PROGRESS.md`. FENCE: As lead, edit only <LEDGER_FILE>, `docs/PROGRESS.md` and `notes/parking-lot.md`. Each worker uses its own worktree with absolute paths and its own build folder. No two workers touch the same file. CHECK: After merging, run `py -3 tools/check.py` and `py -3 tools/golden.py`. Done means both exit 0 on the merged branch. BUDGET: <N> units. Each worker stops after 3 to 5 failed tries. IF BLOCKED: Stop the batch if two merges fail in a row. Report each unit as merged, failed or skipped. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Merged check output. 3) Files changed. 4) Ledger counts before and after. 5) New parking-lot items.
A good answer: A table of units with merged, failed or skipped. Check output from the merged branch, not from a worker's worktree.
Debug with evidence
When a bug has resisted one or two quick fixes, such as a black screen or a GPU hang.
GOAL: Prove the cause of <SYMPTOM> in <GAME>. Do not fix it yet.
READ FIRST: `docs/PROGRESS.md` section Ruled out, `logs/<RUN>.log`, the PM4 capture in <SUBMITS_DIR> or the frame capture in <CAPTURE_DIR>.
FENCE: Edit only `notes/debug/<BUG>.md` and `notes/parking-lot.md`. You may add log lines behind a switch, but no fixes.
CHECK: Run `py -3 -c "import sys;t=open('notes/debug/<BUG>.md',encoding='utf-8').read();sys.exit(0 if all(h in t for h in ['## Cause','## Proof','## Ruled out']) else 1)"`. Then run the on and off commands under Proof yourself. Done means the check exits 0 and the symptom shows only with the switch on.
BUDGET: 90 minutes or 3 hypotheses.
IF BLOCKED: Stop. List what you ruled out, with evidence, and what you need next.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The cause and the proof. 3) Files changed. 4) Hypotheses ruled out. 5) New parking-lot items.A good answer: One proven cause, with a switch or a replay that turns the symptom on and off. Ruled-out ideas are written down with their evidence.
Verify: hunt for cheating
In a fresh session, after any unit is reported DONE.
GOAL: Check that the work in commits <COMMIT_RANGE> is real and stays inside its fence. READ FIRST: `git diff <COMMIT_RANGE>`, the task card in `notes/cards/<CARD>.md`, `CLAUDE.md`. FENCE: Edit only `notes/verify/<DATE>.md`. Change no code. CHECK: Re-run `py -3 tools/check.py` and `py -3 tools/golden.py` yourself. Then search the diff for: returns of success with no work, new title IDs or hashes in shared code, skipped draws or dispatches, changed tests or expected values, changes outside the fence, and copied game bytes. Done means every item has a yes or no with a file and line. BUDGET: 45 minutes. IF BLOCKED: Stop. Say what you could not re-run. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) PASS or FAIL. 2) Check outputs from your own run. 3) Files changed. 4) Each finding with file and line. 5) New parking-lot items.
A good answer: Its own fresh check output, and a yes or no for each cheat type with file and line. It does not trust the worker's pasted output.
Status report
Once a week, or before you plan the next block of work.
GOAL: Write a status report for <GAME> from the ledgers only.
READ FIRST: `docs/imports.md`, `docs/ledger.csv` (Path B), `docs/PROGRESS.md`, the last golden output in `logs/golden-latest.txt`.
FENCE: Edit only `docs/STATUS.md`. Change no code. Do not guess numbers.
CHECK: Run `py -3 tools/count_imports.py`, then `py -3 -c "import sys;t=open('docs/STATUS.md',encoding='utf-8').read();sys.exit(0 if len(t.splitlines())<=60 and 'Source:' in t else 1)"`. Done means both exit 0, and every number names its ledger file after `Source:`.
BUDGET: 20 minutes.
IF BLOCKED: Stop. Say which ledger is missing or out of date.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The numbers with their sources. 3) Files changed. 4) Top three blockers. 5) Parking-lot items that need my decision.A good answer: Short and made of numbers: imports by group, golden scenes passing, functions replaced. Each number names its file.
Session handoff
At the end of every session, or before your plan's limit runs out.
GOAL: Save everything the next session needs to carry on <TASK> without this chat.
READ FIRST: `docs/STATUS.md`, `docs/PROGRESS.md`, `git status`, `git log -5 --oneline`.
FENCE: Edit only `docs/STATUS.md`, `docs/PROGRESS.md` and `notes/parking-lot.md`. Do not start new work.
CHECK: Run `py -3 -c "import sys,datetime;t=open('docs/PROGRESS.md',encoding='utf-8').read();sys.exit(0 if datetime.date.today().isoformat() in t and all(h in t for h in ['Works:','Does not work:','Next step:','Uncommitted:']) else 1)"`. Done means it exits 0, the Works lines hold pasted check output, and new dead ends are in Ruled out.
BUDGET: 15 minutes.
IF BLOCKED: Stop. Say what state you could not save.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The new PROGRESS entry. 3) Files changed. 4) The next step as one task card. 5) New parking-lot items.A good answer: A dated entry a new session can act on, ending with the next task card ready to paste.
Blocked: cannot deliver
When Claude keeps trying and failing, or says something should work without proof.
GOAL: Give me a plain answer on <TASK>: can it be done now, and if not, why not.
READ FIRST: Your last report, `docs/PROGRESS.md` section Ruled out, the failing check output.
FENCE: Edit only `notes/blocked/<TASK>.md`. Change no code. Try nothing new.
CHECK: Run `py -3 -c "import sys;l=open('notes/blocked/<TASK>.md',encoding='utf-8').readline().strip();sys.exit(0 if l in ('CAN','CANNOT','CANNOT YET') else 1)"`. Done means it exits 0, and the note gives the evidence and two or three options with their cost.
BUDGET: 15 minutes.
IF BLOCKED: Say so in the first line. That is a valid answer.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) BLOCKED or PARTIAL. 2) What fails, with output. 3) Files changed. 4) Options: wait for an AnyPS5 update, try KytyPS5 for this part, change the milestone, or stop. 5) What you need from me.A good answer: One word first, then evidence. Options are real choices, such as waiting for upstream or changing the milestone, not more of the same tries.
Scope change request
When Claude wants to go outside its fence, such as editing the shader recompiler during a library task.
GOAL: Ask me before you leave the fence of <TASK>. READ FIRST: The task card for <TASK>, `SCOPE.md`. FENCE: Edit only `notes/scope-requests.md`. Change no code until I answer. CHECK: Run `git status --porcelain`. Done means it lists only `notes/scope-requests.md`, and the request names the files you want to touch, why the fence is not enough, the risk, and how you will check it. BUDGET: 10 minutes. IF BLOCKED: Stop and wait for my answer. OTHER PROBLEMS: Write them in `notes/parking-lot.md`. REPORT: 1) BLOCKED (waiting for approval). 2) The evidence that the fix is outside the fence. 3) Files you want to change. 4) The check you will run. 5) What happens if I say no.
A good answer: A short request with file names and a check, then a stop. No code changed before you answer.
Keep Claude on task
These are the detours Claude tends to take on this system, and the rule that stops each one. The full method is on Stay on Task.
| The detour | The rule to give Claude |
|---|---|
| Claude makes a missing system function return success with no work, so the game moves on. | A function you cannot build calls NotImplemented_nid_no_patch(__func__). Never return a made-up success. (AnyPS5 AGENTS.md) |
| Claude adds a branch for your title ID, a hash or a file path inside shared library code. | Never special-case a title ID, hash, value or path in shared code. If the general rule is unknown, throw and write it in the debt file. (AnyPS5 and PortPS5) |
| Claude skips a draw or a compute job that fails, and calls the black screen fixed. | Never skip a draw, dispatch or system call to get past a bug. Log it, fail visibly, and file the exact opcode. (prosper) |
| Claude says the game runs because the window opened or the process started. | Done means title_run.py prints outcome rendering for both runs. A window, a process or a log line does not count. |
| Claude relaunches the game again and again to try small changes. | Batch your changes into one build, then start the game once. Never launch it in a loop. (Mewgenics-android AGENTS.md) |
| Claude pastes shader bytes, log lines with game data, or save files into the repo or an issue. | Never add keys, firmware, Sony libraries, game files, shader code, saves, frames or recorded input to the repo, CI or issues. (PortPS5) |
| Claude reports green tests from a test run that found no tests. | Run ctest with --no-tests=error and quote the test count with the exit code. (prosper) |
| Claude tries an idea that an earlier session already disproved. | Read the ## Ruled out section before forming a hypothesis. Add every disproved idea there, with its evidence, before you stop. (prosper) |
Claude pulls a big upstream AnyPS5 update in the middle of a task. | Stay on the pinned commit in SCOPE.md. Ask before you merge any upstream update. (Mewgenics-android, Astro Bot) |
Problems other projects hit
The build fails with the wrong compiler. libc.prx needs a MinGW-w64 GCC with the right exception support.
Fix: Use WinLibs GCC 15.2.0 (x86_64-ucrt-posix-seh) or MSYS2 UCRT64 GCC. Scoop's GCC 15.2.0 does not work. Put the right bin folder first on PATH.
Seen in: Dead Cells PS5 Native, AnyPS5
The game will not start, with Windows error 126, even though the .prx files are there.
Fix: Copy libgcc_s_seh-1.dll, libstdc++-6.dll and libwinpthread-1.dll from the compiler's bin folder into libs/. The loader does not search PATH.
Seen in: Dead Cells PS5 Native, AnyPS5
Antivirus software deletes or blocks the relinked .exe and the test programs.
Fix: Add only your build folder to the scanner's exclusions. Tell players to do the same for the game folder.
Seen in: Astro Bot native PC port, AnyPS5
The build fails on long file names under build.
Fix: Clone to a very short path, such as C:\a5, and configure in a new build folder. Moving an old build folder does not help.
Seen in: AnyPS5
The first configure stops with status 60 while it downloads FFmpeg.
Fix: Set SSL_CERT_FILE to Git's CA bundle, such as C:\Program Files\Git\ucrt64\etc\ssl\certs\ca-bundle.crt. Then configure again.
Seen in: AnyPS5
python3 prints Python was not found and exits with code 9009.
Fix: The python.org installer has no python3.exe. Run the tools with py -3 instead.
Seen in: AnyPS5
The launcher says the game is not decrypted, or cannot find the files.
Fix: Use your own decrypted dump. Put the files where the launcher expects them, not in a subfolder. unwrap_self.py refuses files that still look encrypted.
Seen in: Dead Cells PS5 Native, SonsOfSparta-PS5 Native
The game exits with code 0xC0000409, which looks like memory damage.
Fix: Read the error output first. It often means AnyPS5 threw an error and stopped on purpose. On an 8 GB graphics card, Astro Bot needed APS5_HOST_IMPORT_MIB=11264.
Seen in: Astro Bot native PC port
Black tile holes and GPU waits that time out on the compute queues.
Fix: Capture with APS5_DUMP_SUBMITS and run agc_pm4_replay. It lists waits that never release. Astro Bot also swapped in the KytyPS5 graphics stack.
Seen in: Astro Bot native PC port
The game is slow with screen overlays or capture tools running.
Fix: Close overlays such as OBS hooks, ReShade and RivaTuner, or, in the Dead Cells build, add an empty no_vk_layers.txt next to deadcells.exe.
Seen in: Dead Cells PS5 Native
The first play shows glitches and stutters while shaders are built.
Fix: Expect it on the first run. Later runs use the shader cache. Test speed only after a warm-up run.
Seen in: SonsOfSparta-PS5 Native, Dead Cells PS5 Native
Only one game version is tested. Byte patches can break another version.
Fix: Find each patch by its exact bytes and require one match, so it switches off on other versions. Use the expected field in the patch TOML.
Seen in: Astro Bot native PC port, Dead Cells PS5 Native, SonsOfSparta-PS5 Native
Reading only the .text part of the relinked file misses game code.
Fix: Read all executable load ranges of the file instead.
Seen in: Mewgenics-android
Heavy scenes are limited by one CPU thread sending draw commands, not by the graphics card.
Fix: Measure with Tracy or the bench reports before you change anything. Astro Bot spent about 22 ms of a 30 ms frame on this.
Seen in: Astro Bot native PC port, Astro Bot Linux branch of AnyPS5
On an Intel CPU, the relinked game can fail on AMD-only instructions.
Fix: Relink with --to-intel. It cannot fix code the game makes while it runs, so such games need an AMD CPU.
Seen in: AnyPS5
How long it takes
| Max 20x (now called Ultra) | Max 5x (now called Max) | One person, no AI | How sure | |
|---|---|---|---|---|
| Path A: running on PC | 2 to 10 weeks | 5 to 25 weeks | 6 to 18 months | low confidence |
| Path B: full decomp | 1 to 3 years | 2.5 to 7 years | 8 to 20 years, and in practice nobody has done it | low confidence |
Read this first
Numbers assume one medium-sized game and one person giving 10 to 15 hours a week. The fast PS5 dates come from people who often work on the shared tool itself, and work long days. No per-game PC port says whether it used AI or how many hours it took. Only two per-game projects say they used AI, and both target Android. So Path A is low confidence. No PS5 port predates AI agents, so the no-AI number is a guess borrowed from the PS4 guide. No PS5 decomp exists at all, so Path B is low confidence. It uses the PS4 guide's numbers, which lean on PC projects.
What makes it faster or slower
- Whether
AnyPS5already runs your game. Then a port took days. If not, you work inside the shared tool for weeks. - How many imports are absent or stubs at the start.
- Graphics: shader bugs, GPU waits and slow draw handling take the longest.
- How fast the shared tools change. Updates can fix your bug or break your patches.
- For Path B: the number of game functions and how much is C++ classes with unknown layouts.
The facts behind these numbers
| Project | What happened | Link |
|---|---|---|
| SonsOfSparta-PS5 Native | First commit 2026-10-03. First release 2026-10-05. Six releases by 2026-10-16. 4 contributors. No AI use stated. | source |
| PvZ: Replanted Android | First commit 2026-10-11. First release 2026-10-16. 17 of 26 commits carry an AI co-author line. 1 contributor. | source |
| Mewgenics-android | First commit 2026-10-24. First release 2026-10-29. Last commit 2026-11-02. Its README claims 60 FPS in combat on one phone. Root AGENTS.md shows an AI assistant did the work. 1 contributor. | source |
| Astro Bot native PC port | Stages checked 2026-10-28. Repo created 2026-11-08. First release 2026-11-13. Needed a graphics swap to KytyPS5. 1 contributor. No AI use stated. | source |
| Ghost of Yotei branch of AnyPS5 | Fork created 2026-10-05. Open world first reached 2026-10-17. Still about 1.3 FPS on 2026-11-14. | source |
| AnyPS5 | First commit 2026-08-03. First release 2026-09-28. About 40,000 commits by mid-November. Has an AGENTS.md, and each pull request says if AI helped. | source |
| prosper | First commit 2026-07-03. First release 2026-07-14; six releases by 2026-10-10. A PS5 layer built by AI agents with a human who reviews but does not code. 4,945 commits, 7 GitHub contributors. | source |
| OpenRCT2 (PC, for Path B) | Rewrote the original program's procedures in C one by one, run inside the original program until all were done. Repo created 2014-04. | source |
| PC first-person shooter decomp (for Path B) | About 3 months, up to 16 agents on two top plans, 99% of functions present and 83% byte exact. It had the real compiler, which PS5 work does not. | source |
Projects reviewed (14)
RecompLauncher lists one PS5 game, so there are fewer than 10 games on RecompLauncher. All 3 review cards are here. 11 more were reviewed live: one RecompLauncher entry filed under another system, and 10 from outside it. Fewer than 10 real per-game PS5 projects exist anywhere. So five entries are shared tools, marked 'Many games'.
What each project teaches
AnyPS5
Many games (shared tool)
Started: 2026-08. First release: 2026-09. Code: C++.
AI: yes: AGENTS.md for agents; the pull request template asks if the change was AI-assisted Agent files: AGENTS.md.
Lesson: The base of every PC port here. Its strict rule is the key lesson: each function does its real job or throws. A general tool, listed so you can see where the work happens.
KytyPS5
Many games (shared tool)
Started: 2026-07. First release: 2026-10. Code: C++.
AI: none stated (README asks pull requests to say if AI helped)
Lesson: A translation layer, not a port. Its graphics part was fast enough for Astro Bot to borrow. Use it to test whether a game can run at all.
PortPS5
Many games (shared tool)
Started: 2026-09. First release: none. Code: C++.
AI: yes: AGENTS.md says it is the instruction file for AI coding agents, with Claude Code skills Agent files: AGENTS.md, CLAUDE.md, .claude/settings.json, .agents/skills/implement-prx-function/SKILL.md.
Lesson: A model for agent files on PS5: a legal boundary, a no-title-hacks rule, and one skill per kind of job. A general tool, not a game port.
Astro Bot native PC port (AstroHybrid)
Astro Bot
Started: 2026-11. First release: 2026-11. Code: C#, Python and PowerShell.
AI: none stated
Lesson: When the shared renderer was too slow, it swapped in another project's graphics part. All changes live as patch series over pinned commits, and every change is measured on the same scene.
prosper
Many games (shared tool)
Started: 2026-07. First release: 2026-07. Code: C++.
AI: yes: README says AI agents do the work while a human sets direction and reviews Agent files: CLAUDE.md, AGENTS.md, .claude/.
Lesson: The best public example of running agents on PS5 work for months. Its CLAUDE.md records each costly mistake as a rule. A general tool, not a game port.
SonsOfSparta-PS5 Native
God of War: Sons of Sparta
Started: 2026-10. First release: 2026-10. Code: C++.
AI: none stated
Lesson: From first commit to first release in two days. A launcher builds the game from the player's dump on first start, so no game files ship. Listed under 'other' on RecompLauncher; reviewed live for this guide.
Astro Bot Linux branch of AnyPS5
Astro Bot
Started: 2026-09. First release: none. Code: C++.
AI: none stated
Lesson: The author froze a reference build, then sent fixes upstream one topic at a time. Fixes in the shared tool help every later port. Reviewed live for this guide.
Ghost of Yotei on AnyPS5
Ghost of Yotei
Started: 2026-10. First release: none. Code: C++.
AI: none stated (its AGENTS.md comes from AnyPS5) Agent files: AGENTS.md.
Lesson: A big open-world game reached the world in 12 days but still runs near 1 FPS. The branch only moves forward when a build renders a 60-frame reference exactly. Reviewed live for this guide.
SoS-PS5-Android
God of War: Sons of Sparta
Started: 2026-10. First release: none. Code: Java.
AI: none stated
Lesson: Its first milestone only proves that box64 runs x86-64 test code. CI runs it on an ARM64 Linux machine and builds a test app for phones. Small proven steps come before the game. Reviewed live for this guide.
Dead Cells PS5 Native
Dead Cells: Return to Castlevania Edition
Started: 2026-10. First release: 2026-10. Code: C++.
AI: none stated
Lesson: A one-commit snapshot of AnyPS5 with a game target and a launcher. It shows how little per-game code you need once the shared tool runs your game.
NebulaAPU
Astro Bot
Started: 2026-10. First release: none. Code: C++.
AI: none stated
Lesson: A separate runtime for one game reached the title screen with no input. A shared tool went further for the same game. Reviewed live for this guide.
Mewgenics-android
Mewgenics
Started: 2026-10. First release: 2026-10. Code: C and C++.
AI: yes: the root AGENTS.md is written for an AI assistant (quota-limited, batched builds) Agent files: AGENTS.md.
Lesson: The nearest thing to Path B on PS5. It swaps single game routines for native code, guarded by code hashes. Its new LZ4 decoder is checked with test vectors.
PvZ: Replanted Android
Plants vs. Zombies: Replanted
Started: 2026-10. First release: 2026-10. Code: C.
AI: yes: 17 of 26 commits carry an AI co-author line
Lesson: Packaging of shared tools for one game, done in five days with AI help. It became the base for the same author's Mewgenics port. Reviewed live for this guide.
PS5 native macOS port framework
God of War: Sons of Sparta and others
Started: 2026-10. First release: none. Code: C++.
AI: none stated (AGENTS.md is written for coding agents and people) Agent files: AGENTS.md, CLAUDE.md.
Lesson: The only real static recompiler for PS5 games found. It moves a game to Apple silicon without a decomp. Tested on one Mac only.
What we could not check
- No command in this guide was run for this check, and no PS5 dump was used. Commands were checked against each tool's docs and source code only.
title_run.pybehaviour (its outcomes, and that it always exits 0) was read from its source code, not run.- 11 of the 14 projects have no review card. They were checked live against their READMEs and GitHub data on 2026-11-14 UTC.
- The Path B swap-and-compare harness is a plan. No PS5 project has built one.
--to-intelwas not tried on an Intel CPU.- Whether Dead Cells PS5 Native, SonsOfSparta-PS5 Native or the Astro Bot PC port used AI. None of them say.
- Which exact
AnyPS5commit Dead Cells PS5 Native is built from. - Whether
ghidra_ps5_elf_pluginbuilds against Ghidra 12.1.4 or 12.2. Its README names 12.1.3, andGhidraMCPneeds 12.1.4. No reviewed project used the plugin. - The
ps5rsinstall needs Rust 1.85 or newer. I did not check the Rust install steps. - That the PS5 graphics chip is RDNA 2. This comes from
AnyPS5's shader badges andprosper's notes, not from a Sony page.psdevwiki.comreturned an error page (HTTP 403). - The 60 FPS claim of Mewgenics-android was not measured by anyone else.
- The PvZ: Replanted Android AI count is from AI co-author lines in commits. The project does not state it in words.
- The no-AI time for Path A is a guess. No PS5 port predates AI agents.
- The
box64build steps were not read in full. Only the compile doc link and Mewgenics'-DANDROID=ONnote were checked. - The NebulaAPU status line is dated 2026-10-01. Whether work went on elsewhere is unknown.
- The share of
AnyPS5pull requests marked as AI-assisted could not be counted reliably with GitHub search.
Sources
- AnyPS5 README
- AnyPS5 build instructions
- AnyPS5 relinker usage
- AnyPS5 AGENTS.md
- AnyPS5 title_run.py
- Dead Cells PS5 Native README
- SonsOfSparta-PS5 Native README
- Astro Bot native PC port README
- Mewgenics-android README
- PvZ: Replanted Android README
- Ghost of Yotei on AnyPS5 README
- KytyPS5 README
- PortPS5 AGENTS.md
- prosper CLAUDE.md
- ps5-native-mac README
- ps5rs README
- ghidra_ps5_elf_plugin README
- Ghidra README
- WinLibs GCC 15.2.0 release
- Clang PS4 and PS5 toolchain header
- box64 README
- Tracy releases
- OpenRCT2 early fork README
- winget-pkgs manifests (CMake, Ninja, Git, Vulkan SDK)
- AnyPS5 controller timelines
- AnyPS5 Game bring-up discussions
- Ghidra 12.1.3 README (JDK 21)
- GhidraMCP README
- prosper releases
- Dead Cells packaging script
- PC first-person shooter decomp with agents