diff --git a/bemani/format/dxt.py b/bemani/format/dxt.py index d817b38..42383fd 100644 --- a/bemani/format/dxt.py +++ b/bemani/format/dxt.py @@ -13,29 +13,6 @@ import struct from typing import List, Optional, Tuple -def unpack(endian: str, _bytes: bytes) -> int: - STRUCT_SIGNS = { - 1: 'B', - 2: 'H', - 4: 'I', - 8: 'Q' - } - return struct.unpack(endian + STRUCT_SIGNS[len(_bytes)], _bytes)[0] - - -# This function converts RGB565 format to raw pixels -def unpackRGB(packed: int) -> Tuple[int, int, int, int]: - R = (packed >> 11) & 0x1F - G = (packed >> 5) & 0x3F - B = (packed) & 0x1F - - R = (R << 3) | (R >> 2) - G = (G << 2) | (G >> 4) - B = (B << 3) | (B >> 2) - - return (R, G, B, 255) - - class DXTBuffer: def __init__(self, width: int, height: int): self.width = width @@ -44,53 +21,60 @@ class DXTBuffer: self.block_countx = self.width // 4 self.block_county = self.height // 4 - self.decompressed_buffer: List[Optional[bytes]] = [None] * ((width * height) * 2) # Dont ask me why + self.decompressed_buffer: List[Optional[bytes]] = [None] * ((width * height) * 2) - def DXT5Decompress(self, filedata: bytes, endian: str = "<") -> bytes: + def unpackRGB(self, packed: int) -> Tuple[int, int, int]: + # This function converts RGB565 format to raw pixels + R = (packed >> 11) & 0x1F + G = (packed >> 5) & 0x3F + B = (packed) & 0x1F + + R = (R << 3) | (R >> 2) + G = (G << 2) | (G >> 4) + B = (B << 3) | (B >> 2) + + return (R, G, B) + + def swapbytes(self, data: bytes, swap: bool) -> bytes: + if swap: + return b"".join([ + data[(x + 1):(x + 2)] + data[x:(x + 1)] + for x in range(0, len(data), 2) + ]) + return data + + def DXT5Decompress(self, filedata: bytes, swap: bool = False) -> bytes: # Loop through each block and decompress it file = io.BytesIO(filedata) for row in range(self.block_county): for col in range(self.block_countx): - # Get the alpha values - a0 = unpack(endian, file.read(1)) - a1 = unpack(endian, file.read(1)) - atable = file.read(6) - - acode0 = atable[2] | (atable[3] << 8) | (atable[4] << 16) | (atable[5] << 24) - acode1 = atable[0] | (atable[1] << 8) - - # Color 1 color 2, color look up table - c0 = unpack(endian, file.read(2)) - c1 = unpack(endian, file.read(2)) - ctable = unpack(endian, file.read(4)) + # Get the alpha values, and color lookup table + a0, a1, acode0, acode1, c0, c1, ctable = struct.unpack(" bytes: + def DXT1Decompress(self, filedata: bytes, swap: bool = False) -> bytes: # Loop through each block and decompress it file = io.BytesIO(filedata) for row in range(self.block_county): for col in range(self.block_countx): # Color 1 color 2, color look up table - c0 = unpack(endian, file.read(2)) - c1 = unpack(endian, file.read(2)) - ctable = unpack(endian, file.read(4)) + c0, c1, ctable = struct.unpack(" None: code = (ctable >> (2 * ((4 * j) + i))) & 0x03 # Get the color of the current pixel pixel_color = None - r0 = c0[0] - g0 = c0[1] - b0 = c0[2] - - r1 = c1[0] - g1 = c1[1] - b1 = c1[2] + r0, g0, b0 = self.unpackRGB(c0) + r1, g1, b1 = self.unpackRGB(c1) # Sliding scale between colors. if code == 0: @@ -136,9 +115,15 @@ class DXTBuffer: if code == 1: pixel_color = (r1, g1, b1, alpha) if code == 2: - pixel_color = ((2 * r0 + r1) // 3, (2 * g0 + g1) // 3, (2 * b0 + b1) // 3, alpha) + if c0 > c1: + pixel_color = ((2 * r0 + r1) // 3, (2 * g0 + g1) // 3, (2 * b0 + b1) // 3, alpha) + else: + pixel_color = ((r0 + r1) // 2, (g0 + g1) // 2, (b0 + b1) // 2, alpha) if code == 3: - pixel_color = ((r0 + 2 * r1) // 3, (g0 + 2 * g1) // 3, (b0 + 2 * b1) // 3, alpha) + if c0 > c1: + pixel_color = ((r0 + 2 * r1) // 3, (g0 + 2 * g1) // 3, (b0 + 2 * b1) // 3, alpha) + else: + pixel_color = (0, 0, 0, alpha) # While not surpassing the image dimensions, assign pixels the colors. if (x + i) < self.width and (y + j) < self.height: @@ -146,17 +131,9 @@ class DXTBuffer: struct.pack(' int: + def getAlpha(self, i: int, j: int, a0: int, a1: int, acode: int) -> int: # Using the same method as the colors calculate the alpha values - alpha_index = 3 * ((4 * j) + i) - alpha_code = None - - if alpha_index <= 12: - alpha_code = (acode1 >> alpha_index) & 0x07 - elif alpha_index == 15: - alpha_code = (acode1 >> 15) | ((acode0 << 1) & 0x06) - else: - alpha_code = (acode0 >> (alpha_index - 16)) & 0x07 + alpha_code = (acode >> (3 * ((4 * j) + i))) & 0x07 if alpha_code == 0: alpha = a0 diff --git a/bemani/utils/afputils.py b/bemani/utils/afputils.py index 1e1a927..65172d5 100644 --- a/bemani/utils/afputils.py +++ b/bemani/utils/afputils.py @@ -633,11 +633,14 @@ class AFPFile: ) elif fmt == 0x16: # DXT1 format. Game references D3D9 DXT1 texture format. + # Konami seems to have screwed up with DDR PS3 where they + # swap every other byte in the format, even though its specified + # as little-endian by all DXT1 documentation. dxt = DXTBuffer(width, height) img = Image.frombuffer( 'RGBA', (width, height), - dxt.DXT1Decompress(raw_data[64:], endian=self.endian), + dxt.DXT1Decompress(raw_data[64:], swap=self.endian != "<"), 'raw', 'RGBA', 0, @@ -645,11 +648,14 @@ class AFPFile: ) elif fmt == 0x1A: # DXT5 format. Game references D3D9 DXT5 texture format. + # Konami seems to have screwed up with DDR PS3 where they + # swap every other byte in the format, even though its specified + # as little-endian by all DXT5 documentation. dxt = DXTBuffer(width, height) img = Image.frombuffer( 'RGBA', (width, height), - dxt.DXT5Decompress(raw_data[64:], endian=self.endian), + dxt.DXT5Decompress(raw_data[64:], swap=self.endian != "<"), 'raw', 'RGBA', 0,