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