class IdentifyRsp(NpiRequest, SyncRsp, FromNwp): command = Commands.RTLS_CMD_IDENTIFY struct = Struct( "capabilities" / FlagsEnum(Int16ul, Capabilities), "revNum" / Int16ul, "devId" / Enum(Int8ul, DeviceFamily), "identifier" / NiceBytes(ReverseBytes(Byte[6])), )
def __get_struct(is_wiiu: bool) -> Struct: if is_wiiu: content_hash = 'sha1' / Padded(0x20, ChecksumRaw(hashlib.sha1)) app_type = ['app_type' / Hex(Bytes(4)), '_unk2' / Bytes(0x3a)] else: content_hash = 'sha256' / ChecksumRaw(hashlib.sha256) app_type = ['_unk2' / Bytes(0x3e)] return InliningStruct( 'signature' / common.signature, '__raw_header_signed__' @ AttributeRawCopy( InlineStruct( 'issuer' / PaddedString(0x40, 'ascii'), 'format_version' / Byte, 'ca_crl_version' / Byte, 'signer_crl_version' / Byte, '_unk1' / Bytes(1), 'system_version' / Hex(Bytes(8)), 'title_id' / common.TitleID, 'title_type' / Hex(Bytes(4)), 'group_id' / Hex(Bytes(2)), * app_type, 'access_rights' / Hex(Bytes(4)), 'title_version' / Int16ub, 'content_count' / Int16ub, 'boot_index' / Int16ub, Padding(2), 'content_info_sha256' / ChecksumValue(hashlib.sha256, this._.content_info))), 'content_info' / ChecksumSourceData( Array( 64, Struct( 'content_index' / Int16ub, 'content_count' / Int16ub, 'contents_sha256' / IfThenElse( this.content_count > 0, ChecksumValue( hashlib.sha256, lambda this: this._. contents[this.content_index:this.content_index + this.content_count], True), ChecksumRaw(hashlib.sha256))))), 'contents' / Array( this.content_count, ChecksumSourceData( Struct( 'id' / Hex(Int32ub), 'index' / Int16ub, # 0x2000: always set (?) # 0x4000: appears in DLC TMDs ("optional"?) # 0x8000: "shared"? 'type' / FlagsEnum(Int16ub, encrypted=0x0001, hashed=0x0002, cfm=0x0004, unk1=0x2000, unk2=0x4000, unk3=0x8000), 'size' / Int64ub, content_hash))), 'certificates' / common.certificates, VerifyOrWriteChecksums, Terminated)
def __get_struct(is_wiiu: bool) -> Struct: if is_wiiu: swap = lambda x: x # noop # noqa: E731 encoding = 'utf-16-be' # tga file icon_struct = Struct('128' / Bytes(0x2c + 128 * 128 * 4)) icon_struct = 'icons_tga' / Padded(icon_struct.sizeof() + 4, icon_struct) region_all = 0xffffffff else: swap = ByteSwapped encoding = 'utf-16-le' # raw RGB565 icon_struct = 'icons_raw' / Struct('24' / Bytes(24 * 24 * 2), '48' / Bytes(48 * 48 * 2)) region_all = 0x7fffffff return InliningStruct( 'checksum' / ChecksumValue(hashlib.sha256, this), ChecksumSourceData( InlineStruct( 'title_id' / swap(common.TitleID), 'version' / swap(Int32ub), '_unk1' / Bytes(4), 'regions' / swap( FlagsEnum(Int32ub, JP=1 << 0, US=1 << 1, EU=1 << 2, AU=1 << 3, CN=1 << 4, KO=1 << 5, TW=1 << 6, ALL=region_all)), '_unk2' / Bytes(0x1c), 'title_info' / DictZipAdapter( languages, Array( 16, Struct('short_name' / PaddedString(0x80, encoding), 'long_name' / PaddedString(0x100, encoding), 'publisher' / PaddedString(0x80, encoding)))), icon_struct)), VerifyOrWriteChecksums, Terminated)
""" return Int32ul.build(obj.form_id) ServiceFlags = FlagsEnum( Int32ul, weapons=0x00000001, armor=0x00000002, alcohol=0x00000004, books=0x00000008, food=0x00000010, chems=0x00000020, stimpacks=0x00000040, lights=0x00000080, _unknown_0=0x00000100, _unknown_1=0x00000200, miscellaneous=0x00000400, _unknown_2=0x00000800, _unknown_3=0x00001000, potions=0x00002000, training=0x00004000, _unknown_4=0x00008000, recharge=0x00010000, repair=0x00020000, ) EquipmentTypeEnum = Enum( Int32sl, none=-1, big_guns=0,
AlignedStruct, If from etl.parsers.etw.core import Etw, build_etw, Guid as EtwGuid from etl.parsers.tracelogging import build_tracelogging, TraceLogging from etl.utils import Guid from etl.wmi import wmi_trace_marker EventHeaderType = Enum(Int8ul, EVENT_HEADER_EVENT32=0x12, EVENT_HEADER_EVENT64=0x13) EventHeaderFlag = FlagsEnum(Int16ul, EVENT_HEADER_FLAG_EXTENDED_INFO=0x0001, EVENT_HEADER_FLAG_PRIVATE_SESSION=0x0002, EVENT_HEADER_FLAG_STRING_ONLY=0x0004, EVENT_HEADER_FLAG_TRACE_MESSAGE=0x0008, EVENT_HEADER_FLAG_NO_CPUTIME=0x0010, EVENT_HEADER_FLAG_32_BIT_HEADER=0x0020, EVENT_HEADER_FLAG_64_BIT_HEADER=0x0040, EVENT_HEADER_FLAG_CLASSIC_HEADER=0x0100, EVENT_HEADER_FLAG_PROCESSOR_INDEX=0x0200) EventHeaderPropertyFlag = FlagsEnum( Int16ul, EVENT_HEADER_PROPERTY_XML=0x0001, EVENT_HEADER_PROPERTY_FORWARDED_XML=0x0002, EVENT_HEADER_PROPERTY_LEGACY_EVENTLOG=0x0004, EVENT_HEADER_PROPERTY_RELOGGABLE=0x0008) EventDescriptor = Struct("Id" / Int16ul, "Version" / Int8ul, "Channel" / Int8ul, "Level" / Int8ul, "Opcode" / Int8ul, "Task" / Int16ul,
EVENT_TRACE_GROUP_UMS=0x19, EVENT_TRACE_GROUP_ALPC=0x1a, EVENT_TRACE_GROUP_SPLITIO=0x1b, EVENT_TRACE_GROUP_THREAD_POOL=0x1c, EVENT_TRACE_GROUP_HYPERVISOR=0x1d, EVENT_TRACE_GROUP_HYPERVISORX=0x1e, ) """ Different flags use in the trace header to determine the nature of the event :see: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/etw/tracelog/wmi_buffer_header.htm """ EtwBufferFlag = FlagsEnum(Int16ul, ETW_BUFFER_FLAG_NORMAL=0x0, ETW_BUFFER_FLAG_FLUSH_MARKER=0x1, ETW_BUFFER_FLAG_EVENTS_LOST=0x2, ETW_BUFFER_FLAG_BUFFER_LOST=0x4, ETW_BUFFER_FLAG_RTBACKUP_CORRUPT=0x8, ETW_BUFFER_FLAG_RTBACKUP=0x10, ETW_BUFFER_FLAG_PROC_INDEX=0x20, ETW_BUFFER_FLAG_COMPRESSED=0x40) """ The nature of the buffer use to generate the event (ETW) :see: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/etw/tracelog/wmi_buffer_header.htm """ EtwBufferType = Enum(Int16ul, ETW_BUFFER_TYPE_GENERIC=0x0, ETW_BUFFER_TYPE_RUNDOWN=0x1, ETW_BUFFER_TYPE_CTX_SWAP=0x2, ETW_BUFFER_TYPE_REFTIME=0x3, ETW_BUFFER_TYPE_HEADER=0x4, ETW_BUFFER_TYPE_BATCHED=0x5,
unknown=0x7fff, # no track or not analyzed rekordbox=0x8000, cd=0) class StateMaskAdapter(Adapter): def _encode(self, obj, context): return obj | 0x84 # add bits which are always 1 def _decode(self, obj, context): return obj StateMask = FlagsEnum(StateMaskAdapter(Int16ub), on_air=8, sync=16, master=32, play=64) # received on udp port 50002 StatusPacket = Struct( "magic" / Const(b'Qspt1WmJOL', String(10)), "type" / StatusPacketType, "model" / Padded(20, CString(encoding="ascii")), "u1" / Const(1, Int8ub), "u2" / Default( Int8ub, 4 ), # some kind of revision? 3 for cdj2000nx, 4 for xdj1000. 1 for djm/rekordbox, 0 for link query "player_number" / Int8ub, # 0x11 for rekordbox # 34 bytes until now Embedded(
Const(b'Vgm '), 'eof_offset' / Int32ul, 'version' / Int32ul, 'SN76489_clock' / Int32ul, 'YM2413_clock' / Int32ul, 'gd3_offset' / Int32ul, 'nr_samples' / Int32ul, 'loop_offset' / Int32ul, 'loop_nr_samples' / Int32ul, 'frame_rate' / Int32ul, 'SN76489_feedback' / Int16ul, 'SN76489_shift_reg' / Byte, 'SN76489_flags' / FlagsEnum(Byte, freq0_is_0x400 = 0x01, output_negate = 0x02, stereo = 0x04, clock_divider = 0x08, ), 'YM2612_clock' / Int32ul, 'YM2151_clock' / Int32ul, 'data_offset' / Int32ul, 'sega_pcm_clock' / Int32ul, 'sega_pcm_if_reg' / Int32ul, ) class VgmParser(object): def __init__(self, buf, pos): self._buf = buf self._pos = pos def has_more(self):
return encoded class TempAdapter(Adapter): """ Adapter to encode and decode temperature. """ def _decode(self, obj, ctx): return float(obj / 2.0) def _encode(self, obj, ctx): return int(obj * 2.0) ModeFlags = "ModeFlags" / FlagsEnum(Int8ub, AUTO=0x00, MANUAL=0x01, AWAY=0x02, BOOST=0x04, DST=0x08, WINDOW=0x10, LOCKED=0x20, UNKNOWN=0x40, LOW_BATTERY=0x80) class AwayDataAdapter(Adapter): """ Adapter to encode and decode away data. """ def _decode(self, obj, ctx): (day, year, hour_min, month) = struct.unpack("BBBB", obj) year += 2000 min = 0 if hour_min & 0x01: min = 30
FLG_VOLTEX = 0x1000, FLG_ZLIB = 0x80000000, FLG_ZSTD = 0x20000000 } FLG_CONTIGUOUS_MIP; ''' ddsx_flags_enum = FlagsEnum(Int32ul, FLG_7ZIP=0x40000000, FLG_ADDRU_MASK=0xf, FLG_ADDRV_MASK=0xf0, FLG_ARRTEX=0x200000, FLG_COMPR_MASK=0xe0000000, FLG_CONTIGUOUS_MIP=0x100, FLG_CUBTEX=0x800, FLG_GAMMA_EQ_1=0x8000, FLG_GENMIP_BOX=0x2000, FLG_GENMIP_KAIZER=0x4000, FLG_GLES3_TC_FMT=0x100000, FLG_HASBORDER=0x400, FLG_HOLD_SYSMEM_COPY=0x10000, FLG_HQ_PART=0x80000, FLG_NEED_PAIRED_BASETEX=0x20000, FLG_NONPACKED=0x200, FLG_OODLE=0x60000000, FLG_REV_MIP_ORDER=0x40000, FLG_VOLTEX=0x1000, FLG_ZLIB=0x80000000, FLG_ZSTD=0x20000000) ''' struct Header { uint label; uint d3dFormat; uint flags;
RGB = Struct( "red" / Default(Byte, 0), "green" / Default(Byte, 0), "blue" / Default(Byte, 0), ) Color = Struct( Embedded(RGB), "white" / Default(Byte, 0), "brightness" / Default(Byte, 0), ) WeekDayEnum = FlagsEnum(Byte, sun=0x01, mon=0x02, tue=0x04, wed=0x08, thu=0x10, fri=0x20, sat=0x40) Alarm = Struct( "id" / Byte, # 1-6, 6 = wake up fall sleep mode, ff = end of list Embedded(HourMinuteSecond), "mode" / Enum(Byte, Single=0x01, RepeatDaily=0x02, RepeatOnDays=0x03), "days" / Switch( this.mode, { "Single": RawAsInt(Byte), # date in BCD "RepeatDaily": Byte, "RepeatOnDays": WeekDayEnum, },
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
DXGI_FORMAT = Enum(Int32ul, DXGIFormats) """The ``DXGI_FORMAT`` structure. **Reference**: `Microsoft <https://goo.gl/zast7X>`__ """ D3D10_RESOURCE_DIMENSION = Enum(Int32ul, D3D10ResourceDimension) """The ``D3D10_RESOURCE_DIMENSION`` structure. **Reference**: `Microsoft <https://goo.gl/ijDpqi>`__ """ D3D10_RESOURCE_MISC_FLAG = FlagsEnum(Int32ul, D3D10ResourceMiscFlag) """The ``D3D10_RESOURCE_MISC_FLAG`` structure. **Reference**: `Microsoft <https://goo.gl/MazAYd>`__ """ DDS_PIXELFORMAT = Struct( "dwSize" / Const(32, Int32ul), "dwFlags" / Default( FlagsEnum( Int32ul, DDPF_ALPHAPIXELS=0x00000001, DDPF_ALPHA=0x00000002, DDPF_FOURCC=0x00000004, DDPF_RBG=0x00000040,
WCEMIPSV2=0x169, _default_=Pass), "number_of_sections" / Int16ul, "time_stamp" / UTCTimeStamp(), "symbol_table_pointer" / Int32ul, "number_of_symbols" / Int32ul, "optional_header_size" / Int16ul, "characteristics" / FlagsEnum( Int16ul, RELOCS_STRIPPED=0x0001, EXECUTABLE_IMAGE=0x0002, LINE_NUMS_STRIPPED=0x0004, LOCAL_SYMS_STRIPPED=0x0008, AGGRESSIVE_WS_TRIM=0x0010, LARGE_ADDRESS_AWARE=0x0020, MACHINE_16BIT=0x0040, BYTES_REVERSED_LO=0x0080, MACHINE_32BIT=0x0100, DEBUG_STRIPPED=0x0200, REMOVABLE_RUN_FROM_SWAP=0x0400, SYSTEM=0x1000, DLL=0x2000, UNIPROCESSOR_ONLY=0x4000, BIG_ENDIAN_MACHINE=0x8000, ), # symbol table "symbol_table" / Pointer(this.symbol_table_pointer, Array(this.number_of_symbols, symbol_table))) def PEPlusField():
"EraSpecific": 128 } """Used by Enum substruct""" XvdType = {"Fixed": 0, "Dynamic": 1} """Substruct of XvdFileHeader""" XvdExtEntry = Struct("code" / Int32ul, "length" / Int32ul, "offset" / Int64ul, "data_length" / Int32ul, "reserved" / Int32ul) XVD_KEY_SIZE = 32 SANDBOX_ID_SIZE = 16 XVD_HEADER_SIZE = 0x1000 XvdFileHeader = Struct( "signature" / Bytes(0x200), # 0x00 - 0x200 "magic" / Bytes(8), # 0x200 "volume_flags" / FlagsEnum(Int32ul, **XvdVolumeFlags), # 0x208 "format_version" / Int32ul, # 0x20C "filetime_created" / FILETIMEAdapter(), # 0x210 "drive_size" / Int64ul, # 0x218 "content_id" / UUIDAdapter(), # 0x220 "user_id" / UUIDAdapter(), # 0x230 "root_hash" / Bytes(HASH_SIZE), # 0x240 "xvc_hash" / Bytes(HASH_SIZE), # 0x260 "xvd_type" / CEnum(Int32ul, **XvdType), # 0x280 "content_type" / Int32ul, # 0x284 "embedded_xvd_length" / Int32ul, # 0x288 "userdata_length" / Int32ul, # 0x28C "xvc_length" / Int32ul, # 0x290 "dynamic_header_length" / Int32ul, # 0x294 "block_size" / Int32ul, # 0x298 "ext_entry" / Array(4, XvdExtEntry), # 0x29C
def _decode(self, obj, context, path): return float((obj - 7) / 2.0) def _encode(self, obj, context, path): if -3.5 <= obj <= 3.5: return int(obj * 2.0) + 7 raise ValueError("Temperature offset must be between -3.5 and 3.5 (in " "intervals of 0.5).") ModeFlags = "ModeFlags" / FlagsEnum( Int8ub, AUTO=0x00, # always True, doesnt affect building MANUAL=0x01, AWAY=0x02, BOOST=0x04, DST=0x08, WINDOW=0x10, LOCKED=0x20, UNKNOWN=0x40, LOW_BATTERY=0x80, ) class AwayDataAdapter(Adapter): """ Adapter to encode and decode away data. """ def _decode(self, obj, ctx, path): (day, year, hour_min, month) = obj year += 2000 min = 0 if hour_min & 0x01:
Bytes(this.numSymbols * symbol.sizeof()), ), "stringTable" / Pointer( (this.filePoimter + (this.numSymbols * symbol.sizeof()) + 0), ListToBytesAdapter(GreedyRange(Byte)) ), "sizeOptHeader" / Int16ul, "flags" / FlagsEnum(Int16ul, F_RELFLG = 0x0001, F_EXEC = 0x0002, F_LNNO = 0x0004, F_LSYMS = 0x0008, F_LITTLE = 0x0100, F_BIG = 0x0200, F_SYMMERGE = 0x1000, ), "targetID" / Enum(Int16ul, TMS470 = 0x0097, TMS320C5400 = 0x0098, TMS320C6000 = 0x0099, TMS320C5500 = 0x009C, TMS320C2800 = 0x009D, MSP430 = 0x00A0, TMS320C5500plus = 0x00A1, ),
code_data_hash=Bytes(32), full_hash=Bytes(32), name=PascalString(Int8ub, "utf-8"), ) ), ) VersionInfo = Struct( target_id=Hex(Int32ub), se_version=PascalString(Int8ub, "utf-8"), _flags_len=Const(b"\x04"), flags=FlagsEnum( Int32ul, recovery_mode=1, signed_mcu=2, is_onboarded=4, trust_issuer=8, trust_custom_ca=16, hsm_initialized=32, pin_validated=128, ), mcu_version=PascalString(Int8ub, "utf-8"), mcu_hash=Optional(Bytes(32)), ) class AppInfo(object): def __init__( self, name: str, flags: int, code_data_hash: bytes,
lambda obj, ctx: ctx.ports_table.index(obj)) FilesystemQueryVolumeInformationOperationType = Enum( Int8ul, FilesystemQueryVolumeInformationOperation) FilesystemSetVolumeInformationOperationType = Enum( Int8ul, FilesystemSetVolumeInformationOperation) FilesystemQueryInformationOperationType = Enum( Int8ul, FilesystemQueryInformationOperation) FilesystemDirectoryControlOperationType = Enum( Int8ul, FilesysemDirectoryControlOperation) FilesystemSetInformationOperationType = Enum( Int8ul, FilesystemSetInformationOperation) FilesystemPnpOperationType = Enum(Int8ul, FilesystemPnpOperation) FilesystemLockUnlockOperationType = Enum(Int8ul, FilesystemLockUnlockOperation) NetworkOperationFlags = FlagsEnum(Int16ul, is_source_ipv4=1, is_dest_ipv4=2, is_tcp=4) DetailStringInfo = ByteSwapped( BitStruct("is_ascii" / Bit, "char_count" / BitsInteger(15))) def DetailString(detail_info_func): """Strings in the specific detail structure are described by a flags field and a length field """ return IfThenElse( lambda ctx: detail_info_func(ctx).is_ascii, PaddedString(lambda ctx: detail_info_func(ctx).char_count, "ascii"), PaddedString(lambda ctx: detail_info_func(ctx).char_count * 2, "UTF_16_le"))
from construct import \ Struct, Array, Byte, Bytes, CString, Int16ub, Int24ub, Int32ub, \ Const, GreedyBytes, FlagsEnum, IfThenElse, Padding, Check, this, sum_ from constructutils import InliningStruct, Inline # ref: # http://wiiubrew.org/wiki/FST # https://github.com/ihaveamac/wiiu-things/wiki/FST _entry = InliningStruct( 'type' / FlagsEnum(Byte, directory=0x01, special=0x02, deleted=0x80), 'name_offset' / Int24ub, Inline(IfThenElse( this.type.directory, Struct( 'parent_offset' / Int32ub, 'next_entry_index' / Int32ub # index of next entry not belonging to this directory anymore (i.e. index of last entry + 1) ), Struct( 'offset_raw' / Int32ub, 'size' / Int32ub ) )), 'flags' / FlagsEnum(Int16ub, offset_in_bytes=0x0004, hashed_meta=0x0040, hashed_content=0x0400), 'secondary_index' / Int16ub ) struct = Struct( Const(b'FST\0'), 'offset_factor' / Int32ub, 'num_secondary' / Int32ub,
class BSAArchive(BaseArchive): """Archive type for BSA files. BSA stands for "Bethesda ? Archive". These archives are compressed meshes, textures and other static resources that can be loaded as a single file instead of a directory of files (loose files). There are currently 4 versions of BSA: - ???: Morrowind - 103: Oblivion - 104: Fallout 3, Fallout: New Vegas, and Skyrim - 105: Skyrim: Special Edition Note: BSA archives to not read the file data on initialization. Header's, records and names are read in and files are built during :func:`~BSAArchive.iter_files`. **Credit:** - `BAE <https://github.com/jonwd7/bae>`_ """ SIZE_MASK = 0x3fffffff COMPRESSED_MASK = 0xc0000000 header_struct = Struct( "magic" / Bytes(4), "version" / Int32ul, "directory_offset" / Int32ul, "archive_flags" / FlagsEnum( Int32ul, directories_named=0x001, files_named=0x002, files_compressed=0x004, _unknown_0=0x008, _unknown_1=0x010, _unknown_2=0x020, xbox360_archive=0x040, files_prefixed=0x100, _unknown_4=0x200, _unknown_5=0x400, ), "directory_count" / Int32ul, "file_count" / Int32ul, "directory_names_length" / Int32ul, "file_names_length" / Int32ul, "file_flags" / FlagsEnum( Int32ul, nif=0x001, dds=0x002, xml=0x004, wav=0x008, mp3=0x010, txt=0x020, html=0x020, bat=0x020, scc=0x020, spt=0x040, tex=0x080, fnt=0x080, ctl=0x100, ), ) """The structure of BSA headers. Returns: :class:`~construct.core.Struct`: The structure of BSA headers """ directory_record_struct = Struct( "hash" / Int64ul, "file_count" / Int32ul, "_unknown_0" / If(lambda this: this._.header.version >= 105, Int32ul), "name_offset" / IfThenElse(lambda this: this._.header.version >= 105, Int64ul, Int32ul), ) """The structure of directory records. Returns: :class:`~construct.core.Struct`: The structure of directory records """ file_record_struct = Struct("hash" / Int64ul, "size" / Int32ul, "offset" / Int32ul) """The structure of file records. Returns: :class:`~construct.core.Struct`: The structure of file records """ directory_block_struct = Struct( "name" / If( lambda this: this._.header.archive_flags.directories_named, PascalString(VarInt, "utf8"), ), "file_records" / Array( lambda this: this._.directory_records[this._._index].file_count, file_record_struct, ), ) """The structure of directory blocks. Returns: :class:`~construct.core.Struct`: The structure of directory blocks """ archive_struct = Struct( "header" / header_struct, "directory_records" / Array(lambda this: this.header.directory_count, directory_record_struct), "directory_blocks" / Array(lambda this: this.header.directory_count, directory_block_struct), "file_names" / If( lambda this: this.header.archive_flags.files_named, Array(lambda this: this.header.file_count, CString("utf8")), ), ) """The **partial** structure of BSA archives. Return: :class:`~construct.core.Struct`: The **partial** structure of BSA archives """ @property def uncompressed_file_struct(self) -> Struct: """The uncompressed file structure for uncompressed files. Returns: :class:`~construct.core.Struct`: The uncompressed file structure for uncompressed files. """ return Struct("data" / GreedyBytes) @property def compressed_file_struct(self) -> Struct: """The compressed file structure for compressed files. Returns: :class:`~construct.core.Struct`: The compressed file structure for compressed files. """ return Struct( "original_size" / Int32ul, "data" / IfThenElse( self.container.header.version >= 105, LZ4CompressedAdapter(GreedyBytes), Compressed(GreedyBytes, "zlib"), ), ) @classmethod def can_handle(cls, filepath: str) -> bool: """Determines if a given file can be handled by the current archive. Args: filepath (str): The filepath to check if can be handled """ header = cls.header_struct.parse_file(filepath) return header.magic == b"BSA\x00" and header.version in (103, 104, 105) def iter_files(self) -> Generator[ArchiveFile, None, None]: """Iterates over the parsed data and yields instances of :class:`.ArchiveFile`. Yields: :class:`.ArchiveFile`: An file contained within the archive """ file_index = 0 file_struct = self.uncompressed_file_struct if self.container.header.archive_flags.files_compressed: file_struct = self.compressed_file_struct for directory_block in self.container.directory_blocks: # get directory path from directory block directory_path = PureWindowsPath(directory_block.name[:-1]) for file_record in directory_block.file_records: # choose the compressed file structure if compressed mask is set if file_record.size > 0 and ( self.container.header.archive_flags.files_compressed != bool(file_record.size & self.COMPRESSED_MASK)): file_struct = self.compressed_file_struct file_container = file_struct.parse( self.content[file_record.offset:( file_record.offset + (file_record.size & self.SIZE_MASK))]) yield ArchiveFile( filepath=directory_path.joinpath( self.container.file_names[file_index]), data=file_container.data, ) file_index += 1
Bip32Path = Bip32PathAdapter(PrefixedArray(Byte, Int32ub)) PrefixedString = PascalString(Asn1Length, "utf8") AppName = PrefixedString Version = PrefixedString Icon = Prefixed(Asn1Length, GreedyBytes) CURVE_SEPCK256K1 = 1 CURVE_PRIME256R1 = 2 CURVE_ED25519 = 4 Curve = FlagsEnum( Byte, secp256k1=CURVE_SEPCK256K1, prime256r1=CURVE_PRIME256R1, ed25519=CURVE_ED25519 ) DerivationPath = Prefixed( Asn1Length, Struct(curve=Curve, paths=Optional(GreedyRange(Bip32Path))) ) Dependency = Prefixed( Asn1Length, Struct(name=PrefixedString, version=Optional(PrefixedString)) ) Dependencies = Prefixed(Asn1Length, GreedyRange(Dependency)) class BolosTag(enum.IntEnum): BOLOS_TAG_APPNAME = 1
# These should really be combined into a single Sequence, but using Embed on a GreedyRange containing the second # Struct, 'report', caused some type of error I wasn't able to chase down. Also, the 'types' byte must be a raw value # (and, subsequently, Report.Types must subclass int) because of a bug between Const & FlagsEnum: # https://github.com/construct/construct/issues/73 admin_report = Struct( "admin", Const(UBInt16("length"), 13), Const(Byte("ID"), 1), Const(Byte("types"), 4), Const(PascalString("name", length_field=Byte("length"), encoding="utf8"), "admin"), Struct("version", Byte("major"), Byte("minor"), Byte("patch"))) HID_report = OptionalGreedyRange( Struct( "report", UBInt16("length"), Byte("ID"), FlagsEnum(Byte("types"), feature=4, output=2, input=1), PascalString("name", length_field=Byte("length"), encoding="utf8"), Array( lambda ctx: ctx.length - (len(ctx.name) + 1) - 4, BitStruct( "serialization", BitField("length", 6), Enum(BitField("type", 2), utf8=0, Uint=1, Int=2, Float=3))))) def hexstring_to_bytearray(string): return bytearray([int(pair.strip(','), 16) for pair in string.split(" ")]) def serialize(data): if hasattr(data[0], 'version'): return admin_report.build(data[0]) + HID_report.build(data[1:])