diff options
author | Bryan Bishop <kanzure@gmail.com> | 2013-11-10 19:49:39 -0600 |
---|---|---|
committer | Bryan Bishop <kanzure@gmail.com> | 2013-11-10 19:49:39 -0600 |
commit | 9310699565243442a8c363012b8bb09f000ddf29 (patch) | |
tree | a944de54e4609ae45400febe0232e956f0f00c55 | |
parent | d1da18652d96212c05be10a9dfb22bc02071f84e (diff) |
write script to ROM and execute
This handles all of the usual tasks that will be required for injecting
and running custom scripts.
-rw-r--r-- | pokemontools/vba/vba.py | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index 9d2be23..0d678da 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -838,7 +838,7 @@ class crystal(object): self.text_wait() loop_limit -= 1 - def start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): + def broken_start_trainer_battle(self, trainer_group=0x1, trainer_id=0x1): """ This will fail after the first mon is defeated. """ @@ -871,6 +871,86 @@ class crystal(object): self.vba.memory = memory + def inject_script_into_rom(self, asm=[0x91], rom_address=0x75 * 0x4000, wram_address=0xd280, limit=50): + """ + Writes a script to the ROM in a blank location. Calls call_script to + make the game engine aware of the script. Then executes the script and + looks for confirmation thta the script has started to run. + + The script must end itself. + + :param asm: scripting command bytes + :param rom_address: rom location to write asm to + :param wram_address: temporary storage for indicating if the script has + started yet + :param limit: number of frames to emulate before giving up on the start + script + """ + execution_pending = 0 + execution_started = 1 + valid_execution_states = (execution_pending, execution_started) + + # location for byte for whether script has started executing + execution_indicator_address = wram_address + + # backup whatever exists at the current wram location + backup_wram = self.vba.read_memory_at(execution_indicator_address) + + # .. and set it to "pending" + self.vba.write_memory_at(execution_indicator_address, execution_pending) + + # initial script that runs first to tell python that execution started + execution_indicator_script = [ + # writebyte to say that the script has executed + 0x15, execution_started, + + # copyvartobyte + 0x1a, execution_indicator_address & 0xff, execution_indicator_address >> 8, + ] + + # make the indicator script run before the user script + full_script = execution_indicator_script + asm + + # inject the asm + rom = list(self.vba.rom) + rom[rom_address : rom_address + len(full_script)] = full_script + + # set the rom with the injected bytes + self.vba.rom = rom + + # setup the script for execution + self.call_script(rom_address) + + status = execution_pending + while status != execution_started and limit > 0: + # emulator time travel + self.vba.step(count=1) + + # get latest wram + status = self.vba.read_memory_at(execution_indicator_address) + if status not in valid_execution_states: + raise Exception( + "The execution indicator at {addr} has invalid state {value}".format( + addr=hex(execution_indicator_address), + value=status, + ) + ) + elif status == execution_started: + break # hooray + + limit -= 1 + + if status == execution_pending and limit == 0: + raise Exception( + "Emulation timeout while waiting for script to start." + ) + + # The script has started so it's okay to reset wram back to whatever it + # was. + self.vba.write_memory_at(execution_indicator_address, backup_wram) + + return True + def givepoke(self, pokemon_id, level, nickname=None): """ Give the player a pokemon. |