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"