Fix several bugs in DXT5 decompressor, renders fonts much closer to the game now.
This commit is contained in:
parent
abc97bfdcb
commit
a26d8f4b64
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user