re: the splash blur is a TEXTURE -- palogo_*_eff is a baked 10-px glow

Play-test finding 4, answered as a mechanism and from the disc, so it
generalises instead of describing one boot.

ui-splash-draw-pass.md excluded a post-process from GPU state and closed
with "that softness is in the texture or in which quads are drawn, not in
a pass", leaving the two unseparated. It is both, and they are one fact:
each logo ships a second texture that IS the blur -- the same artwork
outset by exactly 10 px per side, concentric to <=1.5 px, drawn as its
own alpha-over quad.

Three results, each with its control:

* The capture's eight anonymous quads are NAMED from the disc. Predicting
  each NDC rect from declared position + decoded sprite size matches all
  eight bijectively; every match <=0.0061, every runner-up >=0.0272, a
  4.5-8.9x margin. That margin is the control -- eight similar boxes
  would match anything.

* REFUTES splash-quad-timeline.txt's "the same three rects scaled
  slightly larger" (my own earlier wording). The x and y scale factors
  differ by up to 0.28; a uniform scale cannot do that, a fixed 10-px
  border can. The conclusion it supported (draw all six quads) stands;
  the model was wrong, and the wrong model tells a port to scale a
  sprite.

* The T8aD blend bit tested OUT of sample on entries 10/11, which were
  not in its 35-row fit and are the screens under complaint.
  Pre-registered additive=false for all eight against 0 additive draws in
  1048; held 8/8, with the control still reporting 9 additive on entry 6.

Also resolves a REFUTED.md 🟡 <our-reader> in the reader's favour: the
prediction is ours and the target is the oracle, so the agreement is
evidence about the reader rather than a claim resting on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
sylph-decoder
2026-09-01 18:24:14 +00:00
parent 32b1ec0b50
commit 1cd5b8b1cb
5 changed files with 448 additions and 0 deletions

View File

@@ -499,6 +499,74 @@ constructed in `sub_8220B610` and released in `sub_821A6470`. A *decoder* betwee
`wButtons` and the menus is where repeat timing, edge detection and remapping
live. That is the next read.
---
## ✅✅ 2026-09-01 (sixth) — **THE BLUR IS A TEXTURE.** `palogo_*_eff.t32` is a baked 10-px glow
Play-test finding 4 is **answered as a mechanism**, and the answer is on the disc,
so it generalises rather than describing one boot.
[`../re/splash-glow-is-a-baked-texture.md`](../re/splash-glow-is-a-baked-texture.md)
> **Each logo ships a SECOND texture that IS the blur** — the same artwork outset
> by exactly **10 pixels on every side** — drawn as its own alpha-over quad,
> concentric with the logo. There is no blur pass, no filter, and nothing to
> compute. Draw the logo alone and you lose the glow completely.
**Your four missing sprites**, by name:
```
palogo_sqex_eff.t32 palogo_gamearts_eff.t32 palogo_seta_eff.t32 palogo_anima_eff.t32
```
### The capture's Q0…Q7 now have names
I predicted each quad's NDC rect from the DECLARED position and the DECODED
sprite size and matched it against the vertex stream. **8 named, 0 unmatched**,
every match ≤ 0.0061 and every runner-up ≥ 0.0272 — a 4.58.9× margin, which is
the control that stops eight similar boxes matching anything.
| quad | is | | quad | is |
|---|---|---|---|---|
| Q0 | `palogo_sqex.t32` | | Q4 | `palogo_gamearts_eff.t32` |
| Q7 | `palogo_sqex_eff.t32` | | Q5 | `palogo_seta_eff.t32` |
| Q1 | `palogo_gamearts.t32` | | Q6 | `palogo_anima_eff.t32` |
| Q2 | `palogo_seta.t32` | | Q3 | `palogo_anima.t32` |
### 🔴 I told you the companions were "the same rects scaled slightly larger". WRONG — do not scale
That was an inference off four rounded NDC numbers. The disc says they are
**concentric 10-px outsets**, and their x/y scale factors differ by up to **0.28**:
| | logo | `_eff` | Δ | scale x / y |
|---|---|---|---|---|
| `sqex` | 666×68 @ (309,330) | 686×89 @ (299,319) | +20×+21 px | 1.030 / **1.309** |
| `gamearts` | 500×71 @ (390,164) | 521×91 @ (379,154) | +21×+20 px | 1.042 / **1.282** |
| `seta` | 240×89 @ (521,316) | 261×110 @ (511,305) | +21×+21 px | 1.087 / **1.236** |
| `anima` | 388×136 @ (446,449) | 407×156 @ (435,440) | +19×+20 px | 1.049 / **1.147** |
A scaled copy has a border that grows with the sprite. The real one is 10 px
whatever the sprite. **Load the `_eff` texture; do not transform the logo.**
### And they are source-over, not additive — tested OUT of sample
The blend bit was fitted on entries 2/4/5/6. Entries **10 and 11 were not in that
sample**. Pre-registered: the census finds additive in **0 of 1 048** splash
draws, so all eight must read `additive = false`. **They do — 8/8**, with a
control showing the same accessor still reports 9 additive on entry 6.
**So do not "add a glow" by switching these to additive.** That would be wrong in
a new way. The softness is entirely the texture's own alpha.
### Timing of the halo
Declared `0@0 → 255@15 → (255 or 212)@30 → 0@45`, against the logo's
`0@15 → 255@30 → … → 0@210` (developer) / `0@255` (publisher). **The halo flashes
during the entry and is gone for the whole hold** — ~45 units — which is exactly
the moment the play-test describes as too soft in your build.
Also new on the formats API and pinned below: `sprite_blend_additive`,
`blend_additive_by_name`, `sprite_header_word_04`, `header_word_04_by_name`, so
you can stop keying a blend map by screen name.
---
## ✅✅ 2026-09-01 — **THE SPLASHES HAVE NO POST-PROCESS.** One pass, source-over, alpha in the vertex stream

View File

@@ -144,6 +144,7 @@ files, which is how the same ground got covered twice.
| [`h3-units-per-frame-preregistration.md`](h3-units-per-frame-preregistration.md) | The prediction, committed before the capture was read | ✅ kept as the control on the row above |
| [`input-pad-read-path.md`](input-pad-read-path.md) | What the game asks the console for, on the pad | ✅ **decoded from the image, 2026-09-01.** `sub_82457038` is the only function that reads controller *data*, and it compares **every** field of `XINPUT_GAMEPAD``wButtons` (full 16 bits), both triggers, all four stick axes — against the previous state. **14/14** loads verified byte-for-byte against `/image/sylpheed.pe`, database used as an index only. **Two input paths**: the polled state and an 8-byte-record `XamInputGetKeystrokeEx` ring. ⚠️ This is the **superset the game can see, not the per-screen set** — reading a field is not acting on it. ❔ Which bits each screen tests is open; footholds are the image's own `C_PAD_DECODER` / `C_PAD_RINGBUF` trace strings (`sub_8220B610` / `sub_821A6470`). |
| [`ui-splash-draw-pass.md`](ui-splash-draw-pass.md) | The splash draw pass — is there a post-process, and where does the fade come from | ✅ **decoded from GPU state, 2026-09-01.** **No post-process pass exists**: over all 1 048 draws of both splashes, one render target (`rt0=[tile=0…]` 1 048/1 048), one pitch, no MSAA, only kColorDepth/kCopy modes, resolve destinations only the two front buffers, and **no texture base anywhere equals a resolve destination**. The only texture bound is the sprite page. Three trivial pixel shaders; the sprite shader **premultiplies**, so `ONE/ONE_MINUS_SRC_ALPHA` is algebraically **source-over**. Parameters come from **per-vertex `k_8_8_8_8` colour** in a per-frame vertex buffer — the shaders read **zero** float constants (`ps_c[n=0]` 1 048/1 048). Ramp reproduces the ✅ 34/frame law. ⚠️ Censusing the whole 600-frame log instead finds the attract movie's 640×360 chroma planes and reads as a half-res blur chain — the frame window is what avoids that. |
| [`splash-glow-is-a-baked-texture.md`](splash-glow-is-a-baked-texture.md) | What the splash "blur" actually is, and which quad is which sprite | ✅ **DECODED (disc), confirmed against the oracle 8/8.** Answers play-test finding 4 as a mechanism: **there is no blur pass and no filter — each logo ships a SECOND texture that IS the blur.** `palogo_<x>_eff.t32` is the same artwork **outset by exactly 10 px on every side**, concentric to ≤ 1.5 px, drawn as its own alpha-over quad. ✅ **The capture's Q0…Q7 are now NAMED**: predicting each NDC rect from the declared position + decoded sprite size matches all eight bijectively, every match ≤ 0.0061 with every runner-up ≥ 0.0272 (4.58.9× margin — the control). 🔴 **Refutes `splash-quad-timeline.txt`'s "the same rects scaled slightly larger"**: x/y scale factors differ by up to 0.28, so a port must **load the `_eff` texture, not transform the logo**. ✅ **Blend bit tested OUT of sample** on entries 10/11 (never in its 35-row fit): pre-registered `additive = false` for all eight against 0 additive draws in 1 048 — held 8/8, control still reports 9 additive on entry 6. 📌 The ⟨our-reader⟩ 🟡 on splash geometry resolves **in the reader's favour** — the prediction is ours, the target is the oracle, so agreement is evidence *about* the reader |
| [`ui-keyframe-record-layout.md`](ui-keyframe-record-layout.md) | A keyframe's time word comes **before** its pose — the placement record, decoded | ✅ CONFIRMED, **decoded**. A group is an 8-byte header then `frames` records of `{u32 time; 36-byte pose}`, so the time precedes the pose; the group's lead-in word at `header+8` is pose 0's time and **every** pose is timed. Disc-wide over 13 991 groups in 33 archives, each test with a control: lead-in prepended is non-decreasing **13 991/13 991**; a non-zero lead-in is strictly below the next time **5 058/5 058** (control 70.9 %); a multi-segment alpha ramp runs at a constant `dα/dt` **857/1 540** against **0/1 042** under the old reading. 🔴 Retires two long-standing corpus claims — *"a group's data stops 4 bytes short of its final block's time slot"* and *"the last keyframe carries no time"* — both of which were this off-by-one. Adoption is free: all 12 `GP_TITLE` builds render byte-identically, and over 217 builds only two elements pick a different `rest()` pose, both between equally invisible ones. ❔ the executable's own parser was **not** found (the 40/60 stride query is weak, not negative) |
| [`structures/ui-composable-bundles.md`](structures/ui-composable-bundles.md) | A screen build is not the only thing `compose` can draw | ✅ CONFIRMED by measurement over the disc, with the artifact to |
| [`structures/ui-focus-and-effect-elements.md`](structures/ui-focus-and-effect-elements.md) | `_eff` glow layers are not focused-state records | ✅ CONFIRMED by measurement over all 965 screen builds on the disc, |

View File

@@ -0,0 +1,163 @@
# The splash "blur" is a baked glow texture — `palogo_*_eff.t32`, a 10-pixel concentric outset
**Status: ✅ decoded (disc), confirmed against the oracle 8/8.** 2026-09-01.
Instruments: prediction ⟨disc⟩ (`parse_build` + `t8ad::parse` over
`GP_TITLE.pak`); target ⟨capture⟩ (the guest vertex stream in
[`data/splash-quad-timeline.txt`](data/splash-quad-timeline.txt) and
[`data/splash-draw-pass-census.txt`](data/splash-draw-pass-census.txt)).
Answers play-test finding 4 — *"the splash fade/blur is more pronounced in the
game"* — at the level the human asked for: **the mechanism, not a curve.**
---
## The answer in one line
> **There is no blur pass and no blur filter. Each logo ships a second texture
> that IS the blur — `palogo_<x>_eff.t32`, the same artwork outset by exactly
> 10 pixels on every side — and the game draws it as an extra alpha-over quad
> concentric with the logo. Drawing the logo alone loses the glow entirely.**
[`ui-splash-draw-pass.md`](ui-splash-draw-pass.md) had already excluded a
post-process from GPU state and closed with *"that softness is in the **texture**
or in **which quads are drawn**, not in a pass"*, leaving the two unseparated.
It is **both, and they are the same fact**: a dedicated soft-edged texture, drawn
as its own quad.
## 1 — the capture's eight anonymous quads, named from the disc
A draw capture sees geometry, not names. `splash-quad-timeline.txt` could only
call them `Q0…Q7`. Predicting each rect from the **declared position** and the
**decoded sprite size**`x_ndc = 2x/1280 1`, `y_ndc = 1 2y/720` — names all
eight, bijectively:
| sprite (disc) | quad | max abs edge error | runner-up | draws |
|---|---|---|---|---|
| `palogo_sqex.t32` | Q0 | 0.0056 | 0.0367 | 111 |
| `palogo_sqex_eff.t32` | Q7 | 0.0061 | 0.0339 | 8 |
| `palogo_gamearts.t32` | Q1 | 0.0056 | 0.0272 | 87 |
| `palogo_gamearts_eff.t32` | Q4 | 0.0037 | 0.0306 | 21 |
| `palogo_seta.t32` | Q2 | 0.0050 | 0.0278 | 87 |
| `palogo_seta_eff.t32` | Q5 | 0.0037 | 0.0328 | 21 |
| `palogo_anima.t32` | Q3 | 0.0050 | 0.0272 | 87 |
| `palogo_anima_eff.t32` | Q6 | 0.0056 | 0.0356 | 21 |
**8 named, 0 unmatched.** The capture quantises to 0.01 NDC, so the tolerance is
0.010; every match lands at ≤ 0.0061 and every **runner-up** sits at ≥ 0.0272 —
a 4.5× to 8.9× margin. **That margin is the control**: eight similar boxes would
match anything, and these do not.
Reproduce: `cargo run --release -p sylpheed-formats --example splash_quad_names`.
📌 **This also settles a `⟨our-reader⟩` 🟡 in the right direction.** The
prediction comes from our reader; the target is the oracle's vertex buffer. The
agreement is therefore **evidence about the reader**, not a claim resting on it —
`parse_build`'s declared placement and `t8ad`'s decoded dimensions reproduce
what the GPU actually drew, on both splash bundles, to within 4 screen pixels.
A disagreement would have indicted the reader instead.
## 2 — what an `_eff` companion actually is
`splash-quad-timeline.txt` described the companions as *"the same three rects
**scaled slightly larger**"*. That is an inference from four rounded NDC numbers,
and the disc refutes it:
```
palogo_sqex_eff outset 10x10 px, centre off by ( 0.0,-0.5), scale 1.030/1.309 NOT uniform
palogo_gamearts_eff outset 10x10 px, centre off by (-0.5, 0.0), scale 1.042/1.282 NOT uniform
palogo_seta_eff outset 10x10 px, centre off by ( 0.5,-0.5), scale 1.087/1.236 NOT uniform
palogo_anima_eff outset 10x10 px, centre off by (-1.5, 1.0), scale 1.049/1.147 NOT uniform
```
| | logo | `_eff` | Δ |
|---|---|---|---|
| `sqex` | 666×68 @ (309,330) | 686×89 @ (299,319) | **+20×+21 px, 10,11** |
| `gamearts` | 500×71 @ (390,164) | 521×91 @ (379,154) | **+21×+20 px, 11,10** |
| `seta` | 240×89 @ (521,316) | 261×110 @ (511,305) | **+21×+21 px, 10,11** |
| `anima` | 388×136 @ (446,449) | 407×156 @ (435,440) | **+19×+20 px, 11,9** |
* **Concentric** — the two centres agree to ≤ 1.5 px in every pair.
* **A constant 10-px outset per side**, in *both* axes, on all four pairs.
* **Not a scale.** The x and y scale factors differ by 0.06 to 0.28. A
"slightly larger copy" model predicts they match; they do not, and the
narrower the logo's height the further apart they get — exactly what a
*fixed-width* border does and a scale cannot.
A fixed 10-pixel halo around the same artwork, in a separate texture, is a
**pre-rendered blur**. Nothing computes it at runtime; it was computed by the
artist and shipped.
Asserted by `eff_is_a_concentric_outset_not_a_scale` in the same example, which
fails if the pair is not concentric, if the outset is not uniform, if it leaves
8…12 px, **or if the scale factors turn out uniform after all** — that last
assertion is the one that would reinstate the reading it displaces.
## 3 — the blend, tested out of sample on these exact screens
[`structures/ui-blend-mode-decoded.md`](structures/ui-blend-mode-decoded.md)
established `T8aD +0x04` bit `0x02` on **35 elements over three screens**
(`GP_TITLE` entries 2, 4, 5, 6). Entries **10 and 11 were not in that sample**,
and they are the screens the play-test says are wrong.
**Pre-registered before reading the disc:** the census finds `0x01010101`
(additive) in **0 of 1 048** splash draws, so if the bit generalises, all eight
splash sprites must read `additive = false`.
```
palogo_sqex.t32 / _eff, palogo_{gamearts,seta,anima}.t32 / _eff
word04 = 0x00008830 -> alpha-over (8 of 8)
additive=0 alpha-over=8 no-header=0
control -- entry 6 through the SAME code path reports additive=9: PASS
```
**Prediction held.** The control matters: the same accessor reports 9 additive
sprites on entry 6, so `additive=0` is a fact about the splashes and not a
harness stuck on one answer.
`cargo run --release -p sylpheed-formats --example splash_blend_check`.
🔴 **So the glow is NOT additive.** It is composited source-over like everything
else, and its softness comes entirely from the texture's own alpha. A port that
"adds a glow" by switching these quads to additive blending will be wrong in a
new way.
## What this means for the port
1. **Draw `palogo_sqex_eff.t32`, `palogo_gamearts_eff.t32`, `palogo_seta_eff.t32`
and `palogo_anima_eff.t32` as their own quads**, at their own declared
positions, with their own keyframe alphas. They are not decorations of the
logo element and they are not derivable from it.
2. **Do not scale the logo to produce them.** They are separate art. A scaled
logo has a border that grows with the logo; the real one is 10 px regardless.
3. **Source-over, not additive**, on all eight.
4. **They are short-lived.** Declared `0@0 → 255@15 → (255 or 212)@30 → 0@45`
against the logo's `0@15 → 255@30 → … → 0@210` (developer) / `0@255`
(publisher). The halo flashes during the entry and is gone for the whole hold.
That is the "more pronounced" moment and it is ~45 units long.
## Refutation attempt, recorded per the adversarial duty
**Target:** `ui-blend-mode-decoded.md`'s *"`T8aD +0x04` bit `0x02` set ⇒
additive"*, tested where it was never fitted — the two splash bundles, on the
screens under play-test complaint, against 1 048 captured draws.
**Result: it SURVIVED, out of sample, 8/8, with a passing control.**
**Second target, same iteration:** `splash-quad-timeline.txt`'s *"the same three
rects scaled slightly larger"*. **REFUTED** — §2. The companions are concentric
10-px outsets, and their x and y scale factors differ by up to 0.28, which a
uniform scale cannot produce. The conclusion the phrase supported (there are six
quads and an export must draw all six) is unaffected; the *model* of what the
extra three are was wrong, and the wrong model tells a port to scale a sprite.
## Reach
⟨disc⟩ for §2 and §3, so both generalise to every screen that uses the same
structures — the `_eff` naming convention and the blend bit are properties of the
shipped data, not of a boot. §1's *agreement* is ⟨disc⟩ × ⟨capture⟩ over the two
splash bundles specifically.
**Not settled here:** whether the `_eff` alpha ramp the port should use is the
declared one or the captured one (they differ; see
[`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md)), and the plate-late
finding (play-test 3), which is untouched.