mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-23 06:15:38 +00:00
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:
parent
106dc2951d
commit
7025cc3382
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,12 +585,7 @@
|
|||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Freeze</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
<string>Simple Cheat Code or Description</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
|
@ -514,20 +603,15 @@
|
|||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<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>
|
||||
|
|
Loading…
Reference in a new issue