Path: blob/master/src/duckstation-qt/advancedsettingswidget.cpp
7411 views
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#include "advancedsettingswidget.h"4#include "logwindow.h"5#include "mainwindow.h"6#include "qtutils.h"7#include "settingswindow.h"8#include "settingwidgetbinder.h"910#include "core/gpu_types.h"1112#include <QtGui/QCursor>13#include <QtWidgets/QMenu>1415#include "moc_advancedsettingswidget.cpp"1617using namespace Qt::StringLiterals;1819static QCheckBox* addBooleanTweakOption(SettingsWindow* dialog, QTableWidget* table, QString name, std::string section,20std::string key, bool default_value)21{22const int row = table->rowCount();2324table->insertRow(row);2526QTableWidgetItem* name_item = new QTableWidgetItem(name);27name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));28table->setItem(row, 0, name_item);2930QCheckBox* cb = new QCheckBox(table);31if (!section.empty() || !key.empty())32{33SettingWidgetBinder::BindWidgetToBoolSetting(dialog->getSettingsInterface(), cb, std::move(section), std::move(key),34default_value);35}3637table->setCellWidget(row, 1, cb);38return cb;39}4041static QCheckBox* setBooleanTweakOption(QTableWidget* table, int row, bool value)42{43QWidget* widget = table->cellWidget(row, 1);44QCheckBox* cb = qobject_cast<QCheckBox*>(widget);45Assert(cb);46cb->setChecked(value);47return cb;48}4950static QSpinBox* addIntRangeTweakOption(SettingsWindow* dialog, QTableWidget* table, QString name, std::string section,51std::string key, int min_value, int max_value, int default_value,52const QString& suffix = QString())53{54const int row = table->rowCount();5556table->insertRow(row);5758QTableWidgetItem* name_item = new QTableWidgetItem(name);59name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));60table->setItem(row, 0, name_item);6162QSpinBox* cb = new QSpinBox(table);63cb->setMinimum(min_value);64cb->setMaximum(max_value);65if (!suffix.isEmpty())66cb->setSuffix(suffix);6768if (!section.empty() || !key.empty())69{70SettingWidgetBinder::BindWidgetToIntSetting(dialog->getSettingsInterface(), cb, std::move(section), std::move(key),71default_value);72}7374table->setCellWidget(row, 1, cb);75return cb;76}7778static QSpinBox* setIntRangeTweakOption(QTableWidget* table, int row, int value)79{80QWidget* widget = table->cellWidget(row, 1);81QSpinBox* cb = qobject_cast<QSpinBox*>(widget);82Assert(cb);83cb->setValue(value);84return cb;85}8687template<typename T>88static QComboBox* addChoiceTweakOption(SettingsWindow* dialog, QTableWidget* table, QString name, std::string section,89std::string key, std::optional<T> (*parse_callback)(const char*),90const char* (*get_value_callback)(T), const char* (*get_display_callback)(T),91u32 num_values, T default_value)92{93const int row = table->rowCount();9495table->insertRow(row);9697QTableWidgetItem* name_item = new QTableWidgetItem(name);98name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));99table->setItem(row, 0, name_item);100101QComboBox* cb = new QComboBox(table);102for (u32 i = 0; i < num_values; i++)103cb->addItem(QString::fromUtf8(get_display_callback(static_cast<T>(i))));104105if (!section.empty() || !key.empty())106{107SettingWidgetBinder::BindWidgetToEnumSetting(dialog->getSettingsInterface(), cb, std::move(section), std::move(key),108parse_callback, get_value_callback, default_value);109}110111table->setCellWidget(row, 1, cb);112return cb;113}114115template<typename T>116static void setChoiceTweakOption(QTableWidget* table, int row, T value)117{118QWidget* widget = table->cellWidget(row, 1);119QComboBox* cb = qobject_cast<QComboBox*>(widget);120Assert(cb);121cb->setCurrentIndex(static_cast<int>(value));122}123124static void addDirectoryOption(SettingsWindow* dialog, QTableWidget* table, const QString& name, std::string section,125std::string key)126{127const int row = table->rowCount();128129table->insertRow(row);130131QTableWidgetItem* name_item = new QTableWidgetItem(name);132name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));133table->setItem(row, 0, name_item);134135QWidget* container = new QWidget(table);136137QHBoxLayout* layout = new QHBoxLayout(container);138layout->setContentsMargins(0, 0, 0, 0);139140QLineEdit* value = new QLineEdit(container);141value->setObjectName("value"_L1);142SettingWidgetBinder::BindWidgetToStringSetting(dialog->getSettingsInterface(), value, std::move(section),143std::move(key));144layout->addWidget(value, 1);145146QPushButton* browse = new QPushButton(container);147browse->setText("..."_L1);148browse->setMaximumWidth(32);149QObject::connect(browse, &QPushButton::clicked, browse, [browse, value, name]() {150const QString path(QDir::toNativeSeparators(QFileDialog::getExistingDirectory(151browse, qApp->translate("AdvancedSettingsWidget", "Select folder for %1").arg(name), value->text())));152if (!path.isEmpty())153value->setText(path);154});155layout->addWidget(browse, 0);156157table->setCellWidget(row, 1, container);158}159160static void setDirectoryOption(QTableWidget* table, int row, const char* value)161{162QWidget* widget = table->cellWidget(row, 1);163Assert(widget);164QLineEdit* valuew = widget->findChild<QLineEdit*>("value"_L1);165Assert(valuew);166valuew->setText(QString::fromUtf8(value));167}168169AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsWindow* dialog, QWidget* parent)170: QWidget(parent), m_dialog(dialog)171{172SettingsInterface* sif = dialog->getSettingsInterface();173174m_ui.setupUi(this);175176for (u32 i = 0; i < static_cast<u32>(Log::Level::MaxCount); i++)177m_ui.logLevel->addItem(QString::fromUtf8(Settings::GetLogLevelDisplayName(static_cast<Log::Level>(i))));178179SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.logLevel, "Logging", "LogLevel", &Settings::ParseLogLevelName,180&Settings::GetLogLevelName, Log::DEFAULT_LOG_LEVEL);181SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToConsole, "Logging", "LogToConsole", false);182SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToDebug, "Logging", "LogToDebug", false);183SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToWindow, "Logging", "LogToWindow", false);184SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToFile, "Logging", "LogToFile", false);185SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logTimestamps, "Logging", "LogTimestamps", true);186SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logFileTimestamps, "Logging", "LogFileTimestamps", false);187connect(m_ui.logToConsole, &QCheckBox::checkStateChanged, this, &AdvancedSettingsWidget::onAnyLogSinksChanged);188connect(m_ui.logToWindow, &QCheckBox::checkStateChanged, this, &AdvancedSettingsWidget::onAnyLogSinksChanged);189connect(m_ui.logToFile, &QCheckBox::checkStateChanged, this, &AdvancedSettingsWidget::onAnyLogSinksChanged);190onAnyLogSinksChanged(); // initialize enabled/disabled state of checkboxes191192connect(m_ui.logChannels, &QAbstractButton::clicked, this, &AdvancedSettingsWidget::onLogChannelsButtonClicked);193194SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showDebugMenu, "Main", "ShowDebugMenu", false);195connect(m_ui.showDebugMenu, &QCheckBox::checkStateChanged, g_main_window, &MainWindow::updateDebugMenuVisibility,196Qt::QueuedConnection);197connect(m_ui.showDebugMenu, &QCheckBox::checkStateChanged, this,198&AdvancedSettingsWidget::onShowDebugOptionsStateChanged);199200connect(m_ui.resetToDefaultButton, &QPushButton::clicked, this, &AdvancedSettingsWidget::onResetToDefaultClicked);201202m_ui.tweakOptionTable->setColumnWidth(0, 380);203m_ui.tweakOptionTable->setColumnWidth(1, 170);204205addTweakOptions();206207dialog->registerWidgetHelp(m_ui.logLevel, tr("Log Level"), tr("Information"),208tr("Sets the verbosity of messages logged. Higher levels will log more messages."));209dialog->registerWidgetHelp(m_ui.logToConsole, tr("Log To System Console"), tr("User Preference"),210tr("Logs messages to the console window."));211dialog->registerWidgetHelp(m_ui.logToDebug, tr("Log To Debug Console"), tr("User Preference"),212tr("Logs messages to the debug console where supported."));213dialog->registerWidgetHelp(m_ui.logToWindow, tr("Log To Window"), tr("User Preference"),214tr("Logs messages to the window."));215dialog->registerWidgetHelp(m_ui.logToFile, tr("Log To File"), tr("User Preference"),216tr("Logs messages to duckstation.log in the user directory."));217dialog->registerWidgetHelp(m_ui.logTimestamps, tr("Log Timestamps"), tr("User Preference"),218tr("Includes the elapsed time since the application start in window and console logs."));219dialog->registerWidgetHelp(m_ui.logFileTimestamps, tr("Log File Timestamps"), tr("User Preference"),220tr("Includes the elapsed time since the application start in file logs."));221dialog->registerWidgetHelp(m_ui.showDebugMenu, tr("Show Debug Menu"), tr("Unchecked"),222tr("Shows a debug menu bar with additional statistics and quick settings."));223}224225AdvancedSettingsWidget::~AdvancedSettingsWidget() = default;226227void AdvancedSettingsWidget::onLogChannelsButtonClicked()228{229QMenu* const menu = QtUtils::NewPopupMenu(this);230LogWindow::populateFilterMenu(menu);231menu->popup(QCursor::pos());232}233234void AdvancedSettingsWidget::onAnyLogSinksChanged()235{236const bool log_to_console = m_dialog->getEffectiveBoolValue("Logging", "LogToConsole", false);237const bool log_to_window = m_dialog->getEffectiveBoolValue("Logging", "LogToWindow", false);238const bool log_to_file = m_dialog->getEffectiveBoolValue("Logging", "LogToFile", false);239240m_ui.logTimestamps->setEnabled(log_to_console || log_to_window);241m_ui.logFileTimestamps->setEnabled(log_to_file);242}243244void AdvancedSettingsWidget::onShowDebugOptionsStateChanged()245{246const bool enabled = QtHost::ShouldShowDebugOptions();247emit onShowDebugOptionsChanged(enabled);248}249250void AdvancedSettingsWidget::addTweakOptions()251{252if (!m_dialog->isPerGameSettings())253{254addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Game Settings"), "Main", "ApplyGameSettings",255true);256}257258addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Compatibility Settings"), "Main",259"ApplyCompatibilitySettings", true);260addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Load Devices From Save States"), "Main",261"LoadDevicesFromSaveStates", false);262addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Pause On Start"), "Main", "StartPaused", false);263addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Save State Compression"), "Main", "SaveStateCompression",264&Settings::ParseSaveStateCompressionModeName, &Settings::GetSaveStateCompressionModeName,265&Settings::GetSaveStateCompressionModeDisplayName,266static_cast<u32>(SaveStateCompressionMode::Count),267Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE);268269if (m_dialog->isPerGameSettings())270{271addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Active Start Offset"), "Display",272"ActiveStartOffset", -5000, 5000, 0);273addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Active End Offset"), "Display",274"ActiveEndOffset", -5000, 5000, 0);275addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Line Start Offset"), "Display",276"LineStartOffset", -128, 127, 0);277addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Line End Offset"), "Display", "LineEndOffset",278-128, 127, 0);279}280281addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("DMA Max Slice Ticks"), "Hacks", "DMAMaxSliceTicks", 1,28210000, Settings::DEFAULT_DMA_MAX_SLICE_TICKS, tr(" cycles"));283addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("DMA Halt Ticks"), "Hacks", "DMAHaltTicks", 1, 10000,284Settings::DEFAULT_DMA_HALT_TICKS, tr(" cycles"));285addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("GPU FIFO Size"), "Hacks", "GPUFIFOSize", 16, 4096,286Settings::DEFAULT_GPU_FIFO_SIZE, tr(" words"));287addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("GPU Max Runahead"), "Hacks", "GPUMaxRunAhead", 0, 1000,288Settings::DEFAULT_GPU_MAX_RUN_AHEAD, tr(" cycles"));289290addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable Recompiler Memory Exceptions"), "CPU",291"RecompilerMemoryExceptions", false);292addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable Recompiler Block Linking"), "CPU",293"RecompilerBlockLinking", true);294addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable Recompiler Fast Memory Access"), "CPU",295"FastmemMode", Settings::ParseCPUFastmemMode, Settings::GetCPUFastmemModeName,296Settings::GetCPUFastmemModeDisplayName, static_cast<u32>(CPUFastmemMode::Count),297Settings::DEFAULT_CPU_FASTMEM_MODE);298299addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Mechacon Version"), "CDROM", "MechaconVersion",300Settings::ParseCDROMMechVersionName, Settings::GetCDROMMechVersionName,301Settings::GetCDROMMechVersionDisplayName, static_cast<u8>(CDROMMechaconVersion::Count),302Settings::DEFAULT_CDROM_MECHACON_VERSION);303addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Readahead Sectors"), "CDROM", "ReadaheadSectors",3040, 32, Settings::DEFAULT_CDROM_READAHEAD_SECTORS, tr(" sectors"));305addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Max Read Speedup Cycles"), "CDROM",306"MaxReadSpeedupCycles", 1, 1000000, Settings::DEFAULT_CDROM_MAX_READ_SPEEDUP_CYCLES,307tr(" cycles"));308addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Max Seek Speedup Cycles"), "CDROM",309"MaxSeekSpeedupCycles", 1, 1000000, Settings::DEFAULT_CDROM_MAX_SEEK_SPEEDUP_CYCLES,310tr(" cycles"));311addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Disable Speedup on MDEC"), "CDROM",312"DisableSpeedupOnMDEC", false);313addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Region Check"), "CDROM", "RegionCheck", false);314addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM SubQ Skew"), "CDROM", "SubQSkew", false);315addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Allow Booting Without SBI File"), "CDROM",316"AllowBootingWithoutSBIFile", false);317318addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable GDB Server"), "Debug", "EnableGDBServer", false);319addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("GDB Server Port"), "Debug", "GDBServerPort", 1, 65535,320Settings::DEFAULT_GDB_SERVER_PORT);321322addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Export Shared Memory"), "Hacks", "ExportSharedMemory",323false);324addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Redirect SIO to TTY"), "SIO", "RedirectToTTY", false);325addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PCDrv"), "PCDrv", "Enabled", false);326addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PCDrv Writes"), "PCDrv", "EnableWrites", false);327addDirectoryOption(m_dialog, m_ui.tweakOptionTable, tr("PCDrv Root Directory"), "PCDrv", "Root");328}329330void AdvancedSettingsWidget::onResetToDefaultClicked()331{332if (!m_dialog->isPerGameSettings())333{334int i = 0;335336setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply Game Settings337setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Apply Compatibility settings338setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Pause On Start339setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Load Devices From Save States340setChoiceTweakOption(m_ui.tweakOptionTable, i++,341Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE); // Save State Compression342setIntRangeTweakOption(m_ui.tweakOptionTable, i++,343static_cast<int>(Settings::DEFAULT_DMA_MAX_SLICE_TICKS)); // DMA max slice ticks344setIntRangeTweakOption(m_ui.tweakOptionTable, i++,345static_cast<int>(Settings::DEFAULT_DMA_HALT_TICKS)); // DMA halt ticks346setIntRangeTweakOption(m_ui.tweakOptionTable, i++,347static_cast<int>(Settings::DEFAULT_GPU_FIFO_SIZE)); // GPU FIFO size348setIntRangeTweakOption(m_ui.tweakOptionTable, i++,349static_cast<int>(Settings::DEFAULT_GPU_MAX_RUN_AHEAD)); // GPU max runahead350setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Recompiler memory exceptions351setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Recompiler block linking352setChoiceTweakOption(m_ui.tweakOptionTable, i++,353Settings::DEFAULT_CPU_FASTMEM_MODE); // Recompiler fastmem mode354setChoiceTweakOption(m_ui.tweakOptionTable, i++,355Settings::DEFAULT_CDROM_MECHACON_VERSION); // CDROM Mechacon Version356setIntRangeTweakOption(m_ui.tweakOptionTable, i++,357Settings::DEFAULT_CDROM_READAHEAD_SECTORS); // CD-ROM Readahead Sectors358setIntRangeTweakOption(m_ui.tweakOptionTable, i++,359Settings::DEFAULT_CDROM_MAX_READ_SPEEDUP_CYCLES); // CD-ROM Max Speedup Read Cycles360setIntRangeTweakOption(m_ui.tweakOptionTable, i++,361Settings::DEFAULT_CDROM_MAX_SEEK_SPEEDUP_CYCLES); // CD-ROM Max Speedup Seek Cycles362setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // CDROM Disable Speedup on MDEC363setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // CDROM Region Check364setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // CDROM SubQ Skew365setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Allow booting without SBI file366setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Enable GDB Server367setIntRangeTweakOption(m_ui.tweakOptionTable, i++, Settings::DEFAULT_GDB_SERVER_PORT); // GDB Server Port368setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Export Shared Memory369setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Redirect SIO to TTY370setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Enable PCDRV371setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Enable PCDRV Writes372setDirectoryOption(m_ui.tweakOptionTable, i++, ""); // PCDrv Root Directory373374return;375}376377// for per-game it's easier to just clear and recreate378INISettingsInterface* sif = m_dialog->getSettingsInterface();379sif->DeleteValue("Main", "ApplyCompatibilitySettings");380sif->DeleteValue("Main", "LoadDevicesFromSaveStates");381sif->DeleteValue("Main", "PauseOnStart");382sif->DeleteValue("Main", "CompressSaveStates");383sif->DeleteValue("Display", "ActiveStartOffset");384sif->DeleteValue("Display", "ActiveEndOffset");385sif->DeleteValue("Display", "LineStartOffset");386sif->DeleteValue("Display", "LineEndOffset");387sif->DeleteValue("Hacks", "DMAMaxSliceTicks");388sif->DeleteValue("Hacks", "DMAHaltTicks");389sif->DeleteValue("Hacks", "GPUFIFOSize");390sif->DeleteValue("Hacks", "GPUMaxRunAhead");391sif->DeleteValue("Hacks", "ExportSharedMemory");392sif->DeleteValue("CPU", "RecompilerMemoryExceptions");393sif->DeleteValue("CPU", "RecompilerBlockLinking");394sif->DeleteValue("CPU", "FastmemMode");395sif->DeleteValue("CDROM", "MechaconVersion");396sif->DeleteValue("CDROM", "ReadaheadSectors");397sif->DeleteValue("CDROM", "MaxReadSpeedupCycles");398sif->DeleteValue("CDROM", "MaxSeekSpeedupCycles");399sif->DeleteValue("CDROM", "DisableSpeedupOnMDEC");400sif->DeleteValue("CDROM", "RegionCheck");401sif->DeleteValue("CDROM", "SubQSkew");402sif->DeleteValue("CDROM", "AllowBootingWithoutSBIFile");403sif->DeleteValue("Debug", "EnableGDBServer");404sif->DeleteValue("Debug", "GDBServerPort");405sif->DeleteValue("SIO", "RedirectToTTY");406sif->DeleteValue("PCDrv", "Enabled");407sif->DeleteValue("PCDrv", "EnableWrites");408sif->DeleteValue("PCDrv", "Root");409QtHost::SaveGameSettings(sif, true);410g_core_thread->reloadGameSettings();411while (m_ui.tweakOptionTable->rowCount() > 0)412m_ui.tweakOptionTable->removeRow(m_ui.tweakOptionTable->rowCount() - 1);413addTweakOptions();414}415416417