diff --git a/bink2w64.vcxproj b/bink2w64.vcxproj
index ba3b385..79093f0 100644
--- a/bink2w64.vcxproj
+++ b/bink2w64.vcxproj
@@ -100,6 +100,7 @@
+
diff --git a/bink2w64.vcxproj.filters b/bink2w64.vcxproj.filters
index ab80bf9..f265fd8 100644
--- a/bink2w64.vcxproj.filters
+++ b/bink2w64.vcxproj.filters
@@ -36,6 +36,7 @@
src
+
diff --git a/src/dllmain.cpp b/src/dllmain.cpp
index f7afcfd..a59669b 100644
--- a/src/dllmain.cpp
+++ b/src/dllmain.cpp
@@ -1,5 +1,4 @@
#include "dllexports.h"
-#define ASI_LOG_FNAME "bink2w64_proxy.log"
#include
#include
@@ -125,7 +124,54 @@ void SetWorkingDirectory() {
SetCurrentDirectoryW(exePath.parent_path().c_str());
}
-void __stdcall OnAttach()
+void __stdcall AsyncAttach()
+{
+ // Handle logic depending on the attached-to exe.
+ switch (GLEBinkProxy.Game)
+ {
+ case LEGameVersion::Launcher:
+ {
+ // Keep trying to find a pattern we are *almost* guaranteed to have until we find it.
+ DRM::WaitForDRMv3();
+
+ if (!GLEBinkProxy.LauncherArgs->Activate())
+ {
+ GLogger.writeln(L"OnAttach: ERROR: handling of Launcher args failed, aborting!");
+ }
+
+ // Hook CreateProcessA so we can properly set the working directory when launching a game.
+ if (!GLEBinkProxy.LauncherFixer->Activate())
+ {
+ GLogger.writeln(L"OnAttach: ERROR: error fixing launcher working directory issue!");
+ }
+
+ break;
+ }
+ default:
+ {
+ GLogger.writeln(L"OnAttach: unsupported game, bye!");
+ return;
+ }
+ }
+
+ return;
+}
+
+using t_PostDRM_hook = void*(void*, char, size_t, void*);
+t_PostDRM_hook* PostDRM_hook_orig = nullptr;
+void* PostDRM_hook(void* param_1, char param_2, size_t param_3, void* param_4)
+{
+ void* ret = PostDRM_hook_orig(param_1, param_2, param_3, param_4);
+ // Unlock the console.
+ if (!GLEBinkProxy.ConsoleEnabler->Activate())
+ {
+ GLogger.writeln(L"OnAttach: ERROR: console bypass installation failed");
+ }
+ GLEBinkProxy.AsiLoader->PostLoad(GLEBinkProxy.SPI);
+ return ret;
+}
+
+bool SyncAttach()
{
// Open console or log.
Utils::SetupOutput();
@@ -147,7 +193,7 @@ void __stdcall OnAttach()
if (mhStatus != MH_OK)
{
GLogger.writeln(L"OnAttach: ERROR: failed to initialize the hooking library (code = %d).", mhStatus);
- return;
+ return false;
}
// Initialize global settings.
@@ -170,6 +216,7 @@ void __stdcall OnAttach()
if (!GLEBinkProxy.AsiLoader->Activate())
{
GLogger.writeln(L"OnAttach: ERROR: loading of one or more ASI plugins failed!");
+ return false;
}
GLogger.writeln(L"OnAttach: got to preload / 0x%p", GLEBinkProxy);
@@ -178,57 +225,18 @@ void __stdcall OnAttach()
// Post-drm mods are loaded in the switch below.
GLEBinkProxy.AsiLoader->PreLoad(GLEBinkProxy.SPI);
- // Prevent the compiler from *potentially* reordering instructions before and after.
- MemoryBarrier();
-
- // Handle logic depending on the attached-to exe.
- switch (GLEBinkProxy.Game)
- {
- case LEGameVersion::LE1:
- case LEGameVersion::LE2:
- case LEGameVersion::LE3:
- {
- // Keep trying to find a pattern we are *almost* guaranteed to have until we find it.
- DRM::WaitForDRMv3();
-
- // Unlock the console.
- if (!GLEBinkProxy.ConsoleEnabler->Activate())
- {
- GLogger.writeln(L"OnAttach: ERROR: console bypass installation failed, aborting!");
- break;
- }
-
- // Load all native mods that declare being post-drm.
- GLEBinkProxy.AsiLoader->PostLoad(GLEBinkProxy.SPI);
-
- break;
- }
- case LEGameVersion::Launcher:
+ if (GLEBinkProxy.Game == LEGameVersion::LE1
+ || GLEBinkProxy.Game == LEGameVersion::LE2
+ || GLEBinkProxy.Game == LEGameVersion::LE3)
{
- // Keep trying to find a pattern we are *almost* guaranteed to have until we find it.
- DRM::WaitForDRMv3();
-
- if (!GLEBinkProxy.LauncherArgs->Activate())
- {
- GLogger.writeln(L"OnAttach: ERROR: handling of Launcher args failed, aborting!");
- }
-
- // Hook CreateProcessA so we can properly set the working directory when launching a game.
- if (!GLEBinkProxy.LauncherFixer->Activate())
- {
- GLogger.writeln(L"OnAttach: ERROR: error fixing launcher working directory issue!");
- }
-
- break;
+ DRM::InstallDRMv4Hook(PostDRM_hook, reinterpret_cast(&PostDRM_hook_orig));
}
- default:
+ else
{
- GLogger.writeln(L"OnAttach: unsupported game, bye!");
- return;
- }
+ CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)AsyncAttach, nullptr, 0, nullptr);
}
- return;
+ return true;
}
void __stdcall OnDetach()
@@ -252,7 +260,7 @@ void __stdcall OnDetach()
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
switch (dwReason) {
case DLL_PROCESS_ATTACH:
- CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)OnAttach, nullptr, 0, nullptr);
+ SyncAttach();
return TRUE;
case DLL_PROCESS_DETACH:
diff --git a/src/dllstruct.h b/src/dllstruct.h
index 0379f65..0100dfc 100644
--- a/src/dllstruct.h
+++ b/src/dllstruct.h
@@ -170,7 +170,7 @@ struct LEBinkProxy
auto leargs = GetStringValueFromHKCU(L"SOFTWARE\\ME3Tweaks", L"LEAutobootArgs");
if (leargs.length() > 0)
{
- CmdLine = new wchar_t[leargs.length()];
+ CmdLine = new wchar_t[leargs.length() + 1];
wcscpy(CmdLine, leargs.data());
DeleteValueFromHKCU(L"SOFTWARE\\ME3Tweaks", L"LEAutobootArgs");
}
@@ -196,4 +196,4 @@ struct LEBinkProxy
// Global instance.
-static LEBinkProxy GLEBinkProxy;
+inline LEBinkProxy GLEBinkProxy;
diff --git a/src/drm.cpp b/src/drm.cpp
new file mode 100644
index 0000000..b813a8f
--- /dev/null
+++ b/src/drm.cpp
@@ -0,0 +1,105 @@
+#include "drm.h"
+#include
+#include
+
+void DRM::WaitForDRMv2()
+{
+ GLogger.writeln(L"WaitForDRMv2: waiting for DRM...");
+
+ if (!DrmEvent->InError())
+ {
+ auto rc = DrmEvent->WaitForIt(10000); // 10 seconds timeout should be more than enough
+ switch (rc)
+ {
+ case Utils::EventWaitValue::Signaled:
+ GLogger.writeln(L"WaitForDRMv2: event signaled!");
+ delete DrmEvent;
+ DrmEvent = nullptr;
+ break;
+ default:
+ GLogger.writeln(L"WaitForDRMv2: event wait failed (EventWaitValue = %d)", (int)rc);
+ }
+ }
+}
+
+void DRM::WaitForDRMv3()
+{
+ int iterations = 0;
+ bool foundPattern = false;
+ BYTE* offset = nullptr;
+
+ do
+ {
+ switch (GLEBinkProxy.Game)
+ {
+ case LEGameVersion::LE1:
+ foundPattern = nullptr != Utils::ScanProcess(LE1_UFunctionBind_Pattern, LE1_UFunctionBind_Mask);
+ break;
+ case LEGameVersion::LE2:
+ foundPattern = nullptr != Utils::ScanProcess(LE2_UFunctionBind_Pattern, LE2_UFunctionBind_Mask);
+ break;
+ case LEGameVersion::LE3:
+ foundPattern = nullptr != Utils::ScanProcess(LE3_UFunctionBind_Pattern, LE3_UFunctionBind_Mask);
+ break;
+ case LEGameVersion::Launcher:
+ foundPattern = nullptr != Utils::ScanProcess(LEL_DRMTest_Pattern, LEL_DRMTest_Mask);
+ break;
+ default:
+ foundPattern = true;
+ }
+
+ iterations++;
+
+ } while (!foundPattern && iterations < 20000);
+
+ if (!foundPattern)
+ {
+ GLogger.writeln(L"WaitForDRMv3 - FAILED TO FIND THE PATTERN in %d iteration(s), but still stopping the poll because YOLO.", iterations);
+ return;
+ }
+
+ GLogger.writeln(L"WaitForDRMv3 - found the pattern in %d iteration(s), stopping the poll.", iterations);
+}
+
+BYTE* GetActivatorModuleBase() {
+ DWORD const CurrentProcess = GetCurrentProcessId();
+ HANDLE const ProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, CurrentProcess);
+
+ BYTE* BaseAddress = nullptr;
+
+ if (ProcessSnapshot != INVALID_HANDLE_VALUE) {
+ MODULEENTRY32 ModuleEntry{};
+ ModuleEntry.dwSize = sizeof ModuleEntry;
+
+ if (Module32First(ProcessSnapshot, &ModuleEntry)) {
+ do {
+ auto const* const ModuleName = ModuleEntry.szModule;
+ if (ModuleName == std::wcsstr(ModuleName, L"Activation64")) {
+ BaseAddress = ModuleEntry.modBaseAddr;
+ break;
+ }
+ } while (Module32Next(ProcessSnapshot, &ModuleEntry));
+ }
+
+ CloseHandle(ProcessSnapshot);
+ }
+
+ return BaseAddress;
+}
+
+void DRM::InstallDRMv4Hook(void* detour, void** original)
+{
+ auto const ActivatorBase = GetActivatorModuleBase();
+ if (!ActivatorBase) {
+ GLogger.writeln(L"InstallDRMv4Hook: ERROR: failed to find Activation64.dll base address.");
+ return;
+ }
+ auto const Offset = ActivatorBase + 0x11480;
+ auto spiReturn = GLEBinkProxy.SPI->InstallHook("PostDRM_hook", Offset, detour, original);
+ if (spiReturn != SPIReturn::Success)
+ {
+ GLogger.writeln(L"InstallDRMv4Hook: ERROR: failed to install post-drm hook (code = %d).", static_cast(spiReturn));
+ return;
+ }
+ GLogger.writeln(L"InstallDRMv4Hook: successfully installed hook on Activation64.dll.");
+}
\ No newline at end of file
diff --git a/src/drm.h b/src/drm.h
index 87ef783..2385c64 100644
--- a/src/drm.h
+++ b/src/drm.h
@@ -1,10 +1,10 @@
#pragma once
-#include "gamever.h"
#include "utils/io.h"
#include "utils/event.h"
-#include "utils/hook.h"
#include "dllstruct.h"
+#include
+#include
namespace DRM
@@ -13,12 +13,12 @@ namespace DRM
// Version 2
// ======================================================================
- static Utils::Event* DrmEvent = nullptr;
- bool GameWindowCreated = false;
+ inline Utils::Event* DrmEvent = nullptr;
+ inline bool GameWindowCreated = false;
typedef HWND(WINAPI* CREATEWINDOWEXW)(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
- CREATEWINDOWEXW CreateWindowExW_orig = nullptr;
- HWND WINAPI CreateWindowExW_hooked(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
+ inline CREATEWINDOWEXW CreateWindowExW_orig = nullptr;
+ inline HWND WINAPI CreateWindowExW_hooked(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
{
//GLogger.writeln(L"CreateWindowExW: lpWindowName = %s", lpWindowName);
if (nullptr != lpWindowName
@@ -34,70 +34,17 @@ namespace DRM
return CreateWindowExW_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}
- void InitializeDRMv2()
+ inline void InitializeDRMv2()
{
DrmEvent = new Utils::Event(L"drm_wait");
}
- void WaitForDRMv2()
- {
- GLogger.writeln(L"WaitForDRMv2: waiting for DRM...");
-
- if (!DrmEvent->InError())
- {
- auto rc = DrmEvent->WaitForIt(10000); // 10 seconds timeout should be more than enough
- switch (rc)
- {
- case Utils::EventWaitValue::Signaled:
- GLogger.writeln(L"WaitForDRMv2: event signaled!");
- delete DrmEvent;
- DrmEvent = nullptr;
- break;
- default:
- GLogger.writeln(L"WaitForDRMv2: event wait failed (EventWaitValue = %d)", (int)rc);
- }
- }
- }
+ void WaitForDRMv2();
// Version 3
// ======================================================================
- void WaitForDRMv3()
- {
- int iterations = 0;
- bool foundPattern = false;
- BYTE* offset = nullptr;
-
- do
- {
- switch (GLEBinkProxy.Game)
- {
- case LEGameVersion::LE1:
- foundPattern = nullptr != Utils::ScanProcess(LE1_UFunctionBind_Pattern, LE1_UFunctionBind_Mask);
- break;
- case LEGameVersion::LE2:
- foundPattern = nullptr != Utils::ScanProcess(LE2_UFunctionBind_Pattern, LE2_UFunctionBind_Mask);
- break;
- case LEGameVersion::LE3:
- foundPattern = nullptr != Utils::ScanProcess(LE3_UFunctionBind_Pattern, LE3_UFunctionBind_Mask);
- break;
- case LEGameVersion::Launcher:
- foundPattern = nullptr != Utils::ScanProcess(LEL_DRMTest_Pattern, LEL_DRMTest_Mask);
- break;
- default:
- foundPattern = true;
- }
-
- iterations++;
-
- } while (!foundPattern && iterations < 20000);
+ void WaitForDRMv3();
- if (!foundPattern)
- {
- GLogger.writeln(L"WaitForDRMv3 - FAILED TO FIND THE PATTERN in %d iteration(s), but still stopping the poll because YOLO.", iterations);
- return;
- }
-
- GLogger.writeln(L"WaitForDRMv3 - found the pattern in %d iteration(s), stopping the poll.", iterations);
- }
+ void InstallDRMv4Hook(void* detour, void** original);
}
diff --git a/src/modules/asi_loader.h b/src/modules/asi_loader.h
index 2056577..9825df0 100644
--- a/src/modules/asi_loader.h
+++ b/src/modules/asi_loader.h
@@ -453,9 +453,6 @@ class AsiLoaderModule
}
}
- //// Give async plugins some time to do things.
- Sleep(250);
-
return true;
}
bool PostLoad(ISharedProxyInterface* interfacePtr)
@@ -473,9 +470,6 @@ class AsiLoaderModule
}
}
- //// Give async plugins some time to do things.
- Sleep(250);
-
return true;
}
};
diff --git a/src/modules/launcher_working_dir_fix.h b/src/modules/launcher_working_dir_fix.h
index 2243a28..c95d9d0 100644
--- a/src/modules/launcher_working_dir_fix.h
+++ b/src/modules/launcher_working_dir_fix.h
@@ -12,8 +12,8 @@
// These have to list outside the class or the actual hooking signature won't be correct
typedef int (WINAPI* CREATEPROCESSA)(LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES,
BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
-CREATEPROCESSA CreateProcessA_orig = NULL;
-int WINAPI CreateProcessA_hook(LPCSTR lpApplicationName, LPSTR lpCommandLine,
+inline CREATEPROCESSA CreateProcessA_orig = NULL;
+inline int WINAPI CreateProcessA_hook(LPCSTR lpApplicationName, LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
diff --git a/src/spi/implementation.h b/src/spi/implementation.h
index d042916..20a2be8 100644
--- a/src/spi/implementation.h
+++ b/src/spi/implementation.h
@@ -162,7 +162,7 @@ namespace SPI
}
*outGameVersion = static_cast(GLEBinkProxy.Game);
- return SPIReturn::FailureUnsupportedYet;
+ return SPIReturn::Success;
}
SPIDEFN FindPattern(void** outOffsetPtr, char* combinedPattern)
diff --git a/src/spi/interface.h b/src/spi/interface.h
index 86df86e..5cc1183 100644
--- a/src/spi/interface.h
+++ b/src/spi/interface.h
@@ -78,7 +78,7 @@ enum class SPIReturn : short
/// Get a string describing an SPIReturn code.
///
/// A const wide string
-const wchar_t* SPIReturnToString(SPIReturn code)
+inline const wchar_t* SPIReturnToString(SPIReturn code)
{
switch (code)
{
diff --git a/src/ue_types.h b/src/ue_types.h
index e4b2258..89cf528 100644
--- a/src/ue_types.h
+++ b/src/ue_types.h
@@ -31,12 +31,12 @@ namespace UE
// A prototype of a function which LE1 uses to decode
// a char name from a FNameEntry structure.
typedef void* (__stdcall* tGetName)(void* name, wchar_t* outBuffer);
- tGetName GetName = nullptr;
+ inline tGetName GetName = nullptr;
// A prototype of a function which LE2/3 use to decode
// a char name from a FNameEntry structure.
typedef void* (__stdcall* tNewGetName)(void* name, wchar_t* outBuffer);
- tNewGetName NewGetName = nullptr;
+ inline tNewGetName NewGetName = nullptr;
// Note:
// I've got a pretty solid way of getting names without hooking ToString (^),
@@ -47,8 +47,8 @@ namespace UE
// A prototype of UFunction::Bind method used to
// bind UScript functions to native implementations.
typedef void(__thiscall* tUFunctionBind)(void* pFunction);
- tUFunctionBind UFunctionBind = nullptr;
- tUFunctionBind UFunctionBind_orig = nullptr;
+ inline tUFunctionBind UFunctionBind = nullptr;
+ inline tUFunctionBind UFunctionBind_orig = nullptr;
// A partial representation of a UObject class.
@@ -131,7 +131,7 @@ namespace UE
// A GNative function which takes no arguments and returns TRUE.
- void AlwaysPositiveNative(UObjectPartial* pObject, void* pFrame, void* pResult)
+ inline void AlwaysPositiveNative(UObjectPartial* pObject, void* pFrame, void* pResult)
{
//GLogger.writeln(L"UE::AlwaysPositiveNative: called for %s.", pObject->GetName());
@@ -156,7 +156,7 @@ namespace UE
}
// A GNative function which takes no arguments and returns FALSE.
- void AlwaysNegativeNative(UObjectPartial* pObject, void* pFrame, void* pResult)
+ inline void AlwaysNegativeNative(UObjectPartial* pObject, void* pFrame, void* pResult)
{
//GLogger.writeln(L"UE::AlwaysNegativeNative: called for %s.", pObject->GetName());
@@ -183,7 +183,7 @@ namespace UE
// A hooked wrapper around UFunction::Bind which calls the original and then
// binds IsShippingPCBuild and IsFinalReleaseDebugConsoleBuild to AlwaysPositiveNative.
- void HookedUFunctionBind(UObjectPartial* pFunction)
+ inline void HookedUFunctionBind(UObjectPartial* pFunction)
{
UFunctionBind_orig(pFunction);
diff --git a/src/utils/hook.h b/src/utils/hook.h
index ee63e6e..b7962cc 100644
--- a/src/utils/hook.h
+++ b/src/utils/hook.h
@@ -58,4 +58,4 @@ namespace Utils
// Global instance.
-Utils::HookManager GHookManager;
+inline Utils::HookManager GHookManager;
diff --git a/src/utils/io.h b/src/utils/io.h
index 69762a8..6033f06 100644
--- a/src/utils/io.h
+++ b/src/utils/io.h
@@ -11,20 +11,18 @@
#include
-#ifndef ASI_LOG_FNAME
-#error Must set ASI log filename!
-#endif
+#define ASI_LOG_FNAME "bink2w64_proxy.log"
#define ASI_IO_LOCK(MUTEX) const std::lock_guard lock(MUTEX);
namespace Utils
{
- FILE* FGLog = nullptr;
- std::mutex GOpenConsoleMtx;
- std::mutex GCloseConsoleMtx;
- std::mutex GWriteLineMtx;
+ inline FILE* FGLog = nullptr;
+ inline std::mutex GOpenConsoleMtx;
+ inline std::mutex GCloseConsoleMtx;
+ inline std::mutex GWriteLineMtx;
- void OpenConsole(FILE* out, FILE* err)
+ inline void OpenConsole(FILE* out, FILE* err)
{
ASI_IO_LOCK(GOpenConsoleMtx);
@@ -38,7 +36,7 @@ namespace Utils
GetConsoleScreenBufferInfo(console, &lpConsoleScreenBufferInfo);
SetConsoleScreenBufferSize(console, { lpConsoleScreenBufferInfo.dwSize.X, 30000 });
}
- void CloseConsole()
+ inline void CloseConsole()
{
ASI_IO_LOCK(GCloseConsoleMtx);
FreeConsole();
@@ -46,7 +44,7 @@ namespace Utils
// Open a console window and redirect output to it.
// In release mode, redirect all output to a file.
- void SetupOutput()
+ inline void SetupOutput()
{
#ifdef ASI_DEBUG
#define ASIOUT stdout
@@ -64,7 +62,7 @@ namespace Utils
#endif
}
// Close the console window.
- void TeardownOutput()
+ inline void TeardownOutput()
{
#ifdef ASI_DEBUG
CloseConsole();
@@ -125,4 +123,4 @@ namespace Utils
}
// Global instance.
-static Utils::RuntimeLogger GLogger;
+inline Utils::RuntimeLogger GLogger;
diff --git a/src/utils/memory.h b/src/utils/memory.h
index f5326ed..4fdbd20 100644
--- a/src/utils/memory.h
+++ b/src/utils/memory.h
@@ -9,14 +9,14 @@
namespace Utils
{
- bool IsExecutableAddress(LPVOID pAddress)
+ inline bool IsExecutableAddress(LPVOID pAddress)
{
MEMORY_BASIC_INFORMATION mi;
VirtualQuery(pAddress, &mi, sizeof(mi));
return (mi.State == MEM_COMMIT && (mi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)));
}
- HMODULE GetGameModuleRange(BYTE** pStart, BYTE** pEnd)
+ inline HMODULE GetGameModuleRange(BYTE** pStart, BYTE** pEnd)
{
HMODULE exeModule = GetModuleHandle(nullptr);
MODULEINFO exeModuleInfo;
@@ -34,7 +34,7 @@ namespace Utils
/// Scan the game module for a sequence of bytes defined by a pattern and a mask.
/// TODO: refactor it to use PEiD patterns.
///
- BYTE* ScanProcess(BYTE* pattern, BYTE* mask)
+ inline BYTE* ScanProcess(BYTE* pattern, BYTE* mask)
{
size_t patternLength = strlen((char*)mask);