flOw - Static Recompilation
flOw
OriginPlayStation 3
TechnologyStatic recompilation (native C/C++)
Available forWindows, Linux, macOS
View official projectAbout the project
Flow es un videojuego independiente creado por Jenova Chen y Nicholas Clark. Originalmente lanzado como un juego Flash gratuito en 2006 para acompañar la tesis de maestría de Chen, fue reelaborado como un juego para PlayStation 3 en 2007 por su estudio de desarrollo Thatgamecompany con la ayuda de Santa Monica Studio. SuperVillain Studios desarrolló una versión del juego para PlayStation Portable en 2008, y versiones para PlayStation 4 y PlayStation Vita en 2013.
Official project information
flOw — Static Recompilation
The first native PC port of thatgamecompany's flOw, rebuilt from the PS3 binary through static recompilation.
flOw (2007) was the debut PlayStation Network title from thatgamecompany — the studio that went on to create Flower, Journey, and Sky. Originally a browser Flash game by Jenova Chen, the PS3 version was rebuilt on Sony's PhyreEngine with 1080p rendering, SIXAXIS tilt controls, and a hypnotic ambient soundtrack. It never received a PC release.
Until now.
This project takes the PS3 EBOOT.elf binary, disassembles all PowerPC functions, lifts them to C, and links against ps3recomp — a set of HLE runtime libraries that replace the PS3 operating system with native host implementations. The result is a standalone Windows executable that runs flOw natively, no emulator required.

flOw's Level 3 rendering natively on Windows via static recompilation — the ocean gradient, drifting plankton, and the player organism (red), drawn through a D3D12 backend on top of the recompiled engine's own initialization.

A deeper level, captured from the v0.6.0 build — drifting plankton and a school of orange creature organisms over the ocean gradient. This boot loads the real decrypted libsre.prx: flOw's cellSpurs/cellSync imports now dispatch into genuinely recompiled Sony library code (relocated, lifted, and namespaced) instead of HLE stubs, eliminating the 0x01000000 unresolved-PRX spin that previously blocked SPURS construction.
Current Status
| Metric | Value |
|---|---|
| Title | flOw |
| Title ID | NPUA80001 |
| Engine | PhyreEngine (Sony) |
| Functions recompiled | 102,056 (OPD + heuristic + branch target splitting + targeted re-lifts) |
| Functions lifted to C | 102,056 (100%) |
| Trampoline sites | 22,000 converted fallthroughs, 143,000 drain sites |
| Imported libraries | 12 |
| Imported functions | 140/140 resolved (100%) |
| Binary size | ~10 MB (ELF) → ~45 MB (exe) |
| HLE bridges | 7/12 real (cellSysutil, cellGcmSys, cellAudio, cellPad, cellFs, cellSysmodule, sysPrxForUser) |
| Remaining TODOs | ~10,000 (mostly VMX comparison + remaining unrecognized op4) |
| Indirect calls | 20,030 bctrl sites → hash table + 3-level OPD dispatch for C++ virtual methods |
| ps3recomp version | v0.4.0+ |
| Target | Windows x86-64 (Linux planned) |
Phase Progress
| Phase | Status | Notes |
|---|---|---|
| PKG extraction | Complete | 553 files extracted from PSN package |
| SELF decryption | Complete | EBOOT.BIN → EBOOT.elf via RPCS3 + RAP |
| Binary analysis | Complete | 91,758 functions via OPD + heuristic + branch target splitting |
| NID resolution | Complete | 140/140 import NIDs mapped to function names |
| ELF structural analysis | Complete | Segments, sections, OPD, TOC, memory map |
| PPU lifting | Complete | 91,758 functions lifted to C++ (190 MB source) |
| Runtime linking | Complete | Builds and links against ps3recomp runtime |
| HLE module registration | Complete | 12 modules, 7 with real HLE bridges |
| CRT startup | Complete | TLS → mutexes → malloc → static constructors |
| CRT abort redirect | Complete | longjmp workaround for constructor stack overflows |
| Game main() | Complete | Loads 7 modules, registers sysutil callback, initializes SPU |
| GCM / RSX init | Complete | Guest command buffer, display buffers, tile/zcull, MapMainMemory |
| Engine init | Complete | PhyreEngine created, 12 subsystems, vtables resolved |
| Input init | Complete | cellPadInit, cellKbInit (×4), cellMouseInit |
| Engine game loop | Running | Continuous frame loop, ~88 FPS, 12 subsystems ticking each frame |
| Natural cellGcmInit | Working | Game's own _cellGcmInitBody fires; cmdbuf 0x00C005E0–0x00CF05E0 |
| Natural VBlank handler | Working | Game's func_000CBE24 dispatched per frame via g_ps3_guest_caller |
| GCM rendering | Working | D3D12 GPU: data-driven scene-builder draws the parsed level (gradient + plankton + organism) into the natural GCM context |
| Buffer flips | Working | cellGcmSetFlipCommand alternating buffers 0/1, batched DRAW_ARRAYS |
| Graphics backend | D3D12 | Device FL11.0, vertex-colored PSO, 112KB VB, DrawInstanced, VSync |
| Audio backend | Wired | cellAudio → WASAPI via ps3recomp |
| Input backend | Wired | cellPad → XInput via ps3recomp |
| SPURS pre-seed | Working | cellSpursInitialize called at engine_run entry on synthesized 2 KB struct |
| SPU PPU fallback | Available | ps3recomp infrastructure complete; flOw doesn't yet trigger SPU thread create |
| FIFO sync | Partial | All-register flag clearing solves 2/3 spin levels in PhyreEngine init |
| Title-state machine | Stuck | Engine alive, no cellPadGetData / cellHddGameCheck / cellFsOpen / sys_ppu_thread_create — natural worker thread for the per-frame loop never spawns |
| Full gameplay | In Progress | Title state needs upstream init we haven't located (gated by r30 = game-state struct we can't synthesize) |
What Works Now
- D3D12 GPU rendering — Level 3 scene: ocean gradient, 9-segment snake creature, 16 food objects, 40 particles (486 vertices via batched DRAW_ARRAYS)
- Level data parsing — reads flOw XML level definitions for authentic background colors, creature parameters, particle counts
- Continuous frame loop — 800+ frames, ~17fps with D3D12 VSync, snapshot restore every 10 frames
- 12 subsystems registered and ticking — engine internal array populated, types 10-19 assigned
- Inline switch table cases — 10 subsystem tick interpolation cases implemented directly in C
- Full GCM command pipeline — NV40 commands → gcm_flush → rsx_process_command_buffer → D3D12 backend
- Batched draw calls — DRAW_ARRAYS split into 255-vertex batches for RSX method format
- RSX command processor — parses NV40 methods (surface, viewport, clear, attribs, draw, begin/end)
- FIFO flag clearing — clears PhyreEngine pending flags on r27/r28/r30/r26/r29/r31 (+0x18)
- FIFO watchdog thread — monitors ctrl->put, scans for SET_REFERENCE, patches NULL OPDs
- D3D12 backend — Device FL11.0, vertex-colored PSO, 112KB VB, DrawInstanced, VSync present
- 102,056 functions recompiled to native C++ (base lift + 2 targeted re-lifts)
- PhyreEngine initialized — 12 subsystems, engine vtable 0x1006E508, game instance at BSS 0x10163764
- GCM / RSX initialized — guest command buffer (960KB), display buffers, tile, zcull, IO mapping
- Input system initialized — cellPadInit, cellKbInit (4 code types), cellMouseInit
- 3-level OPD dispatch for C++ virtual method calls
- ELF data snapshot system — protects 1.3MB of zeroed GOT entries, OPDs, and vtables
- gCellGcmCurrentContext restoration — survives snapshot restores via GOT + BSS chain fix
- Control register emulation — host-endian put/get/ref with per-put-change ref increment
- AddressToOffset fallback — handles unmapped heap addresses for GCM operations
- CRT abort redirect via longjmp — intercepts CRT abort, recovers into main()
- 166 static constructors executing via indirect call dispatch
- VMX/AltiVec — vector loads, stores, float arithmetic, permute, select, compare all working
- 7 HLE modules with real bridges — PPC64 ABI parameter extraction, BE struct output, host OS backends
- HLE malloc — bump allocator bypassing CRT's Dinkumware heap
- D3D12 window — opens on startup with Win32 message pump, RSX null/D3D12 backend ready
- Real WASAPI audio, XInput gamepad, filesystem I/O, lightweight mutexes
- LV2 syscall dispatch with sys_tty_write/read, event ports, timers
Known Issues
- Title-state wall — engine alive but state machine doesn't progress. Disasm-verified: natural
func_000C858Cis one-shot shutdown, not a loop. The natural per-frame outer loop must come from a worker thread spawned by init code we don't reach.sys_ppu_thread_createis never called naturally;cellHddGameCheck/cellFsOpen/cellPadGetDatazero calls. Ourfor(;;)substitutes for the missing worker — works for ticking but doesn't trigger the state machine. v0.6.0 update: with real libsre loaded, force-calling game-main-initfunc_00268504with the correct init state (r3=0) now runs the subsystem-construction sequence cleanly (no more0x01000000/OOM) — the upstream construction the bypass skipped. The remaining gap: driving the state machine's later phases (r3=1still reads partly-unconstructed objects) and populating the UI-handler slot0x10164D20(never written; its writer is still untraced). func_00810BB8is__cxa_pure_virtual— engine vtable[2] is the abstract-method abort stub. Confirmed via disasm. Calling it always aborts. Kept disabled.func_00138B7C→cellHddGameCheckpath — gated bysys_memory_container_create(syscall 0x155); failure jumps tofunc_001392F0which calls cellHddGameCheck with a callback OPD atr30 + -0x7DDC. Reaching it from our injection point needsr30= a game-state struct we haven't synthesized.- Render method callbacks — Engine vtable[2] disabled; our scene-builder gated off once natural GCM ctx is live.
- Empty scene graph — Render context exists but render_ctx+0x3C4 (scene data) has an empty cluster.
- Switch table targets — func_000CA3B4's switch dispatch (types 10-24) targets not in function table. Worked around with inline
subsystem_tick_case(). Lifter needs switch table target extraction. - ELF data zeroing — Workaround: snapshot restore at 6+ points + gCellGcmCurrentContext chain restore + BSS 0x10112000-0x10170000 zeroing.
- Control register endianness — RSX MMIO accessed via lwbrx/stwbrx → host-endian uint32_t* access.
- vm_read32_fast bypass — Snapshot restores via direct memcpy to vm_base.
Build Pipeline (Fully Automated)
# 1. Run the lifter (~2-3 hours for 92K functions)
python tools/recompile.py --skip-find --functions-file combined_functions_split.json \
--ps3recomp-dir D:/recomp/ps3
# 2. Post-process (rename, patch header, fallthrough, bctrl, malloc)
python tools/post_lift.py --recomp-dir src/recomp
# 3. Convert fallthroughs to trampolines (22K conversions, 143K drain sites)
python tools/convert_trampolines.py --recomp-dir src/recomp
# 4. Generate missing stubs
python tools/gen_stubs.py --recomp-dir src/recomp
# 5. Build
cmake -B build -DPS3RECOMP_DIR=D:/recomp/ps3
cmake --build build --config Release
Technical Discoveries
These findings apply to ALL ps3recomp game ports:
-
TOC save in import stubs — The PPC64 ABI requires saving r2 (TOC) to sp+40 before inter-module calls. The lifter doesn't emit this instruction. Import stubs must save TOC in their
nid_dispatch()function or all subsequent TOC-relative loads crash. -
Split-function fallthrough — The lifter splits PPC functions at boundary points, creating multiple C functions. When a function ends without
blr/b, the lifter must emit a call to the next function. Without this, function prologues return before executing their body. -
dcbz must zero memory —
dcbz(data cache block zero) is NOT safe to no-op. Games use dcbz loops for bulk memory initialization. No-oping dcbz creates infinite loops (CTR computed from huge byte counts) and leaves memory regions uninitialized, causing later crashes. -
Guest malloc bypass — The PS3 CRT's Dinkumware malloc requires OS-level heap initialization that recomp doesn't replicate. Replace with a bump allocator in committed guest VM memory.
-
Initial LR — Set ctx.lr to the
sys_process_exitimport stub address before entering the game. The PS3 OS sets LR to the exit handler; leaving it at 0 propagates NULL through the entire CRT stack. -
CRT abort redirect via longjmp — PS3 CRT static constructors can overflow the guest stack due to split-function backward branch recursion. Rather than fixing every constructor, install a SEH handler that catches the access violation and longjmp back to main() with a clean stack. The game still initializes correctly because the constructors that crash are non-essential.
-
Trampoline drain sites — Split-function fallthroughs need a trampoline mechanism: after every
blcall, insert a DRAIN_TRAMPOLINE macro that checks if the callee set a "fall-through target" flag and jumps to the next split fragment. Without this, returning from a split function resumes at the wrong point. -
Backward branch recursion across split boundaries — When the lifter splits a function and encounters a backward branch to an address in a different split fragment, it emits a function call instead of a branch. This creates mutual recursion between fragments, blowing the host stack. Root cause of most constructor crashes.
-
Manual dispatch stubs for mid-function entry points — Some indirect calls target addresses in the middle of lifted functions (not function entry points). These require hand-written dispatch stubs that set up context and jump to the correct offset within the target function.
Import Map
flOw imports 140 functions from 12 PS3 system libraries:
| Library | Functions | HLE Status | Key APIs |
|---|---|---|---|
| sysPrxForUser | 15 | Real | TLS init, lwmutex create/lock/unlock/destroy, thread create, process exit, time |
| cellSysutil | 15 | Real | RegisterCallback, GetSystemParamInt/String, VideoOutGetState/GetResolution/Configure |
| cellGcmSys | 18 | Real | Init, GetConfiguration, GetControlRegister, SetDisplayBuffer, MapMainMemory, SetTile, SetFlip |
| cellSysmodule | 4 | Real | LoadModule/UnloadModule with module ID name logging |
| cellAudio | 7 | Real | Init, PortOpen/Close/Start/Stop, GetPortConfig (WASAPI backend) |
| sys_io | 17 | Real | cellPadInit/End/GetData/GetInfo (XInput), keyboard/mouse stubs |
| sys_fs | 11 | Real | cellFsOpen/Close/Read/Write/Lseek/Fstat/Opendir (host filesystem) |
| sys_net | 21 | Stubbed | BSD sockets — not needed for offline play |
| cellSpurs | 14 | Stubbed | SPU workload management |
| sys_fs | 11 | Stubbed | File I/O — open, read, write, close, stat |
| sceNp | 10 | Stubbed | PlayStation Network services |
| cellAudio | 7 | Stubbed | Audio port management |
| cellNetCtl | 5 | Stubbed | Network control |
| cellSysmodule | 4 | Real | Module load/unload with logging |
| cellSync | 3 | Stubbed | Barrier synchronization |
How It Works
+-----------------------------------------------------------+
| flOw EBOOT.elf |
| (PowerPC 64-bit, big-endian) |
+-----------------------------+-----------------------------+
|
+-----------v-----------+
| Analyze (18K+37K OPD) | 91,758 functions
+-----------+-----------+
|
+---------v---------+
| Lift (ppu_lifter) | ~190 MB of C++ code
+---------+---------+
|
+--------------v--------------+
| Link & Compile |
| |
| ppu_recomp.cpp (~190 MB) |
| + import_stubs.cpp |
| + hle_modules.cpp |
| + vm_bridge.cpp |---> flow.exe (37 MB)
| + ps3recomp runtime |
| + elf_loader.cpp |
+--------------+--------------+
Architecture
- vm_bridge.cpp — Bridges lifter's uint64_t memory API to ps3recomp's uint32_t big-endian VM
- hle_modules.cpp — Registers 12 PS3 modules with NID→handler mappings for HLE dispatch + longjmp abort redirect
- indirect_dispatch.cpp — bctrl hash table + trampoline system for split-function fallthroughs
- import_stubs.cpp — 140 import stub functions that resolve NIDs at runtime
- malloc_override.cpp — Bump allocator (0x00A00000 - 0x10000000) bypassing Dinkumware CRT heap
- elf_loader.cpp — Loads PT_LOAD segments from EBOOT.elf into virtual memory
- ppu_recomp.cpp — 91,758 lifted C++ functions (auto-generated, ~190 MB source)
- func_table.cpp — RecompiledFunc array mapping guest addresses to host functions
Key Technical Details
- TOC base:
0x008969A8(from OPD analysis) - Entry point: OPD
0x00846AE0→ CRT stub0x00010230→0x00010254 - Memory map: Text (8 MB @ 0x10000), Data (0.5 MB @ 0x820000), Rodata (0.2 MB @ 0x10000000), Data+BSS (3.3 MB @ 0x10040000)
- ppu_context: Uses runtime's full struct (GPR, FPR, VR, CR, LR, CTR, XER, FPSCR, CIA)
Building
Prerequisites
- Python 3.8+
- CMake 3.20+
- MSVC 2022 (or Clang/GCC)
- ps3recomp (at
../ps3/or setPS3RECOMP_DIR)
Steps
# Clone
git clone https://github.com/sp00nznet/flow.git
cd flow
# Place your decrypted EBOOT.elf in game/
# (You need a legitimate copy of flOw from PSN)
# Run the recompilation pipeline (takes ~2-3 hours for 92K functions)
python tools/recompile.py --functions-file combined_functions.json \
--skip-find --ps3recomp-dir ../ps3
# Patch the generated header (runtime ppu_context + extern C):
# tools/post_lift.py does it. src/recomp/ is made on your machine by these
# steps and is never committed.
# Build
cmake -B build -DCMAKE_BUILD_TYPE=Release -DPS3RECOMP_DIR=../ps3
cmake --build build --config Release
# Run
./build/Release/flow.exe game/
Note: You must supply your own copy of flOw (NPUA80001). This repo contains only the recompilation toolchain and runtime code, not any copyrighted game data.
Project Structure
flow/
├── README.md
├── CMakeLists.txt # Build config (links ps3recomp runtime)
├── config.toml # Recompiler pipeline configuration
├── analysis.json # 18,159 function boundaries (heuristic)
├── combined_functions.json # 51,658 functions (analysis + OPD)
├── opd_functions.json # 36,827 OPD procedure descriptors
├── elf_analysis.json # Full ELF structural analysis
├── game/ # Place EBOOT.elf + Data/ here (not in repo)
├── src/
│ ├── main.cpp # Entry point, VM/syscall/HLE initialization
│ ├── stubs.cpp # Placeholder function table (no-recomp mode)
│ ├── config.h # Build-time constants
│ ├── elf_loader.cpp/h # ELF segment loader
│ ├── vm_bridge.cpp # Memory API bridge (uint64→uint32, byte swap)
│ ├── hle_modules.cpp # HLE module registration (12 modules, 140 NIDs)
│ ├── indirect_dispatch.cpp # bctrl hash table + trampoline drain system
│ ├── malloc_override.cpp # Bump allocator bypassing CRT heap
│ └── recomp/ # Generated recompiled code (not in repo)
│ ├── ppu_recomp.cpp # 91,758 lifted functions (~190 MB)
│ ├── ppu_recomp.h # Context struct + declarations
│ ├── func_table.cpp # Guest→host function mapping
│ ├── import_stubs.cpp # 140 NID-dispatching import stubs
│ └── missing_stubs.cpp # Empty stubs for unlifted call targets
├── tools/
│ ├── recompile.py # Master recompilation pipeline
│ ├── post_lift.py # Post-process: rename, patch header, fallthrough, bctrl
│ ├── convert_trampolines.py # Fallthrough → trampoline conversion (22K sites)
│ └── gen_stubs.py # Generate missing function stubs
├── extracted/ # Game metadata and analysis outputs
│ └── USRDIR/imports.json # Resolved import table
├── parse_imports_final.py # ELF import table parser (140/140 NIDs)
├── analyze_elf.py # Comprehensive ELF analyzer
└── extract_pkg.py # PKG extraction tool
Related Projects
- ps3recomp — The PS3 HLE runtime library this project builds against
- N64Recomp — The project that proved static recompilation works at scale
- RPCS3 — PS3 emulator whose HLE research makes this possible
Legal
This project does not contain any proprietary Sony code, game binaries, encryption keys, or copyrighted game assets. It is a clean-room reimplementation of PS3 system libraries paired with automated binary translation tools. Users must supply their own legally obtained copy of flOw.
The code in this repo is MIT: see LICENSE. The lifted code
(src/recomp/) is generated locally from your own copy and never committed.
Changelog
v0.6.0 — Real System PRX: libsre cellSpurs (2026-06-19)
- Loads the real decrypted
libsre.prx— Sony's cellSpurs/cellSync library is relocated at a fixed guest base (0x30000000), lifted to per-module-namespaced C, and copied into guest RAM at boot by a new runtime PRX loader. 1003 functions registered, 269 exports published. - cellSpurs imports resolve into real recompiled code —
nid_dispatchnow consults a NID→export registry first; 17 of flOw's imports (all 14 cellSpurs + 3 cellSync barriers) dispatch into libsre via its OPDs instead of HLE stubs, with the module's own TOC. - Eliminated the
0x01000000spin in the cellSpurs object graph — the unresolved-dynamic-PRX-placeholder virtual call that blocked SPURS construction is gone; the realcellSpursInitializeruns and builds a real vtable graph. - SCE PPU relocation format fully decoded — relocations are segment-relative on both ends (
r_infopacks patched- and symbol-segment indices). A whole class of seg1 relocations (OPDs, TOC pointers) had been silently misapplied to seg0; fixing it made the export OPDs resolve correctly. - Game-main-init state bug found —
func_00268504is a state machine:r3=1skips construction (reads uninitialized objects →0x01000000garbage + OOM), whiler3=0runs the subsystem-construction sequence the boot bypass had been skipping — now cleanly, with no garbage, no OOM, no spin. - Hand-edit force-calls characterized — the SPURS-SEED / WORKER-WAKE / SPU-init force-calls are now runtime-toggleable (env vars) and confirmed non-load-bearing for the frame loop; the synthetic SPURS seed is superseded by the real library.
- ps3recomp sister repo — new PRX toolchain:
tools/prx_relocate.py(segment-aware relocator),tools/prx_exports.py(NID→addr export tables),ppu_lifter --symbol-prefix(multi-image linking),elf_parsermodule_info/export fixes, andruntime/prx/prx_loader(load + register + resolve). End-to-end tested against real libsre.
v0.5.0 — Natural Init + SPU Foundation (2026-04-29)
- Natural
_cellGcmInitBodyfires — game's own GCM init runs to completion: cmdbuf at 0x00C005E0–0x00CF05E0 (960 KB), VBlank handler installed, FlipMode set, double-buffered, IO mapping done. - Game-registered VBlank handler runs per frame via cross-repo
g_ps3_guest_caller—cellGcmTickVBlank()from our hand-edit dispatches the game'sfunc_000CBE24every frame. Handler increments TOC-0x54D0 and sets the game-state flag at TOC-0x54CC every Nth tick. - Engine vtable[3] dispatch (
func_000CAA08, the subsystem walker) ticks per frame without exception. 12 subsystems registered intoengine+4..0x30. Per-subsystem vtable[3] also dispatched per frame. - Scene-builder handover — once the game's natural GCM context is detected at
0x101ED198, our placeholder scene-builder is gated off (s_natural_gcm_active). The game's own surface clear (dark blue) is what's now visible. - Direct flip stays on — runtime processes whatever the game emitted into the natural cmdbuf each frame; D3D12 backend presents.
- ~88 FPS sustained, no aborts, no asserts, indefinite stability.
- Disasm-verified the natural lifecycle:
func_000C858Cis genuinely one-shot shutdown (subsystem dtor walk +cellPadEnd/KbEnd/MouseEnd+ tail-call destructor). The game's per-frame outer loop must come from a worker thread spawned by init code we haven't reached. Our hand-edit'sfor(;;)infunc_000C858Csubstitutes for that missing worker. - Pre-init SPURS seed — calls
cellSpursInitializeat engine_run entry on a synthesized 2 KB CellSpurs object so any subsystem that gates on the SPURS magic is satisfied. - vtable[2] is
__cxa_pure_virtual— confirmed:func_00810BB8is the abstract-method abort stub. Calling it is always wrong; kept disabled with documented reasoning.
ps3recomp SPU Stack (sister repo, 11 commits this cycle)
The flOw push surfaced gaps in ps3recomp's SPU/SPURS support. Added a complete PPU-side execution path for any SPU-dependent game:
nid_database.py: 10 → 100+ SPURS NIDs (Attribute*, Taskset*, Task*, Workload*, EventFlag*, JobChain*, LFQueue*).ps3emu/spu_fallback.hregistry —spu_register_ppu_fallback(entry_point, handler, user). Per-game shims register PPU-side equivalents of SPU jobs.sys_spu_image_openparses SPU ELF — validates 0x7F"ELF" magic, extractse_entry(BE u32 at offset 24), writes toimage+4. Fallback registry can now match by real entry point.- SPU thread
args_ea(gpr[8] insys_spu_thread_initialize) plumbed through to fallback handlers;sys_spu_thread_set_argumentupdates it. - Async fallback execution —
sys_spu_thread_group_startspawns one host thread per SPU thread that has a registered fallback (Win32CreateThread/ POSIXpthread_create).sys_spu_thread_group_joinblocks on each thread's finish event and collects worst exit status. sys_spu_thread_get_exit_statusreturns properCELL_ESRCH/CELL_ESTAT/CELL_OKsemantics (matches Sony docs).- Virtual 256 KB local store per SPU thread —
sys_spu_thread_write_ls/_read_lswork end-to-end with 1/2/4/8-byte BE accesses + bounds checks.spu_thread_get_local_store(tid)exposes it to fallback handlers, closing the producer/consumer loop. - Event-queue completion notification —
sys_spu_thread_group_connect_event[_all_threads]records the queue ID;group_joinpushes a completion event{group_id, exit_status, cause, 0}so PPU code blocked insys_event_queue_receivewakes up. - New
sys_event_queue_push_by_id()public helper for non-syscall callers. docs/SPU_FALLBACK.mdwith usage example + LS access pattern.- vt2 watchdog (jmp_buf +
g_vt2_in_progressflag inbridge_sys_lwmutex_lock) for safe spin investigation.
v0.4.0 — Game Reaches main() (2026-03-21)
- 91,758 functions recompiled (up from 51,658) via branch target splitting
- Game reaches main() initialization: loads 5 modules (SYSUTIL_NP, SPURS, USBD, JPGDEC, NET), registers sysutil callback
- Trampoline system for split-function fallthroughs: 22K conversions, 143K DRAIN_TRAMPOLINE sites
- CRT abort redirect via longjmp — workaround for constructor stack overflows caused by backward branch recursion
- Manual dispatch stubs for mid-function entry points
- Module ID mapping fixes in cellSysmodule
- Root cause identified: lifter backward-branch recursion across split-function boundaries
- Next: implement sys_mmapper_allocate_address, fix lifter backward branch handling
v0.3.0 — Game Boots (2026-03-15)
- Lifted 51,658 functions to C++ (up from 18,159)
- Added 64-bit instruction lifting: rldicl, rldicr, rldic, rldimi, indexed loads/stores, FP fused multiply-add, FP compare/conversion
- Wired HLE modules with NID-based dispatch (12 modules, 140 handlers)
- Implemented real HLE: sys_initialize_tls, sys_process_exit, cellSysmoduleLoadModule
- Fixed ps3recomp NID computation (16-byte suffix, little-endian)
- Game boots, enters recompiled CRT code, initializes TLS successfully
- 37 MB native executable
v0.2.0 — Runtime Alignment (2026-03-12)
- Updated to ps3recomp v0.3.1 API
- Added ELF segment loader, recompile pipeline, stubs system
- Aligned CMake build with ps3recomp project template
v0.1.0 — Binary Analysis (2026-03-10)
- Extracted flOw from PSN package
- Discovered 18,159 functions, resolved 139/140 import NIDs
- Created project structure and toolchain integration
"Eat or be eaten."
