summaryrefslogtreecommitdiff
path: root/tools/gfx.py
diff options
context:
space:
mode:
authorRangi <remy.oukaour+rangi42@gmail.com>2018-12-31 17:37:58 -0500
committerRangi <remy.oukaour+rangi42@gmail.com>2018-12-31 17:37:58 -0500
commit729608124fa5f479a82dcc3b13fdcedc473fdafa (patch)
tree9cd2e3f1cb9f83f6e5e5fc055bfcee8bbf0aaa24 /tools/gfx.py
parent90430e6dee9e207dc0b618d07566f83617343f60 (diff)
Remove the 'extras' submodule and include a local gfx.py tool instead
Diffstat (limited to 'tools/gfx.py')
-rw-r--r--tools/gfx.py264
1 files changed, 264 insertions, 0 deletions
diff --git a/tools/gfx.py b/tools/gfx.py
new file mode 100644
index 00000000..fae7bbdc
--- /dev/null
+++ b/tools/gfx.py
@@ -0,0 +1,264 @@
+"""Supplementary scripts for graphics conversion."""
+
+import os
+import argparse
+
+from pokemontools import gfx, lz
+
+
+# Graphics with inverted tilemaps that aren't covered by filepath_rules.
+pics = [
+ 'gfx/shrink1',
+ 'gfx/shrink2',
+]
+
+def recursive_read(filename):
+ 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)
+
+base_stats = None
+def get_base_stats():
+ global base_stats
+ if not base_stats:
+ base_stats = recursive_read('data/base_stats.asm')
+ return base_stats
+
+def get_pokemon_dimensions(path):
+ try:
+ byte = bytearray(open(path, 'rb').read())[0]
+ width = byte & 0xf
+ height = (byte >> 8) & 0xf
+ return width, height
+ except:
+ 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
+
+def filepath_rules(filepath):
+ """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 = ''
+
+ if 'gfx/pokemon/' in filedir:
+ 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'):]
+ if name == 'front' or name == 'front.animated':
+ args['pal_file'] = os.path.join(filedir, 'normal.pal')
+ args['pic'] = True
+ args['animate'] = True
+ elif name == 'back':
+ args['pal_file'] = os.path.join(filedir, 'normal.pal')
+ 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':
+ if pokemon_name and name == 'front' or name == 'front.animated':
+ w, h = get_pokemon_dimensions(filepath.replace(ext, '.dimensions')) or (7, 7)
+ 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
+
+
+def to_1bpp(filename, **kwargs):
+ 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)
+
+def to_2bpp(filename, **kwargs):
+ 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)
+
+def to_png(filename, **kwargs):
+ name, ext = os.path.splitext(filename)
+ if ext == '.1bpp': gfx.export_1bpp_to_png(filename, **kwargs)
+ 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)
+ elif ext == '.2bpp': gfx.export_2bpp_to_png(filename, **kwargs)
+ elif ext == '.png': pass
+ elif ext == '.lz':
+ decompress(filename, **kwargs)
+ to_png(name, **kwargs)
+
+def compress(filename, **kwargs):
+ data = open(filename, 'rb').read()
+ lz_data = lz.Compressed(data).output
+ open(filename + '.lz', 'wb').write(bytearray(lz_data))
+
+def decompress(filename, **kwargs):
+ lz_data = open(filename, 'rb').read()
+ data = lz.Decompressed(lz_data).output
+ name, ext = os.path.splitext(filename)
+ open(name, 'wb').write(bytearray(data))
+
+
+methods = {
+ '2bpp': to_2bpp,
+ '1bpp': to_1bpp,
+ 'png': to_png,
+ 'lz': compress,
+ 'unlz': decompress,
+}
+
+def main(method_name, filenames=None):
+ if filenames is None: filenames = []
+ for filename in filenames:
+ args = filepath_rules(filename)
+ method = methods.get(method_name)
+ if method:
+ method(filename, **args)
+
+def get_args():
+ ap = argparse.ArgumentParser()
+ ap.add_argument('method_name')
+ ap.add_argument('filenames', nargs='*')
+ args = ap.parse_args()
+ return args
+
+if __name__ == '__main__':
+ main(**get_args().__dict__)