diff --git a/src/common/config.cpp b/src/common/config.cpp index eac463d0a..5159c9cf3 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -4,8 +4,9 @@ #include #include #include +#include +#include #include -#include // for wstring support #include #include "common/assert.h" @@ -144,6 +145,9 @@ static ConfigEntry isSideTrophy("right"); static ConfigEntry isConnectedToNetwork(false); static bool enableDiscordRPC = false; static std::filesystem::path sys_modules_path = {}; +static std::filesystem::path sys_font_path = {}; +static std::string sys_font_fallback_name = {}; +static std::unordered_map system_font_overrides; // Input static ConfigEntry cursorState(HideCursorState::Idle); @@ -234,6 +238,47 @@ void setSysModulesPath(const std::filesystem::path& path) { sys_modules_path = path; } +std::filesystem::path getSysFontPath() { + if (sys_font_path.empty()) { + return Common::FS::GetUserPath(Common::FS::PathType::FontDir); + } + return sys_font_path; +} + +void setSysFontPath(const std::filesystem::path& path) { + sys_font_path = path; +} + +std::optional getSystemFontOverride(std::string_view key) { + if (key.empty()) { + return std::nullopt; + } + auto it = system_font_overrides.find(std::string(key)); + if (it == system_font_overrides.end()) { + return std::nullopt; + } + return it->second; +} + +std::string getSystemFontFallbackName() { + return sys_font_fallback_name; +} + +void setSystemFontFallbackName(const std::string& name) { + sys_font_fallback_name = name; +} + +void setSystemFontOverride(std::string_view key, const std::filesystem::path& path) { + if (key.empty()) { + return; + } + system_font_overrides[std::string(key)] = path; +} + +void clearSystemFontOverrides() { + system_font_overrides.clear(); +} + int getVolumeSlider() { return volumeSlider.get(); } @@ -460,7 +505,7 @@ void setShowFpsCounter(bool enable, bool is_game_specific) { showFpsCounter.set(enable, is_game_specific); } -bool isLoggingEnabled() { +static bool isLoggingEnabled() { return logEnabled.get(); } @@ -862,6 +907,10 @@ void load(const std::filesystem::path& path, bool is_game_specific) { return; } + if (!is_game_specific) { + system_font_overrides.clear(); + } + if (data.contains("General")) { const toml::value& general = data.at("General"); @@ -885,6 +934,43 @@ void load(const std::filesystem::path& path, bool is_game_specific) { isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific); defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific); sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path); + // Accept alias without trailing 's' + sys_modules_path = toml::find_fs_path_or(general, "sysModulePath", sys_modules_path); + // Prefer 'sysFontPath'; accept 'SysFontPath' for compatibility + sys_font_path = toml::find_fs_path_or(general, "sysFontPath", sys_font_path); + sys_font_path = toml::find_fs_path_or(general, "SysFontPath", sys_font_path); + } + + if (data.contains("SystemFonts")) { + const toml::value& fonts = data.at("SystemFonts"); + if (fonts.is_table()) { + // Read fallback (lowercase preferred), accept 'Fallback'/'FallbackFontName' for compat + if (fonts.contains("fallback")) { + const auto& v = fonts.at("fallback"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } else if (fonts.contains("Fallback")) { + const auto& v = fonts.at("Fallback"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } else if (fonts.contains("FallbackFontName")) { + const auto& v = fonts.at("FallbackFontName"); + if (v.is_string()) { + sys_font_fallback_name = toml::get(v); + } + } + for (const auto& [name, value] : fonts.as_table()) { + if (name == "fallback" || name == "Fallback" || name == "FallbackFontName") { + continue; + } + if (value.is_string()) { + system_font_overrides[name] = + std::filesystem::path(toml::get(value)); + } + } + } } if (data.contains("Input")) { @@ -1005,7 +1091,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) { } } -void sortTomlSections(toml::ordered_value& data) { +static void sortTomlSections(toml::ordered_value& data) { toml::ordered_value ordered_data; std::vector section_order = {"General", "Input", "Audio", "GPU", "Vulkan", "Debug", "Keys", "GUI", "Settings"}; @@ -1158,6 +1244,22 @@ void save(const std::filesystem::path& path, bool is_game_specific) { // Non game-specific entries data["General"]["enableDiscordRPC"] = enableDiscordRPC; data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data}; + // Save using 'sysFontPath' to match style + data["General"]["sysFontPath"] = string{fmt::UTF(sys_font_path.u8string()).data}; + { + toml::table fonts_table; + if (!sys_font_fallback_name.empty()) { + fonts_table["fallback"] = sys_font_fallback_name; + } + for (const auto& [name, path_override] : system_font_overrides) { + fonts_table[name] = string{fmt::UTF(path_override.u8string()).data}; + } + if (!fonts_table.empty()) { + data["SystemFonts"] = fonts_table; + } else if (data.is_table()) { + data.as_table().erase("SystemFonts"); + } + } data["GUI"]["installDirs"] = install_dirs; data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; @@ -1302,7 +1404,7 @@ hotkey_quit = lctrl, lshift, end )"; } -constexpr std::string_view GetDefaultInputConfig() { +static constexpr std::string_view GetDefaultInputConfig() { return R"(#Feeling lost? Check out the Help section! # Keyboard bindings diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 7137a2205..0415c1a26 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -129,7 +129,6 @@ static auto UserPaths = [] { create_path(PathType::CustomConfigs, user_dir / CUSTOM_CONFIGS); create_path(PathType::CacheDir, user_dir / CACHE_DIR); create_path(PathType::FontDir, user_dir / SYSFONTS_DIR); - // subdirectory for fonts std::filesystem::create_directory(user_dir / SYSFONTS_DIR / "font"); std::filesystem::create_directory(user_dir / SYSFONTS_DIR / "font2"); @@ -148,11 +147,15 @@ static auto UserPaths = [] { notice_file.close(); } - std::ofstream font_instructions(user_dir / SYSFONTS_DIR / "Instructions.txt"); - if (font_instructions.is_open()) { - font_instructions << "Place /preinst/common/font contents into font folder\n" - "Place /system/common/font2 contents into font2 folder\n"; - font_instructions.close(); + const auto instructions_path = user_dir / SYSFONTS_DIR / "Instructions.txt"; + std::error_code ec; + if (!std::filesystem::exists(instructions_path, ec)) { + std::ofstream font_instructions(instructions_path); + if (font_instructions.is_open()) { + font_instructions << "Place system font files (.otf/.ttf) into the 'font' and 'font2' " + "folders.\n"; + font_instructions.close(); + } } return paths; diff --git a/src/common/path_util.h b/src/common/path_util.h index 00cf870e7..cf4611903 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -25,7 +25,7 @@ enum class PathType { CustomTrophy, // Where custom files for trophies are stored. CustomConfigs, // Where custom files for different games are stored. CacheDir, // Where pipeline and shader cache is stored. - FontDir // Where font files are stored. + FontDir, // Where system font files are stored. }; constexpr auto PORTABLE_DIR = "user";