diff options
author | yenatch <yenatch@gmail.com> | 2016-01-28 21:34:38 -0500 |
---|---|---|
committer | yenatch <yenatch@gmail.com> | 2016-01-28 21:34:38 -0500 |
commit | 90a3365a80dcf8b39c2c392e4756f47570b4cca5 (patch) | |
tree | 50de297d57cee2b155f22ffd68628adc97e15867 /pokemontools/gfx.py | |
parent | 8ed04a61e0d02937d0930ffad43feeb0c1f98ee7 (diff) |
Remove the graphics dump code from gfx.py.
Diffstat (limited to 'pokemontools/gfx.py')
-rw-r--r-- | pokemontools/gfx.py | 576 |
1 files changed, 0 insertions, 576 deletions
diff --git a/pokemontools/gfx.py b/pokemontools/gfx.py index ea0ea19..3f221d6 100644 --- a/pokemontools/gfx.py +++ b/pokemontools/gfx.py @@ -5,29 +5,13 @@ import sys import png from math import sqrt, floor, ceil import argparse -import operator import configuration config = configuration.Config() -from pokemon_constants import pokemon_constants -import trainers -import romstr - from lz import Compressed, Decompressed - -def load_rom(filename=config.rom_path): - rom = romstr.RomStr.load(filename=filename) - return bytearray(rom) - -def rom_offset(bank, address): - if address < 0x4000 or address >= 0x8000: - return address - return bank * 0x4000 + address - 0x4000 * bool(bank) - - def split(list_, interval): """ Split a list by length. @@ -196,292 +180,6 @@ def to_file(filename, data): file.close() - - - -sizes = [ - 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 5, 7, 5, 5, 7, 5, - 6, 7, 5, 6, 5, 7, 5, 7, 5, 7, 5, 6, 5, 6, 7, 5, - 6, 7, 5, 6, 6, 7, 5, 6, 5, 7, 5, 6, 7, 5, 7, 5, - 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 6, 7, 5, 6, - 7, 5, 7, 7, 5, 6, 7, 5, 6, 5, 6, 6, 6, 7, 5, 7, - 5, 6, 6, 5, 7, 6, 7, 5, 7, 5, 7, 7, 6, 6, 7, 6, - 7, 5, 7, 5, 5, 7, 7, 5, 6, 7, 6, 7, 6, 7, 7, 7, - 6, 6, 7, 5, 6, 6, 7, 6, 6, 6, 7, 6, 6, 6, 7, 7, - 6, 7, 7, 5, 5, 6, 6, 6, 6, 5, 6, 5, 6, 7, 7, 7, - 7, 7, 5, 6, 7, 7, 5, 5, 6, 7, 5, 6, 7, 5, 6, 7, - 6, 6, 5, 7, 6, 6, 5, 7, 7, 6, 6, 5, 5, 5, 5, 7, - 5, 6, 5, 6, 7, 7, 5, 7, 6, 7, 5, 6, 7, 5, 5, 6, - 6, 5, 6, 6, 6, 6, 7, 6, 5, 6, 7, 5, 7, 6, 6, 7, - 6, 6, 5, 7, 5, 6, 6, 5, 7, 5, 6, 5, 6, 6, 5, 6, - 6, 7, 7, 6, 7, 7, 5, 7, 6, 7, 7, 5, 7, 5, 6, 6, - 6, 7, 7, 7, 7, 5, 6, 7, 7, 7, 5, -] - -def make_sizes(num_monsters=251): - """ - Front pics have specified sizes. - """ - rom = load_rom() - base_stats = 0x51424 - - address = base_stats + 0x11 # pic size - sizes = rom[address : address + 0x20 * num_monsters : 0x20] - sizes = map(lambda x: str(x & 0xf), sizes) - return '\n'.join(' ' * 8 + ', '.join(split(sizes, 16))) - - -def decompress_fx_by_id(i, fxs=0xcfcf6): - rom = load_rom() - addr = fxs + i * 4 - - num_tiles = rom[addr] - bank = rom[addr+1] - address = rom[addr+3] * 0x100 + rom[addr+2] - - offset = rom_offset(bank, address) - fx = Decompressed(rom, start=offset) - return fx - -def rip_compressed_fx(dest='gfx/fx', num_fx=40, fxs=0xcfcf6): - for i in xrange(num_fx): - name = '%.3d' % i - fx = decompress_fx_by_id(i, fxs) - filename = os.path.join(dest, name + '.2bpp.lz') - to_file(filename, fx.compressed_data) - - -monsters = 0x120000 -num_monsters = 251 - -unowns = 0x124000 -num_unowns = 26 -unown_dex = 201 - -def decompress_monster_by_id(rom, mon=0, face='front', crystal=True): - """ - For Unown, use decompress_unown_by_id instead. - """ - if crystal: - bank_offset = 0x36 - else: - bank_offset = 0 - - address = monsters + (mon * 2 + {'front': 0, 'back': 1}.get(face, 0)) * 3 - bank = rom[address] + bank_offset - address = rom[address+2] * 0x100 + rom[address+1] - address = bank * 0x4000 + (address - (0x4000 * bool(bank))) - monster = Decompressed(rom, start=address) - return monster - -def rip_compressed_monster_pics(rom, dest='gfx/pics/', face='both', num_mons=num_monsters, crystal=True): - """ - Extract <num_mons> compressed Pokemon pics from <rom> to directory <dest>. - """ - for mon in range(num_mons): - - mon_name = pokemon_constants[mon + 1].lower().replace('__','_') - size = sizes[mon] - - if mon + 1 == unown_dex: - rip_compressed_unown_pics( - rom=rom, - dest=dest, - face=face, - num_letters=num_unowns, - mon_name=mon_name, - size=size, - crystal=crystal, - ) - - if face in ['front', 'both']: - monster = decompress_monster_by_id(rom, mon, 'front', crystal) - filename = 'front.{0}x{0}.2bpp.lz'.format(size) - path = os.path.join(dest, mon_name, filename) - to_file(path, monster.compressed_data) - - if face in ['back', 'both']: - monster = decompress_monster_by_id(rom, mon, 'back', crystal) - filename = 'back.6x6.2bpp.lz' - path = os.path.join(dest, mon_name, filename) - to_file(path, monster.compressed_data) - -def decompress_unown_by_id(rom, letter, face='front', crystal=True): - if crystal: - bank_offset = 0x36 - else: - bank_offset = 0 - - address = unowns + (letter * 2 + {'front': 0, 'back': 1}.get(face, 0)) * 3 - bank = rom[address] + bank_offset - address = rom[address+2] * 0x100 + rom[address+1] - address = (bank * 0x4000) + (address - (0x4000 * bool(bank))) - unown = Decompressed(rom, start=address) - return unown - -def rip_compressed_unown_pics(rom, dest='gfx/pics/', face='both', num_letters=num_unowns, mon_name='unown', size=sizes[201], crystal=True): - """ - Extract <num_letters> compressed Unown pics from <rom> to directory <dest>. - """ - for letter in range(num_letters): - name = mon_name + '_{}'.format(chr(ord('A') + letter)) - - if face in ['front', 'both']: - unown = decompress_unown_by_id(rom, letter, 'front', crystal) - filename = 'front.{0}x{0}.2bpp.lz'.format(size) - path = os.path.join(dest, name, filename) - to_file(path, unown.compressed_data) - - if face in ['back', 'both']: - unown = decompress_unown_by_id(rom, letter, 'back', crystal) - filename = 'back.6x6.2bpp.lz' - path = os.path.join(dest, name, filename) - to_file(path, unown.compressed_data) - - -trainers_offset = 0x128000 -num_trainers = 67 -trainer_names = [t['constant'] for i, t in trainers.trainer_group_names.items()] - -def decompress_trainer_by_id(rom, i, crystal=True): - rom = load_rom() - if crystal: - bank_offset = 0x36 - else: - bank_offset = 0 - - address = trainers_offset + i * 3 - bank = rom[address] + bank_offset - address = rom[address+2] * 0x100 + rom[address+1] - address = rom_offset(bank, address) - trainer = Decompressed(rom, start=address) - return trainer - -def rip_compressed_trainer_pics(rom): - for t in xrange(num_trainers): - trainer_name = trainer_names[t].lower().replace('_','') - trainer = decompress_trainer_by_id(t) - filename = os.path.join('gfx/trainers/', trainer_name + '.6x6.2bpp.lz') - to_file(filename, trainer.compressed_data) - - -# in order of use (besides repeats) -intro_gfx = [ - ('logo', 0x109407), - ('unowns', 0xE5F5D), - ('pulse', 0xE634D), - ('background', 0xE5C7D), - ('pichu_wooper', 0xE592D), - ('suicune_run', 0xE555D), - ('suicune_jump', 0xE6DED), - ('unown_back', 0xE785D), - ('suicune_close', 0xE681D), - ('suicune_back', 0xE72AD), - ('crystal_unowns', 0xE662D), -] - -intro_tilemaps = [ - ('001', 0xE641D), - ('002', 0xE63DD), - ('003', 0xE5ECD), - ('004', 0xE5E6D), - ('005', 0xE647D), - ('006', 0xE642D), - ('007', 0xE655D), - ('008', 0xE649D), - ('009', 0xE76AD), - ('010', 0xE764D), - ('011', 0xE6D0D), - ('012', 0xE6C3D), - ('013', 0xE778D), - ('014', 0xE76BD), - ('015', 0xE676D), - ('017', 0xE672D), -] - -def rip_compressed_intro(rom, dest='gfx/intro'): - - for name, address in intro_gfx: - filename = os.path.join(dest, name + '.2bpp.lz') - rip_compressed_gfx(rom, address, filename) - - for name, address in intro_tilemaps: - filename = os.path.join(dest, name + '.tilemap.lz') - rip_compressed_gfx(rom, address, filename) - - -title_gfx = [ - ('suicune', 0x10EF46), - ('logo', 0x10F326), - ('crystal', 0x10FCEE), -] - -def rip_compressed_title(rom, dest='gfx/title'): - for name, address in title_gfx: - filename = os.path.join(dest, name + '.2bpp.lz') - rip_compressed_gfx(rom, address, filename) - - -def rip_compressed_tilesets(rom, dest='gfx/tilesets'): - tileset_headers = 0x4d596 - len_tileset = 15 - num_tilesets = 0x25 - - for tileset in xrange(num_tilesets): - addr = tileset * len_tileset + tileset_headers - - bank = rom[addr] - address = rom[addr + 2] * 0x100 + rom[addr + 1] - offset = rom_offset(bank, address) - - filename = os.path.join(dest, tileset_name + '.2bpp.lz') - rip_compressed_gfx(rom, address, filename) - - -misc_pics = [ - ('player', 0x2BA1A, '6x6'), - ('dude', 0x2BBAA, '6x6'), -] - -misc = [ - ('town_map', 0xF8BA0), - ('pokegear', 0x1DE2E4), - ('pokegear_sprites', 0x914DD), -] - -def rip_compressed_misc(rom, dest='gfx/misc'): - for name, address in misc: - filename = os.path.join(dest, name+ '.2bpp.lz') - rip_compressed_gfx(rom, address, filename) - for name, address, dimensions in misc_pics: - filename = os.path.join(dest, name + '.' + dimensions + '.2bpp.lz') - rip_compressed_gfx(rom, address, filename) - - -def rip_compressed_gfx(rom, address, filename): - gfx = Decompressed(rom, start=address) - to_file(filename, gfx.compressed_data) - - -def rip_bulk_gfx(rom, dest='gfx', crystal=True): - rip_compressed_monster_pics(rom, dest=os.path.join(dest, 'pics'), crystal=crystal) - rip_compressed_trainer_pics(rom, dest=os.path.join(dest, 'trainers'), crystal=crystal) - rip_compressed_fx (rom, dest=os.path.join(dest, 'fx')) - rip_compressed_intro (rom, dest=os.path.join(dest, 'intro')) - rip_compressed_title (rom, dest=os.path.join(dest, 'title')) - rip_compressed_tilesets (rom, dest=os.path.join(dest, 'tilesets')) - rip_compressed_misc (rom, dest=os.path.join(dest, 'misc')) - - -def decompress_from_address(address, filename='de.2bpp'): - """ - Write decompressed data from an address to a 2bpp file. - """ - rom = load_rom() - image = Decompressed(rom, start=address) - to_file(filename, image.output) - - def decompress_file(filein, fileout=None): image = bytearray(open(filein).read()) de = Decompressed(image) @@ -500,20 +198,6 @@ def compress_file(filein, fileout=None): to_file(fileout, lz.output) - -def get_uncompressed_gfx(start, num_tiles, filename): - """ - Grab tiles directly from rom and write to file. - """ - rom = load_rom() - bytes_per_tile = 0x10 - length = num_tiles * bytes_per_tile - end = start + length - image = rom[start:end] - to_file(filename, image) - - - def bin_to_rgb(word): red = word & 0b11111 word >>= 5 @@ -522,10 +206,6 @@ def bin_to_rgb(word): blue = word & 0b11111 return (red, green, blue) -def rgb_from_rom(address, length=0x80): - rom = load_rom() - return convert_binary_pal_to_text(rom[address:address+length]) - def convert_binary_pal_to_text_by_filename(filename): pal = bytearray(open(filename).read()) return convert_binary_pal_to_text(pal) @@ -557,70 +237,6 @@ def rewrite_binary_pals_to_text(filenames): out.write(pal_text) -def dump_monster_pals(): - rom = load_rom() - - pals = 0xa8d6 - pal_length = 0x4 - for mon in range(251): - - name = pokemon_constants[mon+1].title().replace('_','') - num = str(mon+1).zfill(3) - dir = 'gfx/pics/'+num+'/' - - address = pals + mon*pal_length*2 - - - pal_data = [] - for byte in range(pal_length): - pal_data.append(rom[address]) - address += 1 - - filename = 'normal.pal' - to_file('../'+dir+filename, pal_data) - - spacing = ' ' * (15 - len(name)) - #print name+'Palette:'+spacing+' INCBIN "'+dir+filename+'"' - - - pal_data = [] - for byte in range(pal_length): - pal_data.append(rom[address]) - address += 1 - - filename = 'shiny.pal' - to_file('../'+dir+filename, pal_data) - - spacing = ' ' * (10 - len(name)) - #print name+'ShinyPalette:'+spacing+' INCBIN "'+dir+filename+'"' - - -def dump_trainer_pals(): - rom = load_rom() - - pals = 0xb0d2 - pal_length = 0x4 - for trainer in range(67): - - name = trainers.trainer_group_names[trainer+1]['constant'].title().replace('_','') - num = str(trainer).zfill(3) - dir = 'gfx/trainers/' - - address = pals + trainer*pal_length - - pal_data = [] - for byte in range(pal_length): - pal_data.append(rom[address]) - address += 1 - - filename = num+'.pal' - to_file('../'+dir+filename, pal_data) - - spacing = ' ' * (12 - len(name)) - print name+'Palette:'+spacing+' INCBIN"'+dir+filename+'"' - - - def flatten(planar): """ Flatten planar 2bpp image data into a quaternary pixel map. @@ -639,7 +255,6 @@ def flatten(planar): strips += strip return strips - def to_lines(image, width): """ Convert a tiled quaternary pixel map to lines of quaternary pixels. @@ -893,127 +508,6 @@ def convert_2bpp_to_png(image, **kwargs): return width, height, palette, greyscale, bitdepth, px_map -def get_pic_animation(tmap, w, h): - """ - Generate pic animation data from a combined tilemap of each frame. - """ - frame_text = '' - bitmask_text = '' - - frames = list(split(tmap, w * h)) - base = frames.pop(0) - bitmasks = [] - - for i in xrange(len(frames)): - frame_text += '\tdw .frame{}\n'.format(i + 1) - - for i, frame in enumerate(frames): - bitmask = map(operator.ne, frame, base) - if bitmask not in bitmasks: - bitmasks.append(bitmask) - which_bitmask = bitmasks.index(bitmask) - - mask = iter(bitmask) - masked_frame = filter(lambda _: mask.next(), frame) - - frame_text += '.frame{}\n'.format(i + 1) - frame_text += '\tdb ${:02x} ; bitmask\n'.format(which_bitmask) - if masked_frame: - frame_text += '\tdb {}\n'.format(', '.join( - map('${:02x}'.format, masked_frame) - )) - - for i, bitmask in enumerate(bitmasks): - bitmask_text += '; {}\n'.format(i) - for byte in split(bitmask, 8): - byte = int(''.join(map(int.__repr__, reversed(byte))), 2) - bitmask_text += '\tdb %{:08b}\n'.format(byte) - - return frame_text, bitmask_text - - -def dump_pic_animations(addresses={'bitmasks': 'BitmasksPointers', 'frames': 'FramesPointers'}, pokemon=pokemon_constants, rom=None): - """ - The code to dump pic animations from rom is mysteriously absent. - Here it is again, but now it dumps images instead of text. - Said text can then be derived from the images. - """ - - if rom is None: rom = load_rom() - - # Labels can be passed in instead of raw addresses. - for which, offset in addresses.items(): - if type(offset) is str: - for line in open('pokecrystal.sym').readlines(): - if offset in line.split(): - addresses[which] = rom_offset(*map(lambda x: int(x, 16), line[:7].split(':'))) - break - - for i, name in pokemon.items(): - if name.lower() == 'unown': continue - - i -= 1 - - directory = os.path.join('gfx', 'pics', name.lower()) - size = sizes[i] - - if i > 151 - 1: - bank = 0x36 - else: - bank = 0x35 - address = addresses['frames'] + i * 2 - address = rom_offset(bank, rom[address] + rom[address + 1] * 0x100) - addrs = [] - while address not in addrs: - addr = rom[address] + rom[address + 1] * 0x100 - addrs.append(rom_offset(bank, addr)) - address += 2 - num_frames = len(addrs) - - # To go any further, we need bitmasks. - # Bitmasks need the number of frames, which we now have. - - bank = 0x34 - address = addresses['bitmasks'] + i * 2 - address = rom_offset(bank, rom[address] + rom[address + 1] * 0x100) - length = size ** 2 - num_bytes = (length + 7) / 8 - bitmasks = [] - for _ in xrange(num_frames): - bitmask = [] - bytes_ = rom[ address : address + num_bytes ] - for byte in bytes_: - bits = map(int, bin(byte)[2:].zfill(8)) - bits.reverse() - bitmask += bits - bitmasks.append(bitmask) - address += num_bytes - - # Back to frames: - frames = [] - for addr in addrs: - bitmask = bitmasks[rom[addr]] - num_tiles = len(filter(int, bitmask)) - frame = (rom[addr], rom[addr + 1 : addr + 1 + num_tiles]) - frames.append(frame) - - tmap = range(length) * (len(frames) + 1) - for i, frame in enumerate(frames): - bitmask = bitmasks[frame[0]] - tiles = (x for x in frame[1]) - for j, bit in enumerate(bitmask): - if bit: - tmap[(i + 1) * length + j] = tiles.next() - - filename = os.path.join(directory, 'front.{0}x{0}.2bpp.lz'.format(size)) - tiles = get_tiles(Decompressed(open(filename).read()).output) - new_tiles = map(tiles.__getitem__, tmap) - new_image = connect(new_tiles) - filename = os.path.splitext(filename)[0] - to_file(filename, new_image) - export_2bpp_to_png(filename) - - def export_png_to_2bpp(filein, fileout=None, palout=None, **kwargs): arguments = { @@ -1259,7 +753,6 @@ def png_to_lz(filein): to_file(name+'.2bpp'+'.lz', Compressed(image).output) - def convert_2bpp_to_1bpp(data): """ Convert planar 2bpp image data to 1bpp. Assume images are two colors. @@ -1322,73 +815,6 @@ def png_to_1bpp(filename, **kwargs): return convert_2bpp_to_1bpp(image) -def mass_to_png(directory='gfx'): - # greyscale - for root, dirs, files in os.walk('./gfx/'): - convert_to_png(map(lambda x: os.path.join(root, x), files)) - -def mass_to_colored_png(directory='gfx'): - # greyscale, unless a palette is detected - for root, dirs, files in os.walk(directory): - for name in files: - - if os.path.splitext(name)[1] == '.2bpp': - pal = None - if 'pics' in root: - pal = 'normal.pal' - elif 'trainers' in root: - pal = os.path.splitext(name)[0] + '.pal' - if pal != None: - pal = os.path.join(root, pal) - export_2bpp_to_png(os.path.join(root, name), pal_file=pal) - - elif os.path.splitext(name)[1] == '.1bpp': - export_1bpp_to_png(os.path.join(root, name)) - - -def append_terminator_to_lzs(directory='gfx'): - """ - Add a terminator to any lz files that were extracted without one. - """ - for root, dirs, files in os.walk(directory): - for filename in files: - path = os.path.join(root, filename) - if os.path.splitext(path)[1] == '.lz': - data = bytearray(open(path,'rb').read()) - - # don't mistake padding for a missing terminator - i = 1 - while data[-i] == 0: - i += 1 - - if data[-i] != 0xff: - data += [0xff] - with open(path, 'wb') as out: - out.write(data) - - -def expand_binary_pic_palettes(directory): - """ - Add white and black to palette files with fewer than 4 colors. - - Pokemon Crystal only defines two colors for a pic palette to - save space, filling in black/white at runtime. - Instead of managing palette files of varying length, black - and white are added to pic palettes and excluded from incbins. - """ - for root, dirs, files in os.walk(directory): - if os.path.join(directory, 'pics') in root or os.path.join(directory, '/trainers') in root: - for name in files: - if os.path.splitext(name)[1] == '.pal': - filename = os.path.join(root, name) - palette = bytearray(open(filename, 'rb').read()) - w = bytearray([0xff, 0x7f]) - b = bytearray([0x00, 0x00]) - if len(palette) == 4: - with open(filename, 'wb') as out: - out.write(w + palette + b) - - def convert_to_2bpp(filenames=[]): for filename in filenames: filename, name, extension = try_decompress(filename) @@ -1471,7 +897,5 @@ def main(): method(args.filenames) - if __name__ == "__main__": main() - |