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