re: the swoosh is solved -- the game draws it as rotated quads and our

blit cannot

Ran the route I corrected last iteration, and it answered the question I
had classified as undecodable.

First the ninth candidate died: every vertex colour in the whole capture
is <alpha>FFFFFF, white RGB with only alpha varying. The game passes no
colour, so that hypothesis is refuted from the running game rather than
from the file.

Then the geometry gave it away. Draw 2 submits TWO PARALLELOGRAMS,
neither axis-aligned -- edges (0.54,-0.56) and (0.44,0.79), roughly 45
and 61 degrees, both extending to y=+/-1.81 in NDC, well off screen.
That is the diagonal Z stroke. And ui_layout::blit walks rows and columns
of an axis-aligned rectangle; it has no rotation at all. So we blit the
sprite upright where the game draws it skewed, which is exactly the
signature I measured: right on average at +1.83, right in position with
the correlation peaking at zero shift, wrong in structure at 0.70.

Nine candidates, and the answer was never going to be a field, because
the difference is not in the sprite -- it is in how the quad is built.

Two things left open and written as such. The decoded keyframe carries
fade, scale, tint, x, y and time and NO rotation, so where the rotation
comes from is not decoded. And the pink-versus-white reading I have been
carrying was a visual comparison of two differently-shaped renderings; it
should be re-checked after geometry rather than treated as a separate
defect.
This commit is contained in:
Sylpheed RE agent
2026-08-28 22:13:50 +00:00
parent 6930d301c1
commit 0ecb20bdbc
4 changed files with 111 additions and 1 deletions

View File

@@ -35,6 +35,11 @@ neighbourhood, not just the line.
## Screens, classes and RTTI
* "the game passes a pink per-vertex colour for the title swoosh" → **refuted by
draw capture.** Every vertex colour in the capture is `<alpha>FFFFFF`, white RGB.
* "the swoosh discrepancy is undecodable" → **solved**: the game submits it as two
**rotated parallelograms**; `ui_layout::blit` only does axis-aligned rectangles.
[`ui-title-build-map.md`](ui-title-build-map.md)
* "the `--log_ui_draws` per-draw capture reads the guest's blend state" →
**mine, and wrong.** It records primitive type, index count, index-buffer
address, VS/PS ucode hashes, texture bindings and vertex attribute 0 — no

View File

@@ -0,0 +1,43 @@
# every draw in SUBMISSION ORDER, undeduplicated, frames 1..3
# tex dimensions identify the sprite; base is the guest address
0 prim=8 indices=3 vs=0x0A6D1DD7767FDF27 ps=0x2E372EA28CC404B7
vb=0x15036638 stride=28 attrs=[57@0 38@12 ] fmt0=57 v: [-0.50,-0.50,z=0.00000] [1279.50,-0.50,z=0.00000] [1279.50,719.50,z=0.00000]
1 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0BA60000 1280x768 fmt=6]
vb=0x15036690 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,-1.00,z=0.00000,col=FFFFFFFF] [-1.00,-1.00,z=0.00000,col=FFFFFFFF]
2 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x11C80000 1280x768 fmt=6]
vb=0x150366F0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [0.70,1.58,z=0.00000,col=C3FFFFFF] [1.24,1.02,z=0.00000,col=C3FFFFFF] [0.40,-1.57,z=0.00000,col=C3FFFFFF] [-0.14,-1.02,z=0.00000,col=C3FFFFFF] [-1.29,1.02,z=0.00000,col=B6FFFFFF] [-0.85,1.81,z=0.00000,col=B6FFFFFF] [0.75,-1.02,z=0.00000,col=B6FFFFFF] [0.31,-1.81,z=0.00000,col=B6FFFFFF]
3 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0B690000 1280x768 fmt=6]
vb=0x150367B0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,-1.00,z=0.00000,col=FFFFFFFF] [-1.00,-1.00,z=0.00000,col=FFFFFFFF]
4 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0B2C0000 1280x768 fmt=6]
vb=0x15036810 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.90,0.68,z=0.00000,col=FFFFFFFF] [0.87,0.68,z=0.00000,col=FFFFFFFF] [0.87,-0.10,z=0.00000,col=FFFFFFFF] [-0.90,-0.10,z=0.00000,col=FFFFFFFF] [-0.89,0.65,z=0.00000,col=FFFFFFFF] [0.86,0.65,z=0.00000,col=FFFFFFFF] [0.86,-0.08,z=0.00000,col=FFFFFFFF] [-0.89,-0.08,z=0.00000,col=FFFFFFFF]
5 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x11C80000 1280x768 fmt=6]
vb=0x150368D0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.71,0.47,z=0.00000,col=FFFFFFFF] [0.72,0.47,z=0.00000,col=FFFFFFFF] [0.72,0.15,z=0.00000,col=FFFFFFFF] [-0.71,0.15,z=0.00000,col=FFFFFFFF] [0.76,-0.05,z=0.00000,col=FFFFFFFF] [0.82,-0.05,z=0.00000,col=FFFFFFFF] [0.82,-0.10,z=0.00000,col=FFFFFFFF] [0.76,-0.10,z=0.00000,col=FFFFFFFF]
6 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0BE30000 1280x768 fmt=6]
vb=0x15036990 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.79,0.15,z=0.00000,col=FFFFFFFF] [0.76,0.15,z=0.00000,col=FFFFFFFF] [0.76,-0.14,z=0.00000,col=FFFFFFFF] [-0.79,-0.14,z=0.00000,col=FFFFFFFF]
7 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x11C80000 1280x768 fmt=6]
vb=0x150369F0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.54,-0.82,z=0.00000,col=FFFFFFFF] [0.54,-0.82,z=0.00000,col=FFFFFFFF] [0.54,-0.87,z=0.00000,col=FFFFFFFF] [-0.54,-0.87,z=0.00000,col=FFFFFFFF] [-0.40,-0.53,z=0.00000,col=FFFFFFFF] [0.40,-0.53,z=0.00000,col=FFFFFFFF] [0.40,-0.67,z=0.00000,col=FFFFFFFF] [-0.40,-0.67,z=0.00000,col=FFFFFFFF]
8 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0B2C0000 1280x768 fmt=6]
vb=0x15036AB0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.42,-0.49,z=0.00000,col=31FFFFFF] [0.42,-0.49,z=0.00000,col=31FFFFFF] [0.42,-0.70,z=0.00000,col=31FFFFFF] [-0.42,-0.70,z=0.00000,col=31FFFFFF]
9 prim=8 indices=3 vs=0x72CBCAA6A7984111 ps=0xE59B2B3DA4AA9008 tex[base=0x10000000 1x1 fmt=26]
10 prim=8 indices=3 vs=0x72CBCAA6A7984111 ps=0xE59B2B3DA4AA9008 tex[base=0x10000000 1x1 fmt=26]
--- frame 3 ---
11 prim=8 indices=3 vs=0x0A6D1DD7767FDF27 ps=0x2E372EA28CC404B7
vb=0x15076C38 stride=28 attrs=[57@0 38@12 ] fmt0=57 v: [-0.50,-0.50,z=0.00000] [1279.50,-0.50,z=0.00000] [1279.50,719.50,z=0.00000]
12 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0BA60000 1280x768 fmt=6]
vb=0x15076C90 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,-1.00,z=0.00000,col=FFFFFFFF] [-1.00,-1.00,z=0.00000,col=FFFFFFFF]
13 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x11C80000 1280x768 fmt=6]
vb=0x15076CF0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [0.73,1.58,z=0.00000,col=C5FFFFFF] [1.27,1.02,z=0.00000,col=C5FFFFFF] [0.43,-1.57,z=0.00000,col=C5FFFFFF] [-0.11,-1.02,z=0.00000,col=C5FFFFFF] [-1.32,1.02,z=0.00000,col=B8FFFFFF] [-0.88,1.81,z=0.00000,col=B8FFFFFF] [0.71,-1.02,z=0.00000,col=B8FFFFFF] [0.27,-1.81,z=0.00000,col=B8FFFFFF]
14 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0B690000 1280x768 fmt=6]
vb=0x15076DB0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,1.00,z=0.00000,col=FFFFFFFF] [1.00,-1.00,z=0.00000,col=FFFFFFFF] [-1.00,-1.00,z=0.00000,col=FFFFFFFF]
15 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0B2C0000 1280x768 fmt=6]
vb=0x15076E10 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.90,0.68,z=0.00000,col=FFFFFFFF] [0.87,0.68,z=0.00000,col=FFFFFFFF] [0.87,-0.10,z=0.00000,col=FFFFFFFF] [-0.90,-0.10,z=0.00000,col=FFFFFFFF] [-0.89,0.65,z=0.00000,col=FFFFFFFF] [0.86,0.65,z=0.00000,col=FFFFFFFF] [0.86,-0.08,z=0.00000,col=FFFFFFFF] [-0.89,-0.08,z=0.00000,col=FFFFFFFF]
16 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x11C80000 1280x768 fmt=6]
vb=0x15076ED0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.71,0.47,z=0.00000,col=FFFFFFFF] [0.72,0.47,z=0.00000,col=FFFFFFFF] [0.72,0.15,z=0.00000,col=FFFFFFFF] [-0.71,0.15,z=0.00000,col=FFFFFFFF] [0.76,-0.05,z=0.00000,col=FFFFFFFF] [0.82,-0.05,z=0.00000,col=FFFFFFFF] [0.82,-0.10,z=0.00000,col=FFFFFFFF] [0.76,-0.10,z=0.00000,col=FFFFFFFF]
17 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0BE30000 1280x768 fmt=6]
vb=0x15076F90 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.79,0.15,z=0.00000,col=FFFFFFFF] [0.76,0.15,z=0.00000,col=FFFFFFFF] [0.76,-0.14,z=0.00000,col=FFFFFFFF] [-0.79,-0.14,z=0.00000,col=FFFFFFFF]
18 prim=13 indices=8 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x11C80000 1280x768 fmt=6]
vb=0x15076FF0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.54,-0.82,z=0.00000,col=FFFFFFFF] [0.54,-0.82,z=0.00000,col=FFFFFFFF] [0.54,-0.87,z=0.00000,col=FFFFFFFF] [-0.54,-0.87,z=0.00000,col=FFFFFFFF] [-0.40,-0.53,z=0.00000,col=FFFFFFFF] [0.40,-0.53,z=0.00000,col=FFFFFFFF] [0.40,-0.67,z=0.00000,col=FFFFFFFF] [-0.40,-0.67,z=0.00000,col=FFFFFFFF]
19 prim=13 indices=4 vs=0xE0BAFB4F520FE441 ps=0xE59B2B3DA4AA9008 tex[base=0x0B2C0000 1280x768 fmt=6]
vb=0x150770B0 stride=24 attrs=[57@0 6@12 37@16 ] fmt0=57 v: [-0.42,-0.49,z=0.00000,col=1EFFFFFF] [0.42,-0.49,z=0.00000,col=1EFFFFFF] [0.42,-0.70,z=0.00000,col=1EFFFFFF] [-0.42,-0.70,z=0.00000,col=1EFFFFFF]
20 prim=8 indices=3 vs=0x72CBCAA6A7984111 ps=0xE59B2B3DA4AA9008 tex[base=0x10000000 1x1 fmt=26]
21 prim=8 indices=3 vs=0x72CBCAA6A7984111 ps=0xE59B2B3DA4AA9008 tex[base=0x10000000 1x1 fmt=26]

View File

@@ -555,7 +555,7 @@ Identical to within 0.001 over 17.5 s. **The band is settled by t = 4.0 s**, the
capture handed to the port agent is sound, and the caveat is withdrawn. It also
corroborates that the groups **hold**: nothing crosses that band in 22 s.
## The swoosh: undecodable from the disc, with reach
## The swoosh — SOLVED by draw capture (see below); the elimination trail kept
Seven candidates eliminated, none confirmed:
@@ -595,3 +595,51 @@ So the route splits:
Either way it is instrumentation of the running guest, not another field in the
file.
## ✅ SOLVED — the game draws the swoosh as ROTATED QUADS, which our blit cannot
The route named in the previous section was run: `--ui_draw_capture_frames=3`,
armed with F10 on the settled title.
[`title-draw-capture-vertex-colours.log`](captures/title-builds/title-draw-capture-vertex-colours.log)
is the capture, 22 draws over 3 frames.
### 🔴 First, the vertex-colour hypothesis dies
Every vertex colour in the entire capture is `<alpha>FFFFFF`**white RGB**, only
the alpha varying: `FFFFFFFF`, `C5FFFFFF`, `C3FFFFFF`, `B8FFFFFF`, `B6FFFFFF`,
`31FFFFFF`, `1EFFFFFF`. The game passes no colour. That was the eighth candidate.
### ✅ And the ninth is the answer — it is the GEOMETRY
Draw 2 submits **two parallelograms, neither axis-aligned**:
| quad | corners (NDC) | edge `v0→v1` | axis-aligned? |
|---|---|---|---|
| A | `(0.70,1.58) (1.24,1.02) (0.40,1.57) (0.14,1.02)` | `(0.54, 0.56)` | **no** |
| B | `(1.29,1.02) (0.85,1.81) (0.75,1.02) (0.31,1.81)` | `(0.44, 0.79)` | **no** |
Both verified parallelograms (opposite edges equal to 0.01), both **rotated**
roughly 45° and 61° — and both extending to `y = ±1.81`, well beyond the screen.
That is the diagonal `Z` stroke.
**Our compositor cannot draw that.** `ui_layout::blit` walks destination rows and
columns of an **axis-aligned rectangle** (`for row in 0..dh { for col in 0..dw`),
sampling the source by a straight ratio. It has no rotation. So the swoosh is
blitted upright where the game draws it skewed — which is exactly the observed
signature: **right on average (+1.83), right in position (peak at (0,0)), wrong in
structure (edge-corr 0.70)**, dark on one side of the true stroke and bright on
the other.
### ❔ And a real gap in the decoded format
The keyframe carries `fade`, `scale_x`, `scale_y`, `tint`, `x`, `y`, `time`
([`ui_layout.rs`](../../crates/sylpheed-formats/src/ui_layout.rs)) — **no
rotation**. So where the game's rotation comes from is **not decoded**: either a
field not yet identified, or the element is positioned by code rather than by its
keyframes. That is the open question this leaves.
⚠️ **The "pink versus white" reading is now suspect.** It was a visual comparison
of two differently-*shaped* renderings. Whether any colour difference survives
correct geometry is **untested**, and should be re-checked rather than carried
forward as a separate defect.