Files
Sylpheed/docs/re/static-route-recovered.md
sylph-decoder e07c1de1b5 re: I cannot measure this emulator's clock -- and that answers the port's 2%
The port put two of my pages against each other: settle->plate 2.135 s and one
focus-ring revolution 2.177 s, both a declared 120 units during a static hold,
2% apart against a 6 ms run-to-run agreement. Fair challenge.

The resolution is that the question assumes a stable wall clock. Same interval,
same container, same day: 2.138, 2.132, and 2.549 s -- a 19% swing, caused by
adding --log_ui_draws=true. The 2% is a fifth of that. The two pages were never
in conflict about the game; they are three readings of one declared quantity
through a clock that moves. What settles the quantity is the disc.

Wall clock cannot separate the hypotheses, so I tried to measure frames instead.
Both instruments are recorded as failures rather than published as numbers:

  * Canary's own [UI-CAP] counter -- the one that produced the corpus's 28.5 fps
    -- costs a third of the frame rate. 300 frames in 16.567 s = 18.11 fps on a
    screen that gives ~28 without it. That reclassifies 28.5 as a load-dependent
    lower bound; it does not overturn it.
  * A distinct-frame counter over the spinning ring FAILED its decisive control:
    15.88 fps against the game's own 17.59 in the same window, 10% low, so the
    ring does not change on every presented frame. Its static control also read
    2.63 instead of ~0. Dead, not tuneable, per METHOD.md.

The rule that follows, and it applies to everything I hand the port: a measured
interval landing near a round number of declared units almost certainly IS that
number of units. Ship the units.

Also recovered here, because the same question needed it: the static PPC route.
Four tools open /work/xenia-rs/sylpheed.db and nothing in this repository builds
it -- no disassembler, no PPC decoder, and default.xex is encrypted (zero
plaintext "GamePart"). Xenia decompresses the image at load, so dump_image.py
reads it out of guest memory and validates it against the corpus's own landmarks:
the 29-entry GamePart id table at 0x820A1630 and the Xbox 360 D3D runtime
strings. String search and table dumps work again; instruction-level work does
not, and the present interval I wanted is an immediate, not a string.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014voBspJ6kFncNErZJuZcLw
2026-08-29 13:16:02 +00:00

4.5 KiB

🟡 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.dbname_block_bases.py, archive_naming.py, isl_cmdtab.py and the census work that produced most of the sub_82xxxxxx findings in 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 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). 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).