Implement MainRAM management dialog😁 (#1248)

* Implement MainRAM management dialog

* Modify RAMInfoDialog

- use emuThread to avoid a race condition.
- replace RAMUpdateThread to QTimer

Co-Authored-By: RSDuck <RSDuck@users.noreply.github.com>

* Update src/frontend/qt_sdl/RAMInfoDialog.cpp

small typo

Co-authored-by: Rayyan Ansari <68647953+RayyanAnsari@users.noreply.github.com>

* Update src/frontend/qt_sdl/RAMInfoDialog.h

small typo

Co-authored-by: Rayyan Ansari <68647953+RayyanAnsari@users.noreply.github.com>

* typo errors in RAMInfoDialog

Rrevious->Previous

* add new line to the end of the file

Co-authored-by: Rayyan Ansari <rayyan@ansari.sh>

* enable raminfo when cart is inserted

* Modify that only the 'value' item can be edited in RAMinfoDialog

* fix: function name incorrect error

* fix: function name incorrect error2

* fix: wrong way to get ram value

Co-authored-by: RSDuck <RSDuck@users.noreply.github.com>
Co-authored-by: Rayyan Ansari <68647953+RayyanAnsari@users.noreply.github.com>
Co-authored-by: Rayyan Ansari <rayyan@ansari.sh>
This commit is contained in:
2jun0
2022-03-07 06:33:11 +09:00
committed by GitHub
parent 38c8b886ea
commit c66df57256
6 changed files with 714 additions and 0 deletions

View File

@ -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;
}