aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/client/languagehooks.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL/client/languagehooks.cpp')
-rw-r--r--NorthstarDLL/client/languagehooks.cpp116
1 files changed, 116 insertions, 0 deletions
diff --git a/NorthstarDLL/client/languagehooks.cpp b/NorthstarDLL/client/languagehooks.cpp
new file mode 100644
index 00000000..1a46633c
--- /dev/null
+++ b/NorthstarDLL/client/languagehooks.cpp
@@ -0,0 +1,116 @@
+#include "pch.h"
+#include "core/tier0.h"
+
+#include <filesystem>
+#include <regex>
+
+AUTOHOOK_INIT()
+
+typedef LANGID (*Tier0_DetectDefaultLanguageType)();
+
+bool CheckLangAudioExists(char* lang)
+{
+ std::string path {"r2\\sound\\general_"};
+ path += lang;
+ path += ".mstr";
+ return fs::exists(path);
+}
+
+std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern)
+{
+ std::vector<std::string> result;
+
+ if (!fs::exists(dir) || !fs::is_directory(dir))
+ return result;
+
+ using iterator = fs::directory_iterator;
+
+ const iterator end;
+ for (iterator iter {dir}; iter != end; ++iter)
+ {
+ const std::string filename = iter->path().filename().string();
+ std::smatch matches;
+ if (fs::is_regular_file(*iter) && std::regex_match(filename, matches, ext_pattern))
+ {
+ result.push_back(std::move(matches.str(1)));
+ }
+ }
+
+ return result;
+}
+
+std::string GetAnyInstalledAudioLanguage()
+{
+ for (const auto& lang : file_list("r2\\sound\\", std::regex(".*?general_([a-z]+)_patch_1\\.mstr")))
+ if (lang != "general" || lang != "")
+ return lang;
+ return "NO LANGUAGE DETECTED";
+}
+
+// clang-format off
+AUTOHOOK(GetGameLanguage, tier0.dll + 0xF560,
+char*, __fastcall, ())
+// clang-format on
+{
+ auto tier0Handle = GetModuleHandleA("tier0.dll");
+ auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage");
+ char* ingameLang1 = (char*)tier0Handle + 0xA9B60; // one of the globals we need to override if overriding lang (size: 256)
+ bool& canOriginDictateLang = *(bool*)((char*)tier0Handle + 0xA9A90);
+
+ const char* forcedLanguage;
+ if (Tier0::CommandLine()->CheckParm("-language", &forcedLanguage))
+ {
+ if (!CheckLangAudioExists((char*)forcedLanguage))
+ {
+ spdlog::info(
+ "User tried to force the language (-language) to \"{}\", but audio for this language doesn't exist and the game is bound "
+ "to error, falling back to next option...",
+ forcedLanguage);
+ }
+ else
+ {
+ spdlog::info("User forcing the language (-language) to: {}", forcedLanguage);
+ strncpy(ingameLang1, forcedLanguage, 256);
+ return ingameLang1;
+ }
+ }
+
+ canOriginDictateLang = true; // let it try
+ {
+ auto lang = GetGameLanguage();
+ if (!CheckLangAudioExists(lang))
+ {
+ if (strcmp(lang, "russian") !=
+ 0) // don't log for "russian" since it's the default and that means Origin detection just didn't change it most likely
+ spdlog::info(
+ "Origin detected language \"{}\", but we do not have audio for it installed, falling back to the next option", lang);
+ }
+ else
+ {
+ spdlog::info("Origin detected language: {}", lang);
+ return lang;
+ }
+ }
+
+ Tier0_DetectDefaultLanguageType(); // force the global in tier0 to be populated with language inferred from user's system rather than
+ // defaulting to Russian
+ canOriginDictateLang = false; // Origin has no say anymore, we will fallback to user's system setup language
+ auto lang = GetGameLanguage();
+ spdlog::info("Detected system language: {}", lang);
+ if (!CheckLangAudioExists(lang))
+ {
+ spdlog::warn("Caution, audio for this language does NOT exist. You might want to override your game language with -language "
+ "command line option.");
+ auto lang = GetAnyInstalledAudioLanguage();
+ spdlog::warn("Falling back to the first installed audio language: {}", lang.c_str());
+ strncpy(ingameLang1, lang.c_str(), 256);
+ return ingameLang1;
+ }
+
+ return lang;
+}
+
+ON_DLL_LOAD_CLIENT("tier0.dll", LanguageHooks, (CModule module))
+{
+ AUTOHOOK_DISPATCH()
+}