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
This commit is contained in:
@@ -1727,9 +1727,13 @@ discovered it after re-exporting. ❔ The sweeps may simply be a small term, and
|
||||
layer — 🟡 which of those is unsettled, and `ChannelMask` is `0x0002` on both,
|
||||
so the file will not say. Today's 347 s concatenation plays the piece twice,
|
||||
the second time as a bass-less stem.
|
||||
❔ **And it is not a seamless loop**: `BGM_001` fades out at 167.663 s and is
|
||||
followed by 6.15 s of silence, with no loop-point field identified. A menu loop
|
||||
is authored.
|
||||
🔴 **"not a seamless loop … no loop-point field identified … a menu loop is
|
||||
authored" — REFUTED 2026-08-30, and this bullet kept saying it for days after
|
||||
the correction existed elsewhere.** The fade and the 6.15 s of silence are real,
|
||||
but there **is** a loop point — in the **XMA decoder context**, set at runtime by
|
||||
`XMASetLoopData`. For `BGM_103` it is **`[9.44 s, 71.31 s]`, cycling every
|
||||
61.87 s**, watched over three wraps. The game never reaches the fade, which is
|
||||
why the stored tail looked unusable. See the 2026-08-30 entries at the top.
|
||||
✅ **The menu's music is `BGM_103`.** The cue *table* cannot say — its BGM
|
||||
entries are numeric — but `GamePart_Title`'s `sub_821C5580` plays **cue 1103**,
|
||||
and `BGM_103.slb`'s two declared waves (3 876 864 / 3 930 112 B) are
|
||||
|
||||
@@ -1431,3 +1431,31 @@ too — a span of zero cannot occur. Both cases point the same way:
|
||||
**The tell is in the rows that agree with you.** Scrutinising the disconfirming half
|
||||
harder is the intuitive discipline and it is the wrong half — a broken instrument
|
||||
and an easy control both fail *silently on the side you were hoping for*.
|
||||
|
||||
## A correction that does not reach the artifact a consumer reads has not been made
|
||||
|
||||
The port agent found that after correcting `loop_end_why` and `loop_start_why`, the
|
||||
field its exporter actually concatenates into `manifest.json` — `loop_why` — was
|
||||
**still shipping** "AUDIBLY WRONG AT THE SEAM" and "no loop-point field has been
|
||||
identified anywhere". Both refuted days earlier. Its corrections existed, were
|
||||
accurate, and were in the wrong fields.
|
||||
|
||||
**And the same audit against this corpus found the same failure here.** The claim
|
||||
*"no loop-point field has been identified in the XMA header, so a menu loop is
|
||||
authored"* was refuted on 2026-08-30 — in a **new page**. The sentence stayed
|
||||
untouched in [`bgm-two-stems.md`](structures/bgm-two-stems.md), which is where
|
||||
anyone looking up BGM behaviour arrives, **and** in `HANDOFF.md`, which is the one
|
||||
page the port is told to read. A reader following either would have got the dead
|
||||
answer with nothing to warn them.
|
||||
|
||||
⚠️ **Writing the correction down is not the same as landing it.** A new page records
|
||||
that you learned something; it does not stop the old sentence being read. When
|
||||
something is refuted, **grep the corpus for the claim, not for the file you were
|
||||
working in**, and fix it where it is stated — especially in `HANDOFF.md`, whose
|
||||
whole purpose is to be the page somebody reads instead of the rest.
|
||||
|
||||
📌 And a trap in doing that audit, also the port's: its first verification reported
|
||||
the stale text **still present**, because the replacement *quotes* the refuted
|
||||
sentences in order to name them — so a substring search matches them inside the
|
||||
paragraph saying they are false. The match was real and meant the opposite of what
|
||||
the search implied. This is "the tell is in the rows that agree" arriving as a grep.
|
||||
|
||||
@@ -117,11 +117,22 @@ transcribing one.
|
||||
* a music bank is **two waves that play together**, not one track and not a
|
||||
sequence — do not concatenate;
|
||||
* both are full length; align them at sample 0;
|
||||
* **the track is not a seamless loop.** `BGM_001` fades out and is followed by
|
||||
**6.15 s of silence** (last sound 167.663 s of 173.809 s; the final second
|
||||
before silence is at RMS 168 against 4 788 at the head). Looping the wave as
|
||||
stored gives a fade-out and six seconds of nothing every cycle. No loop-point
|
||||
field has been identified in the XMA header, so a menu loop is **authored**.
|
||||
* **the track is not a seamless loop *as stored*.** `BGM_001` fades out and is
|
||||
followed by **6.15 s of silence** (last sound 167.663 s of 173.809 s; the final
|
||||
second before silence is at RMS 168 against 4 788 at the head). Looping the wave
|
||||
end-to-end gives a fade-out and six seconds of nothing every cycle.
|
||||
|
||||
🔴 **But "no loop-point field has been identified … so a menu loop is
|
||||
authored" is REFUTED (2026-08-30), and this bullet said it for days after.**
|
||||
There is no loop point in the **file header**; there is one in the **XMA decoder
|
||||
context**, written at runtime via `XMASetLoopData`, and Xenia already logs it.
|
||||
For `BGM_103` it is **`[9.44 s, 71.31 s]`, cycling every 61.87 s** — measured by
|
||||
watching three wraps, so the game never reaches the fade at all. That is *why*
|
||||
the stored tail looks unusable: it is never played.
|
||||
[`menu-bgm-loop-fields-conflict.md`](menu-bgm-loop-fields-conflict.md)
|
||||
|
||||
⚠️ Reach: measured on `BGM_103` only. Whether every bank carries loop bounds is
|
||||
untested — but "the format has nowhere to put one" is dead.
|
||||
|
||||
## ✅ Confirmed at runtime — two stereo streams decode at once
|
||||
|
||||
|
||||
84
tools/re-capture/tbm_screen_capture.py
Executable file
84
tools/re-capture/tbm_screen_capture.py
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user