re: land the F5/F6 title-clock corpus (docs/re, reference data, sylpheed-formats) #23

Merged
fabi merged 511 commits from auto/frame-blend-draw-path into main 2026-09-13 11:16:19 +00:00
2 changed files with 84 additions and 0 deletions
Showing only changes of commit fdc4cc9a62 - Show all commits

View File

@@ -0,0 +1,46 @@
//! What elements sit under the port's hot residual tiles on the main menu?
//!
//! `sylpheed-port` mapped the menu's edge residual at 64 px tiles and handed over
//! coordinates without names — the element inventory is this side's. Hot tiles
//! cluster at x 384–704, y 64–256, hottest at (512,128).
//!
//! ⚠️ Their tiles are in the frame they compare in; this prints DESIGN space, and
//! the two differ by the capture offset (capture_y ≈ 64.8 + 0.992·design_y). Both
//! readings are printed so the mapping is not assumed.
//!
//! cargo run -p sylpheed-formats --example main_menu_element_extents
use sylpheed_formats::{pak::PakArchive, ui_layout};
use std::path::PathBuf;
fn main() {
let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"));
let ar = PakArchive::open(root.join("dat/GP_TITLE.pak")).expect("GP_TITLE");
let by = ar.read(&ar.entries()[5]).expect("entry 5");
let b = ui_layout::parse_build(&by).expect("build");
println!("{:<26} {:>6} {:>6} {:>7} {:>7} {}", "element", "x", "y", "pivot_x", "pivot_y", "sprite");
let mut rows: Vec<(i32, i32, String, String)> = b
.elements
.iter()
.map(|e| {
let k = e.rest();
(k.map(|k| k.x).unwrap_or(0), k.map(|k| k.y).unwrap_or(0),
e.name.clone(), e.sprite.clone().unwrap_or_default())
})
.collect();
rows.sort_by_key(|r| (r.1, r.0));
for (x, y, n, s) in &rows {
// flag anything whose rest position lands in the hot band, read both ways
let cap_y = 64.82 + 0.9919 * (*y as f64);
let hot_design = (384..=704).contains(x) && (64..=256).contains(y);
let hot_capture = (384..=704).contains(x) && (64.0..=256.0).contains(&cap_y);
let mark = match (hot_design, hot_capture) {
(true, true) => " <- HOT both readings",
(true, false) => " <- hot in DESIGN space",
(false, true) => " <- hot in CAPTURE space",
_ => "",
};
println!("{n:<26} {x:>6} {y:>6} {:>7} {:>7} {s}{mark}",
b.elements.iter().find(|e| &e.name == n).map(|e| e.pivot_x).unwrap_or(0),
b.elements.iter().find(|e| &e.name == n).map(|e| e.pivot_y).unwrap_or(0));
}
}

View File

@@ -0,0 +1,38 @@
# What sits under the port's hot residual tiles on the main menu? 2026-08-31.
#
# sylpheed-port mapped the menu's edge residual at 64 px tiles and handed over
# COORDINATES WITHOUT NAMES -- the element inventory is this side's. Their result:
# hot tiles cluster at x 384..704, y 64..256, hottest (512,128) at 3.66x the median
# tile, and NO tile in the hot region has moved (every |dx|,|dy| < 0.1 px against a
# control reading a true 1 px at +0.949).
#
# ⚠️ COORDINATE FRAME. Their tiles are in the frame they compare in; the disc is
# design space, and the two differ by the capture offset
# (capture_y = 64.82 + 0.9919 * design_y). So both readings were tested rather than
# one assumed -- and the answer does not depend on it: three elements are hot under
# BOTH.
#
# ✅ ELEMENTS WHOSE REST POSITION FALLS IN THE HOT BAND (GP_TITLE entry 5):
#
# ptframe1.t32 (440, 108) pivot (124,140) HOT under both readings
# ptbtn01.rat (542, 162) pivot ( 42, 22) HOT under both -- the NEW GAME button
# pteff12.t32 (467, 180) pivot (174,180) HOT under both -- an effect element
# ptbtn02.rat (542, 242) pivot ( 58, 22) hot in DESIGN space only
#
# 📌 THE HOT REGION IS NOT ONE ELEMENT. It is where a FRAME, a BUTTON and an
# EFFECT overlap -- three elements of different kinds stacked in the same band.
# That is consistent with their null result: they went looking for two families of
# tile (edge-only versus hot-everywhere) and found one continuous population, so
# the region has no character of its own.
#
# ⚠️ WHAT THIS DOES NOT SAY. It names what is THERE, not what is wrong. Their map
# already excludes local displacement in these tiles, so this is not a misplaced
# element; the inventory is offered as the next reader's starting point, not as a
# diagnosis. The residual's cause remains open.
#
# 📌 AND A CONTROL LIMIT OF THEIRS THAT CHANGES HOW ANY SLOPE READS: a known +2 px
# displacement localises perfectly but reads back +0.839, because the slope is a
# linearisation (residual ~ dx * gradient) that saturates once dx approaches the
# width of an edge. So their +1 px control gives localisation AND magnitude, the
# +2 px control gives localisation and SIGN only, and **any slope they report is a
# FLOOR on the displacement, never a ceiling**.