diff options
author | yenatch <yenatch@gmail.com> | 2013-12-20 00:43:30 -0500 |
---|---|---|
committer | yenatch <yenatch@gmail.com> | 2013-12-21 00:37:35 -0500 |
commit | efe0b717ef21acde2d30fc7de580547738ca67d6 (patch) | |
tree | 524c63520cc852d4244a899ec08d5e39b517cec8 | |
parent | 973a78167b104479bb6c8c4c528ae914106b1b53 (diff) | |
parent | f0aaf3cd568c485af40690ce0f18a6cd456ed02e (diff) |
Merge remote-tracking branch 'kanzure/master' into battle-animations
Conflicts:
pokemontools/wram.py
-rw-r--r-- | pokemontools/labels.py | 26 | ||||
-rw-r--r-- | pokemontools/map_editor.py | 17 | ||||
-rw-r--r-- | pokemontools/preprocessor.py | 10 | ||||
-rw-r--r-- | pokemontools/scan_includes.py | 36 | ||||
-rw-r--r-- | pokemontools/sym.py | 33 | ||||
-rw-r--r-- | pokemontools/vba/path.py | 57 | ||||
-rw-r--r-- | pokemontools/wram.py | 43 |
7 files changed, 158 insertions, 64 deletions
diff --git a/pokemontools/labels.py b/pokemontools/labels.py index 96e34b9..87e9990 100644 --- a/pokemontools/labels.py +++ b/pokemontools/labels.py @@ -8,33 +8,39 @@ import json import logging import pointers +import sym class Labels(object): """ Store all labels. """ - filename = "labels.json" - def __init__(self, config): + def __init__(self, config, filename="pokecrystal.map"): """ Setup the instance. """ self.config = config - self.path = os.path.join(self.config.path, Labels.filename) + self.filename = filename + self.path = os.path.join(self.config.path, self.filename) def initialize(self): """ Handle anything requiring file-loading and such. """ + # Look for a mapfile if it's not given if not os.path.exists(self.path): - logging.info( - "Running crystal.scan_for_predefined_labels to create \"{0}\". Trying.." - .format(Labels.filename) - ) - import crystal - crystal.scan_for_predefined_labels() + self.filename = find_mapfile_in_dir(self.config.path) + if self.filename == None: + raise Exception, "Couldn't find any mapfiles. Run rgblink -m to create a mapfile." + self.path = os.path.join(self.config.path, self.filename) - self.labels = json.read(open(self.path, "r").read()) + self.labels = sym.read_mapfile(self.path) + +def find_mapfile_in_dir(path): + for filename in os.listdir(path): + if os.path.splitext(filename)[1] == '.map': + return filename + return None def remove_quoted_text(line): """get rid of content inside quotes diff --git a/pokemontools/map_editor.py b/pokemontools/map_editor.py index c30fcd8..43042cb 100644 --- a/pokemontools/map_editor.py +++ b/pokemontools/map_editor.py @@ -128,6 +128,7 @@ def get_constants(config=config): name, value = [s.strip() for s in line.split(' EQU ')] constants[name] = eval(value.split(';')[0].replace('$','0x').replace('%','0b')) config.constants = constants + return constants class Application(Frame): def __init__(self, master=None, config=config): @@ -149,10 +150,10 @@ class Application(Frame): self.picker_frame = Frame(self) self.picker_frame.grid(row=1, column=1) - self.new = Button(self.button_frame) - self.new["text"] = "New" - self.new["command"] = self.new_map - self.new.grid(row=0, column=0, padx=2) + self.button_new = Button(self.button_frame) + self.button_new["text"] = "New" + self.button_new["command"] = self.new_map + self.button_new.grid(row=0, column=0, padx=2) self.open = Button(self.button_frame) self.open["text"] = "Open" @@ -683,14 +684,6 @@ def macro_values(line, macro): values = values[1:] return values -def db_value(line): - macro = 'db' - return macro_values(line, macro) - -def db_values(line): - macro = 'db' - return macro_values(line, macro) - def asm_at_label(asm, label): label_def = label + ':' lines = asm.split('\n') diff --git a/pokemontools/preprocessor.py b/pokemontools/preprocessor.py index 5ac7724..e7de46d 100644 --- a/pokemontools/preprocessor.py +++ b/pokemontools/preprocessor.py @@ -483,22 +483,18 @@ class Preprocessor(object): for l in lines: self.read_line(l) - self.update_globals() - def update_globals(self): """ Add any labels not already in globals.asm. """ - # TODO: pokered needs to be fixed - try: - globes = open(os.path.join(self.config.path, 'globals.asm'), 'r+') + path = os.path.join(self.config.path, 'globals.asm') + if os.path.exists(path): + globes = open(path, 'r+') lines = globes.readlines() for globe in self.globes: line = 'GLOBAL ' + globe + '\n' if line not in lines: globes.write(line) - except Exception as exception: - pass # don't care if it's not there... def read_line(self, l): """ diff --git a/pokemontools/scan_includes.py b/pokemontools/scan_includes.py new file mode 100644 index 0000000..7f34e92 --- /dev/null +++ b/pokemontools/scan_includes.py @@ -0,0 +1,36 @@ +# coding: utf-8 + +""" +Recursively scan an asm file for rgbasm INCLUDEs and INCBINs. +Used to generate dependencies for each rgbasm object. +""" + +import os +import sys + +import configuration +conf = configuration.Config() + +def recursive_scan(filename, includes = []): + if (filename[-4:] == '.asm' or filename[-3] == '.tx') and os.path.exists(filename): + lines = open(filename).readlines() + for line in lines: + for directive in ('INCLUDE', 'INCBIN'): + if directive in line: + line = line[:line.find(';')] + if directive in line: + include = line.split('"')[1] + if include not in includes: + includes += [include] + includes = recursive_scan(os.path.join(conf.path, include), includes) + break + return includes + +if __name__ == '__main__': + filenames = sys.argv[1:] + dependencies = [] + for filename in filenames: + dependencies += recursive_scan(os.path.join(conf.path, filename)) + dependencies = list(set(dependencies)) + sys.stdout.write(' '.join(dependencies)) + diff --git a/pokemontools/sym.py b/pokemontools/sym.py index ebd8532..b1e755f 100644 --- a/pokemontools/sym.py +++ b/pokemontools/sym.py @@ -4,7 +4,7 @@ import os import sys import json -def make_sym_from_json(filename = '../pokecrystal.sym', j = 'labels.json'): +def make_sym_from_json(filename = 'pokecrystal.sym', j = 'labels.json'): output = '' labels = json.load(open(j)) for label in labels: @@ -12,13 +12,13 @@ def make_sym_from_json(filename = '../pokecrystal.sym', j = 'labels.json'): with open(filename, 'w') as sym: sym.write(output) -def make_json_from_mapfile(filename='labels.json', mapfile='../pokecrystal.map'): +def make_json_from_mapfile(filename='labels.json', mapfile='pokecrystal.map'): output = [] labels = filter_wram_addresses(read_mapfile(mapfile)) with open(filename, 'w') as out: out.write(json.dumps(labels)) -def read_mapfile(filename='../pokecrystal.map'): +def read_mapfile(filename='pokecrystal.map'): """ Scrape label addresses from an rgbds mapfile. """ @@ -29,9 +29,15 @@ def read_mapfile(filename='../pokecrystal.map'): lines = mapfile.readlines() for line in lines: - # bank # - if 'Bank #' in line: - cur_bank = int(line.lstrip('Bank #').strip(';\n').strip(' (HOME)')) + if line[0].strip(): # section type def + section_type = line.split(' ')[0] + if section_type == 'Bank': # ROM + cur_bank = int(line.split(' ')[1].split(':')[0][1:]) + elif section_type in ['WRAM0', 'HRAM']: + cur_bank = 0 + elif section_type in ['WRAM, VRAM']: + cur_bank = int(line.split(' ')[2].split(':')[0][1:]) + cur_bank = int(line.split(' ')[2].split(':')[0][1:]) # label definition elif '=' in line: @@ -39,21 +45,10 @@ def read_mapfile(filename='../pokecrystal.map'): address = int(address.lstrip().replace('$', '0x'), 16) label = label.strip() - # rgbds doesn't support ram banks yet bank = cur_bank offset = address - - ranges = [ - 0x8000 <= address < 0xa000, - 0xa000 <= address < 0xc000, - 0xc000 <= address < 0xd000, - 0xd000 <= address < 0xe000, - ] - - if any(ranges): - bank = 0 - else: - offset += (bank * 0x4000 - 0x4000) if bank > 0 else 0 + if address < 0x8000 and bank: # ROM + offset += (bank - 1) * 0x4000 labels += [{ 'label': label, diff --git a/pokemontools/vba/path.py b/pokemontools/vba/path.py index 7596b86..2e50d1b 100644 --- a/pokemontools/vba/path.py +++ b/pokemontools/vba/path.py @@ -9,8 +9,14 @@ path finding implementation import pokemontools.configuration config = pokemontools.configuration.Config() +import pokemontools.crystal import pokemontools.map_gfx +from PIL import ( + Image, + ImageDraw, +) + PENALTIES = { # The minimum cost for a step must be greater than zero or else the path # finding implementation might take the player through elaborate routes @@ -541,7 +547,7 @@ class Map(object): map. """ - def __init__(self, cry, height, width, map_group_id, map_id, config=config): + def __init__(self, cry, parsed_map, height, width, map_group_id, map_id, config=config): """ :param cry: pokemon crystal emulation interface :type cry: crystal @@ -552,11 +558,11 @@ class Map(object): self.threat_zones = set() self.obstacles = set() - self.height = height - self.width = width - + self.parsed_map = parsed_map self.map_group_id = map_group_id self.map_id = map_id + self.height = height + self.width = width def travel_to(self, destination_location): """ @@ -591,8 +597,17 @@ class Map(object): palettes = pokemontools.map_gfx.read_palettes(self.config) map_image = pokemontools.map_gfx.draw_map(self.map_group_id, self.map_id, palettes, show_sprites=True, config=self.config) - # TODO: draw the given path on the map_image image object - raise NotImplementedError + for coordinates in path: + y = coordinates[0] + x = coordinates[1] + + some_image = Image.new("RGBA", (32, 32)) + draw = ImageDraw.Draw(some_image, "RGBA") + draw.rectangle([(0, 0), (32, 32)], fill=(0, 0, 0, 127)) + + target = [(x * 4, y * 4), ((x + 32) * 4, (y + 32) * 4)] + + map_image.paste(some_image, target, mask=some_image) return map_image @@ -611,19 +626,39 @@ class PathPlanner(object): Runs the path planner and returns a list of positions making up the path. """ - raise NotImplementedError + return [(0, 0), (1, 0), (1, 1), (1, 2), (1, 3)] -def broken_main(): +def plan_and_draw_path_on(map_group_id=1, map_id=1, initial_location=(0, 0), final_location=(2, 2), config=config): """ An attempt at an entry point. This hasn't been sufficiently considered yet. """ - current_map = Map.from_wram(cry) + initial_location = (0, 0) + final_location = (2, 2) + map_group_id = 1 + map_id = 1 + + pokemontools.crystal.cachably_parse_rom() + pokemontools.map_gfx.add_pokecrystal_paths_to_configuration(config) - # make a graph based on the map + # get the map based on data from the rom + parsed_map = pokemontools.crystal.map_names[map_group_id][map_id]["header_new"] + + # convert this map into a different structure + current_map = Map(cry=None, parsed_map=parsed_map, height=parsed_map.height.byte, width=parsed_map.width.byte, map_group_id=map_group_id, map_id=map_id, config=config) + + # make a graph based on the map data nodes = create_graph(current_map) - planner = PathPlanner(current_map, (0, 0), (5, 5)) + # make an instance of the planner implementation + planner = PathPlanner(current_map, initial_location, final_location) + + # Make that planner do its planning based on the current configuration. The + # planner should be callable in the future and still have + # previously-calculated state, like cached pre-computed routes or + # something. path = planner.plan() + # show the path on the map drawn = current_map.draw_path(path) + return drawn diff --git a/pokemontools/wram.py b/pokemontools/wram.py index d34737e..1029c8e 100644 --- a/pokemontools/wram.py +++ b/pokemontools/wram.py @@ -23,6 +23,9 @@ def make_wram_labels(wram_sections): wram_labels[label['address']] += [label['label']] return wram_labels +def bracket_value(string, i=0): + return string.split('[')[1 + i*2].split(']')[0] + def read_bss_sections(bss): sections = [] section = { @@ -35,10 +38,40 @@ def read_bss_sections(bss): if 'SECTION' in line: if section: sections.append(section) # last section - address = eval(rgbasm_to_py(line[line.find('[')+1:line.find(']')])) + comment_index = line.find(';') + line, comment = line[:comment_index].lstrip(), line[comment_index:] + + if 'SECTION' == line[:7]: + if section: # previous + sections += [section] + + section_def = line.split(',') + name = section_def[0].split('"')[1] + type_ = section_def[1].strip() + if len(section_def) > 2: + bank = bracket_value(section_def[2]) + else: + bank = None + + if '[' in type_: + address = int(rgbasm_to_py(bracket_value(type_)), 16) + else: + types = { + 'VRAM': 0x8000, + 'SRAM': 0xa000, + 'WRAM0': 0xc000, + 'WRAMX': 0xd000, + 'HRAM': 0xff80, + } + if address == None or bank != section['bank'] or section['type'] != type_: + if type_ in types.keys(): + address = types[type_] + # else: keep going from this address + section = { - 'name': line.split('"')[1], - #'type': line.split(',')[1].split('[')[0].strip(), + 'name': name, + 'type': type_, + 'bank': bank, 'start': address, 'labels': [], } @@ -54,7 +87,7 @@ def read_bss_sections(bss): }] elif line[:3] == 'ds ': - length = eval(rgbasm_to_py(line[3:line.find(';')])) + length = eval(rgbasm_to_py(line[3:])) address += length # adjacent labels use the same space for label in section['labels'][::-1]: @@ -66,7 +99,7 @@ def read_bss_sections(bss): elif 'EQU' in line: # some space is defined using constants name, value = line.split('EQU') - name, value = name.strip(), rgbasm_to_py(value) + name, value = name.strip(), value.strip().replace('$','0x').replace('%','0b') globals()[name] = eval(value) sections.append(section) |