Exemple #1
0
class FMG0(BaseFMG):
    """Used only in Demon's Souls. Big-endian."""

    HEADER_STRUCT = BinaryStruct(
        "x",
        ("big_endian", "?", True),
        ("version", "b", 0),
        "x",
        ("file_size", "i"),
        ("one", "b", 1),
        ("unknown1", "b", -1),
        "2x",
        ("range_count", "i"),
        ("string_count", "i"),
        ("string_offsets_offset", "i"),
        ("zero", "i", 0),
        byte_order=">",
    )
    RANGE_STRUCT = BinaryStruct(
        ("first_index", "i"),
        ("first_id", "i"),
        ("last_id", "i"),
        byte_order=">",
    )
    STRING_OFFSET_STRUCT = BinaryStruct(
        ("offset", "i"),
        byte_order=">",
    )
    BIG_ENDIAN = True
    VERSION = 0

    MAX_LINES = None  # TODO: Don't know for Demon's Souls.
Exemple #2
0
class FMG1(BaseFMG):
    """Used in Dark Souls (both versions) and Dark Souls 2."""

    HEADER_STRUCT = BinaryStruct(
        "x",
        ("big_endian", "?", False),
        ("version", "b", 1),
        "x",
        ("file_size", "i"),
        ("one", "b", 1),
        ("unknown1", "b", 0),
        "2x",
        ("range_count", "i"),
        ("string_count", "i"),
        ("string_offsets_offset", "i"),
        ("zero", "i", 0),
    )
    RANGE_STRUCT = BinaryStruct(
        ("first_index", "i"),
        ("first_id", "i"),
        ("last_id", "i"),
    )
    STRING_OFFSET_STRUCT = BinaryStruct(("offset", "i"), )
    BIG_ENDIAN = False
    VERSION = 1

    MAX_LINES = 11  # TODO: Correct for DS1, not sure about DS2.
Exemple #3
0
class FMG2(BaseFMG):
    """Used in Bloodborne, Dark Souls 3, and Sekiro."""

    HEADER_STRUCT = BinaryStruct(
        "x",
        ("big_endian", "?", False),
        ("version", "b", 2),
        "x",
        ("file_size", "i"),
        ("one", "b", 1),
        ("unknown1", "b", 0),
        "2x",
        ("range_count", "i"),
        ("string_count", "i"),
        ("unknown2", "i", 255),
        ("string_offsets_offset", "q"),
        ("zero", "q", 0),
    )
    RANGE_STRUCT = BinaryStruct(
        ("first_index", "i"),
        ("first_id", "i"),
        ("last_id", "i"),
        "4x",
    )
    STRING_OFFSET_STRUCT = BinaryStruct(("offset", "q"), )
    BIG_ENDIAN = False
    VERSION = 2

    MAX_LINES = None  # TODO: Don't know for Bloodborne or DS3.
Exemple #4
0
class Command(_BaseCommand, abc.ABC):
    STRUCT = BinaryStruct(
        ("bank", "i"),  # Values of 1, 5, 6, 7 have been encountered.
        ("index", "i"),
        ("args_offset", "q"),
        ("args_count", "q"),
    )
    ARG_STRUCT = BinaryStruct(("arg_ezl_offset", "q"), ("arg_ezl_size", "q"), )
Exemple #5
0
class MSBEntryList(_BaseMSBEntryList, abc.ABC):

    MAP_ENTITY_LIST_HEADER = BinaryStruct(
        ("version", "i", 3),
        ("entry_offset_count", "i"),
        ("name_offset", "q"),
    )
    MAP_ENTITY_ENTRY_OFFSET = BinaryStruct(("entry_offset", "q"), )
    MAP_ENTITY_LIST_TAIL = BinaryStruct(("next_entry_list_offset", "q"), )
    NAME_ENCODING = "utf-16-le"
Exemple #6
0
class Condition(_BaseCondition, abc.ABC):
    STRUCT = BinaryStruct(
        ("next_state_offset", "q"),
        ("pass_commands_offset", "q"),
        ("pass_commands_count", "q"),
        ("subcondition_pointers_offset", "q"),
        ("subcondition_pointers_count", "q"),
        ("test_ezl_offset", "q"),
        ("test_ezl_size", "q"),
    )
    POINTER_STRUCT = BinaryStruct(("condition_offset", "q"), )
    STATE_ID_STRUCT = BinaryStruct(("state_id", "q"), )
Exemple #7
0
class ESD(DarkSoulsDSRType, _BaseESD, abc.ABC):

    DCX_TYPE = DCXType.DCX_DFLT_10000_24_9

    EXTERNAL_HEADER_STRUCT = BinaryStruct(
        ("version", "4s", b"fsSL"),  # Note specific case.
        ("one", "i", 1),
        ("game_version", "2i", [GAME_VERSION, GAME_VERSION]),
        ("table_size_offset", "i", 84),
        ("internal_data_size", "i"),  # excludes this external header
        ("unk", "i", 6),
        ("internal_header_size", "i", 72),
        ("internal_header_count", "i", 1),
        ("state_machine_header_size", "i", 32),
        ("state_machine_count", "i"),
        ("state_size", "i", 72),
        ("state_count", "i"),
        ("condition_size", "i", 56),
        ("condition_count", "i"),
        ("command_size", "i", 24),
        ("command_count", "i"),
        ("command_arg_size", "i", 16),
        ("command_arg_count", "i"),
        ("condition_pointers_offset", "i"),
        ("condition_pointers_count", "i"),
        ("esd_name_offset_minus_1", "i"),
        # Not sure why this is minus 1. Use the internal header for the real offset.
        ("esd_name_length", "i"),  # equal to internal header
        ("unk_offset_1", "i"),
        ("unk_size_1", "i", 0),
        ("unk_offset_2", "i"),
        ("unk_size_2", "i", 0),
    )
    INTERNAL_HEADER_STRUCT = BinaryStruct(
        ("one", "i", 1),
        ("magic", "4i"),
        "4x",
        ("state_machine_headers_offset", "q", 72),
        ("state_machine_count", "q"),
        ("esd_name_offset", "q"),  # accurate, unlike external header
        ("esd_name_length", "q"),
        # identical to external header entry; note this is only *half* the name byte size due to UTF-16
        "16x",
    )
    STATE_MACHINE_HEADER_STRUCT = BinaryStruct(
        ("state_machine_index", "q"),
        ("state_machine_offset", "q"),
        ("state_count", "q"),  # number of states
        ("state_machine_offset_2", "q"),  # duplicate
    )
Exemple #8
0
class MSBEvent(BaseMSBEvent, abc.ABC):
    """MSB event entry in Bloodborne."""

    EVENT_HEADER_STRUCT = BinaryStruct(
        ("__name_offset", "q"),
        ("_event_index", "i"),
        ("__event_type", "i"),
        ("_local_event_index", "i"),
        "4x",
        ("__base_data_offset", "q"),
        ("__type_data_offset", "q"),
    )
    EVENT_TYPE_OFFSET = 12
    EVENT_BASE_DATA_STRUCT = BinaryStruct(
        ("_base_part_index", "i"),
        ("_base_region_index", "i"),
        ("entity_id", "i"),
        ("_unknowns", "4b"),
    )
    NAME_ENCODING = "utf-16-le"

    FIELD_INFO = BaseMSBEvent.FIELD_INFO | {
        "entity_id":
        MapFieldInfo(  # definition overridden when used
            "Entity ID",
            int,
            -1,
            "Entity ID for event. Unused for this event type.",
        ),
        "base_part_name":
        MapFieldInfo(  # definition overridden when used
            "Base Part Name",
            MapPart,
            None,
            "Map Part name related to event. Unused for this event type.",
        ),
        "base_region_name":
        MapFieldInfo(  # definition overridden when used
            "Base Region Name",
            Region,
            None,
            "Map Region name related to event. Unused for this event type.",
        ),
    }

    def __init__(self, source=None, **kwargs):
        self._unknowns = [0, 0, 0, 0]
        super().__init__(source=source, **kwargs)
Exemple #9
0
class MSBRegionCylinder(MSBRegion, abc.ABC):
    ENTRY_SUBTYPE = MSBRegionSubtype.Cylinder
    REGION_TYPE_DATA_STRUCT = BinaryStruct(
        ("radius", "f"),
        ("height", "f"),
    )

    FIELD_INFO = MSBRegion.FIELD_INFO | {
        "radius":
        MapFieldInfo(
            "Radius",
            float,
            1.0,
            "Radius (in xz-plane) of cylinder-shaped region.",
        ),
        "height":
        MapFieldInfo(
            "Height",
            float,
            1.0,
            "Height (along y-axis) of cylinder-shaped region.",
        ),
    }

    FIELD_ORDER = (
        "entity_id",
        "translate",
        "rotate",
        "radius",
        "height",
    )

    radius: float
    height: float
Exemple #10
0
class GXItem(BinaryObject):
    """Item that sets various material rendering properties."""

    STRUCT = BinaryStruct(
        ("gx_id", "4s"),  # actually "i" prior to Dark Souls 2 (0x20010) but always stored as bytes here for consistency
        ("unk_x04", "i"),
        ("__size", "i"),  # includes header
    )

    gx_id: bytes
    unk_x04: int
    data: bytes

    def unpack(self, reader: BinaryReader):
        gx_item = reader.unpack_struct(self.STRUCT)
        self.data = reader.read(gx_item.pop("__size") - self.STRUCT.size)
        self.set(**gx_item)

    def pack(self, writer: BinaryWriter):
        writer.pack_struct(
            self.STRUCT,
            self,
            __size=len(self.data) + self.STRUCT.size,
        )
        writer.append(self.data)

    def __repr__(self):
        return f"GXItem(gx_id = {self.gx_id}, unk_x04 = {self.unk_x04}, data = {self.data})"
Exemple #11
0
class BaseMSBRegionRect(BaseMSBRegion, abc.ABC):
    """Almost never used (no volume)."""
    REGION_TYPE_DATA_STRUCT = BinaryStruct(
        ("width", "f"),
        ("depth", "f"),
    )

    FIELD_INFO = BaseMSBRegion.FIELD_INFO | {
        "width":
        MapFieldInfo(
            "Width",
            float,
            1.0,
            "Width (along x-axis) of rectangle-shaped region.",
        ),
        "height":
        MapFieldInfo(
            "Height",
            float,
            1.0,
            "Height (along y-axis) of rectangle-shaped region.",
        ),
    }

    FIELD_ORDER = (
        "entity_id",
        "translate",
        "rotate",
        "width",
        "height",
    )

    width: float
    height: float
Exemple #12
0
class EventArg(_BaseEventArg):
    HEADER_STRUCT = BinaryStruct(
        ("instruction_line", "Q"),
        ("write_from_byte", "Q"),
        ("read_from_byte", "Q"),
        ("bytes_to_write", "Q"),
    )
Exemple #13
0
class MSBPlayerStart(MSBPart, abc.ABC):
    """Starting point for the player character (e.g. a warp point). No additional data.

    Note that these are distinct from Spawn Point events, which are used much more often (e.g. bonfires).

    If the player's position within a given map is lost by the game, they will respawn at the Player Start with entity
    ID -1.
    """

    ENTRY_SUBTYPE = MSBPartSubtype.PlayerStart
    PART_TYPE_DATA_STRUCT = BinaryStruct("16x", )

    FIELD_INFO = {
        "model_name":
        MapFieldInfo(
            "Model Name",
            CharacterModel,
            "c0000",
            "Name of character model to use for this PlayerStart. This should always be c0000.",
        ),
    }

    FIELD_ORDER = (
        "model_name",
        "entity_id",
        "translate",
        "rotate",
    )

    def __init__(self, source=None, **kwargs):
        if source is None:
            kwargs.setdefault("model_name", "c0000")
        super().__init__(source=source, **kwargs)
Exemple #14
0
class MSBMapPiece(MSBPart, abc.ABC):
    """Just a textured, visible mesh asset. Does not include any collision."""

    ENTRY_SUBTYPE = MSBPartSubtype.MapPiece
    PART_TYPE_DATA_STRUCT = BinaryStruct("8x", )

    FIELD_INFO = {
        "model_name":
        MapFieldInfo(
            "Model Name",
            MapPieceModel,
            None,
            "Name of map piece model to use for this map piece.",
        ),
    }

    FIELD_ORDER = (
        "model_name",
        "entity_id",
        "translate",
        "rotate",
        "scale",
        "draw_groups",
        "display_groups",
    )
Exemple #15
0
class Texture(BinaryObject):

    STRUCT = BinaryStruct(
        ("__path__z", "i"),
        ("__texture_type__z", "i"),
        ("scale", "2f"),
        ("unk_x10", "B"),  # 0, 1, or 2
        ("unk_x11", "?"),
        "2x",
        ("unk_x14", "f"),
        ("unk_x18", "f"),
        ("unk_x1C", "f"),
    )

    path: str
    texture_type: str
    scale: Vector2
    unk_x10: int
    unk_x11: bool
    unk_x14: float
    unk_x18: float
    unk_x1C: float

    DEFAULTS = {
        "scale": Vector2.ones(),
        "unk_x10": 1,
        "unk_x11": True,
        "unk_x14": 0.0,
        "unk_x18": 0.0,
        "unk_x1C": 0.0,
    }

    unpack = BinaryObject.default_unpack
    pack = BinaryObject.default_pack

    def set_name(self, name: str):
        """Set '.tga' name of `path`."""
        name = name.removesuffix(".tga").removesuffix(".tpf") + ".tga"
        self.path = str(Path(self.path).with_name(name))

    def __repr__(self):
        lines = [
            f"Texture(",
            f"  path = {repr(self.path)}",
            f"  texture_type = {repr(self.texture_type)}",
        ]
        if self.scale != (1.0, 1.0):
            lines.append(f"  scale = {self.scale}")
        if self.unk_x10 != 1:
            lines.append(f"  unk_x10 = {self.unk_x10}")
        if not self.unk_x11:
            lines.append(f"  unk_x11 = {self.unk_x11}")
        if self.unk_x14 != 0.0:
            lines.append(f"  unk_x14 = {self.unk_x14}")
        if self.unk_x18 != 0.0:
            lines.append(f"  unk_x18 = {self.unk_x18}")
        if self.unk_x1C != 0.0:
            lines.append(f"  unk_x1C = {self.unk_x1C}")
        lines.append(")")
        return "\n".join(lines)
Exemple #16
0
class MSBLightEvent(MSBEvent):
    ENTRY_SUBTYPE = MSBEventSubtype.Light
    EVENT_TYPE_DATA_STRUCT = BinaryStruct(("point_light_id", "i"), )

    FIELD_INFO = MSBEvent.FIELD_INFO | {
        "base_part_name":
        MapFieldInfo(
            "Draw Parent",
            MapPart,
            None,
            "Light will be drawn as long as this parent (usually a Collision or Map Piece part) is drawn.",
        ),
        "base_region_name":
        MapFieldInfo(
            "Light Region",
            Region,
            None,
            "Region (usually a Point) at which Light appears.",
        ),
        "point_light_id":
        MapFieldInfo(
            "Point Light",
            PointLightParam,
            0,
            "Point Light lighting parameter ID to use for this light.",
        ),
    }

    FIELD_ORDER = (
        "base_part_name",
        "base_region_name",
        "point_light_id",
    )
Exemple #17
0
 def GET_HEADER_STRUCT(flags1: ParamFlags1, byte_order) -> BinaryStruct:
     fields = [
         ("name_data_offset", "I"),
         "2x" if
         (flags1[0] and flags1.IntDataOffset) or flags1.LongDataOffset else
         ("row_data_offset", "H"),
         ("unknown", "H"),  # 0 or 1
         ("paramdef_data_version", "H"),
         ("row_count", "H"),
     ]
     if flags1.OffsetParam:
         fields += [
             "4x",
             ("param_type_offset", "q"),
             "20x",
         ]
     else:
         fields.append(("param_type", "32j"))
     fields += [
         ("big_endian", "b", 255 if byte_order == ">" else 0),
         ("flags1", "b"),
         ("flags2", "b"),
         ("paramdef_format_version", "b"),
     ]
     if flags1[0] and flags1.IntDataOffset:
         fields += [
             ("row_data_offset", "i"),
             "12x",
         ]
     elif flags1.LongDataOffset:
         fields += [
             ("row_data_offset", "q"),
             "8x",
         ]
     return BinaryStruct(*fields, byte_order=byte_order)
Exemple #18
0
class MSBPlayerStart(MSBPart):
    ENTRY_SUBTYPE = MSBPartSubtype.PlayerStart
    PART_TYPE_DATA_STRUCT = BinaryStruct(
        "16x",
    )

    FIELD_INFO = MSBPart.FIELD_INFO | {
        "model_name": MapFieldInfo(
            "Model Name",
            CharacterModel,
            "c0000",
            "Name of character model to use for this PlayerStart. This should always be c0000.",
        ),
    }

    FIELD_ORDER = (
        "model_name",
        "entity_id",
        "translate",
        "rotate",
    ) + MSBPart.FIELD_ORDER

    def __init__(self, source=None, **kwargs):
        if source is None:
            # Set some different defaults.
            kwargs.setdefault("model_name", "c0000")
        super().__init__(source=source, **kwargs)
Exemple #19
0
class MSBSpawnerEvent(_BaseMSBSpawnerEvent, MSBEvent):
    """Attributes are identical to base event, except there are eight spawn region slots."""

    EVENT_TYPE_DATA_STRUCT = BinaryStruct(
        ("max_count", "B"),
        ("spawner_type", "b"),
        ("limit_count", "h"),
        ("min_spawner_count", "h"),
        ("max_spawner_count", "h"),
        ("min_interval", "f"),
        ("max_interval", "f"),
        ("initial_spawn_count", "B"),
        "31x",
        ("_spawn_region_indices", "8i"),
        ("_spawn_part_indices", "32i"),
        "64x",
    )
    SPAWN_REGION_COUNT = 8

    FIELD_INFO = _BaseMSBSpawnerEvent.FIELD_INFO | {
        "spawn_region_names":
        MapFieldInfo(
            "Spawn Regions",
            GameObjectSequence((Region, SPAWN_REGION_COUNT)),
            [None] * SPAWN_REGION_COUNT,
            "Regions where entities will be spawned.",
        ),
    }
Exemple #20
0
class ParamDefField(_BaseParamDefField):
    STRUCT = BinaryStruct(
        ("display_name", "64j"),
        ("display_type", "8j"),
        ("display_format", "8j"),  # %i, %u, %d, etc.
        ("default", "f"),
        ("minimum", "f"),
        ("maximum", "f"),
        ("increment", "f"),
        ("edit_type", "i"),
        ("size", "i"),
        ("description_offset", "i"),
        ("internal_type", "32j"),  # could be an enum name (see params.enums)
        ("name", "32j"),
        ("sort_id", "i"),
    )

    def get_display_info(self, row: ParamRow):
        try:
            field_info = get_param_info_field(self.param_name, self.name)
        except ValueError:
            raise ValueError(f"No display information given for field '{self.name}'.")
        return field_info(row)

    def get_default_value(self):
        v = DEFAULTS[self.param_name].get(self.name, self.default)
        if self.bit_count == 1 and self.internal_type != "dummy8":
            return bool(v)
        elif self.internal_type not in {"f32", "f64"}:
            return int(v)
        return v
Exemple #21
0
class FBX(GameFile):
    """Not actually a "game" file, but interchangeable with `FLVER` game files."""

    MAX_VERSION = 7700

    HEADER_STRUCT = BinaryStruct(
        ("magic", "23s", b"Kaydara FBX Binary  \x00\x1a\x00"),
        ("version", "I"),
    )

    # Constant 176 byte footer at the end of every FBX file.
    FOOTER = (
        b"\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e" +
        b"\0" * 20 + b"\xe8\x1c" + b"\0" * 122 +
        b"\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b")

    def __init__(
            self,
            file_source: tp.Union[None, str, Path, bytes, io.BufferedIOBase,
                                  BinaryReader] = None,
            dcx_magic: tuple[int, int] = (),
            **kwargs,
    ):
        self.root_nodes = [
        ]  # type: tp.Union[list[FBXNode32, ...], list[FBXNode64, ...]]
        self.node_class = FBXNode64  # defaults to 64-bit offsets

        super().__init__(file_source, dcx_magic, **kwargs)

    def unpack(self, reader: BinaryReader, **kwargs):
        header = reader.unpack_struct(self.HEADER_STRUCT)
        self.node_class = FBXNode64 if header["version"] >= 7700 else FBXNode32
        if header["version"] > self.MAX_VERSION:
            raise NotImplementedError(
                f"Cannot unpack FBX version {header['version']}. Last supported version is {self.MAX_VERSION}."
            )

        start_offset = self.HEADER_STRUCT.size
        self.root_nodes = []
        while 1:
            node = self.node_class(reader, start_offset=start_offset)
            start_offset += node.size
            if node.is_empty:
                break  # empty node is not kept
            self.root_nodes.append(node)

        # Constant FBX footer is ignored.

    def pack(self):
        raise NotImplementedError("FBX cannot be packed yet.")

    def __getitem__(self, root_node_name: str):
        try:
            return next(n for n in self.root_nodes if n.name == root_node_name)
        except StopIteration:
            raise KeyError(f"No root FBX node named {root_node_name}.")

    def to_string(self) -> str:
        return "\n".join(node.to_string() for node in self.root_nodes)
Exemple #22
0
class EventLayers(_BaseEventLayers):
    HEADER_STRUCT = BinaryStruct(
        ("two", "I", 2),
        ("event_layers", "I"),  # 32-bit bit field
        ("zero", "Q", 0),
        ("minus_one", "q", -1),
        ("one", "Q", 1),
    )
Exemple #23
0
class MSBPartGParam(MSBPart, abc.ABC):
    """Subclass of `MSBPart` that includes GParam fields."""
    PART_GPARAM_STRUCT = BinaryStruct(
        ("light_set_id", "i"),
        ("fog_id", "i"),
        ("light_scattering_id", "i"),
        ("environment_map_id", "i"),
        "16x",
    )

    FIELD_INFO = MSBPart.FIELD_INFO | {
        "light_set_id": MapFieldInfo(
            "Light Set ID",
            int,  # TODO: GParam support.
            0,
            "Light set GParam ID.",
        ),
        "fog_id": MapFieldInfo(
            "Fog Param ID",
            int,  # TODO: GParam support.
            0,
            "Fog GParam ID.",
        ),
        "light_scattering_id": MapFieldInfo(
            "Light Scattering ID",
            int,  # TODO: GParam support.
            0,
            "Light scattering GParam ID.",
        ),
        "environment_map_id": MapFieldInfo(
            "Environment Map ID",
            int,  # TODO: GParam support.
            0,
            "Environment map GParam ID.",
        ),
    }

    FIELD_ORDER = MSBPart.FIELD_ORDER + (
        "light_set_id",
        "fog_id",
        "light_scattering_id",
        "environment_map_id",
    )

    light_set_id: int
    fog_id: int
    light_scattering_id: int
    environment_map_id: int

    def _unpack_gparam_data(self, msb_reader: BinaryReader, part_offset, header):
        if header["__gparam_data_offset"] == 0:
            raise ValueError(f"Zero GParam offset found in GParam-supporting part {self.name}.")
        msb_reader.seek(part_offset + header["__gparam_data_offset"])
        gparam_data = msb_reader.unpack_struct(self.PART_GPARAM_STRUCT)
        self.set(**gparam_data)

    def _pack_gparam_data(self):
        return self.PART_GPARAM_STRUCT.pack(self)
Exemple #24
0
class ESD(_BaseESD, DarkSoulsPTDEType, abc.ABC):

    EXTERNAL_HEADER_STRUCT = BinaryStruct(
        ("version", "4s", b"fSSL"),  # Note specific case.
        ("one", "i", 1),
        ("game_version", "2i", [GAME_VERSION, GAME_VERSION]),
        ("table_size_offset", "i", 84),
        ("internal_data_size", "i"),  # excludes header size
        ("unk", "i", 6),
        ("internal_header_size", "i", 44),
        ("internal_header_count", "i", 1),
        ("state_machine_header_size", "i", 16),
        ("state_machine_count", "i"),
        ("state_size", "i", 36),
        ("state_count", "i"),
        ("condition_size", "i", 28),
        ("condition_count", "i"),
        ("command_size", "i", 16),
        ("command_count", "i"),
        ("command_arg_size", "i", 8),
        ("command_arg_count", "i"),
        ("condition_pointers_offset", "i"),
        ("condition_pointers_count", "i"),
        ("esd_name_offset_minus_1", "i"),
        ("esd_name_length", "i"),
        ("unk_offset_1", "i"),
        ("unk_size_1", "i", 0),
        ("unk_offset_2", "i"),
        ("unk_size_2", "i", 0),
    )
    INTERNAL_HEADER_STRUCT = BinaryStruct(
        ("one", "i", 1),
        ("magic", "4i"),  # TODO: constant within games, at least?
        ("state_machine_headers_offset", "i", 44),
        ("state_machine_count", "i"),
        ("esd_name_offset", "i"),  # accurate, unlike external header
        ("esd_name_length", "i"),
        "8x",
    )
    STATE_MACHINE_HEADER_STRUCT = BinaryStruct(
        ("state_machine_index", "i"),
        ("state_machine_offset", "i"),
        ("state_count", "i"),  # number of states
        ("state_machine_offset_2", "i"),  # duplicate
    )
Exemple #25
0
class MSBNavigationEvent(MSBEvent):
    ENTRY_SUBTYPE = MSBEventSubtype.Navigation
    EVENT_TYPE_DATA_STRUCT = BinaryStruct(
        ("_navigation_region_index", "i"),
        "12x",
    )

    FIELD_INFO = MSBEvent.FIELD_INFO | {
        "entity_id":
        MapFieldInfo(
            "Entity ID",
            int,
            -1,
            "Entity ID used to refer to the Navigation event in other game files.",
        ),
        "navigation_region_name":
        MapFieldInfo(
            "Navmesh Region",
            Region,
            None,
            "Region to which Navigation event is attached, which encloses faces of one or more Navmesh parts.",
        ),
    }

    FIELD_ORDER = (
        "entity_id",
        "navigation_region_name",
    )

    REFERENCE_FIELDS = {
        "parts": ["base_part_name"],
        "regions": ["base_region_name", "navigation_region_name"]
    }

    navigation_region_name: tp.Optional[str]

    def __init__(self, source=None, **kwargs):
        self._navigation_region_index = None
        super().__init__(source=source, **kwargs)

    def set_indices(self, event_index, local_event_index, region_indices,
                    part_indices):
        super().set_indices(event_index, local_event_index, region_indices,
                            part_indices)
        if self.navigation_region_name:
            self._navigation_region_index = region_indices[
                self.navigation_region_name]
        else:
            self._navigation_region_index = -1

    def set_names(self, region_names, part_names):
        super().set_names(region_names, part_names)
        if self._navigation_region_index != -1:
            self.navigation_region_name = region_names[
                self._navigation_region_index]
        else:
            self.navigation_region_name = None
Exemple #26
0
class FBXNode64(FBXNodeBase):
    STRUCT = BinaryStruct(
        ("__end_offset", "q"),
        ("__property_count", "q"),
        ("__property_list_size", "q"),
        ("__name_length", "B"),
    )

    children: list[FBXNode64]
Exemple #27
0
class MSBEvent(_BaseMSBEvent, abc.ABC):

    EVENT_HEADER_STRUCT = BinaryStruct(
        ("__name_offset", "i"),
        ("_event_index", "i"),
        ("__event_type", "i"),
        ("_local_event_index", "i"),
        ("__base_data_offset", "i"),
        ("__type_data_offset", "i"),
        "4x",
    )
    EVENT_BASE_DATA_STRUCT = BinaryStruct(
        ("_base_part_index", "i"),
        ("_base_region_index", "i"),
        ("entity_id", "i"),
        "4x",
    )
    NAME_ENCODING = "shift_jis_2004"
Exemple #28
0
class MSBNavmesh(_BaseMSBNavmesh, MSBPart):
    PART_TYPE_DATA_STRUCT = BinaryStruct(
        ("__navmesh_groups", "4I"),
        "16x",
    )

    FIELD_INFO = MSBPart.FIELD_INFO | _BaseMSBNavmesh.FIELD_INFO | {
        "navmesh_groups":
        MapFieldInfo(
            "Navmesh Groups",
            list,
            set(range(MSBPart.FLAG_SET_SIZE)),
            "Controls collision backread.",
        ),
    }

    FIELD_ORDER = _BaseMSBNavmesh.FIELD_ORDER + ("navmesh_groups", )

    def __init__(self, source=None, **kwargs):
        self._navmesh_groups = set()
        if source is None:
            kwargs.setdefault("is_shadow_source", True)
        super().__init__(source=source, **kwargs)

    def unpack_type_data(self, msb_reader: BinaryReader):
        data = msb_reader.unpack_struct(self.PART_TYPE_DATA_STRUCT,
                                        exclude_asserted=True)
        self._navmesh_groups = int_group_to_bit_set(data["__navmesh_groups"],
                                                    assert_size=4)

    def pack_type_data(self):
        return self.PART_TYPE_DATA_STRUCT.pack(
            __navmesh_groups=bit_set_to_int_group(self._navmesh_groups,
                                                  group_size=4), )

    @property
    def navmesh_groups(self):
        return self._navmesh_groups

    @navmesh_groups.setter
    def navmesh_groups(self, value):
        """Converts value to a `set()` (possibly empty) and validates index range."""
        if value is None or isinstance(value, str) and value in {"None", ""}:
            self._navmesh_groups = set()
            return
        try:
            navmesh_groups = set(value)
        except (TypeError, ValueError):
            raise TypeError(
                "Navmesh groups must be a set, sequence, `None`, 'None', or ''. Or use `set` methods like `.add()`."
            )
        for i in navmesh_groups:
            if not isinstance(i, int) and 0 <= i < self.FLAG_SET_SIZE:
                raise ValueError(
                    f"Invalid navmesh group: {i}. Must be 0 <= i < {self.FLAG_SET_SIZE}."
                )
        self._navmesh_groups = navmesh_groups
Exemple #29
0
class MSBCharacter(_BaseMSBCharacter, MSBPart):
    PART_TYPE_DATA_STRUCT = BinaryStruct(
        "8x",
        ("ai_id", "i"),
        ("character_id", "i"),
        ("talk_id", "i"),
        ("patrol_type", "B"),
        "x",
        ("platoon_id", "H"),
        ("player_id", "i"),
        ("_draw_parent_index", "i"),
        "8x",
        ("_patrol_region_indices", "8h"),
        ("default_animation", "i"),
        ("damage_animation", "i"),
    )

    FIELD_INFO = MSBPart.FIELD_INFO | _BaseMSBCharacter.FIELD_INFO | {
        "patrol_type":
        MapFieldInfo(
            "Patrol Type",
            int,
            0,
            "Patrol behavior type. (Effects unknown.)",
        ),
        "platoon_id":
        MapFieldInfo(
            "Platoon ID",
            int,
            0,
            "Unused 'platoon' ID value.",
        ),
    }

    FIELD_ORDER = _BaseMSBCharacter.FIELD_ORDER + (
        "patrol_type",
        # "platoon_id",
    ) + MSBPart.LIGHTING_FIELD_ORDER + (
        "is_shadow_source",
        "is_shadow_destination",
        "is_shadow_only",
        "draw_by_reflect_cam",
        "draw_only_reflect_cam",
        # "use_depth_bias_float",
        "disable_point_light_effect",
    )

    patrol_type: int
    platoon_id: int

    def __init__(self, source=None, **kwargs):
        if source is None:
            # Set some different defaults.
            kwargs.setdefault("is_shadow_source", True)
            kwargs.setdefault("is_shadow_destination", True)
            kwargs.setdefault("draw_by_reflect_cam", True)
        super().__init__(source=source, **kwargs)
Exemple #30
0
class MSBVFXEvent(MSBEvent):
    ENTRY_SUBTYPE = MSBEventSubtype.VFX

    EVENT_TYPE_DATA_STRUCT = BinaryStruct(
        ("vfx_id", "i"),
        ("starts_disabled", "i"),  # 32-bit bool
    )

    FIELD_INFO = MSBEvent.FIELD_INFO | {
        "base_part_name":
        MapFieldInfo(
            "Draw Parent",
            MapPart,
            None,
            "VFX will be drawn as long as this parent (usually a Collision or Map Piece part) is drawn.",
        ),
        "base_region_name":
        MapFieldInfo(
            "VFX Region",
            Region,
            None,
            "Region (usually a Point) at which VFX appears.",
        ),
        "entity_id":
        MapFieldInfo(
            "Entity ID",
            int,
            -1,
            "Entity ID used to refer to this VFX in other game files.",
        ),
        "vfx_id":
        MapFieldInfo(
            "VFX ID",
            int,
            0,
            "Visual effect ID, which refers to a loaded VFX file.",
        ),
        "starts_disabled":
        MapFieldInfo(
            "Starts Disabled",
            bool,
            False,
            "VFX will not be automatically created on map load (requires event script).",
        )
    }

    FIELD_ORDER = (
        "entity_id",
        "base_part_name",
        "base_region_name",
        "vfx_id",
        "starts_disabled",
    )

    vfx_id: int
    starts_disabled: bool