mirror of
https://github.com/LostArtefacts/TR2X.git
synced 2024-11-23 13:59:45 +00:00
186 lines
5.2 KiB
Python
Executable File
186 lines
5.2 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
|
|
|
|
REPO_DIR = Path(__file__).parent.parent
|
|
DOCS_DIR = REPO_DIR / "docs"
|
|
PROGRESS_TXT_FILE = DOCS_DIR / "progress.txt"
|
|
FUNCS_H_FILE = REPO_DIR / "src/global/funcs.h"
|
|
VARS_H_FILE = REPO_DIR / "src/global/vars.h"
|
|
|
|
|
|
COMMON_HEADER = [
|
|
"// This file is autogenerated. To update it, run tools/generate_funcs.",
|
|
"",
|
|
"#pragma once",
|
|
"",
|
|
]
|
|
|
|
|
|
class ProgressFileSection(StrEnum):
|
|
TYPES = "types"
|
|
FUNCTIONS = "functions"
|
|
VARIABLES = "variables"
|
|
|
|
|
|
@dataclass
|
|
class Symbol:
|
|
offset: int
|
|
signature: str
|
|
flags: str = ""
|
|
|
|
|
|
@dataclass
|
|
class ProgressFile:
|
|
type_definitions: str
|
|
functions: list[Symbol]
|
|
variables: list[Symbol]
|
|
|
|
|
|
def to_int(source: str) -> int | None:
|
|
source = source.strip()
|
|
if source.startswith("/*"):
|
|
source = source[2:]
|
|
if source.endswith("*/"):
|
|
source = source[:-2]
|
|
source = source.strip()
|
|
if not source.replace("-", ""):
|
|
return None
|
|
if source.startswith(("0x", "0X")):
|
|
source = source[2:]
|
|
return int(source, 16)
|
|
|
|
|
|
def parse_progress_file(path: Path) -> ProgressFile:
|
|
result = ProgressFile(type_definitions="", functions=[], variables=[])
|
|
|
|
section: ProgressFileSection | None = None
|
|
for line in path.read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if match := re.match("^# ([A-Z]+)$", line):
|
|
section_name = match.group(1).lower()
|
|
if section_name in list(ProgressFileSection):
|
|
section = ProgressFileSection(section_name)
|
|
|
|
if line.startswith("#") or not line:
|
|
continue
|
|
|
|
if section == ProgressFileSection.TYPES:
|
|
result.type_definitions += line + "\n"
|
|
|
|
if section == ProgressFileSection.FUNCTIONS:
|
|
offset, size, flags, signature = re.split(r"\s+", line, maxsplit=3)
|
|
result.functions.append(
|
|
Symbol(
|
|
signature=signature,
|
|
offset=to_int(offset),
|
|
flags=flags,
|
|
)
|
|
)
|
|
|
|
if section == ProgressFileSection.VARIABLES:
|
|
offset, flags, signature = re.split(r"\s+", line, maxsplit=2)
|
|
result.variables.append(
|
|
Symbol(
|
|
signature=signature,
|
|
offset=to_int(offset),
|
|
flags=flags,
|
|
)
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
def make_func_pointer_define(function: Symbol) -> str:
|
|
if match := re.match(
|
|
r"(?P<ret_type>.+?)\s+(?P<func_name>\w+)\s*\((?P<args>.+)\);?",
|
|
function.signature,
|
|
):
|
|
ret_type = match.group("ret_type")
|
|
func_name = match.group("func_name")
|
|
args = match.group("args")
|
|
return f"#define {func_name} (({ret_type} (*)({args}))0x{function.offset:08X})"
|
|
return ""
|
|
|
|
|
|
def make_var_pointer_define(variable: Symbol) -> str:
|
|
if match := re.match(
|
|
r"^(?P<ret_type>.+?)\s*\((?P<call_type>.*?\s)\s*\*\s*(?P<func_name>\w+)\)\s*\((?P<args>.+)\);?$",
|
|
variable.signature,
|
|
):
|
|
ret_type = match.group("ret_type")
|
|
call_type = match.group("call_type")
|
|
func_name = match.group("func_name")
|
|
args = match.group("args")
|
|
ret = f"#define {func_name} VAR_U_(0x{variable.offset:08X}, {ret_type}({call_type}*)({args}))"
|
|
return ret
|
|
|
|
if match := re.match(
|
|
r"^(?P<ret_type>.+?)\s*(?P<var_name>\w+)\s*(?P<array_def>\[[^\]]*?\])?(\s*=\s*(?P<value_def>.*?))?;?$",
|
|
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")
|
|
if array_def:
|
|
return f"#define {var_name} ARRAY_(0x{variable.offset:08X}, {ret_type}, {array_def})"
|
|
elif value_def:
|
|
return f"#define {var_name} VAR_I_(0x{variable.offset:08X}, {ret_type}, {value_def})"
|
|
else:
|
|
return f"#define {var_name} VAR_U_(0x{variable.offset:08X}, {ret_type})"
|
|
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]))
|
|
|
|
|
|
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 variables:
|
|
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]))
|
|
|
|
|
|
def main() -> None:
|
|
progress_file = parse_progress_file(PROGRESS_TXT_FILE)
|
|
|
|
make_funcs_h(progress_file.functions)
|
|
make_vars_h(progress_file.variables)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|