duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

advancedsettingswidget.cpp (17430B)


      1 // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "advancedsettingswidget.h"
      5 #include "core/gpu_types.h"
      6 #include "mainwindow.h"
      7 #include "qtutils.h"
      8 #include "settingswindow.h"
      9 #include "settingwidgetbinder.h"
     10 
     11 static QCheckBox* addBooleanTweakOption(SettingsWindow* dialog, QTableWidget* table, QString name, std::string section,
     12                                         std::string key, bool default_value)
     13 {
     14   const int row = table->rowCount();
     15 
     16   table->insertRow(row);
     17 
     18   QTableWidgetItem* name_item = new QTableWidgetItem(name);
     19   name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));
     20   table->setItem(row, 0, name_item);
     21 
     22   QCheckBox* cb = new QCheckBox(table);
     23   if (!section.empty() || !key.empty())
     24   {
     25     SettingWidgetBinder::BindWidgetToBoolSetting(dialog->getSettingsInterface(), cb, std::move(section), std::move(key),
     26                                                  default_value);
     27   }
     28 
     29   table->setCellWidget(row, 1, cb);
     30   return cb;
     31 }
     32 
     33 static QCheckBox* setBooleanTweakOption(QTableWidget* table, int row, bool value)
     34 {
     35   QWidget* widget = table->cellWidget(row, 1);
     36   QCheckBox* cb = qobject_cast<QCheckBox*>(widget);
     37   Assert(cb);
     38   cb->setChecked(value);
     39   return cb;
     40 }
     41 
     42 static QSpinBox* addIntRangeTweakOption(SettingsWindow* dialog, QTableWidget* table, QString name, std::string section,
     43                                         std::string key, int min_value, int max_value, int default_value)
     44 {
     45   const int row = table->rowCount();
     46 
     47   table->insertRow(row);
     48 
     49   QTableWidgetItem* name_item = new QTableWidgetItem(name);
     50   name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));
     51   table->setItem(row, 0, name_item);
     52 
     53   QSpinBox* cb = new QSpinBox(table);
     54   cb->setMinimum(min_value);
     55   cb->setMaximum(max_value);
     56   if (!section.empty() || !key.empty())
     57   {
     58     SettingWidgetBinder::BindWidgetToIntSetting(dialog->getSettingsInterface(), cb, std::move(section), std::move(key),
     59                                                 default_value);
     60   }
     61 
     62   table->setCellWidget(row, 1, cb);
     63   return cb;
     64 }
     65 
     66 static QSpinBox* setIntRangeTweakOption(QTableWidget* table, int row, int value)
     67 {
     68   QWidget* widget = table->cellWidget(row, 1);
     69   QSpinBox* cb = qobject_cast<QSpinBox*>(widget);
     70   Assert(cb);
     71   cb->setValue(value);
     72   return cb;
     73 }
     74 
     75 template<typename T>
     76 static QComboBox* addChoiceTweakOption(SettingsWindow* dialog, QTableWidget* table, QString name, std::string section,
     77                                        std::string key, std::optional<T> (*parse_callback)(const char*),
     78                                        const char* (*get_value_callback)(T), const char* (*get_display_callback)(T),
     79                                        u32 num_values, T default_value)
     80 {
     81   const int row = table->rowCount();
     82 
     83   table->insertRow(row);
     84 
     85   QTableWidgetItem* name_item = new QTableWidgetItem(name);
     86   name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));
     87   table->setItem(row, 0, name_item);
     88 
     89   QComboBox* cb = new QComboBox(table);
     90   for (u32 i = 0; i < num_values; i++)
     91     cb->addItem(QString::fromUtf8(get_display_callback(static_cast<T>(i))));
     92 
     93   if (!section.empty() || !key.empty())
     94   {
     95     SettingWidgetBinder::BindWidgetToEnumSetting(dialog->getSettingsInterface(), cb, std::move(section), std::move(key),
     96                                                  parse_callback, get_value_callback, default_value);
     97   }
     98 
     99   table->setCellWidget(row, 1, cb);
    100   return cb;
    101 }
    102 
    103 template<typename T>
    104 static void setChoiceTweakOption(QTableWidget* table, int row, T value)
    105 {
    106   QWidget* widget = table->cellWidget(row, 1);
    107   QComboBox* cb = qobject_cast<QComboBox*>(widget);
    108   Assert(cb);
    109   cb->setCurrentIndex(static_cast<int>(value));
    110 }
    111 
    112 static void addDirectoryOption(SettingsWindow* dialog, QTableWidget* table, const QString& name, std::string section,
    113                                std::string key)
    114 {
    115   const int row = table->rowCount();
    116 
    117   table->insertRow(row);
    118 
    119   QTableWidgetItem* name_item = new QTableWidgetItem(name);
    120   name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));
    121   table->setItem(row, 0, name_item);
    122 
    123   QWidget* container = new QWidget(table);
    124 
    125   QHBoxLayout* layout = new QHBoxLayout(container);
    126   layout->setContentsMargins(0, 0, 0, 0);
    127 
    128   QLineEdit* value = new QLineEdit(container);
    129   value->setObjectName(QStringLiteral("value"));
    130   SettingWidgetBinder::BindWidgetToStringSetting(dialog->getSettingsInterface(), value, std::move(section),
    131                                                  std::move(key));
    132   layout->addWidget(value, 1);
    133 
    134   QPushButton* browse = new QPushButton(container);
    135   browse->setText(QStringLiteral("..."));
    136   browse->setMaximumWidth(32);
    137   QObject::connect(browse, &QPushButton::clicked, browse, [browse, value, name]() {
    138     const QString path(QDir::toNativeSeparators(QFileDialog::getExistingDirectory(
    139       QtUtils::GetRootWidget(browse), qApp->translate("AdvancedSettingsWidget", "Select folder for %1").arg(name),
    140       value->text())));
    141     if (!path.isEmpty())
    142       value->setText(path);
    143   });
    144   layout->addWidget(browse, 0);
    145 
    146   table->setCellWidget(row, 1, container);
    147 }
    148 
    149 static void setDirectoryOption(QTableWidget* table, int row, const char* value)
    150 {
    151   QWidget* widget = table->cellWidget(row, 1);
    152   Assert(widget);
    153   QLineEdit* valuew = widget->findChild<QLineEdit*>(QStringLiteral("value"));
    154   Assert(valuew);
    155   valuew->setText(QString::fromUtf8(value));
    156 }
    157 
    158 AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsWindow* dialog, QWidget* parent)
    159   : QWidget(parent), m_dialog(dialog)
    160 {
    161   SettingsInterface* sif = dialog->getSettingsInterface();
    162 
    163   m_ui.setupUi(this);
    164 
    165   for (u32 i = 0; i < static_cast<u32>(LOGLEVEL_COUNT); i++)
    166     m_ui.logLevel->addItem(QString::fromUtf8(Settings::GetLogLevelDisplayName(static_cast<LOGLEVEL>(i))));
    167 
    168   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.logLevel, "Logging", "LogLevel", &Settings::ParseLogLevelName,
    169                                                &Settings::GetLogLevelName, Settings::DEFAULT_LOG_LEVEL);
    170   SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.logFilter, "Logging", "LogFilter");
    171   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToConsole, "Logging", "LogToConsole", false);
    172   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToDebug, "Logging", "LogToDebug", false);
    173   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToWindow, "Logging", "LogToWindow", false);
    174   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.logToFile, "Logging", "LogToFile", false);
    175 
    176   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showDebugMenu, "Main", "ShowDebugMenu", false);
    177 
    178   connect(m_ui.resetToDefaultButton, &QPushButton::clicked, this, &AdvancedSettingsWidget::onResetToDefaultClicked);
    179   connect(m_ui.showDebugMenu, &QCheckBox::checkStateChanged, g_main_window, &MainWindow::updateDebugMenuVisibility,
    180           Qt::QueuedConnection);
    181   connect(m_ui.showDebugMenu, &QCheckBox::checkStateChanged, this,
    182           &AdvancedSettingsWidget::onShowDebugOptionsStateChanged);
    183 
    184   m_ui.tweakOptionTable->setColumnWidth(0, 380);
    185   m_ui.tweakOptionTable->setColumnWidth(1, 170);
    186 
    187   addTweakOptions();
    188 
    189   dialog->registerWidgetHelp(m_ui.logLevel, tr("Log Level"), tr("Information"),
    190                              tr("Sets the verbosity of messages logged. Higher levels will log more messages."));
    191   dialog->registerWidgetHelp(m_ui.logToConsole, tr("Log To System Console"), tr("User Preference"),
    192                              tr("Logs messages to the console window."));
    193   dialog->registerWidgetHelp(m_ui.logToDebug, tr("Log To Debug Console"), tr("User Preference"),
    194                              tr("Logs messages to the debug console where supported."));
    195   dialog->registerWidgetHelp(m_ui.logToWindow, tr("Log To Window"), tr("User Preference"),
    196                              tr("Logs messages to the window."));
    197   dialog->registerWidgetHelp(m_ui.logToFile, tr("Log To File"), tr("User Preference"),
    198                              tr("Logs messages to duckstation.log in the user directory."));
    199   dialog->registerWidgetHelp(m_ui.showDebugMenu, tr("Show Debug Menu"), tr("Unchecked"),
    200                              tr("Shows a debug menu bar with additional statistics and quick settings."));
    201 }
    202 
    203 AdvancedSettingsWidget::~AdvancedSettingsWidget() = default;
    204 
    205 void AdvancedSettingsWidget::onShowDebugOptionsStateChanged()
    206 {
    207   const bool enabled = QtHost::ShouldShowDebugOptions();
    208   emit onShowDebugOptionsChanged(enabled);
    209 }
    210 
    211 void AdvancedSettingsWidget::addTweakOptions()
    212 {
    213   if (!m_dialog->isPerGameSettings())
    214   {
    215     addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Game Settings"), "Main", "ApplyGameSettings",
    216                           true);
    217   }
    218 
    219   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Apply Compatibility Settings"), "Main",
    220                         "ApplyCompatibilitySettings", true);
    221   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Increase Timer Resolution"), "Main",
    222                         "IncreaseTimerResolution", true);
    223   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Load Devices From Save States"), "Main",
    224                         "LoadDevicesFromSaveStates", false);
    225   addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Save State Compression"), "Main", "SaveStateCompression",
    226                        &Settings::ParseSaveStateCompressionModeName, &Settings::GetSaveStateCompressionModeName,
    227                        &Settings::GetSaveStateCompressionModeDisplayName,
    228                        static_cast<u32>(SaveStateCompressionMode::Count),
    229                        Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE);
    230 
    231   if (m_dialog->isPerGameSettings())
    232   {
    233     addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Active Start Offset"), "Display",
    234                            "ActiveStartOffset", -5000, 5000, 0);
    235     addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Active End Offset"), "Display",
    236                            "ActiveEndOffset", -5000, 5000, 0);
    237     addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Line Start Offset"), "Display",
    238                            "LineStartOffset", -128, 127, 0);
    239     addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Display Line End Offset"), "Display", "LineEndOffset",
    240                            -128, 127, 0);
    241   }
    242 
    243   addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("DMA Max Slice Ticks"), "Hacks", "DMAMaxSliceTicks", 1,
    244                          10000, Settings::DEFAULT_DMA_MAX_SLICE_TICKS);
    245   addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("DMA Halt Ticks"), "Hacks", "DMAHaltTicks", 1, 10000,
    246                          Settings::DEFAULT_DMA_HALT_TICKS);
    247   addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("GPU FIFO Size"), "Hacks", "GPUFIFOSize", 16, 4096,
    248                          Settings::DEFAULT_GPU_FIFO_SIZE);
    249   addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("GPU Max Run-Ahead"), "Hacks", "GPUMaxRunAhead", 0, 1000,
    250                          Settings::DEFAULT_GPU_MAX_RUN_AHEAD);
    251 
    252   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable Recompiler Memory Exceptions"), "CPU",
    253                         "RecompilerMemoryExceptions", false);
    254   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable Recompiler Block Linking"), "CPU",
    255                         "RecompilerBlockLinking", true);
    256   addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable Recompiler Fast Memory Access"), "CPU",
    257                        "FastmemMode", Settings::ParseCPUFastmemMode, Settings::GetCPUFastmemModeName,
    258                        Settings::GetCPUFastmemModeDisplayName, static_cast<u32>(CPUFastmemMode::Count),
    259                        Settings::DEFAULT_CPU_FASTMEM_MODE);
    260 
    261   addChoiceTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Mechacon Version"), "CDROM", "MechaconVersion",
    262                        Settings::ParseCDROMMechVersionName, Settings::GetCDROMMechVersionName,
    263                        Settings::GetCDROMMechVersionDisplayName, static_cast<u8>(CDROMMechaconVersion::Count),
    264                        Settings::DEFAULT_CDROM_MECHACON_VERSION);
    265   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("CD-ROM Region Check"), "CDROM", "RegionCheck", false);
    266   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Allow Booting Without SBI File"), "CDROM",
    267                         "AllowBootingWithoutSBIFile", false);
    268 
    269   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Export Shared Memory"), "Hacks", "ExportSharedMemory",
    270                         false);
    271   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PINE"), "PINE", "Enabled", false);
    272   addIntRangeTweakOption(m_dialog, m_ui.tweakOptionTable, tr("PINE Slot"), "PINE", "Slot", 0, 65535,
    273                          Settings::DEFAULT_PINE_SLOT);
    274 
    275   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PCDrv"), "PCDrv", "Enabled", false);
    276   addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PCDrv Writes"), "PCDrv", "EnableWrites", false);
    277   addDirectoryOption(m_dialog, m_ui.tweakOptionTable, tr("PCDrv Root Directory"), "PCDrv", "Root");
    278 }
    279 
    280 void AdvancedSettingsWidget::onResetToDefaultClicked()
    281 {
    282   if (!m_dialog->isPerGameSettings())
    283   {
    284     int i = 0;
    285 
    286     setBooleanTweakOption(m_ui.tweakOptionTable, i++, true);  // Apply Game Settings
    287     setBooleanTweakOption(m_ui.tweakOptionTable, i++, true);  // Apply compatibility settings
    288     setBooleanTweakOption(m_ui.tweakOptionTable, i++, true);  // Increase Timer Resolution
    289     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Load Devices From Save States
    290     setChoiceTweakOption(m_ui.tweakOptionTable, i++,
    291                          Settings::DEFAULT_SAVE_STATE_COMPRESSION_MODE); // Save State Compression
    292     setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
    293                            static_cast<int>(Settings::DEFAULT_DMA_MAX_SLICE_TICKS)); // DMA max slice ticks
    294     setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
    295                            static_cast<int>(Settings::DEFAULT_DMA_HALT_TICKS)); // DMA halt ticks
    296     setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
    297                            static_cast<int>(Settings::DEFAULT_GPU_FIFO_SIZE)); // GPU FIFO size
    298     setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
    299                            static_cast<int>(Settings::DEFAULT_GPU_MAX_RUN_AHEAD)); // GPU max run-ahead
    300     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                      // Recompiler memory exceptions
    301     setBooleanTweakOption(m_ui.tweakOptionTable, i++, true);                       // Recompiler block linking
    302     setChoiceTweakOption(m_ui.tweakOptionTable, i++,
    303                          Settings::DEFAULT_CPU_FASTMEM_MODE); // Recompiler fastmem mode
    304     setChoiceTweakOption(m_ui.tweakOptionTable, i++,
    305                          Settings::DEFAULT_CDROM_MECHACON_VERSION);                  // CDROM Mechacon Version
    306     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                        // CDROM Region Check
    307     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                        // Allow booting without SBI file
    308     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                        // Export Shared Memory
    309     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                        // Enable PINE
    310     setIntRangeTweakOption(m_ui.tweakOptionTable, i++, Settings::DEFAULT_PINE_SLOT); // PINE Slot
    311     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                        // Enable PCDRV
    312     setBooleanTweakOption(m_ui.tweakOptionTable, i++, false);                        // Enable PCDRV Writes
    313     setDirectoryOption(m_ui.tweakOptionTable, i++, "");                              // PCDrv Root Directory
    314 
    315     return;
    316   }
    317 
    318   // for per-game it's easier to just clear and recreate
    319   SettingsInterface* sif = m_dialog->getSettingsInterface();
    320   sif->DeleteValue("Main", "ApplyCompatibilitySettings");
    321   sif->DeleteValue("Main", "IncreaseTimerResolution");
    322   sif->DeleteValue("Main", "LoadDevicesFromSaveStates");
    323   sif->DeleteValue("Main", "CompressSaveStates");
    324   sif->DeleteValue("Display", "ActiveStartOffset");
    325   sif->DeleteValue("Display", "ActiveEndOffset");
    326   sif->DeleteValue("Display", "LineStartOffset");
    327   sif->DeleteValue("Display", "LineEndOffset");
    328   sif->DeleteValue("Hacks", "DMAMaxSliceTicks");
    329   sif->DeleteValue("Hacks", "DMAHaltTicks");
    330   sif->DeleteValue("Hacks", "GPUFIFOSize");
    331   sif->DeleteValue("Hacks", "GPUMaxRunAhead");
    332   sif->DeleteValue("Hacks", "ExportSharedMemory");
    333   sif->DeleteValue("CPU", "RecompilerMemoryExceptions");
    334   sif->DeleteValue("CPU", "RecompilerBlockLinking");
    335   sif->DeleteValue("CPU", "FastmemMode");
    336   sif->DeleteValue("CDROM", "MechaconVersion");
    337   sif->DeleteValue("CDROM", "RegionCheck");
    338   sif->DeleteValue("CDROM", "AllowBootingWithoutSBIFile");
    339   sif->DeleteValue("PINE", "Enabled");
    340   sif->DeleteValue("PINE", "Slot");
    341   sif->DeleteValue("PCDrv", "Enabled");
    342   sif->DeleteValue("PCDrv", "EnableWrites");
    343   sif->DeleteValue("PCDrv", "Root");
    344   sif->Save();
    345   while (m_ui.tweakOptionTable->rowCount() > 0)
    346     m_ui.tweakOptionTable->removeRow(m_ui.tweakOptionTable->rowCount() - 1);
    347   addTweakOptions();
    348 }