papermario/diff_evt.py

162 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import queue
import time
import subprocess
import sys
from diff import Display, debounced_fs_watch
from colorama import Fore, Style
sys.path.append("tools")
from old.update_evts import parse_symbol_addrs
from tools.disasm_script import ScriptDisassembler, get_constants
parser = argparse.ArgumentParser(description="Diff EVT macros.")
parser.add_argument(
"start",
help="Symbol name or ROM address to start diffing from.",
)
parser.add_argument(
"-w",
"--watch",
action="store_true",
help="Watch for file changes and update the diff automatically.",
)
parser.add_argument("-m", "--make", action="store_true", help="Run ninja automatically.")
parser.add_argument("-o", action="store_true", help="Ignored for compatibility with diff.py.")
class EvtDisplay(Display):
def __init__(self, start):
self.config = None
self.base_lines = None
self.mydump = None
self.emsg = None
self.last_refresh_key = None
self.last_diff_output = None
self.start = start
def run_diff(self):
if self.emsg is not None:
return (self.emsg, self.emsg)
symbols = parse_symbol_addrs()
if self.start in symbols:
start = symbols[self.start].rom_addr
else:
try:
start = int(self.start, base=0)
except ValueError:
print("Start symbol name is not known or is an invalid address.")
exit(2)
with open("ver/current/build/papermario.z64", "rb") as f:
f.seek(start)
current = ScriptDisassembler(f, romstart=start, prelude=False).disassemble()
with open("ver/current/baserom.z64", "rb") as f:
f.seek(start)
target = ScriptDisassembler(f, romstart=start, prelude=False).disassemble()
current = current.splitlines()
target = target.splitlines()
score = 0
output = ""
for i in range(max(len(current), len(target))):
if i >= len(current):
current.append("")
if i >= len(target):
target.append("")
current_line = current[i][4:]
target_line = target[i][4:]
output += Style.RESET_ALL
if current_line != target_line:
output += Fore.RED
score += 10
else:
output += Style.DIM
output += f"{target_line:<60} {current_line:<60}\n"
output = f"{'TARGET':<60} {f'CURRENT ({score})':<60}\n" + output
refresh_key = (current, target)
return (output, refresh_key)
class FakeConfig:
def __init__(self, args):
self.make = args.make
self.source_extensions = ["c", "h"]
def run_ninja():
return subprocess.run(
["ninja", "ver/current/build/papermario.z64"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
def main():
args = parser.parse_args()
get_constants()
display = EvtDisplay(args.start)
if args.watch:
if not args.make:
yn = input(
"Warning: watch-mode (-w) enabled without auto-ninja (-m). "
"You will have to run ninja manually. Ok? (Y/n) "
)
if yn.lower() == "n":
return
if args.make:
watch_sources = ["src", "include"]
else:
watch_sources = ["ver/current/build/papermario.z64"]
q = queue.Queue()
debounced_fs_watch(watch_sources, q, FakeConfig(args), FakeConfig(args))
display.run_async(q)
last_build = 0.0
try:
while True:
t = q.get()
if t is None:
break
if t < last_build:
continue
last_build = time.time()
if args.make:
display.progress("Building...")
ret = run_ninja()
if ret.returncode != 0:
display.update(
ret.stderr.decode("utf-8-sig", "replace") or ret.stdout.decode("utf-8-sig", "replace"),
error=True,
)
continue
display.update(last_build, error=False)
except KeyboardInterrupt:
display.terminate()
else:
display.run_sync()
if __name__ == "__main__":
main()