Value annotations in hex viewer widget

Some refactoring of byte item positioning, to better accommodate value annotations
This commit is contained in:
Nav
2021-12-24 23:29:57 +00:00
parent d59c47a5de
commit 170e30d034
7 changed files with 197 additions and 20 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -3,6 +3,8 @@
#include <QGraphicsItem>
#include <cstdint>
#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);
}

View File

@@ -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::size_t>(
std::floor(
@@ -240,9 +266,9 @@ void ByteItemGraphicsScene::adjustByteItemPositions() {
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
for (auto* annotationItem : this->annotationItems) {
const auto firstByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((startAddress - memoryAddressRange.startAddress) + 1)
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(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) {

View File

@@ -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<FocusedMemoryRegion>& focusedMemoryRegions;
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions;
Targets::TargetMemoryBuffer lastValueBuffer;
std::map<std::uint32_t, ByteItem*> byteItemsByAddress;
std::map<std::uint32_t, AnnotationItem*> annotationItemsByStartAddress;
std::vector<AnnotationItem*> annotationItems;
std::vector<ValueAnnotationItem*> valueAnnotationItems;
std::map<std::size_t, std::vector<ByteItem*>> byteItemsByRowIndex;
std::map<std::size_t, std::vector<ByteItem*>> 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);

View File

@@ -0,0 +1,75 @@
#include "ValueAnnotationItem.hpp"
#include <algorithm>
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<const char*>(asciiData.data()),
static_cast<qsizetype>(asciiData.size())
) + "'";
break;
}
default: {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
}
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <QPainter>
#include <QWidget>
#include <QStyleOptionGraphicsItem>
#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();
};
}