// CheckIntegrity verifies the FIT header CRC. func (h Header) CheckIntegrity() error { if err := checkProtocolVersion(h.ProtocolVersion); err != nil { return err } if string(h.DataType[:len(h.DataType)]) != fitDataTypeString { return errNotFit } if h.Size == headerSizeNoCRC { return nil } if h.CRC == 0 { return nil } crc := dyncrc16.New() bh := make([]byte, h.Size) bh[0] = h.Size bh[1] = h.ProtocolVersion le.PutUint16(bh[2:4], h.ProfileVersion) le.PutUint32(bh[4:8], h.DataSize) copy(bh[8:12], h.DataType[:]) le.PutUint16(bh[12:14], h.CRC) if crc.Sum16() != 0x0000 { return errHdrCRC } return nil }
func (d *decoder) decodeHeader() error { size, err := d.r.ReadByte() if err != nil { return err } if size != headerSizeCRC && size != headerSizeNoCRC { return errHeaderSize } d.h.Size = size if err := d.readFull(d.tmp[:size-1]); err != nil { return err } if err = checkProtocolVersion(d.tmp[0]); err != nil { return err } d.h.ProtocolVersion = d.tmp[0] d.h.ProfileVersion = le.Uint16(d.tmp[1:3]) d.h.DataSize = le.Uint32(d.tmp[3:7]) if string(d.tmp[7:11]) != fitDataTypeString { return errNotFit } copy(d.h.DataType[:], d.tmp[7:11]) if size == headerSizeNoCRC { return nil } d.h.CRC = le.Uint16(d.tmp[11:13]) if d.h.CRC == 0x0000 { return nil } checksum := dyncrc16.New() checksum.Write([]byte{size}) checksum.Write(d.tmp[:size-1]) if checksum.Sum16() != 0x0000 { return errHdrCRC } return nil }
func (d *decoder) decode(r io.Reader, headerOnly, fileIDOnly, crcOnly bool) error { d.crc = dyncrc16.New() tr := io.TeeReader(r, d.crc) // Add buffering if r does not provide ReadByte. if rr, ok := tr.(reader); ok { d.r = rr } else { d.r = bufio.NewReader(tr) } err := d.decodeHeader() if err != nil { return fmt.Errorf("error decoding header: %v", err) } d.fit = new(Fit) d.fit.Header = d.h if debug { log.Println("header decoded:", d.h) } if headerOnly { return nil } if crcOnly { _, err = io.CopyN(ioutil.Discard, d.r, int64(d.h.DataSize)) if err != nil { return fmt.Errorf("error parsing data: %v", err) } goto crc } d.fit.UnknownMessages = make(map[MesgNum]int) d.fit.UnknownFields = make(map[UnknownField]int) err = d.parseFileIdMsg() if err != nil { return fmt.Errorf("error parsing file id message: %v", err) } if fileIDOnly { return nil } err = d.initFileType() if err != nil { return err } for d.n < d.h.DataSize-2 { var ( b byte dm *defmsg msg reflect.Value ) b, err = d.readByte() if err != nil { return fmt.Errorf("error parsing record header: %v", err) } switch { case (b & compressedHeaderMask) == compressedHeaderMask: msg, err = d.parseDataMessage(b, true) if err != nil { return fmt.Errorf("compressed timestamp message: %v", err) } if msg.IsValid() { d.fit.add(msg) } case (b & headerTypeMask) == mesgDefinitionMask: dm, err = d.parseDefinitionMessage(b) if err != nil { return fmt.Errorf("parsing definition message: %v", err) } d.defmsgs[dm.localMsgType] = dm case (b & mesgHeaderMask) == mesgHeaderMask: msg, err = d.parseDataMessage(b, false) if err != nil { return fmt.Errorf("parsing data message: %v", err) } if msg.IsValid() { d.fit.add(msg) } default: return fmt.Errorf("unknown record header, got: %#x", b) } } crc: if err = binary.Read(d.r, binary.LittleEndian, &d.fit.CRC); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return fmt.Errorf("error parsing file CRC: %v", err) } if d.crc.Sum16() != 0x0000 { return IntegrityError("file checksum failed") } return nil }