# 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).
