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>
67 lines
2.8 KiB
Python
67 lines
2.8 KiB
Python
import struct
|
|
d=open("content/VIDEO/GEO/BLX_COP.BGF","rb").read()
|
|
def walk(d,p,end,depth,out):
|
|
while p+3<=end:
|
|
tw=struct.unpack_from("<H",d,p)[0]; p+=2
|
|
cid=tw&0x2fff
|
|
lw=4 if(tw&0x8000)else(2 if(tw&0x4000)else 1)
|
|
if p+lw>end:return
|
|
ln=d[p] if lw==1 else (struct.unpack_from("<H",d,p)[0] if lw==2 else struct.unpack_from("<I",d,p)[0])
|
|
p+=lw
|
|
if p+ln>end:return
|
|
out.append((depth,cid,p,ln,d[p:p+ln]))
|
|
if cid in (0x03,0x40,0x41,0x42,0x46,0x20,0x30,0x31,0x10,0x70,0x48,0x21):
|
|
walk(d,p,p+ln,depth+1,out)
|
|
p+=ln
|
|
if cid==0x0005:break
|
|
out=[]; walk(d,8,len(d),0,out)
|
|
|
|
# For each PMESH: read verts (XYZ_UV, stride20), faces from PCONN(ppf) + CONN(tris)
|
|
# Collect global stats: uv range, bbox, boundary edges per pmesh
|
|
import collections
|
|
pm_idx=0
|
|
tot_uv_min=[1e9,1e9]; tot_uv_max=[-1e9,-1e9]
|
|
bbmin=[1e9]*3; bbmax=[-1e9]*3
|
|
i=0
|
|
while i < len(out):
|
|
depth,cid,p,ln,body=out[i]
|
|
if cid==0x88: # vertex XYZ_UV inside a pmesh
|
|
cnt=ln//20
|
|
verts=[struct.unpack_from("<5f",body,k*20) for k in range(cnt)]
|
|
# gather following face chunks until next vertex/pmesh boundary
|
|
faces=[]
|
|
j=i+1
|
|
while j<len(out):
|
|
d2,c2,p2,l2,b2=out[j]
|
|
if c2 in (0x88,0x89,0x8A,0x80,0x81,0x82): break
|
|
if c2==0x004d and l2>=1: # PCONN
|
|
ppf=b2[0]; n=(l2-1)//4
|
|
idx=[struct.unpack_from("<i",b2,1+k*4)[0] for k in range(n)]
|
|
for f in range(0,n-ppf+1,ppf):
|
|
faces.append(idx[f:f+ppf])
|
|
elif c2==0x0047 and l2>=12: # CONN trilist
|
|
n=l2//4
|
|
idx=[struct.unpack_from("<i",b2,k*4)[0] for k in range(n)]
|
|
for f in range(0,n-2,3):
|
|
faces.append(idx[f:f+3])
|
|
elif c2 in (0x42,0x46,0x40,0x41): break
|
|
j+=1
|
|
pm_idx+=1
|
|
# stats
|
|
for v in verts:
|
|
for a in range(3):
|
|
bbmin[a]=min(bbmin[a],v[a]); bbmax[a]=max(bbmax[a],v[a])
|
|
tot_uv_min[0]=min(tot_uv_min[0],v[3]); tot_uv_max[0]=max(tot_uv_max[0],v[3])
|
|
tot_uv_min[1]=min(tot_uv_min[1],v[4]); tot_uv_max[1]=max(tot_uv_max[1],v[4])
|
|
# boundary edges
|
|
edge=collections.Counter()
|
|
for f in faces:
|
|
for k in range(len(f)):
|
|
a,b=f[k],f[(k+1)%len(f)]
|
|
edge[(min(a,b),max(a,b))]+=1
|
|
boundary=[e for e,c in edge.items() if c==1]
|
|
print(f"PMESH#{pm_idx}: verts={cnt} faces={len(faces)} edges={len(edge)} boundary_edges={len(boundary)}")
|
|
i+=1
|
|
print(f"\nGLOBAL bbox X[{bbmin[0]:.2f},{bbmax[0]:.2f}] Y[{bbmin[1]:.2f},{bbmax[1]:.2f}] Z[{bbmin[2]:.2f},{bbmax[2]:.2f}]")
|
|
print(f"GLOBAL UV U[{tot_uv_min[0]:.4f},{tot_uv_max[0]:.4f}] V[{tot_uv_min[1]:.4f},{tot_uv_max[1]:.4f}]")
|