Their question was which screens the other four of "six" elements are on. The six span the whole 12-screen export: ptframe1/ptframe2 on main_menu and main_menu_jp (trailing run alpha 0xff, VISIBLE -- their rule accepts these, and they are the four it revealed disc-wide), and pteff02 on title and title_jp (trailing run alpha 0x00, TRANSPARENT -- their rule excludes it). That exclusion is correct, and their own measurement is what proves it: pteff02 is the 25 % dim quad, and they measured the title render going from +13.14 to +0.55 against the plate-free capture once it is drawn. So `rest` must stay at 0x40 and must not move to the transparent trailing run -- which is what their alpha rule does. Two investigations converging from opposite directions, and no third discriminator is needed. Also recorded: the reference renderer was stale for three diff runs and reported success throughout, and the shape worth naming is that a build system reporting success is not evidence the artifact you are about to trust is the code you pinned. The RE agent hit the same class of trap this session from the other side. BLOCKED.md: the loop/hold question is answered (groups hold -- ptloop01/ptloop02 park off-screen at x=1521 and x=-839), and the rest_plateau entry is closed as fixed, noting the adopted rule is theirs and not the looser one this port proposed.
168 lines
10 KiB
Markdown
168 lines
10 KiB
Markdown
# Waiting on the RE agent
|
||
|
||
What this port cannot do until an answer lands in
|
||
[`/reborn/docs/port/HANDOFF.md`](https://git.mc02.dev/fabi/Syplheed-Reborn).
|
||
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 `8b4965f`.
|
||
(`/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 | ✅ **answered 2026-08-28** — the RE agent retracted "cannot be extracted". The waves are located in `Static.slb` by playing them: **move `0x1ec0`** (8 192 B, 0.533 s), **confirm `0x5d6c0`** (12 288 B, 1.016 s), **back `0x0ec0`** (4 096 B, 0.344 s), and ⬅➡ play nothing. Move and back reproduce across two boots. 🟡 that the cursor's wave is the cue *named* `SE_UI_CURSOR` is still a name match, and Ⓐ's wave is not separated between `SE_UI_DECIDE` and `SE_UI_SUB_WIN_OPN`. P6 can now export real audio; the exporter has to grow an SE path. |
|
||
| 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).
|
||
|
||
Also newly available, and useful to P3/P5 when they author the flow: the title
|
||
part's transitions are a **lookup by name**, and the game's own screen
|
||
vocabulary includes `TITLE_SCREEN`, `TITLE_MENU`, `LOADING`, `DIFFICULTY`,
|
||
`EXTRA_MENU`, `TUTORIAL_MENU`. Three of those are corroborated by measurements
|
||
taken before the function was opened (`DIFFICULTY` is what `NEW GAME` opens,
|
||
`EXTRA_MENU` is `EXTRAS`, `TUTORIAL_MENU` the lesson list). 🟡 **Candidate, not
|
||
decoded** — the RE agent is explicit that the strings are what the call sites
|
||
*reference*, not proven arguments, and the same list mixes in `TEXT_FONT` and
|
||
`GAMMA_RGB`. So `authored/flow.json` may use these as `goto` names — which is
|
||
better than inventing names — but must mark them as a name match, not a
|
||
measurement.
|
||
|
||
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
|
||
|
||
### ~~Does a keyframe group loop, or hold its last pose?~~ — answered
|
||
|
||
**Answered 2026-08-28 by the RE agent: groups hold.** `ptloop01`/`ptloop02` park
|
||
their sprites at x=1521 and x=−839, both off a 1280-wide design, and 18 s of
|
||
settled title sits at sd ≤ 0.01. `loop*.rat` is a misleading name — these
|
||
animate once during build-in and then rest off-screen.
|
||
|
||
The port's own error here was different and is fixed: it settled at the last
|
||
*timed* keyframe rather than at the hold. See `docs/DECISIONS.md`.
|
||
|
||
Kept for the record:
|
||
|
||
Raised at P2 and **unsettled**. The port holds the last timed keyframe, which is
|
||
right for an entry animation (the main menu settles at t=80, 1.33 s) and is
|
||
proven on the screen P2 gates. The **title** runs to t=269 — 4.48 s — and there
|
||
the port's settled pose and the decoders' `rest` disagree badly (max 142/255).
|
||
|
||
What is known: no element's alpha reverses direction anywhere in this export, so
|
||
nothing pulses, which removes the obvious reason to expect a loop without
|
||
disproving one. What would settle it: **a capture of build 4 alone**. The one
|
||
live title capture composites the `PRESS Ⓐ` plate (build 2) over it, so it
|
||
cannot be diffed against the title by itself.
|
||
|
||
⚠️ Independently, **both** of the port's modes draw a washed-out cyan glow over
|
||
the title logo that the running game does not have. That is a third problem and
|
||
it is P3's; it is noted here so nobody reads the loop question as its cause.
|
||
|
||
Not blocking anything today; raised because the port found them and a guess here
|
||
would be believed later.
|
||
|
||
### ~~`rest_plateau` misfires on elements with no exit animation~~ — fixed
|
||
|
||
**Fixed 2026-08-28** in `sylpheed-formats`, and this port's pin moved
|
||
`8b6dbcf → 5414db3` to take it. The rule adopted is **not** the condition this
|
||
port proposed, which was too loose: a trailing run is the hold exactly when it
|
||
is **visible**. The port's condition would have erased the word PAUSE on
|
||
`pgptitle.rat`, whose trailing run is two identical *transparent* frames.
|
||
|
||
Kept for the record, since the reasoning is still what found it:
|
||
|
||
**This one is a decoder bug, not a question**, and it was the highest-value item
|
||
on this page for the RE agent. `ui_layout::rest_plateau` excludes a run of
|
||
identical keyframes that ends the group, on the grounds that it is the exit. For
|
||
an element that **has no exit animation** the trailing run *is* the hold, and the
|
||
rule falls back to an earlier run — for a slide-in, the invisible pre-roll.
|
||
|
||
The condition that identifies the affected elements exactly, with no false
|
||
positives across this export, is: **the final untimed keyframe has the same pose
|
||
as the last timed one.** Six elements match; `rest()` misses all six.
|
||
|
||
`ptframe1` and `ptframe2` on the main menu are the visible case, and
|
||
`docs/re/captures/main-menu-oracle.png` settles it — the game draws the circuit
|
||
bracket that `rest` calls invisible. `sylpheed-cli screen render` is missing it
|
||
too, so this is not only a port concern.
|
||
|
||
The port needs nothing here: it derives the arrived pose from the keyframes and
|
||
does not use `rest`. Filed because `rest()` is used elsewhere and because a
|
||
capture already proves it.
|
||
|
||
### 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 `.t32` elements match within ±1 px.
|
||
* **38 do not**, and several are not close: `ptlogo_back2` is 1118×262 with pivot
|
||
(500, 117) where half is (559, 131); `ptmsg` is 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.
|