Beispiel #1
0
    def unpack(self, reader: BinaryReader, **kwargs):
        reader.unpack_value("4s", asserted=b"TPF\0")
        self.platform = TPFPlatform(reader.unpack_value("B", offset=0xC))
        reader.byte_order = ">" if self.platform in {
            TPFPlatform.Xbox360, TPFPlatform.PS3
        } else "<"

        reader.unpack_value("i")  # data length
        file_count = reader.unpack_value("i")
        reader.unpack_value("B")  # platform
        self.tpf_flags = reader.unpack_value("B")
        if self.tpf_flags not in {0, 1, 2, 3}:
            raise ValueError(
                f"`TPF.tpf_flags` was {self.tpf_flags}, but expected 0, 1, 2, or 3."
            )
        self.encoding = reader.unpack_value("B")
        if self.encoding not in {0, 1, 2}:
            raise ValueError(
                f"`TPF.encoding` was {self.encoding}, but expected 0, 1, or 2."
            )
        reader.assert_pad(1)

        encoding = reader.get_utf_16_encoding(
        ) if self.encoding == 1 else "shift_jis_2004"
        self.textures = [
            TPFTexture.unpack_from(reader, self.platform, self.tpf_flags,
                                   encoding) for _ in range(file_count)
        ]
Beispiel #2
0
 def unpack_header(self, reader: BinaryReader) -> int:
     self.big_endian = reader.unpack_value("?", offset=0xD)
     reader.byte_order = ">" if self.big_endian else "<"
     self.bit_big_endian = reader.unpack_value("?", offset=0xE)
     reader.unpack_value("4s", asserted=b"BND3")
     self.signature = reader.unpack_value("8s").decode("ascii").rstrip("\0")
     self.flags = BinderFlags.read(reader, self.bit_big_endian)
     reader.byte_order = ">" if self.big_endian or self.flags.is_big_endian else "<"
     reader.seek(2, 1)  # skip peeked endian bytes
     reader.assert_pad(1)
     entry_count = reader.unpack_value("i")
     reader.seek(12, 1)  # skip file size
     return entry_count
Beispiel #3
0
 def from_bnd3_reader(cls, reader: BinaryReader, binder_flags: BinderFlags, bit_big_endian: bool):
     flags = BinderEntryFlags.read(reader, bit_big_endian)
     reader.assert_pad(3)
     compressed_size = reader.unpack_value("i")
     data_offset = reader.unpack_value("q" if binder_flags.has_long_offsets else "I")
     entry_id = reader.unpack_value("i") if binder_flags.has_ids else None
     if binder_flags.has_names:
         path_offset = reader.unpack_value("i")
         path = reader.unpack_string(path_offset, encoding="shift-jis")  # NOT `shift_jis_2004`
     else:
         path = None
     uncompressed_size = reader.unpack_value("i") if binder_flags.has_compression else None
     return cls(
         flags=flags,
         compressed_size=compressed_size,
         entry_id=entry_id,
         path=path,
         uncompressed_size=uncompressed_size,
         data_offset=data_offset,
     )
Beispiel #4
0
 def from_bnd4_reader(cls, reader: BinaryReader, binder_flags: BinderFlags, bit_big_endian: bool, unicode: bool):
     flags = BinderEntryFlags.read(reader, bit_big_endian)
     reader.assert_pad(3)
     assert reader.unpack_value("i") == -1
     compressed_size = reader.unpack_value("q")
     uncompressed_size = reader.unpack_value("q") if binder_flags.has_compression else None
     data_offset = reader.unpack_value("q" if binder_flags.has_long_offsets else "I")
     entry_id = reader.unpack_value("i") if binder_flags.has_ids else None
     if binder_flags.has_names:
         path_offset = reader.unpack_value("I")
         path = reader.unpack_string(path_offset, encoding="utf-16-le" if unicode else "shift-jis")
     else:
         path = None
     return cls(
         flags=flags,
         compressed_size=compressed_size,
         entry_id=entry_id,
         path=path,
         uncompressed_size=uncompressed_size,
         data_offset=data_offset,
     )
Beispiel #5
0
    def unpack_header(self, reader: BinaryReader):
        reader.unpack_value("4s", asserted=b"BND4")
        self.unknown1 = reader.unpack_value("?")
        self.unknown2 = reader.unpack_value("?")
        reader.assert_pad(3)
        self.big_endian = reader.unpack_value("?")
        self.bit_big_endian = not reader.unpack_value("?")  # note reversal
        reader.assert_pad(1)

        reader.byte_order = ">" if self.big_endian else "<"  # no need to check flags for an override in BND4

        entry_count = reader.unpack_value("i")
        reader.unpack_value("q", asserted=0x40)  # header size
        self.signature = reader.unpack_value("8s").decode("ascii").rstrip("\0")
        entry_header_size = reader.unpack_value("q")
        data_offset = reader.unpack_value(
            "q")  # end of all headers, including hash table
        self.unicode = reader.unpack_value("?")
        self.flags = BinderFlags.read(reader, self.bit_big_endian)
        self.hash_table_type = reader.unpack_value("B")
        reader.assert_pad(5)
        hash_table_offset = reader.unpack_value("q")

        flags_header_size = self.flags.get_bnd_entry_header_size()
        if entry_header_size != flags_header_size:
            raise ValueError(
                f"Expected BND entry header size {flags_header_size} based on flags\n"
                f"{self.flags:08b}, but BND header says {entry_header_size}.")
        if self.hash_table_type != 4 and hash_table_offset != 0:
            _LOGGER.warning(
                f"Found non-zero hash table offset {hash_table_offset}, but header says this BHD has no hash "
                f"table.")

        entry_headers = [
            BinderEntryHeader.from_bnd4_reader(reader, self.flags,
                                               self.bit_big_endian,
                                               self.unicode)
            for _ in range(entry_count)
        ]

        if self.hash_table_type == 4:
            # Save the initial hash table.
            reader.seek(hash_table_offset)
            self._most_recent_hash_table = reader.read(data_offset -
                                                       hash_table_offset)

        return entry_headers
Beispiel #6
0
    def unpack_header(self, reader: BinaryReader):
        reader.unpack_value("4s", asserted=b"BND4")
        self.unknown1 = reader.unpack_value("?")
        self.unknown2 = reader.unpack_value("?")
        reader.assert_pad(3)
        self.big_endian = reader.unpack_value("?")
        self.bit_big_endian = not reader.unpack_value("?")  # note reversal
        reader.assert_pad(1)

        reader.byte_order = ">" if self.big_endian else "<"  # no need to check flags for an override in BND4

        entry_count = reader.unpack_value("i")
        reader.unpack_value("q", asserted=0x40)  # header size
        self.signature = reader.unpack_value("8s").decode("ascii").rstrip("\0")
        entry_header_size = reader.unpack_value("q")
        data_offset = reader.unpack_value(
            "q")  # end of all headers, including hash table
        self.unicode = reader.unpack_value("?")
        self.flags = BinderFlags.read(reader, self.bit_big_endian)
        self.hash_table_type = reader.unpack_value("B")
        reader.assert_pad(5)
        hash_table_offset = reader.unpack_value("q")

        return entry_count, entry_header_size, hash_table_offset, data_offset
Beispiel #7
0
    def unpack_from(
        cls,
        reader: BinaryReader,
        platform: TPFPlatform,
        tpf_flags: int,
        encoding: str,
        tpf_path: tp.Union[None, str, Path] = None,
    ):
        self = cls()
        self.tpf_path = tpf_path

        file_offset = reader.unpack_value("I")
        file_size = reader.unpack_value("i")

        self.format = reader.unpack_value("B")
        self.texture_type = TextureType(reader.unpack_value("B"))
        self.mipmaps = reader.unpack_value("B")
        self.texture_flags = reader.unpack_value("B")
        if self.texture_flags not in {0, 1, 2, 3}:
            raise ValueError(
                f"`TPFTexture.flags1` was {self.texture_flags}, but expected 0, 1, 2, or 3."
            )

        if platform != TPFPlatform.PC:
            self.header = TextureHeader
            self.header.width = reader.unpack_value("h")
            self.header.height = reader.unpack_value("h")
            if platform == TPFPlatform.Xbox360:
                reader.assert_pad(4)
            elif platform == TPFPlatform.PS3:
                self.header.unk1 = reader.unpack_value("i")
                if tpf_flags != 0:
                    self.header.unk2 = reader.unpack_value("i")
                    if self.header.unk2 not in {0, 0x68E0, 0xAAE4}:
                        raise ValueError(
                            f"`TextureHeader.unk2` was {self.header.unk2}, but expected 0, 0x68E0, or 0xAAE4."
                        )
            elif platform in {TPFPlatform.PS4, TPFPlatform.XboxOne}:
                self.header.texture_count = reader.unpack_value("i")
                if self.header.texture_count not in {1, 6}:
                    f"`TextureHeader.texture_count` was {self.header.texture_count}, but expected 1 or 6."
                self.header.unk2 = reader.unpack_value("i")
                if self.header.unk2 != 0xD:
                    f"`TextureHeader.unk2` was {self.header.unk2}, but expected 0xD."

        name_offset = reader.unpack_value("I")
        has_float_struct = reader.unpack_value("i") == 1
        if platform in {TPFPlatform.PS4, TPFPlatform.XboxOne}:
            self.header.dxgi_format = reader.unpack_value("i")
        if has_float_struct:
            self.float_struct = FloatStruct.unpack_from(reader)

        with reader.temp_offset(file_offset):
            self.data = reader.read(file_size)
        if self.texture_flags in {2, 3}:
            # Data is DCX-compressed.
            # TODO: should enforce DCX type as 'DCP_EDGE'?
            self.data = decompress(self.data)

        self.name = reader.unpack_string(offset=name_offset, encoding=encoding)

        return self