Mercurial > public > ostc_companion
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HexFile.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,358 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file HexFile.cpp +/// \brief Read .hex file in "Intel HEX" format. +/// \author JD Gascuel. +/// +/// \copyright (c) 2012-2016, JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "HexFile.h" + +#include "Utils/Log.h" +#include "Utils/ProgressEvent.h" + +#include <QCoreApplication> +#include <QTextStream> +#include <QtDebug> + +////////////////////////////////////////////////////////////////////////////// +// Crypto++ objects needed: + +typedef unsigned char byte; + +#include "AES/rijndael.h" + +/////////////////////////////////////////////////////////////////////////////// + +HexFile::HexFile() +: _memSize(0), +#ifdef FROG_MASTER + _baseAddress(0), +#endif + _buffer(0) +{} + +HexFile::~HexFile() +{ + delete[] _buffer; _buffer = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool HexFile::allocate(size_t memSize, unsigned char fill) +{ + delete[] _buffer; + _buffer = new unsigned char[_memSize = memSize]; + Q_CHECK_PTR(_buffer); + + memset(_buffer, fill, memSize); + return true; +} + +bool HexFile::sqwiz(size_t newMemSize) +{ + if( newMemSize >= _memSize ) { + LOG_THROW( "Squiz failed" ); + } + _memSize = newMemSize; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static size_t bytes = 0; + +void HexFile::readLine() +{ + QByteArray line = _file.readLine(); + if( line[0] != ':' ) + LOG_THROW( "Bad HEX format" ); + + unsigned char checksum = 0; + bool ok = true; + for(int i=1; ok && i<line.length(); i+=2) { + if( line[i]=='\r' || line[i]=='\n' ) + break; + checksum += line.mid(i,2).toInt(&ok,16); + } + if( ! ok ) + LOG_THROW( "Bad HEX header" ); + if( checksum != 0 ) + LOG_THROW( "Bad HEX checksum" ); + + int len = line.mid(1,2).toInt(0, 16); + int addr = line.mid(3,2).toInt(0, 16) << 8 + | line.mid(5,2).toInt(0, 16); + int type = line.mid(7,2).toInt(0, 16); + + switch( type ) + { + case 00: //---- Data record ---------------------------------------------- + if( _baseAddress == 0x300000 ) // Skip configuration bits. + return; + if( _baseAddress == 0xF00000 ) // Skip internal prom reset. + return; + + for(int i=0; i<len; i++, ++addr) + { + size_t a = _baseAddress + addr; + if( a >= _memSize ) + LOG_THROW( "BAD HEX address" ); + + if( _buffer[a] != 0xFF ) + LOG_THROW( "Double write" ); + + _buffer[a] = line.mid(9+i*2,2).toInt(&ok,16); + if( !ok ) + LOG_THROW( "Bad HEX byte" ); + bytes++; + } + break; + + case 01: //---- END OF FILE record --------------------------------------- + _file.seek(-1); // Force to end of file. + break; + + case 02: //---- Segment address record ----------------------------------- + if( len != 2 || addr != 0 ) + LOG_THROW( "Bad HEX Segment Address record" ); + _baseAddress = line.mid(9,4).toInt(0,16) << 4; + break; + + case 04: //---- Extended Linear Address Record --------------------------- + if( len != 2 || addr != 0 ) + LOG_THROW( "Bad HEX Extended Linear Address Record" ); + _baseAddress = line.mid( 9,2).toInt(0,16) << 20 + | line.mid(11,2).toInt(0,16) << 16; + break; + + default: + LOG_THROW("Bad HEX subtype"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void HexFile::load(const QString& fileName) +{ + Q_ASSERT(_buffer); + + _file.setFileName(fileName); + if( ! _file.open(QIODevice::ReadOnly) ) + LOG_THROW("Can't open HEX file"); + + bytes = 0; + while( ! _file.atEnd() ) + { + PROGRESS(_file.pos(), _file.size()); + readLine(); + } + PROGRESS_RESET(); + + _file.close(); + + LOG_TRACE( int(bytes/1024.0f) << "KB loaded (" + << int(bytes * 100.0f / _memSize) << "% of firmware area)."); +} + +/////////////////////////////////////////////////////////////////////////////// + +const unsigned char* HexFile::data() const +{ + Q_ASSERT(_buffer); + return _buffer; +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned int HexFile::checksum() const +{ + Q_ASSERT(_buffer); + + unsigned short low = 0; + unsigned short high = 0; + for(size_t i=0; i < _memSize; ++i) + { + low += _buffer[i]; + high += low; + } + return (((unsigned int)high) << 16) + low; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef FROG_MASTER +void HexFile::saveEncrypted(const QString&, byte [16]) +{ + LOG_THROW( "No encryption" ); +} +#else +void HexFile::saveEncrypted(const QString& fileName, byte secretKey[16]) +{ + _file.setFileName(fileName); + if( ! _file.open(QIODevice::WriteOnly|QIODevice::Truncate) ) + LOG_THROW( "Can't save to encrypted file" ); + + QTextStream out(&_file); + out.setIntegerBase(16); + out.setPadChar('0'); + + //---- Generates 128 bits of random initialization vector ---------------- + Rijndael::CFB<128>::IV iv = {0}; + Rijndael::ECB<128> PRNG; PRNG.setupEncrypt(secretKey); + for(int i=0; i<sizeof iv; ++i) + iv[i] = PRNG.get_random() % 256; + + size_t bytes = 0; // encrypted fake address + for(int i0 = 0; i0 < sizeof iv; i0+=0x10) + { + out << qSetFieldWidth(1) << ":" + << qSetFieldWidth(6) << bytes + << qSetFieldWidth(2); + for(int i=0; i<0x10; i++) + out << iv[i0+i]; + out << qSetFieldWidth(1) << "\n"; + bytes += 0x10; + } + + //---- Create stream encryptor ------------------------------------------- + Rijndael::CFB<128> enc(secretKey, iv); + + //---- Process data ------------------------------------------------------ + PROGRESS(0, _memSize); + + byte encrypted[32]; + for(size_t addr = 0; addr < _memSize; addr += 0x10) + { + PROGRESS(addr, _memSize); + + enc.encrypt(_buffer + addr, encrypted); + out << qSetFieldWidth(1) << ":" + << qSetFieldWidth(6) << bytes + << qSetFieldWidth(2); + for(int i=0; i<16; i++) + out << encrypted[i]; + out << qSetFieldWidth(1) << "\n"; + bytes += 16; + } + + //---- Process data ------------------------------------------------------ + unsigned int sum = checksum(); // 33.29.BD.1D + out << qSetFieldWidth(1) << ":" + << qSetFieldWidth(6) << bytes + << qSetFieldWidth(2) + << ((sum ) & 0xff) + << ((sum>> 8) & 0xff) + << ((sum>>16) & 0xff) + << ((sum>>24) & 0xff) + << qSetFieldWidth(1) << "\n" + ; + + qDebug().nospace() << int( bytes/1024.0f) << "KB saved into " + << fileName.section('/', -1); + + PROGRESS_RESET(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void HexFile::loadEncrypted(const QString& fileName, unsigned char secretKey[16]) +{ + Q_ASSERT(_buffer); + + _file.setFileName(fileName); + if( ! _file.open(QIODevice::ReadOnly) ) + LOG_THROW( "Cannot open HEX file " << fileName ); + + //---- Read 128 bits of initialization vector ---------------------------- + Rijndael::CFB<128>::IV iv = {0}; + + unsigned int bytes = 0; + bool ok = true; + for(size_t i0 = 0; i0 < sizeof iv; i0+=0x10) + { + QByteArray line = _file.readLine(); + if( line[0] != ':' ) + LOG_THROW( "Bad HEX line" ); + + unsigned int readAddr = line.mid(1,6).toInt(&ok, 16); + if( !ok || readAddr != bytes ) + LOG_THROW( "Bad HEX address" ); + + for(int i=0; i<0x10; i++) + iv[i0+i] = line.mid(2*i+7,2).toInt(&ok, 16); + if( !ok ) + LOG_THROW( "Bad HEX file format" ); + + bytes += 0x10; + } + + //---- Create stream decryptor ------------------------------------------- + Rijndael::CFB<128> dec(secretKey, iv); + + //---- Process data ------------------------------------------------------ + PROGRESS(0, (int)_memSize); + + Rijndael::Block encrypted; + for(size_t addr = 0; addr < _memSize; addr += 0x10) + { + PROGRESS((int)addr, (int)_memSize); + + QByteArray line = _file.readLine(); + if( line[0] != ':' ) + LOG_THROW( "Bad HEX line" ); + + unsigned int readAddr = line.mid(1,6).toInt(&ok, 16); + if( !ok || readAddr != bytes ) + LOG_THROW( "Bad HEX address" ); + + for(int i=0; i<0x10; i++) + encrypted[i] = line.mid(2*i+7,2).toInt(&ok, 16); + if( !ok ) + LOG_THROW( "Bad HEX file format" ); + bytes += 0x10; + + dec.decrypt(encrypted, *(Rijndael::Block*)(_buffer+addr)); + } + QByteArray line = _file.readLine(); + if( bytes != (unsigned int)line.mid(1,6).toInt(&ok, 16) || !ok ) + LOG_THROW( "Bad HEX address" ); + + unsigned int sum = line.mid( 7,2).toInt(&ok, 16) + + (line.mid( 9,2).toInt(&ok, 16) << 8 ) + + (line.mid(11,2).toInt(&ok, 16) << 16) + + (line.mid(13,2).toInt(&ok, 16) << 24); + if( sum != checksum() ) + LOG_THROW( "Bad HEX checksum" ); + + PROGRESS_RESET(); +}
