The port is deliberately separate from the reverse-engineering project: its own
repository, its own clone, its own container. Two writers in one working tree
means files change under whoever is mid-edit and a `git add -A` by one sweeps up
the other's work -- which happened today in the Reborn tree, so this is set up
not to repeat it.
The wall: Godot never reads a disc format. An offline Rust exporter converts the
user's disc into JSON + PNG + Ogg, and the Godot project reads only that. No
GDExtension, no Rust in port/. Beyond the practical reason -- Godot cannot read
IPFB, RATC, T8aD, XMA or WMV -- there is the design one: modding is a goal, and
if the runtime reads the original formats then modding means reverse
engineering, whereas if it reads JSON it means opening a file.
The decoders come from sylpheed-formats PINNED BY REVISION (47f423f), not
vendored and not reimplemented. `sylpheed_formats::media` in particular already
owns every case where one playable thing is not one archive entry: entries that
span segment files, banks with several sub-waves, and the cutscene voices, which
are one continuous XMA stream chunked into VOICE_*.slb entries whose boundaries
do NOT match the cues. That last one is the easiest thing in this project to get
subtly wrong, so the mission says outright not to re-derive it.
docs/MISSION.md is the objective (P0-P7, each gated by an artifact rather than
by compiling). docs/BLOCKED.md lists what cannot proceed until the RE agent
answers Q1-Q10, and says plainly that none of it may be guessed -- this agent
has no emulator and no oracle, so a value it invents is indistinguishable from a
decoded one a month later.
The container is deliberately small: 3 cpus / 4 GB against the RE container's
6 / 7, and an image with no C++ toolchain, no Vulkan stack and no emulator. Two
full-size containers do not fit on this box beside a desktop.
Its launcher sets the git identity through GIT_AUTHOR_*/GIT_COMMITTER_* rather
than writing [user] into .git/config -- the config route captures every commit
made in that tree, including a human's, which is how six of today's commits
ended up attributed to the RE agent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.5 KiB
Primary objective — the menu shell, running in Godot
Status: active, set 2026-08-28.
Build a Godot 4 project that boots the player's own disc through the sequence the real game uses, and let a person move through it:
developer logo splash → intro video → title / PRESS Ⓐ → main menu → submenus
No gameplay. No 3D. No HUD. No emulator. Done means a human presses a d-pad and Ⓐ and moves through those screens with the right art, animation, music and transitions.
1. You are one of two agents
A container agent does the reverse engineering, in the Syplheed-Reborn repository. It runs the emulator; you do not. You build the port from what it publishes.
The contract is docs/port/HANDOFF.md in that repository. Read it before
assuming any value is on the disc. Every answer there is one of three things,
and the distinction decides what you do:
| meaning | what you do | |
|---|---|---|
| decoded | a field on the disc, with a disc-wide check | read it in the exporter |
| measured | not on the disc, but the running game does this | put it in authored/, cite the finding |
| undecodable | looked for, provably not there | put it in authored/, say it is a decision |
If HANDOFF.md does not answer something you need, say so and move to another milestone. Do not guess and do not reverse engineer it yourself — you have no emulator and no oracle, so a guess here is indistinguishable from a fact and will be believed later.
2. The wall
The Godot project never reads a disc format. No IPFB, no RATC, no T8aD, no XMA, no WMV. If Godot cannot read something, the exporter's job is to emit it differently — not to bridge the gap at runtime.
- No GDExtension. No Rust in
port/. - The decoders come from
sylpheed-formats, pinned by revision. Do not vendor them, do not reimplement them, and do not float the pin — a decoder change landing mid-milestone is exactly the confusion this pin prevents. - Bump the pin deliberately, as its own commit, saying what you wanted from it.
In particular, do not reimplement media assembly. sylpheed_formats::media
already handles the cases where one playable thing is not one archive entry: a
.pak entry that spans segment files, a bank with several sub-waves, and the
cutscene voices — which are one continuous XMA stream chunked into VOICE_*.slb
entries whose boundaries do not match the cues, so a .slb need not hold
the track its name claims. That last one is the single easiest thing in this
project to get subtly wrong. Use resolve_movie_voice_region.
3. Derived vs authored
export/ |
authored/ |
|
|---|---|---|
| produced by | the exporter | you, by hand |
| contains | what the disc says | what we decided |
| hand-edited | never | always |
| in git | no — gitignored | yes |
| on re-export | overwritten wholesale | untouched |
Tempted to hand-fix a file under export/? The fix belongs in the exporter or in
authored/. Every authored/ entry carries a why.
When the RE agent later decodes something you had authored, delete the authored entry and let the exporter emit it. That deletion is the measure of progress.
4. Never commit game assets
export/ is generated from the user's own disc and is gitignored. Code, schemas,
authored/ mappings and docs only. If you are about to commit a sprite PNG or a
transcoded video, stop.
5. Milestones
A milestone is done when its artifact exists, not when the code compiles.
| Milestone | Gate | |
|---|---|---|
| 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 Godot screenshot beside sylpheed-cli screen render of the same build — they should agree, and where they do not, say which is wrong |
| P2 | Keyframe animation | Buttons slide in. Blocked on HANDOFF Q1 (the time unit). Do not invent it |
| P3 | Splash → title, with the transition | Both screens back to back, unattended |
| P4 | Intro video | ADV.wmv plays with audio (§6) |
| P5 | Main menu: navigation, focus states, Ⓐ into a submenu, B back | A human clicks through it |
| P6 | Audio — menu BGM and move/confirm SFX | Sound on the P5 gate. Looping is blocked on HANDOFF Q10 |
| P7 | New-game intro video after NEW GAME | Plays, then returns to a defined state |
Work the lowest unfinished milestone. When one is blocked on an RE answer, say so
in docs/BLOCKED.md, and take the next milestone that is not.
6. The video problem
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 record the exact command in the export manifest so a modder who dislikes the quality can re-run it rather than reverse-engineer what you did. Theora at 720p is not great; if the result is visibly poor, say so and propose the FFmpeg-GDExtension fallback — do not adopt a runtime dependency on your own authority.
Only the boot intro and the one new-game intro are in scope. The disc holds 3.3 GB of video; transcoding all of it is not this milestone.
7. Out of scope
3D, gameplay, HUD, missions, save/load, localisation beyond English, the Ready Room, and any reverse engineering. If you want an answer the disc has not given you, that is a request to the container agent, not a task for you.