diff --git a/EDFModLoader/EDFModLoader.vcxproj b/EDFModLoader/EDFModLoader.vcxproj index 5396b55..2de580a 100644 --- a/EDFModLoader/EDFModLoader.vcxproj +++ b/EDFModLoader/EDFModLoader.vcxproj @@ -113,7 +113,8 @@ true pch.h .\;..\HookLib\HookLib\HookLib;%(AdditionalIncludeDirectories) - MultiThreadedDebug + MultiThreadedDebugDLL + stdcpp17 Windows @@ -132,7 +133,8 @@ true pch.h .\;..\HookLib\HookLib\HookLib;%(AdditionalIncludeDirectories) - MultiThreadedDebug + MultiThreadedDebugDLL + stdcpp17 Windows @@ -153,6 +155,7 @@ pch.h .\;..\HookLib\HookLib\HookLib;%(AdditionalIncludeDirectories) true + stdcpp17 Windows @@ -176,6 +179,7 @@ pch.h .\;..\HookLib\HookLib\HookLib;%(AdditionalIncludeDirectories) true + stdcpp17 Windows diff --git a/EDFModLoader/dllmain.cpp b/EDFModLoader/dllmain.cpp index f4a5fba..9f214b5 100644 --- a/EDFModLoader/dllmain.cpp +++ b/EDFModLoader/dllmain.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "proxy.h" #include "PluginAPI.h" @@ -19,6 +20,7 @@ #include #include +#include typedef struct { @@ -36,8 +38,8 @@ static std::vector hooks; // Holds all original hooked functions typedef void* (__fastcall *initterm_func)(void*, void*); static initterm_func initterm_orig; // Handles opening files from disk or through CRI File System -typedef void* (__fastcall *fnk244d0_func)(void*, void*, void*); -static fnk244d0_func fnk244d0_orig; +typedef void* (__fastcall *crifsio_func)(void*, void*, void*); +static crifsio_func crifsio_orig; // Puts wchar_t* in wstring typedef void* (__fastcall *wstrassign_func)(void*, const wchar_t*, size_t); static wstrassign_func wstrassign_orig; @@ -143,12 +145,7 @@ typedef struct { int version; } PointerSet; -int pointerSet = -1; -PointerSet psets[3] = { - {0xaa36d0, L"EarthDefenceForce 4.1 for Windows", "EDF41", "EML4_Load", {0x667102, 0x8ed80, 0x91580, 0x91790}, 41}, // EDF 4.1 - {0xebcbd0, L"EarthDefenceForce 5 for PC", "EDF5", "EML5_Load", {0x9c835a, 0x244d0, 0x27380, 0x27680}, 5}, // EDF 5 - {0x17e4210, L"EarthDefenceForce 6 for PC", "EDF6", "EML6_Load", {0x12d6f26, 0x748d0, 0x3d440, 0x3e490}, 6}, // EDF 6 -}; +static const char *plugFunc; // Search and load all *.dll files in Mods\Plugins\ folder static void LoadPluginsFromPath(LPCWSTR path, bool asi) { @@ -166,7 +163,7 @@ static void LoadPluginsFromPath(LPCWSTR path, bool asi) { PLOG_INFO << "Loading Plugin: " << plugpath; HMODULE plugin = LoadLibraryW(plugpath); if (plugin != NULL) { - LoadDef loadfunc = (LoadDef)GetProcAddress(plugin, psets[pointerSet].plugfunc); + LoadDef loadfunc = (LoadDef)GetProcAddress(plugin, plugFunc); bool unload = false; if (loadfunc != NULL) { PluginInfo *pluginInfo = new PluginInfo(); @@ -208,7 +205,7 @@ static void LoadPluginsFromPath(LPCWSTR path, bool asi) { procedure(); } } else { - PLOG_WARNING << "Plugin does not contain " << psets[pointerSet].plugfunc << " function"; + PLOG_WARNING << "Plugin does not contain " << plugFunc << " function"; unload = true; } if (unload) { @@ -270,7 +267,7 @@ struct oddstr { }; static_assert(sizeof(oddstr) == 32, "Weird string structure should have a length of 32"); -static void *__fastcall fnk244d0_hook(void *unk1, oddstr *str, void *unk2) { +static void *__fastcall crifsio_hook(void *unk1, oddstr *str, void *unk2) { // First 8 bytes are a pointer if length >= 8 // Otherwise string is stored where pointer will be? wchar_t *path = (wchar_t*)str; @@ -290,7 +287,7 @@ static void *__fastcall fnk244d0_hook(void *unk1, oddstr *str, void *unk2) { } delete[] modpath; } - return fnk244d0_orig(unk1, str, unk2); + return crifsio_orig(unk1, str, unk2); } // Internal logging hook @@ -361,6 +358,21 @@ static const char ModLoaderStr[] = "ModLoader"; PBYTE hmodEXE; char hmodName[MAX_PATH]; +PointerSet psets[] = { + {0xaa36d0, L"EarthDefenceForce 4.1 for Windows", "EDF41", "EML4_Load", {0x667102, 0x8ed80, 0x91580, 0x91790}, 41}, // EDF 4.1 + {0xebcbd0, L"EarthDefenceForce 5 for PC", "EDF5", "EML5_Load", {0x9c835a, 0x244d0, 0x27380, 0x27680}, 5}, // EDF 5 +}; + +static uintptr_t ScanPtr(LightningScanner::ScanResult result) { + void *addr = result.Get(); + if (addr != NULL) { + // Return the result as an offset to the executable + return (uintptr_t)addr - (uintptr_t)hmodEXE; + } else { + return NULL; + } +} + void SetupHook(uintptr_t offset, void **func, void* hook, const char *reason, BOOL active) { if (!offset) { PLOG_INFO << "Skipping unsupported " << hmodName << " hook (" << reason << ")"; @@ -412,62 +424,114 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv PluginInfo *selfInfo = new PluginInfo; selfInfo->infoVersion = PluginInfo::MaxInfoVer; selfInfo->name = "EDFModLoader"; - selfInfo->version = PLUG_VER(1, 0, 8, 0); + selfInfo->version = PLUG_VER(1, 0, 9, 0); PluginData *selfData = new PluginData; selfData->info = selfInfo; selfData->module = hModule; plugins.push_back(selfData); + PluginVersion v = selfInfo->version; + PLOG_INFO.printf("EDFModLoader v%u.%u.%u Initializing\n", v.major, v.minor, v.patch); + + // Setup DLL proxy + wchar_t path[MAX_PATH]; + if (!GetWindowsDirectoryW(path, _countof(path))) { + DWORD dwError = GetLastError(); + PLOG_ERROR << "Failed to get windows directory path: error " << dwError; + return FALSE; + } + + wcscat_s(path, L"\\System32\\winmm.dll"); + + PLOG_INFO << "Loading real winmm.dll"; + HMODULE syswinmm = LoadLibraryW(path); + if (syswinmm == NULL) { + DWORD dwError = GetLastError(); + PLOG_ERROR << "Failed to load system winmm.dll: error " << dwError; + return FALSE; + } + PLOG_INFO << "Setting up dll proxy functions"; + setupFunctions(syswinmm); + // Determine what game is hosting us hmodEXE = (PBYTE)GetModuleHandleW(NULL); if (hmodEXE == NULL) { PLOG_ERROR << "Failed to retrieve a handle to the currently running game"; - return FALSE; + break; } GetModuleFileNameA((HMODULE)hmodEXE, hmodName, _countof(hmodName)); char *hmodFName = PathFindFileNameA(hmodName); memmove(hmodName, hmodFName, strlen(hmodFName) + 1); + + int pointerSet = -1; for (int i = 0; i < _countof(psets); i++) { - // TODO: Hack to support EDF6 - if (lstrcmpiA(hmodName, "EDF6.exe") == 0 && psets[i].version == 6) - { - hmodEXE = (PBYTE)GetModuleHandleW(L"EDF.dll"); - if (hmodEXE == NULL) { - PLOG_ERROR << "Failed to retrieve a handle to EDF.dll"; - return FALSE; - } - GetModuleFileNameA((HMODULE)hmodEXE, hmodName, _countof(hmodName)); - char *hmodFName = PathFindFileNameA(hmodName); - memmove(hmodName, hmodFName, strlen(hmodFName) + 1); - } size_t search_len = wcslen(psets[i].search); if (!IsBadReadPtr(hmodEXE + psets[i].offset, search_len+1) && !wcsncmp((wchar_t*)(hmodEXE + psets[i].offset), psets[i].search, search_len)) { pointerSet = i; break; } } - if (pointerSet == -1) { - PLOG_ERROR << "Failed to determine what exe is running"; - return FALSE; - } - FuncOffsets pointers = psets[pointerSet].pointers; + FuncOffsets pointers = {}; + if (pointerSet != -1) { + PLOG_INFO << "Running under " << psets[pointerSet].ident; + pointers = psets[pointerSet].pointers; + plugFunc = psets[pointerSet].plugfunc; + } else if (lstrcmpiA(hmodName, "EDF6.exe") == 0) { + PLOG_INFO << "Running under EDF6"; + plugFunc = "EML6_Load"; + + // Get a handle to EDF.dll + hmodEXE = (PBYTE)GetModuleHandleW(L"EDF.dll"); + if (hmodEXE == NULL) { + PLOG_ERROR << "Failed to retrieve a handle to EDF.dll"; + break; + } - PluginVersion v = selfInfo->version; - PLOG_INFO.printf("EDFModLoader (%s) v%u.%u.%u Initializing\n", psets[pointerSet].ident, v.major, v.minor, v.patch); + // Determine bounds of executable + MODULEINFO modInfo = {}; + if (!GetModuleInformation(GetCurrentProcess(), (HMODULE)hmodEXE, &modInfo, sizeof(MODULEINFO))) { + PLOG_ERROR << "Failed to fetch size information for EDF.dll"; + break; + } + size_t ScanRange = modInfo.SizeOfImage - 32; // Reduce range by 32 to workaround overrun bug in LightningScanner + + // Update the module name + GetModuleFileNameA((HMODULE)hmodEXE, hmodName, _countof(hmodName)); + char *hmodFName = PathFindFileNameA(hmodName); + memmove(hmodName, hmodFName, strlen(hmodFName) + 1); + + // Hook the DLLs secondary entrypoint for additional initialization + pointers.initterm = (uintptr_t)GetProcAddress((HMODULE)hmodEXE, "CPP_OnBoot"); + if (pointers.initterm != NULL) { + pointers.initterm -= (uintptr_t)hmodEXE; + } else { + PLOG_ERROR << "Failed to locate CPP_OnBoot function"; + } - // Setup DLL proxy - wchar_t path[MAX_PATH]; - if (!GetWindowsDirectoryW(path, _countof(path))) { - DWORD dwError = GetLastError(); - PLOG_ERROR << "Failed to get windows directory path: error " << dwError; - return FALSE; - } + // Find patch points via pointer scanning + LightningScanner::Scanner scanner(""); - wcscat_s(path, L"\\System32\\winmm.dll"); + scanner = LightningScanner::Scanner("48895C2418488974242055574156488BEC4883EC70488B05????F7"); + pointers.redirect = ScanPtr(scanner.Find(hmodEXE, ScanRange)); + if (pointers.redirect == NULL) { + PLOG_ERROR << "Failed to locate CriFsIo function"; + } - PLOG_INFO << "Loading real winmm.dll"; - PLOG_INFO << "Setting up dll proxy functions"; - setupFunctions(LoadLibraryW(path)); + scanner = LightningScanner::Scanner("48895C240848896C2410488974241857415641574883EC20488B71"); + pointers.wstrassign = ScanPtr(scanner.Find(hmodEXE, ScanRange)); + if (pointers.wstrassign == NULL) { + PLOG_ERROR << "Failed to locate std::wstring::assign function"; + } + + scanner = LightningScanner::Scanner("48895424104C894424184C894C2420C3488D"); + pointers.gamelog = ScanPtr(scanner.Find(hmodEXE, ScanRange)); + if (pointers.gamelog == NULL) { + PLOG_ERROR << "Failed to locate game debug logging function"; + } + } else { + PLOG_ERROR << "Failed to determine what exe is running"; + break; + } // Create ModLoader folders CreateDirectoryW(L"Mods", NULL); @@ -476,7 +540,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv // Initialize MinHook if (MH_Initialize() != MH_OK) { PLOG_ERROR << "Failed to initialize MinHook"; - return FALSE; + break; } // Hook function for additional ModLoader initialization @@ -484,7 +548,11 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv // Add Mods folder redirector hook wstrassign_orig = (wstrassign_func)((PBYTE)hmodEXE + pointers.wstrassign); - SetupHook(pointers.redirect, (PVOID*)&fnk244d0_orig, fnk244d0_hook, "Mods folder redirector", Redirect); + if (pointers.wstrassign) { + SetupHook(pointers.redirect, (PVOID *)&crifsio_orig, crifsio_hook, "Mods folder redirector", Redirect); + } else { + PLOG_INFO << "Skipping unsupported " << hmodName << " hook (Mods folder redirector, no std::wstring::assign)"; + } // Add internal logging hook SetupHook(pointers.gamelog, (PVOID*)&gamelog_orig, gamelog_hook, "Interal logging hook", GameLog); diff --git a/Patcher/EDF6/ExtraPatches/ChangeFOV.txt b/Patcher/EDF6/ExtraPatches/ChangeFOV.txt index b299392..faa0220 100644 --- a/Patcher/EDF6/ExtraPatches/ChangeFOV.txt +++ b/Patcher/EDF6/ExtraPatches/ChangeFOV.txt @@ -11,5 +11,5 @@ f32! 0.7853982 ; 45 vFOV (Default) ;f32! 1.5707964 ; 90 vFOV ;f32! 2.9670596 ; Quake Pro -aob vFOVoff F3 0F 10 05 ?? ?? ?? ?? F3 0F 5E 87 10 04 00 00 +aob vFOVoff F30F1005????????F30F5E87 vFOVoff+4: rel32! vFOV \ No newline at end of file diff --git a/Patcher/EDF6/ExtraPatches/ChangePickupLimits.txt b/Patcher/EDF6/ExtraPatches/ChangePickupLimits.txt index 9cf5521..f26f45a 100644 --- a/Patcher/EDF6/ExtraPatches/ChangePickupLimits.txt +++ b/Patcher/EDF6/ExtraPatches/ChangePickupLimits.txt @@ -1,5 +1,9 @@ ; Author: demonized and redone -2C7709: u32! 128 ; Maximum pickups on a map at once. Default 128 +aob maxOnMap 288000000089 +aob maxMission1 00040000735B +aob maxMission2 00040000730EFF -2C737A: u32! 1024 ;Maximum pickups per mission. Default 1024 -2C73C7: u32! 1024 ;Synchronize with above value \ No newline at end of file +maxOnMap+1: u32! 128 ; Maximum pickups on a map at once. Default 128 + +maxMission1: u32! 1024 ;Maximum pickups per mission. Default 1024 +maxMission2: u32! 1024 ;Synchronize with above value \ No newline at end of file diff --git a/Patcher/EDF6/Patches/EOSLauncherBypass.txt b/Patcher/EDF6/Patches/EOSLauncherBypass.txt index 70b57d4..627b2a9 100644 --- a/Patcher/EDF6/Patches/EOSLauncherBypass.txt +++ b/Patcher/EDF6/Patches/EOSLauncherBypass.txt @@ -1,2 +1,3 @@ ; Author: BlueAmulet -702CC2: 90 E9 \ No newline at end of file +aob EOSCheck 200F845E +EOSCheck+1: 90 E9 \ No newline at end of file diff --git a/Patcher/EDF6/Patches/RemoveChatCensor.txt b/Patcher/EDF6/Patches/RemoveChatCensor.txt index 179ff10..18fe312 100644 --- a/Patcher/EDF6/Patches/RemoveChatCensor.txt +++ b/Patcher/EDF6/Patches/RemoveChatCensor.txt @@ -1,2 +1,3 @@ ; Author: Souzooka -71E4A2: 90 E9 \ No newline at end of file +aob ChatFilter 604885C90F84BF +ChatFilter+4: 90 E9 \ No newline at end of file diff --git a/Patcher/dllmain.cpp b/Patcher/dllmain.cpp index 53d829c..1fd7bdf 100644 --- a/Patcher/dllmain.cpp +++ b/Patcher/dllmain.cpp @@ -161,6 +161,7 @@ void EMLCommon_Load(BOOL EDF6) { ltprintf("Failed to fetch size information for %s", hmodName); return; } + size_t ScanRange = modInfo.SizeOfImage - 32; // Reduce range by 32 to workaround overrun bug in LightningScanner SYSTEM_INFO sysInfo = {0}; GetSystemInfo(&sysInfo); @@ -289,14 +290,16 @@ void EMLCommon_Load(BOOL EDF6) { // TODO: Validate pattern if (valid) { - const auto scanner = LightningScanner::Scanner(LightningScanner::Pattern(pattern)); - void *result = scanner.Find(hmodEXE, modInfo.SizeOfImage).Get(); + const LightningScanner::Scanner scanner = LightningScanner::Scanner(LightningScanner::Pattern(pattern)); + void *result = scanner.Find(hmodEXE, ScanRange).Get(); if (result != NULL) { labels[symbol] = (uintptr_t)result - (uintptr_t)hmodEXE; ltprintf("AOB scan found %s at %I64X", symbol.c_str(), labels[symbol]); } else { ltprintf("AOB scan failed: %s: %s", symbol.c_str(), pattern.c_str()); patch = false; + // Create dummy label anyway to reduce future errors + labels[symbol] = (uintptr_t)hmodEXE; } } else { patch = false; @@ -401,7 +404,8 @@ void EMLCommon_Load(BOOL EDF6) { ltprintf("Allocation failed: %s bytes for %s", sizeStr, arguments[0].c_str()); patch = false; } - } else { + } + if (!patch) { // Patch failed, but create dummy label anyway to reduce future errors labels[arguments[0]] = (uintptr_t)target - (uintptr_t)hmodEXE; }