ref: 42367aebdd5262bcc2752e3a2f3bad521693bb36
parent: 34b4230c54cff950a38c4fa80f99029df20e0778
author: Bryan Bishop <[email protected]>
date: Mon Jan 2 12:34:29 EST 2012
tools/extract_maps.py -> extras/ hg-commit-id: 0c8e34461ceb
--- /dev/null
+++ b/extras/extract_maps.py
@@ -1,0 +1,545 @@
+#!/usr/bin/python
+#author: Bryan Bishop <[email protected]>
+#date: 2012-01-02
+#purpose: compute full map pointers
+import json
+
+#parse hex values as base 16 (see calculate_pointer)
+base = 16
+
+#where to load the rom from
+rom_filename = "baserom.gbc"
+rom = None #load the rom later
+
+#map header pointers start at 0x1AE
+start_map_header_pointers = 0x1AE
+
+#bank bytes for each map header start at 0xC23D
+start_map_header_pointer_banks = 0xC23D
+
+#number of maps in this list
+map_count = 0xF8 #including the 0th the total is is 248 or 0xF8
+
+maps = {
+ 0x00: "Pallet Town",
+ 0x01: "Viridian City",
+ 0x02: "Pewter City",
+ 0x03: "Cerulean City",
+ 0x04: "Vermilion City",
+ 0x05: "Lavender Town",
+ 0x06: "Celadon City",
+ 0x07: "Fuchsia City",
+ 0x08: "Cinnibar Island",
+ 0x09: "Indigo Plateau",
+ 0x0A: "Saffron City",
+ 0x0B: "FREEZE",
+ 0x0C: "Route 1",
+ 0x0D: "Route 2",
+ 0x0E: "Route 3",
+ 0x0F: "Route 4",
+ 0x10: "Route 5",
+ 0x11: "Route 6",
+ 0x12: "Route 7",
+ 0x13: "Route 8",
+ 0x14: "Route 9",
+ 0x15: "Route 10",
+ 0x16: "Route 11",
+ 0x17: "Route 12",
+ 0x18: "Route 13",
+ 0x19: "Route 14",
+ 0x1A: "Route 15",
+ 0x1B: "Route 16",
+ 0x1C: "Route 17",
+ 0x1D: "Route 18",
+ 0x1E: "Route 19",
+ 0x1F: "Route 20",
+ 0x20: "Route 21",
+ 0x21: "Route 22",
+ 0x22: "Route 23",
+ 0x23: "Route 24",
+ 0x24: "Route 25",
+ 0x25: "Ash's House F1",
+ 0x26: "Ash's House F2",
+ 0x27: "Gary's House",
+ 0x28: "Oak's Lab",
+ 0x29: "Viridian Poke Center",
+ 0x2A: "Viridian Mart",
+ 0x2B: "School",
+ 0x2C: "Viridian House",
+ 0x2D: "Viridian Gym",
+ 0x2E: "Digletts Cave (Route 2)",
+ 0x2F: "Viridian Forest (exit)",
+ 0x30: "Route 2 House",
+ 0x31: "Route 2 Gate",
+ 0x32: "Viridian Forest (Entrance)",
+ 0x33: "Viridian Forest",
+ 0x34: "Museum F1",
+ 0x35: "Museum F2",
+ 0x36: "Pewter Gym",
+ 0x37: "Pewter House (1)",
+ 0x38: "Pewter Mart",
+ 0x39: "Pewter House (2)",
+ 0x3A: "Pewter PokeCenter",
+ 0x3B: "Mt. Moon (1)",
+ 0x3C: "Mt. Moon (2)",
+ 0x3D: "Mt. Moon (3)",
+ 0x3E: "Cerulean House (Trashed)",
+ 0x3F: "Cerulean House (2)",
+ 0x40: "Cerulean Pokecenter",
+ 0x41: "Cerulean Gym",
+ 0x42: "Bike Shop",
+ 0x43: "Cerulean Mart",
+ 0x44: "Mt. Moon Pokecenter",
+ 0x45: "COPY OF: Trashed House",
+ 0x46: "Route 5 Gate",
+ 0x47: "Underground Tunnel Entrance (Route 5)",
+ 0x48: "Day Care M",
+ 0x49: "Route 6 Gate",
+ 0x4A: "Underground Tunnel Entrance (Route 6)",
+ 0x4B: "COPY OF: Underground Tunnel Entrance (Route 6)",
+ 0x4C: "Route 7 Gate",
+ 0x4D: "Underground Path Entrance (Route 7)",
+ 0x4E: "COPY OF: Underground Path Entrance (Route 7)",
+ 0x4F: "Route 8 Gate",
+ 0x50: "Underground Path Entrance (Route 8)",
+ 0x51: "Rock Tunnel Pokecenter",
+ 0x52: "Rock Tunnel (1)",
+ 0x53: "Power Plant",
+ 0x54: "Route 11 Gate",
+ 0x55: "Digletts Cave Entrance (Route 11)",
+ 0x56: "Route 11 Gate (Upstairs)",
+ 0x57: "Route 12 Gate",
+ 0x58: "Bill's House",
+ 0x59: "Vermilion PokeCenter",
+ 0x5A: "Fan Club",
+ 0x5B: "Vermilion Mart",
+ 0x5C: "Vermilion Gym",
+ 0x5D: "Vermilion House (1)",
+ 0x5E: "Vermilion Dock",
+ 0x5F: "S.S. Anne (1)",
+ 0x60: "S.S. Anne (2)",
+ 0x61: "S.S. Anne (3)",
+ 0x62: "S.S. Anne (4)",
+ 0x63: "S.S. Anne (5)",
+ 0x64: "S.S. Anne (6)",
+ 0x65: "S.S. Anne (7)",
+ 0x66: "S.S. Anne (8)",
+ 0x67: "S.S. Anne (9)",
+ 0x68: "S.S. Anne (10)",
+ 0x69: "FREEZE",
+ 0x6A: "FREEZE",
+ 0x6B: "FREEZE",
+ 0x6C: "Victory Road (1)",
+ 0x6D: "FREEZE",
+ 0x6E: "FREEZE",
+ 0x6F: "FREEZE",
+ 0x70: "FREEZE",
+ 0x71: "Lance",
+ 0x72: "FREEZE",
+ 0x73: "FREEZE",
+ 0x74: "FREEZE",
+ 0x75: "FREEZE",
+ 0x76: "Hall of Fame Room",
+ 0x77: "Underground Path (N/S)",
+ 0x78: "Gary",
+ 0x79: "Underground Path (W/E)",
+ 0x7A: "Celadon Mart (1)",
+ 0x7B: "Celadon Mart (2)",
+ 0x7C: "Celadon Mart (3)",
+ 0x7D: "Celadon Mart (4)",
+ 0x7E: "Celadon Mart (5)",
+ 0x7F: "Celadon Mart (6)",
+ 0x80: "Celadon Mansion (1)",
+ 0x81: "Celadon Mansion (2)",
+ 0x82: "Celadon Mansion (3)",
+ 0x83: "Celadon Mansion (4)",
+ 0x84: "Celadon Mansion (5)",
+ 0x85: "Celadon Pokecenter",
+ 0x86: "Celadon GYM",
+ 0x87: "Celadon Game Corner",
+ 0x88: "Celadon House",
+ 0x89: "Celadon Prize Room",
+ 0x8A: "Celadon Diner",
+ 0x8B: "Celadon House (2)",
+ 0x8C: "Celadon Hotel",
+ 0x8D: "Lavender Pokecenter",
+ 0x8E: "Pokemon Tower (1)",
+ 0x8F: "Pokemon Tower (2)",
+ 0x90: "Pokemon Tower (3)",
+ 0x91: "Pokemon Tower (4)",
+ 0x92: "Pokemon Tower (5)",
+ 0x93: "Pokemon Tower (6) ",
+ 0x94: "Pokemon Tower (7)",
+ 0x95: "Lavender House (1)",
+ 0x96: "Lavender Mart",
+ 0x97: "Lavender House (2)",
+ 0x98: "Fuchsia Mart",
+ 0x99: "Fuchsia House (1)",
+ 0x9A: "Fuchsia Pokecenter",
+ 0x9B: "Fuchsia House (2)",
+ 0x9C: "Safari Zone Entrance",
+ 0x9D: "Fuchsia GYM",
+ 0x9E: "Fuchsia Meeting Room",
+ 0x9F: "Seafoam Islands (2)",
+ 0xA0: "Seafoam Islands (3)",
+ 0xA1: "Seafoam Islands (4)",
+ 0xA2: "Seafoam Islands (5)",
+ 0xA3: "Vermilion House (2)",
+ 0xA4: "Fuchsia House (3)",
+ 0xA5: "Mansion (1)",
+ 0xA6: "Cinnibar Gym",
+ 0xA7: "Lab (1)",
+ 0xA8: "Lab (2)",
+ 0xA9: "Lab (3)",
+ 0xAA: "Lab (4)",
+ 0xAB: "Cinnibar Pokecenter",
+ 0xAC: "Cinnibar Mart",
+ 0xAD: "COPY: Cinnibar Mart",
+ 0xAE: "Indigo Plateau Lobby",
+ 0xAF: "Copycat's House F1",
+ 0xB0: "Copycat's House F2",
+ 0xB1: "Fighting Dojo",
+ 0xB2: "Saffron Gym",
+ 0xB3: "Saffron House (1)",
+ 0xB4: "Saffron Mart",
+ 0xB5: "Silph Co (1)",
+ 0xB6: "Saffron Pokecenter",
+ 0xB7: "Saffron House (2)",
+ 0xB8: "Route 15 Gate",
+ 0xBA: "Route 16 Gate Map",
+ 0xBB: "Route 16 Gate Upstairs",
+ 0xBC: "Route 16 House",
+ 0xBD: "Route 12 House",
+ 0xBE: "Route 18 Gate",
+ 0xBF: "Route 18 Gate Header",
+ 0xC0: "Seafoam Islands (1)",
+ 0xC1: "Route 22 Gate",
+ 0xC2: "Victory Road (2)",
+ 0xC3: "Route 12 Gate Upstairs",
+ 0xC4: "Vermilion House (3)",
+ 0xC5: "Diglett's Cave",
+ 0xC6: "Victory Road (3)",
+ 0xC7: "Rocket Hideout (1)",
+ 0xC8: "Rocket Hideout (2)",
+ 0xC9: "Rocket Hideout (3)",
+ 0xCA: "Rocket Hideout (4) ",
+ 0xCB: "Rocket Hideout (Elevator)",
+ 0xCC: "FREEZE",
+ 0xCD: "FREEZE",
+ 0xCE: "FREEZE",
+ 0xCF: "Silph Co (2)",
+ 0xD0: "Silph Co (3)",
+ 0xD1: "Silph Co (4)",
+ 0xD2: "Silph Co (5)",
+ 0xD3: "Silph Co (6)",
+ 0xD4: "Silph Co (7)",
+ 0xD5: "Silph Co (8)",
+ 0xD6: "Mansion (2)",
+ 0xD7: "Mansion (3)",
+ 0xD8: "Mansion (4)",
+ 0xD9: "Safari Zone East",
+ 0xDA: "Safari Zone North",
+ 0xDB: "Safari Zone West",
+ 0xDC: "Safari Zone Center",
+ 0xDD: "Safari Zone Rest House (1)",
+ 0xDE: "Safari Zone Secret House",
+ 0xDF: "Safari Zone Rest House (2)",
+ 0xE0: "Safari Zone Rest House (3)",
+ 0xE1: "Safari Zone Rest House (4)",
+ 0xE2: "Unknown Dungeon (2)",
+ 0xE3: "Unknown Dungeon (3)",
+ 0xE4: "Unknown Dungeon (1)",
+ 0xE5: "Name Rater",
+ 0xE6: "Cerulean House (3)",
+ 0xE7: "FREEZE",
+ 0xE8: "Rock Tunnel (2)",
+ 0xE9: "Silph Co (9)",
+ 0xEA: "Silph Co (10)",
+ 0xEB: "Silph Co (11)",
+ 0xEC: "Silph Co (Elevator)",
+ 0xED: "FREEZE",
+ 0xEE: "FREEZE",
+ 0xEF: "Battle Center M",
+ 0xF0: "Trade Center M",
+ 0xF1: "FREEZE",
+ 0xF2: "FREEZE",
+ 0xF3: "FREEZE",
+ 0xF4: "FREEZE",
+ 0xF5: "Loreli",
+ 0xF6: "Bruno",
+ 0xF7: "Agatha"
+ }
+
+map_pointers = {
+ #0x00: {
+ # "name": "Pallet Town",
+ # "address": 0x182a1
+ # },
+ }
+
+map_headers = {
+ #0x00: {
+ # "name": "Pallet Town",
+ # "address": 0x182a1,
+ # "tileset"
+ # "y"
+ # "x"
+ # "map_pointer"
+ # "texts_pointer"
+ # "script_pointer"
+ # "connection_byte"
+ # "num_connections"
+ # "connections":
+ # { "0":
+ # { map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer }
+ # },
+ # "object_data_pointer"
+ # },
+ }
+
+#haters gonna hate
+def load_rom(filename=None):
+ "load the rom into a global (returns True/False)"
+ global rom
+
+ if not filename:
+ filename = rom_filename
+
+ try:
+ rom = open(filename, "rb").read()
+ return True
+ except Exception, exception:
+ print "error loading rom"
+ return False
+
+def assert_rom():
+ global rom
+ assert rom, "rom must be loaded, see load_rom()"
+
+def calculate_pointer(short_pointer, bank):
+ short_pointer = int(short_pointer)
+ bank = int(bank)
+
+ pointer = short_pointer - 0x4000 + (bank * 0x4000)
+
+ #result will be an integer
+ return pointer
+
+def get_nth_map_header_pointer_bank_byte_address(map_id):
+ "returns the address to the bank byte associated with this map pointer"
+ address = start_map_header_pointer_banks + map_id
+ return address
+
+def get_nth_map_header_pointer_bank_byte(map_id):
+ "returns the bank number for this map header"
+ assert_rom()
+
+ address = get_nth_map_header_pointer_bank_byte_address(map_id)
+ bank_byte = ord(rom[address])
+ return bank_byte
+
+def get_nth_map_header_pointer(map_id):
+ "returns the full pointer to the map header struct for this map"
+ assert_rom()
+
+ #figure out where the bytes for this pointer are located
+ byte1_address = start_map_header_pointers + (map_id * 2)
+ byte2_address = start_map_header_pointers + (map_id * 2) + 1
+
+ #grab the two bytes making up the partial pointer
+ byte1 = ord(rom[byte1_address])
+ byte2 = ord(rom[byte2_address])
+
+ #swap the bytes (16-bit pointers for z80 are little endian)
+ temp = byte1
+ byte1 = byte2
+ byte2 = temp
+ del temp
+
+ #combine these into a single pointer (0x byte1 byte2)
+ partial_pointer = (byte2 + (byte1 << 8))
+ #print hex(partial_pointer)
+
+ #get the bank id
+ bank = get_nth_map_header_pointer_bank_byte(map_id)
+
+ #calculate the full pointer
+ pointer = calculate_pointer(partial_pointer, bank)
+
+ #return it as an integer
+ return pointer
+
+def load_map_pointers():
+ global maps
+ global map_pointers
+
+ for map in maps.keys():
+ pointer = get_nth_map_header_pointer(map)
+ #print maps[map] + "\t\t\t" + hex(pointer)
+
+ entry = {
+ "name": maps[map],
+ "address": hex(pointer),
+ "bank": hex(get_nth_map_header_pointer_bank_byte(map))
+ }
+ map_pointers[map] = entry
+
+ #print json.dumps(map_pointers)
+
+def read_connection_bytes(connection_bytes, bank):
+ map_id = ord(connection_bytes[0])
+
+ #connection strip
+ connected_map_tile_pointer_byte1 = ord(connection_bytes[1])
+ connected_map_tile_pointer_byte2 = ord(connection_bytes[2])
+ connected_map_tile_pointer = (connected_map_tile_pointer_byte1 + (connected_map_tile_pointer_byte2 << 8))
+
+ #connection strip
+ current_map_tile_pointer_byte1 = ord(connection_bytes[3])
+ current_map_tile_pointer_byte2 = ord(connection_bytes[4])
+ current_map_tile_pointer = (current_map_tile_pointer_byte1 + (current_map_tile_pointer_byte2 << 8))
+
+ bigness_byte = ord(connection_bytes[5])
+ width_byte = ord(connection_bytes[6])
+ y = ord(connection_bytes[7])
+ x = ord(connection_bytes[8])
+
+ #window
+ window_pointer_byte1 = ord(connection_bytes[9])
+ window_pointer_byte2 = ord(connection_bytes[10])
+ window_pointer = (window_pointer_byte1 + (window_pointer_byte2 << 8))
+
+ connection_data = {
+ "map_id": map_id,
+ "connected_map_tile_pointer": hex(connected_map_tile_pointer),
+ "current_map_tile_pointer": hex(current_map_tile_pointer),
+ "bigness": hex(bigness_byte),
+ "width": hex(width_byte),
+ "y": y,
+ "x": x,
+ "window_pointer": hex(window_pointer),
+ }
+ return connection_data
+
+def read_map_header(address, bank): #bank because i'm lazy
+ address = int(address, base)
+ bank = int(bank, base)
+
+ tileset = ord(rom[address])
+ y = ord(rom[address+1])
+ x = ord(rom[address+2])
+
+ map_pointer_byte1 = ord(rom[address+3])
+ map_pointer_byte2 = ord(rom[address+4])
+ partial_map_pointer = (map_pointer_byte1 + (map_pointer_byte2 << 8))
+ map_pointer = calculate_pointer(partial_map_pointer, bank)
+
+ texts_pointer_byte1 = ord(rom[address+5])
+ texts_pointer_byte2 = ord(rom[address+6])
+ partial_texts_pointer = (texts_pointer_byte1 + (texts_pointer_byte2 << 8))
+ texts_pointer = calculate_pointer(partial_texts_pointer, bank)
+
+ script_pointer_byte1 = ord(rom[address+7])
+ script_pointer_byte2 = ord(rom[address+8])
+ partial_script_pointer = (script_pointer_byte1 + ( script_pointer_byte2 << 8))
+ script_pointer = calculate_pointer(partial_script_pointer, bank)
+
+ connection_byte = ord(rom[address+9]) #0xc = NORTH | SOUTH
+ # <&IIMarckus> the connection byte is a bitmask allowing 0-4 connections
+ # <&IIMarckus> each connection is 11 bytes
+ # <&IIMarckus> or'd
+ # <&IIMarckus> east = 1, west = 2, south = 4, north = 8
+ # <&IIMarckus> so a connection byte of 0xc means north/south
+ # <&IIMarckus> which means there are 22 more bytes, 11 for each connection
+ # < kanzure> 4 | 8 = c?
+ # <&IIMarckus> yes
+ # <&IIMarckus> easier to see if you convert to binary
+ # <&IIMarckus> 0100 | 1000 = 1100
+
+ north, south, west, east = False, False, False, False
+ num_connections = 0
+ connection_value = bin(connection_byte)[2:]
+ if connection_value[0] == "1": #NORTH
+ num_connections += 1
+ north = True
+ if len(connection_value) > 1 and connection_value[1] == "1": #SOUTH
+ num_connections += 1
+ south = True
+ if len(connection_value) > 2 and connection_value[2] == "1": #WEST
+ num_connections += 1
+ west = True
+ if len(connection_value) > 3 and connection_value[3] == "1": #EAST
+ num_connections += 1
+ east = True
+
+ #quick test for connection data
+ #connection0_stuff = rom[(address + 10):(address + 10 + 11)]
+ #print "Route: " + hex(ord(connection0_stuff[0]))
+
+ #setup
+ connections = {}
+
+ #go straight to object data if there are no connections
+ if num_connections > 0:
+ for connection in range(0, num_connections):
+ base_connection_address = address + 10 + (11 * connection)
+ connection_bytes = rom[base_connection_address : base_connection_address + 11]
+ connection_data = read_connection_bytes(connection_bytes, bank)
+
+ connections[connection] = connection_data
+
+ #we might have to jump around a bit
+ offset = address + 10 + (11 * num_connections)
+
+ #object data
+ object_data_pointer_byte1 = ord(rom[offset])
+ object_data_pointer_byte2 = ord(rom[offset+1])
+ partial_object_data_pointer = (object_data_pointer_byte1 + (object_data_pointer_byte2 << 8))
+ object_data_pointer = calculate_pointer(partial_object_data_pointer, bank)
+
+ map_header = {
+ "tileset": hex(tileset),
+ "y": hex(y),
+ "x": hex(x),
+ "map_pointer": hex(map_pointer),
+ "texts_pointer": hex(texts_pointer),
+ "script_pointer": hex(script_pointer),
+ "connection_byte": hex(connection_byte),
+ "num_connections": str(num_connections),
+ "connections": connections, #NORTH, SOUTH, WEST, EAST order matters
+ "object_data_pointer": hex(object_data_pointer),
+ }
+ return map_header
+
+def read_all_map_headers():
+ if rom == None: load_rom()
+ assert_rom()
+ if len(map_pointers) == 0: load_map_pointers()
+
+ for map_id in map_pointers.keys():
+ map = map_pointers[map_id]
+ map_header = read_map_header(map["address"], map["bank"])
+
+ map_header["id"] = map_id
+ map_header["name"] = map["name"]
+ map_header["address"] = map["address"]
+ map_header["bank"] = map["bank"]
+
+ map_headers[map_id] = map_header
+
+ return map_headers
+
+if __name__ == "__main__":
+ #read binary data from file
+ load_rom()
+
+ #where are the map structs?
+ load_map_pointers()
+ #print json.dumps(map_pointers)
+
+ #experimental...
+ #print json.dumps(read_map_header(map_pointers[0]["address"], map_pointers[0]["bank"]))
+
+ read_all_map_headers()
+ print json.dumps(map_headers)
+
--- a/tools/extract_maps.py
+++ /dev/null
@@ -1,545 +1,0 @@
-#!/usr/bin/python
-#author: Bryan Bishop <[email protected]>
-#date: 2012-01-02
-#purpose: compute full map pointers
-import json
-
-#parse hex values as base 16 (see calculate_pointer)
-base = 16
-
-#where to load the rom from
-rom_filename = "baserom.gbc"
-rom = None #load the rom later
-
-#map header pointers start at 0x1AE
-start_map_header_pointers = 0x1AE
-
-#bank bytes for each map header start at 0xC23D
-start_map_header_pointer_banks = 0xC23D
-
-#number of maps in this list
-map_count = 0xF8 #including the 0th the total is is 248 or 0xF8
-
-maps = {
- 0x00: "Pallet Town",
- 0x01: "Viridian City",
- 0x02: "Pewter City",
- 0x03: "Cerulean City",
- 0x04: "Vermilion City",
- 0x05: "Lavender Town",
- 0x06: "Celadon City",
- 0x07: "Fuchsia City",
- 0x08: "Cinnibar Island",
- 0x09: "Indigo Plateau",
- 0x0A: "Saffron City",
- 0x0B: "FREEZE",
- 0x0C: "Route 1",
- 0x0D: "Route 2",
- 0x0E: "Route 3",
- 0x0F: "Route 4",
- 0x10: "Route 5",
- 0x11: "Route 6",
- 0x12: "Route 7",
- 0x13: "Route 8",
- 0x14: "Route 9",
- 0x15: "Route 10",
- 0x16: "Route 11",
- 0x17: "Route 12",
- 0x18: "Route 13",
- 0x19: "Route 14",
- 0x1A: "Route 15",
- 0x1B: "Route 16",
- 0x1C: "Route 17",
- 0x1D: "Route 18",
- 0x1E: "Route 19",
- 0x1F: "Route 20",
- 0x20: "Route 21",
- 0x21: "Route 22",
- 0x22: "Route 23",
- 0x23: "Route 24",
- 0x24: "Route 25",
- 0x25: "Ash's House F1",
- 0x26: "Ash's House F2",
- 0x27: "Gary's House",
- 0x28: "Oak's Lab",
- 0x29: "Viridian Poke Center",
- 0x2A: "Viridian Mart",
- 0x2B: "School",
- 0x2C: "Viridian House",
- 0x2D: "Viridian Gym",
- 0x2E: "Digletts Cave (Route 2)",
- 0x2F: "Viridian Forest (exit)",
- 0x30: "Route 2 House",
- 0x31: "Route 2 Gate",
- 0x32: "Viridian Forest (Entrance)",
- 0x33: "Viridian Forest",
- 0x34: "Museum F1",
- 0x35: "Museum F2",
- 0x36: "Pewter Gym",
- 0x37: "Pewter House (1)",
- 0x38: "Pewter Mart",
- 0x39: "Pewter House (2)",
- 0x3A: "Pewter PokeCenter",
- 0x3B: "Mt. Moon (1)",
- 0x3C: "Mt. Moon (2)",
- 0x3D: "Mt. Moon (3)",
- 0x3E: "Cerulean House (Trashed)",
- 0x3F: "Cerulean House (2)",
- 0x40: "Cerulean Pokecenter",
- 0x41: "Cerulean Gym",
- 0x42: "Bike Shop",
- 0x43: "Cerulean Mart",
- 0x44: "Mt. Moon Pokecenter",
- 0x45: "COPY OF: Trashed House",
- 0x46: "Route 5 Gate",
- 0x47: "Underground Tunnel Entrance (Route 5)",
- 0x48: "Day Care M",
- 0x49: "Route 6 Gate",
- 0x4A: "Underground Tunnel Entrance (Route 6)",
- 0x4B: "COPY OF: Underground Tunnel Entrance (Route 6)",
- 0x4C: "Route 7 Gate",
- 0x4D: "Underground Path Entrance (Route 7)",
- 0x4E: "COPY OF: Underground Path Entrance (Route 7)",
- 0x4F: "Route 8 Gate",
- 0x50: "Underground Path Entrance (Route 8)",
- 0x51: "Rock Tunnel Pokecenter",
- 0x52: "Rock Tunnel (1)",
- 0x53: "Power Plant",
- 0x54: "Route 11 Gate",
- 0x55: "Digletts Cave Entrance (Route 11)",
- 0x56: "Route 11 Gate (Upstairs)",
- 0x57: "Route 12 Gate",
- 0x58: "Bill's House",
- 0x59: "Vermilion PokeCenter",
- 0x5A: "Fan Club",
- 0x5B: "Vermilion Mart",
- 0x5C: "Vermilion Gym",
- 0x5D: "Vermilion House (1)",
- 0x5E: "Vermilion Dock",
- 0x5F: "S.S. Anne (1)",
- 0x60: "S.S. Anne (2)",
- 0x61: "S.S. Anne (3)",
- 0x62: "S.S. Anne (4)",
- 0x63: "S.S. Anne (5)",
- 0x64: "S.S. Anne (6)",
- 0x65: "S.S. Anne (7)",
- 0x66: "S.S. Anne (8)",
- 0x67: "S.S. Anne (9)",
- 0x68: "S.S. Anne (10)",
- 0x69: "FREEZE",
- 0x6A: "FREEZE",
- 0x6B: "FREEZE",
- 0x6C: "Victory Road (1)",
- 0x6D: "FREEZE",
- 0x6E: "FREEZE",
- 0x6F: "FREEZE",
- 0x70: "FREEZE",
- 0x71: "Lance",
- 0x72: "FREEZE",
- 0x73: "FREEZE",
- 0x74: "FREEZE",
- 0x75: "FREEZE",
- 0x76: "Hall of Fame Room",
- 0x77: "Underground Path (N/S)",
- 0x78: "Gary",
- 0x79: "Underground Path (W/E)",
- 0x7A: "Celadon Mart (1)",
- 0x7B: "Celadon Mart (2)",
- 0x7C: "Celadon Mart (3)",
- 0x7D: "Celadon Mart (4)",
- 0x7E: "Celadon Mart (5)",
- 0x7F: "Celadon Mart (6)",
- 0x80: "Celadon Mansion (1)",
- 0x81: "Celadon Mansion (2)",
- 0x82: "Celadon Mansion (3)",
- 0x83: "Celadon Mansion (4)",
- 0x84: "Celadon Mansion (5)",
- 0x85: "Celadon Pokecenter",
- 0x86: "Celadon GYM",
- 0x87: "Celadon Game Corner",
- 0x88: "Celadon House",
- 0x89: "Celadon Prize Room",
- 0x8A: "Celadon Diner",
- 0x8B: "Celadon House (2)",
- 0x8C: "Celadon Hotel",
- 0x8D: "Lavender Pokecenter",
- 0x8E: "Pokemon Tower (1)",
- 0x8F: "Pokemon Tower (2)",
- 0x90: "Pokemon Tower (3)",
- 0x91: "Pokemon Tower (4)",
- 0x92: "Pokemon Tower (5)",
- 0x93: "Pokemon Tower (6) ",
- 0x94: "Pokemon Tower (7)",
- 0x95: "Lavender House (1)",
- 0x96: "Lavender Mart",
- 0x97: "Lavender House (2)",
- 0x98: "Fuchsia Mart",
- 0x99: "Fuchsia House (1)",
- 0x9A: "Fuchsia Pokecenter",
- 0x9B: "Fuchsia House (2)",
- 0x9C: "Safari Zone Entrance",
- 0x9D: "Fuchsia GYM",
- 0x9E: "Fuchsia Meeting Room",
- 0x9F: "Seafoam Islands (2)",
- 0xA0: "Seafoam Islands (3)",
- 0xA1: "Seafoam Islands (4)",
- 0xA2: "Seafoam Islands (5)",
- 0xA3: "Vermilion House (2)",
- 0xA4: "Fuchsia House (3)",
- 0xA5: "Mansion (1)",
- 0xA6: "Cinnibar Gym",
- 0xA7: "Lab (1)",
- 0xA8: "Lab (2)",
- 0xA9: "Lab (3)",
- 0xAA: "Lab (4)",
- 0xAB: "Cinnibar Pokecenter",
- 0xAC: "Cinnibar Mart",
- 0xAD: "COPY: Cinnibar Mart",
- 0xAE: "Indigo Plateau Lobby",
- 0xAF: "Copycat's House F1",
- 0xB0: "Copycat's House F2",
- 0xB1: "Fighting Dojo",
- 0xB2: "Saffron Gym",
- 0xB3: "Saffron House (1)",
- 0xB4: "Saffron Mart",
- 0xB5: "Silph Co (1)",
- 0xB6: "Saffron Pokecenter",
- 0xB7: "Saffron House (2)",
- 0xB8: "Route 15 Gate",
- 0xBA: "Route 16 Gate Map",
- 0xBB: "Route 16 Gate Upstairs",
- 0xBC: "Route 16 House",
- 0xBD: "Route 12 House",
- 0xBE: "Route 18 Gate",
- 0xBF: "Route 18 Gate Header",
- 0xC0: "Seafoam Islands (1)",
- 0xC1: "Route 22 Gate",
- 0xC2: "Victory Road (2)",
- 0xC3: "Route 12 Gate Upstairs",
- 0xC4: "Vermilion House (3)",
- 0xC5: "Diglett's Cave",
- 0xC6: "Victory Road (3)",
- 0xC7: "Rocket Hideout (1)",
- 0xC8: "Rocket Hideout (2)",
- 0xC9: "Rocket Hideout (3)",
- 0xCA: "Rocket Hideout (4) ",
- 0xCB: "Rocket Hideout (Elevator)",
- 0xCC: "FREEZE",
- 0xCD: "FREEZE",
- 0xCE: "FREEZE",
- 0xCF: "Silph Co (2)",
- 0xD0: "Silph Co (3)",
- 0xD1: "Silph Co (4)",
- 0xD2: "Silph Co (5)",
- 0xD3: "Silph Co (6)",
- 0xD4: "Silph Co (7)",
- 0xD5: "Silph Co (8)",
- 0xD6: "Mansion (2)",
- 0xD7: "Mansion (3)",
- 0xD8: "Mansion (4)",
- 0xD9: "Safari Zone East",
- 0xDA: "Safari Zone North",
- 0xDB: "Safari Zone West",
- 0xDC: "Safari Zone Center",
- 0xDD: "Safari Zone Rest House (1)",
- 0xDE: "Safari Zone Secret House",
- 0xDF: "Safari Zone Rest House (2)",
- 0xE0: "Safari Zone Rest House (3)",
- 0xE1: "Safari Zone Rest House (4)",
- 0xE2: "Unknown Dungeon (2)",
- 0xE3: "Unknown Dungeon (3)",
- 0xE4: "Unknown Dungeon (1)",
- 0xE5: "Name Rater",
- 0xE6: "Cerulean House (3)",
- 0xE7: "FREEZE",
- 0xE8: "Rock Tunnel (2)",
- 0xE9: "Silph Co (9)",
- 0xEA: "Silph Co (10)",
- 0xEB: "Silph Co (11)",
- 0xEC: "Silph Co (Elevator)",
- 0xED: "FREEZE",
- 0xEE: "FREEZE",
- 0xEF: "Battle Center M",
- 0xF0: "Trade Center M",
- 0xF1: "FREEZE",
- 0xF2: "FREEZE",
- 0xF3: "FREEZE",
- 0xF4: "FREEZE",
- 0xF5: "Loreli",
- 0xF6: "Bruno",
- 0xF7: "Agatha"
- }
-
-map_pointers = {
- #0x00: {
- # "name": "Pallet Town",
- # "address": 0x182a1
- # },
- }
-
-map_headers = {
- #0x00: {
- # "name": "Pallet Town",
- # "address": 0x182a1,
- # "tileset"
- # "y"
- # "x"
- # "map_pointer"
- # "texts_pointer"
- # "script_pointer"
- # "connection_byte"
- # "num_connections"
- # "connections":
- # { "0":
- # { map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer }
- # },
- # "object_data_pointer"
- # },
- }
-
-#haters gonna hate
-def load_rom(filename=None):
- "load the rom into a global (returns True/False)"
- global rom
-
- if not filename:
- filename = rom_filename
-
- try:
- rom = open(filename, "rb").read()
- return True
- except Exception, exception:
- print "error loading rom"
- return False
-
-def assert_rom():
- global rom
- assert rom, "rom must be loaded, see load_rom()"
-
-def calculate_pointer(short_pointer, bank):
- short_pointer = int(short_pointer)
- bank = int(bank)
-
- pointer = short_pointer - 0x4000 + (bank * 0x4000)
-
- #result will be an integer
- return pointer
-
-def get_nth_map_header_pointer_bank_byte_address(map_id):
- "returns the address to the bank byte associated with this map pointer"
- address = start_map_header_pointer_banks + map_id
- return address
-
-def get_nth_map_header_pointer_bank_byte(map_id):
- "returns the bank number for this map header"
- assert_rom()
-
- address = get_nth_map_header_pointer_bank_byte_address(map_id)
- bank_byte = ord(rom[address])
- return bank_byte
-
-def get_nth_map_header_pointer(map_id):
- "returns the full pointer to the map header struct for this map"
- assert_rom()
-
- #figure out where the bytes for this pointer are located
- byte1_address = start_map_header_pointers + (map_id * 2)
- byte2_address = start_map_header_pointers + (map_id * 2) + 1
-
- #grab the two bytes making up the partial pointer
- byte1 = ord(rom[byte1_address])
- byte2 = ord(rom[byte2_address])
-
- #swap the bytes (16-bit pointers for z80 are little endian)
- temp = byte1
- byte1 = byte2
- byte2 = temp
- del temp
-
- #combine these into a single pointer (0x byte1 byte2)
- partial_pointer = (byte2 + (byte1 << 8))
- #print hex(partial_pointer)
-
- #get the bank id
- bank = get_nth_map_header_pointer_bank_byte(map_id)
-
- #calculate the full pointer
- pointer = calculate_pointer(partial_pointer, bank)
-
- #return it as an integer
- return pointer
-
-def load_map_pointers():
- global maps
- global map_pointers
-
- for map in maps.keys():
- pointer = get_nth_map_header_pointer(map)
- #print maps[map] + "\t\t\t" + hex(pointer)
-
- entry = {
- "name": maps[map],
- "address": hex(pointer),
- "bank": hex(get_nth_map_header_pointer_bank_byte(map))
- }
- map_pointers[map] = entry
-
- #print json.dumps(map_pointers)
-
-def read_connection_bytes(connection_bytes, bank):
- map_id = ord(connection_bytes[0])
-
- #connection strip
- connected_map_tile_pointer_byte1 = ord(connection_bytes[1])
- connected_map_tile_pointer_byte2 = ord(connection_bytes[2])
- connected_map_tile_pointer = (connected_map_tile_pointer_byte1 + (connected_map_tile_pointer_byte2 << 8))
-
- #connection strip
- current_map_tile_pointer_byte1 = ord(connection_bytes[3])
- current_map_tile_pointer_byte2 = ord(connection_bytes[4])
- current_map_tile_pointer = (current_map_tile_pointer_byte1 + (current_map_tile_pointer_byte2 << 8))
-
- bigness_byte = ord(connection_bytes[5])
- width_byte = ord(connection_bytes[6])
- y = ord(connection_bytes[7])
- x = ord(connection_bytes[8])
-
- #window
- window_pointer_byte1 = ord(connection_bytes[9])
- window_pointer_byte2 = ord(connection_bytes[10])
- window_pointer = (window_pointer_byte1 + (window_pointer_byte2 << 8))
-
- connection_data = {
- "map_id": map_id,
- "connected_map_tile_pointer": hex(connected_map_tile_pointer),
- "current_map_tile_pointer": hex(current_map_tile_pointer),
- "bigness": hex(bigness_byte),
- "width": hex(width_byte),
- "y": y,
- "x": x,
- "window_pointer": hex(window_pointer),
- }
- return connection_data
-
-def read_map_header(address, bank): #bank because i'm lazy
- address = int(address, base)
- bank = int(bank, base)
-
- tileset = ord(rom[address])
- y = ord(rom[address+1])
- x = ord(rom[address+2])
-
- map_pointer_byte1 = ord(rom[address+3])
- map_pointer_byte2 = ord(rom[address+4])
- partial_map_pointer = (map_pointer_byte1 + (map_pointer_byte2 << 8))
- map_pointer = calculate_pointer(partial_map_pointer, bank)
-
- texts_pointer_byte1 = ord(rom[address+5])
- texts_pointer_byte2 = ord(rom[address+6])
- partial_texts_pointer = (texts_pointer_byte1 + (texts_pointer_byte2 << 8))
- texts_pointer = calculate_pointer(partial_texts_pointer, bank)
-
- script_pointer_byte1 = ord(rom[address+7])
- script_pointer_byte2 = ord(rom[address+8])
- partial_script_pointer = (script_pointer_byte1 + ( script_pointer_byte2 << 8))
- script_pointer = calculate_pointer(partial_script_pointer, bank)
-
- connection_byte = ord(rom[address+9]) #0xc = NORTH | SOUTH
- # <&IIMarckus> the connection byte is a bitmask allowing 0-4 connections
- # <&IIMarckus> each connection is 11 bytes
- # <&IIMarckus> or'd
- # <&IIMarckus> east = 1, west = 2, south = 4, north = 8
- # <&IIMarckus> so a connection byte of 0xc means north/south
- # <&IIMarckus> which means there are 22 more bytes, 11 for each connection
- # < kanzure> 4 | 8 = c?
- # <&IIMarckus> yes
- # <&IIMarckus> easier to see if you convert to binary
- # <&IIMarckus> 0100 | 1000 = 1100
-
- north, south, west, east = False, False, False, False
- num_connections = 0
- connection_value = bin(connection_byte)[2:]
- if connection_value[0] == "1": #NORTH
- num_connections += 1
- north = True
- if len(connection_value) > 1 and connection_value[1] == "1": #SOUTH
- num_connections += 1
- south = True
- if len(connection_value) > 2 and connection_value[2] == "1": #WEST
- num_connections += 1
- west = True
- if len(connection_value) > 3 and connection_value[3] == "1": #EAST
- num_connections += 1
- east = True
-
- #quick test for connection data
- #connection0_stuff = rom[(address + 10):(address + 10 + 11)]
- #print "Route: " + hex(ord(connection0_stuff[0]))
-
- #setup
- connections = {}
-
- #go straight to object data if there are no connections
- if num_connections > 0:
- for connection in range(0, num_connections):
- base_connection_address = address + 10 + (11 * connection)
- connection_bytes = rom[base_connection_address : base_connection_address + 11]
- connection_data = read_connection_bytes(connection_bytes, bank)
-
- connections[connection] = connection_data
-
- #we might have to jump around a bit
- offset = address + 10 + (11 * num_connections)
-
- #object data
- object_data_pointer_byte1 = ord(rom[offset])
- object_data_pointer_byte2 = ord(rom[offset+1])
- partial_object_data_pointer = (object_data_pointer_byte1 + (object_data_pointer_byte2 << 8))
- object_data_pointer = calculate_pointer(partial_object_data_pointer, bank)
-
- map_header = {
- "tileset": hex(tileset),
- "y": hex(y),
- "x": hex(x),
- "map_pointer": hex(map_pointer),
- "texts_pointer": hex(texts_pointer),
- "script_pointer": hex(script_pointer),
- "connection_byte": hex(connection_byte),
- "num_connections": str(num_connections),
- "connections": connections, #NORTH, SOUTH, WEST, EAST order matters
- "object_data_pointer": hex(object_data_pointer),
- }
- return map_header
-
-def read_all_map_headers():
- if rom == None: load_rom()
- assert_rom()
- if len(map_pointers) == 0: load_map_pointers()
-
- for map_id in map_pointers.keys():
- map = map_pointers[map_id]
- map_header = read_map_header(map["address"], map["bank"])
-
- map_header["id"] = map_id
- map_header["name"] = map["name"]
- map_header["address"] = map["address"]
- map_header["bank"] = map["bank"]
-
- map_headers[map_id] = map_header
-
- return map_headers
-
-if __name__ == "__main__":
- #read binary data from file
- load_rom()
-
- #where are the map structs?
- load_map_pointers()
- #print json.dumps(map_pointers)
-
- #experimental...
- #print json.dumps(read_map_header(map_pointers[0]["address"], map_pointers[0]["bank"]))
-
- read_all_map_headers()
- print json.dumps(map_headers)
-