#!/usr/bin/env python3
"""Census: per PATCH, list the exact chunk sequence (vertex chunks 0x80-0x8A,
face chunks 0x47/0x4d, specials 0x2037, material 0x2030) to learn how many
dpl_GEOMETRY objects the 1995 loader creates for a PUNCH-tagged geogroup and
in what list order.  The i860 damageize handler (VREND.MNG @f040f6f8) writes
the three vr_damage tokens into the FIRST THREE geometries' geometry_type --
so the punch triple only functions if the patch has >=3 geometry chunks."""
import struct, sys

VSTRIDE = {0x80:12, 0x81:24, 0x82:28, 0x88:20, 0x89:32, 0x8A:36}
CONTAINERS = {0x40,0x41,0x42,0x46,0x48,0x70,0x10,0x20,0x30,0x3}

def census(path):
    d = open(path,"rb").read()
    out = []
    def read_chunk(p, end):
        if p+2 > end: return None
        tw = struct.unpack_from("<H", d, p)[0]
        cid = tw & 0x2fff
        lw = 4 if (tw & 0x8000) else (2 if (tw & 0x4000) else 1)
        if p+2+lw > end: return None
        if lw==1: ln = d[p+2]
        elif lw==2: ln = struct.unpack_from("<H", d, p+2)[0]
        else: ln = struct.unpack_from("<I", d, p+2)[0]
        body = p+2+lw
        return cid, body, ln, body+ln
    def walk(p, end, patch, depth):
        while p < end:
            r = read_chunk(p, end)
            if not r: break
            cid, body, ln, nxt = r
            if cid == 0x42:
                patch = {"seq":[], "mtl":"", "special":""}
                out.append(patch)
                walk(body, nxt, patch, depth+1)
            elif cid == 0x2030 and patch is not None:
                patch["mtl"] = d[body:body+ln].split(b'\0')[0].decode('latin1','ignore')
            elif cid == 0x2037 and patch is not None:
                patch["special"] = d[body:body+ln].split(b'\0')[0].decode('latin1','ignore')
                patch["seq"].append(("SPECIAL", patch["special"][:40]))
            elif cid in VSTRIDE and patch is not None:
                patch["seq"].append(("VERT%02x" % cid, ln // VSTRIDE[cid]))
            elif cid == 0x47 and patch is not None:
                patch["seq"].append(("CONN", ln//4))
            elif cid == 0x4d and patch is not None:
                ppf = d[body] if ln else 0
                patch["seq"].append(("PCONN(ppf=%d)" % ppf, (ln-1)//4))
            elif cid in CONTAINERS:
                walk(body, nxt, patch, depth+1)
            else:
                if patch is not None and cid not in (0x2046, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2039):
                    patch["seq"].append(("tag%04x" % cid, ln))
            p = nxt
    walk(8, len(d), None, 0)
    return out

if __name__ == "__main__":
    for path in sys.argv[1:]:
        pts = census(path)
        print("== %s: %d patches" % (path, len(pts)))
        for i,pt in enumerate(pts):
            tag = " PUNCH" if "PUNCH" in pt["special"] else ("" if not pt["special"] else " SP:"+pt["special"][:30])
            print("  patch%-3d mtl=%-34s%s" % (i, pt["mtl"][:34], tag))
            print("      seq: %s" % (pt["seq"],))
