shithub: pokecrystal

ref: df21dbccb740b05a649f00f5e0f8c1a63eb75f8e
dir: /extras/disassemble_map_scripts.py/

View raw version
# -*- encoding: utf-8 -*-
"""
Dump out asm for scripting things in bank $25. This script will modify main.asm
and insert all scripting commands.
"""

import crystal
from gbz80disasm import output_bank_opcodes

rom = crystal.load_rom()
roml = [ord(x) for x in rom]

script_command_table_address = 0x96cb1
script_command_count = 170

# a list of addresses for each script command
command_pointers = [crystal.calculate_pointer_from_bytes_at(script_command_table_address + (id * 2), bank=0x25) for id in range(0, 170)]

# a list of hex addresses for each script command in bank $25
command_pointers_hex = ["$%.2x" % (x % 0x4000 + 0x4000) for x in command_pointers]

commands = {}

# force data into a more usable form
for command in crystal.command_classes:
    name = "Script_" + command.macro_name
    id = command.id
    params = {}

    for (id2, param_type) in command.param_types.items():
        param = {
            "name": param_type["name"],
            "type": param_type["class"].__name__,
        }
        params[id2] = param

    if id <= 0xa9:
        commands[id] = {
            "name": name,
            "params": params,
            "address": command_pointers[id],
        }

avoid = [
    0x974b0,
    0x974be,
    0x9754b,
    0x97556,
    0x97562,
    0x9756e,
    0x97540,

    0x96f8e, # verbosegiveitem2
]

class DisassembledScriptCommand():
    """
    Just a temporary object to store information about a script command's asm.
    This is used by some of the infrastructure in crystal.py to automatically
    insert asm into main.asm, rather than having someone do it manually.
    """
    dependencies = None

    def __init__(self, label=None, id=None, address=None, params=None):
        self.id = id
        self.label = crystal.Label(name=label, address=address, object=self)
        self.address = address
        self.params = params

        max_byte_count = 0x4000

        # Some of these scripts need to be truncated before insertion, because
        # output_bank_opcodes doesn't know anything about stopping if some of
        # the local labels are not resolved yet.

        # Script_if_equal
        if address == 0x97540:
            max_byte_count = 86

        # disassemble and laso get the last address
        (asm, last_address, last_hl_address, last_a_address, used_3d97) = output_bank_opcodes(address, max_byte_count=max_byte_count, stop_at=command_pointers, include_last_address=False)

        # remove indentation
        asm = asm.replace("\n\t", "\n")
        if asm[0] == "\t":
            asm = asm[1:]

        # remove the last two newlines
        while asm[-1] == "\n":
            asm = asm[:-1]

        self.asm = asm
        self.last_address = last_address

        # make sure this gets dumped into main.asm
        #if crystal.script_parse_table[self.address] == None and crystal.script_parse_table[self.last_address] == None:
        crystal.script_parse_table[self.address : self.last_address] = self
        #else:
        #    print ".. hm, something is already at " + hex(self.address) + " for " + self.label.name

    def to_asm(self):
        #output += self.label + ": ; " + hex(self.address) + "\n"
        output = "; script command " + hex(self.id) + "\n"
        if len(self.params) > 0:
            output += "; parameters:\n"
        for (id2, param) in self.params.items():
            output += ";     " + param["name"] + " (" + param["type"] + ")\n"
        output += "\n"
        output += self.asm
        return output

    def get_dependencies(*args, **kwargs):
        return []

# make instances of DisassembledScriptCommand
for (id, command) in commands.items():
    name = command["name"]
    params = command["params"]
    address = command["address"]

    script_asm = DisassembledScriptCommand(label=name, id=id, address=address, params=params)
    #print script_asm.to_asm()
    #print crystal.to_asm(script_asm, use_asm_rules=True)

class ScriptCommandTable():
    address = script_command_table_address
    last_address = script_command_table_address + (2 * 170)
    dependencies = None

    def __init__(self):
        self.label = crystal.Label(name="ScriptCommandTable", address=self.address, object=self)

        # make sure this gets dumped into main.asm
        crystal.script_parse_table[self.address : self.last_address] = self

    def get_dependencies(*args, **kwargs):
        return []

    def to_asm(self):
        output = ""
        for (id, command) in commands.items():
            output += "dw " + command["name"] + "; " + hex(command["address"]) + "\n"
        if output[-1] == "\n":
            output = output[:-1]
        return output
script_command_table = ScriptCommandTable()
#print crystal.to_asm(script_command_table, use_asm_rules=True)

# automatic asm insertion
asm = crystal.Asm()
asm.insert_and_dump(limit=500)