Add a few more missing bytes, assert where we always see constants.
This commit is contained in:
parent
d9f96f4b4c
commit
f7ffd9185b
@ -53,6 +53,26 @@ def get_until_null(data: bytes, offset: int) -> bytes:
|
||||
return out
|
||||
|
||||
|
||||
def cap32(val: int) -> int:
|
||||
return val & 0xFFFFFFFF
|
||||
|
||||
|
||||
def poly(val: int) -> int:
|
||||
if (val >> 31) & 1 != 0:
|
||||
return 0x4C11DB7
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def crc32(bytestream: bytes) -> int:
|
||||
# Janky 6-bit CRC for ascii names in PMAN structures.
|
||||
result = 0
|
||||
for byte in bytestream:
|
||||
for i in range(6):
|
||||
result = poly(result) ^ cap32((result << 1) | ((byte >> i) & 1))
|
||||
return result
|
||||
|
||||
|
||||
def descramble_text(text: bytes, obfuscated: bool) -> str:
|
||||
if len(text):
|
||||
if obfuscated and (text[0] - 0x20) > 0x7F:
|
||||
@ -68,12 +88,17 @@ def descramble_text(text: bytes, obfuscated: bool) -> str:
|
||||
def descramble_pman(package_data: bytes, offset: int, endian: str, obfuscated: bool) -> List[str]:
|
||||
# Unclear what the first three unknowns are, but the fourth
|
||||
# looks like it could possibly be two int16s indicating unknown?
|
||||
magic, _, _, _, numentries, _, data_offset = struct.unpack(
|
||||
magic, expect_zero, flags1, flags2, numentries, flags3, data_offset = struct.unpack(
|
||||
f"{endian}4sIIIIII",
|
||||
package_data[offset:(offset + 28)],
|
||||
)
|
||||
add_coverage(offset, 28)
|
||||
|
||||
# I have never seen the first unknown be anything other than zero,
|
||||
# so lets lock that down.
|
||||
if expect_zero != 0:
|
||||
raise Exception("Got a non-zero value for expected zero location in PMAN!")
|
||||
|
||||
if endian == "<" and magic != b"PMAN":
|
||||
raise Exception("Invalid magic value in PMAN structure!")
|
||||
if endian == ">" and magic != b"NAMP":
|
||||
@ -84,13 +109,12 @@ def descramble_pman(package_data: bytes, offset: int, endian: str, obfuscated: b
|
||||
# Jump to the offset, parse it out
|
||||
for i in range(numentries):
|
||||
file_offset = data_offset + (i * 12)
|
||||
# Really not sure on the first entry here, it looks
|
||||
# completely random, so it might be a CRC?
|
||||
_, entry_no, nameoffset = struct.unpack(
|
||||
name_crc, entry_no, nameoffset = struct.unpack(
|
||||
f"{endian}III",
|
||||
package_data[file_offset:(file_offset + 12)],
|
||||
)
|
||||
add_coverage(file_offset, 12)
|
||||
|
||||
if nameoffset == 0:
|
||||
raise Exception("Expected name offset in PMAN data!")
|
||||
|
||||
@ -99,6 +123,9 @@ def descramble_pman(package_data: bytes, offset: int, endian: str, obfuscated: b
|
||||
name = descramble_text(bytedata, obfuscated)
|
||||
names[entry_no] = name
|
||||
|
||||
if name_crc != crc32(name.encode('ascii')):
|
||||
raise Exception(f"Name CRC failed for {name}")
|
||||
|
||||
for i, name in enumerate(names):
|
||||
if name is None:
|
||||
raise Exception(f"Didn't get mapping for entry {i + 1}")
|
||||
@ -237,12 +264,32 @@ def extract(
|
||||
else:
|
||||
# Now, see if we can extract this data.
|
||||
print(f"Writing {filename} texture data...")
|
||||
magic, _, _, length, width, height, fmtflags = struct.unpack(
|
||||
f"{endian}4sIIIHHI",
|
||||
raw_data[0:24],
|
||||
(
|
||||
magic,
|
||||
header_flags1,
|
||||
header_flags2,
|
||||
length,
|
||||
width,
|
||||
height,
|
||||
fmtflags,
|
||||
expected_zero1,
|
||||
expected_zero2,
|
||||
) = struct.unpack(
|
||||
f"{endian}4sIIIHHIII",
|
||||
raw_data[0:32],
|
||||
)
|
||||
if length != len(raw_data):
|
||||
raise Exception("Invalid texture length!")
|
||||
# I have only ever observed the following values across two different games.
|
||||
# Don't want to keep the chunk around so let's assert our assumptions.
|
||||
if (expected_zero1 | expected_zero2) != 0:
|
||||
raise Exception("Found unexpected non-zero value in texture header!")
|
||||
if raw_data[32:44] != b'\0' * 12:
|
||||
raise Exception("Found unexpected non-zero value in texture header!")
|
||||
if struct.unpack(f"{endian}I", raw_data[44:48])[0] != 3:
|
||||
raise Exception("Found unexpected value in texture header!")
|
||||
if raw_data[48:64] != b'\0' * 16:
|
||||
raise Exception("Found unexpected non-zero value in texture header!")
|
||||
fmt = fmtflags & 0xFF
|
||||
|
||||
# Extract flags that the game cares about.
|
||||
@ -655,9 +702,14 @@ def extract(
|
||||
|
||||
# I am not sure what the unknown byte is for. It always appears as
|
||||
# all zeros in all files I've looked at.
|
||||
_, length, binxrpc_offset = struct.unpack(f"{endian}III", data[offset:(offset + 12)])
|
||||
expect_zero, length, binxrpc_offset = struct.unpack(f"{endian}III", data[offset:(offset + 12)])
|
||||
add_coverage(offset, 12)
|
||||
|
||||
if expect_zero != 0:
|
||||
# If we find non-zero versions of this, then that means updating the file is
|
||||
# potentially unsafe as we could rewrite it incorrectly. So, let's assert!
|
||||
raise Exception("Expected a zero in font package header!")
|
||||
|
||||
if binxrpc_offset != 0:
|
||||
benc = BinaryEncoding()
|
||||
fontdata = benc.decode(data[binxrpc_offset:(binxrpc_offset + length)])
|
||||
@ -697,13 +749,18 @@ def extract(
|
||||
# First word is always zero, as observed. I am not ENTIRELY sure that
|
||||
# the second field is length, but it lines up with everything else
|
||||
# I've observed and seems to make sense.
|
||||
_, afp_header_length, afp_header = struct.unpack(
|
||||
expect_zero, afp_header_length, afp_header = struct.unpack(
|
||||
f"{endian}III",
|
||||
data[structure_offset:(structure_offset + 12)]
|
||||
)
|
||||
add_coverage(structure_offset, 12)
|
||||
add_coverage(afp_header, afp_header_length)
|
||||
|
||||
if expect_zero != 0:
|
||||
# If we find non-zero versions of this, then that means updating the file is
|
||||
# potentially unsafe as we could rewrite it incorrectly. So, let's assert!
|
||||
raise Exception("Expected a zero in font package header!")
|
||||
|
||||
# This chunk of data is referred to by name, and then a chunk.
|
||||
anim_name_offset, anim_afp_data_length, anim_afp_data_offset = struct.unpack(
|
||||
f"{endian}III",
|
||||
|
Loading…
Reference in New Issue
Block a user