Upgrade get_theirsce_from_scpk function

This commit is contained in:
Stewie
2022-08-12 21:45:06 -04:00
committed by fortiersteven
parent 5b18683575
commit 67832e8262
2 changed files with 181 additions and 6 deletions

View File

@@ -74,12 +74,25 @@ class ToolsTOR(ToolsTales):
self.extract_TheirSce_XML(scpk_file)
self.id = 1
def get_theirsce_from_scpk(self, scpk, scpk_file_name, debug=False)->bytes:
header = scpk.read(4)
if header != b"SCPK":
# sys.exit(f"{file} is not a .scpk file!")
raise ValueError("File is not a .scpk file!")
def get_theirsce_from_scpk(self, scpk_file_name, debug=False)->bytes:
with open(scpk_file_name,"rb") as scpk:
header = scpk.read(4)
if header != b"SCPK":
# sys.exit(f"{file} is not a .scpk file!")
raise ValueError("File is not a .scpk file!")
scpk.read(4)
nbFiles = struct.unpack("<L", scpk.read(4))[0]
scpk.read(4)
filesSize = []
for i in range(nbFiles):
filesSize.append(struct.unpack("<L", scpk.read(4))[0])
for i in range(nbFiles):
data = scpk.read(filesSize[i])
scpk.read(4)
nbFiles = struct.unpack("<L", scpk.read(4))[0]

162
pak2.py Normal file
View File

@@ -0,0 +1,162 @@
import struct, sys, os, io
from dataclasses import dataclass, field
@dataclass
class pak2_chunks:
theirsce: bytes = b""
lipsync: bytes = b""
unused: bytes = b""
image_unk1: bytes = b""
image_unk2: bytes = b""
image_blobs: list = field(default_factory=list)
@dataclass
class pak2_file:
#offsets: list
char_count: int = 0
slot_count: int = 0
image_count: int = 0
chunks: pak2_chunks = pak2_chunks()
def get_file_name_noext(path):
return os.path.splitext(os.path.basename(path))[0]
def get_parent_folder(path):
return os.path.normpath(os.path.join(path, os.pardir))
def insert_padded_chunk(file: bytes, chunk: bytes, alignment: int = 4):
file.write(chunk)
pad = (alignment - (file.tell() % alignment)) % alignment
file.write(b"\x00" * pad)
return file.tell()
def get_theirsce_from_pak2(file: bytes)->bytes:
offsets = struct.unpack("<3I", file[:12])
# Handle null 2nd offset because of course that's a thing
if offsets[1] == 0:
return file[offsets[0] : offsets[2]]
else:
return file[offsets[0] : offsets[1]]
def get_data(file: bytes)->pak2_file:
offsets = struct.unpack("<6I", file[:24])
data = pak2_file()
data.char_count = struct.unpack("<H", file[0x18:0x1A])[0]
data.slot_count = struct.unpack("<H", file[0x1A:0x1C])[0] # 0x20 always
data.image_count = struct.unpack("<H", file[0x1C:0x1E])[0]
# Handle null 2nd offset because of course that's a thing
if offsets[1] == 0:
data.chunks.theirsce = file[offsets[0] : offsets[2]]
else:
data.chunks.theirsce = file[offsets[0] : offsets[1]]
size = struct.unpack("<I", file[offsets[1] : offsets[1] + 4])[0] + 0x10
data.chunks.lipsync = file[offsets[1] : offsets[1] + size]
data.chunks.unused = file[offsets[2] : offsets[2] + (data.char_count * 4)]
data.chunks.image_unk1 = file[offsets[3] : offsets[3] + (data.slot_count * 4)]
data.chunks.image_unk2 = file[offsets[4] : offsets[4] + (data.image_count * 2)]
# image_data = bytearray(file[offsets[5]:len(file)])
blob_offsets = list(
struct.unpack(
"<%dI" % (data.image_count * 2), file[offsets[5] : offsets[5] + data.image_count * 8]
)
)
del blob_offsets[1::2] #Yeet all the odd offset, they be useless
for blob in blob_offsets:
blob_size = struct.unpack("<I", file[blob : blob + 4])[0]
if blob_size == 0:
blob_size = 0x400
data.chunks.image_blobs.append(file[blob : blob + blob_size])
return data
def create_pak2(data: pak2_file)->bytes:
output = io.BytesIO()
output.seek(0)
output.write(b"\x00" * 0x20)
offsets_new = []
offsets_new.append(output.tell())
# theirsce
offsets_new.append(insert_padded_chunk(output, data.chunks.theirsce))
# lipsync
offsets_new.append(insert_padded_chunk(output, data.chunks.lipsync))
# unused
offsets_new.append(insert_padded_chunk(output, data.chunks.unused))
# unk1
offsets_new.append(insert_padded_chunk(output, data.chunks.image_unk1))
# unk2
offsets_new.append(insert_padded_chunk(output, data.chunks.image_unk2))
# images
# Create image chunk
image_chunk = b"\x00" * (data.image_count * 8) # minimum size
insert_padded_chunk(output, image_chunk, 128)
image_offsets = []
image_offsets.append(output.tell())
for blob in data.chunks.image_blobs:
image_offsets.append(insert_padded_chunk(output, blob, 128))
image_offsets = image_offsets[:-1]
image_offsets = [
val for val in image_offsets for _ in (0, 1)
] # image data offsets are duplicated
# Write image data offsets
output.seek(offsets_new[5])
output.write(struct.pack("<%dI" % len(image_offsets), *image_offsets))
# Write chunk offsets
output.seek(0)
output.write(struct.pack("<%dI" % len(offsets_new), *offsets_new))
# Write metadata
output.write(struct.pack("<H", data.char_count))
output.write(struct.pack("<H", data.slot_count))
output.write(struct.pack("<H", data.image_count))
end = output.getvalue()
return end
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage:")
print("pak2.py [pak2_path] [theirsce_path]")
sys.exit()
with open(sys.argv[1], "rb") as input:
file = input.read()
pak2 = get_data(file)
print("file size: %d" % len(file))
print("char_count: %d" % pak2.char_count)
print("slot_count: %d" % pak2.slot_count)
print("image_count: %d" % pak2.image_count)
print()
# Get new Theirsce, if there's no second arg just reinsert original
if len(sys.argv) > 2:
with open(sys.argv[2], "rb+") as f:
theirsce = f.read()
with open(sys.argv[2] + ".new", "wb+") as output:
output.write(create_pak2(pak2))
print("Done!")