The Decoder proposes a per-screen rate: ~57 units/s for the title, ~35-40 for the splashes, on the ground that a splash at 60 runs 1.5-1.7x too fast. That would be a timeline cause for the human's finding 4, so it matters. THE PORT DID NOT MOVE, and this is why. The 160-unit hold is identifiable: exactly one interval in either splash is 160 units, the DEVELOPER splash's a=255 plateau at t=30..190, inside its declared group t=0..210. So: the 160-unit hold measured 4.514 guest s (1 run) -> 35.4 units/s the 210-unit group AROUND IT measured 3.37/3.50/3.51 s (3 cold boots) -> 60.7 A sub-interval cannot outlast the interval containing it. That is not two methods disagreeing about a rate, it is arithmetic, and one of the two is wrong. Both dwell measurements corroborate ~60 on exactly the two screens now put at 35-39: publisher 255 units in 4.30/4.60/4.37 = 57.7, developer 210 units in 3.37/3.50/3.51 = 60.7, the developer agreeing with its declared value to 1.1 % and two of its runs to 0.3 %. At 35.4 the declared groups would run 5.93 s and 7.20 s -- each splash ~70 % longer than three cold boots measured it. The one escape route is named rather than dismissed: the new figure is in GUEST seconds and the corpus in wall-clock. The known gap between those is Canary at ~28.1 fps against 30, which is 6 % and the wrong direction to close 1.7x. It is their instrument and their clock, so it is asked (H7), not ruled out. ⚠️ The STRUCTURAL claim may still be right -- one rate covering every screen is what is under challenge, and the title's 56.8 does sit ~5 % off the splashes' 58-61. If a per-screen rate is real the port wants the MECHANISM, a field or a GamePart constant, not two authored numbers. That is their next item. Recorded in authored/timing.json so the next iteration does not quietly adopt it, with an explicit instruction not to split the difference: averaging two measurements that cannot both be true is not a third measurement. Also this iteration: H6 -- answered in principle, blocked in practice. They are right that the blend is a decoded disc field (T8aD +0x04 bit 0x02) and that deriving beats transcribing, which would delete an authored entry. But sylpheed-formats does not EXPOSE +0x04 -- Element surfaces kind (+40), parent (+32), pivot, keyframes, focus_link -- and sylpheed-export consumes formats by git TAG, not workspace path, so I cannot reach the parser. Asked: surface the word and tag it. 🔴 And a negative worth having, since it is the obvious thing to try: kind_raw in this export is NOT that field. Its bit 0x2 against the additive map over four screens is ANTI-correlated -- 0 of 14 mapped elements have it set and 9 unmapped ones do. Anyone reaching for `kind_raw & 2` gets the additive set exactly inverted. H5 -- closed. My shared-element hypothesis is refuted: build_12 and build_15 have identical declaration bodies, so identical statistics are one fact. The pgloading_loop5 localisation stands and is the better answer. ✅ And the good news, recorded where the splash work lives: "the declared keyframe timeline reproduces the captured splash" settles in favour of the timeline -- 50 captured alphas, worst error one level in 255, no renderer in the chain. The port's splash keyframes were never the defect. Which leaves the RATE as the remaining candidate for what the human saw, and the rate is what this commit declines to change. Not settled: which of the two splash measurements survives; where a per-screen rate would come from; the +0x04 exposure; H1 (key repeat). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
Sylpheed
A clean-room reverse engineering and port project for Project Sylpheed: Arc of Deception (Xbox 360, 2007).
Three things live here, in one repository so that a change spanning them lands as one commit:
| The decoders | crates/sylpheed-formats — the disc's formats, read and verified disc-wide |
| The port | port/ — a Godot 4 project, plus crates/sylpheed-export which converts a disc into the open asset tree it reads |
| The corpus | docs/re/ — what has been reverse engineered, with its evidence, its retractions and its dead ends |
You need your own copy of the game. No game content is in this repository and none ever will be. The exporter reads the disc you supply.
The oracle is the real game
sylpheed-cliand the Explorer are tools for verifying our decoding. They are hypotheses under test and they have been wrong. When something must be checked against the truth, the truth is the game running in Xenia Canary, captured — not any renderer of ours.
This is stated first because getting it backwards is the most expensive mistake this project has made.
Layout
crates/
sylpheed-formats/ the decoders. Disc-wide verified; the corpus is its spec
sylpheed-cli/ headless tools -- render a screen, dump a table, probe audio
sylpheed-viewer/ the Explorer: a human's window onto the disc. STATIC data only
sylpheed-export/ disc -> the open, moddable asset tree
port/ the Godot 4 project. Reads open formats ONLY
authored/ decisions that are NOT on the disc, each with its reason
data/
base/ generated by the exporter. Gitignored, never hand-edited
mods/ drop-in overrides. Yours
docs/
re/ the corpus: findings, refutations, method traps
game/ how the game is navigated -- menus, modals, flight
port/ the port's mission, its handoff contract, modding rules
-- and RUNNING.md, which is how you actually start it
agents/ how the agent team works together
tools/ capture harnesses, probes, the share tool
exchange/ transient inter-agent files. NOT in git
docker/ the agent containers
Where to start
docs/re/INDEX.md— what is decodeddocs/re/REFUTED.md— what has been tested and dieddocs/re/METHOD.md— traps this project has already paid fordocs/game/navigation.md— how the game is navigateddocs/port/MODDING.md— why the asset tree looks the way it does
Xenia Canary is a separate repository: it is a fork tracking upstream, and it carries our instrumentation.
Conventions
Confidence is per claim, never per document: ✅ CONFIRMED · 🟡 PROBABLE ·
❔ HYPOTHESIS · ❌ REFUTED. A withdrawn result is kept with its reasoning
rather than deleted — that is why the numbers here can be trusted.