view HexFile.cpp @ 12:ac837fe1d590

Switch implementation for reqex class and added RFCOMM as label for Bluetooth based connection by Linux
author Ideenmodellierer
date Mon, 12 Jan 2026 13:58:41 +0000
parents 21ce6187d32e
children
line wrap: on
line source

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