Improved Cheat Memory Scanner functionality

Work done :
** Made the columns sortable (someone asked for this GH I think)
** Moved the Add to Watch button to under the search parameters, and renamed it.
** Made the selection work on a block level so we can block "add results" and block "Remove Watch" on multiple entries
** Made the description show the raw code - so either 3xxxxxxx, 8xxxxxxx, 9xxxxxxx depending on the Data Size and the memory being under 0x200000. This can be edited still.
** Changed the Watch value field to take a hex number in the form 0xX as well as a decimal number.
** Moved the freeze box to the right column, I originally did this prior to finding out about ContiguousSelection and I think it makes more sense next to the value you want to freeze
** Removed the message about 5000 results limitation (it made searching painful) and added a text box showing it permanently along with the number of results (which you can observe going down as you continue the search)
** Hidden the "Save Watch" & "Load Watch" buttons, they have no functionality (yet) and making them invisible in the mean time makes it less confusing.
This commit is contained in:
PugsyMAME 2021-08-06 12:11:22 +01:00 committed by GitHub
parent 106dc2951d
commit 7025cc3382
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 199 additions and 54 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));
}
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)
{
if (is_signed)
@ -173,8 +195,8 @@ void CheatManagerDialog::resizeEvent(QResizeEvent* event)
void CheatManagerDialog::resizeColumns()
{
QtUtils::ResizeColumnsForTableView(m_ui.scanTable, {-1, 100, 100});
QtUtils::ResizeColumnsForTableView(m_ui.watchTable, {50, -1, 100, 150, 100});
QtUtils::ResizeColumnsForTableView(m_ui.scanTable, {-1, 130, 130});
QtUtils::ResizeColumnsForTableView(m_ui.watchTable, {-1, 100, 100, 100, 40});
QtUtils::ResizeColumnsForTreeView(m_ui.cheatList, {-1, 100, 150, 100});
}
@ -195,7 +217,7 @@ void CheatManagerDialog::setUpdateTimerEnabled(bool enabled)
m_update_timer->stop();
}
int CheatManagerDialog::getSelectedResultIndex() const
int CheatManagerDialog::getSelectedResultIndexFirst() const
{
QList<QTableWidgetSelectionRange> sel = m_ui.scanTable->selectedRanges();
if (sel.isEmpty())
@ -204,7 +226,16 @@ int CheatManagerDialog::getSelectedResultIndex() const
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();
if (sel.isEmpty())
@ -213,6 +244,15 @@ int CheatManagerDialog::getSelectedWatchIndex() const
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
{
QTreeWidgetItemIterator iter(m_ui.cheatList);
@ -683,16 +723,20 @@ void CheatManagerDialog::resetClicked()
void CheatManagerDialog::addToWatchClicked()
{
const int index = getSelectedResultIndex();
if (index < 0)
const int indexFirst = getSelectedResultIndexFirst();
const int indexLast = getSelectedResultIndexLast();
if (indexFirst < 0)
return;
for (int index = indexFirst; index <= indexLast; 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_scanner.GetValueSigned(), false);
m_watch.AddEntry(StringUtil::StdStringFromFormat("0x%08x", res.address), res.address, m_scanner.GetSize(), m_scanner.GetValueSigned(), false);
updateWatch();
}
}
void CheatManagerDialog::addManualWatchAddressClicked()
{
std::optional<unsigned> address = QtUtils::PromptForAddress(this, windowTitle(), tr("Enter manual address:"), false);
@ -721,13 +765,17 @@ void CheatManagerDialog::addManualWatchAddressClicked()
void CheatManagerDialog::removeWatchClicked()
{
const int index = getSelectedWatchIndex();
if (index < 0)
const int indexFirst = getSelectedWatchIndexFirst();
const int indexLast = getSelectedWatchIndexLast();
if (indexFirst < 0)
return;
for (int index = indexLast; index >= indexFirst; index--)
{
m_watch.RemoveEntry(static_cast<u32>(index));
updateWatch();
}
}
void CheatManagerDialog::scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{
@ -775,19 +823,19 @@ void CheatManagerDialog::watchItemChanged(QTableWidgetItem* item)
switch (item->column())
{
case 0:
case 4:
{
m_watch.SetEntryFreeze(index, (item->checkState() == Qt::Checked));
}
break;
case 1:
case 0:
{
m_watch.SetEntryDescription(index, item->text().toStdString());
}
break;
case 4:
case 3:
{
const MemoryWatchList::Entry& entry = m_watch.GetEntry(index);
bool value_ok = false;
@ -799,7 +847,11 @@ void CheatManagerDialog::watchItemChanged(QTableWidgetItem* item)
}
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)
m_watch.SetEntryValue(index, static_cast<u32>(value));
}
@ -836,10 +888,6 @@ void CheatManagerDialog::updateResults()
{
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;
}
@ -874,7 +922,11 @@ void CheatManagerDialog::updateResults()
m_ui.scanTable->setItem(row, 2, previous_item);
row++;
}
m_ui.scanResultsCount->setText(QString::number(m_scanner.GetResultCount()));
}
else
m_ui.scanResultsCount->setText("0");
m_ui.scanResetSearch->setEnabled(!results.empty());
m_ui.scanSearchAgain->setEnabled(!results.empty());
@ -923,22 +975,17 @@ void CheatManagerDialog::updateWatch()
{
m_ui.watchTable->insertRow(row);
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, 0, freeze_item);
QTableWidgetItem* description_item = new QTableWidgetItem(QString::fromStdString(res.description));
m_ui.watchTable->setItem(row, 1, description_item);
QTableWidgetItem* description_item = new QTableWidgetItem(formatCheatCode(res.address, res.value, res.size));
m_ui.watchTable->setItem(row, 0, description_item);
QTableWidgetItem* address_item = new QTableWidgetItem(formatHexValue(res.address, 8));
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 =
new QTableWidgetItem(tr(s_size_strings[static_cast<u32>(res.size) + (res.is_signed ? 3 : 0)]));
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;
if (res.size == MemoryAccessSize::Byte)
@ -948,7 +995,13 @@ void CheatManagerDialog::updateWatch()
else
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++;
}
}
@ -966,13 +1019,13 @@ void CheatManagerDialog::updateWatchValues()
if (res.changed)
{
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)
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)
m_ui.watchTable->item(row, 4)->setText(formatHexValue(res.value, 4));
m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 4));
else
m_ui.watchTable->item(row, 4)->setText(formatHexValue(res.value, 8));
m_ui.watchTable->item(row, 3)->setText(formatHexValue(res.value, 8));
}
row++;
}

View file

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

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>864</width>
<height>599</height>
<width>1046</width>
<height>778</height>
</rect>
</property>
<property name="windowTitle">
@ -163,7 +163,7 @@
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
<enum>QAbstractItemView::ContiguousSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
@ -171,6 +171,9 @@
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
@ -450,6 +453,94 @@
</item>
</layout>
</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>
<spacer name="verticalSpacer">
<property name="orientation">
@ -475,7 +566,7 @@
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
<enum>QAbstractItemView::ContiguousSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
@ -483,6 +574,9 @@
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
@ -491,43 +585,33 @@
</attribute>
<column>
<property name="text">
<string>Freeze</string>
<string>Simple Cheat Code or Description</string>
</property>
</column>
<column>
<property name="text">
<string>Description</string>
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
<string>Value</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
<string>Freeze</string>
</property>
</column>
</widget>
</item>
<item>
<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>
<widget class="QPushButton" name="scanAddManualAddress">
<property name="text">
@ -541,7 +625,7 @@
<bool>false</bool>
</property>
<property name="text">
<string>Remove Watch</string>
<string>Remove Selected Entries from Watch List</string>
</property>
</widget>
</item>
@ -550,6 +634,9 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="text">
<string>Load Watch</string>
</property>
@ -560,6 +647,9 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="text">
<string>Save Watch</string>
</property>