From 0ecb20bdbcd5e0764318267385a737ba3dad9014 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Fri, 28 Aug 2026 22:13:50 +0000 Subject: [PATCH] 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 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. --- docs/port/HANDOFF.md | 14 ++++++ docs/re/REFUTED.md | 5 ++ .../title-draw-capture-vertex-colours.log | 43 ++++++++++++++++ docs/re/ui-title-build-map.md | 50 ++++++++++++++++++- 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 docs/re/captures/title-builds/title-draw-capture-vertex-colours.log diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 7546d83..2ae0574 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -109,6 +109,20 @@ authored version can be deleted. test is to blend bit-`0x02` sprites additively and re-correlate the title against the capture. Note `ptlogo_back2eff` is `0x8830` despite its name, so this is a field and not a naming pattern. ❔ Not diagnosed. + ✅ **SOLVED 2026-08-28 by draw capture — it is the GEOMETRY.** The game submits + the swoosh as **two rotated parallelograms** (edges `(0.54,−0.56)` and + `(0.44,0.79)`, ~45° and ~61°, extending to `y=±1.81` NDC). + `ui_layout::blit` draws **axis-aligned rectangles only**, so it blits the sprite + upright — right on average, right in position, wrong in shape, which is the + measured signature exactly. **A port that blits upright rects will have the same + defect.** + 🔴 Vertex colour is refuted with it: every colour in the capture is + `FFFFFF`, white RGB. + ❔ **Format gap:** the decoded keyframe has `fade`/`scale`/`tint`/`x`/`y`/`time` + and **no rotation**, so where the rotation comes from is not decoded. + ⚠️ The earlier "pink versus white" reading compared two differently-shaped + renderings and should be re-checked after geometry, not carried as a separate + defect. ❔ **Classified: undecodable from the disc, with reach.** Seven candidates eliminated — pivot (inert at scale 100 *and* no measured displacement), `fade`, `tint`, texture colour, additive blend via `+0x04` bit `0x02`, and diff --git a/docs/re/REFUTED.md b/docs/re/REFUTED.md index 750e1a6..4cf9fef 100644 --- a/docs/re/REFUTED.md +++ b/docs/re/REFUTED.md @@ -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 `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 diff --git a/docs/re/captures/title-builds/title-draw-capture-vertex-colours.log b/docs/re/captures/title-builds/title-draw-capture-vertex-colours.log new file mode 100644 index 0000000..0346bc6 --- /dev/null +++ b/docs/re/captures/title-builds/title-draw-capture-vertex-colours.log @@ -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] diff --git a/docs/re/ui-title-build-map.md b/docs/re/ui-title-build-map.md index 1f92ec0..66cd2d6 100644 --- a/docs/re/ui-title-build-map.md +++ b/docs/re/ui-title-build-map.md @@ -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 `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. \ No newline at end of file