"""Read-only Options codec derived from the pinned SDK's descriptors/serializer. This establishes packet structure, not BLE support on a particular X4 firmware. No transport, pairing, setting changes, capture, reboot or generic send API. """ import struct MAX_PACKET = 4096 READ_OPTIONS = frozenset({15, 48, 95, 97}) def varint(value): if type(value) is not int or not 0 <= value < 2**64: raise ValueError("Invalid protobuf integer") result = bytearray() while value >= 128: result.append((value & 127) | 128) value >>= 7 result.append(value) return bytes(result) def read_varint(data, offset): value = 0 for shift in range(0, 70, 7): if offset >= len(data): raise ValueError("Truncated varint") byte = data[offset] offset += 1 if shift == 63 and byte > 1: raise ValueError("Overflowed varint") value |= (byte & 127) << shift if not byte & 128: return value, offset raise ValueError("Oversized varint") def protobuf_fields(data): if len(data) > MAX_PACKET: raise ValueError("Oversized protobuf") result, offset = [], 0 while offset < len(data): tag, offset = read_varint(data, offset) number, wire = tag >> 3, tag & 7 if not 1 <= number <= 0x1FFFFFFF: raise ValueError("Invalid field number") if wire == 0: value, offset = read_varint(data, offset) elif wire in {1, 2, 5}: if wire == 2: size, offset = read_varint(data, offset) else: size = 8 if wire == 1 else 4 if size > len(data) - offset: raise ValueError("Truncated field") value, offset = data[offset : offset + size], offset + size else: raise ValueError("Unsupported protobuf wire type") result.append((number, wire, value)) return result def get_options_packet(options, message_id): if ( not options or len(options) > 4 or len(set(options)) != len(options) or any(type(x) is not int or x not in READ_OPTIONS for x in options) ): raise ValueError("Only identity and USB/wakeup option reads are allowed") if type(message_id) is not int or not 1 <= message_id < 0x40000000: raise ValueError("Invalid message ID") payload = b"".join(b"\x08" + varint(option) for option in options) # CameraPacket header7 + CameraMessage header9. Command8=GET_OPTIONS. return ( struct.pack(" MAX_PACKET * 2: raise ValueError("Invalid notification size") self.buffer.extend(chunk) result = [] while len(self.buffer) >= 4: size = struct.unpack_from(" 64: raise ValueError("Heartbeat bound exceeded") continue _, kind, reserved, code, content, ident, tail = struct.unpack("