diff --git a/pcsx2-qt/Debugger/CpuWidget.cpp b/pcsx2-qt/Debugger/CpuWidget.cpp
index e703fffffeaf7..9d9e1ec8a7e95 100644
--- a/pcsx2-qt/Debugger/CpuWidget.cpp
+++ b/pcsx2-qt/Debugger/CpuWidget.cpp
@@ -33,7 +33,6 @@
using namespace QtUtils;
using namespace MipsStackWalk;
-
CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
: m_cpu(cpu)
, m_bpModel(cpu)
diff --git a/pcsx2-qt/Debugger/CpuWidget.h b/pcsx2-qt/Debugger/CpuWidget.h
index 828e4d9a92d5d..7f09a19eebb97 100644
--- a/pcsx2-qt/Debugger/CpuWidget.h
+++ b/pcsx2-qt/Debugger/CpuWidget.h
@@ -33,9 +33,6 @@ class CpuWidget final : public QWidget
CpuWidget(QWidget* parent, DebugInterface& cpu);
~CpuWidget();
-
- // Note: The order of these enum values must reflect the order in thee Search Comparison combobox.
-
public slots:
void paintEvent(QPaintEvent* event);
@@ -92,7 +89,6 @@ public slots:
m_ui.memoryviewWidget->update();
};
-
void saveBreakpointsToDebuggerSettings();
void saveSavedAddressesToDebuggerSettings();
diff --git a/pcsx2-qt/Debugger/CpuWidget.ui b/pcsx2-qt/Debugger/CpuWidget.ui
index 5389105ad14ee..c5ad30f83c4c1 100644
--- a/pcsx2-qt/Debugger/CpuWidget.ui
+++ b/pcsx2-qt/Debugger/CpuWidget.ui
@@ -180,7 +180,7 @@
Memory Search
-
+
0
diff --git a/pcsx2-qt/Debugger/DisassemblyWidget.cpp b/pcsx2-qt/Debugger/DisassemblyWidget.cpp
index cb2d6c9fabfaa..485a1fdc4da9f 100644
--- a/pcsx2-qt/Debugger/DisassemblyWidget.cpp
+++ b/pcsx2-qt/Debugger/DisassemblyWidget.cpp
@@ -410,7 +410,6 @@ void DisassemblyWidget::paintEvent(QPaintEvent* event)
std::vector branchLines = m_disassemblyManager.getBranchLines(m_visibleStart, visibleEnd - m_visibleStart);
s32 branchCount = 0;
- s32 skippedBranches = 0;
for (const auto& branchLine : branchLines)
{
if (branchCount == (m_showInstructionOpcode ? 3 : 5))
@@ -453,12 +452,6 @@ void DisassemblyWidget::paintEvent(QPaintEvent* event)
bottom = (((branchLine.second - m_visibleStart) / 4) * m_rowHeight) + (m_rowHeight / 2);
}
- if ((top < 0 && bottom < 0) || (top > winBottom && bottom > winBottom) || (top < 0 && bottom > winBottom) || (top > winBottom && bottom < 0))
- {
- skippedBranches++;
- continue;
- }
-
branchCount++;
if (branchLine.first == m_selectedAddressStart || branchLine.second == m_selectedAddressStart)
diff --git a/pcsx2-qt/Debugger/MemorySearchWidget.cpp b/pcsx2-qt/Debugger/MemorySearchWidget.cpp
index d3b0b71c4f4cb..324204e7b0cd5 100644
--- a/pcsx2-qt/Debugger/MemorySearchWidget.cpp
+++ b/pcsx2-qt/Debugger/MemorySearchWidget.cpp
@@ -19,6 +19,8 @@
using SearchComparison = MemorySearchWidget::SearchComparison;
using SearchType = MemorySearchWidget::SearchType;
+using SearchResult = MemorySearchWidget::SearchResult;
+using SearchResults = QMap;
using namespace QtUtils;
@@ -31,19 +33,14 @@ MemorySearchWidget::MemorySearchWidget(QWidget* parent)
m_ui.listSearchResults->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.btnSearch, &QPushButton::clicked, this, &MemorySearchWidget::onSearchButtonClicked);
connect(m_ui.btnFilterSearch, &QPushButton::clicked, this, &MemorySearchWidget::onSearchButtonClicked);
- connect(m_ui.listSearchResults, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item) // move back to cpu widget
+ connect(m_ui.listSearchResults, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item)
{
emit switchToMemoryViewTab();
emit goToAddressInMemoryView(item->text().toUInt(nullptr, 16));
});
connect(m_ui.listSearchResults->verticalScrollBar(), &QScrollBar::valueChanged, this, &MemorySearchWidget::onSearchResultsListScroll);
connect(m_ui.listSearchResults, &QListView::customContextMenuRequested, this, &MemorySearchWidget::onListSearchResultsContextMenu);
- connect(m_ui.cmbSearchType, &QComboBox::currentIndexChanged, [this](int i) {
- if (i < 4)
- m_ui.chkSearchHex->setEnabled(true);
- else
- m_ui.chkSearchHex->setEnabled(false);
- });
+ connect(m_ui.cmbSearchType, &QComboBox::currentIndexChanged, this, &MemorySearchWidget::onSearchTypeChanged);
// Ensures we don't retrigger the load results function unintentionally
m_resultsLoadTimer.setInterval(100);
@@ -74,10 +71,8 @@ void MemorySearchWidget::contextRemoveSearchResult()
const int selectedResultIndex = m_ui.listSearchResults->row(m_ui.listSearchResults->selectedItems().first());
const auto* rowToRemove = m_ui.listSearchResults->takeItem(selectedResultIndex);
- if (m_searchResults.size() > static_cast(selectedResultIndex) && m_searchResults.at(selectedResultIndex) == rowToRemove->data(Qt::UserRole).toUInt())
- {
- m_searchResults.erase(m_searchResults.begin() + selectedResultIndex);
- }
+ u32 address = rowToRemove->data(Qt::UserRole).toUInt();
+ m_searchResultsMap.remove(address);
delete rowToRemove;
}
@@ -123,9 +118,22 @@ void MemorySearchWidget::onListSearchResultsContextMenu(QPoint pos)
contextMenu->popup(m_ui.listSearchResults->viewport()->mapToGlobal(pos));
}
+template
+T readValueAtAddress(DebugInterface* cpu, u32 addr);
+template<>
+float readValueAtAddress(DebugInterface* cpu, u32 addr)
+{
+ return std::bit_cast(cpu->read32(addr));
+}
+
+template<>
+double readValueAtAddress(DebugInterface* cpu, u32 addr)
+{
+ return std::bit_cast(cpu->read64(addr));
+}
template
-static T readValueAtAddress(DebugInterface* cpu, u32 addr)
+T readValueAtAddress(DebugInterface* cpu, u32 addr)
{
T val = 0;
switch (sizeof(T))
@@ -164,15 +172,13 @@ static bool memoryValueComparator(SearchComparison searchComparison, T searchVal
{
const T fTop = searchValue + 0.00001f;
const T fBottom = searchValue - 0.00001f;
- const T memValue = std::bit_cast(readValue);
- areValuesEqual = (fBottom < memValue && memValue < fTop);
+ areValuesEqual = (fBottom < readValue && readValue < fTop);
}
else if constexpr (std::is_same_v)
{
const double dTop = searchValue + 0.00001f;
const double dBottom = searchValue - 0.00001f;
- const double memValue = std::bit_cast(readValue);
- areValuesEqual = (dBottom < memValue && memValue < dTop);
+ areValuesEqual = (dBottom < readValue && readValue < dTop);
}
else
{
@@ -195,18 +201,16 @@ static bool memoryValueComparator(SearchComparison searchComparison, T searchVal
{
const T fTop = searchValue + 0.00001f;
const T fBottom = searchValue - 0.00001f;
- const T memValue = std::bit_cast(readValue);
- const bool isGreater = memValue > fTop;
- const bool isLesser = memValue < fBottom;
+ const bool isGreater = readValue > fTop;
+ const bool isLesser = readValue < fBottom;
return isGreaterOperator ? isGreater : isLesser;
}
else if (std::is_same_v)
{
const double dTop = searchValue + 0.00001f;
const double dBottom = searchValue - 0.00001f;
- const double memValue = std::bit_cast(readValue);
- const bool isGreater = memValue > dTop;
- const bool isLesser = memValue < dBottom;
+ const bool isGreater = readValue > dTop;
+ const bool isLesser = readValue < dBottom;
return isGreaterOperator ? isGreater : isLesser;
}
@@ -218,38 +222,104 @@ static bool memoryValueComparator(SearchComparison searchComparison, T searchVal
}
}
+// Handles the comparison of the read value against either the search value, or if existing searchResults are available, the value at the same address in the searchResultsMap
template
-std::vector searchWorker(DebugInterface* cpu, std::vector searchAddresses, SearchComparison searchComparison, u32 start, u32 end, T searchValue)
+bool handleSearchComparison(SearchComparison searchComparison, u32 searchAddress, SearchResults searchResults, T searchValue, T readValue)
{
- std::vector hitAddresses;
- const bool isSearchingRange = searchAddresses.size() <= 0;
+ const bool isNotOperator = searchComparison == SearchComparison::NotEquals || searchComparison == SearchComparison::NotChanged;
+ switch (searchComparison)
+ {
+ case SearchComparison::Equals:
+ case SearchComparison::NotEquals:
+ case SearchComparison::GreaterThan:
+ case SearchComparison::GreaterThanOrEqual:
+ case SearchComparison::LessThan:
+ case SearchComparison::LessThanOrEqual:
+ {
+ return memoryValueComparator(searchComparison, searchValue, readValue);
+ break;
+ }
+ case SearchComparison::Increased:
+ {
+ const T priorValue = searchResults.value(searchAddress).getValue();
+ return memoryValueComparator(SearchComparison::GreaterThan, priorValue, readValue);
+ break;
+ }
+ case SearchComparison::IncreasedBy:
+ {
+
+ const T priorValue = searchResults.value(searchAddress).getValue();
+ const T expectedIncrease = searchValue + priorValue;
+ return memoryValueComparator(SearchComparison::Equals, readValue, expectedIncrease);
+ break;
+ }
+ case SearchComparison::Decreased:
+ {
+ const T priorValue = searchResults.value(searchAddress).getValue();
+ return memoryValueComparator(SearchComparison::LessThan, priorValue, readValue);
+ break;
+ }
+ case SearchComparison::DecreasedBy:
+ {
+ const T priorValue = searchResults.value(searchAddress).getValue();
+ const T expectedDecrease = priorValue - searchValue;
+ return memoryValueComparator(SearchComparison::Equals, readValue, expectedDecrease);
+ break;
+ }
+ case SearchComparison::Changed:
+ case SearchComparison::NotChanged:
+ {
+ const T priorValue = searchResults.value(searchAddress).getValue();
+ return memoryValueComparator(isNotOperator ? SearchComparison::Equals : SearchComparison::NotEquals, priorValue, readValue);
+ break;
+ }
+ case SearchComparison::ChangedBy:
+ {
+ const T priorValue = searchResults.value(searchAddress).getValue();
+ const T expectedIncrease = searchValue + priorValue;
+ const T expectedDecrease = priorValue - searchValue;
+ return memoryValueComparator(SearchComparison::Equals, readValue, expectedIncrease) || memoryValueComparator(SearchComparison::Equals, readValue, expectedDecrease);
+ }
+ default:
+ Console.Error("Debugger: Unknown type when doing memory search!");
+ return false;
+ }
+}
+
+template
+SearchResults searchWorker(DebugInterface* cpu, SearchResults searchResults, SearchType searchType, SearchComparison searchComparison, u32 start, u32 end, T searchValue)
+{
+ SearchResults newSearchResults;
+ const bool isSearchingRange = searchResults.size() <= 0;
if (isSearchingRange)
{
for (u32 addr = start; addr < end; addr += sizeof(T))
{
if (!cpu->isValidAddress(addr))
continue;
+
T readValue = readValueAtAddress(cpu, addr);
- if (memoryValueComparator(searchComparison, searchValue, readValue))
+ if (handleSearchComparison(searchComparison, addr, searchResults, searchValue, readValue))
{
- hitAddresses.push_back(addr);
+ newSearchResults.insert(addr, MemorySearchWidget::SearchResult(addr, QVariant::fromValue(readValue), searchType));
}
}
}
else
{
- for (const u32 addr : searchAddresses)
+ for (const MemorySearchWidget::SearchResult& searchResult : searchResults)
{
+ const u32 addr = searchResult.getAddress();
if (!cpu->isValidAddress(addr))
continue;
T readValue = readValueAtAddress(cpu, addr);
- if (memoryValueComparator(searchComparison, searchValue, readValue))
+ if (handleSearchComparison(searchComparison, addr, searchResults, searchValue, readValue))
{
- hitAddresses.push_back(addr);
+ newSearchResults.insert(addr, MemorySearchWidget::SearchResult(addr, QVariant::fromValue(readValue), searchType));
}
}
}
- return hitAddresses;
+ return newSearchResults;
}
static bool compareByteArrayAtAddress(DebugInterface* cpu, SearchComparison searchComparison, u32 addr, QByteArray value)
@@ -282,55 +352,105 @@ static bool compareByteArrayAtAddress(DebugInterface* cpu, SearchComparison sear
return !isNotOperator;
}
-static std::vector searchWorkerByteArray(DebugInterface* cpu, SearchComparison searchComparison, std::vector searchAddresses, u32 start, u32 end, QByteArray value)
+bool handleArraySearchComparison(DebugInterface* cpu, SearchComparison searchComparison, u32 searchAddress, SearchResults searchResults, QByteArray searchValue)
{
- std::vector hitAddresses;
- const bool isSearchingRange = searchAddresses.size() <= 0;
+ const bool isNotOperator = searchComparison == SearchComparison::NotEquals || searchComparison == SearchComparison::NotChanged;
+ switch (searchComparison)
+ {
+ case SearchComparison::Equals:
+ case SearchComparison::NotEquals:
+ {
+ return compareByteArrayAtAddress(cpu, searchComparison, searchAddress, searchValue);
+ break;
+ }
+ case SearchComparison::Changed:
+ case SearchComparison::NotChanged:
+ {
+ QByteArray priorValue = searchResults.value(searchAddress).getArrayValue();
+ return compareByteArrayAtAddress(cpu, isNotOperator ? SearchComparison::Equals : SearchComparison::NotEquals, searchAddress, priorValue);
+ break;
+ }
+ default:
+ {
+ Console.Error("Debugger: Unknown search comparison when doing memory search");
+ return false;
+ }
+ }
+ // Default to no match found unless the comparison is a NotEquals
+ return isNotOperator;
+}
+
+static QByteArray readArrayAtAddress(DebugInterface* cpu, u32 address, u32 length)
+{
+ QByteArray readArray;
+ for (u32 i = address; i < address + length; i++)
+ {
+ readArray.append(cpu->read8(i));
+ }
+ return readArray;
+}
+
+static SearchResults searchWorkerByteArray(DebugInterface* cpu, SearchType searchType, SearchComparison searchComparison, SearchResults searchResults, u32 start, u32 end, QByteArray searchValue)
+{
+ SearchResults newResults;
+ const bool isSearchingRange = searchResults.size() <= 0;
if (isSearchingRange)
{
for (u32 addr = start; addr < end; addr += 1)
{
- if (compareByteArrayAtAddress(cpu, searchComparison, addr, value))
+ if (!cpu->isValidAddress(addr))
+ continue;
+ if (handleArraySearchComparison(cpu, searchComparison, addr, searchResults, searchValue))
{
- hitAddresses.emplace_back(addr);
- addr += value.length() - 1;
+ newResults.insert(addr, MemorySearchWidget::SearchResult(addr, searchValue, searchType));
+ addr += searchValue.length() - 1;
}
}
}
else
{
- for (u32 addr : searchAddresses)
+ for (MemorySearchWidget::SearchResult searchResult : searchResults)
{
- if (compareByteArrayAtAddress(cpu, searchComparison, addr, value))
+ const u32 addr = searchResult.getAddress();
+ if (!cpu->isValidAddress(addr))
+ continue;
+ if (handleArraySearchComparison(cpu, searchComparison, addr, searchResults, searchValue))
{
- hitAddresses.emplace_back(addr);
+ QByteArray matchValue;
+ if (searchComparison == SearchComparison::Equals)
+ matchValue = searchValue;
+ else if (searchComparison == SearchComparison::NotChanged)
+ matchValue = searchResult.getArrayValue();
+ else
+ matchValue = readArrayAtAddress(cpu, addr, searchValue.length() - 1);
+ newResults.insert(addr, MemorySearchWidget::SearchResult(addr, matchValue, searchType));
}
}
}
- return hitAddresses;
+ return newResults;
}
-std::vector startWorker(DebugInterface* cpu, const SearchType type, const SearchComparison searchComparison, std::vector searchAddresses, u32 start, u32 end, QString value, int base)
+SearchResults startWorker(DebugInterface* cpu, const SearchType type, const SearchComparison comparison, SearchResults searchResults, u32 start, u32 end, QString value, int base)
{
const bool isSigned = value.startsWith("-");
switch (type)
{
case SearchType::ByteType:
- return isSigned ? searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toShort(nullptr, base)) : searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toUShort(nullptr, base));
+ return isSigned ? searchWorker(cpu, searchResults, type, comparison, start, end, value.toShort(nullptr, base)) : searchWorker(cpu, searchResults, type, comparison, start, end, value.toUShort(nullptr, base));
case SearchType::Int16Type:
- return isSigned ? searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toShort(nullptr, base)) : searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toUShort(nullptr, base));
+ return isSigned ? searchWorker(cpu, searchResults, type, comparison, start, end, value.toShort(nullptr, base)) : searchWorker(cpu, searchResults, type, comparison, start, end, value.toUShort(nullptr, base));
case SearchType::Int32Type:
- return isSigned ? searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toInt(nullptr, base)) : searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toUInt(nullptr, base));
+ return isSigned ? searchWorker(cpu, searchResults, type, comparison, start, end, value.toInt(nullptr, base)) : searchWorker(cpu, searchResults, type, comparison, start, end, value.toUInt(nullptr, base));
case SearchType::Int64Type:
- return isSigned ? searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toLong(nullptr, base)) : searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toULongLong(nullptr, base));
+ return isSigned ? searchWorker(cpu, searchResults, type, comparison, start, end, value.toLong(nullptr, base)) : searchWorker(cpu, searchResults, type, comparison, start, end, value.toULongLong(nullptr, base));
case SearchType::FloatType:
- return searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toFloat());
+ return searchWorker(cpu, searchResults, type, comparison, start, end, value.toFloat());
case SearchType::DoubleType:
- return searchWorker(cpu, searchAddresses, searchComparison, start, end, value.toDouble());
+ return searchWorker(cpu, searchResults, type, comparison, start, end, value.toDouble());
case SearchType::StringType:
- return searchWorkerByteArray(cpu, searchComparison, searchAddresses, start, end, value.toUtf8());
+ return searchWorkerByteArray(cpu, type, comparison, searchResults, start, end, value.toUtf8());
case SearchType::ArrayType:
- return searchWorkerByteArray(cpu, searchComparison, searchAddresses, start, end, QByteArray::fromHex(value.toUtf8()));
+ return searchWorkerByteArray(cpu, type, comparison, searchResults, start, end, QByteArray::fromHex(value.toUtf8()));
default:
Console.Error("Debugger: Unknown type when doing memory search!");
break;
@@ -343,7 +463,7 @@ void MemorySearchWidget::onSearchButtonClicked()
if (!m_cpu->isAlive())
return;
- const SearchType searchType = static_cast(m_ui.cmbSearchType->currentIndex());
+ const SearchType searchType = getCurrentSearchType();
const bool searchHex = m_ui.chkSearchHex->isChecked();
bool ok;
@@ -370,23 +490,10 @@ void MemorySearchWidget::onSearchButtonClicked()
}
const QString searchValue = m_ui.txtSearchValue->text();
- const SearchComparison searchComparison = static_cast(m_ui.cmbSearchComparison->currentIndex());
+ const SearchComparison searchComparison = getCurrentSearchComparison();
const bool isFilterSearch = sender() == m_ui.btnFilterSearch;
unsigned long long value;
- const bool isVariableSize = searchType == SearchType::ArrayType || searchType == SearchType::StringType;
- if (isVariableSize && !isFilterSearch && searchComparison == SearchComparison::NotEquals)
- {
- QMessageBox::critical(this, tr("Debugger"), tr("Search types Array and String can use the Not Equals search comparison type with new searches."));
- return;
- }
-
- if (isVariableSize && searchComparison != SearchComparison::Equals && searchComparison != SearchComparison::NotEquals)
- {
- QMessageBox::critical(this, tr("Debugger"), tr("Search types Array and String can only be used with Equals search comparisons."));
- return;
- }
-
switch (searchType)
{
case SearchType::ByteType:
@@ -437,35 +544,39 @@ void MemorySearchWidget::onSearchButtonClicked()
return;
}
- QFutureWatcher>* workerWatcher = new QFutureWatcher>;
-
- connect(workerWatcher, &QFutureWatcher>::finished, [this, workerWatcher] {
+ QFutureWatcher* workerWatcher = new QFutureWatcher();
+ auto onSearchFinished = [this, workerWatcher] {
m_ui.btnSearch->setDisabled(false);
m_ui.listSearchResults->clear();
const auto& results = workerWatcher->future().result();
- m_searchResults = results;
+ m_searchResultsMap = results;
loadSearchResults();
+ m_ui.resultsCountLabel->setText(QString(tr("%0 results found")).arg(results.size()));
m_ui.btnFilterSearch->setDisabled(m_ui.listSearchResults->count() == 0);
- });
+ updateSearchComparisonSelections();
+ };
+ connect(workerWatcher, &QFutureWatcher>::finished, onSearchFinished);
m_ui.btnSearch->setDisabled(true);
- std::vector addresses;
+ SearchResults searchResultsMap;
if (isFilterSearch)
{
- addresses = m_searchResults;
+ searchResultsMap = m_searchResultsMap;
}
- QFuture> workerFuture =
- QtConcurrent::run(startWorker, m_cpu, searchType, searchComparison, addresses, searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
+
+ QFuture workerFuture = QtConcurrent::run(startWorker, m_cpu, searchType, searchComparison, searchResultsMap, searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
workerWatcher->setFuture(workerFuture);
+ connect(workerWatcher, &QFutureWatcher::finished, onSearchFinished);
+ m_ui.resultsCountLabel->setText(tr("Searching..."));
+ m_ui.resultsCountLabel->setVisible(true);
}
void MemorySearchWidget::onSearchResultsListScroll(u32 value)
{
- bool hasResultsToLoad = static_cast(m_ui.listSearchResults->count()) < m_searchResults.size();
- bool scrolledSufficiently = value > (m_ui.listSearchResults->verticalScrollBar()->maximum() * 0.95);
-
+ const bool hasResultsToLoad = static_cast(m_ui.listSearchResults->count()) < m_searchResultsMap.size();
+ const bool scrolledSufficiently = value > (m_ui.listSearchResults->verticalScrollBar()->maximum() * 0.95);
if (!m_resultsLoadTimer.isActive() && hasResultsToLoad && scrolledSufficiently)
{
// Load results once timer ends, allowing us to debounce repeated requests and only do one load.
@@ -476,7 +587,7 @@ void MemorySearchWidget::onSearchResultsListScroll(u32 value)
void MemorySearchWidget::loadSearchResults()
{
const u32 numLoaded = m_ui.listSearchResults->count();
- const u32 amountLeftToLoad = m_searchResults.size() - numLoaded;
+ const u32 amountLeftToLoad = m_searchResultsMap.size() - numLoaded;
if (amountLeftToLoad < 1)
return;
@@ -484,11 +595,93 @@ void MemorySearchWidget::loadSearchResults()
const u32 maxLoadAmount = isFirstLoad ? m_initialResultsLoadLimit : m_numResultsAddedPerLoad;
const u32 numToLoad = amountLeftToLoad > maxLoadAmount ? maxLoadAmount : amountLeftToLoad;
+ const auto addresses = m_searchResultsMap.keys();
for (u32 i = 0; i < numToLoad; i++)
{
- u32 address = m_searchResults.at(numLoaded + i);
+ const u32 address = addresses.at(numLoaded + i);
QListWidgetItem* item = new QListWidgetItem(QtUtils::FilledQStringFromValue(address, 16));
item->setData(Qt::UserRole, address);
m_ui.listSearchResults->addItem(item);
}
}
+
+SearchType MemorySearchWidget::getCurrentSearchType()
+{
+ return static_cast(m_ui.cmbSearchType->currentIndex());
+}
+
+SearchComparison MemorySearchWidget::getCurrentSearchComparison()
+{
+ // Note: The index can't be converted directly to the enum value since we change what comparisons are shown.
+ return m_searchComparisonLabelMap.labelToEnum(m_ui.cmbSearchComparison->currentText());
+}
+
+void MemorySearchWidget::onSearchTypeChanged(int newIndex)
+{
+ if (newIndex < 4)
+ m_ui.chkSearchHex->setEnabled(true);
+ else
+ m_ui.chkSearchHex->setEnabled(false);
+
+ // Clear existing search results when the comparison type changes
+ if (m_searchResultsMap.size() > 0 && (int)(m_searchResultsMap.first().getType()) != newIndex)
+ {
+ m_searchResultsMap.clear();
+ m_ui.btnSearch->setDisabled(false);
+ m_ui.btnFilterSearch->setDisabled(true);
+ }
+ updateSearchComparisonSelections();
+}
+
+void MemorySearchWidget::updateSearchComparisonSelections()
+{
+ const QString selectedComparisonLabel = m_ui.cmbSearchComparison->currentText();
+ const SearchComparison selectedComparison = m_searchComparisonLabelMap.labelToEnum(selectedComparisonLabel);
+
+ const std::vector comparisons = getValidSearchComparisonsForState(getCurrentSearchType(), m_searchResultsMap);
+ m_ui.cmbSearchComparison->clear();
+ for (const SearchComparison comparison : comparisons)
+ {
+ m_ui.cmbSearchComparison->addItem(m_searchComparisonLabelMap.enumToLabel(comparison));
+ }
+
+ // Preserve selection if applicable
+ if (selectedComparison == SearchComparison::Invalid)
+ return;
+ if (std::find(comparisons.begin(), comparisons.end(), selectedComparison) != comparisons.end())
+ m_ui.cmbSearchComparison->setCurrentText(selectedComparisonLabel);
+}
+
+std::vector MemorySearchWidget::getValidSearchComparisonsForState(SearchType type, SearchResults existingResults)
+{
+ const bool hasResults = existingResults.size() > 0;
+ std::vector comparisons = { SearchComparison::Equals };
+
+ if (type == SearchType::ArrayType || type == SearchType::StringType)
+ {
+ if (hasResults && existingResults.first().isArrayValue())
+ {
+ comparisons.push_back(SearchComparison::NotEquals);
+ comparisons.push_back(SearchComparison::Changed);
+ comparisons.push_back(SearchComparison::NotChanged);
+ }
+ return comparisons;
+ }
+ comparisons.push_back(SearchComparison::NotEquals);
+ comparisons.push_back(SearchComparison::GreaterThan);
+ comparisons.push_back(SearchComparison::GreaterThanOrEqual);
+ comparisons.push_back(SearchComparison::LessThan);
+ comparisons.push_back(SearchComparison::LessThanOrEqual);
+
+ if (hasResults && existingResults.first().getType() == type)
+ {
+ comparisons.push_back(SearchComparison::Increased);
+ comparisons.push_back(SearchComparison::IncreasedBy);
+ comparisons.push_back(SearchComparison::Decreased);
+ comparisons.push_back(SearchComparison::DecreasedBy);
+ comparisons.push_back(SearchComparison::Changed);
+ comparisons.push_back(SearchComparison::ChangedBy);
+ comparisons.push_back(SearchComparison::NotChanged);
+ }
+ return comparisons;
+}
diff --git a/pcsx2-qt/Debugger/MemorySearchWidget.h b/pcsx2-qt/Debugger/MemorySearchWidget.h
index 5cd3833dea339..28ff0a372cfaf 100644
--- a/pcsx2-qt/Debugger/MemorySearchWidget.h
+++ b/pcsx2-qt/Debugger/MemorySearchWidget.h
@@ -9,6 +9,7 @@
#include
#include
+#include
class MemorySearchWidget final : public QWidget
{
@@ -39,12 +40,86 @@ class MemorySearchWidget final : public QWidget
GreaterThan,
GreaterThanOrEqual,
LessThan,
- LessThanOrEqual
+ LessThanOrEqual,
+ Increased,
+ IncreasedBy,
+ Decreased,
+ DecreasedBy,
+ Changed,
+ ChangedBy,
+ NotChanged,
+ Invalid
+ };
+
+ class SearchComparisonLabelMap
+ {
+ public:
+ SearchComparisonLabelMap()
+ {
+ insert(SearchComparison::Equals, tr("Equals"));
+ insert(SearchComparison::NotEquals, tr("Not Equals"));
+ insert(SearchComparison::GreaterThan, tr("Greater Than"));
+ insert(SearchComparison::GreaterThanOrEqual, tr("Greater Than Or Equal"));
+ insert(SearchComparison::LessThan, tr("Less Than"));
+ insert(SearchComparison::LessThanOrEqual, tr("Less Than Or Equal"));
+ insert(SearchComparison::Increased, tr("Increased"));
+ insert(SearchComparison::IncreasedBy, tr("Increased By"));
+ insert(SearchComparison::Decreased, tr("Decreased"));
+ insert(SearchComparison::DecreasedBy, tr("Decreased By"));
+ insert(SearchComparison::Changed, tr("Changed"));
+ insert(SearchComparison::ChangedBy, tr("Changed By"));
+ insert(SearchComparison::NotChanged, tr("Not Changed"));
+ insert(SearchComparison::Invalid, "");
+ }
+ SearchComparison labelToEnum(QString comparisonLabel)
+ {
+ return labelToEnumMap.value(comparisonLabel, SearchComparison::Invalid);
+ }
+ QString enumToLabel(SearchComparison comparison) {
+ return enumToLabelMap.value(comparison, "");
+ }
+ private:
+ QMap enumToLabelMap;
+ QMap labelToEnumMap;
+ void insert(SearchComparison comparison, QString comparisonLabel)
+ {
+ enumToLabelMap.insert(comparison, comparisonLabel);
+ labelToEnumMap.insert(comparisonLabel, comparison);
+ };
+ };
+
+ class SearchResult
+ {
+ private:
+ u32 address;
+ QVariant value;
+ SearchType type;
+
+ public:
+ SearchResult() {}
+ SearchResult(u32 address, const QVariant& value, SearchType type)
+ : address(address), value(value), type(type)
+ {
+ }
+ bool isIntegerValue() const { return type == SearchType::ByteType || type == SearchType::Int16Type || type == SearchType::Int32Type || type == SearchType::Int64Type; }
+ bool isFloatValue() const { return type == SearchType::FloatType; }
+ bool isDoubleValue() const { return type == SearchType::DoubleType; }
+ bool isArrayValue() const { return type == SearchType::ArrayType || type == SearchType::StringType; }
+ u32 getAddress() const { return address; }
+ SearchType getType() const { return type; }
+ QByteArray getArrayValue() const { return isArrayValue() ? value.toByteArray() : QByteArray(); }
+
+ template
+ T getValue() const
+ {
+ return value.value();
+ }
};
public slots:
void onSearchButtonClicked();
void onSearchResultsListScroll(u32 value);
+ void onSearchTypeChanged(int newIndex);
void loadSearchResults();
void contextSearchResultGoToDisassembly();
void contextRemoveSearchResult();
@@ -58,13 +133,17 @@ public slots:
void switchToMemoryViewTab();
private:
- std::vector m_searchResults;
-
- Ui::MemorySearchWidget m_ui;
+ QMap m_searchResultsMap;
+ SearchComparisonLabelMap m_searchComparisonLabelMap;
+ Ui::MemorySearchWidget m_ui;
+ DebugInterface* m_cpu;
+ QTimer m_resultsLoadTimer;
- DebugInterface* m_cpu;
- QTimer m_resultsLoadTimer;
-
- u32 m_initialResultsLoadLimit = 20000;
+ u32 m_initialResultsLoadLimit = 20000;
u32 m_numResultsAddedPerLoad = 10000;
+
+ void updateSearchComparisonSelections();
+ std::vector getValidSearchComparisonsForState(SearchType type, QMap existingResults);
+ SearchType getCurrentSearchType();
+ SearchComparison getCurrentSearchComparison();
};
diff --git a/pcsx2-qt/Debugger/MemorySearchWidget.ui b/pcsx2-qt/Debugger/MemorySearchWidget.ui
index 789724a035f52..8e397067bf81e 100644
--- a/pcsx2-qt/Debugger/MemorySearchWidget.ui
+++ b/pcsx2-qt/Debugger/MemorySearchWidget.ui
@@ -22,7 +22,7 @@
-
-
-
+
Value
@@ -32,7 +32,7 @@
-
-
+
Type
@@ -83,7 +83,7 @@
-
-
+
Hex
@@ -162,7 +162,7 @@
-
-
-
+
Start
@@ -176,7 +176,7 @@
-
-
+
End
@@ -194,6 +194,16 @@
-
+ -
+
+
+ false
+
+
+
+
+
+