diff --git a/tools/splat/.gitignore b/tools/splat/.gitignore index 921e677191..90f0ad8ea3 100644 --- a/tools/splat/.gitignore +++ b/tools/splat/.gitignore @@ -2,4 +2,4 @@ venv/ .vscode/ __pycache__/ -util/Yay0decompress +util/n64/Yay0decompress diff --git a/tools/splat/.gitrepo b/tools/splat/.gitrepo index 6b26c87759..543f264205 100644 --- a/tools/splat/.gitrepo +++ b/tools/splat/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/ethteck/splat.git branch = master - commit = 7574db712ef19ca420904c82d3559e9ac4b8c5f5 - parent = 86760369a5ab977c037c21aebf6f10484570642f + commit = 687885c4d8bcb86d399dbb7dcd660ad8548acdf7 + parent = dc7285954620634e352b43bcae320ba1f8dd8de7 method = merge cmdver = 0.4.3 diff --git a/tools/splat/CHANGELOG.md b/tools/splat/CHANGELOG.md new file mode 100644 index 0000000000..8e4f9f6d1a --- /dev/null +++ b/tools/splat/CHANGELOG.md @@ -0,0 +1,12 @@ +# splat Release Notes + +## 0.5 +* n64splat name changed to splat + * Some refactoring was done to support other platforms besides n64 in the future + * New `platform` option, which defaults to `n64` + * This will cause breaking changes in custom segments, so please refer to one of the changes in one of the n64 base segments for details +* Support for custom artifact paths + * New `undefined_syms_auto_path` option + * New `undefined_funcs_auto_path` option + * New `cache_path` option + * (All path-like options' names now end with `_path`) diff --git a/tools/splat/Makefile b/tools/splat/Makefile index cca29fc676..603f567fad 100644 --- a/tools/splat/Makefile +++ b/tools/splat/Makefile @@ -5,7 +5,7 @@ default: all all: Yay0decompress Yay0decompress: - gcc $(UTIL_DIR)/Yay0decompress.c -fPIC -shared -O3 -o $(UTIL_DIR)/Yay0decompress + gcc $(UTIL_DIR)/n64/Yay0decompress.c -fPIC -shared -O3 -o $(UTIL_DIR)/n64/Yay0decompress clean: - rm -f $(UTIL_DIR)/Yay0decompress + rm -f $(UTIL_DIR)/n64/Yay0decompress diff --git a/tools/splat/README.md b/tools/splat/README.md index c76c3d775a..434f797bcf 100644 --- a/tools/splat/README.md +++ b/tools/splat/README.md @@ -1,8 +1,10 @@ -# n64splat -A n64 rom splitting tool to assist with decompilation and modding projects +# splat +A binary splitting tool to assist with decompilation and modding projects -For example usage, see https://github.com/ethteck/papermario -The Makefile `setup` target calls n64splat with a config file that you can use for reference. More documentation coming soon. +Currently, only N64 roms in the .z64 format are supported. + +For example usage, see https://github.com/pmret/papermario +The Makefile `setup` target calls splat with a config file that you can use for reference. More documentation coming soon. ### Requirements Python package requirements can be installed via `pip3 install -r requirements.txt` diff --git a/tools/splat/create_config.py b/tools/splat/create_config.py index cb7bfbc25d..b0e10d9cfd 100755 --- a/tools/splat/create_config.py +++ b/tools/splat/create_config.py @@ -1,13 +1,11 @@ #! /usr/bin/env python3 -from capstone import * -from capstone.mips import * - import argparse -from util import rominfo +from util.n64 import rominfo +from util.n64 import find_code_length from segtypes.code import N64SegCode -parser = argparse.ArgumentParser(description="Create a splat config from a rom") +parser = argparse.ArgumentParser(description="Create a splat config from a rom (currently only n64 .z64 roms supported)") parser.add_argument("rom", help="path to a .z64 rom") @@ -25,12 +23,8 @@ options: with open(rom_path, "rb") as f: fbytes = f.read() - - rom_addr = 0x1000 - - md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_BIG_ENDIAN) - for insn in md.disasm(fbytes[rom_addr:], rom.entry_point): - rom_addr += 4 + + first_section_end = find_code_length.run(fbytes, 0x1000, rom.entry_point) segments = \ """segments: @@ -52,7 +46,7 @@ options: - type: bin start: 0x{:X} - [0x{:X}] -""".format(rom.entry_point, rom_addr, rom.size) +""".format(rom.entry_point, first_section_end, rom.size) outstr = header + segments diff --git a/tools/splat/segtypes/Yay0.py b/tools/splat/segtypes/n64/Yay0.py similarity index 88% rename from tools/splat/segtypes/Yay0.py rename to tools/splat/segtypes/n64/Yay0.py index 7bc376fec4..3edda0a703 100644 --- a/tools/splat/segtypes/Yay0.py +++ b/tools/splat/segtypes/n64/Yay0.py @@ -1,7 +1,6 @@ import os -from segtypes.segment import N64Segment -from pathlib import Path -from util import Yay0decompress +from segtypes.n64.segment import N64Segment +from util.n64 import Yay0decompress class N64SegYay0(N64Segment): def split(self, rom_bytes, base_path): diff --git a/tools/splat/segtypes/n64/__init__.py b/tools/splat/segtypes/n64/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/splat/segtypes/bin.py b/tools/splat/segtypes/n64/bin.py similarity index 93% rename from tools/splat/segtypes/bin.py rename to tools/splat/segtypes/n64/bin.py index 92ca4eb41c..c42979b4ea 100644 --- a/tools/splat/segtypes/bin.py +++ b/tools/splat/segtypes/n64/bin.py @@ -1,5 +1,5 @@ import os -from segtypes.segment import N64Segment +from segtypes.n64.segment import N64Segment from pathlib import Path diff --git a/tools/splat/segtypes/ci4.py b/tools/splat/segtypes/n64/ci4.py similarity index 90% rename from tools/splat/segtypes/ci4.py rename to tools/splat/segtypes/n64/ci4.py index 92bb5ef69d..1632bf23b3 100644 --- a/tools/splat/segtypes/ci4.py +++ b/tools/splat/segtypes/n64/ci4.py @@ -1,4 +1,4 @@ -from segtypes.ci8 import N64SegCi8 +from segtypes.n64.ci8 import N64SegCi8 class N64SegCi4(N64SegCi8): def parse_image(self, data): diff --git a/tools/splat/segtypes/ci8.py b/tools/splat/segtypes/n64/ci8.py similarity index 94% rename from tools/splat/segtypes/ci8.py rename to tools/splat/segtypes/n64/ci8.py index 5bf2115d99..350b3611b3 100644 --- a/tools/splat/segtypes/ci8.py +++ b/tools/splat/segtypes/n64/ci8.py @@ -1,8 +1,7 @@ -from segtypes.segment import N64Segment -from segtypes.rgba16 import N64SegRgba16 +from segtypes.n64.rgba16 import N64SegRgba16 import png import os -from util import Yay0decompress +from util.n64 import Yay0decompress class N64SegCi8(N64SegRgba16): diff --git a/tools/splat/segtypes/code.py b/tools/splat/segtypes/n64/code.py similarity index 99% rename from tools/splat/segtypes/code.py rename to tools/splat/segtypes/n64/code.py index be4262a443..4b56928a43 100644 --- a/tools/splat/segtypes/code.py +++ b/tools/splat/segtypes/n64/code.py @@ -3,7 +3,7 @@ from capstone import * from capstone.mips import * from collections import OrderedDict -from segtypes.segment import N64Segment, parse_segment_name +from segtypes.n64.segment import N64Segment import os from pathlib import Path, PurePath from ranges import Range, RangeDict @@ -508,7 +508,7 @@ class N64SegCode(N64Segment): if sym_type in ["float", "double"]: if sym_type == "float": float_str = floats.format_f32_imm(bits) - elif sym_type == "double": + else: float_str = floats.format_f64_imm(bits) # Fall back to .word if we see weird float values diff --git a/tools/splat/segtypes/header.py b/tools/splat/segtypes/n64/header.py similarity index 97% rename from tools/splat/segtypes/header.py rename to tools/splat/segtypes/n64/header.py index 9a09a06962..4a4e3919a9 100644 --- a/tools/splat/segtypes/header.py +++ b/tools/splat/segtypes/n64/header.py @@ -1,7 +1,6 @@ import os -from segtypes.segment import N64Segment +from segtypes.n64.segment import N64Segment from pathlib import Path -from util import rominfo class N64SegHeader(N64Segment): def should_run(self): diff --git a/tools/splat/segtypes/i4.py b/tools/splat/segtypes/n64/i4.py similarity index 93% rename from tools/splat/segtypes/i4.py rename to tools/splat/segtypes/n64/i4.py index 0524d83e0a..092d50e867 100644 --- a/tools/splat/segtypes/i4.py +++ b/tools/splat/segtypes/n64/i4.py @@ -1,4 +1,4 @@ -from segtypes.rgba16 import N64SegRgba16 +from segtypes.n64.rgba16 import N64SegRgba16 import png from math import ceil diff --git a/tools/splat/segtypes/i8.py b/tools/splat/segtypes/n64/i8.py similarity index 84% rename from tools/splat/segtypes/i8.py rename to tools/splat/segtypes/n64/i8.py index a82741be58..a3d4abef3d 100644 --- a/tools/splat/segtypes/i8.py +++ b/tools/splat/segtypes/n64/i8.py @@ -1,4 +1,4 @@ -from segtypes.i4 import N64SegI4 +from segtypes.n64.i4 import N64SegI4 from math import ceil class N64SegI8(N64SegI4): diff --git a/tools/splat/segtypes/ia16.py b/tools/splat/segtypes/n64/ia16.py similarity index 83% rename from tools/splat/segtypes/ia16.py rename to tools/splat/segtypes/n64/ia16.py index 7d2b10ad8f..2c1025b9e3 100644 --- a/tools/splat/segtypes/ia16.py +++ b/tools/splat/segtypes/n64/ia16.py @@ -1,4 +1,4 @@ -from segtypes.ia4 import N64SegIa4 +from segtypes.n64.ia4 import N64SegIa4 class N64SegIa8(N64SegIa4): def parse_image(self, data): diff --git a/tools/splat/segtypes/ia4.py b/tools/splat/segtypes/n64/ia4.py similarity index 93% rename from tools/splat/segtypes/ia4.py rename to tools/splat/segtypes/n64/ia4.py index c2eec32bfd..909d9817be 100644 --- a/tools/splat/segtypes/ia4.py +++ b/tools/splat/segtypes/n64/ia4.py @@ -1,5 +1,4 @@ -import os -from segtypes.rgba16 import N64SegRgba16 +from segtypes.n64.rgba16 import N64SegRgba16 import png from math import ceil diff --git a/tools/splat/segtypes/ia8.py b/tools/splat/segtypes/n64/ia8.py similarity index 91% rename from tools/splat/segtypes/ia8.py rename to tools/splat/segtypes/n64/ia8.py index b4e3cbec04..e348a2463d 100644 --- a/tools/splat/segtypes/ia8.py +++ b/tools/splat/segtypes/n64/ia8.py @@ -1,5 +1,4 @@ -from segtypes.ia4 import N64SegIa4 -import png +from segtypes.n64.ia4 import N64SegIa4 from math import ceil class N64SegIa8(N64SegIa4): diff --git a/tools/splat/segtypes/palette.py b/tools/splat/segtypes/n64/palette.py similarity index 96% rename from tools/splat/segtypes/palette.py rename to tools/splat/segtypes/n64/palette.py index f20e45c199..331190188e 100644 --- a/tools/splat/segtypes/palette.py +++ b/tools/splat/segtypes/n64/palette.py @@ -1,9 +1,9 @@ import os -from segtypes.segment import N64Segment +from segtypes.n64.segment import N64Segment from util.color import unpack_color +from util.n64 import Yay0decompress from util.iter import iter_in_groups - class N64SegPalette(N64Segment): require_unique_name = False diff --git a/tools/splat/segtypes/rgba16.py b/tools/splat/segtypes/n64/rgba16.py similarity index 96% rename from tools/splat/segtypes/rgba16.py rename to tools/splat/segtypes/n64/rgba16.py index d6dbb097eb..a8d04e4713 100644 --- a/tools/splat/segtypes/rgba16.py +++ b/tools/splat/segtypes/n64/rgba16.py @@ -1,7 +1,6 @@ import os -from segtypes.segment import N64Segment -from pathlib import Path -from util import Yay0decompress +from segtypes.n64.segment import N64Segment +from util.n64 import Yay0decompress import png from math import ceil from util.color import unpack_color diff --git a/tools/splat/segtypes/rgba32.py b/tools/splat/segtypes/n64/rgba32.py similarity index 81% rename from tools/splat/segtypes/rgba32.py rename to tools/splat/segtypes/n64/rgba32.py index 009feb614e..e13c3b0811 100644 --- a/tools/splat/segtypes/rgba32.py +++ b/tools/splat/segtypes/n64/rgba32.py @@ -1,4 +1,4 @@ -from segtypes.rgba16 import N64SegRgba16 +from segtypes.n64.rgba16 import N64SegRgba16 class N64SegRgba32(N64SegRgba16): def parse_image(self, data): diff --git a/tools/splat/segtypes/n64/segment.py b/tools/splat/segtypes/n64/segment.py new file mode 100644 index 0000000000..a4d859948a --- /dev/null +++ b/tools/splat/segtypes/n64/segment.py @@ -0,0 +1,4 @@ +from segtypes.segment import Segment + +class N64Segment(Segment): + pass diff --git a/tools/splat/segtypes/segment.py b/tools/splat/segtypes/segment.py index f7cc819035..2d03dad2fe 100644 --- a/tools/splat/segtypes/segment.py +++ b/tools/splat/segtypes/segment.py @@ -1,7 +1,4 @@ -import os from pathlib import Path, PurePath -import re -import json from util import log default_subalign = 16 @@ -43,7 +40,7 @@ def parse_segment_subalign(segment): return default_subalign -class N64Segment: +class Segment: require_unique_name = True def __init__(self, segment, next_segment, options): @@ -52,8 +49,7 @@ class N64Segment: self.type = parse_segment_type(segment) self.name = parse_segment_name(segment, self.__class__) self.vram_addr = parse_segment_vram(segment) - self.ld_name_override = segment.get( - "ld_name", None) if type(segment) is dict else None + self.ld_name_override = segment.get("ld_name", None) if type(segment) is dict else None self.options = options self.config = segment self.subalign = parse_segment_subalign(segment) diff --git a/tools/splat/split.py b/tools/splat/split.py index 3c2b626fa6..93f38fa4f6 100755 --- a/tools/splat/split.py +++ b/tools/splat/split.py @@ -5,16 +5,12 @@ import importlib import importlib.util import os from ranges import Range, RangeDict -import re from pathlib import Path -import segtypes -import sys import yaml import pickle from colorama import Style, Fore -from collections import OrderedDict -from segtypes.segment import N64Segment, parse_segment_type -from segtypes.code import N64SegCode +from segtypes.segment import parse_segment_type +from segtypes.n64.code import N64SegCode from util import log parser = argparse.ArgumentParser( @@ -81,11 +77,23 @@ def parse_file_start(split_file): def get_symbol_addrs_path(repo_path, options): - return os.path.join(repo_path, options.get("symbol_addrs", "symbol_addrs.txt")) + return os.path.join(repo_path, options.get("symbol_addrs_path", "symbol_addrs.txt")) def get_undefined_syms_path(repo_path, options): - return os.path.join(repo_path, options.get("undefined_syms", "undefined_syms.txt")) + return os.path.join(repo_path, options.get("undefined_syms_path", "undefined_syms.txt")) + + +def get_undefined_syms_auto_path(repo_path, options): + return os.path.join(repo_path, options.get("undefined_syms_auto_path", "undefined_syms_auto.txt")) + + +def get_undefined_funcs_auto_path(repo_path, options): + return os.path.join(repo_path, options.get("undefined_funcs_auto_path", "undefined_funcs_auto.txt")) + + +def get_cache_path(repo_path, options): + return os.path.join(repo_path, options.get("cache_path", ".splat_cache")) def gather_symbols(symbol_addrs_path, undefined_syms_path): @@ -156,13 +164,13 @@ def gather_c_variables(undefined_syms_path): return vars -def get_base_segment_class(seg_type): +def get_base_segment_class(seg_type, platform): try: - segmodule = importlib.import_module("segtypes." + seg_type) + segmodule = importlib.import_module(f"segtypes.{platform}.{seg_type}") except ModuleNotFoundError: return None - return getattr(segmodule, "N64Seg" + seg_type[0].upper() + seg_type[1:]) + return getattr(segmodule, f"{platform.upper()}Seg{seg_type[0].upper()}{seg_type[1:]}") def get_extension_dir(options, config_path): @@ -171,20 +179,20 @@ def get_extension_dir(options, config_path): return os.path.join(Path(config_path).parent, options["extensions"]) -def get_extension_class(options, config_path, seg_type): +def get_extension_class(options, config_path, seg_type, platform): ext_dir = get_extension_dir(options, config_path) if ext_dir == None: return None try: - ext_spec = importlib.util.spec_from_file_location(f"segtypes.{seg_type}", os.path.join(ext_dir, f"{seg_type}.py")) + ext_spec = importlib.util.spec_from_file_location(f"{platform}.segtypes.{seg_type}", os.path.join(ext_dir, f"{seg_type}.py")) ext_mod = importlib.util.module_from_spec(ext_spec) ext_spec.loader.exec_module(ext_mod) except Exception as err: log.write(err, status="error") return None - return getattr(ext_mod, "N64Seg" + seg_type[0].upper() + seg_type[1:]) + return getattr(ext_mod, f"{platform.upper()}Seg{seg_type[0].upper()}{seg_type[1:]}") def fmt_size(size): @@ -196,7 +204,7 @@ def fmt_size(size): return str(size) + " B" -def initialize_segments(options, config_path, config_segments): +def initialize_segments(options, config_path, config_segments, platform): seen_segment_names = set() ret = [] @@ -207,24 +215,16 @@ def initialize_segments(options, config_path, config_segments): seg_type = parse_segment_type(segment) - segment_class = get_base_segment_class(seg_type) + segment_class = get_base_segment_class(seg_type, platform) if segment_class == None: # Look in extensions - segment_class = get_extension_class(options, config_path, seg_type) + segment_class = get_extension_class(options, config_path, seg_type, platform) if segment_class == None: log.write(f"fatal error: could not load segment type '{seg_type}'\n(hint: confirm your extension directory is configured correctly)", status="error") return 2 - try: - segment = segment_class(segment, config_segments[i + 1], options) - except (IndexError, KeyError) as e: - try: - segment = N64Segment(segment, config_segments[i + 1], options) - segment.error(e) - except Exception as e: - log.write(f"fatal error (segment type = {seg_type}): " + str(e), status="error") - return 2 + segment = segment_class(segment, config_segments[i + 1], options) if segment_class.require_unique_name: if segment.name in seen_segment_names: @@ -254,6 +254,7 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False): symbol_addrs_path = get_symbol_addrs_path(repo_path, options) undefined_syms_path = get_undefined_syms_path(repo_path, options) provided_symbols, c_func_labels_to_add, special_labels, ranges = gather_symbols(symbol_addrs_path, undefined_syms_path) + platform = options.get("platform", "n64") processed_segments = [] ld_sections = [] @@ -267,7 +268,7 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False): seg_cached = {} # Load cache - cache_path = Path(repo_path) / ".splat_cache" + cache_path = get_cache_path(repo_path, options) try: with open(cache_path, "rb") as f: cache = pickle.load(f) @@ -275,10 +276,10 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False): cache = {} # Initialize segments - all_segments = initialize_segments(options, config_path, config["segments"]) + all_segments = initialize_segments(options, config_path, config["segments"], platform) for segment in all_segments: - if type(segment) == N64SegCode: + if platform == "n64" and type(segment) == N64SegCode: # remove special-case sometime segment.all_functions = defined_funcs segment.provided_symbols = provided_symbols segment.special_labels = special_labels @@ -314,7 +315,7 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False): if len(segment.errors) == 0: processed_segments.append(segment) - if type(segment) == N64SegCode: + if platform == "n64" and type(segment) == N64SegCode: # edge case undefined_funcs |= segment.glabels_to_add defined_funcs = {**defined_funcs, **segment.glabels_added} undefined_syms |= segment.undefined_syms_to_add @@ -335,21 +336,23 @@ def main(rom_path, config_path, repo_path, modes, verbose, ignore_cache=False): write_ldscript(config['basename'], repo_path, ld_sections, options) # Write undefined_funcs_auto.txt + undefined_funcs_auto_path = get_undefined_funcs_auto_path(repo_path, options) if verbose: - log.write(f"saving undefined_funcs_auto.txt") + log.write(f"saving {undefined_funcs_auto_path}") c_predefined_funcs = set(provided_symbols.keys()) to_write = sorted(undefined_funcs - set(defined_funcs.values()) - c_predefined_funcs) if len(to_write) > 0: - with open(os.path.join(repo_path, "undefined_funcs_auto.txt"), "w", newline="\n") as f: + with open(undefined_funcs_auto_path, "w", newline="\n") as f: for line in to_write: f.write(line + " = 0x" + line.split("_")[1][:8].upper() + ";\n") # write undefined_syms_auto.txt + undefined_syms_auto_path = get_undefined_syms_auto_path(repo_path, options) if verbose: - log.write(f"saving undefined_syms_auto.txt") + log.write(f"saving {undefined_syms_auto_path}") to_write = sorted(undefined_syms, key=lambda x:x[0]) if len(to_write) > 0: - with open(os.path.join(repo_path, "undefined_syms_auto.txt"), "w", newline="\n") as f: + with open(undefined_syms_auto_path, "w", newline="\n") as f: for sym in to_write: f.write(f"{sym[0]} = 0x{sym[1]:X};\n") diff --git a/tools/splat/util/Yay0decompress.c b/tools/splat/util/n64/Yay0decompress.c similarity index 100% rename from tools/splat/util/Yay0decompress.c rename to tools/splat/util/n64/Yay0decompress.c diff --git a/tools/splat/util/Yay0decompress.py b/tools/splat/util/n64/Yay0decompress.py similarity index 100% rename from tools/splat/util/Yay0decompress.py rename to tools/splat/util/n64/Yay0decompress.py diff --git a/tools/splat/util/find_code_length.py b/tools/splat/util/n64/find_code_length.py similarity index 93% rename from tools/splat/util/find_code_length.py rename to tools/splat/util/n64/find_code_length.py index ebffe2d859..59759205d5 100755 --- a/tools/splat/util/find_code_length.py +++ b/tools/splat/util/n64/find_code_length.py @@ -4,9 +4,6 @@ from capstone import * from capstone.mips import * import argparse -import hashlib -import rominfo -import zlib md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_BIG_ENDIAN) @@ -33,7 +30,7 @@ def run(rom_bytes, start_offset, vram, end_offset=None): def main(): args = parser.parse_args() - rom_bytes = rominfo.read_rom(args.rom) + rom_bytes = open(args.rom, "rb").read() start = int(args.start, 0) end = None vram = int(args.vram, 0) diff --git a/tools/splat/util/rominfo.py b/tools/splat/util/n64/rominfo.py similarity index 91% rename from tools/splat/util/rominfo.py rename to tools/splat/util/n64/rominfo.py index 526fcb15d3..b18aab7a6e 100755 --- a/tools/splat/util/rominfo.py +++ b/tools/splat/util/n64/rominfo.py @@ -6,7 +6,7 @@ import zlib parser = argparse.ArgumentParser(description='Gives information on n64 roms') parser.add_argument('rom', help='path to a .z64 rom') -parser.add_argument('--encoding', help='Text encoding the game header is using, defaults to ASCII, see docs.python.org/2.4/lib/standard-encodings.html for valid encodings', default='ASCII') +parser.add_argument('--encoding', help='Text encoding the game header is using; see docs.python.org/3/library/codecs.html#standard-encodings for valid encodings', default='ASCII') country_codes = { 0x37: "Beta", @@ -71,7 +71,7 @@ def get_info_bytes(rom_bytes, encoding): try: name = rom_bytes[0x20:0x34].decode(encoding).strip() except: - print("n64splat could not decode the game name, try using a different encoding by passing the --encoding argument (see docs.python.org/2.4/lib/standard-encodings.html for valid encodings)") + print("splat could not decode the game name; try using a different encoding by passing the --encoding argument (see docs.python.org/3/library/codecs.html#standard-encodings for valid encodings)") exit(1) country_code = rom_bytes[0x3E] diff --git a/tools/splat/util/n64/symbol.py b/tools/splat/util/n64/symbol.py new file mode 100644 index 0000000000..374b2ff086 --- /dev/null +++ b/tools/splat/util/n64/symbol.py @@ -0,0 +1,12 @@ +class N64Symbol: + + @staticmethod + def get_default_name(vram): + return f"D_{vram:X}" + + def __init__(self, vram, rom=None, name=None, segment=None, length=4): + self.vram = vram + self.rom = rom + self.name = name if name else self.get_default_name(vram) + self.segment = segment + self.length = length