From 7762c4b2ce8bd98ded24e3365e676ef768138f3c Mon Sep 17 00:00:00 2001
From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com>
Date: Mon, 3 Jul 2023 13:37:41 +0200
Subject: [PATCH] Light refactor of the IDetour interface

- Use std::map for mapping vtables to interface objects, previously done with a set and a vector.
- Objects are no longer inline, which significantly reduced output code size as all redundant dynamic initializers (created for each translation unit) have been pruned.
---
 r5dev/core/init.cpp                        | 28 +++++++++++-------
 r5dev/thirdparty/detours/CMakeLists.txt    |  1 +
 r5dev/thirdparty/detours/include/idetour.h | 34 +++++-----------------
 r5dev/thirdparty/detours/src/idetour.cpp   | 30 +++++++++++++++++++
 4 files changed, 56 insertions(+), 37 deletions(-)
 create mode 100644 r5dev/thirdparty/detours/src/idetour.cpp

diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp
index 1a32c110..4b3a6a3f 100644
--- a/r5dev/core/init.cpp
+++ b/r5dev/core/init.cpp
@@ -170,9 +170,10 @@ void Systems_Init()
 	DetourUpdateThread(GetCurrentThread());
 
 	// Hook functions
-	for (const IDetour* pDetour : g_DetourVector)
+	for (const auto& elem : g_DetourMap)
 	{
-		pDetour->Attach();
+		const IDetour* detour = elem.second;
+		detour->Attach();
 	}
 
 	// Patch instructions
@@ -215,9 +216,10 @@ void Systems_Shutdown()
 	DetourUpdateThread(GetCurrentThread());
 
 	// Unhook functions
-	for (const IDetour* pDetour : g_DetourVector)
+	for (const auto& elem : g_DetourMap)
 	{
-		pDetour->Detach();
+		const IDetour* detour = elem.second;
+		detour->Detach();
 	}
 
 	// Commit the transaction
@@ -342,11 +344,13 @@ void DetourInit() // Run the sigscan
 	g_SigCache.SetDisabled(bNoSmap);
 	g_SigCache.LoadCache(SIGDB_FILE);
 
-	for (const IDetour* pDetour : g_DetourVector)
+	for (const auto& elem : g_DetourMap)
 	{
-		pDetour->GetCon(); // Constants.
-		pDetour->GetFun(); // Functions.
-		pDetour->GetVar(); // Variables.
+		const IDetour* detour = elem.second;
+
+		detour->GetCon(); // Constants.
+		detour->GetFun(); // Functions.
+		detour->GetVar(); // Variables.
 
 		if (bLogAdr)
 		{
@@ -355,7 +359,7 @@ void DetourInit() // Run the sigscan
 				bInitDivider = true;
 				spdlog::debug("+---------------------------------------------------------------------+\n");
 			}
-			pDetour->GetAdr();
+			detour->GetAdr();
 			spdlog::debug("+---------------------------------------------------------------------+\n");
 		}
 	}
@@ -372,9 +376,11 @@ void DetourInit() // Run the sigscan
 void DetourAddress() // Test the sigscan results
 {
 	spdlog::debug("+---------------------------------------------------------------------+\n");
-	for (const IDetour* pDetour : g_DetourVector)
+	for (const auto& elem : g_DetourMap)
 	{
-		pDetour->GetAdr();
+		const IDetour* detour = elem.second;
+
+		detour->GetAdr();
 		spdlog::debug("+---------------------------------------------------------------------+\n");
 	}
 }
diff --git a/r5dev/thirdparty/detours/CMakeLists.txt b/r5dev/thirdparty/detours/CMakeLists.txt
index ba2b7b09..da7f3793 100644
--- a/r5dev/thirdparty/detours/CMakeLists.txt
+++ b/r5dev/thirdparty/detours/CMakeLists.txt
@@ -7,6 +7,7 @@ add_sources( SOURCE_GROUP "Source"
     "src/creatwth.cpp"
     "src/detours.cpp"
     "src/disasm.cpp"
+    "src/idetour.cpp"
     "src/modules.cpp"
 )
 
diff --git a/r5dev/thirdparty/detours/include/idetour.h b/r5dev/thirdparty/detours/include/idetour.h
index 40b5166c..84e4bf2e 100644
--- a/r5dev/thirdparty/detours/include/idetour.h
+++ b/r5dev/thirdparty/detours/include/idetour.h
@@ -1,10 +1,9 @@
 #ifndef IDETOUR_H
 #define IDETOUR_H
 
-#define ADDDETOUR(x,y) static std::size_t dummy_reg_##y = AddDetour( new x() );
-#define XREGISTER(x,y)  ADDDETOUR(x, y)
-#define REGISTER(x)     XREGISTER(x, __COUNTER__)
-
+//-----------------------------------------------------------------------------
+// Interface class for context hooks
+//-----------------------------------------------------------------------------
 class IDetour
 {
 public:
@@ -18,28 +17,11 @@ public:
 	virtual void Detach(void) const = 0;
 };
 
-class VDetour : public IDetour
-{
-	virtual void GetAdr(void) const { }
-	virtual void GetFun(void) const { }
-	virtual void GetVar(void) const { }
-	virtual void GetCon(void) const { }
+extern std::map<const void*, const IDetour*> g_DetourMap;
+std::size_t AddDetour(IDetour* pDetour);
 
-	virtual void Attach(void) const { }
-	virtual void Detach(void) const { }
-};
-
-inline static std::vector<IDetour*> g_DetourVector;
-inline static std::unordered_set<IDetour*> g_DetourSet;
-inline std::size_t AddDetour(IDetour* pDetour)
-{
-	IDetour* pVFTable = reinterpret_cast<IDetour**>(pDetour)[0];
-	auto p = g_DetourSet.insert(pVFTable); // Only register if VFTable isn't already registered.
-
-	assert(p.second); // Code bug: duplicate registration!!! (called 'REGISTER(...)' from a header file?).
-	p.second ? g_DetourVector.push_back(pDetour) : delete pDetour;
-
-	return g_DetourVector.size();
-}
+#define ADDDETOUR(x,y) static std::size_t dummy_reg_##y = AddDetour( new x() );
+#define XREGISTER(x,y)  ADDDETOUR(x, y)
+#define REGISTER(x)     XREGISTER(x, __COUNTER__)
 
 #endif // IDETOUR_H
diff --git a/r5dev/thirdparty/detours/src/idetour.cpp b/r5dev/thirdparty/detours/src/idetour.cpp
new file mode 100644
index 00000000..45accf1a
--- /dev/null
+++ b/r5dev/thirdparty/detours/src/idetour.cpp
@@ -0,0 +1,30 @@
+//===========================================================================//
+// 
+// Purpose: Hook interface
+// 
+//===========================================================================//
+#include <cassert>
+#include <map>
+#include "../include/idetour.h"
+
+//-----------------------------------------------------------------------------
+// Contains a VFTable pointer, and the class instance. A VFTable can only be
+// used by one class instance. This is to avoid duplicate registrations.
+//-----------------------------------------------------------------------------
+std::map<const void*, const IDetour*> g_DetourMap;
+
+//-----------------------------------------------------------------------------
+// Purpose: adds a detour context to the list
+//-----------------------------------------------------------------------------
+std::size_t AddDetour(IDetour* pDetour)
+{
+	const void* pVFTable = reinterpret_cast<const void**>(pDetour)[0];
+	auto p = g_DetourMap.emplace(pVFTable, pDetour); // Only register if VFTable isn't already registered.
+
+	assert(p.second); // Code bug: duplicate registration!!! (called 'REGISTER(...)' from a header file?).
+
+	if (!p.second)
+		delete pDetour;
+
+	return g_DetourMap.size();
+}