From 998de21d730fbd0e6f59682c960db84de06e0361 Mon Sep 17 00:00:00 2001
From: Amos <48657826+Mauler125@users.noreply.github.com>
Date: Wed, 19 Jan 2022 19:02:18 +0100
Subject: [PATCH] Implement new FileSystem hooks

The hooks will check if the file in question exist on the disk first before falling back to VPK or obtaining it from the cache.
---
 r5dev/public/include/utility.h |  1 +
 r5dev/public/utility.cpp       | 19 +++++++++
 r5dev/squirrel/sqvm.cpp        | 78 ++++++----------------------------
 r5dev/vpc/basefilesystem.cpp   | 51 ++++++++++++++++++++++
 r5dev/vpc/basefilesystem.h     | 12 +++++-
 5 files changed, 95 insertions(+), 66 deletions(-)

diff --git a/r5dev/public/include/utility.h b/r5dev/public/include/utility.h
index 1d114d6d..9a4cea26 100644
--- a/r5dev/public/include/utility.h
+++ b/r5dev/public/include/utility.h
@@ -16,6 +16,7 @@ std::string Base64Encode(const std::string& in);
 std::string Base64Decode(const std::string& in);
 bool StringReplace(std::string& str, const std::string& from, const std::string& to);
 std::string CreateDirectories(std::string svFilePath);
+std::string ConvertToWinPath(const std::string& input);
 std::string StringEscape(const std::string& input);
 std::string StringUnescape(const std::string& input);
 
diff --git a/r5dev/public/utility.cpp b/r5dev/public/utility.cpp
index 431fa831..46468b54 100644
--- a/r5dev/public/utility.cpp
+++ b/r5dev/public/utility.cpp
@@ -296,6 +296,25 @@ std::string CreateDirectories(std::string svFilePath)
     return results;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// For converting filepaths to windows filepaths.
+std::string ConvertToWinPath(const std::string& input)
+{
+    char szFilePath[MAX_PATH] = { 0 };
+    std::string results;
+    sprintf_s(szFilePath, MAX_PATH, "%s", input.c_str());
+
+    // Flip forward slashes in filepath to windows-style backslash
+    for (int i = 0; i < strlen(szFilePath); i++)
+    {
+        if (szFilePath[i] == '/')
+        {
+            szFilePath[i] = '\\';
+        }
+    }
+    return results = szFilePath;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // For escaping special characters in a string.
 std::string StringEscape(const std::string& input)
diff --git a/r5dev/squirrel/sqvm.cpp b/r5dev/squirrel/sqvm.cpp
index 3e819146..7d8341ec 100644
--- a/r5dev/squirrel/sqvm.cpp
+++ b/r5dev/squirrel/sqvm.cpp
@@ -143,82 +143,30 @@ void* HSQVM_WarningFunc(void* sqvm, int a2, int a3, int* nStringSize, void** ppS
 }
 
 //---------------------------------------------------------------------------------
-// Purpose: loads the include file from the mods directory
+// Purpose: prints the global include file the compiler loads for loading scripts
 //---------------------------------------------------------------------------------
 void* HSQVM_LoadRson(const char* szRsonName)
 {
-	char szFilePath[MAX_PATH] = { 0 };
-	sprintf_s(szFilePath, MAX_PATH, "platform\\%s", szRsonName);
-
-	// Flip forward slashes in filepath to windows-style backslash
-	for (int i = 0; i < strlen(szFilePath); i++)
+	if (sq_showrsonloading->GetBool())
 	{
-		if (szFilePath[i] == '/')
-		{
-			szFilePath[i] = '\\';
-		}
-	}
-
-	// Returns the new path if the rson exists on the disk
-	if (FileExists(szFilePath) && SQVM_LoadRson(szRsonName))
-	{
-		if (sq_showrsonloading->GetBool())
-		{
-			DevMsg(eDLL_T::ENGINE, "\n");
-			DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n");
-			DevMsg(eDLL_T::ENGINE, "] RSON_DISK --------------------------------------------------\n");
-			DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szFilePath);
-			DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n");
-			DevMsg(eDLL_T::ENGINE, "\n");
-		}
-		return SQVM_LoadRson(szFilePath);
-	}
-	else
-	{
-		if (sq_showrsonloading->GetBool())
-		{
-			DevMsg(eDLL_T::ENGINE, "\n");
-			DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n");
-			DevMsg(eDLL_T::ENGINE, "] RSON_VPK ---------------------------------------------------\n");
-			DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szRsonName);
-			DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n");
-			DevMsg(eDLL_T::ENGINE, "\n");
-		}
+		DevMsg(eDLL_T::ENGINE, "\n");
+		DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n");
+		DevMsg(eDLL_T::ENGINE, "] RSON_SQVM --------------------------------------------------\n");
+		DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szRsonName);
+		DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n");
+		DevMsg(eDLL_T::ENGINE, "\n");
 	}
 	return SQVM_LoadRson(szRsonName);
 }
 
 //---------------------------------------------------------------------------------
-// Purpose: loads the script file from the mods directory
+// Purpose: prints the scripts the compiler loads from global include to be compiled
 //---------------------------------------------------------------------------------
 bool HSQVM_LoadScript(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag)
 {
-	char filepath[MAX_PATH] = { 0 };
-	sprintf_s(filepath, MAX_PATH, "platform\\%s", szScriptPath);
-
-	// Flip forward slashes in filepath to windows-style backslash
-	for (int i = 0; i < strlen(filepath); i++)
-	{
-		if (filepath[i] == '/')
-		{
-			filepath[i] = '\\';
-		}
-	}
-
 	if (sq_showscriptloading->GetBool())
 	{
-		DevMsg(eDLL_T::ENGINE, "Loading SQVM Script '%s'\n", filepath);
-	}
-
-	// Returns true if the script exists on the disk
-	if (FileExists(filepath) && SQVM_LoadScript(sqvm, filepath, szScriptName, nFlag))
-	{
-		return true;
-	}
-
-	if (sq_showscriptloading->GetBool())
-	{
-		DevMsg(eDLL_T::ENGINE, "FAILED. Try SP / VPK for '%s'\n", filepath);
+		DevMsg(eDLL_T::ENGINE, "Loading SQVM Script '%s'\n", szScriptName);
 	}
 
 	///////////////////////////////////////////////////////////////////////////////
@@ -247,7 +195,7 @@ void HSQVM_RegisterFunction(void* sqvm, const char* szName, const char* szHelpSt
 //---------------------------------------------------------------------------------
 void RegisterServerScriptFunctions(void* sqvm)
 {
-	HSQVM_RegisterFunction(sqvm, "ServerNativeTest", "Native SERVER test function", "void", "", &VSquirrel::SHARED::Script_NativeTest);
+	HSQVM_RegisterFunction(sqvm, "SDKNativeTest", "Native SERVER test function", "void", "", &VSquirrel::SHARED::Script_NativeTest);
 }
 
 #ifndef DEDICATED
@@ -256,7 +204,7 @@ void RegisterServerScriptFunctions(void* sqvm)
 //---------------------------------------------------------------------------------
 void RegisterClientScriptFunctions(void* sqvm)
 {
-	HSQVM_RegisterFunction(sqvm, "ClientNativeTest", "Native CLIENT test function", "void", "", &VSquirrel::SHARED::Script_NativeTest);
+	HSQVM_RegisterFunction(sqvm, "SDKNativeTest", "Native CLIENT test function", "void", "", &VSquirrel::SHARED::Script_NativeTest);
 }
 
 //---------------------------------------------------------------------------------
@@ -264,7 +212,7 @@ void RegisterClientScriptFunctions(void* sqvm)
 //---------------------------------------------------------------------------------
 void RegisterUIScriptFunctions(void* sqvm)
 {
-	HSQVM_RegisterFunction(sqvm, "UINativeTest", "Native UI test function", "void", "", &VSquirrel::SHARED::Script_NativeTest);
+	HSQVM_RegisterFunction(sqvm, "SDKNativeTest", "Native UI test function", "void", "", &VSquirrel::SHARED::Script_NativeTest);
 
 	// functions for retrieving server browser data
 	HSQVM_RegisterFunction(sqvm, "GetServerName", "Gets the name of the server at the specified index of the server list", "string", "int", &VSquirrel::UI::GetServerName);
diff --git a/r5dev/vpc/basefilesystem.cpp b/r5dev/vpc/basefilesystem.cpp
index b28d9a72..68b29f14 100644
--- a/r5dev/vpc/basefilesystem.cpp
+++ b/r5dev/vpc/basefilesystem.cpp
@@ -48,12 +48,63 @@ void HCBaseFileSystem_Warning(void* thisptr, FileWarningLevel_t level, const cha
 	}
 }
 
+//---------------------------------------------------------------------------------
+// Purpose: attempts to load files from disk if exist before loading from VPK
+//---------------------------------------------------------------------------------
+FileHandle_t HCBaseFileSystem_ReadFromVPK(void* pVpk, std::int64_t* pResults, char* pszFilePath)
+{
+	std::string svFilePath = ConvertToWinPath(pszFilePath);
+
+	if (strstr(svFilePath.c_str(), "\\\*\\"))
+	{
+		// Erase '//*/'.
+		svFilePath.erase(0, 4);
+	}
+
+	// TODO: obtain 'mod' SearchPath's instead.
+	svFilePath.insert(0, "platform\\");
+
+	if (FileExists(svFilePath.c_str()) || FileExists(pszFilePath))
+	{
+		*pResults = -1;
+		return (void*)pResults;
+	}
+	return CBaseFileSystem_LoadFromVPK(pVpk, pResults, pszFilePath);
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: attempts to load files from disk if exist before loading from cache
+//---------------------------------------------------------------------------------
+bool HCBaseFileSystem_ReadFromCache(void* pFileSystem, char* pszFilePath, void* pResults)
+{
+	std::string svFilePath = ConvertToWinPath(pszFilePath);
+
+	if (strstr(svFilePath.c_str(), "\\\*\\"))
+	{
+		// Erase '//*/'.
+		svFilePath.erase(0, 4);
+	}
+
+	// TODO: obtain 'mod' SearchPath's instead.
+	svFilePath.insert(0, "platform\\");
+
+	if (FileExists(svFilePath.c_str()) || FileExists(pszFilePath))
+	{
+		return false;
+	}
+	return CBaseFileSystem_LoadFromCache(pFileSystem, pszFilePath, pResults);
+}
+
 void CBaseFileSystem_Attach()
 {
 	DetourAttach((LPVOID*)&CBaseFileSystem_Warning, &HCBaseFileSystem_Warning);
+	DetourAttach((LPVOID*)&CBaseFileSystem_LoadFromVPK, &HCBaseFileSystem_ReadFromVPK);
+	DetourAttach((LPVOID*)&CBaseFileSystem_LoadFromCache, &HCBaseFileSystem_ReadFromCache);
 }
 
 void CBaseFileSystem_Detach()
 {
 	DetourDetach((LPVOID*)&CBaseFileSystem_Warning, &HCBaseFileSystem_Warning);
+	DetourDetach((LPVOID*)&CBaseFileSystem_LoadFromVPK, &HCBaseFileSystem_ReadFromVPK);
+	DetourDetach((LPVOID*)&CBaseFileSystem_LoadFromCache, &HCBaseFileSystem_ReadFromCache);
 }
diff --git a/r5dev/vpc/basefilesystem.h b/r5dev/vpc/basefilesystem.h
index cc16fc15..5c04523c 100644
--- a/r5dev/vpc/basefilesystem.h
+++ b/r5dev/vpc/basefilesystem.h
@@ -1,5 +1,7 @@
 #pragma once
 
+typedef void* FileHandle_t;
+
 enum class FileWarningLevel_t : int
 {
 	FILESYSTEM_WARNING = -1,                        // A problem!
@@ -17,6 +19,12 @@ namespace
 	/* ==== CBASEFILESYSTEM ================================================================================================================================================= */
 	ADDRESS p_CBaseFileSystem_Warning = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x20\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x48", "xxxxxx??????????x");
 	void (*CBaseFileSystem_Warning)(void* thisptr, FileWarningLevel_t level, const char* fmt, ...) = (void (*)(void*, FileWarningLevel_t, const char*, ...))p_CBaseFileSystem_Warning.GetPtr(); /*4C 89 4C 24 20 C3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48*/
+
+	ADDRESS p_CBaseFileSystem_LoadFromVPK = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x57\x48\x81\xEC\x00\x00\x00\x00\x49\x8B\xC0\x4C\x8D\x8C\x24\x00\x00\x00\x00", "xxxx?xxxx????xxxxxxx????");
+	FileHandle_t(*CBaseFileSystem_LoadFromVPK)(void* pVpk, std::int64_t* pResults, char* pszAssetName) = (FileHandle_t(*)(void*, std::int64_t*, char*))p_CBaseFileSystem_LoadFromVPK.GetPtr(); /*48 89 5C 24 ? 57 48 81 EC ? ? ? ? 49 8B C0 4C 8D 8C 24 ? ? ? ?*/
+
+	ADDRESS p_CBaseFileSystem_LoadFromCache = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x81\xEC\x00\x00\x00\x00\x80\x3D\x00\x00\x00\x00\x00\x49\x8B\xD8", "xxxxx????xx?????xxx");
+	bool(*CBaseFileSystem_LoadFromCache)(void* pFileSystem, char* pszAssetName, void* pResults) = (bool(*)(void*, char*, void*))p_CBaseFileSystem_LoadFromCache.GetPtr(); /*40 53 48 81 EC ? ? ? ? 80 3D ? ? ? ? ? 49 8B D8*/
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -28,7 +36,9 @@ class HBaseFileSystem : public IDetour
 {
 	virtual void debugp()
 	{
-		std::cout << "| FUN: CBaseFileSystem::Warning             : 0x" << std::hex << std::uppercase << p_CBaseFileSystem_Warning.GetPtr() << std::setw(npad) << " |" << std::endl;
+		std::cout << "| FUN: CBaseFileSystem::Warning             : 0x" << std::hex << std::uppercase << p_CBaseFileSystem_Warning.GetPtr()     << std::setw(npad)   << " |" << std::endl;
+		std::cout << "| FUN: CBaseFileSystem::LoadFromVPK         : 0x" << std::hex << std::uppercase << p_CBaseFileSystem_LoadFromVPK.GetPtr() << std::setw(npad)   << " |" << std::endl;
+		std::cout << "| FUN: CBaseFileSystem::LoadFromCache       : 0x" << std::hex << std::uppercase << p_CBaseFileSystem_LoadFromCache.GetPtr() << std::setw(npad) << " |" << std::endl;
 		std::cout << "+----------------------------------------------------------------+" << std::endl;
 	}
 };