This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/structures/ratc-child-names.md
Sylpheed RE agent f817dd5939 re(ui): the 60 nameless RATC children are frames, not children -- .tan decoded
Closes the reach caveat the `opt ` name fix left behind: 60 of 18 002 RATC
children carry no `opt ` block, and it was not established whether they lack one
or sit past our 128-byte window.

Neither. They are not children. `examples/ratc_optless_children.rs` re-runs
`ratc::parse`'s own guards over the disc and reports which one fired: all 60 are
"tag beyond the window", none is rejected by length, gap or charset, none is
child #0, and all 60 live in six bundles of one archive. Within a bundle the
distances back to the nearest tag are an exact arithmetic progression, step
60 600 -- ten different records finding the SAME tag, because there is only one.

Reading a bundle directly: children 1..10 are equal-size T8aD blocks under a
single `opt ` name, `pb_f15_eg_anm.tan`. `.tan` is a FRAME SEQUENCE. One block
declares the resource; its payload is a run of T8aD frames.

Disc-wide, over all 18 718 `opt ` names in all 33 paks: a RATC bundle names
exactly six kinds of resource -- `.t32` 14 756, `.rat` 3 311, `.prm` 367,
`.tbm` 224, `.sbo` 54, `.tan` 6. Six `.tan`, ten frames each = 60, the entire
population with nothing left over. The negative is closed, not narrowed.

Consequence recorded but deliberately not fixed: `ratc::parse` over-reports
there, listing a `.tan`'s frames as anonymous children. Nothing in the menu
milestone reads a `.tan` -- it occurs only in GP_READY_ROOM, which S1 ruled
out -- so no screen the port draws changes.

Also a METHOD entry for this container OOM-killing `slb_leading_segment_disc`
under default test parallelism (SIGKILL, no assertion; 8/8 pass with
--test-threads=1).
2026-08-29 07:39:09 +00:00

6.6 KiB
Raw Blame History

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:

"opt " | BE32 length | name | NUL | 3 bytes | <child magic>

Two children of the main menu bundle (GP_TITLE.pak entry 8), raw:

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

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 compares the two readings for every RATC child in all 33 dat/*.pak (data):

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

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):

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; 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 drawnptbase.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

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.