NWPlayer123 icon

parse_bti

NWPlayer123 | PRO | 10/22/18 03:42:12 AM UTC | 0 ⭐ | 480 👁️ | Never ⏰ | []
Python |

11.56 KB

|

None

|

0 👍

/

0 👎

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

Comments