diff options
author | Bryan Bishop <kanzure@gmail.com> | 2013-09-09 02:04:47 -0500 |
---|---|---|
committer | Bryan Bishop <kanzure@gmail.com> | 2013-09-09 02:04:47 -0500 |
commit | 54057bc762b955a4c8924fa951ebf2faadca16a9 (patch) | |
tree | 384eb794ec4d454155e98fb04a5eb0b26a4d34b8 /pokemontools/vba/vba.py | |
parent | 709850fff5ee7c8f6bff61a2b0f3b23d4954dcac (diff) |
strip out jython garbage from vba.py
Diffstat (limited to 'pokemontools/vba/vba.py')
-rw-r--r-- | pokemontools/vba/vba.py | 609 |
1 files changed, 8 insertions, 601 deletions
diff --git a/pokemontools/vba/vba.py b/pokemontools/vba/vba.py index e2d1168..00bde25 100644 --- a/pokemontools/vba/vba.py +++ b/pokemontools/vba/vba.py @@ -1,79 +1,14 @@ # -*- coding: utf-8 -*- """ -vba-clojure (but really it's jython/python/jvm) - -This is jython, not python. Use jython to run this file. Before running this -file, some of the dependencies need to be constructed. These can be obtained -from the vba-clojure project. - sudo apt-get install g++ libtool openjdk-6-jre openjdk-6-jdk libsdl1.2-dev mercurial ant autoconf jython - - export JAVA_INCLUDE_PATH=/usr/lib/jvm/java-6-openjdk-amd64/include/ - export JAVA_INCLUDE_PATH2=/usr/lib/jvm/java-6-openjdk-amd64/include/ - - hg clone http://hg.bortreb.com/vba-clojure - cd vba-clojure/ - ./dl-libs.sh - cd java/ - ant all - cd .. - autoreconf -i - ./configure - make - sudo make install - -Make sure vba-clojure bindings are in $CLASSPATH: - export CLASSPATH=$CLASSPATH:java/dist/gb-bindings.jar - -Make sure vba-clojure is available within "java.library.path": - sudo ln -s \ - $HOME/local/vba-clojure/vba-clojure/src/clojure/.libs/libvba.so.0.0.0 \ - /usr/lib/jni/libvba.so - -(In the above command, substitute the first path with the path of the vba-clojure -directory you made, if it is different.) - -Also make sure VisualBoyAdvance.cfg is somewhere in the $PATH for VBA to find. -A default configuration is provided in vba-clojure under src/. - -Usage (in jython, not python): - import vba - - # activate the laser beam - vba.load_rom("/path/to/baserom.gbc") - - # make the emulator eat some instructions - vba.nstep(300) - - # save the state because we're paranoid - copyrights = vba.get_state() - # or ... - vba.save_state("copyrights") - # >>> vba.load_state("copyrights") == copyrights - # True - - # play for a while, then press F12 - vba.run() - - # let's save the game again - vba.save_state("unknown-delete-me") - - # and let's go back to the other state - vba.set_state(copyrights) - - # or why not the other way around? - vba.set_state(vba.load_state("unknown-delete-me")) - - vba.get_memory_at(0xDCDA) - vba.set_memory_at(0xDCDB, 0xFF) - vba.get_memory_range(0xDCDA, 10) +VBA automation """ import os import sys import re -from array import array import string from copy import copy + import unittest # for converting bytes to readable text @@ -81,22 +16,6 @@ from pokemontools.chars import chars from pokemontools.map_names import map_names -# for _check_java_library_path -from java.lang import System - -# for passing states to the emulator -from java.nio import ByteBuffer - -# For getRegisters and other times we have to pass a java int array to a -# function. -import jarray - -# load in the vba-clojure bindings -import com.aurellem.gb.Gb as Gb - -# load the vba-clojure library -Gb.loadVBA() - import keyboard # just use a default config for now until the globals are removed completely @@ -109,131 +28,13 @@ 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)) -def _check_java_library_path(): - """ - Returns the value of java.library.path. - - The vba-clojure library must be compiled - and linked from this location. - """ - return System.getProperty("java.library.path") +import vba_wrapper -class RomList(list): +vba = vba_wrapper.VBA(rom_path) +registers = vba_wrapper.core.registers.Registers(vba) - """ - Simple wrapper to prevent a giant rom from being shown on screen. - """ - - def __init__(self, *args, **kwargs): - list.__init__(self, *args, **kwargs) - - def __repr__(self): - """ - Simplifies this object so that the output doesn't overflow stdout. - """ - return "RomList(too long)" - -button_masks = { - "a": 0x0001, - "b": 0x0002, - "r": 0x0010, - "l": 0x0020, - "u": 0x0040, - "d": 0x0080, - "select": 0x0004, - "start": 0x0008, - "restart": 0x0800, - "listen": -1, # what? -} - -# useful for directly stating what to press -a, b, r, l, u, d, select, start, restart = "a", "b", "r", "l", "u", "d", "select", "start", "restart" - -def button_combiner(buttons): - """ - Combines multiple button presses into an integer. - - This is used when sending a keypress to the emulator. - """ - result = 0 - - # String inputs need to be cleaned up so that "start" doesn't get - # recognized as "s" and "t" etc.. - if isinstance(buttons, str): - if "restart" in buttons: - buttons = buttons.replace("restart", "") - result |= button_masks["restart"] - if "start" in buttons: - buttons = buttons.replace("start", "") - result |= button_masks["start"] - if "select" in buttons: - buttons = buttons.replace("select", "") - result |= button_masks["select"] - - # allow for the "a, b" and "a b" formats - if ", " in buttons: - buttons = buttons.split(", ") - elif " " in buttons: - buttons = buttons.split(" ") - - if isinstance(buttons, list): - if len(buttons) > 9: - raise Exception("can't combine more than 9 buttons at a time") - - for each in buttons: - result |= button_masks[each] - - #print "button: " + str(result) - return result - -def load_rom(path=None): - """ - Starts the emulator with a certain ROM. - - Defaults to rom_path if no parameters are given. - """ - if path == None: - path = rom_path - try: - root = load_state("root") - except: - # "root.sav" is required because if you create it in the future, you - # will have to shutdown the emulator and possibly lose your state. Some - # functions require there to be at least one root state available to do - # computations between two states. - sys.stderr.write("ERROR: unable to read \"root.sav\", please run" - " generate_root() or get_root() to make an initial save.\n") - Gb.startEmulator(path) - -def shutdown(): - """ - Stops the emulator. Closes the window. - - The "opposite" of this is the load_rom function. - """ - Gb.shutdown() - -def step(): - """ - Advances the emulator forward by one step. - """ - Gb.step() - -def nstep(steplimit): - """ - Step the game forward by a certain number of instructions. - """ - for counter in range(0, steplimit): - Gb.step() - -def step_until_capture(): - """ - Loop step() until SDLK_F12 is detected. - """ - Gb.stepUntilCapture() - -# just some aliases for step_until_capture -run = go = step_until_capture +button_masks = vba_wrapper.core.VBA.button_masks +button_combiner = vba_wrapper.core.VBA.button_combine def translate_chars(charz): result = "" @@ -241,217 +42,6 @@ def translate_chars(charz): result += chars[each] return result -def _create_byte_buffer(data): - """ - Converts data into a ByteBuffer. - - This is useful for interfacing with the Gb class. - """ - buf = ByteBuffer.allocateDirect(len(data)) - if isinstance(data[0], int): - for byte in data: - buf.put(byte) - else: - for byte in data: - buf.put(ord(byte)) - return buf - -def set_state(state, do_step=False): - """ - Injects the given state into the emulator. - - Use do_step if you want to call step(), which also allows - SDL to render the latest frame. Note that the default is to - not step, and that the screen (if it is enabled) will appear - as if it still has the last state loaded. This is normal. - """ - Gb.loadState(_create_byte_buffer(state)) - if do_step: - step() - -def get_state(): - """ - Retrieves the current state of the emulator. - """ - buf = Gb.saveState() - state = [buf.get(x) for x in range(0, buf.capacity())] - arr = array("b") - arr.extend(state) - return arr.tostring() # instead of state - -def save_state(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 = get_state() - if len(name) < 4 or name[-4:] != ".sav": - name += ".sav" - save_path = os.path.join(save_state_path, name) - if not override and os.path.exists(save_path): - raise Exception("oops, save state path already exists: " + str(save_path)) - else: - # convert the state into a reasonable output - data = array('b') - data.extend(state) - output = data.tostring() - - file_handler = open(save_path, "wb") - file_handler.write(output) - file_handler.close() - -def load_state(name): - """ - Reads a state from file based on name. - - Looks in save_state_path for a file - with this name (".sav" is optional). - """ - save_path = os.path.join(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(save_state_path, name) - file_handler = open(save_path, "rb") - state = file_handler.read() - file_handler.close() - return state - -def generate_root(): - """ - Restarts the emulator and saves the initial state to "root.sav". - """ - shutdown() - load_rom() - root = get_state() - save_state("root", state=root, override=True) - return root - -def get_root(): - """ - Loads the root state. - - (Or restarts the emulator and creates a new root state.) - """ - try: - root = load_state("root") - except: - root = generate_root() - -def get_registers(): - """ - Returns a list of current register values. - """ - register_array = jarray.zeros(Gb.NUM_REGISTERS, "i") - Gb.getRegisters(register_array) - return list(register_array) - -def set_registers(registers): - """ - Applies the set of registers to the CPU. - """ - Gb.writeRegisters(registers) -write_registers = set_registers - -def get_rom(): - """ - Returns the ROM in bytes. - """ - rom_array = jarray.zeros(Gb.ROM_SIZE, "i") - Gb.getROM(rom_array) - return RomList(rom_array) - -def get_ram(): - """ - Returns the RAM in bytes. - """ - ram_array = jarray.zeros(Gb.RAM_SIZE, "i") - Gb.getRAM(ram_array) - return RomList(ram_array) - -def say_hello(): - """ - Test that the VBA/GB bindings are working. - """ - Gb.sayHello() - -def get_memory(): - """ - Returns memory in bytes. - """ - memory_size = 0x10000 - memory = jarray.zeros(memory_size, "i") - Gb.getMemory(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(): - """ - Returns a list of pixels on the screen display. - - Broken, probably. Use screenshot() instead. - """ - sys.stderr.write("ERROR: seems to be broken on VBA's end? Good luck. Use" - " screenshot() instead.\n") - size = Gb.DISPLAY_WIDTH * Gb.DISPLAY_HEIGHT - pixels = jarray.zeros(size, "i") - Gb.getPixels(pixels) - return RomList(pixels) - -def screenshot(filename, literal=False): - """ - Saves a PNG screenshot to the file at filename. - - Use literal if you want to store it in the current directory. - Default is to save it to screenshots/ under the project. - """ - screenshots_path = os.path.join(project_path, "screenshots/") - filename = os.path.join(screenshots_path, filename) - if len(filename) < 4 or filename[-4:] != ".png": - filename += ".png" - Gb.nwritePNG(filename) - print "Screenshot saved to: " + str(filename) -save_png = screenshot - -def read_memory(address): - """ - Read an integer at an 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, holdsteps=1, aftersteps=1): """ Press a button. @@ -473,18 +63,6 @@ def press(buttons, holdsteps=1, aftersteps=1): for step_counter in range(0, aftersteps): Gb.step(0) -def get_buttons(): - """ - Returns the currentButtons[0] value - - (an integer with bits set for which - buttons are currently pressed). - """ - return Gb.getCurrentButtons() - -class State(RomList): - name = None - class Recording: def __init__(self): self.frames = [] @@ -499,7 +77,7 @@ class Recording: """ Saves the current state. """ - state = State(get_state()) + state = bytearray(get_state()) state.name = name self.states[self.frame_count] = state @@ -538,104 +116,6 @@ class Recording: frame_id = self.states.index(thing) self.step(first_frame=frame_id, replay=True) -class Registers: - order = [ - "pc", - "sp", - "af", - "bc", - "de", - "hl", - "iff", - "div", - "tima", - "tma", - "tac", - "if", - "lcdc", - "stat", - "scy", - "scx", - "ly", - "lyc", - "dma", - "wy", - "wx", - "vbk", - "hdma1", - "hdma2", - "hdma3", - "hdma4", - "hdma5", - "svbk", - "ie", - ] - - def __setitem__(self, key, value): - current_registers = get_registers() - current_registers[Registers.order.index(key)] = value - set_registers(current_registers) - - def __getitem__(self, key): - current_registers = get_registers() - return current_registers[Registers.order.index(key)] - - def __list__(self): - return get_registers() - - def _get_register(id): - def constructed_func(self, id=copy(id)): - return get_registers()[id] - return constructed_func - - def _set_register(id): - def constructed_func(self, value, id=copy(id)): - current_registers = get_registers() - current_registers[id] = value - set_registers(current_registers) - return constructed_func - - pc = property(fget=_get_register(0), fset=_set_register(0)) - sp = property(fget=_get_register(1), fset=_set_register(1)) - af = property(fget=_get_register(2), fset=_set_register(2)) - bc = property(fget=_get_register(3), fset=_set_register(3)) - de = property(fget=_get_register(4), fset=_set_register(4)) - hl = property(fget=_get_register(5), fset=_set_register(5)) - iff = property(fget=_get_register(6), fset=_set_register(6)) - div = property(fget=_get_register(7), fset=_set_register(7)) - tima = property(fget=_get_register(8), fset=_set_register(8)) - tma = property(fget=_get_register(9), fset=_set_register(9)) - tac = property(fget=_get_register(10), fset=_set_register(10)) - _if = property(fget=_get_register(11), fset=_set_register(11)) - lcdc = property(fget=_get_register(12), fset=_set_register(12)) - stat = property(fget=_get_register(13), fset=_set_register(13)) - scy = property(fget=_get_register(14), fset=_set_register(14)) - scx = property(fget=_get_register(15), fset=_set_register(15)) - ly = property(fget=_get_register(16), fset=_set_register(16)) - lyc = property(fget=_get_register(17), fset=_set_register(17)) - dma = property(fget=_get_register(18), fset=_set_register(18)) - wy = property(fget=_get_register(19), fset=_set_register(19)) - wx = property(fget=_get_register(20), fset=_set_register(20)) - vbk = property(fget=_get_register(21), fset=_set_register(21)) - hdma1 = property(fget=_get_register(22), fset=_set_register(22)) - hdma2 = property(fget=_get_register(23), fset=_set_register(23)) - hdma3 = property(fget=_get_register(24), fset=_set_register(24)) - hdma4 = property(fget=_get_register(25), fset=_set_register(25)) - hdma5 = property(fget=_get_register(26), fset=_set_register(26)) - svbk = property(fget=_get_register(27), fset=_set_register(27)) - ie = property(fget=_get_register(28), fset=_set_register(28)) - - def __repr__(self): - spacing = "\t" - output = "Registers:\n" - for (id, each) in enumerate(self.order): - output += spacing + each + " = " + hex(get_registers()[id]) - #hex(self[each]) - output += "\n" - return output - -registers = Registers() - def call(bank, address): """ Jumps into a function at a certain address. @@ -668,66 +148,6 @@ def call(bank, address): else: registers["pc"] = address -class cheats: - """ - Helpers to manage the cheating infrastructure. - - import vba; vba.load_rom(); vba.cheats.add_gameshark("0100CFCF", "text speedup 1"); vba.cheats.add_gameshark("0101CCCF", "text speedup 2"); vba.go() - """ - - @staticmethod - def enable(id): - """ - void gbCheatEnable(int i) - """ - Gb.cheatEnable(id) - - @staticmethod - def disable(id): - """ - void gbCheatDisable(int i) - """ - Gb.cheatDisable(id) - - @staticmethod - def load_file(filename): - """ - Loads a .clt file. By default each cheat is disabled. - """ - Gb.loadCheatsFromFile(filename) - - @staticmethod - def remove_all(): - """ - Removes all cheats from memory. - - void gbCheatRemoveAll() - """ - Gb.cheatRemoveAll() - - @staticmethod - def remove_cheat(id): - """ - Removes a specific cheat from memory by id. - - void gbCheatRemove(int i) - """ - Gb.cheatRemove(id) - - @staticmethod - def add_gamegenie(code, description=""): - """ - void gbAddGgCheat(const char *code, const char *desc) - """ - Gb.cheatAddGamegenie(code, description) - - @staticmethod - def add_gameshark(code, description=""): - """ - gbAddGsCheat(const char *code, const char *desc) - """ - Gb.cheatAddGameshark(code, description) - def get_stack(): """ Return a list of functions on the stack. @@ -1133,19 +553,6 @@ class crystal: memory = get_memory() class TestEmulator(unittest.TestCase): - try: - state = load_state("cheating-12") - except: - if "__name__" == "__main__": - raise Exception("failed to setup unit tests because no save state found") - - def setUp(self): - load_rom() - set_state(self.state) - - def tearDown(self): - shutdown() - def test_PlaceString(self): call(0, 0x1078) |