-
Notifications
You must be signed in to change notification settings - Fork 83
Ps2xRecomp
The recompiler consumes the TOML configuration generated by the analyzer and produces C++ source code by translating PS2 MIPS instructions into equivalent C++ operations.
The recompiler interprets the TOML entries as instructions for how to generate code:
Functions listed in stubs are not recompiled.
Instead, the recompiler generates small wrapper functions that call known runtime syscall/stub handlers by name.
Functions listed in skip are not recompiled and are treated as “intentionally unsupported”.
The recompiler generates explicit placeholders like:
ps2_stubs::TODO_NAMED("FunctionName")
This makes it obvious what still needs manual work or runtime support.
Use stubs (handler@addr) when:
- You know the address and want to route it to an existing runtime handler.
Use skip when:
- You want an explicit TODO wrapper for now.
- It will call
ps2_stubs::TODO_NAMED(...)and return an error-like value.
Implement/extend ps2_syscalls when:
- Runtime logs show unknown numeric syscalls (
[Syscall TODO] ...). - Behavior is kernel-style and keyed by syscall IDs.
-
general.input: path to the source ELF. -
general.output: output folder where generated C++ will be written. -
general.ghidra_output: optional function map CSV exported from Ghidra. -
general.single_file_output: generate one combined.cppfile (true) or one file per function (false). -
general.patch_syscalls: apply configured patches toSYSCALLinstructions (false recommended). -
general.patch_cop0: apply configured patches to COP0 instructions. -
general.patch_cache: apply configured patches to CACHE instructions. -
general.stubs: additional names to force as stubs (overrides detection). -
general.skip: additional names to force as skipped wrappers (overrides detection).
-
patches.instructions: raw instruction replacements by address (manual patch list).
The recompiler translates each MIPS instruction into a C++ equivalent that updates an emulated CPU context.
Example:
-
MIPS:
addiu $r4, $r4, 0x20
-
C++:
ctx->r4 = ADD32(ctx->r4, 0x20);
This is the core idea: treat registers as fields on a CPU context and rewrite MIPS semantics into C++ operations.
./ps2_recomp config.tomlThis will read the TOML, load the ELF, and generate C++ code into general.output.
The tool is not perfect. After generation, you must review the output for comments indicating instructions the recompiler could not translate.
Some examples:
// Unhandled opcode: <raw>
// Unhandled FPU.S instruction: <raw>
// Unhandled PMTHL instruction: <raw>If your game uses an instruction (or a special-case behavior) that the recompiler doesn’t support yet, you will see these markers in the output.
You typically need to confirm:
- Functions that should be stubs are actually stubbed.
- Functions that should be skipped are actually skipped.
- Required patches are correct and we are not removing important things.
You usually cannot infer semantics from address alone. You combine static and dynamic clues:
Static clues:
- Caller context: inspect all callsites to that address.
- Arguments: what goes in
$a0..$a3. - Return contract: how caller checks
$v0(zero/non-zero, pointer, negative error). - Side effects: writes to known structures, DMA/GS/VIF regions, file paths, RPC payloads.
- Nearby strings/constants: paths (
cdrom0:), format strings, module names.
Dynamic clues:
- Stub fallback logs show
name,PC,RA, and args. - Syscall TODO logs show encoded syscall and register state.
- Runtime logs show repeated PC (spin), unresolved function addresses, and thread state.
Practical strategy:
- First try existing runtime handlers (
sceCdRead,fio*,Sif*, etc.). - If uncertain, use
ret0/ret1/reta0to test whether behavior is just a probe. - If game progresses, replace temporary return stubs with real behavior later.
- If game breaks/hangs, implement real semantics (or a targeted override).
Once the generated code builds cleanly and your stubs/skip/patches configuration is correct, you can proceed to the runtime stage.