NWPlayer123

parse_bti

Oct 21st, 2018
238
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.56 KB | None | 0 0
  1. from PIL import Image
  2. from struct import unpack
  3. from math import ceil
  4. import sys
  5.  
  6. class I4_Texture:
  7.     def __init__(self, height, width, color):
  8.         self.image = Image.new("RGB", (width, height))
  9.         self.img = self.image.load()
  10.         self.width = width
  11.         self.height = height
  12.         self.color = color
  13.  
  14.     def DecodeImage(self, f): #8x8 block size
  15.         for y in range(int(ceil(self.height / 8.0))):
  16.             for x in range(int(ceil(self.width / 8.0))):
  17.                 self.DecodeBlock(f, x * 8, y * 8)
  18.  
  19.     def DecodeBlock(self, f, x, y):
  20.         for ty in range(8): #8 pixel block height
  21.             for tx in range(4): #cuz we read 2 pixels worth
  22.                 data = ord(f.read(1))
  23.                 color1, color2 = self.color.I4ToColor(data)
  24.                 try:
  25.                     self.img[x + (tx * 2), y + ty] = color1
  26.                     self.img[x + (tx * 2) + 1, y + ty] = color2
  27.                 except: pass
  28.  
  29. class I8_Texture:
  30.     def __init__(self, height, width, color):
  31.         self.image = Image.new("RGB", (width, height))
  32.         self.img = self.image.load()
  33.         self.width = width
  34.         self.height = height
  35.         self.color = color
  36.  
  37.     def DecodeImage(self, f): #8x4 block size
  38.         for y in range(int(ceil(self.height / 4.0))):
  39.             for x in range(int(ceil(self.width / 8.0))):
  40.                 self.DecodeBlock(f, x * 8, y * 4)
  41.  
  42.     def DecodeBlock(self, f, x, y):
  43.         for ty in range(4): #4 pixel block height
  44.             for tx in range(8): #8 pixel block width
  45.                 i = ord(f.read(1))
  46.                 try: self.img[x + tx, y + ty] = (i, i, i)
  47.                 except: pass
  48.  
  49. class IA4_Texture:
  50.     def __init__(self, height, width, color):
  51.         self.image = Image.new("RGBA", (width, height))
  52.         self.img = self.image.load()
  53.         self.height = height
  54.         self.width = width
  55.         self.color = color
  56.  
  57.     def DecodeImage(self, f): #8x4 block size
  58.         for y in range(int(ceil(self.height / 4.0))):
  59.             for x in range(int(ceil(self.width / 8.0))):
  60.                 self.DecodeBlock(f, x * 8, y * 4)
  61.  
  62.     def DecodeBlock(self, f, x, y):
  63.         for ty in range(4): #4 pixel block height
  64.             for tx in range(8): #8 pixel block width
  65.                 color = self.color.IA4ToColor(ord(f.read(1)))
  66.                 try: self.img[x + tx, y + ty] = color
  67.                 except: pass
  68.  
  69. class RGB565_Texture:
  70.     def __init__(self, height, width, color):
  71.         self.image = Image.new("RGB", (width, height))
  72.         self.img = self.image.load()
  73.         self.width = width
  74.         self.height = height
  75.         self.color = color
  76.  
  77.     def DecodeImage(self, f): #4x4 block size
  78.         for y in range(int(ceil(self.height / 4.0))):
  79.             for x in range(int(ceil(self.width / 4.0))):
  80.                 self.DecodeBlock(f, x * 4, y * 4)
  81.  
  82.     def DecodeBlock(self, f, x, y):
  83.         for ty in range(4): #4 pixel block height
  84.             for tx in range(4): #4 pixel block width
  85.                 pixel = unpack(">H", f.read(2))[0]
  86.                 color = self.color.RGB565ToColor(pixel)
  87.                 try: self.img[x + tx, y + ty] = color
  88.                 except: pass
  89.  
  90. class RGB5A3_Texture:
  91.     def __init__(self, height, width, color):
  92.         self.image = Image.new("RGBA", (width, height))
  93.         self.img = self.image.load()
  94.         self.width = width
  95.         self.height = height
  96.         self.color = color
  97.  
  98.     def DecodeImage(self, f): #4x4 block size
  99.         for y in range(int(ceil(self.height / 4.0))):
  100.             for x in range(int(ceil(self.width / 4.0))):
  101.                 self.DecodeBlock(f, x * 4, y * 4)
  102.  
  103.     def DecodeBlock(self, f, x, y):
  104.         for ty in range(4): #4 pixel block height
  105.             for tx in range(4): #4 pixel block width
  106.                 pixel = unpack(">H", f.read(2))[0]
  107.                 color = self.color.RGB5A3ToColor(pixel)
  108.                 try: self.img[x + tx, y + ty] = color
  109.                 except: pass
  110.  
  111. class CI8_Texture:
  112.     def __init__(self, height, width, color):
  113.         self.image = Image.new("RGBA", (width, height))
  114.         self.img = self.image.load()
  115.         self.height = height
  116.         self.width = width
  117.         self.color = color
  118.  
  119.     def DecodePalette(self, f):
  120.         info = unpack(">HBBII", f.read(12))
  121.         f.seek(info[4]) #data addr
  122.         palette = []
  123.         if info[3] == 0:
  124.             pass
  125.         elif info[3] == 1:
  126.             pass
  127.         elif info[3] == 2: #RGB5A3
  128.             for i in range(info[0]): #num_entries
  129.                 pixel = unpack(">H", f.read(2))[0]
  130.                 color = self.color.RGB5A3ToColor(pixel)
  131.                 palette.append(color)
  132.         return palette
  133.            
  134.  
  135.     def DecodeImage(self, f, palette):
  136.         for y in range(int(ceil(self.height / 4.0))):
  137.             for x in range(int(ceil(self.width / 8.0))):
  138.                 self.DecodeBlock(f, palette, x * 8, y * 4)
  139.  
  140.     def DecodeBlock(self, f, palette, x, y):
  141.         for ty in range(4):
  142.             for tx in range(8):
  143.                 index = ord(f.read(1))
  144.                 try: self.img[x + tx, y + ty] = palette[index]
  145.                 except: pass
  146.  
  147. class RGBA32_Texture:
  148.     def __init__(self, height, width, color):
  149.         self.image = Image.new("RGBA", (width, height))
  150.         self.img = self.image.load()
  151.         self.width = width
  152.         self.height = height
  153.         self.color = color #we don't need it but add anyways
  154.  
  155.     def DecodeImage(self, f):
  156.         for y in range(int(ceil(self.height / 4.0))):
  157.             for x in range(int(ceil(self.width / 4.0))):
  158.                 self.DecodeBlock(f, x * 4, y * 4)
  159.  
  160.     def DecodeBlock(self, f, x, y):
  161.         r = [];g = [];b = [];a = []
  162.         for i in range(16):
  163.             a.append(ord(f.read(1)))
  164.             r.append(ord(f.read(1)))
  165.         for i in range(16):
  166.             g.append(ord(f.read(1)))
  167.             b.append(ord(f.read(1)))
  168.         for ty in range(4):
  169.             for tx in range(4):
  170.                 i = tx + (ty * 4)
  171.                 try: self.img[x + tx, y + ty] = (r[i], g[i], b[i], a[i])
  172.                 except: pass
  173.  
  174. class CMPR_Texture:
  175.     def __init__(self, height, width, color):
  176.         self.image = Image.new("RGBA", (width, height))
  177.         self.img = self.image.load()
  178.         self.width = width
  179.         self.height = height
  180.         self.color = color
  181.  
  182.     def DecodeImage(self, f):
  183.         for y in range(int(ceil(self.height / 8.0))):
  184.             for x in range(int(ceil(self.width / 8.0))):
  185.                 self.DecodeBlock(f, x * 8, y * 8)
  186.  
  187.     def DecodeBlock(self, f, x, y):
  188.         self.DecodeTile(f.read(8), x + 0, y + 0)
  189.         self.DecodeTile(f.read(8), x + 4, y + 0)
  190.         self.DecodeTile(f.read(8), x + 0, y + 4)
  191.         self.DecodeTile(f.read(8), x + 4, y + 4)
  192.  
  193.     def DecodeTile(self, data, x, y):
  194.         c1, c2 = unpack(">2H", data[0:4])
  195.         r1, g1, b1 = self.color.RGB565ToColor(c1)
  196.         r2, g2, b2 = self.color.RGB565ToColor(c2)
  197.  
  198.         colors = [(), (), (), ()]
  199.         colors[0] = (r1, g1, b1, 0xFF)
  200.         colors[1] = (r2, g2, b2, 0xFF)
  201.  
  202.         if c1 > c2:
  203.             r3 = ((r2 - r1) >> 1) - ((r2 - r1) >> 3)
  204.             g3 = ((g2 - g1) >> 1) - ((g2 - g1) >> 3)
  205.             b3 = ((b2 - b1) >> 1) - ((b2 - b1) >> 3)
  206.  
  207.             colors[2] = (r1 + r3, g1 + g3, b1 + b3, 0xFF)
  208.             colors[3] = (r2 - r3, g2 - g3, b2 - b3, 0xFF)
  209.         else:
  210.             colors[2] = (int((r1 + r2 + 1) / 2.0),
  211.                          int((g1 + g2 + 1) / 2.0),
  212.                          int((b1 + b2 + 1) / 2.0),
  213.                          0xFF)
  214.             colors[3] = (r2, g2, b2, 0x00)
  215.  
  216.         index = unpack(">4B", data[4:])
  217.         for ty in range(4):
  218.             val = index[ty]
  219.             for tx in range(4):
  220.                 color = colors[(val >> 6) & 3]
  221.                 try:
  222.                     self.img[x + tx, y + ty] = color
  223.                     val <<= 2
  224.                 except: pass
  225.                
  226. class ColorConversion:
  227.     def __init__(self):
  228.         self.Bits3To8 = self.MakeDepthConversionTable(3, 8)
  229.         self.Bits8To3 = self.MakeDepthConversionTable(8, 3)
  230.  
  231.         self.Bits4To8 = self.MakeDepthConversionTable(4, 8)
  232.         self.Bits8To4 = self.MakeDepthConversionTable(8, 4)
  233.  
  234.         self.Bits5To8 = self.MakeDepthConversionTable(5, 8)
  235.         self.Bits8To5 = self.MakeDepthConversionTable(8, 5)
  236.  
  237.         self.Bits6To8 = self.MakeDepthConversionTable(6, 8)
  238.         self.Bits8To6 = self.MakeDepthConversionTable(8, 6)
  239.  
  240.     def MakeDepthConversionTable(self, a, b):
  241.         a = 1 << a
  242.         b = 1 << b
  243.         result = []
  244.         for i in range(a):
  245.             result.append(int(float(i) / (a - 1) * (b - 1)) & 0xFF)
  246.         return result
  247.  
  248.     def I4ToColor(self, v):
  249.         i1 = self.Bits4To8[v >> 4]
  250.         i2 = self.Bits4To8[v & 15]
  251.         return [(i1, i1, i1), (i2, i2, i2)]
  252.  
  253.     def IA4ToColor(self, v):
  254.         i = self.Bits4To8[v & 15]
  255.         a = self.Bits4To8[v >> 4]
  256.         return (i, i, i, a)
  257.    
  258.     def RGB565ToColor(self, v):
  259.         return (self.Bits5To8[(v >> 11) & 31],
  260.                 self.Bits6To8[(v >>  5) & 63],
  261.                 self.Bits5To8[(v >>  0) & 31])
  262.  
  263.     def RGB5A3ToColor(self, v):
  264.         if v & 0x8000: #RGB555
  265.             return (self.Bits5To8[(v >> 10) & 31],
  266.                     self.Bits5To8[(v >>  5) & 31],
  267.                     self.Bits5To8[(v >>  0) & 31],
  268.                     0xFF)
  269.         else: #RGB4A3
  270.             return (self.Bits4To8[(v >>  8) & 15],
  271.                     self.Bits4To8[(v >>  4) & 15],
  272.                     self.Bits4To8[(v >>  0) & 15],
  273.                     self.Bits3To8[(v >> 12) &  7])
  274. if __name__ == "__main__":
  275.     with open(sys.argv[1], "rb") as f:
  276.         name = sys.argv[1].split(".")[:-1]
  277.         name = ".".join(name)
  278.         imginfo = unpack(">2B2H2B2H2I2BH2BHI", f.read(0x20))
  279.         #we're now at 0x20 aka start of raw data
  280.         color = ColorConversion()
  281.         if imginfo[0] == 0:
  282.             tex = I4_Texture(imginfo[3], imginfo[2], color)
  283.             tex.DecodeImage(f)
  284.             tex.image.save("%s.png" % name)
  285.         elif imginfo[0] == 1:
  286.             tex = I8_Texture(imginfo[3], imginfo[2], color)
  287.             tex.DecodeImage(f)
  288.             tex.image.save("%s.png" % name)
  289.         elif imginfo[0] == 2:
  290.             tex = IA4_Texture(imginfo[3], imginfo[2], color)
  291.             tex.DecodeImage(f)
  292.             tex.image.save("%s.png" % name)
  293.         elif imginfo[0] == 4:
  294.             tex = RGB565_Texture(imginfo[3], imginfo[2], color)
  295.             tex.DecodeImage(f)
  296.             tex.image.save("%s.png" % name)
  297.         elif imginfo[0] == 5:
  298.             tex = RGB5A3_Texture(imginfo[3], imginfo[2], color)
  299.             tex.DecodeImage(f)
  300.             tex.image.save("%s.png" % name)
  301.         elif imginfo[0] == 6:
  302.             tex = RGBA32_Texture(imginfo[3], imginfo[2], color)
  303.             tex.DecodeImage(f)
  304.             tex.image.save("%s.png" % name)
  305.         elif imginfo[0] == 7:
  306.             tex = RGB565_Texture(imginfo[3], imginfo[2], color)
  307.             tex.DecodeImage(f)
  308.             tex.image.save("%s.png" % name)
  309.         #TODO: double check these:
  310.         elif imginfo[0] == 9:
  311.             tex = CI8_Texture(imginfo[3], imginfo[2], color)
  312.             assert imginfo[8] != 0 #palette
  313.             f.seek(imginfo[8])
  314.             palette = tex.DecodePalette(f)
  315.             tex.DecodeImage(f, palette)
  316.             tex.image.save("%s.png" % name)
  317.         elif imginfo[0] == 14:
  318.             tex = CMPR_Texture(imginfo[3], imginfo[2], color)
  319.             tex.DecodeImage(f)
  320.             tex.image.save("%s.png" % name)
Add Comment
Please, Sign In to add comment