From 170e30d0340b4b6b6af5ba65373d9370f2c804ff Mon Sep 17 00:00:00 2001 From: Nav Date: Fri, 24 Dec 2021 23:29:57 +0000 Subject: [PATCH] Value annotations in hex viewer widget Some refactoring of byte item positioning, to better accommodate value annotations --- CMakeLists.txt | 1 + .../HexViewerWidget/AnnotationItem.cpp | 18 +++++ .../HexViewerWidget/AnnotationItem.hpp | 11 +++ .../HexViewerWidget/ByteItemGraphicsScene.cpp | 70 ++++++++++++----- .../HexViewerWidget/ByteItemGraphicsScene.hpp | 7 +- .../HexViewerWidget/ValueAnnotationItem.cpp | 75 +++++++++++++++++++ .../HexViewerWidget/ValueAnnotationItem.hpp | 35 +++++++++ 7 files changed, 197 insertions(+), 20 deletions(-) create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index caf16300..0a01bfb1 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,6 +199,7 @@ add_executable(Bloom src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp # Memory region manager window src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp index 2ae5adcb..71821211 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp @@ -19,6 +19,24 @@ height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : An this->setToolTip(this->labelText); } +AnnotationItem::AnnotationItem( + const Targets::TargetMemoryAddressRange& addressRange, + const QString& labelText, + AnnotationItemPosition position +): AnnotationItem( + addressRange.startAddress, + addressRange.endAddress - addressRange.startAddress + 1, + labelText, + position +) {} + +AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position) +: AnnotationItem( + focusedMemoryRegion.getAbsoluteAddressRange(), + focusedMemoryRegion.name, + position +) {} + void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { auto lineColor = this->getLineColor(); auto labelFontColor = this->getLabelFontColor(); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp index 530c398e..b4b6754d 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.hpp @@ -3,6 +3,8 @@ #include #include +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" + namespace Bloom::Widgets { enum class AnnotationItemPosition: std::uint8_t @@ -31,6 +33,15 @@ namespace Bloom::Widgets AnnotationItemPosition position ); + AnnotationItem( + const Targets::TargetMemoryAddressRange& + addressRange, + const QString& labelText, + AnnotationItemPosition position + ); + + AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position); + [[nodiscard]] QRectF boundingRect() const override { return QRectF(0, 0, this->width, this->height); } diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp index fd022ddc..930dc6e9 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.cpp @@ -61,6 +61,9 @@ void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buff byteWidget->setValue(buffer.at(byteWidget->byteIndex)); byteWidget->update(); } + + this->updateAnnotationValues(buffer); + this->lastValueBuffer = buffer; } void ByteItemGraphicsScene::refreshRegions() { @@ -88,23 +91,29 @@ void ByteItemGraphicsScene::refreshRegions() { } // Refresh annotation items - for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + for (auto* annotationItem : this->annotationItems) { this->removeItem(annotationItem); delete annotationItem; } - this->annotationItemsByStartAddress.clear(); + this->annotationItems.clear(); + this->valueAnnotationItems.clear(); for (const auto& focusedRegion : this->focusedMemoryRegions) { - const auto addressRange = focusedRegion.getAbsoluteAddressRange(); - auto* annotationItem = new AnnotationItem( - addressRange.startAddress, - addressRange.endAddress - addressRange.startAddress + 1, - focusedRegion.name, - AnnotationItemPosition::BOTTOM - ); + auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM); this->addItem(annotationItem); - this->annotationItemsByStartAddress.insert(std::pair(addressRange.startAddress, annotationItem)); + this->annotationItems.emplace_back(annotationItem); + + if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) { + auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion); + this->addItem(valueAnnotationItem); + this->annotationItems.emplace_back(valueAnnotationItem); + this->valueAnnotationItems.emplace_back(valueAnnotationItem); + } + } + + if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) { + this->updateAnnotationValues(this->lastValueBuffer); } this->adjustSize(true); @@ -168,7 +177,7 @@ void ByteItemGraphicsScene::setEnabled(bool enabled) { byteItem->setEnabled(this->enabled); } - for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + for (auto* annotationItem : this->annotationItems) { annotationItem->setEnabled(this->enabled); } @@ -183,7 +192,7 @@ void ByteItemGraphicsScene::invalidateChildItemCaches() { byteWidget->update(); } - for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + for (auto* annotationItem : this->annotationItems) { annotationItem->update(); } } @@ -225,6 +234,23 @@ void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) } } +void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) { + const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress; + for (auto* valueAnnotationItem : this->valueAnnotationItems) { + if (valueAnnotationItem->size > buffer.size()) { + continue; + } + + const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress; + const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress; + + valueAnnotationItem->setValue(Targets::TargetMemoryBuffer( + buffer.begin() + relativeStartAddress, + buffer.begin() + relativeEndAddress + 1 + )); + } +} + void ByteItemGraphicsScene::adjustByteItemPositions() { const auto columnCount = static_cast( std::floor( @@ -240,9 +266,9 @@ void ByteItemGraphicsScene::adjustByteItemPositions() { auto rowIndicesWithBottomAnnotations = std::set(); const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange; - for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) { + for (auto* annotationItem : this->annotationItems) { const auto firstByteRowIndex = static_cast( - std::ceil(static_cast((startAddress - memoryAddressRange.startAddress) + 1) + std::ceil(static_cast((annotationItem->startAddress - memoryAddressRange.startAddress) + 1) / static_cast(columnCount)) - 1 ); @@ -254,7 +280,13 @@ void ByteItemGraphicsScene::adjustByteItemPositions() { // We only display annotations that span a single row. if (firstByteRowIndex == lastByteRowIndex) { annotationItem->show(); - rowIndicesWithBottomAnnotations.insert(firstByteRowIndex); + + if (annotationItem->position == AnnotationItemPosition::TOP) { + rowIndicesWithTopAnnotations.insert(firstByteRowIndex); + + } else if (annotationItem->position == AnnotationItemPosition::BOTTOM) { + rowIndicesWithBottomAnnotations.insert(firstByteRowIndex); + } } else { annotationItem->hide(); @@ -317,18 +349,18 @@ void ByteItemGraphicsScene::adjustAnnotationItemPositions() { return; } - for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) { - if (!this->byteItemsByAddress.contains(startAddress)) { + for (auto* annotationItem : this->annotationItems) { + if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) { annotationItem->hide(); continue; } - const auto firstByteItemPosition = this->byteItemsByAddress.at(startAddress)->pos(); + const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos(); if (annotationItem->position == AnnotationItemPosition::TOP) { annotationItem->setPos( firstByteItemPosition.x(), - firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT + firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1 ); } else if (annotationItem->position == AnnotationItemPosition::BOTTOM) { diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp index 15187dab..4a4385c7 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItemGraphicsScene.hpp @@ -24,6 +24,7 @@ #include "ByteItem.hpp" #include "ByteAddressContainer.hpp" #include "AnnotationItem.hpp" +#include "ValueAnnotationItem.hpp" #include "HexViewerWidgetSettings.hpp" #include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp" @@ -68,8 +69,11 @@ namespace Bloom::Widgets std::vector& focusedMemoryRegions; std::vector& excludedMemoryRegions; + Targets::TargetMemoryBuffer lastValueBuffer; + std::map byteItemsByAddress; - std::map annotationItemsByStartAddress; + std::vector annotationItems; + std::vector valueAnnotationItems; std::map> byteItemsByRowIndex; std::map> byteItemsByColumnIndex; @@ -96,6 +100,7 @@ namespace Bloom::Widgets return std::max(this->parent->viewport()->width(), 400) - 2; } + void updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer); void adjustByteItemPositions(); void adjustAnnotationItemPositions(); void onTargetStateChanged(Targets::TargetState newState); diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp new file mode 100644 index 00000000..6603f9f0 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp @@ -0,0 +1,75 @@ +#include "ValueAnnotationItem.hpp" + +#include + +using namespace Bloom::Widgets; + +ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion) +: AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP), focusedMemoryRegion(focusedMemoryRegion) { + this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); +} + +void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) { + this->value = value; + this->refreshLabelText(); + this->setToolTip(this->labelText); +} + +void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + auto font = painter->font(); + font.setItalic(true); + painter->setFont(font); + + AnnotationItem::paint(painter, option, widget); +} + +void ValueAnnotationItem::refreshLabelText() { + this->update(); + + const auto& data = this->value; + + if (data.empty()) { + this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); + return; + } + + switch (this->focusedMemoryRegion.dataType) { + case MemoryRegionDataType::INTEGER: { + std::uint64_t integerValue = 0; + for (const auto& byte : data) { + integerValue = (integerValue << 8) | byte; + } + + this->labelText = QString::number(integerValue); + break; + } + case MemoryRegionDataType::ASCII_STRING: { + // Replace non-ASCII chars with '?' + auto asciiData = data; + + std::replace_if( + asciiData.begin(), + asciiData.end(), + [] (unsigned char value) { + /* + * We only care about non-control characters (with the exception of the white space character) in + * the standard ASCII range. + */ + constexpr auto asciiRangeStart = 32; + constexpr auto asciiRangeEnd = 126; + return value < asciiRangeStart || value > asciiRangeEnd; + }, + '?' + ); + + this->labelText = "'" + QString::fromLatin1( + reinterpret_cast(asciiData.data()), + static_cast(asciiData.size()) + ) + "'"; + break; + } + default: { + this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT); + } + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.hpp new file mode 100644 index 00000000..4bd3c339 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +#include "AnnotationItem.hpp" + +#include "src/Targets/TargetMemory.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" + +namespace Bloom::Widgets +{ + class ValueAnnotationItem: public AnnotationItem + { + public: + explicit ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion); + void setValue(const Targets::TargetMemoryBuffer& value); + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + + protected: + [[nodiscard]] QColor getLabelFontColor() const override { + return QColor(0x94, 0x6F, 0x30); + } + + private: + static constexpr auto DEFAULT_LABEL_TEXT = "??"; + + FocusedMemoryRegion focusedMemoryRegion; + Targets::TargetMemoryBuffer value; + + void refreshLabelText(); + }; +}