Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 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 <pic> 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 <n> 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 <s> 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 <pic> 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 <num_mons> compressed Pokemon pics from <rom> to directory <dest>.
- > """
- > 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 <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)
- 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
- <
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement