from PIL import Image
from struct import unpack
from math import ceil
import sys
class I4_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGB", (width, height))
self.img = self.image.load()
self.width = width
self.height = height
self.color = color
def DecodeImage(self, f): #8x8 block size
for y in range(int(ceil(self.height / 8.0))):
for x in range(int(ceil(self.width / 8.0))):
self.DecodeBlock(f, x * 8, y * 8)
def DecodeBlock(self, f, x, y):
for ty in range(8): #8 pixel block height
for tx in range(4): #cuz we read 2 pixels worth
data = ord(f.read(1))
color1, color2 = self.color.I4ToColor(data)
try:
self.img[x + (tx * 2), y + ty] = color1
self.img[x + (tx * 2) + 1, y + ty] = color2
except: pass
class I8_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGB", (width, height))
self.img = self.image.load()
self.width = width
self.height = height
self.color = color
def DecodeImage(self, f): #8x4 block size
for y in range(int(ceil(self.height / 4.0))):
for x in range(int(ceil(self.width / 8.0))):
self.DecodeBlock(f, x * 8, y * 4)
def DecodeBlock(self, f, x, y):
for ty in range(4): #4 pixel block height
for tx in range(8): #8 pixel block width
i = ord(f.read(1))
try: self.img[x + tx, y + ty] = (i, i, i)
except: pass
class IA4_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGBA", (width, height))
self.img = self.image.load()
self.height = height
self.width = width
self.color = color
def DecodeImage(self, f): #8x4 block size
for y in range(int(ceil(self.height / 4.0))):
for x in range(int(ceil(self.width / 8.0))):
self.DecodeBlock(f, x * 8, y * 4)
def DecodeBlock(self, f, x, y):
for ty in range(4): #4 pixel block height
for tx in range(8): #8 pixel block width
color = self.color.IA4ToColor(ord(f.read(1)))
try: self.img[x + tx, y + ty] = color
except: pass
class RGB565_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGB", (width, height))
self.img = self.image.load()
self.width = width
self.height = height
self.color = color
def DecodeImage(self, f): #4x4 block size
for y in range(int(ceil(self.height / 4.0))):
for x in range(int(ceil(self.width / 4.0))):
self.DecodeBlock(f, x * 4, y * 4)
def DecodeBlock(self, f, x, y):
for ty in range(4): #4 pixel block height
for tx in range(4): #4 pixel block width
pixel = unpack(">H", f.read(2))[0]
color = self.color.RGB565ToColor(pixel)
try: self.img[x + tx, y + ty] = color
except: pass
class RGB5A3_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGBA", (width, height))
self.img = self.image.load()
self.width = width
self.height = height
self.color = color
def DecodeImage(self, f): #4x4 block size
for y in range(int(ceil(self.height / 4.0))):
for x in range(int(ceil(self.width / 4.0))):
self.DecodeBlock(f, x * 4, y * 4)
def DecodeBlock(self, f, x, y):
for ty in range(4): #4 pixel block height
for tx in range(4): #4 pixel block width
pixel = unpack(">H", f.read(2))[0]
color = self.color.RGB5A3ToColor(pixel)
try: self.img[x + tx, y + ty] = color
except: pass
class CI8_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGBA", (width, height))
self.img = self.image.load()
self.height = height
self.width = width
self.color = color
def DecodePalette(self, f):
info = unpack(">HBBII", f.read(12))
f.seek(info[4]) #data addr
palette = []
if info[3] == 0:
pass
elif info[3] == 1:
pass
elif info[3] == 2: #RGB5A3
for i in range(info[0]): #num_entries
pixel = unpack(">H", f.read(2))[0]
color = self.color.RGB5A3ToColor(pixel)
palette.append(color)
return palette
def DecodeImage(self, f, palette):
for y in range(int(ceil(self.height / 4.0))):
for x in range(int(ceil(self.width / 8.0))):
self.DecodeBlock(f, palette, x * 8, y * 4)
def DecodeBlock(self, f, palette, x, y):
for ty in range(4):
for tx in range(8):
index = ord(f.read(1))
try: self.img[x + tx, y + ty] = palette[index]
except: pass
class RGBA32_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGBA", (width, height))
self.img = self.image.load()
self.width = width
self.height = height
self.color = color #we don't need it but add anyways
def DecodeImage(self, f):
for y in range(int(ceil(self.height / 4.0))):
for x in range(int(ceil(self.width / 4.0))):
self.DecodeBlock(f, x * 4, y * 4)
def DecodeBlock(self, f, x, y):
r = [];g = [];b = [];a = []
for i in range(16):
a.append(ord(f.read(1)))
r.append(ord(f.read(1)))
for i in range(16):
g.append(ord(f.read(1)))
b.append(ord(f.read(1)))
for ty in range(4):
for tx in range(4):
i = tx + (ty * 4)
try: self.img[x + tx, y + ty] = (r[i], g[i], b[i], a[i])
except: pass
class CMPR_Texture:
def __init__(self, height, width, color):
self.image = Image.new("RGBA", (width, height))
self.img = self.image.load()
self.width = width
self.height = height
self.color = color
def DecodeImage(self, f):
for y in range(int(ceil(self.height / 8.0))):
for x in range(int(ceil(self.width / 8.0))):
self.DecodeBlock(f, x * 8, y * 8)
def DecodeBlock(self, f, x, y):
self.DecodeTile(f.read(8), x + 0, y + 0)
self.DecodeTile(f.read(8), x + 4, y + 0)
self.DecodeTile(f.read(8), x + 0, y + 4)
self.DecodeTile(f.read(8), x + 4, y + 4)
def DecodeTile(self, data, x, y):
c1, c2 = unpack(">2H", data[0:4])
r1, g1, b1 = self.color.RGB565ToColor(c1)
r2, g2, b2 = self.color.RGB565ToColor(c2)
colors = [(), (), (), ()]
colors[0] = (r1, g1, b1, 0xFF)
colors[1] = (r2, g2, b2, 0xFF)
if c1 > c2:
r3 = ((r2 - r1) >> 1) - ((r2 - r1) >> 3)
g3 = ((g2 - g1) >> 1) - ((g2 - g1) >> 3)
b3 = ((b2 - b1) >> 1) - ((b2 - b1) >> 3)
colors[2] = (r1 + r3, g1 + g3, b1 + b3, 0xFF)
colors[3] = (r2 - r3, g2 - g3, b2 - b3, 0xFF)
else:
colors[2] = (int((r1 + r2 + 1) / 2.0),
int((g1 + g2 + 1) / 2.0),
int((b1 + b2 + 1) / 2.0),
0xFF)
colors[3] = (r2, g2, b2, 0x00)
index = unpack(">4B", data[4:])
for ty in range(4):
val = index[ty]
for tx in range(4):
color = colors[(val >> 6) & 3]
try:
self.img[x + tx, y + ty] = color
val <<= 2
except: pass
class ColorConversion:
def __init__(self):
self.Bits3To8 = self.MakeDepthConversionTable(3, 8)
self.Bits8To3 = self.MakeDepthConversionTable(8, 3)
self.Bits4To8 = self.MakeDepthConversionTable(4, 8)
self.Bits8To4 = self.MakeDepthConversionTable(8, 4)
self.Bits5To8 = self.MakeDepthConversionTable(5, 8)
self.Bits8To5 = self.MakeDepthConversionTable(8, 5)
self.Bits6To8 = self.MakeDepthConversionTable(6, 8)
self.Bits8To6 = self.MakeDepthConversionTable(8, 6)
def MakeDepthConversionTable(self, a, b):
a = 1 << a
b = 1 << b
result = []
for i in range(a):
result.append(int(float(i) / (a - 1) * (b - 1)) & 0xFF)
return result
def I4ToColor(self, v):
i1 = self.Bits4To8[v >> 4]
i2 = self.Bits4To8[v & 15]
return [(i1, i1, i1), (i2, i2, i2)]
def IA4ToColor(self, v):
i = self.Bits4To8[v & 15]
a = self.Bits4To8[v >> 4]
return (i, i, i, a)
def RGB565ToColor(self, v):
return (self.Bits5To8[(v >> 11) & 31],
self.Bits6To8[(v >> 5) & 63],
self.Bits5To8[(v >> 0) & 31])
def RGB5A3ToColor(self, v):
if v & 0x8000: #RGB555
return (self.Bits5To8[(v >> 10) & 31],
self.Bits5To8[(v >> 5) & 31],
self.Bits5To8[(v >> 0) & 31],
0xFF)
else: #RGB4A3
return (self.Bits4To8[(v >> 8) & 15],
self.Bits4To8[(v >> 4) & 15],
self.Bits4To8[(v >> 0) & 15],
self.Bits3To8[(v >> 12) & 7])
if __name__ == "__main__":
with open(sys.argv[1], "rb") as f:
name = sys.argv[1].split(".")[:-1]
name = ".".join(name)
imginfo = unpack(">2B2H2B2H2I2BH2BHI", f.read(0x20))
#we're now at 0x20 aka start of raw data
color = ColorConversion()
if imginfo[0] == 0:
tex = I4_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
elif imginfo[0] == 1:
tex = I8_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
elif imginfo[0] == 2:
tex = IA4_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
elif imginfo[0] == 4:
tex = RGB565_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
elif imginfo[0] == 5:
tex = RGB5A3_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
elif imginfo[0] == 6:
tex = RGBA32_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
elif imginfo[0] == 7:
tex = RGB565_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
#TODO: double check these:
elif imginfo[0] == 9:
tex = CI8_Texture(imginfo[3], imginfo[2], color)
assert imginfo[8] != 0 #palette
f.seek(imginfo[8])
palette = tex.DecodePalette(f)
tex.DecodeImage(f, palette)
tex.image.save("%s.png" % name)
elif imginfo[0] == 14:
tex = CMPR_Texture(imginfo[3], imginfo[2], color)
tex.DecodeImage(f)
tex.image.save("%s.png" % name)
Comments