mirror of
https://github.com/fail0verflow/hbc.git
synced 2024-11-16 14:59:14 +01:00
f45a0adb84
I guess we've been doing the clamping wrong all along and it just happened to work in the past?
1737 lines
46 KiB
Python
1737 lines
46 KiB
Python
have_pyglet = False
|
|
|
|
try:
|
|
from pyglet import clock, window
|
|
from pyglet.gl import *
|
|
from pyglet.image import *
|
|
have_pyglet = True
|
|
except:
|
|
pass
|
|
|
|
import sys, os
|
|
sys.path.append(os.path.realpath(os.path.dirname(sys.argv[0]))+"/../Common")
|
|
from Struct import Struct
|
|
import struct
|
|
import pywii as wii
|
|
|
|
def LZ77Decompress(data):
|
|
inp = 0
|
|
dlen = len(data)
|
|
data += '\0' * 16
|
|
|
|
ret = []
|
|
|
|
while inp < dlen:
|
|
bitmask = ord(data[inp])
|
|
inp += 1
|
|
|
|
for i in xrange(8):
|
|
if bitmask & 0x80:
|
|
rep = ord(data[inp])
|
|
repLength = (rep >> 4) + 3
|
|
inp += 1
|
|
repOff = ord(data[inp]) | ((rep & 0x0F) << 8)
|
|
inp += 1
|
|
|
|
assert repOff <= len(ret)
|
|
|
|
while repLength > 0:
|
|
ret.append(ret[-repOff - 1])
|
|
repLength -= 1
|
|
else:
|
|
ret.append(data[inp])
|
|
inp += 1
|
|
|
|
bitmask <<= 1
|
|
|
|
return ''.join(ret)
|
|
|
|
class U8(object):
|
|
class U8Header(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Tag = Struct.string(4)
|
|
self.RootNode = Struct.uint32
|
|
self.HeaderSize = Struct.uint32
|
|
self.DataOffset = Struct.uint32
|
|
self.Zeroes = Struct.uint8[0x10]
|
|
|
|
class U8Node(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Type = Struct.uint16
|
|
self.NameOffset = Struct.uint16
|
|
self.DataOffset = Struct.uint32
|
|
self.Size = Struct.uint32
|
|
|
|
def __init__(self, data=None):
|
|
self.Files = {}
|
|
|
|
if data != None:
|
|
self.Unpack(data)
|
|
|
|
def Unpack(self, data):
|
|
pos = 0
|
|
u8 = self.U8Header()
|
|
u8.unpack(data[pos:pos+len(u8)])
|
|
pos += len(u8)
|
|
|
|
assert u8.Tag == 'U\xAA8-'
|
|
pos += u8.RootNode - 0x20
|
|
|
|
root = self.U8Node()
|
|
root.unpack(data[pos:pos+len(root)])
|
|
pos += len(root)
|
|
|
|
children = []
|
|
for i in xrange(root.Size - 1):
|
|
child = self.U8Node()
|
|
child.unpack(data[pos:pos+len(child)])
|
|
pos += len(child)
|
|
children.append(child)
|
|
|
|
stringTable = data[pos:pos + u8.DataOffset - len(u8) - root.Size * len(root)]
|
|
pos += len(stringTable)
|
|
|
|
currentOffset = u8.DataOffset
|
|
|
|
path = ['.']
|
|
pathDepth = [root.Size - 1]
|
|
for offset,child in enumerate(children):
|
|
name = stringTable[child.NameOffset:].split('\0', 1)[0]
|
|
if child.Type == 0x0100:
|
|
path.append(name)
|
|
pathDepth.append(child.Size-offset-1)
|
|
elif child.Type == 0:
|
|
name = '/'.join(path + [name])
|
|
|
|
if currentOffset < child.DataOffset:
|
|
diff = child.DataOffset - currentOffset
|
|
assert diff <= 32
|
|
|
|
pos += diff
|
|
currentOffset += diff
|
|
|
|
currentOffset += child.Size
|
|
|
|
self.Files[name.lower()] = data[pos:pos+child.Size]
|
|
pos += child.Size
|
|
|
|
pathDepth[-1] -= 1
|
|
if pathDepth[-1] == 0:
|
|
path = path[:-1]
|
|
pathDepth = pathDepth[:-1]
|
|
|
|
class IMD5Header(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Tag = Struct.string(4)
|
|
self.Size = Struct.uint32
|
|
self.Zeroes = Struct.uint8[8]
|
|
self.MD5 = Struct.uint8[0x10]
|
|
|
|
def IMD5(data):
|
|
imd5 = IMD5Header()
|
|
imd5.unpack(data[:len(imd5)])
|
|
assert imd5.Tag == 'IMD5'
|
|
pos = len(imd5)
|
|
|
|
if data[pos:pos+4] == 'LZ77':
|
|
return LZ77Decompress(data[pos+8:])
|
|
else:
|
|
return data[pos:]
|
|
|
|
class TPL(object):
|
|
class TPLHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Magic = Struct.string(4)
|
|
self.Count = Struct.uint32
|
|
self.Size = Struct.uint32
|
|
|
|
class TexOffsets(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.HeaderOff = Struct.uint32
|
|
self.PaletteOff = Struct.uint32
|
|
|
|
class TexHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Size = Struct.uint16[2]
|
|
self.Format = Struct.uint32
|
|
self.DataOff = Struct.uint32
|
|
self.Wrap = Struct.uint32[2]
|
|
self.Filter = Struct.uint32[2]
|
|
self.LODBias = Struct.float
|
|
self.EdgeLOD = Struct.uint8
|
|
self.LOD = Struct.uint8[2]
|
|
self.Unpacked = Struct.uint8
|
|
|
|
class PalHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Count = Struct.uint16
|
|
self.Unpacked = Struct.uint8
|
|
self.Pad = Struct.uint8
|
|
self.Format = Struct.uint32
|
|
self.DataOff = Struct.uint32
|
|
|
|
def __init__(self, data=None):
|
|
self.Textures = []
|
|
|
|
if data != None:
|
|
self.Unpack(data)
|
|
|
|
def Unpack(self, data):
|
|
header = self.TPLHeader()
|
|
header.unpack(data[:len(header)])
|
|
pos = len(header)
|
|
|
|
assert header.Magic == '\x00\x20\xAF\x30'
|
|
assert header.Size == 0xc
|
|
|
|
for i in xrange(header.Count):
|
|
offs = self.TexOffsets()
|
|
offs.unpack(data[pos:pos+len(offs)])
|
|
pos += len(offs)
|
|
|
|
self.Textures.append(self.ParseTexture(data, offs))
|
|
|
|
def ParseTexture(self, data, offs):
|
|
texHeader = self.TexHeader()
|
|
texHeader.unpack(data[offs.HeaderOff:offs.HeaderOff+len(texHeader)])
|
|
format = texHeader.Format
|
|
self.format = format
|
|
rgba = None
|
|
if format == 0:
|
|
rgba = self.I4(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 1:
|
|
rgba = self.I8(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 2:
|
|
rgba = self.IA4(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 3:
|
|
rgba = self.IA8(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 4:
|
|
rgba = self.RGB565(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 5:
|
|
rgba = self.RGB5A3(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 6:
|
|
rgba = self.RGBA8(data[texHeader.DataOff:], texHeader.Size)
|
|
elif format == 14:
|
|
rgba = self.S3TC(data[texHeader.DataOff:], texHeader.Size)
|
|
else:
|
|
print 'Unknown texture format', format
|
|
|
|
if rgba == None:
|
|
rgba = '\0\0\0\0' * texHeader.Size[0] * texHeader.Size[1]
|
|
|
|
image = ImageData(texHeader.Size[1], texHeader.Size[0], 'RGBA', rgba)
|
|
print format
|
|
return image
|
|
|
|
def I4(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 8):
|
|
for j in xrange(0, x, 8):
|
|
ofs = 0
|
|
for k in xrange(8):
|
|
off = min(x - j, 8)
|
|
for sub in xrange(0, off, 2):
|
|
texel = ord(data[inp])
|
|
high, low = texel >> 4, texel & 0xF
|
|
if (outp + ofs + sub) < (x*y):
|
|
out[outp + ofs + sub] = (high << 4) | (high << 20) | (high << 12) | 0xFF<<24
|
|
|
|
if sub + 1 < off:
|
|
if (outp + ofs + sub + 1) < (x*y):
|
|
out[outp + ofs + sub + 1] = (low << 4) | (low << 20) | (low << 12) | 0xFF<<24
|
|
|
|
inp += 1
|
|
|
|
ofs += x
|
|
inp += (8 - off) / 2
|
|
outp += off
|
|
outp += x * 7
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def I8(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y*2)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 4):
|
|
for j in xrange(0, x, 4):
|
|
ofs = 0
|
|
for k in xrange(4):
|
|
off = min(x - j, 4)
|
|
for sub in xrange(off):
|
|
texel = ord(data[inp])
|
|
out[outp + ofs + sub] = (texel << 24) | (texel << 16) | (texel << 8) | 0xFF
|
|
inp += 1
|
|
|
|
ofs += x
|
|
inp += 4 - off * 2
|
|
outp += off
|
|
outp += x * 3
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def IA4(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 4):
|
|
for j in xrange(0, x, 8):
|
|
ofs = 0
|
|
for k in xrange(4):
|
|
off = min(x - j, 8)
|
|
for sub in xrange(off):
|
|
texel = ord(data[inp])
|
|
alpha, inte = texel >> 4, texel & 0xF
|
|
if (outp + ofs + sub) < (x*y):
|
|
out[outp + ofs + sub] = (inte << 4) | (inte << 12) | (inte << 20) | (alpha << 28)
|
|
inp += 1
|
|
|
|
ofs += x
|
|
inp += 8 - off
|
|
outp += off
|
|
outp += x * 3
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def IA8(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 4):
|
|
for j in xrange(0, x, 4):
|
|
ofs = 0
|
|
for k in xrange(4):
|
|
off = min(x - j, 4)
|
|
for sub in xrange(off):
|
|
if (outp + ofs + sub) < (x*y):
|
|
texel = Struct.uint16(data[inp:inp + 2], endian='>')
|
|
p = (texel & 0xFF)
|
|
p |= (texel & 0xFF) << 8
|
|
p |= (texel & 0xFF) << 16
|
|
p |= (texel >> 8) << 24
|
|
out[outp + ofs + sub] = p
|
|
inp += 2
|
|
|
|
ofs += x
|
|
inp += (4 - off) * 2
|
|
outp += off
|
|
outp += x * 3
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def RGB565(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 4):
|
|
for j in xrange(0, x, 4):
|
|
ofs = 0
|
|
for k in xrange(4):
|
|
off = min(x - j, 4)
|
|
for sub in xrange(off):
|
|
if (outp + ofs + sub) < (x*y):
|
|
texel = Struct.uint16(data[inp:inp + 2], endian='>')
|
|
p = ((texel >> 11) & 0x1F) << 3
|
|
p |= ((texel >> 5) & 0x3F) << 10
|
|
p |= ( texel & 0x1F) << 19
|
|
p |= 0xFF<<24
|
|
out[outp + ofs + sub] = p
|
|
inp += 2
|
|
|
|
ofs += x
|
|
inp += (4 - off) * 2
|
|
outp += off
|
|
outp += x * 3
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def RGB5A3(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 4):
|
|
for j in xrange(0, x, 4):
|
|
ofs = 0
|
|
for k in xrange(4):
|
|
off = min(x - j, 4)
|
|
for sub in xrange(off):
|
|
texel = Struct.uint16(data[inp:inp + 2], endian='>')
|
|
if texel & 0x8000:
|
|
p = ((texel >> 10) & 0x1F) << 3
|
|
p |= ((texel >> 5) & 0x1F) << 11
|
|
p |= ( texel & 0x1F) << 19
|
|
p |= 0xFF<<24
|
|
else:
|
|
p = ((texel >> 12) & 0x07) << 29
|
|
p |= ((texel >> 8) & 0x0F) << 4
|
|
p |= ((texel >> 4) & 0x0F) << 12
|
|
p |= (texel & 0x0F) << 20
|
|
if (outp + ofs + sub) < (x*y):
|
|
out[outp + ofs + sub] = p
|
|
inp += 2
|
|
|
|
ofs += x
|
|
inp += (4 - off) * 2
|
|
outp += off
|
|
outp += x * 3
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def RGBA8(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
outp = 0
|
|
inp = 0
|
|
for i in xrange(0, y, 4):
|
|
for j in xrange(0, x, 4):
|
|
ofs = 0
|
|
for k in xrange(4):
|
|
off = min(x - j, 4)
|
|
for sub in xrange(off):
|
|
texel = Struct.uint16(data[inp:inp + 2], endian='>')<<16
|
|
texel |= Struct.uint16(data[inp+32:inp + 34], endian='>')
|
|
if (outp + ofs + sub) < (x*y):
|
|
out[outp + ofs + sub] = texel&0xff000000 | ((texel>>16)&0xff) | (texel&0xff00) | ((texel<<16)&0xff0000)
|
|
inp += 2
|
|
|
|
ofs += x
|
|
inp += (4 - off) * 2
|
|
outp += off
|
|
inp += 32
|
|
outp += x * 3
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
def unpack_rgb565(self,texel):
|
|
b = (texel&0x1f)<<3
|
|
g = ((texel>>5)&0x3f)<<2
|
|
r = ((texel>>11)&0x1f)<<3
|
|
b |= b>>5
|
|
g |= g>>6
|
|
r |= r>>5
|
|
return (0xff<<24) | (b<<16) | (g<<8) | r
|
|
def icolor(self,a,b,fa,fb,fc):
|
|
c = 0
|
|
for i in xrange(0,32,8):
|
|
xa = (a>>i)&0xff
|
|
xb = (b>>i)&0xff
|
|
xc = min(255,max(0,int((xa*fa + xb*fb)/fc)))
|
|
c |= xc<<i
|
|
return c
|
|
|
|
def S3TC(self, data, (y, x)):
|
|
out = [0 for i in xrange(x * y)]
|
|
TILE_WIDTH = 8
|
|
TILE_HEIGHT = 8
|
|
inp = 0
|
|
outp = 0
|
|
for i in xrange(0, y, TILE_HEIGHT):
|
|
for j in xrange(0, x, TILE_WIDTH):
|
|
maxw = min(x - j,TILE_WIDTH)
|
|
for k in xrange(2):
|
|
for l in xrange(2):
|
|
rgb = [0,0,0,0]
|
|
texel1 = Struct.uint16(data[inp:inp + 2], endian='>')
|
|
texel2 = Struct.uint16(data[inp + 2:inp + 4], endian='>')
|
|
rgb[0] = self.unpack_rgb565(texel1)
|
|
rgb[1] = self.unpack_rgb565(texel2)
|
|
|
|
if texel1 > texel2:
|
|
rgb[2] = self.icolor (rgb[0], rgb[1], 2, 1, 3) | 0xff000000
|
|
rgb[3] = self.icolor (rgb[1], rgb[0], 2, 1, 3) | 0xff000000
|
|
else:
|
|
rgb[2] = self.icolor (rgb[0], rgb[1], 0.5, 0.5, 1) | 0xff000000
|
|
rgb[3] = 0
|
|
|
|
# color selection (00, 01, 10, 11)
|
|
cm = map(ord,data[inp+4:inp+8])
|
|
ofs = l*4
|
|
for n in range(4):
|
|
if (ofs + outp)<(x*y):
|
|
# one row (4 texels)
|
|
if maxw > (0 + l*4):
|
|
out[ofs + 0 + outp] = rgb[(cm[n] & 0xc0) >> 6];
|
|
if maxw > (1 + l*4):
|
|
out[ofs + 1 + outp] = rgb[(cm[n] & 0x30) >> 4];
|
|
if maxw > (2 + l*4):
|
|
out[ofs + 2 + outp] = rgb[(cm[n] & 0x0c) >> 2];
|
|
if maxw > (3 + l*4):
|
|
out[ofs + 3 + outp] = rgb[(cm[n] & 0x03) >> 0];
|
|
ofs += x
|
|
inp += 8
|
|
outp += x * 4
|
|
outp += maxw - x * 8
|
|
outp += x * (TILE_HEIGHT - 1)
|
|
|
|
return ''.join(Struct.uint32(p) for p in out)
|
|
|
|
class Object(object):
|
|
def __init__(self, name):
|
|
self.Frame = 0
|
|
self.Name = name
|
|
self.Alpha = 255
|
|
self.Animations = 0
|
|
def __str__(self):
|
|
return self.Name
|
|
|
|
class Pane(Object):
|
|
def __init__(self, name, flags=0x104, alpha=255, coords=[0,0,0,0,0,0,1,1,0,0]):
|
|
Object.__init__(self, name)
|
|
self.Flags = flags
|
|
self.Alpha = alpha
|
|
self.Coords = list(coords)
|
|
self.Children = []
|
|
self.Enabled = True
|
|
|
|
def getX(self): return self.Coords[0]
|
|
def setX(self,v): self.Coords[0] = v
|
|
def getY(self): return self.Coords[1]
|
|
def setY(self,v): self.Coords[1] = v
|
|
def getMagX(self): return self.Coords[6]
|
|
def setMagX(self,v): self.Coords[6] = v
|
|
def getMagY(self): return self.Coords[7]
|
|
def setMagY(self,v): self.Coords[7] = v
|
|
def getW(self): return self.Coords[8]
|
|
def setW(self,v): self.Coords[8] = v
|
|
def getH(self): return self.Coords[9]
|
|
def setH(self,v): self.Coords[9] = v
|
|
def getRot(self): return self.Coords[5]
|
|
def setRot(self,v): self.Coords[5] = v
|
|
|
|
X = property(getX,setX)
|
|
Y = property(getY,setY)
|
|
MagX = property(getMagX,setMagX)
|
|
MagY = property(getMagY,setMagY)
|
|
Width = property(getW,setW)
|
|
Height = property(getH,setH)
|
|
Rot = property(getRot,setRot)
|
|
|
|
def Add(self, child):
|
|
self.Children.append(child)
|
|
|
|
def AnimDone(self):
|
|
if not Object.AnimDone(self):
|
|
return False
|
|
|
|
for child in self.Children:
|
|
if not child.AnimDone():
|
|
return False
|
|
|
|
return True
|
|
|
|
class Picture(Pane):
|
|
def __init__(self, name, flags=0x504, alpha=255, coords=[0,0,0,0,0,0,1,1,0,0], unk=[0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff], mat=0, flags2=0x100, matcoord=[(0,0),(1,0),(0,1),(1,1)]):
|
|
Pane.__init__(self, name, flags, alpha, coords)
|
|
self.Unk = unk
|
|
self.Material = mat
|
|
self.Flags2 = flags2
|
|
self.MaterialCoords = list(matcoord)
|
|
|
|
class ItemList(object):
|
|
HDRLEN=4
|
|
LSIZE=4
|
|
OFFSET=0
|
|
IS_ATOM=True
|
|
def __init__(self, data=None):
|
|
self.Items = []
|
|
if data is not None:
|
|
self.unpack(data)
|
|
|
|
def add(self,n):
|
|
self.Items.append(n)
|
|
|
|
def unpack(self, data):
|
|
pos = 0
|
|
if self.IS_ATOM:
|
|
pos = 8
|
|
count = self.__unpkcnt__(data[pos:])
|
|
pos = self.__getlistoff__(data[pos:])
|
|
for i in xrange(count):
|
|
off = Struct.uint32(data[pos:pos+4], endian='>') + self.OFFSET
|
|
if i == (count-1):
|
|
next = len(data)
|
|
else:
|
|
next = Struct.uint32(data[pos+self.LSIZE:pos+self.LSIZE+4], endian='>') + self.OFFSET
|
|
pos += self.LSIZE
|
|
self.unpack_item(i, data[off:next])
|
|
# sanity check
|
|
#dp = self.pack()
|
|
#assert dp == data
|
|
|
|
def pack(self,extra=None):
|
|
|
|
extradata = ""
|
|
if extra is not None:
|
|
extradata = extra.pack()
|
|
|
|
data = ""
|
|
|
|
offsets = []
|
|
|
|
for n,i in enumerate(self.Items):
|
|
offsets.append(len(data))
|
|
data += self.pack_item(n,i)
|
|
|
|
listlen = len(self.Items) * self.LSIZE
|
|
|
|
head = self.__mkheader__()
|
|
outdata = ""
|
|
if self.IS_ATOM:
|
|
outdata += self.FOURCC + Struct.uint32((len(extradata) + listlen + len(data) + 8 + len(head) + 3) &(~3), endian='>')
|
|
outdata += head
|
|
|
|
dataoff = len(outdata) + listlen - self.OFFSET
|
|
assert dataoff >= 0
|
|
|
|
for n in offsets:
|
|
outdata += Struct.uint32(n + dataoff, endian='>')
|
|
if self.LSIZE > 4:
|
|
outdata += (self.LSIZE-4)*"\x00"
|
|
|
|
outdata += data
|
|
outdata += extradata
|
|
|
|
if len(outdata)%4 != 0:
|
|
outdata += (4-len(outdata)%4)*"\x00"
|
|
|
|
return outdata
|
|
|
|
def __mkheader__(self):
|
|
return Struct.uint16(len(self.Items), endian='>') + "\x00\x00"
|
|
|
|
def __unpkcnt__(self, data):
|
|
return Struct.uint16(data[0:2], endian='>')
|
|
|
|
def __getlistoff__(self, data):
|
|
off=self.HDRLEN
|
|
if self.IS_ATOM:
|
|
off += 8
|
|
return off
|
|
|
|
def __getitem__(self, n):
|
|
return self.Items[n]
|
|
|
|
def __len__(self):
|
|
return len(self.Items)
|
|
|
|
class StdAtom(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.FourCC = Struct.string(4)
|
|
self.Size = Struct.uint32
|
|
def pack(self):
|
|
self.FourCC = self.FOURCC
|
|
self.Size = len(self)
|
|
return Struct.pack(self)
|
|
|
|
class Brlyt(object):
|
|
class BrlytHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Magic = Struct.string(4)
|
|
self.Unk = Struct.uint32
|
|
self.Size = Struct.uint32
|
|
self.UnkCount = Struct.uint16
|
|
self.AtomCount = Struct.uint16
|
|
|
|
class BrlytLYT1(StdAtom):
|
|
FOURCC = "lyt1"
|
|
def __format__(self):
|
|
StdAtom.__format__(self)
|
|
self.Flag = Struct.uint8
|
|
self.pad = Struct.uint8[3]
|
|
self.Width = Struct.float
|
|
self.Height = Struct.float
|
|
|
|
class BrlytTexture(object):
|
|
def __init__(self, name, texture=None):
|
|
self.Name = name
|
|
self.Texture = texture
|
|
self.GLTexture = None
|
|
def create_texture(self):
|
|
self.GLTexture = self.Texture.create_texture(Texture)
|
|
|
|
class BrlytTXL1(ItemList):
|
|
LSIZE = 8
|
|
OFFSET = 12
|
|
FOURCC = "txl1"
|
|
def __init__(self, archive=None, data=None):
|
|
self.Archive = archive
|
|
ItemList.__init__(self, data)
|
|
|
|
def unpack_item(self, i, data):
|
|
fn = data.split('\0', 1)[0]
|
|
print fn
|
|
tex = TPL(self.Archive.Files['./arc/timg/' + fn.lower()]).Textures[0]
|
|
self.Items.append(Brlyt.BrlytTexture(fn, tex))
|
|
def pack_item(self, i, item):
|
|
return item.Name + "\0"
|
|
|
|
class BrlytMAT1(ItemList):
|
|
LSIZE = 4
|
|
OFFSET = 0
|
|
FOURCC = "mat1"
|
|
def __init__(self, data=None):
|
|
ItemList.__init__(self, data)
|
|
|
|
def unpack_item(self, num, data):
|
|
mtl = Brlyt.BrlytMaterial()
|
|
mtl.unpack(data)
|
|
self.Items.append(mtl)
|
|
|
|
def pack_item(self, i, item):
|
|
return item.pack()
|
|
|
|
class BrlytMaterialFlags(object):
|
|
def __init__(self,value=None):
|
|
if value is not None:
|
|
self.unpack(value)
|
|
def unpack(self,value):
|
|
self.value = value
|
|
self.NumTextures = value & 15
|
|
self.NumCoords = (value>>4) & 15
|
|
self.m_2 = (value>>8) & 15
|
|
self.m_4 = bool((value>>12) & 1)
|
|
self.m_6 = (value>>13) & 3
|
|
self.m_5 = (value>>15) & 7
|
|
self.m_3 = (value>>18) & 31
|
|
self.m_9 = bool((value>>23) & 1)
|
|
self.m_10 = bool((value>>24) & 1)
|
|
self.m_7 = bool((value>>25) & 1)
|
|
self.m_8 = bool((value>>27) & 1)
|
|
|
|
def show(self):
|
|
print "Flags: %08x"%self.value
|
|
print "ReserveGXMem("
|
|
print " r4 =",self.NumTextures
|
|
print " r5 =",self.NumCoords
|
|
print " r6 =",self.m_2
|
|
print " r7 =",self.m_3
|
|
print " r8 =",self.m_4
|
|
print " r9 =",self.m_5
|
|
print " r10 =",self.m_6
|
|
print " 0x8(%sp) =",self.m_7
|
|
print " 0xC(%sp) =",self.m_8
|
|
print " 0x10(%sp) =",self.m_9
|
|
print " 0x14(%sp) =",self.m_10
|
|
print ")"
|
|
|
|
def pack(self):
|
|
val = 0
|
|
val |= self.NumTextures
|
|
val |= self.NumCoords << 4
|
|
val |= self.m_2 << 8
|
|
val |= self.m_4 << 12
|
|
val |= self.m_6 << 13
|
|
val |= self.m_5 << 15
|
|
val |= self.m_3 << 18
|
|
val |= self.m_9 << 23
|
|
val |= self.m_10 << 24
|
|
val |= self.m_7 << 25
|
|
val |= self.m_8 << 27
|
|
return val
|
|
|
|
class BrlytMatHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
|
|
def __format__(self):
|
|
self.Name = Struct.string(0x14)
|
|
self.Color1 = Struct.uint16[4]
|
|
self.Color2 = Struct.uint16[4]
|
|
self.Color3 = Struct.uint16[4]
|
|
self.Blah = Struct.uint32[4]
|
|
self.Flags = Struct.uint32
|
|
|
|
# 0-3 num 0-8
|
|
# 4-7 num 0-10
|
|
# 8-11 num 0-8
|
|
# 12 bool
|
|
# 13-14 num 0-3
|
|
# 15-17 num 0-4
|
|
# 18-22 num 0-16
|
|
# 23 bool
|
|
# 24 bool
|
|
# 25 bool a
|
|
# 27 bool
|
|
|
|
|
|
# if 25, xtra word
|
|
# if 27, xtra word
|
|
# if 12, xtra word
|
|
# 13-14 * 0x14
|
|
# 15-17 * 4
|
|
# 18-22 * 16
|
|
# if 23, xtra word
|
|
# if 24, xtra word
|
|
|
|
class BrlytMaterial(object):
|
|
|
|
def __init__(self, name=None):
|
|
if name is not None:
|
|
self.Name = name
|
|
self.Color1 = [0,0,0,0]
|
|
self.Color2 = [255,255,255,255]
|
|
self.Color3 = [255,255,255,255]
|
|
self.Blah = [0xffffffff,0xffffffff,0xffffffff,0xffffffff]
|
|
self.Textures = []
|
|
self.TextureCoords = []
|
|
self.SthB = []
|
|
self.SthI = None
|
|
self.SthJ = None
|
|
self.SthC = None
|
|
self.SthD = []
|
|
self.SthE = []
|
|
self.SthF = []
|
|
self.SthG = 0x77000000
|
|
self.SthH = None
|
|
|
|
def unpack(self, data):
|
|
wii.chexdump(data)
|
|
hdr = Brlyt.BrlytMatHeader()
|
|
hdr.unpack(data)
|
|
self.FlagData = Brlyt.BrlytMaterialFlags(hdr.Flags)
|
|
self.FlagData.show()
|
|
|
|
self.Name = hdr.Name.split("\0",1)[0]
|
|
self.Color1 = hdr.Color1
|
|
self.Color2 = hdr.Color2
|
|
self.Color3 = hdr.Color3
|
|
self.Blah = hdr.Blah
|
|
|
|
ptr = 0x40
|
|
|
|
self.Textures = []
|
|
self.TextureCoords = []
|
|
self.SthB = []
|
|
self.SthI = None
|
|
self.SthJ = None
|
|
self.SthC = None
|
|
self.SthD = []
|
|
self.SthE = []
|
|
self.SthF = []
|
|
self.SthG = None
|
|
self.SthH = None
|
|
|
|
for i in range(self.FlagData.NumTextures):
|
|
texid = Struct.uint16(data[ptr:ptr+2], endian='>')
|
|
texcs = Struct.uint8(data[ptr+2], endian='>')
|
|
texct = Struct.uint8(data[ptr+3], endian='>')
|
|
print " * Texture: %04x %d %d"%(texid,texcs,texct)
|
|
self.Textures.append((texid,texcs,texct))
|
|
ptr += 4
|
|
for i in range(self.FlagData.NumCoords):
|
|
dat = []
|
|
for j in range(5):
|
|
dat.append(Struct.float(data[ptr+j*4:ptr+j*4+4], endian='>'))
|
|
print " * Coords: [",', '.join(["%f"%x for x in dat]),"]"
|
|
ptr += 0x14
|
|
self.TextureCoords.append(dat)
|
|
for i in range(self.FlagData.m_2):
|
|
dat = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
self.SthB.append(dat)
|
|
print " * SthB: %08x"%dat
|
|
ptr += 0x04
|
|
if self.FlagData.m_7:
|
|
self.SthI = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
print " SthI: %08x"%self.SthI
|
|
ptr += 0x04
|
|
if self.FlagData.m_8:
|
|
self.SthJ = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
print " SthJ: %08x"%self.SthJ
|
|
ptr += 0x04
|
|
if self.FlagData.m_4:
|
|
self.SthC = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
print " SthC: %08x"%self.SthC
|
|
ptr += 0x04
|
|
for i in range(self.FlagData.m_6):
|
|
dat = []
|
|
for j in range(5):
|
|
dat.append(Struct.float(data[ptr+j*4:ptr+j*4+4], endian='>'))
|
|
self.SthD.append(dat)
|
|
print " * SthD: [",', '.join(["%f"%x for x in dat]),"]"
|
|
ptr += 0x14
|
|
for i in range(self.FlagData.m_5):
|
|
dat = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
self.SthE.append(dat)
|
|
print " * SthE: %08x"%dat
|
|
ptr += 0x04
|
|
for i in range(self.FlagData.m_3):
|
|
dat = []
|
|
for j in range(4):
|
|
dat.append(Struct.uint32(data[ptr+j*4:ptr+j*4+4], endian='>'))
|
|
self.SthF.append(dat)
|
|
print " * SthF: [",', '.join(["%08x"%x for x in dat]),"]"
|
|
ptr += 0x10
|
|
if self.FlagData.m_9:
|
|
dat = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
self.SthG = dat
|
|
print " SthG: %08x"%dat
|
|
ptr += 0x04
|
|
if self.FlagData.m_10:
|
|
dat = Struct.uint32(data[ptr:ptr+4], endian='>')
|
|
self.SthH = dat
|
|
print " SthH: %08x"%dat
|
|
ptr += 0x04
|
|
|
|
#assert ptr == len(data)
|
|
|
|
def pack(self):
|
|
|
|
self.FlagData = Brlyt.BrlytMaterialFlags()
|
|
|
|
self.FlagData.NumTextures = len(self.Textures)
|
|
self.FlagData.NumCoords = len(self.TextureCoords)
|
|
self.FlagData.m_2 = len(self.SthB)
|
|
self.FlagData.m_7 = self.SthI is not None
|
|
self.FlagData.m_8 = self.SthJ is not None
|
|
self.FlagData.m_4 = self.SthC is not None
|
|
self.FlagData.m_6 = len(self.SthD)
|
|
self.FlagData.m_5 = len(self.SthE)
|
|
self.FlagData.m_3 = len(self.SthF)
|
|
self.FlagData.m_9 = self.SthG is not None
|
|
self.FlagData.m_10 = self.SthH is not None
|
|
|
|
self.Flags = self.FlagData.pack()
|
|
|
|
hdr = Brlyt.BrlytMatHeader()
|
|
|
|
hdr.Name = self.Name + "\x00"*(0x14-len(self.Name))
|
|
hdr.Color1 = self.Color1
|
|
hdr.Color2 = self.Color2
|
|
hdr.Color3 = self.Color3
|
|
hdr.Blah = self.Blah
|
|
hdr.Flags = self.Flags
|
|
|
|
data = hdr.pack()
|
|
|
|
for i in self.Textures:
|
|
data += Struct.uint16(i[0], endian='>')
|
|
data += Struct.uint8(i[1], endian='>')
|
|
data += Struct.uint8(i[2], endian='>')
|
|
for i in self.TextureCoords:
|
|
for j in i:
|
|
data += Struct.float(j, endian='>')
|
|
for i in self.SthB:
|
|
data += Struct.uint32(i, endian='>')
|
|
if self.SthI is not None:
|
|
data += Struct.uint32(self.SthI, endian='>')
|
|
if self.SthJ is not None:
|
|
data += Struct.uint32(self.SthJ, endian='>')
|
|
if self.SthC is not None:
|
|
data += Struct.uint32(self.SthC, endian='>')
|
|
for i in self.SthD:
|
|
for j in i:
|
|
data += Struct.float(j, endian='>')
|
|
for i in self.SthE:
|
|
data += Struct.uint32(i, endian='>')
|
|
for i in self.SthF:
|
|
for j in i:
|
|
data += Struct.uint32(j, endian='>')
|
|
if self.SthG is not None:
|
|
data += Struct.uint32(self.SthG, endian='>')
|
|
if self.SthH is not None:
|
|
data += Struct.uint32(self.SthH, endian='>')
|
|
return data
|
|
|
|
class BrlytPAN1(StdAtom):
|
|
FOURCC = "pan1"
|
|
def __format__(self):
|
|
StdAtom.__format__(self)
|
|
self.Flags = Struct.uint16
|
|
self.Alpha = Struct.uint16
|
|
self.Name = Struct.string(0x18)
|
|
self.Coords = Struct.float[10]
|
|
|
|
class BrlytPIC1(BrlytPAN1):
|
|
FOURCC = "pic1"
|
|
def __format__(self):
|
|
Brlyt.BrlytPAN1.__format__(self)
|
|
self.unk = Struct.uint8[16]
|
|
self.Material = Struct.uint16
|
|
self.Flags2 = Struct.uint16
|
|
self.MaterialCoords = Struct.float[8]
|
|
|
|
def __init__(self, archive, data, renderer):
|
|
self.Archive = archive
|
|
self.Textures = Brlyt.BrlytTXL1()
|
|
self.Materials = Brlyt.BrlytMAT1()
|
|
self.LastPic = None
|
|
self.RootPane = None
|
|
self.RootPaneName = None
|
|
self.Objects = {}
|
|
self.PanePath = []
|
|
self.PaneId = 0
|
|
self.Language = "ENG"
|
|
self.Renderer = renderer
|
|
|
|
if data != None:
|
|
self.Unpack(data)
|
|
|
|
def Unpack(self, data):
|
|
pos = 0
|
|
header = self.BrlytHeader()
|
|
header.unpack(data[:len(header)])
|
|
print "BRLYT header:"
|
|
wii.chexdump(data[:len(header)])
|
|
print " unk1: %08x"%header.Unk
|
|
print " unkc: %08x"%header.UnkCount
|
|
pos += len(header)
|
|
|
|
print " %d atoms"%header.AtomCount
|
|
|
|
assert header.Magic == 'RLYT'
|
|
|
|
for i in xrange(header.AtomCount):
|
|
atom = StdAtom()
|
|
atom.unpack(data[pos:pos+len(atom)])
|
|
|
|
atomdata = data[pos:pos+atom.Size]
|
|
if atom.FourCC == 'txl1':
|
|
self.TXL1(atomdata)
|
|
elif atom.FourCC == 'lyt1':
|
|
self.LYT1(atomdata)
|
|
elif atom.FourCC == 'mat1':
|
|
self.MAT1(atomdata)
|
|
elif atom.FourCC == 'pan1':
|
|
self.PAN1(atomdata)
|
|
elif atom.FourCC == 'pas1':
|
|
self.PAS1(atomdata)
|
|
elif atom.FourCC == 'pae1':
|
|
self.PAE1(atomdata)
|
|
elif atom.FourCC == 'pic1':
|
|
self.PIC1(atomdata)
|
|
elif atom.FourCC == "grp1":
|
|
self.GRP1(atomdata)
|
|
else:
|
|
print "Unknown FOURCC:",atom.FourCC
|
|
wii.chexdump(atomdata)
|
|
|
|
pos += atom.Size
|
|
|
|
def _PackObject(self, object):
|
|
atoms = 0
|
|
if isinstance(object,Pane):
|
|
atoms += 1
|
|
if isinstance(object,Picture):
|
|
atom = Brlyt.BrlytPIC1()
|
|
else:
|
|
atom = Brlyt.BrlytPAN1()
|
|
atom.Name = object.Name + "\x00"*(0x18-len(object.Name))
|
|
atom.Alpha = int(object.Alpha * 256)
|
|
atom.Flags = object.Flags
|
|
atom.Coords = object.Coords
|
|
if isinstance(object,Picture):
|
|
atom.Flags2 = object.Flags2
|
|
atom.Material = object.Material
|
|
atom.unk = object.Unk
|
|
atom.MaterialCoords = sum(map(list,object.MaterialCoords),[])
|
|
data = atom.pack()
|
|
|
|
if len(object.Children) > 0:
|
|
atoms += 2
|
|
data += "pas1\x00\x00\x00\x08"
|
|
for child in object.Children:
|
|
ac, dc = self._PackObject(child)
|
|
data += dc
|
|
atoms += ac
|
|
data += "pae1\x00\x00\x00\x08"
|
|
return atoms, data
|
|
else:
|
|
raise ValueError("Unknown object: "+repr(object))
|
|
|
|
def Pack(self, extra=None):
|
|
|
|
header = self.BrlytHeader()
|
|
header.Unk = 0xfeff0008
|
|
header.UnkCount = 0x10
|
|
header.Magic = "RLYT"
|
|
header.AtomCount = 0
|
|
|
|
data = ""
|
|
data += self.mkLYT1()
|
|
header.AtomCount+=1
|
|
|
|
data += self.mkTXL1()
|
|
header.AtomCount+=1
|
|
|
|
data += self.mkMAT1(extra)
|
|
header.AtomCount+=1
|
|
|
|
atoms, ndata = self._PackObject(self.RootPane)
|
|
data += ndata
|
|
header.AtomCount += atoms
|
|
|
|
#uuugly. TODO: fix this crap.
|
|
data += "grp1\x00\x00\x00\x1cRootGroup\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
header.AtomCount += 1
|
|
|
|
header.Size = len(data) + len(header)
|
|
data = header.pack() + data
|
|
|
|
return data
|
|
|
|
def mkLYT1(self):
|
|
lyt1 = self.BrlytLYT1()
|
|
lyt1.Width = self.Width
|
|
lyt1.Height = self.Height
|
|
lyt1.Flag = 1
|
|
for n in range(3):
|
|
lyt1.pad[n] = 0
|
|
return lyt1.pack()
|
|
|
|
def mkTXL1(self):
|
|
return self.Textures.pack()
|
|
|
|
def mkMAT1(self, extra=None):
|
|
return self.Materials.pack(extra)
|
|
|
|
def LYT1(self, data):
|
|
lyt1 = self.BrlytLYT1()
|
|
lyt1.unpack(data)
|
|
self.Width = lyt1.Width
|
|
self.Height = lyt1.Height
|
|
print "LYT1: %f x %f, flag %d"%(self.Width, self.Height, lyt1.Flag)
|
|
self.Renderer.Create(int(self.Width), int(self.Height))
|
|
|
|
def TXL1(self, data):
|
|
self.Textures = self.BrlytTXL1(self.Archive, data)
|
|
for i in self.Textures:
|
|
i.create_texture()
|
|
|
|
def ApplyMask(self, image, mask):
|
|
print "Making mask:",image,mask
|
|
print image.width,image.height,mask.width,mask.height
|
|
if image.height != mask.height or image.width != mask.width:
|
|
raise ValueError("Mask dimensions must be equal to mask dimensions")
|
|
newdata = [0 for x in xrange(image.height * image.width * 4)]
|
|
|
|
for pix in xrange(image.height * image.width):
|
|
newdata[pix*4 + 0] = image.data[pix*4 + 0]
|
|
newdata[pix*4 + 1] = image.data[pix*4 + 1]
|
|
newdata[pix*4 + 2] = image.data[pix*4 + 2]
|
|
newdata[pix*4 + 3] = mask.data[pix*4 + 0]
|
|
|
|
return ImageData(image.width, image.height, 'RGBA', ''.join(newdata))
|
|
|
|
def MAT1(self, data):
|
|
self.Materials = self.BrlytMAT1(data)
|
|
|
|
def _addpane(self, p):
|
|
self.CurPane = p
|
|
self.Objects[p.Name] = p
|
|
if self.RootPane is None:
|
|
self.RootPane = p
|
|
else:
|
|
self.PanePath[-1].Add(p)
|
|
|
|
def PAN1(self, data):
|
|
wii.chexdump(data)
|
|
pane = Brlyt.BrlytPAN1()
|
|
pane.unpack(data)
|
|
p = Pane(pane.Name.split('\0',1)[0], pane.Flags, pane.Alpha/256.0, pane.Coords)
|
|
print 'Pane %s (flags %04x, alpha %f): ' % (p.Name, pane.Flags, pane.Alpha),pane.Coords
|
|
self._addpane(p)
|
|
|
|
def PAS1(self, data):
|
|
wii.chexdump(data)
|
|
if self.CurPane is None:
|
|
raise ValueError("No current pane!")
|
|
self.PanePath.append(self.CurPane)
|
|
print "Pane start:",'.'.join(map(str,self.PanePath))
|
|
self.CurPane = None
|
|
|
|
def PAE1(self, data):
|
|
print "Pane end:",'.'.join(map(str,self.PanePath))
|
|
self.PanePath = self.PanePath[:-1]
|
|
|
|
def PIC1(self, data):
|
|
wii.chexdump(data)
|
|
pic = Brlyt.BrlytPIC1()
|
|
pic.unpack(data)
|
|
mc = []
|
|
for i in range(4):
|
|
mc.append(pic.MaterialCoords[i*2:i*2+2])
|
|
print mc
|
|
p = Picture(pic.Name.split("\0",1)[0], pic.Flags, pic.Alpha/256.0, pic.Coords, pic.unk, pic.Material, pic.Flags2, mc)
|
|
print repr(p.Name)
|
|
mat = self.Materials[pic.Material]
|
|
if mat is not None:
|
|
self._addpane(p)
|
|
else:
|
|
print 'Picture %s with null material!'
|
|
|
|
def GRP1(self, data):
|
|
wii.chexdump(data)
|
|
if len(data) < 0x1c:
|
|
pass
|
|
lang = data[0x8:0x18].split('\0', 1)[0]
|
|
nitems = Struct.uint16(data[0x18:0x1a], endian='>')
|
|
p = 0x1c
|
|
items = []
|
|
for i in xrange(nitems):
|
|
items.append(data[p:].split('\0', 1)[0])
|
|
p += 0x10
|
|
for i in items:
|
|
try:
|
|
if lang != self.Language:
|
|
self.Objects[i].Enabled = False
|
|
else:
|
|
self.Objects[i].Enabled = True
|
|
except:
|
|
pass
|
|
|
|
class Brlan(object):
|
|
A_COORD = "RLPA"
|
|
A_PARM = "RLVC"
|
|
C_X = 0
|
|
C_Y = 1
|
|
C_ANGLE = 5
|
|
C_MAGX = 6
|
|
C_MAGY = 7
|
|
C_WIDTH = 8
|
|
C_HEIGHT = 9
|
|
P_ALPHA = 0x10
|
|
class BrlanHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Magic = Struct.string(4)
|
|
self.Unk = Struct.uint32
|
|
self.Size = Struct.uint32
|
|
self.UnkCount = Struct.uint16
|
|
self.AtomCount = Struct.uint16
|
|
|
|
def __init__(self, data=None):
|
|
self.Anim = None
|
|
if data != None:
|
|
self.Unpack(data)
|
|
else:
|
|
self.Anim = Brlan.BrlanPAI1()
|
|
|
|
def Unpack(self, data):
|
|
header = self.BrlanHeader()
|
|
header.unpack(data[:len(header)])
|
|
pos = len(header)
|
|
|
|
assert header.Magic == 'RLAN'
|
|
|
|
for i in xrange(header.AtomCount):
|
|
atom = StdAtom()
|
|
atom.unpack(data[pos:pos+len(atom)])
|
|
atomdata = data[pos:pos+atom.Size]
|
|
pos += atom.Size
|
|
|
|
if atom.FourCC == 'pai1':
|
|
self.PAI1(atomdata)
|
|
else:
|
|
print "Unknown animation atom: %s"%atom.FourCC
|
|
|
|
class BrlanPAI1(ItemList):
|
|
LSIZE=4
|
|
OFFSET=0
|
|
FOURCC="pai1"
|
|
HDRLEN=12
|
|
def unpack(self, data):
|
|
self.FrameCount = Struct.uint16(data[8:10], endian='>')
|
|
print self.FrameCount
|
|
ItemList.unpack(self, data)
|
|
def __mkheader__(self):
|
|
hdr = Struct.uint16(self.FrameCount, endian='>')
|
|
hdr += Struct.uint16(0x100, endian='>')
|
|
hdr += Struct.uint32(len(self.Items), endian='>')
|
|
hdr += Struct.uint32(0x14, endian='>')
|
|
return hdr
|
|
def __unpkcnt__(self, data):
|
|
return Struct.uint32(data[4:8], endian='>')
|
|
def __getlistoff__(self, data):
|
|
return Struct.uint32(data[8:12], endian='>')
|
|
def unpack_item (self, num, data):
|
|
self.Items.append(Brlan.BrlanAnimSet(data))
|
|
def pack(self, offset, count):
|
|
self.FrameCount = count
|
|
self.Offset = offset
|
|
return ItemList.pack(self)
|
|
def pack_item (self, num, item):
|
|
return item.pack(self.Offset)
|
|
def __getitem__(self, n):
|
|
if isinstance(n,str):
|
|
for i in self.Items:
|
|
if i.Name == n:
|
|
return i
|
|
raise KeyError("Key %s not found"%n)
|
|
else:
|
|
return ItemList.__getitem__(self, n)
|
|
|
|
def __contains__(self, n):
|
|
if isinstance(n,str):
|
|
for i in self.Items:
|
|
if i.Name == n:
|
|
return True
|
|
return False
|
|
else:
|
|
return n in self.Items
|
|
|
|
|
|
|
|
class BrlanAnimSet(ItemList):
|
|
LSIZE=4
|
|
OFFSET=0
|
|
HDRLEN=0x18
|
|
IS_ATOM=False
|
|
def __init__(self, data=None):
|
|
if data is not None and len(data)<0x14:
|
|
self.Name = data
|
|
ItemList.__init__(self, None)
|
|
else:
|
|
ItemList.__init__(self, data)
|
|
def unpack(self, data):
|
|
self.Name = data[:0x14].split("\0",1)[0]
|
|
print self.Name
|
|
ItemList.unpack(self, data)
|
|
def __mkheader__(self):
|
|
hdr = self.Name + "\x00" * (0x14-len(self.Name))
|
|
hdr += Struct.uint8(len(self.Items), endian='>')
|
|
hdr += "\x00\x00\x00"
|
|
return hdr
|
|
def __unpkcnt__(self, data):
|
|
return Struct.uint8(data[0x14:0x15], endian='>')
|
|
def unpack_item (self, num, data):
|
|
self.Items.append(Brlan.BrlanAnimClass(data))
|
|
def pack_item (self, num, item):
|
|
return item.pack(self.Offset)
|
|
def pack(self, offset):
|
|
self.Offset = offset
|
|
return ItemList.pack(self)
|
|
def __getitem__(self, n):
|
|
if isinstance(n,str):
|
|
for i in self.Items:
|
|
if i.Type == n:
|
|
return i
|
|
raise KeyError("Key %s not found"%n)
|
|
else:
|
|
return ItemList.__getitem__(self, n)
|
|
|
|
class BrlanAnimClass(ItemList):
|
|
LSIZE=4
|
|
OFFSET=0
|
|
HDRLEN=8
|
|
IS_ATOM=False
|
|
|
|
class BrlanAnimClassIterator(object):
|
|
def __init__(self, cl):
|
|
self.cl = cl
|
|
self.item = 0
|
|
def __iter__(self):
|
|
return self
|
|
def next(self):
|
|
if self.item == len(self.cl.Items):
|
|
raise StopIteration()
|
|
else:
|
|
self.item += 1
|
|
return self.cl.Items[self.item-1]
|
|
|
|
def __init__(self, data=None):
|
|
if data is not None and len(data) == 4:
|
|
self.Type = data
|
|
ItemList.__init__(self, None)
|
|
else:
|
|
ItemList.__init__(self, data)
|
|
def unpack(self, data):
|
|
self.Type = data[0:4]
|
|
print " ",self.Type
|
|
ItemList.unpack(self, data)
|
|
def __mkheader__(self):
|
|
hdr = self.Type
|
|
hdr += Struct.uint8(len(self.Items), endian='>')
|
|
hdr += "\x00\x00\x00"
|
|
return hdr
|
|
def __unpkcnt__(self, data):
|
|
return Struct.uint8(data[4:5], endian='>')
|
|
def unpack_item (self, num, data):
|
|
self.Items.append(Brlan.BrlanAnim(data))
|
|
def pack(self, offset):
|
|
self.Offset = offset
|
|
return ItemList.pack(self)
|
|
def pack_item (self, num, item):
|
|
return item.pack(self.Offset)
|
|
def __getitem__(self, n):
|
|
for i in self.Items:
|
|
if i.Type == n:
|
|
return i
|
|
raise KeyError("Key %d not found"%n)
|
|
def __iter__(self):
|
|
return self.BrlanAnimClassIterator(self)
|
|
|
|
class BrlanAnim(object):
|
|
def __init__(self, data=None):
|
|
self.Triplets = []
|
|
self.Unk = 0x200
|
|
self.Type = 0
|
|
if data is not None and isinstance(data,int):
|
|
self.Type = data
|
|
else:
|
|
self.unpack(data)
|
|
def unpack(self, data):
|
|
self.Type = Struct.uint16(data[0:2], endian='>')
|
|
self.Unk = Struct.uint16(data[2:4], endian='>')
|
|
count = Struct.uint16(data[4:6], endian='>')
|
|
pos = Struct.uint32(data[8:12], endian='>')
|
|
print " ",self.Type
|
|
if self.Unk == 0x200:
|
|
print " Triplets:"
|
|
for i in range(count):
|
|
F = Struct.float(data[pos+0:pos+4], endian='>')
|
|
P = Struct.float(data[pos+4:pos+8], endian='>')
|
|
D = Struct.float(data[pos+8:pos+12], endian='>')
|
|
print " %11f %11f %11f"%(F,P,D)
|
|
self.Triplets.append((F,P,D))
|
|
pos += 12
|
|
else:
|
|
print " Unknown format: %04x"%self.Unk
|
|
def pack(self, offset):
|
|
self.Triplets.sort(key=lambda x: x[0])
|
|
t = self.Triplets
|
|
self.Triplets = []
|
|
lt = (None, None, None)
|
|
for i in t:
|
|
if i == lt:
|
|
pass
|
|
elif i[0] == lt[0]:
|
|
raise ValueError("Duplicate triplet frame numbers with different data")
|
|
else:
|
|
self.Triplets.append(i)
|
|
|
|
out = Struct.uint16(self.Type, endian='>')
|
|
out += Struct.uint16(self.Unk, endian='>')
|
|
out += Struct.uint16(len(self.Triplets), endian='>')
|
|
out += "\x00\x00"
|
|
out += Struct.uint32(0xc, endian='>')
|
|
for F,P,D in self.Triplets:
|
|
out += Struct.float(F-offset, endian='>')
|
|
out += Struct.float(P, endian='>')
|
|
out += Struct.float(D, endian='>')
|
|
return out
|
|
def add(self, F, P=None, D=None):
|
|
if isinstance(F,tuple):
|
|
self.Triplets.append(F)
|
|
else:
|
|
self.Triplets.append((F,P,D))
|
|
def rep(self, start, end, times):
|
|
replist = []
|
|
for i in self.Triplets:
|
|
if i[0] >= start and i[0] <= end:
|
|
replist.append(i)
|
|
if len(replist) == 0:
|
|
return
|
|
off = end-start
|
|
for i in range(times-1):
|
|
for a,b,c in replist:
|
|
self.Triplets.append((a+off,b,c))
|
|
off += end-start
|
|
a,b,c = replist[0]
|
|
if a == start:
|
|
self.Triplets.append((a+off,b,c))
|
|
def repsimple(self, start, end, times, sp, sd, ep, ed):
|
|
step = float(end-start) / times
|
|
self.Triplets.append((start, sp, sd))
|
|
self.Triplets.append((start+step/2.0, ep, ed))
|
|
self.rep(start, start+step, times)
|
|
def calc(self, frame):
|
|
for tn in range(len(self.Triplets)):
|
|
if self.Triplets[tn][0] > frame:
|
|
if tn == 0:
|
|
return self.Triplets[0][1]
|
|
else:
|
|
start, value1, coef1 = self.Triplets[tn-1]
|
|
end, value2, coef2 = self.Triplets[tn]
|
|
t = (frame - start) / (end - start)
|
|
nf = end - start
|
|
return \
|
|
coef1 * 1 * nf * (t + t**3 - 2*t**2) + \
|
|
coef2 * 1 * nf * (t**3 - t**2) + \
|
|
value1 * (1 + (2*t**3 - 3*t**2)) + \
|
|
value2 * (-2*t**3 + 3*t**2)
|
|
else:
|
|
return self.Triplets[-1][1]
|
|
def __getitem__(self, n):
|
|
return self.Triplets[n]
|
|
def PAI1(self, data):
|
|
assert self.Anim is None
|
|
self.Anim = self.BrlanPAI1(data)
|
|
return
|
|
def Pack(self, a=None, b=None):
|
|
|
|
if a is None and b is None:
|
|
frames = self.FrameCount
|
|
offset = 0
|
|
elif a is not None and b is None:
|
|
frames = a
|
|
offset = 0
|
|
elif a is not None and b is not None:
|
|
frames = b-a
|
|
offset = a
|
|
else:
|
|
raise ValueError("WTF are you doing")
|
|
|
|
header = self.BrlanHeader()
|
|
header.Unk = 0xfeff0008
|
|
header.UnkCount = 0x10
|
|
header.Magic = "RLAN"
|
|
header.AtomCount = 1
|
|
|
|
data = self.Anim.pack(offset, frames)
|
|
|
|
header.Size = len(data) + len(header)
|
|
data = header.pack() + data
|
|
|
|
return data
|
|
|
|
if have_pyglet:
|
|
class BannerWindow(window.Window):
|
|
def on_resize(self, width, height):
|
|
glViewport(0, 0, width, height)
|
|
glMatrixMode(GL_PROJECTION)
|
|
glLoadIdentity()
|
|
glOrtho(-width/2, width/2, -height/2, height/2, -1, 1)
|
|
glMatrixMode(GL_MODELVIEW)
|
|
|
|
|
|
class Renderer(object):
|
|
def __init__(self):
|
|
self.Brlyt = None
|
|
self.Brlan = None
|
|
|
|
def Create(self, width, height):
|
|
self.Width = width
|
|
self.Height = height
|
|
print "Render: %f x %f"%(self.Width,self.Height)
|
|
self.Window = BannerWindow(self.Width, self.Height)
|
|
self.Window.set_exclusive_mouse(False)
|
|
|
|
glClearColor(0.0, 0.0, 0.0, 0.0)
|
|
glClearDepth(1.0)
|
|
glDepthFunc(GL_LEQUAL)
|
|
glEnable(GL_DEPTH_TEST)
|
|
|
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
|
|
glEnable(GL_BLEND)
|
|
glEnable(GL_TEXTURE_2D)
|
|
clock.set_fps_limit(60)
|
|
|
|
|
|
def Render(self, item, wireframe=False):
|
|
if not item.Enabled:
|
|
return
|
|
if isinstance(item, Picture):
|
|
|
|
mat = self.Brlyt.Materials[item.Material]
|
|
texture = self.Brlyt.Textures[mat.Textures[0][0]].GLTexture
|
|
mtc = mat.TextureCoords[0]
|
|
x, y, a, b, c, rot, xsc, ysc, xs, ys = item.Coords[:10]
|
|
|
|
xs *= xsc
|
|
ys *= ysc
|
|
#print item.Name,x,y,xs,ys
|
|
xc, yc = x-(xs/2),y-(ys/2)
|
|
|
|
glPushMatrix()
|
|
glMatrixMode( GL_MODELVIEW )
|
|
glTranslatef( x, y, 0)
|
|
glRotatef(rot,0,0,1)
|
|
glScalef(xs,ys,0)
|
|
|
|
if not wireframe:
|
|
col = [x/255.0 for x in list(mat.Color2)]
|
|
col[3] = col[3] * item.Alpha / 255.0
|
|
|
|
tw = texture.tex_coords[6]
|
|
th = texture.tex_coords[7]
|
|
|
|
mc = item.MaterialCoords
|
|
|
|
glMatrixMode( GL_TEXTURE )
|
|
glPushMatrix()
|
|
glTranslatef(0.5,0.5,0)
|
|
glTranslatef(mtc[0],mtc[1],0)
|
|
glScalef(mtc[3],mtc[4],0)
|
|
glTranslatef(-0.5,-0.5,0)
|
|
array = (GLfloat * 32)(
|
|
mc[0][0] * tw, mc[0][1] * th, 0, 1.,
|
|
-0.5, 0.5, 0, 1.,
|
|
mc[1][0] * tw, mc[1][1] * th, 0, 1.,
|
|
0.5, 0.5, 0, 1.,
|
|
mc[3][0] * tw, mc[3][1] * th, 0, 1.,
|
|
0.5, -0.5, 0, 1.,
|
|
mc[2][0] * tw, mc[2][1] * th, 0, 1.,
|
|
-0.5, -0.5, 0, 1.)
|
|
|
|
glColor4f(*col)
|
|
glPushAttrib(GL_ENABLE_BIT)
|
|
glEnable(texture.target)
|
|
glBindTexture(texture.target, texture.id)
|
|
if mat.Textures[0][1] == 0x0:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
|
elif mat.Textures[0][1] == 0x1:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_REPEAT)
|
|
elif mat.Textures[0][1] == 0x2:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
|
|
else:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
|
|
if mat.Textures[0][2] == 0x0:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
|
elif mat.Textures[0][2] == 0x1:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_REPEAT)
|
|
elif mat.Textures[0][2] == 0x2:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT)
|
|
else:
|
|
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
|
|
|
|
glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
|
|
glInterleavedArrays(GL_T4F_V4F, 0, array)
|
|
glDrawArrays(GL_QUADS, 0, 4)
|
|
glPopClientAttrib()
|
|
glPopAttrib()
|
|
|
|
glBindTexture(texture.target, 0)
|
|
glPopMatrix()
|
|
glMatrixMode( GL_MODELVIEW )
|
|
|
|
else:
|
|
|
|
glColor3f(1,0,0)
|
|
glBegin(GL_LINE_STRIP)
|
|
glVertex2f(-0.5,-0.5)
|
|
glVertex2f(0.5,-0.5)
|
|
glVertex2f(0.5,0.5)
|
|
glVertex2f(-0.5,0.5)
|
|
glVertex2f(-0.5,-0.5)
|
|
glColor3f(1,1,1)
|
|
glEnd()
|
|
pass
|
|
|
|
glPopMatrix()
|
|
if isinstance(item, Pane):
|
|
if item.Coords is not None:
|
|
x, y, a, b, c, rot, xsc, ysc, xs, ys = item.Coords[:10]
|
|
else:
|
|
x = y = 0
|
|
xs = ys = 0
|
|
xsc = 1
|
|
ysc = 1
|
|
glPushMatrix()
|
|
glTranslatef(x, y, 0)
|
|
glRotatef(rot,0,0,1)
|
|
glScalef(xsc,ysc,0)
|
|
#glScalef(xs,ys,1)
|
|
for child in item.Children:
|
|
self.Render(child,wireframe)
|
|
glPopMatrix()
|
|
|
|
def Animate(self, frame):
|
|
for set in self.Brlan.Anim:
|
|
for clss in set:
|
|
for anim in clss:
|
|
#print set.Name, clss.Type, anim.Type, anim.calc(frame), frame
|
|
if clss.Type == Brlan.A_COORD:
|
|
self.Brlyt.Objects[set.Name].Coords[anim.Type] = anim.calc(frame)
|
|
elif clss.Type == Brlan.A_PARM:
|
|
if anim.Type == Brlan.P_ALPHA:
|
|
self.Brlyt.Objects[set.Name].Alpha = anim.calc(frame)
|
|
|
|
def MainLoop(self, loop):
|
|
frame = 0
|
|
print "Starting mainloop: loop =",loop
|
|
print "Length in frames:",self.Brlan.Anim.FrameCount
|
|
while not self.Window.has_exit:
|
|
self.Window.dispatch_events()
|
|
self.Window.clear()
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
|
glLoadIdentity()
|
|
glColor4f(1.0, 1.0, 1.0, 1.0)
|
|
|
|
self.Render(self.Brlyt.RootPane,False)
|
|
#self.Render(self.Brlyt.RootPane,True)
|
|
|
|
#clock.tick()
|
|
|
|
self.Window.flip()
|
|
if self.Brlan is not None:
|
|
self.Animate(frame)
|
|
if frame >= self.Brlan.Anim.FrameCount:
|
|
if not loop:
|
|
print "Animation done!"
|
|
return True
|
|
else:
|
|
print "Looping..."
|
|
frame = -1
|
|
frame += 1
|
|
|
|
class Alameda(object):
|
|
class IMETHeader(Struct):
|
|
__endian__ = Struct.BE
|
|
def __format__(self):
|
|
self.Zeroes = Struct.uint8[0x40]
|
|
self.IMET = Struct.string(4)
|
|
self.Fixed = Struct.uint8[8]
|
|
self.Sizes = Struct.uint32[3]
|
|
self.Flag1 = Struct.uint32
|
|
self.Names = Struct.string(0x2A<<1, encoding='utf-16-be', stripNulls=True)[7]
|
|
self.Zeroes = Struct.uint8[0x348]
|
|
self.MD5 = Struct.uint8[0x10]
|
|
|
|
def __init__(self, fn, type='icon'):
|
|
renderer = Renderer()
|
|
|
|
fp = file(fn, 'rb')
|
|
|
|
imet = self.IMETHeader()
|
|
try:
|
|
imet.unpack(fp.read(len(imet)))
|
|
except:
|
|
imet = None
|
|
if imet is None or imet.IMET != 'IMET':
|
|
imet = self.IMETHeader()
|
|
fp.seek(0x40)
|
|
imet.unpack(fp.read(len(imet)))
|
|
assert imet.IMET == 'IMET'
|
|
|
|
print 'English title: %s' % imet.Names[1]
|
|
|
|
|
|
root = U8(fp.read())
|
|
if type == 'icon':
|
|
banner = U8(IMD5(root.Files['./meta/icon.bin']))
|
|
renderer.Brlyt = Brlyt(banner, banner.Files['./arc/blyt/icon.brlyt'], renderer)
|
|
fd = None
|
|
try:
|
|
fd = banner.Files['./arc/anim/icon.brlan']
|
|
except:
|
|
pass
|
|
if fd is not None:
|
|
renderer.Brlan = Brlan(fd)
|
|
|
|
loop = True
|
|
else:
|
|
banner = U8(IMD5(root.Files['./meta/banner.bin']))
|
|
renderer.Brlyt = Brlyt(banner, banner.Files['./arc/blyt/banner.brlyt'], renderer)
|
|
loop = not banner.Files.has_key('./arc/anim/banner_start.brlan')
|
|
if not loop:
|
|
renderer.Brlan = Brlan(banner.Files['./arc/anim/banner_start.brlan'])
|
|
else:
|
|
renderer.Brlan = Brlan(banner.Files['./arc/anim/banner.brlan'])
|
|
|
|
if renderer.MainLoop(loop) and type == 'banner' and not loop:
|
|
renderer.Brlan = Brlan(banner.Files['./arc/anim/banner_loop.brlan'])
|
|
renderer.MainLoop(True)
|
|
|
|
if __name__=='__main__':
|
|
Alameda(*sys.argv[1:])
|