/* * File: StSRecordFile.cpp * * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. * See included license file for license details. */ #include "stdafx.h" #include "StSRecordFile.h" #include "string.h" StSRecordFile::StSRecordFile(std::istream & inStream) : m_stream(inStream) { } //! Frees any data allocated as part of an S-record. StSRecordFile::~StSRecordFile() { const_iterator it; for (it = m_records.begin(); it != m_records.end(); it++) { SRecord & theRecord = (SRecord &)*it; if (theRecord.m_data) { delete [] theRecord.m_data; theRecord.m_data = NULL; } } } //! Just looks for "S[0-9]" as the first two characters of the file. bool StSRecordFile::isSRecordFile() { int savePosition = m_stream.tellg(); m_stream.seekg(0, std::ios_base::beg); char buffer[2]; m_stream.read(buffer, 2); bool isSRecord = (buffer[0] == 'S' && isdigit(buffer[1])); m_stream.seekg(savePosition, std::ios_base::beg); return isSRecord; } //! Extract records one line at a time and hand them to the parseLine() //! method. Either CR, LF, or CRLF line endings are supported. The input //! stream is read until EOF. //! The parse() method must be called after the object has been constructed //! before any of the records will become accessible. //! \exception StSRecordParseException will be thrown if any error occurs while //! parsing the input. void StSRecordFile::parse() { // back to start of stream m_stream.seekg(0, std::ios_base::beg); std::string thisLine; do { char thisChar; m_stream.get(thisChar); if (thisChar == '\r' || thisChar == '\n') { // skip the LF in a CRLF if (thisChar == '\r' && m_stream.peek() == '\n') m_stream.ignore(); // parse line if it's not empty if (!thisLine.empty()) { parseLine(thisLine); // reset line thisLine.clear(); } } else { thisLine += thisChar; } } while (!m_stream.eof()); } bool StSRecordFile::isHexDigit(char c) { return (isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } int StSRecordFile::hexDigitToInt(char digit) { if (isdigit(digit)) return digit - '0'; else if (digit >= 'a' && digit <= 'f') return 10 + digit - 'a'; else if (digit >= 'A' && digit <= 'F') return 10 + digit - 'A'; // unknow char return 0; } //! \exception StSRecordParseException is thrown if either of the nibble characters //! is not a valid hex digit. int StSRecordFile::readHexByte(std::string & inString, int inIndex) { char nibbleCharHi= inString[inIndex]; char nibbleCharLo = inString[inIndex + 1]; // must be hex digits if (!(isHexDigit(nibbleCharHi) && isHexDigit(nibbleCharLo))) { throw StSRecordParseException("invalid hex digit"); } return (hexDigitToInt(nibbleCharHi) << 4) | hexDigitToInt(nibbleCharLo); } //! \brief Parses individual S-records. //! //! Takes a single S-record line as input and appends a new SRecord struct //! to the m_records vector. //! \exception StSRecordParseException will be thrown if any error occurs while //! parsing \a inLine. void StSRecordFile::parseLine(std::string & inLine) { int checksum = 0; SRecord newRecord; memset(&newRecord, 0, sizeof(newRecord)); // must start with "S" and be at least a certain length if (inLine[0] != SRECORD_START_CHAR && inLine.length() >= SRECORD_MIN_LENGTH) { throw StSRecordParseException("invalid record length"); } // parse type field char typeChar = inLine[1]; if (!isdigit(typeChar)) { throw StSRecordParseException("invalid S-record type"); } newRecord.m_type = typeChar - '0'; // parse count field newRecord.m_count = readHexByte(inLine, 2); checksum += newRecord.m_count; // verify the record length now that we know the count if (inLine.length() != 4 + newRecord.m_count * 2) { throw StSRecordParseException("invalid record length"); } // get address length int addressLength; // len in bytes bool hasData = false; switch (newRecord.m_type) { case 0: // contains header information addressLength = 2; hasData = true; break; case 1: // data record with 2-byte address addressLength = 2; hasData = true; break; case 2: // data record with 3-byte address addressLength = 3; hasData = true; break; case 3: // data record with 4-byte address addressLength = 4; hasData = true; break; case 5: // the 2-byte address field contains a count of all prior S1, S2, and S3 records addressLength = 2; break; case 7: // entry point record with 4-byte address addressLength = 4; break; case 8: // entry point record with 3-byte address addressLength = 3; break; case 9: // entry point record with 2-byte address addressLength = 2; break; default: // unrecognized type throw StSRecordParseException("unknown S-record type"); break; } // read address int address = 0; int i; for (i=0; i < addressLength; ++i) { int addressByte = readHexByte(inLine, SRECORD_ADDRESS_START_CHAR_INDEX + i * 2); address = (address << 8) | addressByte; checksum += addressByte; } newRecord.m_address = address; // read data if (hasData) { int dataStartCharIndex = 4 + addressLength * 2; int dataLength = newRecord.m_count - addressLength - 1; // total rem - addr - cksum (in bytes) uint8_t * data = new uint8_t[dataLength]; for (i=0; i < dataLength; ++i) { int dataByte = readHexByte(inLine, dataStartCharIndex + i * 2); data[i] = dataByte; checksum += dataByte; } newRecord.m_data = data; newRecord.m_dataCount = dataLength; } // read and compare checksum byte checksum = (~checksum) & 0xff; // low byte of one's complement of sum of other bytes newRecord.m_checksum = readHexByte(inLine, (int)inLine.length() - 2); if (checksum != newRecord.m_checksum) { throw StSRecordParseException("invalid checksum"); } // now save the new S-record m_records.push_back(newRecord); }