r5sdk/r5dev/datacache/mdlcache.cpp

375 lines
14 KiB
C++
Raw Normal View History

//=====================================================================================//
//
// model loading and caching
//
// $NoKeywords: $
//=====================================================================================//
#include "core/stdafx.h"
#include "tier0/threadtools.h"
#include "tier1/cvar.h"
#include "tier1/utldict.h"
#include "datacache/mdlcache.h"
#include "datacache/imdlcache.h"
#include "datacache/idatacache.h"
#include "rtech/rtech_utils.h"
#include "public/studio.h"
//-----------------------------------------------------------------------------
// Purpose: finds an MDL
// Input : *this -
// handle -
// *a3 -
// Output : a pointer to the studiohdr_t object
//-----------------------------------------------------------------------------
2022-05-05 02:54:17 +02:00
studiohdr_t* CMDLCache::FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3)
{
studiohdr_t* pStudioHdr; // rax
EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
2022-05-06 13:21:42 +02:00
studiodata_t* pStudioData = m_MDLDict->Find(handle);
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
if (!g_pMDLFallback->m_hErrorMDL || !g_pMDLFallback->m_hEmptyMDL)
{
2022-05-18 02:04:37 +02:00
if (pStudioData->m_MDLCache)
{
2022-05-18 02:04:37 +02:00
studiohdr_t* pStudioHDR = **reinterpret_cast<studiohdr_t***>(pStudioData);
if (pStudioHDR)
{
const string svStudio = ConvertToUnixPath(pStudioHDR->name);
if (svStudio.compare(ERROR_MODEL) == NULL)
2022-05-18 02:04:37 +02:00
{
g_pMDLFallback->m_pErrorHDR = pStudioHDR;
g_pMDLFallback->m_hErrorMDL = handle;
}
else if (svStudio.compare(EMPTY_MODEL) == NULL)
2022-05-18 02:04:37 +02:00
{
g_pMDLFallback->m_pEmptyHDR = pStudioHDR;
g_pMDLFallback->m_hEmptyMDL = handle;
}
}
}
}
2022-05-05 02:54:17 +02:00
if (!pStudioData)
{
pStudioHdr = GetErrorModel();
if (!IsKnownBadModel(handle))
{
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model with handle \"%hu\" not found and \"%s\" couldn't be loaded.\n", handle, ERROR_MODEL);
else
Error(eDLL_T::ENGINE, NO_ERROR, "Model with handle \"%hu\" not found; replacing with \"%s\".\n", handle, ERROR_MODEL);
}
return pStudioHdr;
}
int nFlags = STUDIOHDR_FLAGS_NEEDS_DEFERRED_ADDITIVE | STUDIOHDR_FLAGS_OBSOLETE;
if ((pStudioData->m_nFlags & nFlags))
{
2022-05-06 13:21:42 +02:00
void* pMDLCache = *reinterpret_cast<void**>(pStudioData);
2022-05-05 02:54:17 +02:00
if (pStudioData->m_MDLCache)
{
if (a3)
{
2022-05-05 02:54:17 +02:00
FindCachedMDL(cache, pStudioData, a3);
pMDLCache = *reinterpret_cast<void**>(pStudioData);
}
pStudioHdr = *reinterpret_cast<studiohdr_t**>(pMDLCache);
if (pStudioHdr)
return pStudioHdr;
2022-05-05 02:54:17 +02:00
return FindUncachedMDL(cache, handle, pStudioData, a3);
}
pMDLCache = pStudioData->m_pAnimData;
2022-05-05 02:54:17 +02:00
if (pMDLCache)
{
pStudioHdr = *reinterpret_cast<studiohdr_t**>(pMDLCache);
if (pStudioHdr)
return pStudioHdr;
}
}
2022-05-05 02:54:17 +02:00
return FindUncachedMDL(cache, handle, pStudioData, a3);
}
//-----------------------------------------------------------------------------
// Purpose: finds an MDL cached
// Input : *this -
2022-05-18 01:00:46 +02:00
// *pStudioData -
// *a3 -
//-----------------------------------------------------------------------------
void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* a3)
{
if (a3)
{
pStudioData->m_Mutex.WaitForLock();
*(_QWORD*)((int64_t)a3 + 0x880) = *(_QWORD*)&pStudioData->pad[0x24];
int64_t v6 = *(_QWORD*)&pStudioData->pad[0x24];
if (v6)
*(_QWORD*)(v6 + 0x878) = (int64_t)a3;
*(_QWORD*)&pStudioData->pad[0x24] = (int64_t)a3;
*(_QWORD*)((int64_t)a3 + 0x870) = (int64_t)cache;
*(_WORD*)((int64_t)a3 + 0x888) = pStudioData->m_Handle;
pStudioData->m_Mutex.ReleaseWaiter();
}
}
//-----------------------------------------------------------------------------
// Purpose: finds an MDL uncached
// Input : *this -
// handle -
2022-05-18 01:00:46 +02:00
// *pStudioData -
// *a4 -
// Output : a pointer to the studiohdr_t object
//-----------------------------------------------------------------------------
studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, studiodata_t* pStudioData, void* a4)
{
2022-05-05 20:36:13 +02:00
studiohdr_t* pStudioHdr; // rdi
studiohdr_t** ppStudioHdr; // rax
pStudioData->m_Mutex.WaitForLock();
EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
void* pModelCache = cache->m_pModelCacheSection;
char* szModelName = (char*)(*(_QWORD*)((int64)pModelCache + 24 * static_cast<int64>(handle) + 8));
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
if (IsBadReadPtrV2(reinterpret_cast<void*>(szModelName)))
{
pStudioHdr = GetErrorModel();
if (!IsKnownBadModel(handle))
{
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model with handle \"%hu\" not found and \"%s\" couldn't be loaded.\n", handle, ERROR_MODEL);
else
Error(eDLL_T::ENGINE, NO_ERROR, "Model with handle \"%hu\" not found; replacing with \"%s\".\n", handle, ERROR_MODEL);
}
pStudioData->m_Mutex.ReleaseWaiter();
return pStudioHdr;
}
size_t nFileNameLen = strlen(szModelName);
if (nFileNameLen < 5 ||
2022-10-11 01:24:08 +02:00
(Q_stricmp(&szModelName[nFileNameLen - 5], ".rmdl") != 0) &&
(Q_stricmp(&szModelName[nFileNameLen - 5], ".rrig") != 0) &&
(Q_stricmp(&szModelName[nFileNameLen - 5], ".rpak") != 0))
{
pStudioHdr = GetErrorModel();
if (!IsKnownBadModel(handle))
{
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Attempted to load old model \"%s\" and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL);
else
Error(eDLL_T::ENGINE, NO_ERROR, "Attempted to load old model \"%s\"; replacing with \"%s\".\n", szModelName, ERROR_MODEL);
}
pStudioData->m_Mutex.ReleaseWaiter();
return pStudioHdr;
}
2022-05-05 20:36:13 +02:00
LOBYTE(pStudioData->m_nGuidLock) = 1;
g_pRTech->StringToGuid(szModelName);
LOBYTE(pStudioData->m_nGuidLock) = 0;
if (!pStudioData->m_MDLCache)
{
2022-05-05 20:36:13 +02:00
ppStudioHdr = (studiohdr_t**)pStudioData->m_pAnimData;
if (ppStudioHdr)
{
2022-05-05 20:36:13 +02:00
pStudioHdr = *ppStudioHdr;
}
else
{
pStudioHdr = GetErrorModel();
if (!IsKnownBadModel(handle))
{
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" not found and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL);
else
Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" not found; replacing with \"%s\".\n", szModelName, ERROR_MODEL);
}
pStudioData->m_Mutex.ReleaseWaiter();
return pStudioHdr;
}
}
else
{
2022-05-18 01:00:46 +02:00
FindCachedMDL(cache, pStudioData, a4);
if ((__int64)*(studiohdr_t**)pStudioData)
{
if ((__int64)*(studiohdr_t**)pStudioData == 0xDEADFEEDDEADFEED)
{
pStudioHdr = GetErrorModel();
if (!IsKnownBadModel(handle))
{
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" has bad studio data and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL);
else
Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" has bad studio data; replacing with \"%s\".\n", szModelName, ERROR_MODEL);
}
}
else
2022-05-05 20:36:13 +02:00
pStudioHdr = **(studiohdr_t***)pStudioData;
}
else
{
pStudioHdr = GetErrorModel();
if (!IsKnownBadModel(handle))
{
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" has no studio data and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL);
else
Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" has no studio data; replacing with \"%s\".\n", szModelName, ERROR_MODEL);
}
}
}
pStudioData->m_Mutex.ReleaseWaiter();
2022-05-05 20:36:13 +02:00
return pStudioHdr;
}
//-----------------------------------------------------------------------------
// Purpose: gets the studiohdr from cache pool by handle
// Input : *this -
// handle -
// Output : a pointer to the studiohdr_t object
//-----------------------------------------------------------------------------
studiohdr_t* CMDLCache::GetStudioHDR(CMDLCache* pMDLCache, MDLHandle_t handle)
{
studiohdr_t* pStudioHdr = nullptr; // rax
if (!handle)
{
pStudioHdr = GetErrorModel();
if (!pStudioHdr)
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Attempted to load model with no handle and \"%s\" couldn't be loaded.\n", ERROR_MODEL);
return pStudioHdr;
}
EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
studiodata_t* pStudioData = m_MDLDict->Find(handle);
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
if (*(_QWORD*)(pStudioData))
{
2022-05-16 02:41:35 +02:00
if (reinterpret_cast<int64_t>(pStudioData->m_MDLCache) != 0xDEADFEEDDEADFEED)
{
void* v4 = *(void**)(*((_QWORD*)pStudioData->m_MDLCache + 1) + 24i64);
if (v4)
pStudioHdr = (studiohdr_t*)((char*)v4 + 0x10);
}
}
return pStudioHdr;
}
//-----------------------------------------------------------------------------
// Purpose: gets the studio hardware data from cache pool by handle
// Input : *this -
// handle -
// Output : a pointer to the studiohwdata_t object
//-----------------------------------------------------------------------------
studiohwdata_t* CMDLCache::GetHardwareData(CMDLCache* cache, MDLHandle_t handle)
{
EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
studiodata_t* pStudioData = m_MDLDict->Find(handle);
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
if (!pStudioData)
{
if (!g_pMDLFallback->m_hErrorMDL)
{
Error(eDLL_T::ENGINE, EXIT_FAILURE, "Studio hardware with handle \"%hu\" not found and \"%s\" couldn't be loaded.\n", handle, ERROR_MODEL);
return nullptr;
}
pStudioData = m_MDLDict->Find(g_pMDLFallback->m_hErrorMDL);
}
if (pStudioData->m_MDLCache)
{
if (reinterpret_cast<int64_t>(pStudioData->m_MDLCache) == 0xDEADFEEDDEADFEED)
return nullptr;
void* pAnimData = (void*)*((_QWORD*)pStudioData->m_MDLCache + 1);
AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&*m_MDLLock));
2022-05-18 02:04:37 +02:00
#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2)
v_CStudioHWDataRef__SetFlags(reinterpret_cast<CStudioHWDataRef*>(pAnimData), 1i64); // !!! DECLARED INLINE IN < S3 !!!
2022-05-18 02:04:37 +02:00
#endif
ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&*m_MDLLock));
}
if ((pStudioData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED))
return &pStudioData->m_pHardwareRef->m_HardwareData;
else
return nullptr;
}
//-----------------------------------------------------------------------------
// Purpose: gets the studio material glue from cache pool by handle
// Input : *this -
// handle -
// Output : a pointer to the CMaterialGlue object
//-----------------------------------------------------------------------------
void* CMDLCache::GetMaterialTable(CMDLCache* cache, MDLHandle_t handle)
{
EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
studiodata_t* pStudioData = m_MDLDict->Find(handle);
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(&*m_MDLMutex));
return &pStudioData->m_pMaterialTable;
}
//-----------------------------------------------------------------------------
// Purpose: gets the error model
// Output : *studiohdr_t
//-----------------------------------------------------------------------------
studiohdr_t* CMDLCache::GetErrorModel(void)
{
if (!old_gather_props->GetBool())
old_gather_props->SetValue(true); // !TODO [AMOS]: mdl/error.rmdl fallback is not supported (yet) in the new GatherProps solution!
return g_pMDLFallback->m_pErrorHDR;
}
//-----------------------------------------------------------------------------
// Purpose: checks if this model handle is within the set of bad models
// Input : handle -
// Output : true if exist, false otherwise
//-----------------------------------------------------------------------------
bool CMDLCache::IsKnownBadModel(MDLHandle_t handle)
{
auto p = g_vBadMDLHandles.insert(handle);
return !p.second;
}
void VMDLCache::Attach() const
{
DetourAttach((LPVOID*)&v_CMDLCache__FindMDL, &CMDLCache::FindMDL);
2022-08-09 10:56:57 +02:00
#ifdef GAMEDLL_S3 // !!! DECLARED INLINE WITH FINDMDL IN < S3 !!!
DetourAttach((LPVOID*)&v_CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL);
DetourAttach((LPVOID*)&v_CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL);
2022-05-18 01:00:46 +02:00
#endif // GAMEDLL_S3
#ifdef GAMEDLL_S3 // !TODO:
DetourAttach((LPVOID*)&v_CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData);
DetourAttach((LPVOID*)&v_CMDLCache__GetStudioHDR, &CMDLCache::GetStudioHDR);
2022-05-18 01:00:46 +02:00
#endif
}
void VMDLCache::Detach() const
{
DetourDetach((LPVOID*)&v_CMDLCache__FindMDL, &CMDLCache::FindMDL);
2022-08-09 10:56:57 +02:00
#ifdef GAMEDLL_S3 // !!! DECLARED INLINE WITH FINDMDL IN < S3 !!!
DetourDetach((LPVOID*)&v_CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL);
DetourDetach((LPVOID*)&v_CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL);
2022-05-18 01:00:46 +02:00
#endif // GAMEDLL_S3
#ifdef GAMEDLL_S3 // !TODO:
DetourDetach((LPVOID*)&v_CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData);
DetourDetach((LPVOID*)&v_CMDLCache__GetStudioHDR, &CMDLCache::GetStudioHDR);
2022-05-18 01:00:46 +02:00
#endif
}