1/* 2 * File: StSRecordFile.cpp 3 * 4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. 5 * See included license file for license details. 6 */ 7 8#include "stdafx.h" 9#include "StSRecordFile.h" 10#include "string.h" 11 12StSRecordFile::StSRecordFile(std::istream & inStream) 13: m_stream(inStream) 14{ 15} 16 17//! Frees any data allocated as part of an S-record. 18StSRecordFile::~StSRecordFile() 19{ 20 const_iterator it; 21 for (it = m_records.begin(); it != m_records.end(); it++) 22 { 23 SRecord & theRecord = (SRecord &)*it; 24 if (theRecord.m_data) 25 { 26 delete [] theRecord.m_data; 27 theRecord.m_data = NULL; 28 } 29 } 30} 31 32//! Just looks for "S[0-9]" as the first two characters of the file. 33bool StSRecordFile::isSRecordFile() 34{ 35 int savePosition = m_stream.tellg(); 36 m_stream.seekg(0, std::ios_base::beg); 37 38 char buffer[2]; 39 m_stream.read(buffer, 2); 40 bool isSRecord = (buffer[0] == 'S' && isdigit(buffer[1])); 41 42 m_stream.seekg(savePosition, std::ios_base::beg); 43 44 return isSRecord; 45} 46 47//! Extract records one line at a time and hand them to the parseLine() 48//! method. Either CR, LF, or CRLF line endings are supported. The input 49//! stream is read until EOF. 50//! The parse() method must be called after the object has been constructed 51//! before any of the records will become accessible. 52//! \exception StSRecordParseException will be thrown if any error occurs while 53//! parsing the input. 54void StSRecordFile::parse() 55{ 56 // back to start of stream 57 m_stream.seekg(0, std::ios_base::beg); 58 59 std::string thisLine; 60 61 do { 62 char thisChar; 63 m_stream.get(thisChar); 64 65 if (thisChar == '\r' || thisChar == '\n') 66 { 67 // skip the LF in a CRLF 68 if (thisChar == '\r' && m_stream.peek() == '\n') 69 m_stream.ignore(); 70 71 // parse line if it's not empty 72 if (!thisLine.empty()) 73 { 74 parseLine(thisLine); 75 76 // reset line 77 thisLine.clear(); 78 } 79 } 80 else 81 { 82 thisLine += thisChar; 83 } 84 } while (!m_stream.eof()); 85} 86 87bool StSRecordFile::isHexDigit(char c) 88{ 89 return (isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); 90} 91 92int StSRecordFile::hexDigitToInt(char digit) 93{ 94 if (isdigit(digit)) 95 return digit - '0'; 96 else if (digit >= 'a' && digit <= 'f') 97 return 10 + digit - 'a'; 98 else if (digit >= 'A' && digit <= 'F') 99 return 10 + digit - 'A'; 100 101 // unknow char 102 return 0; 103} 104 105//! \exception StSRecordParseException is thrown if either of the nibble characters 106//! is not a valid hex digit. 107int StSRecordFile::readHexByte(std::string & inString, int inIndex) 108{ 109 char nibbleCharHi= inString[inIndex]; 110 char nibbleCharLo = inString[inIndex + 1]; 111 112 // must be hex digits 113 if (!(isHexDigit(nibbleCharHi) && isHexDigit(nibbleCharLo))) 114 { 115 throw StSRecordParseException("invalid hex digit"); 116 } 117 118 return (hexDigitToInt(nibbleCharHi) << 4) | hexDigitToInt(nibbleCharLo); 119} 120 121//! \brief Parses individual S-records. 122//! 123//! Takes a single S-record line as input and appends a new SRecord struct 124//! to the m_records vector. 125//! \exception StSRecordParseException will be thrown if any error occurs while 126//! parsing \a inLine. 127void StSRecordFile::parseLine(std::string & inLine) 128{ 129 int checksum = 0; 130 SRecord newRecord; 131 memset(&newRecord, 0, sizeof(newRecord)); 132 133 // must start with "S" and be at least a certain length 134 if (inLine[0] != SRECORD_START_CHAR && inLine.length() >= SRECORD_MIN_LENGTH) 135 { 136 throw StSRecordParseException("invalid record length"); 137 } 138 139 // parse type field 140 char typeChar = inLine[1]; 141 if (!isdigit(typeChar)) 142 { 143 throw StSRecordParseException("invalid S-record type"); 144 } 145 newRecord.m_type = typeChar - '0'; 146 147 // parse count field 148 newRecord.m_count = readHexByte(inLine, 2); 149 checksum += newRecord.m_count; 150 151 // verify the record length now that we know the count 152 if (inLine.length() != 4 + newRecord.m_count * 2) 153 { 154 throw StSRecordParseException("invalid record length"); 155 } 156 157 // get address length 158 int addressLength; // len in bytes 159 bool hasData = false; 160 switch (newRecord.m_type) 161 { 162 case 0: // contains header information 163 addressLength = 2; 164 hasData = true; 165 break; 166 case 1: // data record with 2-byte address 167 addressLength = 2; 168 hasData = true; 169 break; 170 case 2: // data record with 3-byte address 171 addressLength = 3; 172 hasData = true; 173 break; 174 case 3: // data record with 4-byte address 175 addressLength = 4; 176 hasData = true; 177 break; 178 case 5: // the 2-byte address field contains a count of all prior S1, S2, and S3 records 179 addressLength = 2; 180 break; 181 case 7: // entry point record with 4-byte address 182 addressLength = 4; 183 break; 184 case 8: // entry point record with 3-byte address 185 addressLength = 3; 186 break; 187 case 9: // entry point record with 2-byte address 188 addressLength = 2; 189 break; 190 default: 191 // unrecognized type 192 //throw StSRecordParseException("unknown S-record type"); 193 break; 194 } 195 196 // read address 197 int address = 0; 198 int i; 199 for (i=0; i < addressLength; ++i) 200 { 201 int addressByte = readHexByte(inLine, SRECORD_ADDRESS_START_CHAR_INDEX + i * 2); 202 address = (address << 8) | addressByte; 203 checksum += addressByte; 204 } 205 newRecord.m_address = address; 206 207 // read data 208 if (hasData) 209 { 210 int dataStartCharIndex = 4 + addressLength * 2; 211 int dataLength = newRecord.m_count - addressLength - 1; // total rem - addr - cksum (in bytes) 212 uint8_t * data = new uint8_t[dataLength]; 213 214 for (i=0; i < dataLength; ++i) 215 { 216 int dataByte = readHexByte(inLine, dataStartCharIndex + i * 2); 217 data[i] = dataByte; 218 checksum += dataByte; 219 } 220 221 newRecord.m_data = data; 222 newRecord.m_dataCount = dataLength; 223 } 224 225 // read and compare checksum byte 226 checksum = (~checksum) & 0xff; // low byte of one's complement of sum of other bytes 227 newRecord.m_checksum = readHexByte(inLine, (int)inLine.length() - 2); 228 if (checksum != newRecord.m_checksum) 229 { 230 throw StSRecordParseException("invalid checksum"); 231 } 232 233 // now save the new S-record 234 m_records.push_back(newRecord); 235} 236