From 41f13318f4b315c06ffbc4b02fb469fd8b4d6565 Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Thu, 3 Sep 2026 19:21:32 +0000 Subject: [PATCH] port(exporter): 0x3003 is a button too -- the OPTIONS menu navigates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Decoder decoded bit 0 of kind as the PARENT FLAG, disc-wide: kind & 1 agrees with 'has a parent' on 15493 elements, zero disagreements. So 0x3003 is 0x3002 parented, and carries no role information -- the OPTIONS rows were never a different class, they just have a parent. Detector now matches 0x3002 | 0x3003 as TWO LISTED VALUES, not a mask. kind & 0xFFFE == 0x3002 would also match 0x73002/0x73003 -- 160 elements whose 0x70000 bits are undecoded -- silently, on screens neither agent has looked at. Excluded by construction until somebody decides deliberately. Impact measured BEFORE re-exporting: exactly two screens gain buttons, options and options_jp, five rows each. No existing screen changes. Verified: main_menu -> down x3 -> (A) -> OPTIONS, then down x2 moves po_menu_btn2 -> po_menu_btn3, focus ring rendering on the highlighted row. 📌 Waiting was right. The rule I rejected -- 'carries a focus record therefore is a menu item' -- would have reached this answer by a second inference from structure and reclassified elements on unseen screens. The field cost one question and needed no inference. --- crates/sylpheed-export/src/screen.rs | 15 ++++++++++++++- docs/port/options-screens.md | 27 ++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/sylpheed-export/src/screen.rs b/crates/sylpheed-export/src/screen.rs index e505f239..c8750bf0 100644 --- a/crates/sylpheed-export/src/screen.rs +++ b/crates/sylpheed-export/src/screen.rs @@ -642,10 +642,23 @@ pub fn export_build( // Navigation order is geometric: buttons top-to-bottom by resting Y. A // focused-state record is not itself a menu item. + // + // 🔴 `0x3003` IS `0x3002`. Bit 0 of `kind` is the PARENT FLAG and carries no + // role information: decoded disc-wide over every `.pak` in `dat/`, `kind & 1` + // agrees with "has a parent" on 15 493 elements with ZERO disagreements + // (`docs/re/ui-kind-bit0-is-has-parent.md`). Matching only `0x3002` meant the + // OPTIONS menu's five rows -- parented, hence `0x3003` -- were not buttons, + // so the screen opened and could not be navigated. + // + // ⚠️ TWO VALUES, LISTED, NOT A MASK. `kind & 0xFFFE == 0x3002` would also + // match `0x73002`/`0x73003` -- 160 elements whose `0x70000` bits nobody has + // decoded -- and it would do it silently, on screens neither agent has + // looked at. Those are excluded by construction until somebody decides about + // them deliberately. let mut buttons: Vec<(i32, String)> = b .elements .iter() - .filter(|e| e.kind == 0x3002 && !e.focused) + .filter(|e| matches!(e.kind, 0x3002 | 0x3003) && !e.focused) .filter_map(|e| e.rest().map(|k| (k.y, id_of(&e.name)))) .collect(); buttons.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1))); diff --git a/docs/port/options-screens.md b/docs/port/options-screens.md index c533d423..e88e3779 100644 --- a/docs/port/options-screens.md +++ b/docs/port/options-screens.md @@ -50,7 +50,32 @@ anything beyond `main_menu → options` needs the real navigation. `main_menu` `ptbtn04` now has `goto: "options"`. Walked with the menu harness: main_menu → ⬇⬇⬇ → Ⓐ → the OPTIONS root renders. Ⓑ backs out. -## 🔴 The rows do not move, and the reason is a decode question +## ✅ RESOLVED — the rows move. `0x3003` is `0x3002` with the parent bit set + +The Decoder decoded it disc-wide: **bit 0 of `kind` is the PARENT FLAG**, and it +carries no role information. Over every `.pak` in `dat/`, `kind & 1` agrees with +"has a parent" on **15 493 elements with zero disagreements** +(`docs/re/ui-kind-bit0-is-has-parent.md`). The OPTIONS rows are parented; the +main-menu buttons are not. Same record class. + +So the detector now matches `0x3002 | 0x3003` — **two values listed, not a +mask**. `kind & 0xFFFE == 0x3002` would also catch `0x73002`/`0x73003`, 160 +elements whose high bits nobody has decoded, silently and on screens neither +agent has seen. + +**Impact measured before re-exporting, not after:** exactly two screens gain +buttons — `options` and `options_jp`, five rows each. No existing screen changes. + +Verified by walking it: `main_menu` → ⬇⬇⬇ → Ⓐ → OPTIONS, then ⬇⬇ moves +`po_menu_btn2` → `po_menu_btn3` with the focus ring rendering on the highlighted +row. + +📌 **The port was right to wait.** The rejected rule — "carries a focus record ⇒ +menu item" — would have reached the same answer here by a second inference from +structure, and would have reclassified elements on screens nobody had looked at. +The field cost one question and needed no inference at all. + +## The original section, kept for the shape of the block The exporter's button detector is `kind == 0x3002 && !focused`. The OPTIONS rows are **`kind_raw = 0x3003`**, so `role` comes out `unknown`, the export's