diff --git a/ASILoader/ASILoader.vcxproj b/ASILoader/ASILoader.vcxproj
new file mode 100644
index 0000000..b1af04f
--- /dev/null
+++ b/ASILoader/ASILoader.vcxproj
@@ -0,0 +1,186 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {0db8f2e7-eec8-4c7a-a1bf-c7d51af69d28}
+ ASILoader
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;ASILOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ MultiThreaded
+
+ stdcpp17
+
+
+ Windows
+ true
+ false
+
+ version.def
+ version.lib
+ false
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;ASILOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ MultiThreaded
+
+ stdcpp17
+
+
+ Windows
+ true
+ false
+
+ version.def
+ version.lib
+ false
+ false
+
+
+
+
+ Level3
+ true
+ _DEBUG;ASILOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ MultiThreaded
+
+ stdcpp17
+
+
+ Windows
+ true
+ false
+
+ version.def
+ version.lib
+ false
+ false
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;ASILOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ MultiThreaded
+
+ stdcpp17
+
+
+ Windows
+ true
+ false
+
+ version.def
+ version.lib
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ASILoader/framework.h b/ASILoader/framework.h
new file mode 100644
index 0000000..9684e58
--- /dev/null
+++ b/ASILoader/framework.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclure les en-têtes Windows rarement utilisés
+// Fichiers d'en-tête Windows
+#include
diff --git a/ASILoader/pch.h b/ASILoader/pch.h
new file mode 100644
index 0000000..32ba33d
--- /dev/null
+++ b/ASILoader/pch.h
@@ -0,0 +1,13 @@
+// pch.h : Il s'agit d'un fichier d'en-tête précompilé.
+// Les fichiers listés ci-dessous sont compilés une seule fois, ce qui améliore les performances de génération des futures builds.
+// Cela affecte également les performances d'IntelliSense, notamment la complétion du code et de nombreuses fonctionnalités de navigation du code.
+// Toutefois, les fichiers listés ici sont TOUS recompilés si l'un d'entre eux est mis à jour entre les builds.
+// N'ajoutez pas de fichiers fréquemment mis à jour ici, car cela annule les gains de performance.
+
+#ifndef PCH_H
+#define PCH_H
+
+// ajouter les en-têtes à précompiler ici
+#include "framework.h"
+
+#endif //PCH_H
diff --git a/ASILoader/version.cpp b/ASILoader/version.cpp
new file mode 100644
index 0000000..21d1dc1
--- /dev/null
+++ b/ASILoader/version.cpp
@@ -0,0 +1,252 @@
+#include
+#include
+namespace fs = std::filesystem;
+
+static HMODULE real_version = nullptr;
+
+void LoadRealVersionDLL()
+{
+ if (!real_version)
+ {
+ real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+ }
+}
+
+extern "C" BOOL WINAPI GetFileVersionInfoA(LPCSTR file, DWORD handle, DWORD len, LPVOID data)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(LPCSTR, DWORD, DWORD, LPVOID);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoA");
+ return real(file, handle, len, data);
+}
+
+extern "C" BOOL WINAPI GetFileVersionInfoW(LPCWSTR file, DWORD handle, DWORD len, LPVOID data)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(LPCWSTR, DWORD, DWORD, LPVOID);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoW");
+ return real(file, handle, len, data);
+}
+
+extern "C" BOOL WINAPI GetFileVersionInfoExA(DWORD dwFlags, LPCSTR lpstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(DWORD, LPCSTR, DWORD, DWORD, LPVOID);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoExA");
+ return real(dwFlags, lpstrFilename, dwHandle, dwLen, lpData);
+}
+
+extern "C" BOOL WINAPI GetFileVersionInfoExW(DWORD dwFlags, LPCWSTR lpwstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(DWORD, LPCWSTR, DWORD, DWORD, LPVOID);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoExW");
+ return real(dwFlags, lpwstrFilename, dwHandle, dwLen, lpData);
+}
+
+extern "C" DWORD WINAPI GetFileVersionInfoSizeA(LPCSTR lptstrFilename, LPDWORD lpdwHandle)
+{
+ LoadRealVersionDLL();
+ using Fn = DWORD(WINAPI*)(LPCSTR, LPDWORD);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeA");
+ return real(lptstrFilename, lpdwHandle);
+}
+
+extern "C" DWORD WINAPI GetFileVersionInfoSizeW(LPCWSTR lptstrFilename, LPDWORD lpdwHandle)
+{
+ LoadRealVersionDLL();
+ using Fn = DWORD(WINAPI*)(LPCWSTR, LPDWORD);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeW");
+ return real(lptstrFilename, lpdwHandle);
+}
+
+extern "C" DWORD WINAPI GetFileVersionInfoSizeExA(DWORD dwFlags, LPCSTR lpwstrFilename, LPDWORD lpdwHandle)
+{
+ LoadRealVersionDLL();
+ using Fn = DWORD(WINAPI*)(DWORD, LPCSTR, LPDWORD);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeExA");
+ return real(dwFlags, lpwstrFilename, lpdwHandle);
+}
+
+extern "C" DWORD WINAPI GetFileVersionInfoSizeExW(DWORD dwFlags, LPCWSTR lpwstrFilename, LPDWORD lpdwHandle)
+{
+ LoadRealVersionDLL();
+ using Fn = DWORD(WINAPI*)(DWORD, LPCWSTR, LPDWORD);
+ static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeExW");
+ return real(dwFlags, lpwstrFilename, lpdwHandle);
+}
+
+extern "C" DWORD WINAPI VerFindFileA(DWORD uFlags, LPCSTR szFileName, LPCSTR szWinDir, LPCSTR szAppDir, LPSTR szCurDir, UINT* pcchCurDir, LPSTR szDestDir, UINT* pcchDestDir)
+{
+ LoadRealVersionDLL();
+ using Fn = DWORD(WINAPI*)(DWORD, LPCSTR, LPCSTR, LPCSTR, LPSTR, UINT*, LPSTR, UINT*);
+ static Fn real = (Fn)GetProcAddress(real_version, "VerFindFileA");
+ return real(uFlags, szFileName, szWinDir, szAppDir, szCurDir, pcchCurDir, szDestDir, pcchDestDir);
+}
+
+extern "C" DWORD WINAPI VerFindFileW(DWORD uFlags, LPCWSTR szFileName, LPCWSTR szWinDir, LPCWSTR szAppDir, LPWSTR szCurDir, UINT* pcchCurDir, LPWSTR szDestDir, UINT* pcchDestDir)
+{
+ LoadRealVersionDLL();
+ using Fn = DWORD(WINAPI*)(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, UINT*, LPWSTR, UINT*);
+ static Fn real = (Fn)GetProcAddress(real_version, "VerFindFileW");
+ return real(uFlags, szFileName, szWinDir, szAppDir, szCurDir, pcchCurDir, szDestDir, pcchDestDir);
+}
+
+//extern "C" DWORD WINAPI VerInstallFileA(DWORD uFlags, LPCSTR szSrcFileName, LPCSTR szDestFileName, LPCSTR szSrcDir, LPCSTR szDestDir, LPCSTR szCurDir, LPSTR szTmpFile, UINT cchTmpFile, UINT* puVersion)
+//{
+// LoadRealVersionDLL();
+// using Fn = DWORD(WINAPI*)(DWORD, LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPSTR, UINT, UINT*);
+// static Fn real = (Fn)GetProcAddress(real_version, "VerInstallFileA");
+// return real(uFlags, szSrcFileName, szDestFileName, szSrcDir, szDestDir, szCurDir, szTmpFile, cchTmpFile, puVersion);
+//}
+//
+//extern "C" DWORD WINAPI VerInstallFileW(DWORD uFlags, LPCWSTR szSrcFileName, LPCWSTR szDestFileName, LPCWSTR szSrcDir, LPCWSTR szDestDir, LPCWSTR szCurDir, LPWSTR szTmpFile, UINT cchTmpFile, UINT* puVersion)
+//{
+// LoadRealVersionDLL();
+// using Fn = DWORD(WINAPI*)(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, UINT, UINT*);
+// static Fn real = (Fn)GetProcAddress(real_version, "VerInstallFileW");
+// return real(uFlags, szSrcFileName, szDestFileName, szSrcDir, szDestDir, szCurDir, szTmpFile, cchTmpFile, puVersion);
+//}
+
+extern "C" DWORD WINAPI VerLanguageNameA(DWORD wLang, LPSTR szLang, DWORD cchLang)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(DWORD, LPSTR, DWORD);
+ static Fn real = (Fn)GetProcAddress(real_version, "VerLanguageNameA");
+ return real(wLang, szLang, cchLang);
+}
+
+extern "C" DWORD WINAPI VerLanguageNameW(DWORD wLang, LPWSTR szLang, DWORD cchLang)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(DWORD, LPWSTR, DWORD);
+ static Fn real = (Fn)GetProcAddress(real_version, "VerLanguageNameW");
+ return real(wLang, szLang, cchLang);
+}
+
+extern "C" BOOL WINAPI VerQueryValueA(LPCVOID pBlock, LPCSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(LPCVOID, LPCSTR, LPVOID*, PUINT);
+ static Fn real = (Fn)GetProcAddress(real_version, "VerQueryValueA");
+ return real(pBlock, lpSubBlock, lplpBuffer, puLen);
+}
+
+extern "C" BOOL WINAPI VerQueryValueW(LPCVOID pBlock, LPCWSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen)
+{
+ LoadRealVersionDLL();
+ using Fn = BOOL(WINAPI*)(LPCVOID, LPCWSTR, LPVOID*, PUINT);
+ static Fn real = (Fn)GetProcAddress(real_version, "VerQueryValueW");
+ return real(pBlock, lpSubBlock, lplpBuffer, puLen);
+}
+
+// Charge les ASI dans le répertoire courant
+void LoadAllASI()
+{
+ auto path = fs::current_path();
+ for (const auto& entry : fs::directory_iterator(path))
+ {
+ if (entry.path().extension() == ".asi")
+ {
+ LoadLibraryW(entry.path().c_str());
+ }
+ }
+}
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ LoadAllASI();
+ }
+ return TRUE;
+}
+
+
+//// dllmain.cpp : Définit le point d'entrée de l'application DLL.
+//#include
+//#include
+//
+//HMODULE real_version = nullptr;
+//
+//using namespace std;
+//
+//// Typedefs des fonctions qu'on va forwarder
+//typedef BOOL(WINAPI* GetFileVersionInfoAFn)(LPCSTR, DWORD, DWORD, LPVOID);
+//typedef BOOL(WINAPI* GetFileVersionInfoWFn)(LPCWSTR, DWORD, DWORD, LPVOID);
+//
+//GetFileVersionInfoAFn real_GetFileVersionInfoA = nullptr;
+//GetFileVersionInfoWFn real_GetFileVersionInfoW = nullptr;
+//
+//// Charge tous les plugins .asi du dossier du jeu
+//void LoadAllASI()
+//{
+// auto path = std::filesystem::current_path();
+// for (const auto& entry : std::filesystem::directory_iterator(path))
+// {
+// if (entry.path().extension() == ".asi")
+// {
+// LoadLibraryW(entry.path().c_str());
+// }
+// }
+//}
+//
+//// Fonctions exportées, simplement redirigées vers la vraie version.dll
+////extern "C" __declspec(dllexport) BOOL WINAPI GetFileVersionInfoA(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
+//BOOL WINAPI GetFileVersionInfoA(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
+//{
+// if (!real_version) real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+// if (!real_GetFileVersionInfoA) real_GetFileVersionInfoA = (GetFileVersionInfoAFn)GetProcAddress(real_version, "GetFileVersionInfoA");
+// return real_GetFileVersionInfoA(lptstrFilename, dwHandle, dwLen, lpData);
+//}
+//
+//BOOL WINAPI GetFileVersionInfoW(LPCWSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
+//{
+// if (!real_version) real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+// if (!real_GetFileVersionInfoW) real_GetFileVersionInfoW = (GetFileVersionInfoWFn)GetProcAddress(real_version, "GetFileVersionInfoW");
+// return real_GetFileVersionInfoW(lptstrFilename, dwHandle, dwLen, lpData);
+//}
+//
+//DWORD WINAPI GetFileVersionInfoSizeA(LPCSTR lptstrFilename, LPDWORD lpdwHandle)
+//{
+// if (!real_version) real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+// using Fn = DWORD(WINAPI*)(LPCSTR, LPDWORD);
+// static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeA");
+// return real(lptstrFilename, lpdwHandle);
+//}
+//
+//DWORD WINAPI GetFileVersionInfoSizeW(LPCWSTR lptstrFilename, LPDWORD lpdwHandle)
+//{
+// if (!real_version) real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+// using Fn = DWORD(WINAPI*)(LPCWSTR, LPDWORD);
+// static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeW");
+// return real(lptstrFilename, lpdwHandle);
+//}
+//
+//BOOL WINAPI VerQueryValueA(LPCVOID pBlock, LPCSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen)
+//{
+// if (!real_version) real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+// using Fn = BOOL(WINAPI*)(LPCVOID, LPCSTR, LPVOID*, PUINT);
+// static Fn real = (Fn)GetProcAddress(real_version, "VerQueryValueA");
+// return real(pBlock, lpSubBlock, lplpBuffer, puLen);
+//}
+//
+//BOOL WINAPI VerQueryValueW(LPCVOID pBlock, LPCWSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen)
+//{
+// if (!real_version) real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
+// using Fn = BOOL(WINAPI*)(LPCVOID, LPCWSTR, LPVOID*, PUINT);
+// static Fn real = (Fn)GetProcAddress(real_version, "VerQueryValueW");
+// return real(pBlock, lpSubBlock, lplpBuffer, puLen);
+//}
+//
+//// Point d'entrée principal
+//BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+//{
+// if (ul_reason_for_call == DLL_PROCESS_ATTACH)
+// {
+// LoadAllASI();
+// }
+// return TRUE;
+//}
+//
+//
diff --git a/ASILoader/version.def b/ASILoader/version.def
new file mode 100644
index 0000000..15597f0
--- /dev/null
+++ b/ASILoader/version.def
@@ -0,0 +1,16 @@
+LIBRARY version
+EXPORTS
+ GetFileVersionInfoA
+ GetFileVersionInfoW
+ GetFileVersionInfoExA
+ GetFileVersionInfoExW
+ GetFileVersionInfoSizeA
+ GetFileVersionInfoSizeW
+ GetFileVersionInfoSizeExA
+ GetFileVersionInfoSizeExW
+ VerFindFileA
+ VerFindFileW
+ VerLanguageNameA
+ VerLanguageNameW
+ VerQueryValueA
+ VerQueryValueW
diff --git a/Days Gone/DaysGone.vcxproj b/Days Gone/DaysGone.vcxproj
new file mode 100644
index 0000000..95ecfb9
--- /dev/null
+++ b/Days Gone/DaysGone.vcxproj
@@ -0,0 +1,216 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {15907009-8C6D-4083-BF3F-1D298395C69A}
+ ReshadePluginsCore
+ 10.0
+ Days gone
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DaysGoneCore
+ .asi
+
+
+ DaysGoneCore
+ .asi
+
+
+ DaysGoneCore
+ .asi
+
+
+ DaysGoneCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Days Gone/dllmain.cpp b/Days Gone/dllmain.cpp
new file mode 100644
index 0000000..2fb893d
--- /dev/null
+++ b/Days Gone/dllmain.cpp
@@ -0,0 +1,260 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "Days Gone";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "DaysGone.exe";
+
+// 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_hud_fix_enabled = false;
+static int g_AdditionalFOVValue = 0;
+static int g_AdditionalVehicleFOVValue = 0;
+static int g_AdditionalOtherFOVValue = 0;
+static float g_HUDLeft = 0;
+static float g_HUDRight = 0;
+static float g_FOVfactor = 0;
+
+// Shared values
+static float g_baseFOV = 0;
+static float g_FOV_In = 0;
+static float g_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVaddress = nullptr;
+static uint8_t* FOVfactorAddress = nullptr;
+static uint8_t* HUDaddress = nullptr;
+
+// Hooking
+static SafetyHookMid FOVHook{};
+static SafetyHookMid FOVFactorHook{};
+static SafetyHookMid HUDHook{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+static void HUDFixEnabled(bool fix_enabled);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("F3 0F ?? ?? ?? 48 8D ?? ?? E8 ?? ?? ?? ?? 90 48 ?? ?? 48");
+ FOVaddress = Memory::aob_scan(gameExecutable, FOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ //DaysGone.exe + 46CC96 - 48 8B CB - mov rcx, rbx
+ //DaysGone.exe + 46CC99 - FF 90 80 03 00 00 - call qword ptr[rax + 00000380]
+ //DaysGone.exe + 46CC9F - F3 0F 11 47 28 - movss[rdi + 28], xmm0
+ //DaysGone.exe + 46CCA4 - 48 8D 4D 00 - lea rcx, [rbp + 00]
+ //DaysGone.exe + 46CCA8 - E8 B3 8E FA FF - call DaysGone.exe + 415B60
+ 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));
+ }
+ }
+
+ if (FOVfactorAddress == nullptr) {
+ constexpr auto FOVFactorStringObfuscated = make_obfuscated<0x4A>("CC 48 8B ?? ?? F3 0F 10 80 ?? ?? ?? ?? F3 0F");
+ FOVfactorAddress = Memory::aob_scan(gameExecutable, FOVFactorStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+ //DaysGone.exe + 4554E0 - 48 8B 41 28 - mov rax, [rcx + 28]
+ //DaysGone.exe + 4554E4 - F3 0F 10 80 68 03 00 00 - movss xmm0, [rax + 00000368]
+ //DaysGone.exe + 4554EC - F3 0F 59 41 4C - mulss xmm0, [rcx + 4C]
+ //DaysGone.exe + 4554F1 - C3 - ret
+ //DaysGone.exe + 4554F2 - CC - int 3
+ if (!FOVfactorAddress)
+ logger->warn("FOV factor signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("FOV factor signature found at address: 0x{:X}.", reinterpret_cast(FOVfactorAddress));
+ FOVfactorAddress += 0xd;
+ }
+ }
+
+ if (HUDaddress == nullptr) {
+ constexpr auto AspectStringObfuscated = make_obfuscated<0x4A>("48 ?? ?? 48 ?? ?? 66 48 ?? ?? ?? FF ?? 48 8B ?? ?? ?? 0F ?? ??");
+ HUDaddress = Memory::aob_scan(gameExecutable, AspectStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ //DaysGone.exe + 1FBA9E3 - 0F 10 00 - movups xmm0, [rax]
+ //DaysGone.exe + 1FBA9E6 - 48 8B C7 - mov rax, rdi
+ //DaysGone.exe + 1FBA9E9 - 0F 11 07 - movups[rdi], xmm0
+ //DaysGone.exe + 1FBA9EC - 48 83 C4 30 - add rsp, 30
+ //DaysGone.exe + 1FBA9F0 - 5F - pop rdi
+ if (!HUDaddress)
+ logger->warn("HUD signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("HUD signature found at address: 0x{:X}.", reinterpret_cast(HUDaddress));
+ HUDaddress += 0x1B; // Offset for the target opcode
+ }
+ if (FOVaddress && FOVfactorAddress && HUDaddress) {
+ logger->info("All AOB signatures found. Ready to patch...");
+ logger->info("--------------- AOB scanning finished ---------------");
+ AOBScanDone = true;
+ }
+ }
+ }
+ if (FOVaddress && FOVfactorAddress && HUDaddress) {
+ if (g_fix_enabled) {
+ FOVFixEnabled(g_fov_fix_enabled);
+ HUDFixEnabled(g_hud_fix_enabled);
+ }
+ else {
+ FOVFixEnabled(false);
+ HUDFixEnabled(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);
+}
+
+extern "C" __declspec(dllexport) void SetHUDFixEnabled(bool enabled, bool init)
+{
+ g_hud_fix_enabled = enabled;
+ if (!init) HUDFixEnabled(g_hud_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetFOV(int fov)
+{
+ g_AdditionalFOVValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetVehicleFOV(int fov)
+{
+ g_AdditionalVehicleFOVValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetOtherFOV(int fov)
+{
+ g_AdditionalOtherFOVValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetHUDLeft(int fov)
+{
+ g_HUDLeft = (float)fov;
+}
+
+extern "C" __declspec(dllexport) void SetHUDRight(int fov)
+{
+ g_HUDRight = (float)fov;
+}
+
+// Getters for Reshade addon call
+extern "C" __declspec(dllexport) float GetFOVIn() {
+ return g_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetFOVOut() {
+ return g_FOV_Out;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr && FOVfactorAddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ FOVHook = safetyhook::create_mid(FOVaddress,
+ [](SafetyHookContext& ctx) {
+ g_FOV_In = ctx.xmm0.f32[0];
+ if (g_baseFOV == 70) // World base FOV
+ g_FOV_Out = ctx.xmm0.f32[0] += (g_fov_fix_enabled ? g_AdditionalFOVValue : 0); // World FOV
+ else if (g_baseFOV == 90) // Vehicle base FOV
+ g_FOV_Out = ctx.xmm0.f32[0] += (g_fov_fix_enabled ? g_AdditionalVehicleFOVValue : 0); // Vehicle FOV
+ else // Other FOV
+ g_FOV_Out = ctx.xmm0.f32[0] += (g_fov_fix_enabled ? g_AdditionalOtherFOVValue : 0); // Other FOV
+ });
+ }
+ else FOVHook.enable();
+ if (!FOVFactorHook) {
+ FOVFactorHook = safetyhook::create_mid(FOVfactorAddress,
+ [](SafetyHookContext& ctx) {
+ g_FOVfactor = ctx.xmm0.f32[0];
+ g_baseFOV = g_FOV_In / g_FOVfactor;
+ });
+ }
+ else FOVFactorHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ if (!fix_enabled && FOVHook) {
+ FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+static void HUDFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && HUDaddress != nullptr) {
+ if (!HUDHook) {
+ HUDHook = safetyhook::create_mid(HUDaddress,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm0.f32[0] = g_HUDLeft / 100;
+ ctx.xmm0.f32[1] = 0.0f;
+ ctx.xmm0.f32[2] = 1 - (g_HUDRight / 100);
+ ctx.xmm0.f32[3] = 1.0f;
+ memcpy(reinterpret_cast(ctx.rdi), &ctx.xmm0, sizeof(ctx.xmm0)); // Original code movups [rdi],xmm0
+ });
+ }
+ else HUDHook.enable();
+ logger->info("HUD fix enabled");
+ }
+ if (!fix_enabled && HUDHook) {
+ HUDHook.disable();
+ logger->info("HUD fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Dead Space 2023/Dead Space 2023.vcxproj b/Dead Space 2023/Dead Space 2023.vcxproj
new file mode 100644
index 0000000..e7c53d4
--- /dev/null
+++ b/Dead Space 2023/Dead Space 2023.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {CEAC3F9A-E830-4936-A8BB-07FE50F5B3F3}
+ Dead Space 2023
+ 10.0
+ Dead Space 2023
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DeadSpace2023Core
+ .asi
+
+
+ DeadSpace2023Core
+ .asi
+
+
+ DeadSpace2023Core
+ .asi
+
+
+ DeadSpace2023Core
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Dead Space 2023/dllmain.cpp b/Dead Space 2023/dllmain.cpp
new file mode 100644
index 0000000..300bd67
--- /dev/null
+++ b/Dead Space 2023/dllmain.cpp
@@ -0,0 +1,148 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "Dead Space 2023";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "Dead Space.exe";
+
+// Logger
+std::shared_ptr logger;
+
+// Plugin states
+static bool AOBScanDone = false;
+static bool g_fix_enabled = false;
+static bool g_fov_fix_enabled = false;
+static int g_AdditionalValue = 0;
+
+// Shared values
+static float g_FOV_In = 0;
+static float g_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVaddress = nullptr;
+
+// Hooking
+static SafetyHookMid FOVHook{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("F3 0F ?? ?? ?? ?? ?? ?? 0F 2F ?? ?? ?? ?? ?? 77 ?? 48 8B ?? ?? ?? ?? ?? F3 0F ?? ?? ?? F3 0F ?? ?? ?? ?? ?? ??");
+ FOVaddress = Memory::aob_scan(gameExecutable, FOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ //Dead Space.exe + 1ACB628 - 48 8B 83 10 03 00 00 - mov rax, [rbx + 00000310]
+ //Dead Space.exe + 1ACB62F - F3 0F 10 40 6C - movss xmm0, [rax + 6C]
+ //Dead Space.exe + 1ACB634 - F3 0F 59 83 4C 01 00 00 - mulss xmm0, [rbx + 0000014C]
+ //Dead Space.exe + 1ACB63C - 48 83 C4 20 - add rsp, 20
+ //Dead Space.exe + 1ACB640 - 5B - pop rbx
+ if (!FOVaddress) {
+ logger->warn("FOV signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ logger->info("--------------- AOB scanning finished ---------------");
+ }
+ else {
+ logger->info("FOV signature found at address: 0x{:X}.", reinterpret_cast(FOVaddress));
+ FOVaddress += 0x1d; // Offset for the target opcode
+ AOBScanDone = true;
+ }
+ }
+ if (FOVaddress != nullptr) {
+ if (g_fix_enabled) {
+ FOVFixEnabled(g_fov_fix_enabled);
+ }
+ else {
+ FOVFixEnabled(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);
+}
+
+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 GetFOVOut() {
+ return g_FOV_Out;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ //Memory::PatchBytes(FOVaddress, "\x31\xFF\x90", 3);
+ FOVHook = safetyhook::create_mid(FOVaddress,
+ [](SafetyHookContext& ctx) {
+ if (ctx.xmm0.f32[0] >= 44.9f) {
+ g_FOV_In = ctx.xmm0.f32[0];
+ g_FOV_Out = ctx.xmm0.f32[0] += g_AdditionalValue;
+ }
+ });
+ }
+ else FOVHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ if (!fix_enabled && FOVHook) {
+ FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Empire Of The Ants/Empire Of The Ants.vcxproj b/Empire Of The Ants/Empire Of The Ants.vcxproj
new file mode 100644
index 0000000..84f3e2d
--- /dev/null
+++ b/Empire Of The Ants/Empire Of The Ants.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {A2B2F6CC-8A2B-4A4E-A4E6-A5EFF32BF1C0}
+ Empire Of The Ants
+ 10.0
+ Empire Of The Ants
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EmpireOfTheAntsCore
+ .asi
+
+
+ EmpireOfTheAntsCore
+ .asi
+
+
+ EmpireOfTheAntsCore
+ .asi
+
+
+ EmpireOfTheAntsCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Empire Of The Ants/dllmain.cpp b/Empire Of The Ants/dllmain.cpp
new file mode 100644
index 0000000..c34ca59
--- /dev/null
+++ b/Empire Of The Ants/dllmain.cpp
@@ -0,0 +1,231 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "Empire Of The Ants";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "Empire-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);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("77 ?? 48 ?? ?? FF 90 ?? ?? ?? ?? F3 0F ?? ?? ?? 48 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 += 0x10; // Offset for the target opcode
+ }
+ }
+ if (Aspectaddress == nullptr) {
+ constexpr auto AspectStringObfuscated = make_obfuscated<0x4A>("F3 0F ?? ?? ?? ?? ?? ?? 76 ?? F3 0F ?? ?? ?? ?? ?? ?? 48 83 ?? ?? 5B");
+ Aspectaddress = Memory::aob_scan(gameExecutable, AspectStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!Aspectaddress)
+ logger->warn("Aspect ratio signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ 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 && Aspectaddress && DOFaddress) {
+ logger->info("All AOB signatures found. Ready to patch...");
+ logger->info("--------------- AOB scanning finished ---------------");
+ AOBScanDone = true;
+ }
+ }
+ }
+ 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;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ 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) {
+ 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.xmm0.f32[0] = aspectRatio;
+ });
+ }
+ else AspectRatioHook.enable();
+ logger->info("Aspect ratio fix enabled");
+ FOVFixEnabled(fix_enabled); // Usefull to compensate
+ }
+ if (!fix_enabled && AspectRatioHook) {
+ AspectRatioHook.disable();
+ logger->info("Aspect ratio fix disabled");
+ if (!g_fov_fix_enabled)
+ FOVFixEnabled(fix_enabled); // Deactivate FOV if not ticked
+ }
+}
+
+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("DOF fix enabled");
+ }
+ if (!fix_enabled && DOFaddress) {
+ Memory::RestoreBytes(DOFaddress);
+ logger->info("DOF fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ DOFFixEnabled(false); // Core is loaded 3 times so i need to restore previous patched bytes
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Indiana Jones And The Great Circle/Indiana Jones And The Great Circle.vcxproj b/Indiana Jones And The Great Circle/Indiana Jones And The Great Circle.vcxproj
new file mode 100644
index 0000000..17c454e
--- /dev/null
+++ b/Indiana Jones And The Great Circle/Indiana Jones And The Great Circle.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {E8319170-F415-4341-A80E-ED30B08CB431}
+ Indiana Jones And The Great Circle
+ 10.0
+ Indiana Jones And The Great Circle
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IndianaJonesTGCCore
+ .asi
+
+
+ IndianaJonesTGCCore
+ .asi
+
+
+ IndianaJonesTGCCore
+ .asi
+
+
+ IndianaJonesTGCCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Indiana Jones And The Great Circle/dllmain.cpp b/Indiana Jones And The Great Circle/dllmain.cpp
new file mode 100644
index 0000000..29a2422
--- /dev/null
+++ b/Indiana Jones And The Great Circle/dllmain.cpp
@@ -0,0 +1,245 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "IndianaJonesTGC";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "TheGreatCircle.exe";
+const float baseAspect = 1.77777779;
+
+// 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_cinematics_FOV_fix_enabled = false;
+static bool g_cinematics_FT_fix_enabled = false;
+static int g_AdditionalValue = 0;
+static int g_AdditionalCinematicsValue = 0;
+
+// Shared values
+static float g_FOV_In = 0;
+static float g_FOV_Out = 0;
+static float g_Cinematics_FOV_In = 0;
+static float g_Compensated_Cinematics_FOV = 0;
+static float g_Cinematics_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVaddress = nullptr;
+static uint8_t* CinematicsFOVaddress = nullptr;
+static uint8_t* CinematicsFTaddress = nullptr;
+
+// Hooking
+static SafetyHookMid FOVHook{};
+static SafetyHookMid CinematicsFOVHook{};
+static SafetyHookMid CinematicsFTHook{};
+
+// Prototypes for hooking
+static void FOVFixEnabled(bool fix_enabled);
+static void CinematicsFOVFixEnabled(bool fix_enabled);
+static void CinematicsFTFixEnabled(bool fix_enabled);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("F3 44 ?? ?? ?? ?? 48 8B ?? ?? ?? 4C ?? ?? 49 0F ?? ?? ?? ?? ?? ?? 4C 8D");
+ 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 += 0x6; // Offset for the target opcode
+ }
+ }
+ if (CinematicsFOVaddress == nullptr) {
+ constexpr auto CinematicsFOVStringObfuscated = make_obfuscated<0x4A>("F3 0F ?? ?? F3 0F ?? ?? ?? ?? ?? ?? 0F ?? ?? E8 ?? ?? ?? ?? F3 0F ?? ?? E8");
+ CinematicsFOVaddress = Memory::aob_scan(gameExecutable, CinematicsFOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!CinematicsFOVaddress)
+ logger->warn("Cinematics FOV signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Cinematics FOV signature found at address: 0x{:X}.", reinterpret_cast(CinematicsFOVaddress));
+ }
+ }
+ if (CinematicsFTaddress == nullptr) {
+ constexpr auto FTStringObfuscated = make_obfuscated<0x4A>("75 ?? 48 8B ?? ?? ?? ?? ?? 48 ?? ?? 74 ?? 39 ?? ?? 77");
+ CinematicsFTaddress = Memory::aob_scan(gameExecutable, FTStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!CinematicsFTaddress)
+ logger->warn("Cinematics frametime signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Cinematics frametime signature found at address: 0x{:X}.", reinterpret_cast(CinematicsFTaddress));
+ }
+ if (FOVaddress && CinematicsFOVaddress && CinematicsFTaddress) {
+ logger->info("All AOB signatures found. Ready to patch...");
+ logger->info("--------------- AOB scanning finished ---------------");
+ AOBScanDone = true;
+ }
+ }
+ }
+ if (g_fix_enabled) {
+ if (FOVaddress) FOVFixEnabled(g_fov_fix_enabled);
+ if (CinematicsFOVaddress) CinematicsFOVFixEnabled(g_cinematics_FOV_fix_enabled);
+ if (CinematicsFTaddress) CinematicsFTFixEnabled(g_cinematics_FT_fix_enabled);
+ }
+ else {
+ if (FOVaddress) FOVFixEnabled(false);
+ if (CinematicsFOVaddress) CinematicsFOVFixEnabled(false);
+ if (CinematicsFTaddress) CinematicsFTFixEnabled(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); // FOV fix must be enabled when aspect ratio is too to compensate FOV
+}
+
+extern "C" __declspec(dllexport) void SetCinematicsFOVFixEnabled(bool enabled, bool init)
+{
+ g_cinematics_FOV_fix_enabled = enabled;
+ if (!init) CinematicsFOVFixEnabled(g_cinematics_FOV_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetCinematicsFTFixEnabled(bool enabled, bool init)
+{
+ g_cinematics_FT_fix_enabled = enabled;
+ if (!init) CinematicsFTFixEnabled(g_cinematics_FT_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetFOV(int fov)
+{
+ g_AdditionalValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetCinematicsFOV(int fov)
+{
+ g_AdditionalCinematicsValue = fov;
+}
+
+// Getters for Reshade addon call
+extern "C" __declspec(dllexport) float GetFOVIn() {
+ return g_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetFOVOut() {
+ return g_FOV_Out;
+}
+
+extern "C" __declspec(dllexport) float GetCinematicsFOVIn() {
+ return g_Cinematics_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetCinematicsCompensadedFOV() {
+ return g_Compensated_Cinematics_FOV;
+}
+
+extern "C" __declspec(dllexport) float GetCinematicsFOVOut() {
+ return g_Cinematics_FOV_Out;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ FOVHook = safetyhook::create_mid(FOVaddress,
+ [](SafetyHookContext& ctx) {
+ g_FOV_In = ctx.xmm11.f32[0];
+ g_FOV_Out = ctx.xmm11.f32[0] += g_AdditionalValue;
+ });
+ }
+ else FOVHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ if (!fix_enabled && FOVHook) {
+ FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+static void CinematicsFOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && CinematicsFOVaddress != nullptr) {
+ if (!CinematicsFOVHook) {
+ CinematicsFOVHook = safetyhook::create_mid(CinematicsFOVaddress,
+ [](SafetyHookContext& ctx) {
+ g_Cinematics_FOV_In = ctx.xmm3.f32[0];
+ if (g_cinematics_FOV_fix_enabled)
+ g_Compensated_Cinematics_FOV = ctx.xmm3.f32[0] = Maths::CompensateHorizontalFOV(g_Cinematics_FOV_In, baseAspect, aspectRatio);
+ else
+ g_Compensated_Cinematics_FOV = ctx.xmm3.f32[0];
+ g_Cinematics_FOV_Out = ctx.xmm3.f32[0] += g_AdditionalCinematicsValue;
+ });
+ }
+ else CinematicsFOVHook.enable();
+ logger->info("Cinematics FOV fix enabled");
+ }
+ if (!fix_enabled && CinematicsFOVHook) {
+ CinematicsFOVHook.disable();
+ logger->info("Cinematics FOV fix disabled");
+ }
+}
+
+static void CinematicsFTFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && CinematicsFTaddress) {
+ Memory::PatchBytes(CinematicsFTaddress, "\x90\x90", 2); // NOP
+ logger->info("Cinematics frametime fix enabled");
+ }
+ if (!fix_enabled && CinematicsFTaddress) {
+ Memory::RestoreBytes(CinematicsFTaddress);
+ logger->info("Cinematics frametime fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ //CinematicsDOFFixEnabled(false); // Core is loaded 3 times so i need to restore previous patched bytes
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Maths/Maths.cpp b/Maths/Maths.cpp
new file mode 100644
index 0000000..84870a4
--- /dev/null
+++ b/Maths/Maths.cpp
@@ -0,0 +1,27 @@
+// Maths.cpp : Définit les fonctions de la bibliothèque statique.
+//
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#include "Maths.hpp"
+#include
+
+double Maths::DegreesToRadians(double degrees) {
+ return degrees * M_PI / 180.0;
+}
+
+// Convertit des radians en degrés
+double Maths::RadiansToDegrees(double radians) {
+ return radians * 180.0 / M_PI;
+}
+
+double Maths::CompensateHorizontalFOV(double baseHorizontalFOVDeg, double baseAspectRatio, double targetAspectRatio) {
+ double baseFOVRad = DegreesToRadians(baseHorizontalFOVDeg);
+ // Step 1 : FOV vertical from horizontal FOV
+ double verticalFOVRad = 2.0 * std::atan(std::tan(baseFOVRad / 2.0) / baseAspectRatio);
+ // Step 2 : New horizontal FOV for target aspect ratio
+ double newFOVRad = 2.0 * std::atan(std::tan(verticalFOVRad / 2.0) * targetAspectRatio);
+
+ return RadiansToDegrees(newFOVRad);
+}
\ No newline at end of file
diff --git a/Maths/Maths.hpp b/Maths/Maths.hpp
new file mode 100644
index 0000000..f7d9892
--- /dev/null
+++ b/Maths/Maths.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include
+
+class Maths
+{
+ public:
+ // Compute new horizontal FOV based on native and target aspect ratio
+ static double CompensateHorizontalFOV(const double baseHorizontalFOVDeg, const double baseAspectRatio, const double targetAspectRatio);
+
+ private:
+ static double DegreesToRadians(double degrees);
+ static double RadiansToDegrees(double radians);
+};
diff --git a/Maths/Maths.vcxproj b/Maths/Maths.vcxproj
new file mode 100644
index 0000000..b3ab589
--- /dev/null
+++ b/Maths/Maths.vcxproj
@@ -0,0 +1,159 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {9a3c36e4-b32b-43db-ac7b-8ab45dc3097e}
+ Maths
+ 10.0
+
+
+
+ StaticLibrary
+ true
+ v143
+ Unicode
+
+
+ StaticLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ StaticLibrary
+ true
+ v143
+ Unicode
+
+
+ StaticLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Maths/framework.h b/Maths/framework.h
new file mode 100644
index 0000000..afd886f
--- /dev/null
+++ b/Maths/framework.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclure les en-têtes Windows rarement utilisés
diff --git a/Memory/Memory.cpp b/Memory/Memory.cpp
new file mode 100644
index 0000000..215062f
--- /dev/null
+++ b/Memory/Memory.cpp
@@ -0,0 +1,183 @@
+// MemoryScanner.cpp : Définit les fonctions de la bibliothèque statique.
+//
+
+#include "Memory.hpp"
+#include
+#include
+#include
+#include
+#include
+
+static std::shared_ptr _log;
+std::unordered_map Memory::patches;
+
+void Memory::PatchBytes(void* address, const char* bytes, size_t len)
+{
+ auto it = patches.find(address);
+ if (it == patches.end())
+ {
+ // If a patch doesn't exist, create a new one.
+ PatchInfo info;
+ info.address = address;
+ info.originalBytes.resize(len);
+ memcpy(info.originalBytes.data(), address, len);
+ // Store the patch info.
+ patches[address] = info;
+ }
+
+ // Patch the bytes.
+ DWORD oldProtect;
+ VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &oldProtect);
+ memcpy(address, bytes, len);
+ VirtualProtect(address, len, oldProtect, &oldProtect);
+}
+
+//void Memory::RestoreBytes(uintptr_t address)
+void Memory::RestoreBytes(void *address)
+{
+ auto it = patches.find(address);
+ if (it != patches.end())
+ {
+ // Restore the original bytes.
+ const auto& info = it->second;
+ DWORD oldProtect;
+ VirtualProtect(info.address, info.originalBytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect);
+ memcpy(info.address, info.originalBytes.data(), info.originalBytes.size());
+ VirtualProtect(info.address, info.originalBytes.size(), oldProtect, &oldProtect);
+
+ // Remove the patch info.
+ patches.erase(it);
+ }
+}
+
+bool Memory::wait_for_module(const std::string& module_name, int timeoutMs = 15000, int intervalMs = 500)
+{
+ const HANDLE hProc = GetCurrentProcess();
+
+ for (int waited = 0; waited < timeoutMs; waited += intervalMs)
+ {
+ HMODULE hMods[1024];
+ DWORD cbNeeded;
+
+ if (EnumProcessModules(hProc, hMods, sizeof(hMods), &cbNeeded))
+ {
+ for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); ++i)
+ {
+ char modName[MAX_PATH];
+ if (GetModuleBaseNameA(hProc, hMods[i], modName, sizeof(modName)))
+ {
+ if (_stricmp(modName, module_name.c_str()) == 0)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ Sleep(intervalMs);
+ }
+
+ if (_log) _log->warn("Timeout: module '{}' not found in process after {} ms.", module_name, timeoutMs);
+ return false;
+}
+
+std::string Memory::byteToHexEscaped(const BYTE byte) {
+ std::ostringstream oss;
+ oss << "\\x" << std::uppercase << std::hex << std::setw(2)
+ << std::setfill('0') << static_cast(byte);
+ return oss.str();
+}
+
+uint8_t* Memory::aob_scan(
+ const std::string& module_name,
+ const std::string& signature,
+ DWORD protect_flags = PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_READWRITE | PAGE_EXECUTE_WRITECOPY,
+ std::shared_ptr log) {
+
+ _log = log;
+ if (!wait_for_module(module_name))
+ {
+ if (log) log->warn("Skipping AOB scan because module '{}' is unavailable.", module_name);
+ return nullptr;
+ }
+
+ // Convert signature to bytes
+ std::vector pattern_bytes;
+ std::istringstream stream(signature);
+ std::string byte_str;
+ while (stream >> byte_str)
+ {
+ if (byte_str == "??" || byte_str == "?")
+ pattern_bytes.push_back(-1);
+ else
+ pattern_bytes.push_back(static_cast(std::strtol(byte_str.c_str(), nullptr, 16)));
+ }
+
+ HMODULE hMods[1024];
+ DWORD cbNeeded;
+ HANDLE hProc = GetCurrentProcess();
+
+ if (!EnumProcessModules(hProc, hMods, sizeof(hMods), &cbNeeded))
+ {
+ spdlog::error("EnumProcessModules failed.");
+ return nullptr;
+ }
+
+ for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); ++i)
+ {
+ char modName[MAX_PATH];
+ if (GetModuleBaseNameA(hProc, hMods[i], modName, sizeof(modName)))
+ {
+ if (_stricmp(modName, module_name.c_str()) == 0)
+ {
+ MODULEINFO modInfo;
+ if (!GetModuleInformation(hProc, hMods[i], &modInfo, sizeof(modInfo)))
+ {
+ if (log) log->error("GetModuleInformation failed for '{}'", module_name);
+ return nullptr;
+ }
+
+ uint8_t* base = reinterpret_cast(modInfo.lpBaseOfDll);
+ size_t size = modInfo.SizeOfImage;
+ if (log) log->info("Scanning memory region: 0x{:X} - 0x{:X}", reinterpret_cast(base), reinterpret_cast(base + size));
+
+ MEMORY_BASIC_INFORMATION mbi{};
+ for (uint8_t* current = base; current < base + size;)
+ {
+ if (!VirtualQuery(current, &mbi, sizeof(mbi)))
+ break;
+
+ if ((mbi.State & MEM_COMMIT) && (mbi.Protect & protect_flags))
+ {
+ for (size_t i = 0; i <= mbi.RegionSize - pattern_bytes.size(); ++i)
+ {
+ bool match = true;
+ for (size_t j = 0; j < pattern_bytes.size(); ++j)
+ {
+ if (pattern_bytes[j] != -1 && current[i + j] != static_cast(pattern_bytes[j]))
+ {
+ match = false;
+ break;
+ }
+ }
+
+ if (match)
+ {
+ uint8_t* result = current + i;
+ return result;
+ }
+ }
+ }
+
+ current = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize;
+ }
+
+ if (log) log->warn("No AOB match found in module '{}'.", module_name);
+ return nullptr;
+ }
+ }
+ }
+
+ if (log) log->warn("Module '{}' unexpectedly disappeared during scan.", module_name);
+ return nullptr;
+}
diff --git a/Memory/Memory.hpp b/Memory/Memory.hpp
new file mode 100644
index 0000000..475d642
--- /dev/null
+++ b/Memory/Memory.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+
+#define AUTO_ASSEMBLE_TRAMPOLINE(ADDRESS, TRAMPOLINE_LENGTH, INSTRUCTIONS) \
+do { \
+auto allocMemory = Memory::AllocateNearbyMemory(ADDRESS, sizeof INSTRUCTIONS + 14); \
+Memory::CreateTrampoline(ADDRESS, allocMemory, TRAMPOLINE_LENGTH); \
+Memory::WriteInstructions(allocMemory, INSTRUCTIONS, sizeof INSTRUCTIONS, ADDRESS + TRAMPOLINE_LENGTH); \
+} while (false)
+
+
+class Memory
+{
+ public:
+ static void PatchBytes(void* address, const char* bytes, size_t len);
+ //static void RestoreBytes(uintptr_t address);
+ static void RestoreBytes(void* address);
+ // AOB scan dans le module spcifi (par nom), avec filtrage sur protections (ex: PAGE_EXECUTE_READ) et offset optionnel
+ static uint8_t* aob_scan(const std::string& module_name, const std::string& signature, DWORD protect_flags, std::shared_ptr log = nullptr);
+ static bool wait_for_module(const std::string& module_name, int timeoutMs, int intervalMs);
+ static std::string byteToHexEscaped(const BYTE byte);
+ private:
+ struct PatchInfo {
+ void* address;
+ std::vector originalBytes;
+ bool hasTrampoline = false;
+ void* trampolineDestination = nullptr;
+ };
+
+ static std::unordered_map patches;
+};
diff --git a/Memory/Memory.vcxproj b/Memory/Memory.vcxproj
new file mode 100644
index 0000000..9a85b10
--- /dev/null
+++ b/Memory/Memory.vcxproj
@@ -0,0 +1,164 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {f9b5bbc6-67d4-4290-986f-08c6bac41ba3}
+ Memory
+ 10.0
+ Memory
+
+
+
+ StaticLibrary
+ true
+ v143
+ Unicode
+
+
+ StaticLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ StaticLibrary
+ true
+ v143
+ Unicode
+
+
+ StaticLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ $(SolutionDir)external;%(AdditionalIncludeDirectories)
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ $(SolutionDir)external;%(AdditionalIncludeDirectories)
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ $(SolutionDir)external;%(AdditionalIncludeDirectories)
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ $(SolutionDir)external;%(AdditionalIncludeDirectories)
+
+ /utf-8 %(AdditionalOptions)
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Memory/framework.h b/Memory/framework.h
new file mode 100644
index 0000000..afd886f
--- /dev/null
+++ b/Memory/framework.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclure les en-têtes Windows rarement utilisés
diff --git a/Metro Exodus/Metro Exodus.vcxproj b/Metro Exodus/Metro Exodus.vcxproj
new file mode 100644
index 0000000..73e2df2
--- /dev/null
+++ b/Metro Exodus/Metro Exodus.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {1543C697-9528-4D08-A271-222A78A15C1C}
+ Metro Exodus
+ 10.0
+ Metro Exodus
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MetroExodusCore
+ .asi
+
+
+ MetroExodusCore
+ .asi
+
+
+ MetroExodusCore
+ .asi
+
+
+ MetroExodusCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Metro Exodus/dllmain.cpp b/Metro Exodus/dllmain.cpp
new file mode 100644
index 0000000..bda6715
--- /dev/null
+++ b/Metro Exodus/dllmain.cpp
@@ -0,0 +1,269 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "Metro Exodus";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "MetroExodus.exe";
+
+static int screenWidth = GetSystemMetrics(SM_CXSCREEN);
+static int screenHeight = GetSystemMetrics(SM_CYSCREEN);
+static float aspectRatio = (float)screenWidth / screenHeight;
+
+// Logger
+std::shared_ptr logger;
+
+// Plugin states
+static bool AOBScanDone = false;
+static bool g_fix_enabled = false;
+static bool g_fov_fix_enabled = false;
+static bool g_cutscenes_fix_enabled = false;
+static bool g_aspect_ratio_fix_enabled = false;
+static int g_AdditionalFOVValue = 0;
+static int g_AdditionalCutscenesFOVValue = 0;
+
+// Shared values
+static float g_FOV_In = 0;
+static float g_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVaddress = nullptr;
+static uint8_t* CutscenesFOVaddress = nullptr;
+static uint8_t* Aspectaddress_1 = nullptr;
+static uint8_t* Aspectaddress_2 = nullptr;
+
+// Hooking
+static SafetyHookMid FOVHook{};
+static SafetyHookMid CutscenesFOVHook{};
+static SafetyHookMid AspectRatioHook_1{};
+static SafetyHookMid AspectRatioHook_2{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+static void CutscenesFOVFixEnabled(bool fix_enabled);
+static void AspectRatioFixEnabled(bool fix_enabled);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("3C ?? 0F 86 ?? ?? ?? ?? F3 0F");
+ 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 += 0x10; // Offset for the target opcode
+ }
+ }
+ if (CutscenesFOVaddress == nullptr) {
+ constexpr auto CutscenesFOVStringObfuscated = make_obfuscated<0x4A>("F3 0F ?? ?? ?? ?? ?? ?? E9 96 ?? ?? ?? 48 8B");
+ CutscenesFOVaddress = Memory::aob_scan(gameExecutable, CutscenesFOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!CutscenesFOVaddress)
+ logger->warn("Cutscenes FOV signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Cutscenes FOV signature found at address: 0x{:X}.", reinterpret_cast(CutscenesFOVaddress));
+ CutscenesFOVaddress += 0x8; // Offset for the target opcode
+ }
+ }
+ if (Aspectaddress_1 == nullptr) {
+ constexpr auto AspectRatioStringObfuscated_1 = make_obfuscated<0x4A>("F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? ?? ?? ?? 89 84 ?? ?? ?? ?? ?? 8B");
+ Aspectaddress_1 = Memory::aob_scan(gameExecutable, AspectRatioStringObfuscated_1.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!Aspectaddress_1)
+ logger->warn("1st aspect ratio signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("1st aspect ratio signature found at address: 0x{:X}.", reinterpret_cast(Aspectaddress_1));
+ Aspectaddress_1 += 0x8; // Offset for the target opcode
+ }
+ }
+ if (Aspectaddress_2 == nullptr) {
+ constexpr auto AspectRatioStringObfuscated_2 = make_obfuscated<0x4A>("F3 0F ?? ?? ?? ?? ?? ?? 0F ?? ?? 48 8D ?? ?? ?? ?? ?? F3 48 ?? ?? ?? 49 8D");
+ Aspectaddress_2 = Memory::aob_scan(gameExecutable, AspectRatioStringObfuscated_2.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!Aspectaddress_2)
+ logger->warn("2nd aspect ratio signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("2nd aspect ratio signature found at address: 0x{:X}.", reinterpret_cast(Aspectaddress_2));
+ Aspectaddress_2 += 0x8; // Offset for the target opcode
+ }
+ if (FOVaddress && CutscenesFOVaddress && Aspectaddress_1 && Aspectaddress_2) {
+ logger->info("All AOB signatures found. Ready to patch...");
+ logger->info("--------------- AOB scanning finished ---------------");
+ AOBScanDone = true;
+ }
+ }
+ }
+ if (g_fix_enabled) {
+ if (FOVaddress) FOVFixEnabled(g_fov_fix_enabled);
+ if (CutscenesFOVaddress) CutscenesFOVFixEnabled(g_cutscenes_fix_enabled);
+ if (Aspectaddress_1 && Aspectaddress_2) AspectRatioFixEnabled(g_aspect_ratio_fix_enabled);
+ }
+ else {
+ if (FOVaddress) FOVFixEnabled(false);
+ if (CutscenesFOVaddress) CutscenesFOVFixEnabled(false);
+ if (Aspectaddress_1 && Aspectaddress_2) AspectRatioFixEnabled(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);
+}
+
+extern "C" __declspec(dllexport) void SetCutscenesFOVFixEnabled(bool enabled, bool init)
+{
+ g_cutscenes_fix_enabled = enabled;
+ if (!init) CutscenesFOVFixEnabled(g_cutscenes_fix_enabled);
+}
+
+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 SetFOV(int fov)
+{
+ g_AdditionalFOVValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetCutscenesFOV(int fov)
+{
+ g_AdditionalCutscenesFOVValue = fov;
+}
+
+// Getters for Reshade addon call
+extern "C" __declspec(dllexport) float GetFOVIn() {
+ return g_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetFOVOut() {
+ return g_FOV_Out;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ FOVHook = safetyhook::create_mid(FOVaddress,
+ [](SafetyHookContext& ctx) {
+ g_FOV_In = ctx.xmm2.f32[0];
+ g_FOV_Out = ctx.xmm2.f32[0] += g_AdditionalFOVValue;
+ });
+ }
+ else FOVHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ if (!fix_enabled && FOVHook) {
+ FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+static void CutscenesFOVFixEnabled (bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && CutscenesFOVaddress) {
+ if (!CutscenesFOVHook) { // Hook only once
+ CutscenesFOVHook = safetyhook::create_mid(CutscenesFOVaddress,
+ [](SafetyHookContext& ctx) {
+ g_FOV_In = ctx.xmm2.f32[0];
+ g_FOV_Out = ctx.xmm2.f32[0] += g_AdditionalCutscenesFOVValue;
+ });
+ }
+ else CutscenesFOVHook.enable();
+ logger->info("Cutscenes FOV fix enabled");
+ }
+ if (!fix_enabled && CutscenesFOVHook) {
+ CutscenesFOVHook.disable();
+ logger->info("Cutscenes FOV fix disabled");
+ }
+}
+
+static void AspectRatioFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && Aspectaddress_1 && Aspectaddress_2) {
+ if (!AspectRatioHook_1) {
+ AspectRatioHook_1 = safetyhook::create_mid(Aspectaddress_1,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm6.f32[0] = aspectRatio;
+ });
+ }
+ else {
+ AspectRatioHook_1.enable();
+ }
+ if (!AspectRatioHook_2) {
+ AspectRatioHook_2 = safetyhook::create_mid(Aspectaddress_2,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm2.f32[0] = aspectRatio;
+ });
+ }
+ else {
+ AspectRatioHook_2.enable();
+ }
+ logger->info("Aspect ratio fix enabled");
+ }
+ if (!fix_enabled) {
+ if (AspectRatioHook_1 && Aspectaddress_1)
+ AspectRatioHook_1.disable();
+ if (AspectRatioHook_2 && Aspectaddress_2)
+ AspectRatioHook_2.disable();
+
+ uint8_t* Aspectaddress_3 = Aspectaddress_2 - 0x12;
+ int32_t rel_offset = static_cast(Aspectaddress_3[4]) | (static_cast(Aspectaddress_3[5]) << 8) |
+ (static_cast(Aspectaddress_3[6]) << 16) | (static_cast(Aspectaddress_3[7]) << 24);
+ // Calculer l'adresse effective (rip + offset), instruction fait 8 octets
+ uint8_t* target_addr = Aspectaddress_3 + 8 + rel_offset; // opcode is 8 bytes long
+ *reinterpret_cast(target_addr) = 1.78f; // movss xmm2, [MetroExodus.exe + 164FEBC] patch base + offset
+
+ logger->info("Aspect ratio fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Mindseye/Mindseye.vcxproj b/Mindseye/Mindseye.vcxproj
new file mode 100644
index 0000000..f698986
--- /dev/null
+++ b/Mindseye/Mindseye.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {428FCE1A-9CD0-4BB7-81EC-88CA0864A16E}
+ Mindseye
+ 10.0
+ Mindseye
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MindseyeCore
+ .asi
+
+
+ MindseyeCore
+ .asi
+
+
+ MindseyeCore
+ .asi
+
+
+ MindseyeCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Mindseye/dllmain.cpp b/Mindseye/dllmain.cpp
new file mode 100644
index 0000000..11af2f8
--- /dev/null
+++ b/Mindseye/dllmain.cpp
@@ -0,0 +1,225 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "Mindseye";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "MindsEye-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);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("EB ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? 8B 83 ?? ?? ?? ?? 89 ?? ?? 0F B6 8B");
+ 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 scanning 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;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ 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) {
+ 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");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Obfuscate/Obfuscate.vcxproj b/Obfuscate/Obfuscate.vcxproj
new file mode 100644
index 0000000..8c8de3d
--- /dev/null
+++ b/Obfuscate/Obfuscate.vcxproj
@@ -0,0 +1,156 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {7e0aec88-78b4-43ea-bbef-216e00df1424}
+ Obfuscate
+ 10.0
+
+
+
+ StaticLibrary
+ true
+ v143
+ Unicode
+
+
+ StaticLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ StaticLibrary
+ true
+ v143
+ Unicode
+
+
+ StaticLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ stdcpp20
+
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ stdcpp20
+
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ stdcpp20
+
+
+
+
+
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ stdcpp20
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Obfuscate/ObfuscateString.h b/Obfuscate/ObfuscateString.h
new file mode 100644
index 0000000..a37525f
--- /dev/null
+++ b/Obfuscate/ObfuscateString.h
@@ -0,0 +1,33 @@
+#pragma once
+#include
+#include
+
+template
+class ObfuscatedString {
+private:
+ std::array data;
+
+public:
+ constexpr ObfuscatedString(const std::array& obfuscatedData)
+ : data(obfuscatedData) {
+ }
+
+ std::string decrypt() const {
+ std::string result;
+ result.reserve(N);
+ for (char c : data) {
+ result.push_back(c ^ KEY);
+ }
+ return result;
+ }
+};
+
+// Helper constexpr pour faire le chiffrement la compilation
+template
+constexpr auto make_obfuscated(const char(&input)[N]) {
+ std::array result{};
+ for (size_t i = 0; i < N; ++i) {
+ result[i] = input[i] ^ KEY;
+ }
+ return ObfuscatedString(result);
+}
diff --git a/Obfuscate/framework.h b/Obfuscate/framework.h
new file mode 100644
index 0000000..afd886f
--- /dev/null
+++ b/Obfuscate/framework.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclure les en-têtes Windows rarement utilisés
diff --git a/South Of Midnight/South Of Midnight.vcxproj b/South Of Midnight/South Of Midnight.vcxproj
new file mode 100644
index 0000000..ebb30bb
--- /dev/null
+++ b/South Of Midnight/South Of Midnight.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {5e6699da-b075-449f-a941-d757ea22cbd8}
+ ReshadePluginsCore
+ 10.0
+ South Of Midnight
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SouthOfMidnightCore
+ .asi
+
+
+ SouthOfMidnightCore
+ .asi
+
+
+ SouthOfMidnightCore
+ .asi
+
+
+ SouthOfMidnightCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/South Of Midnight/dllmain.cpp b/South Of Midnight/dllmain.cpp
new file mode 100644
index 0000000..2a8d849
--- /dev/null
+++ b/South Of Midnight/dllmain.cpp
@@ -0,0 +1,213 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "South Of Midnight";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "SouthOfMidnight.exe";
+const float baseAspect = 1.777778;
+
+// 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 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;
+
+// Hooking
+static SafetyHookMid FOVHook{};
+static SafetyHookMid AspectRatioHook{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+static void AspectRatioFixEnabled(bool fix_enabled);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("90 0F ?? ?? 48 8B 8C ?? ?? ?? ?? ?? 48 ?? ?? E8 ?? ?? ?? ?? 0F 28");
+ FOVaddress = Memory::aob_scan(gameExecutable, FOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ //SouthOfMidnight.exe + 357E051 - E8 5A 4A ED FD - call SouthOfMidnight.exe + 1452AB0
+ //SouthOfMidnight.exe + 357E056 - 90 - nop
+ //SouthOfMidnight.exe + 357E057 - 0F 28 C6 - movaps xmm0, xmm6
+ //SouthOfMidnight.exe + 357E05A - 48 8B 8C 24 50 09 00 00 - mov rcx, [rsp + 00000950] <== Injection point
+ //SouthOfMidnight.exe + 357E062 - 48 33 CC - xor rcx, rsp
+ 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 += 4; // Offset for the target opcode
+ }
+ }
+ if (Aspectaddress == nullptr) {
+ constexpr auto AspectStringObfuscated = make_obfuscated<0x4A>("EB ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? 8B 87 ?? ?? ?? ?? 89 ?? ?? 0F B6");
+ Aspectaddress = Memory::aob_scan(gameExecutable, AspectStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ //SouthOfMidnight.exe + 3161AB4 - F3 0F 10 87 F8 01 00 00 - movss xmm0, [rdi + 000001F8]
+ //SouthOfMidnight.exe + 3161ABC - F3 0F 11 46 18 - movss[rsi + 18], xmm0
+ //SouthOfMidnight.exe + 3161AC1 - 8B 87 08 02 00 00 - mov eax, [rdi + 00000208]
+ //SouthOfMidnight.exe + 3161AC7 - 89 46 2C - mov[rsi + 2C], eax <== Injection point
+ //SouthOfMidnight.exe + 3161ACA - 0F B6 8F 0C 02 00 00 - movzx ecx, byte ptr[rdi + 0000020C]
+ if (!Aspectaddress)
+ logger->warn("Aspect ratio signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Aspect ratio signature found at address: 0x{:X}.", reinterpret_cast(Aspectaddress));
+ Aspectaddress += 0x15; // Offset for the target opcode
+ }
+ if (FOVaddress != nullptr && Aspectaddress != nullptr) {
+ logger->info("All AOB signatures found. Ready to patch...");
+ logger->info("--------------- AOB scanning finished ---------------");
+ AOBScanDone = true;
+ }
+ }
+ }
+ if (FOVaddress && Aspectaddress) {
+ if (g_fix_enabled) {
+ AspectRatioFixEnabled(g_aspect_ratio_fix_enabled);
+ FOVFixEnabled(g_fov_fix_enabled || g_aspect_ratio_fix_enabled);
+ }
+ else {
+ AspectRatioFixEnabled(false);
+ FOVFixEnabled(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;
+ 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;
+ AspectRatioFixEnabled(g_aspect_ratio_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;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ 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) {
+ 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();
+ logger->info("Aspect ratio fix enabled");
+ FOVFixEnabled(fix_enabled);
+ }
+ if (!fix_enabled && AspectRatioHook) {
+ AspectRatioHook.disable();
+ logger->info("Aspect ratio fix disabled");
+ if (!g_fov_fix_enabled)
+ FOVFixEnabled(fix_enabled);
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ PLUGIN_LOG.c_str();
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/SpiritOfTheNorth/SpiritOfTheNorth.vcxproj b/SpiritOfTheNorth/SpiritOfTheNorth.vcxproj
new file mode 100644
index 0000000..9a138f7
--- /dev/null
+++ b/SpiritOfTheNorth/SpiritOfTheNorth.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {BA6D2823-D7AF-4F03-9E43-F66CE771F25F}
+ SpiritOfTheNorth
+ 10.0
+ Spirit Of The North
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SpiritOfTheNorthCore
+ .asi
+
+
+ SpiritOfTheNorthCore
+ .asi
+
+
+ SpiritOfTheNorthCore
+ .asi
+
+
+ SpiritOfTheNorthCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/SpiritOfTheNorth/dllmain.cpp b/SpiritOfTheNorth/dllmain.cpp
new file mode 100644
index 0000000..0b78dfc
--- /dev/null
+++ b/SpiritOfTheNorth/dllmain.cpp
@@ -0,0 +1,225 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "Spirit Of The North";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+const std::string gameExecutable = "Infused-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);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ constexpr auto FOVStringObfuscated = make_obfuscated<0x4A>("EB ?? F3 0F ?? ?? ?? ?? ?? ?? F3 0F ?? ?? ?? 8B 83 ?? ?? ?? ?? 89");
+ 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 ?? ?? E8 ?? ?? ?? ?? 8B ?? E8 ?? ?? ?? ?? 84 ?? 74 ?? 48");
+ 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 scanning 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;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ 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) {
+ 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");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/TLOU2/TLOU2.vcxproj b/TLOU2/TLOU2.vcxproj
new file mode 100644
index 0000000..cc8fcb9
--- /dev/null
+++ b/TLOU2/TLOU2.vcxproj
@@ -0,0 +1,216 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {4D1258B1-DE55-4220-BFBD-A8616DF7197E}
+ ReshadePluginsCore
+ 10.0
+ TLOU2
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TLOU2Core
+ .asi
+
+
+ TLOU2Core
+ .asi
+
+
+ TLOU2Core
+ .asi
+
+
+ TLOU2Core
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/TLOU2/dllmain.cpp b/TLOU2/dllmain.cpp
new file mode 100644
index 0000000..8804f0d
--- /dev/null
+++ b/TLOU2/dllmain.cpp
@@ -0,0 +1,245 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "The Last Of Us : Part II";
+const std::string PLUGIN_LOG = "TLOU2.log";
+const std::string gameExecutable = "tlou-ii.exe";
+
+// 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_camera_fix_enabled = false;
+static bool g_sharpness_fix_enabled = false;
+static int g_AdditionalFOVValue = 0;
+static float g_CameraValue = 0;
+static float g_SharpnessValue = 0;
+
+// Shared values
+static float g_FOV_In = 0;
+static float g_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVAddress = nullptr;
+static uint8_t* CameraAddress = nullptr;
+static uint8_t* SharpnessAddress = nullptr;
+
+// Hooking
+static SafetyHookMid FOVHook{};
+static SafetyHookMid CameraHook{};
+static SafetyHookMid SharpnessHook{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+static void CameraFixEnabled(bool fix_enabled);
+static void SharpnessFixEnabled(bool fix_enabled);
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled)
+{
+ g_fix_enabled = enabled;
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("--------------- AOB scanning started ---------------");
+ // === AOB Scans ===
+ if (!FOVAddress) {
+ constexpr auto FOVPattern = make_obfuscated<0x4A>("E8 ?? ?? ?? ?? C5 FA ?? ?? ?? ?? ?? ?? C5 F8 ?? ?? E8 ?? ?? ?? ?? C5 7A");
+ FOVAddress = Memory::aob_scan(gameExecutable, FOVPattern.decrypt(), PAGE_EXECUTE_READ);
+ if (FOVAddress)
+ {
+ logger->info("FOV signature found at address: 0x{:X}.", reinterpret_cast(FOVAddress));
+ FOVAddress += 0x5;
+ }
+ else logger->warn("FOV signature not found. Maybe the game was updated.");
+ }
+ if (!CameraAddress) {
+ constexpr auto CameraPattern = make_obfuscated<0x4A>("C5 FA ?? ?? ?? ?? ?? ?? C5 FA ?? ?? ?? C5 22 ?? ?? ?? C4 C1 ?? ?? ?? C5 FA ?? ?? ?? C5 7A");
+ CameraAddress = Memory::aob_scan(gameExecutable, CameraPattern.decrypt(), PAGE_EXECUTE_READ);
+ if (CameraAddress)
+ {
+ logger->info("Camera signature found at address: 0x{:X}.", reinterpret_cast(CameraAddress));
+ CameraAddress += 0x8;
+ }
+ else logger->warn("Camera signature not found. Maybe the game was updated.");
+ }
+ if (!SharpnessAddress) {
+ constexpr auto SharpnessPattern = make_obfuscated<0x4A>("C5 FA ?? ?? ?? ?? ?? ?? 8B 86 ?? ?? ?? ?? 89 87 ?? ?? ?? ?? 0F B6 ?? ?? ?? ?? ?? 89");
+ SharpnessAddress = Memory::aob_scan(gameExecutable, SharpnessPattern.decrypt(), PAGE_EXECUTE_READ);
+ if (SharpnessAddress)
+ {
+ logger->info("Sharpness signature found at address: 0x{:X}.", reinterpret_cast(SharpnessAddress));
+ }
+ else logger->warn("Sharpness signature not found. Maybe the game was updated.");
+ if (FOVAddress && CameraAddress && SharpnessAddress) {
+ logger->info("All AOB signatures found. Ready to patch...");
+ logger->info("--------------- AOB scanning finished ---------------");
+ AOBScanDone = true;
+ }
+ else
+ logger->warn("Some AOB signatures could not be found. Fixes may be partially unavailable.");
+ }
+ }
+ // === Activer ou désactiver les patchs ===
+ if (FOVAddress && CameraAddress && SharpnessAddress)
+ {
+ if (g_fix_enabled)
+ {
+ FOVFixEnabled(g_fov_fix_enabled);
+ CameraFixEnabled(g_camera_fix_enabled);
+ SharpnessFixEnabled(g_sharpness_fix_enabled);
+ }
+ else
+ {
+ FOVFixEnabled(false);
+ CameraFixEnabled(false);
+ SharpnessFixEnabled(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);
+}
+
+extern "C" __declspec(dllexport) void SetCameraFixEnabled(bool enabled, bool init)
+{
+ g_camera_fix_enabled = enabled;
+ if (!init) CameraFixEnabled(g_camera_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetSharpnessFixEnabled(bool enabled, bool init)
+{
+ g_sharpness_fix_enabled = enabled;
+ if (!init) SharpnessFixEnabled(g_sharpness_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetFOV(int fov)
+{
+ g_AdditionalFOVValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetCamera(float cameraDistance)
+{
+ g_CameraValue = cameraDistance;
+}
+
+extern "C" __declspec(dllexport) void SetSharpness(float sharpness)
+{
+ g_SharpnessValue = sharpness;
+}
+
+// Getters for Reshade addon call
+extern "C" __declspec(dllexport) float GetFOVIn() {
+ return g_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetFOVOut() {
+ return g_FOV_Out;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVAddress != nullptr) {
+ if (!FOVHook) { // Hook only once
+ FOVHook = safetyhook::create_mid(FOVAddress,
+ [](SafetyHookContext& ctx) {
+ if (ctx.xmm0.f32[0] == ctx.xmm15.f32[0]) {
+ g_FOV_In = ctx.xmm0.f32[0];
+ g_FOV_Out = ctx.xmm0.f32[0] += (g_fov_fix_enabled ? g_AdditionalFOVValue : 0); // World FOV
+ }
+ });
+ }
+ else FOVHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ if (!fix_enabled && FOVHook) {
+ FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+static void CameraFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && CameraAddress != nullptr) {
+ if (!CameraHook) {
+ CameraHook = safetyhook::create_mid(CameraAddress,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm0.f32[0] = (g_camera_fix_enabled ? g_CameraValue : ctx.xmm0.f32[0]);
+ });
+ }
+ else CameraHook.enable();
+ logger->info("Camera fix enabled");
+ }
+ if (!fix_enabled && CameraHook) {
+ CameraHook.disable();
+ logger->info("Camera fix disabled");
+ }
+}
+
+static void SharpnessFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && SharpnessAddress != nullptr) {
+ if (!SharpnessHook) {
+ SharpnessHook = safetyhook::create_mid(SharpnessAddress,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm0.f32[0] = g_SharpnessValue;
+ });
+ }
+ else SharpnessHook.enable();
+ logger->info("Sharpness fix enabled");
+ }
+ if (!fix_enabled && SharpnessHook) {
+ SharpnessHook.disable();
+ logger->info("sharpness fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+HMODULE g_coreModule = nullptr;
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/Uncharted LOTC/UnchartedLOTC.vcxproj b/Uncharted LOTC/UnchartedLOTC.vcxproj
new file mode 100644
index 0000000..3ba7e67
--- /dev/null
+++ b/Uncharted LOTC/UnchartedLOTC.vcxproj
@@ -0,0 +1,217 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {E6E66136-4426-4F91-872F-FFF96173DB2C}
+ UnchartedLOTC
+ 10.0
+ Uncharted LOTC
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UnchartedLOTCCore
+ .asi
+
+
+ UnchartedLOTCCore
+ .asi
+
+
+ UnchartedLOTCCore
+ .asi
+
+
+ UnchartedLOTCCore
+ .asi
+
+
+
+ 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;%(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;%(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;%(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;%(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/Uncharted LOTC/dllmain.cpp b/Uncharted LOTC/dllmain.cpp
new file mode 100644
index 0000000..dee9112
--- /dev/null
+++ b/Uncharted LOTC/dllmain.cpp
@@ -0,0 +1,397 @@
+#include "Memory.hpp";
+#include "Maths.hpp";
+#include "ObfuscateString.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Constants
+const std::string PLUGIN_NAME = "UnchartedLOTC";
+const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
+std::string previousGameExecutable = "";
+std::string gameExecutable = "";
+
+// Logger
+std::shared_ptr logger;
+
+// Plugin states
+static bool AOBScanDone = false;
+static bool g_fix_enabled = false;
+static bool g_fov_fix_enabled = false;
+static bool g_photo_fix_enabled = false;
+static bool g_cinematics_fix_enabled = false;
+static bool g_aspect_ratio_fix_enabled = false;
+static bool g_DOF_fix_enabled = false;
+static bool g_CA_fix_enabled = false; // Chromatic aberrations
+static bool g_BD_fix_enabled = false; // Barell distortion
+static int g_AdditionalValue = 0;
+static int g_AdditionalPhotoValue = 0;
+static float g_AspectValue = 0;
+
+// Shared values
+static float g_FOV_In = 0;
+static float g_FOV_Out = 0;
+
+// AOB Scan pointers
+static uint8_t* FOVaddress = nullptr;
+static uint8_t* PHOTOaddress = nullptr;
+static uint8_t* CINEMATICSASPECTaddress = nullptr;
+static uint8_t* Aspectaddress = nullptr;
+static uint8_t* DOFaddress = nullptr;
+static uint8_t* EFFECTSaddress = nullptr; // Base effects
+static uint8_t* CAaddress = nullptr; // Base effects
+static uint8_t* BDaddress_1 = nullptr; // Barrel distortions
+static uint8_t* BDaddress_2 = nullptr; // Barrel distortions
+
+// Hooking
+static SafetyHookMid FOVHook{};
+static SafetyHookMid PhotoFOVHook{};
+static SafetyHookMid AspectRatioHook{};
+static SafetyHookMid DOFHook{};
+
+// Prototypes
+static void FOVFixEnabled(bool fix_enabled);
+static void PhotoFOVFixEnabled(bool fix_enabled);
+static void CinematicsARFixEnabled(bool fix_enabled);
+static void AspectRatioFixEnabled(bool fix_enabled);
+static void DOFFixEnabled(bool fix_enabled);
+static void CAFixEnabled(bool fix_enabled);
+static void BDFixEnabled(bool fix_enabled);
+
+// Original byte
+static std::vector originalBytes = std::vector();
+
+HANDLE g_mutex = NULL;
+
+static std::string GetExecutableFileNameOnly() {
+ char path[MAX_PATH];
+ GetModuleFileNameA(nullptr, path, MAX_PATH);
+ return std::filesystem::path(path).filename().string();
+}
+
+extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init)
+{
+ g_fix_enabled = enabled;
+ gameExecutable = GetExecutableFileNameOnly();
+
+ if (g_fix_enabled && !AOBScanDone) {
+ logger->info("Module {} detected", gameExecutable);
+ logger->info("--------------- AOB scanning started ---------------");
+ if (FOVaddress == nullptr) {
+ if (gameExecutable == "u4.exe") {
+ auto FOVStringObfuscated = make_obfuscated<0x4A>("C5 FA ?? ?? ?? ?? ?? ?? C5 F2 ?? ?? ?? ?? ?? ?? C5 7A ?? ?? ?? ?? ?? ?? C5 7A");
+ FOVaddress = Memory::aob_scan(gameExecutable, FOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+ }
+ else {
+ auto FOVStringObfuscated = make_obfuscated<0x4A>("C5 FA ?? ?? ?? ?? ?? ?? C5 F2 ?? ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? C5 92");
+ 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));
+ }
+ }
+ if (PHOTOaddress == nullptr) {
+ auto PhotoFOVStringObfuscated = make_obfuscated<0x4A>("C5 FA ?? ?? ?? ?? ?? ?? C5 FA ?? ?? ?? ?? ?? ?? 41 ?? ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 83");
+ PHOTOaddress = Memory::aob_scan(gameExecutable, PhotoFOVStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!PHOTOaddress)
+ logger->warn("Photo FOV signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Photo FOV signature found at address: 0x{:X}.", reinterpret_cast(PHOTOaddress));
+ PHOTOaddress += 0x8;
+ }
+ }
+ if (CINEMATICSASPECTaddress == nullptr) {
+ constexpr auto CinematicsAspectStringObfuscated = make_obfuscated<0x4A>("48 8B ?? ?? ?? ?? ?? 4C 8B ?? ?? ?? ?? ?? ?? 4C 8B ?? 49 C1");
+ CINEMATICSASPECTaddress = Memory::aob_scan(gameExecutable, CinematicsAspectStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!CINEMATICSASPECTaddress)
+ logger->warn("Cinematics aspect ratio signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Cinematics aspect ratio signature found at address: 0x{:X}.", reinterpret_cast(CINEMATICSASPECTaddress));
+ }
+ }
+ if (Aspectaddress == nullptr) {
+ constexpr auto AspectStringObfuscated = make_obfuscated<0x4A>("44 8B ?? ?? ?? ?? ?? 48 ?? ?? 48 89 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? C5");
+ Aspectaddress = Memory::aob_scan(gameExecutable, AspectStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+
+ if (!Aspectaddress)
+ logger->warn("Aspect ratio signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ logger->info("Aspect ratio signature found at address: 0x{:X}.", reinterpret_cast(Aspectaddress));
+ Aspectaddress += 0x16;
+ }
+ }
+ if (DOFaddress == nullptr) {
+ constexpr auto DOFStringObfuscated = make_obfuscated<0x4A>("41 8B 87 ?? ?? ?? ?? 41 89 86 ?? ?? ?? ?? 41 C7 86");
+ 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 signature found at address: 0x{:X}.", reinterpret_cast(DOFaddress));
+ }
+ }
+ if (EFFECTSaddress == nullptr) {
+ if (gameExecutable == "u4.exe") {
+ auto EffectsStringObfuscated = make_obfuscated<0x4A>("41 8B 86 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 41 8B 86 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 41 8B 86 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 41 8B 87");
+ EFFECTSaddress = Memory::aob_scan(gameExecutable, EffectsStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+ }
+ else {
+ auto EffectsStringObfuscated = make_obfuscated<0x4A>("41 8B 87 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 41 8B 87 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 41 8B 87 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 41 8B 86");
+ EFFECTSaddress = Memory::aob_scan(gameExecutable, EffectsStringObfuscated.decrypt(), PAGE_EXECUTE_READ);
+ }
+
+ if (!EFFECTSaddress)
+ logger->warn("Effects signature not found. Maybe your game has been updated and is no more compatible with this plugin.");
+ else {
+ BDaddress_1 = EFFECTSaddress;
+ BDaddress_2 = EFFECTSaddress + 0xd;
+ CAaddress = EFFECTSaddress + 0x1a;
+ logger->info("Effects signature found at address: 0x{:X}, 0x{:X}, 0x{:X}.", reinterpret_cast(EFFECTSaddress), reinterpret_cast(BDaddress_2), reinterpret_cast(CAaddress));
+ }
+ if (FOVaddress && PHOTOaddress && CINEMATICSASPECTaddress && Aspectaddress && DOFaddress && EFFECTSaddress)
+ logger->info("All AOB signatures found. Ready to patch...");
+ }
+ if (FOVaddress && PHOTOaddress && CINEMATICSASPECTaddress && Aspectaddress && DOFaddress && EFFECTSaddress) AOBScanDone = true;
+ logger->info("--------------- AOB scanning finished ---------------");
+ }
+ if (g_fix_enabled) {
+ if (FOVaddress) FOVFixEnabled(g_fov_fix_enabled);
+ if (PHOTOaddress) PhotoFOVFixEnabled(g_photo_fix_enabled);
+ if (CINEMATICSASPECTaddress) CinematicsARFixEnabled(g_cinematics_fix_enabled);
+ if (Aspectaddress) AspectRatioFixEnabled(g_aspect_ratio_fix_enabled);
+ if (DOFaddress) DOFFixEnabled(g_DOF_fix_enabled);
+ if (CAaddress) CAFixEnabled(g_CA_fix_enabled);
+ if (BDaddress_1 && BDaddress_2) BDFixEnabled(g_BD_fix_enabled);
+ }
+ else {
+ if (FOVaddress) FOVFixEnabled(false);
+ if (PHOTOaddress) PhotoFOVFixEnabled(false);
+ if (CINEMATICSASPECTaddress) CinematicsARFixEnabled(false);
+ if (Aspectaddress) AspectRatioFixEnabled(false);
+ if (DOFaddress) DOFFixEnabled(false);
+ if (CAaddress) CAFixEnabled(false);
+ if (BDaddress_1 && BDaddress_2) BDFixEnabled(false);
+ logger->info("All fixes disabled.");
+ }
+}
+
+// Setters for Reshade addon call
+extern "C" __declspec(dllexport) void SetFOVFixEnabled(bool fix_enabled, bool init)
+{
+ g_fov_fix_enabled = fix_enabled;
+ if (!init) FOVFixEnabled(g_fov_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetPhotoFOVFixEnabled(bool fix_enabled, bool init)
+{
+ g_photo_fix_enabled = fix_enabled;
+ if (!init) PhotoFOVFixEnabled(g_photo_fix_enabled); // FOV fix must be enabled when aspect ratio is too to compensate FOV
+}
+
+extern "C" __declspec(dllexport) void SetCinematicsARFixEnabled(bool fix_enabled, bool init)
+{
+ g_cinematics_fix_enabled = fix_enabled;
+ if (!init) CinematicsARFixEnabled(g_cinematics_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetAspectRatioFixEnabled(bool fix_enabled, bool init)
+{
+ g_aspect_ratio_fix_enabled = fix_enabled;
+ if (!init) AspectRatioFixEnabled(g_aspect_ratio_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetDOFFixEnabled(bool fix_enabled, bool init)
+{
+ g_DOF_fix_enabled = fix_enabled;
+ if (!init) DOFFixEnabled(g_DOF_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetCAFixEnabled(bool fix_enabled, bool init)
+{
+ g_CA_fix_enabled = fix_enabled;
+ if (!init) CAFixEnabled(g_CA_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetBDFixEnabled(bool fix_enabled, bool init)
+{
+ g_BD_fix_enabled = fix_enabled;
+ if (!init) BDFixEnabled(g_BD_fix_enabled);
+}
+
+extern "C" __declspec(dllexport) void SetFOV(int fov)
+{
+ g_AdditionalValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetPhotoFOV(int fov)
+{
+ g_AdditionalPhotoValue = fov;
+}
+
+extern "C" __declspec(dllexport) void SetAspect(float aspect)
+{
+ g_AspectValue = aspect;
+}
+
+// Getters for Reshade addon call
+extern "C" __declspec(dllexport) float GetFOVIn() {
+ return g_FOV_In;
+}
+
+extern "C" __declspec(dllexport) float GetFOVOut() {
+ return g_FOV_Out;
+}
+
+static void FOVFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && FOVaddress) {
+ if (!FOVHook) { // Hook only once
+ FOVHook = safetyhook::create_mid(FOVaddress,
+ [](SafetyHookContext& ctx) {
+ g_FOV_In = ctx.xmm0.f32[0];
+ g_FOV_Out = ctx.xmm0.f32[0] += g_AdditionalValue;
+ });
+ }
+ else FOVHook.enable();
+ logger->info("FOV fix enabled");
+ }
+ if (!fix_enabled && FOVaddress && FOVHook) {
+ FOVHook.disable();
+ logger->info("FOV fix disabled");
+ }
+}
+
+static void PhotoFOVFixEnabled (bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && PHOTOaddress) {
+ if (!PhotoFOVHook) { // Hook only once
+ PhotoFOVHook = safetyhook::create_mid(PHOTOaddress,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm0.f32[0] += g_AdditionalPhotoValue - (g_fov_fix_enabled ? g_AdditionalValue : 0);
+ });
+ }
+ else {
+ PhotoFOVHook.enable();
+ }
+ logger->info("Photo FOV fix enabled");
+ }
+ if (!fix_enabled && PHOTOaddress && PhotoFOVHook) {
+ //if (PhotoFOVHook)
+ PhotoFOVHook.disable();
+ logger->info("Photo FOV fix disabled");
+ }
+}
+
+static void CinematicsARFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && CINEMATICSASPECTaddress) {
+ if (originalBytes.empty()) {
+ originalBytes.resize(1);
+ std::copy_n(CINEMATICSASPECTaddress + 3, 1, originalBytes.begin()); // Copy 1 byte of original offset
+ originalBytes[0] -= 0x10; // Decrease it by 0x10
+ Memory::PatchBytes(CINEMATICSASPECTaddress + 3, reinterpret_cast(originalBytes.data()), 1); // mov rcx,[u4.exe+653E080 - 0x10]
+ logger->info("Cinematics aspect ratio fix enabled");
+ }
+ }
+ if (!fix_enabled && CINEMATICSASPECTaddress) {
+ originalBytes.clear();
+ Memory::RestoreBytes(CINEMATICSASPECTaddress + 3);
+ logger->info("Cinematics aspect ratio fix disabled");
+ }
+}
+
+static void AspectRatioFixEnabled(bool fix_enabled) { // OK ?
+ if (g_fix_enabled && fix_enabled && Aspectaddress) {
+ if (!AspectRatioHook) {
+ AspectRatioHook = safetyhook::create_mid(Aspectaddress,
+ [](SafetyHookContext& ctx) {
+ ctx.xmm0.f32[0] = g_AspectValue;
+ });
+ }
+ else {
+ AspectRatioHook.enable();
+ }
+ logger->info("Aspect ratio fix enabled");
+ }
+ if (!fix_enabled && Aspectaddress && AspectRatioHook) {
+ AspectRatioHook.disable();
+ logger->info("Aspect ratio fix disabled");
+ }
+}
+
+static void DOFFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && DOFaddress) {
+ Memory::PatchBytes(DOFaddress, "\xB8\x00\x00\x80\x7F\x90\x90", 7); // mov eax,0x7F800000
+ logger->info("Depth of field fix enabled");
+ }
+ if (!fix_enabled && DOFaddress) {
+ Memory::RestoreBytes(DOFaddress);
+ logger->info("Depth of field fix disabled");
+ }
+}
+
+static void CAFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && CAaddress) {
+ Memory::PatchBytes(CAaddress, "\x31\xC0\x90\x90\x90\x90\x90", 7); // xor eax,eax No chromatic aberrations
+ logger->info("Chromatic aberrations fix enabled");
+ }
+ if (!fix_enabled && CAaddress) {
+ Memory::RestoreBytes(CAaddress);
+ logger->info("Chromatic aberrations fix disabled");
+ }
+}
+
+static void BDFixEnabled(bool fix_enabled) {
+ if (g_fix_enabled && fix_enabled && BDaddress_1 && BDaddress_2) {
+ Memory::PatchBytes(BDaddress_1, "\x31\xC0\x90\x90\x90\x90\x90", 7); // xor eax,eax No barrel distortions
+ Memory::PatchBytes(BDaddress_2, "\xB8\x00\x00\x80\x3F\x90\x90", 7); // mov eax,0x3f800000 No barrel distortions
+ logger->info("Barrel distortion fix enabled");
+ }
+ if (!fix_enabled && BDaddress_1) {
+ Memory::RestoreBytes(BDaddress_1);
+ Memory::RestoreBytes(BDaddress_2);
+ logger->info("Barrel distortion fix disabled");
+ }
+}
+
+// Initialisation de spdlog avec format personnalisé
+static void InitializeLogger()
+{
+ try
+ {
+ logger = spdlog::basic_logger_mt("Fixlib", PLUGIN_LOG, true);
+ spdlog::set_default_logger(logger);
+ // Format : [YYYY-MM-DD HH:MM:SS] [INFO] message
+ spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
+ spdlog::set_level(spdlog::level::debug);
+ 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);
+ }
+}
+
+// Entrée standard DLL
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitializeLogger();
+ logger->info("Plugin {} loaded.", PLUGIN_NAME);
+ }
+ else if (reason == DLL_PROCESS_DETACH)
+ {
+ //CinematicsDOFFixEnabled(false); // Core is loaded 3 times so i need to restore previous patched bytes
+ logger->info("Plugin {} unloaded.", PLUGIN_NAME);
+ spdlog::drop_all();
+ }
+ return TRUE;
+}
\ No newline at end of file
diff --git a/external/safetyhook/.clang-format b/external/safetyhook/.clang-format
new file mode 100644
index 0000000..cbe96e2
--- /dev/null
+++ b/external/safetyhook/.clang-format
@@ -0,0 +1,14 @@
+---
+BasedOnStyle: LLVM
+IndentWidth: 4
+ColumnLimit: 120
+---
+Language: Cpp
+AccessModifierOffset: -4
+AlignAfterOpenBracket: DontAlign
+AlignEscapedNewlines: Left
+AllowShortFunctionsOnASingleLine: InlineOnly
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+DerivePointerAlignment: false
+PointerAlignment: Left
+...
diff --git a/external/safetyhook/.github/workflows/build.yml b/external/safetyhook/.github/workflows/build.yml
new file mode 100644
index 0000000..4d67ee1
--- /dev/null
+++ b/external/safetyhook/.github/workflows/build.yml
@@ -0,0 +1,85 @@
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ tags:
+ - v*
+
+jobs:
+ build:
+ runs-on: windows-2022
+ strategy:
+ matrix:
+ arch: [ x64, Win32 ]
+ build_type: [ Debug, Release ]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4.1.1
+
+ - name: Configure
+ run: |
+ mkdir build
+ cd build
+ cmake -A ${{matrix.arch}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSAFETYHOOK_FETCH_ZYDIS=ON -DSAFETYHOOK_BUILD_EXAMPLES=ON -DSAFETYHOOK_BUILD_TEST=ON -DSAFETYHOOK_AMALGAMATE=ON ..
+
+ - name: Build
+ run: |
+ cmake --build build --config ${{matrix.build_type}} --parallel
+
+ - name: Test
+ run: |
+ ./build/${{matrix.build_type}}/test.exe
+ ./build/${{matrix.build_type}}/test-amalgamated.exe
+
+ amalgamated-dist:
+ runs-on: windows-2022
+ needs: build
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4.1.1
+
+ - name: Amalgamate
+ run: |
+ python3 amalgamate.py
+
+ - name: Compress amalgamated release
+ uses: vimtor/action-zip@v1.1
+ with:
+ files: amalgamated-dist/
+ dest: safetyhook-amalgamated.zip
+
+ - name: Upload amalgamate
+ uses: actions/upload-artifact@v4.3.0
+ with:
+ name: safetyhook-amalgamated
+ path: amalgamated-dist/
+
+ - name: Download & extract Zydis
+ run: |
+ Invoke-WebRequest -Uri https://github.com/zyantific/zydis/releases/download/v4.0.0/zydis-amalgamated.zip -OutFile zydis.zip
+ Expand-Archive .\zydis.zip .
+ Remove-Item .\zydis.zip
+ @("#define ZYCORE_STATIC_BUILD", "#define ZYDIS_STATIC_BUILD") + (Get-Content -Raw .\amalgamated-dist\Zydis.h) | Set-Content -Encoding utf8 .\amalgamated-dist\Zydis.h
+
+ - name: Compress amalgamated release with Zydis
+ uses: vimtor/action-zip@v1.1
+ with:
+ files: amalgamated-dist/
+ dest: safetyhook-amalgamated-zydis.zip
+
+ - name: Upload amalgamate with Zydis
+ uses: actions/upload-artifact@v4.3.0
+ with:
+ name: safetyhook-amalgamated-zydis
+ path: amalgamated-dist/
+
+ - name: Release
+ uses: softprops/action-gh-release@v0.1.15
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ prerelease: ${{contains(github.ref, '-pre')}}
+ files: |
+ safetyhook-amalgamated.zip
+ safetyhook-amalgamated-zydis.zip
diff --git a/external/safetyhook/.gitignore b/external/safetyhook/.gitignore
new file mode 100644
index 0000000..3b215d6
--- /dev/null
+++ b/external/safetyhook/.gitignore
@@ -0,0 +1,10 @@
+build
+build32
+cmake-build*
+.idea
+.vscode
+.vs/
+docs/html
+docs/latex
+docs/Doxyfile
+amalgamated-dist
\ No newline at end of file
diff --git a/external/safetyhook/CMakeLists.txt b/external/safetyhook/CMakeLists.txt
new file mode 100644
index 0000000..eb51e40
--- /dev/null
+++ b/external/safetyhook/CMakeLists.txt
@@ -0,0 +1,500 @@
+# This file is automatically generated from cmake.toml - DO NOT EDIT
+# See https://github.com/build-cpp/cmkr for more information
+
+cmake_minimum_required(VERSION 3.15)
+
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+ message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
+endif()
+
+set(CMKR_ROOT_PROJECT OFF)
+if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
+ set(CMKR_ROOT_PROJECT ON)
+
+ # Bootstrap cmkr and automatically regenerate CMakeLists.txt
+ include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
+ if(CMKR_INCLUDE_RESULT)
+ cmkr()
+ endif()
+
+ # Enable folder support
+ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+ # Create a configure-time dependency on cmake.toml to improve IDE support
+ configure_file(cmake.toml cmake.toml COPYONLY)
+endif()
+
+# Options
+option(SAFETYHOOK_BUILD_DOCS "" OFF)
+option(SAFETYHOOK_BUILD_TEST "" OFF)
+option(SAFETYHOOK_BUILD_EXAMPLES "" OFF)
+option(SAFETYHOOK_AMALGAMATE "" OFF)
+option(SAFETYHOOK_FETCH_ZYDIS "" ON)
+option(SAFETYHOOK_USE_CXXMODULES "" OFF)
+
+project(safetyhook)
+
+include(FetchContent)
+
+if(SAFETYHOOK_BUILD_TEST) # build-test
+ message(STATUS "Fetching ut (v2.0.1)...")
+ FetchContent_Declare(ut
+ GIT_REPOSITORY
+ "https://github.com/boost-ext/ut.git"
+ GIT_TAG
+ v2.0.1
+ GIT_SHALLOW
+ ON
+ EXCLUDE_FROM_ALL
+ ON
+ )
+ FetchContent_MakeAvailable(ut)
+
+endif()
+if(SAFETYHOOK_BUILD_TEST) # build-test
+ message(STATUS "Fetching xbyak (v6.69)...")
+ FetchContent_Declare(xbyak
+ GIT_REPOSITORY
+ "https://github.com/herumi/xbyak.git"
+ GIT_TAG
+ v6.69
+ GIT_SHALLOW
+ ON
+ EXCLUDE_FROM_ALL
+ ON
+ )
+ FetchContent_MakeAvailable(xbyak)
+
+endif()
+if(SAFETYHOOK_FETCH_ZYDIS) # fetch-zydis
+ option(ZYDIS_BUILD_EXAMPLES "" OFF)
+ option(ZYDIS_BUILD_TOOLS "" OFF)
+ option(ZYDIS_BUILD_DOXYGEN "" OFF)
+
+ message(STATUS "Fetching Zydis (v4.1.0)...")
+ FetchContent_Declare(Zydis
+ GIT_REPOSITORY
+ "https://github.com/zyantific/zydis.git"
+ GIT_TAG
+ v4.1.0
+ GIT_SHALLOW
+ ON
+ EXCLUDE_FROM_ALL
+ ON
+ )
+ FetchContent_MakeAvailable(Zydis)
+
+endif()
+# Packages
+if(SAFETYHOOK_BUILD_DOCS) # build-docs
+ find_package(Doxygen REQUIRED)
+
+endif()
+if(SAFETYHOOK_AMALGAMATE) # amalgamate
+ find_package(Python3 REQUIRED)
+
+endif()
+if((NOT SAFETYHOOK_FETCH_ZYDIS)) # find-zydis
+ find_package(Zydis REQUIRED CONFIG)
+
+endif()
+# Target: safetyhook
+set(safetyhook_SOURCES
+ "src/allocator.cpp"
+ "src/easy.cpp"
+ "src/inline_hook.cpp"
+ "src/mid_hook.cpp"
+ "src/os.linux.cpp"
+ "src/os.windows.cpp"
+ "src/utility.cpp"
+ "src/vmt_hook.cpp"
+ cmake.toml
+)
+
+add_library(safetyhook)
+
+target_sources(safetyhook PRIVATE ${safetyhook_SOURCES})
+source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${safetyhook_SOURCES})
+
+add_library(safetyhook::safetyhook ALIAS safetyhook)
+if(BUILD_SHARED_LIBS) # shared
+ target_compile_definitions(safetyhook PUBLIC
+ SAFETYHOOK_SHARED_LIB
+ )
+endif()
+
+if(BUILD_SHARED_LIBS) # shared
+ target_compile_definitions(safetyhook PRIVATE
+ SAFETYHOOK_BUILDING
+ )
+endif()
+
+target_compile_features(safetyhook PUBLIC
+ cxx_std_23
+)
+
+if(MSVC) # msvc
+ target_compile_options(safetyhook PRIVATE
+ "/permissive-"
+ "/W4"
+ "/w14640"
+ "/wd4251"
+ )
+endif()
+
+if((CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$") OR (CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_C_COMPILER_FRONTEND_VARIANT MATCHES "^MSVC$")) # clang
+ target_compile_options(safetyhook PRIVATE
+ -Wall
+ -Wextra
+ -Wshadow
+ -Wnon-virtual-dtor
+ -pedantic
+ )
+endif()
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU") # gcc
+ target_compile_options(safetyhook PRIVATE
+ -Wall
+ -Wextra
+ -Wshadow
+ -Wnon-virtual-dtor
+ -pedantic
+ )
+endif()
+
+target_include_directories(safetyhook PUBLIC
+ "$"
+ "$"
+)
+
+target_link_libraries(safetyhook PUBLIC
+ Zydis
+)
+
+set(CMKR_TARGET safetyhook)
+if(SAFETYHOOK_USE_CXXMODULES)
+ target_compile_definitions(safetyhook INTERFACE SAFETYHOOK_USE_CXXMODULES)
+endif()
+
+include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/safetyhook-config.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/safetyhook-config.cmake
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/safetyhook
+)
+
+install(
+ TARGETS safetyhook
+ EXPORT safetyhook-targets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+install(
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+install(
+ EXPORT safetyhook-targets
+ FILE safetyhook-targets.cmake
+ NAMESPACE safetyhook::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/safetyhook
+)
+
+install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/safetyhook-config.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/safetyhook
+)
+
+# Target: docs
+if(SAFETYHOOK_BUILD_DOCS) # build-docs
+ set(CMKR_TARGET docs)
+ file(GLOB_RECURSE HEADER_FILES include/*.hpp)
+ set(DOXYGEN_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/docs/html/index.html)
+ set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in)
+ set(DOXYFILE_OUT ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile)
+ configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT})
+ add_custom_command(
+ OUTPUT ${DOXYGEN_INDEX_FILE}
+ DEPENDS ${HEADER_FILES}
+ COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
+ MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
+ COMMENT "Generating docs"
+ )
+ add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE})
+
+ set(docs_SOURCES
+ cmake.toml
+ )
+
+ add_custom_target(docs SOURCES)
+
+ target_sources(docs PRIVATE ${docs_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${docs_SOURCES})
+
+endif()
+# Target: amalgamation
+if(SAFETYHOOK_AMALGAMATE) # amalgamate
+ set(CMKR_TARGET amalgamation)
+ file(GLOB_RECURSE HEADER_FILES include/*.hpp)
+ file(GLOB_RECURSE SOURCE_FILES src/*.cpp)
+ set(AMALGAMATED_FILE ${CMAKE_CURRENT_SOURCE_DIR}/amalgamated-dist/safetyhook.cpp)
+ set(AMALGAMATED_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/amalgamated-dist/safetyhook.hpp)
+ set(AMALGAMATE_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate.py)
+ add_custom_command(
+ OUTPUT ${AMALGAMATED_FILE} ${AMALGAMATED_HEADER}
+ DEPENDS ${HEADER_FILES} ${SOURCE_FILES} ${AMALGAMATE_SCRIPT}
+ COMMAND ${Python3_EXECUTABLE} ${AMALGAMATE_SCRIPT}
+ MAIN_DEPENDENCY ${AMALGAMATE_SCRIPT}
+ COMMENT "Amalgamating"
+ )
+ add_custom_target(Amalgamate ALL DEPENDS ${AMALGAMATED_FILE} ${AMALGAMATED_HEADER})
+
+ set(amalgamation_SOURCES
+ cmake.toml
+ )
+
+ add_custom_target(amalgamation SOURCES)
+
+ target_sources(amalgamation PRIVATE ${amalgamation_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${amalgamation_SOURCES})
+
+endif()
+# Target: example-minimal
+if(SAFETYHOOK_BUILD_EXAMPLES) # build-examples
+ set(example-minimal_SOURCES
+ "example/minimal.cpp"
+ cmake.toml
+ )
+
+ add_executable(example-minimal)
+
+ target_sources(example-minimal PRIVATE ${example-minimal_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example-minimal_SOURCES})
+
+ target_compile_features(example-minimal PRIVATE
+ cxx_std_23
+ )
+
+ target_link_libraries(example-minimal PRIVATE
+ safetyhook::safetyhook
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example-minimal)
+ endif()
+
+endif()
+# Target: example-multiple
+if(SAFETYHOOK_BUILD_EXAMPLES) # build-examples
+ set(example-multiple_SOURCES
+ "example/multiple.cpp"
+ cmake.toml
+ )
+
+ add_executable(example-multiple)
+
+ target_sources(example-multiple PRIVATE ${example-multiple_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example-multiple_SOURCES})
+
+ target_compile_features(example-multiple PRIVATE
+ cxx_std_23
+ )
+
+ target_link_libraries(example-multiple PRIVATE
+ safetyhook::safetyhook
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example-multiple)
+ endif()
+
+endif()
+# Target: example-midhook
+if(SAFETYHOOK_BUILD_EXAMPLES) # build-examples
+ set(example-midhook_SOURCES
+ "example/midhook.cpp"
+ cmake.toml
+ )
+
+ add_executable(example-midhook)
+
+ target_sources(example-midhook PRIVATE ${example-midhook_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example-midhook_SOURCES})
+
+ target_compile_features(example-midhook PRIVATE
+ cxx_std_23
+ )
+
+ target_link_libraries(example-midhook PRIVATE
+ safetyhook::safetyhook
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example-midhook)
+ endif()
+
+endif()
+# Target: example-threadsafe
+if(SAFETYHOOK_BUILD_EXAMPLES) # build-examples
+ set(example-threadsafe_SOURCES
+ "example/threadsafe.cpp"
+ cmake.toml
+ )
+
+ add_executable(example-threadsafe)
+
+ target_sources(example-threadsafe PRIVATE ${example-threadsafe_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example-threadsafe_SOURCES})
+
+ target_compile_features(example-threadsafe PRIVATE
+ cxx_std_23
+ )
+
+ target_link_libraries(example-threadsafe PRIVATE
+ safetyhook::safetyhook
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example-threadsafe)
+ endif()
+
+endif()
+# Target: example-dll
+if(SAFETYHOOK_BUILD_EXAMPLES AND WIN32) # windows-only-example
+ set(example-dll_SOURCES
+ "example/dll.cpp"
+ cmake.toml
+ )
+
+ add_library(example-dll SHARED)
+
+ target_sources(example-dll PRIVATE ${example-dll_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example-dll_SOURCES})
+
+ target_compile_features(example-dll PUBLIC
+ cxx_std_23
+ )
+
+ target_link_libraries(example-dll PUBLIC
+ safetyhook::safetyhook
+ )
+
+endif()
+# Target: example-vmthook
+if(SAFETYHOOK_BUILD_EXAMPLES) # build-examples
+ set(example-vmthook_SOURCES
+ "example/vmthook.cpp"
+ cmake.toml
+ )
+
+ add_executable(example-vmthook)
+
+ target_sources(example-vmthook PRIVATE ${example-vmthook_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example-vmthook_SOURCES})
+
+ target_compile_features(example-vmthook PRIVATE
+ cxx_std_23
+ )
+
+ target_link_libraries(example-vmthook PRIVATE
+ safetyhook::safetyhook
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example-vmthook)
+ endif()
+
+endif()
+# Target: test
+if(SAFETYHOOK_BUILD_TEST) # build-test
+ set(test_SOURCES
+ "test/allocator.cpp"
+ "test/inline_hook.cpp"
+ "test/inline_hook.x86_64.cpp"
+ "test/main.cpp"
+ "test/mid_hook.cpp"
+ "test/vmt_hook.cpp"
+ cmake.toml
+ )
+
+ add_executable(test)
+
+ target_sources(test PRIVATE ${test_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${test_SOURCES})
+
+ target_compile_definitions(test PRIVATE
+ BOOST_UT_DISABLE_MODULE
+ )
+
+ target_compile_features(test PRIVATE
+ cxx_std_23
+ )
+
+ target_link_libraries(test PRIVATE
+ Boost::ut
+ safetyhook::safetyhook
+ xbyak::xbyak
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT test)
+ endif()
+
+endif()
+# Target: test-amalgamated
+if(SAFETYHOOK_BUILD_TEST AND SAFETYHOOK_AMALGAMATE) # build-amalgamate-test
+ set(test-amalgamated_SOURCES
+ "test/allocator.cpp"
+ "test/inline_hook.cpp"
+ "test/inline_hook.x86_64.cpp"
+ "test/main.cpp"
+ "test/mid_hook.cpp"
+ "test/vmt_hook.cpp"
+ "amalgamated-dist/safetyhook.cpp"
+ cmake.toml
+ )
+
+ add_executable(test-amalgamated)
+
+ target_sources(test-amalgamated PRIVATE ${test-amalgamated_SOURCES})
+ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${test-amalgamated_SOURCES})
+
+ target_compile_definitions(test-amalgamated PRIVATE
+ BOOST_UT_DISABLE_MODULE
+ )
+
+ target_compile_features(test-amalgamated PRIVATE
+ cxx_std_23
+ )
+
+ target_include_directories(test-amalgamated PRIVATE
+ "amalgamated-dist/"
+ )
+
+ target_link_libraries(test-amalgamated PRIVATE
+ Zydis
+ Boost::ut
+ xbyak::xbyak
+ )
+
+ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
+ if(NOT CMKR_VS_STARTUP_PROJECT)
+ set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT test-amalgamated)
+ endif()
+
+ set(CMKR_TARGET test-amalgamated)
+ add_dependencies(test-amalgamated Amalgamate)
+
+endif()
diff --git a/external/safetyhook/LICENSE b/external/safetyhook/LICENSE
new file mode 100644
index 0000000..127a5bc
--- /dev/null
+++ b/external/safetyhook/LICENSE
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/external/safetyhook/README.md b/external/safetyhook/README.md
new file mode 100644
index 0000000..dd958d3
--- /dev/null
+++ b/external/safetyhook/README.md
@@ -0,0 +1,71 @@
+# SafetyHook
+
+SafetyHook is a **C++23** procedure hooking library for Windows x86 and x86_64 systems. It aims to make runtime procedure hooking as safe as possible while maintaining simplicity of its implementation. To that end it currently does:
+
+* Stops all other threads when creating or deleting hooks
+* Fixes the IP of threads that may be affected by the creation or deletion of hooks
+* Fixes IP relative displacements of relocated instructions (eg. `lea rax, [rip + 0x1234]`)
+* Fixes relative offsets of relocated instructions (eg. `jmp 0x1234`)
+* Widens short branches into near branches
+* Handles short branches that land within the trampoline
+* Uses a modern disassembler engine that supports the latest instructions
+* Has a carefully designed API that is hard to misuse
+
+## Installation
+
+SafetyHook can be added via CMake's `FetchContent`, git submodules, or copied directly into your project using the amalgamated builds. SafetyHook requires [Zydis](https://github.com/zyantific/zydis) to function.
+
+### Amalgamated builds
+
+This is the easiest way to use safety hook. You can find amalgamated builds on the releases page. Simply download the ZIP file containing [Zydis](https://github.com/zyantific/zydis) (or without [Zydis](https://github.com/zyantific/zydis) if you already have it in your project) and copy the files into your project.
+
+You may need to define `ZYDIS_STATIC_BUILD` if you're using the build with [Zydis](https://github.com/zyantific/zydis) included.
+
+### CMake FetchContent
+
+```CMake
+include(FetchContent)
+
+# Safetyhook
+FetchContent_Declare(
+ safetyhook
+ GIT_REPOSITORY "https://github.com/cursey/safetyhook.git"
+ GIT_TAG "origin/main"
+)
+FetchContent_MakeAvailable(safetyhook)
+```
+
+If you want SafetyHook to fetch [Zydis](https://github.com/zyantific/zydis) you must enable the CMake option `-DSAFETYHOOK_FETCH_ZYDIS=ON`.
+
+## Usage
+
+```C++
+#include
+
+#include
+
+__declspec(noinline) int add(int x, int y) {
+ return x + y;
+}
+
+SafetyHookInline g_add_hook{};
+
+int hook_add(int x, int y) {
+ return g_add_hook.call(x * 2, y * 2);
+}
+
+int main() {
+ std::cout << "unhooked add(2, 3) = " << add(2, 3) << "\n";
+
+ // Create a hook on add (This uses SafetyHook's easy API).
+ g_add_hook = safetyhook::create_inline(reinterpret_cast(add), reinterpret_cast(hook_add));
+
+ std::cout << "hooked add(3, 4) = " << add(3, 4) << "\n";
+
+ g_add_hook = {};
+
+ std::cout << "unhooked add(5, 6) = " << add(5, 6) << "\n";
+
+ return 0;
+}
+```
diff --git a/external/safetyhook/amalgamate.py b/external/safetyhook/amalgamate.py
new file mode 100644
index 0000000..5828eb0
--- /dev/null
+++ b/external/safetyhook/amalgamate.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python3
+
+from pathlib import Path
+from typing import List, Set
+from glob import glob
+from shutil import rmtree
+from textwrap import dedent
+
+import os
+import re
+import sys
+import argparse
+
+SAFETYHOOK_ROOT = Path(__file__).resolve().parent
+PUBLIC_INCLUDE_PATHS = [
+ SAFETYHOOK_ROOT / 'include',
+ SAFETYHOOK_ROOT / 'include' / 'safetyhook',
+]
+INTERNAL_INCLUDE_PATHS = [SAFETYHOOK_ROOT / 'src']
+INCLUDE_REGEXP = re.compile(r'^#\s*include\s*"((?:safety).*)"\s*$')
+OUTPUT_DIR = SAFETYHOOK_ROOT / 'amalgamated-dist'
+FILE_HEADER = ['// DO NOT EDIT. This file is auto-generated by `amalgamate.py`.', '']
+
+parser = argparse.ArgumentParser(description='bundles cpp and hpp files together')
+parser.add_argument('--polyfill', action='store_true',
+ help='Replace std::expected with a polyfill so it can be compiled on C++20 or older. https://raw.githubusercontent.com/TartanLlama/expected/master/include/tl/expected.hpp')
+
+
+# Python versions before 3.10 don't have the root_dir argument for glob, so we
+# crudely emulate it here.
+def glob_in_dir(
+ pattern: str,
+ root_dir: Path,
+):
+ cwd = os.getcwd()
+ root_dir = root_dir.resolve()
+ os.chdir(root_dir)
+ try:
+ for path in glob(pattern, recursive=True):
+ yield Path(root_dir) / path
+ finally:
+ os.chdir(cwd)
+
+
+def find_include_path(
+ include: str,
+ search_paths: List[Path],
+) -> Path:
+ for search_path in search_paths:
+ path = search_path / include
+ if path.exists():
+ return path.absolute()
+ else:
+ raise FileNotFoundError(f'can\'t find header: {include}')
+
+
+def merge_headers(
+ *,
+ header: str,
+ search_paths: List[Path],
+ covered_headers: Set[Path],
+ stack: List[str],
+) -> List[str]:
+ # Locate and load header contents.
+ path = find_include_path(header, search_paths)
+ with path.open() as f:
+ lines = [x.rstrip() for x in f]
+
+ if path in covered_headers:
+ return []
+
+ print(f'Processing header "{header}"')
+ covered_headers.add(path)
+
+ # Print the header we emit next & the include stack (if non-root).
+ include_stack = []
+ if stack:
+ include_stack = [
+ '//',
+ '// Include stack:',
+ *(f'// - {x}' for x in stack)
+ ]
+
+ filtered = [
+ f'',
+ f'//',
+ f'// Header: {header}',
+ *include_stack,
+ f'//',
+ f'',
+ ]
+
+ # Copy over lines and recursively inline all headers.
+ for line in lines:
+ match = INCLUDE_REGEXP.match(line)
+ if not match:
+ filtered.append(line)
+ continue
+
+ # Recurse into includes.
+ filtered += merge_headers(
+ header=match.group(1),
+ search_paths=search_paths,
+ covered_headers=covered_headers,
+ stack=stack + [header],
+ )
+
+ return filtered
+
+
+def merge_sources(*, source_dir: Path, covered_headers: Set[Path]):
+ output = [
+ '#define NOMINMAX',
+ '',
+ '#include "safetyhook.hpp"',
+ '',
+ ]
+
+ for source_file in glob_in_dir('**/*.cpp', source_dir):
+ print(f'Processing source file "{source_file}"')
+
+ # Print some comments to show where the code is from.
+ output += [
+ f'',
+ f'//',
+ f'// Source file: {source_file.relative_to(source_dir)}',
+ f'//',
+ f'',
+ ]
+
+ # Read source file.
+ with (source_dir / source_file).open() as f:
+ lines = [x.rstrip() for x in f]
+
+ # Walk source file's lines.
+ for line in lines:
+ # Emit non-includes as-is.
+ match = INCLUDE_REGEXP.match(line)
+ if not match:
+ output.append(line)
+ continue
+ path = match.group(1)
+
+ if path in covered_headers:
+ continue
+
+ if 'Internal' not in path and 'Generated' not in path:
+ print(
+ f'WARN: Including header that looks like it is public '
+ f'and should thus already be covered by `safetyhook.h` '
+ f'during processing of source files: {path}'
+ )
+
+ print(f'Processing internal header "{path}"')
+ output += merge_headers(
+ header=path,
+ search_paths=PUBLIC_INCLUDE_PATHS + INTERNAL_INCLUDE_PATHS,
+ covered_headers=covered_headers,
+ stack=[],
+ )
+
+ return output
+
+
+def do_polyfill(content):
+ return content.replace('#include ',
+ dedent('''
+ #if __has_include("tl/expected.hpp")
+ #include "tl/expected.hpp"
+ #elif __has_include("expected.hpp")
+ #include "expected.hpp"
+ #else
+ #error "No polyfill found"
+ #endif
+ ''')) \
+ .replace('std::expected', 'tl::expected') \
+ .replace('std::unexpected', 'tl::unexpected')
+
+
+def main():
+ args = parser.parse_args()
+ polyfill = args.polyfill is True
+
+ if OUTPUT_DIR.exists():
+ print('Output directory exists. Deleting.')
+ rmtree(OUTPUT_DIR)
+
+ OUTPUT_DIR.mkdir()
+
+ covered_headers = set()
+ with open(OUTPUT_DIR / 'safetyhook.hpp', 'w') as f:
+ content = '\n'.join(FILE_HEADER + merge_headers(
+ header='safetyhook.hpp',
+ search_paths=PUBLIC_INCLUDE_PATHS,
+ covered_headers=covered_headers,
+ stack=[],
+ ))
+ if polyfill:
+ content = do_polyfill(content)
+ f.write(content)
+
+ print(covered_headers)
+
+ with open(OUTPUT_DIR / 'safetyhook.cpp', 'w') as f:
+ content = '\n'.join(FILE_HEADER + merge_sources(
+ source_dir=SAFETYHOOK_ROOT / 'src',
+ covered_headers=covered_headers,
+ ))
+ if polyfill:
+ content = do_polyfill(content)
+ f.write(content)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/external/safetyhook/cmake.toml b/external/safetyhook/cmake.toml
new file mode 100644
index 0000000..14eb289
--- /dev/null
+++ b/external/safetyhook/cmake.toml
@@ -0,0 +1,211 @@
+# Reference: https://build-cpp.github.io/cmkr/cmake-toml
+[project]
+name = "safetyhook"
+
+[options]
+SAFETYHOOK_BUILD_DOCS = false
+SAFETYHOOK_BUILD_TEST = false
+SAFETYHOOK_BUILD_EXAMPLES = false
+SAFETYHOOK_AMALGAMATE = false
+SAFETYHOOK_FETCH_ZYDIS = true
+SAFETYHOOK_USE_CXXMODULES = false
+
+[conditions]
+build-docs = "SAFETYHOOK_BUILD_DOCS"
+build-test = "SAFETYHOOK_BUILD_TEST"
+build-examples = "SAFETYHOOK_BUILD_EXAMPLES"
+shared = "BUILD_SHARED_LIBS"
+windows-only-example = "SAFETYHOOK_BUILD_EXAMPLES AND WIN32"
+amalgamate = "SAFETYHOOK_AMALGAMATE"
+build-amalgamate-test = "SAFETYHOOK_BUILD_TEST AND SAFETYHOOK_AMALGAMATE"
+fetch-zydis = "SAFETYHOOK_FETCH_ZYDIS"
+find-zydis = "(NOT SAFETYHOOK_FETCH_ZYDIS)"
+
+[fetch-content.ut]
+condition = "build-test"
+git = "https://github.com/boost-ext/ut.git"
+tag = "v2.0.1"
+shallow = true
+EXCLUDE_FROM_ALL = true
+
+[fetch-content.xbyak]
+condition = "build-test"
+git = "https://github.com/herumi/xbyak.git"
+tag = "v6.69"
+shallow = true
+EXCLUDE_FROM_ALL = true
+
+[fetch-content.Zydis]
+condition = "fetch-zydis"
+git = "https://github.com/zyantific/zydis.git"
+tag = "v4.1.0"
+shallow = true
+cmake-before = """
+option(ZYDIS_BUILD_EXAMPLES "" OFF)
+option(ZYDIS_BUILD_TOOLS "" OFF)
+option(ZYDIS_BUILD_DOXYGEN "" OFF)
+"""
+EXCLUDE_FROM_ALL = true
+
+[find-package.Doxygen]
+condition = "build-docs"
+required = true
+
+[find-package.Python3]
+condition = "amalgamate"
+required = true
+
+[find-package.Zydis]
+condition = "find-zydis"
+config = true
+required = true
+
+[target.safetyhook]
+type = "library"
+sources = ["src/*.cpp"]
+include-directories = [
+ "$",
+ "$"
+]
+compile-features = ["cxx_std_23"]
+alias = "safetyhook::safetyhook"
+link-libraries = ["Zydis"]
+msvc.private-compile-options = ["/permissive-", "/W4", "/w14640", "/wd4251"]
+clang.private-compile-options = ["-Wall", "-Wextra", "-Wshadow", "-Wnon-virtual-dtor", "-pedantic"]
+gcc.private-compile-options = ["-Wall", "-Wextra", "-Wshadow", "-Wnon-virtual-dtor", "-pedantic"]
+shared.compile-definitions = ["SAFETYHOOK_SHARED_LIB"]
+shared.private-compile-definitions = ["SAFETYHOOK_BUILDING"]
+cmake-after = """
+if(SAFETYHOOK_USE_CXXMODULES)
+ target_compile_definitions(safetyhook INTERFACE SAFETYHOOK_USE_CXXMODULES)
+endif()
+
+include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/safetyhook-config.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/safetyhook-config.cmake
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/safetyhook
+)
+
+install(
+ TARGETS safetyhook
+ EXPORT safetyhook-targets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+install(
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+install(
+ EXPORT safetyhook-targets
+ FILE safetyhook-targets.cmake
+ NAMESPACE safetyhook::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/safetyhook
+)
+
+install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/safetyhook-config.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/safetyhook
+)
+"""
+
+[target.docs]
+condition = "build-docs"
+type = "custom"
+cmake-before = """
+file(GLOB_RECURSE HEADER_FILES include/*.hpp)
+set(DOXYGEN_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/docs/html/index.html)
+set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in)
+set(DOXYFILE_OUT ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile)
+configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT})
+add_custom_command(
+ OUTPUT ${DOXYGEN_INDEX_FILE}
+ DEPENDS ${HEADER_FILES}
+ COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
+ MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
+ COMMENT "Generating docs"
+)
+add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE})
+"""
+
+[target.amalgamation]
+condition = "amalgamate"
+type = "custom"
+cmake-before = """
+file(GLOB_RECURSE HEADER_FILES include/*.hpp)
+file(GLOB_RECURSE SOURCE_FILES src/*.cpp)
+set(AMALGAMATED_FILE ${CMAKE_CURRENT_SOURCE_DIR}/amalgamated-dist/safetyhook.cpp)
+set(AMALGAMATED_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/amalgamated-dist/safetyhook.hpp)
+set(AMALGAMATE_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/amalgamate.py)
+add_custom_command(
+ OUTPUT ${AMALGAMATED_FILE} ${AMALGAMATED_HEADER}
+ DEPENDS ${HEADER_FILES} ${SOURCE_FILES} ${AMALGAMATE_SCRIPT}
+ COMMAND ${Python3_EXECUTABLE} ${AMALGAMATE_SCRIPT}
+ MAIN_DEPENDENCY ${AMALGAMATE_SCRIPT}
+ COMMENT "Amalgamating"
+)
+add_custom_target(Amalgamate ALL DEPENDS ${AMALGAMATED_FILE} ${AMALGAMATED_HEADER})
+"""
+
+[template.example]
+condition = "build-examples"
+type = "executable"
+link-libraries = ["safetyhook::safetyhook"]
+compile-features = ["cxx_std_23"]
+
+[template.example-dll]
+condition = "windows-only-example"
+type = "shared"
+link-libraries = ["safetyhook::safetyhook"]
+compile-features = ["cxx_std_23"]
+
+[target.example-minimal]
+type = "example"
+sources = ["example/minimal.cpp"]
+
+[target.example-multiple]
+type = "example"
+sources = ["example/multiple.cpp"]
+
+[target.example-midhook]
+type = "example"
+sources = ["example/midhook.cpp"]
+
+[target.example-threadsafe]
+type = "example"
+sources = ["example/threadsafe.cpp"]
+
+[target.example-dll]
+type = "example-dll"
+sources = ["example/dll.cpp"]
+
+[target.example-vmthook]
+type = "example"
+sources = ["example/vmthook.cpp"]
+
+[target.test]
+condition = "build-test"
+type = "executable"
+sources = ["test/*.cpp"]
+link-libraries = ["Boost::ut", "safetyhook::safetyhook", "xbyak::xbyak"]
+compile-definitions = ["BOOST_UT_DISABLE_MODULE"]
+compile-features = ["cxx_std_23"]
+
+[target.test-amalgamated]
+condition = "build-amalgamate-test"
+type = "executable"
+sources = ["test/*.cpp", "amalgamated-dist/safetyhook.cpp"]
+include-directories = ["amalgamated-dist/"]
+link-libraries = ["Zydis", "Boost::ut", "xbyak::xbyak"]
+compile-definitions = ["BOOST_UT_DISABLE_MODULE"]
+compile-features = ["cxx_std_23"]
+cmake-after = """
+add_dependencies(test-amalgamated Amalgamate)
+"""
diff --git a/external/safetyhook/cmake/safetyhook-config.cmake.in b/external/safetyhook/cmake/safetyhook-config.cmake.in
new file mode 100644
index 0000000..5d08ca1
--- /dev/null
+++ b/external/safetyhook/cmake/safetyhook-config.cmake.in
@@ -0,0 +1,7 @@
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+find_dependency(zydis CONFIG)
+
+include("${CMAKE_CURRENT_LIST_DIR}/safetyhook-targets.cmake")
+check_required_components(safetyhook)
diff --git a/external/safetyhook/cmkr.cmake b/external/safetyhook/cmkr.cmake
new file mode 100644
index 0000000..a608a9d
--- /dev/null
+++ b/external/safetyhook/cmkr.cmake
@@ -0,0 +1,253 @@
+include_guard()
+
+# Change these defaults to point to your infrastructure if desired
+set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
+set(CMKR_TAG "v0.2.19" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
+set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
+
+# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
+if(CMAKE_SCRIPT_MODE_FILE)
+ set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build")
+ set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}")
+ file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}")
+endif()
+
+# Set these from the command line to customize for development/debugging purposes
+set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
+set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
+set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration")
+mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE)
+
+# Disable cmkr if generation is disabled
+if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION)
+ message(STATUS "[cmkr] Skipping automatic cmkr generation")
+ unset(CMKR_BUILD_SKIP_GENERATION CACHE)
+ macro(cmkr)
+ endmacro()
+ return()
+endif()
+
+# Disable cmkr if no cmake.toml file is found
+if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
+ message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
+ macro(cmkr)
+ endmacro()
+ return()
+endif()
+
+# Convert a Windows native path to CMake path
+if(CMKR_EXECUTABLE MATCHES "\\\\")
+ string(REPLACE "\\" "/" CMKR_EXECUTABLE_CMAKE "${CMKR_EXECUTABLE}")
+ set(CMKR_EXECUTABLE "${CMKR_EXECUTABLE_CMAKE}" CACHE FILEPATH "" FORCE)
+ unset(CMKR_EXECUTABLE_CMAKE)
+endif()
+
+# Helper macro to execute a process (COMMAND_ERROR_IS_FATAL ANY is 3.19 and higher)
+function(cmkr_exec)
+ execute_process(COMMAND ${ARGV} RESULT_VARIABLE CMKR_EXEC_RESULT)
+ if(NOT CMKR_EXEC_RESULT EQUAL 0)
+ message(FATAL_ERROR "cmkr_exec(${ARGV}) failed (exit code ${CMKR_EXEC_RESULT})")
+ endif()
+endfunction()
+
+# Windows-specific hack (CMAKE_EXECUTABLE_PREFIX is not set at the moment)
+if(WIN32)
+ set(CMKR_EXECUTABLE_NAME "cmkr.exe")
+else()
+ set(CMKR_EXECUTABLE_NAME "cmkr")
+endif()
+
+# Use cached cmkr if found
+if(DEFINED ENV{CMKR_CACHE})
+ set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
+ string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
+ if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
+ set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
+ endif()
+ # Build in release mode for the cache
+ set(CMKR_BUILD_TYPE "Release")
+else()
+ set(CMKR_DIRECTORY_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_")
+endif()
+set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
+set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
+
+# Helper function to check if a string starts with a prefix
+# Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61
+function(cmkr_startswith str prefix result)
+ string(LENGTH "${prefix}" prefix_length)
+ string(LENGTH "${str}" str_length)
+ if(prefix_length LESS_EQUAL str_length)
+ string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix)
+ if(prefix STREQUAL str_prefix)
+ set("${result}" ON PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+ set("${result}" OFF PARENT_SCOPE)
+endfunction()
+
+# Handle upgrading logic
+if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
+ cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD)
+ cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE)
+ if(CMKR_STARTSWITH_BUILD)
+ if(DEFINED ENV{CMKR_CACHE})
+ message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
+ if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
+ set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
+ else()
+ unset(CMKR_EXECUTABLE CACHE)
+ endif()
+ else()
+ message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
+ unset(CMKR_EXECUTABLE CACHE)
+ endif()
+ elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE)
+ message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
+ unset(CMKR_EXECUTABLE CACHE)
+ endif()
+endif()
+
+if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}")
+ message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'")
+elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE)
+ message(FATAL_ERROR "[cmkr] '${CMKR_EXECUTABLE}' not found")
+elseif(NOT CMKR_EXECUTABLE AND EXISTS "${CMKR_CACHED_EXECUTABLE}")
+ set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
+ message(STATUS "[cmkr] Found cached cmkr: '${CMKR_EXECUTABLE}'")
+else()
+ set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
+ message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'")
+
+ message(STATUS "[cmkr] Fetching cmkr...")
+ if(EXISTS "${CMKR_DIRECTORY}")
+ cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}")
+ endif()
+ find_package(Git QUIET REQUIRED)
+ cmkr_exec("${GIT_EXECUTABLE}"
+ clone
+ --config advice.detachedHead=false
+ --branch ${CMKR_TAG}
+ --depth 1
+ ${CMKR_REPO}
+ "${CMKR_DIRECTORY}"
+ )
+ if(CMKR_COMMIT_HASH)
+ execute_process(
+ COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}"
+ RESULT_VARIABLE CMKR_EXEC_RESULT
+ WORKING_DIRECTORY "${CMKR_DIRECTORY}"
+ )
+ if(NOT CMKR_EXEC_RESULT EQUAL 0)
+ message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'")
+ endif()
+ endif()
+ message(STATUS "[cmkr] Building cmkr (using system compiler)...")
+ cmkr_exec("${CMAKE_COMMAND}"
+ --no-warn-unused-cli
+ "${CMKR_DIRECTORY}"
+ "-B${CMKR_DIRECTORY}/build"
+ "-DCMAKE_BUILD_TYPE=${CMKR_BUILD_TYPE}"
+ "-DCMAKE_UNITY_BUILD=ON"
+ "-DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY}"
+ "-DCMKR_GENERATE_DOCUMENTATION=OFF"
+ )
+ cmkr_exec("${CMAKE_COMMAND}"
+ --build "${CMKR_DIRECTORY}/build"
+ --config "${CMKR_BUILD_TYPE}"
+ --parallel
+ )
+ cmkr_exec("${CMAKE_COMMAND}"
+ --install "${CMKR_DIRECTORY}/build"
+ --config "${CMKR_BUILD_TYPE}"
+ --prefix "${CMKR_DIRECTORY}"
+ --component cmkr
+ )
+ if(NOT EXISTS ${CMKR_EXECUTABLE})
+ message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'")
+ endif()
+ cmkr_exec("${CMKR_EXECUTABLE}" version)
+ message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}")
+endif()
+execute_process(COMMAND "${CMKR_EXECUTABLE}" version
+ RESULT_VARIABLE CMKR_EXEC_RESULT
+)
+if(NOT CMKR_EXEC_RESULT EQUAL 0)
+ message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding")
+endif()
+
+# Use cmkr.cmake as a script
+if(CMAKE_SCRIPT_MODE_FILE)
+ if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
+ execute_process(COMMAND "${CMKR_EXECUTABLE}" init
+ RESULT_VARIABLE CMKR_EXEC_RESULT
+ )
+ if(NOT CMKR_EXEC_RESULT EQUAL 0)
+ message(FATAL_ERROR "[cmkr] Failed to bootstrap cmkr project. Please report an issue: https://github.com/build-cpp/cmkr/issues/new")
+ else()
+ message(STATUS "[cmkr] Modify cmake.toml and then configure using: cmake -B build")
+ endif()
+ else()
+ execute_process(COMMAND "${CMKR_EXECUTABLE}" gen
+ RESULT_VARIABLE CMKR_EXEC_RESULT
+ )
+ if(NOT CMKR_EXEC_RESULT EQUAL 0)
+ message(FATAL_ERROR "[cmkr] Failed to generate project.")
+ else()
+ message(STATUS "[cmkr] Configure using: cmake -B build")
+ endif()
+ endif()
+endif()
+
+# This is the macro that contains black magic
+macro(cmkr)
+ # When this macro is called from the generated file, fake some internal CMake variables
+ get_source_file_property(CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" CMKR_CURRENT_LIST_FILE)
+ if(CMKR_CURRENT_LIST_FILE)
+ set(CMAKE_CURRENT_LIST_FILE "${CMKR_CURRENT_LIST_FILE}")
+ get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
+ endif()
+
+ # File-based include guard (include_guard is not documented to work)
+ get_source_file_property(CMKR_INCLUDE_GUARD "${CMAKE_CURRENT_LIST_FILE}" CMKR_INCLUDE_GUARD)
+ if(NOT CMKR_INCLUDE_GUARD)
+ set_source_files_properties("${CMAKE_CURRENT_LIST_FILE}" PROPERTIES CMKR_INCLUDE_GUARD TRUE)
+
+ file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_PRE)
+
+ # Generate CMakeLists.txt
+ cmkr_exec("${CMKR_EXECUTABLE}" gen
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ )
+
+ file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_POST)
+
+ # Delete the temporary file if it was left for some reason
+ set(CMKR_TEMP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt")
+ if(EXISTS "${CMKR_TEMP_FILE}")
+ file(REMOVE "${CMKR_TEMP_FILE}")
+ endif()
+
+ if(NOT CMKR_LIST_FILE_SHA256_PRE STREQUAL CMKR_LIST_FILE_SHA256_POST)
+ # Copy the now-generated CMakeLists.txt to CMakerLists.txt
+ # This is done because you cannot include() a file you are currently in
+ configure_file(CMakeLists.txt "${CMKR_TEMP_FILE}" COPYONLY)
+
+ # Add the macro required for the hack at the start of the cmkr macro
+ set_source_files_properties("${CMKR_TEMP_FILE}" PROPERTIES
+ CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}"
+ )
+
+ # 'Execute' the newly-generated CMakeLists.txt
+ include("${CMKR_TEMP_FILE}")
+
+ # Delete the generated file
+ file(REMOVE "${CMKR_TEMP_FILE}")
+
+ # Do not execute the rest of the original CMakeLists.txt
+ return()
+ endif()
+ # Resume executing the unmodified CMakeLists.txt
+ endif()
+endmacro()
diff --git a/external/safetyhook/docs/Doxyfile.in b/external/safetyhook/docs/Doxyfile.in
new file mode 100644
index 0000000..ff9ad61
--- /dev/null
+++ b/external/safetyhook/docs/Doxyfile.in
@@ -0,0 +1,2776 @@
+# Doxyfile 1.9.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+#
+# Note:
+#
+# Use doxygen to compare the used configuration file with the template
+# configuration file:
+# doxygen -x [configFile]
+# Use doxygen to compare the used configuration file with the template
+# configuration file without replacing the environment variables or CMake type
+# replacement variables:
+# doxygen -x_noenv [configFile]
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "SafetyHook"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = "${CMAKE_CURRENT_SOURCE_DIR}/docs"
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
+# sub-directories (in 2 levels) under the output directory of each output format
+# and will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
+# control the number of sub-directories.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# Controls the number of sub-directories that will be created when
+# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
+# level increment doubles the number of directories, resulting in 4096
+# directories at level 8 which is the default and also the maximum value. The
+# sub-directories are organized in 2 levels, the first level always has a fixed
+# number of 16 directories.
+# Minimum value: 0, maximum value: 8, default value: 8.
+# This tag requires that the tag CREATE_SUBDIRS is set to YES.
+
+CREATE_SUBDIRS_LEVEL = 8
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
+# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
+# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
+# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
+# English messages), Korean, Korean-en (Korean with English messages), Latvian,
+# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
+# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
+# Swedish, Turkish, Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:^^"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". Note that you cannot put \n's in the value part of an alias
+# to insert newlines (in the resulting output). You can put ^^ in the value part
+# of an alias to insert a newline as if a physical newline was in the original
+# file. When you need a literal { or } or , in the value part of an alias you
+# have to escape them by means of a backslash (\), this can lead to conflicts
+# with the commands \{ and \} for these it is advised to use the version @{ and
+# @} or use a double escape (\\{ and \\})
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 5
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which effectively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# will also hide undocumented C++ concepts if enabled. This option has no effect
+# if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# Possible values are: SYSTEM, NO and YES.
+# The default value is: SYSTEM.
+
+CASE_SENSE_NAMES = SYSTEM
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
+
+SHOW_HEADERFILE = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if ... \endif and \cond
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+WARN_IF_INCOMPLETE_DOC = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
+# undocumented enumeration values. If set to NO, doxygen will accept
+# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: NO.
+
+WARN_IF_UNDOC_ENUM_VAL = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# See also: WARN_LINE_FORMAT
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# In the $text part of the WARN_FORMAT command it is possible that a reference
+# to a more specific place is given. To make it easier to jump to this place
+# (outside of doxygen) the user can define a custom "cut" / "paste" string.
+# Example:
+# WARN_LINE_FORMAT = "'vi $file +$line'"
+# See also: WARN_FORMAT
+# The default value is: at line $line of file $file.
+
+WARN_LINE_FORMAT = "at line $line of file $file"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr). In case the file specified cannot be opened for writing the
+# warning and error messages are written to standard error. When as file - is
+# specified the warning and error messages are written to standard output
+# (stdout).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = "${CMAKE_CURRENT_SOURCE_DIR}/include"
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# See also: INPUT_FILE_ENCODING
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
+# character encoding on a per file pattern basis. Doxygen will compare the file
+# name with each pattern and apply the encoding instead of the default
+# INPUT_ENCODING) if there is a match. The character encodings are a list of the
+# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
+# "INPUT_ENCODING" for further information on supported encodings.
+
+INPUT_FILE_ENCODING =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
+# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
+# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.l \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f18 \
+ *.f \
+ *.for \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf \
+ *.ice
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = "${CMAKE_CURRENT_SOURCE_DIR}/include/Zydis.h"
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# ANamespace::AClass, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+#
+#
+# where is the value of the INPUT_FILTER tag, and is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that doxygen will use the data processed and written to standard output
+# for further processing, therefore nothing else, like debug statements or used
+# commands (so in case of a Windows batch file always use @echo OFF), should be
+# written to standard output.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+# The Fortran standard specifies that for fixed formatted Fortran code all
+# characters from position 72 are to be considered as comment. A common
+# extension is to allow longer lines before the automatic comment starts. The
+# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
+# be processed before the automatic comment starts.
+# Minimum value: 7, maximum value: 10000, default value: 72.
+
+FORTRAN_COMMENT_AFTER = 72
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see:
+# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
+# performance. This can be particularly helpful with template rich C++ code for
+# which doxygen's built-in parser lacks the necessary type information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
+# tag is set to YES then doxygen will add the directory of each input to the
+# include path.
+# The default value is: YES.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_ADD_INC_PATHS = YES
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the directory containing a file called compile_commands.json. This
+# file is the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
+# options used when the source files were built. This is equivalent to
+# specifying the -p option to a clang tool, such as clang-check. These options
+# will then be passed to the parser. Any options specified with CLANG_OPTIONS
+# will be added as well.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
+# that should be ignored while generating the index headers. The IGNORE_PREFIX
+# tag works for classes, function and member names. The entity will be placed in
+# the alphabetical list under the first letter of the entity name that remains
+# after removing the prefix.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# Note: Since the styling of scrollbars can currently not be overruled in
+# Webkit/Chromium, the styling will be left out of the default doxygen.css if
+# one or more extra stylesheets have been specified. So if scrollbar
+# customization is desired it has to be added explicitly. For an example see the
+# documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
+# should be rendered with a dark or light theme.
+# Possible values are: LIGHT always generate light mode output, DARK always
+# generate dark mode output, AUTO_LIGHT automatically set the mode according to
+# the user preference, use light mode if no preference is set (the default),
+# AUTO_DARK automatically set the mode according to the user preference, use
+# dark mode if no preference is set and TOGGLE allow to user to switch between
+# light and dark mode via a button.
+# The default value is: AUTO_LIGHT.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE = AUTO_LIGHT
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a color-wheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag determines the URL of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDURL =
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATE_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FULL_SIDEBAR = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
+# addresses.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+OBFUSCATE_EMAILS = YES
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_VERSION = MathJax_2
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH =
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use + S
+# (what the is depends on the OS and browser, but it is typically
+# , /