From a40a0b7b019949a796223df8b4f7086e73b40ac6 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Mon, 24 Aug 2026 07:34:56 +0000 Subject: [PATCH] docs: looking closer at the frame-rate word weakened it, and that is the result The 16.16 reading of +0x04 rested on twelve bundles at 30.0. They are not twelve witnesses: they are TWO bundles - pghud_range_main_em/emeff and pghud_range_nose_em/emeff, both dur 30 - repeated across six language PAKs. A 30-frame flash is equally consistent with 0.5 s at 60 and 1 s at 30, so the observation that "30.0 bundles are shorter" says almost nothing. Worse for the clean fixed-point reading: the four bundles at 0x3C0001 would be 60.0000152 fps, which nobody authors. They are py_ranking_jump/py_ranking_next dialogs, all dur 60. So the better reading is . - a rate-like number in the high half and a small low field that is 0 on 2843 bundles and 1 on four, meaning unknown. Also stated: nothing in this container can settle it by timing, because the emulator runs on software Vulkan far from real time - a stopwatch would measure lavapipe rather than the game. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE --- .../tests/ui_header_time_disc.rs | 15 +++++++++++ docs/re/structures/ui-rat-layout.md | 26 ++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/sylpheed-formats/tests/ui_header_time_disc.rs b/crates/sylpheed-formats/tests/ui_header_time_disc.rs index fc77051..cc3d8d7 100644 --- a/crates/sylpheed-formats/tests/ui_header_time_disc.rs +++ b/crates/sylpheed-formats/tests/ui_header_time_disc.rs @@ -69,6 +69,10 @@ fn header_0x08_against_the_keyframe_times() { let mut ratio: HashMap = HashMap::new(); // Does the rate word co-vary with anything? let mut by_rate: HashMap = HashMap::new(); // rate -> (bundles, max seen 0x08) + // The 16.16 frame-rate reading rests on twelve bundles at 30.0. If those are + // VARIANTS of 60.0 bundles - same elements, different rate - the reading + // gains a real discriminator; if they are unrelated one-offs it does not. + let mut odd_rate: Vec = Vec::new(); for_each_build(&root, |pak, bytes| { if bytes.len() < 0x20 { @@ -86,6 +90,14 @@ fn header_0x08_against_the_keyframe_times() { e.0 += 1; e.1 = e.1.max(dur); + if rate != 0x3C_0000 && odd_rate.len() < 20 { + let names: Vec<&str> = build.elements.iter().map(|e| e.name.as_str()).take(6).collect(); + odd_rate.push(format!( + "{pak}: rate {rate:#x} dur {dur} elements {} {:?}", + build.elements.len(), + names + )); + } let max_time = build .elements .iter() @@ -124,6 +136,9 @@ fn header_0x08_against_the_keyframe_times() { for w in &worst { eprintln!(" over: {w}"); } + for o in &odd_rate { + eprintln!(" non-60 rate: {o}"); + } assert!(animated > 0, "no animated bundles — the sweep is broken"); diff --git a/docs/re/structures/ui-rat-layout.md b/docs/re/structures/ui-rat-layout.md index 48f048d..ccb4d69 100644 --- a/docs/re/structures/ui-rat-layout.md +++ b/docs/re/structures/ui-rat-layout.md @@ -575,7 +575,25 @@ that `+0x08` is the animation's **length in the same unit as the keyframe `time` field**. That the unit is *frames* still rests on the values themselves (30 / 60 / 120 / 1200) and on the 16.16 reading of `+0x04`. -🟡 **`+0x04` stays amber.** The one new piece of evidence is weak but points the -right way: the twelve bundles whose rate word is `0x1E0000` (30.0) have a largest -`+0x08` of **30**, while the 2 843 at `0x3C0000` (60.0) go up to **1 440**. Twelve -bundles is not a demonstration, and nothing here measures a wall-clock duration. +🟡 **`+0x04` stays amber, and looking closer WEAKENED it.** The twelve bundles at +`0x1E0000` are not twelve witnesses: they are **two** bundles repeated across six +language PAKs — + +``` +pghud_range_main_em.t32 + pghud_range_main_emeff.t32 dur 30 +pghud_range_nose_em.t32 + pghud_range_nose_emeff.t32 dur 30 +``` + +— so the whole "30.0 bundles are shorter" observation rests on two authored +assets, and a 30-frame flash is equally consistent with 0.5 s at 60 and 1 s at 30. + +🔴 **And the four `0x3C0001` bundles argue against a clean 16.16 value.** As +fixed-point that is 60.0000152 fps, which nobody authors; the four are +`py_ranking_jump`/`py_ranking_next` dialogs in `GP_DIALOG.pak`, all with +`dur = 60`. The better reading is **`.`** — a rate-like number +in the high half (60 or 30) and a small field in the low half that is 0 on 2 843 +bundles and 1 on four. What that low field means is ❔. + +Nothing here measures a wall-clock duration, and **nothing in this container +can**: the emulator runs on software Vulkan far from real time, so timing an +animation would measure lavapipe, not the game.