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();
+}