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).
This commit is contained in:
@@ -85,9 +85,13 @@ five menu screens.
|
||||
reproduces every one of them. A reading that fixed the 24 but disturbed the rest
|
||||
would be a different rule, not this one.
|
||||
|
||||
🟡 **Reach.** 60 children (0.3 %) have **no** `opt ` block within 128 bytes and
|
||||
still fall back to the scan. None of them is on the five menu screens. Whether
|
||||
they genuinely lack the block or sit past the search window is not established.
|
||||
✅ **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
|
||||
|
||||
|
||||
120
docs/re/structures/ratc-tan-frame-sequence.md
Normal file
120
docs/re/structures/ratc-tan-frame-sequence.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# `.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.
|
||||
Reference in New Issue
Block a user