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"