`main_menu` -- the milestone's gate -- agrees with the reference renderer to
within 3/255 on every channel of every pixel, RMSE 0.38 %, no pixel above 4 %.
Nine of the twelve screens are at or under that; two are byte-identical.
Three exceed it, and each gets a cause rather than a wider tolerance:
* `title` (max 6): every disagreement is INSIDE A TIE -- the derived order and
the CLI's measured-off-the-game order differ only among elements with
identical layer keys. That is exactly the residual HANDOFF Q3 documents and
the export already declares in `unresolved: paint_order_ties`. Cost: 904 px at
4-6/255 in one glow band. The port keeps the stable sort; fitting it to one
screen's capture would be tuning.
* `title_jp` (max 154): `ptlogo_eff2` at 125 % is the only drawn element in the
whole export at a scale that is not a whole multiple of 100 %, and `title_jp`
is the only screen over 6/255. Same fact twice. `ui_layout::blit` samples the
source at the destination pixel's top-left corner; a GPU samples at its
centre, and at 125 % those disagree on one column in five.
I think the CLI is the one that is wrong -- corner-sampled nearest is a
half-pixel bias toward the top-left that no rasteriser produces. But that is a
reading, not a measurement: it needs a framebuffer capture of the Japanese
title screen, so it is filed in BLOCKED.md as a question. The port is NOT
changing to match, because matching would mean reproducing a half-pixel offset
on purpose to make a number smaller.
* `extras` (max 4): two pixels.
Also reconciled: the pivot question predicted a P1 diff could not distinguish
the declared pivot from half the texture, because both renderers use the
declared one. That held. Recorded so the agreement is not later mistaken for
evidence -- and P2 will not settle it either.
BLOCKED.md's `/reborn` HEAD updated to 690683d, with a note that the mount is
read-only by design and `git -C /reborn pull` fails rather than being skipped.
6.1 KiB
Waiting on the RE agent
What this port cannot do until an answer lands in
/reborn/docs/port/HANDOFF.md.
Recorded so it is not re-discovered every iteration.
None of these may be guessed. A value invented here is indistinguishable from
a decoded one a month from now. Where a milestone can proceed with a placeholder,
the placeholder goes in authored/ with a why naming the question it stands in
for, so it is deleted rather than forgotten when the answer arrives.
Last reconciled against HANDOFF.md on 2026-08-28, at /reborn HEAD bc6354d.
(/reborn is mounted read-only, so git -C /reborn pull fails by design; the
mount is refreshed outside this container and HEAD is read, not fetched.)
Still open — these block work
| Milestone | Needs | HANDOFF | State |
|---|---|---|---|
| P6 audio | which cue fires on move / confirm / back | Q8 | ❔ open. The cue table is complete; the event binding is not. P6 cannot bind a sound to a keypress without inventing it. |
| P6 audio | which BGM the menu plays | Q10 | ❔ not on the disc. All 32 banks are named BGM_001…BGM_109 with no semantic name anywhere. The port is choosing a track, and that choice is authored. |
| P6 looping | where a menu loop restarts | Q10 | ❔ BGM_001 fades out at 167.663 s into 6.15 s of silence, and no loop-point field has been identified. A menu loop is authored. |
| P4/P7 video | whether Ⓐ skips a movie | Q9 | 🟡 unsettled — the corpus says Ⓐ skips every time, the boot harness never taps during a movie because it breaks the title. P4 can play the movie; it cannot yet say what a button press does during one. |
P5 NEW GAME |
what Ⓐ on NEW GAME opens |
Q4 | ❔ untested: Ⓐ on it hangs the emulator. The other four destinations are measured. |
| P3 sequencing | what code decides to advance the boot sequence | Q6 | 🟡 the order is observed and the attract cycle timed (~8–10 s idle → fade → ADV.wmv in full → title). The driver is not decoded. P3 can reproduce the observed behaviour and must say it is reproducing an observation. |
Answered since this file was last written — no longer blocking
Q1 (keyframe time unit — linear ramp, 2 units per rendered frame, 1 unit = 1/60 s
measured), Q2 (which build is which screen), Q3 (paint order — a u16 layer key
at +0x0A, decoded), Q5 (navigation: ⬆⬇ wrap, ⬅➡ nothing, Ⓑ up with focus
restored), Q7 (transitions: a fade through black, fade-in decoded, ~0.4 s fade-out
measured), Q9 (ADVERTISE_MOVIE → ADV.wmv is boot intro and attract; MS00A →
S00A.wmv is the new-game intro), Q10 (a bank is two stems played together —
do not concatenate), S1 (Ready Room: no-go).
Three of those are measured, not decoded, and so are authored here rather than exported:
| Authored because it is not on the disc | HANDOFF | Where it lives |
|---|---|---|
1 keyframe unit = 1/60 s |
Q1 | not yet written — P2 |
| initial menu focus (not stable across boots; pick one and say so) | Q5 | not yet written — P5 |
| the ~0.4 s fade-out and the 0.17–0.23 s black hold | Q7 | not yet written — P3 |
Questions this port has raised
Not blocking anything today; raised because the port found them and a guess here would be believed later.
Does the game sample a scaled sprite at the pixel corner or the pixel centre?
Found at P1, by the only screen it could have been found on. title_jp's
ptlogo_eff2 is the single drawn element in the whole export at a scale that
is not a whole multiple of 100 % (125 %), and title_jp is the only one of the
twelve screens whose Godot-vs-CLI diff exceeds 6/255.
The two renderers pick different source texels at a non-integer ratio.
sylpheed_formats::ui_layout::blit samples at the destination pixel's top-left
corner (sxi = col * sw / dw); a GPU samples at its centre
(floor((col+0.5)*sw/dw)). At 125 % they disagree on one column in five — ~30
pixels above 100/255, strung along thin diagonal edges. At every whole multiple
of 100 % they agree exactly, which is why the other eleven screens are clean.
The port has not changed to match: matching would mean reproducing a half- pixel bias on purpose to make a number smaller. The question for the RE agent, when it is cheap: a framebuffer capture of the Japanese title screen would settle it outright, and it is the kind of thing a capture answers in one look.
Cost of being wrong either way: a one-texel edge on one glow, on a screen the English boot path never shows. This is filed, not urgent.
The pivot is not half the texture on GP_TITLE
sylpheed-formats's ui_layout::Element::pivot_x is documented as "for a .t32
element this is exactly half the decoded texture's dimensions (verified 7/7 on
the tutorial bundle)". Counting it over the whole of GP_TITLE as exported:
- 55 of 93 sprite-bearing
.t32elements match within ±1 px. - 38 do not, and several are not close:
ptlogo_back2is 1118×262 with pivot (500, 117) where half is (559, 131);ptmsgis 223×38 with pivot (123, 19) where half is (111.5, 19) — the Y matches and the X does not.
This changes nothing today: the exporter emits the declared pivot and never
derives one, and the pivot only affects drawing when scale ≠ 100 %. But it does
matter, because scale is genuinely animated here — 177 keyframes across
GP_TITLE are not 100 %, including on the title screen the port must draw at P1.
The question for the RE agent, when it is cheap to answer: does the running
game anchor a scale to the declared pivot, or to half the texture? The two
differ by up to 59 px on ptlogo_back2, which is visible. Until then the port
follows the decoders and uses the declared pivot, which is also what
sylpheed-cli screen render does — so a P1 diff cannot distinguish them, and
agreement between the two is not evidence.
P1 has now been run and that prediction held. The port and the CLI agree on every scaled element across all twelve screens; the question is untouched by it. It will stay untouched by P2 as well, since P2 animates the same two renderers' shared assumption. Only a capture answers this.