예제 #1
0
    def unpack(self, buffer: io.BufferedIOBase, bounding_box_has_unknown: bool = None):
        data = self.STRUCT.unpack(buffer)
        mesh_offset = buffer.tell()

        bounding_box_offset = data.pop("__bounding_box_offset")
        if bounding_box_offset != 0:
            buffer.seek(bounding_box_offset)
            self.bounding_box = BoundingBoxWithUnknown(buffer) if bounding_box_has_unknown else BoundingBox(buffer)
        else:
            self.bounding_box = None

        buffer.seek(data.pop("__bone_offset"))
        bone_count = data.pop("__bone_count")
        self.bone_indices = list(unpack_from_buffer(buffer, f"<{bone_count}i",))

        buffer.seek(data.pop("__face_set_offset"))
        face_set_count = data.pop("__face_set_count")
        self._face_set_indices = list(unpack_from_buffer(buffer, f"<{face_set_count}i"))

        buffer.seek(data.pop("__vertex_buffer_offset"))
        vertex_count = data.pop("__vertex_buffer_count")
        self._vertex_buffer_indices = list(unpack_from_buffer(buffer, f"<{vertex_count}i"))

        buffer.seek(mesh_offset)
        self.set(**data)
예제 #2
0
 def _check_use_struct_64(info_buffer, goal_count):
     if goal_count == 0:
         raise LuaError(
             "Cannot detect `LuaInfo` version if no goals are present.")
     elif goal_count >= 2:
         return unpack_from_buffer(info_buffer, "i", offset=0x24)[0] == 0
     else:
         # Hacky check if there's only one goal.
         if unpack_from_buffer(info_buffer, "i", offset=0x18)[0] == 0x28:
             return True
         if unpack_from_buffer(info_buffer, "i", offset=0x14)[0] == 0x20:
             return False
         raise ValueError(
             "Found unexpected data while trying to detect `LuaInfo` version from single goal."
         )
예제 #3
0
 def ENTRY_CLASS(cls, msb_buffer):
     """Detects the appropriate subclass of `MSBPart` to instantiate, and does so."""
     entry_subtype_int = unpack_from_buffer(msb_buffer,
                                            "i",
                                            offset=cls.SUBTYPE_OFFSET,
                                            relative_offset=True)[0]
     try:
         entry_subtype = cls.ENTRY_SUBTYPE_ENUM(entry_subtype_int)
     except ValueError:
         raise MapError(
             f"Entry of type {cls.ENTRY_SUBTYPE_ENUM} has invalid subtype enum: {entry_subtype_int}"
         )
     return cls.SUBTYPE_CLASSES[entry_subtype](msb_buffer)
예제 #4
0
class FaceSet(BinaryObject):

    STRUCT = BinaryStruct(
        ("flags", "B"),
        "3x",
        ("triangle_strip", "?"),
        ("cull_back_faces", "?"),
        ("unk_x06", "h"),
        ("__indices_count", "i"),
        ("__indices_offset", "i"),  # header stops here for versions < 0x20005, which are not supported by Soulstruct
        ("__indices_length", "i"),  # unused
        "4x",
        ("__index_size", "i"),  # 0 (from header), 16, or 32
        "4x",
    )

    flags: FaceSetFlags
    triangle_strip: bool
    cull_back_faces: bool
    unk_x06: int
    indices: list[int]

    def unpack(self, buffer: io.BufferedIOBase, header_index_size: int = None, data_offset: int = None):
        data = self.STRUCT.unpack(buffer)

        if (index_size := data.pop("__index_size")) == 0:
            index_size = header_index_size

        if index_size == 8:
            raise NotImplementedError("Soulstruct cannot support edge-compressed FLVER face sets (`index_size=8`).")
        elif index_size in {16, 32}:
            face_set_offset = buffer.tell()
            indices_count = data.pop("__indices_count")
            indices_offset = data.pop("__indices_offset")
            buffer.seek(data_offset + indices_offset)
            self.indices = list(unpack_from_buffer(buffer, f"<{indices_count}{'H' if index_size == 16 else 'I'}"))
            buffer.seek(face_set_offset)
        else:
            raise ValueError(f"Unsupported face set index size: {index_size}")

        self.set(**data)
예제 #5
0
    def unpack(self, buffer, **kwargs):
        self.byte_order = ">" if unpack_from_buffer(buffer, "B",
                                                    44)[0] == 255 else "<"
        version_info = unpack_from_buffer(buffer, f"{self.byte_order}bbb", 45)
        self.flags1 = ParamFlags1(version_info[0])
        self.flags2 = ParamFlags2(version_info[1])
        self.paramdef_format_version = version_info[2]
        header_struct = self.GET_HEADER_STRUCT(self.flags1, self.byte_order)
        header = header_struct.unpack(buffer)
        try:
            self.param_type = header["param_type"]
        except KeyError:
            self.param_type = read_chars_from_buffer(
                buffer, offset=header["param_type_offset"], encoding="utf-8")
        self.paramdef_data_version = header["paramdef_data_version"]
        self.unknown = header["unknown"]
        # Row data offset in header not used. (It's an unsigned short, yet doesn't limit row count to 5461.)
        name_data_offset = header[
            "name_data_offset"]  # CANNOT BE TRUSTED IN VANILLA FILES! Off by +12 bytes.

        # Load row pointer data.
        if self.flags1.LongDataOffset:
            row_pointers = self.ROW_STRUCT_64.unpack_count(
                buffer, count=header["row_count"])
        else:
            row_pointers = self.ROW_STRUCT_32.unpack_count(
                buffer, count=header["row_count"])
        row_data_offset = buffer.tell()  # Reliable row data offset.

        # Row size is lazily determined. TODO: Unpack row data in sequence and associate with names separately.
        if len(row_pointers) == 0:
            return
        elif len(row_pointers) == 1:
            # NOTE: The only vanilla param in Dark Souls with one row is LEVELSYNC_PARAM_ST (Remastered only),
            # for which the row size is hard-coded here. Otherwise, we can trust the repacked offset from Soulstruct
            # (and SoulsFormats, etc.).
            if self.param_type == "LEVELSYNC_PARAM_ST":
                row_size = 220
            else:
                row_size = name_data_offset - row_data_offset
        else:
            row_size = row_pointers[1]["data_offset"] - row_pointers[0][
                "data_offset"]

        # Note that we no longer need to track buffer offset.
        name_encoding = self.get_name_encoding()
        for row_struct in row_pointers:
            buffer.seek(row_struct["data_offset"])
            row_data = buffer.read(row_size)
            if row_struct["name_offset"] != 0:
                try:
                    name = read_chars_from_buffer(
                        buffer,
                        offset=row_struct["name_offset"],
                        encoding=name_encoding,
                        reset_old_offset=False,  # no need to reset
                    )
                except UnicodeDecodeError as ex:
                    if ex.object in self.undecodable_row_names:
                        name = read_chars_from_buffer(
                            buffer,
                            offset=row_struct["name_offset"],
                            encoding=None,
                            reset_old_offset=False,  # no need to reset
                        )
                    else:
                        raise
                except ValueError:
                    buffer.seek(row_struct["name_offset"])
                    _LOGGER.error(
                        f"Error encountered while parsing row name string in {self.param_type}.\n"
                        f"    Header: {header}\n"
                        f"    Row Struct: {row_struct}\n"
                        f"    30 chrs of name data: {' '.join(f'{{:02x}}'.format(x) for x in buffer.read(30))}"
                    )
                    raise
            else:
                name = ""
            self.rows[row_struct["id"]] = ParamRow(row_data,
                                                   self.paramdef,
                                                   name=name)
예제 #6
0
 def unpack_argb(cls, buffer: io.BufferedIOBase):
     alpha, red, green, blue = unpack_from_buffer(buffer, "<4f")
     return cls(red, green, blue, alpha)
예제 #7
0
 def unpack_bgra(cls, buffer: io.BufferedIOBase):
     blue, green, red, alpha = unpack_from_buffer(buffer, "<4f")
     return cls(red, green, blue, alpha)
예제 #8
0
 def unpack(self, buffer):
     self.gx_items = []
     while (gx_id := unpack_from_buffer(
             buffer, "<i", offset=buffer.tell())) not in {2**31 - 1, -1}:
         self.gx_items.append(GXItem(buffer))
예제 #9
0
    def read(self, buffer: io.BufferedIOBase, layout: BufferLayout,
             uv_factor: float):
        self.uvs = []
        self.tangents = []
        self.colors = []
        for member in layout:

            not_implemented = False

            if member.semantic == LayoutSemantic.Position:
                if member.layout_type == LayoutType.Float3:
                    self.position = Vector3(unpack_from_buffer(buffer, "<3f"))
                elif member.layout_type == LayoutType.Float4:
                    self.position = Vector3(unpack_from_buffer(buffer,
                                                               "<3f"))[:3]
                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:
                    self.bone_weights = VertexBoneWeights(
                        *
                        [w / 127.0 for w in unpack_from_buffer(buffer, "<4b")])
                elif member.layout_type == LayoutType.Byte4C:
                    self.bone_weights = VertexBoneWeights(
                        *
                        [w / 255.0 for w in unpack_from_buffer(buffer, "<4B")])
                elif member.layout_type == LayoutType.UVPair:
                    self.bone_weights = VertexBoneWeights(*[
                        w / 32767.0 for w in unpack_from_buffer(buffer, "<4h")
                    ])
                elif member.layout_type == LayoutType.Short4ToFloat4A:
                    self.bone_weights = VertexBoneWeights(*[
                        w / 32767.0 for w in unpack_from_buffer(buffer, "<4h")
                    ])
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.BoneIndices:
                if member.layout_type == LayoutType.Byte4B:
                    self.bone_indices = VertexBoneIndices(
                        *unpack_from_buffer(buffer, "<4B"))
                elif member.layout_type == LayoutType.ShortBoneIndices:
                    self.bone_indices = VertexBoneIndices(
                        *unpack_from_buffer(buffer, "<4h"))
                elif member.layout_type == LayoutType.Byte4E:
                    self.bone_indices = VertexBoneIndices(
                        *unpack_from_buffer(buffer, "<4B"))
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.Normal:
                if member.layout_type == LayoutType.Float3:
                    self.normal = Vector3(unpack_from_buffer(buffer, "<3f"))
                elif member.layout_type == LayoutType.Float4:
                    self.normal = Vector3(unpack_from_buffer(buffer, "<3f"))
                    float_normal_w = unpack_from_buffer(buffer, "<f")[0]
                    self.normal_w = int(float_normal_w)
                    if self.normal_w != float_normal_w:
                        raise ValueError(
                            f"`normal_w` float was not a whole number.")
                elif member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4B,
                        LayoutType.Byte4C, LayoutType.Byte4E
                }:
                    self.normal = Vector3([
                        (x - 127) / 127.0
                        for x in unpack_from_buffer(buffer, "<3B")
                    ])
                    self.normal_w = unpack_from_buffer(buffer, "<B")[0]
                elif member.layout_type == LayoutType.Short2toFloat2:
                    self.normal_w = unpack_from_buffer(buffer, "<B")[0]
                    self.normal = Vector3(
                        [x / 127.0 for x in unpack_from_buffer(buffer, "<3b")])
                elif member.layout_type == LayoutType.Short4ToFloat4A:
                    self.normal = Vector3([
                        x / 32767.0 for x in unpack_from_buffer(buffer, "<3h")
                    ])
                    self.normal_w = unpack_from_buffer(buffer, "<h")[0]
                elif member.layout_type == LayoutType.Short4ToFloat4B:
                    self.normal = Vector3([
                        (x - 32767) / 32767.0
                        for x in unpack_from_buffer(buffer, "<3H")
                    ])
                    self.normal_w = unpack_from_buffer(buffer, "<h")[0]
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.UV:
                if member.layout_type == LayoutType.Float2:
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<2f"), 0.0))
                elif member.layout_type == LayoutType.Float3:
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<3f")))
                elif member.layout_type == LayoutType.Float4:
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<2f"), 0.0))
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<2f"), 0.0))
                elif member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4B,
                        LayoutType.Short2toFloat2, LayoutType.Byte4C,
                        LayoutType.UV
                }:
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<2h"), 0) /
                        uv_factor)
                elif member.layout_type == LayoutType.UVPair:
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<2h"), 0) /
                        uv_factor)
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<2h"), 0) /
                        uv_factor)
                elif member.layout_type == LayoutType.Short4ToFloat4B:
                    self.uvs.append(
                        Vector3(*unpack_from_buffer(buffer, "<3h")) /
                        uv_factor)
                    if unpack_from_buffer(buffer, "<h") != 0:
                        raise ValueError(
                            "Expected null byte after reading UV | Short4ToFloat4B vertex member."
                        )
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.Tangent:
                if member.layout_type == LayoutType.Float4:
                    self.tangents.append(
                        Vector4(*unpack_from_buffer(buffer, "<4f")))
                elif member.layout_type in {
                        LayoutType.Byte4A,
                        LayoutType.Byte4B,
                        LayoutType.Byte4C,
                        LayoutType.Short4ToFloat4A,
                        LayoutType.Byte4E,
                }:
                    tangent = Vector4([
                        (x - 127) / 127.0
                        for x in unpack_from_buffer(buffer, "<4B")
                    ])
                    self.tangents.append(tangent)
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.Bitangent:
                if member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4B,
                        LayoutType.Byte4C, LayoutType.Byte4E
                }:
                    self.bitangent = Vector4([
                        (x - 127) / 127.0
                        for x in unpack_from_buffer(buffer, "<4B")
                    ])
                else:
                    not_implemented = True
            elif member.semantic == LayoutSemantic.VertexColor:
                if member.layout_type == LayoutType.Float4:
                    self.colors.append(
                        Color(*unpack_from_buffer(buffer, "<4f")))
                elif member.layout_type in {
                        LayoutType.Byte4A, LayoutType.Byte4C
                }:
                    self.colors.append(
                        Color(*[
                            b / 255.0
                            for b in unpack_from_buffer(buffer, "<4B")
                        ]))
                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}")