rpcs3/rpcs3/rpcs3qt/auto_pause_settings_dialog.cpp

289 lines
8.3 KiB
C++

#include "auto_pause_settings_dialog.h"
#include "table_item_delegate.h"
#include "Emu/System.h"
#include <QFontDatabase>
#include <QMenu>
#include <QMouseEvent>
#include <QVBoxLayout>
#include <QPushButton>
#include "util/logs.hpp"
#include "Utilities/File.h"
LOG_CHANNEL(autopause_log, "AutoPause");
constexpr auto qstr = QString::fromStdString;
auto_pause_settings_dialog::auto_pause_settings_dialog(QWidget *parent) : QDialog(parent)
{
QLabel *description = new QLabel(tr("To use auto pause: enter the ID(s) of a function or a system call.\nRestart of the game is required to apply. You can enable/disable this in the settings."), this);
m_pause_list = new QTableWidget(this);
m_pause_list->setColumnCount(2);
m_pause_list->setHorizontalHeaderLabels(QStringList() << tr("Call ID") << tr("Type"));
//m_pause_list->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
m_pause_list->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pause_list->setContextMenuPolicy(Qt::CustomContextMenu);
m_pause_list->setItemDelegate(new table_item_delegate(this));
m_pause_list->setShowGrid(false);
QPushButton *clearButton = new QPushButton(tr("Clear"), this);
QPushButton *reloadButton = new QPushButton(tr("Reload"), this);
QPushButton *saveButton = new QPushButton(tr("Save"), this);
QPushButton *cancelButton = new QPushButton(tr("Cancel"), this);
cancelButton->setDefault(true);
QHBoxLayout *buttonsLayout = new QHBoxLayout();
buttonsLayout->addWidget(clearButton);
buttonsLayout->addWidget(reloadButton);
buttonsLayout->addStretch();
buttonsLayout->addWidget(saveButton);
buttonsLayout->addWidget(cancelButton);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(description);
mainLayout->addWidget(m_pause_list);
mainLayout->addLayout(buttonsLayout);
setLayout(mainLayout);
setMinimumSize(QSize(400, 360));
setWindowTitle(tr("Auto Pause Manager"));
setObjectName("auto_pause_manager");
// Events
connect(m_pause_list, &QTableWidget::customContextMenuRequested, this, &auto_pause_settings_dialog::ShowContextMenu);
connect(clearButton, &QAbstractButton::clicked, [this](){ m_entries.clear(); UpdateList(); });
connect(reloadButton, &QAbstractButton::clicked, [this](){ LoadEntries(); UpdateList(); });
connect(saveButton, &QAbstractButton::clicked, [this]()
{
SaveEntries();
autopause_log.success("File pause.bin was updated.");
});
connect(cancelButton, &QAbstractButton::clicked, this, &QWidget::close);
Emu.GracefulShutdown(false);
LoadEntries();
UpdateList();
setFixedSize(sizeHint());
}
// Copied some from AutoPause.
void auto_pause_settings_dialog::LoadEntries()
{
m_entries.clear();
m_entries.reserve(16);
const fs::file list(fs::get_config_dir() + "pause.bin");
if (list)
{
// System calls ID and Function calls ID are all u32 iirc.
u32 num;
const usz fmax = list.size();
usz fcur = 0;
list.seek(0);
while (fcur <= fmax - sizeof(u32))
{
list.read(&num, sizeof(u32));
fcur += sizeof(u32);
if (num == 0xFFFFFFFF) break;
m_entries.emplace_back(num);
}
}
}
// Copied some from AutoPause.
// Tip: This one doesn't check for the file is being read or not.
// This would always use a 0xFFFFFFFF as end of the pause.bin
void auto_pause_settings_dialog::SaveEntries()
{
fs::file list(fs::get_config_dir() + "pause.bin", fs::rewrite);
//System calls ID and Function calls ID are all u32 iirc.
u32 num = 0;
list.seek(0);
for (usz i = 0; i < m_entries.size(); ++i)
{
if (num == 0xFFFFFFFF) continue;
num = m_entries[i];
list.write(&num, sizeof(u32));
}
num = 0xFFFFFFFF;
list.write(&num, sizeof(u32));
}
void auto_pause_settings_dialog::UpdateList()
{
const int entries_size = static_cast<int>(m_entries.size());
m_pause_list->clearContents();
m_pause_list->setRowCount(entries_size);
for (int i = 0; i < entries_size; ++i)
{
QTableWidgetItem* callItem = new QTableWidgetItem;
QTableWidgetItem* typeItem = new QTableWidgetItem;
callItem->setFlags(callItem->flags() & ~Qt::ItemIsEditable);
typeItem->setFlags(typeItem->flags() & ~Qt::ItemIsEditable);
if (m_entries[i] != 0xFFFFFFFF)
{
callItem->setData(Qt::DisplayRole, qstr(fmt::format("%08x", m_entries[i])));
}
else
{
callItem->setData(Qt::DisplayRole, tr("Unset"));
}
if (m_entries[i] < 1024)
{
typeItem->setData(Qt::DisplayRole, tr("System Call"));
}
else
{
typeItem->setData(Qt::DisplayRole, tr("Function Call"));
}
m_pause_list->setItem(i, 0, callItem);
m_pause_list->setItem(i, 1, typeItem);
}
}
void auto_pause_settings_dialog::ShowContextMenu(const QPoint &pos)
{
const int row = m_pause_list->indexAt(pos).row();
QMenu myMenu;
// Make Actions
QAction* add = myMenu.addAction(tr("&Add"));
QAction* remove = myMenu.addAction(tr("&Remove"));
myMenu.addSeparator();
QAction* config = myMenu.addAction(tr("&Config"));
if (row == -1)
{
remove->setEnabled(false);
config->setEnabled(false);
}
auto OnEntryConfig = [this](int row, bool newEntry)
{
AutoPauseConfigDialog *config = new AutoPauseConfigDialog(this, this, newEntry, &m_entries[row]);
config->setModal(true);
config->exec();
UpdateList();
};
connect(add, &QAction::triggered, this, [=, this]()
{
m_entries.emplace_back(0xFFFFFFFF);
UpdateList();
const int idx = static_cast<int>(m_entries.size()) - 1;
m_pause_list->selectRow(idx);
OnEntryConfig(idx, true);
});
connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove);
connect(config, &QAction::triggered, this, [=, this]() {OnEntryConfig(row, false); });
myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos));
}
void auto_pause_settings_dialog::OnRemove()
{
QModelIndexList selection = m_pause_list->selectionModel()->selectedRows();
std::sort(selection.begin(), selection.end());
for (int i = selection.count() - 1; i >= 0; i--)
{
m_entries.erase(m_entries.begin() + selection.at(i).row());
}
UpdateList();
}
void auto_pause_settings_dialog::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
{
return;
}
if (event->key() == Qt::Key_Delete)
{
OnRemove();
}
}
AutoPauseConfigDialog::AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32 *entry)
: QDialog(parent), m_presult(entry), m_newEntry(newEntry), m_apsd(apsd)
{
m_entry = *m_presult;
setMinimumSize(QSize(300, -1));
QPushButton* button_ok = new QPushButton(tr("&Ok"), this);
QPushButton* button_cancel = new QPushButton(tr("&Cancel"), this);
button_ok->setFixedWidth(50);
button_cancel->setFixedWidth(50);
QLabel* description = new QLabel(tr("Specify ID of System Call or Function Call below. You need to use a Hexadecimal ID."), this);
description->setWordWrap(true);
m_current_converted = new QLabel(tr("Currently it gets an id of \"Unset\"."), this);
m_current_converted->setWordWrap(true);
m_id = new QLineEdit(this);
m_id->setText(qstr(fmt::format("%08x", m_entry)));
m_id->setPlaceholderText("ffffffff");
m_id->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
m_id->setMaxLength(8);
m_id->setFixedWidth(65);
setWindowTitle("Auto Pause Setting: " + m_id->text());
connect(button_cancel, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnCancel);
connect(button_ok, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnOk);
connect(m_id, &QLineEdit::textChanged, this, &AutoPauseConfigDialog::OnUpdateValue);
QHBoxLayout* configHBox = new QHBoxLayout();
configHBox->addWidget(m_id);
configHBox->addWidget(button_ok);
configHBox->addWidget(button_cancel);
configHBox->setAlignment(Qt::AlignCenter);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(description);
mainLayout->addLayout(configHBox);
mainLayout->addWidget(m_current_converted);
setLayout(mainLayout);
setFixedSize(QSize(300, sizeHint().height()));
OnUpdateValue();
}
void AutoPauseConfigDialog::OnOk()
{
bool ok;
const ullong value = m_id->text().toULongLong(&ok, 16);
m_entry = value;
*m_presult = m_entry;
accept();
}
void AutoPauseConfigDialog::OnCancel()
{
if (m_newEntry)
{
m_apsd->OnRemove();
}
close();
}
void AutoPauseConfigDialog::OnUpdateValue() const
{
bool ok;
const ullong value = m_id->text().toULongLong(&ok, 16);
const bool is_ok = ok && value <= u32{umax};
m_current_converted->setText(tr("Current value: %1 (%2)").arg(value, 8, 16).arg(is_ok ? tr("OK") : tr("Conversion failed")));
}