2015-06-28 23:50:50 -07:00
|
|
|
"""Supplementary scripts for graphics conversion."""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import argparse
|
|
|
|
|
2018-12-31 14:17:55 -08:00
|
|
|
from pokemontools import gfx, lz
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
|
|
|
|
# Graphics with inverted tilemaps that aren't covered by filepath_rules.
|
|
|
|
pics = [
|
2016-05-17 20:01:43 -07:00
|
|
|
'gfx/shrink1',
|
|
|
|
'gfx/shrink2',
|
2015-06-28 23:50:50 -07:00
|
|
|
]
|
|
|
|
|
2016-02-17 13:15:18 -08:00
|
|
|
def recursive_read(filename):
|
2016-05-17 20:01:43 -07:00
|
|
|
def recurse(filename_):
|
|
|
|
lines = []
|
|
|
|
for line in open(filename_):
|
|
|
|
if 'include "' in line.lower():
|
|
|
|
lines += recurse(line.split('"')[1])
|
|
|
|
else:
|
|
|
|
lines += [line]
|
|
|
|
return lines
|
|
|
|
lines = recurse(filename)
|
|
|
|
return ''.join(lines)
|
2016-02-17 13:15:18 -08:00
|
|
|
|
2015-11-01 15:06:15 -08:00
|
|
|
base_stats = None
|
2015-08-30 11:06:37 -07:00
|
|
|
def get_base_stats():
|
2016-05-17 20:01:43 -07:00
|
|
|
global base_stats
|
|
|
|
if not base_stats:
|
|
|
|
base_stats = recursive_read('data/base_stats.asm')
|
|
|
|
return base_stats
|
2015-08-30 11:06:37 -07:00
|
|
|
|
2017-10-21 23:04:06 -07:00
|
|
|
def get_pokemon_dimensions(path):
|
2016-05-17 20:01:43 -07:00
|
|
|
try:
|
2017-10-21 23:04:06 -07:00
|
|
|
byte = bytearray(open(path, 'rb').read())[0]
|
|
|
|
width = byte & 0xf
|
|
|
|
height = (byte >> 8) & 0xf
|
|
|
|
return width, height
|
2016-05-17 20:01:43 -07:00
|
|
|
except:
|
2017-10-21 23:04:06 -07:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def get_animation_frames(path=None, w=7, h=7, bitmask_path=None, frame_path=None):
|
|
|
|
"""Retrieve animation frame tilemaps from generated frame/bitmask data."""
|
|
|
|
if not path:
|
|
|
|
path = bitmask_path
|
|
|
|
if not path:
|
|
|
|
path = frame_path
|
|
|
|
if not path:
|
|
|
|
raise Exception("need at least one of path, bitmask_path or frame_path")
|
|
|
|
|
|
|
|
if not bitmask_path:
|
|
|
|
bitmask_path = os.path.join(os.path.split(path)[0], 'bitmask.asm')
|
|
|
|
if not frame_path:
|
|
|
|
frame_path = os.path.join(os.path.split(path)[0], 'frames.asm')
|
|
|
|
bitmask_lines = open(bitmask_path).readlines()
|
|
|
|
frame_lines = open(frame_path).readlines()
|
|
|
|
|
|
|
|
bitmask_length = w * h
|
|
|
|
|
|
|
|
bitmasks = []
|
|
|
|
bitmask = []
|
|
|
|
for line in bitmask_lines:
|
|
|
|
if '\tdb ' in line:
|
|
|
|
value = line.split('\tdb ')[1].strip().replace('%', '0b')
|
|
|
|
value = int(value, 0)
|
|
|
|
#print line.strip(), value, len(bitmasks), len(bitmask)
|
|
|
|
for bit in xrange(8):
|
|
|
|
bitmask += [(value >> bit) & 1]
|
|
|
|
if len(bitmask) >= bitmask_length:
|
|
|
|
bitmasks += [bitmask]
|
|
|
|
bitmask = []
|
|
|
|
break
|
|
|
|
if bitmask:
|
|
|
|
bitmasks += [bitmask]
|
|
|
|
|
|
|
|
frames = []
|
|
|
|
frame_labels = []
|
|
|
|
i = 0
|
|
|
|
for line in frame_lines:
|
|
|
|
if '\tdw ' in line:
|
|
|
|
frame_labels += [line.split('\tdw ')[1].strip()]
|
|
|
|
else:
|
|
|
|
for part in line.split():
|
|
|
|
part = part.strip()
|
|
|
|
if part in frame_labels:
|
|
|
|
frames += [(part, i)]
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
results = []
|
|
|
|
|
|
|
|
for label, i in frames:
|
|
|
|
result = []
|
|
|
|
|
|
|
|
# get the bitmask and tile ids for each frame
|
|
|
|
# don't care if we read past bounds, so just read the rest of the file
|
|
|
|
values = []
|
|
|
|
for line in frame_lines[i:]:
|
|
|
|
if '\tdb ' in line:
|
|
|
|
values += line.split('\tdb ')[1].split(';')[0].split(',')
|
|
|
|
|
|
|
|
#print bitmasks
|
|
|
|
#print values[0]
|
|
|
|
#print int(values[0].replace('$', '0x'), 0)
|
|
|
|
bitmask = bitmasks[int(values[0].replace('$', '0x'), 0)]
|
|
|
|
tiles = values[1:]
|
|
|
|
k = 0
|
|
|
|
j = 0
|
|
|
|
for bit in bitmask:
|
|
|
|
if bit:
|
|
|
|
result += [int(tiles[k].replace('$', '0x'), 0)]
|
|
|
|
k += 1
|
|
|
|
else:
|
|
|
|
result += [j]
|
|
|
|
j += 1
|
|
|
|
|
|
|
|
results += [result]
|
|
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
def get_animated_graphics(path, w=7, h=7, bitmask_path=None, frame_path=None):
|
|
|
|
frames = get_animation_frames(path, w, h, bitmask_path, frame_path)
|
|
|
|
new_path = path.replace('.animated.2bpp', '.2bpp')
|
|
|
|
tiles = gfx.get_tiles(bytearray(open(path, 'rb').read()))
|
|
|
|
new_tiles = tiles[:w * h]
|
|
|
|
for frame in frames:
|
|
|
|
for tile in frame:
|
|
|
|
new_tiles += [tiles[tile]]
|
|
|
|
new_graphic = gfx.connect(new_tiles)
|
|
|
|
print new_path, list(new_graphic)
|
|
|
|
open(new_path, 'wb').write(bytearray(new_graphic))
|
|
|
|
return new_path
|
2015-08-30 11:06:37 -07:00
|
|
|
|
2015-06-28 23:50:50 -07:00
|
|
|
def filepath_rules(filepath):
|
2016-05-17 20:01:43 -07:00
|
|
|
"""Infer attributes of certain graphics by their location in the filesystem."""
|
|
|
|
args = {}
|
|
|
|
|
|
|
|
filedir, filename = os.path.split(filepath)
|
|
|
|
if filedir.startswith('./'):
|
|
|
|
filedir = filedir[2:]
|
|
|
|
|
|
|
|
name, ext = os.path.splitext(filename)
|
|
|
|
if ext == '.lz':
|
|
|
|
name, ext = os.path.splitext(name)
|
|
|
|
|
|
|
|
pokemon_name = ''
|
|
|
|
|
2018-12-31 14:17:55 -08:00
|
|
|
if 'gfx/pokemon/' in filedir:
|
2016-05-17 20:01:43 -07:00
|
|
|
pokemon_name = filedir.split('/')[-1]
|
|
|
|
if pokemon_name.startswith('unown_'):
|
|
|
|
index = filedir.find(pokemon_name)
|
|
|
|
if index != -1:
|
|
|
|
filedir = filedir[:index + len('unown')] + filedir[index + len('unown_a'):]
|
2017-10-21 23:04:06 -07:00
|
|
|
if name == 'front' or name == 'front.animated':
|
2016-05-17 20:01:43 -07:00
|
|
|
args['pal_file'] = os.path.join(filedir, 'normal.pal')
|
|
|
|
args['pic'] = True
|
|
|
|
args['animate'] = True
|
|
|
|
elif name == 'back':
|
2017-05-28 11:15:22 -07:00
|
|
|
args['pal_file'] = os.path.join(filedir, 'normal.pal')
|
2016-05-17 20:01:43 -07:00
|
|
|
args['pic'] = True
|
|
|
|
|
|
|
|
elif 'gfx/trainers' in filedir:
|
|
|
|
args['pic'] = True
|
|
|
|
|
|
|
|
elif os.path.join(filedir, name) in pics:
|
|
|
|
args['pic'] = True
|
|
|
|
|
|
|
|
elif filedir == 'gfx/tilesets':
|
|
|
|
args['tileset'] = True
|
|
|
|
|
|
|
|
if args.get('pal_file'):
|
|
|
|
if os.path.exists(args['pal_file']):
|
|
|
|
args['palout'] = args['pal_file']
|
|
|
|
else:
|
|
|
|
del args['pal_file']
|
|
|
|
|
|
|
|
if args.get('pic'):
|
|
|
|
if ext == '.png':
|
|
|
|
w, h = gfx.png.Reader(filepath).asRGBA8()[:2]
|
|
|
|
w = min(w/8, h/8)
|
|
|
|
args['pic_dimensions'] = w, w
|
|
|
|
elif ext == '.2bpp':
|
2017-10-21 23:04:06 -07:00
|
|
|
if pokemon_name and name == 'front' or name == 'front.animated':
|
|
|
|
w, h = get_pokemon_dimensions(filepath.replace(ext, '.dimensions')) or (7, 7)
|
2016-05-17 20:01:43 -07:00
|
|
|
args['pic_dimensions'] = w, w
|
|
|
|
elif pokemon_name and name == 'back':
|
|
|
|
args['pic_dimensions'] = 6, 6
|
|
|
|
else:
|
|
|
|
args['pic_dimensions'] = 7, 7
|
|
|
|
|
|
|
|
if args.get('tileset'):
|
|
|
|
args['width'] = 128
|
|
|
|
return args
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
|
|
|
|
def to_1bpp(filename, **kwargs):
|
2016-05-17 20:01:43 -07:00
|
|
|
name, ext = os.path.splitext(filename)
|
|
|
|
if ext == '.1bpp': pass
|
|
|
|
elif ext == '.2bpp': gfx.export_2bpp_to_1bpp(filename, **kwargs)
|
|
|
|
elif ext == '.png': gfx.export_png_to_1bpp(filename, **kwargs)
|
|
|
|
elif ext == '.lz':
|
|
|
|
decompress(filename, **kwargs)
|
|
|
|
to_1bpp(name, **kwargs)
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
def to_2bpp(filename, **kwargs):
|
2016-05-17 20:01:43 -07:00
|
|
|
name, ext = os.path.splitext(filename)
|
|
|
|
if ext == '.1bpp': gfx.export_1bpp_to_2bpp(filename, **kwargs)
|
|
|
|
elif ext == '.2bpp': pass
|
|
|
|
elif ext == '.png': gfx.export_png_to_2bpp(filename, **kwargs)
|
|
|
|
elif ext == '.lz':
|
|
|
|
decompress(filename, **kwargs)
|
|
|
|
to_2bpp(name, **kwargs)
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
def to_png(filename, **kwargs):
|
2016-05-17 20:01:43 -07:00
|
|
|
name, ext = os.path.splitext(filename)
|
|
|
|
if ext == '.1bpp': gfx.export_1bpp_to_png(filename, **kwargs)
|
2017-10-21 23:04:06 -07:00
|
|
|
elif ext == '.2bpp' and name.endswith('.animated'):
|
|
|
|
w, h = kwargs.get('pic_dimensions') or (7, 7)
|
|
|
|
new_path = get_animated_graphics(filename, w=w, h=h)
|
|
|
|
return to_png(new_path, **kwargs)
|
2016-05-17 20:01:43 -07:00
|
|
|
elif ext == '.2bpp': gfx.export_2bpp_to_png(filename, **kwargs)
|
|
|
|
elif ext == '.png': pass
|
|
|
|
elif ext == '.lz':
|
|
|
|
decompress(filename, **kwargs)
|
|
|
|
to_png(name, **kwargs)
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
def compress(filename, **kwargs):
|
2016-05-17 20:01:43 -07:00
|
|
|
data = open(filename, 'rb').read()
|
|
|
|
lz_data = lz.Compressed(data).output
|
|
|
|
open(filename + '.lz', 'wb').write(bytearray(lz_data))
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
def decompress(filename, **kwargs):
|
2016-05-17 20:01:43 -07:00
|
|
|
lz_data = open(filename, 'rb').read()
|
|
|
|
data = lz.Decompressed(lz_data).output
|
|
|
|
name, ext = os.path.splitext(filename)
|
|
|
|
open(name, 'wb').write(bytearray(data))
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
|
|
|
|
methods = {
|
2016-05-17 20:01:43 -07:00
|
|
|
'2bpp': to_2bpp,
|
|
|
|
'1bpp': to_1bpp,
|
|
|
|
'png': to_png,
|
|
|
|
'lz': compress,
|
|
|
|
'unlz': decompress,
|
2015-06-28 23:50:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def main(method_name, filenames=None):
|
2016-05-17 20:01:43 -07:00
|
|
|
if filenames is None: filenames = []
|
|
|
|
for filename in filenames:
|
|
|
|
args = filepath_rules(filename)
|
|
|
|
method = methods.get(method_name)
|
|
|
|
if method:
|
|
|
|
method(filename, **args)
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
def get_args():
|
2016-05-17 20:01:43 -07:00
|
|
|
ap = argparse.ArgumentParser()
|
|
|
|
ap.add_argument('method_name')
|
|
|
|
ap.add_argument('filenames', nargs='*')
|
|
|
|
args = ap.parse_args()
|
|
|
|
return args
|
2015-06-28 23:50:50 -07:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2016-05-17 20:01:43 -07:00
|
|
|
main(**get_args().__dict__)
|