mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
105 lines
4.6 KiB
Python
105 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""gen_engine_decls.py — the canonical-decls header (Phase 16, Drew's "lever 1", cookbook §14c-c).
|
|
|
|
Declare EVERY engine function (from its definition signature) and EVERY data symbol ONCE, canonically,
|
|
in src/shared/engine_decls.h. The overlay .c includes it; harvest drafts then strip ALL their own
|
|
declarations -> a draft can NEVER conflict with engine_core.h's definitions or another draft's guess.
|
|
This dissolves the dominant whole-binary gate-failure (`conflicting types for func_X`) on call-heavy
|
|
functions, which m2c declares with a guessed signature (and without `extern`, so sig_unify misses them).
|
|
|
|
python3 tools/gen_engine_decls.py # regenerate src/shared/engine_decls.h
|
|
"""
|
|
import os, re
|
|
|
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
ECORE = os.path.join(REPO, "src/shared/engine_core.h")
|
|
OVC = os.path.join(REPO, "src/ov_SC01_077/ov_SC01_077.c")
|
|
OUT = os.path.join(REPO, "src/shared/engine_decls.h")
|
|
# a function DEFINITION signature: `<ret> func_XXXX(<params>) {` (in an engine_core.h macro or inline)
|
|
DEF_RE = re.compile(r"\b([A-Za-z_][\w \*]*\bfunc_[0-9A-Fa-f]+\s*\([^){;]*\))\s*\{")
|
|
DATA_RE = re.compile(r"extern\s+([A-Za-z_][\w\s\*]*?\bD_[0-9A-Fa-f]+\s*(?:\[\s*\])?)\s*;")
|
|
|
|
|
|
SCALARS = {"void", "u8", "u16", "u32", "s8", "s16", "s32", "u64", "s64", "f32", "f64", "char",
|
|
"int", "short", "long", "unsigned", "signed", "float", "double",
|
|
"M2C_UNK", "M2C_UNK8", "M2C_UNK16", "M2C_UNK32", "M2C_UNK64"}
|
|
|
|
|
|
def known_structs():
|
|
"""struct names declared in engine_types.h (forward decls + defs)."""
|
|
names = set()
|
|
p = os.path.join(REPO, "src/shared/engine_types.h")
|
|
if os.path.exists(p):
|
|
for m in re.finditer(r"\b(?:struct|union)\s+([A-Za-z_]\w*)", open(p).read()):
|
|
names.add(m.group(1))
|
|
return names
|
|
|
|
|
|
def resolvable(sig, structs):
|
|
"""True iff every type token in the signature is a scalar or a known shared struct/union."""
|
|
s = re.sub(r"\bfunc_[0-9A-Fa-f]+\b", "", sig) # drop the function name
|
|
s = re.sub(r"\b[a-z_]\w*\s*(?=[,)])", "", s) # drop param names (lowercase, before , or ))
|
|
for m in re.finditer(r"\b(struct|union)\s+([A-Za-z_]\w*)", s):
|
|
if m.group(2) not in structs:
|
|
return False
|
|
s2 = re.sub(r"\b(struct|union)\s+\w+", "", s) # remove resolved struct refs
|
|
for tok in re.findall(r"\b([A-Za-z_]\w*)\b", s2):
|
|
if tok not in SCALARS:
|
|
return False
|
|
return True
|
|
|
|
|
|
def def_sigs(path, structs):
|
|
"""addr -> canonical prototype string, from every RESOLVABLE function DEFINITION in `path`."""
|
|
out = {}
|
|
if not os.path.exists(path):
|
|
return out
|
|
for m in DEF_RE.finditer(open(path).read()):
|
|
sig = re.sub(r"\s+", " ", m.group(1)).strip().rstrip("\\").strip()
|
|
if not resolvable(sig, structs):
|
|
continue
|
|
a = int(re.search(r"func_([0-9A-Fa-f]+)", sig).group(1), 16)
|
|
out.setdefault(a, sig) # first definition wins (engine_core.h is authoritative)
|
|
return out
|
|
|
|
|
|
def data_decls(paths):
|
|
out = {}
|
|
for p in paths:
|
|
if not os.path.exists(p):
|
|
continue
|
|
for m in DATA_RE.finditer(open(p).read()):
|
|
decl = re.sub(r"\s+", " ", m.group(1)).strip()
|
|
sym = re.search(r"D_[0-9A-Fa-f]+", decl).group(0)
|
|
out.setdefault(sym, decl)
|
|
return out
|
|
|
|
|
|
def main():
|
|
structs = known_structs()
|
|
funcs = {}
|
|
funcs.update(def_sigs(ECORE, structs)) # engine_core.h macro defs (authoritative)
|
|
for a, s in def_sigs(OVC, structs).items(): # + inline 077-local defs
|
|
funcs.setdefault(a, s)
|
|
data = {k: v for k, v in data_decls([ECORE, OVC]).items() if resolvable(v, structs)}
|
|
|
|
lines = ["#ifndef BFM_ENGINE_DECLS_H", "#define BFM_ENGINE_DECLS_H",
|
|
"/* src/shared/engine_decls.h — GENERATED by tools/gen_engine_decls.py (Phase 16 lever 1).",
|
|
" * Canonical declaration of every engine function + data symbol, so harvest drafts strip",
|
|
" * their own decls and can never `conflicting types`. Regenerate after new matches land. */",
|
|
'#include "common.h"', '#include "engine_types.h"', ""]
|
|
lines.append(f"/* {len(funcs)} function prototypes */")
|
|
for a in sorted(funcs):
|
|
lines.append(funcs[a] + ";")
|
|
lines.append("")
|
|
lines.append(f"/* {len(data)} data symbols */")
|
|
for sym in sorted(data):
|
|
lines.append(f"extern {data[sym]};")
|
|
lines += ["", "#endif"]
|
|
open(OUT, "w").write("\n".join(lines) + "\n")
|
|
print(f"wrote {OUT}: {len(funcs)} func protos + {len(data)} data decls")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|