def PrefixedOffset(sizetype, type, offs=0): return C.FocusedSeq( "content", "_data" / C.Rebuild( C.Struct("size" / C.Rebuild(sizetype, C.len_(this.data) - offs), "data" / C.Bytes(this.size + offs)), lambda obj: {"data": type.build(obj.content, **{ **obj._params, **obj })}), "content" / C.RestreamData(this._data.data, type))
def LifeScanPacket(include_link_control: bool, ) -> construct.Struct: # pylint: disable=invalid-name if include_link_control: link_control_construct = _LINK_CONTROL else: link_control_construct = construct.Const(b"\x00") return construct.Struct( "data" / construct.RawCopy( construct.Struct( construct.Const(b"\x02"), # stx "length" / construct.Rebuild( construct.Byte, lambda this: len(this.message) + 6), "link_control" / link_control_construct, "message" / construct.Bytes(lambda this: this.length - 6), construct.Const(b"\x03"), # etx ), ), "checksum" / construct.Checksum(construct.Int16ul, lifescan.crc_ccitt, construct.this.data.data), )
def LifeScanPacket(include_link_control): # pylint: disable=invalid-name # type: (bool) -> construct.Struct if include_link_control: link_control_construct = _LINK_CONTROL else: link_control_construct = construct.Const(b'\x00') return construct.Struct( 'data' / construct.RawCopy( construct.Struct( construct.Const(b'\x02'), # stx 'length' / construct.Rebuild( construct.Byte, lambda this: len(this.message) + 6), 'link_control' / link_control_construct, 'message' / construct.Bytes(lambda this: this.length - 6), construct.Const(b'\x03'), # etx ), ), 'checksum' / construct.Checksum(construct.Int16ul, lifescan.crc_ccitt, construct.this.data.data), )
def LifeScanPacket(command_prefix, include_link_control): if include_link_control: link_control_construct = _LINK_CONTROL else: link_control_construct = construct.Const(b'\x00') command_prefix_construct = construct.Const(command_prefix, construct.Byte) return construct.Struct( 'data' / construct.RawCopy( construct.Struct( construct.Const(b'\x02'), # stx 'length' / construct.Rebuild( construct.Byte, lambda this: len(this.message) + 7), 'link_control' / link_control_construct, 'command_prefix' / command_prefix_construct, 'message' / construct.Bytes(lambda this: this.length - 7), construct.Const(b'\x03'), # etx ), ), 'checksum' / construct.Checksum(construct.Int16ul, lifescan.crc_ccitt, construct.this.data.data), )
def DataEntry(type): return C.FocusedSeq( "values", "_count" / C.Rebuild(Int32ul, C.len_(this.values)), "values" / C.Switch( type, { 0: Pass, 1: ClassEntry(), 2: ClassEntry(), 3: Byte, #boolean 4: Int8ul, 5: Int16ul, 6: Int32ul, 7: Int64ul, 8: Int8sl, 9: Int16sl, 10: Int32sl, 11: Int64sl, 12: Float32l, 13: Float64l, 14: CString("utf8"), 15: RGBA(), 16: Int64ul, #pointer #17: Int32ul #size, potentially not a uint but that's probably the best option of it 20: Vector3(), 21: Vector4(), 22: Quat4(), 32: CString( 'utf8' ), #specifically a CString, while 14 is probably something like std::string 46: Capsule(), 64: Vector2() }, default=C.StopFieldError)[this._count], )
"require_user_click" / c.Flag, "red_background" / c.Flag, "delay" / c.BitsInteger(4), ), _transform_vendor_trust, 2, _transform_vendor_trust, 2) VendorHeader = c.Struct( "_start_offset" / c.Tell, "magic" / c.Const(b"TRZV"), "_header_len" / c.Padding(4), "expiry" / c.Int32ul, "version" / c.Struct( "major" / c.Int8ul, "minor" / c.Int8ul, ), "vendor_sigs_required" / c.Int8ul, "vendor_sigs_n" / c.Rebuild(c.Int8ul, c.len_(c.this.pubkeys)), "vendor_trust" / VendorTrust, "reserved" / c.Padding(14), "pubkeys" / c.Bytes(32)[c.this.vendor_sigs_n], "vendor_string" / c.Aligned(4, c.PascalString(c.Int8ul, "utf-8")), "vendor_image" / Toif, "_data_end_offset" / c.Tell, c.Padding(-(c.this._data_end_offset + 65) % 512), "sigmask" / c.Byte, "signature" / c.Bytes(64), "_end_offset" / c.Tell, "header_len" / c.Pointer(c.this._start_offset + 4, c.Rebuild(c.Int32ul, c.this._end_offset - c.this._start_offset)), )
def create_rotator(inverse: bool): return lambda obj, ctx: bytes( rotate_bytes(obj, ctx.header.bytes_rotation, ctx.header.bytes_rotation, inverse)) PermalinkBinary = construct.FocusedSeq( "fields", schema_version=construct.Const(_CURRENT_SCHEMA_VERSION, construct.Byte), fields=construct.RawCopy( construct.Aligned( 3, construct.Struct( header=construct.BitStruct( has_seed_hash=construct.Rebuild( construct.Flag, construct.this._.seed_hash != None), bytes_rotation=construct.Rebuild( construct.BitsInteger(7), lambda ctx: single_byte_hash(ctx._.generator_params) >> 1, )), seed_hash=construct.If(construct.this.header.has_seed_hash, construct.Bytes(5)), randovania_version=construct.Bytes(4), # short git hash generator_params=construct.ExprAdapter( construct.Prefixed(construct.VarInt, construct.GreedyBytes), # parsing decoder=create_rotator(inverse=True), # building encoder=create_rotator(inverse=False),
@classmethod def _get_type_for_name(cls, name): """ Returns the type that's appropriate for a given descriptor field name. """ try: return cls.USB_TYPES[cls._get_prefix(name)] except KeyError: raise ValueError( "field names must be formatted per the USB standard!") def __init__(self, description="", default=None): self.description = description self.default = default def __rtruediv__(self, field_name): field_type = self._get_type_for_name(field_name) if self.default is not None: field_type = construct.Default(field_type, self.default) # Build our subconstruct. Construct makes this look super weird, # but this is actually "we have a field with <field_name> of type <field_type>". # In long form, we'll call it "description". return (field_name / field_type) * self.description # Convenience type that gets a descriptor's own length. DescriptorLength = \ construct.Rebuild(construct.Int8ul, construct.len_(construct.this)) \ * "Descriptor Length"
ClassMemberDefinition = Struct( "name" / DataPointer(Int64ul, CString("utf8"), "names"), "type" / Byte, "unkn" / Byte, "size" / Byte, "_unknData" / C.Default(Byte[69], [0 for _ in range(69)]), ) ClassDefinition = DataPointer( Int64ul, Struct("hash" / Int64ul, "members" / C.PrefixedArray(Int64ul, ClassMemberDefinition)), "definitionData") ClassDefinitionList = C.FocusedSeq( "definitions", "_count" / C.Rebuild(Int32ul, C.len_(this.definitions)), "definitions" / C.Prefixed( Int32ul, C.Aligned( 8, C.FocusedSeq( "definitions", "definitions" / ClassDefinition[this._._count], DataEntries("definitionData"), DataEntries("names"), )))) # Hierarchy handling varcount = 0
heading_radians=c.Float32l, # 132 flags=ValidFlags, # 134 _pad3=c.Padding(140 - 134), # 140 time_offset=c.Int32ul, sounded_data=c.Array(count=c.this.packet_size, subcon=c.Int8ul)) sl3_frame = c.Struct( # 0 frame_offset=c.Int32ul, # 4 _pad4=c.Padding(4), # 8 frame_size=c.Rebuild(c.Int16ul, c.this.packet_size + 168), previous_frame_size=c.Int16ul, channel_type=ChannelType, # 14 _pad14=c.Padding(2), # 16 frame_index=c.Int32ul, upper_limit_feet=c.Float32l, lower_limit_feet=c.Float32l, # 28 _pad28=c.Padding(40 - 28), # 40 creation_date_time=c.Default(c.Int32sl, -1), packet_size=c.Int16ul, # 44 _pad46=c.Padding(2),
def xor_checksum(msg: bytes) -> int: return functools.reduce(operator.xor, msg) class Direction(enum.Enum): In = 0x20 Out = 0x10 _PACKET = construct.Struct( stx=construct.Const(0x53, construct.Byte), direction=construct.Mapping(construct.Byte, {e: e.value for e in Direction}), length=construct.Rebuild(construct.Byte, lambda this: len(this.message) + 2), message=construct.Bytes(lambda this: this.length - 2), checksum=construct.Checksum(construct.Byte, xor_checksum, construct.this.message), etx=construct.Const(0xAA, construct.Byte), ) _FIRST_MESSAGE = construct.Struct( const_1=construct.Const(0x30, construct.Byte), count=construct.Int16ub, const_2=construct.Const(0xAA, construct.Byte)[19], ) _CHALLENGE_PACKET_FULL = b"\x53\x20\x04\x10\x30\x20\xAA" _RESPONSE_MESSAGE = b"\x10\x40"
def profile_base(is_v1, recipe_name_encoding="GBK"): """Build a Construct for IHCooker recipes based on version and name encoding.""" return c.Struct( c.Const(3, c.Int8un), "device_version" / c.Default(c.Enum(c.Int8ub, **DEVICE_ID), 1 if is_v1 else 2), "menu_location" / c.Default(c.ExprValidator(c.Int8ub, lambda o, _: 0 <= o < 10), 9), "recipe_name" / c.Default( c.ExprAdapter( c.StringEncoded( # PaddedString wrapper does not support GBK encoding. c.FixedSized( RECIPE_NAME_MAX_LEN_V1 if is_v1 else RECIPE_NAME_MAX_LEN_V2, c.NullStripped(c.GreedyBytes), ), recipe_name_encoding, ), lambda x, _: x.replace("\n", " "), lambda x, _: x.replace(" ", "\n"), ), "Unnamed", ), c.Padding(1) if is_v1 else c.Padding(2), "recipe_id" / c.Default(c.Int32ub, lambda _: random.randint(0, 2 ** 32 - 1)), "menu_settings" / c.Default( c.BitStruct( # byte 37 "save_recipe" / c.Default(c.Flag, 0), "confirm_start" / c.Default(c.Flag, 0), "menu_unknown3" / c.Default(c.Flag, 0), "menu_unknown4" / c.Default(c.Flag, 0), "menu_unknown5" / c.Default(c.Flag, 0), "menu_unknown6" / c.Default(c.Flag, 0), "menu_unknown7" / c.Default(c.Flag, 0), "menu_unknown8" / c.Default(c.Flag, 0), ), {}, ), "duration_hours" / c.Rebuild( c.Int8ub, lambda ctx: ctx.get("duration_minutes", 0) // 60 ), # byte 38 "duration_minutes" / c.Default( c.ExprAdapter( c.Int8ub, lambda obj, ctx: obj + ctx.duration_hours * 60, c.obj_ % 60 ), 60, ), # byte 39 "duration_max_hours" / c.Rebuild( c.Int8ub, lambda ctx: ctx.get("duration_max_minutes", 0) // 60 ), # byte 40 "duration_max_minutes" / c.Default( c.ExprAdapter( c.Int8ub, lambda obj, ctx: obj + ctx.duration_max_hours * 60, c.obj_ % 60, ), 0, ), # byte 41 "duration_min_hours" / c.Rebuild( c.Int8ub, lambda ctx: ctx.get("duration_min_minutes", 0) // 60 ), # byte 42 "duration_min_minutes" / c.Default( c.ExprAdapter( c.Int8ub, lambda obj, ctx: obj + ctx.duration_min_hours * 60, c.obj_ % 60, ), 0, ), # byte 43 c.Padding(2), # byte 44, 45 "unknown_46" / c.Default(c.Byte, 1), # byte 46, should be set to 1 c.Padding(7) if is_v1 else c.Padding(1), "stages" / c.Default( ArrayDefault( 15, c.Struct( # byte 48-168 "mode" / c.Default(c.Enum(c.Byte, StageMode), StageMode.FireMode), "hours" / c.Rebuild( c.Int8ub, lambda ctx: (ctx.get("minutes", 0) // 60) + 128 ), "minutes" / c.Default( c.ExprAdapter( c.Int8ub, decoder=lambda obj, ctx: obj + (ctx.hours - 128) * 60, encoder=c.obj_ % 60, ), DEFAULT_PHASE_MINUTES, ), "temp_threshold" / c.Default(c.Int8ub, DEFAULT_THRESHOLD_CELCIUS), "temp_target" / c.Default(c.Int8ub, DEFAULT_TEMP_TARGET_CELCIUS), "power" / c.Default(c.Int8ub, DEFAULT_FIRE_LEVEL), "fire_off" / c.Default(c.Int8ub, DEFAULT_FIRE_ON_OFF), "fire_on" / c.Default(c.Int8ub, DEFAULT_FIRE_ON_OFF), ), default=dict( mode=StageMode.FireMode, minutes=DEFAULT_PHASE_MINUTES, temp_threshold=DEFAULT_THRESHOLD_CELCIUS, temp_target=DEFAULT_TEMP_TARGET_CELCIUS, power=DEFAULT_FIRE_LEVEL, fire_off=DEFAULT_FIRE_ON_OFF, fire_on=DEFAULT_FIRE_ON_OFF, ), ), [], ), c.Padding(16) if is_v1 else c.Padding(6), # byte 169-174 "unknown175" / c.Default(c.Int8ub, 0), "unknown176" / c.Default(c.Int8ub, 0), "unknown177" / c.Default(c.Int8ub, 0), "crc" # byte 178-179 / RebuildStream( c.Bytes(2), crc16 ), # Default profiles have invalid crc, c.Checksum() raises undesired error when parsed. )
def decode_attrs(raw: str) -> dict: import shlex pairs = shlex.split(raw) attrs = dict(p.split("=", maxsplit=1) for p in pairs) return attrs def encode_attrs(attrs: dict) -> str: return " ".join(f"{k}=\"{str(v)}\"" for k, v in attrs.items()) PlumbMessageFormat = construct.Struct( "src" / header_line, "dst" / header_line, "type" / header_line, "attrs" / header_line, "ndata" / construct.Rebuild(header_line, lambda this: str(len(this.data))), "data" / construct.Byte[lambda this: int(this.ndata)]) class PlumbMsg(typing.NamedTuple): src: str dst: str type: str attrs: dict ndata: int data: bytes class ParseError(ValueError): pass
), ) MSC_TRANSPORT_SUSPEND_RESPONSE_PACKET = construct.Padded( BLOCK_SIZE, construct.Struct(HEADER_CON, RANDOM_TOKEN_CON, construct.Const(bytes([0x00]))), ) MSC_TRANSPORT_COMMAND_PACKET = construct.Padded( BLOCK_SIZE, construct.Struct( HEADER_CON, TOKEN_CON, "command_data_length" / construct.Rebuild( construct.Int16ub, construct.len_(construct.this.command_data) ), construct.Const(bytes([0x00, 0x00])), "command_data" / construct.Byte[construct.this.command_data_length], ), ) MSC_TRANSPORT_RESPONSE_PACKET = construct.Padded( BLOCK_SIZE, construct.Struct( HEADER_CON, RANDOM_TOKEN_CON, "response_data" / construct.Prefixed(construct.Int16ub, construct.GreedyBytes), ), )
ConfigurationDescriptor = DescriptorFormat( "bLength" / construct.Const(9, construct.Int8ul), "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.CONFIGURATION), "wTotalLength" / DescriptorField("Length including subordinates"), "bNumInterfaces" / DescriptorField("Interface count"), "bConfigurationValue" / DescriptorField("Configuration number", default=1), "iConfiguration" / DescriptorField("Description string", default=0), "bmAttributes" / DescriptorField("Attributes", default=0x80), "bMaxPower" / DescriptorField("Max power consumption", default=250), ) # Field that automatically reflects a string descriptor's length. StringDescriptorLength = construct.Rebuild( construct.Int8ul, construct.len_(this.bString) * 2 + 2) StringDescriptor = DescriptorFormat( "bLength" / StringDescriptorLength, "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.STRING), "bString" / construct.GreedyString("utf_16_le")) StringLanguageDescriptorLength = \ construct.Rebuild(construct.Int8ul, construct.len_(this.wLANGID) * 2 + 2) StringLanguageDescriptor = DescriptorFormat( "bLength" / StringLanguageDescriptorLength, "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.STRING), "wLANGID" / construct.GreedyRange(construct.Int16ul))
TRANSPORT_COMMAND_PACKET = construct.Struct( construct.Const(bytes([0x5C, 0x54])), "command" / construct.Int16ub, "command_data" / construct.Prefixed( construct.Int16ub, construct.GreedyRange(TRANSPORT_DATA_PARAMETER)), ) TRANSPORT_COMMAND_CONTINUE_FRAGMENTED_READ = bytes([0xC5]) TRANSPORT_COMMAND_ABORT_FRAGMENTED_READ = bytes([0xC4]) TRANSPORT_ERROR_RESPONSE_PACKET = construct.Struct("error_code" / construct.Int16ub) TRANSPORT_EXPORT_DATA_RESPONSE_PACKET = construct.Struct( construct.Const(bytes([0x90, 0x00])), "response_data_length" / construct.Rebuild( construct.Int64ub, construct.len_(construct.this.response_data)), "response_data" / construct.GreedyBytes, ) TRANSPORT_RESPONSE_PACKET = construct.Struct( "response_data_length" / construct.Rebuild( construct.Int16ub, construct.len_(construct.this.response_data)), "response_data" / construct.GreedyBytes, ) TRANSPORT_RESULT = construct.GreedyRange(TRANSPORT_DATA_PARAMETER) class Transport: def __init__(self, tse_path): self._transport = msc_transport.MscTransport(tse_path)