File indexing completed on 2025-01-19 05:20:19

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2010 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "bytearrayihexstreamencoder.hpp"
0010 
0011 // lib
0012 #include <bytearrayview.hpp>
0013 // Okteta gui
0014 #include <Okteta/ByteArrayTableLayout>
0015 // Okteta core
0016 #include <Okteta/AbstractByteArrayModel>
0017 // KF
0018 #include <KConfigGroup>
0019 #include <KSharedConfig>
0020 #include <KLocalizedString>
0021 // Qt
0022 #include <QTextStream>
0023 // Std
0024 #include <algorithm>
0025 #include <iterator>
0026 
0027 const std::array<QString, Kasten::IHexStreamEncoderSettings::AddressSizeCount>
0028 Kasten::IHexStreamEncoderSettings::addressSizeConfigValueList = {
0029     QStringLiteral("32"),
0030     QStringLiteral("16"),
0031     QStringLiteral("8"),
0032 };
0033 
0034 template <>
0035 inline Kasten::IHexStreamEncoderSettings::AddressSizeId KConfigGroup::readEntry(const char *key,
0036                                                                                    const Kasten::IHexStreamEncoderSettings::AddressSizeId &defaultValue) const
0037 {
0038     const QString entry = readEntry(key, QString());
0039 
0040     auto it = std::find(Kasten::IHexStreamEncoderSettings::addressSizeConfigValueList.cbegin(),
0041                         Kasten::IHexStreamEncoderSettings::addressSizeConfigValueList.cend(),
0042                         entry);
0043     if (it == Kasten::IHexStreamEncoderSettings::addressSizeConfigValueList.cend()) {
0044         return defaultValue;
0045     }
0046 
0047     const int listIndex = std::distance(Kasten::IHexStreamEncoderSettings::addressSizeConfigValueList.cbegin(), it);
0048     return static_cast<Kasten::IHexStreamEncoderSettings::AddressSizeId>(listIndex);
0049 }
0050 
0051 template <>
0052 inline void KConfigGroup::writeEntry(const char *key,
0053                                      const Kasten::IHexStreamEncoderSettings::AddressSizeId &value,
0054                                      KConfigBase::WriteConfigFlags flags)
0055 {
0056     const int listIndex = static_cast<int>(value);
0057     writeEntry(key, Kasten::IHexStreamEncoderSettings::addressSizeConfigValueList[listIndex], flags);
0058 }
0059 
0060 namespace Kasten {
0061 
0062 const char ByteArrayIHexStreamEncoder::hexDigits[16] = {
0063     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
0064 };
0065 
0066 IHexStreamEncoderSettings::IHexStreamEncoderSettings() = default;
0067 
0068 bool IHexStreamEncoderSettings::operator==(const IHexStreamEncoderSettings& other) const
0069 {
0070     return (addressSizeId == other.addressSizeId);
0071 }
0072 
0073 void IHexStreamEncoderSettings::loadConfig(const KConfigGroup& configGroup)
0074 {
0075     addressSizeId = configGroup.readEntry(AddressSizeConfigKey, DefaultAddressSize);
0076 }
0077 
0078 void IHexStreamEncoderSettings::saveConfig(KConfigGroup& configGroup) const
0079 {
0080     configGroup.writeEntry(AddressSizeConfigKey, addressSizeId);
0081 }
0082 
0083 
0084 void ByteArrayIHexStreamEncoder::streamLine(QTextStream& textStream,
0085                                             const unsigned char* line)
0086 {
0087     // checksum is two's complement of sum of the values in the line
0088     unsigned char checksum = 0;
0089 
0090     textStream << startCode;
0091 
0092     const uint length = byteCountLineSize + addressLineSize + recordTypeLineSize + line[0];
0093     for (uint i = 0; i < length; ++i) {
0094         const unsigned char byte = line[i];
0095         textStream << hexValueOfNibble(byte >> 4)
0096                    << hexValueOfNibble(byte);
0097         checksum += byte;
0098     }
0099 
0100     checksum = (checksum ^ 0xFF) + 1;
0101     textStream << hexValueOfNibble(checksum >> 4)
0102                << hexValueOfNibble(checksum) << '\n';
0103 }
0104 
0105 void ByteArrayIHexStreamEncoder::streamExtendedSegmentAddress(QTextStream& textStream,
0106                                                               unsigned char* line,
0107                                                               quint16 upperSegmentBaseAddress)
0108 {
0109     constexpr int nullAddress = 0;
0110     constexpr int upperSegmentBaseAddressSize = 2;
0111 
0112     line[byteCountLineOffset] = upperSegmentBaseAddressSize;
0113     writeBigEndian(&line[addressLineOffset], nullAddress, addressLineSize);
0114     line[recordTypeLineOffset] = extendedSegmentAddressRecordCode;
0115     line[dataLineOffset] = upperSegmentBaseAddress >> 8;
0116     line[dataLineOffset + 1] = upperSegmentBaseAddress;
0117 
0118     streamLine(textStream, line);
0119 }
0120 
0121 void ByteArrayIHexStreamEncoder::streamExtendedLinearAddress(QTextStream& textStream,
0122                                                              unsigned char* line,
0123                                                              quint16 upperLinearBaseAddress)
0124 {
0125     constexpr int nullAddress = 0;
0126     constexpr int upperLinearBaseAddressSize = 2;
0127 
0128     line[byteCountLineOffset] = upperLinearBaseAddressSize;
0129     writeBigEndian(&line[addressLineOffset], nullAddress, addressLineSize);
0130     line[recordTypeLineOffset] = extendedLinearAddressRecordCode;
0131     line[dataLineOffset] = upperLinearBaseAddress >> 8;
0132     line[dataLineOffset + 1] = upperLinearBaseAddress;
0133 
0134     streamLine(textStream, line);
0135 }
0136 
0137 void ByteArrayIHexStreamEncoder::streamEndOfFile(QTextStream& textStream,
0138                                                  unsigned char* line,
0139                                                  quint16 startAddress)
0140 {
0141     constexpr int endOfFileByteCount = 0;
0142 
0143     line[byteCountLineOffset] = endOfFileByteCount;
0144     writeBigEndian(&line[addressLineOffset], startAddress, addressLineSize);
0145     line[recordTypeLineOffset] = endOfFileRecordCode;
0146 
0147     streamLine(textStream, line);
0148 }
0149 
0150 ByteArrayIHexStreamEncoder::ByteArrayIHexStreamEncoder()
0151     : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "Intel Hex"), QStringLiteral("text/x-ihex"))
0152 {
0153     const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0154     mSettings.loadConfig(configGroup);
0155 }
0156 
0157 ByteArrayIHexStreamEncoder::~ByteArrayIHexStreamEncoder() = default;
0158 
0159 void ByteArrayIHexStreamEncoder::setSettings(const IHexStreamEncoderSettings& settings)
0160 {
0161     if (mSettings == settings) {
0162         return;
0163     }
0164 
0165     mSettings = settings;
0166     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0167     mSettings.saveConfig(configGroup);
0168     Q_EMIT settingsChanged();
0169 }
0170 
0171 bool ByteArrayIHexStreamEncoder::encodeDataToStream(QIODevice* device,
0172                                                     const ByteArrayView* byteArrayView,
0173                                                     const Okteta::AbstractByteArrayModel* byteArrayModel,
0174                                                     const Okteta::AddressRange& range)
0175 {
0176     Q_UNUSED(byteArrayView);
0177 
0178     bool success = true;
0179 
0180     // encode
0181     QTextStream textStream(device);
0182 
0183     // prepare
0184     constexpr int maxDataPerLineCount = 255;
0185     constexpr int maxLineLength =
0186         maxDataPerLineCount + byteCountLineSize + addressLineSize + recordTypeLineSize;
0187 
0188     const Okteta::ByteArrayTableLayout layout(byteArrayView->noOfBytesPerLine(),
0189                                               byteArrayView->firstLineOffset(),
0190                                               byteArrayView->startOffset(), 0, byteArrayModel->size());
0191 
0192     const Okteta::Coord startCoord = layout.coordOfIndex(range.start());
0193     const int lastLinePosition = layout.lastLinePosition(startCoord.line());
0194     const int dataPerLineCount = qMin(byteArrayView->noOfBytesPerLine(), maxDataPerLineCount);
0195     unsigned char line[maxLineLength];
0196     unsigned char* lineData = &line[dataLineOffset];
0197     Okteta::Address lineOffset = range.start();
0198     const int firstDataEnd = lastLinePosition - startCoord.pos() + 1;
0199     int nextUpperAddressChangeDataEnd = 0x10000 - (range.start() & 0xFFFF);
0200     int d = 0;
0201     int nextDataEnd = qMin(dataPerLineCount,
0202                            qMin(firstDataEnd, nextUpperAddressChangeDataEnd));
0203 
0204     // data
0205     if (mSettings.addressSizeId == IHexStreamEncoderSettings::AddressSizeId::Bits32) {
0206         const quint16 upperLinearBaseAddress = (range.start() >> 16);
0207         streamExtendedLinearAddress(textStream, line, upperLinearBaseAddress);
0208     } else if (mSettings.addressSizeId == IHexStreamEncoderSettings::AddressSizeId::Bits16) {
0209         const quint16 upperSegmentBaseAddress = (range.start() >> 4) & 0xF000;
0210         streamExtendedSegmentAddress(textStream, line, upperSegmentBaseAddress);
0211     }
0212     Okteta::Address i = range.start();
0213     while (i <= range.end()) {
0214         const Okteta::Byte byte = byteArrayModel->byte(i);
0215         lineData[d] = byte;
0216 
0217         ++d;
0218         ++i;
0219         if (d == nextDataEnd) {
0220             line[byteCountLineOffset] = d;
0221             writeBigEndian(&line[addressLineOffset], lineOffset, addressLineSize);
0222             line[recordTypeLineOffset] = dataRecordCode;
0223 
0224             streamLine(textStream, line);
0225 
0226             lineOffset = i;
0227 
0228             if (d == nextUpperAddressChangeDataEnd) {
0229                 if (mSettings.addressSizeId == IHexStreamEncoderSettings::AddressSizeId::Bits32) {
0230                     const quint16 upperLinearBaseAddress = (i >> 16);
0231                     streamExtendedLinearAddress(textStream, line, upperLinearBaseAddress);
0232                 } else if (mSettings.addressSizeId == IHexStreamEncoderSettings::AddressSizeId::Bits16) {
0233                     const quint16 upperSegmentBaseAddress = (i >> 4) & 0xF000;
0234                     streamExtendedSegmentAddress(textStream, line, upperSegmentBaseAddress);
0235                 }
0236             }
0237             nextUpperAddressChangeDataEnd = 0x10000 - (i & 0xFFFF);
0238             nextDataEnd = qMin(dataPerLineCount,
0239                                qMin(range.end() - i + 1, nextUpperAddressChangeDataEnd));
0240             d = 0;
0241         }
0242     }
0243 
0244     // footer
0245     streamEndOfFile(textStream, line);
0246 
0247     return success;
0248 }
0249 
0250 }
0251 
0252 #include "moc_bytearrayihexstreamencoder.cpp"