Files
TeslaRel410/emulator/firmware-decomp/capture_triage.py
T
CydandClaude Opus 4.8 cdc2c724db Capture triage: parse-only census of every wire capture
capture_triage.py parses each capture's first slice (no emulation) and reports
the embedded firmware build, action census, init config, and any actions outside
cap7's known-good set. Results: all captures share build 0x31440 + the same init
config; ravtest (RAV = the walled terrain map), dtest, cap6/cap8/capture/
sharksval are clean cap7-profile candidates; sdemo/duane/fishspls add morph;
bt3/munga7 add act31/43/45/list_remove. cap6's earlier "boot derail" was likely
just an under-budgeted run (87K cmds to parse), not a fault.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:29:27 -05:00

80 lines
3.1 KiB
Python

"""Triage captures for battle-scene replay: for each capture, WITHOUT emulation,
parse the first slice of the wire log and report (a) embedded firmware build header,
(b) action census, (c) the init config string, (d) actions outside cap7's known-good
set. Cheap parse-only pass to pick the best replay candidates."""
import sys, os, struct
sys.path.insert(0, r'C:\VWE\TeslaRel410\emulator\firmware-decomp')
sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha')
from vrboard import Assembler, A
P = r'C:\VWE\TeslaRel410\dpl3-revive\patha'
BOOT = {int(A.hspcode), int(A.code860), int(A.data860), int(A.bss860), int(A.args860)}
def parse_slice(path, slice_bytes=6_000_000):
with open(path, 'rb') as f:
data = f.read(slice_bytes)
start = None
for i in range(0, len(data) - 8):
w = struct.unpack_from('<I', data, i)[0]
if (w & 0xffff0000) == 0x40ff0000 and 8 <= (w & 0xffff) <= 4096:
act = struct.unpack_from('<I', data, i + 4)[0]
if act in (A.init, A.code860, A.args860, A.data860, A.bss860):
start = i; break
if start is None:
return None, None
asm = Assembler(); asm.feed(data[start:])
out = []
for m in asm:
if not m.iserver:
out.append((int(m.action), m.payload))
return out, start
def fw_hdr(cmds):
for a, pl in cmds:
if a == int(A.code860) and len(pl) == 40:
return struct.unpack_from('<7I', pl, 0)
return None
def name(a):
try: return A(a).name
except Exception: return 'act%d' % a
# build cap7's reference action set first
ref_cmds, _ = parse_slice(os.path.join(P, 'cap7.raw.bin'))
ref_set = set(a for a, _ in ref_cmds if a not in BOOT)
print("cap7 reference actions:", sorted(name(a) for a in ref_set))
print("=" * 100)
CAPS = ['ravtest', 'solo1', 'batest', 'cap6', 'cap8', 'capture', 'sdemo4', 'sdemo5',
'munga7', 'fxtest', 'glblade', 'trek', 'klngvid', 'dtest', 'duane', 'bt3',
'fishspls', 'sharksval', 'blade']
import collections
for cap in CAPS:
path = os.path.join(P, cap + '.raw.bin')
if not os.path.exists(path) or os.path.getsize(path) == 0:
continue
try:
cmds, start = parse_slice(path)
if not cmds:
print("%-12s UNPARSEABLE" % cap); continue
h = fw_hdr(cmds)
fw = ("fw:text=%#x" % h[0]) if h else "fw:none-in-slice"
census = collections.Counter(a for a, _ in cmds if a not in BOOT)
extra = set(census) - ref_set
draws = census.get(int(A.draw_scene), 0)
# init config string
cfg = ""
for a, pl in cmds:
if a == int(A.init) and pl:
cfg = pl.split(b'\0')[0][:90].decode('latin1', 'replace')
break
print("%-12s %8dB %s cmds:%d draws:%d" %
(cap, os.path.getsize(path), fw, len(cmds), draws))
print(" actions: %s" % {name(a): n for a, n in census.most_common(10)})
if extra:
print(" EXTRA (not in cap7): %s" % sorted(name(a) for a in extra))
if cfg:
print(" init: %s" % cfg)
except Exception as e:
print("%-12s ERR %s" % (cap, e))