xref: /netbsd-src/external/bsd/elftosb/dist/common/StSRecordFile.cpp (revision 993229b6fea628ff8b1fa09146c80b0cfb2768eb)
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 
StSRecordFile(std::istream & inStream)12 StSRecordFile::StSRecordFile(std::istream & inStream)
13 :	m_stream(inStream)
14 {
15 }
16 
17 //! Frees any data allocated as part of an S-record.
~StSRecordFile()18 StSRecordFile::~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.
isSRecordFile()33 bool 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.
parse()54 void 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 
isHexDigit(char c)87 bool StSRecordFile::isHexDigit(char c)
88 {
89 	return (isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
90 }
91 
hexDigitToInt(char digit)92 int 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.
readHexByte(std::string & inString,int inIndex)107 int 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.
parseLine(std::string & inLine)127 void 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