1
0
mirror of synced 2024-11-12 01:00:46 +01:00

Fix several bugs in DXT5 decompressor, renders fonts much closer to the game now.

This commit is contained in:
Jennifer Taylor 2020-11-06 18:40:41 +00:00
parent abc97bfdcb
commit a26d8f4b64
2 changed files with 47 additions and 44 deletions

View File

@ -20,7 +20,7 @@ def unpack(_bytes: bytes) -> int:
4: 'I',
8: 'Q'
}
return struct.unpack('<' + STRUCT_SIGNS[len(_bytes)], _bytes)[0]
return struct.unpack('>' + STRUCT_SIGNS[len(_bytes)], _bytes)[0]
# This function converts RGB565 format to raw pixels
@ -51,7 +51,6 @@ class DXTBuffer:
file = io.BytesIO(filedata)
for row in range(self.block_county):
for col in range(self.block_countx):
# Get the alpha values
a0 = unpack(file.read(1))
a1 = unpack(file.read(1))
@ -68,10 +67,10 @@ class DXTBuffer:
# The 4x4 Lookup table loop
for j in range(4):
for i in range(4):
alpha = self.getAlpha(j, i, a0, a1, acode0, acode1)
alpha = self.getAlpha(i, j, a0, a1, acode0, acode1)
self.getColors(
row * 4,
col * 4,
row * 4,
i,
j,
ctable,
@ -97,8 +96,8 @@ class DXTBuffer:
for j in range(4):
for i in range(4):
self.getColors(
row * 4,
col * 4,
row * 4,
i,
j,
ctable,
@ -120,7 +119,7 @@ class DXTBuffer:
c1: Tuple[int, int, int, int],
alpha: int,
) -> None:
code = (ctable >> (2 * (4 * i + j))) & 0x03 # Get the color of the current pixel
code = (ctable >> (2 * ((4 * i) + j))) & 0x03 # Get the color of the current pixel
pixel_color = None
r0 = c0[0]
@ -131,39 +130,26 @@ class DXTBuffer:
g1 = c1[1]
b1 = c1[2]
# Main two colors
# Sliding scale between colors.
if code == 0:
pixel_color = (r0, g0, b0, alpha)
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 code == 3:
pixel_color = ((r0 + 2 * r1) // 3, (g0 + 2 * g1) // 3, (b0 + 2 * b1) // 3, alpha)
# Use the lookup table to determine the other two colors
if c0 > c1:
if code == 2:
pixel_color = ((2 * r0 + r1) // 3, (2 * g0 + g1) // 3, (2 * b0 + b1) // 3, alpha)
if code == 3:
pixel_color = ((r0 + 2 * r1) // 3, (g0 + 2 * g1) // 3, (b0 + 2 * b1) // 3, alpha)
else:
if code == 2:
pixel_color = ((r0 + r1) // 2, (g0 + g1) // 2, (b0 + b1) // 2, alpha)
if code == 3:
pixel_color = (0, 0, 0, alpha)
# While not surpassing the image dimensions, assign pixels the colors
if (x + i) < self.width:
# While not surpassing the image dimensions, assign pixels the colors.
if (x + i) < self.width and (y + j) < self.height:
self.decompressed_buffer[(y + j) * self.width + (x + i)] = (
struct.pack('<B', pixel_color[0]) +
struct.pack('<B', pixel_color[1]) +
struct.pack('<B', pixel_color[2]) +
struct.pack('<B', pixel_color[3])
struct.pack('<BBBB', *pixel_color)
)
def getAlpha(self, i: int, j: int, a0: int, a1: int, acode0: int, acode1: int) -> int:
# Using the same method as the colors calculate the alpha values
alpha = 255
alpha_index = 3 * (4 * j + i)
alpha_index = 3 * ((4 * j) + i)
alpha_code = None
if alpha_index <= 12:
@ -185,14 +171,6 @@ class DXTBuffer:
alpha = 0
elif alpha_code == 7:
alpha = 255
elif alpha_code == 5:
alpha = (1 * a0 + 4 * a1) // 5
elif alpha_code == 4:
alpha = (2 * a0 + 3 * a1) // 5
elif alpha_code == 3:
alpha = (3 * a0 + 2 * a1) // 5
elif alpha_code == 2:
alpha = (4 * a0 + 1 * a1) // 5
else:
alpha = 0 # For safety
alpha = ((6 - alpha_code) * a0 + (alpha_code - 1) * a1) // 5
return alpha

View File

@ -106,7 +106,13 @@ def descramble_pman(package_data: bytes, offset: int, endian: str, obfuscated: b
return names
def extract(filename: str, output_dir: str, *, write: bool, verbose: bool = False) -> None:
def extract(
filename: str,
output_dir: str, *,
write: bool = True,
verbose: bool = False,
raw: bool = False,
) -> None:
with open(filename, "rb") as fp:
data = fp.read()
@ -230,10 +236,19 @@ def extract(filename: str, output_dir: str, *, write: bool, verbose: bool = Fals
else:
# Now, see if we can extract this data.
print(f"Writing {filename} texture data...")
magic, _, _, _, width, height, fmt, _, flags2, flags1 = struct.unpack(
f"{endian}4sIIIHHBBBB",
magic, _, _, length, width, height, fmtflags = struct.unpack(
f"{endian}4sIIIHHI",
raw_data[0:24],
)
if length != len(raw_data):
raise Exception("Invalid texture length!")
fmt = fmtflags & 0xFF
flags1 = (fmtflags >> 24) & 0xFF
flags2 = (fmtflags >> 16) & 0xFF
unk1 = 3 if (flags1 & 0xF == 1) else 1
unk2 = 3 if ((flags1 >> 4) & 0xF == 1) else 1
unk3 = 1 if (flags2 & 0xF == 1) else 2
unk4 = 1 if ((flags2 >> 4) & 0xF == 1) else 2
if endian == "<" and magic != b"TDXT":
raise Exception("Unexpected texture format!")
@ -246,7 +261,6 @@ def extract(filename: str, output_dir: str, *, write: bool, verbose: bool = Fals
'RGB', (width, height), raw_data[64:], 'raw', 'RGB',
)
# 0x10 = Seems to be some sort of RGB with color swapping.
# 0x11 = Unknown entirely, PS3 format. Looks to be one byte per pixel.
# 0x15 = Looks like RGB but reversed (end and beginning bytes swapped).
# 0x16 = DTX1 format, when I encounter this I'll hook it up.
elif fmt == 0x1A:
@ -261,7 +275,6 @@ def extract(filename: str, output_dir: str, *, write: bool, verbose: bool = Fals
0,
1,
)
img = ImageOps.flip(img).rotate(-90, expand=True)
# 0x1E = I have no idea what format this is.
# 0x1F = 16bpp, possibly grayscale? Maybe 555A or 565 color?
elif fmt == 0x20:
@ -278,7 +291,7 @@ def extract(filename: str, output_dir: str, *, write: bool, verbose: bool = Fals
if img:
with open(f"{filename}.png", "wb") as bfp:
img.save(bfp, format='PNG')
else:
if not img or raw:
with open(f"{filename}.raw", "wb") as bfp:
bfp.write(raw_data)
with open(f"{filename}.xml", "w") as sfp:
@ -698,9 +711,21 @@ def main() -> int:
action="store_true",
help="Display verbuse debugging output.",
)
parser.add_argument(
"-r",
"--write-raw",
action="store_true",
help="Always write raw texture files.",
)
args = parser.parse_args()
extract(args.file, args.dir, write=not args.pretend, verbose=args.verbose)
extract(
args.file,
args.dir,
write=not args.pretend,
verbose=args.verbose,
raw=args.write_raw,
)
return 0