#include "auto_pause_settings_dialog.h" #include "table_item_delegate.h" #include "Emu/System.h" #include #include #include #include #include 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); pauseList = new QTableWidget(this); pauseList->setColumnCount(2); pauseList->setHorizontalHeaderLabels(QStringList() << tr("Call ID") << tr("Type")); //pauseList->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); pauseList->setSelectionBehavior(QAbstractItemView::SelectRows); pauseList->setContextMenuPolicy(Qt::CustomContextMenu); pauseList->setItemDelegate(new table_item_delegate(this)); pauseList->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(pauseList); mainLayout->addLayout(buttonsLayout); setLayout(mainLayout); setMinimumSize(QSize(400, 360)); setWindowTitle(tr("Auto Pause Manager")); setObjectName("auto_pause_manager"); //Events connect(pauseList, &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.SetForceBoot(true); Emu.Stop(); LoadEntries(); UpdateList(); setFixedSize(sizeHint()); } //Copied some from AutoPause. void auto_pause_settings_dialog::LoadEntries(void) { m_entries.clear(); m_entries.reserve(16); fs::file list(fs::get_config_dir() + "pause.bin"); if (list) { //System calls ID and Function calls ID are all u32 iirc. u32 num; size_t fmax = list.size(); size_t 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(void) { 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 (size_t 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(void) { const int entries_size = static_cast(m_entries.size()); pauseList->clearContents(); pauseList->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")); } pauseList->setItem(i, 0, callItem); pauseList->setItem(i, 1, typeItem); } } void auto_pause_settings_dialog::ShowContextMenu(const QPoint &pos) { int row = pauseList->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]() { m_entries.emplace_back(0xFFFFFFFF); UpdateList(); int idx = static_cast(m_entries.size()) - 1; pauseList->selectRow(idx); OnEntryConfig(idx, true); }); connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove); connect(config, &QAction::triggered, [=, this]() {OnEntryConfig(row, false); }); myMenu.exec(pauseList->viewport()->mapToGlobal(pos)); } void auto_pause_settings_dialog::OnRemove() { QModelIndexList selection = pauseList->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->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; 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() { bool ok; ullong value = m_id->text().toULongLong(&ok, 16); const bool is_ok = ok && value <= UINT32_MAX; m_current_converted->setText(qstr(fmt::format("Current value: %08x (%s)", u32(value), is_ok ? "OK" : "conversion failed"))); }