# How the Code Works — an Engine Tour A guided tour of Xenosaga III's engine as recovered by the decompilation. Addresses are clickable into `symbol_map.txt` / `functions/`. ## The shape of the machine The PS2 is six processors in a trenchcoat, and the game uses all of them: - **EE (R5900)** — main CPU. Everything in `slus/` and `ov*/` runs here, including 128-bit MMI SIMD instructions throughout. - **VU0** — vector co-processor used in *macro mode* (inline coprocessor-2 instructions inside EE code; the `TiVu0` module wraps it). - **VU1** — geometry processor. Runs the 28 microprograms from `.vutext` (see `slus/vutext.s`) which build GIF packets and `XGKICK` them to the GS. - **GS** — rasterizer, driven entirely by those packets. - **IOP** — I/O processor running separate `.irx` modules: `sio2man` (pads/cards), `mcserv`, `padman`, `cri_adxi` (audio streaming), `iopsub`. - **SPU2** — audio DSP, fed by CRI's ADX middleware. ## Boot flow 1. `entry()` at `0x00120008` — three-phase BSS clear (with 128-bit stores), runtime init, kernel syscalls. 2. `main()` at `0x00181af0` — stashes argv, then its **first call jumps into the overlay region** (`0xa80000`): the boot/menu logic lives in overlay space from the very start (the SLUS ELF embeds OV01's image, so the window is pre-populated). 3. From there: an event-driven main loop. ## The event loop The dispatcher at `0xa81718` returns packed 32-bit event words: ```c event = FUN_00a81718(); channel = event >> 0x18; // top 8 bits: channel code = event & 0xffffff; // low 24 bits: function pointer if (code != 0xa81718 && channel != 1) (*(code *)code)(); // dispatch ``` Cheap, cache-friendly, and everything in the game hangs off it. (Anyone patching the main loop must preserve this packing.) ## Overlays — one window, three tenants `OV01` (credits/menu), `OV02` (battle), `OV04` (bonus/debug) all load into the **same memory window at `0xa80000`**, bank-switched as needed; the main executable references them through the `.DVP.ovlytab` section. Four thunks in SLUS jump to fixed addresses in OV02's bank. There is no OV03 on disc — a reserved slot. ## Engine subsystems, by prefix | Prefix | Origin | What it does | |---|---|---| | `Ti*` | Monolith in-house | Animation/physics/environment toolkit, 15 modules: camera solver, motion solver, collision (`TiDynamicsColl`, `TiGlobalColl`), motion blend, parts animation, water sim (`TiWatSim`, `TiWater`), wind (`TiWindAnim`/`Base`), VU0 math (`TiVu0`), animation curves | | `XAct*` | Monolith in-house | "Xeno Actor" — the entity system. Characters are `XAct_Actor` instances; resources load through a typed `XAct_Rsrc_*` enum | | `LSC_*` | Monolith in-house | Script containers: `LSC_Create` allocates a container with callback tables; `LSC_ExecServer` checksums script data and validates entry filenames before execution | | `SRD_*` | Monolith in-house | Disc I/O abstraction with runtime `DVD \| HOST` (devkit) source switch | | `nj*` | Sega Ninja library | Licensed Dreamcast-era math/string utilities | | `mwPly*`, `mwSfd*`, `mwAdx*`, `ADXM_*`, `MWSTM_*` | CRI middleware | SOFDEC video player, ADX audio, streaming — the best-recovered code in the package because CRI's error strings name their own functions | ## The data-driven core The game is mostly *data*: an episode/scenario system (`g_sEpisode` global with `.pCut`, `.pSoundBuf`, `.pszStrWork` fields, recovered from assertion strings) reads per-episode scripts via `psEpReadScript(pszEpisode[nEpisode], pWork)`, which flow into the LSC containers above. Actors, animations, models, and effects are all loaded by typed resource requests — see [FILETYPE-INDEX.md](FILETYPE-INDEX.md) for what each extension means. ## Reading a decompiled function ```c // 0x0012f058 mwPlyCreateSofdec ← address + recovered name // size=1852 bytes // strings: ← every literal this function loads; // 'E20010703C mwPlyCreateSofdec: create error' usually reveals purpose void mwPlyCreateSofdec(undefined8 param_1, undefined4 *param_2) { ... } ``` - `FUN_00xxxxxx` — function not yet renamed; the address is its identity. - `undefined4` / `undefined8` — Ghidra couldn't infer the type (most variables; type recovery is the current frontier). - `DAT_005xxxxx` — a global; check the section map in the package README (`.data`/`.rodata`/`.sdata`/`.bss` ranges) to see what kind. - `// strings:` annotations + `symbol_map.txt` + `strings.txt` are the three navigation tools: find a string, find its function, walk callers. ## What's recovered and what isn't Recovered: full control flow (zero truncations), 223 original function names, ~60 original source filenames, complete cross-binary call graph, all string references, the VU1 microcode. Not recovered: variable types and struct layouts (the ELF was stripped — inference only goes so far), most function names, original comments (forever lost). The path forward is struct recovery — see [GOALS.md](GOALS.md).