re: placement X/Y are SIGNED, the tail word is time, and the resting pose is max-dwell

Dumping the remaining Arsenal components turned up placements reading as
4294966780 and 4294966856 -- negative as i32 (-516, -440). X and Y are signed;
off-screen animation starts are negative, and read as unsigned a reimplementation
would draw them four billion pixels off.

The raw region then showed the rest of it. The trailing word is a TIME, and a
group is an in -> hold -> out animation: prselect_win1 runs
t=4:-516 -> 6:-71 -> 7:81 -> 8:127 -> 23:134 -> 24:134 -> 25:127 -> 27:81 ->
31:-71 -> 1:-516. So NEITHER the first nor the last keyframe is where the element
sits -- both are off-screen. The resting position is the max-dwell keyframe,
(127,155)..(134,155) here, which is where the weapon-list panel actually appears.

This corrects my own note from earlier today, which reported "final=" positions.
Static elements were unaffected (the pause menu's buttons have identical
keyframes) but animated ones were misread. screen_layout.rs now reports the
max-dwell pose plus the full timeline, and both captures are regenerated.

Also seen while dumping: kind 0x2, and a .sbo child (scrollbar) parented to the
list window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-11 22:14:36 +00:00
parent de357ffdc9
commit 35e2d1067f
4 changed files with 101 additions and 44 deletions

View File

@@ -202,17 +202,33 @@ recording them because a static-only reading would have shipped them:
```
group header: u32 element index (0,1,2… — the correspondence is stated, not inferred)
u32 keyframe count
then `count` keyframes, 40 bytes apart, each holding the familiar block:
then `count` keyframes, 40 bytes apart, each holding:
u32 scale X = 100 u32 scale Y = 100
u32 tint = 0xffffffff
u32 X u32 Y (+ a trailing time field)
i32 X i32 Y ← SIGNED
u32 time u32 flags/terminator
```
**Three things here are easy to get wrong, and were:**
- **X and Y are signed.** Off-screen animation starts are negative — the Arsenal's
weapon-list window begins at **X = 516**. Read as unsigned it is 4 294 966 780,
which a naïve implementation would happily draw four billion pixels to the right.
- **The trailing word is a time**, and a group is an **in → hold → out**
animation. `prselect_win1` runs `t=4:516 → 6:71 → 7:81 → 8:127 → 23:134 →
24:134 → 25:127 → 27:81 → 31:71 → 1:516`.
- **So neither the first nor the last keyframe is where the element sits.** Both
are off-screen for an animated element. The resting position is the
**max-dwell** keyframe — the one with the longest gap to the next time — which
for that window is **(127,155)…(134,155)**, matching where the list panel
actually appears. `screen_layout.rs` reports that, not `final`.
For the tutorial pause bundle this yields 11 groups for 11 elements, and every
group's header index and count match the blocks actually present. The values are
self-evidently right: the three menu buttons sit at X=546, **70 px apart**
(288 / 358 / 428), the title at (540,119), the message at (451,545), and the
`eff*` sprites carry multi-position fly-ins.
(288 / 358 / 428) — static, every keyframe identical, which is why their reading
was unaffected by the first/last mistake above — the title at (540,119), the
message at (451,545), and the `eff*` sprites carry multi-position fly-ins.
**Cross-checked against the records themselves:** `pgp_ttrl_btn10.rat` places its
sprite at **(546,288)** — identical to its inline group. So the inline region is