TR2X/tools/generate_funcs
2024-09-30 17:41:21 +02:00

166 lines
4.9 KiB
Python
Executable File

#!/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<ret_type>.+?\W)(?P<func_name>\b\w+)\s*\((?P<args>.+)\);?",
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 <libtrx/game/collision.h>",
"#include <libtrx/game/effects.h>",
"#include <libtrx/game/gameflow/types.h>",
"#include <libtrx/game/items.h>",
"#include <libtrx/game/lara/types.h>",
"#include <libtrx/game/lot.h>",
"#include <libtrx/game/math.h>",
"#include <libtrx/game/objects/common.h>",
"#include <libtrx/game/rooms/types.h>",
"",
"#include <ddraw.h>",
"#include <ddrawi.h>",
"#include <d3dhal.h>",
"#include <dsound.h>",
"#include <stdbool.h>",
"#include <stdint.h>",
"#include <windows.h>",
"",
"#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()