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)
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()
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)
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)
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
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}")
def pack(self, writer: BinaryWriter, struct_offset: int): writer.pack_struct( self.STRUCT, self, __struct_offset=struct_offset, )
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)
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)
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)
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)