Beispiel #1
0
    def unpack(self, buffer, **kwargs):
        self.header_struct = BinaryStruct(*self.HEADER_STRUCT_START,
                                          byte_order="<")
        header = self.header_struct.unpack(buffer)
        self._check_version(header["version"].decode())
        self.signature = header["signature"].rstrip(b"\0").decode()
        self.magic = header["magic"]
        self.big_endian = header["big_endian"] or is_big_endian(self.magic)
        byte_order = ">" if self.big_endian else "<"
        header.update(
            self.header_struct.unpack(buffer,
                                      *self.HEADER_STRUCT_ENDIAN,
                                      byte_order=byte_order))
        self.unknown = header["unknown"]

        self.entry_header_struct = BinaryStruct(*self.BND_ENTRY_HEADER,
                                                byte_order=byte_order)
        if has_id(self.magic):
            self.entry_header_struct.add_fields(self.ENTRY_ID,
                                                byte_order=byte_order)
        if has_path(self.magic):
            self.entry_header_struct.add_fields(self.NAME_OFFSET,
                                                byte_order=byte_order)
        if has_uncompressed_size(self.magic):
            self.entry_header_struct.add_fields(self.UNCOMPRESSED_DATA_SIZE,
                                                byte_order=byte_order)

        # NOTE: BND paths are *not* encoded in `shift_jis_2004`, unlike most other strings! They are `shift-jis`.
        #  The main annoyance here is that escaped backslashes are encoded as the yen symbol in `shift_jis_2004`.
        for entry in BNDEntry.unpack(buffer,
                                     self.entry_header_struct,
                                     path_encoding="shift-jis",
                                     count=header["entry_count"]):
            self.add_entry(entry)
Beispiel #2
0
    def pack(self):
        header = self.header_struct.pack(goal_count=len(self.goals))
        packed_goals = b""
        packed_strings = b""
        goal_struct = BinaryStruct(
            *(self.GOAL_STRUCT_64
              if self.use_struct_64 else self.GOAL_STRUCT_32),
            byte_order=">" if self.big_endian else "<",
        )
        packed_strings_offset = len(header) + len(
            self.goals) * goal_struct.size
        encoding = self.encoding
        z_term = b"\0\0" if self.use_struct_64 else b"\0"
        for goal in self.goals:
            name_offset = packed_strings_offset + len(packed_strings)
            packed_strings += goal.goal_name.encode(encoding=encoding) + z_term
            goal_kwargs = goal.get_interrupt_details()
            logic_interrupt_name = goal_kwargs.pop("logic_interrupt_name")
            if logic_interrupt_name:
                logic_interrupt_name_offset = packed_strings_offset + len(
                    packed_strings)
                packed_strings += logic_interrupt_name.encode(
                    encoding=encoding) + z_term
            else:
                logic_interrupt_name_offset = 0
            packed_goals += goal_struct.pack(
                goal_id=goal.goal_id,
                name_offset=name_offset,
                logic_interrupt_name_offset=logic_interrupt_name_offset,
                **goal_kwargs,
            )

        return header + packed_goals + packed_strings
Beispiel #3
0
    def create_header_structs(self):
        self._most_recent_hash_table = b""  # Hash table will need to be built on first pack.
        self._most_recent_entry_count = len(self._entries)
        self._most_recent_paths = [entry.path for entry in self._entries]

        self.header_struct = BinaryStruct(*self.HEADER_STRUCT_START,
                                          byte_order="<")
        byte_order = ">" if self.big_endian else "<"
        self.header_struct.add_fields(*self.HEADER_STRUCT_ENDIAN,
                                      byte_order=byte_order)
        self.entry_header_struct = BinaryStruct(*self.BND_ENTRY_HEADER,
                                                byte_order=byte_order)
        if has_uncompressed_size(self.magic):
            self.entry_header_struct.add_fields(self.UNCOMPRESSED_DATA_SIZE,
                                                byte_order=byte_order)
        self.entry_header_struct.add_fields(self.DATA_OFFSET,
                                            byte_order=byte_order)
        if has_id(self.magic):
            self.entry_header_struct.add_fields(self.ENTRY_ID,
                                                byte_order=byte_order)
        if has_path(self.magic):
            self.entry_header_struct.add_fields(self.NAME_OFFSET,
                                                byte_order=byte_order)
        if self.magic == 0x20:
            # Extra pad.
            self.entry_header_struct.add_fields("8x")
Beispiel #4
0
    def __init__(self,
                 luainfo_source=None,
                 big_endian=False,
                 use_struct_64=False):
        self.big_endian = big_endian
        self.use_struct_64 = use_struct_64
        self.luainfo_path = None
        self.header_struct = BinaryStruct(
            *self.HEADER_STRUCT, byte_order=">" if self.big_endian else "<")

        self.goals = []  # type: List[LuaGoal]

        if luainfo_source is None:
            return
        if isinstance(luainfo_source, (list, tuple)):
            self.goals = luainfo_source  # type: List[LuaGoal]
            return
        if isinstance(luainfo_source, (str, Path)):
            self.luainfo_path = Path(luainfo_source)
            with self.luainfo_path.open("rb") as f:
                self.unpack(f)
            return
        if hasattr(luainfo_source, "data"):
            luainfo_source = luainfo_source.data
        if isinstance(luainfo_source, bytes):
            luainfo_source = io.BytesIO(luainfo_source)
        if isinstance(luainfo_source, io.BufferedIOBase):
            self.unpack(luainfo_source)
Beispiel #5
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.
Beispiel #6
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.
Beispiel #7
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.
Beispiel #8
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"), )
Beispiel #9
0
class MSBEntryList(_BaseMSBEntryList, abc.ABC):

    MAP_ENTITY_LIST_HEADER = BinaryStruct(
        "4x",
        ("name_offset", "i"),
        ("entry_offset_count", "i"),
    )
    MAP_ENTITY_ENTRY_OFFSET = BinaryStruct(("entry_offset", "i"), )
    MAP_ENTITY_LIST_TAIL = BinaryStruct(("next_entry_list_offset", "i"), )
    NAME_ENCODING = "utf-8"
Beispiel #10
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"), )
Beispiel #11
0
class ESD(_BaseESD, abc.ABC):
    GAME = DARK_SOULS_DSR

    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
    )
Beispiel #12
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)
Beispiel #13
0
class MSBMessageEvent(MSBEvent):
    ENTRY_SUBTYPE = MSBEventSubtype.Message
    EVENT_TYPE_DATA_STRUCT = BinaryStruct(
        ("text_id", "h"),
        ("unk_x02_x04", "h"),
        ("is_hidden", "?"),
        "3x",
    )

    FIELD_INFO = MSBEvent.FIELD_INFO | {
        "base_part_name": MapFieldInfo(
            "Draw Parent",
            MapPart,
            None,
            "Message will be drawn as long as this parent (usually a Collision or Map Piece part) is drawn.",
        ),
        "base_region_name": MapFieldInfo(
            "Message Region",
            Region,
            None,
            "Region (usually a Point) at which Message appears.",
        ),
        "entity_id": MapFieldInfo(
            "Entity ID",
            int,
            -1,
            "Entity ID used to refer to the Message in other game files.",
        ),
        "text_id": MapFieldInfo(
            "Message Text ID",
            SoapstoneMessage,
            -1,
            "Soapstone Messages text ID shown when soapstone message is examined.",
        ),
        "unk_x02_x04": MapFieldInfo(
            "Unknown [02-04]",
            int,
            2,
            "Unknown. Often set to 2.",
        ),
        "is_hidden": MapFieldInfo(
            "Is Hidden",
            bool,
            False,
            "If True, the message must be manually enabled with an event script or by using Seek Guidance.",
        ),
    }

    FIELD_ORDER = (
        "entity_id",
        "base_part_name",
        "base_region_name",
        "text_id",
        "unk_x02_x04",
        "is_hidden",
    )

    text_id: int
    unk_x02_x04: int
    is_hidden: bool
Beispiel #14
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
Beispiel #15
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.",
        ),
    }
Beispiel #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",
    )
Beispiel #17
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)
Beispiel #18
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
Beispiel #19
0
class EventArg(_BaseEventArg):
    HEADER_STRUCT = BinaryStruct(
        ("instruction_line", "Q"),
        ("write_from_byte", "Q"),
        ("read_from_byte", "Q"),
        ("bytes_to_write", "Q"),
    )
Beispiel #20
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",
    )
Beispiel #21
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),
    )
Beispiel #22
0
class ESD(_BaseESD, abc.ABC):
    GAME = DARK_SOULS_PTDE

    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
    )
Beispiel #23
0
 def create_header_structs(self):
     self.header_struct = BinaryStruct(*self.HEADER_STRUCT_START,
                                       byte_order="<")
     byte_order = ">" if self.big_endian else "<"
     self.header_struct.add_fields(*self.HEADER_STRUCT_ENDIAN,
                                   byte_order=byte_order)
     self.entry_header_struct = BinaryStruct(*self.BND_ENTRY_HEADER,
                                             byte_order=byte_order)
     if has_id(self.magic):
         self.entry_header_struct.add_fields(self.ENTRY_ID,
                                             byte_order=byte_order)
     if has_path(self.magic):
         self.entry_header_struct.add_fields(self.NAME_OFFSET,
                                             byte_order=byte_order)
     if has_uncompressed_size(self.magic):
         self.entry_header_struct.add_fields(self.UNCOMPRESSED_DATA_SIZE,
                                             byte_order=byte_order)
Beispiel #24
0
class MSBEvent(_BaseMSBEvent):

    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"
Beispiel #25
0
class EventLayers(_BaseEventLayers):
    """Never used in DS1 and very probably not actually supported by the engine."""

    HEADER_STRUCT = BinaryStruct(
        ("two", "I", 2),
        ("event_layers", "I"),  # 32-bit bit field
        ("zero", "I", 0),  # format is a guess
        ("minus_one", "i", -1),  # format is a guess
        ("one", "I", 1),  # format is a guess
    )
Beispiel #26
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)
Beispiel #27
0
class MSBTreasureEvent(_BaseMSBTreasureEvent, MSBEvent):
    EVENT_TYPE_DATA_STRUCT = BinaryStruct(
        "4x",
        ("_treasure_part_index", "i"),
        ("item_lot_1", "i"),
        ("minus_one_1", "i", -1),
        ("item_lot_2", "i"),
        ("minus_one_2", "i", -1),
        ("item_lot_3", "i"),
        ("minus_one_3", "i", -1),
        ("item_lot_4", "i"),
        ("minus_one_4", "i", -1),
        ("item_lot_5", "i"),
        ("minus_one_5", "i", -1),
        ("is_in_chest", "?"),
        ("is_hidden", "?"),
        "2x",
    )

    FIELD_INFO = _BaseMSBTreasureEvent.FIELD_INFO | MSBEvent.FIELD_INFO | {
        # base_part, base_region, and entity_id are unused for Treasure.
        "item_lot_4":
        MapFieldInfo(
            "Item Lot 4",
            ItemLotParam,
            -1,
            "Fourth item lot of treasure. (Note that the item lots that are +1 to +5 from this ID will also be "
            "awarded.)",
        ),
        "item_lot_5":
        MapFieldInfo(
            "Item Lot 5",
            ItemLotParam,
            -1,
            "Fifth item lot of treasure. (Note that the item lots that are +1 to +5 from this ID will also be "
            "awarded.)",
        ),
    }

    FIELD_ORDER = (
        "treasure_part_name",
        "item_lot_1",
        "item_lot_2",
        "item_lot_3",
        "item_lot_4",
        "item_lot_5",
        "is_in_chest",
        "is_hidden",
    )

    def __init__(self, source=None, **kwargs):
        self.item_lot_4 = -1
        self.item_lot_5 = -1
        super().__init__(source, **kwargs)
Beispiel #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_buffer):
        data = self.PART_TYPE_DATA_STRUCT.unpack(msb_buffer, 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
Beispiel #29
0
class Instruction(_BaseInstruction):
    DECOMPILER = InstructionDecompiler()
    INSTRUCTION_ARG_TYPES = INSTRUCTION_ARG_TYPES
    EventLayers = EventLayers
    HEADER_STRUCT = BinaryStruct(
        ("instruction_class", "I"),
        ("instruction_index", "I"),
        ("base_args_size", "Q"),
        ("first_base_arg_offset", "i"),
        "4x",
        ("first_event_layers_offset", "q"),
    )
Beispiel #30
0
class State(_BaseState, abc.ABC):
    STRUCT = BinaryStruct(
        ("index", "q"),
        ("condition_pointers_offset", "q"),
        ("condition_pointers_count", "q"),
        ("enter_commands_offset", "q"),
        ("enter_commands_count", "q"),
        ("exit_commands_offset", "q"),
        ("exit_commands_count", "q"),
        ("ongoing_commands_offset", "q"),
        ("ongoing_commands_count", "q"),
    )