ps3recomp is a toolkit for statically recompiling PlayStation 3 games into native PC executables. Instead of emulating the PS3's Cell processor at runtime (like RPCS3), we translate the PowerPC assembly into C/C++ ahead of time, compile it with a standard compiler, and link it against a runtime library that provides PS3 system services.
| RPCS3 | ps3recomp | |
|---|---|---|
| Approach | Dynamic emulation | Static recompilation |
| Translation | At runtime (JIT) | Ahead of time (to C/C++) |
| Performance | Good, but CPU-intensive | Native speed — no translation overhead |
| Compatibility | ~70% of library (one binary, many games) | Per-game project (deep but narrow) |
| Moddability | Limited | Full — it's just C/C++ you can edit |
| Hardware requirements | High-end PC recommended | Mid-range PC is fine |
Both approaches have value. RPCS3 is better for broad compatibility; ps3recomp is better for native ports and preservation.
Yes, under the same principles as all clean-room recompilation projects:
- No Sony code is included — the runtime is written from scratch
- Users must supply their own legally obtained game binaries
- Function behavior is reimplemented based on public documentation and RPCS3's open-source reference
flOw by thatgamecompany is the first active port — see the case study. The framework is ready for other games; contributions welcome.
- Windows — clang-cl, D3D12, XInput, WASAPI
- Linux — GCC/Clang, SDL2, Vulkan (opt-in) or the headless null backend
- macOS arm64 — Apple Clang, Metal, SDL2
All three build and run their platform test suites on every pull request. Windows is the only host currently claimed to run a recompiled retail title from this tree. Linux and macOS also boot the synthetic PPU smoke title through the project template, but no retail title is validated from this tree on either POSIX host yet.
- Parse the PS3 ELF binary to find code, data, and imports
- Disassemble PowerPC instructions into structured IR
- Lift each function to equivalent C code (register file → local variables, memory ops →
vm_read/vm_write) - Link the generated C with ps3recomp's runtime library (VM, syscalls, HLE modules)
- Compile with MSVC/GCC/Clang to produce a native x86-64 executable
The Cell processor has 6 SPU cores with their own ISA and 256KB local store each. ps3recomp handles SPUs via:
- HLE (preferred) — many SPU tasks (audio mixing, decompression) can be replaced with native equivalents
- SPU lifter — for complex SPU programs,
tools/spu_lifter.pytranslates SPU assembly to C - SPURS management — the cellSpurs module handles task scheduling without actual SPU execution
The runtime and HLE modules are C17 for maximum ABI compatibility:
- C has a stable, well-defined ABI across compilers
- Simpler linkage with generated code (no name mangling)
- Game projects use C++20 for convenience — the public API headers have
extern "C"guards
A Name ID is how PS3 dynamic linking works. Instead of symbol names, the PS3 uses 32-bit hashes:
NID = first_4_bytes_LE(SHA1(function_name + 16_byte_suffix))
When a game imports cellAudioInit, the binary contains NID 0x0B168F92 (not the string). ps3recomp's module system computes NIDs from names and matches them at load time.
See NID_SYSTEM.md for the deep dive.
The RSX (Reality Synthesizer) is an NV47-class GPU. Games write GPU commands to a command buffer via cellGcmSys. Currently:
- cellGcmSys is fully implemented — display buffers, memory mapping, tile/zcull config, flip control
- Command buffer parsing is not — translating NV47 methods to Vulkan/D3D12 draw calls is the largest remaining task
Functionally equivalent, not cycle-accurate. The goal is identical observable behavior (same pixels on screen, same audio, same game logic) — not matching PS3 timing to the nanosecond. This gives us freedom to use modern APIs and optimizations.
undefined reference to 'vm_base'
vm_base is declared extern in the runtime. Your game project's main.cpp must define it:
uint8_t* vm_base = nullptr;/bigobj required (MSVC)
Large generated source files (>65K sections) need the /bigobj compiler flag:
target_compile_options(my_game PRIVATE /bigobj)incompatible pointer types
Runtime headers are C; if you're including them from C++, wrap with:
extern "C" {
#include "runtime/memory/vm.h"
}C11 _Atomic in C++ file
Some headers (e.g., cellSync.h) use C11 atomics incompatible with C++. Only include headers for modules you actually bridge — use stubs for the rest.
[HLE] UNIMPLEMENTED: funcName (NID 0xABCDEF01)
The game called a function your HLE modules don't handle. Either:
- Add the function to your module registration
- Check if ps3recomp already has a real implementation in
libs/ - Add a stub that returns
CELL_OKif the function isn't critical
vm_init failed
The runtime couldn't allocate the 4GB virtual address space. Check:
- Sufficient virtual memory available (Windows: increase pagefile; Linux: check
ulimit -v) - You're building 64-bit, not 32-bit
Crash in recompiled code (no HLE log) The crash is in lifted game code, not HLE. Common causes:
- Missing TOC save — import stubs should save r2 to sp+40 before calling handlers
- Empty stub function — a branch target wasn't in the function list, so it's a no-op
- Unlifted instruction — check generated code for
/* TODO: ... */comments - Address sign-extension — 32-bit addresses sign-extended to 64 bits in GPRs
Wrong endianness (garbled data)
All guest memory is big-endian. Use vm_read32()/vm_write32() (which byte-swap) instead of raw pointer access. For struct output from HLE bridges, write each field individually with vm_write*.
function not found at address 0xNNNNNNNN
The lifter references a function that wasn't in the function list. Add it manually:
{"address": "0xNNNNNNNN", "size": 64, "source": "manual"}TODO: unimplemented instruction
The lifter doesn't support this instruction yet. Common remaining gaps:
- VMX/AltiVec SIMD (
vaddfp,vmaddfp,vsel,vsldoi, etc.) — the largest category - Some
rld*rotate variants with uncommon operand patterns - Extended opcode 31 forms (
op31_x202,op31_x231)
Recently added (may still show as TODO in older lifter output):
mulld/divd(64-bit arithmetic),adde/subfe(carry ops)cror/crand/crnand/crxor/crnor(CR logical)lwarx/stwcx.(atomic reservation),stdux/ldux(update indexed)tw/td(trap — no-oped)
Fix in tools/ppu_lifter.py to benefit all game ports.
Split-function crash (prologue returns without executing body)
The lifter may split one PPC function into multiple C functions at boundary points. If the first piece (prologue) returns without calling the second piece (body), the function never executes. Fix: the lifter now emits fallthrough calls when a function doesn't end with blr/b. If you see this in older output, re-run the lifter or patch with a fallthrough call script.
Within 2-3x of the original PS3 performance on modern hardware, often faster. The overhead comes from:
- Endian byte-swapping on every memory access
- Function call overhead for
vm_read/vm_write - Unoptimized generated code (the lifter doesn't do advanced optimization)
- Split into batches — the lifter can output multiple smaller files instead of one giant one
- Use
/bigobjon MSVC for files with >65K sections - Parallel compilation — let MSBuild/Ninja use multiple cores
- Incremental builds — only recompile changed files
- Release builds — debug builds of 156 MB source files can take 30+ minutes; Release is faster
Yes, but prefer fixing the lifter over hand-editing individual functions. Lifter improvements benefit every game port. Only hand-edit for truly game-specific one-off issues.