Example #1
0
 def pack(self, writer: BinaryWriter):
     writer.pack("i", self.unk0)
     writer.pack("i", len(self.values) * 4)
     writer.pack(f"{len(self.values)}f", *self.values)
Example #2
0
    def pack(self):

        writer = BinaryWriter(big_endian=self.header.endian == b"B\0")
        encoding = ("utf-16-be" if writer.big_endian else
                    "utf-16-le") if self.header.unicode else "shift_jis_2004"

        true_face_count = 0
        total_face_count = 0
        for mesh in self.meshes:
            allow_primitive_restarts = len(
                mesh.vertices) < 2**16 - 1  # max unsigned short value
            for face_set in mesh.face_sets:
                face_set_true_count, face_set_total_count = face_set.get_face_counts(
                    allow_primitive_restarts)
                true_face_count += face_set_true_count
                total_face_count += face_set_total_count

        if self.header.version < Version.Bloodborne_DS3_A:
            # Set header's `vertex_index_size` to the largest size detected across all `FaceSet`s (16 or 32).
            header_vertex_indices_size = 16
            for mesh in self.meshes:
                for face_set in mesh.face_sets:
                    face_set_vertex_index_size = face_set.get_vertex_index_size(
                    )
                    header_vertex_indices_size = max(
                        header_vertex_indices_size, face_set_vertex_index_size)
        else:
            # Vertex size is stored per `VertexBuffer`.
            header_vertex_indices_size = 0

        self.header.pack(
            writer,
            dummy_count=len(self.dummies),
            material_count=len(self.materials),
            bone_count=len(self.bones),
            mesh_count=len(self.meshes),
            vertex_buffer_count=sum(
                len(mesh.vertex_buffers) for mesh in self.meshes),
            face_set_count=sum(len(mesh.face_sets) for mesh in self.meshes),
            buffer_layout_count=len(self.buffer_layouts),
            texture_count=sum(
                len(material.textures) for material in self.materials),
            true_face_count=true_face_count,
            total_face_count=total_face_count,
            vertex_indices_size=header_vertex_indices_size,
        )

        for dummy in self.dummies:
            dummy.pack(writer,
                       color_is_argb=self.header.version == Version.DarkSouls2)

        for material in self.materials:
            material.pack(writer)

        for bone in self.bones:
            bone.pack(writer)

        for mesh in self.meshes:
            mesh.pack(writer)

        for mesh in self.meshes:
            for face_set in mesh.face_sets:
                if header_vertex_indices_size == 0:
                    face_set_vertex_index_size = face_set.get_vertex_index_size(
                    )
                else:
                    face_set_vertex_index_size = header_vertex_indices_size
                face_set.pack(writer, face_set_vertex_index_size)

        for mesh in self.meshes:
            for i, vertex_buffer in enumerate(mesh.vertex_buffers):
                vertex_buffer.pack(
                    writer,
                    self.header.version,
                    mesh_vertex_buffer_index=i,
                    buffer_layouts=self.buffer_layouts,
                    mesh_vertex_count=len(mesh.vertices),
                )

        for i, buffer_layout in enumerate(self.buffer_layouts):
            buffer_layout.pack(writer)

        first_texture_index = 0
        for i, material in enumerate(self.materials):
            material.pack_textures(writer,
                                   first_texture_index=first_texture_index)
            first_texture_index += len(material.textures)

        # TODO: Write unknown Sekiro struct here.

        # Indexed data only after this point, with 16 pad bytes between each data type.

        writer.pad_align(16)
        for i, buffer_layout in enumerate(self.buffer_layouts):
            buffer_layout.pack_members(writer)

        writer.pad_align(16)
        for i, mesh in enumerate(self.meshes):
            mesh.pack_bounding_box(writer)

        writer.pad_align(16)
        bone_indices_start = writer.position
        for i, mesh in enumerate(self.meshes):
            mesh.pack_bone_indices(writer,
                                   bone_indices_start=bone_indices_start)

        writer.pad_align(16)
        first_face_set_index = 0
        for i, mesh in enumerate(self.meshes):
            mesh.pack_face_set_indices(writer, first_face_set_index)
            first_face_set_index += len(mesh.face_sets)

        writer.pad_align(16)
        first_vertex_buffer_index = 0
        for mesh in self.meshes:
            mesh.pack_vertex_buffer_indices(writer, first_vertex_buffer_index)
            first_vertex_buffer_index += len(mesh.vertex_buffers)

        writer.pad_align(16)
        gx_offsets = []
        for gx_list in self.gx_lists:
            gx_offsets.append(writer.position)
            gx_list.pack(writer)
        for material in self.materials:
            material.fill_gx_offset(writer, gx_offsets)

        writer.pad_align(16)
        for material in self.materials:
            material.pack_strings(writer, encoding)
            for texture in material.textures:
                texture.pack_zstring(writer, "path", encoding=encoding)
                texture.pack_zstring(writer, "texture_type", encoding=encoding)

        writer.pad_align(16)
        for bone in self.bones:
            bone.pack_zstring(writer, "name", encoding=encoding)

        alignment = 32 if self.header.version <= 0x2000E else 16
        writer.pad_align(alignment)
        if self.header.version in {Version.DarkSouls2_NT, Version.DarkSouls2}:
            writer.pad(32)

        vertex_data_start = writer.position
        self.header.fill(writer, vertex_data_offset=vertex_data_start)

        for mesh in self.meshes:
            for face_set in mesh.face_sets:
                if header_vertex_indices_size == 0:
                    face_set_vertex_index_size = face_set.get_vertex_index_size(
                    )
                else:
                    face_set_vertex_index_size = header_vertex_indices_size
                writer.pad_align(16)
                face_set.pack_vertex_indices(
                    writer,
                    vertex_index_size=face_set_vertex_index_size,
                    vertex_indices_offset=writer.position - vertex_data_start,
                )

            for vertex in mesh.vertices:
                vertex.prepare_pack()

            for vertex_buffer in mesh.vertex_buffers:
                writer.pad_align(16)
                uv_factor = 2048 if self.header.version >= Version.DarkSouls2_NT else 1024
                vertex_buffer.pack_buffer(
                    writer,
                    buffer_layouts=self.buffer_layouts,
                    vertices=mesh.vertices,
                    buffer_offset=writer.position - vertex_data_start,
                    uv_factor=uv_factor,
                )

            for vertex in mesh.vertices:
                vertex.finish_pack()

        writer.pad_align(16)
        self.header.fill(writer,
                         vertex_data_size=writer.position - vertex_data_start)
        if self.header.version in {Version.DarkSouls2_NT, Version.DarkSouls2}:
            writer.pad(32)

        return writer.finish()
Example #3
0
 def pack(self, writer: BinaryWriter, bit_big_endian: bool):
     if not bit_big_endian:
         writer.pack("B", int(f"{self:08b}"[::-1], 2))
     else:
         writer.pack("B", self)
Example #4
0
 def pack(self, writer: BinaryWriter, bit_big_endian: bool):
     if not bit_big_endian and not (self.is_big_endian and not self.has_flag_7):
         writer.pack("B", int(f"{self:08b}"[::-1], 2))
     else:
         writer.pack("B", self)
Example #5
0
 def pack_members(self, writer: BinaryWriter):
     writer.fill("__member_offset", writer.position, obj=self)
     struct_offset = 0
     for member in self.members:
         member.pack(writer, struct_offset)
         struct_offset += member.size
Example #6
0
    def pack(self, writer: BinaryWriter, layout: BufferLayout,
             uv_factor: float):
        for member in layout:

            not_implemented = False

            if member.semantic == LayoutSemantic.Position:
                if member.layout_type == LayoutType.Float3:
                    writer.pack("3f", *self.position)
                elif member.layout_type == LayoutType.Float4:
                    writer.pack("4f", *self.position, 0.0)
                elif member.layout_type == LayoutType.EdgeCompressed:
                    raise NotImplementedError(
                        "Soulstruct cannot load FLVERs with edge-compressed vertex positions."
                    )
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.BoneWeights:
                if member.layout_type == LayoutType.Byte4A:
                    writer.pack("4b",
                                *[int(w * 127) for w in self.bone_weights])
                elif member.layout_type == LayoutType.Byte4C:
                    writer.pack("4B",
                                *[int(w * 255) for w in self.bone_weights])
                elif member.layout_type in {
                        LayoutType.UVPair, LayoutType.Short4ToFloat4A
                }:
                    writer.pack("4h",
                                *[int(w * 32767) for w in self.bone_weights])
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.BoneIndices:
                if member.layout_type in {
                        LayoutType.Byte4B, LayoutType.Byte4E
                }:
                    writer.pack("4B", *self.bone_indices)
                elif member.layout_type == LayoutType.ShortBoneIndices:
                    writer.pack("4h", *self.bone_indices)
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.Normal:
                if member.layout_type == LayoutType.Float3:
                    writer.pack("3f", *self.normal)
                elif member.layout_type == LayoutType.Float4:
                    writer.pack("4f", *self.normal, self.normal_w)
                elif member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4B,
                        LayoutType.Byte4C, LayoutType.Byte4E
                }:
                    writer.pack("4B",
                                *[int(x * 127 + 127) for x in self.normal],
                                self.normal_w)
                elif member.layout_type == LayoutType.Short2toFloat2:
                    writer.pack("B3b", self.normal_w,
                                *[int(x * 127) for x in self.normal])
                elif member.layout_type == LayoutType.Short4ToFloat4A:
                    writer.pack("4h", *[int(x * 32767) for x in self.normal],
                                self.normal_w)
                elif member.layout_type == LayoutType.Short4ToFloat4B:
                    writer.pack("4H",
                                *[int(x * 32767 + 32767) for x in self.normal],
                                self.normal_w)
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.UV:
                uv = self.uv_queue.pop() * uv_factor
                if member.layout_type == LayoutType.Float2:
                    writer.pack("2f", uv.x, uv.y)
                elif member.layout_type == LayoutType.Float3:
                    writer.pack("3f", uv.x, uv.y, uv.z)
                elif member.layout_type == LayoutType.Float4:
                    writer.pack("2f", uv.x, uv.y)
                    uv = self.uv_queue.pop() * uv_factor
                    writer.pack("2f", uv.x, uv.y)
                elif member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4B,
                        LayoutType.Short2toFloat2, LayoutType.Byte4C,
                        LayoutType.UV
                }:
                    writer.pack("2h", int(uv.x), int(uv.y))
                elif member.layout_type == LayoutType.UVPair:
                    writer.pack("2h", int(uv.x), int(uv.y))
                    uv = self.uv_queue.pop() * uv_factor
                    writer.pack("2h", int(uv.x), int(uv.y))
                elif member.layout_type == LayoutType.Short4ToFloat4B:
                    writer.pack("4h", int(uv.x), int(uv.y), int(uv.z), 0)
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.Tangent:
                tangent = self.tangent_queue.pop()
                if member.layout_type == LayoutType.Float4:
                    writer.pack("4f", *tangent)
                elif member.layout_type in {
                        LayoutType.Byte4A,
                        LayoutType.Byte4B,
                        LayoutType.Byte4C,
                        LayoutType.Short4ToFloat4A,
                        LayoutType.Byte4E,
                }:
                    writer.pack("4B", *[int(x * 127 + 127) for x in tangent])
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.Bitangent:
                if member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4B,
                        LayoutType.Byte4C, LayoutType.Byte4E
                }:
                    writer.pack("4B",
                                *[int(x * 127 + 127) for x in self.bitangent])
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.VertexColor:
                color = self.color_queue.pop()
                if member.layout_type == LayoutType.Float4:
                    writer.pack("4f", *color)
                elif member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4C
                }:
                    writer.pack("4B", *[int(c * 255) for c in color])
                else:
                    not_implemented = True
            else:
                not_implemented = True

            if not_implemented:
                raise NotImplementedError(
                    f"Unsupported vertex member semantic/type combination: "
                    f"{member.semantic.name} | {member.layout_type.name}")
Example #7
0
 def pack(self, writer: BinaryWriter, struct_offset: int):
     writer.pack_struct(
         self.STRUCT,
         self,
         __struct_offset=struct_offset,
     )
Example #8
0
 def pack_bnd4(self, writer: BinaryWriter, binder_flags: BinderFlags, bit_big_endian: bool):
     self.flags.pack(writer, bit_big_endian)
     writer.pad(3)
     writer.pack("i", -1)
     writer.pack("q", self.compressed_size)
     if binder_flags.has_compression:
         writer.pack("q", self.uncompressed_size)
     writer.reserve("data_offset", "q" if binder_flags.has_long_offsets else "I", obj=self)
     if binder_flags.has_ids:
         writer.pack("i", self.id)
     if binder_flags.has_names:
         writer.reserve("path_offset", "i", obj=self)
Example #9
0
 def pack(self, writer: BinaryWriter):
     for gx_item in self.gx_items:
         gx_item.pack(writer)
     writer.pack("iii", self.terminator_id, 100,
                 self.terminator_null_count + 12)
     writer.pad(self.terminator_null_count)
Example #10
0
 def fill_gx_offset(self, writer: BinaryWriter, gx_offsets: list[int]):
     writer.fill("__gx_offset",
                 0 if self.gx_index == -1 else gx_offsets[self.gx_index],
                 obj=self)
Example #11
0
 def pack_textures(self, writer: BinaryWriter, first_texture_index: int):
     writer.fill("_first_texture_index", first_texture_index, obj=self)
     for texture in self.textures:
         texture.pack(writer)