Files
BT412/scratchpad/punch_census.py
T
arcattackandClaude Fable 5 8ed6184d65 Combat: AUTHENTIC weapon groups -- streamed per-mech button bindings + the real fire chain (task #5)
The recovered system: fire channels = LBE4ControlsManager buttonGroups
(0x40/0x45/0x46/0x47); default groups = the per-mech type-6 controls-map
resource in BTL4.RES, installed by the T0 CreateStreamedMappings the port
already called -- it needed only the TriggerState attribute (id 0x13 PINNED
to the binary value; fireImpulse@0x31C is the binary's TriggerState) and an
input feed.  Keyboard/harness now push press/release edges into the button
groups; the gBT*Trigger bypasses, per-type keyboard split and 1,0 pulse
hack are retired -- weapons sharing a button fire TOGETHER (madcat Trigger
= 4 weapons).  Myomers @4b9550/@4b95b8 misattribution corrected (they are
MechWeapon ConfigureMappables/ChooseButton).  Verified 2-node: kill through
the authentic chain (12 hits vs ~36 pre-groups).  Config-mode session
(regrouping UI) = the remaining stage, KB-scoped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 13:06:29 -05:00

65 lines
2.9 KiB
Python

#!/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"],))