summaryrefslogtreecommitdiff
path: root/pokemontools/vba/vba.py
diff options
context:
space:
mode:
authorBryan Bishop <kanzure@gmail.com>2013-09-09 02:04:47 -0500
committerBryan Bishop <kanzure@gmail.com>2013-09-09 02:04:47 -0500
commit54057bc762b955a4c8924fa951ebf2faadca16a9 (patch)
tree384eb794ec4d454155e98fb04a5eb0b26a4d34b8 /pokemontools/vba/vba.py
parent709850fff5ee7c8f6bff61a2b0f3b23d4954dcac (diff)
strip out jython garbage from vba.py
Diffstat (limited to 'pokemontools/vba/vba.py')
-rw-r--r--pokemontools/vba/vba.py609
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)