#!/usr/bin/env python3 from pathlib import Path import re from tr2x.ida_progress import FUNC_PTR_RE, VAR_RE, Symbol, parse_progress_file from tr2x.paths import TR2X_PROGRESS_FILE, TR2X_SRC_DIR FUNCS_H_FILE = TR2X_SRC_DIR / "global/funcs.h" VARS_H_FILE = TR2X_SRC_DIR / "global/vars_decomp.h" TYPES_H_FILE = TR2X_SRC_DIR / "global/types.h" COMMON_HEADER = [ "// This file is autogenerated. To update it, run tools/generate_funcs.", "", "#pragma once", "", ] def update_file(path: Path, new_content: str) -> None: if path.read_text() != new_content: path.write_text(new_content) def make_func_pointer_define(function: Symbol) -> str: if match := re.match( r"(?P.+?\W)(?P\b\w+)\s*\((?P.+)\);?", function.signature, ): ret_type = match.group("ret_type").strip() func_name = match.group("func_name").strip() args = match.group("args").strip() return f"#define {func_name} (({ret_type} (*)({args})){function.offset_str})" return "" def make_var_pointer_define(variable: Symbol) -> str: if match := FUNC_PTR_RE.match(variable.signature): ret_type = match.group("ret_type") call_type = match.group("call_type") array_def = match.group("array_def") func_name = match.group("func_name") args = match.group("args") if array_def: return f"#define {func_name} (*(({ret_type}({call_type} *(*){array_def})({args})){variable.offset_str}))" else: return f"#define {func_name} (*({ret_type}({call_type}**)({args})){variable.offset_str})" if match := VAR_RE.match(variable.signature): ret_type = match.group("ret_type") var_name = match.group("var_name") array_def = match.group("array_def") value_def = match.group("value_def") comment = match.group("comment") if not array_def and not value_def and comment == "no-dereferencing": return f"#define {var_name} (({ret_type}){variable.offset_str})" if array_def: return f"#define {var_name} (*({ret_type}(*){array_def}){variable.offset_str})" elif value_def: return f"#define {var_name} (*({ret_type}*){variable.offset_str}) // = {value_def}" else: return f"#define {var_name} (*({ret_type}*){variable.offset_str})" print("warn: unrecognized signature", variable.signature) return "" def make_funcs_h(functions: list[Symbol]) -> None: header = [ *COMMON_HEADER, '#include "global/types.h"', "", "// clang-format off", ] footer = ["// clang-format on"] defines = [] for function in functions: if "+" not in function.flags and ( define := make_func_pointer_define(function) ): defines.append(define) update_file(FUNCS_H_FILE, "\n".join([*header, *defines, *footer]) + "\n") def make_vars_h(variables: list[Symbol]) -> None: header = [ *COMMON_HEADER, '#include "global/types.h"', '#include "inject_util.h"', "", "// clang-format off", ] footer = [ "", "// clang-format on", ] defines = [] for variable in sorted(variables, key=lambda symbol: symbol.offset): if "+" not in variable.flags and ( define := make_var_pointer_define(variable) ): defines.append(define) update_file(VARS_H_FILE, "\n".join([*header, *defines, *footer]) + "\n") def make_types_h(types: list[str]) -> None: header = [ *COMMON_HEADER, '#include "const.h"', "", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", "", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", "", "#pragma pack(push, 1)", "", "// clang-format off", ] footer = [ "", "// clang-format on", ] update_file( TYPES_H_FILE, "\n".join( [ *header, "\n\n".join([definition.strip() for definition in types if '// decompiled' not in definition.strip()]), *footer, ] ) + "\n", ) def main() -> None: progress_file = parse_progress_file(TR2X_PROGRESS_FILE) make_funcs_h(progress_file.functions) make_vars_h(progress_file.variables) make_types_h(progress_file.types) if __name__ == "__main__": main()