mirror of
https://github.com/grp/Wii.py.git
synced 2025-06-18 23:05:48 -04:00
removed useless files #1
This commit is contained in:
parent
1ca063a8fc
commit
a072ea5a75
801
TPL.py
801
TPL.py
@ -1,801 +0,0 @@
|
|||||||
import os, struct, subprocess, fnmatch, shutil, urllib, array
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
from Struct import Struct
|
|
||||||
|
|
||||||
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: RGBA8, RGB565, I4, IA4, I8, IA8, CI4, CI8, CMP, CI14X2. Currently not working is RBG5A3. RBG5A3 is having alpha issues, i'm working on it.
|
|
||||||
|
|
||||||
Currently support to convert to TPL: I4, I8, IA4, IA8, RBG565, RBGA8. Currently not working/done are CI4, CI8, CMP, CI14X2, and RBG5A3. RBG5A3 is having alpha issues."""
|
|
||||||
|
|
||||||
|
|
||||||
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(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
|
|
||||||
|
|
||||||
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."""
|
|
||||||
data = open(self.file, "rb").read()
|
|
||||||
|
|
||||||
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
|
|
||||||
"""This will draw a simple window with the TPL image displayed on it. It uses WxPython for the window creation and management. The window has a minimum width and height of 300 x 200. Does not return a value.
|
|
||||||
|
|
||||||
Again, only a single texture is supported."""
|
|
||||||
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)] = out[m + (l * x)] | ((r<<0) | (a<<24))
|
|
||||||
else:
|
|
||||||
g = (texel >> 8) & 0xff
|
|
||||||
b = (texel >> 0) & 0xff
|
|
||||||
out[m + (l * x)] = 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 = 255
|
|
||||||
|
|
||||||
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 = 255
|
|
||||||
|
|
||||||
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)
|
|
377
U8.py
377
U8.py
@ -1,377 +0,0 @@
|
|||||||
import os, struct, subprocess, fnmatch, shutil, urllib, array
|
|
||||||
|
|
||||||
from Struct import Struct
|
|
||||||
|
|
||||||
from common import *
|
|
||||||
|
|
||||||
|
|
||||||
class U8():
|
|
||||||
"""This class can unpack and pack U8 archives, which are used all over the Wii. They are often used in Banners and contents in Downloadable Titles. Please remove all headers and compression first, kthx.
|
|
||||||
|
|
||||||
The f parameter is either the source folder to pack, or the source file to unpack."""
|
|
||||||
class U8Header(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.tag = Struct.string(4)
|
|
||||||
self.rootnode_offset = Struct.uint32
|
|
||||||
self.header_size = Struct.uint32
|
|
||||||
self.data_offset = Struct.uint32
|
|
||||||
self.zeroes = Struct.string(16)
|
|
||||||
class U8Node(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.type = Struct.uint16
|
|
||||||
self.name_offset = Struct.uint16
|
|
||||||
self.data_offset = Struct.uint32
|
|
||||||
self.size = Struct.uint32
|
|
||||||
def __init__(self, f):
|
|
||||||
self.f = f
|
|
||||||
def _pack(self, file, recursion, is_root = 0): #internal
|
|
||||||
node = self.U8Node()
|
|
||||||
node.name_offset = len(self.strings)
|
|
||||||
if(is_root != 1):
|
|
||||||
self.strings += (file)
|
|
||||||
self.strings += ("\x00")
|
|
||||||
|
|
||||||
if(os.path.isdir(file)):
|
|
||||||
node.type = 0x0100
|
|
||||||
node.data_offset = recursion - 1
|
|
||||||
recursion += 1
|
|
||||||
files = sorted(os.listdir(file))
|
|
||||||
#if(sorted(files) == ["banner.bin", "icon.bin", "sound.bin"]):
|
|
||||||
# files = ["icon.bin", "banner.bin", "sound.bin"]
|
|
||||||
|
|
||||||
oldsz = len(self.nodes)
|
|
||||||
if(is_root != 1):
|
|
||||||
self.nodes.append(node)
|
|
||||||
|
|
||||||
os.chdir(file)
|
|
||||||
for entry in files:
|
|
||||||
if(entry != ".DS_Store" and entry[len(entry) - 4:] != "_out"):
|
|
||||||
self._pack(entry, recursion)
|
|
||||||
os.chdir("..")
|
|
||||||
|
|
||||||
self.nodes[oldsz].size = len(self.nodes) + 1
|
|
||||||
else:
|
|
||||||
f = open(file, "rb")
|
|
||||||
data = f.read()
|
|
||||||
f.close()
|
|
||||||
sz = len(data)
|
|
||||||
data += "\x00" * (align(sz, 32) - sz) #32 seems to work best for fuzzyness? I'm still really not sure
|
|
||||||
node.data_offset = len(self.data)
|
|
||||||
self.data += data
|
|
||||||
node.size = sz
|
|
||||||
node.type = 0x0000
|
|
||||||
if(is_root != 1):
|
|
||||||
self.nodes.append(node)
|
|
||||||
|
|
||||||
def pack(self, fn = ""):
|
|
||||||
"""This function will pack a folder into a U8 archive. The output file name is specified in the parameter fn. If fn is an empty string, the filename is deduced from the input folder name. Returns the output filename.
|
|
||||||
|
|
||||||
This creates valid U8 archives for all purposes."""
|
|
||||||
header = self.U8Header()
|
|
||||||
rootnode = self.U8Node()
|
|
||||||
|
|
||||||
header.tag = "U\xAA8-"
|
|
||||||
header.rootnode_offset = 0x20
|
|
||||||
header.zeroes = "\x00" * 16
|
|
||||||
|
|
||||||
self.nodes = []
|
|
||||||
self.strings = "\x00"
|
|
||||||
self.data = ""
|
|
||||||
origdir = os.getcwd()
|
|
||||||
os.chdir(self.f)
|
|
||||||
self._pack(".", 0, 1)
|
|
||||||
os.chdir(origdir)
|
|
||||||
|
|
||||||
header.header_size = (len(self.nodes) + 1) * len(rootnode) + len(self.strings)
|
|
||||||
header.data_offset = align(header.header_size + header.rootnode_offset, 64)
|
|
||||||
rootnode.size = len(self.nodes) + 1
|
|
||||||
rootnode.type = 0x0100
|
|
||||||
|
|
||||||
for i in range(len(self.nodes)):
|
|
||||||
if(self.nodes[i].type == 0x0000):
|
|
||||||
self.nodes[i].data_offset += header.data_offset
|
|
||||||
|
|
||||||
if(fn == ""):
|
|
||||||
if(self.f[len(self.f) - 4:] == "_out"):
|
|
||||||
fn = os.path.dirname(self.f) + "/" + os.path.basename(self.f)[:-4].replace("_", ".")
|
|
||||||
else:
|
|
||||||
fn = self.f
|
|
||||||
|
|
||||||
fd = open(fn, "wb")
|
|
||||||
fd.write(header.pack())
|
|
||||||
fd.write(rootnode.pack())
|
|
||||||
for node in self.nodes:
|
|
||||||
fd.write(node.pack())
|
|
||||||
fd.write(self.strings)
|
|
||||||
fd.write("\x00" * (header.data_offset - header.rootnode_offset - header.header_size))
|
|
||||||
fd.write(self.data)
|
|
||||||
fd.close()
|
|
||||||
|
|
||||||
return fn
|
|
||||||
|
|
||||||
def unpack(self, fn = ""):
|
|
||||||
"""This will unpack the U8 archive specified by the initialization parameter into either the folder specified by the parameter fn, or into a folder created with this formula:
|
|
||||||
``filename_extension_out``
|
|
||||||
|
|
||||||
This will recreate the directory structure, including the initial root folder in the U8 archive (such as "arc" or "meta"). Returns the output directory name."""
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
|
|
||||||
header = self.U8Header()
|
|
||||||
header.unpack(data[offset:offset + len(header)])
|
|
||||||
offset += len(header)
|
|
||||||
|
|
||||||
if(header.tag != "U\xAA8-"):
|
|
||||||
raise NameError("Bad U8 Tag")
|
|
||||||
offset = header.rootnode_offset
|
|
||||||
|
|
||||||
rootnode = self.U8Node()
|
|
||||||
rootnode.unpack(data[offset:offset + len(rootnode)])
|
|
||||||
offset += len(rootnode)
|
|
||||||
|
|
||||||
nodes = []
|
|
||||||
for i in range(rootnode.size - 1):
|
|
||||||
node = self.U8Node()
|
|
||||||
node.unpack(data[offset:offset + len(node)])
|
|
||||||
offset += len(node)
|
|
||||||
nodes.append(node)
|
|
||||||
|
|
||||||
strings = data[offset:offset + header.data_offset - len(header) - (len(rootnode) * rootnode.size)]
|
|
||||||
offset += len(strings)
|
|
||||||
|
|
||||||
if(fn == ""):
|
|
||||||
fn = os.path.dirname(self.f) + "/" + os.path.basename(self.f).replace(".", "_") + "_out"
|
|
||||||
try:
|
|
||||||
origdir = os.getcwd()
|
|
||||||
os.mkdir(fn)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
os.chdir(fn)
|
|
||||||
|
|
||||||
recursion = [rootnode.size]
|
|
||||||
counter = 0
|
|
||||||
for node in nodes:
|
|
||||||
counter += 1
|
|
||||||
name = strings[node.name_offset:].split('\0', 1)[0]
|
|
||||||
|
|
||||||
if(node.type == 0x0100): #folder
|
|
||||||
recursion.append(node.size)
|
|
||||||
try:
|
|
||||||
os.mkdir(name)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
os.chdir(name)
|
|
||||||
continue
|
|
||||||
elif(node.type == 0): #file
|
|
||||||
file = open(name, "wb")
|
|
||||||
file.write(data[node.data_offset:node.data_offset + node.size])
|
|
||||||
offset += node.size
|
|
||||||
else: #unknown
|
|
||||||
pass #ignore
|
|
||||||
|
|
||||||
sz = recursion.pop()
|
|
||||||
if(sz == counter + 1):
|
|
||||||
os.chdir("..")
|
|
||||||
else:
|
|
||||||
recursion.append(sz)
|
|
||||||
os.chdir("..")
|
|
||||||
|
|
||||||
os.chdir(origdir)
|
|
||||||
return fn
|
|
||||||
def __str__(self):
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
|
|
||||||
header = self.U8Header()
|
|
||||||
header.unpack(data[offset:offset + len(header)])
|
|
||||||
offset += len(header)
|
|
||||||
|
|
||||||
if(header.tag != "U\xAA8-"):
|
|
||||||
raise NameError("Bad U8 Tag")
|
|
||||||
offset = header.rootnode_offset
|
|
||||||
|
|
||||||
rootnode = self.U8Node()
|
|
||||||
rootnode.unpack(data[offset:offset + len(rootnode)])
|
|
||||||
offset += len(rootnode)
|
|
||||||
|
|
||||||
nodes = []
|
|
||||||
for i in xrange(rootnode.size - 1):
|
|
||||||
node = self.U8Node()
|
|
||||||
node.unpack(data[offset:offset + len(node)])
|
|
||||||
offset += len(node)
|
|
||||||
nodes.append(node)
|
|
||||||
|
|
||||||
strings = data[offset:offset + header.data_offset - len(header) - (len(rootnode) * rootnode.size)]
|
|
||||||
offset += len(strings)
|
|
||||||
|
|
||||||
out = "[root]\n"
|
|
||||||
recursion = [rootnode.size]
|
|
||||||
counter = 0
|
|
||||||
for node in nodes:
|
|
||||||
counter += 1
|
|
||||||
name = strings[node.name_offset:].split('\0', 1)[0]
|
|
||||||
out += " " * len(recursion)
|
|
||||||
if(node.type == 0x0100): #folder
|
|
||||||
recursion.append(node.size)
|
|
||||||
out += "[%s]\n" % name
|
|
||||||
continue
|
|
||||||
elif(node.type == 0): #file
|
|
||||||
out += "%s\n" % name
|
|
||||||
offset += node.size
|
|
||||||
else: #unknown, ignore
|
|
||||||
pass
|
|
||||||
|
|
||||||
sz = recursion.pop()
|
|
||||||
if(sz == counter + 1):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
recursion.append(sz)
|
|
||||||
return out
|
|
||||||
|
|
||||||
class IMD5():
|
|
||||||
"""This class can add and remove IMD5 headers to files. The parameter f is the file to use for the addition or removal of the header. IMD5 headers are found in banner.bin, icon.bin, and sound.bin."""
|
|
||||||
class IMD5Header(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.tag = Struct.string(4)
|
|
||||||
self.size = Struct.uint32
|
|
||||||
self.zeroes = Struct.uint8[8]
|
|
||||||
self.crypto = Struct.string(16)
|
|
||||||
def __init__(self, f):
|
|
||||||
self.f = f
|
|
||||||
def add(self, fn = ""):
|
|
||||||
"""This function adds an IMD5 header to the file specified by f in the initializer. The output file is specified with fn, if it is empty, it will overwrite the input file. If the file already has an IMD5 header, it will now have two. Returns the output filename."""
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
|
|
||||||
imd5 = self.IMD5Header()
|
|
||||||
for i in range(8):
|
|
||||||
imd5.zeroes[i] = 0x00
|
|
||||||
imd5.tag = "IMD5"
|
|
||||||
imd5.size = len(data)
|
|
||||||
imd5.crypto = str(Crypto().createMD5Hash(data))
|
|
||||||
data = imd5.pack() + data
|
|
||||||
|
|
||||||
if(fn != ""):
|
|
||||||
open(fn, "wb").write(data)
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
open(self.f, "wb").write(data)
|
|
||||||
return self.f
|
|
||||||
def remove(self, fn = ""):
|
|
||||||
"""This will remove an IMD5 header from the file specified in f, if one exists. If there is no IMD5 header, it will output the file as it is. It will output in the parameter fn if available, otherwise it will overwrite the source. Returns the output filename."""
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
imd5 = self.IMD5Header()
|
|
||||||
if(data[:4] != "IMD5"):
|
|
||||||
if(fn != ""):
|
|
||||||
open(fn, "wb").write(data)
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
return self.f
|
|
||||||
data = data[len(imd5):]
|
|
||||||
|
|
||||||
if(fn != ""):
|
|
||||||
open(fn, "wb").write(data)
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
open(self.f, "wb").write(data)
|
|
||||||
return self.f
|
|
||||||
|
|
||||||
class IMET():
|
|
||||||
"""IMET headers are found in Opening.bnr and 0000000.app files. They contain the channel titles and more metadata about channels. They are in two different formats with different amounts of padding before the start of the IMET header. This class suports both.
|
|
||||||
|
|
||||||
The parameter f is used to specify the input file name."""
|
|
||||||
class IMETHeader(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.zeroes = Struct.uint8[128]
|
|
||||||
self.tag = Struct.string(4)
|
|
||||||
self.unk = Struct.uint64
|
|
||||||
self.sizes = Struct.uint32[3] #icon, banner, sound
|
|
||||||
self.unk2 = Struct.uint32
|
|
||||||
self.names = Struct.string(84, encoding = "utf-16-be", stripNulls = True)[7]
|
|
||||||
self.zeroes2 = Struct.uint8[840]
|
|
||||||
self.hash = Struct.string(16)
|
|
||||||
def __init__(self, f):
|
|
||||||
self.f = f
|
|
||||||
def add(self, iconsz, bannersz, soundsz, name = "", langs = [], fn = ""):
|
|
||||||
"""This function adds an IMET header to the file specified with f in the initializer. The file will be output to fn if it is not empty, otherwise it will overwrite the input file. You must specify the size of banner.bin in bannersz, and respectivly for iconsz and soundsz. langs is an optional arguement that is a list of different langauge channel titles. name is the english name that is copied everywhere in langs that there is an empty string. Returns the output filename."""
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
imet = self.IMETHeader()
|
|
||||||
|
|
||||||
for i in imet.zeroes:
|
|
||||||
imet.zeroes[i] = 0x00
|
|
||||||
imet.tag = "IMET"
|
|
||||||
imet.unk = 0x0000060000000003
|
|
||||||
imet.sizes[0] = iconsz
|
|
||||||
imet.sizes[1] = bannersz
|
|
||||||
imet.sizes[2] = soundsz
|
|
||||||
imet.unk2 = 0
|
|
||||||
for i in range(len(imet.names)):
|
|
||||||
if(len(langs) > 0 and langs[i] != ""):
|
|
||||||
imet.names[i] = langs[i]
|
|
||||||
else:
|
|
||||||
imet.names[i] = name
|
|
||||||
for i in imet.zeroes2:
|
|
||||||
imet.zeroes2[i] = 0x00
|
|
||||||
imet.hash = "\x00" * 16
|
|
||||||
|
|
||||||
tmp = imet.pack()
|
|
||||||
imet.hash = Crypto().createMD5Hash(tmp[0x40:0x640]) #0x00 or 0x40?
|
|
||||||
|
|
||||||
data = imet.pack() + data
|
|
||||||
|
|
||||||
if(fn != ""):
|
|
||||||
open(fn, "wb").write(data)
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
open(self.f, "wb").write(data)
|
|
||||||
return self.f
|
|
||||||
|
|
||||||
def remove(self, fn = ""):
|
|
||||||
"""This method removes an IMET header from a file specified with f in the initializer. fn is the output file name if it isn't an empty string, if it is, it will overwrite the input. If the input has no IMD5 header, it is output as is. Returns the output filename."""
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
if(data[0x80:0x84] == "IMET"):
|
|
||||||
data = data[0x640:]
|
|
||||||
elif(data[0x40:0x44] == "IMET"):
|
|
||||||
data = data[0x640:]
|
|
||||||
else:
|
|
||||||
if(fn != ""):
|
|
||||||
open(fn, "wb").write(data)
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
return self.f
|
|
||||||
if(fn != ""):
|
|
||||||
open(fn, "wb").write(data)
|
|
||||||
return fn
|
|
||||||
else:
|
|
||||||
open(self.f, "wb").write(data)
|
|
||||||
return self.f
|
|
||||||
def getTitle(self):
|
|
||||||
imet = self.IMETHeader()
|
|
||||||
data = open(self.f, "rb").read()
|
|
||||||
|
|
||||||
if(data[0x40:0x44] == "IMET"):
|
|
||||||
pass
|
|
||||||
elif(data[0x80:0x84] == "IMET"):
|
|
||||||
data = data[0x40:]
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
imet.unpack(data[:len(imet)])
|
|
||||||
name = imet.names[1]
|
|
||||||
topop = []
|
|
||||||
for i in range(len(name)):
|
|
||||||
if(name[i] == "\x00"):
|
|
||||||
topop.append(i)
|
|
||||||
name = list(name)
|
|
||||||
popped = 0 #don't ask me why I did this
|
|
||||||
for pop in topop:
|
|
||||||
name.pop(pop - popped)
|
|
||||||
popped += 1
|
|
||||||
|
|
||||||
name = ''.join(name)
|
|
||||||
return name
|
|
260
savedata.py
260
savedata.py
@ -1,260 +0,0 @@
|
|||||||
import os, struct, subprocess, fnmatch, shutil, urllib, array
|
|
||||||
|
|
||||||
from Struct import Struct
|
|
||||||
|
|
||||||
from common import *
|
|
||||||
from title import *
|
|
||||||
from TPL import *
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
class Savegame():
|
|
||||||
class savegameHeader(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.savegameId = Struct.uint32[2]
|
|
||||||
self.bannerSize = Struct.uint32
|
|
||||||
self.permissions = Struct.uint8
|
|
||||||
self.unknown1 = Struct.uint8
|
|
||||||
self.md5hash = Struct.string(16)
|
|
||||||
self.unknown2 = Struct.uint16
|
|
||||||
|
|
||||||
class savegameBanner(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.magic = Struct.string(4)
|
|
||||||
self.reserved = Struct.uint8[4]
|
|
||||||
self.flags = Struct.uint32
|
|
||||||
self.reserved = Struct.uint32[5]
|
|
||||||
self.gameTitle = Struct.string(64)
|
|
||||||
self.gameSubTitle = Struct.string(64)
|
|
||||||
self.banner = Struct.string(24576)
|
|
||||||
self.icon0 = Struct.string(4608)
|
|
||||||
self.icon1 = Struct.string(4608)
|
|
||||||
self.icon2 = Struct.string(4608)
|
|
||||||
self.icon3 = Struct.string(4608)
|
|
||||||
self.icon4 = Struct.string(4608)
|
|
||||||
self.icon5 = Struct.string(4608)
|
|
||||||
self.icon6 = Struct.string(4608)
|
|
||||||
self.icon7 = Struct.string(4608)
|
|
||||||
|
|
||||||
class backupHeader(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.hdrSize = Struct.uint32
|
|
||||||
self.magic = Struct.string(2)
|
|
||||||
self.version = Struct.uint16
|
|
||||||
self.NGid = Struct.uint32
|
|
||||||
self.filesCount = Struct.uint32
|
|
||||||
self.filesSize = Struct.uint32
|
|
||||||
self.unknown1 = Struct.uint32
|
|
||||||
self.unknown2 = Struct.uint32
|
|
||||||
self.totalSize = Struct.uint32
|
|
||||||
self.unknown3 = Struct.uint8[64]
|
|
||||||
self.unknown4 = Struct.uint32
|
|
||||||
self.gameId = Struct.string(4)
|
|
||||||
self.wiiMacAddr = Struct.uint8[6]
|
|
||||||
self.unknown6 = Struct.uint16
|
|
||||||
self.padding = Struct.uint32[4]
|
|
||||||
|
|
||||||
class fileHeader(Struct):
|
|
||||||
__endian__ = Struct.BE
|
|
||||||
def __format__(self):
|
|
||||||
self.magic = Struct.uint32
|
|
||||||
self.size = Struct.uint32
|
|
||||||
self.permissions = Struct.uint8
|
|
||||||
self.attribute = Struct.uint8
|
|
||||||
self.type = Struct.uint8
|
|
||||||
self.nameIV = Struct.string(0x45)
|
|
||||||
|
|
||||||
def __init__(self, f):
|
|
||||||
self.f = f
|
|
||||||
try:
|
|
||||||
self.fd = open(f, 'r+b')
|
|
||||||
except:
|
|
||||||
raise Exception('Cannot open input')
|
|
||||||
|
|
||||||
self.sdKey = '\xab\x01\xb9\xd8\xe1\x62\x2b\x08\xaf\xba\xd8\x4d\xbf\xc2\xa5\x5d'
|
|
||||||
self.sdIv = '\x21\x67\x12\xe6\xaa\x1f\x68\x9f\x95\xc5\xa2\x23\x24\xdc\x6a\x98'
|
|
||||||
|
|
||||||
self.iconCount = 1
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
ret = ''
|
|
||||||
ret += '\nSavegame header \n'
|
|
||||||
|
|
||||||
ret += 'Savegame ID : 0x%x%x\n' % (self.hdr.savegameId[0], self.hdr.savegameId[1])
|
|
||||||
ret += 'Banner size : 0x%x\n' % self.hdr.bannerSize
|
|
||||||
ret += 'Permissions : 0x%x\n' % self.hdr.permissions
|
|
||||||
ret += 'Unknown : 0x%x\n' % self.hdr.unknown1
|
|
||||||
ret += 'MD5 hash : %s\n' % hexdump(self.hdr.md5hash)
|
|
||||||
ret += 'Unknown : 0x%x\n' % self.hdr.unknown2
|
|
||||||
|
|
||||||
ret += '\nBanner header \n'
|
|
||||||
|
|
||||||
ret += 'Flags : 0x%x\n' % self.bnr.flags
|
|
||||||
ret += 'Game title : %s\n' % self.bnr.gameTitle
|
|
||||||
ret += 'Game subtitle : %s\n' % self.bnr.gameSubTitle
|
|
||||||
ret += 'Icons found : %i\n' % self.iconCount
|
|
||||||
|
|
||||||
ret += '\nBackup header \n'
|
|
||||||
|
|
||||||
ret += 'Header size : 0x%x (+ 0x10 of padding) version 0x%x\n' % (self.bkHdr.hdrSize, self.bkHdr.version)
|
|
||||||
if self.bkHdr.gameId[3] == 'P':
|
|
||||||
ret += 'Region : PAL\n'
|
|
||||||
elif self.bkHdr.gameId[3] == 'E':
|
|
||||||
ret += 'Region : NTSC\n'
|
|
||||||
elif self.bkHdr.gameId[3] == 'J':
|
|
||||||
ret += 'Region : JAP\n'
|
|
||||||
ret += 'Game ID : %s\n' % self.bkHdr.gameId
|
|
||||||
ret += 'Wii unique ID : 0x%x\n' % self.bkHdr.NGid
|
|
||||||
ret += 'Wii MAC address %02x:%02x:%02x:%02x:%02x:%02x\n' % (self.bkHdr.wiiMacAddr[0], self.bkHdr.wiiMacAddr[1], self.bkHdr.wiiMacAddr[2], self.bkHdr.wiiMacAddr[3], self.bkHdr.wiiMacAddr[4], self.bkHdr.wiiMacAddr[5])
|
|
||||||
ret += 'Found %i files for %i bytes\n' % (self.bkHdr.filesCount, self.bkHdr.filesSize)
|
|
||||||
ret += 'Total size : %i bytes\n' % self.bkHdr.totalSize
|
|
||||||
ret += 'This save is %i blocks wise' % (self.bkHdr.totalSize / 0x20000)
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def extractFiles(self):
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.mkdir(os.path.dirname(self.f) + '/' + self.bkHdr.gameId)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
os.chdir(os.path.dirname(self.f) + '/' + self.bkHdr.gameId)
|
|
||||||
|
|
||||||
self.fd.seek(self.fileStartOffset)
|
|
||||||
|
|
||||||
for i in range(self.bkHdr.filesCount):
|
|
||||||
|
|
||||||
fileHdr = self.fd.read(0x80)
|
|
||||||
fileHdr = self.fileHeader().unpack(fileHdr)
|
|
||||||
|
|
||||||
if fileHdr.magic != 0x03adf17e:
|
|
||||||
raise Exception('Wrong file magic')
|
|
||||||
|
|
||||||
fileHdr.size = align(fileHdr.size, 64)
|
|
||||||
|
|
||||||
ivpos = 0
|
|
||||||
name = ""
|
|
||||||
i = 0
|
|
||||||
for char in list(fileHdr.nameIV):
|
|
||||||
if(char == "\x00"):
|
|
||||||
i -= 1
|
|
||||||
ivpos = i
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
name += char
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
|
|
||||||
fileIV = fileHdr.nameIV[ivpos:ivpos + 16]
|
|
||||||
|
|
||||||
if len(fileIV) != 16:
|
|
||||||
raise Exception('IV alignment issue')
|
|
||||||
|
|
||||||
if fileHdr.type == 1:
|
|
||||||
print 'Extracted %s (%ib)' % (name, fileHdr.size)
|
|
||||||
|
|
||||||
fileBuffer = self.fd.read(fileHdr.size)
|
|
||||||
fileBuffer = Crypto().decryptData(self.sdKey, fileIV, fileBuffer, True)
|
|
||||||
try:
|
|
||||||
open(name, 'w+b').write(fileBuffer)
|
|
||||||
except:
|
|
||||||
raise Exception('Cannot write the output')
|
|
||||||
elif fileHdr.type == 2:
|
|
||||||
print 'Extracted folder %s' % name
|
|
||||||
try:
|
|
||||||
os.mkdir(name)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
print 'Attribute %i Permission %i' % (fileHdr.attribute, fileHdr.permissions)
|
|
||||||
print 'File IV : 0x%s' % hexdump(fileIV, '')
|
|
||||||
|
|
||||||
os.chdir('..')
|
|
||||||
|
|
||||||
def analyzeHeader(self):
|
|
||||||
headerBuffer = self.fd.read(0xF0C0)
|
|
||||||
headerBuffer = Crypto().decryptData(self.sdKey, self.sdIv, headerBuffer, True)
|
|
||||||
|
|
||||||
self.hdr = self.savegameHeader().unpack(headerBuffer[:0x20])
|
|
||||||
|
|
||||||
#headerBuffer.replace(self.hdr.md5hash, '\x0e\x65\x37\x81\x99\xbe\x45\x17\xab\x06\xec\x22\x45\x1a\x57\x93')
|
|
||||||
#print 'Reashed md5 : %s' % hexdump(Crypto().createMD5Hash(headerBuffer))
|
|
||||||
|
|
||||||
self.bnr = self.savegameBanner().unpack(headerBuffer[0x20:])
|
|
||||||
|
|
||||||
if self.bnr.magic != 'WIBN':
|
|
||||||
raise Exception ('Wrong magic, should be WIBN')
|
|
||||||
|
|
||||||
if self.hdr.bannerSize != 0x72A0:
|
|
||||||
self.iconCount += 7
|
|
||||||
|
|
||||||
bkHdrBuffer = self.fd.read(0x80)
|
|
||||||
self.bkHdr = self.backupHeader().unpack(bkHdrBuffer)
|
|
||||||
|
|
||||||
if self.bkHdr.magic != 'Bk' or self.bkHdr.hdrSize != 0x70:
|
|
||||||
raise Exception ('Bk header error')
|
|
||||||
|
|
||||||
self.fileStartOffset = self.fd.tell()
|
|
||||||
|
|
||||||
def eraseWiiMac(self):
|
|
||||||
self.fd.seek(0xF128)
|
|
||||||
print self.fd.write('\x00' * 6)
|
|
||||||
|
|
||||||
def getBanner(self):
|
|
||||||
try:
|
|
||||||
os.mkdir(os.path.dirname(self.f) + '/' + self.bkHdr.gameId)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
os.chdir(os.path.dirname(self.f) + '/' + self.bkHdr.gameId)
|
|
||||||
|
|
||||||
return Image.fromstring("RGBA", (192, 64), TPL('').RGB5A3((192, 64), self.bnr.banner)).save('banner', 'png')
|
|
||||||
|
|
||||||
def getIcon(self, index):
|
|
||||||
if index < 0 or index > 7 or index > self.iconCount:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.mkdir(os.path.dirname(self.f) + '/' + self.bkHdr.gameId)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
os.chdir(os.path.dirname(self.f) + '/' + self.bkHdr.gameId)
|
|
||||||
|
|
||||||
if index == 0:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon0)).save('icon' + str(index), 'png')
|
|
||||||
if index == 1:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon1)).save('icon' + str(index), 'png')
|
|
||||||
if index == 2:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon2)).save('icon' + str(index), 'png')
|
|
||||||
if index == 3:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon3)).save('icon' + str(index), 'png')
|
|
||||||
if index == 4:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon4)).save('icon' + str(index), 'png')
|
|
||||||
if index == 5:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon5)).save('icon' + str(index), 'png')
|
|
||||||
if index == 6:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon6)).save('icon' + str(index), 'png')
|
|
||||||
if index == 7:
|
|
||||||
return Image.fromstring("RGBA", (48, 48), TPL('').RGB5A3((48, 48), self.bnr.icon7)).save('icon' + str(index), 'png')
|
|
||||||
|
|
||||||
def getIconsCount(self):
|
|
||||||
return self.iconCount
|
|
||||||
|
|
||||||
def getSaveString(self, string):
|
|
||||||
if string == 'id':
|
|
||||||
return self.bkHdr.gameId
|
|
||||||
elif string == 'title':
|
|
||||||
return self.bnr.gameTitle
|
|
||||||
elif string == 'subtitle':
|
|
||||||
return self.bnr.gameSubTitle
|
|
||||||
elif string == 'mac':
|
|
||||||
return self.bkHdr.wiiMacAddr[0] + ':' + self.bkHdr.wiiMacAddr[1] + ':' + self.bkHdr.wiiMacAddr[2] + ':' + self.bkHdr.wiiMacAddr[3] + ':' + self.bkHdr.wiiMacAddr[4] + ':' + self.bkHdr.wiiMacAddr[5]
|
|
||||||
|
|
||||||
def getFilesCount(self):
|
|
||||||
return self.bkHdr.filesCount
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user