From 73cee9f7052cb7feb56e311d35a95e4633e972f6 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 10 Jan 2024 13:35:06 +1000 Subject: [PATCH] Settings: Add UserResources to EmuFolders Allowing some resources, such as fonts/sounds to be overridden by the user. --- src/core/achievements.cpp | 6 +-- src/core/cheats.cpp | 2 +- src/core/game_database.cpp | 8 ++-- src/core/settings.cpp | 49 +++++++++++++++++------- src/core/settings.h | 15 ++++++-- src/duckstation-nogui/nogui_host.cpp | 23 +++++++---- src/duckstation-qt/qthost.cpp | 23 +++++++---- src/duckstation-regtest/regtest_host.cpp | 8 ++-- src/util/host.h | 8 ++-- src/util/imgui_fullscreen.cpp | 2 +- src/util/imgui_manager.cpp | 8 ++-- src/util/postprocessing.cpp | 4 +- src/util/postprocessing_shader_fx.cpp | 6 +-- src/util/sdl_input_source.cpp | 2 +- 14 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index 8b81324de..9eb614580 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -1022,7 +1022,7 @@ void Achievements::DisplayAchievementSummary() // Technically not going through the resource API, but since we're passing this to something else, we can't. if (g_settings.achievements_sound_effects) - PlatformMisc::PlaySoundAsync(Path::Combine(EmuFolders::Resources, INFO_SOUND_NAME).c_str()); + PlatformMisc::PlaySoundAsync(EmuFolders::GetOverridableResourcePath(INFO_SOUND_NAME).c_str()); } void Achievements::DisplayHardcoreDeferredMessage() @@ -1069,7 +1069,7 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event) } if (g_settings.achievements_sound_effects) - PlatformMisc::PlaySoundAsync(Path::Combine(EmuFolders::Resources, UNLOCK_SOUND_NAME).c_str()); + PlatformMisc::PlaySoundAsync(EmuFolders::GetOverridableResourcePath(UNLOCK_SOUND_NAME).c_str()); } void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event) @@ -1144,7 +1144,7 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even } if (g_settings.achievements_sound_effects) - PlatformMisc::PlaySoundAsync(Path::Combine(EmuFolders::Resources, LBSUBMIT_SOUND_NAME).c_str()); + PlatformMisc::PlaySoundAsync(EmuFolders::GetOverridableResourcePath(LBSUBMIT_SOUND_NAME).c_str()); } void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* event) diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 228dc691b..bf3926775 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -689,7 +689,7 @@ bool CheatList::SaveToPCSXRFile(const char* filename) bool CheatList::LoadFromPackage(const std::string& serial) { - const std::optional db_string(Host::ReadResourceFileToString("chtdb.txt")); + const std::optional db_string(Host::ReadResourceFileToString("chtdb.txt", false)); if (!db_string.has_value()) return false; diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index b1836be4b..08b2fecf8 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -580,7 +580,7 @@ bool GameDatabase::LoadFromCache() return false; } - const u64 gamedb_ts = Host::GetResourceFileTimestamp("gamedb.json").value_or(0); + const u64 gamedb_ts = Host::GetResourceFileTimestamp("gamedb.json", false).value_or(0); u32 signature, version, num_entries, num_codes; u64 file_gamedb_ts; @@ -674,7 +674,7 @@ bool GameDatabase::LoadFromCache() bool GameDatabase::SaveToCache() { - const u64 gamedb_ts = Host::GetResourceFileTimestamp("gamedb.json").value_or(0); + const u64 gamedb_ts = Host::GetResourceFileTimestamp("gamedb.json", false).value_or(0); std::unique_ptr stream( ByteStream::OpenFile(GetCacheFile().c_str(), BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | @@ -830,7 +830,7 @@ static std::optional GetOptionalFloatFromObject(const rapidjson::Value& o bool GameDatabase::LoadGameDBJson() { - std::optional gamedb_data(Host::ReadResourceFileToString("gamedb.json")); + std::optional gamedb_data(Host::ReadResourceFileToString("gamedb.json", false)); if (!gamedb_data.has_value()) { Log_ErrorPrintf("Failed to read game database"); @@ -1082,7 +1082,7 @@ void GameDatabase::EnsureTrackHashesMapLoaded() bool GameDatabase::LoadTrackHashes() { - std::optional gamedb_data(Host::ReadResourceFileToString("gamedb.json")); + std::optional gamedb_data(Host::ReadResourceFileToString("gamedb.json", false)); if (!gamedb_data.has_value()) { Log_ErrorPrintf("Failed to read game database"); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 040126ada..bbcfa7155 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "settings.h" @@ -1504,6 +1504,7 @@ std::string EmuFolders::SaveStates; std::string EmuFolders::Screenshots; std::string EmuFolders::Shaders; std::string EmuFolders::Textures; +std::string EmuFolders::UserResources; void EmuFolders::SetDefaults() { @@ -1519,6 +1520,7 @@ void EmuFolders::SetDefaults() Screenshots = Path::Combine(DataRoot, "screenshots"); Shaders = Path::Combine(DataRoot, "shaders"); Textures = Path::Combine(DataRoot, "textures"); + UserResources = Path::Combine(DataRoot, "resources"); } static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* section, @@ -1546,19 +1548,22 @@ void EmuFolders::LoadConfig(SettingsInterface& si) Screenshots = LoadPathFromSettings(si, DataRoot, "Folders", "Screenshots", "screenshots"); Shaders = LoadPathFromSettings(si, DataRoot, "Folders", "Shaders", "shaders"); Textures = LoadPathFromSettings(si, DataRoot, "Folders", "Textures", "textures"); + UserResources = LoadPathFromSettings(si, DataRoot, "Folders", "UserResources", "resources"); - Log_DevPrintf("BIOS Directory: %s", Bios.c_str()); - Log_DevPrintf("Cache Directory: %s", Cache.c_str()); - Log_DevPrintf("Cheats Directory: %s", Cheats.c_str()); - Log_DevPrintf("Covers Directory: %s", Covers.c_str()); - Log_DevPrintf("Dumps Directory: %s", Dumps.c_str()); - Log_DevPrintf("Game Settings Directory: %s", GameSettings.c_str()); - Log_DevPrintf("Input Profile Directory: %s", InputProfiles.c_str()); - Log_DevPrintf("MemoryCards Directory: %s", MemoryCards.c_str()); - Log_DevPrintf("SaveStates Directory: %s", SaveStates.c_str()); - Log_DevPrintf("Screenshots Directory: %s", Screenshots.c_str()); - Log_DevPrintf("Shaders Directory: %s", Shaders.c_str()); - Log_DevPrintf("Textures Directory: %s", Textures.c_str()); + Log_DevFmt("BIOS Directory: {}", Bios); + Log_DevFmt("Cache Directory: {}", Cache); + Log_DevFmt("Cheats Directory: {}", Cheats); + Log_DevFmt("Covers Directory: {}", Covers); + Log_DevFmt("Dumps Directory: {}", Dumps); + Log_DevFmt("Game Settings Directory: {}", GameSettings); + Log_DevFmt("Input Profile Directory: {}", InputProfiles); + Log_DevFmt("MemoryCards Directory: {}", MemoryCards); + Log_DevFmt("Resources Directory: {}", Resources); + Log_DevFmt("SaveStates Directory: {}", SaveStates); + Log_DevFmt("Screenshots Directory: {}", Screenshots); + Log_DevFmt("Shaders Directory: {}", Shaders); + Log_DevFmt("Textures Directory: {}", Textures); + Log_DevFmt("User Resources Directory: {}", UserResources); } void EmuFolders::Save(SettingsInterface& si) @@ -1576,6 +1581,7 @@ void EmuFolders::Save(SettingsInterface& si) si.SetStringValue("Folders", "Screenshots", Path::MakeRelative(Screenshots, DataRoot).c_str()); si.SetStringValue("Folders", "Shaders", Path::MakeRelative(Shaders, DataRoot).c_str()); si.SetStringValue("Folders", "Textures", Path::MakeRelative(Textures, DataRoot).c_str()); + si.SetStringValue("Folders", "UserResources", Path::MakeRelative(UserResources, DataRoot).c_str()); } void EmuFolders::Update() @@ -1623,9 +1629,26 @@ bool EmuFolders::EnsureFoldersExist() Path::Combine(Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Textures").c_str(), false) && result; result = FileSystem::EnsureDirectoryExists(Textures.c_str(), false) && result; + result = FileSystem::EnsureDirectoryExists(UserResources.c_str(), false) && result; return result; } +std::string EmuFolders::GetOverridableResourcePath(std::string_view name) +{ + std::string upath = Path::Combine(UserResources, name); + if (FileSystem::FileExists(upath.c_str())) + { + if (UserResources != Resources) + Log_WarningFmt("Using user-provided resource file {}", name); + } + else + { + upath = Path::Combine(Resources, name); + } + + return upath; +} + static const char* s_log_filters[] = { "Achievements", "AnalogController", diff --git a/src/core/settings.h b/src/core/settings.h index 08a6c3128..aa4445219 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -1,15 +1,20 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once + +#include "types.h" + +#include "util/audio_stream.h" + #include "common/log.h" #include "common/settings_interface.h" #include "common/small_string.h" -#include "types.h" -#include "util/audio_stream.h" + #include #include #include +#include #include enum class RenderAPI : u32; @@ -525,6 +530,7 @@ extern std::string SaveStates; extern std::string Screenshots; extern std::string Shaders; extern std::string Textures; +extern std::string UserResources; // Assumes that AppRoot and DataRoot have been initialized. void SetDefaults(); @@ -534,4 +540,7 @@ void Save(SettingsInterface& si); /// Updates the variables in the EmuFolders namespace, reloading subsystems if needed. void Update(); + +/// Returns the path to a resource file, allowing the user to override it. +std::string GetOverridableResourcePath(std::string_view name); } // namespace EmuFolders diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp index ec251fa27..284593ffe 100644 --- a/src/duckstation-nogui/nogui_host.cpp +++ b/src/duckstation-nogui/nogui_host.cpp @@ -75,6 +75,7 @@ static void SetResourcesDirectory(); static void SetDataDirectory(); static bool SetCriticalFolders(); static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller); +static std::string GetResourcePath(std::string_view name, bool allow_override); static void StartCPUThread(); static void StopCPUThread(); static void ProcessCPUThreadEvents(bool block); @@ -360,33 +361,39 @@ s32 Host::Internal::GetTranslatedStringImpl(const std::string_view& context, con return static_cast(msg.size()); } -bool Host::ResourceFileExists(const char* filename) +ALWAYS_INLINE std::string NoGUIHost::GetResourcePath(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + return allow_override ? EmuFolders::GetOverridableResourcePath(filename) : + Path::Combine(EmuFolders::Resources, filename); +} + +bool Host::ResourceFileExists(std::string_view filename, bool allow_override) +{ + const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); return FileSystem::FileExists(path.c_str()); } -std::optional> Host::ReadResourceFile(const char* filename) +std::optional> Host::ReadResourceFile(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); std::optional> ret(FileSystem::ReadBinaryFile(path.c_str())); if (!ret.has_value()) Log_ErrorPrintf("Failed to read resource file '%s'", filename); return ret; } -std::optional Host::ReadResourceFileToString(const char* filename) +std::optional Host::ReadResourceFileToString(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); std::optional ret(FileSystem::ReadFileToString(path.c_str())); if (!ret.has_value()) Log_ErrorPrintf("Failed to read resource file to string '%s'", filename); return ret; } -std::optional Host::GetResourceFileTimestamp(const char* filename) +std::optional Host::GetResourceFileTimestamp(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); FILESYSTEM_STAT_DATA sd; if (!FileSystem::StatFile(path.c_str(), &sd)) { diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index d6f644b72..25455bede 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -90,6 +90,7 @@ static bool SetCriticalFolders(); static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller); static void SaveSettings(); static bool RunSetupWizard(); +static std::string GetResourcePath(std::string_view name, bool allow_override); static void InitializeEarlyConsole(); static void HookSignals(); static void PrintCommandLineVersion(); @@ -1442,33 +1443,39 @@ void Host::OnInputDeviceDisconnected(const std::string_view& identifier) identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size())); } -bool Host::ResourceFileExists(const char* filename) +ALWAYS_INLINE std::string QtHost::GetResourcePath(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + return allow_override ? EmuFolders::GetOverridableResourcePath(filename) : Path::Combine(EmuFolders::Resources, filename); +} + +bool Host::ResourceFileExists(std::string_view filename, bool allow_override) +{ + const std::string path = QtHost::GetResourcePath(filename, allow_override); return FileSystem::FileExists(path.c_str()); } -std::optional> Host::ReadResourceFile(const char* filename) +std::optional> Host::ReadResourceFile(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path = QtHost::GetResourcePath(filename, allow_override); std::optional> ret(FileSystem::ReadBinaryFile(path.c_str())); if (!ret.has_value()) Log_ErrorPrintf("Failed to read resource file '%s'", filename); return ret; } -std::optional Host::ReadResourceFileToString(const char* filename) +std::optional Host::ReadResourceFileToString(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path = QtHost::GetResourcePath(filename, allow_override); std::optional ret(FileSystem::ReadFileToString(path.c_str())); if (!ret.has_value()) Log_ErrorPrintf("Failed to read resource file to string '%s'", filename); return ret; } -std::optional Host::GetResourceFileTimestamp(const char* filename) +std::optional Host::GetResourceFileTimestamp(std::string_view filename, bool allow_override) { - const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path = QtHost::GetResourcePath(filename, allow_override); + FILESYSTEM_STAT_DATA sd; if (!FileSystem::StatFile(path.c_str(), &sd)) { diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index 4333143e3..3b8df9b91 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -181,13 +181,13 @@ void Host::CommitBaseSettingChanges() // noop, in memory } -bool Host::ResourceFileExists(const char* filename) +bool Host::ResourceFileExists(std::string_view filename, bool allow_override) { const std::string path(Path::Combine(EmuFolders::Resources, filename)); return FileSystem::FileExists(path.c_str()); } -std::optional> Host::ReadResourceFile(const char* filename) +std::optional> Host::ReadResourceFile(std::string_view filename, bool allow_override) { const std::string path(Path::Combine(EmuFolders::Resources, filename)); std::optional> ret(FileSystem::ReadBinaryFile(path.c_str())); @@ -196,7 +196,7 @@ std::optional> Host::ReadResourceFile(const char* filename) return ret; } -std::optional Host::ReadResourceFileToString(const char* filename) +std::optional Host::ReadResourceFileToString(std::string_view filename, bool allow_override) { const std::string path(Path::Combine(EmuFolders::Resources, filename)); std::optional ret(FileSystem::ReadFileToString(path.c_str())); @@ -205,7 +205,7 @@ std::optional Host::ReadResourceFileToString(const char* filename) return ret; } -std::optional Host::GetResourceFileTimestamp(const char* filename) +std::optional Host::GetResourceFileTimestamp(std::string_view filename, bool allow_override) { const std::string path(Path::Combine(EmuFolders::Resources, filename)); FILESYSTEM_STAT_DATA sd; diff --git a/src/util/host.h b/src/util/host.h index 707b8b890..5ae509604 100644 --- a/src/util/host.h +++ b/src/util/host.h @@ -13,17 +13,17 @@ namespace Host { /// Returns true if the specified resource file exists. -bool ResourceFileExists(const char* filename); +bool ResourceFileExists(std::string_view filename, bool allow_override); /// Reads a file from the resources directory of the application. /// This may be outside of the "normal" filesystem on platforms such as Mac. -std::optional> ReadResourceFile(const char* filename); +std::optional> ReadResourceFile(std::string_view filename, bool allow_override); /// Reads a resource file file from the resources directory as a string. -std::optional ReadResourceFileToString(const char* filename); +std::optional ReadResourceFileToString(std::string_view filename, bool allow_override); /// Returns the modified time of a resource. -std::optional GetResourceFileTimestamp(const char* filename); +std::optional GetResourceFileTimestamp(std::string_view filename, bool allow_override); /// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller. void ReportErrorAsync(const std::string_view& title, const std::string_view& message); diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index 047d01456..c616655ef 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -274,7 +274,7 @@ std::optional ImGuiFullscreen::LoadTextureImage(const char* if (Path::IsAbsolute(path)) data = FileSystem::ReadBinaryFile(path); else - data = Host::ReadResourceFile(path); + data = Host::ReadResourceFile(path, true); if (data.has_value()) { image = Common::RGBA8Image(); diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp index a531f7eaa..51c4192e6 100644 --- a/src/util/imgui_manager.cpp +++ b/src/util/imgui_manager.cpp @@ -477,7 +477,7 @@ bool ImGuiManager::LoadFontData() if (s_standard_font_data.empty()) { std::optional> font_data = s_font_path.empty() ? - Host::ReadResourceFile("fonts/Roboto-Regular.ttf") : + Host::ReadResourceFile("fonts/Roboto-Regular.ttf", true) : FileSystem::ReadBinaryFile(s_font_path.c_str()); if (!font_data.has_value()) return false; @@ -487,7 +487,7 @@ bool ImGuiManager::LoadFontData() if (s_fixed_font_data.empty()) { - std::optional> font_data = Host::ReadResourceFile("fonts/RobotoMono-Medium.ttf"); + std::optional> font_data = Host::ReadResourceFile("fonts/RobotoMono-Medium.ttf", true); if (!font_data.has_value()) return false; @@ -496,7 +496,7 @@ bool ImGuiManager::LoadFontData() if (s_icon_fa_font_data.empty()) { - std::optional> font_data = Host::ReadResourceFile("fonts/fa-solid-900.ttf"); + std::optional> font_data = Host::ReadResourceFile("fonts/fa-solid-900.ttf", true); if (!font_data.has_value()) return false; @@ -505,7 +505,7 @@ bool ImGuiManager::LoadFontData() if (s_icon_pf_font_data.empty()) { - std::optional> font_data = Host::ReadResourceFile("fonts/promptfont.otf"); + std::optional> font_data = Host::ReadResourceFile("fonts/promptfont.otf", true); if (!font_data.has_value()) return false; diff --git a/src/util/postprocessing.cpp b/src/util/postprocessing.cpp index 5b6823537..e01d02e96 100644 --- a/src/util/postprocessing.cpp +++ b/src/util/postprocessing.cpp @@ -402,7 +402,7 @@ std::unique_ptr PostProcessing::TryLoadingShader(const s filename = fmt::format("shaders/reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name); - resource_str = Host::ReadResourceFileToString(filename.c_str()); + resource_str = Host::ReadResourceFileToString(filename.c_str(), true); if (resource_str.has_value()) { std::unique_ptr shader = std::make_unique(); @@ -414,7 +414,7 @@ std::unique_ptr PostProcessing::TryLoadingShader(const s } filename = fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name); - resource_str = Host::ReadResourceFileToString(filename.c_str()); + resource_str = Host::ReadResourceFileToString(filename.c_str(), true); if (resource_str.has_value()) { std::unique_ptr shader = std::make_unique(); diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp index 73701ce4d..b39b2424d 100644 --- a/src/util/postprocessing_shader_fx.cpp +++ b/src/util/postprocessing_shader_fx.cpp @@ -45,7 +45,7 @@ static bool PreprocessorFileExistsCallback(const std::string& path) if (Path::IsAbsolute(path)) return FileSystem::FileExists(path.c_str()); - return Host::ResourceFileExists(path.c_str()); + return Host::ResourceFileExists(path.c_str(), true); } static bool PreprocessorReadFileCallback(const std::string& path, std::string& data) @@ -54,7 +54,7 @@ static bool PreprocessorReadFileCallback(const std::string& path, std::string& d if (Path::IsAbsolute(path)) rdata = FileSystem::ReadFileToString(path.c_str()); else - rdata = Host::ReadResourceFileToString(path.c_str()); + rdata = Host::ReadResourceFileToString(path.c_str(), true); if (!rdata.has_value()) return false; @@ -935,7 +935,7 @@ bool PostProcessing::ReShadeFXShader::CreatePasses(GPUTexture::Format backbuffer { // Might be a base file/resource instead. const std::string resource_name = Path::Combine("shaders/reshade/Textures", source); - if (std::optional> resdata = Host::ReadResourceFile(resource_name.c_str()); + if (std::optional> resdata = Host::ReadResourceFile(resource_name.c_str(), true); !resdata.has_value() || !image.LoadFromBuffer(resource_name.c_str(), resdata->data(), resdata->size())) { Error::SetString(error, fmt::format("Failed to load image '{}' (from '{}')", source, image_path).c_str()); diff --git a/src/util/sdl_input_source.cpp b/src/util/sdl_input_source.cpp index 17cc698eb..4db534bbe 100644 --- a/src/util/sdl_input_source.cpp +++ b/src/util/sdl_input_source.cpp @@ -243,7 +243,7 @@ void SDLInputSource::SetHints() Log_InfoFmt("Using Controller DB from user directory: '{}'", upath); SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, upath.c_str()); } - else if (const std::string rpath = Path::Combine(EmuFolders::Resources, CONTROLLER_DB_FILENAME); + else if (const std::string rpath = EmuFolders::GetOverridableResourcePath(CONTROLLER_DB_FILENAME); FileSystem::FileExists(rpath.c_str())) { Log_InfoPrint("Using Controller DB from resources.");