Merge pull request #2478 from PugsyMAME/master

Improved Cheat Memory Scanner functionality
This commit is contained in:
Connor McLaughlin 2021-08-07 11:35:10 +10:00 committed by GitHub
commit eeb2e01a42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 200 additions and 55 deletions

View file

@ -45,6 +45,28 @@ static QString formatHexAndDecValue(u32 value, u8 size, bool is_signed)
return QStringLiteral("0x%1 (%2)").arg(static_cast<u32>(value), size, 16, QChar('0')).arg(static_cast<uint>(value)); return QStringLiteral("0x%1 (%2)").arg(static_cast<u32>(value), size, 16, QChar('0')).arg(static_cast<uint>(value));
} }
static QString formatCheatCode(u32 address, u32 value, const MemoryAccessSize size)
{
if (size == MemoryAccessSize::Byte && address <= 0x00200000)
return QStringLiteral("CHEAT CODE: %1 %2")
.arg(static_cast<u32>(address) + 0x30000000, 8, 16, QChar('0')).toUpper()
.arg(static_cast<u16>(value), 4, 16, QChar('0')).toUpper();
else if (size == MemoryAccessSize::HalfWord && address <= 0x001FFFFE)
return QStringLiteral("CHEAT CODE: %1 %2")
.arg(static_cast<u32>(address) + 0x80000000, 8, 16, QChar('0')).toUpper()
.arg(static_cast<u16>(value), 4, 16, QChar('0')).toUpper();
else if (size == MemoryAccessSize::Word && address <= 0x001FFFFC)
return QStringLiteral("CHEAT CODE: %1 %2")
.arg(static_cast<u32>(address) + 0x90000000, 8, 16, QChar('0')).toUpper()
.arg(static_cast<u32>(value), 8, 16, QChar('0')).toUpper();
else
return QStringLiteral("OUTSIDE RAM RANGE. POKE %1 with %2")
.arg(static_cast<u32>(address), 8, 16, QChar('0')).toUpper()
.arg(static_cast<u16>(value), 8, 16, QChar('0')).toUpper();
}
static QString formatValue(u32 value, bool is_signed) static QString formatValue(u32 value, bool is_signed)
{ {
if (is_signed) if (is_signed)
@ -173,8 +195,8 @@ void CheatManagerDialog::resizeEvent(QResizeEvent* event)
void CheatManagerDialog::resizeColumns() void CheatManagerDialog::resizeColumns()
{ {
QtUtils::ResizeColumnsForTableView(m_ui.scanTable, {-1, 100, 100}); QtUtils::ResizeColumnsForTableView(m_ui.scanTable, {-1, 130, 130});
QtUtils::ResizeColumnsForTableView(m_ui.watchTable, {50, -1, 100, 150, 100}); QtUtils::ResizeColumnsForTableView(m_ui.watchTable, {-1, 100, 100, 100, 40});
QtUtils::ResizeColumnsForTreeView(m_ui.cheatList, {-1, 100, 150, 100}); QtUtils::ResizeColumnsForTreeView(m_ui.cheatList, {-1, 100, 150, 100});
} }
@ -195,7 +217,7 @@ void CheatManagerDialog::setUpdateTimerEnabled(bool enabled)
m_update_timer->stop(); m_update_timer->stop();
} }
int CheatManagerDialog::getSelectedResultIndex() const int CheatManagerDialog::getSelectedResultIndexFirst() const
{ {
QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges(); QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
if (sel.isEmpty()) if (sel.isEmpty())
@ -204,7 +226,16 @@ int CheatManagerDialog::getSelectedResultIndex() const
return sel.front().topRow(); return sel.front().topRow();
} }
int CheatManagerDialog::getSelectedWatchIndex() const int CheatManagerDialog::getSelectedResultIndexLast() const
{
QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
if (sel.isEmpty())
return -1;
return sel.front().bottomRow();
}
int CheatManagerDialog::getSelectedWatchIndexFirst() const
{ {
QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges(); QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges();
if (sel.isEmpty()) if (sel.isEmpty())
@ -213,6 +244,15 @@ int CheatManagerDialog::getSelectedWatchIndex() const
return sel.front().topRow(); return sel.front().topRow();
} }
int CheatManagerDialog::getSelectedWatchIndexLast() const
{
QList<QTableWidgetSelectionRange> sel = m_ui.watchTable->selectedRanges();
if (sel.isEmpty())
return -1;
return sel.front().bottomRow();
}
QTreeWidgetItem* CheatManagerDialog::getItemForCheatIndex(u32 index) const QTreeWidgetItem* CheatManagerDialog::getItemForCheatIndex(u32 index) const
{ {
QTreeWidgetItemIterator iter(m_ui.cheatList); QTreeWidgetItemIterator iter(m_ui.cheatList);
@ -683,16 +723,20 @@ void CheatManagerDialog::resetClicked()
void CheatManagerDialog::addToWatchClicked() void CheatManagerDialog::addToWatchClicked()
{ {
const int index = getSelectedResultIndex(); const int indexFirst = getSelectedResultIndexFirst();
if (index < 0) const int indexLast = getSelectedResultIndexLast();
if (indexFirst < 0)
return; return;
for (int index = indexFirst; index <= indexLast; index++)
{
const MemoryScan::Result& res = m_scanner.GetResults()[static_cast<u32>(index)]; const MemoryScan::Result& res = m_scanner.GetResults()[static_cast<u32>(index)];
m_watch.AddEntry(StringUtil::StdStringFromFormat("0x%08x", res.address), res.address, m_scanner.GetSize(), m_watch.AddEntry(StringUtil::StdStringFromFormat("0x%08x", res.address), res.address, m_scanner.GetSize(), m_scanner.GetValueSigned(), false);
m_scanner.GetValueSigned(), false);
updateWatch(); updateWatch();
} }
}
void CheatManagerDialog::addManualWatchAddressClicked() void CheatManagerDialog::addManualWatchAddressClicked()
{ {
std::optional<unsigned> address = QtUtils::PromptForAddress(this, windowTitle(), tr("Enter manual address:"), false); std::optional<unsigned> address = QtUtils::PromptForAddress(this, windowTitle(), tr("Enter manual address:"), false);
@ -721,13 +765,17 @@ void CheatManagerDialog::addManualWatchAddressClicked()
void CheatManagerDialog::removeWatchClicked() void CheatManagerDialog::removeWatchClicked()
{ {
const int index = getSelectedWatchIndex(); const int indexFirst = getSelectedWatchIndexFirst();
if (index < 0) const int indexLast = getSelectedWatchIndexLast();
if (indexFirst < 0)
return; return;
for (int index = indexLast; index >= indexFirst; index--)
{
m_watch.RemoveEntry(static_cast<u32>(index)); m_watch.RemoveEntry(static_cast<u32>(index));
updateWatch(); updateWatch();
} }
}
void CheatManagerDialog::scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) void CheatManagerDialog::scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{ {
@ -775,19 +823,19 @@ void CheatManagerDialog::watchItemChanged(QTableWidgetItem* item)
switch (item->column()) switch (item->column())
{ {
case 0: case 4:
{ {
m_watch.SetEntryFreeze(index, (item->checkState() == Qt::Checked)); m_watch.SetEntryFreeze(index, (item->checkState() == Qt::Checked));
} }
break; break;
case 1: case 0:
{ {
m_watch.SetEntryDescription(index, item->text().toStdString()); m_watch.SetEntryDescription(index, item->text().toStdString());
} }
break; break;
case 4: case 3:
{ {
const MemoryWatchList::Entry& entry = m_watch.GetEntry(index); const MemoryWatchList::Entry& entry = m_watch.GetEntry(index);
bool value_ok = false; bool value_ok = false;
@ -799,7 +847,11 @@ void CheatManagerDialog::watchItemChanged(QTableWidgetItem* item)
} }
else else
{ {
uint value = item->text().toUInt(&value_ok); uint value;
if (item->text()[1] == 'x' || item->text()[1] == 'X')
value = item->text().toUInt(&value_ok, 16);
else
value = item->text().toUInt(&value_ok);
if (value_ok) if (value_ok)
m_watch.SetEntryValue(index, static_cast<u32>(value)); m_watch.SetEntryValue(index, static_cast<u32>(value));
} }
@ -836,10 +888,6 @@ void CheatManagerDialog::updateResults()
{ {
if (row == MAX_DISPLAYED_SCAN_RESULTS) if (row == MAX_DISPLAYED_SCAN_RESULTS)
{ {
QMessageBox::information(this, tr("Memory Scan"),
tr("Memory scan found %1 addresses, but only the first %2 are displayed.")
.arg(m_scanner.GetResultCount())
.arg(MAX_DISPLAYED_SCAN_RESULTS));
break; break;
} }
@ -874,7 +922,11 @@ void CheatManagerDialog::updateResults()
m_ui.scanTable->setItem(row, 2, previous_item); m_ui.scanTable->setItem(row, 2, previous_item);
row++; row++;
} }
m_ui.scanResultsCount->setText(QString::number(m_scanner.GetResultCount()));
} }
else
m_ui.scanResultsCount->setText("0");
m_ui.scanResetSearch->setEnabled(!results.empty()); m_ui.scanResetSearch->setEnabled(!results.empty());
m_ui.scanSearchAgain->setEnabled(!results.empty()); m_ui.scanSearchAgain->setEnabled(!results.empty());
@ -923,22 +975,17 @@ void CheatManagerDialog::updateWatch()
{ {
m_ui.watchTable->insertRow(row); m_ui.watchTable->insertRow(row);
QTableWidgetItem* freeze_item = new QTableWidgetItem(); QTableWidgetItem* description_item = new QTableWidgetItem(formatCheatCode(res.address, res.value, res.size));
freeze_item->setFlags(freeze_item->flags() | (Qt::ItemIsEditable | Qt::ItemIsUserCheckable)); m_ui.watchTable->setItem(row, 0, description_item);
freeze_item->setCheckState(res.freeze ? Qt::Checked : Qt::Unchecked);
m_ui.watchTable->setItem(row, 0, freeze_item);
QTableWidgetItem* description_item = new QTableWidgetItem(QString::fromStdString(res.description));
m_ui.watchTable->setItem(row, 1, description_item);
QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8)); QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8));
address_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable)); address_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
m_ui.watchTable->setItem(row, 2, address_item); m_ui.watchTable->setItem(row, 1, address_item);
QTableWidgetItem* size_item = QTableWidgetItem* size_item =
new QTableWidgetItem(tr(s_size_strings[static_cast<u32>(res.size) + (res.is_signed ? 3 : 0)])); new QTableWidgetItem(tr(s_size_strings[static_cast<u32>(res.size) + (res.is_signed ? 3 : 0)]));
size_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable)); size_item->setFlags(address_item->flags() & ~(Qt::ItemIsEditable));
m_ui.watchTable->setItem(row, 3, size_item); m_ui.watchTable->setItem(row, 2, size_item);
QTableWidgetItem* value_item; QTableWidgetItem* value_item;
if (res.size == MemoryAccessSize::Byte) if (res.size == MemoryAccessSize::Byte)
@ -948,7 +995,13 @@ void CheatManagerDialog::updateWatch()
else else
value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 8, res.is_signed)); value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 8, res.is_signed));
m_ui.watchTable->setItem(row, 4, value_item); m_ui.watchTable->setItem(row, 3, value_item);
QTableWidgetItem* freeze_item = new QTableWidgetItem();
freeze_item->setFlags(freeze_item->flags() | (Qt::ItemIsEditable | Qt::ItemIsUserCheckable));
freeze_item->setCheckState(res.freeze ? Qt::Checked : Qt::Unchecked);
m_ui.watchTable->setItem(row, 4, freeze_item);
row++; row++;
} }
} }
@ -966,13 +1019,13 @@ void CheatManagerDialog::updateWatchValues()
if (res.changed) if (res.changed)
{ {
if (m_ui.scanValueBase->currentIndex() == 0) if (m_ui.scanValueBase->currentIndex() == 0)
m_ui.watchTable->item(row, 4)->setText(formatValue(res.value, res.is_signed)); m_ui.watchTable->item(row, 3)->setText(formatValue(res.value, res.is_signed));
else if (m_scanner.GetSize() == MemoryAccessSize::Byte) else if (m_scanner.GetSize() == MemoryAccessSize::Byte)
m_ui.watchTable->item(row, 4)->setText(formatHexValue(res.value, 2)); m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 2));
else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord) else if (m_scanner.GetSize() == MemoryAccessSize::HalfWord)
m_ui.watchTable->item(row, 4)->setText(formatHexValue(res.value, 4)); m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 4));
else else
m_ui.watchTable->item(row, 4)->setText(formatHexValue(res.value, 8)); m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 8));
} }
row++; row++;
} }

View file

@ -73,8 +73,10 @@ private:
QTreeWidgetItem* createItemForCheatGroup(const QString& group_name) const; QTreeWidgetItem* createItemForCheatGroup(const QString& group_name) const;
QStringList getCheatGroupNames() const; QStringList getCheatGroupNames() const;
int getSelectedCheatIndex() const; int getSelectedCheatIndex() const;
int getSelectedResultIndex() const; int getSelectedResultIndexFirst() const;
int getSelectedWatchIndex() const; int getSelectedResultIndexLast() const;
int getSelectedWatchIndexFirst() const;
int getSelectedWatchIndexLast() const;
Ui::CheatManagerDialog m_ui; Ui::CheatManagerDialog m_ui;

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>864</width> <width>1046</width>
<height>599</height> <height>778</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -163,7 +163,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::ContiguousSelection</enum>
</property> </property>
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>
@ -171,6 +171,9 @@
<property name="showGrid"> <property name="showGrid">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderHighlightSections"> <attribute name="horizontalHeaderHighlightSections">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
@ -450,6 +453,94 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QPushButton" name="scanAddWatch">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Add Selected Results To Watch List</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QTextEdit" name="scanResults">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>30</height>
</size>
</property>
<property name="inputMethodHints">
<set>Qt::ImhNone</set>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
<property name="text">
<string>Number of Results (Display limited to first 5000) : </string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="scanResultsCount">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>30</height>
</size>
</property>
<property name="inputMethodHints">
<set>Qt::ImhNone</set>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -475,7 +566,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::ContiguousSelection</enum>
</property> </property>
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>
@ -483,6 +574,9 @@
<property name="showGrid"> <property name="showGrid">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderHighlightSections"> <attribute name="horizontalHeaderHighlightSections">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
@ -491,12 +585,7 @@
</attribute> </attribute>
<column> <column>
<property name="text"> <property name="text">
<string>Freeze</string> <string>Simple Cheat Code or Description</string>
</property>
</column>
<column>
<property name="text">
<string>Description</string>
</property> </property>
</column> </column>
<column> <column>
@ -514,20 +603,15 @@
<string>Value</string> <string>Value</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>Freeze</string>
</property>
</column>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="scanAddWatch">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Add To Watch</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="scanAddManualAddress"> <widget class="QPushButton" name="scanAddManualAddress">
<property name="text"> <property name="text">
@ -541,7 +625,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Remove Watch</string> <string>Remove Selected Entries from Watch List</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -550,6 +634,9 @@
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="visible">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Load Watch</string> <string>Load Watch</string>
</property> </property>
@ -560,6 +647,9 @@
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="visible">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Save Watch</string> <string>Save Watch</string>
</property> </property>