Пример #1
0
    def pack_type_data(self):
        navmesh_groups = _enabled_flags_to_flag_group(self.navmesh_groups)

        if self.area_name_id == -1 and not self.__force_area_banner:
            raise InvalidFieldValueError(
                "'force_area_banner' must be enabled if 'area_name_id' is -1 (default)."
            )
        signed_area_name_id = self.area_name_id * (
            -1 if self.area_name_id >= 0 and self.__force_area_banner else 1)
        if self.__stable_footing_flag != 0:
            play_region_id = -self.__stable_footing_flag - 10
        else:
            play_region_id = self.__play_region_id
        return BinaryStruct(*self.PART_COLLISION_STRUCT).pack(
            hit_filter_id=self.hit_filter_id,
            sound_space_type=self.sound_space_type,
            env_light_map_spot_index=self.env_light_map_spot_index,
            reflect_plane_height=self.reflect_plane_height,
            navmesh_groups=navmesh_groups,
            vagrant_entity_ids=self.vagrant_entity_ids,
            area_name_id=signed_area_name_id,
            starts_disabled=self.starts_disabled,
            attached_bonfire=self.attached_bonfire,
            play_region_id=play_region_id,
            lock_cam_param_id_1=self.lock_cam_param_id_1,
            lock_cam_param_id_2=self.lock_cam_param_id_2,
        )
Пример #2
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.PART_OBJECT_STRUCT).unpack(msb_buffer)
     self._collision_index = data.collision_index
     self.unk_x08_x0c = data.unk_x08_x0c
     self.object_pose = data.object_pose
     self.unk_x0e_x10 = data.unk_x0e_x10
     self.unk_x10_x14 = data.unk_x10_x14
Пример #3
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.EVENT_OBJ_ACT_STRUCT).unpack(msb_buffer)
     self.obj_act_entity_id = data.obj_act_entity_id
     self._obj_act_part_index = data.obj_act_part_index
     self.obj_act_param_id = data.obj_act_param_id
     self.unk_x0a_x0c = data.unk_x0a_x0c
     self.obj_act_flag = data.obj_act_flag
Пример #4
0
 def pack_type_data(self):
     return BinaryStruct(*self.PART_OBJECT_STRUCT).pack(
         collision_index=self._collision_index,
         unk_x08_x0c=self.unk_x08_x0c,
         object_pose=self.object_pose,
         unk_x0e_x10=self.unk_x0e_x10,
         unk_x10_x14=self.unk_x10_x14,
     )
Пример #5
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.EVENT_ENVIRONMENT_STRUCT).unpack(msb_buffer)
     self.unk_x00_x04 = data.unk_x00_x04
     self.unk_x04_x08 = data.unk_x04_x08
     self.unk_x08_x0c = data.unk_x08_x0c
     self.unk_x0c_x10 = data.unk_x0c_x10
     self.unk_x10_x14 = data.unk_x10_x14
     self.unk_x14_x18 = data.unk_x14_x18
Пример #6
0
 def pack_type_data(self):
     return BinaryStruct(*self.EVENT_OBJ_ACT_STRUCT).pack(
         obj_act_entity_id=self.obj_act_entity_id,
         obj_act_part_index=self._obj_act_part_index,
         obj_act_param_id=self.obj_act_param_id,
         unk_x0a_x0c=self.unk_x0a_x0c,
         obj_act_flag=self.obj_act_flag,
     )
Пример #7
0
 def pack_type_data(self):
     return BinaryStruct(*self.EVENT_ENVIRONMENT_STRUCT).pack(
         unk_x00_x04=self.unk_x00_x04,
         unk_x04_x08=self.unk_x04_x08,
         unk_x08_x0c=self.unk_x08_x0c,
         unk_x0c_x10=self.unk_x0c_x10,
         unk_x10_x14=self.unk_x10_x14,
         unk_x14_x18=self.unk_x14_x18,
     )
Пример #8
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.EVENT_TREASURE_STRUCT).unpack(msb_buffer)
     self._treasure_part_index = data.treasure_part_index
     self.item_lot_1 = data.item_lot_1
     self.item_lot_2 = data.item_lot_2
     self.item_lot_3 = data.item_lot_3
     self.item_lot_4 = data.item_lot_4
     self.item_lot_5 = data.item_lot_5
     self.in_chest = data.in_chest
     self.starts_disabled = data.starts_disabled
Пример #9
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.PART_CHARACTER_STRUCT).unpack(msb_buffer)
     self.think_param_id = data.think_param_id
     self.npc_param_id = data.npc_param_id
     self.talk_id = data.talk_id
     self.unk_x14_x18 = data.unk_x14_x18
     self.chara_init_id = data.chara_init_id
     self._collision_index = data.collision_index
     self._patrol_point_indices = data.patrol_point_indices
     self.default_animation = data.default_animation
     self.unk_x3c_x40 = data.unk_x3c_x40
Пример #10
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.EVENT_SPAWNER_STRUCT).unpack(msb_buffer)
     self.max_count = data.max_count
     self.limit_count = data.limit_count
     self.min_spawner_count = data.min_spawner_count
     self.max_spawner_count = data.max_spawner_count
     self.min_interval = data.min_interval
     self.max_interval = data.max_interval
     self.initial_spawn_count = data.initial_spawn_count
     self._spawn_region_indices = data.spawn_region_indices
     self._spawn_part_indices = data.spawn_part_indices
Пример #11
0
 def pack_type_data(self):
     return BinaryStruct(*self.EVENT_TREASURE_STRUCT).pack(
         treasure_part_index=self._treasure_part_index,
         item_lot_1=self.item_lot_1,
         item_lot_2=self.item_lot_2,
         item_lot_3=self.item_lot_3,
         item_lot_4=self.item_lot_4,
         item_lot_5=self.item_lot_5,
         in_chest=self.in_chest,
         starts_disabled=self.starts_disabled,
     )
Пример #12
0
 def pack_type_data(self):
     return BinaryStruct(*self.EVENT_SPAWNER_STRUCT).pack(
         max_count=self.max_count,
         limit_count=self.limit_count,
         min_spawner_count=self.min_spawner_count,
         max_spawner_count=self.max_spawner_count,
         min_interval=self.min_interval,
         max_interval=self.max_interval,
         initial_spawn_count=self.initial_spawn_count,
         spawn_region_indices=self._spawn_region_indices,
         spawn_part_indices=self._spawn_part_indices,
     )
Пример #13
0
 def pack_type_data(self):
     return BinaryStruct(*self.PART_CHARACTER_STRUCT).pack(
         think_param_id=self.think_param_id,
         npc_param_id=self.npc_param_id,
         talk_id=self.talk_id,
         unk_x14_x18=self.unk_x14_x18,
         chara_init_id=self.chara_init_id,
         collision_index=self._collision_index,
         patrol_point_indices=self._patrol_point_indices,
         default_animation=self.default_animation,
         unk_x3c_x40=self.unk_x3c_x40,
     )
Пример #14
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.EVENT_WIND_STRUCT).unpack(msb_buffer)
     self.unk_x00_x04 = data.unk_x00_x04
     self.unk_x04_x08 = data.unk_x04_x08
     self.unk_x08_x0c = data.unk_x08_x0c
     self.unk_x0c_x10 = data.unk_x0c_x10
     self.unk_x10_x14 = data.unk_x10_x14
     self.unk_x14_x18 = data.unk_x14_x18
     self.unk_x18_x1c = data.unk_x18_x1c
     self.unk_x1c_x20 = data.unk_x1c_x20
     self.unk_x20_x24 = data.unk_x20_x24
     self.unk_x24_x28 = data.unk_x24_x28
     self.unk_x28_x2c = data.unk_x28_x2c
     self.unk_x2c_x30 = data.unk_x2c_x30
     self.unk_x30_x34 = data.unk_x30_x34
     self.unk_x34_x38 = data.unk_x34_x38
     self.unk_x38_x3c = data.unk_x38_x3c
     self.unk_x3c_x40 = data.unk_x3c_x40
Пример #15
0
 def pack_type_data(self):
     return BinaryStruct(*self.EVENT_WIND_STRUCT).pack(
         unk_x00_x04=self.unk_x00_x04,
         unk_x04_x08=self.unk_x04_x08,
         unk_x08_x0c=self.unk_x08_x0c,
         unk_x0c_x10=self.unk_x0c_x10,
         unk_x10_x14=self.unk_x10_x14,
         unk_x14_x18=self.unk_x14_x18,
         unk_x18_x1c=self.unk_x18_x1c,
         unk_x1c_x20=self.unk_x1c_x20,
         unk_x20_x24=self.unk_x20_x24,
         unk_x24_x28=self.unk_x24_x28,
         unk_x28_x2c=self.unk_x28_x2c,
         unk_x2c_x30=self.unk_x2c_x30,
         unk_x30_x34=self.unk_x30_x34,
         unk_x34_x38=self.unk_x34_x38,
         unk_x38_x3c=self.unk_x38_x3c,
         unk_x3c_x40=self.unk_x3c_x40,
     )
Пример #16
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.PART_COLLISION_STRUCT).unpack(msb_buffer)
     self.hit_filter_id = data.hit_filter_id
     self.sound_space_type = data.sound_space_type
     self.env_light_map_spot_index = data.env_light_map_spot_index
     self.reflect_plane_height = data.reflect_plane_height
     self.navmesh_groups = _flag_group_to_enabled_flags(data.navmesh_groups)
     self.vagrant_entity_ids = data.vagrant_entity_ids
     self.area_name_id = abs(
         data.area_name_id) if data.area_name_id >= 0 else -1
     self.__force_area_banner = data.area_name_id < 0  # Custom field.
     self.starts_disabled = data.starts_disabled
     self.attached_bonfire = data.attached_bonfire
     if data.play_region_id > -10:
         self.__play_region_id = data.play_region_id
         self.__stable_footing_flag = 0
     else:
         self.__play_region_id = 0
         self.__stable_footing_flag = -data.play_region_id - 10
     self.lock_cam_param_id_1 = data.lock_cam_param_id_1
     self.lock_cam_param_id_2 = data.lock_cam_param_id_2
Пример #17
0
 def pack_type_data(self):
     return BinaryStruct(*self.BOX_STRUCT).pack(
         width=self.width,
         depth=self.depth,
         height=self.height,
     )
Пример #18
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.BOX_STRUCT).unpack(msb_buffer)
     self.width = data.width
     self.depth = data.depth
     self.height = data.height
Пример #19
0
 def pack_type_data(self):
     return BinaryStruct(*self.RECT_STRUCT).pack(
         width=self.width,
         depth=self.depth,
     )
Пример #20
0
class BaseMSBRegion(MSBEntryEntity):

    REGION_STRUCT = BinaryStruct(
        ('name_offset', 'i'),
        '4x',
        ('region_index', 'i'),
        ('region_type', 'i'),
        ('translate', '3f'),
        ('rotate', '3f'),  # These are Euler angle rotations (and can therefore be gimbal-locked).
        ('unknown_offset_1', 'i'),
        ('unknown_offset_2', 'i'),
        ('type_data_offset', 'i'),
        ('entity_id_offset', 'i'),
        '4x',
    )

    FIELD_INFO = {
        'translate': (
            'Translate', True, Vector,
            "3D coordinates of the region's position. Note that this is the middle of the bottom face for box "
            "regions."),
        'rotate': (
            'Rotate', True, Vector,
            "Euler angles for region rotation around its local X, Y, and Z axes."),
        'entity_id': (
            'Entity ID', True, int,
            "Entity ID used to refer to the region in other game files."),
    }

    ENTRY_TYPE = None

    def __init__(self, msb_region_source):
        super().__init__()
        self._region_index = None
        self.translate = None
        self.rotate = None

        if isinstance(msb_region_source, bytes):
            msb_region_source = BytesIO(msb_region_source)
        if isinstance(msb_region_source, BufferedReader):
            self.unpack(msb_region_source)
        else:
            raise TypeError("'msb_model_source' must be a buffer or bytes.")

    def unpack(self, msb_buffer):
        region_offset = msb_buffer.tell()
        base_data = self.REGION_STRUCT.unpack(msb_buffer)
        self.name = read_chars_from_buffer(
            msb_buffer, offset=region_offset + base_data.name_offset, encoding='shift-jis')
        self._region_index = base_data.region_index
        self.translate = Vector(base_data.translate)
        self.rotate = Vector(base_data.rotate)
        self.check_null_field(msb_buffer, region_offset + base_data.unknown_offset_1)
        self.check_null_field(msb_buffer, region_offset + base_data.unknown_offset_2)

        if base_data.type_data_offset != 0:
            msb_buffer.seek(region_offset + base_data.type_data_offset)
            self.unpack_type_data(msb_buffer)

        msb_buffer.seek(region_offset + base_data.entity_id_offset)
        self.entity_id = struct.unpack('i', msb_buffer.read(4))[0]

        return region_offset + base_data.entity_id_offset

    def unpack_type_data(self, msb_buffer):
        raise NotImplementedError

    def pack(self):
        name_offset = self.REGION_STRUCT.size
        packed_name = pad_chars(self.get_name_to_pack(), encoding='shift-jis', pad_to_multiple_of=4)
        unknown_offset_1 = name_offset + len(packed_name)
        unknown_offset_2 = unknown_offset_1 + 4
        packed_type_data = self.pack_type_data()
        if packed_type_data:
            type_data_offset = unknown_offset_2 + 4
            entity_id_offset = type_data_offset + len(packed_type_data)
        else:
            type_data_offset = 0
            entity_id_offset = unknown_offset_2 + 4
        packed_base_data = self.REGION_STRUCT.pack(
            name_offset=name_offset,
            region_index=self._region_index,
            region_type=self.ENTRY_TYPE,
            translate=list(self.translate),
            rotate=list(self.rotate),
            unknown_offset_1=unknown_offset_1,
            unknown_offset_2=unknown_offset_2,
            type_data_offset=type_data_offset,
            entity_id_offset=entity_id_offset,
        )
        packed_entity_id = struct.pack('i', self.entity_id)
        return packed_base_data + packed_name + b'\0\0\0\0' * 2 + packed_type_data + packed_entity_id

    def pack_type_data(self):
        raise NotImplementedError

    def set_indices(self, region_index):
        self._region_index = region_index

    @staticmethod
    def check_null_field(msb_buffer, offset_to_null):
        msb_buffer.seek(offset_to_null)
        zero = msb_buffer.read(4)
        if zero != b'\0\0\0\0':
            _LOGGER.warning(f"Null data entry in MSB region was not zero: {zero}.")

    @staticmethod
    def auto_region_subclass(msb_buffer):
        old_offset = msb_buffer.tell()
        msb_buffer.seek(old_offset + 12)
        try:
            region_type_int = struct.unpack('i', msb_buffer.read(4))[0]
            region_type = MSB_REGION_TYPE(region_type_int)
        except (ValueError, TypeError):
            region_type = None
        msb_buffer.seek(old_offset)
        return REGION_TYPE_CLASSES[region_type](msb_buffer)
Пример #21
0
 def pack_type_data(self):
     return BinaryStruct(*self.CYLINDER_STRUCT).pack(
         radius=self.radius,
         height=self.height,
     )
Пример #22
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.CYLINDER_STRUCT).unpack(msb_buffer)
     self.radius = data.radius
     self.height = data.height
Пример #23
0
 def pack_type_data(self):
     return BinaryStruct(*self.SPHERE_STRUCT).pack(
         radius=self.radius,
     )
Пример #24
0
 def unpack_type_data(self, msb_buffer):
     self.radius = BinaryStruct(*self.SPHERE_STRUCT).unpack(msb_buffer).radius
Пример #25
0
 def pack_type_data(self):
     return BinaryStruct(*self.BOX_STRUCT).pack(
         width=float(self.width), depth=float(self.depth), height=float(self.height)
     )
Пример #26
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(*self.RECT_STRUCT).unpack(msb_buffer)
     self.width = data["width"]
     self.depth = data["depth"]
Пример #27
0
class BaseMSBRegion(MSBEntryEntityCoordinates):

    REGION_STRUCT = BinaryStruct(
        ("name_offset", "i"),
        "4x",
        ("region_index", "i"),
        ("region_type", "i"),
        ("translate", "3f"),
        ("rotate", "3f"),  # These are Euler angle rotations (and can therefore be gimbal-locked).
        ("unknown_offset_1", "i"),
        ("unknown_offset_2", "i"),
        ("type_data_offset", "i"),
        ("entity_id_offset", "i"),
        "4x",
    )

    FIELD_INFO = {
        "translate": (
            "Translate",
            True,
            Vector3,
            "3D coordinates of the region's position. Note that this is the middle of the bottom face for box "
            "regions.",
        ),
        "rotate": ("Rotate", True, Vector3, "Euler angles for region rotation around its local X, Y, and Z axes."),
        "entity_id": ("Entity ID", True, int, "Entity ID used to refer to the region in other game files."),
    }

    ENTRY_SUBTYPE = None  # type: MSBRegionSubtype

    def __init__(self, msb_region_source=None):
        super().__init__()
        self._region_index = None  # Final automatic assignment done on `MSB.pack()`.

        if isinstance(msb_region_source, bytes):
            msb_region_source = BytesIO(msb_region_source)
        if isinstance(msb_region_source, BufferedReader):
            self.unpack(msb_region_source)
        elif msb_region_source is not None:
            raise TypeError("`msb_model_source` must be a buffer, `bytes`, or `None`.")

    def unpack(self, msb_buffer):
        region_offset = msb_buffer.tell()
        base_data = self.REGION_STRUCT.unpack(msb_buffer)
        self.name = read_chars_from_buffer(
            msb_buffer, offset=region_offset + base_data["name_offset"], encoding="shift-jis"
        )
        self._region_index = base_data["region_index"]
        self.translate = Vector3(base_data["translate"])
        self.rotate = Vector3(base_data["rotate"])
        self.check_null_field(msb_buffer, region_offset + base_data["unknown_offset_1"])
        self.check_null_field(msb_buffer, region_offset + base_data["unknown_offset_2"])

        if base_data["type_data_offset"] != 0:
            msb_buffer.seek(region_offset + base_data["type_data_offset"])
            self.unpack_type_data(msb_buffer)

        msb_buffer.seek(region_offset + base_data["entity_id_offset"])
        self.entity_id = struct.unpack("i", msb_buffer.read(4))[0]

        return region_offset + base_data["entity_id_offset"]

    def unpack_type_data(self, msb_buffer):
        # TODO: Use type struct.
        raise NotImplementedError

    def pack(self):
        name_offset = self.REGION_STRUCT.size
        packed_name = pad_chars(self.get_name_to_pack(), encoding="shift-jis", pad_to_multiple_of=4)
        unknown_offset_1 = name_offset + len(packed_name)
        unknown_offset_2 = unknown_offset_1 + 4
        packed_type_data = self.pack_type_data()
        if packed_type_data:
            type_data_offset = unknown_offset_2 + 4
            entity_id_offset = type_data_offset + len(packed_type_data)
        else:
            type_data_offset = 0
            entity_id_offset = unknown_offset_2 + 4
        packed_base_data = self.REGION_STRUCT.pack(
            name_offset=name_offset,
            region_index=self._region_index,
            region_type=self.ENTRY_SUBTYPE,
            translate=list(self.translate),
            rotate=list(self.rotate),
            unknown_offset_1=unknown_offset_1,
            unknown_offset_2=unknown_offset_2,
            type_data_offset=type_data_offset,
            entity_id_offset=entity_id_offset,
        )
        packed_entity_id = struct.pack("i", self.entity_id)
        return packed_base_data + packed_name + b"\0\0\0\0" * 2 + packed_type_data + packed_entity_id

    def pack_type_data(self):
        raise NotImplementedError

    def set_indices(self, region_index):
        self._region_index = region_index

    @staticmethod
    def check_null_field(msb_buffer, offset_to_null):
        msb_buffer.seek(offset_to_null)
        zero = msb_buffer.read(4)
        if zero != b"\0\0\0\0":
            _LOGGER.warning(f"Null data entry in MSB region was not zero: {zero}.")

    @staticmethod
    def auto_region_subclass(msb_buffer):
        old_offset = msb_buffer.tell()
        msb_buffer.seek(old_offset + 12)
        try:
            region_type = MSBRegionSubtype(struct.unpack("i", msb_buffer.read(4))[0])
        except (ValueError, TypeError):
            raise ValueError("Could not detect region subtype from MSB data.")
        msb_buffer.seek(old_offset)
        return MSB_REGION_TYPE_CLASSES[region_type](msb_buffer)
Пример #28
0
 def unpack_type_data(self, msb_buffer):
     self.radius = BinaryStruct(*self.CIRCLE_STRUCT).unpack(msb_buffer)["radius"]
Пример #29
0
 def pack_type_data(self):
     return BinaryStruct(*self.PART_MAP_LOAD_TRIGGER_STRUCT).pack(
         collision_index=self._collision_index,
         map_id=self.map_id,
     )
Пример #30
0
 def unpack_type_data(self, msb_buffer):
     data = BinaryStruct(
         *self.PART_MAP_LOAD_TRIGGER_STRUCT).unpack(msb_buffer)
     self.collision_name = None
     self._collision_index = data.collision_index
     self.map_id = data.map_id  # TODO: Convert to a GameMap instance.