shithub: pokered

ref: ad8782826e048d50c7dec0c0edf3f31f5f3d588e
dir: /extras/pretty_map_headers.py/

View raw version
#!/usr/bin/python
# -*- coding: utf-8 -*-
#author: Bryan Bishop <kanzure@gmail.com>
#date: 2012-01-02
#purpose: dump asm for each map header
import json
import extract_maps
import sprite_helper
import random
import string
import analyze_texts #hopefully not a dependency loop

base = 16
spacing = "    "
all_texts = None

#map constants
map_constants = [
["PALLET_TOWN", 0x00],
["VIRIDIAN_CITY", 0x01],
["PEWTER_CITY", 0x02],
["CERULEAN_CITY", 0x03],
["LAVENDER_TOWN", 0x04],
["VERMILION_CITY", 0x05],
["CELADON_CITY", 0x06],
["FUCHSIA_CITY", 0x07],
["CINNABAR_ISLAND", 0x08],
["INDIGO_PLATEAU", 0x09],
["SAFFRON_CITY", 0x0A],
["ROUTE_1", 0x0C],
["ROUTE_2", 0x0D],
["ROUTE_3", 0x0E],
["ROUTE_4", 0x0F],
["ROUTE_5", 0x10],
["ROUTE_6", 0x11],
["ROUTE_7", 0x12],
["ROUTE_8", 0x13],
["ROUTE_9", 0x14],
["ROUTE_10", 0x15],
["ROUTE_11", 0x16],
["ROUTE_12", 0x17],
["ROUTE_13", 0x18],
["ROUTE_14", 0x19],
["ROUTE_15", 0x1A],
["ROUTE_16", 0x1B],
["ROUTE_17", 0x1C],
["ROUTE_18", 0x1D],
["ROUTE_19", 0x1E],
["ROUTE_20", 0x1F],
["ROUTE_21", 0x20],
["ROUTE_22", 0x21],
["ROUTE_23", 0x22],
["ROUTE_24", 0x23],
["ROUTE_25", 0x24],
["REDS_HOUSE_1F", 0x25],
["REDS_HOUSE_2F", 0x26],
["BLUES_HOUSE", 0x27],
["OAKS_LAB", 0x28],
["VIRIDIAN_POKECENTER", 0x29],
["VIRIDIAN_MART", 0x2A],
["VIRIDIAN_SCHOOL", 0x2B],
["VIRIDIAN_HOUSE", 0x2C],
["VIRIDIAN_GYM", 0x2D],
["DIGLETTS_CAVE_EXIT", 0x2E],
["VIRIDIAN_FOREST_EXIT", 0x2F],
["ROUTE_2_HOUSE", 0x30],
["ROUTE_2_GATE", 0x31],
["VIRIDIAN_FOREST_ENTRANCE", 0x32],
["VIRIDIAN_FOREST", 0x33],
["MUSEUM_1F", 0x34],
["MUSEUM_2F", 0x35],
["PEWTER_GYM", 0x36],
["PEWTER_HOUSE_1", 0x37],
["PEWTER_MART", 0x38],
["PEWTER_HOUSE_2", 0x39],
["PEWTER_POKECENTER", 0x3A],
["MT_MOON_1", 0x3B],
["MT_MOON_2", 0x3C],
["MT_MOON_3", 0x3D],
["TRASHED_HOUSE", 0x3E],
["CERULEAN_HOUSE", 0x3F],
["CERULEAN_POKECENTER", 0x40],
["CERULEAN_GYM", 0x41],
["BIKE_SHOP", 0x42],
["CERULEAN_MART", 0x43],
["MT_MOON_POKECENTER", 0x44],
["ROUTE_5_GATE", 0x46],
["PATH_ENTRANCE_ROUTE_5", 0x47],
["DAYCAREM", 0x48],
["ROUTE_6_GATE", 0x49],
["PATH_ENTRANCE_ROUTE_6", 0x4A],
["ROUTE_7_GATE", 0x4C],
["PATH_ENTRANCE_ROUTE_7", 0x4D],
["ROUTE_8_GATE", 0x4F],
["PATH_ENTRANCE_ROUTE_8", 0x50],
["ROCK_TUNNEL_POKECENTER", 0x51],
["ROCK_TUNNEL_1", 0x52],
["POWER_PLANT", 0x53],
["ROUTE_11_GATE_1F", 0x54],
["DIGLETTS_CAVE_ENTRANCE", 0x55],
["ROUTE_11_GATE_2F", 0x56],
["ROUTE_12_GATE", 0x57],
["BILLS_HOUSE", 0x58],
["VERMILION_POKECENTER", 0x59],
["POKEMON_FAN_CLUB", 0x5A],
["VERMILION_MART", 0x5B],
["VERMILION_GYM", 0x5C],
["VERMILION_HOUSE_1", 0x5D],
["VERMILION_DOCK", 0x5E],
["SS_ANNE_1", 0x5F],
["SS_ANNE_2", 0x60],
["SS_ANNE_3", 0x61],
["SS_ANNE_4", 0x62],
["SS_ANNE_5", 0x63],
["SS_ANNE_6", 0x64],
["SS_ANNE_7", 0x65],
["SS_ANNE_8", 0x66],
["SS_ANNE_9", 0x67],
["SS_ANNE_10", 0x68],
["VICTORY_ROAD_1", 0x6C],
["LANCES_ROOM", 0x71],
["HALL_OF_FAME", 0x76],
["UNDERGROUND_PATH_NS", 0x77],
["CHAMPIONS_ROOM", 0x78],
["UNDERGROUND_PATH_WE", 0x79],
["CELADON_MART_1", 0x7A],
["CELADON_MART_2", 0x7B],
["CELADON_MART_3", 0x7C],
["CELADON_MART_4", 0x7D],
["CELADON_MART_5", 0x7E],
["CELADON_MART_6", 0x7F],
["CELADON_MANSION_1", 0x80],
["CELADON_MANSION_2", 0x81],
["CELADON_MANSION_3", 0x82],
["CELADON_MANSION_4", 0x83],
["CELADON_MANSION_5", 0x84],
["CELADON_POKECENTER", 0x85],
["CELADON_GYM", 0x86],
["GAME_CORNER", 0x87],
["CELADON_HOUSE", 0x88],
["CELADONPRIZE_ROOM", 0x89],
["CELADON_DINER", 0x8A],
["CELADON_HOUSE_2", 0x8B],
["CELADONHOTEL", 0x8C],
["LAVENDER_POKECENTER", 0x8D],
["POKEMONTOWER_1", 0x8E],
["POKEMONTOWER_2", 0x8F],
["POKEMONTOWER_3", 0x90],
["POKEMONTOWER_4", 0x91],
["POKEMONTOWER_5", 0x92],
["POKEMONTOWER_6", 0x93],
["POKEMONTOWER_7", 0x94],
["LAVENDER_HOUSE_1", 0x95],
["LAVENDER_MART", 0x96],
["LAVENDER_HOUSE_2", 0x97],
["FUCHSIA_MART", 0x98],
["FUCHSIA_HOUSE_1", 0x99],
["FUCHSIA_POKECENTER", 0x9A],
["FUCHSIA_HOUSE_2", 0x9B],
["SAFARIZONEENTRANCE", 0x9C],
["FUCHSIA_GYM", 0x9D],
["FUCHSIAMEETINGROOM", 0x9E],
["SEAFOAM_ISLANDS_2", 0x9F],
["SEAFOAM_ISLANDS_3", 0xA0],
["SEAFOAM_ISLANDS_4", 0xA1],
["SEAFOAM_ISLANDS_5", 0xA2],
["VERMILION_HOUSE_2", 0xA3],
["FUCHSIA_HOUSE_3", 0xA4],
["MANSION_1", 0xA5],
["CINNABAR_GYM", 0xA6],
["CINNABAR_LAB_1", 0xA7],
["CINNABAR_LAB_2", 0xA8],
["CINNABAR_LAB_3", 0xA9],
["CINNABAR_LAB_4", 0xAA],
["CINNABAR_POKECENTER", 0xAB],
["CINNABAR_MART", 0xAC],
["INDIGO_PLATEAU_LOBBY", 0xAE],
["COPYCATS_HOUSE_1F", 0xAF],
["COPYCATS_HOUSE_2F", 0xB0],
["FIGHTINGDOJO", 0xB1],
["SAFFRON_GYM", 0xB2],
["SAFFRON_HOUSE_1", 0xB3],
["SAFFRON_MART", 0xB4],
["SILPH_CO_1F", 0xB5],
["SAFFRON_POKECENTER", 0xB6],
["SAFFRON_HOUSE_2", 0xB7],
["ROUTE_15_GATE", 0xB8],
["ROUTE_16_GATE_1F", 0xBA],
["ROUTE_16_GATE_2F", 0xBB],
["ROUTE_16_HOUSE", 0xBC],
["ROUTE_12_HOUSE", 0xBD],
["ROUTE_18_GATE_1F", 0xBE],
["ROUTE_18_GATE_2F", 0xBF],
["SEAFOAM_ISLANDS_1", 0xC0],
["ROUTE_22_GATE", 0xC1],
["VICTORY_ROAD_2", 0xC2],
["ROUTE_12_GATE_2F", 0xC3],
["VERMILION_HOUSE_3", 0xC4],
["DIGLETTS_CAVE", 0xC5],
["VICTORY_ROAD_3", 0xC6],
["ROCKET_HIDEOUT_1", 0xC7],
["ROCKET_HIDEOUT_2", 0xC8],
["ROCKET_HIDEOUT_3", 0xC9],
["ROCKET_HIDEOUT_4", 0xCA],
["ROCKET_HIDEOUT_ELEVATOR", 0xCB],
["SILPH_CO_2F", 0xCF],
["SILPH_CO_3F", 0xD0],
["SILPH_CO_4F", 0xD1],
["SILPH_CO_5F", 0xD2],
["SILPH_CO_6F", 0xD3],
["SILPH_CO_7F", 0xD4],
["SILPH_CO_8F", 0xD5],
["MANSION_2", 0xD6],
["MANSION_3", 0xD7],
["MANSION_4", 0xD8],
["SAFARI_ZONE_EAST", 0xD9],
["SAFARI_ZONE_NORTH", 0xDA],
["SAFARI_ZONE_WEST", 0xDB],
["SAFARI_ZONE_CENTER", 0xDC],
["SAFARI_ZONE_REST_HOUSE_1", 0xDD],
["SAFARI_ZONE_SECRET_HOUSE", 0xDE],
["SAFARI_ZONE_REST_HOUSE_2", 0xDF],
["SAFARI_ZONE_REST_HOUSE_3", 0xE0],
["SAFARI_ZONE_REST_HOUSE_4", 0xE1],
["UNKNOWN_DUNGEON_2", 0xE2],
["UNKNOWN_DUNGEON_3", 0xE3],
["UNKNOWN_DUNGEON_1", 0xE4],
["NAME_RATERS_HOUSE", 0xE5],
["CERULEAN_HOUSE_3", 0xE6],
["ROCK_TUNNEL_2", 0xE8],
["SILPH_CO_9F", 0xE9],
["SILPH_CO_10F", 0xEA],
["SILPH_CO_11F", 0xEB],
["SILPH_CO_ELEVATOR", 0xEC],
["BATTLE_CENTER", 0xEF],
["TRADE_CENTER", 0xF0],
["LORELEIS_ROOM", 0xF5],
["BRUNOS_ROOM", 0xF6],
["AGATHAS_ROOM", 0xF7],
["BEACH_HOUSE", 0xF8]]

#i prefer a different data structure
temp = {}
for constant in map_constants:
    value = constant[1]
    name = constant[0]
    temp[value] = name
map_constants = temp
del temp

#these appear outside of quotes
constant_abbreviation_bytes = {
    0x4A: "Char4A",
    0x52: "Char52",
    0x53: "Char53",
    0x54: "Char54",
    0x56: "Char56",
    0x5B: "Char5B",
    0x5C: "Char5C",
    0x5D: "Char5D",
    0x5E: "Char5E",
}

#these appear in quotes
char_conversion = [
(" ", 0x7F),
("A", 0x80),
("B", 0x81),
("C", 0x82),
("D", 0x83),
("E", 0x84),
("F", 0x85),
("G", 0x86),
("H", 0x87),
("I", 0x88),
("J", 0x89),
("K", 0x8A),
("L", 0x8B),
("M", 0x8C),
("N", 0x8D),
("O", 0x8E),
("P", 0x8F),
("Q", 0x90),
("R", 0x91),
("S", 0x92),
("T", 0x93),
("U", 0x94),
("V", 0x95),
("W", 0x96),
("X", 0x97),
("Y", 0x98),
("Z", 0x99),
("(", 0x9A),
(")", 0x9B),
(":", 0x9C),
(";", 0x9D),
("[", 0x9E),
("]", 0x9F),
("a", 0xA0),
("b", 0xA1),
("c", 0xA2),
("d", 0xA3),
("e", 0xA4),
("f", 0xA5),
("g", 0xA6),
("h", 0xA7),
("i", 0xA8),
("j", 0xA9),
("k", 0xAA),
("l", 0xAB),
("m", 0xAC),
("n", 0xAD),
("o", 0xAE),
("p", 0xAF),
("q", 0xB0),
("r", 0xB1),
("s", 0xB2),
("t", 0xB3),
("u", 0xB4),
("v", 0xB5),
("w", 0xB6),
("x", 0xB7),
("y", 0xB8),
("z", 0xB9),
("é", 0xBA),
("'d", 0xBB),
("'l", 0xBC),
("'s", 0xBD),
("'t", 0xBE),
("'v", 0xBF),
("'", 0xE0),
("-", 0xE3),
("'r", 0xE4),
("'m", 0xE5),
("?", 0xE6),
("!", 0xE7),
(".", 0xE8),
("♂", 0xEF),
("/", 0xF3),
(",", 0xF4),
("♀", 0xF5),
("0", 0xF6),
("1", 0xF7),
("2", 0xF8),
("3", 0xF9),
("4", 0xFA),
("5", 0xFB),
("6", 0xFC),
("7", 0xFD),
("8", 0xFE),
("9", 0xFF)]
#these appear in quotes
txt_bytes = {
    0x50: "@",
    0x54: "#",
    0x75: "…",
}
for item in char_conversion:
    txt_bytes[item[1]] = item[0]
del char_conversion

#this was originally for renaming freeze maps for a unique name
def random_hash():
    available_chars = string.hexdigits[:16]
    return ''.join(
        random.choice(available_chars)
        for dummy in xrange(5))

def offset_to_pointer(offset):
    if type(offset) == str: offset = int(offset, base)
    return int(offset) % 0x4000 + 0x4000

def map_name_cleaner(name, id):
    "names have to be acceptable asm labels"
    
    #duck out early
    if name == "FREEZE":
        #name += "_" + random_hash() + "_h"
        name += "_" + str(id) + "_h"
        return name

    #the long haul
    name = name.replace(":", "")
    name = name.replace("(", "")
    name = name.replace(")", "")
    name = name.replace("'", "")
    name = name.replace("/", "") #N/S -> NS, W/E -> WE
    name = name.replace(".", "") #S.S. -> SS, Mt. -> Mt
    name = name.replace(" ", "") #or '_' ??
    name = name + "_h"

    return name

def write_connections(north, south, west, east):
    #north 0, south 1, west 2, east 3
    if north and south and west and east: return "NORTH | SOUTH | WEST | EAST"
    if north and south and west and not east: return "NORTH | SOUTH | WEST"
    if north and south and not west and east: return "NORTH | SOUTH | EAST"
    if north and not south and west and east: return "NORTH | WEST | EAST"
    if not north and south and west and east: return "SOUTH | WEST | EAST"
    if north and south and not west and not east: return "NORTH | SOUTH"
    if not north and not south and west and east: return "WEST | EAST"
    if not north and south and west and not east: return "SOUTH | WEST"
    if not north and south and not west and east: return "SOUTH | EAST"
    if north and not south and west and not east: return "NORTH | WEST"
    if north and not south and not west and east: return "NORTH | EAST"
    raise Exception, "unpredicted outcome on write_connections"

#TODO: make this elegant
def connection_line(byte):
    if type(byte) == str:
        byte = int(byte, base)

    connections = 0
    north, south, west, east = False, False, False, False

    temp = bin(byte)[2:]

    if len(temp) == 1:
        if temp[0] == "1": #EAST
            east = True
    elif len(temp) == 2:
        if temp[0] == "1": #WEST
            west = True
        if temp[1] == "1": #EAST
            east = True
    elif len(temp) == 3:
        if temp[0] == "1": #SOUTH
            south = True
        if temp[1] == "1": #WEST
            west = True
        if temp[2] == "1": #EAST
            east = True
    elif len(temp) == 4:
        if temp[0] == "1": #NORTH
            north = True
        if temp[1] == "1": #SOUTH
            south = True
        if temp[2] == "1": #WEST
            west = True
        if temp[3] == "1": #EAST
            east = True
    
    if north: connections += 1
    if south: connections += 1
    if west: connections += 1
    if east: connections += 1

    #i don't have time to optimize this
    if connections == 0:
        return "$00"
    if connections == 1:
        if north: return "NORTH"
        if south: return "SOUTH"
        if west: return "WEST"
        if east: return "EAST"
    if connections >= 2:
        return write_connections(north, south, west, east)

def connection_pretty_printer(connections):
#map_id, connected_map_tile_pointer, current_map_tile_pointer, bigness, width, y, x, window_pointer
    output = ""

    for connection in connections.keys():
        connection = connections[connection]
        map_id = hex(connection["map_id"])[2:].zfill(2)
        connected_map_tile_pointer = connection["connected_map_tile_pointer"][2:]
        current_map_tile_pointer = connection["current_map_tile_pointer"][2:]
        bigness = hex(int(connection["bigness"], base))[2:].zfill(2)
        width = hex(int(connection["width"], base))[2:].zfill(2)
        y = hex(connection["y"])[2:].zfill(2)
        x = hex(connection["x"])[2:].zfill(2)
        window_pointer = connection["window_pointer"][2:]

        output += spacing + "db $" + map_id + " ; some map\n"
        output += spacing + "dw $" + connected_map_tile_pointer + ", $" + current_map_tile_pointer + " ; pointers (connected, current) (strip)\n"
        output += spacing + "db $" + bigness + ", $" + width + " ; bigness, width\n"
        output += spacing + "db $" + y + ", $" + x + " ; alignments (y, x)\n"
        output += spacing + "dw $" + window_pointer + " ; window\n\n"

    return output

def map_header_pretty_printer(map_header):
    address = map_header["address"]
    bank = map_header["bank"]
    id = map_header["id"]
    
    name = map_header["name"]
    asm_name = map_name_cleaner(name, id)
    if name == "FREEZE": return "" #skip freeze maps

    tileset = map_header["tileset"][2:]
    y = int(map_header["y"], base)
    x = int(map_header["x"], base)
    map_pointer = map_header["map_pointer"]
    texts_pointer = map_header["texts_pointer"]
    script_pointer = map_header["script_pointer"]
    connection_byte = map_header["connection_byte"]
    connections = map_header["connections"]
    object_data_pointer = map_header["object_data_pointer"]

    byte_size = 12 + (11 * len(connections.keys()))

    map_pointer = hex(offset_to_pointer(map_pointer))[2:]
    texts_pointer = hex(offset_to_pointer(texts_pointer))[2:]
    script_pointer = hex(offset_to_pointer(script_pointer))[2:]
    object_data_pointer = hex(offset_to_pointer(object_data_pointer))[2:]

    #formatting: hex(y)[2:].zill(2) or "%02x" % (y,)

    output  = asm_name + ": ; " + address + " to " + hex(int(address, base) + byte_size) + " (" + str(byte_size) + " bytes) (id=" + str(id) + ")\n"
    output += spacing + "db $" + str(tileset).zfill(2) + " ; tileset\n"
    output += spacing + "db $" + hex(y)[2:].zfill(2) + ", $" + hex(x)[2:].zfill(2) + " ; dimensions (y, x)\n"
    output += spacing + "dw $" + map_pointer + ", $" + texts_pointer + ", $" + script_pointer + " ; blocks, texts, scripts\n"
    output += spacing + "db " + connection_line(connection_byte) + " ; connections\n\n"
    
    if len(connections) > 0:
        output += spacing + "; connections data\n\n"
        output += connection_pretty_printer(connections)
        output += spacing + "; end connection data\n\n"

    #TODO: print out label for object_data_pointer if it's already in the file
    output += spacing + "dw $" + object_data_pointer + " ; objects\n"

    return output

def make_object_label_name(name):
    """make a label for the asm file
    like: PalletTownObject"""
    name = map_name_cleaner(name, None)
    return name.replace("_h", "") + "Object"

def make_text_label(map_name, id):
    """using standard object labels
    for instance, PalletTownText3"""
    label = map_name_cleaner(map_name, None)[:-2] + "Text" + str(id)
    return label

def object_data_pretty_printer(map_id):
    map = extract_maps.map_headers[map_id]
    output = ""

    label_name = make_object_label_name(map["name"])
    object_data_pointer = map["object_data_pointer"]
    object = map["object_data"]
    size = extract_maps.compute_object_data_size(object)
    
    output += label_name + ": ; " + object_data_pointer + " (size=" + str(size) + ")\n"
    output += spacing + "db $" + hex(object["maps_border_tile"])[2:] + " ; border tile\n"
    output += "\n"
    output += spacing + "db $" + hex(int(object["number_of_warps"]))[2:] + " ; warps\n"

    #warps
    for warp_id in object["warps"]:
        warp = object["warps"][warp_id]
        y = warp["y"]
        x = warp["x"]
        warp_to_point = warp["warp_to_point"]
        warp_to_map_id = warp["warp_to_map_id"]

        try:
            warp_to_map_constant = map_constants[warp_to_map_id]
        except Exception, exc:
            warp_to_map_constant = "$" + hex(warp_to_map_id)[2:]

        output += spacing + "db $" + hex(int(y))[2:] + ", $" + hex(int(x))[2:] + ", $" + hex(int(warp_to_point))[2:] + ", " + warp_to_map_constant + "\n"
    
    output += "\n"
    output += spacing + "db $" + hex(int(object["number_of_signs"]))[2:] + " ; signs\n"

    #signs
    for sign_id in object["signs"]:
        sign = object["signs"][sign_id]
        y = sign["y"]
        x = sign["x"]
        text_id = sign["text_id"]

        output += spacing + "db $" + hex(int(y))[2:] + ", $" + hex(int(x))[2:] + ", $" + hex(int(text_id))[2:] + " ; " + make_text_label(map["name"], text_id) + "\n"
    
    output += "\n"
    output += spacing + "db $" + hex(int(object["number_of_things"]))[2:] + " ; people\n"

    #people
    for thing_id in object["things"]:
        thing = object["things"][thing_id]
        
        ending = ""
        if thing["type"] == "item":
            ending = ", $" + hex(int(thing["item_number"]))[2:] + " ; item\n"
        elif thing["type"] == "trainer":
            ending = ", $" + hex(int(thing["trainer_type"]))[2:] + ", $" + hex(int(thing["pokemon_set"]))[2:] + " ; trainer\n"
        else:
            ending = " ; person\n"

        picture_number = hex(int(thing["picture_number"]))[2:]
        y = hex(int(thing["y"]) - 4)[2:]
        x = hex(int(thing["x"]) - 4)[2:]
        movement1 = hex(int(thing["movement1"]))[2:]
        movement2 = hex(int(thing["movement2"]))[2:]
        text_id = hex(int(thing["original_text_string_number"]))[2:]

        output += spacing + "db " + sprite_helper.sprites[thing["picture_number"]] + ", $" + y + " + 4, $" + x + " + 4, $" + movement1 + ", $" + movement2 + ", $" + text_id + ending
    
    output += "\n"

    if object["number_of_warps"] > 0:
        output += spacing + "; warp-to\n"

        for warp_to_id in object["warp_tos"]:
            warp_to = object["warp_tos"][warp_to_id]
            map_width = map["x"]
            warp_to_y = hex(int(warp_to["y"]))[2:]
            warp_to_x = hex(int(warp_to["x"]))[2:]

            try:
                previous_location = map_constants[object["warps"][warp_to_id]["warp_to_map_id"]]
                comment = " ; " + previous_location
            except Exception, exc:
                comment = ""

            output += spacing + "EVENT_DISP $" + map_width[2:] + ", $" + warp_to_y + ", $" + warp_to_x + comment + "\n"
            #output += spacing + "dw $" + hex(int(warp_to["event_displacement"][1]))[2:] + hex(int(warp_to["event_displacement"][0]))[2:] + "\n"
            #output += spacing + "db $" + hex(int(warp_to["y"]))[2:] + ", $" + hex(int(warp_to["x"]))[2:] + "\n"
            #output += "\n"
    
        output += "\n"

    while output[-1] == "\n":
        output = output[:-1]

    output += "\n"
    return output

def find_all_tx_fars():
    global all_texts
    tx_fars = [] #[map_id, text_id, text_pointer, tx_far_pointer, TX_FAR]
    for map_id in all_texts:
        map2 = all_texts[map_id]
        for text_id in map2.keys():
            text = map2[text_id]
            for command_id in text.keys():
                command = text[command_id]
                if "TX_FAR" in command.keys():
                    TX_FAR = command["TX_FAR"]
                    if TX_FAR[0]["type"] == 0x0:
                        tx_fars.append([map_id, text_id, analyze_texts.get_text_pointer(int(extract_maps.map_headers[map_id]["texts_pointer"], 16), text_id), command["pointer"], TX_FAR])
    return tx_fars

def tx_far_label_maker(map_name, text_id):
    label = "_" + map_name_cleaner(map_name, None)[:-2] + "Text" + str(text_id)
    return label

def tx_far_pretty_printer(tx_far):
    "pretty output for a tx_far"
    map_id = tx_far[0]
    map2 = extract_maps.map_headers[map_id]
    text_id = tx_far[1]
    text_pointer = tx_far[2]
    tx_far_start_address = tx_far[3]
    text_far = tx_far[4]
    lines = text_far[0]["lines"]
    label = tx_far_label_maker(map2["name"], text_id)

    #add the ending byte on the next line
    #lines[len(lines.keys())+1] = [text_far[1]["type"]]

    #add the ending byte to the last line- always seems $57
    lines[len(lines.keys())-1].append(text_far[1]["type"])

    output  = "\n"
    output += label + ": ; " + hex(tx_far_start_address) + "\n"
    first = True
    for line_id in lines:
        line = lines[line_id]
        output += spacing + "db "
        if first:
            output += "$0, "
            first = False
        
        quotes_open = False
        first_byte = True
        was_byte = False
        byte_count = 0
        for byte in line:
            if byte in txt_bytes:
                if not quotes_open and not first_byte: #start text
                    output += ", \""
                    quotes_open = True
                    first_byte = False
                if not quotes_open and first_byte: #start text
                    output += "\""
                    quotes_open = True
                output += txt_bytes[byte]
            elif byte in constant_abbreviation_bytes:
                if quotes_open:
                    output += "\""
                    quotes_open = False
                if not first_byte:
                    output += ", "
                output += constant_abbreviation_bytes[byte]
            else:
                if quotes_open:
                    output += "\""
                    quotes_open = False
                
                #if you want the ending byte on the last line
                #if not (byte == 0x57 or byte == 0x50 or byte == 0x58):
                if not first_byte:
                    output += ", "
                
                output += "$" + hex(byte)[2:]
                was_byte = True

                #add a comma unless it's the end of the line
                #if byte_count+1 != len(line):
                #    output += ", "
            
            first_byte = False
            byte_count += 1
        #close final quotes
        if quotes_open:
            output += "\""
            quotes_open = False

        output += "\n"

    #output += "\n"
    return output

def print_all_headers():
    maps = []
    for map in extract_maps.map_headers:
        maps.append(extract_maps.map_headers[map])
    
    maps = sorted(maps, key=lambda map: int(map["address"], base))

    for map in maps:
        output = map_header_pretty_printer(map)
        if output != "": print output

if __name__ == "__main__":
    #read binary data from file
    extract_maps.load_rom()

    #where are the map structs?
    extract_maps.load_map_pointers()

    #load map headers into memory
    extract_maps.read_all_map_headers()

    #load texts
    all_texts = analyze_texts.analyze_texts()

    #print them out
    #print_all_headers()

    #print out only the object data for pallet town (map 0)
    #print object_data_pretty_printer(0)

    #prepare to pretty print tx_fars
    #first you must load all_texts
    tx_fars = find_all_tx_fars()
    for entry in tx_fars:
        print tx_far_pretty_printer(entry)