From 202a400f25cb724ce602e6557c6e925fbd0afcc2 Mon Sep 17 00:00:00 2001 From: holasoyender Date: Tue, 29 Aug 2023 19:15:33 +0200 Subject: [PATCH] Localization! --- .gitmodules | 3 ++ README.md | 9 +++- SoTLoader/SoTLoader.vcxproj | 3 ++ SoTLoader/SoTLoader.vcxproj.filters | 21 +++++--- SoTLoader/locale/locale.h | 77 +++++++++++++++++++++++++++++ SoTLoader/locale/utils.h | 12 +++++ SoTLoader/main.cpp | 72 +++++++++++++++++++-------- SoTLoader/utils/logger.h | 2 +- langs/en.yml | 21 ++++++++ langs/es.yml | 21 ++++++++ yaml | 1 + 11 files changed, 213 insertions(+), 29 deletions(-) create mode 100644 .gitmodules create mode 100644 SoTLoader/locale/locale.h create mode 100644 SoTLoader/locale/utils.h create mode 100644 langs/en.yml create mode 100644 langs/es.yml create mode 160000 yaml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d72829d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "yaml"] + path = yaml + url = https://github.com/jbeder/yaml-cpp diff --git a/README.md b/README.md index 7013574..0329c8c 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,17 @@ This software is intended to be used for educational purposes only. I am not res ### Warning This software is provided "as is" without warranty of any kind. The author is not responsible for any damage caused by this software. +## Localizations +This injector automatically detects the language of the user, and it will show strings in the following languages: +- English +- Spanish + +Feel free to contribute with more localizations. + ## How to use 1. Download the latest release from the [releases page](https://github.com/holasoyender/SoTLoader/releases) 2. Extract the zip file -3. Move the DLL file you want to inject to the same folder as the executable +3. Move the DLL file(s) you want to inject to the `libs` folder (create it if it doesn't exist) 4. Run the executable, if you move more than one DLL file to the folder, you will be asked which one you want to inject ## Unloading the DLL diff --git a/SoTLoader/SoTLoader.vcxproj b/SoTLoader/SoTLoader.vcxproj index cd281f9..832d097 100644 --- a/SoTLoader/SoTLoader.vcxproj +++ b/SoTLoader/SoTLoader.vcxproj @@ -51,6 +51,7 @@ v143 true Unicode + false @@ -138,6 +139,8 @@ + + diff --git a/SoTLoader/SoTLoader.vcxproj.filters b/SoTLoader/SoTLoader.vcxproj.filters index 4f8d330..4b3b9a1 100644 --- a/SoTLoader/SoTLoader.vcxproj.filters +++ b/SoTLoader/SoTLoader.vcxproj.filters @@ -19,6 +19,9 @@ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + {8df5946d-a287-4ac8-997a-efb83585596d} + @@ -29,12 +32,6 @@ - - resources - - - resources - headers @@ -53,6 +50,18 @@ headers\utils + + headers + + + headers + + + headers\locale + + + headers\locale + diff --git a/SoTLoader/locale/locale.h b/SoTLoader/locale/locale.h new file mode 100644 index 0000000..cddaff1 --- /dev/null +++ b/SoTLoader/locale/locale.h @@ -0,0 +1,77 @@ +#pragma once + +#include "utils.h" +#include + +class Locale +{ + +private: + std::string langCode; + std::fstream file; + YAML::Node node; + + void loadFile() { + std::string path = "locale/" + langCode + ".yml"; + file.open(path, std::ios::in); + if (!file.is_open()) { + logger::error("Failed to open file: ", path, ". Defaulting to en.yml"); + file.open("locale/en.yml", std::ios::in); + if (!file.is_open()) { + logger::error("Failed to open file: locale/en.yml"); + system("pause"); + exit(1); + } + } + } + +public: + Locale(std::string langCode) + { + this->langCode = langCode; + + loadFile(); + + try { + node = YAML::Load(file); + } + catch (const std::runtime_error& re) + { + logger::error("Runtime error when loading lang file: ", re.what()); + system("pause"); + exit(1); + } + catch (const std::exception& ex) + { + logger::error("Error occurred: ", ex.what()); + system("pause"); + exit(1); + } + catch (...) + { + logger::error("Unknown failure occurred. Possible memory corruption"); + system("pause"); + exit(1); + } + } + + std::string get(const std::string& key, std::string default_value = "Unknown string") { + try { + return node[key].as(); + } + catch (const std::runtime_error& re) + { + logger::error("Runtime error when getting string: ", re.what()); + } + catch (const std::exception& ex) + { + logger::error("Error occurred when getting string: ", ex.what()); + } + catch (...) + { + logger::error("Unknown failure occurred when getting string. Possible memory corruption"); + } + return default_value; + } + +}; \ No newline at end of file diff --git a/SoTLoader/locale/utils.h b/SoTLoader/locale/utils.h new file mode 100644 index 0000000..5e4a4b5 --- /dev/null +++ b/SoTLoader/locale/utils.h @@ -0,0 +1,12 @@ +#pragma once + +inline std::string getSystemLang() { + LCID lcid = GetThreadLocale(); + wchar_t name[LOCALE_NAME_MAX_LENGTH]; + wchar_t parentLocateName[LOCALE_NAME_MAX_LENGTH]; + LCIDToLocaleName(lcid, name, LOCALE_NAME_MAX_LENGTH, 0); + GetLocaleInfoEx(name, LOCALE_SPARENT, parentLocateName, LOCALE_NAME_MAX_LENGTH); + + std::wstring langCode = std::wstring(parentLocateName); + return std::string(langCode.begin(), langCode.end()); +} \ No newline at end of file diff --git a/SoTLoader/main.cpp b/SoTLoader/main.cpp index e177ff6..cd3ae01 100644 --- a/SoTLoader/main.cpp +++ b/SoTLoader/main.cpp @@ -4,6 +4,7 @@ #include "injector/memory.h" #include "injector/files.h" #include "utils/access.h" +#include "locale/locale.h" #define COMPILATION_DATE __DATE__ " " __TIME__ @@ -20,18 +21,40 @@ int main(int argc, char* argv[]) LPVOID allocatedMem; HANDLE thread; + std::vector dlls; + SetConsoleTitle(L"Sea of Thieves DLL Loader - " COMPILATION_DATE); - std::vector dlls = GetFilesInDirectoryWithExtension(CurrentPath(), ".dll"); + if (!std::filesystem::exists("logs")) { + std::filesystem::create_directory("logs"); + } + + if (!std::filesystem::exists("locale")) { + logger::error("The \"locale\" directory is missing, please download the latest release from https://github.com/holasoyender/SoTLoader"); + system("pause"); + return 0; + } + + std::string langCode = getSystemLang(); + logger::info("Detected system language: ", langCode); + Locale lang(langCode); + + if (!std::filesystem::exists("libs")) { + std::filesystem::create_directory("libs"); + logger::info(lang.get("created_libs_folder")); + goto EXIT; + } + + dlls = GetFilesInDirectoryWithExtension(CurrentPath() + "\\libs", ".dll"); switch (dlls.size()) { { case 0: - logger::error("No DLLs found in current directory, please place DLLs in the same directory as this executable."); + logger::error(lang.get("no_dlls_found")); goto EXIT; } case 1: - logger::info("Found 1 DLL: ", dlls[0]); + logger::info(lang.get("found_one"), dlls[0]); dll_name = dlls[0]; break; default: @@ -43,9 +66,9 @@ int main(int argc, char* argv[]) dlls_names_list += pos + ". " + dll_name + "\n"; } - logger::warn("Found more than 1 DLL, please select one:\n", dlls_names_list); + logger::warn(lang.get("found_multiple"), dlls_names_list); - std::cout << "Enter DLL index: "; + std::cout << lang.get("input_index"); int dll_index = 0; std::cin >> dll_index; @@ -53,48 +76,55 @@ int main(int argc, char* argv[]) break; } - logger::info("Loading DLL: ", dll_name.substr(dll_name.find_last_of("\\") + 1), "..."); + logger::info(lang.get("loading_dll"), dll_name.substr(dll_name.find_last_of("\\") + 1), "..."); if (hwndproc == NULL) { - logger::error("Game not found, please check that the game is running and try again."); + logger::error(lang.get("fail_game_not_found")); goto EXIT; } hr = GetWindowThreadProcessId(hwndproc, &processID); if (FAILED(hr)) { - logger::error("Failed to get process ID, HRESULT: ", hr); + logger::error(lang.get("fail_process_id"), hr); goto EXIT; } - logger::info("Process \"", GAME_NAME, "\" found with ID: ", processID); + logger::info(lang.get("found_process_id"), ", HRESULT: ", processID, " (", GAME_NAME, ")"); if (!IsAdmin()) { - logger::error("Please run this program as administrator."); + logger::error(lang.get("fail_admin")); goto EXIT; } process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, processID); if (process == NULL) { auto Error = GetLastError(); - logger::error("Failed to open process, HRESULT: ", hr, " Error: ", Error); + logger::error(lang.get("fail_open_process"), ", HRESULT: ", hr, " Error: ", Error); goto EXIT; } if (IsModuleInProcess(process, dll_name)) { - logger::error("That DLL is already loaded in the process. Do you want to unload it? (y/n)"); + logger::error(lang.get("fail_already_loaded"), " (y/n)"); char unload; std::cin >> unload; if (unload == 'y' || unload == 'Y' || unload == 's' || unload == 'S') { if (!UnloadModuleFromProcess(process, dll_name)) { - logger::error("Failed to unload DLL, HRESULT: ", hr); + logger::error(lang.get("fail_unload"), ", HRESULT: ", hr); goto EXIT; } - logger::info("DLL unloaded successfully, reloading..."); + + logger::info(lang.get("dll_unloaded"), " (y/n)"); + char load; + std::cin >> load; + + if (load != 'y' && load != 'Y' && load != 's' && load != 'S') { + goto EXIT; + } } else { - logger::info("DLL not loaded."); + logger::info(lang.get("dll_not_unloaded")); goto EXIT; } } @@ -104,27 +134,27 @@ int main(int argc, char* argv[]) allocatedMem = VirtualAllocEx(process, NULL, sizeof(dll_name_char), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (allocatedMem == NULL) { - logger::error("Failed to allocate memory in process, HRESULT: ", hr); + logger::error(lang.get("fail_malloc"), ", HRESULT: ", hr); goto EXIT; } if (!WriteProcessMemory(process, allocatedMem, dll_name_char, sizeof(dll_name_char), NULL)) { - logger::error("Failed to write to process memory, HRESULT: ", hr); + logger::error(lang.get("fail_write_memory"), ", HRESULT: ", hr); goto EXIT; } thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, allocatedMem, 0, NULL); if (thread == NULL) { - logger::error("Failed to create remote thread, HRESULT: ", hr); + logger::error(lang.get("fail_create_thread"), ", HRESULT: ", hr); goto EXIT; } if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) { - logger::error("Failed to wait for thread, HRESULT: ", hr); - goto EXIT; + logger::error(lang.get("fail_thread_wait"), ", HRESULT: ", hr); + goto EXIT; } - logger::info("DLL loaded successfully!"); + logger::info(lang.get("dll_loaded")); if (thread != NULL) CloseHandle(thread); diff --git a/SoTLoader/utils/logger.h b/SoTLoader/utils/logger.h index 4d5bd3b..831c9eb 100644 --- a/SoTLoader/utils/logger.h +++ b/SoTLoader/utils/logger.h @@ -4,7 +4,7 @@ namespace logger { - std::string FILE_NAME = "SoTLoader.log"; + std::string FILE_NAME = "logs/SoTLoader.log"; template std::string getDateFormatted() { diff --git a/langs/en.yml b/langs/en.yml new file mode 100644 index 0000000..e8a121e --- /dev/null +++ b/langs/en.yml @@ -0,0 +1,21 @@ +created_libs_folder: "Created \"libs\" directory, please place DLLs in that directory and try again." +no_dlls_found: "No DLLs found in \"libs\" directory, please place DLLs in that directory and try again." +found_one: "Found one DLL: " +found_multiple: "Found more than 1 DLL, please select one:\n" +input_index: "Enter DLL index: " +loading_dll: "Loading DLL: " +found_process_id: "Process found with ID: " +dll_unloaded: "DLL unloaded successfully, do you want to load it again?" +dll_not_unloaded: "DLL not unloaded, exiting..." +dll_loaded: "DLL loaded successfully!" + +fail_game_not_found: "Game not found, please check that the game is running and try again." +fail_process_id: "Failed to get process ID" +fail_admin: "Please run this program as administrator and try again." +fail_open_process: "Failed to open process" +fail_already_loaded: "That DLL is already loaded in the process. Do you want to unload it?" +fail_unload: "Failed to unload DLL" +fail_malloc: "Failed to allocate memory in the process" +fail_write_memory: "Failed to write memory in the process" +fail_create_thread: "Failed to create remote thread in the process" +fail_thread_wait: "Failed to wait for remote thread in the process" \ No newline at end of file diff --git a/langs/es.yml b/langs/es.yml new file mode 100644 index 0000000..a876731 --- /dev/null +++ b/langs/es.yml @@ -0,0 +1,21 @@ +created_libs_folder: "Se ha creado el directorio \"libs\", por favor coloque los DLLs en ese directorio y vuelva a intentarlo." +no_dlls_found: "No se encontraron DLLs en el directorio \"libs\", por favor coloque los DLLs en ese directorio y vuelva a intentarlo." +found_one: "Se encontró un DLL: " +found_multiple: "Se encontraron más de 1 DLL, por favor seleccione uno:\n" +input_index: "Ingrese el índice del DLL: " +loading_dll: "Cargando DLL: " +found_process_id: "Proceso encontrado con ID: " +dll_unloaded: "DLL descargado con éxito, ¿quieres volver a cargarlo?" +dll_not_unloaded: "DLL no descargado, saliendo..." +dll_loaded: "DLL cargado con éxito!" + +fail_game_not_found: "Juego no encontrado, por favor verifique que el juego se esté ejecutando y vuelva a intentarlo." +fail_process_id: "No se pudo obtener el ID del proceso" +fail_admin: "Por favor, ejecute este programa como administrador y vuelva a intentarlo." +fail_open_process: "No se pudo abrir el proceso" +fail_already_loaded: "Ese DLL ya está cargado en el proceso. ¿Quieres descargarlo?" +fail_unload: "No se pudo descargar DLL" +fail_malloc: "No se pudo asignar memoria en el proceso" +fail_write_memory: "No se pudo escribir en la memoria del proceso" +fail_create_thread: "No se pudo crear un hilo remoto en el proceso" +fail_thread_wait: "No se pudo esperar el hilo remoto en el proceso" \ No newline at end of file diff --git a/yaml b/yaml new file mode 160000 index 0000000..fcbb819 --- /dev/null +++ b/yaml @@ -0,0 +1 @@ +Subproject commit fcbb8193b94921e058be7b563aea053531e5b2d9