//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #include "datacache/iresourceaccesscontrol.h" #include "tier1/utlvector.h" #include "tier1/utlstring.h" #include "tier2/tier2.h" #include "tier1/convar.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // NOTE: Necessary until we have all our resources wrangled. //----------------------------------------------------------------------------- static ConVar res_restrict_access( "res_restrict_access", "0" ); //----------------------------------------------------------------------------- // Implementation class //----------------------------------------------------------------------------- class CResourceAccessControl : public CTier1AppSystem< IResourceAccessControl > { typedef CTier1AppSystem< IResourceAccessControl > BaseClass; // Inherited from IAppSystem public: virtual void Shutdown(); // Inherited from IResourceAccessControl public: virtual ResourceList_t CreateResourceList( const char *pDebugName ); virtual void DestroyAllResourceLists( ); virtual void AddResource( ResourceList_t hResourceList, ResourceTypeOld_t nType, const char *pResourceName ); virtual void LimitAccess( ResourceList_t hResourceList ); virtual bool IsAccessAllowed( ResourceTypeOld_t nType, const char *pResource ); // Other public methods public: CResourceAccessControl(); private: enum { MAX_THREADS = 16 }; struct ResourceInfo_t { CUtlString m_DebugName; CUtlVector< CUtlString > m_Resources[RESOURCE_TYPE_OLD_COUNT]; }; bool ContainsResource( ResourceList_t hResourceList, ResourceTypeOld_t nType, const char *pResourceName ); int FindOrAddCurrentThreadID(); void FixupResourceName( const char *pResourceName, char *pBuf, int nBufLen ); CUtlVector< ResourceInfo_t > m_ResourceLists; int m_pLimitAccess[MAX_THREADS]; unsigned long m_pThread[MAX_THREADS]; int m_nThreadCount; CThreadMutex m_Mutex; }; //----------------------------------------------------------------------------- // Singleton //----------------------------------------------------------------------------- CResourceAccessControl g_ResourceAccessControl; EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CResourceAccessControl, IResourceAccessControl, RESOURCE_ACCESS_CONTROL_INTERFACE_VERSION, g_ResourceAccessControl ); //----------------------------------------------------------------------------- // Resource type names //----------------------------------------------------------------------------- static const char *s_pResourceTypeName[] = { "vgui panel", // .res file "material", // .vmt file "model", // .mdl file "particle system", // particle system "game sound", // game sound }; //----------------------------------------------------------------------------- // Constructor, destructor //----------------------------------------------------------------------------- CResourceAccessControl::CResourceAccessControl() { m_nThreadCount = 0; memset( m_pLimitAccess, 0xFF, sizeof(m_pLimitAccess) ); memset( m_pThread, 0, sizeof(m_pThread) ); } //----------------------------------------------------------------------------- // Shutdown //----------------------------------------------------------------------------- void CResourceAccessControl::Shutdown() { DestroyAllResourceLists(); memset( m_pLimitAccess, 0xFF, sizeof(m_pLimitAccess) ); BaseClass::Shutdown(); } //----------------------------------------------------------------------------- // Creates a resource list //----------------------------------------------------------------------------- int CResourceAccessControl::FindOrAddCurrentThreadID() { unsigned long nThreadId = ThreadGetCurrentId(); for ( int i = 0; i < m_nThreadCount; ++i ) { if ( m_pThread[i] == nThreadId ) return i; } if ( m_nThreadCount >= MAX_THREADS ) { Error( "Exceeded maximum number of unique threads (%d) attempting to access datacache.\n", MAX_THREADS ); return -1; } m_Mutex.Lock(); int nIndex = m_nThreadCount; m_pThread[m_nThreadCount++] = nThreadId; m_Mutex.Unlock(); return nIndex; } //----------------------------------------------------------------------------- // Fixes up the asset name //----------------------------------------------------------------------------- void CResourceAccessControl::FixupResourceName( const char *pResourceName, char *pBuf, int nBufLen ) { Q_StripExtension( pResourceName, pBuf, nBufLen ); Q_FixSlashes( pBuf, '/' ); V_RemoveDotSlashes( pBuf, '/' ); } //----------------------------------------------------------------------------- // Creates a resource list //----------------------------------------------------------------------------- ResourceList_t CResourceAccessControl::CreateResourceList( const char *pDebugName ) { int nIndex = m_ResourceLists.AddToTail(); m_ResourceLists[nIndex].m_DebugName.Set( pDebugName ); return (ResourceList_t)(intp)nIndex; } void CResourceAccessControl::DestroyAllResourceLists( ) { m_ResourceLists.Purge(); memset( m_pLimitAccess, 0xFF, sizeof(m_pLimitAccess) ); } bool CResourceAccessControl::ContainsResource( ResourceList_t hResourceList, ResourceTypeOld_t nType, const char *pResourceName ) { char pBuf[MAX_PATH]; FixupResourceName( pResourceName, pBuf, sizeof(pBuf) ); int nIndex = size_cast< int >( (intp)hResourceList ); CUtlVector< CUtlString > &list = m_ResourceLists[nIndex].m_Resources[nType]; int nCount = list.Count(); for ( int i = 0; i < nCount; ++i ) { if ( !Q_stricmp( pBuf, list[i].Get() ) ) return true; } return false; } void CResourceAccessControl::AddResource( ResourceList_t hResourceList, ResourceTypeOld_t nType, const char *pResourceName ) { if ( ContainsResource( hResourceList, nType, pResourceName ) ) return; int nIndex = size_cast< int >( (intp)hResourceList ); int nSubIndex = m_ResourceLists[nIndex].m_Resources[nType].AddToTail(); char pBuf[MAX_PATH]; FixupResourceName( pResourceName, pBuf, sizeof(pBuf) ); m_ResourceLists[nIndex].m_Resources[nType][nSubIndex].Set( pBuf ); } void CResourceAccessControl::LimitAccess( ResourceList_t hResourceList ) { if ( !res_restrict_access.GetInt() ) return; int nIndex = FindOrAddCurrentThreadID(); if ( ( m_pLimitAccess[nIndex] >= 0 ) && ( hResourceList != RESOURCE_LIST_INVALID ) ) { Warning( "Attempted to limit access while already limiting access!\n" ); return; } m_pLimitAccess[nIndex] = size_cast< int >( (intp)hResourceList ); } bool CResourceAccessControl::IsAccessAllowed( ResourceTypeOld_t nType, const char *pResourceName ) { int nIndex = FindOrAddCurrentThreadID(); if ( m_pLimitAccess[nIndex] < 0 ) return true; int hResourceList = m_pLimitAccess[nIndex]; if ( ContainsResource( (ResourceList_t)( intp )hResourceList, nType, pResourceName ) ) return true; COMPILE_TIME_ASSERT( ARRAYSIZE( s_pResourceTypeName ) == RESOURCE_TYPE_OLD_COUNT ); Warning( "Access to %s resource \"%s\" denied. Missing precache in %s?\n", s_pResourceTypeName[nType], pResourceName, m_ResourceLists[hResourceList].m_DebugName.Get() ); return false; }