mirror of
https://github.com/pret/pokemon-reverse-engineering-tools.git
synced 2026-07-19 00:43:18 -05:00
Merge remote-tracking branch 'kanzure/master' into battle-animations
Conflicts: pokemontools/wram.py
This commit is contained in:
commit
efe0b717ef
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
36
pokemontools/scan_includes.py
Normal file
36
pokemontools/scan_includes.py
Normal file
|
|
@ -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))
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
# make a graph based on the map
|
||||
pokemontools.crystal.cachably_parse_rom()
|
||||
pokemontools.map_gfx.add_pokecrystal_paths_to_configuration(config)
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user