summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pokemontools/audio.py2
-rw-r--r--pokemontools/battle_animations.py294
-rw-r--r--pokemontools/crystal.py24
-rw-r--r--pokemontools/gfx.py108
-rw-r--r--pokemontools/preprocessor.py84
-rw-r--r--pokemontools/wram.py20
6 files changed, 414 insertions, 118 deletions
diff --git a/pokemontools/audio.py b/pokemontools/audio.py
index 38fd65f..0e7d375 100644
--- a/pokemontools/audio.py
+++ b/pokemontools/audio.py
@@ -26,7 +26,7 @@ conf = configuration.Config()
def sort_asms(asms):
"""sort and remove duplicates from a list of tuples
format (address, asm, last_address)"""
- return sorted(set(asms), key=lambda (x,y,z):(x,z,not y.startswith(';'), ':' not in y))
+ return sorted(set(asms), key=lambda (x,y,z):(x,z,not y.startswith(';'), ':' not in y, y))
class NybbleParam:
size = 0.5
diff --git a/pokemontools/battle_animations.py b/pokemontools/battle_animations.py
new file mode 100644
index 0000000..96f0090
--- /dev/null
+++ b/pokemontools/battle_animations.py
@@ -0,0 +1,294 @@
+# coding: utf-8
+
+import os
+from new import classobj
+
+import configuration
+conf = configuration.Config()
+
+from crystal import (
+ SingleByteParam,
+ PointerLabelParam,
+ DecimalParam,
+ BigEndianParam,
+ Command,
+ load_rom
+)
+
+from gbz80disasm import get_local_address, get_global_address
+from audio import sort_asms
+
+
+from wram import read_constants
+
+rom = bytearray(load_rom())
+
+sfx_constants = read_constants(os.path.join(conf.path, 'constants/sfx_constants.asm'))
+class SoundEffectParam(SingleByteParam):
+ def to_asm(self):
+ if self.byte in sfx_constants.keys():
+ sfx_constant = sfx_constants[self.byte]
+ return sfx_constant
+ return SingleByteParam.to_asm(self)
+
+anim_gfx_constants = read_constants(os.path.join(conf.path, 'constants/gfx_constants.asm'))
+class AnimGFXParam(SingleByteParam):
+ def to_asm(self):
+ if self.byte in anim_gfx_constants.keys():
+ return anim_gfx_constants[self.byte]
+ return SingleByteParam.to_asm(self)
+
+anims = read_constants(os.path.join(conf.path, 'constants/animation_constants.asm'))
+objs = { k: v for k, v in anims.items() if 'ANIM_OBJ' in v }
+bgs = { k: v for k, v in anims.items() if 'ANIM_BG' in v }
+anims = { k: v.replace('ANIM_','') for k, v in anims.items() }
+from move_constants import moves
+anims.update(moves)
+
+class AnimObjParam(SingleByteParam):
+ def to_asm(self):
+ if self.byte in objs.keys():
+ return objs[self.byte]
+ return SingleByteParam.to_asm(self)
+
+class BGEffectParam(SingleByteParam):
+ def to_asm(self):
+ if self.byte in bgs.keys():
+ return bgs[self.byte]
+ return SingleByteParam.to_asm(self)
+
+
+battle_animation_commands = {
+ 0xd0: ['anim_obj', ['obj', AnimObjParam], ['x', DecimalParam], ['y', DecimalParam], ['param', SingleByteParam]],
+ 0xd1: ['anim_1gfx', ['gfx1', AnimGFXParam]],
+ 0xd2: ['anim_2gfx', ['gfx1', AnimGFXParam], ['gfx2', AnimGFXParam]],
+ 0xd3: ['anim_3gfx', ['gfx1', AnimGFXParam], ['gfx2', AnimGFXParam], ['gfx3', AnimGFXParam]],
+ 0xd4: ['anim_4gfx', ['gfx1', AnimGFXParam], ['gfx2', AnimGFXParam], ['gfx3', AnimGFXParam], ['gfx4', AnimGFXParam]],
+ 0xd5: ['anim_5gfx', ['gfx1', AnimGFXParam], ['gfx2', AnimGFXParam], ['gfx3', AnimGFXParam], ['gfx4', AnimGFXParam], ['gfx5', AnimGFXParam]],
+ 0xd6: ['anim_incobj', ['id', SingleByteParam]],
+ 0xd7: ['anim_setobj', ['id', SingleByteParam], ['obj', AnimObjParam]], # bug: second param is interpreted as a command if not found in the object array
+ 0xd8: ['anim_incbgeffect', ['effect', BGEffectParam]],
+ 0xd9: ['anim_enemyfeetobj'],
+ 0xda: ['anim_playerheadobj'],
+ 0xdb: ['anim_checkpokeball'],
+ 0xdc: ['anim_transform'],
+ 0xdd: ['anim_raisesub'],
+ 0xde: ['anim_dropsub'],
+ 0xdf: ['anim_resetobp0'],
+ 0xe0: ['anim_sound', ['tracks', SingleByteParam], ['id', SoundEffectParam]],
+ 0xe1: ['anim_cry', ['pitch', SingleByteParam]],
+ 0xe2: ['anim_minimizeopp'], # unused
+ 0xe3: ['anim_oamon'],
+ 0xe4: ['anim_oamoff'],
+ 0xe5: ['anim_clearobjs'],
+ 0xe6: ['anim_beatup'],
+ 0xe7: ['anim_0xe7'], # nothing
+ 0xe8: ['anim_updateactorpic'],
+ 0xe9: ['anim_minimize'],
+ 0xea: ['anim_0xea'], # nothing
+ 0xeb: ['anim_0xeb'], # nothing
+ 0xec: ['anim_0xec'], # nothing
+ 0xed: ['anim_0xed'], # nothing
+ 0xee: ['anim_jumpand', ['value', SingleByteParam], ['address', PointerLabelParam]],
+ 0xef: ['anim_jumpuntil', ['address', PointerLabelParam]],
+ 0xf0: ['anim_bgeffect', ['effect', BGEffectParam], ['unknown', SingleByteParam], ['unknown', SingleByteParam], ['unknown', SingleByteParam]],
+ 0xf1: ['anim_bgp', ['colors', SingleByteParam]],
+ 0xf2: ['anim_obp0', ['colors', SingleByteParam]],
+ 0xf3: ['anim_obp1', ['colors', SingleByteParam]],
+ 0xf4: ['anim_clearsprites'],
+ 0xf5: ['anim_0xf5'], # nothing
+ 0xf6: ['anim_0xf6'], # nothing
+ 0xf7: ['anim_0xf7'], # nothing
+ 0xf8: ['anim_jumpif', ['value', SingleByteParam], ['address', PointerLabelParam]],
+ 0xf9: ['anim_setvar', ['value', SingleByteParam]],
+ 0xfa: ['anim_incvar'],
+ 0xfb: ['anim_jumpvar', ['value', SingleByteParam], ['address', PointerLabelParam]],
+ 0xfc: ['anim_jump', ['address', PointerLabelParam]],
+ 0xfd: ['anim_loop', ['count', SingleByteParam], ['address', PointerLabelParam]],
+ 0xfe: ['anim_call', ['address', PointerLabelParam]],
+ 0xff: ['anim_ret'],
+}
+
+battle_animation_enders = [
+ 'anim_jump',
+ 'anim_ret',
+]
+
+def create_battle_animation_classes():
+ classes = []
+ for cmd, command in battle_animation_commands.items():
+ cmd_name = command[0]
+ params = {
+ 'id': cmd,
+ 'size': 1,
+ 'end': cmd_name in battle_animation_enders,
+ 'macro_name': cmd_name,
+ 'param_types': {},
+ }
+ for i, (name, class_) in enumerate(command[1:]):
+ params['param_types'][i] = {'name': name, 'class': class_}
+ params['size'] += class_.size
+ class_name = cmd_name + 'Command'
+ class_ = classobj(class_name, (Command,), params)
+ globals()[class_name] = class_
+ classes += [class_]
+ return classes
+
+battle_animation_classes = create_battle_animation_classes()
+
+
+class Wait(Command):
+ macro_name = 'anim_wait'
+ size = 1
+ end = macro_name in battle_animation_enders
+ param_types = {
+ 0: {'name': 'duration', 'class': DecimalParam},
+ }
+ override_byte_check = True
+
+
+class BattleAnim:
+
+ def __init__(self, address, base_label=None, label=None, used_labels=[]):
+ self.start_address = address
+ self.address = address
+
+ self.base_label = base_label
+ if self.base_label == None:
+ self.base_label = 'BattleAnim_' + hex(self.start_address)
+
+ self.label = label
+ if self.label == None:
+ self.label = self.base_label
+
+ self.used_labels = used_labels
+
+ self.output = []
+ self.labels = []
+ self.label_asm = (
+ self.start_address,
+ '%s: ; %x' % (self.label, self.start_address),
+ self.start_address
+ )
+ self.labels += [self.label_asm]
+ self.used_labels += [self.label_asm]
+
+ self.parse()
+
+ def parse(self):
+
+ done = False
+ while not done:
+ cmd = rom[self.address]
+ class_ = self.get_command_class(cmd)(address=self.address)
+ asm = class_.to_asm()
+
+ # label jumps/calls
+ for key, param in class_.param_types.items():
+ if param['class'] == PointerLabelParam:
+ label_address = class_.params[key].parsed_address
+ label = '%s_branch_%x' % (self.base_label, label_address)
+ label_def = '%s: ; %x' % (label, label_address)
+ label_asm = (label_address, label_def, label_address)
+ if label_asm not in self.used_labels:
+ self.labels += [label_asm]
+ asm = asm.replace('$%x' % get_local_address(label_address), label)
+
+ self.output += [(self.address, '\t' + asm, self.address + class_.size)]
+ self.address += class_.size
+
+ done = class_.end
+ # infinite loops are enders
+ if class_.macro_name == 'anim_loop':
+ if class_.params[0].byte == 0:
+ done = True
+
+ # last_address comment
+ self.output += [(self.address, '; %x\n' % self.address, self.address)]
+
+ # parse any other branches too
+ self.labels = list(set(self.labels))
+ for address, asm, last_address in self.labels:
+ if not (self.start_address <= address < self.address) and (address, asm, last_address) not in self.used_labels:
+ self.used_labels += [(address, asm, last_address)]
+ sub = BattleAnim(address=address, base_label=self.base_label, label=asm.split(':')[0], used_labels=self.used_labels)
+ self.output += sub.output
+ self.labels += sub.labels
+
+ self.output = list(set(self.output))
+ self.labels = list(set(self.labels))
+
+ def to_asm(self):
+ output = sorted(self.output + self.labels, key = lambda (x, y, z): (x, z))
+ text = ''
+ for (address, asm, last_address) in output:
+ text += asm + '\n'
+ #text += '; %x\n' % last_address
+ return text
+
+ def get_command_class(self, cmd):
+ if cmd < 0xd0:
+ return Wait
+ for class_ in battle_animation_classes:
+ if class_.id == cmd:
+ return class_
+ return None
+
+
+def battle_anim_label(i):
+ if i in anims.keys():
+ base_label = 'BattleAnim_%s' % anims[i].title().replace('_','')
+ else:
+ base_label = 'BattleAnim_%d' % i
+ return base_label
+
+def dump_battle_anims(table_address=0xc906f, num_anims=278):
+ """
+ Dump each battle animation from a pointer table.
+ """
+
+ asms = []
+
+ asms += [(table_address, 'BattleAnimations: ; %x' % table_address, table_address)]
+
+ address = table_address
+ bank = address / 0x4000
+
+ for i in xrange(num_anims):
+ pointer_address = address
+ anim_address = rom[pointer_address] + rom[pointer_address + 1] * 0x100
+ anim_address = get_global_address(anim_address, bank)
+ base_label = battle_anim_label(i)
+ address += 2
+
+ # anim pointer
+ asms += [(pointer_address, '\tdw %s' % base_label, address)]
+
+ # anim script
+ anim = BattleAnim(address=anim_address, base_label=base_label)
+ asms += anim.output + anim.labels
+
+ asms += [(address, '; %x\n' % address, address)]
+
+ # jp sonicboom
+ anim = BattleAnim(address=0xc9c00, base_label='BattleAnim_Sonicboom_JP')
+ asms += anim.output + anim.labels
+
+ asms = sort_asms(asms)
+ return asms
+
+def print_asm_list(asms):
+ # incbin any unknown areas
+ # not really needed since there are no gaps
+ last = asms[0][0]
+ for addr, asm, last_addr in asms:
+ if addr > last:
+ print '\nINCBIN "baserom.gbc", $%x, $%x - $%x\n\n' % (last, addr, last)
+ if addr >= last:
+ print asm
+ last = last_addr
+
+if __name__ == '__main__':
+ asms = dump_battle_anims()
+ print_asm_list(asms)
+
diff --git a/pokemontools/crystal.py b/pokemontools/crystal.py
index eb88b6b..eed7c32 100644
--- a/pokemontools/crystal.py
+++ b/pokemontools/crystal.py
@@ -853,8 +853,6 @@ class PointerLabelParam(MultiByteParam):
# bank can be overriden
if "bank" in kwargs.keys():
if kwargs["bank"] != False and kwargs["bank"] != None and kwargs["bank"] in [True, "reverse"]:
- # not +=1 because child classes set size=3 already
- self.size = self.default_size + 1
self.given_bank = kwargs["bank"]
#if kwargs["bank"] not in [None, False, True, "reverse"]:
# raise Exception("bank cannot be: " + str(kwargs["bank"]))
@@ -927,8 +925,11 @@ class PointerLabelParam(MultiByteParam):
bank_part = "$%.2x" % (pointers.calculate_bank(caddress))
else:
bank_part = "BANK("+label+")"
+ # for labels, expand bank_part at build time
+ if bank in ["reverse", True] and label:
+ return pointer_part
# return the asm based on the order the bytes were specified to be in
- if bank == "reverse": # pointer, bank
+ elif bank == "reverse": # pointer, bank
return pointer_part+", "+bank_part
elif bank == True: # bank, pointer
return bank_part+", "+pointer_part
@@ -944,13 +945,22 @@ class PointerLabelParam(MultiByteParam):
raise Exception("this should never happen")
class PointerLabelBeforeBank(PointerLabelParam):
- bank = True # bank appears first, see calculate_pointer_from_bytes_at
size = 3
- byte_type = "dw"
+ bank = True # bank appears first, see calculate_pointer_from_bytes_at
+ byte_type = 'db'
+
+ @staticmethod
+ def from_asm(value):
+ return 'BANK({0})\n\tdw {0}'.format(value)
class PointerLabelAfterBank(PointerLabelParam):
- bank = "reverse" # bank appears last, see calculate_pointer_from_bytes_at
size = 3
+ bank = "reverse" # bank appears last, see calculate_pointer_from_bytes_at
+ byte_type = 'dw'
+
+ @staticmethod
+ def from_asm(value):
+ return '{0}\n\tdb BANK({0})'.format(value)
class ScriptPointerLabelParam(PointerLabelParam): pass
@@ -2358,7 +2368,7 @@ pksv_crystal_more = {
0xA1: ["halloffame"],
0xA2: ["credits"],
0xA3: ["warpfacing", ["facing", SingleByteParam], ["map_group", MapGroupParam], ["map_id", MapIdParam], ["x", SingleByteParam], ["y", SingleByteParam]],
- 0xA4: ["storetext", ["pointer", PointerLabelBeforeBank], ["memory", SingleByteParam]],
+ 0xA4: ["storetext", ["memory", SingleByteParam]],
0xA5: ["displaylocation", ["id", SingleByteParam], ["memory", SingleByteParam]],
0xA6: ["trainerclassname", ["id", SingleByteParam]],
0xA7: ["name", ["type", SingleByteParam], ["id", SingleByteParam]],
diff --git a/pokemontools/gfx.py b/pokemontools/gfx.py
index 8397337..e452da4 100644
--- a/pokemontools/gfx.py
+++ b/pokemontools/gfx.py
@@ -1012,7 +1012,7 @@ def get_uncompressed_gfx(start, num_tiles, filename):
-def hex_to_rgb(word):
+def bin_to_rgb(word):
red = word & 0b11111
word >>= 5
green = word & 0b11111
@@ -1020,23 +1020,39 @@ def hex_to_rgb(word):
blue = word & 0b11111
return (red, green, blue)
-def grab_palettes(address, length=0x80):
+def rgb_from_rom(address, length=0x80):
+ return convert_binary_pal_to_text(rom[address:address+length])
+
+def convert_binary_pal_to_text_by_filename(filename):
+ with open(filename) as f:
+ pal = bytearray(f.read())
+ return convert_binary_pal_to_text(pal)
+
+def convert_binary_pal_to_text(pal):
output = ''
- for word in range(length/2):
- color = ord(rom[address+1])*0x100 + ord(rom[address])
- address += 2
- color = hex_to_rgb(color)
- red = str(color[0]).zfill(2)
- green = str(color[1]).zfill(2)
- blue = str(color[2]).zfill(2)
- output += '\tRGB '+red+', '+green+', '+blue
+ words = [hi * 0x100 + lo for lo, hi in zip(pal[::2], pal[1::2])]
+ for word in words:
+ red, green, blue = ['%.2d' % c for c in bin_to_rgb(word)]
+ output += '\tRGB ' + ', '.join((red, green, blue))
output += '\n'
return output
+def read_rgb_macros(lines):
+ colors = []
+ for line in lines:
+ macro = line.split(" ")[0].strip()
+ if macro == 'RGB':
+ params = ' '.join(line.split(" ")[1:]).split(',')
+ red, green, blue = [int(v) for v in params]
+ colors += [[red, green, blue]]
+ return colors
-
-
+def rewrite_binary_pals_to_text(filenames):
+ for filename in filenames:
+ pal_text = convert_binary_pal_to_text_by_filename(filename)
+ with open(filename, 'w') as out:
+ out.write(pal_text)
def dump_monster_pals():
@@ -1147,6 +1163,9 @@ def to_lines(image, width):
def dmg2rgb(word):
+ """
+ For PNGs.
+ """
def shift(value):
while True:
yield value & (2**5 - 1)
@@ -1159,27 +1178,49 @@ def dmg2rgb(word):
def rgb_to_dmg(color):
+ """
+ For PNGs.
+ """
word = (color['r'] / 8)
word += (color['g'] / 8) << 5
word += (color['b'] / 8) << 10
return word
-def png_pal(filename):
- with open(filename, 'rb') as pal_data:
- words = pal_data.read()
- dmg_pals = []
- for word in range(len(words)/2):
- dmg_pals.append(ord(words[word*2]) + ord(words[word*2+1])*0x100)
+def pal_to_png(filename):
+ """
+ Interpret a .pal file as a png palette.
+ """
+ with open(filename) as rgbs:
+ colors = read_rgb_macros(rgbs.readlines())
+ a = 255
palette = []
+ for color in colors:
+ # even distribution over 000-255
+ r, g, b = [int(hue * 8.25) for hue in color]
+ palette += [(r, g, b, a)]
white = (255,255,255,255)
black = (000,000,000,255)
- for word in dmg_pals: palette += [dmg2rgb(word)]
- if white not in dmg_pals and len(palette) < 4: palette = [white] + palette
- if black not in dmg_pals and len(palette) < 4: palette += [black]
+ if white not in palette and len(palette) < 4:
+ palette = [white] + palette
+ if black not in palette and len(palette) < 4:
+ palette = palette + [black]
return palette
+def png_to_rgb(palette):
+ """
+ Convert a png palette to rgb macros.
+ """
+ output = ''
+ for color in palette:
+ r, g, b = [color[c] / 8 for c in 'rgb']
+ output += '\tRGB ' + ', '.join(['%.2d' % hue for hue in (r, g, b)])
+ output += '\n'
+ return output
+
+
+
def export_2bpp_to_png(filein, fileout=None, pal_file=None, height=0, width=0):
if fileout == None:
fileout = os.path.splitext(filein)[0] + '.png'
@@ -1234,7 +1275,7 @@ def convert_2bpp_to_png(image, width=0, height=0, pal_file=None):
px_map = [[3 - pixel for pixel in line] for line in lines]
else: # gbc color
- palette = png_pal(pal_file)
+ palette = pal_to_png(pal_file)
greyscale = False
bitdepth = 8
px_map = [[pixel for pixel in line] for line in lines]
@@ -1371,13 +1412,22 @@ def png_to_2bpp(filein):
def export_palette(palette, filename):
+ """
+ Export a palette from png to rgb macros in a .pal file.
+ """
+
if os.path.exists(filename):
- output = []
- for color in palette:
- word = rgb_to_dmg(color)
- output += [word & 0xff]
- output += [word >> 8]
- to_file(filename, output)
+
+ # Pic palettes are 2 colors (black/white are added later).
+ with open(filename) as rgbs:
+ colors = read_rgb_macros(rgbs.readlines())
+
+ if len(colors) == 2:
+ palette = palette[1:3]
+
+ text = png_to_rgb(palette)
+ with open(filename, 'w') as out:
+ out.write(text)
def png_to_lz(filein):
@@ -1566,6 +1616,8 @@ def expand_pic_palettes():
with open(filename, 'wb') as out:
out.write(w + palette + b)
+
+
if __name__ == "__main__":
debug = False
diff --git a/pokemontools/preprocessor.py b/pokemontools/preprocessor.py
index bde5f70..e7de46d 100644
--- a/pokemontools/preprocessor.py
+++ b/pokemontools/preprocessor.py
@@ -549,40 +549,22 @@ class Preprocessor(object):
original_line = line
- # remove trailing newline
- if line[-1] == "\n":
- line = line[:-1]
+ has_tab = line[0] == "\t"
- # remove first tab
- has_tab = False
- if line[0] == "\t":
- has_tab = True
- line = line[1:]
-
- # remove duplicate whitespace (also trailing)
+ # remove whitespace
line = " ".join(line.split())
- params = []
-
# check if the line has params
if " " in line:
# split the line into separate parameters
params = line.replace(token, "").split(",")
-
- # check if there are no params (redundant)
- if len(params) == 1 and params[0] == "":
- raise exceptions.MacroException("macro has no params?")
+ else:
+ params = []
# write out a comment showing the original line
if show_original_lines:
sys.stdout.write("; original_line: " + original_line)
- # rgbasm can handle "db" so no preprocessing is required, plus this wont be
- # reached because of earlier checks in macro_test.
- if macro.macro_name in ["db", "dw"]:
- sys.stdout.write(original_line)
- return
-
# rgbasm can handle other macros too
if "is_rgbasm_macro" in dir(macro):
if macro.is_rgbasm_macro:
@@ -600,64 +582,18 @@ class Preprocessor(object):
if do_macro_sanity_check:
self.check_macro_sanity(params, macro, original_line)
- # used for storetext
- correction = 0
-
output = ""
-
- index = 0
- while index < len(params):
- param_type = macro.param_types[index - correction]
+ for index in xrange(len(params)):
+ param_type = macro.param_types[index]
description = param_type["name"].strip()
param_klass = param_type["class"]
- byte_type = param_klass.byte_type # db or dw
- size = param_klass.size
+ byte_type = param_klass.byte_type
param = params[index].strip()
- # param_klass.to_asm() won't work here because it doesn't
- # include db/dw.
-
- # some parameters are really multiple types of bytes
- if (byte_type == "dw" and size != 2) or \
- (byte_type == "db" and size != 1):
-
- output += ("; " + description + "\n")
-
- if size == 3 and is_based_on(param_klass, "PointerLabelBeforeBank"):
- # write the bank first
- output += ("db " + param + "\n")
- # write the pointer second
- output += ("dw " + params[index+1].strip() + "\n")
- index += 2
- correction += 1
- elif size == 3 and is_based_on(param_klass, "PointerLabelAfterBank"):
- # write the pointer first
- output += ("dw " + param + "\n")
- # write the bank second
- output += ("db " + params[index+1].strip() + "\n")
- index += 2
- correction += 1
- elif size == 3 and "from_asm" in dir(param_klass):
- output += ("\t" + byte_type + " " + param_klass.from_asm(param) + "\n")
- index += 1
- else:
- raise exceptions.MacroException(
- "dunno what to do with this macro param ({klass}) in line: {line}"
- .format(
- klass=param_klass,
- line=original_line,
- )
- )
-
- elif "from_asm" in dir(param_klass):
- output += ("\t" + byte_type + " " + param_klass.from_asm(param) + " ; " + description + "\n")
- index += 1
-
- # or just print out the byte
- else:
- output += ("\t" + byte_type + " " + param + " ; " + description + "\n")
+ if "from_asm" in dir(param_klass):
+ param = param_klass.from_asm(param)
- index += 1
+ output += ("\t" + byte_type + " " + param + " ; " + description + "\n")
sys.stdout.write(output)
diff --git a/pokemontools/wram.py b/pokemontools/wram.py
index a132289..1029c8e 100644
--- a/pokemontools/wram.py
+++ b/pokemontools/wram.py
@@ -9,6 +9,11 @@ import os
NUM_OBJECTS = 0x10
OBJECT_LENGTH = 0x10
+
+def rgbasm_to_py(text):
+ return text.replace('$', '0x').replace('%', '0b')
+
+
def make_wram_labels(wram_sections):
wram_labels = {}
for section in wram_sections:
@@ -24,15 +29,14 @@ def bracket_value(string, i=0):
def read_bss_sections(bss):
sections = []
section = {
- 'name': None,
- 'type': None,
- 'bank': None,
- 'start': None,
- 'labels': [],
+ "labels": [],
}
address = None
if type(bss) is not list: bss = bss.split('\n')
for line in bss:
+ line = line.lstrip()
+ if 'SECTION' in line:
+ if section: sections.append(section) # last section
comment_index = line.find(';')
line, comment = line[:comment_index].lstrip(), line[comment_index:]
@@ -50,7 +54,7 @@ def read_bss_sections(bss):
bank = None
if '[' in type_:
- address = int(bracket_value(type_).replace('$','0x'), 16)
+ address = int(rgbasm_to_py(bracket_value(type_)), 16)
else:
types = {
'VRAM': 0x8000,
@@ -83,7 +87,7 @@ def read_bss_sections(bss):
}]
elif line[:3] == 'ds ':
- length = eval(line[3:].replace('$','0x'))
+ length = eval(rgbasm_to_py(line[3:]))
address += length
# adjacent labels use the same space
for label in section['labels'][::-1]:
@@ -102,7 +106,7 @@ def read_bss_sections(bss):
return sections
def constants_to_dict(constants):
- return dict((eval(constant[constant.find('EQU')+3:constant.find(';')].replace('$','0x')), constant[:constant.find('EQU')].strip()) for constant in constants)
+ return dict((eval(rgbasm_to_py(constant[constant.find('EQU')+3:constant.find(';')])), constant[:constant.find('EQU')].strip()) for constant in constants)
def scrape_constants(text):
if type(text) is not list: