Created
March 18, 2025 16:10
-
-
Save Yanis002/6ea653805cd599ae4ba0a896807fb869 to your computer and use it in GitHub Desktop.
collection of scripts to translate decomp symbols to gz lib
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # wrapper for sym_info.py that will print a symbol's name for every versions supported by gz | |
| # this is assuming sym_info.py is in the same folder | |
| import sys | |
| import sym_info | |
| def main(): | |
| versions = [ | |
| "ntsc-1.0", | |
| "ntsc-1.1", | |
| "ntsc-1.2", | |
| "gc-jp-ce", | |
| "gc-jp", | |
| "gc-us", | |
| "gc-jp-mq", | |
| "gc-us-mq", | |
| ] | |
| for v in versions: | |
| sym_info.sym_info_main(False, f"{sys.argv[1]}", v, False) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # SPDX-FileCopyrightText: © 2025 ZeldaRET | |
| # SPDX-License-Identifier: CC0-1.0 | |
| # modified version of decomp's sym_info.py to allow usage in another script easily (was lazy ngl) | |
| import argparse | |
| import bisect | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Optional | |
| import struct | |
| import sys | |
| import elftools.elf.elffile | |
| import mapfile_parser | |
| @dataclass | |
| class MdebugSymbolicHeader: | |
| magic: int | |
| vstamp: int | |
| ilineMax: int | |
| cbLine: int | |
| cbLineOffset: int | |
| idnMax: int | |
| cbDnOffset: int | |
| ipdMax: int | |
| cbPdOffset: int | |
| isymMax: int | |
| cbSymOffset: int | |
| ioptMax: int | |
| cbOptOffset: int | |
| iauxMax: int | |
| cbAuxOffset: int | |
| issMax: int | |
| cbSsOffset: int | |
| issExtMax: int | |
| cbSsExtOffset: int | |
| ifdMax: int | |
| cbFdOffset: int | |
| crfd: int | |
| cbRfdOffset: int | |
| iextMax: int | |
| cbExtOffset: int | |
| @dataclass | |
| class MdebugFileDescriptor: | |
| adr: int | |
| rss: int | |
| issBase: int | |
| cbSs: int | |
| isymBase: int | |
| csym: int | |
| ilineBase: int | |
| cline: int | |
| ioptBase: int | |
| copt: int | |
| ipdFirst: int | |
| cpd: int | |
| iauxBase: int | |
| caux: int | |
| rfdBase: int | |
| crfd: int | |
| bitfield: int | |
| cbLineOffset: int | |
| cbLine: int | |
| @dataclass | |
| class MdebugSymbol: | |
| iss: int | |
| value: int | |
| st: int | |
| sc: int | |
| index: int | |
| @dataclass | |
| class LocalSymbol: | |
| name: str | |
| address: int | |
| def read_mdebug_symbolic_header(f, offset: int) -> MdebugSymbolicHeader: | |
| f.seek(offset) | |
| data = f.read(96) | |
| return MdebugSymbolicHeader(*struct.unpack(">2H23I", data)) | |
| def read_mdebug_file_descriptor(f, offset: int) -> MdebugFileDescriptor: | |
| f.seek(offset) | |
| data = f.read(72) | |
| return MdebugFileDescriptor(*struct.unpack(">I2iI6iHh4iI2I", data)) | |
| def read_mdebug_symbol(f, offset: int) -> MdebugSymbol: | |
| f.seek(offset) | |
| data = f.read(12) | |
| word0, word1, word2 = struct.unpack(">III", data) | |
| return MdebugSymbol( | |
| word0, word1, (word2 >> 26) & 0x3F, (word2 >> 21) & 0x1F, word2 & 0xFFFFF | |
| ) | |
| def read_mdebug_string(f, offset: int) -> str: | |
| f.seek(offset) | |
| data = bytearray() | |
| while True: | |
| char = f.read(1)[0] | |
| if char == 0: | |
| break | |
| data.append(char) | |
| return data.decode("ascii") | |
| def read_local_symbols_from_mdebug(elf_path: Path) -> list[LocalSymbol]: | |
| local_symbols = [] | |
| with open(elf_path, "r+b") as f: | |
| elf = elftools.elf.elffile.ELFFile(f) | |
| mdebug_offset = 0 | |
| for section in elf.iter_sections(): | |
| if section.name == ".mdebug": | |
| mdebug_offset = section["sh_offset"] | |
| break | |
| if mdebug_offset == 0: | |
| print(f"No .mdebug section found in '{elf_path}'") | |
| return [] | |
| symbolic_header = read_mdebug_symbolic_header(f, mdebug_offset) | |
| for fd_num in range(symbolic_header.ifdMax): | |
| fd = read_mdebug_file_descriptor( | |
| f, symbolic_header.cbFdOffset + fd_num * 72 | |
| ) | |
| for sym_num in range(fd.isymBase, fd.isymBase + fd.csym): | |
| sym = read_mdebug_symbol(f, symbolic_header.cbSymOffset + sym_num * 12) | |
| if sym.st == 2: # stStatic | |
| if not ( | |
| sym.sc == 2 or sym.sc == 3 or sym.sc == 15 | |
| ): # scData, scBss, scRData | |
| continue | |
| sym_name = read_mdebug_string( | |
| f, symbolic_header.cbSsOffset + fd.issBase + sym.iss | |
| ) | |
| # EGCS mangles names of internal variables, and seemingly ":V" is for in-function static variables | |
| if "." in sym_name: | |
| continue | |
| if ":" in sym_name: | |
| sym_name, rest = sym_name.split(":", 1) | |
| if not rest.startswith("V"): | |
| continue | |
| local_symbols.append(LocalSymbol(sym_name, sym.value)) | |
| elif sym.st == 14: # stStaticProc | |
| sym_name = read_mdebug_string( | |
| f, symbolic_header.cbSsOffset + fd.issBase + sym.iss | |
| ) | |
| local_symbols.append(LocalSymbol(sym_name, sym.value)) | |
| return local_symbols | |
| def merge_local_symbols( | |
| map_file: mapfile_parser.mapfile.MapFile, local_symbols: list[LocalSymbol] | |
| ): | |
| local_symbols.sort(key=lambda s: s.address) | |
| for segment in map_file: | |
| for file in segment: | |
| # TODO: handle segmented addresses? | |
| if file.vram < 0x80000000: | |
| continue | |
| start_address = file.vram | |
| end_address = file.vram + file.size | |
| start_index = bisect.bisect_left( | |
| local_symbols, start_address, key=lambda s: s.address | |
| ) | |
| end_index = bisect.bisect_left( | |
| local_symbols, end_address, key=lambda s: s.address | |
| ) | |
| if start_index == end_index: | |
| continue | |
| symbols = file.copySymbolList() | |
| for sym in local_symbols[start_index:end_index]: | |
| if file.vrom is None: | |
| vrom = None | |
| else: | |
| vrom = sym.address - start_address + file.vrom | |
| symbols.append( | |
| mapfile_parser.mapfile.Symbol( | |
| sym.name, sym.address, None, vrom, None | |
| ) | |
| ) | |
| symbols.sort(key=lambda s: s.vram) | |
| # Recompute symbol sizes | |
| for i in range(len(symbols)): | |
| if i == len(symbols) - 1: | |
| symbols[i].size = end_address - symbols[i].vram | |
| else: | |
| symbols[i].size = symbols[i + 1].vram - symbols[i].vram | |
| file.setSymbolList(symbols) | |
| def find_symbols_by_name( | |
| map_file: mapfile_parser.mapfile.MapFile, sym_name: str | |
| ) -> list[mapfile_parser.mapfile.FoundSymbolInfo]: | |
| infos = [] | |
| for segment in map_file: | |
| for file in segment: | |
| for sym in file: | |
| if sym.name == sym_name: | |
| infos.append(mapfile_parser.mapfile.FoundSymbolInfo(file, sym)) | |
| return infos | |
| def print_map_file(map_file: mapfile_parser.mapfile.MapFile): | |
| for segment in map_file: | |
| print(f"{segment.name}") | |
| for file in segment: | |
| # Ignore debug sections | |
| if ( | |
| file.sectionType in (".pdr", ".line", ".gnu.attributes") | |
| or file.sectionType.startswith(".debug") | |
| or file.sectionType.startswith(".mdebug") | |
| ): | |
| continue | |
| print(f" {file.asStr()}") | |
| for sym in file: | |
| vram_str = f"{sym.vram:08X}" | |
| if sym.vrom is None: | |
| vrom_str = " " | |
| else: | |
| vrom_str = f"{sym.vrom:06X}" | |
| print(f" {vram_str} {vrom_str} {sym.name}") | |
| def sym_info_main(use_args: bool=True, sym_name: str="", oot_version: str="gc-eu-mq-dbg", use_expected: bool=False): | |
| if use_args: | |
| parser = argparse.ArgumentParser( | |
| description="Display various information about symbol or addresses." | |
| ) | |
| parser.add_argument( | |
| "symname", | |
| nargs="?", | |
| help="symbol name or VROM/VRAM address to lookup. If not given, all symbols will be printed.", | |
| ) | |
| parser.add_argument( | |
| "-e", | |
| "--expected", | |
| dest="use_expected", | |
| action="store_true", | |
| help="use the map file and elf in expected/build/ instead of build/", | |
| ) | |
| parser.add_argument( | |
| "-v", | |
| "--version", | |
| dest="oot_version", | |
| help="which version should be processed (default: gc-eu-mq-dbg)", | |
| default="gc-eu-mq-dbg", | |
| ) | |
| args = parser.parse_args() | |
| oot_version = args.oot_version | |
| use_expected = args.use_expected | |
| sym_name = args.symname | |
| BUILTMAP = Path("build") / oot_version / f"oot-{oot_version}.map" | |
| BUILTELF = Path("build") / oot_version / f"oot-{oot_version}.elf" | |
| map_path = BUILTMAP | |
| elf_path = BUILTELF | |
| if use_expected: | |
| map_path = "expected" / BUILTMAP | |
| elf_path = "expected" / BUILTELF | |
| if not map_path.exists(): | |
| print(f"Could not find map_file at '{map_path}'") | |
| sys.exit(1) | |
| map_file = mapfile_parser.mapfile.MapFile() | |
| map_file.readMapFile(map_path) | |
| if elf_path.exists(): | |
| local_symbols = read_local_symbols_from_mdebug(elf_path) | |
| merge_local_symbols(map_file, local_symbols) | |
| else: | |
| print( | |
| f"Could not find ELF file at '{elf_path}', local symbols will not be available" | |
| ) | |
| if sym_name is None or len(sym_name) == 0: | |
| print_map_file(map_file) | |
| sys.exit(0) | |
| infos: list[mapfile_parser.mapfile.FoundSymbolInfo] = [] | |
| possible_files: list[mapfile_parser.mapfile.File] = [] | |
| try: | |
| address = int(sym_name, 0) | |
| if address >= 0x01000000: | |
| info, possible_files = map_file.findSymbolByVram(address) | |
| if info is not None: | |
| infos = [info] | |
| else: | |
| info, possible_files = map_file.findSymbolByVrom(address) | |
| if info is not None: | |
| infos = [info] | |
| except ValueError: | |
| infos = find_symbols_by_name(map_file, sym_name) | |
| if not infos: | |
| print(f"'{sym_name}' not found in map file '{map_path}'") | |
| if len(possible_files) > 0: | |
| print("But it may be a local symbol of either of the following files:") | |
| for f in possible_files: | |
| print(f" {f.asStr()})") | |
| sys.exit(1) | |
| for info in infos: | |
| print(info.getAsStrPlusOffset(sym_name)) | |
| if __name__ == "__main__": | |
| sym_info_main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # appends a lib entry for every supported versions based on the output provided by sym_gz.py | |
| # simply copy paste it, not gonna bother doing more than this (it's already helpful enough for my needs) | |
| # execute this at the root of your gz project | |
| from pathlib import Path | |
| data = ( | |
| """Symbol 'Player_InCsMode' (VRAM: 0x8007943C, VROM: 0xAEF39C, SIZE: 0x3C, build/ntsc-1.0/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x8007943C, VROM: 0xAEF39C, SIZE: 0x3C, build/ntsc-1.1/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x80079ACC, VROM: 0xAEF3EC, SIZE: 0x3C, build/ntsc-1.2/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x80078F8C, VROM: 0xAEE0AC, SIZE: 0x3C, build/gc-jp-ce/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x80078FAC, VROM: 0xAEE0CC, SIZE: 0x3C, build/gc-jp/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x80078F8C, VROM: 0xAEE0AC, SIZE: 0x3C, build/gc-us/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x80078FAC, VROM: 0xAEE0CC, SIZE: 0x3C, build/gc-jp-mq/src/code/z_player_lib.o) | |
| Symbol 'Player_InCsMode' (VRAM: 0x80078F8C, VROM: 0xAEE0AC, SIZE: 0x3C, build/gc-us-mq/src/code/z_player_lib.o) | |
| """ | |
| ) | |
| def main(): | |
| versions_map = { | |
| "ntsc-1.0": "liboot-1.0", | |
| "ntsc-1.1": "liboot-1.1", | |
| "ntsc-1.2": "liboot-1.2", | |
| "gc-jp-ce": "liboot-ce-j", | |
| "gc-jp": "liboot-gc-j", | |
| "gc-us": "liboot-gc-u", | |
| "gc-jp-mq": "liboot-mq-j", | |
| "gc-us-mq": "liboot-mq-u", | |
| } | |
| for line in data.split("\n"): | |
| if len(line.strip()) > 0: | |
| sym_name = line.split("' (")[0].removeprefix("Symbol '") | |
| split = line.removesuffix(")").split("(")[1].split(", ") | |
| vram = split[0].split(": ")[1] | |
| version = split[3].split("/")[1] | |
| with Path(f"./lib/{versions_map[version]}.a").open("a") as file: | |
| file.write(f"{sym_name} = {vram} ;\n") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment