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