From 21ab7fed6ca137b576c5e28c974b71b96d8fb31a Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 31 Oct 2020 02:28:18 +0000 Subject: [PATCH] new dsl syntax --- Makefile | 4 +- tools/compile_dsl_macros.py | 97 +++++- tools/disasm_map.py | 17 +- tools/disasm_script.py | 598 ++++++++++++++++++++++++------------ 4 files changed, 513 insertions(+), 203 deletions(-) diff --git a/Makefile b/Makefile index ece296eab5..06a8a51d95 100644 --- a/Makefile +++ b/Makefile @@ -135,7 +135,7 @@ $(BUILD_DIR)/%.c.o: %.c $(MDEPS) $(CPP) $(CPPFLAGS) -o - $< $(CPPMFLAGS) | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) -o $@ - # Compile C files (with DSL macros) -$(foreach cfile, $(DSL_C_FILES), $(BUILD_DIR)/$(cfile).o): $(BUILD_DIR)/%.c.o: %.c $(MDEPS) +$(foreach cfile, $(DSL_C_FILES), $(BUILD_DIR)/$(cfile).o): $(BUILD_DIR)/%.c.o: %.c $(MDEPS) tools/compile_dsl_macros.py @mkdir -p $(shell dirname $@) $(CPP) $(CPPFLAGS) -o - $< $(CPPMFLAGS) | tools/compile_dsl_macros.py | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) -o $@ - @@ -151,7 +151,7 @@ $(BUILD_DIR)/bin/assets/%: bin/assets/%.bin @mkdir -p $(shell dirname $@) @cp $< $@ -$(ASSETS_BIN): $(ASSET_FILES) $(YAY0_ASSET_FILES) +$(ASSETS_BIN): $(ASSET_FILES) $(YAY0_ASSET_FILES) sources.mk @mkdir -p $(shell dirname $@) @echo "building $@" @$(PYTHON) tools/build_assets_bin.py $@ $(ASSET_FILES) diff --git a/tools/compile_dsl_macros.py b/tools/compile_dsl_macros.py index d62cab7d29..fa759bd752 100755 --- a/tools/compile_dsl_macros.py +++ b/tools/compile_dsl_macros.py @@ -29,17 +29,18 @@ script_parser = Lark(r""" block: "{" NEWLINE* (stmt STMT_SEP)* NEWLINE* "}" ?stmt: call - | label ":" -> label_decl + | label ":" [stmt] -> label_decl | "goto" label -> label_goto | if_stmt | "return" -> return_stmt | "break" -> break_stmt | "sleep" expr -> sleep_stmt + | "sleep" expr "secs" -> sleep_secs_stmt | "spawn" expr -> spawn_stmt | "await" expr -> await_stmt | lhs "=" "spawn" expr -> spawn_set_stmt | lhs set_op expr -> set_stmt - | lhs ":=" expr -> set_const_stmt + | "const" lhs set_op expr -> set_const_stmt | bind_stmt | bind_set_stmt | "unbind" -> unbind_stmt @@ -49,12 +50,19 @@ script_parser = Lark(r""" | kill_stmt | loop_stmt | loop_until_stmt + | ["await"] block -> block_stmt + | "spawn" block -> spawn_block_stmt + | "parallel" block -> parallel_block_stmt call: CNAME "(" [expr ("," expr)* [","]] ")" if_stmt: "if" expr if_op expr block ["else" block] ?if_op: "==" -> if_op_eq | "!=" -> if_op_ne + | ">" -> if_op_gt + | "<" -> if_op_lt + | ">=" -> if_op_ge + | "<=" -> if_op_le suspend_stmt: "suspend" control_type expr ("," control_type expr)* [","] resume_stmt: "resume" control_type expr ("," control_type expr)* [","] @@ -84,6 +92,11 @@ script_parser = Lark(r""" | "*=" -> set_op_mul | "/=" -> set_op_div | "%=" -> set_op_mod + | "&=" -> set_op_and + | "|=" -> set_op_or + | ":=" -> set_op_eq_const + | ":&=" -> set_op_and_const + | ":|=" -> set_op_or_const c_const_expr: c_const_expr_internal c_const_expr_internal: "(" (c_const_expr_internal | NOT_PARENS)+ ")" @@ -175,6 +188,22 @@ class LoopUntilCtx(CmdCtx): def break_opcode(self, meta): raise CompileError("breaking out of a loop..until is not supported (hint: use a label)", meta) +class LabelCtx(CmdCtx): + def __init__(self, label): + super().__init__() + self.label = label + + # TODO: implement break_opcode so you can do lbl: loop { break lbl } + +class BlockCtx(CmdCtx): + pass + +class SpawnCtx(CmdCtx): + pass + +class ParallelCtx(CmdCtx): + pass + class CompileError(Exception): def __init__(self, message, meta): super().__init__(message) @@ -255,6 +284,10 @@ class Compile(Transformer): return [ Cmd(op, a, b, meta=tree.meta), *block, Cmd(0x13) ] def if_op_eq(self, tree): return 0x0A def if_op_ne(self, tree): return 0x0B + def if_op_lt(self, tree): return 0x0C + def if_op_gt(self, tree): return 0x0D + def if_op_le(self, tree): return 0x0E + def if_op_ge(self, tree): return 0x0F def loop_stmt(self, tree): expr = tree.children.pop(0) if len(tree.children) > 1 else 0 @@ -336,6 +369,8 @@ class Compile(Transformer): def sleep_stmt(self, tree): return Cmd(0x08, tree.children[0], meta=tree.meta) + def sleep_secs_stmt(self, tree): + return Cmd(0x09, tree.children[0], meta=tree.meta) def bind_stmt(self, tree): script, trigger, target = tree.children @@ -366,12 +401,16 @@ class Compile(Transformer): raise CompileError(f"operation `{opcodes['__op__']}' not supported for ints", tree.meta) return Cmd(opcode, lhs, rhs) def set_const_stmt(self, tree): - lhs, rhs = tree.children - return Cmd(0x25, lhs, rhs) + lhs, opcodes, rhs = tree.children + opcode = opcodes.get("const", None) + if not opcode: + raise CompileError(f"operation `{opcodes['__op__']}' not supported for consts", tree.meta) + return Cmd(opcode, lhs, rhs) def set_op_eq(self, tree): return { "__op__": "=", "int": 0x24, + "const": 0x25, "float": 0x26, } def set_op_add(self, tree): @@ -403,10 +442,37 @@ class Compile(Transformer): "__op__": "%", "int": 0x2B, } + def set_op_and(self, tree): + return { + "__op__": "&", + "int": 0x3F, + "const": 0x41, + } + def set_op_or(self, tree): + return { + "__op__": "|", + "int": 0x40, + "const": 0x42, + } def label_decl(self, tree): - label = tree.children[0] - return Cmd(0x03, label, meta=tree.meta) + if len(tree.children) == 0: + label = tree.children[0] + return Cmd(0x03, label, meta=tree.meta) + else: + label, cmd_or_block = tree.children + + if type(cmd_or_block) is not list: + cmd_or_block = [cmd_or_block] + + for cmd in cmd_or_block: + if isinstance(cmd, BaseCmd): + cmd.add_context(LabelCtx(label)) + + return [ + Cmd(0x03, label, meta=tree.meta), + *cmd_or_block + ] def label_goto(self, tree): label = tree.children[0] return Cmd(0x04, label, meta=tree.meta) @@ -416,6 +482,25 @@ class Compile(Transformer): return self.alloc.labels.index(name) raise CompileError(f"label `{name}' is undeclared", tree.meta) + def block_stmt(self, tree): + block, = tree.children + for cmd in block: + if isinstance(cmd, BaseCmd): + cmd.add_context(BlockCtx()) + return block + def spawn_block_stmt(self, tree): + block, = tree.children + for cmd in block: + if isinstance(cmd, BaseCmd): + cmd.add_context(SpawnCtx()) + return [ Cmd(0x56, meta=tree.meta), *block, Cmd(0x57) ] + def parallel_block_stmt(self, tree): + block, = tree.children + for cmd in block: + if isinstance(cmd, BaseCmd): + cmd.add_context(ParallelCtx()) + return [ Cmd(0x58, meta=tree.meta), *block, Cmd(0x59) ] + def compile_script(s): tree = script_parser.parse(s) diff --git a/tools/disasm_map.py b/tools/disasm_map.py index 6e68c556b4..3c3bbb993a 100755 --- a/tools/disasm_map.py +++ b/tools/disasm_map.py @@ -6,7 +6,7 @@ import yaml import json from struct import unpack -from disasm_script import disassemble as disassemble_script +import disasm_script def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"): out = "" @@ -15,6 +15,9 @@ def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"): while len(midx) > 0: struct = midx.pop(0) name = struct["name"] + + print(name) + if name == "Script_Main": name = f"M(Main)" #print(f"{offset:X} ({name}, start = {struct['start']:X}, len = {struct['length']:X})") @@ -28,7 +31,12 @@ def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"): # format struct if struct["type"].startswith("Script"): - out += disassemble_script(bytes, f"M({name})", symbol_map) + pos = bytes.tell() + try: + out += disasm_script.ScriptDSLDisassembler(bytes, f"M({name})", symbol_map).disassemble() + except disasm_script.UnsupportedScript as e: + bytes.seek(pos) + out += disasm_script.ScriptDisassembler(bytes, f"M({name})", symbol_map).disassemble() elif struct["type"] == "Padding": # nops at end of file bytes.seek(offset % 4, 1) @@ -56,7 +64,7 @@ def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"): out += f" .background = &gBackgroundImage,\n" elif bg != 0: raise Exception(f"unknown MapConfig background {bg:X}") - out += f" .tattle = {tattle:X},\n" + out += f" .tattle = 0x{tattle:X},\n" out += f"}};\n" elif struct["type"] == "ASCII": @@ -218,4 +226,5 @@ if __name__ == "__main__": if len(disasm.strip()) > 0: with open(f"{src_dir}/{rom_addr:X}.bin.c", "w") as f: - f.write(disasm) + f.write(f'#include "{map_name}.h"\n\n') + f.write(disasm.rstrip() + "\n") diff --git a/tools/disasm_script.py b/tools/disasm_script.py index 33c834df2a..0c5ffc9ac4 100755 --- a/tools/disasm_script.py +++ b/tools/disasm_script.py @@ -2,17 +2,19 @@ import sys -_star_rod_lib = None -def star_rod_lib(): - global _star_rod_lib +_script_lib = None +def script_lib(): + global _script_lib - if not _star_rod_lib: - _star_rod_lib = {} + if not _script_lib: + _script_lib = {} from pathlib import Path from os import path import re + # star rod database + """ LIB_LINE_RE = re.compile(r"\s+:\s+") NAME_RE = re.compile(r"({[^}]*})?\s*([a-zA-Z0-9_]+)") @@ -28,46 +30,71 @@ def star_rod_lib(): if name := NAME_RE.match(parts[2]): name = name.group(2) - _star_rod_lib[vaddr] = name - - """ - if "map" in str(filename): - if kind == "api": - print(f"ApiStatus {name}(ScriptInstance* script, s32 isInitialCall);") - elif kind == "scr": - print(f"extern Script {name};") - """ + _script_lib[vaddr] = name except: pass + """ - return _star_rod_lib + # symbol_addrs.txt + with open(Path(path.dirname(__file__), "symbol_addrs.txt"), "r") as file: + for line in file.readlines(): + s = line.split(";") + name = s[0] + addr = s[1] + _script_lib[int(addr, 16)] = name -def disassemble(bytes, script_name = "script", symbol_map = {}): - out = "" - prefix = "" + return _script_lib - indent = 1 - indent_used = False +class ScriptDisassembler: + def __init__(self, bytes, script_name = "script", symbol_map = {}): + self.bytes = bytes + self.script_name = script_name + self.symbol_map = symbol_map - def write_line(line): - nonlocal out, indent, indent_used - if indent < 0: indent = 0 - if indent > 1: indent_used = True - out += " " * indent - out += line - out += "\n" - def prefix_line(line): - nonlocal prefix - prefix += line - prefix += "\n" + self.out = "" + self.prefix = "" - def var(arg): - if arg in symbol_map: - return symbol_map[arg] + self.indent = 1 + self.indent_used = False + + self.done = False + + def disassemble(self): + while True: + opcode = self.read_word() + argc = self.read_word() + + if opcode > 0xFF or argc > 0xFF: + raise Exception(f"script '{script_name}' is malformed") + + argv = [] + for i in range(0, argc): + argv.append(self.read_word()) + + self.disassemble_command(opcode, argc, argv) + + if self.done: + return self.prefix + self.out + + def write_line(self, line): + if self.indent < 0: self.indent = 0 + if self.indent > 1: self.indent_used = True + + self.out += " " * self.indent + self.out += line + 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] v = arg - 2**32 # convert to s32 if v > -250000000: - if v <= -220000000: return f"SI_FIXED({(v + 230000000) / 1024}f)" + if v <= -220000000: return f"SI_FIXED({(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})" @@ -86,12 +113,12 @@ def disassemble(bytes, script_name = "script", symbol_map = {}): else: return f"{arg}" - def addr_ref(addr): - if addr in symbol_map: - return symbol_map[addr] - return star_rod_lib().get(addr, f"0x{addr:08X}") + 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}") - def trigger(trigger): + def trigger(self, trigger): if trigger == 0x00000080: trigger = "TriggerFlag_FLOOR_TOUCH" if trigger == 0x00800000: trigger = "TriggerFlag_FLOOR_ABOVE" if trigger == 0x00000800: trigger = "TriggerFlag_FLOOR_INTERACT" @@ -106,206 +133,391 @@ def disassemble(bytes, script_name = "script", symbol_map = {}): if trigger == 0x00100000: trigger = "TriggerFlag_BOMB" return trigger - def read_word(): - return int.from_bytes(bytes.read(4), byteorder="big") - - while True: - opcode = read_word() - argc = read_word() - - if opcode > 0xFF or argc > 0xFF: - return f"/* malformed script: {script_name} */\n" - - argv = [] - for i in range(0, argc): - argv.append(read_word()) + def read_word(self): + return int.from_bytes(self.bytes.read(4), byteorder="big") + def disassemble_command(self, opcode, argc, argv): if opcode == 0x01: - write_line("SI_END(),") - indent -= 1 + self.write_line("SI_END(),") + self.indent -= 1 - if indent_used: - prefix_line("// *INDENT-OFF*") - prefix_line(f"Script {script_name} = {{") - write_line("};") - write_line("// *INDENT-ON*") + 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*") else: - prefix_line(f"Script {script_name} = {{") - write_line("};") + self.prefix_line(f"Script {self.script_name} = {{") + self.write_line("};") - return prefix + out - elif opcode == 0x02: write_line(f"SI_RETURN(),") - elif opcode == 0x03: write_line(f"SI_LABEL({var(argv[0])}),") - elif opcode == 0x04: write_line(f"SI_GOTO({var(argv[0])}),") + self.done = True + elif opcode == 0x02: self.write_line(f"SI_RETURN(),") + elif opcode == 0x03: self.write_line(f"SI_LABEL({self.var(argv[0])}),") + elif opcode == 0x04: self.write_line(f"SI_GOTO({self.var(argv[0])}),") elif opcode == 0x05: - write_line(f"SI_LOOP({var(argv[0])}),") - indent += 1 + self.write_line(f"SI_LOOP({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x06: - indent -= 1 - write_line("SI_END_LOOP(),") - elif opcode == 0x07: write_line(f"SI_BREAK_LOOP(),") - elif opcode == 0x08: write_line(f"SI_WAIT_FRAMES({var(argv[0])}),") - elif opcode == 0x09: write_line(f"SI_WAIT_SECS({var(argv[0])}),") + self.indent -= 1 + self.write_line("SI_END_LOOP(),") + elif opcode == 0x07: self.write_line(f"SI_BREAK_LOOP(),") + elif opcode == 0x08: self.write_line(f"SI_WAIT_FRAMES({self.var(argv[0])}),") + elif opcode == 0x09: self.write_line(f"SI_WAIT_SECS({self.var(argv[0])}),") elif opcode == 0x0A: - write_line(f"SI_IF_EQ({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_EQ({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x0B: - write_line(f"SI_IF_NE({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_NE({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x0C: - write_line(f"SI_IF_LT({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_LT({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x0D: - write_line(f"SI_IF_GT({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_GT({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x0E: - write_line(f"SI_IF_LE({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_LE({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x0F: - write_line(f"SI_IF_GE({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_GE({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x10: - write_line(f"SI_IF_BITS_ON({var(argv[0])}, {var(argv[1])}),") - indent += 1 + self.write_line(f"SI_IF_BITS_ON({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 elif opcode == 0x11: - write_line(f"SI_IF_BITS_OFF({var(argv[0])}, {var(argv[1])}),") - indent += 1 - elif opcode == 0x12: write_line(f"SI_ELSE(),") + self.write_line(f"SI_IF_BITS_OFF({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 + elif opcode == 0x12: + self.indent -= 1 + self.write_line(f"SI_ELSE(),") + self.indent += 1 elif opcode == 0x13: - indent -= 1 - write_line(f"SI_END_IF(),") + self.indent -= 1 + self.write_line(f"SI_END_IF(),") elif opcode == 0x14: - write_line(f"SI_SWITCH({var(argv[0])}),") - indent += 2 + self.write_line(f"SI_SWITCH({self.var(argv[0])}),") + self.indent += 2 elif opcode == 0x15: - write_line(f"SI_SWITCH_CONST(0x{argv[0]:X}),") - indent += 2 + self.write_line(f"SI_SWITCH_CONST(0x{argv[0]:X}),") + self.indent += 2 elif opcode == 0x16: - indent -= 1 - write_line(f"SI_CASE_EQ({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_EQ({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x17: - indent -= 1 - write_line(f"SI_CASE_NE({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_NE({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x18: - indent -= 1 - write_line(f"SI_CASE_LT({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_LT({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x19: - indent -= 1 - write_line(f"SI_CASE_GT({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_GT({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x1A: - indent -= 1 - write_line(f"SI_CASE_LE({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_LE({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x1B: - indent -= 1 - write_line(f"SI_CASE_GE({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_GE({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x1C: - indent -= 1 - write_line(f"SI_CASE_DEFAULT(),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_DEFAULT(),") + self.indent += 1 elif opcode == 0x1D: - indent -= 1 - write_line(f"SI_CASE_OR_EQ({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_OR_EQ({self.var(argv[0])}),") + self.indent += 1 # opcode 0x1E? elif opcode == 0x1F: - indent -= 1 - write_line(f"SI_CASE_BITS_ON({var(argv[0])}),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_CASE_BITS_ON({self.var(argv[0])}),") + self.indent += 1 elif opcode == 0x20: - indent -= 1 - write_line(f"SI_END_MULTI_CASE(),") - indent += 1 + self.indent -= 1 + self.write_line(f"SI_END_MULTI_CASE(),") + self.indent += 1 elif opcode == 0x21: - indent -= 1 - write_line(f"SI_CASE_RANGE({var(argv[0])}, {var(argv[1])}),") - indent += 1 - elif opcode == 0x22: write_line(f"SI_BREAK_CASE(),") + self.indent -= 1 + self.write_line(f"SI_CASE_RANGE({self.var(argv[0])}, {self.var(argv[1])}),") + self.indent += 1 + elif opcode == 0x22: self.write_line(f"SI_BREAK_CASE(),") elif opcode == 0x23: - indent -= 2 - write_line(f"SI_END_SWITCH(),") - elif opcode == 0x24: write_line(f"SI_SET({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x25: write_line(f"SI_SET_CONST({var(argv[0])}, 0x{argv[1]:X}),") - elif opcode == 0x26: write_line(f"SI_SET_F({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x27: write_line(f"SI_ADD({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x28: write_line(f"SI_SUB({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x29: write_line(f"SI_MUL({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x2A: write_line(f"SI_DIV({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x2B: write_line(f"SI_MOD({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x2C: write_line(f"SI_ADD_F({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x2D: write_line(f"SI_SUB_F({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x2E: write_line(f"SI_MUL_F({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x2F: write_line(f"SI_DIV_F({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x30: write_line(f"SI_USE_BUFFER({var(argv[0])}),") + self.indent -= 2 + self.write_line(f"SI_END_SWITCH(),") + elif opcode == 0x24: self.write_line(f"SI_SET({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x25: self.write_line(f"SI_SET_CONST({self.var(argv[0])}, 0x{argv[1]:X}),") + elif opcode == 0x26: self.write_line(f"SI_SET_F({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x27: self.write_line(f"SI_ADD({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x28: self.write_line(f"SI_SUB({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x29: self.write_line(f"SI_MUL({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x2A: self.write_line(f"SI_DIV({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x2B: self.write_line(f"SI_MOD({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x2C: self.write_line(f"SI_ADD_F({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x2D: self.write_line(f"SI_SUB_F({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x2E: self.write_line(f"SI_MUL_F({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x2F: self.write_line(f"SI_DIV_F({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x30: self.write_line(f"SI_USE_BUFFER({self.var(argv[0])}),") # TODO: SI_BUF commands - elif opcode == 0x3C: write_line(f"SI_USE_ARRAY({var(argv[0])}),") - elif opcode == 0x3D: write_line(f"SI_NEW_ARRAY({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x3E: write_line(f"SI_USE_FLAGS({var(argv[0])}),") - elif opcode == 0x3F: write_line(f"SI_AND({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x40: write_line(f"SI_OR({var(argv[0])}, {var(argv[1])}),") - elif opcode == 0x41: write_line(f"SI_AND_CONST({var(argv[0])}, 0x{argv[1]:X})") - elif opcode == 0x42: write_line(f"SI_OR_CONST({var(argv[0])}, 0x{argv[1]:X})") + elif opcode == 0x3C: self.write_line(f"SI_USE_ARRAY({self.var(argv[0])}),") + elif opcode == 0x3D: self.write_line(f"SI_NEW_ARRAY({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x3E: self.write_line(f"SI_USE_FLAGS({self.var(argv[0])}),") + elif opcode == 0x3F: self.write_line(f"SI_AND({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x40: self.write_line(f"SI_OR({self.var(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x41: self.write_line(f"SI_AND_CONST({self.var(argv[0])}, 0x{argv[1]:X})") + elif opcode == 0x42: self.write_line(f"SI_OR_CONST({self.var(argv[0])}, 0x{argv[1]:X})") elif opcode == 0x43: argv_str = "" for arg in argv[1:]: argv_str += ", " - argv_str += var(arg) + argv_str += self.var(arg) - write_line(f"SI_CALL({addr_ref(argv[0])}{argv_str}),") - elif opcode == 0x44: write_line(f"SI_EXEC({addr_ref(argv[0])}),") - elif opcode == 0x45: write_line(f"SI_EXEC_GET_ID({addr_ref(argv[0])}, {var(argv[1])}),") - elif opcode == 0x46: write_line(f"SI_EXEC_WAIT({addr_ref(argv[0])}),") + self.write_line(f"SI_CALL({self.addr_ref(argv[0])}{argv_str}),") + elif opcode == 0x44: self.write_line(f"SI_EXEC({self.addr_ref(argv[0])}),") + elif opcode == 0x45: self.write_line(f"SI_EXEC_GET_ID({self.addr_ref(argv[0])}, {self.var(argv[1])}),") + elif opcode == 0x46: self.write_line(f"SI_EXEC_WAIT({self.addr_ref(argv[0])}),") elif opcode == 0x47: - if argv[3] != 1: - raise "BIND argv[3] != 1" - - write_line(f"SI_BIND({addr_ref(argv[0])}, {trigger(argv[1])}, {var(argv[2])}, {'NULL' if argv[4] == 0 else var(argv[4])}),") - elif opcode == 0x48: write_line(f"SI_UNBIND_ME(),") - elif opcode == 0x49: write_line(f"SI_KILL({var(argv[0])}),") - elif opcode == 0x4A: write_line(f"SI_JUMP({var(argv[0])}),") - elif opcode == 0x4B: write_line(f"SI_PRIORITY({var(argv[0])}),") - elif opcode == 0x4C: write_line(f"SI_TIMESCALE({var(argv[0])}),") - elif opcode == 0x4D: write_line(f"SI_GROUP({var(argv[0])}),") + assert argv[3] == 1 + self.write_line(f"SI_BIND({self.addr_ref(argv[0])}, {self.trigger(argv[1])}, {self.var(argv[2])}, {'NULL' if argv[4] == 0 else self.var(argv[4])}),") + elif opcode == 0x48: self.write_line(f"SI_UNBIND_ME(),") + elif opcode == 0x49: self.write_line(f"SI_KILL({self.var(argv[0])}),") + elif opcode == 0x4A: self.write_line(f"SI_JUMP({self.var(argv[0])}),") + elif opcode == 0x4B: self.write_line(f"SI_PRIORITY({self.var(argv[0])}),") + elif opcode == 0x4C: self.write_line(f"SI_TIMESCALE({self.var(argv[0])}),") + elif opcode == 0x4D: self.write_line(f"SI_GROUP({self.var(argv[0])}),") elif opcode == 0x4E: - if argv[4] != 0: - raise "BIND_PADLOCK argv[4] != NULL" - if argv[5] != 1: - raise "BIND_PADLOCK argv[5] != 1" - - write_line(f"SI_BIND_PADLOCK({addr_ref(argv[0])}, {trigger(argv[1])}, {var(argv[2])}, {var(argv[3])}),") - elif opcode == 0x4F: write_line(f"SI_SUSPEND_GROUP({var(argv[0])}),") - elif opcode == 0x50: write_line(f"SI_RESUME_GROUP({var(argv[0])}),") - elif opcode == 0x51: write_line(f"SI_SUSPEND_GROUP_NOT_ME({var(argv[0])}),") - elif opcode == 0x52: write_line(f"SI_RESUME_GROUP_NOT_ME({var(argv[0])}),") - elif opcode == 0x53: write_line(f"SI_SUSPEND({var(argv[0])}),") - elif opcode == 0x54: write_line(f"SI_RESUME({var(argv[0])}),") - elif opcode == 0x55: write_line(f"SI_EXISTS({var(argv[0])}),") + assert argv[4] == 0 + assert argv[5] == 1 + self.write_line(f"SI_BIND_PADLOCK({self.addr_ref(argv[0])}, {self.trigger(argv[1])}, {self.var(argv[2])}, {self.var(argv[3])}),") + elif opcode == 0x4F: self.write_line(f"SI_SUSPEND_GROUP({self.var(argv[0])}),") + elif opcode == 0x50: self.write_line(f"SI_RESUME_GROUP({self.var(argv[0])}),") + elif opcode == 0x51: self.write_line(f"SI_SUSPEND_GROUP_NOT_ME({self.var(argv[0])}),") + elif opcode == 0x52: self.write_line(f"SI_RESUME_GROUP_NOT_ME({self.var(argv[0])}),") + elif opcode == 0x53: self.write_line(f"SI_SUSPEND({self.var(argv[0])}),") + elif opcode == 0x54: self.write_line(f"SI_RESUME({self.var(argv[0])}),") + elif opcode == 0x55: self.write_line(f"SI_EXISTS({self.var(argv[0])}),") elif opcode == 0x56: - write_line("SI_THREAD(),") - indent += 1 + self.write_line("SI_THREAD(),") + self.indent += 1 elif opcode == 0x57: - indent -= 1 - write_line("SI_END_THREAD(),") + self.indent -= 1 + self.write_line("SI_END_THREAD(),") elif opcode == 0x58: - write_line("SI_CHILD_THREAD(),") - indent += 1 + self.write_line("SI_CHILD_THREAD(),") + self.indent += 1 elif opcode == 0x59: - indent -= 1 - write_line("SI_END_CHILD_THREAD(),") + self.indent -= 1 + self.write_line("SI_END_CHILD_THREAD(),") else: # unknown opcode argv_str = "" for arg in argv: argv_str += ", " argv_str += f"0x{arg:X}" - write_line(f"SI_CMD(0x{opcode:02X}{argv_str}),") + self.write_line(f"SI_CMD(0x{opcode:02X}{argv_str}),") - raise "Reached end of data before END command" +class UnsupportedScript(Exception): + pass + +class ScriptDSLDisassembler(ScriptDisassembler): + 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 verify_float(self, var): + try: + float(var) + except Exception: + # not a float! + raise UnsupportedScript("non-float used in float command") + + return var + + def disassemble_command(self, opcode, argc, argv): + 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("});") + + self.done = True + elif opcode == 0x02: self.write_line(f"return") + elif opcode == 0x03: self.write_line(f"lbl{self.var(argv[0])}:") + elif opcode == 0x04: self.write_line(f"goto lbl{self.var(argv[0])}") + 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 + elif opcode == 0x12: + self.indent -= 1 + self.write_line("} else {") + self.indent += 1 + elif opcode == 0x13: + self.indent -= 1 + self.write_line("}") + # elif opcode == 0x14: + # self.write_line(f"SI_SWITCH({self.var(argv[0])}),") + # self.indent += 2 + # elif opcode == 0x15: + # self.write_line(f"SI_SWITCH_CONST(0x{argv[0]:X}),") + # self.indent += 2 + # elif opcode == 0x16: + # self.indent -= 1 + # self.write_line(f"SI_CASE_EQ({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x17: + # self.indent -= 1 + # self.write_line(f"SI_CASE_NE({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x18: + # self.indent -= 1 + # self.write_line(f"SI_CASE_LT({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x19: + # self.indent -= 1 + # self.write_line(f"SI_CASE_GT({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x1A: + # self.indent -= 1 + # self.write_line(f"SI_CASE_LE({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x1B: + # self.indent -= 1 + # self.write_line(f"SI_CASE_GE({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x1C: + # self.indent -= 1 + # self.write_line(f"SI_CASE_DEFAULT(),") + # self.indent += 1 + # elif opcode == 0x1D: + # self.indent -= 1 + # self.write_line(f"SI_CASE_OR_EQ({self.var(argv[0])}),") + # self.indent += 1 + # # opcode 0x1E? + # elif opcode == 0x1F: + # self.indent -= 1 + # self.write_line(f"SI_CASE_BITS_ON({self.var(argv[0])}),") + # self.indent += 1 + # elif opcode == 0x20: + # self.indent -= 1 + # self.write_line(f"SI_END_MULTI_CASE(),") + # self.indent += 1 + # elif opcode == 0x21: + # self.indent -= 1 + # self.write_line(f"case {self.var(argv[0])}..{self.var(argv[1])}:") + # self.indent += 1 + # elif opcode == 0x22: self.write_line("break") + # elif opcode == 0x23: + # self.indent -= 2 + # self.write_line("}") + elif opcode == 0x24: self.write_line(f"{self.var(argv[0])} = {self.var(argv[1])}") + elif opcode == 0x25: self.write_line(f"const {self.var(argv[0])} = 0x{argv[1]:X}") + elif opcode == 0x26: self.write_line(f"{self.var(argv[0])} = {self.verify_float(self.var(argv[1]))}") + 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: self.write_line(f"{self.var(argv[0])} += {self.verify_float(self.var(argv[1]))}") + elif opcode == 0x2D: self.write_line(f"{self.var(argv[0])} -= {self.verify_float(self.var(argv[1]))}") + elif opcode == 0x2E: self.write_line(f"{self.var(argv[0])} *= {self.verify_float(self.var(argv[1]))}") + elif opcode == 0x2F: self.write_line(f"{self.var(argv[0])} /= {self.verify_float(self.var(argv[1]))}") + 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"const {self.var(argv[0])} &= {argv[1]:X})") + elif opcode == 0x42: self.write_line(f"const {self.var(argv[0])} |= {argv[1]:X})") + 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: + raise UnsupportedScript(f"DSL does not support script opcode {opcode:X}") if __name__ == "__main__": if len(sys.argv) <= 1: @@ -317,4 +529,8 @@ if __name__ == "__main__": with open(file, "rb") as f: f.seek(offset) - print(disassemble(f), end="") + + try: + print(ScriptDSLDisassembler(f).disassemble(), end="") + except UnsupportedScript: + print(ScriptDisassembler(f).disassemble(), end="")