#!/usr/bin/env python3 """When does each title sprite get DRAWN, frame by frame, in the real game? Reads a `xenia_re_ui_draws_NN.log` armed early (so the window contains the frames in which the screen is BUILT, not just its steady state) and reports, per texture size, the first and last frame it is bound in. The point is a falsifiable prediction. `docs/re/structures/ui-settle-time.md` decodes `ptlogo_back2eff1`..`eff5` as five staggered two-frame flashes that sweep across the logo once and are extinguished by keyframe t110, while `ptlogo_back2eff` and `ptlogo_back2` hold for the rest of the screen. Four of the five have UNIQUE decoded dimensions, so the log can confirm or refute that directly: eff1 167x126 eff2 258x203 eff3 408x203 eff4 749x203 If they appear in a short contiguous run of early frames and never again, the decode is right. If they are bound every frame, or never, it is wrong. buildin_timeline.py [--dims WxH,...] """ import re, sys, collections # GP_TITLE build 4, from `sylpheed-cli`/`sprite_dims`. Two share 1133x280, which # is why the capture also records per-vertex colour alpha. KNOWN = { (167,126): "ptlogo_back2eff1 FLASH t54-58", (258,203): "ptlogo_back2eff2 FLASH t58-62", (408,203): "ptlogo_back2eff3 FLASH t62-66", (749,203): "ptlogo_back2eff4 FLASH t~64", (1133,280): "ptlogo_back2eff / eff5 (AMBIGUOUS: same size)", (1118,262): "ptlogo_back2 holds t80-243", (919,113): "ptlogo1 holds", (992,104): "ptlogo2 holds", (1280,720): "pteff04 full-screen", (640,360): "ptbase2", (694,20): "ptcopyright", (399,180): "pteff03 / pteff03a (sweeps)", (517,131): "ptlogoall_eff", (235,180): "ptlogoall_eff2", (640,319): "pteff01", (37,17): "ptlogo_tm", } def main(): path = sys.argv[1] frame = None seen = collections.defaultdict(list) # dims -> [frames] per_frame = collections.Counter() frames = [] for line in open(path, errors="replace"): m = re.match(r"--- frame (\d+) ---", line) if m: frame = int(m.group(1)); frames.append(frame); continue if frame is None: continue for w, h in re.findall(r"tex\[base=0x[0-9A-F]+ (\d+)x(\d+) fmt=\d+\]", line): seen[(int(w), int(h))].append(frame) if line.startswith(("0","1","2","3","4","5","6","7","8","9")) or re.match(r"^\s*\d+ prim=", line): per_frame[frame] += 1 if not frames: print("no frames in the log"); return lo, hi = min(frames), max(frames) print(f"frames {lo}..{hi} ({len(set(frames))} distinct) draws {sum(per_frame.values())}\n") rows = [] for dims, fl in seen.items(): s = sorted(set(fl)) rows.append((s[0], dims, s[-1], len(s), len(fl))) rows.sort() print(f" {'first':>7} {'last':>7} {'frames':>7} {'draws':>7} {'size':>10} what") for first, dims, last, nf, nd in rows: name = KNOWN.get(dims, "") span = last - first tag = " ⟵ TRANSIENT" if nf <= 12 and span <= 20 else (" (every frame)" if nf > 0.8*len(set(frames)) else "") print(f" {first:>7} {last:>7} {nf:>7} {nd:>7} {dims[0]:>4}x{dims[1]:<4} {name}{tag}") if __name__ == "__main__": main()