papermario/tools/old/update_evts.py

191 lines
5.8 KiB
Python
Raw Normal View History

#!/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",
2022-01-01 14:08:04 +01:00
"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>")