# `.tan` — one name over a run of frames, and the 60 "nameless" children it explains **Status:** ✅ `DECODED`, with a disc-wide check. This closes the 🟡 reach caveat left open by [`ratc-child-names.md`](ratc-child-names.md): *"60 children have no `opt ` block within 128 bytes and still fall back to the scan — whether they genuinely lack the block or sit past the search window is not established."* **Neither.** They are not children. They are the **ten frames of a single `.tan` resource**, and the one `opt ` block that names the whole run sits up to 545 KB behind the last of them. ## What was measured [`examples/ratc_optless_children.rs`](../../../crates/sylpheed-formats/examples/ratc_optless_children.rs) re-runs `ratc::parse`'s own guards over every child on the disc and reports, for each rejection, *which* guard fired and whether a tag exists further back ([data](../data/ratc-tan-frame-sequence.txt)): | | | |---|---| | RATC children scanned | 18 002 | | with no accepted `opt ` block | **60** | | of those, rejected by **length**, **gap** or **charset** | **0** | | of those, rejected because the only tag is **beyond the 128-byte window** | **60** | | of those, that are child #0 of their bundle | **0** | | archives involved | **1** — `GP_READY_ROOM.pak` | | bundles involved | **6** — entries 26, 30, 159, 160, 1029, 1050, each exactly 706 609 B | | children involved | **1…10 of each**, never 0 and never 11+ | The distances are the tell. Within one bundle they are ```text 213, 60 813, 121 413, 182 013, 242 613, 303 213, 363 813, 424 413, 485 013, 545 613 ``` — an exact arithmetic progression, step **60 600**. Ten different children all find the **same** `opt ` tag, because there is only one. Nine of them do not have a block that is merely far away; they have no block. ## What they are Reading bundle 26 directly, without the Rust parser, the 15 "children" resolve: | # | offset | kind | size | `opt ` at | name | |---|---|---|---|---|---| | 0 | `0x000444` | T8aD | 1 933 | −40 | `pbf15_energie_generator2.t32` | | **1…9** | `0x000bd1` … | T8aD | **60 600** each | −213 … −485 013 | **`pb_f15_eg_anm.tan`** | | **10** | `0x085e49` | T8aD | **60 632** | −545 613 | **`pb_f15_eg_anm.tan`** | | 11 | `0x094b21` | T8aD | 17 523 | −32 | `pbf15_pd_inside2.t32` | | 12 | `0x098f94` | T8aD | 75 068 | −35 | `pbenergie_generator.t32` | | 13 | `0x0ab4d0` | T8aD | 4 697 | −28 | `pbf15_eg_eff.t32` | | 14 | `0x0ac729` | RATC | 264 | −25 | `pb_s15_eg.rat` | So the format is doing something perfectly ordinary that our scan had no concept of: **`.tan` is a frame sequence.** One `opt ` block declares the resource, and its payload is a run of equal-size `T8aD` blocks, one per frame. The name is on the disc and always was. What was missing was the idea that one name can cover more than one block. `anm` in `pb_f15_eg_anm` is the authors' own abbreviation, and it agrees. ## The disc-wide check Every `opt ` block in every RATC bundle in all 33 `dat/*.pak`, by the extension it names ([`tools/re-capture/ratc_opt_name_census.py`](../../../tools/re-capture/ratc_opt_name_census.py)): | extension | count | what it is | |---|---|---| | `.t32` | 14 756 | a `T8aD` sprite | | `.rat` | 3 311 | a nested RATC leaf | | `.prm` | 367 | a primitive | | `.tbm` | 224 | — | | `.sbo` | 54 | — | | **`.tan`** | **6** | **a frame sequence** | | | **18 718** | | A RATC bundle names exactly six kinds of resource, and **`.tan` occurs six times on the whole disc** — all of them `pb_f15_eg_anm.tan`, one per language copy of the same bundle, each holding **10 frames**. **6 × 10 = 60.** That is the entire population of opt-less children, with nothing left over. The negative is closed, not narrowed. ## ⚠️ What this says about `ratc::parse` The child list **over-reports**. `parse` finds children by scanning for the four child magics, so a `.tan`'s ten frames are listed as ten anonymous children of the bundle rather than as one named resource with ten frames. The disc's "18 002 children" is therefore 18 002 *magic-delimited blocks*, of which 60 are frames. **Not changed here**, deliberately: nothing in the menu milestone reads a `.tan`, and a rewrite of the child model is a bigger change than the one fact it would buy. Recorded so that a later consumer of `.tan` knows the shape it needs. ## ❔ Not established * **The frame timing.** Ten frames of the same size is a sequence; nothing here shows the rate, whether it loops, or whether the frames are equal-duration. No field was looked for. * **The pixel layout of a 60 600-byte frame.** They decode as `T8aD` like any other sprite as far as the magic goes; their dimensions were not read. * **What `.tbm` and `.sbo` are.** They surfaced from the same census and are recorded above as counts only. * **The two `opt ` totals do not reconcile exactly** and are not forced to. This census counts **18 718** blocks; the Rust audit in [`ratc-child-names.md`](ratc-child-names.md) counts **17 942** children *with* a block. They apply different guards — the Rust one additionally requires the named thing to be one of the four child magics and to follow within 8 bytes, which `.prm` / `.tbm` / `.sbo` (645 blocks) never satisfy. That accounts for most of the 776 difference but not all of it, and the remainder was not chased. Each number is reported as what its own script measured. ## Scope `GP_READY_ROOM.pak` is **out of scope** for the menu milestone ([S1 is a no-go](../ready-room-probe.md)), and `.tan` occurs in no other archive. **None of the five menu screens contains a `.tan`**, so nothing the port draws changes. This closes a caveat on a decode the port *does* depend on, rather than adding a capability.