mirror of
https://github.com/pmret/papermario.git
synced 2024-09-16 14:32:37 +02:00
Add Python linter to github actions (#1100)
* Add Python linter to github actions * wip * Add back splat_ext * Format files * C++ -> C * format 2 files * split workflow into separate file, line length 120, fix excludes * -l 120 in ci * update black locally and apply formatting changes * pyproject.toject --------- Co-authored-by: Ethan Roseman <ethteck@gmail.com>
This commit is contained in:
parent
a69ae38bfe
commit
ae66312d8c
2
.github/workflows/lint.yaml
vendored
2
.github/workflows/lint.yaml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cpp_lint:
|
cpp_lint:
|
||||||
name: Format and lint
|
name: C format and lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- 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": [
|
"includePath": [
|
||||||
"${workspaceFolder}/include",
|
"${workspaceFolder}/include",
|
||||||
"${workspaceFolder}/ver/us/build/include",
|
"${workspaceFolder}/ver/pal/build/include",
|
||||||
"${workspaceFolder}/src",
|
"${workspaceFolder}/src",
|
||||||
"${workspaceFolder}/assets/us"
|
"${workspaceFolder}/assets/pal"
|
||||||
],
|
],
|
||||||
"defines": [
|
"defines": [
|
||||||
"F3DEX_GBI_2",
|
"F3DEX_GBI_2",
|
||||||
|
18
.vscode/settings.json
vendored
18
.vscode/settings.json
vendored
@ -25,7 +25,9 @@
|
|||||||
"docs/doxygen": true,
|
"docs/doxygen": true,
|
||||||
"expected": true,
|
"expected": true,
|
||||||
"ver/jp/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": [
|
"python.autoComplete.extraPaths": [
|
||||||
"./tools"
|
"./tools"
|
||||||
@ -47,6 +49,7 @@
|
|||||||
"*.h": "c",
|
"*.h": "c",
|
||||||
},
|
},
|
||||||
"C_Cpp.autoAddFileAssociations": false,
|
"C_Cpp.autoAddFileAssociations": false,
|
||||||
|
"C_Cpp.default.cStandard": "c89",
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"**/.git": true,
|
"**/.git": true,
|
||||||
"**/.splat_cache": true,
|
"**/.splat_cache": true,
|
||||||
@ -56,7 +59,14 @@
|
|||||||
"**/*.i": true,
|
"**/*.i": true,
|
||||||
"docs/doxygen": true
|
"docs/doxygen": true
|
||||||
},
|
},
|
||||||
"C_Cpp.default.cStandard": "c89",
|
"[python]": {
|
||||||
"python.linting.mypyEnabled": true,
|
"editor.formatOnType": true,
|
||||||
"python.linting.enabled": 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
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def strip_c_comments(text):
|
def strip_c_comments(text):
|
||||||
def replacer(match):
|
def replacer(match):
|
||||||
s = match.group(0)
|
s = match.group(0)
|
||||||
if s.startswith('/'):
|
if s.startswith("/"):
|
||||||
return " "
|
return " "
|
||||||
else:
|
else:
|
||||||
return s
|
return s
|
||||||
|
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
||||||
re.DOTALL | re.MULTILINE
|
re.DOTALL | re.MULTILINE,
|
||||||
)
|
)
|
||||||
return re.sub(pattern, replacer, text)
|
return re.sub(pattern, replacer, text)
|
||||||
|
|
||||||
c_func_pattern = re.compile(
|
|
||||||
r"^(static\s+)?[^\s]+\s+([^\s(]+)\(([^;)]*)\)[^;]+{",
|
c_func_pattern = re.compile(r"^(static\s+)?[^\s]+\s+([^\s(]+)\(([^;)]*)\)[^;]+{", re.MULTILINE)
|
||||||
re.MULTILINE
|
|
||||||
)
|
|
||||||
def funcs_in_c(text):
|
def funcs_in_c(text):
|
||||||
return (match.group(2) for match in c_func_pattern.finditer(text))
|
return (match.group(2) for match in c_func_pattern.finditer(text))
|
||||||
|
|
||||||
asm_func_pattern = re.compile(
|
|
||||||
r"INCLUDE_ASM\([^,]+, [^,]+, ([^,)]+)",
|
asm_func_pattern = re.compile(r"INCLUDE_ASM\([^,]+, [^,]+, ([^,)]+)", re.MULTILINE)
|
||||||
re.MULTILINE
|
|
||||||
)
|
|
||||||
def include_asms_in_c(text):
|
def include_asms_in_c(text):
|
||||||
return (match.group(1) for match in asm_func_pattern.finditer(text))
|
return (match.group(1) for match in asm_func_pattern.finditer(text))
|
||||||
|
|
||||||
|
|
||||||
def stuff(version):
|
def stuff(version):
|
||||||
DIR = os.path.dirname(__file__)
|
DIR = os.path.dirname(__file__)
|
||||||
NONMATCHINGS_DIR = Path(os.path.join(DIR, "ver", version, "asm", "nonmatchings"))
|
NONMATCHINGS_DIR = Path(os.path.join(DIR, "ver", version, "asm", "nonmatchings"))
|
||||||
@ -76,6 +79,7 @@ def stuff(version):
|
|||||||
if not os.listdir(folder[0]):
|
if not os.listdir(folder[0]):
|
||||||
os.removedirs(folder[0])
|
os.removedirs(folder[0])
|
||||||
|
|
||||||
|
|
||||||
stuff("jp")
|
stuff("jp")
|
||||||
stuff("us")
|
stuff("us")
|
||||||
stuff("pal")
|
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 old.update_evts import parse_symbol_addrs
|
||||||
from tools.disasm_script import ScriptDisassembler, get_constants
|
from tools.disasm_script import ScriptDisassembler, get_constants
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Diff EVT macros.")
|
||||||
description="Diff EVT macros."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"start",
|
"start",
|
||||||
@ -26,21 +24,13 @@ parser.add_argument(
|
|||||||
"-w",
|
"-w",
|
||||||
"--watch",
|
"--watch",
|
||||||
action="store_true",
|
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(
|
parser.add_argument("-m", "--make", action="store_true", help="Run ninja automatically.")
|
||||||
"-m",
|
|
||||||
"--make",
|
parser.add_argument("-o", action="store_true", help="Ignored for compatibility with diff.py.")
|
||||||
action="store_true",
|
|
||||||
help="Run ninja automatically."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-o",
|
|
||||||
action="store_true",
|
|
||||||
help="Ignored for compatibility with diff.py."
|
|
||||||
)
|
|
||||||
|
|
||||||
class EvtDisplay(Display):
|
class EvtDisplay(Display):
|
||||||
def __init__(self, start):
|
def __init__(self, start):
|
||||||
@ -106,11 +96,13 @@ class EvtDisplay(Display):
|
|||||||
refresh_key = (current, target)
|
refresh_key = (current, target)
|
||||||
return (output, refresh_key)
|
return (output, refresh_key)
|
||||||
|
|
||||||
class FakeConfig():
|
|
||||||
|
class FakeConfig:
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
self.make = args.make
|
self.make = args.make
|
||||||
self.source_extensions = ["c", "h"]
|
self.source_extensions = ["c", "h"]
|
||||||
|
|
||||||
|
|
||||||
def run_ninja():
|
def run_ninja():
|
||||||
return subprocess.run(
|
return subprocess.run(
|
||||||
["ninja", "ver/current/build/papermario.z64"],
|
["ninja", "ver/current/build/papermario.z64"],
|
||||||
@ -118,6 +110,7 @@ def run_ninja():
|
|||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
get_constants()
|
get_constants()
|
||||||
@ -153,8 +146,7 @@ def main():
|
|||||||
ret = run_ninja()
|
ret = run_ninja()
|
||||||
if ret.returncode != 0:
|
if ret.returncode != 0:
|
||||||
display.update(
|
display.update(
|
||||||
ret.stderr.decode("utf-8-sig", "replace")
|
ret.stderr.decode("utf-8-sig", "replace") or ret.stdout.decode("utf-8-sig", "replace"),
|
||||||
or ret.stdout.decode("utf-8-sig", "replace"),
|
|
||||||
error=True,
|
error=True,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@ -164,5 +156,6 @@ def main():
|
|||||||
else:
|
else:
|
||||||
display.run_sync()
|
display.run_sync()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -22,7 +22,7 @@ parser.add_argument(
|
|||||||
action="store",
|
action="store",
|
||||||
default=False,
|
default=False,
|
||||||
const="prompt",
|
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(
|
parser.add_argument(
|
||||||
"-m", "--make", help="run ninja before finding difference(s)", action="store_true"
|
"-m", "--make", help="run ninja before finding difference(s)", action="store_true"
|
||||||
@ -101,7 +101,9 @@ def search_rom_address(target_addr):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if rom > target_addr:
|
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_ram = ram
|
||||||
prev_rom = rom
|
prev_rom = rom
|
||||||
@ -214,9 +216,7 @@ if diffs == 0:
|
|||||||
if len(found_instr_diff) > 0:
|
if len(found_instr_diff) > 0:
|
||||||
for i in found_instr_diff:
|
for i in found_instr_diff:
|
||||||
print(f"Instruction difference at ROM addr 0x{i:X}, {search_rom_address(i)}")
|
print(f"Instruction difference at ROM addr 0x{i:X}, {search_rom_address(i)}")
|
||||||
print(
|
print(f"Bytes: {hexbytes(mybin[i : i + 4])} vs {hexbytes(basebin[i : i + 4])}")
|
||||||
f"Bytes: {hexbytes(mybin[i : i + 4])} vs {hexbytes(basebin[i : i + 4])}"
|
|
||||||
)
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
definite_shift = diffs > shift_cap
|
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]
|
version = Path("ver/current").resolve().parts[-1]
|
||||||
|
|
||||||
csv = (
|
csv = urlopen(f"https://papermar.io/reports/progress_{version}.csv").read().decode("utf-8")
|
||||||
urlopen(f"https://papermar.io/reports/progress_{version}.csv")
|
|
||||||
.read()
|
|
||||||
.decode("utf-8")
|
|
||||||
)
|
|
||||||
latest = csv.split("\n")[-2]
|
latest = csv.split("\n")[-2]
|
||||||
|
|
||||||
(
|
(
|
||||||
@ -56,14 +52,10 @@ def load_latest_progress(version):
|
|||||||
|
|
||||||
def get_func_info():
|
def get_func_info():
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE)
|
||||||
["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE
|
|
||||||
)
|
|
||||||
nm_lines = result.stdout.decode().split("\n")
|
nm_lines = result.stdout.decode().split("\n")
|
||||||
except:
|
except:
|
||||||
print(
|
print(f"Error: Could not run objdump on {elf_path} - make sure that the project is built")
|
||||||
f"Error: Could not run objdump on {elf_path} - make sure that the project is built"
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sizes = {}
|
sizes = {}
|
||||||
@ -135,19 +127,13 @@ def do_section_progress(
|
|||||||
section_vram_end,
|
section_vram_end,
|
||||||
):
|
):
|
||||||
funcs = get_funcs_in_vram_range(vrams, section_vram_start, section_vram_end)
|
funcs = get_funcs_in_vram_range(vrams, section_vram_start, section_vram_end)
|
||||||
matching_size, nonmatching_size = get_funcs_sizes(
|
matching_size, nonmatching_size = get_funcs_sizes(sizes, matchings, nonmatchings, restrict_to=funcs)
|
||||||
sizes, matchings, nonmatchings, restrict_to=funcs
|
|
||||||
)
|
|
||||||
section_total_size = matching_size + nonmatching_size
|
section_total_size = matching_size + nonmatching_size
|
||||||
progress_ratio = (matching_size / section_total_size) * 100
|
progress_ratio = (matching_size / section_total_size) * 100
|
||||||
matching_ratio = (matching_size / total_size) * 100
|
matching_ratio = (matching_size / total_size) * 100
|
||||||
total_ratio = (section_total_size / total_size) * 100
|
total_ratio = (section_total_size / total_size) * 100
|
||||||
print(
|
print(f"\t{section_name}: {matching_size} matching bytes / {section_total_size} total ({progress_ratio:.2f}%)")
|
||||||
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\t(matched {matching_ratio:.2f}% of {total_ratio:.2f}% total rom for {section_name})"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
@ -163,9 +149,7 @@ def main(args):
|
|||||||
nonmatching_funcs = get_nonmatching_funcs()
|
nonmatching_funcs = get_nonmatching_funcs()
|
||||||
matching_funcs = all_funcs - nonmatching_funcs
|
matching_funcs = all_funcs - nonmatching_funcs
|
||||||
|
|
||||||
matching_size, nonmatching_size = get_funcs_sizes(
|
matching_size, nonmatching_size = get_funcs_sizes(sizes, matching_funcs, nonmatching_funcs)
|
||||||
sizes, matching_funcs, nonmatching_funcs
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(all_funcs) == 0:
|
if len(all_funcs) == 0:
|
||||||
funcs_matching_ratio = 0.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(f"Warning: category/total size mismatch on version {args.version}!\n")
|
||||||
print("Matching size: " + str(matching_size))
|
print("Matching size: " + str(matching_size))
|
||||||
print("Nonmatching size: " + str(nonmatching_size))
|
print("Nonmatching size: " + str(nonmatching_size))
|
||||||
print(
|
print("Sum: " + str(matching_size + nonmatching_size) + " (should be " + str(total_size) + ")")
|
||||||
"Sum: "
|
print(f"{len(matching_funcs)} matched functions / {len(all_funcs)} total ({funcs_matching_ratio:.2f}%)")
|
||||||
+ str(matching_size + nonmatching_size)
|
print(f"{matching_size} matching bytes / {total_size} total ({matching_ratio:.2f}%)")
|
||||||
+ " (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(
|
do_section_progress(
|
||||||
"effects",
|
"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__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
asm_dir = script_dir + "/../ver/current/asm/nonmatchings"
|
asm_dir = script_dir + "/../ver/current/asm/nonmatchings"
|
||||||
|
|
||||||
modes = [ "min", "max", "avg", "total", "size" ]
|
modes = ["min", "max", "avg", "total", "size"]
|
||||||
|
|
||||||
sizes = {}
|
sizes = {}
|
||||||
|
|
||||||
@ -47,18 +47,53 @@ def do_dir(root, dir):
|
|||||||
|
|
||||||
avg = 0 if len(files) == 0 else total / len(files)
|
avg = 0 if len(files) == 0 else total / len(files)
|
||||||
|
|
||||||
sizes[root + "/" + dir] = ((min, max, total, avg, len(files)))
|
sizes[root + "/" + dir] = (min, max, total, avg, len(files))
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="A tool to receive information about the number of non-matching .s files "
|
parser = argparse.ArgumentParser(
|
||||||
+"per .c file, or the size of .s files, measured by their number of instructions. "
|
description="A tool to receive information about the number of non-matching .s files "
|
||||||
+"Option -p is used by default if no option is specified.")
|
+ "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 = 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(
|
||||||
group.add_argument("-a", "--alphabetical", help="Print the size of .s files, ordered by name.", action='store_true', required=False)
|
"-f",
|
||||||
group.add_argument("-s", "--size", help="Print the size of .s files, ordered by size.", action='store_true', required=False)
|
"--files",
|
||||||
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)
|
help="Default. Print the number of non-matching .s files per .c file, ordered by size.",
|
||||||
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)
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -12,6 +12,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
with open(infile, "rb") as i:
|
with open(infile, "rb") as i:
|
||||||
for char in i.read():
|
for char in i.read():
|
||||||
f.write(f'0x{char:02X}, ')
|
f.write(f"0x{char:02X}, ")
|
||||||
|
|
||||||
f.write(f"}};\n")
|
f.write(f"}};\n")
|
||||||
|
@ -4,10 +4,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
ASSETS_DIR = (
|
ASSETS_DIR = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) / "assets"
|
||||||
Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
||||||
/ "assets"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=None)
|
||||||
|
@ -28,9 +28,7 @@ PIGMENT_REQ_VERSION = "0.3.0"
|
|||||||
|
|
||||||
|
|
||||||
def exec_shell(command: List[str]) -> str:
|
def exec_shell(command: List[str]) -> str:
|
||||||
ret = subprocess.run(
|
ret = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
|
||||||
)
|
|
||||||
return ret.stdout
|
return ret.stdout
|
||||||
|
|
||||||
|
|
||||||
@ -50,9 +48,7 @@ def write_ninja_rules(
|
|||||||
if use_ccache:
|
if use_ccache:
|
||||||
ccache = "ccache "
|
ccache = "ccache "
|
||||||
try:
|
try:
|
||||||
subprocess.call(
|
subprocess.call(["ccache"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
["ccache"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
|
||||||
)
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
ccache = ""
|
ccache = ""
|
||||||
|
|
||||||
@ -134,9 +130,7 @@ def write_ninja_rules(
|
|||||||
command="sha1sum -c $in && touch $out" if DO_SHA1_CHECK else "touch $out",
|
command="sha1sum -c $in && touch $out" if DO_SHA1_CHECK else "touch $out",
|
||||||
)
|
)
|
||||||
|
|
||||||
ninja.rule(
|
ninja.rule("cpp", description="cpp $in", command=f"{cpp} $in {extra_cppflags} -P -o $out")
|
||||||
"cpp", description="cpp $in", command=f"{cpp} $in {extra_cppflags} -P -o $out"
|
|
||||||
)
|
|
||||||
|
|
||||||
ninja.rule(
|
ninja.rule(
|
||||||
"cc",
|
"cc",
|
||||||
@ -287,9 +281,7 @@ def write_ninja_rules(
|
|||||||
command=f"$python {BUILD_TOOLS}/mapfs/pack_title_data.py $out $in",
|
command=f"$python {BUILD_TOOLS}/mapfs/pack_title_data.py $out $in",
|
||||||
)
|
)
|
||||||
|
|
||||||
ninja.rule(
|
ninja.rule("map_header", command=f"$python {BUILD_TOOLS}/mapfs/map_header.py $in > $out")
|
||||||
"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")
|
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",
|
command=f"$python {BUILD_TOOLS}/sprite/sprite_shading_profiles.py $in $out $header_path",
|
||||||
)
|
)
|
||||||
|
|
||||||
ninja.rule(
|
ninja.rule("imgfx_data", command=f"$python {BUILD_TOOLS}/imgfx/imgfx_data.py $in $out")
|
||||||
"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("shape", command=f"$python {BUILD_TOOLS}/mapfs/shape.py $in $out")
|
||||||
|
|
||||||
ninja.rule(
|
ninja.rule("effect_data", command=f"$python {BUILD_TOOLS}/effects.py $in_yaml $out_dir")
|
||||||
"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")
|
ninja.rule("pm_sbn", command=f"$python {BUILD_TOOLS}/audio/sbn.py $out $in")
|
||||||
|
|
||||||
with Path("tools/permuter_settings.toml").open("w") as f:
|
with Path("tools/permuter_settings.toml").open("w") as f:
|
||||||
f.write(
|
f.write(f"compiler_command = \"{cc} {CPPFLAGS.replace('$version', 'pal')} {cflags} -DPERMUTER -fforce-addr\"\n")
|
||||||
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'assembler_command = "{cross}as -EB -march=vr4300 -mtune=vr4300 -Iinclude"\n'
|
|
||||||
)
|
|
||||||
f.write(f'compiler_type = "gcc"\n')
|
f.write(f'compiler_type = "gcc"\n')
|
||||||
f.write(
|
f.write(
|
||||||
"""
|
"""
|
||||||
@ -512,11 +496,7 @@ class Configure:
|
|||||||
for object_path in object_paths:
|
for object_path in object_paths:
|
||||||
if object_path.suffixes[-1] == ".o":
|
if object_path.suffixes[-1] == ".o":
|
||||||
built_objects.add(str(object_path))
|
built_objects.add(str(object_path))
|
||||||
elif (
|
elif object_path.suffixes[-1] == ".h" or task == "bin_inc_c" or task == "pal_inc_c":
|
||||||
object_path.suffixes[-1] == ".h"
|
|
||||||
or task == "bin_inc_c"
|
|
||||||
or task == "pal_inc_c"
|
|
||||||
):
|
|
||||||
generated_headers.append(str(object_path))
|
generated_headers.append(str(object_path))
|
||||||
|
|
||||||
# don't rebuild objects if we've already seen all of them
|
# 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):
|
if isinstance(seg, segtypes.n64.header.N64SegHeader):
|
||||||
build(entry.object_path, entry.src_paths, "as")
|
build(entry.object_path, entry.src_paths, "as")
|
||||||
elif isinstance(seg, segtypes.common.asm.CommonSegAsm) or (
|
elif isinstance(seg, segtypes.common.asm.CommonSegAsm) or (
|
||||||
isinstance(seg, segtypes.common.data.CommonSegData)
|
isinstance(seg, segtypes.common.data.CommonSegData) and not seg.type[0] == "."
|
||||||
and not seg.type[0] == "."
|
|
||||||
):
|
):
|
||||||
build(entry.object_path, entry.src_paths, "as")
|
build(entry.object_path, entry.src_paths, "as")
|
||||||
elif seg.type in ["pm_effect_loads", "pm_effect_shims"]:
|
elif seg.type in ["pm_effect_loads", "pm_effect_shims"]:
|
||||||
build(entry.object_path, entry.src_paths, "as")
|
build(entry.object_path, entry.src_paths, "as")
|
||||||
elif isinstance(seg, segtypes.common.c.CommonSegC) or (
|
elif isinstance(seg, segtypes.common.c.CommonSegC) or (
|
||||||
isinstance(seg, segtypes.common.data.CommonSegData)
|
isinstance(seg, segtypes.common.data.CommonSegData) and seg.type[0] == "."
|
||||||
and seg.type[0] == "."
|
|
||||||
):
|
):
|
||||||
cflags = None
|
cflags = None
|
||||||
if isinstance(seg.yaml, dict):
|
if isinstance(seg.yaml, dict):
|
||||||
@ -619,16 +597,12 @@ class Configure:
|
|||||||
task = "cc_272"
|
task = "cc_272"
|
||||||
cflags = cflags.replace("gcc_272", "")
|
cflags = cflags.replace("gcc_272", "")
|
||||||
|
|
||||||
encoding = (
|
encoding = "CP932" # similar to SHIFT-JIS, but includes backslash and tilde
|
||||||
"CP932" # similar to SHIFT-JIS, but includes backslash and tilde
|
|
||||||
)
|
|
||||||
if version == "ique":
|
if version == "ique":
|
||||||
encoding = "EUC-JP"
|
encoding = "EUC-JP"
|
||||||
|
|
||||||
# Dead cod
|
# Dead cod
|
||||||
if isinstance(seg.parent.yaml, dict) and seg.parent.yaml.get(
|
if isinstance(seg.parent.yaml, dict) and seg.parent.yaml.get("dead_code", False):
|
||||||
"dead_code", False
|
|
||||||
):
|
|
||||||
obj_path = str(entry.object_path)
|
obj_path = str(entry.object_path)
|
||||||
init_obj_path = Path(obj_path + ".dead")
|
init_obj_path = Path(obj_path + ".dead")
|
||||||
build(
|
build(
|
||||||
@ -677,9 +651,7 @@ class Configure:
|
|||||||
|
|
||||||
src_paths = [seg.out_path().relative_to(ROOT)]
|
src_paths = [seg.out_path().relative_to(ROOT)]
|
||||||
inc_dir = self.build_path() / "include" / seg.dir
|
inc_dir = self.build_path() / "include" / seg.dir
|
||||||
bin_path = (
|
bin_path = self.build_path() / seg.dir / (seg.name + ".png.bin")
|
||||||
self.build_path() / seg.dir / (seg.name + ".png.bin")
|
|
||||||
)
|
|
||||||
|
|
||||||
build(
|
build(
|
||||||
bin_path,
|
bin_path,
|
||||||
@ -691,9 +663,7 @@ class Configure:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert seg.vram_start is not None, (
|
assert seg.vram_start is not None, "img with vram_start unset: " + seg.name
|
||||||
"img with vram_start unset: " + seg.name
|
|
||||||
)
|
|
||||||
|
|
||||||
c_sym = seg.create_symbol(
|
c_sym = seg.create_symbol(
|
||||||
addr=seg.vram_start,
|
addr=seg.vram_start,
|
||||||
@ -720,9 +690,7 @@ class Configure:
|
|||||||
elif isinstance(seg, segtypes.n64.palette.N64SegPalette):
|
elif isinstance(seg, segtypes.n64.palette.N64SegPalette):
|
||||||
src_paths = [seg.out_path().relative_to(ROOT)]
|
src_paths = [seg.out_path().relative_to(ROOT)]
|
||||||
inc_dir = self.build_path() / "include" / seg.dir
|
inc_dir = self.build_path() / "include" / seg.dir
|
||||||
bin_path = (
|
bin_path = self.build_path() / seg.dir / (seg.name + ".pal.bin")
|
||||||
self.build_path() / seg.dir / (seg.name + ".pal.bin")
|
|
||||||
)
|
|
||||||
|
|
||||||
build(
|
build(
|
||||||
bin_path,
|
bin_path,
|
||||||
@ -833,9 +801,7 @@ class Configure:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Sprites .bin
|
# Sprites .bin
|
||||||
sprite_player_header_path = str(
|
sprite_player_header_path = str(self.build_path() / "include/sprite/player.h")
|
||||||
self.build_path() / "include/sprite/player.h"
|
|
||||||
)
|
|
||||||
|
|
||||||
build(
|
build(
|
||||||
entry.object_path.with_suffix(".bin"),
|
entry.object_path.with_suffix(".bin"),
|
||||||
@ -843,9 +809,7 @@ class Configure:
|
|||||||
"sprites",
|
"sprites",
|
||||||
variables={
|
variables={
|
||||||
"header_out": sprite_player_header_path,
|
"header_out": sprite_player_header_path,
|
||||||
"build_dir": str(
|
"build_dir": str(self.build_path() / "assets" / self.version / "sprite"),
|
||||||
self.build_path() / "assets" / self.version / "sprite"
|
|
||||||
),
|
|
||||||
"asset_stack": ",".join(self.asset_stack),
|
"asset_stack": ",".join(self.asset_stack),
|
||||||
},
|
},
|
||||||
implicit_outputs=[sprite_player_header_path],
|
implicit_outputs=[sprite_player_header_path],
|
||||||
@ -859,9 +823,7 @@ class Configure:
|
|||||||
msg_bins = []
|
msg_bins = []
|
||||||
|
|
||||||
for section_idx, msg_path in enumerate(entry.src_paths):
|
for section_idx, msg_path in enumerate(entry.src_paths):
|
||||||
bin_path = (
|
bin_path = entry.object_path.with_suffix("") / f"{section_idx:02X}.bin"
|
||||||
entry.object_path.with_suffix("") / f"{section_idx:02X}.bin"
|
|
||||||
)
|
|
||||||
msg_bins.append(bin_path)
|
msg_bins.append(bin_path)
|
||||||
build(bin_path, [msg_path], "msg")
|
build(bin_path, [msg_path], "msg")
|
||||||
|
|
||||||
@ -1005,16 +967,12 @@ class Configure:
|
|||||||
)
|
)
|
||||||
elif name.endswith("_shape_built"):
|
elif name.endswith("_shape_built"):
|
||||||
base_name = name[:-6]
|
base_name = name[:-6]
|
||||||
raw_bin_path = self.resolve_asset_path(
|
raw_bin_path = self.resolve_asset_path(f"assets/x/mapfs/geom/{base_name}.bin")
|
||||||
f"assets/x/mapfs/geom/{base_name}.bin"
|
|
||||||
)
|
|
||||||
bin_path = bin_path.parent / "geom" / (base_name + ".bin")
|
bin_path = bin_path.parent / "geom" / (base_name + ".bin")
|
||||||
|
|
||||||
if c_maps:
|
if c_maps:
|
||||||
# raw bin -> c -> o -> elf -> objcopy -> final bin file
|
# raw bin -> c -> o -> elf -> objcopy -> final bin file
|
||||||
c_file_path = (
|
c_file_path = (bin_path.parent / "geom" / base_name).with_suffix(".c")
|
||||||
bin_path.parent / "geom" / base_name
|
|
||||||
).with_suffix(".c")
|
|
||||||
o_path = bin_path.parent / "geom" / (base_name + ".o")
|
o_path = bin_path.parent / "geom" / (base_name + ".o")
|
||||||
elf_path = bin_path.parent / "geom" / (base_name + ".elf")
|
elf_path = bin_path.parent / "geom" / (base_name + ".elf")
|
||||||
|
|
||||||
@ -1056,12 +1014,7 @@ class Configure:
|
|||||||
rasters = []
|
rasters = []
|
||||||
|
|
||||||
for src_path in entry.src_paths:
|
for src_path in entry.src_paths:
|
||||||
out_path = (
|
out_path = self.build_path() / seg.dir / seg.name / (src_path.stem + ".bin")
|
||||||
self.build_path()
|
|
||||||
/ seg.dir
|
|
||||||
/ seg.name
|
|
||||||
/ (src_path.stem + ".bin")
|
|
||||||
)
|
|
||||||
build(
|
build(
|
||||||
out_path,
|
out_path,
|
||||||
[src_path],
|
[src_path],
|
||||||
@ -1079,13 +1032,7 @@ class Configure:
|
|||||||
palettes = []
|
palettes = []
|
||||||
|
|
||||||
for src_path in entry.src_paths:
|
for src_path in entry.src_paths:
|
||||||
out_path = (
|
out_path = self.build_path() / seg.dir / seg.name / "palette" / (src_path.stem + ".bin")
|
||||||
self.build_path()
|
|
||||||
/ seg.dir
|
|
||||||
/ seg.name
|
|
||||||
/ "palette"
|
|
||||||
/ (src_path.stem + ".bin")
|
|
||||||
)
|
|
||||||
build(
|
build(
|
||||||
out_path,
|
out_path,
|
||||||
[src_path],
|
[src_path],
|
||||||
@ -1100,9 +1047,7 @@ class Configure:
|
|||||||
build(entry.object_path.with_suffix(""), palettes, "charset_palettes")
|
build(entry.object_path.with_suffix(""), palettes, "charset_palettes")
|
||||||
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
||||||
elif seg.type == "pm_sprite_shading_profiles":
|
elif seg.type == "pm_sprite_shading_profiles":
|
||||||
header_path = str(
|
header_path = str(self.build_path() / "include/sprite/sprite_shading_profiles.h")
|
||||||
self.build_path() / "include/sprite/sprite_shading_profiles.h"
|
|
||||||
)
|
|
||||||
build(
|
build(
|
||||||
entry.object_path.with_suffix(""),
|
entry.object_path.with_suffix(""),
|
||||||
entry.src_paths,
|
entry.src_paths,
|
||||||
@ -1115,14 +1060,12 @@ class Configure:
|
|||||||
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
||||||
elif seg.type == "pm_sbn":
|
elif seg.type == "pm_sbn":
|
||||||
sbn_path = entry.object_path.with_suffix("")
|
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")
|
build(entry.object_path, [sbn_path], "bin")
|
||||||
elif seg.type == "linker" or seg.type == "linker_offset":
|
elif seg.type == "linker" or seg.type == "linker_offset":
|
||||||
pass
|
pass
|
||||||
elif seg.type == "pm_imgfx_data":
|
elif seg.type == "pm_imgfx_data":
|
||||||
c_file_path = (
|
c_file_path = Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c")
|
||||||
Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c")
|
|
||||||
)
|
|
||||||
build(c_file_path, entry.src_paths, "imgfx_data")
|
build(c_file_path, entry.src_paths, "imgfx_data")
|
||||||
|
|
||||||
build(
|
build(
|
||||||
@ -1136,9 +1079,7 @@ class Configure:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(f"don't know how to build {seg.__class__.__name__} '{seg.name}'")
|
||||||
f"don't know how to build {seg.__class__.__name__} '{seg.name}'"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run undefined_syms through cpp
|
# Run undefined_syms through cpp
|
||||||
ninja.build(
|
ninja.build(
|
||||||
@ -1216,20 +1157,14 @@ if __name__ == "__main__":
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Delete assets and previously-built files",
|
help="Delete assets and previously-built files",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument("--splat", default="tools/splat", help="Path to splat tool to use")
|
||||||
"--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(
|
|
||||||
"--split-code", action="store_true", help="Re-split code segments to asm files"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--no-split-assets",
|
"--no-split-assets",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Don't split assets from the baserom(s)",
|
help="Don't split assets from the baserom(s)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument("-d", "--debug", action="store_true", help="Generate debugging information")
|
||||||
"-d", "--debug", action="store_true", help="Generate debugging information"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-n",
|
"-n",
|
||||||
"--non-matching",
|
"--non-matching",
|
||||||
@ -1272,12 +1207,8 @@ if __name__ == "__main__":
|
|||||||
pass
|
pass
|
||||||
if args.cpp is None:
|
if args.cpp is None:
|
||||||
print("error: system C preprocessor is not GNU!")
|
print("error: system C preprocessor is not GNU!")
|
||||||
print(
|
print("This is a known issue on macOS - only clang's cpp is installed by default.")
|
||||||
"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(
|
|
||||||
"Use 'brew' to obtain GNU cpp, then run this script again with the --cpp option, e.g."
|
|
||||||
)
|
|
||||||
print(f" ./configure --cpp {gcc_cpps[0]}")
|
print(f" ./configure --cpp {gcc_cpps[0]}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@ -1285,15 +1216,11 @@ if __name__ == "__main__":
|
|||||||
version = exec_shell([PIGMENT, "--version"]).split(" ")[1].strip()
|
version = exec_shell([PIGMENT, "--version"]).split(" ")[1].strip()
|
||||||
|
|
||||||
if version < PIGMENT_REQ_VERSION:
|
if version < PIGMENT_REQ_VERSION:
|
||||||
print(
|
print(f"error: {PIGMENT} version {PIGMENT_REQ_VERSION} or newer is required, system version is {version}\n")
|
||||||
f"error: {PIGMENT} version {PIGMENT_REQ_VERSION} or newer is required, system version is {version}\n"
|
|
||||||
)
|
|
||||||
exit(1)
|
exit(1)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"error: {PIGMENT} is not installed\n")
|
print(f"error: {PIGMENT} is not installed\n")
|
||||||
print(
|
print("To build and install it, obtain cargo:\n\tcurl https://sh.rustup.rs -sSf | sh")
|
||||||
"To build and install it, obtain cargo:\n\tcurl https://sh.rustup.rs -sSf | sh"
|
|
||||||
)
|
|
||||||
print(f"and then run:\n\tcargo install {PIGMENT}")
|
print(f"and then run:\n\tcargo install {PIGMENT}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@ -1382,12 +1309,8 @@ if __name__ == "__main__":
|
|||||||
# include tools/splat_ext in the python path
|
# include tools/splat_ext in the python path
|
||||||
sys.path.append(str((ROOT / "tools/splat_ext").resolve()))
|
sys.path.append(str((ROOT / "tools/splat_ext").resolve()))
|
||||||
|
|
||||||
configure.split(
|
configure.split(not args.no_split_assets, args.split_code, args.shift, args.debug)
|
||||||
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.write_ninja(
|
|
||||||
ninja, skip_files, non_matching, args.modern_gcc, args.c_maps
|
|
||||||
)
|
|
||||||
|
|
||||||
all_rom_oks.append(str(configure.rom_ok_path()))
|
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__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Builds effect table, function declarations, macros, and enum")
|
||||||
description="Builds effect table, function declarations, macros, and enum"
|
|
||||||
)
|
|
||||||
parser.add_argument("in_yaml")
|
parser.add_argument("in_yaml")
|
||||||
parser.add_argument("out_dir", type=Path)
|
parser.add_argument("out_dir", type=Path)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -31,9 +29,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
effect_enum_text += f" {enum_name} = 0x{i:02X},\n"
|
effect_enum_text += f" {enum_name} = 0x{i:02X},\n"
|
||||||
if not effect.empty:
|
if not effect.empty:
|
||||||
effect_table_text += (
|
effect_table_text += f" FX_ENTRY({effect.name}, effect_gfx_{effect.gfx}),\n"
|
||||||
f" FX_ENTRY({effect.name}, effect_gfx_{effect.gfx}),\n"
|
|
||||||
)
|
|
||||||
fx_decls_text += effect.get_macro_call("fx_" + effect.name) + ";\n"
|
fx_decls_text += effect.get_macro_call("fx_" + effect.name) + ";\n"
|
||||||
main_decls_text += effect.get_macro_call(effect.name + "_main") + ";\n"
|
main_decls_text += effect.get_macro_call(effect.name + "_main") + ";\n"
|
||||||
macro_defs += effect.get_macro_def() + "\n"
|
macro_defs += effect.get_macro_def() + "\n"
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import sys, os
|
import sys, os
|
||||||
|
|
||||||
#Under normal compilation we rely on splat to use a discard option in the ldscript
|
# 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 not include sections in the elf then just output all sections, however under debug we want
|
||||||
#to have debug sections.
|
# to have debug sections.
|
||||||
#In debugging mode splat is told to output a list of sections it is custom creating, which are
|
# 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
|
# 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
|
# 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
|
# 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.
|
# the required sections to the .z64 without outputting everything.
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
infile, outfile = sys.argv[1:]
|
infile, outfile = sys.argv[1:]
|
||||||
|
|
||||||
#generate output based on input
|
# generate output based on input
|
||||||
file_data = open(infile,"r").read().split("\n")
|
file_data = open(infile, "r").read().split("\n")
|
||||||
if len(file_data[-1]) == 0:
|
if len(file_data[-1]) == 0:
|
||||||
file_data.pop()
|
file_data.pop()
|
||||||
|
|
||||||
outdata = "-j " + " -j ".join(file_data)
|
outdata = "-j " + " -j ".join(file_data)
|
||||||
with open(outfile, "w") as f:
|
with open(outfile, "w") as f:
|
||||||
f.write(outdata)
|
f.write(outdata)
|
||||||
|
@ -221,12 +221,8 @@ class Converter:
|
|||||||
|
|
||||||
# header (struct BackgroundHeader)
|
# header (struct BackgroundHeader)
|
||||||
for i, palette in enumerate(palettes):
|
for i, palette in enumerate(palettes):
|
||||||
out_bytes += (baseaddr + palettes_len + headers_len).to_bytes(
|
out_bytes += (baseaddr + palettes_len + headers_len).to_bytes(4, byteorder="big") # raster offset
|
||||||
4, byteorder="big"
|
out_bytes += (baseaddr + headers_len + 0x200 * i).to_bytes(4, byteorder="big") # palette offset
|
||||||
) # 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 += (12).to_bytes(2, byteorder="big") # startX
|
||||||
out_bytes += (20).to_bytes(2, byteorder="big") # startY
|
out_bytes += (20).to_bytes(2, byteorder="big") # startY
|
||||||
out_bytes += (out_width).to_bytes(2, byteorder="big") # width
|
out_bytes += (out_width).to_bytes(2, byteorder="big") # width
|
||||||
@ -263,8 +259,6 @@ if __name__ == "__main__":
|
|||||||
flip_x = "--flip-x" in argv
|
flip_x = "--flip-x" in argv
|
||||||
flip_y = "--flip-y" in argv
|
flip_y = "--flip-y" in argv
|
||||||
|
|
||||||
(out_bytes, out_width, out_height) = Converter(
|
(out_bytes, out_width, out_height) = Converter(mode, infile, flip_x, flip_y).convert()
|
||||||
mode, infile, flip_x, flip_y
|
|
||||||
).convert()
|
|
||||||
with open(argv[3], "wb") as f:
|
with open(argv[3], "wb") as f:
|
||||||
f.write(out_bytes)
|
f.write(out_bytes)
|
||||||
|
@ -6,6 +6,7 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Vertex:
|
class Vertex:
|
||||||
idx: int
|
idx: int
|
||||||
@ -20,7 +21,16 @@ class Vertex:
|
|||||||
a: int
|
a: int
|
||||||
|
|
||||||
def toJSON(self):
|
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
|
@dataclass
|
||||||
class Triangle:
|
class Triangle:
|
||||||
@ -31,6 +41,7 @@ class Triangle:
|
|||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return f" [{self.i}, {self.j}, {self.k}]"
|
return f" [{self.i}, {self.j}, {self.k}]"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Anim:
|
class Anim:
|
||||||
name: str
|
name: str
|
||||||
@ -46,13 +57,15 @@ class Anim:
|
|||||||
triangles: List[Triangle]
|
triangles: List[Triangle]
|
||||||
|
|
||||||
def toJSON(self):
|
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])
|
trianglestr = ",\n".join([t.toJSON() for t in self.triangles])
|
||||||
|
|
||||||
ret = "{\n"
|
ret = "{\n"
|
||||||
ret += " \"flags\": " + str(self.flags) + ",\n"
|
ret += ' "flags": ' + str(self.flags) + ",\n"
|
||||||
ret += " \"frames\": [\n" + framestr + "],\n"
|
ret += ' "frames": [\n' + framestr + "],\n"
|
||||||
ret += " \"triangles\": [\n" + trianglestr + "]\n"
|
ret += ' "triangles": [\n' + trianglestr + "]\n"
|
||||||
ret += "}"
|
ret += "}"
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
@ -60,7 +73,10 @@ class Anim:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def fromJSON(name: str, data: Any) -> "Anim":
|
def fromJSON(name: str, data: Any) -> "Anim":
|
||||||
flags = data["flags"]
|
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"]]
|
triangles = [Triangle(*t) for t in data["triangles"]]
|
||||||
|
|
||||||
return Anim(
|
return Anim(
|
||||||
@ -73,15 +89,16 @@ class Anim:
|
|||||||
keyframes=len(frames),
|
keyframes=len(frames),
|
||||||
flags=flags,
|
flags=flags,
|
||||||
frames=frames,
|
frames=frames,
|
||||||
triangles=triangles
|
triangles=triangles,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def build(inputs: List[Path], output: Path):
|
def build(inputs: List[Path], output: Path):
|
||||||
with open(output, "w") as f:
|
with open(output, "w") as f:
|
||||||
f.write("/* NOTE: This file is autogenerated, do not edit */\n\n")
|
f.write("/* NOTE: This file is autogenerated, do not edit */\n\n")
|
||||||
f.write("#include \"PR/gbi.h\"\n")
|
f.write('#include "PR/gbi.h"\n')
|
||||||
f.write("#include \"macros.h\"\n")
|
f.write('#include "macros.h"\n')
|
||||||
f.write("#include \"imgfx.h\"\n\n")
|
f.write('#include "imgfx.h"\n\n')
|
||||||
|
|
||||||
for input in inputs:
|
for input in inputs:
|
||||||
with open(input, "r") as fin:
|
with open(input, "r") as fin:
|
||||||
@ -101,7 +118,9 @@ def build(inputs: List[Path], output: Path):
|
|||||||
for frame in anim.frames:
|
for frame in anim.frames:
|
||||||
f.write(" {\n")
|
f.write(" {\n")
|
||||||
for vtx in frame:
|
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")
|
||||||
f.write("};\n\n")
|
f.write("};\n\n")
|
||||||
|
|
||||||
@ -134,7 +153,9 @@ def build(inputs: List[Path], output: Path):
|
|||||||
|
|
||||||
# We need a new chunk
|
# We need a new chunk
|
||||||
if max_t1 >= 32 and not just_chunked:
|
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
|
just_chunked = True
|
||||||
f.write(chunk_text)
|
f.write(chunk_text)
|
||||||
chunk_text = ""
|
chunk_text = ""
|
||||||
@ -155,7 +176,9 @@ def build(inputs: List[Path], output: Path):
|
|||||||
old_max_t = max(max_t1, max_t2)
|
old_max_t = max(max_t1, max_t2)
|
||||||
|
|
||||||
# Dump final chunk
|
# 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(chunk_text)
|
||||||
f.write(" gsSPEndDisplayList(),\n")
|
f.write(" gsSPEndDisplayList(),\n")
|
||||||
f.write("};\n\n")
|
f.write("};\n\n")
|
||||||
|
@ -20,7 +20,7 @@ if __name__ == "__main__":
|
|||||||
mode = sys.argv[2]
|
mode = sys.argv[2]
|
||||||
|
|
||||||
syms_to_max = {
|
syms_to_max = {
|
||||||
"entity_data_vram_end" : [
|
"entity_data_vram_end": [
|
||||||
"entity_default_VRAM_END",
|
"entity_default_VRAM_END",
|
||||||
"entity_jan_iwa_VRAM_END",
|
"entity_jan_iwa_VRAM_END",
|
||||||
"entity_sbk_omo_VRAM_END",
|
"entity_sbk_omo_VRAM_END",
|
||||||
@ -44,7 +44,7 @@ if __name__ == "__main__":
|
|||||||
"world_action_use_spinning_flower_VRAM_END",
|
"world_action_use_spinning_flower_VRAM_END",
|
||||||
"world_action_use_tweester_VRAM_END",
|
"world_action_use_tweester_VRAM_END",
|
||||||
"world_action_sneaky_parasol_VRAM_END",
|
"world_action_sneaky_parasol_VRAM_END",
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs: Dict[str, List[int]] = {}
|
addrs: Dict[str, List[int]] = {}
|
||||||
@ -80,7 +80,9 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
out_addrs = {sym: max(addrs[sym]) for sym in addrs}
|
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 = ""
|
out = ""
|
||||||
for sym in out_addrs:
|
for sym in out_addrs:
|
||||||
|
@ -4,9 +4,11 @@ from sys import argv
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
|
||||||
def next_multiple(pos, multiple):
|
def next_multiple(pos, multiple):
|
||||||
return pos + pos % multiple
|
return pos + pos % multiple
|
||||||
|
|
||||||
|
|
||||||
def get_version_date(version):
|
def get_version_date(version):
|
||||||
if version == "us":
|
if version == "us":
|
||||||
return "Map Ver.00/11/07 15:36"
|
return "Map Ver.00/11/07 15:36"
|
||||||
@ -17,6 +19,7 @@ def get_version_date(version):
|
|||||||
else:
|
else:
|
||||||
return "Map Ver.??/??/?? ??:??"
|
return "Map Ver.??/??/?? ??:??"
|
||||||
|
|
||||||
|
|
||||||
def build_mapfs(out_bin, assets, version):
|
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.
|
# 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
|
# 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
|
decompressed_size = decompressed.stat().st_size
|
||||||
size = next_multiple(compressed.stat().st_size, 2) if compressed.exists() else decompressed_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
|
# write all previously-written names; required to match
|
||||||
lastname = name + lastname[len(name):]
|
lastname = name + lastname[len(name) :]
|
||||||
f.seek(toc_entry_pos)
|
f.seek(toc_entry_pos)
|
||||||
f.write(lastname.encode('ascii'))
|
f.write(lastname.encode("ascii"))
|
||||||
|
|
||||||
# write TOC entry.
|
# write TOC entry.
|
||||||
f.seek(toc_entry_pos + 0x10)
|
f.seek(toc_entry_pos + 0x10)
|
||||||
@ -61,14 +64,15 @@ def build_mapfs(out_bin, assets, version):
|
|||||||
|
|
||||||
last_name_entry = "end_data\0"
|
last_name_entry = "end_data\0"
|
||||||
f.seek(toc_entry_pos)
|
f.seek(toc_entry_pos)
|
||||||
lastname = last_name_entry + lastname[len(last_name_entry):]
|
lastname = last_name_entry + lastname[len(last_name_entry) :]
|
||||||
f.write(lastname.encode('ascii'))
|
f.write(lastname.encode("ascii"))
|
||||||
|
|
||||||
f.seek(toc_entry_pos + 0x18)
|
f.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__":
|
if __name__ == "__main__":
|
||||||
argv.pop(0) # python3
|
argv.pop(0) # python3
|
||||||
version = argv.pop(0)
|
version = argv.pop(0)
|
||||||
out = argv.pop(0)
|
out = argv.pop(0)
|
||||||
|
|
||||||
@ -76,6 +80,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# pairs
|
# pairs
|
||||||
for i in range(0, len(argv), 2):
|
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)
|
build_mapfs(out, assets, version)
|
||||||
|
@ -4,17 +4,19 @@ from sys import argv, stderr
|
|||||||
from os import path
|
from os import path
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
|
|
||||||
|
|
||||||
def eprint(*args, **kwargs):
|
def eprint(*args, **kwargs):
|
||||||
print(*args, file=stderr, **kwargs)
|
print(*args, file=stderr, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
_, xml_path = argv
|
_, xml_path = argv
|
||||||
xml = parse(xml_path)
|
xml = parse(xml_path)
|
||||||
|
|
||||||
map_name = path.basename(xml_path)[:-4]
|
map_name = path.basename(xml_path)[:-4]
|
||||||
|
|
||||||
print("#include \"common.h\"")
|
print('#include "common.h"')
|
||||||
print("#include \"map.h\"")
|
print('#include "map.h"')
|
||||||
print("")
|
print("")
|
||||||
print("#ifndef NAMESPACE")
|
print("#ifndef NAMESPACE")
|
||||||
print(f"#define NAMESPACE {map_name}")
|
print(f"#define NAMESPACE {map_name}")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from sys import argv
|
from sys import argv
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
argv.pop(0) # python3
|
argv.pop(0) # python3
|
||||||
|
|
||||||
if len(argv) > 4:
|
if len(argv) > 4:
|
||||||
out, img1, img2, img3, img2_pal = argv
|
out, img1, img2, img3, img2_pal = argv
|
||||||
|
@ -90,27 +90,17 @@ class HeaderSegment(Segment):
|
|||||||
# note: do not push model root yet
|
# note: do not push model root yet
|
||||||
shape.root_node = NodeSegment(self.ptr_root_node, "Node")
|
shape.root_node = NodeSegment(self.ptr_root_node, "Node")
|
||||||
|
|
||||||
shape.vtx_table = shape.push(
|
shape.vtx_table = shape.push(VertexTableSegment(self.ptr_vtx_table, "VertexTable"))
|
||||||
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.model_names = shape.push(
|
shape.zone_names = shape.push(StringListSegment(self.ptr_zone_names, "ZoneNames"))
|
||||||
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):
|
def print(self, shape):
|
||||||
shape.print(f"ShapeFileHeader {self.get_sym()} = {{")
|
shape.print(f"ShapeFileHeader {self.get_sym()} = {{")
|
||||||
shape.print(f" .root = &{shape.get_symbol(self.ptr_root_node)},")
|
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" .vertexTable = {shape.get_symbol(self.ptr_vtx_table)},")
|
||||||
shape.print(f" .modelNames = {shape.get_symbol(self.ptr_model_names)},")
|
shape.print(f" .modelNames = {shape.get_symbol(self.ptr_model_names)},")
|
||||||
shape.print(
|
shape.print(f" .colliderNames = {shape.get_symbol(self.ptr_collider_names)},")
|
||||||
f" .colliderNames = {shape.get_symbol(self.ptr_collider_names)},"
|
|
||||||
)
|
|
||||||
if self.ptr_zone_names != 0:
|
if self.ptr_zone_names != 0:
|
||||||
shape.print(f" .zoneNames = {shape.get_symbol(self.ptr_zone_names)},")
|
shape.print(f" .zoneNames = {shape.get_symbol(self.ptr_zone_names)},")
|
||||||
shape.print("};")
|
shape.print("};")
|
||||||
@ -216,9 +206,7 @@ class NodeSegment(Segment):
|
|||||||
|
|
||||||
self.model_name = shape.model_name_map[self.addr]
|
self.model_name = shape.model_name_map[self.addr]
|
||||||
shape.push(GroupDataSegment(self.ptr_group_data, "GroupData", self.model_name))
|
shape.push(GroupDataSegment(self.ptr_group_data, "GroupData", self.model_name))
|
||||||
shape.push(
|
shape.push(DisplayDataSegment(self.ptr_display_data, "DisplayData", self.model_name))
|
||||||
DisplayDataSegment(self.ptr_display_data, "DisplayData", self.model_name)
|
|
||||||
)
|
|
||||||
shape.push(
|
shape.push(
|
||||||
PropertyListSegment(
|
PropertyListSegment(
|
||||||
self.ptr_property_list,
|
self.ptr_property_list,
|
||||||
@ -284,37 +272,25 @@ class PropertyListSegment(Segment):
|
|||||||
|
|
||||||
if key == 0x5E:
|
if key == 0x5E:
|
||||||
if value == 0:
|
if value == 0:
|
||||||
shape.print(
|
shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = NULL }}}},")
|
||||||
f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = NULL }}}},"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
tex_name = read_ascii_string(shape.file_bytes, value)
|
tex_name = read_ascii_string(shape.file_bytes, value)
|
||||||
shape.print(
|
shape.print(f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{tex_name}" }}}},')
|
||||||
f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{tex_name}" }}}},'
|
|
||||||
)
|
|
||||||
elif key == 0x5F:
|
elif key == 0x5F:
|
||||||
shape.print(
|
shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}},")
|
||||||
f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}},"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if fmt == 0: # int
|
if fmt == 0: # int
|
||||||
shape.print(
|
shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}},")
|
||||||
f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .s = {hex(value)} }}}},"
|
|
||||||
)
|
|
||||||
elif fmt == 1: # float
|
elif fmt == 1: # float
|
||||||
temp = struct.pack(">I", value)
|
temp = struct.pack(">I", value)
|
||||||
(f,) = struct.unpack(">f", temp)
|
(f,) = struct.unpack(">f", temp)
|
||||||
shape.print(
|
shape.print(f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .f = {f} }}}},")
|
||||||
f" {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .f = {f} }}}},"
|
|
||||||
)
|
|
||||||
elif fmt == 2: # pointer
|
elif fmt == 2: # pointer
|
||||||
shape.print(
|
shape.print(
|
||||||
f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{shape.get_symbol(value)}" }}}},'
|
f' {{ .key = {hex(key)}, .dataType = {fmt}, .data = {{ .p = "{shape.get_symbol(value)}" }}}},'
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(f"Invalid property: 0x{key:08X} 0x{fmt:08X} 0x{value:08X}")
|
||||||
f"Invalid property: 0x{key:08X} 0x{fmt:08X} 0x{value:08X}"
|
|
||||||
)
|
|
||||||
|
|
||||||
shape.print("};")
|
shape.print("};")
|
||||||
|
|
||||||
@ -334,25 +310,15 @@ class GroupDataSegment(Segment):
|
|||||||
self.ptr_children,
|
self.ptr_children,
|
||||||
) = struct.unpack(">IIIII", shape.file_bytes[start : start + 20])
|
) = struct.unpack(">IIIII", shape.file_bytes[start : start + 20])
|
||||||
|
|
||||||
shape.push(
|
shape.push(NodeListSegment(self.ptr_children, "Children", self.model_name, self.num_children))
|
||||||
NodeListSegment(
|
shape.push(LightSetSegment(self.ptr_lights, "Lights", self.model_name, self.num_lights))
|
||||||
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))
|
shape.push(MatrixSegment(self.ptr_transform_mtx, "Mtx", self.model_name))
|
||||||
|
|
||||||
def print(self, shape):
|
def print(self, shape):
|
||||||
shape.print(f"ModelGroupData {self.get_sym()} = {{")
|
shape.print(f"ModelGroupData {self.get_sym()} = {{")
|
||||||
if self.ptr_transform_mtx != 0:
|
if self.ptr_transform_mtx != 0:
|
||||||
shape.print(
|
shape.print(f" .transformMatrix = (Mtx*) &{shape.get_symbol(self.ptr_transform_mtx)},")
|
||||||
f" .transformMatrix = (Mtx*) &{shape.get_symbol(self.ptr_transform_mtx)},"
|
shape.print(f" .lightingGroup = (Lightsn*) &{shape.get_symbol(self.ptr_lights)},")
|
||||||
)
|
|
||||||
shape.print(
|
|
||||||
f" .lightingGroup = (Lightsn*) &{shape.get_symbol(self.ptr_lights)},"
|
|
||||||
)
|
|
||||||
shape.print(f" .numLights = {self.num_lights},")
|
shape.print(f" .numLights = {self.num_lights},")
|
||||||
shape.print(f" .childList = {shape.get_symbol(self.ptr_children)},")
|
shape.print(f" .childList = {shape.get_symbol(self.ptr_children)},")
|
||||||
shape.print(f" .numChildren = {self.num_children},")
|
shape.print(f" .numChildren = {self.num_children},")
|
||||||
@ -392,18 +358,14 @@ class MatrixSegment(Segment):
|
|||||||
for i in range(4):
|
for i in range(4):
|
||||||
(a, b, c, d) = struct.unpack(">hhhh", shape.file_bytes[pos : pos + 8])
|
(a, b, c, d) = struct.unpack(">hhhh", shape.file_bytes[pos : pos + 8])
|
||||||
pos += 8
|
pos += 8
|
||||||
shape.print(
|
shape.print(f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }},")
|
||||||
f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }},"
|
|
||||||
)
|
|
||||||
shape.print(" },")
|
shape.print(" },")
|
||||||
|
|
||||||
shape.print(" .frac = {")
|
shape.print(" .frac = {")
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
(a, b, c, d) = struct.unpack(">hhhh", shape.file_bytes[pos : pos + 8])
|
(a, b, c, d) = struct.unpack(">hhhh", shape.file_bytes[pos : pos + 8])
|
||||||
pos += 8
|
pos += 8
|
||||||
shape.print(
|
shape.print(f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }},")
|
||||||
f" {{ {hex(a):4}, {hex(b):4}, {hex(c):4}, {hex(d):4} }},"
|
|
||||||
)
|
|
||||||
shape.print(" },")
|
shape.print(" },")
|
||||||
|
|
||||||
shape.print("};")
|
shape.print("};")
|
||||||
@ -416,13 +378,9 @@ class DisplayDataSegment(Segment):
|
|||||||
|
|
||||||
def scan(self, shape):
|
def scan(self, shape):
|
||||||
start = self.addr - BASE_ADDR
|
start = self.addr - BASE_ADDR
|
||||||
(self.ptr_display_list,) = struct.unpack(
|
(self.ptr_display_list,) = struct.unpack(">I", shape.file_bytes[start : start + 4])
|
||||||
">I", shape.file_bytes[start : start + 4]
|
|
||||||
)
|
|
||||||
|
|
||||||
gfx_segment = shape.push(
|
gfx_segment = shape.push(DisplayListSegment(self.ptr_display_list, "Gfx", self.model_name))
|
||||||
DisplayListSegment(self.ptr_display_list, "Gfx", self.model_name)
|
|
||||||
)
|
|
||||||
# Gfx segments may have been already visited during root Gfx traversal
|
# 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
|
# so we will now force the associated model name to be the current model
|
||||||
gfx_segment.model_name = self.model_name
|
gfx_segment.model_name = self.model_name
|
||||||
@ -449,15 +407,9 @@ class DisplayListSegment(Segment):
|
|||||||
if op == GFX_END_DL:
|
if op == GFX_END_DL:
|
||||||
break
|
break
|
||||||
elif op == GFX_START_DL:
|
elif op == GFX_START_DL:
|
||||||
shape.push(
|
shape.push(DisplayListSegment(w2, f"Gfx_{hex(w2)[2:].upper()}", self.model_name))
|
||||||
DisplayListSegment(
|
|
||||||
w2, f"Gfx_{hex(w2)[2:].upper()}", self.model_name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif op == GFX_LOAD_MATRIX:
|
elif op == GFX_LOAD_MATRIX:
|
||||||
shape.push(
|
shape.push(MatrixSegment(w2, f"Mtx_{hex(w2)[2:]}", model_name=self.model_name))
|
||||||
MatrixSegment(w2, f"Mtx_{hex(w2)[2:]}", model_name=self.model_name)
|
|
||||||
)
|
|
||||||
elif op == GFX_LOAD_VTX:
|
elif op == GFX_LOAD_VTX:
|
||||||
num = (w1 >> 12) & 0xFFF
|
num = (w1 >> 12) & 0xFFF
|
||||||
idx = (w2 - shape.vtx_table.addr) // 0x10
|
idx = (w2 - shape.vtx_table.addr) // 0x10
|
||||||
@ -492,9 +444,7 @@ class DisplayListSegment(Segment):
|
|||||||
end = (w1 & 0x00000FFF) // 2
|
end = (w1 & 0x00000FFF) // 2
|
||||||
buf_pos = end - num
|
buf_pos = end - num
|
||||||
index = (w2 - shape.vtx_table.addr) // 0x10
|
index = (w2 - shape.vtx_table.addr) // 0x10
|
||||||
shape.print(
|
shape.print(f" gsSPVertex(&{shape.vtx_table.get_sym()}[{index}], {num}, {buf_pos}),")
|
||||||
f" gsSPVertex(&{shape.vtx_table.get_sym()}[{index}], {num}, {buf_pos}),"
|
|
||||||
)
|
|
||||||
elif op == GFX_DRAW_TRI:
|
elif op == GFX_DRAW_TRI:
|
||||||
i = (w1 & 0x00FF0000) >> 16
|
i = (w1 & 0x00FF0000) >> 16
|
||||||
j = (w1 & 0x0000FF00) >> 8
|
j = (w1 & 0x0000FF00) >> 8
|
||||||
@ -507,9 +457,7 @@ class DisplayListSegment(Segment):
|
|||||||
d = (w2 & 0x00FF0000) >> 16
|
d = (w2 & 0x00FF0000) >> 16
|
||||||
e = (w2 & 0x0000FF00) >> 8
|
e = (w2 & 0x0000FF00) >> 8
|
||||||
f = w2 & 0x000000FF
|
f = w2 & 0x000000FF
|
||||||
shape.print(
|
shape.print(f" gsSP2Triangles({a // 2}, {b // 2}, {c // 2}, 0, {d // 2}, {e // 2}, {f // 2}, 0),")
|
||||||
f" gsSP2Triangles({a // 2}, {b // 2}, {c // 2}, 0, {d // 2}, {e // 2}, {f // 2}, 0),"
|
|
||||||
)
|
|
||||||
elif op == GFX_RDP_PIPE_SYNC:
|
elif op == GFX_RDP_PIPE_SYNC:
|
||||||
shape.print(" gsDPPipeSync(),")
|
shape.print(" gsDPPipeSync(),")
|
||||||
elif op == GFX_POP_MATRIX:
|
elif op == GFX_POP_MATRIX:
|
||||||
@ -522,9 +470,7 @@ class DisplayListSegment(Segment):
|
|||||||
flags = self.get_geometry_flags(~(w1 | 0xFF000000))
|
flags = self.get_geometry_flags(~(w1 | 0xFF000000))
|
||||||
shape.print(f" gsSPClearGeometryMode({flags}),")
|
shape.print(f" gsSPClearGeometryMode({flags}),")
|
||||||
elif op == GFX_LOAD_MATRIX:
|
elif op == GFX_LOAD_MATRIX:
|
||||||
shape.print(
|
shape.print(f" gsSPMatrix(&{shape.get_symbol(w2)}, G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW),")
|
||||||
f" gsSPMatrix(&{shape.get_symbol(w2)}, G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW),"
|
|
||||||
)
|
|
||||||
elif op == GFX_START_DL:
|
elif op == GFX_START_DL:
|
||||||
shape.print(f" gsSPDisplayList({shape.get_symbol(w2)}),")
|
shape.print(f" gsSPDisplayList({shape.get_symbol(w2)}),")
|
||||||
elif op == GFX_END_DL:
|
elif op == GFX_END_DL:
|
||||||
@ -610,9 +556,7 @@ class ShapeFile:
|
|||||||
child_start = ptr_children - BASE_ADDR
|
child_start = ptr_children - BASE_ADDR
|
||||||
|
|
||||||
for i in range(num_children):
|
for i in range(num_children):
|
||||||
(ptr_child,) = struct.unpack(
|
(ptr_child,) = struct.unpack(">I", self.file_bytes[child_start : child_start + 4])
|
||||||
">I", self.file_bytes[child_start : child_start + 4]
|
|
||||||
)
|
|
||||||
self.build_model_name_map(ptr_child, names)
|
self.build_model_name_map(ptr_child, names)
|
||||||
child_start += 4
|
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:
|
if main_data == None:
|
||||||
raise Exception(f"Texture {ret.img_name} has no definition for 'main'")
|
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_fmt_name, ret.main_hwrap, ret.main_vwrap) = ret.read_json_img(main_data, "main", ret.img_name)
|
||||||
main_data, "main", ret.img_name
|
|
||||||
)
|
|
||||||
(ret.main_fmt, ret.main_depth) = get_format_code(main_fmt_name)
|
(ret.main_fmt, ret.main_depth) = get_format_code(main_fmt_name)
|
||||||
|
|
||||||
# read main image
|
# read main image
|
||||||
img_path = get_asset_path(
|
img_path = get_asset_path(Path(f"mapfs/tex/{tex_name}/{ret.img_name}.png"), asset_stack)
|
||||||
Path(f"mapfs/tex/{tex_name}/{ret.img_name}.png"), asset_stack
|
|
||||||
)
|
|
||||||
if not os.path.isfile(img_path):
|
if not os.path.isfile(img_path):
|
||||||
raise Exception(f"Could not find main image for texture: {ret.img_name}")
|
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
|
ret.has_aux = "aux" in json_data
|
||||||
if ret.has_aux:
|
if ret.has_aux:
|
||||||
aux_data = json_data.get("aux")
|
aux_data = json_data.get("aux")
|
||||||
(aux_fmt_name, ret.aux_hwrap, ret.aux_vwrap) = ret.read_json_img(
|
(aux_fmt_name, ret.aux_hwrap, ret.aux_vwrap) = ret.read_json_img(aux_data, "aux", ret.img_name)
|
||||||
aux_data, "aux", ret.img_name
|
|
||||||
)
|
|
||||||
|
|
||||||
if aux_fmt_name == "Shared":
|
if aux_fmt_name == "Shared":
|
||||||
# aux tiles have blank attributes in SHARED mode
|
# 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
|
ret.extra_tiles = TILES_INDEPENDENT_AUX
|
||||||
|
|
||||||
# read aux image
|
# read aux image
|
||||||
img_path = get_asset_path(
|
img_path = get_asset_path(Path(f"mapfs/tex/{tex_name}/{ret.img_name}_AUX.png"), asset_stack)
|
||||||
Path(f"mapfs/tex/{tex_name}/{ret.img_name}_AUX.png"), asset_stack
|
|
||||||
)
|
|
||||||
if not os.path.isfile(img_path):
|
if not os.path.isfile(img_path):
|
||||||
raise Exception(f"Could not find AUX image for texture: {ret.img_name}")
|
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})"
|
f"Texture {ret.img_name} is missing mipmap level {mipmap_idx} (size = {mmw} x {mmh})"
|
||||||
)
|
)
|
||||||
|
|
||||||
(raster, pal, width, height) = ret.get_img_file(
|
(raster, pal, width, height) = ret.get_img_file(main_fmt_name, str(img_path))
|
||||||
main_fmt_name, str(img_path)
|
|
||||||
)
|
|
||||||
ret.mipmaps.append(raster)
|
ret.mipmaps.append(raster)
|
||||||
if width != mmw or height != mmh:
|
if width != mmw or height != mmh:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
@ -160,9 +150,7 @@ def img_from_json(json_data, tex_name: str, asset_stack: Tuple[Path, ...]) -> Te
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def build(
|
def build(out_path: Path, tex_name: str, asset_stack: Tuple[Path, ...], endian: str = "big"):
|
||||||
out_path: Path, tex_name: str, asset_stack: Tuple[Path, ...], endian: str = "big"
|
|
||||||
):
|
|
||||||
out_bytes = bytearray()
|
out_bytes = bytearray()
|
||||||
|
|
||||||
json_path = get_asset_path(Path(f"mapfs/tex/{tex_name}.json"), asset_stack)
|
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)
|
json_data = json.loads(json_str)
|
||||||
|
|
||||||
if len(json_data) > 128:
|
if len(json_data) > 128:
|
||||||
raise Exception(
|
raise Exception(f"Maximum number of textures (128) exceeded by {tex_name} ({len(json_data)})`")
|
||||||
f"Maximum number of textures (128) exceeded by {tex_name} ({len(json_data)})`"
|
|
||||||
)
|
|
||||||
|
|
||||||
for img_data in json_data:
|
for img_data in json_data:
|
||||||
img = img_from_json(img_data, tex_name, asset_stack)
|
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("bin_out", type=Path, help="Output binary file path")
|
||||||
parser.add_argument("name", help="Name of tex subdirectory")
|
parser.add_argument("name", help="Name of tex subdirectory")
|
||||||
parser.add_argument("asset_stack", help="comma-separated asset stack")
|
parser.add_argument("asset_stack", help="comma-separated asset stack")
|
||||||
parser.add_argument(
|
parser.add_argument("--endian", choices=["big", "little"], default="big", help="Output endianness")
|
||||||
"--endian", choices=["big", "little"], default="big", help="Output endianness"
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
asset_stack = tuple(Path(d) for d in args.asset_stack.split(","))
|
asset_stack = tuple(Path(d) for d in args.asset_stack.split(","))
|
||||||
|
@ -6,6 +6,7 @@ import re
|
|||||||
import msgpack
|
import msgpack
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
def __init__(self, d: dict, header_file_index: int):
|
def __init__(self, d: dict, header_file_index: int):
|
||||||
self.section = d.get("section")
|
self.section = d.get("section")
|
||||||
@ -14,6 +15,7 @@ class Message:
|
|||||||
self.bytes = d["bytes"]
|
self.bytes = d["bytes"]
|
||||||
self.header_file_index = header_file_index
|
self.header_file_index = header_file_index
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(argv) < 3:
|
if len(argv) < 3:
|
||||||
print("usage: combine.py [out.bin] [out.h] [compiled...]")
|
print("usage: combine.py [out.bin] [out.h] [compiled...]")
|
||||||
@ -22,7 +24,7 @@ if __name__ == "__main__":
|
|||||||
_, outfile, header_file, *infiles = argv
|
_, outfile, header_file, *infiles = argv
|
||||||
|
|
||||||
messages = []
|
messages = []
|
||||||
#header_files = []
|
# header_files = []
|
||||||
|
|
||||||
for i, infile in enumerate(infiles):
|
for i, infile in enumerate(infiles):
|
||||||
# if infile == "--headers":
|
# if infile == "--headers":
|
||||||
@ -34,12 +36,12 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
with open(outfile, "wb") as f:
|
with open(outfile, "wb") as f:
|
||||||
# sectioned+indexed, followed by just sectioned, followed by just indexed, followed by named (unsectioned & unindexed)
|
# 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()
|
names = set()
|
||||||
|
|
||||||
sections = []
|
sections = []
|
||||||
#messages_by_file = {}
|
# messages_by_file = {}
|
||||||
|
|
||||||
for message in messages:
|
for message in messages:
|
||||||
if message.section is None:
|
if message.section is None:
|
||||||
@ -64,10 +66,10 @@ if __name__ == "__main__":
|
|||||||
# else:
|
# else:
|
||||||
# names.add(message.name)
|
# names.add(message.name)
|
||||||
|
|
||||||
# if message.header_file_index in messages_by_file:
|
# if message.header_file_index in messages_by_file:
|
||||||
# messages_by_file[message.header_file_index].add(message)
|
# messages_by_file[message.header_file_index].add(message)
|
||||||
# else:
|
# else:
|
||||||
# messages_by_file[message.header_file_index] = set([message])
|
# messages_by_file[message.header_file_index] = set([message])
|
||||||
|
|
||||||
if message.index in section:
|
if message.index in section:
|
||||||
print(f"warning: multiple messages allocated to id {section_idx:02X}:{message.index:03X}")
|
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
|
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 = []
|
section_offsets = []
|
||||||
for section in sections:
|
for section in sections:
|
||||||
@ -97,21 +99,15 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# padding
|
# padding
|
||||||
while f.tell() % 0x10 != 0:
|
while f.tell() % 0x10 != 0:
|
||||||
f.write(b'\0\0\0\0')
|
f.write(b"\0\0\0\0")
|
||||||
|
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
for offset in section_offsets:
|
for offset in section_offsets:
|
||||||
f.write(offset.to_bytes(4, byteorder="big"))
|
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:
|
with open(header_file, "w") as f:
|
||||||
f.write(
|
f.write(f"#ifndef _MESSAGE_IDS_H_\n" f"#define _MESSAGE_IDS_H_\n" "\n" '#include "messages.h"\n' "\n")
|
||||||
f"#ifndef _MESSAGE_IDS_H_\n"
|
|
||||||
f"#define _MESSAGE_IDS_H_\n"
|
|
||||||
"\n"
|
|
||||||
'#include "messages.h"\n'
|
|
||||||
"\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
for message in messages:
|
for message in messages:
|
||||||
if message.name:
|
if message.name:
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
from sys import argv
|
from sys import argv
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import re
|
import re
|
||||||
import msgpack # way faster than pickle
|
import msgpack # way faster than pickle
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
def __init__(self, name, section, index):
|
def __init__(self, name, section, index):
|
||||||
@ -11,7 +12,8 @@ class Message:
|
|||||||
self.section = section
|
self.section = section
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
self.bytes = [] # XXX: bytearray would be better
|
self.bytes = [] # XXX: bytearray would be better
|
||||||
|
|
||||||
|
|
||||||
def try_convert_int(s):
|
def try_convert_int(s):
|
||||||
try:
|
try:
|
||||||
@ -19,10 +21,11 @@ def try_convert_int(s):
|
|||||||
except:
|
except:
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def parse_command(source):
|
def parse_command(source):
|
||||||
if source[0] != "[":
|
if source[0] != "[":
|
||||||
return None, [], {}, source
|
return None, [], {}, source
|
||||||
source = source[1:] # "["
|
source = source[1:] # "["
|
||||||
|
|
||||||
inside_brackets = ""
|
inside_brackets = ""
|
||||||
while source[0] != "]":
|
while source[0] != "]":
|
||||||
@ -31,7 +34,7 @@ def parse_command(source):
|
|||||||
|
|
||||||
inside_brackets += source[0]
|
inside_brackets += source[0]
|
||||||
source = source[1:]
|
source = source[1:]
|
||||||
source = source[1:] # "]"
|
source = source[1:] # "]"
|
||||||
|
|
||||||
command, *raw_args = inside_brackets.split(" ")
|
command, *raw_args = inside_brackets.split(" ")
|
||||||
|
|
||||||
@ -58,6 +61,7 @@ def parse_command(source):
|
|||||||
|
|
||||||
return command.lower(), args, named_args, source
|
return command.lower(), args, named_args, source
|
||||||
|
|
||||||
|
|
||||||
def color_to_code(color, style):
|
def color_to_code(color, style):
|
||||||
COLORS = {
|
COLORS = {
|
||||||
"diary": {
|
"diary": {
|
||||||
@ -90,26 +94,30 @@ def color_to_code(color, style):
|
|||||||
"red": 0x19,
|
"red": 0x19,
|
||||||
"blue": 0x1A,
|
"blue": 0x1A,
|
||||||
"green": 0x1B,
|
"green": 0x1B,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if type(color) is int:
|
if type(color) is int:
|
||||||
return color
|
return color
|
||||||
|
|
||||||
return COLORS.get(style, {
|
return COLORS.get(
|
||||||
# [style:left], [style:right]
|
style,
|
||||||
"normal": 0x0A,
|
{
|
||||||
"red": 0x20,
|
# [style:left], [style:right]
|
||||||
"pink": 0x21,
|
"normal": 0x0A,
|
||||||
"purple": 0x22,
|
"red": 0x20,
|
||||||
"blue": 0x23,
|
"pink": 0x21,
|
||||||
"cyan": 0x24,
|
"purple": 0x22,
|
||||||
"green": 0x25,
|
"blue": 0x23,
|
||||||
"yellow": 0x26,
|
"cyan": 0x24,
|
||||||
}).get(color)
|
"green": 0x25,
|
||||||
|
"yellow": 0x26,
|
||||||
|
},
|
||||||
|
).get(color)
|
||||||
|
|
||||||
|
|
||||||
CHARSET = {
|
CHARSET = {
|
||||||
#"𝅘𝅥𝅮": 0x00,
|
# "𝅘𝅥𝅮": 0x00,
|
||||||
"!": 0x01,
|
"!": 0x01,
|
||||||
'"': 0x02,
|
'"': 0x02,
|
||||||
"#": 0x03,
|
"#": 0x03,
|
||||||
@ -323,19 +331,22 @@ CHARSET_CREDITS = {
|
|||||||
" ": 0xF7,
|
" ": 0xF7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def strip_c_comments(text):
|
def strip_c_comments(text):
|
||||||
def replacer(match):
|
def replacer(match):
|
||||||
s = match.group(0)
|
s = match.group(0)
|
||||||
if s.startswith('/'):
|
if s.startswith("/"):
|
||||||
return " "
|
return " "
|
||||||
else:
|
else:
|
||||||
return s
|
return s
|
||||||
|
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
|
||||||
re.DOTALL | re.MULTILINE
|
re.DOTALL | re.MULTILINE,
|
||||||
)
|
)
|
||||||
return re.sub(pattern, replacer, text)
|
return re.sub(pattern, replacer, text)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(argv) < 3:
|
if len(argv) < 3:
|
||||||
print("usage: parse_compile.py [in.msg] [out.msgpack] [--c]")
|
print("usage: parse_compile.py [in.msg] [out.msgpack] [--c]")
|
||||||
@ -435,7 +446,7 @@ if __name__ == "__main__":
|
|||||||
print(f"{filename}:{lineno}: expected opening brace ('{{')")
|
print(f"{filename}:{lineno}: expected opening brace ('{{')")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
source = source[1:] # {
|
source = source[1:] # {
|
||||||
|
|
||||||
# count indent level
|
# count indent level
|
||||||
indent_level = 0
|
indent_level = 0
|
||||||
@ -484,8 +495,8 @@ if __name__ == "__main__":
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
message.bytes += [0xFF, 0x05, color]
|
message.bytes += [0xFF, 0x05, color]
|
||||||
#color_stack.append(color)
|
# color_stack.append(color)
|
||||||
#elif command == "/color":
|
# elif command == "/color":
|
||||||
# color_stack.pop()
|
# color_stack.pop()
|
||||||
# message.bytes += [0xFF, 0x05, color_stack[0]]
|
# message.bytes += [0xFF, 0x05, color_stack[0]]
|
||||||
elif command == "style":
|
elif command == "style":
|
||||||
@ -517,7 +528,13 @@ if __name__ == "__main__":
|
|||||||
print(f"{filename}:{lineno}: 'choice' style requires size=_,_")
|
print(f"{filename}:{lineno}: 'choice' style requires size=_,_")
|
||||||
exit(1)
|
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":
|
elif style == "inspect":
|
||||||
message.bytes += [0x06]
|
message.bytes += [0x06]
|
||||||
elif style == "sign":
|
elif style == "sign":
|
||||||
@ -553,7 +570,13 @@ if __name__ == "__main__":
|
|||||||
print(f"{filename}:{lineno}: 'upgrade' style requires size=_,_")
|
print(f"{filename}:{lineno}: 'upgrade' style requires size=_,_")
|
||||||
exit(1)
|
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":
|
elif style == "narrate":
|
||||||
message.bytes += [0x0D]
|
message.bytes += [0x0D]
|
||||||
elif style == "epilogue":
|
elif style == "epilogue":
|
||||||
@ -579,7 +602,7 @@ if __name__ == "__main__":
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
message.bytes += [0xFF, 0x00, font]
|
message.bytes += [0xFF, 0x00, font]
|
||||||
#font_stack.append(font)
|
# font_stack.append(font)
|
||||||
|
|
||||||
if font == 3 or font == 4:
|
if font == 3 or font == 4:
|
||||||
charset = CHARSET_CREDITS
|
charset = CHARSET_CREDITS
|
||||||
@ -709,7 +732,13 @@ if __name__ == "__main__":
|
|||||||
print(f"{filename}:{lineno}: {command} command requires raster=_")
|
print(f"{filename}:{lineno}: {command} command requires raster=_")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
message.bytes += [0xFF, 0x16, spriteid >> 8, spriteid & 0xFF, raster]
|
message.bytes += [
|
||||||
|
0xFF,
|
||||||
|
0x16,
|
||||||
|
spriteid >> 8,
|
||||||
|
spriteid & 0xFF,
|
||||||
|
raster,
|
||||||
|
]
|
||||||
elif command == "itemicon":
|
elif command == "itemicon":
|
||||||
itemid = named_args.get("itemid")
|
itemid = named_args.get("itemid")
|
||||||
|
|
||||||
@ -722,7 +751,7 @@ if __name__ == "__main__":
|
|||||||
message.bytes += [0xFF, 0x17, itemid >> 8, itemid & 0xFF]
|
message.bytes += [0xFF, 0x17, itemid >> 8, itemid & 0xFF]
|
||||||
elif command == "image":
|
elif command == "image":
|
||||||
index = named_args.get("index")
|
index = named_args.get("index")
|
||||||
pos = named_args.get("pos") # xx,y
|
pos = named_args.get("pos") # xx,y
|
||||||
hasborder = named_args.get("hasborder")
|
hasborder = named_args.get("hasborder")
|
||||||
alpha = named_args.get("alpha")
|
alpha = named_args.get("alpha")
|
||||||
fadeamount = named_args.get("fadeamount")
|
fadeamount = named_args.get("fadeamount")
|
||||||
@ -743,7 +772,17 @@ if __name__ == "__main__":
|
|||||||
print(f"{filename}:{lineno}: {command} command requires fadeamount=_")
|
print(f"{filename}:{lineno}: {command} command requires fadeamount=_")
|
||||||
exit(1)
|
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":
|
elif command == "hideimage":
|
||||||
fadeamount = named_args.get("fadeamount", 0)
|
fadeamount = named_args.get("fadeamount", 0)
|
||||||
|
|
||||||
@ -933,9 +972,16 @@ if __name__ == "__main__":
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
message.bytes += [
|
message.bytes += [
|
||||||
0xFF, 0x2C,
|
0xFF,
|
||||||
soundids[0] >> 24, (soundids[0] >> 16) & 0xFF, (soundids[0] >> 8) & 0xFF, soundids[0] & 0xFF,
|
0x2C,
|
||||||
soundids[1] >> 24, (soundids[1] >> 16) & 0xFF, (soundids[1] >> 8) & 0xFF, soundids[1] & 0xFF,
|
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":
|
elif command == "volume":
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
@ -962,70 +1008,184 @@ if __name__ == "__main__":
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
message.bytes += [0xFF, 0x2F, sound]
|
message.bytes += [0xFF, 0x2F, sound]
|
||||||
#sound_stack.append(sound)
|
# sound_stack.append(sound)
|
||||||
# elif command == "/sound":
|
# elif command == "/sound":
|
||||||
# sound_stack.pop()
|
# sound_stack.pop()
|
||||||
# message.bytes += [0xFF, 0x2F, sound_stack[0]]
|
# message.bytes += [0xFF, 0x2F, sound_stack[0]]
|
||||||
elif command == "a":
|
elif command == "a":
|
||||||
color_code = color_to_code("blue", "button")
|
color_code = color_to_code("blue", "button")
|
||||||
assert color_code is not None
|
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":
|
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
|
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":
|
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
|
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":
|
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
|
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":
|
elif command == "z":
|
||||||
color_code = color_to_code("grey", "button")
|
color_code = color_to_code("grey", "button")
|
||||||
assert color_code is not None
|
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":
|
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
|
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":
|
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
|
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":
|
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
|
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":
|
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
|
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":
|
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
|
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":
|
elif command == "~a":
|
||||||
message.bytes += [0x98]
|
message.bytes += [0x98]
|
||||||
elif command == "~b":
|
elif command == "~b":
|
||||||
message.bytes += [0x99]
|
message.bytes += [0x99]
|
||||||
elif command == "~l":
|
elif command == "~l":
|
||||||
message.bytes += [0x9a]
|
message.bytes += [0x9A]
|
||||||
elif command == "~r":
|
elif command == "~r":
|
||||||
message.bytes += [0x9b]
|
message.bytes += [0x9B]
|
||||||
elif command == "~z":
|
elif command == "~z":
|
||||||
message.bytes += [0x9c]
|
message.bytes += [0x9C]
|
||||||
elif command == "~c-up":
|
elif command == "~c-up":
|
||||||
message.bytes += [0x9d]
|
message.bytes += [0x9D]
|
||||||
elif command == "~c-down":
|
elif command == "~c-down":
|
||||||
message.bytes += [0x9e]
|
message.bytes += [0x9E]
|
||||||
elif command == "~c-left":
|
elif command == "~c-left":
|
||||||
message.bytes += [0x9f]
|
message.bytes += [0x9F]
|
||||||
elif command == "~c-right":
|
elif command == "~c-right":
|
||||||
message.bytes += [0xa0]
|
message.bytes += [0xA0]
|
||||||
elif command == "~start":
|
elif command == "~start":
|
||||||
message.bytes += [0xa1]
|
message.bytes += [0xA1]
|
||||||
elif command == "note":
|
elif command == "note":
|
||||||
message.bytes += [0x00]
|
message.bytes += [0x00]
|
||||||
elif command == "heart":
|
elif command == "heart":
|
||||||
@ -1043,24 +1203,24 @@ if __name__ == "__main__":
|
|||||||
elif command == "restorepos":
|
elif command == "restorepos":
|
||||||
message.bytes += [0xFF, 0x23]
|
message.bytes += [0xFF, 0x23]
|
||||||
elif command == "enablecdownnext":
|
elif command == "enablecdownnext":
|
||||||
message.bytes += [0xFF, 0x2b]
|
message.bytes += [0xFF, 0x2B]
|
||||||
elif command == "beginchoice":
|
elif command == "beginchoice":
|
||||||
choiceindex = 0
|
choiceindex = 0
|
||||||
message.bytes += [0xFF, 0x09] # delayoff
|
message.bytes += [0xFF, 0x09] # delayoff
|
||||||
elif command == "option" and choiceindex >= 0:
|
elif command == "option" and choiceindex >= 0:
|
||||||
message.bytes += [0xFF, 0x1E, choiceindex] # cursor n
|
message.bytes += [0xFF, 0x1E, choiceindex] # cursor n
|
||||||
message.bytes += [0xFF, 0x21, choiceindex] # option n
|
message.bytes += [0xFF, 0x21, choiceindex] # option n
|
||||||
choiceindex += 1
|
choiceindex += 1
|
||||||
elif command == "endchoice" and choiceindex >= 0:
|
elif command == "endchoice" and choiceindex >= 0:
|
||||||
cancel = named_args.get("cancel")
|
cancel = named_args.get("cancel")
|
||||||
|
|
||||||
message.bytes += [0xFF, 0x21, 255] # option 255
|
message.bytes += [0xFF, 0x21, 255] # option 255
|
||||||
message.bytes += [0xFF, 0x0A] # delayon
|
message.bytes += [0xFF, 0x0A] # delayon
|
||||||
|
|
||||||
if isinstance(cancel, int):
|
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
|
choiceindex = -1
|
||||||
elif command == "animation" and choiceindex >= 0:
|
elif command == "animation" and choiceindex >= 0:
|
||||||
@ -1074,7 +1234,7 @@ if __name__ == "__main__":
|
|||||||
if source[0] == "}":
|
if source[0] == "}":
|
||||||
if not explicit_end:
|
if not explicit_end:
|
||||||
print(f"{filename}:{lineno}: warning: string lacks an [end] command")
|
print(f"{filename}:{lineno}: warning: string lacks an [end] command")
|
||||||
#message.bytes += [0xFD]
|
# message.bytes += [0xFD]
|
||||||
explicit_end = False
|
explicit_end = False
|
||||||
|
|
||||||
# sanity check
|
# sanity check
|
||||||
@ -1087,7 +1247,7 @@ if __name__ == "__main__":
|
|||||||
message.bytes += [0x00]
|
message.bytes += [0x00]
|
||||||
|
|
||||||
message = None
|
message = None
|
||||||
source = source[1:] # }
|
source = source[1:] # }
|
||||||
indent_level = 0
|
indent_level = 0
|
||||||
choiceindex = -1
|
choiceindex = -1
|
||||||
continue
|
continue
|
||||||
@ -1124,9 +1284,15 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
with open(outfile, "wb") as f:
|
with open(outfile, "wb") as f:
|
||||||
msgpack.pack([{
|
msgpack.pack(
|
||||||
"section": message.section,
|
[
|
||||||
"index": message.index,
|
{
|
||||||
"name": message.name,
|
"section": message.section,
|
||||||
"bytes": bytes(message.bytes),
|
"index": message.index,
|
||||||
} for message in messages], f)
|
"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}[] = {{ ")
|
f.write(f"unsigned short {cname}[] = {{ ")
|
||||||
|
|
||||||
with open(infile, "rb") as i:
|
with open(infile, "rb") as i:
|
||||||
while (short := i.read(2)):
|
while short := i.read(2):
|
||||||
color = struct.unpack('>H', short)[0]
|
color = struct.unpack(">H", short)[0]
|
||||||
f.write(f'0x{color:04X}, ')
|
f.write(f"0x{color:04X}, ")
|
||||||
|
|
||||||
f.write("};\n")
|
f.write("};\n")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from sys import argv
|
from sys import argv
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
argv.pop(0) # python3
|
argv.pop(0) # python3
|
||||||
out = argv.pop(0)
|
out = argv.pop(0)
|
||||||
|
|
||||||
with open(out, "wb") as f:
|
with open(out, "wb") as f:
|
||||||
@ -11,4 +11,4 @@ if __name__ == "__main__":
|
|||||||
with open(path, "rb") as j:
|
with open(path, "rb") as j:
|
||||||
f.write(j.read())
|
f.write(j.read())
|
||||||
while f.tell() % 8 != 0:
|
while f.tell() % 8 != 0:
|
||||||
f.write(b'\0')
|
f.write(b"\0")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from sys import argv
|
from sys import argv
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
argv.pop(0) # python3
|
argv.pop(0) # python3
|
||||||
out = argv.pop(0)
|
out = argv.pop(0)
|
||||||
|
|
||||||
with open(out, "wb") as f:
|
with open(out, "wb") as f:
|
||||||
|
@ -9,6 +9,7 @@ import xml.etree.ElementTree as ET
|
|||||||
from img.build import Converter
|
from img.build import Converter
|
||||||
import png
|
import png
|
||||||
|
|
||||||
|
|
||||||
def get_img_file(fmt_str, img_file: str):
|
def get_img_file(fmt_str, img_file: str):
|
||||||
def pack_color(r, g, b, a):
|
def pack_color(r, g, b, a):
|
||||||
r = r >> 3
|
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)
|
return (out_img, out_pal, out_w, out_h)
|
||||||
|
|
||||||
|
|
||||||
def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path, ...]):
|
def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path, ...]):
|
||||||
out_bytes = bytearray()
|
out_bytes = bytearray()
|
||||||
offsets: Dict[str, int] = {}
|
offsets: Dict[str, int] = {}
|
||||||
@ -51,7 +53,7 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path
|
|||||||
if type is None:
|
if type is None:
|
||||||
raise Exception("Icon os missing attribute: 'type'")
|
raise Exception("Icon os missing attribute: 'type'")
|
||||||
|
|
||||||
name = re.sub("\\W","_",file)
|
name = re.sub("\\W", "_", file)
|
||||||
|
|
||||||
if type == "solo" or type == "pair":
|
if type == "solo" or type == "pair":
|
||||||
img_path = str(get_asset_path(Path(f"icon/{file}.png"), asset_stack))
|
img_path = str(get_asset_path(Path(f"icon/{file}.png"), asset_stack))
|
||||||
@ -94,6 +96,7 @@ def build(out_bin: Path, in_xml: Path, out_header: Path, asset_stack: Tuple[Path
|
|||||||
|
|
||||||
f.write("\n#endif // ICON_OFFSETS_H\n")
|
f.write("\n#endif // ICON_OFFSETS_H\n")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Icon archive")
|
parser = argparse.ArgumentParser(description="Icon archive")
|
||||||
parser.add_argument("out_bin", type=Path, help="output binary file path")
|
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 p, palette_name in enumerate(sprite.palette_names):
|
||||||
for a, name in enumerate(sprite.animation_names):
|
for a, name in enumerate(sprite.animation_names):
|
||||||
if palette_name == "Default":
|
if palette_name == "Default":
|
||||||
f.write(
|
f.write(f"#define ANIM_{sprite_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n")
|
||||||
f"#define ANIM_{sprite_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
f.write(
|
f.write(f"#define ANIM_{sprite_name}_{palette_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n")
|
||||||
f"#define ANIM_{sprite_name}_{palette_name}_{name} 0x{s:02X}{p:02X}{a:02X}\n"
|
|
||||||
)
|
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
f.write("#endif\n")
|
f.write("#endif\n")
|
||||||
|
@ -76,9 +76,7 @@ def from_dir(
|
|||||||
|
|
||||||
palettes.append(palette)
|
palettes.append(palette)
|
||||||
|
|
||||||
palette_names.append(
|
palette_names.append(Palette.get("name", Palette.attrib["src"].split(".png")[0]))
|
||||||
Palette.get("name", Palette.attrib["src"].split(".png")[0])
|
|
||||||
)
|
|
||||||
|
|
||||||
images = []
|
images = []
|
||||||
image_names: List[str] = []
|
image_names: List[str] = []
|
||||||
@ -192,9 +190,7 @@ if __name__ == "__main__":
|
|||||||
palette_offsets.append(f.tell())
|
palette_offsets.append(f.tell())
|
||||||
for rgba in palette:
|
for rgba in palette:
|
||||||
if rgba[3] not in (0, 0xFF):
|
if rgba[3] not in (0, 0xFF):
|
||||||
print(
|
print("error: translucent pixels not allowed in palette {sprite.palette_names[i]}")
|
||||||
"error: translucent pixels not allowed in palette {sprite.palette_names[i]}"
|
|
||||||
)
|
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
color = pack_color(*rgba)
|
color = pack_color(*rgba)
|
||||||
|
@ -8,11 +8,13 @@ from pathlib import Path
|
|||||||
import struct
|
import struct
|
||||||
from typing import List, Literal
|
from typing import List, Literal
|
||||||
|
|
||||||
|
|
||||||
class LightMode(Enum):
|
class LightMode(Enum):
|
||||||
UNIFORM = 0
|
UNIFORM = 0
|
||||||
LINEAR = 4
|
LINEAR = 4
|
||||||
QUADRATIC = 8
|
QUADRATIC = 8
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Light:
|
class Light:
|
||||||
flags: int
|
flags: int
|
||||||
@ -83,9 +85,14 @@ def groups_from_json(data) -> List[SpriteShadingGroup]:
|
|||||||
)
|
)
|
||||||
return groups
|
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 "<"
|
END = ">" if endian == "big" else "<"
|
||||||
|
|
||||||
with open(input, "r") as f:
|
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)
|
offsets_table.extend(profile_lists)
|
||||||
if matching:
|
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
|
final_data = offsets_table + data_table
|
||||||
if matching:
|
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:
|
with open(bin_out, "wb") as f:
|
||||||
f.write(final_data)
|
f.write(final_data)
|
||||||
|
@ -33,9 +33,7 @@ import xml.etree.ElementTree as ET
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, List, Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
TOOLS_DIR = Path(
|
TOOLS_DIR = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
)
|
|
||||||
sys.path.append(str(TOOLS_DIR))
|
sys.path.append(str(TOOLS_DIR))
|
||||||
|
|
||||||
ASSET_DIR = TOOLS_DIR.parent / "assets"
|
ASSET_DIR = TOOLS_DIR.parent / "assets"
|
||||||
@ -52,11 +50,7 @@ def pack_color(r, g, b, a) -> int:
|
|||||||
def get_player_sprite_metadata(
|
def get_player_sprite_metadata(
|
||||||
asset_stack: Tuple[Path, ...],
|
asset_stack: Tuple[Path, ...],
|
||||||
) -> Tuple[str, List[str], List[str]]:
|
) -> Tuple[str, List[str], List[str]]:
|
||||||
orderings_tree = ET.parse(
|
orderings_tree = ET.parse(get_asset_path(Path("sprite") / PLAYER_SPRITE_MEDADATA_XML_FILENAME, asset_stack))
|
||||||
get_asset_path(
|
|
||||||
Path("sprite") / PLAYER_SPRITE_MEDADATA_XML_FILENAME, asset_stack
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
build_info = str(orderings_tree.getroot()[0].text)
|
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]:
|
def get_npc_sprite_metadata(asset_stack: Tuple[Path, ...]) -> List[str]:
|
||||||
orderings_tree = ET.parse(
|
orderings_tree = ET.parse(get_asset_path(Path("sprite") / NPC_SPRITE_MEDADATA_XML_FILENAME, asset_stack))
|
||||||
get_asset_path(Path("sprite") / NPC_SPRITE_MEDADATA_XML_FILENAME, asset_stack)
|
|
||||||
)
|
|
||||||
|
|
||||||
sprite_order: List[str] = []
|
sprite_order: List[str] = []
|
||||||
for sprite_tag in orderings_tree.getroot()[0]:
|
for sprite_tag in orderings_tree.getroot()[0]:
|
||||||
@ -100,18 +92,14 @@ PALETTE_CACHE: Dict[str, bytes] = {}
|
|||||||
PLAYER_XML_CACHE: Dict[str, ET.Element] = {}
|
PLAYER_XML_CACHE: Dict[str, ET.Element] = {}
|
||||||
|
|
||||||
# TODO perhaps encode this better
|
# TODO perhaps encode this better
|
||||||
SPECIAL_RASTER_BYTES = (
|
SPECIAL_RASTER_BYTES = b"\x80\x30\x02\x10\x00\x00\x02\x00\x00\x00\x00\x01\x00\x10\x00\x00"
|
||||||
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, ...]):
|
def cache_player_rasters(raster_order: List[str], asset_stack: Tuple[Path, ...]):
|
||||||
# Read all player rasters and cache them
|
# Read all player rasters and cache them
|
||||||
cur_offset = 0
|
cur_offset = 0
|
||||||
for raster_name in raster_order:
|
for raster_name in raster_order:
|
||||||
png_path = get_asset_path(
|
png_path = get_asset_path(Path(f"sprite/player/rasters/{raster_name}.png"), asset_stack)
|
||||||
Path(f"sprite/player/rasters/{raster_name}.png"), asset_stack
|
|
||||||
)
|
|
||||||
|
|
||||||
# "Weird" raster
|
# "Weird" raster
|
||||||
if os.path.getsize(png_path) == 0x10:
|
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"]
|
source = palette_xml.attrib["src"]
|
||||||
front_only = bool(palette_xml.get("front_only", False))
|
front_only = bool(palette_xml.get("front_only", False))
|
||||||
if source not in PALETTE_CACHE:
|
if source not in PALETTE_CACHE:
|
||||||
palette_path = get_asset_path(
|
palette_path = get_asset_path(Path(f"sprite/player/palettes/{source}"), asset_stack)
|
||||||
Path(f"sprite/player/palettes/{source}"), asset_stack
|
|
||||||
)
|
|
||||||
with open(palette_path, "rb") as f:
|
with open(palette_path, "rb") as f:
|
||||||
img = png.Reader(f)
|
img = png.Reader(f)
|
||||||
img.preamble(True)
|
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:
|
if "back" in raster_xml.attrib:
|
||||||
has_back = True
|
has_back = True
|
||||||
r = player_raster_from_xml(raster_xml, back=False)
|
r = player_raster_from_xml(raster_xml, back=False)
|
||||||
raster_bytes += struct.pack(
|
raster_bytes += struct.pack(">IBBBB", raster_offset, r.width, r.height, r.palette_idx, 0xFF)
|
||||||
">IBBBB", raster_offset, r.width, r.height, r.palette_idx, 0xFF
|
|
||||||
)
|
|
||||||
|
|
||||||
raster_offset += r.width * r.height // 2
|
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)
|
height = int(special[1], 16)
|
||||||
palette = int(raster_xml.attrib.get(BACK_PALETTE_XML, r.palette_idx))
|
palette = int(raster_xml.attrib.get(BACK_PALETTE_XML, r.palette_idx))
|
||||||
|
|
||||||
raster_bytes_back += struct.pack(
|
raster_bytes_back += struct.pack(">IBBBB", raster_offset, width, height, palette, 0xFF)
|
||||||
">IBBBB", raster_offset, width, height, palette, 0xFF
|
|
||||||
)
|
|
||||||
|
|
||||||
if is_back:
|
if is_back:
|
||||||
raster_offset += width * height // 2
|
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""
|
raster_offsets_bytes_back = b""
|
||||||
for i in range(len(xml[1])):
|
for i in range(len(xml[1])):
|
||||||
raster_offsets_bytes += int.to_bytes(raster_list_start + i * 8, 4, "big")
|
raster_offsets_bytes += int.to_bytes(raster_list_start + i * 8, 4, "big")
|
||||||
raster_offsets_bytes_back += int.to_bytes(
|
raster_offsets_bytes_back += int.to_bytes(raster_list_start_back + i * 8, 4, "big")
|
||||||
raster_list_start_back + i * 8, 4, "big"
|
|
||||||
)
|
|
||||||
raster_offsets_bytes += LIST_END_BYTES
|
raster_offsets_bytes += LIST_END_BYTES
|
||||||
raster_offsets_bytes_back += 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")
|
palette_offsets_bytes += int.to_bytes(palette_list_start + i * 0x20, 4, "big")
|
||||||
front_only = bool(palette_xml.attrib.get("front_only", False))
|
front_only = bool(palette_xml.attrib.get("front_only", False))
|
||||||
if not front_only:
|
if not front_only:
|
||||||
palette_offsets_bytes_back += int.to_bytes(
|
palette_offsets_bytes_back += int.to_bytes(palette_list_start_back + i * 0x20, 4, "big")
|
||||||
palette_list_start_back + i * 0x20, 4, "big"
|
|
||||||
)
|
|
||||||
palette_offsets_bytes += LIST_END_BYTES
|
palette_offsets_bytes += LIST_END_BYTES
|
||||||
palette_offsets_bytes_back += 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]:
|
for palette_xml in sprite_xml[0]:
|
||||||
palette_id = int(palette_xml.attrib["id"], 0x10)
|
palette_id = int(palette_xml.attrib["id"], 0x10)
|
||||||
palette_name = palette_xml.attrib["name"]
|
palette_name = palette_xml.attrib["name"]
|
||||||
player_palettes[sprite_name][
|
player_palettes[sprite_name][f"SPR_PAL_{sprite_name}_{palette_name}"] = palette_id
|
||||||
f"SPR_PAL_{sprite_name}_{palette_name}"
|
|
||||||
] = palette_id
|
|
||||||
|
|
||||||
for anim_id, anim_xml in enumerate(sprite_xml[2]):
|
for anim_id, anim_xml in enumerate(sprite_xml[2]):
|
||||||
anim_name = anim_xml.attrib["name"]
|
anim_name = anim_xml.attrib["name"]
|
||||||
@ -413,9 +389,7 @@ def write_player_sprite_header(
|
|||||||
for raster_xml in sprite_xml[1]:
|
for raster_xml in sprite_xml[1]:
|
||||||
raster_id = int(raster_xml.attrib["id"], 0x10)
|
raster_id = int(raster_xml.attrib["id"], 0x10)
|
||||||
raster_name = raster_xml.attrib["name"]
|
raster_name = raster_xml.attrib["name"]
|
||||||
player_rasters[sprite_name][
|
player_rasters[sprite_name][f"SPR_IMG_{sprite_name}_{raster_name}"] = raster_id
|
||||||
f"SPR_IMG_{sprite_name}_{raster_name}"
|
|
||||||
] = raster_id
|
|
||||||
|
|
||||||
raster = RASTER_CACHE[raster_xml.attrib["src"][:-4]]
|
raster = RASTER_CACHE[raster_xml.attrib["src"][:-4]]
|
||||||
if max_size < raster.size:
|
if max_size < raster.size:
|
||||||
@ -449,9 +423,7 @@ def write_player_sprite_header(
|
|||||||
f.write("};\n\n")
|
f.write("};\n\n")
|
||||||
|
|
||||||
for sprite_name in max_sprite_sizes:
|
for sprite_name in max_sprite_sizes:
|
||||||
f.write(
|
f.write(f"#define MAX_IMG_{sprite_name} 0x{max_sprite_sizes[sprite_name]:04X}\n")
|
||||||
f"#define MAX_IMG_{sprite_name} 0x{max_sprite_sizes[sprite_name]:04X}\n"
|
|
||||||
)
|
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
for sprite_name in sprite_order:
|
for sprite_name in sprite_order:
|
||||||
@ -472,15 +444,11 @@ def write_player_sprite_header(
|
|||||||
f.write(f"#endif // {ifdef_name}\n")
|
f.write(f"#endif // {ifdef_name}\n")
|
||||||
|
|
||||||
|
|
||||||
def build_player_sprites(
|
def build_player_sprites(sprite_order: List[str], build_dir: Path, asset_stack: Tuple[Path, ...]) -> bytes:
|
||||||
sprite_order: List[str], build_dir: Path, asset_stack: Tuple[Path, ...]
|
|
||||||
) -> bytes:
|
|
||||||
sprite_bytes: List[bytes] = []
|
sprite_bytes: List[bytes] = []
|
||||||
|
|
||||||
for sprite_name in sprite_order:
|
for sprite_name in sprite_order:
|
||||||
sprite_bytes.extend(
|
sprite_bytes.extend(player_xml_to_bytes(PLAYER_XML_CACHE[sprite_name], asset_stack))
|
||||||
player_xml_to_bytes(PLAYER_XML_CACHE[sprite_name], asset_stack)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Compress sprite bytes
|
# Compress sprite bytes
|
||||||
compressed_sprite_bytes: bytes = b""
|
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 has_back:
|
||||||
if "back" in raster_xml.attrib:
|
if "back" in raster_xml.attrib:
|
||||||
png_info = RASTER_CACHE[raster_xml.attrib["back"][:-4]]
|
png_info = RASTER_CACHE[raster_xml.attrib["back"][:-4]]
|
||||||
sheet_rtes_back.append(
|
sheet_rtes_back.append(RasterTableEntry(png_info.offset, png_info.size))
|
||||||
RasterTableEntry(png_info.offset, png_info.size)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
sheet_rtes_back.append(RasterTableEntry(SPECIAL_RASTER, 0x10))
|
sheet_rtes_back.append(RasterTableEntry(SPECIAL_RASTER, 0x10))
|
||||||
|
|
||||||
@ -623,27 +589,21 @@ def build(
|
|||||||
build_dir: Path,
|
build_dir: Path,
|
||||||
asset_stack: Tuple[Path, ...],
|
asset_stack: Tuple[Path, ...],
|
||||||
) -> None:
|
) -> None:
|
||||||
build_info, player_sprite_order, player_raster_order = get_player_sprite_metadata(
|
build_info, player_sprite_order, player_raster_order = get_player_sprite_metadata(asset_stack)
|
||||||
asset_stack
|
|
||||||
)
|
|
||||||
npc_sprite_order = get_npc_sprite_metadata(asset_stack)
|
npc_sprite_order = get_npc_sprite_metadata(asset_stack)
|
||||||
|
|
||||||
cache_player_rasters(player_raster_order, asset_stack)
|
cache_player_rasters(player_raster_order, asset_stack)
|
||||||
|
|
||||||
# Read and cache player XMLs
|
# Read and cache player XMLs
|
||||||
for sprite_name in player_sprite_order:
|
for sprite_name in player_sprite_order:
|
||||||
sprite_xml = ET.parse(
|
sprite_xml = ET.parse(get_asset_path(Path(f"sprite/player/{sprite_name}.xml"), asset_stack)).getroot()
|
||||||
get_asset_path(Path(f"sprite/player/{sprite_name}.xml"), asset_stack)
|
|
||||||
).getroot()
|
|
||||||
PLAYER_XML_CACHE[sprite_name] = sprite_xml
|
PLAYER_XML_CACHE[sprite_name] = sprite_xml
|
||||||
|
|
||||||
# Encode build_info to bytes and pad to 0x10
|
# Encode build_info to bytes and pad to 0x10
|
||||||
build_info_bytes = build_info.encode("ascii")
|
build_info_bytes = build_info.encode("ascii")
|
||||||
build_info_bytes += b"\0" * (0x10 - len(build_info_bytes))
|
build_info_bytes += b"\0" * (0x10 - len(build_info_bytes))
|
||||||
|
|
||||||
player_sprite_bytes = build_player_sprites(
|
player_sprite_bytes = build_player_sprites(player_sprite_order, build_dir / "player", asset_stack)
|
||||||
player_sprite_order, build_dir / "player", asset_stack
|
|
||||||
)
|
|
||||||
player_raster_bytes = build_player_rasters(player_sprite_order, player_raster_order)
|
player_raster_bytes = build_player_rasters(player_sprite_order, player_raster_order)
|
||||||
npc_sprite_bytes = build_npc_sprites(npc_sprite_order, build_dir)
|
npc_sprite_bytes = build_npc_sprites(npc_sprite_order, build_dir)
|
||||||
|
|
||||||
@ -659,13 +619,7 @@ def build(
|
|||||||
npc_sprites_offset,
|
npc_sprites_offset,
|
||||||
)
|
)
|
||||||
|
|
||||||
final_data = (
|
final_data = build_info_bytes + major_file_divisons + player_raster_bytes + player_sprite_bytes + npc_sprite_bytes
|
||||||
build_info_bytes
|
|
||||||
+ major_file_divisons
|
|
||||||
+ player_raster_bytes
|
|
||||||
+ player_sprite_bytes
|
|
||||||
+ npc_sprite_bytes
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(out_file, "wb") as f:
|
with open(out_file, "wb") as f:
|
||||||
f.write(final_data)
|
f.write(final_data)
|
||||||
|
@ -12,9 +12,7 @@ for root, dirs, files in os.walk("assets/us/mapfs/geom"):
|
|||||||
if file.endswith("_shape.bin"):
|
if file.endswith("_shape.bin"):
|
||||||
total += 1
|
total += 1
|
||||||
shape_file = os.path.join(root, file)
|
shape_file = os.path.join(root, file)
|
||||||
built_data_file = Path("ver/us/build") / shape_file.replace(
|
built_data_file = Path("ver/us/build") / shape_file.replace("_shape.bin", "_shape_data.bin")
|
||||||
"_shape.bin", "_shape_data.bin"
|
|
||||||
)
|
|
||||||
|
|
||||||
if filecmp.cmp(shape_file, built_data_file, shallow=False):
|
if filecmp.cmp(shape_file, built_data_file, shallow=False):
|
||||||
matching += 1
|
matching += 1
|
||||||
|
@ -2,53 +2,61 @@
|
|||||||
|
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
|
||||||
def read(f):
|
def read(f):
|
||||||
return struct.unpack('>h', f.read(2))[0]
|
return struct.unpack(">h", f.read(2))[0]
|
||||||
|
|
||||||
|
|
||||||
def i2f(x):
|
def i2f(x):
|
||||||
return round(x * 180 / 32767 * 200) / 200
|
return round(x * 180 / 32767 * 200) / 200
|
||||||
|
|
||||||
|
|
||||||
def parse(f):
|
def parse(f):
|
||||||
print('AnimScript script = {')
|
print("AnimScript script = {")
|
||||||
indent = ' '
|
indent = " "
|
||||||
while True:
|
while True:
|
||||||
op = read(f)
|
op = read(f)
|
||||||
if op == 0:
|
if op == 0:
|
||||||
print(f'{indent}AS_END,')
|
print(f"{indent}AS_END,")
|
||||||
break
|
break
|
||||||
if op == 1:
|
if op == 1:
|
||||||
print(f'{indent}AS_WAIT, {read(f)},')
|
print(f"{indent}AS_WAIT, {read(f)},")
|
||||||
elif op == 3:
|
elif op == 3:
|
||||||
indent = indent[:-4]
|
indent = indent[:-4]
|
||||||
print(f'{indent}AS_END_LOOP,')
|
print(f"{indent}AS_END_LOOP,")
|
||||||
elif op == 5:
|
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:
|
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:
|
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:
|
elif op == 10:
|
||||||
print(f'{indent}AS_LOOP,')
|
print(f"{indent}AS_LOOP,")
|
||||||
indent += ' '
|
indent += " "
|
||||||
elif op == 14:
|
elif op == 14:
|
||||||
print(f'{indent}AS_SET_FLAGS, {read(f)},')
|
print(f"{indent}AS_SET_FLAGS, {read(f)},")
|
||||||
elif op == 15:
|
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:
|
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:
|
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:
|
elif op == 18:
|
||||||
print(f'{indent}AS_SET_RENDER_MODE, {read(f)},')
|
print(f"{indent}AS_SET_RENDER_MODE, {read(f)},")
|
||||||
elif op == 19:
|
elif op == 19:
|
||||||
print(f'{indent}AS_OP_19,')
|
print(f"{indent}AS_OP_19,")
|
||||||
else:
|
else:
|
||||||
raise Exception(str(f'Unknown opcode {op}'))
|
raise Exception(str(f"Unknown opcode {op}"))
|
||||||
print('};')
|
print("};")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("file", type=str, help="File to dissassemble from")
|
parser.add_argument("file", type=str, help="File to dissassemble from")
|
||||||
parser.add_argument("offset", help="Offset to start dissassembling from")
|
parser.add_argument("offset", help="Offset to start dissassembling from")
|
||||||
|
@ -3,62 +3,63 @@
|
|||||||
import struct
|
import struct
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
def fmt_size(val):
|
def fmt_size(val):
|
||||||
if val == 0:
|
if val == 0:
|
||||||
return 'HUD_ELEMENT_SIZE_8x8'
|
return "HUD_ELEMENT_SIZE_8x8"
|
||||||
elif val == 1:
|
elif val == 1:
|
||||||
return 'HUD_ELEMENT_SIZE_16x16'
|
return "HUD_ELEMENT_SIZE_16x16"
|
||||||
elif val == 2:
|
elif val == 2:
|
||||||
return 'HUD_ELEMENT_SIZE_24x24'
|
return "HUD_ELEMENT_SIZE_24x24"
|
||||||
elif val == 3:
|
elif val == 3:
|
||||||
return 'HUD_ELEMENT_SIZE_32x32'
|
return "HUD_ELEMENT_SIZE_32x32"
|
||||||
elif val == 4:
|
elif val == 4:
|
||||||
return 'HUD_ELEMENT_SIZE_48x48'
|
return "HUD_ELEMENT_SIZE_48x48"
|
||||||
elif val == 5:
|
elif val == 5:
|
||||||
return 'HUD_ELEMENT_SIZE_64x64'
|
return "HUD_ELEMENT_SIZE_64x64"
|
||||||
elif val == 6:
|
elif val == 6:
|
||||||
return 'HUD_ELEMENT_SIZE_8x16'
|
return "HUD_ELEMENT_SIZE_8x16"
|
||||||
elif val == 7:
|
elif val == 7:
|
||||||
return 'HUD_ELEMENT_SIZE_16x8'
|
return "HUD_ELEMENT_SIZE_16x8"
|
||||||
elif val == 8:
|
elif val == 8:
|
||||||
return 'HUD_ELEMENT_SIZE_16x24'
|
return "HUD_ELEMENT_SIZE_16x24"
|
||||||
elif val == 9:
|
elif val == 9:
|
||||||
return 'HUD_ELEMENT_SIZE_16x32'
|
return "HUD_ELEMENT_SIZE_16x32"
|
||||||
elif val == 10:
|
elif val == 10:
|
||||||
return 'HUD_ELEMENT_SIZE_64x32'
|
return "HUD_ELEMENT_SIZE_64x32"
|
||||||
elif val == 11:
|
elif val == 11:
|
||||||
return 'HUD_ELEMENT_SIZE_32x16'
|
return "HUD_ELEMENT_SIZE_32x16"
|
||||||
elif val == 12:
|
elif val == 12:
|
||||||
return 'HUD_ELEMENT_SIZE_12x12'
|
return "HUD_ELEMENT_SIZE_12x12"
|
||||||
elif val == 13:
|
elif val == 13:
|
||||||
return 'HUD_ELEMENT_SIZE_48x24'
|
return "HUD_ELEMENT_SIZE_48x24"
|
||||||
elif val == 14:
|
elif val == 14:
|
||||||
return 'HUD_ELEMENT_SIZE_32x8'
|
return "HUD_ELEMENT_SIZE_32x8"
|
||||||
elif val == 15:
|
elif val == 15:
|
||||||
return 'HUD_ELEMENT_SIZE_24x8'
|
return "HUD_ELEMENT_SIZE_24x8"
|
||||||
elif val == 16:
|
elif val == 16:
|
||||||
return 'HUD_ELEMENT_SIZE_64x16'
|
return "HUD_ELEMENT_SIZE_64x16"
|
||||||
elif val == 17:
|
elif val == 17:
|
||||||
return 'HUD_ELEMENT_SIZE_16x64'
|
return "HUD_ELEMENT_SIZE_16x64"
|
||||||
elif val == 18:
|
elif val == 18:
|
||||||
return 'HUD_ELEMENT_SIZE_192x32'
|
return "HUD_ELEMENT_SIZE_192x32"
|
||||||
elif val == 19:
|
elif val == 19:
|
||||||
return 'HUD_ELEMENT_SIZE_40x40'
|
return "HUD_ELEMENT_SIZE_40x40"
|
||||||
elif val == 20:
|
elif val == 20:
|
||||||
return 'HUD_ELEMENT_SIZE_24x16'
|
return "HUD_ELEMENT_SIZE_24x16"
|
||||||
elif val == 21:
|
elif val == 21:
|
||||||
return 'HUD_ELEMENT_SIZE_32x40'
|
return "HUD_ELEMENT_SIZE_32x40"
|
||||||
elif val == 22:
|
elif val == 22:
|
||||||
return 'HUD_ELEMENT_SIZE_40x16'
|
return "HUD_ELEMENT_SIZE_40x16"
|
||||||
elif val == 23:
|
elif val == 23:
|
||||||
return 'HUD_ELEMENT_SIZE_40x24'
|
return "HUD_ELEMENT_SIZE_40x24"
|
||||||
elif val == 24:
|
elif val == 24:
|
||||||
return 'HUD_ELEMENT_SIZE_32x24'
|
return "HUD_ELEMENT_SIZE_32x24"
|
||||||
else:
|
else:
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
class HudElementScript():
|
class HudElementScript:
|
||||||
def __init__(self, symbol):
|
def __init__(self, symbol):
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.buffer = []
|
self.buffer = []
|
||||||
@ -172,7 +173,7 @@ if __name__ == "__main__":
|
|||||||
if word > 0x8000000:
|
if word > 0x8000000:
|
||||||
word = f"0x{word:X}"
|
word = f"0x{word:X}"
|
||||||
else:
|
else:
|
||||||
word, = struct.unpack(">i", struct.pack(">I", word))
|
(word,) = struct.unpack(">i", struct.pack(">I", word))
|
||||||
print(word)
|
print(word)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
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:
|
for ins in insns:
|
||||||
ret.append(ins >> 2)
|
ret.append(ins >> 2)
|
||||||
|
|
||||||
return bytes(ret).decode('utf-8'), bs
|
return bytes(ret).decode("utf-8"), bs
|
||||||
|
|
||||||
|
|
||||||
def parse_map(fname):
|
def parse_map(fname):
|
||||||
@ -80,12 +80,7 @@ def parse_map(fname):
|
|||||||
continue
|
continue
|
||||||
prev_line = line
|
prev_line = line
|
||||||
|
|
||||||
if (
|
if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line:
|
||||||
ram_offset is None
|
|
||||||
or "=" in line
|
|
||||||
or "*fill*" in line
|
|
||||||
or " 0x" not in line
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
ram = int(line[16 : 16 + 18], 0)
|
ram = int(line[16 : 16 + 18], 0)
|
||||||
rom = ram - ram_offset
|
rom = ram - ram_offset
|
||||||
@ -177,7 +172,7 @@ def do_query(query):
|
|||||||
break
|
break
|
||||||
match_str = "{:.3f} - {}".format(matches[match], match)
|
match_str = "{:.3f} - {}".format(matches[match], match)
|
||||||
if match not in s_files:
|
if match not in s_files:
|
||||||
match_str += " (decompiled)"
|
match_str += " (decompiled)"
|
||||||
print(match_str)
|
print(match_str)
|
||||||
i += 1
|
i += 1
|
||||||
print()
|
print()
|
||||||
@ -203,7 +198,10 @@ def all_matches(all_funcs_flag):
|
|||||||
file = to_match_files[0]
|
file = to_match_files[0]
|
||||||
|
|
||||||
i += 1
|
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:
|
if get_symbol_length(file) < 16:
|
||||||
to_match_files.remove(file)
|
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)
|
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):
|
def output_match_dict(
|
||||||
out_file = open(datetime.today().strftime('%Y-%m-%d-%H-%M-%S') + "_all_matches.txt", "w+")
|
match_dict,
|
||||||
|
num_decomped_dupes,
|
||||||
|
num_undecomped_dupes,
|
||||||
|
num_perfect_dupes,
|
||||||
|
num_checked_files,
|
||||||
|
):
|
||||||
|
out_file = open(datetime.today().strftime("%Y-%m-%d-%H-%M-%S") + "_all_matches.txt", "w+")
|
||||||
|
|
||||||
out_file.write("Number of s-files: " + str(len(s_files)) + "\n"
|
out_file.write(
|
||||||
"Number of checked s-files: " + str(round(num_checked_files)) + "\n"
|
"Number of s-files: " + str(len(s_files)) + "\n"
|
||||||
"Number of decompiled duplicates found: " + str(num_decomped_dupes) + "\n"
|
"Number of checked s-files: " + str(round(num_checked_files)) + "\n"
|
||||||
"Number of undecompiled duplicates found: " + str(num_undecomped_dupes) + "\n"
|
"Number of decompiled duplicates found: " + str(num_decomped_dupes) + "\n"
|
||||||
"Number of overall exact duplicates found: " + str(num_perfect_dupes) + "\n\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))
|
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():
|
for file_name, matches in sorted_dict.items():
|
||||||
out_file.write(file_name + " - found " + str(matches[0]) + " matches total:\n")
|
out_file.write(file_name + " - found " + str(matches[0]) + " matches total:\n")
|
||||||
for match in matches[1]:
|
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()
|
out_file.close()
|
||||||
|
|
||||||
|
|
||||||
def is_decompiled(sym):
|
def is_decompiled(sym):
|
||||||
return sym not in s_files
|
return sym not in s_files
|
||||||
|
|
||||||
|
|
||||||
def do_cross_query():
|
def do_cross_query():
|
||||||
ccount = Counter()
|
ccount = Counter()
|
||||||
clusters = []
|
clusters = []
|
||||||
|
|
||||||
sym_bytes = {}
|
sym_bytes = {}
|
||||||
for sym_name in map_syms:
|
for sym_name in map_syms:
|
||||||
if not sym_name.startswith("D_") and \
|
if (
|
||||||
not sym_name.startswith("_binary") and \
|
not sym_name.startswith("D_")
|
||||||
not sym_name.startswith("jtbl_") and \
|
and not sym_name.startswith("_binary")
|
||||||
not re.match(r"L[0-9A-F]{8}_[0-9A-F]{5,6}", sym_name):
|
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:
|
if get_symbol_length(sym_name) > 16:
|
||||||
sym_bytes[sym_name] = get_symbol_bytes(map_offsets, sym_name)
|
sym_bytes[sym_name] = get_symbol_bytes(map_offsets, sym_name)
|
||||||
|
|
||||||
@ -303,14 +313,48 @@ def do_cross_query():
|
|||||||
print(ccount.most_common(100))
|
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 = 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(
|
||||||
group.add_argument("-c", "--cross", help="do a cross query over the codebase", action='store_true', required=False)
|
"-a",
|
||||||
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)
|
"--all",
|
||||||
parser.add_argument("query", help="function or file", nargs='?', default=None)
|
help="find ALL duplicates and output them into a file",
|
||||||
parser.add_argument("-t", "--threshold", help="score threshold between 0 and 1 (higher is more restrictive)", type=float, default=0.9, required=False)
|
action="store_true",
|
||||||
parser.add_argument("-n", "--num-out", help="number of functions to display", type=int, default=100, required=False)
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -60,14 +60,10 @@ def get_all_unmatched_functions():
|
|||||||
|
|
||||||
def get_func_sizes() -> Dict[str, int]:
|
def get_func_sizes() -> Dict[str, int]:
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE)
|
||||||
["mips-linux-gnu-objdump", "-x", elf_path], stdout=subprocess.PIPE
|
|
||||||
)
|
|
||||||
nm_lines = result.stdout.decode().split("\n")
|
nm_lines = result.stdout.decode().split("\n")
|
||||||
except:
|
except:
|
||||||
print(
|
print(f"Error: Could not run objdump on {elf_path} - make sure that the project is built")
|
||||||
f"Error: Could not run objdump on {elf_path} - make sure that the project is built"
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
sizes: Dict[str, int] = {}
|
sizes: Dict[str, int] = {}
|
||||||
@ -127,12 +123,7 @@ def parse_map() -> OrderedDict[str, Symbol]:
|
|||||||
continue
|
continue
|
||||||
prev_line = line
|
prev_line = line
|
||||||
|
|
||||||
if (
|
if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line:
|
||||||
ram_offset is None
|
|
||||||
or "=" in line
|
|
||||||
or "*fill*" in line
|
|
||||||
or " 0x" not in line
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
ram = int(line[16 : 16 + 18], 0)
|
ram = int(line[16 : 16 + 18], 0)
|
||||||
rom = ram - ram_offset
|
rom = ram - ram_offset
|
||||||
@ -240,9 +231,7 @@ def group_matches(
|
|||||||
continue
|
continue
|
||||||
if max is not None and query_start > max:
|
if max is not None and query_start > max:
|
||||||
continue
|
continue
|
||||||
if contains is not None and (
|
if contains is not None and (query_start > contains or query_start + length < contains):
|
||||||
query_start > contains or query_start + length < contains
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ret.append(Result(query, target, query_start, target_start, length))
|
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]:
|
def get_tu_offset(obj_file: Path, symbol: str) -> Optional[int]:
|
||||||
objdump = "mips-linux-gnu-objdump"
|
objdump = "mips-linux-gnu-objdump"
|
||||||
|
|
||||||
objdump_out = (
|
objdump_out = subprocess.run([objdump, "-t", obj_file], stdout=subprocess.PIPE).stdout.decode("utf-8").split("\n")
|
||||||
subprocess.run([objdump, "-t", obj_file], stdout=subprocess.PIPE)
|
|
||||||
.stdout.decode("utf-8")
|
|
||||||
.split("\n")
|
|
||||||
)
|
|
||||||
|
|
||||||
if not objdump_out:
|
if not objdump_out:
|
||||||
return None
|
return None
|
||||||
@ -398,9 +383,7 @@ def get_matches(
|
|||||||
if not matches:
|
if not matches:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
results: list[Result] = group_matches(
|
results: list[Result] = group_matches(query, symbol, matches, window_size, min, max, contains)
|
||||||
query, symbol, matches, window_size, min, max, contains
|
|
||||||
)
|
|
||||||
if not results:
|
if not results:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -427,12 +410,12 @@ def get_matches(
|
|||||||
|
|
||||||
target_range_str = ""
|
target_range_str = ""
|
||||||
if c_range:
|
if c_range:
|
||||||
target_range_str = (
|
target_range_str = fg.li_cyan + f" (line {c_range} in {obj_file.stem})" + fg.rs
|
||||||
fg.li_cyan + f" (line {c_range} in {obj_file.stem})" + fg.rs
|
|
||||||
)
|
|
||||||
|
|
||||||
query_str = f"query [{result.query_start}-{result.query_end}]"
|
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}")
|
print(f"\t{query_str} matches {target_str}")
|
||||||
|
|
||||||
if show_disasm:
|
if show_disasm:
|
||||||
@ -441,20 +424,12 @@ def get_matches(
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
print("rabbitizer not found, cannot show disassembly")
|
print("rabbitizer not found, cannot show disassembly")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
result_query_bytes = query_bytes.bytes[
|
result_query_bytes = query_bytes.bytes[result.query_start * 4 : result.query_end * 4]
|
||||||
result.query_start * 4 : result.query_end * 4
|
result_target_bytes = sym_bytes.bytes[result.target_start * 4 : result.target_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):
|
for i in range(0, len(result_query_bytes), 4):
|
||||||
q_insn = rabbitizer.Instruction(
|
q_insn = rabbitizer.Instruction(int.from_bytes(result_query_bytes[i : i + 4], "big"))
|
||||||
int.from_bytes(result_query_bytes[i : i + 4], "big")
|
t_insn = rabbitizer.Instruction(int.from_bytes(result_target_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()}")
|
print(f"\t\t{q_insn.disassemble():35} | {t_insn.disassemble()}")
|
||||||
|
|
||||||
|
@ -2,20 +2,32 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def get_variable(arg):
|
def get_variable(arg):
|
||||||
v = arg - 2**32 # convert to s32
|
v = arg - 2**32 # convert to s32
|
||||||
if v > -250000000:
|
if v > -250000000:
|
||||||
if v <= -220000000: return f"EVT_FLOAT({(v + 230000000) / 1024})"
|
if v <= -220000000:
|
||||||
elif v <= -200000000: return f"ArrayFlag({v + 210000000})"
|
return f"EVT_FLOAT({(v + 230000000) / 1024})"
|
||||||
elif v <= -180000000: return f"ArrayVar({v + 190000000})"
|
elif v <= -200000000:
|
||||||
elif v <= -160000000: return f"GameByte({v + 170000000})"
|
return f"ArrayFlag({v + 210000000})"
|
||||||
elif v <= -140000000: return f"AreaByte({v + 150000000})"
|
elif v <= -180000000:
|
||||||
elif v <= -120000000: return f"GameFlag({v + 130000000})"
|
return f"ArrayVar({v + 190000000})"
|
||||||
elif v <= -100000000: return f"AreaFlag({v + 110000000})"
|
elif v <= -160000000:
|
||||||
elif v <= -80000000: return f"MapFlag({v + 90000000})"
|
return f"GameByte({v + 170000000})"
|
||||||
elif v <= -60000000: return f"LocalFlag({v + 70000000})"
|
elif v <= -140000000:
|
||||||
elif v <= -40000000: return f"MapVar({v + 50000000})"
|
return f"AreaByte({v + 150000000})"
|
||||||
elif v <= -20000000: return f"LocalVar({v + 30000000})"
|
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:
|
if arg == 0xFFFFFFFF:
|
||||||
return "-1"
|
return "-1"
|
||||||
@ -26,6 +38,7 @@ def get_variable(arg):
|
|||||||
else:
|
else:
|
||||||
return f"{arg}"
|
return f"{arg}"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
print(get_variable(int(sys.argv[1], 0)))
|
print(get_variable(int(sys.argv[1], 0)))
|
||||||
|
@ -20,14 +20,14 @@ CPP_FLAGS = [
|
|||||||
"-D_LANGUAGE_C",
|
"-D_LANGUAGE_C",
|
||||||
"-DF3DEX_GBI_2",
|
"-DF3DEX_GBI_2",
|
||||||
"-D_MIPS_SZLONG=32",
|
"-D_MIPS_SZLONG=32",
|
||||||
"-DSCRIPT(test...)={}"
|
"-DSCRIPT(test...)={}" "-D__attribute__(test...)=",
|
||||||
"-D__attribute__(test...)=",
|
|
||||||
"-D__asm__(test...)=",
|
"-D__asm__(test...)=",
|
||||||
"-ffreestanding",
|
"-ffreestanding",
|
||||||
"-DM2CTX",
|
"-DM2CTX",
|
||||||
"-DVERSION_PAL",
|
"-DVERSION_PAL",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def import_c_file(in_file) -> str:
|
def import_c_file(in_file) -> str:
|
||||||
in_file = os.path.relpath(in_file, root_dir)
|
in_file = os.path.relpath(in_file, root_dir)
|
||||||
cpp_command = ["gcc", "-E", "-P", "-dM", *CPP_FLAGS, in_file]
|
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")
|
out_text += subprocess.check_output(cpp_command2, cwd=root_dir, encoding="utf-8")
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
print(
|
print(
|
||||||
"Failed to preprocess input file, when running command:\n"
|
"Failed to preprocess input file, when running command:\n" + cpp_command,
|
||||||
+ cpp_command,
|
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not out_text:
|
if not out_text:
|
||||||
@ -56,10 +55,9 @@ def import_c_file(in_file) -> str:
|
|||||||
out_text = out_text.replace(line + "\n", "")
|
out_text = out_text.replace(line + "\n", "")
|
||||||
return out_text
|
return out_text
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="""Create a context file which can be used for mips_to_c""")
|
||||||
description="""Create a context file which can be used for mips_to_c"""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"c_file",
|
"c_file",
|
||||||
help="""File from which to create context""",
|
help="""File from which to create context""",
|
||||||
|
@ -22,7 +22,7 @@ def data_to_c(file_path):
|
|||||||
output = ""
|
output = ""
|
||||||
pattern = re.compile(r"(dlabel (jtbl_.*|.+_.*)\n.(\w+) (.*))")
|
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 type == "word":
|
||||||
if symbol.startswith("jtbl_"):
|
if symbol.startswith("jtbl_"):
|
||||||
output += "dlabel " + symbol + "\n" + ".word " + data.replace("L", ".L") + "\n\n"
|
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):
|
if not os.path.exists(output_dir):
|
||||||
os.mkdir(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 = open("data2c/" + file_name + ".c", "w+")
|
||||||
file.write(output)
|
file.write(output)
|
||||||
file.close()
|
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 = 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("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(
|
||||||
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)
|
"--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()
|
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}"):
|
if os.path.exists(f"ver/current/asm/nonmatchings/{args.src}"):
|
||||||
print("moving asm/nonmatchings files")
|
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"):
|
if os.path.exists(f"ver/current/asm/data/{args.src}.data.s"):
|
||||||
print("moving data file")
|
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"):
|
if os.path.exists(f"ver/current/asm/data/{args.src}.rodata.s"):
|
||||||
print("moving rodata file")
|
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, "../.."))
|
root_dir = os.path.abspath(os.path.join(script_dir, "../.."))
|
||||||
|
|
||||||
import glob, os
|
import glob, os
|
||||||
|
|
||||||
os.chdir(root_dir)
|
os.chdir(root_dir)
|
||||||
|
|
||||||
for f in Path(root_dir).rglob("*.bin"):
|
for f in Path(root_dir).rglob("*.bin"):
|
||||||
@ -20,7 +21,10 @@ for f in Path(root_dir).rglob("*.bin"):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
ras = []
|
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")
|
output = result.stdout.decode().split("\n")
|
||||||
|
|
||||||
for line in output:
|
for line in output:
|
||||||
|
@ -18,7 +18,7 @@ for line in inlines:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if area:
|
if area:
|
||||||
fname = line[line.rfind("`") + 1:line.rfind("'")]
|
fname = line[line.rfind("`") + 1 : line.rfind("'")]
|
||||||
renames[fname] = area
|
renames[fname] = area
|
||||||
|
|
||||||
pairs = []
|
pairs = []
|
||||||
|
@ -28,7 +28,7 @@ for filename, line_number, bad_symbol_name in problems:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if old_line.startswith("N(") or old_line.startswith("await N("):
|
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:
|
else:
|
||||||
good_symbol_name = old_line.split("(", 1)[0]
|
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:
|
with open(filename, "w") as f:
|
||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def auto_int(x):
|
def auto_int(x):
|
||||||
return int(x, 0)
|
return int(x, 0)
|
||||||
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Generate rename file for effects")
|
||||||
description="Generate rename file for effects"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"id",
|
"id",
|
||||||
@ -23,6 +23,7 @@ parser.add_argument(
|
|||||||
help="Name (in snake case) to change the effect to",
|
help="Name (in snake case) to change the effect to",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
id = args.id
|
id = args.id
|
||||||
to = args.to
|
to = args.to
|
||||||
@ -31,7 +32,7 @@ def main(args):
|
|||||||
|
|
||||||
hex_str = f"{id:02x}".upper()
|
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"Effect{id} {struct_name}FXData")
|
||||||
to_write.append(f"playFX_{hex_str} fx_{to}")
|
to_write.append(f"playFX_{hex_str} fx_{to}")
|
||||||
to_write.append(f"FX_ENTRY_NUMBERED({id}, FX_ENTRY({to},")
|
to_write.append(f"FX_ENTRY_NUMBERED({id}, FX_ENTRY({to},")
|
||||||
@ -47,6 +48,7 @@ def main(args):
|
|||||||
for line in to_write:
|
for line in to_write:
|
||||||
f.write(f"{line}\n")
|
f.write(f"{line}\n")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
main(args)
|
main(args)
|
||||||
|
@ -6,8 +6,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("baserom")
|
parser.add_argument("baserom")
|
||||||
parser.add_argument("start", 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))
|
parser.add_argument("end", type=lambda x: int(x, 0))
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
baserom_path = Path(__file__).parent.parent / "baserom.z64"
|
baserom_path = Path(__file__).parent.parent / "baserom.z64"
|
||||||
@ -25,9 +25,13 @@ while i < args.end:
|
|||||||
while unpack_from("B", baserom, i)[0] == 0:
|
while unpack_from("B", baserom, i)[0] == 0:
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
#print(f"Start {hex(dis_start)} end {hex(i)}")
|
# 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()}",
|
gfxdis = subprocess.run(
|
||||||
capture_output=True, shell=True, text=True)
|
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]
|
commands = gfxdis.stdout.splitlines()[1:-1]
|
||||||
new_commands = []
|
new_commands = []
|
||||||
|
@ -3,11 +3,23 @@ import argparse
|
|||||||
from struct import unpack_from
|
from struct import unpack_from
|
||||||
|
|
||||||
CONSTANTS = {}
|
CONSTANTS = {}
|
||||||
|
|
||||||
|
|
||||||
def get_constants():
|
def get_constants():
|
||||||
global CONSTANTS
|
global CONSTANTS
|
||||||
valid_enums = { "StoryProgress", "ItemIDs", "PlayerAnims",
|
valid_enums = {
|
||||||
"ActorIDs", "Events", "SoundIDs", "SongIDs", "Locations",
|
"StoryProgress",
|
||||||
"AmbientSounds", "NpcIDs", "Emotes" }
|
"ItemIDs",
|
||||||
|
"PlayerAnims",
|
||||||
|
"ActorIDs",
|
||||||
|
"Events",
|
||||||
|
"SoundIDs",
|
||||||
|
"SongIDs",
|
||||||
|
"Locations",
|
||||||
|
"AmbientSounds",
|
||||||
|
"NpcIDs",
|
||||||
|
"Emotes",
|
||||||
|
}
|
||||||
for enum in valid_enums:
|
for enum in valid_enums:
|
||||||
CONSTANTS[enum] = {}
|
CONSTANTS[enum] = {}
|
||||||
CONSTANTS["NPC_SPRITE"] = {}
|
CONSTANTS["NPC_SPRITE"] = {}
|
||||||
@ -16,7 +28,7 @@ def get_constants():
|
|||||||
enums = Path(include_path / "enums.h").read_text().splitlines()
|
enums = Path(include_path / "enums.h").read_text().splitlines()
|
||||||
|
|
||||||
# defines
|
# defines
|
||||||
'''
|
"""
|
||||||
for line in enums.splitlines():
|
for line in enums.splitlines():
|
||||||
this_enum = ""
|
this_enum = ""
|
||||||
for enum in valid_enums:
|
for enum in valid_enums:
|
||||||
@ -31,12 +43,12 @@ def get_constants():
|
|||||||
id_ = id_.split(" ",1)[0]
|
id_ = id_.split(" ",1)[0]
|
||||||
CONSTANTS[this_enum][int(id_, 16)] = name
|
CONSTANTS[this_enum][int(id_, 16)] = name
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
# enums
|
# enums
|
||||||
for i,line in enumerate(enums):
|
for i, line in enumerate(enums):
|
||||||
if line.startswith("enum "):
|
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:
|
if enum_name in valid_enums:
|
||||||
CONSTANTS[enum_name] = {}
|
CONSTANTS[enum_name] = {}
|
||||||
last_num = 0
|
last_num = 0
|
||||||
@ -47,7 +59,7 @@ def get_constants():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
name = enums[i].strip()
|
name = enums[i].strip()
|
||||||
val = last_num+1
|
val = last_num + 1
|
||||||
if "=" in name:
|
if "=" in name:
|
||||||
name, val = name.split(" = ")
|
name, val = name.split(" = ")
|
||||||
val = int(val[:-1], 0)
|
val = int(val[:-1], 0)
|
||||||
@ -56,14 +68,16 @@ def get_constants():
|
|||||||
else:
|
else:
|
||||||
name = name[:-1]
|
name = name[:-1]
|
||||||
name = name.strip()
|
name = name.strip()
|
||||||
#print("\"" + name + "\"", "===", val)
|
# print("\"" + name + "\"", "===", val)
|
||||||
|
|
||||||
CONSTANTS[enum_name][val] = name.strip()
|
CONSTANTS[enum_name][val] = name.strip()
|
||||||
i += 1
|
i += 1
|
||||||
last_num = val
|
last_num = val
|
||||||
|
|
||||||
# sprites
|
# 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():
|
for file in sprite_path.iterdir():
|
||||||
fd = file.read_text()
|
fd = file.read_text()
|
||||||
for line in fd.splitlines():
|
for line in fd.splitlines():
|
||||||
@ -76,10 +90,10 @@ def get_constants():
|
|||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = line.split(" ",2)[1]
|
name = line.split(" ", 2)[1]
|
||||||
id_ = line.split("0x", 1)[1]
|
id_ = line.split("0x", 1)[1]
|
||||||
if " " in id_:
|
if " " in id_:
|
||||||
id_ = id_.split(" ",1)[0]
|
id_ = id_.split(" ", 1)[0]
|
||||||
name = name.split(f"_{enum}_", 1)[1]
|
name = name.split(f"_{enum}_", 1)[1]
|
||||||
if enum == "NPC_SPRITE":
|
if enum == "NPC_SPRITE":
|
||||||
saved_name = name
|
saved_name = name
|
||||||
@ -89,7 +103,11 @@ def get_constants():
|
|||||||
|
|
||||||
if enum == "NPC_SPRITE":
|
if enum == "NPC_SPRITE":
|
||||||
if int(id_, 16) not in CONSTANTS["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
|
CONSTANTS[enum][int(id_, 16)]["name"] = name
|
||||||
elif enum == "NPC_PALETTE":
|
elif enum == "NPC_PALETTE":
|
||||||
CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["palettes"][int(id_, 16)] = name
|
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
|
CONSTANTS["NPC_SPRITE"][int(saved_id, 16)]["anims"][int(id_, 16)] = name
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
STRUCTS = {}
|
STRUCTS = {}
|
||||||
|
|
||||||
|
|
||||||
def parse_var(line):
|
def parse_var(line):
|
||||||
#print(f"Parsing {line}")
|
# print(f"Parsing {line}")
|
||||||
if "*/ " in line:
|
if "*/ " in line:
|
||||||
line = line.split("*/ ",1)[1]
|
line = line.split("*/ ", 1)[1]
|
||||||
line = line.split(";",1)[0].strip()
|
line = line.split(";", 1)[0].strip()
|
||||||
|
|
||||||
if "," in line or "(*" in line:
|
if "," in line or "(*" in line:
|
||||||
return (None, None, None, None)
|
return (None, None, None, None)
|
||||||
elif "union " in line:
|
elif "union " in line:
|
||||||
return ("union", None, None, None)
|
return ("union", None, None, None)
|
||||||
|
|
||||||
#print(f"Parsed {line}")
|
# print(f"Parsed {line}")
|
||||||
if " " in line:
|
if " " in line:
|
||||||
if line.startswith("struct "):
|
if line.startswith("struct "):
|
||||||
struct, type_, name = line.split(" ")
|
struct, type_, name = line.split(" ")
|
||||||
@ -131,22 +151,23 @@ def parse_var(line):
|
|||||||
is_ptr = "*" in type_ or type_ == "UNK_PTR"
|
is_ptr = "*" in type_ or type_ == "UNK_PTR"
|
||||||
return (type_, name, count, is_ptr)
|
return (type_, name, count, is_ptr)
|
||||||
|
|
||||||
|
|
||||||
def parse_file(filename):
|
def parse_file(filename):
|
||||||
fd = filename.read_text().splitlines()
|
fd = filename.read_text().splitlines()
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(fd):
|
while i < len(fd):
|
||||||
#supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS]
|
# supported = [f"typedef struct {x}" in fd[i] for x in SUPPORTED_STRUCTS]
|
||||||
#if any(supported):
|
# if any(supported):
|
||||||
if "typedef struct " in fd[i]:
|
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]
|
supported_name = fd[i].split("typedef struct ", 1)[1].split(" {", 1)[0]
|
||||||
if supported_name == "{":
|
if supported_name == "{":
|
||||||
supported_name = ""
|
supported_name = ""
|
||||||
#print(f"Parsing struct \"{supported_name}\"")
|
# print(f"Parsing struct \"{supported_name}\"")
|
||||||
|
|
||||||
struct_to_add = []
|
struct_to_add = []
|
||||||
i += 1
|
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])
|
type_, name, count, ptr = parse_var(fd[i])
|
||||||
|
|
||||||
if type_ == None:
|
if type_ == None:
|
||||||
@ -158,27 +179,37 @@ def parse_file(filename):
|
|||||||
i += 1
|
i += 1
|
||||||
while "}" not in fd[i]:
|
while "}" not in fd[i]:
|
||||||
type_, name, count, ptr = parse_var(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
|
i += 1
|
||||||
name = fd[i].split("}", 1)[1].split(";", 1)[0]
|
name = fd[i].split("}", 1)[1].split(";", 1)[0]
|
||||||
|
|
||||||
#print(supported_name, type_, name, count)
|
# print(supported_name, type_, name, count)
|
||||||
struct_to_add.append({"type":type_, "name": name, "num":count, "ptr":ptr, "union":union})
|
struct_to_add.append(
|
||||||
|
{
|
||||||
|
"type": type_,
|
||||||
|
"name": name,
|
||||||
|
"num": count,
|
||||||
|
"ptr": ptr,
|
||||||
|
"union": union,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
#print(f"Broke on line {fd[i]}")
|
# print(f"Broke on line {fd[i]}")
|
||||||
#print()
|
# print()
|
||||||
if supported_name == "":
|
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:
|
if "[" in supported_name:
|
||||||
supported_name = supported_name[:-2]
|
supported_name = supported_name[:-2]
|
||||||
STRUCTS[supported_name] = struct_to_add
|
STRUCTS[supported_name] = struct_to_add
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
def get_structs():
|
def get_structs():
|
||||||
parse_file(Path(Path(__file__).parent.parent / "include" / "map.h"))
|
parse_file(Path(Path(__file__).parent.parent / "include" / "map.h"))
|
||||||
parse_file(Path(Path(__file__).parent.parent / "include" / "common_structs.h"))
|
parse_file(Path(Path(__file__).parent.parent / "include" / "common_structs.h"))
|
||||||
|
|
||||||
|
|
||||||
def get_vals(fd, offset, var):
|
def get_vals(fd, offset, var):
|
||||||
global STRUCTS
|
global STRUCTS
|
||||||
|
|
||||||
@ -192,63 +223,63 @@ def get_vals(fd, offset, var):
|
|||||||
for var2 in STRUCTS[var["type"]]:
|
for var2 in STRUCTS[var["type"]]:
|
||||||
out3, offset = get_vals(fd, offset, var2)
|
out3, offset = get_vals(fd, offset, var2)
|
||||||
data.extend(out3)
|
data.extend(out3)
|
||||||
#if var["num"] == 1:
|
# if var["num"] == 1:
|
||||||
# out.extend(out2)
|
# out.extend(out2)
|
||||||
#else:
|
# else:
|
||||||
#out.append(out2)
|
# out.append(out2)
|
||||||
else:
|
else:
|
||||||
type_ = "int"
|
type_ = "int"
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
if var["type"] == "s8" or var["type"] == "char":
|
if var["type"] == "s8" or var["type"] == "char":
|
||||||
if var["type"] == "char":
|
if var["type"] == "char":
|
||||||
type_ = "hex"
|
type_ = "hex"
|
||||||
fmt = "X"
|
fmt = "X"
|
||||||
data = unpack_from('>b', fd, offset)[0]
|
data = unpack_from(">b", fd, offset)[0]
|
||||||
offset += 1
|
offset += 1
|
||||||
elif var["type"] == "u8":
|
elif var["type"] == "u8":
|
||||||
data = unpack_from('>B', fd, offset)[0]
|
data = unpack_from(">B", fd, offset)[0]
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
offset += 1
|
offset += 1
|
||||||
elif var["type"] == "s16" or var["type"] in ("s16"):
|
elif var["type"] == "s16" or var["type"] in ("s16"):
|
||||||
offset += offset % 2
|
offset += offset % 2
|
||||||
data = unpack_from('>h', fd, offset)[0]
|
data = unpack_from(">h", fd, offset)[0]
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
offset += 2
|
offset += 2
|
||||||
elif var["type"] == "u16":
|
elif var["type"] == "u16":
|
||||||
offset += offset % 2
|
offset += offset % 2
|
||||||
data = unpack_from('>H', fd, offset)[0]
|
data = unpack_from(">H", fd, offset)[0]
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
offset += 2
|
offset += 2
|
||||||
elif var["type"] == "s32" or var["type"] in ("s32"):
|
elif var["type"] == "s32" or var["type"] in ("s32"):
|
||||||
poff = offset
|
poff = offset
|
||||||
offset += offset % 4
|
offset += offset % 4
|
||||||
data = unpack_from('>i', fd, offset)[0]
|
data = unpack_from(">i", fd, offset)[0]
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
offset += 4
|
offset += 4
|
||||||
elif var["type"] == "u32":
|
elif var["type"] == "u32":
|
||||||
offset += offset % 4
|
offset += offset % 4
|
||||||
data = unpack_from('>I', fd, offset)[0]
|
data = unpack_from(">I", fd, offset)[0]
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
offset += 4
|
offset += 4
|
||||||
elif var["type"] == "f32":
|
elif var["type"] == "f32":
|
||||||
offset += offset % 4
|
offset += offset % 4
|
||||||
data = unpack_from('>f', fd, offset)[0]
|
data = unpack_from(">f", fd, offset)[0]
|
||||||
type_ = "float"
|
type_ = "float"
|
||||||
fmt = ".01f"
|
fmt = ".01f"
|
||||||
offset += 4
|
offset += 4
|
||||||
elif var["type"] == "X32":
|
elif var["type"] == "X32":
|
||||||
offset += offset % 4
|
offset += offset % 4
|
||||||
data = unpack_from('>f', fd, offset)[0]
|
data = unpack_from(">f", fd, offset)[0]
|
||||||
type_ = "Xfloat"
|
type_ = "Xfloat"
|
||||||
fmt = ".01f"
|
fmt = ".01f"
|
||||||
if data < -1000.0 or data > 1000.0:
|
if data < -1000.0 or data > 1000.0:
|
||||||
type_ = "Xint"
|
type_ = "Xint"
|
||||||
fmt = "d"
|
fmt = "d"
|
||||||
data = unpack_from('>i', fd, offset)[0]
|
data = unpack_from(">i", fd, offset)[0]
|
||||||
offset += 4
|
offset += 4
|
||||||
elif var["ptr"]:
|
elif var["ptr"]:
|
||||||
offset += offset % 4
|
offset += offset % 4
|
||||||
data = unpack_from('>I', fd, offset)[0]
|
data = unpack_from(">I", fd, offset)[0]
|
||||||
type_ = "ptr"
|
type_ = "ptr"
|
||||||
fmt = "08X"
|
fmt = "08X"
|
||||||
offset += 4
|
offset += 4
|
||||||
@ -256,24 +287,26 @@ def get_vals(fd, offset, var):
|
|||||||
print(f"Unknown data type \"{var['type']}\"")
|
print(f"Unknown data type \"{var['type']}\"")
|
||||||
exit()
|
exit()
|
||||||
if var["num"] == 1:
|
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:
|
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:
|
if var["num"] > 1:
|
||||||
out.append(arr)
|
out.append(arr)
|
||||||
return out, offset
|
return out, offset
|
||||||
|
|
||||||
|
|
||||||
def INDENT(depth):
|
def INDENT(depth):
|
||||||
return f" " * depth
|
return f" " * depth
|
||||||
|
|
||||||
|
|
||||||
def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
||||||
out = []
|
out = []
|
||||||
for val in vals:
|
for val in vals:
|
||||||
line = ""
|
line = ""
|
||||||
if needs_name:
|
if needs_name:
|
||||||
line = INDENT(indent)
|
line = INDENT(indent)
|
||||||
#print(val)
|
# print(val)
|
||||||
# array
|
# array
|
||||||
if type(val) is list:
|
if type(val) is list:
|
||||||
line += f".{val[0]['name']} = " + "{ "
|
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 += "\n"
|
||||||
line += INDENT(indent)
|
line += INDENT(indent)
|
||||||
line += "{ "
|
line += "{ "
|
||||||
for x,val2 in enumerate(val["data"]):
|
for x, val2 in enumerate(val["data"]):
|
||||||
if x > 0:
|
if x > 0:
|
||||||
line += ", "
|
line += ", "
|
||||||
#line += f".{val2['name']} = "
|
# line += f".{val2['name']} = "
|
||||||
fmt = val2["fmt"]
|
fmt = val2["fmt"]
|
||||||
if val2["type"] == "float":
|
if val2["type"] == "float":
|
||||||
line += f"{val2['data']:{fmt}}f"
|
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:
|
if not is_array:
|
||||||
line += ","
|
line += ","
|
||||||
else:
|
else:
|
||||||
|
|
||||||
if "flags" in val["name"].lower() or "animations" in val["name"].lower():
|
if "flags" in val["name"].lower() or "animations" in val["name"].lower():
|
||||||
if val["name"] == "flags":
|
if val["name"] == "flags":
|
||||||
val["fmt"] = "08X"
|
val["fmt"] = "08X"
|
||||||
@ -338,10 +370,10 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
|||||||
elif val["type"] == "hex":
|
elif val["type"] == "hex":
|
||||||
line += f"0x{val['data']:{fmt}}"
|
line += f"0x{val['data']:{fmt}}"
|
||||||
elif val["type"] == "ptr":
|
elif val["type"] == "ptr":
|
||||||
if val["data"] == 0:
|
if val["data"] == 0:
|
||||||
line += f"NULL"
|
line += f"NULL"
|
||||||
else:
|
else:
|
||||||
line += f"0x{val['data']:{fmt}}"
|
line += f"0x{val['data']:{fmt}}"
|
||||||
else:
|
else:
|
||||||
line += f"{val['data']}"
|
line += f"{val['data']}"
|
||||||
|
|
||||||
@ -352,6 +384,7 @@ def print_data(vals, indent, needs_name, is_array=False, is_struct=False):
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def output_type2(fd, count, offset, var):
|
def output_type2(fd, count, offset, var):
|
||||||
ultra_out = []
|
ultra_out = []
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
@ -364,10 +397,11 @@ def output_type2(fd, count, offset, var):
|
|||||||
out.extend(print_data(vals, 1, True))
|
out.extend(print_data(vals, 1, True))
|
||||||
out.append("};")
|
out.append("};")
|
||||||
ultra_out.append(out)
|
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 type(val) == list:
|
||||||
if check_list(val, depth + 1):
|
if check_list(val, depth + 1):
|
||||||
return True
|
return True
|
||||||
@ -375,9 +409,10 @@ def check_list(vals, depth = 0):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def recurse_check_list(vals):
|
def recurse_check_list(vals):
|
||||||
res = 0
|
res = 0
|
||||||
for x,val in enumerate(vals):
|
for x, val in enumerate(vals):
|
||||||
if type(val) == list:
|
if type(val) == list:
|
||||||
if check_list(val, 1):
|
if check_list(val, 1):
|
||||||
return len(vals) - x
|
return len(vals) - x
|
||||||
@ -385,14 +420,15 @@ def recurse_check_list(vals):
|
|||||||
return len(vals) - x
|
return len(vals) - x
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def get_single_struct_vals(fd, i):
|
def get_single_struct_vals(fd, i):
|
||||||
vals = []
|
vals = []
|
||||||
if not fd[i].rstrip().endswith("},"):
|
if not fd[i].rstrip().endswith("},"):
|
||||||
# must be a sub-struct over multiple lines
|
# must be a sub-struct over multiple lines
|
||||||
old_i = i
|
old_i = i
|
||||||
i += 1
|
i += 1
|
||||||
while not ("}," in fd[i] and "." in fd[i+1]):
|
while not ("}," in fd[i] and "." in fd[i + 1]):
|
||||||
temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ")
|
temp = fd[i].split("{", 1)[1].split("}", 1)[0].split(", ")
|
||||||
a = []
|
a = []
|
||||||
for x in temp:
|
for x in temp:
|
||||||
x = x.strip()
|
x = x.strip()
|
||||||
@ -405,7 +441,7 @@ def get_single_struct_vals(fd, i):
|
|||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
# single line
|
# single line
|
||||||
temp = fd[i].split("{",1)[1].split("}",1)[0].split(", ")
|
temp = fd[i].split("{", 1)[1].split("}", 1)[0].split(", ")
|
||||||
a = []
|
a = []
|
||||||
for x in temp:
|
for x in temp:
|
||||||
x = x.strip()
|
x = x.strip()
|
||||||
@ -417,10 +453,11 @@ def get_single_struct_vals(fd, i):
|
|||||||
vals.extend(a)
|
vals.extend(a)
|
||||||
return vals, i
|
return vals, i
|
||||||
|
|
||||||
|
|
||||||
def cull_struct(fd, i, entirely=False):
|
def cull_struct(fd, i, entirely=False):
|
||||||
out = []
|
out = []
|
||||||
vals = []
|
vals = []
|
||||||
#print(f"Culling Starting at {fd[i]}")
|
# print(f"Culling Starting at {fd[i]}")
|
||||||
if not fd[i].rstrip().endswith("},"):
|
if not fd[i].rstrip().endswith("},"):
|
||||||
# must be a sub-struct over multiple lines
|
# must be a sub-struct over multiple lines
|
||||||
old_i = i
|
old_i = i
|
||||||
@ -428,9 +465,9 @@ def cull_struct(fd, i, entirely=False):
|
|||||||
|
|
||||||
# reverse and cull entries of only zeros
|
# reverse and cull entries of only zeros
|
||||||
x = recurse_check_list(vals[::-1])
|
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:
|
if x < 0:
|
||||||
#print(f"Ending at {fd[i]}")
|
# print(f"Ending at {fd[i]}")
|
||||||
return None, i
|
return None, i
|
||||||
|
|
||||||
out.append(fd[old_i])
|
out.append(fd[old_i])
|
||||||
@ -441,36 +478,37 @@ def cull_struct(fd, i, entirely=False):
|
|||||||
out.append(fd[old_i])
|
out.append(fd[old_i])
|
||||||
old_i += 1
|
old_i += 1
|
||||||
|
|
||||||
#print(f"Ending at {fd[i]}")
|
# print(f"Ending at {fd[i]}")
|
||||||
else:
|
else:
|
||||||
prefix = fd[i].split("{",1)[0] + "{ "
|
prefix = fd[i].split("{", 1)[0] + "{ "
|
||||||
suffix = " },"
|
suffix = " },"
|
||||||
|
|
||||||
vals, i = get_single_struct_vals(fd, i)
|
vals, i = get_single_struct_vals(fd, i)
|
||||||
|
|
||||||
# reverse and cull entries of only zeros
|
# reverse and cull entries of only zeros
|
||||||
x = recurse_check_list(vals[::-1])
|
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:
|
if x < 0:
|
||||||
#print(f"Ending at {fd[i]}")
|
# print(f"Ending at {fd[i]}")
|
||||||
return None, i
|
return None, i
|
||||||
|
|
||||||
#out.append(prefix)
|
# out.append(prefix)
|
||||||
if entirely:
|
if entirely:
|
||||||
x = len(vals)
|
x = len(vals)
|
||||||
temp = ""
|
temp = ""
|
||||||
for z,y in enumerate(range(x)):
|
for z, y in enumerate(range(x)):
|
||||||
if z > 0:
|
if z > 0:
|
||||||
prefix += ", "
|
prefix += ", "
|
||||||
prefix += f"{vals[y]}"
|
prefix += f"{vals[y]}"
|
||||||
out.append(prefix + suffix)
|
out.append(prefix + suffix)
|
||||||
#print(f"Ending at {fd[i]}")
|
# print(f"Ending at {fd[i]}")
|
||||||
return "\n".join(out), i
|
return "\n".join(out), i
|
||||||
|
|
||||||
|
|
||||||
def MacroReplaceStaticNPC(fd):
|
def MacroReplaceStaticNPC(fd):
|
||||||
structs = { "unk_1C":True, "movement":False, "unk_1E0":True }
|
structs = {"unk_1C": True, "movement": False, "unk_1E0": True}
|
||||||
#replace_cull_struct = { "unk_1C", "movement", "unk_1E0" }
|
# replace_cull_struct = { "unk_1C", "movement", "unk_1E0" }
|
||||||
#replace_cull = { "tattle", "extraAnimations", "itemDropChance" }
|
# replace_cull = { "tattle", "extraAnimations", "itemDropChance" }
|
||||||
fd = fd.splitlines()
|
fd = fd.splitlines()
|
||||||
out = []
|
out = []
|
||||||
i = 0
|
i = 0
|
||||||
@ -479,7 +517,7 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
for x in structs:
|
for x in structs:
|
||||||
if f".{x}" in fd[i]:
|
if f".{x}" in fd[i]:
|
||||||
found = x
|
found = x
|
||||||
break;
|
break
|
||||||
if found:
|
if found:
|
||||||
# just cull it if possible
|
# just cull it if possible
|
||||||
vals, i = cull_struct(fd, i, structs[found])
|
vals, i = cull_struct(fd, i, structs[found])
|
||||||
@ -489,9 +527,9 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
i += 1
|
i += 1
|
||||||
continue
|
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 != ";":
|
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:
|
if "0x" in val:
|
||||||
val = int(val, 16)
|
val = int(val, 16)
|
||||||
elif "NULL" in val:
|
elif "NULL" in val:
|
||||||
@ -507,8 +545,8 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
if ".itemDrops" in fd[i]:
|
if ".itemDrops" in fd[i]:
|
||||||
vals, x = cull_struct(fd, i)
|
vals, x = cull_struct(fd, i)
|
||||||
|
|
||||||
indent = len(fd[i].split(".",1)[0]) // 4
|
indent = len(fd[i].split(".", 1)[0]) // 4
|
||||||
new_line = fd[i].split("{",1)[0] + "{\n"
|
new_line = fd[i].split("{", 1)[0] + "{\n"
|
||||||
|
|
||||||
if not vals:
|
if not vals:
|
||||||
i = x
|
i = x
|
||||||
@ -525,7 +563,7 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
|
|
||||||
added_item = True
|
added_item = True
|
||||||
item_name = CONSTANTS["ItemIDs"][item[0]]
|
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:
|
if added_item:
|
||||||
new_line += " " * indent + "},"
|
new_line += " " * indent + "},"
|
||||||
@ -534,18 +572,18 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
i = x
|
i = x
|
||||||
|
|
||||||
elif ".animations" in fd[i]:
|
elif ".animations" in fd[i]:
|
||||||
indent = len(fd[i].split(".",1)[0]) // 4
|
indent = len(fd[i].split(".", 1)[0]) // 4
|
||||||
new_line = fd[i].split("{",1)[0] + "{\n"
|
new_line = fd[i].split("{", 1)[0] + "{\n"
|
||||||
|
|
||||||
vals, x = get_single_struct_vals(fd, i)
|
vals, x = get_single_struct_vals(fd, i)
|
||||||
for val in vals:
|
for val in vals:
|
||||||
sprite_id = (val & 0x00FF0000) >> 16
|
sprite_id = (val & 0x00FF0000) >> 16
|
||||||
palette_id = (val & 0x0000FF00) >> 8
|
palette_id = (val & 0x0000FF00) >> 8
|
||||||
anim_id = (val & 0x000000FF) >> 0
|
anim_id = (val & 0x000000FF) >> 0
|
||||||
sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
sprite = CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||||
palette = CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
palette = CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
||||||
anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
anim = CONSTANTS["NPC_SPRITE"][sprite_id]["anims"][anim_id]
|
||||||
new_line += " " * (indent+1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
new_line += " " * (indent + 1) + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||||
new_line += " " * indent + "},"
|
new_line += " " * indent + "},"
|
||||||
out.append(new_line)
|
out.append(new_line)
|
||||||
i = x
|
i = x
|
||||||
@ -553,7 +591,7 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
elif ".heartDrops" in fd[i] or ".flowerDrops" in fd[i]:
|
elif ".heartDrops" in fd[i] or ".flowerDrops" in fd[i]:
|
||||||
vals, x = get_single_struct_vals(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]
|
attempts = vals[0][2]
|
||||||
|
|
||||||
@ -564,10 +602,16 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
new_line += f"GENEROUS_HEART_DROPS({attempts}),"
|
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:
|
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}),"
|
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,"
|
new_line += f"NO_DROPS,"
|
||||||
else:
|
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()
|
exit()
|
||||||
else:
|
else:
|
||||||
if round(vals[0][1] / 327.67, 2) == 50 and round(vals[0][3] / 327.67, 2) == 40:
|
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}),"
|
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:
|
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}),"
|
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,"
|
new_line += f"NO_DROPS,"
|
||||||
else:
|
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()
|
exit()
|
||||||
|
|
||||||
out.append(new_line)
|
out.append(new_line)
|
||||||
@ -590,8 +640,9 @@ def MacroReplaceStaticNPC(fd):
|
|||||||
i += 1
|
i += 1
|
||||||
return "\n".join(out)
|
return "\n".join(out)
|
||||||
|
|
||||||
|
|
||||||
def MacroReplaceNpcSettings(fd):
|
def MacroReplaceNpcSettings(fd):
|
||||||
replace_cull = { "unk_00", "unk_24" }
|
replace_cull = {"unk_00", "unk_24"}
|
||||||
fd = fd.splitlines()
|
fd = fd.splitlines()
|
||||||
out = []
|
out = []
|
||||||
i = 0
|
i = 0
|
||||||
@ -605,6 +656,7 @@ def MacroReplaceNpcSettings(fd):
|
|||||||
i += 1
|
i += 1
|
||||||
return "\n".join(out)
|
return "\n".join(out)
|
||||||
|
|
||||||
|
|
||||||
def MacroReplaceNpcGroupList(fd):
|
def MacroReplaceNpcGroupList(fd):
|
||||||
fd = fd.splitlines()
|
fd = fd.splitlines()
|
||||||
out = []
|
out = []
|
||||||
@ -617,21 +669,31 @@ def MacroReplaceNpcGroupList(fd):
|
|||||||
val = 0
|
val = 0
|
||||||
else:
|
else:
|
||||||
if "0x" in fd[i]:
|
if "0x" in fd[i]:
|
||||||
val = int(fd[i].split(" = ",1)[1][:-1], 16)
|
val = int(fd[i].split(" = ", 1)[1][:-1], 16)
|
||||||
else:
|
else:
|
||||||
val = int(fd[i].split(" = ",1)[1][:-1], 10)
|
val = int(fd[i].split(" = ", 1)[1][:-1], 10)
|
||||||
|
|
||||||
vals.append(val)
|
vals.append(val)
|
||||||
i += 1
|
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)
|
return "\n".join(out)
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("file", type=str, help="File to decompile struct from")
|
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("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("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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
get_constants()
|
get_constants()
|
||||||
@ -641,14 +703,14 @@ if args.type not in STRUCTS:
|
|||||||
print(f"Unknown struct type {args.type}")
|
print(f"Unknown struct type {args.type}")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
'''
|
"""
|
||||||
out = [f"{args.type} = " + "{\n"]
|
out = [f"{args.type} = " + "{\n"]
|
||||||
offset = args.offset
|
offset = args.offset
|
||||||
for var in STRUCTS[args.type]:
|
for var in STRUCTS[args.type]:
|
||||||
line, offset = output_type(fd, offset, var, 1)
|
line, offset = output_type(fd, offset, var, 1)
|
||||||
out.append(line)
|
out.append(line)
|
||||||
out.append("};")
|
out.append("};")
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if args.count == 0:
|
if args.count == 0:
|
||||||
args.count = 1
|
args.count = 1
|
||||||
@ -656,7 +718,7 @@ if args.count == 0:
|
|||||||
fd = Path(args.file).resolve().read_bytes()
|
fd = Path(args.file).resolve().read_bytes()
|
||||||
offset, out = output_type2(fd, args.count, args.offset, STRUCTS[args.type])
|
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)
|
out[i] = "\n".join(entry)
|
||||||
|
|
||||||
print(f"EvtScript range 0x{args.offset:08X} - 0x{offset:08X}")
|
print(f"EvtScript range 0x{args.offset:08X} - 0x{offset:08X}")
|
||||||
|
@ -90,6 +90,7 @@ data_to_thing = {
|
|||||||
"413F20": "star_outline",
|
"413F20": "star_outline",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def handle_symbol(effect, symbol):
|
def handle_symbol(effect, symbol):
|
||||||
for root, dirs, files in os.walk(asm_effects_dir + effect + "/"):
|
for root, dirs, files in os.walk(asm_effects_dir + effect + "/"):
|
||||||
for f_name in files:
|
for f_name in files:
|
||||||
@ -139,6 +140,7 @@ def handle_file(f_path):
|
|||||||
continue
|
continue
|
||||||
migrated = handle_symbol(data_to_thing[effect], symbol)
|
migrated = handle_symbol(data_to_thing[effect], symbol)
|
||||||
|
|
||||||
|
|
||||||
# Walk through asm files and rename stuff
|
# Walk through asm files and rename stuff
|
||||||
print("Walking through asm files")
|
print("Walking through asm files")
|
||||||
for root, dirs, files in os.walk(asm_data_dir):
|
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:
|
with open(c_file_path, "w", newline="\n") as f:
|
||||||
f.write("".join(c_lines))
|
f.write("".join(c_lines))
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ asm_dir = root_dir + "ver/current/asm/"
|
|||||||
asm_world_dir = asm_dir + "nonmatchings/world/"
|
asm_world_dir = asm_dir + "nonmatchings/world/"
|
||||||
asm_data_dir = asm_dir + "data/"
|
asm_data_dir = asm_dir + "data/"
|
||||||
|
|
||||||
|
|
||||||
def handle_symbol(area, symbol):
|
def handle_symbol(area, symbol):
|
||||||
for root, dirs, files in os.walk(asm_world_dir + area[0] + "/" + area[1]):
|
for root, dirs, files in os.walk(asm_world_dir + area[0] + "/" + area[1]):
|
||||||
for f_name in files:
|
for f_name in files:
|
||||||
@ -55,6 +56,7 @@ def handle_file(f_path):
|
|||||||
for symbol in reversed(symbols):
|
for symbol in reversed(symbols):
|
||||||
migrated = handle_symbol(area, symbol)
|
migrated = handle_symbol(area, symbol)
|
||||||
|
|
||||||
|
|
||||||
# Walk through asm files and rename stuff
|
# Walk through asm files and rename stuff
|
||||||
print("Walking through asm files")
|
print("Walking through asm files")
|
||||||
for root, dirs, files in os.walk(asm_data_dir):
|
for root, dirs, files in os.walk(asm_data_dir):
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def parse_folder(path):
|
def parse_folder(path):
|
||||||
for entry in path.iterdir():
|
for entry in path.iterdir():
|
||||||
if entry.is_dir():
|
if entry.is_dir():
|
||||||
@ -10,4 +11,5 @@ def parse_folder(path):
|
|||||||
fd.append("")
|
fd.append("")
|
||||||
entry.write_text("\n".join(fd))
|
entry.write_text("\n".join(fd))
|
||||||
|
|
||||||
|
|
||||||
parse_folder(Path("src"))
|
parse_folder(Path("src"))
|
||||||
|
@ -10,7 +10,7 @@ with open("tools/symz.txt") as f:
|
|||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if line.strip() and not line.startswith("//"):
|
if line.strip() and not line.startswith("//"):
|
||||||
name, addr = line.strip().strip(";").split(" = ")
|
name, addr = line.strip().strip(";").split(" = ")
|
||||||
try :
|
try:
|
||||||
addr = int(addr, 0)
|
addr = int(addr, 0)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
continue
|
||||||
|
@ -12,7 +12,10 @@ src_dir = root_dir + "src/"
|
|||||||
asm_dir = root_dir + "ver/current/asm/"
|
asm_dir = root_dir + "ver/current/asm/"
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Replace many functions with one")
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -47,11 +47,14 @@ def parse_symbol_addrs():
|
|||||||
symbol_addrs = {}
|
symbol_addrs = {}
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
name = line[:line.find(" ")]
|
name = line[: line.find(" ")]
|
||||||
|
|
||||||
attributes = line[line.find("//"):].split(" ")
|
attributes = line[line.find("//") :].split(" ")
|
||||||
ram_addr = int(line[:line.find(";")].split("=")[1].strip(), base=0)
|
ram_addr = int(line[: line.find(";")].split("=")[1].strip(), base=0)
|
||||||
rom_addr = next((int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"), None)
|
rom_addr = next(
|
||||||
|
(int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
symbol_addrs[name] = Symbol(ram_addr, rom_addr)
|
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:
|
if "#define NAMESPACE " in line_content:
|
||||||
namespace = line_content.split(" ")[2].strip()
|
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])
|
# namespace = NAMESPACES.get(filename, filename.split("/")[-2].split(".")[0])
|
||||||
elif namespace_temp is not None:
|
elif namespace_temp is not None:
|
||||||
namespace = namespace_temp[0][:-2]
|
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"
|
lines[range.start] = lines[range.start][:macro_start_idx] + "{\n"
|
||||||
|
|
||||||
# Remove other lines
|
# Remove other lines
|
||||||
lines = lines[:range.start + 1] + lines[range.end + 1:]
|
lines = lines[: range.start + 1] + lines[range.end + 1 :]
|
||||||
|
|
||||||
# Find the symbol
|
# Find the symbol
|
||||||
try:
|
try:
|
||||||
@ -128,7 +131,7 @@ def replace_old_script_macros(filename, symbol_addrs):
|
|||||||
local_symbol_map = {}
|
local_symbol_map = {}
|
||||||
for sym in symbol_addrs:
|
for sym in symbol_addrs:
|
||||||
if sym.startswith(range.namespace):
|
if sym.startswith(range.namespace):
|
||||||
key = "N(" + sym[len(range.namespace)+1:] + ")"
|
key = "N(" + sym[len(range.namespace) + 1 :] + ")"
|
||||||
else:
|
else:
|
||||||
key = sym
|
key = sym
|
||||||
|
|
||||||
@ -143,7 +146,8 @@ def replace_old_script_macros(filename, symbol_addrs):
|
|||||||
|
|
||||||
# Disassemble the script
|
# Disassemble the script
|
||||||
rom.seek(range_sym.rom_addr)
|
rom.seek(range_sym.rom_addr)
|
||||||
evt_code = ScriptDisassembler(rom,
|
evt_code = ScriptDisassembler(
|
||||||
|
rom,
|
||||||
script_name=range.symbol_name,
|
script_name=range.symbol_name,
|
||||||
romstart=range_sym.rom_addr,
|
romstart=range_sym.rom_addr,
|
||||||
prelude=False,
|
prelude=False,
|
||||||
|
@ -16,6 +16,7 @@ renames = {}
|
|||||||
patterns = []
|
patterns = []
|
||||||
deletes = []
|
deletes = []
|
||||||
|
|
||||||
|
|
||||||
def handle_file(f_path, try_rename_file=False):
|
def handle_file(f_path, try_rename_file=False):
|
||||||
with open(f_path) as f:
|
with open(f_path) as f:
|
||||||
f_text_orig = f.read()
|
f_text_orig = f.read()
|
||||||
@ -35,17 +36,18 @@ def handle_file(f_path, try_rename_file=False):
|
|||||||
# replace all matches
|
# replace all matches
|
||||||
for match in matches:
|
for match in matches:
|
||||||
# head part
|
# head part
|
||||||
to_join.append(f_text[pos:match[1]])
|
to_join.append(f_text[pos : match[1]])
|
||||||
to_replace = patterns[match[0]]
|
to_replace = patterns[match[0]]
|
||||||
to_join.append(renames[to_replace])
|
to_join.append(renames[to_replace])
|
||||||
pos = match[2]
|
pos = match[2]
|
||||||
# tail part
|
# tail part
|
||||||
to_join.append(f_text[pos:])
|
to_join.append(f_text[pos:])
|
||||||
f_text = ''.join(to_join);
|
f_text = "".join(to_join)
|
||||||
# save changes
|
# save changes
|
||||||
with open(f_path, "w", newline="\n") as f:
|
with open(f_path, "w", newline="\n") as f:
|
||||||
f.write(f_text)
|
f.write(f_text)
|
||||||
|
|
||||||
|
|
||||||
# Read input file
|
# Read input file
|
||||||
# One valid whitespace-separated find-replace pair is given per line
|
# One valid whitespace-separated find-replace pair is given per line
|
||||||
with open(os.path.join(script_dir, "to_rename.txt")) as f:
|
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]
|
renames[split[0]] = split[1]
|
||||||
patterns.append(split[0])
|
patterns.append(split[0])
|
||||||
elif len(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)
|
ac = ahocorasick_rs.AhoCorasick(patterns, matchkind=MATCHKIND_LEFTMOST_LONGEST)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
def decode(data):
|
def decode(data):
|
||||||
length = 0
|
length = 0
|
||||||
is_dbl_char = False
|
is_dbl_char = False
|
||||||
@ -20,7 +21,8 @@ def decode(data):
|
|||||||
|
|
||||||
length += 1
|
length += 1
|
||||||
|
|
||||||
return data[:length].decode('shift-jis')
|
return data[:length].decode("shift-jis")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from segtypes.n64.gfx import N64SegGfx
|
from segtypes.n64.gfx import N64SegGfx
|
||||||
|
|
||||||
|
|
||||||
class N64SegGfx_common(N64SegGfx):
|
class N64SegGfx_common(N64SegGfx):
|
||||||
def format_sym_name(self, sym):
|
def format_sym_name(self, sym):
|
||||||
return f"N({sym.name[7:]})"
|
return f"N({sym.name[7:]})"
|
||||||
|
@ -67,8 +67,6 @@ glabel {name}
|
|||||||
ret = []
|
ret = []
|
||||||
|
|
||||||
for shim in self.shims:
|
for shim in self.shims:
|
||||||
ret.append(
|
ret.append(LinkerEntry(self, [self.shim_path(shim)], self.shim_path(shim), ".text"))
|
||||||
LinkerEntry(self, [self.shim_path(shim)], self.shim_path(shim), ".text")
|
|
||||||
)
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -13,14 +13,14 @@ script_dir = Path(os.path.dirname(os.path.realpath(__file__)))
|
|||||||
|
|
||||||
|
|
||||||
def indent(elem, level=0):
|
def indent(elem, level=0):
|
||||||
i = "\n" + level*" "
|
i = "\n" + level * " "
|
||||||
if len(elem):
|
if len(elem):
|
||||||
if not elem.text or not elem.text.strip():
|
if not elem.text or not elem.text.strip():
|
||||||
elem.text = i + " "
|
elem.text = i + " "
|
||||||
if not elem.tail or not elem.tail.strip():
|
if not elem.tail or not elem.tail.strip():
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
for elem in elem:
|
for elem in elem:
|
||||||
indent(elem, level+1)
|
indent(elem, level + 1)
|
||||||
if not elem.tail or not elem.tail.strip():
|
if not elem.tail or not elem.tail.strip():
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
else:
|
else:
|
||||||
@ -28,7 +28,7 @@ def indent(elem, level=0):
|
|||||||
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()
|
root = tree.getroot()
|
||||||
indent(root)
|
indent(root)
|
||||||
xml_str = ET.tostring(root, encoding="unicode")
|
xml_str = ET.tostring(root, encoding="unicode")
|
||||||
@ -53,7 +53,7 @@ class N64SegPm_icons(N64Segment):
|
|||||||
with open(script_dir / "icon.yaml") as f:
|
with open(script_dir / "icon.yaml") as f:
|
||||||
self.icons = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
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
|
pos = 0
|
||||||
self.files = []
|
self.files = []
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class N64SegPm_icons(N64Segment):
|
|||||||
|
|
||||||
IconList = ET.Element("Icons")
|
IconList = ET.Element("Icons")
|
||||||
|
|
||||||
for (_, icon) in enumerate(self.icons):
|
for _, icon in enumerate(self.icons):
|
||||||
# read yaml entry
|
# read yaml entry
|
||||||
fmt = icon[0]
|
fmt = icon[0]
|
||||||
name = icon[1]
|
name = icon[1]
|
||||||
|
@ -41,9 +41,7 @@ class N64SegPm_imgfx_data(N64Segment):
|
|||||||
frame: List[Vertex] = []
|
frame: List[Vertex] = []
|
||||||
|
|
||||||
for j in range(vtx_count):
|
for j in range(vtx_count):
|
||||||
x, y, z, u, v, r, g, b, a = struct.unpack(
|
x, y, z, u, v, r, g, b, a = struct.unpack(">hhhBBbbbB", data[pos : pos + 12])
|
||||||
">hhhBBbbbB", data[pos : pos + 12]
|
|
||||||
)
|
|
||||||
pos += 12
|
pos += 12
|
||||||
frame.append(Vertex(j, x, y, z, u, v, r, g, b, a))
|
frame.append(Vertex(j, x, y, z, u, v, r, g, b, a))
|
||||||
|
|
||||||
@ -121,10 +119,7 @@ class N64SegPm_imgfx_data(N64Segment):
|
|||||||
return [
|
return [
|
||||||
LinkerEntry(
|
LinkerEntry(
|
||||||
self,
|
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",
|
options.opts.asset_path / "imgfx" / f"{self.name}.c",
|
||||||
self.get_linker_section(),
|
self.get_linker_section(),
|
||||||
)
|
)
|
||||||
|
@ -135,65 +135,45 @@ class N64SegPm_map_data(N64Segment):
|
|||||||
w = png.Writer(150, 105, palette=parse_palette(bytes[:0x200]))
|
w = png.Writer(150, 105, palette=parse_palette(bytes[:0x200]))
|
||||||
w.write_array(f, bytes[0x200:])
|
w.write_array(f, bytes[0x200:])
|
||||||
elif name == "title_data":
|
elif name == "title_data":
|
||||||
if "ver/us" in str(options.opts.target_path) or "ver/pal" in str(
|
if "ver/us" in str(options.opts.target_path) or "ver/pal" in str(options.opts.target_path):
|
||||||
options.opts.target_path
|
|
||||||
):
|
|
||||||
w = 200
|
w = 200
|
||||||
h = 112
|
h = 112
|
||||||
img = n64img.image.RGBA32(
|
img = n64img.image.RGBA32(data=bytes[0x2210 : 0x2210 + w * h * 4], width=w, height=h)
|
||||||
data=bytes[0x2210 : 0x2210 + w * h * 4], width=w, height=h
|
|
||||||
)
|
|
||||||
img.write(fs_dir / "title/logotype.png")
|
img.write(fs_dir / "title/logotype.png")
|
||||||
|
|
||||||
w = 144
|
w = 144
|
||||||
h = 32
|
h = 32
|
||||||
img = n64img.image.IA8(
|
img = n64img.image.IA8(data=bytes[0x10 : 0x10 + w * h], width=w, height=h)
|
||||||
data=bytes[0x10 : 0x10 + w * h], width=w, height=h
|
|
||||||
)
|
|
||||||
img.write(fs_dir / "title/copyright.png")
|
img.write(fs_dir / "title/copyright.png")
|
||||||
|
|
||||||
w = 128
|
w = 128
|
||||||
h = 32
|
h = 32
|
||||||
img = n64img.image.IA8(
|
img = n64img.image.IA8(data=bytes[0x1210 : 0x1210 + w * h], width=w, height=h)
|
||||||
data=bytes[0x1210 : 0x1210 + w * h], width=w, height=h
|
|
||||||
)
|
|
||||||
img.write(fs_dir / "title/press_start.png")
|
img.write(fs_dir / "title/press_start.png")
|
||||||
else:
|
else:
|
||||||
w = 272
|
w = 272
|
||||||
h = 88
|
h = 88
|
||||||
img = n64img.image.RGBA32(
|
img = n64img.image.RGBA32(data=bytes[0x1830 : 0x1830 + w * h * 4], width=w, height=h)
|
||||||
data=bytes[0x1830 : 0x1830 + w * h * 4], width=w, height=h
|
|
||||||
)
|
|
||||||
img.write(fs_dir / "title/logotype.png")
|
img.write(fs_dir / "title/logotype.png")
|
||||||
|
|
||||||
w = 128
|
w = 128
|
||||||
h = 32
|
h = 32
|
||||||
img = n64img.image.CI4(
|
img = n64img.image.CI4(data=bytes[0x10 : 0x10 + (w * h // 2)], width=w, height=h)
|
||||||
data=bytes[0x10 : 0x10 + (w * h // 2)], width=w, height=h
|
|
||||||
)
|
|
||||||
img.palette = parse_palette(bytes[0x810:0x830])
|
img.palette = parse_palette(bytes[0x810:0x830])
|
||||||
img.write(fs_dir / "title/copyright.png")
|
img.write(fs_dir / "title/copyright.png")
|
||||||
|
|
||||||
w = 128
|
w = 128
|
||||||
h = 32
|
h = 32
|
||||||
img = n64img.image.IA8(
|
img = n64img.image.IA8(data=bytes[0x830 : 0x830 + w * h], width=w, height=h)
|
||||||
data=bytes[0x830 : 0x830 + w * h], width=w, height=h
|
|
||||||
)
|
|
||||||
img.write(fs_dir / "title/press_start.png")
|
img.write(fs_dir / "title/press_start.png")
|
||||||
elif name.endswith("_bg"):
|
elif name.endswith("_bg"):
|
||||||
|
|
||||||
def write_bg_png(bytes, path, header_offset=0):
|
def write_bg_png(bytes, path, header_offset=0):
|
||||||
header = bytes[header_offset : header_offset + 0x10]
|
header = bytes[header_offset : header_offset + 0x10]
|
||||||
|
|
||||||
raster_offset = (
|
raster_offset = int.from_bytes(header[0:4], byteorder="big") - 0x80200000
|
||||||
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
|
||||||
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")
|
width = int.from_bytes(header[12:14], byteorder="big")
|
||||||
height = int.from_bytes(header[14:16], byteorder="big")
|
height = int.from_bytes(header[14:16], byteorder="big")
|
||||||
|
|
||||||
@ -202,9 +182,7 @@ class N64SegPm_map_data(N64Segment):
|
|||||||
w = png.Writer(
|
w = png.Writer(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
palette=parse_palette(
|
palette=parse_palette(bytes[palette_offset : palette_offset + 512]),
|
||||||
bytes[palette_offset : palette_offset + 512]
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
w.write_array(f, bytes[raster_offset:])
|
w.write_array(f, bytes[raster_offset:])
|
||||||
|
|
||||||
@ -212,9 +190,7 @@ class N64SegPm_map_data(N64Segment):
|
|||||||
|
|
||||||
# sbk_bg has an alternative palette
|
# sbk_bg has an alternative palette
|
||||||
if name == "sbk_bg":
|
if name == "sbk_bg":
|
||||||
write_bg_png(
|
write_bg_png(bytes, fs_dir / "bg" / f"{name}.alt.png", header_offset=0x10)
|
||||||
bytes, fs_dir / "bg" / f"{name}.alt.png", header_offset=0x10
|
|
||||||
)
|
|
||||||
elif name.endswith("_tex"):
|
elif name.endswith("_tex"):
|
||||||
TexArchive.extract(bytes, fs_dir / "tex" / name)
|
TexArchive.extract(bytes, fs_dir / "tex" / name)
|
||||||
else:
|
else:
|
||||||
|
@ -188,13 +188,23 @@ CHARSET = {
|
|||||||
0x02: "[Style left]\n",
|
0x02: "[Style left]\n",
|
||||||
0x03: "[Style center]\n",
|
0x03: "[Style center]\n",
|
||||||
0x04: "[Style tattle]\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",
|
0x06: "[Style inspect]\n",
|
||||||
0x07: "[Style sign]\n",
|
0x07: "[Style sign]\n",
|
||||||
0x08: {None: lambda d: (f"[Style lamppost height={d[0]}]\n", 1)},
|
0x08: {None: lambda d: (f"[Style lamppost height={d[0]}]\n", 1)},
|
||||||
0x09: {None: lambda d: (f"[Style postcard index={d[0]}]\n", 1)},
|
0x09: {None: lambda d: (f"[Style postcard index={d[0]}]\n", 1)},
|
||||||
0x0A: "[Style popup]\n",
|
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",
|
0x0D: "[Style narrate]\n",
|
||||||
0x0E: "[Style epilogue]\n",
|
0x0E: "[Style epilogue]\n",
|
||||||
},
|
},
|
||||||
@ -215,17 +225,13 @@ CHARSET = {
|
|||||||
# 0x24: "[color:cyan]",
|
# 0x24: "[color:cyan]",
|
||||||
# 0x25: "[color:green]",
|
# 0x25: "[color:green]",
|
||||||
# 0x26: "[color:yellow]",
|
# 0x26: "[color:yellow]",
|
||||||
|
|
||||||
# 0x00: "[color=normal ctx=diary]",
|
# 0x00: "[color=normal ctx=diary]",
|
||||||
# 0x07: "[color=red ctx=diary]",
|
# 0x07: "[color=red ctx=diary]",
|
||||||
|
|
||||||
# 0x17: "[color=dark ctx=inspect]",
|
# 0x17: "[color=dark ctx=inspect]",
|
||||||
|
|
||||||
# 0x18: "[color=normal ctx=sign]",
|
# 0x18: "[color=normal ctx=sign]",
|
||||||
# 0x19: "[color=red ctx=sign]",
|
# 0x19: "[color=red ctx=sign]",
|
||||||
# 0x1A: "[color=blue ctx=sign]",
|
# 0x1A: "[color=blue ctx=sign]",
|
||||||
# 0x1B: "[color=green ctx=sign]",
|
# 0x1B: "[color=green ctx=sign]",
|
||||||
|
|
||||||
# 0x28: "[color=red ctx=popup]",
|
# 0x28: "[color=red ctx=popup]",
|
||||||
# 0x29: "[color=pink ctx=popup]",
|
# 0x29: "[color=pink ctx=popup]",
|
||||||
# 0x2A: "[color=purple ctx=popup]",
|
# 0x2A: "[color=purple ctx=popup]",
|
||||||
@ -234,7 +240,6 @@ CHARSET = {
|
|||||||
# 0x2D: "[color=green ctx=popup]",
|
# 0x2D: "[color=green ctx=popup]",
|
||||||
# 0x2E: "[color=yellow ctx=popup]",
|
# 0x2E: "[color=yellow ctx=popup]",
|
||||||
# 0x2F: "[color=normal ctx=popup]",
|
# 0x2F: "[color=normal ctx=popup]",
|
||||||
|
|
||||||
None: lambda d: (f"[Color 0x{d[0]:X}]", 1),
|
None: lambda d: (f"[Color 0x{d[0]:X}]", 1),
|
||||||
},
|
},
|
||||||
0x07: "[InputOff]\n",
|
0x07: "[InputOff]\n",
|
||||||
@ -252,9 +257,19 @@ CHARSET = {
|
|||||||
0x13: {None: lambda d: (f"[Down {d[0]}]", 1)},
|
0x13: {None: lambda d: (f"[Down {d[0]}]", 1)},
|
||||||
0x14: {None: lambda d: (f"[Up {d[0]}]", 1)},
|
0x14: {None: lambda d: (f"[Up {d[0]}]", 1)},
|
||||||
0x15: {None: lambda d: (f"[InlineImage index={d[0]}]\n", 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)},
|
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)},
|
0x19: {None: lambda d: (f"[HideImage fadeAmount={d[0]}]\n", 1)},
|
||||||
0x1A: {None: lambda d: (f"[AnimDelay index={d[1]} delay={d[2]}]", 3)},
|
0x1A: {None: lambda d: (f"[AnimDelay index={d[1]} delay={d[2]}]", 3)},
|
||||||
0x1B: {None: lambda d: (f"[AnimLoop {d[0]} {d[1]}]", 2)},
|
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)},
|
0x21: {None: lambda d: (f"[Option {d[0]}]", 1)},
|
||||||
0x22: "[SavePos]",
|
0x22: "[SavePos]",
|
||||||
0x23: "[RestorePos]",
|
0x23: "[RestorePos]",
|
||||||
0x24: {0xFF: {0x05: {
|
0x24: {
|
||||||
0x10: {0x98: {0xFF: {0x25: "[A]"}}},
|
0xFF: {
|
||||||
0x11: {0x99: {0xFF: {0x25: "[B]"}}},
|
0x05: {
|
||||||
0x12: {0xA1: {0xFF: {0x25: "[START]"}}},
|
0x10: {0x98: {0xFF: {0x25: "[A]"}}},
|
||||||
0x13: {
|
0x11: {0x99: {0xFF: {0x25: "[B]"}}},
|
||||||
0x9D: {0xFF: {0x25: "[C-UP]"}},
|
0x12: {0xA1: {0xFF: {0x25: "[START]"}}},
|
||||||
0x9E: {0xFF: {0x25: "[C-DOWN]"}},
|
0x13: {
|
||||||
0x9F: {0xFF: {0x25: "[C-LEFT]"}},
|
0x9D: {0xFF: {0x25: "[C-UP]"}},
|
||||||
0xA0: {0xFF: {0x25: "[C-RIGHT]"}},
|
0x9E: {0xFF: {0x25: "[C-DOWN]"}},
|
||||||
},
|
0x9F: {0xFF: {0x25: "[C-LEFT]"}},
|
||||||
0x14: {0x9C: {0xFF: {0x25: "[Z]"}}},
|
0xA0: {0xFF: {0x25: "[C-RIGHT]"}},
|
||||||
}}},
|
},
|
||||||
#0x24: "[SaveColor]",
|
0x14: {0x9C: {0xFF: {0x25: "[Z]"}}},
|
||||||
#0x25: "[RestoreColor]",
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
# 0x24: "[SaveColor]",
|
||||||
|
# 0x25: "[RestoreColor]",
|
||||||
0x26: {
|
0x26: {
|
||||||
0x00: "[Shake]",
|
0x00: "[Shake]",
|
||||||
0x01: "[Wave]",
|
0x01: "[Wave]",
|
||||||
@ -307,7 +326,12 @@ CHARSET = {
|
|||||||
0x28: {None: lambda d: (f"[Var {d[0]}]", 1)},
|
0x28: {None: lambda d: (f"[Var {d[0]}]", 1)},
|
||||||
0x29: {None: lambda d: (f"[CenterX {d[0]}]", 1)},
|
0x29: {None: lambda d: (f"[CenterX {d[0]}]", 1)},
|
||||||
0x2B: "[EnableCDownNext]",
|
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)},
|
0x2E: {None: lambda d: (f"[Volume {d[0]}]", 1)},
|
||||||
0x2F: {
|
0x2F: {
|
||||||
0: "[Voice normal]\n",
|
0: "[Voice normal]\n",
|
||||||
@ -315,7 +339,7 @@ CHARSET = {
|
|||||||
2: "[Voice star]\n",
|
2: "[Voice star]\n",
|
||||||
None: lambda d: (f"[Voice {d[0]}]\n", 1),
|
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),
|
None: lambda d: (f"[Raw 0x{d[0]:02X}]", 1),
|
||||||
}
|
}
|
||||||
@ -366,6 +390,7 @@ CHARSET_CREDITS = {
|
|||||||
0xF7: " ",
|
0xF7: " ",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class N64SegPm_msg(N64Segment):
|
class N64SegPm_msg(N64Segment):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -393,12 +418,12 @@ class N64SegPm_msg(N64Segment):
|
|||||||
self.msg_names = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
self.msg_names = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
||||||
|
|
||||||
def split(self, rom_bytes):
|
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 = []
|
section_offsets = []
|
||||||
pos = 0
|
pos = 0
|
||||||
while True:
|
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:
|
if offset == 0:
|
||||||
break
|
break
|
||||||
@ -417,7 +442,7 @@ class N64SegPm_msg(N64Segment):
|
|||||||
msg_offsets = []
|
msg_offsets = []
|
||||||
pos = section_offset
|
pos = section_offset
|
||||||
while True:
|
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:
|
if offset == section_offset:
|
||||||
break
|
break
|
||||||
@ -425,7 +450,7 @@ class N64SegPm_msg(N64Segment):
|
|||||||
msg_offsets.append(offset)
|
msg_offsets.append(offset)
|
||||||
pos += 4
|
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")
|
path = msg_dir / Path(name + ".msg")
|
||||||
|
|
||||||
@ -449,7 +474,6 @@ class N64SegPm_msg(N64Segment):
|
|||||||
self.write_message_markup(data[msg_offset:])
|
self.write_message_markup(data[msg_offset:])
|
||||||
self.f.write("\n}\n")
|
self.f.write("\n}\n")
|
||||||
|
|
||||||
|
|
||||||
def get_linker_entries(self):
|
def get_linker_entries(self):
|
||||||
from segtypes.linker_entry import LinkerEntry
|
from segtypes.linker_entry import LinkerEntry
|
||||||
|
|
||||||
@ -458,7 +482,6 @@ class N64SegPm_msg(N64Segment):
|
|||||||
|
|
||||||
return [LinkerEntry(self, out_paths, base_path, ".data")]
|
return [LinkerEntry(self, out_paths, base_path, ".data")]
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_name(addr):
|
def get_default_name(addr):
|
||||||
return "msg"
|
return "msg"
|
||||||
|
@ -56,9 +56,7 @@ class SBN:
|
|||||||
entry_addr = header.tableOffset
|
entry_addr = header.tableOffset
|
||||||
seen_entry_offsets = set()
|
seen_entry_offsets = set()
|
||||||
for i in range(header.numEntries):
|
for i in range(header.numEntries):
|
||||||
entry = SBNFileEntry(
|
entry = SBNFileEntry(*struct.unpack_from(SBNFileEntry.fstring, data, entry_addr))
|
||||||
*struct.unpack_from(SBNFileEntry.fstring, data, entry_addr)
|
|
||||||
)
|
|
||||||
entry_addr += SBNFileEntry.length
|
entry_addr += SBNFileEntry.length
|
||||||
|
|
||||||
# Check for duplicate entry offsets
|
# Check for duplicate entry offsets
|
||||||
@ -152,9 +150,7 @@ class SBN:
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Unsupported file extension")
|
raise ValueError("Unsupported file extension")
|
||||||
|
|
||||||
entry = SBNFileEntry(
|
entry = SBNFileEntry(offset=current_file_offset, fmt=format, size=file.fakesize)
|
||||||
offset=current_file_offset, fmt=format, size=file.fakesize
|
|
||||||
)
|
|
||||||
|
|
||||||
struct.pack_into(
|
struct.pack_into(
|
||||||
SBNFileEntry.fstring,
|
SBNFileEntry.fstring,
|
||||||
@ -222,9 +218,7 @@ class SBN:
|
|||||||
|
|
||||||
with open(path / "sbn.yaml", "w") as f:
|
with open(path / "sbn.yaml", "w") as f:
|
||||||
# Filename->ID map
|
# Filename->ID map
|
||||||
f.write(
|
f.write("# Mapping of filenames to entry IDs. Use 'id: auto' to automatically assign a unique ID.\n")
|
||||||
"# Mapping of filenames to entry IDs. Use 'id: auto' to automatically assign a unique ID.\n"
|
|
||||||
)
|
|
||||||
f.write(
|
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.
|
# '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")
|
f.write("\n")
|
||||||
|
|
||||||
# INIT mseqs
|
# INIT mseqs
|
||||||
f.write(
|
f.write("# AuGlobals::mseqFileList. Not sure why there's non-MSEQ files here!\n")
|
||||||
"# AuGlobals::mseqFileList. Not sure why there's non-MSEQ files here!\n"
|
|
||||||
)
|
|
||||||
f.write("mseqs:\n")
|
f.write("mseqs:\n")
|
||||||
for id, entry in enumerate(self.init.mseq_entries):
|
for id, entry in enumerate(self.init.mseq_entries):
|
||||||
f.write(f" - id: 0x{id:02x}\n")
|
f.write(f" - id: 0x{id:02x}\n")
|
||||||
@ -357,9 +349,7 @@ class SBN:
|
|||||||
assert type(bk_file) == str
|
assert type(bk_file) == str
|
||||||
bk_file_ids.append(self.lookup_file_id(bk_file))
|
bk_file_ids.append(self.lookup_file_id(bk_file))
|
||||||
|
|
||||||
init_song_entry = InitSongEntry(
|
init_song_entry = InitSongEntry(file_id, bk_file_ids[0], bk_file_ids[1], bk_file_ids[2])
|
||||||
file_id, bk_file_ids[0], bk_file_ids[1], bk_file_ids[2]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Replace self.init.song_entries[id]
|
# Replace self.init.song_entries[id]
|
||||||
if id < len(self.init.song_entries):
|
if id < len(self.init.song_entries):
|
||||||
@ -562,9 +552,7 @@ class INIT:
|
|||||||
song_addr = header.tblOffset
|
song_addr = header.tblOffset
|
||||||
song_number = 0
|
song_number = 0
|
||||||
while True:
|
while True:
|
||||||
song = InitSongEntry(
|
song = InitSongEntry(*struct.unpack_from(InitSongEntry.fstring, data, song_addr))
|
||||||
*struct.unpack_from(InitSongEntry.fstring, data, song_addr)
|
|
||||||
)
|
|
||||||
|
|
||||||
if song.bgmFileIndex == 0xFFFF:
|
if song.bgmFileIndex == 0xFFFF:
|
||||||
break
|
break
|
||||||
@ -590,9 +578,7 @@ class INIT:
|
|||||||
entries_len = header.entriesSize // 4 - 1
|
entries_len = header.entriesSize // 4 - 1
|
||||||
|
|
||||||
for i in range(entries_len):
|
for i in range(entries_len):
|
||||||
entry = BufferEntry(
|
entry = BufferEntry(*struct.unpack_from(BufferEntry.fstring, data, entries_addr))
|
||||||
*struct.unpack_from(BufferEntry.fstring, data, entries_addr)
|
|
||||||
)
|
|
||||||
entries_addr += BufferEntry.length
|
entries_addr += BufferEntry.length
|
||||||
|
|
||||||
self.bk_entries.append(entry)
|
self.bk_entries.append(entry)
|
||||||
@ -708,9 +694,7 @@ if splat_loaded:
|
|||||||
out = options.opts.asset_path / self.dir / (self.name + ".sbn")
|
out = options.opts.asset_path / self.dir / (self.name + ".sbn")
|
||||||
|
|
||||||
sbn = SBN()
|
sbn = SBN()
|
||||||
config_files = sbn.read(
|
config_files = sbn.read(dir) # TODO: LayeredFS/AssetsFS read, supporting merges
|
||||||
dir
|
|
||||||
) # TODO: LayeredFS/AssetsFS read, supporting merges
|
|
||||||
inputs = config_files + [dir / f.file_name() for f in sbn.files]
|
inputs = config_files + [dir / f.file_name() for f in sbn.files]
|
||||||
return [
|
return [
|
||||||
LinkerEntry(
|
LinkerEntry(
|
||||||
|
@ -140,9 +140,7 @@ def extract(input_data: bytes, endian: Literal["big", "little"] = "big") -> str:
|
|||||||
profile_list = []
|
profile_list = []
|
||||||
|
|
||||||
for _ in range(len(PROFILE_NAMES[g])):
|
for _ in range(len(PROFILE_NAMES[g])):
|
||||||
profile_list.append(
|
profile_list.append(struct.unpack(END + "i", offsets_table[pl_it : pl_it + 4])[0])
|
||||||
struct.unpack(END + "i", offsets_table[pl_it : pl_it + 4])[0]
|
|
||||||
)
|
|
||||||
pl_it += 4
|
pl_it += 4
|
||||||
|
|
||||||
for j, pl_offset in enumerate(profile_list):
|
for j, pl_offset in enumerate(profile_list):
|
||||||
|
@ -101,14 +101,14 @@ LIST_END_BYTES = b"\xFF\xFF\xFF\xFF"
|
|||||||
|
|
||||||
|
|
||||||
def indent(elem, level=0):
|
def indent(elem, level=0):
|
||||||
i = "\n" + level*" "
|
i = "\n" + level * " "
|
||||||
if len(elem):
|
if len(elem):
|
||||||
if not elem.text or not elem.text.strip():
|
if not elem.text or not elem.text.strip():
|
||||||
elem.text = i + " "
|
elem.text = i + " "
|
||||||
if not elem.tail or not elem.tail.strip():
|
if not elem.tail or not elem.tail.strip():
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
for elem in elem:
|
for elem in elem:
|
||||||
indent(elem, level+1)
|
indent(elem, level + 1)
|
||||||
if not elem.tail or not elem.tail.strip():
|
if not elem.tail or not elem.tail.strip():
|
||||||
elem.tail = i
|
elem.tail = i
|
||||||
else:
|
else:
|
||||||
@ -116,7 +116,7 @@ def indent(elem, level=0):
|
|||||||
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()
|
root = tree.getroot()
|
||||||
indent(root)
|
indent(root)
|
||||||
xml_str = ET.tostring(root, encoding="unicode")
|
xml_str = ET.tostring(root, encoding="unicode")
|
||||||
@ -136,9 +136,7 @@ class RasterTableEntry:
|
|||||||
raster_bytes: bytes = field(default_factory=bytes)
|
raster_bytes: bytes = field(default_factory=bytes)
|
||||||
palette: Optional[bytes] = None
|
palette: Optional[bytes] = None
|
||||||
|
|
||||||
def write_png(
|
def write_png(self, raster_buffer: bytes, path: Path, palette: Optional[bytes] = None):
|
||||||
self, raster_buffer: bytes, path: Path, palette: Optional[bytes] = None
|
|
||||||
):
|
|
||||||
if self.height == 0 or self.width == 0:
|
if self.height == 0 or self.width == 0:
|
||||||
raise ValueError("Raster size has not been set")
|
raise ValueError("Raster size has not been set")
|
||||||
|
|
||||||
@ -149,9 +147,7 @@ class RasterTableEntry:
|
|||||||
raise ValueError("Palette has not been set")
|
raise ValueError("Palette has not been set")
|
||||||
|
|
||||||
if self.raster_bytes is not None:
|
if self.raster_bytes is not None:
|
||||||
self.raster_bytes = raster_buffer[
|
self.raster_bytes = raster_buffer[self.offset : self.offset + (self.width * self.height // 2)]
|
||||||
self.offset : self.offset + (self.width * self.height // 2)
|
|
||||||
]
|
|
||||||
|
|
||||||
img = CI4(self.raster_bytes, self.width, self.height)
|
img = CI4(self.raster_bytes, self.width, self.height)
|
||||||
img.set_palette(palette)
|
img.set_palette(palette)
|
||||||
@ -264,9 +260,7 @@ class PlayerSprite:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def extract_raster_table_entries(
|
def extract_raster_table_entries(data: bytes, raster_sets: List[PlayerSpriteRasterSet]) -> Dict[int, RasterTableEntry]:
|
||||||
data: bytes, raster_sets: List[PlayerSpriteRasterSet]
|
|
||||||
) -> Dict[int, RasterTableEntry]:
|
|
||||||
ret: Dict[int, RasterTableEntry] = {}
|
ret: Dict[int, RasterTableEntry] = {}
|
||||||
current_section_pos = 0
|
current_section_pos = 0
|
||||||
current_section = 0
|
current_section = 0
|
||||||
@ -297,9 +291,7 @@ def extract_raster_table_entries(
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def extract_sprites(
|
def extract_sprites(yay0_data: bytes, raster_sets: List[PlayerSpriteRasterSet]) -> List[PlayerSprite]:
|
||||||
yay0_data: bytes, raster_sets: List[PlayerSpriteRasterSet]
|
|
||||||
) -> List[PlayerSprite]:
|
|
||||||
yay0_splits = []
|
yay0_splits = []
|
||||||
for i in range(14):
|
for i in range(14):
|
||||||
yay0_splits.append(int.from_bytes(yay0_data[i * 4 : i * 4 + 4], "big"))
|
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_table_entry_dict: Dict[int, RasterTableEntry],
|
||||||
raster_names: List[str],
|
raster_names: List[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
def get_sprite_name_from_offset(
|
def get_sprite_name_from_offset(offset: int, offsets: List[int], names: List[str]) -> str:
|
||||||
offset: int, offsets: List[int], names: List[str]
|
|
||||||
) -> str:
|
|
||||||
return names[offsets.index(offset)]
|
return names[offsets.index(offset)]
|
||||||
|
|
||||||
sprite_idx = 0
|
sprite_idx = 0
|
||||||
@ -426,9 +416,7 @@ def write_player_xmls(
|
|||||||
back_raster = cur_sprite_back.rasters[i]
|
back_raster = cur_sprite_back.rasters[i]
|
||||||
|
|
||||||
if back_raster.is_special:
|
if back_raster.is_special:
|
||||||
raster_attributes[
|
raster_attributes["special"] = f"{back_raster.width & 0xFF:X},{back_raster.height & 0xFF:X}"
|
||||||
"special"
|
|
||||||
] = f"{back_raster.width & 0xFF:X},{back_raster.height & 0xFF:X}"
|
|
||||||
else:
|
else:
|
||||||
back_name_offset = raster_sets[sprite_idx + 1].raster_offsets[i]
|
back_name_offset = raster_sets[sprite_idx + 1].raster_offsets[i]
|
||||||
raster_attributes[
|
raster_attributes[
|
||||||
@ -483,9 +471,7 @@ def write_player_xmls(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for anim in comp.animations:
|
for anim in comp.animations:
|
||||||
ET.SubElement(
|
ET.SubElement(Component, anim.__class__.__name__, anim.get_attributes())
|
||||||
Component, anim.__class__.__name__, anim.get_attributes()
|
|
||||||
)
|
|
||||||
|
|
||||||
xml = ET.ElementTree(SpriteSheet)
|
xml = ET.ElementTree(SpriteSheet)
|
||||||
pretty_print_xml(xml, out_path / f"{cur_sprite_name}.xml")
|
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:
|
if pal_name not in dumped_palettes:
|
||||||
offset = PLAYER_PAL_TO_RASTER[pal_name]
|
offset = PLAYER_PAL_TO_RASTER[pal_name]
|
||||||
if pal_name not in PLAYER_PAL_TO_RASTER:
|
if pal_name not in PLAYER_PAL_TO_RASTER:
|
||||||
print(
|
print(f"WARNING: Palette {pal_name} has no specified raster, not dumping!")
|
||||||
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)
|
||||||
)
|
|
||||||
raster_table_entry_dict[offset].write_png(
|
|
||||||
raster_data, path / (pal_name + ".png"), palette
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
###########
|
###########
|
||||||
@ -606,12 +588,8 @@ class NpcSprite:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_bytes(data: bytearray):
|
def from_bytes(data: bytearray):
|
||||||
image_offsets = read_offset_list(
|
image_offsets = read_offset_list(data[int.from_bytes(data[0:4], byteorder="big") :])
|
||||||
data[int.from_bytes(data[0:4], byteorder="big") :]
|
palette_offsets = read_offset_list(data[int.from_bytes(data[4:8], 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")
|
max_components = int.from_bytes(data[8:0xC], byteorder="big")
|
||||||
num_variations = int.from_bytes(data[0xC:0x10], byteorder="big")
|
num_variations = int.from_bytes(data[0xC:0x10], byteorder="big")
|
||||||
animation_offsets = read_offset_list(data[0x10:])
|
animation_offsets = read_offset_list(data[0x10:])
|
||||||
@ -683,11 +661,7 @@ class NpcSprite:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for i, palette in enumerate(self.palettes):
|
for i, palette in enumerate(self.palettes):
|
||||||
name = (
|
name = self.palette_names[i] if (self.palette_names and i < len(self.palette_names)) else f"Pal{i:02X}"
|
||||||
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:
|
if i in palette_to_raster:
|
||||||
img = palette_to_raster[i][0]
|
img = palette_to_raster[i][0]
|
||||||
@ -710,9 +684,7 @@ class NpcSprite:
|
|||||||
AnimationList,
|
AnimationList,
|
||||||
"Animation",
|
"Animation",
|
||||||
{
|
{
|
||||||
"name": self.animation_names[i]
|
"name": self.animation_names[i] if self.animation_names else f"Anim{i:02X}",
|
||||||
if self.animation_names
|
|
||||||
else f"Anim{i:02X}",
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -727,9 +699,7 @@ class NpcSprite:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for anim in comp.animations:
|
for anim in comp.animations:
|
||||||
ET.SubElement(
|
ET.SubElement(Component, anim.__class__.__name__, anim.get_attributes())
|
||||||
Component, anim.__class__.__name__, anim.get_attributes()
|
|
||||||
)
|
|
||||||
|
|
||||||
xml = ET.ElementTree(SpriteSheet)
|
xml = ET.ElementTree(SpriteSheet)
|
||||||
pretty_print_xml(xml, path / "SpriteSheet.xml")
|
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)]
|
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:
|
def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml) -> None:
|
||||||
super().__init__(
|
super().__init__(rom_start, rom_end, type, name, vram_start, args=args, yaml=yaml)
|
||||||
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:
|
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)
|
self.npc_cfg = yaml_loader.load(f.read(), Loader=yaml_loader.SafeLoader)
|
||||||
@ -752,9 +720,7 @@ class N64SegPm_sprites(N64Segment):
|
|||||||
def out_path(self):
|
def out_path(self):
|
||||||
return options.opts.asset_path / "sprite" / "sprites"
|
return options.opts.asset_path / "sprite" / "sprites"
|
||||||
|
|
||||||
def split_player(
|
def split_player(self, build_date: str, player_raster_data: bytes, player_yay0_data: bytes) -> None:
|
||||||
self, build_date: str, player_raster_data: bytes, player_yay0_data: bytes
|
|
||||||
) -> None:
|
|
||||||
player_sprite_cfg = self.player_cfg["player_sprites"]
|
player_sprite_cfg = self.player_cfg["player_sprites"]
|
||||||
player_raster_names: List[str] = self.player_cfg["player_rasters"]
|
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
|
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
|
sprite_end_offset = int.from_bytes(sprite_in_bytes[0x1C:0x20], "big") + 0x10
|
||||||
|
|
||||||
player_raster_data: bytes = sprite_in_bytes[
|
player_raster_data: bytes = sprite_in_bytes[player_raster_offset:player_yay0_offset]
|
||||||
player_raster_offset:player_yay0_offset
|
|
||||||
]
|
|
||||||
player_yay0_data: bytes = sprite_in_bytes[player_yay0_offset:npc_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]
|
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"]
|
src_paths = [options.opts.asset_path / "sprite"]
|
||||||
|
|
||||||
# for NPC
|
# for NPC
|
||||||
src_paths += [
|
src_paths += [options.opts.asset_path / "sprite" / "npc" / sprite_name for sprite_name in self.npc_cfg]
|
||||||
options.opts.asset_path / "sprite" / "npc" / sprite_name
|
|
||||||
for sprite_name in self.npc_cfg
|
|
||||||
]
|
|
||||||
|
|
||||||
return [
|
return [LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section())]
|
||||||
LinkerEntry(self, src_paths, self.out_path(), self.get_linker_section())
|
|
||||||
]
|
|
||||||
|
|
||||||
def cache(self):
|
def cache(self):
|
||||||
return (self.yaml, self.rom_end, self.player_cfg, self.npc_cfg)
|
return (self.yaml, self.rom_end, self.player_cfg, self.npc_cfg)
|
||||||
|
@ -217,7 +217,6 @@ class AnimComponent:
|
|||||||
def size(self):
|
def size(self):
|
||||||
return len(self.commands)
|
return len(self.commands)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_commands(command_list: List[int]) -> List[Animation]:
|
def parse_commands(command_list: List[int]) -> List[Animation]:
|
||||||
ret: List[Animation] = []
|
ret: List[Animation] = []
|
||||||
@ -329,10 +328,7 @@ class AnimComponent:
|
|||||||
|
|
||||||
x, y, z = struct.unpack(">hhh", data[6:12])
|
x, y, z = struct.unpack(">hhh", data[6:12])
|
||||||
|
|
||||||
commands = [
|
commands = [int.from_bytes(d[0:2], byteorder="big", signed=False) for d in iter_in_groups(commands_data, 2)]
|
||||||
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)
|
return AnimComponent(x, y, z, commands)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -260,9 +260,7 @@ class TexImage:
|
|||||||
self.main_height,
|
self.main_height,
|
||||||
)
|
)
|
||||||
if self.main_fmt == FMT_CI:
|
if self.main_fmt == FMT_CI:
|
||||||
self.main_img.palette = self.get_n64_pal(
|
self.main_img.palette = self.get_n64_pal(texbuf, self.main_fmt, self.main_depth)
|
||||||
texbuf, self.main_fmt, self.main_depth
|
|
||||||
)
|
|
||||||
# main img + mipmaps
|
# main img + mipmaps
|
||||||
elif self.extra_tiles == TILES_MIPMAPS:
|
elif self.extra_tiles == TILES_MIPMAPS:
|
||||||
self.has_mipmaps = True
|
self.has_mipmaps = True
|
||||||
@ -282,9 +280,7 @@ class TexImage:
|
|||||||
break
|
break
|
||||||
mmw = self.main_width // divisor
|
mmw = self.main_width // divisor
|
||||||
mmh = self.main_height // divisor
|
mmh = self.main_height // divisor
|
||||||
mipmap = self.get_n64_img(
|
mipmap = self.get_n64_img(texbuf, self.main_fmt, self.main_depth, mmw, mmh)
|
||||||
texbuf, self.main_fmt, self.main_depth, mmw, mmh
|
|
||||||
)
|
|
||||||
self.mipmaps.append(mipmap)
|
self.mipmaps.append(mipmap)
|
||||||
|
|
||||||
divisor = divisor * 2
|
divisor = divisor * 2
|
||||||
@ -334,13 +330,9 @@ class TexImage:
|
|||||||
pal = self.get_n64_pal(texbuf, self.main_fmt, self.main_depth)
|
pal = self.get_n64_pal(texbuf, self.main_fmt, self.main_depth)
|
||||||
self.main_img.palette = pal
|
self.main_img.palette = pal
|
||||||
# read aux
|
# read aux
|
||||||
self.aux_img = self.get_n64_img(
|
self.aux_img = self.get_n64_img(texbuf, self.aux_fmt, self.aux_depth, self.aux_width, self.aux_height)
|
||||||
texbuf, self.aux_fmt, self.aux_depth, self.aux_width, self.aux_height
|
|
||||||
)
|
|
||||||
if self.aux_fmt == FMT_CI:
|
if self.aux_fmt == FMT_CI:
|
||||||
self.aux_img.palette = self.get_n64_pal(
|
self.aux_img.palette = self.get_n64_pal(texbuf, self.aux_fmt, self.aux_depth)
|
||||||
texbuf, self.aux_fmt, self.aux_depth
|
|
||||||
)
|
|
||||||
|
|
||||||
# constructs a dictionary entry for the tex archive for this texture
|
# constructs a dictionary entry for the tex archive for this texture
|
||||||
def get_json_entry(self):
|
def get_json_entry(self):
|
||||||
@ -418,9 +410,7 @@ class TexImage:
|
|||||||
|
|
||||||
return (r << 11) | (g << 6) | (b << 1) | a
|
return (r << 11) | (g << 6) | (b << 1) | a
|
||||||
|
|
||||||
(out_img, out_w, out_h) = Converter(
|
(out_img, out_w, out_h) = Converter(mode=fmt_str.lower(), infile=img_file, flip_y=True).convert()
|
||||||
mode=fmt_str.lower(), infile=img_file, flip_y=True
|
|
||||||
).convert()
|
|
||||||
|
|
||||||
out_pal = bytearray()
|
out_pal = bytearray()
|
||||||
if fmt_str == "CI4" or fmt_str == "CI8":
|
if fmt_str == "CI4" or fmt_str == "CI8":
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from segtypes.n64.vtx import N64SegVtx
|
from segtypes.n64.vtx import N64SegVtx
|
||||||
|
|
||||||
|
|
||||||
class N64SegVtx_common(N64SegVtx):
|
class N64SegVtx_common(N64SegVtx):
|
||||||
def format_sym_name(self, sym):
|
def format_sym_name(self, sym):
|
||||||
return f"N({sym.name[7:]})"
|
return f"N({sym.name[7:]})"
|
||||||
|
@ -12,11 +12,11 @@ def create_enum(file_content, prefix, ordering):
|
|||||||
max_size = 0
|
max_size = 0
|
||||||
|
|
||||||
if ordering:
|
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:
|
if len(key) > max_size:
|
||||||
max_size = len(key)
|
max_size = len(key)
|
||||||
else:
|
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:
|
if len(value) > max_size:
|
||||||
max_size = len(value)
|
max_size = len(value)
|
||||||
|
|
||||||
@ -25,15 +25,25 @@ def create_enum(file_content, prefix, ordering):
|
|||||||
else:
|
else:
|
||||||
prefix = ""
|
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:
|
if ordering:
|
||||||
key = '_'.join(re.sub(r'([A-Z]{1,2})', r' \1', key).split()).replace("N_P_C", "NPC").replace("__", "_").replace("-", "")
|
key = (
|
||||||
key = prefix.upper() + '_{:<{width}}'.format(key, width=max_size + 2).upper()
|
"_".join(re.sub(r"([A-Z]{1,2})", r" \1", key).split())
|
||||||
ret += " " + key + " = 0x" + '{:>{fill}{width}}'.format(value, fill=0, width=8) + ",\n"
|
.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:
|
else:
|
||||||
value = '_'.join(re.sub(r'([A-Z]{1,2})', r' \1', value).split()).replace("N_P_C", "NPC").replace("__", "_").replace("-", "")
|
value = (
|
||||||
value = prefix.upper() + '_{:<{width}}'.format(value, width=max_size + 2).upper()
|
"_".join(re.sub(r"([A-Z]{1,2})", r" \1", value).split())
|
||||||
ret += " " + value + " = 0x" + '{:>{fill}{width}}'.format(key, fill=0, width=8) + ",\n"
|
.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"
|
ret += "};\n"
|
||||||
|
|
||||||
@ -51,9 +61,9 @@ def single_translation(file_path):
|
|||||||
print("File not found at the given path.")
|
print("File not found at the given path.")
|
||||||
return "t", "y"
|
return "t", "y"
|
||||||
|
|
||||||
enum_namespace = re.search(r'(\w*)\s+%\s+namespace', 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)
|
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)
|
reversed_order = re.search(r"(\w*)\s+%\s+reversed", file_content).group(1)
|
||||||
ret += "enum " + enum_name + " {\n"
|
ret += "enum " + enum_name + " {\n"
|
||||||
|
|
||||||
if reversed_order == "true":
|
if reversed_order == "true":
|
||||||
@ -76,9 +86,9 @@ def recursive_translation(database_path):
|
|||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
enum_namespace = re.search(r'(\w*)\s+%\s+namespace', 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)
|
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)
|
reversed_order = re.search(r"(\w*)\s+%\s+reversed", file_content).group(1)
|
||||||
ret += "enum " + enum_name + " {\n"
|
ret += "enum " + enum_name + " {\n"
|
||||||
|
|
||||||
if reversed_order == "true":
|
if reversed_order == "true":
|
||||||
@ -106,9 +116,17 @@ def main(args):
|
|||||||
file.close()
|
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("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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -24,12 +24,16 @@ INCLUDES_NEEDED["npcs"] = {}
|
|||||||
INCLUDES_NEEDED["sprites"] = set()
|
INCLUDES_NEEDED["sprites"] = set()
|
||||||
INCLUDES_NEEDED["tattle"] = []
|
INCLUDES_NEEDED["tattle"] = []
|
||||||
|
|
||||||
|
|
||||||
def get_flag_name(arg):
|
def get_flag_name(arg):
|
||||||
v = arg - 2**32 # convert to s32
|
v = arg - 2**32 # convert to s32
|
||||||
if v > -250000000:
|
if v > -250000000:
|
||||||
if v <= -220000000: return str((v + 230000000) / 1024)
|
if v <= -220000000:
|
||||||
elif v <= -200000000: return f"ArrayFlag({v + 210000000})"
|
return str((v + 230000000) / 1024)
|
||||||
elif v <= -180000000: return f"ArrayVar({v + 190000000})"
|
elif v <= -200000000:
|
||||||
|
return f"ArrayFlag({v + 210000000})"
|
||||||
|
elif v <= -180000000:
|
||||||
|
return f"ArrayVar({v + 190000000})"
|
||||||
elif v <= -160000000:
|
elif v <= -160000000:
|
||||||
if v + 170000000 == 0:
|
if v + 170000000 == 0:
|
||||||
return "GB_StoryProgress"
|
return "GB_StoryProgress"
|
||||||
@ -37,13 +41,20 @@ def get_flag_name(arg):
|
|||||||
return "GB_WorldLocation"
|
return "GB_WorldLocation"
|
||||||
else:
|
else:
|
||||||
return f"GameByte({v + 170000000})"
|
return f"GameByte({v + 170000000})"
|
||||||
elif v <= -140000000: return f"AreaByte({v + 150000000})"
|
elif v <= -140000000:
|
||||||
elif v <= -120000000: return f"GameFlag({v + 130000000})"
|
return f"AreaByte({v + 150000000})"
|
||||||
elif v <= -100000000: return f"AreaFlag({v + 110000000})"
|
elif v <= -120000000:
|
||||||
elif v <= -80000000: return f"MapFlag({v + 90000000})"
|
return f"GameFlag({v + 130000000})"
|
||||||
elif v <= -60000000: return f"LocalFlag({v + 70000000})"
|
elif v <= -100000000:
|
||||||
elif v <= -40000000: return f"MapVar({v + 50000000})"
|
return f"AreaFlag({v + 110000000})"
|
||||||
elif v <= -20000000: return f"LocalVar({v + 30000000})"
|
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:
|
if arg == 0xFFFFFFFF:
|
||||||
return "-1"
|
return "-1"
|
||||||
@ -54,6 +65,7 @@ def get_flag_name(arg):
|
|||||||
else:
|
else:
|
||||||
return f"{arg}"
|
return f"{arg}"
|
||||||
|
|
||||||
|
|
||||||
def get_function_list(area_name, map_name, rom_offset):
|
def get_function_list(area_name, map_name, rom_offset):
|
||||||
map_file = (Path(__file__).parent.parent / "ver" / "current" / "build" / "papermario.map").read_text().splitlines()
|
map_file = (Path(__file__).parent.parent / "ver" / "current" / "build" / "papermario.map").read_text().splitlines()
|
||||||
i = 0
|
i = 0
|
||||||
@ -70,7 +82,7 @@ def get_function_list(area_name, map_name, rom_offset):
|
|||||||
vram = int(vram, 16)
|
vram = int(vram, 16)
|
||||||
func = func.replace(f"{map_name}_", "")
|
func = func.replace(f"{map_name}_", "")
|
||||||
if func.count("_") == 2:
|
if func.count("_") == 2:
|
||||||
func = func.rsplit("_",1)[0]
|
func = func.rsplit("_", 1)[0]
|
||||||
functions[vram] = func
|
functions[vram] = func
|
||||||
i += 1
|
i += 1
|
||||||
if firstFind:
|
if firstFind:
|
||||||
@ -79,6 +91,7 @@ def get_function_list(area_name, map_name, rom_offset):
|
|||||||
|
|
||||||
return functions
|
return functions
|
||||||
|
|
||||||
|
|
||||||
def get_include_list(area_name, map_name):
|
def get_include_list(area_name, map_name):
|
||||||
include_path = Path(__file__).parent.parent / "src" / "world" / "common"
|
include_path = Path(__file__).parent.parent / "src" / "world" / "common"
|
||||||
includes = set()
|
includes = set()
|
||||||
@ -87,16 +100,18 @@ def get_include_list(area_name, map_name):
|
|||||||
with open(file, "r", encoding="utf8") as f:
|
with open(file, "r", encoding="utf8") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
if (line.startswith("void N(") or line.startswith("ApiStatus N(")) and "{" in line:
|
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)
|
includes.add(func_name)
|
||||||
return includes
|
return includes
|
||||||
|
|
||||||
|
|
||||||
def read_enum(num: int, constants_name: str) -> str:
|
def read_enum(num: int, constants_name: str) -> str:
|
||||||
if num in disasm_script.CONSTANTS[constants_name]:
|
if num in disasm_script.CONSTANTS[constants_name]:
|
||||||
return disasm_script.CONSTANTS[constants_name][num]
|
return disasm_script.CONSTANTS[constants_name][num]
|
||||||
else:
|
else:
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def read_flags(flags: int, constants_name: str) -> str:
|
def read_flags(flags: int, constants_name: str) -> str:
|
||||||
enabled = []
|
enabled = []
|
||||||
for x in range(32):
|
for x in range(32):
|
||||||
@ -115,6 +130,7 @@ def read_flags(flags: int, constants_name: str) -> str:
|
|||||||
|
|
||||||
return " | ".join(enabled)
|
return " | ".join(enabled)
|
||||||
|
|
||||||
|
|
||||||
def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str:
|
def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str:
|
||||||
if addr == 0:
|
if addr == 0:
|
||||||
return "NULL"
|
return "NULL"
|
||||||
@ -126,6 +142,7 @@ def read_ptr(addr: int, symbol_map: dict, needs_ampersand: bool = False) -> str:
|
|||||||
else:
|
else:
|
||||||
return f"(void*) 0x{addr:08X}"
|
return f"(void*) 0x{addr:08X}"
|
||||||
|
|
||||||
|
|
||||||
def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace=None):
|
def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace=None):
|
||||||
global INCLUDES_NEEDED, INCLUDED
|
global INCLUDES_NEEDED, INCLUDED
|
||||||
out = ""
|
out = ""
|
||||||
@ -139,7 +156,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
|
|
||||||
def transform_symbol_name(symbol):
|
def transform_symbol_name(symbol):
|
||||||
if namespace and symbol.startswith(namespace + "_"):
|
if namespace and symbol.startswith(namespace + "_"):
|
||||||
return "N(" + symbol[len(namespace)+1:] + ")"
|
return "N(" + symbol[len(namespace) + 1 :] + ")"
|
||||||
return symbol
|
return symbol
|
||||||
|
|
||||||
while len(midx) > 0:
|
while len(midx) > 0:
|
||||||
@ -148,7 +165,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
name = struct["name"]
|
name = struct["name"]
|
||||||
print(name, file=sys.stderr)
|
print(name, file=sys.stderr)
|
||||||
|
|
||||||
#INCLUDED["functions"].add(name)
|
# INCLUDED["functions"].add(name)
|
||||||
|
|
||||||
if comments:
|
if comments:
|
||||||
out += f"// {romstart+struct['start']:X}-{romstart+struct['end']:X} (VRAM: {struct['vaddr']:X})\n"
|
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
|
main_script_name = name
|
||||||
|
|
||||||
# For PlayMusic script if using a separate header file
|
# For PlayMusic script if using a separate header file
|
||||||
#if afterHeader:
|
# if afterHeader:
|
||||||
# INCLUDES_NEEDED["forward"].append(f"EvtScript " + name + ";")
|
# INCLUDES_NEEDED["forward"].append(f"EvtScript " + name + ";")
|
||||||
# afterHeader = False
|
# 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(
|
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,
|
transform_symbol_name=transform_symbol_name,
|
||||||
use_script_lib=False,
|
use_script_lib=False,
|
||||||
).disassemble()
|
).disassemble()
|
||||||
|
|
||||||
if "EVS_ShakeTree" in name or "EVS_SearchBush" in name:
|
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:
|
if not treePrint:
|
||||||
out += f"=======================================\n"
|
out += f"=======================================\n"
|
||||||
out += f"==========BELOW foliage.inc.c==========\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()
|
script_text = script_text.splitlines()
|
||||||
walkDistance = exitIdx = map_ = entryIdx = ""
|
walkDistance = exitIdx = map_ = entryIdx = ""
|
||||||
if "UseExitHeading" in script_text[2]:
|
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]:
|
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:
|
if walkDistance and exitIdx and map_ and entryIdx:
|
||||||
out += f"EvtScript {name} = EVT_EXIT_WALK({walkDistance}, {exitIdx}, {map_}, {entryIdx});\n"
|
out += f"EvtScript {name} = EVT_EXIT_WALK({walkDistance}, {exitIdx}, {map_}, {entryIdx});\n"
|
||||||
else:
|
else:
|
||||||
@ -210,8 +232,11 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
z = []
|
z = []
|
||||||
w = []
|
w = []
|
||||||
for _ in range(entry_count):
|
for _ in range(entry_count):
|
||||||
a,b,c,d = unpack_from(">ffff", entry_list, pos)
|
a, b, c, d = unpack_from(">ffff", entry_list, pos)
|
||||||
x.append(f"{a:.01f}"); y.append(f"{b:.01f}"); z.append(f"{c:.01f}"); w.append(f"{d:.01f}")
|
x.append(f"{a:.01f}")
|
||||||
|
y.append(f"{b:.01f}")
|
||||||
|
z.append(f"{c:.01f}")
|
||||||
|
w.append(f"{d:.01f}")
|
||||||
pos += 16
|
pos += 16
|
||||||
|
|
||||||
x_size = max([len(a) for a in x])
|
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])
|
z_size = max([len(a) for a in z])
|
||||||
w_size = max([len(a) for a in w])
|
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 {{ {a:>{x_size}}f, {b:>{y_size}}f, {c:>{z_size}}f, {d:>{w_size}}f }},"
|
||||||
|
|
||||||
out += f"\n}};\n"
|
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"]
|
var_names = ["unk_00", "unk_24"]
|
||||||
data = unpack_from(">4B", npcSettings, i)
|
data = unpack_from(">4B", npcSettings, i)
|
||||||
if not sum(data) == 0:
|
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:
|
elif i == 0x4 or i == 0x28:
|
||||||
var_names = ["height", "radius", "level", "unk_2A"]
|
var_names = ["height", "radius", "level", "unk_2A"]
|
||||||
for x,var in enumerate(unpack_from(">2h", npcSettings, i)):
|
for x, var in enumerate(unpack_from(">2h", npcSettings, i)):
|
||||||
var_name = var_names[x if i == 0x4 else x+2]
|
var_name = var_names[x if i == 0x4 else x + 2]
|
||||||
if not var == 0:
|
if not var == 0:
|
||||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||||
elif i == 0x8:
|
elif i == 0x8:
|
||||||
var_names = ["otherAI", "onInteract", "ai", "onHit", "aux", "onDefeat", "flags"]
|
var_names = [
|
||||||
for x,var in enumerate(unpack_from(f">7I", npcSettings, i)):
|
"otherAI",
|
||||||
|
"onInteract",
|
||||||
|
"ai",
|
||||||
|
"onHit",
|
||||||
|
"aux",
|
||||||
|
"onDefeat",
|
||||||
|
"flags",
|
||||||
|
]
|
||||||
|
for x, var in enumerate(unpack_from(f">7I", npcSettings, i)):
|
||||||
var_name = var_names[x]
|
var_name = var_names[x]
|
||||||
if not var == 0:
|
if not var == 0:
|
||||||
if var == 0x80077F70:
|
if var == 0x80077F70:
|
||||||
@ -264,8 +302,20 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
npcAISettings = bytes.read(struct["length"])
|
npcAISettings = bytes.read(struct["length"])
|
||||||
|
|
||||||
i = x = 0
|
i = x = 0
|
||||||
var_names = ["moveSpeed", "moveTime", "waitTime", "alertRadius", "unk_10", "unk_14",
|
var_names = [
|
||||||
"chaseSpeed", "unk_1C", "unk_20", "chaseRadius", "unk_28", "unk_2C"]
|
"moveSpeed",
|
||||||
|
"moveTime",
|
||||||
|
"waitTime",
|
||||||
|
"alertRadius",
|
||||||
|
"unk_10",
|
||||||
|
"unk_14",
|
||||||
|
"chaseSpeed",
|
||||||
|
"unk_1C",
|
||||||
|
"unk_20",
|
||||||
|
"chaseRadius",
|
||||||
|
"unk_28",
|
||||||
|
"unk_2C",
|
||||||
|
]
|
||||||
while i < struct["length"]:
|
while i < struct["length"]:
|
||||||
var_f, var_i1, var_i2 = unpack_from(f">fii", npcAISettings, i)
|
var_f, var_i1, var_i2 = unpack_from(f">fii", npcAISettings, i)
|
||||||
if not var_f == 0:
|
if not var_f == 0:
|
||||||
@ -274,7 +324,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
# account for X32
|
# account for X32
|
||||||
if var_names[x + 1] in ["unk_10", "unk_1C", "unk_28"]:
|
if var_names[x + 1] in ["unk_10", "unk_1C", "unk_28"]:
|
||||||
if var_i1 < -100000 or var_i1 > 100000:
|
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:
|
else:
|
||||||
tmp_out += INDENT + f".{var_names[x + 1]} = {{ .s = {var_i1} }},\n"
|
tmp_out += INDENT + f".{var_names[x + 1]} = {{ .s = {var_i1} }},\n"
|
||||||
else:
|
else:
|
||||||
@ -289,32 +342,56 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
elif struct["type"] == "NpcGroup":
|
elif struct["type"] == "NpcGroup":
|
||||||
staticNpc = bytes.read(struct["length"])
|
staticNpc = bytes.read(struct["length"])
|
||||||
curr_base = 0
|
curr_base = 0
|
||||||
numNpcs = struct['length'] // 0x1F0
|
numNpcs = struct["length"] // 0x1F0
|
||||||
tmp_out = f"NpcData {name}" + ("[]" if numNpcs > 1 else "") + f" = {{\n"
|
tmp_out = f"NpcData {name}" + ("[]" if numNpcs > 1 else "") + f" = {{\n"
|
||||||
|
|
||||||
for z in range(numNpcs):
|
for z in range(numNpcs):
|
||||||
i = 0
|
i = 0
|
||||||
var_names = ["id", "settings", "pos", "flags",
|
var_names = [
|
||||||
"init", "unk_1C", "yaw", "dropFlags",
|
"id",
|
||||||
"itemDropChance", "itemDrops", "heartDrops", "flowerDrops",
|
"settings",
|
||||||
"minCoinBonus", "maxCoinBonus", "movement", "animations",
|
"pos",
|
||||||
"unk_1E0", "extraAnimations", "tattle"]
|
"flags",
|
||||||
|
"init",
|
||||||
|
"unk_1C",
|
||||||
|
"yaw",
|
||||||
|
"dropFlags",
|
||||||
|
"itemDropChance",
|
||||||
|
"itemDrops",
|
||||||
|
"heartDrops",
|
||||||
|
"flowerDrops",
|
||||||
|
"minCoinBonus",
|
||||||
|
"maxCoinBonus",
|
||||||
|
"movement",
|
||||||
|
"animations",
|
||||||
|
"unk_1E0",
|
||||||
|
"extraAnimations",
|
||||||
|
"tattle",
|
||||||
|
]
|
||||||
|
|
||||||
if numNpcs > 1:
|
if numNpcs > 1:
|
||||||
tmp_out += INDENT + f"{{\n"
|
tmp_out += INDENT + f"{{\n"
|
||||||
INDENT = INDENT*2
|
INDENT = INDENT * 2
|
||||||
|
|
||||||
while i < 0x1F0:
|
while i < 0x1F0:
|
||||||
if i == 0x0 or i == 0x24:
|
if i == 0x0 or i == 0x24:
|
||||||
var_name = var_names[0] if i == 0x0 else var_names[6]
|
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":
|
if var_name == "id":
|
||||||
tmp_out += INDENT + f".{var_name} = {disasm_script.CONSTANTS['MAP_NPCS'][var]},\n"
|
tmp_out += INDENT + f".{var_name} = {disasm_script.CONSTANTS['MAP_NPCS'][var]},\n"
|
||||||
else:
|
else:
|
||||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||||
elif i == 0x4 or i == 0x14 or i == 0x18 or i == 0x1E8:
|
elif i == 0x4 or i == 0x14 or i == 0x18 or i == 0x1E8:
|
||||||
var_name = var_names[1] if i == 0x4 else var_names[3] if i == 0x14 else var_names[4] if i == 0x18 else var_names[17]
|
var_name = (
|
||||||
addr = unpack_from(f">I", staticNpc, curr_base+i)[0]
|
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 not addr == 0:
|
||||||
if var_name != "flags" and addr in symbol_map:
|
if var_name != "flags" and addr in symbol_map:
|
||||||
if var_name == "extraAnimations":
|
if var_name == "extraAnimations":
|
||||||
@ -338,17 +415,17 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
enabled.append(0)
|
enabled.append(0)
|
||||||
tmp_out += INDENT + f".{var_name} = " + " | ".join(enabled) + f",\n"
|
tmp_out += INDENT + f".{var_name} = " + " | ".join(enabled) + f",\n"
|
||||||
elif i == 0x8:
|
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:
|
if not sum(pos) == 0:
|
||||||
tmp_out += INDENT + f".pos = {{ {pos[0]:.01f}f, {pos[1]:.01f}f, {pos[2]:.01f}f }},\n"
|
tmp_out += INDENT + f".pos = {{ {pos[0]:.01f}f, {pos[1]:.01f}f, {pos[2]:.01f}f }},\n"
|
||||||
elif i == 0x1C or i == 0x1E0:
|
elif i == 0x1C or i == 0x1E0:
|
||||||
var_name = var_names[5] if i == 0x1C else var_names[16]
|
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:
|
if not sum(data) == 0:
|
||||||
tmp_out += INDENT + f".{var_name} = {{ " + ", ".join(f"{x:02X}" for x in data) + f"}},\n"
|
tmp_out += INDENT + f".{var_name} = {{ " + ", ".join(f"{x:02X}" for x in data) + f"}},\n"
|
||||||
elif i == 0x28 or i == 0x29:
|
elif i == 0x28 or i == 0x29:
|
||||||
var_name = var_names[7] if i == 0x28 else var_names[8]
|
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 not var == 0:
|
||||||
if var_name == "dropFlags":
|
if var_name == "dropFlags":
|
||||||
tmp_out += INDENT + f".{var_name} = 0x{abs(var):02X},\n"
|
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]
|
var_name = var_names[9]
|
||||||
tmp_tmp = ""
|
tmp_tmp = ""
|
||||||
for x in range(8):
|
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):
|
if not (item == 0 and weight == 0 and unk_08 == 0):
|
||||||
item = disasm_script.CONSTANTS["ItemIDs"][item] if item in disasm_script.CONSTANTS["ItemIDs"] else f"{item}"
|
item = (
|
||||||
tmp_tmp += INDENT*2 + f"{{ {item}, {weight}, {unk_08} }},\n"
|
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
|
i += 0x6
|
||||||
|
|
||||||
if tmp_tmp:
|
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]
|
var_name = var_names[10] if i == 0x5A else var_names[11]
|
||||||
drops = []
|
drops = []
|
||||||
for x in range(8):
|
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):
|
if not (cutoff == 0 and generalChance == 0 and attempts == 0 and chancePerAttempt == 0):
|
||||||
drops.append([cutoff, generalChance, attempts, chancePerAttempt])
|
drops.append([cutoff, generalChance, attempts, chancePerAttempt])
|
||||||
i += 0x8
|
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]})"
|
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:
|
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]})"
|
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"
|
tmp_out += f"NO_DROPS"
|
||||||
else:
|
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()
|
exit()
|
||||||
else:
|
else:
|
||||||
if round(drops[0][1] / 327.67, 2) == 50 and round(drops[0][3] / 327.67, 2) == 40:
|
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]})"
|
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:
|
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]})"
|
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"
|
tmp_out += f"NO_DROPS"
|
||||||
else:
|
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()
|
exit()
|
||||||
|
|
||||||
tmp_out += f",\n"
|
tmp_out += f",\n"
|
||||||
elif i == 0xDA or i == 0xDC:
|
elif i == 0xDA or i == 0xDC:
|
||||||
var_name = var_names[12] if i == 0xDA else var_names[13]
|
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:
|
if not var == 0:
|
||||||
tmp_out += INDENT + f".{var_name} = {var},\n"
|
tmp_out += INDENT + f".{var_name} = {var},\n"
|
||||||
elif i == 0xE0:
|
elif i == 0xE0:
|
||||||
data = unpack_from(">48i", staticNpc, curr_base+i)
|
data = unpack_from(">48i", staticNpc, curr_base + i)
|
||||||
if not sum(data) == 0:
|
if not sum(data) == 0:
|
||||||
end_pos = len(data)
|
end_pos = len(data)
|
||||||
for x,datum in enumerate(data):
|
for x, datum in enumerate(data):
|
||||||
if not datum == 0:
|
if not datum == 0:
|
||||||
end_pos = x
|
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:
|
elif i == 0x1A0:
|
||||||
tmp_out += INDENT + f".{var_names[15]} = {{\n"
|
tmp_out += INDENT + f".{var_names[15]} = {{\n"
|
||||||
for x in range(16):
|
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:
|
if not anim == 0:
|
||||||
sprite_id = (anim & 0x00FF0000) >> 16
|
sprite_id = (anim & 0x00FF0000) >> 16
|
||||||
palette_id = (anim & 0x0000FF00) >> 8
|
palette_id = (anim & 0x0000FF00) >> 8
|
||||||
anim_id = (anim & 0x000000FF) >> 0
|
anim_id = (anim & 0x000000FF) >> 0
|
||||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||||
palette = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
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:
|
if numNpcs > 1:
|
||||||
tmp_out += INDENT + " " + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
tmp_out += INDENT + " " + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||||
else:
|
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)
|
INCLUDES_NEEDED["sprites"].add(sprite)
|
||||||
i += 4
|
i += 4
|
||||||
tmp_out += INDENT + f"}},\n"
|
tmp_out += INDENT + f"}},\n"
|
||||||
i -= 1
|
i -= 1
|
||||||
elif i == 0x1EC:
|
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:
|
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
|
i += 1
|
||||||
|
|
||||||
if numNpcs > 1:
|
if numNpcs > 1:
|
||||||
INDENT = INDENT[:len(INDENT)//2]
|
INDENT = INDENT[: len(INDENT) // 2]
|
||||||
tmp_out += INDENT + f"}},\n"
|
tmp_out += INDENT + f"}},\n"
|
||||||
if z+1 == numNpcs:
|
if z + 1 == numNpcs:
|
||||||
tmp_out += "};\n"
|
tmp_out += "};\n"
|
||||||
|
|
||||||
curr_base += 0x1F0
|
curr_base += 0x1F0
|
||||||
@ -464,12 +570,12 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
if anim == 0xFFFFFFFF:
|
if anim == 0xFFFFFFFF:
|
||||||
tmp_out += INDENT + f"ANIM_LIST_END,\n"
|
tmp_out += INDENT + f"ANIM_LIST_END,\n"
|
||||||
elif not anim == 0:
|
elif not anim == 0:
|
||||||
sprite_id = (anim & 0x00FF0000) >> 16
|
sprite_id = (anim & 0x00FF0000) >> 16
|
||||||
palette_id = (anim & 0x0000FF00) >> 8
|
palette_id = (anim & 0x0000FF00) >> 8
|
||||||
anim_id = (anim & 0x000000FF) >> 0
|
anim_id = (anim & 0x000000FF) >> 0
|
||||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"]
|
||||||
palette = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["palettes"][palette_id]
|
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"
|
tmp_out += INDENT + f"NPC_ANIM_{sprite}_{palette}_{anim},\n"
|
||||||
INCLUDES_NEEDED["sprites"].add(sprite)
|
INCLUDES_NEEDED["sprites"].add(sprite)
|
||||||
i += 4
|
i += 4
|
||||||
@ -487,7 +593,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
battle_b = (battle & 0x00FF0000) >> 16
|
battle_b = (battle & 0x00FF0000) >> 16
|
||||||
battle_c = (battle & 0x0000FF00) >> 8
|
battle_c = (battle & 0x0000FF00) >> 8
|
||||||
battle_d = (battle & 0x000000FF) >> 0
|
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"]:
|
if symbol_map[npcs][0][1] not in INCLUDED["functions"]:
|
||||||
INCLUDES_NEEDED["forward"].append(symbol_map[npcs][0][1])
|
INCLUDES_NEEDED["forward"].append(symbol_map[npcs][0][1])
|
||||||
i += 0xC
|
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" {disasm_script.CONSTANTS['ItemIDs'][item]},\n"
|
||||||
out += f"}};\n"
|
out += f"}};\n"
|
||||||
elif struct["type"] == "TreeDropList":
|
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
|
symbol_map[struct["vaddr"]][0][1] = new_name
|
||||||
|
|
||||||
out += f"FoliageDropList {new_name} = {{\n"
|
out += f"FoliageDropList {new_name} = {{\n"
|
||||||
@ -518,7 +627,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
pos = 4
|
pos = 4
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
entry = list(unpack_from(">7I", data, pos))
|
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[1] = entry[1] - 0x100000000 if entry[1] >= 0x80000000 else entry[1]
|
||||||
entry[2] = entry[2] - 0x100000000 if entry[2] >= 0x80000000 else entry[2]
|
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"
|
out += f"{INDENT * 3}.pos = {{ {entry[1]}, {entry[2]}, {entry[3]} }},\n"
|
||||||
if entry[4] != 0:
|
if entry[4] != 0:
|
||||||
out += f"{INDENT * 3}.spawnMode = 0x{entry[4]:X},\n"
|
out += f"{INDENT * 3}.spawnMode = 0x{entry[4]:X},\n"
|
||||||
if flag1 != '0':
|
if flag1 != "0":
|
||||||
out += f"{INDENT * 3}.pickupFlag = {flag1},\n"
|
out += f"{INDENT * 3}.pickupFlag = {flag1},\n"
|
||||||
if flag2 != '0':
|
if flag2 != "0":
|
||||||
out += f"{INDENT * 3}.spawnFlag = {flag2},\n"
|
out += f"{INDENT * 3}.spawnFlag = {flag2},\n"
|
||||||
out += f"{INDENT * 2}}},\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":
|
elif struct["type"] == "TreeModelList" or struct["type"] == "TreeEffectVectors":
|
||||||
isModelList = struct["type"] == "TreeModelList"
|
isModelList = struct["type"] == "TreeModelList"
|
||||||
|
|
||||||
name_parts = name.split('_')
|
name_parts = name.split("_")
|
||||||
if isModelList:
|
if isModelList:
|
||||||
new_name = "N(" + name_parts[1].lower() + "_" + name_parts[2]
|
new_name = "N(" + name_parts[1].lower() + "_" + name_parts[2]
|
||||||
else:
|
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[1] = entry[1] - 0x100000000 if entry[1] >= 0x80000000 else entry[1]
|
||||||
entry[2] = entry[2] - 0x100000000 if entry[2] >= 0x80000000 else entry[2]
|
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"
|
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"
|
out += f"}};\n"
|
||||||
|
|
||||||
elif struct["type"] == "SearchBushEvent":
|
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
|
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"
|
out += f"SearchBushConfig {new_name} = {{\n"
|
||||||
|
|
||||||
data = bytes.read(struct["length"])
|
data = bytes.read(struct["length"])
|
||||||
@ -621,10 +730,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
out += f"}};\n"
|
out += f"}};\n"
|
||||||
|
|
||||||
elif struct["type"] == "ShakeTreeEvent":
|
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
|
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"
|
out += f"ShakeTreeConfig {new_name} = {{\n"
|
||||||
|
|
||||||
data = bytes.read(struct["length"])
|
data = bytes.read(struct["length"])
|
||||||
@ -656,20 +765,22 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
|
|
||||||
bytes.read(0x10)
|
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" .main = &N(main),\n"
|
||||||
out += f" .entryList = &{entry_list_name},\n"
|
out += f" .entryList = &{entry_list_name},\n"
|
||||||
out += f" .entryCount = ENTRY_COUNT({entry_list_name}),\n"
|
out += f" .entryCount = ENTRY_COUNT({entry_list_name}),\n"
|
||||||
|
|
||||||
bytes.read(0x1C)
|
bytes.read(0x1C)
|
||||||
|
|
||||||
bg,tattle = unpack(">II", bytes.read(4 * 2))
|
bg, tattle = unpack(">II", bytes.read(4 * 2))
|
||||||
if bg == 0x80200000:
|
if bg == 0x80200000:
|
||||||
out += f" .background = &gBackgroundImage,\n"
|
out += f" .background = &gBackgroundImage,\n"
|
||||||
elif bg != 0:
|
elif bg != 0:
|
||||||
raise Exception(f"unknown MapSettings background {bg:X}")
|
raise Exception(f"unknown MapSettings background {bg:X}")
|
||||||
#out += f" .tattle = 0x{tattle:X},\n"
|
# out += f" .tattle = 0x{tattle:X},\n"
|
||||||
INCLUDES_NEEDED["tattle"].append(f"- [0x{(tattle & 0xFF0000) >> 16:02X}, 0x{tattle & 0xFFFF:04X}, {map_name}_tattle]")
|
INCLUDES_NEEDED["tattle"].append(
|
||||||
|
f"- [0x{(tattle & 0xFF0000) >> 16:02X}, 0x{tattle & 0xFFFF:04X}, {map_name}_tattle]"
|
||||||
|
)
|
||||||
out += f" .tattle = {{ MSG_{map_name}_tattle }},\n"
|
out += f" .tattle = {{ MSG_{map_name}_tattle }},\n"
|
||||||
|
|
||||||
out += f"}};\n"
|
out += f"}};\n"
|
||||||
@ -678,7 +789,7 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
bytes.read(struct["length"])
|
bytes.read(struct["length"])
|
||||||
out += f"s32 {name}();\n"
|
out += f"s32 {name}();\n"
|
||||||
elif struct["type"] == "FloatTable":
|
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})"
|
name = f"N(D_{vram:X}_{(vram - 0x80240000) + romstart:X})"
|
||||||
struct["name"] = name
|
struct["name"] = name
|
||||||
out += f"f32 {name}[] = {{"
|
out += f"f32 {name}[] = {{"
|
||||||
@ -695,10 +806,10 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
out += f"Vec3f {name}[] = {{\n"
|
out += f"Vec3f {name}[] = {{\n"
|
||||||
out += f"\t"
|
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)
|
x, y, z = unpack_from(">fff", data, pos)
|
||||||
out += f" {{ {x:.01f}, {y:.01f}, {z:.01f} }},"
|
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\t"
|
||||||
out += f"\n}};\n"
|
out += f"\n}};\n"
|
||||||
|
|
||||||
@ -721,7 +832,6 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
else:
|
else:
|
||||||
out += f".actor = {actor}, "
|
out += f".actor = {actor}, "
|
||||||
|
|
||||||
|
|
||||||
if position in symbol_map:
|
if position in symbol_map:
|
||||||
out += f".home = {{ .vec = &{symbol_map[position][0][1]} }}"
|
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]
|
element = disasm_script.CONSTANTS["Statuses"][element]
|
||||||
|
|
||||||
value = (anim & 0x00FFFFFF)
|
value = anim & 0x00FFFFFF
|
||||||
|
|
||||||
if value in disasm_script.CONSTANTS["NPC_SPRITE"]:
|
if value in disasm_script.CONSTANTS["NPC_SPRITE"]:
|
||||||
INCLUDES_NEEDED["sprites"].add(disasm_script.CONSTANTS['NPC_SPRITE'][str(value) + ".h"])
|
INCLUDES_NEEDED["sprites"].add(disasm_script.CONSTANTS["NPC_SPRITE"][str(value) + ".h"])
|
||||||
anim = disasm_script.CONSTANTS['NPC_SPRITE'][value]
|
anim = disasm_script.CONSTANTS["NPC_SPRITE"][value]
|
||||||
else:
|
else:
|
||||||
anim = f"{anim:06X}"
|
anim = f"{anim:06X}"
|
||||||
|
|
||||||
@ -851,15 +961,15 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
out += INDENT + "{\n"
|
out += INDENT + "{\n"
|
||||||
out += INDENT + INDENT + f".flags = {read_flags(d[0], 'ActorPartFlags')},\n"
|
out += INDENT + INDENT + f".flags = {read_flags(d[0], 'ActorPartFlags')},\n"
|
||||||
out += INDENT + INDENT + f".index = {d[1]},\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".posOffset = {{ {d[2]}, {d[3]}, {d[4]} }},\n"
|
||||||
out += INDENT + INDENT + f".targetOffset = {{ {d[5]}, {d[6]} }},\n"
|
out += INDENT + INDENT + f".targetOffset = {{ {d[5]}, {d[6]} }},\n"
|
||||||
out += INDENT + INDENT + f".opacity = {d[7]},\n"
|
out += INDENT + INDENT + f".opacity = {d[7]},\n"
|
||||||
out += INDENT + INDENT + f".idleAnimations = N(IdleAnimations_{d[8]:08X}),\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".defenseTable = N(DefenseTable_{d[9]:08X}),\n"
|
||||||
out += INDENT + INDENT + f".eventFlags = {read_flags(d[10], 'ActorEventFlags')},\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".elementImmunityFlags = {read_flags(d[11], 'ElementImmunityFlags')},\n"
|
||||||
out += INDENT + INDENT + f".unk_1C = {d[12]},\n"
|
out += INDENT + INDENT + f".unk_1C = {d[12]},\n"
|
||||||
out += INDENT + INDENT + f".unk_1D = {d[13]},\n"
|
out += INDENT + INDENT + f".unk_1D = {d[13]},\n"
|
||||||
out += INDENT + "},\n"
|
out += INDENT + "},\n"
|
||||||
|
|
||||||
out += f"}};\n"
|
out += f"}};\n"
|
||||||
@ -895,7 +1005,18 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
elif struct["type"] == "Stage":
|
elif struct["type"] == "Stage":
|
||||||
out += f"Stage NAMESPACE = {{\n"
|
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:
|
if texture != 0:
|
||||||
out += f" .texture = {symbol_map[texture][0][1]},\n"
|
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" .unk_24 = {unk_24:X},\n"
|
||||||
|
|
||||||
out += f"}};\n"
|
out += f"}};\n"
|
||||||
else: # unknown type of struct
|
else: # unknown type of struct
|
||||||
if struct["name"].startswith("N(unk_802"):
|
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})"
|
name = f"N(D_{vram:X}_{(vram - 0x80240000) + romstart:X})"
|
||||||
struct["name"] = name
|
struct["name"] = name
|
||||||
|
|
||||||
@ -957,53 +1078,63 @@ def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0, namespace
|
|||||||
# end of data
|
# end of data
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def parse_midx(file, prefix="", vram=0x80240000):
|
def parse_midx(file, prefix="", vram=0x80240000):
|
||||||
structs = []
|
structs = []
|
||||||
|
|
||||||
for line in file.readlines():
|
for line in file.readlines():
|
||||||
s = line.split("#")
|
s = line.split("#")
|
||||||
if len(s) == 5 or len(s) == 6:
|
if len(s) == 5 or len(s) == 6:
|
||||||
if s[0] == "$Start": continue
|
if s[0] == "$Start":
|
||||||
if s[0] == "$End": continue
|
continue
|
||||||
|
if s[0] == "$End":
|
||||||
|
continue
|
||||||
|
|
||||||
structs.append({
|
structs.append(
|
||||||
"name": "N(" + prefix + name_struct(s[0]) + ")",
|
{
|
||||||
"type": s[1],
|
"name": "N(" + prefix + name_struct(s[0]) + ")",
|
||||||
"start": int(s[2], 16),
|
"type": s[1],
|
||||||
"vaddr": int(s[3], 16),
|
"start": int(s[2], 16),
|
||||||
"length": int(s[4], 16),
|
"vaddr": int(s[3], 16),
|
||||||
"end": int(s[2], 16) + int(s[4], 16),
|
"length": int(s[4], 16),
|
||||||
})
|
"end": int(s[2], 16) + int(s[4], 16),
|
||||||
|
}
|
||||||
|
)
|
||||||
elif "Missing" in s:
|
elif "Missing" in s:
|
||||||
start = int(s[1], 16)
|
start = int(s[1], 16)
|
||||||
end = int(s[2], 16)
|
end = int(s[2], 16)
|
||||||
vaddr = start + vram
|
vaddr = start + vram
|
||||||
structs.append({
|
structs.append(
|
||||||
"name": f"{prefix}unk_missing_{vaddr:X}",
|
{
|
||||||
"type": "Missing",
|
"name": f"{prefix}unk_missing_{vaddr:X}",
|
||||||
"start": start,
|
"type": "Missing",
|
||||||
"vaddr": vaddr,
|
"start": start,
|
||||||
"length": end - start,
|
"vaddr": vaddr,
|
||||||
"end": end,
|
"length": end - start,
|
||||||
})
|
"end": end,
|
||||||
|
}
|
||||||
|
)
|
||||||
elif "Padding" in s:
|
elif "Padding" in s:
|
||||||
start = int(s[1], 16)
|
start = int(s[1], 16)
|
||||||
end = int(s[2], 16)
|
end = int(s[2], 16)
|
||||||
vaddr = start + vram
|
vaddr = start + vram
|
||||||
structs.append({
|
structs.append(
|
||||||
"name": f"{prefix}pad_{start:X}",
|
{
|
||||||
"type": "Padding",
|
"name": f"{prefix}pad_{start:X}",
|
||||||
"start": start,
|
"type": "Padding",
|
||||||
"vaddr": vaddr,
|
"start": start,
|
||||||
"length": end - start,
|
"vaddr": vaddr,
|
||||||
"end": end,
|
"length": end - start,
|
||||||
})
|
"end": end,
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception(str(s))
|
raise Exception(str(s))
|
||||||
|
|
||||||
structs.sort(key=lambda s: s["start"])
|
structs.sort(key=lambda s: s["start"])
|
||||||
return structs
|
return structs
|
||||||
|
|
||||||
|
|
||||||
def name_struct(s):
|
def name_struct(s):
|
||||||
s = s[1:].replace("???", "unk")
|
s = s[1:].replace("???", "unk")
|
||||||
|
|
||||||
@ -1032,10 +1163,11 @@ def name_struct(s):
|
|||||||
|
|
||||||
return s[0].lower() + s[1:]
|
return s[0].lower() + s[1:]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Converts split data to C using a Star Rod idx file")
|
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("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")
|
parser.add_argument("--comments", action="store_true", help="Write offset/vaddr comments")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -1053,8 +1185,7 @@ if __name__ == "__main__":
|
|||||||
segment_name = f"battle/partners/{battle_area}"
|
segment_name = f"battle/partners/{battle_area}"
|
||||||
elif "/starpower/src/" in args.idxfile:
|
elif "/starpower/src/" in args.idxfile:
|
||||||
segment_name = (
|
segment_name = (
|
||||||
f"battle/star/{battle_area}"
|
f"battle/star/{battle_area}".replace("starstorm", "star_storm")
|
||||||
.replace("starstorm", "star_storm")
|
|
||||||
.replace("chillout", "chill_out")
|
.replace("chillout", "chill_out")
|
||||||
.replace("timeout", "time_out")
|
.replace("timeout", "time_out")
|
||||||
.replace("upandaway", "up_and_away")
|
.replace("upandaway", "up_and_away")
|
||||||
@ -1069,6 +1200,7 @@ if __name__ == "__main__":
|
|||||||
is_battle = True
|
is_battle = True
|
||||||
|
|
||||||
symbol_map = disasm_script.script_lib()
|
symbol_map = disasm_script.script_lib()
|
||||||
|
|
||||||
def add_to_symbol_map(addr, pair):
|
def add_to_symbol_map(addr, pair):
|
||||||
if addr in symbol_map:
|
if addr in symbol_map:
|
||||||
symbol_map[addr].append(pair)
|
symbol_map[addr].append(pair)
|
||||||
@ -1082,7 +1214,9 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
rom_offset = -1
|
rom_offset = -1
|
||||||
for segment in splat_config["segments"]:
|
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"]
|
rom_offset = segment["start"]
|
||||||
vram = segment["vram"]
|
vram = segment["vram"]
|
||||||
break
|
break
|
||||||
@ -1102,11 +1236,11 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
with open(os.path.join(DIR, "../ver/current/baserom.z64"), "rb") as romfile:
|
with open(os.path.join(DIR, "../ver/current/baserom.z64"), "rb") as romfile:
|
||||||
name_fixes = {
|
name_fixes = {
|
||||||
"script_NpcAI": "npcAI",
|
"script_NpcAI": "npcAI",
|
||||||
"aISettings": "npcAISettings",
|
"aISettings": "npcAISettings",
|
||||||
"script_ExitWalk": "exitWalk",
|
"script_ExitWalk": "exitWalk",
|
||||||
"script_MakeEntities": "makeEntities",
|
"script_MakeEntities": "makeEntities",
|
||||||
}
|
}
|
||||||
total_npc_counts = {}
|
total_npc_counts = {}
|
||||||
for struct in midx:
|
for struct in midx:
|
||||||
romfile.seek(struct["start"] + rom_offset)
|
romfile.seek(struct["start"] + rom_offset)
|
||||||
@ -1116,13 +1250,13 @@ if __name__ == "__main__":
|
|||||||
if name.startswith("N("):
|
if name.startswith("N("):
|
||||||
name = name[2:-1]
|
name = name[2:-1]
|
||||||
|
|
||||||
if struct['vaddr'] in function_replacements:
|
if struct["vaddr"] in function_replacements:
|
||||||
name = function_replacements[struct['vaddr']]
|
name = function_replacements[struct["vaddr"]]
|
||||||
|
|
||||||
if name.split("_",1)[0] in name_fixes:
|
if name.split("_", 1)[0] in name_fixes:
|
||||||
name = name_fixes[name.split("_",1)[0]] + "_" + name.rsplit("_",1)[1]
|
name = name_fixes[name.split("_", 1)[0]] + "_" + name.rsplit("_", 1)[1]
|
||||||
elif name.startswith("script_"):
|
elif name.startswith("script_"):
|
||||||
name = name.split("script_",1)[1]
|
name = name.split("script_", 1)[1]
|
||||||
elif "_Main_" in name:
|
elif "_Main_" in name:
|
||||||
name = "main"
|
name = "main"
|
||||||
elif "ASCII" in name:
|
elif "ASCII" in name:
|
||||||
@ -1154,19 +1288,19 @@ if __name__ == "__main__":
|
|||||||
double_literal = f"{double}"
|
double_literal = f"{double}"
|
||||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], double_literal])
|
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], double_literal])
|
||||||
elif struct["type"] == "NpcGroup":
|
elif struct["type"] == "NpcGroup":
|
||||||
for z in range(struct["length"]//0x1F0):
|
for z in range(struct["length"] // 0x1F0):
|
||||||
npc = romfile.read(0x1F0)
|
npc = romfile.read(0x1F0)
|
||||||
npc_id = unpack_from(">I", npc, 0)[0]
|
npc_id = unpack_from(">I", npc, 0)[0]
|
||||||
if npc_id >= 0:
|
if npc_id >= 0:
|
||||||
anim = unpack_from(">I", npc, 0x1A0)[0]
|
anim = unpack_from(">I", npc, 0x1A0)[0]
|
||||||
if not anim == 0:
|
if not anim == 0:
|
||||||
sprite_id = (anim & 0x00FF0000) >> 16
|
sprite_id = (anim & 0x00FF0000) >> 16
|
||||||
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"].upper()
|
sprite = disasm_script.CONSTANTS["NPC_SPRITE"][sprite_id]["name"].upper()
|
||||||
if npc_id not in total_npc_counts:
|
if npc_id not in total_npc_counts:
|
||||||
total_npc_counts[npc_id] = sprite
|
total_npc_counts[npc_id] = sprite
|
||||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
||||||
else:
|
else:
|
||||||
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
add_to_symbol_map(struct["vaddr"], [struct["vaddr"], struct["name"]])
|
||||||
|
|
||||||
# fix NPC names
|
# fix NPC names
|
||||||
curr_counts = {}
|
curr_counts = {}
|
||||||
@ -1186,17 +1320,24 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
romfile.seek(rom_offset, 0)
|
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")
|
print("========== Includes needed: ===========\n")
|
||||||
if is_battle:
|
if is_battle:
|
||||||
print(f"#include \"battle/battle.h\"")
|
print(f'#include "battle/battle.h"')
|
||||||
else:
|
else:
|
||||||
print(f"#include \"map.h\"")
|
print(f'#include "map.h"')
|
||||||
print(f"#include \"message_ids.h\"")
|
print(f'#include "message_ids.h"')
|
||||||
if INCLUDES_NEEDED["sprites"]:
|
if INCLUDES_NEEDED["sprites"]:
|
||||||
for npc in sorted(INCLUDES_NEEDED["sprites"]):
|
for npc in sorted(INCLUDES_NEEDED["sprites"]):
|
||||||
print(f"#include \"sprite/npc/{npc}\"")
|
print(f'#include "sprite/npc/{npc}"')
|
||||||
print()
|
print()
|
||||||
|
|
||||||
if INCLUDES_NEEDED["forward"]:
|
if INCLUDES_NEEDED["forward"]:
|
||||||
@ -1213,7 +1354,7 @@ if __name__ == "__main__":
|
|||||||
print(f"enum {{")
|
print(f"enum {{")
|
||||||
lastnum = -1
|
lastnum = -1
|
||||||
for i, (k, v) in enumerate(sorted(INCLUDES_NEEDED["npcs"].items())):
|
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
|
lastnum = k
|
||||||
print(f"}};")
|
print(f"}};")
|
||||||
print()
|
print()
|
||||||
|
@ -6,29 +6,24 @@ import argparse
|
|||||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
|
root_dir = os.path.abspath(os.path.join(script_dir, ".."))
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Display various information about a symbol or address.")
|
||||||
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(
|
|
||||||
"name",
|
|
||||||
type=str,
|
|
||||||
default="",
|
|
||||||
help="symbol name or ROM/RAM address to lookup"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e",
|
"-e",
|
||||||
"--expected",
|
"--expected",
|
||||||
dest="use_expected",
|
dest="use_expected",
|
||||||
action="store_true",
|
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):
|
def get_map(expected: bool = False):
|
||||||
mymap = os.path.join(root_dir, "ver", "current", "build", "papermario.map")
|
mymap = os.path.join(root_dir, "ver", "current", "build", "papermario.map")
|
||||||
if expected:
|
if expected:
|
||||||
mymap = os.path.join(root_dir, "ver", "current", "expected", "build", "papermario.map")
|
mymap = os.path.join(root_dir, "ver", "current", "expected", "build", "papermario.map")
|
||||||
return mymap
|
return mymap
|
||||||
|
|
||||||
|
|
||||||
def search_address(target_addr, map=get_map()):
|
def search_address(target_addr, map=get_map()):
|
||||||
is_ram = target_addr & 0x80000000
|
is_ram = target_addr & 0x80000000
|
||||||
ram_offset = None
|
ram_offset = None
|
||||||
@ -52,12 +47,7 @@ def search_address(target_addr, map=get_map()):
|
|||||||
|
|
||||||
prev_line = line
|
prev_line = line
|
||||||
|
|
||||||
if (
|
if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line:
|
||||||
ram_offset is None
|
|
||||||
or "=" in line
|
|
||||||
or "*fill*" in line
|
|
||||||
or " 0x" not in line
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ram = int(line[16 : 16 + 18], 0)
|
ram = int(line[16 : 16 + 18], 0)
|
||||||
@ -84,6 +74,7 @@ def search_address(target_addr, map=get_map()):
|
|||||||
|
|
||||||
return "at end of rom?"
|
return "at end of rom?"
|
||||||
|
|
||||||
|
|
||||||
def search_symbol(target_sym, map=get_map()):
|
def search_symbol(target_sym, map=get_map()):
|
||||||
ram_offset = None
|
ram_offset = None
|
||||||
cur_file = "<no file>"
|
cur_file = "<no file>"
|
||||||
@ -98,12 +89,7 @@ def search_symbol(target_sym, map=get_map()):
|
|||||||
|
|
||||||
prev_line = line
|
prev_line = line
|
||||||
|
|
||||||
if (
|
if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line:
|
||||||
ram_offset is None
|
|
||||||
or "=" in line
|
|
||||||
or "*fill*" in line
|
|
||||||
or " 0x" not in line
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ram = int(line[16 : 16 + 18], 0)
|
ram = int(line[16 : 16 + 18], 0)
|
||||||
@ -122,6 +108,7 @@ def search_symbol(target_sym, map=get_map()):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ ignores = set()
|
|||||||
|
|
||||||
verbose = False
|
verbose = False
|
||||||
|
|
||||||
|
|
||||||
def read_ignores():
|
def read_ignores():
|
||||||
with open(ignores_path) as f:
|
with open(ignores_path) as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
@ -35,6 +36,7 @@ def read_ignores():
|
|||||||
if name != "":
|
if name != "":
|
||||||
ignores.add(name)
|
ignores.add(name)
|
||||||
|
|
||||||
|
|
||||||
def scan_map():
|
def scan_map():
|
||||||
ram_offset = None
|
ram_offset = None
|
||||||
cur_file = "<no file>"
|
cur_file = "<no file>"
|
||||||
@ -49,12 +51,7 @@ def scan_map():
|
|||||||
|
|
||||||
prev_line = line
|
prev_line = line
|
||||||
|
|
||||||
if (
|
if ram_offset is None or "=" in line or "*fill*" in line or " 0x" not in line:
|
||||||
ram_offset is None
|
|
||||||
or "=" in line
|
|
||||||
or "*fill*" in line
|
|
||||||
or " 0x" not in line
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ram = int(line[16 : 16 + 18], 0)
|
ram = int(line[16 : 16 + 18], 0)
|
||||||
@ -70,12 +67,13 @@ def scan_map():
|
|||||||
|
|
||||||
map_symbols[sym] = (rom, cur_file, ram)
|
map_symbols[sym] = (rom, cur_file, ram)
|
||||||
|
|
||||||
|
|
||||||
def read_symbol_addrs():
|
def read_symbol_addrs():
|
||||||
unique_lines = set()
|
unique_lines = set()
|
||||||
|
|
||||||
with open(symbol_addrs_path, "r") as f:
|
with open(symbol_addrs_path, "r") as f:
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
unique_lines.add(line)
|
unique_lines.add(line)
|
||||||
|
|
||||||
for line in unique_lines:
|
for line in unique_lines:
|
||||||
if "_ROM_START" in line or "_ROM_END" in line:
|
if "_ROM_START" in line or "_ROM_END" in line:
|
||||||
@ -117,9 +115,10 @@ def read_symbol_addrs():
|
|||||||
else:
|
else:
|
||||||
dead_symbols.append([name, int(addr, 0), type, rom, opts])
|
dead_symbols.append([name, int(addr, 0), type, rom, opts])
|
||||||
|
|
||||||
|
|
||||||
def read_elf():
|
def read_elf():
|
||||||
try:
|
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")
|
objdump_lines = result.stdout.decode().split("\n")
|
||||||
except:
|
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")
|
||||||
@ -133,13 +132,15 @@ def read_elf():
|
|||||||
if "_ROM_START" in name or "_ROM_END" in name:
|
if "_ROM_START" in name or "_ROM_END" in name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if "/" in name or \
|
if (
|
||||||
"." in name or \
|
"/" in name
|
||||||
name in ignores or \
|
or "." in name
|
||||||
name.startswith("_") or \
|
or name in ignores
|
||||||
name.startswith("jtbl_") or \
|
or name.startswith("_")
|
||||||
name.endswith(".o") or \
|
or name.startswith("jtbl_")
|
||||||
re.match(r"L[0-9A-F]{8}", name):
|
or name.endswith(".o")
|
||||||
|
or re.match(r"L[0-9A-F]{8}", name)
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
addr = int(components[0], 16)
|
addr = int(components[0], 16)
|
||||||
@ -153,14 +154,16 @@ def read_elf():
|
|||||||
if name in map_symbols:
|
if name in map_symbols:
|
||||||
rom = map_symbols[name][0]
|
rom = map_symbols[name][0]
|
||||||
elif re.match(".*_[0-9A-F]{8}_[0-9A-F]{6}", name):
|
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))
|
elf_symbols.append((name, addr, type, rom))
|
||||||
|
|
||||||
|
|
||||||
def log(s):
|
def log(s):
|
||||||
if verbose:
|
if verbose:
|
||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
|
|
||||||
def reconcile_symbols():
|
def reconcile_symbols():
|
||||||
print(f"Processing {str(len(elf_symbols))} elf symbols...")
|
print(f"Processing {str(len(elf_symbols))} elf symbols...")
|
||||||
|
|
||||||
@ -175,7 +178,9 @@ def reconcile_symbols():
|
|||||||
name_match = known_sym
|
name_match = known_sym
|
||||||
|
|
||||||
if elf_sym[1] != known_sym[1]:
|
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
|
# Rom
|
||||||
if not rom_match:
|
if not rom_match:
|
||||||
@ -186,7 +191,15 @@ def reconcile_symbols():
|
|||||||
|
|
||||||
if not name_match and not rom_match:
|
if not name_match and not rom_match:
|
||||||
log(f"Creating new symbol {elf_sym[0]}")
|
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:
|
elif not name_match:
|
||||||
log(f"Renaming identical rom address symbol {rom_match[0]} to {elf_sym[0]}")
|
log(f"Renaming identical rom address symbol {rom_match[0]} to {elf_sym[0]}")
|
||||||
rom_match[0] = 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]}")
|
log(f"Adding rom address {elf_sym[3]} to symbol {name_match[0]}")
|
||||||
name_match[3] = elf_sym[3]
|
name_match[3] = elf_sym[3]
|
||||||
|
|
||||||
|
|
||||||
def write_new_symbol_addrs():
|
def write_new_symbol_addrs():
|
||||||
with open(symbol_addrs_path, "w", newline="\n") as f:
|
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])):
|
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}"
|
line += f" {thing}"
|
||||||
f.write(line + "\n")
|
f.write(line + "\n")
|
||||||
|
|
||||||
|
|
||||||
read_ignores()
|
read_ignores()
|
||||||
scan_map()
|
scan_map()
|
||||||
read_symbol_addrs()
|
read_symbol_addrs()
|
||||||
|
@ -11,8 +11,14 @@ def countFileLines(filename: str) -> int:
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('currentwarnings', help="Name of file which contains the current warnings of the repo.")
|
parser.add_argument(
|
||||||
parser.add_argument('newwarnings', help="Name of file which contains the *new* warnings of the repo.")
|
"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")
|
parser.add_argument("--pr-message", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user