From 78cb1d4e9b405c5490e43879493d9291ddb83186 Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Sun, 30 Aug 2026 11:06:18 +0000 Subject: [PATCH] re: B on the main menu goes to the TITLE -- measured, filling an empty evidence cell menu-navigation-semantics.md had this row at yellow with an EMPTY evidence cell, and it is what the port still authors as on_cancel. Delivery-confirmed via [RE-INPUT] (B is kXInputPadB = 0x5801), change detected rather than timed. B delivered at 331.2 s; the glyph leaves 327 by 331.6 and 73.5 % of pixels differ. Both captures name themselves: PROJECT SYLPHEED with the (C)2006,2007 SQUARE ENIX line. Three things measured: * B on the main menu goes to the title; * latency <= 0.4 s at a 4 Hz sample rate, where the corpus previously had this as 'not measured (a backlogged probe void)'; * NO loading screen in between -- the disc carries four pgloading_* bundles and none appears on this path. What the run CANNOT say, recorded in the table rather than glossed: 'B on the title -> nothing' is still unevidenced. The second B was delivered during the title's build-in, so the glyph 0 -> 154 change after it is the build-in completing, not a response. A run that answers that row must wait for the title to settle before pressing. The 're-draws PRESS A after a beat' half of the first row is also still unevidenced -- the run ended with the plate absent. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v --- docs/re/data/b-on-main-menu.txt | 63 ++++++++++++++ docs/re/menu-navigation-semantics.md | 4 +- tools/re-capture/b_from_menu.py | 125 +++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 docs/re/data/b-on-main-menu.txt create mode 100755 tools/re-capture/b_from_menu.py diff --git a/docs/re/data/b-on-main-menu.txt b/docs/re/data/b-on-main-menu.txt new file mode 100644 index 00000000..4aff0a74 --- /dev/null +++ b/docs/re/data/b-on-main-menu.txt @@ -0,0 +1,63 @@ +# What โ’ท does on the MAIN MENU -- measured 2026-08-30. +# +# menu-navigation-semantics.md had this row at ๐ŸŸก with an EMPTY evidence cell, +# and it is what the port still authors as on_cancel. +# +# Harness: tools/re-capture/b_from_menu.py -- the plate-pulse title detector, +# the glyph-327 menu detector, delivery confirmed from [RE-INPUT] rather than +# from the pad, and change detected rather than timed. B is kXInputPadB = +# 0x5801 (ui/virtual_key.h:323). +# +# [321.7s] TITLE (glyph 959) +# [322.7s] A delivered (attempt 1) +# [327.7s] MENU (glyph 327) -- pressing B +# [331.2s] B delivered (attempt 1) +# [335.0s] screen changed: 73.5 % of pixels differ, glyph 0 +# [341.3s] glyph 154 +# +# THE SERIES ACROSS THE PRESS (t : glyph): +# 326.2 : 0 +# 326.5 : 327 +# 326.7 : 327 +# 326.9 : 327 +# 327.2 : 327 +# 327.4 : 327 +# 327.7 : 327 +# 331.2 : 327 +# 331.6 : 0 +# 332.4 : 0 +# 332.8 : 0 +# 333.3 : 0 +# 333.8 : 0 +# 334.1 : 0 +# 334.5 : 0 +# 335.0 : 0 +# 340.2 : 0 +# 340.5 : 54 +# 340.7 : 154 +# 340.8 : 154 +# 340.9 : 154 +# 341.0 : 154 +# 341.1 : 154 +# 341.2 : 154 +# 341.3 : 154 +# +# READING: B on the main menu goes to the TITLE. Both captures name +# themselves -- PROJECT SYLPHEED with the (C)2006,2007 SQUARE ENIX line. +# 2-after-B-on-menu.png the title MID-BUILD-IN: wordmark drawn, the green +# copyright line not yet -- which is why glyph is 0 +# while 78.0 % of the surface is inked +# 3-after-B-again.png copyright drawn, glyph 154 = the plate-absent floor +# +# โœ… NO LOADING SCREEN between. The disc carries four pgloading_* bundles and +# none appears on this path. +# +# โœ… LATENCY <= 0.4 s: B delivered at 331.2, glyph leaves 327 by 331.6, at a +# 4 Hz sample rate. The corpus previously had this as 'not measured (a +# backlogged probe void)'. +# +# ๐Ÿ”ด WHAT THIS RUN CANNOT SAY: 'B on the title -> nothing'. The second B was +# delivered at 340.2, DURING the title's build-in, so the glyph 0 -> 154 +# change that follows is the build-in completing and not a response. That row +# stays ๐ŸŸก with no evidence, and a run that answers it must wait for the title +# to SETTLE before pressing. diff --git a/docs/re/menu-navigation-semantics.md b/docs/re/menu-navigation-semantics.md index 04395664..70a9bb4d 100644 --- a/docs/re/menu-navigation-semantics.md +++ b/docs/re/menu-navigation-semantics.md @@ -23,8 +23,8 @@ with [`tools/re-capture/menu_focus.py`](../../tools/re-capture/menu_focus.py). | **wrap at the bottom** | โฌ‡ from the last item goes to the **first** | same, panels 3โ†’4, and 4 presses from `EXTRAS` landing on `OPTIONS` โ€” i.e. wrapping โ€” is what makes the count come out | | **left / right** | **nothing**, on the main menu | cursor unmoved across one โฌ… and one โžก | | **โ’ท on a submenu** | returns to the parent **with focus restored to the item you entered from** โ€” `LOAD GAME`โ†’`LOAD GAME`, `TUTORIAL`โ†’`TUTORIAL`, `OPTIONS`โ†’`OPTIONS`, `EXTRAS`โ†’`EXTRAS` | 4/4 | -| **โ’ท on the main menu** | ๐ŸŸก goes to the **title**, which re-draws `PRESS โ’ถ BUTTON` after a beat | **none** โ€” and the main menu's own footer does not advertise โ’ท; [downgraded below](#-refutation-attempt-2026-08-29--the-main-menus-own-footer-does-not-advertise-โ“‘) | -| **โ’ท on the title** | ๐ŸŸก **nothing** | **none** | +| **โ’ท on the main menu** | โœ… goes to the **title** โ€” measured 2026-08-30, delivery-confirmed, **โ‰ค 0.4 s**, and with **no loading screen** in between. โš ๏ธ the *"re-draws `PRESS โ’ถ` after a beat"* half is still unevidenced: the run ended with the plate still absent | **1 run** โ€” [`../re/data/b-on-main-menu.txt`](data/b-on-main-menu.txt) ยท [mid-build-in](captures/title-builds/live-b-on-menu-title-buildin.png) ยท [settled](captures/title-builds/live-b-on-menu-title-settled.png). The footer point below still stands | +| **โ’ท on the title** | ๐ŸŸก **nothing** | **none** โ€” โš ๏ธ and the 2026-08-30 run **cannot** be counted here: its second โ’ท was delivered *during* the title's build-in, so the glyph 0 โ†’ 154 that followed is the build-in completing, not a response. A run that answers this must wait for the title to **settle** first | Wrap holds on both screens tested โ€” the 5-item main menu and the 3-item `EXTRAS` submenu โ€” so it is a menu rule, not a per-screen table. diff --git a/tools/re-capture/b_from_menu.py b/tools/re-capture/b_from_menu.py new file mode 100755 index 00000000..4c08dd87 --- /dev/null +++ b/tools/re-capture/b_from_menu.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""What does โ’ท do on the MAIN MENU, and then on the TITLE? + +`menu-navigation-semantics.md` has those two rows at ๐ŸŸก with **empty evidence +cells** โ€” "goes to the title, which re-draws PRESS โ’ถ after a beat" and "nothing" โ€” +while `โ’ท on a submenu` is 4/4 measured. They are what the port still authors as +`on_cancel`. + +Everything here is the harness validated in `tbm-submenu-not-reached.md`: +the plate-pulse title detector, the glyph-327 menu detector, **delivery confirmed +from `[RE-INPUT]` rather than from the pad**, and change detected rather than timed. +โ’ท is `kXInputPadB = 0x5801` (`ui/virtual_key.h:323`). + + b_from_menu.py LOG OUTDIR [wait_s] +""" +import os +import re +import subprocess +import sys +import time + +import numpy as np +from PIL import Image + +LOG, OUT = sys.argv[1], sys.argv[2] +WAIT = float(sys.argv[3]) if len(sys.argv) > 3 else 480 +W, H = 1280, 720 +NEED, CEIL, HOLD = 500, 2500, 12 +MENU_LO, MENU_HI, MENU_HOLD = 250, 420, 6 +PAD = os.path.join(os.path.dirname(os.path.abspath(__file__)), "pad.py") + + +def deliveries(vk): + pat = re.compile((r"RE-INPUT\] XamInputGetKeystrokeEx -> user=\d+ vk=%s flags=0001" % vk).encode()) + try: + return len(pat.findall(open(LOG, "rb").read())) + except FileNotFoundError: + return 0 + + +def press(btn, vk, tries=5): + for k in range(tries): + before = deliveries(vk) + subprocess.run([sys.executable, PAD, "tap", btn, "0.5"], check=False) + for _ in range(20): + time.sleep(0.25) + if deliveries(vk) > before: + print(f"[{time.time()-T0:7.1f}s] {btn} delivered (attempt {k+1})", flush=True) + return True + print(f"[{time.time()-T0:7.1f}s] {btn} NOT delivered (attempt {k+1})", flush=True) + return False + + +def _open(): + return subprocess.Popen( + ["ffmpeg", "-loglevel", "error", "-f", "x11grab", "-draw_mouse", "0", + "-video_size", f"{W}x{H}", "-i", ":98", "-r", "4", + "-f", "rawvideo", "-pix_fmt", "rgb24", "-"], + stdout=subprocess.PIPE, bufsize=W * H * 3 * 2) + + +def glyph(a): + r, g, b = a[:, :, 0], a[:, :, 1], a[:, :, 2] + return int(((g > 130) & (g - r > 45) & (g - b > 45)).sum()) + + +T0 = time.time() +p, n, seg = _open(), W * H * 3, time.time() +log = open(f"{OUT}/series.tsv", "w"); log.write("# t_s\tglyph\tmean\tphase\n") +phase, streak, base, stable, mark = "wait", 0, None, 0, None +while True: + el = time.time() - T0 + if el > WAIT: + print(f"TIMEOUT in phase {phase}", flush=True); break + if time.time() - seg > 30: + p.kill(); p = _open(); seg = time.time() + buf = p.stdout.read(n) + if len(buf) < n: + p.kill(); p = _open(); seg = time.time(); continue + a = np.frombuffer(buf, np.uint8).reshape(H, W, 3).astype(int) + c = glyph(a) + log.write(f"{el:.3f}\t{c}\t{a.mean():.3f}\t{phase}\n"); log.flush() + if phase == "wait": + streak = streak + 1 if NEED <= c <= CEIL else 0 + if streak >= HOLD: + print(f"[{el:7.1f}s] TITLE (glyph {c})", flush=True) + press("A", "5800"); phase, streak = "tomenu", 0 + elif phase == "tomenu": + streak = streak + 1 if MENU_LO <= c <= MENU_HI else 0 + if streak >= MENU_HOLD: + Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/1-menu.png") + base = a.astype(float) + print(f"[{el:7.1f}s] MENU (glyph {c}) โ€” pressing B", flush=True) + time.sleep(2.0) + press("B", "5801"); mark = time.time(); phase, stable = "afterB1", 0 + elif phase == "afterB1": + diff = float((np.abs(a - base).max(axis=2) > 12).mean()) + if diff > 0.25: + stable += 1 + if stable >= 8: + Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/2-after-B-on-menu.png") + print(f"[{el:7.1f}s] B ON MENU CHANGED THE SCREEN: {100*diff:.1f}% differ, glyph {c}", flush=True) + base = a.astype(float); time.sleep(3.0) + press("B", "5801"); mark = time.time(); phase, stable = "afterB2", 0 + else: + stable = 0 + if time.time() - mark > 30: + Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/2-after-B-on-menu.png") + print(f"[{el:7.1f}s] B ON MENU: NO CHANGE in 30 s ({100*diff:.1f}% differ, glyph {c})", flush=True) + base = a.astype(float); press("B", "5801"); mark = time.time(); phase, stable = "afterB2", 0 + elif phase == "afterB2": + diff = float((np.abs(a - base).max(axis=2) > 12).mean()) + if diff > 0.25: + stable += 1 + if stable >= 8: + Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/3-after-B-again.png") + print(f"[{el:7.1f}s] SECOND B CHANGED THE SCREEN: {100*diff:.1f}% differ, glyph {c}", flush=True) + break + else: + stable = 0 + if time.time() - mark > 30: + Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/3-after-B-again.png") + print(f"[{el:7.1f}s] SECOND B: NO CHANGE in 30 s ({100*diff:.1f}% differ, glyph {c})", flush=True) + break +p.kill(); log.close()