From 4ec15b1bc9a4179dbc60e7ca481fff68beed3768 Mon Sep 17 00:00:00 2001 From: megazig Date: Mon, 26 Oct 2009 05:22:08 -0500 Subject: [PATCH] updated image.py to one from someone on wadder.net forum --- image.py | 1662 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 862 insertions(+), 800 deletions(-) diff --git a/image.py b/image.py index ccde7c2..fb18685 100644 --- a/image.py +++ b/image.py @@ -1,800 +1,862 @@ -from common import * - -def flatten(myTuple): - if (len(myTuple) == 4): - return myTuple[0] << 0 | myTuple[1] << 8 | myTuple[2] << 16 | myTuple[3] << 24 - else: - return myTuple[0] << 0 | myTuple[1] << 8 | myTuple[2] << 16 | 0xff << 24 - -def round_up(x, n): - left = x % n - return x + left - -def avg(w0, w1, c0, c1): - a0 = c0 >> 11 - a1 = c1 >> 11 - a = (w0*a0 + w1*a1) / (w0 + w1) - c = (a << 11) & 0xffff - - a0 = (c0 >> 5) & 63 - a1 = (c1 >> 5) & 63 - a = (w0*a0 + w1*a1) / (w0 + w1) - c = c | ((a << 5) & 0xffff) - - a0 = c0 & 31 - a1 = c1 & 31 - a = (w0*a0 + w1*a1) / (w0 + w1) - c = c | a - - return c - - -class TPL: - """This is the class to generate TPL texutres from PNG images, and to convert TPL textures to PNG images. The parameter file specifies the filename of the source, either a PNG image or a TPL image. - - Currently supported are the following formats to convert from TPL (all formats): RGBA8, RGB565, RGB5A3, I4, IA4, I8, IA8, CI4, CI8, CMP, CI14X2. - - Currently supported to convert to TPL: I4, I8, IA4, IA8, RBG565, RBGA8, RGB5A3. Currently not supported are CI4, CI8, CMP, CI14X2.""" - - - class TPLHeader(Struct): - __endian__ = Struct.BE - def __format__(self): - self.magic = Struct.uint32 - self.ntextures = Struct.uint32 - self.header_size = Struct.uint32 - class TPLTexture(Struct): - __endian__ = Struct.BE - def __format__(self): - self.header_offset = Struct.uint32 - self.palette_offset = Struct.uint32 - class TPLTextureHeader(Struct): - __endian__ = Struct.BE - def __format__(self): - self.height = Struct.uint16 - self.width = Struct.uint16 - self.format = Struct.uint32 - self.data_off = Struct.uint32 - self.wrap = Struct.uint32[2] - self.filter = Struct.uint32[2] - self.lod_bias = Struct.float - self.edge_lod = Struct.uint8 - self.min_lod = Struct.uint8 - self.max_lod = Struct.uint8 - self.unpacked = Struct.uint8 - class TPLPaletteHeader(Struct): - __endian__ = Struct.BE - def __format__(self): - self.nitems = Struct.uint16 - self.unpacked = Struct.uint8 - self.pad = Struct.uint8 - self.format = Struct.uint32 - self.offset = Struct.uint32 - def __init__(self, file): - if(not ("\x00" in file) and os.path.isfile(file)): - self.file = file - self.data = None - else: - self.file = None - self.data = file - def toTPL(self, outfile, (width, height) = (None, None), format = "RGBA8"): #single texture only - """This converts an image into a TPL. The image is specified as the file parameter to the class initializer, while the output filename is specified here as the parameter outfile. Width and height are optional parameters and specify the size to resize the image to, if needed. Returns the output filename. - - This only can create TPL images with a single texture.""" - head = self.TPLHeader() - head.magic = 0x0020AF30 - head.ntextures = 1 - head.header_size = 0x0C - - tex = self.TPLTexture() - tex.header_offset = 0x14 - tex.pallete_offset = 0 - - img = Image.open(self.file) - theWidth, theHeight = img.size - if(width != None and height != None and (width != theWidth or height != theHeight)): - img = img.resize((width, height), Image.ANTIALIAS) - w, h = img.size - - texhead = self.TPLTextureHeader() - texhead.height = h - texhead.width = w - if format == "I4": - texhead.format = 0 - tpldata = self.toI4((w, h), img) - elif format == "I8": - texhead.format = 1 - tpldata = self.toI8((w, h), img) - elif format == "IA4": - texhead.format = 2 - tpldata = self.toIA4((w, h), img) - elif format == "IA8": - texhead.format = 3 - tpldata = self.toIA8((w, h), img) - elif format == "RGB565": - texhead.format = 4 - tpldata = self.toRGB565((w, h), img) - elif format == "RGB5A3": - texhead.format = 5 - tpldata = self.toRGB5A3((w, h), img) - elif format == "RGBA8": - texhead.format = 6 - tpldata = self.toRGBA8((w, h), img) - elif format == "CI4": - texhead.format = 8 - ''' ADD toCI4 ''' - raise Exception("toCI4 not done") - #tpldata = self.toCI4((w, h), img) - elif format == "CI8": - texhead.format = 9 - ''' ADD toCI8 ''' - raise Exception("toCI8 not done") - #tpldata = self.toCI8((w, h), img) - elif format == "CI14X2": - texhead.format = 10 - ''' ADD toCI14X2 ''' - raise Exception("toCI14X2 not done") - #tpldata = self.toCI14X2((w, h), img) - elif format == "CMP": - texhead.format = 14 - ''' ADD toCMP ''' - raise Exception("toCMP not done") - #tpldata = self.toCMP((w, h), img) - - texhead.data_off = 0x14 + len(texhead) - texhead.wrap = [0, 0] - texhead.filter = [1, 1] - texhead.lod_bias = 0 - texhead.edge_lod = 0 - texhead.min_lod = 0 - texhead.max_lod = 0 - texhead.unpacked = 0 - - f = open(outfile, "wb") - f.write(head.pack()) - f.write(tex.pack()) - f.write(texhead.pack()) - if format == "I4": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) / 2) + "B", *tpldata)) - if format == "I8": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "B", *tpldata)) - if format == "IA4": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "B", *tpldata)) - if format == "IA8": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "H", *tpldata)) - if format == "RGB565": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "H", *tpldata)) - if format == "RGB5A3": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "H", *tpldata)) - if format == "RGBA8": - f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) - if format == "CI4": - ''' ADD toCI4 ''' - #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) - if format == "CI8": - ''' ADD toCI8 ''' - #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) - if format == "CI14X2": - ''' ADD toCI14X2 ''' - #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) - if format == "CMP": - ''' ADD toCMP ''' - #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) - f.close() - - return outfile - def toI4(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4) / 2)] - outp = 0 - inp = list(img.getdata()) - for y1 in range(0, h, 8): - for x1 in range(0, w, 8): - for y in range(y1, y1+8, 1): - for x in range(x1, x1+8, 2): - if x>=w or y>=h: - newpixel = 0 - else: - rgba = flatten(inp[x+y*w]) - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - i1 = ((r + g + b) / 3) & 0xff - rgba = flatten(inp[x+1+y*w]) - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - i2 = ((r + g + b) / 3) & 0xff - - newpixel = (((i1 * 15) / 255) << 4) - newpixel |= (((i2 * 15) / 255) & 0xf) - out[outp] = newpixel - outp += 1 - return out - def toI8(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4))] - outp = 0 - inp = list(img.getdata()) - for y1 in range(0, h, 4): - for x1 in range(0, w, 8): - for y in range(y1, y1+4, 1): - for x in range(x1, x1+8, 1): - rgba = flatten(inp[x + (y * w)]) - if x>= w or y>=h: - i1 = 0 - else: - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - i1 = ((r + g + b) / 3) & 0xff - out[outp] = i1 - outp += 1 - return out - def toIA4(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4))] - outp = 0 - inp = list(img.getdata()) - for y1 in range(0, h, 4): - for x1 in range(0, w, 8): - for y in range(y1, y1+4, 1): - for x in range(x1, x1+8, 1): - if x>=w or y>=h: - newpixel = 0 - else: - rgba = flatten(inp[x + (y * w)]) - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - i1 = ((r + g + b) / 3) & 0xff - a1 = (rgba >> 24) & 0xff - - newpixel = (((i1 * 15) / 255) & 0xf) - newpixel = newpixel | (((a1 * 15) / 255) << 4) - out[outp] = newpixel - outp += 1 - return out - def toIA8(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4))] - outp = 0 - inp = list(img.getdata()) - for y1 in range(0, h, 4): - for x1 in range(0, w, 4): - for y in range(y1, y1+4, 1): - for x in range(x1, x1+4, 1): - if x>=w or y>=h: - newpixel = 0 - else: - rgba = flatten(inp[x + (y * w)]) - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - i1 = ((r + g + b) / 3) & 0xff - a1 = (rgba >> 24) & 0xff - - newpixel = i1 << 8 - newpixel = newpixel | a1 - out[outp] = newpixel - outp += 1 - return out - def toRGB565(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4))] - outp = 0 - inp = img.getdata() - for y1 in range(0, h, 4): - for x1 in range(0, w, 4): - for y in range(y1, y1+4, 1): - for x in range(x1, x1+4, 1): - newpixel = 0 - if x>=w or y>=h: - newpixel = 0 - else: - rgba = flatten(inp[x+y*w]) - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - newpixel = ((b >>3) << 11) | ((g >>2) << 5) | ((r >>3) << 0) - out[outp] = newpixel - outp += 1 - return out - def toRGB5A3(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4))] - outp = 0 - inp = list(img.getdata()) - for y1 in range(0, h, 4): - for x1 in range(0, w, 4): - for y in range(y1, y1+4, 1): - for x in range(x1, x1+4, 1): - newpixel = 0 - if x>=w or y>=h: - newpixel = 0 - else: - rgba = flatten(inp[x + (y * h)]) - r = (rgba >> 0) & 0xff - g = (rgba >> 8) & 0xff - b = (rgba >> 16) & 0xff - a = (rgba >> 24) & 0xff - if (a <= 0xda): - newpixel &= ~(1 << 15) - r = ((r * 15) / 255) & 0xf - g = ((g * 15) / 255) & 0xf - b = ((b * 15) / 255) & 0xf - a = ((a * 7) / 255) & 0x7 - #newpixel |= r << 12 - #newpixel |= g << 8 - #newpixel |= b << 4 - #newpixel |= a << 0 - newpixel |= a << 12 - newpixel |= b << 8 - newpixel |= g << 4 - newpixel |= r << 0 - else: - newpixel |= (1 << 15) - r = ((r * 31) / 255) & 0x1f - g = ((g * 31) / 255) & 0x1f - b = ((b * 31) / 255) & 0x1f - newpixel |= b << 10 - newpixel |= g << 5 - newpixel |= r << 0 - out[outp] = newpixel - outp += 1 - return out - def toRGBA8(self, (w, h), img): - out = [0 for i in range(align(w, 4) * align(h, 4) * 4)] - inp = list(img.getdata()) - iv = 0 - z = 0 - lr = [0 for i in range(32)] - lg = [0 for i in range(32)] - lb = [0 for i in range(32)] - la = [0 for i in range(32)] - for y1 in range(0, h, 4): - for x1 in range(0, w, 4): - for y in range(y1, y1 + 4, 1): - for x in range(x1, x1 + 4, 1): - if(y >= h or x >= w): - lr[z] = 0 - lg[z] = 0 - lb[z] = 0 - la[z] = 0 - else: - rgba = flatten(inp[x + (y * w)]) - lr[z] = (rgba >> 0) & 0xff - lg[z] = (rgba >> 8) & 0xff - lb[z] = (rgba >> 16) & 0xff - la[z] = (rgba >> 24) & 0xff - z += 1 - if(z == 16): - for i in range(16): - out[iv] = la[i] & 0xff - iv += 1 - out[iv] = lr[i] & 0xff - iv += 1 - for i in range(16): - out[iv] = lg[i] & 0xff - iv += 1 - out[iv] = lb[i] & 0xff - iv += 1 - z = 0 - return out - def toImage(self, outfile): - """This converts a TPL texture to a PNG image. You specify the input TPL filename in the initializer, and you specify the output filename in the outfile parameter to this method. Returns the output filename. - - This only supports single textured TPL images.""" - if(self.file): - data = open(self.file, "rb").read() - else: - data = self.data - - header = self.TPLHeader() - textures = [] - pos = 0 - - header.unpack(data[pos:pos + len(header)]) - pos += len(header) - - palette_offsets = [] - - for i in range(header.ntextures): - tmp = self.TPLTexture() - tmp.unpack(data[pos:pos + len(tmp)]) - textures.append(tmp) - pos += len(tmp) - if(tmp.palette_offset > 0): - palette_offsets.append(tmp.palette_offset) - - if(header.ntextures > 1): - raise ValueError("Only one texture supported. Don't touch me!") - - for i in range(header.ntextures): - head = textures[i] - tex = self.TPLTextureHeader() - tex.unpack(data[head.header_offset:head.header_offset + len(tex)]) - w = tex.width - h = tex.height - print tex.format - if(tex.format == 0): #I4, 4-bit - tpldata = struct.unpack(">" + str((w * h) / 2) + "B", data[tex.data_off:tex.data_off + ((w * h) / 2)]) - rgbdata = self.I4((w, h), tpldata) - - elif(tex.format == 1): #I8, 8-bit - tpldata = struct.unpack(">" + str(w * h) + "B", data[tex.data_off:tex.data_off + (w * h * 1)]) - rgbdata = self.I8((w, h), tpldata) - elif(tex.format == 2): #IA4, 8-bit - tpldata = struct.unpack(">" + str(w * h) + "B", data[tex.data_off:tex.data_off + (w * h * 1)]) - rgbdata = self.IA4((w, h), tpldata) - - elif(tex.format == 4): #RGB565, 16-bit - tpldata = data[tex.data_off:] - rgbdata = self.RGB565((w, h), tpldata) - elif(tex.format == 5): #RGB5A3, 16-bit - tpldata = data[tex.data_off:] - rgbdata = self.RGB5A3((w, h), tpldata) - elif(tex.format == 3): #IA8, 16-bit - tpldata = data[tex.data_off:] - rgbdata = self.IA8((w, h), tpldata) - - elif(tex.format == 6): #RGBA8, 32-bit, but for easyness's sake lets do it with 16-bit - tpldata = data[tex.data_off:] - rgbdata = self.RGBA8((w, h), tpldata) - - elif(tex.format == 8 or tex.format == 9 or tex.format == 10): - palhead = self.TPLPaletteHeader() - offs = palette_offsets.pop(0) - palhead.unpack(data[offs:offs + len(palhead)]) - - tpldata = struct.unpack(">" + str(palhead.nitems) + "H", data[palhead.offset:palhead.offset + (palhead.nitems * 2)]) - if(palhead.format == 0): - palette_data = self.IA8((palhead.nitems, 1), tpldata)[0] - elif(palhead.format == 1): - palette_data = self.RGB565((palhead.nitems, 1), tpldata)[0] - elif(palhead.format == 2): - palette_data = self.RGB5A3((palhead.nitems, 1), tpldata)[0] - - paldata = [] - for i in range(0, palhead.nitems * 4, 4): - tmp = 0 - tmp |= palette_data[i + 0] << 24 - tmp |= palette_data[i + 1] << 16 - tmp |= palette_data[i + 2] << 8 - tmp |= palette_data[i + 3] << 0 - paldata.append(tmp) - - if(tex.format == 8): - tpldata = struct.unpack(">" + str((w * h) / 2) + "B", data[tex.data_off:tex.data_off + ((w * h) / 2)]) - rgbdata = self.CI4((w, h), tpldata, paldata) - if(tex.format == 9): - tpldata = struct.unpack(">" + str(w * h) + "B", data[tex.data_off:tex.data_off + (w * h * 1)]) - rgbdata = self.CI8((w, h), tpldata, paldata) - if(tex.format == 10): - tpldata = struct.unpack(">" + str(w * h) + "H", data[tex.data_off:tex.data_off + (w * h * 2)]) - rgbdata = self.CI14X2((w, h), tpldata, paldata) - elif(tex.format == 14): - tpldata = ''.join(data[tex.data_off:]) - - rgbdata = self.CMP((w, h), tpldata) - else: - raise TypeError("Unsupported TPL Format: " + str(tex.format)) - - output = Image.fromstring("RGBA", (w, h), rgbdata) - ext = outfile[outfile.rfind(".")+1:] - output.save(outfile, ext) - - return outfile - def getSizes(self): - """This returns a tuple containing the width and height of the TPL image filename in the class initializer. Will only return the size of single textured TPL images.""" - if(self.file): - data = open(self.file, "rb").read() - else: - data = self.data - - header = self.TPLHeader() - textures = [] - pos = 0 - - header.unpack(data[pos:pos + len(header)]) - pos += len(header) - - for i in range(header.ntextures): - tmp = self.TPLTexture() - tmp.unpack(data[pos:pos + len(tmp)]) - textures.append(tmp) - pos += len(tmp) - - for i in range(header.ntextures): - head = textures[i] - tex = self.TPLTextureHeader() - tex.unpack(data[head.header_offset:head.header_offset + len(tex)]) - w = tex.width - h = tex.height - return (w, h) - def toScreen(self): #single texture only - import wx - class imp(wx.Dialog): - def __init__(self, title, im): - w = img.GetWidth() - h = img.GetHeight() - - wx.Dialog.__init__(self, None, -1, title, size = (max(w, 300), max(h, 200))) - - wx.StaticBitmap(self, -1, im, ( ((max(w, 300) - w) / 2), ((max(h, 200) - h) / 2) ), (w, h)) - self.toImage("tmp.png") - img = wx.Image("tmp.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap() - w = img.GetWidth() - h = img.GetHeight() - dialog = imp("TPL (" + str(w) + ", " + str(h) + ")", img) - dialog.ShowModal() - dialog.Destroy() - os.unlink("tmp.png") - def RGBA8(self, (x, y), data): - out = [0 for i in range(x * y)] - inp = 0 - for i in xrange(0, y, 4): - for j in xrange(0, x, 4): - for k in xrange(2): - for l in xrange(i, i + 4, 1): - for m in xrange(j, j + 4, 1): - texel = Struct.uint16(data[inp * 2:inp * 2 + 2], endian = '>') - inp += 1 - if (m >= x) or (l >= y): - continue - if k == 0: - a = (texel >> 8) & 0xff - r = (texel >> 0) & 0xff - out[m + (l * x)] |= ((r << 0) | (a << 24)) - else: - g = (texel >> 8) & 0xff - b = (texel >> 0) & 0xff - out[m + (l * x)] |= ((g << 8) | (b << 16)) - return ''.join(Struct.uint32(p) for p in out) - def RGB5A3(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 4): - for y1 in range(y, y + 4): - for x1 in range(x, x + 4): - if(y1 >= h or x1 >= w): - continue - pixel = Struct.uint16(jar[i * 2:i * 2 + 2], endian='>') - i += 1 - - if(pixel & (1 << 15)): #RGB555 - b = (((pixel >> 10) & 0x1F) * 255) / 31 - g = (((pixel >> 5) & 0x1F) * 255) / 31 - r = (((pixel >> 0) & 0x1F) * 255) / 31 - a = 255 - else: #RGB4A3 - a = (((pixel >> 12) & 0x07) * 255) / 7 - b = (((pixel >> 8) & 0x0F) * 255) / 15 - g = (((pixel >> 4) & 0x0F) * 255) / 15 - r = (((pixel >> 0) & 0x0F) * 255)/ 15 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[(y1 * w) + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def RGB565(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 4): - for y1 in range(y, y + 4): - for x1 in range(x, x + 4): - if(y1 >= h or x1 >= w): - continue - pixel = Struct.uint16(jar[i * 2:i * 2 + 2], endian='>') - i += 1 - - b = (((pixel >> 11) & 0x1F) << 3) & 0xff - g = (((pixel >> 5) & 0x3F) << 2) & 0xff - r = (((pixel >> 0) & 0x1F) << 3) & 0xff - a = 255 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def I4(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 8): - for x in range(0, w, 8): - for y1 in range(y, y + 8): - for x1 in range(x, x + 8, 2): - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - - r = (pixel >> 4) * 255 / 15 - g = (pixel >> 4) * 255 / 15 - b = (pixel >> 4) * 255 / 15 - a = (pixel >> 4) * 255 / 15 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - i += 1 - - r = (pixel & 0x0F) * 255 / 15 - g = (pixel & 0x0F) * 255 / 15 - b = (pixel & 0x0F) * 255 / 15 - a = (pixel & 0x0F) * 255 / 15 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1 + 1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def IA4(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 8): - for y1 in range(y, y + 4): - for x1 in range(x, x + 8): - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - i += 1 - - r = ((pixel & 0x0F) * 255 / 15) & 0xff - g = ((pixel & 0x0F) * 255 / 15) & 0xff - b = ((pixel & 0x0F) * 255 / 15) & 0xff - a = (((pixel >> 4) * 255) / 15) & 0xff - - rgba = ( r<< 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def I8(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 8): - for y1 in range(y, y + 4): - for x1 in range(x, x + 8): - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - i += 1 - - r = pixel - g = pixel - b = pixel - a = 255 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def IA8(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 4): - for y1 in range(y, y + 4): - for x1 in range(x, x + 4): - if(y1 >= h or x1 >= w): - continue - pixel = Struct.uint16(jar[i * 2:i * 2 + 2], endian='>') - i += 1 - - r = (pixel >> 8) & 0xff - g = (pixel >> 8) & 0xff - b = (pixel >> 8) & 0xff - a = pixel & 0xff - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def CI4(self, (w, h), jar, pal): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 8): - for x in range(0, w, 8): - for y1 in range(y, y + 8): - for x1 in range(x, x + 8, 2): - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - - r = (pal[pixel] & 0xFF000000) >> 24 - g = (pal[pixel] & 0x00FF0000) >> 16 - b = (pal[pixel] & 0x0000FF00) >> 8 - a = (pal[pixel] & 0x000000FF) >> 0 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - i += 1 - - r = (pal[pixel] & 0xFF000000) >> 24 - g = (pal[pixel] & 0x00FF0000) >> 16 - b = (pal[pixel] & 0x0000FF00) >> 8 - a = (pal[pixel] & 0x000000FF) >> 0 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1 + 1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def CI8(self, (w, h), jar, pal): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 8): - for y1 in range(y, y + 4): - for x1 in range(x, x + 8): - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - i += 1 - - r = (pal[pixel] & 0xFF000000) >> 24 - g = (pal[pixel] & 0x00FF0000) >> 16 - b = (pal[pixel] & 0x0000FF00) >> 8 - a = (pal[pixel] & 0x000000FF) >> 0 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) - def CMP(self, (w, h), data): - temp = [0 for i in range(w * h)] - pix = [ 0 , 0 , 0 ] - c = [ 0 , 0 , 0 , 0 ] - outp = 0 - for y in xrange(h): - for x in xrange(w): - ww = round_up(w, 8) - - x0 = x & 0x03 - x1 = (x >> 2) & 0x01 - x2 = x >> 3 - - y0 = y & 0x03 - y1 = (y >> 2) & 0x01 - y2 = y >> 3 - - off = (8 * x1) + (16 * y1) + (32 * x2) + (4 * ww * y2) - - c[0] = Struct.uint16(data[off + 0:off + 2], endian='>') - c[1] = Struct.uint16(data[off + 2:off + 4], endian='>') - if(c[0] > c[1]): - c[2] = avg(2, 1, c[0], c[1]) - c[3] = avg(1, 2, c[0], c[1]) - else: - c[2] = avg(1, 1, c[0], c[1]) - c[3] = 0 - - px = Struct.uint32(data[off+4:off + 8], endian='>') - ix = x0 + ( 4 * y0 ) - raw = c[(px >> (30 - (2 * ix))) & 0x03] - - pix[0] = (raw >> 8) & 0xf8 - pix[1] = (raw >> 3) & 0xf8 - pix[2] = (raw << 3) & 0xf8 - - temp[outp] = (pix[0] <<0) | (pix[1] << 8) | (pix[2] << 16) | (255 << 24) - outp += 1 - return ''.join(Struct.uint32(p) for p in temp) - def CI14X2(self, (w, h), jar): - out = [0 for i in range(w * h)] - i = 0 - for y in range(0, h, 4): - for x in range(0, w, 4): - for y1 in range(y, y + 4): - for x1 in range(x, x + 4): - if(y1 >= h or x1 >= w): - continue - pixel = jar[i] - i += 1 - - r = (pal[pixel & 0x3FFF] & 0xFF000000) >> 24 - g = (pal[pixel & 0x3FFF] & 0x00FF0000) >> 16 - b = (pal[pixel & 0x3FFF] & 0x0000FF00) >> 8 - a = (pal[pixel & 0x3FFF] & 0x000000FF) >> 0 - - rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) - out[y1 * w + x1] = rgba - return ''.join(Struct.uint32(p) for p in out) -if __name__=='__main__': - app = wx.PySimpleApp() - app.MainLoop() - TPL(*sys.argv[1:]).toScreen() +from common import * + +def flatten(myTuple): + if (len(myTuple) == 4): + return myTuple[0] << 0 | myTuple[1] << 8 | myTuple[2] << 16 | myTuple[3] << 24 + else: + return myTuple[0] << 0 | myTuple[1] << 8 | myTuple[2] << 16 | 0xff << 24 + +def round_up(x, n): + left = x % n + return x + left + +def avg(w0, w1, c0, c1): + a0 = c0 >> 11 + a1 = c1 >> 11 + a = (w0*a0 + w1*a1) / (w0 + w1) + c = (a << 11) & 0xffff + + a0 = (c0 >> 5) & 63 + a1 = (c1 >> 5) & 63 + a = (w0*a0 + w1*a1) / (w0 + w1) + c = c | ((a << 5) & 0xffff) + + a0 = c0 & 31 + a1 = c1 & 31 + a = (w0*a0 + w1*a1) / (w0 + w1) + c = c | a + + return c + + +class TPL: + """This is the class to generate TPL texutres from PNG images, and to convert TPL textures to PNG images. The parameter file specifies the filename of the source, either a PNG image or a TPL image. + + Currently supported are the following formats to convert from TPL (all formats): RGBA8, RGB565, RGB5A3, I4, IA4, I8, IA8, CI4, CI8, CMP, CI14X2. + + Currently supported to convert to TPL: I4, I8, IA4, IA8, RBG565, RBGA8, RGB5A3. Currently not supported are CI4, CI8, CMP, CI14X2.""" + + + class TPLHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.magic = Struct.uint32 + self.ntextures = Struct.uint32 + self.header_size = Struct.uint32 + class TPLTexture(Struct): + __endian__ = Struct.BE + def __format__(self): + self.header_offset = Struct.uint32 + self.palette_offset = Struct.uint32 + class TPLTextureHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.height = Struct.uint16 + self.width = Struct.uint16 + self.format = Struct.uint32 + self.data_off = Struct.uint32 + self.wrap = Struct.uint32[2] + self.filter = Struct.uint32[2] + self.lod_bias = Struct.float + self.edge_lod = Struct.uint8 + self.min_lod = Struct.uint8 + self.max_lod = Struct.uint8 + self.unpacked = Struct.uint8 + class TPLPaletteHeader(Struct): + __endian__ = Struct.BE + def __format__(self): + self.nitems = Struct.uint16 + self.unpacked = Struct.uint8 + self.pad = Struct.uint8 + self.format = Struct.uint32 + self.offset = Struct.uint32 + def __init__(self, file): + if(not ("\x00" in file) and os.path.isfile(file)): + self.file = file + self.data = None + else: + self.file = None + self.data = file + def toTPL(self, outfile, (width, height) = (None, None), format = "RGBA8"): #single texture only + """This converts an image into a TPL. The image is specified as the file parameter to the class initializer, while the output filename is specified here as the parameter outfile. Width and height are optional parameters and specify the size to resize the image to, if needed. Returns the output filename. + + This only can create TPL images with a single texture.""" + head = self.TPLHeader() + head.magic = 0x0020AF30 + head.ntextures = 1 + head.header_size = 0x0C + + tex = self.TPLTexture() + tex.header_offset = 0x14 + tex.pallete_offset = 0 + + img = Image.open(self.file) + theWidth, theHeight = img.size + if(width != None and height != None and (width != theWidth or height != theHeight)): + img = img.resize((width, height), Image.ANTIALIAS) + w, h = img.size + + texhead = self.TPLTextureHeader() + texhead.height = h + texhead.width = w + if format == "I4": + texhead.format = 0 + tpldata = self.toI4((w, h), img) + elif format == "I8": + texhead.format = 1 + tpldata = self.toI8((w, h), img) + elif format == "IA4": + texhead.format = 2 + tpldata = self.toIA4((w, h), img) + elif format == "IA8": + texhead.format = 3 + tpldata = self.toIA8((w, h), img) + elif format == "RGB565": + texhead.format = 4 + tpldata = self.toRGB565((w, h), img) + elif format == "RGB5A3": + texhead.format = 5 + tpldata = self.toRGB5A3((w, h), img) + elif format == "RGBA8": + texhead.format = 6 + tpldata = self.toRGBA8((w, h), img) + elif format == "CI4": + texhead.format = 8 + ''' ADD toCI4 ''' + raise Exception("toCI4 not done") + #tpldata = self.toCI4((w, h), img) + elif format == "CI8": + texhead.format = 9 + ''' ADD toCI8 ''' + raise Exception("toCI8 not done") + #tpldata = self.toCI8((w, h), img) + elif format == "CI14X2": + texhead.format = 10 + ''' ADD toCI14X2 ''' + raise Exception("toCI14X2 not done") + #tpldata = self.toCI14X2((w, h), img) + elif format == "CMP": + texhead.format = 14 + ''' ADD toCMP ''' + raise Exception("toCMP not done") + #tpldata = self.toCMP((w, h), img) + + texhead.data_off = 0x14 + len(texhead) + texhead.wrap = [0, 0] + texhead.filter = [1, 1] + texhead.lod_bias = 0 + texhead.edge_lod = 0 + texhead.min_lod = 0 + texhead.max_lod = 0 + texhead.unpacked = 0 + + f = open(outfile, "wb") + f.write(head.pack()) + f.write(tex.pack()) + f.write(texhead.pack()) + if format == "I4": + f.write(struct.pack(">" + str(align(w,8) * align(h,8) / 2) + "B", *tpldata)) + if format == "I8": + f.write(struct.pack(">" + str(align(w,8) * align(h,4) * 1) + "B", *tpldata)) + if format == "IA4": + f.write(struct.pack(">" + str(align(w,8) * align(h,4) * 1) + "B", *tpldata)) + if format == "IA8": + f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "H", *tpldata)) + if format == "RGB565": + f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "H", *tpldata)) + if format == "RGB5A3": + f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 1) + "H", *tpldata)) + if format == "RGBA8": + f.write(struct.pack(">" + str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) + if format == "CI4": + ''' ADD toCI4 ''' + #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) + if format == "CI8": + ''' ADD toCI8 ''' + #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) + if format == "CI14X2": + ''' ADD toCI14X2 ''' + #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) + if format == "CMP": + ''' ADD toCMP ''' + #f.write(struct.pack(">"+ str(align(w,4) * align(h,4) * 4) + "B", *tpldata)) + f.close() + + return outfile + def toI4(self, (w, h), img): + out = [0 for i in xrange(align(w, 8) * align(h, 8) / 2)] + outp = 0 + inp = list(img.getdata()) + for y1 in xrange(0, h, 8): + for x1 in xrange(0, w, 8): + for y in xrange(y1, y1+8, 1): + for x in xrange(x1, x1+8, 2): + if x>=w or y>=h: + newpixel = 0 + else: + rgba = flatten(inp[x+y*w]) + r = (rgba >> 0) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 16) & 0xff + i1 = ((r + g + b) / 3) & 0xff + rgba = flatten(inp[x+1+y*w]) + r = (rgba >> 0) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 16) & 0xff + i2 = ((r + g + b) / 3) & 0xff + + newpixel = (((i1 * 15) / 255) << 4) + newpixel |= (((i2 * 15) / 255) & 0xf) + out[outp] = newpixel + outp += 1 + return out + def toI8(self, (w, h), img): + out = [0 for i in xrange(align(w, 8) * align(h, 4))] + outp = 0 + inp = list(img.getdata()) + for y1 in xrange(0, h, 4): + for x1 in xrange(0, w, 8): + for y in xrange(y1, y1+4, 1): + for x in xrange(x1, x1+8, 1): + if x>= w or y>=h: + i1 = 0 + else: + rgba = flatten(inp[x + (y * w)]) + r = (rgba >> 0) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 16) & 0xff + i1 = ((r + g + b) / 3) & 0xff + out[outp] = i1 + outp += 1 + return out + def toIA4(self, (w, h), img): + out = [0 for i in xrange(align(w, 8) * align(h, 4))] + outp = 0 + inp = list(img.getdata()) + for y1 in xrange(0, h, 4): + for x1 in xrange(0, w, 8): + for y in xrange(y1, y1+4, 1): + for x in xrange(x1, x1+8, 1): + if x>=w or y>=h: + newpixel = 0 + else: + rgba = flatten(inp[x + (y * w)]) + r = (rgba >> 0) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 16) & 0xff + i1 = ((r + g + b) / 3) & 0xff + a1 = (rgba >> 24) & 0xff + + newpixel = (((i1 * 15) / 255) & 0xf) + newpixel = newpixel | (((a1 * 15) / 255) << 4) + out[outp] = newpixel + outp += 1 + return out + def toIA8(self, (w, h), img): + out = [0 for i in xrange(align(w, 4) * align(h, 4))] + outp = 0 + inp = list(img.getdata()) + for y1 in xrange(0, h, 4): + for x1 in xrange(0, w, 4): + for y in xrange(y1, y1+4, 1): + for x in xrange(x1, x1+4, 1): + if x>=w or y>=h: + newpixel = 0 + else: + rgba = flatten(inp[x + (y * w)]) + r = (rgba >> 0) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 16) & 0xff + i1 = ((r + g + b) / 3) & 0xff + a1 = (rgba >> 24) & 0xff + + newpixel = i1 << 8 + newpixel = newpixel | a1 + out[outp] = newpixel + outp += 1 + return out + def toRGB565(self, (w, h), img): + out = [0 for i in xrange(align(w, 4) * align(h, 4))] + outp = 0 + inp = img.getdata() + for y1 in xrange(0, h, 4): + for x1 in xrange(0, w, 4): + for y in xrange(y1, y1+4, 1): + for x in xrange(x1, x1+4, 1): + newpixel = 0 + if x>=w or y>=h: + newpixel = 0 + else: + rgba = flatten(inp[x+y*w]) + r = (rgba >> 16) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 0) & 0xff + newpixel = ((b >>3) << 11) | ((g >>2) << 5) | ((r >>3) << 0) + out[outp] = newpixel + outp += 1 + return out + def toRGB5A3(self, (w, h), img): + out = [0 for i in xrange(align(w, 4) * align(h, 4))] + outp = 0 + inp = list(img.getdata()) + for y1 in xrange(0, h, 4): + for x1 in xrange(0, w, 4): + for y in xrange(y1, y1+4, 1): + for x in xrange(x1, x1+4, 1): + newpixel = 0 + if x>=w or y>=h: + newpixel = 0 + else: + rgba = flatten(inp[x + (y * w)]) + r = (rgba >> 0) & 0xff + g = (rgba >> 8) & 0xff + b = (rgba >> 16) & 0xff + a = (rgba >> 24) & 0xff + if (a <= 0xda): + newpixel &= ~(1 << 15) + r = ((r * 15) / 255) & 0xf + g = ((g * 15) / 255) & 0xf + b = ((b * 15) / 255) & 0xf + a = ((a * 7) / 255) & 0x7 + #newpixel |= r << 12 + #newpixel |= g << 8 + #newpixel |= b << 4 + #newpixel |= a << 0 + newpixel |= a << 12 + newpixel |= b << 0 + newpixel |= g << 4 + newpixel |= r << 8 + else: + newpixel |= (1 << 15) + r = ((r * 31) / 255) & 0x1f + g = ((g * 31) / 255) & 0x1f + b = ((b * 31) / 255) & 0x1f + newpixel |= b << 0 + newpixel |= g << 5 + newpixel |= r << 10 + out[outp] = newpixel + outp += 1 + return out + def toRGBA8(self, (w, h), img): + out = [0 for i in xrange(align(w, 4) * align(h, 4) * 4)] + inp = list(img.getdata()) + iv = 0 + z = 0 + lr = [0 for i in xrange(32)] + lg = [0 for i in xrange(32)] + lb = [0 for i in xrange(32)] + la = [0 for i in xrange(32)] + for y1 in xrange(0, h, 4): + for x1 in xrange(0, w, 4): + for y in xrange(y1, y1 + 4, 1): + for x in xrange(x1, x1 + 4, 1): + if(y >= h or x >= w): + lr[z] = 0 + lg[z] = 0 + lb[z] = 0 + la[z] = 0 + else: + rgba = flatten(inp[x + (y * w)]) + lr[z] = (rgba >> 0) & 0xff + lg[z] = (rgba >> 8) & 0xff + lb[z] = (rgba >> 16) & 0xff + la[z] = (rgba >> 24) & 0xff + z += 1 + if(z == 16): + for i in xrange(16): + out[iv] = la[i] & 0xff + iv += 1 + out[iv] = lr[i] & 0xff + iv += 1 + for i in xrange(16): + out[iv] = lg[i] & 0xff + iv += 1 + out[iv] = lb[i] & 0xff + iv += 1 + z = 0 + return out + def toImage(self, outfile): + if(self.file): + data = open(self.file, "rb").read() + else: + data = self.data + + header = self.TPLHeader() + textures = [] + pos = 0 + + header.unpack(data[pos:pos + len(header)]) + pos += len(header) + + palette_offsets = [] + + for i in xrange(header.ntextures): + tmp = self.TPLTexture() + tmp.unpack(data[pos:pos + len(tmp)]) + textures.append(tmp) + pos += len(tmp) + if(tmp.palette_offset > 0): + palette_offsets.append(tmp.palette_offset) + + if(header.ntextures > 1): + raise ValueError("Only one texture supported. Don't touch me!") + + for i in xrange(header.ntextures): + head = textures[i] + tex = self.TPLTextureHeader() + tex.unpack(data[head.header_offset:head.header_offset + len(tex)]) + w = tex.width + h = tex.height + + if(tex.format == 0): #I4, 4-bit + tpldata = struct.unpack(">" + str((w * h) / 2) + "B", data[tex.data_off:tex.data_off + ((w * h) / 2)]) + rgbdata = self.I4((w, h), tpldata) + + elif(tex.format == 1): #I8, 8-bit + tpldata = struct.unpack(">" + str(w * h) + "B", data[tex.data_off:tex.data_off + (w * h * 1)]) + rgbdata = self.I8((w, h), tpldata) + elif(tex.format == 2): #IA4, 8-bit + tpldata = struct.unpack(">" + str(w * h) + "B", data[tex.data_off:tex.data_off + (w * h * 1)]) + rgbdata = self.IA4((w, h), tpldata) + + elif(tex.format == 4): #RGB565, 16-bit + tpldata = data[tex.data_off:] + rgbdata = self.RGB565((w, h), tpldata) + elif(tex.format == 5): #RGB5A3, 16-bit + tpldata = data[tex.data_off:] + rgbdata = self.RGB5A3((w, h), tpldata) + elif(tex.format == 3): #IA8, 16-bit + tpldata = data[tex.data_off:] + rgbdata = self.IA8((w, h), tpldata) + + elif(tex.format == 6): #RGBA8, 32-bit, but for easyness's sake lets do it with 16-bit + tpldata = data[tex.data_off:] + rgbdata = self.RGBA8((w, h), tpldata) + + elif(tex.format == 8 or tex.format == 9 or tex.format == 10): + palhead = self.TPLPaletteHeader() + offs = palette_offsets.pop(0) + palhead.unpack(data[offs:offs + len(palhead)]) + + tpldata = struct.unpack(">" + str(palhead.nitems) + "H", data[palhead.offset:palhead.offset + (palhead.nitems * 2)]) + if(palhead.format == 0): + palette_data = self.IA8((palhead.nitems, 1), tpldata)[0] + elif(palhead.format == 1): + palette_data = self.RGB565((palhead.nitems, 1), tpldata)[0] + elif(palhead.format == 2): + palette_data = self.RGB5A3((palhead.nitems, 1), tpldata)[0] + + paldata = [] + for i in xrange(0, palhead.nitems * 4, 4): + tmp = 0 + tmp |= palette_data[i + 0] << 24 + tmp |= palette_data[i + 1] << 16 + tmp |= palette_data[i + 2] << 8 + tmp |= palette_data[i + 3] << 0 + paldata.append(tmp) + + if(tex.format == 8): + tpldata = struct.unpack(">" + str((w * h) / 2) + "B", data[tex.data_off:tex.data_off + ((w * h) / 2)]) + rgbdata = self.CI4((w, h), tpldata, paldata) + if(tex.format == 9): + tpldata = struct.unpack(">" + str(w * h) + "B", data[tex.data_off:tex.data_off + (w * h * 1)]) + rgbdata = self.CI8((w, h), tpldata, paldata) + if(tex.format == 10): + tpldata = struct.unpack(">" + str(w * h) + "H", data[tex.data_off:tex.data_off + (w * h * 2)]) + rgbdata = self.CI14X2((w, h), tpldata, paldata) + elif(tex.format == 14): + tpldata = ''.join(data[tex.data_off:]) + + rgbdata = self.CMP((w, h), tpldata) + else: + raise TypeError("Unsupported TPL Format: " + str(tex.format)) + + output = Image.fromstring("RGBA", (w, h), rgbdata) + ext = outfile[outfile.rfind(".")+1:] + output.save(outfile, ext) + + return outfile + def getSizes(self): + """This returns a tuple containing the width and height of the TPL image filename in the class initializer. Will only return the size of single textured TPL images.""" + if(self.file): + data = open(self.file, "rb").read() + else: + data = self.data + + header = self.TPLHeader() + textures = [] + pos = 0 + + header.unpack(data[pos:pos + len(header)]) + pos += len(header) + + for i in xrange(header.ntextures): + tmp = self.TPLTexture() + tmp.unpack(data[pos:pos + len(tmp)]) + textures.append(tmp) + pos += len(tmp) + + for i in xrange(header.ntextures): + head = textures[i] + tex = self.TPLTextureHeader() + tex.unpack(data[head.header_offset:head.header_offset + len(tex)]) + w = tex.width + h = tex.height + return (w, h) + def toScreen(self): #single texture only + import wx + class imp(wx.Dialog): + def __init__(self, title, im): + w = img.GetWidth() + h = img.GetHeight() + + wx.Dialog.__init__(self, None, -1, title, size = (max(w, 300), max(h, 200))) + + wx.StaticBitmap(self, -1, im, ( ((max(w, 300) - w) / 2), ((max(h, 200) - h) / 2) ), (w, h)) + self.toImage("tmp.png") + img = wx.Image("tmp.png", wx.BITMAP_TYPE_ANY).ConvertToBitmap() + w = img.GetWidth() + h = img.GetHeight() + dialog = imp("TPL (" + str(w) + ", " + str(h) + ")", img) + dialog.ShowModal() + dialog.Destroy() + os.unlink("tmp.png") + def RGBA8(self, (x, y), data): + out = [0 for i in xrange(x * y)] + inp = 0 + for i in xrange(0, y, 4): + for j in xrange(0, x, 4): + for k in xrange(2): + for l in xrange(i, i + 4, 1): + for m in xrange(j, j + 4, 1): + texel = Struct.uint8(data[inp:inp + 1], endian = '>') + inp += 1 + texel2 = Struct.uint8(data[inp:inp + 1], endian = '>') + inp += 1 + if (m >= x) or (l >= y): + continue + if k == 0: # ARARARAR + a = (texel) & 0xff + r = (texel2) & 0xff + out[m + (l * x)] |= ((r << 0) | (a << 24)) + else: # GBGBGBGB + g = (texel) & 0xff + b = (texel2) & 0xff + out[m + (l * x)] |= ((g << 8) | (b << 16)) + return ''.join(Struct.uint32(p) for p in out) + def RGB5A3(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 4): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 4): + pixel = Struct.uint16(jar[i * 2:i * 2 + 2], endian='>') + i += 1 + + if(y1 >= h or x1 >= w): + continue + + if(pixel & (1 << 15)): #RGB555 + b = (((pixel >> 10) & 0x1F) * 255) / 31 + g = (((pixel >> 5) & 0x1F) * 255) / 31 + r = (((pixel >> 0) & 0x1F) * 255) / 31 + a = 255 + else: #RGB4A3 + a = (((pixel >> 12) & 0x07) * 255) / 7 + b = (((pixel >> 8) & 0x0F) * 255) / 15 + g = (((pixel >> 4) & 0x0F) * 255) / 15 + r = (((pixel >> 0) & 0x0F) * 255)/ 15 + + rgba = (r << 16) | (g << 8) | (b << 0) | (a << 24) + out[(y1 * w) + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def RGB565(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 4): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 4): + pixel = Struct.uint16(jar[i * 2:i * 2 + 2], endian='>') + i += 1 + + if(y1 >= h or x1 >= w): + continue + + b = (((pixel >> 11) & 0x1F) << 3) & 0xff + g = (((pixel >> 5) & 0x3F) << 2) & 0xff + r = (((pixel >> 0) & 0x1F) << 3) & 0xff + a = 255 + + rgba = (r << 16) | (g << 8) | (b << 0) | (a << 24) + out[y1 * w + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def I4(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 8): + for x in xrange(0, w, 8): + for y1 in xrange(y, y + 8): + for x1 in xrange(x, x + 8, 2): + pixel = jar[i] + + if(y1 >= h or x1 >= w): + continue + + r = (pixel >> 4) * 255 / 15 + g = (pixel >> 4) * 255 / 15 + b = (pixel >> 4) * 255 / 15 + a = (pixel >> 4) * 255 / 15 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + + pixel = jar[i] + i += 1 + + if(y1 >= h or x1 >= w): + continue + + r = (pixel & 0x0F) * 255 / 15 + g = (pixel & 0x0F) * 255 / 15 + b = (pixel & 0x0F) * 255 / 15 + a = (pixel & 0x0F) * 255 / 15 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1 + 1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def IA4(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 8): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 8): + pixel = jar[i] + i += 1 + + if(y1 >= h or x1 >= w): + continue + + r = ((pixel & 0x0F) * 255 / 15) & 0xff + g = ((pixel & 0x0F) * 255 / 15) & 0xff + b = ((pixel & 0x0F) * 255 / 15) & 0xff + a = (((pixel >> 4) * 255) / 15) & 0xff + + rgba = ( r<< 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def I8(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 8): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 8): + pixel = jar[i] + i += 1 + + if(y1 >= h or x1 >= w): + continue + + r = pixel + g = pixel + b = pixel + a = 255 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def IA8(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 4): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 4): + pixel = Struct.uint16(jar[i * 2:i * 2 + 2], endian='>') + i += 1 + + if(y1 >= h or x1 >= w): + continue + + r = (pixel >> 8) + g = (pixel >> 8) + b = (pixel >> 8) + a = pixel & 0xff + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def CI4(self, (w, h), jar, pal): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 8): + for x in xrange(0, w, 8): + for y1 in xrange(y, y + 8): + for x1 in xrange(x, x + 8, 2): + if(y1 >= h or x1 >= w): + continue + pixel = jar[i] + + r = (pal[pixel] & 0xFF000000) >> 24 + g = (pal[pixel] & 0x00FF0000) >> 16 + b = (pal[pixel] & 0x0000FF00) >> 8 + a = (pal[pixel] & 0x000000FF) >> 0 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + + if(y1 >= h or x1 >= w): + continue + pixel = jar[i] + i += 1 + + r = (pal[pixel] & 0xFF000000) >> 24 + g = (pal[pixel] & 0x00FF0000) >> 16 + b = (pal[pixel] & 0x0000FF00) >> 8 + a = (pal[pixel] & 0x000000FF) >> 0 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1 + 1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def CI8(self, (w, h), jar, pal): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 8): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 8): + if(y1 >= h or x1 >= w): + continue + pixel = jar[i] + i += 1 + + r = (pal[pixel] & 0xFF000000) >> 24 + g = (pal[pixel] & 0x00FF0000) >> 16 + b = (pal[pixel] & 0x0000FF00) >> 8 + a = (pal[pixel] & 0x000000FF) >> 0 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def CMP(self, (w, h), data): + temp = [0 for i in xrange(w * h)] + pix = [ 0 , 0 , 0 ] + c = [ 0 , 0 , 0 , 0 ] + outp = 0 + for y in xrange(h): + for x in xrange(w): + ww = round_up(w, 8) + + x0 = x & 0x03 + x1 = (x >> 2) & 0x01 + x2 = x >> 3 + + y0 = y & 0x03 + y1 = (y >> 2) & 0x01 + y2 = y >> 3 + + off = (8 * x1) + (16 * y1) + (32 * x2) + (4 * ww * y2) + + c[0] = Struct.uint16(data[off + 0:off + 2], endian='>') + c[1] = Struct.uint16(data[off + 2:off + 4], endian='>') + if(c[0] > c[1]): + c[2] = avg(2, 1, c[0], c[1]) + c[3] = avg(1, 2, c[0], c[1]) + else: + c[2] = avg(1, 1, c[0], c[1]) + c[3] = 0 + + px = Struct.uint32(data[off+4:off + 8], endian='>') + ix = x0 + ( 4 * y0 ) + raw = c[(px >> (30 - (2 * ix))) & 0x03] + + pix[0] = (raw >> 8) & 0xf8 + pix[1] = (raw >> 3) & 0xf8 + pix[2] = (raw << 3) & 0xf8 + + temp[outp] = (pix[0] <<0) | (pix[1] << 8) | (pix[2] << 16) | (255 << 24) + outp += 1 + return ''.join(Struct.uint32(p) for p in temp) + def CI14X2(self, (w, h), jar): + out = [0 for i in xrange(w * h)] + i = 0 + for y in xrange(0, h, 4): + for x in xrange(0, w, 4): + for y1 in xrange(y, y + 4): + for x1 in xrange(x, x + 4): + if(y1 >= h or x1 >= w): + continue + pixel = jar[i] + i += 1 + + r = (pal[pixel & 0x3FFF] & 0xFF000000) >> 24 + g = (pal[pixel & 0x3FFF] & 0x00FF0000) >> 16 + b = (pal[pixel & 0x3FFF] & 0x0000FF00) >> 8 + a = (pal[pixel & 0x3FFF] & 0x000000FF) >> 0 + + rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24) + out[y1 * w + x1] = rgba + return ''.join(Struct.uint32(p) for p in out) + def getFormat(self): + if(self.file): + data = open(self.file, "rb").read() + else: + data = self.data + + header = self.TPLHeader() + textures = [] + pos = 0 + + header.unpack(data[pos:pos + len(header)]) + pos += len(header) + + palette_offsets = [] + + for i in xrange(header.ntextures): + tmp = self.TPLTexture() + tmp.unpack(data[pos:pos + len(tmp)]) + textures.append(tmp) + pos += len(tmp) + if(tmp.palette_offset > 0): + palette_offsets.append(tmp.palette_offset) + + for i in xrange(header.ntextures): + head = textures[i] + tex = self.TPLTextureHeader() + tex.unpack(data[head.header_offset:head.header_offset + len(tex)]) + + if(tex.format == 0): #I4, 4-bit + return "I4" + elif(tex.format == 1): #I8, 8-bit + return "I8" + elif(tex.format == 2): #IA4, 8-bit + return "IA4" + elif(tex.format == 4): #RGB565, 16-bit + return "RGB565" + elif(tex.format == 5): #RGB5A3, 16-bit + return "RGB5A3" + elif(tex.format == 3): #IA8, 16-bit + return "IA8" + elif(tex.format == 6): #RGBA8, 32-bit + return "RGBA8" + elif(tex.format == 8): + return "CI4" + elif(tex.format == 9): + return "CI8" + elif(tex.format == 10): + return "CI14X2" + elif(tex.format == 14): + return "CMP" + else: + return "CRAP" + raise TypeError("Unknown TPL Format: %d" % tex.format) + + +if __name__=='__main__': + import wx + app = wx.PySimpleApp() + app.MainLoop() + TPL(*sys.argv[1:]).toScreen()