# 🟡 The static PPC route was lost with the container migration — the image is back, the disassembly is not **Status:** ✅ the **image** is recovered and validated; 🔴 the **disassembly database** it is analysed with does not exist in this repository at all, and never did. 2026-08-29. ## What broke Four tools in `tools/re-capture/` open a DuckDB database at `/work/xenia-rs/sylpheed.db` — `name_block_bases.py`, `archive_naming.py`, `isl_cmdtab.py` and the census work that produced most of the `sub_82xxxxxx` findings in [`INDEX.md`](INDEX.md). In this container: ``` $ ls /work/xenia-rs ls: cannot access '/work/xenia-rs': No such file or directory ``` 🔴 **And nothing in the repository builds it.** A grep for `duckdb` finds only *consumers*; there is no disassembler, no PPC decoder vendored, and `capstone` is not installed (and `pip install` is refused here by PEP 668). So the static route is four read-only clients of a producer that is not in the tree. ⚠️ **This is not a small gap.** Every finding that cites a function address — the GamePart registry, the challenge gate, the ISL command table, `PlayerParams`, the boot sequencer — is currently **unre-checkable in this container**. They are not wrong; they are unverifiable, which is a different and quieter problem. ## `default.xex` is not a substitute The disc's executable is encrypted and LZX-compressed: ``` $ xxd -l 16 /disc/default.xex 00000000: 5845 5832 0000 0001 0000 3000 ... XEX2......0. $ strings -a /disc/default.xex | grep -c GamePart 0 ``` The header is intact — `XEX2`, media id `535107D4`, original PE name `default.pe` — and everything after it is noise. No decrypt/decompress exists in the tree either. ## ✅ What is recovered, and how it is validated Xenia decrypts, decompresses and relocates the image at load, so a **running guest holds the flat VA image** the corpus calls the `.pe` (`VA = 0x82000000 + offset`). `tools/re-capture/dump_image.py` reads it straight out of `/dev/shm/xenia_memory_*` — no debugger, no emulator patch, no pause. ``` $ tools/re-capture/dump_image.py /sylph-home/re/sylpheed-image.pe wrote ... 4194304 bytes VA 0x82000000..0x82400000 validated: GamePart id table + D3D runtime strings; 1013/1024 non-empty 4K pages ``` **The validation is the corpus's own landmarks, not the tool's.** A mis-based or partial dump fails both: * `0x820A1630` holds the **GamePart id table** — 29 `.rdata` pointers resolving to `GP_TITLE` (0) … `GP_TEST` (28), with `GP_CHALLENGE` at **26**, exactly as [`challenge-mission-gate.md`](challenge-mission-gate.md) records; * the image carries the **Xbox 360 D3D runtime's own error strings** (`ERR[D3D]: Unanticipated CPU_INTERRUPT`, `D3D9D.LIB`), which only the real loaded executable has. So string search, table dumps and pointer chasing work again today. **What does not** is anything needing decoded instructions: no `mnemonic`/`operands`, so no xref search, no base solving, no call graphs. ⚠️ It also depends on a **booted emulator**, which is a bad dependency for the foundation of the static corpus. Dump once and keep the file. ## 🔵 For the human — what is actually needed Not the image; that is solved. What is missing is the **producer of the database**, and its schema, which the four consumers pin exactly: | table | columns used | |---|---| | `functions` | `address`, `end_address`, `name` | | `instructions` | `address`, `mnemonic`, `operands` | | `strings` | `address`, `content` | An XEX unpacker would also be worth having on its own, so the static route stops needing a running emulator to bootstrap. ## What it blocks right now The one open question this iteration wanted it for: **the game's present interval**, i.e. the step from *one submitted frame* to *1/30 s of real time*. "2 units per submitted frame" is already grounded and emulator-independent — it was read off a frame-indexed draw capture ([`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md)). The remaining link is `D3DPRESENT_PARAMETERS.PresentationInterval` at device creation: `_ONE` means the game runs at the console's 60 Hz vblank, `_TWO` at 30. That is a constant loaded into a register, so **finding it needs disassembly, not string search** — the image contains the D3D runtime but the interval is an immediate, not a string. Settling it would move Q1's seconds-per-unit from **measured** to **decoded**, and it is the only part of the UI clock still resting on a wall clock this container has now been shown to move by 19 % ([`present-rate-instrument-failed.md`](present-rate-instrument-failed.md)).