Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Source/Dolphin-memory-engine.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
<ClInclude Include="**\*.h" Exclude="GUI\**\*.h" />
<!-- These files don't need to be Moc'ed -->
<ClInclude Include="GUI\GUICommon.h" />
<ClInclude Include="GUI\MemCopy\DlgCopy.h" />
<ClInclude Include="GUI\MemWatcher\Dialogs\DlgAddWatchEntry.h" />
<ClInclude Include="GUI\MemWatcher\MemWatchDelegate.h" />
<ClInclude Include="GUI\MemWatcher\MemWatchTreeNode.h" />
Expand Down
11 changes: 11 additions & 0 deletions Source/GUI/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "../DolphinProcess/DolphinAccessor.h"
#include "../MemoryWatch/MemWatchEntry.h"
#include "MemCopy/DlgCopy.h"
#include "Settings/DlgSettings.h"
#include "Settings/SConfig.h"

Expand Down Expand Up @@ -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);
Expand All @@ -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,
[=]
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions Source/GUI/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class MainWindow : public QMainWindow
void onOpenSettings();
void onImportFromCT();
void onExportAsCSV();
void onCopyMemory();
void onAbout();
void onQuit();

Expand Down Expand Up @@ -73,4 +74,5 @@ class MainWindow : public QMainWindow
QAction* m_actSettings;
QAction* m_actQuit;
QAction* m_actAbout;
QAction* m_actCopyMemory;
};
305 changes: 305 additions & 0 deletions Source/GUI/MemCopy/DlgCopy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
#include "DlgCopy.h"

#include "../../DolphinProcess/DolphinAccessor.h"
#include <QAbstractButton>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QVBoxLayout>
#include <sstream>
#include <utility>
#include <qmessagebox.h>
#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<char> 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();
}
Loading