from dataclasses import dataclass import struct from typing import Optional from ..formats.FileIO import FileIO from ..utils import comptolib @dataclass class pak_file: is_compressed: bool type: int data: bytes class Pak: def __init__(self) -> None: self.type = -1 self.align = False self.files: list[pak_file] = [] @staticmethod def from_path(path, type) -> "Pak": with FileIO(path) as f: self = Pak() if type != -1: self.type = type file_amount = f.read_uint32() self.files = [] blobs: list[bytes] = [] offsets: list[int] = [] sizes: list[int] = [] # Pak0 if type == 0: for _ in range(file_amount): sizes.append(f.read_uint32()) for size in sizes: blobs.append(f.read(size)) # Pak1 elif type == 1: for _ in range(file_amount): offsets.append(f.read_uint32()) sizes.append(f.read_uint32()) for offset, size in zip(offsets, sizes): f.seek(offset) blobs.append(f.read(size)) # Pak3 elif type == 3: for _ in range(file_amount): offsets.append(f.read_uint32()) f.seek(0, 2) offsets.append(f.tell()) for i, j in zip(offsets[::1], offsets[1::1]): sizes.append(j - i) for offset, size in zip(offsets, sizes): f.seek(offset) blobs.append(f.read(size)) for off in offsets: if off % 0x10 == 0: self.align = True break for blob in blobs: is_compressed = comptolib.is_compressed(blob) c_type = 0 if is_compressed: c_type = blob[0] blob = comptolib.decompress_data(blob) self.files.append(pak_file(is_compressed, c_type, blob)) return self @staticmethod def get_pak_type(data: bytes) -> Optional[int]: is_aligned = False data_size = len(data) if data_size < 0x8: return None files = struct.unpack(" pak3_header_size: calculated_size = 0 for size in struct.unpack(f"<{files}I", data[4 : (files + 1) * 4]): calculated_size += size if calculated_size == len(data) - pak3_header_size: return 0 # Test for pak1 if is_aligned: if pak1_check == first_entry: return 1 elif pak1_header_size == first_entry: return 1 # Test for pak3 previous = 0 for offset in struct.unpack(f"<{files}I", data[4 : (files + 1) * 4]): if offset > previous and offset >= pak3_header_size: previous = offset else: return None last_offset = (4 * (files + 1)) + 8 if data[last_offset:first_entry] == b"\x00" * (first_entry - last_offset): return 3 return None def to_bytes(self, type=-1) -> bytes: compose_mode = type if type != -1 else self.type if compose_mode == -1: raise ValueError("Trying to compose an invalid PAK type") # Collect blobs blobs = [] for blob in self.files: if blob.is_compressed: blobs.append(comptolib.compress_data(blob.data, version=blob.type)) else: blobs.append(blob.data) # Compose out = struct.pack("