shithub: pokecrystal

Download patch

ref: fb7c3a3ea5c758e1f4ddd2d1d859115cbb419248
parent: 1d6fa83902fb34113389436cc3fac349b839f7db
author: Bryan Bishop <[email protected]>
date: Sat May 19 11:43:26 EDT 2012

move trainer parsing classes into crystal.py

--- a/extras/crystal.py
+++ b/extras/crystal.py
@@ -3434,6 +3434,306 @@
 
 from trainers import *
 
+# for fixing trainer_group_names
+import re
+
+trainer_group_pointer_table_address    = 0x39999
+trainer_group_pointer_table_address_gs = 0x3993E
+
+class TrainerGroupTable:
+    """ A list of pointers.
+    """
+
+    def __init__(self):
+        assert 0x43 in trainer_group_maximums.keys(), "TrainerGroupTable should onyl be created after all the trainers have been found"
+        self.address = trainer_group_pointer_table_address
+        self.bank = calculate_bank(trainer_group_pointer_table_address)
+        self.label = Label(name="TrainerGroupPointerTable", address=self.address, object=self)
+        self.size = None
+        self.last_address = None
+        self.dependencies = None
+        self.headers = []
+        self.parse()
+
+        # TODO: add this to script_parse_table
+        #script_parse_table[address : self.last_address] = self
+
+    def get_dependencies(self, recompute=False, global_dependencies=set()):
+        global_dependencies.update(self.headers)
+        if recompute == True and self.dependencies != None and self.dependencies != []: 
+            return self.dependencies
+        dependencies = [self.headers]
+        for header in self.headers:
+            dependencies += header.get_dependencies(recompute=recompute, global_dependencies=global_dependencies)
+        return dependencies
+
+    def parse(self):
+        size = 0 
+        for (key, kvalue) in trainer_group_names.items():
+            # calculate the location of this trainer group header from its pointer
+            pointer_bytes_location = kvalue["pointer_address"]
+            parsed_address = calculate_pointer_from_bytes_at(pointer_bytes_location, bank=self.bank)
+            trainer_group_names[key]["parsed_address"] = parsed_address
+
+            # parse the trainer group header at this location
+            name = kvalue["name"]
+            trainer_group_header = TrainerGroupHeader(address=parsed_address, group_id=key, group_name=name)
+            trainer_group_names[key]["header"] = trainer_group_header
+            self.headers.append(trainer_group_header)
+
+            # keep track of the size of this pointer table
+            size += 2
+        self.size = size
+        self.last_address = self.address + self.size
+
+    def to_asm(self):
+        output = "".join([str("dw "+get_label_for(header.address)+"\n") for header in self.headers])
+        return output
+
+class TrainerGroupHeader:
+    """
+    A trainer group header is a repeating list of individual trainer headers.
+
+    <Trainer Name> <0x50> <Data type> <Pokémon Data>+ <0xFF>
+
+    Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers.
+    Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders.
+    Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans.
+    Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>. Used by a few Cooltrainers.
+    """
+
+    def __init__(self, address=None, group_id=None, group_name=None):
+        assert address!=None, "TrainerGroupHeader requires an address"
+        assert group_id!=None, "TrainerGroupHeader requires a group_id"
+        assert group_name!=None, "TrainerGroupHeader requires a group_name"
+
+        self.address = address
+        self.group_id = group_id
+        self.group_name = group_name
+        self.dependencies = None
+        self.individual_trainer_headers = []
+        self.label = Label(name=group_name+"TrainerGroupHeader", address=self.address, object=self)
+        self.parse()
+
+        # TODO: add this to script_parse_table
+        #script_parse_table[address : self.last_address] = self
+
+    def get_dependencies(self, recompute=False, global_dependencies=set()):
+        """ TrainerGroupHeader has no dependencies.
+        """
+        # TODO: possibly include self.individual_trainer_headers
+        if recompute or self.dependencies == None:
+            self.dependencies = []
+        return self.dependencies
+
+    def parse(self):
+        """
+        how do i know when there's no more data for this header?
+         do a global analysis of the rom and figure out the max ids
+         this wont work for rom hacks of course
+        see find_trainer_ids_from_scripts
+        """
+        size = 0
+        current_address = self.address
+
+        # create an IndividualTrainerHeader for each id in range(min id, max id + 1)
+        min_id = min(trainer_group_maximums[self.group_id])
+        max_id = max(trainer_group_maximums[self.group_id])
+
+        for trainer_id in range(min_id, max_id+1):
+            trainer_header = TrainerHeader(address=current_address, trainer_group_id=self.group_id, trainer_id=trainer_id, parent=self)
+            current_address += trainer_header.size
+            size += trainer_header.size
+
+        self.last_address = current_address
+        self.size = size
+
+    def to_asm(self):
+        raise NotImplementedError
+        output += ""
+
+class TrainerHeader:
+    """
+    <Trainer Name> <0x50> <Data type> <Pokémon Data>+ <0xFF>
+
+    Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers.
+    Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders.
+    Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans.
+    Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>. Used by a few Cooltrainers.
+    """
+
+    def __init__(self, address=None, trainer_group_id=None, trainer_id=None, parent=None):
+        self.parent = parent
+        self.address = address
+        self.trainer_group_id = trainer_group_id
+        self.trainer_id = trainer_id
+        self.dependencies = []
+        self.size = None
+        self.last_address = None
+        self.parse()
+        self.label = Label(name=self.make_name(), address=self.address, object=self)
+        # this shouldn't be added to script_parse_table because
+        # TrainerGroupHeader covers its address range
+
+    def make_name(self):
+        """ Must occur after parse() is called.
+        Constructs a name based on self.parent.group_name and self.name.
+        """
+        return self.parent.group_name + "_" + self.name
+
+    def get_dependencies(self, recompute=False, global_dependencies=set()):
+        if recompute or self.dependencies == None:
+            self.dependencies = []
+        return self.dependencies
+
+    def parse(self):
+        address = self.address
+
+        # figure out how many bytes until 0x50 "@"
+        jump = how_many_until(chr(0x50), address)
+
+        # parse the "@" into the name
+        self.name = parse_text_at(address, jump+1)
+
+        # where is the next byte?
+        current_address = address + jump + 1
+
+        # figure out the pokemon data type
+        self.data_type = ord(rom[current_address])
+
+        current_address += 1
+
+        # figure out which partymon parser to use for this trainer header
+        party_mon_parser = None
+        for monparser in trainer_party_mon_parsers:
+            if monparser.id == self.data_type:
+                party_mon_parser = monparser
+                break
+
+        if party_mon_parser == None:
+            raise Exception, "no trainer party mon parser found to parse data type " + hex(self.data_type)
+
+        self.party_mons = party_mon_parser(address=current_address, group_id=self.trainer_group_id, trainer_id=self.trainer_id, parent=self)
+
+        # let's have everything in trainer_party_mon_parsers handle the last $FF
+        self.size = self.party_mons.size + 1 + len(self.name)
+        self.last_address = self.party_mons.last_address
+
+    def to_asm(self):
+        output = "db \""+self.name+"\"\n"
+        output += "; data type\n"
+        output += "db $%.2x\n"%(self.data_byte)
+        output += self.party_mons.to_asm()
+        return output
+
+# TODO: MoveParam should map to an actual attack
+MoveParam = SingleByteParam
+
+class TrainerPartyMonParser:
+    """ Just a generic trainer party mon parser.
+    Don't use this directly. Only use the child classes.
+    """
+    id = None
+    dependencies = None
+    params = []
+    param_types = None
+
+    # could go either way on this one.. TrainerGroupHeader.parse would need to be changed
+    # so as to not increase current_address by one after reading "data_type"
+    override_byte_check = True
+
+    def __init__(self, address=None, group_id=None, trainer_id=None, parent=None):
+        self.address = address
+        self.group_id = group_id
+        self.trainer_id = trainer_id
+        self.parent = parent
+        self.args = {}
+        self.params = {}
+        self.parse()
+
+        # pick up the $FF at the end
+        self.size += 1
+        self.last_address += 1
+
+    # this is exactly Command.parse
+    def parse(self):
+        #id, size (inclusive), param_types
+        #param_type = {"name": each[1], "class": each[0]}
+        if not self.override_byte_check:
+            current_address = self.address+1
+        else:
+            current_address = self.address
+        byte = ord(rom[self.address])
+        if not self.override_byte_check and (not byte == self.id):
+            raise Exception, "byte ("+hex(byte)+") != self.id ("+hex(self.id)+")"
+        i = 0
+        for (key, param_type) in self.param_types.items():
+            name = param_type["name"]
+            klass = param_type["class"]
+            #make an instance of this class, like SingleByteParam()
+            #or ItemLabelByte.. by making an instance, obj.parse() is called
+            obj = klass(address=current_address, name=name, parent=self, **dict([(k,v) for (k, v) in self.args.items() if k not in ["parent"]]))
+            #save this for later
+            self.params[i] = obj
+            #increment our counters
+            current_address += obj.size
+            i += 1
+        self.last_address = current_address
+        return True
+
+    def to_asm(self):
+        output = "; " + ", ".join([param_type["name"] for param_type in self.param_types]) + "\n"
+        output += "db " + ", ".join([param.to_asm() for (name, param) in self.params.items()])
+        output += "\n"
+        output += "db $FF ; end trainer party mons"
+        return output
+
+class TrainerPartyMonParser0(TrainerPartyMonParser):
+    """ Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. """
+    id = 0
+    size = 2 + 1
+    param_types = {
+        0: {"name": "level", "class": DecimalParam},
+        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."""
+    id = 1
+    size = 6 + 1
+    param_types = {
+        0: {"name": "level", "class": DecimalParam},
+        1: {"name": "species", "class": PokemonParam},
+        2: {"name": "move1", "class": MoveParam},
+        3: {"name": "move2", "class": MoveParam},
+        4: {"name": "move3", "class": MoveParam},
+        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. """
+    id = 2
+    size = 3 + 1
+    param_types = {
+        0: {"name": "level", "class": DecimalParam},
+        1: {"name": "species", "class": PokemonParam},
+        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. """
+    id = 3
+    size = 7 + 1
+    param_types = {
+        0: {"name": "level", "class": DecimalParam},
+        1: {"name": "species", "class": PokemonParam},
+        2: {"name": "item", "class": ItemLabelByte},
+        3: {"name": "move1", "class": MoveParam},
+        4: {"name": "move2", "class": MoveParam},
+        5: {"name": "move3", "class": MoveParam},
+        6: {"name": "move4", "class": MoveParam},
+    }
+
+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.
 
@@ -7623,7 +7923,7 @@
     find_trainer_ids_from_scripts()
 
     # and parse the main TrainerGroupTable once we know the max number of trainers
-    #gtable = TrainerGroupTable()
+    trainer_group_header = TrainerGroupTable()
 
 #just a helpful alias
 main=run_main
--- a/extras/trainers.py
+++ b/extras/trainers.py
@@ -5,7 +5,7 @@
 import re
 
 trainer_group_pointer_table_address    = 0x39999
-trianer_group_pointer_table_address_gs = 0x3993E
+trainer_group_pointer_table_address_gs = 0x3993E
 
 # TODO: check if "é", ".", "♂", "♀" are okay in the output
 trainer_group_names = {
@@ -102,268 +102,3 @@
 
 # remove [Blue] from each trainer group name
 remove_parentheticals_from_trainer_group_names()
-
-class TrainerGroupTable:
-    """ A list of pointers.
-    """
-
-    def __init__(self):
-        assert 0x43 in trainer_group_maximums.keys(), "TrainerGroupTable should onyl be created after all the trainers have been found"
-        self.address = trainer_group_pointer_table_address
-        self.bank = calculate_bank(trainer_group_pointer_table_address)
-        self.label = Label(name="TrainerGroupPointerTable", address=self.address, object=self)
-        self.size = None
-        self.last_address = None
-        self.dependencies = None
-        self.headers = []
-        self.parse()
-
-        # TODO: add this to script_parse_table
-        #script_parse_table[address : self.last_address] = self
-
-    def get_dependencies(self, recompute=False, global_dependencies=set()):
-        global_dependencies.update(self.headers)
-        if recompute == True and self.dependencies != None and self.dependencies != []:
-            return self.dependencies
-        dependencies = [self.headers]
-        for header in self.headers:
-            dependencies += header.get_dependencies(recompute=recompute, global_dependencies=global_dependencies)
-        return dependencies
-
-    def parse(self):
-        size = 0
-        for (key, kvalue) in trainer_group_names.items():
-            # calculate the location of this trainer group header from its pointer
-            pointer_bytes_location = kvalue["pointer_address"]
-            parsed_address = calculate_pointer_from_bytes_at(pointer_bytes_location, bank=self.bank)
-            trainer_group_names[key]["parsed_address"] = parsed_address
-
-            # parse the trainer group header at this location
-            name = kvalue["name"]
-            trainer_group_header = TrainerGroupHeader(address=parsed_address, group_id=key, group_name=name)
-            trainer_group_names[key]["header"] = trainer_group_header
-            self.headers.append(trainer_group_header)
-
-            # keep track of the size of this pointer table
-            size += 2
-        self.size = size
-        self.last_address = self.address + self.size
-
-    def to_asm(self):
-        output = "".join([str("dw "+get_label_for(header.address)+"\n") for header in self.headers])
-        return output
-
-class TrainerGroupHeader:
-    """
-    A trainer group header is a repeating list of individual trainer headers.
-
-    <Trainer Name> <0x50> <Data type> <Pokémon Data>+ <0xFF>
-
-    Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers.
-    Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders.
-    Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans.
-    Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>. Used by a few Cooltrainers.
-    """
-
-    def __init__(self, address=None, group_id=None, group_name=None):
-        assert address!=None, "TrainerGroupHeader requires an address"
-        assert group_id!=None, "TrainerGroupHeader requires a group_id"
-        assert group_name!=None, "TrainerGroupHeader requires a group_name"
-
-        self.address = address
-        self.group_id = group_id
-        self.group_name = group_name
-        self.dependencies = None
-        self.individual_trainer_headers = []
-        self.label = Label(name=group_name+"TrainerGroupHeader", address=self.address, object=self)
-        self.parse()
-
-        # TODO: add this to script_parse_table
-        #script_parse_table[address : self.last_address] = self
-
-    def get_dependencies(self, recompute=False, global_dependencies=set()):
-        """ TrainerGroupHeader has no dependencies.
-        """
-        # TODO: possibly include self.individual_trainer_headers
-        if recompute or self.dependencies == None:
-            self.dependencies = []
-        return self.dependencies
-
-    def parse(self):
-        """
-        how do i know when there's no more data for this header?
-         do a global analysis of the rom and figure out the max ids
-         this wont work for rom hacks of course
-        see find_trainer_ids_from_scripts
-        """
-        size = 0
-        current_address = self.address
-
-        # create an IndividualTrainerHeader for each id in range(min id, max id + 1)
-        min_id = min(trainer_group_maximums[self.group_id])
-        max_id = max(trainer_group_maximums[self.group_id])
-
-        for trainer_id in range(min_id, max_id+1):
-            trainer_header = TrainerHeader(address=current_address, trainer_group_id=self.group_id, trainer_id=trainer_id, parent=self)
-            current_address += trainer_header.size
-            size += trainer_header.size
-
-        self.last_address = current_address
-        self.size = size
-
-    def to_asm(self):
-        raise NotImplementedError
-        output += "
-
-class TrainerHeader:
-    """
-    <Trainer Name> <0x50> <Data type> <Pokémon Data>+ <0xFF>
-
-    Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers.
-    Data type <0x01>: Pokémon Data is <Level> <Pokémon> <Move1> <Move2> <Move3> <Move4>. Used often for Gym Leaders.
-    Data type <0x02>: Pokémon Data is <Level> <Pokémon> <Held Item>. Used mainly by Pokéfans.
-    Data type <0x03>: Pokémon Data is <Level> <Pokémon> <Held Item> <Move1> <Move2> <Move3> <Move4>. Used by a few Cooltrainers.
-    """
-
-    def __init__(self, address=None, trainer_group_id=None, trainer_id=None, parent=None):
-        self.parent = parent
-        self.address = address
-        self.trainer_group_id = trainer_group_id
-        self.trainer_id = trainer_id
-        self.dependencies = []
-        self.size = None
-        self.last_address = None
-        self.parse()
-        self.label = Label(name=self.make_name(), address=self.address, object=self)
-        # this shouldn't be added to script_parse_table because
-        # TrainerGroupHeader covers its address range
-
-    def make_name(self):
-        """ Must occur after parse() is called.
-        Constructs a name based on self.parent.group_name and self.name.
-        """
-        return self.parent.group_name + "_" + self.name
-
-    def get_dependencies(self, recompute=False, global_dependencies=set()):
-        if recompute or self.dependencies == None:
-            self.dependencies = []
-        return self.dependencies
-
-    def parse(self):
-        address = self.address
-
-        # figure out how many bytes until 0x50 "@"
-        jump = how_many_until(chr(0x50), address)
-
-        # parse the "@" into the name
-        self.name = parse_text_at(address, jump+1)
-
-        # where is the next byte?
-        current_address = address + jump + 1
-
-        # figure out the pokemon data type
-        self.data_type = ord(rom[current_address])
-
-        current_address += 1
-
-        # figure out which partymon parser to use for this trainer header
-        party_mon_parser = None
-        for monparser in trainer_party_mon_parsers:
-            if monparser.id == self.data_type:
-                party_mon_parser = monparser
-                break
-
-        if party_mon_parser == None:
-            raise Exception, "no trainer party mon parser found to parse data type " + hex(self.data_type)
-
-        self.party_mons = party_mon_parser(address=current_address, group_id=self.trainer_group_id, trainer_id=self.trainer_id, parent=self)
-
-        # let's have everything in trainer_party_mon_parsers handle the last $FF
-        self.size = self.party_mons.size + 1 + len(self.name)
-        self.last_address = self.party_mons.last_address
-
-    def to_asm(self):
-        output = "db \""+self.name+"\"\n"
-        output += "; data type\n"
-        output += "db $%.2x\n"%(self.data_byte)
-        output += self.party_mons.to_asm()
-        return output
-
-class TrainerPartyMonParser:
-    """ Just a generic trainer party mon parser.
-    Don't use this directly. Only use the child classes.
-    """
-    id = None
-    dependencies = None
-    params = []
-    param_types = None
-
-    # could go either way on this one.. TrainerGroupHeader.parse would need to be changed
-    # so as to not increase current_address by one after reading "data_type"
-    override_byte_check = True
-
-    def __init__(self, address=None, group_id=None, trainer_id=None, parent=None):
-        self.address = address
-        self.group_id = group_id
-        self.trainer_id = trainer_id
-        self.parent = parent
-        self.parse()
-
-        # pick up the $FF at the end
-        self.size += 1
-        self.last_address += 1
-
-    parse = Command.parse
-
-    def to_asm(self):
-        output = "; " + ", ".join([param_type["name"] for param_type in self.param_types]) + "\n"
-        output += "db " + ", ".join([param.to_asm() for (name, param) in self.params.items()])
-        output += "\n"
-        output += "db $FF ; end trainer party mons"
-        return output
-
-class TrainerPartyMonParser0(TrainerPartyMonParser):
-    """ Data type <0x00>: Pokémon Data is <Level> <Species>. Used by most trainers. """
-    id = 0
-    size = 2 + 1
-    param_types = {
-        0: {"name": "level", "class": DecimalParam},
-        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."""
-    id = 1
-    size = 6 + 1
-    param_types = {
-        0: {"name": "level", "class": DecimalParam},
-        1: {"name": "species", "class": PokemonParam},
-        2: {"name": "move1", "class": MoveParam},
-        3: {"name": "move2", "class": MoveParam},
-        4: {"name": "move3", "class": MoveParam},
-        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. """
-    id = 2
-    size = 3 + 1
-    param_types = {
-        0: {"name": "level", "class": DecimalParam},
-        1: {"name": "species", "class": PokemonParam},
-        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. """
-    id = 3
-    size = 7 + 1
-    param_types = {
-        0: {"name": "level", "class": DecimalParam},
-        1: {"name": "species", "class": PokemonParam},
-        2: {"name": "item", "class": ItemLabelByte},
-        3: {"name": "move1", "class": MoveParam},
-        4: {"name": "move2", "class": MoveParam},
-        5: {"name": "move3", "class": MoveParam},
-        6: {"name": "move4", "class": MoveParam},
-    }
-
-trainer_party_mon_parsers = [TrainerPartyMonParser0, TrainerPartyMonParser1, TrainerPartyMonParser2, TrainerPartyMonParser3]