docs: record the gauge attribute-wave (mechanism + 4 increments) + durable RE tools
Document the cockpit-gauge attribute-pointer system in GAUGE_COMPOSITE.md (§Attribute wave) and CLAUDE.md §10: how a Subsystem/Attribute or bare-Attribute binding resolves (ParseAttribute -> FindSubsystem/GetName -> GetAttributePointer -> activeAttributeIndex), what a class must publish (AttributeID enum + AttributePointers[] + GetAttributeIndex chained + DefaultData wiring), the DENSE-TABLE HAZARD (Build leaves gap slots uninitialized, Find strcmps every slot -> tables must be a dense prefix), and the base-primitive vs BT-specific widget split. Records the 4 committed increments (HeatSink table, Mech table, vertBar, segmentArcRatio) and the follow-ups (oneOfSeveralPixInt/map/PlayerStatus/ vehicleSubSystems, the #if0'd HeatSink-bank AmbientTemperature, the Reservoir coolantCapacity shadow). Promote the two reverse-engineering helpers from the session scratchpad into tools/ (durable): disas2.py (capstone + PE parse -- recovers x87 float math Ghidra drops; FUN_004dcd94=round, FUN_004dcd00=fabs) and vtdump.py (dump a class vtable to find an override the assert-anchored decomp never exported, e.g. SegmentArcRatio::Execute). Doc references updated scratchpad/ -> tools/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a2ee1181e6
commit
c2c4ab1f67
@@ -0,0 +1,59 @@
|
||||
import struct, sys
|
||||
from capstone import *
|
||||
|
||||
PATH = r"C:\git\bt411\content\BTL4OPT.EXE"
|
||||
data = open(PATH, "rb").read()
|
||||
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
|
||||
coff = e_lfanew + 4
|
||||
num_sec = struct.unpack_from("<H", data, coff+2)[0]
|
||||
opt_size = struct.unpack_from("<H", data, coff+16)[0]
|
||||
opt = coff + 20
|
||||
image_base = struct.unpack_from("<I", data, opt+28)[0]
|
||||
sec_tbl = opt + opt_size
|
||||
secs = []
|
||||
for i in range(num_sec):
|
||||
off = sec_tbl + i*40
|
||||
vsize, va, rawsize, rawptr = struct.unpack_from("<IIII", data, off+8)
|
||||
secs.append((va, vsize, rawptr, rawsize))
|
||||
|
||||
def va_to_off(va):
|
||||
rva = va - image_base
|
||||
for sva, vsize, rawptr, rawsize in secs:
|
||||
if sva <= rva < sva + max(vsize, rawsize):
|
||||
return rawptr + (rva - sva)
|
||||
return None
|
||||
|
||||
def read_va(va, n):
|
||||
o = va_to_off(va)
|
||||
return data[o:o+n] if o is not None else None
|
||||
|
||||
def fconst(va):
|
||||
b = read_va(va, 4)
|
||||
return struct.unpack("<f", b)[0] if b else None
|
||||
|
||||
FN = int(sys.argv[1], 16)
|
||||
LEN = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x140
|
||||
code = read_va(FN, LEN)
|
||||
md = Cs(CS_ARCH_X86, CS_MODE_32); md.detail = True
|
||||
known = {"0x408328":"SinCos","0x4dcd94":"Round(ftol ST0)","0x40954c":"Quat->Euler",
|
||||
"0x4700bc":"NumericDisplay::ctor","0x4700ac":"nameCopy","0x442aec":"bitMapBin.Get",
|
||||
"0x442c12":"bitMapBin.Release","0x444818":"GraphicGauge::ctor","0x474855":"GaugeConnDirectOf::ctor",
|
||||
"0x4c2ff8":"tiledBlit","0x402298":"operator new","0x4dcdec":"Round64"}
|
||||
print(f"=== disasm @0x{FN:x} len=0x{LEN:x} ===")
|
||||
for ins in md.disasm(code, FN):
|
||||
line = f"0x{ins.address:x}: {ins.mnemonic:<7} {ins.op_str}"
|
||||
for tok in ins.op_str.replace("[", " ").replace("]", " ").split():
|
||||
if tok.startswith("0x"):
|
||||
try:
|
||||
a = int(tok, 16)
|
||||
if 0x4b0000 <= a <= 0x520000:
|
||||
fv = fconst(a)
|
||||
if fv is not None:
|
||||
line += f" ; [0x{a:x}]={fv:g}f"
|
||||
except ValueError:
|
||||
pass
|
||||
if ins.mnemonic == "call" and ins.op_str in known:
|
||||
line += f" ; {known[ins.op_str]}"
|
||||
print(line)
|
||||
if ins.mnemonic == "ret":
|
||||
break
|
||||
@@ -0,0 +1,30 @@
|
||||
import struct, sys
|
||||
PATH = r"C:\git\bt411\content\BTL4OPT.EXE"
|
||||
data = open(PATH, "rb").read()
|
||||
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
|
||||
coff = e_lfanew + 4
|
||||
num_sec = struct.unpack_from("<H", data, coff+2)[0]
|
||||
opt_size = struct.unpack_from("<H", data, coff+16)[0]
|
||||
opt = coff + 20
|
||||
image_base = struct.unpack_from("<I", data, opt+28)[0]
|
||||
sec_tbl = opt + opt_size
|
||||
secs = []
|
||||
for i in range(num_sec):
|
||||
off = sec_tbl + i*40
|
||||
vsize, va, rawsize, rawptr = struct.unpack_from("<IIII", data, off+8)
|
||||
secs.append((va, vsize, rawptr, rawsize))
|
||||
def va_to_off(va):
|
||||
rva = va - image_base
|
||||
for sva, vsize, rawptr, rawsize in secs:
|
||||
if sva <= rva < sva + max(vsize, rawsize):
|
||||
return rawptr + (rva - sva)
|
||||
return None
|
||||
VT = int(sys.argv[1], 16)
|
||||
N = int(sys.argv[2]) if len(sys.argv) > 2 else 32
|
||||
o = va_to_off(VT)
|
||||
print(f"vtable @0x{VT:x}:")
|
||||
for i in range(N):
|
||||
fp = struct.unpack_from("<I", data, o + i*4)[0]
|
||||
tag = ""
|
||||
if 0x4c2f94 <= fp <= 0x4c6771: tag = " <-- BT btl4gaug fn"
|
||||
print(f" [+0x{i*4:02x}] slot{i:2d} = 0x{fp:x}{tag}")
|
||||
Reference in New Issue
Block a user