mirror of
https://github.com/pmret/papermario.git
synced 2024-11-17 16:32:40 +01: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'
|
@ -10,7 +10,7 @@ from enum import IntEnum
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
asm_dir = script_dir + "/../ver/current/asm/nonmatchings"
|
||||
|
||||
modes = [ "min", "max", "avg", "total", "size" ]
|
||||
modes = ["min", "max", "avg", "total", "size"]
|
||||
|
||||
sizes = {}
|
||||
|
||||
@ -47,18 +47,53 @@ def do_dir(root, dir):
|
||||
|
||||
avg = 0 if len(files) == 0 else total / len(files)
|
||||
|
||||
sizes[root + "/" + dir] = ((min, max, total, avg, len(files)))
|
||||
sizes[root + "/" + dir] = (min, max, total, avg, len(files))
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="A tool to receive information about the number of non-matching .s files "
|
||||
+"per .c file, or the size of .s files, measured by their number of instructions. "
|
||||
+"Option -p is used by default if no option is specified.")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="A tool to receive information about the number of non-matching .s files "
|
||||
+ "per .c file, or the size of .s files, measured by their number of instructions. "
|
||||
+ "Option -p is used by default if no option is specified."
|
||||
)
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("-f", "--files", help="Default. Print the number of non-matching .s files per .c file, ordered by size.", action='store_true', required=False)
|
||||
group.add_argument("-a", "--alphabetical", help="Print the size of .s files, ordered by name.", action='store_true', required=False)
|
||||
group.add_argument("-s", "--size", help="Print the size of .s files, ordered by size.", action='store_true', required=False)
|
||||
parser.add_argument("-l", "--limit", help="Only print the .c --files that are greater than or equal to the value.", type=int, default=0, required=False)
|
||||
parser.add_argument("-m", "--mode", help="Switches between output modes for --files. Allowed values are: {min, max, avg, total, size}.", choices=modes, default="size", metavar='', required=False)
|
||||
group.add_argument(
|
||||
"-f",
|
||||
"--files",
|
||||
help="Default. Print the number of non-matching .s files per .c file, ordered by size.",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
group.add_argument(
|
||||
"-a",
|
||||
"--alphabetical",
|
||||
help="Print the size of .s files, ordered by name.",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
group.add_argument(
|
||||
"-s",
|
||||
"--size",
|
||||
help="Print the size of .s files, ordered by size.",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--limit",
|
||||
help="Only print the .c --files that are greater than or equal to the value.",
|
||||
type=int,
|
||||
default=0,
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--mode",
|
||||
help="Switches between output modes for --files. Allowed values are: {min, max, avg, total, size}.",
|
||||
choices=modes,
|
||||
default="size",
|
||||
metavar="",
|
||||
required=False,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -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,
|
||||
@ -1115,14 +1060,12 @@ class Configure:
|
||||
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
||||
elif seg.type == "pm_sbn":
|
||||
sbn_path = entry.object_path.with_suffix("")
|
||||
build(sbn_path, entry.src_paths, "pm_sbn") # could have non-yaml inputs be implicit
|
||||
build(sbn_path, entry.src_paths, "pm_sbn") # could have non-yaml inputs be implicit
|
||||
build(entry.object_path, [sbn_path], "bin")
|
||||
elif seg.type == "linker" or seg.type == "linker_offset":
|
||||
pass
|
||||
elif seg.type == "pm_imgfx_data":
|
||||
c_file_path = (
|
||||
Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c")
|
||||
)
|
||||
c_file_path = Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c")
|
||||
build(c_file_path, entry.src_paths, "imgfx_data")
|
||||
|
||||
build(
|
||||
@ -1136,9 +1079,7 @@ class Configure:
|
||||
},
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
f"don't know how to build {seg.__class__.__name__} '{seg.name}'"
|
||||
)
|
||||
raise Exception(f"don't know how to build {seg.__class__.__name__} '{seg.name}'")
|
||||
|
||||
# Run undefined_syms through cpp
|
||||
ninja.build(
|
||||
@ -1216,20 +1157,14 @@ if __name__ == "__main__":
|
||||
action="store_true",
|
||||
help="Delete assets and previously-built files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--splat", default="tools/splat", help="Path to splat tool to use"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--split-code", action="store_true", help="Re-split code segments to asm files"
|
||||
)
|
||||
parser.add_argument("--splat", default="tools/splat", help="Path to splat tool to use")
|
||||
parser.add_argument("--split-code", action="store_true", help="Re-split code segments to asm files")
|
||||
parser.add_argument(
|
||||
"--no-split-assets",
|
||||
action="store_true",
|
||||
help="Don't split assets from the baserom(s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--debug", action="store_true", help="Generate debugging information"
|
||||
)
|
||||
parser.add_argument("-d", "--debug", action="store_true", help="Generate debugging information")
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--non-matching",
|
||||
@ -1272,12 +1207,8 @@ if __name__ == "__main__":
|
||||
pass
|
||||
if args.cpp is None:
|
||||
print("error: system C preprocessor is not GNU!")
|
||||
print(
|
||||
"This is a known issue on macOS - only clang's cpp is installed by default."
|
||||
)
|
||||
print(
|
||||
"Use 'brew' to obtain GNU cpp, then run this script again with the --cpp option, e.g."
|
||||
)
|
||||
print("This is a known issue on macOS - only clang's cpp is installed by default.")
|
||||
print("Use 'brew' to obtain GNU cpp, then run this script again with the --cpp option, e.g.")
|
||||
print(f" ./configure --cpp {gcc_cpps[0]}")
|
||||
exit(1)
|
||||
|
||||
@ -1285,15 +1216,11 @@ if __name__ == "__main__":
|
||||
version = exec_shell([PIGMENT, "--version"]).split(" ")[1].strip()
|
||||
|
||||
if version < PIGMENT_REQ_VERSION:
|
||||
print(
|
||||
f"error: {PIGMENT} version {PIGMENT_REQ_VERSION} or newer is required, system version is {version}\n"
|
||||
)
|
||||
print(f"error: {PIGMENT} version {PIGMENT_REQ_VERSION} or newer is required, system version is {version}\n")
|
||||
exit(1)
|
||||
except FileNotFoundError:
|
||||
print(f"error: {PIGMENT} is not installed\n")
|
||||
print(
|
||||
"To build and install it, obtain cargo:\n\tcurl https://sh.rustup.rs -sSf | sh"
|
||||
)
|
||||
print("To build and install it, obtain cargo:\n\tcurl https://sh.rustup.rs -sSf | sh")
|
||||
print(f"and then run:\n\tcargo install {PIGMENT}")
|
||||
exit(1)
|
||||
|
||||
@ -1382,12 +1309,8 @@ if __name__ == "__main__":
|
||||
# include tools/splat_ext in the python path
|
||||
sys.path.append(str((ROOT / "tools/splat_ext").resolve()))
|
||||
|
||||
configure.split(
|
||||
not args.no_split_assets, args.split_code, args.shift, args.debug
|
||||
)
|
||||
configure.write_ninja(
|
||||
ninja, skip_files, non_matching, args.modern_gcc, args.c_maps
|
||||
)
|
||||
configure.split(not args.no_split_assets, args.split_code, args.shift, args.debug)
|
||||
configure.write_ninja(ninja, skip_files, non_matching, args.modern_gcc, args.c_maps)
|
||||
|
||||
all_rom_oks.append(str(configure.rom_ok_path()))
|
||||
|
||||
|
@ -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"
|
||||
|
@ -1,23 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, os
|
||||
|
||||
#Under normal compilation we rely on splat to use a discard option in the ldscript
|
||||
#to not include sections in the elf then just output all sections, however under debug we want
|
||||
#to have debug sections.
|
||||
#In debugging mode splat is told to output a list of sections it is custom creating, which are
|
||||
#all of the sections we export to the z64 file with an objcopy. The below chunk of code is
|
||||
#responsible for adding -j to each of the names and outputting a file for objcopy to use
|
||||
#so we can still generate a elf file with all the extra debugging sections and still output
|
||||
# Under normal compilation we rely on splat to use a discard option in the ldscript
|
||||
# to not include sections in the elf then just output all sections, however under debug we want
|
||||
# to have debug sections.
|
||||
# In debugging mode splat is told to output a list of sections it is custom creating, which are
|
||||
# all of the sections we export to the z64 file with an objcopy. The below chunk of code is
|
||||
# responsible for adding -j to each of the names and outputting a file for objcopy to use
|
||||
# so we can still generate a elf file with all the extra debugging sections and still output
|
||||
# the required sections to the .z64 without outputting everything.
|
||||
|
||||
if __name__ == "__main__":
|
||||
infile, outfile = sys.argv[1:]
|
||||
infile, outfile = sys.argv[1:]
|
||||
|
||||
#generate output based on input
|
||||
file_data = open(infile,"r").read().split("\n")
|
||||
if len(file_data[-1]) == 0:
|
||||
file_data.pop()
|
||||
# generate output based on input
|
||||
file_data = open(infile, "r").read().split("\n")
|
||||
if len(file_data[-1]) == 0:
|
||||
file_data.pop()
|
||||
|
||||
outdata = "-j " + " -j ".join(file_data)
|
||||
with open(outfile, "w") as f:
|
||||
f.write(outdata)
|
||||
outdata = "-j " + " -j ".join(file_data)
|
||||
with open(outfile, "w") as f:
|
||||
f.write(outdata)
|
||||
|
@ -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")
|
||||
|
@ -20,7 +20,7 @@ if __name__ == "__main__":
|
||||
mode = sys.argv[2]
|
||||
|
||||
syms_to_max = {
|
||||
"entity_data_vram_end" : [
|
||||
"entity_data_vram_end": [
|
||||
"entity_default_VRAM_END",
|
||||
"entity_jan_iwa_VRAM_END",
|
||||
"entity_sbk_omo_VRAM_END",
|
||||
@ -44,7 +44,7 @@ if __name__ == "__main__":
|
||||
"world_action_use_spinning_flower_VRAM_END",
|
||||
"world_action_use_tweester_VRAM_END",
|
||||
"world_action_sneaky_parasol_VRAM_END",
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
addrs: Dict[str, List[int]] = {}
|
||||
@ -80,7 +80,9 @@ if __name__ == "__main__":
|
||||
|
||||
out_addrs = {sym: max(addrs[sym]) for sym in addrs}
|
||||
|
||||
out_addrs["entity_data_vram_end"] = out_addrs["entity_data_vram_end"] + out_addrs["world_action_vram_end"] - HARDCODED_ADDR
|
||||
out_addrs["entity_data_vram_end"] = (
|
||||
out_addrs["entity_data_vram_end"] + out_addrs["world_action_vram_end"] - HARDCODED_ADDR
|
||||
)
|
||||
|
||||
out = ""
|
||||
for sym in out_addrs:
|
||||
|
@ -4,9 +4,11 @@ from sys import argv
|
||||
from pathlib import Path
|
||||
import struct
|
||||
|
||||
|
||||
def next_multiple(pos, multiple):
|
||||
return pos + pos % multiple
|
||||
|
||||
|
||||
def get_version_date(version):
|
||||
if version == "us":
|
||||
return "Map Ver.00/11/07 15:36"
|
||||
@ -17,6 +19,7 @@ def get_version_date(version):
|
||||
else:
|
||||
return "Map Ver.??/??/?? ??:??"
|
||||
|
||||
|
||||
def build_mapfs(out_bin, assets, version):
|
||||
# every TOC entry's name field has data after the null terminator made up from all the previous name fields.
|
||||
# we probably don't have to do this for the game to read the data properly (it doesn't read past the null terminator
|
||||
@ -38,12 +41,12 @@ def build_mapfs(out_bin, assets, version):
|
||||
decompressed_size = decompressed.stat().st_size
|
||||
size = next_multiple(compressed.stat().st_size, 2) if compressed.exists() else decompressed_size
|
||||
|
||||
#print(f"{name} {offset:08X} {size:08X} {decompressed_size:08X}")
|
||||
# print(f"{name} {offset:08X} {size:08X} {decompressed_size:08X}")
|
||||
|
||||
# write all previously-written names; required to match
|
||||
lastname = name + lastname[len(name):]
|
||||
lastname = name + lastname[len(name) :]
|
||||
f.seek(toc_entry_pos)
|
||||
f.write(lastname.encode('ascii'))
|
||||
f.write(lastname.encode("ascii"))
|
||||
|
||||
# write TOC entry.
|
||||
f.seek(toc_entry_pos + 0x10)
|
||||
@ -61,14 +64,15 @@ def build_mapfs(out_bin, assets, version):
|
||||
|
||||
last_name_entry = "end_data\0"
|
||||
f.seek(toc_entry_pos)
|
||||
lastname = last_name_entry + lastname[len(last_name_entry):]
|
||||
f.write(lastname.encode('ascii'))
|
||||
lastname = last_name_entry + lastname[len(last_name_entry) :]
|
||||
f.write(lastname.encode("ascii"))
|
||||
|
||||
f.seek(toc_entry_pos + 0x18)
|
||||
f.write((0x903F0000).to_bytes(4, byteorder="big")) # TODO: figure out purpose
|
||||
f.write((0x903F0000).to_bytes(4, byteorder="big")) # TODO: figure out purpose
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv.pop(0) # python3
|
||||
argv.pop(0) # python3
|
||||
version = argv.pop(0)
|
||||
out = argv.pop(0)
|
||||
|
||||
@ -76,6 +80,6 @@ if __name__ == "__main__":
|
||||
|
||||
# pairs
|
||||
for i in range(0, len(argv), 2):
|
||||
assets.append((Path(argv[i]), Path(argv[i+1])))
|
||||
assets.append((Path(argv[i]), Path(argv[i + 1])))
|
||||
|
||||
build_mapfs(out, assets, version)
|
||||
|
@ -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}")
|
||||
|
@ -3,7 +3,7 @@
|
||||
from sys import argv
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv.pop(0) # python3
|
||||
argv.pop(0) # python3
|
||||
|
||||
if len(argv) > 4:
|
||||
out, img1, img2, img3, img2_pal = argv
|
||||
|
@ -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...]")
|
||||
@ -22,7 +24,7 @@ if __name__ == "__main__":
|
||||
_, outfile, header_file, *infiles = argv
|
||||
|
||||
messages = []
|
||||
#header_files = []
|
||||
# header_files = []
|
||||
|
||||
for i, infile in enumerate(infiles):
|
||||
# if infile == "--headers":
|
||||
@ -34,12 +36,12 @@ if __name__ == "__main__":
|
||||
|
||||
with open(outfile, "wb") as f:
|
||||
# sectioned+indexed, followed by just sectioned, followed by just indexed, followed by named (unsectioned & unindexed)
|
||||
#messages.sort(key=lambda msg: bool(msg.section)<<2 + bool(msg.index))
|
||||
# messages.sort(key=lambda msg: bool(msg.section)<<2 + bool(msg.index))
|
||||
|
||||
names = set()
|
||||
|
||||
sections = []
|
||||
#messages_by_file = {}
|
||||
# messages_by_file = {}
|
||||
|
||||
for message in messages:
|
||||
if message.section is None:
|
||||
@ -64,10 +66,10 @@ if __name__ == "__main__":
|
||||
# else:
|
||||
# names.add(message.name)
|
||||
|
||||
# if message.header_file_index in messages_by_file:
|
||||
# messages_by_file[message.header_file_index].add(message)
|
||||
# else:
|
||||
# messages_by_file[message.header_file_index] = set([message])
|
||||
# if message.header_file_index in messages_by_file:
|
||||
# messages_by_file[message.header_file_index].add(message)
|
||||
# else:
|
||||
# messages_by_file[message.header_file_index] = set([message])
|
||||
|
||||
if message.index in section:
|
||||
print(f"warning: multiple messages allocated to id {section_idx:02X}:{message.index:03X}")
|
||||
@ -77,7 +79,7 @@ if __name__ == "__main__":
|
||||
|
||||
section[message.index] = message
|
||||
|
||||
f.seek((len(sections) + 1) * 4) # skip past table of contents
|
||||
f.seek((len(sections) + 1) * 4) # skip past table of contents
|
||||
|
||||
section_offsets = []
|
||||
for section in sections:
|
||||
@ -97,21 +99,15 @@ if __name__ == "__main__":
|
||||
|
||||
# padding
|
||||
while f.tell() % 0x10 != 0:
|
||||
f.write(b'\0\0\0\0')
|
||||
f.write(b"\0\0\0\0")
|
||||
|
||||
f.seek(0)
|
||||
for offset in section_offsets:
|
||||
f.write(offset.to_bytes(4, byteorder="big"))
|
||||
f.write(b'\0\0\0\0')
|
||||
f.write(b"\0\0\0\0")
|
||||
|
||||
with open(header_file, "w") as f:
|
||||
f.write(
|
||||
f"#ifndef _MESSAGE_IDS_H_\n"
|
||||
f"#define _MESSAGE_IDS_H_\n"
|
||||
"\n"
|
||||
'#include "messages.h"\n'
|
||||
"\n"
|
||||
)
|
||||
f.write(f"#ifndef _MESSAGE_IDS_H_\n" f"#define _MESSAGE_IDS_H_\n" "\n" '#include "messages.h"\n' "\n")
|
||||
|
||||
for message in messages:
|
||||
if message.name:
|
||||
|
@ -3,7 +3,8 @@
|
||||
from sys import argv
|
||||
from collections import OrderedDict
|
||||
import re
|
||||
import msgpack # way faster than pickle
|
||||
import msgpack # way faster than pickle
|
||||
|
||||
|
||||
class Message:
|
||||
def __init__(self, name, section, index):
|
||||
@ -11,7 +12,8 @@ class Message:
|
||||
self.section = section
|
||||
self.index = index
|
||||
|
||||
self.bytes = [] # XXX: bytearray would be better
|
||||
self.bytes = [] # XXX: bytearray would be better
|
||||
|
||||
|
||||
def try_convert_int(s):
|
||||
try:
|
||||
@ -19,10 +21,11 @@ def try_convert_int(s):
|
||||
except:
|
||||
return s
|
||||
|
||||
|
||||
def parse_command(source):
|
||||
if source[0] != "[":
|
||||
return None, [], {}, source
|
||||
source = source[1:] # "["
|
||||
source = source[1:] # "["
|
||||
|
||||
inside_brackets = ""
|
||||
while source[0] != "]":
|
||||
@ -31,7 +34,7 @@ def parse_command(source):
|
||||
|
||||
inside_brackets += source[0]
|
||||
source = source[1:]
|
||||
source = source[1:] # "]"
|
||||
source = source[1:] # "]"
|
||||
|
||||
command, *raw_args = inside_brackets.split(" ")
|
||||
|
||||
@ -58,6 +61,7 @@ def parse_command(source):
|
||||
|
||||
return command.lower(), args, named_args, source
|
||||
|
||||
|
||||
def color_to_code(color, style):
|
||||
COLORS = {
|
||||
"diary": {
|
||||
@ -90,26 +94,30 @@ def color_to_code(color, style):
|
||||
"red": 0x19,
|
||||
"blue": 0x1A,
|
||||
"green": 0x1B,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if type(color) is int:
|
||||
return color
|
||||
|
||||
return COLORS.get(style, {
|
||||
# [style:left], [style:right]
|
||||
"normal": 0x0A,
|
||||
"red": 0x20,
|
||||
"pink": 0x21,
|
||||
"purple": 0x22,
|
||||
"blue": 0x23,
|
||||
"cyan": 0x24,
|
||||
"green": 0x25,
|
||||
"yellow": 0x26,
|
||||
}).get(color)
|
||||
return COLORS.get(
|
||||
style,
|
||||
{
|
||||
# [style:left], [style:right]
|
||||
"normal": 0x0A,
|
||||
"red": 0x20,
|
||||
"pink": 0x21,
|
||||
"purple": 0x22,
|
||||
"blue": 0x23,
|
||||
"cyan": 0x24,
|
||||
"green": 0x25,
|
||||
"yellow": 0x26,
|
||||
},
|
||||
).get(color)
|
||||
|
||||
|
||||
CHARSET = {
|
||||
#"𝅘𝅥𝅮": 0x00,
|
||||
# "𝅘𝅥𝅮": 0x00,
|
||||
"!": 0x01,
|
||||
'"': 0x02,
|
||||
"#": 0x03,
|
||||
@ -323,19 +331,22 @@ CHARSET_CREDITS = {
|
||||
" ": 0xF7,
|
||||
}
|
||||
|
||||
|
||||
def strip_c_comments(text):
|
||||
def replacer(match):
|
||||
s = match.group(0)
|
||||
if s.startswith('/'):
|
||||
if s.startswith("/"):
|
||||
return " "
|
||||
else:
|
||||
return s
|
||||
|
||||
pattern = re.compile(
|
||||
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
||||
re.DOTALL | re.MULTILINE
|
||||
re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
return re.sub(pattern, replacer, text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) < 3:
|
||||
print("usage: parse_compile.py [in.msg] [out.msgpack] [--c]")
|
||||
@ -435,7 +446,7 @@ if __name__ == "__main__":
|
||||
print(f"{filename}:{lineno}: expected opening brace ('{{')")
|
||||
exit(1)
|
||||
|
||||
source = source[1:] # {
|
||||
source = source[1:] # {
|
||||
|
||||
# count indent level
|
||||
indent_level = 0
|
||||
@ -484,8 +495,8 @@ if __name__ == "__main__":
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0xFF, 0x05, color]
|
||||
#color_stack.append(color)
|
||||
#elif command == "/color":
|
||||
# color_stack.append(color)
|
||||
# elif command == "/color":
|
||||
# color_stack.pop()
|
||||
# message.bytes += [0xFF, 0x05, color_stack[0]]
|
||||
elif command == "style":
|
||||
@ -517,7 +528,13 @@ if __name__ == "__main__":
|
||||
print(f"{filename}:{lineno}: 'choice' style requires size=_,_")
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0x05, pos[0], pos[1], size[0], size[1]]
|
||||
message.bytes += [
|
||||
0x05,
|
||||
pos[0],
|
||||
pos[1],
|
||||
size[0],
|
||||
size[1],
|
||||
]
|
||||
elif style == "inspect":
|
||||
message.bytes += [0x06]
|
||||
elif style == "sign":
|
||||
@ -553,7 +570,13 @@ if __name__ == "__main__":
|
||||
print(f"{filename}:{lineno}: 'upgrade' style requires size=_,_")
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0x0C, pos[0], pos[1], size[0], size[1]]
|
||||
message.bytes += [
|
||||
0x0C,
|
||||
pos[0],
|
||||
pos[1],
|
||||
size[0],
|
||||
size[1],
|
||||
]
|
||||
elif style == "narrate":
|
||||
message.bytes += [0x0D]
|
||||
elif style == "epilogue":
|
||||
@ -579,7 +602,7 @@ if __name__ == "__main__":
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0xFF, 0x00, font]
|
||||
#font_stack.append(font)
|
||||
# font_stack.append(font)
|
||||
|
||||
if font == 3 or font == 4:
|
||||
charset = CHARSET_CREDITS
|
||||
@ -709,7 +732,13 @@ if __name__ == "__main__":
|
||||
print(f"{filename}:{lineno}: {command} command requires raster=_")
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0xFF, 0x16, spriteid >> 8, spriteid & 0xFF, raster]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x16,
|
||||
spriteid >> 8,
|
||||
spriteid & 0xFF,
|
||||
raster,
|
||||
]
|
||||
elif command == "itemicon":
|
||||
itemid = named_args.get("itemid")
|
||||
|
||||
@ -722,7 +751,7 @@ if __name__ == "__main__":
|
||||
message.bytes += [0xFF, 0x17, itemid >> 8, itemid & 0xFF]
|
||||
elif command == "image":
|
||||
index = named_args.get("index")
|
||||
pos = named_args.get("pos") # xx,y
|
||||
pos = named_args.get("pos") # xx,y
|
||||
hasborder = named_args.get("hasborder")
|
||||
alpha = named_args.get("alpha")
|
||||
fadeamount = named_args.get("fadeamount")
|
||||
@ -743,7 +772,17 @@ if __name__ == "__main__":
|
||||
print(f"{filename}:{lineno}: {command} command requires fadeamount=_")
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0xFF, 0x18, index, pos[0] >> 8, pos[0] & 0xFF, pos[1], hasborder, alpha, fadeamount]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x18,
|
||||
index,
|
||||
pos[0] >> 8,
|
||||
pos[0] & 0xFF,
|
||||
pos[1],
|
||||
hasborder,
|
||||
alpha,
|
||||
fadeamount,
|
||||
]
|
||||
elif command == "hideimage":
|
||||
fadeamount = named_args.get("fadeamount", 0)
|
||||
|
||||
@ -933,9 +972,16 @@ if __name__ == "__main__":
|
||||
exit(1)
|
||||
|
||||
message.bytes += [
|
||||
0xFF, 0x2C,
|
||||
soundids[0] >> 24, (soundids[0] >> 16) & 0xFF, (soundids[0] >> 8) & 0xFF, soundids[0] & 0xFF,
|
||||
soundids[1] >> 24, (soundids[1] >> 16) & 0xFF, (soundids[1] >> 8) & 0xFF, soundids[1] & 0xFF,
|
||||
0xFF,
|
||||
0x2C,
|
||||
soundids[0] >> 24,
|
||||
(soundids[0] >> 16) & 0xFF,
|
||||
(soundids[0] >> 8) & 0xFF,
|
||||
soundids[0] & 0xFF,
|
||||
soundids[1] >> 24,
|
||||
(soundids[1] >> 16) & 0xFF,
|
||||
(soundids[1] >> 8) & 0xFF,
|
||||
soundids[1] & 0xFF,
|
||||
]
|
||||
elif command == "volume":
|
||||
if len(args) != 1:
|
||||
@ -962,70 +1008,184 @@ if __name__ == "__main__":
|
||||
exit(1)
|
||||
|
||||
message.bytes += [0xFF, 0x2F, sound]
|
||||
#sound_stack.append(sound)
|
||||
# sound_stack.append(sound)
|
||||
# elif command == "/sound":
|
||||
# sound_stack.pop()
|
||||
# message.bytes += [0xFF, 0x2F, sound_stack[0]]
|
||||
elif command == "a":
|
||||
color_code = color_to_code("blue", "button")
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x98, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x98,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "b":
|
||||
color_code = color_to_code(named_args.get("color", "green"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "green"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x99, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x99,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "l":
|
||||
color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "gray"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9A, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x9A,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "r":
|
||||
color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "gray"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9B, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x9B,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "z":
|
||||
color_code = color_to_code("grey", "button")
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9C, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x9C,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "c-up":
|
||||
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "yellow"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9D, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x9D,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "c-down":
|
||||
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "yellow"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9E, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x9E,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "c-left":
|
||||
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "yellow"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9F, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0x9F,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "c-right":
|
||||
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "yellow"),
|
||||
named_args.get("ctx", "button"),
|
||||
)
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA0, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0xA0,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "start":
|
||||
color_code = color_to_code(named_args.get("color", "red"), named_args.get("ctx", "button"))#
|
||||
color_code = color_to_code(
|
||||
named_args.get("color", "red"),
|
||||
named_args.get("ctx", "button"),
|
||||
) #
|
||||
assert color_code is not None
|
||||
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA1, 0xFF, 0x25]
|
||||
message.bytes += [
|
||||
0xFF,
|
||||
0x24,
|
||||
0xFF,
|
||||
0x05,
|
||||
color_code,
|
||||
0xA1,
|
||||
0xFF,
|
||||
0x25,
|
||||
]
|
||||
elif command == "~a":
|
||||
message.bytes += [0x98]
|
||||
elif command == "~b":
|
||||
message.bytes += [0x99]
|
||||
elif command == "~l":
|
||||
message.bytes += [0x9a]
|
||||
message.bytes += [0x9A]
|
||||
elif command == "~r":
|
||||
message.bytes += [0x9b]
|
||||
message.bytes += [0x9B]
|
||||
elif command == "~z":
|
||||
message.bytes += [0x9c]
|
||||
message.bytes += [0x9C]
|
||||
elif command == "~c-up":
|
||||
message.bytes += [0x9d]
|
||||
message.bytes += [0x9D]
|
||||
elif command == "~c-down":
|
||||
message.bytes += [0x9e]
|
||||
message.bytes += [0x9E]
|
||||
elif command == "~c-left":
|
||||
message.bytes += [0x9f]
|
||||
message.bytes += [0x9F]
|
||||
elif command == "~c-right":
|
||||
message.bytes += [0xa0]
|
||||
message.bytes += [0xA0]
|
||||
elif command == "~start":
|
||||
message.bytes += [0xa1]
|
||||
message.bytes += [0xA1]
|
||||
elif command == "note":
|
||||
message.bytes += [0x00]
|
||||
elif command == "heart":
|
||||
@ -1043,24 +1203,24 @@ if __name__ == "__main__":
|
||||
elif command == "restorepos":
|
||||
message.bytes += [0xFF, 0x23]
|
||||
elif command == "enablecdownnext":
|
||||
message.bytes += [0xFF, 0x2b]
|
||||
message.bytes += [0xFF, 0x2B]
|
||||
elif command == "beginchoice":
|
||||
choiceindex = 0
|
||||
message.bytes += [0xFF, 0x09] # delayoff
|
||||
message.bytes += [0xFF, 0x09] # delayoff
|
||||
elif command == "option" and choiceindex >= 0:
|
||||
message.bytes += [0xFF, 0x1E, choiceindex] # cursor n
|
||||
message.bytes += [0xFF, 0x21, choiceindex] # option n
|
||||
message.bytes += [0xFF, 0x1E, choiceindex] # cursor n
|
||||
message.bytes += [0xFF, 0x21, choiceindex] # option n
|
||||
choiceindex += 1
|
||||
elif command == "endchoice" and choiceindex >= 0:
|
||||
cancel = named_args.get("cancel")
|
||||
|
||||
message.bytes += [0xFF, 0x21, 255] # option 255
|
||||
message.bytes += [0xFF, 0x0A] # delayon
|
||||
message.bytes += [0xFF, 0x21, 255] # option 255
|
||||
message.bytes += [0xFF, 0x0A] # delayon
|
||||
|
||||
if isinstance(cancel, int):
|
||||
message.bytes += [0xFF, 0x20, cancel] # setcancel n
|
||||
message.bytes += [0xFF, 0x20, cancel] # setcancel n
|
||||
|
||||
message.bytes += [0xFF, 0x1F, choiceindex] # endchoice n
|
||||
message.bytes += [0xFF, 0x1F, choiceindex] # endchoice n
|
||||
|
||||
choiceindex = -1
|
||||
elif command == "animation" and choiceindex >= 0:
|
||||
@ -1074,7 +1234,7 @@ if __name__ == "__main__":
|
||||
if source[0] == "}":
|
||||
if not explicit_end:
|
||||
print(f"{filename}:{lineno}: warning: string lacks an [end] command")
|
||||
#message.bytes += [0xFD]
|
||||
# message.bytes += [0xFD]
|
||||
explicit_end = False
|
||||
|
||||
# sanity check
|
||||
@ -1087,7 +1247,7 @@ if __name__ == "__main__":
|
||||
message.bytes += [0x00]
|
||||
|
||||
message = None
|
||||
source = source[1:] # }
|
||||
source = source[1:] # }
|
||||
indent_level = 0
|
||||
choiceindex = -1
|
||||
continue
|
||||
@ -1124,9 +1284,15 @@ if __name__ == "__main__":
|
||||
|
||||
else:
|
||||
with open(outfile, "wb") as f:
|
||||
msgpack.pack([{
|
||||
"section": message.section,
|
||||
"index": message.index,
|
||||
"name": message.name,
|
||||
"bytes": bytes(message.bytes),
|
||||
} for message in messages], f)
|
||||
msgpack.pack(
|
||||
[
|
||||
{
|
||||
"section": message.section,
|
||||
"index": message.index,
|
||||
"name": message.name,
|
||||
"bytes": bytes(message.bytes),
|
||||
}
|
||||
for message in messages
|
||||
],
|
||||
f,
|
||||
)
|
||||
|
@ -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")
|
||||
|
@ -3,7 +3,7 @@
|
||||
from sys import argv
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv.pop(0) # python3
|
||||
argv.pop(0) # python3
|
||||
out = argv.pop(0)
|
||||
|
||||
with open(out, "wb") as f:
|
||||
@ -11,4 +11,4 @@ if __name__ == "__main__":
|
||||
with open(path, "rb") as j:
|
||||
f.write(j.read())
|
||||
while f.tell() % 8 != 0:
|
||||
f.write(b'\0')
|
||||
f.write(b"\0")
|
||||
|
@ -3,7 +3,7 @@
|
||||
from sys import argv
|
||||
|
||||
if __name__ == "__main__":
|
||||
argv.pop(0) # python3
|
||||
argv.pop(0) # python3
|
||||
out = argv.pop(0)
|
||||
|
||||
with open(out, "wb") as f:
|
||||
|
@ -9,6 +9,7 @@ import xml.etree.ElementTree as ET
|
||||
from img.build import Converter
|
||||
import png
|
||||
|
||||
|
||||
def get_img_file(fmt_str, img_file: str):
|
||||
def pack_color(r, g, b, a):
|
||||
r = r >> 3
|
||||
@ -34,6 +35,7 @@ def get_img_file(fmt_str, img_file: str):
|
||||
|
||||
return (out_img, out_pal, out_w, out_h)
|
||||
|
||||
|
||||
def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path, ...]):
|
||||
out_bytes = bytearray()
|
||||
offsets: Dict[str, int] = {}
|
||||
@ -47,11 +49,11 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path
|
||||
|
||||
if file is None:
|
||||
raise Exception("Icon os missing attribute: 'name'")
|
||||
|
||||
|
||||
if type is None:
|
||||
raise Exception("Icon os missing attribute: 'type'")
|
||||
|
||||
name = re.sub("\\W","_",file)
|
||||
|
||||
name = re.sub("\\W", "_", file)
|
||||
|
||||
if type == "solo" or type == "pair":
|
||||
img_path = str(get_asset_path(Path(f"icon/{file}.png"), asset_stack))
|
||||
@ -66,7 +68,7 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path
|
||||
if type == "pair":
|
||||
img_path = str(get_asset_path(Path(f"icon/{file}.disabled.png"), asset_stack))
|
||||
(out_img, out_pal, out_w, out_h) = get_img_file("CI4", str(img_path))
|
||||
|
||||
|
||||
offsets[name + "_disabled_raster"] = offsets[name + "_raster"]
|
||||
offsets[name + "_disabled_palette"] = len(out_bytes)
|
||||
out_bytes += out_pal
|
||||
@ -88,12 +90,13 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path
|
||||
f.write("#ifndef ICON_OFFSETS_H\n")
|
||||
f.write("#define ICON_OFFSETS_H\n")
|
||||
f.write(f"/* This file is auto-generated. Do not edit. */\n\n")
|
||||
|
||||
|
||||
for name, offset in offsets.items():
|
||||
f.write(f"#define ICON_{name} 0x{offset:X}\n")
|
||||
|
||||
f.write("\n#endif // ICON_OFFSETS_H\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Icon archive")
|
||||
parser.add_argument("out_bin", type=Path, help="output binary file path")
|
||||
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -59,7 +59,7 @@ def get_symbol_bytes(offsets, func):
|
||||
for ins in insns:
|
||||
ret.append(ins >> 2)
|
||||
|
||||
return bytes(ret).decode('utf-8'), bs
|
||||
return bytes(ret).decode("utf-8"), bs
|
||||
|
||||
|
||||
def parse_map(fname):
|
||||
@ -80,12 +80,7 @@ def parse_map(fname):
|
||||
continue
|
||||
prev_line = line
|
||||
|
||||
if (
|
||||
ram_offset is None
|
||||
or "=" in line
|
||||
or "*fill*" in line
|
||||
or " 0x" not in line
|
||||
):
|
||||
if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line:
|
||||
continue
|
||||
ram = int(line[16 : 16 + 18], 0)
|
||||
rom = ram - ram_offset
|
||||
@ -177,7 +172,7 @@ def do_query(query):
|
||||
break
|
||||
match_str = "{:.3f} - {}".format(matches[match], match)
|
||||
if match not in s_files:
|
||||
match_str += " (decompiled)"
|
||||
match_str += " (decompiled)"
|
||||
print(match_str)
|
||||
i += 1
|
||||
print()
|
||||
@ -203,7 +198,10 @@ def all_matches(all_funcs_flag):
|
||||
file = to_match_files[0]
|
||||
|
||||
i += 1
|
||||
print("File matching progress: {:%}".format(i / (len(s_files) - iter_limit)), end='\r')
|
||||
print(
|
||||
"File matching progress: {:%}".format(i / (len(s_files) - iter_limit)),
|
||||
end="\r",
|
||||
)
|
||||
|
||||
if get_symbol_length(file) < 16:
|
||||
to_match_files.remove(file)
|
||||
@ -241,18 +239,26 @@ def all_matches(all_funcs_flag):
|
||||
output_match_dict(match_dict, num_decomped_dupes, num_undecomped_dupes, num_perfect_dupes, i)
|
||||
|
||||
|
||||
def output_match_dict(match_dict, num_decomped_dupes, num_undecomped_dupes, num_perfect_dupes, num_checked_files):
|
||||
out_file = open(datetime.today().strftime('%Y-%m-%d-%H-%M-%S') + "_all_matches.txt", "w+")
|
||||
def output_match_dict(
|
||||
match_dict,
|
||||
num_decomped_dupes,
|
||||
num_undecomped_dupes,
|
||||
num_perfect_dupes,
|
||||
num_checked_files,
|
||||
):
|
||||
out_file = open(datetime.today().strftime("%Y-%m-%d-%H-%M-%S") + "_all_matches.txt", "w+")
|
||||
|
||||
out_file.write("Number of s-files: " + str(len(s_files)) + "\n"
|
||||
"Number of checked s-files: " + str(round(num_checked_files)) + "\n"
|
||||
"Number of decompiled duplicates found: " + str(num_decomped_dupes) + "\n"
|
||||
"Number of undecompiled duplicates found: " + str(num_undecomped_dupes) + "\n"
|
||||
"Number of overall exact duplicates found: " + str(num_perfect_dupes) + "\n\n")
|
||||
out_file.write(
|
||||
"Number of s-files: " + str(len(s_files)) + "\n"
|
||||
"Number of checked s-files: " + str(round(num_checked_files)) + "\n"
|
||||
"Number of decompiled duplicates found: " + str(num_decomped_dupes) + "\n"
|
||||
"Number of undecompiled duplicates found: " + str(num_undecomped_dupes) + "\n"
|
||||
"Number of overall exact duplicates found: " + str(num_perfect_dupes) + "\n\n"
|
||||
)
|
||||
|
||||
sorted_dict = OrderedDict(sorted(match_dict.items(), key=lambda item: item[1][0], reverse=True))
|
||||
|
||||
print("Creating output file: " + out_file.name, end='\n')
|
||||
print("Creating output file: " + out_file.name, end="\n")
|
||||
for file_name, matches in sorted_dict.items():
|
||||
out_file.write(file_name + " - found " + str(matches[0]) + " matches total:\n")
|
||||
for match in matches[1]:
|
||||
@ -261,19 +267,23 @@ def output_match_dict(match_dict, num_decomped_dupes, num_undecomped_dupes, num_
|
||||
|
||||
out_file.close()
|
||||
|
||||
|
||||
def is_decompiled(sym):
|
||||
return sym not in s_files
|
||||
|
||||
|
||||
def do_cross_query():
|
||||
ccount = Counter()
|
||||
clusters = []
|
||||
|
||||
sym_bytes = {}
|
||||
for sym_name in map_syms:
|
||||
if not sym_name.startswith("D_") and \
|
||||
not sym_name.startswith("_binary") and \
|
||||
not sym_name.startswith("jtbl_") and \
|
||||
not re.match(r"L[0-9A-F]{8}_[0-9A-F]{5,6}", sym_name):
|
||||
if (
|
||||
not sym_name.startswith("D_")
|
||||
and not sym_name.startswith("_binary")
|
||||
and not sym_name.startswith("jtbl_")
|
||||
and not re.match(r"L[0-9A-F]{8}_[0-9A-F]{5,6}", sym_name)
|
||||
):
|
||||
if get_symbol_length(sym_name) > 16:
|
||||
sym_bytes[sym_name] = get_symbol_bytes(map_offsets, sym_name)
|
||||
|
||||
@ -303,14 +313,48 @@ def do_cross_query():
|
||||
print(ccount.most_common(100))
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Tool to find duplicates for a specific function or to find all duplicates across the codebase.")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Tool to find duplicates for a specific function or to find all duplicates across the codebase."
|
||||
)
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("-a", "--all", help="find ALL duplicates and output them into a file", action='store_true', required=False)
|
||||
group.add_argument("-c", "--cross", help="do a cross query over the codebase", action='store_true', required=False)
|
||||
group.add_argument("-s", "--short", help="find MOST duplicates besides some very small duplicates. Cuts the runtime in half with minimal loss", action='store_true', required=False)
|
||||
parser.add_argument("query", help="function or file", nargs='?', default=None)
|
||||
parser.add_argument("-t", "--threshold", help="score threshold between 0 and 1 (higher is more restrictive)", type=float, default=0.9, required=False)
|
||||
parser.add_argument("-n", "--num-out", help="number of functions to display", type=int, default=100, required=False)
|
||||
group.add_argument(
|
||||
"-a",
|
||||
"--all",
|
||||
help="find ALL duplicates and output them into a file",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
group.add_argument(
|
||||
"-c",
|
||||
"--cross",
|
||||
help="do a cross query over the codebase",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
group.add_argument(
|
||||
"-s",
|
||||
"--short",
|
||||
help="find MOST duplicates besides some very small duplicates. Cuts the runtime in half with minimal loss",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument("query", help="function or file", nargs="?", default=None)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--threshold",
|
||||
help="score threshold between 0 and 1 (higher is more restrictive)",
|
||||
type=float,
|
||||
default=0.9,
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num-out",
|
||||
help="number of functions to display",
|
||||
type=int,
|
||||
default=100,
|
||||
required=False,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -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
|
||||
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,10 +42,9 @@ def import_c_file(in_file) -> str:
|
||||
out_text += subprocess.check_output(cpp_command2, cwd=root_dir, encoding="utf-8")
|
||||
except subprocess.CalledProcessError:
|
||||
print(
|
||||
"Failed to preprocess input file, when running command:\n"
|
||||
+ cpp_command,
|
||||
"Failed to preprocess input file, when running command:\n" + cpp_command,
|
||||
file=sys.stderr,
|
||||
)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if not out_text:
|
||||
@ -56,10 +55,9 @@ def import_c_file(in_file) -> str:
|
||||
out_text = out_text.replace(line + "\n", "")
|
||||
return out_text
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="""Create a context file which can be used for mips_to_c"""
|
||||
)
|
||||
parser = argparse.ArgumentParser(description="""Create a context file which can be used for mips_to_c""")
|
||||
parser.add_argument(
|
||||
"c_file",
|
||||
help="""File from which to create context""",
|
||||
|
@ -22,7 +22,7 @@ def data_to_c(file_path):
|
||||
output = ""
|
||||
pattern = re.compile(r"(dlabel (jtbl_.*|.+_.*)\n.(\w+) (.*))")
|
||||
|
||||
for (all, symbol, type, data) in re.findall(pattern, s):
|
||||
for all, symbol, type, data in re.findall(pattern, s):
|
||||
if type == "word":
|
||||
if symbol.startswith("jtbl_"):
|
||||
output += "dlabel " + symbol + "\n" + ".word " + data.replace("L", ".L") + "\n\n"
|
||||
@ -42,7 +42,7 @@ def out_to_file(output, file_path):
|
||||
if not os.path.exists(output_dir):
|
||||
os.mkdir(output_dir)
|
||||
|
||||
file_name = file_path[file_path.rfind("/"):-7]
|
||||
file_name = file_path[file_path.rfind("/") : -7]
|
||||
file = open("data2c/" + file_name + ".c", "w+")
|
||||
file.write(output)
|
||||
file.close()
|
||||
@ -74,9 +74,19 @@ def query(file, to_file):
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Tool to translate .data.s files to data arrays")
|
||||
parser.add_argument("query", help="data file", nargs='?', default=None)
|
||||
parser.add_argument("--all", help="translate all data files at once and output them into /data2c", action='store_true', required=False)
|
||||
parser.add_argument("--to-file", help="redirect the output into a file. Can not be used in combination with --all", action='store_true', required=False)
|
||||
parser.add_argument("query", help="data file", nargs="?", default=None)
|
||||
parser.add_argument(
|
||||
"--all",
|
||||
help="translate all data files at once and output them into /data2c",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--to-file",
|
||||
help="redirect the output into a file. Can not be used in combination with --all",
|
||||
action="store_true",
|
||||
required=False,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -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:
|
||||
|
@ -18,7 +18,7 @@ for line in inlines:
|
||||
break
|
||||
|
||||
if area:
|
||||
fname = line[line.rfind("`") + 1:line.rfind("'")]
|
||||
fname = line[line.rfind("`") + 1 : line.rfind("'")]
|
||||
renames[fname] = area
|
||||
|
||||
pairs = []
|
||||
|
@ -28,7 +28,7 @@ for filename, line_number, bad_symbol_name in problems:
|
||||
continue
|
||||
|
||||
if old_line.startswith("N(") or old_line.startswith("await N("):
|
||||
good_symbol_name = old_line[old_line.find("N("):].split(")", 1)[0] + ")"
|
||||
good_symbol_name = old_line[old_line.find("N(") :].split(")", 1)[0] + ")"
|
||||
else:
|
||||
good_symbol_name = old_line.split("(", 1)[0]
|
||||
|
||||
@ -38,4 +38,3 @@ for filename, line_number, bad_symbol_name in problems:
|
||||
|
||||
with open(filename, "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -6,8 +6,8 @@ from pathlib import Path
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("baserom")
|
||||
parser.add_argument("start", type=lambda x:int(x, 0))
|
||||
parser.add_argument("end", type=lambda x:int(x, 0))
|
||||
parser.add_argument("start", type=lambda x: int(x, 0))
|
||||
parser.add_argument("end", type=lambda x: int(x, 0))
|
||||
args = parser.parse_args()
|
||||
|
||||
baserom_path = Path(__file__).parent.parent / "baserom.z64"
|
||||
@ -25,9 +25,13 @@ while i < args.end:
|
||||
while unpack_from("B", baserom, i)[0] == 0:
|
||||
i += 1
|
||||
|
||||
#print(f"Start {hex(dis_start)} end {hex(i)}")
|
||||
gfxdis = subprocess.run(f"{gfxdis_path.resolve()} " + f"-x " + f"-dc " + f"-d {baserom[dis_start:i].hex()}",
|
||||
capture_output=True, shell=True, text=True)
|
||||
# print(f"Start {hex(dis_start)} end {hex(i)}")
|
||||
gfxdis = subprocess.run(
|
||||
f"{gfxdis_path.resolve()} " + f"-x " + f"-dc " + f"-d {baserom[dis_start:i].hex()}",
|
||||
capture_output=True,
|
||||
shell=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
commands = gfxdis.stdout.splitlines()[1:-1]
|
||||
new_commands = []
|
||||
|
@ -3,11 +3,23 @@ import argparse
|
||||
from struct import unpack_from
|
||||
|
||||
CONSTANTS = {}
|
||||
|
||||
|
||||
def get_constants():
|
||||
global CONSTANTS
|
||||
valid_enums = { "StoryProgress", "ItemIDs", "PlayerAnims",
|
||||
"ActorIDs", "Events", "SoundIDs", "SongIDs", "Locations",
|
||||
"AmbientSounds", "NpcIDs", "Emotes" }
|
||||
valid_enums = {
|
||||
"StoryProgress",
|
||||
"ItemIDs",
|
||||
"PlayerAnims",
|
||||
"ActorIDs",
|
||||
"Events",
|
||||
"SoundIDs",
|
||||
"SongIDs",
|
||||
"Locations",
|
||||
"AmbientSounds",
|
||||
"NpcIDs",
|
||||
"Emotes",
|
||||
}
|
||||
for enum in valid_enums:
|
||||
CONSTANTS[enum] = {}
|
||||
CONSTANTS["NPC_SPRITE"] = {}
|
||||
@ -16,7 +28,7 @@ def get_constants():
|
||||
enums = Path(include_path / "enums.h").read_text().splitlines()
|
||||
|
||||
# defines
|
||||
'''
|
||||
"""
|
||||
for line in enums.splitlines():
|
||||
this_enum = ""
|
||||
for enum in valid_enums:
|
||||
@ -31,12 +43,12 @@ def get_constants():
|
||||
id_ = id_.split(" ",1)[0]
|
||||
CONSTANTS[this_enum][int(id_, 16)] = name
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
# enums
|
||||
for i,line in enumerate(enums):
|
||||
for i, line in enumerate(enums):
|
||||
if line.startswith("enum "):
|
||||
enum_name = line.split(" ",1)[1].split(" {",1)[0]
|
||||
enum_name = line.split(" ", 1)[1].split(" {", 1)[0]
|
||||
if enum_name in valid_enums:
|
||||
CONSTANTS[enum_name] = {}
|
||||
last_num = 0
|
||||
@ -47,7 +59,7 @@ def get_constants():
|
||||
continue
|
||||
|
||||
name = enums[i].strip()
|
||||
val = last_num+1
|
||||
val = last_num + 1
|
||||
if "=" in name:
|
||||
name, val = name.split(" = ")
|
||||
val = int(val[:-1], 0)
|
||||
@ -56,14 +68,16 @@ def get_constants():
|
||||
else:
|
||||
name = name[:-1]
|
||||
name = name.strip()
|
||||
#print("\"" + name + "\"", "===", val)
|
||||
# print("\"" + name + "\"", "===", val)
|
||||
|
||||
CONSTANTS[enum_name][val] = name.strip()
|
||||
i += 1
|
||||
last_num = val
|
||||
|
||||
# sprites
|
||||
sprite_path = Path(Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc")
|
||||
sprite_path = Path(
|
||||
Path(__file__).resolve().parent.parent / "ver" / "current" / "build" / "include" / "sprite" / "npc"
|
||||
)
|
||||
for file in sprite_path.iterdir():
|
||||
fd = file.read_text()
|
||||
for line in fd.splitlines():
|
||||
@ -76,10 +90,10 @@ def get_constants():
|
||||
else:
|
||||
continue
|
||||
|
||||
name = line.split(" ",2)[1]
|
||||
name = line.split(" ", 2)[1]
|
||||
id_ = line.split("0x", 1)[1]
|
||||
if " " in id_:
|
||||
id_ = id_.split(" ",1)[0]
|
||||
id_ = id_.split(" ", 1)[0]
|
||||
name = name.split(f"_{enum}_", 1)[1]
|
||||
if enum == "NPC_SPRITE":
|
||||
saved_name = name
|
||||
@ -89,7 +103,11 @@ def get_constants():
|
||||
|
||||
if enum == "NPC_SPRITE":
|
||||
if int(id_, 16) not in CONSTANTS["NPC_SPRITE"]:
|
||||
CONSTANTS[enum][int(id_, 16)] = {"name":"", "palettes":{}, "anims":{}}
|
||||
CONSTANTS[enum][int(id_, 16)] = {
|
||||
"name": "",
|
||||
"palettes": {},
|
||||
"anims": {},
|
||||
}
|
||||
CONSTANTS[enum][int(id_, 16)]["name"] = name
|
||||
elif enum == "NPC_PALETTE":
|
||||
CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["palettes"][int(id_, 16)] = name
|
||||
@ -97,20 +115,22 @@ def get_constants():
|
||||
CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["anims"][int(id_, 16)] = name
|
||||
return
|
||||
|
||||
|
||||
STRUCTS = {}
|
||||
|
||||
|
||||
def parse_var(line):
|
||||
#print(f"Parsing {line}")
|
||||
# print(f"Parsing {line}")
|
||||
if "*/ " in line:
|
||||
line = line.split("*/ ",1)[1]
|
||||
line = line.split(";",1)[0].strip()
|
||||
line = line.split("*/ ", 1)[1]
|
||||
line = line.split(";", 1)[0].strip()
|
||||
|
||||
if "," in line or "(*" in line:
|
||||
return (None, None, None, None)
|
||||
elif "union " in line:
|
||||
return ("union", None, None, None)
|
||||
|
||||
#print(f"Parsed {line}")
|
||||
# print(f"Parsed {line}")
|
||||
if " " in line:
|
||||
if line.startswith("struct "):
|
||||
struct, type_, name = line.split(" ")
|
||||
@ -131,22 +151,23 @@ def parse_var(line):
|
||||
is_ptr = "*" in type_ or type_ == "UNK_PTR"
|
||||
return (type_, name, count, is_ptr)
|
||||
|
||||
|
||||
def parse_file(filename):
|
||||
fd = filename.read_text().splitlines()
|
||||
i = 0
|
||||
while i < len(fd):
|
||||
#supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS]
|
||||
#if any(supported):
|
||||
# supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS]
|
||||
# if any(supported):
|
||||
if "typedef struct " in fd[i]:
|
||||
#supported_name = [SUPPORTED_STRUCTS[i] for i,x in enumerate(supported) if x][0]
|
||||
# supported_name = [SUPPORTED_STRUCTS[i] for i,x in enumerate(supported) if x][0]
|
||||
supported_name = fd[i].split("typedef struct ", 1)[1].split(" {", 1)[0]
|
||||
if supported_name == "{":
|
||||
supported_name = ""
|
||||
#print(f"Parsing struct \"{supported_name}\"")
|
||||
# print(f"Parsing struct \"{supported_name}\"")
|
||||
|
||||
struct_to_add = []
|
||||
i += 1
|
||||
while ("} " + f"{supported_name.upper()}") not in fd[i].split(";",1)[0].upper():
|
||||
while ("} " + f"{supported_name.upper()}") not in fd[i].split(";", 1)[0].upper():
|
||||
type_, name, count, ptr = parse_var(fd[i])
|
||||
|
||||
if type_ == None:
|
||||
@ -158,27 +179,37 @@ def parse_file(filename):
|
||||
i += 1
|
||||
while "}" not in fd[i]:
|
||||
type_, name, count, ptr = parse_var(fd[i])
|
||||
union.append({"type":type_, "name": name, "num":count, "ptr":ptr})
|
||||
union.append({"type": type_, "name": name, "num": count, "ptr": ptr})
|
||||
i += 1
|
||||
name = fd[i].split("}", 1)[1].split(";", 1)[0]
|
||||
|
||||
#print(supported_name, type_, name, count)
|
||||
struct_to_add.append({"type":type_, "name": name, "num":count, "ptr":ptr, "union":union})
|
||||
# print(supported_name, type_, name, count)
|
||||
struct_to_add.append(
|
||||
{
|
||||
"type": type_,
|
||||
"name": name,
|
||||
"num": count,
|
||||
"ptr": ptr,
|
||||
"union": union,
|
||||
}
|
||||
)
|
||||
|
||||
i += 1
|
||||
#print(f"Broke on line {fd[i]}")
|
||||
#print()
|
||||
# print(f"Broke on line {fd[i]}")
|
||||
# print()
|
||||
if supported_name == "":
|
||||
supported_name = fd[i].split("} ",1)[1].split(";",1)[0]
|
||||
supported_name = fd[i].split("} ", 1)[1].split(";", 1)[0]
|
||||
if "[" in supported_name:
|
||||
supported_name = supported_name[:-2]
|
||||
STRUCTS[supported_name] = struct_to_add
|
||||
i += 1
|
||||
|
||||
|
||||
def get_structs():
|
||||
parse_file(Path(Path(__file__).parent.parent / "include" / "map.h"))
|
||||
parse_file(Path(Path(__file__).parent.parent / "include" / "common_structs.h"))
|
||||
|
||||
|
||||
def get_vals(fd, offset, var):
|
||||
global STRUCTS
|
||||
|
||||
@ -192,63 +223,63 @@ def get_vals(fd, offset, var):
|
||||
for var2 in STRUCTS[var["type"]]:
|
||||
out3, offset = get_vals(fd, offset, var2)
|
||||
data.extend(out3)
|
||||
#if var["num"] == 1:
|
||||
# if var["num"] == 1:
|
||||
# out.extend(out2)
|
||||
#else:
|
||||
#out.append(out2)
|
||||
# else:
|
||||
# out.append(out2)
|
||||
else:
|
||||
type_ = "int"
|
||||
fmt = "d"
|
||||
if var["type"] == "s8" or var["type"] == "char":
|
||||
if var["type"] == "char":
|
||||
if var["type"] == "s8" or var["type"] == "char":
|
||||
if var["type"] == "char":
|
||||
type_ = "hex"
|
||||
fmt = "X"
|
||||
data = unpack_from('>b', fd, offset)[0]
|
||||
data = unpack_from(">b", fd, offset)[0]
|
||||
offset += 1
|
||||
elif var["type"] == "u8":
|
||||
data = unpack_from('>B', fd, offset)[0]
|
||||
elif var["type"] == "u8":
|
||||
data = unpack_from(">B", fd, offset)[0]
|
||||
fmt = "d"
|
||||
offset += 1
|
||||
elif var["type"] == "s16" or var["type"] in ("s16"):
|
||||
elif var["type"] == "s16" or var["type"] in ("s16"):
|
||||
offset += offset % 2
|
||||
data = unpack_from('>h', fd, offset)[0]
|
||||
data = unpack_from(">h", fd, offset)[0]
|
||||
fmt = "d"
|
||||
offset += 2
|
||||
elif var["type"] == "u16":
|
||||
elif var["type"] == "u16":
|
||||
offset += offset % 2
|
||||
data = unpack_from('>H', fd, offset)[0]
|
||||
data = unpack_from(">H", fd, offset)[0]
|
||||
fmt = "d"
|
||||
offset += 2
|
||||
elif var["type"] == "s32" or var["type"] in ("s32"):
|
||||
elif var["type"] == "s32" or var["type"] in ("s32"):
|
||||
poff = offset
|
||||
offset += offset % 4
|
||||
data = unpack_from('>i', fd, offset)[0]
|
||||
data = unpack_from(">i", fd, offset)[0]
|
||||
fmt = "d"
|
||||
offset += 4
|
||||
elif var["type"] == "u32":
|
||||
elif var["type"] == "u32":
|
||||
offset += offset % 4
|
||||
data = unpack_from('>I', fd, offset)[0]
|
||||
data = unpack_from(">I", fd, offset)[0]
|
||||
fmt = "d"
|
||||
offset += 4
|
||||
elif var["type"] == "f32":
|
||||
elif var["type"] == "f32":
|
||||
offset += offset % 4
|
||||
data = unpack_from('>f', fd, offset)[0]
|
||||
data = unpack_from(">f", fd, offset)[0]
|
||||
type_ = "float"
|
||||
fmt = ".01f"
|
||||
offset += 4
|
||||
elif var["type"] == "X32":
|
||||
offset += offset % 4
|
||||
data = unpack_from('>f', fd, offset)[0]
|
||||
data = unpack_from(">f", fd, offset)[0]
|
||||
type_ = "Xfloat"
|
||||
fmt = ".01f"
|
||||
if data < -1000.0 or data > 1000.0:
|
||||
type_ = "Xint"
|
||||
fmt = "d"
|
||||
data = unpack_from('>i', fd, offset)[0]
|
||||
data = unpack_from(">i", fd, offset)[0]
|
||||
offset += 4
|
||||
elif var["ptr"]:
|
||||
offset += offset % 4
|
||||
data = unpack_from('>I', fd, offset)[0]
|
||||
data = unpack_from(">I", fd, offset)[0]
|
||||
type_ = "ptr"
|
||||
fmt = "08X"
|
||||
offset += 4
|
||||
@ -256,24 +287,26 @@ def get_vals(fd, offset, var):
|
||||
print(f"Unknown data type \"{var['type']}\"")
|
||||
exit()
|
||||
if var["num"] == 1:
|
||||
out.append({"name":var["name"], "type":type_, "fmt":fmt, "data":data})
|
||||
out.append({"name": var["name"], "type": type_, "fmt": fmt, "data": data})
|
||||
else:
|
||||
arr.append({"name":var["name"], "type":type_, "fmt":fmt, "data":data})
|
||||
arr.append({"name": var["name"], "type": type_, "fmt": fmt, "data": data})
|
||||
|
||||
if var["num"] > 1:
|
||||
out.append(arr)
|
||||
return out, offset
|
||||
|
||||
|
||||
def INDENT(depth):
|
||||
return f" " * depth
|
||||
|
||||
|
||||
def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
||||
out = []
|
||||
for val in vals:
|
||||
line = ""
|
||||
if needs_name:
|
||||
line = INDENT(indent)
|
||||
#print(val)
|
||||
# print(val)
|
||||
# array
|
||||
if type(val) is list:
|
||||
line += f".{val[0]['name']} = " + "{ "
|
||||
@ -291,10 +324,10 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
||||
line += "\n"
|
||||
line += INDENT(indent)
|
||||
line += "{ "
|
||||
for x,val2 in enumerate(val["data"]):
|
||||
for x, val2 in enumerate(val["data"]):
|
||||
if x > 0:
|
||||
line += ", "
|
||||
#line += f".{val2['name']} = "
|
||||
# line += f".{val2['name']} = "
|
||||
fmt = val2["fmt"]
|
||||
if val2["type"] == "float":
|
||||
line += f"{val2['data']:{fmt}}f"
|
||||
@ -315,7 +348,6 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
||||
if not is_array:
|
||||
line += ","
|
||||
else:
|
||||
|
||||
if "flags" in val["name"].lower() or "animations" in val["name"].lower():
|
||||
if val["name"] == "flags":
|
||||
val["fmt"] = "08X"
|
||||
@ -338,10 +370,10 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
||||
elif val["type"] == "hex":
|
||||
line += f"0x{val['data']:{fmt}}"
|
||||
elif val["type"] == "ptr":
|
||||
if val["data"] == 0:
|
||||
line += f"NULL"
|
||||
else:
|
||||
line += f"0x{val['data']:{fmt}}"
|
||||
if val["data"] == 0:
|
||||
line += f"NULL"
|
||||
else:
|
||||
line += f"0x{val['data']:{fmt}}"
|
||||
else:
|
||||
line += f"{val['data']}"
|
||||
|
||||
@ -352,6 +384,7 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def output_type2(fd, count, offset, var):
|
||||
ultra_out = []
|
||||
for i in range(count):
|
||||
@ -364,10 +397,11 @@ def output_type2(fd, count, offset, var):
|
||||
out.extend(print_data(vals, 1, True))
|
||||
out.append("};")
|
||||
ultra_out.append(out)
|
||||
return offset, ultra_out #"\n".join(out)
|
||||
return offset, ultra_out # "\n".join(out)
|
||||
|
||||
def check_list(vals, depth = 0):
|
||||
for x,val in enumerate(vals):
|
||||
|
||||
def check_list(vals, depth=0):
|
||||
for x, val in enumerate(vals):
|
||||
if type(val) == list:
|
||||
if check_list(val, depth + 1):
|
||||
return True
|
||||
@ -375,9 +409,10 @@ def check_list(vals, depth = 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def recurse_check_list(vals):
|
||||
res = 0
|
||||
for x,val in enumerate(vals):
|
||||
for x, val in enumerate(vals):
|
||||
if type(val) == list:
|
||||
if check_list(val, 1):
|
||||
return len(vals) - x
|
||||
@ -385,14 +420,15 @@ def recurse_check_list(vals):
|
||||
return len(vals) - x
|
||||
return -1
|
||||
|
||||
|
||||
def get_single_struct_vals(fd, i):
|
||||
vals = []
|
||||
if not fd[i].rstrip().endswith("},"):
|
||||
# must be a sub-struct over multiple lines
|
||||
old_i = i
|
||||
i += 1
|
||||
while not ("}," in fd[i] and "." in fd[i+1]):
|
||||
temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ")
|
||||
while not ("}," in fd[i] and "." in fd[i + 1]):
|
||||
temp = fd[i].split("{", 1)[1].split("}", 1)[0].split(", ")
|
||||
a = []
|
||||
for x in temp:
|
||||
x = x.strip()
|
||||
@ -405,7 +441,7 @@ def get_single_struct_vals(fd, i):
|
||||
i += 1
|
||||
else:
|
||||
# single line
|
||||
temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ")
|
||||
temp = fd[i].split("{", 1)[1].split("}", 1)[0].split(", ")
|
||||
a = []
|
||||
for x in temp:
|
||||
x = x.strip()
|
||||
@ -417,10 +453,11 @@ def get_single_struct_vals(fd, i):
|
||||
vals.extend(a)
|
||||
return vals, i
|
||||
|
||||
|
||||
def cull_struct(fd, i, entirely=False):
|
||||
out = []
|
||||
vals = []
|
||||
#print(f"Culling Starting at {fd[i]}")
|
||||
# print(f"Culling Starting at {fd[i]}")
|
||||
if not fd[i].rstrip().endswith("},"):
|
||||
# must be a sub-struct over multiple lines
|
||||
old_i = i
|
||||
@ -428,9 +465,9 @@ def cull_struct(fd, i, entirely=False):
|
||||
|
||||
# reverse and cull entries of only zeros
|
||||
x = recurse_check_list(vals[::-1])
|
||||
#print(f"Found first index of empty values at idx {x}, vals: {vals}")
|
||||
# print(f"Found first index of empty values at idx {x}, vals: {vals}")
|
||||
if x < 0:
|
||||
#print(f"Ending at {fd[i]}")
|
||||
# print(f"Ending at {fd[i]}")
|
||||
return None, i
|
||||
|
||||
out.append(fd[old_i])
|
||||
@ -441,36 +478,37 @@ def cull_struct(fd, i, entirely=False):
|
||||
out.append(fd[old_i])
|
||||
old_i += 1
|
||||
|
||||
#print(f"Ending at {fd[i]}")
|
||||
# print(f"Ending at {fd[i]}")
|
||||
else:
|
||||
prefix = fd[i].split("{",1)[0] + "{ "
|
||||
prefix = fd[i].split("{", 1)[0] + "{ "
|
||||
suffix = " },"
|
||||
|
||||
vals, i = get_single_struct_vals(fd, i)
|
||||
|
||||
# reverse and cull entries of only zeros
|
||||
x = recurse_check_list(vals[::-1])
|
||||
#print(f"Found first index of empty values at idx {x}, vals: {vals}")
|
||||
# print(f"Found first index of empty values at idx {x}, vals: {vals}")
|
||||
if x < 0:
|
||||
#print(f"Ending at {fd[i]}")
|
||||
# print(f"Ending at {fd[i]}")
|
||||
return None, i
|
||||
|
||||
#out.append(prefix)
|
||||
# out.append(prefix)
|
||||
if entirely:
|
||||
x = len(vals)
|
||||
temp = ""
|
||||
for z,y in enumerate(range(x)):
|
||||
for z, y in enumerate(range(x)):
|
||||
if z > 0:
|
||||
prefix += ", "
|
||||
prefix += f"{vals[y]}"
|
||||
out.append(prefix + suffix)
|
||||
#print(f"Ending at {fd[i]}")
|
||||
# print(f"Ending at {fd[i]}")
|
||||
return "\n".join(out), i
|
||||
|
||||
|
||||
def MacroReplaceStaticNPC(fd):
|
||||
structs = { "unk_1C":True, "movement":False, "unk_1E0":True }
|
||||
#replace_cull_struct = { "unk_1C", "movement", "unk_1E0" }
|
||||
#replace_cull = { "tattle", "extraAnimations", "itemDropChance" }
|
||||
structs = {"unk_1C": True, "movement": False, "unk_1E0": True}
|
||||
# replace_cull_struct = { "unk_1C", "movement", "unk_1E0" }
|
||||
# replace_cull = { "tattle", "extraAnimations", "itemDropChance" }
|
||||
fd = fd.splitlines()
|
||||
out = []
|
||||
i = 0
|
||||
@ -479,7 +517,7 @@ def MacroReplaceStaticNPC(fd):
|
||||
for x in structs:
|
||||
if f".{x}" in fd[i]:
|
||||
found = x
|
||||
break;
|
||||
break
|
||||
if found:
|
||||
# just cull it if possible
|
||||
vals, i = cull_struct(fd, i, structs[found])
|
||||
@ -489,9 +527,9 @@ def MacroReplaceStaticNPC(fd):
|
||||
i += 1
|
||||
continue
|
||||
|
||||
name = fd[i].split(" = ",1)[0].strip()[1:]
|
||||
name = fd[i].split(" = ", 1)[0].strip()[1:]
|
||||
if name not in structs and "{" not in fd[i] and name != ";":
|
||||
val = fd[i].split(" = ",1)[1][:-1]
|
||||
val = fd[i].split(" = ", 1)[1][:-1]
|
||||
if "0x" in val:
|
||||
val = int(val, 16)
|
||||
elif "NULL" in val:
|
||||
@ -507,8 +545,8 @@ def MacroReplaceStaticNPC(fd):
|
||||
if ".itemDrops" in fd[i]:
|
||||
vals, x = cull_struct(fd, i)
|
||||
|
||||
indent = len(fd[i].split(".",1)[0]) // 4
|
||||
new_line = fd[i].split("{",1)[0] + "{\n"
|
||||
indent = len(fd[i].split(".", 1)[0]) // 4
|
||||
new_line = fd[i].split("{", 1)[0] + "{\n"
|
||||
|
||||
if not vals:
|
||||
i = x
|
||||
@ -525,7 +563,7 @@ def MacroReplaceStaticNPC(fd):
|
||||
|
||||
added_item = True
|
||||
item_name = CONSTANTS["ItemIDs"][item[0]]
|
||||
new_line += " " * (indent+1) + "{ " + item_name + f", {item[1]}, {item[2]}" + " },\n"
|
||||
new_line += " " * (indent + 1) + "{ " + item_name + f", {item[1]}, {item[2]}" + " },\n"
|
||||
|
||||
if added_item:
|
||||
new_line += " " * indent + "},"
|
||||
@ -534,18 +572,18 @@ def MacroReplaceStaticNPC(fd):
|
||||
i = x
|
||||
|
||||
elif ".animations" in fd[i]:
|
||||
indent = len(fd[i].split(".",1)[0]) // 4
|
||||
new_line = fd[i].split("{",1)[0] + "{\n"
|
||||
indent = len(fd[i].split(".", 1)[0]) // 4
|
||||
new_line = fd[i].split("{", 1)[0] + "{\n"
|
||||
|
||||
vals, x = get_single_struct_vals(fd, i)
|
||||
for val in vals:
|
||||
sprite_id = (val & 0x00FF0000) >> 16
|
||||
sprite_id = (val & 0x00FF0000) >> 16
|
||||
palette_id = (val & 0x0000FF00) >> 8
|
||||
anim_id = (val & 0x000000FF) >> 0
|
||||
sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||
anim_id = (val & 0x000000FF) >> 0
|
||||
sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||
palette = CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
||||
anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||
new_line += " " * (indent+1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||
anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||
new_line += " " * (indent + 1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||
new_line += " " * indent + "},"
|
||||
out.append(new_line)
|
||||
i = x
|
||||
@ -553,7 +591,7 @@ def MacroReplaceStaticNPC(fd):
|
||||
elif ".heartDrops" in fd[i] or ".flowerDrops" in fd[i]:
|
||||
vals, x = get_single_struct_vals(fd, i)
|
||||
|
||||
new_line = fd[i].split("{",1)[0]
|
||||
new_line = fd[i].split("{", 1)[0]
|
||||
|
||||
attempts = vals[0][2]
|
||||
|
||||
@ -564,10 +602,16 @@ def MacroReplaceStaticNPC(fd):
|
||||
new_line += f"GENEROUS_HEART_DROPS({attempts}),"
|
||||
elif round(vals[0][1] / 327.67, 2) == 80 and round(vals[0][3] / 327.67, 2) == 60:
|
||||
new_line += f"GENEROUS_WHEN_LOW_HEART_DROPS({attempts}),"
|
||||
elif round(vals[0][0] / 327.67, 2) == 100 and round(vals[0][1] / 327.67, 2) == 0 and round(vals[0][2] / 327.67, 2) == 0:
|
||||
elif (
|
||||
round(vals[0][0] / 327.67, 2) == 100
|
||||
and round(vals[0][1] / 327.67, 2) == 0
|
||||
and round(vals[0][2] / 327.67, 2) == 0
|
||||
):
|
||||
new_line += f"NO_DROPS,"
|
||||
else:
|
||||
print(f"Unknown heart drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}")
|
||||
print(
|
||||
f"Unknown heart drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}"
|
||||
)
|
||||
exit()
|
||||
else:
|
||||
if round(vals[0][1] / 327.67, 2) == 50 and round(vals[0][3] / 327.67, 2) == 40:
|
||||
@ -576,10 +620,16 @@ def MacroReplaceStaticNPC(fd):
|
||||
new_line += f"GENEROUS_WHEN_LOW_FLOWER_DROPS({attempts}),"
|
||||
elif round(vals[0][1] / 327.67, 2) == 40 and round(vals[0][3] / 327.67, 2) == 40:
|
||||
new_line += f"REDUCED_FLOWER_DROPS({attempts}),"
|
||||
elif round(vals[0][0] / 327.67, 2) == 100 and round(vals[0][1] / 327.67, 2) == 0 and round(vals[0][2] / 327.67, 2) == 0:
|
||||
elif (
|
||||
round(vals[0][0] / 327.67, 2) == 100
|
||||
and round(vals[0][1] / 327.67, 2) == 0
|
||||
and round(vals[0][2] / 327.67, 2) == 0
|
||||
):
|
||||
new_line += f"NO_DROPS,"
|
||||
else:
|
||||
print(f"Unknown flower drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}")
|
||||
print(
|
||||
f"Unknown flower drop macro, values were {round(vals[0][1] / 327.67, 2)} and {round(vals[0][3] / 327.67, 2)}"
|
||||
)
|
||||
exit()
|
||||
|
||||
out.append(new_line)
|
||||
@ -590,8 +640,9 @@ def MacroReplaceStaticNPC(fd):
|
||||
i += 1
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def MacroReplaceNpcSettings(fd):
|
||||
replace_cull = { "unk_00", "unk_24" }
|
||||
replace_cull = {"unk_00", "unk_24"}
|
||||
fd = fd.splitlines()
|
||||
out = []
|
||||
i = 0
|
||||
@ -605,6 +656,7 @@ def MacroReplaceNpcSettings(fd):
|
||||
i += 1
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def MacroReplaceNpcGroupList(fd):
|
||||
fd = fd.splitlines()
|
||||
out = []
|
||||
@ -617,21 +669,31 @@ def MacroReplaceNpcGroupList(fd):
|
||||
val = 0
|
||||
else:
|
||||
if "0x" in fd[i]:
|
||||
val = int(fd[i].split(" = ",1)[1][:-1], 16)
|
||||
val = int(fd[i].split(" = ", 1)[1][:-1], 16)
|
||||
else:
|
||||
val = int(fd[i].split(" = ",1)[1][:-1], 10)
|
||||
val = int(fd[i].split(" = ", 1)[1][:-1], 10)
|
||||
|
||||
vals.append(val)
|
||||
i += 1
|
||||
|
||||
out.append(f" NPC_GROUP(N(D_{vals[1]:X}), BATTLE_ID({(vals[2] & 0xFF000000) >> 24}, {(vals[2] & 0xFF0000) >> 16}, {(vals[2] & 0xFF00) >> 8}, {vals[2] & 0xFF})),")
|
||||
out.append(
|
||||
f" NPC_GROUP(N(D_{vals[1]:X}), BATTLE_ID({(vals[2] & 0xFF000000) >> 24}, {(vals[2] & 0xFF0000) >> 16}, {(vals[2] & 0xFF00) >> 8}, {vals[2] & 0xFF})),"
|
||||
)
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("file", type=str, help="File to decompile struct from")
|
||||
parser.add_argument("type", type=str, help="Struct type to decompile")
|
||||
parser.add_argument("offset", type=lambda x: int(x, 0), help="Offset to decompile struct from")
|
||||
parser.add_argument("--count", "-c", "--c", type=int, default=0, help="Num to try and decompile (NpcGroupList)")
|
||||
parser.add_argument(
|
||||
"--count",
|
||||
"-c",
|
||||
"--c",
|
||||
type=int,
|
||||
default=0,
|
||||
help="Num to try and decompile (NpcGroupList)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
get_constants()
|
||||
@ -641,14 +703,14 @@ if args.type not in STRUCTS:
|
||||
print(f"Unknown struct type {args.type}")
|
||||
exit()
|
||||
|
||||
'''
|
||||
"""
|
||||
out = [f"{args.type} = " + "{\n"]
|
||||
offset = args.offset
|
||||
for var in STRUCTS[args.type]:
|
||||
line, offset = output_type(fd, offset, var, 1)
|
||||
out.append(line)
|
||||
out.append("};")
|
||||
'''
|
||||
"""
|
||||
|
||||
if args.count == 0:
|
||||
args.count = 1
|
||||
@ -656,7 +718,7 @@ if args.count == 0:
|
||||
fd = Path(args.file).resolve().read_bytes()
|
||||
offset, out = output_type2(fd, args.count, args.offset, STRUCTS[args.type])
|
||||
|
||||
for i,entry in enumerate(out):
|
||||
for i, entry in enumerate(out):
|
||||
out[i] = "\n".join(entry)
|
||||
|
||||
print(f"EvtScript range 0x{args.offset:08X} - 0x{offset:08X}")
|
||||
|
@ -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"))
|
||||
|
@ -10,7 +10,7 @@ with open("tools/symz.txt") as f:
|
||||
for line in f.readlines():
|
||||
if line.strip() and not line.startswith("//"):
|
||||
name, addr = line.strip().strip(";").split(" = ")
|
||||
try :
|
||||
try:
|
||||
addr = int(addr, 0)
|
||||
except ValueError:
|
||||
continue
|
||||
|
@ -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()
|
||||
|
||||
|
@ -47,11 +47,14 @@ def parse_symbol_addrs():
|
||||
symbol_addrs = {}
|
||||
|
||||
for line in lines:
|
||||
name = line[:line.find(" ")]
|
||||
name = line[: line.find(" ")]
|
||||
|
||||
attributes = line[line.find("//"):].split(" ")
|
||||
ram_addr = int(line[:line.find(";")].split("=")[1].strip(), base=0)
|
||||
rom_addr = next((int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"), None)
|
||||
attributes = line[line.find("//") :].split(" ")
|
||||
ram_addr = int(line[: line.find(";")].split("=")[1].strip(), base=0)
|
||||
rom_addr = next(
|
||||
(int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"),
|
||||
None,
|
||||
)
|
||||
|
||||
symbol_addrs[name] = Symbol(ram_addr, rom_addr)
|
||||
|
||||
@ -73,7 +76,7 @@ def find_old_script_ranges(lines, filename):
|
||||
|
||||
if "#define NAMESPACE " in line_content:
|
||||
namespace = line_content.split(" ")[2].strip()
|
||||
#elif namespace == "events" or namespace == "header":
|
||||
# elif namespace == "events" or namespace == "header":
|
||||
# namespace = NAMESPACES.get(filename, filename.split("/")[-2].split(".")[0])
|
||||
elif namespace_temp is not None:
|
||||
namespace = namespace_temp[0][:-2]
|
||||
@ -113,7 +116,7 @@ def replace_old_script_macros(filename, symbol_addrs):
|
||||
lines[range.start] = lines[range.start][:macro_start_idx] + "{\n"
|
||||
|
||||
# Remove other lines
|
||||
lines = lines[:range.start + 1] + lines[range.end + 1:]
|
||||
lines = lines[: range.start + 1] + lines[range.end + 1 :]
|
||||
|
||||
# Find the symbol
|
||||
try:
|
||||
@ -128,7 +131,7 @@ def replace_old_script_macros(filename, symbol_addrs):
|
||||
local_symbol_map = {}
|
||||
for sym in symbol_addrs:
|
||||
if sym.startswith(range.namespace):
|
||||
key = "N(" + sym[len(range.namespace)+1:] + ")"
|
||||
key = "N(" + sym[len(range.namespace) + 1 :] + ")"
|
||||
else:
|
||||
key = sym
|
||||
|
||||
@ -143,7 +146,8 @@ def replace_old_script_macros(filename, symbol_addrs):
|
||||
|
||||
# Disassemble the script
|
||||
rom.seek(range_sym.rom_addr)
|
||||
evt_code = ScriptDisassembler(rom,
|
||||
evt_code = ScriptDisassembler(
|
||||
rom,
|
||||
script_name=range.symbol_name,
|
||||
romstart=range_sym.rom_addr,
|
||||
prelude=False,
|
||||
|
@ -16,6 +16,7 @@ renames = {}
|
||||
patterns = []
|
||||
deletes = []
|
||||
|
||||
|
||||
def handle_file(f_path, try_rename_file=False):
|
||||
with open(f_path) as f:
|
||||
f_text_orig = f.read()
|
||||
@ -35,17 +36,18 @@ def handle_file(f_path, try_rename_file=False):
|
||||
# replace all matches
|
||||
for match in matches:
|
||||
# head part
|
||||
to_join.append(f_text[pos:match[1]])
|
||||
to_join.append(f_text[pos : match[1]])
|
||||
to_replace = patterns[match[0]]
|
||||
to_join.append(renames[to_replace])
|
||||
pos = match[2]
|
||||
# tail part
|
||||
to_join.append(f_text[pos:])
|
||||
f_text = ''.join(to_join);
|
||||
f_text = "".join(to_join)
|
||||
# save changes
|
||||
with open(f_path, "w", newline="\n") as f:
|
||||
f.write(f_text)
|
||||
|
||||
|
||||
# Read input file
|
||||
# One valid whitespace-separated find-replace pair is given per line
|
||||
with open(os.path.join(script_dir, "to_rename.txt")) as f:
|
||||
@ -59,7 +61,7 @@ for line in renames_text:
|
||||
renames[split[0]] = split[1]
|
||||
patterns.append(split[0])
|
||||
elif len(split) != 0:
|
||||
raise Exception("input contains invalid rename pattern: \n\"" + line.strip() + "\"")
|
||||
raise Exception('input contains invalid rename pattern: \n"' + line.strip() + '"')
|
||||
|
||||
ac = ahocorasick_rs.AhoCorasick(patterns, matchkind=MATCHKIND_LEFTMOST_LONGEST)
|
||||
|
||||
|
@ -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
|
||||
|
@ -13,22 +13,22 @@ script_dir = Path(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
|
||||
def indent(elem, level=0):
|
||||
i = "\n" + level*" "
|
||||
i = "\n" + level * " "
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + " "
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
indent(elem, level+1)
|
||||
indent(elem, level + 1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
elem.tail = i
|
||||
|
||||
|
||||
def pretty_print_xml(tree : ET.ElementTree, path : Path):
|
||||
def pretty_print_xml(tree: ET.ElementTree, path: Path):
|
||||
root = tree.getroot()
|
||||
indent(root)
|
||||
xml_str = ET.tostring(root, encoding="unicode")
|
||||
@ -49,11 +49,11 @@ def parse_palette(data):
|
||||
class N64SegPm_icons(N64Segment):
|
||||
def split(self, rom_bytes):
|
||||
self.out_dir = options.opts.asset_path / "icon"
|
||||
|
||||
|
||||
with open(script_dir / "icon.yaml") as f:
|
||||
self.icons = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
||||
|
||||
data = rom_bytes[self.rom_start: self.rom_end]
|
||||
data = rom_bytes[self.rom_start : self.rom_end]
|
||||
pos = 0
|
||||
self.files = []
|
||||
|
||||
@ -65,11 +65,11 @@ class N64SegPm_icons(N64Segment):
|
||||
|
||||
IconList = ET.Element("Icons")
|
||||
|
||||
for (_, icon) in enumerate(self.icons):
|
||||
for _, icon in enumerate(self.icons):
|
||||
# read yaml entry
|
||||
fmt = icon[0]
|
||||
name = icon[1]
|
||||
w = int(icon[2])
|
||||
w = int(icon[2])
|
||||
h = int(icon[3])
|
||||
|
||||
if fmt == "solo" or fmt == "pair":
|
||||
|
@ -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,20 +280,24 @@ CHARSET = {
|
||||
0x21: {None: lambda d: (f"[Option {d[0]}]", 1)},
|
||||
0x22: "[SavePos]",
|
||||
0x23: "[RestorePos]",
|
||||
0x24: {0xFF: {0x05: {
|
||||
0x10: {0x98: {0xFF: {0x25: "[A]"}}},
|
||||
0x11: {0x99: {0xFF: {0x25: "[B]"}}},
|
||||
0x12: {0xA1: {0xFF: {0x25: "[START]"}}},
|
||||
0x13: {
|
||||
0x9D: {0xFF: {0x25: "[C-UP]"}},
|
||||
0x9E: {0xFF: {0x25: "[C-DOWN]"}},
|
||||
0x9F: {0xFF: {0x25: "[C-LEFT]"}},
|
||||
0xA0: {0xFF: {0x25: "[C-RIGHT]"}},
|
||||
},
|
||||
0x14: {0x9C: {0xFF: {0x25: "[Z]"}}},
|
||||
}}},
|
||||
#0x24: "[SaveColor]",
|
||||
#0x25: "[RestoreColor]",
|
||||
0x24: {
|
||||
0xFF: {
|
||||
0x05: {
|
||||
0x10: {0x98: {0xFF: {0x25: "[A]"}}},
|
||||
0x11: {0x99: {0xFF: {0x25: "[B]"}}},
|
||||
0x12: {0xA1: {0xFF: {0x25: "[START]"}}},
|
||||
0x13: {
|
||||
0x9D: {0xFF: {0x25: "[C-UP]"}},
|
||||
0x9E: {0xFF: {0x25: "[C-DOWN]"}},
|
||||
0x9F: {0xFF: {0x25: "[C-LEFT]"}},
|
||||
0xA0: {0xFF: {0x25: "[C-RIGHT]"}},
|
||||
},
|
||||
0x14: {0x9C: {0xFF: {0x25: "[Z]"}}},
|
||||
}
|
||||
}
|
||||
},
|
||||
# 0x24: "[SaveColor]",
|
||||
# 0x25: "[RestoreColor]",
|
||||
0x26: {
|
||||
0x00: "[Shake]",
|
||||
0x01: "[Wave]",
|
||||
@ -307,7 +326,12 @@ CHARSET = {
|
||||
0x28: {None: lambda d: (f"[Var {d[0]}]", 1)},
|
||||
0x29: {None: lambda d: (f"[CenterX {d[0]}]", 1)},
|
||||
0x2B: "[EnableCDownNext]",
|
||||
0x2C: {None: lambda d: (f"[CustomVoice soundIDs=0x{d[0]:02X}{d[1]:02X}{d[2]:02X}{d[3]:02X},{d[4]:02X}{d[5]:02X}{d[6]:02X}{d[7]:02X}]", 8)},
|
||||
0x2C: {
|
||||
None: lambda d: (
|
||||
f"[CustomVoice soundIDs=0x{d[0]:02X}{d[1]:02X}{d[2]:02X}{d[3]:02X},{d[4]:02X}{d[5]:02X}{d[6]:02X}{d[7]:02X}]",
|
||||
8,
|
||||
)
|
||||
},
|
||||
0x2E: {None: lambda d: (f"[Volume {d[0]}]", 1)},
|
||||
0x2F: {
|
||||
0: "[Voice normal]\n",
|
||||
@ -315,7 +339,7 @@ CHARSET = {
|
||||
2: "[Voice star]\n",
|
||||
None: lambda d: (f"[Voice {d[0]}]\n", 1),
|
||||
},
|
||||
#None: lambda d: (f"[func_{d[0]:02X}]", 1),
|
||||
# None: lambda d: (f"[func_{d[0]:02X}]", 1),
|
||||
},
|
||||
None: lambda d: (f"[Raw 0x{d[0]:02X}]", 1),
|
||||
}
|
||||
@ -366,6 +390,7 @@ CHARSET_CREDITS = {
|
||||
0xF7: " ",
|
||||
}
|
||||
|
||||
|
||||
class N64SegPm_msg(N64Segment):
|
||||
def __init__(
|
||||
self,
|
||||
@ -393,12 +418,12 @@ class N64SegPm_msg(N64Segment):
|
||||
self.msg_names = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
||||
|
||||
def split(self, rom_bytes):
|
||||
data = rom_bytes[self.rom_start: self.rom_end]
|
||||
data = rom_bytes[self.rom_start : self.rom_end]
|
||||
|
||||
section_offsets = []
|
||||
pos = 0
|
||||
while True:
|
||||
offset = int.from_bytes(data[pos:pos+4], byteorder="big")
|
||||
offset = int.from_bytes(data[pos : pos + 4], byteorder="big")
|
||||
|
||||
if offset == 0:
|
||||
break
|
||||
@ -417,7 +442,7 @@ class N64SegPm_msg(N64Segment):
|
||||
msg_offsets = []
|
||||
pos = section_offset
|
||||
while True:
|
||||
offset = int.from_bytes(data[pos:pos+4], byteorder="big")
|
||||
offset = int.from_bytes(data[pos : pos + 4], byteorder="big")
|
||||
|
||||
if offset == section_offset:
|
||||
break
|
||||
@ -425,7 +450,7 @@ class N64SegPm_msg(N64Segment):
|
||||
msg_offsets.append(offset)
|
||||
pos += 4
|
||||
|
||||
#self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})")
|
||||
# self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})")
|
||||
|
||||
path = msg_dir / Path(name + ".msg")
|
||||
|
||||
@ -449,7 +474,6 @@ class N64SegPm_msg(N64Segment):
|
||||
self.write_message_markup(data[msg_offset:])
|
||||
self.f.write("\n}\n")
|
||||
|
||||
|
||||
def get_linker_entries(self):
|
||||
from segtypes.linker_entry import LinkerEntry
|
||||
|
||||
@ -458,7 +482,6 @@ class N64SegPm_msg(N64Segment):
|
||||
|
||||
return [LinkerEntry(self, out_paths, base_path, ".data")]
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_default_name(addr):
|
||||
return "msg"
|
||||
|
@ -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):
|
||||
|
@ -101,22 +101,22 @@ LIST_END_BYTES = b"\xFF\xFF\xFF\xFF"
|
||||
|
||||
|
||||
def indent(elem, level=0):
|
||||
i = "\n" + level*" "
|
||||
i = "\n" + level * " "
|
||||
if len(elem):
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = i + " "
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
for elem in elem:
|
||||
indent(elem, level+1)
|
||||
indent(elem, level + 1)
|
||||
if not elem.tail or not elem.tail.strip():
|
||||
elem.tail = i
|
||||
else:
|
||||
if level and (not elem.tail or not elem.tail.strip()):
|
||||
elem.tail = i
|
||||
elem.tail = i
|
||||
|
||||
|
||||
def pretty_print_xml(tree : ET.ElementTree, path : Path):
|
||||
def pretty_print_xml(tree: ET.ElementTree, path: Path):
|
||||
root = tree.getroot()
|
||||
indent(root)
|
||||
xml_str = ET.tostring(root, encoding="unicode")
|
||||
@ -136,9 +136,7 @@ class RasterTableEntry:
|
||||
raster_bytes: bytes = field(default_factory=bytes)
|
||||
palette: Optional[bytes] = None
|
||||
|
||||
def write_png(
|
||||
self, raster_buffer: bytes, path: Path, palette: Optional[bytes] = None
|
||||
):
|
||||
def write_png(self, raster_buffer: bytes, path: Path, palette: Optional[bytes] = None):
|
||||
if self.height == 0 or self.width == 0:
|
||||
raise ValueError("Raster size has not been set")
|
||||
|
||||
@ -149,9 +147,7 @@ class RasterTableEntry:
|
||||
raise ValueError("Palette has not been set")
|
||||
|
||||
if self.raster_bytes is not None:
|
||||
self.raster_bytes = raster_buffer[
|
||||
self.offset : self.offset + (self.width * self.height // 2)
|
||||
]
|
||||
self.raster_bytes = raster_buffer[self.offset : self.offset + (self.width * self.height // 2)]
|
||||
|
||||
img = CI4(self.raster_bytes, self.width, self.height)
|
||||
img.set_palette(palette)
|
||||
@ -264,9 +260,7 @@ class PlayerSprite:
|
||||
)
|
||||
|
||||
|
||||
def extract_raster_table_entries(
|
||||
data: bytes, raster_sets: List[PlayerSpriteRasterSet]
|
||||
) -> Dict[int, RasterTableEntry]:
|
||||
def extract_raster_table_entries(data: bytes, raster_sets: List[PlayerSpriteRasterSet]) -> Dict[int, RasterTableEntry]:
|
||||
ret: Dict[int, RasterTableEntry] = {}
|
||||
current_section_pos = 0
|
||||
current_section = 0
|
||||
@ -297,9 +291,7 @@ def extract_raster_table_entries(
|
||||
return ret
|
||||
|
||||
|
||||
def extract_sprites(
|
||||
yay0_data: bytes, raster_sets: List[PlayerSpriteRasterSet]
|
||||
) -> List[PlayerSprite]:
|
||||
def extract_sprites(yay0_data: bytes, raster_sets: List[PlayerSpriteRasterSet]) -> List[PlayerSprite]:
|
||||
yay0_splits = []
|
||||
for i in range(14):
|
||||
yay0_splits.append(int.from_bytes(yay0_data[i * 4 : i * 4 + 4], "big"))
|
||||
@ -375,9 +367,7 @@ def write_player_xmls(
|
||||
raster_table_entry_dict: Dict[int, RasterTableEntry],
|
||||
raster_names: List[str],
|
||||
) -> None:
|
||||
def get_sprite_name_from_offset(
|
||||
offset: int, offsets: List[int], names: List[str]
|
||||
) -> str:
|
||||
def get_sprite_name_from_offset(offset: int, offsets: List[int], names: List[str]) -> str:
|
||||
return names[offsets.index(offset)]
|
||||
|
||||
sprite_idx = 0
|
||||
@ -426,9 +416,7 @@ def write_player_xmls(
|
||||
back_raster = cur_sprite_back.rasters[i]
|
||||
|
||||
if back_raster.is_special:
|
||||
raster_attributes[
|
||||
"special"
|
||||
] = f"{back_raster.width & 0xFF:X},{back_raster.height & 0xFF:X}"
|
||||
raster_attributes["special"] = f"{back_raster.width & 0xFF:X},{back_raster.height & 0xFF:X}"
|
||||
else:
|
||||
back_name_offset = raster_sets[sprite_idx + 1].raster_offsets[i]
|
||||
raster_attributes[
|
||||
@ -483,9 +471,7 @@ def write_player_xmls(
|
||||
)
|
||||
|
||||
for anim in comp.animations:
|
||||
ET.SubElement(
|
||||
Component, anim.__class__.__name__, anim.get_attributes()
|
||||
)
|
||||
ET.SubElement(Component, anim.__class__.__name__, anim.get_attributes())
|
||||
|
||||
xml = ET.ElementTree(SpriteSheet)
|
||||
pretty_print_xml(xml, out_path / f"{cur_sprite_name}.xml")
|
||||
@ -547,12 +533,8 @@ def write_player_palettes(
|
||||
if pal_name not in dumped_palettes:
|
||||
offset = PLAYER_PAL_TO_RASTER[pal_name]
|
||||
if pal_name not in PLAYER_PAL_TO_RASTER:
|
||||
print(
|
||||
f"WARNING: Palette {pal_name} has no specified raster, not dumping!"
|
||||
)
|
||||
raster_table_entry_dict[offset].write_png(
|
||||
raster_data, path / (pal_name + ".png"), palette
|
||||
)
|
||||
print(f"WARNING: Palette {pal_name} has no specified raster, not dumping!")
|
||||
raster_table_entry_dict[offset].write_png(raster_data, path / (pal_name + ".png"), palette)
|
||||
|
||||
|
||||
###########
|
||||
@ -606,12 +588,8 @@ class NpcSprite:
|
||||
|
||||
@staticmethod
|
||||
def from_bytes(data: bytearray):
|
||||
image_offsets = read_offset_list(
|
||||
data[int.from_bytes(data[0:4], byteorder="big") :]
|
||||
)
|
||||
palette_offsets = read_offset_list(
|
||||
data[int.from_bytes(data[4:8], byteorder="big") :]
|
||||
)
|
||||
image_offsets = read_offset_list(data[int.from_bytes(data[0:4], byteorder="big") :])
|
||||
palette_offsets = read_offset_list(data[int.from_bytes(data[4:8], byteorder="big") :])
|
||||
max_components = int.from_bytes(data[8:0xC], byteorder="big")
|
||||
num_variations = int.from_bytes(data[0xC:0x10], byteorder="big")
|
||||
animation_offsets = read_offset_list(data[0x10:])
|
||||
@ -683,11 +661,7 @@ class NpcSprite:
|
||||
)
|
||||
|
||||
for i, palette in enumerate(self.palettes):
|
||||
name = (
|
||||
self.palette_names[i]
|
||||
if (self.palette_names and i < len(self.palette_names))
|
||||
else f"Pal{i:02X}"
|
||||
)
|
||||
name = self.palette_names[i] if (self.palette_names and i < len(self.palette_names)) else f"Pal{i:02X}"
|
||||
|
||||
if i in palette_to_raster:
|
||||
img = palette_to_raster[i][0]
|
||||
@ -710,9 +684,7 @@ class NpcSprite:
|
||||
AnimationList,
|
||||
"Animation",
|
||||
{
|
||||
"name": self.animation_names[i]
|
||||
if self.animation_names
|
||||
else f"Anim{i:02X}",
|
||||
"name": self.animation_names[i] if self.animation_names else f"Anim{i:02X}",
|
||||
},
|
||||
)
|
||||
|
||||
@ -727,9 +699,7 @@ class NpcSprite:
|
||||
)
|
||||
|
||||
for anim in comp.animations:
|
||||
ET.SubElement(
|
||||
Component, anim.__class__.__name__, anim.get_attributes()
|
||||
)
|
||||
ET.SubElement(Component, anim.__class__.__name__, anim.get_attributes())
|
||||
|
||||
xml = ET.ElementTree(SpriteSheet)
|
||||
pretty_print_xml(xml, path / "SpriteSheet.xml")
|
||||
@ -739,9 +709,7 @@ class N64SegPm_sprites(N64Segment):
|
||||
DEFAULT_NPC_SPRITE_NAMES = [f"{i:02X}" for i in range(0xEA)]
|
||||
|
||||
def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml) -> None:
|
||||
super().__init__(
|
||||
rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml
|
||||
)
|
||||
super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml)
|
||||
|
||||
with (Path(__file__).parent / f"npc_sprite_names.yaml").open("r") as f:
|
||||
self.npc_cfg = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
||||
@ -752,9 +720,7 @@ class N64SegPm_sprites(N64Segment):
|
||||
def out_path(self):
|
||||
return options.opts.asset_path / "sprite" / "sprites"
|
||||
|
||||
def split_player(
|
||||
self, build_date: str, player_raster_data: bytes, player_yay0_data: bytes
|
||||
) -> None:
|
||||
def split_player(self, build_date: str, player_raster_data: bytes, player_yay0_data: bytes) -> None:
|
||||
player_sprite_cfg = self.player_cfg["player_sprites"]
|
||||
player_raster_names: List[str] = self.player_cfg["player_rasters"]
|
||||
|
||||
@ -854,9 +820,7 @@ class N64SegPm_sprites(N64Segment):
|
||||
npc_yay0_offset = int.from_bytes(sprite_in_bytes[0x18:0x1C], "big") + 0x10
|
||||
sprite_end_offset = int.from_bytes(sprite_in_bytes[0x1C:0x20], "big") + 0x10
|
||||
|
||||
player_raster_data: bytes = sprite_in_bytes[
|
||||
player_raster_offset:player_yay0_offset
|
||||
]
|
||||
player_raster_data: bytes = sprite_in_bytes[player_raster_offset:player_yay0_offset]
|
||||
player_yay0_data: bytes = sprite_in_bytes[player_yay0_offset:npc_yay0_offset]
|
||||
npc_yay0_data: bytes = sprite_in_bytes[npc_yay0_offset:sprite_end_offset]
|
||||
|
||||
@ -869,14 +833,9 @@ class N64SegPm_sprites(N64Segment):
|
||||
src_paths = [options.opts.asset_path / "sprite"]
|
||||
|
||||
# for NPC
|
||||
src_paths += [
|
||||
options.opts.asset_path / "sprite" / "npc" / sprite_name
|
||||
for sprite_name in self.npc_cfg
|
||||
]
|
||||
src_paths += [options.opts.asset_path / "sprite" / "npc" / sprite_name for sprite_name in self.npc_cfg]
|
||||
|
||||
return [
|
||||
LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section())
|
||||
]
|
||||
return [LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section())]
|
||||
|
||||
def cache(self):
|
||||
return (self.yaml, self.rom_end, self.player_cfg, self.npc_cfg)
|
||||
|
@ -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
|
||||
v = arg - 2**32 # convert to s32
|
||||
if v > -250000000:
|
||||
if v <= -220000000: return str((v + 230000000) / 1024)
|
||||
elif v <= -200000000: return f"ArrayFlag({v + 210000000})"
|
||||
elif v <= -180000000: return f"ArrayVar({v + 190000000})"
|
||||
if v <= -220000000:
|
||||
return str((v + 230000000) / 1024)
|
||||
elif v <= -200000000:
|
||||
return f"ArrayFlag({v + 210000000})"
|
||||
elif v <= -180000000:
|
||||
return f"ArrayVar({v + 190000000})"
|
||||
elif v <= -160000000:
|
||||
if v + 170000000 == 0:
|
||||
return "GB_StoryProgress"
|
||||
@ -37,13 +41,20 @@ def get_flag_name(arg):
|
||||
return "GB_WorldLocation"
|
||||
else:
|
||||
return f"GameByte({v + 170000000})"
|
||||
elif v <= -140000000: return f"AreaByte({v + 150000000})"
|
||||
elif v <= -120000000: return f"GameFlag({v + 130000000})"
|
||||
elif v <= -100000000: return f"AreaFlag({v + 110000000})"
|
||||
elif v <= -80000000: return f"MapFlag({v + 90000000})"
|
||||
elif v <= -60000000: return f"LocalFlag({v + 70000000})"
|
||||
elif v <= -40000000: return f"MapVar({v + 50000000})"
|
||||
elif v <= -20000000: return f"LocalVar({v + 30000000})"
|
||||
elif v <= -140000000:
|
||||
return f"AreaByte({v + 150000000})"
|
||||
elif v <= -120000000:
|
||||
return f"GameFlag({v + 130000000})"
|
||||
elif v <= -100000000:
|
||||
return f"AreaFlag({v + 110000000})"
|
||||
elif v <= -80000000:
|
||||
return f"MapFlag({v + 90000000})"
|
||||
elif v <= -60000000:
|
||||
return f"LocalFlag({v + 70000000})"
|
||||
elif v <= -40000000:
|
||||
return f"MapVar({v + 50000000})"
|
||||
elif v <= -20000000:
|
||||
return f"LocalVar({v + 30000000})"
|
||||
|
||||
if arg == 0xFFFFFFFF:
|
||||
return "-1"
|
||||
@ -54,6 +65,7 @@ def get_flag_name(arg):
|
||||
else:
|
||||
return f"{arg}"
|
||||
|
||||
|
||||
def get_function_list(area_name, map_name, rom_offset):
|
||||
map_file = (Path(__file__).parent.parent / "ver" / "current" / "build" / "papermario.map").read_text().splitlines()
|
||||
i = 0
|
||||
@ -70,7 +82,7 @@ def get_function_list(area_name, map_name, rom_offset):
|
||||
vram = int(vram, 16)
|
||||
func = func.replace(f"{map_name}_", "")
|
||||
if func.count("_") == 2:
|
||||
func = func.rsplit("_",1)[0]
|
||||
func = func.rsplit("_", 1)[0]
|
||||
functions[vram] = func
|
||||
i += 1
|
||||
if firstFind:
|
||||
@ -79,6 +91,7 @@ def get_function_list(area_name, map_name, rom_offset):
|
||||
|
||||
return functions
|
||||
|
||||
|
||||
def get_include_list(area_name, map_name):
|
||||
include_path = Path(__file__).parent.parent / "src" / "world" / "common"
|
||||
includes = set()
|
||||
@ -87,16 +100,18 @@ def get_include_list(area_name, map_name):
|
||||
with open(file, "r", encoding="utf8") as f:
|
||||
for line in f:
|
||||
if (line.startswith("void N(") or line.startswith("ApiStatus N(")) and "{" in line:
|
||||
func_name = line.split("N(",1)[1].split(")",1)[0]
|
||||
func_name = line.split("N(", 1)[1].split(")", 1)[0]
|
||||
includes.add(func_name)
|
||||
return includes
|
||||
|
||||
|
||||
def read_enum(num: int, constants_name: str) -> str:
|
||||
if num in disasm_script.CONSTANTS[constants_name]:
|
||||
return disasm_script.CONSTANTS[constants_name][num]
|
||||
else:
|
||||
return num
|
||||
|
||||
|
||||
def read_flags(flags: int, constants_name: str) -> str:
|
||||
enabled = []
|
||||
for x in range(32):
|
||||
@ -115,6 +130,7 @@ def read_flags(flags: int, constants_name: str) -> str:
|
||||
|
||||
return " | ".join(enabled)
|
||||
|
||||
|
||||
def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str:
|
||||
if addr == 0:
|
||||
return "NULL"
|
||||
@ -126,6 +142,7 @@ def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str:
|
||||
else:
|
||||
return f"(void*) 0x{addr:08X}"
|
||||
|
||||
|
||||
def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace=None):
|
||||
global INCLUDES_NEEDED, INCLUDED
|
||||
out = ""
|
||||
@ -139,7 +156,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
|
||||
def transform_symbol_name(symbol):
|
||||
if namespace and symbol.startswith(namespace + "_"):
|
||||
return "N(" + symbol[len(namespace)+1:] + ")"
|
||||
return "N(" + symbol[len(namespace) + 1 :] + ")"
|
||||
return symbol
|
||||
|
||||
while len(midx) > 0:
|
||||
@ -148,7 +165,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
name = struct["name"]
|
||||
print(name, file=sys.stderr)
|
||||
|
||||
#INCLUDED["functions"].add(name)
|
||||
# INCLUDED["functions"].add(name)
|
||||
|
||||
if comments:
|
||||
out += f"// {romstart+struct['start']:X}-{romstart+struct['end']:X} (VRAM: {struct['vaddr']:X})\n"
|
||||
@ -165,18 +182,23 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
main_script_name = name
|
||||
|
||||
# For PlayMusic script if using a separate header file
|
||||
#if afterHeader:
|
||||
# if afterHeader:
|
||||
# INCLUDES_NEEDED["forward"].append(f"EvtScript " + name + ";")
|
||||
# afterHeader = False
|
||||
disasm_script.LOCAL_WORDS = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
|
||||
disasm_script.LOCAL_WORDS = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
script_text = disasm_script.ScriptDisassembler(
|
||||
bytes, name, symbol_map, romstart, INCLUDES_NEEDED, INCLUDED,
|
||||
bytes,
|
||||
name,
|
||||
symbol_map,
|
||||
romstart,
|
||||
INCLUDES_NEEDED,
|
||||
INCLUDED,
|
||||
transform_symbol_name=transform_symbol_name,
|
||||
use_script_lib=False,
|
||||
).disassemble()
|
||||
|
||||
if "EVS_ShakeTree" in name or "EVS_SearchBush" in name:
|
||||
symbol_map[struct["vaddr"]][0][1] = name.split("_",1)[0] + ")"
|
||||
symbol_map[struct["vaddr"]][0][1] = name.split("_", 1)[0] + ")"
|
||||
if not treePrint:
|
||||
out += f"=======================================\n"
|
||||
out += f"==========BELOW foliage.inc.c==========\n"
|
||||
@ -188,9 +210,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
script_text = script_text.splitlines()
|
||||
walkDistance = exitIdx = map_ = entryIdx = ""
|
||||
if "UseExitHeading" in script_text[2]:
|
||||
walkDistance, exitIdx = script_text[2].split("(",1)[1].split(")",1)[0].split(",")
|
||||
walkDistance, exitIdx = script_text[2].split("(", 1)[1].split(")", 1)[0].split(",")
|
||||
if "GotoMap" in script_text[4]:
|
||||
map_, entryIdx = script_text[4].split("(",1)[1].split(")",1)[0].split(",")
|
||||
map_, entryIdx = script_text[4].split("(", 1)[1].split(")", 1)[0].split(",")
|
||||
if walkDistance and exitIdx and map_ and entryIdx:
|
||||
out += f"EvtScript {name} = EVT_EXIT_WALK({walkDistance}, {exitIdx}, {map_}, {entryIdx});\n"
|
||||
else:
|
||||
@ -210,8 +232,11 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
z = []
|
||||
w = []
|
||||
for _ in range(entry_count):
|
||||
a,b,c,d = unpack_from(">ffff", entry_list, pos)
|
||||
x.append(f"{a:.01f}"); y.append(f"{b:.01f}"); z.append(f"{c:.01f}"); w.append(f"{d:.01f}")
|
||||
a, b, c, d = unpack_from(">ffff", entry_list, pos)
|
||||
x.append(f"{a:.01f}")
|
||||
y.append(f"{b:.01f}")
|
||||
z.append(f"{c:.01f}")
|
||||
w.append(f"{d:.01f}")
|
||||
pos += 16
|
||||
|
||||
x_size = max([len(a) for a in x])
|
||||
@ -219,7 +244,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
z_size = max([len(a) for a in z])
|
||||
w_size = max([len(a) for a in w])
|
||||
|
||||
for a,b,c,d in zip(x,y,z,w):
|
||||
for a, b, c, d in zip(x, y, z, w):
|
||||
out += f"\n {{ {a:>{x_size}}f, {b:>{y_size}}f, {c:>{z_size}}f, {d:>{w_size}}f }},"
|
||||
|
||||
out += f"\n}};\n"
|
||||
@ -233,16 +258,29 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
var_names = ["unk_00", "unk_24"]
|
||||
data = unpack_from(">4B", npcSettings, i)
|
||||
if not sum(data) == 0:
|
||||
tmp_out += INDENT + f".{var_names[0] if i == 0 else var_names[1]} = {{ " + ", ".join(f"0x{x:02X}" for x in data) + f" }},\n"
|
||||
tmp_out += (
|
||||
INDENT
|
||||
+ f".{var_names[0] if i == 0 else var_names[1]} = {{ "
|
||||
+ ", ".join(f"0x{x:02X}" for x in data)
|
||||
+ f" }},\n"
|
||||
)
|
||||
elif i == 0x4 or i == 0x28:
|
||||
var_names = ["height", "radius", "level", "unk_2A"]
|
||||
for x,var in enumerate(unpack_from(">2h", npcSettings, i)):
|
||||
var_name = var_names[x if i == 0x4 else x+2]
|
||||
for x, var in enumerate(unpack_from(">2h", npcSettings, i)):
|
||||
var_name = var_names[x if i == 0x4 else x + 2]
|
||||
if not var == 0:
|
||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||
elif i == 0x8:
|
||||
var_names = ["otherAI", "onInteract", "ai", "onHit", "aux", "onDefeat", "flags"]
|
||||
for x,var in enumerate(unpack_from(f">7I", npcSettings, i)):
|
||||
var_names = [
|
||||
"otherAI",
|
||||
"onInteract",
|
||||
"ai",
|
||||
"onHit",
|
||||
"aux",
|
||||
"onDefeat",
|
||||
"flags",
|
||||
]
|
||||
for x, var in enumerate(unpack_from(f">7I", npcSettings, i)):
|
||||
var_name = var_names[x]
|
||||
if not var == 0:
|
||||
if var == 0x80077F70:
|
||||
@ -264,8 +302,20 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
npcAISettings = bytes.read(struct["length"])
|
||||
|
||||
i = x = 0
|
||||
var_names = ["moveSpeed", "moveTime", "waitTime", "alertRadius", "unk_10", "unk_14",
|
||||
"chaseSpeed", "unk_1C", "unk_20", "chaseRadius", "unk_28", "unk_2C"]
|
||||
var_names = [
|
||||
"moveSpeed",
|
||||
"moveTime",
|
||||
"waitTime",
|
||||
"alertRadius",
|
||||
"unk_10",
|
||||
"unk_14",
|
||||
"chaseSpeed",
|
||||
"unk_1C",
|
||||
"unk_20",
|
||||
"chaseRadius",
|
||||
"unk_28",
|
||||
"unk_2C",
|
||||
]
|
||||
while i < struct["length"]:
|
||||
var_f, var_i1, var_i2 = unpack_from(f">fii", npcAISettings, i)
|
||||
if not var_f == 0:
|
||||
@ -274,7 +324,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
# account for X32
|
||||
if var_names[x + 1] in ["unk_10", "unk_1C", "unk_28"]:
|
||||
if var_i1 < -100000 or var_i1 > 100000:
|
||||
tmp_out += INDENT + f".{var_names[x + 1]} = {{ .f = {unpack_from('>f', npcAISettings, i+4)[0]:.01f}f }},\n"
|
||||
tmp_out += (
|
||||
INDENT
|
||||
+ f".{var_names[x + 1]} = {{ .f = {unpack_from('>f', npcAISettings, i+4)[0]:.01f}f }},\n"
|
||||
)
|
||||
else:
|
||||
tmp_out += INDENT + f".{var_names[x + 1]} = {{ .s = {var_i1} }},\n"
|
||||
else:
|
||||
@ -289,32 +342,56 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
elif struct["type"] == "NpcGroup":
|
||||
staticNpc = bytes.read(struct["length"])
|
||||
curr_base = 0
|
||||
numNpcs = struct['length'] // 0x1F0
|
||||
numNpcs = struct["length"] // 0x1F0
|
||||
tmp_out = f"NpcData {name}" + ("[]" if numNpcs > 1 else "") + f" = {{\n"
|
||||
|
||||
for z in range(numNpcs):
|
||||
i = 0
|
||||
var_names = ["id", "settings", "pos", "flags",
|
||||
"init", "unk_1C", "yaw", "dropFlags",
|
||||
"itemDropChance", "itemDrops", "heartDrops", "flowerDrops",
|
||||
"minCoinBonus", "maxCoinBonus", "movement", "animations",
|
||||
"unk_1E0", "extraAnimations", "tattle"]
|
||||
var_names = [
|
||||
"id",
|
||||
"settings",
|
||||
"pos",
|
||||
"flags",
|
||||
"init",
|
||||
"unk_1C",
|
||||
"yaw",
|
||||
"dropFlags",
|
||||
"itemDropChance",
|
||||
"itemDrops",
|
||||
"heartDrops",
|
||||
"flowerDrops",
|
||||
"minCoinBonus",
|
||||
"maxCoinBonus",
|
||||
"movement",
|
||||
"animations",
|
||||
"unk_1E0",
|
||||
"extraAnimations",
|
||||
"tattle",
|
||||
]
|
||||
|
||||
if numNpcs > 1:
|
||||
tmp_out += INDENT + f"{{\n"
|
||||
INDENT = INDENT*2
|
||||
INDENT = INDENT * 2
|
||||
|
||||
while i < 0x1F0:
|
||||
if i == 0x0 or i == 0x24:
|
||||
var_name = var_names[0] if i == 0x0 else var_names[6]
|
||||
var = unpack_from(f">i", staticNpc, curr_base+i)[0]
|
||||
var = unpack_from(f">i", staticNpc, curr_base + i)[0]
|
||||
if var_name == "id":
|
||||
tmp_out += INDENT + f".{var_name} = {disasm_script.CONSTANTS['MAP_NPCS'][var]},\n"
|
||||
else:
|
||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||
elif i == 0x4 or i == 0x14 or i == 0x18 or i == 0x1E8:
|
||||
var_name = var_names[1] if i == 0x4 else var_names[3] if i == 0x14 else var_names[4] if i == 0x18 else var_names[17]
|
||||
addr = unpack_from(f">I", staticNpc, curr_base+i)[0]
|
||||
var_name = (
|
||||
var_names[1]
|
||||
if i == 0x4
|
||||
else var_names[3]
|
||||
if i == 0x14
|
||||
else var_names[4]
|
||||
if i == 0x18
|
||||
else var_names[17]
|
||||
)
|
||||
addr = unpack_from(f">I", staticNpc, curr_base + i)[0]
|
||||
if not addr == 0:
|
||||
if var_name != "flags" and addr in symbol_map:
|
||||
if var_name == "extraAnimations":
|
||||
@ -338,17 +415,17 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
enabled.append(0)
|
||||
tmp_out += INDENT + f".{var_name} = " + " | ".join(enabled) + f",\n"
|
||||
elif i == 0x8:
|
||||
pos = unpack_from(f">fff", staticNpc, curr_base+i)
|
||||
pos = unpack_from(f">fff", staticNpc, curr_base + i)
|
||||
if not sum(pos) == 0:
|
||||
tmp_out += INDENT + f".pos = {{ {pos[0]:.01f}f, {pos[1]:.01f}f, {pos[2]:.01f}f }},\n"
|
||||
elif i == 0x1C or i == 0x1E0:
|
||||
var_name = var_names[5] if i == 0x1C else var_names[16]
|
||||
data = unpack_from(f">8B", staticNpc, curr_base+i)
|
||||
data = unpack_from(f">8B", staticNpc, curr_base + i)
|
||||
if not sum(data) == 0:
|
||||
tmp_out += INDENT + f".{var_name} = {{ " + ", ".join(f"{x:02X}" for x in data) + f"}},\n"
|
||||
elif i == 0x28 or i == 0x29:
|
||||
var_name = var_names[7] if i == 0x28 else var_names[8]
|
||||
var = unpack_from(f">b", staticNpc, curr_base+i)[0]
|
||||
var = unpack_from(f">b", staticNpc, curr_base + i)[0]
|
||||
if not var == 0:
|
||||
if var_name == "dropFlags":
|
||||
tmp_out += INDENT + f".{var_name} = 0x{abs(var):02X},\n"
|
||||
@ -358,10 +435,14 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
var_name = var_names[9]
|
||||
tmp_tmp = ""
|
||||
for x in range(8):
|
||||
item, weight, unk_08 = unpack_from(f">3h", staticNpc, curr_base+i)
|
||||
item, weight, unk_08 = unpack_from(f">3h", staticNpc, curr_base + i)
|
||||
if not (item == 0 and weight == 0 and unk_08 == 0):
|
||||
item = disasm_script.CONSTANTS["ItemIDs"][item] if item in disasm_script.CONSTANTS["ItemIDs"] else f"{item}"
|
||||
tmp_tmp += INDENT*2 + f"{{ {item}, {weight}, {unk_08} }},\n"
|
||||
item = (
|
||||
disasm_script.CONSTANTS["ItemIDs"][item]
|
||||
if item in disasm_script.CONSTANTS["ItemIDs"]
|
||||
else f"{item}"
|
||||
)
|
||||
tmp_tmp += INDENT * 2 + f"{{ {item}, {weight}, {unk_08} }},\n"
|
||||
i += 0x6
|
||||
|
||||
if tmp_tmp:
|
||||
@ -374,7 +455,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
var_name = var_names[10] if i == 0x5A else var_names[11]
|
||||
drops = []
|
||||
for x in range(8):
|
||||
cutoff, generalChance, attempts, chancePerAttempt = unpack_from(f">4h", staticNpc, curr_base+i)
|
||||
(
|
||||
cutoff,
|
||||
generalChance,
|
||||
attempts,
|
||||
chancePerAttempt,
|
||||
) = unpack_from(f">4h", staticNpc, curr_base + i)
|
||||
if not (cutoff == 0 and generalChance == 0 and attempts == 0 and chancePerAttempt == 0):
|
||||
drops.append([cutoff, generalChance, attempts, chancePerAttempt])
|
||||
i += 0x8
|
||||
@ -388,10 +474,16 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
tmp_out += f"GENEROUS_HEART_DROPS({drops[0][2]})"
|
||||
elif round(drops[0][1] / 327.67, 2) == 80 and round(drops[0][3] / 327.67, 2) == 60:
|
||||
tmp_out += f"GENEROUS_WHEN_LOW_HEART_DROPS({drops[0][2]})"
|
||||
elif round(drops[0][0] / 327.67, 2) == 100 and round(drops[0][1] / 327.67, 2) == 0 and round(drops[0][2] / 327.67, 2) == 0:
|
||||
elif (
|
||||
round(drops[0][0] / 327.67, 2) == 100
|
||||
and round(drops[0][1] / 327.67, 2) == 0
|
||||
and round(drops[0][2] / 327.67, 2) == 0
|
||||
):
|
||||
tmp_out += f"NO_DROPS"
|
||||
else:
|
||||
print(f"Unknown heart drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}")
|
||||
print(
|
||||
f"Unknown heart drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}"
|
||||
)
|
||||
exit()
|
||||
else:
|
||||
if round(drops[0][1] / 327.67, 2) == 50 and round(drops[0][3] / 327.67, 2) == 40:
|
||||
@ -400,56 +492,70 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
tmp_out += f"GENEROUS_WHEN_LOW_FLOWER_DROPS({drops[0][2]})"
|
||||
elif round(drops[0][1] / 327.67, 2) == 40 and round(drops[0][3] / 327.67, 2) == 40:
|
||||
tmp_out += f"REDUCED_FLOWER_DROPS({drops[0][2]})"
|
||||
elif round(drops[0][0] / 327.67, 2) == 100 and round(drops[0][1] / 327.67, 2) == 0 and round(drops[0][2] / 327.67, 2) == 0:
|
||||
elif (
|
||||
round(drops[0][0] / 327.67, 2) == 100
|
||||
and round(drops[0][1] / 327.67, 2) == 0
|
||||
and round(drops[0][2] / 327.67, 2) == 0
|
||||
):
|
||||
tmp_out += f"NO_DROPS"
|
||||
else:
|
||||
print(f"Unknown flower drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}")
|
||||
print(
|
||||
f"Unknown flower drop macro, values were {round(drops[0][1] / 327.67, 2)} and {round(drops[0][3] / 327.67, 2)}"
|
||||
)
|
||||
exit()
|
||||
|
||||
tmp_out += f",\n"
|
||||
elif i == 0xDA or i == 0xDC:
|
||||
var_name = var_names[12] if i == 0xDA else var_names[13]
|
||||
var = unpack_from(">h", staticNpc, curr_base+i)[0]
|
||||
var = unpack_from(">h", staticNpc, curr_base + i)[0]
|
||||
if not var == 0:
|
||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||
elif i == 0xE0:
|
||||
data = unpack_from(">48i", staticNpc, curr_base+i)
|
||||
data = unpack_from(">48i", staticNpc, curr_base + i)
|
||||
if not sum(data) == 0:
|
||||
end_pos = len(data)
|
||||
for x,datum in enumerate(data):
|
||||
for x, datum in enumerate(data):
|
||||
if not datum == 0:
|
||||
end_pos = x
|
||||
tmp_out += INDENT + f".territory = { .temp = {{ " + ", ".join(f"{x}" for x in data[:end_pos+1]) + f" }}},\n"
|
||||
tmp_out += (
|
||||
INDENT
|
||||
+ f".territory = {{ .temp = {{ "
|
||||
+ ", ".join(f"{x}" for x in data[: end_pos + 1])
|
||||
+ f" }}}},\n"
|
||||
)
|
||||
elif i == 0x1A0:
|
||||
tmp_out += INDENT + f".{var_names[15]} = {{\n"
|
||||
for x in range(16):
|
||||
anim = unpack_from(">I", staticNpc, curr_base+i)[0]
|
||||
anim = unpack_from(">I", staticNpc, curr_base + i)[0]
|
||||
if not anim == 0:
|
||||
sprite_id = (anim & 0x00FF0000) >> 16
|
||||
sprite_id = (anim & 0x00FF0000) >> 16
|
||||
palette_id = (anim & 0x0000FF00) >> 8
|
||||
anim_id = (anim & 0x000000FF) >> 0
|
||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||
anim_id = (anim & 0x000000FF) >> 0
|
||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||
palette = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
||||
anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||
anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||
if numNpcs > 1:
|
||||
tmp_out += INDENT + " " + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||
else:
|
||||
tmp_out += INDENT*2 + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||
tmp_out += INDENT * 2 + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||
INCLUDES_NEEDED["sprites"].add(sprite)
|
||||
i += 4
|
||||
tmp_out += INDENT + f"}},\n"
|
||||
i -= 1
|
||||
elif i == 0x1EC:
|
||||
var = unpack_from(">I", staticNpc, curr_base+i)[0]
|
||||
var = unpack_from(">I", staticNpc, curr_base + i)[0]
|
||||
if not var == 0:
|
||||
tmp_out += INDENT + f".{var_names[18]} = MESSAGE_ID(0x{(var & 0xFF0000) >> 16:02X}, 0x{var & 0xFFFF:04X}),\n"
|
||||
tmp_out += (
|
||||
INDENT
|
||||
+ f".{var_names[18]} = MESSAGE_ID(0x{(var & 0xFF0000) >> 16:02X}, 0x{var & 0xFFFF:04X}),\n"
|
||||
)
|
||||
|
||||
i += 1
|
||||
|
||||
if numNpcs > 1:
|
||||
INDENT = INDENT[:len(INDENT)//2]
|
||||
INDENT = INDENT[: len(INDENT) // 2]
|
||||
tmp_out += INDENT + f"}},\n"
|
||||
if z+1 == numNpcs:
|
||||
if z + 1 == numNpcs:
|
||||
tmp_out += "};\n"
|
||||
|
||||
curr_base += 0x1F0
|
||||
@ -464,12 +570,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
if anim == 0xFFFFFFFF:
|
||||
tmp_out += INDENT + f"ANIM_LIST_END,\n"
|
||||
elif not anim == 0:
|
||||
sprite_id = (anim & 0x00FF0000) >> 16
|
||||
sprite_id = (anim & 0x00FF0000) >> 16
|
||||
palette_id = (anim & 0x0000FF00) >> 8
|
||||
anim_id = (anim & 0x000000FF) >> 0
|
||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||
anim_id = (anim & 0x000000FF) >> 0
|
||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||
palette = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
||||
anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||
anim = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||
tmp_out += INDENT + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||
INCLUDES_NEEDED["sprites"].add(sprite)
|
||||
i += 4
|
||||
@ -487,7 +593,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
battle_b = (battle & 0x00FF0000) >> 16
|
||||
battle_c = (battle & 0x0000FF00) >> 8
|
||||
battle_d = (battle & 0x000000FF) >> 0
|
||||
tmp_out += INDENT + f"NPC_GROUP({symbol_map[npcs][0][1]}, BATTLE_ID({battle_a}, {battle_b}, {battle_c}, {battle_d})),\n"
|
||||
tmp_out += (
|
||||
INDENT
|
||||
+ f"NPC_GROUP({symbol_map[npcs][0][1]}, BATTLE_ID({battle_a}, {battle_b}, {battle_c}, {battle_d})),\n"
|
||||
)
|
||||
if symbol_map[npcs][0][1] not in INCLUDED["functions"]:
|
||||
INCLUDES_NEEDED["forward"].append(symbol_map[npcs][0][1])
|
||||
i += 0xC
|
||||
@ -502,7 +611,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
out += f" {disasm_script.CONSTANTS['ItemIDs'][item]},\n"
|
||||
out += f"}};\n"
|
||||
elif struct["type"] == "TreeDropList":
|
||||
new_name = "N(" + name.split('_',1)[1][:-1].lower() + "_Drops)"
|
||||
new_name = "N(" + name.split("_", 1)[1][:-1].lower() + "_Drops)"
|
||||
symbol_map[struct["vaddr"]][0][1] = new_name
|
||||
|
||||
out += f"FoliageDropList {new_name} = {{\n"
|
||||
@ -518,7 +627,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
pos = 4
|
||||
for _ in range(count):
|
||||
entry = list(unpack_from(">7I", data, pos))
|
||||
pos += 7*4
|
||||
pos += 7 * 4
|
||||
|
||||
entry[1] = entry[1] - 0x100000000 if entry[1] >= 0x80000000 else entry[1]
|
||||
entry[2] = entry[2] - 0x100000000 if entry[2] >= 0x80000000 else entry[2]
|
||||
@ -532,9 +641,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
out += f"{INDENT * 3}.pos = {{ {entry[1]}, {entry[2]}, {entry[3]} }},\n"
|
||||
if entry[4] != 0:
|
||||
out += f"{INDENT * 3}.spawnMode = 0x{entry[4]:X},\n"
|
||||
if flag1 != '0':
|
||||
if flag1 != "0":
|
||||
out += f"{INDENT * 3}.pickupFlag = {flag1},\n"
|
||||
if flag2 != '0':
|
||||
if flag2 != "0":
|
||||
out += f"{INDENT * 3}.spawnFlag = {flag2},\n"
|
||||
out += f"{INDENT * 2}}},\n"
|
||||
|
||||
@ -546,7 +655,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
elif struct["type"] == "TreeModelList" or struct["type"] == "TreeEffectVectors":
|
||||
isModelList = struct["type"] == "TreeModelList"
|
||||
|
||||
name_parts = name.split('_')
|
||||
name_parts = name.split("_")
|
||||
if isModelList:
|
||||
new_name = "N(" + name_parts[1].lower() + "_" + name_parts[2]
|
||||
else:
|
||||
@ -590,7 +699,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
entry[1] = entry[1] - 0x100000000 if entry[1] >= 0x80000000 else entry[1]
|
||||
entry[2] = entry[2] - 0x100000000 if entry[2] >= 0x80000000 else entry[2]
|
||||
|
||||
pos += 3*4
|
||||
pos += 3 * 4
|
||||
|
||||
out += f"{INDENT * 2}{{ {entry[0]}, {entry[1]}, {entry[2]} }},\n"
|
||||
|
||||
@ -600,10 +709,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
out += f"}};\n"
|
||||
|
||||
elif struct["type"] == "SearchBushEvent":
|
||||
new_name = "N(" + name.split('_',1)[1].lower()
|
||||
new_name = "N(" + name.split("_", 1)[1].lower()
|
||||
symbol_map[struct["vaddr"]][0][1] = new_name
|
||||
|
||||
num = int(new_name.split("bush",1)[1][:-1])
|
||||
num = int(new_name.split("bush", 1)[1][:-1])
|
||||
out += f"SearchBushConfig {new_name} = {{\n"
|
||||
|
||||
data = bytes.read(struct["length"])
|
||||
@ -621,10 +730,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
out += f"}};\n"
|
||||
|
||||
elif struct["type"] == "ShakeTreeEvent":
|
||||
new_name = "N(" + name.split('_',1)[1].lower()
|
||||
new_name = "N(" + name.split("_", 1)[1].lower()
|
||||
symbol_map[struct["vaddr"]][0][1] = new_name
|
||||
|
||||
num = int(new_name.split("tree",1)[1][:-1])
|
||||
num = int(new_name.split("tree", 1)[1][:-1])
|
||||
out += f"ShakeTreeConfig {new_name} = {{\n"
|
||||
|
||||
data = bytes.read(struct["length"])
|
||||
@ -656,20 +765,22 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
|
||||
bytes.read(0x10)
|
||||
|
||||
main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3))
|
||||
main, entry_list, entry_count = unpack(">IIi", bytes.read(4 * 3))
|
||||
out += f" .main = &N(main),\n"
|
||||
out += f" .entryList = &{entry_list_name},\n"
|
||||
out += f" .entryCount = ENTRY_COUNT({entry_list_name}),\n"
|
||||
|
||||
bytes.read(0x1C)
|
||||
|
||||
bg,tattle = unpack(">II", bytes.read(4 * 2))
|
||||
bg, tattle = unpack(">II", bytes.read(4 * 2))
|
||||
if bg == 0x80200000:
|
||||
out += f" .background = &gBackgroundImage,\n"
|
||||
elif bg != 0:
|
||||
raise Exception(f"unknown MapSettings background {bg:X}")
|
||||
#out += f" .tattle = 0x{tattle:X},\n"
|
||||
INCLUDES_NEEDED["tattle"].append(f"- [0x{(tattle & 0xFF0000) >> 16:02X}, 0x{tattle & 0xFFFF:04X}, {map_name}_tattle]")
|
||||
# out += f" .tattle = 0x{tattle:X},\n"
|
||||
INCLUDES_NEEDED["tattle"].append(
|
||||
f"- [0x{(tattle & 0xFF0000) >> 16:02X}, 0x{tattle & 0xFFFF:04X}, {map_name}_tattle]"
|
||||
)
|
||||
out += f" .tattle = {{ MSG_{map_name}_tattle }},\n"
|
||||
|
||||
out += f"}};\n"
|
||||
@ -678,7 +789,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
bytes.read(struct["length"])
|
||||
out += f"s32 {name}();\n"
|
||||
elif struct["type"] == "FloatTable":
|
||||
vram = int(name.split("_",1)[1][:-1], 16)
|
||||
vram = int(name.split("_", 1)[1][:-1], 16)
|
||||
name = f"N(D_{vram:X}_{(vram - 0x80240000) + romstart:X})"
|
||||
struct["name"] = name
|
||||
out += f"f32 {name}[] = {{"
|
||||
@ -695,10 +806,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
if len(data) > 0:
|
||||
out += f"Vec3f {name}[] = {{\n"
|
||||
out += f"\t"
|
||||
for i,pos in enumerate(range(0, len(data), 0xC)):
|
||||
for i, pos in enumerate(range(0, len(data), 0xC)):
|
||||
x, y, z = unpack_from(">fff", data, pos)
|
||||
out += f" {{ {x:.01f}, {y:.01f}, {z:.01f} }},"
|
||||
if (i+1) % 2 == 0:
|
||||
if (i + 1) % 2 == 0:
|
||||
out += f"\n\t"
|
||||
out += f"\n}};\n"
|
||||
|
||||
@ -721,7 +832,6 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
else:
|
||||
out += f".actor = {actor}, "
|
||||
|
||||
|
||||
if position in symbol_map:
|
||||
out += f".home = {{ .vec = &{symbol_map[position][0][1]} }}"
|
||||
|
||||
@ -829,11 +939,11 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
|
||||
element = disasm_script.CONSTANTS["Statuses"][element]
|
||||
|
||||
value = (anim & 0x00FFFFFF)
|
||||
value = anim & 0x00FFFFFF
|
||||
|
||||
if value in disasm_script.CONSTANTS["NPC_SPRITE"]:
|
||||
INCLUDES_NEEDED["sprites"].add(disasm_script.CONSTANTS['NPC_SPRITE'][str(value) + ".h"])
|
||||
anim = disasm_script.CONSTANTS['NPC_SPRITE'][value]
|
||||
INCLUDES_NEEDED["sprites"].add(disasm_script.CONSTANTS["NPC_SPRITE"][str(value) + ".h"])
|
||||
anim = disasm_script.CONSTANTS["NPC_SPRITE"][value]
|
||||
else:
|
||||
anim = f"{anim:06X}"
|
||||
|
||||
@ -851,15 +961,15 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
out += INDENT + "{\n"
|
||||
out += INDENT + INDENT + f".flags = {read_flags(d[0], 'ActorPartFlags')},\n"
|
||||
out += INDENT + INDENT + f".index = {d[1]},\n"
|
||||
out += INDENT + INDENT + f".posOffset = {{ {d[2]}, {d[3]}, {d[4]} }},\n"
|
||||
out += INDENT + INDENT + f".targetOffset = {{ {d[5]}, {d[6]} }},\n"
|
||||
out += INDENT + INDENT + f".opacity = {d[7]},\n"
|
||||
out += INDENT + INDENT + f".idleAnimations = N(IdleAnimations_{d[8]:08X}),\n"
|
||||
out += INDENT + INDENT + f".defenseTable = N(DefenseTable_{d[9]:08X}),\n"
|
||||
out += INDENT + INDENT + f".eventFlags = {read_flags(d[10], 'ActorEventFlags')},\n"
|
||||
out += INDENT + INDENT + f".elementImmunityFlags = {read_flags(d[11], 'ElementImmunityFlags')},\n"
|
||||
out += INDENT + INDENT + f".unk_1C = {d[12]},\n"
|
||||
out += INDENT + INDENT + f".unk_1D = {d[13]},\n"
|
||||
out += INDENT + INDENT + f".posOffset = {{ {d[2]}, {d[3]}, {d[4]} }},\n"
|
||||
out += INDENT + INDENT + f".targetOffset = {{ {d[5]}, {d[6]} }},\n"
|
||||
out += INDENT + INDENT + f".opacity = {d[7]},\n"
|
||||
out += INDENT + INDENT + f".idleAnimations = N(IdleAnimations_{d[8]:08X}),\n"
|
||||
out += INDENT + INDENT + f".defenseTable = N(DefenseTable_{d[9]:08X}),\n"
|
||||
out += INDENT + INDENT + f".eventFlags = {read_flags(d[10], 'ActorEventFlags')},\n"
|
||||
out += INDENT + INDENT + f".elementImmunityFlags = {read_flags(d[11], 'ElementImmunityFlags')},\n"
|
||||
out += INDENT + INDENT + f".unk_1C = {d[12]},\n"
|
||||
out += INDENT + INDENT + f".unk_1D = {d[13]},\n"
|
||||
out += INDENT + "},\n"
|
||||
|
||||
out += f"}};\n"
|
||||
@ -895,7 +1005,18 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
elif struct["type"] == "Stage":
|
||||
out += f"Stage NAMESPACE = {{\n"
|
||||
|
||||
texture, shape, hit, preBattle, postBattle, bg, unk_18, unk_1C, unk_20, unk_24 = unpack(">IIIIIIIIII", bytes.read(struct["length"]))
|
||||
(
|
||||
texture,
|
||||
shape,
|
||||
hit,
|
||||
preBattle,
|
||||
postBattle,
|
||||
bg,
|
||||
unk_18,
|
||||
unk_1C,
|
||||
unk_20,
|
||||
unk_24,
|
||||
) = unpack(">IIIIIIIIII", bytes.read(struct["length"]))
|
||||
|
||||
if texture != 0:
|
||||
out += f" .texture = {symbol_map[texture][0][1]},\n"
|
||||
@ -928,9 +1049,9 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
out += f" .unk_24 = {unk_24:X},\n"
|
||||
|
||||
out += f"}};\n"
|
||||
else: # unknown type of struct
|
||||
else: # unknown type of struct
|
||||
if struct["name"].startswith("N(unk_802"):
|
||||
vram = int(name.split("_",1)[1][:-1], 16)
|
||||
vram = int(name.split("_", 1)[1][:-1], 16)
|
||||
name = f"N(D_{vram:X}_{(vram - 0x80240000) + romstart:X})"
|
||||
struct["name"] = name
|
||||
|
||||
@ -957,53 +1078,63 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
||||
# end of data
|
||||
return out
|
||||
|
||||
|
||||
def parse_midx(file, prefix="", vram=0x80240000):
|
||||
structs = []
|
||||
|
||||
for line in file.readlines():
|
||||
s = line.split("#")
|
||||
if len(s) == 5 or len(s) == 6:
|
||||
if s[0] == "$Start": continue
|
||||
if s[0] == "$End": continue
|
||||
if s[0] == "$Start":
|
||||
continue
|
||||
if s[0] == "$End":
|
||||
continue
|
||||
|
||||
structs.append({
|
||||
"name": "N(" + prefix + name_struct(s[0]) + ")",
|
||||
"type": s[1],
|
||||
"start": int(s[2], 16),
|
||||
"vaddr": int(s[3], 16),
|
||||
"length": int(s[4], 16),
|
||||
"end": int(s[2], 16) + int(s[4], 16),
|
||||
})
|
||||
structs.append(
|
||||
{
|
||||
"name": "N(" + prefix + name_struct(s[0]) + ")",
|
||||
"type": s[1],
|
||||
"start": int(s[2], 16),
|
||||
"vaddr": int(s[3], 16),
|
||||
"length": int(s[4], 16),
|
||||
"end": int(s[2], 16) + int(s[4], 16),
|
||||
}
|
||||
)
|
||||
elif "Missing" in s:
|
||||
start = int(s[1], 16)
|
||||
end = int(s[2], 16)
|
||||
vaddr = start + vram
|
||||
structs.append({
|
||||
"name": f"{prefix}unk_missing_{vaddr:X}",
|
||||
"type": "Missing",
|
||||
"start": start,
|
||||
"vaddr": vaddr,
|
||||
"length": end - start,
|
||||
"end": end,
|
||||
})
|
||||
structs.append(
|
||||
{
|
||||
"name": f"{prefix}unk_missing_{vaddr:X}",
|
||||
"type": "Missing",
|
||||
"start": start,
|
||||
"vaddr": vaddr,
|
||||
"length": end - start,
|
||||
"end": end,
|
||||
}
|
||||
)
|
||||
elif "Padding" in s:
|
||||
start = int(s[1], 16)
|
||||
end = int(s[2], 16)
|
||||
vaddr = start + vram
|
||||
structs.append({
|
||||
"name": f"{prefix}pad_{start:X}",
|
||||
"type": "Padding",
|
||||
"start": start,
|
||||
"vaddr": vaddr,
|
||||
"length": end - start,
|
||||
"end": end,
|
||||
})
|
||||
structs.append(
|
||||
{
|
||||
"name": f"{prefix}pad_{start:X}",
|
||||
"type": "Padding",
|
||||
"start": start,
|
||||
"vaddr": vaddr,
|
||||
"length": end - start,
|
||||
"end": end,
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise Exception(str(s))
|
||||
|
||||
structs.sort(key=lambda s: s["start"])
|
||||
return structs
|
||||
|
||||
|
||||
def name_struct(s):
|
||||
s = s[1:].replace("???", "unk")
|
||||
|
||||
@ -1032,10 +1163,11 @@ def name_struct(s):
|
||||
|
||||
return s[0].lower() + s[1:]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Converts split data to C using a Star Rod idx file")
|
||||
parser.add_argument("idxfile", help="Input .*idx file from Star Rod dump")
|
||||
parser.add_argument("namespace", nargs='?', help="Value of NAMESPACE macro")
|
||||
parser.add_argument("namespace", nargs="?", help="Value of NAMESPACE macro")
|
||||
parser.add_argument("--comments", action="store_true", help="Write offset/vaddr comments")
|
||||
|
||||
args = parser.parse_args()
|
||||
@ -1053,8 +1185,7 @@ if __name__ == "__main__":
|
||||
segment_name = f"battle/partners/{battle_area}"
|
||||
elif "/starpower/src/" in args.idxfile:
|
||||
segment_name = (
|
||||
f"battle/star/{battle_area}"
|
||||
.replace("starstorm", "star_storm")
|
||||
f"battle/star/{battle_area}".replace("starstorm", "star_storm")
|
||||
.replace("chillout", "chill_out")
|
||||
.replace("timeout", "time_out")
|
||||
.replace("upandaway", "up_and_away")
|
||||
@ -1069,6 +1200,7 @@ if __name__ == "__main__":
|
||||
is_battle = True
|
||||
|
||||
symbol_map = disasm_script.script_lib()
|
||||
|
||||
def add_to_symbol_map(addr, pair):
|
||||
if addr in symbol_map:
|
||||
symbol_map[addr].append(pair)
|
||||
@ -1082,7 +1214,9 @@ if __name__ == "__main__":
|
||||
|
||||
rom_offset = -1
|
||||
for segment in splat_config["segments"]:
|
||||
if isinstance(segment, dict) and (segment.get("dir") == segment_name or segment.get("name") == segment_name):
|
||||
if isinstance(segment, dict) and (
|
||||
segment.get("dir") == segment_name or segment.get("name") == segment_name
|
||||
):
|
||||
rom_offset = segment["start"]
|
||||
vram = segment["vram"]
|
||||
break
|
||||
@ -1102,11 +1236,11 @@ if __name__ == "__main__":
|
||||
|
||||
with open(os.path.join(DIR, "../ver/current/baserom.z64"), "rb") as romfile:
|
||||
name_fixes = {
|
||||
"script_NpcAI": "npcAI",
|
||||
"aISettings": "npcAISettings",
|
||||
"script_ExitWalk": "exitWalk",
|
||||
"script_MakeEntities": "makeEntities",
|
||||
}
|
||||
"script_NpcAI": "npcAI",
|
||||
"aISettings": "npcAISettings",
|
||||
"script_ExitWalk": "exitWalk",
|
||||
"script_MakeEntities": "makeEntities",
|
||||
}
|
||||
total_npc_counts = {}
|
||||
for struct in midx:
|
||||
romfile.seek(struct["start"] + rom_offset)
|
||||
@ -1116,13 +1250,13 @@ if __name__ == "__main__":
|
||||
if name.startswith("N("):
|
||||
name = name[2:-1]
|
||||
|
||||
if struct['vaddr'] in function_replacements:
|
||||
name = function_replacements[struct['vaddr']]
|
||||
if struct["vaddr"] in function_replacements:
|
||||
name = function_replacements[struct["vaddr"]]
|
||||
|
||||
if name.split("_",1)[0] in name_fixes:
|
||||
name = name_fixes[name.split("_",1)[0]] + "_" + name.rsplit("_",1)[1]
|
||||
if name.split("_", 1)[0] in name_fixes:
|
||||
name = name_fixes[name.split("_", 1)[0]] + "_" + name.rsplit("_", 1)[1]
|
||||
elif name.startswith("script_"):
|
||||
name = name.split("script_",1)[1]
|
||||
name = name.split("script_", 1)[1]
|
||||
elif "_Main_" in name:
|
||||
name = "main"
|
||||
elif "ASCII" in name:
|
||||
@ -1154,19 +1288,19 @@ if __name__ == "__main__":
|
||||
double_literal = f"{double}"
|
||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], double_literal])
|
||||
elif struct["type"] == "NpcGroup":
|
||||
for z in range(struct["length"]//0x1F0):
|
||||
for z in range(struct["length"] // 0x1F0):
|
||||
npc = romfile.read(0x1F0)
|
||||
npc_id = unpack_from(">I", npc, 0)[0]
|
||||
if npc_id >= 0:
|
||||
anim = unpack_from(">I", npc, 0x1A0)[0]
|
||||
if not anim == 0:
|
||||
sprite_id = (anim & 0x00FF0000) >> 16
|
||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"].upper()
|
||||
sprite_id = (anim & 0x00FF0000) >> 16
|
||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"].upper()
|
||||
if npc_id not in total_npc_counts:
|
||||
total_npc_counts[npc_id] = sprite
|
||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
||||
else:
|
||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
||||
|
||||
# fix NPC names
|
||||
curr_counts = {}
|
||||
@ -1186,17 +1320,24 @@ if __name__ == "__main__":
|
||||
|
||||
romfile.seek(rom_offset, 0)
|
||||
|
||||
disasm = disassemble(romfile, midx, symbol_map, args.comments, rom_offset, namespace=args.namespace)
|
||||
disasm = disassemble(
|
||||
romfile,
|
||||
midx,
|
||||
symbol_map,
|
||||
args.comments,
|
||||
rom_offset,
|
||||
namespace=args.namespace,
|
||||
)
|
||||
|
||||
print("========== Includes needed: ===========\n")
|
||||
if is_battle:
|
||||
print(f"#include \"battle/battle.h\"")
|
||||
print(f'#include "battle/battle.h"')
|
||||
else:
|
||||
print(f"#include \"map.h\"")
|
||||
print(f"#include \"message_ids.h\"")
|
||||
print(f'#include "map.h"')
|
||||
print(f'#include "message_ids.h"')
|
||||
if INCLUDES_NEEDED["sprites"]:
|
||||
for npc in sorted(INCLUDES_NEEDED["sprites"]):
|
||||
print(f"#include \"sprite/npc/{npc}\"")
|
||||
print(f'#include "sprite/npc/{npc}"')
|
||||
print()
|
||||
|
||||
if INCLUDES_NEEDED["forward"]:
|
||||
@ -1213,7 +1354,7 @@ if __name__ == "__main__":
|
||||
print(f"enum {{")
|
||||
lastnum = -1
|
||||
for i, (k, v) in enumerate(sorted(INCLUDES_NEEDED["npcs"].items())):
|
||||
print(f" {v}" + (f" = {k}" if ((k > 0 and i == 0) or (k != lastnum+1)) else "") + ",")
|
||||
print(f" {v}" + (f" = {k}" if ((k > 0 and i == 0) or (k != lastnum + 1)) else "") + ",")
|
||||
lastnum = k
|
||||
print(f"}};")
|
||||
print()
|
||||
|
@ -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,12 +67,13 @@ def scan_map():
|
||||
|
||||
map_symbols[sym] = (rom, cur_file, ram)
|
||||
|
||||
|
||||
def read_symbol_addrs():
|
||||
unique_lines = set()
|
||||
|
||||
with open(symbol_addrs_path, "r") as f:
|
||||
for line in f.readlines():
|
||||
unique_lines.add(line)
|
||||
unique_lines.add(line)
|
||||
|
||||
for line in unique_lines:
|
||||
if "_ROM_START" in line or "_ROM_END" in line:
|
||||
@ -117,9 +115,10 @@ def read_symbol_addrs():
|
||||
else:
|
||||
dead_symbols.append([name, int(addr, 0), type, rom, opts])
|
||||
|
||||
|
||||
def read_elf():
|
||||
try:
|
||||
result = subprocess.run(['mips-linux-gnu-objdump', '-x', elf_path], stdout=subprocess.PIPE)
|
||||
result = subprocess.run(["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE)
|
||||
objdump_lines = result.stdout.decode().split("\n")
|
||||
except:
|
||||
print(f"Error: Could not run objdump on {elf_path} - make sure that the project is built")
|
||||
@ -133,13 +132,15 @@ def read_elf():
|
||||
if "_ROM_START" in name or "_ROM_END" in name:
|
||||
continue
|
||||
|
||||
if "/" in name or \
|
||||
"." in name or \
|
||||
name in ignores or \
|
||||
name.startswith("_") or \
|
||||
name.startswith("jtbl_") or \
|
||||
name.endswith(".o") or \
|
||||
re.match(r"L[0-9A-F]{8}", name):
|
||||
if (
|
||||
"/" in name
|
||||
or "." in name
|
||||
or name in ignores
|
||||
or name.startswith("_")
|
||||
or name.startswith("jtbl_")
|
||||
or name.endswith(".o")
|
||||
or re.match(r"L[0-9A-F]{8}", name)
|
||||
):
|
||||
continue
|
||||
|
||||
addr = int(components[0], 16)
|
||||
@ -153,14 +154,16 @@ def read_elf():
|
||||
if name in map_symbols:
|
||||
rom = map_symbols[name][0]
|
||||
elif re.match(".*_[0-9A-F]{8}_[0-9A-F]{6}", name):
|
||||
rom = int(name.split('_')[-1], 16)
|
||||
rom = int(name.split("_")[-1], 16)
|
||||
|
||||
elf_symbols.append((name, addr, type, rom))
|
||||
|
||||
|
||||
def log(s):
|
||||
if verbose:
|
||||
print(s)
|
||||
|
||||
|
||||
def reconcile_symbols():
|
||||
print(f"Processing {str(len(elf_symbols))} elf symbols...")
|
||||
|
||||
@ -175,7 +178,9 @@ def reconcile_symbols():
|
||||
name_match = known_sym
|
||||
|
||||
if elf_sym[1] != known_sym[1]:
|
||||
log(f"Ram mismatch! {elf_sym[0]} is 0x{elf_sym[1]:X} in the elf and 0x{known_sym[1]} in symbol_addrs")
|
||||
log(
|
||||
f"Ram mismatch! {elf_sym[0]} is 0x{elf_sym[1]:X} in the elf and 0x{known_sym[1]} in symbol_addrs"
|
||||
)
|
||||
|
||||
# Rom
|
||||
if not rom_match:
|
||||
@ -186,7 +191,15 @@ def reconcile_symbols():
|
||||
|
||||
if not name_match and not rom_match:
|
||||
log(f"Creating new symbol {elf_sym[0]}")
|
||||
symbol_addrs.append([elf_sym[0], elf_sym[1], elf_sym[2], elf_sym[3] if elf_sym[3] else -1, []])
|
||||
symbol_addrs.append(
|
||||
[
|
||||
elf_sym[0],
|
||||
elf_sym[1],
|
||||
elf_sym[2],
|
||||
elf_sym[3] if elf_sym[3] else -1,
|
||||
[],
|
||||
]
|
||||
)
|
||||
elif not name_match:
|
||||
log(f"Renaming identical rom address symbol {rom_match[0]} to {elf_sym[0]}")
|
||||
rom_match[0] = elf_sym[0]
|
||||
@ -197,6 +210,7 @@ def reconcile_symbols():
|
||||
log(f"Adding rom address {elf_sym[3]} to symbol {name_match[0]}")
|
||||
name_match[3] = elf_sym[3]
|
||||
|
||||
|
||||
def write_new_symbol_addrs():
|
||||
with open(symbol_addrs_path, "w", newline="\n") as f:
|
||||
for symbol in sorted(symbol_addrs, key=lambda x: (x[3] == -1, x[3], x[1], x[0])):
|
||||
@ -220,6 +234,7 @@ def write_new_symbol_addrs():
|
||||
line += f" {thing}"
|
||||
f.write(line + "\n")
|
||||
|
||||
|
||||
read_ignores()
|
||||
scan_map()
|
||||
read_symbol_addrs()
|
||||
|
@ -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