Skip to content

Commit

Permalink
Localization!
Browse files Browse the repository at this point in the history
  • Loading branch information
holasoyender committed Aug 29, 2023
1 parent b03a0d0 commit 202a400
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 29 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "yaml"]
path = yaml
url = https://github.com/jbeder/yaml-cpp
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions SoTLoader/SoTLoader.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>false</UseOfMfc>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
Expand Down Expand Up @@ -138,6 +139,8 @@
<ItemGroup>
<ClInclude Include="injector\files.h" />
<ClInclude Include="injector\memory.h" />
<ClInclude Include="locale\locale.h" />
<ClInclude Include="locale\utils.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
Expand Down
21 changes: 15 additions & 6 deletions SoTLoader/SoTLoader.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="headers\locale">
<UniqueIdentifier>{8df5946d-a287-4ac8-997a-efb83585596d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
Expand All @@ -29,12 +32,6 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>resources</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>resources</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>headers</Filter>
</ClInclude>
Expand All @@ -53,6 +50,18 @@
<ClInclude Include="utils\access.h">
<Filter>headers\utils</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="locale\locale.h">
<Filter>headers\locale</Filter>
</ClInclude>
<ClInclude Include="locale\utils.h">
<Filter>headers\locale</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="SoTLoader.rc">
Expand Down
77 changes: 77 additions & 0 deletions SoTLoader/locale/locale.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include "utils.h"
#include <yaml-cpp/yaml.h>

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<std::string>();
}
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;
}

};
12 changes: 12 additions & 0 deletions SoTLoader/locale/utils.h
Original file line number Diff line number Diff line change
@@ -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());
}
72 changes: 51 additions & 21 deletions SoTLoader/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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__

Expand All @@ -20,18 +21,40 @@ int main(int argc, char* argv[])
LPVOID allocatedMem;
HANDLE thread;

std::vector<std::string> dlls;

SetConsoleTitle(L"Sea of Thieves DLL Loader - " COMPILATION_DATE);

std::vector<std::string> 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:
Expand All @@ -43,58 +66,65 @@ 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;
dll_name = dlls[dll_index - 1];
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;
}
}
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion SoTLoader/utils/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace logger {

std::string FILE_NAME = "SoTLoader.log";
std::string FILE_NAME = "logs/SoTLoader.log";

template<typename = std::string>
std::string getDateFormatted() {
Expand Down
21 changes: 21 additions & 0 deletions langs/en.yml
Original file line number Diff line number Diff line change
@@ -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"
21 changes: 21 additions & 0 deletions langs/es.yml
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions yaml
Submodule yaml added at fcbb81

0 comments on commit 202a400

Please sign in to comment.