aboutsummaryrefslogtreecommitdiff
path: root/primedev/core
diff options
context:
space:
mode:
Diffstat (limited to 'primedev/core')
-rw-r--r--primedev/core/convar/convar.cpp5
-rw-r--r--primedev/core/convar/convar.h1
-rw-r--r--primedev/core/convar/cvar.cpp1
-rw-r--r--primedev/core/convar/cvar.h1
-rw-r--r--primedev/core/filesystem/filesystem.cpp83
-rw-r--r--primedev/core/filesystem/filesystem.h3
-rw-r--r--primedev/core/filesystem/rpakfilesystem.cpp543
-rw-r--r--primedev/core/filesystem/rpakfilesystem.h86
-rw-r--r--primedev/core/hooks.cpp38
-rw-r--r--primedev/core/hooks.h17
-rw-r--r--primedev/core/math/bitbuf.h115
-rw-r--r--primedev/core/math/color.h80
-rw-r--r--primedev/core/math/vector.h60
-rw-r--r--primedev/core/memalloc.h5
-rw-r--r--primedev/core/sourceinterface.cpp49
-rw-r--r--primedev/core/sourceinterface.h38
-rw-r--r--primedev/core/tier0.cpp29
-rw-r--r--primedev/core/tier1.h6
-rw-r--r--primedev/core/vanilla.h5
19 files changed, 668 insertions, 497 deletions
diff --git a/primedev/core/convar/convar.cpp b/primedev/core/convar/convar.cpp
index 87a1e159..802c088d 100644
--- a/primedev/core/convar/convar.cpp
+++ b/primedev/core/convar/convar.cpp
@@ -1,7 +1,7 @@
#include "bits.h"
#include "cvar.h"
#include "convar.h"
-#include "core/sourceinterface.h"
+#include "core/tier1.h"
#include <float.h>
@@ -35,8 +35,7 @@ ON_DLL_LOAD("engine.dll", ConVar, (CModule module))
g_pConVar_Vtable = module.Offset(0x67FD28);
g_pIConVar_Vtable = module.Offset(0x67FDC8);
- g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
- g_pCVar = *g_pCVarInterface;
+ g_pCVar = Sys_GetFactoryPtr("vstdlib.dll", "VEngineCvar007").RCast<CCvar*>();
}
//-----------------------------------------------------------------------------
diff --git a/primedev/core/convar/convar.h b/primedev/core/convar/convar.h
index f0366b46..33a50c1c 100644
--- a/primedev/core/convar/convar.h
+++ b/primedev/core/convar/convar.h
@@ -1,5 +1,4 @@
#pragma once
-#include "core/sourceinterface.h"
#include "core/math/color.h"
#include "cvar.h"
#include "concommand.h"
diff --git a/primedev/core/convar/cvar.cpp b/primedev/core/convar/cvar.cpp
index aa5f0365..78da1ad2 100644
--- a/primedev/core/convar/cvar.cpp
+++ b/primedev/core/convar/cvar.cpp
@@ -22,5 +22,4 @@ std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
return allConVars;
}
-SourceInterface<CCvar>* g_pCVarInterface;
CCvar* g_pCVar;
diff --git a/primedev/core/convar/cvar.h b/primedev/core/convar/cvar.h
index beaa84f4..024f2b87 100644
--- a/primedev/core/convar/cvar.h
+++ b/primedev/core/convar/cvar.h
@@ -34,5 +34,4 @@ public:
std::unordered_map<std::string, ConCommandBase*> DumpToMap();
};
-extern SourceInterface<CCvar>* g_pCVarInterface;
extern CCvar* g_pCVar;
diff --git a/primedev/core/filesystem/filesystem.cpp b/primedev/core/filesystem/filesystem.cpp
index b39939e4..3c711e8e 100644
--- a/primedev/core/filesystem/filesystem.cpp
+++ b/primedev/core/filesystem/filesystem.cpp
@@ -1,34 +1,32 @@
#include "filesystem.h"
-#include "core/sourceinterface.h"
+#include "core/tier1.h"
#include "mods/modmanager.h"
#include <iostream>
#include <sstream>
-AUTOHOOK_INIT()
-
bool bReadingOriginalFile = false;
std::string sCurrentModPath;
ConVar* Cvar_ns_fs_log_reads;
-SourceInterface<IFileSystem>* g_pFilesystem;
+IFileSystem* g_pFilesystem;
std::string ReadVPKFile(const char* path)
{
// read scripts.rson file, todo: check if this can be overwritten
- FileHandle_t fileHandle = (*g_pFilesystem)->m_vtable2->Open(&(*g_pFilesystem)->m_vtable2, path, "rb", "GAME", 0);
+ FileHandle_t fileHandle = g_pFilesystem->m_vtable2->Open(&g_pFilesystem->m_vtable2, path, "rb", "GAME", 0);
std::stringstream fileStream;
int bytesRead = 0;
char data[4096];
do
{
- bytesRead = (*g_pFilesystem)->m_vtable2->Read(&(*g_pFilesystem)->m_vtable2, data, (int)std::size(data), fileHandle);
+ bytesRead = g_pFilesystem->m_vtable2->Read(&g_pFilesystem->m_vtable2, data, (int)std::size(data), fileHandle);
fileStream.write(data, bytesRead);
} while (bytesRead == std::size(data));
- (*g_pFilesystem)->m_vtable2->Close(*g_pFilesystem, fileHandle);
+ g_pFilesystem->m_vtable2->Close(g_pFilesystem, fileHandle);
return fileStream.str();
}
@@ -44,18 +42,17 @@ std::string ReadVPKOriginalFile(const char* path)
return ret;
}
-// clang-format off
-HOOK(AddSearchPathHook, AddSearchPath,
-void, __fastcall, (IFileSystem * fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType))
-// clang-format on
+static void(__fastcall* o_pAddSearchPath)(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType) =
+ nullptr;
+static void __fastcall h_AddSearchPath(IFileSystem* fileSystem, const char* pPath, const char* pathID, SearchPathAdd_t addType)
{
- AddSearchPath(fileSystem, pPath, pathID, addType);
+ o_pAddSearchPath(fileSystem, pPath, pathID, addType);
// make sure current mod paths are at head
if (!strcmp(pathID, "GAME") && sCurrentModPath.compare(pPath) && addType == PATH_ADD_TO_HEAD)
{
- AddSearchPath(fileSystem, sCurrentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD);
- AddSearchPath(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(fileSystem, sCurrentModPath.c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(fileSystem, GetCompiledAssetsPath().string().c_str(), "GAME", PATH_ADD_TO_HEAD);
}
}
@@ -67,13 +64,13 @@ void SetNewModSearchPaths(Mod* mod)
{
if ((fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().compare(sCurrentModPath))
{
- AddSearchPath(
- &*(*g_pFilesystem), (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(
+ g_pFilesystem, (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
sCurrentModPath = (fs::absolute(mod->m_ModDirectory) / MOD_OVERRIDE_DIR).string();
}
}
else // push compiled to head
- AddSearchPath(&*(*g_pFilesystem), fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
+ o_pAddSearchPath(g_pFilesystem, fs::absolute(GetCompiledAssetsPath()).string().c_str(), "GAME", PATH_ADD_TO_HEAD);
}
bool TryReplaceFile(const char* pPath, bool shouldCompile)
@@ -97,22 +94,17 @@ bool TryReplaceFile(const char* pPath, bool shouldCompile)
}
// force modded files to be read from mods, not cache
-// clang-format off
-HOOK(ReadFromCacheHook, ReadFromCache,
-bool, __fastcall, (IFileSystem * filesystem, char* pPath, void* result))
-// clang-format off
+static bool(__fastcall* o_pReadFromCache)(IFileSystem* filesystem, char* pPath, void* result) = nullptr;
+static bool __fastcall h_ReadFromCache(IFileSystem* filesystem, char* pPath, void* result)
{
if (TryReplaceFile(pPath, true))
return false;
- return ReadFromCache(filesystem, pPath, result);
+ return o_pReadFromCache(filesystem, pPath, result);
}
-// force modded files to be read from mods, not vpk
-// clang-format off
-AUTOHOOK(ReadFileFromVPK, filesystem_stdio.dll + 0x5CBA0,
-FileHandle_t, __fastcall, (VPKData* vpkInfo, uint64_t* b, char* filename))
-// clang-format on
+static FileHandle_t(__fastcall* o_pReadFileFromVPK)(VPKData* vpkInfo, uint64_t* b, char* filename) = nullptr;
+static FileHandle_t __fastcall h_ReadFileFromVPK(VPKData* vpkInfo, uint64_t* b, char* filename)
{
// don't compile here because this is only ever called from OpenEx, which already compiles
if (TryReplaceFile(filename, false))
@@ -121,22 +113,24 @@ FileHandle_t, __fastcall, (VPKData* vpkInfo, uint64_t* b, char* filename))
return b;
}
- return ReadFileFromVPK(vpkInfo, b, filename);
+ return o_pReadFileFromVPK(vpkInfo, b, filename);
}
-// clang-format off
-AUTOHOOK(CBaseFileSystem__OpenEx, filesystem_stdio.dll + 0x15F50,
-FileHandle_t, __fastcall, (IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char **ppszResolvedFilename))
-// clang-format on
+static FileHandle_t(__fastcall* o_pCBaseFileSystem__OpenEx)(
+ IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char** ppszResolvedFilename) =
+ nullptr;
+static FileHandle_t __fastcall h_CBaseFileSystem__OpenEx(
+ IFileSystem* filesystem, const char* pPath, const char* pOptions, uint32_t flags, const char* pPathID, char** ppszResolvedFilename)
{
TryReplaceFile(pPath, true);
- return CBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename);
+ return o_pCBaseFileSystem__OpenEx(filesystem, pPath, pOptions, flags, pPathID, ppszResolvedFilename);
}
-HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char* pVpkPath))
+static VPKData* (*o_pMountVPK)(IFileSystem* fileSystem, const char* pVpkPath) = nullptr;
+static VPKData* h_MountVPK(IFileSystem* fileSystem, const char* pVpkPath)
{
NS::log::fs->info("MountVPK {}", pVpkPath);
- VPKData* ret = MountVPK(fileSystem, pVpkPath);
+ VPKData* ret = o_pMountVPK(fileSystem, pVpkPath);
for (Mod mod : g_pModManager->m_LoadedMods)
{
@@ -156,7 +150,7 @@ HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char*
continue;
}
- VPKData* loaded = MountVPK(fileSystem, vpkEntry.m_sVpkPath.c_str());
+ VPKData* loaded = o_pMountVPK(fileSystem, vpkEntry.m_sVpkPath.c_str());
if (!ret) // this is primarily for map vpks and stuff, so the map's vpk is what gets returned from here
ret = loaded;
}
@@ -167,11 +161,18 @@ HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char*
ON_DLL_LOAD("filesystem_stdio.dll", Filesystem, (CModule module))
{
- AUTOHOOK_DISPATCH()
+ o_pReadFileFromVPK = module.Offset(0x5CBA0).RCast<decltype(o_pReadFileFromVPK)>();
+ HookAttach(&(PVOID&)o_pReadFileFromVPK, (PVOID)h_ReadFileFromVPK);
+
+ o_pCBaseFileSystem__OpenEx = module.Offset(0x15F50).RCast<decltype(o_pCBaseFileSystem__OpenEx)>();
+ HookAttach(&(PVOID&)o_pCBaseFileSystem__OpenEx, (PVOID)h_CBaseFileSystem__OpenEx);
- g_pFilesystem = new SourceInterface<IFileSystem>("filesystem_stdio.dll", "VFileSystem017");
+ g_pFilesystem = Sys_GetFactoryPtr("filesystem_stdio.dll", "VFileSystem017").RCast<IFileSystem*>();
- AddSearchPathHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->AddSearchPath);
- ReadFromCacheHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->ReadFromCache);
- MountVPKHook.Dispatch((LPVOID)(*g_pFilesystem)->m_vtable->MountVPK);
+ o_pAddSearchPath = reinterpret_cast<decltype(o_pAddSearchPath)>(g_pFilesystem->m_vtable->AddSearchPath);
+ HookAttach(&(PVOID&)o_pAddSearchPath, (PVOID)h_AddSearchPath);
+ o_pReadFromCache = reinterpret_cast<decltype(o_pReadFromCache)>(g_pFilesystem->m_vtable->ReadFromCache);
+ HookAttach(&(PVOID&)o_pReadFromCache, (PVOID)h_ReadFromCache);
+ o_pMountVPK = reinterpret_cast<decltype(o_pMountVPK)>(g_pFilesystem->m_vtable->MountVPK);
+ HookAttach(&(PVOID&)o_pMountVPK, (PVOID)h_MountVPK);
}
diff --git a/primedev/core/filesystem/filesystem.h b/primedev/core/filesystem/filesystem.h
index 4e2c18d9..2c592b3f 100644
--- a/primedev/core/filesystem/filesystem.h
+++ b/primedev/core/filesystem/filesystem.h
@@ -1,5 +1,4 @@
#pragma once
-#include "core/sourceinterface.h"
// taken from ttf2sdk
typedef void* FileHandle_t;
@@ -48,7 +47,7 @@ public:
VTable2* m_vtable2;
};
-extern SourceInterface<IFileSystem>* g_pFilesystem;
+extern IFileSystem* g_pFilesystem;
std::string ReadVPKFile(const char* path);
std::string ReadVPKOriginalFile(const char* path);
diff --git a/primedev/core/filesystem/rpakfilesystem.cpp b/primedev/core/filesystem/rpakfilesystem.cpp
index da72646b..c3e5e74e 100644
--- a/primedev/core/filesystem/rpakfilesystem.cpp
+++ b/primedev/core/filesystem/rpakfilesystem.cpp
@@ -2,206 +2,447 @@
#include "mods/modmanager.h"
#include "dedicated/dedicated.h"
#include "core/tier0.h"
+#include "util/utils.h"
-AUTOHOOK_INIT()
-
-// there are more i'm just too lazy to add
+#pragma pack(push, 1)
struct PakLoadFuncs
{
- void* unk0[3];
- int (*LoadPakAsync)(const char* pPath, void* unknownSingleton, int flags, void* callback0, void* callback1);
- void* unk1[2];
- void* (*UnloadPak)(int iPakHandle, void* callback);
- void* unk2[6];
- void* (*LoadFile)(const char* path); // unsure
- void* unk3[10];
- void* (*ReadFileAsync)(const char* pPath, void* a2);
+ void (*InitRpakSystem)();
+ void (*AddAssetLoaderWithJobDetails)(/*assetTypeHeader*/ void*, uint32_t, int);
+ void (*AddAssetLoader)(/*assetTypeHeader*/ void*);
+ PakHandle (*LoadRpakFileAsync)(const char* pPath, void* allocator, int flags);
+ void (*LoadRpakFile)(const char*, __int64(__fastcall*)(), __int64, void(__cdecl*)());
+ __int64 qword28;
+ void (*UnloadPak)(PakHandle iPakHandle, void* callback);
+ __int64 qword38;
+ __int64 qword40;
+ __int64 qword48;
+ __int64 qword50;
+ FARPROC (*GetDllCallback)(__int16 a1, const CHAR* a2);
+ __int64 (*GetAssetByHash)(__int64 hash);
+ __int64 (*GetAssetByName)(const char* a1);
+ __int64 qword70;
+ __int64 qword78;
+ __int64 qword80;
+ __int64 qword88;
+ __int64 qword90;
+ __int64 qword98;
+ __int64 qwordA0;
+ __int64 qwordA8;
+ __int64 qwordB0;
+ __int64 qwordBB;
+ void* (*OpenFile)(const char* pPath);
+ __int64 CloseFile;
+ __int64 qwordD0;
+ __int64 FileReadAsync;
+ __int64 ComplexFileReadAsync;
+ __int64 GetReadJobState;
+ __int64 WaitForFileReadJobComplete;
+ __int64 CancelFileReadJob;
+ __int64 CancelFileReadJobAsync;
+ __int64 qword108;
};
+static_assert(sizeof(PakLoadFuncs) == 0x110);
+#pragma pack(pop)
PakLoadFuncs* g_pakLoadApi;
-
PakLoadManager* g_pPakLoadManager;
-void** pUnknownPakLoadSingleton;
-int PakLoadManager::LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource)
+static char* pszCurrentMapRpakPath = nullptr;
+static PakHandle* piCurrentMapRpakHandle = nullptr;
+static PakHandle* piCurrentMapPatchRpakHandle = nullptr;
+static /*CModelLoader*/ void** ppModelLoader = nullptr;
+static void** rpakMemoryAllocator = nullptr;
+
+static __int64 (*o_pLoadGametypeSpecificRpaks)(const char* levelName) = nullptr;
+static __int64 (**o_pCleanMaterialSystemStuff)() = nullptr;
+static __int64 (**o_pCModelLoader_UnreferenceAllModels)(/*CModelLoader*/ void* a1) = nullptr;
+static char* (*o_pLoadlevelLoadscreen)(const char* levelName) = nullptr;
+static unsigned int (*o_pGetPakPatchNumber)(const char* pPakPath) = nullptr;
+
+// Marks all mod Paks to be unloaded on next map load.
+// Also cleans up any mod Paks that are already unloaded.
+void PakLoadManager::UnloadAllModPaks()
{
- int nHandle = g_pakLoadApi->LoadPakAsync(pPath, *pUnknownPakLoadSingleton, 2, nullptr, nullptr);
-
- // set the load source of the pak we just loaded
- if (nHandle != -1)
- GetPakInfo(nHandle)->m_nLoadSource = nLoadSource;
-
- return nHandle;
+ NS::log::rpak->info("Reloading RPaks on next map load...");
+ for (auto& modPak : m_modPaks)
+ {
+ modPak.m_markedForDelete = true;
+ }
+ // clean up any paks that are both marked for unload and already unloaded
+ CleanUpUnloadedPaks();
+ SetForceReloadOnMapLoad(true);
}
-void PakLoadManager::UnloadPak(const int nPakHandle)
+// Tracks all Paks related to a mod.
+void PakLoadManager::TrackModPaks(Mod& mod)
{
- g_pakLoadApi->UnloadPak(nPakHandle, nullptr);
+ const fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
+
+ for (auto& modRpakEntry : mod.Rpaks)
+ {
+ ModPak_t pak;
+ pak.m_modName = mod.Name;
+ pak.m_path = (modPakPath / modRpakEntry.m_pakName).string();
+ pak.m_pathHash = STR_HASH(pak.m_path);
+
+ pak.m_preload = modRpakEntry.m_preload;
+ pak.m_dependentPakHash = modRpakEntry.m_dependentPakHash;
+ pak.m_mapRegex = modRpakEntry.m_loadRegex;
+
+ m_modPaks.push_back(pak);
+ }
}
-void PakLoadManager::UnloadMapPaks()
+// Untracks all paks that aren't currently loaded and are marked for unload.
+void PakLoadManager::CleanUpUnloadedPaks()
{
- for (auto& pair : m_vLoadedPaks)
- if (pair.second.m_nLoadSource == ePakLoadSource::MAP)
- UnloadPak(pair.first);
+ auto fnRemovePredicate = [](ModPak_t& pak) -> bool { return pak.m_markedForDelete && pak.m_handle == PakHandle::INVALID; };
+
+ m_modPaks.erase(std::remove_if(m_modPaks.begin(), m_modPaks.end(), fnRemovePredicate), m_modPaks.end());
}
-LoadedPak* PakLoadManager::TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash)
+// Unloads all paks that are marked for unload.
+void PakLoadManager::UnloadMarkedPaks()
{
- LoadedPak pak;
- pak.m_nLoadSource = nLoadSource;
- pak.m_nPakHandle = nPakHandle;
- pak.m_nPakNameHash = nPakNameHash;
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_handle == PakHandle::INVALID || !modPak.m_markedForDelete)
+ continue;
- m_vLoadedPaks.insert(std::make_pair(nPakHandle, pak));
- return &m_vLoadedPaks.at(nPakHandle);
+ g_pakLoadApi->UnloadPak(modPak.m_handle, *o_pCleanMaterialSystemStuff);
+ modPak.m_handle = PakHandle::INVALID;
+ }
}
-void PakLoadManager::RemoveLoadedPak(int nPakHandle)
+// Loads all modded paks for the given map.
+void PakLoadManager::LoadModPaksForMap(const char* mapName)
{
- m_vLoadedPaks.erase(nPakHandle);
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ for (auto& modPak : m_modPaks)
+ {
+ // don't load paks that are already loaded
+ if (modPak.m_handle != PakHandle::INVALID)
+ continue;
+ std::cmatch matches;
+ if (!std::regex_match(mapName, matches, modPak.m_mapRegex))
+ continue;
+
+ modPak.m_handle = g_pakLoadApi->LoadRpakFileAsync(modPak.m_path.c_str(), *rpakMemoryAllocator, 7);
+ m_mapPaks.push_back(modPak.m_pathHash);
+ }
}
-LoadedPak* PakLoadManager::GetPakInfo(const int nPakHandle)
+// Unloads all modded map paks.
+void PakLoadManager::UnloadModPaks()
{
- return &m_vLoadedPaks.at(nPakHandle);
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+
+ for (auto& modPak : m_modPaks)
+ {
+ for (auto it = m_mapPaks.begin(); it != m_mapPaks.end(); ++it)
+ {
+ if (*it != modPak.m_pathHash)
+ continue;
+
+ m_mapPaks.erase(it, it + 1);
+ g_pakLoadApi->UnloadPak(modPak.m_handle, *o_pCleanMaterialSystemStuff);
+ modPak.m_handle = PakHandle::INVALID;
+ break;
+ }
+ }
+
+ // If this has happened, we may have leaked a pak?
+ // It basically means that none of the entries in m_modPaks matched the hash in m_mapPaks so we didn't end up unloading it
+ assert_msg(m_mapPaks.size() == 0, "Not all map paks were unloaded?");
}
-int PakLoadManager::GetPakHandle(const size_t nPakNameHash)
+// Called after a Pak was loaded.
+void PakLoadManager::OnPakLoaded(std::string& originalPath, std::string& resultingPath, PakHandle resultingHandle)
{
- for (auto& pair : m_vLoadedPaks)
- if (pair.second.m_nPakNameHash == nPakNameHash)
- return pair.first;
+ if (IsVanillaCall())
+ {
+ // add entry to loaded vanilla rpaks
+ m_vanillaPaks.emplace_back(originalPath, resultingHandle);
+ }
- return -1;
+ LoadDependentPaks(resultingPath, resultingHandle);
}
-int PakLoadManager::GetPakHandle(const char* pPath)
+// Called before a Pak was unloaded.
+void PakLoadManager::OnPakUnloading(PakHandle handle)
{
- return GetPakHandle(STR_HASH(pPath));
+ UnloadDependentPaks(handle);
+
+ if (IsVanillaCall())
+ {
+ // remove entry from loaded vanilla rpaks
+ auto fnRemovePredicate = [handle](std::pair<std::string, PakHandle>& pair) -> bool { return pair.second == handle; };
+
+ m_vanillaPaks.erase(std::remove_if(m_vanillaPaks.begin(), m_vanillaPaks.end(), fnRemovePredicate), m_vanillaPaks.end());
+
+ // no need to handle aliasing here, if vanilla wants it gone, it's gone
+ }
+ else
+ {
+ // note: aliasing is handled the old way, long term todo: move it over to the PakLoadManager
+ // handle the potential unloading of an aliased vanilla rpak (we aliased it, and we are now unloading the alias, so we should load
+ // the vanilla one again)
+ // for (auto& [path, resultingHandle] : m_vanillaPaks)
+ //{
+ // if (resultingHandle != handle)
+ // continue;
+
+ // // load vanilla rpak
+ //}
+ }
+
+ // set handle of the mod pak (if any) that has this handle for proper tracking
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_handle == handle)
+ modPak.m_handle = PakHandle::INVALID;
+ }
}
-void* PakLoadManager::LoadFile(const char* path)
+// Whether the vanilla game has this rpak
+static bool VanillaHasPak(const char* pakName)
{
- return g_pakLoadApi->LoadFile(path);
+ fs::path originalPath = fs::path("./r2/paks/Win64") / pakName;
+ unsigned int highestPatch = o_pGetPakPatchNumber(pakName);
+ if (highestPatch)
+ {
+ // add the patch path to the extension
+ char buf[16];
+ snprintf(buf, sizeof(buf), "(%02u).rpak", highestPatch);
+ // remove the .rpak and add the new suffix
+ originalPath = originalPath.replace_extension().string() + buf;
+ }
+ else
+ {
+ originalPath /= pakName;
+ }
+
+ return fs::exists(originalPath);
}
-void HandlePakAliases(char** map)
+// If vanilla doesn't have an rpak for this path, tries to map it to a modded rpak of the same name.
+void PakLoadManager::FixupPakPath(std::string& pakPath)
{
- // convert the pak being loaded to it's aliased one, e.g. aliasing mp_hub_timeshift => sp_hub_timeshift
- for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--)
+ if (VanillaHasPak(pakPath.c_str()))
+ return;
+
+ for (ModPak_t& modPak : m_modPaks)
{
- Mod* mod = &g_pModManager->m_LoadedMods[i];
- if (!mod->m_bEnabled)
+ if (modPak.m_markedForDelete)
continue;
- if (mod->RpakAliases.find(*map) != mod->RpakAliases.end())
+ fs::path modPakFilename = fs::path(modPak.m_path).filename();
+ if (pakPath == modPakFilename.string())
{
- *map = &mod->RpakAliases[*map][0];
+ pakPath = modPak.m_path;
return;
}
}
}
-void LoadPreloadPaks()
+// Loads all "Preload" Paks. todo: deprecate Preload.
+void PakLoadManager::LoadPreloadPaks()
{
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ for (auto& modPak : m_modPaks)
{
- if (!mod.m_bEnabled)
+ if (modPak.m_markedForDelete || modPak.m_handle != PakHandle::INVALID || !modPak.m_preload)
continue;
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
+ modPak.m_handle = g_pakLoadApi->LoadRpakFileAsync(modPak.m_path.c_str(), *rpakMemoryAllocator, 7);
+ }
+}
- for (ModRpakEntry& pak : mod.Rpaks)
- if (pak.m_bAutoLoad)
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT);
+// Causes all "Postload" paks to be loaded again.
+void PakLoadManager::ReloadPostloadPaks()
+{
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ // pretend that we just loaded all of these vanilla paks
+ for (auto& [path, handle] : m_vanillaPaks)
+ {
+ LoadDependentPaks(path, handle);
}
}
-void LoadPostloadPaks(const char* pPath)
+// Wrapper for Pak load API.
+void* PakLoadManager::OpenFile(const char* path)
{
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
+ return g_pakLoadApi->OpenFile(path);
+}
+
+// Loads Paks that depend on this Pak.
+void PakLoadManager::LoadDependentPaks(std::string& path, PakHandle handle)
+{
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
+
+ const size_t hash = STR_HASH(path);
+ for (auto& modPak : m_modPaks)
{
- if (!mod.m_bEnabled)
+ if (modPak.m_handle != PakHandle::INVALID)
+ continue;
+ if (modPak.m_dependentPakHash != hash)
continue;
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
-
- for (ModRpakEntry& pak : mod.Rpaks)
- if (pak.m_sLoadAfterPak == pPath)
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::CONSTANT);
+ // load pak
+ modPak.m_handle = g_pakLoadApi->LoadRpakFileAsync(modPak.m_path.c_str(), *rpakMemoryAllocator, 7);
+ m_dependentPaks.emplace_back(handle, hash);
}
}
-void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName)
+// Unloads Paks that depend on this Pak.
+void PakLoadManager::UnloadDependentPaks(PakHandle handle)
{
- // whether the vanilla game has this rpak
- bool bHasOriginalPak = fs::exists(fs::path("r2/paks/Win64/") / *pakName);
+ ++m_reentranceCounter;
+ const ScopeGuard guard([&]() { --m_reentranceCounter; });
- // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks
- for (Mod& mod : g_pModManager->m_LoadedMods)
+ auto fnRemovePredicate = [&](std::pair<PakHandle, size_t>& pair) -> bool
{
- if (!mod.m_bEnabled)
- continue;
+ if (pair.first != handle)
+ return false;
- // need to get a relative path of mod to mod folder
- fs::path modPakPath("./" / mod.m_ModDirectory / "paks");
+ for (auto& modPak : m_modPaks)
+ {
+ if (modPak.m_pathHash != pair.second || modPak.m_handle == PakHandle::INVALID)
+ continue;
- for (ModRpakEntry& pak : mod.Rpaks)
+ // unload pak
+ g_pakLoadApi->UnloadPak(modPak.m_handle, *o_pCleanMaterialSystemStuff);
+ modPak.m_handle = PakHandle::INVALID;
+ }
+
+ return true;
+ };
+ m_dependentPaks.erase(std::remove_if(m_dependentPaks.begin(), m_dependentPaks.end(), fnRemovePredicate), m_dependentPaks.end());
+}
+
+// Handles aliases for rpaks defined in rpak.json, effectively redirecting an rpak load to a different path.
+static void HandlePakAliases(std::string& originalPath)
+{
+ // convert the pak being loaded to its aliased one, e.g. aliasing mp_hub_timeshift => sp_hub_timeshift
+ for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--)
+ {
+ Mod* mod = &g_pModManager->m_LoadedMods[i];
+ if (!mod->m_bEnabled)
+ continue;
+
+ if (mod->RpakAliases.find(originalPath) != mod->RpakAliases.end())
{
- if (!pak.m_bAutoLoad && !pak.m_sPakName.compare(*pakName))
- {
- // if the game doesn't have the original pak, let it handle loading this one as if it was the one it was loading originally
- if (!bHasOriginalPak)
- {
- std::string path = (modPakPath / pak.m_sPakName).string();
- *pakName = new char[path.size() + 1];
- strcpy(*pakName, &path[0]);
- (*pakName)[path.size()] = '\0';
-
- bHasOriginalPak = true;
- *bNeedToFreePakName =
- true; // we can't free this memory until we're done with the pak, so let whatever's calling this deal with it
- }
- else
- g_pPakLoadManager->LoadPakAsync((modPakPath / pak.m_sPakName).string().c_str(), ePakLoadSource::MAP);
- }
+ originalPath = mod->RpakAliases[originalPath];
+ return;
}
}
}
-// clang-format off
-HOOK(LoadPakAsyncHook, LoadPakAsync,
-int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallback0, void* pCallback1))
-// clang-format on
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool (*o_pLoadMapRpaks)(char* mapPath) = nullptr;
+static bool h_LoadMapRpaks(char* mapPath)
{
- HandlePakAliases(&pPath);
+ // unload all mod rpaks that are marked for unload
+ g_pPakLoadManager->UnloadMarkedPaks();
+ g_pPakLoadManager->CleanUpUnloadedPaks();
+
+ // strip file extension
+ const std::string mapName = fs::path(mapPath).replace_extension().string();
+
+ // load mp_common, sp_common etc.
+ o_pLoadGametypeSpecificRpaks(mapName.c_str());
- // dont load the pak if it's currently loaded already
- size_t nPathHash = STR_HASH(pPath);
- if (g_pPakLoadManager->GetPakHandle(nPathHash) != -1)
- return -1;
+ // unload old modded map paks
+ g_pPakLoadManager->UnloadModPaks();
+ // load modded map paks
+ g_pPakLoadManager->LoadModPaksForMap(mapName.c_str());
- bool bNeedToFreePakName = false;
+ // don't load/unload anything when going to the lobby, presumably to save load times when going back to the same map
+ if (!g_pPakLoadManager->GetForceReloadOnMapLoad() && !strcmp("mp_lobby", mapName.c_str()))
+ return false;
- static bool bShouldLoadPaks = true;
- if (bShouldLoadPaks)
+ if (g_pPakLoadManager->GetForceReloadOnMapLoad())
{
- // make a copy of the path for comparing to determine whether we should load this pak on dedi, before it could get overwritten by
- // LoadCustomMapPaks
- std::string originalPath(pPath);
+ g_pPakLoadManager->LoadPreloadPaks();
+ g_pPakLoadManager->ReloadPostloadPaks();
+ }
+
+ char mapRpakStr[272];
+ snprintf(mapRpakStr, 272, "%s.rpak", mapName.c_str());
+
+ // if level being loaded is the same as current level, do nothing
+ if (!g_pPakLoadManager->GetForceReloadOnMapLoad() && !strcmp(mapRpakStr, pszCurrentMapRpakPath))
+ return true;
+
+ strcpy(pszCurrentMapRpakPath, mapRpakStr);
- // disable preloading while we're doing this
- bShouldLoadPaks = false;
+ (*o_pCleanMaterialSystemStuff)();
+ o_pLoadlevelLoadscreen(mapName.c_str());
- LoadPreloadPaks();
- LoadCustomMapPaks(&pPath, &bNeedToFreePakName);
+ // unload old map rpaks
+ PakHandle curHandle = *piCurrentMapRpakHandle;
+ PakHandle curPatchHandle = *piCurrentMapPatchRpakHandle;
+ if (curHandle != PakHandle::INVALID)
+ {
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+ g_pakLoadApi->UnloadPak(curHandle, *o_pCleanMaterialSystemStuff);
+ *piCurrentMapRpakHandle = PakHandle::INVALID;
+ }
+ if (curPatchHandle != PakHandle::INVALID)
+ {
+ (*o_pCModelLoader_UnreferenceAllModels)(*ppModelLoader);
+ (*o_pCleanMaterialSystemStuff)();
+ g_pakLoadApi->UnloadPak(curPatchHandle, *o_pCleanMaterialSystemStuff);
+ *piCurrentMapPatchRpakHandle = PakHandle::INVALID;
+ }
- bShouldLoadPaks = true;
+ *piCurrentMapRpakHandle = g_pakLoadApi->LoadRpakFileAsync(mapRpakStr, *rpakMemoryAllocator, 7);
+
+ // load special _patch rpak (seemingly used for dev things?)
+ char levelPatchRpakStr[272];
+ snprintf(levelPatchRpakStr, 272, "%s_patch.rpak", mapName.c_str());
+ *piCurrentMapPatchRpakHandle = g_pakLoadApi->LoadRpakFileAsync(levelPatchRpakStr, *rpakMemoryAllocator, 7);
+
+ // we just reloaded the paks, so we don't need to force it again
+ g_pPakLoadManager->SetForceReloadOnMapLoad(false);
+ return true;
+}
+
+// clang-format off
+HOOK(LoadPakAsyncHook, LoadPakAsync,
+PakHandle, __fastcall, (const char* pPath, void* memoryAllocator, int flags))
+// clang-format on
+{
+ // make a copy of the path for comparing to determine whether we should load this pak on dedi, before it could get overwritten
+ std::string svOriginalPath(pPath);
+
+ std::string resultingPath(pPath);
+ HandlePakAliases(resultingPath);
+
+ if (g_pPakLoadManager->IsVanillaCall())
+ {
+ g_pPakLoadManager->LoadPreloadPaks();
+ g_pPakLoadManager->FixupPakPath(resultingPath);
// do this after custom paks load and in bShouldLoadPaks so we only ever call this on the root pakload call
// todo: could probably add some way to flag custom paks to not be loaded on dedicated servers in rpak.json
@@ -210,44 +451,27 @@ int, __fastcall, (char* pPath, void* unknownSingleton, int flags, void* pCallbac
// sp_<map> rpaks contain tutorial ghost data
// sucks to have to load the entire rpak for that but sp was never meant to be done on dedi
if (IsDedicatedServer() &&
- (CommandLine()->CheckParm("-nopakdedi") || strncmp(&originalPath[0], "common", 6) && strncmp(&originalPath[0], "sp_", 3)))
+ (CommandLine()->CheckParm("-nopakdedi") || strncmp(&svOriginalPath[0], "common", 6) && strncmp(&svOriginalPath[0], "sp_", 3)))
{
- if (bNeedToFreePakName)
- delete[] pPath;
-
- NS::log::rpak->info("Not loading pak {} for dedicated server", originalPath);
- return -1;
+ NS::log::rpak->info("Not loading pak {} for dedicated server", svOriginalPath);
+ return PakHandle::INVALID;
}
}
- int iPakHandle = LoadPakAsync(pPath, unknownSingleton, flags, pCallback0, pCallback1);
- NS::log::rpak->info("LoadPakAsync {} {}", pPath, iPakHandle);
-
- // trak the pak
- g_pPakLoadManager->TrackLoadedPak(ePakLoadSource::UNTRACKED, iPakHandle, nPathHash);
- LoadPostloadPaks(pPath);
+ PakHandle iPakHandle = LoadPakAsync(resultingPath.c_str(), memoryAllocator, flags);
+ NS::log::rpak->info("LoadPakAsync {} {}", resultingPath, iPakHandle);
- if (bNeedToFreePakName)
- delete[] pPath;
+ g_pPakLoadManager->OnPakLoaded(svOriginalPath, resultingPath, iPakHandle);
return iPakHandle;
}
// clang-format off
HOOK(UnloadPakHook, UnloadPak,
-void*, __fastcall, (int nPakHandle, void* pCallback))
+void*, __fastcall, (PakHandle nPakHandle, void* pCallback))
// clang-format on
{
- // stop tracking the pak
- g_pPakLoadManager->RemoveLoadedPak(nPakHandle);
-
- static bool bShouldUnloadPaks = true;
- if (bShouldUnloadPaks)
- {
- bShouldUnloadPaks = false;
- g_pPakLoadManager->UnloadMapPaks();
- bShouldUnloadPaks = true;
- }
+ g_pPakLoadManager->OnPakUnloading(nPakHandle);
NS::log::rpak->info("UnloadPak {}", nPakHandle);
return UnloadPak(nPakHandle, pCallback);
@@ -256,7 +480,7 @@ void*, __fastcall, (int nPakHandle, void* pCallback))
// we hook this exclusively for resolving stbsp paths, but seemingly it's also used for other stuff like vpk, rpak, mprj and starpak loads
// tbh this actually might be for memory mapped files or something, would make sense i think
// clang-format off
-HOOK(ReadFileAsyncHook, ReadFileAsync,
+HOOK(OpenFileHook, o_pOpenFile,
void*, __fastcall, (const char* pPath, void* pCallback))
// clang-format on
{
@@ -329,19 +553,34 @@ void*, __fastcall, (const char* pPath, void* pCallback))
NS::log::rpak->info("LoadStreamPak: {}", filename.string());
}
- return ReadFileAsync(pPath, pCallback);
+ return o_pOpenFile(pPath, pCallback);
}
ON_DLL_LOAD("engine.dll", RpakFilesystem, (CModule module))
{
- AUTOHOOK_DISPATCH();
-
g_pPakLoadManager = new PakLoadManager;
g_pakLoadApi = module.Offset(0x5BED78).Deref().RCast<PakLoadFuncs*>();
- pUnknownPakLoadSingleton = module.Offset(0x7C5E20).RCast<void**>();
- LoadPakAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->LoadPakAsync);
+ LoadPakAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->LoadRpakFileAsync);
UnloadPakHook.Dispatch((LPVOID*)g_pakLoadApi->UnloadPak);
- ReadFileAsyncHook.Dispatch((LPVOID*)g_pakLoadApi->ReadFileAsync);
+ OpenFileHook.Dispatch((LPVOID*)g_pakLoadApi->OpenFile);
+
+ pszCurrentMapRpakPath = module.Offset(0x1315C3E0).RCast<decltype(pszCurrentMapRpakPath)>();
+ piCurrentMapRpakHandle = module.Offset(0x7CB5A0).RCast<decltype(piCurrentMapRpakHandle)>();
+ piCurrentMapPatchRpakHandle = module.Offset(0x7CB5A4).RCast<decltype(piCurrentMapPatchRpakHandle)>();
+ ppModelLoader = module.Offset(0x7C4AC0).RCast<decltype(ppModelLoader)>();
+ rpakMemoryAllocator = module.Offset(0x7C5E20).RCast<decltype(rpakMemoryAllocator)>();
+
+ o_pLoadGametypeSpecificRpaks = module.Offset(0x15AD20).RCast<decltype(o_pLoadGametypeSpecificRpaks)>();
+ o_pCleanMaterialSystemStuff = module.Offset(0x12A11F00).RCast<decltype(o_pCleanMaterialSystemStuff)>();
+ o_pCModelLoader_UnreferenceAllModels = module.Offset(0x5ED580).RCast<decltype(o_pCModelLoader_UnreferenceAllModels)>();
+ o_pLoadlevelLoadscreen = module.Offset(0x15A810).RCast<decltype(o_pLoadlevelLoadscreen)>();
+
+ o_pLoadMapRpaks = module.Offset(0x15A8C0).RCast<decltype(o_pLoadMapRpaks)>();
+ HookAttach(&(PVOID&)o_pLoadMapRpaks, (PVOID)h_LoadMapRpaks);
+
+ // kinda bad, doing things in rtech in an engine callback but it seems fine for now
+ CModule rtechModule(GetModuleHandleA("rtech_game.dll"));
+ o_pGetPakPatchNumber = rtechModule.Offset(0x9A00).RCast<decltype(o_pGetPakPatchNumber)>();
}
diff --git a/primedev/core/filesystem/rpakfilesystem.h b/primedev/core/filesystem/rpakfilesystem.h
index bcd57a73..87a41e7b 100644
--- a/primedev/core/filesystem/rpakfilesystem.h
+++ b/primedev/core/filesystem/rpakfilesystem.h
@@ -1,39 +1,81 @@
#pragma once
-enum class ePakLoadSource
-{
- UNTRACKED = -1, // not a pak we loaded, we shouldn't touch this one
+#include <regex>
- CONSTANT, // should be loaded at all times
- MAP // loaded from a map, should be unloaded when the map is unloaded
+enum PakHandle : int
+{
+ INVALID = -1,
};
-struct LoadedPak
+struct ModPak_t
{
- ePakLoadSource m_nLoadSource;
- int m_nPakHandle;
- size_t m_nPakNameHash;
+ std::string m_modName;
+
+ std::string m_path;
+ size_t m_pathHash = 0;
+
+ // If the map being loaded matches this regex, this pak will be loaded.
+ std::regex m_mapRegex;
+ // If a pak with a hash matching this is loaded, this pak will be loaded.
+ size_t m_dependentPakHash = 0;
+ // If this is set, this pak will be loaded whenever any other pak is loaded.
+ bool m_preload = false;
+
+ // If this is set, the Pak will be unloaded on next map load
+ bool m_markedForDelete = false;
+ // The current rpak handle associated with this Pak
+ PakHandle m_handle = PakHandle::INVALID;
};
class PakLoadManager
{
-private:
- std::map<int, LoadedPak> m_vLoadedPaks {};
- std::unordered_map<size_t, int> m_HashToPakHandle {};
-
public:
- int LoadPakAsync(const char* pPath, const ePakLoadSource nLoadSource);
- void UnloadPak(const int nPakHandle);
- void UnloadMapPaks();
- void* LoadFile(const char* path); // this is a guess
+ void UnloadAllModPaks();
+ void TrackModPaks(Mod& mod);
+
+ void CleanUpUnloadedPaks();
+ void UnloadMarkedPaks();
+
+ void LoadModPaksForMap(const char* mapName);
+ void UnloadModPaks();
+
+ // Whether the current context is a vanilla call to a function, or a modded one
+ bool IsVanillaCall() const { return m_reentranceCounter == 0; }
+ // Whether paks will be forced to reload on the next map load
+ bool GetForceReloadOnMapLoad() const { return m_forceReloadOnMapLoad; }
+ void SetForceReloadOnMapLoad(bool value) { m_forceReloadOnMapLoad = value; }
+
+ void OnPakLoaded(std::string& originalPath, std::string& resultingPath, PakHandle resultingHandle);
+ void OnPakUnloading(PakHandle handle);
+
+ void FixupPakPath(std::string& path);
+
+ void LoadPreloadPaks();
+ void ReloadPostloadPaks();
+
+ void* OpenFile(const char* path);
+
+private:
+ void LoadDependentPaks(std::string& path, PakHandle handle);
+ void UnloadDependentPaks(PakHandle handle);
- LoadedPak* TrackLoadedPak(ePakLoadSource nLoadSource, int nPakHandle, size_t nPakNameHash);
- void RemoveLoadedPak(int nPakHandle);
+ // All paks that vanilla has attempted to load. (they may have been aliased away)
+ // Also known as a list of rpaks that the vanilla game would have loaded at this point in time.
+ std::vector<std::pair<std::string, PakHandle>> m_vanillaPaks;
- LoadedPak* GetPakInfo(const int nPakHandle);
+ // All mod Paks that are currently tracked
+ std::vector<ModPak_t> m_modPaks;
+ // Hashes of the currently loaded map mod paks
+ std::vector<size_t> m_mapPaks;
+ // Currently loaded Pak path hashes that depend on a handle to remain loaded (Postload)
+ std::vector<std::pair<PakHandle, size_t>> m_dependentPaks;
- int GetPakHandle(const size_t nPakNameHash);
- int GetPakHandle(const char* pPath);
+ // Used to force rpaks to be unloaded and reloaded on the next map load.
+ // Vanilla behaviour is to not do this when loading into mp_lobby, or loading into the same map you were last in.
+ bool m_forceReloadOnMapLoad = false;
+ // Used to track if the current hook call is a vanilla call or not.
+ // When loading/unloading a mod Pak, increment this before doing so, and decrement afterwards.
+ int m_reentranceCounter = 0;
};
extern PakLoadManager* g_pPakLoadManager;
diff --git a/primedev/core/hooks.cpp b/primedev/core/hooks.cpp
index 5026f837..6146c93c 100644
--- a/primedev/core/hooks.cpp
+++ b/primedev/core/hooks.cpp
@@ -12,8 +12,6 @@
namespace fs = std::filesystem;
-AUTOHOOK_INIT()
-
// called from the ON_DLL_LOAD macros
__dllLoadCallback::__dllLoadCallback(
eDllLoadCallbackSide side, const std::string dllName, DllLoadCallbackFuncType callback, std::string uniqueStr, std::string reliesOn)
@@ -85,18 +83,18 @@ void __fileAutohook::DispatchForModule(const char* pModuleName)
hook->Dispatch();
}
-ManualHook::ManualHook(const char* funcName, LPVOID func) : pHookFunc(func), ppOrigFunc(nullptr)
+ManualHook::ManualHook(const char* funcName, LPVOID func)
+ : svFuncName(funcName)
+ , pHookFunc(func)
+ , ppOrigFunc(nullptr)
{
- const size_t iFuncNameStrlen = strlen(funcName);
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
}
-ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func) : pHookFunc(func), ppOrigFunc(orig)
+ManualHook::ManualHook(const char* funcName, LPVOID* orig, LPVOID func)
+ : svFuncName(funcName)
+ , pHookFunc(func)
+ , ppOrigFunc(orig)
{
- const size_t iFuncNameStrlen = strlen(funcName);
- pFuncName = new char[iFuncNameStrlen];
- memcpy(pFuncName, funcName, iFuncNameStrlen);
}
bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig)
@@ -105,19 +103,19 @@ bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig)
ppOrigFunc = orig;
if (!addr)
- spdlog::error("Address for hook {} is invalid", pFuncName);
+ spdlog::error("Address for hook {} is invalid", svFuncName);
else if (MH_CreateHook(addr, pHookFunc, ppOrigFunc) == MH_OK)
{
if (MH_EnableHook(addr) == MH_OK)
{
- spdlog::info("Enabling hook {}", pFuncName);
+ spdlog::info("Enabling hook {}", svFuncName);
return true;
}
else
- spdlog::error("MH_EnableHook failed for function {}", pFuncName);
+ spdlog::error("MH_EnableHook failed for function {}", svFuncName);
}
else
- spdlog::error("MH_CreateHook failed for function {}", pFuncName);
+ spdlog::error("MH_CreateHook failed for function {}", svFuncName);
return false;
}
@@ -223,14 +221,15 @@ void MakeHook(LPVOID pTarget, LPVOID pDetour, void* ppOriginal, const char* pFun
spdlog::error("MH_CreateHook failed for function {}", pStrippedFuncName);
}
-AUTOHOOK_ABSOLUTEADDR(_GetCommandLineA, (LPVOID)GetCommandLineA, LPSTR, WINAPI, ())
+static LPSTR(WINAPI* o_pGetCommandLineA)() = nullptr;
+static LPSTR WINAPI h_GetCommandLineA()
{
static char* cmdlineModified;
static char* cmdlineOrg;
if (cmdlineOrg == nullptr || cmdlineModified == nullptr)
{
- cmdlineOrg = _GetCommandLineA();
+ cmdlineOrg = o_pGetCommandLineA();
bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument
bool ignoreStartupArgs = strstr(cmdlineOrg, "-nostartupargs");
@@ -328,8 +327,6 @@ void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress)
void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress)
{
- CModule cModule(moduleAddress);
-
while (true)
{
bool bDoneCalling = true;
@@ -396,6 +393,7 @@ void HookSys_Init()
{
spdlog::error("MH_Initialize (minhook initialization) failed");
}
- // todo: remove remaining instances of autohook in this file
- AUTOHOOK_DISPATCH()
+
+ o_pGetCommandLineA = GetCommandLineA;
+ HookAttach(&(PVOID&)o_pGetCommandLineA, (PVOID)h_GetCommandLineA);
}
diff --git a/primedev/core/hooks.h b/primedev/core/hooks.h
index facf51bf..26d9483c 100644
--- a/primedev/core/hooks.h
+++ b/primedev/core/hooks.h
@@ -140,7 +140,9 @@ public:
__autohook() = delete;
__autohook(__fileAutohook* autohook, const char* funcName, LPVOID absoluteAddress, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig), iAbsoluteAddress(absoluteAddress)
+ : pHookFunc(func)
+ , ppOrigFunc(orig)
+ , iAbsoluteAddress(absoluteAddress)
{
iAddressResolutionMode = ABSOLUTE_ADDR;
@@ -152,7 +154,8 @@ public:
}
__autohook(__fileAutohook* autohook, const char* funcName, const char* addrString, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig)
+ : pHookFunc(func)
+ , ppOrigFunc(orig)
{
iAddressResolutionMode = OFFSET_STRING;
@@ -168,7 +171,8 @@ public:
}
__autohook(__fileAutohook* autohook, const char* funcName, const char* moduleName, const char* procName, LPVOID* orig, LPVOID func)
- : pHookFunc(func), ppOrigFunc(orig)
+ : pHookFunc(func)
+ , ppOrigFunc(orig)
{
iAddressResolutionMode = PROCADDRESS;
@@ -278,7 +282,7 @@ public:
class ManualHook
{
public:
- char* pFuncName;
+ std::string svFuncName;
LPVOID pHookFunc;
LPVOID* ppOrigFunc;
@@ -327,10 +331,7 @@ public:
pAutohook->vars.push_back(this);
}
- void Dispatch()
- {
- *m_pTarget = (void*)ParseDLLOffsetString(m_pAddrString);
- }
+ void Dispatch() { *m_pTarget = (void*)ParseDLLOffsetString(m_pAddrString); }
};
// VAR_AT(engine.dll+0x404, ConVar*, Cvar_host_timescale)
diff --git a/primedev/core/math/bitbuf.h b/primedev/core/math/bitbuf.h
index a06dab17..33056e63 100644
--- a/primedev/core/math/bitbuf.h
+++ b/primedev/core/math/bitbuf.h
@@ -90,25 +90,13 @@ enum EBitCoordType
class BitBufferBase
{
protected:
- INLINE void SetName(const char* name)
- {
- m_BufferName = name;
- }
+ INLINE void SetName(const char* name) { m_BufferName = name; }
public:
- INLINE bool IsOverflowed()
- {
- return m_Overflow;
- }
- INLINE void SetOverflowed()
- {
- m_Overflow = true;
- }
+ INLINE bool IsOverflowed() { return m_Overflow; }
+ INLINE void SetOverflowed() { m_Overflow = true; }
- INLINE const char* GetName()
- {
- return m_BufferName;
- }
+ INLINE const char* GetName() { return m_BufferName; }
private:
const char* m_BufferName = "";
@@ -437,28 +425,13 @@ public:
return fReturn;
}
- INLINE i32 ReadChar()
- {
- return ReadSBitLong(sizeof(char) << 3);
- }
- INLINE u32 ReadByte()
- {
- return ReadUBitLong(sizeof(unsigned char) << 3);
- }
+ INLINE i32 ReadChar() { return ReadSBitLong(sizeof(char) << 3); }
+ INLINE u32 ReadByte() { return ReadUBitLong(sizeof(unsigned char) << 3); }
- INLINE i32 ReadShort()
- {
- return ReadSBitLong(sizeof(short) << 3);
- }
- INLINE u32 ReadWord()
- {
- return ReadUBitLong(sizeof(unsigned short) << 3);
- }
+ INLINE i32 ReadShort() { return ReadSBitLong(sizeof(short) << 3); }
+ INLINE u32 ReadWord() { return ReadUBitLong(sizeof(unsigned short) << 3); }
- INLINE i32 ReadLong()
- {
- return (i32)(ReadUBitLong(sizeof(i32) << 3));
- }
+ INLINE i32 ReadLong() { return (i32)(ReadUBitLong(sizeof(i32) << 3)); }
INLINE float ReadFloat()
{
u32 temp = ReadUBitLong(sizeof(float) << 3);
@@ -687,24 +660,12 @@ public:
return std::min(nCurOfs + nAdjust, m_DataBits);
}
- INLINE bool SeekRelative(size_t offset)
- {
- return Seek(GetNumBitsRead() + offset);
- }
+ INLINE bool SeekRelative(size_t offset) { return Seek(GetNumBitsRead() + offset); }
- INLINE size_t TotalBytesAvailable()
- {
- return m_DataBytes;
- }
+ INLINE size_t TotalBytesAvailable() { return m_DataBytes; }
- INLINE size_t GetNumBitsLeft()
- {
- return m_DataBits - GetNumBitsRead();
- }
- INLINE size_t GetNumBytesLeft()
- {
- return GetNumBitsLeft() >> 3;
- }
+ INLINE size_t GetNumBitsLeft() { return m_DataBits - GetNumBitsRead(); }
+ INLINE size_t GetNumBytesLeft() { return GetNumBitsLeft() >> 3; }
private:
size_t m_DataBits; // 0x0010
@@ -743,10 +704,7 @@ public:
m_DataEnd = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_Data) + m_DataBytes);
}
- INLINE int GetNumBitsLeft()
- {
- return m_OutBitsLeft + (32 * (m_DataEnd - m_DataOut - 1));
- }
+ INLINE int GetNumBitsLeft() { return m_OutBitsLeft + (32 * (m_DataEnd - m_DataOut - 1)); }
INLINE void Reset()
{
@@ -775,10 +733,7 @@ public:
return reinterpret_cast<u8*>(m_Data);
}
- INLINE u8* GetData()
- {
- return GetBasePointer();
- }
+ INLINE u8* GetData() { return GetBasePointer(); }
INLINE void Finish()
{
@@ -851,10 +806,7 @@ public:
}
}
- INLINE void WriteSBitLong(i32 data, i32 numBits)
- {
- WriteUBitLong((u32)data, numBits, false);
- }
+ INLINE void WriteSBitLong(i32 data, i32 numBits) { WriteUBitLong((u32)data, numBits, false); }
INLINE void WriteUBitVar(u32 n)
{
@@ -911,40 +863,19 @@ public:
return !IsOverflowed();
}
- INLINE bool WriteBytes(const uptr data, i32 numBytes)
- {
- return WriteBits(data, numBytes << 3);
- }
+ INLINE bool WriteBytes(const uptr data, i32 numBytes) { return WriteBits(data, numBytes << 3); }
- INLINE i32 GetNumBitsWritten()
- {
- return (32 - m_OutBitsLeft) + (32 * (m_DataOut - m_Data));
- }
+ INLINE i32 GetNumBitsWritten() { return (32 - m_OutBitsLeft) + (32 * (m_DataOut - m_Data)); }
- INLINE i32 GetNumBytesWritten()
- {
- return (GetNumBitsWritten() + 7) >> 3;
- }
+ INLINE i32 GetNumBytesWritten() { return (GetNumBitsWritten() + 7) >> 3; }
- INLINE void WriteChar(i32 val)
- {
- WriteSBitLong(val, sizeof(char) << 3);
- }
+ INLINE void WriteChar(i32 val) { WriteSBitLong(val, sizeof(char) << 3); }
- INLINE void WriteByte(i32 val)
- {
- WriteUBitLong(val, sizeof(unsigned char) << 3, false);
- }
+ INLINE void WriteByte(i32 val) { WriteUBitLong(val, sizeof(unsigned char) << 3, false); }
- INLINE void WriteShort(i32 val)
- {
- WriteSBitLong(val, sizeof(short) << 3);
- }
+ INLINE void WriteShort(i32 val) { WriteSBitLong(val, sizeof(short) << 3); }
- INLINE void WriteWord(i32 val)
- {
- WriteUBitLong(val, sizeof(unsigned short) << 3);
- }
+ INLINE void WriteWord(i32 val) { WriteUBitLong(val, sizeof(unsigned short) << 3); }
INLINE bool WriteString(const char* str)
{
diff --git a/primedev/core/math/color.h b/primedev/core/math/color.h
index 013c4e4c..a46bc089 100644
--- a/primedev/core/math/color.h
+++ b/primedev/core/math/color.h
@@ -7,22 +7,10 @@ struct color24
typedef struct color32_s
{
- bool operator!=(const struct color32_s& other) const
- {
- return r != other.r || g != other.g || b != other.b || a != other.a;
- }
- inline unsigned* asInt(void)
- {
- return reinterpret_cast<unsigned*>(this);
- }
- inline const unsigned* asInt(void) const
- {
- return reinterpret_cast<const unsigned*>(this);
- }
- inline void Copy(const color32_s& rhs)
- {
- *asInt() = *rhs.asInt();
- }
+ bool operator!=(const struct color32_s& other) const { return r != other.r || g != other.g || b != other.b || a != other.a; }
+ inline unsigned* asInt(void) { return reinterpret_cast<unsigned*>(this); }
+ inline const unsigned* asInt(void) const { return reinterpret_cast<const unsigned*>(this); }
+ inline void Copy(const color32_s& rhs) { *asInt() = *rhs.asInt(); }
uint8_t r, g, b, a;
} color32;
@@ -79,55 +67,22 @@ public:
_b = _color[2];
_a = _color[3];
}
- int GetValue(int index) const
- {
- return _color[index];
- }
- void SetRawColor(int color32)
- {
- *((int*)this) = color32;
- }
- int GetRawColor(void) const
- {
- return *((int*)this);
- }
+ int GetValue(int index) const { return _color[index]; }
+ void SetRawColor(int color32) { *((int*)this) = color32; }
+ int GetRawColor(void) const { return *((int*)this); }
- inline int r() const
- {
- return _color[0];
- }
- inline int g() const
- {
- return _color[1];
- }
- inline int b() const
- {
- return _color[2];
- }
- inline int a() const
- {
- return _color[3];
- }
+ inline int r() const { return _color[0]; }
+ inline int g() const { return _color[1]; }
+ inline int b() const { return _color[2]; }
+ inline int a() const { return _color[3]; }
- unsigned char& operator[](int index)
- {
- return _color[index];
- }
+ unsigned char& operator[](int index) { return _color[index]; }
- const unsigned char& operator[](int index) const
- {
- return _color[index];
- }
+ const unsigned char& operator[](int index) const { return _color[index]; }
- bool operator==(const Color& rhs) const
- {
- return (*((int*)this) == *((int*)&rhs));
- }
+ bool operator==(const Color& rhs) const { return (*((int*)this) == *((int*)&rhs)); }
- bool operator!=(const Color& rhs) const
- {
- return !(operator==(rhs));
- }
+ bool operator!=(const Color& rhs) const { return !(operator==(rhs)); }
Color& operator=(const Color& rhs)
{
@@ -164,10 +119,7 @@ public:
return out;
}
- SourceColor ToSourceColor()
- {
- return SourceColor(_color[0], _color[1], _color[2], _color[3]);
- }
+ SourceColor ToSourceColor() { return SourceColor(_color[0], _color[1], _color[2], _color[3]); }
private:
unsigned char _color[4];
diff --git a/primedev/core/math/vector.h b/primedev/core/math/vector.h
index e62f2c93..ea1de65e 100644
--- a/primedev/core/math/vector.h
+++ b/primedev/core/math/vector.h
@@ -13,15 +13,27 @@ class Vector3
public:
float x, y, z;
- Vector3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
- Vector3(float _f) : x(_f), y(_f), z(_f) {}
- Vector3() : x(0.0f), y(0.0f), z(0.0f) {}
-
- inline bool IsValid()
+ Vector3(float _x, float _y, float _z)
+ : x(_x)
+ , y(_y)
+ , z(_z)
+ {
+ }
+ Vector3(float _f)
+ : x(_f)
+ , y(_f)
+ , z(_f)
+ {
+ }
+ Vector3()
+ : x(0.0f)
+ , y(0.0f)
+ , z(0.0f)
{
- return IsFinite(x) && IsFinite(y) && IsFinite(z);
}
+ inline bool IsValid() { return IsFinite(x) && IsFinite(y) && IsFinite(z); }
+
inline void Init(float fX = 0.0f, float fY = 0.0f, float fZ = 0.0f)
{
x = fX;
@@ -29,10 +41,7 @@ public:
z = fZ;
}
- inline float Length()
- {
- return FastSqrt(x * x + y * y + z * z);
- }
+ inline float Length() { return FastSqrt(x * x + y * y + z * z); }
inline float DistTo(const Vector3& vOther)
{
@@ -44,10 +53,7 @@ public:
return vDelta.Length();
}
- float Dot(const Vector3& vOther) const
- {
- return x * vOther.x + y * vOther.y + z * vOther.z;
- }
+ float Dot(const Vector3& vOther) const { return x * vOther.x + y * vOther.y + z * vOther.z; }
// Cross product between two vectors.
Vector3 Cross(const Vector3& vOther) const;
@@ -311,17 +317,29 @@ public:
float y;
float z;
- QAngle(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
- QAngle(float _f) : x(_f), y(_f), z(_f) {}
- QAngle() : x(0.0f), y(0.0f), z(0.0f) {}
+ QAngle(float _x, float _y, float _z)
+ : x(_x)
+ , y(_y)
+ , z(_z)
+ {
+ }
+ QAngle(float _f)
+ : x(_f)
+ , y(_f)
+ , z(_f)
+ {
+ }
+ QAngle()
+ : x(0.0f)
+ , y(0.0f)
+ , z(0.0f)
+ {
+ }
Vector3 GetNormal() const;
// todo: more operators maybe
- bool operator==(const QAngle& other)
- {
- return x == other.x && y == other.y && z == other.z;
- }
+ bool operator==(const QAngle& other) { return x == other.x && y == other.y && z == other.z; }
};
inline Vector3 QAngle::GetNormal() const
diff --git a/primedev/core/memalloc.h b/primedev/core/memalloc.h
index 73e078f5..a55ce361 100644
--- a/primedev/core/memalloc.h
+++ b/primedev/core/memalloc.h
@@ -40,10 +40,7 @@ public:
}
return _realloc_base(originalPtr, newSize);
}
- static void Free(void* ptr)
- {
- _free_base(ptr);
- }
+ static void Free(void* ptr) { _free_base(ptr); }
};
typedef rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<SourceAllocator>, SourceAllocator> rapidjson_document;
diff --git a/primedev/core/sourceinterface.cpp b/primedev/core/sourceinterface.cpp
index 5a72beb0..7ce33925 100644
--- a/primedev/core/sourceinterface.cpp
+++ b/primedev/core/sourceinterface.cpp
@@ -1,16 +1,11 @@
-#include "sourceinterface.h"
#include "logging/sourceconsole.h"
-AUTOHOOK_INIT()
-
// really wanted to do a modular callback system here but honestly couldn't be bothered so hardcoding stuff for now: todo later
-// clang-format off
-AUTOHOOK_PROCADDRESS(ClientCreateInterface, client.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
+static void*(__fastcall* o_pClientCreateInterface)(const char* pName, const int* pReturnCode) = nullptr;
+static void* __fastcall h_ClientCreateInterface(const char* pName, const int* pReturnCode)
{
- void* ret = ClientCreateInterface(pName, pReturnCode);
+ void* ret = o_pClientCreateInterface(pName, pReturnCode);
spdlog::info("CreateInterface CLIENT {}", pName);
if (!strcmp(pName, "GameClientExports001"))
@@ -19,30 +14,38 @@ void*, __fastcall, (const char* pName, const int* pReturnCode))
return ret;
}
-// clang-format off
-AUTOHOOK_PROCADDRESS(ServerCreateInterface, server.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
+static void*(__fastcall* o_pServerCreateInterface)(const char* pName, const int* pReturnCode) = nullptr;
+static void* __fastcall h_ServerCreateInterface(const char* pName, const int* pReturnCode)
{
- void* ret = ServerCreateInterface(pName, pReturnCode);
+ void* ret = o_pServerCreateInterface(pName, pReturnCode);
spdlog::info("CreateInterface SERVER {}", pName);
return ret;
}
-// clang-format off
-AUTOHOOK_PROCADDRESS(EngineCreateInterface, engine.dll, CreateInterface,
-void*, __fastcall, (const char* pName, const int* pReturnCode))
-// clang-format on
+static void*(__fastcall* o_pEngineCreateInterface)(const char* pName, const int* pReturnCode) = nullptr;
+static void* __fastcall h_EngineCreateInterface(const char* pName, const int* pReturnCode)
{
- void* ret = EngineCreateInterface(pName, pReturnCode);
+ void* ret = o_pEngineCreateInterface(pName, pReturnCode);
spdlog::info("CreateInterface ENGINE {}", pName);
return ret;
}
-// clang-format off
-ON_DLL_LOAD("client.dll", ClientInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(client.dll)}
-ON_DLL_LOAD("server.dll", ServerInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(server.dll)}
-ON_DLL_LOAD("engine.dll", EngineInterface, (CModule module)) {AUTOHOOK_DISPATCH_MODULE(engine.dll)}
-// clang-format on
+ON_DLL_LOAD("client.dll", ClientInterface, (CModule module))
+{
+ o_pClientCreateInterface = module.GetExportedFunction("CreateInterface").RCast<decltype(o_pClientCreateInterface)>();
+ HookAttach(&(PVOID&)o_pClientCreateInterface, (PVOID)h_ClientCreateInterface);
+}
+
+ON_DLL_LOAD("server.dll", ServerInterface, (CModule module))
+{
+ o_pServerCreateInterface = module.GetExportedFunction("CreateInterface").RCast<decltype(o_pServerCreateInterface)>();
+ HookAttach(&(PVOID&)o_pServerCreateInterface, (PVOID)h_ServerCreateInterface);
+}
+
+ON_DLL_LOAD("engine.dll", EngineInterface, (CModule module))
+{
+ o_pEngineCreateInterface = module.GetExportedFunction("CreateInterface").RCast<decltype(o_pEngineCreateInterface)>();
+ HookAttach(&(PVOID&)o_pEngineCreateInterface, (PVOID)h_EngineCreateInterface);
+}
diff --git a/primedev/core/sourceinterface.h b/primedev/core/sourceinterface.h
deleted file mode 100644
index bbbbd3bc..00000000
--- a/primedev/core/sourceinterface.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-#include <string>
-
-// interface return status
-enum class InterfaceStatus : int
-{
- IFACE_OK = 0,
- IFACE_FAILED,
-};
-
-// literally just copied from ttf2sdk definition
-typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
-
-template <typename T> class SourceInterface
-{
-private:
- T* m_interface;
-
-public:
- SourceInterface(const std::string& moduleName, const std::string& interfaceName)
- {
- HMODULE handle = GetModuleHandleA(moduleName.c_str());
- CreateInterfaceFn createInterface = (CreateInterfaceFn)GetProcAddress(handle, "CreateInterface");
- m_interface = (T*)createInterface(interfaceName.c_str(), NULL);
- if (m_interface == nullptr)
- spdlog::error("Failed to call CreateInterface for %s in %s", interfaceName, moduleName);
- }
-
- T* operator->() const
- {
- return m_interface;
- }
-
- operator T*() const
- {
- return m_interface;
- }
-};
diff --git a/primedev/core/tier0.cpp b/primedev/core/tier0.cpp
index dd5ac245..639b3bf8 100644
--- a/primedev/core/tier0.cpp
+++ b/primedev/core/tier0.cpp
@@ -18,11 +18,40 @@ void TryCreateGlobalMemAlloc()
g_pMemAllocSingleton = CreateGlobalMemAlloc(); // if it already exists, this returns the preexisting IMemAlloc instance
}
+HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ // need to grab it dynamically as this function was only introduced at some point in Windows 10
+ static decltype(&SetThreadDescription) _SetThreadDescription =
+ CModule("KernelBase.dll").GetExportedFunction("SetThreadDescription").RCast<decltype(&SetThreadDescription)>();
+
+ if (_SetThreadDescription)
+ return _SetThreadDescription(hThread, lpThreadDescription);
+
+ return ERROR_OLD_WIN_VERSION;
+}
+
+static void(__fastcall* o_pThreadSetDebugName)(HANDLE threadHandle, const char* name) = nullptr;
+static void __fastcall h_ThreadSetDebugName(HANDLE threadHandle, const char* name)
+{
+ if (threadHandle == 0)
+ threadHandle = GetCurrentThread();
+
+ // TODO: This "method" of "charset conversion" from string to wstring is abhorrent. Change it to a proper one
+ // as soon as Northstar has some helper function to do proper charset conversions.
+ auto tmp = std::string(name);
+ _SetThreadDescription(threadHandle, std::wstring(tmp.begin(), tmp.end()).c_str());
+
+ o_pThreadSetDebugName(threadHandle, name);
+}
+
ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module))
{
// shouldn't be necessary, but do this just in case
TryCreateGlobalMemAlloc();
+ o_pThreadSetDebugName = module.GetExportedFunction("ThreadSetDebugName").RCast<decltype(o_pThreadSetDebugName)>();
+ HookAttach(&(PVOID&)o_pThreadSetDebugName, (PVOID)h_ThreadSetDebugName);
+
// setup tier0 funcs
CommandLine = module.GetExportedFunction("CommandLine").RCast<CommandLineType>();
Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast<Plat_FloatTimeType>();
diff --git a/primedev/core/tier1.h b/primedev/core/tier1.h
index d162e7c8..36f577cc 100644
--- a/primedev/core/tier1.h
+++ b/primedev/core/tier1.h
@@ -7,6 +7,12 @@
#define CREATEINTERFACE_PROCNAME "CreateInterface"
+enum class InterfaceStatus : int
+{
+ IFACE_OK = 0,
+ IFACE_FAILED,
+};
+
typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode);
CMemory Sys_GetFactoryPtr(const std::string& svModuleName, const std::string& svFact);
diff --git a/primedev/core/vanilla.h b/primedev/core/vanilla.h
index b0797803..356495c8 100644
--- a/primedev/core/vanilla.h
+++ b/primedev/core/vanilla.h
@@ -17,10 +17,7 @@ public:
m_bIsVanillaCompatible = isVanilla;
}
- bool GetVanillaCompatibility()
- {
- return m_bIsVanillaCompatible;
- }
+ bool GetVanillaCompatibility() { return m_bIsVanillaCompatible; }
private:
bool m_bIsVanillaCompatible = false;