//=====================================================================================// // // 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 //----------------------------------------------------------------------------- studiohdr_t* CMDLCache::FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3) { studiohdr_t* pStudioHdr; // rax EnterCriticalSection(reinterpret_cast(&*m_MDLMutex)); studiodata_t* pStudioData = m_MDLDict->Find(handle); LeaveCriticalSection(reinterpret_cast(&*m_MDLMutex)); if (!g_pMDLFallback->m_hErrorMDL || !g_pMDLFallback->m_hEmptyMDL) { if (pStudioData->m_MDLCache) { studiohdr_t* pStudioHDR = **reinterpret_cast(pStudioData); if (pStudioHDR) { const string svStudio = ConvertToUnixPath(pStudioHDR->name); if (svStudio.compare(ERROR_MODEL) == NULL) { g_pMDLFallback->m_pErrorHDR = pStudioHDR; g_pMDLFallback->m_hErrorMDL = handle; } else if (svStudio.compare(EMPTY_MODEL) == NULL) { g_pMDLFallback->m_pEmptyHDR = pStudioHDR; g_pMDLFallback->m_hEmptyMDL = handle; } } } } 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)) { void* pMDLCache = *reinterpret_cast(pStudioData); if (pStudioData->m_MDLCache) { if (a3) { FindCachedMDL(cache, pStudioData, a3); pMDLCache = *reinterpret_cast(pStudioData); } pStudioHdr = *reinterpret_cast(pMDLCache); if (pStudioHdr) return pStudioHdr; return FindUncachedMDL(cache, handle, pStudioData, a3); } pMDLCache = pStudioData->m_pAnimData; if (pMDLCache) { pStudioHdr = *reinterpret_cast(pMDLCache); if (pStudioHdr) return pStudioHdr; } } return FindUncachedMDL(cache, handle, pStudioData, a3); } //----------------------------------------------------------------------------- // Purpose: finds an MDL cached // Input : *this - // *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 - // *pStudioData - // *a4 - // Output : a pointer to the studiohdr_t object //----------------------------------------------------------------------------- studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, studiodata_t* pStudioData, void* a4) { studiohdr_t* pStudioHdr; // rdi studiohdr_t** ppStudioHdr; // rax pStudioData->m_Mutex.WaitForLock(); EnterCriticalSection(reinterpret_cast(&*m_MDLMutex)); void* pModelCache = cache->m_pModelCacheSection; char* szModelName = (char*)(*(_QWORD*)((int64)pModelCache + 24 * static_cast(handle) + 8)); LeaveCriticalSection(reinterpret_cast(&*m_MDLMutex)); if (IsBadReadPtrV2(reinterpret_cast(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 || (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; } LOBYTE(pStudioData->m_nGuidLock) = 1; g_pRTech->StringToGuid(szModelName); LOBYTE(pStudioData->m_nGuidLock) = 0; if (!pStudioData->m_MDLCache) { ppStudioHdr = (studiohdr_t**)pStudioData->m_pAnimData; if (ppStudioHdr) { 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 { 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 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(); 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(&*m_MDLMutex)); studiodata_t* pStudioData = m_MDLDict->Find(handle); LeaveCriticalSection(reinterpret_cast(&*m_MDLMutex)); if (*(_QWORD*)(pStudioData)) { if (reinterpret_cast(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(&*m_MDLMutex)); studiodata_t* pStudioData = m_MDLDict->Find(handle); LeaveCriticalSection(reinterpret_cast(&*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(pStudioData->m_MDLCache) == 0xDEADFEEDDEADFEED) return nullptr; void* pAnimData = (void*)*((_QWORD*)pStudioData->m_MDLCache + 1); AcquireSRWLockExclusive(reinterpret_cast(&*m_MDLLock)); #if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) v_CStudioHWDataRef__SetFlags(reinterpret_cast(pAnimData), 1i64); // !!! DECLARED INLINE IN < S3 !!! #endif ReleaseSRWLockExclusive(reinterpret_cast(&*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(&*m_MDLMutex)); studiodata_t* pStudioData = m_MDLDict->Find(handle); LeaveCriticalSection(reinterpret_cast(&*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); #ifdef GAMEDLL_S3 // !!! DECLARED INLINE WITH FINDMDL IN < S3 !!! DetourAttach((LPVOID*)&v_CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL); DetourAttach((LPVOID*)&v_CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL); #endif // GAMEDLL_S3 #ifdef GAMEDLL_S3 // !TODO: DetourAttach((LPVOID*)&v_CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData); DetourAttach((LPVOID*)&v_CMDLCache__GetStudioHDR, &CMDLCache::GetStudioHDR); #endif } void VMDLCache::Detach() const { DetourDetach((LPVOID*)&v_CMDLCache__FindMDL, &CMDLCache::FindMDL); #ifdef GAMEDLL_S3 // !!! DECLARED INLINE WITH FINDMDL IN < S3 !!! DetourDetach((LPVOID*)&v_CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL); DetourDetach((LPVOID*)&v_CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL); #endif // GAMEDLL_S3 #ifdef GAMEDLL_S3 // !TODO: DetourDetach((LPVOID*)&v_CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData); DetourDetach((LPVOID*)&v_CMDLCache__GetStudioHDR, &CMDLCache::GetStudioHDR); #endif }