mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-08 09:43:04 -04:00
f5498c3c66
⚠️ R22 CLEAN-FLEET OWED (two agents still reading asm/, so `make clean` is unsafe). Each of the 12
was gated whole-binary AND independently re-checked against its own config/check.<bin>.sha (12/12),
stubs confirmed replaced — but incremental (§130). Treat as UNCONFIRMED until the clean run.
- THREE isolated cheap-Opus agents, briefed with §150/§151 + the mandatory all-drafts scan,
CONVERGED INDEPENDENTLY: func_8017C6F4's 947-ins body exists in 12 OVERLAYS under 5 DIFFERENT
NAMES at 6 DIFFERENT ADDRESSES, each differing by exactly TWO per-overlay symbols (screen-rect
helper + 64x64 cell table). Gated 12/12, 0 failed. 11,364 ins from this morning's single crack.
- WHY IT HID ~30 PHASES (cookbook §152): name-keyed grouping scattered it across 5 names,
address-keyed across 6 addresses (and the address collides with an unrelated 15-ins body in 3
other overlays), and h_seq-keyed scattered it too — which is why the Phase-26 sweeps missed it.
THE KEY IS BYTE SIZE: `grep -rl 'nonmatching .*, 0xECC' asm/*/nonmatchings/*/` returns exactly
the 12, reads the asm (cannot go stale like family_hseq.json), no false positives. Refines the
Phase-26 "h_seq is spent" finding: h_seq is worth exactly ONE size-keyed sweep behind each FRESH
core crack — here it paid 11:1.
- TWO CAUTIONS THAT TRAVEL WITH IT: (1) a MASKED tool cannot validate a remap — match_one and
rtu_match both mask jal/%hi/%lo, exactly the fields a remap edits, so a wrong symbol map still
reports MATCH; gate remaps by the whole-binary SHA only. (2) a stale residual is NOT evidence two
functions differ — I briefed "func_8017C59C scores 340, different body"; refuted in one command
(that 340 came from a pre-§150-fix draft, which scores nonzero against its own target too).
- OPEN TOOL DEFECT (R32): family_remap's unit backscan halts at the first #define, so it carried
16/16 gte macros and 0/10 typedefs, silently — the §146 gap from the other side.
- MY ERROR, RETRACTED IN THE LOG (S43-9): I reported the 263x5 cluster as "5 byte-identical, 1,315
ins". FALSE — the drafts had been reverted, so I measured the INCLUDE_ASM STUB BASELINE, which is
byte-identical by construction. R34's trap, self-inflicted by hand-building instead of using
harvest_verify. Nothing was banked there; the cluster is UNRESOLVED. ("41 behemoth drafts" was
likewise a file count — 79 files, 20 distinct functions.)
78 lines
3.3 KiB
Python
78 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""P30 S43 task #10 — measure the 41 permuter-unblocked behemoth drafts against the RIGHT body.
|
|
|
|
Until today the permuter was silently dead on every draft carrying `#define ... __asm__` + `#include`
|
|
(p16_permute's cpp fallback). 41 of those 63 drafts are behemoth renderer drafts and have never had a
|
|
working permuter run. func_8017EF68 came out of this pool at 2-of-969.
|
|
|
|
TRAP THIS GUARDS: the same address holds DIFFERENT bodies in different overlays (S43: 0x8017C6F4 is
|
|
15 ins in three overlays and 948 in four others; 0x8017EF68 is 12 vs 970). A draft measured against
|
|
the wrong variant reads as garbage. So: measure every draft against EVERY variant and keep the best.
|
|
"""
|
|
import glob, os, re, subprocess, json, sys
|
|
|
|
DIRS = ('.run/drafts-behemoth3', '.run/drafts-p30beh', '.run/drafts-behemoth2')
|
|
DEF = re.compile(r'^\s*#\s*define\b[^\n]*__asm__', re.M)
|
|
INC = re.compile(r'^\s*#\s*include', re.M)
|
|
FN = re.compile(r'\bfunc_[0-9A-Fa-f]{8}\b')
|
|
|
|
|
|
def variants(fn):
|
|
"""[(binary, asm_subdir, nins)] for every overlay that has this function."""
|
|
out = []
|
|
for p in subprocess.run(['find', 'asm', '-name', f'{fn}.s'],
|
|
capture_output=True, text=True).stdout.split():
|
|
parts = p.split('/')
|
|
if len(parts) < 5:
|
|
continue
|
|
n = sum(1 for l in open(p, errors='replace') if l.lstrip().startswith('/*'))
|
|
out.append((parts[1], '/'.join(parts[:4]), n))
|
|
return sorted(out, key=lambda t: -t[2])
|
|
|
|
|
|
def measure(fn, draft, subdir):
|
|
p = subprocess.run(['.venv/bin/python', 'tools/match_one.py', fn, '--c', draft,
|
|
'--asm-subdir', subdir], capture_output=True, text=True, timeout=400)
|
|
first = (p.stdout or p.stderr or '?').strip().splitlines()
|
|
first = first[0] if first else '?'
|
|
if 'MATCH' in first and 'DIFF' not in first:
|
|
return 0, first
|
|
m = re.search(r'(\d+) mismatched', first)
|
|
return (int(m.group(1)) if m else None), first
|
|
|
|
|
|
rows = []
|
|
drafts = []
|
|
for d in DIRS:
|
|
for p in sorted(glob.glob(d + '/*.c')):
|
|
t = open(p, errors='replace').read()
|
|
if DEF.search(t) and INC.search(t):
|
|
drafts.append(p)
|
|
print(f'{len(drafts)} permuter-unblocked behemoth drafts', flush=True)
|
|
|
|
for p in drafts:
|
|
names = FN.findall(os.path.basename(p)) or FN.findall(open(p, errors='replace').read()[:4000])
|
|
if not names:
|
|
print(f' {p}: no function name'); continue
|
|
fn = names[0]
|
|
best = None
|
|
for (b, sub, n) in variants(fn):
|
|
try:
|
|
score, line = measure(fn, p, sub)
|
|
except subprocess.TimeoutExpired:
|
|
continue
|
|
if score is not None and (best is None or score < best[0]):
|
|
best = (score, b, sub, n)
|
|
if best:
|
|
rows.append({'draft': p, 'fn': fn, 'closeness': best[0],
|
|
'binary': best[1], 'asm_subdir': best[2], 'nins': best[3]})
|
|
print(f' {fn:18s} best={best[0]:5d} of {best[3]:4d} ins in {best[1]} [{os.path.basename(os.path.dirname(p))}]', flush=True)
|
|
else:
|
|
print(f' {fn:18s} no measurable variant', flush=True)
|
|
|
|
rows.sort(key=lambda r: r['closeness'])
|
|
json.dump(rows, open('.run/s43/behemoth_sweep.json', 'w'), indent=1)
|
|
print('\n=== RANKED (permuter-tractable first) ===')
|
|
for r in rows[:20]:
|
|
print(f" {r['fn']} close={r['closeness']} nins={r['nins']} {r['binary']}")
|