diff --git a/pcsx2/Achievements.cpp b/pcsx2/Achievements.cpp index 88291ea58a..42f209395d 100644 --- a/pcsx2/Achievements.cpp +++ b/pcsx2/Achievements.cpp @@ -1027,7 +1027,7 @@ void Achievements::DisplayAchievementSummary() // Technically not going through the resource API, but since we're passing this to something else, we can't. if (EmuConfig.Achievements.SoundEffects) - Common::PlaySoundAsync(Path::Combine(EmuFolders::Resources, INFO_SOUND_NAME).c_str()); + Common::PlaySoundAsync(EmuFolders::GetOverridableResourcePath(INFO_SOUND_NAME).c_str()); } void Achievements::DisplayHardcoreDeferredMessage() @@ -1079,7 +1079,7 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event) } if (EmuConfig.Achievements.SoundEffects) - Common::PlaySoundAsync(Path::Combine(EmuFolders::Resources, UNLOCK_SOUND_NAME).c_str()); + Common::PlaySoundAsync(EmuFolders::GetOverridableResourcePath(UNLOCK_SOUND_NAME).c_str()); } void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event) @@ -1170,7 +1170,7 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even } if (EmuConfig.Achievements.SoundEffects) - Common::PlaySoundAsync(Path::Combine(EmuFolders::Resources, LBSUBMIT_SOUND_NAME).c_str()); + Common::PlaySoundAsync(EmuFolders::GetOverridableResourcePath(LBSUBMIT_SOUND_NAME).c_str()); } void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* event) diff --git a/pcsx2/Config.h b/pcsx2/Config.h index b84fdb1c4d..d7293f43c7 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -1225,6 +1225,7 @@ namespace EmuFolders extern std::string Cheats; extern std::string Patches; extern std::string Resources; + extern std::string UserResources; extern std::string Cache; extern std::string Covers; extern std::string GameSettings; @@ -1241,7 +1242,10 @@ namespace EmuFolders bool EnsureFoldersExist(); /// Opens the specified log file for writing. - std::FILE* OpenLogFile(const std::string_view& name, const char* mode); + std::FILE* OpenLogFile(std::string_view name, const char* mode); + + /// 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/pcsx2/ImGui/ImGuiManager.cpp b/pcsx2/ImGui/ImGuiManager.cpp index 41d7fdbc72..1bde333456 100644 --- a/pcsx2/ImGui/ImGuiManager.cpp +++ b/pcsx2/ImGui/ImGuiManager.cpp @@ -395,7 +395,7 @@ bool ImGuiManager::LoadFontData() { std::optional> font_data = s_font_path.empty() ? FileSystem::ReadBinaryFile( - Path::Combine(EmuFolders::Resources, "fonts" FS_OSPATH_SEPARATOR_STR "Roboto-Regular.ttf").c_str()) : + EmuFolders::GetOverridableResourcePath("fonts" FS_OSPATH_SEPARATOR_STR "Roboto-Regular.ttf").c_str()) : FileSystem::ReadBinaryFile(s_font_path.c_str()); if (!font_data.has_value()) return false; @@ -406,7 +406,7 @@ bool ImGuiManager::LoadFontData() if (s_fixed_font_data.empty()) { std::optional> font_data = FileSystem::ReadBinaryFile( - Path::Combine(EmuFolders::Resources, "fonts" FS_OSPATH_SEPARATOR_STR "RobotoMono-Medium.ttf").c_str()); + EmuFolders::GetOverridableResourcePath("fonts" FS_OSPATH_SEPARATOR_STR "RobotoMono-Medium.ttf").c_str()); if (!font_data.has_value()) return false; @@ -416,7 +416,7 @@ bool ImGuiManager::LoadFontData() if (s_icon_fa_font_data.empty()) { std::optional> font_data = - FileSystem::ReadBinaryFile(Path::Combine(EmuFolders::Resources, "fonts" FS_OSPATH_SEPARATOR_STR "fa-solid-900.ttf").c_str()); + FileSystem::ReadBinaryFile(EmuFolders::GetOverridableResourcePath("fonts" FS_OSPATH_SEPARATOR_STR "fa-solid-900.ttf").c_str()); if (!font_data.has_value()) return false; @@ -426,7 +426,7 @@ bool ImGuiManager::LoadFontData() if (s_icon_pf_font_data.empty()) { std::optional> font_data = - FileSystem::ReadBinaryFile(Path::Combine(EmuFolders::Resources, "fonts" FS_OSPATH_SEPARATOR_STR "promptfont.otf").c_str()); + FileSystem::ReadBinaryFile(EmuFolders::GetOverridableResourcePath("fonts" FS_OSPATH_SEPARATOR_STR "promptfont.otf").c_str()); if (!font_data.has_value()) return false; diff --git a/pcsx2/Input/SDLInputSource.cpp b/pcsx2/Input/SDLInputSource.cpp index 7be7d706fc..5f9a814db4 100644 --- a/pcsx2/Input/SDLInputSource.cpp +++ b/pcsx2/Input/SDLInputSource.cpp @@ -231,7 +231,7 @@ void SDLInputSource::SetHints() Console.WriteLn(Color_StrongGreen, fmt::format("SDLInputSource: 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); FileSystem::FileExists(rpath.c_str())) + else if (const std::string rpath = EmuFolders::GetOverridableResourcePath(CONTROLLER_DB_FILENAME); FileSystem::FileExists(rpath.c_str())) { Console.WriteLn(Color_StrongGreen, "SDLInputSource: Using Controller DB from resources."); SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG_FILE, rpath.c_str()); diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 880fdd0f40..e05441565e 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -159,6 +159,7 @@ namespace EmuFolders std::string Cheats; std::string Patches; std::string Resources; + std::string UserResources; std::string Cache; std::string Covers; std::string GameSettings; @@ -1980,6 +1981,7 @@ void EmuFolders::SetDefaults(SettingsInterface& si) si.SetStringValue("Folders", "Logs", "logs"); si.SetStringValue("Folders", "Cheats", "cheats"); si.SetStringValue("Folders", "Patches", "patches"); + si.SetStringValue("Folders", "UserResources", "resources"); si.SetStringValue("Folders", "Cache", "cache"); si.SetStringValue("Folders", "Textures", "textures"); si.SetStringValue("Folders", "InputProfiles", "inputprofiles"); @@ -2005,6 +2007,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si) Patches = LoadPathFromSettings(si, DataRoot, "Patches", "patches"); Covers = LoadPathFromSettings(si, DataRoot, "Covers", "covers"); GameSettings = LoadPathFromSettings(si, DataRoot, "GameSettings", "gamesettings"); + UserResources = LoadPathFromSettings(si, DataRoot, "UserResources", "resources"); Cache = LoadPathFromSettings(si, DataRoot, "Cache", "cache"); Textures = LoadPathFromSettings(si, DataRoot, "Textures", "textures"); InputProfiles = LoadPathFromSettings(si, DataRoot, "InputProfiles", "inputprofiles"); @@ -2019,6 +2022,8 @@ void EmuFolders::LoadConfig(SettingsInterface& si) Console.WriteLn("Patches Directory: %s", Patches.c_str()); Console.WriteLn("Covers Directory: %s", Covers.c_str()); Console.WriteLn("Game Settings Directory: %s", GameSettings.c_str()); + Console.WriteLn("Resources Directory: %s", Resources.c_str()); + Console.WriteLn("User Resources Directory: %s", UserResources.c_str()); Console.WriteLn("Cache Directory: %s", Cache.c_str()); Console.WriteLn("Textures Directory: %s", Textures.c_str()); Console.WriteLn("Input Profile Directory: %s", InputProfiles.c_str()); @@ -2037,6 +2042,7 @@ bool EmuFolders::EnsureFoldersExist() result = FileSystem::CreateDirectoryPath(Patches.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(Covers.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(GameSettings.c_str(), false) && result; + result = FileSystem::CreateDirectoryPath(UserResources.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(Cache.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(Textures.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(InputProfiles.c_str(), false) && result; @@ -2044,7 +2050,7 @@ bool EmuFolders::EnsureFoldersExist() return result; } -std::FILE* EmuFolders::OpenLogFile(const std::string_view& name, const char* mode) +std::FILE* EmuFolders::OpenLogFile(std::string_view name, const char* mode) { if (name.empty()) return nullptr; @@ -2052,3 +2058,19 @@ std::FILE* EmuFolders::OpenLogFile(const std::string_view& name, const char* mod const std::string path(Path::Combine(Logs, name)); return FileSystem::OpenCFile(path.c_str(), mode); } + +std::string EmuFolders::GetOverridableResourcePath(std::string_view name) +{ + std::string upath = Path::Combine(UserResources, name); + if (FileSystem::FileExists(upath.c_str())) + { + if (UserResources != Resources) + Console.Warning(fmt::format("Using user-provided resource file {}", name)); + } + else + { + upath = Path::Combine(Resources, name); + } + + return upath; +}