diff --git a/data/localizedstrings/english.json b/data/localizedstrings/english.json new file mode 100644 index 00000000..71c239a0 --- /dev/null +++ b/data/localizedstrings/english.json @@ -0,0 +1,39 @@ +{ + "MENU_MODS": "MODS", + "MENU_MODS_DESC": "Load installed mods.", + "LUA_MENU_MOD_DESC_DEFAULT": "Load &&1.", + "LUA_MENU_MOD_DESC": "&&1\nAuthor: &&2\nVersion: &&3", + "LUA_MENU_LOADED_MOD": "Loaded mod: ^3&&1", + "LUA_MENU_AVAILABLE_MODS": "Available mods", + "LUA_MENU_UNLOAD": "Unload", + "LUA_MENU_UNLOAD_DESC": "Unload the currently loaded mod.", + "LUA_MENU_WORKSHOP": "Workshop", + "LUA_MENU_WORKSHOP_DESC": "Download and install mods.", + + "MENU_GENERAL": "H2-MOD", + "MENU_GENERAL_DESC": "Set h2-mod's settings.", + "LUA_MENU_AUTO_UPDATE": "Automatic updates", + "LUA_MENU_AUTO_UPDATE_DESC": "Enable or disable automatic updates on startup.", + "LUA_MENU_CHECK_UPDATES": "Check for updates", + "LUA_MENU_CHECK_UPDATES_DESC": "Check for updates.", + "LUA_MENU_DRAWING": "Hud", + "LUA_MENU_UPDATES": "Updates", + "LUA_MENU_RENDERING": "Rendering", + "LUA_MENU_DRAW_FPS": "Draw FPS", + "LUA_MENU_DRAW_FPS_DESC": "Enable or disable drawing fps or viewpos on screen.", + "LUA_MENU_FPS_ONLY": "FPS only", + "LUA_MENU_FPS_AND_VIEWPOS": "FPS and View Pos", + "LUA_MENU_DRAW_SPEED": "Draw speed", + "LUA_MENU_DRAW_SPEED_DESC": "Enable or disable drawing the player speed on screen.", + "LUA_MENU_DRAW_SPEEDGRAPH": "Draw speed graph", + "LUA_MENU_DRAW_SPEEDGRAPH_DESC": "Enable or disable the speed graph.", + "LUA_MENU_R_FULLBRIGHT": "Fullbright", + "LUA_MENU_R_FULLBRIGHT_DESC": "Change the fullbright mode", + "LUA_MENU_MODE2": "No dynamic lighting", + "LUA_MENU_MODE3": "Debug shader", + + "MENU_SYSINFO_CUSTOMER_SUPPORT_LINK": "Github Page:", + "MENU_SYSINFO_CUSTOMER_SUPPORT_URL": "https://github.com/fedddddd/h2-mod", + "MENU_SYSINFO_DONATION_LINK": "Donation link:", + "MENU_SYSINFO_DONATION_URL": "https://paypal.me/fedecek" +} \ No newline at end of file diff --git a/data/localizedstrings/italian.json b/data/localizedstrings/italian.json new file mode 100644 index 00000000..5928206f --- /dev/null +++ b/data/localizedstrings/italian.json @@ -0,0 +1,39 @@ +{ + "MENU_MODS": "MODS", + "MENU_MODS_DESC": "Abilita mod installate.", + "LUA_MENU_MOD_DESC_DEFAULT": "Abilita &&1.", + "LUA_MENU_MOD_DESC": "&&1\nAutore: &&2\nVersione: &&3", + "LUA_MENU_LOADED_MOD": "Mod attiva: ^3&&1", + "LUA_MENU_AVAILABLE_MODS": "Mod disponibili", + "LUA_MENU_UNLOAD": "Disabilita", + "LUA_MENU_UNLOAD_DESC": "Disabilita la mod attualmente attiva.", + "LUA_MENU_WORKSHOP": "Workshop", + "LUA_MENU_WORKSHOP_DESC": "Scarica e installa mod.", + + "MENU_GENERAL": "H2-MOD", + "MENU_GENERAL_DESC": "Imposta le opzioni di h2-mod.", + "LUA_MENU_AUTO_UPDATE": "Aggiornamenti automatici", + "LUA_MENU_AUTO_UPDATE_DESC": "Controlla automaticamente gli aggiornamenti all'avvio.", + "LUA_MENU_CHECK_UPDATES": "Controlla gli aggiornamenti", + "LUA_MENU_CHECK_UPDATES_DESC": "Controlla gli aggiornamenti.", + "LUA_MENU_DRAWING": "Hud", + "LUA_MENU_UPDATES": "Aggiornamenti", + "LUA_MENU_RENDERING": "Rendering", + "LUA_MENU_DRAW_FPS": "Mostra FPS", + "LUA_MENU_DRAW_FPS_DESC": "Mostra gli FPS o la posizione sullo schermo.", + "LUA_MENU_FPS_ONLY": "Solo FPS", + "LUA_MENU_FPS_AND_VIEWPOS": "FPS e Posizione", + "LUA_MENU_DRAW_SPEED": "Mostra la velocità", + "LUA_MENU_DRAW_SPEED_DESC": "Mostra la velocità sullo schermo.", + "LUA_MENU_DRAW_SPEEDGRAPH": "Mostra il grafico della velocità", + "LUA_MENU_DRAW_SPEEDGRAPH_DESC": "Attiva o disattiva il grafico della velocità.", + "LUA_MENU_R_FULLBRIGHT": "Fullbright", + "LUA_MENU_R_FULLBRIGHT_DESC": "Cambia la modalità di fullbright", + "LUA_MENU_MODE2": "No illuminazione dinamica", + "LUA_MENU_MODE3": "Shader di debug", + + "MENU_SYSINFO_CUSTOMER_SUPPORT_LINK": "Pagina di Github:", + "MENU_SYSINFO_CUSTOMER_SUPPORT_URL": "https://github.com/fedddddd/h2-mod", + "MENU_SYSINFO_DONATION_LINK": "Link per donazioni:", + "MENU_SYSINFO_DONATION_URL": "https://paypal.me/fedecek" +} \ No newline at end of file diff --git a/data/ui_scripts/mods/loading.lua b/data/ui_scripts/mods/loading.lua index 7918d7f0..1947acec 100644 --- a/data/ui_scripts/mods/loading.lua +++ b/data/ui_scripts/mods/loading.lua @@ -1,14 +1,3 @@ -game:addlocalizedstring("MENU_MODS", "MODS") -game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.") -game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.") -game:addlocalizedstring("LUA_MENU_MOD_DESC", "&&1\nAuthor: &&2\nVersion: &&3") -game:addlocalizedstring("LUA_MENU_LOADED_MOD", "Loaded mod: ^3&&1") -game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods") -game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload") -game:addlocalizedstring("LUA_MENU_UNLOAD_DESC", "Unload the currently loaded mod.") -game:addlocalizedstring("LUA_MENU_WORKSHOP", "Workshop") -game:addlocalizedstring("LUA_MENU_WORKSHOP_DESC", "Download and install mods.") - function createdivider(menu, text) local element = LUI.UIElement.new( { leftAnchor = true, @@ -52,7 +41,6 @@ LUI.addmenubutton("main_campaign", { function getmodname(path) local name = path game:addlocalizedstring(name, name) - game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.") local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name) local infofile = path .. "/info.json" diff --git a/data/ui_scripts/settings/__init__.lua b/data/ui_scripts/settings/__init__.lua index 627ac784..acb749d6 100644 --- a/data/ui_scripts/settings/__init__.lua +++ b/data/ui_scripts/settings/__init__.lua @@ -1,30 +1,5 @@ require("language") -game:addlocalizedstring("MENU_GENERAL", "GENERAL") -game:addlocalizedstring("MENU_GENERAL_DESC", "Set the client's settings.") -game:addlocalizedstring("LUA_MENU_AUTO_UPDATE", "Automatic updates") -game:addlocalizedstring("LUA_MENU_CHECK_UPDATES", "Check for updates") -game:addlocalizedstring("LUA_MENU_CHECK_UPDATES_DESC", "Check for updates.") -game:addlocalizedstring("LUA_MENU_DRAWING", "Drawing") -game:addlocalizedstring("LUA_MENU_UPDATES", "Updates") -game:addlocalizedstring("LUA_MENU_RENDERING", "Rendering") - -game:addlocalizedstring("LUA_MENU_DRAW_FPS", "Draw FPS") -game:addlocalizedstring("LUA_MENU_DRAW_FPS_DESC", "Enable or disable drawing fps or viewpos on screen.") -game:addlocalizedstring("LUA_MENU_FPS_ONLY", "FPS only") -game:addlocalizedstring("LUA_MENU_FPS_AND_VIEWPOS", "FPS and View Pos") - -game:addlocalizedstring("LUA_MENU_DRAW_SPEED", "Draw speed") -game:addlocalizedstring("LUA_MENU_DRAW_SPEED_DESC", "Enable or disable drawing the player speed on screen.") - -game:addlocalizedstring("LUA_MENU_DRAW_SPEEDGRAPH", "Draw speed graph") -game:addlocalizedstring("LUA_MENU_DRAW_SPEEDGRAPH_DESC", "Enable or disable the speed graph.") - -game:addlocalizedstring("LUA_MENU_R_FULLBRIGHT", "Fullbright") -game:addlocalizedstring("LUA_MENU_R_FULLBRIGHT_DESC", "Change the fullbright mode.") -game:addlocalizedstring("LUA_MENU_MODE2", "Mode 2") -game:addlocalizedstring("LUA_MENU_MODE3", "Mode 3") - function createdivider(menu, text) local element = LUI.UIElement.new({ leftAnchor = true, @@ -67,7 +42,7 @@ LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1) menu, "cg_auto_update", "@LUA_MENU_AUTO_UPDATE", - "Enable or disable automatic updates on startup.", + "@LUA_MENU_AUTO_UPDATE_DESC", { { text = "@LUA_MENU_ENABLED", @@ -113,7 +88,7 @@ LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1) menu, "cg_drawSpeed", "@LUA_MENU_DRAW_SPEED", - "Enable or disable drawing the player speed on screen.", + "@LUA_MENU_DRAW_SPEED_DESC", { { text = "@LUA_MENU_ENABLED", diff --git a/src/client/component/branding.cpp b/src/client/component/branding.cpp index 42204839..bc2ec0ca 100644 --- a/src/client/component/branding.cpp +++ b/src/client/component/branding.cpp @@ -40,10 +40,6 @@ namespace branding scheduler::loop(draw, scheduler::pipeline::renderer); localized_strings::override("MENU_SP_CAMPAIGN", "H2-MOD"); - localized_strings::override("MENU_SYSINFO_CUSTOMER_SUPPORT_LINK", "Github Page:"); - localized_strings::override("MENU_SYSINFO_CUSTOMER_SUPPORT_URL", "https://github.com/fedddddd/h2-mod"); - localized_strings::override("MENU_SYSINFO_DONATION_LINK", "Donation Link:"); - localized_strings::override("MENU_SYSINFO_DONATION_URL", "https://paypal.me/fedecek"); utils::hook::jump(0x14033D550, get_build_number_stub, true); } diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index 9d298857..1a4fe877 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -3,6 +3,7 @@ #include "filesystem.hpp" #include "console.hpp" +#include "localized_strings.hpp" #include "game/game.hpp" @@ -38,6 +39,8 @@ namespace filesystem filesystem::register_path(L"h2-mod"); filesystem::register_path(L"data"); + localized_strings::clear(); + utils::hook::invoke(0x14060BF50, name); } diff --git a/src/client/component/localized_strings.cpp b/src/client/component/localized_strings.cpp index 9b89c194..ab42592f 100644 --- a/src/client/component/localized_strings.cpp +++ b/src/client/component/localized_strings.cpp @@ -1,12 +1,17 @@ #include #include "loader/component_loader.hpp" + #include "localized_strings.hpp" +#include "game_console.hpp" +#include "filesystem.hpp" +#include "console.hpp" + +#include "game/game.hpp" + #include #include #include -#include "game/game.hpp" - -#include "game_console.hpp" +#include namespace localized_strings { @@ -14,7 +19,13 @@ namespace localized_strings { utils::hook::detour seh_string_ed_get_string_hook; - using localized_map = std::unordered_map; + struct localize_entry + { + std::string value{}; + bool volatile_{}; + }; + + using localized_map = std::unordered_map; utils::concurrency::container localized_overrides; const char* seh_string_ed_get_string(const char* reference) @@ -24,7 +35,7 @@ namespace localized_strings const auto entry = map.find(reference); if (entry != map.end()) { - return utils::string::va("%s", entry->second.data()); + return utils::string::va("%s", entry->second.value.data()); } return seh_string_ed_get_string_hook.invoke(reference); @@ -39,7 +50,7 @@ namespace localized_strings const auto entry = map.find(name); if (entry != map.end()) { - return utils::string::va("%s", entry->second.data()); + return utils::string::va("%s", entry->second.value.data()); } return nullptr; @@ -56,16 +67,119 @@ namespace localized_strings return static_cast(&entry); } + + bool parse_localized_strings_file(const std::string& data) + { + rapidjson::Document j; + j.Parse(data.data()); + + if (!j.IsObject()) + { + return false; + } + + localized_overrides.access([&](localized_map& map) + { + const auto obj = j.GetObj(); + for (const auto& [key, value] : obj) + { + if (!key.IsString() || !value.IsString()) + { + continue; + } + + const auto name = key.GetString(); + const auto str = value.GetString(); + + const auto entry = map.find(name); + if (entry == map.end() || entry->second.volatile_) + { + map[name] = {str, true}; + } + } + }); + + return true; + } + + bool try_load_file(const std::string& path, const std::string& language) + { + const auto file = utils::string::va("%s/localizedstrings/%s.json", path.data(), language.data()); + if (!utils::io::file_exists(file)) + { + return false; + } + + console::info("[Localized strings] Parsing %s\n", file); + const auto data = utils::io::read_file(file); + if (!parse_localized_strings_file(data)) + { + console::error("[Localized strings] Invalid language json file\n"); + return false; + } + + return true; + } + + void load_localized_strings() + { + bool found = false; + + const auto search_paths = filesystem::get_search_paths(); + const auto language = game::SEH_GetCurrentLanguageName(); + + for (const auto& path : search_paths) + { + if (!try_load_file(path, language)) + { + if (try_load_file(path, "english")) + { + found = true; + console::warn("[Localized strings] No valid language file found for '%s' in '%s', falling back to 'english'\n", + language, path.data()); + } + } + else + { + found = true; + } + } + + if (!found) + { + console::warn("[Localized strings] No valid language file found!\n"); + } + } } - void override(const std::string& key, const std::string& value) + void override(const std::string& key, const std::string& value, bool volatile_) { localized_overrides.access([&](localized_map& map) { - map[key] = value; + map[key] = {value, volatile_}; }); } + void clear() + { + localized_overrides.access([&](localized_map& map) + { + for (auto i = map.begin(); i != map.end();) + { + if (i->second.volatile_) + { + i = map.erase(i); + } + else + { + ++i; + } + } + }); + + load_localized_strings(); + } + class component final : public component_interface { public: diff --git a/src/client/component/localized_strings.hpp b/src/client/component/localized_strings.hpp index 01d15907..7a0d8f07 100644 --- a/src/client/component/localized_strings.hpp +++ b/src/client/component/localized_strings.hpp @@ -2,5 +2,6 @@ namespace localized_strings { - void override(const std::string& key, const std::string& value); + void override(const std::string& key, const std::string& value, bool volatile_ = false); + void clear(); } \ No newline at end of file diff --git a/src/client/component/mods.cpp b/src/client/component/mods.cpp index a67f7c08..62a158f5 100644 --- a/src/client/component/mods.cpp +++ b/src/client/component/mods.cpp @@ -11,6 +11,7 @@ #include "fonts.hpp" #include "mods.hpp" #include "mapents.hpp" +#include "localized_strings.hpp" #include #include @@ -34,6 +35,7 @@ namespace mods } mapents::clear(); + localized_strings::clear(); db_release_xassets_hook.invoke(); } diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 8aabc443..5f0c04bf 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -258,7 +258,7 @@ namespace ui_scripting game_type["addlocalizedstring"] = [](const game&, const std::string& string, const std::string& value) { - localized_strings::override(string, value); + localized_strings::override(string, value, true); }; game_type["setlanguage"] = [](const game&, const std::string& language) diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp index e690d71e..a7cf604f 100644 --- a/src/client/game/scripting/lua/context.cpp +++ b/src/client/game/scripting/lua/context.cpp @@ -764,7 +764,7 @@ namespace scripting::lua game_type["addlocalizedstring"] = [](const game&, const std::string& string, const std::string& value) { - localized_strings::override(string, value); + localized_strings::override(string, value, true); }; } }