From 56f94c74714d13680031ca38faa38ff1a506b9b2 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Fri, 9 Dec 2022 08:45:46 +0000 Subject: [PATCH] Add a `--lang` command line argument This allows forcing the locale without changing the settings. The `forceLocale` option also lets us avoid changing the locale in settings if fonts.mpq is missing, fixing the following scenario: 1. System locale is ja 2. Launch the game -- fonts are missing 3. Close the game, download fonts.mpq 4. Launch the game again -- it is now in English because the settings have changed --- Source/diablo.cpp | 19 ++++++++++------ Source/init.cpp | 2 +- Source/utils/language.cpp | 31 ++++++++++++++++++--------- Source/utils/language.h | 4 ++++ tools/measure_timedemo_performance.py | 2 +- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index f61d406b7..9cf1cf2dc 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -907,6 +907,7 @@ void PrintHelpOption(string_view flags, string_view description) PrintHelpOption("--data-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the folder of diabdat.mpq")); PrintHelpOption("--save-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the folder of save files")); PrintHelpOption("--config-dir", _(/* TRANSLATORS: Commandline Option */ "Specify the location of diablo.ini")); + PrintHelpOption("--lang", _(/* TRANSLATORS: Commandline Option */ "Specify the language code (e.g. en or pt_BR)")); PrintHelpOption("-n", _(/* TRANSLATORS: Commandline Option */ "Skip startup videos")); PrintHelpOption("-f", _(/* TRANSLATORS: Commandline Option */ "Display frames per second")); PrintHelpOption("--verbose", _(/* TRANSLATORS: Commandline Option */ "Enable verbose logging")); @@ -939,7 +940,7 @@ void PrintHelpOption(string_view flags, string_view description) void PrintFlagsRequiresArgument(string_view flag) { printInConsole(flag); - printInConsole("requires an argument"); + printInConsole(" requires an argument"); printNewlineInConsole(); } @@ -968,26 +969,32 @@ void DiabloParseFlags(int argc, char **argv) } else if (arg == "--data-dir") { if (i + 1 == argc) { PrintFlagsRequiresArgument("--data-dir"); - diablo_quit(0); + diablo_quit(64); } paths::SetBasePath(argv[++i]); } else if (arg == "--save-dir") { if (i + 1 == argc) { PrintFlagsRequiresArgument("--save-dir"); - diablo_quit(0); + diablo_quit(64); } paths::SetPrefPath(argv[++i]); } else if (arg == "--config-dir") { if (i + 1 == argc) { PrintFlagsRequiresArgument("--config-dir"); - diablo_quit(0); + diablo_quit(64); } paths::SetConfigPath(argv[++i]); + } else if (arg == "--lang") { + if (i + 1 == argc) { + PrintFlagsRequiresArgument("--lang"); + diablo_quit(64); + } + forceLocale = argv[++i]; #ifndef DISABLE_DEMOMODE } else if (arg == "--demo") { if (i + 1 == argc) { PrintFlagsRequiresArgument("--demo"); - diablo_quit(0); + diablo_quit(64); } demoNumber = SDL_atoi(argv[++i]); gbShowIntro = false; @@ -996,7 +1003,7 @@ void DiabloParseFlags(int argc, char **argv) } else if (arg == "--record") { if (i + 1 == argc) { PrintFlagsRequiresArgument("--record"); - diablo_quit(0); + diablo_quit(64); } recordNumber = SDL_atoi(argv[++i]); } else if (arg == "--create-reference") { diff --git a/Source/init.cpp b/Source/init.cpp index 3d059de4f..5e772f3aa 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -216,7 +216,7 @@ void LoadLanguageArchive() lang_mpq = std::nullopt; #endif - string_view code = *sgOptions.Language.code; + string_view code = GetLanguageCode(); if (code != "en") { std::string langMpqName { code }; #ifdef UNPACKED_MPQS diff --git a/Source/utils/language.cpp b/Source/utils/language.cpp index 9aa154309..ea6fe279e 100644 --- a/Source/utils/language.cpp +++ b/Source/utils/language.cpp @@ -19,6 +19,8 @@ #define MO_MAGIC 0x950412de +std::string forceLocale; + namespace { using namespace devilution; @@ -112,16 +114,16 @@ string_view TrimRight(string_view str) return str; } -// English, Danish, Spanish, Italian, Swedish -unsigned PluralForms = 2; -tl::function_ref GetLocalPluralId = [](int n) -> int { return n != 1 ? 1 : 0; }; - /** plural=(n != 1); */ int PluralIfNotOne(int n) { return n != 1 ? 1 : 0; } +// English, Danish, Spanish, Italian, Swedish +unsigned PluralForms = 2; +tl::function_ref GetLocalPluralId = PluralIfNotOne; + /** * Match plural=(n != 1);" */ @@ -148,7 +150,7 @@ void SetPluralForm(string_view expression) // en, bg, da, de, es, it, sv if (expression == "(n != 1)") { - GetLocalPluralId = &PluralIfNotOne; + GetLocalPluralId = PluralIfNotOne; return; } @@ -340,9 +342,16 @@ bool HasTranslation(const std::string &locale) }); } +string_view GetLanguageCode() +{ + if (!forceLocale.empty()) + return forceLocale; + return *sgOptions.Language.code; +} + bool IsSmallFontTall() { - const string_view code = (*sgOptions.Language.code).substr(0, 2); + const string_view code = GetLanguageCode().substr(0, 2); return code == "zh" || code == "ja" || code == "ko"; } @@ -356,14 +365,14 @@ void LanguageInitialize() UiErrorOkDialog( "Missing fonts.mpq", StrCat("fonts.mpq is required for locale \"", - *sgOptions.Language.code, + GetLanguageCode(), "\"\n\n" "Please download fonts.mpq from:\n" "github.com/diasurgical/\ndevilutionx-assets/releases")); - sgOptions.Language.code = "en"; + forceLocale = "en"; } - const std::string lang(*sgOptions.Language.code); + const std::string lang(GetLanguageCode()); AssetHandle handle; const uint32_t loadTranslationsStart = SDL_GetTicks(); @@ -377,7 +386,9 @@ void LanguageInitialize() break; } if (!handle.ok()) { - GetLocalPluralId = &PluralIfNotOne; // Reset to English plural form + // Reset to English, which is always available: + forceLocale = "en"; + GetLocalPluralId = PluralIfNotOne; return; } diff --git a/Source/utils/language.h b/Source/utils/language.h index 5c8a98463..2233b9107 100644 --- a/Source/utils/language.h +++ b/Source/utils/language.h @@ -10,6 +10,10 @@ #define N_(x) (x) #define P_(context, x) (x) +extern std::string forceLocale; + +devilution::string_view GetLanguageCode(); + bool HasTranslation(const std::string &locale); void LanguageInitialize(); diff --git a/tools/measure_timedemo_performance.py b/tools/measure_timedemo_performance.py index e2a053acc..0839b079a 100755 --- a/tools/measure_timedemo_performance.py +++ b/tools/measure_timedemo_performance.py @@ -15,7 +15,7 @@ class RunMetrics(NamedTuple): def measure(binary: str) -> RunMetrics: result: subprocess.CompletedProcess = subprocess.run( - [binary, '--diablo', '--spawn', '--demo', '0', '--timedemo'], capture_output=True) + [binary, '--diablo', '--spawn', '--lang', 'en', '--demo', '0', '--timedemo'], capture_output=True) match = _TIME_AND_FPS_REGEX.search(result.stderr) if not match: raise Exception(f"Failed to parse output in:\n{result.stderr}")