#!/usr/bin/env python3 """Parse a `xenia_re_ui_draws_NN.log` into ONE ROW PER QUAD. 🔴 The reason this file exists. A draw can BATCH several quads — `indices=8` is two, `indices=24` is six — and the log dumps only the first 8 vertices. Taking min/max over a line's whole vertex list therefore merges quads into one box. That is not a theoretical hazard. It silently produced two wrong findings on 2026-08-29: * `ptlogo_back2eff3` (408x203 @ 788,117) is batched with `ptlogo_back2eff4` (749x203 @ 447,117), and eff3 sits ENTIRELY INSIDE eff4's x-range, so the union equals eff4 exactly. The merged box matched eff4 to 1 px and eff3 "was never drawn" — reported, with three other explanations ruled out. * the developer splash's `gamearts_eff` + `seta_eff` merged into a 525x259 box that was read as "the three logos composited into one quad". Vertices come in groups of four, one per quad. Read them that way. """ import re, sys, json def quads(path, lo=None, hi=None): """Yield (frame, index_count, logged_quads, expected_quads, x, y, w, h, alpha).""" frame = None pend = None for line in open(path, errors="replace"): m = re.match(r"--- frame (\d+) ---", line) if m: frame = int(m.group(1)); pend = None; continue if frame is None: continue if lo is not None and not (lo <= frame <= hi): continue mm = re.match(r"^\s*\d+ prim=(\d+) indices=(\d+)", line) if mm: pend = int(mm.group(2)); continue vs = re.findall(r"\[(-?\d+\.\d+),(-?\d+\.\d+),z=[-\d.]+(?:,col=([0-9A-F]{8}))?\]", line) if not vs or pend is None: continue exp = max(1, pend // 4) got = len(vs) // 4 for k in range(got): g = vs[k*4:(k+1)*4] xs = [(float(a) + 1) / 2 * 1280 for a, b, _ in g] ys = [(1 - float(b)) / 2 * 720 for a, b, _ in g] col = next((c for _, _, c in g if c), None) yield (frame, pend, got, exp, round(min(xs)), round(min(ys)), round(max(xs) - min(xs)), round(max(ys) - min(ys)), int(col[:2], 16) if col else -1) pend = None if __name__ == "__main__": path = sys.argv[1] lo, hi = (int(sys.argv[2]), int(sys.argv[3])) if len(sys.argv) > 3 else (None, None) unlogged = 0 for q in quads(path, lo, hi): if q[2] < q[3]: unlogged += q[3] - q[2] print(",".join(str(v) for v in q)) if unlogged: print(f"# WARNING: {unlogged} quads were batched but NOT logged (8-vertex cap)", file=sys.stderr)