Texture Data Section

This section has a bunch of textures

Header

Offset Type Description
0x0 uint16 Number of textures
0x2 uint16 Number of CLUTs (color lookup tables)

Texture Descriptor

Immediately following the header (at offset 0x4 from the start of the file) is an array of texture descriptors.

Offset Type Description
0x0 uint32 Image Data Pointer (relative to header)
0x4 uint32 Palette Data Pointer (relative to header) (might be null)
0x8 uint16 Image height
0xa uint16 Image width
0xc bool Edge LOD enable (?)
0xd uint8 Min LOD (?)
0xe uint8 Max LOD (?)
0xf bool Unpacked?
0x10 byte[7] Unknown/padding
0x17 uint8 Image format
0x18 uint16 Palette entry count
0x1a uint8 Palette format
0x1b byte[5] Unknown/padding

This is probably a version of the TEX0 format. I extract the textures by shuffling around the data to create a valid TPL file and then converting it to a png with wimgt. I don't know where the LODBias, wrapS, wrapT, minFilter, and magFilter are in the header, so I assume they're 0.

def toFile(self, path):
    dataLength = self.height * self.width * {0:4,1:8,2:8,3:16,4:16,5:16,6:32,8:4,9:8,10:16,14:4}[self.format] >> 3
    hasPalette = self.paletteDataPtr > 0
    fname = path + '.tpl'
    pngname = path + '.png'

    out = open(fname, 'wb')
    # TEXPalette
    out.write(itb(0x0020AF30, 4))
    out.write(itb(1, 4))
    out.write(itb(0xC, 4))

    # TEXDescriptor
    out.write(itb(0x14, 4))
    if hasPalette:
        out.write(itb(0x38, 4))
    else:
        out.write(itb(0, 4))

    # TEXHeader
    out.write(itb(self.height, 2))
    out.write(itb(self.width, 2))
    out.write(itb(self.format, 4))
    out.write(itb(0x40 + hasPalette * 0x20, 4))
    out.write(itb(self.wrapS, 4))
    out.write(itb(self.wrapT, 4))
    out.write(itb(self.minFilter, 4))
    out.write(itb(self.magFilter, 4))
    out.write(itb(self.LODBias, 4))
    out.write(itb(self.edgeLODEnable, 1))
    out.write(itb(self.minLOD, 1))
    out.write(itb(self.maxLOD, 1))
    out.write(itb(self.unpacked, 1))

    paletteDataLen = self.paletteEntries * 2
    paletteDataOffset = 0
    paletteDataPad = 0
    if hasPalette:
        paletteDataOffset = 0x60 + dataLength
        mod = paletteDataOffset % 0x20
        if mod != 0:
            paletteDataPad = 0x20 - mod
            paletteDataOffset += paletteDataPad
        # Palette header
        out.write(itb(self.paletteEntries, 2))
        out.write(itb(1, 1))
        out.write(itb(0, 1))
        out.write(itb(self.paletteFormat, 4))
        out.write(itb(paletteDataOffset, 4))

    # Pad to multiple of 0x20 (0x40 or 0x60 here)
    out.write(itb(0, 0x8 + hasPalette * 0x14))
    self.parent.seek(self.dataPtr)
    out.write(self.parent.read(dataLength))
    if hasPalette:
        out.write(itb(0, paletteDataPad))
        self.parent.seek(self.paletteDataPtr)
        out.write(self.parent.read(paletteDataLen))
    out.close()
    os.system('wimgt decode -q -d ' + pngname + ' ' + fname)
    os.remove(fname)