Files
BT411/scratchpad/dis_4b2980.py
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

92 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Extract 0x4b2980-0x4b2d8b from section_dump.txt and disassemble (x86-32).
Dump line format: ' 4b2980 558bec83 c4d85356 578b5d08 d94510d8 U.....SVW.]..E..'
address (hex, no 0x) then up to 4 groups of 4 bytes (8 hex chars each)."""
import re, sys
from capstone import Cs, CS_ARCH_X86, CS_MODE_32
DUMP = r"C:\git\bt411\reference\decomp\section_dump.txt"
START = 0x4b2980
END = 0x4b2d8c # exclusive
# named symbols for annotation
SYMS = {
0x4b2d8c: "Gyroscope::ApplyDamageImpulse",
0x4b2de4: "Gyroscope::ApplyDamageTorque",
0x4b2e50: "Gyroscope::ApplyVerticalImpulse",
0x408440: "vec3_copy_A", 0x408e90: "vec3_copy_B",
0x4085ec: "vec3_add", 0x408644: "vec3_sub",
0x4086ac: "vec3_scale", 0x4092fc: "vec3_scale_B",
0x4086d0: "vec3_mul_cw", 0x408744: "vec3_x_mat34_rows",
0x40aadc: "mat34_identity", 0x40ab44: "mat34_from_placement",
0x409a00: "quat_from_rot", 0x4091f4: "eps_cmp_A", 0x4084fc: "eps_cmp_B",
0x409390: "lerp", 0x40b104: "mat34_affine_mult",
}
GYRO = {
0x1D8:"exageration",0x1DC:"eyePosition",0x1E8:"springConstant",0x1F4:"dampingConstant",
0x200:"eyeClampUpper",0x20C:"eyeClampLower",0x218:"posSpring",0x224:"negSpring",
0x230:"eyeForce",0x23C:"eyeVelocity",0x248:"eyeWork",0x254:"spare0",
0x258:"externalPitchPtr",0x25C:"workMatrix",0x28C:"placePos",0x298:"placeQuat",
0x2A8:"placeRot",0x2B4:"bodyOrientation",0x2C0:"rotationSpringConstant",
0x2CC:"rotationDampingConstant",0x2D8:"bodyClampUpper",0x2E4:"bodyClampLower",
0x2F0:"rotationPosSpring",0x2FC:"rotationNegSpring",0x308:"bodyForce",
0x314:"bodyVelocity",0x320:"bodyWork",0x32C:"damageMultiplier",0x340:"damageResponse",
0x3A8:"swayBias",0x3BC:"swayAngle",0x3C8:"eyeJointNode",0x3CC:"mechJointNode",
}
def gyro_annot(off):
best = None
for k,v in GYRO.items():
if k <= off:
if best is None or k > best[0]:
best = (k,v)
if best and off - best[0] < 0x50:
d = off - best[0]
return f"{best[1]}" + (f"+0x{d:x}" if d else "")
return None
mem = {}
pat = re.compile(r'^\s*([0-9a-f]{6,8})\s((?:[0-9a-f]{2,8}\s?)+)')
with open(DUMP) as f:
for line in f:
m = pat.match(line)
if not m: continue
addr = int(m.group(1), 16)
if addr + 16 < START or addr > END: continue
hexpart = m.group(2).split()
b = bytes.fromhex(''.join(hexpart))
for i, byte in enumerate(b):
mem[addr+i] = byte
code = bytes(mem.get(a, 0x90) for a in range(START, END))
missing = [a for a in range(START, END) if a not in mem]
if missing:
print(f"WARNING: {len(missing)} missing bytes, first at {missing[0]:#x}", file=sys.stderr)
md = Cs(CS_ARCH_X86, CS_MODE_32)
md.detail = True
# jump table at 0x4b2b10 (5 dwords) -- disassemble around it
segs = [(START, 0x4b2b10), (0x4b2b24, END)]
insns = []
for s,e in segs:
insns.extend(md.disasm(bytes(mem[a] for a in range(s,e)), s))
if e == 0x4b2b10:
import struct
for i in range(5):
d = struct.unpack('<I', bytes(mem[0x4b2b10+4*i+j] for j in range(4)))[0]
print(f"; jumptable[{i}] = {d:#010x}")
for ins in insns:
annot = ""
# annotate call targets
if ins.mnemonic == 'call' and ins.op_str.startswith('0x'):
tgt = int(ins.op_str, 16)
if tgt in SYMS: annot = f" ; {SYMS[tgt]}"
else: annot = " ; FUN_%08x" % tgt
# annotate gyro member offsets in memory operands
for m2 in re.finditer(r'\+ 0x([0-9a-f]+)\]', ins.op_str):
off = int(m2.group(1), 16)
g = gyro_annot(off)
if g: annot += f" ; [{g}]"
print(f"{ins.address:08x}: {ins.mnemonic:8s} {ins.op_str}{annot}")