From c3f31d694acb28d788e644de869c55b9401c65f0 Mon Sep 17 00:00:00 2001
From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com>
Date: Wed, 4 May 2022 02:25:27 +0200
Subject: [PATCH] Improved mod RPak loading between changelevel

Note: this does not work reliably still.

The only way we could make this work 100% reliable would be to fully rebuild '0x140341D40' in the SDK and load our pak files among with the pre-existing g_pakLoadApi->AsyncLoad() call in this function, as this will ensure everything will be ran synchronously.

The current approach by taking some JT fifolock wrapper will only work reliably between one level change, unsure why the second and up fail.
---
 r5dev/core/init.cpp          |   4 ++
 r5dev/datacache/mdlcache.cpp |   4 +-
 r5dev/datacache/mdlcache.h   |   2 +-
 r5dev/engine/cmodel_bsp.cpp  | 101 +++++++++++++++++++++++++++++++++++
 r5dev/engine/cmodel_bsp.h    |  20 ++++++-
 r5dev/engine/host_state.cpp  |  54 +++++++++++++------
 r5dev/engine/host_state.h    |   6 ++-
 r5dev/rtech/rtech_game.cpp   |  15 ++++--
 r5dev/tier0/jobthread.h      |   9 +++-
 9 files changed, 187 insertions(+), 28 deletions(-)

diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp
index c97400dc..4263ed00 100644
--- a/r5dev/core/init.cpp
+++ b/r5dev/core/init.cpp
@@ -169,6 +169,8 @@ void Systems_Init()
 #endif // !CLIENT_DLL && GAMEDLL_S3
 
 	CHostState_Attach();
+
+	CModelBsp_Attach();
 	CModelLoader_Attach();
 
 #if !defined(DEDICATED) && defined (GAMEDLL_S3)
@@ -281,6 +283,8 @@ void Systems_Shutdown()
 #endif // !CLIENT_DLL && GAMEDLL_S3
 
 	CHostState_Detach();
+
+	CModelBsp_Detach();
 	CModelLoader_Detach();
 
 #if !defined(DEDICATED) && defined (GAMEDLL_S3)
diff --git a/r5dev/datacache/mdlcache.cpp b/r5dev/datacache/mdlcache.cpp
index 7d3b241c..78c5badd 100644
--- a/r5dev/datacache/mdlcache.cpp
+++ b/r5dev/datacache/mdlcache.cpp
@@ -174,7 +174,7 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, vo
         else
         {
         LABEL_ERROR:
-            if (std::find(g_vBadMDLHandles.begin(), g_vBadMDLHandles.end(), handle) == g_vBadMDLHandles.end())
+            if (std::find(g_BadMDLHandles.begin(), g_BadMDLHandles.end(), handle) == g_BadMDLHandles.end())
             {
                 if (bInvalidHandle)
                     Error(eDLL_T::ENGINE, "Model with handle \"hu\" not found; replacing with \"%s\".\n", handle, ERROR_MODEL);
@@ -188,7 +188,7 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, vo
                         Error(eDLL_T::ENGINE, "Model \"%s\" not found and \"%s\" couldn't be loaded.\n", v8, ERROR_MODEL);
                 }
 
-                g_vBadMDLHandles.push_back(handle);
+                g_BadMDLHandles.push_back(handle);
             }
             v17 = g_pMDLFallback->m_pErrorHDR;
             old_gather_props->SetValue(true); // mdl/error.rmdl fallback is not supported (yet) in the new GatherProps solution!
diff --git a/r5dev/datacache/mdlcache.h b/r5dev/datacache/mdlcache.h
index 9b1169f6..abfbdf27 100644
--- a/r5dev/datacache/mdlcache.h
+++ b/r5dev/datacache/mdlcache.h
@@ -35,7 +35,7 @@ struct CMDLFallBack
 	}
 };
 inline CMDLFallBack* g_pMDLFallback = new CMDLFallBack();
-inline vector<MDLHandle_t> g_vBadMDLHandles;
+inline vector<MDLHandle_t> g_BadMDLHandles;
 
 class CMDLCache
 {
diff --git a/r5dev/engine/cmodel_bsp.cpp b/r5dev/engine/cmodel_bsp.cpp
index 5363454c..415a9fa4 100644
--- a/r5dev/engine/cmodel_bsp.cpp
+++ b/r5dev/engine/cmodel_bsp.cpp
@@ -6,6 +6,7 @@
 //=============================================================================//
 
 #include "core/stdafx.h"
+#include "tier0/jobthread.h"
 #include "engine/host_cmd.h"
 #include "engine/host_state.h"
 #include "engine/sys_utils.h"
@@ -59,3 +60,103 @@ void MOD_PreloadPak()
 		}
 	}
 }
+
+//-----------------------------------------------------------------------------
+// Purpose: load assets for level with fifolock (still not reliable enough).
+// Input  : svSetFile - 
+// TODO   : Rebuild '0x140341D40' and load paks from there, this should always work.
+//-----------------------------------------------------------------------------
+#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
+bool MOD_LoadPakForMap()
+#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
+bool MOD_LoadPakForMap(void* pBuffer)
+#endif
+{
+	if (!g_bLevelResourceInitialized &&
+		g_bBasePaksInitialized)
+	{
+		g_bLevelResourceInitialized = true;
+
+		if (g_pHostState->LevelHasChanged())
+		{
+			JT_AcquireFifoLock();
+			MOD_PreloadPak();
+		}
+	}
+
+	return v_MOD_LoadPakForMap(pBuffer);
+
+	//void* v1; // r8
+	//const char* append_rpak_var; // rdx
+	//char* result; // rax
+	//int v4; // ecx
+	//int v5; // edx
+	//__int64 v6; // rax
+	//char v7; // cl
+	//int v8; // ebx
+	//char rpak_name_var[272]; // [rsp+20h] [rbp-118h] BYREF
+
+	//static auto unk_14D475233 = CMemory(0x141744E70).RCast<void*>();
+	//static auto byte_14D475220 = CMemory(0x14D475220).RCast<char(*)[19]>();
+	//static auto byte_1666ECF20 = CMemory(0x1666ECF20).RCast<char*>();
+	//static auto dword_141717BB8 = CMemory(0x141717BB8).RCast<int*>();
+
+	//static auto sub_14023BDD0 = CMemory(0x14023BDD0).RCast<__int64(*)()>();
+	//static auto sub_1404418A0 = CMemory(0x1404418A0).RCast<__int64(*)(int)>();
+	//static auto sub_140441520 = CMemory(0x140441520).RCast<__int64(*)(int, void*)>();
+
+	//v1 = &*(void**)unk_14D475233;
+	//append_rpak_var = "%s.rpak";
+	//if (!*byte_14D475220[0])
+	//	v1 = pBuffer;
+	//if (!*byte_14D475220[0])
+	//	append_rpak_var = "%s_loadscreen.rpak";
+	//sprintf(rpak_name_var, append_rpak_var, v1);
+	//result = byte_1666ECF20;
+	//do
+	//{
+	//	v4 = (unsigned __int8)result[rpak_name_var - byte_1666ECF20];
+	//	v5 = (unsigned __int8)*result - v4;
+	//	if (v5)
+	//		break;
+	//	++result;
+	//} while (v4);
+	//if (v5)
+	//{
+	//	v6 = 0i64;                                  // copying rpak name into byte buffer
+	//	do
+	//	{
+	//		v7 = rpak_name_var[v6];
+	//		byte_1666ECF20[v6++] = v7;
+	//	} while (v7);
+	//	sub_14023BDD0();
+	//	if (*dword_141717BB8 != -1)
+	//		sub_1404418A0(*dword_141717BB8);
+
+	//	if (!g_bLevelResourceInitialized &&
+	//		g_bBasePaksInitialized)
+	//		MOD_PreloadPak();
+	//	result = (char*)g_pakLoadApi->AsyncLoad(rpak_name_var);
+
+	//	v8 = (int)result;
+	//	if ((_DWORD)result != -1)
+	//	{
+	//		result = (char*)sub_140441520((unsigned int)result, nullptr);
+	//		if (!(_BYTE)result)
+	//			v8 = -1;
+	//	}
+	//	*dword_141717BB8 = v8;
+	//}
+	//return result;
+}
+
+
+void CModelBsp_Attach()
+{
+	DetourAttach((LPVOID*)&v_MOD_LoadPakForMap, &MOD_LoadPakForMap);
+}
+
+void CModelBsp_Detach()
+{
+	DetourDetach((LPVOID*)&v_MOD_LoadPakForMap, &MOD_LoadPakForMap);
+}
\ No newline at end of file
diff --git a/r5dev/engine/cmodel_bsp.h b/r5dev/engine/cmodel_bsp.h
index b3d4f3cf..ff276b9c 100644
--- a/r5dev/engine/cmodel_bsp.h
+++ b/r5dev/engine/cmodel_bsp.h
@@ -3,23 +3,41 @@
 inline CMemory p_CollisionBSPData_LinkPhysics;
 inline auto CollisionBSPData_LinkPhysics = p_CollisionBSPData_LinkPhysics.RCast<uint64_t(*)(void* thisptr)>();
 
+inline CMemory p_MOD_LoadPakForMap;
+#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
+inline auto v_MOD_LoadPakForMap = p_MOD_LoadPakForMap.RCast<bool(*)(void)>();
+#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
+inline auto v_MOD_LoadPakForMap = p_MOD_LoadPakForMap.RCast<bool(*)(void* pBuffer)>();
+#endif
+
 void MOD_PreloadPak();
+
+void CModelBsp_Attach();
+void CModelBsp_Detach();
 ///////////////////////////////////////////////////////////////////////////////
 class HModel_BSP : public IDetour
 {
 	virtual void GetAdr(void) const
 	{
 		std::cout << "| FUN: CollisionBSPData_LinkPhysics         : 0x" << std::hex << std::uppercase << p_CollisionBSPData_LinkPhysics.GetPtr()  << std::setw(nPad) << " |" << std::endl;
+		std::cout << "| FUN: MOD_LoadPakForMap                    : 0x" << std::hex << std::uppercase << p_MOD_LoadPakForMap.GetPtr()  << std::setw(nPad) << " |" << std::endl;
 		std::cout << "+----------------------------------------------------------------+" << std::endl;
 	}
 	virtual void GetFun(void) const
 	{
 #if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
 		p_CollisionBSPData_LinkPhysics = g_mGameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\x48\x83\xC1\x08\xE8\x00\x00\x00\x00\x48\x8D\x4B\x68"), "xxxxxxxxxxxxxx????xxxx");
+		CollisionBSPData_LinkPhysics = p_CollisionBSPData_LinkPhysics.RCast<uint64_t(*)(void*)>(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 33 ED*/
+
+		p_MOD_LoadPakForMap = g_mGameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x48\x81\xEC\x00\x00\x00\x00\x4C\x8B\xC1\x48\x8D\x15\x00\x00\x00\x00\x48\x8D\x4C\x24\x00\xE8\x00\x00\x00\x00\x4C\x8D\x0D\x00\x00\x00\x00"), "xxx????xxxxxx????xxxx?x????xxx????");
+		v_MOD_LoadPakForMap = p_MOD_LoadPakForMap.RCast<bool(*)(void)>(); /*48 81 EC ? ? ? ? 4C 8B C1 48 8D 15 ? ? ? ? 48 8D 4C 24 ? E8 ? ? ? ? 4C 8D 0D ? ? ? ?*/
 #elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
 		p_CollisionBSPData_LinkPhysics = g_mGameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x57\x48\x81\xEC\x00\x00\x00\x00\x48\x8B\xF9\x33\xED"), "xxxx?xxxx?xxxx????xxxxx");
-#endif
 		CollisionBSPData_LinkPhysics = p_CollisionBSPData_LinkPhysics.RCast<uint64_t(*)(void*)>(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 33 ED*/
+
+		p_MOD_LoadPakForMap = g_mGameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x48\x81\xEC\x00\x00\x00\x00\x0F\xB6\x05\x00\x00\x00\x00\x4C\x8D\x05\x00\x00\x00\x00\x84\xC0"), "xxx????xxx????xxx????xx");
+		v_MOD_LoadPakForMap = p_MOD_LoadPakForMap.RCast<bool(*)(void* pBuffer)>(); /*48 81 EC ? ? ? ? 0F B6 05 ? ? ? ? 4C 8D 05 ? ? ? ? 84 C0*/
+#endif
 	}
 	virtual void GetVar(void) const { }
 	virtual void GetCon(void) const { }
diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp
index fc5cbd46..42a03d8e 100644
--- a/r5dev/engine/host_state.cpp
+++ b/r5dev/engine/host_state.cpp
@@ -5,6 +5,7 @@
 //=============================================================================//
 
 #include "core/stdafx.h"
+#include "tier0/jobthread.h"
 #include "tier0/commandline.h"
 #include "tier0/fasttimer.h"
 #include "tier1/cmd.h"
@@ -46,6 +47,8 @@
 #endif // !CLIENT_DLL
 
 bool g_bLevelResourceInitialized = false;
+bool g_bBasePaksInitialized = false;
+string g_svPrevLevelName;
 //-----------------------------------------------------------------------------
 // Purpose: state machine's main processing loop
 //-----------------------------------------------------------------------------
@@ -107,10 +110,8 @@ FORCEINLINE void CHostState::FrameUpdate(CHostState* rcx, void* rdx, float time)
 			case HostStates_t::HS_GAME_SHUTDOWN:
 			{
 				DevMsg(eDLL_T::ENGINE, "%s - Shutdown host game\n", "CHostState::FrameUpdate");
-
 				g_bLevelResourceInitialized = false;
 				CHostState_State_GameShutDown(g_pHostState);
-				g_pHostState->UnloadPakFile(); 
 				break;
 			}
 			case HostStates_t::HS_RESTART:
@@ -173,12 +174,13 @@ FORCEINLINE void CHostState::Init(void)
 	m_vecLocation.Init();
 	m_angLocation.Init();
 	m_iCurrentState = HostStates_t::HS_NEW_GAME;
+	g_svPrevLevelName = m_levelName;
 }
 
 //-----------------------------------------------------------------------------
 // Purpose: state machine setup
 //-----------------------------------------------------------------------------
-FORCEINLINE void CHostState::Setup(void) const
+FORCEINLINE void CHostState::Setup(void) 
 {
 	g_pHostState->LoadConfig();
 	g_pConVar->PurgeHostNames();
@@ -196,13 +198,7 @@ FORCEINLINE void CHostState::Setup(void) const
 	{
 		NET_GenerateKey();
 	}
-
-#ifdef DEDICATED
-	const char* szNoMap = "server_idle";
-#else // DEDICATED
-	const char* szNoMap = "main_menu";
-#endif
-	snprintf(const_cast<char*>(m_levelName), sizeof(m_levelName), szNoMap);
+	ResetLevelName();
 	KeyValues::Init();
 }
 
@@ -297,12 +293,8 @@ FORCEINLINE void CHostState::GameShutDown(void)
 		g_pServerGameDLL->GameShutdown();
 #endif // !CLIENT_DLL
 		m_bActiveGame = 0;
-#ifdef DEDICATED
-		const char* szNoMap = "server_idle";
-#else // DEDICATED
-		const char* szNoMap = "main_menu";
-#endif
-		snprintf(const_cast<char*>(m_levelName), sizeof(m_levelName), szNoMap);
+
+		ResetLevelName();
 	}
 }
 
@@ -311,6 +303,9 @@ FORCEINLINE void CHostState::GameShutDown(void)
 //-----------------------------------------------------------------------------
 FORCEINLINE void CHostState::UnloadPakFile(void) const
 {
+	if (g_pHostState->m_iCurrentState != HostStates_t::HS_SHUTDOWN && !LevelHasChanged())
+		return; // Do not issue unload if we reload the same level.
+
 	for (auto& it : g_LoadedPakHandle)
 	{
 		if (it >= 0)
@@ -326,7 +321,7 @@ FORCEINLINE void CHostState::UnloadPakFile(void) const
 		}
 	}
 	g_LoadedPakHandle.clear();
-	g_vBadMDLHandles.clear();
+	g_BadMDLHandles.clear();
 }
 
 //-----------------------------------------------------------------------------
@@ -360,6 +355,7 @@ FORCEINLINE void CHostState::State_NewGame(void)
 	{
 		m_iNextState = HostStates_t::HS_RUN;
 	}
+	g_svPrevLevelName = m_levelName;
 }
 
 //-----------------------------------------------------------------------------
@@ -387,6 +383,7 @@ FORCEINLINE void CHostState::State_ChangeLevelSP(void)
 	{
 		m_iNextState = HostStates_t::HS_RUN;
 	}
+	g_svPrevLevelName = m_levelName;
 }
 
 //-----------------------------------------------------------------------------
@@ -420,6 +417,29 @@ FORCEINLINE void CHostState::State_ChangeLevelMP(void)
 	{
 		m_iNextState = HostStates_t::HS_RUN;
 	}
+	g_svPrevLevelName = m_levelName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: resets the level name
+//-----------------------------------------------------------------------------
+FORCEINLINE void CHostState::ResetLevelName(void)
+{
+#ifdef DEDICATED
+	const char* szNoMap = "server_idle";
+#else // DEDICATED
+	const char* szNoMap = "main_menu";
+#endif
+	snprintf(const_cast<char*>(m_levelName), sizeof(m_levelName), szNoMap);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: checks if level has changed
+// Output : true if level name deviates from previous level
+//-----------------------------------------------------------------------------
+FORCEINLINE bool CHostState::LevelHasChanged(void) const
+{
+	return (strcmp(m_levelName, g_svPrevLevelName.c_str()) != 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/r5dev/engine/host_state.h b/r5dev/engine/host_state.h
index a1db6c3f..7cdff537 100644
--- a/r5dev/engine/host_state.h
+++ b/r5dev/engine/host_state.h
@@ -21,7 +21,7 @@ public:
 	FORCEINLINE void LoadConfig(void) const;
 
 	FORCEINLINE void Init(void);
-	FORCEINLINE void Setup(void) const;
+	FORCEINLINE void Setup(void);
 	FORCEINLINE void Think(void) const;
 
 	FORCEINLINE void GameShutDown(void);
@@ -31,6 +31,9 @@ public:
 	FORCEINLINE void State_ChangeLevelSP(void);
 	FORCEINLINE void State_ChangeLevelMP(void);
 
+	FORCEINLINE void ResetLevelName(void);
+	FORCEINLINE bool LevelHasChanged(void) const;
+
 public:
 	HostStates_t m_iCurrentState;                    //0x0000
 	HostStates_t m_iNextState;                       //0x0004
@@ -60,6 +63,7 @@ inline CMemory p_CHostState_State_GameShutDown;
 inline auto CHostState_State_GameShutDown = p_CHostState_State_GameShutDown.RCast<void(*)(CHostState* thisptr)>();
 
 extern bool g_bLevelResourceInitialized;
+extern bool g_bBasePaksInitialized;
 ///////////////////////////////////////////////////////////////////////////////
 void CHostState_Attach();
 void CHostState_Detach();
diff --git a/r5dev/rtech/rtech_game.cpp b/r5dev/rtech/rtech_game.cpp
index 3cc0dcbe..73806e8a 100644
--- a/r5dev/rtech/rtech_game.cpp
+++ b/r5dev/rtech/rtech_game.cpp
@@ -40,14 +40,19 @@ RPakHandle_t CPakFile::AsyncLoad(const char* szPakFileName, uintptr_t pMalloc, i
 		return pakHandle;
 	}
 #endif // DEDICATED
-	static bool bBasePaksLoaded = false;
 
-	if (strcmp(szPakFileName, "mp_lobby.rpak") == 0 || !g_bLevelResourceInitialized && !g_pHostState->m_bActiveGame &&
-		bBasePaksLoaded)
+	if (strcmp(szPakFileName, "mp_lobby.rpak") == 0)
+		g_bBasePaksInitialized = true;
+
+	static bool bOnce = false;
+	if (g_bBasePaksInitialized && !g_bLevelResourceInitialized
+		&& !bOnce)
 	{
-		bBasePaksLoaded = true; // By the time mp_lobby.rpak is loaded, all the base paks are loaded as well and we can load anything else.
 		g_bLevelResourceInitialized = true;
-		MOD_PreloadPak();
+		bOnce = true;
+
+		if (g_pHostState->LevelHasChanged())
+			MOD_PreloadPak();
 	}
 
 	string svPakFilePathMod = "paks\\Win32\\" + string(szPakFileName);
diff --git a/r5dev/tier0/jobthread.h b/r5dev/tier0/jobthread.h
index 0849e4bf..69d3bb90 100644
--- a/r5dev/tier0/jobthread.h
+++ b/r5dev/tier0/jobthread.h
@@ -4,6 +4,9 @@
 inline CMemory p_JT_HelpWithAnything;
 inline auto JT_HelpWithAnything = p_JT_HelpWithAnything.RCast<void* (*)(bool bShouldLoadPak)>();
 
+inline CMemory p_JT_AcquireFifoLock;
+inline auto JT_AcquireFifoLock = p_JT_AcquireFifoLock.RCast<void* (*)(void)>();
+
 void JT_Attach();
 void JT_Detach();
 ///////////////////////////////////////////////////////////////////////////////
@@ -12,6 +15,7 @@ class HJobThread : public IDetour
 	virtual void GetAdr(void) const
 	{
 		std::cout << "| FUN: JT_HelpWithAnything                  : 0x" << std::hex << std::uppercase << p_JT_HelpWithAnything.GetPtr() << std::setw(nPad) << " |" << std::endl;
+		std::cout << "| FUN: JT_AcquireFifoLock                   : 0x" << std::hex << std::uppercase << p_JT_AcquireFifoLock.GetPtr()  << std::setw(nPad) << " |" << std::endl;
 		std::cout << "+----------------------------------------------------------------+" << std::endl;
 	}
 	virtual void GetFun(void) const
@@ -21,7 +25,10 @@ class HJobThread : public IDetour
 #elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
 		p_JT_HelpWithAnything = g_mGameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xEC\x30\x80\x3D\x00\x00\x00\x00\x00"), "xxxx?xxxx?xxxxxxx?????");
 #endif
-		JT_HelpWithAnything = p_JT_HelpWithAnything.RCast<void* (*)(bool bShouldLoadPak)>(); /*48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 30 80 3D ? ? ? ? ?*/
+		p_JT_AcquireFifoLock = g_mGameDll.FindPatternSIMD(reinterpret_cast<rsig_t>("\x40\x55\x48\x83\xEC\x30\x33\xED"), "xxxxxxxx");
+
+		JT_HelpWithAnything = p_JT_HelpWithAnything.RCast<void* (*)(bool bShouldLoadPak)>(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 80 3D ?? ?? ?? ?? ??*/
+		JT_AcquireFifoLock = p_JT_AcquireFifoLock.RCast<void* (*)(void)>();                  /*40 55 48 83 EC 30 33 ED*/
 	}
 	virtual void GetVar(void) const { }
 	virtual void GetCon(void) const { }