comparison HexFile.cpp @ 1:0b3630a29ad8

Initial version based on previous repository. Project was ported to QT6 and in now cmake based.
author Ideenmodellierer <tiefenrauscher@web.de>
date Thu, 27 Nov 2025 18:40:28 +0100
parents
children
comparison
equal deleted inserted replaced
0:76ccd6ce50c0 1:0b3630a29ad8
1 /////////////////////////////////////////////////////////////////////////////
2 /// \file HexFile.cpp
3 /// \brief Read .hex file in "Intel HEX" format.
4 /// \author JD Gascuel.
5 ///
6 /// \copyright (c) 2012-2016, JD Gascuel. All rights reserved.
7 /// $Id$
8 //////////////////////////////////////////////////////////////////////////////
9 //
10 // BSD 2-Clause License:
11 //
12 // Redistribution and use in source and binary forms, with or without
13 // modification, are permitted provided that the following conditions
14 // are met:
15 //
16 // 1. Redistributions of source code must retain the above copyright notice,
17 // this list of conditions and the following disclaimer.
18 //
19 // 2. Redistributions in binary form must reproduce the above copyright notice,
20 // this list of conditions and the following disclaimer in the documentation
21 // and/or other materials provided with the distribution.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 // THE POSSIBILITY OF SUCH DAMAGE.
34 //
35 //////////////////////////////////////////////////////////////////////////////
36
37 #include "HexFile.h"
38
39 #include "Utils/Log.h"
40 #include "Utils/ProgressEvent.h"
41
42 #include <QCoreApplication>
43 #include <QTextStream>
44 #include <QtDebug>
45
46 //////////////////////////////////////////////////////////////////////////////
47 // Crypto++ objects needed:
48
49 typedef unsigned char byte;
50
51 #include "AES/rijndael.h"
52
53 ///////////////////////////////////////////////////////////////////////////////
54
55 HexFile::HexFile()
56 : _memSize(0),
57 #ifdef FROG_MASTER
58 _baseAddress(0),
59 #endif
60 _buffer(0)
61 {}
62
63 HexFile::~HexFile()
64 {
65 delete[] _buffer; _buffer = 0;
66 }
67
68 ///////////////////////////////////////////////////////////////////////////////
69
70 bool HexFile::allocate(size_t memSize, unsigned char fill)
71 {
72 delete[] _buffer;
73 _buffer = new unsigned char[_memSize = memSize];
74 Q_CHECK_PTR(_buffer);
75
76 memset(_buffer, fill, memSize);
77 return true;
78 }
79
80 bool HexFile::sqwiz(size_t newMemSize)
81 {
82 if( newMemSize >= _memSize ) {
83 LOG_THROW( "Squiz failed" );
84 }
85 _memSize = newMemSize;
86 return true;
87 }
88
89 ///////////////////////////////////////////////////////////////////////////////
90
91 static size_t bytes = 0;
92
93 void HexFile::readLine()
94 {
95 QByteArray line = _file.readLine();
96 if( line[0] != ':' )
97 LOG_THROW( "Bad HEX format" );
98
99 unsigned char checksum = 0;
100 bool ok = true;
101 for(int i=1; ok && i<line.length(); i+=2) {
102 if( line[i]=='\r' || line[i]=='\n' )
103 break;
104 checksum += line.mid(i,2).toInt(&ok,16);
105 }
106 if( ! ok )
107 LOG_THROW( "Bad HEX header" );
108 if( checksum != 0 )
109 LOG_THROW( "Bad HEX checksum" );
110
111 int len = line.mid(1,2).toInt(0, 16);
112 int addr = line.mid(3,2).toInt(0, 16) << 8
113 | line.mid(5,2).toInt(0, 16);
114 int type = line.mid(7,2).toInt(0, 16);
115
116 switch( type )
117 {
118 case 00: //---- Data record ----------------------------------------------
119 if( _baseAddress == 0x300000 ) // Skip configuration bits.
120 return;
121 if( _baseAddress == 0xF00000 ) // Skip internal prom reset.
122 return;
123
124 for(int i=0; i<len; i++, ++addr)
125 {
126 size_t a = _baseAddress + addr;
127 if( a >= _memSize )
128 LOG_THROW( "BAD HEX address" );
129
130 if( _buffer[a] != 0xFF )
131 LOG_THROW( "Double write" );
132
133 _buffer[a] = line.mid(9+i*2,2).toInt(&ok,16);
134 if( !ok )
135 LOG_THROW( "Bad HEX byte" );
136 bytes++;
137 }
138 break;
139
140 case 01: //---- END OF FILE record ---------------------------------------
141 _file.seek(-1); // Force to end of file.
142 break;
143
144 case 02: //---- Segment address record -----------------------------------
145 if( len != 2 || addr != 0 )
146 LOG_THROW( "Bad HEX Segment Address record" );
147 _baseAddress = line.mid(9,4).toInt(0,16) << 4;
148 break;
149
150 case 04: //---- Extended Linear Address Record ---------------------------
151 if( len != 2 || addr != 0 )
152 LOG_THROW( "Bad HEX Extended Linear Address Record" );
153 _baseAddress = line.mid( 9,2).toInt(0,16) << 20
154 | line.mid(11,2).toInt(0,16) << 16;
155 break;
156
157 default:
158 LOG_THROW("Bad HEX subtype");
159 }
160 }
161
162 ///////////////////////////////////////////////////////////////////////////////
163
164 void HexFile::load(const QString& fileName)
165 {
166 Q_ASSERT(_buffer);
167
168 _file.setFileName(fileName);
169 if( ! _file.open(QIODevice::ReadOnly) )
170 LOG_THROW("Can't open HEX file");
171
172 bytes = 0;
173 while( ! _file.atEnd() )
174 {
175 PROGRESS(_file.pos(), _file.size());
176 readLine();
177 }
178 PROGRESS_RESET();
179
180 _file.close();
181
182 LOG_TRACE( int(bytes/1024.0f) << "KB loaded ("
183 << int(bytes * 100.0f / _memSize) << "% of firmware area).");
184 }
185
186 ///////////////////////////////////////////////////////////////////////////////
187
188 const unsigned char* HexFile::data() const
189 {
190 Q_ASSERT(_buffer);
191 return _buffer;
192 }
193
194 ///////////////////////////////////////////////////////////////////////////////
195
196 unsigned int HexFile::checksum() const
197 {
198 Q_ASSERT(_buffer);
199
200 unsigned short low = 0;
201 unsigned short high = 0;
202 for(size_t i=0; i < _memSize; ++i)
203 {
204 low += _buffer[i];
205 high += low;
206 }
207 return (((unsigned int)high) << 16) + low;
208 }
209
210 ///////////////////////////////////////////////////////////////////////////////
211
212 #ifndef FROG_MASTER
213 void HexFile::saveEncrypted(const QString&, byte [16])
214 {
215 LOG_THROW( "No encryption" );
216 }
217 #else
218 void HexFile::saveEncrypted(const QString& fileName, byte secretKey[16])
219 {
220 _file.setFileName(fileName);
221 if( ! _file.open(QIODevice::WriteOnly|QIODevice::Truncate) )
222 LOG_THROW( "Can't save to encrypted file" );
223
224 QTextStream out(&_file);
225 out.setIntegerBase(16);
226 out.setPadChar('0');
227
228 //---- Generates 128 bits of random initialization vector ----------------
229 Rijndael::CFB<128>::IV iv = {0};
230 Rijndael::ECB<128> PRNG; PRNG.setupEncrypt(secretKey);
231 for(int i=0; i<sizeof iv; ++i)
232 iv[i] = PRNG.get_random() % 256;
233
234 size_t bytes = 0; // encrypted fake address
235 for(int i0 = 0; i0 < sizeof iv; i0+=0x10)
236 {
237 out << qSetFieldWidth(1) << ":"
238 << qSetFieldWidth(6) << bytes
239 << qSetFieldWidth(2);
240 for(int i=0; i<0x10; i++)
241 out << iv[i0+i];
242 out << qSetFieldWidth(1) << "\n";
243 bytes += 0x10;
244 }
245
246 //---- Create stream encryptor -------------------------------------------
247 Rijndael::CFB<128> enc(secretKey, iv);
248
249 //---- Process data ------------------------------------------------------
250 PROGRESS(0, _memSize);
251
252 byte encrypted[32];
253 for(size_t addr = 0; addr < _memSize; addr += 0x10)
254 {
255 PROGRESS(addr, _memSize);
256
257 enc.encrypt(_buffer + addr, encrypted);
258 out << qSetFieldWidth(1) << ":"
259 << qSetFieldWidth(6) << bytes
260 << qSetFieldWidth(2);
261 for(int i=0; i<16; i++)
262 out << encrypted[i];
263 out << qSetFieldWidth(1) << "\n";
264 bytes += 16;
265 }
266
267 //---- Process data ------------------------------------------------------
268 unsigned int sum = checksum(); // 33.29.BD.1D
269 out << qSetFieldWidth(1) << ":"
270 << qSetFieldWidth(6) << bytes
271 << qSetFieldWidth(2)
272 << ((sum ) & 0xff)
273 << ((sum>> 8) & 0xff)
274 << ((sum>>16) & 0xff)
275 << ((sum>>24) & 0xff)
276 << qSetFieldWidth(1) << "\n"
277 ;
278
279 qDebug().nospace() << int( bytes/1024.0f) << "KB saved into "
280 << fileName.section('/', -1);
281
282 PROGRESS_RESET();
283 }
284 #endif
285
286 ///////////////////////////////////////////////////////////////////////////////
287
288 void HexFile::loadEncrypted(const QString& fileName, unsigned char secretKey[16])
289 {
290 Q_ASSERT(_buffer);
291
292 _file.setFileName(fileName);
293 if( ! _file.open(QIODevice::ReadOnly) )
294 LOG_THROW( "Cannot open HEX file " << fileName );
295
296 //---- Read 128 bits of initialization vector ----------------------------
297 Rijndael::CFB<128>::IV iv = {0};
298
299 unsigned int bytes = 0;
300 bool ok = true;
301 for(size_t i0 = 0; i0 < sizeof iv; i0+=0x10)
302 {
303 QByteArray line = _file.readLine();
304 if( line[0] != ':' )
305 LOG_THROW( "Bad HEX line" );
306
307 unsigned int readAddr = line.mid(1,6).toInt(&ok, 16);
308 if( !ok || readAddr != bytes )
309 LOG_THROW( "Bad HEX address" );
310
311 for(int i=0; i<0x10; i++)
312 iv[i0+i] = line.mid(2*i+7,2).toInt(&ok, 16);
313 if( !ok )
314 LOG_THROW( "Bad HEX file format" );
315
316 bytes += 0x10;
317 }
318
319 //---- Create stream decryptor -------------------------------------------
320 Rijndael::CFB<128> dec(secretKey, iv);
321
322 //---- Process data ------------------------------------------------------
323 PROGRESS(0, (int)_memSize);
324
325 Rijndael::Block encrypted;
326 for(size_t addr = 0; addr < _memSize; addr += 0x10)
327 {
328 PROGRESS((int)addr, (int)_memSize);
329
330 QByteArray line = _file.readLine();
331 if( line[0] != ':' )
332 LOG_THROW( "Bad HEX line" );
333
334 unsigned int readAddr = line.mid(1,6).toInt(&ok, 16);
335 if( !ok || readAddr != bytes )
336 LOG_THROW( "Bad HEX address" );
337
338 for(int i=0; i<0x10; i++)
339 encrypted[i] = line.mid(2*i+7,2).toInt(&ok, 16);
340 if( !ok )
341 LOG_THROW( "Bad HEX file format" );
342 bytes += 0x10;
343
344 dec.decrypt(encrypted, *(Rijndael::Block*)(_buffer+addr));
345 }
346 QByteArray line = _file.readLine();
347 if( bytes != (unsigned int)line.mid(1,6).toInt(&ok, 16) || !ok )
348 LOG_THROW( "Bad HEX address" );
349
350 unsigned int sum = line.mid( 7,2).toInt(&ok, 16)
351 + (line.mid( 9,2).toInt(&ok, 16) << 8 )
352 + (line.mid(11,2).toInt(&ok, 16) << 16)
353 + (line.mid(13,2).toInt(&ok, 16) << 24);
354 if( sum != checksum() )
355 LOG_THROW( "Bad HEX checksum" );
356
357 PROGRESS_RESET();
358 }