# A nested record's `+0x08` is its loop length — and the plate holds dark for 15 units **Classification: decoded.** The field is read from the disc and checked disc-wide (1 781 records, 0 violations). The consequence for the `PRESS Ⓐ` plate is then tested against the corpus's existing measurements of the running game, which it passes and the previous reading fails. ## The question The port asked it directly: the plate's pulse group runs t=0 → t=105 with alpha 0 at both ends. **Does it loop from its start, or hold at alpha 0 between cycles?** It matters because a 105-unit period is **1.750 s**, and the corpus timed the real pulse four times at **2.12 / 2.19 / 2.34 / 2.31 s** — about 17 % longer. The port shipped 105 anyway, saying the disc's number was wrong, because the alternative (129) was built on an `exit_ramp_units` constant that a decode had just deleted. ## The answer: it holds, and the period is 120 A nested record is **itself a RATC bundle, with its own header**, and that header's `+0x08` is a frame count — the same field [`ui-header-time-disc`](../../crates/sylpheed-formats/tests/ui_header_time_disc.rs) already tests as an animation length at the top level. Read it on the record and the answer is immediate: | record | `+0x08` | largest keyframe | slack | |---|---|---|---| | `ptbtn00f.rat` — the plate glow | **120** | 105 | **15** | | `ptbtn01f` … `ptbtn05f` — main menu focus | 120 | 120 | 0 | | `ptloop01.rat` | 600 | 600 | 0 | | `ptloop02.rat` | 720 | 720 | 0 | **The glow ramps 0 → 80 → 0 over 105 units inside a 120-unit cycle, so it rests dark for 15 units between pulses.** The five main-menu focus records fill their cycle exactly, which is what shows the slack is a property of *this record* rather than of the format. ## Disc-wide [`record-loop-length-census.txt`](../data/record-loop-length-census.txt) — every nested record on the disc that has a timed keyframe: | | count | share | |---|---|---| | nested records with timed keyframes | 1 781 | — | | `+0x08 == ` largest keyframe time | 1 643 | 92.3 % | | `+0x08 > ` largest keyframe time (a hold) | 138 | 7.7 % | | **`+0x08 < ` largest keyframe time** | **0** | **0.00 %** | The last row is the falsifier and it never fires: **no record declares a cycle that would restart before its own last pose.** The 7.7 % is what keeps the reading from being an unfalsifiable relabelling of the keyframes — if every record declared exactly its own last keyframe time, the field would carry nothing. ## The falsification test against the running game Both hypotheses are periods in declared units, and both have to be converted by the same emulator pacing factor. That factor is measured **independently**, on the main menu's focus ring: declared 120 units, measured **2.177 s** ([`focus-ring-spin-measured.md`](../focus-ring-spin-measured.md)), so the factor is **1.0885** against a nominal 60 units/s. | plate period | nominal | factor it would need to reach 2.12–2.34 s | verdict | |---|---|---|---| | 105 units | 1.750 s | **1.211 … 1.337** | 🔴 **excludes** the ring's 1.0885 | | **120 units** | 2.000 s | **1.060 … 1.170** | ✅ **contains** the ring's 1.0885 | At 120 units and the ring's own factor the plate should pulse every **2.177 s**, against a measured 2.12–2.34 s. **105 cannot reach the measured range under any pacing factor that the ring also satisfies.** This is a genuine test rather than a fit: the ring and the plate are different elements in different bundles, measured in separate runs, and the only thing tying them together is that both declare a 120-unit cycle. ## What this replaces * 🔴 **105 is wrong** and the port should stop shipping it. The number is **120**, and it comes from the disc — not from `exit_ramp_units`, the deleted constant whose 129 happened to fit. * ✅ **The port's ambiguity is genuinely resolved**, just not the way it read: the old 123-vs-129 pair straddled the right answer without containing it. * ⚠️ **The 2.24 s mean is still ~3 % above the 2.177 s prediction.** That is inside the spread of the four measurements (2.12–2.34) and is not evidence of a further hold; it is what a four-sample wall-clock measurement of a ~2 s period in this emulator looks like. ## Reach ⚠️ **This says where a cycle ends, not that every record cycles.** 92.3 % of records declare no slack at all, and a record whose element is not in a repeating state (a button's base record, a one-shot build-in) has a length that is simply its own duration. Nothing here establishes *which* records the game restarts — only that when one does, `+0x08` is where. ❔ **The top-level `+0x08` is not the same thing.** Every `GP_TITLE` entry declares 300 there while its elements end at 244–269, and no screen visibly repeats every 5 s. Whether the top-level field is a loop length, a budget or something else is untouched by this. ## Reproducing ```bash cargo run -p sylpheed-formats --example record_loop_length SYLPHEED_DISC=/disc cargo test -p sylpheed-formats --test ui_record_loop_length_disc ```