re_match_program: the whole-program LTCG build and check; progress counts its exact matches

This commit is contained in:
KeelrLeek committed 2026-10-05 15:11:21 -05:00
1 parent d2c365d531
commit 5a0fb44c7d
3 files changed
+54 -2

No files matched your search

+35
View File
@@ -531,3 +531,38 @@ def run_toolkit(config, key, argv, json_to_file=False, timeout=900):
return json.loads(text)
except ValueError:
raise HarborError(f"{key} failed: " + (result.stdout + result.stderr).strip()[-800:]) from None
def match_program(config, targets=None, decomp=None):
"""Build every readable function into one /GL + /LTCG image the way the game was built (stand-in callers, stubs
for the rest, globals at their original addresses) and check the targets against the original bytes. Slow: a
build takes 1.5 to 9 minutes depending on load."""
import subprocess
import sys
import tempfile
config = config or {}
script = config.get("ltcg")
if not script or not Path(script).exists():
raise HarborError("this project has no ltcg script in its [reverse] table")
wanted = [t.strip() for t in re.split(r"[,\s]+", str(targets or "")) if t.strip()]
extra = ["--decomp", str(decomp)] if decomp else []
run = lambda argv, timeout: subprocess.run([sys.executable, "-B", str(script), *argv], capture_output=True,
text=True, encoding="utf-8", errors="replace", timeout=timeout,
cwd=str(Path(script).parent),
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0))
built = run(["build", *extra], 1800)
if built.returncode != 0:
raise HarborError("the whole-program build failed: " + (built.stdout + built.stderr).strip()[-800:])
with tempfile.TemporaryDirectory(dir=config.get("debugger_temp")) as tmp:
out = Path(tmp) / "check.json"
checked = run(["check", *extra, "--json", str(out)] + (["--only", ",".join(wanted)] if wanted else []), 900)
if not out.exists():
raise HarborError("the whole-program check failed: " + (checked.stdout + checked.stderr).strip()[-800:])
rows = json.loads(out.read_text(encoding="utf-8"))
counts = {}
for r in rows:
counts[r.get("status")] = counts.get(r.get("status"), 0) + 1
keep = ("address", "name", "status", "first_difference", "similarity", "registers_in", "ours_registers_in",
"ret_pops", "ours_ret_pops", "relocation_problems", "note")
return {"counts": counts, "results": [{k: r[k] for k in keep if k in r} for r in rows] if wanted else
[{k: r[k] for k in keep if k in r} for r in rows if r.get("status") != "differs"][:200]}