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>
55 lines
2.5 KiB
Python
55 lines
2.5 KiB
Python
import struct, math
|
|
data = open("content/VIDEO/GEO/TSPHERE.BGF","rb").read()
|
|
print("file bytes:", len(data))
|
|
|
|
TAGS = {0x0003:"HEADER",0x0005:"BIZ_DONE",0x0040:"OBJECT",0x0041:"LOD",0x0042:"PATCH",
|
|
0x0046:"PMESH",0x0047:"CONN",0x0048:"SPHERE_LIST",0x004d:"PCONN",0x0070:"BOUND",
|
|
0x0080:"V_XYZ",0x0081:"V_XYZ_N",0x0082:"V_XYZ_RGBA",0x0088:"V_XYZ_UV",0x0089:"V_XYZ_N_UV",
|
|
0x008A:"V_XYZ_RGBA_UV",0x2030:"SV_MATERIAL",0x2037:"SV_SPECIAL",0x2008:"NAME"}
|
|
CONTAINER={0x0003,0x0070,0x0040,0x0041,0x0042,0x0046,0x0048,0x0010,0x0020,0x0030}
|
|
|
|
verts=[] # (x,y,z,u,v)
|
|
def parse(p,end,depth):
|
|
while p+3<=end:
|
|
tagword=struct.unpack_from("<H",data,p)[0]; p+=2
|
|
cid=tagword&0x2fff
|
|
lw=4 if (tagword&0x8000) else (2 if (tagword&0x4000) else 1)
|
|
if p+lw>end: return
|
|
ln=data[p] if lw==1 else (struct.unpack_from("<H",data,p)[0] if lw==2 else struct.unpack_from("<I",data,p)[0])
|
|
p+=lw
|
|
if p+ln>end: return
|
|
name=TAGS.get(cid,hex(cid))
|
|
if cid in (0x0088,0x0089,0x008A,0x0080,0x0081,0x0082):
|
|
# vertex list
|
|
stride={0x0080:12,0x0081:24,0x0082:28,0x0088:20,0x0089:32,0x008A:36}[cid]
|
|
n=ln//stride
|
|
print(" "*depth+f"{name} verts={n} stride={stride} len={ln}")
|
|
for i in range(n):
|
|
off=p+i*stride
|
|
x,y,z=struct.unpack_from("<fff",data,off)
|
|
u=v=0.0
|
|
if cid==0x0089: u,v=struct.unpack_from("<ff",data,off+24)
|
|
elif cid==0x0088: u,v=struct.unpack_from("<ff",data,off+12)
|
|
elif cid==0x008A: u,v=struct.unpack_from("<ff",data,off+28)
|
|
verts.append((x,y,z,u,v))
|
|
else:
|
|
print(" "*depth+f"{name} id={hex(cid)} len={ln}")
|
|
if cid in CONTAINER:
|
|
parse(p,p+ln,depth+1)
|
|
p+=ln
|
|
if cid==0x0005: break
|
|
parse(8,len(data),0)
|
|
|
|
if verts:
|
|
xs=[v[0] for v in verts]; ys=[v[1] for v in verts]; zs=[v[2] for v in verts]
|
|
us=[v[3] for v in verts]; vs=[v[4] for v in verts]
|
|
print("\n=== GEOMETRY ===")
|
|
print(f"nverts={len(verts)}")
|
|
print(f"x [{min(xs):.2f},{max(xs):.2f}] y [{min(ys):.2f},{max(ys):.2f}] z [{min(zs):.2f},{max(zs):.2f}]")
|
|
print(f"u [{min(us):.3f},{max(us):.3f}] v [{min(vs):.3f},{max(vs):.3f}]")
|
|
# radius from axis (0,8.25,z) per z, to see cone vs sphere vs cylinder
|
|
print("\n=== per-vertex: z, radius_from_yaxis(@8.25), u, v (first 40) ===")
|
|
for (x,y,z,u,v) in verts[:40]:
|
|
dy=y-8.25; r=math.sqrt(x*x+dy*dy)
|
|
print(f" z={z:7.2f} r={r:7.2f} u={u:7.3f} v={v:7.3f} (x={x:6.2f} y={y:6.2f})")
|