diff --git a/Reshade Plugins Core.sln b/Reshade Plugins Core.sln
index 738e920..70a1e18 100644
--- a/Reshade Plugins Core.sln
+++ b/Reshade Plugins Core.sln
@@ -39,6 +39,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TLOU", "TLOU\TLOU.vcxproj",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Starfield", "Starfield\Starfield.vcxproj", "{A41D75D0-D4F9-4688-93EE-C33CBC266F52}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Wuchang FF", "WuchangFF\WuchangFF.vcxproj", "{C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -183,6 +185,14 @@ Global
{A41D75D0-D4F9-4688-93EE-C33CBC266F52}.Release|x64.Build.0 = Release|x64
{A41D75D0-D4F9-4688-93EE-C33CBC266F52}.Release|x86.ActiveCfg = Release|Win32
{A41D75D0-D4F9-4688-93EE-C33CBC266F52}.Release|x86.Build.0 = Release|Win32
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Debug|x64.ActiveCfg = Debug|x64
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Debug|x64.Build.0 = Debug|x64
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Debug|x86.ActiveCfg = Debug|Win32
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Debug|x86.Build.0 = Debug|Win32
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Release|x64.ActiveCfg = Release|x64
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Release|x64.Build.0 = Release|x64
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Release|x86.ActiveCfg = Release|Win32
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/WuchangFF/WuchangFF.vcxproj b/WuchangFF/WuchangFF.vcxproj
new file mode 100644
index 0000000..47dcc12
--- /dev/null
+++ b/WuchangFF/WuchangFF.vcxproj
@@ -0,0 +1,231 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {C3F58709-7AD9-4361-9C06-44A3BFFB9CE3}
+ Wuchang FF
+ 10.0
+ Wuchang FF
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ WuchangFFCore
+ .dll
+
+
+ WuchangFFCore
+ .dll
+
+
+ WuchangFFCore
+ .dll
+
+
+ WuchangFFCore
+ .dll
+
+
+
+ Level3
+ true
+
+
+ true
+ NotUsing
+
+
+
+ $(SolutionDir)Memory;$(SolutionDir)Maths;$(SolutionDir)Obfuscate;$(SolutionDir)external;$(SolutionDir)external\safetyhook\include;$(SolutionDir)external\zydis\dependencies\zycore\include;$(SolutionDir)external\zydis\include;$(SolutionDir)zydis\src;$(SolutionDir)external\MinHook\include;%(AdditionalIncludeDirectories)
+ /utf-8 %(AdditionalOptions)
+ MultiThreadedDLL
+ stdcpp23
+ true
+
+
+ Windows
+ true
+ false
+ $(SolutionDir)external\zydis\Libs;$(SolutionDir)external\Maths;%(AdditionalLibraryDirectories)
+ Zydis.lib;Maths.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ true
+ true
+
+
+ true
+ NotUsing
+
+
+
+ $(SolutionDir)Memory;$(SolutionDir)Maths;$(SolutionDir)Obfuscate;$(SolutionDir)external;$(SolutionDir)external\safetyhook\include;$(SolutionDir)external\zydis\dependencies\zycore\include;$(SolutionDir)external\zydis\include;$(SolutionDir)zydis\src;$(SolutionDir)external\MinHook\include;%(AdditionalIncludeDirectories)
+ /utf-8 %(AdditionalOptions)
+ MultiThreadedDLL
+ stdcpp23
+ true
+
+
+ Windows
+ true
+ false
+ $(SolutionDir)external\zydis\Libs;$(SolutionDir)external\Maths;%(AdditionalLibraryDirectories)
+ Zydis.lib;Maths.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+
+
+ true
+ NotUsing
+
+
+
+ $(SolutionDir)Memory;$(SolutionDir)Maths;$(SolutionDir)Obfuscate;$(SolutionDir)external;$(SolutionDir)external\safetyhook\include;$(SolutionDir)external\zydis\dependencies\zycore\include;$(SolutionDir)external\zydis\include;$(SolutionDir)zydis\src;$(SolutionDir)external\MinHook\include;%(AdditionalIncludeDirectories)
+ /utf-8 %(AdditionalOptions)
+ MultiThreadedDLL
+ stdcpp23
+ true
+
+
+ Windows
+ true
+ false
+ $(SolutionDir)external\zydis\Libs;$(SolutionDir)external\Maths;%(AdditionalLibraryDirectories)
+ Zydis.lib;Maths.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ true
+ true
+
+
+ true
+ NotUsing
+
+
+
+ $(SolutionDir)Memory;$(SolutionDir)Maths;$(SolutionDir)Obfuscate;$(SolutionDir)external;$(SolutionDir)external\safetyhook\include;$(SolutionDir)external\zydis\dependencies\zycore\include;$(SolutionDir)external\zydis\include;$(SolutionDir)zydis\src;$(SolutionDir)external\MinHook\include;%(AdditionalIncludeDirectories)
+ /utf-8 %(AdditionalOptions)
+ MultiThreadedDLL
+ stdcpp23
+ true
+
+
+ Windows
+ true
+ false
+ $(SolutionDir)external\zydis\Libs;$(SolutionDir)external\Maths;%(AdditionalLibraryDirectories)
+ Zydis.lib;Maths.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {f9b5bbc6-67d4-4290-986f-08c6bac41ba3}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WuchangFF/dllmain.cpp b/WuchangFF/dllmain.cpp
new file mode 100644
index 0000000..ea55898
--- /dev/null
+++ b/WuchangFF/dllmain.cpp
@@ -0,0 +1,410 @@
+// At this point code injection into the game results in crash.
+// Do not use this plugin dll injection.
+
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+//#include
+#include
+#include
+#include
+#include
+#include
+
+
+// Constants
+const std::string PLUGIN_NAME = "WuchangFF";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "Project_Plague-Win64-Shipping.exe";
+const float baseAspect = 1.777777791;
+
+// Logger
+std::shared_ptr logger;
+
+// Screen informations
+static int screenWidth = GetSystemMetrics(SM_CXSCREEN);
+static int screenHeight = GetSystemMetrics(SM_CYSCREEN);
+static float aspectRatio = (float)screenWidth / screenHeight;
+
+// Plugin states
+static bool AOBScanDone = false;
+static bool g_fix_enabled = false;
+static bool g_fov_fix_enabled = false;
+static bool g_aspect_ratio_fix_enabled = false;
+static bool g_DOF_fix_enabled = false;
+static int g_AdditionalValue = 0;
+
+// Shared values
+static float g_FOV_In = 0;
+static float g_Compensated_FOV = 0;
+static float g_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVaddress = nullptr;
+static uint8_t* Aspectaddress = nullptr;
+static uint8_t* DOFaddress = nullptr;
+
+// Hooking
+//static SafetyHookMid FOVHook{};
+//static SafetyHookMid AspectRatioHook{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+static void AspectRatioFixEnabled(bool fix_enabled);
+static void DOFFixEnabled(bool fix_enabled);
+
+bool IsReadableExecutable(void* addr)
+{
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery(addr, &mbi, sizeof(mbi)))
+ {
+ DWORD protect = mbi.Protect;
+ return (protect & PAGE_EXECUTE_READ) || (protect & PAGE_EXECUTE_READWRITE) || (protect & PAGE_EXECUTE_WRITECOPY);
+ }
+ return false;
+}
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scan started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("EB ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? 0F ?? ?? 8B 83");
+ FOVaddress = Memory::aob_scan(gameExecutable, FOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!FOVaddress)
+ logger->warn("FOV signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("FOV signature found at address: 0x{:X}.", reinterpret_cast(FOVaddress));
+ FOVaddress += 0xa; // Offset for the target opcode
+ }
+ }
+ if (Aspectaddress == nullptr) {
+ if (FOVaddress) {
+ Aspectaddress = FOVaddress + 0x0b;
+ logger->info("Aspect ratio signature found at address: 0x{:X}.", reinterpret_cast(Aspectaddress));
+ }
+ }
+ if (DOFaddress == nullptr) {
+ constexpr auto DOFStringObfuscated = make_obfuscated<0x4A>("8B ?? ?? 48 ?? ?? E8 ?? ?? ?? ?? 0F ?? ?? 48 6B ?? ?? 48 8D");
+ DOFaddress = Memory::aob_scan(gameExecutable, DOFStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!DOFaddress)
+ logger->warn("DOF signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("DOF ratio signature found at address: 0x{:X}.", reinterpret_cast(DOFaddress));
+ }
+ if (FOVaddress != nullptr && Aspectaddress != nullptr && DOFaddress != nullptr)
+ logger->info("All AOB signatures found. Ready to patch...");
+ if (FOVaddress && Aspectaddress && DOFaddress) AOBScanDone = true;
+ logger->info("--------------- AOB scan finished ---------------");
+ }
+ }
+ if (g_fix_enabled) {
+ if (FOVaddress) FOVFixEnabled(g_fov_fix_enabled || g_aspect_ratio_fix_enabled);
+ if (Aspectaddress) AspectRatioFixEnabled(g_aspect_ratio_fix_enabled);
+ if (DOFaddress) DOFFixEnabled(g_DOF_fix_enabled);
+ }
+ else {
+ if (FOVaddress) FOVFixEnabled(false);
+ if (Aspectaddress) AspectRatioFixEnabled(false);
+ if (DOFaddress) DOFFixEnabled(false);
+ logger->info("All fixes disabled.");
+ }
+}
+
+// Setters for Reshade addon call
+extern "C" __declspec(dllexport) void SetFOVFixEnabled(bool enabled, bool init)
+{
+ g_fov_fix_enabled = enabled;
+ if (!init) FOVFixEnabled(g_fov_fix_enabled || g_aspect_ratio_fix_enabled); // FOV fix must be enabled when aspect ratio is too to compensate FOV
+}
+
+extern "C" __declspec(dllexport) void SetAspectRatioFixEnabled(bool enabled, bool init)
+{
+ g_aspect_ratio_fix_enabled = enabled;
+ if (!init) AspectRatioFixEnabled(g_aspect_ratio_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetDOFFixEnabled(bool enabled, bool init)
+{
+ g_DOF_fix_enabled = enabled;
+ if (!init) DOFFixEnabled(g_DOF_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetFOV(int fov)
+{
+ g_AdditionalValue = fov;
+}
+
+// Getters for Reshade addon call
+extern "C" __declspec(dllexport) float GetFOVIn() {
+ return g_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetCompensatedFOV() {
+ return g_Compensated_FOV;
+}
+
+extern "C" __declspec(dllexport) float GetFOVOut() {
+ return g_FOV_Out;
+}
+
+DWORD GetCurrentThreadIdSafe() {
+ return GetCurrentThreadId();
+}
+DWORD GetMainThreadId(DWORD processId) {
+ DWORD mainThreadId = 0;
+ FILETIME earliestCreateTime = { MAXDWORD, MAXDWORD };
+
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) return 0;
+
+ THREADENTRY32 te;
+ te.dwSize = sizeof(te);
+
+ if (Thread32First(snapshot, &te)) {
+ do {
+ if (te.th32OwnerProcessID == processId) {
+ HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te.th32ThreadID);
+ if (hThread) {
+ FILETIME createTime, exitTime, kernelTime, userTime;
+ if (GetThreadTimes(hThread, &createTime, &exitTime, &kernelTime, &userTime)) {
+ if (CompareFileTime(&createTime, &earliestCreateTime) < 0) {
+ earliestCreateTime = createTime;
+ mainThreadId = te.th32ThreadID;
+ }
+ }
+ CloseHandle(hThread);
+ }
+ }
+ } while (Thread32Next(snapshot, &te));
+ }
+
+ CloseHandle(snapshot);
+ return mainThreadId;
+}
+
+void FreezeOtherThreads() {
+ DWORD currentThreadId = GetCurrentThreadId();
+ DWORD currentProcessId = GetCurrentProcessId();
+ DWORD mainThreadId = GetMainThreadId(currentProcessId);
+
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) return;
+
+ THREADENTRY32 te;
+ te.dwSize = sizeof(te);
+
+ if (Thread32First(snapshot, &te)) {
+ do {
+ if (te.th32OwnerProcessID == currentProcessId &&
+ te.th32ThreadID != currentThreadId &&
+ te.th32ThreadID != mainThreadId) {
+ HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
+ if (hThread) {
+ SuspendThread(hThread);
+ CloseHandle(hThread);
+ }
+ }
+ } while (Thread32Next(snapshot, &te));
+ }
+
+ CloseHandle(snapshot);
+}
+
+
+void ResumeThreads() {
+ DWORD currentThreadId = GetCurrentThreadId();
+ DWORD currentProcessId = GetCurrentProcessId();
+
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) return;
+
+ THREADENTRY32 te;
+ te.dwSize = sizeof(te);
+
+ if (Thread32First(snapshot, &te)) {
+ do {
+ if (te.th32OwnerProcessID == currentProcessId && te.th32ThreadID != currentThreadId) {
+ HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
+ if (hThread) {
+ ResumeThread(hThread);
+ CloseHandle(hThread);
+ }
+ }
+ } while (Thread32Next(snapshot, &te));
+ }
+
+ CloseHandle(snapshot);
+}
+
+void* g_Trampoline = nullptr;
+
+void* AllocateNear(void* nearAddr, size_t size) {
+ SYSTEM_INFO sysInfo;
+ GetSystemInfo(&sysInfo);
+
+ uintptr_t startAddr = reinterpret_cast(nearAddr);
+ uintptr_t minAddr = (startAddr > 0x7FFFFFFF) ? startAddr - 0x7FFFFFFF : 0; // -2Go
+ uintptr_t maxAddr = startAddr + 0x7FFFFFFF; // +2Go
+
+ uintptr_t addr = minAddr;
+ while (addr < maxAddr) {
+ void* alloc = VirtualAlloc(reinterpret_cast(addr), size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (alloc != nullptr) {
+ logger->info("Allocated memory at 0x{:X} near 0x{:X}", reinterpret_cast(alloc), reinterpret_cast(nearAddr));
+ return alloc;
+ }
+ addr += sysInfo.dwPageSize;
+ }
+
+ logger->info("Failed to allocate memory near 0x{:X}", reinterpret_cast(nearAddr));
+ return nullptr;
+}
+
+void* CreateTrampoline(void* target_addr, size_t length = 5) {
+ if (length < 5) length = 5; // minimum 5 octets pour jump
+
+ // Allouer mémoire proche
+ //void* trampoline = AllocateNear(target_addr, length + 5); // +5 octets pour jump retour
+ void* trampoline = AllocateNear(target_addr, length + 5); // +5 octets pour jump retour
+ if (!trampoline) {
+ logger->info("Failed to allocate trampoline memory");
+ return nullptr;
+ }
+
+ // Copier les octets originaux
+ memcpy(trampoline, target_addr, length);
+
+ // Calculer l'adresse de retour (target_addr + length)
+ uintptr_t retAddr = reinterpret_cast(target_addr) + length;
+ uintptr_t jmpFrom = reinterpret_cast(trampoline) + length;
+
+ // Ecrire un jump relatif 5 octets à la fin du trampoline pour revenir au code original
+ int32_t relAddr = static_cast(retAddr - (jmpFrom + 5)); // rel = dest - (src+5)
+ uint8_t* p = reinterpret_cast(jmpFrom);
+ p[0] = 0xE9; // opcode JMP rel32
+ memcpy(p + 1, &relAddr, sizeof(relAddr));
+
+ logger->info("Trampoline created at 0x{:X}, jump back to 0x{:X}", reinterpret_cast(trampoline), retAddr);
+
+ return trampoline;
+}
+
+//void HookFunction(void* target, void* destination, size_t length = 5) {
+// DWORD oldProtect;
+// VirtualProtect(target, length, PAGE_EXECUTE_READWRITE, &oldProtect);
+//
+// uintptr_t rel_addr = (uintptr_t)destination - (uintptr_t)target - 5;
+// uint8_t patch[5] = { 0xE9 }; // jmp rel32
+// *reinterpret_cast(patch + 1) = static_cast(rel_addr);
+//
+// logger->info("Patching FOVaddress (0x{:X}) to jump to trampoline (0x{:X})", reinterpret_cast(target), reinterpret_cast(destination));
+// memcpy(target, patch, 5);
+// VirtualProtect(target, length, oldProtect, &oldProtect);
+//}
+using FOVFuncType = void(__fastcall*)();
+FOVFuncType originalFOV = nullptr;
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+
+ //if (g_Trampoline)
+ // HookFunction(FOVaddress, g_Trampoline, 5);
+
+ //if (!FOVHook) { // Hook only once
+ //if (IsReadableExecutable(FOVaddress)) {
+ //FOVHook = safetyhook::create_mid(FOVaddress,
+ // [](SafetyHookContext& ctx) {
+ // //g_FOV_In = ctx.xmm0.f32[0];
+ // //if (g_aspect_ratio_fix_enabled)
+ // // g_Compensated_FOV = ctx.xmm0.f32[0] = Maths::CompensateHorizontalFOV(g_FOV_In, baseAspect, aspectRatio);
+ // //else
+ // // g_Compensated_FOV = ctx.xmm0.f32[0];
+ // //g_FOV_Out = ctx.xmm0.f32[0] += (g_fov_fix_enabled ? g_AdditionalValue : 0);
+ // });
+ //}
+ //}
+ //else FOVHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ //if (!fix_enabled /* && FOVHook * / ) {
+ if (!fix_enabled) {
+ //FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+static void AspectRatioFixEnabled(bool fix_enabled) {
+ //if (g_fix_enabled && fix_enabled && Aspectaddress != nullptr) {
+ // if (!AspectRatioHook) {
+ // AspectRatioHook = safetyhook::create_mid(Aspectaddress,
+ // [](SafetyHookContext& ctx) {
+ // ctx.rax = *reinterpret_cast(&aspectRatio);
+ // });
+ // }
+ // else {
+ // AspectRatioHook.enable();
+ // FOVFixEnabled(fix_enabled); // Usefull to compensate
+ // }
+ // logger->info("Aspect ratio fix enabled");
+ //}
+ //if (!fix_enabled && AspectRatioHook && Aspectaddress) {
+ // AspectRatioHook.disable();
+ // logger->info("Aspect ratio fix disabled");
+ //}
+}
+
+static void DOFFixEnabled(bool fix_enabled) {
+ //if (g_fix_enabled && fix_enabled && DOFaddress != nullptr) {
+ // Memory::PatchBytes(DOFaddress, "\x31\xFF\x90", 3); // xor edi,edi r.DepthOfFieldQuality = 0
+ // logger->info("Depth of field fix enabled");
+ //}
+ //if (!fix_enabled && DOFaddress) {
+ // Memory::RestoreBytes(DOFaddress);
+ // logger->info("Depth of field fix disabled");
+ //}
+}
+
+
+static void InitializeLogger()
+{
+ try
+ {
+ std::filesystem::path log_path = std::filesystem::absolute(PLUGIN_LOG);
+ if (std::filesystem::exists(log_path))
+ std::filesystem::remove(log_path);
+ logger = std::make_shared("Wuchang Fallen Feathers", std::make_shared(PLUGIN_LOG, 10 * 1024 * 1024, 1));
+ logger->flush_on(spdlog::level::debug); // Flush automatically
+ }
+ catch (const spdlog::spdlog_ex& ex)
+ {
+ std::string plugin_error_message = "Could not open " + PLUGIN_LOG;
+ MessageBoxA(nullptr, plugin_error_message.c_str(), "Logger Error", MB_ICONERROR | MB_OK);
+ }
+}
+
+// Standard dll entry
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ if (MH_Initialize() != MH_OK) {
+ logger->info("MinHook initialization failed!");
+ return FALSE;
+ }
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file