Custom localization file support

This commit is contained in:
Federico Cecchetto 2022-07-16 00:29:44 +02:00
parent 4fa5dbe930
commit a487e6fcfa
11 changed files with 211 additions and 54 deletions

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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) function createdivider(menu, text)
local element = LUI.UIElement.new( { local element = LUI.UIElement.new( {
leftAnchor = true, leftAnchor = true,
@ -52,7 +41,6 @@ LUI.addmenubutton("main_campaign", {
function getmodname(path) function getmodname(path)
local name = path local name = path
game:addlocalizedstring(name, name) game:addlocalizedstring(name, name)
game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.")
local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name) local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name)
local infofile = path .. "/info.json" local infofile = path .. "/info.json"

View File

@ -1,30 +1,5 @@
require("language") 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) function createdivider(menu, text)
local element = LUI.UIElement.new({ local element = LUI.UIElement.new({
leftAnchor = true, leftAnchor = true,
@ -67,7 +42,7 @@ LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
menu, menu,
"cg_auto_update", "cg_auto_update",
"@LUA_MENU_AUTO_UPDATE", "@LUA_MENU_AUTO_UPDATE",
"Enable or disable automatic updates on startup.", "@LUA_MENU_AUTO_UPDATE_DESC",
{ {
{ {
text = "@LUA_MENU_ENABLED", text = "@LUA_MENU_ENABLED",
@ -113,7 +88,7 @@ LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
menu, menu,
"cg_drawSpeed", "cg_drawSpeed",
"@LUA_MENU_DRAW_SPEED", "@LUA_MENU_DRAW_SPEED",
"Enable or disable drawing the player speed on screen.", "@LUA_MENU_DRAW_SPEED_DESC",
{ {
{ {
text = "@LUA_MENU_ENABLED", text = "@LUA_MENU_ENABLED",

View File

@ -40,10 +40,6 @@ namespace branding
scheduler::loop(draw, scheduler::pipeline::renderer); scheduler::loop(draw, scheduler::pipeline::renderer);
localized_strings::override("MENU_SP_CAMPAIGN", "H2-MOD"); 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); utils::hook::jump(0x14033D550, get_build_number_stub, true);
} }

View File

@ -3,6 +3,7 @@
#include "filesystem.hpp" #include "filesystem.hpp"
#include "console.hpp" #include "console.hpp"
#include "localized_strings.hpp"
#include "game/game.hpp" #include "game/game.hpp"
@ -38,6 +39,8 @@ namespace filesystem
filesystem::register_path(L"h2-mod"); filesystem::register_path(L"h2-mod");
filesystem::register_path(L"data"); filesystem::register_path(L"data");
localized_strings::clear();
utils::hook::invoke<void>(0x14060BF50, name); utils::hook::invoke<void>(0x14060BF50, name);
} }

View File

@ -1,12 +1,17 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "localized_strings.hpp" #include "localized_strings.hpp"
#include "game_console.hpp"
#include "filesystem.hpp"
#include "console.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/concurrency.hpp> #include <utils/concurrency.hpp>
#include "game/game.hpp" #include <utils/io.hpp>
#include "game_console.hpp"
namespace localized_strings namespace localized_strings
{ {
@ -14,7 +19,13 @@ namespace localized_strings
{ {
utils::hook::detour seh_string_ed_get_string_hook; utils::hook::detour seh_string_ed_get_string_hook;
using localized_map = std::unordered_map<std::string, std::string>; struct localize_entry
{
std::string value{};
bool volatile_{};
};
using localized_map = std::unordered_map<std::string, localize_entry>;
utils::concurrency::container<localized_map> localized_overrides; utils::concurrency::container<localized_map> localized_overrides;
const char* seh_string_ed_get_string(const char* reference) const char* seh_string_ed_get_string(const char* reference)
@ -24,7 +35,7 @@ namespace localized_strings
const auto entry = map.find(reference); const auto entry = map.find(reference);
if (entry != map.end()) 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<const char*>(reference); return seh_string_ed_get_string_hook.invoke<const char*>(reference);
@ -39,7 +50,7 @@ namespace localized_strings
const auto entry = map.find(name); const auto entry = map.find(name);
if (entry != map.end()) if (entry != map.end())
{ {
return utils::string::va("%s", entry->second.data()); return utils::string::va("%s", entry->second.value.data());
} }
return nullptr; return nullptr;
@ -56,16 +67,119 @@ namespace localized_strings
return static_cast<game::XAssetHeader>(&entry); return static_cast<game::XAssetHeader>(&entry);
} }
bool parse_localized_strings_file(const std::string& data)
{
rapidjson::Document j;
j.Parse(data.data());
if (!j.IsObject())
{
return false;
} }
void override(const std::string& key, const std::string& value) 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, bool volatile_)
{ {
localized_overrides.access([&](localized_map& map) 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 class component final : public component_interface
{ {
public: public:

View File

@ -2,5 +2,6 @@
namespace localized_strings 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();
} }

View File

@ -11,6 +11,7 @@
#include "fonts.hpp" #include "fonts.hpp"
#include "mods.hpp" #include "mods.hpp"
#include "mapents.hpp" #include "mapents.hpp"
#include "localized_strings.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp> #include <utils/io.hpp>
@ -34,6 +35,7 @@ namespace mods
} }
mapents::clear(); mapents::clear();
localized_strings::clear();
db_release_xassets_hook.invoke<void>(); db_release_xassets_hook.invoke<void>();
} }

View File

@ -258,7 +258,7 @@ namespace ui_scripting
game_type["addlocalizedstring"] = [](const game&, const std::string& string, game_type["addlocalizedstring"] = [](const game&, const std::string& string,
const std::string& value) const std::string& value)
{ {
localized_strings::override(string, value); localized_strings::override(string, value, true);
}; };
game_type["setlanguage"] = [](const game&, const std::string& language) game_type["setlanguage"] = [](const game&, const std::string& language)

View File

@ -764,7 +764,7 @@ namespace scripting::lua
game_type["addlocalizedstring"] = [](const game&, const std::string& string, game_type["addlocalizedstring"] = [](const game&, const std::string& string,
const std::string& value) const std::string& value)
{ {
localized_strings::override(string, value); localized_strings::override(string, value, true);
}; };
} }
} }