diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 35b90d2f6..356ba681e 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -199,53 +199,57 @@ void HostInterface::AddFormattedOSDMessage(float duration, const char* format, . AddOSDMessage(std::move(message), duration); } -std::string HostInterface::GetBIOSDirectory() const +std::string HostInterface::GetBIOSDirectory() { + std::string dir = GetStringSettingValue("BIOS", "SearchDirectory", ""); + if (!dir.empty()) + return dir; + return GetUserDirectoryRelativePath("bios"); } std::optional> HostInterface::GetBIOSImage(ConsoleRegion region) { - const std::string* bios_path; + std::string bios_dir = GetBIOSDirectory(); + std::string bios_name; switch (region) { case ConsoleRegion::NTSC_J: - bios_path = &g_settings.bios_path_ntsc_j; + bios_name = GetStringSettingValue("BIOS", "PathNTSCJ", ""); break; case ConsoleRegion::PAL: - bios_path = &g_settings.bios_path_pal; + bios_name = GetStringSettingValue("BIOS", "PAL", ""); break; case ConsoleRegion::NTSC_U: default: - bios_path = &g_settings.bios_path_ntsc_u; + bios_name = GetStringSettingValue("BIOS", "PathNTSCU", ""); break; } - if (bios_path->empty()) + if (bios_name.empty()) { // auto-detect - return FindBIOSImageInDirectory(region, GetBIOSDirectory().c_str()); + return FindBIOSImageInDirectory(region, bios_dir.c_str()); } // try the configured path std::optional image = BIOS::LoadImageFromFile( - StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", GetBIOSDirectory().c_str(), bios_path->c_str()) - .c_str()); + StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", bios_dir.c_str(), bios_name.c_str()).c_str()); if (!image.has_value()) { g_host_interface->ReportFormattedError( g_host_interface->TranslateString("HostInterface", "Failed to load configured BIOS file '%s'"), - bios_path->c_str()); + bios_name.c_str()); return std::nullopt; } BIOS::Hash found_hash = BIOS::GetHash(*image); - Log_DevPrintf("Hash for BIOS '%s': %s", bios_path->c_str(), found_hash.ToString().c_str()); + Log_DevPrintf("Hash for BIOS '%s': %s", bios_name.c_str(), found_hash.ToString().c_str()); if (!BIOS::IsValidHashForRegion(region, found_hash)) - Log_WarningPrintf("Hash for BIOS '%s' does not match region. This may cause issues.", bios_path->c_str()); + Log_WarningPrintf("Hash for BIOS '%s' does not match region. This may cause issues.", bios_name.c_str()); return image; } @@ -330,11 +334,6 @@ HostInterface::FindBIOSImagesInDirectory(const char* directory) return results; } -std::vector> HostInterface::FindBIOSImagesInUserDirectory() -{ - return FindBIOSImagesInDirectory(GetUserDirectoryRelativePath("bios").c_str()); -} - bool HostInterface::LoadState(const char* filename) { std::unique_ptr stream = FileSystem::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); @@ -464,6 +463,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si) si.SetBoolValue("Audio", "Sync", true); si.SetBoolValue("Audio", "DumpOnBoot", false); + si.SetStringValue("BIOS", "SearchDirectory", ""); si.SetStringValue("BIOS", "PathNTSCU", ""); si.SetStringValue("BIOS", "PathNTSCJ", ""); si.SetStringValue("BIOS", "PathPAL", ""); diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 72743b510..b1e7166fe 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -119,7 +119,7 @@ public: virtual std::string TranslateStdString(const char* context, const char* str) const; /// Returns the path to the directory to search for BIOS images. - virtual std::string GetBIOSDirectory() const; + virtual std::string GetBIOSDirectory(); /// Loads the BIOS image for the specified region. std::optional> GetBIOSImage(ConsoleRegion region); @@ -130,7 +130,6 @@ public: /// Returns a list of filenames and descriptions for BIOS images in a directory. std::vector> FindBIOSImagesInDirectory(const char* directory); - std::vector> FindBIOSImagesInUserDirectory(); virtual void OnRunningGameChanged(); virtual void OnSystemPerformanceCountersUpdated(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6622525c7..1db3c91e2 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -157,9 +157,6 @@ void Settings::Load(SettingsInterface& si) gpu_fifo_size = static_cast(si.GetIntValue("Hacks", "GPUFIFOSize", DEFAULT_GPU_FIFO_SIZE)); gpu_max_run_ahead = si.GetIntValue("Hacks", "GPUMaxRunAhead", DEFAULT_GPU_MAX_RUN_AHEAD); - bios_path_ntsc_u = si.GetStringValue("BIOS", "PathNTSCU", ""); - bios_path_ntsc_j = si.GetStringValue("BIOS", "PathNTSCJ", ""); - bios_path_pal = si.GetStringValue("BIOS", "PathPAL", ""); bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", false); bios_patch_fast_boot = si.GetBoolValue("BIOS", "PatchFastBoot", false); @@ -273,9 +270,6 @@ void Settings::Save(SettingsInterface& si) const si.SetIntValue("Hacks", "GPUFIFOSize", gpu_fifo_size); si.SetIntValue("Hacks", "GPUMaxRunAhead", gpu_max_run_ahead); - si.SetStringValue("BIOS", "PathNTSCJ", bios_path_ntsc_j.c_str()); - si.SetStringValue("BIOS", "PathNTSCU", bios_path_ntsc_u.c_str()); - si.SetStringValue("BIOS", "PathPAL", bios_path_pal.c_str()); si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable); si.SetBoolValue("BIOS", "PatchFastBoot", bios_patch_fast_boot); diff --git a/src/core/settings.h b/src/core/settings.h index 199f677c6..ed2879bbe 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -147,9 +147,6 @@ struct Settings // TODO: Controllers, memory cards, etc. - std::string bios_path_ntsc_j; - std::string bios_path_ntsc_u; - std::string bios_path_pal; bool bios_patch_tty_enable = false; bool bios_patch_fast_boot = false; diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index 24b953722..fa4039d29 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -646,7 +646,7 @@ bool LibretroHostInterface::HasCoreVariablesChanged() return (g_retro_environment_callback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &changed) && changed); } -std::string LibretroHostInterface::GetBIOSDirectory() const +std::string LibretroHostInterface::GetBIOSDirectory() { // Assume BIOS files are located in system directory. const char* system_directory = nullptr; diff --git a/src/duckstation-libretro/libretro_host_interface.h b/src/duckstation-libretro/libretro_host_interface.h index c0671c7a0..e4cb649c1 100644 --- a/src/duckstation-libretro/libretro_host_interface.h +++ b/src/duckstation-libretro/libretro_host_interface.h @@ -28,7 +28,7 @@ public: std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const override; std::string GetShaderCacheBasePath() const override; std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") override; - std::string GetBIOSDirectory() const override; + std::string GetBIOSDirectory() override; // Called by frontend void retro_get_system_av_info(struct retro_system_av_info* info); diff --git a/src/duckstation-qt/biossettingswidget.cpp b/src/duckstation-qt/biossettingswidget.cpp index 5da0d57f1..31c76032d 100644 --- a/src/duckstation-qt/biossettingswidget.cpp +++ b/src/duckstation-qt/biossettingswidget.cpp @@ -1,6 +1,7 @@ #include "biossettingswidget.h" #include "core/bios.h" #include "qthostinterface.h" +#include "qtutils.h" #include "settingsdialog.h" #include "settingwidgetbinder.h" #include @@ -9,6 +10,9 @@ static void populateDropDownForRegion(ConsoleRegion region, QComboBox* cb, std::vector>& images) { + QSignalBlocker sb(cb); + cb->clear(); + cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), QT_TRANSLATE_NOOP("BIOSSettingsWidget", "Auto-Detect")); @@ -94,14 +98,7 @@ BIOSSettingsWidget::BIOSSettingsWidget(QtHostInterface* host_interface, QWidget* tr("Patches the BIOS to skip the console's boot animation. Does not work with all games, " "but usually safe to enabled.")); - auto images = m_host_interface->FindBIOSImagesInUserDirectory(); - populateDropDownForRegion(ConsoleRegion::NTSC_J, m_ui.imageNTSCJ, images); - populateDropDownForRegion(ConsoleRegion::NTSC_U, m_ui.imageNTSCU, images); - populateDropDownForRegion(ConsoleRegion::PAL, m_ui.imagePAL, images); - - setDropDownValue(m_ui.imageNTSCJ, m_host_interface->GetStringSettingValue("BIOS", "PathNTSCJ", "")); - setDropDownValue(m_ui.imageNTSCU, m_host_interface->GetStringSettingValue("BIOS", "PathNTSCU", "")); - setDropDownValue(m_ui.imagePAL, m_host_interface->GetStringSettingValue("BIOS", "PathPAL", "")); + refreshList(); connect(m_ui.imageNTSCJ, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { m_host_interface->SetStringSettingValue("BIOS", "PathNTSCJ", @@ -118,6 +115,52 @@ BIOSSettingsWidget::BIOSSettingsWidget(QtHostInterface* host_interface, QWidget* m_ui.imagePAL->itemData(index).toString().toStdString().c_str()); m_host_interface->applySettings(); }); + + connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList); + + std::string current_search_directory = g_host_interface->GetBIOSDirectory(); + m_ui.searchDirectory->setText(QString::fromStdString(current_search_directory)); + connect(m_ui.searchDirectory, &QLineEdit::textChanged, [this](const QString& text) { + if (text.isEmpty()) + { + m_host_interface->RemoveSettingValue("BIOS", "SearchDirectory"); + } + else + { + m_host_interface->SetStringSettingValue("BIOS", "SearchDirectory", text.toStdString().c_str()); + } + refreshList(); + }); + connect(m_ui.browseSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::browseSearchDirectory); + connect(m_ui.openSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::openSearchDirectory); } BIOSSettingsWidget::~BIOSSettingsWidget() = default; + +void BIOSSettingsWidget::refreshList() +{ + auto images = m_host_interface->FindBIOSImagesInDirectory(m_host_interface->GetBIOSDirectory().c_str()); + populateDropDownForRegion(ConsoleRegion::NTSC_J, m_ui.imageNTSCJ, images); + populateDropDownForRegion(ConsoleRegion::NTSC_U, m_ui.imageNTSCU, images); + populateDropDownForRegion(ConsoleRegion::PAL, m_ui.imagePAL, images); + + setDropDownValue(m_ui.imageNTSCJ, m_host_interface->GetStringSettingValue("BIOS", "PathNTSCJ", "")); + setDropDownValue(m_ui.imageNTSCU, m_host_interface->GetStringSettingValue("BIOS", "PathNTSCU", "")); + setDropDownValue(m_ui.imagePAL, m_host_interface->GetStringSettingValue("BIOS", "PathPAL", "")); +} + +void BIOSSettingsWidget::browseSearchDirectory() +{ + QString directory = QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(this), tr("Select Directory"), + m_ui.searchDirectory->text()); + if (directory.isEmpty()) + return; + + m_ui.searchDirectory->setText(directory); +} + +void BIOSSettingsWidget::openSearchDirectory() +{ + QString dir = QString::fromStdString(m_host_interface->GetBIOSDirectory()); + QtUtils::OpenURL(this, QUrl::fromLocalFile(dir)); +} diff --git a/src/duckstation-qt/biossettingswidget.h b/src/duckstation-qt/biossettingswidget.h index 7e324f40e..39f604e31 100644 --- a/src/duckstation-qt/biossettingswidget.h +++ b/src/duckstation-qt/biossettingswidget.h @@ -15,6 +15,11 @@ public: explicit BIOSSettingsWidget(QtHostInterface* host_interface, QWidget* parent, SettingsDialog* dialog); ~BIOSSettingsWidget(); +private Q_SLOTS: + void refreshList(); + void browseSearchDirectory(); + void openSearchDirectory(); + private: Ui::BIOSSettingsWidget m_ui; diff --git a/src/duckstation-qt/biossettingswidget.ui b/src/duckstation-qt/biossettingswidget.ui index e89b3298b..c728c0949 100644 --- a/src/duckstation-qt/biossettingswidget.ui +++ b/src/duckstation-qt/biossettingswidget.ui @@ -6,8 +6,8 @@ 0 0 - 472 - 259 + 529 + 330 @@ -32,13 +32,6 @@ BIOS Selection - - - - NTSC-J (Japan): - - - @@ -49,23 +42,6 @@ - - - - NTSC-U/C (US/Canada): - - - - - - - - 0 - 0 - - - - @@ -73,6 +49,20 @@ + + + + NTSC-J (Japan): + + + + + + + NTSC-U/C (US/Canada): + + + @@ -83,6 +73,80 @@ + + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Open in Explorer... + + + + + + + Refresh List + + + + + + + + + + + + BIOS Directory + + + + + + DuckStation will search for BIOS images in this directory. + + + true + + + + + + + + + + + + Browse... + + + + +