Merge branch 'yenatch/map-editor' into master

Conflicts:
	pokemontools/crystal.py

Fixes #40.
This commit is contained in:
Bryan Bishop 2013-09-26 10:46:04 -05:00
commit c02c65fe74
2 changed files with 102 additions and 38 deletions

View File

@ -67,6 +67,9 @@ is_valid_address = addresses.is_valid_address
import old_text_script
OldTextScript = old_text_script
import config
conf = config.Config()
from map_names import map_names
# ---- script_parse_table explanation ----
@ -112,16 +115,22 @@ def map_name_cleaner(input):
rom = romstr.RomStr(None)
def direct_load_rom(filename="../baserom.gbc"):
def direct_load_rom(filename=None):
"""loads bytes into memory"""
if filename == None:
filename = os.path.join(conf.path, "baserom.gbc")
global rom
file_handler = open(filename, "rb")
rom = romstr.RomStr(file_handler.read())
file_handler.close()
return rom
def load_rom(filename="../baserom.gbc"):
def load_rom(filename=None):
"""checks that the loaded rom matches the path
and then loads the rom if necessary."""
if filename == None:
filename = os.path.join(conf.path, "baserom.gbc")
global rom
if rom != romstr.RomStr(None) and rom != None:
return rom
if not isinstance(rom, romstr.RomStr):
@ -129,14 +138,19 @@ def load_rom(filename="../baserom.gbc"):
elif os.lstat(filename).st_size != len(rom):
return direct_load_rom(filename)
def direct_load_asm(filename="../main.asm"):
def direct_load_asm(filename=None):
if filename == None:
filename = os.path.join(conf.path, "main.asm")
"""returns asm source code (AsmList) from a file"""
asm = open(filename, "r").read().split("\n")
asm = romstr.AsmList(asm)
return asm
def load_asm(filename="../main.asm"):
def load_asm(filename=None):
"""returns asm source code (AsmList) from a file (uses a global)"""
if filename == None:
filename = os.path.join(conf.path, "main.asm")
global asm
asm = direct_load_asm(filename=filename)
return asm
@ -2654,7 +2668,9 @@ effect_classes = create_effect_command_classes()
def generate_macros(filename="../script_macros.asm"):
def generate_macros(filename=None):
if filename == None:
filename = os.path.join(conf.path, "script_macros.asm")
"""generates all macros based on commands
this is dumped into script_macros.asm"""
output = "; This file is generated by generate_macros.\n"
@ -5278,7 +5294,7 @@ def parse_second_map_header_at(address, map_group=None, map_id=None, debug=True)
class MapBlockData:
base_label = "MapBlockData_"
maps_path = os.path.realpath(os.path.join(os.path.realpath("."), "../maps"))
maps_path = os.path.realpath(os.path.join(conf.path, "maps"))
def __init__(self, address, map_group=None, map_id=None, debug=True, bank=None, label=None, width=None, height=None):
self.address = address
@ -5922,9 +5938,11 @@ def reset_incbins():
isolate_incbins(asm=asm)
process_incbins()
def find_incbin_to_replace_for(address, debug=False, rom_file="../baserom.gbc"):
def find_incbin_to_replace_for(address, debug=False, rom_file=None):
"""returns a line number for which incbin to edit
if you were to insert bytes into main.asm"""
if rom_file == None:
rom_file = os.path.join(conf.path, "baserom.gbc")
if type(address) == str: address = int(address, 16)
if not (0 <= address <= os.lstat(rom_file).st_size):
raise IndexError("address is out of bounds")
@ -5952,7 +5970,7 @@ def find_incbin_to_replace_for(address, debug=False, rom_file="../baserom.gbc"):
return incbin_key
return None
def split_incbin_line_into_three(line, start_address, byte_count, rom_file="../baserom.gbc"):
def split_incbin_line_into_three(line, start_address, byte_count, rom_file=None):
"""
splits an incbin line into three pieces.
you can replace the middle one with the new content of length bytecount
@ -5960,6 +5978,8 @@ def split_incbin_line_into_three(line, start_address, byte_count, rom_file="../b
start_address: where you want to start inserting bytes
byte_count: how many bytes you will be inserting
"""
if rom_file == None:
rom_file = os.path.join(conf.path, "baserom.gbc")
if type(start_address) == str: start_address = int(start_address, 16)
if not (0 <= start_address <= os.lstat(rom_file).st_size):
raise IndexError("start_address is out of bounds")
@ -6019,9 +6039,9 @@ def generate_diff_insert(line_number, newline, debug=False):
CalledProcessError = None
try:
diffcontent = subprocess.check_output("diff -u ../main.asm " + newfile_filename, shell=True)
diffcontent = subprocess.check_output("diff -u " + os.path.join(conf.path, "main.asm") + " " + newfile_filename, shell=True)
except (AttributeError, CalledProcessError):
p = subprocess.Popen(["diff", "-u", "../main.asm", newfile_filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p = subprocess.Popen(["diff", "-u", os.path.join(conf.path, "main.asm"), newfile_filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
diffcontent = out
@ -6041,8 +6061,8 @@ def apply_diff(diff, try_fixing=True, do_compile=True):
fh.close()
# apply the patch
os.system("cp ../main.asm ../main1.asm")
os.system("patch ../main.asm temp.patch")
os.system("cp " + os.path.join(conf.path, "main.asm") + " " + os.path.join(conf.path, "main1.asm"))
os.system("patch " + os.path.join(conf.path, "main.asm") + " " + "temp.patch")
# remove the patch
os.system("rm temp.patch")
@ -6050,11 +6070,11 @@ def apply_diff(diff, try_fixing=True, do_compile=True):
# confirm it's working
if do_compile:
try:
subprocess.check_call("cd ../; make clean; make", shell=True)
subprocess.check_call("cd " + conf.path + "; make clean; make", shell=True)
return True
except Exception, exc:
if try_fixing:
os.system("mv ../main1.asm ../main.asm")
os.system("mv " + os.path.join(conf.path, "main1.asm") + " " + os.path.join(conf.path, "main.asm"))
return False
import crystalparts.asmline
@ -6179,9 +6199,21 @@ class AsmSection:
def to_asm(self):
return self.line
new_asm = None
def load_asm2(filename=None, force=False):
"""loads the asm source code into memory"""
if filename == None:
filename = os.path.join(conf.path, "main.asm")
global new_asm
if new_asm == None or force:
new_asm = Asm(filename=filename)
return new_asm
class Asm:
"""controls the overall asm output"""
def __init__(self, filename="../main.asm", debug=True):
def __init__(self, filename=None, debug=True):
if filename == None:
filename = os.path.join(conf.path, "main.asm")
self.parts = []
self.labels = []
self.filename = filename

View File

@ -6,12 +6,12 @@ from ttk import Frame, Style
import PIL
from PIL import Image, ImageTk
import configuration
conf = configuration.Config()
import config
conf = config.Config()
version = 'crystal'
#version = 'red'
#version = 'crystal'
version = 'red'
if version == 'crystal':
map_dir = os.path.join(conf.path, 'maps/')
@ -317,12 +317,15 @@ class Map:
# Draw one block (4x4 tiles)
block = self.blockdata[block_y * self.width + block_x]
for j, tile in enumerate(self.tileset.blocks[block]):
# Tile gfx are split in half to make vram mapping easier
if tile >= 0x80:
tile -= 0x20
tile_x = block_x * 32 + (j % 4) * 8
tile_y = block_y * 32 + (j / 4) * 8
self.canvas.create_image(index + tile_x, indey + tile_y, image=self.tileset.tiles[tile])
try:
# Tile gfx are split in half to make vram mapping easier
if tile >= 0x80:
tile -= 0x20
tile_x = block_x * 32 + (j % 4) * 8
tile_y = block_y * 32 + (j / 4) * 8
self.canvas.create_image(index + tile_x, indey + tile_y, image=self.tileset.tiles[tile])
except:
pass
class Tileset:
@ -343,11 +346,30 @@ class Tileset:
self.get_blocks()
self.get_tiles()
def get_tileset_gfx_filename(self):
filename = None
if version == 'red':
tileset_defs = open(os.path.join(conf.path, 'main.asm'), 'r').read()
incbin = asm_at_label(tileset_defs, 'Tset%.2X_GFX' % self.id)
print incbin
filename = read_header_macros(incbin, ['filename'], ['INCBIN'])[0][0].replace('"','').replace('.2bpp','.png')
filename = os.path.join(conf.path, filename)
print filename
if not filename:
filename = os.path.join(
gfx_dir,
to_gfx_name(self.id) + '.png'
)
return filename
def get_tiles(self):
filename = os.path.join(
gfx_dir,
to_gfx_name(self.id) + '.png'
)
filename = self.get_tileset_gfx_filename()
if not os.path.exists(filename):
import gfx
gfx.to_png(filename.replace('.png','.2bpp'), filename)
self.img = Image.open(filename)
self.img.width, self.img.height = self.img.size
self.tiles = []
@ -452,7 +474,7 @@ def map_header(name):
headers = open(os.path.join(header_dir, 'map_headers.asm'), 'r').read()
label = name + '_MapHeader'
header = asm_at_label(headers, label)
macros = [ 'db', 'dw', 'db' ]
macros = [ 'db', 'db', 'db', 'dw', 'db', 'db', 'db', 'db' ]
attributes = [
'bank',
'tileset_id',
@ -478,7 +500,7 @@ def map_header(name):
label = headers[i:i+len(lower_label)]
header = asm_at_label(headers, label)
macros = [ 'db', 'db', 'dw', 'db' ]
macros = [ 'db', 'db', 'db', 'dw', 'dw', 'dw', 'db' ]
attributes = [
'tileset_id',
'height',
@ -509,7 +531,7 @@ def second_map_header(name):
headers = open(os.path.join(header_dir, 'second_map_headers.asm'), 'r').read()
label = name + '_SecondMapHeader'
header = asm_at_label(headers, label)
macros = [ 'db', 'db', 'dbw', 'dbw', 'dw', 'db' ]
macros = [ 'db', 'db', 'db', 'db', 'dw', 'db', 'dw', 'dw', 'db' ]
attributes = [
'border_block',
'height',
@ -531,19 +553,21 @@ def second_map_header(name):
def connections(which_connections, header, l=0):
directions = { 'north': {}, 'south': {}, 'west': {}, 'east': {} }
macros = [ 'db', 'dw', 'dw', 'db', 'db', 'dw' ]
if version == 'crystal':
macros = [ 'db', 'db' ]
attributes = [
'map_group',
'map_no',
]
elif version == 'red':
macros = [ 'db' ]
attributes = [
'map_id',
]
macros += [ 'dw', 'dw', 'db', 'db', 'db', 'db', 'dw' ]
attributes += [
'strip_pointer',
'strip_destination',
@ -570,8 +594,9 @@ def read_header_macros(header, attributes, macros):
l = 0
for l, (asm, comment) in enumerate(header):
if asm.strip() != '':
values += macro_values(asm, macros[i])
i += 1
mvalues = macro_values(asm, macros[i])
values += mvalues
i += len(mvalues)
if len(values) >= len(attributes):
l += 1
break
@ -587,7 +612,10 @@ def script_header(asm, name):
def macro_values(line, macro):
values = line[line.find(macro) + len(macro):].split(',')
return [v.replace('$','0x').strip() for v in values]
values = [v.replace('$','0x').strip() for v in values]
if values[0] == 'w': # dbw
values = values[1:]
return values
def db_value(line):
macro = 'db'
@ -602,8 +630,12 @@ from preprocessor import separate_comment
def asm_at_label(asm, label):
label_def = label + ':'
start = asm.find(label_def) + len(label_def)
lines = asm[start:].split('\n')
lines = asm.split('\n')
for line in lines:
if line.startswith(label_def):
lines = lines[lines.index(line):]
lines[0] = lines[0][len(label_def):]
break
# go until the next label
content = []
for line in lines: