inital commit

This commit is contained in:
2026-07-12 15:06:31 -05:00
commit 75f1616cb3
77 changed files with 75469 additions and 0 deletions
+288
View File
@@ -0,0 +1,288 @@
# pyright: reportUnknownMemberType=false
import re
import argparse
from typing import cast
from pathlib import Path
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
"""
matches variable declaration:
/* SECTION ADDRESS */ VAR_TYPE NAME[NUMEL];
"""
re_glob = re.compile(
r"^/\* (?P<section>.*?) (?P<address>.*?) \*/ (?P<var_type>[^(]*) (?P<name>.*?)(?:\[(?P<numel>.*?)\])?;"
)
"""
matches structs (or unions) with no typedef:
struct NAME { // SIZE
...
};
"""
re_struct = re.compile(
r"^(?:struct|union) (?P<name>.*?) \{ // (?P<size>0x[0-9a-f]+)\n.*?^\};", flags=re.MULTILINE | re.DOTALL
)
"""
matches structs (or unions) with typedef:
typedef struct { // SIZE
...
} NAME;
"""
re_typedef_struct = re.compile(
r"^typedef (?:struct|union) \{ // (?P<size>0x[0-9a-f]+)\n.*?^\} (?P<name>.*?);", flags=re.MULTILINE | re.DOTALL
)
sizes: dict[str, int] = {
"char": 1,
"u_char": 1,
"short": 2,
"short int": 2,
"u_short": 2,
"int": 4,
"u_int": 4,
"float": 4,
"u_long128": 8,
"sceVu0FMATRIX": 4 * 4 * 4,
"sceVu0FVECTOR": 4 * 4,
"sceSifClientData": 0x2C,
}
command_script_keywords = (
"VERSION",
"SECTIONS",
"ABSOLUTE",
"LOADADDR",
"ALIGN",
"DEFINED",
"NEXT",
"SIZEOF",
"SIZEOF_HEADERS",
"MAX",
"MIN",
"PHDRS",
"CREATE_OBJECT_SYMBOLS",
"BYTE",
"SHORT",
"LONG",
"SQUAD",
"FILL",
"BLOCK",
"NOLOAD",
"AT",
"OVERLAY",
"NOCROSSREFS",
"PT_NULL",
"PT_LOAD",
"PT_DYNAMIC",
"PT_INTERP",
"PT_NOTE",
"PT_SHLIB",
"PT_PHDR",
"ENTRY",
"FLOAT",
"NOFLOAT",
"FORCE_COMMON_ALLOCATION",
"INCLUDE",
"INPUT",
"GROUP",
"OUTPUT",
"OUTPUT_ARCH",
"OUTPUT_FORMAT",
"SEARCH_DIR",
"STARTUP",
"TARGET",
"NOCROSSREFS",
)
class GlobalVarLineMatch:
max_section_len: int = 0
max_address_len: int = 0
max_var_type_len: int = 0
max_name_len: int = 0
max_numel_len: int = 0
_name_cache: list[str] = []
@classmethod
def _get_unique_name(cls, name: str):
i = 1
unique_name = f"{name}__local_{i}"
while unique_name in cls._name_cache:
i += 1
unique_name = f"{name}__local_{i}"
cls._name_cache.append(unique_name)
return unique_name
def __init__(
self,
symtab: SymbolTableSection,
/,
*,
section: str,
address: str | int,
var_type: str,
name: str,
numel: str | int | None = None,
):
self.symtab: SymbolTableSection = symtab
self.section: str = section
self.address: int = address if isinstance(address, int) else int(address, 16)
self.var_type: str = var_type
self.name: str = name
self.numel: int
while self.name.startswith("*"):
self.name = self.name[1:]
self.var_type += "*"
# resolve function pointers (e.g., 'void (*SpecialEventInitTbl[0])...')
if match := re.match(r"\(\*(.*?)\[.*?\]\).*", self.name):
self.name = match.group(1)
self.name = self.name.replace("*", "")
if numel is not None:
if isinstance(numel, int):
self.numel = numel
else:
prod_numel = 1
numels = numel.split("][")
for n in numels:
prod_numel *= int(n)
self.numel = prod_numel
if numel is None:
self.numel = 1
# get symbol
symbol = self.symtab.get_symbol_by_name(self.name)
assert isinstance(symbol, list)
symbol = next(
(sym for sym in symbol if sym.name == self.name and sym.entry["st_value"] == self.address),
None,
)
assert symbol
self.symbol = symbol
if not self.is_global:
self.name = self._get_unique_name(self.name)
escaped_name_len = len(str(self.name))
if self.name in command_script_keywords:
escaped_name_len += 2
GlobalVarLineMatch.max_section_len = max(GlobalVarLineMatch.max_section_len, len(str(self.section)))
GlobalVarLineMatch.max_address_len = max(GlobalVarLineMatch.max_address_len, len(str(self.address)))
GlobalVarLineMatch.max_var_type_len = max(GlobalVarLineMatch.max_var_type_len, len(str(self.var_type)))
GlobalVarLineMatch.max_name_len = max(GlobalVarLineMatch.max_name_len, escaped_name_len)
GlobalVarLineMatch.max_numel_len = max(GlobalVarLineMatch.max_numel_len, len(str(self.numel)))
@property
def is_global(self):
return cast(str, self.symbol["st_info"]["bind"]) == "STB_GLOBAL"
@property
def is_hidden(self):
return cast(str, self.symbol["st_other"]["visibility"]) == "STV_HIDDEN"
@property
def size(self):
size = cast(int, self.symbol["st_size"])
if size == 0:
if "*" in self.var_type:
size = 4
elif self.var_type in sizes:
size = sizes[self.var_type] * self.numel
return size
def to_string(self, as_linker_command_file: bool):
name = self.name if self.name not in command_script_keywords else f'"{self.name}"'
cls_str = f"{name:{GlobalVarLineMatch.max_name_len}s} = 0x{self.address:08x};"
if not as_linker_command_file:
if self.size > 0:
cls_str = f"{cls_str} // size:0x{self.size:x}"
else:
cls_str = f"{cls_str} //0 {self.var_type} * {self.numel}"
# cls_str += f" bind:{'global' if self.is_global else 'local'}"
# cls_str += f" visibility:{'hidden' if self.is_hidden else 'visible'}"
return cls_str
def __str__(self):
return self.to_string(as_linker_command_file=False)
def parse_globals(elf_path: Path, globals_path: Path, types_path: Path, as_linker_command_file: bool):
with open(elf_path, mode="rb") as fh:
elf = ELFFile(fh)
# Find the symbol table.
symtab = elf.get_section_by_name(".symtab")
assert isinstance(symtab, SymbolTableSection)
with open(types_path, mode="r") as f:
types_data = f.read()
for struct_type, size_hex_str in re_struct.findall(types_data):
struct_type = cast(str, struct_type)
struct_size = int(cast(str, size_hex_str), 16)
# assert not (struct_type in sizes and sizes[struct_type] != struct_size)
sizes[struct_type] = struct_size
for size_hex_str, struct_type in re_typedef_struct.findall(types_data):
struct_type = cast(str, struct_type)
struct_size = int(cast(str, size_hex_str), 16)
# assert not (struct_type in sizes and sizes[struct_type] != struct_size), (
# struct_type,
# sizes[struct_type],
# struct_size,
# )
sizes[struct_type] = struct_size
if "tagSE_WRK" in sizes:
sizes["SE_WRK"] = sizes["tagSE_WRK"]
with open(globals_path, mode="r") as f:
lines = [line.strip() for line in f.readlines() if line.startswith("/*") and line.strip().endswith(";")]
matches = [GlobalVarLineMatch(symtab, **match.groupdict()) for line in lines if (match := re_glob.match(line))]
matches.sort(key=lambda match: match.address)
for match in matches:
print(match.to_string(as_linker_command_file=as_linker_command_file))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--language", required=True, choices=["us", "eu"])
parser.add_argument("--as-linker-command-file", action="store_true")
args = parser.parse_args()
if args.language == "us":
elf_path = Path("config/us/SLUS_203.88")
globals_path = Path("ccc/SLUS_203.88/globals.h")
types_path = Path("ccc/SLUS_203.88/types.h")
else:
elf_path = Path("config/eu/SLES_508.21")
globals_path = Path("ccc/SLES_508.21/globals.h")
types_path = Path("ccc/SLES_508.21/types.h")
parse_globals(elf_path, globals_path, types_path, args.as_linker_command_file)
if __name__ == "__main__":
main()