The port refuted the stronger half of the last claim and was right. I wrote that the units/frame drift explained the publisher splash's 4.1% error against its declared dwell. It explains the sign only. Their test verified exactly here: the publisher/developer dwell ratio is 1.2143 declared, 1.2784 as the corpus's three cold boots measure it, and 1.3678 as this container's drift predicts -- so the drift's direction is right and real evidence, but its magnitude is about 2.4x too strong. One refinement, because the means are being compared more finely than n=3 supports: the corpus's three boots individually give excesses of +0.89%, +8.24% and +6.79%, a spread of 7.3 percentage points -- WIDER than the 5.30 pp gap under test -- and boot 1's ratio (1.2251) is essentially the declared 1.2143. This run sits 2.3 sigma above their mean: suggestive, not established. Not closable without a frame log from the corpus's instrument, which was screenshot timing and has none. An attempt to give this side an n of 3 failed on tooling and is recorded: ARM=early loses its F10 about 40% of the time -- two of five runs logged "ARMED EARLY" and produced no draw log at all, with nothing in the session log distinguishing them. Also fences the 33% drift against a misreading the port flagged: it is PRESENTATION pacing and cannot reach keyframe_units_per_second = 60, which is the game's logical rate, decoded under Q1, and which a renderer converts through at its own frame rate. And records a cross-check neither side went looking for: the batch counts are 1 and 2 on the publisher against 3 and 6 on the developer, and the port reports a count restricted to SPRITE-BEARING elements reproduces that exactly from the export -- so palogo_eff0, the layerless forced backdrop, is not in the batched draw, confirmed from the file. Two instruments that disagreed about that element in every previous iteration now agree on which one it is. New tool splash_boundaries.py carries the corrected counting method. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
71 lines
2.8 KiB
Python
Executable File
71 lines
2.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Boot-splash boundaries from a UI draw log, counted the way that survives the cap.
|
|
|
|
🔴 Do NOT read element visibility off which quads the log prints. A draw batches
|
|
several quads (`indices=24` is six) and the log dumps only the first 8 vertices —
|
|
two quads — so which elements appear is the first two IN THE BATCH, and that set
|
|
moves as elements fade. On the developer splash the three glows hold that prefix
|
|
until t=45, which makes the three wordmarks look as though they start there.
|
|
|
|
`indices / 4` is how many quads the draw actually holds, and the cap cannot touch
|
|
it. Its transitions land exactly where the declared count of elements with
|
|
alpha > 0 changes:
|
|
|
|
publisher 1 -> 2 -> 1 (wordmark joins the glow at t=15; glow ends at t=45)
|
|
developer 3 -> 6 -> 3 (three wordmarks join three glows; glows end)
|
|
|
|
so each run yields calibration points at t = 15, 45 and the splash's end.
|
|
"""
|
|
import sys, collections
|
|
sys.path.insert(0, '/work/tools/re-capture')
|
|
from quads_per_frame import quads
|
|
|
|
def batch_runs(path, lo=0, hi=400):
|
|
per = {}
|
|
for (f, ind, got, exp, x, y, w, h, a) in quads(path, lo, hi):
|
|
if exp >= 1:
|
|
per[f] = max(per.get(f, 0), exp if exp >= 2 else 1)
|
|
runs = []
|
|
cur = None
|
|
for f in sorted(per):
|
|
n = per[f]
|
|
if cur and cur[2] == n and f <= cur[1] + 2:
|
|
cur[1] = f
|
|
else:
|
|
if cur: runs.append(tuple(cur))
|
|
cur = [f, f, n]
|
|
if cur: runs.append(tuple(cur))
|
|
return [r for r in runs if r[1] - r[0] >= 1]
|
|
|
|
def main():
|
|
path = sys.argv[1]
|
|
runs = batch_runs(path)
|
|
print(f"# {path}")
|
|
for a, b, n in runs:
|
|
print(f" frames {a:>4}..{b:<4} ({b-a+1:>3}) {n} quads")
|
|
# publisher = the 2-quad run; developer = the 6-quad run
|
|
pub = [r for r in runs if r[2] == 2]
|
|
dev6 = [r for r in runs if r[2] == 6]
|
|
dev3 = [r for r in runs if r[2] == 3]
|
|
if pub and dev6 and dev3:
|
|
p = pub[0]
|
|
d6 = dev6[0]
|
|
d3after = [r for r in dev3 if r[0] > d6[1]]
|
|
d3before = [r for r in dev3 if r[1] < d6[0]]
|
|
if d3after and d3before:
|
|
pub_start, pub_end = d3before[0][0], None
|
|
# publisher span: its own 2-quad run brackets t=15..45; the screen ends
|
|
# at the last frame before the developer's first 3-quad run
|
|
pub_last = d3before[0][0] - 1
|
|
pub_first = 1
|
|
dev_first = d3before[0][0]
|
|
dev_last = d3after[-1][1]
|
|
print(f"\n publisher frames {pub_first}..{pub_last} = {pub_last-pub_first+1}")
|
|
print(f" developer frames {dev_first}..{dev_last} = {dev_last-dev_first+1}")
|
|
r = (pub_last-pub_first+1)/(dev_last-dev_first+1)
|
|
print(f" ratio = {r:.4f} (declared 255/210 = {255/210:.4f}, "
|
|
f"excess {(r-255/210)/(255/210)*100:+.2f}%)")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|