From c3060477f90f601cc9c0ff5c3c7cf3328db0fdc5 Mon Sep 17 00:00:00 2001 From: roeming Date: Mon, 20 Jun 2022 21:33:17 -0400 Subject: [PATCH 1/5] Added a dialog for copying memory from RAM and formatting it into byte strings --- Source/Dolphin-memory-engine.vcxproj | 1 + Source/GUI/MainWindow.cpp | 11 + Source/GUI/MainWindow.h | 2 + Source/GUI/MemCopy/DlgCopy.cpp | 305 +++++++++++++++++++++++++++ Source/GUI/MemCopy/DlgCopy.h | 45 ++++ 5 files changed, 364 insertions(+) create mode 100644 Source/GUI/MemCopy/DlgCopy.cpp create mode 100644 Source/GUI/MemCopy/DlgCopy.h 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..7d0048e7 --- /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; +}; From c979676dbe60b6c3d1ec01acf1b0e2a434d30522 Mon Sep 17 00:00:00 2001 From: roeming Date: Tue, 21 Jun 2022 10:26:56 -0400 Subject: [PATCH 2/5] Added undo button to memory scanner --- Source/GUI/MemScanner/MemScanWidget.cpp | 35 +++++++++++++++++++++++++ Source/GUI/MemScanner/MemScanWidget.h | 2 ++ Source/MemoryScanner/MemoryScanner.cpp | 28 ++++++++++++++++++++ Source/MemoryScanner/MemoryScanner.h | 7 ++++- 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Source/GUI/MemScanner/MemScanWidget.cpp b/Source/GUI/MemScanner/MemScanWidget.cpp index 4c4ba3e5..14914773 100644 --- a/Source/GUI/MemScanner/MemScanWidget.cpp +++ b/Source/GUI/MemScanner/MemScanWidget.cpp @@ -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, [=] { @@ -134,6 +137,7 @@ void MemScanWidget::makeLayouts() 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(); @@ -318,6 +322,7 @@ void MemScanWidget::onFirstScan() m_btnFirstScan->hide(); m_btnNextScan->show(); m_btnResetScan->show(); + m_btnUndoScan->show(); m_cmbScanType->setDisabled(true); m_chkSignedScan->setDisabled(true); m_chkEnforceMemAlignement->setDisabled(true); @@ -349,6 +354,35 @@ void MemScanWidget::onNextScan() } } +void MemScanWidget::onUndoScan() +{ + if (m_memScanner->getUndoCount() > 0) + { + 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(); + } + } + else + { + onResetScan(); + } +} + void MemScanWidget::onResetScan() { m_memScanner->reset(); @@ -359,6 +393,7 @@ void MemScanWidget::onResetScan() m_btnFirstScan->show(); m_btnNextScan->hide(); m_btnResetScan->hide(); + m_btnUndoScan->hide(); 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..18296cad 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(); @@ -61,6 +62,7 @@ class MemScanWidget : public QWidget 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..93273fc1 100644 --- a/Source/MemoryScanner/MemoryScanner.cpp +++ b/Source/MemoryScanner/MemoryScanner.cpp @@ -233,8 +233,12 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter } delete[] noOffset; + m_UndoStack.push(m_resultsConsoleAddr); + m_undoCount = m_UndoStack.size(); + m_resultsConsoleAddr.clear(); std::swap(m_resultsConsoleAddr, newerResults); + delete[] m_scanRAMCache; m_scanRAMCache = nullptr; m_scanRAMCache = newerRAMCache; @@ -250,6 +254,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, @@ -452,11 +461,30 @@ std::string MemScanner::addSpacesToBytesArrays(const std::string& bytesArray) co return result; } +bool MemScanner::undoScan() +{ + if (m_undoCount > 0) + { + m_resultsConsoleAddr = m_UndoStack.top(); + m_resultCount = m_resultsConsoleAddr.size(); + + m_UndoStack.pop(); + m_undoCount = m_UndoStack.size(); + return true; + } + return false; +} + size_t MemScanner::getResultCount() const { return m_resultCount; } +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..3e66aa4d 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, @@ -129,6 +131,7 @@ class MemScanner std::vector getResultsConsoleAddr() const; size_t getResultCount() const; + size_t getUndoCount() const; int getTermsNumForFilter(const ScanFiter filter) const; Common::MemType getType() const; Common::MemBase getBase() const; @@ -154,8 +157,10 @@ class MemScanner bool m_enforceMemAlignement = true; bool m_memIsSigned = false; std::vector m_resultsConsoleAddr; - bool m_wasUnknownInitialValue = false; size_t m_resultCount = 0; + std::stack> m_UndoStack; + size_t m_undoCount = 0; + bool m_wasUnknownInitialValue = false; char* m_scanRAMCache = nullptr; bool m_scanStarted = false; }; From 80fadd90e307356d8c49bde248e8b18ba644d6ed Mon Sep 17 00:00:00 2001 From: roeming Date: Tue, 21 Jun 2022 15:32:26 -0400 Subject: [PATCH 3/5] Added a range scanner, fix for undo bug --- Source/GUI/MemScanner/MemScanWidget.cpp | 105 +++++++++++++++++- Source/GUI/MemScanner/MemScanWidget.h | 2 + Source/MemoryScanner/MemoryScanner.cpp | 138 +++++++++++++++++++++--- Source/MemoryScanner/MemoryScanner.h | 21 +++- 4 files changed, 248 insertions(+), 18 deletions(-) diff --git a/Source/GUI/MemScanner/MemScanWidget.cpp b/Source/GUI/MemScanner/MemScanWidget.cpp index 14914773..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() @@ -79,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(); @@ -134,6 +144,10 @@ 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); @@ -163,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); @@ -297,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()); @@ -323,6 +415,9 @@ void MemScanWidget::onFirstScan() 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); @@ -351,12 +446,14 @@ void MemScanWidget::onNextScan() m_btnAddSelection->setEnabled(true); m_btnRemoveSelection->setEnabled(true); } + + m_btnUndoScan->setEnabled(m_memScanner->hasUndo()); } } void MemScanWidget::onUndoScan() { - if (m_memScanner->getUndoCount() > 0) + if (m_memScanner->hasUndo()) { m_memScanner->undoScan(); @@ -376,6 +473,8 @@ void MemScanWidget::onUndoScan() m_btnRemoveSelection->setEnabled(false); m_resultsListModel->updateAfterScannerReset(); } + + m_btnUndoScan->setEnabled(m_memScanner->hasUndo()); } else { @@ -394,6 +493,8 @@ void MemScanWidget::onResetScan() 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 18296cad..ed0f70c5 100644 --- a/Source/GUI/MemScanner/MemScanWidget.h +++ b/Source/GUI/MemScanner/MemScanWidget.h @@ -59,6 +59,8 @@ 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; diff --git a/Source/MemoryScanner/MemoryScanner.cpp b/Source/MemoryScanner/MemoryScanner.cpp index 93273fc1..3ac3b201 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); @@ -208,7 +244,7 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter 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,7 +269,10 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter } delete[] noOffset; - m_UndoStack.push(m_resultsConsoleAddr); + + bool wasUninitialized = m_resultsConsoleAddr.size() < newerResults.size(); + + m_UndoStack.push({m_resultsConsoleAddr, wasUninitialized}); m_undoCount = m_UndoStack.size(); m_resultsConsoleAddr.clear(); @@ -386,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) @@ -465,11 +557,24 @@ bool MemScanner::undoScan() { if (m_undoCount > 0) { - m_resultsConsoleAddr = m_UndoStack.top(); + 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; @@ -480,6 +585,11 @@ size_t MemScanner::getResultCount() const return m_resultCount; } +bool MemScanner::hasUndo() const +{ + return m_undoCount > 0; +} + size_t MemScanner::getUndoCount() const { return m_undoCount; diff --git a/Source/MemoryScanner/MemoryScanner.h b/Source/MemoryScanner/MemoryScanner.h index 3e66aa4d..a1be5cad 100644 --- a/Source/MemoryScanner/MemoryScanner.h +++ b/Source/MemoryScanner/MemoryScanner.h @@ -128,9 +128,14 @@ 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; @@ -151,16 +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; + size_t m_resultCount = 0; - std::stack> m_UndoStack; 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; }; From bd1f90252c2429d2e6cf87100794977ef21089dc Mon Sep 17 00:00:00 2001 From: roeming Date: Thu, 23 Jun 2022 10:58:35 -0400 Subject: [PATCH 4/5] fix for uninitialized bug --- Source/MemoryScanner/MemoryScanner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/MemoryScanner/MemoryScanner.cpp b/Source/MemoryScanner/MemoryScanner.cpp index 3ac3b201..3e2c6cfd 100644 --- a/Source/MemoryScanner/MemoryScanner.cpp +++ b/Source/MemoryScanner/MemoryScanner.cpp @@ -238,6 +238,8 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter std::vector newerResults = std::vector(); bool aramAccessible = DolphinComm::DolphinAccessor::isARAMAccessible(); + + bool wasUninitialized = m_wasUnknownInitialValue; if (m_wasUnknownInitialValue) { @@ -270,8 +272,6 @@ Common::MemOperationReturnCode MemScanner::nextScan(const MemScanner::ScanFiter delete[] noOffset; - bool wasUninitialized = m_resultsConsoleAddr.size() < newerResults.size(); - m_UndoStack.push({m_resultsConsoleAddr, wasUninitialized}); m_undoCount = m_UndoStack.size(); From a62feb5bd7c0379027fd4497ff71e4ec0ef13652 Mon Sep 17 00:00:00 2001 From: CNace13 Date: Sun, 16 Jul 2023 17:31:16 -0400 Subject: [PATCH 5/5] Compiles on Linux --- Source/CMakeLists.txt | 1 + Source/GUI/MemCopy/DlgCopy.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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/GUI/MemCopy/DlgCopy.h b/Source/GUI/MemCopy/DlgCopy.h index 7d0048e7..b4797abe 100644 --- a/Source/GUI/MemCopy/DlgCopy.h +++ b/Source/GUI/MemCopy/DlgCopy.h @@ -4,7 +4,7 @@ #include #include #include -#include "..\GUICommon.h" +#include "../GUICommon.h" #include #include