# A RATC child's name is stated by an `opt ` block, not by the bytes before it **Status:** ✅ `DECODED`, with a disc-wide check. This fixes a decoder defect that silently dropped the **full-resolution background from all five menu screens**. ## The field Immediately before nearly every child of a `RATC` bundle — 17 942 of the disc's 18 002 — sits an `opt ` block: ```text "opt " | BE32 length | name | NUL | 3 bytes | ``` Two children of the main menu bundle (`GP_TITLE.pak` entry 8), raw: ```text opt 00 00 00 0a p t b a s e . t 3 2 00 0e 10 a4 T8aD opt 00 00 00 0b p t e f f 0 5 . t 3 2 00 38 41 58 T8aD ^^^^^^^^ "8AX" ``` This is the **same `opt ` block** `ui_layout::opt_link` already decodes for a button's focus link. Nothing new had to be discovered to read it — only noticed. ## The defect it fixes `ratc::parse` named each child with `name_before`: the last printable run in the 96 bytes before the child's magic. That is a heuristic, and it is *usually* right — `ptbase.t32`'s three trailing bytes are `0e 10 a4`, not printable, so the scan walks back to the real name. But `pteff05.t32`'s trailing three bytes are `38 41 58`, which is `"8AX"` in ASCII. The scan takes those, the child is registered under the name `8AX`, the element that declares `pteff05.t32` matches nothing in the sprite table, and `compose` drops it through ```rust let Some(sprite) = el.sprite.as_ref() else { continue }; ``` — *before* the arm that records a `missing` sprite. So the screen's background vanished with **no diagnostic at all**: `screen render` reported "all resolved". ⚠️ `8AX` was carried in `docs/` as though it were a name the game uses — the old text read "the `T8aD` behind its `opt ` link is registered under the name `8AX`". It is not a name. It is three bytes of the preceding record's payload. ## The disc-wide check [`examples/ratc_child_names.rs`](../../../crates/sylpheed-formats/examples/ratc_child_names.rs) compares the two readings for every RATC child in all 33 `dat/*.pak` ([data](../data/ratc-child-name-audit.txt)): | | | |---|---| | RATC children scanned | **18 002** | | carrying an `opt ` block | 17 942 | | scanned name **agrees** with it | **17 918** | | scanned name **differs** | **24** | Every one of the 24 is the same failure: a 3-byte printable tail beating a real name. | scanned | actual | count | where | |---|---|---|---| | `8AX` | `pbbg.t32` | 12 | `GP_READY_ROOM` | | `8AX` | `pteff05.t32` | 4 | `GP_TITLE` 5, 6, 8, 9 — the menus | | `8AX` | `pmbase.t32` | 4 | `GP_STAGE_CLEAR` | | `8AX` | `pteff04.t32` | 2 | `GP_TITLE` 4, 7 — the title | | `'OX` | `po_keys_win1.t32` | 2 | `GP_OPTIONS` | ⚠️ **What I checked about the 24, precisely.** That the recovered name is the one the bundle actually wants is verified for the ten `GP_TITLE` and `GP_STAGE_CLEAR` cases: on the title screens `pteff04.t32`/`pteff05.t32` are *declared elements* that previously resolved to nothing and now resolve, and `pmbase.t32` is the target of `GP_STAGE_CLEAR`'s long-standing dangling reference (below). For the 12 `GP_READY_ROOM` (`pbbg.t32`) and 2 `GP_OPTIONS` (`po_keys_win1.t32`) cases I checked only that no element in those bundles is left unresolved afterwards — which is consistent with, not proof of, the same story. None of the 14 is on the five menu screens. **The control is the 17 918 the heuristic already got right**: the `opt ` reading reproduces every one of them. A reading that fixed the 24 but disturbed the rest would be a different rule, not this one. ✅ **Reach — closed 2026-08-29.** 60 children (0.3 %) have **no** `opt ` block within 128 bytes and fall back to the scan. **They are not children.** They are the ten frames of the disc's only `.tan` resource, `pb_f15_eg_anm.tan`, in the six language copies of one `GP_READY_ROOM` bundle — 6 × 10 = 60, the whole population with nothing left over. One `opt ` block names the whole run, which is why nine of the ten find no block of their own. None is on the five menu screens. [`ratc-tan-frame-sequence.md`](ratc-tan-frame-sequence.md) ## What it changes in the composite Resolving the name makes the element resolve, so `compose` now draws it. On all five port screens ([before/after](../data/ratc-name-fix-render-effect.txt)): | build | screen | mean brightness | high-frequency detail | |---|---|---|---| | 4 | title | 72.77 → 72.81 | **×1.15** | | 5 | main menu (JP) | 42.73 → 42.74 | **×1.27** | | 6 | `EXTRAS` (JP) | 44.08 → 44.09 | **×1.27** | | 8 | main menu | 41.57 → 41.58 | **×1.30** | | 9 | `EXTRAS` | 42.99 → 43.00 | **×1.29** | The brightness is unmoved and the detail is up by a quarter — which is exactly the signature of *the same artwork at twice the resolution* replacing a 2× upscale, and not of new content appearing. That it *should* be the full-res art was settled separately and against the running game, in [`ui-8ax-fullres-background.md`](ui-8ax-fullres-background.md); this page only supplies the name that lets the renderer find it. ✅ **Nothing is covered.** The background is opaque and full-screen, and on the menu it paints 4th of 16, so the worry is real. Measured at each element's resting rect, before vs after: `ptbtn01` sd 59.98 → 60.03, `ptbtn03` 63.40 → 63.45, `ptbtn05` 66.54 → 66.58, `ptframe1` 51.16 → 51.20, `ptmsg` 56.11 → 56.12. Everything survives; only the two `loop*` elements paint beneath it, and those are excluded from the default composite anyway. ⚠️ **Both backgrounds are now drawn** — `ptbase.t32` upscaled 2×, then the full-res one opaquely over it. Correct output, wasted fill. The port should draw only the full-res one, and take its *timing* from `ptbase`'s element, which is the one carrying the keyframes. ## What is NOT decoded ❔ **No declaration word points at the child.** Before reading the bytes I tested whether the 60-byte element declaration indexes the T8aD child table. Its unread words are `+28`, `+36`, `+44` and `+56`; the control is the resolved elements, whose child index is known. On the main menu, of 13 controls the words reproduce the child index **1, 0, 0 and 1** times — and both 1s are the trivial index-0 case. There is no pointer; the association is by name, and the name is the `opt ` string. [`decl_word_probe.rs`](../../../crates/sylpheed-formats/examples/decl_word_probe.rs) ✅ Incidental, from the same probe: **`+44` is a button ordinal.** It is `1…5` on exactly the five `ptbtn0N.rat` elements of the main menu, in order, and `−1` on every other element. Not needed for anything open, and recorded rather than chased.