mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 12:02:30 +01:00
ae66312d8c
* 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>
191 lines
5.8 KiB
Python
Executable File
191 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import re
|
|
|
|
from disasm_script import ScriptDisassembler, get_constants
|
|
from glob import glob
|
|
import os
|
|
|
|
NAMESPACES = {
|
|
"src/battle/area_kmr_part_1/stage/clouds.inc.c": "b_area_kmr_part_1_kmr_03",
|
|
"src/common/foliage.inc.c": "kmr_03",
|
|
}
|
|
|
|
for filename in glob("src/battle/item/*.c"):
|
|
parts = filename.split("/")[1:]
|
|
parts[-1] = parts[-1].split(".")[0]
|
|
NAMESPACES[filename] = "_".join(ActorParts)
|
|
|
|
NAMESPACES["src/battle/item/UseItem.inc.c"] = "battle_item_food"
|
|
|
|
for filename in glob("src/world/*/*/*.c"):
|
|
map_name = filename.split("/")[3]
|
|
NAMESPACES[filename] = map_name
|
|
|
|
|
|
class UserException(Exception):
|
|
pass
|
|
|
|
|
|
class Range:
|
|
def __init__(self, start: int, end: int, symbol_name: str, namespace: str):
|
|
self.start = start
|
|
self.end = end
|
|
self.symbol_name = symbol_name
|
|
self.namespace = namespace
|
|
|
|
|
|
class Symbol:
|
|
def __init__(self, ram_addr, rom_addr):
|
|
self.ram_addr = ram_addr
|
|
self.rom_addr = rom_addr
|
|
|
|
|
|
def parse_symbol_addrs():
|
|
with open("ver/us/symbol_addrs.txt", "r") as f:
|
|
lines = f.readlines()
|
|
|
|
symbol_addrs = {}
|
|
|
|
for line in lines:
|
|
name = line[: line.find(" ")]
|
|
|
|
attributes = line[line.find("//") :].split(" ")
|
|
ram_addr = int(line[: line.find(";")].split("=")[1].strip(), base=0)
|
|
rom_addr = next(
|
|
(int(attr.split(":")[1], base=0) for attr in attributes if attr.split(":")[0] == "rom"),
|
|
None,
|
|
)
|
|
|
|
symbol_addrs[name] = Symbol(ram_addr, rom_addr)
|
|
|
|
return symbol_addrs
|
|
|
|
|
|
def find_old_script_ranges(lines, filename):
|
|
"""
|
|
Finds all ranges that contain the old SCRIPT macro.
|
|
"""
|
|
|
|
start_line_no = None
|
|
symbol_name = None
|
|
namespace = NAMESPACES.get(filename, filename.split("/")[-1].split(".")[0])
|
|
|
|
for line_no, line_content in enumerate(lines):
|
|
r = re.compile(r".*\.h")
|
|
namespace_temp = list(filter(r.match, os.listdir(os.path.dirname(os.path.abspath(filename)))))
|
|
|
|
if "#define NAMESPACE " in line_content:
|
|
namespace = line_content.split(" ")[2].strip()
|
|
# elif namespace == "events" or namespace == "header":
|
|
# namespace = NAMESPACES.get(filename, filename.split("/")[-2].split(".")[0])
|
|
elif namespace_temp is not None:
|
|
namespace = namespace_temp[0][:-2]
|
|
|
|
if "SCRIPT({" in line_content:
|
|
start_line_no = line_no
|
|
symbol_name = eval_namespace(line_content.split(" ")[1], namespace)
|
|
elif "});" in line_content and start_line_no is not None:
|
|
yield Range(start_line_no, line_no, symbol_name, namespace)
|
|
start_line_no = None
|
|
|
|
|
|
def eval_namespace(sym, namespace):
|
|
if sym.startswith("N("):
|
|
return namespace + "_" + sym[2:-1]
|
|
else:
|
|
return sym
|
|
|
|
|
|
def replace_old_script_macros(filename, symbol_addrs):
|
|
with open(filename, "r") as f:
|
|
lines = f.readlines()
|
|
|
|
with open("ver/us/baserom.z64", "rb") as rom:
|
|
num_scripts_replaced = 0
|
|
|
|
while True:
|
|
try:
|
|
range = next(find_old_script_ranges(lines, filename))
|
|
except StopIteration:
|
|
break
|
|
|
|
# Replace initial line
|
|
macro_start_idx = lines[range.start].find("SCRIPT({\n")
|
|
if macro_start_idx == -1:
|
|
raise Exception("Could not find SCRIPT macro start")
|
|
lines[range.start] = lines[range.start][:macro_start_idx] + "{\n"
|
|
|
|
# Remove other lines
|
|
lines = lines[: range.start + 1] + lines[range.end + 1 :]
|
|
|
|
# Find the symbol
|
|
try:
|
|
range_sym = symbol_addrs[range.symbol_name]
|
|
except KeyError:
|
|
raise UserException(f"Symbol {range.symbol_name} is not in symbol_addrs")
|
|
|
|
if not range_sym.rom_addr:
|
|
raise UserException(f"Symbol {range.symbol_name} lacks a rom address in symbol_addrs")
|
|
|
|
# Make local symbol map, replacing namespaced symbols with N(sym)
|
|
local_symbol_map = {}
|
|
for sym in symbol_addrs:
|
|
if sym.startswith(range.namespace):
|
|
key = "N(" + sym[len(range.namespace) + 1 :] + ")"
|
|
else:
|
|
key = sym
|
|
|
|
symbol = symbol_addrs[sym]
|
|
|
|
if symbol.ram_addr in local_symbol_map:
|
|
cur_sym_name = local_symbol_map[symbol.ram_addr][0][1]
|
|
if cur_sym_name.startswith("N("):
|
|
continue
|
|
|
|
local_symbol_map[symbol.ram_addr] = [[symbol.ram_addr, key]]
|
|
|
|
# Disassemble the script
|
|
rom.seek(range_sym.rom_addr)
|
|
evt_code = ScriptDisassembler(
|
|
rom,
|
|
script_name=range.symbol_name,
|
|
romstart=range_sym.rom_addr,
|
|
prelude=False,
|
|
symbol_map=local_symbol_map,
|
|
).disassemble()
|
|
lines.insert(range.start + 1, f"{evt_code}}};\n")
|
|
|
|
num_scripts_replaced += 1
|
|
|
|
if num_scripts_replaced > 0:
|
|
with open(filename, "w") as f:
|
|
f.writelines(lines)
|
|
|
|
return num_scripts_replaced
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
symbol_addrs = parse_symbol_addrs()
|
|
get_constants()
|
|
|
|
num_errors = 0
|
|
|
|
if len(sys.argv) > 1:
|
|
for filename in sys.argv[1:]:
|
|
try:
|
|
num_scripts_replaced = replace_old_script_macros(filename, symbol_addrs)
|
|
if num_scripts_replaced > 0:
|
|
print(f"{num_scripts_replaced} old scripts replaced in {filename}")
|
|
except UserException as e:
|
|
print(f"{filename} ERROR: {e}")
|
|
num_errors += 1
|
|
|
|
if num_errors > 0:
|
|
print(f"ERROR: {num_errors} files had errors.")
|
|
exit(1)
|
|
else:
|
|
print("warning: no files specified")
|
|
print("usage: ./tools/update_evts.c <files to modify>")
|