Mercurial > public > ostc_companion
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 } |
