shithub: pokecrystal

Download patch

ref: 4b2d4337b4f1edbac50fc369c0207043e0a9cce3
parent: ac773a9dc1499f7070dc98236c66de6309885210
parent: 859c70ff42588a9de354063c42f6665e5d174299
author: Bryan Bishop <kanzure@gmail.com>
date: Tue Jun 25 16:10:38 EDT 2013

Merge pull request #149 from yenatch/new-install

reformat install + fix source preprocessing

--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,6 +1,6 @@
 # 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
@@ -7,17 +7,108 @@
 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 
 
 # unittest2 is required if using python2.6
@@ -46,36 +137,21 @@
 git config diff.hex.textconv hexdump
 ```
 
-To compile the ROM from ASM source:
+To compile the rom from asm source:
+```bash
+make
 ```
-make clean && 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!
+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
+# Now what?
 
-Set up [GitHub for Windows](http://windows.github.com/) and clone this repository.
+**main.asm** is a good starting point. The structure of the source is laid out here.
 
-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)).
+* **Can't find something?** Anyone can add to the source. There's lots to be uncovered.
 
-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`
+* **Do your own thing!** The asm source is hack-friendly, and the supplementary scripts in extras/ can be used for other projects.
 
-Install [make](http://gnuwin32.sourceforge.net/downlinks/make.php) for Windows.
-
-To compile the ROM from ASM source, run `pokecrystal.bat`.
-
-That will take between 3 and 15 seconds, depending on your computer. If you see
-`FC: no differences encountered`, the build was successful! Rejoice!
-
-Now you may try messing around with `main.asm`, or just do whatever you wanted to.
-
-
-# Contributions are welcome!
+* We'll be happy to answer any **questions** on **[nucleus.kafuka.org #skeetendo](https://kiwiirc.com/client/irc.nolimitzone.com/?#skeetendo)**.
 
--- 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
+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')
+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 @@
 	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 $^
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 
 ## 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 @@
 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
 
-* Tackle some [issues](https://github.com/kanzure/pokecrystal/issues)!
+* 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)**.
+
--- a/preprocessor.py
+++ b/preprocessor.py
@@ -310,25 +310,15 @@
     """
     Separates asm and comments on a single line.
     """
-
-    asm        = ""
-    comment    = None
     in_quotes  = False
+    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
 
-    # 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
-
 def quote_translator(asm):
     """
     Writes asm with quoted text translated into bytes.
@@ -335,27 +325,22 @@
     """
 
     # 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 asms[0]\
+    or "INCBIN"  in asms[0]\
+    or "INCLUDE" in asms[0]:
+        return asm
 
-    if "section" in lowasm \
-    or "incbin" in lowasm:
-        sys.stdout.write(asm)
-        return
-
     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
@@ -362,36 +347,28 @@
             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,22 +398,18 @@
 
             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 output
 
-    return
-
 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):
@@ -443,10 +416,8 @@
     """
     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,57 +571,39 @@
 
     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."""
@@ -657,7 +610,7 @@
 
     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")
--- /dev/null
+++ b/prequeue.py
@@ -1,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()
+
--