lands on the known tie-break group Chasing the residual I left open last iteration, and it turns out I had attributed the port agent's slab to the wrong thing. First the refutation. My hypothesis was that our dim is applied over the whole frame rather than beneath the UI where its layer key puts it. If that were true the logo would render too dark. It reads +2.36 against a background of -0.74, so the compositor honours the paint order and the hypothesis is dead. What the residual actually is: with the dim in place the error is not uniform but concentrated in one band, running -38.6 in one tile and +33.8 three tiles to its right. Cropping that band from capture and render shows it immediately -- the game draws the logo's Z swoosh thin with a pink edge, and we draw it thick and solid white. That, not the missing dim, is the washed-out slab over the title logo the port agent reported. The dim explains a uniform +13; this explains the slab. The elements are ptlogo_back2, its glow, and the five ptlogo_back2eff1..5 segments -- which are also the group carrying this corpus's known unsolved paint-order tie-break at key 0x8083, on the same screen. I have recorded that as a lead and explicitly not as a diagnosis, because a blend-order swap is a poor explanation for white instead of pink and I would expect a second cause.
437 lines
21 KiB
Markdown
437 lines
21 KiB
Markdown
# Which `GP_TITLE` build is which screen — measured against the running game
|
||
|
||
**Status:** ✅ `CONFIRMED` for the four screens the boot path actually shows
|
||
(title art, `PRESS Ⓐ BUTTON`, main menu, `EXTRAS`); 🟡 `PROBABLE` for their
|
||
Japanese twins; ❔ open for the two `DELTASABER` plates.
|
||
|
||
Answers [MISSION Q2](../port/MISSION.md). The previous statement — *"build 4
|
||
title, 5 main menu, 6/8/9 submenus"* — is **partly wrong** and is withdrawn:
|
||
build 8 is the **Japanese main menu**, not a submenu, and `GP_TITLE` holds
|
||
exactly **one** submenu (`EXTRAS`), in two languages.
|
||
|
||
## The archive is eight screens, each shipped twice
|
||
|
||
`GP_TITLE.pak` has 16 entries; `sylpheed-cli screen list` calls 12 of them
|
||
composable builds. The 16 fall into **eight pairs**, and each pair's two members
|
||
have near-identical sizes and name hashes that differ by a constant — one
|
||
character of the name apart:
|
||
|
||
| entry | hash | bytes | build idx | pair |
|
||
|---|---|---|---|---|
|
||
| 0 | `01a2db9c` | 483 958 | 0 | A |
|
||
| 1 | `0ff0b8a8` | 483 958 | 1 | A |
|
||
| 2 | `285d8849` | 267 014 | 2 | B |
|
||
| 3 | `369773cb` | 267 014 | 3 | B |
|
||
| 4 | `a60fcb85` | 12 278 666 | 4 | C |
|
||
| 7 | `b483e6e6` | 13 363 328 | 7 | C |
|
||
| 5 | `a715f485` | 6 977 437 | 5 | D |
|
||
| 8 | `b58a0fe6` | 6 931 653 | 8 | D |
|
||
| 6 | `a81c1d85` | 6 549 126 | 6 | E |
|
||
| 9 | `b69038e6` | 6 548 438 | 9 | E |
|
||
| 10 | `cdba806e` | 426 473 | — | F |
|
||
| 13 | `db2ea1f8` | 423 333 | — | F |
|
||
| 11 | `cec0a96e` | 999 643 | — | G |
|
||
| 14 | `dc34caf8` | 999 643 | — | G |
|
||
| 12 | `cf2a8ccd` | 1 774 639 | 10 | H |
|
||
| 15 | `dd56d99b` | 1 774 639 | 11 | H |
|
||
|
||
For the three pairs whose members differ visibly — C, D, E — **the difference is
|
||
English versus Japanese**: build 7 is the title art with the katakana subtitle
|
||
プロジェクト シルフィード, build 8 the main menu reading 新規 / ロード /
|
||
チュートリアル / オプション / エクストラ, build 9 the `EXTRAS` submenu as
|
||
エクストラ. That is the whole of the language split we can see. In pairs A, B and
|
||
H the two members render **byte-identical PNGs** — the archive still carries two
|
||
copies, but the artwork does not change with the language.
|
||
|
||
Pairs F and G are the four entries `screen list` does *not* classify as builds by
|
||
default. **They are the developer splash**, and they render — see below.
|
||
|
||
## The map
|
||
|
||
Contact sheet of every render:
|
||
[`captures/title-builds/title-build-contact-sheet.png`](captures/title-builds/title-build-contact-sheet.png).
|
||
|
||
| build | what it is | confirmed how |
|
||
|---|---|---|
|
||
| 0, 1 | a `DELTASABER / SYLPHEED A.I.` plate low-left on black, no background | ❔ **not observed running.** Never seen in the boot path, the main menu, `EXTRAS` or `MISSION SELECT` |
|
||
| **2**, 3 | the `PRESS Ⓐ BUTTON` plate — **an overlay build of its own**, not a state of build 4 | ✅ seen composited over build 4 on the live title, at the same rect our render puts it |
|
||
| **4** | title art, English (`PROJECT SYLPHEED`, ™, `(C)2006,2007 SQUARE ENIX`) | ✅ [`live-title-press-a.png`](captures/title-builds/live-title-press-a.png) |
|
||
| 7 | the same, Japanese | 🟡 renders as the JP twin of build 4; the container runs an English locale, so it was not seen |
|
||
| **5** | main menu, English: NEW GAME / LOAD GAME / TUTORIAL / OPTIONS / EXTRAS, footer `Ⓐ : OK` | ✅ [`live-main-menu.png`](captures/title-builds/live-main-menu.png) — element for element |
|
||
| 8 | the same, Japanese | 🟡 as above |
|
||
| **6** | the `EXTRAS` submenu, English: MISSION SELECT / MOVIE THEATER / BACK, footer `Ⓐ : OK Ⓑ : Back` | ✅ [`live-extras.png`](captures/title-builds/live-extras.png) |
|
||
| 9 | the same, Japanese | 🟡 as above |
|
||
| 10, 11 | the same `DELTASABER` plate as build 0, over a dark circuit-line background | ❔ **not observed running** |
|
||
|
||
## ✅ The splash — the four "non-build" entries, rendered
|
||
|
||
`screen list --all` widens the enumeration to every composable bundle and shows
|
||
all **16** entries. The four that the default listing drops are the two halves of
|
||
the developer splash, each shipped twice:
|
||
|
||
| entry | elements / sprites | what it is |
|
||
|---|---|---|
|
||
| **10, 13** | 3 / 2 | the white **`SQUARE ENIX`** publisher logo |
|
||
| **11, 14** | 7 / 6 | **`GAME ARTS` / `SETA` / `studio anima`** — the developer logos |
|
||
|
||
[`splash-entries-rendered.png`](captures/title-builds/splash-entries-rendered.png).
|
||
Rendered with `screen render --all --primitives --black`. Entry 11's seven
|
||
elements are the three logos, their three `_eff` glows and the
|
||
`palogo_eff0.prm` black backdrop — exactly the composition
|
||
[`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md) measured
|
||
for the splash.
|
||
|
||
### ✅ Confirmed against the running game — 2026-08-28
|
||
|
||
The 🟡 above ("no framebuffer capture to diff against") is closed. Recording the
|
||
boot from the moment the window appears, at 2 fps, catches the splash before the
|
||
movie:
|
||
[`live-splash-publisher.png`](captures/title-builds/live-splash-publisher.png),
|
||
[`live-splash-developer.png`](captures/title-builds/live-splash-developer.png).
|
||
|
||
Edge-correlated against the renders, **with the other half as a negative
|
||
control**:
|
||
|
||
| capture | vs entry 10 | vs entry 11 |
|
||
|---|---|---|
|
||
| publisher frame (t ≈ 2.0 s) | **0.9146** @ (0,0) | 0.027 ✗ |
|
||
| developer frame (t ≈ 6.0 s) | −0.033 ✗ | **0.9792** @ (0,0) |
|
||
|
||
Both halves match their own render at zero shift and are firmly rejected by the
|
||
other. The `13`/`14` twins score 0.876 / 0.966 — near-identical artwork, so this
|
||
test cannot tell a pair apart, only a screen from a different screen.
|
||
|
||
### ✅ The splash's timing is DECODED, and the capture confirms it
|
||
|
||
Re-recorded at **10 fps** (the 2 fps pass below was ±0.5 s and could not see
|
||
ramps at all). The splash **fades, both ways** — it does not cut — and the
|
||
bundle's own keyframes say so:
|
||
|
||
```
|
||
$ sylpheed-cli screen info --all --build 10 GP_TITLE.pak
|
||
1 palogo_sqex.t32 7 kf [15 30 235 239 251 255 -]
|
||
2 palogo_sqex_eff.t32 4 kf [15 30 45 -] (the glow, child of 1)
|
||
|
||
$ ... --build 11
|
||
1 palogo_gamearts.t32 7 kf [15 30 190 194 206 210 -] (seta, anima identical)
|
||
2 palogo_gamearts_eff 4 kf [15 30 45 -]
|
||
```
|
||
|
||
Under Q1's `1 unit = 1/60 s`:
|
||
|
||
| | declared | measured at 10 fps |
|
||
|---|---|---|
|
||
| `SQUARE ENIX` ramp in | `15 → 30` = 0.25 s | rise 0.4 → 0.8 s |
|
||
| `SQUARE ENIX` hold | `30 → 235` = **3.42 s** | ≈ 3.5 s (0.8 → 4.3 s) |
|
||
| `SQUARE ENIX` fade out | `235 → 255` = **0.33 s** | **≈ 0.3 s** (4.4 → 4.7 s) |
|
||
| developer hold | `30 → 190` = **2.67 s** | ≈ 2.4 s (5.6 → 8.0 s) |
|
||
| developer fade out | `190 → 210` = **0.33 s** | **≈ 0.3 s** (8.1 → 8.4 s) |
|
||
|
||
The offset between declared and measured start is ≈ 0.35 s, which is simply that
|
||
the recording's `t = 0` is when the *window* appears, not when the guest starts
|
||
drawing. Everything downstream of that lines up.
|
||
|
||
**The overshoot is the glow.** The measured rise peaks (6.21) at 0.8 s and settles
|
||
back (5.39) by 1.1 s, which looks like a bloom. It is the `_eff` child element:
|
||
its keyframes are `15 → 30 → 45`, so it ramps in *after* the logo and then back
|
||
down, while the logo itself holds. Decoded, not a rendering artifact.
|
||
|
||
So the port can **read** the splash's timing off the disc rather than author it —
|
||
the first screen's animation is not a measurement it has to trust.
|
||
|
||
### The wall-clock sequence, for orientation
|
||
|
||
| | |
|
||
|---|---|
|
||
| `SQUARE ENIX` on screen | **≈ 0.5 → 4.0 s** after the window appears |
|
||
| black | ≈ 4.5 s |
|
||
| `GAME ARTS` / `SETA` / `studio anima` | **≈ 5.0 → 7.5 s** |
|
||
| black | ≈ 9.0 s |
|
||
| `ADV.wmv` begins | **≈ 9.5 s** |
|
||
|
||
⚠️ The frames at ≈ 9.5–12.5 s show `SQUARE ENIX` again in **cyan**. That is not a
|
||
third splash — it is the intro movie's own opening, which the milestone-2 notes
|
||
describe as "white SQUARE ENIX + cyan glow + red diamonds". A capture-only reading
|
||
would have recorded a third logo screen that does not exist.
|
||
|
||
**So the first of the five screens now has a reference composite, a framebuffer
|
||
capture, and its on-screen durations.**
|
||
|
||
**Reach of the two negatives.** Builds 0/1 and 10/11 were looked for in: the whole
|
||
boot sequence (a 5 s-cadence filmstrip from launch to the title, ~190 s), the
|
||
title, the main menu, the `EXTRAS` submenu, the `LOAD GAME` slot list, the
|
||
transition into `MISSION SELECT` (a 40-frame burst), and the attract cycle. They
|
||
appear in none of them. The obvious remaining candidate is a *long* load — a
|
||
mission launch — which is out of this objective's scope; they are most likely a
|
||
loading/AI-chatter plate. That is a hypothesis, not a result.
|
||
|
||
## Three things the captures settle beyond the map
|
||
|
||
**The live title is two builds composited.** Build 4 draws the art; build 2 draws
|
||
`PRESS Ⓐ BUTTON` on top, and it **fades in a beat later** — a screenshot taken
|
||
2.5 s after arriving at the title has the art and no plate, one taken ~1 s later
|
||
has both. The port must treat the plate as its own timed element.
|
||
|
||
**The attract-loop title carries the plate too.** After ~8–10 s idle the title
|
||
fades to black, a full-motion video plays for ~85 s, and the title comes back —
|
||
*with* `PRESS Ⓐ BUTTON`
|
||
([`live-attract-title-press-a-band.png`](captures/title-builds/live-attract-title-press-a-band.png)).
|
||
This is consistent with, and adds nothing to, the draw-quad comparison in
|
||
[`canary-scripted-input-traps.md`](canary-scripted-input-traps.md): the plate is
|
||
not the tell that distinguishes the boot title from the attract title.
|
||
|
||
**`EXTRAS` is the only main-menu destination inside `GP_TITLE`.** Ⓐ on `EXTRAS`
|
||
opens build 6 — measured. The other four destinations leave the archive: Ⓐ on
|
||
`LOAD GAME` opened a `LOAD GAME` slot list, and Ⓐ on `MISSION SELECT` inside
|
||
`EXTRAS` opened a `MISSION SELECT` screen, neither of which is a `GP_TITLE`
|
||
build. `dat/` carries `GP_SAVE_LOAD.pak`, `GP_TUTORIAL.pak`, `GP_OPTIONS.pak`,
|
||
`GP_MISSION_SELECT.pak` and `GP_MOVIE_THEATER.pak`; **that those are the archives
|
||
behind the other four buttons is an inference from the names, not a measurement.**
|
||
|
||
## How to reproduce
|
||
|
||
```bash
|
||
sylpheed-cli screen list "$SYLPHEED_DISC/dat/GP_TITLE.pak"
|
||
sylpheed-cli screen render --build 6 "$SYLPHEED_DISC/dat/GP_TITLE.pak" /tmp/b6.png
|
||
tools/re-capture/boot_menu.sh q2 # boots to the main menu, cursor on NEW GAME
|
||
tools/re-capture/pad.py dpad down 0.35 # x4 -> EXTRAS
|
||
tools/re-capture/pad.py tap A 0.30
|
||
```
|
||
|
||
Mind the d-pad hold — see [`METHOD.md`](METHOD.md).
|
||
|
||
## ✅ The title is not a still image — it loops, ≈ 2.2 s
|
||
|
||
Recording the title at 10 fps for 22 s shows the screen **never settles**. After
|
||
the build-in it oscillates by about ±0.5 in mean luminance, continuously:
|
||
|
||
```
|
||
peaks at 5.6 7.8 10.0 12.7 15.2 17.1 19.2 21.3 s
|
||
intervals 2.2 2.2 2.7 2.5 1.9 2.1 2.1
|
||
mean 2.24 s
|
||
```
|
||
|
||
**measured**, n = 7 intervals, spread 1.9–2.7 s — peak-picking a low-amplitude
|
||
signal is coarse, so read it as **≈ 2.2 s ± 0.4**, not a precise period.
|
||
|
||
The mechanism is already decoded: build 4 declares **`ptloop01.rat` /
|
||
`ptloop02.rat`**, and `loop*.rat` is a **looping sprite animation** rather than a
|
||
composition ([`INDEX.md`](INDEX.md), UI screen layout row). So the title carries a
|
||
looping element by construction; what is new is that it runs at ≈ 2.2 s and never
|
||
stops.
|
||
|
||
### ✅ The loop records ARE decoded — and they are not what I measured
|
||
|
||
`ptloop01.rat` is an **`opt `-linked leaf record**: in build 4 the chunk `opt `
|
||
(size `0x0c`) carries the name, immediately followed by a `RATC` blob at
|
||
**`0xbb5966`** (`ptloop02.rat` the same at `0xbb5a82`). Its pivot fields read
|
||
`200`/`90`, matching `screen info`'s `pivot (200,90)` — the right blob.
|
||
|
||
**The keyframes use the ordinary 40-byte block layout**, starting at `+0x68`:
|
||
|
||
| | `ptloop01` → `pteff03.t32` | `ptloop02` → `pteff03a.t32` |
|
||
|---|---|---|
|
||
| kf0 | `t=150` x=**−639** α=`ff` | `t=150` x=**1721** α=`00` |
|
||
| kf1 | `t=540` x=−39 α=`80` | `t=630` x=1111 α=`80` |
|
||
| kf2 | `t=600` x=**1521** α=`ff` | `t=720` x=**−839** α=`ff` |
|
||
| scale | 100 × 600 | 100 × 800 |
|
||
| span | 150 → 600 = **450 units = 7.5 s** | 150 → 720 = **570 units = 9.5 s** |
|
||
|
||
Y is constant at 270 and X runs off one edge to the other, so these are
|
||
**horizontal light sweeps** — `loop01` left → right, `loop02` right → left.
|
||
|
||
### 🔴 Two things I wrote last iteration are wrong
|
||
|
||
**1. "A leaf record's keyframes are not in the build's 40-byte layout."**
|
||
Withdrawn — they are, exactly. The scan that "found nothing" demanded **29**
|
||
strictly-increasing times because I read the word at `+0x004` (`0x001e0000`) as a
|
||
keyframe count. These records hold **three** keyframes. A filter that hard-codes
|
||
the expected count rejects the right structure; whatever the `30` is, it is not
|
||
the number of keyframes here.
|
||
|
||
**2. "The ≈ 2.2 s oscillation is the `ptloop` elements."** Withdrawn — I asserted
|
||
the link because build 4 declares those elements, not because anything showed it.
|
||
The decoded sweeps run **7.5 s and 9.5 s**. A 22 s capture would show ~3 peaks
|
||
from a 7.5 s cycle; it showed **8**. So the loops are *not* what the oscillation
|
||
measured.
|
||
|
||
### ✅ Identified: it is the `PRESS Ⓐ BUTTON` plate pulsing
|
||
|
||
A per-tile amplitude map over the capture (8 × 6 grid, 18 s after build-in)
|
||
localises the 2.3 s period precisely:
|
||
|
||
| region | sd | dominant lag |
|
||
|---|---|---|
|
||
| band x ≈ 318–954, y ≈ 560–672 | **7.65** | **2.3 s** |
|
||
| wordmark centre | 0.06 | — |
|
||
| bottom-right corner | 0.003 | — |
|
||
|
||
That band is the `PRESS Ⓐ BUTTON` plate's rest position (`ptbtn00.rat`, rest
|
||
`(383,550)`, pivot `(256,25)`). It is **build 2**, composited over the title — not
|
||
build 4.
|
||
|
||
Decoding `ptbtn00f.rat`, the plate's highlight variant, gives the pulse directly:
|
||
|
||
| kf | t | alpha |
|
||
|---|---|---|
|
||
| 0 | 6 | `0x00` |
|
||
| 1 | 29 | `0x06` |
|
||
| 2 | 35 | `0x4a` |
|
||
| 3 | 50 | `0x50` |
|
||
| 4 | 58 | `0x50` (hold) |
|
||
| 5 | 97 | `0x4a` |
|
||
| 6 | 105 | `0x06` |
|
||
|
||
A glow that fades in to `0x50` and back out — exactly a "press start" pulse.
|
||
|
||
There is an **eighth** keyframe: fade `0x00ffffff` at the same position — the glow
|
||
returns to **fully transparent**, so this is a closed cycle, not a one-shot ramp.
|
||
|
||
🟡 **The cycle length is still not readable — and now that is an observation.**
|
||
The eighth block's time slot contains the four bytes `end `, the record's ASCII
|
||
terminator: the record simply stops there and the value does not exist. So the
|
||
corpus's "a group's last block has no time of its own" rule holds here in a second
|
||
form — not the next group's index, but the chunk terminator.
|
||
|
||
Declared span is **≥ 105 units = 1.75 s**; measured **≈ 2.3 s** (≈ 138 units),
|
||
which would need a final step of ≈ 33 units. **That 33 is fitted to the
|
||
measurement, not read from the file**, and is recorded only so nobody re-derives
|
||
it as if it were a decode.
|
||
|
||
❔ **The word at `+0x004` is not a keyframe count.** It reads `0x003c0000` (60)
|
||
here with 8 keyframes, and `0x001e0000` (30) in the loop records with 3. Whatever
|
||
it is, it is not the count, and it is not decoded.
|
||
|
||
### ❔ The declared 4.08 s build-in was not tested
|
||
|
||
That was the intent of this recording and it did not work. The title was reached
|
||
by **skipping the movie with Ⓐ**, which cuts to black and brings the title up on a
|
||
path that may not be the normal one; and the visible rise (≈ 2.7 s, from t ≈ 0.8
|
||
to 3.5) is a *luminance* curve, which
|
||
[`screen-transitions.md`](screen-transitions.md) already establishes is **not**
|
||
the fade quad's ramp. So ≈ 2.7 s neither confirms nor contradicts build 4's
|
||
declared `16 → 261` (4.08 s); the two are not measuring the same thing.
|
||
|
||
Testing it properly needs the title reached **without** a skip, and a way to
|
||
separate the quad from the elements — neither of which this recording had.
|
||
|
||
|
||
## ✅ For the port: build 4 alone, and groups HOLD rather than loop
|
||
|
||
Both answers the port agent asked for, in one recording.
|
||
|
||
### The capture — the title with no `PRESS Ⓐ` plate over it
|
||
|
||
Ⓑ from the main menu returns to the title and the plate fades in **a beat later**,
|
||
which opens a clean window. Recorded at 20 fps from the press:
|
||
|
||
| | |
|
||
|---|---|
|
||
| title art appears | t ≈ 1.10 s |
|
||
| builds in | 1.10 → 3.70 s (mean 41.8 → 63.2) |
|
||
| **settled, still no plate** | **3.70 → 5.00 s** |
|
||
| plate arrives | t ≈ 5.10 s (band jumps 282 → 3 755 bright px) |
|
||
|
||
[`live-title-build4-no-plate.png`](captures/title-builds/live-title-build4-no-plate.png)
|
||
is t = 4.0 s — build 4, settled, unobstructed. That is the reference for the
|
||
washed-out cyan glow slab the port reports drawing and the game not having.
|
||
|
||
### ✅ A keyframe group HOLDS at its last keyframe — it does not loop
|
||
|
||
This follows from the decoded sweeps plus a measurement, and the two agree:
|
||
|
||
* `ptloop01.rat`'s final keyframe puts `pteff03.t32` at **x = 1521** and
|
||
`ptloop02.rat`'s puts `pteff03a.t32` at **x = −839** — both **off-screen** on a
|
||
1280-wide design. A group that holds therefore parks both sweep sprites out of
|
||
view and nothing moves after the build-in.
|
||
* Measured: over 18 s of settled title the centre tiles sit at **sd ≤ 0.01**
|
||
(per-tile map above). A looping group would recross the screen every **7.5 s**
|
||
and be unmissable.
|
||
|
||
So the name `loop*.rat` is misleading — in this build the records animate **once**
|
||
during the build-in and then rest off-screen. 🟡 This is about *these* groups on
|
||
*this* screen; nothing here says no group anywhere loops.
|
||
|
||
## ✅ The title's settled pose is `rest`, and the "washed-out slab" is a MISSING dim
|
||
|
||
The port agent could not decide whether `rest` or its played-out timeline is the
|
||
title's arrived pose (they disagree by 142–247/255), and separately reported a
|
||
*"washed-out cyan glow slab over the title logo that the running game doesn't
|
||
have"*. The plate-free capture settles both.
|
||
|
||
`sylpheed-cli screen render --build 4 --black`, edge-correlated against
|
||
[`live-title-build4-no-plate.png`](captures/title-builds/live-title-build4-no-plate.png)
|
||
— a capture of the real screen, so this is an independent oracle and not one
|
||
renderer checking another:
|
||
|
||
> **0.9163 at shift (0,0)**, and per band 0.92 / 0.78 / 0.93.
|
||
|
||
**Geometry is right**, so `rest` is the arrived pose for the title.
|
||
|
||
### The slab is the 25 % dim, absent — not a glow, present
|
||
|
||
`rest` alone is **uniformly too bright**, and the excess leans cyan:
|
||
|
||
| render | mean (render − capture) | R | G | B |
|
||
|---|---|---|---|---|
|
||
| `--black` | **+13.14** | +12.35 | +13.58 | +13.48 |
|
||
| `--black --primitives` | **+0.55** | +1.30 | +0.89 | −0.54 |
|
||
|
||
Drawing the `.prm` primitives collapses the excess to nothing. The element is
|
||
**`pteff02.prm`**, the 25 % dim quad (build 4, rest at `t=46`, fade `0x40` = 64 =
|
||
25 %) — and `--primitives` is **off by default**.
|
||
|
||
So the "washed-out cyan slab" is not something being drawn that shouldn't be. It
|
||
is the **dim that should be drawn and isn't**: without it every pixel sits ~13
|
||
high, and because the title art is blue-dominant the shortfall reads as a cyan
|
||
wash. ⚠️ Any consumer of `screen render` that omits `--primitives` on this screen
|
||
gets it.
|
||
|
||
### 🟡 The residual: right on average, not right per pixel
|
||
|
||
With primitives the *mean* is essentially exact (+0.55) but pixel agreement is
|
||
slightly **worse** — edge-correlation 0.9163 → **0.9066**, and pixels differing by
|
||
> 20 rise 108 051 → 162 636. So the dim's average contribution is right while its
|
||
application is not exactly the game's (blend mode or per-region alpha). Not
|
||
diagnosed.
|
||
|
||
### 🔴 The real defect: the logo swoosh is drawn white and thick
|
||
|
||
With the dim in place, the residual is **not uniform** — it is a dark patch beside
|
||
a bright one in one band:
|
||
|
||
```
|
||
row1 (y 112-225): -2.0 +3.0 -8.8 -38.6 -17.6 +16.2 +33.8 +24.4
|
||
```
|
||
|
||
Cropping that band from capture and render
|
||
([`title-swoosh-capture-vs-render.png`](captures/title-builds/title-swoosh-capture-vs-render.png))
|
||
shows it plainly: the game draws the logo's `Z` **swoosh thin, with a
|
||
pink/magenta edge**; our render draws it **thick and solid white**. Too bright to
|
||
its right, too dark where the game's thin stroke actually falls.
|
||
|
||
**This — not the missing dim — is the port agent's "washed-out slab over the title
|
||
logo".** The dim explains a *uniform* +13; the slab is this.
|
||
|
||
The elements are `ptlogo_back2.t32` (rest `(71,126)`, pivot `(500,117)` — a
|
||
1000 × 234 diagonal), its glow `ptlogo_back2eff.t32`, and the five
|
||
`ptlogo_back2eff1…5` segments at y ≈ 117–194 — exactly the band that disagrees.
|
||
|
||
🟡 **A connection worth chasing, not a diagnosis.** Those five segments are the
|
||
group whose **paint-order tie-break is the known unsolved residual**
|
||
([`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md)): they
|
||
share key `0x8083`, the game paints them `14,15,18,16,17`, the stable sort paints
|
||
`14,15,16,17,18`, and the measured cost is "`ptlogo_back2eff5` against `eff3`
|
||
(22 568 px) and against `eff4` (32 395 px)". **Same screen, same elements.**
|
||
|
||
❔ But a blend-order swap is a poor explanation for *white instead of pink* — that
|
||
looks like a tint or blend-mode problem, so I would expect a second cause. **Not
|
||
diagnosed**, and recorded as two candidate leads rather than one answer.
|
||
|
||
### 🔴 Refuted on the way
|
||
|
||
The obvious guess — *our dim is applied over the whole frame instead of beneath
|
||
the UI, where its layer key puts it* — is **wrong**. If it were, the logo would
|
||
render too dark; it reads **+2.36** against a background of **−0.74**. The
|
||
compositor honours the paint order here.
|