summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--comparator.py32
-rw-r--r--crystal.py130
-rw-r--r--gfx.py21
-rw-r--r--graph.py26
-rw-r--r--labels.py3
-rw-r--r--map_names.py2
-rw-r--r--romstr.py49
-rw-r--r--trainers.py3
8 files changed, 172 insertions, 94 deletions
diff --git a/comparator.py b/comparator.py
index 6d981e4..48bdb6b 100644
--- a/comparator.py
+++ b/comparator.py
@@ -17,12 +17,14 @@ from romstr import (
)
def load_rom(path):
- """ Loads a ROM file into an abbreviated RomStr object.
+ """
+ Loads a ROM file into an abbreviated RomStr object.
"""
return direct_load_rom(filename=path)
def load_asm(path):
- """ Loads source ASM into an abbreviated AsmList object.
+ """
+ Loads source ASM into an abbreviated AsmList object.
"""
return direct_load_asm(filename=path)
@@ -38,7 +40,8 @@ def findall_iter(sub, string):
return iter(next_index(len(sub)).next, -1)
class Address(int):
- """ A simple int wrapper to take 0xFFFF and $FFFF addresses.
+ """
+ A simple int wrapper to take 0xFFFF and $FFFF addresses.
"""
def __new__(cls, x=None, *args, **kwargs):
@@ -59,8 +62,9 @@ class Address(int):
found_blobs = []
class BinaryBlob(object):
- """ Stores a label, line number, and addresses of a function from Pokémon
- Red. These details can be used to determine whether or not the function was
+ """
+ Stores a label, line number, and addresses of a function from Pokémon Red.
+ These details can be used to determine whether or not the function was
copied into Pokémon Crystal.
"""
@@ -100,7 +104,8 @@ class BinaryBlob(object):
self.find_by_first_bytes()
def __repr__(self):
- """ A beautiful poem.
+ """
+ A beautiful poem.
"""
r = "BinaryBlob("
@@ -122,13 +127,15 @@ class BinaryBlob(object):
return self.__repr__()
def parse_from_red(self):
- """ Reads bytes from Pokémon Red and stores them.
+ """
+ Reads bytes from Pokémon Red and stores them.
"""
self.bytes = redrom[self.start_address : self.end_address + 1]
def pretty_bytes(self):
- """ Returns a better looking range of bytes.
+ """
+ Returns a better looking range of bytes.
"""
bytes = redrom.interval(self.start_address, \
@@ -138,7 +145,8 @@ class BinaryBlob(object):
return bytes
def find_in_crystal(self):
- """ Checks whether or not the bytes appear in Pokémon Crystal.
+ """
+ Checks whether or not the bytes appear in Pokémon Crystal.
"""
finditer = findall_iter(self.bytes, cryrom)
@@ -151,7 +159,8 @@ class BinaryBlob(object):
print self.label + ": found " + str(len(self.locations)) + " matches."
def find_by_first_bytes(self):
- """ Finds this blob in Crystal based on the first n bytes.
+ """
+ Finds this blob in Crystal based on the first n bytes.
"""
# how many bytes to match
@@ -184,7 +193,8 @@ redrom = load_rom(pokered_rom_path)
redsrc = load_asm(pokered_src_path)
def scan_red_asm(bank_stop=3, debug=True):
- """ Scans the ASM from Pokémon Red. Finds labels and objects. Does things.
+ """
+ Scans the ASM from Pokémon Red. Finds labels and objects. Does things.
Uses get_label_from_line and get_address_from_line_comment.
"""
diff --git a/crystal.py b/crystal.py
index c7b0939..680a441 100644
--- a/crystal.py
+++ b/crystal.py
@@ -275,10 +275,11 @@ def command_debug_information(command_byte=None, map_group=None, map_id=None, ad
all_texts = []
class TextScript:
- """ A text is a sequence of bytes (and sometimes commands). It's not the
- same thing as a Script. The bytes are translated into characters based
- on the lookup table (see chars.py). The in-text commands are for including
- values from RAM, playing sound, etc.
+ """
+ A text is a sequence of bytes (and sometimes commands). It's not the same
+ thing as a Script. The bytes are translated into characters based on the
+ lookup table (see chars.py). The in-text commands are for including values
+ from RAM, playing sound, etc.
see: http://hax.iimarck.us/files/scriptingcodes_eng.htm#InText
"""
@@ -1157,7 +1158,8 @@ def generate_map_constants():
print maps
def generate_map_constants_dimensions():
- """ Generate _WIDTH and _HEIGHT properties.
+ """
+ Generate _WIDTH and _HEIGHT properties.
"""
global map_internal_ids
output = ""
@@ -1172,8 +1174,10 @@ def generate_map_constants_dimensions():
return output
def transform_wildmons(asm):
- """ Converts a wildmons section to use map constants.
- input: wildmons text. """
+ """
+ Converts a wildmons section to use map constants.
+ input: wildmons text.
+ """
asmlines = asm.split("\n")
returnlines = []
for line in asmlines:
@@ -1899,7 +1903,8 @@ class GivePoke(Command):
return True
class DataByteWordMacro(Command):
- """ Only used by the preprocessor.
+ """
+ Only used by the preprocessor.
"""
id = None
@@ -2000,9 +2005,9 @@ movement_command_bases = {
# create MovementCommands from movement_command_bases
def create_movement_commands(debug=False):
- """ Creates MovementCommands from movement_command_bases.
- This is just a cheap trick instead of manually defining
- all of those classes.
+ """
+ Creates MovementCommands from movement_command_bases. This is just a cheap
+ trick instead of manually defining all of those classes.
"""
#movement_command_classes = inspect.getmembers(sys.modules[__name__], \
# lambda obj: inspect.isclass(obj) and \
@@ -3776,7 +3781,8 @@ class TrainerFragmentParam(PointerLabelParam):
trainer_group_table = None
class TrainerGroupTable:
- """ A list of pointers.
+ """
+ A list of pointers.
This should probably be called TrainerGroupPointerTable.
"""
@@ -3854,7 +3860,8 @@ class TrainerGroupHeader:
script_parse_table[address : self.last_address] = self
def get_dependencies(self, recompute=False, global_dependencies=set()):
- """ TrainerGroupHeader has no dependencies.
+ """
+ TrainerGroupHeader has no dependencies.
"""
# TODO: possibly include self.individual_trainer_headers
if recompute or self.dependencies == None:
@@ -3940,7 +3947,8 @@ class TrainerHeader:
# TrainerGroupHeader covers its address range
def make_name(self):
- """ Must occur after parse() is called.
+ """
+ Must occur after parse() is called.
Constructs a name based on self.parent.group_name and self.name.
"""
if self.trainer_group_id in [0x14, 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2B, 0x2C, 0x2D, 0x2F, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x41]:
@@ -4021,7 +4029,8 @@ class TrainerHeader:
return output
class TrainerPartyMonParser:
- """ Just a generic trainer party mon parser.
+ """
+ Just a generic trainer party mon parser.
Don't use this directly. Only use the child classes.
"""
id = None
@@ -4078,7 +4087,9 @@ class TrainerPartyMonParser:
return output
class TrainerPartyMonParser0(TrainerPartyMonParser):
- """ Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. """
+ """
+ Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers.
+ """
id = 0
size = 2 + 1
param_types = {
@@ -4086,7 +4097,10 @@ class TrainerPartyMonParser0(TrainerPartyMonParser):
1: {"name": "species", "class": PokemonParam},
}
class TrainerPartyMonParser1(TrainerPartyMonParser):
- """ Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders."""
+ """
+ Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3>
+ <Move4>. Used often for Gym Leaders.
+ """
id = 1
size = 6 + 1
param_types = {
@@ -4098,7 +4112,9 @@ class TrainerPartyMonParser1(TrainerPartyMonParser):
5: {"name": "move4", "class": MoveParam},
}
class TrainerPartyMonParser2(TrainerPartyMonParser):
- """ Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans. """
+ """
+ Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans.
+ """
id = 2
size = 3 + 1
param_types = {
@@ -4107,8 +4123,11 @@ class TrainerPartyMonParser2(TrainerPartyMonParser):
2: {"name": "item", "class": ItemLabelByte},
}
class TrainerPartyMonParser3(TrainerPartyMonParser):
- """ Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>.
- Used by a few Cooltrainers. """
+ """
+ Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1>
+ <Move2> <Move3> <Move4>.
+ Used by a few Cooltrainers.
+ """
id = 3
size = 7 + 1
param_types = {
@@ -4124,7 +4143,8 @@ class TrainerPartyMonParser3(TrainerPartyMonParser):
trainer_party_mon_parsers = [TrainerPartyMonParser0, TrainerPartyMonParser1, TrainerPartyMonParser2, TrainerPartyMonParser3]
def find_trainer_ids_from_scripts():
- """ Looks through all scripts to find trainer group numbers and trainer numbers.
+ """
+ Looks through all scripts to find trainer group numbers and trainer numbers.
This can be used with trainer_group_maximums to figure out the current number of
trainers in each of the originating trainer groups.
@@ -4145,7 +4165,8 @@ def find_trainer_ids_from_scripts():
trainer_group_maximums[key] = value
def report_unreferenced_trainer_ids():
- """ Reports on the number of unreferenced trainer ids in each group.
+ """
+ Reports on the number of unreferenced trainer ids in each group.
This should be called after find_trainer_ids_from_scripts.
@@ -4186,7 +4207,8 @@ def report_unreferenced_trainer_ids():
print "total unreferenced trainers: " + str(total_unreferenced_trainers)
def check_script_has_trainer_data(script):
- """ see find_trainer_ids_from_scripts
+ """
+ see find_trainer_ids_from_scripts
"""
for command in script.commands:
trainer_group = None
@@ -4206,7 +4228,7 @@ def check_script_has_trainer_data(script):
trainer_group_maximums[trainer_group] = set([trainer_id])
def trainer_name_from_group(group_id, trainer_id=0):
- """ This doesn't actually work for trainer_id > 0."""
+ """This doesn't actually work for trainer_id > 0."""
bank = calculate_bank(0x39999)
ptr_address = 0x39999 + ((group_id - 1)*2)
address = calculate_pointer_from_bytes_at(ptr_address, bank=bank)
@@ -4214,7 +4236,8 @@ def trainer_name_from_group(group_id, trainer_id=0):
return text
def trainer_group_report():
- """ Reports how many trainer ids are used in each trainer group.
+ """
+ Reports how many trainer ids are used in each trainer group.
"""
output = ""
total = 0
@@ -4231,7 +4254,8 @@ def trainer_group_report():
return output
def make_trainer_group_name_trainer_ids(trainer_group_table, debug=True):
- """ Edits trainer_group_names and sets the trainer names.
+ """
+ Edits trainer_group_names and sets the trainer names.
For instance, "AMY & MAY" becomes "AMY_AND_MAY1" and "AMY_AND_MAY2"
This should only be used after TrainerGroupTable.parse has been called.
@@ -4269,7 +4293,8 @@ def make_trainer_group_name_trainer_ids(trainer_group_table, debug=True):
print "done improving trainer names"
def pretty_print_trainer_id_constants():
- """ Prints out some constants for trainer ids, for "constants.asm".
+ """
+ Prints out some constants for trainer ids, for "constants.asm".
make_trainer_group_name_trainer_ids must be called prior to this.
"""
@@ -5000,7 +5025,8 @@ def old_parse_map_header_at(address, map_group=None, map_id=None, debug=True):
def get_direction(connection_byte, connection_id):
- """ Given a connection byte and a connection id, which direction is this
+ """
+ Given a connection byte and a connection id, which direction is this
connection?
example:
@@ -6443,7 +6469,8 @@ def parse_all_map_headers(debug=True):
map_names[group_id][map_id]["header_old"] = old_parsed_map
class PokedexEntryPointerTable:
- """ A list of pointers.
+ """
+ A list of pointers.
"""
def __init__(self):
@@ -6492,8 +6519,6 @@ class PokedexEntryPointerTable:
return output
class PokedexEntry:
- """ """
-
def __init__(self, address, pokemon_id):
self.address = address
self.dependencies = None
@@ -6982,7 +7007,8 @@ class Asm:
return llabel
return False
def does_address_have_label(self, address):
- """ Checks if an address has a label.
+ """
+ Checks if an address has a label.
"""
# either something will directly have the address
# or- it's possibel that no label was given
@@ -7208,8 +7234,8 @@ def list_things_in_bank(bank):
return objects
def list_texts_in_bank(bank):
- """ Narrows down the list of objects
- that you will be inserting into Asm.
+ """
+ Narrows down the list of objects that you will be inserting into Asm.
"""
if len(all_texts) == 0:
raise Exception("all_texts is blank.. run_main() will populate it")
@@ -7226,8 +7252,8 @@ def list_texts_in_bank(bank):
return texts
def list_movements_in_bank(bank):
- """ Narrows down the list of objects
- to speed up Asm insertion.
+ """
+ Narrows down the list of objects to speed up Asm insertion.
"""
if len(all_movements) == 0:
raise Exception("all_movements is blank.. run_main() will populate it")
@@ -7242,8 +7268,9 @@ def list_movements_in_bank(bank):
return movements
def dump_asm_for_texts_in_bank(bank, start=50, end=100):
- """ Simple utility to help with dumping texts into a particular bank. This
- is helpful for figuring out which text is breaking that bank.
+ """
+ Simple utility to help with dumping texts into a particular bank. This is
+ helpful for figuring out which text is breaking that bank.
"""
# load and parse the ROM if necessary
if rom == None or len(rom) <= 4:
@@ -7278,7 +7305,8 @@ def dump_asm_for_movements_in_bank(bank, start=0, end=100):
print "done dumping movements for bank $%.2x" % (bank)
def dump_things_in_bank(bank, start=50, end=100):
- """ is helpful for figuring out which object is breaking that bank.
+ """
+ is helpful for figuring out which object is breaking that bank.
"""
# load and parse the ROM if necessary
if rom == None or len(rom) <= 4:
@@ -7370,7 +7398,8 @@ def get_label_for(address):
all_new_labels = []
class Label:
- """ Every object in script_parse_table is given a label.
+ """
+ Every object in script_parse_table is given a label.
This label is simply a way to keep track of what objects have
been previously written to file.
@@ -7406,8 +7435,9 @@ class Label:
all_new_labels.append(self)
def check_is_in_file(self):
- """ This method checks if the label appears in the file
- based on the entries to the Asm.parts list.
+ """
+ This method checks if the label appears in the file based on the
+ entries to the Asm.parts list.
"""
# assert new_asm != None, "new_asm should be an instance of Asm"
load_asm2()
@@ -7416,18 +7446,19 @@ class Label:
return is_in_file
def check_address_is_in_file(self):
- """ Checks if the address is in use by another label.
+ """
+ Checks if the address is in use by another label.
"""
load_asm2()
self.address_is_in_file = new_asm.does_address_have_label(self.address)
return self.address_is_in_file
def old_check_address_is_in_file(self):
- """ Checks whether or not the address of the object is
- already in the file. This might happen if the label name
- is different but the address is the same. Another scenario
- is that the label is already used, but at a different
- address.
+ """
+ Checks whether or not the address of the object is already in the file.
+ This might happen if the label name is different but the address is the
+ same. Another scenario is that the label is already used, but at a
+ different address.
This method works by looking at the INCBINs. When there is
an INCBIN that covers this address in the file, then there
@@ -7447,7 +7478,8 @@ class Label:
return False
def make_label(self):
- """ Generates a label name based on parents and self.object.
+ """
+ Generates a label name based on parents and self.object.
"""
object = self.object
name = object.make_label()
diff --git a/gfx.py b/gfx.py
index 3d8e950..a09e230 100644
--- a/gfx.py
+++ b/gfx.py
@@ -1492,8 +1492,25 @@ def append_terminator_to_lzs(directory):
new.write(data)
new.close()
-
-
+def lz_to_png_by_file(filename):
+ """
+ Converts a lz file to png. Dumps a 2bpp file too.
+ """
+ assert filename[-3:] == ".lz"
+ lz_data = open(filename, "rb").read()
+ bpp = Decompressed(lz).output
+ bpp_filename = filename.replace(".lz", ".2bpp")
+ to_file(bpp_filename, bpp)
+ to_png(bpp_filename)
+
+def dump_tileset_pngs():
+ """
+ Converts .lz format tilesets into .png format tilesets. Also, leaves a
+ bunch of wonderful .2bpp files everywhere for your amusement.
+ """
+ for tileset_id in range(37):
+ tileset_filename = "../gfx/tilesets/" + str(tileset_id).zfill(2) + ".lz"
+ lz_to_png_by_file(tileset_filename)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
diff --git a/graph.py b/graph.py
index b545083..47087e5 100644
--- a/graph.py
+++ b/graph.py
@@ -10,7 +10,8 @@ from romstr import (
)
class RomGraph(nx.DiGraph):
- """ Graphs various functions pointing to each other.
+ """
+ Graphs various functions pointing to each other.
TODO: Bank switches are nasty. They should be detected. Otherwise,
functions will point to non-functions within the same bank. Another way
@@ -35,7 +36,8 @@ class RomGraph(nx.DiGraph):
rompath = "../baserom.gbc"
def __init__(self, rom=None, **kwargs):
- """ Loads and parses the ROM into a function graph.
+ """
+ Loads and parses the ROM into a function graph.
"""
# continue the initialization
nx.DiGraph.__init__(self, **kwargs)
@@ -50,14 +52,16 @@ class RomGraph(nx.DiGraph):
self.parse()
def load_rom(self):
- """ Creates a RomStr from rompath.
+ """
+ Creates a RomStr from rompath.
"""
file_handler = open(self.rompath, "r")
self.rom = RomStr(file_handler.read())
file_handler.close()
def parse(self):
- """ Parses the ROM starting with the first function address. Each
+ """
+ Parses the ROM starting with the first function address. Each
function is disassembled and parsed to find where else it leads to.
"""
functions = {}
@@ -123,13 +127,15 @@ class RomGraph(nx.DiGraph):
self.functions = functions
def pretty_printer(self):
- """ Shows some text output describing which nodes point to which other
- nodes.
+ """
+ Shows some text output describing which nodes point to which other
+ nodes.
"""
print self.edges()
def to_d3(self):
- """ Exports to d3.js because we're gangster like that.
+ """
+ Exports to d3.js because we're gangster like that.
"""
import networkx.readwrite.json_graph as json_graph
content = json_graph.dumps(self)
@@ -138,12 +144,14 @@ class RomGraph(nx.DiGraph):
fh.close()
def to_gephi(self):
- """ Generates a gexf file.
+ """
+ Generates a gexf file.
"""
nx.write_gexf(self, "graph.gexf")
class RedGraph(RomGraph):
- """ Not implemented. Go away.
+ """
+ Not implemented. Go away.
"""
rompath = "../pokered-baserom.gbc"
diff --git a/labels.py b/labels.py
index e57c6e2..61ec4c2 100644
--- a/labels.py
+++ b/labels.py
@@ -130,7 +130,8 @@ def line_has_comment_address(line, returnable={}, bank=None):
return True
def get_address_from_line_comment(line, bank=None):
- """ wrapper for line_has_comment_address
+ """
+ wrapper for line_has_comment_address
"""
returnable = {}
result = line_has_comment_address(line, returnable=returnable, bank=bank)
diff --git a/map_names.py b/map_names.py
index 599b377..00cf452 100644
--- a/map_names.py
+++ b/map_names.py
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
-"""
-"""
# this is modified in crystal.py during run-time
map_names = {
diff --git a/romstr.py b/romstr.py
index 5701f19..90c099f 100644
--- a/romstr.py
+++ b/romstr.py
@@ -32,7 +32,8 @@ end_08_scripts_with = [
spacing = "\t"
class RomStr(str):
- """ Simple wrapper to prevent a giant rom from being shown on screen.
+ """
+ Simple wrapper to prevent a giant rom from being shown on screen.
"""
def __init__(self, *args, **kwargs):
@@ -41,13 +42,15 @@ class RomStr(str):
str.__init__(self)
def __repr__(self):
- """ Simplifies this object so that the output doesn't overflow stdout.
+ """
+ Simplifies this object so that the output doesn't overflow stdout.
"""
return "RomStr(too long)"
@classmethod
def load(cls, filename=None, crystal=True, red=False):
- """ Loads a ROM into a RomStr.
+ """
+ Loads a ROM into a RomStr.
"""
if crystal and not red and not filename:
file_handler = open("../baserom.gbc", "r")
@@ -62,8 +65,9 @@ class RomStr(str):
return RomStr(bytes)
def load_labels(self, filename="labels.json"):
- """ Loads labels from labels.json, or parses the source code file and
- generates new labels.
+ """
+ Loads labels from labels.json, or parses the source code file and
+ generates new labels.
"""
filename = os.path.join(os.path.dirname(__file__), filename)
@@ -109,7 +113,8 @@ class RomStr(str):
self.labels = json.read(open(filename, "r").read())
def get_address_for(self, label):
- """ Returns the address of a label. This is slow and could be improved
+ """
+ Returns the address of a label. This is slow and could be improved
dramatically.
"""
label = str(label)
@@ -119,18 +124,20 @@ class RomStr(str):
return None
def length(self):
- """ len(self)
+ """
+ len(self)
"""
return len(self)
def len(self):
- """ len(self)
+ """
+ len(self)
"""
return self.length()
def interval(self, offset, length, strings=True, debug=True):
- """ returns hex values for the rom starting at offset until
- offset+length
+ """
+ returns hex values for the rom starting at offset until offset+length
"""
returnable = []
for byte in self[offset:offset+length]:
@@ -141,16 +148,17 @@ class RomStr(str):
return returnable
def until(self, offset, byte, strings=True, debug=False):
- """ Returns hex values from rom starting at offset until the given
- byte.
+ """
+ Returns hex values from rom starting at offset until the given byte.
"""
return self.interval(offset, self.find(chr(byte), offset) - offset, strings=strings)
def to_asm(self, address, end_address=None, size=None, max_size=0x4000, debug=None):
- """ Disassembles ASM at some address. This will stop disassembling when
- either the end_address or size is met. Also, there's a maximum size
- that will be parsed, so that large patches of data aren't parsed as
- code.
+ """
+ Disassembles ASM at some address. This will stop disassembling when
+ either the end_address or size is met. Also, there's a maximum size
+ that will be parsed, so that large patches of data aren't parsed as
+ code.
"""
if type(address) in [str, unicode] and "0x" in address:
address = int(address, 16)
@@ -185,16 +193,19 @@ class RomStr(str):
#return DisAsm(start_address=start_address, end_address=end_address, size=size, max_size=max_size, debug=debug, rom=self)
class AsmList(list):
- """ Simple wrapper to prevent all asm lines from being shown on screen.
+ """
+ Simple wrapper to prevent all asm lines from being shown on screen.
"""
def length(self):
- """ len(self)
+ """
+ len(self)
"""
return len(self)
def __repr__(self):
- """ Simplifies this object so that the output doesn't overflow stdout.
+ """
+ Simplifies this object so that the output doesn't overflow stdout.
"""
return "AsmList(too long)"
diff --git a/trainers.py b/trainers.py
index 786454a..cf17b98 100644
--- a/trainers.py
+++ b/trainers.py
@@ -83,7 +83,8 @@ trainer_group_names = {
}
def remove_parentheticals_from_trainer_group_names():
- """ Clean up the trainer group names.
+ """
+ Clean up the trainer group names.
"""
i = 0
for (key, value) in trainer_group_names.items():