mirror of https://github.com/RPCS3/rpcs3.git
289 lines
8.3 KiB
C++
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")));
|
|
}
|