File indexing completed on 2024-06-16 05:25:06
0001 /* 0002 This file is part of the Okteta Kasten Framework, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2009, 2010, 2011, 2012 Alex Richardson <alex.richardson@gmx.de> 0005 SPDX-FileCopyrightText: 2009 Friedrich W. H. Kossebau <kossebau@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0008 */ 0009 0010 #include "structurestool.hpp" 0011 #include "structuredefinitionfile.hpp" 0012 #include "structuresmanager.hpp" 0013 #include <structureslogging.hpp> 0014 #include "structuretreemodel.hpp" 0015 // Okteta Kasten core 0016 #include <Kasten/Okteta/ByteArrayDocument> 0017 // Okteta core 0018 #include <Okteta/Character> 0019 #include <Okteta/CharCodec> 0020 #include <Okteta/AbstractByteArrayModel> 0021 // KF 0022 #include <KLocalizedString> 0023 // Qt 0024 #include <QModelIndex> 0025 #include <QRegularExpression> 0026 #include <QByteArray> 0027 0028 #include "script/scripthandler.hpp" 0029 #include "datatypes/datainformation.hpp" 0030 #include "structureviewpreferences.hpp" 0031 0032 namespace Kasten { 0033 0034 StructuresTool::StructuresTool() 0035 : mByteOrder(StructureViewPreferences::byteOrder()) 0036 , mManager(new StructuresManager(this)) 0037 , mWritingData(false) 0038 , mCurrentItemDataChanged(false) 0039 , mIsStructureMarked(false) 0040 { 0041 // leave mLoadedFiles empty for now, since otherwise loading slot will not work 0042 setObjectName(QStringLiteral("StructuresTool")); 0043 mManager->reloadPaths(); 0044 setEnabledStructuresInView(); 0045 // mUtf8Codec = QTextCodec::codecForName("UTF-8"); 0046 0047 connect(this, &StructuresTool::byteOrderChanged, this, &StructuresTool::onByteOrderChanged); 0048 } 0049 0050 StructuresTool::~StructuresTool() = default; 0051 0052 void StructuresTool::onByteOrderChanged() 0053 { 0054 updateData(Okteta::ArrayChangeMetricsList()); 0055 } 0056 0057 void StructuresTool::setByteOrder(QSysInfo::Endian order) 0058 { 0059 if (order != StructureViewPreferences::byteOrder() || order != mByteOrder) { 0060 Q_EMIT byteOrderChanged(); 0061 StructureViewPreferences::setByteOrder(order); 0062 mByteOrder = order; 0063 updateData(Okteta::ArrayChangeMetricsList()); 0064 } 0065 } 0066 0067 QString StructuresTool::title() const 0068 { 0069 return i18nc("@title:window", "Structures"); 0070 } 0071 0072 void StructuresTool::setTargetModel(AbstractModel* model) 0073 { 0074 // just a copy of the code in poddecodertool.h 0075 if (mByteArrayView) { 0076 mByteArrayView->disconnect(this); 0077 if (mIsStructureMarked) { 0078 unmark(); 0079 } 0080 } 0081 if (mByteArrayModel) { 0082 mByteArrayModel->disconnect(this); 0083 } 0084 0085 mByteArrayView = model ? model->findBaseModel<ByteArrayView*>() : nullptr; 0086 ByteArrayDocument* document = mByteArrayView ? 0087 qobject_cast<ByteArrayDocument*>(mByteArrayView->baseModel()) : nullptr; 0088 mByteArrayModel = document ? document->content() : nullptr; 0089 0090 if (mByteArrayModel && mByteArrayView) { 0091 mCursorIndex = mByteArrayView->cursorPosition(); 0092 connect(mByteArrayView, &ByteArrayView::cursorPositionChanged, 0093 this, &StructuresTool::onCursorPositionChange); 0094 connect(mByteArrayModel, &Okteta::AbstractByteArrayModel::contentsChanged, 0095 this, &StructuresTool::onContentsChange); 0096 } 0097 Q_EMIT byteArrayModelChanged(mByteArrayModel); 0098 updateData(Okteta::ArrayChangeMetricsList()); 0099 } 0100 0101 void StructuresTool::onCursorPositionChange(Okteta::Address pos) 0102 { 0103 if (mCursorIndex != pos) { 0104 mCursorIndex = pos; 0105 updateData(Okteta::ArrayChangeMetricsList()); 0106 } 0107 } 0108 0109 void StructuresTool::setByteOrder(int order) 0110 { 0111 if (order == QSysInfo::LittleEndian) { 0112 setByteOrder(QSysInfo::LittleEndian); 0113 } else if (order == QSysInfo::BigEndian) { 0114 setByteOrder(QSysInfo::BigEndian); 0115 } else { 0116 qCWarning(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "invalid byte order set:" << order; 0117 } 0118 } 0119 0120 void StructuresTool::onContentsChange(const Okteta::ArrayChangeMetricsList& list) 0121 { 0122 qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "contents changed"; 0123 for (int i = 0; i < list.size(); ++i) { 0124 const Okteta::ArrayChangeMetrics& acm = list.at(i); 0125 qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "change: t=" << acm.type() << "o=" << acm.offset() << "a2=" << acm.removeLength() << "a3=" << acm.insertLength(); 0126 } 0127 0128 updateData(list); 0129 } 0130 0131 bool StructuresTool::setData(const QVariant& value, int role, DataInformation* item, uint row) 0132 { 0133 Q_UNUSED(row) 0134 if (!mByteArrayModel) { 0135 return false; 0136 } 0137 if (role != Qt::EditRole) { 0138 return false; 0139 } 0140 Q_CHECK_PTR(item); 0141 0142 TopLevelDataInformation* topLevel = item->topLevelDataInformation(); 0143 const Okteta::Address structureStart = startAddress(topLevel); 0144 // block immediate data updating 0145 mWritingData = true; 0146 0147 bool ret = false; 0148 BitCount64 position = item->positionInFile(structureStart); 0149 const quint64 remainingBits = qMax(mByteArrayModel->size() * 8 - qint64(position), qint64(0)); 0150 quint8 bitOffs = position % 8; 0151 ret = item->setData(value, mByteArrayModel, Okteta::Address(position / 8), remainingBits, bitOffs); 0152 0153 // unblock updating and catch up 0154 mWritingData = false; 0155 updateData(mArrayChangesWhileWriting); 0156 mArrayChangesWhileWriting.clear(); 0157 0158 return ret; 0159 } 0160 0161 void StructuresTool::updateData(const Okteta::ArrayChangeMetricsList& list) 0162 { 0163 if (mWritingData) { 0164 mArrayChangesWhileWriting.append(list); 0165 return; 0166 } 0167 if (!mByteArrayModel) { 0168 return; 0169 } 0170 0171 for (int i = 0; i < mData.size(); i++) { 0172 const TopLevelDataInformation::Ptr& dat = mData.at(i); 0173 dat->read(mByteArrayModel, mCursorIndex, list, false); 0174 if (mCurrentItemDataChanged) { 0175 Q_EMIT dataChanged(i, mData.at(i)->actualDataInformation()); 0176 } 0177 mCurrentItemDataChanged = false; 0178 } 0179 } 0180 0181 // model interface: 0182 QVariant StructuresTool::headerData(int column, int role) const 0183 { 0184 if (role == Qt::DisplayRole) { 0185 switch (column) 0186 { 0187 case DataInformation::ColumnName: 0188 return i18nc("name of a data structure", "Name"); 0189 case DataInformation::ColumnType: 0190 return i18nc("type of a data structure", "Type"); 0191 case DataInformation::ColumnValue: 0192 return i18nc("value of a data structure (primitive type)", "Value"); 0193 case DataInformation::ColumnSize: 0194 return i18nc("size of a data structure", "Size"); 0195 default: 0196 return {}; 0197 } 0198 } 0199 return {}; 0200 } 0201 0202 int StructuresTool::childCount() const 0203 { 0204 return mData.size(); 0205 } 0206 0207 DataInformation* StructuresTool::childAt(int idx) const 0208 { 0209 if (idx >= mData.size() || idx < 0) { 0210 return nullptr; 0211 } 0212 // don't expose the topLevelDataInformation, since this may cause crashes 0213 return mData.at(idx)->actualDataInformation(); 0214 } 0215 0216 void StructuresTool::addChildItem(TopLevelDataInformation* child) 0217 { 0218 Q_CHECK_PTR(child); 0219 child->setParent(this); 0220 if (child->isValid()) { 0221 child->setIndex(mData.size()); 0222 connect(child, &TopLevelDataInformation::dataChanged, this, &StructuresTool::onChildItemDataChanged); 0223 connect(child, &TopLevelDataInformation::childrenAboutToBeInserted, 0224 this, &StructuresTool::childrenAboutToBeInserted); 0225 connect(child, &TopLevelDataInformation::childrenInserted, 0226 this, &StructuresTool::childrenInserted); 0227 connect(child, &TopLevelDataInformation::childrenAboutToBeRemoved, 0228 this, &StructuresTool::childrenAboutToBeRemoved); 0229 connect(child, &TopLevelDataInformation::childrenRemoved, 0230 this, &StructuresTool::childrenRemoved); 0231 connect(this, &StructuresTool::byteArrayModelChanged, 0232 child, &TopLevelDataInformation::newModelActivated); 0233 mData.append(QSharedPointer<TopLevelDataInformation>(child)); 0234 // ensure that locking gets set up properly 0235 if (mByteArrayModel) { 0236 child->newModelActivated(mByteArrayModel); 0237 } 0238 } else { 0239 child->setIndex(mInvalidData.size()); 0240 mInvalidData.append(QSharedPointer<TopLevelDataInformation>(child)); 0241 } 0242 } 0243 0244 void StructuresTool::setEnabledStructuresInView() 0245 { 0246 mData.clear(); 0247 mInvalidData.clear(); 0248 Q_EMIT dataCleared(); 0249 0250 QRegularExpression regex(QStringLiteral("'(.+)':'(.+)'")); 0251 QStringList loadedStructs = StructureViewPreferences::loadedStructures(); 0252 qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "loadedStructs " << loadedStructs; 0253 for (int i = 0; i < loadedStructs.size(); ++i) { 0254 const QString& s = loadedStructs.at(i); 0255 QRegularExpressionMatch match = regex.match(s); 0256 if (match.hasMatch()) { 0257 const QString structureId = match.captured(1); 0258 const QString name = match.captured(2); 0259 // qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "structureId=" << path << " structureName=" << name; 0260 StructureDefinitionFile* def = mManager->definition(structureId); 0261 if (!def) { 0262 continue; 0263 } 0264 if (!def->isValid()) { 0265 continue; 0266 } 0267 // should be valid now 0268 if (name == QLatin1String("*")) { 0269 // add all of them 0270 const QVector<TopLevelDataInformation*> structs = def->structures(); 0271 for (auto* structure : structs) { 0272 addChildItem(structure); 0273 } 0274 } else { 0275 TopLevelDataInformation* data = def->structure(name); 0276 if (data) { 0277 addChildItem(data); 0278 } else { 0279 qCDebug(LOG_KASTEN_OKTETA_CONTROLLERS_STRUCTURES) << "Could not find structure with name" << name << "in" << structureId; 0280 } 0281 } 0282 } 0283 } 0284 0285 for (int i = 0; i < mData.size(); ++i) { 0286 Q_EMIT dataChanged(i, mData.at(i)->actualDataInformation()); 0287 } 0288 0289 updateData(Okteta::ArrayChangeMetricsList()); 0290 0291 } 0292 0293 Okteta::Address StructuresTool::startAddress(const TopLevelDataInformation* data) const 0294 { 0295 if (data->isLockedFor(mByteArrayModel)) { 0296 return data->lockPositionFor(mByteArrayModel); 0297 } 0298 0299 return mCursorIndex; 0300 } 0301 0302 Okteta::AddressRange StructuresTool::dataRange(const DataInformation* data) const 0303 { 0304 Q_CHECK_PTR(data->topLevelDataInformation()); 0305 const Okteta::Address baseAddress = startAddress(data->topLevelDataInformation()); 0306 // FIXME support range of partial bytes 0307 int length = data->size() / 8; 0308 const int maxLen = mByteArrayModel->size() - baseAddress; 0309 length = qMin(length, maxLen); 0310 const Okteta::Address startOffset = Okteta::Address(data->positionInFile(baseAddress) / 8); 0311 return Okteta::AddressRange::fromWidth(startOffset, length); 0312 } 0313 0314 void StructuresTool::selectBytesInView(const QModelIndex& idx) 0315 { 0316 if (!mByteArrayModel || !mByteArrayView || !idx.isValid()) { 0317 return; 0318 } 0319 const auto* data = idx.data(StructureTreeModel::DataInformationRole).value<DataInformation*>(); 0320 if (!data) { 0321 return; 0322 } 0323 const Okteta::AddressRange selection = dataRange(data); 0324 mByteArrayView->setSelection(selection.start(), selection.end()); 0325 mByteArrayView->setFocus(); 0326 } 0327 0328 void StructuresTool::mark(const QModelIndex& idx) 0329 { 0330 if (!mByteArrayModel || !mByteArrayView || !idx.isValid()) { 0331 return; 0332 } 0333 const auto* data = idx.data(StructureTreeModel::DataInformationRole).value<DataInformation*>(); 0334 if (!data) { 0335 return; 0336 } 0337 const Okteta::AddressRange markingRange = dataRange(data); 0338 0339 mByteArrayView->setMarking(markingRange, true); 0340 mIsStructureMarked = true; 0341 } 0342 0343 void StructuresTool::unmark(/*const QModelIndex& idx*/) 0344 { 0345 if (mByteArrayView) { 0346 mByteArrayView->setMarking(Okteta::AddressRange()); 0347 mIsStructureMarked = false; 0348 } 0349 } 0350 0351 QByteArray StructuresTool::bytes(const DataInformation* data) const 0352 { 0353 const Okteta::AddressRange range = dataRange(data); 0354 QByteArray bytes; 0355 bytes.resize(range.width()); 0356 mByteArrayModel->copyTo(reinterpret_cast<Okteta::Byte*>(bytes.data()), range.start(), range.width()); 0357 return bytes; 0358 } 0359 0360 void StructuresTool::validateAllStructures() 0361 { 0362 if (!mByteArrayModel) { 0363 return; // no point validating 0364 } 0365 const int size = mData.size(); 0366 for (int i = 0; i < size; ++i) { 0367 mData.at(i)->validate(); 0368 } 0369 } 0370 0371 bool StructuresTool::isFileLoaded() const 0372 { 0373 return (mByteArrayModel != nullptr); 0374 } 0375 0376 void StructuresTool::lockStructure(const QModelIndex& idx) 0377 { 0378 if (!mByteArrayModel || !idx.isValid()) { 0379 return; 0380 } 0381 auto* data = idx.data(StructureTreeModel::DataInformationRole).value<DataInformation*>(); 0382 if (!data) { 0383 return; 0384 } 0385 TopLevelDataInformation* top = data->topLevelDataInformation(); 0386 Q_ASSERT(top); 0387 if (top) { 0388 top->lockPositionToOffset(mCursorIndex, mByteArrayModel); 0389 } 0390 } 0391 0392 void StructuresTool::unlockStructure(const QModelIndex& idx) 0393 { 0394 if (!mByteArrayModel || !idx.isValid()) { 0395 return; 0396 } 0397 auto* data = idx.data(StructureTreeModel::DataInformationRole).value<DataInformation*>(); 0398 if (!data) { 0399 return; 0400 } 0401 TopLevelDataInformation* top = data->topLevelDataInformation(); 0402 Q_CHECK_PTR(top); 0403 0404 const bool wasMarked = mIsStructureMarked; 0405 if (wasMarked) { 0406 unmark(); 0407 } 0408 top->unlockPosition(mByteArrayModel); 0409 // now read from the current position: 0410 top->read(mByteArrayModel, mCursorIndex, Okteta::ArrayChangeMetricsList(), true); 0411 if (wasMarked) { 0412 mark(idx); // we have to change the marked range, otherwise it stays at the previous locked offset 0413 } 0414 } 0415 0416 bool StructuresTool::isStructureLocked(const QModelIndex& idx) const 0417 { 0418 if (!mByteArrayModel || !idx.isValid()) { 0419 return false; 0420 } 0421 auto* data = idx.data(StructureTreeModel::DataInformationRole).value<DataInformation*>(); 0422 if (!data) { 0423 return false; 0424 } 0425 TopLevelDataInformation* top = data->topLevelDataInformation(); 0426 Q_ASSERT(top); 0427 if (top) { 0428 return top->isLockedFor(mByteArrayModel); 0429 } 0430 return false; 0431 } 0432 0433 bool StructuresTool::canStructureBeLocked(const QModelIndex& idx) const 0434 { 0435 // we need a valid model and a valid index 0436 return mByteArrayModel && idx.isValid() && idx.data(StructureTreeModel::DataInformationRole).value<DataInformation*>(); 0437 } 0438 0439 void StructuresTool::onChildItemDataChanged() 0440 { 0441 mCurrentItemDataChanged = true; 0442 } 0443 0444 Okteta::AbstractByteArrayModel* StructuresTool::byteArrayModel() const 0445 { 0446 return mByteArrayModel; 0447 } 0448 0449 StructuresManager* StructuresTool::manager() const 0450 { 0451 return mManager.get(); 0452 } 0453 0454 QSysInfo::Endian StructuresTool::byteOrder() const 0455 { 0456 return mByteOrder; 0457 } 0458 0459 TopLevelDataInformation::List StructuresTool::allData() const 0460 { 0461 TopLevelDataInformation::List ret; 0462 ret << mData << mInvalidData; 0463 return ret; 0464 } 0465 0466 } 0467 0468 #include "moc_structurestool.cpp"