This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/port/MISSION.md
Sylpheed RE agent 0f1c0f4e14 re: the JP-locale capture is not blocked -- I stopped one grep too early
Last iteration I wrote into MISSION.md that a Japanese-locale capture is
impossible here, because user_language is DECLARE_int32 at four call
sites with no DEFINE and no entry in xenia-canary.config.toml. That is
true, and it was not the question. The language is PERSISTED:
kernel_state.cc builds XConfig over <storage_root>/xconfig.settings,
SetDefaults() only supplies a value when the file has none, and the file
is writable. Checking where a setting is stored rather than where it is
configured turned "blocked, needs a human decision" into a two-line edit.
Withdrawn from MISSION.md; METHOD and REFUTED lines added.

The field is located from struct landmarks rather than a hard-coded
offset, and the check re-runs on every invocation so it fails loudly if
the layout moves:

  music_volume 0.7f at User+449  -> BE float at 2727 -> User base 0x8e6
  language          at User+44   -> reads 1 (kEnglish) at 0x912
  country           at User+64   -> reads 103 (US) at 0x926

XLanguage::kJapanese = 2 (xbox.h:307). set_console_language.py wraps it
with a backup and a --restore.

The capture itself is still NOT taken, for a smaller reason than I
claimed. A run with the locale set to Japanese booted fine but never
reached the title in 787 s: wait_title.sh's green-(A) oracle never fired
and burst-sampling found no frame correlating above 0.18 with either
build-7 render -- the run sat in the attract loop. So it needs a longer
or pad-driven run, not a rebuilt emulator. Emulator stopped, lock
cleared, locale restored to English.

Nothing is decided about the keyframe-time association or the rest()
rule; this only changes what standing between us and deciding them.
2026-08-29 00:05:58 +00:00

10 KiB
Raw Blame History

Primary objective — answer everything the menu port needs

Status: active, set 2026-08-28. This replaces "work the RE backlog" as the agent's primary objective. It does not change what the agent does — it is still reverse engineering — it changes what earns attention: an item is worth doing when the menu port is blocked on it.

Who builds what

You do not build the port. A separate agent will build it, from what you produce. Your deliverable is decoded, verified, written-down answers plus the reference data that proves them.

container agent (you) port agent
decodes the disc — consumes your answers
measures the running game — no emulator
writes docs/re/ and docs/port/HANDOFF.md reads them
Godot project, asset pipeline, transcoding

If you find yourself designing an export schema or writing GDScript, you have crossed the line. Stop and go back to the question you were answering.

crates/sylpheed-viewer is also not yours to change for this objective. The Explorer is the human's tool for exploring and verifying the RE work, it keeps its static-data-only rule, and the port does not depend on it.

The target

Someone else has to build this, from your answers alone:

developer logo splash  →  intro video  →  title / PRESS Ⓐ  →  main menu  →  submenus

No gameplay, no 3D, no HUD, no missions. If an answer is not needed to put those five screens on a display and let a person move through them with a d-pad and Ⓐ, it is not in this objective.

What is already answered

Do not re-derive these. They are in docs/re/ and the disc atlas:

  • The screen archive. GP_TITLE.pak holds the whole title-side tree — build 4 the title with animating wordmarks, build 5 the five-button main menu, builds 6/8/9 submenus, and the developer splash as the palogo bundle in the same pak.
  • Buttons are identifiable as data. Element kind 0x3002 is a button, 0x0 decoration, 0x10 a primitive; buttons sort top-to-bottom by resting Y; each pairs with an f-suffixed highlighted variant.
  • The screen vocabulary. The GamePart id table, 29 entries at .rdata 0x820A1630, confirmed by the executable's own factory-registration strings.
  • The resting pose rule — the hold, not the longest dwell — and that a keyframe is the start of a ramp.
  • Screen composition, pixel-accurate for the tutorial pause menu and the title main menu, via sylpheed-cli screen render.
  • The logo splash is not a video. logo1logo4 are manifest-bound with no .wmv on the disc; the splash is the RATC screen, which already renders.

The open questions — these are the objective

Ordered by what blocks the port earliest. Each is done when its gate exists: a written docs/re/ result with the evidence, and reference data committed alongside it.

Question Gate
Q1 What is a keyframe time? Values run 16…269. 60 Hz frames would make the title intro ~4.5 s — plausible and untested. Also: is the ramp linear, or eased? A measured answer against the running game, not an inference. Everything animated downstream depends on this number
Q2 Which build is which screen state? Confirm build↔state for splash, title/PRESS Ⓐ, main menu and each submenu A table, each row confirmed against a capture of the real screen
Q3 Paint order for these six screens. Solved at runtime, unsolved from the file — the declaration table is provably not it Either a rule derived from the bundle, or six measured orders and a clear statement that no file-side rule was found
Q4 What does each button do? Labels are baked into the sprites; no decoded field says which GamePart a button opens Button → GamePart id, from code or from driving the game. Say which
Q5 Navigation semantics. Initial focus, wrap-around at the ends, whether left/right does anything, what B does on each screen Observed behaviour, per screen
Q6 The boot sequence, and what drives it. Order is observable; the data or code that sequences it is not decoded. Include the attract loop and what returns to the title The sequence, plus whatever the game reads to decide it
Q7 Transitions. What happens visually between screens — the pteff00.prm quads, a fade, a cut — and its timing Described and timed against a capture
Q8 Menu audio. Which BGM per screen; which cue on move / confirm / back / error. The cue table is complete; the event binding is not Cue names bound to events, with how you established each
Q9 Video binding. Which movie is the boot intro vs the new-game intro; whether playback is skippable and what ends it Named movies plus the playback rules
Q10 What are a music bank's sub-waves? BGM_001.slb is three sub-waves — 10 KB, 4.47 MB, 4.67 MB — and we currently concatenate them blindly into one 347 s track. Two near-equal halves could be intro + loop, or two variations, or two halves of one piece. A menu that loops its music needs to know which The role of each sub-wave, established for at least the menu BGM. "Concatenate" is a decision, not a default — right now it is a default nobody chose
S1 Ready Room probe. DONE 2026-08-28 — no-go. It is 2D and enumerates fine, but the pak is briefing/tactical-map content, not the Ready Room menu go/no-go written

🟡 Needs one more run — a Japanese-locale capture

Recorded rather than worked around, per "do not improvise around a blocker".

Two questions have converged on needing one capture we cannot take: a running capture of GP_TITLE build 7, the Japanese title screen.

  • Q1's keyframe-time association. The shifted reading is favoured 26× by a calibration-free measurement, and the only render it changes on the whole disc is build 7 (ui-keyframe-time-unit.md).
  • What rest() should return for a plateau-less element. The one element that discriminates, ptlogo_eff3.t32, is also in build 7 (structures/ui-resting-pose.md).

🔴 The "cannot" was wrong, and is withdrawn (2026-08-29). It is true that user_language is only ever DECLARE_int32 here, with no DEFINE and no entry in xenia-canary.config.toml, so there is no flag to pass — and passing an unknown one is specifically dangerous, because run-canary's own header records that xenia calls ShowSimpleMessageBox from ParseLaunchArguments before logging starts. But the cvar is not the only route, and I stopped at the first one I checked.

The language is persisted, and canary's own file is writable. kernel_state.cc builds XConfig over <storage_root>/xconfig.settings, which exists here at /sylph-home/re/.local/share/Xenia/xconfig.settings (6 680 B). user.language is a big-endian u32 at file offset 0x912, located by three independent landmarks rather than guessed:

landmark expected found
music_volume (User+449) 0.7f BE float at 2727 → User base 0x8e6
language (User+44) 1 = kEnglish, the hard-coded default 1 at 0x912
country (User+64) United States 103 at 0x926

XLanguage::kJapanese = 2 (xbox.h:307). Writing 2 there and restoring afterwards is using canary's own persistence, not patching its code.

🟡 Still not settled, for a smaller reason. A run with the locale set to Japanese booted fine, but never reached the title in 787 swait_title.sh's green-Ⓐ oracle never fired, and burst-sampling 8 frames found none correlating above 0.18 with either build-7 render. The run sat in the attract loop. So this needs a longer or pad-driven run, not a rebuilt emulator. The locale was restored to English afterwards.

⚠️ Neither question blocks the five menu screens. Q1's interpolation law is settled and only multi-keyframe absolute timing is open; rest() differs from its alternative on one element across all five screens, and the current answer there is the defensible one.

Known unknowns — say so, do not fill them in

Some of these may turn out to be undecodable. That is a valid, useful answer, and it is better than a guess, because the port agent will otherwise have to author the mapping by hand and needs to know it is authoring rather than transcribing.

For each question, the answer is one of:

  • decoded — here is the field, here is the disc-wide check;
  • measured — not on the disc in any form we found, but here is what the running game does, and here is the capture;
  • undecodable, with reach — we looked here, here and here, and this is why it is not there.

Never a fourth thing. In particular: if Q4 ends as "read the labels off the sprite images by eye", say exactly that — it is then an authored mapping on the port side, not a disc fact, and mislabelling it would put a guess into the port wearing the badge of a measurement.

The Ready Room probe (S1) — one iteration, then stop

GP_READY_ROOM.pak is the largest UI archive on the disc, 1 106 entries, and only 6 of its names resolve. It is also ISL-scripted. That is either a week or a quarter, and one cheap test tells you which.

Our screen catalog enumerates bundles by content, not by name — is_build / is_composable read the bytes — so unrecoverable paths do not necessarily mean unrenderable screens.

sylpheed-cli screen list "$SYLPHEED_DISC/dat/GP_READY_ROOM.pak"
sylpheed-cli screen render --build <n> "$SYLPHEED_DISC/dat/GP_READY_ROOM.pak" /tmp/rr.png

Report how many builds it finds, whether any composite looks like a Ready Room, and whether the room is 2D at all or 3D with a UI overlay — if it is 3D the answer is no-go by definition, not "try harder".

Then stop and write the go/no-go. Do not start Ready Room work on your own authority.

Handing it over

HANDOFF.md is the single page the port agent reads. Keep it current as you answer questions: it is a summary with links into docs/re/, not a second copy of the findings. An answer that is not reachable from HANDOFF.md has not been delivered.

Out of scope

3D, gameplay, HUD, missions, save/load, localisation beyond English, the Godot project itself, any asset pipeline, and any archive outside GP_TITLE, tables.pak, sound.pak and dat/movie/ — except for the S1 probe.