1 /*
2     This file is part of the Okteta Kasten module, made within the KDE community.
3 
4     SPDX-FileCopyrightText: 2007-2010 Friedrich W. H. Kossebau <kossebau@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8 
9 #include "poddecodertool.hpp"
10 
11 // lib
12 #include "typecodecs/binary8codec.hpp"
13 #include "typecodecs/octal8codec.hpp"
14 #include "typecodecs/hexadecimal8codec.hpp"
15 #include "typecodecs/uint8codec.hpp"
16 #include "typecodecs/uint16codec.hpp"
17 #include "typecodecs/uint32codec.hpp"
18 #include "typecodecs/uint64codec.hpp"
19 #include "typecodecs/sint8codec.hpp"
20 #include "typecodecs/sint16codec.hpp"
21 #include "typecodecs/sint32codec.hpp"
22 #include "typecodecs/sint64codec.hpp"
23 #include "typecodecs/float32codec.hpp"
24 #include "typecodecs/float64codec.hpp"
25 #include "typecodecs/char8codec.hpp"
26 #include "typecodecs/utf8codec.hpp"
27 #include "abstracttypecodec.hpp"
28 #include "abstractdifferentsizedialog.hpp"
29 // Okteta Kasten gui
30 #include <Kasten/Okteta/ByteArrayView>
31 // Okteta Kasten core
32 #include <Kasten/Okteta/ByteArrayDocument>
33 // Okteta core
34 #include <Okteta/CharCodec>
35 #include <Okteta/AbstractByteArrayModel>
36 #include <Okteta/ArrayChangeMetricsList>
37 #include <Okteta/ChangesDescribable>
38 // KF
39 #include <KLocalizedString>
40 
41 namespace Kasten {
42 
43 enum PODTypes
44 {
45     BinaryId = 0,
46     OctalId,
47     HexadecimalId,
48     Signed8BitId,
49     Unsigned8BitId,
50     Signed16BitId,
51     Unsigned16BitId,
52     Signed32BitId,
53     Unsigned32BitId,
54     Signed64BitId,
55     Unsigned64BitId,
56     Float32BitId,
57     Float64BitId,
58     Char8BitId,
59     UTF8Id,
60 //     UTF16Id,
61     PODTypeCount
62 };
63 
PODDecoderTool()64 PODDecoderTool::PODDecoderTool()
65     : mReadOnly(true)
66     , mIsPodMarked(false)
67     , mCharCodec(Okteta::CharCodec::createCodec(Okteta::LocalEncoding))
68     , mUnsignedAsHex(true)
69 {
70     setObjectName(QStringLiteral("PODDecoder"));
71 
72     setupDecoder();
73 }
74 
~PODDecoderTool()75 PODDecoderTool::~PODDecoderTool()
76 {
77     delete mCharCodec;
78     qDeleteAll(mTypeCodecs);
79 }
80 
title() const81 QString PODDecoderTool::title() const { return i18nc("@title:window", "Decoding Table"); }
isReadOnly() const82 bool PODDecoderTool::isReadOnly() const { return mReadOnly; }
isApplyable() const83 bool PODDecoderTool::isApplyable() const { return (mByteArrayModel != nullptr); }
84 
setTargetModel(AbstractModel * model)85 void PODDecoderTool::setTargetModel(AbstractModel* model)
86 {
87     const bool oldIsApplyable = isApplyable();
88 
89     if (mByteArrayView) {
90         mByteArrayView->disconnect(this);
91         if (mIsPodMarked) {
92             unmarkPOD();
93         }
94     }
95     if (mByteArrayModel) {
96         mByteArrayModel->disconnect(this);
97     }
98 
99     mByteArrayView = model ? model->findBaseModel<ByteArrayView*>() : nullptr;
100     ByteArrayDocument* document =
101         mByteArrayView ? qobject_cast<ByteArrayDocument*>(mByteArrayView->baseModel()) : nullptr;
102     mByteArrayModel = document ? document->content() : nullptr;
103 
104     if (mByteArrayModel && mByteArrayView) {
105         mCursorIndex = mByteArrayView->cursorPosition();
106         connect(mByteArrayView, &ByteArrayView::cursorPositionChanged,
107                 this, &PODDecoderTool::onCursorPositionChange);
108         connect(mByteArrayModel, &Okteta::AbstractByteArrayModel::contentsChanged,
109                 this, &PODDecoderTool::onContentsChange);
110         connect(mByteArrayView,  &ByteArrayView::charCodecChanged,
111                 this, &PODDecoderTool::onCharCodecChange);
112         connect(mByteArrayView, &ByteArrayView::readOnlyChanged,
113                 this, &PODDecoderTool::onReadOnlyChanged);
114         onCharCodecChange(mByteArrayView->charCodingName());
115     }
116 
117     updateData();
118     onReadOnlyChanged();
119     const bool newIsApplyable = isApplyable();
120     if (oldIsApplyable != newIsApplyable) {
121         emit isApplyableChanged(newIsApplyable);
122     }
123 }
124 
setupDecoder()125 void PODDecoderTool::setupDecoder()
126 {
127     mTypeCodecs.resize(PODTypeCount);
128     mTypeCodecs[BinaryId] =        new Okteta::Binary8Codec();
129     mTypeCodecs[OctalId] =         new Okteta::Octal8Codec();
130     mTypeCodecs[HexadecimalId] =   new Okteta::Hexadecimal8Codec();
131     mTypeCodecs[Signed8BitId] =    new Okteta::SInt8Codec();
132     mTypeCodecs[Unsigned8BitId] =  new Okteta::UInt8Codec();
133     mTypeCodecs[Signed16BitId] =   new Okteta::SInt16Codec();
134     mTypeCodecs[Unsigned16BitId] = new Okteta::UInt16Codec();
135     mTypeCodecs[Signed32BitId] =   new Okteta::SInt32Codec();
136     mTypeCodecs[Unsigned32BitId] = new Okteta::UInt32Codec();
137     mTypeCodecs[Signed64BitId] =   new Okteta::SInt64Codec();
138     mTypeCodecs[Unsigned64BitId] = new Okteta::UInt64Codec();
139     mTypeCodecs[Float32BitId] =    new Okteta::Float32Codec();
140     mTypeCodecs[Float64BitId] =    new Okteta::Float64Codec();
141     mTypeCodecs[Char8BitId] =      new Okteta::Char8Codec(mCharCodec);
142     mTypeCodecs[UTF8Id] =          new Okteta::Utf8Codec();
143 
144 #if 0
145     mDecoderNameList[UTF16Id] =
146         i18nc("@label:textbox", "UTF-16:");
147 #endif
148 
149     mDecodedValueList.resize(PODTypeCount);
150     mDecodedValueByteCountList.resize(PODTypeCount);
151 }
152 
setDifferentSizeDialog(AbstractDifferentSizeDialog * differentSizeDialog)153 void PODDecoderTool::setDifferentSizeDialog(AbstractDifferentSizeDialog* differentSizeDialog)
154 {
155     mDifferentSizeDialog = differentSizeDialog;
156 }
157 
setUnsignedAsHex(bool unsignedAsHex)158 void PODDecoderTool::setUnsignedAsHex(bool unsignedAsHex)
159 {
160     if (mUnsignedAsHex == unsignedAsHex) {
161         return;
162     }
163 
164     mUnsignedAsHex = unsignedAsHex;
165 
166     updateData();
167 }
168 
setByteOrder(int byteOrder)169 void PODDecoderTool::setByteOrder(int byteOrder)
170 {
171     // TODO: test on no change is done in PODData, not this level
172     mPODData.setByteOrder((QSysInfo::Endian)byteOrder);
173     updateData();
174 }
175 
onCharCodecChange(const QString & codecName)176 void PODDecoderTool::onCharCodecChange(const QString& codecName)
177 {
178     if (codecName == mCharCodec->name()) {
179         return;
180     }
181 
182     delete mCharCodec;
183     mCharCodec = Okteta::CharCodec::createCodec(codecName);
184     static_cast<Okteta::Char8Codec*>(mTypeCodecs[Char8BitId])->setCharCodec(mCharCodec);
185     updateData();
186 }
187 
onCursorPositionChange(Okteta::Address pos)188 void PODDecoderTool::onCursorPositionChange(Okteta::Address pos)
189 {
190     mCursorIndex = pos;
191     updateData();
192 }
193 
onContentsChange()194 void PODDecoderTool::onContentsChange()
195 {
196     // TODO: only update if affected
197     updateData();
198 }
199 
podCount() const200 int PODDecoderTool::podCount() const { return mTypeCodecs.count(); }
201 
nameOfPOD(int podId) const202 QString PODDecoderTool::nameOfPOD(int podId) const
203 {
204     return mTypeCodecs[podId]->name();
205 }
206 
value(int podId) const207 QVariant PODDecoderTool::value(int podId) const
208 {
209     // TODO: add caching here
210     return mDecodedValueList[podId];
211 }
212 
setData(const QVariant & data,int podId)213 void PODDecoderTool::setData(const QVariant& data, int podId)
214 {
215     Okteta::AbstractTypeCodec* typeCodec = mTypeCodecs[podId];
216 
217     // QVariant::operator=() only compares values' addresses for custom types,
218     // so the comparison for values needs to be done by someone with knowledge about the type.
219     const bool isUnchangedValue = typeCodec->areEqual(data, mDecodedValueList[podId]);
220 
221     if (isUnchangedValue) {
222         return;
223     }
224 
225     QByteArray bytes = typeCodec->valueToBytes(data);
226 
227     const int bytesSize = bytes.size();
228     if (bytesSize == 0) {
229         return;
230     }
231 
232     // need to swap the bytes
233     if (mPODData.byteOrder() != QSysInfo::ByteOrder) {
234         const int firstHalfBytesCount = bytesSize / 2;
235         int j = bytesSize - 1;
236         for (int i = 0; i < firstHalfBytesCount; ++i, --j) {
237             const char helper = bytes[i];
238             bytes[i] = bytes[j];
239             bytes[j] = helper;
240         }
241     }
242 
243     const int oldValueSize = mDecodedValueByteCountList[podId];
244     int removedBytesSize = bytesSize;
245     if (bytesSize != oldValueSize) {
246 //         const int sizeLeft = mByteArrayModel->size() - mCursorIndex;
247         const Answer answer = Cancel; // TODO: non-persistent editor closes on new dialog -> crash after dialog
248 //             mDifferentSizeDialog ? mDifferentSizeDialog->query( bytesSize, oldValueSize, sizeLeft ) : Cancel;
249         if (answer == Cancel) {
250             return;
251         }
252 
253         if (answer == AdaptSize) {
254             removedBytesSize = oldValueSize;
255         }
256     }
257 
258     Okteta::ChangesDescribable* changesDescribable =
259         qobject_cast<Okteta::ChangesDescribable*>(mByteArrayModel);
260 
261     if (changesDescribable) {
262         changesDescribable->openGroupedChange(i18nc("Edited as %datatype", "Edited as %1", typeCodec->name()));
263     }
264     mByteArrayModel->replace(Okteta::AddressRange::fromWidth(mCursorIndex, removedBytesSize), bytes);
265     if (changesDescribable) {
266         changesDescribable->closeGroupedChange();
267     }
268 }
269 
updateData()270 void PODDecoderTool::updateData()
271 {
272     int dataSize;
273     if (mByteArrayModel) {
274         dataSize = mByteArrayModel->size() - mCursorIndex;
275         if (dataSize > Okteta::PODData::Size) {
276             dataSize = Okteta::PODData::Size;
277         } else if (dataSize < 0) {
278             dataSize = 0;
279         }
280     } else {
281         dataSize = 0;
282     }
283 
284     const bool hasDataSet = (dataSize > 0);
285     if (hasDataSet) {
286         mByteArrayModel->copyTo(mPODData.rawData(), mCursorIndex, Okteta::PODData::Size);
287     }
288 
289     const bool hasChanged = mPODData.updateRawData(dataSize);
290 
291     if (!hasChanged) {
292         return;
293     }
294 
295     // TODO: only calculate on demand + cache
296     for (int podId = 0; podId < PODTypeCount; ++podId) {
297         int byteCount = 0;
298         mDecodedValueList[podId] = mTypeCodecs[podId]->value(mPODData, &byteCount);
299         mDecodedValueByteCountList[podId] = byteCount;
300     }
301 
302     // TODO: only emit for those strings that changed
303     emit dataChanged();
304 }
305 
markPOD(int podId)306 void PODDecoderTool::markPOD(int podId)
307 {
308     const int length = mDecodedValueByteCountList[podId];
309     const Okteta::AddressRange markingRange = Okteta::AddressRange::fromWidth(mCursorIndex, length);
310     mByteArrayView->setMarking(markingRange, true);
311     mIsPodMarked = true;
312 }
313 
unmarkPOD()314 void PODDecoderTool::unmarkPOD()
315 {
316 // TODO: marked region is property of document, not view?
317     mByteArrayView->setMarking(Okteta::AddressRange());
318     mIsPodMarked = false;
319 }
320 
onReadOnlyChanged()321 void PODDecoderTool::onReadOnlyChanged()
322 {
323     const bool newReadOnly = ((!mByteArrayModel) || (!mByteArrayView)
324                               || mByteArrayView->isReadOnly());
325     if (newReadOnly != mReadOnly) {
326         mReadOnly = newReadOnly;
327         emit readOnlyChanged(newReadOnly);
328     }
329 }
330 
331 }
332