hit-location cylinder: raw-bytes audit of the shipped tables + chassis->table map + wedge orientation

Audit of Cyd's hit-model artifact against THE BINARY (user mandate: not vs the
port).  res29_scan.py parses the type-29 streams straight from BTL4.RES bytes:
18 streams / 8 distinct confirmed; the 7-wedge cells (BLH band 6, VUL band 4)
and the all-fixed BLH/OWN twist patterns are authored fact; every artifact
spot-check cell matched the raw bytes verbatim.  Chassis->table resolved by
live-dump matching (MadCat and Avatar ride DIFFERENT same-zone-set tables).
Authored slice names fix the wedge orientation: FRONT = the +Z arc, W1|W2 seam
= dead ahead -- corrects the #92 comment's W6-frontal claim.  Collision type-0
divert re-grounded at 0x4a0361/0x4a036d: rams never touch the cylinder.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-08-02 12:52:20 -05:00
co-authored by Claude Opus 5
parent 07072fc9f0
commit 47d9a61402
2 changed files with 172 additions and 0 deletions
+141
View File
@@ -0,0 +1,141 @@
"""Parse every type-29 DamageLookupTable stream STRAIGHT OUT of BTL4.RES bytes.
Audit tool for Cyd's hit-location artifact: zero port code involved -- the
stream grammar is the one byte-verified from the binary's ctors
(@0x49ea48 table / PieSlice / @0x49e5e4 leaf ReadEntries):
Table { i32 layerCount; Layer[layerCount] }
Layer { i32 rotateWithTorso; i32 sliceCount; Slice[sliceCount] }
Slice { i32 nameLen; char name[nameLen]; u8 NUL; i32 entryCount;
Entry { f32 cumulative; i32 zoneIndex } [entryCount] }
The scanner walks the whole file trying that grammar at every offset whose
u32 is a plausible layer count, with strict validation (printable names,
ascending cumulatives ending ~1.0, sane counts). False positives cannot
survive the full-table parse.
"""
import struct, hashlib, json, sys
RES = r"C:\git\bt411\content\BTL4.RES"
d = open(RES, "rb").read()
N = len(d)
def u32(o): return struct.unpack_from("<i", d, o)[0]
def f32(o): return struct.unpack_from("<f", d, o)[0]
def parse_table(o0):
o = o0
layers = u32(o); o += 4
if not (1 <= layers <= 12): return None
out = []
for _L in range(layers):
if o + 8 > N: return None
rot = u32(o); scount = u32(o + 4); o += 8
if rot not in (0, 1) or not (1 <= scount <= 16): return None
slices = []
for _S in range(scount):
if o + 4 > N: return None
nl = u32(o); o += 4
if not (1 <= nl <= 24) or o + nl + 1 > N: return None
name = d[o:o + nl]
if not all(32 <= b < 127 for b in name): return None
if d[o + nl] != 0: return None
o += nl + 1
if o + 4 > N: return None
ec = u32(o); o += 4
if not (1 <= ec <= 16): return None
ents = []
prev = -1e-6
for _E in range(ec):
if o + 8 > N: return None
cum = f32(o); zi = u32(o + 4); o += 8
if not (0.0 < cum <= 1.0001) or cum < prev - 1e-5: return None
if not (0 <= zi <= 63): return None
ents.append((round(cum, 5), zi))
prev = cum
if abs(ents[-1][0] - 1.0) > 0.02: return None
slices.append((name.decode(), ents))
out.append((rot, slices))
return out, o - o0
tables = []
o = 0
while o < N - 8:
v = u32(o)
if 1 <= v <= 12:
r = parse_table(o)
if r:
tables.append((o, r[0]))
o += r[1]
continue
o += 1
print("tables parsed from raw RES bytes: %d" % len(tables))
def canon(t):
return json.dumps([[rot, [[n, e] for n, e in sl]] for rot, sl in t])
groups = {}
for off, t in tables:
h = hashlib.md5(canon(t).encode()).hexdigest()[:8]
groups.setdefault(h, []).append((off, t))
print("distinct by content: %d" % len(groups))
for h, g in groups.items():
t = g[0][1]
rots = "".join(str(r) for r, _ in t)
sls = ",".join(str(len(sl)) for _, sl in t)
print(" %s copies=%d layers=%d rot=%s slices=%s" %
(h, len(g), len(t), rots, sls))
# ---- spot checks against the artifact's DATA (percent deltas, entry order) ----
def deltas(ents):
out = []; prev = 0.0
for cum, zi in ents:
out.append(round(cum - prev, 4)); prev = cum
return out
def find(pred, what):
hits = [h for h, g in groups.items() if pred(g[0][1])]
print("%-58s %s" % (what, hits if hits else "NOT FOUND"))
return hits
print("\n--- artifact spot checks (band index 0 = first parsed = bottom) ---")
find(lambda t: len(t) == 7 and len(t[6][1]) == 8 and
t[6][1][0][0] == "TopRight" and deltas(t[6][1][0][1]) == [0.6, 0.4],
"LOK band6 TopRight 60/40 (missle-first)")
find(lambda t: len(t) == 7 and len(t[6][1]) == 8 and
t[6][1][0][0] == "TopRight" and deltas(t[6][1][0][1]) == [0.4, 0.6],
"THR band6 TopRight 40/60")
find(lambda t: len(t) == 7 and
all(len(ents) == 1 and abs(ents[0][0] - 1.0) < .001
for _n, ents in t[6][1]),
"OWN band6 single-entry 100% cells")
find(lambda t: len(t) == 7 and len(t[6][1]) == 7,
"BLH: 7-slice band 6")
find(lambda t: len(t) == 7 and len(t[4][1]) == 7,
"VUL/BAT: 7-slice band 4")
find(lambda t: len(t) == 7 and len(t[5][1]) == 8 and
t[5][1][1][0] == "FrontChest" and deltas(t[5][1][1][1]) == [0.1, 0.1, 0.1, 0.1, 0.5, 0.1],
"AVA-A band5 FrontChest ..50% utorso")
find(lambda t: len(t) == 7 and len(t[6][1]) == 8 and
t[6][1][0][0] == "TopRight" and deltas(t[6][1][0][1]) == [0.5, 0.15, 0.35],
"AVA-B/VUL/SND2 band6 TopRight 50/15/35")
find(lambda t: len(t) == 7 and len(t[4][1]) == 8 and
t[4][1][1][0] == "FrontWaist" and deltas(t[4][1][1][1]) == [0.3, 0.3, 0.4],
"SND2 band4 FrontWaist 30/30/40")
find(lambda t: t[0][1][0][0] == "RightFoot" and deltas(t[0][1][0][1]) == [0.5, 0.3, 0.2],
"band0 RightFoot 50/30/20 (common)")
find(lambda t: len(t) == 7 and len(t[3][1]) == 8 and
t[3][1][0][0] == "RightHip" and deltas(t[3][1][0][1]) == [0.4, 0.3, 0.3],
"BLH band3 RightHip 40/30/30")
find(lambda t: len(t) == 7 and len(t[4][1]) == 7 and
t[4][1][0][0] == "RightWaist" and deltas(t[4][1][0][1]) == [0.15, 0.1, 0.25, 0.25, 0.1, 0.15],
"VUL band4 RightWaist 15/10/25/25/10/15")
# twist-pattern census
pats = {}
for h, g in groups.items():
rots = "".join(str(r) for r, _ in g[0][1])
pats.setdefault(rots, []).append(h)
print("\nrot patterns:", {k: len(v) for k, v in pats.items()})