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>
94 lines
4.2 KiB
Python
94 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Dump the three sub-geometries of each PUNCH patch separately:
|
|
chunk1 (dmg_set mask), chunk2 (undmg2enbl DRAWN), chunk3 (dmg_clear).
|
|
Check: are chunk2 and chunk3 identical? bbox/plane relationships chunk1 vs chunk2."""
|
|
import struct, sys
|
|
import numpy as np
|
|
|
|
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 parse(path):
|
|
d = open(path,"rb").read()
|
|
patches = []
|
|
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):
|
|
while p < end:
|
|
r = read_chunk(p, end)
|
|
if not r: break
|
|
cid, body, ln, nxt = r
|
|
if cid == 0x42:
|
|
patch = {"mtl":"", "punch":False, "geoms":[]}
|
|
patches.append(patch)
|
|
walk(body, nxt, patch)
|
|
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:
|
|
if b"PUNCH" in d[body:body+ln]: patch["punch"] = True
|
|
elif cid in VSTRIDE and patch is not None:
|
|
st = VSTRIDE[cid]
|
|
n = ln // st
|
|
verts = []
|
|
for i in range(n):
|
|
o = body + i*st
|
|
verts.append(struct.unpack_from("<fff", d, o))
|
|
patch["geoms"].append({"verts":verts, "faces":[], "vt":cid})
|
|
elif cid == 0x47 and patch is not None and patch["geoms"]:
|
|
g = patch["geoms"][-1]
|
|
n = ln//4
|
|
idx = [struct.unpack_from("<I", d, body+i*4)[0] for i in range(n)]
|
|
for f in range(0,(n//3)*3,3):
|
|
g["faces"].append(tuple(idx[f:f+3]))
|
|
elif cid == 0x4d and patch is not None and patch["geoms"]:
|
|
g = patch["geoms"][-1]
|
|
ppf = d[body]; n=(ln-1)//4
|
|
idx=[struct.unpack_from("<I",d,body+1+i*4)[0] for i in range(n)]
|
|
for f in range(0,(n//ppf)*ppf,ppf):
|
|
poly=[idx[f+j] for j in range(ppf)]
|
|
for k in range(1,ppf-1):
|
|
g["faces"].append((poly[0],poly[k],poly[k+1]))
|
|
elif cid in CONTAINERS:
|
|
walk(body, nxt, patch)
|
|
p = nxt
|
|
walk(8, len(d), None)
|
|
return patches
|
|
|
|
def bbox(verts):
|
|
V = np.array(verts)
|
|
return V.min(0), V.max(0)
|
|
|
|
for path in sys.argv[1:]:
|
|
print("==", path)
|
|
for pi, pt in enumerate(parse(path)):
|
|
if not pt["punch"] or not pt["geoms"]: continue
|
|
print(" patch%d mtl=%s %d sub-geometries" % (pi, pt["mtl"][:40], len(pt["geoms"])))
|
|
for gi, g in enumerate(pt["geoms"]):
|
|
lo, hi = bbox(g["verts"])
|
|
print(" g%d: %d verts %d tris bbox [%7.3f %7.3f %7.3f] .. [%7.3f %7.3f %7.3f]"
|
|
% (gi, len(g["verts"]), len(g["faces"]), *lo, *hi))
|
|
if len(pt["geoms"]) == 3:
|
|
v2 = np.array(pt["geoms"][1]["verts"]); v3 = np.array(pt["geoms"][2]["verts"])
|
|
same = v2.shape == v3.shape and np.allclose(v2, v3, atol=1e-6)
|
|
f2 = pt["geoms"][1]["faces"]; f3 = pt["geoms"][2]["faces"]
|
|
print(" g1 vs g2(twin?): verts identical=%s faces identical=%s" % (same, f2==f3))
|
|
if not same and v2.shape == v3.shape:
|
|
dd = np.abs(v2-v3).max()
|
|
print(" max coord delta = %g" % dd)
|
|
# g0 vs g1 proximity: distance of each g1 vert to g0 bbox
|
|
v1 = np.array(pt["geoms"][0]["verts"])
|
|
lo0, hi0 = v1.min(0), v1.max(0)
|
|
lo1, hi1 = v2.min(0), v2.max(0)
|
|
print(" mask(g0) bbox vs drawn(g1) bbox: g0 contains g1: %s ; g1 contains g0: %s"
|
|
% (bool(np.all(lo0 <= lo1+1e-4) and np.all(hi0 >= hi1-1e-4)),
|
|
bool(np.all(lo1 <= lo0+1e-4) and np.all(hi1 >= hi0-1e-4))))
|