Files
PS2Recomp/README.md
T
Ranieri 043bf3bf8d Feature/stub by address (#61)
* feat: split runtime code in small files to be easy to develop

* feat: split stubs in inl files

* feat: function auto link function treat functions with underscore as same as without underscore

* feat: remove underscore prefix from stubs

* feat: thread and flags refactor

* feat: propagate request stop
feat: some elf validation on runtime

* feat: remove Z7 compiler options
feat: added a literal float case error on vu

* fix: fix critical recompiler error on marking pc calls

* feat: better system interrupt
refactor: small refactor on thread system

* feat: stubs implements for resident evil code veronica

* feat: merge fileio

* feat: stub by address

* feat: codegen now auto-routes J/JAL callssites with known relocation symbols to syscalls/stubs
2026-02-18 01:35:20 -03:00

5.2 KiB

PS2Recomp: PlayStation 2 Static Recompiler (Experimental)

Discord

Also check our WIKI

This project statically recompiles PS2 ELF binaries into C++ and provides a runtime to execute the generated code.

Modules

  • ps2xAnalyzer: scans ELF/functions and writes TOML config (stubs, skip, instruction patches).
  • ps2xRecomp: reads TOML + ELF, decodes R5900 instructions, and generates C++ output.
  • ps2xRuntime: hosts memory, function registration, syscall dispatch, and hardware stubs.

Features

  • Translates MIPS R5900 instructions to C++ code
  • PS2-specific MMI and VU0 macro support.
  • Single-file or multi-file output.
  • Configurable stubs, skips, and instruction patches.
  • Instruction-driven syscall handling.

How It Works

PS2Recomp works by:

  • Parsing a PS2 ELF file to extract functions, symbols, and relocations
  • Decoding the MIPS R5900 instructions in each function
  • Translating those instructions to equivalent C++ code
  • Generating a runtime that can execute the recompiled code

The translated code is very literal, with each MIPS instruction mapping to a C++ operation. For example, addiu $r4, $r4, 0x20 becomes ctx->r4 = ADD32(ctx->r4, 0X20);.

Current Behavior

  • stubs entries generate wrappers that call known runtime syscall/stub handlers by name.
  • stubs also supports address bindings with handler@0xADDRESS for stripped games (for example sceCdRead@0x00123456).
  • Recompiler now tries relocation-symbol auto-binding at callsites (J/JAL) before raw address dispatch; when relocation symbol is known (for example sceCdRead), it can call runtime handlers without manual address mapping.
  • skip entries are not recompiled and generate explicit ps2_stubs::TODO_NAMED(...) wrappers.
  • Recompiled SYSCALL now calls runtime->handleSyscall(...) with the encoded syscall immediate.
  • Runtime syscall dispatch tries encoded syscall ID first, then falls back to $v1.

Requirements

  • CMake 3.20+
  • C++20 compiler (currently tested mainly with MSVC)
  • SSE4/AVX host support for some vector paths

Build

git clone --recurse-submodules https://github.com/ran-j/PS2Recomp.git
cd PS2Recomp

cmake -S . -B out/build
cmake --build out/build --config Debug

Usage

  1. Analyze ELF and generate config:
./ps2_analyzer your_game.elf config.toml

For better results on retail games, see the Ghidra Workflow.

  1. Recompile using generated TOML:
./ps2_recomp config.toml
  1. Build generated output and link with ps2xRuntime.

Configuration

Main fields in config.toml:

  • general.input: source ELF path.
  • general.ghidra_output: optional function map CSV.
  • general.output: generated C++ output folder.
  • general.single_file_output: one combined cpp or one file per function.
  • general.patch_syscalls: apply configured patches to SYSCALL instructions (false recommended).
  • general.patch_cop0: apply configured patches to COP0 instructions.
  • general.patch_cache: apply configured patches to CACHE instructions.
  • general.stubs: names to force as stubs. Also accepts handler@0xADDRESS to bind a stripped function address directly to a runtime syscall/stub handler.
  • general.skip: names to force as skipped wrappers.
  • patches.instructions: raw instruction replacements by address.

Address binding for stripped ELFs:

  • Use handler@0xADDRESS inside general.stubs to map a stripped function start directly to a runtime handler.
  • Example: sceCdRead@0x00123456 binds function start 0x00123456 to ps2_stubs::sceCdRead(...).
  • Before manual binding, try plain recompilation first: if ELF relocation symbols are present for calls, runtime handler routing can be inferred automatically.
  • The address must be the function start in that exact ELF build.
  • Addresses are not portable across different games/regions/builds.
  • The handler name must exist in runtime call lists (PS2_SYSCALL_LIST or PS2_STUB_LIST).

Example:

[general]
input = "path/to/game.elf"
ghidra_output = ""
output = "output/"

single_file_output = true
patch_syscalls = false
patch_cop0 = true
patch_cache = true

stubs = ["printf", "malloc", "free"]

# stripped function binding by address:
# stubs = ["sceCdRead@0x00123456", "SifLoadModule@0x00127890"]
# mixed example:
# stubs = ["printf", "sceCdRead@0x00123456", "SifLoadModule@0x00127890"]

skip = ["abort", "exit"]

[patches]
instructions = [
  { address = "0x100004", value = "0x00000000" }
]

Runtime

To execute the recompiled code.

ps2xRuntime currently provides:

  • Guest memory model and function dispatch table.
  • Some syscall dispatcher with common kernel IDs.
  • Basic GS/VU/file/system stubs.
  • Foundation to expand and port your game.

Limitations

  • Graphics Synthesizer and other hardware components need external implementation
  • VU1 microcode is not complete.
  • Hardware emulation is partial and many paths are stubbed.

Acknowledgments

  • Inspired by N64Recomp
  • Uses ELFIO for ELF parsing
  • Uses toml11 for TOML parsing
  • Uses fmt for string formatting