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 }