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