various helper functions for the vba interface

New vba.py features include:
* get_memory_at
* get_memory_range
* set_memory_at

Also, the "crystal" class has a number of specialty helpers:
* crystal.walk_through_walls
* crystal.get_player_name
* crystal.get_map_name
* crystal.get_xy
* crystal.nstep (which sets memory each step by calling certain
* functions, like walk_through_walls)
* crystal.is_in_battle
* crystal.get_gender
This commit is contained in:
Bryan Bishop 2013-02-26 21:49:54 -06:00
parent 160acfa296
commit ec098d1a03

View File

@ -54,12 +54,13 @@ Usage (in jython, not python):
# or why not the other way around? # or why not the other way around?
vba.set_state(vba.load_state("unknown-delete-me")) vba.set_state(vba.load_state("unknown-delete-me"))
registers = vba.get_registers() vba.get_memory_at(0xDCDA)
vba.set_memory_at(0xDCDB, 0xFF)
vba.get_memory_range(0xDCDA, 10)
TOOD: TOOD:
[ ] set a specific register [ ] set a specific register
[ ] get a specific register [ ] get a specific register
[ ] write value at address
[ ] breakpoints [ ] breakpoints
[ ] vgm stuff [ ] vgm stuff
[ ] gbz80disasm integration [ ] gbz80disasm integration
@ -71,6 +72,11 @@ import os
import sys import sys
from array import array from array import array
# for converting bytes to readable text
from chars import chars
from map_names import map_names
# for _check_java_library_path # for _check_java_library_path
from java.lang import System from java.lang import System
@ -157,7 +163,7 @@ def button_combiner(buttons):
for each in buttons: for each in buttons:
result |= button_masks[each] result |= button_masks[each]
print "button: " + str(result) print "button: " + str(result)
return result return result
@ -208,6 +214,12 @@ def step_until_capture():
# just some aliases for step_until_capture # just some aliases for step_until_capture
run = go = step_until_capture run = go = step_until_capture
def translate_chars(charz):
result = ""
for each in charz:
result += chars[each]
return result
def _create_byte_buffer(data): def _create_byte_buffer(data):
""" """
Converts data into a ByteBuffer. This is useful for interfacing with the Gb Converts data into a ByteBuffer. This is useful for interfacing with the Gb
@ -310,7 +322,7 @@ def get_registers():
def get_rom(): def get_rom():
""" """
Returns the ROM in bytes.. in a string. Returns the ROM in bytes.
""" """
rom_array = jarray.zeros(Gb.ROM_SIZE, "i") rom_array = jarray.zeros(Gb.ROM_SIZE, "i")
Gb.getROM(rom_array) Gb.getROM(rom_array)
@ -318,7 +330,7 @@ def get_rom():
def get_ram(): def get_ram():
""" """
Returns the RAM in bytes in a string. Returns the RAM in bytes.
""" """
ram_array = jarray.zeros(Gb.RAM_SIZE, "i") ram_array = jarray.zeros(Gb.RAM_SIZE, "i")
Gb.getRAM(ram_array) Gb.getRAM(ram_array)
@ -332,14 +344,20 @@ def say_hello():
def get_memory(): def get_memory():
""" """
Returns memory in bytes in a string. Returns memory in bytes.
""" """
raise NotImplementedError("dunno how to calculate memory size") memory_size = 0x10000
# memory_size = ...
memory = jarray.zeros(memory_size, "i") memory = jarray.zeros(memory_size, "i")
Gb.getMemory(memory) Gb.getMemory(memory)
return RomList(memory) return RomList(memory)
def set_memory(memory):
"""
Sets memory in the emulator. Use get_memory() to retrieve the current
state.
"""
Gb.writeMemory(memory)
def get_pixels(): def get_pixels():
""" """
Returns a list of pixels on the screen display. Broken, probably. Use Returns a list of pixels on the screen display. Broken, probably. Use
@ -371,6 +389,27 @@ def read_memory(address):
Read an integer at an address. Read an integer at an address.
""" """
return Gb.readMemory(address) return Gb.readMemory(address)
get_memory_at = read_memory
def get_memory_range(start_address, byte_count):
"""
Returns a list of bytes.
start_address - address to start reading at
byte_count - how many bytes (0 returns just 1 byte)
"""
bytez = []
for counter in range(0, byte_count):
byte = get_memory_at(start_address + counter)
bytez.append(byte)
return bytez
def set_memory_at(address, value):
"""
Sets a byte at a certain address in memory. This directly sets the memory
instead of copying the memory from the emulator.
"""
Gb.setMemoryAt(address, value)
def press(buttons, steplimit=1): def press(buttons, steplimit=1):
""" """
@ -386,3 +425,160 @@ def press(buttons, steplimit=1):
for step_counter in range(0, steplimit): for step_counter in range(0, steplimit):
Gb.step(number) Gb.step(number)
class crystal:
"""
Just a simple namespace to store a bunch of functions for Pokémon Crystal.
"""
@staticmethod
def walk_through_walls_slow():
memory = get_memory()
memory[0xC2FA] = 0
memory[0xC2FB] = 0
memory[0xC2FC] = 0
memory[0xC2FD] = 0
set_memory(memory)
@staticmethod
def walk_through_walls():
"""
Lets the player walk all over the map. These values are probably reset
by some of the map/collision functions when you move on to a new
location, so this needs 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)
@staticmethod
def nstep(steplimit=500):
"""
Steps the CPU forward and calls some functions in between each step,
like to manipulate memory. This is pretty slow.
"""
for step_counter in range(0, steplimit):
crystal.walk_through_walls()
step()
@staticmethod
def get_map_group_id():
"""
Returns the current map group.
"""
return get_memory_at(0xdcb5)
@staticmethod
def get_map_id():
"""
Returns the map number of the current map.
"""
return get_memory_at(0xdcb6)
@staticmethod
def get_map_name():
"""
Figures out the current map name.
"""
map_group_id = crystal.get_map_group_id()
map_id = crystal.get_map_id()
return map_names[map_group_id][map_id]["name"]
@staticmethod
def get_xy():
"""
(x, y) coordinates of player on map.
Relative to top-left corner of map.
"""
x = get_memory_at(0xdcb8)
y = get_memory_at(0xdcb7)
return (x, y)
@staticmethod
def is_in_battle():
"""
Checks whether or not we're in a battle.
"""
return (get_memory_at(0xd22d) != 0) or crystal.is_in_link_battle()
@staticmethod
def is_in_link_battle():
return get_memory_at(0xc2dc) != 0
@staticmethod
def unlock_flypoints():
"""
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)
@staticmethod
def get_gender():
"""
Returns 'male' or 'female'.
"""
gender = get_memory_at(0xD472)
if gender == 0:
return "male"
elif gender == 1:
return "female"
else:
return gender
@staticmethod
def get_player_name():
"""
Returns the 7 characters making up the player's name.
"""
bytez = get_memory_range(0xD47D, 7)
name = translate_chars(bytez)
return name
@staticmethod
def set_partymon2():
"""
This causes corruption, so it's not working yet.
"""
memory = get_memory()
memory[0xdcd7] = 2
memory[0xdcd9] = 0x7
memory[0xdd0f] = 0x7
memory[0xdd10] = 0x1
# moves
memory[0xdd11] = 0x1
memory[0xdd12] = 0x2
memory[0xdd13] = 0x3
memory[0xdd14] = 0x4
# id
memory[0xdd15] = 0x1
memory[0xdd16] = 0x2
# experience
memory[0xdd17] = 0x2
memory[0xdd18] = 0x3
memory[0xdd19] = 0x4
# hp
memory[0xdd1a] = 0x5
memory[0xdd1b] = 0x6
# current hp
memory[0xdd31] = 0x10
memory[0xdd32] = 0x25
# max hp
memory[0xdd33] = 0x10
memory[0xdd34] = 0x40
set_memory(memory)