import struct, collections

RES = r"C:\git\nick-games\decomp\BTL4.RES"
data = open(RES, "rb").read()

labOnly, maxID = struct.unpack_from("<ii", data, 4)
offsets = struct.unpack_from("<%dI" % maxID, data, 12)
Res = collections.namedtuple("Res", "rid rtype name prio flags off length")
resources = {}
for o in offsets:
    if o == 0: continue
    rid, rtype = struct.unpack_from("<ii", data, o)
    name = data[o+8:o+40].split(b"\0")[0].decode("ascii", "replace")
    prio, flags, roff, rlen = struct.unpack_from("<iIII", data, o+40)
    resources[rid] = Res(rid, rtype, name, prio, flags, roff, rlen)

def list_members(r):
    cnt = struct.unpack_from("<i", data, r.off)[0]
    return struct.unpack_from("<%di" % cnt, data, r.off+4)

def solid_member(model_rid):
    r = resources.get(model_rid)
    if not r or r.rtype != 1: return None
    for m in list_members(r):
        mr = resources.get(m)
        if mr and mr.rtype == 9: return mr
    return None

SOLID_TYPES = {0:"Block",1:"Sphere",2:"Cone",3:"ReducibleBlock",4:"Ramp-Z",5:"Ramp-X",6:"Ramp+Z",7:"Ramp+X",
 8:"InvRamp-Z",9:"InvRamp-X",10:"InvRamp+Z",11:"InvRamp+X",12:"Wedge-Z+X",13:"Wedge-Z-X",14:"Wedge+Z-X",
 15:"Wedge+Z+X",16:"XCyl",17:"YCyl",18:"ZCyl",19:"RTile",20:"LTile"}

def decode_solids(r):
    out = []
    n = r.length // 60
    for i in range(n):
        base = r.off + i*60
        reclen, = struct.unpack_from("<I", data, base)
        se = struct.unpack_from("<6f", data, base+4)   # solidExtents minX maxX minY maxY minZ maxZ
        sl = struct.unpack_from("<6f", data, base+28)
        st, mt = struct.unpack_from("<ii", data, base+52)
        out.append((se, sl, st, mt))
    return out

MAPS = ["cavern","grass","rav","polar3","polar4","arena1","arena2","dbase"]
MAKE_MSG_SIZE_MIN = 28  # sanity

for mname in MAPS:
    mres = [r for r in resources.values() if r.rtype == 14 and r.name == mname]
    if not mres:
        print("MAP %s: NOT FOUND" % mname); continue
    r = mres[0]
    off = r.off
    count, = struct.unpack_from("<i", data, off)
    p = off + 4
    inst = collections.Counter()          # per model name
    inst_class = {}
    solid_recs = collections.Counter()    # solids records per model
    includes = []
    total = 0
    bad = 0
    ends = off + r.length
    for i in range(count):
        if p + 28 > ends: bad += 1; break
        mlen, mid, mflags = struct.unpack_from("<IiI", data, p)
        cls, = struct.unpack_from("<i", data, p+28)
        rid, iflags = struct.unpack_from("<iI", data, p+40)
        pos = struct.unpack_from("<3f", data, p+48)
        total += 1
        if mflags & 0x2:  # MapStreamMarkerFlag = 1<<1 (ReliableBit=0 -> NextBit=1)
            rr = resources.get(rid)
            includes.append(rr.name if rr else "?%d" % rid)
        else:
            rr = resources.get(rid)
            nm = rr.name if rr else ("?rid=%d" % rid)
            inst[nm] += 1
            inst_class[nm] = cls
            s = solid_member(rid)
            if s: solid_recs[nm] = s.length // 60
        p += (mlen + 3) & ~3
    print("\n===== MAP %s (id=%d, %d messages) =====" % (mname, r.rid, count))
    if includes: print("  includes:", includes)
    tot_solids = 0
    for nm, c in sorted(inst.items()):
        sr = solid_recs.get(nm, 0)
        tot_solids += sr * c
        print("  %-14s x%-3d class=%-3d solidsRecords=%s" % (nm, c, inst_class[nm], sr if sr else "-"))
    print("  TOTAL instances=%d, instances-with-solids=%d, total solid records=%d" %
          (sum(inst.values()), sum(c for nm,c in inst.items() if solid_recs.get(nm)), tot_solids))

# dump key ground solids
print("\n\n== key solid stream contents ==")
for nm in ["gr100_cv.sld","gr100acv.sld","rfloor_c.sld","afloor~1.sld","hillg1_c.sld","buttea_c.sld","wall1_cv.sld","bld08_cv.sld"]:
    rs = [r for r in resources.values() if r.rtype == 9 and r.name == nm]
    if not rs: print(nm, "NOT FOUND"); continue
    r = rs[0]
    print("\n-- %s (%d records)" % (nm, r.length//60))
    for se, sl, st, mt in decode_solids(r)[:6]:
        print("   type=%-10s mat=%d solidExt X[%g,%g] Y[%g,%g] Z[%g,%g]" %
              (SOLID_TYPES.get(st,st), mt, se[0],se[1],se[2],se[3],se[4],se[5]))
