mirror of
https://gitlab.com/xCrystal/pokecrystal-board.git
synced 2025-01-23 09:16:20 -08:00
commit
aee96a5cf7
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Finds shared functions between red/crystal.
|
Find shared functions between red/crystal.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from crystal import (
|
from crystal import (
|
||||||
@ -18,13 +18,13 @@ from romstr import (
|
|||||||
|
|
||||||
def load_rom(path):
|
def load_rom(path):
|
||||||
"""
|
"""
|
||||||
Loads a ROM file into an abbreviated RomStr object.
|
Load a ROM file into an abbreviated RomStr object.
|
||||||
"""
|
"""
|
||||||
return direct_load_rom(filename=path)
|
return direct_load_rom(filename=path)
|
||||||
|
|
||||||
def load_asm(path):
|
def load_asm(path):
|
||||||
"""
|
"""
|
||||||
Loads source ASM into an abbreviated AsmList object.
|
Load source ASM into an abbreviated AsmList object.
|
||||||
"""
|
"""
|
||||||
return direct_load_asm(filename=path)
|
return direct_load_asm(filename=path)
|
||||||
|
|
||||||
@ -63,7 +63,8 @@ found_blobs = []
|
|||||||
|
|
||||||
class BinaryBlob(object):
|
class BinaryBlob(object):
|
||||||
"""
|
"""
|
||||||
Stores a label, line number, and addresses of a function from Pokémon Red.
|
Store a label, line number, and addresses of a function from Pokémon Red.
|
||||||
|
|
||||||
These details can be used to determine whether or not the function was
|
These details can be used to determine whether or not the function was
|
||||||
copied into Pokémon Crystal.
|
copied into Pokémon Crystal.
|
||||||
"""
|
"""
|
||||||
@ -128,7 +129,7 @@ class BinaryBlob(object):
|
|||||||
|
|
||||||
def parse_from_red(self):
|
def parse_from_red(self):
|
||||||
"""
|
"""
|
||||||
Reads bytes from Pokémon Red and stores them.
|
Read bytes from Pokémon Red and stores them.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.bytes = redrom[self.start_address : self.end_address + 1]
|
self.bytes = redrom[self.start_address : self.end_address + 1]
|
||||||
@ -146,7 +147,7 @@ class BinaryBlob(object):
|
|||||||
|
|
||||||
def find_in_crystal(self):
|
def find_in_crystal(self):
|
||||||
"""
|
"""
|
||||||
Checks whether or not the bytes appear in Pokémon Crystal.
|
Check whether or not the bytes appear in Pokémon Crystal.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
finditer = findall_iter(self.bytes, cryrom)
|
finditer = findall_iter(self.bytes, cryrom)
|
||||||
@ -160,7 +161,7 @@ class BinaryBlob(object):
|
|||||||
|
|
||||||
def find_by_first_bytes(self):
|
def find_by_first_bytes(self):
|
||||||
"""
|
"""
|
||||||
Finds this blob in Crystal based on the first n bytes.
|
Find this blob in Crystal based on the first n bytes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# how many bytes to match
|
# how many bytes to match
|
||||||
@ -194,7 +195,7 @@ redsrc = load_asm(pokered_src_path)
|
|||||||
|
|
||||||
def scan_red_asm(bank_stop=3, debug=True):
|
def scan_red_asm(bank_stop=3, debug=True):
|
||||||
"""
|
"""
|
||||||
Scans the ASM from Pokémon Red. Finds labels and objects. Does things.
|
Scan the ASM from Pokémon Red. Finds labels and objects. Does things.
|
||||||
|
|
||||||
Uses get_label_from_line and get_address_from_line_comment.
|
Uses get_label_from_line and get_address_from_line_comment.
|
||||||
"""
|
"""
|
||||||
|
@ -13,6 +13,11 @@ if not hasattr(json, "read"):
|
|||||||
json.read = json.loads
|
json.read = json.loads
|
||||||
|
|
||||||
def load_rom(filename="../baserom.gbc"):
|
def load_rom(filename="../baserom.gbc"):
|
||||||
|
"""
|
||||||
|
Load the specified rom.
|
||||||
|
|
||||||
|
If no rom is given, load "../baserom.gbc".
|
||||||
|
"""
|
||||||
global rom
|
global rom
|
||||||
rom = bytearray(open(filename,'rb').read())
|
rom = bytearray(open(filename,'rb').read())
|
||||||
return rom
|
return rom
|
||||||
@ -557,6 +562,11 @@ call_commands = [0xdc, 0xd4, 0xc4, 0xcc, 0xcd]
|
|||||||
|
|
||||||
all_labels = {}
|
all_labels = {}
|
||||||
def load_labels(filename="labels.json"):
|
def load_labels(filename="labels.json"):
|
||||||
|
"""
|
||||||
|
Load labels from specified file.
|
||||||
|
|
||||||
|
If no filename is given, loads 'labels.json'.
|
||||||
|
"""
|
||||||
global all_labels
|
global all_labels
|
||||||
|
|
||||||
# don't re-load labels each time
|
# don't re-load labels each time
|
||||||
@ -588,21 +598,28 @@ def find_label(local_address, bank_id=0):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def asm_label(address):
|
def asm_label(address):
|
||||||
# why using a random value when you can use the address?
|
"""
|
||||||
|
Return the ASM label using the address.
|
||||||
|
"""
|
||||||
|
|
||||||
return ".ASM_" + hex(address)[2:]
|
return ".ASM_" + hex(address)[2:]
|
||||||
|
|
||||||
def output_bank_opcodes(original_offset, max_byte_count=0x4000, include_last_address=True, stop_at=[], debug = False):
|
def output_bank_opcodes(original_offset, max_byte_count=0x4000, include_last_address=True, stop_at=[], debug=False):
|
||||||
#fs = current_address
|
"""
|
||||||
#b = bank_byte
|
Output bank opcodes.
|
||||||
#in = input_data -- rom
|
|
||||||
#bank_size = byte_count
|
|
||||||
#i = offset
|
|
||||||
#ad = end_address
|
|
||||||
#a, oa = current_byte_number
|
|
||||||
|
|
||||||
# stop_at can be used to supply a list of addresses to not disassemble
|
fs = current_address
|
||||||
# over. This is useful if you know in advance that there are a lot of
|
b = bank_byte
|
||||||
# fall-throughs.
|
in = input_data -- rom
|
||||||
|
bank_size = byte_count
|
||||||
|
i = offset
|
||||||
|
ad = end_address
|
||||||
|
a, oa = current_byte_number
|
||||||
|
|
||||||
|
stop_at can be used to supply a list of addresses to not disassemble
|
||||||
|
over. This is useful if you know in advance that there are a lot of
|
||||||
|
fall-throughs.
|
||||||
|
"""
|
||||||
|
|
||||||
load_labels()
|
load_labels()
|
||||||
load_rom()
|
load_rom()
|
||||||
@ -851,8 +868,9 @@ def output_bank_opcodes(original_offset, max_byte_count=0x4000, include_last_add
|
|||||||
|
|
||||||
def has_outstanding_labels(byte_labels):
|
def has_outstanding_labels(byte_labels):
|
||||||
"""
|
"""
|
||||||
If a label is used once in the asm output, then that means it has to be
|
Check whether a label is used once in the asm output.
|
||||||
called or specified later.
|
|
||||||
|
If so, then that means it has to be called or specified later.
|
||||||
"""
|
"""
|
||||||
for label_line in byte_labels.keys():
|
for label_line in byte_labels.keys():
|
||||||
real_line = byte_labels[label_line]
|
real_line = byte_labels[label_line]
|
||||||
|
112
extras/gfx.py
112
extras/gfx.py
@ -16,6 +16,9 @@ if __name__ != "__main__":
|
|||||||
|
|
||||||
|
|
||||||
def mkdir_p(path):
|
def mkdir_p(path):
|
||||||
|
"""
|
||||||
|
Make a directory at a given path.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
except OSError as exc: # Python >2.5
|
except OSError as exc: # Python >2.5
|
||||||
@ -25,7 +28,9 @@ def mkdir_p(path):
|
|||||||
|
|
||||||
|
|
||||||
def hex_dump(input, debug = True):
|
def hex_dump(input, debug = True):
|
||||||
"""display hex dump in rows of 16 bytes"""
|
"""
|
||||||
|
Display hex dump in rows of 16 bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
dump = ''
|
dump = ''
|
||||||
output = ''
|
output = ''
|
||||||
@ -52,6 +57,7 @@ def hex_dump(input, debug = True):
|
|||||||
# margin
|
# margin
|
||||||
for char in range(margin):
|
for char in range(margin):
|
||||||
output += ' '
|
output += ' '
|
||||||
|
|
||||||
#
|
#
|
||||||
for byte in range(bytes_per_line):
|
for byte in range(bytes_per_line):
|
||||||
output += hex(byte)[2:].zfill(2) + ' '
|
output += hex(byte)[2:].zfill(2) + ' '
|
||||||
@ -71,7 +77,9 @@ def hex_dump(input, debug = True):
|
|||||||
|
|
||||||
|
|
||||||
def get_tiles(image):
|
def get_tiles(image):
|
||||||
"""split a 2bpp image into 8x8 tiles"""
|
"""
|
||||||
|
Split a 2bpp image into 8x8 tiles.
|
||||||
|
"""
|
||||||
tiles = []
|
tiles = []
|
||||||
tile = []
|
tile = []
|
||||||
bytes_per_tile = 16
|
bytes_per_tile = 16
|
||||||
@ -91,7 +99,9 @@ def get_tiles(image):
|
|||||||
|
|
||||||
|
|
||||||
def connect(tiles):
|
def connect(tiles):
|
||||||
"""combine 8x8 tiles into a 2bpp image"""
|
"""
|
||||||
|
Combine 8x8 tiles into a 2bpp image.
|
||||||
|
"""
|
||||||
out = []
|
out = []
|
||||||
for tile in tiles:
|
for tile in tiles:
|
||||||
for byte in tile:
|
for byte in tile:
|
||||||
@ -100,7 +110,9 @@ def connect(tiles):
|
|||||||
|
|
||||||
|
|
||||||
def transpose(tiles):
|
def transpose(tiles):
|
||||||
"""transpose a tile arrangement along line y=x"""
|
"""
|
||||||
|
Transpose a tile arrangement along line y=x.
|
||||||
|
"""
|
||||||
|
|
||||||
# horizontal <-> vertical
|
# horizontal <-> vertical
|
||||||
# 00 01 02 03 04 05 00 06 0c 12 18 1e
|
# 00 01 02 03 04 05 00 06 0c 12 18 1e
|
||||||
@ -184,10 +196,12 @@ lowmax = 1 << 5 # standard 5-bit param
|
|||||||
|
|
||||||
|
|
||||||
class Compressed:
|
class Compressed:
|
||||||
"""compress 2bpp data"""
|
|
||||||
|
"""
|
||||||
|
Compress 2bpp data.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, image = None, mode = 'horiz', size = None):
|
def __init__(self, image = None, mode = 'horiz', size = None):
|
||||||
|
|
||||||
assert image, 'need something to compress!'
|
assert image, 'need something to compress!'
|
||||||
image = list(image)
|
image = list(image)
|
||||||
self.image = image
|
self.image = image
|
||||||
@ -231,7 +245,9 @@ class Compressed:
|
|||||||
|
|
||||||
|
|
||||||
def compress(self):
|
def compress(self):
|
||||||
"""incomplete, but outputs working compressed data"""
|
"""
|
||||||
|
Incomplete, but outputs working compressed data.
|
||||||
|
"""
|
||||||
|
|
||||||
self.address = 0
|
self.address = 0
|
||||||
|
|
||||||
@ -308,10 +324,12 @@ class Compressed:
|
|||||||
|
|
||||||
|
|
||||||
def scanRepeats(self):
|
def scanRepeats(self):
|
||||||
"""works, but doesn't do flipped/reversed streams yet
|
"""
|
||||||
|
Works, but doesn't do flipped/reversed streams yet.
|
||||||
|
|
||||||
this takes up most of the compress time and only saves a few bytes
|
This takes up most of the compress time and only saves a few bytes
|
||||||
it might be more feasible to exclude it entirely"""
|
it might be more feasible to exclude it entirely.
|
||||||
|
"""
|
||||||
|
|
||||||
self.repeats = []
|
self.repeats = []
|
||||||
self.flips = []
|
self.flips = []
|
||||||
@ -567,7 +585,8 @@ class Compressed:
|
|||||||
|
|
||||||
|
|
||||||
class Decompressed:
|
class Decompressed:
|
||||||
"""parse compressed 2bpp data
|
"""
|
||||||
|
Parse compressed 2bpp data.
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
[compressed 2bpp data]
|
[compressed 2bpp data]
|
||||||
@ -576,7 +595,8 @@ class Decompressed:
|
|||||||
[start] (optional)
|
[start] (optional)
|
||||||
|
|
||||||
splits output into pic [size] and animation tiles if applicable
|
splits output into pic [size] and animation tiles if applicable
|
||||||
data can be fed in from rom if [start] is specified"""
|
data can be fed in from rom if [start] is specified
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, lz = None, mode = None, size = None, start = 0):
|
def __init__(self, lz = None, mode = None, size = None, start = 0):
|
||||||
# todo: play nice with Compressed
|
# todo: play nice with Compressed
|
||||||
@ -615,7 +635,9 @@ class Decompressed:
|
|||||||
|
|
||||||
|
|
||||||
def decompress(self):
|
def decompress(self):
|
||||||
"""replica of crystal's decompression"""
|
"""
|
||||||
|
Replica of crystal's decompression.
|
||||||
|
"""
|
||||||
|
|
||||||
self.output = []
|
self.output = []
|
||||||
|
|
||||||
@ -674,19 +696,25 @@ class Decompressed:
|
|||||||
self.getCurByte()
|
self.getCurByte()
|
||||||
|
|
||||||
def doLiteral(self):
|
def doLiteral(self):
|
||||||
# copy 2bpp data directly
|
"""
|
||||||
|
Copy 2bpp data directly.
|
||||||
|
"""
|
||||||
for byte in range(self.length):
|
for byte in range(self.length):
|
||||||
self.next()
|
self.next()
|
||||||
self.output.append(self.byte)
|
self.output.append(self.byte)
|
||||||
|
|
||||||
def doIter(self):
|
def doIter(self):
|
||||||
# write one byte repeatedly
|
"""
|
||||||
|
Write one byte repeatedly.
|
||||||
|
"""
|
||||||
self.next()
|
self.next()
|
||||||
for byte in range(self.length):
|
for byte in range(self.length):
|
||||||
self.output.append(self.byte)
|
self.output.append(self.byte)
|
||||||
|
|
||||||
def doAlt(self):
|
def doAlt(self):
|
||||||
# write alternating bytes
|
"""
|
||||||
|
Write alternating bytes.
|
||||||
|
"""
|
||||||
self.alts = []
|
self.alts = []
|
||||||
self.next()
|
self.next()
|
||||||
self.alts.append(self.byte)
|
self.alts.append(self.byte)
|
||||||
@ -697,25 +725,34 @@ class Decompressed:
|
|||||||
self.output.append(self.alts[byte&1])
|
self.output.append(self.alts[byte&1])
|
||||||
|
|
||||||
def doZeros(self):
|
def doZeros(self):
|
||||||
# write zeros
|
"""
|
||||||
|
Write zeros.
|
||||||
|
"""
|
||||||
for byte in range(self.length):
|
for byte in range(self.length):
|
||||||
self.output.append(0x00)
|
self.output.append(0x00)
|
||||||
|
|
||||||
def doFlip(self):
|
def doFlip(self):
|
||||||
# repeat flipped bytes from 2bpp output
|
"""
|
||||||
# eg 11100100 -> 00100111
|
Repeat flipped bytes from 2bpp output.
|
||||||
# quat 3 2 1 0 -> 0 2 1 3
|
|
||||||
|
eg 11100100 -> 00100111
|
||||||
|
quat 3 2 1 0 -> 0 2 1 3
|
||||||
|
"""
|
||||||
for byte in range(self.length):
|
for byte in range(self.length):
|
||||||
flipped = sum(1<<(7-i) for i in range(8) if self.output[self.displacement+byte]>>i&1)
|
flipped = sum(1<<(7-i) for i in range(8) if self.output[self.displacement+byte]>>i&1)
|
||||||
self.output.append(flipped)
|
self.output.append(flipped)
|
||||||
|
|
||||||
def doReverse(self):
|
def doReverse(self):
|
||||||
# repeat reversed bytes from 2bpp output
|
"""
|
||||||
|
Repeat reversed bytes from 2bpp output.
|
||||||
|
"""
|
||||||
for byte in range(self.length):
|
for byte in range(self.length):
|
||||||
self.output.append(self.output[self.displacement-byte])
|
self.output.append(self.output[self.displacement-byte])
|
||||||
|
|
||||||
def doRepeat(self):
|
def doRepeat(self):
|
||||||
# repeat bytes from 2bpp output
|
"""
|
||||||
|
Repeat bytes from 2bpp output.
|
||||||
|
"""
|
||||||
for byte in range(self.length):
|
for byte in range(self.length):
|
||||||
self.output.append(self.output[self.displacement+byte])
|
self.output.append(self.output[self.displacement+byte])
|
||||||
|
|
||||||
@ -741,7 +778,9 @@ sizes = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
def make_sizes():
|
def make_sizes():
|
||||||
"""front pics have specified sizes"""
|
"""
|
||||||
|
Front pics have specified sizes.
|
||||||
|
"""
|
||||||
top = 251
|
top = 251
|
||||||
base_stats = 0x51424
|
base_stats = 0x51424
|
||||||
# print monster sizes
|
# print monster sizes
|
||||||
@ -955,7 +994,9 @@ def decompress_misc():
|
|||||||
to_file(filename, gfx.output)
|
to_file(filename, gfx.output)
|
||||||
|
|
||||||
def decompress_all(debug = False):
|
def decompress_all(debug = False):
|
||||||
"""decompress all known compressed data in baserom"""
|
"""
|
||||||
|
Decompress all known compressed data in baserom.
|
||||||
|
"""
|
||||||
|
|
||||||
if debug: print 'fronts'
|
if debug: print 'fronts'
|
||||||
decompress_monsters(front)
|
decompress_monsters(front)
|
||||||
@ -988,7 +1029,9 @@ def decompress_all(debug = False):
|
|||||||
|
|
||||||
|
|
||||||
def decompress_from_address(address, mode='horiz', filename = 'de.2bpp', size = None):
|
def decompress_from_address(address, mode='horiz', filename = 'de.2bpp', size = None):
|
||||||
"""write decompressed data from an address to a 2bpp file"""
|
"""
|
||||||
|
Write decompressed data from an address to a 2bpp file.
|
||||||
|
"""
|
||||||
image = Decompressed(rom, mode, size, address)
|
image = Decompressed(rom, mode, size, address)
|
||||||
to_file(filename, image.pic)
|
to_file(filename, image.pic)
|
||||||
|
|
||||||
@ -1034,7 +1077,9 @@ def compress_monster_frontpic(id, fileout):
|
|||||||
|
|
||||||
|
|
||||||
def get_uncompressed_gfx(start, num_tiles, filename):
|
def get_uncompressed_gfx(start, num_tiles, filename):
|
||||||
"""grab tiles directly from rom and write to file"""
|
"""
|
||||||
|
Grab tiles directly from rom and write to file.
|
||||||
|
"""
|
||||||
bytes_per_tile = 0x10
|
bytes_per_tile = 0x10
|
||||||
length = num_tiles*bytes_per_tile
|
length = num_tiles*bytes_per_tile
|
||||||
end = start + length
|
end = start + length
|
||||||
@ -1139,7 +1184,7 @@ def dump_trainer_pals():
|
|||||||
|
|
||||||
def flatten(planar):
|
def flatten(planar):
|
||||||
"""
|
"""
|
||||||
Flattens planar 2bpp image data into a quaternary pixel map.
|
Flatten planar 2bpp image data into a quaternary pixel map.
|
||||||
"""
|
"""
|
||||||
strips = []
|
strips = []
|
||||||
for pair in range(len(planar)/2):
|
for pair in range(len(planar)/2):
|
||||||
@ -1155,7 +1200,7 @@ def flatten(planar):
|
|||||||
|
|
||||||
def to_lines(image, width):
|
def to_lines(image, width):
|
||||||
"""
|
"""
|
||||||
Converts a tiled quaternary pixel map to lines of quaternary pixels.
|
Convert a tiled quaternary pixel map to lines of quaternary pixels.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tile = 8 * 8
|
tile = 8 * 8
|
||||||
@ -1208,7 +1253,7 @@ def png_pal(filename):
|
|||||||
|
|
||||||
def to_png(filein, fileout=None, pal_file=None, height=None, width=None):
|
def to_png(filein, fileout=None, pal_file=None, height=None, width=None):
|
||||||
"""
|
"""
|
||||||
Takes a planar 2bpp graphics file and converts it to png.
|
Take a planar 2bpp graphics file and converts it to png.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if fileout == None: fileout = '.'.join(filein.split('.')[:-1]) + '.png'
|
if fileout == None: fileout = '.'.join(filein.split('.')[:-1]) + '.png'
|
||||||
@ -1296,7 +1341,7 @@ def to_png(filein, fileout=None, pal_file=None, height=None, width=None):
|
|||||||
|
|
||||||
def to_2bpp(filein, fileout=None, palout=None):
|
def to_2bpp(filein, fileout=None, palout=None):
|
||||||
"""
|
"""
|
||||||
Takes a png and converts it to planar 2bpp.
|
Take a png and converts it to planar 2bpp.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if fileout == None: fileout = '.'.join(filein.split('.')[:-1]) + '.2bpp'
|
if fileout == None: fileout = '.'.join(filein.split('.')[:-1]) + '.2bpp'
|
||||||
@ -1500,7 +1545,7 @@ def append_terminator_to_lzs(directory):
|
|||||||
|
|
||||||
def lz_to_png_by_file(filename):
|
def lz_to_png_by_file(filename):
|
||||||
"""
|
"""
|
||||||
Converts a lz file to png. Dumps a 2bpp file too.
|
Convert a lz file to png. Dump a 2bpp file too.
|
||||||
"""
|
"""
|
||||||
assert filename[-3:] == ".lz"
|
assert filename[-3:] == ".lz"
|
||||||
lz_data = open(filename, "rb").read()
|
lz_data = open(filename, "rb").read()
|
||||||
@ -1511,8 +1556,9 @@ def lz_to_png_by_file(filename):
|
|||||||
|
|
||||||
def dump_tileset_pngs():
|
def dump_tileset_pngs():
|
||||||
"""
|
"""
|
||||||
Converts .lz format tilesets into .png format tilesets. Also, leaves a
|
Convert .lz format tilesets into .png format tilesets.
|
||||||
bunch of wonderful .2bpp files everywhere for your amusement.
|
|
||||||
|
Also, leaves a bunch of wonderful .2bpp files everywhere for your amusement.
|
||||||
"""
|
"""
|
||||||
for tileset_id in range(37):
|
for tileset_id in range(37):
|
||||||
tileset_filename = "../gfx/tilesets/" + str(tileset_id).zfill(2) + ".lz"
|
tileset_filename = "../gfx/tilesets/" + str(tileset_id).zfill(2) + ".lz"
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import gfx
|
import gfx
|
||||||
|
|
||||||
def rip_sprites_from_bank(bank, offset=0):
|
def rip_sprites_from_bank(bank, offset=0):
|
||||||
|
"""
|
||||||
|
Rips sprites from specified bank.
|
||||||
|
|
||||||
|
Sprites are 4x4.
|
||||||
|
"""
|
||||||
file_handler = open("../gfx/overworld/bank" + str(hex(bank))[2:] + ".asm", "w")
|
file_handler = open("../gfx/overworld/bank" + str(hex(bank))[2:] + ".asm", "w")
|
||||||
for sprite in range(0 + offset, 256 + offset):
|
for sprite in range(0 + offset, 256 + offset):
|
||||||
filename = "../gfx/overworld/" + str(sprite).zfill(3) + ".2bpp"
|
filename = "../gfx/overworld/" + str(sprite).zfill(3) + ".2bpp"
|
||||||
|
@ -50,7 +50,7 @@ class RomStr(str):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, filename=None, crystal=True, red=False):
|
def load(cls, filename=None, crystal=True, red=False):
|
||||||
"""
|
"""
|
||||||
Loads a ROM into a RomStr.
|
Load a ROM into a RomStr.
|
||||||
"""
|
"""
|
||||||
if crystal and not red and not filename:
|
if crystal and not red and not filename:
|
||||||
file_handler = open("../baserom.gbc", "r")
|
file_handler = open("../baserom.gbc", "r")
|
||||||
@ -66,8 +66,10 @@ class RomStr(str):
|
|||||||
|
|
||||||
def load_labels(self, filename="labels.json"):
|
def load_labels(self, filename="labels.json"):
|
||||||
"""
|
"""
|
||||||
Loads labels from labels.json, or parses the source code file and
|
Loads labels from labels.json.
|
||||||
generates new labels.
|
|
||||||
|
(Or parses the source code file and
|
||||||
|
generates new labels.)
|
||||||
"""
|
"""
|
||||||
filename = os.path.join(os.path.dirname(__file__), filename)
|
filename = os.path.join(os.path.dirname(__file__), filename)
|
||||||
|
|
||||||
@ -114,8 +116,9 @@ class RomStr(str):
|
|||||||
|
|
||||||
def get_address_for(self, label):
|
def get_address_for(self, label):
|
||||||
"""
|
"""
|
||||||
Returns the address of a label. This is slow and could be improved
|
Return the address of a label.
|
||||||
dramatically.
|
|
||||||
|
This is slow and could be improved dramatically.
|
||||||
"""
|
"""
|
||||||
label = str(label)
|
label = str(label)
|
||||||
for address in self.labels.keys():
|
for address in self.labels.keys():
|
||||||
@ -137,7 +140,7 @@ class RomStr(str):
|
|||||||
|
|
||||||
def interval(self, offset, length, strings=True, debug=True):
|
def interval(self, offset, length, strings=True, debug=True):
|
||||||
"""
|
"""
|
||||||
returns hex values for the rom starting at offset until offset+length
|
Return hex values for the rom starting at offset until offset+length.
|
||||||
"""
|
"""
|
||||||
returnable = []
|
returnable = []
|
||||||
for byte in self[offset:offset+length]:
|
for byte in self[offset:offset+length]:
|
||||||
@ -149,16 +152,17 @@ class RomStr(str):
|
|||||||
|
|
||||||
def until(self, offset, byte, strings=True, debug=False):
|
def until(self, offset, byte, strings=True, debug=False):
|
||||||
"""
|
"""
|
||||||
Returns hex values from rom starting at offset until the given byte.
|
Return hex values from rom starting at offset until the given byte.
|
||||||
"""
|
"""
|
||||||
return self.interval(offset, self.find(chr(byte), offset) - offset, strings=strings)
|
return self.interval(offset, self.find(chr(byte), offset) - offset, strings=strings)
|
||||||
|
|
||||||
def to_asm(self, address, end_address=None, size=None, max_size=0x4000, debug=None):
|
def to_asm(self, address, end_address=None, size=None, max_size=0x4000, debug=None):
|
||||||
"""
|
"""
|
||||||
Disassembles ASM at some address. This will stop disassembling when
|
Disassemble ASM at some address.
|
||||||
either the end_address or size is met. Also, there's a maximum size
|
|
||||||
that will be parsed, so that large patches of data aren't parsed as
|
This will stop disassembling when either the end_address or size is
|
||||||
code.
|
met. Also, there's a maximum size that will be parsed, so that large
|
||||||
|
patches of data aren't parsed as code.
|
||||||
"""
|
"""
|
||||||
if type(address) in [str, unicode] and "0x" in address:
|
if type(address) in [str, unicode] and "0x" in address:
|
||||||
address = int(address, 16)
|
address = int(address, 16)
|
||||||
|
126
extras/vba.py
126
extras/vba.py
@ -6,13 +6,15 @@ vba-clojure (but really it's jython/python/jvm)
|
|||||||
This is jython, not python. Use jython to run this file. Before running this
|
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
|
file, some of the dependencies need to be constructed. These can be obtained
|
||||||
from the vba-clojure project.
|
from the vba-clojure project.
|
||||||
sudo apt-get install g++ libtool openjdk-6-jre openjdk-6-jdk libsdl1.2-dev ant jython
|
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_PATH=/usr/lib/jvm/java-6-openjdk-amd64/include/
|
||||||
export JAVA_INCLUDE_PATH2=/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
|
hg clone http://hg.bortreb.com/vba-clojure
|
||||||
cd vba-clojure/java/
|
cd vba-clojure/
|
||||||
|
./dl-libs.sh
|
||||||
|
cd java/
|
||||||
ant all
|
ant all
|
||||||
cd ..
|
cd ..
|
||||||
autoreconf -i
|
autoreconf -i
|
||||||
@ -28,6 +30,9 @@ Make sure vba-clojure is available within "java.library.path":
|
|||||||
$HOME/local/vba-clojure/vba-clojure/src/clojure/.libs/libvba.so.0.0.0 \
|
$HOME/local/vba-clojure/vba-clojure/src/clojure/.libs/libvba.so.0.0.0 \
|
||||||
/usr/lib/jni/libvba.so
|
/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.
|
Also make sure VisualBoyAdvance.cfg is somewhere in the $PATH for VBA to find.
|
||||||
A default configuration is provided in vba-clojure under src/.
|
A default configuration is provided in vba-clojure under src/.
|
||||||
|
|
||||||
@ -114,15 +119,19 @@ if not os.path.exists(rom_path):
|
|||||||
|
|
||||||
def _check_java_library_path():
|
def _check_java_library_path():
|
||||||
"""
|
"""
|
||||||
Returns the value of java.library.path. The vba-clojure library must be
|
Returns the value of java.library.path.
|
||||||
compiled and linked from this location.
|
|
||||||
|
The vba-clojure library must be compiled
|
||||||
|
and linked from this location.
|
||||||
"""
|
"""
|
||||||
return System.getProperty("java.library.path")
|
return System.getProperty("java.library.path")
|
||||||
|
|
||||||
class RomList(list):
|
class RomList(list):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Simple wrapper to prevent a giant rom from being shown on screen.
|
Simple wrapper to prevent a giant rom from being shown on screen.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
list.__init__(self, *args, **kwargs)
|
list.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
@ -150,8 +159,9 @@ a, b, r, l, u, d, select, start, restart = "a", "b", "r", "l", "u", "d", "select
|
|||||||
|
|
||||||
def button_combiner(buttons):
|
def button_combiner(buttons):
|
||||||
"""
|
"""
|
||||||
Combines multiple button presses into an integer. This is used when sending
|
Combines multiple button presses into an integer.
|
||||||
a keypress to the emulator.
|
|
||||||
|
This is used when sending a keypress to the emulator.
|
||||||
"""
|
"""
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
@ -186,8 +196,9 @@ def button_combiner(buttons):
|
|||||||
|
|
||||||
def load_rom(path=None):
|
def load_rom(path=None):
|
||||||
"""
|
"""
|
||||||
Starts the emulator with a certain ROM. Defaults to rom_path if no
|
Starts the emulator with a certain ROM.
|
||||||
parameters are given.
|
|
||||||
|
Defaults to rom_path if no parameters are given.
|
||||||
"""
|
"""
|
||||||
if path == None:
|
if path == None:
|
||||||
path = rom_path
|
path = rom_path
|
||||||
@ -204,8 +215,9 @@ def load_rom(path=None):
|
|||||||
|
|
||||||
def shutdown():
|
def shutdown():
|
||||||
"""
|
"""
|
||||||
Stops the emulator. Closes the window. The "opposite" of this is the
|
Stops the emulator. Closes the window.
|
||||||
load_rom function.
|
|
||||||
|
The "opposite" of this is the load_rom function.
|
||||||
"""
|
"""
|
||||||
Gb.shutdown()
|
Gb.shutdown()
|
||||||
|
|
||||||
@ -239,8 +251,9 @@ def translate_chars(charz):
|
|||||||
|
|
||||||
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.
|
||||||
class.
|
|
||||||
|
This is useful for interfacing with the Gb class.
|
||||||
"""
|
"""
|
||||||
buf = ByteBuffer.allocateDirect(len(data))
|
buf = ByteBuffer.allocateDirect(len(data))
|
||||||
if isinstance(data[0], int):
|
if isinstance(data[0], int):
|
||||||
@ -253,9 +266,11 @@ def _create_byte_buffer(data):
|
|||||||
|
|
||||||
def set_state(state, do_step=False):
|
def set_state(state, do_step=False):
|
||||||
"""
|
"""
|
||||||
Injects the given state into the emulator. Use do_step if you want to call
|
Injects the given state into the emulator.
|
||||||
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
|
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.
|
as if it still has the last state loaded. This is normal.
|
||||||
"""
|
"""
|
||||||
Gb.loadState(_create_byte_buffer(state))
|
Gb.loadState(_create_byte_buffer(state))
|
||||||
@ -274,7 +289,9 @@ def get_state():
|
|||||||
|
|
||||||
def save_state(name, state=None, override=False):
|
def save_state(name, state=None, override=False):
|
||||||
"""
|
"""
|
||||||
Saves the given state to save_state_path. The file format must be ".sav"
|
Saves the given state to save_state_path.
|
||||||
|
|
||||||
|
The file format must be ".sav"
|
||||||
(and this will be appended to your string if necessary).
|
(and this will be appended to your string if necessary).
|
||||||
"""
|
"""
|
||||||
if state == None:
|
if state == None:
|
||||||
@ -296,7 +313,9 @@ def save_state(name, state=None, override=False):
|
|||||||
|
|
||||||
def load_state(name):
|
def load_state(name):
|
||||||
"""
|
"""
|
||||||
Reads a state from file based on name. Looks in save_state_path for a file
|
Reads a state from file based on name.
|
||||||
|
|
||||||
|
Looks in save_state_path for a file
|
||||||
with this name (".sav" is optional).
|
with this name (".sav" is optional).
|
||||||
"""
|
"""
|
||||||
save_path = os.path.join(save_state_path, name)
|
save_path = os.path.join(save_state_path, name)
|
||||||
@ -321,8 +340,9 @@ def generate_root():
|
|||||||
|
|
||||||
def get_root():
|
def get_root():
|
||||||
"""
|
"""
|
||||||
Loads the root state, or restarts the emulator and creates a new root
|
Loads the root state.
|
||||||
state.
|
|
||||||
|
(Or restarts the emulator and creates a new root state.)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
root = load_state("root")
|
root = load_state("root")
|
||||||
@ -377,15 +397,17 @@ def get_memory():
|
|||||||
|
|
||||||
def set_memory(memory):
|
def set_memory(memory):
|
||||||
"""
|
"""
|
||||||
Sets memory in the emulator. Use get_memory() to retrieve the current
|
Sets memory in the emulator.
|
||||||
state.
|
|
||||||
|
Use get_memory() to retrieve the current state.
|
||||||
"""
|
"""
|
||||||
Gb.writeMemory(memory)
|
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.
|
||||||
screenshot() instead.
|
|
||||||
|
Broken, probably. Use screenshot() instead.
|
||||||
"""
|
"""
|
||||||
sys.stderr.write("ERROR: seems to be broken on VBA's end? Good luck. Use"
|
sys.stderr.write("ERROR: seems to be broken on VBA's end? Good luck. Use"
|
||||||
" screenshot() instead.\n")
|
" screenshot() instead.\n")
|
||||||
@ -396,9 +418,10 @@ def get_pixels():
|
|||||||
|
|
||||||
def screenshot(filename, literal=False):
|
def screenshot(filename, literal=False):
|
||||||
"""
|
"""
|
||||||
Saves a PNG screenshot to the file at filename. Use literal if you want to
|
Saves a PNG screenshot to the file at filename.
|
||||||
store it in the current directory. Default is to save it to screenshots/
|
|
||||||
under the project.
|
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/")
|
screenshots_path = os.path.join(project_path, "screenshots/")
|
||||||
filename = os.path.join(screenshots_path, filename)
|
filename = os.path.join(screenshots_path, filename)
|
||||||
@ -430,14 +453,18 @@ def get_memory_range(start_address, byte_count):
|
|||||||
|
|
||||||
def set_memory_at(address, value):
|
def set_memory_at(address, value):
|
||||||
"""
|
"""
|
||||||
Sets a byte at a certain address in memory. This directly sets the memory
|
Sets a byte at a certain address in memory.
|
||||||
instead of copying the memory from the emulator.
|
|
||||||
|
This directly sets the memory instead of copying
|
||||||
|
the memory from the emulator.
|
||||||
"""
|
"""
|
||||||
Gb.setMemoryAt(address, value)
|
Gb.setMemoryAt(address, value)
|
||||||
|
|
||||||
def press(buttons, holdsteps=1, aftersteps=1):
|
def press(buttons, holdsteps=1, aftersteps=1):
|
||||||
"""
|
"""
|
||||||
Press a button. Use steplimit to say for how many steps you want to press
|
Press a button.
|
||||||
|
|
||||||
|
Use steplimit to say for how many steps you want to press
|
||||||
the button (try leaving it at the default, 1).
|
the button (try leaving it at the default, 1).
|
||||||
"""
|
"""
|
||||||
if hasattr(buttons, "__len__"):
|
if hasattr(buttons, "__len__"):
|
||||||
@ -456,7 +483,9 @@ def press(buttons, holdsteps=1, aftersteps=1):
|
|||||||
|
|
||||||
def get_buttons():
|
def get_buttons():
|
||||||
"""
|
"""
|
||||||
Returns the currentButtons[0] value (an integer with bits set for which
|
Returns the currentButtons[0] value
|
||||||
|
|
||||||
|
(an integer with bits set for which
|
||||||
buttons are currently pressed).
|
buttons are currently pressed).
|
||||||
"""
|
"""
|
||||||
return Gb.getCurrentButtons()
|
return Gb.getCurrentButtons()
|
||||||
@ -708,6 +737,7 @@ class cheats:
|
|||||||
Gb.cheatAddGameshark(code, description)
|
Gb.cheatAddGameshark(code, description)
|
||||||
|
|
||||||
class crystal:
|
class crystal:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Just a simple namespace to store a bunch of functions for Pokémon Crystal.
|
Just a simple namespace to store a bunch of functions for Pokémon Crystal.
|
||||||
"""
|
"""
|
||||||
@ -715,8 +745,7 @@ class crystal:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def text_wait(step_size=10, max_wait=500):
|
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 when text is done being drawn to screen.
|
||||||
presses the "A" button.
|
|
||||||
|
|
||||||
:param step_size: number of steps per wait loop
|
:param step_size: number of steps per wait loop
|
||||||
:param max_wait: number of wait loops to perform
|
:param max_wait: number of wait loops to perform
|
||||||
@ -744,10 +773,12 @@ class crystal:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def walk_through_walls():
|
def walk_through_walls():
|
||||||
"""
|
"""
|
||||||
Lets the player walk all over the map. These values are probably reset
|
Lets the player walk all over the map.
|
||||||
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
|
These values are probably reset by some of the map/collision
|
||||||
walk-through-walls is desired.
|
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(0xC2FA, 0)
|
||||||
set_memory_at(0xC2FB, 0)
|
set_memory_at(0xC2FB, 0)
|
||||||
@ -761,8 +792,9 @@ class crystal:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def nstep(steplimit=500):
|
def nstep(steplimit=500):
|
||||||
"""
|
"""
|
||||||
Steps the CPU forward and calls some functions in between each step,
|
Steps the CPU forward and calls some functions in between each step.
|
||||||
like to manipulate memory. This is pretty slow.
|
|
||||||
|
(For example, to manipulate memory.) This is pretty slow.
|
||||||
"""
|
"""
|
||||||
for step_counter in range(0, steplimit):
|
for step_counter in range(0, steplimit):
|
||||||
crystal.walk_through_walls()
|
crystal.walk_through_walls()
|
||||||
@ -806,6 +838,7 @@ class crystal:
|
|||||||
def get_xy():
|
def get_xy():
|
||||||
"""
|
"""
|
||||||
(x, y) coordinates of player on map.
|
(x, y) coordinates of player on map.
|
||||||
|
|
||||||
Relative to top-left corner of map.
|
Relative to top-left corner of map.
|
||||||
"""
|
"""
|
||||||
x = get_memory_at(0xdcb8)
|
x = get_memory_at(0xdcb8)
|
||||||
@ -815,9 +848,10 @@ class crystal:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def menu_select(id=1):
|
def menu_select(id=1):
|
||||||
"""
|
"""
|
||||||
Sets the cursor to the given pokemon in the player's party. This is
|
Sets the cursor to the given pokemon in the player's party.
|
||||||
under Start -> PKMN. This is useful for selecting a certain pokemon
|
|
||||||
with fly or another skill.
|
This is under Start -> PKMN. This is useful for selecting a
|
||||||
|
certain pokemon with fly or another skill.
|
||||||
|
|
||||||
This probably works on other menus.
|
This probably works on other menus.
|
||||||
"""
|
"""
|
||||||
@ -902,8 +936,9 @@ class crystal:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_text():
|
def get_text():
|
||||||
"""
|
"""
|
||||||
Returns alphanumeric text on the screen. Other characters will not be
|
Returns alphanumeric text on the screen.
|
||||||
shown.
|
|
||||||
|
Other characters will not be shown.
|
||||||
"""
|
"""
|
||||||
output = ""
|
output = ""
|
||||||
tiles = get_memory_range(0xc4a0, 1000)
|
tiles = get_memory_range(0xc4a0, 1000)
|
||||||
@ -942,8 +977,9 @@ class crystal:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def write(something="TrAiNeR"):
|
def write(something="TrAiNeR"):
|
||||||
"""
|
"""
|
||||||
Uses a planning algorithm to type out a word in the most efficient way
|
Types out a word.
|
||||||
possible.
|
|
||||||
|
Uses a planning algorithm to do this in the most efficient way possible.
|
||||||
"""
|
"""
|
||||||
button_sequence = keyboard.plan_typing(something)
|
button_sequence = keyboard.plan_typing(something)
|
||||||
crystal.keyboard_apply([[x] for x in button_sequence])
|
crystal.keyboard_apply([[x] for x in button_sequence])
|
||||||
|
@ -26,8 +26,10 @@ def main():
|
|||||||
|
|
||||||
def skippable(func):
|
def skippable(func):
|
||||||
"""
|
"""
|
||||||
Makes a function skippable by saving the state before and after the
|
Makes a function skippable.
|
||||||
function runs. Pass "skip=True" to the function to load the previous save
|
|
||||||
|
Saves 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.
|
state from when the function finished.
|
||||||
"""
|
"""
|
||||||
def wrapped_function(*args, **kwargs):
|
def wrapped_function(*args, **kwargs):
|
||||||
@ -433,6 +435,8 @@ def handle_elm(starter_choice):
|
|||||||
@skippable
|
@skippable
|
||||||
def new_bark_level_grind(level):
|
def new_bark_level_grind(level):
|
||||||
"""
|
"""
|
||||||
|
Do level grinding in New Bark.
|
||||||
|
|
||||||
Starting just outside of Elm's Lab, do some level grinding until the first
|
Starting just outside of Elm's Lab, do some level grinding until the first
|
||||||
partymon level is equal to the given value..
|
partymon level is equal to the given value..
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user