mirror of
https://github.com/pret/pokemon-reverse-engineering-tools.git
synced 2026-06-24 00:19:57 -05:00
commit
1840111de5
|
|
@ -112,6 +112,18 @@ def deinterleave_tiles(image, width):
|
|||
return connect(deinterleave(get_tiles(image), width))
|
||||
|
||||
|
||||
def condense_tiles_to_map(image):
|
||||
tiles = get_tiles(image)
|
||||
new_tiles = []
|
||||
tilemap = []
|
||||
for tile in tiles:
|
||||
if tile not in new_tiles:
|
||||
new_tiles += [tile]
|
||||
tilemap += [new_tiles.index(tile)]
|
||||
new_image = connect(new_tiles)
|
||||
return new_image, tilemap
|
||||
|
||||
|
||||
def to_file(filename, data):
|
||||
file = open(filename, 'wb')
|
||||
for byte in data:
|
||||
|
|
@ -1282,6 +1294,11 @@ def read_filename_arguments(filename):
|
|||
parsed_arguments['pic_dimensions'] = (int(w), int(h))
|
||||
elif argument == 'interleave':
|
||||
parsed_arguments['interleave'] = True
|
||||
elif argument == 'norepeat':
|
||||
parsed_arguments['norepeat'] = True
|
||||
elif argument == 'arrange':
|
||||
parsed_arguments['norepeat'] = True
|
||||
parsed_arguments['tilemap'] = True
|
||||
return parsed_arguments
|
||||
|
||||
|
||||
|
|
@ -1339,15 +1356,22 @@ def convert_2bpp_to_png(image, **kwargs):
|
|||
# Pad the image by a given number of tiles if asked.
|
||||
image += chr(0) * 0x10 * tile_padding
|
||||
|
||||
# Frontpics are transposed independently of animation graphics.
|
||||
# Some images are transposed in blocks.
|
||||
if pic_dimensions:
|
||||
w, h = pic_dimensions
|
||||
i = w * h * 0x10
|
||||
pic = ''.join(transpose_tiles(image[:i], w))
|
||||
anim = image[i:]
|
||||
image = pic + anim
|
||||
# Pad out animation tiles as well.
|
||||
image += chr(0) * 0x10 * ((w - len(get_tiles(image)) % h) % w)
|
||||
if not width: width = w * 8
|
||||
|
||||
pic_length = w * h * 0x10
|
||||
|
||||
trailing = len(image) % pic_length
|
||||
|
||||
pic = []
|
||||
for i in xrange(0, len(image) - trailing, pic_length):
|
||||
pic += transpose_tiles(image[i:i+pic_length], w)
|
||||
image = ''.join(pic) + image[len(image) - trailing:]
|
||||
|
||||
# Pad out trailing lines.
|
||||
image += chr(0) * 0x10 * ((w - (len(image) / 0x10) % h) % w)
|
||||
|
||||
def px_length(img):
|
||||
return len(img) * 4
|
||||
|
|
@ -1413,12 +1437,16 @@ def export_png_to_2bpp(filein, fileout=None, palout=None, tile_padding=0, pic_di
|
|||
}
|
||||
arguments.update(read_filename_arguments(filein))
|
||||
|
||||
image, palette = png_to_2bpp(filein, **arguments)
|
||||
image, palette, tmap = png_to_2bpp(filein, **arguments)
|
||||
|
||||
if fileout == None:
|
||||
fileout = os.path.splitext(filein)[0] + '.2bpp'
|
||||
to_file(fileout, image)
|
||||
|
||||
if tmap != None:
|
||||
mapout = os.path.splitext(fileout)[0] + '.tilemap'
|
||||
to_file(mapout, tmap)
|
||||
|
||||
if palout == None:
|
||||
palout = os.path.splitext(fileout)[0] + '.pal'
|
||||
export_palette(palette, palout)
|
||||
|
|
@ -1433,12 +1461,12 @@ def get_image_padding(width, height, wstep=8, hstep=8):
|
|||
'bottom': 0,
|
||||
}
|
||||
|
||||
if width % wstep:
|
||||
if width % wstep and width >= wstep:
|
||||
pad = float(width % wstep) / 2
|
||||
padding['left'] = int(ceil(pad))
|
||||
padding['right'] = int(floor(pad))
|
||||
|
||||
if height % hstep:
|
||||
if height % hstep and height >= hstep:
|
||||
pad = float(height % hstep) / 2
|
||||
padding['top'] = int(ceil(pad))
|
||||
padding['bottom'] = int(floor(pad))
|
||||
|
|
@ -1454,6 +1482,8 @@ def png_to_2bpp(filein, **kwargs):
|
|||
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)
|
||||
|
||||
with open(filein, 'rb') as data:
|
||||
width, height, rgba, info = png.Reader(data).asRGBA8()
|
||||
|
|
@ -1520,15 +1550,15 @@ def png_to_2bpp(filein, **kwargs):
|
|||
# Graphics are stored in tiles instead of lines
|
||||
tile_width = 8
|
||||
tile_height = 8
|
||||
num_columns = width / tile_width
|
||||
num_rows = height / tile_height
|
||||
num_columns = max(width, tile_width) / tile_width
|
||||
num_rows = max(height, tile_height) / tile_height
|
||||
image = []
|
||||
|
||||
for row in xrange(num_rows):
|
||||
for column in xrange(num_columns):
|
||||
|
||||
# Split it up into strips to convert to planar data
|
||||
for strip in xrange(tile_height):
|
||||
for strip in xrange(min(tile_height, height)):
|
||||
anchor = (
|
||||
row * num_columns * tile_width * tile_height +
|
||||
column * tile_width +
|
||||
|
|
@ -1541,13 +1571,23 @@ def png_to_2bpp(filein, **kwargs):
|
|||
top += (quad /2 & 1) << (7 - bit)
|
||||
image += [bottom, top]
|
||||
|
||||
# Frontpics are transposed independently of animation graphics.
|
||||
if pic_dimensions:
|
||||
w, h = pic_dimensions
|
||||
i = w * h * 0x10
|
||||
pic = transpose_tiles(image[:i], w)
|
||||
anim = image[i:]
|
||||
image = pic + anim
|
||||
w, h = pic_dimensions
|
||||
|
||||
tiles = get_tiles(image)
|
||||
pic_length = w * h
|
||||
tile_width = width / 8
|
||||
trailing = len(tiles) % pic_length
|
||||
new_image = []
|
||||
for block in xrange(len(tiles) / pic_length):
|
||||
offset = (h * tile_width) * ((block * w) / tile_width) + ((block * w) % tile_width)
|
||||
pic = []
|
||||
for row in xrange(h):
|
||||
index = offset + (row * tile_width)
|
||||
pic += tiles[index:index + w]
|
||||
new_image += transpose(pic, w)
|
||||
new_image += tiles[len(tiles) - trailing:]
|
||||
image = connect(new_image)
|
||||
|
||||
# Remove any tile padding used to make the png rectangular.
|
||||
image = image[:len(image) - tile_padding * 0x10]
|
||||
|
|
@ -1555,7 +1595,12 @@ def png_to_2bpp(filein, **kwargs):
|
|||
if interleave:
|
||||
image = deinterleave_tiles(image, num_columns)
|
||||
|
||||
return image, palette
|
||||
if norepeat:
|
||||
image, tmap = condense_tiles_to_map(image)
|
||||
if not tilemap:
|
||||
tmap = None
|
||||
|
||||
return image, palette, tmap
|
||||
|
||||
|
||||
def export_palette(palette, filename):
|
||||
|
|
@ -1645,7 +1690,7 @@ def export_png_to_1bpp(filename, fileout=None):
|
|||
to_file(fileout, image)
|
||||
|
||||
def png_to_1bpp(filename, **kwargs):
|
||||
image, palette = png_to_2bpp(filename, **kwargs)
|
||||
image, palette, tmap = png_to_2bpp(filename, **kwargs)
|
||||
return convert_2bpp_to_1bpp(image)
|
||||
|
||||
|
||||
|
|
@ -1789,7 +1834,7 @@ def expand_pic_palettes():
|
|||
|
||||
def convert_to_2bpp(filenames=[]):
|
||||
for filename in filenames:
|
||||
name, extension = os.path.splitext(filename)
|
||||
filename, name, extension = try_decompress(filename)
|
||||
if extension == '.1bpp':
|
||||
export_1bpp_to_2bpp(filename)
|
||||
elif extension == '.2bpp':
|
||||
|
|
@ -1801,7 +1846,7 @@ def convert_to_2bpp(filenames=[]):
|
|||
|
||||
def convert_to_1bpp(filenames=[]):
|
||||
for filename in filenames:
|
||||
name, extension = os.path.splitext(filename)
|
||||
filename, name, extension = try_decompress(filename)
|
||||
if extension == '.1bpp':
|
||||
pass
|
||||
elif extension == '.2bpp':
|
||||
|
|
@ -1813,7 +1858,7 @@ def convert_to_1bpp(filenames=[]):
|
|||
|
||||
def convert_to_png(filenames=[]):
|
||||
for filename in filenames:
|
||||
name, extension = os.path.splitext(filename)
|
||||
filename, name, extension = try_decompress(filename)
|
||||
if extension == '.1bpp':
|
||||
export_1bpp_to_png(filename)
|
||||
elif extension == '.2bpp':
|
||||
|
|
@ -1836,6 +1881,19 @@ def decompress(filenames=[]):
|
|||
data = Decompressed(lz_data).output
|
||||
to_file(name, data)
|
||||
|
||||
def try_decompress(filename):
|
||||
"""
|
||||
Try to decompress a graphic when determining the filetype.
|
||||
This skips the manual unlz step when attempting
|
||||
to convert lz-compressed graphics to png.
|
||||
"""
|
||||
name, extension = os.path.splitext(filename)
|
||||
if extension == '.lz':
|
||||
decompress([filename])
|
||||
filename = name
|
||||
name, extension = os.path.splitext(filename)
|
||||
return filename, name, extension
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
|
|
|
|||
488
pokemontools/pic.py
Normal file
488
pokemontools/pic.py
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
A library for use with compressed monster and trainer pics in pokered.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from math import sqrt
|
||||
|
||||
from gfx import transpose_tiles
|
||||
|
||||
|
||||
def bitflip(x, n):
|
||||
r = 0
|
||||
while n:
|
||||
r = (r << 1) | (x & 1)
|
||||
x >>= 1
|
||||
n -= 1
|
||||
return r
|
||||
|
||||
|
||||
class Decompressor:
|
||||
"""
|
||||
pokered pic decompression.
|
||||
|
||||
Ported to python 2.7 from the python 3 code at https://github.com/magical/pokemon-sprites-rby.
|
||||
"""
|
||||
|
||||
table1 = [(2 << i) - 1 for i in xrange(16)]
|
||||
table2 = [
|
||||
[0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5, 0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa],
|
||||
[0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa, 0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5], # prev ^ 0xf
|
||||
[0x0, 0x8, 0xc, 0x4, 0xe, 0x6, 0x2, 0xa, 0xf, 0x7, 0x3, 0xb, 0x1, 0x9, 0xd, 0x5],
|
||||
[0xf, 0x7, 0x3, 0xb, 0x1, 0x9, 0xd, 0x5, 0x0, 0x8, 0xc, 0x4, 0xe, 0x6, 0x2, 0xa], # prev ^ 0xf
|
||||
]
|
||||
table3 = [bitflip(i, 4) for i in xrange(16)]
|
||||
|
||||
tilesize = 8
|
||||
|
||||
|
||||
def __init__(self, f, mirror=False, planar=True):
|
||||
self.bs = fbitstream(f)
|
||||
self.mirror = mirror
|
||||
self.planar = planar
|
||||
self.data = None
|
||||
|
||||
def decompress(self):
|
||||
rams = [[], []]
|
||||
|
||||
self.sizex = self._readint(4) * self.tilesize
|
||||
self.sizey = self._readint(4)
|
||||
|
||||
self.size = self.sizex * self.sizey
|
||||
|
||||
self.ramorder = self._readbit()
|
||||
|
||||
r1 = self.ramorder
|
||||
r2 = self.ramorder ^ 1
|
||||
|
||||
self._fillram(rams[r1])
|
||||
mode = self._readbit()
|
||||
if mode:
|
||||
mode += self._readbit()
|
||||
self._fillram(rams[r2])
|
||||
|
||||
rams[0] = bytearray(bitgroups_to_bytes(rams[0]))
|
||||
rams[1] = bytearray(bitgroups_to_bytes(rams[1]))
|
||||
|
||||
if mode == 0:
|
||||
self._decode(rams[0])
|
||||
self._decode(rams[1])
|
||||
elif mode == 1:
|
||||
self._decode(rams[r1])
|
||||
self._xor(rams[r1], rams[r2])
|
||||
elif mode == 2:
|
||||
self._decode(rams[r2], mirror=False)
|
||||
self._decode(rams[r1])
|
||||
self._xor(rams[r1], rams[r2])
|
||||
else:
|
||||
raise Exception, "Invalid deinterlace mode!"
|
||||
|
||||
data = []
|
||||
if self.planar:
|
||||
for a, b in zip(rams[0], rams[1]):
|
||||
data += [a, b]
|
||||
self.data = bytearray(data)
|
||||
else:
|
||||
for a, b in zip(bitstream(rams[0]), bitstream(rams[1])):
|
||||
data.append(a | (b << 1))
|
||||
self.data = bitgroups_to_bytes(data)
|
||||
|
||||
def _fillram(self, ram):
|
||||
mode = ['rle', 'data'][self._readbit()]
|
||||
size = self.size * 4
|
||||
while len(ram) < size:
|
||||
if mode == 'rle':
|
||||
self._read_rle_chunk(ram)
|
||||
mode = 'data'
|
||||
elif mode == 'data':
|
||||
self._read_data_chunk(ram, size)
|
||||
mode = 'rle'
|
||||
if len(ram) > size:
|
||||
#ram = ram[:size]
|
||||
raise ValueError(size, len(ram))
|
||||
|
||||
ram[:] = self._deinterlace_bitgroups(ram)
|
||||
|
||||
def _read_rle_chunk(self, ram):
|
||||
|
||||
i = 0
|
||||
while self._readbit():
|
||||
i += 1
|
||||
|
||||
n = self.table1[i]
|
||||
a = self._readint(i + 1)
|
||||
n += a
|
||||
|
||||
for i in xrange(n):
|
||||
ram.append(0)
|
||||
|
||||
def _read_data_chunk(self, ram, size):
|
||||
while 1:
|
||||
bitgroup = self._readint(2)
|
||||
if bitgroup == 0:
|
||||
break
|
||||
ram.append(bitgroup)
|
||||
|
||||
if size <= len(ram):
|
||||
break
|
||||
|
||||
def _decode(self, ram, mirror=None):
|
||||
if mirror is None:
|
||||
mirror = self.mirror
|
||||
|
||||
for x in xrange(self.sizex):
|
||||
bit = 0
|
||||
for y in xrange(self.sizey):
|
||||
i = y * self.sizex + x
|
||||
a = (ram[i] >> 4) & 0xf
|
||||
b = ram[i] & 0xf
|
||||
|
||||
a = self.table2[bit][a]
|
||||
bit = a & 1
|
||||
if mirror:
|
||||
a = self.table3[a]
|
||||
|
||||
b = self.table2[bit][b]
|
||||
bit = b & 1
|
||||
if mirror:
|
||||
b = self.table3[b]
|
||||
|
||||
ram[i] = (a << 4) | b
|
||||
|
||||
def _xor(self, ram1, ram2, mirror=None):
|
||||
if mirror is None:
|
||||
mirror = self.mirror
|
||||
|
||||
for i in xrange(len(ram2)):
|
||||
if mirror:
|
||||
a = (ram2[i] >> 4) & 0xf
|
||||
b = ram2[i] & 0xf
|
||||
a = self.table3[a]
|
||||
b = self.table3[b]
|
||||
ram2[i] = (a << 4) | b
|
||||
|
||||
ram2[i] ^= ram1[i]
|
||||
|
||||
def _deinterlace_bitgroups(self, bits):
|
||||
l = []
|
||||
for y in xrange(self.sizey):
|
||||
for x in xrange(self.sizex):
|
||||
i = 4 * y * self.sizex + x
|
||||
for j in xrange(4):
|
||||
l.append(bits[i])
|
||||
i += self.sizex
|
||||
return l
|
||||
|
||||
|
||||
def _readbit(self):
|
||||
return next(self.bs)
|
||||
|
||||
def _readint(self, count):
|
||||
return readint(self.bs, count)
|
||||
|
||||
|
||||
def fbitstream(f):
|
||||
while 1:
|
||||
char = f.read(1)
|
||||
if not char:
|
||||
break
|
||||
byte = ord(char)
|
||||
|
||||
for i in xrange(7, -1, -1):
|
||||
yield (byte >> i) & 1
|
||||
|
||||
def bitstream(b):
|
||||
for byte in b:
|
||||
for i in xrange(7, -1, -1):
|
||||
yield (byte >> i) & 1
|
||||
|
||||
def readint(bs, count):
|
||||
n = 0
|
||||
while count:
|
||||
n <<= 1
|
||||
n |= next(bs)
|
||||
count -= 1
|
||||
return n
|
||||
|
||||
def bitgroups_to_bytes(bits):
|
||||
l = []
|
||||
for i in xrange(0, len(bits) - 3, 4):
|
||||
n = ((bits[i + 0] << 6)
|
||||
| (bits[i + 1] << 4)
|
||||
| (bits[i + 2] << 2)
|
||||
| (bits[i + 3] << 0))
|
||||
l.append(n)
|
||||
return bytearray(l)
|
||||
|
||||
|
||||
def bytes_to_bits(bytelist):
|
||||
return list(bitstream(bytelist))
|
||||
|
||||
|
||||
class Compressor:
|
||||
"""
|
||||
pokered pic compression.
|
||||
|
||||
Adapted from stag019's C compressor.
|
||||
"""
|
||||
|
||||
table1 = [(2 << i) - 1 for i in xrange(16)]
|
||||
table2 = [
|
||||
[0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8],
|
||||
[0x8, 0x9, 0xb, 0xa, 0xe, 0xf, 0xd, 0xc, 0x4, 0x5, 0x7, 0x6, 0x2, 0x3, 0x1, 0x0], # reverse
|
||||
]
|
||||
table3 = [bitflip(i, 4) for i in xrange(16)]
|
||||
|
||||
def __init__(self, image, width=None, height=None):
|
||||
self.image = bytearray(image)
|
||||
self.size = len(self.image)
|
||||
|
||||
planar_tile = 8 * 8 / 4
|
||||
tile_size = self.size / planar_tile
|
||||
if height and not width: width = tile_size / height
|
||||
elif width and not height: height = tile_size / width
|
||||
elif not width and not height: width = height = int(sqrt(tile_size))
|
||||
self.width, self.height = width, height
|
||||
|
||||
def compress(self):
|
||||
"""
|
||||
Compress the image five times (twice for each mode, except 0)
|
||||
and use the smallest one (in bits).
|
||||
"""
|
||||
rams = [[],[]]
|
||||
datas = []
|
||||
|
||||
for mode in xrange(3):
|
||||
|
||||
# Order is redundant for mode 0.
|
||||
|
||||
# While this seems like an optimization,
|
||||
# it's actually required for 1:1 compression
|
||||
# to the original compressed pics.
|
||||
|
||||
# This appears to be the algorithm
|
||||
# that Game Freak's compressor used.
|
||||
|
||||
# Using order 0 instead of 1 breaks this feature.
|
||||
|
||||
for order in xrange(2):
|
||||
if mode == 0 and order == 0:
|
||||
continue
|
||||
for i in xrange(2):
|
||||
rams[i] = self.image[i::2]
|
||||
self._interpret_compress(rams, mode, order)
|
||||
datas += [(self.data[:], int(self.which_bit))]
|
||||
|
||||
# Pick the smallest pic, measured in bits.
|
||||
datas = sorted(datas, key=lambda (data, bit): (len(data), -bit))
|
||||
self.data, self.which_bit = datas[0]
|
||||
|
||||
def _interpret_compress(self, rams, mode, order):
|
||||
self.data = []
|
||||
self.which_bit = 0
|
||||
|
||||
r1 = order
|
||||
r2 = order ^ 1
|
||||
|
||||
if mode == 0:
|
||||
self._encode(rams[1])
|
||||
self._encode(rams[0])
|
||||
elif mode == 1:
|
||||
self._xor(rams[r1], rams[r2])
|
||||
self._encode(rams[r1])
|
||||
elif mode == 2:
|
||||
self._xor(rams[r1], rams[r2])
|
||||
self._encode(rams[r1])
|
||||
self._encode(rams[r2], mirror=False)
|
||||
else:
|
||||
raise Exception, 'invalid interlace mode!'
|
||||
|
||||
self._writeint(self.height, 4)
|
||||
self._writeint(self.width, 4)
|
||||
|
||||
self._writebit(order)
|
||||
|
||||
self._fillram(rams[r1])
|
||||
if mode == 0:
|
||||
self._writebit(0)
|
||||
else:
|
||||
self._writebit(1)
|
||||
self._writebit(mode - 1)
|
||||
self._fillram(rams[r2])
|
||||
|
||||
def _fillram(self, ram):
|
||||
rle = 0
|
||||
nums = 0
|
||||
bitgroups = []
|
||||
|
||||
for x in xrange(self.width):
|
||||
for bit in xrange(0, 8, 2):
|
||||
byte = x * self.height * 8
|
||||
for y in xrange(self.height * 8):
|
||||
bitgroup = (ram[byte] >> (6 - bit)) & 3
|
||||
if bitgroup == 0:
|
||||
if rle == 0:
|
||||
self._writebit(0)
|
||||
elif rle == 1:
|
||||
nums += 1
|
||||
else:
|
||||
self._data_packet(bitgroups)
|
||||
self._writebit(0)
|
||||
self._writebit(0)
|
||||
rle = 1
|
||||
bitgroups = []
|
||||
else:
|
||||
if rle == 0:
|
||||
self._writebit(1)
|
||||
elif rle == 1:
|
||||
self._rle(nums)
|
||||
rle = -1
|
||||
bitgroups += [bitgroup]
|
||||
nums = 0
|
||||
byte += 1
|
||||
|
||||
if rle == 1:
|
||||
self._rle(nums)
|
||||
else:
|
||||
self._data_packet(bitgroups)
|
||||
|
||||
def _data_packet(self, bitgroups):
|
||||
for bitgroup in bitgroups:
|
||||
self._writebit((bitgroup >> 1) & 1)
|
||||
self._writebit((bitgroup >> 0) & 1)
|
||||
|
||||
def _rle(self, nums):
|
||||
nums += 1
|
||||
|
||||
# Get the previous power of 2.
|
||||
# Deriving the bitcount from that seems to be
|
||||
# faster on average than using the lookup table.
|
||||
v = nums
|
||||
v += 1
|
||||
v |= v >> 1
|
||||
v |= v >> 2
|
||||
v |= v >> 4
|
||||
v |= v >> 8
|
||||
v |= v >> 16
|
||||
v -= v >> 1
|
||||
v -= 1
|
||||
number = nums - v
|
||||
|
||||
bitcount = -1
|
||||
while v:
|
||||
v >>= 1
|
||||
bitcount += 1
|
||||
|
||||
for j in xrange(bitcount):
|
||||
self._writebit(1)
|
||||
self._writebit(0)
|
||||
for j in xrange(bitcount, -1, -1):
|
||||
self._writebit((number >> j) & 1)
|
||||
|
||||
def _encode(self, ram, mirror=None):
|
||||
a = b = 0
|
||||
for i in xrange(len(ram)):
|
||||
j = i / self.height
|
||||
j += i % self.height * self.width * 8
|
||||
if i % self.height == 0:
|
||||
b = 0
|
||||
|
||||
a = (ram[j] >> 4) & 0xf
|
||||
table = b & 1
|
||||
code_1 = self.table2[table][a]
|
||||
|
||||
b = ram[j] & 0xf
|
||||
table = a & 1
|
||||
code_2 = self.table2[table][b]
|
||||
|
||||
ram[j] = (code_1 << 4) | code_2
|
||||
|
||||
def _xor(self, ram1, ram2):
|
||||
for i in xrange(len(ram2)):
|
||||
ram2[i] ^= ram1[i]
|
||||
|
||||
def _writebit(self, bit):
|
||||
self.which_bit -= 1
|
||||
if self.which_bit == -1:
|
||||
self.which_bit = 7
|
||||
self.data += [0]
|
||||
if bit: self.data[-1] |= bit << self.which_bit
|
||||
|
||||
def _writeint(self, num, size=None):
|
||||
bits = []
|
||||
if size:
|
||||
for i in xrange(size):
|
||||
bits += [num & 1]
|
||||
num >>= 1
|
||||
else:
|
||||
while num > 0:
|
||||
bits += [num & 1]
|
||||
num >>= 1
|
||||
for bit in reversed(bits):
|
||||
self._writebit(bit)
|
||||
|
||||
|
||||
def decompress(f, offset=None, mirror=False):
|
||||
"""
|
||||
Decompress a pic given a file object. Return a planar 2bpp image.
|
||||
|
||||
Optional: offset (for roms).
|
||||
"""
|
||||
if offset is not None:
|
||||
f.seek(offset)
|
||||
dcmp = Decompressor(f, mirror=mirror)
|
||||
dcmp.decompress()
|
||||
return dcmp.data
|
||||
|
||||
|
||||
def compress(f):
|
||||
"""
|
||||
Compress a planar 2bpp into a pic.
|
||||
"""
|
||||
comp = Compressor(f)
|
||||
comp.compress()
|
||||
return comp.data
|
||||
|
||||
|
||||
def decompress_file(filename):
|
||||
"""
|
||||
Decompress a pic given a filename.
|
||||
Export the resulting planar 2bpp image to
|
||||
"""
|
||||
pic = open(filename, 'rb')
|
||||
image = decompress(pic)
|
||||
image = transpose_tiles(image)
|
||||
image = bytearray(image)
|
||||
output_filename = os.path.splitext(filename)[0] + '.2bpp'
|
||||
with open(output_filename, 'wb') as out:
|
||||
out.write(image)
|
||||
|
||||
def compress_file(filename):
|
||||
image = open(filename, 'rb').read()
|
||||
image = transpose_tiles(image)
|
||||
pic = compress(image)
|
||||
pic = bytearray(pic)
|
||||
output_filename = os.path.splitext(filename)[0] + '.pic'
|
||||
with open(output_filename, 'wb') as out:
|
||||
out.write(pic)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('mode')
|
||||
ap.add_argument('filenames', nargs='*')
|
||||
args = ap.parse_args()
|
||||
|
||||
for filename in args.filenames:
|
||||
if args.mode == 'decompress':
|
||||
decompress_file(filename)
|
||||
elif args.mode == 'compress':
|
||||
compress_file(filename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user