#!/usr/bin/env python3 """Verification harness for the mech .data{GameModel} field map. Reads every mapped value through its header-derived offset and compares against the source .data, across all 64 chassis we hold both forms for. Unlike a value-discovered map this one can genuinely fail: the offsets come from declaration order, so a mis-parsed header shows up at once as a whole column of wrong values. Keys that miss consistently by a constant factor are reported with their decoded/source ratio, which is how unit conversions (deg -> rad, kph -> m/s) announce themselves. python3 verify_data.py """ import sys, os, re, collections sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import datamap def close(a, b): return abs(a - b) <= max(1e-4, abs(b) * 1e-5) def matches(got, want_text, typ): """Compare a decoded value against the raw source text.""" if typ in datamap.VECTORS: nums = [float(x) for x in re.findall(r'-?\d+\.?\d*(?:[eE][-+]?\d+)?', want_text)] return len(nums) == len(got) and all(close(a, b) for a, b in zip(got, nums)) if typ == "char": return got.lower() == want_text.strip().lower() if typ in ("bool", "BYTE"): w = want_text.strip().lower() if w in ("true", "yes", "1"): return got == 1 if w in ("false", "no", "0"): return got == 0 return None try: return close(float(got), float(want_text)) except ValueError: return None def main(): pairs, field_map = datamap.build() t = collections.Counter() bad = collections.Counter() examples = [] for ch, kv, blob in pairs: for key, (off, typ, size, angle) in field_map.items(): if key not in kv: continue got = datamap.read(blob, off, typ, size, angle) ok = matches(got, kv[key], typ) if ok is None: t["skipped"] += 1 continue t["values"] += 1 if ok: t["ok"] += 1 else: bad[key] += 1 if len(examples) < 12: examples.append((ch, key, typ, kv[key], got, off)) print(f"chassis : {len(pairs)}") print(f"mapped keys : {len(field_map)}") print(f"values compared : {t['values']} exact: {t['ok']} wrong: {t['values'] - t['ok']}") print(f"not comparable : {t['skipped']} (enum/resource text, decoded separately)") if bad: print("\nkeys not matching:") for k, v in bad.most_common(25): print(f" {k:34s} x{v}") print("\nexamples (chassis, key, type, source, decoded, offset):") for ch, key, typ, src, got, off in examples: print(f" {ch:14s} {key:28s} {typ:10s} src={src!r:24s} got={got!r} @{off}") if __name__ == "__main__": main()