Refactored byte item selection and highlighting in hex viewer
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <set>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "src/Targets/TargetMemory.hpp"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
@@ -28,7 +29,7 @@ namespace Widgets
|
|||||||
* always be enabled.
|
* always be enabled.
|
||||||
*/
|
*/
|
||||||
using IsEnabledCallbackType = std::function<
|
using IsEnabledCallbackType = std::function<
|
||||||
bool(const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>&)
|
bool(const std::set<Targets::TargetMemoryAddress>&)
|
||||||
>;
|
>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -41,6 +42,6 @@ namespace Widgets
|
|||||||
);
|
);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void invoked(const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress);
|
void invoked(const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace Widgets
|
|||||||
ByteItem* hoveredByteItem = nullptr;
|
ByteItem* hoveredByteItem = nullptr;
|
||||||
std::optional<Targets::TargetStackPointer> currentStackPointer;
|
std::optional<Targets::TargetStackPointer> currentStackPointer;
|
||||||
bool highlightingEnabled = false;
|
bool highlightingEnabled = false;
|
||||||
|
std::set<Targets::TargetMemoryAddressRange> highlightedAddressRanges;
|
||||||
|
|
||||||
HexViewerSharedState(
|
HexViewerSharedState(
|
||||||
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
||||||
|
|||||||
@@ -242,22 +242,17 @@ namespace Widgets
|
|||||||
this->byteItemGraphicsScene->addExternalContextMenuAction(action);
|
this->byteItemGraphicsScene->addExternalContextMenuAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HexViewerWidget::highlightBytes(const std::set<Targets::TargetMemoryAddress>& addresses) {
|
void HexViewerWidget::highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
|
||||||
this->byteItemGraphicsScene->highlightByteItems(addresses);
|
this->byteItemGraphicsScene->highlightByteItemRanges(addressRanges);
|
||||||
}
|
|
||||||
|
|
||||||
void HexViewerWidget::highlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
|
|
||||||
this->byteItemGraphicsScene->highlightByteItems(this->addressRangesToAddresses(addressRanges));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HexViewerWidget::clearHighlighting() {
|
void HexViewerWidget::clearHighlighting() {
|
||||||
this->highlightBytes(std::set<Targets::TargetMemoryAddress>());
|
this->byteItemGraphicsScene->clearByteItemHighlighting();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HexViewerWidget::selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
|
void HexViewerWidget::selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
|
||||||
const auto addresses = this->addressRangesToAddresses(addressRanges);
|
this->byteItemGraphicsScene->highlightByteItemRanges(addressRanges);
|
||||||
this->byteItemGraphicsScene->highlightByteItems(addresses);
|
this->byteItemGraphicsScene->selectByteItemRanges(addressRanges);
|
||||||
this->byteItemGraphicsScene->selectByteItems(addresses);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HexViewerWidget::centerOnByte(Targets::TargetMemoryAddress address) {
|
void HexViewerWidget::centerOnByte(Targets::TargetMemoryAddress address) {
|
||||||
@@ -377,9 +372,9 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HexViewerWidget::onByteSelectionChanged(
|
void HexViewerWidget::onByteSelectionChanged(
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
|
const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses
|
||||||
) {
|
) {
|
||||||
const auto selectionCount = selectedByteItemsByAddress.size();
|
const auto selectionCount = selectedByteItemAddresses.size();
|
||||||
|
|
||||||
if (selectionCount == 0) {
|
if (selectionCount == 0) {
|
||||||
this->selectionCountLabel->hide();
|
this->selectionCountLabel->hide();
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ namespace Widgets
|
|||||||
void refreshRegions();
|
void refreshRegions();
|
||||||
void setStackPointer(Targets::TargetStackPointer stackPointer);
|
void setStackPointer(Targets::TargetStackPointer stackPointer);
|
||||||
void addExternalContextMenuAction(ContextMenuAction* action);
|
void addExternalContextMenuAction(ContextMenuAction* action);
|
||||||
void highlightBytes(const std::set<Targets::TargetMemoryAddress>& addresses);
|
void highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
||||||
void highlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
|
||||||
void clearHighlighting();
|
void clearHighlighting();
|
||||||
void selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
void selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
||||||
void centerOnByte(Targets::TargetMemoryAddress address);
|
void centerOnByte(Targets::TargetMemoryAddress address);
|
||||||
@@ -91,9 +90,7 @@ namespace Widgets
|
|||||||
void setDisplayAsciiEnabled(bool enabled);
|
void setDisplayAsciiEnabled(bool enabled);
|
||||||
void onGoToAddressInputChanged();
|
void onGoToAddressInputChanged();
|
||||||
void onHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
|
void onHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
|
||||||
void onByteSelectionChanged(
|
void onByteSelectionChanged(const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses);
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
|
|
||||||
);
|
|
||||||
std::set<Targets::TargetMemoryAddress> addressRangesToAddresses(
|
std::set<Targets::TargetMemoryAddress> addressRangesToAddresses(
|
||||||
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
|
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -215,18 +215,18 @@ namespace Widgets
|
|||||||
InsightWorker::queueTask(constructHexViewerTopLevelGroupItem);
|
InsightWorker::queueTask(constructHexViewerTopLevelGroupItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) {
|
void ItemGraphicsScene::updateStackPointer(Targets::TargetStackPointer stackPointer) {
|
||||||
this->state.currentStackPointer = stackPointer;
|
this->state.currentStackPointer = stackPointer;
|
||||||
this->rebuildItemHierarchy();
|
this->rebuildItemHierarchy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::selectByteItems(const std::set<std::uint32_t>& addresses) {
|
void ItemGraphicsScene::selectByteItems(const std::set<Targets::TargetMemoryAddress>& addresses) {
|
||||||
this->selectedByteItemsByAddress.clear();
|
this->selectedByteItemAddresses.clear();
|
||||||
|
|
||||||
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
|
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
|
||||||
if (addresses.contains(address)) {
|
if (addresses.contains(address)) {
|
||||||
byteItem.selected = true;
|
byteItem.selected = true;
|
||||||
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
|
this->selectedByteItemAddresses.insert(byteItem.startAddress);
|
||||||
|
|
||||||
} else if (byteItem.selected) {
|
} else if (byteItem.selected) {
|
||||||
byteItem.selected = false;
|
byteItem.selected = false;
|
||||||
@@ -234,16 +234,37 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->update();
|
this->update();
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::highlightByteItems(const std::set<std::uint32_t>& addresses) {
|
void ItemGraphicsScene::selectByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
|
||||||
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
|
return this->selectByteItems(this->addressRangesToAddresses(addressRanges));
|
||||||
byteItem.highlighted = addresses.contains(address);
|
}
|
||||||
|
|
||||||
|
void ItemGraphicsScene::highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
|
||||||
|
/*
|
||||||
|
* Don't bother updating the byte items if addressRanges is empty - updating the this->state.highlightingEnabled
|
||||||
|
* flag will prevent the highlighting, and the byte items will be updated the next time we actually want to
|
||||||
|
* highlight something.
|
||||||
|
*
|
||||||
|
* Not pretty but it saves a lot of cycles.
|
||||||
|
*/
|
||||||
|
if (!addressRanges.empty()) {
|
||||||
|
const auto addresses = this->addressRangesToAddresses(addressRanges);
|
||||||
|
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
|
||||||
|
byteItem.highlighted = addresses.contains(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state.highlightingEnabled = !addresses.empty();
|
this->state.highlightingEnabled = !addressRanges.empty();
|
||||||
|
this->state.highlightedAddressRanges = std::move(addressRanges);
|
||||||
this->update();
|
this->update();
|
||||||
|
|
||||||
|
emit this->highlightingChanged(addressRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemGraphicsScene::clearByteItemHighlighting() {
|
||||||
|
this->highlightByteItemRanges({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::rebuildItemHierarchy() {
|
void ItemGraphicsScene::rebuildItemHierarchy() {
|
||||||
@@ -323,7 +344,7 @@ namespace Widgets
|
|||||||
|
|
||||||
void ItemGraphicsScene::addExternalContextMenuAction(ContextMenuAction* action) {
|
void ItemGraphicsScene::addExternalContextMenuAction(ContextMenuAction* action) {
|
||||||
QObject::connect(action, &QAction::triggered, this, [this, action] () {
|
QObject::connect(action, &QAction::triggered, this, [this, action] () {
|
||||||
emit action->invoked(this->selectedByteItemsByAddress);
|
emit action->invoked(this->selectedByteItemAddresses);
|
||||||
});
|
});
|
||||||
|
|
||||||
this->externalContextMenuActions.push_back(action);
|
this->externalContextMenuActions.push_back(action);
|
||||||
@@ -367,8 +388,7 @@ namespace Widgets
|
|||||||
const auto mousePosition = mouseEvent->buttonDownScenePos(button);
|
const auto mousePosition = mouseEvent->buttonDownScenePos(button);
|
||||||
|
|
||||||
if (this->state.highlightingEnabled) {
|
if (this->state.highlightingEnabled) {
|
||||||
this->state.highlightingEnabled = false;
|
this->clearByteItemHighlighting();
|
||||||
this->update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mousePosition.x() <= this->byteAddressContainer->boundingRect().width()) {
|
if (mousePosition.x() <= this->byteAddressContainer->boundingRect().width()) {
|
||||||
@@ -424,12 +444,12 @@ namespace Widgets
|
|||||||
this->toggleByteItemSelection(byteItem);
|
this->toggleByteItemSelection(byteItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemAddresses);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->toggleByteItemSelection(*clickedByteItem);
|
this->toggleByteItemSelection(*clickedByteItem);
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemAddresses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,7 +483,7 @@ namespace Widgets
|
|||||||
for (auto& byteItem : items) {
|
for (auto& byteItem : items) {
|
||||||
this->selectByteItem(*byteItem);
|
this->selectByteItem(*byteItem);
|
||||||
}
|
}
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* hoveredByteItem = this->itemIndex->byteItemAt(mousePosition);
|
auto* hoveredByteItem = this->itemIndex->byteItemAt(mousePosition);
|
||||||
@@ -485,7 +505,7 @@ namespace Widgets
|
|||||||
void ItemGraphicsScene::keyPressEvent(QKeyEvent* keyEvent) {
|
void ItemGraphicsScene::keyPressEvent(QKeyEvent* keyEvent) {
|
||||||
const auto key = keyEvent->key();
|
const auto key = keyEvent->key();
|
||||||
|
|
||||||
if (key == Qt::Key_Escape && !this->selectedByteItemsByAddress.empty()) {
|
if (key == Qt::Key_Escape && !this->selectedByteItemAddresses.empty()) {
|
||||||
this->clearByteItemSelection();
|
this->clearByteItemSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -514,7 +534,7 @@ namespace Widgets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto itemsSelected = !this->selectedByteItemsByAddress.empty();
|
const auto itemsSelected = !this->selectedByteItemAddresses.empty();
|
||||||
|
|
||||||
auto* menu = new QMenu(this->parent);
|
auto* menu = new QMenu(this->parent);
|
||||||
menu->setLayoutDirection(Qt::LayoutDirection::LeftToRight);
|
menu->setLayoutDirection(Qt::LayoutDirection::LeftToRight);
|
||||||
@@ -548,7 +568,7 @@ namespace Widgets
|
|||||||
itemsSelected
|
itemsSelected
|
||||||
&& (
|
&& (
|
||||||
!externalAction->isEnabledCallback.has_value()
|
!externalAction->isEnabledCallback.has_value()
|
||||||
|| externalAction->isEnabledCallback.value()(this->selectedByteItemsByAddress)
|
|| externalAction->isEnabledCallback.value()(this->selectedByteItemAddresses)
|
||||||
)
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
@@ -620,12 +640,12 @@ namespace Widgets
|
|||||||
|
|
||||||
void ItemGraphicsScene::selectByteItem(ByteItem& byteItem) {
|
void ItemGraphicsScene::selectByteItem(ByteItem& byteItem) {
|
||||||
byteItem.selected = true;
|
byteItem.selected = true;
|
||||||
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
|
this->selectedByteItemAddresses.insert(byteItem.startAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::deselectByteItem(ByteItem& byteItem) {
|
void ItemGraphicsScene::deselectByteItem(ByteItem& byteItem) {
|
||||||
byteItem.selected = false;
|
byteItem.selected = false;
|
||||||
this->selectedByteItemsByAddress.erase(byteItem.startAddress);
|
this->selectedByteItemAddresses.erase(byteItem.startAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::toggleByteItemSelection(ByteItem& byteItem) {
|
void ItemGraphicsScene::toggleByteItemSelection(ByteItem& byteItem) {
|
||||||
@@ -638,23 +658,24 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::clearByteItemSelection() {
|
void ItemGraphicsScene::clearByteItemSelection() {
|
||||||
for (auto& [address, byteItem] : this->selectedByteItemsByAddress) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
byteItem->selected = false;
|
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(address);
|
||||||
|
byteItem.selected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->selectedByteItemsByAddress.clear();
|
this->selectedByteItemAddresses.clear();
|
||||||
this->update();
|
this->update();
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::selectAllByteItems() {
|
void ItemGraphicsScene::selectAllByteItems() {
|
||||||
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
|
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
|
||||||
byteItem.selected = true;
|
byteItem.selected = true;
|
||||||
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
|
this->selectedByteItemAddresses.insert(byteItem.startAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->update();
|
this->update();
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
emit this->selectionChanged(this->selectedByteItemAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::setAddressType(AddressType type) {
|
void ItemGraphicsScene::setAddressType(AddressType type) {
|
||||||
@@ -666,34 +687,31 @@ namespace Widgets
|
|||||||
this->byteAddressContainer->invalidateChildItemCaches();
|
this->byteAddressContainer->invalidateChildItemCaches();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<Targets::TargetMemoryAddress, ByteItem*> ItemGraphicsScene::sortedByteItemsByAddress() {
|
std::set<Targets::TargetMemoryAddress> ItemGraphicsScene::excludedAddresses() {
|
||||||
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
|
auto output = std::set<Targets::TargetMemoryAddress>();
|
||||||
std::transform(
|
|
||||||
this->selectedByteItemsByAddress.begin(),
|
|
||||||
this->selectedByteItemsByAddress.end(),
|
|
||||||
std::inserter(sortedByteItemsByAddress, sortedByteItemsByAddress.end()),
|
|
||||||
[] (const decltype(this->selectedByteItemsByAddress)::value_type& pair) {
|
|
||||||
return pair;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return sortedByteItemsByAddress;
|
for (const auto& excludedRegion : this->excludedMemoryRegions) {
|
||||||
|
const auto regionAddresses = excludedRegion.addressRange.addresses();
|
||||||
|
output.insert(regionAddresses.begin(), regionAddresses.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::copyAddressesToClipboard(AddressType type) {
|
void ItemGraphicsScene::copyAddressesToClipboard(AddressType type) {
|
||||||
if (this->selectedByteItemsByAddress.empty()) {
|
if (this->selectedByteItemAddresses.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = QString();
|
auto data = QString();
|
||||||
const auto memoryStartAddress = this->state.memoryDescriptor.addressRange.startAddress;
|
const auto memoryStartAddress = this->state.memoryDescriptor.addressRange.startAddress;
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
data.append(
|
data.append(
|
||||||
"0x" + QString::number(
|
"0x" + QString::number(
|
||||||
type == AddressType::RELATIVE
|
type == AddressType::RELATIVE
|
||||||
? byteItem->startAddress - memoryStartAddress
|
? address - memoryStartAddress
|
||||||
: byteItem->startAddress,
|
: address,
|
||||||
16
|
16
|
||||||
).rightJustified(8, '0').toUpper() + "\n"
|
).rightJustified(8, '0').toUpper() + "\n"
|
||||||
);
|
);
|
||||||
@@ -703,16 +721,17 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::copyHexValuesToClipboard(bool withDelimiters) {
|
void ItemGraphicsScene::copyHexValuesToClipboard(bool withDelimiters) {
|
||||||
if (this->selectedByteItemsByAddress.empty()) {
|
if (this->selectedByteItemAddresses.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto excludedAddresses = this->excludedAddresses();
|
||||||
auto data = QString();
|
auto data = QString();
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
const unsigned char byteValue = byteItem->excluded
|
const unsigned char byteValue = excludedAddresses.contains(address)
|
||||||
? 0x00
|
? 0x00
|
||||||
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
|
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
|
||||||
|
|
||||||
data.append(
|
data.append(
|
||||||
withDelimiters
|
withDelimiters
|
||||||
@@ -725,16 +744,17 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::copyDecimalValuesToClipboard() {
|
void ItemGraphicsScene::copyDecimalValuesToClipboard() {
|
||||||
if (this->selectedByteItemsByAddress.empty() || !this->state.data.has_value()) {
|
if (this->selectedByteItemAddresses.empty() || !this->state.data.has_value()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto excludedAddresses = this->excludedAddresses();
|
||||||
auto data = QString();
|
auto data = QString();
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
const unsigned char byteValue = byteItem->excluded
|
const unsigned char byteValue = excludedAddresses.contains(address)
|
||||||
? 0x00
|
? 0x00
|
||||||
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
|
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
|
||||||
data.append(QString::number(byteValue, 10) + "\n");
|
data.append(QString::number(byteValue, 10) + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,16 +762,17 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::copyBinaryBitStringToClipboard(bool withDelimiters) {
|
void ItemGraphicsScene::copyBinaryBitStringToClipboard(bool withDelimiters) {
|
||||||
if (this->selectedByteItemsByAddress.empty()) {
|
if (this->selectedByteItemAddresses.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto excludedAddresses = this->excludedAddresses();
|
||||||
auto data = QString();
|
auto data = QString();
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
const unsigned char byteValue = byteItem->excluded
|
const unsigned char byteValue = excludedAddresses.contains(address)
|
||||||
? 0x00
|
? 0x00
|
||||||
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
|
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
|
||||||
|
|
||||||
data.append(
|
data.append(
|
||||||
withDelimiters
|
withDelimiters
|
||||||
@@ -764,16 +785,17 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::copyValueMappingToClipboard() {
|
void ItemGraphicsScene::copyValueMappingToClipboard() {
|
||||||
if (this->selectedByteItemsByAddress.empty() || !this->state.data.has_value()) {
|
if (this->selectedByteItemAddresses.empty() || !this->state.data.has_value()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto excludedAddresses = this->excludedAddresses();
|
||||||
auto data = QJsonObject();
|
auto data = QJsonObject();
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
const unsigned char byteValue = byteItem->excluded
|
const unsigned char byteValue = excludedAddresses.contains(address)
|
||||||
? 0x00
|
? 0x00
|
||||||
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
|
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
|
||||||
|
|
||||||
data.insert(
|
data.insert(
|
||||||
"0x" + QString::number(address, 16).rightJustified(8, '0').toUpper(),
|
"0x" + QString::number(address, 16).rightJustified(8, '0').toUpper(),
|
||||||
@@ -785,17 +807,18 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ItemGraphicsScene::copyAsciiValueToClipboard() {
|
void ItemGraphicsScene::copyAsciiValueToClipboard() {
|
||||||
if (this->selectedByteItemsByAddress.empty() || !this->state.data.has_value()) {
|
if (this->selectedByteItemAddresses.empty() || !this->state.data.has_value()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto excludedAddresses = this->excludedAddresses();
|
||||||
auto data = QString();
|
auto data = QString();
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
|
for (const auto& address : this->selectedByteItemAddresses) {
|
||||||
const unsigned char byteValue =
|
const unsigned char byteValue =
|
||||||
(*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
|
(*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
|
||||||
|
|
||||||
if (byteItem->excluded || byteValue < 32 || byteValue > 126) {
|
if (excludedAddresses.contains(address) || byteValue < 32 || byteValue > 126) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,4 +827,17 @@ namespace Widgets
|
|||||||
|
|
||||||
QApplication::clipboard()->setText(std::move(data));
|
QApplication::clipboard()->setText(std::move(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<Targets::TargetMemoryAddress> ItemGraphicsScene::addressRangesToAddresses(
|
||||||
|
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
|
||||||
|
) {
|
||||||
|
auto addresses = std::set<Targets::TargetMemoryAddress>();
|
||||||
|
|
||||||
|
for (const auto& range : addressRanges) {
|
||||||
|
const auto rangeAddresses = range.addresses();
|
||||||
|
addresses.insert(rangeAddresses.begin(), rangeAddresses.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ namespace Widgets
|
|||||||
void init();
|
void init();
|
||||||
void updateStackPointer(Targets::TargetStackPointer stackPointer);
|
void updateStackPointer(Targets::TargetStackPointer stackPointer);
|
||||||
void selectByteItems(const std::set<Targets::TargetMemoryAddress>& addresses);
|
void selectByteItems(const std::set<Targets::TargetMemoryAddress>& addresses);
|
||||||
void highlightByteItems(const std::set<Targets::TargetMemoryAddress>& addresses);
|
void selectByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
||||||
|
void highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
||||||
|
void clearByteItemHighlighting();
|
||||||
void rebuildItemHierarchy();
|
void rebuildItemHierarchy();
|
||||||
void adjustSize();
|
void adjustSize();
|
||||||
void setEnabled(bool enabled);
|
void setEnabled(bool enabled);
|
||||||
@@ -67,9 +69,8 @@ namespace Widgets
|
|||||||
signals:
|
signals:
|
||||||
void ready();
|
void ready();
|
||||||
void hoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
|
void hoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
|
||||||
void selectionChanged(
|
void selectionChanged(const std::set<Targets::TargetMemoryAddress>& addresses);
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
|
void highlightingChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
||||||
);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
@@ -91,6 +92,7 @@ namespace Widgets
|
|||||||
ByteAddressContainer* byteAddressContainer = nullptr;
|
ByteAddressContainer* byteAddressContainer = nullptr;
|
||||||
|
|
||||||
std::unordered_map<Targets::TargetMemoryAddress, ByteItem*> selectedByteItemsByAddress;
|
std::unordered_map<Targets::TargetMemoryAddress, ByteItem*> selectedByteItemsByAddress;
|
||||||
|
std::set<Targets::TargetMemoryAddress> selectedByteItemAddresses;
|
||||||
|
|
||||||
QGraphicsRectItem* rubberBandRectItem = nullptr;
|
QGraphicsRectItem* rubberBandRectItem = nullptr;
|
||||||
std::optional<QPointF> rubberBandInitPoint = std::nullopt;
|
std::optional<QPointF> rubberBandInitPoint = std::nullopt;
|
||||||
@@ -154,12 +156,15 @@ namespace Widgets
|
|||||||
void clearByteItemSelection();
|
void clearByteItemSelection();
|
||||||
void selectAllByteItems();
|
void selectAllByteItems();
|
||||||
void setAddressType(AddressType type);
|
void setAddressType(AddressType type);
|
||||||
std::map<Targets::TargetMemoryAddress, ByteItem*> sortedByteItemsByAddress();
|
std::set<Targets::TargetMemoryAddress> excludedAddresses();
|
||||||
void copyAddressesToClipboard(AddressType type);
|
void copyAddressesToClipboard(AddressType type);
|
||||||
void copyHexValuesToClipboard(bool withDelimiters);
|
void copyHexValuesToClipboard(bool withDelimiters);
|
||||||
void copyDecimalValuesToClipboard();
|
void copyDecimalValuesToClipboard();
|
||||||
void copyBinaryBitStringToClipboard(bool withDelimiters);
|
void copyBinaryBitStringToClipboard(bool withDelimiters);
|
||||||
void copyValueMappingToClipboard();
|
void copyValueMappingToClipboard();
|
||||||
void copyAsciiValueToClipboard();
|
void copyAsciiValueToClipboard();
|
||||||
|
std::set<Targets::TargetMemoryAddress> addressRangesToAddresses(
|
||||||
|
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,3 +138,14 @@ bool MemorySnapshot::isCompatible(const Targets::TargetMemoryDescriptor& memoryD
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<Targets::TargetMemoryAddress> MemorySnapshot::excludedAddresses() const {
|
||||||
|
auto output = std::set<Targets::TargetMemoryAddress>();
|
||||||
|
|
||||||
|
for (const auto& excludedRegion : this->excludedRegions) {
|
||||||
|
const auto regionAddresses = excludedRegion.addressRange.addresses();
|
||||||
|
output.insert(regionAddresses.begin(), regionAddresses.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public:
|
|||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
|
|
||||||
bool isCompatible(const Targets::TargetMemoryDescriptor& memoryDescriptor) const;
|
bool isCompatible(const Targets::TargetMemoryDescriptor& memoryDescriptor) const;
|
||||||
|
std::set<Targets::TargetMemoryAddress> excludedAddresses() const;
|
||||||
|
|
||||||
virtual ~MemorySnapshot() = default;
|
virtual ~MemorySnapshot() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ namespace Widgets
|
|||||||
bool syncingScroll = false;
|
bool syncingScroll = false;
|
||||||
bool syncingHover = false;
|
bool syncingHover = false;
|
||||||
bool syncingSelection = false;
|
bool syncingSelection = false;
|
||||||
|
bool syncingHighlightedRanges = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ namespace Widgets
|
|||||||
this,
|
this,
|
||||||
&DifferentialItemGraphicsScene::onOtherSelectionChanged
|
&DifferentialItemGraphicsScene::onOtherSelectionChanged
|
||||||
);
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->other,
|
||||||
|
&DifferentialItemGraphicsScene::highlightingChanged,
|
||||||
|
this,
|
||||||
|
&DifferentialItemGraphicsScene::onOtherHighlightedByteRangesChanged
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportTop() {
|
ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportTop() {
|
||||||
@@ -97,23 +104,27 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DifferentialItemGraphicsScene::onOtherSelectionChanged(
|
void DifferentialItemGraphicsScene::onOtherSelectionChanged(
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
|
const std::set<Targets::TargetMemoryAddress>& addresses
|
||||||
) {
|
) {
|
||||||
if (!this->snapshotDiffSettings.syncHexViewerSelection || this->diffHexViewerState.syncingSelection) {
|
if (!this->snapshotDiffSettings.syncHexViewerSelection || this->diffHexViewerState.syncingSelection) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->diffHexViewerState.syncingSelection = true;
|
this->diffHexViewerState.syncingSelection = true;
|
||||||
this->clearByteItemSelection();
|
this->selectByteItems(addresses);
|
||||||
|
this->diffHexViewerState.syncingSelection = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& [address, otherByteItem] : selectedByteItemsByAddress) {
|
void DifferentialItemGraphicsScene::onOtherHighlightedByteRangesChanged(
|
||||||
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(address);
|
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
|
||||||
byteItem.selected = true;
|
) {
|
||||||
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
|
if (this->diffHexViewerState.syncingHighlightedRanges) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit this->selectionChanged(this->selectedByteItemsByAddress);
|
this->diffHexViewerState.syncingHighlightedRanges = true;
|
||||||
this->diffHexViewerState.syncingSelection = false;
|
this->highlightByteItemRanges(addressRanges);
|
||||||
|
this->diffHexViewerState.syncingHighlightedRanges = false;
|
||||||
|
|
||||||
this->update();
|
this->update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ namespace Widgets
|
|||||||
QMargins margins() override;
|
QMargins margins() override;
|
||||||
|
|
||||||
void onOtherHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
|
void onOtherHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
|
||||||
void onOtherSelectionChanged(
|
void onOtherSelectionChanged(const std::set<Targets::TargetMemoryAddress>& addresses);
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
|
void onOtherHighlightedByteRangesChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ namespace Widgets
|
|||||||
|
|
||||||
this->restoreBytesAction = new ContextMenuAction(
|
this->restoreBytesAction = new ContextMenuAction(
|
||||||
"Restore Selection",
|
"Restore Selection",
|
||||||
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>&) {
|
[this] (const std::set<Targets::TargetMemoryAddress>&) {
|
||||||
return this->memoryDescriptor.access.writeableDuringDebugSession;
|
return this->memoryDescriptor.access.writeableDuringDebugSession;
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
@@ -95,8 +95,8 @@ namespace Widgets
|
|||||||
this->restoreBytesAction,
|
this->restoreBytesAction,
|
||||||
&ContextMenuAction::invoked,
|
&ContextMenuAction::invoked,
|
||||||
this,
|
this,
|
||||||
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress) {
|
[this] (const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses) {
|
||||||
this->restoreSelectedBytes(selectedByteItemsByAddress, true);
|
this->restoreSelectedBytes(selectedByteItemAddresses, true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -446,20 +446,20 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SnapshotDiff::restoreSelectedBytes(
|
void SnapshotDiff::restoreSelectedBytes(
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
|
std::set<Targets::TargetMemoryAddress> addresses,
|
||||||
bool confirmationPromptEnabled
|
bool confirmationPromptEnabled
|
||||||
) {
|
) {
|
||||||
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
|
auto excludedAddresses = std::set<Targets::TargetMemoryAddress>();
|
||||||
|
for (const auto& excludedRegion : this->excludedRegionsA) {
|
||||||
for (const auto& pair : selectedByteItemsByAddress) {
|
const auto regionAddresses = excludedRegion.addressRange.addresses();
|
||||||
if (pair.second->excluded) {
|
excludedAddresses.insert(regionAddresses.begin(), regionAddresses.end());
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortedByteItemsByAddress.insert(pair);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortedByteItemsByAddress.empty()) {
|
std::erase_if(addresses, [excludedAddresses] (const auto& address) {
|
||||||
|
return excludedAddresses.contains(address);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (addresses.empty()) {
|
||||||
// The user has only selected bytes that are within an excluded region - nothing to do here
|
// The user has only selected bytes that are within an excluded region - nothing to do here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -467,8 +467,7 @@ namespace Widgets
|
|||||||
if (confirmationPromptEnabled) {
|
if (confirmationPromptEnabled) {
|
||||||
auto* confirmationDialog = new ConfirmationDialog(
|
auto* confirmationDialog = new ConfirmationDialog(
|
||||||
"Restore selected bytes",
|
"Restore selected bytes",
|
||||||
"This operation will write " + QString::number(sortedByteItemsByAddress.size())
|
"This operation will write " + QString::number(addresses.size()) + " byte(s) to the target's "
|
||||||
+ " byte(s) to the target's "
|
|
||||||
+ EnumToStringMappings::targetMemoryTypes.at(this->memoryDescriptor.type).toUpper()
|
+ EnumToStringMappings::targetMemoryTypes.at(this->memoryDescriptor.type).toUpper()
|
||||||
+ ".<br/><br/>Are you sure you want to proceed?",
|
+ ".<br/><br/>Are you sure you want to proceed?",
|
||||||
"Proceed",
|
"Proceed",
|
||||||
@@ -480,8 +479,8 @@ namespace Widgets
|
|||||||
confirmationDialog,
|
confirmationDialog,
|
||||||
&ConfirmationDialog::confirmed,
|
&ConfirmationDialog::confirmed,
|
||||||
this,
|
this,
|
||||||
[this, selectedByteItemsByAddress] {
|
[this, addresses] {
|
||||||
this->restoreSelectedBytes(selectedByteItemsByAddress, false);
|
this->restoreSelectedBytes(addresses, false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -491,10 +490,10 @@ namespace Widgets
|
|||||||
|
|
||||||
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
|
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
|
||||||
|
|
||||||
Targets::TargetMemoryAddress blockStartAddress = sortedByteItemsByAddress.begin()->first;
|
Targets::TargetMemoryAddress blockStartAddress = *(addresses.begin());
|
||||||
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
|
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : sortedByteItemsByAddress) {
|
for (const auto& address : addresses) {
|
||||||
if (address > (blockEndAddress + 1)) {
|
if (address > (blockEndAddress + 1)) {
|
||||||
// Commit the block
|
// Commit the block
|
||||||
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
|
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ namespace Widgets
|
|||||||
void setSyncHexViewerSelectionEnabled(bool enabled);
|
void setSyncHexViewerSelectionEnabled(bool enabled);
|
||||||
|
|
||||||
void restoreSelectedBytes(
|
void restoreSelectedBytes(
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
|
std::set<Targets::TargetMemoryAddress> addresses,
|
||||||
bool confirmationPromptEnabled
|
bool confirmationPromptEnabled
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ namespace Widgets
|
|||||||
|
|
||||||
this->restoreBytesAction = new ContextMenuAction(
|
this->restoreBytesAction = new ContextMenuAction(
|
||||||
"Restore Selection",
|
"Restore Selection",
|
||||||
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>&) {
|
[this] (const std::set<Targets::TargetMemoryAddress>&) {
|
||||||
return this->memoryDescriptor.access.writeableDuringDebugSession;
|
return this->memoryDescriptor.access.writeableDuringDebugSession;
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
@@ -170,8 +170,8 @@ namespace Widgets
|
|||||||
this->restoreBytesAction,
|
this->restoreBytesAction,
|
||||||
&ContextMenuAction::invoked,
|
&ContextMenuAction::invoked,
|
||||||
this,
|
this,
|
||||||
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress) {
|
[this] (const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses) {
|
||||||
this->restoreSelectedBytes(selectedByteItemsByAddress, true);
|
this->restoreSelectedBytes(selectedByteItemAddresses, true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -197,21 +197,15 @@ namespace Widgets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SnapshotViewer::restoreSelectedBytes(
|
void SnapshotViewer::restoreSelectedBytes(
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
|
std::set<Targets::TargetMemoryAddress> addresses,
|
||||||
bool confirmationPromptEnabled
|
bool confirmationPromptEnabled
|
||||||
) {
|
) {
|
||||||
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
|
const auto excludedAddresses = this->snapshot.excludedAddresses();
|
||||||
|
std::erase_if(addresses, [excludedAddresses] (const auto& address) {
|
||||||
|
return excludedAddresses.contains(address);
|
||||||
|
});
|
||||||
|
|
||||||
// Ideally, we'd use std::transform here, but that would require an additional pass to remove excluded bytes
|
if (addresses.empty()) {
|
||||||
for (const auto& pair : selectedByteItemsByAddress) {
|
|
||||||
if (pair.second->excluded) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortedByteItemsByAddress.insert(pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sortedByteItemsByAddress.empty()) {
|
|
||||||
// The user has only selected bytes that are within an excluded region - nothing to do here
|
// The user has only selected bytes that are within an excluded region - nothing to do here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -219,8 +213,7 @@ namespace Widgets
|
|||||||
if (confirmationPromptEnabled) {
|
if (confirmationPromptEnabled) {
|
||||||
auto* confirmationDialog = new ConfirmationDialog(
|
auto* confirmationDialog = new ConfirmationDialog(
|
||||||
"Restore selected bytes",
|
"Restore selected bytes",
|
||||||
"This operation will write " + QString::number(sortedByteItemsByAddress.size())
|
"This operation will write " + QString::number(addresses.size()) + " byte(s) to the target's "
|
||||||
+ " byte(s) to the target's "
|
|
||||||
+ EnumToStringMappings::targetMemoryTypes.at(this->memoryDescriptor.type).toUpper()
|
+ EnumToStringMappings::targetMemoryTypes.at(this->memoryDescriptor.type).toUpper()
|
||||||
+ ".<br/><br/>Are you sure you want to proceed?",
|
+ ".<br/><br/>Are you sure you want to proceed?",
|
||||||
"Proceed",
|
"Proceed",
|
||||||
@@ -232,8 +225,8 @@ namespace Widgets
|
|||||||
confirmationDialog,
|
confirmationDialog,
|
||||||
&ConfirmationDialog::confirmed,
|
&ConfirmationDialog::confirmed,
|
||||||
this,
|
this,
|
||||||
[this, selectedByteItemsByAddress] {
|
[this, addresses] {
|
||||||
this->restoreSelectedBytes(selectedByteItemsByAddress, false);
|
this->restoreSelectedBytes(addresses, false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -243,10 +236,10 @@ namespace Widgets
|
|||||||
|
|
||||||
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
|
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
|
||||||
|
|
||||||
Targets::TargetMemoryAddress blockStartAddress = sortedByteItemsByAddress.begin()->first;
|
Targets::TargetMemoryAddress blockStartAddress = *(addresses.begin());
|
||||||
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
|
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
|
||||||
|
|
||||||
for (const auto& [address, byteItem] : sortedByteItemsByAddress) {
|
for (const auto& address : addresses) {
|
||||||
if (address > (blockEndAddress + 1)) {
|
if (address > (blockEndAddress + 1)) {
|
||||||
// Commit the block
|
// Commit the block
|
||||||
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
|
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace Widgets
|
|||||||
|
|
||||||
void onHexViewerReady();
|
void onHexViewerReady();
|
||||||
void restoreSelectedBytes(
|
void restoreSelectedBytes(
|
||||||
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
|
std::set<Targets::TargetMemoryAddress> addresses,
|
||||||
bool confirmationPromptEnabled
|
bool confirmationPromptEnabled
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace Targets
|
namespace Targets
|
||||||
@@ -60,6 +61,17 @@ namespace Targets
|
|||||||
[[nodiscard]] bool contains(const TargetMemoryAddressRange& addressRange) const {
|
[[nodiscard]] bool contains(const TargetMemoryAddressRange& addressRange) const {
|
||||||
return this->startAddress <= addressRange.startAddress && this->endAddress >= addressRange.endAddress;
|
return this->startAddress <= addressRange.startAddress && this->endAddress >= addressRange.endAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<Targets::TargetMemoryAddress> addresses() const {
|
||||||
|
auto addresses = std::set<Targets::TargetMemoryAddress>();
|
||||||
|
auto addressesIt = addresses.end();
|
||||||
|
|
||||||
|
for (auto i = this->startAddress; i <= this->endAddress; ++i) {
|
||||||
|
addressesIt = addresses.insert(addressesIt, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TargetMemoryAccess
|
struct TargetMemoryAccess
|
||||||
|
|||||||
Reference in New Issue
Block a user