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>
66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
import struct
|
|
b=open("content/VIDEO/GEO/BLX_COP.BGF",'rb').read()
|
|
def rdU16(p):return struct.unpack_from('<H',b,p)[0]
|
|
def rdU32(p):return struct.unpack_from('<I',b,p)[0]
|
|
CONT={0x0003,0x0070,0x0040,0x0041,0x0042,0x0046,0x0048,0x0010,0x0020,0x0030}
|
|
def parse(p,end,out):
|
|
while p+3<=end:
|
|
tw=rdU16(p);p+=2;idv=tw&0x2fff
|
|
lw=4 if(tw&0x8000)else(2 if(tw&0x4000)else 1)
|
|
if p+lw>end:break
|
|
ln=b[p] if lw==1 else(rdU16(p) if lw==2 else rdU32(p));p+=lw
|
|
if p+ln>end:break
|
|
out.append((idv,p,ln))
|
|
if idv in CONT:parse(p,p+ln,out)
|
|
p+=ln
|
|
if idv==0x0005:break
|
|
out=[];parse(8,len(b),out)
|
|
# Walk patches->pmeshes, build faces per pmesh with local base like the loader
|
|
# We approximate by processing each PMESH: first vtx chunk => localCount; PCONN/CONN => faces
|
|
# Reconstruct nesting: iterate, track current pmesh vert base globally
|
|
tris=[]
|
|
i=0
|
|
# simpler: re-parse with structure by scanning sequentially; each VTX starts a new local set
|
|
verts=[] # global positions
|
|
def add_pmesh(vtx_off,vtx_len,faces_pconn,faces_conn):
|
|
base=len(verts)
|
|
n=vtx_len//20
|
|
for k in range(n):
|
|
x,y,z=struct.unpack_from('<3f',b,vtx_off+k*20)
|
|
verts.append((x,y,z))
|
|
for idx,ppf in faces_pconn:
|
|
for f in range(0,len(idx)-ppf+1,ppf):
|
|
a=idx[f]
|
|
for kk in range(1,ppf-1):
|
|
bb=idx[f+kk];cc=idx[f+kk+1]
|
|
if 0<=a<n and 0<=bb<n and 0<=cc<n:
|
|
tris.append((base+a,base+bb,base+cc))
|
|
for idx in faces_conn:
|
|
for f in range(0,len(idx)-2,3):
|
|
a,bb,cc=idx[f],idx[f+1],idx[f+2]
|
|
if 0<=a<n and 0<=bb<n and 0<=cc<n:
|
|
tris.append((base+a,base+bb,base+cc))
|
|
# group chunks into pmeshes
|
|
cur=None
|
|
for idv,p,ln in out:
|
|
if idv==0x0046: # PMESH start
|
|
if cur:add_pmesh(*cur)
|
|
cur=[None,0,[],[]]
|
|
elif idv in(0x0088,0x0089,0x008A,0x0080,0x0081,0x0082) and cur is not None and cur[0] is None:
|
|
cur[0]=p;cur[1]=ln
|
|
elif idv==0x004d and cur is not None: # PCONN
|
|
ppf=b[p];n=(ln-1)//4;idx=[rdU32(p+1+j*4) for j in range(n)]
|
|
# indices may be signed; keep as is
|
|
cur[2].append((idx,ppf))
|
|
elif idv==0x0047 and cur is not None: # CONN
|
|
n=ln//4;idx=[rdU32(p+j*4) for j in range(n)];cur[3].append(idx)
|
|
if cur:add_pmesh(*cur)
|
|
print("total verts=%d tris=%d"%(len(verts),len(tris)))
|
|
from collections import Counter
|
|
ec=Counter()
|
|
for a,bb,cc in tris:
|
|
for e in((a,bb),(bb,cc),(cc,a)):
|
|
ec[tuple(sorted(e))]+=1
|
|
bnd=sum(1 for v in ec.values() if v==1)
|
|
print("unique edges=%d boundary(used once)=%d interior=%d"%(len(ec),bnd,sum(1 for v in ec.values() if v>1)))
|