import 'dart:typed_data'; import 'package:abawo_bt_app/model/shifter_types.dart'; const int _startPayloadLength = 11; class DfuStartPayload { const DfuStartPayload({ required this.totalLength, required this.imageCrc32, required this.sessionId, required this.flags, }); final int totalLength; final int imageCrc32; final int sessionId; final int flags; } class DfuDataFrame { const DfuDataFrame({ required this.sequence, required this.offset, required this.payloadLength, required this.bytes, }); final int sequence; final int offset; final int payloadLength; final Uint8List bytes; } class DfuProtocol { const DfuProtocol._(); static Uint8List encodeStartPayload(DfuStartPayload payload) { final data = ByteData(_startPayloadLength); data.setUint8(0, universalShifterDfuOpcodeStart); data.setUint32(1, payload.totalLength, Endian.little); data.setUint32(5, payload.imageCrc32, Endian.little); data.setUint8(9, payload.sessionId); data.setUint8(10, payload.flags); return data.buffer.asUint8List(); } static Uint8List encodeFinishPayload() { return Uint8List.fromList([universalShifterDfuOpcodeFinish]); } static Uint8List encodeAbortPayload() { return Uint8List.fromList([universalShifterDfuOpcodeAbort]); } static List buildDataFrames( List imageBytes, { int startSequence = 0, }) { final frames = []; var seq = _asU8(startSequence); var offset = 0; while (offset < imageBytes.length) { final remaining = imageBytes.length - offset; final chunkLength = remaining < universalShifterDfuFramePayloadSizeBytes ? remaining : universalShifterDfuFramePayloadSizeBytes; final frame = Uint8List(universalShifterDfuFrameSizeBytes); frame[0] = seq; frame.setRange(1, 1 + chunkLength, imageBytes, offset); frames.add( DfuDataFrame( sequence: seq, offset: offset, payloadLength: chunkLength, bytes: frame, ), ); offset += chunkLength; seq = nextSequence(seq); } return frames; } static int nextSequence(int sequence) { return _asU8(sequence + 1); } static int rewindSequenceFromAck(int acknowledgedSequence) { return nextSequence(acknowledgedSequence); } static int sequenceDistance(int from, int to) { return _asU8(to - from); } static int parseAckPayload(List payload) { if (payload.length != 1) { throw const FormatException('ACK payload must be exactly 1 byte.'); } return _asU8(payload.first); } static const int crc32Initial = 0xFFFFFFFF; static const int _crc32PolynomialReflected = 0xEDB88320; static int crc32Update(int crc, List bytes) { var next = crc & 0xFFFFFFFF; for (final byte in bytes) { next ^= byte; for (var bit = 0; bit < 8; bit++) { if ((next & 0x1) != 0) { next = (next >> 1) ^ _crc32PolynomialReflected; } else { next >>= 1; } } } return next & 0xFFFFFFFF; } static int crc32Finalize(int crc) { return (crc ^ 0xFFFFFFFF) & 0xFFFFFFFF; } static int crc32(List bytes) { return crc32Finalize(crc32Update(crc32Initial, bytes)); } static int _asU8(int value) { return value & 0xFF; } }