aboutsummaryrefslogtreecommitdiff
path: root/mp/src/tier1/KeyValues.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mp/src/tier1/KeyValues.cpp')
-rw-r--r--mp/src/tier1/KeyValues.cpp331
1 files changed, 229 insertions, 102 deletions
diff --git a/mp/src/tier1/KeyValues.cpp b/mp/src/tier1/KeyValues.cpp
index c72172ca..ad4af096 100644
--- a/mp/src/tier1/KeyValues.cpp
+++ b/mp/src/tier1/KeyValues.cpp
@@ -18,14 +18,16 @@
#include <KeyValues.h>
#include "filesystem.h"
#include <vstdlib/IKeyValuesSystem.h>
+#include "tier0/icommandline.h"
#include <Color.h>
#include <stdlib.h>
#include "tier0/dbg.h"
#include "tier0/mem.h"
-#include "utlvector.h"
#include "utlbuffer.h"
#include "utlhash.h"
+#include "utlvector.h"
+#include "utlqueue.h"
#include "UtlSortVector.h"
#include "convar.h"
@@ -84,16 +86,19 @@ public:
{
Assert( stackLevel >= 0 );
Assert( stackLevel < m_errorIndex );
- m_errorStack[stackLevel] = symName;
+ if ( stackLevel < MAX_ERROR_STACK )
+ m_errorStack[stackLevel] = symName;
}
// Hit an error, report it and the parsing stack for context
void ReportError( const char *pError )
{
+ bool bSpewCR = false;
+
Warning( "KeyValues Error: %s in file %s\n", pError, m_pFilename );
for ( int i = 0; i < m_maxErrorIndex; i++ )
{
- if ( m_errorStack[i] != INVALID_KEY_SYMBOL )
+ if ( i < MAX_ERROR_STACK && m_errorStack[i] != INVALID_KEY_SYMBOL )
{
if ( i < m_errorIndex )
{
@@ -103,9 +108,13 @@ public:
{
Warning( "(*%s*), ", KeyValues::CallGetStringForSymbol(m_errorStack[i]) );
}
+
+ bSpewCR = true;
}
}
- Warning( "\n" );
+
+ if ( bSpewCR )
+ Warning( "\n" );
}
private:
@@ -137,6 +146,10 @@ public:
{
g_KeyValuesErrorStack.Reset( m_stackLevel, symName );
}
+ int GetStackLevel() const
+ {
+ return m_stackLevel;
+ }
private:
void Init( int symName )
{
@@ -630,15 +643,62 @@ void KeyValues::UsesConditionals(bool state)
//-----------------------------------------------------------------------------
// Purpose: Load keyValues from disk
//-----------------------------------------------------------------------------
-bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID )
+bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool refreshCache )
{
Assert(filesystem);
#ifdef WIN32
Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) );
#endif
+
+#ifdef STAGING_ONLY
+ static bool s_bCacheEnabled = !!CommandLine()->FindParm( "-enable_keyvalues_cache" );
+ const bool bUseCache = s_bCacheEnabled && ( s_pfGetSymbolForString == KeyValues::GetSymbolForStringClassic );
+#else
+ /*
+ People are cheating with the keyvalue cache enabled by doing the below, so disable it.
+
+ For example if one is to allow a blue demoman texture on sv_pure they
+ change it to this, "$basetexture" "temp/demoman_blue". Remember to move the
+ demoman texture to the temp folder in the materials folder. It will likely
+ not be there so make a new folder for it. Once the directory in the
+ demoman_blue vmt is changed to the temp folder and the vtf texture is in
+ the temp folder itself you are finally done.
+
+ I packed my mods into a vpk but I don't think it's required. Once in game
+ you must create a server via the create server button and select the map
+ that will load the custom texture before you join a valve server. I suggest
+ you only do this with player textures and such as they are always loaded.
+ After you load the map you join the valve server and the textures should
+ appear and work on valve servers.
+
+ This can be done on any sv_pure 1 server but it depends on what is type of
+ files are allowed. All valve servers allow temp files so that is the
+ example I used here."
+
+ So all vmt's files can bypass sv_pure 1. And I believe this mod is mostly
+ made of vmt files, so valve's sv_pure 1 bull is pretty redundant.
+ */
+ const bool bUseCache = false;
+#endif
+
+ // If pathID is null, we cannot cache the result because that has a weird iterate-through-a-bunch-of-locations behavior.
+ const bool bUseCacheForRead = bUseCache && !refreshCache && pathID != NULL;
+ const bool bUseCacheForWrite = bUseCache && pathID != NULL;
+
+ COM_TimestampedLog( "KeyValues::LoadFromFile(%s%s%s): Begin", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "" );
+
+ // Keep a cache of keyvalues, try to load it here.
+ if ( bUseCacheForRead && KeyValuesSystem()->LoadFileKeyValuesFromCache( this, resourceName, pathID, filesystem ) ) {
+ COM_TimestampedLog( "KeyValues::LoadFromFile(%s%s%s): End / CacheHit", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "" );
+ return true;
+ }
+
FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
if ( !f )
+ {
+ COM_TimestampedLog("KeyValues::LoadFromFile(%s%s%s): End / FileNotFound", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
return false;
+ }
s_LastFileLoadingFrom = (char*)resourceName;
@@ -660,28 +720,41 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN
buffer[fileSize+1] = 0; // double NULL terminating in case this is a unicode file
bRetOK = LoadFromBuffer( resourceName, buffer, filesystem );
}
+
+ // The cache relies on the KeyValuesSystem string table, which will only be valid if we're
+ // using classic mode.
+ if ( bUseCacheForWrite && bRetOK )
+ {
+ KeyValuesSystem()->AddFileKeyValuesToCache( this, resourceName, pathID );
+ }
- ((IFileSystem *)filesystem)->FreeOptimalReadBuffer( buffer );
+ ( (IFileSystem *)filesystem )->FreeOptimalReadBuffer( buffer );
+
+ COM_TimestampedLog("KeyValues::LoadFromFile(%s%s%s): End / Success", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
return bRetOK;
}
//-----------------------------------------------------------------------------
// Purpose: Save the keyvalues to disk
-// Creates the path to the file if it doesn't exist
+// Creates the path to the file if it doesn't exist
//-----------------------------------------------------------------------------
-bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/ )
+bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/, bool bCacheResult /*= false*/ )
{
// create a write file
FileHandle_t f = filesystem->Open(resourceName, "wb", pathID);
if ( f == FILESYSTEM_INVALID_HANDLE )
{
- DevMsg(1, "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n",
+ DevMsg(1, "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n",
resourceName?resourceName:"NULL", pathID?pathID:"NULL" );
return false;
}
+ KeyValuesSystem()->InvalidateCacheForFile( resourceName, pathID );
+ if ( bCacheResult ) {
+ KeyValuesSystem()->AddFileKeyValuesToCache( this, resourceName, pathID );
+ }
RecursiveSaveToFile(filesystem, f, NULL, 0, sortKeys, bAllowEmptyString );
filesystem->Close(f);
@@ -1671,115 +1744,138 @@ void KeyValues::SetPtr( const char *keyName, void *value )
}
}
-void KeyValues::RecursiveCopyKeyValues( KeyValues& src )
+//-----------------------------------------------------------------------------
+// Purpose: Copies the tree from the other KeyValues into this one, recursively
+// beginning with the root specified by rootSrc.
+//-----------------------------------------------------------------------------
+void KeyValues::CopyKeyValuesFromRecursive( const KeyValues& rootSrc )
{
- // garymcthack - need to check this code for possible buffer overruns.
-
- m_iKeyName = src.GetNameSymbol();
+ // This code used to be recursive, which was more elegant. Unfortunately, it also blew the stack for large
+ // KeyValues. So now we have the iterative version which is uglier but doesn't blow the stack.
+ // This uses breadth-first traversal.
+
+ struct CopyStruct
+ {
+ KeyValues* dst;
+ const KeyValues* src;
+ };
+
+ char tmp[256];
+ KeyValues* localDst = NULL;
- if( !src.m_pSub )
+ CUtlQueue<CopyStruct> nodeQ;
+ nodeQ.Insert({ this, &rootSrc });
+
+ while ( nodeQ.Count() > 0 )
{
- m_iDataType = src.m_iDataType;
- char buf[256];
- switch( src.m_iDataType )
+ CopyStruct cs = nodeQ.RemoveAtHead();
+
+ // Process all the siblings of the current node. If anyone has a child, add it to the queue.
+ while (cs.src)
{
- case TYPE_NONE:
- break;
- case TYPE_STRING:
- if( src.m_sValue )
- {
- int len = Q_strlen(src.m_sValue) + 1;
- m_sValue = new char[len];
- Q_strncpy( m_sValue, src.m_sValue, len );
- }
- break;
- case TYPE_INT:
- {
- m_iValue = src.m_iValue;
- Q_snprintf( buf,sizeof(buf), "%d", m_iValue );
- int len = Q_strlen(buf) + 1;
- m_sValue = new char[len];
- Q_strncpy( m_sValue, buf, len );
- }
- break;
- case TYPE_FLOAT:
- {
- m_flValue = src.m_flValue;
- Q_snprintf( buf,sizeof(buf), "%f", m_flValue );
- int len = Q_strlen(buf) + 1;
- m_sValue = new char[len];
- Q_strncpy( m_sValue, buf, len );
- }
- break;
- case TYPE_PTR:
- {
- m_pValue = src.m_pValue;
- }
- break;
- case TYPE_UINT64:
- {
- m_sValue = new char[sizeof(uint64)];
- Q_memcpy( m_sValue, src.m_sValue, sizeof(uint64) );
+ Assert( (cs.src != NULL) == (cs.dst != NULL) );
+
+ // Copy the node contents
+ cs.dst->CopyKeyValue( *cs.src, sizeof(tmp), tmp );
+
+ // Add children to the queue to process later.
+ if (cs.src->m_pSub) {
+ cs.dst->m_pSub = localDst = new KeyValues( NULL );
+ nodeQ.Insert({ localDst, cs.src->m_pSub });
}
- break;
- case TYPE_COLOR:
- {
- m_Color[0] = src.m_Color[0];
- m_Color[1] = src.m_Color[1];
- m_Color[2] = src.m_Color[2];
- m_Color[3] = src.m_Color[3];
+
+ // Process siblings until we hit the end of the line.
+ if (cs.src->m_pPeer) {
+ cs.dst->m_pPeer = new KeyValues( NULL );
}
- break;
-
- default:
- {
- // do nothing . .what the heck is this?
- Assert( 0 );
+ else {
+ cs.dst->m_pPeer = NULL;
}
- break;
- }
+ // Advance to the next peer.
+ cs.src = cs.src->m_pPeer;
+ cs.dst = cs.dst->m_pPeer;
+ }
}
-#if 0
- KeyValues *pDst = this;
- for ( KeyValues *pSrc = src.m_pSub; pSrc; pSrc = pSrc->m_pPeer )
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies a single KeyValue from src to this, using the provided temporary
+// buffer if the keytype requires it. Does NOT recurse.
+//-----------------------------------------------------------------------------
+void KeyValues::CopyKeyValue( const KeyValues& src, size_t tmpBufferSizeB, char* tmpBuffer )
+{
+ m_iKeyName = src.GetNameSymbol();
+
+ if ( src.m_pSub )
+ return;
+
+ m_iDataType = src.m_iDataType;
+
+ switch( src.m_iDataType )
{
- if ( pSrc->m_pSub )
+ case TYPE_NONE:
+ break;
+ case TYPE_STRING:
+ if( src.m_sValue )
{
- pDst->m_pSub = new KeyValues( pSrc->m_pSub->getName() );
- pDst->m_pSub->RecursiveCopyKeyValues( *pSrc->m_pSub );
+ int len = Q_strlen(src.m_sValue) + 1;
+ m_sValue = new char[len];
+ Q_strncpy( m_sValue, src.m_sValue, len );
}
- else
+ break;
+ case TYPE_INT:
{
- // copy non-empty keys
- if ( pSrc->m_sValue && *(pSrc->m_sValue) )
- {
- pDst->m_pPeer = new KeyValues(
- }
+ m_iValue = src.m_iValue;
+ Q_snprintf( tmpBuffer, tmpBufferSizeB, "%d", m_iValue );
+ int len = Q_strlen(tmpBuffer) + 1;
+ m_sValue = new char[len];
+ Q_strncpy( m_sValue, tmpBuffer, len );
}
- }
-#endif
-
- // Handle the immediate child
- if( src.m_pSub )
- {
- m_pSub = new KeyValues( NULL );
- m_pSub->RecursiveCopyKeyValues( *src.m_pSub );
- }
-
- // Handle the immediate peer
- if( src.m_pPeer )
- {
- m_pPeer = new KeyValues( NULL );
- m_pPeer->RecursiveCopyKeyValues( *src.m_pPeer );
+ break;
+ case TYPE_FLOAT:
+ {
+ m_flValue = src.m_flValue;
+ Q_snprintf( tmpBuffer, tmpBufferSizeB, "%f", m_flValue );
+ int len = Q_strlen(tmpBuffer) + 1;
+ m_sValue = new char[len];
+ Q_strncpy( m_sValue, tmpBuffer, len );
+ }
+ break;
+ case TYPE_PTR:
+ {
+ m_pValue = src.m_pValue;
+ }
+ break;
+ case TYPE_UINT64:
+ {
+ m_sValue = new char[sizeof(uint64)];
+ Q_memcpy( m_sValue, src.m_sValue, sizeof(uint64) );
+ }
+ break;
+ case TYPE_COLOR:
+ {
+ m_Color[0] = src.m_Color[0];
+ m_Color[1] = src.m_Color[1];
+ m_Color[2] = src.m_Color[2];
+ m_Color[3] = src.m_Color[3];
+ }
+ break;
+
+ default:
+ {
+ // do nothing . .what the heck is this?
+ Assert( 0 );
+ }
+ break;
}
}
-KeyValues& KeyValues::operator=( KeyValues& src )
+KeyValues& KeyValues::operator=( const KeyValues& src )
{
RemoveEverything();
Init(); // reset all values
- RecursiveCopyKeyValues( src );
+ CopyKeyValuesFromRecursive( src );
return *this;
}
@@ -1878,6 +1974,25 @@ KeyValues *KeyValues::MakeCopy( void ) const
return newKeyValue;
}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *KeyValues::MakeCopy( bool copySiblings ) const
+{
+ KeyValues* rootDest = MakeCopy();
+ if ( !copySiblings )
+ return rootDest;
+
+ const KeyValues* curSrc = GetNextKey();
+ KeyValues* curDest = rootDest;
+ while (curSrc) {
+ curDest->SetNextKey( curSrc->MakeCopy() );
+ curDest = curDest->GetNextKey();
+ curSrc = curSrc->GetNextKey();
+ }
+
+ return rootDest;
+}
//-----------------------------------------------------------------------------
// Purpose: Check if a keyName has no value assigned to it.
@@ -2243,6 +2358,8 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, I
if ( !pBuffer )
return true;
+ COM_TimestampedLog("KeyValues::LoadFromBuffer(%s%s%s): Begin", pPathID ? pPathID : "", pPathID && resourceName ? "/" : "", resourceName ? resourceName : "");
+
int nLen = Q_strlen( pBuffer );
CUtlBuffer buf( pBuffer, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
@@ -2255,7 +2372,11 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, I
buf.AssumeMemory( pUTF8Buf, nUTF8Len, nUTF8Len, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
}
- return LoadFromBuffer( resourceName, buf, pFileSystem, pPathID );
+ bool retVal = LoadFromBuffer( resourceName, buf, pFileSystem, pPathID );
+
+ COM_TimestampedLog("KeyValues::LoadFromBuffer(%s%s%s): End", pPathID ? pPathID : "", pPathID && resourceName ? "/" : "", resourceName ? resourceName : "");
+
+ return retVal;
}
//-----------------------------------------------------------------------------
@@ -2266,6 +2387,12 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b
CKeyErrorContext errorReport(this);
bool wasQuoted;
bool wasConditional;
+ if ( errorReport.GetStackLevel() > 100 )
+ {
+ g_KeyValuesErrorStack.ReportError( "RecursiveLoadFromBuffer: recursion overflow" );
+ return;
+ }
+
// keep this out of the stack until a key is parsed
CKeyErrorContext errorKey( INVALID_KEY_SYMBOL );
@@ -2570,7 +2697,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth )
{
char token[KEYVALUES_TOKEN_SIZE];
- buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 );
+ buffer.GetString( token );
token[KEYVALUES_TOKEN_SIZE-1] = 0;
dat->SetName( token );
}
@@ -2586,7 +2713,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth )
case TYPE_STRING:
{
char token[KEYVALUES_TOKEN_SIZE];
- buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 );
+ buffer.GetString( token );
token[KEYVALUES_TOKEN_SIZE-1] = 0;
int len = Q_strlen( token );
@@ -3039,4 +3166,4 @@ bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText )
Msg( "%s", szText );
}
return true;
-} \ No newline at end of file
+}