From 6ec2257cbcf24b566731aa1445d29c1f3849a5be Mon Sep 17 00:00:00 2001 From: Bryan Bishop Date: Wed, 7 Mar 2012 22:31:59 -0600 Subject: [PATCH] more crystal script parsing --- extras/crystal.py | 736 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 632 insertions(+), 104 deletions(-) diff --git a/extras/crystal.py b/extras/crystal.py index 316030d5c..33e422676 100644 --- a/extras/crystal.py +++ b/extras/crystal.py @@ -382,96 +382,168 @@ def calculate_pointer_from_bytes_at(address, bank=False): temp = byte1 + (byte2 << 8) return calculate_pointer(temp, bank) -def command_debug_information(command_byte=None, map_group=None, map_id=None, address=None): - return "parsing command byte " + hex(command_byte) + " for map " + \ - str(map_group) + "." + str(map_id) + " at " + hex(address) +def clean_up_long_info(long_info): + """cleans up some data from parse_script_engine_script_at formatting issues""" + long_info = str(long_info) + #get rid of the first newline + if long_info[0] == "\n": + long_info = long_info[1:] + #get rid of the last newline and any leftover space + if long_info.count("\n") > 0: + if long_info[long_info.rindex("\n")+1:].isspace(): + long_info = long_info[:long_info.rindex("\n")] + #remove spaces+hash from the front of each line + new_lines = [] + for line in long_info.split("\n"): + line = line.strip() + if line[0] == "#": + line = line[1:] + new_lines.append(line) + long_info = "\n".join(new_lines) + return long_info -def parse_script_at(address): +def command_debug_information(command_byte=None, map_group=None, map_id=None, address=None, info=None, long_info=None): + info1 = "parsing command byte " + hex(command_byte) + " for map " + \ + str(map_group) + "." + str(map_id) + " at " + hex(address) + info1 += "\tinfo: " + str(info) + info1 += "\tlong_info: " + long_info + return info1 + +def parse_text_engine_script_at(address): + """parses a text-engine script ("in-text scripts") + http://hax.iimarck.us/files/scriptingcodes_eng.htm#InText + """ + global rom + if rom == None: + load_rom() + commands = {} + return commands + +def parse_script_engine_script_at(address, map_group=None, map_id=None): """parses a script-engine script""" global rom if rom == None: load_rom() - return {} commands = {} offset = address end = False while not end: + info, long_info, size = None, None, 0 command_byte = ord(rom[offset]) command = {"type": command_byte, "start_address": offset} - print command_debug_information(command_byte=command_byte, map_group=map_group, map_id=map_id, address=offset) #size is the total size including the command byte #last_byte_address is offset+size-1 start_address = offset if command_byte == 0x00: #Pointer code [2b+ret] - #2byte pointer points to script; when pointed script ends --> return to old script - #[code][2 byte pointer] + info = "pointer code" + long_info = """ + 2byte pointer points to script; when pointed script ends --> return to old script + [code][2 byte pointer] + """ size = 3 start_address = offset last_byte_address = offset + size - 1 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) #XXX should we also parse this target script? elif command_byte == 0x01: #Pointer code [3b+ret] - #3byte pointer points to script; when pointed script ends --> return to old script - #[Code][resp. pointer(2byte or 3byte)] + info = "pointer code" + long_info = """ + 3byte pointer points to script; when pointed script ends --> return to old script + [Code][resp. pointer(2byte or 3byte)] + """ size = 4 + info = "pointer code" command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=True) elif command_byte == 0x02: #Pointer code [2b+3b+ret] - #2byte pointer points to 3byte pointer; when pointed script --> return to old script - #[Code][resp. pointer(2byte or 3byte)] + info = "pointer code" + long_info = """ + 2byte pointer points to 3byte pointer; when pointed script --> return to old script + [Code][resp. pointer(2byte or 3byte)] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x03: #Pointer code [2b] #XXX what does "new script is part of main script" mean? - #2byte pointer points to script; new script is part of main script - #[Code][resp. pointer(2byte or 3byte)] + info = "pointer code" + long_info = """ + 2byte pointer points to script; new script is part of main script + [Code][resp. pointer(2byte or 3byte)] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x04: #Pointer code [3b] - #3byte pointer points to script; new script is part of main script - #[Code][resp. pointer(2byte or 3byte)] + info = "pointer code" + long_info = """ + 3byte pointer points to script; new script is part of main script + [Code][resp. pointer(2byte or 3byte)] + """ size = 4 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=True) elif command_byte == 0x05: #Pointer code [2b+3b] - #2byte pointer points to 3byte pointer; new script is part of main script - #[Code][resp. pointer(2byte or 3byte)] + info = "pointer code" + long_info = """ + 2byte pointer points to 3byte pointer; new script is part of main script + [Code][resp. pointer(2byte or 3byte)] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x06: #RAM check [=byte] - #When the conditional is true... - #.. then go to pointed script, else resume interpreting after the pointer + info = "RAM check [=byte]" + long_info = """ + When the conditional is true... + .. then go to pointed script, else resume interpreting after the pointer + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x07: #RAM check [<>byte] - #When the conditional is true... - #.. then go to pointed script, else resume interpreting after the pointer + info = "RAM check [<>byte]" + long_info = """ + When the conditional is true... + .. then go to pointed script, else resume interpreting after the pointer + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x08: #RAM check [=0] - #When the conditional is true... - #.. then go to pointed script, else resume interpreting after the pointer + info = "RAM check [=0]" + long_info = """ + When the conditional is true... + .. then go to pointed script, else resume interpreting after the pointer + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x09: #RAM check [<>0] - #When the conditional is true... - #.. then go to pointed script, else resume interpreting after the pointer + info = "RAM check [<>0]" + long_info = """ + When the conditional is true... + .. then go to pointed script, else resume interpreting after the pointer + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x0A: #RAM check [byte] - #When the conditional is true... - #.. then go to pointed script, else resume interpreting after the pointer + info = "RAM check [>byte]" + long_info = """ + When the conditional is true... + .. then go to pointed script, else resume interpreting after the pointer + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1) elif command_byte == 0x0C: #0C codes [xxyy] - #Calls predefined scripts. After this code the script ends. - #[0C][xxyy] + info = "call predefined script then end" + long_info = """ + Calls predefined scripts. After this code the script ends. + [0C][xxyy] + """ size = 3 end = True byte1 = ord(rom[offset+1]) @@ -480,7 +552,11 @@ def parse_script_at(address): #0000 to 000AD ... XXX how should these be handled? command["predefined_script_number"] = number elif command_byte == 0x0D: #0D codes [xxyy] - #Calls predefined scripts. Exactly like $0C except the script does not end. + info = "call some predefined script" + long_info = """ + Calls predefined scripts. Exactly like $0C except the script does not end. + [0D][xxyy] + """ size = 3 byte1 = ord(rom[offset+1]) byte2 = ord(rom[offset+2]) @@ -488,92 +564,134 @@ def parse_script_at(address): #0000 to 000AD ... XXX how should these be handled? command["predefined_script_number"] = number elif command_byte == 0x0E: #ASM code1 [3b] - #Calls a predefined routine by interpreting the ASM the pointer points to. - #[0E][3byte pointer] + info = "ASM code1 [3b]" + long_info = """ + Calls a predefined routine by interpreting the ASM the pointer points to. + [0E][3byte pointer] + """ size = 4 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=True) #XXX should we dissassemble the asm at the target location? elif command_byte == 0x0F: #0F codes [xxyy] - #Calls predefined scripts. - #[0F][xxyy] - #NOTE: For (some) dialogues the font needs to be loaded with the Text box&font code. + info = "call some predefined script [0F][yyxx]" + long_info = """ + Calls predefined scripts. + [0F][yyxx] + NOTE: For (some) dialogues the font needs to be loaded with the Text box&font code. + """ size = 3 byte1 = ord(rom[offset+1]) byte2 = ord(rom[offset+2]) number = byte1 + (byte2 << 8) command["predefined_script_number"] = number elif command_byte == 0x10: #ASM code2 [2b] - #Call an ASM script via a 2byte pointer pointing to a 3byte pointer. - #[10][2byte pointer pointing to 3byte pointer pointing to ASM script] + info = "ASM code2 [2b to 3b to asm]" + long_info = """ + Call an ASM script via a 2byte pointer pointing to a 3byte pointer. + [10][2byte pointer pointing to 3byte pointer pointing to ASM script] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) #XXX should i include the 3-byte pointer at the target location? #XXX should we dissassemble the asm at the target location? elif command_byte == 0x11: #Trigger event check1 [xxyy] - #Check the current number of the trigger event on map (map bank/map no). - #[11][MapBank][MapNo] + info = "Trigger event check1 [xx][yy]" + long_info = """ + Check the current number of the trigger event on map (map group/map id). + [11][map group][map number] + """ size = 3 command["map_group"] = ord(rom[start_address+1]) command["map_id"] = ord(rom[start_address+2]) elif command_byte == 0x12: #Activate trigger event from afar [xxyyzz] - #Changes trigger event number on map (map bank/map no) to xx. - #xx = trigger event number that should be activated - #[12][MapBank][MapNo][xx] + info = "Activate trigger event from afar [xx][yy][zz]" + long_info = """ + Changes trigger event number on map (map bank/map no) to xx. + xx = trigger event number that should be activated + [12][MapBank][MapNo][xx] + """ size = 4 command["map_group"] = ord(rom[start_address+1]) command["map_id"] = ord(rom[start_address+2]) command["trigger_number"] = ord(rom[start_address+3]) elif command_byte == 0x13: #Trigger event check - #Checks the number of the trigger events on the current map. - #[13] + info = "Trigger event check" + long_info = """ + Checks the number of the trigger events on the current map. + [13] + """ size = 1 elif command_byte == 0x14: #De-/activate trigger event [xx] - #Changes trigger event number on current map to xx. - #xx = trigger event number that should be activated - #[14][xx] - #deactivate? Just activate a different trigger event number. There's a limit of 1 active trigger. + info = "De-/activate trigger event [xx]" + long_info = """ + Changes trigger event number on current map to xx. + xx = trigger event number that should be activated + [14][xx] + deactivate? Just activate a different trigger event number. There's a limit of 1 active trigger. + """ size = 2 command["trigger_number"] = ord(rom[start_address+1]) elif command_byte == 0x15: #Load variable into RAM [xx] - #[15][xx] + info = "Load variable into RAM [xx]" + long_info = "[15][xx]" size = 2 command["variable"] = ord(rom[start_address+1]) elif command_byte == 0x16: #Add variables [xx] - #Adds xx and the variable in RAM. + info = "Add variables [xx]" + long_info = """ + Adds xx and the variable in RAM. #[16][xx] + """ size = 2 command["variable"] = ord(rom[start_address+1]) elif command_byte == 0x17: #Random number [xx] + info = "Random number [xx]" + long_info = """ #Reads xx and creates a random number between 00 and xx -1. #According to this xx can be all but 00. Random number = [00; xx) #The nearer the random number is to xx, the rarer it occurs. #Random number gets written to RAM. + """ size = 2 command["rarest"] = ord(rom[start_address+1]) elif command_byte == 0x18: #Version check + info = "G/S version check" + long_info = """ #Check if version is gold or silver. Gives feedback. #00 = Gold #01 = Silver #[18] + """ size = 1 elif command_byte == 0x19: #Copy variable code1 [xxyy] + info = "Copy from RAM address to script RAM variable [xxyy]" + long_info = """ #Writes variable from ram address to RAM. #[19][2-byte RAM address] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x1A: #Copy variable code2 [xxyy] + info = "Write variable from script RAM variable to actual RAM address [xxyy]" + long_info = """ #Writes variable from RAM to actual RAM address. #[1A][2-byte RAM address] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x1B: #Load variable [xxyyzz] + info = "Load variable [xxyy][zz]" + long_info = """ #Writes zz to ram address. #[1B][2-byte RAM address][zz] + """ size = 4 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) command["value"] = ord(rom[start_address+3]) elif command_byte == 0x1C: #Check codes [xx] #XXX no idea what's going on in this one :( + info = "Check pre-ID-mapped RAM location [xx]" + long_info = """ #Checks special game-technical values and writes then into RAM. #[1C][following part][Ram check (when <> 08/09 see „numbers“ in list of following parts)] #following part (and then hex values) @@ -615,63 +733,89 @@ def parse_script_at(address): #12 = X position of HIRO #13 = Y position of HIRO #14 = phone call number + """ size = 2 #i think? command["following_part"] = ord(rom[start_address+1]) elif command_byte == 0x1D: #Input code1 [xx] + info = "Write to pre-ID-mapped RAM location [xx]" + long_info = """ #Writes variable from RAM to special game-technical value offsets. #[1D][following part] #where [following part] is the same as 0x1C + """ size = 2 command["following_part"] = ord(rom[start_address+1]) elif command_byte == 0x1E: #Input code2 [xxyy] + info = "Write byte value to pre-ID-mapped RAM location [aa][xx]" + long_info = """ #Writes variable xx to special game-technical value offsets. #[1E][following part][xx] #where [following part] is the same as 0x1C + """ size = 3 command["following_part"] = ord(rom[start_address+1]) command["value"] = ord(rom[start_address+2]) elif command_byte == 0x1F: #Give item code [xxyy] + info = "Give item by id and quantity [xx][yy]" + long_info = """ #Gives item (item no) amount times. #feedback: # 00 = bag full # 01 = OK #[1F][item no][amount] + """ size = 3 command["item_id"] = ord(rom[start_address+1]) command["quantity"] = ord(rom[start_address+2]) elif command_byte == 0x20: #Take item code [xxyy] + info = "Take item by id and quantity [xx][yy]" + long_info = """ #Gives item (item no) amount times #feedback: # 00 = not enough items #[20][item no][amount] + """ size = 3 command["item_id"] = ord(rom[start_address+1]) command["quantity"] = ord(rom[start_address+2]) elif command_byte == 0x21: #Check for item code [xx] + info = "Check if player has item [xx]" + long_info = """ #Checks if item is possessed. #feedback: # 00 = does not have item # 01 = has item #[21][item no] + """ size = 2 command["item_id"] = ord(rom[start_address+1]) elif command_byte == 0x22: #Give money code [xxyyzzaa] + info = "Give money to HIRO/account [xxyyzzaa]" + long_info = """ #Gives zzyyxx money to HIRO/account. #zzyyxx = amount of money (000000 - 0F423F) #[22][00-HIRO/01-account][xxyyzz] + """ size = 5 bytes = rom_interval(start_address, size, strings=False) command["account"] = bytes[1] - raise NotImplementedError, "don't know if zzyyxx is a decimal or hex value" + command["amount"] = bytes[2:] + #raise NotImplementedError, "don't know if zzyyxx is a decimal or hex value" elif command_byte == 0x23: #Take money code [xxyyzzaa] + info = "Take money from HIRO/account [xxyyzzaa]" + long_info = """ #Takes zzyyxx money from HIRO/account. #zzyyxx = amount of money (000000 - 0F423F) #[23][00-HIRO/01-account][xxyyzz] + """ size = 5 bytes = rom_interval(start_address, size, strings=False) command["account"] = bytes[1] - raise NotImplementedError, "don't know if zzyyxx is a decimal or hex value" + command["amount"] = bytes[2:] + #raise NotImplementedError, "don't know if zzyyxx is a decimal or hex value" elif command_byte == 0x24: #Check for money code [xxyyzzaa] + info = "Check if HIRO/account has enough money [xxyyzzaa]" + long_info = """ #Checks if HIRO/account has got zzyyxx money. #feedback: # 00 = enough money @@ -679,25 +823,36 @@ def parse_script_at(address): # 02 = less money #zzyyxx = amount of money (000000 - 0F423F) #[24][00-HIRO/01-account][xxyyzz] + """ size = 5 bytes = rom_interval(start_address, size, strings=False) command["account"] = bytes[1] - raise NotImplementedError, "don't know if zzyyxx is a decimal or hex value" + command["quantity"] = bytes[2:] + #XXX how is quantity formatted? + #raise NotImplementedError, "don't know if zzyyxx is a decimal or hex value" elif command_byte == 0x25: #Give coins code [xxyy] + info = "Give coins to HIRO [xxyy]" + long_info = """ #Gives coins to HIRO. #yyxx = amount of coins (0000 - 270F) #[25][xxyy] + """ size = 3 bytes = rom_interval(start_address, size, strings=False) command["quantity"] = bytes[1] + (bytes[2] << 8) elif command_byte == 0x26: #Take coins code [xxyy] + info = "Take coins from HIRO [xxyy]" + long_info = """ #Takes coins away from HIRO. #yyxx = amount of coins (0000 - 270F) #[26][xxyy] + """ size = 3 bytes = rom_interval(start_address, size, strings=False) command["quantity"] = bytes[1] + (bytes[2] << 8) elif command_byte == 0x27: #Check for coins code [xxyy] + info = "Check if HIRO has enough coins [xxyy]" + long_info = """ #Checks if HIRO has enough coins. #feedback: # 00 = has enough coins @@ -705,10 +860,13 @@ def parse_script_at(address): # 02 = does not have enough #yyxx = amount of coins necessary (0000 - 270F) #[27][xxyy] + """ size = 3 bytes = rom_interval(start_address, size, strings=False) command["quantity"] = bytes[1] + (bytes[2] << 8) elif command_byte == 0x28: #Give cell phone number [xx] + info = "Give cell phone number [xx]" + long_info = """ #Gives number to HIRO. #feedback: # 00 = number was added successfully @@ -719,44 +877,59 @@ def parse_script_at(address): #02 = bike store #03 = bll #04 = elm + """ size = 2 command["number"] = ord(rom[start_address+1]) elif command_byte == 0x29: #Take cell phone number [xx] + info = "Delete cell phone number [xx]" + long_info = """ #Deletes a number from the list. #feedback: # 00 = number deleted successfully # 01 = number wasn't in list #xx = number of person #[29][xx] + """ size = 2 command["number"] = ord(rom[start_address+1]) elif command_byte == 0x2A: #Check for cell phone number [xx] + info = "Check for cell phone number [xx]" + long_info = """ #Checks if a number is in the list. #feedback: # 00 = number is in list # 01 = number not in list #xx = number to look for #[2A][xx] + """ size = 2 command["number"] = ord(rom[start_address+1]) elif command_byte == 0x2B: #Check time of day [xx] + info = "Check time of day [xx]" + long_info = """ #Checks the time of day. #feedback: # 00 = time of day is the same # 01 = time of day is not the same #[2B][time of day (01morn-04night)] + """ size = 2 command["time_of_day"] = ord(rom[start_address+1]) elif command_byte == 0x2C: #Check for PKMN [xx] + info = "Check for pokemon [xx]" + long_info = """ #Checks if there is a certain PKMN in team. #feedback: # 00 = in team # 01 = not in team #xx = pkmn id #[2C][xx] + """ size = 2 command["pokemon_id"] = ord(rom[start_address+1]) elif command_byte == 0x2D: #Give PKMN [xxyyzzaa(+2b +2b)] + info = "Give pokemon [pokemon][level][item][trainer2b][...]" + long_info = """ #Gives a PKMN if there's space #feedback: # trainer id @@ -765,6 +938,7 @@ def parse_script_at(address): # 00 = HIRO # 01 = after the main code there are 4 bytes added # [2byte pointer to trainer's name (max.0x0A figures + 0x50)][2byte pointer to nickname (max.0x0A figures + 0x50)] + """ size = 5 bytes = rom_interval(start_address, size, strings=False) command["pokemon_id"] = bytes[1] @@ -777,21 +951,29 @@ def parse_script_at(address): command["trainer_name_pointer"] = calculate_pointer_from_bytes_at(start_address+5, bank=False) command["pokemon_nickname_pointer"] = calculate_pointer_from_bytes_at(start_address+7, bank=False) elif command_byte == 0x2E: #Give EGG [xxyy] + info = "Give egg [xx][yy]" + long_info = """ #Gives egg if there's space. #feedback: # 00 = OK # 02 = transaction not complete #[2E][PKMN][PKMNlvl] + """ size = 3 command["pokemon_id"] = ord(rom[start_address+1]) command["pokemon_level"] = ord(rom[start_address+2]) elif command_byte == 0x2F: #Attach item code [2B] + info = "Attach item to last pokemon in list [xxyy]" + long_info = """ #Gives last PKMN in list an item and letter text if applicable. Replaces existing items. #[2F][2byte pointer to item no + 0x20 bytes letter text] + """ size = 3 command["item_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) #XXX are those 20 bytes supposed to happen here? or at the pointer's destination? elif command_byte == 0x30: #Check letter code [2b] + info = "Check letter against known letter [xxyy]" + long_info = """ #Opens a PKMN list. Selected PKMN must have the right letter + the right contents. If OK, then PKMN is taken away #feedback: # 00 = wrong letter @@ -800,60 +982,85 @@ def parse_script_at(address): # 03 = Chosen PKMN has no letter # 04 = Chosen PKMN is the only one in the list. #[30][2byte pointer to letter item no + 0x20 bytes letter text] + """ size = 3 command["item_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x31: #BitTable1 check [xxyy] + info = "Check some bit on bit table 1 [xxyy]" + long_info = """ #Checks whether a bit of BitTable1 has the value 0 or 1. #feedback: # 00 = value 0 (off) # 01 = value 1 (on) #[31][2-byte bit number] + """ #XXX what format is the 2-byte number in? size = 3 bytes = rom_interval(start_address+1, size-1, strings=False) command["bit_number_bytes"] = bytes #raise NotImplementedError, "what format is the 2-byte number in?" elif command_byte == 0x32: #BitTable1 reset [xxyy] + info = "Reset (to 0) a bit on bit table 1 [xxyy]" + long_info = """ #Sets a bit of BitTable1 to value 0. #[32][Bit no (2byte)] + """ size = 3 bytes = rom_interval(start_address+1, size-1, strings=False) command["bit_number_bytes"] = bytes elif command_byte == 0x33: #BitTable1 set [xxyy] + info = "Set (to 1) a bit on bit table 1 [xxyy]" + long_info = """ #Sets a bit of BitTable1 to value 1. #[33][Bit-No (2byte)] + """ size = 3 bytes = rom_interval(start_address+1, size-1, strings=False) command["bit_number_bytes"] = bytes elif command_byte == 0x34: #BitTable2 check [xxyy] + info = "Check some bit on bit table 2 [xxyy]" + long_info = """ #Checks whether a bit of BitTable2 has the value 0 or 1. #feedback: # 00 = value 0 (off) # 01 = value 1 (on) #[34][Bit no (2byte)] + """ size = 3 bytes = rom_interval(start_address+1, size-1, strings=False) command["bit_number_bytes"] = bytes elif command_byte == 0x35: #BitTable2 reset [xxyy] + info = "Reset (to 0) a bit on bit table 2 [xxyy]" + long_info = """ #Sets a bit of BitTable2 to value 0. #[35][Bit no (2byte)] + """ size = 3 bytes = rom_interval(start_address+1, size-1, strings=False) command["bit_number_bytes"] = bytes elif command_byte == 0x36: #BitTable2 set [xxyy] + info = "Set (to 1) a bit on bit table 2 [xxyy]" + long_info = """ #Sets a bit of BitTable2 to value 1. #[36][Bit no (2byte)] + """ size = 3 bytes = rom_interval(start_address+1, size-1, strings=False) command["bit_number_bytes"] = bytes elif command_byte == 0x37: #Deactivate PKMN battles + info = "Turn off wild pokemon battles" + long_info = """ #This code turns all wild PKMN battles off. #[37] + """ size = 1 elif command_byte == 0x38: #Activate PKMN battles - #This code turns all wild PKMN battles on. + info = "Turn no wild pokemon battles" + long_info = "This code turns all wild PKMN battles on." size = 1 elif command_byte == 0x39: #X/Y comparison [xxyy] + info = "BUGGY x/y comparison [xxyy]" + long_info = """ #This code is buggy (Bug fix: 0x3021 --> C6) and can't used as #described without fix. This code compares the X and Y coordinates of #HIRO with the ones in a table (max. 20h XY pairs) on the current map. @@ -862,222 +1069,328 @@ def parse_script_at(address): #in a command queue, because with every regular move of HIRO the bits #are reset again. This code is an alternative to the trigger events and #can be used via the command queue code. + #See Write command queue, Additional documentation: 3:4661 with c= index + #in table (start=00), hl=D171, b=01, d=00. + """ size = 3 command["table_pointer"] = rom_interval(start_address+1, size-1, strings=False) - #See Write command queue, Additional documentation: 3:4661 with c= index in table (start=00), hl=D171, b=01, d=00. elif command_byte == 0x3A: #Warp modifier [xxyyzz] + info = "Set target for 0xFF warps [warp id][map group][map id]" + long_info = """ #Changes warp data for all warps of the current map that have a 0xFF for warp-to data. #[3A][Nee warp-to][New map bank][New map no] + """ size = 4 bytes = rom_interval(start_address+1, size-1, strings=False) command["nee_warp_to"] = bytes[0] command["map_group"] = bytes[1] command["map_id"] = bytes[2] elif command_byte == 0x3B: #Blackout modifier [xxyy] + info = "Blackout warp modifier [map group][map id]" + long_info = """ #Changes the map HIRO arrives at, after having a blackout. #There needs to be flying data for that map. #[3B][Map bank][Map no] + """ size = 3 command["map_group"] = ord(rom[start_address+1]) command["map_id"] = ord(rom[start_address+2]) elif command_byte == 0x3C: #Warp code [xxyyzzaa] + info = "Warp to [map group][map id][x][y]" + long_info = """ #Warps to another map. #If all data is 00s, then the current map is reloaded with #the current X and Y coordinates. Old script is not finished #without a [90]. #[3C][Map bank][Map no][X][Y] + """ size = 5 command["map_group"] = ord(rom[start_address+1]) command["map_id"] = ord(rom[start_address+2]) command["x"] = ord(rom[start_address+3]) command["y"] = ord(rom[start_address+4]) elif command_byte == 0x3D: #Account code [xxyy] + info = "Read money amount [xx][yy]" + long_info = """ #Reads amount of money in accounts of HIRO and mother and writes #it to MEMORY1, 2 or 3 for later use in text. #[3D][00 = HIRO| <> 00 = Mother][00-02 MEMORY] #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#InText01 + """ size = 3 command["account_id"] = ord(rom[start_address+1]) command["memory_id"] = ord(rom[start_address+2]) elif command_byte == 0x3E: #Coin case code [xx] + info = "Read coins amount [xx]" + long_info = """ #Reads amount of coins in coin case and writes it to MEMORY 1, 2, #or 3 for later use in text. #[3E][00-02 MEMORY] + """ size = 2 command["memory_id"] = ord(rom[start_address+1]) elif command_byte == 0x3F: #Display RAM [xx] + info = "Copy script RAM value into memX [xx]" + long_info = """ #Reads RAM value and writes it to MEMORY1, 2 or 3 for later use in text. #[3F][00-02 MEMORY] + """ size = 2 command["memory_id"] = ord(rom[start_address+1]) elif command_byte == 0x40: #Display pokémon name [xxyy] - #Writes PokéMon name to MEMORY1, 2 or 3 for later use in text. + info = "Copy pokemon name (by id) to memX [id][xx]" + long_info = """ + #Writes pokémon name to MEMORY1, 2 or 3 for later use in text. #[40][PKMN no][00-02 MEMORY] + """ size = 3 command["map_id"] = ord(rom[start_address+1]) command["memory_id"] = ord(rom[start_address+2]) elif command_byte == 0x41: #Display item name [xxyy] + info = "Copy item name (by id) to memX [id][xx]" + long_info = """ #Writes item name to MEMORY1, 2 or 3 for later use in text. #[41][Item no][00-02 MEMORY] + """ size = 3 command["item_id"] = ord(rom[start_address+1]) command["memory_id"] = ord(rom[start_address+2]) elif command_byte == 0x42: #Display location name [xx] + info = "Copy map name to memX [xx]" + long_info = """ #Writes current location's name to MEMORY1, 2 or 3 for later use in text. #[42][00-02 MEMORY] + """ size = 2 command["memory_id"] = ord(rom[start_address+1]) elif command_byte == 0x43: #Display trainer name [xxyyzz] + info = "Copy trainer name (by id&group) to memZ [xx][yy][zz]" + long_info = """ #Writes trainer name to MEMORY1, 2 or 3 for later use in text. #[43][Trainer number][Trainer group][00-02 MEMORY] + """ size = 4 command["trainer_id"] = ord(rom[start_address+1]) command["trainer_group"] = ord(rom[start_address+2]) command["memory_id"] = ord(rom[start_address+3]) elif command_byte == 0x44: #Display strings [2b + xx] + info = "Copy text (by pointer) to memX [aabb][xx]" + long_info = """ #Writes string to MEMORY1, 2 or 3 for later use in text. #[44][2byte pointer to string (max. 0x0C figures + 0x50)][00-02 MEMORY] #See 0C codes: 0C2900, 0C2A00, 0C1B00, 0C2200, Usage of variable strings in text. + """ size = 4 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) command["memory_id"] = ord(rom[start_address+3]) elif command_byte == 0x45: #Stow away item code + info = "Show HIRO put the ITEMNAME in the ITEMPOCKET text box" + long_info = """ #Text box: "HIRO put the ITEMNAME in the ITEMPOCKET." #The item number has to have been loaded beforehand #(e.g. by Give item code). + """ size = 1 elif command_byte == 0x46: #Full item pocket code + info = "Show ITEMPOCKET is full textbox" + long_info = """ #Text box: "ITEMPOCKET is full..." The item number has to have #been loaded beforehand (e.g. by Give item code). + """ size = 1 elif command_byte == 0x47: #Text box&font code - #Loads the font into the ram and opens a text box. + info = "Loads the font into the ram and opens a text box." size = 1 elif command_byte == 0x48: #Refresh code [xx] + info = "Screen refresh [xx]" + long_info = """ #Executes a complete screen refresh. #[48][xx] #xx is a dummy byte + """ size = 2 command["dummy"] = ord(rom[start_address+1]) elif command_byte == 0x49: #Load moving sprites - #Loads moving sprites for person events into ram. + info = "Load moving sprites into memory" + long_info = "Loads moving sprites for person events into ram." size = 1 elif command_byte == 0x4A: #Load byte to C1CE [xx] + info = "Load specific byte to $C1CE [xx]" + long_info = """ #Loads a byte to C1CE. Seems to have no function in the game. #[4A][Byte] + """ + size = 2 command["byte"] = ord(rom[start_address+1]) elif command_byte == 0x4B: #Display text [3b] + info = "Display text by pointer [bb][xxyy]" + long_info = """ #Opens a text box and writes text. Doesn't load font. #[4B][Text bank][2byte text pointer] + """ size = 4 command["text_group"] = ord(rom[start_address+1]) command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=True) elif command_byte == 0x4C: #Display text [2b] + info = "Display text by pointer [xxyy]" + long_info = """ #Opens a text box and writes text. Doesn't load font. #[4C][2byte text pointer] + """ size = 3 command["pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x4D: #Repeat text [xxyy] + info = "Repeat text [FF][FF]" + long_info = """ #Opens a text box and writes the text written latest resp. whose address was put statically to D175-D177. #Doesn't load font. #[4D][FF][FF] #Without FF for both bytes, no operation occurs + """ size = 3 command["bytes"] = rom_interval(start_address+1, 2, strings=False) elif command_byte == 0x4E: #YES/No box + info = "YES/No box" + long_info = """ #Displays a YES/NO box at X0F/Y07 #feedback: # 00 = no # 01 = yes + """ size = 1 elif command_byte == 0x4F: #Menu data code [2b] + info = "Load menu data by pointer [xxyy]" + long_info = """ #Loads data for menus #[4F][2byte pointer to menu data] #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDatA4F + """ size = 3 command["menu_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x50: #Write backup code - #Writes backup of parts of the screen the box was overlapping. + info = "Write screen backup" + long_info = "Writes backup of parts of the screen the box was overlapping." size = 1 elif command_byte == 0x51: #Text1 code [2b] + info = "Display text (by pointer), turn to HIRO, end [xxyy]" + long_info = """ #Displays a text and lets person turn to HIRO. #Afterwards there is no other script interpreted. #Corresponds to 6A + 47 + 4C + 53 + 49 + 90 #[51][2byte textpointer] + """ size = 3 + end = True command["text_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x52: #Text2 code [2b] + info = "Display text (by pointer) and end [xxyy]" + long_info = """ #Displays a text. Afterwards there is no other script interpreted. #Corresponds to 47 + 4C + 53 + 49 + 90 #[52][2byte textpointer] + """ size = 3 + end = True command["text_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x53: #Close text box code - #Closes a text box which was opened by 47 resp. 4B/4C/4D. + info = "Close text box" + long_info = "Closes a text box which was opened by 47 resp. 4B/4C/4D." size = 1 elif command_byte == 0x54: #Keep text box open code - #Keeps a text box open which was opened by 47 resp. 4B/4C/4D. + info = "Keep text box open" + long_info = "Keeps a text box open which was opened by 47 resp. 4B/4C/4D." size = 1 elif command_byte == 0x55: #Pokémon picture code [xx] + info = "Display a pokemon picture in a box by pokemon id [xx]" + long_info = """ #Opens a box and puts a Pokémon picture into it. #[55][xx] #xx: # <>00 : Pokémon no # =00 : Pokémon no gets read from RAM + """ size = 2 command["byte"] = ord(rom[start_address+1]) elif command_byte == 0x56: #Pokémon picture YES/NO code + info = "?? Display a pokemon picture and a yes/no box" + long_info = """ #Displays a YES/NO box at X08/Y05. #feedback: # 00 = no chosen # 01 = yes chosen + """ size = 1 elif command_byte == 0x57: #Menu interpreter 1 + info = "Menu interpreter 1 (see menu loader)" + long_info = """ #Interprets menu data loaded by 4F. #see also http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDatA57 + """ size = 1 elif command_byte == 0x58: #Menu interpreter 2 + info = "Menu interpreter 2 (see menu loader)" + long_info = """ #Interprets menu data loaded by 4F. #see also http://hax.iimarck.us/files/scriptingcodes_eng.htm#Marke57 #see also http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDatA58 + """ size = 1 elif command_byte == 0x59: #Load Pikachu data - #Loads 0x19 (Pikachu) to PokéRAM and level 5 to LevelRAM. + info = "Load pikachu data" + long_info = "Loads 0x19 (Pikachu) to PokéRAM and level 5 to LevelRAM." size = 1 elif command_byte == 0x5A: #Delete FightRAM/reset person check + info = "? Disable fleeing from battle" + long_info = """ #Deletes the value in BattleRAM. #Turns off the check if the battle was started by entering #a trainer's area of view. + """ size = 1 elif command_byte == 0x5B: #Load trainer data1 + info = "Load trainer from RAM" + long_info = """ #Loads trainer data when HIRO is in a trainer's range of sight. #Trainer group is read from CF2E and written to #TrRAM1, the trainer number is read from CF2F and written to #TrRAM2. 81 is written to BattleRAM. + """ size = 1 elif command_byte == 0x5C: #Load Pokémon data [xxyy] + info = "Loads pokemon by id and level for BattleRAM [xx][yy]" + long_info = """ #Loads Pokémon data. Writes 80 to BattleRAM. #[5C][Poke no][Level] + """ size = 3 command["pokemon_id"] = ord(rom[start_address+1]) command["pokemon_level"] = ord(rom[start_address+2]) elif command_byte == 0x5D: #Load trainer data2 [xxyy] + info = "Load trainer by group/id for BattleRAM [xx][yy]" + long_info = """ #Loads trainer data. Trainer group --> TrRAM1, #trainer number --> TrRAM2. Writes 81 to BattleRAM. #[5D][Trainer group][Trainer no] + """ size = 3 command["trainer_group"] = ord(rom[start_address+1]) command["trainer_id"] = ord(rom[start_address+2]) elif command_byte == 0x5E: #Start battle + info = "Start pre-configured battle" + long_info = """ #Starts trainer or Pokémon battle. BattleRAM: 80 = Poké battle; 81 = Trainer battle. #feedback: # 00 = win # 01 = lose + """ size = 1 elif command_byte == 0x5F: #Return to In game engine after battle - #Returns to ingame engine and evaluates battle. When lost then return to last Pokémon Center etc. + info = "Return to in-game engine after battle" + long_info = "Returns to ingame engine and evaluates battle. When lost then return to last Pokémon Center etc." size = 1 elif command_byte == 0x60: #Learn how to catch PKMN [xx] + info = "Pokemon catching tutorial [xx]" + long_info = """ #Starts a learn-how-to-catch battle with a Pokémon, whose data needs to be loaded beforehand #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#Marke5C #Player has to have at least 1 Pokémon for it to work. @@ -1085,16 +1398,23 @@ def parse_script_at(address): #[60][xx] #xx: Between 01 and 03. If <> 03 then HIRO sprite instead of dude sprite and kills #itself when using the item system. + """ size = 2 command["byte"] = ord(rom[start_address+1]) elif command_byte == 0x61: #Trainer text code - #Interprets the data of a in the event structure defined trainer. Xx decides which text to use. - #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#Eventaufbau + info = "Set trainer text by id [xx]" + long_info = """ + #Interprets the data of a in the event structure defined trainer. #[61][xx] + #Xx decides which text to use. #xx: Between 00 and 03. + #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#Eventaufbau + """ size = 2 command["byte"] = ord(rom[start_address+1]) elif command_byte == 0x62: #Trainer status code [xx] + info = "? Check trainer status [xx]" + long_info = """ #Checks/changes the status of a in the event structure defined trainer. #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#Eventaufbau #[62][xx] @@ -1102,357 +1422,522 @@ def parse_script_at(address): # 00 = deactivate # 01 = activate # 02 = check + """ size = 2 command["byte"] = ord(rom[start_address+1]) - elif command_byte == 0x63: #Pointer Win/Loss [2b + 2b] - #Writes the win/loss pointer of a battle into the ram. + elif command_byte == 0x63: #Pointer Win/Lose [2b + 2b] + info = "Set win/lose pointers for battle [xxyy][xxyy]" + long_info = """ + #Writes the win/lose pointer of a battle into the ram. #[63][2byte pointer to text Win][2byte pointer to text Loss*] #* When pointer = 0000 then "Blackout" instead of return to gameplay. + """ size = 5 command["won_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) command["lost_pointer"] = calculate_pointer_from_bytes_at(start_address+3, bank=False) elif command_byte == 0x64: #Script talk-after + #XXX this is a really poor description of whatever this is + info = "? Load the trainer talk-after script" + long_info = """ #Interprets which script is going to be run, when a in the event-structure-defined #trainer is talked to again. #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#Eventaufbau #[64] + """ size = 1 elif command_byte == 0x65: #Script talk-after-cancel + info = "Disable/cancel trainer after-battle text" + long_info = """ #Cancels the talk-after script of the in the event-structure-defined #trainer when talk-after script is executed just after the battle. #[65] + """ size = 1 elif command_byte == 0x66: #Script talk-after-check + #XXX also bad explanation/name... + info = "? Check if trainer talk-after script is executed just after battle or not" + long_info = """ #Checks if the talk-after script of the event structure defined trainer #is executed just after the battle or at a later point in time. #feedback: # 00 = no # 01 = yes #[66] + """ size = 1 elif command_byte == 0x67: #Set talked-to person [xx] + info = "Set last talked-to person [xx]" + long_info = """ #Sets the number of the last person talked to. #[67][person] + """ size = 2 command["person_id"] = ord(rom[start_address+1]) elif command_byte == 0x68: #Moving code [xx + 2b] + info = "Move person (by id) with moving data (by pointer) [id][xxyy]" + long_info = """ #Moves the person using moving data. #[68][Person][2byte pointer to moving data] #see also http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzB68bis69 + """ size = 4 command["person_id"] = ord(rom[start_address+1]) command["moving_data_pointer"] = calculate_pointer_from_bytes_at(start_address+2, bank=False) elif command_byte == 0x69: #Moving code for talked-to person [2b] + info = "Move talked-to person with moving data (by pointer) [xxyy]" + long_info = """ #Moves talked-to person using moving data. #[69][2byte pointer to moving data] + """ size = 3 command["moving_data_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x6A: #Talk-to facing code + info = "Move talked-to person's facing direction to HIRO" + long_info = """ #Turns the heads of the talked-to persons to HIRO. #[6A] + """ size = 1 elif command_byte == 0x6B: #Facing of people code [xxyy] + info = "Move facing direction of person1 to look at person2 [2][1]" + long_info = """ #Turns the head of person1 to another person2. #[6B][Person2][Person1] #Person2 = If number is greater than 0xFD, then use number of talked-to person. #Person1 = If number equals 0xFE, then take number of talked-to person. + """ size = 3 command["person2_id"] = ord(rom[start_address+1]) command["person1_id"] = ord(rom[start_address+2]) elif command_byte == 0x6C: #Variable sprites [xxyy] + info = "Store value in variable sprite RAM location x by id Y [xx][yy]" + long_info = """ #Writes a number to the variable sprite RAM from D555 to D564 (see Compendium on the sprite system). #[6C][xx][Sprite no] #xx: Number between 0x00 and 0x0F + """ size = 3 command["number"] = ord(rom[start_address+1]) command["sprite_id"] = ord(rom[start_address+2]) elif command_byte == 0x6D: #Hide person [xx] + info = "Hide person by id [xx]" + long_info = """ #Hides a person. #[6D][person id] + """ size = 2 command["person_id"] = ord(rom[start_address+1]) elif command_byte == 0x6E: #Show person [xx] + info = "Show person by id [xx]" + long_info = """ #Shows a hidden person again. #[6E][person id] + """ size = 2 command["person_id"] = ord(rom[start_address+1]) elif command_byte == 0x6F: #Following code1 [xxyy] + info = "Following code1 [leader id][follower id]" + long_info = """ #A person1 follows another person2. The person1 that follows #just repeats the movement of person2, even if the persons are #not directly next to each other. #[6F][Leader Person2][Follower Person1] + """ size = 3 command["leader_person_id"] = ord(rom[start_address+1]) command["follower_person_id"] = ord(rom[start_address+2]) elif command_byte == 0x70: #Stop following code - #Ends all current follow codes. + info = "Stop all follow code" + long_info = "Ends all current follow codes." size = 1 elif command_byte == 0x71: #Move person [xxyyzz] + info = "Move person by id to xy [id][xx][yy]" + long_info = """ #Sets the X/Y values of a person anew. #The person doesn't get shown immediately. Use hide&show. #[71][Person][X][Y] + """ size = 4 command["person_id"] = ord(rom[start_address+1]) command["x"] = ord(rom[start_address+2]) command["y"] = ord(rom[start_address+3]) elif command_byte == 0x72: #Write person location [xx] (lock person location?) + info = "Lock person's location by id [id]" + long_info = """ #Writes the current X/Y values of a person into the ram. #The person is going to stand at its current location even when #it's out of HIRO's sight and is not going to return to its old #location until the next map load. #[72][person] + """ size = 2 command["person_id"] = ord(rom[start_address+1]) elif command_byte == 0x73: #Load emoticons [xx] + info = "Load emoticon bubble [xx]" + long_info = """ #Loads the emoticon bubble depending on the given bubble number. #[73][bubble number] #xx: If xx = FF then take number from RAM. - #00 = Exclamation mark - #01 = Question mark - #02 = Happy - #03 = Sad - #04 = Heart - #05 = Flash - #06 = Snoring - #07 = Fish + # 00 = Exclamation mark + # 01 = Question mark + # 02 = Happy + # 03 = Sad + # 04 = Heart + # 05 = Flash + # 06 = Snoring + # 07 = Fish + """ size = 2 command["bubble_number"] = ord(rom[start_address+1]) elif command_byte == 0x74: #Display emoticon [xxyyzz] + info = "Display emoticon by bubble id and person id and time [xx][yy][zz]" + long_info = """ #Displays the bubble above a persons head for the given time period. #Attention: Bubbles get loaded into ram! #[74][Bubble][Person][Time] #for bubble ids see 0x73 + """ size = 4 command["bubble_number"] = ord(rom[start_address+1]) command["person_id"] = ord(rom[start_address+2]) command["time"] = ord(rom[start_address+3]) elif command_byte == 0x75: #Change facing [xxyy] + info = "Set facing direction of person [person][facing]" + long_info = """ #Changes the facing direction of a person. #[75][person][facing] + """ size = 3 command["person_id"] = ord(rom[start_address+1]) command["facing"] = ord(rom[start_address+2]) elif command_byte == 0x76: #Following code2 [xxyy] + info = "Following code2 [leader id][follower id]" + long_info = """ #A person1 follows a person2. The following person1 automatically clings to person2. #Person1 just follows person2, but doesn't make the exact same movements at person2. #[76][Leader Person2][Follower Person1] + """ size = 3 command["leader_person_id"] = ord(rom[start_address+1]) command["follower_person_id"] = ord(rom[start_address+2]) elif command_byte == 0x77: #Earth quake [xx] + info = "Earthquake [xx]" + long_info = """ #The screen shakes. xx gives time as well as displacement of the screen. #[77][xx] + """ size = 2 command["shake_byte"] = ord(rom[start_address+1]) elif command_byte == 0x78: #Exchange map [3b] + info = "Draw map data over current map [bank][pointer]" + long_info = """ #This code draws another whole map as wide and high as the #current map over the current map. #The 3byte pointer points to the new map. #[78][3byte pointer to new map data] + """ size = 4 command["map_data_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=True) elif command_byte == 0x79: #Change block code [xxyyzz] + info = "Change block to block id on map [xx][yy][id]" + long_info = """ #Changes a block on the current map by giving the new block #number and its X/Y values measured in half-blocks. #[79][X][Y][Block] + """ size = 4 command["x"] = ord(rom[start_address+1]) command["y"] = ord(rom[start_address+2]) command["block"] = ord(rom[start_address+3]) elif command_byte == 0x7A: #Reload map code + info = "Reload/redisplay map" + long_info = """ #Reloads and re-displays the map completely. #Loads tileset and all map data anew. Screen gets light. #[7A] + """ size = 1 elif command_byte == 0x7B: #Reload map part code + info = "Reload/redisplay map portion occupied by HIRO" + long_info = """ #Reloads and re-displays the part of the map HIRO is on, #without reloading any other map data or the tileset. #[7B] + """ size = 1 elif command_byte == 0x7C: #Write command queue + info = "Write command queue [xxyy]" + long_info = """ #Writes a command queue to the next free slot in ram. #Max 4 command queues à 5 bytes. This code is buggy (bug fix: 25:7C74 --> 12). #[7C][2byte pointer to 5byte command queue] #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDok25_7CC9 + """ size = 3 command["command_queue_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x7D: #Delete command queue + info = "Delete command queue" + long_info = """ #Deletes a command queue and frees a slot in ram. #[7D][First command of the resp. queue] + """ #XXX wtf? size = 2 command["first_command"] = ord(rom[start_address+1]) elif command_byte == 0x7E: #Song code1 [xxyy] + info = "Play music by number [xxyy]" + long_info = """ #Immediately plays the music. #[7E][Music no (2byte)] #Music no: See the music archive that should accompany #this document Thanks to Filb. He dumped all the songs #via gameboy player and gave them to me. + """ size = 3 #XXX what is the format of this music data? command["music_number"] = rom_interval(start_address+1, size-1, strings=False) elif command_byte == 0x7F: #Song code2 + info = "Song code2" + long_info = """ #Plays the music of the trainer group in TrRAM1. #Takes music numbers from list at 3A:5027. #[7F] + """ size = 1 elif command_byte == 0x80: #Music fade-out code [xxyy][zz] + info = "Music fade-out then play next [xxyy][time]" + long_info = """ #The current music is faded out and the new music is played afterwards. #[80][Music no (2byte)][Time to fade out (00-7F)] + """ size = 4 command["music_number"] = rom_interval(start_address+1, 2, strings=False) command["fade_time"] = ord(rom[start_address+3]) elif command_byte == 0x81: #Play map music code + info = "Play map's music" + long_info = """ #Starts playing the original map music. #Includes special check for surfer and bug contest song. #[81] + """ size = 1 elif command_byte == 0x82: #Map reload music code + info = "Reload map music" + long_info = """ #After a map reload no music is played. #[82] + """ size = 1 elif command_byte == 0x83: #Cry code [xx00] + info = "Play cry by id or RAM [cry][00]" + long_info = """ #Plays the Pokémon's cry. #[83][Cry no][00] #If the cry no = 00 then the number is taken from RAM. + """ size = 3 command["cry_number"] = ord(rom[start_address+1]) command["other_byte"] = ord(rom[start_address+2]) elif command_byte == 0x84: #Sound code [xxyy] + info = "Play sound by sound number [xxyy]" + long_info = """ #Plays the sound. #[84][Sound no (2byte)] #Sound no: See the music archive that should accompany this document #Thanks to philb for this matter. He helped me to record a big #part of these sounds. + """ size = 3 command["sound_number"] = rom_interval(start_address+1, 2, strings=False) elif command_byte == 0x85: #Key-down code + info = "Wait for key-down" + long_info = """ #Waits for the Player to press a button. #[85] + """ size = 1 elif command_byte == 0x86: #Warp sound + info = "Warp sound" + long_info = """ #Evaluates which sound is played when HIRO enters a Warp field. #Usage via script ingame is rather not useful. #[86] + """ size = 1 elif command_byte == 0x87: #Special sound + info = "Special sound if TM was last checked" + long_info = """ #When last given/checked Item was a TM then it plays sound 0x9B. If not, then 0x01. #[87] + """ size = 1 elif command_byte == 0x88: #Engine remote control [2b] + info = "Engine remote control [bb][xxyy]" + long_info = """ #This code controls the engine via "data stream". #[88][3byte pointer to control structure] #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDatA88 + """ size = 4 command["data_stream_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=True) elif command_byte == 0x89: #Load map anew [xx] + info = "Load map with specific loading process [xx]" + long_info = """ #The number decides which map loading process is used. #The number must be 0xF0 + process number to work correctly. #[89][Number] #see map loading process: # http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDok5_5550 + """ size = 2 command["number"] = ord(rom[start_address+1]) elif command_byte == 0x8A: #Waiting code [xx] + info = "Wait code" + long_info = """ #This code lets the game wait for 2 * xx time intervals. #[8A][xx] #xx: Numbers from 0x01 to 0xFF. #If 0x00 is chosen then the time can be manipulated by previously loading a number to RAM2. + """ size = 2 command["time"] = ord(rom[start_address+1]) elif command_byte == 0x8B: #Deactivate static facing [xx] + info = "Deactive static facing after time [xx]" + long_info = """ #Deactivates static facings on all persons on the screen after a time xx. #[8B][xx] + """ size = 2 command["time"] = ord(rom[start_address+1]) elif command_byte == 0x8C: #Priority jump1 [2b] + info = "Priority jump to script by pointer [xxyy]" + long_info = """ #The pointer acts like code 00, but with this higher #functions like the bike etc. are not paid attention to, #while the script is running. #[8C][2byte pointer to script] + """ size = 3 command["script_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x8D: #Warp check + info = "Reactive all engine checks if player is warping" + long_info = """ #If HIRO is entering or leaving a warp then this code reactivates all the engine-checks. #[8D] + """ size = 1 elif command_byte == 0x8E: #Priority jump2 [2b] + info = "Priority jump to script by pointer (after 1st cycle) [xxyy]" + long_info = """ #The pointer acts like code 03, but with this code all #higher functions wait for a cycle before the script gets interpreted. #[8E][2byte pointer to script] + """ size = 3 command["script_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x8F: #Return code1 + info = "Return code 1" + long_info = """ #Ends the current script and loads the backup offset for "linked" #scripts if applicable. The sophisticated functions are not affected #and run like before the code. This code is mostly used for scripts #called by the 2nd part of the script header, because else malfunctions #occur. #[8F] + """ size = 1 end = True elif command_byte == 0x90: #Return code2 + info = "Return code 2" + long_info = """ #Ends the current script and loads the backup offset for "linked" #scripts if applicable. The sophisticated functions get reset if #no backup offset was loaded. This code is used to end most scripts. #[90] + """ size = 1 end = True elif command_byte == 0x91: #Return code3 + info = "Return code 3" + long_info = """ #Reloads the map completely like the code 0x7A #and else acts completely like Return code2 #[91] #see reload map code # http://hax.iimarck.us/files/scriptingcodes_eng.htm#Marke7A #see 0x90 + """ size = 1 #XXX does this end the script?? "else acts like 0x90" # else? what's the "if"? end = True elif command_byte == 0x92: #Reset sophisticated functions + info = "Reset sophisticated functions" + long_info = """ #Resets all sophisticated functions to 0. #[92] + """ size = 1 elif command_byte == 0x93: #Mart menu [xxyyzz] + info = "Mart menu [dialog no][mart no 2b]" + long_info = """ #Displays a whole mart menu, however, doesn't load font to ram. #[93][Dialog no][Mart no (2byte)] #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#AwBsp93 #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzB93 + """ size = 4 command["dialog_number"] = ord(rom[start_address+1]) command["mart_number"] = rom_interval(start_address+2, 2, strings=False) elif command_byte == 0x94: #Elevator menu [2b] + info = "Display elevator menu by pointer [xxyy]" + long_info = """ #Displays a whole elevator menu, but it doesn't load font to ram. #Only works with warps with warp-to = 0xFF. #[94][2byte pointer to floor list] + """ size = 3 command["floor_list_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x95: #Trade menu [xx] + info = "Display trade menu by trade id [xx]" + long_info = """ #Displays a whole trade menu, but it doesn't load font to ram. #[95][trade no] #see http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDokTausch + """ size = 2 command["trade_number"] = ord(rom[start_address+1]) elif command_byte == 0x96: #Give cell phone number with YES/NO [xx] + info = "Give cell phone number by id with YES/NO [id]" + long_info = """ #Gives a telephone number but asks for decision beforehand. #feedback: # 00 = ok chosen # 01 = Cell phone number already registered/Memory full # 02 = no chosen #[96][Cell phone number] + """ size = 2 #maybe this next param should be called "phone_number" command["number"] = ord(rom[start_address+1]) elif command_byte == 0x97: #Call code [2b] + info = "Call code pointing to name of caller [xxyy]" + long_info = """ #Displays the upper cell phone box and displays a freely selectable name. #[97][2byte pointer to name of caller] + """ size = 3 command["caller_name_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank=False) elif command_byte == 0x98: #Hang-up code + info = "Hang-up phone" + long_info = """ #Simulates the hanging-up. #[98] + """ size = 1 elif command_byte == 0x99: #Decoration code [xx] + info = "Set monologue decoration [xx]" + long_info = """ #Displays monologues according to the selected ornament. #[99][xx] #xx values: @@ -1461,18 +1946,24 @@ def parse_script_at(address): # 02 = Ornament right # 03 = Huge doll # 04 = Console + """ size = 2 command["ornament"] = ord(rom[start_address+1]) elif command_byte == 0x9A: #Berry tree code [xx] + info = "Berry tree by tree id [xx]" + long_info = """ #Creates a typical berry tree monologue. #There is a maximum of 32 berry trees in the game. #After this code the script ends. #[9A][Fruit tree number] #Fruit tree number + 11:4091 is the offset where the item no of the berry is defined. + """ size = 2 end = True command["tree_id"] = ord(rom[start_address+1]) elif command_byte == 0x9B: #Cell phone call code [xx00] + info = "Cell phone call [call id][00]" + long_info = """ #Initiates with the next step on a outer world map (permission byte) a phone call. #[9B][Call no][00] #call no: @@ -1484,61 +1975,92 @@ def parse_script_at(address): # 06 = Bike shop gives bike away # 07 = Mother is unhappy that HIRO didn't talk to her before leaving # 08 = PROF. ELM has got something for HIRO a second time + """ size = 3 command["call_id"] = ord(rom[start_address+1]) command["byte"] = ord(rom[start_address+2]) elif command_byte == 0x9C: #Check cell phone call code + info = "Check if/which a phone call is active" + long_info = """ #Checks if a phone call is "in the line". #feedback: # 00 = no # <>00 = call number #[9C] + """ size = 1 elif command_byte == 0x9D: #Commented give item code [xxyy] + info = "Give item by id and quantity with 'put in pocket' text [id][qty]" + long_info = """ #The same as 0x1F but this code comments where #HIRO puts what item in a short monologue. #[9D][Item][Amount] + """ size = 3 command["item_id"] = ord(rom[start_address+1]) - command["quantity"] = ord[rom[start_address+2]) + command["quantity"] = ord(rom[start_address+2]) elif command_byte == 0x9E: #Load special wild PKMN data [xxyy] + info = "Load wild pokemon data for a remote map [map group][map id]" + long_info = """ #Activates the checks in the special tables for the wild pokémon data. - #[9E][Mapbank][Map no] + #[9E][map group][map id] #see also http://hax.iimarck.us/files/scriptingcodes_eng.htm#ZusatzDok3E_66ED + """ size = 3 command["map_group"] = ord(rom[start_address+1]) command["map_id"] = ord(rom[start_address+2]) elif command_byte == 0x9F: #Hall of Fame code + info = "Hall of Fame" + long_info = """ #Saves and enters HIRO's complete Team in the Hall of Fame. #Shows the credits and restarts the game with HIRO located in New Bark Town. #[9F] + """ size = 1 elif command_byte == 0xA0: #Credits code + info = "Credits" + long_info = """ #Shows the credits and HIRO is located on the Silver mountain plateau. #[A0] + """ size = 1 elif command_byte == 0xA1: #Facing warp + info = "Warp-to and set facing direction [Facing (00-03)][Map bank][Map no][X][Y]" + long_info = """ #Acts like code 0x3C but defines the desired facing of HIRO. #[A1][Facing (00-03)][Map bank][Map no][X][Y] + """ size = 6 command["facing"] = ord(rom[start_address+1]) command["map_group"] = ord(rom[start_address+2]) - command["map_id"] = ord(rom[start_addresss+3]) + command["map_id"] = ord(rom[start_address+3]) command["x"] = ord(rom[start_address+4]) command["y"] = ord(rom[start_address+5]) elif command_byte == 0xA2: #MEMORY code [2b + Bank + xx] + info = "Set memX to a string by a pointer [aabb][bank][xx]" + long_info = """ #MEMORY1, 2 or 3 can directly be filled with a string from #a different rom bank. #[A2][2byte pointer][Bank][00-02 MEMORY] + """ size = 5 command["string_pointer"] = calculate_pointer_from_bytes_at(start_address+1, bank="reversed") command["string_pointer_bank"] = ord(rom[start_address+3]) command["memory_id"] = ord(rom[start_address+4]) elif command_byte == 0xA3: #Display any location name [xx] + info = "Copy the name of a location (by id) to TEMPMEMORY1" + long_info = """ #By the location number the name of that location is written to TEMPMEMORY1. #[A3][Location no] + """ size = 2 command["location_number"] = ord(rom[start_address+1]) + else: + end = True + #raise NotImplementedError, "command byte is " + hex(command_byte) + " at " + hex(offset) + " on map " + str(map_group) + "." + str(map_id) + long_info = clean_up_long_info(long_info) + print command_debug_information(command_byte=command_byte, map_group=map_group, map_id=map_id, address=offset, info=info, long_info=long_info) + #store the size of the command command["size"] = size #the end address is just offset + size - 1 (because size includes command byte) @@ -1569,7 +2091,7 @@ def parse_warp_bytes(some_bytes): "map_id": map_id, }) return warps -def parse_xy_trigger_bytes(some_bytes, bank=None): +def parse_xy_trigger_bytes(some_bytes, bank=None, map_group=None, map_id=None): """parse some number of triggers from the data""" assert len(some_bytes) % trigger_byte_size == 0, "wrong number of bytes" triggers = [] @@ -1585,7 +2107,8 @@ def parse_xy_trigger_bytes(some_bytes, bank=None): script = None if bank: script_address = calculate_pointer(script_ptr, bank) - script = parse_script_at(script_address) + print "******* parsing xy trigger byte scripts... x=" + str(x) + " y=" + str(y) + script = parse_script_engine_script_at(script_address, map_group=map_group, map_id=map_id) triggers.append({ "trigger_number": trigger_number, @@ -1598,7 +2121,7 @@ def parse_xy_trigger_bytes(some_bytes, bank=None): "script": script, }) return triggers -def parse_signpost_bytes(some_bytes, bank=None): +def parse_signpost_bytes(some_bytes, bank=None, map_group=None, map_id=None): """parse some number of signposts from the data [Y position][X position][Function][Script pointer (2byte)] @@ -1635,8 +2158,9 @@ def parse_signpost_bytes(some_bytes, bank=None): script_address = None script = None if bank: + print "******* parsing signpost script.. signpost is at: x=" + str(x) + " y=" + str(y) script_address = calculate_pointer(script_pointer, bank) - script = parse_script_at(script) + script = parse_script_engine_script_at(script_address, map_group=map_group, map_id=map_id) signposts.append({ "y": y, "x": x, @@ -1647,7 +2171,7 @@ def parse_signpost_bytes(some_bytes, bank=None): "script": script, }) return signposts -def parse_people_event_bytes(some_bytes, address=None): #max of 14 people per map? +def parse_people_event_bytes(some_bytes, address=None, map_group=None, map_id=None): #max of 14 people per map? """parse some number of people-events from the data see http://hax.iimarck.us/files/scriptingcodes_eng.htm#Scripthdr @@ -1686,8 +2210,9 @@ def parse_people_event_bytes(some_bytes, address=None): #max of 14 people per ma script_address = None script = None if bank: + print "parsing a person-script at x=" + str(x) + " y=" + str(y) script_address = calculate_pointer(script_pointer, bank) - script = parse_script_at(script_address) + script = parse_script_engine_script_at(script_address, map_group=map_group, map_id=map_id) #take the script pointer @@ -1752,7 +2277,7 @@ class PeopleEvent(MapEventElement): standard_size = people_event_byte_size parse_func = parse_people_event_bytes -def parse_map_header_at(address): +def parse_map_header_at(address, map_group=None, map_id=None): """parses an arbitrary map header at some address""" bytes = rom_interval(address, map_header_byte_size, strings=False) bank = bytes[0] @@ -1776,12 +2301,12 @@ def parse_map_header_at(address): "fishing": fishing_group, } map_header.update(parse_second_map_header_at(second_map_header_address)) - map_header.update(parse_map_event_header_at(map_header["event_address"])) + map_header.update(parse_map_event_header_at(map_header["event_address"], map_group=map_group, map_id=map_id)) #maybe this next one should be under the "scripts" key? - map_header.update(parse_map_script_header_at(map_header["script_address"])) + map_header.update(parse_map_script_header_at(map_header["script_address"], map_group=map_group, map_id=map_id)) return map_header -def parse_second_map_header_at(address): +def parse_second_map_header_at(address, map_group=None, map_id=None): """each map has a second map header""" bytes = rom_interval(address, second_map_header_byte_size, strings=False) border_block = bytes[0] @@ -1813,7 +2338,7 @@ def parse_second_map_header_at(address): "connections": connections, } -def parse_map_event_header_at(address): +def parse_map_event_header_at(address, map_group=None, map_id=None): """parse crystal map event header byte structure thing""" returnable = {} @@ -1836,24 +2361,27 @@ def parse_map_event_header_at(address): trigger_byte_count = trigger_byte_size * trigger_count triggers = rom_interval(after_warps+1, trigger_byte_count) after_triggers = after_warps + 1 + trigger_byte_count - returnable.update({"xy_trigger_count": trigger_count, "xy_triggers": parse_xy_trigger_bytes(triggers, bank=bank)}) + returnable.update({"xy_trigger_count": trigger_count, "xy_triggers": parse_xy_trigger_bytes(triggers, bank=bank, map_group=map_group, map_id=map_id)}) #signposts signpost_count = ord(rom[after_triggers]) signpost_byte_count = signpost_byte_size * signpost_count signposts = rom_interval(after_triggers+1, signpost_byte_count) after_signposts = after_triggers + 1 + signpost_byte_count - returnable.update({"signpost_count": signpost_count, "signposts": parse_signpost_bytes(signposts, bank=bank)}) - + returnable.update({"signpost_count": signpost_count, "signposts": parse_signpost_bytes(signposts, bank=bank, map_group=map_group, map_id=map_id)}) + + print "skipping event data... (oops)" + #raise NotImplementedError, "holy mother of god" + #XXX parse trainer data too.. this changes the structure of people events... #people events - people_event_count = ord(rom[after_signposts]) - people_event_byte_count = people_event_byte_size * people_event_count - people_events = rom_interval(after_signposts+1, people_event_byte_count) - returnable.update({"people_event_count": people_event_count, "people_events": parse_people_event_bytes(people_events, address=after_signposts+1)}) + #people_event_count = ord(rom[after_signposts]) + #people_event_byte_count = people_event_byte_size * people_event_count + #people_events = rom_interval(after_signposts+1, people_event_byte_count) + #returnable.update({"people_event_count": people_event_count, "people_events": parse_people_event_bytes(people_events, address=after_signposts+1, map_group=map_group, map_id=map_id)}) return returnable -def parse_map_script_header_at(address): +def parse_map_script_header_at(address, map_group=None, map_id=None): """parses a script header This structure allows the game to have e.g. one-time only events on a map @@ -1915,7 +2443,7 @@ def parse_map_script_header_at(address): byte2 = trigger_pointer[1] ptr = byte1 + (byte2 << 8) trigger_address = calculate_pointer(ptr, calculate_bank(address)) - trigger_script = parse_script_at(trigger_address) + trigger_script = parse_script_engine_script_at(trigger_address, map_group=map_group, map_id=map_id) triggers[index] = { "script": trigger_script, "address": trigger_address, @@ -1937,7 +2465,7 @@ def parse_map_script_header_at(address): callback_byte2 = callback_line[2] callback_ptr = callback_byte1 + (callback_byte2 << 8) callback_address = calculate_pointer(callback_ptr, calculate_bank(address)) - callback_script = parse_script_at(callback_address) + callback_script = parse_script_engine_script_at(callback_address) callback_pointers[len(callback_pointers.keys())] = [hook_byte, callback_ptr] callbacks[index] = { "script": callback_script, @@ -1968,7 +2496,7 @@ def parse_all_map_headers(): for map_id, map_data in group_data.items(): map_header_offset = offset + ((map_id - 1) * map_header_byte_size) print "map_group is: " + str(group_id) + " map_id is: " + str(map_id) - parsed_map = parse_map_header_at(map_header_offset) + parsed_map = parse_map_header_at(map_header_offset, map_group=group_id, map_id=map_id) map_names[group_id][map_id].update(parsed_map) map_names[group_id][map_id]["header_offset"] = map_header_offset