7a8 > import operator 12c13 < import pokemon_constants --- > from pokemon_constants import pokemon_constants 15a17 > from lz import Compressed, Decompressed 17,19c19,28 < def load_rom(): < rom = romstr.RomStr.load(filename=config.rom_path) < return rom --- > > > 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) 69a79,83 > > 00 01 02 03 00 04 08 > 04 05 06 07 <-> 01 05 09 > 08 09 0a 0b 02 06 0a > 03 07 0b 117c131,137 < def condense_tiles_to_map(image): --- > def condense_image_to_map(image, pic=0): > """ > Reduce an image of adjacent frames to an image containing a base frame and any unrepeated tiles. > Returns the new image and the corresponding tilemap used to reconstruct the input image. > > If is 0, ignore the concept of frames. This behavior might be better off as another function. > """ 119,124c139 < new_tiles = [] < tilemap = [] < for tile in tiles: < if tile not in new_tiles: < new_tiles += [tile] < tilemap += [new_tiles.index(tile)] --- > new_tiles, tilemap = condense_tiles_to_map(tiles, pic) 127a143,146 > def condense_tiles_to_map(tiles, pic=0): > """ > Reduce a sequence of tiles representing adjacent frames to a base frame and any unrepeated tiles. > Returns the new tiles and the corresponding tilemap used to reconstruct the input tile sequence. 129,422c148,149 < def to_file(filename, data): < file = open(filename, 'wb') < for byte in data: < file.write('%c' % byte) < file.close() < < < < """ < A rundown of Pokemon Crystal's compression scheme: < < Control commands occupy bits 5-7. < Bits 0-4 serve as the first parameter for each command. < """ < lz_commands = { < 'literal': 0, # n values for n bytes < 'iterate': 1, # one value for n bytes < 'alternate': 2, # alternate two values for n bytes < 'blank': 3, # zero for n bytes < } < < """ < Repeater commands repeat any data that was just decompressed. < They take an additional signed parameter to mark a relative starting point. < These wrap around (positive from the start, negative from the current position). < """ < lz_commands.update({ < 'repeat': 4, # n bytes starting from s < 'flip': 5, # n bytes in reverse bit order starting from s < 'reverse': 6, # n bytes backwards starting from s < }) < < """ < The long command is used when 5 bits aren't enough. Bits 2-4 contain a new control code. < Bits 0-1 are appended to a new byte as 8-9, allowing a 10-bit parameter. < """ < lz_commands.update({ < 'long': 7, # n is now 10 bits for a new control code < }) < max_length = 1 << 10 # can't go higher than 10 bits < lowmax = 1 << 5 # standard 5-bit param < < """ < If 0xff is encountered instead of a command, decompression ends. < """ < lz_end = 0xff < < < class Compressed: < < """ < Compress arbitrary data, usually 2bpp. < """ < < def __init__(self, image=None, mode='horiz', size=None): < assert image, 'need something to compress!' < image = list(image) < self.image = image < self.pic = [] < self.animtiles = [] < < # only transpose pic (animtiles were never transposed in decompression) < if size != None: < for byte in range((size*size)*16): < self.pic += image[byte] < for byte in range(((size*size)*16),len(image)): < self.animtiles += image[byte] < else: < self.pic = image < < if mode == 'vert': < self.tiles = get_tiles(self.pic) < self.tiles = transpose(self.tiles) < self.pic = connect(self.tiles) < < self.image = self.pic + self.animtiles < < self.end = len(self.image) < < self.byte = None < self.address = 0 < < self.stream = [] < < self.zeros = [] < self.alts = [] < self.iters = [] < self.repeats = [] < self.flips = [] < self.reverses = [] < self.literals = [] < < self.output = [] < < self.compress() < < < def compress(self): < """ < Incomplete, but outputs working compressed data. < """ < < self.address = 0 < < # todo < #self.scanRepeats() < < while ( self.address < self.end ): < < #if (self.repeats): < # self.doRepeats() < < #if (self.flips): < # self.doFlips() < < #if (self.reverses): < # self.doReverses < < if (self.checkWhitespace()): < self.doLiterals() < self.doWhitespace() < < elif (self.checkIter()): < self.doLiterals() < self.doIter() < < elif (self.checkAlts()): < self.doLiterals() < self.doAlts() < < else: # doesn't fit any pattern -> literal < self.addLiteral() < self.next() < < self.doStream() < < # add any literals we've been sitting on < self.doLiterals() < < # done < self.output.append(lz_end) < < < def getCurByte(self): < if self.address < self.end: < self.byte = ord(self.image[self.address]) < else: self.byte = None < < def next(self): < self.address += 1 < self.getCurByte() < < def addLiteral(self): < self.getCurByte() < self.literals.append(self.byte) < if len(self.literals) > max_length: < raise Exception, "literals exceeded max length and the compressor didn't catch it" < elif len(self.literals) == max_length: < self.doLiterals() < < def doLiterals(self): < if len(self.literals) > lowmax: < self.output.append( (lz_commands['long'] << 5) | (lz_commands['literal'] << 2) | ((len(self.literals) - 1) >> 8) ) < self.output.append( (len(self.literals) - 1) & 0xff ) < elif len(self.literals) > 0: < self.output.append( (lz_commands['literal'] << 5) | (len(self.literals) - 1) ) < for byte in self.literals: < self.output.append(byte) < self.literals = [] < < def doStream(self): < for byte in self.stream: < self.output.append(byte) < self.stream = [] < < < def scanRepeats(self): < """ < Works, but doesn't do flipped/reversed streams yet. < < This takes up most of the compress time and only saves a few bytes. < It might be more effective to exclude it entirely. < """ < < self.repeats = [] < self.flips = [] < self.reverses = [] < < # make a 5-letter word list of the sequence < letters = 5 # how many bytes it costs to use a repeat over a literal < # any shorter and it's not worth the trouble < num_words = len(self.image) - letters < words = [] < for i in range(self.address,num_words): < word = [] < for j in range(letters): < word.append( ord(self.image[i+j]) ) < words.append((word, i)) < < zeros = [] < for zero in range(letters): < zeros.append( 0 ) < < # check for matches < def get_matches(): < # TODO: < # append to 3 different match lists instead of yielding to one < # < #flipped = [] < #for byte in enumerate(this[0]): < # flipped.append( sum(1<<(7-i) for i in range(8) if (this[0][byte])>>i&1) ) < #reversed = this[0][::-1] < # < for whereabout, this in enumerate(words): < for that in range(whereabout+1,len(words)): < if words[that][0] == this[0]: < if words[that][1] - this[1] >= letters: < # remove zeros < if this[0] != zeros: < yield [this[0], this[1], words[that][1]] < < matches = list(get_matches()) < < # remove more zeros < buffer = [] < for match in matches: < # count consecutive zeros in a word < num_zeros = 0 < highest = 0 < for j in range(letters): < if match[0][j] == 0: < num_zeros += 1 < else: < if highest < num_zeros: highest = num_zeros < num_zeros = 0 < if highest < 4: < # any more than 3 zeros in a row isn't worth it < # (and likely to already be accounted for) < buffer.append(match) < matches = buffer < < # combine overlapping matches < buffer = [] < for this, match in enumerate(matches): < if this < len(matches) - 1: # special case for the last match < if matches[this+1][1] <= (match[1] + len(match[0])): # check overlap < if match[1] + len(match[0]) < match[2]: < # next match now contains this match's bytes too < # this only appends the last byte (assumes overlaps are +1 < match[0].append(matches[this+1][0][-1]) < matches[this+1] = match < elif match[1] + len(match[0]) == match[2]: < # we've run into the thing we matched < buffer.append(match) < # else we've gone past it and we can ignore it < else: # no more overlaps < buffer.append(match) < else: # last match, so there's nothing to check < buffer.append(match) < matches = buffer < < # remove alternating sequences < buffer = [] < for match in matches: < for i in range(6 if letters > 6 else letters): < if match[0][i] != match[0][i&1]: < buffer.append(match) < break < matches = buffer < < self.repeats = matches < < < def doRepeats(self): < """doesn't output the right values yet""" < < unusedrepeats = [] < for repeat in self.repeats: < if self.address >= repeat[2]: < < # how far in we are < length = (len(repeat[0]) - (self.address - repeat[2])) < < # decide which side we're copying from < if (self.address - repeat[1]) <= 0x80: < self.doLiterals() < self.stream.append( (lz_commands['repeat'] << 5) | length - 1 ) < < # wrong? < self.stream.append( (((self.address - repeat[1])^0xff)+1)&0xff ) < < else: < self.doLiterals() < self.stream.append( (lz_commands['repeat'] << 5) | length - 1 ) --- > If is 0, ignore the concept of frames. This behavior might be better off as another function. > """ 424,457c151,153 < # wrong? < self.stream.append(repeat[1]>>8) < self.stream.append(repeat[1]&0xff) < < #print hex(self.address) + ': ' + hex(len(self.output)) + ' ' + hex(length) < self.address += length < < else: unusedrepeats.append(repeat) < < self.repeats = unusedrepeats < < < def checkWhitespace(self): < self.zeros = [] < self.getCurByte() < original_address = self.address < < if ( self.byte == 0 ): < while ( self.byte == 0 ) & ( len(self.zeros) <= max_length ): < self.zeros.append(self.byte) < self.next() < if len(self.zeros) > 1: < return True < self.address = original_address < return False < < def doWhitespace(self): < if (len(self.zeros) + 1) >= lowmax: < self.stream.append( (lz_commands['long'] << 5) | (lz_commands['blank'] << 2) | ((len(self.zeros) - 1) >> 8) ) < self.stream.append( (len(self.zeros) - 1) & 0xff ) < elif len(self.zeros) > 1: < self.stream.append( lz_commands['blank'] << 5 | (len(self.zeros) - 1) ) < else: < raise Exception, "checkWhitespace() should prevent this from happening" --- > # Leave the first frame intact for pics. > new_tiles = tiles[:pic] > tilemap = range(pic) 458a155,157 > for i, tile in enumerate(tiles[pic:]): > if tile not in new_tiles: > new_tiles.append(tile) 460,515c159,169 < def checkAlts(self): < self.alts = [] < self.getCurByte() < original_address = self.address < num_alts = 0 < < # make sure we don't check for alts at the end of the file < if self.address+3 >= self.end: return False < < self.alts.append(self.byte) < self.alts.append(ord(self.image[self.address+1])) < < # are we onto smething? < if ( ord(self.image[self.address+2]) == self.alts[0] ): < cur_alt = 0 < while (ord(self.image[(self.address)+1]) == self.alts[num_alts&1]) & (num_alts <= max_length): < num_alts += 1 < self.next() < # include the last alternated byte < num_alts += 1 < self.address = original_address < if num_alts > lowmax: < return True < elif num_alts > 2: < return True < return False < < def doAlts(self): < original_address = self.address < self.getCurByte() < < #self.alts = [] < #num_alts = 0 < < #self.alts.append(self.byte) < #self.alts.append(ord(self.image[self.address+1])) < < #i = 0 < #while (ord(self.image[self.address+1]) == self.alts[i^1]) & (num_alts <= max_length): < # num_alts += 1 < # i ^=1 < # self.next() < ## include the last alternated byte < #num_alts += 1 < < num_alts = len(self.iters) + 1 < < if num_alts > lowmax: < self.stream.append( (lz_commands['long'] << 5) | (lz_commands['alternate'] << 2) | ((num_alts - 1) >> 8) ) < self.stream.append( num_alts & 0xff ) < self.stream.append( self.alts[0] ) < self.stream.append( self.alts[1] ) < elif num_alts > 2: < self.stream.append( (lz_commands['alternate'] << 5) | (num_alts - 1) ) < self.stream.append( self.alts[0] ) < self.stream.append( self.alts[1] ) --- > if pic: > # Match the first frame exactly where possible. > # This reduces the space needed to replace tiles in pic animations. > # For example, if a tile is repeated twice in the first frame, > # but at the same relative index as the second tile, use the second index. > # When creating a bitmask later, the second index would not require a replacement, but the first index would have. > pic_i = i % pic > if tile == new_tiles[pic_i]: > tilemap.append(pic_i) > else: > tilemap.append(new_tiles.index(tile)) 517,521c171,172 < raise Exception, "checkAlts() should prevent this from happening" < < self.address = original_address < self.address += num_alts < --- > tilemap.append(new_tiles.index(tile)) > return new_tiles, tilemap 523,560c174,186 < def checkIter(self): < self.iters = [] < self.getCurByte() < iter = self.byte < original_address = self.address < while (self.byte == iter) & (len(self.iters) < max_length): < self.iters.append(self.byte) < self.next() < self.address = original_address < if len(self.iters) > 3: < # 3 or fewer isn't worth the trouble and actually longer < # if part of a larger literal set < return True < < return False < < def doIter(self): < self.getCurByte() < iter = self.byte < original_address = self.address < < self.iters = [] < while (self.byte == iter) & (len(self.iters) < max_length): < self.iters.append(self.byte) < self.next() < < if (len(self.iters) - 1) >= lowmax: < self.stream.append( (lz_commands['long'] << 5) | (lz_commands['iterate'] << 2) | ((len(self.iters)-1) >> 8) ) < self.stream.append( (len(self.iters) - 1) & 0xff ) < self.stream.append( iter ) < elif len(self.iters) > 3: < # 3 or fewer isn't worth the trouble and actually longer < # if part of a larger literal set < self.stream.append( (lz_commands['iterate'] << 5) | (len(self.iters) - 1) ) < self.stream.append( iter ) < else: < self.address = original_address < raise Exception, "checkIter() should prevent this from happening" --- > def test_condense_tiles_to_map(): > test = condense_tiles_to_map(list('abcadbae')) > if test != (list('abcde'), [0, 1, 2, 0, 3, 1, 0, 4]): > raise Exception(test) > test = condense_tiles_to_map(list('abcadbae'), 2) > if test != (list('abcde'), [0, 1, 2, 0, 3, 1, 0, 4]): > raise Exception(test) > test = condense_tiles_to_map(list('abcadbae'), 4) > if test != (list('abcade'), [0, 1, 2, 3, 4, 1, 0, 5]): > raise Exception(test) > test = condense_tiles_to_map(list('abcadbea'), 4) > if test != (list('abcade'), [0, 1, 2, 3, 4, 1, 5, 3]): > raise Exception(test) 563c189 < class Decompressed: --- > def to_file(filename, data): 565,574c191 < Parse compressed data, usually 2bpp. < < parameters: < [compressed data] < [tile arrangement] default: 'vert' < [size of pic] default: None < [start] (optional) < < splits output into pic [size] and animation tiles if applicable < data can be fed in from rom if [start] is specified --- > Apparently open(filename, 'wb').write(bytearray(data)) won't work. 575a193,196 > file = open(filename, 'wb') > for byte in data: > file.write('%c' % byte) > file.close() 577,626d197 < def __init__(self, lz=None, mode=None, size=None, start=0): < # todo: play nice with Compressed < < assert lz, 'need something to compress!' < self.lz = lz < < self.byte = None < self.address = 0 < self.start = start < < self.output = [] < < self.decompress() < < debug = False < # print tuple containing start and end address < if debug: print '(' + hex(self.start) + ', ' + hex(self.start + self.address+1) + '),' < < # only transpose pic < self.pic = [] < self.animtiles = [] < < if size != None: < self.tiles = get_tiles(self.output) < self.pic = connect(self.tiles[:(size*size)]) < self.animtiles = connect(self.tiles[(size*size):]) < else: self.pic = self.output < < if mode == 'vert': < self.tiles = get_tiles(self.pic) < self.tiles = transpose(self.tiles) < self.pic = connect(self.tiles) < < self.output = self.pic + self.animtiles < < < def decompress(self): < """ < Replica of crystal's decompression. < """ < < self.output = [] < < while True: < self.getCurByte() < < if (self.byte == lz_end): < break < < self.cmd = (self.byte & 0b11100000) >> 5 628,733d198 < if self.cmd == lz_commands['long']: # 10-bit param < self.cmd = (self.byte & 0b00011100) >> 2 < self.length = (self.byte & 0b00000011) << 8 < self.next() < self.length += self.byte + 1 < else: # 5-bit param < self.length = (self.byte & 0b00011111) + 1 < < # literals < if self.cmd == lz_commands['literal']: < self.doLiteral() < elif self.cmd == lz_commands['iterate']: < self.doIter() < elif self.cmd == lz_commands['alternate']: < self.doAlt() < elif self.cmd == lz_commands['blank']: < self.doZeros() < < else: # repeaters < self.next() < if self.byte > 0x7f: # negative < self.displacement = self.byte & 0x7f < self.displacement = len(self.output) - self.displacement - 1 < else: # positive < self.displacement = self.byte * 0x100 < self.next() < self.displacement += self.byte < < if self.cmd == lz_commands['flip']: < self.doFlip() < elif self.cmd == lz_commands['reverse']: < self.doReverse() < else: # lz_commands['repeat'] < self.doRepeat() < < self.address += 1 < #self.next() # somewhat of a hack < < < def getCurByte(self): < self.byte = ord(self.lz[self.start+self.address]) < < def next(self): < self.address += 1 < self.getCurByte() < < def doLiteral(self): < """ < Copy data directly. < """ < for byte in range(self.length): < self.next() < self.output.append(self.byte) < < def doIter(self): < """ < Write one byte repeatedly. < """ < self.next() < for byte in range(self.length): < self.output.append(self.byte) < < def doAlt(self): < """ < Write alternating bytes. < """ < self.alts = [] < self.next() < self.alts.append(self.byte) < self.next() < self.alts.append(self.byte) < < for byte in range(self.length): < self.output.append(self.alts[byte&1]) < < def doZeros(self): < """ < Write zeros. < """ < for byte in range(self.length): < self.output.append(0x00) < < def doFlip(self): < """ < Repeat flipped bytes from output. < < eg 11100100 -> 00100111 < quat 3 2 1 0 -> 0 2 1 3 < """ < for byte in range(self.length): < flipped = sum(1<<(7-i) for i in range(8) if self.output[self.displacement+byte]>>i&1) < self.output.append(flipped) < < def doReverse(self): < """ < Repeat reversed bytes from output. < """ < for byte in range(self.length): < self.output.append(self.output[self.displacement-byte]) < < def doRepeat(self): < """ < Repeat bytes from output. < """ < for byte in range(self.length): < self.output.append(self.output[self.displacement+byte]) 756c221 < def make_sizes(): --- > def make_sizes(num_monsters=251): 761d225 < top = 251 763,766d226 < # print monster sizes < address = base_stats + 0x11 < < output = '' 768,772c228,231 < for id in range(top): < size = (ord(rom[address])) & 0x0f < if id % 16 == 0: output += '\n\t' < output += str(size) + ', ' < address += 0x20 --- > 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))) 774d232 < print output 775a234,236 > def decompress_fx_by_id(i, fxs=0xcfcf6): > rom = load_rom() > addr = fxs + i * 4 776a238,240 > num_tiles = rom[addr] > bank = rom[addr+1] > address = rom[addr+3] * 0x100 + rom[addr+2] 778,788c242,243 < def decompress_fx_by_id(id, fxs=0xcfcf6): < rom = load_rom() < address = fxs + id*4 # len_fxptr < # get size < num_tiles = ord(rom[address]) # # tiles < # get pointer < bank = ord(rom[address+1]) < address = (ord(rom[address+3]) << 8) + ord(rom[address+2]) < address = (bank * 0x4000) + (address & 0x3fff) < # decompress < fx = Decompressed(rom, 'horiz', num_tiles, address) --- > offset = rom_offset(bank, address) > fx = Decompressed(rom, start=offset) 791,795c246,251 < def decompress_fx(num_fx=40): < for id in range(num_fx): < fx = decompress_fx_by_id(id) < filename = './gfx/fx/' + str(id).zfill(3) + '.2bpp' # ./gfx/fx/039.2bpp < to_file(filename, fx.pic) --- > 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) 798,801d253 < num_pics = 2 < front = 0 < back = 1 < 809,823c261,274 < def decompress_monster_by_id(id=0, type=front): < rom = load_rom() < # no unowns here < if id + 1 == unown_dex: return None < # get size < if type == front: < size = sizes[id] < else: size = None < # get pointer < address = monsters + (id*2 + type)*3 # bank, address < bank = ord(rom[address]) + 0x36 # crystal < address = (ord(rom[address+2]) << 8) + ord(rom[address+1]) < address = (bank * 0x4000) + (address & 0x3fff) < # decompress < monster = Decompressed(rom, 'vert', size, address) --- > 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) 826,841c277,281 < def decompress_monsters(type=front): < for id in range(num_monsters): < # decompress < monster = decompress_monster_by_id(id, type) < if monster != None: # no unowns here < if not type: # front < filename = 'front.2bpp' < folder = './gfx/pics/' + str(id+1).zfill(3) + '/' < to_file(folder+filename, monster.pic) < filename = 'tiles.2bpp' < folder = './gfx/pics/' + str(id+1).zfill(3) + '/' < to_file(folder+filename, monster.animtiles) < else: # back < filename = 'back.2bpp' < folder = './gfx/pics/' + str(id+1).zfill(3) + '/' < to_file(folder+filename, monster.pic) --- > def rip_compressed_monster_pics(rom, dest='gfx/pics/', face='both', num_mons=num_monsters, crystal=True): > """ > Extract compressed Pokemon pics from to directory . > """ > for mon in range(num_mons): 842a283,284 > mon_name = pokemon_constants[mon + 1].lower().replace('__','_') > size = sizes[mon] 844,856c286,319 < def decompress_unown_by_id(letter, type=front): < rom = load_rom() < # get size < if type == front: < size = sizes[unown_dex-1] < else: size = None < # get pointer < address = unowns + (letter*2 + type)*3 # bank, address < bank = ord(rom[address]) + 0x36 # crystal < address = (ord(rom[address+2]) << 8) + ord(rom[address+1]) < address = (bank * 0x4000) + (address & 0x3fff) < # decompress < unown = Decompressed(rom, 'vert', size, address) --- > 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) 859,874c322,339 < def decompress_unowns(type=front): < for letter in range(num_unowns): < # decompress < unown = decompress_unown_by_id(letter, type) < < if not type: # front < filename = 'front.2bpp' < folder = './gfx/pics/' + str(unown_dex).zfill(3) + chr(ord('a') + letter) + '/' < to_file(folder+filename, unown.pic) < filename = 'tiles.2bpp' < folder = './gfx/anim/' < to_file(folder+filename, unown.animtiles) < else: # back < filename = 'back.2bpp' < folder = './gfx/pics/' + str(unown_dex).zfill(3) + chr(ord('a') + letter) + '/' < to_file(folder+filename, unown.pic) --- > def rip_compressed_unown_pics(rom, dest='gfx/pics/', face='both', num_letters=num_unowns, mon_name='unown', size=sizes[201], crystal=True): > """ > Extract compressed Unown pics from to directory . > """ > 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) 877c342 < trainers = 0x128000 --- > trainers_offset = 0x128000 878a344 > trainer_names = [t['constant'] for i, t in trainers.trainer_group_names.items()] 880c346 < def decompress_trainer_by_id(id): --- > def decompress_trainer_by_id(rom, i, crystal=True): 882,888c348,357 < # get pointer < address = trainers + id*3 # bank, address < bank = ord(rom[address]) + 0x36 # crystal < address = (ord(rom[address+2]) << 8) + ord(rom[address+1]) < address = (bank * 0x4000) + (address & 0x3fff) < # decompress < trainer = Decompressed(rom, 'vert', None, address) --- > 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) 891,896c360,365 < def decompress_trainers(): < for id in range(num_trainers): < # decompress < trainer = decompress_trainer_by_id(id) < filename = './gfx/trainers/' + str(id).zfill(3) + '.2bpp' # ./gfx/trainers/066.2bpp < to_file(filename, trainer.pic) --- > 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) 899c368 < # in order of use (sans repeats) --- > # in order of use (besides repeats) 901,925c370,379 < ('logo', 0x109407), < ('001', 0xE641D), # tilemap < ('unowns', 0xE5F5D), < ('pulse', 0xE634D), < ('002', 0xE63DD), # tilemap < ('003', 0xE5ECD), # tilemap < ('background', 0xE5C7D), < ('004', 0xE5E6D), # tilemap < ('005', 0xE647D), # tilemap < ('006', 0xE642D), # tilemap < ('pichu_wooper', 0xE592D), < ('suicune_run', 0xE555D), < ('007', 0xE655D), # tilemap < ('008', 0xE649D), # tilemap < ('009', 0xE76AD), # tilemap < ('suicune_jump', 0xE6DED), < ('unown_back', 0xE785D), < ('010', 0xE764D), # tilemap < ('011', 0xE6D0D), # tilemap < ('suicune_close', 0xE681D), < ('012', 0xE6C3D), # tilemap < ('013', 0xE778D), # tilemap < ('suicune_back', 0xE72AD), < ('014', 0xE76BD), # tilemap < ('015', 0xE676D), # tilemap --- > ('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), 927d380 < ('017', 0xE672D), # tilemap 930,931c383,403 < def decompress_intro(): < rom = load_rom() --- > 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'): > 933,935c405,410 < filename = './gfx/intro/' + name + '.2bpp' < gfx = Decompressed( rom, 'horiz', None, address ) < to_file(filename, gfx.output) --- > 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) 940c415 < ('logo', 0x10F326), --- > ('logo', 0x10F326), 944,945c419 < def decompress_title(): < rom = load_rom() --- > def rip_compressed_title(rom, dest='gfx/title'): 947,949c421,422 < filename = './gfx/title/' + name + '.2bpp' < gfx = Decompressed( rom, 'horiz', None, address ) < to_file(filename, gfx.output) --- > filename = os.path.join(dest, name + '.2bpp.lz') > rip_compressed_gfx(rom, address, filename) 951,976d423 < def decompress_tilesets(): < rom = load_rom() < tileset_headers = 0x4d596 < len_tileset = 15 < num_tilesets = 0x25 < for tileset in range(num_tilesets): < ptr = tileset*len_tileset + tileset_headers < address = (ord(rom[ptr])*0x4000) + (((ord(rom[ptr+1]))+ord(rom[ptr+2])*0x100)&0x3fff) < tiles = Decompressed( rom, 'horiz', None, address ) < filename = './gfx/tilesets/'+str(tileset).zfill(2)+'.2bpp' < to_file( filename, tiles.output ) < #print '(' + hex(address) + ', '+ hex(address+tiles.address+1) + '),' < < misc = [ < ('player', 0x2BA1A, 'vert'), < ('dude', 0x2BBAA, 'vert'), < ('town_map', 0xF8BA0, 'horiz'), < ('pokegear', 0x1DE2E4, 'horiz'), < ('pokegear_sprites', 0x914DD, 'horiz'), < ] < def decompress_misc(): < rom = load_rom() < for name, address, mode in misc: < filename = './gfx/misc/' + name + '.2bpp' < gfx = Decompressed( rom, mode, None, address ) < to_file(filename, gfx.output) 978,990c425,428 < def decompress_all(debug=False): < """ < Decompress all known compressed data in baserom. < """ < < if debug: print 'fronts' < decompress_monsters(front) < if debug: print 'backs' < decompress_monsters(back) < if debug: print 'unown fronts' < decompress_unowns(front) < if debug: print 'unown backs' < decompress_unowns(back) --- > def rip_compressed_tilesets(rom, dest='gfx/tilesets'): > tileset_headers = 0x4d596 > len_tileset = 15 > num_tilesets = 0x25 992,993c430,431 < if debug: print 'trainers' < decompress_trainers() --- > for tileset in xrange(num_tilesets): > addr = tileset * len_tileset + tileset_headers 995,996c433,435 < if debug: print 'fx' < decompress_fx() --- > bank = rom[addr] > address = rom[addr + 2] * 0x100 + rom[addr + 1] > offset = rom_offset(bank, address) 998,999c437,438 < if debug: print 'intro' < decompress_intro() --- > filename = os.path.join(dest, tileset_name + '.2bpp.lz') > rip_compressed_gfx(rom, address, filename) 1001,1002d439 < if debug: print 'title' < decompress_title() 1004,1005c441,444 < if debug: print 'tilesets' < decompress_tilesets() --- > misc_pics = [ > ('player', 0x2BA1A, '6x6'), > ('dude', 0x2BBAA, '6x6'), > ] 1007,1008c446,450 < if debug: print 'misc' < decompress_misc() --- > misc = [ > ('town_map', 0xF8BA0), > ('pokegear', 0x1DE2E4), > ('pokegear_sprites', 0x914DD), > ] 1010c452,473 < return --- > 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')) 1013c476 < def decompress_from_address(address, mode='horiz', filename='de.2bpp', size=None): --- > def decompress_from_address(address, filename='de.2bpp'): 1018,1019c481,482 < image = Decompressed(rom, mode, size, address) < to_file(filename, image.pic) --- > image = Decompressed(rom, start=address) > to_file(filename, image.output) 1022,1029c485,487 < def decompress_file(filein, fileout, mode='horiz', size=None): < f = open(filein, 'rb') < image = f.read() < f.close() < < de = Decompressed(image, mode, size) < < to_file(fileout, de.pic) --- > def decompress_file(filein, fileout=None): > image = bytearray(open(filein).read()) > de = Decompressed(image) 1030a489,491 > if fileout == None: > fileout = os.path.splitext(filein)[0] > to_file(fileout, de.output) 1032,1035d492 < def compress_file(filein, fileout, mode='horiz'): < f = open(filein, 'rb') < image = f.read() < f.close() 1037c494,496 < lz = Compressed(image, mode) --- > def compress_file(filein, fileout=None): > image = bytearray(open(filein).read()) > lz = Compressed(image) 1038a498,499 > if fileout == None: > fileout = filein + '.lz' 1043,1061d503 < < def compress_monster_frontpic(id, fileout): < mode = 'vert' < < fpic = './gfx/pics/' + str(id).zfill(3) + '/front.2bpp' < fanim = './gfx/pics/' + str(id).zfill(3) + '/tiles.2bpp' < < pic = open(fpic, 'rb').read() < anim = open(fanim, 'rb').read() < image = pic + anim < < lz = Compressed(image, mode, sizes[id-1]) < < out = './gfx/pics/' + str(id).zfill(3) + '/front.lz' < < to_file(out, lz.output) < < < 1068,1072c510,512 < length = num_tiles*bytes_per_tile < end = start + length < image = [] < for address in range(start,end): < image.append(ord(rom[address])) --- > length = num_tiles * bytes_per_tile > end = start + length > image = rom[start:end] 1078c518 < red = word & 0b11111 --- > red = word & 0b11111 1082c522 < blue = word & 0b11111 --- > blue = word & 0b11111 1090,1091c530 < with open(filename) as f: < pal = bytearray(f.read()) --- > pal = bytearray(open(filename).read()) 1128c567 < name = pokemon_constants.pokemon_constants[mon+1].title().replace('_','') --- > name = pokemon_constants[mon+1].title().replace('_','') 1137c576 < pal_data.append(ord(rom[address])) --- > pal_data.append(rom[address]) 1149c588 < pal_data.append(ord(rom[address])) --- > pal_data.append(rom[address]) 1174c613 < pal_data.append(ord(rom[address])) --- > pal_data.append(rom[address]) 1191,1192c630,631 < bottom = ord(bottom) < top = ord(top) --- > bottom = bottom > top = top 1287c726,733 < int_args = { --- > """ > Infer graphics conversion arguments given a filename. > > Arguments are separated with '.'. > """ > parsed_arguments = {} > > int_arguments = { 1292,1293c738 < parsed_arguments = {} < arguments = os.path.splitext(filename)[0].split('.')[1:] --- > arguments = os.path.splitext(filename)[0].lstrip('.').split('.')[1:] 1294a740,741 > > # Check for integer arguments first (i.e. "w128"). 1298c745 < arg = int_args.get(arg, False) --- > arg = int_arguments.get(arg, False) 1301,1308c748 < elif len(argument) == 3: < w, x, h = argument[:3] < if w.isdigit() and h.isdigit() and x == 'x': < parsed_arguments['pic_dimensions'] = (int(w), int(h)) < elif argument == 'interleave': < parsed_arguments['interleave'] = True < elif argument == 'norepeat': < parsed_arguments['norepeat'] = True --- > 1311a752,761 > > # Pic dimensions (i.e. "6x6"). > elif 'x' in argument and any(map(str.isdigit, argument)): > w, h = argument.split('x') > if w.isdigit() and h.isdigit(): > parsed_arguments['pic_dimensions'] = (int(w), int(h)) > > else: > parsed_arguments[argument] = True > 1315c765 < def export_2bpp_to_png(filein, fileout=None, pal_file=None, height=0, width=0, tile_padding=0, pic_dimensions=None): --- > def export_2bpp_to_png(filein, fileout=None, pal_file=None, height=0, width=0, tile_padding=0, pic_dimensions=None, **kwargs): 1354a805,808 > image = bytearray(image) > > pad_color = bytearray([0]) > 1364c818 < image = ''.join(interleave_tiles(image, width / 8)) --- > image = interleave_tiles(image, width / 8) 1367c821 < image += chr(0) * 0x10 * tile_padding --- > image += pad_color * 0x10 * tile_padding 1380,1381c834,835 < pic += transpose_tiles(image[i:i+pic_length], w) < image = ''.join(pic) + image[len(image) - trailing:] --- > pic += transpose_tiles(image[i:i+pic_length], h) > image = bytearray(pic) + image[len(image) - trailing:] 1384c838 < image += chr(0) * 0x10 * ((w - (len(image) / 0x10) % h) % w) --- > image += pad_color * 0x10 * ((w - (len(image) / 0x10) % h) % w) 1394c848 < image += chr(0) * 0x10 * more_tile_padding --- > image += pad_color * 0x10 * more_tile_padding 1399c853 < image += chr(0) * 0x10 * more_tile_padding --- > image += pad_color * 0x10 * more_tile_padding 1405c859 < image += chr(0) * 0x10 * more_tile_padding --- > image += pad_color * 0x10 * more_tile_padding 1442c896,1017 < def export_png_to_2bpp(filein, fileout=None, palout=None, tile_padding=0, pic_dimensions=None): --- > 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): 1445,1446c1020,1023 < 'tile_padding': tile_padding, < 'pic_dimensions': pic_dimensions, --- > 'tile_padding': 0, > 'pic_dimensions': None, > 'animate': False, > 'stupid_bitmask_hack': [], 1447a1025 > arguments.update(kwargs) 1450c1028 < image, palette, tmap = png_to_2bpp(filein, **arguments) --- > image, arguments = png_to_2bpp(filein, **arguments) 1456,1458c1034,1038 < if tmap != None: < mapout = os.path.splitext(fileout)[0] + '.tilemap' < to_file(mapout, tmap) --- > tmap = arguments.get('tmap') > > if tmap != None and arguments['animate'] and arguments['pic_dimensions']: > # Generate pic animation data. > frame_text, bitmask_text = get_pic_animation(tmap, *arguments['pic_dimensions']) 1459a1040,1060 > frames_path = os.path.join(os.path.split(fileout)[0], 'frames.asm') > with open(frames_path, 'w') as out: > out.write(frame_text) > > bitmask_path = os.path.join(os.path.split(fileout)[0], 'bitmask.asm') > > # The following Pokemon have a bitmask dummied out. > for exception in arguments['stupid_bitmask_hack']: > if exception in bitmask_path: > bitmasks = bitmask_text.split(';') > bitmasks[-1] = bitmasks[-1].replace('1', '0') > bitmask_text = ';'.join(bitmasks) > > with open(bitmask_path, 'w') as out: > out.write(bitmask_text) > > elif tmap != None and arguments.get('tilemap', False): > tilemap_path = os.path.splitext(fileout)[0] + '.tilemap' > to_file(tilemap_path, tmap) > > palette = arguments.get('palette') 1492,1496c1093,1100 < tile_padding = kwargs.get('tile_padding', 0) < pic_dimensions = kwargs.get('pic_dimensions', None) < interleave = kwargs.get('interleave', False) < norepeat = kwargs.get('norepeat', False) < tilemap = kwargs.get('tilemap', False) --- > arguments = { > 'tile_padding': 0, > 'pic_dimensions': False, > 'interleave': False, > 'norepeat': False, > 'tilemap': False, > } > arguments.update(kwargs) 1498,1501c1102,1107 < with open(filein, 'rb') as data: < width, height, rgba, info = png.Reader(data).asRGBA8() < rgba = list(rgba) < greyscale = info['greyscale'] --- > if type(filein) is str: > filein = open(filein) > > assert type(filein) is file > > width, height, rgba, info = png.Reader(filein).asRGBA8() 1504c1110 < len_px = 4 # rgba --- > len_px = len('rgba') 1510,1514c1116 < color = { 'r': line[px ], < 'g': line[px+1], < 'b': line[px+2], < 'a': line[px+3], } < newline += [color] --- > color = dict(zip('rgba', line[px:px+len_px])) 1516c1118,1125 < palette += [color] --- > if len(palette) < 4: > palette += [color] > else: > # TODO Find the nearest match > print 'WARNING: %s: Color %s truncated to' % (filein, color), > color = sorted(palette, key=lambda x: sum(x.values()))[0] > print color > newline += [color] 1519c1128 < assert len(palette) <= 4, 'Palette should be 4 colors, is really %d' % len(palette) --- > assert len(palette) <= 4, '%s: palette should be 4 colors, is really %d (%s)' % (filein, len(palette), palette) 1522,1523c1131 < hues = { < 'white': { 'r': 0xff, 'g': 0xff, 'b': 0xff, 'a': 0xff }, --- > greyscale = { 1526a1135 > 'white': { 'r': 0xff, 'g': 0xff, 'b': 0xff, 'a': 0xff }, 1528c1137,1138 < for hue in hues.values(): --- > preference = 'white', 'black', 'grey', 'gray' > for hue in map(greyscale.get, preference): 1534,1540c1144 < # Sort palettes by luminance < def luminance(color): < rough = { 'r': 4.7, < 'g': 1.4, < 'b': 13.8, } < return sum(color[key] * rough[key] for key in rough.keys()) < palette.sort(key=luminance) --- > palette.sort(key=lambda x: sum(x.values())) 1549c1153 < pad = [0] --- > pad = bytearray([0]) 1584,1585c1188,1197 < if pic_dimensions: < w, h = pic_dimensions --- > dim = arguments['pic_dimensions'] > if dim: > if type(dim) in (tuple, list): > w, h = dim > else: > # infer dimensions based on width. > w = width / tile_width > h = height / tile_height > if h % w == 0: > h = w 1603c1215,1217 < image = image[:len(image) - tile_padding * 0x10] --- > image = image[:len(image) - arguments['tile_padding'] * 0x10] > > tmap = None 1605c1219 < if interleave: --- > if arguments['interleave']: 1608,1611c1222,1227 < if norepeat: < image, tmap = condense_tiles_to_map(image) < if not tilemap: < tmap = None --- > if arguments['pic_dimensions']: > image, tmap = condense_image_to_map(image, w * h) > elif arguments['norepeat']: > image, tmap = condense_image_to_map(image) > if not arguments['tilemap']: > tmap = None 1613c1229,1231 < return image, palette, tmap --- > arguments.update({ 'palette': palette, 'tmap': tmap, }) > > return image, arguments 1703c1321 < image, palette, tmap = png_to_2bpp(filename, **kwargs) --- > image, kwargs = png_to_2bpp(filename, **kwargs) 1707c1325 < def mass_to_png(debug=False): --- > def mass_to_png(directory='gfx'): 1710,1713c1328 < for name in files: < if debug: print os.path.splitext(name), os.path.join(root, name) < if os.path.splitext(name)[1] == '.2bpp': < export_2bpp_to_png(os.path.join(root, name)) --- > convert_to_png(map(lambda x: os.path.join(root, x), files)) 1715c1330 < def mass_to_colored_png(debug=False): --- > def mass_to_colored_png(directory='gfx'): 1717,1729c1332 < for root, dirs, files in os.walk('./gfx/'): < if 'pics' not in root and 'trainers' not in root: < for name in files: < if debug: print os.path.splitext(name), os.path.join(root, name) < if os.path.splitext(name)[1] == '.2bpp': < export_2bpp_to_png(os.path.join(root, name)) < os.utime(os.path.join(root, name), None) < elif os.path.splitext(name)[1] == '.1bpp': < export_1bpp_to_png(os.path.join(root, name)) < os.utime(os.path.join(root, name), None) < < # only monster and trainer pics for now < for root, dirs, files in os.walk('./gfx/pics/'): --- > for root, dirs, files in os.walk(directory): 1731,1737d1333 < if debug: print os.path.splitext(name), os.path.join(root, name) < if os.path.splitext(name)[1] == '.2bpp': < if 'normal.pal' in files: < export_2bpp_to_png(os.path.join(root, name), None, os.path.join(root, 'normal.pal')) < else: < export_2bpp_to_png(os.path.join(root, name)) < os.utime(os.path.join(root, name), None) 1739,1741d1334 < for root, dirs, files in os.walk('./gfx/trainers/'): < for name in files: < if debug: print os.path.splitext(name), os.path.join(root, name) 1743,1744c1336,1343 < export_2bpp_to_png(os.path.join(root, name)) < os.utime(os.path.join(root, name), None) --- > 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) 1745a1345,1346 > elif os.path.splitext(name)[1] == '.1bpp': > export_1bpp_to_png(os.path.join(root, name)) 1747,1769d1347 < def mass_decompress(debug=False): < for root, dirs, files in os.walk('./gfx/'): < for name in files: < if 'lz' in name: < if '/pics' in root: < if 'front' in name: < id = root.split('pics/')[1][:3] < if id != 'egg': < with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert', sizes[int(id)-1]) < else: < with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert', 4) < to_file(os.path.join(root, 'front.2bpp'), de.pic) < to_file(os.path.join(root, 'tiles.2bpp'), de.animtiles) < elif 'back' in name: < with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert') < to_file(os.path.join(root, 'back.2bpp'), de.output) < elif '/trainers' in root or '/fx' in root: < with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert') < to_file(os.path.join(root, os.path.splitext(name)[0]+'.2bpp'), de.output) < else: < with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read()) < to_file(os.path.join(root, os.path.splitext(name)[0]+'.2bpp'), de.output) < os.utime(os.path.join(root, name), None) 1771,1783c1349 < def append_terminator_to_lzs(directory): < # fix lzs that were extracted with a missing terminator < for root, dirs, files in os.walk(directory): < for file in files: < if '.lz' in file: < data = open(root+file,'rb').read() < if data[-1] != chr(0xff): < data += chr(0xff) < new = open(root+file,'wb') < new.write(data) < new.close() < < def export_lz_to_png(filename): --- > def append_terminator_to_lzs(directory='gfx'): 1785c1351 < Convert a lz file to png. Dump a 2bpp file too. --- > Add a terminator to any lz files that were extracted without one. 1787,1814c1353,1367 < assert filename[-3:] == ".lz" < lz_data = open(filename, "rb").read() < < bpp = Decompressed(lz_data).output < bpp_filename = os.path.splitext(filename)[0] < to_file(bpp_filename, bpp) < < export_2bpp_to_png(bpp_filename) < < # touch the lz file so it doesn't get remade < os.utime(filename, None) < < def dump_tileset_pngs(): < """ < Convert .lz format tilesets into .png format tilesets. < < Also, leaves a bunch of wonderful .2bpp files everywhere for your amusement. < """ < for tileset_id in range(37): < tileset_filename = "./gfx/tilesets/" + str(tileset_id).zfill(2) + ".lz" < export_lz_to_png(tileset_filename) < < def decompress_frontpic(lz_file): < """ < Convert the pic portion of front.lz to front.2bpp < """ < lz = open(lz_file, 'rb').read() < to_file(Decompressed(lz).pic, os.path.splitext(filein)[0] + '.2bpp') --- > 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) 1816,1821d1368 < def decompress_frontpic_anim(lz_file): < """ < Convert the animation tile portion of front.lz to tiles.2bpp < """ < lz = open(lz_file, 'rb').read() < to_file(Decompressed(lz).animtiles, 'tiles.2bpp') 1823c1370 < def expand_pic_palettes(): --- > def expand_binary_pic_palettes(directory): 1832,1833c1379,1380 < for root, dirs, files in os.walk('./gfx/'): < if 'gfx/pics' in root or 'gfx/trainers' in root: --- > for root, dirs, files in os.walk(directory): > if os.path.join(directory, 'pics') in root or os.path.join(directory, '/trainers') in root: 1930d1476 <