mirror of
https://github.com/pmret/papermario.git
synced 2024-09-08 02:29:49 +02:00
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 <ethteck@gmail.com>
This commit is contained in:
parent
a69ae38bfe
commit
ae66312d8c
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
@ -4,7 +4,7 @@ on:
|
||||
|
||||
jobs:
|
||||
cpp_lint:
|
||||
name: Format and lint
|
||||
name: C format and lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
24
.github/workflows/python.yaml
vendored
Normal file
24
.github/workflows/python.yaml
vendored
Normal file
@ -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
|
4
.vscode/c_cpp_properties.json
vendored
4
.vscode/c_cpp_properties.json
vendored
@ -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",
|
||||
|
18
.vscode/settings.json
vendored
18
.vscode/settings.json
vendored
@ -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"
|
||||
],
|
||||
}
|
||||
|
24
coverage.py
24
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")
|
||||
|
29
diff_evt.py
29
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()
|
||||
|
@ -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
|
||||
|
46
progress.py
46
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",
|
||||
|
4
pyproject.toml
Normal file
4
pyproject.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
exclude = 'tools/splat/'
|
||||
extend-exclude = 'diff.py'
|
@ -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 "
|
||||
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.")
|
||||
+ "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()
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
@ -1120,9 +1065,7 @@ class Configure:
|
||||
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()))
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
@ -43,7 +46,7 @@ def build_mapfs(out_bin, assets, version):
|
||||
# write all previously-written names; required to match
|
||||
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)
|
||||
@ -62,11 +65,12 @@ 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'))
|
||||
f.write(lastname.encode("ascii"))
|
||||
|
||||
f.seek(toc_entry_pos + 0x18)
|
||||
f.write((0x903F0000).to_bytes(4, byteorder="big")) # TODO: figure out purpose
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv.pop(0) # python3
|
||||
version = argv.pop(0)
|
||||
|
@ -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}")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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(","))
|
||||
|
@ -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...]")
|
||||
@ -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:
|
||||
|
@ -5,6 +5,7 @@ from collections import OrderedDict
|
||||
import re
|
||||
import msgpack # way faster than pickle
|
||||
|
||||
|
||||
class Message:
|
||||
def __init__(self, name, section, index):
|
||||
self.name = name
|
||||
@ -13,12 +14,14 @@ class Message:
|
||||
|
||||
self.bytes = [] # XXX: bytearray would be better
|
||||
|
||||
|
||||
def try_convert_int(s):
|
||||
try:
|
||||
return int(s, base=0)
|
||||
except:
|
||||
return s
|
||||
|
||||
|
||||
def parse_command(source):
|
||||
if source[0] != "[":
|
||||
return None, [], {}, source
|
||||
@ -58,6 +61,7 @@ def parse_command(source):
|
||||
|
||||
return command.lower(), args, named_args, source
|
||||
|
||||
|
||||
def color_to_code(color, style):
|
||||
COLORS = {
|
||||
"diary": {
|
||||
@ -90,13 +94,15 @@ def color_to_code(color, style):
|
||||
"red": 0x19,
|
||||
"blue": 0x1A,
|
||||
"green": 0x1B,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if type(color) is int:
|
||||
return color
|
||||
|
||||
return COLORS.get(style, {
|
||||
return COLORS.get(
|
||||
style,
|
||||
{
|
||||
# [style:left], [style:right]
|
||||
"normal": 0x0A,
|
||||
"red": 0x20,
|
||||
@ -106,7 +112,9 @@ def color_to_code(color, style):
|
||||
"cyan": 0x24,
|
||||
"green": 0x25,
|
||||
"yellow": 0x26,
|
||||
}).get(color)
|
||||
},
|
||||
).get(color)
|
||||
|
||||
|
||||
CHARSET = {
|
||||
# "𝅘𝅥𝅮": 0x00,
|
||||
@ -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]")
|
||||
@ -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":
|
||||
@ -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")
|
||||
|
||||
@ -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:
|
||||
@ -969,63 +1015,177 @@ if __name__ == "__main__":
|
||||
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,7 +1203,7 @@ 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
|
||||
@ -1124,9 +1284,15 @@ if __name__ == "__main__":
|
||||
|
||||
else:
|
||||
with open(outfile, "wb") as f:
|
||||
msgpack.pack([{
|
||||
msgpack.pack(
|
||||
[
|
||||
{
|
||||
"section": message.section,
|
||||
"index": message.index,
|
||||
"name": message.name,
|
||||
"bytes": bytes(message.bytes),
|
||||
} for message in messages], f)
|
||||
}
|
||||
for message in messages
|
||||
],
|
||||
f,
|
||||
)
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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] = {}
|
||||
@ -94,6 +96,7 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path
|
||||
|
||||
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")
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -5,6 +5,8 @@ from pathlib import Path
|
||||
|
||||
|
||||
_script_lib = None
|
||||
|
||||
|
||||
def script_lib(offset=0):
|
||||
global _script_lib
|
||||
|
||||
@ -77,6 +79,7 @@ def extend_symbol_map(a, b):
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def round_fixed(f: float) -> float:
|
||||
g = f * 100.0
|
||||
whole = round(g)
|
||||
@ -84,6 +87,7 @@ def round_fixed(f: float) -> float:
|
||||
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,6 +107,7 @@ 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):
|
||||
@ -137,23 +142,59 @@ def browse_header(valid_enums, enums):
|
||||
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]
|
||||
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()
|
||||
# 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():
|
||||
@ -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):
|
||||
@ -247,8 +290,7 @@ 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_"))):
|
||||
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:
|
||||
@ -283,13 +325,13 @@ 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:
|
||||
@ -341,9 +383,11 @@ 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
|
||||
@ -361,7 +405,6 @@ replace_funcs = {
|
||||
"AddActorVar": {0: "ActorIDs"},
|
||||
"AddKeyItem": {0: "ItemIDs"},
|
||||
"AddGoalPos": {0: "ActorIDs"},
|
||||
|
||||
"BattleCamTargetActor": {0: "ActorIDs"},
|
||||
"BindHandleEvent": {0: "ActorIDs"},
|
||||
"BindIdle": {0: "ActorIDs"},
|
||||
@ -371,33 +414,40 @@ replace_funcs = {
|
||||
"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"},
|
||||
|
||||
"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"},
|
||||
@ -409,7 +459,6 @@ replace_funcs = {
|
||||
"SetNpcFoldParams": {0: "NpcIDs"},
|
||||
"SetNpcFoldFlags": {0: "NpcIDs"},
|
||||
"UpdatePlayerFold": {0: "PlayerAnims"},
|
||||
|
||||
"GetAnimation": {0: "ActorIDs", 2: "CustomAnim"},
|
||||
"GetActorFlags": {0: "ActorIDs", 1: "ActorFlags"},
|
||||
"GetActorHP": {0: "ActorIDs"},
|
||||
@ -435,33 +484,26 @@ replace_funcs = {
|
||||
"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"},
|
||||
@ -471,14 +513,12 @@ replace_funcs = {
|
||||
"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"},
|
||||
@ -494,7 +534,6 @@ replace_funcs = {
|
||||
"SetActorType": {0: "ActorIDs", 1: "ActorType"},
|
||||
"SetActorVar": {0: "ActorIDs"},
|
||||
"SetActorYaw": {0: "ActorIDs"},
|
||||
|
||||
"SetAnimation": {0: "ActorIDs", 2: "CustomAnim"},
|
||||
"SetAnimationRate": {0: "ActorIDs"},
|
||||
"SetBattleFlagBits": {0: "BattleStatusFlags1"},
|
||||
@ -511,9 +550,13 @@ replace_funcs = {
|
||||
"SetIdleAnimations": {0: "ActorIDs"},
|
||||
"SetIdleGoal": {0: "ActorIDs"},
|
||||
"SetIdleGoalToHome": {0: "ActorIDs"},
|
||||
"SetJumpAnimations" :{0:"ActorIDs", 2:"PlayerAnims", 3:"PlayerAnims", 4:"PlayerAnims"},
|
||||
"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"},
|
||||
@ -524,9 +567,7 @@ replace_funcs = {
|
||||
"SetNpcSpeed": {0: "NpcIDs"},
|
||||
"SetNpcSprite": {1: "Hex"},
|
||||
"SetNpcYaw": {0: "NpcIDs"},
|
||||
|
||||
"SetOwnerID": {0: "ActorIDs"},
|
||||
|
||||
"SetPartAlpha": {0: "ActorIDs"},
|
||||
"SetPartDispOffset": {0: "ActorIDs"},
|
||||
"SetPartEventBits": {0: "ActorIDs", 2: "ActorEventFlags"},
|
||||
@ -542,7 +583,6 @@ replace_funcs = {
|
||||
"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
|
||||
@ -557,13 +597,12 @@ replace_funcs = {
|
||||
"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(")")]
|
||||
|
||||
@ -584,6 +623,7 @@ def replace_constants(self, func, args):
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def fix_1_arg(self, lw, arg, format):
|
||||
if lw:
|
||||
lw = int(trim_lw(lw))
|
||||
@ -599,8 +639,20 @@ def fix_1_arg(self, lw, arg, format):
|
||||
|
||||
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
|
||||
@ -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
|
||||
@ -673,17 +727,28 @@ class ScriptDisassembler:
|
||||
|
||||
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"
|
||||
@ -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)
|
||||
@ -749,18 +814,30 @@ class ScriptDisassembler:
|
||||
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]
|
||||
@ -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])})")
|
||||
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,7 +1228,12 @@ 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
|
||||
@ -1065,14 +1241,19 @@ if __name__ == "__main__":
|
||||
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} "
|
||||
# f"(0x{script.end_pos - script.start_pos:X} bytes, {script.instructions} instructions)")
|
||||
# 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}")
|
||||
@ -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()
|
||||
|
@ -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
|
||||
@ -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"
|
||||
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")
|
||||
"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()
|
||||
|
||||
|
@ -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()}")
|
||||
|
||||
|
@ -2,20 +2,32 @@
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def get_variable(arg):
|
||||
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)))
|
||||
|
@ -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,8 +42,7 @@ 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)
|
||||
@ -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""",
|
||||
|
@ -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"
|
||||
@ -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()
|
||||
|
||||
|
@ -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",
|
||||
)
|
||||
|
@ -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:
|
||||
|
@ -38,4 +38,3 @@ for filename, line_number, bad_symbol_name in problems:
|
||||
|
||||
with open(filename, "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -26,8 +26,12 @@ while i < args.end:
|
||||
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)
|
||||
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 = []
|
||||
|
@ -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,7 +43,7 @@ def get_constants():
|
||||
id_ = id_.split(" ",1)[0]
|
||||
CONSTANTS[this_enum][int(id_, 16)] = name
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
# enums
|
||||
for i, line in enumerate(enums):
|
||||
@ -63,7 +75,9 @@ def get_constants():
|
||||
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():
|
||||
@ -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,8 +115,10 @@ 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}")
|
||||
if "*/ " in line:
|
||||
@ -131,6 +151,7 @@ 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
|
||||
@ -163,7 +184,15 @@ def parse_file(filename):
|
||||
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})
|
||||
struct_to_add.append(
|
||||
{
|
||||
"type": type_,
|
||||
"name": name,
|
||||
"num": count,
|
||||
"ptr": ptr,
|
||||
"union": union,
|
||||
}
|
||||
)
|
||||
|
||||
i += 1
|
||||
# print(f"Broke on line {fd[i]}")
|
||||
@ -175,10 +204,12 @@ def parse_file(filename):
|
||||
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
|
||||
|
||||
@ -203,52 +234,52 @@ def get_vals(fd, offset, var):
|
||||
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]
|
||||
data = unpack_from(">B", fd, offset)[0]
|
||||
fmt = "d"
|
||||
offset += 1
|
||||
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":
|
||||
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"):
|
||||
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":
|
||||
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":
|
||||
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
|
||||
@ -264,9 +295,11 @@ def get_vals(fd, offset, var):
|
||||
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:
|
||||
@ -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"
|
||||
@ -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):
|
||||
@ -366,6 +399,7 @@ def output_type2(fd, count, offset, var):
|
||||
ultra_out.append(out)
|
||||
return offset, ultra_out # "\n".join(out)
|
||||
|
||||
|
||||
def check_list(vals, depth=0):
|
||||
for x, val in enumerate(vals):
|
||||
if type(val) == list:
|
||||
@ -375,6 +409,7 @@ def check_list(vals, depth = 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def recurse_check_list(vals):
|
||||
res = 0
|
||||
for x, val in enumerate(vals):
|
||||
@ -385,6 +420,7 @@ 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("},"):
|
||||
@ -417,6 +453,7 @@ def get_single_struct_vals(fd, i):
|
||||
vals.extend(a)
|
||||
return vals, i
|
||||
|
||||
|
||||
def cull_struct(fd, i, entirely=False):
|
||||
out = []
|
||||
vals = []
|
||||
@ -467,6 +504,7 @@ def cull_struct(fd, i, entirely=False):
|
||||
# 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" }
|
||||
@ -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])
|
||||
@ -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,6 +640,7 @@ def MacroReplaceStaticNPC(fd):
|
||||
i += 1
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def MacroReplaceNpcSettings(fd):
|
||||
replace_cull = {"unk_00", "unk_24"}
|
||||
fd = fd.splitlines()
|
||||
@ -605,6 +656,7 @@ def MacroReplaceNpcSettings(fd):
|
||||
i += 1
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def MacroReplaceNpcGroupList(fd):
|
||||
fd = fd.splitlines()
|
||||
out = []
|
||||
@ -624,14 +676,24 @@ def MacroReplaceNpcGroupList(fd):
|
||||
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
|
||||
|
@ -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):
|
||||
|
@ -64,4 +64,3 @@ for segment in config["segments"]:
|
||||
|
||||
with open(c_file_path, "w", newline="\n") as f:
|
||||
f.write("".join(c_lines))
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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"))
|
||||
|
@ -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()
|
||||
|
||||
|
@ -51,7 +51,10 @@ def parse_symbol_addrs():
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
@ -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,
|
||||
|
@ -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()
|
||||
@ -41,11 +42,12 @@ def handle_file(f_path, try_rename_file=False):
|
||||
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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:]})"
|
||||
|
@ -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
|
||||
|
@ -65,7 +65,7 @@ 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]
|
||||
|
@ -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(),
|
||||
)
|
||||
|
@ -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:
|
||||
|
@ -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,7 +280,9 @@ CHARSET = {
|
||||
0x21: {None: lambda d: (f"[Option {d[0]}]", 1)},
|
||||
0x22: "[SavePos]",
|
||||
0x23: "[RestorePos]",
|
||||
0x24: {0xFF: {0x05: {
|
||||
0x24: {
|
||||
0xFF: {
|
||||
0x05: {
|
||||
0x10: {0x98: {0xFF: {0x25: "[A]"}}},
|
||||
0x11: {0x99: {0xFF: {0x25: "[B]"}}},
|
||||
0x12: {0xA1: {0xFF: {0x25: "[START]"}}},
|
||||
@ -276,7 +293,9 @@ CHARSET = {
|
||||
0xA0: {0xFF: {0x25: "[C-RIGHT]"}},
|
||||
},
|
||||
0x14: {0x9C: {0xFF: {0x25: "[Z]"}}},
|
||||
}}},
|
||||
}
|
||||
}
|
||||
},
|
||||
# 0x24: "[SaveColor]",
|
||||
# 0x25: "[RestoreColor]",
|
||||
0x26: {
|
||||
@ -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",
|
||||
@ -366,6 +390,7 @@ CHARSET_CREDITS = {
|
||||
0xF7: " ",
|
||||
}
|
||||
|
||||
|
||||
class N64SegPm_msg(N64Segment):
|
||||
def __init__(
|
||||
self,
|
||||
@ -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"
|
||||
|
@ -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(
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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":
|
||||
|
@ -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:]})"
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
@ -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()
|
||||
@ -91,12 +104,14 @@ def get_include_list(area_name, map_name):
|
||||
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 = ""
|
||||
@ -170,7 +187,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
# afterHeader = False
|
||||
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()
|
||||
@ -211,7 +233,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
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}")
|
||||
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])
|
||||
@ -233,7 +258,12 @@ 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)):
|
||||
@ -241,7 +271,15 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
if not var == 0:
|
||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||
elif i == 0x8:
|
||||
var_names = ["otherAI", "onInteract", "ai", "onHit", "aux", "onDefeat", "flags"]
|
||||
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:
|
||||
@ -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,16 +342,32 @@ 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"
|
||||
@ -313,7 +382,15 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
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]
|
||||
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:
|
||||
@ -360,7 +437,11 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
for x in range(8):
|
||||
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}"
|
||||
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
|
||||
|
||||
@ -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,10 +492,16 @@ 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"
|
||||
@ -419,7 +517,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
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):
|
||||
@ -442,7 +545,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
elif i == 0x1EC:
|
||||
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
|
||||
|
||||
@ -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"
|
||||
@ -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:
|
||||
@ -600,7 +709,7 @@ 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])
|
||||
@ -621,7 +730,7 @@ 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])
|
||||
@ -669,7 +778,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
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]")
|
||||
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"
|
||||
@ -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}"
|
||||
|
||||
@ -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"
|
||||
@ -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({
|
||||
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({
|
||||
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({
|
||||
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
|
||||
@ -1116,8 +1250,8 @@ 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]
|
||||
@ -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"]:
|
||||
|
@ -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 = "<no 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()
|
||||
|
||||
|
@ -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 = "<no 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,6 +67,7 @@ def scan_map():
|
||||
|
||||
map_symbols[sym] = (rom, cur_file, ram)
|
||||
|
||||
|
||||
def read_symbol_addrs():
|
||||
unique_lines = set()
|
||||
|
||||
@ -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()
|
||||
|
@ -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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user