diff options
Diffstat (limited to 'pokemontools/old_text_script.py')
| -rw-r--r-- | pokemontools/old_text_script.py | 526 | 
1 files changed, 526 insertions, 0 deletions
| diff --git a/pokemontools/old_text_script.py b/pokemontools/old_text_script.py new file mode 100644 index 0000000..912c027 --- /dev/null +++ b/pokemontools/old_text_script.py @@ -0,0 +1,526 @@ +""" +An old implementation of TextScript that may not be useful anymore. +""" + +class OldTextScript: +    "a text is a sequence of commands different from a script-engine script" +    base_label = "UnknownText_" +    def __init__(self, address, map_group=None, map_id=None, debug=True, show=True, force=False, label=None): +        self.address = address +        self.map_group, self.map_id, self.debug, self.show, self.force = map_group, map_id, debug, show, force +        if not label: +            label = self.base_label + hex(address) +        self.label = Label(name=label, address=address, object=self) +        self.dependencies = [] +        self.parse_text_at(address) + +    @staticmethod +    def find_addresses(): +        """returns a list of text pointers +        useful for testing parse_text_engine_script_at + +        Note that this list is not exhaustive. There are some texts that +        are only pointed to from some script that a current script just +        points to. So find_all_text_pointers_in_script_engine_script will +        have to recursively follow through each script to find those. +        .. it does this now :) +        """ +        addresses = set() +        # for each map group +        for map_group in map_names: +            # for each map id +            for map_id in map_names[map_group]: +                # skip the offset key +                if map_id == "offset": continue +                # dump this into smap +                smap = map_names[map_group][map_id] +                # signposts +                signposts = smap["signposts"] +                # for each signpost +                for signpost in signposts: +                    if signpost["func"] in [0, 1, 2, 3, 4]: +                        # dump this into script +                        script = signpost["script"] +                    elif signpost["func"] in [05, 06]: +                        script = signpost["script"] +                    else: continue +                    # skip signposts with no bytes +                    if len(script) == 0: continue +                    # find all text pointers in script +                    texts = find_all_text_pointers_in_script_engine_script(script, smap["event_bank"]) +                    # dump these addresses in +                    addresses.update(texts) +                # xy triggers +                xy_triggers = smap["xy_triggers"] +                # for each xy trigger +                for xy_trigger in xy_triggers: +                    # dump this into script +                    script = xy_trigger["script"] +                    # find all text pointers in script +                    texts = find_all_text_pointers_in_script_engine_script(script, smap["event_bank"]) +                    # dump these addresses in +                    addresses.update(texts) +                # trigger scripts +                triggers = smap["trigger_scripts"] +                # for each trigger +                for (i, trigger) in triggers.items(): +                    # dump this into script +                    script = trigger["script"] +                    # find all text pointers in script +                    texts = find_all_text_pointers_in_script_engine_script(script, pointers.calculate_bank(trigger["address"])) +                    # dump these addresses in +                    addresses.update(texts) +                # callback scripts +                callbacks = smap["callback_scripts"] +                # for each callback +                for (k, callback) in callbacks.items(): +                    # dump this into script +                    script = callback["script"] +                    # find all text pointers in script +                    texts = find_all_text_pointers_in_script_engine_script(script, pointers.calculate_bank(callback["address"])) +                    # dump these addresses in +                    addresses.update(texts) +                # people-events +                events = smap["people_events"] +                # for each event +                for event in events: +                    if event["event_type"] == "script": +                        # dump this into script +                        script = event["script"] +                        # find all text pointers in script +                        texts = find_all_text_pointers_in_script_engine_script(script, smap["event_bank"]) +                        # dump these addresses in +                        addresses.update(texts) +                    if event["event_type"] == "trainer": +                        trainer_data = event["trainer_data"] +                        addresses.update([trainer_data["text_when_seen_ptr"]]) +                        addresses.update([trainer_data["text_when_trainer_beaten_ptr"]]) +                        trainer_bank = pointers.calculate_bank(event["trainer_data_address"]) +                        script1 = trainer_data["script_talk_again"] +                        texts1 = find_all_text_pointers_in_script_engine_script(script1, trainer_bank) +                        addresses.update(texts1) +                        script2 = trainer_data["script_when_lost"] +                        texts2 = find_all_text_pointers_in_script_engine_script(script2, trainer_bank) +                        addresses.update(texts2) +        return addresses + +    def parse_text_at(self, address): +        """parses a text-engine script ("in-text scripts") +        http://hax.iimarck.us/files/scriptingcodes_eng.htm#InText + +        This is presently very broken. + +        see parse_text_at2, parse_text_at, and process_00_subcommands +        """ +        global rom, text_count, max_texts, texts, script_parse_table +        if rom == None: +            direct_load_rom() +        if address == None: +            return "not a script" +        map_group, map_id, debug, show, force = self.map_group, self.map_id, self.debug, self.show, self.force +        commands = {} + +        if is_script_already_parsed_at(address) and not force: +            logging.debug("text is already parsed at this location: {0}".format(hex(address))) +            raise Exception("text is already parsed, what's going on ?") +            return script_parse_table[address] + +        total_text_commands = 0 +        command_counter = 0 +        original_address = address +        offset = address +        end = False +        script_parse_table[original_address:original_address+1] = "incomplete text" +        while not end: +            address = offset +            command = {} +            command_byte = ord(rom[address]) +            if debug: +                logging.debug( +                    "TextScript.parse_script_at has encountered a command byte {0} at {1}" +                    .format(hex(command_byte), hex(address)) +                ) +            end_address = address + 1 +            if  command_byte == 0: +                # read until $57, $50 or $58 +                jump57 = how_many_until(chr(0x57), offset, rom) +                jump50 = how_many_until(chr(0x50), offset, rom) +                jump58 = how_many_until(chr(0x58), offset, rom) + +                # whichever command comes first +                jump = min([jump57, jump50, jump58]) + +                end_address = offset + jump # we want the address before $57 + +                lines = process_00_subcommands(offset+1, end_address, debug=debug) + +                if show and debug: +                    text = parse_text_at2(offset+1, end_address-offset+1, debug=debug) +                    logging.debug("output of parse_text_at2 is {0}".format(text)) + +                command = {"type": command_byte, +                           "start_address": offset, +                           "end_address": end_address, +                           "size": jump, +                           "lines": lines, +                          } + +                offset += jump +            elif command_byte == 0x17: +                # TX_FAR [pointer][bank] +                pointer_byte1 = ord(rom[offset+1]) +                pointer_byte2 = ord(rom[offset+2]) +                pointer_bank = ord(rom[offset+3]) + +                pointer = (pointer_byte1 + (pointer_byte2 << 8)) +                pointer = extract_maps.calculate_pointer(pointer, pointer_bank) + +                text = TextScript(pointer, map_group=self.map_group, map_id=self.amp_id, debug=self.debug, \ +                                  show=self.debug, force=self.debug, label="Target"+self.label.name) +                if text.is_valid(): +                    self.dependencies.append(text) + +                command = {"type": command_byte, +                           "start_address": offset, +                           "end_address": offset + 3, # last byte belonging to this command +                           "pointer": pointer, # parameter +                           "text": text, +                          } + +                offset += 3 + 1 +            elif command_byte == 0x50 or command_byte == 0x57 or command_byte == 0x58: # end text +                command = {"type": command_byte, +                           "start_address": offset, +                           "end_address": offset, +                          } + +                # this byte simply indicates to end the script +                end = True + +                # this byte simply indicates to end the script +                if command_byte == 0x50 and ord(rom[offset+1]) == 0x50: # $50$50 means end completely +                    end = True +                    commands[command_counter+1] = command + +                    # also save the next byte, before we quit +                    commands[command_counter+1]["start_address"] += 1 +                    commands[command_counter+1]["end_address"] += 1 +                    add_command_byte_to_totals(command_byte) +                elif command_byte == 0x50: # only end if we started with $0 +                    if len(commands.keys()) > 0: +                        if commands[0]["type"] == 0x0: end = True +                elif command_byte == 0x57 or command_byte == 0x58: # end completely +                    end = True +                    offset += 1 # go past this 0x50 +            elif command_byte == 0x1: +                # 01 = text from RAM. [01][2-byte pointer] +                size = 3 # total size, including the command byte +                pointer_byte1 = ord(rom[offset+1]) +                pointer_byte2 = ord(rom[offset+2]) + +                command = {"type": command_byte, +                           "start_address": offset+1, +                           "end_address": offset+2, # last byte belonging to this command +                           "pointer": [pointer_byte1, pointer_byte2], # RAM pointer +                          } + +                # view near these bytes +                # subsection = rom[offset:offset+size+1] #peak ahead +                #for x in subsection: +                #    print hex(ord(x)) +                #print "--" + +                offset += 2 + 1 # go to the next byte + +                # use this to look at the surrounding bytes +                if debug: +                    logging.debug("next command is {0}".format(hex(ord(rom[offset])))) +                    logging.debug( +                        ".. current command number is {counter} near {offset} on map_id={map_id}" +                        .format( +                            counter=command_counter, +                            offset=hex(offset), +                            map_id=map_id, +                        ) +                    ) +            elif command_byte == 0x7: +                # 07 = shift texts 1 row above (2nd line becomes 1st line); address for next text = 2nd line. [07] +                size = 1 +                command = {"type": command_byte, +                           "start_address": offset, +                           "end_address": offset, +                          } +                offset += 1 +            elif command_byte == 0x3: +                # 03 = set new address in RAM for text. [03][2-byte RAM address] +                size = 3 +                command = {"type": command_byte, "start_address": offset, "end_address": offset+2} +                offset += size +            elif command_byte == 0x4: # draw box +                # 04 = draw box. [04][2-Byte pointer][height Y][width X] +                size = 5 # including the command +                command = { +                            "type": command_byte, +                            "start_address": offset, +                            "end_address": offset + size, +                            "pointer_bytes": [ord(rom[offset+1]), ord(rom[offset+2])], +                            "y": ord(rom[offset+3]), +                            "x": ord(rom[offset+4]), +                          } +                offset += size + 1 +            elif command_byte == 0x5: +                # 05 = write text starting at 2nd line of text-box. [05][text][ending command] +                # read until $57, $50 or $58 +                jump57 = how_many_until(chr(0x57), offset, rom) +                jump50 = how_many_until(chr(0x50), offset, rom) +                jump58 = how_many_until(chr(0x58), offset, rom) + +                # whichever command comes first +                jump = min([jump57, jump50, jump58]) + +                end_address = offset + jump # we want the address before $57 + +                lines = process_00_subcommands(offset+1, end_address, debug=debug) + +                if show and debug: +                    text = parse_text_at2(offset+1, end_address-offset+1, debug=debug) +                    logging.debug("parse_text_at2 text is {0}".format(text)) + +                command = {"type": command_byte, +                           "start_address": offset, +                           "end_address": end_address, +                           "size": jump, +                           "lines": lines, +                          } +                offset = end_address + 1 +            elif command_byte == 0x6: +                # 06 = wait for keypress A or B (put blinking arrow in textbox). [06] +                command = {"type": command_byte, "start_address": offset, "end_address": offset} +                offset += 1 +            elif command_byte == 0x7: +                # 07 = shift texts 1 row above (2nd line becomes 1st line); address for next text = 2nd line. [07] +                command = {"type": command_byte, "start_address": offset, "end_address": offset} +                offset += 1 +            elif command_byte == 0x8: +                # 08 = asm until whenever +                command = {"type": command_byte, "start_address": offset, "end_address": offset} +                offset += 1 +                end = True +            elif command_byte == 0x9: +                # 09 = write hex-to-dec number from RAM to textbox [09][2-byte RAM address][byte bbbbcccc] +                #  bbbb = how many bytes to read (read number is big-endian) +                #  cccc = how many digits display (decimal) +                #(note: max of decimal digits is 7,i.e. max number correctly displayable is 9999999) +                ram_address_byte1 = ord(rom[offset+1]) +                ram_address_byte2 = ord(rom[offset+2]) +                read_byte = ord(rom[offset+3]) + +                command = { +                            "type": command_byte, +                            "address": [ram_address_byte1, ram_address_byte2], +                            "read_byte": read_byte, # split this up when we make a macro for this +                          } + +                offset += 4 +            else: +                #if len(commands) > 0: +                #   print "Unknown text command " + hex(command_byte) + " at " + hex(offset) + ", script began with " + hex(commands[0]["type"]) +                if debug: +                    logging.debug( +                        "Unknown text command at {offset} - command: {command} on map_id={map_id}" +                        .format( +                            offset=hex(offset), +                            command=hex(ord(rom[offset])), +                            map_id=map_id, +                        ) +                    ) + +                # end at the first unknown command +                end = True +            commands[command_counter] = command +            command_counter += 1 +        total_text_commands += len(commands) + +        text_count += 1 +        #if text_count >= max_texts: +        #    sys.exit() + +        self.commands = commands +        self.last_address = offset +        script_parse_table[original_address:offset] = self +        all_texts.append(self) +        self.size = self.byte_count = self.last_address - original_address +        return commands + +    def get_dependencies(self, recompute=False, global_dependencies=set()): +        global_dependencies.update(self.dependencies) +        return self.dependencies + +    def to_asm(self, label=None): +        address = self.address +        start_address = address +        if label == None: label = self.label.name +        # using deepcopy because otherwise additional @s get appended each time +        # like to the end of the text for TextScript(0x5cf3a) +        commands = deepcopy(self.commands) +        # apparently this isn't important anymore? +        needs_to_begin_with_0 = True +        # start with zero please +        byte_count = 0 +        # where we store all output +        output = "" +        had_text_end_byte = False +        had_text_end_byte_57_58 = False +        had_db_last = False +        xspacing = "" +        # reset this pretty fast.. +        first_line = True +        # for each command.. +        for this_command in commands.keys(): +            if not "lines" in commands[this_command].keys(): +                command = commands[this_command] +                if not "type" in command.keys(): +                    logging.debug("ERROR in command: {0}".format(command)) +                    continue # dunno what to do here? + +                if   command["type"] == 0x1: # TX_RAM +                    p1 = command["pointer"][0] +                    p2 = command["pointer"][1] + +                    # remember to account for big endian -> little endian +                    output += "\n" + xspacing + "TX_RAM $%.2x%.2x" %(p2, p1) +                    byte_count += 3 +                    had_db_last = False +                elif command["type"] == 0x17: # TX_FAR +                    #p1 = command["pointer"][0] +                    #p2 = command["pointer"][1] +                    output += "\n" + xspacing + "TX_FAR _" + label + " ; " + hex(command["pointer"]) +                    byte_count += 4 # $17, bank, address word +                    had_db_last = False +                elif command["type"] == 0x9: # TX_RAM_HEX2DEC +                    # address, read_byte +                    output += "\n" + xspacing + "TX_NUM $%.2x%.2x, $%.2x" % (command["address"][1], command["address"][0], command["read_byte"]) +                    had_db_last = False +                    byte_count += 4 +                elif command["type"] == 0x50 and not had_text_end_byte: +                    # had_text_end_byte helps us avoid repeating $50s +                    if had_db_last: +                        output += ", $50" +                    else: +                        output += "\n" + xspacing + "db $50" +                    byte_count += 1 +                    had_db_last = True +                elif command["type"] in [0x57, 0x58] and not had_text_end_byte_57_58: +                    if had_db_last: +                        output += ", $%.2x" % (command["type"]) +                    else: +                        output += "\n" + xspacing + "db $%.2x" % (command["type"]) +                    byte_count += 1 +                    had_db_last = True +                elif command["type"] in [0x57, 0x58] and had_text_end_byte_57_58: +                    pass # this is ok +                elif command["type"] == 0x50 and had_text_end_byte: +                    pass # this is also ok +                elif command["type"] == 0x0b: +                    if had_db_last: +                        output += ", $0b" +                    else: +                        output += "\n" + xspacing + "db $0B" +                    byte_count += 1 +                    had_db_last = True +                elif command["type"] == 0x11: +                    if had_db_last: +                        output += ", $11" +                    else: +                        output += "\n" + xspacing + "db $11" +                    byte_count += 1 +                    had_db_last = True +                elif command["type"] == 0x6: # wait for keypress +                    if had_db_last: +                        output += ", $6" +                    else: +                        output += "\n" + xspacing + "db $6" +                    byte_count += 1 +                    had_db_last = True +                else: +                    logging.debug("ERROR in command: {0}".format(hex(command["type"]))) +                    had_db_last = False + +                # everything else is for $0s, really +                continue +            lines = commands[this_command]["lines"] + +            # reset this in case we have non-$0s later +            had_db_last = False + +            # add the ending byte to the last line- always seems $57 +            # this should already be in there, but it's not because of a bug in the text parser +            lines[len(lines.keys())-1].append(commands[len(commands.keys())-1]["type"]) + +            first = True # first byte +            for line_id in lines: +                line = lines[line_id] +                output += xspacing + "db " +                if first and needs_to_begin_with_0: +                    output += "$0, " +                    first = False +                    byte_count += 1 + +                quotes_open = False +                first_byte = True +                was_byte = False +                for byte in line: +                    if byte == 0x50: +                        had_text_end_byte = True # don't repeat it +                    if byte in [0x58, 0x57]: +                        had_text_end_byte_57_58 = True + +                    if byte in chars.chars: +                        if not quotes_open and not first_byte: # start text +                            output += ", \"" +                            quotes_open = True +                            first_byte = False +                        if not quotes_open and first_byte: # start text +                            output += "\"" +                            quotes_open = True +                        output += chars.chars[byte] +                    elif byte in constant_abbreviation_bytes: +                        if quotes_open: +                            output += "\"" +                            quotes_open = False +                        if not first_byte: +                            output += ", " +                        output += constant_abbreviation_bytes[byte] +                    else: +                        if quotes_open: +                            output += "\"" +                            quotes_open = False + +                        # if you want the ending byte on the last line +                        #if not (byte == 0x57 or byte == 0x50 or byte == 0x58): +                        if not first_byte: +                            output += ", " + +                        output += "$" + hex(byte)[2:] +                        was_byte = True + +                        # add a comma unless it's the end of the line +                        #if byte_count+1 != len(line): +                        #    output += ", " + +                    first_byte = False +                    byte_count += 1 +                # close final quotes +                if quotes_open: +                    output += "\"" +                    quotes_open = False + +                output += "\n" +        #include_newline = "\n" +        #if len(output)!=0 and output[-1] == "\n": +        #    include_newline = "" +        #output += include_newline + "; " + hex(start_address) + " + " + str(byte_count) + " bytes = " + hex(start_address + byte_count) +        if len(output) > 0 and output[-1] == "\n": +            output = output[:-1] +        self.size = self.byte_count = byte_count +        return output | 
