diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index 60d7b64c11..b204908233 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -412,6 +412,17 @@ void EmuThread::reloadGameSettings() } } +void EmuThread::updateEmuFolders() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::updateEmuFolders, Qt::QueuedConnection); + return; + } + + Host::Internal::UpdateEmuFolders(); +} + void EmuThread::loadOurSettings() { m_verbose_status = Host::GetBaseBoolSettingValue("UI", "VerboseStatusBar", false); diff --git a/pcsx2-qt/EmuThread.h b/pcsx2-qt/EmuThread.h index 2c498fc147..135ef2d7d2 100644 --- a/pcsx2-qt/EmuThread.h +++ b/pcsx2-qt/EmuThread.h @@ -70,6 +70,7 @@ public Q_SLOTS: void setSurfaceless(bool surfaceless); void applySettings(); void reloadGameSettings(); + void updateEmuFolders(); void toggleSoftwareRendering(); void switchRenderer(GSRendererType renderer); void changeDisc(const QString& path); diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 0d9fe4cebd..0b23c92e47 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -228,14 +228,6 @@ void QtHost::SetDataDirectory() EmuFolders::DataRoot = EmuFolders::AppRoot; } -void QtHost::UpdateFolders() -{ - // TODO: This should happen with the VM thread paused. - auto lock = Host::GetSettingsLock(); - EmuFolders::LoadConfig(*s_base_settings_interface.get()); - EmuFolders::EnsureFoldersExist(); -} - bool QtHost::InitializeConfig() { if (!SetCriticalFolders()) diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index ca9f8672e5..44319adaeb 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -40,8 +40,6 @@ namespace QtHost bool Initialize(); void Shutdown(); - void UpdateFolders(); - /// Sets batch mode (exit after game shutdown). bool InBatchMode(); void SetBatchMode(bool enabled); diff --git a/pcsx2-qt/SettingWidgetBinder.h b/pcsx2-qt/SettingWidgetBinder.h index 26306c73b5..316e446357 100644 --- a/pcsx2-qt/SettingWidgetBinder.h +++ b/pcsx2-qt/SettingWidgetBinder.h @@ -20,17 +20,23 @@ #include #include +#include #include #include #include +#include #include #include #include +#include "common/Path.h" + +#include "pcsx2/Config.h" #include "pcsx2/HostSettings.h" #include "EmuThread.h" #include "QtHost.h" +#include "QtUtils.h" #include "Settings/SettingsDialog.h" namespace SettingWidgetBinder @@ -228,7 +234,7 @@ namespace SettingWidgetBinder static std::optional getNullableStringValue(const QCheckBox* widget) { return (widget->checkState() == Qt::PartiallyChecked) ? - std::nullopt : + std::nullopt : std::optional(widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0")); } static void setNullableStringValue(QCheckBox* widget, std::optional value) @@ -769,4 +775,70 @@ namespace SettingWidgetBinder } } + template + static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget, + QAbstractButton* browse_button, QAbstractButton* open_button, QAbstractButton* reset_button, + std::string section, std::string key, std::string default_value) + { + using Accessor = SettingAccessor; + + std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())); + if (!Path::IsAbsolute(current_path)) + current_path = Path::Combine(EmuFolders::DataRoot, current_path); + + const QString value(QString::fromStdString(current_path)); + Accessor::setStringValue(widget, value); + + // if we're doing per-game settings, disable the widget, we only allow folder changes in the base config + if (sif) + { + widget->setEnabled(false); + if (browse_button) + browse_button->setEnabled(false); + if (reset_button) + reset_button->setEnabled(false); + return; + } + + Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { + const std::string new_value(Accessor::getStringValue(widget).toStdString()); + if (!new_value.empty()) + { + std::string relative_path(Path::MakeRelative(new_value, EmuFolders::DataRoot)); + QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str()); + } + else + { + QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + } + + g_emu_thread->updateEmuFolders(); + }); + + if (browse_button) + { + QObject::connect(browse_button, &QAbstractButton::clicked, browse_button, [widget, key]() { + const QString path(QDir::toNativeSeparators(QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(widget), + qApp->translate("SettingWidgetBinder", "Select folder for %1").arg(QString::fromStdString(key))))); + if (path.isEmpty()) + return; + + Accessor::setStringValue(widget, path); + }); + } + if (open_button) + { + QObject::connect(open_button, &QAbstractButton::clicked, open_button, [widget]() { + QString path(Accessor::getStringValue(widget)); + if (!path.isEmpty()) + QtUtils::OpenURL(QtUtils::GetRootWidget(widget), QUrl::fromLocalFile(path)); + }); + } + if (reset_button) + { + QObject::connect(reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value)]() { + Accessor::setStringValue(widget, QString::fromStdString(Path::Combine(EmuFolders::AppRoot, default_value))); + }); + } + } } // namespace SettingWidgetBinder diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp index badc2e3d07..6dc7467c98 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp @@ -38,28 +38,17 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent) SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.patchRegion, "EmuCore", "PatchBios", false); SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.regionComboBox, "EmuCore", "PatchRegion", BiosZoneStrings, BiosZoneBytes, BiosZoneBytes[0]); + SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory, + m_ui.resetSearchDirectory, "Folders", "Bios", "bios"); - dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"),tr("Unchecked"), + dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"), tr("Unchecked"), tr("Patches the BIOS region byte in ROM. Not recommended unless you really know what you're doing.")); dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"), tr("Patches the BIOS to skip the console's boot animation.")); - updateSearchDirectory(); refreshList(); - connect(m_ui.searchDirectory, &QLineEdit::textChanged, [this](const QString& text) { - QtHost::SetBaseStringSettingValue("Folders", "Bios", text.toUtf8().constData()); - QtHost::UpdateFolders(); - refreshList(); - }); - connect(m_ui.resetSearchDirectory, &QPushButton::clicked, [this]() { - QtHost::RemoveBaseSettingValue("Folders", "Bios"); - QtHost::UpdateFolders(); - updateSearchDirectory(); - refreshList(); - }); - connect(m_ui.browseSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::browseSearchDirectory); - connect(m_ui.openSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::openSearchDirectory); + connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList); connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList); connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged); @@ -89,27 +78,6 @@ void BIOSSettingsWidget::refreshList() m_refresh_thread->start(); } -void BIOSSettingsWidget::browseSearchDirectory() -{ - QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory( - QtUtils::GetRootWidget(this), tr("Select Directory"), m_ui.searchDirectory->text())); - if (directory.isEmpty()) - return; - - m_ui.searchDirectory->setText(directory); -} - -void BIOSSettingsWidget::openSearchDirectory() -{ - QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectory->text())); -} - -void BIOSSettingsWidget::updateSearchDirectory() -{ - // this will generate a full path - m_ui.searchDirectory->setText(QString::fromStdString(EmuFolders::Bios)); -} - void BIOSSettingsWidget::listRefreshed(const QVector& items) { const std::string selected_bios(Host::GetBaseStringSettingValue("Filenames", "BIOS")); diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.h b/pcsx2-qt/Settings/BIOSSettingsWidget.h index 02b6ed8ac6..6e5be18884 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.h +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.h @@ -47,9 +47,6 @@ public: private Q_SLOTS: void refreshList(); - void browseSearchDirectory(); - void openSearchDirectory(); - void updateSearchDirectory(); void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous); void listRefreshed(const QVector& items); diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.ui b/pcsx2-qt/Settings/BIOSSettingsWidget.ui index f6b069ef77..24abd272fc 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.ui +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.ui @@ -188,7 +188,7 @@ - + diff --git a/pcsx2/HostSettings.cpp b/pcsx2/HostSettings.cpp index 2e0273775e..7844864e61 100644 --- a/pcsx2/HostSettings.cpp +++ b/pcsx2/HostSettings.cpp @@ -15,9 +15,14 @@ #include "PrecompiledHeader.h" #include "common/Assertions.h" +#include "Frontend/LayeredSettingsInterface.h" +#include "GS.h" +#include "GS/Renderers/HW/GSTextureReplacements.h" #include "Host.h" #include "HostSettings.h" -#include "Frontend/LayeredSettingsInterface.h" +#include "MemoryCardFile.h" +#include "Sio.h" +#include "VMManager.h" static std::mutex s_settings_mutex; static LayeredSettingsInterface s_layered_settings_interface; @@ -202,3 +207,45 @@ void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif) std::unique_lock lock(s_settings_mutex); s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif); } + +void Host::Internal::UpdateEmuFolders() +{ + const std::string old_cheats_directory(EmuFolders::Cheats); + const std::string old_cheats_ws_directory(EmuFolders::CheatsWS); + const std::string old_cheats_ni_directory(EmuFolders::CheatsNI); + const std::string old_memcards_directory(EmuFolders::MemoryCards); + const std::string old_textures_directory(EmuFolders::Textures); + + EmuFolders::LoadConfig(*GetBaseSettingsLayer()); + EmuFolders::EnsureFoldersExist(); + + if (VMManager::HasValidVM()) + { + if (EmuFolders::Cheats != old_cheats_directory || + EmuFolders::CheatsWS != old_cheats_ws_directory || + EmuFolders::CheatsNI != old_cheats_ni_directory) + { + VMManager::ReloadPatches(true, true); + } + + if (EmuFolders::MemoryCards != old_memcards_directory) + { + FileMcd_EmuClose(); + FileMcd_EmuOpen(); + + for (u32 port = 0; port < 2; port++) + { + for (u32 slot = 0; slot < 4; slot++) + SetForceMcdEjectTimeoutNow(port, slot); + } + } + + if (EmuFolders::Textures != old_textures_directory) + { + GetMTGS().RunOnGSThread([]() { + if (VMManager::HasValidVM()) + GSTextureReplacements::ReloadReplacementMap(); + }); + } + } +} diff --git a/pcsx2/HostSettings.h b/pcsx2/HostSettings.h index e99c0c0bd5..e23f6b938f 100644 --- a/pcsx2/HostSettings.h +++ b/pcsx2/HostSettings.h @@ -79,5 +79,8 @@ namespace Host /// Sets the input profile settings layer. Called by VMManager when the game changes. void SetInputSettingsLayer(SettingsInterface* sif); + + /// Updates the variables in the EmuFolders namespace, reloading subsystems if needed. Must call with the lock held. + void UpdateEmuFolders(); } // namespace Internal } // namespace Host \ No newline at end of file