PlayStation decomp guide
Two free recompilers make PC builds quick; a byte-exact decomp still takes months.
What this system is like
The PlayStation is one of the busiest systems for this work. RecompLauncher lists 179 projects for 143 games. About three in four call themselves recompiles. A static recompile, or recomp, turns the game's machine code into C or C# by tool.
For Path A you use a shared recomp framework. PSXRecomp writes C and runs on Windows. RecompOne writes C# for .NET. PSXRecomp boots on the free OpenBIOS. RecompOne fakes the BIOS calls in its own code. Either way, you only need your own disc. PSXRecomp still calls itself a beta. Some games reach a first frame in days. A full, clean playthrough takes weeks.
Path B is a matching decomp. You write C that the old Sony compiler turns back into the exact same bytes. The tools are mature, but they live on Linux, so you use WSL. Two PlayStation games now have all their game code matched in C. They are Silent Hill and Yu-Gi-Oh! Forbidden Memories. Both still keep some data or library code as assembly.
Most games load extra code from the disc while they run. These files are called overlays. Each one must be found, split and checked on its own. Plan for this from day one.
Code comes in pieces
A game loads level code from the disc into the same RAM range. These files are overlays. Each overlay needs its own function list and its own check. Miss one and that level never works.
One SDK in every game
Nearly every game links Sony's Psy-Q libraries. Signature files can name those functions for you. Recomp runtimes and port layers like PsyCross replace them. Nobody should decompile them by hand.
The GTE math chip
The GTE has its own instructions for 3D math. Plain Ghidra reads them badly. Use the ghidra_psx_ldr loader. REDRIVER2 had to use IDA for its GTE code.
A CD, not a cartridge
Keep the raw 2352-byte sectors. A cooked .iso drops movie audio and video. Many projects check the disc by hash before they build.
Two frameworks, two AI rules
PSXRecomp welcomes AI-assisted work if it is reviewed and tested. RecompOne rejects AI-written pull requests. Its authors also refuse to help AI-made ports. Pick with that in mind.
Some discs kept debug symbols
A few discs shipped with .SYM debug files. REDRIVER2 got every name and type from two of them. Always look for these before you name anything.
Two paths
Get it running on PC
A framework reads the boot file and each overlay and writes C. It links that C to a runtime that acts like the console. The free OpenBIOS boots the game, so the game needs no BIOS dump. The Beetle debug oracle is another matter: the framework's own notes start it with a retail BIOS file. You fix crashes in the settings, seeds or framework, never in the made code. This guide uses PSXRecomp because it runs on Windows and welcomes AI-assisted work.
Full decomp for porting
You split the game with splat and rebuild it at once from its own assembly. Then you swap assembly for C, one function at a time. Each step must keep the whole file byte for byte the same as the disc. When it is done, you link the C to a Psy-Q stand-in like PsyCross and build for PC. Silent Hill's PC port did this with PsyCross. Yu-Gi-Oh! Forbidden Memories matched its game code the same way, then wrote its own PC layer.
Path A: Get it running on PC
A framework reads the boot file and each overlay and writes C. It links that C to a runtime that acts like the console. The free OpenBIOS boots the game, so the game needs no BIOS dump. The Beetle debug oracle is another matter: the framework's own notes start it with a retail BIOS file. You fix crashes in the settings, seeds or framework, never in the made code. This guide uses PSXRecomp because it runs on Windows and welcomes AI-assisted work.
Method: static recompilation
Pick this when you want to play on PC soon. It is the right first goal for almost every PlayStation game.
Scope the game and record the disc
Pick one game and one disc version. Record its hashes so every later check uses the same disc. Look for work that already exists. A finished decomp or a recomp can save you months.
- Dump your own disc as
.bin/.cuewith raw 2352-byte sectors. Keep it outside the repo. - Read the serial from
SYSTEM.CNF, such asSLUS-00707. - Record size, MD5, SHA-1 and SHA-256 in
DISC.md, like Ape Escape did. - Search the RecompLauncher list, GitHub and the
PSXRecompgames table for your game. - Look for
.SYMdebug files on every regional disc you own. - Write
SCOPE.md: goal, serial, hashes, technique, first milestone, its check, and what is out of scope.
Prompt for ClaudeGOAL: Draft SCOPE.md for a static recomp of <GAME> (<SERIAL>) with PSXRecomp. READ FIRST: DISC.md, https://github.com/RetroPortingToolKit/psxrecomp (Games table), the RecompLauncher entry for <GAME> if any. FENCE: Create or edit only SCOPE.md and notes/prior-work.md. Do not touch any disc file. Do not install anything. CHECK: Run `Get-FileHash -Algorithm SHA256 <BIN PATH>`. Done means the hash it prints is the one in SCOPE.md and DISC.md. SCOPE.md also names the serial, the technique, milestone 1 and its exact check command. Every prior-work claim has a URL. BUDGET: Stop after 30 minutes of searching. IF BLOCKED: Stop. Tell me what you could not find and what you need from me. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The SCOPE.md text. 3) Prior work found, with URLs. 4) Open questions for me.
Done whenGet-FileHash -Algorithm SHA256on your.binprints the same hash thatSCOPE.mdrecords, andSCOPE.mdnames one milestone check. You approved it.Proof this works: Ape Escape Recompiled - Enhanced Edition (DISC.md with Redump hashes), REDRIVER2 (names from .SYM files), https://github.com/RetroPortingToolKit/psxrecomp
- Dump your own disc as
Set up PSXRecomp on Windows
You install the tools and let the setup script build a first game project. The script probes the disc, writes
game.tomland a seed list, and builds. A seed is a known function start address.- In PowerShell run
winget install Git.Git Python.Python.3.12. - Download
cmake-clang-v1-windows-x64.zipfrom the RetroPorting-Toolchains releases. Extract it toC:\RecompDev\Toolchain. - Set
$env:PSXRECOMP_TOOLCHAIN_DIRto that folder and add itsbinto$env:Path. - Run
git clone --recurse-submodules https://github.com/RetroPortingToolKit/psxrecomp.git. - From
psxrecomp, runpowershell -File tools\new_project_layout\setup_project.ps1 -Disc '<path>\game.cue' -Dir C:\RecompDev -Name <Name> -GithubRepo <folder> -Players 1 -Yes -EnableRecompUi -NoNetplay -NoFetchBoxart -NoGithub -Generate -EnableBuild. - Write
tools/check.ps1. It rebuilds withpython psxrecomp/psxrecomp_cli.py rebuild --config game.toml --project-root . --build-dir build-release --no-pgo. - Then it sets
$env:PSX_DEBUG_SERVER = '1'and starts the game file frombuild-release. The build log names that file. - It polls
python psxrecomp/tools/debug_client.py pinguntil the frame count passes a target, such as 600. It exits 1 if that takes more than 60 seconds. Step 4 adds the title check to it. - Write
CLAUDE.md,PROGRESS.mdandnotes/parking-lot.md.
Prompt for ClaudeGOAL: Make one command, tools/check.ps1, that rebuilds the <GAME> project and prints PASS or FAIL. READ FIRST: SCOPE.md, psxrecomp/docs/GETTING_STARTED.md, psxrecomp/docs/GAME_PROJECT_SETUP.md, psxrecomp/docs/TCP_COMMANDS.md, game.toml. FENCE: Edit only tools/, CLAUDE.md, PROGRESS.md and .gitignore. Never edit generated/, psxrecomp/ or recomp-ui/. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run `powershell -File tools/check.ps1`. Done means it rebuilds, boots the game, sees the frame count pass <FRAMES> through `debug_client.py ping`, and exits 0. Then set the frame target to 1000000000 and run it again. It must exit 1 after 60 seconds. Put the target back after. BUDGET: Stop after 3 failed tries, or 45 minutes with no progress. IF BLOCKED: Stop. Do not work around it. Paste the exact error and the step it came from. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste both check runs. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whentools/check.ps1exits 0 on a clean checkout once the frame count passes the target. It exits 1 when the target is out of reach.Proof this works: https://github.com/RetroPortingToolKit/psxrecomp/blob/master/docs/GETTING_STARTED.md, PaRappa The Rapper Recompiled, ValkyrieRecomp
- In PowerShell run
Map the code and the overlays
Claude studies the boot file and finds every overlay. It marks Psy-Q library code so nobody spends time on it. The result is a function ledger and an overlay list in files.
- Install JDK 21 (64-bit), then Ghidra 12.1.4. Add the
ghidra_psx_ldrzip built for 12.1.4 (release 2026.10.03). Load the boot EXE with the PSX loader. - Apply the Psy-Q signatures the loader offers. Mark matched library functions as
sdkin the ledger. - Optional: install
GhidraMCPand runclaude mcp add ghidra -- uvx --from git+https://github.com/LaurieWired/GhidraMCP ghidra-mcp --ghidra-server http://127.0.0.1:8080/. - Write
notes/functions.csvwith address, size, name, kind and status columns. - Find the loader code and write
notes/overlays.md: file name, load address and how it was proven. - Add confirmed function starts to
seeds/ghidra_funcs.txt.
Prompt for ClaudeGOAL: Write notes/functions.csv and notes/overlays.md for <GAME> (<SERIAL>). READ FIRST: SCOPE.md, game.toml, seeds/ghidra_funcs.txt, the Ghidra project <PROJECT> (use the ghidra MCP server). FENCE: Edit only notes/ and seeds/ghidra_funcs.txt. Ghidra is read-only except for names and comments. Never edit generated/ or psxrecomp/. CHECK: Every overlay row gives the loader function address that proves it. Every seed you add is a real function start in Ghidra. Run tools/check.ps1. Done means it still passes. BUDGET: Stop after 2 hours, or after 3 overlays you cannot prove. IF BLOCKED: Stop. List what you could not prove and why. Do not guess an address. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Counts: functions, sdk functions, overlays. 3) Check output. 4) Files changed. 5) Unproven items.
Done whennotes/functions.csvlists every known function with a status, and each overlay innotes/overlays.mdcites its loader address.Proof this works: Ape Escape Recompiled - Enhanced Edition (docs/AOT_OVERLAYS.md, Ghidra MCP config), MediEvil Recomp (one entry and function map per overlay), https://github.com/lab313ru/ghidra_psx_ldr
- Install JDK 21 (64-bit), then Ghidra 12.1.4. Add the
First light: boot to the title screen
You build and run the game until it shows its first frames. A script proves it with a screenshot, so no person has to watch. This becomes your golden run.
- In PowerShell, set
$env:PSX_DEBUG_SERVER = '1', then start the game. The debug server listens on127.0.0.1port 4370. - Run
python psxrecomp/tools/debug_client.py pingto see the frame count rise. - Send button presses with the
set_inputcommand, thenclear_input, to get past the logos. - Run
python psxrecomp/tools/debug_client.py screenshot path=<abs path>\title.png. - Look at the first good
title.pngyourself. Save it astests/golden/title.png. That is your approved picture. - Make
tools/golden_title.pycompare each newtitle.pngwith the approved one. Fail if too many pixels differ, or if the title is not reached in time. - Add
tools/golden_title.pytotools/check.ps1.
Prompt for ClaudeGOAL: Boot <GAME> to its title screen and make tools/golden_title.py prove it with no person watching. READ FIRST: SCOPE.md, psxrecomp/docs/TCP_COMMANDS.md, psxrecomp/docs/EXECUTION_MODEL.md, notes/overlays.md. FENCE: Edit only game.toml, seeds/ghidra_funcs.txt and tools/. Never edit generated/ by hand: change the inputs and regenerate. Do not stub any function. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run `python tools/golden_title.py`. Done means it saves title.png within <SECONDS> seconds, the picture matches tests/golden/title.png within <TOLERANCE>, and it exits 0. Then run tools/check.ps1. It must pass. Never replace tests/golden/title.png yourself. BUDGET: Stop after 4 failed builds, or 60 minutes with no new frame reached. IF BLOCKED: Stop. Report the last frame number, the PC address and the missing call. Do not work around it. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the golden run output and attach title.png. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenpython tools/golden_title.pyexits 0 inside the time limit, and itstitle.pngmatches your approvedtests/golden/title.pngwithin the set tolerance.Proof this works: Crash Bandicoot PC (bring-up order in docs/CRASH_BANDICOOT_RECOMP.md), https://github.com/RetroPortingToolKit/psxrecomp/blob/master/docs/TCP_COMMANDS.md
- In PowerShell, set
Grind: fix one break at a time
Now Claude fixes one crash, freeze or missing function per session. It compares your build with the Beetle PSX oracle. An oracle is a trusted reference that shows the right answer. Every fix goes in the seeds, the config or the framework.
- Write the route list in
PROGRESS.md: title, menu, first level, each later level. - Build the
psx-beetleoracle from the framework source. Its steps are in section 16 ofpsxrecomp/CLAUDE.md. It listens on port 4380. - For a freeze, bring both programs to the same point. Run
python psxrecomp/tools/debug_client.py get_registers, then the same with--port 4380. Compare the two. - Do not use
debug_client.py comparefor this. It checks port 4371, the old DuckStation oracle, not Beetle. - Add missed function starts to the seeds. Then run
python psxrecomp/psxrecomp_cli.py generate --config game.toml --project-root . --disc <path>\game.cue. - Log each case in
ISSUES.md: addresses, what was tried, what failed, where it stopped. - Fix the bring-up classes in the order Crash Bandicoot PC used: BIOS and pad, CD, GPU present, VSync, memcpy, jump tables.
Prompt for ClaudeGOAL: Fix one break: <SYMPTOM> at <ROUTE POINT> in <GAME>. READ FIRST: CLAUDE.md, ISSUES.md, notes/overlays.md, psxrecomp/docs/TCP_COMMANDS.md. FENCE: Edit only game.toml, seeds/ghidra_funcs.txt, ISSUES.md and tools/. Never edit generated/. Never deliver a fake event, add a stub or return a made-up value. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run tools/check.ps1 and `python tools/golden_route.py <ROUTE POINT>`. Done means both pass, and the old golden runs still pass. BUDGET: Stop after 3 failed fixes, or 60 minutes with no new evidence. IF BLOCKED: Stop. Show the first divergence from the Beetle oracle on port 4380 (address, register, frame). Say what you need. Do not guess. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The proven cause, with the oracle output. 3) Check output. 4) Files changed. 5) New parking-lot items.
Done whenThe new route point passes its golden run, and every older golden run still passes.Proof this works: Crash Bandicoot PC (VSync and root-counter fixes), Ape Escape Recompiled - Enhanced Edition (ISSUES.md log), https://github.com/RetroPortingToolKit/psxrecomp/blob/master/CLAUDE.md, https://github.com/RetroPortingToolKit/psxrecomp/blob/master/tools/debug_client.py (compare uses ports 4370 and 4371)
- Write the route list in
Cover every overlay and disc
Reaching the title screen does not prove later levels work. Each overlay must be compiled ahead of time or caught and cached. You walk the whole game and close each gap.
- Read
psxrecomp/docs/AOT_SHARDING.mdand list each overlay's bytes and loader innotes/overlays.md. - Add one golden run per level or area. Use saved inputs, not live play.
- For a multi-disc game, register the set with
python psxrecomp/tools/new_project_layout/update_disc_set.py --game-toml game.toml <disc1.cue> <disc2.cue>. - Mark each overlay in the ledger as
native,cachedorexcluded, with the reason.
Prompt for ClaudeGOAL: Make overlay <OVERLAY FILE> of <GAME> run as native code, proven by a golden run. READ FIRST: notes/overlays.md, psxrecomp/docs/AOT_SHARDING.md, psxrecomp/docs/EXECUTION_MODEL.md, ISSUES.md. FENCE: Edit only game.toml, seeds/, notes/ and tools/. Never edit generated/ or psxrecomp/. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run tools/check.ps1 and `python tools/golden_route.py <AREA>`. Done means both pass and the ledger row reads native or cached. BUDGET: Stop after 3 failed tries on this overlay. IF BLOCKED: Stop. Report the overlay bytes, loader address and what the runtime said. Record it as excluded with the reason. Do not hide it. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Ledger row before and after. 4) Files changed.
Done whenEvery overlay row innotes/overlays.mdis native, cached or excluded with a reason, and each area has a passing golden run.Proof this works: Ape Escape Recompiled - Enhanced Edition (49 AOT overlay images, two documented exclusions), Symphony Recomp (127 overlay function maps), https://github.com/RetroPortingToolKit/psxrecomp
- Read
Harden: a regression set and clean builds
You make old fixes stay fixed. Saved inputs replay the route on every change. A clean checkout must build and pass with one command. Then you check the release zip itself.
- Run every golden run from
tools/check.ps1. Fail on any one. - Pin the framework submodules and record the commits in
framework_pins.txt. - Build from a fresh clone in a new folder. Run the full check there.
- Open the release zip outside any git folder and launch it. Check that it holds no
.bin,.cueor BIOS file.
Prompt for ClaudeGOAL: Make tools/check.ps1 run the full regression set for <GAME> from a fresh clone. READ FIRST: PROGRESS.md, tools/, framework_pins.txt, the PaRappa and Ape Escape release checks as examples. FENCE: Edit only tools/, PROGRESS.md and framework_pins.txt. Do not change any expected screenshot or hash. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: In a fresh clone, run `powershell -File tools/check.ps1`. Done means every golden run passes and the zip check finds no disc or BIOS file. BUDGET: Stop after 3 failed tries. IF BLOCKED: Stop. Say which run failed and paste its output. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Full check output. 3) Files changed. 4) What is left.
Done whenA fresh clone passes the full regression set with one command, and the zip check passes.Proof this works: PaRappa The Rapper Recompiled (framework_pins.txt, CI zip checks), Ape Escape Recompiled - Enhanced Edition (extract the zip outside git and launch it), ValkyrieRecomp (CI tests the finished artifact)
- Run every golden run from
Add enhancements one at a time
Only after the stock game works do you add widescreen, higher resolution or mods. Each feature gets its own check. With the feature off, the game must act exactly as before.
- Turn on the
[widescreen]block ingame.tomland find the game's cull sites. - Compare the same frame with the feature on and off. Do not compare two separate runs.
- Keep mods as
.psxmodpackages with byte guards, not as edits to made code. - Add one golden run per feature to the regression set.
Prompt for ClaudeGOAL: Add <FEATURE> to <GAME> without changing the stock game. READ FIRST: psxrecomp/docs/WIDESCREEN.md, psxrecomp/psxmod.md, PROGRESS.md. FENCE: Edit only game.toml, mods/ and tools/. Never edit generated/. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run tools/check.ps1 with the feature off: all golden runs must pass unchanged. Then run tools/golden_feature.py <FEATURE>. Done means both pass. BUDGET: Stop after 3 failed tries. IF BLOCKED: Stop. Show the frame where it breaks and say why. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Both check outputs. 3) Files changed. 4) Known limits.
Done whenWith the feature off, every golden run passes unchanged; with it on, its own golden run passes.Proof this works: Crash Bandicoot PC (WideCheck compares the same ordering table), https://github.com/RetroPortingToolKit/psxrecomp/blob/master/docs/WIDESCREEN.md
- Turn on the
Path B: Full decomp for porting
You split the game with splat and rebuild it at once from its own assembly. Then you swap assembly for C, one function at a time. Each step must keep the whole file byte for byte the same as the disc. When it is done, you link the C to a Psy-Q stand-in like PsyCross and build for PC. Silent Hill's PC port did this with PsyCross. Yu-Gi-Oh! Forbidden Memories matched its game code the same way, then wrote its own PC layer.
Method: matching decompilation
Pick this when you want readable source to port, mod or study. It takes many months. Do Path A first if you just want to play.
Scope: prior work and the compiler
Check if someone already started a decomp of your game. Look for debug symbols. Note which Psy-Q compiler the game may use. This decides months of work.
- Search GitHub and decomp.dev for
<game> decomp. Known PS1 decomps includesotn-decomp,silent-hill-decomp,memories-decompandCTR-ModSDK. - Look for
.SYMfiles on each regional disc. Dump them withsymdumpif found. - Note the Psy-Q library version strings in the boot EXE.
- Write
SCOPE.mdwith the EXE hash, the plan for each overlay and what is out of scope.
Prompt for ClaudeGOAL: Draft SCOPE.md for a matching decomp of <GAME> (<SERIAL>). READ FIRST: DISC.md, https://github.com/Xeeynamo/sotn-decomp, https://github.com/shdecompilations/silent-hill-decomp, https://decomp.dev. FENCE: Create or edit only SCOPE.md and notes/prior-work.md. Do not install anything. Do not touch disc files. CHECK: Run `sha256sum <BOOT EXE>` in WSL. Done means it prints the SHA-256 written in SCOPE.md. SCOPE.md also gives the overlay count, a compiler guess with its evidence, and milestone 1 with its check. Every claim has a URL or an address. BUDGET: Stop after 45 minutes. IF BLOCKED: Stop. Say what you could not find and what you need. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) SCOPE.md text. 3) Prior work with URLs. 4) Questions for me.
Done whensha256sumon the boot EXE prints the hash inSCOPE.md, which also gives the overlay count and a compiler guess with evidence. You approved it.Proof this works: REDRIVER2 (debug symbols from Italian and Spanish discs), Forbidden Memories Recompiled (compiler fingerprinting), https://github.com/stohrendorf/symdump
- Search GitHub and decomp.dev for
Set up WSL and the one check
You build the matching toolchain in WSL. You extract the boot EXE and overlays from your disc. The one check rebuilds them and compares SHA-256 hashes.
- In WSL Ubuntu 24.04 run
sudo apt install git make python3-venv binutils-mipsel-linux-gnu.maspsxcallsmipsel-linux-gnu-asby default, andsotn-decompinstalls this package. - Make a venv and run
python3 -m pip install -U splat64[mips]. - Download a
gcc-2.8.1-psxorgcc-2.7.2-psxtarball from theold-gccreleases intotools/. - Add
maspsxas a submodule:git submodule add https://github.com/mkst/maspsx tools/maspsx. - Run
dumpsxisoon your.cueto get the boot EXE and overlay files. Keep them in an ignored folder. - Write
checksum.shaand amake checktarget that runssha256sum -c checksum.sha.
Prompt for ClaudeGOAL: Make `make check` rebuild <GAME>'s boot EXE and compare its SHA-256 with checksum.sha. READ FIRST: SCOPE.md, https://github.com/ethteck/splat, https://github.com/mkst/maspsx, https://github.com/decompals/old-gcc. FENCE: Edit only Makefile, tools/, config/ and .gitignore. Never edit checksum.sha after I approve it. Never commit game files. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run `make clean && make check` in WSL. Done means it prints OK for the boot EXE. Then, on a throwaway git branch, change one instruction in one asm file and run it again. It must print FAILED. Delete the branch after. BUDGET: Stop after 3 failed tries, or 60 minutes with no progress. IF BLOCKED: Stop. Paste the exact error. Do not edit the checksum to get a pass. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Both check runs. 3) Files changed. 4) What is left.
Done whenmake clean && make checkprints OK in WSL, and fails when one byte is wrong.Proof this works: Silent Hill Decomp (sha256sum against configs/<version>/checksum.sha), Forbidden Memories Recompiled (make match accepts only the retail SHA-256), https://github.com/Xeeynamo/sotn-decomp/wiki/Build
- In WSL Ubuntu 24.04 run
Split the EXE and each overlay
splatcuts the EXE into code and data files. At first every function is plain assembly. The build must still match before you write any C.- Run
python3 -m splat create_config <BOOT EXE>to get a first yaml. Move it toconfig/<serial>.yaml. - Check the yaml's load address against the EXE header. Fix it if needed.
- Run
python3 -m splat split config/<serial>.yaml. - Write one yaml and one checksum per overlay, with its own load address.
- Run
make check. Fix the yaml until every file matches.
Prompt for ClaudeGOAL: Split <GAME>'s boot EXE with splat so the all-assembly build matches. READ FIRST: config/<SERIAL>.yaml, notes/functions.csv, https://github.com/ethteck/splat/wiki. FENCE: Edit only config/ and symbol_addrs.txt. Never edit asm/ files by hand: regenerate them with splat. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run `make clean && make check`. Done means it prints OK. BUDGET: Stop after 4 failed tries. IF BLOCKED: Stop. Paste the first mismatching offset and the yaml segment around it. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Segment count. 4) Files changed.
Done whenThe all-assembly build of the EXE matches, and each split overlay passes its own checksum.Proof this works: Silent Hill Decomp (splat yaml per version, overlays per map), Forbidden Memories Recompiled (config/slus_01411/split.yaml), https://github.com/ethteck/splat/blob/main/src/splat/scripts/create_config.py (reads a PS-X EXE)
- Run
Map functions and mark the SDK
Claude fills the function ledger. Psy-Q library functions get marked so they stay as assembly or come from a shared decomp. Game functions get a size and an easy, medium or hard tag.
- Load the EXE in Ghidra with
ghidra_psx_ldrand apply the Psy-Q signatures. - Mark matched SDK functions as
sdkinnotes/functions.csv. - Keep names as
func_<address>until a name has evidence. - Sort game functions by size. Small leaf functions go first.
Prompt for ClaudeGOAL: Fill notes/functions.csv for <GAME> with address, size, name, kind and status. READ FIRST: asm/ (read-only), symbol_addrs.txt, the Ghidra project <PROJECT> through the ghidra MCP server. FENCE: Edit only notes/functions.csv and symbol_addrs.txt. Never edit asm/ or src/. Do not rename a symbol without evidence written in the notes column. CHECK: Run `make clean && make check` and `python3 tools/ledger_check.py`. Done means both pass and every row has a size and a status. BUDGET: Stop after 2 hours. IF BLOCKED: Stop. List the rows you could not fill and why. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Counts by kind and status. 3) Check output. 4) Files changed.
Done whenEvery function in the ledger has a size, a kind (gameorsdk) and a status, andmake checkstill passes.Proof this works: PsyDoom (PSXObjSigGen and PSXExeSigMatcher to name SDK code first), Forbidden Memories Recompiled (functions.csv as the one inventory), https://github.com/lab313ru/ghidra_psx_ldr
- Load the EXE in Ghidra with
First light: find the compiler, match one function
You prove the toolchain by matching one small function. This settles the GCC version, the ASPSX version and the flags. Write the winning setup down as a named profile.
- Pick a small leaf function with no calls.
- Get a first draft with
python3 m2c.py -t mipsel-gcc-c -f <func> asm/<file>.s. - Try
gcc-2.8.1-psxandgcc-2.7.2-psxwithmaspsx --aspsx-version=<ver>. Change one thing per try. - Use
objdiffor decomp.me with apsyq4.xpreset to compare. - Save the winner in
config/compiler_profiles.json. Never put flags in one-off commands.
Prompt for ClaudeGOAL: Match one small function, <FUNC>, and record the compiler profile that did it. READ FIRST: asm/<FILE>.s, notes/functions.csv, https://github.com/mkst/maspsx (ASPSX version table), config/compiler_profiles.json. FENCE: Edit only src/<FILE>.c, config/compiler_profiles.json and notes/attempts.csv. Never edit asm/, checksum.sha or the Makefile check. No inline asm, no register pins, no embedded bytes. CHECK: Run `make clean && make check`. Done means OK with <FUNC> built from C, and objdiff shows 100% for it. BUDGET: Stop after 12 attempts. Log each one in notes/attempts.csv with the profile and the first difference. IF BLOCKED: Stop. Report the best score, the profiles tried and the first differing instruction. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) The profile. 4) Files changed.
Done whenOne function is built from C,make checkstill prints OK, and its compiler profile is saved.Proof this works: Forbidden Memories Recompiled (GCC 2.8.1 + ASPSX 2.81 with -O2 -G8 -mips1), Silent Hill Decomp (gcc 2.8.1-psx and 2.7.2-cdk with maspsx 2.67 and 2.77), https://decomp.me
Grind: match functions in parallel
Now workers match one function each. A script picks the next one, not the model. Every try is logged, so no session repeats a dead end. The full build hash is the only verdict.
- Hand out functions by size, smallest first. Later, pick ones that look like matched ones.
- Give each worker its own worktree with
claude -w <name>. - Run
decomp-permuteronly on near misses, and have Claude explain the result. - Commit each verified match on its own. Reset the worktree after a failure.
- Merge, then run
make clean && make checkon the merged branch.
Prompt for ClaudeGOAL: Make <FUNC> in src/<FILE>.c match 100%. READ FIRST: asm/<FILE>/<FUNC>.s, notes/attempts.csv rows for <FUNC>, config/compiler_profiles.json, up to 3 matched functions that look like it. FENCE: Edit only src/<FILE>.c and notes/attempts.csv. Never edit asm/, include/ shared headers, checksum.sha or the Makefile. No inline asm, no register pins, no embedded bytes, no goto added only to match. Use only named compiler profiles. CHECK: Run `make clean && make check`. Done means OK, and objdiff shows 100% for <FUNC>. BUDGET: Stop after 10 attempts, or 4 attempts with no gain. Log every attempt with its mismatch reason. IF BLOCKED: Stop. Report the best score, the first differing instructions and a cause class. Leave the function as assembly. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Diff summary. 4) Files changed.
Done whenThe ledger shows the target share of game functions matched, andmake clean && make checkprints OK on the merged branch.Proof this works: Forbidden Memories Recompiled (attempt ledger, make candidates, make match-incremental), Silent Hill Decomp (objdiff, permuter, m2c), https://github.com/Ketbome/decomp-skills
Finish overlays and data
Each overlay gets the same loop. Then raw data becomes typed C structs. Names replace
func_addresses when there is evidence. A runtime trace answers what static reading cannot.- Run the unit loop per overlay, each with its own checksum.
- Move data tables into C structs and keep
make checkgreen. - For a runtime question, write a short PCSX-Redux Lua script. Run it with
pcsx-redux -interpreter -debugger -iso <cue> -dofile <script>.lua. - Paste the result into
notes/with what you saw. Then delete the script.
Prompt for ClaudeGOAL: Bring overlay <OVERLAY> of <GAME> to 100% matched code. READ FIRST: config/<OVERLAY>.yaml, notes/functions.csv rows for <OVERLAY>, notes/attempts.csv. FENCE: Edit only src/<OVERLAY>/ and notes/. Never edit asm/, checksum files or the Makefile. Never touch other overlays. CHECK: Run `make clean && make check`. Done means OK for the EXE and every overlay. BUDGET: Stop after 3 hours, or when the remaining functions all have 10 failed attempts. IF BLOCKED: Stop. List the functions left and their best scores. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Matched count before and after. 4) Files changed.
Done whenEvery overlay's checksum passes with all game code in C, except functions listed as assembly with evidence.Proof this works: Forbidden Memories Recompiled (all five overlays 100%, 61 functions kept as handwritten assembly with evidence), Forbidden Memories Recompiled (PCSX-Redux Lua traces in tools/trace), https://pcsx-redux.consoledev.net/cli_flags/
Harden: verify and hunt for cheats
A fresh agent re-checks the claimed work. It looks for tricks that pass the hash but are not real C. A clean clone must pass the full check.
- In a fresh clone run
make clean && make check. - Search
src/forasm(,__asm__, register pins and straygoto. - Confirm
checksum.shaand the Makefile have not changed since you approved them. - Add a Claude Code hook that blocks edits to
asm/,checksum.shaandMakefile.
Prompt for ClaudeGOAL: Verify the last <N> claimed matches in <GAME> are real. READ FIRST: git log for the last <N> commits, notes/attempts.csv, CLAUDE.md banned list. FENCE: Read-only. Do not edit any file except notes/verify-<DATE>.md. CHECK: In a fresh clone run `make clean && make check`. Done means OK, plus a written verdict per commit. BUDGET: Stop after 90 minutes. IF BLOCKED: Stop. Say what you could not check. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Per commit: real, suspect or cheat, with the line. 4) Any change to checksum.sha, the Makefile or asm/.
Done whenA fresh clone prints OK, and the verify note finds no inline asm, no edited checksum and no edits outside the fence.Proof this works: Forbidden Memories Recompiled (check-matching-source-contracts and other policy checks), https://github.com/cdlewis/snowboardkids2-decomp/tree/main/.claude/hooks
- In a fresh clone run
Port: link the C to a Psy-Q stand-in
You build the matched C for PC against a layer that acts like Psy-Q. Build 32-bit first, because the code stores pointers in 32-bit fields. Mark every port change so the matching build stays the same.
- Add
PsyCrossas a submodule.PsyZis a newer choice with 64-bit support. - Wrap each PC change in one
#ifdef, likeSH_PC_PORTin Silent Hill. - Build with
-m32first. Add_Static_assertchecks for struct layouts. - Build overlays as DLLs, or compile all in and track which one is loaded.
- Keep
make checkpassing for the console build after every port change. - Add golden runs and input replays for the PC build.
Prompt for ClaudeGOAL: Get <GAME>'s matched C to boot to the title screen on PC through PsyCross. READ FIRST: SCOPE.md, https://github.com/OpenDriver2/PsyCross, the Silent Hill port docs on struct offsets, notes/overlays.md. FENCE: Edit only port/ and code inside #ifdef <GAME>_PC_PORT blocks. Never change the console build output. Do not add features, refactors, tools or cleanups I did not ask for. CHECK: Run `make clean && make check` (console build still OK) and `port/golden_title.sh` (PC build reaches the title in <SECONDS> seconds). BUDGET: Stop after 4 failed builds, or 90 minutes with no new frame. IF BLOCKED: Stop. Report the crash address, the struct or call involved, and what you need. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Both check outputs. 3) Every port change listed in notes/port-fixes.md. 4) Files changed.
Done whenThe console build still prints OK, and the PC build passes the same golden runs to the title and first level.Proof this works: Silent Hill Decomp (PsyCross, SH_PC_PORT, map DLLs, _Static_assert), CTR Native (32-bit first, retail-shaped GPU packets), https://github.com/Xeeynamo/psyz
- Add
Tools you need
| Tool | What it does | How to get it | How we know |
|---|---|---|---|
| PSXRecomp Required both | A PlayStation recomp framework. It turns MIPS code into C and links it to a shared runtime. It ships the free OpenBIOS and a debug server. Path A | `winget install Git.Git Python.Python.3.12`, extract `cmake-clang-v1-windows-x64.zip`, then `git clone --recurse-submodules https://github.com/RetroPortingToolKit/psxrecomp.git` | README, CLAUDE.md, docs/GETTING_STARTED.md, docs/GAME_PROJECT_SETUP.md and docs/TCP_COMMANDS.md (read 2026-11-13). mstan/psxrecomp now redirects here. Used by: ValkyrieRecomp, PaRappa, Ape Escape, Parasite Eve HD. |
| RecompOne Helpful both | A second recomp framework. It turns MIPS code into C# and runs it on .NET. Its authors reject AI-written pull requests. Path A | Install the .NET 10 SDK, add RecompOne as a submodule, then `dotnet build RecompOne.Recompiler -c Release` | README and wiki page 'How to recompile a game?' (read 2026-11-13; the wiki marks itself outdated). Used by: Symphony Recomp, MediEvil Recomp, Crash Bandicoot PC. |
| Ghidra Required both | A free disassembler and decompiler. It shows the game's code as assembly and rough C. Path A Path B | Install JDK 21 (64-bit), download `ghidra_12.1.4_PUBLIC_20260921.zip` from the releases page, extract it, run `ghidraRun.bat`. Use 12.1.4, not 12.2, because GhidraMCP 2.0 needs it. Ghidra 12.2 asks for JDK 25. | README at tag Ghidra_12.1.4_build (JDK 21) and current README (JDK 25), releases list (latest Ghidra_12.2_build), read 2026-11-13. Used by: REDRIVER2, ValkyrieRecomp, Ape Escape, GT2 PC, Silent Hill. |
| ghidra_psx_ldr Required both | A Ghidra add-on that loads PS-X EXE files. It applies Psy-Q signatures and decodes GTE instructions. Path A Path B | Download `ghidra_12.1.4_PUBLIC_20261004_ghidra_psx_ldr.zip` from release 2026.10.03, then in Ghidra use File, Install Extensions | README and releases (read 2026-11-13). Release 2026.10.03 has a 12.1.4 build. Used by: Silent Hill Decomp. |
| GhidraMCP Helpful both | An MCP server that lets Claude ask Ghidra for code, names and references. MCP is a plug-in link between Claude and a tool. Path A Path B | Install the release zip in Ghidra 12.1.4, install `uv`, then `claude mcp add ghidra -- uvx --from git+https://github.com/LaurieWired/GhidraMCP ghidra-mcp --ghidra-server http://127.0.0.1:8080/` | README, release 2.0 (read 2026-11-13). Ghidra MCP named in the PSXRecomp rules and in Ape Escape's .mcp.json.example. |
| PCSX-Redux Helpful both | A PlayStation emulator built for debugging. It has Lua scripts, a GDB server and breakpoints. OpenBIOS comes from this project. Path A Path B | Download the Windows x64 build from the README's download table. Linux AppImages are listed there too. | README and https://pcsx-redux.consoledev.net/cli_flags/ (read 2026-11-13). Used by: Forbidden Memories Recompiled (Lua traces). |
| mkpsxiso / dumpsxiso Required both | dumpsxiso pulls every file off a PlayStation disc image and writes an XML layout. mkpsxiso builds a disc back.Path B | Download the release zip for your system from the releases page | README, release v2.30 (read 2026-11-13). Used by: Silent Hill Decomp, REDRIVER2. |
| splat Required WSL/Linux | Splits a PS-X EXE or overlay into assembly and data files. It writes the build pieces for a matching decomp. Path B | In WSL: `python3 -m pip install -U splat64[mips]` | README (read 2026-11-13): supports N64, PSX, PS2 and PSP. Used by: Silent Hill Decomp, Forbidden Memories Recompiled, sotn-decomp. |
| old-gcc Required WSL/Linux | Prebuilt old GCC versions, including PlayStation builds like gcc-2.8.1-psx and gcc-2.7.2-cdk.Path B | In WSL, download the `-psx` tarball you need from the releases page and extract it into `tools/` | README and release 0.17 asset list (read 2026-11-13). Used by: Forbidden Memories Recompiled. |
| maspsx Required WSL/Linux | Stands in for Sony's ASPSX assembler. It reshapes GCC output so GNU as builds the same object.Path B | `git submodule add https://github.com/mkst/maspsx tools/maspsx`; needs `binutils-mipsel-linux-gnu`, since it calls `mipsel-linux-gnu-as` unless you pass `--gnu-as-path` | README with the ASPSX 1.05 to 2.86 table (read 2026-11-13). Used by: Silent Hill Decomp, Forbidden Memories Recompiled, sotn-decomp. |
| objdiff Required both | Shows how close your compiled function is to the original, instruction by instruction. It supports PS1 MIPS. Path B | Download `objdiff-windows-x86_64.exe`, or the CLI from the releases page | README, release v3.8.3 (read 2026-11-13). Used by: Silent Hill Decomp. |
| m2c Helpful WSL/Linux | Turns one function's assembly into a rough C draft. You then fix the draft until it matches. Path B | In WSL: `git clone https://github.com/matt-kempster/m2c`, then run `python3 m2c.py -t mipsel-gcc-c` | README (read 2026-11-13). Used by: Silent Hill Decomp, Forbidden Memories Recompiled, sotn-decomp. |
| decomp-permuter Helpful WSL/Linux | Tries many small random changes to a C function to find a closer match. Use it only on near misses. Path B | In WSL: clone it, then `python3 -m pip install pynacl toml Levenshtein` | README (read 2026-11-13): permuter@home does not work on native Windows, WSL does. Used by: Silent Hill Decomp, Forbidden Memories Recompiled. |
| PsyCross Required both | Rebuilds the Psy-Q libraries on SDL2, OpenGL and OpenAL. Decompiled games link to it to run on PC. Path B | `git submodule add https://github.com/OpenDriver2/PsyCross`; needs SDL2 2.0.16+ and OpenAL-soft 1.21+ | README (read 2026-11-13). Used by: REDRIVER2, Silent Hill Decomp, CTR Native (as a starting point). |
| symdump Helpful Windows | Reads the .SYM debug files that a few discs shipped with. It prints names and types, like Sony's own DUMPSYM.EXE.Path B | Download `symdump.exe` from release 1.0 on the releases page. It is a C# program from 2017. | README and releases list (read 2026-11-13). Its command-line options were not read. Used by: REDRIVER2. |
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.
matching-decomp (Ketbome)
A one-function loop with a small dk.py tool for MIPS games on a splat layout. It lists PS1 and keeps state in files. It needs Python 3.9+ with pyelftools, and it makes git commits, so save your work first.
npx skills add ketbome/decomp-skills --skill matching-decomp -a claude-code
decompile-to-matching-c-cpp
Rules for byte-matching C that ban fake matches. It fits the splat, m2c and objdiff setup used here.
npx skills add xsyetopz/skills --skill decompile-to-matching-c-cpp -a claude-code
debug-game-in-emulator
Makes each emulator run the same: a throwaway data folder, a log and a time limit. It lists PS1 and DuckStation. It expects your own BIOS dump, unlike the OpenBIOS path above.
npx skills add xsyetopz/skills --skill debug-game-in-emulator -a claude-code
n64-decomp-splat
A reference for splat yaml files. It was written for N64, but its author says most of it fits PS1.
npx skills add marijnvdwerf/agent-skills --skill n64-decomp-splat -a claude-code
verification-before-completion
Claude may not say done without fresh proof. That fits golden runs and make check.
npx skills add obra/superpowers --skill verification-before-completion -a claude-code
dispatching-parallel-agents
Helps a lead session brief one worker per function or per overlay.
npx skills add obra/superpowers --skill dispatching-parallel-agents -a claude-code
handoff
Writes a short note so a fresh session can carry on. Tell it to save inside your repo, not the temp folder.
npx skills add mattpocock/skills --skill handoff -a claude-code
GhidraMCP
The PSXRecomp author needed Ghidra in the loop to stop Claude from guessing. Ape Escape ships a Ghidra MCP example config.
claude mcp add ghidra -- uvx --from git+https://github.com/LaurieWired/GhidraMCP ghidra-mcp --ghidra-server http://127.0.0.1:8080/
psx-unit-match
Every worker matches the same way, with the same stop rule and the same log.
A project skill in `.claude/skills/psx-unit-match/SKILL.md`. It reads one function's `.s` file, its ledger rows and three similar matched functions. It runs `m2c`, then loops on `make clean && make check` with one change per try. It logs each try to `notes/attempts.csv` and stops at 10 tries. It names the banned tricks: inline asm, register pins, embedded bytes.
psx-crash-triage
Path A bugs get a proven cause first. That stops stubs and fake events.
A project skill in `.claude/skills/psx-crash-triage/SKILL.md`. It starts the build with `PSX_DEBUG_SERVER=1`. It reads `get_registers` from port 4370 and from the Beetle oracle on port 4380, one at a time, and compares them. It never uses `compare`, which points at the old port 4371. It reads the PC address in Ghidra and writes the first divergence to `ISSUES.md` before any fix.
Claude setup for this system
On Path A, one unit is one crash, one missing function or one overlay. Each unit has a golden run that a script checks. On Path B, one unit is one function. Its check is the full make clean && make check hash. Group work by overlay, so two workers never edit the same file. Keep the Psy-Q library out of the queue.
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 | Check for an existing decomp, recomp or .SYM files. Pick the disc version and the framework. Write SCOPE.md. | 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 PSXRecomp on Windows, or the splat, old-gcc and maspsx toolchain in WSL. Write tools/check.ps1 or make check. | Opus 5.5 effort medium | Sonnet 5.5 effort medium |
| Study the game file | Load the EXE with ghidra_psx_ldr. Mark Psy-Q code, find overlay loaders and fill notes/functions.csv. | Opus 5.5 effort high | Opus 5.5 effort mediumRaise to high for function edges and data layouts. A mistake there spreads. |
| Bulk units | Match small game functions, one per worker. Add proven seeds for missed functions. | 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 | Big functions, GTE-heavy code and odd ASPSX output. Functions that failed 10 bulk tries. | 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. |
| Build the runtime | Framework fixes on Path A: timing, VSync, CD and XA, pad input. On Path B, port glue in PsyCross or PsyZ. | Opus 5.5 effort high | Opus 5.5 effort mediumRaise to high for timing and graphics bugs. |
| Find a bug | Freezes and wrong frames. Compare with the Beetle oracle or a PCSX-Redux trace before any fix. | 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. |
| Check the work | A fresh session re-runs the check in a clean clone. It hunts for inline asm, edited hashes, stubs and fake events. | 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 PROGRESS.md, ISSUES.md, the ledger and the parking lot after each unit. | Haiku 5.5 effort medium | Haiku 5.5 effort medium |
How many sessions at once
Max 20x (now called Ultra)
Path B: run 3 to 5 workers, each in its own worktree on its own overlay or file group. Add one lead and one verify session. Path A: run 1 or 2 workers on separate areas, since most fixes touch shared config.
Max 5x (now called Max)
Path B: run 1 or 2 workers and a verify pass at the end of the day. Path A: one worker at a time. Save hard units for when you have room in your weekly limit.
Lines for this project's CLAUDE.md
- Read `SCOPE.md` and `PROGRESS.md` at the start of every session. - Never edit files in `generated/` or `asm/`. Change the seeds, the config or the generator, then regenerate. - The only verdict is `make clean && make check` (Path B) or `powershell -File tools/check.ps1` (Path A). A diff score is evidence, not proof. - Never edit `checksum.sha`, `DISC.md` hashes, golden screenshots or the check scripts. - No stubs, no fake events, no made-up return values. A missing function aborts with an error. - No inline asm, register pins or embedded bytes to force a match. - Use only named compiler profiles from `config/compiler_profiles.json`. - Keep names as `func_<address>` until you have evidence. Write the evidence in the ledger. - Log every attempt in `notes/attempts.csv` with its mismatch reason. Read it before you start a function. - If you cannot answer from Ghidra, the disassembly or the oracle, say 'I don't know yet'. - Put side issues in `notes/parking-lot.md`. Do not fix them. - In a worktree, use absolute paths and run the build only inside that worktree.
Keep the context small
- Give Claude a function address and file path, not pasted assembly.
- Ask a subagent to read big Ghidra output and report ten lines back.
- Clear the chat after each function or crash. Start the next one from the ledger.
- Keep
CLAUDE.mdshort. Point tonotes/by topic. - Use the debug server for runtime state. Do not dump huge log files.
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, to choose Path A or Path B for one game.
GOAL: Tell me if <GAME> (<SERIAL>) is a good PlayStation project, and by which path. READ FIRST: DISC.md, https://github.com/RetroPortingToolKit/psxrecomp, https://github.com/BlackLabelHQ/RecompOne, decomp.dev, GitHub search for '<GAME> decomp' and '<GAME> recomp'. FENCE: Write only notes/feasibility.md. Do not install anything or touch disc files. CHECK: Run `Get-FileHash -Algorithm SHA256 <BIN PATH>`. Done means the hash matches DISC.md, and the note lists prior work with URLs, the overlay count from the disc file list, and a path choice with three reasons. BUDGET: Stop after 45 minutes. IF BLOCKED: Stop. Say what you could not learn. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Path A or B, and why. 3) Prior work. 4) Risks. 5) A draft SCOPE.md.
A good answer: A clear pick, real URLs, an overlay count and a draft SCOPE.md. It says plainly if a decomp already exists.
Map the binary
After setup, to build the function ledger and overlay list.
GOAL: Build notes/functions.csv and notes/overlays.md for <GAME>. READ FIRST: SCOPE.md, the Ghidra project <PROJECT> through the ghidra MCP server, the disc file list from dumpsxiso. FENCE: Edit only notes/. Ghidra: names and comments only. CHECK: Every function row has address, size, kind and status. Every overlay row cites its loader address. Run `python3 tools/ledger_check.py`. Done means it passes. BUDGET: Stop after 2 hours. IF BLOCKED: Stop. List what you could not prove. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Counts. 3) Check output. 4) Unproven items.
A good answer: Counts of game and SDK functions, a list of overlays with loader addresses, and a short list of open items.
Unit loop, Path A (one break)
For each crash, freeze or missing function in the recomp.
GOAL: Fix <SYMPTOM> at <ROUTE POINT> in the <GAME> recomp. READ FIRST: ISSUES.md, notes/overlays.md, psxrecomp/docs/TCP_COMMANDS.md. FENCE: Edit only game.toml, seeds/ and ISSUES.md. Never edit generated/. No stubs, no fake events. CHECK: Run tools/check.ps1 and `python tools/golden_route.py <ROUTE POINT>`. Done means both pass. BUDGET: Stop after 3 failed fixes. IF BLOCKED: Stop. Show the first divergence from the Beetle oracle on port 4380. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proven cause. 3) Check output. 4) Files changed.
A good answer: DONE with the oracle evidence for the cause, a small change to seeds or config, and passing checks.
Unit loop, Path B (one function)
For each function in the matching grind.
GOAL: Make <FUNC> match 100%. READ FIRST: asm/<FILE>/<FUNC>.s, notes/attempts.csv rows for <FUNC>, config/compiler_profiles.json. FENCE: Edit only src/<FILE>.c and notes/attempts.csv. No inline asm, no register pins, no embedded bytes. CHECK: Run `make clean && make check`. Done means OK and objdiff shows 100% for <FUNC>. BUDGET: Stop after 10 attempts, or 4 with no gain. IF BLOCKED: Stop. Report the best score and the first differing instructions. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Files changed.
A good answer: DONE with make check output pasted, or PARTIAL with the best score and a named cause.
Batch dispatch
A lead session hands out the next functions to workers.
GOAL: Pick the next <N> functions and start one worker per function. READ FIRST: notes/functions.csv, notes/attempts.csv, CLAUDE.md. FENCE: Do not edit src/ yourself. Each worker uses its own worktree (`claude -w <name>`) and only its own file. Skip sdk rows and rows with 10 failed attempts. CHECK: After workers finish, merge and run `make clean && make check` on the merged branch. Done means OK. BUDGET: Stop after one batch. IF BLOCKED: Stop. Report which merges broke the build. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Per function: matched or not. 3) Merged check output.
A good answer: A table of functions and results, and one merged make check output that prints OK.
Debug with evidence
When the game freezes, crashes or draws wrong.
GOAL: Find the cause of <BUG> in <GAME>. Do not fix it yet. READ FIRST: ISSUES.md, git log since <DATE> for the files involved. FENCE: Read-only, except ISSUES.md. Run `git show <COMMIT> -- <FILE>` first if a commit is suspected. CHECK: The cause is proven by one of: an oracle divergence, a PCSX-Redux trace, or a git diff. Write the proof in ISSUES.md. BUDGET: Stop after 60 minutes. IF BLOCKED: Stop. List what you ruled out and what evidence you need. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The cause and its proof. 3) A fix plan for me to approve.
A good answer: One proven cause with an address or a diff, plus a fix plan. No code changed yet.
Verify (hunt for cheating)
In a fresh session after any batch of claimed work.
GOAL: Check that the last <N> commits did real work. READ FIRST: git log -<N>, CLAUDE.md. FENCE: Read-only, except notes/verify-<DATE>.md. CHECK: In a fresh clone, run the project check. Search the diff for inline asm, stubs, fake events, edited hashes, edited golden images and edits outside each card's fence. BUDGET: Stop after 90 minutes. IF BLOCKED: Stop. Say what you could not check. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Check output. 3) Per commit: real, suspect or cheat, with the line.
A good answer: A verdict per commit with line numbers. Any cheat is named, not excused.
Status report
At the end of a week, or before you plan the next one.
GOAL: Give me a status report for <GAME>. READ FIRST: PROGRESS.md, notes/functions.csv, notes/overlays.md, ISSUES.md. FENCE: Read-only. CHECK: Every number comes from a file or a command you ran. Run the project check and paste the summary line. BUDGET: Stop after 15 minutes. IF BLOCKED: Stop. Say which file is missing. REPORT: 1) Matched or native counts, by overlay. 2) Golden runs passing. 3) Open issues. 4) Next three units.
A good answer: Short numbers from the ledger and one pasted check line. No padding.
Session handoff
Before you stop for the day or clear the chat.
GOAL: Save what the next session needs. READ FIRST: PROGRESS.md, ISSUES.md, notes/attempts.csv. FENCE: Edit only PROGRESS.md and ISSUES.md. CHECK: A fresh session could start the next unit from these files alone. Run the project check and paste its last line. BUDGET: Stop after 10 minutes. IF BLOCKED: Stop. Say what you could not record. REPORT: 1) What works now. 2) The unit in progress and its best result. 3) Dead ends. 4) The next step.
A good answer: PROGRESS.md names the next unit, its files and its check, plus the dead ends tried.
Blocked or cannot deliver
When a unit keeps failing and you need a straight answer.
GOAL: Tell me plainly if <UNIT> can be done, and how. READ FIRST: notes/attempts.csv or ISSUES.md rows for <UNIT>. FENCE: Read-only. CHECK: Run the unit's check once more (`make clean && make check` or `powershell -File tools/check.ps1`) and paste the output. Each claim cites an attempt row, an address or that output. BUDGET: Stop after 20 minutes. IF BLOCKED: Say so. Do not guess. REPORT: 1) CAN, CANNOT or UNKNOWN. 2) Why, with evidence. 3) Two or three options, with cost in hours. 4) What you need from me.
A good answer: One word first, evidence after, and real options such as 'keep as assembly with notes'.
Scope change request
When Claude wants to work outside its fence.
GOAL: Ask me before you leave the fence for <UNIT>. READ FIRST: SCOPE.md, the current task card. FENCE: Make no edits until I answer. CHECK: Your request names the files, the reason with evidence, and how we will check the change. BUDGET: One message. IF BLOCKED: Wait for my answer. Do not work around the fence. REPORT: 1) What you want to change. 2) Why, with proof. 3) The risk. 4) The check.
A good answer: A short request I can answer yes or no. No edits made.
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 edits the made C in generated/ to get past a crash. | Never edit generated/. Fix the seeds, game.toml or the framework, then regenerate. Block the folder with a hook. |
| Claude hand-delivers a fake event or a stub so a screen unlocks. | No stubs, no fake events. A missing function aborts with an error. The phase is done only when the real screen shows. |
Claude names VSync or another SDK call by guess, and timing goes wrong. | Name an SDK function only from a signature match or a traced call. Write the proof in the ledger. |
Claude adds fprintf logging and fills the disk with traces. | Use the debug server or a short Lua trace. No new log files. |
| Claude tries a new compiler or flag inside one command to force a match. | Use only named profiles in config/compiler_profiles.json. A new profile needs my approval. |
Claude renames many func_ symbols to make progress look better. | Do not rename without evidence. Write the evidence in the ledger notes column. |
| Claude trusts a match from one worktree and merges it. | A match counts only after make clean && make check on the merged branch. |
Problems other projects hit
A cooked 2048-byte .iso loses XA audio and STR movies.
Fix: Dump raw 2352-byte .bin/.cue. Check it against Redump hashes.
Seen in: CTR Native, Weltall, Gran Turismo 2 PC, VR & Browser
A config named an internal wait routine as VSync. The game ran at double speed.
Fix: Find the real Psy-Q VSync entry before you write the stand-in. Dump the game's timing values first.
Seen in: Crash Bandicoot PC
Jumps into the middle of a function broke function-level recompiling. Duff's device loops were one case.
Fix: Replace such routines with a high-level stand-in, or emit a switch on known targets.
Seen in: Crash Bandicoot PC
Overlays share load addresses. Code in a missed overlay never gets compiled.
Fix: Give each overlay its own entry and function map. Find overlays from the loader code, not from play.
Seen in: MediEvil Recomp, Symphony Recomp, Ape Escape Recompiled - Enhanced Edition
Hardcoded struct offsets read the wrong field on 64-bit with no crash.
Fix: Build 32-bit first. Use offsetof and add _Static_assert layout checks.
Seen in: Silent Hill Decomp, CTR Native, Forbidden Memories Recompiled
The old-gcc PSX patch can drop default flags such as soft float.
Fix: Pass every flag on purpose in a named compiler profile.
Seen in: Forbidden Memories Recompiled
PCSX-Redux breakpoints do not fire on the dynarec CPU.
Fix: Start it with -interpreter when you trace.
Seen in: Forbidden Memories Recompiled
A release shipped with an empty Mods page. The build log looked fine.
Fix: Open the finished zip in CI and check its contents.
Seen in: ValkyrieRecomp, Ape Escape Recompiled - Enhanced Edition
Build folders shared between worktrees reused another checkout's objects.
Fix: Give each worktree its own build folder. Force a full rebuild when the checkout changes.
Seen in: Forbidden Memories Recompiled
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 8 weeks | 5 to 20 weeks | 4 to 12 months | medium confidence |
| Path B: full decomp | 5 to 14 months | 10 months to 3 years | 4 to 10 years | low confidence |
Read this first
All numbers assume one medium-sized game and one person directing Claude for 10 to 15 hours a week. Path A rests on several dated AI-assisted PlayStation projects, but only one states a duration, so confidence is medium. That one was a prototype, and its author built the framework at the same time. Path B rests on one PlayStation project that used 20 people, plus one N64 project. That fits our one-person case poorly, so confidence is low.
What makes it faster or slower
- How many overlays the game has. Each one is its own split, map and check.
- Whether a decomp, a recomp or
.SYMdebug files already exist for the game. - How fast you find the exact GCC and ASPSX versions.
- How much GTE math and hand-written assembly the game uses.
- Movies, XA audio and multi-disc games add runtime work on Path A.
The facts behind these numbers
| Project | What happened | Link |
|---|---|---|
| PSXRecomp + Tomba! | A playable prototype after about three weeks, by a person new to compilers using Claude Code. He says a first game takes weeks to months, later ones days to weeks. | source |
| PaRappa The Rapper Recompiled | First commit 2026-10-05, first release 2026-10-07. 3 contributors. Developer says AI was used. Only Stage 1 was tested. | source |
| Forbidden Memories Recompiled | First commit 2026-08-30, preview release 2026-09-26, all 1,134 game C targets matched by November 2026. 20 contributors. AI used. | source |
| Symphony Recomp | First commit 2026-07-09, first release 2026-08-02. 6 contributors. No AI. Built on RecompOne, whose author had worked on it for about five months (readonlymemo.com interview). | source |
| PsyDoom | First commit 2019-10-03, first release 2020-01-23. 4 contributors. No AI stated. | source |
| REDRIVER2 | Skeleton Feb 2019, a year of preparation, fully playable Sep 2020. 7 contributors. No AI stated. | source |
| silent-hill-decomp | Repo created 2021-11-22. All 6,530 functions matched, but only about 52% of data, on decomp.dev on 2026-11-13. README says human-driven. 27 contributors. | source |
| Snowboard Kids (N64, same MIPS family) | A matching decomp of 2,145 functions in about 84 days. One main author and three key helpers used agents from the start. | source |
Projects reviewed (15)
The top-starred project for each of the top 12 PlayStation games on RecompLauncher, plus three where the developer says AI was used. RecompLauncher lists 179 PlayStation projects for 143 games.
What each project teaches
REDRIVER2
Driver 2
Started: 2019-02. First release: 2020-11. Code: C.
AI: none stated
Lesson: A hand-made, non-matching decomp can work if you tag each function as decompiled, tested or altered. Debug symbols from two discs gave every name.
Symphony Recomp
Castlevania: Symphony of the Night
Started: 2026-07. First release: 2026-08. Code: C#.
AI: none: README says no AI was used and AI pull requests are closed
Lesson: One JSON config lists every overlay with its own function map. An existing decomp gave the names, even though the build does not use it.
Silent Hill Decomp
Silent Hill
Started: 2021-09. First release: 2026-03. Code: C and C++.
AI: yes: README says the PC port is heavily AI-assisted with Claude, and a workflow runs Claude Code on @claude mentions; the decomp itself was human work Agent files: CLAUDE.md, .claude/settings.json.
Lesson: Port a finished decomp by linking it to PsyCross. Wrap every PC change in one #ifdef so the matching build stays exact.
Severed Chains
Legend of Dragoon
Started: 2021-06. First release: 2022-12. Code: Java.
AI: none: README says AI was not used
Lesson: A hand rewrite in Java from the disassembly. It keeps the original address in each name, so code stays traceable.
PsyDoom
Doom
Started: 2019-10. First release: 2020-01. Code: C++ and C.
AI: none stated
Lesson: Replace emulated code one function at a time, so the game runs at every step. Demo replays with stored results check each change.
CTR Native
Crash Team Racing
Started: 2022-08. First release: 2026-06. Code: C.
AI: none stated Agent files: externals/SDL/CLAUDE.md (SDL's own file).
Lesson: Build the port 32-bit first. PS1 code keeps pointers in 32-bit fields, and GPU packets need retail-shaped links.
Crash Bandicoot PC
Crash Bandicoot
Started: 2026-07. First release: 2026-07. Code: C#.
AI: none stated (store flag only)
Lesson: Its docs give a bring-up order: BIOS and pad, CD, GPU, VSync, memcpy, jump tables. A wrong VSync name made the game run at double speed.
Forbidden Memories Recompiled
Yu-Gi-Oh! Forbidden Memories
Started: 2026-08. First release: 2026-09. Code: C.
AI: yes: README says decompiled 'by hand and AI'; 60+ of 100 recent commits sampled carry Claude co-author trailers; a Copilot CLI instruction file Agent files: .github/copilot-instructions.md.
Lesson: The best PS1 model for agents: one hash check, one function ledger, an attempt log, and named compiler profiles. It reached 100% matched game C.
MediEvil Recomp
MediEvil
Started: 2026-08. First release: 2026-10. Code: C#.
AI: none: README says no AI was used
Lesson: Keep enhancements as separate patch files. Never commit the made code; regenerate it from the disc.
Parasite Eve HD Remaster
Parasite Eve
Started: 2026-09. First release: none. Code: none published.
AI: not stated for code; README says backgrounds were upscaled with AI (store flag only for code)
Lesson: A news page only, with no source yet. It shows a decomp helps a recomp with file formats and addresses.
Weltall
Xenogears
Started: 2026-10. First release: 2026-10. Code: C++.
AI: none stated
Lesson: Tests compare the port with digests from the original code run on an emulated CPU. Tests skip, not fail, when no disc is present.
Gran Turismo 2 PC, VR & Browser
Gran Turismo 2
Started: 2026-09. First release: 2026-09. Code: C++.
AI: none stated
Lesson: A small R3000 interpreter runs the original routine as a test oracle. Any difference from the port fails the test.
ValkyrieRecomp
Valkyrie Profile
Started: 2026-08. First release: 2026-08. Code: C and Python.
AI: yes: README says AI-assisted tools may be used and are reviewed; catalog names Codex
Lesson: A PSXRecomp game project can be tiny: game.toml, a seed list and a CMake file. Record the disc hashes in the config.
Ape Escape Recompiled - Enhanced Edition
Ape Escape
Started: 2026-05. First release: 2026-10. Code: Python, C and C++.
AI: yes: developer flag in the catalog; the repo ships a Ghidra MCP example config, and the inherited upstream CLAUDE.md was deleted on 2026-10-16
Lesson: Find overlays from the loader code and log each investigation in ISSUES.md. A later session can then resume without repeating work.
PaRappa The Rapper Recompiled
PaRappa the Rapper
Started: 2026-10. First release: 2026-10. Code: C.
AI: yes: developer flag in the catalog
Lesson: First seeds come from the boot file's call targets, 605 here. Expect to add more as overlays show up.
What we could not check
- No step here was run on this machine. Commands come from the tools' own docs and the reviewed repos.
- Whether
splat,m2candmaspsxrun on native Windows. Their READMEs do not say. Every PS1 matching project read uses Linux or WSL. - GhidraMCP 2.0 needs Ghidra 12.1.4, and the newest Ghidra is 12.2. Pairing 12.1.4 with the 2026.10.03
ghidra_psx_ldrbuild was not tested. - Whether the
psx-beetleoracle can boot with OpenBIOS. The framework's notes start it with a retailSCPH1001.BIN, so you may need your own BIOS dump to use it. - Whether a game project made by
setup_project.ps1can buildpsx-beetleitself. The build steps found are for the framework repo. - The PCSX-Redux Lua trace command in Path B was put together from its flag list. It was not run here.
- The command-line options of
symdump. Only its README and release list were read. - The exact day Forbidden Memories reached 100% matched code.
- How long the PlayStation games in the evidence took to become playable start to finish. Most repos give only release dates.
- The
RecompOnewiki marks itself outdated, so its commands may have changed. - Whether the AI flags set by the store, not the developer, are accurate.
- The
tools/golden_*.py,tools/ledger_check.pyandcheck.ps1scripts in the steps are scripts you write. They are not part of any tool.
Sources
- PSXRecomp README
- PSXRecomp getting started
- PSXRecomp CLAUDE.md
- PSXRecomp TCP debug commands
- RecompOne README
- RecompOne wiki: How to recompile a game?
- Stanley: I built a PS1 static recompiler with Claude Code
- Stanley: Recomp + AI, 5 months later
- splat README
- maspsx README
- old-gcc README and releases
- objdiff README
- m2c README
- decomp-permuter README
- ghidra_psx_ldr README
- GhidraMCP README
- Ghidra README
- PCSX-Redux README
- PCSX-Redux command line flags
- mkpsxiso README
- PsyCross README
- PsyZ README
- sotn-decomp README
- sotn-decomp build guide
- silent-hill-decomp README
- decomp.dev: silent-hill-decomp progress
- decomp.me PS1 compiler list
- psx-spx hardware reference (source)
- psx-spx memory map
- symdump README
- Snowboard Kids in 84 days
- PSXRecomp debug_client.py (compare targets ports 4370 and 4371)
- splat create_config script (PS-X EXE support)
- symdump releases
- ROM interview: RecompOne author on about five months of work