mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-03 08:07:25 -04:00
109ce6a2c3
- CRACKED at xHigh and VERIFIED INDEPENDENTLY: match_one MATCH (1061 ins); agent re-matched 3x from clean runs (100% register-masked AND register-kept, all 10 regions, frame 0x270 exact). §81 carve chain clean first try: jr_isolate_all --only -> byte-identical cacaf7c2 -> jtbl_carve (43-piece set) -> byte-identical -> bank -> R22 clean-fleet 140/140, tools-health OK. instr 80.6%; distinct-code 3,844,100 -> 3,845,161. - WHAT IT IS: the matched base func_8017CA80 + camera height-band cull + distance-driven CLUT fade. func_8004974C (TransposeMatrix) sits in a 36-ins prologue deriving a Y band; the part-level `lim >= g.otz` cull is GONE; flat arms gain an `sz < lim` near-plane cull. The base+one-extra-callee fingerprint predicted this exactly. - §82 ORACLE 1 -- A DUPLICATED `addiu $aN,$sp,K` ACROSS A `jal` MEANS THE BLOCK WAS INLINED. `&X` on any non-first local always creates a pseudo and CSE always merges two of them (expr.c:6260 ADDR_EXPR -> force_operand(..., NULL); exception: virtual-stack-vars offset 0). So the same stack address re-materialised at two sites separated by a jal means CSE was PREVENTED from merging => not the same function body. 17 non-inline spellings failed; a `static inline` helper reproduced the prologue BYTE-FOR-BYTE first try. Reusable probe: scan the ~1,200 built objects for that signature in NON-INCLUDE_ASM functions. - §82 ORACLE 2 -- SCALAR vs AGGREGATE DECIDES *WHEN* A STACK SLOT IS ALLOCATED: lazily at first `&` for a scalar, AT DECLARATION for an aggregate. Six GTE result words had to be six separate longs, not a struct, or they don't land after the inlined helper's temps and the frame isn't 0x270. Second-order: it also flips MEM_IN_STRUCT_P (§30's /s) -- with one word a fixed-address scalar, ((PolyF3*)pkt)->rgbc stops aliasing it, so a store needed respelling to keep the target's nop. A scalar-vs-struct choice is simultaneously a frame-layout AND an aliasing decision. - BANKING FOOTNOTE (§75a class A): first bank rejected `conflicting types for ApplyMatrixSV` -- draft (MATRIX2*, SVECTOR2*, SVECTOR2*) vs the TU/fleet canon (void*, void*, void*), 2,286 of 2,835 sites. Conforming the decl is byte-neutral and banked first try. On a jr function expect BOTH gates to speak: the carve chain answers the jump table, §75a answers the declarations. - Also reproduced: §78 (reuse a busy variable), §80(i) (a lever went -8 -> exactly neutral as the base moved), §72 (a register pin made it worse). - AGENT'S OWN CAVEAT, recorded not hidden: one zero-byte __asm__ keeps a vestigial `mnc = hmid` alive that flow.c would delete (costing 10 ins + the 0x130 spill slot). Emits nothing, compile is 1061 exact, but it is a documented stand-in -- 12 natural spellings measured, all DCE'd.
33 lines
1.5 KiB
Python
33 lines
1.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Side-by-side index-wise listing of a compiled object vs target .s over a range.
|
|
usage: c954_side.py <obj.o> <lo> <hi> [fn]"""
|
|
import sys, os, re, subprocess, shutil
|
|
sys.path.insert(0, '/home/musashi/bfm-decomp/tools')
|
|
import masked_diff
|
|
|
|
OBJ = sys.argv[1]; LO = int(sys.argv[2]); HI = int(sys.argv[3])
|
|
FN = sys.argv[4] if len(sys.argv) > 4 else 'func_8017C730'
|
|
TGT = '/home/musashi/bfm-decomp/asm/ov_SC03_010/nonmatchings/ov_SC03_010_jr_8017AE2C/%s.s' % FN
|
|
|
|
OD = [c for c in ["mips-linux-gnu-objdump", "mipsel-linux-gnu-objdump"] if shutil.which(c)][0]
|
|
out = subprocess.run([OD, "-drz", OBJ], capture_output=True, text=True).stdout
|
|
mt = []
|
|
inside = False
|
|
for line in out.splitlines():
|
|
if re.match(r'^[0-9a-f]+ <%s>:' % FN, line): inside = True; continue
|
|
if inside:
|
|
if re.match(r'^[0-9a-f]+ <', line): break
|
|
m = re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(.*)', line)
|
|
if m: mt.append(re.sub(r'\s+', ' ', m.group(1).strip()))
|
|
tt = []
|
|
for line in open(TGT):
|
|
m = re.match(r'\s*/\* \w+ [0-9A-F]{8} [0-9A-F]{8} \*/\s+(.*)', line)
|
|
if m: tt.append(re.sub(r'\s+', ' ', m.group(1).strip().rstrip()))
|
|
|
|
mine = masked_diff.insns_from_object(OBJ, FN)
|
|
tgt = masked_diff.insns_from_s(TGT)
|
|
diffs = {i for i, a, b in masked_diff.structured_diff(mine, tgt)}
|
|
for k in range(LO, min(HI, max(len(mt), len(tt)))):
|
|
flag = '*' if k in diffs else ' '
|
|
print('%s %4d | %-38s | %s' % (flag, k, mt[k] if k < len(mt) else '', tt[k] if k < len(tt) else ''))
|