TR2X/tools/generate_funcs
2024-04-30 11:01:52 +02:00

172 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python3
import re
from collections.abc import Callable, Iterable
from dataclasses import dataclass
from enum import StrEnum
from pathlib import Path
from tr2x.ida_progress import 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.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 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 := re.match(
r"^"
r"(?P<ret_type>.+?)\s*"
r"\("
r"\s*\*(?P<call_type>.*?\s)\s*(?P<func_name>\w+)"
r"(?P<array_def>\[[^\]]*?\]+)?"
r"\)\s*"
r"\((?P<args>.+)\)"
r";?$",
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 := re.match(
r"^"
r"(?P<ret_type>.+?)\s*"
r"(?P<var_name>\b\w+)\s*"
r"(?P<array_def>(?:\[[^\]]*?\])*)"
r"(\s*=\s*(?P<value_def>.*?))?"
r";?"
r"(\s*\/\/\s*(?P<comment>.*))?"
r"$",
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)
FUNCS_H_FILE.write_text("\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",
"extern const char *g_TR2XVersion;",
]
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)
VARS_H_FILE.write_text("\n".join([*header, *defines, *footer]) + "\n")
def make_types_h(types: list[str]) -> None:
header = [
*COMMON_HEADER,
"#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",
]
TYPES_H_FILE.write_text(
"\n".join(
[
*header,
"\n\n".join([definition.strip() for definition in types]),
*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()