Files
Sylpheed/docs/port/MISSION.md
Sylpheed RE agent 965c3f70fd port: make the Godot menu shell the agent's primary objective
Sets a new mission: boot the real disc through developer splash -> intro video
-> title -> main menu -> submenus in Godot 4, interactively, with no gameplay,
no 3D and no emulator.

docs/port/MISSION.md defines it -- eight gated milestones, each finished by an
ARTIFACT rather than by compiling, plus the Ready Room as an explicitly gated
stretch goal with a one-iteration probe that decides go/no-go. GP_READY_ROOM is
1106 entries with 6 recoverable names and is ISL-scripted, so it is either a
week or a quarter, and the agent must not start it on its own authority.

Architecture, per the user's decision: the Godot project is INDEPENDENT of the
Rust viewer and never reads a disc format. An offline Rust exporter converts the
disc into open formats; Godot reads only those. No GDExtension, no Rust in the
Godot project, and sylpheed-viewer is off limits -- it stays the human's
verification tool with its static-data rule intact.

docs/port/FORMAT.md specifies the open format, versioned, because modding is the
port's second goal and that makes the layout a deliverable rather than a temp
directory: JSON over XML (Godot parses JSON natively; its XMLParser is SAX),
names never hashes, provenance in every generated file, and unknowns listed
rather than guessed.

The discipline the whole thing rests on is the derived/authored split. `export/`
is regenerated wholesale and never hand-edited; `authored/` is hand-written and
survives a re-export. Three things this milestone needs are NOT on the disc in
any decoded form -- which button does what, paint order, and menu sound cues --
so they live in `authored/` with a stated `why`. Deleting an authored entry
because the exporter can now emit it IS the measure of progress.

`export/` is gitignored: it is generated from the user's own disc and this stays
a clean-room repo.

Container: adds a pinned Godot 4 (windowed under Xvfb for screenshots, plus a
headless wrapper). ffmpeg already carries libtheora, which is the video target --
Godot 4 plays only Ogg Theora natively and the disc's ADV.wmv is WMV3/WMA Pro.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:11:35 +02:00

8.8 KiB
Raw Blame History

Primary objective — a playable menu shell in Godot

Status: active, set 2026-08-28. This supersedes "work the RE backlog" as the agent's primary objective. Reverse engineering continues, but it is now in service of this goal: an item earns attention because the port needs it.

What we are building

A Godot 4 project that boots to an interactive main menu using the player's own disc, through the sequence the real game uses:

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

No gameplay. No 3D. No emulator. The success condition is a human pressing a d-pad and Ⓐ and moving through those screens with the right art, the right animation, the right music and the right transitions.

Ready Room is a gated stretch goal. See §6 — do the probe first, then stop and report rather than committing to it.

1. The two halves, and the wall between them

The Godot project never reads a disc format. It reads only open formats. The conversion happens offline, in Rust, and the wall between the two is the point:

  disc  ──▶  crates/sylpheed-export  ──▶  export/     ──▶  port/  (Godot 4)
            (Rust, uses sylpheed-formats)  JSON + PNG + OGG          reads ONLY
                                           + Ogg Theora              open formats
  • No GDExtension. No Rust in the Godot project. If Godot needs something it cannot read, the exporter's job is to emit it in a form Godot can, not to bridge the two runtimes.
  • crates/sylpheed-viewer is not involved. The Explorer is the human's tool for exploring and verifying the RE work. It stays Rust/Bevy, it keeps its static-data rule, and it is neither a dependency of nor a dependent on the port. Do not refactor it "to share code with" the port.
  • sylpheed-formats is shared, because it is the decoder and there must be one.

2. The format is a deliverable, not a dump

The second goal of this port is modding. That makes the exported layout part of the product, not a temp directory:

  • JSON, not XML. Godot parses JSON natively with JSON.parse_string; its XMLParser is a SAX-style API that would need a hand-written binding for every schema. JSON is also what every external tool a modder might reach for already speaks.
  • Names, never hashes. A file is screens/title/main_menu.json, not 0x90822a39.json. Where the disc's own name was never recovered — the six *2D archives and GP_READY_ROOM, see the atlas — emit a stable synthetic id and record in the file that the real name is unknown. A modder must be able to tell a recovered name from one we invented.
  • A tree that means something. screens/, sprites/, audio/music/, audio/sfx/, video/, text/<lang>/ — organised by what a person is looking for, not by which archive it came out of.
  • Provenance in every generated file. Source archive, entry index, and the exporter version. This is what keeps the export auditable against the disc instead of drifting into an unverifiable fork.

The schema lives in FORMAT.md and is versioned. Changing it is a deliberate act with a version bump, not a silent edit.

3. Derived vs authored — keep these apart

This is the discipline that matters most, and it is the corpus's own rule applied to a new artefact.

export/ authored/
produced by running the exporter a human or the agent, by hand
contains what the disc says what we decided
edited by hand never always
committed to git no (see §4) yes
when regenerated overwritten wholesale untouched

Some things this milestone needs are not on the disc in any form we have decoded, and they must live in authored/ with a stated reason:

  • Which button does what. ptbtn01…05 are sprites with baked-in labels. A human can read "NEW GAME" off the image; there is no field that says so. The mapping button → GamePart id is an authored guess until code says otherwise.
  • Paint order. Solved at runtime, unsolved from the file — the declaration table is provably not it. For these six screens, author the z-order and mark it authored. Do not let a hand-tuned order leak into export/ and later get mistaken for a disc fact.
  • Sound-cue bindings. Which cue fires on move / confirm / back.

Every authored/ entry carries a why field. When a later iteration decodes the real answer, the authored entry is deleted and the exporter emits it instead — and that transition is the measure of progress.

4. Do not commit game assets

export/ is generated from the user's own disc and must be gitignored. The repository stays a clean-room port: code, schemas, authored mappings and documentation. Anyone building it supplies their own disc. If you find yourself committing a PNG of a game sprite or a transcoded video, stop.

5. Milestones, each with a gate

Do these in order. A milestone is done when its artifact exists, not when the code compiles.

Milestone Gate (the artifact)
P0 Exporter skeleton; one screen and its sprites to export/ export/screens/title/main_menu.json validates against FORMAT.md, and the PNGs open
P1 Godot renders that screen statically at 1280×720 A screenshot from Godot beside sylpheed-cli screen render of the same build — they should agree
P2 Keyframe animation The main menu's buttons slide in. Measure the time unit first (§7)
P3 Splash → title, with the transition Godot runs the two screens back to back unattended
P4 Intro video ADV.wmv plays in Godot with audio (§7)
P5 Main menu: d-pad navigation, focus states, Ⓐ into a submenu, B back A human clicks through it
P6 Audio — menu BGM and move/confirm SFX Sound on the gate in P5
P7 New-game intro video after NEW GAME Plays, then returns to a defined state
S1 Gated — Ready Room. Run the probe in §6 and stop A written go / no-go

6. The Ready Room probe — do this early, it is one iteration

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 could mean a week or a quarter, and one cheap test tells you which.

The probe: our screen catalog enumerates bundles by content, not by name — is_build / is_composable read the bytes. Unrecoverable paths therefore 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 screens are 2D at all or whether the room is 3D with a UI overlay — because if it is 3D, this milestone is out of scope by definition and the answer is no-go, not "try harder".

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

7. Two decisions already made, and one measurement owed

Video: transcode to Ogg Theora. ADV.wmv is WMV3 video with WMA Pro audio, 1280×720 at 30 fps, 137 s. Godot 4 plays only Ogg Theora natively. Transcode with ffmpeg and keep the exact command in the exporter's config so it can be re-run with different settings. Theora at 720p is not great; if the result is visibly poor, say so and propose the FFmpeg-GDExtension fallback — do not silently adopt a dependency. Only ADV.wmv and the one new-game intro are in scope; the disc holds 3.3 GB of video and transcoding all of it is not this milestone.

Screens: GP_TITLE.pak is the whole title-side tree. Build 4 is the title with the animating wordmarks, build 5 the five-button main menu, builds 6/8/9 are submenus, and the developer splash is the palogo bundle in the same archive. Buttons are identifiable as data: element kind 0x3002, ordered top-to-bottom by resting Y, each paired with an f-suffixed highlighted variant. You do not need the undecoded navigation graph to make a vertical menu work.

The measurement owed: what is a keyframe time? Values run 16…269 on the title build. 60 Hz frames would make the title intro ~4.5 s, which is plausible and untested. Settle it against a capture before building animation on it — this is exactly the kind of number that silently poisons everything downstream. ui-resting-pose.md already establishes that a keyframe is the start of a ramp, not a pose that is held.

8. What is explicitly out of scope

3D, gameplay, HUD, missions, the emulator, save/load, localisation beyond English, and any archive outside GP_TITLE, tables.pak, sound.pak and dat/movie/ — except for the Ready Room probe in §6.