|
|
|
@ -19,6 +19,8 @@
|
|
|
|
|
|
|
|
|
|
using SearchComparison = MemorySearchWidget::SearchComparison;
|
|
|
|
|
using SearchType = MemorySearchWidget::SearchType;
|
|
|
|
|
using SearchResult = MemorySearchWidget::SearchResult;
|
|
|
|
|
using SearchResults = QMap<u32, MemorySearchWidget::SearchResult>;
|
|
|
|
|
|
|
|
|
|
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<size_t>(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<typename T>
|
|
|
|
|
T readValueAtAddress(DebugInterface* cpu, u32 addr);
|
|
|
|
|
template<>
|
|
|
|
|
float readValueAtAddress<float>(DebugInterface* cpu, u32 addr)
|
|
|
|
|
{
|
|
|
|
|
return std::bit_cast<float>(cpu->read32(addr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
double readValueAtAddress<double>(DebugInterface* cpu, u32 addr)
|
|
|
|
|
{
|
|
|
|
|
return std::bit_cast<double>(cpu->read64(addr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
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<float, u32>(readValue);
|
|
|
|
|
areValuesEqual = (fBottom < memValue && memValue < fTop);
|
|
|
|
|
areValuesEqual = (fBottom < readValue && readValue < fTop);
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (std::is_same_v<T, double>)
|
|
|
|
|
{
|
|
|
|
|
const double dTop = searchValue + 0.00001f;
|
|
|
|
|
const double dBottom = searchValue - 0.00001f;
|
|
|
|
|
const double memValue = std::bit_cast<double, u64>(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<float, u32>(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<T, double>)
|
|
|
|
|
{
|
|
|
|
|
const double dTop = searchValue + 0.00001f;
|
|
|
|
|
const double dBottom = searchValue - 0.00001f;
|
|
|
|
|
const double memValue = std::bit_cast<double, u64>(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 <typename T>
|
|
|
|
|
std::vector<u32> searchWorker(DebugInterface* cpu, std::vector<u32> searchAddresses, SearchComparison searchComparison, u32 start, u32 end, T searchValue)
|
|
|
|
|
bool handleSearchComparison(SearchComparison searchComparison, u32 searchAddress, SearchResults searchResults, T searchValue, T readValue)
|
|
|
|
|
{
|
|
|
|
|
std::vector<u32> 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<T>();
|
|
|
|
|
return memoryValueComparator(SearchComparison::GreaterThan, priorValue, readValue);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SearchComparison::IncreasedBy:
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
const T priorValue = searchResults.value(searchAddress).getValue<T>();
|
|
|
|
|
const T expectedIncrease = searchValue + priorValue;
|
|
|
|
|
return memoryValueComparator(SearchComparison::Equals, readValue, expectedIncrease);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SearchComparison::Decreased:
|
|
|
|
|
{
|
|
|
|
|
const T priorValue = searchResults.value(searchAddress).getValue<T>();
|
|
|
|
|
return memoryValueComparator(SearchComparison::LessThan, priorValue, readValue);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SearchComparison::DecreasedBy:
|
|
|
|
|
{
|
|
|
|
|
const T priorValue = searchResults.value(searchAddress).getValue<T>();
|
|
|
|
|
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<T>();
|
|
|
|
|
return memoryValueComparator(isNotOperator ? SearchComparison::Equals : SearchComparison::NotEquals, priorValue, readValue);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SearchComparison::ChangedBy:
|
|
|
|
|
{
|
|
|
|
|
const T priorValue = searchResults.value(searchAddress).getValue<T>();
|
|
|
|
|
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 <typename T>
|
|
|
|
|
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<T>(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<T>(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<u32> searchWorkerByteArray(DebugInterface* cpu, SearchComparison searchComparison, std::vector<u32> searchAddresses, u32 start, u32 end, QByteArray value)
|
|
|
|
|
bool handleArraySearchComparison(DebugInterface* cpu, SearchComparison searchComparison, u32 searchAddress, SearchResults searchResults, QByteArray searchValue)
|
|
|
|
|
{
|
|
|
|
|
std::vector<u32> 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<u32> startWorker(DebugInterface* cpu, const SearchType type, const SearchComparison searchComparison, std::vector<u32> 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<s8>(cpu, searchAddresses, searchComparison, start, end, value.toShort(nullptr, base)) : searchWorker<u8>(cpu, searchAddresses, searchComparison, start, end, value.toUShort(nullptr, base));
|
|
|
|
|
return isSigned ? searchWorker<s8>(cpu, searchResults, type, comparison, start, end, value.toShort(nullptr, base)) : searchWorker<u8>(cpu, searchResults, type, comparison, start, end, value.toUShort(nullptr, base));
|
|
|
|
|
case SearchType::Int16Type:
|
|
|
|
|
return isSigned ? searchWorker<s16>(cpu, searchAddresses, searchComparison, start, end, value.toShort(nullptr, base)) : searchWorker<u16>(cpu, searchAddresses, searchComparison, start, end, value.toUShort(nullptr, base));
|
|
|
|
|
return isSigned ? searchWorker<s16>(cpu, searchResults, type, comparison, start, end, value.toShort(nullptr, base)) : searchWorker<u16>(cpu, searchResults, type, comparison, start, end, value.toUShort(nullptr, base));
|
|
|
|
|
case SearchType::Int32Type:
|
|
|
|
|
return isSigned ? searchWorker<s32>(cpu, searchAddresses, searchComparison, start, end, value.toInt(nullptr, base)) : searchWorker<u32>(cpu, searchAddresses, searchComparison, start, end, value.toUInt(nullptr, base));
|
|
|
|
|
return isSigned ? searchWorker<s32>(cpu, searchResults, type, comparison, start, end, value.toInt(nullptr, base)) : searchWorker<u32>(cpu, searchResults, type, comparison, start, end, value.toUInt(nullptr, base));
|
|
|
|
|
case SearchType::Int64Type:
|
|
|
|
|
return isSigned ? searchWorker<s64>(cpu, searchAddresses, searchComparison, start, end, value.toLong(nullptr, base)) : searchWorker<s64>(cpu, searchAddresses, searchComparison, start, end, value.toULongLong(nullptr, base));
|
|
|
|
|
return isSigned ? searchWorker<s64>(cpu, searchResults, type, comparison, start, end, value.toLong(nullptr, base)) : searchWorker<s64>(cpu, searchResults, type, comparison, start, end, value.toULongLong(nullptr, base));
|
|
|
|
|
case SearchType::FloatType:
|
|
|
|
|
return searchWorker<float>(cpu, searchAddresses, searchComparison, start, end, value.toFloat());
|
|
|
|
|
return searchWorker<float>(cpu, searchResults, type, comparison, start, end, value.toFloat());
|
|
|
|
|
case SearchType::DoubleType:
|
|
|
|
|
return searchWorker<double>(cpu, searchAddresses, searchComparison, start, end, value.toDouble());
|
|
|
|
|
return searchWorker<double>(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<SearchType>(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<SearchComparison>(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<std::vector<u32>>* workerWatcher = new QFutureWatcher<std::vector<u32>>;
|
|
|
|
|
|
|
|
|
|
connect(workerWatcher, &QFutureWatcher<std::vector<u32>>::finished, [this, workerWatcher] {
|
|
|
|
|
QFutureWatcher<SearchResults>* workerWatcher = new QFutureWatcher<SearchResults>();
|
|
|
|
|
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<std::vector<u32>>::finished, onSearchFinished);
|
|
|
|
|
|
|
|
|
|
m_ui.btnSearch->setDisabled(true);
|
|
|
|
|
std::vector<u32> addresses;
|
|
|
|
|
SearchResults searchResultsMap;
|
|
|
|
|
if (isFilterSearch)
|
|
|
|
|
{
|
|
|
|
|
addresses = m_searchResults;
|
|
|
|
|
searchResultsMap = m_searchResultsMap;
|
|
|
|
|
}
|
|
|
|
|
QFuture<std::vector<u32>> workerFuture =
|
|
|
|
|
QtConcurrent::run(startWorker, m_cpu, searchType, searchComparison, addresses, searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
|
|
|
|
|
|
|
|
|
|
QFuture<SearchResults> workerFuture = QtConcurrent::run(startWorker, m_cpu, searchType, searchComparison, searchResultsMap, searchStart, searchEnd, searchValue, searchHex ? 16 : 10);
|
|
|
|
|
workerWatcher->setFuture(workerFuture);
|
|
|
|
|
connect(workerWatcher, &QFutureWatcher<SearchResults>::finished, onSearchFinished);
|
|
|
|
|
m_ui.resultsCountLabel->setText(tr("Searching..."));
|
|
|
|
|
m_ui.resultsCountLabel->setVisible(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemorySearchWidget::onSearchResultsListScroll(u32 value)
|
|
|
|
|
{
|
|
|
|
|
bool hasResultsToLoad = static_cast<size_t>(m_ui.listSearchResults->count()) < m_searchResults.size();
|
|
|
|
|
bool scrolledSufficiently = value > (m_ui.listSearchResults->verticalScrollBar()->maximum() * 0.95);
|
|
|
|
|
|
|
|
|
|
const bool hasResultsToLoad = static_cast<qsizetype>(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<SearchType>(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<SearchComparison> 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<SearchComparison> MemorySearchWidget::getValidSearchComparisonsForState(SearchType type, SearchResults existingResults)
|
|
|
|
|
{
|
|
|
|
|
const bool hasResults = existingResults.size() > 0;
|
|
|
|
|
std::vector<SearchComparison> 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;
|
|
|
|
|
}
|
|
|
|
|