Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/duckstation-qt/consolesettingswidget.cpp
4242 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "consolesettingswidget.h"
5
#include "qtutils.h"
6
#include "settingswindow.h"
7
#include "settingwidgetbinder.h"
8
9
#include "core/game_database.h"
10
#include "core/system.h"
11
12
#include "util/cd_image.h"
13
14
#include <QtWidgets/QMessageBox>
15
#include <QtWidgets/QPushButton>
16
17
#include "moc_consolesettingswidget.cpp"
18
19
static constexpr const int CDROM_SPEEDUP_VALUES[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0};
20
21
ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* parent)
22
: QWidget(parent), m_dialog(dialog)
23
{
24
SettingsInterface* sif = dialog->getSettingsInterface();
25
26
m_ui.setupUi(this);
27
28
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.region, "Console", "Region", &Settings::ParseConsoleRegionName,
29
&Settings::GetConsoleRegionName, &Settings::GetConsoleRegionDisplayName,
30
Settings::DEFAULT_CONSOLE_REGION, ConsoleRegion::Count,
31
&QtUtils::GetIconForRegion);
32
SettingWidgetBinder::BindWidgetToEnumSetting(
33
sif, m_ui.forceVideoTiming, "GPU", "ForceVideoTiming", &Settings::ParseForceVideoTimingName,
34
&Settings::GetForceVideoTimingName, &Settings::GetForceVideoTimingDisplayName,
35
Settings::DEFAULT_FORCE_VIDEO_TIMING_MODE, ForceVideoTimingMode::Count, +[](ForceVideoTimingMode mode) {
36
return QtUtils::GetIconForRegion(
37
(mode == ForceVideoTimingMode::Disabled) ?
38
ConsoleRegion::Auto :
39
((mode == ForceVideoTimingMode::NTSC) ? ConsoleRegion::NTSC_U : ConsoleRegion::PAL));
40
});
41
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false);
42
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardBoot, "BIOS", "FastForwardBoot", false);
43
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false);
44
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardMemoryCardAccess, "MemoryCards",
45
"FastForwardAccess", false);
46
connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &ConsoleSettingsWidget::onFastBootChanged);
47
onFastBootChanged();
48
49
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.cpuExecutionMode, "CPU", "ExecutionMode",
50
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName,
51
&Settings::GetCPUExecutionModeDisplayName,
52
Settings::DEFAULT_CPU_EXECUTION_MODE, CPUExecutionMode::Count);
53
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCPUClockSpeedControl, "CPU", "OverclockEnable", false);
54
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.recompilerICache, "CPU", "RecompilerICache", false);
55
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImageToRAM, "CDROM", "LoadImageToRAM", false);
56
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromAutoDiscChange, "CDROM", "AutoDiscChange", false);
57
58
if (!m_dialog->isPerGameSettings())
59
{
60
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImagePatches, "CDROM", "LoadImagePatches", false);
61
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromIgnoreDriveSubcode, "CDROM", "IgnoreHostSubcode",
62
false);
63
}
64
else
65
{
66
m_ui.cdromGridLayout->removeWidget(m_ui.cdromIgnoreDriveSubcode);
67
delete m_ui.cdromIgnoreDriveSubcode;
68
m_ui.cdromIgnoreDriveSubcode = nullptr;
69
m_ui.cdromGridLayout->removeWidget(m_ui.cdromLoadImagePatches);
70
delete m_ui.cdromLoadImagePatches;
71
m_ui.cdromLoadImagePatches = nullptr;
72
}
73
74
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromSeekSpeedup, "CDROM", "SeekSpeedup", 1,
75
CDROM_SPEEDUP_VALUES);
76
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromReadSpeedup, "CDROM", "ReadSpeedup", 1,
77
CDROM_SPEEDUP_VALUES);
78
79
dialog->registerWidgetHelp(m_ui.region, tr("Region"), tr("Auto-Detect"),
80
tr("Determines the emulated hardware type."));
81
dialog->registerWidgetHelp(
82
m_ui.forceVideoTiming, tr("Frame Rate"), tr("Auto-Detect"),
83
tr("Utilizes the chosen frame timing regardless of the active region. This feature can be used to force PAL games "
84
"to run at 60Hz and NTSC games to run at 50Hz. For most games which have a speed tied to the framerate, this "
85
"will result in the game running approximately 17% faster or slower. For variable frame rate games, it may not "
86
"affect the speed."));
87
m_dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
88
tr("Skips the boot animation. Safe to enable."));
89
m_dialog->registerWidgetHelp(m_ui.fastForwardBoot, tr("Fast Forward Boot"), tr("Unchecked"),
90
tr("Fast forwards through the early loading process when fast booting, saving time. "
91
"Results may vary between games."));
92
dialog->registerWidgetHelp(
93
m_ui.enable8MBRAM, tr("Enable 8MB RAM (Dev Console)"), tr("Unchecked"),
94
tr("Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles. Games have "
95
"to use a larger heap size for "
96
"this additional RAM to be usable. Titles which rely on memory mirrors may break, so it should only be used "
97
"with compatible mods."));
98
m_dialog->registerWidgetHelp(m_ui.fastForwardMemoryCardAccess, tr("Fast Forward Memory Card Access"), tr("Unchecked"),
99
tr("Fast forwards through memory card access, both loading and saving. Can reduce "
100
"waiting times in games that frequently access memory cards."));
101
102
dialog->registerWidgetHelp(m_ui.cpuExecutionMode, tr("Execution Mode"), tr("Recompiler (Fastest)"),
103
tr("Determines how the emulated CPU executes instructions."));
104
dialog->registerWidgetHelp(m_ui.enableCPUClockSpeedControl,
105
tr("Enable Clock Speed Control (Overclocking/Underclocking)"), tr("Unchecked"),
106
tr("When this option is chosen, the clock speed set below will be used."));
107
dialog->registerWidgetHelp(m_ui.cpuClockSpeed, tr("Overclocking Percentage"), tr("100%"),
108
tr("Selects the percentage of the normal clock speed the emulated hardware will run at."));
109
dialog->registerWidgetHelp(m_ui.recompilerICache, tr("Enable Recompiler ICache"), tr("Unchecked"),
110
tr("Simulates stalls in the recompilers when the emulated CPU would have to fetch "
111
"instructions into its cache. Makes games run closer to their console framerate, at a "
112
"small cost to performance. Interpreter mode always simulates the instruction cache."));
113
114
dialog->registerWidgetHelp(
115
m_ui.cdromReadSpeedup, tr("CD-ROM Read Speedup"), tr("None (Double Speed)"),
116
tr("Speeds up CD-ROM reads by the specified factor. Only applies to double-speed reads, and is ignored when audio "
117
"is playing. May improve loading speeds in some games, at the cost of breaking others."));
118
dialog->registerWidgetHelp(
119
m_ui.cdromSeekSpeedup, tr("CD-ROM Seek Speedup"), tr("None (Normal Speed)"),
120
tr("Reduces the simulated time for the CD-ROM sled to move to different areas of the disc. Can improve loading "
121
"times, but crash games which do not expect the CD-ROM to operate faster."));
122
dialog->registerWidgetHelp(
123
m_ui.cdromLoadImageToRAM, tr("Preload Image to RAM"), tr("Unchecked"),
124
tr("Loads the game image into RAM. Useful for network paths that may become unreliable during gameplay. In some "
125
"cases also eliminates stutter when games initiate audio track playback."));
126
dialog->registerWidgetHelp(m_ui.cdromLoadImagePatches, tr("Apply Image Patches"), tr("Unchecked"),
127
tr("Automatically applies patches to disc images when they are present in the same "
128
"directory. Currently only PPF patches are supported with this option."));
129
dialog->registerWidgetHelp(
130
m_ui.cdromAutoDiscChange, tr("Switch to Next Disc on Stop"), tr("Unchecked"),
131
tr("Automatically switches to the next disc in the game when the game stops the CD-ROM motor. No switch will occur "
132
"if the last disc in the game is already selected. <strong>Does not work for all games.</strong>"));
133
dialog->registerWidgetHelp(
134
m_ui.cdromIgnoreDriveSubcode, tr("Ignore Drive Subcode"), tr("Unchecked"),
135
tr("Ignores the subchannel provided by the drive when using physical discs, instead always generating subchannel "
136
"data. Won't work with libcrypt games, but can improve read reliability on some drives."));
137
138
m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false));
139
140
connect(m_ui.enableCPUClockSpeedControl, &QCheckBox::checkStateChanged, this,
141
&ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked);
142
connect(m_ui.cpuClockSpeed, &QSlider::valueChanged, this, &ConsoleSettingsWidget::onCPUClockSpeedValueChanged);
143
144
SettingWidgetBinder::SetAvailability(m_ui.fastBoot, !m_dialog->hasGameTrait(GameDatabase::Trait::ForceFullBoot));
145
SettingWidgetBinder::SetAvailability(m_ui.fastForwardBoot, !m_dialog->hasGameTrait(GameDatabase::Trait::ForceFullBoot));
146
SettingWidgetBinder::SetAvailability(
147
m_ui.cpuExecutionMode, !m_dialog->hasGameTrait(GameDatabase::Trait::ForceInterpreter), m_ui.cpuExecutionModeLabel);
148
SettingWidgetBinder::SetAvailability(m_ui.cdromReadSpeedup,
149
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableCDROMReadSpeedup),
150
m_ui.cdromReadSpeedupLabel);
151
SettingWidgetBinder::SetAvailability(m_ui.cdromSeekSpeedup,
152
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableCDROMSeekSpeedup),
153
m_ui.cdromSeekSpeedupLabel);
154
155
calculateCPUClockValue();
156
}
157
158
ConsoleSettingsWidget::~ConsoleSettingsWidget() = default;
159
160
void ConsoleSettingsWidget::onFastBootChanged()
161
{
162
const bool fast_boot_enabled =
163
m_dialog->getEffectiveBoolValue("BIOS", "PatchFastBoot", Settings::DEFAULT_FAST_BOOT_VALUE);
164
m_ui.fastForwardBoot->setEnabled(fast_boot_enabled);
165
}
166
167
void ConsoleSettingsWidget::updateRecompilerICacheEnabled()
168
{
169
const CPUExecutionMode mode =
170
Settings::ParseCPUExecutionMode(
171
m_dialog
172
->getEffectiveStringValue("CPU", "ExecutionMode",
173
Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE))
174
.c_str())
175
.value_or(Settings::DEFAULT_CPU_EXECUTION_MODE);
176
m_ui.recompilerICache->setEnabled(mode != CPUExecutionMode::Interpreter);
177
}
178
179
void ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked(int state)
180
{
181
if (state == Qt::Checked &&
182
(!m_dialog->isPerGameSettings() || !Host::GetBaseBoolSettingValue("CPU", "OverclockEnable", false)) &&
183
!Host::GetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", false))
184
{
185
const QString message =
186
tr("Enabling CPU overclocking will break games, cause bugs, reduce performance and can significantly increase "
187
"system requirements.\n\nBy enabling this option you are agreeing to not create any bug reports unless you "
188
"have confirmed the bug also occurs with overclocking disabled.\n\nThis warning will only be shown once.");
189
190
QMessageBox mb(QMessageBox::Warning, tr("CPU Overclocking Warning"), message, QMessageBox::NoButton, this);
191
mb.setWindowModality(Qt::WindowModal);
192
const QAbstractButton* const yes_button =
193
mb.addButton(tr("Yes, I will confirm bugs without overclocking before reporting."), QMessageBox::YesRole);
194
mb.addButton(tr("No, take me back to safety."), QMessageBox::NoRole);
195
mb.exec();
196
197
if (mb.clickedButton() != yes_button)
198
{
199
QSignalBlocker sb(m_ui.enableCPUClockSpeedControl);
200
if (m_dialog->isPerGameSettings())
201
{
202
m_ui.enableCPUClockSpeedControl->setCheckState(Qt::PartiallyChecked);
203
m_dialog->removeSettingValue("CPU", "OverclockEnable");
204
}
205
else
206
{
207
m_ui.enableCPUClockSpeedControl->setCheckState(Qt::Unchecked);
208
m_dialog->setBoolSettingValue("CPU", "OverclockEnable", false);
209
}
210
211
return;
212
}
213
214
Host::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true);
215
Host::CommitBaseSettingChanges();
216
}
217
218
m_ui.cpuClockSpeed->setEnabled(m_dialog->getEffectiveBoolValue("CPU", "OverclockEnable", false));
219
updateCPUClockSpeedLabel();
220
}
221
222
void ConsoleSettingsWidget::onCPUClockSpeedValueChanged(int value)
223
{
224
const u32 percent = static_cast<u32>(m_ui.cpuClockSpeed->value());
225
u32 numerator, denominator;
226
Settings::CPUOverclockPercentToFraction(percent, &numerator, &denominator);
227
m_dialog->setIntSettingValue("CPU", "OverclockNumerator", static_cast<int>(numerator));
228
m_dialog->setIntSettingValue("CPU", "OverclockDenominator", static_cast<int>(denominator));
229
updateCPUClockSpeedLabel();
230
}
231
232
void ConsoleSettingsWidget::updateCPUClockSpeedLabel()
233
{
234
const int percent = m_ui.enableCPUClockSpeedControl->isChecked() ? m_ui.cpuClockSpeed->value() : 100;
235
const double frequency = (static_cast<double>(System::MASTER_CLOCK) * static_cast<double>(percent)) / 100.0;
236
m_ui.cpuClockSpeedLabel->setText(tr("%1% (%2MHz)").arg(percent).arg(frequency / 1000000.0, 0, 'f', 2));
237
}
238
239
void ConsoleSettingsWidget::calculateCPUClockValue()
240
{
241
const u32 numerator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockNumerator", 1));
242
const u32 denominator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockDenominator", 1));
243
const u32 percent = Settings::CPUOverclockFractionToPercent(numerator, denominator);
244
QSignalBlocker sb(m_ui.cpuClockSpeed);
245
m_ui.cpuClockSpeed->setValue(static_cast<int>(percent));
246
updateCPUClockSpeedLabel();
247
}
248
249