PS2Recomp: A Static Recompiler That Turns PS2 ELFs into Native C++
Playstation 2 Static Recompiler & Runtime Tool to make native PC ports
At a glance
- What is it?
- PS2Recomp translates PlayStation 2 MIPS R5900 binaries into C++ and provides a runtime to execute the result. It targets engineers building native PC ports, but it is experimental and demands significant manual work.
- Who is it for?
- Adopt PS2Recomp if you are an experienced reverse engineer who wants to build a native PC port of a specific PS2 game and can invest time in Ghidra analysis and iterative runtime stubbing. Do not use it if you expect a turnkey emulator or a quick port.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What PS2Recomp Solves and Who It Is For
PS2Recomp addresses the problem of porting PlayStation 2 games to native PC without emulation. Instead of interpreting or dynamically recompiling MIPS instructions at runtime, it statically translates the original ELF binary into C++ source code. That C++ is then compiled with a normal host compiler, producing a native executable. This approach can yield better performance than emulation and gives the porter direct control over the generated code. The intended audience is not casual players but engineers and reverse engineers who are willing to work with disassembly, function maps, and runtime stubs. The project is experimental, as its README states, and it is not a polished product. It is a toolkit for people who already know how to analyze PS2 binaries and want a structured path to a native port.
The Pipeline: From ELF to C++ to Runtime
The workflow is split into three modules. First, ps2xAnalyzer scans the ELF and produces a TOML configuration that lists functions, stubs, skips, and instruction patches. Second, ps2xRecomp reads that TOML and the ELF, decodes each R5900 instruction, and emits C++ code. Third, ps2xRuntime provides the execution environment: guest memory, a function dispatch table, syscall handling, and hardware stubs. The generated code is deliberately literal. The README shows that a MIPS instruction like `addiu $r4, $r4, 0x20` becomes `ctx->r4 = ADD32(ctx->r4, 0X20);`. This literal translation makes the output predictable and easier to debug, but it also means the generated code is not optimized. The runtime is where most of the real work happens, because every syscall and hardware access must be implemented manually or through stubs.
Getting It Running: Build and Configuration Steps
Building the tools requires CMake 3.20 or newer and a C++20 compiler, with MSVC being the most tested. The README gives these commands: clone with `--recurse-submodules`, then `cmake -S . -B out/build` and `cmake --build out/build --config Debug`. After building, you have two executables: `ps2_analyzer` and `ps2_recomp`. The preferred workflow for retail or stripped games starts in Ghidra: you run the `ExportPS2Functions.java` script from `ps2xRecomp/tools/ghidra/`, which produces a TOML and CSV function map. Then you run `./ps2_recomp config.toml` with that exported TOML. The fallback is `./ps2_analyzer your_game.elf config.toml`, which is faster but less accurate on stripped games and more likely to miss internal callable entry points. The config file has a `general` section with keys like `input`, `ghidra_output`, `output`, `single_file_output`, `low_memory_mode`, and `output_worker_threads`. There are also `patches.instructions` for raw instruction replacements. The README explicitly recommends the Ghidra workflow over the analyzer for retail games.
Handling Stripped Binaries with Address Bindings
A significant part of the configuration is dedicated to stripped ELFs, where symbol names are missing. The `stubs` array in the TOML accepts entries like `handler@0xADDRESS` to bind a specific function start address to a runtime handler. For example, `sceCdRead@0x00123456` maps the function at that address to `ps2_stubs::sceCdRead(...)`. There are also generic return handlers: `ret0@0xADDR`, `ret1@0xADDR`, and `reta0@0xADDR`, which are useful for triage when you just want a function to return a fixed value. The README warns that addresses are not portable across different game builds or regions, and the handler name must exist in the runtime's call lists (`PS2_SYSCALL_LIST` or `PS2_STUB_LIST`). This mechanism is a stopgap. The documentation advises that you should prefer full recompilation from Ghidra-exported data, because the extra boundaries and synthetic entry points are usually more valuable than manual early triage.
Syscall Dispatch and Relocation Auto-Binding
The recompiler handles syscalls and indirect calls in a specific way. Recompiled `SYSCALL` instructions call `runtime->handleSyscall(...)` with the encoded syscall immediate. The runtime dispatch tries the encoded syscall ID first, then falls back to using the `$v1` register. For `J` and `JAL` instructions, the recompiler attempts relocation-symbol auto-binding before falling back to raw address dispatch. If the relocation symbol is known, like `sceCdRead`, it can call the runtime handler directly without manual address mapping. Unresolved static calls fall back to `runtime->lookupFunction(0x...)`. This design reduces the amount of manual stub configuration needed, but it depends on the quality of the relocation information in the ELF. For stripped binaries, that information is often incomplete, which is why the address binding mechanism exists.
The Runtime and Game Override Hooks
The runtime is not a full emulator. It provides a guest memory model, a function dispatch table, a syscall dispatcher with common kernel IDs, and basic GS/VU/file/system stubs. The README describes it as a foundation to expand and port your game. There is also `ps2xIOP`, which offers portable, instance-owned IOP HLE services, game profiles, and a C plugin ABI. Game overrides are a separate mechanism: C++ code that runs during `loadELF` and can replace EE function bindings by address for one specific game build. The API is in `ps2xRuntime/include/game_overrides.h`, with the macro `PS2_REGISTER_GAME_OVERRIDE(name, elfName, entry, crc32, applyFn)`. This is build-scoped and separate from global stubs. The distinction matters: IOP RPC/DMA behavior belongs in a `ps2xIOP` profile, not in a game override. This modularity is useful, but it also means you have to learn multiple extension points and decide where each fix belongs.
Limitations and Wrong-Tool Cases
The most obvious limitation is that this is not a turnkey solution. The runtime stubs are basic, and every game will require significant work to implement missing syscalls and hardware behavior. The README itself calls the project experimental. The literal translation of instructions means the generated C++ is large and unoptimized, which could be a problem for performance-sensitive games. The fallback analyzer is explicitly less accurate on stripped retail games, so you cannot skip Ghidra if you want reliable results. Another limitation is that address bindings are per-build, so porting a different region or version of the same game requires redoing that work. If your goal is simply to play PS2 games on a PC, this is the wrong tool; a dynamic recompiler like PCSX2 is far more practical. PS2Recomp is for people who want a native port, not a compatibility layer.
Alternatives and Maintenance Considerations
The main alternative is dynamic recompilation, as used by PCSX2. That approach translates code at runtime, which avoids the need for static analysis and function maps, but it keeps the emulator layer and does not produce a standalone native binary. Another alternative is manual reverse engineering and rewriting, which gives full control but is far more labor-intensive. PS2Recomp sits in between: it automates the instruction translation but leaves the hard problems of runtime stubbing and game-specific logic to the porter. The project is under active development, with releases v0.2, v0.3, and v0.4 pushed between March and April 2026. The license is GPL-3.0, which means any distributed derivative work must also be GPL-licensed. That is a real constraint for commercial ports. The build requires CMake and a C++20 compiler, and the runtime has SSE4/AVX host support for some vector paths, so older hardware may not work. The maintenance cost is high: you must keep up with tool updates, re-run the pipeline when the game's ELF changes, and maintain a growing set of stubs and overrides for each game.
Editorial conclusion
Adopt PS2Recomp if you are an experienced reverse engineer who wants to build a native PC port of a specific PS2 game and can invest time in Ghidra analysis and iterative runtime stubbing. Do not use it if you expect a turnkey emulator or a quick port. Before committing, verify that your target ELF is not heavily obfuscated, that you can produce a reliable function map from Ghidra, and that the runtime's current syscall and hardware stubs cover the game's needs. The project is actively developed, but it is experimental and the runtime is a foundation, not a finished platform.
Community notes