summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INSTALL.md134
-rw-r--r--Makefile32
-rw-r--r--README.md18
-rw-r--r--extras/vba.py41
-rw-r--r--extras/vba_autoplayer.py464
-rw-r--r--preprocessor.py147
-rw-r--r--prequeue.py17
7 files changed, 696 insertions, 157 deletions
diff --git a/INSTALL.md b/INSTALL.md
index 0443412f0..68d5c09b8 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,21 +1,112 @@
# Getting Started
-Compiling requires a certain Pokemon Crystal ROM:
+Compiling **pokecrystal.gbc** requires a certain **Pokemon Crystal** rom:
```
Pokemon - Crystal Version (UE) (V1.0) [C][!].gbc
md5: 9f2922b235a5eeb78d65594e82ef5dde
```
-Save it as `baserom.gbc` in the repository.
+Save it as **baserom.gbc** in the repository.
-Feel free to ask us on nucleus.kafuka.org #skeetendo if something goes wrong (remember to tell where)!
+Feel free to ask us on **[nucleus.kafuka.org #skeetendo](https://kiwiirc.com/client/irc.nolimitzone.com/?#skeetendo)** if something goes wrong!
-Don't know how to use IRC? Try [mibbit](http://chat.mibbit.com/?server=nucleus.kafuka.org&channel=#skeetendo).
+# Windows
+If you are on Windows and can't install Linux, **Cygwin** is a great alternative.
-## Linux
+## Installing Cygwin
+
+Cygwin provides a virtual Linux environment on Windows systems. Just get **setup.exe**.
+
+**http://cygwin.com/install.html**
+
+During the install:
+
+* Keep the defaults.
+
+* Most mirrors are molasses. Use **http://mirrors.kernel.org**.
+
+
+## Using Cygwin
+
+Launch the **Cygwin terminal**. Maybe you know your way around the Linux terminal, **bash**. If not, a crash course:
+```bash
+# list files in current directory
+ls
+
+# show current directory
+pwd
+
+# change directory
+cd /away/we/go
+```
+
+
+## Getting up and running
+
+We need a couple more things to be able to compile.
+
+If you're feeling lazy, just paste these commands into your terminal.
+
+```bash
+apt-cyg install make git wget python python-setuptools
+easy_install pip
+```
+
+**rgbds** will let you compile Game Boy roms.
+
+```bash
+cd ~
+
+# download rgbds binaries
+wget http://diyhpl.us/~bryan/irc/pokered/rgbds/rgbds.zip
+unzip rgbds.zip
+rm rgbds.zip
+
+# make rgbds accessible for all time
+export PATH=$PATH:`pwd`/rgbds
+echo "export PATH=$PATH" >> ~/.bashrc
+```
+
+Set up the **pokecrystal** repository:
+
+```bash
+git clone https://github.com/kanzure/pokecrystal
+cd pokecrystal
+
+# install python requirements
+pip install -r requirements.txt
+```
+
+## Don't forget baserom.gbc!!
+
+Make sure you downloaded a base rom. Name it **baserom.gbc**.
+
+Now you should be able to build **pokecrystal.gbc** for the first time.
+
+This compiles a new rom from the source code, with any patches filled in from the base rom.
+```bash
+make
+```
+
+This ought to take **between 3 and 15 seconds**, depending on your computer.
+
+If you see `cmp baserom.gbc pokecrystal.gbc` as the last line, the build was successful!
+
+Your first compile processes every source file at once. After that, **only modified source files have to be reprocessed**, so compiling again should be a few seconds faster.
+
+Other **make targets** that may come in handy:
+
+`make clean` deletes any preprocessed source files (.tx), rgbds object files and pokecrystal.gbc, in case something goes wrong.
+
+`make pngs` decompresses any **lz** files in gfx/ and then exports any graphics files to **png**.
+
+`make lzs` does the reverse. This is already part of the build process, so **modified pngs will automatically be converted to 2bpp and lz-compressed** without any additional work.
+
+
+# Linux
```bash
sudo apt-get install make gcc bison git python python-setuptools
@@ -46,36 +137,21 @@ pip install -r requirements.txt
git config diff.hex.textconv hexdump
```
-To compile the ROM from ASM source:
-```
-make clean && make
+To compile the rom from asm source:
+```bash
+make
```
-That will take between 3 and 15 seconds, depending on your computer. If you see
-`cmp baserom.gbc pokecrystal.gbc` as the last line, the build was successful! Rejoice!
-
-
-## Windows
-
-Set up [GitHub for Windows](http://windows.github.com/) and clone this repository.
-
-If you haven't already, get [Python 2.7](http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi) ([64-bit](http://www.python.org/ftp/python/2.7.3/python-2.7.3.amd64.msi)).
-
-Extract the following files from the [RGBDS](https://github.com/downloads/bentley/rgbds/rgbds-0.0.1.zip) package into the repository:
-`rgbasm.exe`
-`rgbds.exe`
-`rgbfix.exe`
-`rgblink.exe`
+That will take between 3 and 15 seconds, depending on your computer. If you see `cmp baserom.gbc pokecrystal.gbc` as the last line, the build was successful! Rejoice!
-Install [make](http://gnuwin32.sourceforge.net/downlinks/make.php) for Windows.
-To compile the ROM from ASM source, run `pokecrystal.bat`.
+# Now what?
-That will take between 3 and 15 seconds, depending on your computer. If you see
-`FC: no differences encountered`, the build was successful! Rejoice!
+**main.asm** is a good starting point. The structure of the source is laid out here.
-Now you may try messing around with `main.asm`, or just do whatever you wanted to.
+* **Can't find something?** Anyone can add to the source. There's lots to be uncovered.
+* **Do your own thing!** The asm source is hack-friendly, and the supplementary scripts in extras/ can be used for other projects.
-# Contributions are welcome!
+* We'll be happy to answer any **questions** on **[nucleus.kafuka.org #skeetendo](https://kiwiirc.com/client/irc.nolimitzone.com/?#skeetendo)**.
diff --git a/Makefile b/Makefile
index 15fa7f5c1..37e9aef4f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,29 +1,24 @@
.SUFFIXES: .asm .tx .o .gbc .png .2bpp .lz
-TEXTFILES = \
- text/sweethoney.tx \
- text/phone/bill.tx \
- text/phone/elm.tx \
- text/phone/mom.tx \
- text/phone/trainers1.tx \
- text/common.tx \
- text/common_2.tx \
- text/common_3.tx \
- main.tx
-
-PNG_GFX = $(shell find gfx/ -type f -name '*.png')
-LZ_GFX = $(shell find gfx/ -type f -name '*.lz')
-TWOBPP_GFX = $(shell find gfx/ -type f -name '*.2bpp')
+TEXTFILES := $(shell find ./ -type f -name '*.asm' | grep -v pokecrystal.asm | grep -v constants.asm | grep -v gbhw.asm | grep -v hram.asm | grep -v constants | grep -v wram.asm)
+TEXTQUEUE :=
+
+PNG_GFX := $(shell find gfx/ -type f -name '*.png')
+LZ_GFX := $(shell find gfx/ -type f -name '*.lz')
+TWOBPP_GFX := $(shell find gfx/ -type f -name '*.2bpp')
all: pokecrystal.gbc
cmp baserom.gbc $<
clean:
- rm -f main.tx pokecrystal.o pokecrystal.gbc ${TEXTFILES}
-pokecrystal.o: pokecrystal.asm constants.asm wram.asm ${TEXTFILES} lzs
+ rm -f pokecrystal.o pokecrystal.gbc
+ @echo 'rm -f $(TEXTFILES:.asm=.tx)'
+ @rm -f $(TEXTFILES:.asm=.tx)
+pokecrystal.o: $(TEXTFILES:.asm=.tx) $(LZ_GFX) $(TWOBPP_GFX)
+ python prequeue.py $(TEXTQUEUE)
rgbasm -o pokecrystal.o pokecrystal.asm
-
.asm.tx:
- python preprocessor.py < $< > $@
+ $(eval TEXTQUEUE := $(TEXTQUEUE) $<)
+ @rm $@
pokecrystal.gbc: pokecrystal.o
rgblink -o $@ $<
@@ -33,6 +28,7 @@ pngs:
cd extras && python gfx.py mass-decompress && python gfx.py dump-pngs
lzs: $(LZ_GFX) $(TWOBPP_GFX)
+ @:
gfx/pics/%/front.lz: gfx/pics/%/front.png gfx/pics/%/tiles.2bpp
python extras/gfx.py png-to-lz --front $^
diff --git a/README.md b/README.md
index f8b59dddd..6adb65625 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ The source code in this project successfully converts back into a ROM image. All
## Base ROM
-The following ROM is required for compiling:
+The following rom is required for compiling:
```
Pokemon - Crystal Version (UE) (V1.0) [C][!].gbc
@@ -17,16 +17,18 @@ md5: 9f2922b235a5eeb78d65594e82ef5dde
Eventually this will not be necessary.
-## See also
+## What can I do?
-* disassembly of [Pokémon Red](http://bitbucket.org/iimarckus/pokered).
+* Are we missing something? Make a pull request! Contributions are welcome.
+* Take a look at some of the disasm tools in **extras/**. Most of the scripts are generalized enough to take apart other Game Boy games.
-## Contributing
+* Tackle some **[issues](https://github.com/kanzure/pokecrystal/issues)**!
-* Hang out with us on IRC: `nucleus.kafuka.org #skeetendo` (for example, by
-using [mibbit](http://chat.mibbit.com/)).
-* Are we missing something? Make a pull request! Contributions are welcome.
+## See also
+
+* Hang out with us on irc: **[nucleus.kafuka.org #skeetendo](https://kiwiirc.com/client/irc.nolimitzone.com/?#skeetendo)**
+
+* Disassembly of **[Pokémon Red](http://bitbucket.org/iimarckus/pokered)**.
-* Tackle some [issues](https://github.com/kanzure/pokecrystal/issues)!
diff --git a/extras/vba.py b/extras/vba.py
index 317f70540..f85f838b4 100644
--- a/extras/vba.py
+++ b/extras/vba.py
@@ -159,15 +159,21 @@ def button_combiner(buttons):
# recognized as "s" and "t" etc..
if isinstance(buttons, str):
if "restart" in buttons:
- buttons.replace("restart", "")
+ buttons = buttons.replace("restart", "")
result |= button_masks["restart"]
if "start" in buttons:
- buttons.replace("start", "")
+ buttons = buttons.replace("start", "")
result |= button_masks["start"]
if "select" in buttons:
- buttons.replace("select", "")
+ 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")
@@ -429,7 +435,7 @@ def set_memory_at(address, value):
"""
Gb.setMemoryAt(address, value)
-def press(buttons, steplimit=1):
+def press(buttons, holdsteps=1, aftersteps=1):
"""
Press a button. Use steplimit to say for how many steps you want to press
the button (try leaving it at the default, 1).
@@ -440,9 +446,14 @@ def press(buttons, steplimit=1):
number = buttons
else:
number = buttons
- for step_counter in range(0, steplimit):
+ 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 get_buttons():
"""
Returns the currentButtons[0] value (an integer with bits set for which
@@ -702,6 +713,26 @@ class crystal:
"""
@staticmethod
+ def text_wait(step_size=10, max_wait=500):
+ """
+ Watches for a sign that text is done being drawn to screen, then
+ presses the "A" button.
+
+ :param step_size: number of steps per wait loop
+ :param max_wait: number of wait loops to perform
+ """
+ for x in range(0, max_wait):
+ hi = get_memory_at(registers.sp + 1)
+ lo = get_memory_at(registers.sp)
+ address = ((hi << 8) | lo)
+ if address == 0xaef:
+ break
+ else:
+ nstep(step_size)
+
+ press("a", holdsteps=50, aftersteps=1)
+
+ @staticmethod
def walk_through_walls_slow():
memory = get_memory()
memory[0xC2FA] = 0
diff --git a/extras/vba_autoplayer.py b/extras/vba_autoplayer.py
new file mode 100644
index 000000000..eafbff134
--- /dev/null
+++ b/extras/vba_autoplayer.py
@@ -0,0 +1,464 @@
+# -*- coding: utf-8 -*-
+"""
+Programmatic speedrun of Pokémon Crystal
+"""
+import os
+
+# bring in the emulator and basic tools
+import vba
+
+def main():
+ """
+ Start the game.
+ """
+ vba.load_rom()
+
+ # get past the opening sequence
+ skip_intro()
+
+ # walk to mom and handle her text
+ handle_mom()
+
+ # walk to elm and do whatever he wants
+ handle_elm("totodile")
+
+ new_bark_level_grind(10, skip=False)
+
+def skippable(func):
+ """
+ Makes a function skippable by saving the state before and after the
+ function runs. Pass "skip=True" to the function to load the previous save
+ state from when the function finished.
+ """
+ def wrapped_function(*args, **kwargs):
+ skip = True
+
+ if "skip" in kwargs.keys():
+ skip = kwargs["skip"]
+ del kwargs["skip"]
+
+ # override skip if there's no save
+ if skip:
+ full_name = func.__name__ + "-end.sav"
+ if not os.path.exists(os.path.join(vba.save_state_path, full_name)):
+ skip = False
+
+ return_value = None
+
+ if not skip:
+ vba.save_state(func.__name__ + "-start", override=True)
+ return_value = func(*args, **kwargs)
+ vba.save_state(func.__name__ + "-end", override=True)
+ elif skip:
+ vba.set_state(vba.load_state(func.__name__ + "-end"))
+
+ return return_value
+ return wrapped_function
+
+@skippable
+def skip_intro():
+ """
+ Skip the game boot intro sequence.
+ """
+ # copyright sequence
+ vba.nstep(400)
+
+ # skip the ditto sequence
+ vba.press("a")
+ vba.nstep(100)
+
+ # skip the start screen
+ vba.press("start")
+ vba.nstep(100)
+
+ # click "new game"
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # Are you a boy? Or are you a girl?
+ vba.nstep(145)
+
+ # pick "boy"
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # ....
+ vba.crystal.text_wait()
+
+ vba.crystal.text_wait()
+
+ # What time is it?
+ vba.crystal.text_wait()
+
+ # DAY 10 o'clock
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # WHAT? DAY 10 o'clock? YES/NO.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # How many minutes? 0 min.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # Whoa! 0 min.? YES/NO.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # People call me
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # tures that we call
+ vba.crystal.text_wait()
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.crystal.text_wait()
+
+ # everything about pokemon yet.
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # Now, what did you say your name was?
+ vba.crystal.text_wait()
+
+ # move down to "CHRIS"
+ vba.press("d")
+ vba.nstep(50)
+
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # wait until playable
+ # could be 150, but it sometimes takes longer??
+ vba.nstep(200)
+
+ return
+
+@skippable
+def handle_mom():
+ """
+ Walk to mom. Handle her speech and questions.
+ """
+ vba.press("r"); vba.nstep(50)
+ vba.press("r"); vba.nstep(50)
+ vba.press("r"); vba.nstep(50)
+ vba.press("r"); vba.nstep(50)
+ vba.press("r"); vba.nstep(50)
+ vba.press("u"); vba.nstep(50)
+ vba.press("u"); vba.nstep(50)
+ vba.press("u"); vba.nstep(50)
+ vba.press("u"); vba.nstep(50)
+
+ # wait for next map to load
+ vba.nstep(50)
+
+ vba.press("d"); vba.nstep(50)
+ vba.press("d"); vba.nstep(50)
+ vba.press("d"); vba.nstep(50)
+
+ # walk into mom's line of sight
+ vba.press("d"); vba.nstep(50)
+
+ vba.nstep(50)
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # What day is it? SUNDAY.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # SUNDAY, is it? YES/NO
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ vba.nstep(200)
+
+ # is it DST now? YES/NO.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # 10:06 AM DST, is that OK? YES/NO.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # know how to use the PHONE? YES/NO.
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.press("a", holdsteps=50, aftersteps=1)
+
+ # have to wait for her to move back :(
+ vba.nstep(50)
+
+ # face down
+ vba.press("d"); vba.nstep(50)
+
+ return
+
+@skippable
+def handle_elm(starter_choice):
+ """
+ Walk to Elm's Lab and get a starter.
+ """
+
+ # walk down
+ vba.press("d"); vba.nstep(50)
+ vba.press("d"); vba.nstep(50)
+
+ # face left
+ vba.press("l"); vba.nstep(50)
+
+ # walk left
+ vba.press("l"); vba.nstep(50)
+ vba.press("l"); vba.nstep(50)
+
+ # face down
+ vba.press("d"); vba.nstep(50)
+
+ # walk down
+ vba.press("d"); vba.nstep(50)
+
+ # walk down to warp to outside
+ vba.press("d"); vba.nstep(50)
+ vba.nstep(10)
+
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+
+ vba.press("u", holdsteps=10, aftersteps=50)
+ vba.press("u", holdsteps=10, aftersteps=50)
+
+ # warp into elm's lab (bottom left warp)
+ vba.press("u", holdsteps=5, aftersteps=50)
+
+ # let the script play
+ vba.nstep(200)
+
+ vba.crystal.text_wait()
+ # I needed to ask you a fa
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.nstep(50)
+
+ # YES/NO.
+ vba.press("a")
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.press("a")
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.press("a")
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.crystal.text_wait()
+
+ for x in range(0, 8): # was 15
+ vba.crystal.text_wait()
+
+ vba.nstep(50)
+ vba.press("a")
+ vba.nstep(100)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # Go on! Pick one.
+ vba.nstep(100)
+ vba.press("a"); vba.nstep(50)
+
+ vba.press("r"); vba.nstep(50)
+ vba.press("r"); vba.nstep(50)
+
+ right = 0
+ if starter_choice in [1, "cyndaquil"]:
+ right = 0
+ elif starter_choice in [2, "totodile"]:
+ right = 1
+ elif starter_choice in [3, "chikorita"]:
+ right = 2
+ else:
+ raise Exception("bad starter")
+
+ for each in range(0, right):
+ vba.press("r"); vba.nstep(50)
+
+ # look up
+ vba.press("u", holdsteps=5, aftersteps=50)
+
+ # get menu
+ vba.press("a", holdsteps=5, aftersteps=50)
+
+ # let the image show
+ vba.nstep(100)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # YES/NO.
+ vba.press("a")
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # received.. music is playing.
+ vba.press("a")
+ vba.crystal.text_wait()
+
+ # YES/NO (nickname)
+ vba.crystal.text_wait()
+
+ vba.press("b")
+
+ # get back to elm
+ vba.nstep(100)
+ vba.nstep(100)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # phone number..
+ vba.press("a")
+ vba.crystal.text_wait()
+ vba.nstep(100)
+ vba.press("a")
+ vba.nstep(300)
+ vba.press("a")
+ vba.nstep(100)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ # I'm counting on you!
+ vba.nstep(200)
+ vba.press("a")
+ vba.nstep(50)
+
+ # look down
+ vba.press("d", holdsteps=5, aftersteps=50)
+
+ # walk down
+ vba.press("d", holdsteps=10, aftersteps=50)
+
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.press("a")
+ vba.nstep(50)
+ vba.press("a")
+
+ vba.crystal.text_wait()
+ vba.crystal.text_wait()
+
+ vba.press("a")
+ vba.nstep(50)
+
+ vba.crystal.text_wait()
+ vba.press("a")
+
+ vba.nstep(100)
+
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+
+ # step outside
+ vba.nstep(40)
+
+@skippable
+def new_bark_level_grind(level):
+ """
+ Starting just outside of Elm's Lab, do some level grinding until the first
+ partymon level is equal to the given value..
+ """
+
+ # walk to the grass area
+ new_bark_level_grind_walk_to_grass(skip=False)
+
+ # TODO: walk around in grass, handle battles
+ # TODO: heal at the lab, then repeat entire function
+
+@skippable
+def new_bark_level_grind_travel_to_grass():
+ """
+ Move to just above the grass.
+ """
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+ vba.press("l", holdsteps=10, aftersteps=50)
+
+ vba.press("d", holdsteps=10, aftersteps=50)
+ vba.press("d", holdsteps=10, aftersteps=50)
+
+if __name__ == "__main__":
+ main()
diff --git a/preprocessor.py b/preprocessor.py
index 18e96dff0..468ae41a5 100644
--- a/preprocessor.py
+++ b/preprocessor.py
@@ -310,24 +310,14 @@ def separate_comment(l):
"""
Separates asm and comments on a single line.
"""
-
- asm = ""
- comment = None
in_quotes = False
-
- # token either belongs to the line or to the comment
- for token in l:
- if comment:
- comment += token
- else:
- if not in_quotes:
- if token == ";":
- comment = ";"
- continue
- if token == "\"":
- in_quotes = not in_quotes
- asm += token
- return asm, comment
+ for i in xrange(len(l)):
+ if not in_quotes:
+ if l[i] == ";":
+ break
+ if l[i] == "\"":
+ in_quotes = not in_quotes
+ return l[:i], l[i:] or None
def quote_translator(asm):
"""
@@ -335,63 +325,50 @@ def quote_translator(asm):
"""
# split by quotes
- asms = asm.split("\"")
+ asms = asm.split('"')
# skip asm that actually does use ASCII in quotes
- lowasm = asms[0].lower()
-
- if "section" in lowasm \
- or "incbin" in lowasm:
- sys.stdout.write(asm)
- return
+ if "SECTION" in asms[0]\
+ or "INCBIN" in asms[0]\
+ or "INCLUDE" in asms[0]:
+ return asm
print_macro = False
if asms[0].strip() == 'print':
asms[0] = asms[0].replace('print','db 0,')
print_macro = True
- output = ""
+ output = ''
even = False
- i = 0
for token in asms:
- i = i + 1
-
if even:
characters = []
# token is a string to convert to byte values
while len(token):
# read a single UTF-8 codepoint
char = token[0]
- if ord(char) >= 0xFC:
- char = char + token[1:6]
- token = token[6:]
- elif ord(char) >= 0xF8:
- char = char + token[1:5]
- token = token[5:]
- elif ord(char) >= 0xF0:
- char = char + token[1:4]
- token = token[4:]
- elif ord(char) >= 0xE0:
- char = char + token[1:3]
- token = token[3:]
- elif ord(char) >= 0xC0:
+ if ord(char) < 0xc0:
+ token = token[1:]
+ # certain apostrophe-letter pairs are considered a single character
+ if char == "'" and token:
+ if token[0] in 'dlmrstv':
+ char += token[0]
+ token = token[1:]
+ elif ord(char) < 0xe0:
char = char + token[1:2]
token = token[2:]
+ elif ord(char) < 0xf0:
+ char = char + token[1:3]
+ token = token[3:]
+ elif ord(char) < 0xf8:
+ char = char + token[1:4]
+ token = token[4:]
+ elif ord(char) < 0xfc:
+ char = char + token[1:5]
+ token = token[5:]
else:
- token = token[1:]
-
- # certain apostrophe-letter pairs are only a single byte
- if char == "'" and len(token) > 0 and \
- (token[0] == "d" or \
- token[0] == "l" or \
- token[0] == "m" or \
- token[0] == "r" or \
- token[0] == "s" or \
- token[0] == "t" or \
- token[0] == "v"):
- char = char + token[0]
- token = token[1:]
-
+ char = char + token[1:6]
+ token = token[6:]
characters += [char]
if print_macro:
@@ -421,32 +398,26 @@ def quote_translator(asm):
output += ", ".join(["${0:02X}".format(chars[char]) for char in characters])
- # if not even
else:
- output += (token)
+ output += token
even = not even
- sys.stdout.write(output)
-
- return
+ return output
def extract_token(asm):
- token = asm.split(" ")[0].replace("\t", "").replace("\n", "")
- return token
+ return asm.split(" ")[0].strip()
def make_macro_table():
- return dict([(macro.macro_name, macro) for macro in macros])
+ return dict(((macro.macro_name, macro) for macro in macros))
macro_table = make_macro_table()
def macro_test(asm):
"""
Returns a matching macro, or None/False.
"""
-
# macros are determined by the first symbol on the line
token = extract_token(asm)
-
# check against all names
if token in macro_table:
return (macro_table[token], token)
@@ -600,64 +571,46 @@ def macro_translator(macro, token, line):
sys.stdout.write(output)
-def include_file(asm):
- """This is more reliable than rgbasm/rgbds including files on its own."""
-
- prefix = asm.split("INCLUDE \"")[0] + '\n'
- filename = asm.split("\"")[1]
- suffix = asm.split("\"")[2]
-
- read_line(prefix)
-
- lines = open(filename, "r").readlines()
-
- for line in lines:
- read_line(line)
-
- read_line(suffix)
-
def read_line(l):
"""Preprocesses a given line of asm."""
- # strip and store any comment on this line
- if ";" in l:
- asm, comment = separate_comment(l)
- else:
- asm = l
- comment = None
+ # strip comments from asm
+ asm, comment = separate_comment(l)
- # handle INCLUDE as a special case
- if "INCLUDE \"" in l:
- include_file(asm)
+ # export all labels
+ if ':' in asm[:asm.find('"')]:
+ sys.stdout.write('GLOBAL ' + asm.split(':')[0] + '\n')
+
+ # expect preprocessed .asm files
+ if "INCLUDE" in asm:
+ asm = asm.replace('.asm','.tx')
+ sys.stdout.write(asm)
# ascii string macro preserves the bytes as ascii (skip the translator)
- elif len(asm) > 6 and "\tascii " in [asm[:7], "\t" + asm[:6]]:
+ elif len(asm) > 6 and "ascii " == asm[:6] or "\tascii " == asm[:7]:
asm = asm.replace("ascii", "db", 1)
sys.stdout.write(asm)
# convert text to bytes when a quote appears (not in a comment)
elif "\"" in asm:
- quote_translator(asm)
+ sys.stdout.write(quote_translator(asm))
# check against other preprocessor features
else:
macro, token = macro_test(asm)
-
if macro:
macro_translator(macro, token, asm)
else:
sys.stdout.write(asm)
- # show line comment
- if comment != None:
- sys.stdout.write(comment)
+ if comment: sys.stdout.write(comment)
def preprocess(lines=None):
"""Main entry point for the preprocessor."""
if not lines:
# read each line from stdin
- lines = sys.stdin
+ lines = (sys.stdin.readlines())
elif not isinstance(lines, list):
# split up the input into individual lines
lines = lines.split("\n")
diff --git a/prequeue.py b/prequeue.py
new file mode 100644
index 000000000..156d3e942
--- /dev/null
+++ b/prequeue.py
@@ -0,0 +1,17 @@
+# coding: utf-8
+
+# Starting a new python process to preprocess each source file
+# creates too much overhead. Instead, a list of files to preprocess
+# is fed into a script run from a single process.
+
+import os
+import sys
+import preprocessor
+
+if __name__ == '__main__':
+ for source in sys.argv[1:]:
+ dest = os.path.splitext(source)[0] + '.tx'
+ sys.stdin = open(source, 'r')
+ sys.stdout = open(dest, 'w')
+ preprocessor.preprocess()
+