mirror of
https://github.com/pret/pokered.git
synced 2024-10-23 23:18:23 +00:00
1f1f31ff9c
hg-commit-id: 27e8096bb251
703 lines
23 KiB
Python
703 lines
23 KiB
Python
#!/usr/bin/python
|
|
#author: Bryan Bishop <kanzure@gmail.com>
|
|
#date: 2012-01-02
|
|
#url: http://hax.iimarck.us/files/rbheaders.txt
|
|
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
|
|
|
|
bad_maps = [0x0b, 0x45, 0x4b, 0x4e, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x72, 0x73, 0x74, 0x75, 0xad, 0xcc, 0xcd, 0xce, 0xe7, 0xed, 0xee, 0xf1, 0xf2, 0xf3, 0xf4]
|
|
|
|
maps = {
|
|
0x00: "Pallet Town",
|
|
0x01: "Viridian City",
|
|
0x02: "Pewter City",
|
|
0x03: "Cerulean City",
|
|
0x04: "Lavender Town", #??
|
|
0x05: "Vermilion City", #correct
|
|
0x06: "Celadon City",
|
|
0x07: "Fuchsia City",
|
|
0x08: "Cinnabar 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: "Red's House 1F",
|
|
0x26: "Red's House 2F",
|
|
0x27: "Blue'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 Roof",
|
|
0x7F: "Celadon Mart Elevator",
|
|
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 Mart 5",
|
|
0x89: "Celadon Prize Room",
|
|
0x8A: "Celadon Diner",
|
|
0x8B: "Celadon House",
|
|
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: "Cinnabar Gym",
|
|
0xA7: "Lab (1)",
|
|
0xA8: "Lab (2)",
|
|
0xA9: "Lab (3)",
|
|
0xAA: "Lab (4)",
|
|
0xAB: "Cinnabar Pokecenter",
|
|
0xAC: "Cinnabar Mart",
|
|
0xAD: "COPY: Cinnabar 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: "Lorelei",
|
|
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_warp_data(address, warp_count):
|
|
warps = {}
|
|
for warp_id in range(0, warp_count):
|
|
offset = address + (warp_id*4) #4 bytes per warp
|
|
warp = {}
|
|
|
|
warp["y"] = ord(rom[offset])
|
|
warp["x"] = ord(rom[offset+1])
|
|
warp["warp_to_point"] = ord(rom[offset+2])
|
|
warp["warp_to_map_id"] = ord(rom[offset+3])
|
|
|
|
warps[warp_id] = warp
|
|
return warps
|
|
|
|
def read_sign_data(address, sign_count):
|
|
signs = {}
|
|
for sign_id in range(0, sign_count):
|
|
offset = address + (sign_id * 3)
|
|
sign = {}
|
|
sign["y"] = ord(rom[offset])
|
|
sign["x"] = ord(rom[offset+1])
|
|
sign["text_id"] = ord(rom[offset+2])
|
|
signs[sign_id] = sign
|
|
return signs
|
|
|
|
def read_warp_tos(address, warp_count):
|
|
warp_tos = {}
|
|
for warp_to_id in range(0, warp_count):
|
|
offset = address + (warp_to_id * 4)
|
|
warp_to = {}
|
|
warp_to["event_displacement"] = [ord(rom[offset]),ord(rom[offset+1])]
|
|
warp_to["y"] = ord(rom[offset+2])
|
|
warp_to["x"] = ord(rom[offset+3])
|
|
warp_tos[warp_to_id] = warp_to
|
|
return warp_tos
|
|
|
|
def get_object_data(address):
|
|
if type(address) == str: address = int(address, base)
|
|
output = {}
|
|
|
|
maps_border_tile = ord(rom[address])
|
|
|
|
number_of_warps = ord(rom[address+1])
|
|
if number_of_warps == 0: warps = {}
|
|
else:
|
|
warps = read_warp_data(address+2, number_of_warps)
|
|
|
|
offset = number_of_warps * 4
|
|
address = address + 2 + offset
|
|
|
|
number_of_signs = ord(rom[address])
|
|
if number_of_signs == 0: signs = {}
|
|
else:
|
|
signs = read_sign_data(address+1, number_of_signs)
|
|
|
|
offset = number_of_signs * 3
|
|
address = address + 1 + offset
|
|
|
|
number_of_things = ord(rom[address])
|
|
address = address + 1
|
|
|
|
things = {}
|
|
for thing_id in range(0, number_of_things):
|
|
thing = {}
|
|
picture_number = ord(rom[address])
|
|
y = ord(rom[address+1])
|
|
x = ord(rom[address+2])
|
|
movement1 = ord(rom[address+3])
|
|
movement2 = ord(rom[address+4])
|
|
text_string_number = ord(rom[address+5])
|
|
|
|
address += 5 + 1
|
|
|
|
if text_string_number & (1 << 6) != 0: #trainer
|
|
thing["type"] = "trainer"
|
|
thing["trainer_type"] = ord(rom[address])
|
|
thing["pokemon_set"] = ord(rom[address+1])
|
|
address += 2
|
|
elif text_string_number & (1 << 7) != 0: #item
|
|
thing["type"] = "item"
|
|
thing["item_number"] = ord(rom[address])
|
|
address += 1
|
|
else: #normal person
|
|
thing["type"] = "person"
|
|
|
|
thing["picture_number"] = picture_number
|
|
thing["y"] = y
|
|
thing["x"] = x
|
|
thing["movement1"] = movement1
|
|
thing["movement2"] = movement2
|
|
thing["original_text_string_number"] = text_string_number
|
|
thing["text_string_number"] = text_string_number & 0xF
|
|
things[thing_id] = thing
|
|
|
|
warp_tos = read_warp_tos(address, number_of_warps)
|
|
|
|
output["maps_border_tile"] = maps_border_tile
|
|
output["number_of_warps"] = number_of_warps
|
|
output["warps"] = warps
|
|
output["number_of_signs"] = number_of_signs
|
|
output["signs"] = signs
|
|
output["number_of_things"] = number_of_things
|
|
output["things"] = things
|
|
output["warp_tos"] = warp_tos
|
|
|
|
return output
|
|
|
|
def compute_object_data_size(object):
|
|
size = 4
|
|
size += 6 * (int(object["number_of_things"]))
|
|
|
|
trainer_count = 0
|
|
item_count = 0
|
|
for thing in object["things"]:
|
|
thing = object["things"][thing]
|
|
if thing["type"] == "trainer": trainer_count += 1
|
|
elif thing["type"] == "item": item_count += 1
|
|
|
|
size += 2 * trainer_count
|
|
size += item_count
|
|
|
|
size += 8 * object["number_of_warps"]
|
|
size += 3 * object["number_of_signs"]
|
|
|
|
return size
|
|
|
|
def get_direction(connection_byte, connection_id):
|
|
"""given a connection byte and a connection id, which direction is this connection?
|
|
the 0th connection of $5 is SOUTH and the 1st connection is EAST"""
|
|
connection_options = [0b1000, 0b0100, 0b0010, 0b0001]
|
|
results = ["NORTH", "SOUTH", "WEST", "EAST"]
|
|
for option in connection_options:
|
|
if (option & connection_byte) == 0:
|
|
results[connection_options.index(option)] = ""
|
|
#prune results
|
|
while "" in results:
|
|
results.remove("")
|
|
return results[connection_id]
|
|
|
|
def read_map_header(address, bank):
|
|
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
|
|
|
|
num_connections = 0
|
|
connection_value = bin(connection_byte)[2:]
|
|
if connection_value[0] == "1": #NORTH
|
|
num_connections += 1
|
|
if len(connection_value) > 1 and connection_value[1] == "1": #SOUTH
|
|
num_connections += 1
|
|
if len(connection_value) > 2 and connection_value[2] == "1": #WEST
|
|
num_connections += 1
|
|
if len(connection_value) > 3 and connection_value[3] == "1": #EAST
|
|
num_connections += 1
|
|
|
|
#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)
|
|
connection_data["direction"] = get_direction(connection_byte, connection)
|
|
|
|
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)
|
|
object_data = get_object_data(object_data_pointer)
|
|
|
|
texts = set()
|
|
for thing_id in object_data["things"].keys():
|
|
thing = object_data["things"][thing_id]
|
|
texts.add(thing["text_string_number"])
|
|
for sign_id in object_data["signs"].keys():
|
|
sign = object_data["signs"][sign_id]
|
|
texts.add(sign["text_id"])
|
|
texts = list(texts)
|
|
number_of_referenced_texts = len(texts)
|
|
|
|
map_header = {
|
|
"tileset": hex(tileset),
|
|
"y": hex(y),
|
|
"x": hex(x),
|
|
"map_pointer": hex(map_pointer),
|
|
"texts_pointer": hex(texts_pointer),
|
|
"number_of_referenced_texts": number_of_referenced_texts,
|
|
"referenced_texts": texts,
|
|
"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),
|
|
"object_data": object_data,
|
|
}
|
|
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():
|
|
if map_id in bad_maps: continue
|
|
map2 = map_pointers[map_id]
|
|
map_header = read_map_header(map2["address"], map2["bank"])
|
|
|
|
map_header["id"] = map_id
|
|
map_header["name"] = map2["name"]
|
|
map_header["address"] = map2["address"]
|
|
map_header["bank"] = map2["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)
|
|
|
|
#print map_headers[37]
|
|
|
|
for header in map_headers:
|
|
if header in bad_maps: continue
|
|
print "map " + str(header) + " has " + str(map_headers[header]["number_of_referenced_texts"]) + " referenced texts"
|
|
|