papermario/tools/disasm_script.py

647 lines
29 KiB
Python
Raw Normal View History

2020-10-19 23:58:57 +02:00
#! /usr/bin/python3
import sys
2020-10-31 03:28:18 +01:00
_script_lib = None
def script_lib():
global _script_lib
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
if not _script_lib:
_script_lib = {}
2020-10-19 23:58:57 +02:00
from pathlib import Path
from os import path
import re
2020-10-31 03:28:18 +01:00
# star rod database
"""
2020-10-19 23:58:57 +02:00
LIB_LINE_RE = re.compile(r"\s+:\s+")
NAME_RE = re.compile(r"({[^}]*})?\s*([a-zA-Z0-9_]+)")
for filename in Path(path.dirname(__file__), "star-rod", "database").rglob("*.lib"):
with open(filename, "r") as file:
for line in file.readlines():
parts = LIB_LINE_RE.split(line)
if len(parts) >= 3:
try:
kind = parts[0]
vaddr = int(parts[1].split(", ")[0], 16)
if name := NAME_RE.match(parts[2]):
name = name.group(2)
2020-10-31 03:28:18 +01:00
_script_lib[vaddr] = name
2020-10-19 23:58:57 +02:00
except:
pass
2020-10-31 03:28:18 +01:00
"""
# symbol_addrs.txt
with open(Path(path.dirname(__file__), "symbol_addrs.txt"), "r") as file:
for line in file.readlines():
line = line.split(";")[0]
s = [s.strip() for s in line.split("=", 1)]
2020-10-31 03:28:18 +01:00
name = s[0]
addr = s[1]
_script_lib[int(addr, 16)] = name
return _script_lib
class ScriptDisassembler:
def __init__(self, bytes, script_name = "script", symbol_map = {}):
self.bytes = bytes
self.script_name = script_name
self.symbol_map = symbol_map
self.out = ""
self.prefix = ""
self.indent = 1
self.indent_used = False
self.done = False
def disassemble(self):
while True:
opcode = self.read_word()
argc = self.read_word()
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
if opcode > 0xFF or argc > 0xFF:
raise Exception(f"script '{script_name}' is malformed")
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
argv = []
for i in range(0, argc):
argv.append(self.read_word())
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
self.disassemble_command(opcode, argc, argv)
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
if self.done:
return self.prefix + self.out
2020-10-19 23:58:57 +02:00
2020-11-08 15:57:58 +01:00
def write(self, line):
2020-10-31 03:28:18 +01:00
if self.indent < 0: self.indent = 0
if self.indent > 1: self.indent_used = True
self.out += " " * self.indent
self.out += line
2020-11-08 15:57:58 +01:00
def write_line(self, line):
self.write(line)
2020-10-31 03:28:18 +01:00
self.out += "\n"
def prefix_line(self, line):
self.prefix += line
self.prefix += "\n"
def var(self, arg):
if arg in self.symbol_map:
return self.symbol_map[arg]
2020-10-21 04:10:13 +02:00
2020-10-19 23:58:57 +02:00
v = arg - 2**32 # convert to s32
if v > -250000000:
2020-10-31 03:28:18 +01:00
if v <= -220000000: return f"SI_FIXED({(v + 230000000) / 1024})"
2020-10-19 23:58:57 +02:00
elif v <= -200000000: return f"SI_ARRAY_FLAG({v + 210000000})"
elif v <= -180000000: return f"SI_ARRAY({v + 190000000})"
elif v <= -160000000: return f"SI_SAVE_VAR({v + 170000000})"
elif v <= -140000000: return f"SI_AREA_VAR({v + 150000000})"
elif v <= -120000000: return f"SI_SAVE_FLAG({v + 130000000})"
elif v <= -100000000: return f"SI_AREA_FLAG({v + 110000000})"
elif v <= -80000000: return f"SI_MAP_FLAG({v + 90000000})"
elif v <= -60000000: return f"SI_FLAG({v + 70000000})"
elif v <= -40000000: return f"SI_MAP_VAR({v + 50000000})"
elif v <= -20000000: return f"SI_VAR({v + 30000000})"
if arg == 0xFFFFFFFF:
return "-1"
elif ((arg & 0xFF000000) == 0x80000000) or arg > 10000:
return f"0x{arg:X}"
else:
return f"{arg}"
2020-10-31 03:28:18 +01:00
def addr_ref(self, addr):
if addr in self.symbol_map:
return self.symbol_map[addr]
return script_lib().get(addr, f"0x{addr:08X}")
2020-10-21 04:10:13 +02:00
2020-10-31 03:28:18 +01:00
def trigger(self, trigger):
2020-10-19 23:58:57 +02:00
if trigger == 0x00000080: trigger = "TriggerFlag_FLOOR_TOUCH"
if trigger == 0x00800000: trigger = "TriggerFlag_FLOOR_ABOVE"
if trigger == 0x00000800: trigger = "TriggerFlag_FLOOR_INTERACT"
if trigger == 0x00000200: trigger = "TriggerFlag_FLOOR_JUMP"
if trigger == 0x00000400: trigger = "TriggerFlag_WALL_TOUCH"
if trigger == 0x00000040: trigger = "TriggerFlag_WALL_PUSH"
if trigger == 0x00000100: trigger = "TriggerFlag_WALL_INTERACT"
if trigger == 0x00001000: trigger = "TriggerFlag_WALL_HAMMER"
if trigger == 0x00040000: trigger = "TriggerFlag_CEILING_TOUCH"
if trigger == 0x00010000: trigger = "TriggerFlag_SAVE_FLAG_SET"
if trigger == 0x00020000: trigger = "TriggerFlag_AREA_FLAG_SET"
if trigger == 0x00100000: trigger = "TriggerFlag_BOMB"
return f"0x{trigger:X}" if type(trigger) is int else trigger
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
def read_word(self):
return int.from_bytes(self.bytes.read(4), byteorder="big")
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
def disassemble_command(self, opcode, argc, argv):
2020-10-19 23:58:57 +02:00
if opcode == 0x01:
self.write_line("SI_CMD(ScriptOpcode_END)")
2020-10-31 03:28:18 +01:00
self.indent -= 1
if self.indent_used:
self.prefix_line("// *INDENT-OFF*")
self.prefix_line(f"Script {self.script_name} = {{")
self.write_line("};")
self.write_line("// *INDENT-ON*")
2020-10-19 23:58:57 +02:00
else:
2020-10-31 03:28:18 +01:00
self.prefix_line(f"Script {self.script_name} = {{")
self.write_line("};")
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
self.done = True
elif opcode == 0x02: self.write_line(f"SI_CMD(ScriptOpcode_RETURN),")
elif opcode == 0x03: self.write_line(f"SI_CMD(ScriptOpcode_LABEL, {self.var(argv[0])}),")
elif opcode == 0x04: self.write_line(f"SI_CMD(ScriptOpcode_GOTO, {self.var(argv[0])}),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x05:
self.write_line(f"SI_CMD(ScriptOpcode_LOOP, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x06:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line("SI_CMD(ScriptOpcode_END_LOOP),")
elif opcode == 0x07: self.write_line(f"SI_CMD(ScriptOpcode_BREAK_LOOP),")
elif opcode == 0x08: self.write_line(f"SI_CMD(ScriptOpcode_SLEEP_FRAMES, {self.var(argv[0])}),")
elif opcode == 0x09: self.write_line(f"SI_CMD(ScriptOpcode_SLEEP_SECS, {self.var(argv[0])}),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x0A:
self.write_line(f"SI_CMD(ScriptOpcode_IF_EQ, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x0B:
self.write_line(f"SI_CMD(ScriptOpcode_IF_NE, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x0C:
self.write_line(f"SI_CMD(ScriptOpcode_IF_LT, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x0D:
self.write_line(f"SI_CMD(ScriptOpcode_IF_GT, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x0E:
self.write_line(f"SI_CMD(ScriptOpcode_IF_LE, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x0F:
self.write_line(f"SI_CMD(ScriptOpcode_IF_GE, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x10:
self.write_line(f"SI_CMD(ScriptOpcode_IF_FLAG, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x11:
self.write_line(f"SI_CMD(ScriptOpcode_IF_NOT_FLAG, ({self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
elif opcode == 0x12:
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_ELSE),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x13:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_END_IF),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x14:
self.write_line(f"SI_CMD(ScriptOpcode_MATCH, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 2
2020-10-19 23:58:57 +02:00
elif opcode == 0x15:
self.write_line(f"SI_CMD(ScriptOpcode_MATCH_CONST, 0x{argv[0]:X}),")
2020-10-31 03:28:18 +01:00
self.indent += 2
2020-10-19 23:58:57 +02:00
elif opcode == 0x16:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_EQ, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x17:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_NE, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x18:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_LT, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x19:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_GT, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x1A:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_LE, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x1B:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_GE, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x1C:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_ELSE),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x1D:
2020-10-31 03:28:18 +01:00
self.indent -= 1
2020-11-08 20:20:09 +01:00
self.write_line(f"SI_CMD(ScriptOpcode_CASE_MULTI_OR_EQ, {self.var(argv[0])}),")
self.indent += 1
elif opcode == 0x1E:
self.indent -= 1
2020-11-08 20:20:09 +01:00
self.write_line(f"SI_CMD(ScriptOpcode_CASE_MULTI_AND_EQ, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x1F:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_FLAG, {self.var(argv[0])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x20:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_END_CASE_MULTI),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x21:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line(f"SI_CMD(ScriptOpcode_CASE_RANGE, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-31 03:28:18 +01:00
self.indent += 1
elif opcode == 0x22: self.write_line(f"SI_CMD(ScriptOpcode_BREAK_CASE),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x23:
2020-10-31 03:28:18 +01:00
self.indent -= 2
self.write_line(f"SI_CMD(ScriptOpcode_END_MATCH),")
elif opcode == 0x24: self.write_line(f"SI_CMD(ScriptOpcode_SET, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x25: self.write_line(f"SI_CMD(ScriptOpcode_SET_CONST, {self.var(argv[0])}, 0x{argv[1]:X}),")
elif opcode == 0x26: self.write_line(f"SI_CMD(ScriptOpcode_SET_F, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x27: self.write_line(f"SI_CMD(ScriptOpcode_ADD, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x28: self.write_line(f"SI_CMD(ScriptOpcode_SUB, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x29: self.write_line(f"SI_CMD(ScriptOpcode_MUL, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x2A: self.write_line(f"SI_CMD(ScriptOpcode_DIV, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x2B: self.write_line(f"SI_CMD(ScriptOpcode_MOD, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x2C: self.write_line(f"SI_CMD(ScriptOpcode_ADD_F, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x2D: self.write_line(f"SI_CMD(ScriptOpcode_SUB_F, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x2E: self.write_line(f"SI_CMD(ScriptOpcode_MUL_F, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x2F: self.write_line(f"SI_CMD(ScriptOpcode_DIV_F, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x30: self.write_line(f"SI_CMD(ScriptOpcode_USE_BUFFER, {self.var(argv[0])}),")
elif opcode == 0x31:
args = ["ScriptOpcode_BUFFER_READ_1",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x32:
args = ["ScriptOpcode_BUFFER_READ_2",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x33:
args = ["ScriptOpcode_BUFFER_READ_3",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x34:
args = ["ScriptOpcode_BUFFER_READ_4",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x35:
args = ["ScriptOpcode_BUFFER_PEEK",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x36: self.write_line(f"SI_CMD(ScriptOpcode_USE_BUFFER_f, {self.var(argv[0])}),")
elif opcode == 0x37:
args = ["ScriptOpcode_BUFFER_READ_1_F",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x38:
args = ["ScriptOpcode_BUFFER_READ_2_F",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x39:
args = ["ScriptOpcode_BUFFER_READ_3_F",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x3A:
args = ["ScriptOpcode_BUFFER_READ_4_F",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x3B:
args = ["ScriptOpcode_BUFFER_PEEK_F",*map(self.var, argv)]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x3C: self.write_line(f"SI_CMD(ScriptOpcode_USE_ARRAY, {self.var(argv[0])}),")
elif opcode == 0x3D: self.write_line(f"SI_CMD(ScriptOpcode_NEW_ARRAY, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x3E: self.write_line(f"SI_CMD(ScriptOpcode_USE_FLAGS, {self.var(argv[0])}),")
elif opcode == 0x3F: self.write_line(f"SI_CMD(ScriptOpcode_AND, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x40: self.write_line(f"SI_CMD(ScriptOpcode_OR, {self.var(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x41: self.write_line(f"SI_CMD(ScriptOpcode_AND_CONST, {self.var(argv[0])}, 0x{argv[1]:X})")
elif opcode == 0x42: self.write_line(f"SI_CMD(ScriptOpcode_OR_CONST, {self.var(argv[0])}, 0x{argv[1]:X})")
2020-10-19 23:58:57 +02:00
elif opcode == 0x43:
args = ["ScriptOpcode_CALL", self.addr_ref(argv[0]), *map(self.var, argv[1:])]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x44: self.write_line(f"SI_CMD(ScriptOpcode_SPAWN, {self.addr_ref(argv[0])}),")
elif opcode == 0x45: self.write_line(f"SI_CMD(ScriptOpcode_SPAWN_GET_ID, {self.addr_ref(argv[0])}, {self.var(argv[1])}),")
elif opcode == 0x46: self.write_line(f"SI_CMD(ScriptOpcode_AWAIT_SCRIPT, {self.addr_ref(argv[0])}),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x47:
args = ["ScriptOpcode_BIND_TRIGGER", self.addr_ref(argv[0]), self.trigger(argv[1]), *map(self.var, argv[2:])]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x48: self.write_line(f"SI_CMD(ScriptOpcode_UNBIND),")
elif opcode == 0x49: self.write_line(f"SI_CMD(ScriptOpcode_KILL_SCRIPT, {self.var(argv[0])}),")
elif opcode == 0x4A: self.write_line(f"SI_CMD(ScriptOpcode_JUMP, {self.var(argv[0])}),")
elif opcode == 0x4B: self.write_line(f"SI_CMD(ScriptOpcode_SET_PRIORITY, {self.var(argv[0])}),")
elif opcode == 0x4C: self.write_line(f"SI_CMD(ScriptOpcode_SET_TIMESCALE, {self.var(argv[0])}),")
elif opcode == 0x4D: self.write_line(f"SI_CMD(ScriptOpcode_SET_GROUP, {self.var(argv[0])}),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x4E:
args = ["ScriptOpcode_BIND_TRIGGER", self.addr_ref(argv[0]), self.trigger(argv[1]), *map(self.var, argv[2:])]
self.write_line(f"SI_CMD({', '.join(args)}),")
elif opcode == 0x4F: self.write_line(f"SI_CMD(ScriptOpcode_SUSPEND_GROUP, {self.var(argv[0])}),")
elif opcode == 0x50: self.write_line(f"SI_CMD(ScriptOpcode_RESUME_GROUP, {self.var(argv[0])}),")
elif opcode == 0x51: self.write_line(f"SI_CMD(ScriptOpcode_SUSPEND_OTHERS, {self.var(argv[0])}),")
elif opcode == 0x52: self.write_line(f"SI_CMD(ScriptOpcode_RESUME_OTHERS, {self.var(argv[0])}),")
elif opcode == 0x53: self.write_line(f"SI_CMD(ScriptOpcode_SUSPEND_SCRIPT, {self.var(argv[0])}),")
elif opcode == 0x54: self.write_line(f"SI_CMD(ScriptOpcode_RESUME_SCRIPT, {self.var(argv[0])}),")
elif opcode == 0x55: self.write_line(f"SI_CMD(ScriptOpcode_SCRIPT_EXISTS, {self.var(argv[0])}, {self.var(argv[1])}),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x56:
self.write_line("SI_CMD(ScriptOpcode_SPAWN_THREAD),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x57:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line("SI_CMD(ScriptOpcode_END_SPAWN_THREAD),")
2020-10-19 23:58:57 +02:00
elif opcode == 0x58:
self.write_line("SI_CMD(ScriptOpcode_PARALLEL_THREAD),")
2020-10-31 03:28:18 +01:00
self.indent += 1
2020-10-19 23:58:57 +02:00
elif opcode == 0x59:
2020-10-31 03:28:18 +01:00
self.indent -= 1
self.write_line("SI_CMD(ScriptOpcode_END_PARALLEL_THREAD),")
2020-10-19 23:58:57 +02:00
else:
# unknown opcode
argv_str = ""
for arg in argv:
argv_str += ", "
argv_str += f"0x{arg:X}"
2020-10-31 03:28:18 +01:00
self.write_line(f"SI_CMD(0x{opcode:02X}{argv_str}),")
class UnsupportedScript(Exception):
pass
class ScriptDSLDisassembler(ScriptDisassembler):
2020-11-08 13:16:10 +01:00
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# True: case block
# CASE: single condition
# MULTI: multi-condition(s)
# MATCH: match block
2020-11-08 15:57:58 +01:00
self.case_stack = []
self.was_multi_case = False
@property
def in_case(self):
return self.case_stack[-1] if self.case_stack else False
2020-11-08 13:16:10 +01:00
2020-10-31 03:28:18 +01:00
def var(self, arg):
if arg in self.symbol_map:
return self.symbol_map[arg]
v = arg - 2**32 # convert to s32
if v > -250000000:
if v <= -220000000: return str((v + 230000000) / 1024)
elif v <= -200000000: return f"SI_ARRAY_FLAG({v + 210000000})"
elif v <= -180000000: return f"SI_ARRAY({v + 190000000})"
elif v <= -160000000: return f"SI_SAVE_VAR({v + 170000000})"
elif v <= -140000000: return f"SI_AREA_VAR({v + 150000000})"
elif v <= -120000000: return f"SI_SAVE_FLAG({v + 130000000})"
elif v <= -100000000: return f"SI_AREA_FLAG({v + 110000000})"
elif v <= -80000000: return f"SI_MAP_FLAG({v + 90000000})"
elif v <= -60000000: return f"SI_FLAG({v + 70000000})"
elif v <= -40000000: return f"SI_MAP_VAR({v + 50000000})"
elif v <= -20000000: return f"SI_VAR({v + 30000000})"
if arg == 0xFFFFFFFF:
return "-1"
elif ((arg & 0xFF000000) == 0x80000000) or arg > 10000:
return f"0x{arg:X}"
else:
return f"{arg}"
def is_float(self, var):
2020-10-31 03:28:18 +01:00
try:
float(var)
return True
2020-10-31 03:28:18 +01:00
except Exception:
return False
2020-10-31 03:28:18 +01:00
def disassemble_command(self, opcode, argc, argv):
2020-11-08 15:57:58 +01:00
# write case block braces
if self.in_case == "CASE" or self.in_case == "MULTI":
2020-11-08 15:57:58 +01:00
if opcode == 0x1D: # multi case
pass
elif 0x16 <= opcode <= 0x21: # standard case conditions
# open and close empty case
self.out += " {}\n"
2020-11-08 15:57:58 +01:00
self.case_stack.pop()
assert self.in_case == "MATCH"
self.was_multi_case = False
2020-11-08 15:57:58 +01:00
else:
# open case
self.out += " {\n"
self.case_stack.append(True)
self.indent += 1
elif self.in_case != "MATCH" and 0x16 <= opcode <= 0x21: # new case, not including the first
assert self.case_stack.pop() == True
self.was_multi_case = self.case_stack.pop() == "MULTI"
assert self.in_case == "MATCH"
2020-11-08 15:57:58 +01:00
self.indent -= 1
self.write_line("}")
2020-10-31 03:28:18 +01:00
if opcode == 0x01:
if self.out.endswith("return\n"):
# implicit return; break
self.out = self.out[:-7].rstrip() + "\n"
else:
self.write_line("break")
self.indent -= 1
self.prefix_line(f"Script {self.script_name} = SCRIPT({{")
self.write_line("});")
2020-10-19 23:58:57 +02:00
2020-10-31 03:28:18 +01:00
self.done = True
elif opcode == 0x02: self.write_line(f"return")
2020-11-08 15:57:58 +01:00
elif opcode == 0x03: self.write_line(f"{self.var(argv[0])}:")
elif opcode == 0x04: self.write_line(f"goto {self.var(argv[0])}")
2020-10-31 03:28:18 +01:00
elif opcode == 0x05:
if argv[0] == 0:
self.write_line("loop {")
else:
self.write_line(f"loop {self.var(argv[0])} {{")
self.indent += 1
elif opcode == 0x06:
self.indent -= 1
self.write_line("}")
elif opcode == 0x07: self.write_line(f"break")
elif opcode == 0x08: self.write_line(f"sleep {self.var(argv[0])}")
elif opcode == 0x09: self.write_line(f"sleep {self.var(argv[0])} secs")
elif opcode == 0x0A:
self.write_line(f"if {self.var(argv[0])} == {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x0B:
self.write_line(f"if {self.var(argv[0])} != {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x0C:
self.write_line(f"if {self.var(argv[0])} < {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x0D:
self.write_line(f"if {self.var(argv[0])} > {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x0E:
self.write_line(f"if {self.var(argv[0])} <= {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x0F:
self.write_line(f"if {self.var(argv[0])} >= {self.var(argv[1])} {{")
self.indent += 1
2020-11-08 15:57:58 +01:00
elif opcode == 0x10:
self.write_line(f"if {self.var(argv[0])} ? {self.var(argv[1])} {{")
self.indent += 1
2020-10-31 03:28:18 +01:00
elif opcode == 0x12:
self.indent -= 1
self.write_line("} else {")
self.indent += 1
elif opcode == 0x13:
self.indent -= 1
self.write_line("}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x14:
self.write_line(f"match {self.var(argv[0])} {{")
self.indent += 1
self.case_stack.append("MATCH")
elif opcode == 0x15:
self.write_line(f"matchc {self.var(argv[0])} {{")
self.indent += 1
self.case_stack.append("MATCH")
2020-11-08 13:16:10 +01:00
elif opcode == 0x16:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f"== {self.var(argv[0])}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x17:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f"!= {self.var(argv[0])}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x18:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f"< {self.var(argv[0])}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x19:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f"> {self.var(argv[0])}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x1A:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f"<= {self.var(argv[0])}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x1B:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f">= {self.var(argv[0])}")
2020-11-08 13:16:10 +01:00
elif opcode == 0x1C:
self.case_stack.append("CASE")
2020-11-08 15:57:58 +01:00
self.write(f"else")
elif opcode == 0x1D:
if self.in_case == "CASE" or self.in_case == "MULTI":
2020-11-08 15:57:58 +01:00
self.out += f", {self.var(argv[0])}"
# replace(!) CASE with MULTI
self.case_stack.pop()
self.case_stack.append("MULTI")
2020-11-08 15:57:58 +01:00
else:
self.write(f"{self.var(argv[0])}")
self.case_stack.append("MULTI")
2020-11-08 13:16:10 +01:00
# opcode 0x1E?
elif opcode == 0x1F:
self.case_stack.append("CASE")
self.write_line(f"? {self.var(argv[0])}")
elif opcode == 0x20:
if not self.was_multi_case:
raise UnsupportedScript("unexpected SI_END_MULTI_CASE")
2020-11-08 13:16:10 +01:00
elif opcode == 0x21:
self.indent -= 1
self.write_line(f"{self.var(argv[0])}..{self.var(argv[1])}")
2020-11-08 13:16:10 +01:00
self.indent += 1
elif opcode == 0x22: self.write_line("break")
elif opcode == 0x23:
# close open case if needed
if self.in_case != "MATCH":
self.case_stack.pop() == True
self.case_stack.pop() in ["MULTI", "CASE"]
self.indent -= 1
2020-11-08 15:57:58 +01:00
self.write_line("}")
assert self.case_stack.pop() == "MATCH"
2020-11-08 13:16:10 +01:00
self.indent -= 1
self.write_line("}")
2020-10-31 03:28:18 +01:00
elif opcode == 0x24: self.write_line(f"{self.var(argv[0])} = {self.var(argv[1])}")
elif opcode == 0x25: self.write_line(f"{self.var(argv[0])} =c 0x{argv[1]:X}")
elif opcode == 0x26:
lhs = self.var(argv[1])
if self.is_float(lhs):
self.write_line(f"{self.var(argv[0])} = {lhs}")
else:
self.write_line(f"{self.var(argv[0])} =f {lhs}")
2020-10-31 03:28:18 +01:00
elif opcode == 0x27: self.write_line(f"{self.var(argv[0])} += {self.var(argv[1])}")
elif opcode == 0x28: self.write_line(f"{self.var(argv[0])} -= {self.var(argv[1])}")
elif opcode == 0x29: self.write_line(f"{self.var(argv[0])} *= {self.var(argv[1])}")
elif opcode == 0x2A: self.write_line(f"{self.var(argv[0])} /= {self.var(argv[1])}")
elif opcode == 0x2B: self.write_line(f"{self.var(argv[0])} %= {self.var(argv[1])}")
elif opcode == 0x2C:
lhs = self.var(argv[1])
if self.is_float(lhs):
self.write_line(f"{self.var(argv[0])} += {lhs}")
else:
self.write_line(f"{self.var(argv[0])} +=f {lhs}")
elif opcode == 0x2D:
lhs = self.var(argv[1])
if self.is_float(lhs):
self.write_line(f"{self.var(argv[0])} -= {lhs}")
else:
self.write_line(f"{self.var(argv[0])} -=f {lhs}")
elif opcode == 0x2E:
lhs = self.var(argv[1])
if self.is_float(lhs):
self.write_line(f"{self.var(argv[0])} *= {lhs}")
else:
self.write_line(f"{self.var(argv[0])} *=f {lhs}")
elif opcode == 0x2F:
lhs = self.var(argv[1])
if self.is_float(lhs):
self.write_line(f"{self.var(argv[0])} /= {lhs}")
else:
self.write_line(f"{self.var(argv[0])} /=f {lhs}")
2020-10-31 03:28:18 +01:00
elif opcode == 0x3F: self.write_line(f"{self.var(argv[0])} &= {self.var(argv[1])}")
elif opcode == 0x40: self.write_line(f"{self.var(argv[0])} |= {self.var(argv[1])}")
elif opcode == 0x41: self.write_line(f"{self.var(argv[0])} &=c {argv[1]:X}")
elif opcode == 0x42: self.write_line(f"{self.var(argv[0])} |=c {argv[1]:X}")
2020-10-31 03:28:18 +01:00
elif opcode == 0x43:
argv_str = ", ".join(self.var(arg) for arg in argv[1:])
self.write_line(f"{self.addr_ref(argv[0])}({argv_str})")
elif opcode == 0x44: self.write_line(f"spawn {self.addr_ref(argv[0])}")
elif opcode == 0x45: self.write_line(f"{self.var(argv[1])} = spawn ({self.addr_ref(argv[0])}")
elif opcode == 0x46: self.write_line(f"await {self.addr_ref(argv[0])}")
elif opcode == 0x47:
assert argv[3] == 1
if argv[4] != 0:
self.write_line(f"{self.var(argv[4])} = bind {self.addr_ref(argv[0])} to {self.trigger(argv[1])} {self.var(argv[2])}")
else:
self.write_line(f"bind {self.addr_ref(argv[0])} to {self.trigger(argv[1])} {self.var(argv[2])}")
elif opcode == 0x48: self.write_line(f"unbind")
elif opcode == 0x49: self.write_line(f"kill {self.var(argv[0])}")
elif opcode == 0x4D: self.write_line(f"group {self.var(argv[0])}")
elif opcode == 0x4F: self.write_line(f"suspend group {self.var(argv[0])}")
elif opcode == 0x50: self.write_line(f"resume group {self.var(argv[0])}")
elif opcode == 0x51: self.write_line(f"suspend others {self.var(argv[0])}")
elif opcode == 0x52: self.write_line(f"resume others {self.var(argv[0])}")
elif opcode == 0x53: self.write_line(f"suspend {self.var(argv[0])}")
elif opcode == 0x54: self.write_line(f"resume {self.var(argv[0])}")
elif opcode == 0x56:
self.write_line("spawn {")
self.indent += 1
elif opcode == 0x57:
self.indent -= 1
self.write_line("}")
elif opcode == 0x58:
self.write_line("parallel {")
self.indent += 1
elif opcode == 0x59:
self.indent -= 1
self.write_line("}")
else:
2020-11-08 15:57:58 +01:00
raise UnsupportedScript(f"DSL does not support script opcode 0x{opcode:X}")
2020-10-19 23:58:57 +02:00
if __name__ == "__main__":
if len(sys.argv) <= 1:
print("usage: ./disasm_script.py <file> [offset]")
exit()
file = sys.argv[1]
offset = eval(sys.argv[2]) if len(sys.argv) >= 3 else 0
with open(file, "rb") as f:
f.seek(offset)
2020-10-31 03:28:18 +01:00
try:
print(ScriptDSLDisassembler(f).disassemble(), end="")
except UnsupportedScript:
print(ScriptDisassembler(f).disassemble(), end="")