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>
80 lines
3.1 KiB
Python
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))
|