import sys, glob, os, struct sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import geomlib as G, mapchunk, lamapaths USAGE = 'usage: debugviz_check.py []' if len(sys.argv) < 2 or sys.argv[1] in ('-h', '--help'): sys.exit(USAGE) SRC = lamapaths.need(sys.argv[2] if len(sys.argv) > 2 else lamapaths.CD_WADS, 'CD wads folder', 'pass it as 2nd argument or set LLAMAMOD_CD') + '/' bad = 0 for f in sorted(glob.glob(sys.argv[1] + '/*.wad')): n = os.path.basename(f); err = [] ch = G.wad_read(f); o = G.wad_read(SRC + n) wf = G.wfpc_flags(ch) trak = G.parse_trak(G.wad_get(ch, 'TRAK')) assert G.build_trak(trak) == G.wad_get(ch, 'TRAK') pages, texs = G.parse_text(G.wad_get(ch, 'TEXT')) otrak = G.parse_trak(G.wad_get(o, 'TRAK')) nt = len(texs) for mi, m in enumerate(trak): if len(m.verts) > 8000: err.append('model %d %d verts' % (mi, len(m.verts))) run = 0 for k, fc in enumerate(m.faces): if fc['tex'] >= nt: err.append('tex idx'); break if max(fc['v']) >= len(m.verts): err.append('vert idx'); break run = 1 if fc['flags'] & 8 else run + 1 if run > 256: err.append('batch>256 model %d' % mi); break if mi < len(otrak): om = otrak[mi] if m.coll != om.coll: err.append('COLLISION CHANGED model %d' % mi) if m.verts[:len(om.verts)] != om.verts or m.faces[:len(om.faces)] != om.faces: err.append('original geometry changed %d' % mi) elif any(len(g) for g in m.coll): err.append('new model with collision') vc = [len(m.verts) for m in trak] mp = mapchunk.parse(G.wad_get(ch, 'MAP'), wf, vc) om = mapchunk.parse(G.wad_get(o, 'MAP'), wf, [len(m.verts) for m in otrak]) if mp['rest'] or mapchunk.build(mp, wf) != G.wad_get(ch, 'MAP'): err.append('MAP roundtrip') for k in ('strats', 'nodes', 'list1', 'lights', 'tab32', 'grid', 'alts', 'strat_x', 'paths', 'x200', 'last'): if mp[k] != om[k]: err.append('MAP %s changed' % k) if mp['tiles'][:len(om['tiles'])] != om['tiles']: err.append('tiles changed') W, H = mp['W'], mp['H'] for t in mp['tiles']: if not (0 <= t[0] - 0.5 < W and 0 <= -t[2] - 0.5 < H) or t[4] >= len(trak): err.append('tile out of bounds'); break # colour block: nv*frames per tile gm = G.parse_map_tiles(G.wad_get(ch, 'MAP'), wf, vc) for i, fr in enumerate(gm['cols']): if any(len(c) != vc[gm['midx'][i]] for c in fr): err.append('colour block %d' % i); break # collision unchanged per tile instance of the original tiles for k in ('TEXT',): pass others = [t for t, _ in ch if t not in ('TRAK', 'MAP ', 'TEXT')] for t in others: if G.wad_get(ch, t) != G.wad_get(o, t): err.append('chunk %s changed' % t) sz = sum(len(G.wad_get(ch, t)) for t in ('TRAK', 'STPC', 'MAP ', 'LGPC')) print('%-13s %s maxV %4d maxF %4d models %4d tiles %5d TRAK+STPC+MAP+LGPC %.1f MB' % ( n, 'OK ' if not err else 'ERROR ' + '; '.join(sorted(set(err))[:4]), max(vc), max(len(m.faces) for m in trak), len(trak), len(mp['tiles']), sz / 2**20)) bad += bool(err) print('errors:', bad)