Last active
          January 19, 2021 11:20 
        
      - 
      
- 
        Save xPaw/47e28b4160e7aa8961ec313df22494bb to your computer and use it in GitHub Desktop. 
Revisions
- 
        xPaw revised this gist Jan 19, 2021 . 1 changed file with 876 additions and 316 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,7 +28,7 @@ #include "utlvector.h" #include "utlbuffer.h" #include "utlhash.h" #include "tier0/vprof.h" // memdbgon must be the last include file in a .cpp file!!! #include <tier0/memdbgon.h> @@ -48,8 +48,7 @@ int (*KeyValues::s_pfGetSymbolForString)( const char *name, bool bCreate ) = &Ke const char *(*KeyValues::s_pfGetStringForSymbol)( int symbol ) = &KeyValues::GetStringForSymbolClassic; CKeyValuesGrowableStringTable *KeyValues::s_pGrowableStringTable = NULL; #define KEYVALUES_TOKEN_SIZE (1024 * 32) #define INTERNALWRITE( pData, len ) InternalWrite( filesystem, f, pBuf, pData, len ) @@ -58,12 +57,13 @@ static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; CExpressionEvaluator g_ExpressionEvaluator; // a simple class to keep track of a stack of valid parsed symbols const int MAX_ERROR_STACK = 64; class CKeyValuesErrorStack { public: CKeyValuesErrorStack() : m_pFilename("NULL"), m_errorIndex(0), m_maxErrorIndex(0), m_bEncounteredErrors(false) {} void SetFilename( const char *pFilename ) { @@ -95,7 +95,8 @@ class CKeyValuesErrorStack void Reset( int stackLevel, int symName ) { Assert( stackLevel >= 0 && stackLevel < m_errorIndex ); if ( stackLevel < MAX_ERROR_STACK ) m_errorStack[stackLevel] = symName; } // Hit an error, report it and the parsing stack for context @@ -104,7 +105,7 @@ class CKeyValuesErrorStack Warning( "KeyValues Error: %s in file %s\n", pError, m_pFilename ); for ( int i = 0; i < m_maxErrorIndex; i++ ) { if ( i < MAX_ERROR_STACK && m_errorStack[i] != INVALID_KEY_SYMBOL ) { if ( i < m_errorIndex ) { @@ -117,16 +118,75 @@ class CKeyValuesErrorStack } } Warning( "\n" ); m_bEncounteredErrors = true; } bool EncounteredAnyErrors() { return m_bEncounteredErrors; } void ClearErrorFlag() { m_bEncounteredErrors = false; } private: int m_errorStack[MAX_ERROR_STACK]; const char *m_pFilename; int m_errorIndex; int m_maxErrorIndex; bool m_bEncounteredErrors; } g_KeyValuesErrorStack; // This class gets the tokens out of a CUtlBuffer for KeyValues. // Since KeyValues likes to seek backwards and seeking won't work with a text-mode CUtlStreamBuffer // (which is what dmserializers uses), this class allows you to seek back one token. class CKeyValuesTokenReader { public: CKeyValuesTokenReader( KeyValues *pKeyValues, CUtlBuffer &buf ); const char* ReadToken( bool &wasQuoted, bool &wasConditional ); void SeekBackOneToken(); private: KeyValues *m_pKeyValues; CUtlBuffer &m_Buffer; int m_nTokensRead; bool m_bUsePriorToken; bool m_bPriorTokenWasQuoted; bool m_bPriorTokenWasConditional; static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; }; char CKeyValuesTokenReader::s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; CKeyValuesTokenReader::CKeyValuesTokenReader( KeyValues *pKeyValues, CUtlBuffer &buf ) : m_Buffer( buf ) { m_pKeyValues = pKeyValues; m_nTokensRead = 0; m_bUsePriorToken = false; } void CKeyValuesTokenReader::SeekBackOneToken() { if ( m_bUsePriorToken ) Plat_FatalError( "CKeyValuesTokenReader::SeekBackOneToken: It is only possible to seek back one token at a time" ); if ( m_nTokensRead == 0 ) Plat_FatalError( "CkeyValuesTokenReader::SeekBackOneToken: No tokens read yet" ); m_bUsePriorToken = true; } // a simple helper that creates stack entries as it goes in & out of scope class CKeyErrorContext { @@ -143,6 +203,10 @@ class CKeyErrorContext { g_KeyValuesErrorStack.Reset( m_stackLevel, symName ); } int GetStackLevel() const { return m_stackLevel; } private: void Init( int symName ) { @@ -180,7 +244,7 @@ class CLeakTrack void AddKv( KeyValues *kv, char const *name ) { kve k; V_strncpy( k.name, name ? name : "NULL", sizeof( k.name ) ); k.kv = kv; keys.AddToTail( k ); @@ -230,45 +294,45 @@ class CKeyValuesGrowableStringTable m_vecStrings.AddToTail( '\0' ); } // Translates a string to an index int GetSymbolForString( const char *name, bool bCreate = true ) { AUTO_LOCK( m_mutex ); // Put the current details into our hash functor m_Functor.SetCurString( name ); m_Functor.SetCurStringBase( (const char *)m_vecStrings.Base() ); if ( bCreate ) { bool bInserted = false; UtlHashHandle_t hElement = m_hashLookup.Insert( -1, &bInserted ); if ( bInserted ) { int iIndex = m_vecStrings.AddMultipleToTail( V_strlen( name ) + 1, name ); m_hashLookup[ hElement ] = iIndex; } return m_hashLookup[ hElement ]; } else { UtlHashHandle_t hElement = m_hashLookup.Find( -1 ); if ( m_hashLookup.IsValidHandle( hElement ) ) return m_hashLookup[ hElement ]; else return -1; } } // Translates an index back to a string const char *GetStringForSymbol( int symbol ) { return (const char *)m_vecStrings.Base() + symbol; } private: // A class plugged into CUtlHash that allows us to change the behavior of the table // and store only the index in the table. class CLookupFunctor @@ -285,7 +349,7 @@ class CKeyValuesGrowableStringTable { const char *pchLhs = nLhs > 0 ? m_pchCurBase + nLhs : m_pchCurString; const char *pchRhs = nRhs > 0 ? m_pchCurBase + nRhs : m_pchCurString; return ( 0 == V_stricmp( pchLhs, pchRhs ) ); } @@ -447,7 +511,7 @@ void KeyValues::Init() m_sValue = NULL; m_wsValue = NULL; m_pValue = NULL; m_bHasEscapeSequences = 0; } @@ -461,6 +525,14 @@ KeyValues::~KeyValues() RemoveEverything(); } // for backwards compat - we used to need this to force the free to run from the same DLL // as the alloc void KeyValues::deleteThis() { delete this; } //----------------------------------------------------------------------------- // Purpose: remove everything //----------------------------------------------------------------------------- @@ -513,96 +585,93 @@ void KeyValues::ChainKeyValue( KeyValues* pChain ) //----------------------------------------------------------------------------- const char *KeyValues::GetName( void ) const { AssertMsg( this, "Member function called on NULL KeyValues" ); return this ? KeyValuesSystem()->GetStringForSymbol( MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) ) : ""; } const char* CKeyValuesTokenReader::ReadToken( bool &wasQuoted, bool &wasConditional ) { if ( m_bUsePriorToken ) { m_bUsePriorToken = false; wasQuoted = m_bPriorTokenWasQuoted; wasConditional = m_bPriorTokenWasConditional; return s_pTokenBuf; } m_bPriorTokenWasQuoted = wasQuoted = false; m_bPriorTokenWasConditional = wasConditional = false; if ( !m_Buffer.IsValid() ) return NULL; // eating white spaces and remarks loop while ( true ) { m_Buffer.EatWhiteSpace(); if ( !m_Buffer.IsValid() ) { return NULL; // file ends after reading whitespaces } // stop if it's not a comment; a new token starts here if ( !m_Buffer.EatCPPComment() ) break; } const char *c = (const char*)m_Buffer.PeekGet( sizeof(char), 0 ); if ( !c ) { return NULL; } // read quoted strings specially if ( *c == '\"' ) { m_bPriorTokenWasQuoted = wasQuoted = true; m_Buffer.GetDelimitedString( m_pKeyValues->m_bHasEscapeSequences ? GetCStringCharConversion() : GetNoEscCharConversion(), s_pTokenBuf, KEYVALUES_TOKEN_SIZE ); ++m_nTokensRead; return s_pTokenBuf; } if ( *c == '{' || *c == '}' || *c == '=' ) { // it's a control char, just add this one char and stop reading s_pTokenBuf[0] = *c; s_pTokenBuf[1] = 0; m_Buffer.GetChar(); ++m_nTokensRead; return s_pTokenBuf; } // read in the token until we hit a whitespace or a control character bool bReportedError = false; bool bConditionalStart = false; int nCount = 0; while ( 1 ) { c = (const char*)m_Buffer.PeekGet( sizeof(char), 0 ); // end of file if ( !c || *c == 0 ) break; // break if any control character appears in non quoted tokens if ( *c == '"' || *c == '{' || *c == '}' || *c == '=' ) break; if ( *c == '[' ) bConditionalStart = true; if ( *c == ']' && bConditionalStart ) { m_bPriorTokenWasConditional = wasConditional = true; bConditionalStart = false; } // break on whitespace if ( V_isspace(*c) && !bConditionalStart ) break; @@ -616,14 +685,29 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon g_KeyValuesErrorStack.ReportError(" ReadToken overflow" ); } m_Buffer.GetChar(); } s_pTokenBuf[ nCount ] = 0; ++m_nTokensRead; return s_pTokenBuf; } //----------------------------------------------------------------------------- // Purpose: Get the symbol name of the current key section //----------------------------------------------------------------------------- int KeyValues::GetNameSymbol() const { AssertMsg( this, "Member function called on NULL KeyValues" ); return this ? m_iKeyName : INVALID_KEY_SYMBOL; } int KeyValues::GetNameSymbolCaseSensitive() const { AssertMsg( this, "Member function called on NULL KeyValues" ); return this ? MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) : INVALID_KEY_SYMBOL; } //----------------------------------------------------------------------------- // Purpose: if parser should translate escape sequences ( /n, /t etc), set to true @@ -639,9 +723,8 @@ void KeyValues::UsesEscapeSequences(bool state) //----------------------------------------------------------------------------- bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, GetSymbolProc_t pfnEvaluateSymbolProc ) { //TM_ZONE_FILTERED( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s %s", __FUNCTION__, tmDynamicString( TELEMETRY_LEVEL0, resourceName ) ); FileHandle_t f = filesystem->Open(resourceName, "rb", pathID); if ( !f ) return false; @@ -654,7 +737,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize ); Assert( buffer ); // read into local buffer bool bRetOK = ( ((IFileSystem *)filesystem)->ReadEx( buffer, bufSize, fileSize, f ) != 0 ); @@ -664,21 +747,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN { buffer[fileSize] = 0; // null terminate file as EOF buffer[fileSize+1] = 0; // double NULL terminating in case this is a unicode file bRetOK = LoadFromBuffer( resourceName, buffer, filesystem, pathID, pfnEvaluateSymbolProc ); } ((IFileSystem *)filesystem)->FreeOptimalReadBuffer( buffer ); @@ -690,7 +759,7 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN // Purpose: Save the keyvalues to disk // Creates the path to the file if it doesn't exist //----------------------------------------------------------------------------- bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool bWriteEmptySubkeys ) { // create a write file FileHandle_t f = filesystem->Open(resourceName, "wb", pathID); @@ -702,7 +771,7 @@ bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceNam return false; } RecursiveSaveToFile(filesystem, f, NULL, 0, bWriteEmptySubkeys); filesystem->Close(f); return true; @@ -726,7 +795,7 @@ void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t { // handle double quote chars within the string // the worst possible case is that the whole string is quotes int len = V_strlen(pszString); char *convertedString = (char *) alloca ((len + 1) * sizeof(char) * 2); int j=0; for (int i=0; i <= len; i++) @@ -745,7 +814,7 @@ void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t j++; } INTERNALWRITE(convertedString, V_strlen(convertedString)); } @@ -767,7 +836,7 @@ void KeyValues::InternalWrite( IBaseFileSystem *filesystem, FileHandle_t f, CUtl // Purpose: Save keyvalues from disk, if subkey values are detected, calls // itself to save those //----------------------------------------------------------------------------- void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel, bool bWriteEmptySubkeys ) { // write header WriteIndents( filesystem, f, pBuf, indentLevel ); @@ -782,14 +851,22 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f { if ( dat->m_pSub ) { dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1, bWriteEmptySubkeys ); } else { // only write non-empty keys switch (dat->m_iDataType) { case TYPE_NONE: { if ( bWriteEmptySubkeys ) { dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1, bWriteEmptySubkeys ); } break; } case TYPE_STRING: { if (dat->m_sValue && *(dat->m_sValue)) @@ -816,7 +893,7 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); WriteConvertedString(filesystem, f, pBuf, buf); @@ -831,13 +908,13 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); char buf[32]; V_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue); INTERNALWRITE(buf, V_strlen(buf)); INTERNALWRITE("\"\n", 2); break; } @@ -846,14 +923,14 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); char buf[32]; // write "0x" + 16 char 0-padded hex encoded 64 bit value V_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) ); INTERNALWRITE(buf, V_strlen(buf)); INTERNALWRITE("\"\n", 2); break; } @@ -862,13 +939,13 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); char buf[48]; V_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue); INTERNALWRITE(buf, V_strlen(buf)); INTERNALWRITE("\"\n", 2); break; } @@ -892,6 +969,7 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f //----------------------------------------------------------------------------- KeyValues *KeyValues::FindKey(int keySymbol) const { AssertMsg( this, "Member function called on NULL KeyValues" ); for (KeyValues *dat = this ? m_pSub : NULL; dat != NULL; dat = dat->m_pPeer) { if ( dat->m_iKeyName == (uint32) keySymbol ) @@ -911,6 +989,7 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) // Validate NULL == this early out if ( !this ) { AssertMsg( false, "KeyValues::FindKey called on NULL pointer!" ); // Undefined behavior. Could blow up on a new platform. Don't do it. Assert( !bCreate ); return NULL; } @@ -920,19 +999,27 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) return this; // look for '/' characters deliminating sub fields CUtlVector< char > szBuf; const char *subStr = strchr(keyName, '/'); const char *searchStr = keyName; // pull out the substring if it exists if ( subStr ) { int size = subStr - keyName; Assert( size >= 0 ); Assert( size < 1024 * 1024 ); szBuf.EnsureCount( size + 1 ); V_memcpy( szBuf.Base(), keyName, size ); szBuf[size] = 0; if ( V_strlen( keyName ) > 1 ) { // If the key name is just '/', we don't treat is as a key with subfields, but use the '/' as a key name directly searchStr = szBuf.Base(); } } // lookup the symbol for the search string, // we do not need the case-sensitive symbol at this time // because if the key is found, then it will be found by case-insensitive lookup @@ -971,7 +1058,7 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) { // we need to create a new key dat = new KeyValues( searchStr ); // Assert(dat != NULL); // insert new key at end of list if (lastItem) @@ -993,7 +1080,7 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) return NULL; } } // if we've still got a subStr we need to keep looking deeper in the tree if ( subStr ) { @@ -1014,6 +1101,8 @@ KeyValues *KeyValues::CreateNewKey() int newID = 1; // search for any key with higher values KeyValues *pLastChild = NULL; for (KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer) { // case-insensitive string compare @@ -1022,12 +1111,14 @@ KeyValues *KeyValues::CreateNewKey() { newID = val + 1; } pLastChild = dat; } char buf[12]; V_snprintf( buf, sizeof(buf), "%d", newID ); return CreateKeyUsingKnownLastChild( buf, pLastChild ); } @@ -1036,24 +1127,72 @@ KeyValues *KeyValues::CreateNewKey() //----------------------------------------------------------------------------- KeyValues* KeyValues::CreateKey( const char *keyName ) { KeyValues *pLastChild = FindLastSubKey(); return CreateKeyUsingKnownLastChild( keyName, pLastChild ); } //----------------------------------------------------------------------------- // Create a new sibling key //----------------------------------------------------------------------------- KeyValues* KeyValues::CreatePeerKey( const char *keyName ) { KeyValues* dat = new KeyValues( keyName ); //dat->Internal_SetHasEscapeSequences( Internal_HasEscapeSequences() ); // use same format as peer dat->m_bHasEscapeSequences = m_bHasEscapeSequences; // insert into peer linked list after self. dat->m_pPeer = m_pPeer; m_pPeer = dat; return dat; } //----------------------------------------------------------------------------- KeyValues* KeyValues::CreateKeyUsingKnownLastChild( const char *keyName, KeyValues *pLastChild ) { // Create a new key KeyValues* dat = new KeyValues( keyName ); dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does // add into subkey list AddSubkeyUsingKnownLastChild( dat, pLastChild ); return dat; } //----------------------------------------------------------------------------- void KeyValues::AddSubkeyUsingKnownLastChild( KeyValues *pSubkey, KeyValues *pLastChild ) { // Make sure the subkey isn't a child of some other keyvalues Assert( pSubkey != NULL ); Assert( pSubkey->m_pPeer == NULL ); // Empty child list? if ( pLastChild == NULL ) { Assert( m_pSub == NULL ); m_pSub = pSubkey; } else { Assert( m_pSub != NULL ); Assert( pLastChild->m_pPeer == NULL ); pLastChild->SetNextKey( pSubkey ); } } //----------------------------------------------------------------------------- // Adds a subkey. Make sure the subkey isn't a child of some other keyvalues //----------------------------------------------------------------------------- void KeyValues::AddSubKey( KeyValues *pSubkey ) { // Make sure the subkey isn't a child of some other keyvalues Assert( pSubkey != NULL ); Assert( pSubkey->m_pPeer == NULL ); // add into subkey list @@ -1074,7 +1213,7 @@ void KeyValues::AddSubKey( KeyValues *pSubkey ) } //----------------------------------------------------------------------------- // Purpose: Remove a subkey from the list //----------------------------------------------------------------------------- @@ -1099,7 +1238,7 @@ void KeyValues::RemoveSubKey(KeyValues *subKey) kv->m_pPeer = subKey->m_pPeer; break; } kv = kv->m_pPeer; } } @@ -1196,7 +1335,7 @@ void KeyValues::ElideSubKey( KeyValues *pSubKey ) { // No children, simply remove the key *ppPointerToFix = pSubKey->m_pPeer; delete pSubKey; } else { @@ -1212,7 +1351,7 @@ void KeyValues::ElideSubKey( KeyValues *pSubKey ) // Detach the node to be elided pSubKey->m_pSub = NULL; pSubKey->m_pPeer = NULL; delete pSubKey; } return; } @@ -1222,19 +1361,38 @@ void KeyValues::ElideSubKey( KeyValues *pSubKey ) } //----------------------------------------------------------------------------- // Purpose: Locate last child. Returns NULL if we have no children //----------------------------------------------------------------------------- KeyValues *KeyValues::FindLastSubKey() { // No children? if ( m_pSub == NULL ) return NULL; // Scan for the last one KeyValues *pLastChild = m_pSub; while ( pLastChild->m_pPeer ) pLastChild = pLastChild->m_pPeer; return pLastChild; } //----------------------------------------------------------------------------- // Purpose: Return the first subkey in the list //----------------------------------------------------------------------------- KeyValues *KeyValues::GetFirstSubKey() const { AssertMsg( this, "Member function called on NULL KeyValues" ); return this ? m_pSub : NULL; } //----------------------------------------------------------------------------- // Purpose: Return the next subkey //----------------------------------------------------------------------------- KeyValues *KeyValues::GetNextKey() const { AssertMsg( this, "Member function called on NULL KeyValues" ); return this ? m_pPeer : NULL; } @@ -1249,6 +1407,7 @@ void KeyValues::SetNextKey( KeyValues *pDat ) KeyValues* KeyValues::GetFirstTrueSubKey() { AssertMsg( this, "Member function called on NULL KeyValues" ); KeyValues *pRet = this ? m_pSub : NULL; while ( pRet && pRet->m_iDataType != TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1258,6 +1417,7 @@ KeyValues* KeyValues::GetFirstTrueSubKey() KeyValues* KeyValues::GetNextTrueSubKey() { AssertMsg( this, "Member function called on NULL KeyValues" ); KeyValues *pRet = this ? m_pPeer : NULL; while ( pRet && pRet->m_iDataType != TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1267,6 +1427,7 @@ KeyValues* KeyValues::GetNextTrueSubKey() KeyValues* KeyValues::GetFirstValue() { AssertMsg( this, "Member function called on NULL KeyValues" ); KeyValues *pRet = this ? m_pSub : NULL; while ( pRet && pRet->m_iDataType == TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1276,6 +1437,7 @@ KeyValues* KeyValues::GetFirstValue() KeyValues* KeyValues::GetNextValue() { AssertMsg( this, "Member function called on NULL KeyValues" ); KeyValues *pRet = this ? m_pPeer : NULL; while ( pRet && pRet->m_iDataType == TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1341,8 +1503,9 @@ uint64 KeyValues::GetUint64( const char *keyName, uint64 defaultValue ) return (int)dat->m_flValue; case TYPE_UINT64: return *((uint64 *)dat->m_sValue); case TYPE_PTR: return (uint64)(uintp)dat->m_pValue; case TYPE_INT: default: return dat->m_iValue; }; @@ -1395,7 +1558,7 @@ float KeyValues::GetFloat( const char *keyName, float defaultValue ) #else return (float) wcstof( dat->m_wsValue, (wchar_t **)NULL ); #endif case TYPE_FLOAT: return dat->m_flValue; case TYPE_INT: return (float)dat->m_iValue; @@ -1423,41 +1586,48 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue switch ( dat->m_iDataType ) { case TYPE_FLOAT: V_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue ); SetString( keyName, buf ); break; case TYPE_PTR: V_snprintf( buf, sizeof( buf ), "%lld", CastPtrToInt64( dat->m_pValue ) ); SetString( keyName, buf ); break; case TYPE_INT: V_snprintf( buf, sizeof( buf ), "%d", dat->m_iValue ); SetString( keyName, buf ); break; case TYPE_UINT64: V_snprintf( buf, sizeof( buf ), "%lld", *((uint64 *)(dat->m_sValue)) ); SetString( keyName, buf ); break; case TYPE_COLOR: V_snprintf( buf, sizeof( buf ), "%d %d %d %d", dat->m_Color[0], dat->m_Color[1], dat->m_Color[2], dat->m_Color[3] ); SetString( keyName, buf ); break; case TYPE_WSTRING: { // convert the string to char *, set it for future use, and return it char wideBuf[512]; int result = V_UnicodeToUTF8(dat->m_wsValue, wideBuf, 512); if ( result ) { // note: this will copy wideBuf SetString( keyName, wideBuf ); } else { return defaultValue; } break; } case TYPE_STRING: break; default: return defaultValue; }; return dat->m_sValue; } return defaultValue; @@ -1476,8 +1646,11 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%f", dat->m_flValue); SetWString( keyName, wbuf); break; case TYPE_PTR: swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%lld", (int64)(size_t)dat->m_pValue ); SetWString( keyName, wbuf ); break; case TYPE_INT: swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%d", dat->m_iValue ); SetWString( keyName, wbuf ); break; @@ -1487,30 +1660,34 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul SetWString( keyName, wbuf ); } break; case TYPE_COLOR: swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%d %d %d %d", dat->m_Color[0], dat->m_Color[1], dat->m_Color[2], dat->m_Color[3] ); SetWString( keyName, wbuf ); break; case TYPE_WSTRING: break; case TYPE_STRING: { int bufSize = V_strlen(dat->m_sValue) + 1; wchar_t *pWBuf = new wchar_t[ bufSize ]; int result = V_UTF8ToUnicode(dat->m_sValue, pWBuf, bufSize * sizeof( wchar_t ) ); if ( result >= 0 ) // may be a zero length string { SetWString( keyName, pWBuf); } else { delete [] pWBuf; return defaultValue; } delete [] pWBuf; break; } default: return defaultValue; }; return (const wchar_t* )dat->m_wsValue; } return defaultValue; @@ -1543,7 +1720,7 @@ Color KeyValues::GetColor( const char *keyName , const Color& defaultColor ) else if ( dat->m_iDataType == TYPE_STRING ) { // parse the colors out of the string float a = 0, b = 0, c = 0, d = 0; sscanf(dat->m_sValue, "%f %f %f %f", &a, &b, &c, &d); color[0] = (unsigned char)a; color[1] = (unsigned char)b; @@ -1586,9 +1763,9 @@ void KeyValues::SetStringValue( char const *strValue ) } // allocate memory for the new value and copy it in int len = V_strlen( strValue ); m_sValue = new char[len + 1]; V_memcpy( m_sValue, strValue, len+1 ); m_iDataType = TYPE_STRING; } @@ -1625,9 +1802,9 @@ void KeyValues::SetWString( const char *keyName, const wchar_t *value ) } // allocate memory for the new value and copy it in int len = V_wcslen( value ); dat->m_wsValue = new wchar_t[len + 1]; V_memcpy( dat->m_wsValue, value, (len+1) * sizeof(wchar_t) ); dat->m_iDataType = TYPE_WSTRING; } @@ -1686,7 +1863,7 @@ void KeyValues::SetName( const char * setName ) { HKeySymbol hCaseSensitiveKeyName = INVALID_KEY_SYMBOL, hCaseInsensitiveKeyName = INVALID_KEY_SYMBOL; hCaseSensitiveKeyName = KeyValuesSystem()->GetSymbolForStringCaseSensitive( hCaseInsensitiveKeyName, setName ); m_iKeyName = hCaseInsensitiveKeyName; SPLIT_3_BYTES_INTO_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2, hCaseSensitiveKeyName ); } @@ -1708,7 +1885,7 @@ void KeyValues::SetPtr( const char *keyName, void *value ) void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) { // garymcthack - need to check this code for possible buffer overruns. m_iKeyName = src.m_iKeyName; m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1; m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2; @@ -1724,27 +1901,27 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) case TYPE_STRING: if( src.m_sValue ) { int len = V_strlen(src.m_sValue) + 1; m_sValue = new char[len]; V_strncpy( m_sValue, src.m_sValue, len ); } break; case TYPE_INT: { m_iValue = src.m_iValue; V_snprintf( buf,sizeof(buf), "%d", m_iValue ); int len = V_strlen(buf) + 1; m_sValue = new char[len]; V_strncpy( m_sValue, buf, len ); } break; case TYPE_FLOAT: { m_flValue = src.m_flValue; V_snprintf( buf,sizeof(buf), "%f", m_flValue ); int len = V_strlen(buf) + 1; m_sValue = new char[len]; V_strncpy( m_sValue, buf, len ); } break; case TYPE_PTR: @@ -1755,7 +1932,7 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) case TYPE_UINT64: { m_sValue = new char[sizeof(uint64)]; V_memcpy( m_sValue, src.m_sValue, sizeof(uint64) ); } break; case TYPE_COLOR: @@ -1766,7 +1943,7 @@ void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) m_Color[3] = src.m_Color[3]; } break; default: { // do nothing . .what the heck is this? @@ -1832,7 +2009,7 @@ void KeyValues::CopySubkeys( KeyValues *pParent ) const { // take a copy of the subkey KeyValues *dat = sub->MakeCopy(); // add into subkey list if (pPrev) { @@ -1863,20 +2040,20 @@ KeyValues *KeyValues::MakeCopy( void ) const { if ( m_sValue ) { int len = V_strlen( m_sValue ); Assert( !newKeyValue->m_sValue ); newKeyValue->m_sValue = new char[len + 1]; V_memcpy( newKeyValue->m_sValue, m_sValue, len+1 ); } } break; case TYPE_WSTRING: { if ( m_wsValue ) { int len = V_wcslen( m_wsValue ); newKeyValue->m_wsValue = new wchar_t[len+1]; V_memcpy( newKeyValue->m_wsValue, m_wsValue, (len+1)*sizeof(wchar_t)); } } break; @@ -1892,7 +2069,7 @@ KeyValues *KeyValues::MakeCopy( void ) const case TYPE_PTR: newKeyValue->m_pValue = m_pValue; break; case TYPE_COLOR: newKeyValue->m_Color[0] = m_Color[0]; newKeyValue->m_Color[1] = m_Color[1]; @@ -1902,7 +2079,7 @@ KeyValues *KeyValues::MakeCopy( void ) const case TYPE_UINT64: newKeyValue->m_sValue = new char[sizeof(uint64)]; V_memcpy( newKeyValue->m_sValue, m_sValue, sizeof(uint64) ); break; }; @@ -1949,14 +2126,12 @@ KeyValues::types_t KeyValues::GetDataType(const char *keyName) return TYPE_NONE; } KeyValues::types_t KeyValues::GetDataType( void ) const { return (types_t)m_iDataType; } //----------------------------------------------------------------------------- // Purpose: // Input : includedKeys - @@ -1982,12 +2157,12 @@ void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) } void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoinclude, IBaseFileSystem* pFileSystem, const char *pPathID, CUtlVector< KeyValues * >& includedKeys, GetSymbolProc_t pfnEvaluateSymbolProc ) { Assert( resourceName ); Assert( filetoinclude ); Assert( pFileSystem ); // Load it... if ( !pFileSystem ) { @@ -1996,20 +2171,20 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi // Get relative subdirectory char fullpath[ 512 ]; V_strncpy( fullpath, resourceName, sizeof( fullpath ) ); // Strip off characters back to start or first / bool done = false; int len = V_strlen( fullpath ); while ( !done ) { if ( len <= 0 ) { break; } if ( fullpath[ len - 1 ] == '\\' || fullpath[ len - 1 ] == '/' ) { break; } @@ -2020,7 +2195,7 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi } // Append included file V_strncat( fullpath, filetoinclude, sizeof( fullpath ), COPY_ALL_CHARACTERS ); KeyValues *newKV = new KeyValues( fullpath ); @@ -2035,7 +2210,7 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi else { DevMsg( "KeyValues::ParseIncludedKeys: Couldn't load included keyvalue file %s\n", fullpath ); delete newKV; } // s_CurrentFileSymbol = save; @@ -2077,7 +2252,7 @@ void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV ) // If we have a child by the same name, merge those keys for ( KeyValues *newChild = m_pSub; newChild != NULL; newChild = newChild->m_pPeer ) { if ( !V_strcmp( baseChild->GetName(), newChild->GetName() ) ) { newChild->RecursiveMergeKeyValues( baseChild ); bFoundMatch = true; @@ -2130,7 +2305,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase buf.GetUnsignedChar(); // get the pool identifier, allows the fs to bind the expected string pool unsigned int poolKey = buf.GetUnsignedInt(); RemoveEverything(); Init(); @@ -2144,13 +2319,15 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase CUtlVector< KeyValues * > baseKeys; bool wasQuoted; bool wasConditional; CKeyValuesTokenReader tokenReader( this, buf ); g_KeyValuesErrorStack.SetFilename( resourceName ); do { bool bAccepted = true; // the first thing must be a key const char *s = tokenReader.ReadToken( wasQuoted, wasConditional ); if ( !buf.IsValid() || !s ) break; @@ -2161,9 +2338,9 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase break; } if ( !V_stricmp( s, "#include" ) ) // special include macro (not a key name) { s = tokenReader.ReadToken( wasQuoted, wasConditional ); // Name of subfile to load is now in s if ( !s || *s == 0 ) @@ -2177,9 +2354,9 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase continue; } else if ( !V_stricmp( s, "#base" ) ) { s = tokenReader.ReadToken( wasQuoted, wasConditional ); // Name of subfile to load is now in s if ( !s || *s == 0 ) @@ -2212,20 +2389,20 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase } // get the '{' s = tokenReader.ReadToken( wasQuoted, wasConditional ); if ( wasConditional ) { bAccepted = EvaluateConditional( s, pfnEvaluateSymbolProc ); // Now get the '{' s = tokenReader.ReadToken( wasQuoted, wasConditional ); } if ( s && *s == '{' && !wasQuoted ) { // header is valid so load the file pCurrentKey->RecursiveLoadFromBuffer( resourceName, tokenReader, pfnEvaluateSymbolProc ); } else { @@ -2254,7 +2431,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase for ( i = includedKeys.Count() - 1; i > 0; i-- ) { KeyValues *kv = includedKeys[ i ]; delete kv; } } @@ -2265,13 +2442,14 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase for ( i = baseKeys.Count() - 1; i >= 0; i-- ) { KeyValues *kv = baseKeys[ i ]; delete kv; } } bool bErrors = g_KeyValuesErrorStack.EncounteredAnyErrors(); g_KeyValuesErrorStack.SetFilename( "" ); g_KeyValuesErrorStack.ClearErrorFlag(); return !bErrors; } @@ -2309,19 +2487,32 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, I //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CKeyValuesTokenReader &tokenReader, GetSymbolProc_t pfnEvaluateSymbolProc ) { CKeyErrorContext errorReport( GetNameSymbolCaseSensitive() ); 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 ); // Locate the last child. (Almost always, we will not have any children.) // We maintain the pointer to the last child here, so we don't have to re-locate // it each time we append the next subkey, which causes O(N^2) time KeyValues *pLastChild = FindLastSubKey(); // Keep parsing until we hit the closing brace which terminates this block, or a parse error while ( 1 ) { bool bAccepted = true; // get the key name const char * name = tokenReader.ReadToken( wasQuoted, wasConditional ); if ( !name ) // EOF stop reading { @@ -2340,27 +2531,60 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b // Always create the key; note that this could potentially // cause some duplication, but that's what we want sometimes KeyValues *dat = CreateKeyUsingKnownLastChild( name, pLastChild ); errorKey.Reset( dat->GetNameSymbolCaseSensitive() ); // get the value const char * value = tokenReader.ReadToken( wasQuoted, wasConditional ); bool bFoundConditional = wasConditional; if ( wasConditional && value ) { bAccepted = EvaluateConditional( value, pfnEvaluateSymbolProc ); // get the real value value = tokenReader.ReadToken( wasQuoted, wasConditional ); } if ( !value ) { g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got NULL key" ); break; } // support the '=' as an assignment, makes multiple-keys-on-one-line easier to read in a keyvalues file if ( *value == '=' && !wasQuoted ) { // just skip over it value = tokenReader.ReadToken( wasQuoted, wasConditional ); bFoundConditional = wasConditional; if ( wasConditional && value ) { bAccepted = EvaluateConditional( value, pfnEvaluateSymbolProc ); // get the real value value = tokenReader.ReadToken( wasQuoted, wasConditional ); } if ( bFoundConditional && bAccepted ) { // if there is a conditional key see if we already have the key defined and blow it away, last one in the list wins KeyValues *pExistingKey = this->FindKey( dat->GetNameSymbol() ); if ( pExistingKey && pExistingKey != dat ) { this->RemoveSubKey( pExistingKey ); pExistingKey->deleteThis(); } } } if ( !value ) { g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got NULL key" ); break; } if ( *value == '}' && !wasQuoted ) { g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got } in key" ); @@ -2372,7 +2596,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b // this isn't a key, it's a section errorKey.Reset( INVALID_KEY_SYMBOL ); // sub value list dat->RecursiveLoadFromBuffer( resourceName, tokenReader, pfnEvaluateSymbolProc ); } else { @@ -2381,23 +2605,23 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer: got conditional between key and value" ); break; } if (dat->m_sValue) { delete[] dat->m_sValue; dat->m_sValue = NULL; } int len = V_strlen( value ); // Here, let's determine if we got a float or an int.... char* pIEnd; // pos where int scan ended char* pFEnd; // pos where float scan ended const char* pSEnd = value + len ; // pos where token ends long lval = strtol( value, &pIEnd, 10 ); float fval = (float)strtod( value, &pFEnd ); bool bOverflow = ( lval == LONG_MAX || lval == LONG_MIN ) && errno == ERANGE; #ifdef POSIX // strtod supports hex representation in strings under posix but we DON'T // want that support in keyvalues, so undo it here if needed @@ -2407,7 +2631,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b pFEnd = (char *)value; } #endif if ( *value == 0 ) { dat->m_iDataType = TYPE_STRING; @@ -2437,7 +2661,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b } else if (pIEnd == pSEnd && !bOverflow) { dat->m_iValue = size_cast< int >( lval ); dat->m_iDataType = TYPE_INT; } else @@ -2449,26 +2673,42 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b { // copy in the string information dat->m_sValue = new char[len+1]; V_memcpy( dat->m_sValue, value, len+1 ); } // Look ahead one token for a conditional tag const char *peek = tokenReader.ReadToken( wasQuoted, wasConditional ); if ( wasConditional ) { bAccepted = EvaluateConditional( peek, pfnEvaluateSymbolProc ); } else { tokenReader.SeekBackOneToken(); } } Assert( dat->m_pPeer == NULL ); if ( bAccepted ) { Assert( pLastChild == NULL || pLastChild->m_pPeer == dat ); pLastChild = dat; } else { //this->RemoveSubKey( dat ); if ( pLastChild == NULL ) { Assert( this->m_pSub == dat ); this->m_pSub = NULL; } else { Assert( pLastChild->m_pPeer == dat ); pLastChild->m_pPeer = NULL; } delete dat; dat = NULL; } } @@ -2484,7 +2724,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) const return false; // Write subkeys: // loop through all our peers for ( const KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer ) { @@ -2516,7 +2756,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) const } case TYPE_WSTRING: { int nLength = dat->m_wsValue ? V_wcslen( dat->m_wsValue ) : 0; buffer.PutShort( nLength ); for( int k = 0; k < nLength; ++ k ) { @@ -2552,7 +2792,17 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) const } case TYPE_PTR: { #if defined( PLATFORM_64BITS ) // We only put an int here, because 32-bit clients do not expect 64 bits. It'll cause them to read the wrong // amount of data and then crash. Longer term, we may bump this up in size on all platforms, but short term // we don't really have much of a choice other than sticking in something that appears to not be NULL. if ( dat->m_pValue != 0 && ( ( (int)(intp)dat->m_pValue ) == 0 ) ) buffer.PutInt( 31337 ); // Put not 0, but not a valid number. Yuck. else buffer.PutInt( ( (int)(intp)dat->m_pValue ) ); #else buffer.PutPtr( dat->m_pValue ); #endif break; } @@ -2568,7 +2818,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) const } // read KeyValues from binary buffer, returns true if parsing was successful bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) { if ( buffer.IsText() ) // must be a binary buffer return false; @@ -2578,11 +2828,16 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) RemoveEverything(); // remove current content Init(); // reset if ( nStackDepth > 100 ) { AssertMsgOnce( false, "KeyValues::ReadAsBinary() stack depth > 100\n" ); return false; } KeyValues *dat = this; types_t type = (types_t)buffer.GetUnsignedChar(); // loop through all our peers while ( true ) { @@ -2591,36 +2846,39 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) dat->m_iDataType = type; { char token[ KEYVALUES_TOKEN_SIZE ]; buffer.GetString( token, KEYVALUES_TOKEN_SIZE - 1 ); token[ KEYVALUES_TOKEN_SIZE - 1 ] = 0; dat->SetName( token ); } switch ( type ) { case TYPE_NONE: { dat->m_pSub = new KeyValues(""); dat->m_pSub->ReadAsBinary( buffer, nStackDepth + 1 ); break; } case TYPE_STRING: { char token[ KEYVALUES_TOKEN_SIZE ]; buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); token[KEYVALUES_TOKEN_SIZE-1] = 0; int len = V_strlen( token ); dat->m_sValue = new char[len + 1]; V_memcpy( dat->m_sValue, token, len+1 ); break; } case TYPE_WSTRING: { int nLength = buffer.GetShort(); dat->m_wsValue = new wchar_t[nLength + 1]; for( int k = 0; k < nLength; ++ k ) { dat->m_wsValue[k] = buffer.GetShort(); @@ -2657,7 +2915,14 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) } case TYPE_PTR: { #if defined( PLATFORM_64BITS ) // We need to ensure we only read 32 bits out of the stream because 32 bit clients only wrote // 32 bits of data there. The actual pointer is irrelevant, all that we really care about here // contractually is whether the pointer is zero or not zero. dat->m_pValue = ( void* )( intp )buffer.GetInt(); #else dat->m_pValue = buffer.GetPtr(); #endif break; } @@ -2681,9 +2946,243 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) return buffer.IsValid(); } // writes KeyValue as binary data to buffer // removes empty keys bool KeyValues::WriteAsBinaryFiltered( CUtlBuffer &buffer ) { if ( buffer.IsText() ) // must be a binary buffer return false; if ( !buffer.IsValid() ) // must be valid, no overflows etc return false; // Write header buffer.PutString( GetName() ); // loop through all our keys writing them to buffer for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer ) { if ( dat->m_pSub ) { buffer.PutUnsignedChar( TYPE_NONE ); dat->WriteAsBinaryFiltered( buffer ); } else { if ( dat->m_iDataType == TYPE_NONE ) { continue; // None with no subs will be filtered } // write type and name buffer.PutUnsignedChar( dat->m_iDataType ); buffer.PutString( dat->GetName() ); // write type switch (dat->m_iDataType) { case TYPE_STRING: if (dat->m_sValue && *(dat->m_sValue)) { buffer.PutString( dat->m_sValue ); } else { buffer.PutString( "" ); } break; case TYPE_WSTRING: { int nLength = dat->m_wsValue ? Q_wcslen( dat->m_wsValue ) : 0; buffer.PutShort( nLength ); for( int k = 0; k < nLength; ++ k ) { buffer.PutShort( ( unsigned short ) dat->m_wsValue[k] ); } break; } case TYPE_INT: { buffer.PutInt( dat->m_iValue ); break; } case TYPE_UINT64: { buffer.PutInt64( *((int64 *)dat->m_sValue) ); break; } case TYPE_FLOAT: { buffer.PutFloat( dat->m_flValue ); break; } case TYPE_COLOR: { buffer.PutUnsignedChar( dat->m_Color[0] ); buffer.PutUnsignedChar( dat->m_Color[1] ); buffer.PutUnsignedChar( dat->m_Color[2] ); buffer.PutUnsignedChar( dat->m_Color[3] ); break; } case TYPE_PTR: { #if defined( PLATFORM_64BITS ) // We only put an int here, because 32-bit clients do not expect 64 bits. It'll cause them to read the wrong // amount of data and then crash. Longer term, we may bump this up in size on all platforms, but short term // we don't really have much of a choice other than sticking in something that appears to not be NULL. if ( dat->m_pValue != 0 && ( ( (int)(intp)dat->m_pValue ) == 0 ) ) buffer.PutInt( 31337 ); // Put not 0, but not a valid number. Yuck. else buffer.PutInt( ( (int)(intp)dat->m_pValue ) ); #else buffer.PutPtr( dat->m_pValue ); #endif break; } default: break; } } } // write tail, marks end of peers buffer.PutUnsignedChar( TYPE_NUMTYPES ); return buffer.IsValid(); } // read KeyValues from binary buffer, returns true if parsing was successful bool KeyValues::ReadAsBinaryFiltered( CUtlBuffer &buffer, int nStackDepth ) { if ( buffer.IsText() ) // must be a binary buffer return false; if ( !buffer.IsValid() ) // must be valid, no overflows etc return false; RemoveEverything(); // remove current content Init(); // reset if ( nStackDepth > 100 ) { AssertMsgOnce( false, "KeyValues::ReadAsBinaryFiltered() stack depth > 100\n" ); return false; } char name[KEYVALUES_TOKEN_SIZE]; // Read header buffer.GetString( name, KEYVALUES_TOKEN_SIZE-1 ); name[KEYVALUES_TOKEN_SIZE-1] = 0; SetName( name ); // loop through all our peers while ( true ) { types_t type = (types_t)buffer.GetUnsignedChar(); if ( type == TYPE_NUMTYPES ) break; if ( type == TYPE_NONE ) { KeyValues *newKey = CreateKey(""); newKey->ReadAsBinaryFiltered( buffer, nStackDepth + 1 ); } else { buffer.GetString( name, KEYVALUES_TOKEN_SIZE-1 ); name[KEYVALUES_TOKEN_SIZE-1] = 0; switch ( type ) { case TYPE_STRING: { char token[KEYVALUES_TOKEN_SIZE]; buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); token[KEYVALUES_TOKEN_SIZE-1] = 0; SetString( name, token ); } break; case TYPE_WSTRING: { int nLength = buffer.GetShort(); wchar_t *wsValue = new wchar_t[nLength + 1]; for( int k = 0; k < nLength; ++ k ) { wsValue[k] = buffer.GetShort(); } wsValue[ nLength ] = 0; SetWString( name, wsValue ); delete[] wsValue; } break; case TYPE_INT: { int value = buffer.GetInt(); SetInt( name, value ); } break; case TYPE_UINT64: { uint64 value = buffer.GetInt64(); SetUint64( name, value ); } break; case TYPE_FLOAT: { float value = buffer.GetFloat(); SetFloat( name, value ); } break; case TYPE_COLOR: { unsigned char c0 = buffer.GetUnsignedChar(); unsigned char c1 = buffer.GetUnsignedChar(); unsigned char c2 = buffer.GetUnsignedChar(); unsigned char c3 = buffer.GetUnsignedChar(); SetColor( name, Color( c0, c1, c2, c3 ) ); } break; case TYPE_PTR: { #if defined( PLATFORM_64BITS ) // We need to ensure we only read 32 bits out of the stream because 32 bit clients only wrote // 32 bits of data there. The actual pointer is irrelevant, all that we really care about here // contractually is whether the pointer is zero or not zero. void* value = ( void* )( intp )buffer.GetInt(); #else void* value = buffer.GetPtr(); #endif SetPtr( name, value ); } break; default: break; } } if ( !buffer.IsValid() ) // error occured return false; } return buffer.IsValid(); } //----------------------------------------------------------------------------- // Purpose: memory allocator //----------------------------------------------------------------------------- bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *pFileSystem, unsigned int poolKey, GetSymbolProc_t pfnEvaluateSymbolProc ) { @@ -2699,11 +3198,11 @@ bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *p if ( !buffer.IsValid() ) // must be valid, no overflows etc return false; char token[KEYVALUES_TOKEN_SIZE]; KeyValues *dat = this; types_t type = (types_t)buffer.GetUnsignedChar(); // loop through all our peers while ( true ) { @@ -2716,7 +3215,7 @@ bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *p if ( !((IFileSystem*)pFileSystem)->GetStringFromKVPool( poolKey, stringKey, token, sizeof( token ) ) ) return false; dat->SetName( token ); switch ( type ) { case TYPE_NONE: @@ -2729,7 +3228,7 @@ bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *p case TYPE_STRING: { stringKey = buffer.GetUnsignedInt(); if ( !((IFileSystem*)pFileSystem)->GetStringFromKVPool( poolKey, stringKey, token, sizeof( token ) ) ) return false; int len = Q_strlen( token ); @@ -2780,7 +3279,14 @@ bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *p case TYPE_PTR: { #if defined( PLATFORM_64BITS ) // We need to ensure we only read 32 bits out of the stream because 32 bit clients only wrote // 32 bits of data there. The actual pointer is irrelevant, all that we really care about here // contractually is whether the pointer is zero or not zero. dat->m_pValue = ( void* )( intp )buffer.GetInt(); #else dat->m_pValue = buffer.GetPtr(); #endif break; } @@ -2832,9 +3338,6 @@ bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *p #include "tier0/memdbgoff.h" void *KeyValues::operator new( size_t iAllocSize ) { MEM_ALLOC_CREDIT(); @@ -2849,21 +3352,20 @@ void *KeyValues::operator new( size_t iAllocSize, int nBlockUse, const char *pFi return p; } void KeyValues::operator delete( void *pMem ) { KeyValuesSystem()->FreeKeyValuesMemory( (KeyValues *)pMem ); } void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine ) { KeyValuesSystem()->FreeKeyValuesMemory( (KeyValues *)pMem ); } #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest ) { uint8 *dest = ( uint8 * ) pDest; @@ -2873,51 +3375,51 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl KeyValues * find_it = FindKey( pUnpackTable->m_pKeyName ); switch( pUnpackTable->m_eDataType ) { case UNPACK_TYPE_FLOAT: { float default_value = ( pUnpackTable->m_pKeyDefault ) ? atof( pUnpackTable->m_pKeyDefault ) : 0.0; *( ( float * ) dest_field ) = GetFloat( pUnpackTable->m_pKeyName, default_value ); break; } break; case UNPACK_TYPE_VECTOR: { Vector *dest_v = ( Vector * ) dest_field; char const *src_string = GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); if ( ( ! src_string ) || ( sscanf(src_string,"%f %f %f", & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z )) != 3 )) dest_v->Init( 0, 0, 0 ); } break; case UNPACK_TYPE_FOUR_FLOATS: { float *dest_f = ( float * ) dest_field; char const *src_string = GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); if ( ( ! src_string ) || ( sscanf(src_string,"%f %f %f %f", dest_f, dest_f + 1, dest_f + 2, dest_f + 3 )) != 4 ) memset( dest_f, 0, 4 * sizeof( float ) ); } break; case UNPACK_TYPE_TWO_FLOATS: { float *dest_f = ( float * ) dest_field; char const *src_string = GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); if ( ( ! src_string ) || ( sscanf(src_string,"%f %f", dest_f, dest_f + 1 )) != 2 ) memset( dest_f, 0, 2 * sizeof( float ) ); } break; case UNPACK_TYPE_STRING: { char *dest_s = ( char * ) dest_field; char const *pDefault = ""; @@ -2926,12 +3428,12 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl pDefault = pUnpackTable->m_pKeyDefault; } strncpy( dest_s, GetString( pUnpackTable->m_pKeyName, pDefault ), pUnpackTable->m_nFieldSize ); } break; case UNPACK_TYPE_INT: { int *dest_i = ( int * ) dest_field; int default_int = 0; @@ -2941,7 +3443,7 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl } break; case UNPACK_TYPE_VECTOR_COLOR: { Vector *dest_v = ( Vector * ) dest_field; if ( find_it ) @@ -2955,7 +3457,7 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl { if ( pUnpackTable->m_pKeyDefault ) sscanf(pUnpackTable->m_pKeyDefault,"%f %f %f", & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z )); else dest_v->Init( 0, 0, 0 ); } @@ -2993,14 +3495,14 @@ bool KeyValues::ProcessResolutionKeys( const char *pResString ) pSubKey->ProcessResolutionKeys( pResString ); // check to see if our substring is present if ( V_stristr( pSubKey->GetName(), pResString ) != NULL ) { char normalKeyName[128]; V_strncpy( normalKeyName, pSubKey->GetName(), sizeof( normalKeyName ) ); // substring must match exactly, otherwise keys like "_lodef" and "_lodef_wide" would clash. char *pString = V_stristr( normalKeyName, pResString ); if ( pString && !V_stricmp( pString, pResString ) ) { *pString = '\0'; @@ -3010,7 +3512,7 @@ bool KeyValues::ProcessResolutionKeys( const char *pResString ) { // remove the key RemoveSubKey( pKey ); delete pKey; } // rename the marked key @@ -3062,7 +3564,7 @@ void KeyValues::MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp /* = MERGE if ( KeyValues *valStorage = this->FindKey( szName, false ) ) { this->RemoveSubKey( valStorage ); delete valStorage; } this->AddSubKey( val->MakeCopy() ); } @@ -3088,7 +3590,7 @@ void KeyValues::MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp /* = MERGE if ( KeyValues *valStorage = this->FindKey( szName, false ) ) { this->RemoveSubKey( valStorage ); delete valStorage; } else continue; @@ -3115,7 +3617,7 @@ void KeyValues::MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp /* = MERGE if ( KeyValues *valStorage = this->FindKey( szName, false ) ) { this->RemoveSubKey( valStorage ); delete valStorage; } } } @@ -3160,7 +3662,7 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, return NULL; char chName[256] = {0}; char chValue[1024] = {0}; for ( ; ; ) { @@ -3175,7 +3677,7 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, szStringVal = szVarName + 1; break; } V_strncpy( chName, szVarName, ( int )MIN( sizeof( chName ), szEnd - szVarName + 1 ) ); szVarName = chName; szStringVal = szEnd; @@ -3192,7 +3694,7 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, kv->SetString( szVarName, "" ); break; } V_strncpy( chValue, szVarValue, ( int )MIN( sizeof( chValue ), szEnd - szVarValue + 1 ) ); szVarValue = chValue; szStringVal = szEnd; @@ -3206,7 +3708,7 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, { kv->SetInt( szVarName, atoi( szInt ) ); } else if ( !V_stricmp( szVarValue, "#empty#" ) ) { kv->SetString( szVarName, "" ); } @@ -3236,7 +3738,66 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, return kv; } //----------------------------------------------------------------------------- // Purpose: comparison function for keyvalues //----------------------------------------------------------------------------- bool KeyValues::IsEqual( KeyValues *pRHS ) { if ( !pRHS ) return false; // check our key if ( m_iDataType != pRHS->m_iDataType ) return false; switch ( m_iDataType ) { case TYPE_STRING: return V_strcmp( GetString(), pRHS->GetString() ) == 0; case TYPE_WSTRING: return V_wcscmp( GetWString(), pRHS->GetWString() ) == 0; case TYPE_FLOAT: return m_flValue == pRHS->m_flValue; case TYPE_UINT64: return GetUint64() == pRHS->GetUint64(); case TYPE_NONE: { // walk through the subkeys - does it in order right now KeyValues *pkv = GetFirstSubKey(); KeyValues *pkvRHS = pRHS->GetFirstSubKey(); bool bRet = false; while ( 1 ) { // ended at the same time, good if ( !pkv && !pkvRHS ) { bRet = true; break; } // uneven number of keys, failure if ( !pkv || !pkvRHS ) break; // recursively compare if ( !pkv->IsEqual( pkvRHS ) ) break; pkv = pkv->GetNextKey(); pkvRHS = pkvRHS->GetNextKey(); } return bRet; } case TYPE_INT: case TYPE_PTR: return m_iValue == pRHS->m_iValue; default: Assert( false ); } return true; } // // KeyValues dumping implementation @@ -3245,7 +3806,7 @@ bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */ ) { if ( !pDump->KvBeginKey( this, nIndentLevel ) ) return false; // Dump values for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() ) { @@ -3311,17 +3872,17 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { int n = val->GetInt(); char *chBuffer = ( char * ) stackalloc( 128 ); V_snprintf( chBuffer, 128, "int( %d = 0x%X )", n, n ); if ( !KvWriteText( chBuffer ) ) return false; } break; case KeyValues::TYPE_FLOAT: { float fl = val->GetFloat(); char *chBuffer = ( char * ) stackalloc( 128 ); V_snprintf( chBuffer, 128, "float( %f )", fl ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3331,7 +3892,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { void *ptr = val->GetPtr(); char *chBuffer = ( char * ) stackalloc( 128 ); V_snprintf( chBuffer, 128, "ptr( 0x%p )", ptr ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3340,10 +3901,10 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel case KeyValues::TYPE_WSTRING: { wchar_t const *wsz = val->GetWString(); int nLen = V_wcslen( wsz ); int numBytes = nLen*2 + 64; char *chBuffer = ( char * ) stackalloc( numBytes ); V_snprintf( chBuffer, numBytes, "%ls [wstring, len = %d]", wsz, nLen ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3353,7 +3914,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { uint64 n = val->GetUint64(); char *chBuffer = ( char * ) stackalloc( 128 ); V_snprintf( chBuffer, 128, "u64( %lld = 0x%llX )", n, n ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3365,7 +3926,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { int n = val->GetDataType(); char *chBuffer = ( char * ) stackalloc( 128 ); V_snprintf( chBuffer, 128, "??kvtype[%d]", n ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3423,4 +3984,3 @@ bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText ) return true; } 
- 
        xPaw revised this gist Jan 19, 2021 . 1 changed file with 954 additions and 737 deletions.There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,4 @@ //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -7,46 +7,56 @@ //=============================================================================// #if defined( _WIN32 ) && !defined( _X360 ) #include <windows.h> // for widechartomultibyte and multibytetowidechar #elif defined(POSIX) #include <wchar.h> // wcslen() #define _alloca alloca #define _wtoi(arg) wcstol(arg, NULL, 10) #define _wtoi64(arg) wcstoll(arg, NULL, 10) #endif #include <keyvalues.h> #include "filesystem.h" #include <vstdlib/ikeyvaluessystem.h> #include <color.h> #include <stdlib.h> #include <ctype.h> #include "tier1/convar.h" #include "tier0/dbg.h" #include "tier0/mem.h" #include "utlvector.h" #include "utlbuffer.h" #include "utlhash.h" #include <vstdlib/vstrtools.h> // memdbgon must be the last include file in a .cpp file!!! #include <tier0/memdbgon.h> //////// VPROF? ////////////////// // For an example of how to mark up this file with VPROF nodes, see // changelist 702984. However, be aware that calls to FindKey and Init // may occur outside of Vprof's usual hierarchy, which can cause strange // duplicate KeyValues::FindKey nodes at the root level and other // confusing effects. ////////////////////////////////// static char * s_LastFileLoadingFrom = "unknown"; // just needed for error messages // Statics for the growable string table int (*KeyValues::s_pfGetSymbolForString)( const char *name, bool bCreate ) = &KeyValues::GetSymbolForStringClassic; const char *(*KeyValues::s_pfGetStringForSymbol)( int symbol ) = &KeyValues::GetStringForSymbolClassic; CKeyValuesGrowableStringTable *KeyValues::s_pGrowableStringTable = NULL; #define KEYVALUES_TOKEN_SIZE 1024 static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE]; #define INTERNALWRITE( pData, len ) InternalWrite( filesystem, f, pBuf, pData, len ) #define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( uint16 )x2) << 8 ) | (uint8)(x1)) #define SPLIT_3_BYTES_INTO_1_AND_2( x1, x2, x3 ) do { x1 = (uint8)(x3); x2 = (uint16)( (x3) >> 8 ); } while( 0 ) CExpressionEvaluator g_ExpressionEvaluator; // a simple class to keep track of a stack of valid parsed symbols const int MAX_ERROR_STACK = 64; @@ -70,7 +80,7 @@ class CKeyValuesErrorStack m_errorStack[m_errorIndex] = symName; } m_errorIndex++; m_maxErrorIndex = MAX( m_maxErrorIndex, (m_errorIndex-1) ); return m_errorIndex-1; } @@ -84,37 +94,29 @@ class CKeyValuesErrorStack // Allows you to keep the same stack level, but change the name as you parse peers void Reset( int stackLevel, int symName ) { Assert( stackLevel >= 0 && stackLevel < m_errorIndex ); m_errorStack[stackLevel] = symName; } // Hit an error, report it and the parsing stack for context void ReportError( const char *pError ) { 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 < m_errorIndex ) { Warning( "%s, ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) ); } else { Warning( "(*%s*), ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) ); } } } Warning( "\n" ); } private: @@ -129,27 +131,18 @@ class CKeyValuesErrorStack class CKeyErrorContext { public: ~CKeyErrorContext() { g_KeyValuesErrorStack.Pop(); } explicit CKeyErrorContext( int symName ) { Init( symName ); } void Reset( int symName ) { g_KeyValuesErrorStack.Reset( m_stackLevel, symName ); } private: void Init( int symName ) { @@ -231,12 +224,8 @@ class CKeyValuesGrowableStringTable public: // Constructor CKeyValuesGrowableStringTable() : m_vecStrings( 0, 512 * 1024 ), m_hashLookup( 2048, 0, 0, m_Functor, m_Functor ) { m_vecStrings.AddToTail( '\0' ); } @@ -338,12 +327,10 @@ void KeyValues::SetUseGrowableStringTable( bool bUseGrowableTable ) { s_pfGetStringForSymbol = &(KeyValues::GetStringForSymbolClassic); s_pfGetSymbolForString = &(KeyValues::GetSymbolForStringClassic); } } //----------------------------------------------------------------------------- // Purpose: Bodys of the function pointers used for interacting with the key // name string table @@ -448,7 +435,9 @@ KeyValues::KeyValues( const char *setName, const char *firstKey, int firstValue, //----------------------------------------------------------------------------- void KeyValues::Init() { m_iKeyName = 0; m_iKeyNameCaseSensitive1 = 0; m_iKeyNameCaseSensitive2 = 0; m_iDataType = TYPE_NONE; m_pSub = NULL; @@ -459,11 +448,7 @@ void KeyValues::Init() m_wsValue = NULL; m_pValue = NULL; m_bHasEscapeSequences = 0; } //----------------------------------------------------------------------------- @@ -508,9 +493,9 @@ void KeyValues::RemoveEverything() // Input : *f - //----------------------------------------------------------------------------- void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel ) { RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel ); } //----------------------------------------------------------------------------- @@ -528,9 +513,23 @@ void KeyValues::ChainKeyValue( KeyValues* pChain ) //----------------------------------------------------------------------------- const char *KeyValues::GetName( void ) const { return this ? KeyValuesSystem()->GetStringForSymbol( MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) ) : ""; } //----------------------------------------------------------------------------- // Purpose: Get the symbol name of the current key section //----------------------------------------------------------------------------- int KeyValues::GetNameSymbol() const { return this ? m_iKeyName : INVALID_KEY_SYMBOL; } int KeyValues::GetNameSymbolCaseSensitive() const { return this ? MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) : INVALID_KEY_SYMBOL; } //----------------------------------------------------------------------------- // Purpose: Read a single token from buffer (0 terminated) //----------------------------------------------------------------------------- @@ -581,7 +580,7 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon bool bReportedError = false; bool bConditionalStart = false; int nCount = 0; while ( c = (const char*)buf.PeekGet( sizeof(char), 0 ) ) { // end of file if ( *c == 0 ) @@ -592,15 +591,19 @@ const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasCon break; if ( *c == '[' ) { bConditionalStart = true; } if ( *c == ']' && bConditionalStart ) { bConditionalStart = false; wasConditional = true; } // conditionals need to get tokenized as delimited by [] // othwerwise break on whitespace if ( V_isspace(*c) && !bConditionalStart ) break; if (nCount < (KEYVALUES_TOKEN_SIZE-1) ) @@ -631,77 +634,17 @@ void KeyValues::UsesEscapeSequences(bool state) } //----------------------------------------------------------------------------- // Purpose: Load keyValues from disk //----------------------------------------------------------------------------- bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, GetSymbolProc_t pfnEvaluateSymbolProc ) { #if defined( _WIN32 ) Assert( IsGameConsole() || ( _heapchk() == _HEAPOK ) ); #endif FileHandle_t f = filesystem->Open(resourceName, "rb", pathID); if ( !f ) return false; s_LastFileLoadingFrom = (char*)resourceName; @@ -721,44 +664,45 @@ bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceN { buffer[fileSize] = 0; // null terminate file as EOF buffer[fileSize+1] = 0; // double NULL terminating in case this is a unicode file CUtlBuffer utlBuffer; if ( IsGameConsole() && (unsigned int)((unsigned char *)buffer)[0] == KV_BINARY_POOLED_FORMAT ) { // kv contents are compiled binary utlBuffer.SetExternalBuffer( buffer, bufSize, fileSize, CUtlBuffer::READ_ONLY ); } else { // kv contents are text int nLen = Q_strlen( buffer ); utlBuffer.SetExternalBuffer( buffer, bufSize, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); } bRetOK = LoadFromBuffer( resourceName, utlBuffer, filesystem, pathID, pfnEvaluateSymbolProc ); } ((IFileSystem *)filesystem)->FreeOptimalReadBuffer( buffer ); return bRetOK; } //----------------------------------------------------------------------------- // Purpose: Save the keyvalues to disk // Creates the path to the file if it doesn't exist //----------------------------------------------------------------------------- bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID ) { // create a write file FileHandle_t f = filesystem->Open(resourceName, "wb", pathID); if ( f == FILESYSTEM_INVALID_HANDLE ) { DevMsg( "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n", resourceName?resourceName:"NULL", pathID?pathID:"NULL" ); return false; } RecursiveSaveToFile(filesystem, f, NULL, 0); filesystem->Close(f); return true; @@ -783,7 +727,7 @@ void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t // handle double quote chars within the string // the worst possible case is that the whole string is quotes int len = Q_strlen(pszString); char *convertedString = (char *) alloca ((len + 1) * sizeof(char) * 2); int j=0; for (int i=0; i <= len; i++) { @@ -801,7 +745,7 @@ void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t j++; } INTERNALWRITE(convertedString, strlen(convertedString)); } @@ -816,13 +760,14 @@ void KeyValues::InternalWrite( IBaseFileSystem *filesystem, FileHandle_t f, CUtl { pBuf->Put( pData, len ); } } //----------------------------------------------------------------------------- // Purpose: Save keyvalues from disk, if subkey values are detected, calls // itself to save those //----------------------------------------------------------------------------- void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel ) { // write header WriteIndents( filesystem, f, pBuf, indentLevel ); @@ -833,148 +778,123 @@ void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f INTERNALWRITE("{\n", 2); // loop through all our keys writing them to disk for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer ) { if ( dat->m_pSub ) { dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1 ); } else { // only write non-empty keys switch (dat->m_iDataType) { case TYPE_STRING: { if (dat->m_sValue && *(dat->m_sValue)) { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); WriteConvertedString(filesystem, f, pBuf, dat->GetName()); INTERNALWRITE("\"\t\t\"", 4); WriteConvertedString(filesystem, f, pBuf, dat->m_sValue); INTERNALWRITE("\"\n", 2); } break; } case TYPE_WSTRING: { if ( dat->m_wsValue ) { static char buf[KEYVALUES_TOKEN_SIZE]; // make sure we have enough space int result = V_UnicodeToUTF8( dat->m_wsValue, buf, KEYVALUES_TOKEN_SIZE); if (result) { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); WriteConvertedString(filesystem, f, pBuf, buf); INTERNALWRITE("\"\n", 2); } } break; } case TYPE_INT: { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); char buf[32]; Q_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue); INTERNALWRITE(buf, Q_strlen(buf)); INTERNALWRITE("\"\n", 2); break; } case TYPE_UINT64: { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); char buf[32]; // write "0x" + 16 char 0-padded hex encoded 64 bit value Q_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) ); INTERNALWRITE(buf, Q_strlen(buf)); INTERNALWRITE("\"\n", 2); break; } case TYPE_FLOAT: { WriteIndents(filesystem, f, pBuf, indentLevel + 1); INTERNALWRITE("\"", 1); INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName())); INTERNALWRITE("\"\t\t\"", 4); char buf[48]; Q_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue); INTERNALWRITE(buf, Q_strlen(buf)); INTERNALWRITE("\"\n", 2); break; } case TYPE_COLOR: DevMsg( "KeyValues::RecursiveSaveToFile: TODO, missing code for TYPE_COLOR.\n" ); break; default: break; } } } // write tail WriteIndents(filesystem, f, pBuf, indentLevel); INTERNALWRITE("}\n", 2); } //----------------------------------------------------------------------------- // Purpose: looks up a key by symbol name //----------------------------------------------------------------------------- KeyValues *KeyValues::FindKey(int keySymbol) const { for (KeyValues *dat = this ? m_pSub : NULL; dat != NULL; dat = dat->m_pPeer) { if ( dat->m_iKeyName == (uint32) keySymbol ) return dat; } @@ -988,6 +908,13 @@ KeyValues *KeyValues::FindKey(int keySymbol) const //----------------------------------------------------------------------------- KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) { // Validate NULL == this early out if ( !this ) { Assert( !bCreate ); return NULL; } // return the current key if a NULL subkey is asked for if (!keyName || !keyName[0]) return this; @@ -1006,9 +933,12 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) searchStr = szBuf; } // lookup the symbol for the search string, // we do not need the case-sensitive symbol at this time // because if the key is found, then it will be found by case-insensitive lookup // if the key is not found and needs to be created we will pass the actual searchStr // and have the new KeyValues constructor get/create the case-sensitive symbol HKeySymbol iSearchStr = KeyValuesSystem()->GetSymbolForString( searchStr, bCreate ); if ( iSearchStr == INVALID_KEY_SYMBOL ) { // not found, couldn't possibly be in key value list @@ -1023,7 +953,7 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) lastItem = dat; // record the last item looked at (for if we need to append to the end of the list) // symbol compare if ( dat->m_iKeyName == ( uint32 ) iSearchStr ) { break; } @@ -1043,9 +973,6 @@ KeyValues *KeyValues::FindKey(const char *keyName, bool bCreate) dat = new KeyValues( searchStr ); // Assert(dat != NULL); // insert new key at end of list if (lastItem) { @@ -1087,7 +1014,6 @@ KeyValues *KeyValues::CreateNewKey() int newID = 1; // search for any key with higher values for (KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer) { // case-insensitive string compare @@ -1096,14 +1022,12 @@ KeyValues *KeyValues::CreateNewKey() { newID = val + 1; } } char buf[12]; Q_snprintf( buf, sizeof(buf), "%d", newID ); return CreateKey( buf ); } @@ -1112,65 +1036,24 @@ KeyValues *KeyValues::CreateNewKey() //----------------------------------------------------------------------------- KeyValues* KeyValues::CreateKey( const char *keyName ) { // key wasn't found so just create a new one KeyValues* dat = new KeyValues( keyName ); dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does // add into subkey list AddSubKey( dat ); return dat; } //----------------------------------------------------------------------------- // Adds a subkey. Make sure the subkey isn't a child of some other keyvalues //----------------------------------------------------------------------------- void KeyValues::AddSubKey( KeyValues *pSubkey ) { // Make sure the subkey isn't a child of some other keyvalues Assert( pSubkey->m_pPeer == NULL ); // add into subkey list @@ -1224,23 +1107,135 @@ void KeyValues::RemoveSubKey(KeyValues *subKey) subKey->m_pPeer = NULL; } void KeyValues::InsertSubKey( int nIndex, KeyValues *pSubKey ) { // Sub key must be valid and not part of another chain Assert( pSubKey && pSubKey->m_pPeer == NULL ); if ( nIndex == 0 ) { pSubKey->m_pPeer = m_pSub; m_pSub = pSubKey; return; } else { int nCurrentIndex = 0; for ( KeyValues *pIter = GetFirstSubKey(); pIter != NULL; pIter = pIter->GetNextKey() ) { ++ nCurrentIndex; if ( nCurrentIndex == nIndex) { pSubKey->m_pPeer = pIter->m_pPeer; pIter->m_pPeer = pSubKey; return; } } // Index is out of range if we get here Assert( 0 ); return; } } bool KeyValues::ContainsSubKey( KeyValues *pSubKey ) { for ( KeyValues *pIter = GetFirstSubKey(); pIter != NULL; pIter = pIter->GetNextKey() ) { if ( pSubKey == pIter ) { return true; } } return false; } void KeyValues::SwapSubKey( KeyValues *pExistingSubkey, KeyValues *pNewSubKey ) { Assert( pExistingSubkey != NULL && pNewSubKey != NULL ); // Make sure the new sub key isn't a child of some other keyvalues Assert( pNewSubKey->m_pPeer == NULL ); // Check the list pointer if ( m_pSub == pExistingSubkey ) { pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; pExistingSubkey->m_pPeer = NULL; m_pSub = pNewSubKey; } else { // Look through the list KeyValues *kv = m_pSub; while ( kv->m_pPeer ) { if ( kv->m_pPeer == pExistingSubkey ) { pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; pExistingSubkey->m_pPeer = NULL; kv->m_pPeer = pNewSubKey; break; } kv = kv->m_pPeer; } // Existing sub key should always be found, otherwise it's a bug in the calling code. Assert( kv->m_pPeer != NULL ); } } void KeyValues::ElideSubKey( KeyValues *pSubKey ) { // This pointer's "next" pointer needs to be fixed up when we elide the key KeyValues **ppPointerToFix = &m_pSub; for ( KeyValues *pKeyIter = m_pSub; pKeyIter != NULL; ppPointerToFix = &pKeyIter->m_pPeer, pKeyIter = pKeyIter->GetNextKey() ) { if ( pKeyIter == pSubKey ) { if ( pSubKey->m_pSub == NULL ) { // No children, simply remove the key *ppPointerToFix = pSubKey->m_pPeer; pSubKey->deleteThis(); } else { *ppPointerToFix = pSubKey->m_pSub; // Attach the remainder of this chain to the last child of pSubKey KeyValues *pChildIter = pSubKey->m_pSub; while ( pChildIter->m_pPeer != NULL ) { pChildIter = pChildIter->m_pPeer; } // Now points to the last child of pSubKey pChildIter->m_pPeer = pSubKey->m_pPeer; // Detach the node to be elided pSubKey->m_pSub = NULL; pSubKey->m_pPeer = NULL; pSubKey->deleteThis(); } return; } } // Key not found; that's caller error. Assert( 0 ); } //----------------------------------------------------------------------------- // Purpose: Return the first subkey in the list //----------------------------------------------------------------------------- KeyValues *KeyValues::GetFirstSubKey() { return this ? m_pSub : NULL; } //----------------------------------------------------------------------------- // Purpose: Return the next subkey //----------------------------------------------------------------------------- KeyValues *KeyValues::GetNextKey() { return this ? m_pPeer : NULL; } //----------------------------------------------------------------------------- @@ -1254,7 +1249,7 @@ void KeyValues::SetNextKey( KeyValues *pDat ) KeyValues* KeyValues::GetFirstTrueSubKey() { KeyValues *pRet = this ? m_pSub : NULL; while ( pRet && pRet->m_iDataType != TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1263,7 +1258,7 @@ KeyValues* KeyValues::GetFirstTrueSubKey() KeyValues* KeyValues::GetNextTrueSubKey() { KeyValues *pRet = this ? m_pPeer : NULL; while ( pRet && pRet->m_iDataType != TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1272,7 +1267,7 @@ KeyValues* KeyValues::GetNextTrueSubKey() KeyValues* KeyValues::GetFirstValue() { KeyValues *pRet = this ? m_pSub : NULL; while ( pRet && pRet->m_iDataType == TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1281,7 +1276,7 @@ KeyValues* KeyValues::GetFirstValue() KeyValues* KeyValues::GetNextValue() { KeyValues *pRet = this ? m_pPeer : NULL; while ( pRet && pRet->m_iDataType == TYPE_NONE ) pRet = pRet->m_pPeer; @@ -1331,9 +1326,17 @@ uint64 KeyValues::GetUint64( const char *keyName, uint64 defaultValue ) switch ( dat->m_iDataType ) { case TYPE_STRING: { uint64 uiResult = 0ull; sscanf( dat->m_sValue, "%lld", &uiResult ); return uiResult; } case TYPE_WSTRING: { uint64 uiResult = 0ull; swscanf( dat->m_wsValue, L"%lld", &uiResult ); return uiResult; } case TYPE_FLOAT: return (int)dat->m_flValue; case TYPE_UINT64: @@ -1390,8 +1393,7 @@ float KeyValues::GetFloat( const char *keyName, float defaultValue ) #ifdef WIN32 return (float) _wtof(dat->m_wsValue); // no wtof #else return (float) wcstof( dat->m_wsValue, (wchar_t **)NULL ); #endif case TYPE_FLOAT: return dat->m_flValue; @@ -1424,11 +1426,8 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue Q_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue ); SetString( keyName, buf ); break; case TYPE_INT: case TYPE_PTR: Q_snprintf( buf, sizeof( buf ), "%d", dat->m_iValue ); SetString( keyName, buf ); break; @@ -1441,7 +1440,7 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue { // convert the string to char *, set it for future use, and return it char wideBuf[512]; int result = V_UnicodeToUTF8(dat->m_wsValue, wideBuf, 512); if ( result ) { // note: this will copy wideBuf @@ -1477,11 +1476,8 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul swprintf(wbuf, Q_ARRAYSIZE(wbuf), L"%f", dat->m_flValue); SetWString( keyName, wbuf); break; case TYPE_INT: case TYPE_PTR: swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%d", dat->m_iValue ); SetWString( keyName, wbuf ); break; @@ -1498,7 +1494,7 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul { int bufSize = Q_strlen(dat->m_sValue) + 1; wchar_t *pWBuf = new wchar_t[ bufSize ]; int result = V_UTF8ToUnicode(dat->m_sValue, pWBuf, bufSize * sizeof( wchar_t ) ); if ( result >= 0 ) // may be a zero length string { SetWString( keyName, pWBuf); @@ -1520,31 +1516,13 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul return defaultValue; } //----------------------------------------------------------------------------- // Purpose: Gets a color //----------------------------------------------------------------------------- Color KeyValues::GetColor( const char *keyName , const Color& defaultColor ) { Color color = defaultColor; KeyValues *dat = FindKey( keyName , false ); if ( dat ) { if ( dat->m_iDataType == TYPE_COLOR ) @@ -1556,16 +1534,16 @@ Color KeyValues::GetColor( const char *keyName ) } else if ( dat->m_iDataType == TYPE_FLOAT ) { color[0] = (unsigned char)dat->m_flValue; } else if ( dat->m_iDataType == TYPE_INT ) { color[0] = (unsigned char)dat->m_iValue; } else if ( dat->m_iDataType == TYPE_STRING ) { // parse the colors out of the string float a, b, c, d; sscanf(dat->m_sValue, "%f %f %f %f", &a, &b, &c, &d); color[0] = (unsigned char)a; color[1] = (unsigned char)b; @@ -1620,33 +1598,9 @@ void KeyValues::SetStringValue( char const *strValue ) //----------------------------------------------------------------------------- void KeyValues::SetString( const char *keyName, const char *value ) { if ( KeyValues *dat = FindKey( keyName, true ) ) { dat->SetStringValue( value ); } } @@ -1671,7 +1625,7 @@ void KeyValues::SetWString( const char *keyName, const wchar_t *value ) } // allocate memory for the new value and copy it in int len = wcslen( value ); dat->m_wsValue = new wchar_t[len + 1]; Q_memcpy( dat->m_wsValue, value, (len+1) * sizeof(wchar_t) ); @@ -1730,7 +1684,11 @@ void KeyValues::SetFloat( const char *keyName, float value ) void KeyValues::SetName( const char * setName ) { HKeySymbol hCaseSensitiveKeyName = INVALID_KEY_SYMBOL, hCaseInsensitiveKeyName = INVALID_KEY_SYMBOL; hCaseSensitiveKeyName = KeyValuesSystem()->GetSymbolForStringCaseSensitive( hCaseInsensitiveKeyName, setName ); m_iKeyName = hCaseInsensitiveKeyName; SPLIT_3_BYTES_INTO_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2, hCaseSensitiveKeyName ); } //----------------------------------------------------------------------------- @@ -1747,138 +1705,117 @@ void KeyValues::SetPtr( const char *keyName, void *value ) } } void KeyValues::RecursiveCopyKeyValues( KeyValues& src ) { // garymcthack - need to check this code for possible buffer overruns. m_iKeyName = src.m_iKeyName; m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1; m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2; if( !src.m_pSub ) { m_iDataType = src.m_iDataType; char buf[256]; switch( src.m_iDataType ) { 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) ); } 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; } } #if 0 KeyValues *pDst = this; for ( KeyValues *pSrc = src.m_pSub; pSrc; pSrc = pSrc->m_pPeer ) { if ( pSrc->m_pSub ) { pDst->m_pSub = new KeyValues( pSrc->m_pSub->getName() ); pDst->m_pSub->RecursiveCopyKeyValues( *pSrc->m_pSub ); } else { // copy non-empty keys if ( pSrc->m_sValue && *(pSrc->m_sValue) ) { pDst->m_pPeer = new KeyValues( } } } #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 ); } } KeyValues& KeyValues::operator=( KeyValues& src ) { RemoveEverything(); Init(); // reset all values RecursiveCopyKeyValues( src ); return *this; } @@ -1918,9 +1855,6 @@ KeyValues *KeyValues::MakeCopy( void ) const { KeyValues *newKeyValue = new KeyValues(GetName()); // copy data newKeyValue->m_iDataType = m_iDataType; switch ( m_iDataType ) @@ -1940,7 +1874,7 @@ KeyValues *KeyValues::MakeCopy( void ) const { if ( m_wsValue ) { int len = wcslen( m_wsValue ); newKeyValue->m_wsValue = new wchar_t[len+1]; Q_memcpy( newKeyValue->m_wsValue, m_wsValue, (len+1)*sizeof(wchar_t)); } @@ -1977,25 +1911,6 @@ KeyValues *KeyValues::MakeCopy( void ) const return newKeyValue; } //----------------------------------------------------------------------------- // Purpose: Check if a keyName has no value assigned to it. @@ -2049,13 +1964,14 @@ void KeyValues::deleteThis() void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) { // Append any included keys, too... int includeCount = includedKeys.Count(); int i; for ( i = 0; i < includeCount; i++ ) { KeyValues *kv = includedKeys[ i ]; Assert( kv ); KeyValues *insertSpot = this; while ( insertSpot->GetNextKey() ) { insertSpot = insertSpot->GetNextKey(); @@ -2066,7 +1982,7 @@ void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys ) } void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoinclude, IBaseFileSystem* pFileSystem, const char *pPathID, CUtlVector< KeyValues * >& includedKeys, GetSymbolProc_t pfnEvaluateSymbolProc ) { Assert( resourceName ); Assert( filetoinclude ); @@ -2083,8 +1999,9 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi Q_strncpy( fullpath, resourceName, sizeof( fullpath ) ); // Strip off characters back to start or first / bool done = false; int len = Q_strlen( fullpath ); while ( !done ) { if ( len <= 0 ) { @@ -2110,9 +2027,8 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi // CUtlSymbol save = s_CurrentFileSymbol; // did that had any use ??? newKV->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent if ( newKV->LoadFromFile( pFileSystem, fullpath, pPathID, pfnEvaluateSymbolProc ) ) { includedKeys.AddToTail( newKV ); } @@ -2180,48 +2096,48 @@ void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV ) } //----------------------------------------------------------------------------- // Returns whether a keyvalues conditional expression string evaluates to true or false //----------------------------------------------------------------------------- bool KeyValues::EvaluateConditional( const char *pExpressionString, GetSymbolProc_t pfnEvaluateSymbolProc ) { // evaluate the infix expression, calling the symbol proc to resolve each symbol's value bool bResult = false; bool bValid = g_ExpressionEvaluator.Evaluate( bResult, pExpressionString, pfnEvaluateSymbolProc ); if ( !bValid ) { g_KeyValuesErrorStack.ReportError( "KV Conditional Evaluation Error" ); } return bResult; } // prevent two threads from entering this at the same time and trying to share the global error reporting and parse buffers static CThreadFastMutex g_KVMutex; //----------------------------------------------------------------------------- // Read from a buffer... //----------------------------------------------------------------------------- bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc ) { AUTO_LOCK_FM( g_KVMutex ); if ( IsGameConsole() ) { // Let's not crash if the buffer is empty unsigned char *pData = buf.Size() > 0 ? (unsigned char *)buf.PeekGet() : NULL; if ( pData && (unsigned int)pData[0] == KV_BINARY_POOLED_FORMAT ) { // skip past binary marker buf.GetUnsignedChar(); // get the pool identifier, allows the fs to bind the expected string pool unsigned int poolKey = buf.GetUnsignedInt(); RemoveEverything(); Init(); return ReadAsBinaryPooledFormat( buf, pFileSystem, poolKey, pfnEvaluateSymbolProc ); } } KeyValues *pPreviousKey = NULL; KeyValues *pCurrentKey = this; CUtlVector< KeyValues * > includedKeys; @@ -2235,9 +2151,16 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase // the first thing must be a key const char *s = ReadToken( buf, wasQuoted, wasConditional ); if ( !buf.IsValid() || !s ) break; if ( !wasQuoted && *s == '\0' ) { // non quoted empty strings stop parsing // quoted empty strings are allowed to support unnnamed KV sections break; } if ( !Q_stricmp( s, "#include" ) ) // special include macro (not a key name) { s = ReadToken( buf, wasQuoted, wasConditional ); @@ -2249,7 +2172,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase } else { ParseIncludedKeys( resourceName, s, pFileSystem, pPathID, includedKeys, pfnEvaluateSymbolProc ); } continue; @@ -2265,7 +2188,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase } else { ParseIncludedKeys( resourceName, s, pFileSystem, pPathID, baseKeys, pfnEvaluateSymbolProc ); } continue; @@ -2277,7 +2200,6 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase Assert( pCurrentKey ); pCurrentKey->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // same format has parent use if ( pPreviousKey ) { @@ -2294,7 +2216,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase if ( wasConditional ) { bAccepted = EvaluateConditional( s, pfnEvaluateSymbolProc ); // Now get the '{' s = ReadToken( buf, wasQuoted, wasConditional ); @@ -2303,7 +2225,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase if ( s && *s == '{' && !wasQuoted ) { // header is valid so load the file pCurrentKey->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc ); } else { @@ -2356,16 +2278,23 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase //----------------------------------------------------------------------------- // Read from a buffer... //----------------------------------------------------------------------------- bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc ) { if ( !pBuffer ) return true; if ( IsGameConsole() && (unsigned int)((unsigned char *)pBuffer)[0] == KV_BINARY_POOLED_FORMAT ) { // bad, got a binary compiled KV file through an unexpected text path // not all paths support binary compiled kv, needs to get fixed // need to have caller supply buffer length (strlen not valid), this interface change was never plumbed Warning( "ERROR! Binary compiled KV '%s' in an unexpected handler\n", resourceName ); Assert( 0 ); return false; } int nLen = V_strlen( pBuffer ); CUtlBuffer buf( pBuffer, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); // Translate Unicode files into UTF-8 before proceeding if ( nLen > 2 && (uint8)pBuffer[0] == 0xFF && (uint8)pBuffer[1] == 0xFE ) { @@ -2374,37 +2303,19 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, I V_UnicodeToUTF8( (wchar_t*)(pBuffer+2), pUTF8Buf, nUTF8Len ); buf.AssumeMemory( pUTF8Buf, nUTF8Len, nUTF8Len, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); } return LoadFromBuffer( resourceName, buf, pFileSystem, pPathID, pfnEvaluateSymbolProc ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc ) { CKeyErrorContext errorReport( GetNameSymbolCaseSensitive() ); bool wasQuoted; bool wasConditional; // keep this out of the stack until a key is parsed CKeyErrorContext errorKey( INVALID_KEY_SYMBOL ); while ( 1 ) { bool bAccepted = true; @@ -2429,16 +2340,16 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b // Always create the key; note that this could potentially // cause some duplication, but that's what we want sometimes KeyValues *dat = CreateKey( name ); errorKey.Reset( dat->GetNameSymbolCaseSensitive() ); // get the value const char * value = ReadToken( buf, wasQuoted, wasConditional ); if ( wasConditional && value ) { bAccepted = EvaluateConditional( value, pfnEvaluateSymbolProc ); // get the real value value = ReadToken( buf, wasQuoted, wasConditional ); @@ -2461,7 +2372,7 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b // this isn't a key, it's a section errorKey.Reset( INVALID_KEY_SYMBOL ); // sub value list dat->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc ); } else { @@ -2546,44 +2457,25 @@ void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &b const char *peek = ReadToken( buf, wasQuoted, wasConditional ); if ( wasConditional ) { bAccepted = EvaluateConditional( peek, pfnEvaluateSymbolProc ); } else { buf.SeekGet( CUtlBuffer::SEEK_HEAD, prevPos ); } } if ( !bAccepted ) { this->RemoveSubKey( dat ); dat->deleteThis(); dat = NULL; } } } // writes KeyValue as binary data to buffer bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) const { if ( buffer.IsText() ) // must be a binary buffer return false; @@ -2594,7 +2486,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) // Write subkeys: // loop through all our peers for ( const KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer ) { // write type buffer.PutUnsignedChar( dat->m_iDataType ); @@ -2624,7 +2516,12 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) } case TYPE_WSTRING: { int nLength = dat->m_wsValue ? Q_wcslen( dat->m_wsValue ) : 0; buffer.PutShort( nLength ); for( int k = 0; k < nLength; ++ k ) { buffer.PutShort( ( unsigned short ) dat->m_wsValue[k] ); } break; } @@ -2636,7 +2533,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) case TYPE_UINT64: { buffer.PutInt64( *((int64 *)dat->m_sValue) ); break; } @@ -2656,6 +2553,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) case TYPE_PTR: { buffer.PutUnsignedInt( (int)dat->m_pValue ); break; } default: @@ -2670,7 +2568,7 @@ bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) } // read KeyValues from binary buffer, returns true if parsing was successful bool KeyValues::ReadAsBinary( CUtlBuffer &buffer ) { if ( buffer.IsText() ) // must be a binary buffer return false; @@ -2681,12 +2579,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) RemoveEverything(); // remove current content Init(); // reset char token[KEYVALUES_TOKEN_SIZE]; KeyValues *dat = this; types_t type = (types_t)buffer.GetUnsignedChar(); @@ -2698,25 +2591,22 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) dat->m_iDataType = type; buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); token[KEYVALUES_TOKEN_SIZE-1] = 0; dat->SetName( token ); switch ( type ) { case TYPE_NONE: { dat->m_pSub = new KeyValues(""); dat->m_pSub->ReadAsBinary( buffer ); break; } case TYPE_STRING: { buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); token[KEYVALUES_TOKEN_SIZE-1] = 0; int len = Q_strlen( token ); @@ -2727,7 +2617,15 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) } case TYPE_WSTRING: { int nLength = buffer.GetShort(); dat->m_wsValue = new wchar_t[nLength + 1]; for( int k = 0; k < nLength; ++ k ) { dat->m_wsValue[k] = buffer.GetShort(); } dat->m_wsValue[ nLength ] = 0; break; } @@ -2760,6 +2658,7 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) case TYPE_PTR: { dat->m_pValue = (void*)buffer.GetUnsignedInt(); break; } default: @@ -2782,6 +2681,155 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) return buffer.IsValid(); } //----------------------------------------------------------------------------- // Alternate dense binary format that pools all the strings, the xbox supports // this during creation of each mod dir's zip processing of kv files. //----------------------------------------------------------------------------- bool KeyValues::ReadAsBinaryPooledFormat( CUtlBuffer &buffer, IBaseFileSystem *pFileSystem, unsigned int poolKey, GetSymbolProc_t pfnEvaluateSymbolProc ) { // xbox only support if ( !IsGameConsole() ) { Assert( 0 ); return false; } if ( buffer.IsText() ) // must be a binary buffer return false; if ( !buffer.IsValid() ) // must be valid, no overflows etc return false; char token[KEYVALUES_TOKEN_SIZE]; KeyValues *dat = this; types_t type = (types_t)buffer.GetUnsignedChar(); // loop through all our peers while ( true ) { if ( type == TYPE_NUMTYPES ) break; // no more peers dat->m_iDataType = type; unsigned int stringKey = buffer.GetUnsignedInt(); if ( !((IFileSystem*)pFileSystem)->GetStringFromKVPool( poolKey, stringKey, token, sizeof( token ) ) ) return false; dat->SetName( token ); switch ( type ) { case TYPE_NONE: { dat->m_pSub = new KeyValues( "" ); if ( !dat->m_pSub->ReadAsBinaryPooledFormat( buffer, pFileSystem, poolKey, pfnEvaluateSymbolProc ) ) return false; break; } case TYPE_STRING: { unsigned int stringKey = buffer.GetUnsignedInt(); if ( !((IFileSystem*)pFileSystem)->GetStringFromKVPool( poolKey, stringKey, token, sizeof( token ) ) ) return false; int len = Q_strlen( token ); dat->m_sValue = new char[len + 1]; Q_memcpy( dat->m_sValue, token, len+1 ); break; } case TYPE_WSTRING: { int nLength = buffer.GetShort(); dat->m_wsValue = new wchar_t[nLength + 1]; for ( int k = 0; k < nLength; ++k ) { dat->m_wsValue[k] = buffer.GetShort(); } dat->m_wsValue[nLength] = 0; break; } case TYPE_INT: { dat->m_iValue = buffer.GetInt(); break; } case TYPE_UINT64: { dat->m_sValue = new char[sizeof(uint64)]; *((uint64 *)dat->m_sValue) = buffer.GetInt64(); break; } case TYPE_FLOAT: { dat->m_flValue = buffer.GetFloat(); break; } case TYPE_COLOR: { dat->m_Color[0] = buffer.GetUnsignedChar(); dat->m_Color[1] = buffer.GetUnsignedChar(); dat->m_Color[2] = buffer.GetUnsignedChar(); dat->m_Color[3] = buffer.GetUnsignedChar(); break; } case TYPE_PTR: { dat->m_pValue = (void*)buffer.GetUnsignedInt(); break; } case TYPE_COMPILED_INT_0: { // only for dense storage purposes, flip back to preferred internal format dat->m_iDataType = TYPE_INT; dat->m_iValue = 0; break; } case TYPE_COMPILED_INT_1: { // only for dense storage purposes, flip back to preferred internal format dat->m_iDataType = TYPE_INT; dat->m_iValue = 1; break; } case TYPE_COMPILED_INT_BYTE: { // only for dense storage purposes, flip back to preferred internal format dat->m_iDataType = TYPE_INT; dat->m_iValue = buffer.GetChar(); break; } default: break; } if ( !buffer.IsValid() ) // error occured return false; if ( !buffer.GetBytesRemaining() ) break; type = (types_t)buffer.GetUnsignedChar(); if ( type == TYPE_NUMTYPES ) break; // new peer follows dat->m_pPeer = new KeyValues(""); dat = dat->m_pPeer; } return buffer.IsValid(); } #include "tier0/memdbgoff.h" //----------------------------------------------------------------------------- @@ -2790,17 +2838,19 @@ bool KeyValues::ReadAsBinary( CUtlBuffer &buffer, int nStackDepth ) void *KeyValues::operator new( size_t iAllocSize ) { MEM_ALLOC_CREDIT(); return KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize); } void *KeyValues::operator new( size_t iAllocSize, int nBlockUse, const char *pFileName, int nLine ) { MemAlloc_PushAllocDbgInfo( pFileName, nLine ); void *p = KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize); MemAlloc_PopAllocDbgInfo(); return p; } #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: deallocator //----------------------------------------------------------------------------- @@ -2814,104 +2864,89 @@ void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileNam KeyValuesSystem()->FreeKeyValuesMemory(pMem); } void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest ) { uint8 *dest = ( uint8 * ) pDest; while( pUnpackTable->m_pKeyName ) { uint8 * dest_field = dest + pUnpackTable->m_nFieldOffset; KeyValues * find_it = FindKey( pUnpackTable->m_pKeyName ); switch( pUnpackTable->m_eDataType ) { case UNPACK_TYPE_FLOAT: { float default_value = ( pUnpackTable->m_pKeyDefault ) ? atof( pUnpackTable->m_pKeyDefault ) : 0.0; *( ( float * ) dest_field ) = GetFloat( pUnpackTable->m_pKeyName, default_value ); break; } break; case UNPACK_TYPE_VECTOR: { Vector *dest_v = ( Vector * ) dest_field; char const *src_string = GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); if ( ( ! src_string ) || ( sscanf(src_string,"%f %f %f", & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z )) != 3 )) dest_v->Init( 0, 0, 0 ); } break; case UNPACK_TYPE_FOUR_FLOATS: { float *dest_f = ( float * ) dest_field; char const *src_string = GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); if ( ( ! src_string ) || ( sscanf(src_string,"%f %f %f %f", dest_f, dest_f + 1, dest_f + 2, dest_f + 3 )) != 4 ) memset( dest_f, 0, 4 * sizeof( float ) ); } break; case UNPACK_TYPE_TWO_FLOATS: { float *dest_f = ( float * ) dest_field; char const *src_string = GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault ); if ( ( ! src_string ) || ( sscanf(src_string,"%f %f", dest_f, dest_f + 1 )) != 2 ) memset( dest_f, 0, 2 * sizeof( float ) ); } break; case UNPACK_TYPE_STRING: { char *dest_s = ( char * ) dest_field; char const *pDefault = ""; if ( pUnpackTable->m_pKeyDefault ) { pDefault = pUnpackTable->m_pKeyDefault; } strncpy( dest_s, GetString( pUnpackTable->m_pKeyName, pDefault ), pUnpackTable->m_nFieldSize ); } break; case UNPACK_TYPE_INT: { int *dest_i = ( int * ) dest_field; int default_int = 0; if ( pUnpackTable->m_pKeyDefault ) default_int = atoi( pUnpackTable->m_pKeyDefault ); *( dest_i ) = GetInt( pUnpackTable->m_pKeyName, default_int ); } break; case UNPACK_TYPE_VECTOR_COLOR: { Vector *dest_v = ( Vector * ) dest_field; if ( find_it ) { Color c = GetColor( pUnpackTable->m_pKeyName ); dest_v->x = c.r(); dest_v->y = c.g(); dest_v->z = c.b(); @@ -2920,11 +2955,11 @@ void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTabl { if ( pUnpackTable->m_pKeyDefault ) sscanf(pUnpackTable->m_pKeyDefault,"%f %f %f", & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z )); else dest_v->Init( 0, 0, 0 ); } *( dest_v ) *= ( 1.0 / 255 ); } } pUnpackTable++; @@ -2975,6 +3010,7 @@ bool KeyValues::ProcessResolutionKeys( const char *pResString ) { // remove the key RemoveSubKey( pKey ); pKey->deleteThis(); } // rename the marked key @@ -2986,65 +3022,244 @@ bool KeyValues::ProcessResolutionKeys( const char *pResString ) return true; } // // KeyValues merge operations // void KeyValues::MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp /* = MERGE_KV_ALL */ ) { if ( !this || !kvMerge ) return; switch ( eOp ) { case MERGE_KV_ALL: MergeFrom( kvMerge->FindKey( "update" ), MERGE_KV_UPDATE ); MergeFrom( kvMerge->FindKey( "delete" ), MERGE_KV_DELETE ); MergeFrom( kvMerge->FindKey( "borrow" ), MERGE_KV_BORROW ); return; case MERGE_KV_UPDATE: { for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) { char const *szName = sub->GetName(); KeyValues *subStorage = this->FindKey( szName, false ); if ( !subStorage ) { AddSubKey( sub->MakeCopy() ); } else { subStorage->MergeFrom( sub, eOp ); } } for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() ) { char const *szName = val->GetName(); if ( KeyValues *valStorage = this->FindKey( szName, false ) ) { this->RemoveSubKey( valStorage ); valStorage->deleteThis(); } this->AddSubKey( val->MakeCopy() ); } } return; case MERGE_KV_BORROW: { for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) { char const *szName = sub->GetName(); KeyValues *subStorage = this->FindKey( szName, false ); if ( !subStorage ) continue; subStorage->MergeFrom( sub, eOp ); } for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() ) { char const *szName = val->GetName(); if ( KeyValues *valStorage = this->FindKey( szName, false ) ) { this->RemoveSubKey( valStorage ); valStorage->deleteThis(); } else continue; this->AddSubKey( val->MakeCopy() ); } } return; case MERGE_KV_DELETE: { for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() ) { char const *szName = sub->GetName(); if ( KeyValues *subStorage = this->FindKey( szName, false ) ) { subStorage->MergeFrom( sub, eOp ); } } for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() ) { char const *szName = val->GetName(); if ( KeyValues *valStorage = this->FindKey( szName, false ) ) { this->RemoveSubKey( valStorage ); valStorage->deleteThis(); } } } return; } } // // KeyValues from string parsing // static char const * ParseStringToken( char const *szStringVal, char const **ppEndOfParse ) { // Eat whitespace while ( V_isspace( *szStringVal ) ) ++ szStringVal; char const *pszResult = szStringVal; while ( *szStringVal && !V_isspace( *szStringVal ) ) ++ szStringVal; if ( ppEndOfParse ) { *ppEndOfParse = szStringVal; } return pszResult; } KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, char const **ppEndOfParse ) { if ( !szName ) szName = ""; if ( !szStringVal ) szStringVal = ""; KeyValues *kv = new KeyValues( szName ); if ( !kv ) return NULL; char chName[256] = {0}; char chValue[256] = {0}; for ( ; ; ) { char const *szEnd; char const *szVarValue = NULL; char const *szVarName = ParseStringToken( szStringVal, &szEnd ); if ( !*szVarName ) break; if ( *szVarName == '}' ) { szStringVal = szVarName + 1; break; } Q_strncpy( chName, szVarName, MIN( sizeof( chName ), szEnd - szVarName + 1 ) ); szVarName = chName; szStringVal = szEnd; if ( *szVarName == '{' ) { szVarName = ""; goto do_sub_key; } szVarValue = ParseStringToken( szStringVal, &szEnd ); if ( *szVarValue == '}' ) { szStringVal = szVarValue + 1; kv->SetString( szVarName, "" ); break; } Q_strncpy( chValue, szVarValue, MIN( sizeof( chValue ), szEnd - szVarValue + 1 ) ); szVarValue = chValue; szStringVal = szEnd; if ( *szVarValue == '{' ) { goto do_sub_key; } // Try to recognize some known types if ( char const *szInt = StringAfterPrefix( szVarValue, "#int#" ) ) { kv->SetInt( szVarName, atoi( szInt ) ); } else if ( !Q_stricmp( szVarValue, "#empty#" ) ) { kv->SetString( szVarName, "" ); } else { kv->SetString( szVarName, szVarValue ); } continue; do_sub_key: { KeyValues *pSubKey = KeyValues::FromString( szVarName, szStringVal, &szEnd ); if ( pSubKey ) { kv->AddSubKey( pSubKey ); } szStringVal = szEnd; continue; } } if ( ppEndOfParse ) { *ppEndOfParse = szStringVal; } return kv; } // // KeyValues dumping implementation // bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */ ) { if ( !pDump->KvBeginKey( this, nIndentLevel ) ) return false; // Dump values for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() ) { if ( !pDump->KvWriteValue( val, nIndentLevel + 1 ) ) return false; } // Dump subkeys for ( KeyValues *sub = this ? GetFirstTrueSubKey() : NULL; sub; sub = sub->GetNextTrueSubKey() ) { if ( !sub->Dump( pDump, nIndentLevel + 1 ) ) return false; } return pDump->KvEndKey( this, nIndentLevel ); } @@ -3055,9 +3270,7 @@ bool IKeyValuesDumpContextAsText::KvBeginKey( KeyValues *pKey, int nIndentLevel return KvWriteIndent( nIndentLevel ) && KvWriteText( pKey->GetName() ) && KvWriteText( " {\n" ); } else { @@ -3098,7 +3311,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { int n = val->GetInt(); char *chBuffer = ( char * ) stackalloc( 128 ); Q_snprintf( chBuffer, 128, "int( %d = 0x%X )", n, n ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3108,7 +3321,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { float fl = val->GetFloat(); char *chBuffer = ( char * ) stackalloc( 128 ); Q_snprintf( chBuffer, 128, "float( %f )", fl ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3118,7 +3331,7 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { void *ptr = val->GetPtr(); char *chBuffer = ( char * ) stackalloc( 128 ); Q_snprintf( chBuffer, 128, "ptr( 0x%p )", ptr ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3127,10 +3340,10 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel case KeyValues::TYPE_WSTRING: { wchar_t const *wsz = val->GetWString(); int nLen = Q_wcslen( wsz ); int numBytes = nLen*2 + 64; char *chBuffer = ( char * ) stackalloc( numBytes ); Q_snprintf( chBuffer, numBytes, "%ls [wstring, len = %d]", wsz, nLen ); if ( !KvWriteText( chBuffer ) ) return false; } @@ -3140,22 +3353,24 @@ bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel { uint64 n = val->GetUint64(); char *chBuffer = ( char * ) stackalloc( 128 ); Q_snprintf( chBuffer, 128, "u64( %lld = 0x%llX )", n, n ); if ( !KvWriteText( chBuffer ) ) return false; } break; default: break; #if 0 // this code was accidentally stubbed out by a mis-integration in CL722860; it hasn't been tested { int n = val->GetDataType(); char *chBuffer = ( char * ) stackalloc( 128 ); Q_snprintf( chBuffer, 128, "??kvtype[%d]", n ); if ( !KvWriteText( chBuffer ) ) return false; } break; #endif } return KvWriteText( "\n" ); @@ -3199,11 +3414,13 @@ bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText ) { if ( m_nDeveloperLevel > 0 ) { DevMsg( "%s", szText ); } else { Msg( "%s", szText ); } return true; } 
 
- 
        xPaw created this gist Jan 19, 2021 .There are no files selected for viewing