docs/re: SOLVED — the paint order is the screen object's child array at +0x30

The screen object holds a SECOND list of its elements, a reordering built at load
time, and that list is the paint order. It is 24 pointers at +0x30, each the
+0x00 field of one of the 48-byte element records, so both arrays hold the same
objects in different orders.

Checked against the draw capture rather than asserted: the seven elements the
capture can name sit at child slots 0, 6, 7, 13, 16, 17, 22 — strictly ascending,
in exactly the captured submission order. It also resolves the one sub-order no
static field could explain, the pair that decodes to the same 1133x280: slot 6 is
element 20 and slot 7 is element 19, so they paint 20-then-19, DESCENDING in
declaration terms. And the kind=0x4 repeat instances sit immediately after their
template, where the declaration table interleaves them.

Stated as unsolved, because the port cannot read a runtime array: deriving this
order from the bundle. The order is clearly structured rather than arbitrary —
elements sharing a sprite are adjacent and the full-screen effects lead — so it
is worth attacking, but it is not attacked here.
This commit is contained in:
Sylpheed RE agent
2026-08-19 01:46:00 +00:00
parent 3cf4e77076
commit 2afdb32baf

View File

@@ -85,3 +85,64 @@ not small integers), so the obvious "draw order table" reading is already out.
structure that can be dumped in seconds, rather than a code-reading exercise. The
next probe is to dump `+0x24` and `+0x30` on the title build and see whether
either is 24 entries in a non-declaration order.
## ✅ The paint order is the screen object's CHILD array at `+0x30`
**Status: `CONFIRMED`.** The question this project has carried since the first
capture — *what orders the elements, given that the declaration table does not*
is answered: the screen object holds a **second list**, a reordering of the
elements built at load time, and that list is the paint order.
`+0x30` is `{ptr, count, capacity}` → an array of **24 pointers** (not 12-byte
records like `+0x14`). Each pointer is the `+0x00` field of one of the 48-byte
element records, so the two arrays hold the same objects in different orders. The
permutation, read live off the title screen:
| child slot | element | | child slot | element |
|---|---|---|---|---|
| 0 | 9 `ptbase2` | | 12 | 17 `back2eff4` |
| 1 | 11 `ptloop01` | | 13 | **0 `ptlogo1`** |
| 2 | 12 `ptloop02` | | 14 | 2 `ptlogo1` (copy) |
| 3 | 10 `pteff04` | | 15 | 4 `ptlogo1` (copy) |
| 4 | 13 `pteff02.prm` | | 16 | **7 `ptlogo_tm`** |
| 5 | 6 `pteff01` | | 17 | **1 `ptlogo2`** |
| 6 | **20 `ptlogo_back2eff`** | | 18 | 3 `ptlogo2` (copy) |
| 7 | **19 `ptlogo_back2`** | | 19 | 5 `ptlogo2` (copy) |
| 8 | 14 `back2eff1` | | 20 | 22 `ptlogoall_eff` |
| 9 | 15 `back2eff2` | | 21 | 23 `ptlogoall_eff2` |
| 10 | 18 `back2eff5` | | 22 | **21 `ptcopyright`** |
| 11 | 16 `back2eff3` | | 23 | 8 `pteff00.prm` |
### Checked against the capture, not merely plausible
The seven elements the draw capture can name occupy child slots
```
ptbase2 0 back2eff 6 back2 7 ptlogo1 13 tm 16 ptlogo2 17 copyright 22
```
**strictly ascending, in exactly the captured submission order**. Two details
make this more than a coincidence of a short list:
* it **resolves the ambiguity** that no static field could: slot 6 is element
**20** (`ptlogo_back2eff`), slot 7 is element 19, so the pair paints
20-then-19 — *descending* in declaration terms. Nothing in the file predicts
that, and the runtime list states it;
* the three `kind = 0x4` repeat instances of each logo sit **immediately after
their template** (slots 13,14,15 and 17,18,19), which the declaration table
interleaves (0,1,2,3,4,5). They draw at α=0 and so never appeared in the
capture, but their placement in the list is consistent with the grouping.
### What this means for the port, stated carefully
The reimplementation cannot read a runtime array — it has to *derive* this order
from the bundle. **That derivation is not solved.** What the order shows is
structure worth attacking: elements sharing a sprite are adjacent
(`ptlogo1`×3 together, `ptlogo2`×3 together, the `back2eff*` family together),
and the full-screen/effect elements lead. So the load-time build is doing some
grouping, not an arbitrary shuffle.
Until it is derived, a reimplementation has two honest options: hard-code the
captured order for the screens that have been captured, or paint in declaration
order and accept that the title screen composites wrongly. The first is what the
evidence supports; the second is what the viewer does today.