Files
Sylpheed/tools/re-capture/tbm_screen_capture.py
sylph-decoder 12159be61f re: land the loop-point correction where the claim actually lives
The port found its exporter still shipping 'no loop-point field has been
identified anywhere' in the field manifest.json concatenates, days after the
correction existed in other fields. Auditing this corpus the same way found the
same failure here: the refuted sentence was still standing untouched in
bgm-two-stems.md -- where anyone looking up BGM behaviour arrives -- and in
HANDOFF.md, the one page the port is told to read. My correction had gone into a
NEW page only.

Both fixed in place, each naming the refutation rather than quietly deleting the
old claim, and each carrying the measured window [9.44, 71.31] s at 61.87 s.

METHOD entry: writing a correction down is not landing it. Grep the corpus for
the CLAIM, not for the file you were working in. Plus the port's trap in doing
that audit -- a replacement that quotes the refuted sentence in order to name it
will match a substring search from inside the paragraph saying it is false.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
2026-08-30 10:20:04 +00:00

85 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Reach a submenu that carries a `.tbm` and capture it.
`ui-forced-backdrop.md` leaves 24 of its 62 deciding verdicts on `.tbm` elements
whose pixels this corpus cannot locate: not in the bundle, not a file, not a pak
entry, and **not in our composite** — `compose` skips an element with no resolvable
sprite, so our renderer draws *nothing* for a `.tbm`. The open question is whether
the game draws anything either. If it does not, those verdicts are inert rather
than correct.
⚠️ **No focus detector is needed, and that is deliberate.**
`s00a-drive-blocked-by-focus.md` records that a per-row brightness statistic
**failed its own control**, and that wrap-around makes counting presses useless.
But every main-menu destination except `EXTRAS` lands on an archive holding a
`.tbm` decider — `GP_SYSTEM` (`pqbase`), `GP_TUTORIAL` (`pubase`),
`GP_SAVE_LOAD` (`px_replay_base`), `GP_DIALOG` (`pcbase`). So pressing Ⓐ on
whatever happens to be focused is very likely to land somewhere useful, and the
screen is identified **afterwards, from the capture**, rather than chosen in
advance.
tbm_screen_capture.py OUTDIR [wait_s]
"""
import os
import subprocess
import sys
import time
import numpy as np
from PIL import Image
OUT = sys.argv[1]
WAIT = float(sys.argv[2]) if len(sys.argv) > 2 else 420
W, H = 1280, 720
NEED, CEIL, HOLD = 500, 2500, 12
PAD = os.path.join(os.path.dirname(os.path.abspath(__file__)), "pad.py")
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 tap(btn="A"):
subprocess.run([sys.executable, PAD, "tap", btn, "0.12"], check=False)
print(f"[{time.time()-T0:7.1f}s] tapped {btn}", flush=True)
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, mark = "wait", 0, None
while True:
el = time.time() - T0
if phase == "wait" and el > WAIT:
print("TITLE NEVER APPEARED", 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:
tap(); mark = time.time(); phase = "menu"
elif phase == "menu" and time.time() - mark > 8:
Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/menu.png")
print(f"[{el:7.1f}s] menu captured (glyph {c}) — pressing A into a submenu", flush=True)
tap(); mark = time.time(); phase = "submenu"
elif phase == "submenu" and time.time() - mark > 10:
Image.fromarray(a.astype(np.uint8)).save(f"{OUT}/submenu.png")
print(f"[{el:7.1f}s] submenu captured (glyph {c}, mean {a.mean():.1f})", flush=True)
break
p.kill(); log.close()