Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/duckstation-qt/biossettingswidget.cpp
4246 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "biossettingswidget.h"
5
#include "qthost.h"
6
#include "qtutils.h"
7
#include "settingswindow.h"
8
#include "settingwidgetbinder.h"
9
10
#include "core/bios.h"
11
#include "core/settings.h"
12
13
#include <QtCore/QDir>
14
#include <QtWidgets/QFileDialog>
15
#include <algorithm>
16
17
#include "moc_biossettingswidget.cpp"
18
19
BIOSSettingsWidget::BIOSSettingsWidget(SettingsWindow* dialog, QWidget* parent) : QWidget(parent), m_dialog(dialog)
20
{
21
SettingsInterface* sif = dialog->getSettingsInterface();
22
23
m_ui.setupUi(this);
24
25
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTTYLogging, "BIOS", "TTYLogging", false);
26
27
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.pioDeviceType, "PIO", "DeviceType",
28
&Settings::ParsePIODeviceTypeName, &Settings::GetPIODeviceTypeModeName,
29
&Settings::GetPIODeviceTypeModeDisplayName,
30
Settings::DEFAULT_PIO_DEVICE_TYPE, PIODeviceType::MaxCount);
31
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.pioImagePath, "PIO", "FlashImagePath");
32
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pioSwitchActive, "PIO", "SwitchActive", true);
33
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pioImageWrites, "PIO", "FlashImageWriteEnable", false);
34
connect(m_ui.pioDeviceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
35
&BIOSSettingsWidget::onPIODeviceTypeChanged);
36
connect(m_ui.pioImagePathBrowse, &QPushButton::clicked, this, &BIOSSettingsWidget::onPIOImagePathBrowseClicked);
37
onPIODeviceTypeChanged();
38
39
connect(m_ui.imageNTSCJ, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
40
if (m_dialog->isPerGameSettings() && index == 0)
41
{
42
m_dialog->removeSettingValue("BIOS", "PathNTSCJ");
43
}
44
else
45
{
46
m_dialog->setStringSettingValue("BIOS", "PathNTSCJ",
47
m_ui.imageNTSCJ->itemData(index).toString().toStdString().c_str());
48
}
49
});
50
connect(m_ui.imageNTSCU, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
51
if (m_dialog->isPerGameSettings() && index == 0)
52
{
53
m_dialog->removeSettingValue("BIOS", "PathNTSCU");
54
}
55
else
56
{
57
m_dialog->setStringSettingValue("BIOS", "PathNTSCU",
58
m_ui.imageNTSCU->itemData(index).toString().toStdString().c_str());
59
}
60
});
61
connect(m_ui.imagePAL, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
62
if (m_dialog->isPerGameSettings() && index == 0)
63
{
64
m_dialog->removeSettingValue("BIOS", "PathPAL");
65
}
66
else
67
{
68
m_dialog->setStringSettingValue("BIOS", "PathPAL",
69
m_ui.imagePAL->itemData(index).toString().toStdString().c_str());
70
}
71
});
72
73
connect(m_ui.rescan, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
74
75
if (!m_dialog->isPerGameSettings())
76
{
77
SettingWidgetBinder::BindWidgetToFolderSetting(
78
sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, tr("Select BIOS Directory"), m_ui.searchDirectoryOpen,
79
m_ui.searchDirectoryReset, "BIOS", "SearchDirectory", Path::Combine(EmuFolders::DataRoot, "bios"));
80
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
81
}
82
else
83
{
84
m_ui.mainLayout->removeWidget(m_ui.directoryGroupBox);
85
delete m_ui.directoryGroupBox;
86
m_ui.directoryGroupBox = nullptr;
87
m_ui.directoryGroupBoxLabel = nullptr;
88
m_ui.directoryGroupBoxLayout = nullptr;
89
m_ui.directoryGroupBoxHorizontalLayout = nullptr;
90
m_ui.searchDirectory = nullptr;
91
m_ui.browseSearchDirectory = nullptr;
92
}
93
94
refreshList();
95
96
dialog->registerWidgetHelp(m_ui.pioDeviceType, tr("Device Type"), tr("None"),
97
tr("Simulates a device plugged into the console's parallel port. Usually these are flash "
98
"cartridges, and require some sort of image dump to function."));
99
dialog->registerWidgetHelp(m_ui.pioImagePath, tr("Image Path"), tr("Empty"),
100
tr("Sets the path to the image used for flash cartridges."));
101
dialog->registerWidgetHelp(m_ui.pioSwitchActive, tr("Cartridge Switch On"), tr("Checked"),
102
tr("Simulates the position of the switch on the cartridge. Most cartridges require the "
103
"switch to be on for it to activate on startup."));
104
dialog->registerWidgetHelp(
105
m_ui.pioImageWrites, tr("Allow Image Writes"), tr("Unchecked"),
106
tr("Stores any images made to the cartridge's flash storage back to the host's file system. <strong>This will "
107
"overwrite your cartridge dump,</strong> you should ensure you have a backup first."));
108
dialog->registerWidgetHelp(m_ui.enableTTYLogging, tr("Enable TTY Logging"), tr("Unchecked"),
109
tr("Logs BIOS calls to printf(). Not all games contain debugging messages."));
110
}
111
112
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
113
114
void BIOSSettingsWidget::refreshList()
115
{
116
auto images = BIOS::FindBIOSImagesInDirectory(EmuFolders::Bios.c_str());
117
populateDropDownForRegion(ConsoleRegion::NTSC_J, m_ui.imageNTSCJ, images, m_dialog->isPerGameSettings());
118
populateDropDownForRegion(ConsoleRegion::NTSC_U, m_ui.imageNTSCU, images, m_dialog->isPerGameSettings());
119
populateDropDownForRegion(ConsoleRegion::PAL, m_ui.imagePAL, images, m_dialog->isPerGameSettings());
120
121
setDropDownValue(m_ui.imageNTSCJ, m_dialog->getStringValue("BIOS", "PathNTSCJ", std::nullopt),
122
m_dialog->isPerGameSettings());
123
setDropDownValue(m_ui.imageNTSCU, m_dialog->getStringValue("BIOS", "PathNTSCU", std::nullopt),
124
m_dialog->isPerGameSettings());
125
setDropDownValue(m_ui.imagePAL, m_dialog->getStringValue("BIOS", "PathPAL", std::nullopt),
126
m_dialog->isPerGameSettings());
127
}
128
129
void BIOSSettingsWidget::populateDropDownForRegion(ConsoleRegion region, QComboBox* cb,
130
std::vector<std::pair<std::string, const BIOS::ImageInfo*>>& images,
131
bool per_game)
132
{
133
QSignalBlocker sb(cb);
134
cb->clear();
135
136
if (per_game)
137
cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), tr("Use Global Setting"));
138
139
cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), tr("Auto-Detect"));
140
141
std::sort(images.begin(), images.end(), [region](const auto& left, const auto& right) {
142
const bool left_region_match = (left.second && left.second->region == region);
143
const bool right_region_match = (right.second && right.second->region == region);
144
if (left_region_match && !right_region_match)
145
return true;
146
else if (right_region_match && !left_region_match)
147
return false;
148
149
return left.first < right.first;
150
});
151
152
for (const auto& [name, info] : images)
153
{
154
QString name_str(QString::fromStdString(name));
155
cb->addItem(QtUtils::GetIconForRegion(info ? info->region : ConsoleRegion::Count),
156
QStringLiteral("%1 (%2)")
157
.arg(info ? QString(info->description) : qApp->translate("BIOSSettingsWidget", "Unknown"))
158
.arg(name_str),
159
QVariant(name_str));
160
}
161
}
162
163
void BIOSSettingsWidget::setDropDownValue(QComboBox* cb, const std::optional<std::string>& name, bool per_game)
164
{
165
QSignalBlocker sb(cb);
166
167
if (!name.has_value() || name->empty())
168
{
169
cb->setCurrentIndex((per_game && name.has_value()) ? 1 : 0);
170
return;
171
}
172
173
QString qname(QString::fromStdString(name.value()));
174
for (int i = 1; i < cb->count(); i++)
175
{
176
if (cb->itemData(i) == qname)
177
{
178
cb->setCurrentIndex(i);
179
return;
180
}
181
}
182
183
cb->addItem(qname, QVariant(qname));
184
cb->setCurrentIndex(cb->count() - 1);
185
}
186
187
void BIOSSettingsWidget::onPIODeviceTypeChanged()
188
{
189
const PIODeviceType type =
190
Settings::ParsePIODeviceTypeName(
191
m_dialog
192
->getEffectiveStringValue("PIO", "DeviceType",
193
Settings::GetPIODeviceTypeModeName(Settings::DEFAULT_PIO_DEVICE_TYPE))
194
.c_str())
195
.value_or(Settings::DEFAULT_PIO_DEVICE_TYPE);
196
const bool has_image = (type == PIODeviceType::XplorerCart);
197
const bool has_switch = (type == PIODeviceType::XplorerCart);
198
m_ui.pioImagePathLabel->setEnabled(has_image);
199
m_ui.pioImagePath->setEnabled(has_image);
200
m_ui.pioImagePathBrowse->setEnabled(has_image);
201
m_ui.pioImageWrites->setEnabled(has_image);
202
m_ui.pioSwitchActive->setEnabled(has_switch);
203
}
204
205
void BIOSSettingsWidget::onPIOImagePathBrowseClicked()
206
{
207
const QString path = QDir::toNativeSeparators(
208
QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select PIO Image"), m_ui.pioImagePath->text()));
209
if (path.isEmpty())
210
return;
211
212
m_ui.pioImagePath->setText(path);
213
}
214
215