diff options
Diffstat (limited to 'pokemontools/vba/vba.py')
| -rw-r--r-- | pokemontools/vba/vba.py | 601 | 
1 files changed, 379 insertions, 222 deletions
| diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index d7fdf1d..3367dee 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -9,115 +9,160 @@ import re  import string  from copy import copy -import unittest -  # for converting bytes to readable text -from pokemontools.chars import chars +from pokemontools.chars import ( +    chars, +) -from pokemontools.map_names import map_names +from pokemontools.map_names import ( +    map_names, +)  import keyboard  # just use a default config for now until the globals are removed completely -import pokemontools.config as conf -config = conf.Config() -project_path = config.path -save_state_path = config.save_state_path -rom_path = config.rom_path - -if not os.path.exists(rom_path): -    raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) +import pokemontools.configuration as configuration  import vba_wrapper -vba = vba_wrapper.VBA(rom_path) -registers = vba_wrapper.core.registers.Registers(vba) -  button_masks = vba_wrapper.core.VBA.button_masks  button_combiner = vba_wrapper.core.VBA.button_combine  def translate_chars(charz): +    """ +    Translate a string from the in-game format to readable form. This is +    accomplished through the same lookup table that the preprocessors use. +    """      result = ""      for each in charz:          result += chars[each]      return result -def press(buttons, holdsteps=1, aftersteps=1): +class crystal(object):      """ -    Press a button. - -    Use steplimit to say for how many steps you want to press -    the button (try leaving it at the default, 1). -    """ -    if hasattr(buttons, "__len__"): -        number = button_combiner(buttons) -    elif isinstance(buttons, int): -        number = buttons -    else: -        number = buttons -    for step_counter in range(0, holdsteps): -        Gb.step(number) - -    # clear the button press -    if aftersteps > 0: -        for step_counter in range(0, aftersteps): -            Gb.step(0) - -def call(bank, address): +    Just a simple namespace to store a bunch of functions for Pokémon Crystal. +    There can only be one running instance of the emulator per process because +    it's a poorly written shared library.      """ -    Jumps into a function at a certain address. -    Go into the start menu, pause the game and try call(1, 0x1078) to see a -    string printed to the screen. -    """ -    push = [ -        registers.pc, -        registers.hl, -        registers.de, -        registers.bc, -        registers.af, -        0x3bb7, -    ] - -    for value in push: -        registers.sp -= 2 -        set_memory_at(registers.sp + 1, value >> 8) -        set_memory_at(registers.sp, value & 0xFF) -        if get_memory_range(registers.sp, 2) != [value & 0xFF, value >> 8]: -            print "desired memory values: " + str([value & 0xFF, value >> 8] ) -            print "actual memory values: " + str(get_memory_range(registers.sp , 2)) -            print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(get_memory_at(registers.sp)) - -    if bank != 0: -        registers["af"] = (bank << 8) | (registers["af"] & 0xFF) -        registers["hl"] = address -        registers["pc"] = 0x2d63 # FarJump -    else: -        registers["pc"] = address - -def get_stack(): -    """ -    Return a list of functions on the stack. -    """ -    addresses = [] -    sp = registers.sp +    def __init__(self, config=None): +        """ +        Launch the VBA controller. +        """ +        if not config: +            config = configuration.Config() -    for x in range(0, 11): -        sp = sp - (2 * x) -        hi = get_memory_at(sp + 1) -        lo = get_memory_at(sp) -        address = ((hi << 8) | lo) -        addresses.append(address) +        self.config = config -    return addresses +        self.vba = vba_wrapper.VBA(self.config.rom_path) +        self.registers = vba_wrapper.core.registers.Registers(self.vba) -class crystal: -    """ -    Just a simple namespace to store a bunch of functions for Pokémon Crystal. -    """ +        if not os.path.exists(self.config.rom_path): +            raise Exception("rom_path is not configured properly; edit vba_config.py? " + str(rom_path)) + +    def shutdown(self): +        """ +        Reset the emulator. +        """ +        self.vba.shutdown() + +    def save_state(self, name, state=None, override=False): +        """ +        Saves the given state to save_state_path. + +        The file format must be ".sav", and this will be appended to your +        string if necessary. +        """ +        if state == None: +            state = self.vba.state + +        if len(name) < 4 or name[-4:] != ".sav": +            name += ".sav" + +        save_path = os.path.join(self.config.save_state_path, name) + +        if not override and os.path.exists(save_path): +            raise Exception("oops, save state path already exists: {0}".format(save_path)) + +        with open(save_path, "wb") as file_handler: +            file_handler.write(state) + +    def load_state(self, name, loadit=True): +        """ +        Read a state from file based on the name of the state. + +        Looks in save_state_path for a file with this name (".sav" is +        optional). + +        @param loadit: whether or not to set the emulator to this state +        """ +        save_path = os.path.join(self.config.save_state_path, name) + +        if not os.path.exists(save_path): +            if len(name) < 4 or name[-4:] != ".sav": +                name += ".sav" +                save_path = os.path.join(self.config.save_state_path, name) + +        with open(save_path, "rb") as file_handler: +            state = file_handler.read() + +        if loadit: +            self.vba.state = state + +        return state + +    def call(self, bank, address): +        """ +        Jumps into a function at a certain address. + +        Go into the start menu, pause the game and try call(1, 0x1078) to see a +        string printed to the screen. +        """ +        push = [ +            self.registers.pc, +            self.registers.hl, +            self.registers.de, +            self.registers.bc, +            self.registers.af, +            0x3bb7, +        ] + +        self.push_stack(push) + +        if bank != 0: +            self.registers["af"] = (bank << 8) | (self.registers["af"] & 0xFF) +            self.registers["hl"] = address +            self.registers["pc"] = 0x2d63 # FarJump +        else: +            self.registers["pc"] = address + +    def push_stack(self, push): +        for value in push: +            self.registers["sp"] -= 2 +            self.vba.write_memory_at(self.registers.sp + 1, value >> 8) +            self.vba.write_memory_at(self.registers.sp, value & 0xFF) +            if list(self.vba.memory[self.registers.sp : self.registers.sp + 2]) != [value & 0xFF, value >> 8]: +                print "desired memory values: " + str([value & 0xFF, value >> 8] ) +                print "actual memory values: " + str(list(self.vba.memory[self.registers.sp : self.registers.sp + 2])) +                print "wrong value at " + hex(self.registers.sp) + " expected " + hex(value) + " but got " + hex(self.vba.read_memory_at(self.registers.sp)) -    @staticmethod -    def text_wait(step_size=1, max_wait=200, sfx_limit=0, debug=False, callback=None): +    def get_stack(self): +        """ +        Return a list of functions on the stack. +        """ +        addresses = [] +        sp = self.registers.sp + +        for x in range(0, 11): +            sp = sp - (2 * x) +            hi = self.vba.read_memory_at(sp + 1) +            lo = self.vba.read_memory_at(sp) +            address = ((hi << 8) | lo) +            addresses.append(address) + +        return addresses + +    def text_wait(self, step_size=1, max_wait=200, sfx_limit=0, debug=False, callback=None):          """          Presses the "A" button when text is done being drawn to screen. @@ -134,22 +179,22 @@ class crystal:          :param max_wait: number of wait loops to perform          """          while max_wait > 0: -            hi = get_memory_at(registers.sp + 1) -            lo = get_memory_at(registers.sp) +            hi = self.vba.read_memory_at(self.registers.sp + 1) +            lo = self.vba.read_memory_at(self.registers.sp)              address = ((hi << 8) | lo)              if address in range(0xa1b, 0xa46) + range(0xaaf, 0xaf5): #  0xaef:                  print "pressing, then breaking.. address is: " + str(hex(address))                  # set CurSFX -                set_memory_at(0xc2bf, 0) +                self.vba.write_memory_at(0xc2bf, 0) -                press("a", holdsteps=10, aftersteps=1) +                self.vba.press("a", hold=10, after=1)                  # check if CurSFX is SFX_READ_TEXT_2 -                if get_memory_at(0xc2bf) == 0x8: +                if self.vba.read_memory_at(0xc2bf) == 0x8:                      print "cursfx is set to SFX_READ_TEXT_2, looping.." -                    return crystal.text_wait(step_size=step_size, max_wait=max_wait, debug=debug, callback=callback, sfx_limit=sfx_limit) +                    return self.text_wait(step_size=step_size, max_wait=max_wait, debug=debug, callback=callback, sfx_limit=sfx_limit)                  else:                      if sfx_limit > 0:                          sfx_limit = sfx_limit - 1 @@ -160,7 +205,7 @@ class crystal:                          break              else: -                stack = get_stack() +                stack = self.get_stack()                  # yes/no box or the name selection box                  if address in range(0xa46, 0xaaf): @@ -179,14 +224,14 @@ class crystal:                      break                  else: -                    nstep(step_size) +                    self.vba.step(count=step_size)              # if there is a callback, then call the callback and exit when the              # callback returns True. This is especially useful during the              # OakSpeech intro where textboxes are running constantly, and then              # suddenly the player can move around. One way to detect that is to              # set callback to a function that returns -            # "vba.get_memory_at(0xcfb1) != 0". +            # "vba.read_memory_at(0xcfb1) != 0".              if callback != None:                  result = callback()                  if result == True: @@ -202,17 +247,15 @@ class crystal:          if max_wait == 0:              print "max_wait was hit" -    @staticmethod -    def walk_through_walls_slow(): -        memory = get_memory() +    def walk_through_walls_slow(self): +        memory = self.vba.memory          memory[0xC2FA] = 0          memory[0xC2FB] = 0          memory[0xC2FC] = 0          memory[0xC2FD] = 0 -        set_memory(memory) +        self.vba.memory = memory -    @staticmethod -    def walk_through_walls(): +    def walk_through_walls(self):          """          Lets the player walk all over the map. @@ -221,73 +264,65 @@ class crystal:          to be executed each step/tick if continuous walk-through-walls          is desired.          """ -        set_memory_at(0xC2FA, 0) -        set_memory_at(0xC2FB, 0) -        set_memory_at(0xC2FC, 0) -        set_memory_at(0xC2FD, 0) +        self.vba.write_memory_at(0xC2FA, 0) +        self.vba.write_memory_at(0xC2FB, 0) +        self.vba.write_memory_at(0xC2FC, 0) +        self.vba.write_memory_at(0xC2FD, 0)      #@staticmethod      #def set_enemy_level(level): -    #    set_memory_at(0xd213, level) +    #    vba.write_memory_at(0xd213, level) -    @staticmethod -    def nstep(steplimit=500): +    def nstep(self, steplimit=500):          """          Steps the CPU forward and calls some functions in between each step.          (For example, to manipulate memory.) This is pretty slow.          """          for step_counter in range(0, steplimit): -            crystal.walk_through_walls() +            self.walk_through_walls()              #call(0x1, 0x1078) -            step() +            self.vba.step() -    @staticmethod -    def disable_triggers(): -        set_memory_at(0x23c4, 0xAF) -        set_memory_at(0x23d0, 0xAF); +    def disable_triggers(self): +        self.vba.write_memory_at(0x23c4, 0xAF) +        self.vba.write_memory_at(0x23d0, 0xAF); -    @staticmethod -    def disable_callbacks(): -        set_memory_at(0x23f2, 0xAF) -        set_memory_at(0x23fe, 0xAF) +    def disable_callbacks(self): +        self.vba.write_memory_at(0x23f2, 0xAF) +        self.vba.write_memory_at(0x23fe, 0xAF) -    @staticmethod -    def get_map_group_id(): +    def get_map_group_id(self):          """          Returns the current map group.          """ -        return get_memory_at(0xdcb5) +        return self.vba.read_memory_at(0xdcb5) -    @staticmethod -    def get_map_id(): +    def get_map_id(self):          """          Returns the map number of the current map.          """ -        return get_memory_at(0xdcb6) +        return self.vba.read_memory_at(0xdcb6) -    @staticmethod -    def get_map_name(): +    def get_map_name(self, map_names=map_names):          """          Figures out the current map name.          """ -        map_group_id = crystal.get_map_group_id() -        map_id = crystal.get_map_id() +        map_group_id = self.get_map_group_id() +        map_id = self.get_map_id()          return map_names[map_group_id][map_id]["name"] -    @staticmethod -    def get_xy(): +    def get_xy(self):          """          (x, y) coordinates of player on map.          Relative to top-left corner of map.          """ -        x = get_memory_at(0xdcb8) -        y = get_memory_at(0xdcb7) +        x = self.vba.read_memory_at(0xdcb8) +        y = self.vba.read_memory_at(0xdcb7)          return (x, y) -    @staticmethod -    def menu_select(id=1): +    def menu_select(self, id=1):          """          Sets the cursor to the given pokemon in the player's party. @@ -296,38 +331,34 @@ class crystal:          This probably works on other menus.          """ -        set_memory_at(0xcfa9, id) +        self.vba.write_memory_at(0xcfa9, id) -    @staticmethod -    def is_in_battle(): +    def is_in_battle(self):          """          Checks whether or not we're in a battle.          """ -        return (get_memory_at(0xd22d) != 0) or crystal.is_in_link_battle() +        return (self.vba.read_memory_at(0xd22d) != 0) or self.is_in_link_battle() -    @staticmethod -    def is_in_link_battle(): -        return get_memory_at(0xc2dc) != 0 +    def is_in_link_battle(self): +        return self.vba.read_memory_at(0xc2dc) != 0 -    @staticmethod -    def unlock_flypoints(): +    def unlock_flypoints(self):          """          Unlocks different destinations for flying.          Note: this might start at 0xDCA4 (minus one on all addresses), but not          sure.          """ -        set_memory_at(0xDCA5, 0xFF) -        set_memory_at(0xDCA6, 0xFF) -        set_memory_at(0xDCA7, 0xFF) -        set_memory_at(0xDCA8, 0xFF) +        self.vba.write_memory_at(0xDCA5, 0xFF) +        self.vba.write_memory_at(0xDCA6, 0xFF) +        self.vba.write_memory_at(0xDCA7, 0xFF) +        self.vba.write_memory_at(0xDCA8, 0xFF) -    @staticmethod -    def get_gender(): +    def get_gender(self):          """          Returns 'male' or 'female'.          """ -        gender = get_memory_at(0xD472) +        gender = self.vba.read_memory_at(0xD472)          if gender == 0:              return "male"          elif gender == 1: @@ -335,54 +366,59 @@ class crystal:          else:              return gender -    @staticmethod -    def get_player_name(): +    def get_player_name(self):          """          Returns the 7 characters making up the player's name.          """ -        bytez = get_memory_range(0xD47D, 7) +        bytez = self.vba.memory[0xD47D:0xD47D + 7]          name = translate_chars(bytez)          return name -    @staticmethod -    def warp(map_group_id, map_id, x, y): -        set_memory_at(0xdcb5, map_group_id) -        set_memory_at(0xdcb6, map_id) -        set_memory_at(0xdcb7, y) -        set_memory_at(0xdcb8, x) -        set_memory_at(0xd001, 0xFF) -        set_memory_at(0xff9f, 0xF1) -        set_memory_at(0xd432, 1) -        set_memory_at(0xd434, 0 & 251) - -    @staticmethod -    def warp_pokecenter(): -        crystal.warp(1, 1, 3, 3) -        crystal.nstep(200) - -    @staticmethod -    def masterballs(): +    def warp(self, map_group_id, map_id, x, y): +        """ +        Warp into another map. +        """ +        self.vba.write_memory_at(0xdcb5, map_group_id) +        self.vba.write_memory_at(0xdcb6, map_id) +        self.vba.write_memory_at(0xdcb7, y) +        self.vba.write_memory_at(0xdcb8, x) +        self.vba.write_memory_at(0xd001, 0xFF) +        self.vba.write_memory_at(0xff9f, 0xF1) +        self.vba.write_memory_at(0xd432, 1) +        self.vba.write_memory_at(0xd434, 0 & 251) + +    def warp_pokecenter(self): +        """ +        Warp straight into a pokecenter. +        """ +        self.warp(1, 1, 3, 3) +        self.nstep(200) + +    def masterballs(self): +        """ +        Deposit some pokeballs into the first few slots of the pack. This +        overrides whatever items were previously there. +        """          # masterball -        set_memory_at(0xd8d8, 1) -        set_memory_at(0xd8d9, 99) +        self.vba.write_memory_at(0xd8d8, 1) +        self.vba.write_memory_at(0xd8d9, 99)          # ultraball -        set_memory_at(0xd8da, 2) -        set_memory_at(0xd8db, 99) +        self.vba.write_memory_at(0xd8da, 2) +        self.vba.write_memory_at(0xd8db, 99)          # pokeballs -        set_memory_at(0xd8dc, 5) -        set_memory_at(0xd8dd, 99) +        self.vba.write_memory_at(0xd8dc, 5) +        self.vba.write_memory_at(0xd8dd, 99) -    @staticmethod -    def get_text(): +    def get_text(self, chars=chars):          """          Returns alphanumeric text on the screen.          Other characters will not be shown.          """          output = "" -        tiles = get_memory_range(0xc4a0, 1000) +        tiles = self.vba.memory[0xc4a0:0xc4a0 + 1000]          for each in tiles:              if each in chars.keys():                  thing = chars[each] @@ -405,32 +441,69 @@ class crystal:          return output -    @staticmethod -    def keyboard_apply(button_sequence): +    def is_showing_stats_screen(self): +        """ +        This is meant to detect whether or not the stats screen is showing. +        This is the menu that pops up after leveling up. +        """ +        # These words must be on the screen if the stats screen is currently +        # displayed. +        parts = [ +            "ATTACK", +            "DEFENSE", +            "SPCL.ATK", +            "SPCL.DEF", +            "SPEED", +        ] + +        # get the current text on the screen +        text = self.get_text() + +        if all([part in text for part in parts]): +            return True +        else: +            return False + +    def handle_stats_screen(self, force=False): +        """ +        Attempts to bypass a stats screen. Set force=True if you want to make +        the attempt regardless of whether or not the system thinks a stats +        screen is showing. +        """ +        if self.is_showing_stats_screen() or force: +            self.vba.press("a") +            self.vba.step(count=20) + +    def keyboard_apply(self, button_sequence):          """          Applies a sequence of buttons to the on-screen keyboard.          """          for buttons in button_sequence: -            press(buttons) -            nstep(2) -            press([]) +            self.vba.press(buttons) + +            if buttons == "select": +                self.vba.step(count=5) +            else: +                self.vba.step(count=2) -    @staticmethod -    def write(something="TrAiNeR"): +            self.vba.press([]) + +    def write(self, something="TrAiNeR"):          """          Types out a word.          Uses a planning algorithm to do this in the most efficient way possible.          """          button_sequence = keyboard.plan_typing(something) -        crystal.keyboard_apply([[x] for x in button_sequence]) +        self.vba.step(count=10) +        self.keyboard_apply([[x] for x in button_sequence]) +        return button_sequence -    @staticmethod -    def set_partymon2(): +    def set_partymon2(self):          """          This causes corruption, so it's not working yet.          """ -        memory = get_memory() +        memory = self.vba.memory          memory[0xdcd7] = 2          memory[0xdcd9] = 0x7 @@ -464,19 +537,18 @@ class crystal:          memory[0xdd33] = 0x10          memory[0xdd34] = 0x40 -        set_memory(memory) +        self.vba.memory = memory -    @staticmethod -    def wait_for_script_running(debug=False, limit=1000): +    def wait_for_script_running(self, debug=False, limit=1000):          """          Wait until ScriptRunning isn't -1.          """          while limit > 0: -            if get_memory_at(0xd438) != 255: +            if self.vba.read_memory_at(0xd438) != 255:                  print "script is done executing"                  return              else: -                step() +                self.vba.step()              if debug:                  limit = limit - 1 @@ -484,44 +556,129 @@ class crystal:          if limit == 0:              print "limit ran out" -    @staticmethod -    def move(cmd): +    def move(self, cmd):          """          Attempt to move the player.          """ -        press(cmd, holdsteps=10, aftersteps=0) -        press([]) +        if isinstance(cmd, list): +            for command in cmd: +                self.move(command) +        else: +            self.vba.press(cmd, hold=10, after=0) +            self.vba.press([]) + +            memory = self.vba.memory +            #while memory[0xd4e1] == 2 and memory[0xd042] != 0x3e: +            while memory[0xd043] in [0, 1, 2, 3]: +            #while memory[0xd043] in [0, 1, 2, 3] or memory[0xd042] != 0x3e: +                self.vba.step(count=10) +                memory = self.vba.memory + +    def get_enemy_hp(self): +        """ +        Returns the HP of the current enemy. +        """ +        hp = ((self.vba.memory[0xd218] << 8) | self.vba.memory[0xd217]) +        return hp + +    def start_trainer_battle_lamely(self, map_group=0x1, map_id=0xc, x=6, y=8, direction="l", loop_limit=10): +        """ +        Starts a trainer battle by warping into a map at the designated +        coordinates, pressing the direction button for a full walk step (which +        ideally should be blocked, this is mainly to establish direction), and +        then pressing "a" to initiate the trainer battle. +        """ +        self.warp(map_group, map_id, x, y) + +        # finish loading the map, might not be necessary? +        self.nstep(100) -        memory = get_memory() -        #while memory[0xd4e1] == 2 and memory[0xd042] != 0x3e: -        while memory[0xd043] in [0, 1, 2, 3]: -        #while memory[0xd043] in [0, 1, 2, 3] or memory[0xd042] != 0x3e: -            nstep(10) -            memory = get_memory() +        # face towards the trainer (or whatever direction was specified). If +        # this direction is blocked, then this will only change which direction +        # the character is facing. However, if this direction is not blocked by +        # the map or by an npc, then this will cause an entire step to be +        # taken. +        self.vba.press([direction]) -class TestEmulator(unittest.TestCase): -    def test_PlaceString(self): -        call(0, 0x1078) +        # talk to the trainer, don't assume line of sight will be triggered +        self.vba.press(["a"]) +        self.vba.press([]) + +        # trainer might talk, skip any text until the player can choose moves +        while not self.is_in_battle() and loop_limit > 0: +            self.text_wait() +            loop_limit -= 1 + +    def broken_start_random_battle(self): +        self.push_stack([self.registers.pc]) +        self.registers["pc"] = address % 0x4000 +        self.call(address / 0x4000, address % 0x4000) + +    def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): +        """ +        This will fail after the first mon is defeated. +        """ +        Script_startbattle_address = 0x97436 + +        # setup the battle +        memory = self.vba.memory +        memory[0xd459] = 0x81 +        memory[0xd22f] = trainer_group +        memory[0xd231] = trainer_id +        self.vba.memory = memory + +        self.call(0x25, (Script_startbattle_address % 0x4000) + 0x4000) + +    def broken_start_random_battle_by_rocksmash_battle_script(self): +        """ +        This doesn't start a battle. +        """ +        CallScript_address = 0x261f +        RockSmashBattleScript_address = 0x97cf9 +        ScriptRunning = 0xd438 +        ScriptBank = 0xd439 +        ScriptPos = 0xd43a -        # where to draw the text -        registers["hl"] = 0xc4a0 +        memory = self.vba.memory +        memory[ScriptBank] = RockSmashBattleScript_address / 0x4000 +        memory[ScriptPos] = ((RockSmashBattleScript_address % 0x4000 + 0x4000) & 0xff00) >> 8 +        memory[ScriptPos+1] = ((RockSmashBattleScript_address % 0x4000 + 0x4000) & 0xff) +        memory[ScriptRunning] = 0xff +        self.vba.memory = memory -        # what text to read from -        registers["de"] = 0x1276 +        self.vba.registers["af"] = ((RockSmashBattleScript_address / 0x4000) << 8) | (self.vba.registers.af & 0xff) +        self.vba.registers["hl"] = (RockSmashBattleScript_address % 0x4000) + 0x4000 +        self.call(0x0, CallScript_address) -        nstep(10) +    #def attempt_start_battle_by_startbattle(self): +    #    StartBattle_address = 0x3f4c1 +    #    self.call(StartBattle_address / 0x4000, (StartBattle_address % 0x4000) + 0x4000) -        text = crystal.get_text() +    #def attempt_start_random_battle_by_wild_battle(self): +    #    start_wild_battle = 0x3f4dd +    #    #self.call(start_wild_battle / 0x4000, start_wild_battle % 0x4000) +    #    #self.vba.registers["pc"] = ... -        self.assertTrue("TRAINER" in text) +    def old_crap(self): +        CallScript_address = 0x261f +        RockSmashBattleScript_address = 0x97cf9 +        RockSmashEncounter_address = 0x97cc0 +        StartBattle_address = 0x3f4c1 +        ScriptRunning = 0xd438 +        ScriptBank = 0xd439 +        ScriptPos = 0xd43a +        start_wild_battle = 0x3f4dd +        script = 0x1a1dc6 -class TestWriter(unittest.TestCase): -    def test_very_basic(self): -        button_sequence = keyboard.plan_typing("an") -        expected_result = ["select", "a", "d", "r", "r", "r", "r", "a"] +        #self.call(StartBattle_address / 0x4000, StartBattle_address % 0x4000) +        #self.call(RockSmashEncounter_address / 0x4000, RockSmashEncounter_address % 0x4000) -        self.assertEqual(len(expected_result), len(button_sequence)) -        self.assertEqual(expected_result, button_sequence) +        #self.push_stack([self.registers.pc]) +        #memory[ScriptBank] = script / 0x4000 +        #memory[ScriptPos] = ((script % 0x4000) & 0xff00) >> 8 +        #memory[ScriptPos+1] = ((script % 0x4000) & 0xff) +        #memory[ScriptRunning] = 0xff + +        #self.call(start_wild_battle / 0x4000, start_wild_battle % 0x4000) -if __name__ == "__main__": -    unittest.main() +        #self.vba.memory = memory | 
