def warp_action(name='warp_action'): return Struct( name, Byte('warp_type'), Switch('warp_action_type', lambda ctx: ctx['warp_type'], { 1: LazyBound('next', lambda: WARP_WORLD), 2: HexAdapter(Field('uuid', 16)), 3: SBInt32('alias') }, default=Pass))
"data" / Switch( lambda ctx: ctx.fxMagic, { 'FXP_PARAMS': "data" / Struct( "prgName" / String(28, padchar='\0'), Array(lambda ctx: ctx['_']['count'], "params" / Float32b), ), 'FXP_OPAQUE_CHUNK': "data" / Struct( "prgName" / String(28, padchar='\0'), "size" / Int32ub, "chunk" / Bytes(lambda ctx: ctx['size']), ), 'FXB_REGULAR': "data" / Struct( "future" / Bytes(128), # zeros # Array of FXP_PARAMS vst2preset Array(lambda ctx: ctx['_']['count'], "presets" / LazyBound(lambda: vst2preset)), ), 'FXB_OPAQUE_CHUNK': "data" / Struct( "future" / Bytes(128), # zeros "size" / Int32ub, # Unknown format of internal chunk "chunk" / Bytes(lambda ctx: ctx['size']), ), }), )
class HexAdapter(Adapter): def _encode(self, obj, context): # The code seems backward, but I assure you it's correct. return obj.decode('hex') def _decode(self, obj, context): return obj.encode('hex') WARP_WORLD = Struct( 'to_world', Byte('world_id'), Switch('world_type', lambda ctx: ctx['world_id'], { 1: LazyBound('next', lambda: WARP_WORLD_CELESTIAL), 2: LazyBound('next', lambda: WARP_WORLD_PLAYER), 3: LazyBound('next', lambda: WARP_WORLD_MISSION) }, default=Pass)) WARP_WORLD_CELESTIAL = Struct( 'celestial_world', SBInt32('x'), SBInt32('y'), SBInt32('z'), SBInt32('planet'), SBInt32('satellite'), Optional(Byte('has_position')), If(lambda ctx: ctx['has_position'], Struct('position', SBInt32('x'), SBInt32('y')))) WARP_WORLD_PLAYER = Struct( 'player_world', HexAdapter(Field('uuid', 16)), Optional(Byte('has_position')), If(lambda ctx: ctx['has_position'],
OP_TEST=26, OP_TESTSET=27, OP_CALL=28, OP_TAILCALL=29, OP_RETURN=30, OP_FORLOOP=31, OP_FORPREP=32, OP_TFORLOOP=33, OP_SETLIST=34, OP_CLOSE=35, OP_CLOSURE=36, OP_VARARG=37, ) # String = PascalString(LazyBound(lambda: LuaSize_t), "ascii") String = Struct("size" / LazyBound(lambda: LuaSize_t), "str" / Bytes(this.size)) GlobalHead = Struct( "signature" / Const(b"\x1bLua"), "version" / Version, "format" / Format, "endian" / Endian, "size_int" / Int8ul, "size_size_t" / Int8ul, "size_instruction" / Int8ul, "size_lua_number" / Int8ul, "lua_num_valid" / Byte, ) ProtoHead = Struct("source" / String, "linedefined" / Int32ul,
) thread_group_snapshot = Struct( predefined_name_substruct, 'obj' / Struct('tgs_id' / Int64ul, 'tgs_name' / Padded(16, CString('utf8')), 'tgs_flags' / Int64ul), ) type_array_pad0 = Struct( 'flags_type' / Computed(lambda ctx: (ctx._.flags >> 32) & 0xffffffff), 'type' / Computed(lambda ctx: kcdata_types_enum.decmapping[ctx.flags_type]), 'count' / Computed(lambda ctx: ctx._.flags & 0xffffffff), 'name' / Computed(lambda ctx: predefined_names[ctx.type]), 'obj' / Array( this.count, LazyBound(lambda: Switch(lambda ctx: ctx.type, kcdata_types_structures, default=GreedyBytes))), ) type_array_pad4 = Struct( 'flags_type' / Computed(lambda ctx: (ctx._.flags >> 32) & 0xffffffff), 'type' / Computed(lambda ctx: kcdata_types_enum.decmapping[ctx.flags_type]), 'count' / Computed(lambda ctx: ctx._.flags & 0xffffffff), 'name' / Computed(lambda ctx: predefined_names[ctx.type]), 'obj' / Array( this.count, LazyBound(lambda: Switch(lambda ctx: ctx.type, kcdata_types_structures, default=GreedyBytes))), Padding(4), )
"was_same_owner" / Int16ul, "indirect_fire_flag" / Byte, "move_sprite_id" / Int16ul, "fight_sprite_id" / Int16ul, "wait_sprite_id" / Int16ul, "last_target_position" / vector) unit_action = Struct( "type" / Int16ul, "data" / If( lambda ctx: ctx.type > 0, Struct( "state" / IfThenElse( lambda ctx: find_version(ctx) in [Version.AOK, Version.AOC], Byte, Int32ul), "target_object_pointer" / Int32sl, "target_object_pointer2" / Int32sl, "target_object_id" / Int32sl, "target_object_id_2" / Int32sl, "position" / vector, "timer" / Float32l, "target_moved_state" / Byte, "task_id" / Int16ul, "sub_action_value" / Byte, "sub_actions" / LazyBound(lambda x: action_list), "sprite_id" / Int16sl, Embedded("additional" / Switch(lambda ctx: ctx._.type, { 1: move_to, 3: enter, 9: attack, 21: make }, default=Pass))))) action_list = "action_list" / Struct("actions" / RepeatUntil( lambda x, lst, ctx: lst[-1].type == 0, unit_action)) action = "action" / Struct( Embedded(base_moving), "waiting" / Byte, "command_flag" / Byte, "selected_group_info" / If(lambda ctx: find_version(ctx) != Version.AOK, Int16ul), "actions" / action_list)
l = [] key = evaluate(self.key, context) for i in obj: l.append(i ^ key) return bytes(l) def _encode(self, obj, context, path): l = [] key = evaluate(self.key, context) for i in obj: l.append(i ^ key) return bytes(l) String = Struct( "size" / LazyBound(lambda: LuaSize_t), "str" / StrAdapter( (this.size * 13 + 55) & 0xff, Bytes(this.size))) GlobalHead = Struct("signature" / Const(b"\x1bFate/Z\x1b"), "version" / Version, "format" / Format, "endian" / Endian, "size_int" / Int8ul, "size_size_t" / Int8ul, "size_instruction" / Int8ul, "size_lua_number" / Int8ul, "lua_num_valid" / Byte) ProtoHead = Struct("numparams" / Int8ul, "source" / String, "nups" / Int8ul, "linedefined" / Int32ul, "is_vararg" / Int8ul, "lastlinedefined" / Int32ul, "maxstacksize" / Int8ul) class InstrctionAdapter(Adapter): def _decode(self, obj, context, path):
) unit_action = Struct( "type"/Int16ul, "data"/If(lambda ctx: ctx.type > 0, Struct( "state"/IfThenElse(lambda ctx: find_version(ctx) in [Version.AOK, Version.AOC, Version.HD], Byte, Int32ul), "target_object_pointer"/Int32sl, "target_object_pointer2"/Int32sl, "target_object_id"/Int32sl, "target_object_id_2"/Int32sl, "position"/vector, "timer"/Float32l, "target_moved_state"/Byte, "task_id"/Int16ul, "sub_action_value"/Byte, "sub_actions"/LazyBound(lambda x: action_list), "sprite_id"/Int16sl, Embedded("additional"/Switch(lambda ctx: ctx._.type, { 1: move_to, 3: enter, 9: attack, 21: make }, default=Pass)) )) ) action_list = "action_list"/Struct( "actions"/RepeatUntil(lambda x,lst,ctx: lst[-1].type == 0, unit_action) ) action = "action"/Struct(
Struct( 'data', String('prgName', 28, padchar='\0'), Array(lambda ctx: ctx['_']['count'], BFloat32('params')), ), 'FXP_OPAQUE_CHUNK': Struct( 'data', String('prgName', 28, padchar='\0'), UBInt32('size'), Bytes('chunk', lambda ctx: ctx['size']), ), 'FXB_REGULAR': Struct( 'data', Bytes('future', 128), # zeros # Array of FXP_PARAMS vst2preset Array(lambda ctx: ctx['_']['count'], LazyBound('presets', lambda: vst2preset)), ), 'FXB_OPAQUE_CHUNK': Struct( 'data', Bytes('future', 128), # zeros UBInt32('size'), # Unknown format of internal chunk Bytes('chunk', lambda ctx: ctx['size']), ), }), )
HEXINT32 = 20 HEXINT64 = 21 COUNTEDSTRING = 22 COUNTEDANSISTRING = 23 STRUCT = 24 COUNTEDBINARY = 25 CCOUNT = 32 VCCOUNT = 64 CHAIN = 128 TlMetaDataField = Struct( "name" / CString("ascii"), "tag_in" / Int8ul, "tag_out" / If(lambda this: this.tag_in & TagIn.CHAIN.value, LazyBound(lambda: Int8ul))) TlMetaData = Struct("size" / Int16ul, "tag" / Int8ul, "name" / CString("ascii"), "fields" / GreedyRange(TlMetaDataField)) def build_tracelogging(event): """ Build tracelogging based on the source event Check if event have an extended :param event: event that encompass Tracelogging data """ if event.extended_data is not None: for extended_data in event.extended_data: if extended_data.ext_type == 11:
'WIPTag', 'name' / WIPString, 'type' / WIPTagType, 'data_start' / Int64sl, 'data_end' / Int64sl, # 'start'/Tell, # OneOf(Computed(this.start == this.data_start), [True]), 'data_size' / Computed(this.data_end - this.data_start), 'data' / If( this.data_end > this.data_start, Switch( this.type, dict( WIP_TAG_LIST=RepeatUntil( lambda obj, ctx: obj.data_end >= ctx.data_end, LazyBound(lambda: WIPTag)), WIP_TAG_EXTENDED=Bytes(10), WIP_TAG_DOUBLE=Float64l, WIP_TAG_FLOAT=Float32l, WIP_TAG_INT64=Int64sl, WIP_TAG_INT32=Array(this.data_size // 4, Int32sl), WIP_TAG_UINT32=Array(this.data_size // 4, Int32ul), WIP_TAG_CHAR=Array(this.data_size, Int8ul), WIP_TAG_BOOL=Int8ul, WIP_TAG_STRING=WIPString, ))), 'end' / Tell, # pad to get to data_end Padding(this.data_end - this.end), )
tuple : 105, etString : 107, list : 108, Binary : 109, long : 111, Fun : 112, MFA : 113, map : 116, BitBinary : 77, } if obj == []: return 106 return mapping[obj.__class__] # Recurrent term term_ = LazyBound(lambda: term) atom_cache_ref = ExprAdapter(Int8ub, encoder=lambda obj, ctx: obj.index, decoder=lambda obj, ctx: AtomCacheReference(obj)) small_integer = Int8ub integer = Int32sb float_ = ExprAdapter(PaddedString(31, encoding="ascii"), encoder=lambda obj, ctx: u"{:.20e} ".format(obj), decoder=lambda obj, ctx: float(obj)) atom = PascalString(lengthfield=Int16ub, encoding="latin1") reference = ExprAdapter(Sequence("node" / term_, "id" / Int32ub, "creation" / Int8ub), encoder=lambda obj, ctx: (obj.node, obj.id, obj.creation), decoder=lambda obj, ctx: Reference(*obj))
class FNVPlugin(BasePlugin): """The plugin for Fallout: New Vegas. This plugin structure *should* handle plugins for the games: - Fallout 3 - Fallout: New Vegas Note: This structure *currently* reads all data on initialization. This may appear as *slower* initialization times for larger plugins. Should be fixed by `lazy constructs <https://construct.readthedocs.io/en/latest/lazy.html>`_ when they become more stable. **Credit:** - `FopDoc <https://tes5edit.github.io/fopdoc/FalloutNV/Records.html>`_ """ subrecord_struct = Struct( "type" / PaddedString(4, "utf8"), "data_size" / Int16ul, "data" / Bytes(lambda this: this.data_size), "parsed" / Computed( lambda this: FNVPlugin.parse_subrecord( this._.id, this._.type, this.type, this.data ) ), ) """The structure for FO3/FNV subrecords. Returns: :class:`~construct.core.Struct`: The structure of FO3/FNV subrecords """ record_struct = Struct( "type" / PaddedString(4, "utf8"), "data_size" / Int32ul, "flags" / FlagsEnum( Int32ul, master=0x00000001, _unknown_0=0x00000002, _unknown_1=0x00000004, _unknown_2=0x00000008, form_initialized=0x00000010, deleted=0x00000020, constant=0x00000040, fire_disabled=0x00000080, inaccessible=0x00000100, casts_shadows=0x00000200, persistent=0x00000400, initially_disabled=0x00000800, ignored=0x00001000, no_voice_filter=0x00002000, cannot_save=0x00004000, visible_when_distant=0x00008000, random_anim_start=0x00010000, dangerous=0x00020000, compressed=0x00040000, cant_wait=0x00080000, _unknown_3=0x00100000, _unknown_4=0x00200000, _unknown_5=0x00400000, _unknown_6=0x00800000, destructible=0x01000000, obstacle=0x02000000, navmesh_filter=0x04000000, navmesh_box=0x08000000, non_pipboy=0x10000000, child_can_use=0x20000000, navmesh_ground=0x40000000, _unknown_7=0x80000000, ), "id" / Int32ul, "revision" / Int32ul, "version" / Int16ul, "_unknown_0" / Int16ul, # NOTE: ignores compressed data size as it is handled by GreedyBytes If(lambda this: this.flags.compressed, Padding(4)), "data" / IfThenElse( lambda this: this.flags.compressed, Compressed(Bytes(lambda this: this.data_size), "zlib"), Bytes(lambda this: this.data_size), ), "subrecords" / Computed( lambda this: GreedyRange(FNVPlugin.subrecord_struct).parse( this.data, id=this.id, type=this.type ) ), ) """The structure for FO3/FNV records. Returns: :class:`~construct.core.Struct`: The structure of FO3/FNV records """ # TODO: instead of using ``GreedyRange`` to handle parsing unknown length lists, # should probably use other repeaters to avoid messy construct debugging # (will always raise exception when expects record type to exist, but gets 0 bytes) group_struct = Struct( "type" / Const(b"GRUP"), "group_size" / Int32ul, # TODO: find a better way of lazily building ``label`` in place # instead of computing it later # NOTE: deferred until group_type is determined "_label" / Bytes(4), "group_type" / Enum( Int32sl, top_level=0, world_children=1, interior_cell_block=2, interior_cell_subblock=3, exterior_cell_block=4, exterior_cell_subblock=5, cell_children=6, topic_children=7, cell_persistent_children=8, cell_temporary_children=9, cell_visible_distant_children=10, ), "label" / Computed( lambda this: Switch( this.group_type, { "top_level": PaddedString(4, "utf8"), "world_children": FNVFormID(["WRLD"]), "interior_cell_block": Int32sl, "interior_cell_subblock": Int32sl, "exterior_cell_block": Struct("y" / Int8sl, "x" / Int8sl), "exterior_cell_subblock": Struct("y" / Int8sl, "x" / Int8sl), "cell_children": FNVFormID(["CELL"]), "topic_children": FNVFormID(["DIAL"]), "cell_persistent_children": FNVFormID(["CELL"]), "cell_temporary_children": FNVFormID(["CELL"]), "cell_visible_distant_children": FNVFormID(["CELL"]), }, default=GreedyBytes, ).parse(this._label) ), "stamp" / Int16ul, "_unknown_0" / Bytes(6), "data" / Bytes(lambda this: this.group_size - 24), "subgroups" / If( lambda this: (len(this.data) > 4 and this.data[:4] == b"GRUP"), Computed( lambda this: GreedyRange( LazyBound(lambda: FNVPlugin.group_struct) ).parse(this.data) ), ), "records" / If( lambda this: this.subgroups is None, Computed( lambda this: GreedyRange(FNVPlugin.record_struct).parse(this.data) ), ), ) """The structure for FO3/FNV groups. Returns: :class:`~construct.core.Struct`: The structure of FO3/FNV groups """ plugin_struct = Struct( "header" / record_struct * "Plugin header record", "groups" / GreedyRange(group_struct) * "Plugin groups", ) """The structure for FO3/FNV plugins. Returns: :class:`~construct.core.Struct`: The structure of FO3/FNV plugins """ # NOTE: working record is mangaled in order to protect state during # subrecord parsing for record state __working_record = {} @classmethod def can_handle(cls, filepath: str) -> bool: """Determines if a given file can be handled by the plugin. Args: filepath (str): The filepath to evaluate Raises: FileNotFoundError: When the given `filepath` cannot be found Returns: bool: True if file can be handled, otherwise False """ if not os.path.isfile(filepath): raise FileNotFoundError(f"file {filepath!r} does not exist") header = cls.record_struct.parse_file(filepath) # NOTE: must clear class working record after every "full" file parse # otherwise, subsequent parses will have fragmented data when trying to discover # and parse subrecords cls.__working_record = {} return header.type == "TES4" and header.version == 15 @classmethod def parse_subrecord( cls, record_id: int, record_type: str, subrecord_type: str, subrecord_data: bytes, strict: bool = True, ) -> Container: """Parses a subrecord's data. Args: record_type (str): The parent record type subrecord_type (str): The subrecord type subrecord_data (bytes): The subrecord data to parse strict (bool): Defaults to True, If True, enforce strict subrecord discovery Returns: Container: The resulting parsed container """ (record_type, subrecord_type) = (record_type.upper(), subrecord_type.upper()) # handle reset of working record state if record_id not in cls.__working_record: cls.__working_record[record_id] = [] record_subrecords = RecordMapping.get(record_type) if record_subrecords: (parsed, working_record) = record_subrecords.handle_working( subrecord_type, subrecord_data, cls.__working_record[record_id], strict=strict, ) cls.__working_record[record_id].extend(working_record) return parsed
"BinaryArrayTypeEnum" / BinaryArrayTypeEnumeration, "Rank" / Int32ul, "Lengths" / Array(this.Rank, Int32ul), "LowerBounds" / If(this.BinaryArrayTypeEnum == BinaryArrayTypeEnumeration.SingleOffset or this.BinaryArrayTypeEnum == BinaryArrayTypeEnumeration.JaggedOffset or this.BinaryArrayTypeEnum == BinaryArrayTypeEnumeration.RectangularOffset, Array(this.Rank, Int32sl)), "TypeEnum" / BinaryTypeEnumeration, "Infos" / Switch(this.TypeEnum, { BinaryTypeEnumeration.Primitive: PrimitiveTypeEnumeration, BinaryTypeEnumeration.SystemClass: LengthPrefixedString, BinaryTypeEnumeration.Class: ClassTypeInfo, BinaryTypeEnumeration.PrimitiveArray: PrimitiveTypeEnumeration}, default=None), "CountElts" / Computed(lambda ctx: functools.reduce(operator.mul, ctx.Lengths, 1)), "Values" / Array(this.CountElts, Switch(this.TypeEnum, { BinaryTypeEnumeration.Primitive: Switch(this.Infos, PrimitiveTypeParsers), BinaryTypeEnumeration.String: LengthPrefixedString, BinaryTypeEnumeration.PrimitiveArray: LazyBound(lambda: Record), BinaryTypeEnumeration.Class: LazyBound(lambda: Record), BinaryTypeEnumeration.SystemClass: LazyBound(lambda: Record), }))) # Records SerializationHeaderRecord = Struct( "RecordTypeEnum" / RecordTypeEnum, "RootId" / Int32ul, "HeaderId" / Int32ul, "MajorVersion" / Int32ul, "MinorVersion" / Int32ul) BinaryObjectString = Struct( "RecordTypeEnum" / RecordTypeEnum,
SystemTraceMarker = Enum( Int8ul, SYSTEM_TRACE_MARKER_32=0x01, SYSTEM_TRACE_MARKER_64=0x02, COMPACT_TRACE_MARKER_32=0x03, COMPACT_TRACE_MARKER_64=0x04, ) SystemTraceHeader = Struct( "start_mark" / Computed(lambda this: this._io.tell()), "marker" / wmi_trace_marker(SystemTraceMarker), "header" / WmiTracePacket, "thread_id" / Int32ul, "process_id" / Int32ul, "system_time" / Int64ul, "kernel_time" / If(lambda this: this.marker.type.enum in [SystemTraceMarker.SYSTEM_TRACE_MARKER_32, SystemTraceMarker.SYSTEM_TRACE_MARKER_64], LazyBound(lambda: Int32ul)), "user_time" / If(lambda this: this.marker.type.enum in [SystemTraceMarker.SYSTEM_TRACE_MARKER_32, SystemTraceMarker.SYSTEM_TRACE_MARKER_64], LazyBound(lambda: Int32ul)), "sizeof" / Computed(lambda this: this._io.tell() - this.start_mark) ) SystemTraceRecord = Struct( "system_header" / SystemTraceHeader, "mof_data" / Bytes(lambda this: this.system_header.header.size - this.system_header.sizeof) ) class System: """ A System log from Windows Kernel """
HEXINT32 = 20 HEXINT64 = 21 COUNTEDSTRING = 22 COUNTEDANSISTRING = 23 STRUCT = 24 COUNTEDBINARY = 25 CCOUNT = 32 VCCOUNT = 64 CHAIN = 128 TlMetaDataField = Struct( "name" / CString("ascii"), "tag_in" / Int8ul, "tag_out" / If(lambda this: this.tag_in & TagIn.CHAIN.value, LazyBound(lambda: Int8ul)), "unknown" / If(lambda this: this.tag_out and bool(this.tag_out & 0x80), LazyBound(lambda: Int32ul))) TlMetaData = Struct( "size" / Int16ul, "tag" / Int8ul, "unknown" / If(lambda this: this.tag & 0x80, LazyBound(lambda: Int8ul)), "name" / CString("ascii"), "fields" / GreedyRange(TlMetaDataField)) def build_tracelogging(event): """ Build tracelogging based on the source event Check if event have an extended :param event: event that encompass Tracelogging data """