diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 4849eac1..2d98045a 100755
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -19,6 +19,7 @@ set(SRCS ${DolphinProcessSrc}
GUI/GUICommon.cpp
GUI/Settings/SConfig.cpp
GUI/Settings/DlgSettings.cpp
+ GUI/MemCopy/DlgCopy.cpp
GUI/MemWatcher/MemWatchDelegate.cpp
GUI/MemWatcher/MemWatchModel.cpp
GUI/MemWatcher/Dialogs/DlgChangeType.cpp
diff --git a/Source/Dolphin-memory-engine.vcxproj b/Source/Dolphin-memory-engine.vcxproj
index 32d5afc7..0aeba89a 100755
--- a/Source/Dolphin-memory-engine.vcxproj
+++ b/Source/Dolphin-memory-engine.vcxproj
@@ -118,6 +118,7 @@
+
diff --git a/Source/GUI/MainWindow.cpp b/Source/GUI/MainWindow.cpp
index 8613b46f..882953e6 100644
--- a/Source/GUI/MainWindow.cpp
+++ b/Source/GUI/MainWindow.cpp
@@ -10,6 +10,7 @@
#include "../DolphinProcess/DolphinAccessor.h"
#include "../MemoryWatch/MemWatchEntry.h"
+#include "MemCopy/DlgCopy.h"
#include "Settings/DlgSettings.h"
#include "Settings/SConfig.h"
@@ -45,6 +46,7 @@ void MainWindow::makeMenus()
m_actImportFromCT->setShortcut(Qt::Modifier::CTRL + Qt::Key::Key_I);
m_actSettings = new QAction(tr("&Settings"), this);
+ m_actCopyMemory = new QAction(tr("&Copy Memory Range"), this);
m_actViewScanner = new QAction(tr("&Scanner"), this);
m_actViewScanner->setCheckable(true);
@@ -60,6 +62,7 @@ void MainWindow::makeMenus()
connect(m_actExportAsCSV, &QAction::triggered, this, &MainWindow::onExportAsCSV);
connect(m_actSettings, &QAction::triggered, this, &MainWindow::onOpenSettings);
+ connect(m_actCopyMemory, &QAction::triggered, this, &MainWindow::onCopyMemory);
connect(m_actViewScanner, &QAction::toggled, this,
[=]
@@ -87,6 +90,7 @@ void MainWindow::makeMenus()
m_menuView = menuBar()->addMenu(tr("&View"));
m_menuView->addAction(m_actViewScanner);
+ m_menuView->addAction(m_actCopyMemory);
m_menuHelp = menuBar()->addMenu(tr("&Help"));
m_menuHelp->addAction(m_actAbout);
@@ -337,6 +341,13 @@ void MainWindow::onExportAsCSV()
m_watcher->exportWatchListAsCSV();
}
+void MainWindow::onCopyMemory()
+{
+ DlgCopy* dlg = new DlgCopy(this);
+ int dlgResult = dlg->exec();
+ delete dlg;
+}
+
void MainWindow::onOpenSettings()
{
DlgSettings* dlg = new DlgSettings(this);
diff --git a/Source/GUI/MainWindow.h b/Source/GUI/MainWindow.h
index d6241097..ffde8e7e 100644
--- a/Source/GUI/MainWindow.h
+++ b/Source/GUI/MainWindow.h
@@ -39,6 +39,7 @@ class MainWindow : public QMainWindow
void onOpenSettings();
void onImportFromCT();
void onExportAsCSV();
+ void onCopyMemory();
void onAbout();
void onQuit();
@@ -73,4 +74,5 @@ class MainWindow : public QMainWindow
QAction* m_actSettings;
QAction* m_actQuit;
QAction* m_actAbout;
+ QAction* m_actCopyMemory;
};
diff --git a/Source/GUI/MemCopy/DlgCopy.cpp b/Source/GUI/MemCopy/DlgCopy.cpp
new file mode 100644
index 00000000..8bdb91da
--- /dev/null
+++ b/Source/GUI/MemCopy/DlgCopy.cpp
@@ -0,0 +1,305 @@
+#include "DlgCopy.h"
+
+#include "../../DolphinProcess/DolphinAccessor.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "../../Common/CommonUtils.h"
+
+DlgCopy::DlgCopy(QWidget* parent) : QDialog(parent)
+{
+ QGroupBox* grbCopySettings = new QGroupBox();
+
+ QVBoxLayout* entireCopyLayout = new QVBoxLayout;
+
+ QFormLayout* copySettingsLayout = new QFormLayout();
+ m_spnWatcherCopyAddress = new QLineEdit();
+ m_spnWatcherCopyAddress->setMaxLength(8);
+ copySettingsLayout->addRow("Base Address", m_spnWatcherCopyAddress);
+
+ m_spnWatcherCopySize = new QLineEdit();
+ copySettingsLayout->addRow("Byte Count", m_spnWatcherCopySize);
+ copySettingsLayout->setLabelAlignment(Qt::AlignRight);
+ entireCopyLayout->addLayout(copySettingsLayout);
+
+ m_cmbViewerBytesSeparator = new QComboBox();
+ m_cmbViewerBytesSeparator->addItem("Byte String", ByteStringFormats::ByteString);
+ m_cmbViewerBytesSeparator->addItem("Byte String (No Spaces)", ByteStringFormats::ByteStringNoSpaces);
+ m_cmbViewerBytesSeparator->addItem("Python Byte String", ByteStringFormats::PythonByteString);
+ m_cmbViewerBytesSeparator->addItem("Python List", ByteStringFormats::PythonList);
+ m_cmbViewerBytesSeparator->addItem("C Array", ByteStringFormats::CArray);
+ copySettingsLayout->addRow("Byte Format", m_cmbViewerBytesSeparator);
+
+ m_spnWatcherCopyOutput = new QTextEdit();
+ m_spnWatcherCopyOutput->setWordWrapMode(QTextOption::WrapMode::WrapAnywhere);
+ copySettingsLayout->addRow("Output", m_spnWatcherCopyOutput);
+
+ grbCopySettings->setLayout(entireCopyLayout);
+
+ m_buttonsDlg = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Close);
+ m_buttonsDlg->setStyleSheet("* { button-layout: 2 }");
+
+ connect(m_buttonsDlg, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ connect(m_buttonsDlg, &QDialogButtonBox::clicked, this,
+ [=](QAbstractButton* button)
+ {
+ auto role = m_buttonsDlg->buttonRole(button);
+ if (role == QDialogButtonBox::ApplyRole)
+ {
+ if (DolphinComm::DolphinAccessor::getStatus() !=
+ DolphinComm::DolphinAccessor::DolphinStatus::hooked)
+ {
+ enablePage(false);
+ return;
+ }
+ copyMemory();
+ }
+ else if (role == QDialogButtonBox::Close)
+ {
+ QDialog::close();
+ }
+ });
+
+ connect(m_cmbViewerBytesSeparator, &QComboBox::currentTextChanged, this,
+ [=](const QString& string)
+ {
+ updateMemoryText();
+ });
+
+ QVBoxLayout* mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(grbCopySettings);
+ mainLayout->addWidget(m_buttonsDlg);
+
+ enablePage(DolphinComm::DolphinAccessor::getStatus() ==
+ DolphinComm::DolphinAccessor::DolphinStatus::hooked);
+
+ setLayout(mainLayout);
+
+ setWindowTitle(tr("Copy Memory Range"));
+
+ setDefaults();
+}
+
+DlgCopy::~DlgCopy()
+{
+ delete m_buttonsDlg;
+}
+
+void DlgCopy::setDefaults()
+{
+ m_spnWatcherCopyAddress->setText("");
+ m_spnWatcherCopySize->setText("");
+ m_cmbViewerBytesSeparator->setCurrentIndex(ByteStringFormats::ByteString);
+}
+
+void DlgCopy::enablePage(bool enable)
+{
+ m_cmbViewerBytesSeparator->setEnabled(enable);
+ m_spnWatcherCopyAddress->setEnabled(enable);
+ m_spnWatcherCopySize->setEnabled(enable);
+ m_spnWatcherCopyOutput->setEnabled(enable);
+ m_buttonsDlg->setEnabled(enable);
+}
+
+bool DlgCopy::copyMemory()
+{
+ u32 address, count;
+ QMessageBox* errorBox;
+
+ if (!hexStringToU32(m_spnWatcherCopyAddress->text().toStdString(), address))
+ {
+ QString errorMsg =
+ tr("The address you entered is invalid, make sure it is an "
+ "hexadecimal number between 0x80000000 and 0x817FFFFF");
+ if (DolphinComm::DolphinAccessor::isMEM2Present())
+ errorMsg.append(tr(" or between 0x90000000 and 0x93FFFFFF"));
+
+ errorBox = new QMessageBox(QMessageBox::Critical, tr("Invalid address"), errorMsg,
+ QMessageBox::Ok, nullptr);
+ errorBox->exec();
+
+ return false;
+ }
+
+ if (!uintStringToU32(m_spnWatcherCopySize->text().toStdString(), count))
+ {
+ if (!hexStringToU32(m_spnWatcherCopySize->text().toStdString(), count))
+ {
+ errorBox = new QMessageBox(
+ QMessageBox::Critical, tr("Invalid value"),
+ tr("Please make sure the byte count is a valid number in base10 or hexadecimal.\n"
+ "i.e. 5, 10, 12, 16, 0x05, 0xf1, 0x100, 0xab12\n"),
+ QMessageBox::Ok, nullptr);
+ errorBox->exec();
+
+ return false;
+ }
+ }
+
+ if (!DolphinComm::DolphinAccessor::isValidConsoleAddress(address) ||
+ !DolphinComm::DolphinAccessor::isValidConsoleAddress(address + count))
+ {
+ errorBox =
+ new QMessageBox(QMessageBox::Critical, tr("Error reading bytes"),
+ tr("The suggested range of bytes is invalid."), QMessageBox::Ok, nullptr);
+ errorBox->exec();
+
+ return false;
+ }
+
+ std::vector newData(count);
+
+ if (!DolphinComm::DolphinAccessor::readFromRAM(
+ Common::dolphinAddrToOffset(address, DolphinComm::DolphinAccessor::isARAMAccessible()),
+ newData.data(), newData.size(), false))
+ {
+ errorBox = new QMessageBox(QMessageBox::Critical, tr("Error reading bytes"),
+ tr("Dolphin was unable to read the bytes from the suggested range."),
+ QMessageBox::Ok, nullptr);
+ errorBox->exec();
+
+ return false;
+ }
+
+ m_Data = newData;
+
+ updateMemoryText();
+
+ return true;
+}
+
+void DlgCopy::updateMemoryText()
+{
+ m_spnWatcherCopyOutput->setText(QString::fromStdString(charToHexString(m_Data.data(), m_Data.size(),
+ (DlgCopy::ByteStringFormats)m_cmbViewerBytesSeparator->currentIndex())));
+}
+
+bool DlgCopy::isHexString(std::string str)
+{
+ if (str.length() > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ {
+ str = str.substr(2);
+ }
+
+ for (char c : str)
+ {
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+ {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool DlgCopy::hexStringToU32(std::string str, u32& output)
+{
+ // if (str.empty() || str.length() % 2 == 1)
+ if (str.empty())
+ return false;
+
+ if (!isHexString(str))
+ return false;
+
+ std::stringstream ss(str);
+
+ ss >> std::hex;
+ ss >> output;
+
+ return true;
+}
+
+bool DlgCopy::isUnsignedIntegerString(std::string str)
+{
+ for (char c: str)
+ {
+ if (c >= '0' && c <= '9')
+ {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool DlgCopy::uintStringToU32(std::string str, u32& output)
+{
+ if (!isUnsignedIntegerString(str) || str.empty() || str.length() > 10)
+ return false;
+
+ u64 u = std::stoll(str);
+
+ if (u > ULONG_MAX)
+ return false;
+
+ output = (u32)u;
+ return true;
+}
+
+std::string DlgCopy::charToHexString(char* input, size_t count, DlgCopy::ByteStringFormats format)
+{
+ std::stringstream ss;
+ const char convert[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ std::string beforeAll = "";
+ std::string beforeByte = "";
+ std::string betweenBytes = "";
+ std::string afterAll = "";
+
+ switch (format)
+ {
+ case ByteString:
+ beforeAll = "";
+ beforeByte = "";
+ betweenBytes = " ";
+ afterAll = "";
+ break;
+ case ByteStringNoSpaces:
+ beforeAll = "";
+ beforeByte = "";
+ betweenBytes = "";
+ afterAll = "";
+ break;
+ case PythonByteString:
+ beforeAll = "b\'";
+ beforeByte = "\\x";
+ betweenBytes = "";
+ afterAll = "\'";
+ break;
+ case PythonList:
+ beforeAll = "[ ";
+ beforeByte = "0x";
+ betweenBytes = ", ";
+ afterAll = " ]";
+ break;
+ case CArray:
+ beforeAll = "{ ";
+ beforeByte = "0x";
+ betweenBytes = ", ";
+ afterAll = " }";
+ break;
+ default:
+ return "";
+ }
+
+ ss << beforeAll;
+
+ for (int i = 0; i < count; i++)
+ {
+ ss << beforeByte << convert[(input[i] >> 4) & 0xf] << convert[input[i] & 0xf];
+
+ if (i != count - 1)
+ {
+ ss << betweenBytes;
+ }
+ }
+
+ ss << afterAll;
+
+ return ss.str();
+}
diff --git a/Source/GUI/MemCopy/DlgCopy.h b/Source/GUI/MemCopy/DlgCopy.h
new file mode 100644
index 00000000..b4797abe
--- /dev/null
+++ b/Source/GUI/MemCopy/DlgCopy.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "../GUICommon.h"
+#include
+#include
+
+class DlgCopy : public QDialog
+{
+public:
+ DlgCopy(QWidget* parent = nullptr);
+ ~DlgCopy();
+
+private:
+ enum ByteStringFormats
+ {
+ ByteString = 0,
+ ByteStringNoSpaces = 1,
+ PythonByteString = 2,
+ PythonList = 3,
+ CArray = 4
+ };
+
+ void setDefaults();
+ void enablePage(bool enable);
+ bool copyMemory();
+ void updateMemoryText();
+
+ static bool hexStringToU32(std::string str, u32& output);
+ static bool isHexString(std::string str);
+ static bool isUnsignedIntegerString(std::string str);
+ static bool uintStringToU32(std::string str, u32& output);
+ static std::string charToHexString(char* input, size_t count, ByteStringFormats format);
+
+ QLineEdit* m_spnWatcherCopyAddress;
+ QLineEdit* m_spnWatcherCopySize;
+ QTextEdit* m_spnWatcherCopyOutput;
+ QComboBox* m_cmbViewerBytesSeparator;
+ QDialogButtonBox* m_buttonsDlg;
+
+ std::vector m_Data;
+};
diff --git a/Source/GUI/MemScanner/MemScanWidget.cpp b/Source/GUI/MemScanner/MemScanWidget.cpp
index 4c4ba3e5..58afe6d9 100644
--- a/Source/GUI/MemScanner/MemScanWidget.cpp
+++ b/Source/GUI/MemScanner/MemScanWidget.cpp
@@ -7,7 +7,7 @@
#include
#include
#include
-
+#include
#include "../GUICommon.h"
MemScanWidget::MemScanWidget()
@@ -58,12 +58,15 @@ void MemScanWidget::initialiseWidgets()
m_btnFirstScan = new QPushButton(tr("First scan"));
m_btnNextScan = new QPushButton(tr("Next scan"));
m_btnNextScan->hide();
+ m_btnUndoScan = new QPushButton(tr("Undo scan"));
+ m_btnUndoScan->hide();
m_btnResetScan = new QPushButton(tr("Reset scan"));
m_btnResetScan->hide();
connect(m_btnFirstScan, &QPushButton::clicked, this, &MemScanWidget::onFirstScan);
connect(m_btnNextScan, &QPushButton::clicked, this, &MemScanWidget::onNextScan);
connect(m_btnResetScan, &QPushButton::clicked, this, &MemScanWidget::onResetScan);
+ connect(m_btnUndoScan, &QPushButton::clicked, this, &MemScanWidget::onUndoScan);
QShortcut* scanShortcut = new QShortcut(QKeySequence(Qt::Key::Key_Enter), this);
connect(scanShortcut, &QShortcut::activated, this, [=] {
@@ -76,6 +79,16 @@ void MemScanWidget::initialiseWidgets()
m_txbSearchTerm1 = new QLineEdit();
m_txbSearchTerm2 = new QLineEdit();
+ m_txbSearchRange1 = new QLineEdit();
+ m_txbSearchRange1->setMaxLength(8);
+ m_txbSearchRange1->setPlaceholderText("Search Begin (Optional)");
+ m_txbSearchRange1->setToolTip("Search Range Begin (Optional)");
+
+ m_txbSearchRange2 = new QLineEdit();
+ m_txbSearchRange2->setMaxLength(8);
+ m_txbSearchRange2->setPlaceholderText("Search End (Optional)");
+ m_txbSearchRange2->setToolTip("Search Range End (Optional)");
+
m_searchTerm2Widget = new QWidget();
m_searchTerm2Widget->hide();
@@ -131,9 +144,14 @@ void MemScanWidget::makeLayouts()
results_layout->addWidget(m_tblResulstList);
results_layout->addLayout(multiAddButtons_layout);
+ QHBoxLayout* range_layout = new QHBoxLayout();
+ range_layout->addWidget(m_txbSearchRange1);
+ range_layout->addWidget(m_txbSearchRange2);
+
QHBoxLayout* buttons_layout = new QHBoxLayout();
buttons_layout->addWidget(m_btnFirstScan);
buttons_layout->addWidget(m_btnNextScan);
+ buttons_layout->addWidget(m_btnUndoScan);
buttons_layout->addWidget(m_btnResetScan);
QHBoxLayout* searchTerm2_layout = new QHBoxLayout();
@@ -159,6 +177,7 @@ void MemScanWidget::makeLayouts()
layout_extraParams->addWidget(m_chkSignedScan);
QVBoxLayout* scannerParams_layout = new QVBoxLayout();
+ scannerParams_layout->addLayout(range_layout);
scannerParams_layout->addLayout(buttons_layout);
scannerParams_layout->addWidget(m_cmbScanType);
scannerParams_layout->addWidget(m_cmbScanFilter);
@@ -293,6 +312,83 @@ void MemScanWidget::onScanMemTypeChanged()
void MemScanWidget::onFirstScan()
{
+ m_memScanner->resetSearchRange();
+
+ bool usedCustomBeginning = false;
+ bool usedCustomEnding = false;
+ u32 endAddress;
+ u32 beginAddress;
+
+ if (m_txbSearchRange1->text().size() > 0)
+ {
+ usedCustomBeginning = true;
+ std::stringstream ss;
+ ss << m_txbSearchRange1->text().toStdString();
+ ss >> std::hex;
+ ss >> beginAddress;
+ if (ss.fail())
+ {
+ QMessageBox* errorBox =
+ new QMessageBox(QMessageBox::Critical, tr("Invalid term(s)"),
+ tr("The term you entered for the Search Range Begin (%1) is invalid")
+ .arg(m_txbSearchRange1->text()),
+ QMessageBox::Ok, this);
+ errorBox->exec();
+ return;
+ }
+
+ if (!m_memScanner->setSearchRangeBegin(beginAddress))
+ {
+ QMessageBox* errorBox = new QMessageBox(
+ QMessageBox::Critical, tr("Invalid term(s)"),
+ tr("The term you entered for the Search Range Begin (%1) is an invalid address")
+ .arg(m_txbSearchRange1->text()),
+ QMessageBox::Ok, this);
+ errorBox->exec();
+ return;
+ }
+ }
+
+ if (m_txbSearchRange2->text().size() > 0)
+ {
+ usedCustomEnding = true;
+ std::stringstream ss;
+ ss << m_txbSearchRange2->text().toStdString();
+ ss >> std::hex;
+ ss >> endAddress;
+ if (ss.fail())
+ {
+ QMessageBox* errorBox = new QMessageBox(QMessageBox::Critical, tr("Invalid term(s)"),
+ tr("The term you entered for the Search Range End (%1) is invalid")
+ .arg(m_txbSearchRange2->text()),
+ QMessageBox::Ok, this);
+ errorBox->exec();
+ return;
+ }
+
+ if (!m_memScanner->setSearchRangeEnd(endAddress))
+ {
+ QMessageBox* errorBox = new QMessageBox(
+ QMessageBox::Critical, tr("Invalid term(s)"),
+ tr("The term you entered for the Search Range End (%1) is an invalid address")
+ .arg(m_txbSearchRange2->text()),
+ QMessageBox::Ok, this);
+ errorBox->exec();
+ return;
+ }
+ }
+
+ if (usedCustomBeginning && usedCustomEnding && endAddress < beginAddress)
+ {
+ QMessageBox* errorBox = new QMessageBox(
+ QMessageBox::Critical, tr("Invalid term(s)"),
+ tr("The search range you specified (%1 - %2) is negative")
+ .arg(m_txbSearchRange1->text(), m_txbSearchRange2->text()),
+ QMessageBox::Ok, this);
+ errorBox->exec();
+ return;
+ }
+
m_memScanner->setType(static_cast(m_cmbScanType->currentIndex()));
m_memScanner->setIsSigned(m_chkSignedScan->isChecked());
m_memScanner->setEnforceMemAlignement(m_chkEnforceMemAlignement->isChecked());
@@ -318,6 +414,10 @@ void MemScanWidget::onFirstScan()
m_btnFirstScan->hide();
m_btnNextScan->show();
m_btnResetScan->show();
+ m_btnUndoScan->show();
+ m_btnUndoScan->setEnabled(m_memScanner->hasUndo());
+ m_txbSearchRange1->hide();
+ m_txbSearchRange2->hide();
m_cmbScanType->setDisabled(true);
m_chkSignedScan->setDisabled(true);
m_chkEnforceMemAlignement->setDisabled(true);
@@ -346,6 +446,39 @@ void MemScanWidget::onNextScan()
m_btnAddSelection->setEnabled(true);
m_btnRemoveSelection->setEnabled(true);
}
+
+ m_btnUndoScan->setEnabled(m_memScanner->hasUndo());
+ }
+}
+
+void MemScanWidget::onUndoScan()
+{
+ if (m_memScanner->hasUndo())
+ {
+ m_memScanner->undoScan();
+
+ int resultsFound = static_cast(m_memScanner->getResultCount());
+ m_lblResultCount->setText(
+ tr("%1 result(s) found", "", resultsFound).arg(QString::number(resultsFound)));
+ if (resultsFound <= 1000 && resultsFound != 0)
+ {
+ m_btnAddAll->setEnabled(true);
+ m_btnAddSelection->setEnabled(true);
+ m_btnRemoveSelection->setEnabled(true);
+ }
+ else
+ {
+ m_btnAddAll->setEnabled(false);
+ m_btnAddSelection->setEnabled(false);
+ m_btnRemoveSelection->setEnabled(false);
+ m_resultsListModel->updateAfterScannerReset();
+ }
+
+ m_btnUndoScan->setEnabled(m_memScanner->hasUndo());
+ }
+ else
+ {
+ onResetScan();
}
}
@@ -359,6 +492,9 @@ void MemScanWidget::onResetScan()
m_btnFirstScan->show();
m_btnNextScan->hide();
m_btnResetScan->hide();
+ m_btnUndoScan->hide();
+ m_txbSearchRange1->show();
+ m_txbSearchRange2->show();
m_cmbScanType->setEnabled(true);
m_chkSignedScan->setEnabled(true);
m_chkEnforceMemAlignement->setEnabled(true);
diff --git a/Source/GUI/MemScanner/MemScanWidget.h b/Source/GUI/MemScanner/MemScanWidget.h
index f7e9536f..ed0f70c5 100644
--- a/Source/GUI/MemScanner/MemScanWidget.h
+++ b/Source/GUI/MemScanner/MemScanWidget.h
@@ -33,6 +33,7 @@ class MemScanWidget : public QWidget
void handleScannerErrors(const Common::MemOperationReturnCode errorCode);
void onFirstScan();
void onNextScan();
+ void onUndoScan();
void onResetScan();
void onAddSelection();
void onRemoveSelection();
@@ -58,9 +59,12 @@ class MemScanWidget : public QWidget
MemScanner* m_memScanner;
ResultsListModel* m_resultsListModel;
+ QLineEdit* m_txbSearchRange1;
+ QLineEdit* m_txbSearchRange2;
QPushButton* m_btnFirstScan;
QPushButton* m_btnNextScan;
QPushButton* m_btnResetScan;
+ QPushButton* m_btnUndoScan;
QPushButton* m_btnAddSelection;
QPushButton* m_btnAddAll;
QPushButton* m_btnRemoveSelection;
diff --git a/Source/MemoryScanner/MemoryScanner.cpp b/Source/MemoryScanner/MemoryScanner.cpp
index ea3358ea..3e2c6cfd 100644
--- a/Source/MemoryScanner/MemoryScanner.cpp
+++ b/Source/MemoryScanner/MemoryScanner.cpp
@@ -21,19 +21,51 @@ Common::MemOperationReturnCode MemScanner::firstScan(const MemScanner::ScanFiter
{
return Common::MemOperationReturnCode::operationFailed;
}
- u32 ramSize = DolphinComm::DolphinAccessor::getRAMCacheSize();
+ u32 ramSize = static_cast(DolphinComm::DolphinAccessor::getRAMCacheSize());
m_scanRAMCache = new char[ramSize];
std::memcpy(m_scanRAMCache, DolphinComm::DolphinAccessor::getRAMCache(), ramSize);
+ u32 beginA = m_searchInRangeBegin ? m_beginSearchRange : 0;
+ u32 endA = m_searchInRangeEnd ? m_endSearchRange : ramSize;
+
+ if (m_searchInRangeBegin || m_searchInRangeEnd)
+ {
+ ramSize = endA - beginA;
+ }
+
if (filter == ScanFiter::unknownInitial)
{
- int alignementDivision =
- m_enforceMemAlignement ? Common::getNbrBytesAlignementForType(m_memType) : 1;
- m_resultCount = ((ramSize / alignementDivision) -
- Common::getSizeForType(m_memType, static_cast(1)));
- m_wasUnknownInitialValue = true;
- m_memSize = 1;
- m_scanStarted = true;
+ if (m_searchInRangeBegin || m_searchInRangeEnd)
+ {
+ int alignementDivision =
+ m_enforceMemAlignement ? Common::getNbrBytesAlignementForType(m_memType) : 1;
+ m_wasUnknownInitialValue = false;
+ m_memSize = Common::getSizeForType(m_memType, static_cast(1));
+ m_scanStarted = true;
+
+ bool aram = DolphinComm::DolphinAccessor::isARAMAccessible();
+
+ u32 alignedBeginA =
+ beginA + ((alignementDivision - (beginA % alignementDivision)) % alignementDivision);
+
+ for (u32 i = alignedBeginA; i < endA - m_memSize; i += alignementDivision)
+ {
+ m_resultsConsoleAddr.push_back(Common::offsetToDolphinAddr(i, aram));
+ }
+
+ m_resultCount = m_resultsConsoleAddr.size();
+ }
+ else
+ {
+ int alignementDivision =
+ m_enforceMemAlignement ? Common::getNbrBytesAlignementForType(m_memType) : 1;
+ m_resultCount = ((ramSize / alignementDivision) -
+ Common::getSizeForType(m_memType, static_cast(1)));
+ m_wasUnknownInitialValue = true;
+ m_memSize = 1;
+ m_scanStarted = true;
+ }
+
return Common::MemOperationReturnCode::OK;
}
@@ -88,7 +120,11 @@ Common::MemOperationReturnCode MemScanner::firstScan(const MemScanner::ScanFiter
std::memset(noOffset, 0, m_memSize);
int increment = m_enforceMemAlignement ? Common::getNbrBytesAlignementForType(m_memType) : 1;
- for (u32 i = 0; i < (ramSize - m_memSize); i += increment)
+
+ u32 beginSearch = beginA;
+ u32 endSearch = endA - static_cast(m_memSize);
+
+ for (u32 i = beginSearch; i < endSearch; i += increment)
{
char* memoryCandidate = &m_scanRAMCache[i];
bool isResult = false;
@@ -153,7 +189,7 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter
{
return Common::MemOperationReturnCode::operationFailed;
}
- u32 ramSize = DolphinComm::DolphinAccessor::getRAMCacheSize();
+ u32 ramSize = static_cast(DolphinComm::DolphinAccessor::getRAMCacheSize());
newerRAMCache = new char[ramSize];
std::memcpy(newerRAMCache, DolphinComm::DolphinAccessor::getRAMCache(), ramSize);
@@ -202,13 +238,15 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter
std::vector newerResults = std::vector();
bool aramAccessible = DolphinComm::DolphinAccessor::isARAMAccessible();
+
+ bool wasUninitialized = m_wasUnknownInitialValue;
if (m_wasUnknownInitialValue)
{
m_wasUnknownInitialValue = false;
int increment = m_enforceMemAlignement ? Common::getNbrBytesAlignementForType(m_memType) : 1;
- for (u32 i = 0; i < (ramSize - m_memSize); i += increment)
+ for (u32 i = 0; i < ramSize - static_cast(m_memSize); i += increment)
{
if (isHitNextScan(filter, memoryToCompare1, memoryToCompare2, noOffset, newerRAMCache,
m_memSize, i))
@@ -233,8 +271,13 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter
}
delete[] noOffset;
+
+ m_UndoStack.push({m_resultsConsoleAddr, wasUninitialized});
+ m_undoCount = m_UndoStack.size();
+
m_resultsConsoleAddr.clear();
std::swap(m_resultsConsoleAddr, newerResults);
+
delete[] m_scanRAMCache;
m_scanRAMCache = nullptr;
m_scanRAMCache = newerRAMCache;
@@ -250,6 +293,11 @@ void MemScanner::reset()
m_scanRAMCache = nullptr;
m_resultCount = 0;
m_scanStarted = false;
+ while (!m_UndoStack.empty())
+ {
+ m_UndoStack.pop();
+ }
+ m_undoCount = 0;
}
inline bool MemScanner::isHitNextScan(const MemScanner::ScanFiter filter,
@@ -377,6 +425,59 @@ void MemScanner::setIsSigned(const bool isSigned)
m_memIsSigned = isSigned;
}
+void MemScanner::resetSearchRange()
+{
+ m_searchInRangeBegin = false;
+ m_searchInRangeEnd = false;
+ m_beginSearchRange = 0;
+ m_endSearchRange = 0;
+}
+
+bool MemScanner::setSearchRange(u32 beginRange, u32 endRange)
+{
+ if (!DolphinComm::DolphinAccessor::isValidConsoleAddress(beginRange) ||
+ !DolphinComm::DolphinAccessor::isValidConsoleAddress(endRange))
+ {
+ return false;
+ }
+
+ m_searchInRangeBegin = true;
+ m_searchInRangeEnd = true;
+ bool aram = DolphinComm::DolphinAccessor::isARAMAccessible();
+ m_beginSearchRange = Common::offsetToCacheIndex(Common::dolphinAddrToOffset(beginRange, aram), aram);
+ m_endSearchRange = Common::offsetToCacheIndex(Common::dolphinAddrToOffset(endRange, aram), aram);
+
+ return true;
+}
+
+bool MemScanner::setSearchRangeBegin(u32 beginRange)
+{
+ if (!DolphinComm::DolphinAccessor::isValidConsoleAddress(beginRange))
+ {
+ return false;
+ }
+
+ m_searchInRangeBegin = true;
+ bool aram = DolphinComm::DolphinAccessor::isARAMAccessible();
+ m_beginSearchRange = Common::offsetToCacheIndex(Common::dolphinAddrToOffset(beginRange, aram), aram);
+
+ return true;
+}
+
+bool MemScanner::setSearchRangeEnd(u32 endRange)
+{
+ if (!DolphinComm::DolphinAccessor::isValidConsoleAddress(endRange))
+ {
+ return false;
+ }
+
+ m_searchInRangeEnd = true;
+ bool aram = DolphinComm::DolphinAccessor::isARAMAccessible();
+ m_endSearchRange = Common::offsetToCacheIndex(Common::dolphinAddrToOffset(endRange, aram), aram) + 1;
+
+ return true;
+}
+
int MemScanner::getTermsNumForFilter(const MemScanner::ScanFiter filter) const
{
if (filter == MemScanner::ScanFiter::between)
@@ -452,11 +553,48 @@ std::string MemScanner::addSpacesToBytesArrays(const std::string& bytesArray) co
return result;
}
+bool MemScanner::undoScan()
+{
+ if (m_undoCount > 0)
+ {
+ m_resultsConsoleAddr = m_UndoStack.top().data;
+ m_resultCount = m_resultsConsoleAddr.size();
+ bool wasUninitialzed = m_UndoStack.top().wasUnknownInitialState;
+
+ m_UndoStack.pop();
+ m_undoCount = m_UndoStack.size();
+
+ if (wasUninitialzed)
+ {
+ u32 ramSize = static_cast(DolphinComm::DolphinAccessor::getRAMCacheSize());
+ int alignementDivision =
+ m_enforceMemAlignement ? Common::getNbrBytesAlignementForType(m_memType) : 1;
+ m_resultCount = ((ramSize / alignementDivision) -
+ Common::getSizeForType(m_memType, static_cast(1)));
+ m_wasUnknownInitialValue = true;
+ m_memSize = 1;
+ }
+
+ return true;
+ }
+ return false;
+}
+
size_t MemScanner::getResultCount() const
{
return m_resultCount;
}
+bool MemScanner::hasUndo() const
+{
+ return m_undoCount > 0;
+}
+
+size_t MemScanner::getUndoCount() const
+{
+ return m_undoCount;
+}
+
bool MemScanner::hasScanStarted() const
{
return m_scanStarted;
diff --git a/Source/MemoryScanner/MemoryScanner.h b/Source/MemoryScanner/MemoryScanner.h
index 79b0552b..a1be5cad 100644
--- a/Source/MemoryScanner/MemoryScanner.h
+++ b/Source/MemoryScanner/MemoryScanner.h
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include "../Common/CommonTypes.h"
#include "../Common/CommonUtils.h"
@@ -40,6 +41,7 @@ class MemScanner
const std::string& searchTerm2);
Common::MemOperationReturnCode nextScan(const ScanFiter filter, const std::string& searchTerm1,
const std::string& searchTerm2);
+ bool undoScan();
void reset();
inline CompareResult compareMemoryAsNumbers(const char* first, const char* second,
const char* offset, bool offsetInvert,
@@ -126,9 +128,15 @@ class MemScanner
void setBase(const Common::MemBase base);
void setEnforceMemAlignement(const bool enforceAlignement);
void setIsSigned(const bool isSigned);
+ void resetSearchRange();
+ bool setSearchRangeBegin(u32 beginIndex);
+ bool setSearchRangeEnd(u32 endIndex);
+ bool setSearchRange(u32 beginIndex, u32 endIndex);
std::vector getResultsConsoleAddr() const;
size_t getResultCount() const;
+ bool hasUndo() const;
+ size_t getUndoCount() const;
int getTermsNumForFilter(const ScanFiter filter) const;
Common::MemType getType() const;
Common::MemBase getBase() const;
@@ -148,14 +156,28 @@ class MemScanner
const u32 consoleOffset) const;
std::string addSpacesToBytesArrays(const std::string& bytesArray) const;
+ bool m_searchInRangeBegin = false;
+ bool m_searchInRangeEnd = false;
+ u32 m_beginSearchRange = 0;
+ u32 m_endSearchRange = 0;
+
Common::MemType m_memType = Common::MemType::type_byte;
Common::MemBase m_memBase = Common::MemBase::base_decimal;
size_t m_memSize;
bool m_enforceMemAlignement = true;
bool m_memIsSigned = false;
- std::vector m_resultsConsoleAddr;
- bool m_wasUnknownInitialValue = false;
+
size_t m_resultCount = 0;
+ size_t m_undoCount = 0;
+ bool m_wasUnknownInitialValue = false;
char* m_scanRAMCache = nullptr;
bool m_scanStarted = false;
+
+ struct MemScannerUndoAction
+ {
+ std::vector data;
+ bool wasUnknownInitialState = false;
+ };
+ std::stack m_UndoStack;
+ std::vector m_resultsConsoleAddr;
};