diff options
-rw-r--r-- | src/frontend/qt_sdl/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/frontend/qt_sdl/RAMInfoDialog.cpp | 302 | ||||
-rw-r--r-- | src/frontend/qt_sdl/RAMInfoDialog.h | 161 | ||||
-rw-r--r-- | src/frontend/qt_sdl/RAMInfoDialog.ui | 237 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 11 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.h | 2 |
6 files changed, 714 insertions, 0 deletions
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 4b5cb12..8b29bae 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -16,6 +16,7 @@ SET(SOURCES_QT_SDL WifiSettingsDialog.cpp InterfaceSettingsDialog.cpp ROMInfoDialog.cpp + RAMInfoDialog.cpp TitleManagerDialog.cpp Input.cpp LAN_PCap.cpp diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp new file mode 100644 index 0000000..b13ff02 --- /dev/null +++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp @@ -0,0 +1,302 @@ +/* + Copyright 2016-2021 Arisotura + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "RAMInfoDialog.h" +#include "ui_RAMInfoDialog.h" + +#include "main.h" + +extern EmuThread* emuThread; + +s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType) +{ + switch (byteType) + { + case ramInfo_OneByte: + return *(s8*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + case ramInfo_TwoBytes: + return *(s16*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + case ramInfo_FourBytes: + return *(s32*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + default: + return 0; + } +} + +RAMInfoDialog* RAMInfoDialog::currentDlg = nullptr; + +RAMInfoDialog::RAMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::RAMInfoDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + qRegisterMetaType<QVector<int>>("QVector<int>"); + qRegisterMetaType<u32>("u32"); + qRegisterMetaType<s32>("s32"); + qRegisterMetaType<s16>("s16"); + qRegisterMetaType<s8>("s8"); + + SearchThread = new RAMSearchThread(this); + connect(SearchThread, &RAMSearchThread::SetProgressbarValue, this, &RAMInfoDialog::SetProgressbarValue); + connect(SearchThread, &RAMSearchThread::finished, this, &RAMInfoDialog::OnSearchFinished); + // First search (Show everything in main ram) + SearchThread->Start(ramInfoSTh_SearchAll); + + TableUpdater = new QTimer(this); + TableUpdater->setInterval(100); + connect(TableUpdater, &QTimer::timeout, this, &RAMInfoDialog::ShowRowsInTable); + TableUpdater->start(); +} + +RAMInfoDialog::~RAMInfoDialog() +{ + delete SearchThread; + if (TableUpdater->isActive()) + TableUpdater->stop(); + delete TableUpdater; + delete ui; +} + +void RAMInfoDialog::OnSearchFinished() +{ + SearchThread->wait(); + ui->btnSearch->setEnabled(true); + ui->ramTable->clearContents(); + ui->ramTable->setRowCount(SearchThread->GetResults()->size()); + ui->ramTable->verticalScrollBar()->setSliderPosition(0); + ui->txtFound->setText(QString("Found: %1").arg(SearchThread->GetResults()->size())); +} + +void RAMInfoDialog::ShowRowsInTable() +{ + const u32& scrollValue = ui->ramTable->verticalScrollBar()->sliderPosition(); + std::vector<ramInfo_RowData>* RowDataVector = SearchThread->GetResults(); + + for (u32 row = scrollValue; row < std::min<u32>(scrollValue+25, RowDataVector->size()); row++) + { + ramInfo_RowData& rowData = RowDataVector->at(row); + rowData.Update(SearchThread->GetSearchByteType()); + + if (ui->ramTable->item(row, ramInfo_Address) == nullptr) + { + // A new row + QTableWidgetItem* addressItem = new QTableWidgetItem(QString("%1").arg(rowData.Address, 8, 16)); + QTableWidgetItem* valueItem = new QTableWidgetItem(QString("%1").arg(rowData.Value)); + QTableWidgetItem* previousItem = new QTableWidgetItem(QString("%1").arg(rowData.Previous)); + + addressItem->setFlags(addressItem->flags() & ~Qt::ItemIsEditable); + valueItem->setFlags(valueItem->flags() | Qt::ItemIsEditable); + previousItem->setFlags(previousItem->flags() & ~Qt::ItemIsEditable); + + ui->ramTable->setItem(row, ramInfo_Address, addressItem); + ui->ramTable->setItem(row, ramInfo_Value, valueItem); + ui->ramTable->setItem(row, ramInfo_Previous, previousItem); + } + else + { + // A row that exists + ui->ramTable->item(row, ramInfo_Address)->setText(QString("%1").arg(rowData.Address, 8, 16)); + ui->ramTable->item(row, ramInfo_Value)->setText(QString("%1").arg(rowData.Value)); + ui->ramTable->item(row, ramInfo_Previous)->setText(QString("%1").arg(rowData.Previous)); + if (rowData.Value != rowData.Previous) + ui->ramTable->item(row, ramInfo_Previous)->setForeground(Qt::red); + } + } +} + +void RAMInfoDialog::ClearTableContents() +{ + ui->ramTable->clearContents(); + ui->ramTable->setRowCount(0); +} + +void RAMInfoDialog::SetProgressbarValue(const u32& value) +{ + ui->progressBar->setValue(value); +} + +void RAMInfoDialog::done(int r) +{ + QDialog::done(r); + closeDlg(); +} + +void RAMInfoDialog::on_btnSearch_clicked() +{ + ui->btnSearch->setEnabled(false); + ui->radiobtn1byte->setEnabled(false); + ui->radiobtn2bytes->setEnabled(false); + ui->radiobtn4bytes->setEnabled(false); + + if (ui->txtSearch->text().isEmpty()) + SearchThread->Start(ramInfoSTh_SearchAll); + else + SearchThread->Start(ui->txtSearch->text().toInt()); + + if (!TableUpdater->isActive()) + TableUpdater->start(); +} + +void RAMInfoDialog::on_btnClear_clicked() +{ + SearchThread->Stop(); + TableUpdater->stop(); + + ui->radiobtn1byte->setEnabled(true); + ui->radiobtn2bytes->setEnabled(true); + ui->radiobtn4bytes->setEnabled(true); + + OnSearchFinished(); +} + +void RAMInfoDialog::on_radiobtn1byte_clicked() +{ + SearchThread->SetSearchByteType(ramInfo_OneByte); +} + +void RAMInfoDialog::on_radiobtn2bytes_clicked() +{ + SearchThread->SetSearchByteType(ramInfo_TwoBytes); +} + +void RAMInfoDialog::on_radiobtn4bytes_clicked() +{ + SearchThread->SetSearchByteType(ramInfo_FourBytes); +} + +void RAMInfoDialog::on_ramTable_itemChanged(QTableWidgetItem *item) +{ + ramInfo_RowData& rowData = SearchThread->GetResults()->at(item->row()); + s32 itemValue = item->text().toInt(); + + if (rowData.Value != itemValue) + rowData.SetValue(itemValue); +} + +/** + * RAMSearchThread + */ + +RAMSearchThread::RAMSearchThread(RAMInfoDialog* dialog) : Dialog(dialog) +{ + RowDataVector = new std::vector<ramInfo_RowData>(); +} + +RAMSearchThread::~RAMSearchThread() +{ + Stop(); + if (RowDataVector) + { + delete RowDataVector; + RowDataVector = nullptr; + } +} + +void RAMSearchThread::Start(const s32& searchValue, const ramInfoSTh_SearchMode& searchMode) +{ + SearchValue = searchValue; + SearchMode = searchMode; + start(); +} + +void RAMSearchThread::Start(const ramInfoSTh_SearchMode& searchMode) +{ + SearchMode = searchMode; + start(); +} + +void RAMSearchThread::Stop() +{ + SearchRunning = false; + RowDataVector->clear(); + quit(); + wait(); +} + +void RAMSearchThread::run() +{ + SearchRunning = true; + u32 progress = 0; + + // Pause game running + emuThread->emuPause(); + + // For following search modes below, RowDataVector must be filled. + if (SearchMode == ramInfoSTh_SearchAll || RowDataVector->size() == 0) + { + // First search mode + for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+NDS::MainRAMMaxSize; addr += SearchByteType) + { + const s32& value = GetMainRAMValue(addr, SearchByteType); + + RowDataVector->push_back({ addr, value, value }); + + // A solution to prevent to call too many slot. + u32 newProgress = (int)((addr-0x02000000) / (NDS::MainRAMMaxSize-1.0f) * 100); + if (progress < newProgress) + { + progress = newProgress; + emit SetProgressbarValue(progress); + } + } + } + + if (SearchMode == ramInfoSTh_Default) + { + // Next search mode + std::vector<ramInfo_RowData>* newRowDataVector = new std::vector<ramInfo_RowData>(); + for (u32 row = 0; SearchRunning && row < RowDataVector->size(); row++) + { + const u32& addr = RowDataVector->at(row).Address; + const s32& value = GetMainRAMValue(addr, SearchByteType); + + if (SearchValue == value) + newRowDataVector->push_back({ addr, value, value }); + + // A solution to prevent to call too many slot. + u32 newProgress = (int)(row / (RowDataVector->size()-1.0f) * 100); + if (progress < newProgress) + { + progress = newProgress; + emit SetProgressbarValue(progress); + } + } + delete RowDataVector; + RowDataVector = newRowDataVector; + } + + // Unpause game running + emuThread->emuUnpause(); + + SearchRunning = false; +} + +void RAMSearchThread::SetSearchByteType(const ramInfo_ByteType& bytetype) +{ + SearchByteType = bytetype; +} + +ramInfo_ByteType RAMSearchThread::GetSearchByteType() const +{ + return SearchByteType; +} + +std::vector<ramInfo_RowData>* RAMSearchThread::GetResults() +{ + return RowDataVector; +} diff --git a/src/frontend/qt_sdl/RAMInfoDialog.h b/src/frontend/qt_sdl/RAMInfoDialog.h new file mode 100644 index 0000000..f44ae93 --- /dev/null +++ b/src/frontend/qt_sdl/RAMInfoDialog.h @@ -0,0 +1,161 @@ +/* + Copyright 2016-2021 Arisotura + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef RAMINFODIALOG_H +#define RAMINFODIALOG_H + +#include <QDialog> +#include <QTableWidget> +#include <QScrollBar> +#include <QThread> +#include <QTimer> + +#include "types.h" +#include "NDS.h" + +namespace Ui { class RAMInfoDialog; } +class RAMInfoDialog; +class RAMSearchThread; +class RAMUpdateThread; + +enum ramInfo_ByteType +{ + ramInfo_OneByte = 1, + ramInfo_TwoBytes = 2, + ramInfo_FourBytes = 4 +}; + +enum ramInfoSTh_SearchMode +{ + ramInfoSTh_Default, + ramInfoSTh_SearchAll +}; + +enum +{ + ramInfo_Address, + ramInfo_Value, + ramInfo_Previous +}; + +s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType); + +struct ramInfo_RowData +{ + u32 Address; + s32 Value; + s32 Previous; + + void Update(const ramInfo_ByteType& byteType) + { + Value = GetMainRAMValue(Address, byteType); + } + + void SetValue(const s32& value) + { + NDS::MainRAM[Address&NDS::MainRAMMask] = (u32)value; + Value = value; + } +}; + +class RAMInfoDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RAMInfoDialog(QWidget* parent); + ~RAMInfoDialog(); + + static RAMInfoDialog* currentDlg; + static RAMInfoDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new RAMInfoDialog(parent); + currentDlg->show(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + + s32 SearchValue = 0; + + void ClearTableContents(); + +private slots: + void done(int r); + + void on_btnSearch_clicked(); + void on_btnClear_clicked(); + void on_radiobtn1byte_clicked(); + void on_radiobtn2bytes_clicked(); + void on_radiobtn4bytes_clicked(); + void on_ramTable_itemChanged(QTableWidgetItem *item); + + void OnSearchFinished(); + void ShowRowsInTable(); + void SetProgressbarValue(const u32& value); + +private: + Ui::RAMInfoDialog* ui; + + RAMSearchThread* SearchThread; + QTimer* TableUpdater; +}; + +class RAMSearchThread : public QThread +{ + Q_OBJECT + +public: + explicit RAMSearchThread(RAMInfoDialog* dialog); + ~RAMSearchThread() override; + + void Start(const s32& searchValue, const ramInfoSTh_SearchMode& searchMode = ramInfoSTh_Default); + void Start(const ramInfoSTh_SearchMode& searchMode); + + void SetSearchByteType(const ramInfo_ByteType& bytetype); + ramInfo_ByteType GetSearchByteType() const; + std::vector<ramInfo_RowData>* GetResults(); + + void Stop(); + +private: + void run(); + + RAMInfoDialog* Dialog; + bool SearchRunning = false; + + ramInfoSTh_SearchMode SearchMode; + s32 SearchValue; + ramInfo_ByteType SearchByteType = ramInfo_OneByte; + std::vector<ramInfo_RowData>* RowDataVector = nullptr; + + void ClearTableContents(); + +signals: + void SetProgressbarValue(const u32& value); +}; + +#endif // RAMINFODIALOG_H diff --git a/src/frontend/qt_sdl/RAMInfoDialog.ui b/src/frontend/qt_sdl/RAMInfoDialog.ui new file mode 100644 index 0000000..46beaa5 --- /dev/null +++ b/src/frontend/qt_sdl/RAMInfoDialog.ui @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RAMInfoDialog</class> + <widget class="QDialog" name="RAMInfoDialog"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>550</width> + <height>411</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>550</width> + <height>411</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>550</width> + <height>411</height> + </size> + </property> + <property name="windowTitle"> + <string>RAM info - melonDS</string> + </property> + <property name="modal"> + <bool>false</bool> + </property> + <widget class="QGroupBox" name="groupBox"> + <property name="geometry"> + <rect> + <x>340</x> + <y>10</y> + <width>201</width> + <height>111</height> + </rect> + </property> + <property name="title"> + <string>Search</string> + </property> + <widget class="QPushButton" name="btnSearch"> + <property name="geometry"> + <rect> + <x>130</x> + <y>20</y> + <width>61</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Search</string> + </property> + </widget> + <widget class="QLineEdit" name="txtSearch"> + <property name="geometry"> + <rect> + <x>50</x> + <y>20</y> + <width>71</width> + <height>21</height> + </rect> + </property> + <property name="maxLength"> + <number>5</number> + </property> + </widget> + <widget class="QPushButton" name="btnClear"> + <property name="geometry"> + <rect> + <x>120</x> + <y>80</y> + <width>71</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Clear</string> + </property> + </widget> + <widget class="QLabel" name="labelValue"> + <property name="geometry"> + <rect> + <x>10</x> + <y>20</y> + <width>41</width> + <height>21</height> + </rect> + </property> + <property name="text"> + <string>Value:</string> + </property> + </widget> + <widget class="QRadioButton" name="radiobtn1byte"> + <property name="geometry"> + <rect> + <x>10</x> + <y>50</y> + <width>90</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>1byte</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" name="radiobtn2bytes"> + <property name="geometry"> + <rect> + <x>10</x> + <y>70</y> + <width>90</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>2bytes</string> + </property> + </widget> + <widget class="QRadioButton" name="radiobtn4bytes"> + <property name="geometry"> + <rect> + <x>10</x> + <y>90</y> + <width>90</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>4bytes</string> + </property> + </widget> + </widget> + <widget class="QProgressBar" name="progressBar"> + <property name="geometry"> + <rect> + <x>10</x> + <y>380</y> + <width>321</width> + <height>23</height> + </rect> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>0</number> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="textVisible"> + <bool>true</bool> + </property> + <property name="format"> + <string>%p%</string> + </property> + </widget> + <widget class="QTableWidget" name="ramTable"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>321</width> + <height>341</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>321</width> + <height>341</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>321</width> + <height>341</height> + </size> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked</set> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + <attribute name="verticalHeaderMinimumSectionSize"> + <number>16</number> + </attribute> + <attribute name="verticalHeaderDefaultSectionSize"> + <number>16</number> + </attribute> + <column> + <property name="text"> + <string>Address</string> + </property> + </column> + <column> + <property name="text"> + <string>Value</string> + </property> + </column> + <column> + <property name="text"> + <string>Previous</string> + </property> + </column> + </widget> + <widget class="QLabel" name="txtFound"> + <property name="geometry"> + <rect> + <x>10</x> + <y>10</y> + <width>101</width> + <height>16</height> + </rect> + </property> + <property name="text"> + <string>Found:</string> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 535bfee..7454ffc 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -60,6 +60,7 @@ #include "WifiSettingsDialog.h" #include "InterfaceSettingsDialog.h" #include "ROMInfoDialog.h" +#include "RAMInfoDialog.h" #include "TitleManagerDialog.h" #include "types.h" @@ -1440,6 +1441,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actROMInfo = menu->addAction("ROM info"); connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); + actRAMInfo = menu->addAction("RAM info"); + connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); + actTitleManager = menu->addAction("Manage DSi titles"); connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); } @@ -1671,6 +1675,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actEnableCheats->setChecked(Config::EnableCheats); actROMInfo->setEnabled(false); + actRAMInfo->setEnabled(false); actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); @@ -2082,6 +2087,7 @@ void MainWindow::updateCartInserted(bool gba) actImportSavefile->setEnabled(inserted); actSetupCheats->setEnabled(inserted); actROMInfo->setEnabled(inserted); + actRAMInfo->setEnabled(inserted); } } @@ -2557,6 +2563,11 @@ void MainWindow::onROMInfo() ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this); } +void MainWindow::onRAMInfo() +{ + RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this); +} + void MainWindow::onOpenTitleManager() { TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index d4e40d7..6929d69 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -252,6 +252,7 @@ private slots: void onSetupCheats(); void onCheatsDialogFinished(int res); void onROMInfo(); + void onRAMInfo(); void onOpenTitleManager(); void onOpenEmuSettings(); @@ -341,6 +342,7 @@ public: QAction* actEnableCheats; QAction* actSetupCheats; QAction* actROMInfo; + QAction* actRAMInfo; QAction* actTitleManager; QAction* actEmuSettings; |