From ae66312d8ce8b9e857b4d377b7a532be65061dbf Mon Sep 17 00:00:00 2001 From: lshamis Date: Sat, 29 Jul 2023 10:03:17 -0700 Subject: [PATCH] Add Python linter to github actions (#1100) * Add Python linter to github actions * wip * Add back splat_ext * Format files * C++ -> C * format 2 files * split workflow into separate file, line length 120, fix excludes * -l 120 in ci * update black locally and apply formatting changes * pyproject.toject --------- Co-authored-by: Ethan Roseman --- .github/workflows/lint.yaml | 2 +- .github/workflows/python.yaml | 24 + .vscode/c_cpp_properties.json | 4 +- .vscode/settings.json | 18 +- coverage.py | 24 +- diff_evt.py | 29 +- first_diff.py | 10 +- progress.py | 46 +- pyproject.toml | 4 + tools/asm_sizes.py | 55 +- tools/build/bin_inc_c.py | 2 +- tools/build/common.py | 5 +- tools/build/configure.py | 149 +-- tools/build/effects.py | 8 +- tools/build/genobjcopy.py | 30 +- tools/build/img/build.py | 12 +- tools/build/imgfx/imgfx_data.py | 49 +- tools/build/ld/multilink_calc.py | 8 +- tools/build/mapfs/combine.py | 20 +- tools/build/mapfs/map_header.py | 6 +- tools/build/mapfs/pack_title_data.py | 2 +- tools/build/mapfs/shape.py | 108 +-- tools/build/mapfs/tex.py | 32 +- tools/build/msg/combine.py | 30 +- tools/build/msg/parse_compile.py | 314 +++++-- tools/build/pal_inc_c.py | 6 +- tools/build/pm_charset.py | 4 +- tools/build/pm_charset_palettes.py | 2 +- tools/build/pm_icons.py | 13 +- tools/build/sprite/header.py | 8 +- tools/build/sprite/npc_sprite.py | 8 +- tools/build/sprite/sprite_shading_profiles.py | 15 +- tools/build/sprite/sprites.py | 86 +- tools/compare_shapes.py | 4 +- tools/disasm_animation.py | 46 +- tools/disasm_hud_element_animation.py | 55 +- tools/disasm_script.py | 883 +++++++++++------- tools/find_duplicates.py | 100 +- tools/find_similar_areas.py | 53 +- tools/get_variable.py | 37 +- tools/m2ctx.py | 14 +- tools/migrate_data_to_c.py | 20 +- tools/mv_segment.py | 15 +- tools/old/codescan.py | 6 +- tools/old/create_renames.py | 2 +- tools/old/fix_bad_evt_changes.py | 3 +- tools/old/gen_effect_renames.py | 10 +- tools/old/gfxdis_loop.py | 14 +- tools/old/make_npc_structs.py | 272 +++--- tools/old/migrate_effect_rodata.py | 2 + tools/old/migrate_exit_strings.py | 1 - tools/old/migrate_rodata.py | 2 + tools/old/new_lines.py | 2 + tools/old/sortsymz.py | 2 +- tools/old/substitute.py | 5 +- tools/old/update_evts.py | 20 +- tools/rename.py | 8 +- tools/sjis.py | 4 +- tools/splat_ext/gfx_common.py | 1 + tools/splat_ext/pm_effect_shims.py | 4 +- tools/splat_ext/pm_icons.py | 16 +- tools/splat_ext/pm_imgfx_data.py | 9 +- tools/splat_ext/pm_map_data.py | 48 +- tools/splat_ext/pm_msg.py | 85 +- tools/splat_ext/pm_sbn.py | 32 +- tools/splat_ext/pm_sprite_shading_profiles.py | 4 +- tools/splat_ext/pm_sprites.py | 87 +- tools/splat_ext/sprite_common.py | 6 +- tools/splat_ext/tex_archives.py | 20 +- tools/splat_ext/vtx_common.py | 1 + tools/star_rod_enum_to_decomp.py | 52 +- tools/star_rod_idx_to_c.py | 451 ++++++--- tools/sym_info.py | 31 +- tools/update_symbol_addrs.py | 51 +- tools/warnings_count/compare_warnings.py | 10 +- 75 files changed, 2033 insertions(+), 1588 deletions(-) create mode 100644 .github/workflows/python.yaml create mode 100644 pyproject.toml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index b5595d180e..5e17852098 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -4,7 +4,7 @@ on: jobs: cpp_lint: - name: Format and lint + name: C format and lint runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml new file mode 100644 index 0000000000..f312dedc49 --- /dev/null +++ b/.github/workflows/python.yaml @@ -0,0 +1,24 @@ +name: Python Formatting & Linting +on: + pull_request: + +jobs: + py_format: + name: black formatting + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install black + - name: Run black + run: | + black . --check diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a321648bfe..a29e1c2fdb 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -12,9 +12,9 @@ }, "includePath": [ "${workspaceFolder}/include", - "${workspaceFolder}/ver/us/build/include", + "${workspaceFolder}/ver/pal/build/include", "${workspaceFolder}/src", - "${workspaceFolder}/assets/us" + "${workspaceFolder}/assets/pal" ], "defines": [ "F3DEX_GBI_2", diff --git a/.vscode/settings.json b/.vscode/settings.json index f141d82e68..8ed6ebd102 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,7 +25,9 @@ "docs/doxygen": true, "expected": true, "ver/jp/expected": true, - "ver/us/expected": true + "ver/us/expected": true, + "ver/pal/expected": true, + "ver/ique/expected": true }, "python.autoComplete.extraPaths": [ "./tools" @@ -47,6 +49,7 @@ "*.h": "c", }, "C_Cpp.autoAddFileAssociations": false, + "C_Cpp.default.cStandard": "c89", "files.exclude": { "**/.git": true, "**/.splat_cache": true, @@ -56,7 +59,14 @@ "**/*.i": true, "docs/doxygen": true }, - "C_Cpp.default.cStandard": "c89", - "python.linting.mypyEnabled": true, - "python.linting.enabled": true, + "[python]": { + "editor.formatOnType": true, + "editor.wordBasedSuggestions": false, + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "modifications", + "editor.defaultFormatter": "ms-python.black-formatter", + }, + "black-formatter.args": [ + "-l 120" + ], } diff --git a/coverage.py b/coverage.py index 8ae0f8336e..9fd41f1a41 100755 --- a/coverage.py +++ b/coverage.py @@ -5,33 +5,36 @@ import re import sys from pathlib import Path + def strip_c_comments(text): def replacer(match): s = match.group(0) - if s.startswith('/'): + if s.startswith("/"): return " " else: return s + pattern = re.compile( r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', - re.DOTALL | re.MULTILINE + re.DOTALL | re.MULTILINE, ) return re.sub(pattern, replacer, text) -c_func_pattern = re.compile( - r"^(static\s+)?[^\s]+\s+([^\s(]+)\(([^;)]*)\)[^;]+{", - re.MULTILINE -) + +c_func_pattern = re.compile(r"^(static\s+)?[^\s]+\s+([^\s(]+)\(([^;)]*)\)[^;]+{", re.MULTILINE) + + def funcs_in_c(text): return (match.group(2) for match in c_func_pattern.finditer(text)) -asm_func_pattern = re.compile( - r"INCLUDE_ASM\([^,]+, [^,]+, ([^,)]+)", - re.MULTILINE -) + +asm_func_pattern = re.compile(r"INCLUDE_ASM\([^,]+, [^,]+, ([^,)]+)", re.MULTILINE) + + def include_asms_in_c(text): return (match.group(1) for match in asm_func_pattern.finditer(text)) + def stuff(version): DIR = os.path.dirname(__file__) NONMATCHINGS_DIR = Path(os.path.join(DIR, "ver", version, "asm", "nonmatchings")) @@ -76,6 +79,7 @@ def stuff(version): if not os.listdir(folder[0]): os.removedirs(folder[0]) + stuff("jp") stuff("us") stuff("pal") diff --git a/diff_evt.py b/diff_evt.py index 5cfc78d9ab..6a2abff44f 100755 --- a/diff_evt.py +++ b/diff_evt.py @@ -13,9 +13,7 @@ sys.path.append("tools") from old.update_evts import parse_symbol_addrs from tools.disasm_script import ScriptDisassembler, get_constants -parser = argparse.ArgumentParser( - description="Diff EVT macros." -) +parser = argparse.ArgumentParser(description="Diff EVT macros.") parser.add_argument( "start", @@ -26,21 +24,13 @@ parser.add_argument( "-w", "--watch", action="store_true", - help="Watch for file changes and update the diff automatically." + help="Watch for file changes and update the diff automatically.", ) -parser.add_argument( - "-m", - "--make", - action="store_true", - help="Run ninja automatically." -) +parser.add_argument("-m", "--make", action="store_true", help="Run ninja automatically.") + +parser.add_argument("-o", action="store_true", help="Ignored for compatibility with diff.py.") -parser.add_argument( - "-o", - action="store_true", - help="Ignored for compatibility with diff.py." -) class EvtDisplay(Display): def __init__(self, start): @@ -106,11 +96,13 @@ class EvtDisplay(Display): refresh_key = (current, target) return (output, refresh_key) -class FakeConfig(): + +class FakeConfig: def __init__(self, args): self.make = args.make self.source_extensions = ["c", "h"] + def run_ninja(): return subprocess.run( ["ninja", "ver/current/build/papermario.z64"], @@ -118,6 +110,7 @@ def run_ninja(): stdout=subprocess.PIPE, ) + def main(): args = parser.parse_args() get_constants() @@ -153,8 +146,7 @@ def main(): ret = run_ninja() if ret.returncode != 0: display.update( - ret.stderr.decode("utf-8-sig", "replace") - or ret.stdout.decode("utf-8-sig", "replace"), + ret.stderr.decode("utf-8-sig", "replace") or ret.stdout.decode("utf-8-sig", "replace"), error=True, ) continue @@ -164,5 +156,6 @@ def main(): else: display.run_sync() + if __name__ == "__main__": main() diff --git a/first_diff.py b/first_diff.py index 5f891a0b68..e2c7728969 100755 --- a/first_diff.py +++ b/first_diff.py @@ -22,7 +22,7 @@ parser.add_argument( action="store", default=False, const="prompt", - help="run diff.py on the result with the provided arguments" + help="run diff.py on the result with the provided arguments", ) parser.add_argument( "-m", "--make", help="run ninja before finding difference(s)", action="store_true" @@ -101,7 +101,9 @@ def search_rom_address(target_addr): continue if rom > target_addr: - return f"{prev_sym} (RAM 0x{prev_ram:X}, ROM 0x{prev_rom:X}, {prev_file})" + return ( + f"{prev_sym} (RAM 0x{prev_ram:X}, ROM 0x{prev_rom:X}, {prev_file})" + ) prev_ram = ram prev_rom = rom @@ -214,9 +216,7 @@ if diffs == 0: if len(found_instr_diff) > 0: for i in found_instr_diff: print(f"Instruction difference at ROM addr 0x{i:X}, {search_rom_address(i)}") - print( - f"Bytes: {hexbytes(mybin[i : i + 4])} vs {hexbytes(basebin[i : i + 4])}" - ) + print(f"Bytes: {hexbytes(mybin[i : i + 4])} vs {hexbytes(basebin[i : i + 4])}") print() definite_shift = diffs > shift_cap diff --git a/progress.py b/progress.py index 442c10ccd5..33bc896cbb 100755 --- a/progress.py +++ b/progress.py @@ -25,11 +25,7 @@ def load_latest_progress(version): version = Path("ver/current").resolve().parts[-1] - csv = ( - urlopen(f"https://papermar.io/reports/progress_{version}.csv") - .read() - .decode("utf-8") - ) + csv = urlopen(f"https://papermar.io/reports/progress_{version}.csv").read().decode("utf-8") latest = csv.split("\n")[-2] ( @@ -56,14 +52,10 @@ def load_latest_progress(version): def get_func_info(): try: - result = subprocess.run( - ["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE - ) + result = subprocess.run(["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE) nm_lines = result.stdout.decode().split("\n") except: - print( - f"Error: Could not run objdump on {elf_path} - make sure that the project is built" - ) + print(f"Error: Could not run objdump on {elf_path} - make sure that the project is built") sys.exit(1) sizes = {} @@ -135,19 +127,13 @@ def do_section_progress( section_vram_end, ): funcs = get_funcs_in_vram_range(vrams, section_vram_start, section_vram_end) - matching_size, nonmatching_size = get_funcs_sizes( - sizes, matchings, nonmatchings, restrict_to=funcs - ) + matching_size, nonmatching_size = get_funcs_sizes(sizes, matchings, nonmatchings, restrict_to=funcs) section_total_size = matching_size + nonmatching_size progress_ratio = (matching_size / section_total_size) * 100 matching_ratio = (matching_size / total_size) * 100 total_ratio = (section_total_size / total_size) * 100 - print( - f"\t{section_name}: {matching_size} matching bytes / {section_total_size} total ({progress_ratio:.2f}%)" - ) - print( - f"\t\t(matched {matching_ratio:.2f}% of {total_ratio:.2f}% total rom for {section_name})" - ) + print(f"\t{section_name}: {matching_size} matching bytes / {section_total_size} total ({progress_ratio:.2f}%)") + print(f"\t\t(matched {matching_ratio:.2f}% of {total_ratio:.2f}% total rom for {section_name})") def main(args): @@ -163,9 +149,7 @@ def main(args): nonmatching_funcs = get_nonmatching_funcs() matching_funcs = all_funcs - nonmatching_funcs - matching_size, nonmatching_size = get_funcs_sizes( - sizes, matching_funcs, nonmatching_funcs - ) + matching_size, nonmatching_size = get_funcs_sizes(sizes, matching_funcs, nonmatching_funcs) if len(all_funcs) == 0: funcs_matching_ratio = 0.0 @@ -238,19 +222,9 @@ def main(args): print(f"Warning: category/total size mismatch on version {args.version}!\n") print("Matching size: " + str(matching_size)) print("Nonmatching size: " + str(nonmatching_size)) - print( - "Sum: " - + str(matching_size + nonmatching_size) - + " (should be " - + str(total_size) - + ")" - ) - print( - f"{len(matching_funcs)} matched functions / {len(all_funcs)} total ({funcs_matching_ratio:.2f}%)" - ) - print( - f"{matching_size} matching bytes / {total_size} total ({matching_ratio:.2f}%)" - ) + print("Sum: " + str(matching_size + nonmatching_size) + " (should be " + str(total_size) + ")") + print(f"{len(matching_funcs)} matched functions / {len(all_funcs)} total ({funcs_matching_ratio:.2f}%)") + print(f"{matching_size} matching bytes / {total_size} total ({matching_ratio:.2f}%)") do_section_progress( "effects", diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..27b0c1b0f1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[tool.black] +line-length = 120 +exclude = 'tools/splat/' +extend-exclude = 'diff.py' diff --git a/tools/asm_sizes.py b/tools/asm_sizes.py index 6de3f3e84e..05dad64aec 100755 --- a/tools/asm_sizes.py +++ b/tools/asm_sizes.py @@ -10,7 +10,7 @@ from enum import IntEnum script_dir = os.path.dirname(os.path.realpath(__file__)) asm_dir = script_dir + "/../ver/current/asm/nonmatchings" -modes = [ "min", "max", "avg", "total", "size" ] +modes = ["min", "max", "avg", "total", "size"] sizes = {} @@ -47,18 +47,53 @@ def do_dir(root, dir): avg = 0 if len(files) == 0 else total / len(files) - sizes[root + "/" + dir] = ((min, max, total, avg, len(files))) + sizes[root + "/" + dir] = (min, max, total, avg, len(files)) -parser = argparse.ArgumentParser(description="A tool to receive information about the number of non-matching .s files " - +"per .c file, or the size of .s files, measured by their number of instructions. " - +"Option -p is used by default if no option is specified.") +parser = argparse.ArgumentParser( + description="A tool to receive information about the number of non-matching .s files " + + "per .c file, or the size of .s files, measured by their number of instructions. " + + "Option -p is used by default if no option is specified." +) group = parser.add_mutually_exclusive_group() -group.add_argument("-f", "--files", help="Default. Print the number of non-matching .s files per .c file, ordered by size.", action='store_true', required=False) -group.add_argument("-a", "--alphabetical", help="Print the size of .s files, ordered by name.", action='store_true', required=False) -group.add_argument("-s", "--size", help="Print the size of .s files, ordered by size.", action='store_true', required=False) -parser.add_argument("-l", "--limit", help="Only print the .c --files that are greater than or equal to the value.", type=int, default=0, required=False) -parser.add_argument("-m", "--mode", help="Switches between output modes for --files. Allowed values are: {min, max, avg, total, size}.", choices=modes, default="size", metavar='', required=False) +group.add_argument( + "-f", + "--files", + help="Default. Print the number of non-matching .s files per .c file, ordered by size.", + action="store_true", + required=False, +) +group.add_argument( + "-a", + "--alphabetical", + help="Print the size of .s files, ordered by name.", + action="store_true", + required=False, +) +group.add_argument( + "-s", + "--size", + help="Print the size of .s files, ordered by size.", + action="store_true", + required=False, +) +parser.add_argument( + "-l", + "--limit", + help="Only print the .c --files that are greater than or equal to the value.", + type=int, + default=0, + required=False, +) +parser.add_argument( + "-m", + "--mode", + help="Switches between output modes for --files. Allowed values are: {min, max, avg, total, size}.", + choices=modes, + default="size", + metavar="", + required=False, +) args = parser.parse_args() diff --git a/tools/build/bin_inc_c.py b/tools/build/bin_inc_c.py index c05118a0bd..9a78c307cc 100755 --- a/tools/build/bin_inc_c.py +++ b/tools/build/bin_inc_c.py @@ -12,6 +12,6 @@ if __name__ == "__main__": with open(infile, "rb") as i: for char in i.read(): - f.write(f'0x{char:02X}, ') + f.write(f"0x{char:02X}, ") f.write(f"}};\n") diff --git a/tools/build/common.py b/tools/build/common.py index e8836d4589..f79263a828 100644 --- a/tools/build/common.py +++ b/tools/build/common.py @@ -4,10 +4,7 @@ import os from pathlib import Path from typing import Tuple -ASSETS_DIR = ( - Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) - / "assets" -) +ASSETS_DIR = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) / "assets" @lru_cache(maxsize=None) diff --git a/tools/build/configure.py b/tools/build/configure.py index 6802e1ee51..7b08e2c0d5 100755 --- a/tools/build/configure.py +++ b/tools/build/configure.py @@ -28,9 +28,7 @@ PIGMENT_REQ_VERSION = "0.3.0" def exec_shell(command: List[str]) -> str: - ret = subprocess.run( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True - ) + ret = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) return ret.stdout @@ -50,9 +48,7 @@ def write_ninja_rules( if use_ccache: ccache = "ccache " try: - subprocess.call( - ["ccache"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL - ) + subprocess.call(["ccache"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except FileNotFoundError: ccache = "" @@ -134,9 +130,7 @@ def write_ninja_rules( command="sha1sum -c $in && touch $out" if DO_SHA1_CHECK else "touch $out", ) - ninja.rule( - "cpp", description="cpp $in", command=f"{cpp} $in {extra_cppflags} -P -o $out" - ) + ninja.rule("cpp", description="cpp $in", command=f"{cpp} $in {extra_cppflags} -P -o $out") ninja.rule( "cc", @@ -287,9 +281,7 @@ def write_ninja_rules( command=f"$python {BUILD_TOOLS}/mapfs/pack_title_data.py $out $in", ) - ninja.rule( - "map_header", command=f"$python {BUILD_TOOLS}/mapfs/map_header.py $in > $out" - ) + ninja.rule("map_header", command=f"$python {BUILD_TOOLS}/mapfs/map_header.py $in > $out") ninja.rule("charset", command=f"$python {BUILD_TOOLS}/pm_charset.py $out $in") @@ -303,25 +295,17 @@ def write_ninja_rules( command=f"$python {BUILD_TOOLS}/sprite/sprite_shading_profiles.py $in $out $header_path", ) - ninja.rule( - "imgfx_data", command=f"$python {BUILD_TOOLS}/imgfx/imgfx_data.py $in $out" - ) + ninja.rule("imgfx_data", command=f"$python {BUILD_TOOLS}/imgfx/imgfx_data.py $in $out") ninja.rule("shape", command=f"$python {BUILD_TOOLS}/mapfs/shape.py $in $out") - ninja.rule( - "effect_data", command=f"$python {BUILD_TOOLS}/effects.py $in_yaml $out_dir" - ) + ninja.rule("effect_data", command=f"$python {BUILD_TOOLS}/effects.py $in_yaml $out_dir") ninja.rule("pm_sbn", command=f"$python {BUILD_TOOLS}/audio/sbn.py $out $in") with Path("tools/permuter_settings.toml").open("w") as f: - f.write( - f"compiler_command = \"{cc} {CPPFLAGS.replace('$version', 'pal')} {cflags} -DPERMUTER -fforce-addr\"\n" - ) - f.write( - f'assembler_command = "{cross}as -EB -march=vr4300 -mtune=vr4300 -Iinclude"\n' - ) + f.write(f"compiler_command = \"{cc} {CPPFLAGS.replace('$version', 'pal')} {cflags} -DPERMUTER -fforce-addr\"\n") + f.write(f'assembler_command = "{cross}as -EB -march=vr4300 -mtune=vr4300 -Iinclude"\n') f.write(f'compiler_type = "gcc"\n') f.write( """ @@ -512,11 +496,7 @@ class Configure: for object_path in object_paths: if object_path.suffixes[-1] == ".o": built_objects.add(str(object_path)) - elif ( - object_path.suffixes[-1] == ".h" - or task == "bin_inc_c" - or task == "pal_inc_c" - ): + elif object_path.suffixes[-1] == ".h" or task == "bin_inc_c" or task == "pal_inc_c": generated_headers.append(str(object_path)) # don't rebuild objects if we've already seen all of them @@ -580,15 +560,13 @@ class Configure: if isinstance(seg, segtypes.n64.header.N64SegHeader): build(entry.object_path, entry.src_paths, "as") elif isinstance(seg, segtypes.common.asm.CommonSegAsm) or ( - isinstance(seg, segtypes.common.data.CommonSegData) - and not seg.type[0] == "." + isinstance(seg, segtypes.common.data.CommonSegData) and not seg.type[0] == "." ): build(entry.object_path, entry.src_paths, "as") elif seg.type in ["pm_effect_loads", "pm_effect_shims"]: build(entry.object_path, entry.src_paths, "as") elif isinstance(seg, segtypes.common.c.CommonSegC) or ( - isinstance(seg, segtypes.common.data.CommonSegData) - and seg.type[0] == "." + isinstance(seg, segtypes.common.data.CommonSegData) and seg.type[0] == "." ): cflags = None if isinstance(seg.yaml, dict): @@ -619,16 +597,12 @@ class Configure: task = "cc_272" cflags = cflags.replace("gcc_272", "") - encoding = ( - "CP932" # similar to SHIFT-JIS, but includes backslash and tilde - ) + encoding = "CP932" # similar to SHIFT-JIS, but includes backslash and tilde if version == "ique": encoding = "EUC-JP" # Dead cod - if isinstance(seg.parent.yaml, dict) and seg.parent.yaml.get( - "dead_code", False - ): + if isinstance(seg.parent.yaml, dict) and seg.parent.yaml.get("dead_code", False): obj_path = str(entry.object_path) init_obj_path = Path(obj_path + ".dead") build( @@ -677,9 +651,7 @@ class Configure: src_paths = [seg.out_path().relative_to(ROOT)] inc_dir = self.build_path() / "include" / seg.dir - bin_path = ( - self.build_path() / seg.dir / (seg.name + ".png.bin") - ) + bin_path = self.build_path() / seg.dir / (seg.name + ".png.bin") build( bin_path, @@ -691,9 +663,7 @@ class Configure: }, ) - assert seg.vram_start is not None, ( - "img with vram_start unset: " + seg.name - ) + assert seg.vram_start is not None, "img with vram_start unset: " + seg.name c_sym = seg.create_symbol( addr=seg.vram_start, @@ -720,9 +690,7 @@ class Configure: elif isinstance(seg, segtypes.n64.palette.N64SegPalette): src_paths = [seg.out_path().relative_to(ROOT)] inc_dir = self.build_path() / "include" / seg.dir - bin_path = ( - self.build_path() / seg.dir / (seg.name + ".pal.bin") - ) + bin_path = self.build_path() / seg.dir / (seg.name + ".pal.bin") build( bin_path, @@ -833,9 +801,7 @@ class Configure: ) # Sprites .bin - sprite_player_header_path = str( - self.build_path() / "include/sprite/player.h" - ) + sprite_player_header_path = str(self.build_path() / "include/sprite/player.h") build( entry.object_path.with_suffix(".bin"), @@ -843,9 +809,7 @@ class Configure: "sprites", variables={ "header_out": sprite_player_header_path, - "build_dir": str( - self.build_path() / "assets" / self.version / "sprite" - ), + "build_dir": str(self.build_path() / "assets" / self.version / "sprite"), "asset_stack": ",".join(self.asset_stack), }, implicit_outputs=[sprite_player_header_path], @@ -859,9 +823,7 @@ class Configure: msg_bins = [] for section_idx, msg_path in enumerate(entry.src_paths): - bin_path = ( - entry.object_path.with_suffix("") / f"{section_idx:02X}.bin" - ) + bin_path = entry.object_path.with_suffix("") / f"{section_idx:02X}.bin" msg_bins.append(bin_path) build(bin_path, [msg_path], "msg") @@ -1005,16 +967,12 @@ class Configure: ) elif name.endswith("_shape_built"): base_name = name[:-6] - raw_bin_path = self.resolve_asset_path( - f"assets/x/mapfs/geom/{base_name}.bin" - ) + raw_bin_path = self.resolve_asset_path(f"assets/x/mapfs/geom/{base_name}.bin") bin_path = bin_path.parent / "geom" / (base_name + ".bin") if c_maps: # raw bin -> c -> o -> elf -> objcopy -> final bin file - c_file_path = ( - bin_path.parent / "geom" / base_name - ).with_suffix(".c") + c_file_path = (bin_path.parent / "geom" / base_name).with_suffix(".c") o_path = bin_path.parent / "geom" / (base_name + ".o") elf_path = bin_path.parent / "geom" / (base_name + ".elf") @@ -1056,12 +1014,7 @@ class Configure: rasters = [] for src_path in entry.src_paths: - out_path = ( - self.build_path() - / seg.dir - / seg.name - / (src_path.stem + ".bin") - ) + out_path = self.build_path() / seg.dir / seg.name / (src_path.stem + ".bin") build( out_path, [src_path], @@ -1079,13 +1032,7 @@ class Configure: palettes = [] for src_path in entry.src_paths: - out_path = ( - self.build_path() - / seg.dir - / seg.name - / "palette" - / (src_path.stem + ".bin") - ) + out_path = self.build_path() / seg.dir / seg.name / "palette" / (src_path.stem + ".bin") build( out_path, [src_path], @@ -1100,9 +1047,7 @@ class Configure: build(entry.object_path.with_suffix(""), palettes, "charset_palettes") build(entry.object_path, [entry.object_path.with_suffix("")], "bin") elif seg.type == "pm_sprite_shading_profiles": - header_path = str( - self.build_path() / "include/sprite/sprite_shading_profiles.h" - ) + header_path = str(self.build_path() / "include/sprite/sprite_shading_profiles.h") build( entry.object_path.with_suffix(""), entry.src_paths, @@ -1115,14 +1060,12 @@ class Configure: build(entry.object_path, [entry.object_path.with_suffix("")], "bin") elif seg.type == "pm_sbn": sbn_path = entry.object_path.with_suffix("") - build(sbn_path, entry.src_paths, "pm_sbn") # could have non-yaml inputs be implicit + build(sbn_path, entry.src_paths, "pm_sbn") # could have non-yaml inputs be implicit build(entry.object_path, [sbn_path], "bin") elif seg.type == "linker" or seg.type == "linker_offset": pass elif seg.type == "pm_imgfx_data": - c_file_path = ( - Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c") - ) + c_file_path = Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c") build(c_file_path, entry.src_paths, "imgfx_data") build( @@ -1136,9 +1079,7 @@ class Configure: }, ) else: - raise Exception( - f"don't know how to build {seg.__class__.__name__} '{seg.name}'" - ) + raise Exception(f"don't know how to build {seg.__class__.__name__} '{seg.name}'") # Run undefined_syms through cpp ninja.build( @@ -1216,20 +1157,14 @@ if __name__ == "__main__": action="store_true", help="Delete assets and previously-built files", ) - parser.add_argument( - "--splat", default="tools/splat", help="Path to splat tool to use" - ) - parser.add_argument( - "--split-code", action="store_true", help="Re-split code segments to asm files" - ) + parser.add_argument("--splat", default="tools/splat", help="Path to splat tool to use") + parser.add_argument("--split-code", action="store_true", help="Re-split code segments to asm files") parser.add_argument( "--no-split-assets", action="store_true", help="Don't split assets from the baserom(s)", ) - parser.add_argument( - "-d", "--debug", action="store_true", help="Generate debugging information" - ) + parser.add_argument("-d", "--debug", action="store_true", help="Generate debugging information") parser.add_argument( "-n", "--non-matching", @@ -1272,12 +1207,8 @@ if __name__ == "__main__": pass if args.cpp is None: print("error: system C preprocessor is not GNU!") - print( - "This is a known issue on macOS - only clang's cpp is installed by default." - ) - print( - "Use 'brew' to obtain GNU cpp, then run this script again with the --cpp option, e.g." - ) + print("This is a known issue on macOS - only clang's cpp is installed by default.") + print("Use 'brew' to obtain GNU cpp, then run this script again with the --cpp option, e.g.") print(f" ./configure --cpp {gcc_cpps[0]}") exit(1) @@ -1285,15 +1216,11 @@ if __name__ == "__main__": version = exec_shell([PIGMENT, "--version"]).split(" ")[1].strip() if version < PIGMENT_REQ_VERSION: - print( - f"error: {PIGMENT} version {PIGMENT_REQ_VERSION} or newer is required, system version is {version}\n" - ) + print(f"error: {PIGMENT} version {PIGMENT_REQ_VERSION} or newer is required, system version is {version}\n") exit(1) except FileNotFoundError: print(f"error: {PIGMENT} is not installed\n") - print( - "To build and install it, obtain cargo:\n\tcurl https://sh.rustup.rs -sSf | sh" - ) + print("To build and install it, obtain cargo:\n\tcurl https://sh.rustup.rs -sSf | sh") print(f"and then run:\n\tcargo install {PIGMENT}") exit(1) @@ -1382,12 +1309,8 @@ if __name__ == "__main__": # include tools/splat_ext in the python path sys.path.append(str((ROOT / "tools/splat_ext").resolve())) - configure.split( - not args.no_split_assets, args.split_code, args.shift, args.debug - ) - configure.write_ninja( - ninja, skip_files, non_matching, args.modern_gcc, args.c_maps - ) + configure.split(not args.no_split_assets, args.split_code, args.shift, args.debug) + configure.write_ninja(ninja, skip_files, non_matching, args.modern_gcc, args.c_maps) all_rom_oks.append(str(configure.rom_ok_path())) diff --git a/tools/build/effects.py b/tools/build/effects.py index 9d8b48b308..9705280c90 100644 --- a/tools/build/effects.py +++ b/tools/build/effects.py @@ -9,9 +9,7 @@ from splat_ext.pm_effect_loads import effects_from_yaml if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Builds effect table, function declarations, macros, and enum" - ) + parser = argparse.ArgumentParser(description="Builds effect table, function declarations, macros, and enum") parser.add_argument("in_yaml") parser.add_argument("out_dir", type=Path) args = parser.parse_args() @@ -31,9 +29,7 @@ if __name__ == "__main__": effect_enum_text += f" {enum_name} = 0x{i:02X},\n" if not effect.empty: - effect_table_text += ( - f" FX_ENTRY({effect.name}, effect_gfx_{effect.gfx}),\n" - ) + effect_table_text += f" FX_ENTRY({effect.name}, effect_gfx_{effect.gfx}),\n" fx_decls_text += effect.get_macro_call("fx_" + effect.name) + ";\n" main_decls_text += effect.get_macro_call(effect.name + "_main") + ";\n" macro_defs += effect.get_macro_def() + "\n" diff --git a/tools/build/genobjcopy.py b/tools/build/genobjcopy.py index 2232a4e828..e70b0671eb 100755 --- a/tools/build/genobjcopy.py +++ b/tools/build/genobjcopy.py @@ -1,23 +1,23 @@ #!/usr/bin/env python3 import sys, os -#Under normal compilation we rely on splat to use a discard option in the ldscript -#to not include sections in the elf then just output all sections, however under debug we want -#to have debug sections. -#In debugging mode splat is told to output a list of sections it is custom creating, which are -#all of the sections we export to the z64 file with an objcopy. The below chunk of code is -#responsible for adding -j to each of the names and outputting a file for objcopy to use -#so we can still generate a elf file with all the extra debugging sections and still output +# Under normal compilation we rely on splat to use a discard option in the ldscript +# to not include sections in the elf then just output all sections, however under debug we want +# to have debug sections. +# In debugging mode splat is told to output a list of sections it is custom creating, which are +# all of the sections we export to the z64 file with an objcopy. The below chunk of code is +# responsible for adding -j to each of the names and outputting a file for objcopy to use +# so we can still generate a elf file with all the extra debugging sections and still output # the required sections to the .z64 without outputting everything. if __name__ == "__main__": - infile, outfile = sys.argv[1:] + infile, outfile = sys.argv[1:] - #generate output based on input - file_data = open(infile,"r").read().split("\n") - if len(file_data[-1]) == 0: - file_data.pop() + # generate output based on input + file_data = open(infile, "r").read().split("\n") + if len(file_data[-1]) == 0: + file_data.pop() - outdata = "-j " + " -j ".join(file_data) - with open(outfile, "w") as f: - f.write(outdata) + outdata = "-j " + " -j ".join(file_data) + with open(outfile, "w") as f: + f.write(outdata) diff --git a/tools/build/img/build.py b/tools/build/img/build.py index 45041d302a..f4f5eb3847 100755 --- a/tools/build/img/build.py +++ b/tools/build/img/build.py @@ -221,12 +221,8 @@ class Converter: # header (struct BackgroundHeader) for i, palette in enumerate(palettes): - out_bytes += (baseaddr + palettes_len + headers_len).to_bytes( - 4, byteorder="big" - ) # raster offset - out_bytes += (baseaddr + headers_len + 0x200 * i).to_bytes( - 4, byteorder="big" - ) # palette offset + out_bytes += (baseaddr + palettes_len + headers_len).to_bytes(4, byteorder="big") # raster offset + out_bytes += (baseaddr + headers_len + 0x200 * i).to_bytes(4, byteorder="big") # palette offset out_bytes += (12).to_bytes(2, byteorder="big") # startX out_bytes += (20).to_bytes(2, byteorder="big") # startY out_bytes += (out_width).to_bytes(2, byteorder="big") # width @@ -263,8 +259,6 @@ if __name__ == "__main__": flip_x = "--flip-x" in argv flip_y = "--flip-y" in argv - (out_bytes, out_width, out_height) = Converter( - mode, infile, flip_x, flip_y - ).convert() + (out_bytes, out_width, out_height) = Converter(mode, infile, flip_x, flip_y).convert() with open(argv[3], "wb") as f: f.write(out_bytes) diff --git a/tools/build/imgfx/imgfx_data.py b/tools/build/imgfx/imgfx_data.py index 18126f1fc8..9056ba1bb1 100755 --- a/tools/build/imgfx/imgfx_data.py +++ b/tools/build/imgfx/imgfx_data.py @@ -6,6 +6,7 @@ import json from pathlib import Path from typing import Any, List + @dataclass class Vertex: idx: int @@ -20,7 +21,16 @@ class Vertex: a: int def toJSON(self): - return " { \"pos\": [" + f"{self.x}, {self.y}, {self.z}" + "], \"uv\": [" + f"{self.u}, {self.v}" + "], \"rgba\": [" + f"{self.r}, {self.g}, {self.b}, {self.a}" + "] }" + return ( + ' { "pos": [' + + f"{self.x}, {self.y}, {self.z}" + + '], "uv": [' + + f"{self.u}, {self.v}" + + '], "rgba": [' + + f"{self.r}, {self.g}, {self.b}, {self.a}" + + "] }" + ) + @dataclass class Triangle: @@ -31,6 +41,7 @@ class Triangle: def toJSON(self): return f" [{self.i}, {self.j}, {self.k}]" + @dataclass class Anim: name: str @@ -46,13 +57,15 @@ class Anim: triangles: List[Triangle] def toJSON(self): - framestr = ",\n".join([" [\n" + ",\n".join([v.toJSON() for v in frame]) + "\n ]" for i, frame in enumerate(self.frames)]) + framestr = ",\n".join( + [" [\n" + ",\n".join([v.toJSON() for v in frame]) + "\n ]" for i, frame in enumerate(self.frames)] + ) trianglestr = ",\n".join([t.toJSON() for t in self.triangles]) ret = "{\n" - ret += " \"flags\": " + str(self.flags) + ",\n" - ret += " \"frames\": [\n" + framestr + "],\n" - ret += " \"triangles\": [\n" + trianglestr + "]\n" + ret += ' "flags": ' + str(self.flags) + ",\n" + ret += ' "frames": [\n' + framestr + "],\n" + ret += ' "triangles": [\n' + trianglestr + "]\n" ret += "}" return ret @@ -60,7 +73,10 @@ class Anim: @staticmethod def fromJSON(name: str, data: Any) -> "Anim": flags = data["flags"] - frames = [[Vertex(idx, *vtx["pos"], *vtx["uv"], *vtx["rgba"]) for idx, vtx in enumerate(frame)] for frame in data["frames"]] + frames = [ + [Vertex(idx, *vtx["pos"], *vtx["uv"], *vtx["rgba"]) for idx, vtx in enumerate(frame)] + for frame in data["frames"] + ] triangles = [Triangle(*t) for t in data["triangles"]] return Anim( @@ -73,15 +89,16 @@ class Anim: keyframes=len(frames), flags=flags, frames=frames, - triangles=triangles + triangles=triangles, ) + def build(inputs: List[Path], output: Path): with open(output, "w") as f: f.write("/* NOTE: This file is autogenerated, do not edit */\n\n") - f.write("#include \"PR/gbi.h\"\n") - f.write("#include \"macros.h\"\n") - f.write("#include \"imgfx.h\"\n\n") + f.write('#include "PR/gbi.h"\n') + f.write('#include "macros.h"\n') + f.write('#include "imgfx.h"\n\n') for input in inputs: with open(input, "r") as fin: @@ -101,7 +118,9 @@ def build(inputs: List[Path], output: Path): for frame in anim.frames: f.write(" {\n") for vtx in frame: - f.write(f" {{ {{{vtx.x}, {vtx.y}, {vtx.z}}}, {{{vtx.u}, {vtx.v}}}, {{{vtx.r}, {vtx.g}, {vtx.b}}}, {vtx.a} }},\n") + f.write( + f" {{ {{{vtx.x}, {vtx.y}, {vtx.z}}}, {{{vtx.u}, {vtx.v}}}, {{{vtx.r}, {vtx.g}, {vtx.b}}}, {vtx.a} }},\n" + ) f.write(" },\n") f.write("};\n\n") @@ -134,7 +153,9 @@ def build(inputs: List[Path], output: Path): # We need a new chunk if max_t1 >= 32 and not just_chunked: - chunk_text = f" gsSPVertex((u8*){vtx_name} + 0xC * {sub_num}, {min(32, max_t1 + 1)}, 0),\n" + chunk_text + chunk_text = ( + f" gsSPVertex((u8*){vtx_name} + 0xC * {sub_num}, {min(32, max_t1 + 1)}, 0),\n" + chunk_text + ) just_chunked = True f.write(chunk_text) chunk_text = "" @@ -155,7 +176,9 @@ def build(inputs: List[Path], output: Path): old_max_t = max(max_t1, max_t2) # Dump final chunk - chunk_text = f" gsSPVertex((u8*){vtx_name} + 0xC * {sub_num}, {max(max_t1, max_t2) + 1}, 0),\n" + chunk_text + chunk_text = ( + f" gsSPVertex((u8*){vtx_name} + 0xC * {sub_num}, {max(max_t1, max_t2) + 1}, 0),\n" + chunk_text + ) f.write(chunk_text) f.write(" gsSPEndDisplayList(),\n") f.write("};\n\n") diff --git a/tools/build/ld/multilink_calc.py b/tools/build/ld/multilink_calc.py index 05af4a7fa0..3239c0c4d5 100755 --- a/tools/build/ld/multilink_calc.py +++ b/tools/build/ld/multilink_calc.py @@ -20,7 +20,7 @@ if __name__ == "__main__": mode = sys.argv[2] syms_to_max = { - "entity_data_vram_end" : [ + "entity_data_vram_end": [ "entity_default_VRAM_END", "entity_jan_iwa_VRAM_END", "entity_sbk_omo_VRAM_END", @@ -44,7 +44,7 @@ if __name__ == "__main__": "world_action_use_spinning_flower_VRAM_END", "world_action_use_tweester_VRAM_END", "world_action_sneaky_parasol_VRAM_END", - ] + ], } addrs: Dict[str, List[int]] = {} @@ -80,7 +80,9 @@ if __name__ == "__main__": out_addrs = {sym: max(addrs[sym]) for sym in addrs} - out_addrs["entity_data_vram_end"] = out_addrs["entity_data_vram_end"] + out_addrs["world_action_vram_end"] - HARDCODED_ADDR + out_addrs["entity_data_vram_end"] = ( + out_addrs["entity_data_vram_end"] + out_addrs["world_action_vram_end"] - HARDCODED_ADDR + ) out = "" for sym in out_addrs: diff --git a/tools/build/mapfs/combine.py b/tools/build/mapfs/combine.py index 62b7ab95a7..0050c54e30 100755 --- a/tools/build/mapfs/combine.py +++ b/tools/build/mapfs/combine.py @@ -4,9 +4,11 @@ from sys import argv from pathlib import Path import struct + def next_multiple(pos, multiple): return pos + pos % multiple + def get_version_date(version): if version == "us": return "Map Ver.00/11/07 15:36" @@ -17,6 +19,7 @@ def get_version_date(version): else: return "Map Ver.??/??/?? ??:??" + def build_mapfs(out_bin, assets, version): # every TOC entry's name field has data after the null terminator made up from all the previous name fields. # we probably don't have to do this for the game to read the data properly (it doesn't read past the null terminator @@ -38,12 +41,12 @@ def build_mapfs(out_bin, assets, version): decompressed_size = decompressed.stat().st_size size = next_multiple(compressed.stat().st_size, 2) if compressed.exists() else decompressed_size - #print(f"{name} {offset:08X} {size:08X} {decompressed_size:08X}") + # print(f"{name} {offset:08X} {size:08X} {decompressed_size:08X}") # write all previously-written names; required to match - lastname = name + lastname[len(name):] + lastname = name + lastname[len(name) :] f.seek(toc_entry_pos) - f.write(lastname.encode('ascii')) + f.write(lastname.encode("ascii")) # write TOC entry. f.seek(toc_entry_pos + 0x10) @@ -61,14 +64,15 @@ def build_mapfs(out_bin, assets, version): last_name_entry = "end_data\0" f.seek(toc_entry_pos) - lastname = last_name_entry + lastname[len(last_name_entry):] - f.write(lastname.encode('ascii')) + lastname = last_name_entry + lastname[len(last_name_entry) :] + f.write(lastname.encode("ascii")) f.seek(toc_entry_pos + 0x18) - f.write((0x903F0000).to_bytes(4, byteorder="big")) # TODO: figure out purpose + f.write((0x903F0000).to_bytes(4, byteorder="big")) # TODO: figure out purpose + if __name__ == "__main__": - argv.pop(0) # python3 + argv.pop(0) # python3 version = argv.pop(0) out = argv.pop(0) @@ -76,6 +80,6 @@ if __name__ == "__main__": # pairs for i in range(0, len(argv), 2): - assets.append((Path(argv[i]), Path(argv[i+1]))) + assets.append((Path(argv[i]), Path(argv[i + 1]))) build_mapfs(out, assets, version) diff --git a/tools/build/mapfs/map_header.py b/tools/build/mapfs/map_header.py index 7812fa31da..f62efcdb73 100755 --- a/tools/build/mapfs/map_header.py +++ b/tools/build/mapfs/map_header.py @@ -4,17 +4,19 @@ from sys import argv, stderr from os import path from xml.dom.minidom import parse + def eprint(*args, **kwargs): print(*args, file=stderr, **kwargs) + if __name__ == "__main__": _, xml_path = argv xml = parse(xml_path) map_name = path.basename(xml_path)[:-4] - print("#include \"common.h\"") - print("#include \"map.h\"") + print('#include "common.h"') + print('#include "map.h"') print("") print("#ifndef NAMESPACE") print(f"#define NAMESPACE {map_name}") diff --git a/tools/build/mapfs/pack_title_data.py b/tools/build/mapfs/pack_title_data.py index 222c7f2697..84e7978243 100755 --- a/tools/build/mapfs/pack_title_data.py +++ b/tools/build/mapfs/pack_title_data.py @@ -3,7 +3,7 @@ from sys import argv if __name__ == "__main__": - argv.pop(0) # python3 + argv.pop(0) # python3 if len(argv) > 4: out, img1, img2, img3, img2_pal = argv diff --git a/tools/build/mapfs/shape.py b/tools/build/mapfs/shape.py index 2f85e85ebe..a2ee8ddff6 100755 --- a/tools/build/mapfs/shape.py +++ b/tools/build/mapfs/shape.py @@ -90,27 +90,17 @@ class HeaderSegment(Segment): # note: do not push model root yet shape.root_node = NodeSegment(self.ptr_root_node, "Node") - shape.vtx_table = shape.push( - VertexTableSegment(self.ptr_vtx_table, "VertexTable") - ) - shape.model_names = shape.push( - StringListSegment(self.ptr_model_names, "ModelNames") - ) - shape.collider_names = shape.push( - StringListSegment(self.ptr_collider_names, "ColliderNames") - ) - shape.zone_names = shape.push( - StringListSegment(self.ptr_zone_names, "ZoneNames") - ) + shape.vtx_table = shape.push(VertexTableSegment(self.ptr_vtx_table, "VertexTable")) + shape.model_names = shape.push(StringListSegment(self.ptr_model_names, "ModelNames")) + shape.collider_names = shape.push(StringListSegment(self.ptr_collider_names, "ColliderNames")) + shape.zone_names = shape.push(StringListSegment(self.ptr_zone_names, "ZoneNames")) def print(self, shape): shape.print(f"ShapeFileHeader {self.get_sym()} = {{") shape.print(f" .root = &{shape.get_symbol(self.ptr_root_node)},") shape.print(f" .vertexTable = {shape.get_symbol(self.ptr_vtx_table)},") shape.print(f" .modelNames = {shape.get_symbol(self.ptr_model_names)},") - shape.print( - f" .colliderNames = {shape.get_symbol(self.ptr_collider_names)}," - ) + shape.print(f" .colliderNames = {shape.get_symbol(self.ptr_collider_names)},") if self.ptr_zone_names != 0: shape.print(f" .zoneNames = {shape.get_symbol(self.ptr_zone_names)},") shape.print("};") @@ -216,9 +206,7 @@ class NodeSegment(Segment): self.model_name = shape.model_name_map[self.addr] shape.push(GroupDataSegment(self.ptr_group_data, "GroupData", self.model_name)) - shape.push( - DisplayDataSegment(self.ptr_display_data, "DisplayData", self.model_name) - ) + shape.push(DisplayDataSegment(self.ptr_display_data, "DisplayData", self.model_name)) shape.push( PropertyListSegment( self.ptr_property_list, @@ -284,37 +272,25 @@ class PropertyListSegment(Segment): if key == 0x5E: if value == 0: - shape.print( - f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = NULL }}}}," - ) + shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = NULL }}}},") else: tex_name = read_ascii_string(shape.file_bytes, value) - shape.print( - f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{tex_name}" }}}},' - ) + shape.print(f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{tex_name}" }}}},') elif key == 0x5F: - shape.print( - f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}}," - ) + shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}},") else: if fmt == 0: # int - shape.print( - f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}}," - ) + shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}},") elif fmt == 1: # float temp = struct.pack(">I", value) (f,) = struct.unpack(">f", temp) - shape.print( - f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .f = {f} }}}}," - ) + shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .f = {f} }}}},") elif fmt == 2: # pointer shape.print( f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{shape.get_symbol(value)}" }}}},' ) else: - raise Exception( - f"Invalid property: 0x{key:08X} 0x{fmt:08X} 0x{value:08X}" - ) + raise Exception(f"Invalid property: 0x{key:08X} 0x{fmt:08X} 0x{value:08X}") shape.print("};") @@ -334,25 +310,15 @@ class GroupDataSegment(Segment): self.ptr_children, ) = struct.unpack(">IIIII", shape.file_bytes[start : start + 20]) - shape.push( - NodeListSegment( - self.ptr_children, "Children", self.model_name, self.num_children - ) - ) - shape.push( - LightSetSegment(self.ptr_lights, "Lights", self.model_name, self.num_lights) - ) + shape.push(NodeListSegment(self.ptr_children, "Children", self.model_name, self.num_children)) + shape.push(LightSetSegment(self.ptr_lights, "Lights", self.model_name, self.num_lights)) shape.push(MatrixSegment(self.ptr_transform_mtx, "Mtx", self.model_name)) def print(self, shape): shape.print(f"ModelGroupData {self.get_sym()} = {{") if self.ptr_transform_mtx != 0: - shape.print( - f" .transformMatrix = (Mtx*) &{shape.get_symbol(self.ptr_transform_mtx)}," - ) - shape.print( - f" .lightingGroup = (Lightsn*) &{shape.get_symbol(self.ptr_lights)}," - ) + shape.print(f" .transformMatrix = (Mtx*) &{shape.get_symbol(self.ptr_transform_mtx)},") + shape.print(f" .lightingGroup = (Lightsn*) &{shape.get_symbol(self.ptr_lights)},") shape.print(f" .numLights = {self.num_lights},") shape.print(f" .childList = {shape.get_symbol(self.ptr_children)},") shape.print(f" .numChildren = {self.num_children},") @@ -392,18 +358,14 @@ class MatrixSegment(Segment): for i in range(4): (a, b, c, d) = struct.unpack(">hhhh", shape.file_bytes[pos : pos + 8]) pos += 8 - shape.print( - f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }}," - ) + shape.print(f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }},") shape.print(" },") shape.print(" .frac = {") for i in range(4): (a, b, c, d) = struct.unpack(">hhhh", shape.file_bytes[pos : pos + 8]) pos += 8 - shape.print( - f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }}," - ) + shape.print(f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }},") shape.print(" },") shape.print("};") @@ -416,13 +378,9 @@ class DisplayDataSegment(Segment): def scan(self, shape): start = self.addr - BASE_ADDR - (self.ptr_display_list,) = struct.unpack( - ">I", shape.file_bytes[start : start + 4] - ) + (self.ptr_display_list,) = struct.unpack(">I", shape.file_bytes[start : start + 4]) - gfx_segment = shape.push( - DisplayListSegment(self.ptr_display_list, "Gfx", self.model_name) - ) + gfx_segment = shape.push(DisplayListSegment(self.ptr_display_list, "Gfx", self.model_name)) # Gfx segments may have been already visited during root Gfx traversal # so we will now force the associated model name to be the current model gfx_segment.model_name = self.model_name @@ -449,15 +407,9 @@ class DisplayListSegment(Segment): if op == GFX_END_DL: break elif op == GFX_START_DL: - shape.push( - DisplayListSegment( - w2, f"Gfx_{hex(w2)[2:].upper()}", self.model_name - ) - ) + shape.push(DisplayListSegment(w2, f"Gfx_{hex(w2)[2:].upper()}", self.model_name)) elif op == GFX_LOAD_MATRIX: - shape.push( - MatrixSegment(w2, f"Mtx_{hex(w2)[2:]}", model_name=self.model_name) - ) + shape.push(MatrixSegment(w2, f"Mtx_{hex(w2)[2:]}", model_name=self.model_name)) elif op == GFX_LOAD_VTX: num = (w1 >> 12) & 0xFFF idx = (w2 - shape.vtx_table.addr) // 0x10 @@ -492,9 +444,7 @@ class DisplayListSegment(Segment): end = (w1 & 0x00000FFF) // 2 buf_pos = end - num index = (w2 - shape.vtx_table.addr) // 0x10 - shape.print( - f" gsSPVertex(&{shape.vtx_table.get_sym()}[{index}], {num}, {buf_pos})," - ) + shape.print(f" gsSPVertex(&{shape.vtx_table.get_sym()}[{index}], {num}, {buf_pos}),") elif op == GFX_DRAW_TRI: i = (w1 & 0x00FF0000) >> 16 j = (w1 & 0x0000FF00) >> 8 @@ -507,9 +457,7 @@ class DisplayListSegment(Segment): d = (w2 & 0x00FF0000) >> 16 e = (w2 & 0x0000FF00) >> 8 f = w2 & 0x000000FF - shape.print( - f" gsSP2Triangles({a // 2}, {b // 2}, {c // 2}, 0, {d // 2}, {e // 2}, {f // 2}, 0)," - ) + shape.print(f" gsSP2Triangles({a // 2}, {b // 2}, {c // 2}, 0, {d // 2}, {e // 2}, {f // 2}, 0),") elif op == GFX_RDP_PIPE_SYNC: shape.print(" gsDPPipeSync(),") elif op == GFX_POP_MATRIX: @@ -522,9 +470,7 @@ class DisplayListSegment(Segment): flags = self.get_geometry_flags(~(w1 | 0xFF000000)) shape.print(f" gsSPClearGeometryMode({flags}),") elif op == GFX_LOAD_MATRIX: - shape.print( - f" gsSPMatrix(&{shape.get_symbol(w2)}, G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW)," - ) + shape.print(f" gsSPMatrix(&{shape.get_symbol(w2)}, G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW),") elif op == GFX_START_DL: shape.print(f" gsSPDisplayList({shape.get_symbol(w2)}),") elif op == GFX_END_DL: @@ -610,9 +556,7 @@ class ShapeFile: child_start = ptr_children - BASE_ADDR for i in range(num_children): - (ptr_child,) = struct.unpack( - ">I", self.file_bytes[child_start : child_start + 4] - ) + (ptr_child,) = struct.unpack(">I", self.file_bytes[child_start : child_start + 4]) self.build_model_name_map(ptr_child, names) child_start += 4 diff --git a/tools/build/mapfs/tex.py b/tools/build/mapfs/tex.py index e0b39a5981..f560a907a3 100644 --- a/tools/build/mapfs/tex.py +++ b/tools/build/mapfs/tex.py @@ -40,15 +40,11 @@ def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> Te if main_data == None: raise Exception(f"Texture {ret.img_name} has no definition for 'main'") - (main_fmt_name, ret.main_hwrap, ret.main_vwrap) = ret.read_json_img( - main_data, "main", ret.img_name - ) + (main_fmt_name, ret.main_hwrap, ret.main_vwrap) = ret.read_json_img(main_data, "main", ret.img_name) (ret.main_fmt, ret.main_depth) = get_format_code(main_fmt_name) # read main image - img_path = get_asset_path( - Path(f"mapfs/tex/{tex_name}/{ret.img_name}.png"), asset_stack - ) + img_path = get_asset_path(Path(f"mapfs/tex/{tex_name}/{ret.img_name}.png"), asset_stack) if not os.path.isfile(img_path): raise Exception(f"Could not find main image for texture: {ret.img_name}") ( @@ -62,9 +58,7 @@ def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> Te ret.has_aux = "aux" in json_data if ret.has_aux: aux_data = json_data.get("aux") - (aux_fmt_name, ret.aux_hwrap, ret.aux_vwrap) = ret.read_json_img( - aux_data, "aux", ret.img_name - ) + (aux_fmt_name, ret.aux_hwrap, ret.aux_vwrap) = ret.read_json_img(aux_data, "aux", ret.img_name) if aux_fmt_name == "Shared": # aux tiles have blank attributes in SHARED mode @@ -79,9 +73,7 @@ def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> Te ret.extra_tiles = TILES_INDEPENDENT_AUX # read aux image - img_path = get_asset_path( - Path(f"mapfs/tex/{tex_name}/{ret.img_name}_AUX.png"), asset_stack - ) + img_path = get_asset_path(Path(f"mapfs/tex/{tex_name}/{ret.img_name}_AUX.png"), asset_stack) if not os.path.isfile(img_path): raise Exception(f"Could not find AUX image for texture: {ret.img_name}") ( @@ -127,9 +119,7 @@ def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> Te f"Texture {ret.img_name} is missing mipmap level {mipmap_idx} (size = {mmw} x {mmh})" ) - (raster, pal, width, height) = ret.get_img_file( - main_fmt_name, str(img_path) - ) + (raster, pal, width, height) = ret.get_img_file(main_fmt_name, str(img_path)) ret.mipmaps.append(raster) if width != mmw or height != mmh: raise Exception( @@ -160,9 +150,7 @@ def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> Te return ret -def build( - out_path: Path, tex_name: str, asset_stack: Tuple[Path, ...], endian: str = "big" -): +def build(out_path: Path, tex_name: str, asset_stack: Tuple[Path, ...], endian: str = "big"): out_bytes = bytearray() json_path = get_asset_path(Path(f"mapfs/tex/{tex_name}.json"), asset_stack) @@ -172,9 +160,7 @@ def build( json_data = json.loads(json_str) if len(json_data) > 128: - raise Exception( - f"Maximum number of textures (128) exceeded by {tex_name} ({len(json_data)})`" - ) + raise Exception(f"Maximum number of textures (128) exceeded by {tex_name} ({len(json_data)})`") for img_data in json_data: img = img_from_json(img_data, tex_name, asset_stack) @@ -189,9 +175,7 @@ if __name__ == "__main__": parser.add_argument("bin_out", type=Path, help="Output binary file path") parser.add_argument("name", help="Name of tex subdirectory") parser.add_argument("asset_stack", help="comma-separated asset stack") - parser.add_argument( - "--endian", choices=["big", "little"], default="big", help="Output endianness" - ) + parser.add_argument("--endian", choices=["big", "little"], default="big", help="Output endianness") args = parser.parse_args() asset_stack = tuple(Path(d) for d in args.asset_stack.split(",")) diff --git a/tools/build/msg/combine.py b/tools/build/msg/combine.py index 5b6cc4af41..b28f1e8d43 100755 --- a/tools/build/msg/combine.py +++ b/tools/build/msg/combine.py @@ -6,6 +6,7 @@ import re import msgpack import os + class Message: def __init__(self, d: dict, header_file_index: int): self.section = d.get("section") @@ -14,6 +15,7 @@ class Message: self.bytes = d["bytes"] self.header_file_index = header_file_index + if __name__ == "__main__": if len(argv) < 3: print("usage: combine.py [out.bin] [out.h] [compiled...]") @@ -22,7 +24,7 @@ if __name__ == "__main__": _, outfile, header_file, *infiles = argv messages = [] - #header_files = [] + # header_files = [] for i, infile in enumerate(infiles): # if infile == "--headers": @@ -34,12 +36,12 @@ if __name__ == "__main__": with open(outfile, "wb") as f: # sectioned+indexed, followed by just sectioned, followed by just indexed, followed by named (unsectioned & unindexed) - #messages.sort(key=lambda msg: bool(msg.section)<<2 + bool(msg.index)) + # messages.sort(key=lambda msg: bool(msg.section)<<2 + bool(msg.index)) names = set() sections = [] - #messages_by_file = {} + # messages_by_file = {} for message in messages: if message.section is None: @@ -64,10 +66,10 @@ if __name__ == "__main__": # else: # names.add(message.name) - # if message.header_file_index in messages_by_file: - # messages_by_file[message.header_file_index].add(message) - # else: - # messages_by_file[message.header_file_index] = set([message]) + # if message.header_file_index in messages_by_file: + # messages_by_file[message.header_file_index].add(message) + # else: + # messages_by_file[message.header_file_index] = set([message]) if message.index in section: print(f"warning: multiple messages allocated to id {section_idx:02X}:{message.index:03X}") @@ -77,7 +79,7 @@ if __name__ == "__main__": section[message.index] = message - f.seek((len(sections) + 1) * 4) # skip past table of contents + f.seek((len(sections) + 1) * 4) # skip past table of contents section_offsets = [] for section in sections: @@ -97,21 +99,15 @@ if __name__ == "__main__": # padding while f.tell() % 0x10 != 0: - f.write(b'\0\0\0\0') + f.write(b"\0\0\0\0") f.seek(0) for offset in section_offsets: f.write(offset.to_bytes(4, byteorder="big")) - f.write(b'\0\0\0\0') + f.write(b"\0\0\0\0") with open(header_file, "w") as f: - f.write( - f"#ifndef _MESSAGE_IDS_H_\n" - f"#define _MESSAGE_IDS_H_\n" - "\n" - '#include "messages.h"\n' - "\n" - ) + f.write(f"#ifndef _MESSAGE_IDS_H_\n" f"#define _MESSAGE_IDS_H_\n" "\n" '#include "messages.h"\n' "\n") for message in messages: if message.name: diff --git a/tools/build/msg/parse_compile.py b/tools/build/msg/parse_compile.py index 85d40c731a..c9d4d2e345 100755 --- a/tools/build/msg/parse_compile.py +++ b/tools/build/msg/parse_compile.py @@ -3,7 +3,8 @@ from sys import argv from collections import OrderedDict import re -import msgpack # way faster than pickle +import msgpack # way faster than pickle + class Message: def __init__(self, name, section, index): @@ -11,7 +12,8 @@ class Message: self.section = section self.index = index - self.bytes = [] # XXX: bytearray would be better + self.bytes = [] # XXX: bytearray would be better + def try_convert_int(s): try: @@ -19,10 +21,11 @@ def try_convert_int(s): except: return s + def parse_command(source): if source[0] != "[": return None, [], {}, source - source = source[1:] # "[" + source = source[1:] # "[" inside_brackets = "" while source[0] != "]": @@ -31,7 +34,7 @@ def parse_command(source): inside_brackets += source[0] source = source[1:] - source = source[1:] # "]" + source = source[1:] # "]" command, *raw_args = inside_brackets.split(" ") @@ -58,6 +61,7 @@ def parse_command(source): return command.lower(), args, named_args, source + def color_to_code(color, style): COLORS = { "diary": { @@ -90,26 +94,30 @@ def color_to_code(color, style): "red": 0x19, "blue": 0x1A, "green": 0x1B, - } + }, } if type(color) is int: return color - return COLORS.get(style, { - # [style:left], [style:right] - "normal": 0x0A, - "red": 0x20, - "pink": 0x21, - "purple": 0x22, - "blue": 0x23, - "cyan": 0x24, - "green": 0x25, - "yellow": 0x26, - }).get(color) + return COLORS.get( + style, + { + # [style:left], [style:right] + "normal": 0x0A, + "red": 0x20, + "pink": 0x21, + "purple": 0x22, + "blue": 0x23, + "cyan": 0x24, + "green": 0x25, + "yellow": 0x26, + }, + ).get(color) + CHARSET = { - #"𝅘𝅥𝅮": 0x00, + # "𝅘𝅥𝅮": 0x00, "!": 0x01, '"': 0x02, "#": 0x03, @@ -323,19 +331,22 @@ CHARSET_CREDITS = { " ": 0xF7, } + def strip_c_comments(text): def replacer(match): s = match.group(0) - if s.startswith('/'): + if s.startswith("/"): return " " else: return s + pattern = re.compile( r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', - re.DOTALL | re.MULTILINE + re.DOTALL | re.MULTILINE, ) return re.sub(pattern, replacer, text) + if __name__ == "__main__": if len(argv) < 3: print("usage: parse_compile.py [in.msg] [out.msgpack] [--c]") @@ -435,7 +446,7 @@ if __name__ == "__main__": print(f"{filename}:{lineno}: expected opening brace ('{{')") exit(1) - source = source[1:] # { + source = source[1:] # { # count indent level indent_level = 0 @@ -484,8 +495,8 @@ if __name__ == "__main__": exit(1) message.bytes += [0xFF, 0x05, color] - #color_stack.append(color) - #elif command == "/color": + # color_stack.append(color) + # elif command == "/color": # color_stack.pop() # message.bytes += [0xFF, 0x05, color_stack[0]] elif command == "style": @@ -517,7 +528,13 @@ if __name__ == "__main__": print(f"{filename}:{lineno}: 'choice' style requires size=_,_") exit(1) - message.bytes += [0x05, pos[0], pos[1], size[0], size[1]] + message.bytes += [ + 0x05, + pos[0], + pos[1], + size[0], + size[1], + ] elif style == "inspect": message.bytes += [0x06] elif style == "sign": @@ -553,7 +570,13 @@ if __name__ == "__main__": print(f"{filename}:{lineno}: 'upgrade' style requires size=_,_") exit(1) - message.bytes += [0x0C, pos[0], pos[1], size[0], size[1]] + message.bytes += [ + 0x0C, + pos[0], + pos[1], + size[0], + size[1], + ] elif style == "narrate": message.bytes += [0x0D] elif style == "epilogue": @@ -579,7 +602,7 @@ if __name__ == "__main__": exit(1) message.bytes += [0xFF, 0x00, font] - #font_stack.append(font) + # font_stack.append(font) if font == 3 or font == 4: charset = CHARSET_CREDITS @@ -709,7 +732,13 @@ if __name__ == "__main__": print(f"{filename}:{lineno}: {command} command requires raster=_") exit(1) - message.bytes += [0xFF, 0x16, spriteid >> 8, spriteid & 0xFF, raster] + message.bytes += [ + 0xFF, + 0x16, + spriteid >> 8, + spriteid & 0xFF, + raster, + ] elif command == "itemicon": itemid = named_args.get("itemid") @@ -722,7 +751,7 @@ if __name__ == "__main__": message.bytes += [0xFF, 0x17, itemid >> 8, itemid & 0xFF] elif command == "image": index = named_args.get("index") - pos = named_args.get("pos") # xx,y + pos = named_args.get("pos") # xx,y hasborder = named_args.get("hasborder") alpha = named_args.get("alpha") fadeamount = named_args.get("fadeamount") @@ -743,7 +772,17 @@ if __name__ == "__main__": print(f"{filename}:{lineno}: {command} command requires fadeamount=_") exit(1) - message.bytes += [0xFF, 0x18, index, pos[0] >> 8, pos[0] & 0xFF, pos[1], hasborder, alpha, fadeamount] + message.bytes += [ + 0xFF, + 0x18, + index, + pos[0] >> 8, + pos[0] & 0xFF, + pos[1], + hasborder, + alpha, + fadeamount, + ] elif command == "hideimage": fadeamount = named_args.get("fadeamount", 0) @@ -933,9 +972,16 @@ if __name__ == "__main__": exit(1) message.bytes += [ - 0xFF, 0x2C, - soundids[0] >> 24, (soundids[0] >> 16) & 0xFF, (soundids[0] >> 8) & 0xFF, soundids[0] & 0xFF, - soundids[1] >> 24, (soundids[1] >> 16) & 0xFF, (soundids[1] >> 8) & 0xFF, soundids[1] & 0xFF, + 0xFF, + 0x2C, + soundids[0] >> 24, + (soundids[0] >> 16) & 0xFF, + (soundids[0] >> 8) & 0xFF, + soundids[0] & 0xFF, + soundids[1] >> 24, + (soundids[1] >> 16) & 0xFF, + (soundids[1] >> 8) & 0xFF, + soundids[1] & 0xFF, ] elif command == "volume": if len(args) != 1: @@ -962,70 +1008,184 @@ if __name__ == "__main__": exit(1) message.bytes += [0xFF, 0x2F, sound] - #sound_stack.append(sound) + # sound_stack.append(sound) # elif command == "/sound": # sound_stack.pop() # message.bytes += [0xFF, 0x2F, sound_stack[0]] elif command == "a": color_code = color_to_code("blue", "button") assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x98, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x98, + 0xFF, + 0x25, + ] elif command == "b": - color_code = color_to_code(named_args.get("color", "green"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "green"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x99, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x99, + 0xFF, + 0x25, + ] elif command == "l": - color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "gray"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9A, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x9A, + 0xFF, + 0x25, + ] elif command == "r": - color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "gray"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9B, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x9B, + 0xFF, + 0x25, + ] elif command == "z": color_code = color_to_code("grey", "button") assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9C, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x9C, + 0xFF, + 0x25, + ] elif command == "c-up": - color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "yellow"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9D, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x9D, + 0xFF, + 0x25, + ] elif command == "c-down": - color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "yellow"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9E, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x9E, + 0xFF, + 0x25, + ] elif command == "c-left": - color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "yellow"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9F, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0x9F, + 0xFF, + 0x25, + ] elif command == "c-right": - color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) + color_code = color_to_code( + named_args.get("color", "yellow"), + named_args.get("ctx", "button"), + ) assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA0, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0xA0, + 0xFF, + 0x25, + ] elif command == "start": - color_code = color_to_code(named_args.get("color", "red"), named_args.get("ctx", "button"))# + color_code = color_to_code( + named_args.get("color", "red"), + named_args.get("ctx", "button"), + ) # assert color_code is not None - message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA1, 0xFF, 0x25] + message.bytes += [ + 0xFF, + 0x24, + 0xFF, + 0x05, + color_code, + 0xA1, + 0xFF, + 0x25, + ] elif command == "~a": message.bytes += [0x98] elif command == "~b": message.bytes += [0x99] elif command == "~l": - message.bytes += [0x9a] + message.bytes += [0x9A] elif command == "~r": - message.bytes += [0x9b] + message.bytes += [0x9B] elif command == "~z": - message.bytes += [0x9c] + message.bytes += [0x9C] elif command == "~c-up": - message.bytes += [0x9d] + message.bytes += [0x9D] elif command == "~c-down": - message.bytes += [0x9e] + message.bytes += [0x9E] elif command == "~c-left": - message.bytes += [0x9f] + message.bytes += [0x9F] elif command == "~c-right": - message.bytes += [0xa0] + message.bytes += [0xA0] elif command == "~start": - message.bytes += [0xa1] + message.bytes += [0xA1] elif command == "note": message.bytes += [0x00] elif command == "heart": @@ -1043,24 +1203,24 @@ if __name__ == "__main__": elif command == "restorepos": message.bytes += [0xFF, 0x23] elif command == "enablecdownnext": - message.bytes += [0xFF, 0x2b] + message.bytes += [0xFF, 0x2B] elif command == "beginchoice": choiceindex = 0 - message.bytes += [0xFF, 0x09] # delayoff + message.bytes += [0xFF, 0x09] # delayoff elif command == "option" and choiceindex >= 0: - message.bytes += [0xFF, 0x1E, choiceindex] # cursor n - message.bytes += [0xFF, 0x21, choiceindex] # option n + message.bytes += [0xFF, 0x1E, choiceindex] # cursor n + message.bytes += [0xFF, 0x21, choiceindex] # option n choiceindex += 1 elif command == "endchoice" and choiceindex >= 0: cancel = named_args.get("cancel") - message.bytes += [0xFF, 0x21, 255] # option 255 - message.bytes += [0xFF, 0x0A] # delayon + message.bytes += [0xFF, 0x21, 255] # option 255 + message.bytes += [0xFF, 0x0A] # delayon if isinstance(cancel, int): - message.bytes += [0xFF, 0x20, cancel] # setcancel n + message.bytes += [0xFF, 0x20, cancel] # setcancel n - message.bytes += [0xFF, 0x1F, choiceindex] # endchoice n + message.bytes += [0xFF, 0x1F, choiceindex] # endchoice n choiceindex = -1 elif command == "animation" and choiceindex >= 0: @@ -1074,7 +1234,7 @@ if __name__ == "__main__": if source[0] == "}": if not explicit_end: print(f"{filename}:{lineno}: warning: string lacks an [end] command") - #message.bytes += [0xFD] + # message.bytes += [0xFD] explicit_end = False # sanity check @@ -1087,7 +1247,7 @@ if __name__ == "__main__": message.bytes += [0x00] message = None - source = source[1:] # } + source = source[1:] # } indent_level = 0 choiceindex = -1 continue @@ -1124,9 +1284,15 @@ if __name__ == "__main__": else: with open(outfile, "wb") as f: - msgpack.pack([{ - "section": message.section, - "index": message.index, - "name": message.name, - "bytes": bytes(message.bytes), - } for message in messages], f) + msgpack.pack( + [ + { + "section": message.section, + "index": message.index, + "name": message.name, + "bytes": bytes(message.bytes), + } + for message in messages + ], + f, + ) diff --git a/tools/build/pal_inc_c.py b/tools/build/pal_inc_c.py index 4a0ce30cf4..1299e0f1ed 100755 --- a/tools/build/pal_inc_c.py +++ b/tools/build/pal_inc_c.py @@ -10,8 +10,8 @@ if __name__ == "__main__": f.write(f"unsigned short {cname}[] = {{ ") with open(infile, "rb") as i: - while (short := i.read(2)): - color = struct.unpack('>H', short)[0] - f.write(f'0x{color:04X}, ') + while short := i.read(2): + color = struct.unpack(">H", short)[0] + f.write(f"0x{color:04X}, ") f.write("};\n") diff --git a/tools/build/pm_charset.py b/tools/build/pm_charset.py index b7830021f7..43d47028d9 100755 --- a/tools/build/pm_charset.py +++ b/tools/build/pm_charset.py @@ -3,7 +3,7 @@ from sys import argv if __name__ == "__main__": - argv.pop(0) # python3 + argv.pop(0) # python3 out = argv.pop(0) with open(out, "wb") as f: @@ -11,4 +11,4 @@ if __name__ == "__main__": with open(path, "rb") as j: f.write(j.read()) while f.tell() % 8 != 0: - f.write(b'\0') + f.write(b"\0") diff --git a/tools/build/pm_charset_palettes.py b/tools/build/pm_charset_palettes.py index af5040a218..b16b5ca748 100755 --- a/tools/build/pm_charset_palettes.py +++ b/tools/build/pm_charset_palettes.py @@ -3,7 +3,7 @@ from sys import argv if __name__ == "__main__": - argv.pop(0) # python3 + argv.pop(0) # python3 out = argv.pop(0) with open(out, "wb") as f: diff --git a/tools/build/pm_icons.py b/tools/build/pm_icons.py index d240d2d508..5bb0e8111c 100644 --- a/tools/build/pm_icons.py +++ b/tools/build/pm_icons.py @@ -9,6 +9,7 @@ import xml.etree.ElementTree as ET from img.build import Converter import png + def get_img_file(fmt_str, img_file: str): def pack_color(r, g, b, a): r = r >> 3 @@ -34,6 +35,7 @@ def get_img_file(fmt_str, img_file: str): return (out_img, out_pal, out_w, out_h) + def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path, ...]): out_bytes = bytearray() offsets: Dict[str, int] = {} @@ -47,11 +49,11 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path if file is None: raise Exception("Icon os missing attribute: 'name'") - + if type is None: raise Exception("Icon os missing attribute: 'type'") - - name = re.sub("\\W","_",file) + + name = re.sub("\\W", "_", file) if type == "solo" or type == "pair": img_path = str(get_asset_path(Path(f"icon/{file}.png"), asset_stack)) @@ -66,7 +68,7 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path if type == "pair": img_path = str(get_asset_path(Path(f"icon/{file}.disabled.png"), asset_stack)) (out_img, out_pal, out_w, out_h) = get_img_file("CI4", str(img_path)) - + offsets[name + "_disabled_raster"] = offsets[name + "_raster"] offsets[name + "_disabled_palette"] = len(out_bytes) out_bytes += out_pal @@ -88,12 +90,13 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path f.write("#ifndef ICON_OFFSETS_H\n") f.write("#define ICON_OFFSETS_H\n") f.write(f"/* This file is auto-generated. Do not edit. */\n\n") - + for name, offset in offsets.items(): f.write(f"#define ICON_{name} 0x{offset:X}\n") f.write("\n#endif // ICON_OFFSETS_H\n") + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Icon archive") parser.add_argument("out_bin", type=Path, help="output binary file path") diff --git a/tools/build/sprite/header.py b/tools/build/sprite/header.py index 5a43f7a2e7..cb4b9d4c78 100755 --- a/tools/build/sprite/header.py +++ b/tools/build/sprite/header.py @@ -50,13 +50,9 @@ if __name__ == "__main__": for p, palette_name in enumerate(sprite.palette_names): for a, name in enumerate(sprite.animation_names): if palette_name == "Default": - f.write( - f"#define ANIM_{sprite_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n" - ) + f.write(f"#define ANIM_{sprite_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n") else: - f.write( - f"#define ANIM_{sprite_name}_{palette_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n" - ) + f.write(f"#define ANIM_{sprite_name}_{palette_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n") f.write("\n") f.write("#endif\n") diff --git a/tools/build/sprite/npc_sprite.py b/tools/build/sprite/npc_sprite.py index c6df2759c4..b60268899b 100644 --- a/tools/build/sprite/npc_sprite.py +++ b/tools/build/sprite/npc_sprite.py @@ -76,9 +76,7 @@ def from_dir( palettes.append(palette) - palette_names.append( - Palette.get("name", Palette.attrib["src"].split(".png")[0]) - ) + palette_names.append(Palette.get("name", Palette.attrib["src"].split(".png")[0])) images = [] image_names: List[str] = [] @@ -192,9 +190,7 @@ if __name__ == "__main__": palette_offsets.append(f.tell()) for rgba in palette: if rgba[3] not in (0, 0xFF): - print( - "error: translucent pixels not allowed in palette {sprite.palette_names[i]}" - ) + print("error: translucent pixels not allowed in palette {sprite.palette_names[i]}") exit(1) color = pack_color(*rgba) diff --git a/tools/build/sprite/sprite_shading_profiles.py b/tools/build/sprite/sprite_shading_profiles.py index 066f5d0d00..3c5aee2c5c 100755 --- a/tools/build/sprite/sprite_shading_profiles.py +++ b/tools/build/sprite/sprite_shading_profiles.py @@ -8,11 +8,13 @@ from pathlib import Path import struct from typing import List, Literal + class LightMode(Enum): UNIFORM = 0 LINEAR = 4 QUADRATIC = 8 + @dataclass class Light: flags: int @@ -83,9 +85,14 @@ def groups_from_json(data) -> List[SpriteShadingGroup]: ) return groups -def build(input: Path, bin_out: Path, header_out: Path, endian: Literal["big", "little"]="big", - matching: bool = True): +def build( + input: Path, + bin_out: Path, + header_out: Path, + endian: Literal["big", "little"] = "big", + matching: bool = True, +): END = ">" if endian == "big" else "<" with open(input, "r") as f: @@ -145,11 +152,11 @@ def build(input: Path, bin_out: Path, header_out: Path, endian: Literal["big", " offsets_table.extend(profile_lists) if matching: - offsets_table += b'\0' * (0x1D0 - len(offsets_table)) # Pad to 0x1D0 + offsets_table += b"\0" * (0x1D0 - len(offsets_table)) # Pad to 0x1D0 final_data = offsets_table + data_table if matching: - final_data += b'\0' * (0xE70 - len(final_data)) # Pad to 0xE70 + final_data += b"\0" * (0xE70 - len(final_data)) # Pad to 0xE70 with open(bin_out, "wb") as f: f.write(final_data) diff --git a/tools/build/sprite/sprites.py b/tools/build/sprite/sprites.py index bd131e62b1..931e1932e7 100755 --- a/tools/build/sprite/sprites.py +++ b/tools/build/sprite/sprites.py @@ -33,9 +33,7 @@ import xml.etree.ElementTree as ET from dataclasses import dataclass from typing import Dict, List, Tuple -TOOLS_DIR = Path( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -) +TOOLS_DIR = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) sys.path.append(str(TOOLS_DIR)) ASSET_DIR = TOOLS_DIR.parent / "assets" @@ -52,11 +50,7 @@ def pack_color(r, g, b, a) -> int: def get_player_sprite_metadata( asset_stack: Tuple[Path, ...], ) -> Tuple[str, List[str], List[str]]: - orderings_tree = ET.parse( - get_asset_path( - Path("sprite") / PLAYER_SPRITE_MEDADATA_XML_FILENAME, asset_stack - ) - ) + orderings_tree = ET.parse(get_asset_path(Path("sprite") / PLAYER_SPRITE_MEDADATA_XML_FILENAME, asset_stack)) build_info = str(orderings_tree.getroot()[0].text) @@ -72,9 +66,7 @@ def get_player_sprite_metadata( def get_npc_sprite_metadata(asset_stack: Tuple[Path, ...]) -> List[str]: - orderings_tree = ET.parse( - get_asset_path(Path("sprite") / NPC_SPRITE_MEDADATA_XML_FILENAME, asset_stack) - ) + orderings_tree = ET.parse(get_asset_path(Path("sprite") / NPC_SPRITE_MEDADATA_XML_FILENAME, asset_stack)) sprite_order: List[str] = [] for sprite_tag in orderings_tree.getroot()[0]: @@ -100,18 +92,14 @@ PALETTE_CACHE: Dict[str, bytes] = {} PLAYER_XML_CACHE: Dict[str, ET.Element] = {} # TODO perhaps encode this better -SPECIAL_RASTER_BYTES = ( - b"\x80\x30\x02\x10\x00\x00\x02\x00\x00\x00\x00\x01\x00\x10\x00\x00" -) +SPECIAL_RASTER_BYTES = b"\x80\x30\x02\x10\x00\x00\x02\x00\x00\x00\x00\x01\x00\x10\x00\x00" def cache_player_rasters(raster_order: List[str], asset_stack: Tuple[Path, ...]): # Read all player rasters and cache them cur_offset = 0 for raster_name in raster_order: - png_path = get_asset_path( - Path(f"sprite/player/rasters/{raster_name}.png"), asset_stack - ) + png_path = get_asset_path(Path(f"sprite/player/rasters/{raster_name}.png"), asset_stack) # "Weird" raster if os.path.getsize(png_path) == 0x10: @@ -232,9 +220,7 @@ def player_xml_to_bytes(xml: ET.Element, asset_stack: Tuple[Path, ...]) -> List[ source = palette_xml.attrib["src"] front_only = bool(palette_xml.get("front_only", False)) if source not in PALETTE_CACHE: - palette_path = get_asset_path( - Path(f"sprite/player/palettes/{source}"), asset_stack - ) + palette_path = get_asset_path(Path(f"sprite/player/palettes/{source}"), asset_stack) with open(palette_path, "rb") as f: img = png.Reader(f) img.preamble(True) @@ -270,9 +256,7 @@ def player_xml_to_bytes(xml: ET.Element, asset_stack: Tuple[Path, ...]) -> List[ if "back" in raster_xml.attrib: has_back = True r = player_raster_from_xml(raster_xml, back=False) - raster_bytes += struct.pack( - ">IBBBB", raster_offset, r.width, r.height, r.palette_idx, 0xFF - ) + raster_bytes += struct.pack(">IBBBB", raster_offset, r.width, r.height, r.palette_idx, 0xFF) raster_offset += r.width * r.height // 2 @@ -292,9 +276,7 @@ def player_xml_to_bytes(xml: ET.Element, asset_stack: Tuple[Path, ...]) -> List[ height = int(special[1], 16) palette = int(raster_xml.attrib.get(BACK_PALETTE_XML, r.palette_idx)) - raster_bytes_back += struct.pack( - ">IBBBB", raster_offset, width, height, palette, 0xFF - ) + raster_bytes_back += struct.pack(">IBBBB", raster_offset, width, height, palette, 0xFF) if is_back: raster_offset += width * height // 2 @@ -312,9 +294,7 @@ def player_xml_to_bytes(xml: ET.Element, asset_stack: Tuple[Path, ...]) -> List[ raster_offsets_bytes_back = b"" for i in range(len(xml[1])): raster_offsets_bytes += int.to_bytes(raster_list_start + i * 8, 4, "big") - raster_offsets_bytes_back += int.to_bytes( - raster_list_start_back + i * 8, 4, "big" - ) + raster_offsets_bytes_back += int.to_bytes(raster_list_start_back + i * 8, 4, "big") raster_offsets_bytes += LIST_END_BYTES raster_offsets_bytes_back += LIST_END_BYTES @@ -330,9 +310,7 @@ def player_xml_to_bytes(xml: ET.Element, asset_stack: Tuple[Path, ...]) -> List[ palette_offsets_bytes += int.to_bytes(palette_list_start + i * 0x20, 4, "big") front_only = bool(palette_xml.attrib.get("front_only", False)) if not front_only: - palette_offsets_bytes_back += int.to_bytes( - palette_list_start_back + i * 0x20, 4, "big" - ) + palette_offsets_bytes_back += int.to_bytes(palette_list_start_back + i * 0x20, 4, "big") palette_offsets_bytes += LIST_END_BYTES palette_offsets_bytes_back += LIST_END_BYTES @@ -397,9 +375,7 @@ def write_player_sprite_header( for palette_xml in sprite_xml[0]: palette_id = int(palette_xml.attrib["id"], 0x10) palette_name = palette_xml.attrib["name"] - player_palettes[sprite_name][ - f"SPR_PAL_{sprite_name}_{palette_name}" - ] = palette_id + player_palettes[sprite_name][f"SPR_PAL_{sprite_name}_{palette_name}"] = palette_id for anim_id, anim_xml in enumerate(sprite_xml[2]): anim_name = anim_xml.attrib["name"] @@ -413,9 +389,7 @@ def write_player_sprite_header( for raster_xml in sprite_xml[1]: raster_id = int(raster_xml.attrib["id"], 0x10) raster_name = raster_xml.attrib["name"] - player_rasters[sprite_name][ - f"SPR_IMG_{sprite_name}_{raster_name}" - ] = raster_id + player_rasters[sprite_name][f"SPR_IMG_{sprite_name}_{raster_name}"] = raster_id raster = RASTER_CACHE[raster_xml.attrib["src"][:-4]] if max_size < raster.size: @@ -449,9 +423,7 @@ def write_player_sprite_header( f.write("};\n\n") for sprite_name in max_sprite_sizes: - f.write( - f"#define MAX_IMG_{sprite_name} 0x{max_sprite_sizes[sprite_name]:04X}\n" - ) + f.write(f"#define MAX_IMG_{sprite_name} 0x{max_sprite_sizes[sprite_name]:04X}\n") f.write("\n") for sprite_name in sprite_order: @@ -472,15 +444,11 @@ def write_player_sprite_header( f.write(f"#endif // {ifdef_name}\n") -def build_player_sprites( - sprite_order: List[str], build_dir: Path, asset_stack: Tuple[Path, ...] -) -> bytes: +def build_player_sprites(sprite_order: List[str], build_dir: Path, asset_stack: Tuple[Path, ...]) -> bytes: sprite_bytes: List[bytes] = [] for sprite_name in sprite_order: - sprite_bytes.extend( - player_xml_to_bytes(PLAYER_XML_CACHE[sprite_name], asset_stack) - ) + sprite_bytes.extend(player_xml_to_bytes(PLAYER_XML_CACHE[sprite_name], asset_stack)) # Compress sprite bytes compressed_sprite_bytes: bytes = b"" @@ -560,9 +528,7 @@ def build_player_rasters(sprite_order: List[str], raster_order: List[str]) -> by if has_back: if "back" in raster_xml.attrib: png_info = RASTER_CACHE[raster_xml.attrib["back"][:-4]] - sheet_rtes_back.append( - RasterTableEntry(png_info.offset, png_info.size) - ) + sheet_rtes_back.append(RasterTableEntry(png_info.offset, png_info.size)) else: sheet_rtes_back.append(RasterTableEntry(SPECIAL_RASTER, 0x10)) @@ -623,27 +589,21 @@ def build( build_dir: Path, asset_stack: Tuple[Path, ...], ) -> None: - build_info, player_sprite_order, player_raster_order = get_player_sprite_metadata( - asset_stack - ) + build_info, player_sprite_order, player_raster_order = get_player_sprite_metadata(asset_stack) npc_sprite_order = get_npc_sprite_metadata(asset_stack) cache_player_rasters(player_raster_order, asset_stack) # Read and cache player XMLs for sprite_name in player_sprite_order: - sprite_xml = ET.parse( - get_asset_path(Path(f"sprite/player/{sprite_name}.xml"), asset_stack) - ).getroot() + sprite_xml = ET.parse(get_asset_path(Path(f"sprite/player/{sprite_name}.xml"), asset_stack)).getroot() PLAYER_XML_CACHE[sprite_name] = sprite_xml # Encode build_info to bytes and pad to 0x10 build_info_bytes = build_info.encode("ascii") build_info_bytes += b"\0" * (0x10 - len(build_info_bytes)) - player_sprite_bytes = build_player_sprites( - player_sprite_order, build_dir / "player", asset_stack - ) + player_sprite_bytes = build_player_sprites(player_sprite_order, build_dir / "player", asset_stack) player_raster_bytes = build_player_rasters(player_sprite_order, player_raster_order) npc_sprite_bytes = build_npc_sprites(npc_sprite_order, build_dir) @@ -659,13 +619,7 @@ def build( npc_sprites_offset, ) - final_data = ( - build_info_bytes - + major_file_divisons - + player_raster_bytes - + player_sprite_bytes - + npc_sprite_bytes - ) + final_data = build_info_bytes + major_file_divisons + player_raster_bytes + player_sprite_bytes + npc_sprite_bytes with open(out_file, "wb") as f: f.write(final_data) diff --git a/tools/compare_shapes.py b/tools/compare_shapes.py index 00260f2788..19dc2e238a 100755 --- a/tools/compare_shapes.py +++ b/tools/compare_shapes.py @@ -12,9 +12,7 @@ for root, dirs, files in os.walk("assets/us/mapfs/geom"): if file.endswith("_shape.bin"): total += 1 shape_file = os.path.join(root, file) - built_data_file = Path("ver/us/build") / shape_file.replace( - "_shape.bin", "_shape_data.bin" - ) + built_data_file = Path("ver/us/build") / shape_file.replace("_shape.bin", "_shape_data.bin") if filecmp.cmp(shape_file, built_data_file, shallow=False): matching += 1 diff --git a/tools/disasm_animation.py b/tools/disasm_animation.py index 7f50b962c2..14bea9e0e8 100755 --- a/tools/disasm_animation.py +++ b/tools/disasm_animation.py @@ -2,53 +2,61 @@ import struct + def read(f): - return struct.unpack('>h', f.read(2))[0] + return struct.unpack(">h", f.read(2))[0] + def i2f(x): return round(x * 180 / 32767 * 200) / 200 + def parse(f): - print('AnimScript script = {') - indent = ' ' + print("AnimScript script = {") + indent = " " while True: op = read(f) if op == 0: - print(f'{indent}AS_END,') + print(f"{indent}AS_END,") break if op == 1: - print(f'{indent}AS_WAIT, {read(f)},') + print(f"{indent}AS_WAIT, {read(f)},") elif op == 3: indent = indent[:-4] - print(f'{indent}AS_END_LOOP,') + print(f"{indent}AS_END_LOOP,") elif op == 5: - print(f'{indent}AS_SET_ROTATION, {read(f)}, AS_F({i2f(read(f))}), AS_F({i2f(read(f))}), AS_F({i2f(read(f))}),') + print( + f"{indent}AS_SET_ROTATION, {read(f)}, AS_F({i2f(read(f))}), AS_F({i2f(read(f))}), AS_F({i2f(read(f))})," + ) elif op == 6: - print(f'{indent}AS_ADD_ROTATION, {read(f)}, AS_F({i2f(read(f))}), AS_F({i2f(read(f))}), AS_F({i2f(read(f))}),') + print( + f"{indent}AS_ADD_ROTATION, {read(f)}, AS_F({i2f(read(f))}), AS_F({i2f(read(f))}), AS_F({i2f(read(f))})," + ) elif op == 8: - print(f'{indent}AS_SET_POS, {read(f)}, {read(f)}, {read(f)}, {read(f)},') + print(f"{indent}AS_SET_POS, {read(f)}, {read(f)}, {read(f)}, {read(f)},") elif op == 10: - print(f'{indent}AS_LOOP,') - indent += ' ' + print(f"{indent}AS_LOOP,") + indent += " " elif op == 14: - print(f'{indent}AS_SET_FLAGS, {read(f)},') + print(f"{indent}AS_SET_FLAGS, {read(f)},") elif op == 15: - print(f'{indent}AS_SET_NODE_FLAGS, {read(f)}, {read(f)},') + print(f"{indent}AS_SET_NODE_FLAGS, {read(f)}, {read(f)},") elif op == 16: - print(f'{indent}AS_CLEAR_NODE_FLAGS, {read(f)}, {read(f)},') + print(f"{indent}AS_CLEAR_NODE_FLAGS, {read(f)}, {read(f)},") elif op == 17: - print(f'{indent}AS_SET_SCALE, {read(f)}, AS_F({i2f(read(f))}), AS_F({i2f(read(f))}), AS_F({i2f(read(f))}),') + print(f"{indent}AS_SET_SCALE, {read(f)}, AS_F({i2f(read(f))}), AS_F({i2f(read(f))}), AS_F({i2f(read(f))}),") elif op == 18: - print(f'{indent}AS_SET_RENDER_MODE, {read(f)},') + print(f"{indent}AS_SET_RENDER_MODE, {read(f)},") elif op == 19: - print(f'{indent}AS_OP_19,') + print(f"{indent}AS_OP_19,") else: - raise Exception(str(f'Unknown opcode {op}')) - print('};') + raise Exception(str(f"Unknown opcode {op}")) + print("};") if __name__ == "__main__": import argparse + parser = argparse.ArgumentParser() parser.add_argument("file", type=str, help="File to dissassemble from") parser.add_argument("offset", help="Offset to start dissassembling from") diff --git a/tools/disasm_hud_element_animation.py b/tools/disasm_hud_element_animation.py index ce9a1dcc31..421899d8a8 100755 --- a/tools/disasm_hud_element_animation.py +++ b/tools/disasm_hud_element_animation.py @@ -3,62 +3,63 @@ import struct import argparse + def fmt_size(val): if val == 0: - return 'HUD_ELEMENT_SIZE_8x8' + return "HUD_ELEMENT_SIZE_8x8" elif val == 1: - return 'HUD_ELEMENT_SIZE_16x16' + return "HUD_ELEMENT_SIZE_16x16" elif val == 2: - return 'HUD_ELEMENT_SIZE_24x24' + return "HUD_ELEMENT_SIZE_24x24" elif val == 3: - return 'HUD_ELEMENT_SIZE_32x32' + return "HUD_ELEMENT_SIZE_32x32" elif val == 4: - return 'HUD_ELEMENT_SIZE_48x48' + return "HUD_ELEMENT_SIZE_48x48" elif val == 5: - return 'HUD_ELEMENT_SIZE_64x64' + return "HUD_ELEMENT_SIZE_64x64" elif val == 6: - return 'HUD_ELEMENT_SIZE_8x16' + return "HUD_ELEMENT_SIZE_8x16" elif val == 7: - return 'HUD_ELEMENT_SIZE_16x8' + return "HUD_ELEMENT_SIZE_16x8" elif val == 8: - return 'HUD_ELEMENT_SIZE_16x24' + return "HUD_ELEMENT_SIZE_16x24" elif val == 9: - return 'HUD_ELEMENT_SIZE_16x32' + return "HUD_ELEMENT_SIZE_16x32" elif val == 10: - return 'HUD_ELEMENT_SIZE_64x32' + return "HUD_ELEMENT_SIZE_64x32" elif val == 11: - return 'HUD_ELEMENT_SIZE_32x16' + return "HUD_ELEMENT_SIZE_32x16" elif val == 12: - return 'HUD_ELEMENT_SIZE_12x12' + return "HUD_ELEMENT_SIZE_12x12" elif val == 13: - return 'HUD_ELEMENT_SIZE_48x24' + return "HUD_ELEMENT_SIZE_48x24" elif val == 14: - return 'HUD_ELEMENT_SIZE_32x8' + return "HUD_ELEMENT_SIZE_32x8" elif val == 15: - return 'HUD_ELEMENT_SIZE_24x8' + return "HUD_ELEMENT_SIZE_24x8" elif val == 16: - return 'HUD_ELEMENT_SIZE_64x16' + return "HUD_ELEMENT_SIZE_64x16" elif val == 17: - return 'HUD_ELEMENT_SIZE_16x64' + return "HUD_ELEMENT_SIZE_16x64" elif val == 18: - return 'HUD_ELEMENT_SIZE_192x32' + return "HUD_ELEMENT_SIZE_192x32" elif val == 19: - return 'HUD_ELEMENT_SIZE_40x40' + return "HUD_ELEMENT_SIZE_40x40" elif val == 20: - return 'HUD_ELEMENT_SIZE_24x16' + return "HUD_ELEMENT_SIZE_24x16" elif val == 21: - return 'HUD_ELEMENT_SIZE_32x40' + return "HUD_ELEMENT_SIZE_32x40" elif val == 22: - return 'HUD_ELEMENT_SIZE_40x16' + return "HUD_ELEMENT_SIZE_40x16" elif val == 23: - return 'HUD_ELEMENT_SIZE_40x24' + return "HUD_ELEMENT_SIZE_40x24" elif val == 24: - return 'HUD_ELEMENT_SIZE_32x24' + return "HUD_ELEMENT_SIZE_32x24" else: return val -class HudElementScript(): +class HudElementScript: def __init__(self, symbol): self.symbol = symbol self.buffer = [] @@ -172,7 +173,7 @@ if __name__ == "__main__": if word > 0x8000000: word = f"0x{word:X}" else: - word, = struct.unpack(">i", struct.pack(">I", word)) + (word,) = struct.unpack(">i", struct.pack(">I", word)) print(word) except ValueError: pass diff --git a/tools/disasm_script.py b/tools/disasm_script.py index ddb420daff..efabc61296 100755 --- a/tools/disasm_script.py +++ b/tools/disasm_script.py @@ -5,6 +5,8 @@ from pathlib import Path _script_lib = None + + def script_lib(offset=0): global _script_lib @@ -43,9 +45,9 @@ def script_lib(offset=0): raddr = "0xFFFFFFFF" if "rom:" in line: - raddr = line.split("rom:",1)[1] + raddr = line.split("rom:", 1)[1] if " " in raddr: - raddr = raddr.split(" ",1)[0] + raddr = raddr.split(" ", 1)[0] raddr = raddr.strip() if vaddr not in _script_lib: @@ -77,13 +79,15 @@ def extend_symbol_map(a, b): return a + def round_fixed(f: float) -> float: g = f * 100.0 whole = round(g) - if abs(g - whole) <= 100.0/1024.0: + if abs(g - whole) <= 100.0 / 1024.0: f = whole / 100.0 return f + def find_symbol_in_overlay(symbol_map, overlay_rom_addr, symbol_ram_addr): if not symbol_ram_addr in symbol_map: return None @@ -103,11 +107,12 @@ def find_symbol_in_overlay(symbol_map, overlay_rom_addr, symbol_ram_addr): return symbol_map[symbol_ram_addr][0][1] + def browse_header(valid_enums, enums): # enums - for i,line in enumerate(enums): + for i, line in enumerate(enums): if (line.startswith("enum ")) or (line.startswith("typedef enum ")): - enum_name = line.split("enum ",1)[1].split(" {",1)[0] + enum_name = line.split("enum ", 1)[1].split(" {", 1)[0] if enum_name in valid_enums: CONSTANTS[enum_name] = {} last_num = -1 @@ -118,10 +123,10 @@ def browse_header(valid_enums, enums): continue if "//" in enums[i]: - name = enums[i].split("//",1)[0].strip() + name = enums[i].split("//", 1)[0].strip() else: name = enums[i].strip() - val = last_num+1 + val = last_num + 1 if "=" in name: name, val = name.split(" = ") val = int(val[:-1], 0) @@ -130,30 +135,66 @@ def browse_header(valid_enums, enums): else: name = name[:-1] name = name.strip() - #print("\"" + name + "\"", "===", val) + # print("\"" + name + "\"", "===", val) CONSTANTS[enum_name][val] = name.strip() i += 1 last_num = val return + # Grab CONSTANTS from the include/ folder to save manual work CONSTANTS = {} SWITCH_TYPES = [] -LOCAL_WORDS = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] +LOCAL_WORDS = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] SAVE_VARS = set() + + def get_constants(): global CONSTANTS global VALID_SAVE_VARS - valid_enums = { "StoryProgress", "ItemIDs", "PlayerAnims", "ActorType", - "ActorIDs", "Events", "SoundIDs", "SongIDs", "Locations", - "AmbientSounds", "NpcIDs", "Emotes", "NpcFlags", "Statuses", "Elements", - "DamageTypes", "ElementImmunityFlags", "HitResults", "ActorFlags", "ActorPartFlags", - "ActorEventFlags", "ElementFlags", "EncounterTriggers", "Abilities", - "EasingType", "DecorationIDs", "HitResults", "Phases", "ItemSpawnModes", - "ActionStates", "Triggers", "Buttons", "ActionCommand", "MoveIDs", "BattleStatusFlags1", - "BattleStatusFlags2", "BtlCameraPreset", "EffectID", "StatusFlags" } + valid_enums = { + "StoryProgress", + "ItemIDs", + "PlayerAnims", + "ActorType", + "ActorIDs", + "Events", + "SoundIDs", + "SongIDs", + "Locations", + "AmbientSounds", + "NpcIDs", + "Emotes", + "NpcFlags", + "Statuses", + "Elements", + "DamageTypes", + "ElementImmunityFlags", + "HitResults", + "ActorFlags", + "ActorPartFlags", + "ActorEventFlags", + "ElementFlags", + "EncounterTriggers", + "Abilities", + "EasingType", + "DecorationIDs", + "HitResults", + "Phases", + "ItemSpawnModes", + "ActionStates", + "Triggers", + "Buttons", + "ActionCommand", + "MoveIDs", + "BattleStatusFlags1", + "BattleStatusFlags2", + "BtlCameraPreset", + "EffectID", + "StatusFlags", + } for enum in valid_enums: CONSTANTS[enum] = {} CONSTANTS["NPC_SPRITE"] = {} @@ -167,12 +208,11 @@ def get_constants(): enums = Path(include_path / "effects.h").read_text().splitlines() browse_header(valid_enums, enums) - include_path = Path(Path(__file__).resolve().parent.parent / "src" / "battle") enums = Path(include_path / "battle.h").read_text().splitlines() browse_header(valid_enums, enums) - ''' + """ # defines for line in enums: this_enum = "" @@ -186,11 +226,13 @@ def get_constants(): if " " in id_: id_ = id_.split(" ",1)[0] CONSTANTS[this_enum][int(id_, 16)] = name - ''' + """ - #exit() + # exit() # sprites - sprite_path = Path(Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc") + sprite_path = Path( + Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc" + ) for file in sprite_path.iterdir(): fd = file.read_text() for line in fd.splitlines(): @@ -199,7 +241,7 @@ def get_constants(): header_file_name = file.parts[-1] - name = line.split(" ",2)[1] + name = line.split(" ", 2)[1] value = int(line.split("0x", 1)[1], base=16) CONSTANTS["NPC_SPRITE"][value] = name @@ -215,8 +257,8 @@ def make_anim_macro(self, sprite, palette, anim): value = (sprite << 16) | (palette << 8) | anim if value in CONSTANTS["NPC_SPRITE"]: - self.INCLUDES_NEEDED["sprites"].add(CONSTANTS['NPC_SPRITE'][str(value) + ".h"]) - return CONSTANTS['NPC_SPRITE'][value] + self.INCLUDES_NEEDED["sprites"].add(CONSTANTS["NPC_SPRITE"][str(value) + ".h"]) + return CONSTANTS["NPC_SPRITE"][value] else: return f"0x{sprite:02X}{palette:02X}{anim:02X}" @@ -227,6 +269,7 @@ def remove_evt_ptr(s): else: return s + def make_flags(argNum, flags, new_args): enabled = [] for x in range(32): @@ -246,9 +289,8 @@ def fix_args(self, func, args, info): new_args = [] args = args.split(", ") - for i,arg in enumerate(args): - if ((remove_evt_ptr(arg).startswith("D_B")) or - (i == 0 and func == "MakeEntity" and arg.startswith("D_"))): + for i, arg in enumerate(args): + if (remove_evt_ptr(arg).startswith("D_B")) or (i == 0 and func == "MakeEntity" and arg.startswith("D_")): if func == "MakeEntity": arg = "MAKE_ENTITY_END" else: @@ -257,15 +299,15 @@ def fix_args(self, func, args, info): if "0x" in arg and int(remove_evt_ptr(arg), 16) >= 0xF0000000: arg = f"{int(remove_evt_ptr(arg), 16) - 0x100000000}" - if i in info or (i+1 == len(args) and -1 in info): + if i in info or (i + 1 == len(args) and -1 in info): if arg.startswith("LW"): argNum = trim_lw(arg) LOCAL_WORDS[int(argNum)] = info[i] new_args.append(f"{arg}") - #print(LOCAL_WORDS) + # print(LOCAL_WORDS) continue - if i+1 == len(args) and -1 in info: + if i + 1 == len(args) and -1 in info: i = -1 if "_" in arg: new_args.append(f"{arg}") @@ -283,25 +325,25 @@ def fix_args(self, func, args, info): elif info[i] == "CustomAnim": if argNum != -1: try: - value = (argNum & 0x00FFFFFF) + value = argNum & 0x00FFFFFF if func == "SetAnimation" and int(new_args[1], 10) == 0: call = f"{CONSTANTS['PlayerAnims'][argNum]}" elif value in CONSTANTS["NPC_SPRITE"]: - self.INCLUDES_NEEDED["sprites"].add(CONSTANTS['NPC_SPRITE'][str(value) + ".h"]) - call = CONSTANTS['NPC_SPRITE'][value] + self.INCLUDES_NEEDED["sprites"].add(CONSTANTS["NPC_SPRITE"][str(value) + ".h"]) + call = CONSTANTS["NPC_SPRITE"][value] else: call = f"{argNum:06X}" except ValueError: - call = f"0x{argNum:06X}" + call = f"0x{argNum:06X}" except KeyError: - call = f"0x{argNum:06X}" + call = f"0x{argNum:06X}" else: call = "-1" new_args.append(call) elif info[i] == "CustomMsg": type_ = (argNum & 0xFF0000) >> 16 - num_ = (argNum & 0xFFFF) >> 0 + num_ = (argNum & 0xFFFF) >> 0 new_args.append(f"MESSAGE_ID(0x{type_:02X}, 0x{num_:04X})") elif info[i] == "NpcFlags": make_flags(argNum, "NpcFlags", new_args) @@ -341,12 +383,14 @@ def fix_args(self, func, args, info): new_args.append(f"{CONSTANTS[info[i]][argNum]}") except KeyError: if not (info[i] == "NpcIDs" and argNum > 0): - print(f"0x{argNum:X} was not found within {info[i]} constants for function {func} arg {i}, add it.") + print( + f"0x{argNum:X} was not found within {info[i]} constants for function {func} arg {i}, add it." + ) - if (info[i] == "ItemIDs" and argNum < 0): + if info[i] == "ItemIDs" and argNum < 0: new_args.append(f"{int(argNum)}") else: - #Print the unknowns in hex + # Print the unknowns in hex new_args.append(self.var(argNum)) else: @@ -355,217 +399,212 @@ def fix_args(self, func, args, info): replace_funcs = { - "ActorExists" :{0:"ActorIDs", 1:"Bool"}, - "ActorSpeak" :{0:"CustomMsg", 1:"ActorIDs", 3:"CustomAnim", 4:"CustomAnim"}, - "AddActorDecoration" :{0:"ActorIDs"}, - "AddActorVar" :{0:"ActorIDs"}, - "AddKeyItem" :{0:"ItemIDs"}, - "AddGoalPos" :{0:"ActorIDs"}, - - "BattleCamTargetActor" :{0:"ActorIDs"}, - "BindHandleEvent" :{0:"ActorIDs"}, - "BindIdle" :{0:"ActorIDs"}, - "BindNextTurn" :{0:"ActorIDs"}, - "BindNpcAI" :{0:"NpcIDs"}, - "BindNpcDefeat" :{0:"NpcIDs"}, - "BindNpcIdle" :{0:"NpcIDs"}, - "BindNpcInteract" :{0:"NpcIDs"}, - "BindTakeTurn" :{0:"ActorIDs"}, - - "CheckButtonDown" :{0:"Hex", 1:"Bool"}, - "ContinueSpeech" :{1:"CustomAnim", 2:"CustomAnim", 4:"CustomMsg"}, - "CopyBuffs" :{0:"ActorIDs", 1:"ActorIDs"}, - "CopyStatusEffects" :{0:"ActorIDs", 1:"ActorIDs"}, - "CountPlayerTargets" :{0:"ActorIDs"}, - - "DisablePlayerInput" :{0:"Bool"}, - "DisablePlayerPhysics" :{0:"Bool"}, - "DispatchDamagePlayerEvent" :{1:"Events"}, - "DispatchEvent" :{0:"ActorIDs", 1:"Events"}, - - "EnableActorBlur" :{0:"ActorIDs"}, - "EnableActorGlow" :{0:"ActorIDs", 1:"Bool"}, - "EnableIdleScript" :{0:"ActorIDs"}, - "EnableNpcShadow" :{0:"NpcIDs", 1:"Bool"}, - "EndActorSpeech" :{0:"ActorIDs", 2:"CustomAnim", 3:"CustomAnim"}, - "EndSpeech" :{1:"CustomAnim", 2:"CustomAnim"}, - "EnemyDamageTarget" :{0:"ActorIDs", 1:"HitResults", 2:"DamageTypes", 4:"StatusFlags", 6:"BattleStatusFlags1"}, - "EnemyTestTarget" :{0:"ActorIDs", 1:"HitResults", 2:"DamageTypes", 3:"StatusFlags", 5:"BattleStatusFlags1"}, - - "FallToGoal" :{0:"ActorIDs"}, - "FindKeyItem" :{0:"ItemIDs"}, - "FlyPartTo" :{0:"ActorIDs"}, - "FlyToGoal" :{0:"ActorIDs"}, - "ForceHomePos" :{0:"ActorIDs"}, - - "func_8026DF88" :{0:"ActorIDs"}, - "SetActorPaletteEffect" :{0:"ActorIDs"}, - "SetActorPaletteSwapParams" :{0:"ActorIDs"}, - "func_8026ED20" :{0:"ActorIDs"}, - "HideHealthBar" :{0:"ActorIDs"}, - "func_8027D434" :{0:"ActorIDs"}, - "SetProjectileTargetOffset" :{0:"ActorIDs"}, - "GetInstigatorValue" :{0:"ActorIDs"}, - "SetNpcFoldParams" :{0:"NpcIDs"}, - "SetNpcFoldFlags" :{0:"NpcIDs"}, - "UpdatePlayerFold" :{0:"PlayerAnims"}, - - "GetAnimation" :{0:"ActorIDs", 2:"CustomAnim"}, - "GetActorFlags" :{0:"ActorIDs", 1:"ActorFlags"}, - "GetActorHP" :{0:"ActorIDs"}, - "GetActorPos" :{0:"ActorIDs"}, - "GetActorSize" :{0:"ActorIDs"}, - "GetActorVar" :{0:"ActorIDs"}, - "GetBattleFlags" :{0:"BattleStatusFlags1"}, - "GetBattlePhase" :{0:"Phases"}, - "GetDistanceToGoal" :{0:"ActorIDs"}, - "GetEnemyMaxHP" :{0:"ActorIDs"}, - "GetGoalPos" :{0:"ActorIDs"}, - "GetHomePos" :{0:"ActorIDs"}, - "GetIdleGoal" :{0:"ActorIDs"}, - "GetIndexFromHome" :{0:"ActorIDs"}, - "GetIndexFromPos" :{0:"ActorIDs"}, - "GetItemPower" :{0:"ItemIDs"}, - "GetLastDamage" :{0:"ActorIDs"}, - "GetLastElement" :{0:"DamageTypes"}, - "GetLastEvent" :{0:"ActorIDs", 1:"Events"}, - "GetPartOffset" :{0:"ActorIDs"}, - "GetPartPos" :{0:"ActorIDs"}, - "GetPartRotation" :{0:"ActorIDs"}, - "GetNpcPos" :{0:"NpcIDs"}, - "GetOriginalActorType" :{0:"ActorIDs", 1:"ActorType"}, - "GetStatusFlags" :{0:"ActorIDs", 1:"StatusFlags"}, - - "HidePlayerShadow" :{0:"Bool"}, - "HPBarToCurrent" :{0:"ActorIDs"}, - "HPBarToHome" :{0:"ActorIDs"}, - - "IdleFlyToGoal" :{0:"ActorIDs"}, - "IdleJumpToGoal" :{0:"ActorIDs"}, - "IdleRunToGoal" :{0:"ActorIDs"}, - "InterpNpcYaw" :{0:"NpcIDs"}, - - "JumpPartTo" :{0:"ActorIDs"}, - "JumpToGoal" :{0:"ActorIDs", 2:"Bool", 3:"Bool", 4:"Bool"}, - "JumpWithBounce" :{0:"ActorIDs"}, - - "LandJump" :{0:"ActorIDs"}, - "LoadActionCommand" :{0:"ActionCommand"}, - - "MakeEntity" :{0:"Hex"}, - "MakeItemEntity" :{0:"ItemIDs"}, - "ModifyColliderFlags" :{2:"Hex"}, - - "NpcFaceNpc" :{0:"NpcIDs", 1:"NpcIDs"}, - "NpcFacePlayer" :{0:"NpcIDs"}, - "NpcJump0" :{0:"NpcIDs"}, - "NpcJump1" :{0:"NpcIDs"}, - "NpcMoveTo" :{0:"NpcIDs"}, - - "PlayAmbientSounds" :{0:"AmbientSounds"}, - "PlayEffect" :{0:"EffectID"}, - "PlayLoopingSoundAtActor" :{0:"ActorIDs", 2:"SoundIDs"}, - "PlaySound" :{0:"SoundIDs"}, - "PlaySoundAt" :{0:"SoundIDs"}, - "PlaySoundAtActor" :{0:"ActorIDs", 1:"SoundIDs"}, - "PlaySoundAtModel" :{1:"SoundIDs"}, - "PlaySoundAtNpc" :{0:"NpcIDs", 1:"SoundIDs"}, - "PlaySoundAtPart" :{0:"ActorIDs", 2:"SoundIDs"}, - - "RemoveActor" :{0:"ActorIDs"}, - "RemoveActorDecoration" :{0:"ActorIDs"}, - "RemoveNpc" :{0:"NpcIDs"}, - "ResetActorSounds" :{0:"ActorIDs"}, - "ResetAllActorSounds" :{0:"ActorIDs"}, - "RunToGoal" :{0:"ActorIDs", 2:"Bool"}, - - "SetActorDispOffset" :{0:"ActorIDs"}, - "SetActorFlagBits" :{0:"ActorIDs", 1:"ActorFlags"}, - "SetActorIdleSpeed" :{0:"ActorIDs"}, - "SetActorIdleJumpGravity" :{0:"ActorIDs"}, - "SetActorJumpGravity" :{0:"ActorIDs"}, - "SetActorPos" :{0:"ActorIDs"}, - "SetActorRotation" :{0:"ActorIDs"}, - "SetActorRotationOffset" :{0:"ActorIDs"}, - "SetActorScale" :{0:"ActorIDs"}, - "SetActorSize" :{0:"ActorIDs"}, - "SetActorSounds" :{0:"ActorIDs"}, - "SetActorSpeed" :{0:"ActorIDs"}, - "SetActorType" :{0:"ActorIDs", 1:"ActorType"}, - "SetActorVar" :{0:"ActorIDs"}, - "SetActorYaw" :{0:"ActorIDs"}, - - "SetAnimation" :{0:"ActorIDs", 2:"CustomAnim"}, - "SetAnimationRate" :{0:"ActorIDs"}, - "SetBattleFlagBits" :{0:"BattleStatusFlags1"}, - "SetBattleFlagBits2" :{0:"BattleStatusFlags2"}, - "SetDefenseTable" :{0:"ActorIDs"}, - "SetEnemyHP" :{0:"ActorIDs"}, - "SetEnemyTargetOffset" :{0:"ActorIDs"}, - "SetGoalPos" :{0:"ActorIDs"}, - "SetGoalToFirstTarget" :{0:"ActorIDs"}, - "SetGoalToHome" :{0:"ActorIDs"}, - "SetGoalToIndex" :{0:"ActorIDs"}, - "SetGoalToTarget" :{0:"ActorIDs"}, - "SetHomePos" :{0:"ActorIDs"}, - "SetIdleAnimations" :{0:"ActorIDs"}, - "SetIdleGoal" :{0:"ActorIDs"}, - "SetIdleGoalToHome" :{0:"ActorIDs"}, - "SetJumpAnimations" :{0:"ActorIDs", 2:"PlayerAnims", 3:"PlayerAnims", 4:"PlayerAnims"}, - "SetMusicTrack" :{1:"SongIDs"}, - - "SetNpcAnimation" :{0:"NpcIDs", 1:"CustomAnim"}, - "SetNpcAux" :{0:"NpcIDs"}, - "SetNpcFlagBits" :{0:"NpcIDs", 1:"NpcFlags", 2:"Bool"}, - "SetNpcJumpscale" :{0:"NpcIDs"}, - "SetNpcPos" :{0:"NpcIDs"}, - "SetNpcRotation" :{0:"NpcIDs"}, - "SetNpcScale" :{0:"NpcIDs"}, - "SetNpcSpeed" :{0:"NpcIDs"}, - "SetNpcSprite" :{1:"Hex"}, - "SetNpcYaw" :{0:"NpcIDs"}, - - "SetOwnerID" :{0:"ActorIDs"}, - - "SetPartAlpha" :{0:"ActorIDs"}, - "SetPartDispOffset" :{0:"ActorIDs"}, - "SetPartEventBits" :{0:"ActorIDs", 2:"ActorEventFlags"}, - "SetPartFlags" :{0:"ActorIDs", 2:"ActorPartFlags"}, - "SetPartFlagBits" :{0:"ActorIDs", 2:"ActorPartFlags"}, - "SetPartJumpGravity" :{0:"ActorIDs"}, - "SetPartMoveSpeed" :{0:"ActorIDs"}, - "SetPartPos" :{0:"ActorIDs"}, - "SetPartRotation" :{0:"ActorIDs"}, - "SetPartRotationOffset" :{0:"ActorIDs"}, - "SetPartScale" :{0:"ActorIDs"}, - "SetPartSize" :{0:"ActorIDs"}, - "SetPartSounds" :{0:"ActorIDs"}, - "SetPartTargetFlagBits" :{0:"ActorIDs"}, - "SetPartYaw" :{0:"ActorIDs"}, - - "SetPlayerAnimation" :{0:"PlayerAnims"}, - "SetSelfEnemyFlagBits" :{0:"NpcFlags", 1:"Bool"}, - #"SetSelfVar" :{1:"Bool"}, # apparently this was a bool in some scripts but it passes non-0/1 values, including negatives - "SetSpriteShading" :{0:"CustomAnim"}, - "SetStatusTable" :{0:"ActorIDs"}, - "SetTargetActor" :{0:"ActorIDs", 1:"ActorIDs"}, - "SetTargetOffset" :{0:"ActorIDs"}, - "ShowChoice" :{0:"CustomMsg"}, - "ShowEmote" :{1:"Emotes"}, - "ShowMessageAtScreenPos" :{0:"CustomMsg"}, - "ShowMessageAtWorldPos" :{0:"CustomMsg"}, - "SpeakToPlayer" :{0:"NpcIDs", 1:"CustomAnim", 2:"CustomAnim", -1:"CustomMsg"}, - "StopLoopingSoundAtActor" :{0:"ActorIDs"}, - "SwitchMessage" :{0:"CustomMsg"}, - - "UseBattleCamPreset" :{0:"BtlCameraPreset"}, - "UseIdleAnimation" :{0:"ActorIDs", 1:"Bool"}, - - "WasStatusInflicted" :{0:"ActorIDs", 1:"Bool"}, + "ActorExists": {0: "ActorIDs", 1: "Bool"}, + "ActorSpeak": {0: "CustomMsg", 1: "ActorIDs", 3: "CustomAnim", 4: "CustomAnim"}, + "AddActorDecoration": {0: "ActorIDs"}, + "AddActorVar": {0: "ActorIDs"}, + "AddKeyItem": {0: "ItemIDs"}, + "AddGoalPos": {0: "ActorIDs"}, + "BattleCamTargetActor": {0: "ActorIDs"}, + "BindHandleEvent": {0: "ActorIDs"}, + "BindIdle": {0: "ActorIDs"}, + "BindNextTurn": {0: "ActorIDs"}, + "BindNpcAI": {0: "NpcIDs"}, + "BindNpcDefeat": {0: "NpcIDs"}, + "BindNpcIdle": {0: "NpcIDs"}, + "BindNpcInteract": {0: "NpcIDs"}, + "BindTakeTurn": {0: "ActorIDs"}, + "CheckButtonDown": {0: "Hex", 1: "Bool"}, + "ContinueSpeech": {1: "CustomAnim", 2: "CustomAnim", 4: "CustomMsg"}, + "CopyBuffs": {0: "ActorIDs", 1: "ActorIDs"}, + "CopyStatusEffects": {0: "ActorIDs", 1: "ActorIDs"}, + "CountPlayerTargets": {0: "ActorIDs"}, + "DisablePlayerInput": {0: "Bool"}, + "DisablePlayerPhysics": {0: "Bool"}, + "DispatchDamagePlayerEvent": {1: "Events"}, + "DispatchEvent": {0: "ActorIDs", 1: "Events"}, + "EnableActorBlur": {0: "ActorIDs"}, + "EnableActorGlow": {0: "ActorIDs", 1: "Bool"}, + "EnableIdleScript": {0: "ActorIDs"}, + "EnableNpcShadow": {0: "NpcIDs", 1: "Bool"}, + "EndActorSpeech": {0: "ActorIDs", 2: "CustomAnim", 3: "CustomAnim"}, + "EndSpeech": {1: "CustomAnim", 2: "CustomAnim"}, + "EnemyDamageTarget": { + 0: "ActorIDs", + 1: "HitResults", + 2: "DamageTypes", + 4: "StatusFlags", + 6: "BattleStatusFlags1", + }, + "EnemyTestTarget": { + 0: "ActorIDs", + 1: "HitResults", + 2: "DamageTypes", + 3: "StatusFlags", + 5: "BattleStatusFlags1", + }, + "FallToGoal": {0: "ActorIDs"}, + "FindKeyItem": {0: "ItemIDs"}, + "FlyPartTo": {0: "ActorIDs"}, + "FlyToGoal": {0: "ActorIDs"}, + "ForceHomePos": {0: "ActorIDs"}, + "func_8026DF88": {0: "ActorIDs"}, + "SetActorPaletteEffect": {0: "ActorIDs"}, + "SetActorPaletteSwapParams": {0: "ActorIDs"}, + "func_8026ED20": {0: "ActorIDs"}, + "HideHealthBar": {0: "ActorIDs"}, + "func_8027D434": {0: "ActorIDs"}, + "SetProjectileTargetOffset": {0: "ActorIDs"}, + "GetInstigatorValue": {0: "ActorIDs"}, + "SetNpcFoldParams": {0: "NpcIDs"}, + "SetNpcFoldFlags": {0: "NpcIDs"}, + "UpdatePlayerFold": {0: "PlayerAnims"}, + "GetAnimation": {0: "ActorIDs", 2: "CustomAnim"}, + "GetActorFlags": {0: "ActorIDs", 1: "ActorFlags"}, + "GetActorHP": {0: "ActorIDs"}, + "GetActorPos": {0: "ActorIDs"}, + "GetActorSize": {0: "ActorIDs"}, + "GetActorVar": {0: "ActorIDs"}, + "GetBattleFlags": {0: "BattleStatusFlags1"}, + "GetBattlePhase": {0: "Phases"}, + "GetDistanceToGoal": {0: "ActorIDs"}, + "GetEnemyMaxHP": {0: "ActorIDs"}, + "GetGoalPos": {0: "ActorIDs"}, + "GetHomePos": {0: "ActorIDs"}, + "GetIdleGoal": {0: "ActorIDs"}, + "GetIndexFromHome": {0: "ActorIDs"}, + "GetIndexFromPos": {0: "ActorIDs"}, + "GetItemPower": {0: "ItemIDs"}, + "GetLastDamage": {0: "ActorIDs"}, + "GetLastElement": {0: "DamageTypes"}, + "GetLastEvent": {0: "ActorIDs", 1: "Events"}, + "GetPartOffset": {0: "ActorIDs"}, + "GetPartPos": {0: "ActorIDs"}, + "GetPartRotation": {0: "ActorIDs"}, + "GetNpcPos": {0: "NpcIDs"}, + "GetOriginalActorType": {0: "ActorIDs", 1: "ActorType"}, + "GetStatusFlags": {0: "ActorIDs", 1: "StatusFlags"}, + "HidePlayerShadow": {0: "Bool"}, + "HPBarToCurrent": {0: "ActorIDs"}, + "HPBarToHome": {0: "ActorIDs"}, + "IdleFlyToGoal": {0: "ActorIDs"}, + "IdleJumpToGoal": {0: "ActorIDs"}, + "IdleRunToGoal": {0: "ActorIDs"}, + "InterpNpcYaw": {0: "NpcIDs"}, + "JumpPartTo": {0: "ActorIDs"}, + "JumpToGoal": {0: "ActorIDs", 2: "Bool", 3: "Bool", 4: "Bool"}, + "JumpWithBounce": {0: "ActorIDs"}, + "LandJump": {0: "ActorIDs"}, + "LoadActionCommand": {0: "ActionCommand"}, + "MakeEntity": {0: "Hex"}, + "MakeItemEntity": {0: "ItemIDs"}, + "ModifyColliderFlags": {2: "Hex"}, + "NpcFaceNpc": {0: "NpcIDs", 1: "NpcIDs"}, + "NpcFacePlayer": {0: "NpcIDs"}, + "NpcJump0": {0: "NpcIDs"}, + "NpcJump1": {0: "NpcIDs"}, + "NpcMoveTo": {0: "NpcIDs"}, + "PlayAmbientSounds": {0: "AmbientSounds"}, + "PlayEffect": {0: "EffectID"}, + "PlayLoopingSoundAtActor": {0: "ActorIDs", 2: "SoundIDs"}, + "PlaySound": {0: "SoundIDs"}, + "PlaySoundAt": {0: "SoundIDs"}, + "PlaySoundAtActor": {0: "ActorIDs", 1: "SoundIDs"}, + "PlaySoundAtModel": {1: "SoundIDs"}, + "PlaySoundAtNpc": {0: "NpcIDs", 1: "SoundIDs"}, + "PlaySoundAtPart": {0: "ActorIDs", 2: "SoundIDs"}, + "RemoveActor": {0: "ActorIDs"}, + "RemoveActorDecoration": {0: "ActorIDs"}, + "RemoveNpc": {0: "NpcIDs"}, + "ResetActorSounds": {0: "ActorIDs"}, + "ResetAllActorSounds": {0: "ActorIDs"}, + "RunToGoal": {0: "ActorIDs", 2: "Bool"}, + "SetActorDispOffset": {0: "ActorIDs"}, + "SetActorFlagBits": {0: "ActorIDs", 1: "ActorFlags"}, + "SetActorIdleSpeed": {0: "ActorIDs"}, + "SetActorIdleJumpGravity": {0: "ActorIDs"}, + "SetActorJumpGravity": {0: "ActorIDs"}, + "SetActorPos": {0: "ActorIDs"}, + "SetActorRotation": {0: "ActorIDs"}, + "SetActorRotationOffset": {0: "ActorIDs"}, + "SetActorScale": {0: "ActorIDs"}, + "SetActorSize": {0: "ActorIDs"}, + "SetActorSounds": {0: "ActorIDs"}, + "SetActorSpeed": {0: "ActorIDs"}, + "SetActorType": {0: "ActorIDs", 1: "ActorType"}, + "SetActorVar": {0: "ActorIDs"}, + "SetActorYaw": {0: "ActorIDs"}, + "SetAnimation": {0: "ActorIDs", 2: "CustomAnim"}, + "SetAnimationRate": {0: "ActorIDs"}, + "SetBattleFlagBits": {0: "BattleStatusFlags1"}, + "SetBattleFlagBits2": {0: "BattleStatusFlags2"}, + "SetDefenseTable": {0: "ActorIDs"}, + "SetEnemyHP": {0: "ActorIDs"}, + "SetEnemyTargetOffset": {0: "ActorIDs"}, + "SetGoalPos": {0: "ActorIDs"}, + "SetGoalToFirstTarget": {0: "ActorIDs"}, + "SetGoalToHome": {0: "ActorIDs"}, + "SetGoalToIndex": {0: "ActorIDs"}, + "SetGoalToTarget": {0: "ActorIDs"}, + "SetHomePos": {0: "ActorIDs"}, + "SetIdleAnimations": {0: "ActorIDs"}, + "SetIdleGoal": {0: "ActorIDs"}, + "SetIdleGoalToHome": {0: "ActorIDs"}, + "SetJumpAnimations": { + 0: "ActorIDs", + 2: "PlayerAnims", + 3: "PlayerAnims", + 4: "PlayerAnims", + }, + "SetMusicTrack": {1: "SongIDs"}, + "SetNpcAnimation": {0: "NpcIDs", 1: "CustomAnim"}, + "SetNpcAux": {0: "NpcIDs"}, + "SetNpcFlagBits": {0: "NpcIDs", 1: "NpcFlags", 2: "Bool"}, + "SetNpcJumpscale": {0: "NpcIDs"}, + "SetNpcPos": {0: "NpcIDs"}, + "SetNpcRotation": {0: "NpcIDs"}, + "SetNpcScale": {0: "NpcIDs"}, + "SetNpcSpeed": {0: "NpcIDs"}, + "SetNpcSprite": {1: "Hex"}, + "SetNpcYaw": {0: "NpcIDs"}, + "SetOwnerID": {0: "ActorIDs"}, + "SetPartAlpha": {0: "ActorIDs"}, + "SetPartDispOffset": {0: "ActorIDs"}, + "SetPartEventBits": {0: "ActorIDs", 2: "ActorEventFlags"}, + "SetPartFlags": {0: "ActorIDs", 2: "ActorPartFlags"}, + "SetPartFlagBits": {0: "ActorIDs", 2: "ActorPartFlags"}, + "SetPartJumpGravity": {0: "ActorIDs"}, + "SetPartMoveSpeed": {0: "ActorIDs"}, + "SetPartPos": {0: "ActorIDs"}, + "SetPartRotation": {0: "ActorIDs"}, + "SetPartRotationOffset": {0: "ActorIDs"}, + "SetPartScale": {0: "ActorIDs"}, + "SetPartSize": {0: "ActorIDs"}, + "SetPartSounds": {0: "ActorIDs"}, + "SetPartTargetFlagBits": {0: "ActorIDs"}, + "SetPartYaw": {0: "ActorIDs"}, + "SetPlayerAnimation": {0: "PlayerAnims"}, + "SetSelfEnemyFlagBits": {0: "NpcFlags", 1: "Bool"}, + # "SetSelfVar" :{1:"Bool"}, # apparently this was a bool in some scripts but it passes non-0/1 values, including negatives + "SetSpriteShading": {0: "CustomAnim"}, + "SetStatusTable": {0: "ActorIDs"}, + "SetTargetActor": {0: "ActorIDs", 1: "ActorIDs"}, + "SetTargetOffset": {0: "ActorIDs"}, + "ShowChoice": {0: "CustomMsg"}, + "ShowEmote": {1: "Emotes"}, + "ShowMessageAtScreenPos": {0: "CustomMsg"}, + "ShowMessageAtWorldPos": {0: "CustomMsg"}, + "SpeakToPlayer": {0: "NpcIDs", 1: "CustomAnim", 2: "CustomAnim", -1: "CustomMsg"}, + "StopLoopingSoundAtActor": {0: "ActorIDs"}, + "SwitchMessage": {0: "CustomMsg"}, + "UseBattleCamPreset": {0: "BtlCameraPreset"}, + "UseIdleAnimation": {0: "ActorIDs", 1: "Bool"}, + "WasStatusInflicted": {0: "ActorIDs", 1: "Bool"}, } + def trim_lw(arg): - return arg[arg.find("(")+1:arg.find(")")] + return arg[arg.find("(") + 1 : arg.find(")")] def replace_constants(self, func, args): @@ -584,23 +623,36 @@ def replace_constants(self, func, args): return args + def fix_1_arg(self, lw, arg, format): if lw: lw = int(trim_lw(lw)) if LOCAL_WORDS[lw]: - info = {0 : LOCAL_WORDS[lw]} + info = {0: LOCAL_WORDS[lw]} args = f"{arg}" return fix_args(self, 0, args, info) if format: - info = {0 : format} + info = {0: format} args = f"{arg}" return fix_args(self, 0, args, info) return arg + class ScriptDisassembler: - def __init__(self, bytes, script_name = "script", symbol_map = {}, romstart = 0, INCLUDES_NEEDED = {"forward": [], "sprites": set(), "npcs": []}, INCLUDED = {"functions": set(), "includes": set()}, prelude = True, transform_symbol_name=None, use_script_lib=True): + def __init__( + self, + bytes, + script_name="script", + symbol_map={}, + romstart=0, + INCLUDES_NEEDED={"forward": [], "sprites": set(), "npcs": []}, + INCLUDED={"functions": set(), "includes": set()}, + prelude=True, + transform_symbol_name=None, + use_script_lib=True, + ): self.bytes = bytes self.script_name = script_name self.prelude = prelude @@ -632,7 +684,7 @@ class ScriptDisassembler: opcode = self.read_word() argc = self.read_word() - #print(f"Op {opcode:X}, argc {argc}") + # print(f"Op {opcode:X}, argc {argc}") if opcode > 0xFF or argc > 0xFF: raise Exception(f"script '{self.script_name}' is malformed (opcode {opcode:X}, argc {argc:X})") @@ -641,7 +693,7 @@ class ScriptDisassembler: for i in range(0, argc): argv.append(self.read_word()) - #print(argv) + # print(argv) self.disassemble_command(opcode, argc, argv) @@ -652,8 +704,10 @@ class ScriptDisassembler: return self.prefix + self.out def write(self, line): - if self.indent < 0: self.indent = 0 - if self.indent > 1: self.indent_used = True + if self.indent < 0: + self.indent = 0 + if self.indent > 1: + self.indent_used = True self.out += " " * self.indent self.out += line @@ -666,24 +720,35 @@ class ScriptDisassembler: self.prefix += line self.prefix += "\n" - def var(self, arg, prefer_hex = False, use_evt_ptr = True): + def var(self, arg, prefer_hex=False, use_evt_ptr=True): if arg in self.symbol_map: s = self.symbol_map[arg][0][1] return f"EVT_PTR({s})" if use_evt_ptr else s - v = arg - 2**32 # convert to s32 + v = arg - 2**32 # convert to s32 if v > -250000000: - if v <= -220000000: return f"EVT_FLOAT({round_fixed((v + 230000000) / 1024)})" - elif v <= -200000000: return f"ArrayFlag({v + 210000000})" - elif v <= -180000000: return f"ArrayVar({v + 190000000})" - elif v <= -160000000: return f"GameByte({v + 170000000})" - elif v <= -140000000: return f"AreaByte({v + 150000000})" - elif v <= -120000000: return f"GameFlag({v + 130000000})" - elif v <= -100000000: return f"AreaFlag({v + 110000000})" - elif v <= -80000000: return f"MapFlag({v + 90000000})" - elif v <= -60000000: return f"LocalFlag({v + 70000000})" - elif v <= -40000000: return f"MapVar({v + 50000000})" - elif v <= -20000000: return f"LocalVar({v + 30000000})" + if v <= -220000000: + return f"EVT_FLOAT({round_fixed((v + 230000000) / 1024)})" + elif v <= -200000000: + return f"ArrayFlag({v + 210000000})" + elif v <= -180000000: + return f"ArrayVar({v + 190000000})" + elif v <= -160000000: + return f"GameByte({v + 170000000})" + elif v <= -140000000: + return f"AreaByte({v + 150000000})" + elif v <= -120000000: + return f"GameFlag({v + 130000000})" + elif v <= -100000000: + return f"AreaFlag({v + 110000000})" + elif v <= -80000000: + return f"MapFlag({v + 90000000})" + elif v <= -60000000: + return f"LocalFlag({v + 70000000})" + elif v <= -40000000: + return f"MapVar({v + 50000000})" + elif v <= -20000000: + return f"LocalVar({v + 30000000})" if arg == 0xFFFFFFFF: return "-1" @@ -697,7 +762,7 @@ class ScriptDisassembler: return f"{arg}" def replace_star_rod_function_name(self, name): - vram = int(name.split("_",1)[1], 16) + vram = int(name.split("_", 1)[1], 16) name = "N(" + name.replace("function", "func") + f"_{(vram - 0x80240000)+self.romstart:X}" + ")" return name @@ -737,7 +802,7 @@ class ScriptDisassembler: self.INCLUDES_NEEDED["forward"].append(prefix + name + suffix + ";") self.INCLUDED["functions"].add(name) return name - elif not isArg or name.startswith("\""): + elif not isArg or name.startswith('"'): return name else: return str(addr) @@ -746,21 +811,33 @@ class ScriptDisassembler: def addr_ref(self, addr, isArg=False): if addr in self.symbol_map: return self.replace_star_rod_prefix(addr, isArg) - return self.var(addr) #f"0x{addr:08X}" + return self.var(addr) # f"0x{addr:08X}" def trigger(self, trigger): - if trigger == 0x00000040: trigger = "TRIGGER_WALL_PUSH" - if trigger == 0x00000080: trigger = "TRIGGER_FLOOR_TOUCH" - if trigger == 0x00000100: trigger = "TRIGGER_WALL_PRESS_A" - if trigger == 0x00000200: trigger = "TRIGGER_FLOOR_JUMP" - if trigger == 0x00000400: trigger = "TRIGGER_WALL_TOUCH" - if trigger == 0x00000800: trigger = "TRIGGER_FLOOR_PRESS_A" - if trigger == 0x00001000: trigger = "TRIGGER_WALL_HAMMER" - if trigger == 0x00010000: trigger = "TRIGGER_GAME_FLAG_SET" - if trigger == 0x00020000: trigger = "TRIGGER_AREA_FLAG_SET" - if trigger == 0x00040000: trigger = "TRIGGER_CEILING_TOUCH" - if trigger == 0x00080000: trigger = "TRIGGER_FLOOR_ABOVE" - if trigger == 0x00100000: trigger = "TRIGGER_POINT_BOMB" + if trigger == 0x00000040: + trigger = "TRIGGER_WALL_PUSH" + if trigger == 0x00000080: + trigger = "TRIGGER_FLOOR_TOUCH" + if trigger == 0x00000100: + trigger = "TRIGGER_WALL_PRESS_A" + if trigger == 0x00000200: + trigger = "TRIGGER_FLOOR_JUMP" + if trigger == 0x00000400: + trigger = "TRIGGER_WALL_TOUCH" + if trigger == 0x00000800: + trigger = "TRIGGER_FLOOR_PRESS_A" + if trigger == 0x00001000: + trigger = "TRIGGER_WALL_HAMMER" + if trigger == 0x00010000: + trigger = "TRIGGER_GAME_FLAG_SET" + if trigger == 0x00020000: + trigger = "TRIGGER_AREA_FLAG_SET" + if trigger == 0x00040000: + trigger = "TRIGGER_CEILING_TOUCH" + if trigger == 0x00080000: + trigger = "TRIGGER_FLOOR_ABOVE" + if trigger == 0x00100000: + trigger = "TRIGGER_POINT_BOMB" return f"0x{trigger:X}" if type(trigger) is int else trigger def read_word(self): @@ -773,24 +850,32 @@ class ScriptDisassembler: if self.prelude: try: - self.prefix_line(f"EvtScript D_{self.script_name - info[0] + info[2]:08X}_{self.script_name:06X} = {{") + self.prefix_line( + f"EvtScript D_{self.script_name - info[0] + info[2]:08X}_{self.script_name:06X} = {{" + ) self.write_line("};") except: self.prefix_line(f"EvtScript {self.script_name} = {{") self.write_line("};") self.done = True - elif opcode == 0x02: self.write_line(f"EVT_RETURN") - elif opcode == 0x03: self.write_line(f"EVT_LABEL({self.var(argv[0])})") - elif opcode == 0x04: self.write_line(f"EVT_GOTO({self.var(argv[0])})") + elif opcode == 0x02: + self.write_line(f"EVT_RETURN") + elif opcode == 0x03: + self.write_line(f"EVT_LABEL({self.var(argv[0])})") + elif opcode == 0x04: + self.write_line(f"EVT_GOTO({self.var(argv[0])})") elif opcode == 0x05: self.write_line(f"EVT_LOOP({self.var(argv[0])})") self.indent += 1 elif opcode == 0x06: self.indent -= 1 self.write_line("EVT_END_LOOP") - elif opcode == 0x07: self.write_line(f"EVT_BREAK_LOOP") - elif opcode == 0x08: self.write_line(f"EVT_WAIT({self.var(argv[0])})") - elif opcode == 0x09: self.write_line(f"EVT_WAIT_SECS({self.var(argv[0])})") + elif opcode == 0x07: + self.write_line(f"EVT_BREAK_LOOP") + elif opcode == 0x08: + self.write_line(f"EVT_WAIT({self.var(argv[0])})") + elif opcode == 0x09: + self.write_line(f"EVT_WAIT_SECS({self.var(argv[0])})") elif opcode == 0x0A: if self.var(argv[0]).startswith("LW"): args_str = fix_1_arg(self, self.var(argv[0]), self.var(argv[1]), 0) @@ -893,7 +978,8 @@ class ScriptDisassembler: self.indent -= 1 self.write_line(f"EVT_CASE_RANGE({self.var(argv[0])}, {self.var(argv[1])})") self.indent += 1 - elif opcode == 0x22: self.write_line(f"EVT_BREAK_SWITCH") + elif opcode == 0x22: + self.write_line(f"EVT_BREAK_SWITCH") elif opcode == 0x23: self.indent -= 2 del SWITCH_TYPES[-1] @@ -902,17 +988,17 @@ class ScriptDisassembler: if self.var(argv[0]).startswith("LW"): new_arg = trim_lw(self.var(argv[0])) if self.var(argv[1]).startswith("LW"): - from_arg = trim_lw(self.var(argv[1])) # Carry type info of LW if being set from an LW + from_arg = trim_lw(self.var(argv[1])) # Carry type info of LW if being set from an LW else: - from_arg = 0 # If a constant, we no longer know the type + from_arg = 0 # If a constant, we no longer know the type LOCAL_WORDS[int(new_arg)] = LOCAL_WORDS[int(from_arg)] self.write_line(f"EVT_SET({self.var(argv[0])}, {self.var(argv[1])})") elif opcode == 0x25: argNum = argv[1] - sprite = (argNum & 0xFF0000) >> 16 - palette = (argNum & 0xFF00) >> 8 - anim = (argNum & 0xFF) >> 0 + sprite = (argNum & 0xFF0000) >> 16 + palette = (argNum & 0xFF00) >> 8 + anim = (argNum & 0xFF) >> 0 if sprite > 0: value = make_anim_macro(self, sprite, palette, anim) elif argNum > 100: @@ -920,41 +1006,60 @@ class ScriptDisassembler: else: value = f"{argNum}" self.write_line(f"EVT_SET_CONST({self.var(argv[0])}, {value})") - elif opcode == 0x26: self.write_line(f"EVT_SETF({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x27: self.write_line(f"EVT_ADD({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x28: self.write_line(f"EVT_SUB({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x29: self.write_line(f"EVT_MUL({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x2A: self.write_line(f"EVT_DIV({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x2B: self.write_line(f"EVT_MOD({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x2C: self.write_line(f"EVT_ADDF({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x2D: self.write_line(f"EVT_SUBF({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x2E: self.write_line(f"EVT_MULF({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x2F: self.write_line(f"EVT_DIVF({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x30: self.write_line(f"EVT_USE_BUF({self.var(argv[0])})") + elif opcode == 0x26: + self.write_line(f"EVT_SETF({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x27: + self.write_line(f"EVT_ADD({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x28: + self.write_line(f"EVT_SUB({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x29: + self.write_line(f"EVT_MUL({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x2A: + self.write_line(f"EVT_DIV({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x2B: + self.write_line(f"EVT_MOD({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x2C: + self.write_line(f"EVT_ADDF({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x2D: + self.write_line(f"EVT_SUBF({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x2E: + self.write_line(f"EVT_MULF({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x2F: + self.write_line(f"EVT_DIVF({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x30: + self.write_line(f"EVT_USE_BUF({self.var(argv[0])})") elif opcode == 0x31 or opcode == 0x32 or opcode == 0x33 or opcode == 0x34: args = [*map(self.var, argv)] self.write_line(f"EVT_BUF_READ{opcode - 0x30}({', '.join(args)})") elif opcode == 0x35: args = [*map(self.var, argv)] self.write_line(f"EVT_BUF_PEEK({', '.join(args)})") - elif opcode == 0x36: self.write_line(f"EVT_USE_FBUF({self.var(argv[0])})") + elif opcode == 0x36: + self.write_line(f"EVT_USE_FBUF({self.var(argv[0])})") elif opcode == 0x37 or opcode == 0x38 or opcode == 0x39 or opcode == 0x3A: args = [*map(self.var, argv)] self.write_line(f"EVT_FBUF_READ{opcode - 0x36}({', '.join(args)})") elif opcode == 0x3B: args = [*map(self.var, argv)] self.write_line(f"EVT_FBUF_PEEK({', '.join(args)})") - elif opcode == 0x3C: self.write_line(f"EVT_USE_ARRAY({self.var(argv[0])})") - elif opcode == 0x3D: self.write_line(f"EVT_USE_FLAG_ARRAY({self.var(argv[0])})") - elif opcode == 0x3E: self.write_line(f"EVT_MALLOC_ARRAY({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x3F: self.write_line(f"EVT_BITWISE_AND({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x40: self.write_line(f"EVT_BITWISE_AND_CONST({self.var(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x41: self.write_line(f"EVT_BITWISE_OR({self.var(argv[0])}, 0x{argv[1]:X})") - elif opcode == 0x42: self.write_line(f"EVT_BITWISE_OR_CONST({self.var(argv[0])}, 0x{argv[1]:X})") + elif opcode == 0x3C: + self.write_line(f"EVT_USE_ARRAY({self.var(argv[0])})") + elif opcode == 0x3D: + self.write_line(f"EVT_USE_FLAG_ARRAY({self.var(argv[0])})") + elif opcode == 0x3E: + self.write_line(f"EVT_MALLOC_ARRAY({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x3F: + self.write_line(f"EVT_BITWISE_AND({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x40: + self.write_line(f"EVT_BITWISE_AND_CONST({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x41: + self.write_line(f"EVT_BITWISE_OR({self.var(argv[0])}, 0x{argv[1]:X})") + elif opcode == 0x42: + self.write_line(f"EVT_BITWISE_OR_CONST({self.var(argv[0])}, 0x{argv[1]:X})") elif opcode == 0x43: func = self.addr_ref(argv[0]) args = [self.var(a, use_evt_ptr=True) for a in argv[1:]] - args_str = ', '.join(args) + args_str = ", ".join(args) args_str = replace_constants(self, func, args_str) if func.startswith("evt_"): # use func-specific macro @@ -963,34 +1068,60 @@ class ScriptDisassembler: # or ar not migrated, we have to create a placeholder elif func == "GotoMap" or func == "GotoMapSpecial": args = [self.var(a, use_evt_ptr=True) for a in argv[2:]] - args_str = ', '.join(args) + args_str = ", ".join(args) self.write_line(f"EVT_CALL({func}, EVT_PTR(UNK_STR_{argv[1]:X}), {args_str})") elif args_str: self.write_line(f"EVT_CALL({func}, {args_str})") else: - self.write_line(f"EVT_CALL({func})") # no args - elif opcode == 0x44: self.write_line(f"EVT_EXEC({self.addr_ref(argv[0])})") - elif opcode == 0x45: self.write_line(f"EVT_EXEC_GET_TID({self.addr_ref(argv[0])}, {self.var(argv[1])})") - elif opcode == 0x46: self.write_line(f"EVT_EXEC_WAIT({self.addr_ref(argv[0])})") + self.write_line(f"EVT_CALL({func})") # no args + elif opcode == 0x44: + self.write_line(f"EVT_EXEC({self.addr_ref(argv[0])})") + elif opcode == 0x45: + self.write_line(f"EVT_EXEC_GET_TID({self.addr_ref(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x46: + self.write_line(f"EVT_EXEC_WAIT({self.addr_ref(argv[0])})") elif opcode == 0x47: - args = [self.addr_ref(argv[0]), self.trigger(argv[1]), self.collider_id(argv[2]), *map(self.var, argv[3:])] + args = [ + self.addr_ref(argv[0]), + self.trigger(argv[1]), + self.collider_id(argv[2]), + *map(self.var, argv[3:]), + ] self.write_line(f"EVT_BIND_TRIGGER({', '.join(args)})") - elif opcode == 0x48: self.write_line(f"EVT_UNBIND") - elif opcode == 0x49: self.write_line(f"EVT_KILL_THREAD({self.var(argv[0])})") - elif opcode == 0x4A: self.write_line(f"EVT_JUMP({self.var(argv[0])})") - elif opcode == 0x4B: self.write_line(f"EVT_SET_PRIORITY({self.var(argv[0])})") - elif opcode == 0x4C: self.write_line(f"EVT_SET_TIMESCALE({self.var(argv[0])})") - elif opcode == 0x4D: self.write_line(f"EVT_SET_GROUP({self.var(argv[0])})") + elif opcode == 0x48: + self.write_line(f"EVT_UNBIND") + elif opcode == 0x49: + self.write_line(f"EVT_KILL_THREAD({self.var(argv[0])})") + elif opcode == 0x4A: + self.write_line(f"EVT_JUMP({self.var(argv[0])})") + elif opcode == 0x4B: + self.write_line(f"EVT_SET_PRIORITY({self.var(argv[0])})") + elif opcode == 0x4C: + self.write_line(f"EVT_SET_TIMESCALE({self.var(argv[0])})") + elif opcode == 0x4D: + self.write_line(f"EVT_SET_GROUP({self.var(argv[0])})") elif opcode == 0x4E: - args = [self.addr_ref(argv[0]), self.trigger(argv[1]), self.collider_id(argv[2]), *map(self.var, argv[3:])] + args = [ + self.addr_ref(argv[0]), + self.trigger(argv[1]), + self.collider_id(argv[2]), + *map(self.var, argv[3:]), + ] self.write_line(f"EVT_BIND_PADLOCK({', '.join(args)})") - elif opcode == 0x4F: self.write_line(f"EVT_SUSPEND_GROUP({self.var(argv[0])})") - elif opcode == 0x50: self.write_line(f"EVT_RESUME_GROUP({self.var(argv[0])})") - elif opcode == 0x51: self.write_line(f"EVT_SUSPEND_OTHERS({self.var(argv[0])})") - elif opcode == 0x52: self.write_line(f"EVT_RESUME_OTHERS({self.var(argv[0])})") - elif opcode == 0x53: self.write_line(f"EVT_SUSPEND_THREAD({self.var(argv[0])})") - elif opcode == 0x54: self.write_line(f"EVT_RESUME_THREAD({self.var(argv[0])})") - elif opcode == 0x55: self.write_line(f"EVT_IS_THREAD_RUNNING({self.var(argv[0])}, {self.var(argv[1])})") + elif opcode == 0x4F: + self.write_line(f"EVT_SUSPEND_GROUP({self.var(argv[0])})") + elif opcode == 0x50: + self.write_line(f"EVT_RESUME_GROUP({self.var(argv[0])})") + elif opcode == 0x51: + self.write_line(f"EVT_SUSPEND_OTHERS({self.var(argv[0])})") + elif opcode == 0x52: + self.write_line(f"EVT_RESUME_OTHERS({self.var(argv[0])})") + elif opcode == 0x53: + self.write_line(f"EVT_SUSPEND_THREAD({self.var(argv[0])})") + elif opcode == 0x54: + self.write_line(f"EVT_RESUME_THREAD({self.var(argv[0])})") + elif opcode == 0x55: + self.write_line(f"EVT_IS_THREAD_RUNNING({self.var(argv[0])}, {self.var(argv[1])})") elif opcode == 0x56: self.write_line("EVT_THREAD") self.indent += 1 @@ -1010,22 +1141,62 @@ class ScriptDisassembler: argv_str += ", " argv_str += f"0x{arg:X}" self.write_line(f"0x{opcode:02X}{argv_str}),") + def collider_id(self, arg): if arg >= 0x4000 and arg <= 0x5000: return f"EVT_ENTITY_INDEX({arg - 0x4000})" else: return self.var(arg) + + class UnsupportedScript(Exception): pass + + if __name__ == "__main__": import argparse + parser = argparse.ArgumentParser() parser.add_argument("file", type=str, help="File to dissassemble from") parser.add_argument("offset", help="Offset to start dissassembling from") - parser.add_argument("-end", "-e", "--e", type=lambda x: int(x, 16), default=0, dest="end", required=False, help="End offset to stop dissassembling from.\nOnly used as a way to find valid scripts.") - parser.add_argument("-vram", "-v", "--v", type=lambda x: int(x, 16), default=0, dest="vram", required=False, help="VRAM start will be tracked and used for the script output name") - parser.add_argument("-si", "--si", action="store_true", default=False, dest="si", required=False, help="Force si script output") - parser.add_argument("-blob", "--b", action="store_true", default=False, dest="blob", required=False, help="If there is a blob of scripts.") + parser.add_argument( + "-end", + "-e", + "--e", + type=lambda x: int(x, 16), + default=0, + dest="end", + required=False, + help="End offset to stop dissassembling from.\nOnly used as a way to find valid scripts.", + ) + parser.add_argument( + "-vram", + "-v", + "--v", + type=lambda x: int(x, 16), + default=0, + dest="vram", + required=False, + help="VRAM start will be tracked and used for the script output name", + ) + parser.add_argument( + "-si", + "--si", + action="store_true", + default=False, + dest="si", + required=False, + help="Force si script output", + ) + parser.add_argument( + "-blob", + "--b", + action="store_true", + default=False, + dest="blob", + required=False, + help="If there is a blob of scripts.", + ) args = parser.parse_args() vram_base = args.vram get_constants() @@ -1057,25 +1228,35 @@ if __name__ == "__main__": script_text = script.disassemble() if script.instructions > 1 and "_EVT_CMD" not in script_text: if gap and first_print: - potential_struct_sizes = { "NpcData": 0x1F0, "MobileAISettings":0x30, "NpcSettings":0x2C, "NpcGroupList":0xC } + potential_struct_sizes = { + "NpcData": 0x1F0, + "MobileAISettings": 0x30, + "NpcSettings": 0x2C, + "NpcGroupList": 0xC, + } gap_size = offset - gap_start potential_struct = "Unknown data" potential_count = 1 - for k,v in potential_struct_sizes.items(): + for k, v in potential_struct_sizes.items(): if gap_size % v == 0: potential_struct = k potential_count = gap_size // v - print(f"========== 0x{gap_size:X} byte gap ({potential_count} {potential_struct}?) 0x{gap_start:X} - 0x{offset:X} ==========") + print( + f"========== 0x{gap_size:X} byte gap ({potential_count} {potential_struct}?) 0x{gap_start:X} - 0x{offset:X} ==========" + ) print() gap = False - #print(f"EvtScript read from 0x{script.start_pos:X} to 0x{script.end_pos:X} " + # print(f"EvtScript read from 0x{script.start_pos:X} to 0x{script.end_pos:X} " # f"(0x{script.end_pos - script.start_pos:X} bytes, {script.instructions} instructions)") - #print() + # print() vram = f"{args.vram:X}_" if vram_base > 0 else f"" - script_text = script_text.replace("EvtScript script = SCRIPT({", f"EvtScript N(D_{vram}{offset:X}) = " + "SCRIPT({") + script_text = script_text.replace( + "EvtScript script = SCRIPT({", + f"EvtScript N(D_{vram}{offset:X}) = " + "SCRIPT({", + ) print(script_text, end="") print() - #print(f"Valid script found at 0x{offset:X}") + # print(f"Valid script found at 0x{offset:X}") args.vram += script.end_pos - offset offset = script.end_pos first_print = True @@ -1093,7 +1274,6 @@ if __name__ == "__main__": args.vram += 4 else: with open(args.file, "rb") as f: - f.seek(offset) loffset = args.offset looping = 1 @@ -1102,7 +1282,10 @@ if __name__ == "__main__": script = ScriptDisassembler(f, loffset, {}, 0x978DE0, INCLUDES_NEEDED, INCLUDED) if args.si: - print(ScriptDisassembler(f, loffset, {}, 0x978DE0, INCLUDES_NEEDED, INCLUDED).disassemble(), end="") + print( + ScriptDisassembler(f, loffset, {}, 0x978DE0, INCLUDES_NEEDED, INCLUDED).disassemble(), + end="", + ) else: try: script_text = script.disassemble() @@ -1120,7 +1303,7 @@ if __name__ == "__main__": print(e) loffset = script.end_pos - LOCAL_WORDS = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + LOCAL_WORDS = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] looping = args.blob try: loffset = _script_lib[loffset - info[0] + info[2]][0][1] diff --git a/tools/find_duplicates.py b/tools/find_duplicates.py index 862ea1f661..46d75f98f0 100755 --- a/tools/find_duplicates.py +++ b/tools/find_duplicates.py @@ -59,7 +59,7 @@ def get_symbol_bytes(offsets, func): for ins in insns: ret.append(ins >> 2) - return bytes(ret).decode('utf-8'), bs + return bytes(ret).decode("utf-8"), bs def parse_map(fname): @@ -80,12 +80,7 @@ def parse_map(fname): continue prev_line = line - if ( - ram_offset is None - or "=" in line - or "*fill*" in line - or " 0x" not in line - ): + if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line: continue ram = int(line[16 : 16 + 18], 0) rom = ram - ram_offset @@ -177,7 +172,7 @@ def do_query(query): break match_str = "{:.3f} - {}".format(matches[match], match) if match not in s_files: - match_str += " (decompiled)" + match_str += " (decompiled)" print(match_str) i += 1 print() @@ -203,7 +198,10 @@ def all_matches(all_funcs_flag): file = to_match_files[0] i += 1 - print("File matching progress: {:%}".format(i / (len(s_files) - iter_limit)), end='\r') + print( + "File matching progress: {:%}".format(i / (len(s_files) - iter_limit)), + end="\r", + ) if get_symbol_length(file) < 16: to_match_files.remove(file) @@ -241,18 +239,26 @@ def all_matches(all_funcs_flag): output_match_dict(match_dict, num_decomped_dupes, num_undecomped_dupes, num_perfect_dupes, i) -def output_match_dict(match_dict, num_decomped_dupes, num_undecomped_dupes, num_perfect_dupes, num_checked_files): - out_file = open(datetime.today().strftime('%Y-%m-%d-%H-%M-%S') + "_all_matches.txt", "w+") +def output_match_dict( + match_dict, + num_decomped_dupes, + num_undecomped_dupes, + num_perfect_dupes, + num_checked_files, +): + out_file = open(datetime.today().strftime("%Y-%m-%d-%H-%M-%S") + "_all_matches.txt", "w+") - out_file.write("Number of s-files: " + str(len(s_files)) + "\n" - "Number of checked s-files: " + str(round(num_checked_files)) + "\n" - "Number of decompiled duplicates found: " + str(num_decomped_dupes) + "\n" - "Number of undecompiled duplicates found: " + str(num_undecomped_dupes) + "\n" - "Number of overall exact duplicates found: " + str(num_perfect_dupes) + "\n\n") + out_file.write( + "Number of s-files: " + str(len(s_files)) + "\n" + "Number of checked s-files: " + str(round(num_checked_files)) + "\n" + "Number of decompiled duplicates found: " + str(num_decomped_dupes) + "\n" + "Number of undecompiled duplicates found: " + str(num_undecomped_dupes) + "\n" + "Number of overall exact duplicates found: " + str(num_perfect_dupes) + "\n\n" + ) sorted_dict = OrderedDict(sorted(match_dict.items(), key=lambda item: item[1][0], reverse=True)) - print("Creating output file: " + out_file.name, end='\n') + print("Creating output file: " + out_file.name, end="\n") for file_name, matches in sorted_dict.items(): out_file.write(file_name + " - found " + str(matches[0]) + " matches total:\n") for match in matches[1]: @@ -261,19 +267,23 @@ def output_match_dict(match_dict, num_decomped_dupes, num_undecomped_dupes, num_ out_file.close() + def is_decompiled(sym): return sym not in s_files + def do_cross_query(): ccount = Counter() clusters = [] sym_bytes = {} for sym_name in map_syms: - if not sym_name.startswith("D_") and \ - not sym_name.startswith("_binary") and \ - not sym_name.startswith("jtbl_") and \ - not re.match(r"L[0-9A-F]{8}_[0-9A-F]{5,6}", sym_name): + if ( + not sym_name.startswith("D_") + and not sym_name.startswith("_binary") + and not sym_name.startswith("jtbl_") + and not re.match(r"L[0-9A-F]{8}_[0-9A-F]{5,6}", sym_name) + ): if get_symbol_length(sym_name) > 16: sym_bytes[sym_name] = get_symbol_bytes(map_offsets, sym_name) @@ -303,14 +313,48 @@ def do_cross_query(): print(ccount.most_common(100)) -parser = argparse.ArgumentParser(description="Tool to find duplicates for a specific function or to find all duplicates across the codebase.") +parser = argparse.ArgumentParser( + description="Tool to find duplicates for a specific function or to find all duplicates across the codebase." +) group = parser.add_mutually_exclusive_group() -group.add_argument("-a", "--all", help="find ALL duplicates and output them into a file", action='store_true', required=False) -group.add_argument("-c", "--cross", help="do a cross query over the codebase", action='store_true', required=False) -group.add_argument("-s", "--short", help="find MOST duplicates besides some very small duplicates. Cuts the runtime in half with minimal loss", action='store_true', required=False) -parser.add_argument("query", help="function or file", nargs='?', default=None) -parser.add_argument("-t", "--threshold", help="score threshold between 0 and 1 (higher is more restrictive)", type=float, default=0.9, required=False) -parser.add_argument("-n", "--num-out", help="number of functions to display", type=int, default=100, required=False) +group.add_argument( + "-a", + "--all", + help="find ALL duplicates and output them into a file", + action="store_true", + required=False, +) +group.add_argument( + "-c", + "--cross", + help="do a cross query over the codebase", + action="store_true", + required=False, +) +group.add_argument( + "-s", + "--short", + help="find MOST duplicates besides some very small duplicates. Cuts the runtime in half with minimal loss", + action="store_true", + required=False, +) +parser.add_argument("query", help="function or file", nargs="?", default=None) +parser.add_argument( + "-t", + "--threshold", + help="score threshold between 0 and 1 (higher is more restrictive)", + type=float, + default=0.9, + required=False, +) +parser.add_argument( + "-n", + "--num-out", + help="number of functions to display", + type=int, + default=100, + required=False, +) args = parser.parse_args() diff --git a/tools/find_similar_areas.py b/tools/find_similar_areas.py index 303b8bc661..09125e68b0 100755 --- a/tools/find_similar_areas.py +++ b/tools/find_similar_areas.py @@ -60,14 +60,10 @@ def get_all_unmatched_functions(): def get_func_sizes() -> Dict[str, int]: try: - result = subprocess.run( - ["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE - ) + result = subprocess.run(["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE) nm_lines = result.stdout.decode().split("\n") except: - print( - f"Error: Could not run objdump on {elf_path} - make sure that the project is built" - ) + print(f"Error: Could not run objdump on {elf_path} - make sure that the project is built") sys.exit(1) sizes: Dict[str, int] = {} @@ -127,12 +123,7 @@ def parse_map() -> OrderedDict[str, Symbol]: continue prev_line = line - if ( - ram_offset is None - or "=" in line - or "*fill*" in line - or " 0x" not in line - ): + if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line: continue ram = int(line[16 : 16 + 18], 0) rom = ram - ram_offset @@ -240,9 +231,7 @@ def group_matches( continue if max is not None and query_start > max: continue - if contains is not None and ( - query_start > contains or query_start + length < contains - ): + if contains is not None and (query_start > contains or query_start + length < contains): continue ret.append(Result(query, target, query_start, target_start, length)) @@ -291,11 +280,7 @@ def get_line_numbers(obj_file: Path) -> Dict[int, int]: def get_tu_offset(obj_file: Path, symbol: str) -> Optional[int]: objdump = "mips-linux-gnu-objdump" - objdump_out = ( - subprocess.run([objdump, "-t", obj_file], stdout=subprocess.PIPE) - .stdout.decode("utf-8") - .split("\n") - ) + objdump_out = subprocess.run([objdump, "-t", obj_file], stdout=subprocess.PIPE).stdout.decode("utf-8").split("\n") if not objdump_out: return None @@ -398,9 +383,7 @@ def get_matches( if not matches: continue - results: list[Result] = group_matches( - query, symbol, matches, window_size, min, max, contains - ) + results: list[Result] = group_matches(query, symbol, matches, window_size, min, max, contains) if not results: continue @@ -427,12 +410,12 @@ def get_matches( target_range_str = "" if c_range: - target_range_str = ( - fg.li_cyan + f" (line {c_range} in {obj_file.stem})" + fg.rs - ) + target_range_str = fg.li_cyan + f" (line {c_range} in {obj_file.stem})" + fg.rs query_str = f"query [{result.query_start}-{result.query_end}]" - target_str = f"{symbol} [insn {result.target_start}-{result.target_end}] ({result.length} total){target_range_str}" + target_str = ( + f"{symbol} [insn {result.target_start}-{result.target_end}] ({result.length} total){target_range_str}" + ) print(f"\t{query_str} matches {target_str}") if show_disasm: @@ -441,20 +424,12 @@ def get_matches( except ImportError: print("rabbitizer not found, cannot show disassembly") sys.exit(1) - result_query_bytes = query_bytes.bytes[ - result.query_start * 4 : result.query_end * 4 - ] - result_target_bytes = sym_bytes.bytes[ - result.target_start * 4 : result.target_end * 4 - ] + result_query_bytes = query_bytes.bytes[result.query_start * 4 : result.query_end * 4] + result_target_bytes = sym_bytes.bytes[result.target_start * 4 : result.target_end * 4] for i in range(0, len(result_query_bytes), 4): - q_insn = rabbitizer.Instruction( - int.from_bytes(result_query_bytes[i : i + 4], "big") - ) - t_insn = rabbitizer.Instruction( - int.from_bytes(result_target_bytes[i : i + 4], "big") - ) + q_insn = rabbitizer.Instruction(int.from_bytes(result_query_bytes[i : i + 4], "big")) + t_insn = rabbitizer.Instruction(int.from_bytes(result_target_bytes[i : i + 4], "big")) print(f"\t\t{q_insn.disassemble():35} | {t_insn.disassemble()}") diff --git a/tools/get_variable.py b/tools/get_variable.py index 3455e5b6b0..c6f8cae76d 100755 --- a/tools/get_variable.py +++ b/tools/get_variable.py @@ -2,20 +2,32 @@ import sys + def get_variable(arg): - v = arg - 2**32 # convert to s32 + v = arg - 2**32 # convert to s32 if v > -250000000: - if v <= -220000000: return f"EVT_FLOAT({(v + 230000000) / 1024})" - elif v <= -200000000: return f"ArrayFlag({v + 210000000})" - elif v <= -180000000: return f"ArrayVar({v + 190000000})" - elif v <= -160000000: return f"GameByte({v + 170000000})" - elif v <= -140000000: return f"AreaByte({v + 150000000})" - elif v <= -120000000: return f"GameFlag({v + 130000000})" - elif v <= -100000000: return f"AreaFlag({v + 110000000})" - elif v <= -80000000: return f"MapFlag({v + 90000000})" - elif v <= -60000000: return f"LocalFlag({v + 70000000})" - elif v <= -40000000: return f"MapVar({v + 50000000})" - elif v <= -20000000: return f"LocalVar({v + 30000000})" + if v <= -220000000: + return f"EVT_FLOAT({(v + 230000000) / 1024})" + elif v <= -200000000: + return f"ArrayFlag({v + 210000000})" + elif v <= -180000000: + return f"ArrayVar({v + 190000000})" + elif v <= -160000000: + return f"GameByte({v + 170000000})" + elif v <= -140000000: + return f"AreaByte({v + 150000000})" + elif v <= -120000000: + return f"GameFlag({v + 130000000})" + elif v <= -100000000: + return f"AreaFlag({v + 110000000})" + elif v <= -80000000: + return f"MapFlag({v + 90000000})" + elif v <= -60000000: + return f"LocalFlag({v + 70000000})" + elif v <= -40000000: + return f"MapVar({v + 50000000})" + elif v <= -20000000: + return f"LocalVar({v + 30000000})" if arg == 0xFFFFFFFF: return "-1" @@ -26,6 +38,7 @@ def get_variable(arg): else: return f"{arg}" + if __name__ == "__main__": try: print(get_variable(int(sys.argv[1], 0))) diff --git a/tools/m2ctx.py b/tools/m2ctx.py index 7f2b17fe93..6ec2de3f4f 100755 --- a/tools/m2ctx.py +++ b/tools/m2ctx.py @@ -20,14 +20,14 @@ CPP_FLAGS = [ "-D_LANGUAGE_C", "-DF3DEX_GBI_2", "-D_MIPS_SZLONG=32", - "-DSCRIPT(test...)={}" - "-D__attribute__(test...)=", + "-DSCRIPT(test...)={}" "-D__attribute__(test...)=", "-D__asm__(test...)=", "-ffreestanding", "-DM2CTX", "-DVERSION_PAL", ] + def import_c_file(in_file) -> str: in_file = os.path.relpath(in_file, root_dir) cpp_command = ["gcc", "-E", "-P", "-dM", *CPP_FLAGS, in_file] @@ -42,10 +42,9 @@ def import_c_file(in_file) -> str: out_text += subprocess.check_output(cpp_command2, cwd=root_dir, encoding="utf-8") except subprocess.CalledProcessError: print( - "Failed to preprocess input file, when running command:\n" - + cpp_command, + "Failed to preprocess input file, when running command:\n" + cpp_command, file=sys.stderr, - ) + ) sys.exit(1) if not out_text: @@ -56,10 +55,9 @@ def import_c_file(in_file) -> str: out_text = out_text.replace(line + "\n", "") return out_text + def main(): - parser = argparse.ArgumentParser( - description="""Create a context file which can be used for mips_to_c""" - ) + parser = argparse.ArgumentParser(description="""Create a context file which can be used for mips_to_c""") parser.add_argument( "c_file", help="""File from which to create context""", diff --git a/tools/migrate_data_to_c.py b/tools/migrate_data_to_c.py index 743f946293..42e0485334 100755 --- a/tools/migrate_data_to_c.py +++ b/tools/migrate_data_to_c.py @@ -22,7 +22,7 @@ def data_to_c(file_path): output = "" pattern = re.compile(r"(dlabel (jtbl_.*|.+_.*)\n.(\w+) (.*))") - for (all, symbol, type, data) in re.findall(pattern, s): + for all, symbol, type, data in re.findall(pattern, s): if type == "word": if symbol.startswith("jtbl_"): output += "dlabel " + symbol + "\n" + ".word " + data.replace("L", ".L") + "\n\n" @@ -42,7 +42,7 @@ def out_to_file(output, file_path): if not os.path.exists(output_dir): os.mkdir(output_dir) - file_name = file_path[file_path.rfind("/"):-7] + file_name = file_path[file_path.rfind("/") : -7] file = open("data2c/" + file_name + ".c", "w+") file.write(output) file.close() @@ -74,9 +74,19 @@ def query(file, to_file): parser = argparse.ArgumentParser(description="Tool to translate .data.s files to data arrays") -parser.add_argument("query", help="data file", nargs='?', default=None) -parser.add_argument("--all", help="translate all data files at once and output them into /data2c", action='store_true', required=False) -parser.add_argument("--to-file", help="redirect the output into a file. Can not be used in combination with --all", action='store_true', required=False) +parser.add_argument("query", help="data file", nargs="?", default=None) +parser.add_argument( + "--all", + help="translate all data files at once and output them into /data2c", + action="store_true", + required=False, +) +parser.add_argument( + "--to-file", + help="redirect the output into a file. Can not be used in combination with --all", + action="store_true", + required=False, +) args = parser.parse_args() diff --git a/tools/mv_segment.py b/tools/mv_segment.py index 457435ce0e..b73f9fe556 100755 --- a/tools/mv_segment.py +++ b/tools/mv_segment.py @@ -26,12 +26,21 @@ if os.path.exists(f"src/{args.src}.c"): if os.path.exists(f"ver/current/asm/nonmatchings/{args.src}"): print("moving asm/nonmatchings files") - os.rename(f"ver/current/asm/nonmatchings/{args.src}", f"ver/current/asm/nonmatchings/{args.dest}") + os.rename( + f"ver/current/asm/nonmatchings/{args.src}", + f"ver/current/asm/nonmatchings/{args.dest}", + ) if os.path.exists(f"ver/current/asm/data/{args.src}.data.s"): print("moving data file") - os.rename(f"ver/current/asm/data/{args.src}.data.s", f"ver/current/asm/data/{args.dest}.data.s") + os.rename( + f"ver/current/asm/data/{args.src}.data.s", + f"ver/current/asm/data/{args.dest}.data.s", + ) if os.path.exists(f"ver/current/asm/data/{args.src}.rodata.s"): print("moving rodata file") - os.rename(f"ver/current/asm/data/{args.src}.rodata.s", f"ver/current/asm/data/{args.dest}.rodata.s") + os.rename( + f"ver/current/asm/data/{args.src}.rodata.s", + f"ver/current/asm/data/{args.dest}.rodata.s", + ) diff --git a/tools/old/codescan.py b/tools/old/codescan.py index 95441525ae..6a854e5119 100755 --- a/tools/old/codescan.py +++ b/tools/old/codescan.py @@ -11,6 +11,7 @@ script_dir = os.path.dirname(os.path.realpath(__file__)) root_dir = os.path.abspath(os.path.join(script_dir, "../..")) import glob, os + os.chdir(root_dir) for f in Path(root_dir).rglob("*.bin"): @@ -20,7 +21,10 @@ for f in Path(root_dir).rglob("*.bin"): continue ras = [] - result = subprocess.run(["mips-linux-gnu-objdump", "-Dz", "-bbinary", "-mmips", "-EB" , f], stdout=subprocess.PIPE) + result = subprocess.run( + ["mips-linux-gnu-objdump", "-Dz", "-bbinary", "-mmips", "-EB", f], + stdout=subprocess.PIPE, + ) output = result.stdout.decode().split("\n") for line in output: diff --git a/tools/old/create_renames.py b/tools/old/create_renames.py index 5552755162..4811d4662b 100644 --- a/tools/old/create_renames.py +++ b/tools/old/create_renames.py @@ -18,7 +18,7 @@ for line in inlines: break if area: - fname = line[line.rfind("`") + 1:line.rfind("'")] + fname = line[line.rfind("`") + 1 : line.rfind("'")] renames[fname] = area pairs = [] diff --git a/tools/old/fix_bad_evt_changes.py b/tools/old/fix_bad_evt_changes.py index e9ebfef7fa..e2ce853113 100644 --- a/tools/old/fix_bad_evt_changes.py +++ b/tools/old/fix_bad_evt_changes.py @@ -28,7 +28,7 @@ for filename, line_number, bad_symbol_name in problems: continue if old_line.startswith("N(") or old_line.startswith("await N("): - good_symbol_name = old_line[old_line.find("N("):].split(")", 1)[0] + ")" + good_symbol_name = old_line[old_line.find("N(") :].split(")", 1)[0] + ")" else: good_symbol_name = old_line.split("(", 1)[0] @@ -38,4 +38,3 @@ for filename, line_number, bad_symbol_name in problems: with open(filename, "w") as f: f.writelines(lines) - diff --git a/tools/old/gen_effect_renames.py b/tools/old/gen_effect_renames.py index 9886f610b7..2e30139752 100755 --- a/tools/old/gen_effect_renames.py +++ b/tools/old/gen_effect_renames.py @@ -3,14 +3,14 @@ import argparse import os + def auto_int(x): return int(x, 0) + script_dir = os.path.dirname(os.path.realpath(__file__)) -parser = argparse.ArgumentParser( - description="Generate rename file for effects" -) +parser = argparse.ArgumentParser(description="Generate rename file for effects") parser.add_argument( "id", @@ -23,6 +23,7 @@ parser.add_argument( help="Name (in snake case) to change the effect to", ) + def main(args): id = args.id to = args.to @@ -31,7 +32,7 @@ def main(args): hex_str = f"{id:02x}".upper() - struct_name = ''.join(word.title() for word in to.split('_')) + struct_name = "".join(word.title() for word in to.split("_")) to_write.append(f"Effect{id} {struct_name}FXData") to_write.append(f"playFX_{hex_str} fx_{to}") to_write.append(f"FX_ENTRY_NUMBERED({id}, FX_ENTRY({to},") @@ -47,6 +48,7 @@ def main(args): for line in to_write: f.write(f"{line}\n") + if __name__ == "__main__": args = parser.parse_args() main(args) diff --git a/tools/old/gfxdis_loop.py b/tools/old/gfxdis_loop.py index a713d40bfd..bcf171c843 100644 --- a/tools/old/gfxdis_loop.py +++ b/tools/old/gfxdis_loop.py @@ -6,8 +6,8 @@ from pathlib import Path parser = argparse.ArgumentParser() parser.add_argument("baserom") -parser.add_argument("start", type=lambda x:int(x, 0)) -parser.add_argument("end", type=lambda x:int(x, 0)) +parser.add_argument("start", type=lambda x: int(x, 0)) +parser.add_argument("end", type=lambda x: int(x, 0)) args = parser.parse_args() baserom_path = Path(__file__).parent.parent / "baserom.z64" @@ -25,9 +25,13 @@ while i < args.end: while unpack_from("B", baserom, i)[0] == 0: i += 1 - #print(f"Start {hex(dis_start)} end {hex(i)}") - gfxdis = subprocess.run(f"{gfxdis_path.resolve()} " + f"-x " + f"-dc " + f"-d {baserom[dis_start:i].hex()}", - capture_output=True, shell=True, text=True) + # print(f"Start {hex(dis_start)} end {hex(i)}") + gfxdis = subprocess.run( + f"{gfxdis_path.resolve()} " + f"-x " + f"-dc " + f"-d {baserom[dis_start:i].hex()}", + capture_output=True, + shell=True, + text=True, + ) commands = gfxdis.stdout.splitlines()[1:-1] new_commands = [] diff --git a/tools/old/make_npc_structs.py b/tools/old/make_npc_structs.py index aeded3429c..d931ab4754 100644 --- a/tools/old/make_npc_structs.py +++ b/tools/old/make_npc_structs.py @@ -3,11 +3,23 @@ import argparse from struct import unpack_from CONSTANTS = {} + + def get_constants(): global CONSTANTS - valid_enums = { "StoryProgress", "ItemIDs", "PlayerAnims", - "ActorIDs", "Events", "SoundIDs", "SongIDs", "Locations", - "AmbientSounds", "NpcIDs", "Emotes" } + valid_enums = { + "StoryProgress", + "ItemIDs", + "PlayerAnims", + "ActorIDs", + "Events", + "SoundIDs", + "SongIDs", + "Locations", + "AmbientSounds", + "NpcIDs", + "Emotes", + } for enum in valid_enums: CONSTANTS[enum] = {} CONSTANTS["NPC_SPRITE"] = {} @@ -16,7 +28,7 @@ def get_constants(): enums = Path(include_path / "enums.h").read_text().splitlines() # defines - ''' + """ for line in enums.splitlines(): this_enum = "" for enum in valid_enums: @@ -31,12 +43,12 @@ def get_constants(): id_ = id_.split(" ",1)[0] CONSTANTS[this_enum][int(id_, 16)] = name - ''' + """ # enums - for i,line in enumerate(enums): + for i, line in enumerate(enums): if line.startswith("enum "): - enum_name = line.split(" ",1)[1].split(" {",1)[0] + enum_name = line.split(" ", 1)[1].split(" {", 1)[0] if enum_name in valid_enums: CONSTANTS[enum_name] = {} last_num = 0 @@ -47,7 +59,7 @@ def get_constants(): continue name = enums[i].strip() - val = last_num+1 + val = last_num + 1 if "=" in name: name, val = name.split(" = ") val = int(val[:-1], 0) @@ -56,14 +68,16 @@ def get_constants(): else: name = name[:-1] name = name.strip() - #print("\"" + name + "\"", "===", val) + # print("\"" + name + "\"", "===", val) CONSTANTS[enum_name][val] = name.strip() i += 1 last_num = val # sprites - sprite_path = Path(Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc") + sprite_path = Path( + Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc" + ) for file in sprite_path.iterdir(): fd = file.read_text() for line in fd.splitlines(): @@ -76,10 +90,10 @@ def get_constants(): else: continue - name = line.split(" ",2)[1] + name = line.split(" ", 2)[1] id_ = line.split("0x", 1)[1] if " " in id_: - id_ = id_.split(" ",1)[0] + id_ = id_.split(" ", 1)[0] name = name.split(f"_{enum}_", 1)[1] if enum == "NPC_SPRITE": saved_name = name @@ -89,7 +103,11 @@ def get_constants(): if enum == "NPC_SPRITE": if int(id_, 16) not in CONSTANTS["NPC_SPRITE"]: - CONSTANTS[enum][int(id_, 16)] = {"name":"", "palettes":{}, "anims":{}} + CONSTANTS[enum][int(id_, 16)] = { + "name": "", + "palettes": {}, + "anims": {}, + } CONSTANTS[enum][int(id_, 16)]["name"] = name elif enum == "NPC_PALETTE": CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["palettes"][int(id_, 16)] = name @@ -97,20 +115,22 @@ def get_constants(): CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["anims"][int(id_, 16)] = name return + STRUCTS = {} + def parse_var(line): - #print(f"Parsing {line}") + # print(f"Parsing {line}") if "*/ " in line: - line = line.split("*/ ",1)[1] - line = line.split(";",1)[0].strip() + line = line.split("*/ ", 1)[1] + line = line.split(";", 1)[0].strip() if "," in line or "(*" in line: return (None, None, None, None) elif "union " in line: return ("union", None, None, None) - #print(f"Parsed {line}") + # print(f"Parsed {line}") if " " in line: if line.startswith("struct "): struct, type_, name = line.split(" ") @@ -131,22 +151,23 @@ def parse_var(line): is_ptr = "*" in type_ or type_ == "UNK_PTR" return (type_, name, count, is_ptr) + def parse_file(filename): fd = filename.read_text().splitlines() i = 0 while i < len(fd): - #supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS] - #if any(supported): + # supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS] + # if any(supported): if "typedef struct " in fd[i]: - #supported_name = [SUPPORTED_STRUCTS[i] for i,x in enumerate(supported) if x][0] + # supported_name = [SUPPORTED_STRUCTS[i] for i,x in enumerate(supported) if x][0] supported_name = fd[i].split("typedef struct ", 1)[1].split(" {", 1)[0] if supported_name == "{": supported_name = "" - #print(f"Parsing struct \"{supported_name}\"") + # print(f"Parsing struct \"{supported_name}\"") struct_to_add = [] i += 1 - while ("} " + f"{supported_name.upper()}") not in fd[i].split(";",1)[0].upper(): + while ("} " + f"{supported_name.upper()}") not in fd[i].split(";", 1)[0].upper(): type_, name, count, ptr = parse_var(fd[i]) if type_ == None: @@ -158,27 +179,37 @@ def parse_file(filename): i += 1 while "}" not in fd[i]: type_, name, count, ptr = parse_var(fd[i]) - union.append({"type":type_, "name": name, "num":count, "ptr":ptr}) + union.append({"type": type_, "name": name, "num": count, "ptr": ptr}) i += 1 name = fd[i].split("}", 1)[1].split(";", 1)[0] - #print(supported_name, type_, name, count) - struct_to_add.append({"type":type_, "name": name, "num":count, "ptr":ptr, "union":union}) + # print(supported_name, type_, name, count) + struct_to_add.append( + { + "type": type_, + "name": name, + "num": count, + "ptr": ptr, + "union": union, + } + ) i += 1 - #print(f"Broke on line {fd[i]}") - #print() + # print(f"Broke on line {fd[i]}") + # print() if supported_name == "": - supported_name = fd[i].split("} ",1)[1].split(";",1)[0] + supported_name = fd[i].split("} ", 1)[1].split(";", 1)[0] if "[" in supported_name: supported_name = supported_name[:-2] STRUCTS[supported_name] = struct_to_add i += 1 + def get_structs(): parse_file(Path(Path(__file__).parent.parent / "include" / "map.h")) parse_file(Path(Path(__file__).parent.parent / "include" / "common_structs.h")) + def get_vals(fd, offset, var): global STRUCTS @@ -192,63 +223,63 @@ def get_vals(fd, offset, var): for var2 in STRUCTS[var["type"]]: out3, offset = get_vals(fd, offset, var2) data.extend(out3) - #if var["num"] == 1: + # if var["num"] == 1: # out.extend(out2) - #else: - #out.append(out2) + # else: + # out.append(out2) else: type_ = "int" fmt = "d" - if var["type"] == "s8" or var["type"] == "char": - if var["type"] == "char": + if var["type"] == "s8" or var["type"] == "char": + if var["type"] == "char": type_ = "hex" fmt = "X" - data = unpack_from('>b', fd, offset)[0] + data = unpack_from(">b", fd, offset)[0] offset += 1 - elif var["type"] == "u8": - data = unpack_from('>B', fd, offset)[0] + elif var["type"] == "u8": + data = unpack_from(">B", fd, offset)[0] fmt = "d" offset += 1 - elif var["type"] == "s16" or var["type"] in ("s16"): + elif var["type"] == "s16" or var["type"] in ("s16"): offset += offset % 2 - data = unpack_from('>h', fd, offset)[0] + data = unpack_from(">h", fd, offset)[0] fmt = "d" offset += 2 - elif var["type"] == "u16": + elif var["type"] == "u16": offset += offset % 2 - data = unpack_from('>H', fd, offset)[0] + data = unpack_from(">H", fd, offset)[0] fmt = "d" offset += 2 - elif var["type"] == "s32" or var["type"] in ("s32"): + elif var["type"] == "s32" or var["type"] in ("s32"): poff = offset offset += offset % 4 - data = unpack_from('>i', fd, offset)[0] + data = unpack_from(">i", fd, offset)[0] fmt = "d" offset += 4 - elif var["type"] == "u32": + elif var["type"] == "u32": offset += offset % 4 - data = unpack_from('>I', fd, offset)[0] + data = unpack_from(">I", fd, offset)[0] fmt = "d" offset += 4 - elif var["type"] == "f32": + elif var["type"] == "f32": offset += offset % 4 - data = unpack_from('>f', fd, offset)[0] + data = unpack_from(">f", fd, offset)[0] type_ = "float" fmt = ".01f" offset += 4 elif var["type"] == "X32": offset += offset % 4 - data = unpack_from('>f', fd, offset)[0] + data = unpack_from(">f", fd, offset)[0] type_ = "Xfloat" fmt = ".01f" if data < -1000.0 or data > 1000.0: type_ = "Xint" fmt = "d" - data = unpack_from('>i', fd, offset)[0] + data = unpack_from(">i", fd, offset)[0] offset += 4 elif var["ptr"]: offset += offset % 4 - data = unpack_from('>I', fd, offset)[0] + data = unpack_from(">I", fd, offset)[0] type_ = "ptr" fmt = "08X" offset += 4 @@ -256,24 +287,26 @@ def get_vals(fd, offset, var): print(f"Unknown data type \"{var['type']}\"") exit() if var["num"] == 1: - out.append({"name":var["name"], "type":type_, "fmt":fmt, "data":data}) + out.append({"name": var["name"], "type": type_, "fmt": fmt, "data": data}) else: - arr.append({"name":var["name"], "type":type_, "fmt":fmt, "data":data}) + arr.append({"name": var["name"], "type": type_, "fmt": fmt, "data": data}) if var["num"] > 1: out.append(arr) return out, offset + def INDENT(depth): return f" " * depth + def print_data(vals, indent, needs_name, is_array=False, is_struct=False): out = [] for val in vals: line = "" if needs_name: line = INDENT(indent) - #print(val) + # print(val) # array if type(val) is list: line += f".{val[0]['name']} = " + "{ " @@ -291,10 +324,10 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False): line += "\n" line += INDENT(indent) line += "{ " - for x,val2 in enumerate(val["data"]): + for x, val2 in enumerate(val["data"]): if x > 0: line += ", " - #line += f".{val2['name']} = " + # line += f".{val2['name']} = " fmt = val2["fmt"] if val2["type"] == "float": line += f"{val2['data']:{fmt}}f" @@ -315,7 +348,6 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False): if not is_array: line += "," else: - if "flags" in val["name"].lower() or "animations" in val["name"].lower(): if val["name"] == "flags": val["fmt"] = "08X" @@ -338,10 +370,10 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False): elif val["type"] == "hex": line += f"0x{val['data']:{fmt}}" elif val["type"] == "ptr": - if val["data"] == 0: - line += f"NULL" - else: - line += f"0x{val['data']:{fmt}}" + if val["data"] == 0: + line += f"NULL" + else: + line += f"0x{val['data']:{fmt}}" else: line += f"{val['data']}" @@ -352,6 +384,7 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False): return out + def output_type2(fd, count, offset, var): ultra_out = [] for i in range(count): @@ -364,10 +397,11 @@ def output_type2(fd, count, offset, var): out.extend(print_data(vals, 1, True)) out.append("};") ultra_out.append(out) - return offset, ultra_out #"\n".join(out) + return offset, ultra_out # "\n".join(out) -def check_list(vals, depth = 0): - for x,val in enumerate(vals): + +def check_list(vals, depth=0): + for x, val in enumerate(vals): if type(val) == list: if check_list(val, depth + 1): return True @@ -375,9 +409,10 @@ def check_list(vals, depth = 0): return True return False + def recurse_check_list(vals): res = 0 - for x,val in enumerate(vals): + for x, val in enumerate(vals): if type(val) == list: if check_list(val, 1): return len(vals) - x @@ -385,14 +420,15 @@ def recurse_check_list(vals): return len(vals) - x return -1 + def get_single_struct_vals(fd, i): vals = [] if not fd[i].rstrip().endswith("},"): # must be a sub-struct over multiple lines old_i = i i += 1 - while not ("}," in fd[i] and "." in fd[i+1]): - temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ") + while not ("}," in fd[i] and "." in fd[i + 1]): + temp = fd[i].split("{", 1)[1].split("}", 1)[0].split(", ") a = [] for x in temp: x = x.strip() @@ -405,7 +441,7 @@ def get_single_struct_vals(fd, i): i += 1 else: # single line - temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ") + temp = fd[i].split("{", 1)[1].split("}", 1)[0].split(", ") a = [] for x in temp: x = x.strip() @@ -417,10 +453,11 @@ def get_single_struct_vals(fd, i): vals.extend(a) return vals, i + def cull_struct(fd, i, entirely=False): out = [] vals = [] - #print(f"Culling Starting at {fd[i]}") + # print(f"Culling Starting at {fd[i]}") if not fd[i].rstrip().endswith("},"): # must be a sub-struct over multiple lines old_i = i @@ -428,9 +465,9 @@ def cull_struct(fd, i, entirely=False): # reverse and cull entries of only zeros x = recurse_check_list(vals[::-1]) - #print(f"Found first index of empty values at idx {x}, vals: {vals}") + # print(f"Found first index of empty values at idx {x}, vals: {vals}") if x < 0: - #print(f"Ending at {fd[i]}") + # print(f"Ending at {fd[i]}") return None, i out.append(fd[old_i]) @@ -441,36 +478,37 @@ def cull_struct(fd, i, entirely=False): out.append(fd[old_i]) old_i += 1 - #print(f"Ending at {fd[i]}") + # print(f"Ending at {fd[i]}") else: - prefix = fd[i].split("{",1)[0] + "{ " + prefix = fd[i].split("{", 1)[0] + "{ " suffix = " }," vals, i = get_single_struct_vals(fd, i) # reverse and cull entries of only zeros x = recurse_check_list(vals[::-1]) - #print(f"Found first index of empty values at idx {x}, vals: {vals}") + # print(f"Found first index of empty values at idx {x}, vals: {vals}") if x < 0: - #print(f"Ending at {fd[i]}") + # print(f"Ending at {fd[i]}") return None, i - #out.append(prefix) + # out.append(prefix) if entirely: x = len(vals) temp = "" - for z,y in enumerate(range(x)): + for z, y in enumerate(range(x)): if z > 0: prefix += ", " prefix += f"{vals[y]}" out.append(prefix + suffix) - #print(f"Ending at {fd[i]}") + # print(f"Ending at {fd[i]}") return "\n".join(out), i + def MacroReplaceStaticNPC(fd): - structs = { "unk_1C":True, "movement":False, "unk_1E0":True } - #replace_cull_struct = { "unk_1C", "movement", "unk_1E0" } - #replace_cull = { "tattle", "extraAnimations", "itemDropChance" } + structs = {"unk_1C": True, "movement": False, "unk_1E0": True} + # replace_cull_struct = { "unk_1C", "movement", "unk_1E0" } + # replace_cull = { "tattle", "extraAnimations", "itemDropChance" } fd = fd.splitlines() out = [] i = 0 @@ -479,7 +517,7 @@ def MacroReplaceStaticNPC(fd): for x in structs: if f".{x}" in fd[i]: found = x - break; + break if found: # just cull it if possible vals, i = cull_struct(fd, i, structs[found]) @@ -489,9 +527,9 @@ def MacroReplaceStaticNPC(fd): i += 1 continue - name = fd[i].split(" = ",1)[0].strip()[1:] + name = fd[i].split(" = ", 1)[0].strip()[1:] if name not in structs and "{" not in fd[i] and name != ";": - val = fd[i].split(" = ",1)[1][:-1] + val = fd[i].split(" = ", 1)[1][:-1] if "0x" in val: val = int(val, 16) elif "NULL" in val: @@ -507,8 +545,8 @@ def MacroReplaceStaticNPC(fd): if ".itemDrops" in fd[i]: vals, x = cull_struct(fd, i) - indent = len(fd[i].split(".",1)[0]) // 4 - new_line = fd[i].split("{",1)[0] + "{\n" + indent = len(fd[i].split(".", 1)[0]) // 4 + new_line = fd[i].split("{", 1)[0] + "{\n" if not vals: i = x @@ -525,7 +563,7 @@ def MacroReplaceStaticNPC(fd): added_item = True item_name = CONSTANTS["ItemIDs"][item[0]] - new_line += " " * (indent+1) + "{ " + item_name + f", {item[1]}, {item[2]}" + " },\n" + new_line += " " * (indent + 1) + "{ " + item_name + f", {item[1]}, {item[2]}" + " },\n" if added_item: new_line += " " * indent + "}," @@ -534,18 +572,18 @@ def MacroReplaceStaticNPC(fd): i = x elif ".animations" in fd[i]: - indent = len(fd[i].split(".",1)[0]) // 4 - new_line = fd[i].split("{",1)[0] + "{\n" + indent = len(fd[i].split(".", 1)[0]) // 4 + new_line = fd[i].split("{", 1)[0] + "{\n" vals, x = get_single_struct_vals(fd, i) for val in vals: - sprite_id = (val & 0x00FF0000) >> 16 + sprite_id = (val & 0x00FF0000) >> 16 palette_id = (val & 0x0000FF00) >> 8 - anim_id = (val & 0x000000FF) >> 0 - sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"] + anim_id = (val & 0x000000FF) >> 0 + sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"] palette = CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id] - anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id] - new_line += " " * (indent+1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n" + anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id] + new_line += " " * (indent + 1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n" new_line += " " * indent + "}," out.append(new_line) i = x @@ -553,7 +591,7 @@ def MacroReplaceStaticNPC(fd): elif ".heartDrops" in fd[i] or ".flowerDrops" in fd[i]: vals, x = get_single_struct_vals(fd, i) - new_line = fd[i].split("{",1)[0] + new_line = fd[i].split("{", 1)[0] attempts = vals[0][2] @@ -564,10 +602,16 @@ def MacroReplaceStaticNPC(fd): new_line += f"GENEROUS_HEART_DROPS({attempts})," elif round(vals[0][1] / 327.67, 2) == 80 and round(vals[0][3] / 327.67, 2) == 60: new_line += f"GENEROUS_WHEN_LOW_HEART_DROPS({attempts})," - elif round(vals[0][0] / 327.67, 2) == 100 and round(vals[0][1] / 327.67, 2) == 0 and round(vals[0][2] / 327.67, 2) == 0: + elif ( + round(vals[0][0] / 327.67, 2) == 100 + and round(vals[0][1] / 327.67, 2) == 0 + and round(vals[0][2] / 327.67, 2) == 0 + ): new_line += f"NO_DROPS," else: - print(f"Unknown heart drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}") + print( + f"Unknown heart drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}" + ) exit() else: if round(vals[0][1] / 327.67, 2) == 50 and round(vals[0][3] / 327.67, 2) == 40: @@ -576,10 +620,16 @@ def MacroReplaceStaticNPC(fd): new_line += f"GENEROUS_WHEN_LOW_FLOWER_DROPS({attempts})," elif round(vals[0][1] / 327.67, 2) == 40 and round(vals[0][3] / 327.67, 2) == 40: new_line += f"REDUCED_FLOWER_DROPS({attempts})," - elif round(vals[0][0] / 327.67, 2) == 100 and round(vals[0][1] / 327.67, 2) == 0 and round(vals[0][2] / 327.67, 2) == 0: + elif ( + round(vals[0][0] / 327.67, 2) == 100 + and round(vals[0][1] / 327.67, 2) == 0 + and round(vals[0][2] / 327.67, 2) == 0 + ): new_line += f"NO_DROPS," else: - print(f"Unknown flower drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}") + print( + f"Unknown flower drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}" + ) exit() out.append(new_line) @@ -590,8 +640,9 @@ def MacroReplaceStaticNPC(fd): i += 1 return "\n".join(out) + def MacroReplaceNpcSettings(fd): - replace_cull = { "unk_00", "unk_24" } + replace_cull = {"unk_00", "unk_24"} fd = fd.splitlines() out = [] i = 0 @@ -605,6 +656,7 @@ def MacroReplaceNpcSettings(fd): i += 1 return "\n".join(out) + def MacroReplaceNpcGroupList(fd): fd = fd.splitlines() out = [] @@ -617,21 +669,31 @@ def MacroReplaceNpcGroupList(fd): val = 0 else: if "0x" in fd[i]: - val = int(fd[i].split(" = ",1)[1][:-1], 16) + val = int(fd[i].split(" = ", 1)[1][:-1], 16) else: - val = int(fd[i].split(" = ",1)[1][:-1], 10) + val = int(fd[i].split(" = ", 1)[1][:-1], 10) vals.append(val) i += 1 - out.append(f" NPC_GROUP(N(D_{vals[1]:X}), BATTLE_ID({(vals[2] & 0xFF000000) >> 24}, {(vals[2] & 0xFF0000) >> 16}, {(vals[2] & 0xFF00) >> 8}, {vals[2] & 0xFF})),") + out.append( + f" NPC_GROUP(N(D_{vals[1]:X}), BATTLE_ID({(vals[2] & 0xFF000000) >> 24}, {(vals[2] & 0xFF0000) >> 16}, {(vals[2] & 0xFF00) >> 8}, {vals[2] & 0xFF}))," + ) return "\n".join(out) + parser = argparse.ArgumentParser() parser.add_argument("file", type=str, help="File to decompile struct from") parser.add_argument("type", type=str, help="Struct type to decompile") parser.add_argument("offset", type=lambda x: int(x, 0), help="Offset to decompile struct from") -parser.add_argument("--count", "-c", "--c", type=int, default=0, help="Num to try and decompile (NpcGroupList)") +parser.add_argument( + "--count", + "-c", + "--c", + type=int, + default=0, + help="Num to try and decompile (NpcGroupList)", +) args = parser.parse_args() get_constants() @@ -641,14 +703,14 @@ if args.type not in STRUCTS: print(f"Unknown struct type {args.type}") exit() -''' +""" out = [f"{args.type} = " + "{\n"] offset = args.offset for var in STRUCTS[args.type]: line, offset = output_type(fd, offset, var, 1) out.append(line) out.append("};") -''' +""" if args.count == 0: args.count = 1 @@ -656,7 +718,7 @@ if args.count == 0: fd = Path(args.file).resolve().read_bytes() offset, out = output_type2(fd, args.count, args.offset, STRUCTS[args.type]) -for i,entry in enumerate(out): +for i, entry in enumerate(out): out[i] = "\n".join(entry) print(f"EvtScript range 0x{args.offset:08X} - 0x{offset:08X}") diff --git a/tools/old/migrate_effect_rodata.py b/tools/old/migrate_effect_rodata.py index ce4fa2695d..07ffb0b17a 100755 --- a/tools/old/migrate_effect_rodata.py +++ b/tools/old/migrate_effect_rodata.py @@ -90,6 +90,7 @@ data_to_thing = { "413F20": "star_outline", } + def handle_symbol(effect, symbol): for root, dirs, files in os.walk(asm_effects_dir + effect + "/"): for f_name in files: @@ -139,6 +140,7 @@ def handle_file(f_path): continue migrated = handle_symbol(data_to_thing[effect], symbol) + # Walk through asm files and rename stuff print("Walking through asm files") for root, dirs, files in os.walk(asm_data_dir): diff --git a/tools/old/migrate_exit_strings.py b/tools/old/migrate_exit_strings.py index 067f378bee..93ae109d0a 100755 --- a/tools/old/migrate_exit_strings.py +++ b/tools/old/migrate_exit_strings.py @@ -64,4 +64,3 @@ for segment in config["segments"]: with open(c_file_path, "w", newline="\n") as f: f.write("".join(c_lines)) - diff --git a/tools/old/migrate_rodata.py b/tools/old/migrate_rodata.py index 65393e5b06..1eb60364f2 100755 --- a/tools/old/migrate_rodata.py +++ b/tools/old/migrate_rodata.py @@ -9,6 +9,7 @@ asm_dir = root_dir + "ver/current/asm/" asm_world_dir = asm_dir + "nonmatchings/world/" asm_data_dir = asm_dir + "data/" + def handle_symbol(area, symbol): for root, dirs, files in os.walk(asm_world_dir + area[0] + "/" + area[1]): for f_name in files: @@ -55,6 +56,7 @@ def handle_file(f_path): for symbol in reversed(symbols): migrated = handle_symbol(area, symbol) + # Walk through asm files and rename stuff print("Walking through asm files") for root, dirs, files in os.walk(asm_data_dir): diff --git a/tools/old/new_lines.py b/tools/old/new_lines.py index e669d0555f..a9a8636c3a 100644 --- a/tools/old/new_lines.py +++ b/tools/old/new_lines.py @@ -1,5 +1,6 @@ from pathlib import Path + def parse_folder(path): for entry in path.iterdir(): if entry.is_dir(): @@ -10,4 +11,5 @@ def parse_folder(path): fd.append("") entry.write_text("\n".join(fd)) + parse_folder(Path("src")) diff --git a/tools/old/sortsymz.py b/tools/old/sortsymz.py index f1b09e297a..000ae6c500 100755 --- a/tools/old/sortsymz.py +++ b/tools/old/sortsymz.py @@ -10,7 +10,7 @@ with open("tools/symz.txt") as f: for line in f.readlines(): if line.strip() and not line.startswith("//"): name, addr = line.strip().strip(";").split(" = ") - try : + try: addr = int(addr, 0) except ValueError: continue diff --git a/tools/old/substitute.py b/tools/old/substitute.py index ea7c39f7fd..264d81b24c 100755 --- a/tools/old/substitute.py +++ b/tools/old/substitute.py @@ -12,7 +12,10 @@ src_dir = root_dir + "src/" asm_dir = root_dir + "ver/current/asm/" parser = argparse.ArgumentParser(description="Replace many functions with one") -parser.add_argument("from_list", help="path to line-separated file of functions to be replaced. first line is the string to replace them with") +parser.add_argument( + "from_list", + help="path to line-separated file of functions to be replaced. first line is the string to replace them with", +) args = parser.parse_args() diff --git a/tools/old/update_evts.py b/tools/old/update_evts.py index 256ee30f2c..fc920e86b3 100755 --- a/tools/old/update_evts.py +++ b/tools/old/update_evts.py @@ -47,11 +47,14 @@ def parse_symbol_addrs(): symbol_addrs = {} for line in lines: - name = line[:line.find(" ")] + name = line[: line.find(" ")] - attributes = line[line.find("//"):].split(" ") - ram_addr = int(line[:line.find(";")].split("=")[1].strip(), base=0) - rom_addr = next((int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"), None) + attributes = line[line.find("//") :].split(" ") + ram_addr = int(line[: line.find(";")].split("=")[1].strip(), base=0) + rom_addr = next( + (int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"), + None, + ) symbol_addrs[name] = Symbol(ram_addr, rom_addr) @@ -73,7 +76,7 @@ def find_old_script_ranges(lines, filename): if "#define NAMESPACE " in line_content: namespace = line_content.split(" ")[2].strip() - #elif namespace == "events" or namespace == "header": + # elif namespace == "events" or namespace == "header": # namespace = NAMESPACES.get(filename, filename.split("/")[-2].split(".")[0]) elif namespace_temp is not None: namespace = namespace_temp[0][:-2] @@ -113,7 +116,7 @@ def replace_old_script_macros(filename, symbol_addrs): lines[range.start] = lines[range.start][:macro_start_idx] + "{\n" # Remove other lines - lines = lines[:range.start + 1] + lines[range.end + 1:] + lines = lines[: range.start + 1] + lines[range.end + 1 :] # Find the symbol try: @@ -128,7 +131,7 @@ def replace_old_script_macros(filename, symbol_addrs): local_symbol_map = {} for sym in symbol_addrs: if sym.startswith(range.namespace): - key = "N(" + sym[len(range.namespace)+1:] + ")" + key = "N(" + sym[len(range.namespace) + 1 :] + ")" else: key = sym @@ -143,7 +146,8 @@ def replace_old_script_macros(filename, symbol_addrs): # Disassemble the script rom.seek(range_sym.rom_addr) - evt_code = ScriptDisassembler(rom, + evt_code = ScriptDisassembler( + rom, script_name=range.symbol_name, romstart=range_sym.rom_addr, prelude=False, diff --git a/tools/rename.py b/tools/rename.py index f245b9a9fe..05df53dca3 100755 --- a/tools/rename.py +++ b/tools/rename.py @@ -16,6 +16,7 @@ renames = {} patterns = [] deletes = [] + def handle_file(f_path, try_rename_file=False): with open(f_path) as f: f_text_orig = f.read() @@ -35,17 +36,18 @@ def handle_file(f_path, try_rename_file=False): # replace all matches for match in matches: # head part - to_join.append(f_text[pos:match[1]]) + to_join.append(f_text[pos : match[1]]) to_replace = patterns[match[0]] to_join.append(renames[to_replace]) pos = match[2] # tail part to_join.append(f_text[pos:]) - f_text = ''.join(to_join); + f_text = "".join(to_join) # save changes with open(f_path, "w", newline="\n") as f: f.write(f_text) + # Read input file # One valid whitespace-separated find-replace pair is given per line with open(os.path.join(script_dir, "to_rename.txt")) as f: @@ -59,7 +61,7 @@ for line in renames_text: renames[split[0]] = split[1] patterns.append(split[0]) elif len(split) != 0: - raise Exception("input contains invalid rename pattern: \n\"" + line.strip() + "\"") + raise Exception('input contains invalid rename pattern: \n"' + line.strip() + '"') ac = ahocorasick_rs.AhoCorasick(patterns, matchkind=MATCHKIND_LEFTMOST_LONGEST) diff --git a/tools/sjis.py b/tools/sjis.py index 19252ff334..5677b2377b 100755 --- a/tools/sjis.py +++ b/tools/sjis.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 + def decode(data): length = 0 is_dbl_char = False @@ -20,7 +21,8 @@ def decode(data): length += 1 - return data[:length].decode('shift-jis') + return data[:length].decode("shift-jis") + if __name__ == "__main__": import sys diff --git a/tools/splat_ext/gfx_common.py b/tools/splat_ext/gfx_common.py index dadfe1f85c..ce70d2c7fc 100644 --- a/tools/splat_ext/gfx_common.py +++ b/tools/splat_ext/gfx_common.py @@ -1,5 +1,6 @@ from segtypes.n64.gfx import N64SegGfx + class N64SegGfx_common(N64SegGfx): def format_sym_name(self, sym): return f"N({sym.name[7:]})" diff --git a/tools/splat_ext/pm_effect_shims.py b/tools/splat_ext/pm_effect_shims.py index 4caaaba14d..0b5240dbc5 100644 --- a/tools/splat_ext/pm_effect_shims.py +++ b/tools/splat_ext/pm_effect_shims.py @@ -67,8 +67,6 @@ glabel {name} ret = [] for shim in self.shims: - ret.append( - LinkerEntry(self, [self.shim_path(shim)], self.shim_path(shim), ".text") - ) + ret.append(LinkerEntry(self, [self.shim_path(shim)], self.shim_path(shim), ".text")) return ret diff --git a/tools/splat_ext/pm_icons.py b/tools/splat_ext/pm_icons.py index 36859be817..2ad35fe447 100644 --- a/tools/splat_ext/pm_icons.py +++ b/tools/splat_ext/pm_icons.py @@ -13,22 +13,22 @@ script_dir = Path(os.path.dirname(os.path.realpath(__file__))) def indent(elem, level=0): - i = "\n" + level*" " + i = "\n" + level * " " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: - indent(elem, level+1) + indent(elem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i + elem.tail = i -def pretty_print_xml(tree : ET.ElementTree, path : Path): +def pretty_print_xml(tree: ET.ElementTree, path: Path): root = tree.getroot() indent(root) xml_str = ET.tostring(root, encoding="unicode") @@ -49,11 +49,11 @@ def parse_palette(data): class N64SegPm_icons(N64Segment): def split(self, rom_bytes): self.out_dir = options.opts.asset_path / "icon" - + with open(script_dir / "icon.yaml") as f: self.icons = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader) - data = rom_bytes[self.rom_start: self.rom_end] + data = rom_bytes[self.rom_start : self.rom_end] pos = 0 self.files = [] @@ -65,11 +65,11 @@ class N64SegPm_icons(N64Segment): IconList = ET.Element("Icons") - for (_, icon) in enumerate(self.icons): + for _, icon in enumerate(self.icons): # read yaml entry fmt = icon[0] name = icon[1] - w = int(icon[2]) + w = int(icon[2]) h = int(icon[3]) if fmt == "solo" or fmt == "pair": diff --git a/tools/splat_ext/pm_imgfx_data.py b/tools/splat_ext/pm_imgfx_data.py index b3b78165d2..3ec39f50b0 100644 --- a/tools/splat_ext/pm_imgfx_data.py +++ b/tools/splat_ext/pm_imgfx_data.py @@ -41,9 +41,7 @@ class N64SegPm_imgfx_data(N64Segment): frame: List[Vertex] = [] for j in range(vtx_count): - x, y, z, u, v, r, g, b, a = struct.unpack( - ">hhhBBbbbB", data[pos : pos + 12] - ) + x, y, z, u, v, r, g, b, a = struct.unpack(">hhhBBbbbB", data[pos : pos + 12]) pos += 12 frame.append(Vertex(j, x, y, z, u, v, r, g, b, a)) @@ -121,10 +119,7 @@ class N64SegPm_imgfx_data(N64Segment): return [ LinkerEntry( self, - [ - self.OUT_DIR / f"{name}.json" - for name, _ in self.yaml.get("animations") - ], + [self.OUT_DIR / f"{name}.json" for name, _ in self.yaml.get("animations")], options.opts.asset_path / "imgfx" / f"{self.name}.c", self.get_linker_section(), ) diff --git a/tools/splat_ext/pm_map_data.py b/tools/splat_ext/pm_map_data.py index 9fcc909831..b47b516510 100644 --- a/tools/splat_ext/pm_map_data.py +++ b/tools/splat_ext/pm_map_data.py @@ -135,65 +135,45 @@ class N64SegPm_map_data(N64Segment): w = png.Writer(150, 105, palette=parse_palette(bytes[:0x200])) w.write_array(f, bytes[0x200:]) elif name == "title_data": - if "ver/us" in str(options.opts.target_path) or "ver/pal" in str( - options.opts.target_path - ): + if "ver/us" in str(options.opts.target_path) or "ver/pal" in str(options.opts.target_path): w = 200 h = 112 - img = n64img.image.RGBA32( - data=bytes[0x2210 : 0x2210 + w * h * 4], width=w, height=h - ) + img = n64img.image.RGBA32(data=bytes[0x2210 : 0x2210 + w * h * 4], width=w, height=h) img.write(fs_dir / "title/logotype.png") w = 144 h = 32 - img = n64img.image.IA8( - data=bytes[0x10 : 0x10 + w * h], width=w, height=h - ) + img = n64img.image.IA8(data=bytes[0x10 : 0x10 + w * h], width=w, height=h) img.write(fs_dir / "title/copyright.png") w = 128 h = 32 - img = n64img.image.IA8( - data=bytes[0x1210 : 0x1210 + w * h], width=w, height=h - ) + img = n64img.image.IA8(data=bytes[0x1210 : 0x1210 + w * h], width=w, height=h) img.write(fs_dir / "title/press_start.png") else: w = 272 h = 88 - img = n64img.image.RGBA32( - data=bytes[0x1830 : 0x1830 + w * h * 4], width=w, height=h - ) + img = n64img.image.RGBA32(data=bytes[0x1830 : 0x1830 + w * h * 4], width=w, height=h) img.write(fs_dir / "title/logotype.png") w = 128 h = 32 - img = n64img.image.CI4( - data=bytes[0x10 : 0x10 + (w * h // 2)], width=w, height=h - ) + img = n64img.image.CI4(data=bytes[0x10 : 0x10 + (w * h // 2)], width=w, height=h) img.palette = parse_palette(bytes[0x810:0x830]) img.write(fs_dir / "title/copyright.png") w = 128 h = 32 - img = n64img.image.IA8( - data=bytes[0x830 : 0x830 + w * h], width=w, height=h - ) + img = n64img.image.IA8(data=bytes[0x830 : 0x830 + w * h], width=w, height=h) img.write(fs_dir / "title/press_start.png") elif name.endswith("_bg"): def write_bg_png(bytes, path, header_offset=0): header = bytes[header_offset : header_offset + 0x10] - raster_offset = ( - int.from_bytes(header[0:4], byteorder="big") - 0x80200000 - ) - palette_offset = ( - int.from_bytes(header[4:8], byteorder="big") - 0x80200000 - ) - assert ( - int.from_bytes(header[8:12], byteorder="big") == 0x000C0014 - ) # draw pos + raster_offset = int.from_bytes(header[0:4], byteorder="big") - 0x80200000 + palette_offset = int.from_bytes(header[4:8], byteorder="big") - 0x80200000 + assert int.from_bytes(header[8:12], byteorder="big") == 0x000C0014 # draw pos width = int.from_bytes(header[12:14], byteorder="big") height = int.from_bytes(header[14:16], byteorder="big") @@ -202,9 +182,7 @@ class N64SegPm_map_data(N64Segment): w = png.Writer( width, height, - palette=parse_palette( - bytes[palette_offset : palette_offset + 512] - ), + palette=parse_palette(bytes[palette_offset : palette_offset + 512]), ) w.write_array(f, bytes[raster_offset:]) @@ -212,9 +190,7 @@ class N64SegPm_map_data(N64Segment): # sbk_bg has an alternative palette if name == "sbk_bg": - write_bg_png( - bytes, fs_dir / "bg" / f"{name}.alt.png", header_offset=0x10 - ) + write_bg_png(bytes, fs_dir / "bg" / f"{name}.alt.png", header_offset=0x10) elif name.endswith("_tex"): TexArchive.extract(bytes, fs_dir / "tex" / name) else: diff --git a/tools/splat_ext/pm_msg.py b/tools/splat_ext/pm_msg.py index fa7be3ce9e..7fce2326b5 100644 --- a/tools/splat_ext/pm_msg.py +++ b/tools/splat_ext/pm_msg.py @@ -188,13 +188,23 @@ CHARSET = { 0x02: "[Style left]\n", 0x03: "[Style center]\n", 0x04: "[Style tattle]\n", - 0x05: {None: lambda d: (f"[Style choice pos={d[0]},{d[1]} size={d[2]},{d[3]}]\n", 4)}, + 0x05: { + None: lambda d: ( + f"[Style choice pos={d[0]},{d[1]} size={d[2]},{d[3]}]\n", + 4, + ) + }, 0x06: "[Style inspect]\n", 0x07: "[Style sign]\n", 0x08: {None: lambda d: (f"[Style lamppost height={d[0]}]\n", 1)}, 0x09: {None: lambda d: (f"[Style postcard index={d[0]}]\n", 1)}, 0x0A: "[Style popup]\n", - 0x0C: {None: lambda d: (f"[Style upgrade pos={d[0]},{d[1]} size={d[2]},{d[3]}]\n", 4)}, + 0x0C: { + None: lambda d: ( + f"[Style upgrade pos={d[0]},{d[1]} size={d[2]},{d[3]}]\n", + 4, + ) + }, 0x0D: "[Style narrate]\n", 0x0E: "[Style epilogue]\n", }, @@ -215,17 +225,13 @@ CHARSET = { # 0x24: "[color:cyan]", # 0x25: "[color:green]", # 0x26: "[color:yellow]", - # 0x00: "[color=normal ctx=diary]", # 0x07: "[color=red ctx=diary]", - # 0x17: "[color=dark ctx=inspect]", - # 0x18: "[color=normal ctx=sign]", # 0x19: "[color=red ctx=sign]", # 0x1A: "[color=blue ctx=sign]", # 0x1B: "[color=green ctx=sign]", - # 0x28: "[color=red ctx=popup]", # 0x29: "[color=pink ctx=popup]", # 0x2A: "[color=purple ctx=popup]", @@ -234,7 +240,6 @@ CHARSET = { # 0x2D: "[color=green ctx=popup]", # 0x2E: "[color=yellow ctx=popup]", # 0x2F: "[color=normal ctx=popup]", - None: lambda d: (f"[Color 0x{d[0]:X}]", 1), }, 0x07: "[InputOff]\n", @@ -252,9 +257,19 @@ CHARSET = { 0x13: {None: lambda d: (f"[Down {d[0]}]", 1)}, 0x14: {None: lambda d: (f"[Up {d[0]}]", 1)}, 0x15: {None: lambda d: (f"[InlineImage index={d[0]}]\n", 1)}, - 0x16: {None: lambda d: (f"[AnimSprite spriteID=0x{d[0]:02X}{d[1]:02X} raster={d[2]}]\n", 3)}, + 0x16: { + None: lambda d: ( + f"[AnimSprite spriteID=0x{d[0]:02X}{d[1]:02X} raster={d[2]}]\n", + 3, + ) + }, 0x17: {None: lambda d: (f"[ItemIcon itemID=0x{d[0]:02X}{d[1]:02X}]\n", 2)}, - 0x18: {None: lambda d: (f"[Image index={d[0]} pos={(d[1] << 8) + d[2]},{d[3]} hasBorder={d[4]} alpha={d[5]} fadeAmount={d[6]}]\n", 7)}, + 0x18: { + None: lambda d: ( + f"[Image index={d[0]} pos={(d[1] << 8) + d[2]},{d[3]} hasBorder={d[4]} alpha={d[5]} fadeAmount={d[6]}]\n", + 7, + ) + }, 0x19: {None: lambda d: (f"[HideImage fadeAmount={d[0]}]\n", 1)}, 0x1A: {None: lambda d: (f"[AnimDelay index={d[1]} delay={d[2]}]", 3)}, 0x1B: {None: lambda d: (f"[AnimLoop {d[0]} {d[1]}]", 2)}, @@ -265,20 +280,24 @@ CHARSET = { 0x21: {None: lambda d: (f"[Option {d[0]}]", 1)}, 0x22: "[SavePos]", 0x23: "[RestorePos]", - 0x24: {0xFF: {0x05: { - 0x10: {0x98: {0xFF: {0x25: "[A]"}}}, - 0x11: {0x99: {0xFF: {0x25: "[B]"}}}, - 0x12: {0xA1: {0xFF: {0x25: "[START]"}}}, - 0x13: { - 0x9D: {0xFF: {0x25: "[C-UP]"}}, - 0x9E: {0xFF: {0x25: "[C-DOWN]"}}, - 0x9F: {0xFF: {0x25: "[C-LEFT]"}}, - 0xA0: {0xFF: {0x25: "[C-RIGHT]"}}, - }, - 0x14: {0x9C: {0xFF: {0x25: "[Z]"}}}, - }}}, - #0x24: "[SaveColor]", - #0x25: "[RestoreColor]", + 0x24: { + 0xFF: { + 0x05: { + 0x10: {0x98: {0xFF: {0x25: "[A]"}}}, + 0x11: {0x99: {0xFF: {0x25: "[B]"}}}, + 0x12: {0xA1: {0xFF: {0x25: "[START]"}}}, + 0x13: { + 0x9D: {0xFF: {0x25: "[C-UP]"}}, + 0x9E: {0xFF: {0x25: "[C-DOWN]"}}, + 0x9F: {0xFF: {0x25: "[C-LEFT]"}}, + 0xA0: {0xFF: {0x25: "[C-RIGHT]"}}, + }, + 0x14: {0x9C: {0xFF: {0x25: "[Z]"}}}, + } + } + }, + # 0x24: "[SaveColor]", + # 0x25: "[RestoreColor]", 0x26: { 0x00: "[Shake]", 0x01: "[Wave]", @@ -307,7 +326,12 @@ CHARSET = { 0x28: {None: lambda d: (f"[Var {d[0]}]", 1)}, 0x29: {None: lambda d: (f"[CenterX {d[0]}]", 1)}, 0x2B: "[EnableCDownNext]", - 0x2C: {None: lambda d: (f"[CustomVoice soundIDs=0x{d[0]:02X}{d[1]:02X}{d[2]:02X}{d[3]:02X},{d[4]:02X}{d[5]:02X}{d[6]:02X}{d[7]:02X}]", 8)}, + 0x2C: { + None: lambda d: ( + f"[CustomVoice soundIDs=0x{d[0]:02X}{d[1]:02X}{d[2]:02X}{d[3]:02X},{d[4]:02X}{d[5]:02X}{d[6]:02X}{d[7]:02X}]", + 8, + ) + }, 0x2E: {None: lambda d: (f"[Volume {d[0]}]", 1)}, 0x2F: { 0: "[Voice normal]\n", @@ -315,7 +339,7 @@ CHARSET = { 2: "[Voice star]\n", None: lambda d: (f"[Voice {d[0]}]\n", 1), }, - #None: lambda d: (f"[func_{d[0]:02X}]", 1), + # None: lambda d: (f"[func_{d[0]:02X}]", 1), }, None: lambda d: (f"[Raw 0x{d[0]:02X}]", 1), } @@ -366,6 +390,7 @@ CHARSET_CREDITS = { 0xF7: " ", } + class N64SegPm_msg(N64Segment): def __init__( self, @@ -393,12 +418,12 @@ class N64SegPm_msg(N64Segment): self.msg_names = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader) def split(self, rom_bytes): - data = rom_bytes[self.rom_start: self.rom_end] + data = rom_bytes[self.rom_start : self.rom_end] section_offsets = [] pos = 0 while True: - offset = int.from_bytes(data[pos:pos+4], byteorder="big") + offset = int.from_bytes(data[pos : pos + 4], byteorder="big") if offset == 0: break @@ -417,7 +442,7 @@ class N64SegPm_msg(N64Segment): msg_offsets = [] pos = section_offset while True: - offset = int.from_bytes(data[pos:pos+4], byteorder="big") + offset = int.from_bytes(data[pos : pos + 4], byteorder="big") if offset == section_offset: break @@ -425,7 +450,7 @@ class N64SegPm_msg(N64Segment): msg_offsets.append(offset) pos += 4 - #self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})") + # self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})") path = msg_dir / Path(name + ".msg") @@ -449,7 +474,6 @@ class N64SegPm_msg(N64Segment): self.write_message_markup(data[msg_offset:]) self.f.write("\n}\n") - def get_linker_entries(self): from segtypes.linker_entry import LinkerEntry @@ -458,7 +482,6 @@ class N64SegPm_msg(N64Segment): return [LinkerEntry(self, out_paths, base_path, ".data")] - @staticmethod def get_default_name(addr): return "msg" diff --git a/tools/splat_ext/pm_sbn.py b/tools/splat_ext/pm_sbn.py index b1a68599bb..12f9899382 100644 --- a/tools/splat_ext/pm_sbn.py +++ b/tools/splat_ext/pm_sbn.py @@ -56,9 +56,7 @@ class SBN: entry_addr = header.tableOffset seen_entry_offsets = set() for i in range(header.numEntries): - entry = SBNFileEntry( - *struct.unpack_from(SBNFileEntry.fstring, data, entry_addr) - ) + entry = SBNFileEntry(*struct.unpack_from(SBNFileEntry.fstring, data, entry_addr)) entry_addr += SBNFileEntry.length # Check for duplicate entry offsets @@ -152,9 +150,7 @@ class SBN: else: raise ValueError("Unsupported file extension") - entry = SBNFileEntry( - offset=current_file_offset, fmt=format, size=file.fakesize - ) + entry = SBNFileEntry(offset=current_file_offset, fmt=format, size=file.fakesize) struct.pack_into( SBNFileEntry.fstring, @@ -222,9 +218,7 @@ class SBN: with open(path / "sbn.yaml", "w") as f: # Filename->ID map - f.write( - "# Mapping of filenames to entry IDs. Use 'id: auto' to automatically assign a unique ID.\n" - ) + f.write("# Mapping of filenames to entry IDs. Use 'id: auto' to automatically assign a unique ID.\n") f.write( """ # 'fakesize is an interesting case. In the final ROM, the size of a file is stored in the file header and the entry table. @@ -267,9 +261,7 @@ class SBN: f.write("\n") # INIT mseqs - f.write( - "# AuGlobals::mseqFileList. Not sure why there's non-MSEQ files here!\n" - ) + f.write("# AuGlobals::mseqFileList. Not sure why there's non-MSEQ files here!\n") f.write("mseqs:\n") for id, entry in enumerate(self.init.mseq_entries): f.write(f" - id: 0x{id:02x}\n") @@ -357,9 +349,7 @@ class SBN: assert type(bk_file) == str bk_file_ids.append(self.lookup_file_id(bk_file)) - init_song_entry = InitSongEntry( - file_id, bk_file_ids[0], bk_file_ids[1], bk_file_ids[2] - ) + init_song_entry = InitSongEntry(file_id, bk_file_ids[0], bk_file_ids[1], bk_file_ids[2]) # Replace self.init.song_entries[id] if id < len(self.init.song_entries): @@ -562,9 +552,7 @@ class INIT: song_addr = header.tblOffset song_number = 0 while True: - song = InitSongEntry( - *struct.unpack_from(InitSongEntry.fstring, data, song_addr) - ) + song = InitSongEntry(*struct.unpack_from(InitSongEntry.fstring, data, song_addr)) if song.bgmFileIndex == 0xFFFF: break @@ -590,9 +578,7 @@ class INIT: entries_len = header.entriesSize // 4 - 1 for i in range(entries_len): - entry = BufferEntry( - *struct.unpack_from(BufferEntry.fstring, data, entries_addr) - ) + entry = BufferEntry(*struct.unpack_from(BufferEntry.fstring, data, entries_addr)) entries_addr += BufferEntry.length self.bk_entries.append(entry) @@ -708,9 +694,7 @@ if splat_loaded: out = options.opts.asset_path / self.dir / (self.name + ".sbn") sbn = SBN() - config_files = sbn.read( - dir - ) # TODO: LayeredFS/AssetsFS read, supporting merges + config_files = sbn.read(dir) # TODO: LayeredFS/AssetsFS read, supporting merges inputs = config_files + [dir / f.file_name() for f in sbn.files] return [ LinkerEntry( diff --git a/tools/splat_ext/pm_sprite_shading_profiles.py b/tools/splat_ext/pm_sprite_shading_profiles.py index f951b34d49..7441870063 100644 --- a/tools/splat_ext/pm_sprite_shading_profiles.py +++ b/tools/splat_ext/pm_sprite_shading_profiles.py @@ -140,9 +140,7 @@ def extract(input_data: bytes, endian: Literal["big", "little"] = "big") -> str: profile_list = [] for _ in range(len(PROFILE_NAMES[g])): - profile_list.append( - struct.unpack(END + "i", offsets_table[pl_it : pl_it + 4])[0] - ) + profile_list.append(struct.unpack(END + "i", offsets_table[pl_it : pl_it + 4])[0]) pl_it += 4 for j, pl_offset in enumerate(profile_list): diff --git a/tools/splat_ext/pm_sprites.py b/tools/splat_ext/pm_sprites.py index c9708ea2ee..3e23a2b26b 100644 --- a/tools/splat_ext/pm_sprites.py +++ b/tools/splat_ext/pm_sprites.py @@ -101,22 +101,22 @@ LIST_END_BYTES = b"\xFF\xFF\xFF\xFF" def indent(elem, level=0): - i = "\n" + level*" " + i = "\n" + level * " " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: - indent(elem, level+1) + indent(elem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i + elem.tail = i -def pretty_print_xml(tree : ET.ElementTree, path : Path): +def pretty_print_xml(tree: ET.ElementTree, path: Path): root = tree.getroot() indent(root) xml_str = ET.tostring(root, encoding="unicode") @@ -136,9 +136,7 @@ class RasterTableEntry: raster_bytes: bytes = field(default_factory=bytes) palette: Optional[bytes] = None - def write_png( - self, raster_buffer: bytes, path: Path, palette: Optional[bytes] = None - ): + def write_png(self, raster_buffer: bytes, path: Path, palette: Optional[bytes] = None): if self.height == 0 or self.width == 0: raise ValueError("Raster size has not been set") @@ -149,9 +147,7 @@ class RasterTableEntry: raise ValueError("Palette has not been set") if self.raster_bytes is not None: - self.raster_bytes = raster_buffer[ - self.offset : self.offset + (self.width * self.height // 2) - ] + self.raster_bytes = raster_buffer[self.offset : self.offset + (self.width * self.height // 2)] img = CI4(self.raster_bytes, self.width, self.height) img.set_palette(palette) @@ -264,9 +260,7 @@ class PlayerSprite: ) -def extract_raster_table_entries( - data: bytes, raster_sets: List[PlayerSpriteRasterSet] -) -> Dict[int, RasterTableEntry]: +def extract_raster_table_entries(data: bytes, raster_sets: List[PlayerSpriteRasterSet]) -> Dict[int, RasterTableEntry]: ret: Dict[int, RasterTableEntry] = {} current_section_pos = 0 current_section = 0 @@ -297,9 +291,7 @@ def extract_raster_table_entries( return ret -def extract_sprites( - yay0_data: bytes, raster_sets: List[PlayerSpriteRasterSet] -) -> List[PlayerSprite]: +def extract_sprites(yay0_data: bytes, raster_sets: List[PlayerSpriteRasterSet]) -> List[PlayerSprite]: yay0_splits = [] for i in range(14): yay0_splits.append(int.from_bytes(yay0_data[i * 4 : i * 4 + 4], "big")) @@ -375,9 +367,7 @@ def write_player_xmls( raster_table_entry_dict: Dict[int, RasterTableEntry], raster_names: List[str], ) -> None: - def get_sprite_name_from_offset( - offset: int, offsets: List[int], names: List[str] - ) -> str: + def get_sprite_name_from_offset(offset: int, offsets: List[int], names: List[str]) -> str: return names[offsets.index(offset)] sprite_idx = 0 @@ -426,9 +416,7 @@ def write_player_xmls( back_raster = cur_sprite_back.rasters[i] if back_raster.is_special: - raster_attributes[ - "special" - ] = f"{back_raster.width & 0xFF:X},{back_raster.height & 0xFF:X}" + raster_attributes["special"] = f"{back_raster.width & 0xFF:X},{back_raster.height & 0xFF:X}" else: back_name_offset = raster_sets[sprite_idx + 1].raster_offsets[i] raster_attributes[ @@ -483,9 +471,7 @@ def write_player_xmls( ) for anim in comp.animations: - ET.SubElement( - Component, anim.__class__.__name__, anim.get_attributes() - ) + ET.SubElement(Component, anim.__class__.__name__, anim.get_attributes()) xml = ET.ElementTree(SpriteSheet) pretty_print_xml(xml, out_path / f"{cur_sprite_name}.xml") @@ -547,12 +533,8 @@ def write_player_palettes( if pal_name not in dumped_palettes: offset = PLAYER_PAL_TO_RASTER[pal_name] if pal_name not in PLAYER_PAL_TO_RASTER: - print( - f"WARNING: Palette {pal_name} has no specified raster, not dumping!" - ) - raster_table_entry_dict[offset].write_png( - raster_data, path / (pal_name + ".png"), palette - ) + print(f"WARNING: Palette {pal_name} has no specified raster, not dumping!") + raster_table_entry_dict[offset].write_png(raster_data, path / (pal_name + ".png"), palette) ########### @@ -606,12 +588,8 @@ class NpcSprite: @staticmethod def from_bytes(data: bytearray): - image_offsets = read_offset_list( - data[int.from_bytes(data[0:4], byteorder="big") :] - ) - palette_offsets = read_offset_list( - data[int.from_bytes(data[4:8], byteorder="big") :] - ) + image_offsets = read_offset_list(data[int.from_bytes(data[0:4], byteorder="big") :]) + palette_offsets = read_offset_list(data[int.from_bytes(data[4:8], byteorder="big") :]) max_components = int.from_bytes(data[8:0xC], byteorder="big") num_variations = int.from_bytes(data[0xC:0x10], byteorder="big") animation_offsets = read_offset_list(data[0x10:]) @@ -683,11 +661,7 @@ class NpcSprite: ) for i, palette in enumerate(self.palettes): - name = ( - self.palette_names[i] - if (self.palette_names and i < len(self.palette_names)) - else f"Pal{i:02X}" - ) + name = self.palette_names[i] if (self.palette_names and i < len(self.palette_names)) else f"Pal{i:02X}" if i in palette_to_raster: img = palette_to_raster[i][0] @@ -710,9 +684,7 @@ class NpcSprite: AnimationList, "Animation", { - "name": self.animation_names[i] - if self.animation_names - else f"Anim{i:02X}", + "name": self.animation_names[i] if self.animation_names else f"Anim{i:02X}", }, ) @@ -727,9 +699,7 @@ class NpcSprite: ) for anim in comp.animations: - ET.SubElement( - Component, anim.__class__.__name__, anim.get_attributes() - ) + ET.SubElement(Component, anim.__class__.__name__, anim.get_attributes()) xml = ET.ElementTree(SpriteSheet) pretty_print_xml(xml, path / "SpriteSheet.xml") @@ -739,9 +709,7 @@ class N64SegPm_sprites(N64Segment): DEFAULT_NPC_SPRITE_NAMES = [f"{i:02X}" for i in range(0xEA)] def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml) -> None: - super().__init__( - rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml - ) + super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml) with (Path(__file__).parent / f"npc_sprite_names.yaml").open("r") as f: self.npc_cfg = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader) @@ -752,9 +720,7 @@ class N64SegPm_sprites(N64Segment): def out_path(self): return options.opts.asset_path / "sprite" / "sprites" - def split_player( - self, build_date: str, player_raster_data: bytes, player_yay0_data: bytes - ) -> None: + def split_player(self, build_date: str, player_raster_data: bytes, player_yay0_data: bytes) -> None: player_sprite_cfg = self.player_cfg["player_sprites"] player_raster_names: List[str] = self.player_cfg["player_rasters"] @@ -854,9 +820,7 @@ class N64SegPm_sprites(N64Segment): npc_yay0_offset = int.from_bytes(sprite_in_bytes[0x18:0x1C], "big") + 0x10 sprite_end_offset = int.from_bytes(sprite_in_bytes[0x1C:0x20], "big") + 0x10 - player_raster_data: bytes = sprite_in_bytes[ - player_raster_offset:player_yay0_offset - ] + player_raster_data: bytes = sprite_in_bytes[player_raster_offset:player_yay0_offset] player_yay0_data: bytes = sprite_in_bytes[player_yay0_offset:npc_yay0_offset] npc_yay0_data: bytes = sprite_in_bytes[npc_yay0_offset:sprite_end_offset] @@ -869,14 +833,9 @@ class N64SegPm_sprites(N64Segment): src_paths = [options.opts.asset_path / "sprite"] # for NPC - src_paths += [ - options.opts.asset_path / "sprite" / "npc" / sprite_name - for sprite_name in self.npc_cfg - ] + src_paths += [options.opts.asset_path / "sprite" / "npc" / sprite_name for sprite_name in self.npc_cfg] - return [ - LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section()) - ] + return [LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section())] def cache(self): return (self.yaml, self.rom_end, self.player_cfg, self.npc_cfg) diff --git a/tools/splat_ext/sprite_common.py b/tools/splat_ext/sprite_common.py index 1284e39e6b..dea9b1ecb0 100644 --- a/tools/splat_ext/sprite_common.py +++ b/tools/splat_ext/sprite_common.py @@ -217,7 +217,6 @@ class AnimComponent: def size(self): return len(self.commands) - @staticmethod def parse_commands(command_list: List[int]) -> List[Animation]: ret: List[Animation] = [] @@ -329,10 +328,7 @@ class AnimComponent: x, y, z = struct.unpack(">hhh", data[6:12]) - commands = [ - int.from_bytes(d[0:2], byteorder="big", signed=False) - for d in iter_in_groups(commands_data, 2) - ] + commands = [int.from_bytes(d[0:2], byteorder="big", signed=False) for d in iter_in_groups(commands_data, 2)] return AnimComponent(x, y, z, commands) @property diff --git a/tools/splat_ext/tex_archives.py b/tools/splat_ext/tex_archives.py index 67cbd5e064..3531d97146 100644 --- a/tools/splat_ext/tex_archives.py +++ b/tools/splat_ext/tex_archives.py @@ -260,9 +260,7 @@ class TexImage: self.main_height, ) if self.main_fmt == FMT_CI: - self.main_img.palette = self.get_n64_pal( - texbuf, self.main_fmt, self.main_depth - ) + self.main_img.palette = self.get_n64_pal(texbuf, self.main_fmt, self.main_depth) # main img + mipmaps elif self.extra_tiles == TILES_MIPMAPS: self.has_mipmaps = True @@ -282,9 +280,7 @@ class TexImage: break mmw = self.main_width // divisor mmh = self.main_height // divisor - mipmap = self.get_n64_img( - texbuf, self.main_fmt, self.main_depth, mmw, mmh - ) + mipmap = self.get_n64_img(texbuf, self.main_fmt, self.main_depth, mmw, mmh) self.mipmaps.append(mipmap) divisor = divisor * 2 @@ -334,13 +330,9 @@ class TexImage: pal = self.get_n64_pal(texbuf, self.main_fmt, self.main_depth) self.main_img.palette = pal # read aux - self.aux_img = self.get_n64_img( - texbuf, self.aux_fmt, self.aux_depth, self.aux_width, self.aux_height - ) + self.aux_img = self.get_n64_img(texbuf, self.aux_fmt, self.aux_depth, self.aux_width, self.aux_height) if self.aux_fmt == FMT_CI: - self.aux_img.palette = self.get_n64_pal( - texbuf, self.aux_fmt, self.aux_depth - ) + self.aux_img.palette = self.get_n64_pal(texbuf, self.aux_fmt, self.aux_depth) # constructs a dictionary entry for the tex archive for this texture def get_json_entry(self): @@ -418,9 +410,7 @@ class TexImage: return (r << 11) | (g << 6) | (b << 1) | a - (out_img, out_w, out_h) = Converter( - mode=fmt_str.lower(), infile=img_file, flip_y=True - ).convert() + (out_img, out_w, out_h) = Converter(mode=fmt_str.lower(), infile=img_file, flip_y=True).convert() out_pal = bytearray() if fmt_str == "CI4" or fmt_str == "CI8": diff --git a/tools/splat_ext/vtx_common.py b/tools/splat_ext/vtx_common.py index c6c23e4b49..bdfa05815f 100644 --- a/tools/splat_ext/vtx_common.py +++ b/tools/splat_ext/vtx_common.py @@ -1,5 +1,6 @@ from segtypes.n64.vtx import N64SegVtx + class N64SegVtx_common(N64SegVtx): def format_sym_name(self, sym): return f"N({sym.name[7:]})" diff --git a/tools/star_rod_enum_to_decomp.py b/tools/star_rod_enum_to_decomp.py index 0e9874325a..2eed0cfa4e 100755 --- a/tools/star_rod_enum_to_decomp.py +++ b/tools/star_rod_enum_to_decomp.py @@ -12,11 +12,11 @@ def create_enum(file_content, prefix, ordering): max_size = 0 if ordering: - for (key, value) in re.findall(r'(\S+)\s+=\s+(\S+)', file_content): + for key, value in re.findall(r"(\S+)\s+=\s+(\S+)", file_content): if len(key) > max_size: max_size = len(key) else: - for (key, value) in re.findall(r'(\S+)\s+=\s+(\S+)', file_content): + for key, value in re.findall(r"(\S+)\s+=\s+(\S+)", file_content): if len(value) > max_size: max_size = len(value) @@ -25,15 +25,25 @@ def create_enum(file_content, prefix, ordering): else: prefix = "" - for (key, value) in re.findall(r'(\S+)\s+=\s+(\S+)', file_content): + for key, value in re.findall(r"(\S+)\s+=\s+(\S+)", file_content): if ordering: - key = '_'.join(re.sub(r'([A-Z]{1,2})', r' \1', key).split()).replace("N_P_C", "NPC").replace("__", "_").replace("-", "") - key = prefix.upper() + '_{:<{width}}'.format(key, width=max_size + 2).upper() - ret += " " + key + " = 0x" + '{:>{fill}{width}}'.format(value, fill=0, width=8) + ",\n" + key = ( + "_".join(re.sub(r"([A-Z]{1,2})", r" \1", key).split()) + .replace("N_P_C", "NPC") + .replace("__", "_") + .replace("-", "") + ) + key = prefix.upper() + "_{:<{width}}".format(key, width=max_size + 2).upper() + ret += " " + key + " = 0x" + "{:>{fill}{width}}".format(value, fill=0, width=8) + ",\n" else: - value = '_'.join(re.sub(r'([A-Z]{1,2})', r' \1', value).split()).replace("N_P_C", "NPC").replace("__", "_").replace("-", "") - value = prefix.upper() + '_{:<{width}}'.format(value, width=max_size + 2).upper() - ret += " " + value + " = 0x" + '{:>{fill}{width}}'.format(key, fill=0, width=8) + ",\n" + value = ( + "_".join(re.sub(r"([A-Z]{1,2})", r" \1", value).split()) + .replace("N_P_C", "NPC") + .replace("__", "_") + .replace("-", "") + ) + value = prefix.upper() + "_{:<{width}}".format(value, width=max_size + 2).upper() + ret += " " + value + " = 0x" + "{:>{fill}{width}}".format(key, fill=0, width=8) + ",\n" ret += "};\n" @@ -51,9 +61,9 @@ def single_translation(file_path): print("File not found at the given path.") return "t", "y" - enum_namespace = re.search(r'(\w*)\s+%\s+namespace', file_content).group(1) - enum_name = re.search(r'(\w*)\s+%\s+library name', file_content).group(1) - reversed_order = re.search(r'(\w*)\s+%\s+reversed', file_content).group(1) + enum_namespace = re.search(r"(\w*)\s+%\s+namespace", file_content).group(1) + enum_name = re.search(r"(\w*)\s+%\s+library name", file_content).group(1) + reversed_order = re.search(r"(\w*)\s+%\s+reversed", file_content).group(1) ret += "enum " + enum_name + " {\n" if reversed_order == "true": @@ -76,9 +86,9 @@ def recursive_translation(database_path): except: continue - enum_namespace = re.search(r'(\w*)\s+%\s+namespace', file_content).group(1) - enum_name = re.search(r'(\w*)\s+%\s+library name', file_content).group(1) - reversed_order = re.search(r'(\w*)\s+%\s+reversed', file_content).group(1) + enum_namespace = re.search(r"(\w*)\s+%\s+namespace", file_content).group(1) + enum_name = re.search(r"(\w*)\s+%\s+library name", file_content).group(1) + reversed_order = re.search(r"(\w*)\s+%\s+reversed", file_content).group(1) ret += "enum " + enum_name + " {\n" if reversed_order == "true": @@ -106,9 +116,17 @@ def main(args): file.close() -parser = argparse.ArgumentParser(description='Convert a StarRod enum into an enum that is in a decomp compatible format') +parser = argparse.ArgumentParser( + description="Convert a StarRod enum into an enum that is in a decomp compatible format" +) parser.add_argument("query", help="StarRod enum file or folder") -parser.add_argument("-r", "--recursive", help="recursively convert all files to enums", type=bool, required=False) +parser.add_argument( + "-r", + "--recursive", + help="recursively convert all files to enums", + type=bool, + required=False, +) args = parser.parse_args() diff --git a/tools/star_rod_idx_to_c.py b/tools/star_rod_idx_to_c.py index 16bf191d57..6098ce484f 100755 --- a/tools/star_rod_idx_to_c.py +++ b/tools/star_rod_idx_to_c.py @@ -24,12 +24,16 @@ INCLUDES_NEEDED["npcs"] = {} INCLUDES_NEEDED["sprites"] = set() INCLUDES_NEEDED["tattle"] = [] + def get_flag_name(arg): - v = arg - 2**32 # convert to s32 + v = arg - 2**32 # convert to s32 if v > -250000000: - if v <= -220000000: return str((v + 230000000) / 1024) - elif v <= -200000000: return f"ArrayFlag({v + 210000000})" - elif v <= -180000000: return f"ArrayVar({v + 190000000})" + if v <= -220000000: + return str((v + 230000000) / 1024) + elif v <= -200000000: + return f"ArrayFlag({v + 210000000})" + elif v <= -180000000: + return f"ArrayVar({v + 190000000})" elif v <= -160000000: if v + 170000000 == 0: return "GB_StoryProgress" @@ -37,13 +41,20 @@ def get_flag_name(arg): return "GB_WorldLocation" else: return f"GameByte({v + 170000000})" - elif v <= -140000000: return f"AreaByte({v + 150000000})" - elif v <= -120000000: return f"GameFlag({v + 130000000})" - elif v <= -100000000: return f"AreaFlag({v + 110000000})" - elif v <= -80000000: return f"MapFlag({v + 90000000})" - elif v <= -60000000: return f"LocalFlag({v + 70000000})" - elif v <= -40000000: return f"MapVar({v + 50000000})" - elif v <= -20000000: return f"LocalVar({v + 30000000})" + elif v <= -140000000: + return f"AreaByte({v + 150000000})" + elif v <= -120000000: + return f"GameFlag({v + 130000000})" + elif v <= -100000000: + return f"AreaFlag({v + 110000000})" + elif v <= -80000000: + return f"MapFlag({v + 90000000})" + elif v <= -60000000: + return f"LocalFlag({v + 70000000})" + elif v <= -40000000: + return f"MapVar({v + 50000000})" + elif v <= -20000000: + return f"LocalVar({v + 30000000})" if arg == 0xFFFFFFFF: return "-1" @@ -54,6 +65,7 @@ def get_flag_name(arg): else: return f"{arg}" + def get_function_list(area_name, map_name, rom_offset): map_file = (Path(__file__).parent.parent / "ver" / "current" / "build" / "papermario.map").read_text().splitlines() i = 0 @@ -70,7 +82,7 @@ def get_function_list(area_name, map_name, rom_offset): vram = int(vram, 16) func = func.replace(f"{map_name}_", "") if func.count("_") == 2: - func = func.rsplit("_",1)[0] + func = func.rsplit("_", 1)[0] functions[vram] = func i += 1 if firstFind: @@ -79,6 +91,7 @@ def get_function_list(area_name, map_name, rom_offset): return functions + def get_include_list(area_name, map_name): include_path = Path(__file__).parent.parent / "src" / "world" / "common" includes = set() @@ -87,16 +100,18 @@ def get_include_list(area_name, map_name): with open(file, "r", encoding="utf8") as f: for line in f: if (line.startswith("void N(") or line.startswith("ApiStatus N(")) and "{" in line: - func_name = line.split("N(",1)[1].split(")",1)[0] + func_name = line.split("N(", 1)[1].split(")", 1)[0] includes.add(func_name) return includes + def read_enum(num: int, constants_name: str) -> str: if num in disasm_script.CONSTANTS[constants_name]: return disasm_script.CONSTANTS[constants_name][num] else: return num + def read_flags(flags: int, constants_name: str) -> str: enabled = [] for x in range(32): @@ -115,6 +130,7 @@ def read_flags(flags: int, constants_name: str) -> str: return " | ".join(enabled) + def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str: if addr == 0: return "NULL" @@ -126,6 +142,7 @@ def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str: else: return f"(void*) 0x{addr:08X}" + def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace=None): global INCLUDES_NEEDED, INCLUDED out = "" @@ -139,7 +156,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace def transform_symbol_name(symbol): if namespace and symbol.startswith(namespace + "_"): - return "N(" + symbol[len(namespace)+1:] + ")" + return "N(" + symbol[len(namespace) + 1 :] + ")" return symbol while len(midx) > 0: @@ -148,7 +165,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace name = struct["name"] print(name, file=sys.stderr) - #INCLUDED["functions"].add(name) + # INCLUDED["functions"].add(name) if comments: out += f"// {romstart+struct['start']:X}-{romstart+struct['end']:X} (VRAM: {struct['vaddr']:X})\n" @@ -165,18 +182,23 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace main_script_name = name # For PlayMusic script if using a separate header file - #if afterHeader: + # if afterHeader: # INCLUDES_NEEDED["forward"].append(f"EvtScript " + name + ";") # afterHeader = False - disasm_script.LOCAL_WORDS = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + disasm_script.LOCAL_WORDS = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] script_text = disasm_script.ScriptDisassembler( - bytes, name, symbol_map, romstart, INCLUDES_NEEDED, INCLUDED, + bytes, + name, + symbol_map, + romstart, + INCLUDES_NEEDED, + INCLUDED, transform_symbol_name=transform_symbol_name, use_script_lib=False, ).disassemble() if "EVS_ShakeTree" in name or "EVS_SearchBush" in name: - symbol_map[struct["vaddr"]][0][1] = name.split("_",1)[0] + ")" + symbol_map[struct["vaddr"]][0][1] = name.split("_", 1)[0] + ")" if not treePrint: out += f"=======================================\n" out += f"==========BELOW foliage.inc.c==========\n" @@ -188,9 +210,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace script_text = script_text.splitlines() walkDistance = exitIdx = map_ = entryIdx = "" if "UseExitHeading" in script_text[2]: - walkDistance, exitIdx = script_text[2].split("(",1)[1].split(")",1)[0].split(",") + walkDistance, exitIdx = script_text[2].split("(", 1)[1].split(")", 1)[0].split(",") if "GotoMap" in script_text[4]: - map_, entryIdx = script_text[4].split("(",1)[1].split(")",1)[0].split(",") + map_, entryIdx = script_text[4].split("(", 1)[1].split(")", 1)[0].split(",") if walkDistance and exitIdx and map_ and entryIdx: out += f"EvtScript {name} = EVT_EXIT_WALK({walkDistance}, {exitIdx}, {map_}, {entryIdx});\n" else: @@ -210,8 +232,11 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace z = [] w = [] for _ in range(entry_count): - a,b,c,d = unpack_from(">ffff", entry_list, pos) - x.append(f"{a:.01f}"); y.append(f"{b:.01f}"); z.append(f"{c:.01f}"); w.append(f"{d:.01f}") + a, b, c, d = unpack_from(">ffff", entry_list, pos) + x.append(f"{a:.01f}") + y.append(f"{b:.01f}") + z.append(f"{c:.01f}") + w.append(f"{d:.01f}") pos += 16 x_size = max([len(a) for a in x]) @@ -219,7 +244,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace z_size = max([len(a) for a in z]) w_size = max([len(a) for a in w]) - for a,b,c,d in zip(x,y,z,w): + for a, b, c, d in zip(x, y, z, w): out += f"\n {{ {a:>{x_size}}f, {b:>{y_size}}f, {c:>{z_size}}f, {d:>{w_size}}f }}," out += f"\n}};\n" @@ -233,16 +258,29 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace var_names = ["unk_00", "unk_24"] data = unpack_from(">4B", npcSettings, i) if not sum(data) == 0: - tmp_out += INDENT + f".{var_names[0] if i == 0 else var_names[1]} = {{ " + ", ".join(f"0x{x:02X}" for x in data) + f" }},\n" + tmp_out += ( + INDENT + + f".{var_names[0] if i == 0 else var_names[1]} = {{ " + + ", ".join(f"0x{x:02X}" for x in data) + + f" }},\n" + ) elif i == 0x4 or i == 0x28: var_names = ["height", "radius", "level", "unk_2A"] - for x,var in enumerate(unpack_from(">2h", npcSettings, i)): - var_name = var_names[x if i == 0x4 else x+2] + for x, var in enumerate(unpack_from(">2h", npcSettings, i)): + var_name = var_names[x if i == 0x4 else x + 2] if not var == 0: tmp_out += INDENT + f".{var_name} = {var},\n" elif i == 0x8: - var_names = ["otherAI", "onInteract", "ai", "onHit", "aux", "onDefeat", "flags"] - for x,var in enumerate(unpack_from(f">7I", npcSettings, i)): + var_names = [ + "otherAI", + "onInteract", + "ai", + "onHit", + "aux", + "onDefeat", + "flags", + ] + for x, var in enumerate(unpack_from(f">7I", npcSettings, i)): var_name = var_names[x] if not var == 0: if var == 0x80077F70: @@ -264,8 +302,20 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace npcAISettings = bytes.read(struct["length"]) i = x = 0 - var_names = ["moveSpeed", "moveTime", "waitTime", "alertRadius", "unk_10", "unk_14", - "chaseSpeed", "unk_1C", "unk_20", "chaseRadius", "unk_28", "unk_2C"] + var_names = [ + "moveSpeed", + "moveTime", + "waitTime", + "alertRadius", + "unk_10", + "unk_14", + "chaseSpeed", + "unk_1C", + "unk_20", + "chaseRadius", + "unk_28", + "unk_2C", + ] while i < struct["length"]: var_f, var_i1, var_i2 = unpack_from(f">fii", npcAISettings, i) if not var_f == 0: @@ -274,7 +324,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace # account for X32 if var_names[x + 1] in ["unk_10", "unk_1C", "unk_28"]: if var_i1 < -100000 or var_i1 > 100000: - tmp_out += INDENT + f".{var_names[x + 1]} = {{ .f = {unpack_from('>f', npcAISettings, i+4)[0]:.01f}f }},\n" + tmp_out += ( + INDENT + + f".{var_names[x + 1]} = {{ .f = {unpack_from('>f', npcAISettings, i+4)[0]:.01f}f }},\n" + ) else: tmp_out += INDENT + f".{var_names[x + 1]} = {{ .s = {var_i1} }},\n" else: @@ -289,32 +342,56 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace elif struct["type"] == "NpcGroup": staticNpc = bytes.read(struct["length"]) curr_base = 0 - numNpcs = struct['length'] // 0x1F0 + numNpcs = struct["length"] // 0x1F0 tmp_out = f"NpcData {name}" + ("[]" if numNpcs > 1 else "") + f" = {{\n" for z in range(numNpcs): i = 0 - var_names = ["id", "settings", "pos", "flags", - "init", "unk_1C", "yaw", "dropFlags", - "itemDropChance", "itemDrops", "heartDrops", "flowerDrops", - "minCoinBonus", "maxCoinBonus", "movement", "animations", - "unk_1E0", "extraAnimations", "tattle"] + var_names = [ + "id", + "settings", + "pos", + "flags", + "init", + "unk_1C", + "yaw", + "dropFlags", + "itemDropChance", + "itemDrops", + "heartDrops", + "flowerDrops", + "minCoinBonus", + "maxCoinBonus", + "movement", + "animations", + "unk_1E0", + "extraAnimations", + "tattle", + ] if numNpcs > 1: tmp_out += INDENT + f"{{\n" - INDENT = INDENT*2 + INDENT = INDENT * 2 while i < 0x1F0: if i == 0x0 or i == 0x24: var_name = var_names[0] if i == 0x0 else var_names[6] - var = unpack_from(f">i", staticNpc, curr_base+i)[0] + var = unpack_from(f">i", staticNpc, curr_base + i)[0] if var_name == "id": tmp_out += INDENT + f".{var_name} = {disasm_script.CONSTANTS['MAP_NPCS'][var]},\n" else: tmp_out += INDENT + f".{var_name} = {var},\n" elif i == 0x4 or i == 0x14 or i == 0x18 or i == 0x1E8: - var_name = var_names[1] if i == 0x4 else var_names[3] if i == 0x14 else var_names[4] if i == 0x18 else var_names[17] - addr = unpack_from(f">I", staticNpc, curr_base+i)[0] + var_name = ( + var_names[1] + if i == 0x4 + else var_names[3] + if i == 0x14 + else var_names[4] + if i == 0x18 + else var_names[17] + ) + addr = unpack_from(f">I", staticNpc, curr_base + i)[0] if not addr == 0: if var_name != "flags" and addr in symbol_map: if var_name == "extraAnimations": @@ -338,17 +415,17 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace enabled.append(0) tmp_out += INDENT + f".{var_name} = " + " | ".join(enabled) + f",\n" elif i == 0x8: - pos = unpack_from(f">fff", staticNpc, curr_base+i) + pos = unpack_from(f">fff", staticNpc, curr_base + i) if not sum(pos) == 0: tmp_out += INDENT + f".pos = {{ {pos[0]:.01f}f, {pos[1]:.01f}f, {pos[2]:.01f}f }},\n" elif i == 0x1C or i == 0x1E0: var_name = var_names[5] if i == 0x1C else var_names[16] - data = unpack_from(f">8B", staticNpc, curr_base+i) + data = unpack_from(f">8B", staticNpc, curr_base + i) if not sum(data) == 0: tmp_out += INDENT + f".{var_name} = {{ " + ", ".join(f"{x:02X}" for x in data) + f"}},\n" elif i == 0x28 or i == 0x29: var_name = var_names[7] if i == 0x28 else var_names[8] - var = unpack_from(f">b", staticNpc, curr_base+i)[0] + var = unpack_from(f">b", staticNpc, curr_base + i)[0] if not var == 0: if var_name == "dropFlags": tmp_out += INDENT + f".{var_name} = 0x{abs(var):02X},\n" @@ -358,10 +435,14 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace var_name = var_names[9] tmp_tmp = "" for x in range(8): - item, weight, unk_08 = unpack_from(f">3h", staticNpc, curr_base+i) + item, weight, unk_08 = unpack_from(f">3h", staticNpc, curr_base + i) if not (item == 0 and weight == 0 and unk_08 == 0): - item = disasm_script.CONSTANTS["ItemIDs"][item] if item in disasm_script.CONSTANTS["ItemIDs"] else f"{item}" - tmp_tmp += INDENT*2 + f"{{ {item}, {weight}, {unk_08} }},\n" + item = ( + disasm_script.CONSTANTS["ItemIDs"][item] + if item in disasm_script.CONSTANTS["ItemIDs"] + else f"{item}" + ) + tmp_tmp += INDENT * 2 + f"{{ {item}, {weight}, {unk_08} }},\n" i += 0x6 if tmp_tmp: @@ -374,7 +455,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace var_name = var_names[10] if i == 0x5A else var_names[11] drops = [] for x in range(8): - cutoff, generalChance, attempts, chancePerAttempt = unpack_from(f">4h", staticNpc, curr_base+i) + ( + cutoff, + generalChance, + attempts, + chancePerAttempt, + ) = unpack_from(f">4h", staticNpc, curr_base + i) if not (cutoff == 0 and generalChance == 0 and attempts == 0 and chancePerAttempt == 0): drops.append([cutoff, generalChance, attempts, chancePerAttempt]) i += 0x8 @@ -388,10 +474,16 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace tmp_out += f"GENEROUS_HEART_DROPS({drops[0][2]})" elif round(drops[0][1] / 327.67, 2) == 80 and round(drops[0][3] / 327.67, 2) == 60: tmp_out += f"GENEROUS_WHEN_LOW_HEART_DROPS({drops[0][2]})" - elif round(drops[0][0] / 327.67, 2) == 100 and round(drops[0][1] / 327.67, 2) == 0 and round(drops[0][2] / 327.67, 2) == 0: + elif ( + round(drops[0][0] / 327.67, 2) == 100 + and round(drops[0][1] / 327.67, 2) == 0 + and round(drops[0][2] / 327.67, 2) == 0 + ): tmp_out += f"NO_DROPS" else: - print(f"Unknown heart drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}") + print( + f"Unknown heart drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}" + ) exit() else: if round(drops[0][1] / 327.67, 2) == 50 and round(drops[0][3] / 327.67, 2) == 40: @@ -400,56 +492,70 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace tmp_out += f"GENEROUS_WHEN_LOW_FLOWER_DROPS({drops[0][2]})" elif round(drops[0][1] / 327.67, 2) == 40 and round(drops[0][3] / 327.67, 2) == 40: tmp_out += f"REDUCED_FLOWER_DROPS({drops[0][2]})" - elif round(drops[0][0] / 327.67, 2) == 100 and round(drops[0][1] / 327.67, 2) == 0 and round(drops[0][2] / 327.67, 2) == 0: + elif ( + round(drops[0][0] / 327.67, 2) == 100 + and round(drops[0][1] / 327.67, 2) == 0 + and round(drops[0][2] / 327.67, 2) == 0 + ): tmp_out += f"NO_DROPS" else: - print(f"Unknown flower drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}") + print( + f"Unknown flower drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}" + ) exit() tmp_out += f",\n" elif i == 0xDA or i == 0xDC: var_name = var_names[12] if i == 0xDA else var_names[13] - var = unpack_from(">h", staticNpc, curr_base+i)[0] + var = unpack_from(">h", staticNpc, curr_base + i)[0] if not var == 0: tmp_out += INDENT + f".{var_name} = {var},\n" elif i == 0xE0: - data = unpack_from(">48i", staticNpc, curr_base+i) + data = unpack_from(">48i", staticNpc, curr_base + i) if not sum(data) == 0: end_pos = len(data) - for x,datum in enumerate(data): + for x, datum in enumerate(data): if not datum == 0: end_pos = x - tmp_out += INDENT + f".territory = { .temp = {{ " + ", ".join(f"{x}" for x in data[:end_pos+1]) + f" }}},\n" + tmp_out += ( + INDENT + + f".territory = {{ .temp = {{ " + + ", ".join(f"{x}" for x in data[: end_pos + 1]) + + f" }}}},\n" + ) elif i == 0x1A0: tmp_out += INDENT + f".{var_names[15]} = {{\n" for x in range(16): - anim = unpack_from(">I", staticNpc, curr_base+i)[0] + anim = unpack_from(">I", staticNpc, curr_base + i)[0] if not anim == 0: - sprite_id = (anim & 0x00FF0000) >> 16 + sprite_id = (anim & 0x00FF0000) >> 16 palette_id = (anim & 0x0000FF00) >> 8 - anim_id = (anim & 0x000000FF) >> 0 - sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"] + anim_id = (anim & 0x000000FF) >> 0 + sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"] palette = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id] - anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id] + anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id] if numNpcs > 1: tmp_out += INDENT + " " + f"NPC_ANIM_{sprite}_{palette}_{anim},\n" else: - tmp_out += INDENT*2 + f"NPC_ANIM_{sprite}_{palette}_{anim},\n" + tmp_out += INDENT * 2 + f"NPC_ANIM_{sprite}_{palette}_{anim},\n" INCLUDES_NEEDED["sprites"].add(sprite) i += 4 tmp_out += INDENT + f"}},\n" i -= 1 elif i == 0x1EC: - var = unpack_from(">I", staticNpc, curr_base+i)[0] + var = unpack_from(">I", staticNpc, curr_base + i)[0] if not var == 0: - tmp_out += INDENT + f".{var_names[18]} = MESSAGE_ID(0x{(var & 0xFF0000) >> 16:02X}, 0x{var & 0xFFFF:04X}),\n" + tmp_out += ( + INDENT + + f".{var_names[18]} = MESSAGE_ID(0x{(var & 0xFF0000) >> 16:02X}, 0x{var & 0xFFFF:04X}),\n" + ) i += 1 if numNpcs > 1: - INDENT = INDENT[:len(INDENT)//2] + INDENT = INDENT[: len(INDENT) // 2] tmp_out += INDENT + f"}},\n" - if z+1 == numNpcs: + if z + 1 == numNpcs: tmp_out += "};\n" curr_base += 0x1F0 @@ -464,12 +570,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace if anim == 0xFFFFFFFF: tmp_out += INDENT + f"ANIM_LIST_END,\n" elif not anim == 0: - sprite_id = (anim & 0x00FF0000) >> 16 + sprite_id = (anim & 0x00FF0000) >> 16 palette_id = (anim & 0x0000FF00) >> 8 - anim_id = (anim & 0x000000FF) >> 0 - sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"] + anim_id = (anim & 0x000000FF) >> 0 + sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"] palette = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id] - anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id] + anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id] tmp_out += INDENT + f"NPC_ANIM_{sprite}_{palette}_{anim},\n" INCLUDES_NEEDED["sprites"].add(sprite) i += 4 @@ -487,7 +593,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace battle_b = (battle & 0x00FF0000) >> 16 battle_c = (battle & 0x0000FF00) >> 8 battle_d = (battle & 0x000000FF) >> 0 - tmp_out += INDENT + f"NPC_GROUP({symbol_map[npcs][0][1]}, BATTLE_ID({battle_a}, {battle_b}, {battle_c}, {battle_d})),\n" + tmp_out += ( + INDENT + + f"NPC_GROUP({symbol_map[npcs][0][1]}, BATTLE_ID({battle_a}, {battle_b}, {battle_c}, {battle_d})),\n" + ) if symbol_map[npcs][0][1] not in INCLUDED["functions"]: INCLUDES_NEEDED["forward"].append(symbol_map[npcs][0][1]) i += 0xC @@ -502,7 +611,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace out += f" {disasm_script.CONSTANTS['ItemIDs'][item]},\n" out += f"}};\n" elif struct["type"] == "TreeDropList": - new_name = "N(" + name.split('_',1)[1][:-1].lower() + "_Drops)" + new_name = "N(" + name.split("_", 1)[1][:-1].lower() + "_Drops)" symbol_map[struct["vaddr"]][0][1] = new_name out += f"FoliageDropList {new_name} = {{\n" @@ -518,7 +627,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace pos = 4 for _ in range(count): entry = list(unpack_from(">7I", data, pos)) - pos += 7*4 + pos += 7 * 4 entry[1] = entry[1] - 0x100000000 if entry[1] >= 0x80000000 else entry[1] entry[2] = entry[2] - 0x100000000 if entry[2] >= 0x80000000 else entry[2] @@ -532,9 +641,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace out += f"{INDENT * 3}.pos = {{ {entry[1]}, {entry[2]}, {entry[3]} }},\n" if entry[4] != 0: out += f"{INDENT * 3}.spawnMode = 0x{entry[4]:X},\n" - if flag1 != '0': + if flag1 != "0": out += f"{INDENT * 3}.pickupFlag = {flag1},\n" - if flag2 != '0': + if flag2 != "0": out += f"{INDENT * 3}.spawnFlag = {flag2},\n" out += f"{INDENT * 2}}},\n" @@ -546,7 +655,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace elif struct["type"] == "TreeModelList" or struct["type"] == "TreeEffectVectors": isModelList = struct["type"] == "TreeModelList" - name_parts = name.split('_') + name_parts = name.split("_") if isModelList: new_name = "N(" + name_parts[1].lower() + "_" + name_parts[2] else: @@ -590,7 +699,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace entry[1] = entry[1] - 0x100000000 if entry[1] >= 0x80000000 else entry[1] entry[2] = entry[2] - 0x100000000 if entry[2] >= 0x80000000 else entry[2] - pos += 3*4 + pos += 3 * 4 out += f"{INDENT * 2}{{ {entry[0]}, {entry[1]}, {entry[2]} }},\n" @@ -600,10 +709,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace out += f"}};\n" elif struct["type"] == "SearchBushEvent": - new_name = "N(" + name.split('_',1)[1].lower() + new_name = "N(" + name.split("_", 1)[1].lower() symbol_map[struct["vaddr"]][0][1] = new_name - num = int(new_name.split("bush",1)[1][:-1]) + num = int(new_name.split("bush", 1)[1][:-1]) out += f"SearchBushConfig {new_name} = {{\n" data = bytes.read(struct["length"]) @@ -621,10 +730,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace out += f"}};\n" elif struct["type"] == "ShakeTreeEvent": - new_name = "N(" + name.split('_',1)[1].lower() + new_name = "N(" + name.split("_", 1)[1].lower() symbol_map[struct["vaddr"]][0][1] = new_name - num = int(new_name.split("tree",1)[1][:-1]) + num = int(new_name.split("tree", 1)[1][:-1]) out += f"ShakeTreeConfig {new_name} = {{\n" data = bytes.read(struct["length"]) @@ -656,20 +765,22 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace bytes.read(0x10) - main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3)) + main, entry_list, entry_count = unpack(">IIi", bytes.read(4 * 3)) out += f" .main = &N(main),\n" out += f" .entryList = &{entry_list_name},\n" out += f" .entryCount = ENTRY_COUNT({entry_list_name}),\n" bytes.read(0x1C) - bg,tattle = unpack(">II", bytes.read(4 * 2)) + bg, tattle = unpack(">II", bytes.read(4 * 2)) if bg == 0x80200000: out += f" .background = &gBackgroundImage,\n" elif bg != 0: raise Exception(f"unknown MapSettings background {bg:X}") - #out += f" .tattle = 0x{tattle:X},\n" - INCLUDES_NEEDED["tattle"].append(f"- [0x{(tattle & 0xFF0000) >> 16:02X}, 0x{tattle & 0xFFFF:04X}, {map_name}_tattle]") + # out += f" .tattle = 0x{tattle:X},\n" + INCLUDES_NEEDED["tattle"].append( + f"- [0x{(tattle & 0xFF0000) >> 16:02X}, 0x{tattle & 0xFFFF:04X}, {map_name}_tattle]" + ) out += f" .tattle = {{ MSG_{map_name}_tattle }},\n" out += f"}};\n" @@ -678,7 +789,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace bytes.read(struct["length"]) out += f"s32 {name}();\n" elif struct["type"] == "FloatTable": - vram = int(name.split("_",1)[1][:-1], 16) + vram = int(name.split("_", 1)[1][:-1], 16) name = f"N(D_{vram:X}_{(vram - 0x80240000) + romstart:X})" struct["name"] = name out += f"f32 {name}[] = {{" @@ -695,10 +806,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace if len(data) > 0: out += f"Vec3f {name}[] = {{\n" out += f"\t" - for i,pos in enumerate(range(0, len(data), 0xC)): + for i, pos in enumerate(range(0, len(data), 0xC)): x, y, z = unpack_from(">fff", data, pos) out += f" {{ {x:.01f}, {y:.01f}, {z:.01f} }}," - if (i+1) % 2 == 0: + if (i + 1) % 2 == 0: out += f"\n\t" out += f"\n}};\n" @@ -721,7 +832,6 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace else: out += f".actor = {actor}, " - if position in symbol_map: out += f".home = {{ .vec = &{symbol_map[position][0][1]} }}" @@ -829,11 +939,11 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace element = disasm_script.CONSTANTS["Statuses"][element] - value = (anim & 0x00FFFFFF) + value = anim & 0x00FFFFFF if value in disasm_script.CONSTANTS["NPC_SPRITE"]: - INCLUDES_NEEDED["sprites"].add(disasm_script.CONSTANTS['NPC_SPRITE'][str(value) + ".h"]) - anim = disasm_script.CONSTANTS['NPC_SPRITE'][value] + INCLUDES_NEEDED["sprites"].add(disasm_script.CONSTANTS["NPC_SPRITE"][str(value) + ".h"]) + anim = disasm_script.CONSTANTS["NPC_SPRITE"][value] else: anim = f"{anim:06X}" @@ -851,15 +961,15 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace out += INDENT + "{\n" out += INDENT + INDENT + f".flags = {read_flags(d[0], 'ActorPartFlags')},\n" out += INDENT + INDENT + f".index = {d[1]},\n" - out += INDENT + INDENT + f".posOffset = {{ {d[2]}, {d[3]}, {d[4]} }},\n" - out += INDENT + INDENT + f".targetOffset = {{ {d[5]}, {d[6]} }},\n" - out += INDENT + INDENT + f".opacity = {d[7]},\n" - out += INDENT + INDENT + f".idleAnimations = N(IdleAnimations_{d[8]:08X}),\n" - out += INDENT + INDENT + f".defenseTable = N(DefenseTable_{d[9]:08X}),\n" - out += INDENT + INDENT + f".eventFlags = {read_flags(d[10], 'ActorEventFlags')},\n" - out += INDENT + INDENT + f".elementImmunityFlags = {read_flags(d[11], 'ElementImmunityFlags')},\n" - out += INDENT + INDENT + f".unk_1C = {d[12]},\n" - out += INDENT + INDENT + f".unk_1D = {d[13]},\n" + out += INDENT + INDENT + f".posOffset = {{ {d[2]}, {d[3]}, {d[4]} }},\n" + out += INDENT + INDENT + f".targetOffset = {{ {d[5]}, {d[6]} }},\n" + out += INDENT + INDENT + f".opacity = {d[7]},\n" + out += INDENT + INDENT + f".idleAnimations = N(IdleAnimations_{d[8]:08X}),\n" + out += INDENT + INDENT + f".defenseTable = N(DefenseTable_{d[9]:08X}),\n" + out += INDENT + INDENT + f".eventFlags = {read_flags(d[10], 'ActorEventFlags')},\n" + out += INDENT + INDENT + f".elementImmunityFlags = {read_flags(d[11], 'ElementImmunityFlags')},\n" + out += INDENT + INDENT + f".unk_1C = {d[12]},\n" + out += INDENT + INDENT + f".unk_1D = {d[13]},\n" out += INDENT + "},\n" out += f"}};\n" @@ -895,7 +1005,18 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace elif struct["type"] == "Stage": out += f"Stage NAMESPACE = {{\n" - texture, shape, hit, preBattle, postBattle, bg, unk_18, unk_1C, unk_20, unk_24 = unpack(">IIIIIIIIII", bytes.read(struct["length"])) + ( + texture, + shape, + hit, + preBattle, + postBattle, + bg, + unk_18, + unk_1C, + unk_20, + unk_24, + ) = unpack(">IIIIIIIIII", bytes.read(struct["length"])) if texture != 0: out += f" .texture = {symbol_map[texture][0][1]},\n" @@ -928,9 +1049,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace out += f" .unk_24 = {unk_24:X},\n" out += f"}};\n" - else: # unknown type of struct + else: # unknown type of struct if struct["name"].startswith("N(unk_802"): - vram = int(name.split("_",1)[1][:-1], 16) + vram = int(name.split("_", 1)[1][:-1], 16) name = f"N(D_{vram:X}_{(vram - 0x80240000) + romstart:X})" struct["name"] = name @@ -957,53 +1078,63 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace # end of data return out + def parse_midx(file, prefix="", vram=0x80240000): structs = [] for line in file.readlines(): s = line.split("#") if len(s) == 5 or len(s) == 6: - if s[0] == "$Start": continue - if s[0] == "$End": continue + if s[0] == "$Start": + continue + if s[0] == "$End": + continue - structs.append({ - "name": "N(" + prefix + name_struct(s[0]) + ")", - "type": s[1], - "start": int(s[2], 16), - "vaddr": int(s[3], 16), - "length": int(s[4], 16), - "end": int(s[2], 16) + int(s[4], 16), - }) + structs.append( + { + "name": "N(" + prefix + name_struct(s[0]) + ")", + "type": s[1], + "start": int(s[2], 16), + "vaddr": int(s[3], 16), + "length": int(s[4], 16), + "end": int(s[2], 16) + int(s[4], 16), + } + ) elif "Missing" in s: start = int(s[1], 16) end = int(s[2], 16) vaddr = start + vram - structs.append({ - "name": f"{prefix}unk_missing_{vaddr:X}", - "type": "Missing", - "start": start, - "vaddr": vaddr, - "length": end - start, - "end": end, - }) + structs.append( + { + "name": f"{prefix}unk_missing_{vaddr:X}", + "type": "Missing", + "start": start, + "vaddr": vaddr, + "length": end - start, + "end": end, + } + ) elif "Padding" in s: start = int(s[1], 16) end = int(s[2], 16) vaddr = start + vram - structs.append({ - "name": f"{prefix}pad_{start:X}", - "type": "Padding", - "start": start, - "vaddr": vaddr, - "length": end - start, - "end": end, - }) + structs.append( + { + "name": f"{prefix}pad_{start:X}", + "type": "Padding", + "start": start, + "vaddr": vaddr, + "length": end - start, + "end": end, + } + ) else: raise Exception(str(s)) structs.sort(key=lambda s: s["start"]) return structs + def name_struct(s): s = s[1:].replace("???", "unk") @@ -1032,10 +1163,11 @@ def name_struct(s): return s[0].lower() + s[1:] + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Converts split data to C using a Star Rod idx file") parser.add_argument("idxfile", help="Input .*idx file from Star Rod dump") - parser.add_argument("namespace", nargs='?', help="Value of NAMESPACE macro") + parser.add_argument("namespace", nargs="?", help="Value of NAMESPACE macro") parser.add_argument("--comments", action="store_true", help="Write offset/vaddr comments") args = parser.parse_args() @@ -1053,8 +1185,7 @@ if __name__ == "__main__": segment_name = f"battle/partners/{battle_area}" elif "/starpower/src/" in args.idxfile: segment_name = ( - f"battle/star/{battle_area}" - .replace("starstorm", "star_storm") + f"battle/star/{battle_area}".replace("starstorm", "star_storm") .replace("chillout", "chill_out") .replace("timeout", "time_out") .replace("upandaway", "up_and_away") @@ -1069,6 +1200,7 @@ if __name__ == "__main__": is_battle = True symbol_map = disasm_script.script_lib() + def add_to_symbol_map(addr, pair): if addr in symbol_map: symbol_map[addr].append(pair) @@ -1082,7 +1214,9 @@ if __name__ == "__main__": rom_offset = -1 for segment in splat_config["segments"]: - if isinstance(segment, dict) and (segment.get("dir") == segment_name or segment.get("name") == segment_name): + if isinstance(segment, dict) and ( + segment.get("dir") == segment_name or segment.get("name") == segment_name + ): rom_offset = segment["start"] vram = segment["vram"] break @@ -1102,11 +1236,11 @@ if __name__ == "__main__": with open(os.path.join(DIR, "../ver/current/baserom.z64"), "rb") as romfile: name_fixes = { - "script_NpcAI": "npcAI", - "aISettings": "npcAISettings", - "script_ExitWalk": "exitWalk", - "script_MakeEntities": "makeEntities", - } + "script_NpcAI": "npcAI", + "aISettings": "npcAISettings", + "script_ExitWalk": "exitWalk", + "script_MakeEntities": "makeEntities", + } total_npc_counts = {} for struct in midx: romfile.seek(struct["start"] + rom_offset) @@ -1116,13 +1250,13 @@ if __name__ == "__main__": if name.startswith("N("): name = name[2:-1] - if struct['vaddr'] in function_replacements: - name = function_replacements[struct['vaddr']] + if struct["vaddr"] in function_replacements: + name = function_replacements[struct["vaddr"]] - if name.split("_",1)[0] in name_fixes: - name = name_fixes[name.split("_",1)[0]] + "_" + name.rsplit("_",1)[1] + if name.split("_", 1)[0] in name_fixes: + name = name_fixes[name.split("_", 1)[0]] + "_" + name.rsplit("_", 1)[1] elif name.startswith("script_"): - name = name.split("script_",1)[1] + name = name.split("script_", 1)[1] elif "_Main_" in name: name = "main" elif "ASCII" in name: @@ -1154,19 +1288,19 @@ if __name__ == "__main__": double_literal = f"{double}" add_to_symbol_map(struct["vaddr"], [struct["vaddr"], double_literal]) elif struct["type"] == "NpcGroup": - for z in range(struct["length"]//0x1F0): + for z in range(struct["length"] // 0x1F0): npc = romfile.read(0x1F0) npc_id = unpack_from(">I", npc, 0)[0] if npc_id >= 0: anim = unpack_from(">I", npc, 0x1A0)[0] if not anim == 0: - sprite_id = (anim & 0x00FF0000) >> 16 - sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"].upper() + sprite_id = (anim & 0x00FF0000) >> 16 + sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"].upper() if npc_id not in total_npc_counts: total_npc_counts[npc_id] = sprite add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]]) else: - add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]]) + add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]]) # fix NPC names curr_counts = {} @@ -1186,17 +1320,24 @@ if __name__ == "__main__": romfile.seek(rom_offset, 0) - disasm = disassemble(romfile, midx, symbol_map, args.comments, rom_offset, namespace=args.namespace) + disasm = disassemble( + romfile, + midx, + symbol_map, + args.comments, + rom_offset, + namespace=args.namespace, + ) print("========== Includes needed: ===========\n") if is_battle: - print(f"#include \"battle/battle.h\"") + print(f'#include "battle/battle.h"') else: - print(f"#include \"map.h\"") - print(f"#include \"message_ids.h\"") + print(f'#include "map.h"') + print(f'#include "message_ids.h"') if INCLUDES_NEEDED["sprites"]: for npc in sorted(INCLUDES_NEEDED["sprites"]): - print(f"#include \"sprite/npc/{npc}\"") + print(f'#include "sprite/npc/{npc}"') print() if INCLUDES_NEEDED["forward"]: @@ -1213,7 +1354,7 @@ if __name__ == "__main__": print(f"enum {{") lastnum = -1 for i, (k, v) in enumerate(sorted(INCLUDES_NEEDED["npcs"].items())): - print(f" {v}" + (f" = {k}" if ((k > 0 and i == 0) or (k != lastnum+1)) else "") + ",") + print(f" {v}" + (f" = {k}" if ((k > 0 and i == 0) or (k != lastnum + 1)) else "") + ",") lastnum = k print(f"}};") print() diff --git a/tools/sym_info.py b/tools/sym_info.py index 74c4f5359f..cfe6c07655 100755 --- a/tools/sym_info.py +++ b/tools/sym_info.py @@ -6,29 +6,24 @@ import argparse script_dir = os.path.dirname(os.path.realpath(__file__)) root_dir = os.path.abspath(os.path.join(script_dir, "..")) -parser = argparse.ArgumentParser( - description="Display various information about a symbol or address." -) -parser.add_argument( - "name", - type=str, - default="", - help="symbol name or ROM/RAM address to lookup" -) +parser = argparse.ArgumentParser(description="Display various information about a symbol or address.") +parser.add_argument("name", type=str, default="", help="symbol name or ROM/RAM address to lookup") parser.add_argument( "-e", "--expected", dest="use_expected", action="store_true", - help="use the map file in expected/build/ instead of build/" + help="use the map file in expected/build/ instead of build/", ) + def get_map(expected: bool = False): mymap = os.path.join(root_dir, "ver", "current", "build", "papermario.map") if expected: mymap = os.path.join(root_dir, "ver", "current", "expected", "build", "papermario.map") return mymap + def search_address(target_addr, map=get_map()): is_ram = target_addr & 0x80000000 ram_offset = None @@ -52,12 +47,7 @@ def search_address(target_addr, map=get_map()): prev_line = line - if ( - ram_offset is None - or "=" in line - or "*fill*" in line - or " 0x" not in line - ): + if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line: continue ram = int(line[16 : 16 + 18], 0) @@ -84,6 +74,7 @@ def search_address(target_addr, map=get_map()): return "at end of rom?" + def search_symbol(target_sym, map=get_map()): ram_offset = None cur_file = "" @@ -98,12 +89,7 @@ def search_symbol(target_sym, map=get_map()): prev_line = line - if ( - ram_offset is None - or "=" in line - or "*fill*" in line - or " 0x" not in line - ): + if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line: continue ram = int(line[16 : 16 + 18], 0) @@ -122,6 +108,7 @@ def search_symbol(target_sym, map=get_map()): return None + if __name__ == "__main__": args = parser.parse_args() diff --git a/tools/update_symbol_addrs.py b/tools/update_symbol_addrs.py index 5e1263ecf4..c3df51f049 100755 --- a/tools/update_symbol_addrs.py +++ b/tools/update_symbol_addrs.py @@ -26,6 +26,7 @@ ignores = set() verbose = False + def read_ignores(): with open(ignores_path) as f: lines = f.readlines() @@ -35,6 +36,7 @@ def read_ignores(): if name != "": ignores.add(name) + def scan_map(): ram_offset = None cur_file = "" @@ -49,12 +51,7 @@ def scan_map(): prev_line = line - if ( - ram_offset is None - or "=" in line - or "*fill*" in line - or " 0x" not in line - ): + if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line: continue ram = int(line[16 : 16 + 18], 0) @@ -70,12 +67,13 @@ def scan_map(): map_symbols[sym] = (rom, cur_file, ram) + def read_symbol_addrs(): unique_lines = set() with open(symbol_addrs_path, "r") as f: for line in f.readlines(): - unique_lines.add(line) + unique_lines.add(line) for line in unique_lines: if "_ROM_START" in line or "_ROM_END" in line: @@ -117,9 +115,10 @@ def read_symbol_addrs(): else: dead_symbols.append([name, int(addr, 0), type, rom, opts]) + def read_elf(): try: - result = subprocess.run(['mips-linux-gnu-objdump', '-x', elf_path], stdout=subprocess.PIPE) + result = subprocess.run(["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE) objdump_lines = result.stdout.decode().split("\n") except: print(f"Error: Could not run objdump on {elf_path} - make sure that the project is built") @@ -133,13 +132,15 @@ def read_elf(): if "_ROM_START" in name or "_ROM_END" in name: continue - if "/" in name or \ - "." in name or \ - name in ignores or \ - name.startswith("_") or \ - name.startswith("jtbl_") or \ - name.endswith(".o") or \ - re.match(r"L[0-9A-F]{8}", name): + if ( + "/" in name + or "." in name + or name in ignores + or name.startswith("_") + or name.startswith("jtbl_") + or name.endswith(".o") + or re.match(r"L[0-9A-F]{8}", name) + ): continue addr = int(components[0], 16) @@ -153,14 +154,16 @@ def read_elf(): if name in map_symbols: rom = map_symbols[name][0] elif re.match(".*_[0-9A-F]{8}_[0-9A-F]{6}", name): - rom = int(name.split('_')[-1], 16) + rom = int(name.split("_")[-1], 16) elf_symbols.append((name, addr, type, rom)) + def log(s): if verbose: print(s) + def reconcile_symbols(): print(f"Processing {str(len(elf_symbols))} elf symbols...") @@ -175,7 +178,9 @@ def reconcile_symbols(): name_match = known_sym if elf_sym[1] != known_sym[1]: - log(f"Ram mismatch! {elf_sym[0]} is 0x{elf_sym[1]:X} in the elf and 0x{known_sym[1]} in symbol_addrs") + log( + f"Ram mismatch! {elf_sym[0]} is 0x{elf_sym[1]:X} in the elf and 0x{known_sym[1]} in symbol_addrs" + ) # Rom if not rom_match: @@ -186,7 +191,15 @@ def reconcile_symbols(): if not name_match and not rom_match: log(f"Creating new symbol {elf_sym[0]}") - symbol_addrs.append([elf_sym[0], elf_sym[1], elf_sym[2], elf_sym[3] if elf_sym[3] else -1, []]) + symbol_addrs.append( + [ + elf_sym[0], + elf_sym[1], + elf_sym[2], + elf_sym[3] if elf_sym[3] else -1, + [], + ] + ) elif not name_match: log(f"Renaming identical rom address symbol {rom_match[0]} to {elf_sym[0]}") rom_match[0] = elf_sym[0] @@ -197,6 +210,7 @@ def reconcile_symbols(): log(f"Adding rom address {elf_sym[3]} to symbol {name_match[0]}") name_match[3] = elf_sym[3] + def write_new_symbol_addrs(): with open(symbol_addrs_path, "w", newline="\n") as f: for symbol in sorted(symbol_addrs, key=lambda x: (x[3] == -1, x[3], x[1], x[0])): @@ -220,6 +234,7 @@ def write_new_symbol_addrs(): line += f" {thing}" f.write(line + "\n") + read_ignores() scan_map() read_symbol_addrs() diff --git a/tools/warnings_count/compare_warnings.py b/tools/warnings_count/compare_warnings.py index e6640f4525..cfd2008e9d 100755 --- a/tools/warnings_count/compare_warnings.py +++ b/tools/warnings_count/compare_warnings.py @@ -11,8 +11,14 @@ def countFileLines(filename: str) -> int: def main(): parser = argparse.ArgumentParser() - parser.add_argument('currentwarnings', help="Name of file which contains the current warnings of the repo.") - parser.add_argument('newwarnings', help="Name of file which contains the *new* warnings of the repo.") + parser.add_argument( + "currentwarnings", + help="Name of file which contains the current warnings of the repo.", + ) + parser.add_argument( + "newwarnings", + help="Name of file which contains the *new* warnings of the repo.", + ) parser.add_argument("--pr-message", action="store_true") args = parser.parse_args()