Skip to content

Instantly share code, notes, and snippets.

@xPaw
Last active January 19, 2021 11:20
Show Gist options
  • Save xPaw/47e28b4160e7aa8961ec313df22494bb to your computer and use it in GitHub Desktop.
Save xPaw/47e28b4160e7aa8961ec313df22494bb to your computer and use it in GitHub Desktop.

Revisions

  1. xPaw revised this gist Jan 19, 2021. 1 changed file with 876 additions and 316 deletions.
    1,192 changes: 876 additions & 316 deletions KeyValues.cpp
    Original file line number Diff line number Diff line change
    @@ -28,7 +28,7 @@
    #include "utlvector.h"
    #include "utlbuffer.h"
    #include "utlhash.h"
    #include <vstdlib/vstrtools.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
    static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE];
    #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) {}
    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 );
    m_errorStack[stackLevel] = symName;
    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 ( m_errorStack[i] != INVALID_KEY_SYMBOL )
    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;
    Q_strncpy( k.name, name ? name : "NULL", sizeof( k.name ) );
    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;
    }
    // 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 ) ) : "";
    }

    //-----------------------------------------------------------------------------
    // 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
    const char* CKeyValuesTokenReader::ReadToken( bool &wasQuoted, bool &wasConditional )
    {
    return this ? MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) : INVALID_KEY_SYMBOL;
    }

    if ( m_bUsePriorToken )
    {
    m_bUsePriorToken = false;
    wasQuoted = m_bPriorTokenWasQuoted;
    wasConditional = m_bPriorTokenWasConditional;
    return s_pTokenBuf;
    }

    //-----------------------------------------------------------------------------
    // Purpose: Read a single token from buffer (0 terminated)
    //-----------------------------------------------------------------------------
    #pragma warning (disable:4706)
    const char *KeyValues::ReadToken( CUtlBuffer &buf, bool &wasQuoted, bool &wasConditional )
    {
    wasQuoted = false;
    wasConditional = false;
    m_bPriorTokenWasQuoted = wasQuoted = false;
    m_bPriorTokenWasConditional = wasConditional = false;

    if ( !buf.IsValid() )
    if ( !m_Buffer.IsValid() )
    return NULL;

    // eating white spaces and remarks loop
    while ( true )
    {
    buf.EatWhiteSpace();
    if ( !buf.IsValid() )
    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 ( !buf.EatCPPComment() )
    if ( !m_Buffer.EatCPPComment() )
    break;
    }

    const char *c = (const char*)buf.PeekGet( sizeof(char), 0 );
    const char *c = (const char*)m_Buffer.PeekGet( sizeof(char), 0 );
    if ( !c )
    {
    return NULL;
    }

    // read quoted strings specially
    if ( *c == '\"' )
    {
    wasQuoted = true;
    buf.GetDelimitedString( m_bHasEscapeSequences ? GetCStringCharConversion() : GetNoEscCharConversion(),
    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 == '}' )
    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;
    buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 1 );
    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 ( c = (const char*)buf.PeekGet( sizeof(char), 0 ) )
    while ( 1 )
    {
    c = (const char*)m_Buffer.PeekGet( sizeof(char), 0 );

    // end of file
    if ( *c == 0 )
    if ( !c || *c == 0 )
    break;

    // break if any control character appears in non quoted tokens
    if ( *c == '"' || *c == '{' || *c == '}' )
    if ( *c == '"' || *c == '{' || *c == '}' || *c == '=' )
    break;

    if ( *c == '[' )
    {
    bConditionalStart = true;
    }

    if ( *c == ']' && bConditionalStart )
    {
    m_bPriorTokenWasConditional = wasConditional = true;
    bConditionalStart = false;
    wasConditional = true;
    }

    // conditionals need to get tokenized as delimited by []
    // othwerwise break on whitespace
    // 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" );
    }

    buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 1 );
    m_Buffer.GetChar();
    }
    s_pTokenBuf[ nCount ] = 0;
    ++m_nTokensRead;

    return s_pTokenBuf;
    }
    #pragma warning (default:4706)


    //-----------------------------------------------------------------------------
    // 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 )
    {
    #if defined( _WIN32 )
    Assert( IsGameConsole() || ( _heapchk() == _HEAPOK ) );
    #endif
    //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

    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 );
    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 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);
    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 = Q_strlen(pszString);
    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, strlen(convertedString));
    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 )
    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 );
    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(), Q_strlen(dat->GetName()));
    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(), Q_strlen(dat->GetName()));
    INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName()));
    INTERNALWRITE("\"\t\t\"", 4);

    char buf[32];
    Q_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue);
    V_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue);

    INTERNALWRITE(buf, Q_strlen(buf));
    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(), Q_strlen(dat->GetName()));
    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
    Q_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) );
    V_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) );

    INTERNALWRITE(buf, Q_strlen(buf));
    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(), Q_strlen(dat->GetName()));
    INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName()));
    INTERNALWRITE("\"\t\t\"", 4);

    char buf[48];
    Q_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue);
    V_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue);

    INTERNALWRITE(buf, Q_strlen(buf));
    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
    char szBuf[256];
    CUtlVector< char > szBuf;
    const char *subStr = strchr(keyName, '/');
    const char *searchStr = keyName;

    // pull out the substring if it exists
    if (subStr)
    if ( subStr )
    {
    int size = subStr - keyName;
    Q_memcpy( szBuf, keyName, size );
    Assert( size >= 0 );
    Assert( size < 1024 * 1024 );
    szBuf.EnsureCount( size + 1 );
    V_memcpy( szBuf.Base(), keyName, size );
    szBuf[size] = 0;
    searchStr = szBuf;
    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);
    // 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];
    Q_snprintf( buf, sizeof(buf), "%d", newID );
    V_snprintf( buf, sizeof(buf), "%d", newID );

    return CreateKey( buf );
    return CreateKeyUsingKnownLastChild( buf, pLastChild );
    }


    @@ -1036,24 +1127,72 @@ KeyValues *KeyValues::CreateNewKey()
    //-----------------------------------------------------------------------------
    KeyValues* KeyValues::CreateKey( const char *keyName )
    {
    // key wasn't found so just create a new one
    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->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does
    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
    AddSubKey( dat );
    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;
    pSubKey->deleteThis();
    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;
    pSubKey->deleteThis();
    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()
    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()
    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_INT:
    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:
    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:
    Q_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue );
    V_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 );
    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:
    Q_snprintf( buf, sizeof( buf ), "%lld", *((uint64 *)(dat->m_sValue)) );
    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;
    // 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;
    }
    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_INT:
    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 = 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);
    }
    else
    {
    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;
    return defaultValue;
    break;
    }
    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, b, c, d;
    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 = Q_strlen( strValue );
    int len = V_strlen( strValue );
    m_sValue = new char[len + 1];
    Q_memcpy( m_sValue, strValue, 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 = wcslen( value );
    int len = V_wcslen( value );
    dat->m_wsValue = new wchar_t[len + 1];
    Q_memcpy( dat->m_wsValue, value, (len+1) * sizeof(wchar_t) );
    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 = Q_strlen(src.m_sValue) + 1;
    int len = V_strlen(src.m_sValue) + 1;
    m_sValue = new char[len];
    Q_strncpy( m_sValue, src.m_sValue, len );
    V_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;
    V_snprintf( buf,sizeof(buf), "%d", m_iValue );
    int len = V_strlen(buf) + 1;
    m_sValue = new char[len];
    Q_strncpy( m_sValue, buf, len );
    V_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;
    V_snprintf( buf,sizeof(buf), "%f", m_flValue );
    int len = V_strlen(buf) + 1;
    m_sValue = new char[len];
    Q_strncpy( m_sValue, buf, 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)];
    Q_memcpy( m_sValue, src.m_sValue, 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 = Q_strlen( m_sValue );
    int len = V_strlen( m_sValue );
    Assert( !newKeyValue->m_sValue );
    newKeyValue->m_sValue = new char[len + 1];
    Q_memcpy( newKeyValue->m_sValue, m_sValue, len+1 );
    V_memcpy( newKeyValue->m_sValue, m_sValue, len+1 );
    }
    }
    break;
    case TYPE_WSTRING:
    {
    if ( m_wsValue )
    {
    int len = wcslen( m_wsValue );
    int len = V_wcslen( m_wsValue );
    newKeyValue->m_wsValue = new wchar_t[len+1];
    Q_memcpy( newKeyValue->m_wsValue, m_wsValue, (len+1)*sizeof(wchar_t));
    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)];
    Q_memcpy( newKeyValue->m_sValue, m_sValue, 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;
    }

    //-----------------------------------------------------------------------------
    // Purpose: Deletion, ensures object gets deleted from correct heap
    //-----------------------------------------------------------------------------
    void KeyValues::deleteThis()
    KeyValues::types_t KeyValues::GetDataType( void ) const
    {
    delete this;
    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 )
    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 ];
    Q_strncpy( fullpath, resourceName, sizeof( fullpath ) );
    V_strncpy( fullpath, resourceName, sizeof( fullpath ) );

    // Strip off characters back to start or first /
    bool done = false;
    int len = Q_strlen( fullpath );
    int len = V_strlen( fullpath );
    while ( !done )
    {
    if ( len <= 0 )
    {
    break;
    }

    if ( fullpath[ len - 1 ] == '\\' ||
    fullpath[ len - 1 ] == '/' )
    fullpath[ len - 1 ] == '/' )
    {
    break;
    }
    @@ -2020,7 +2195,7 @@ void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoi
    }

    // Append included file
    Q_strncat( fullpath, filetoinclude, sizeof( fullpath ), COPY_ALL_CHARACTERS );
    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 );
    newKV->deleteThis();
    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 ( !Q_strcmp( baseChild->GetName(), newChild->GetName() ) )
    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 = ReadToken( buf, wasQuoted, wasConditional );
    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 ( !Q_stricmp( s, "#include" ) ) // special include macro (not a key name)
    if ( !V_stricmp( s, "#include" ) ) // special include macro (not a key name)
    {
    s = ReadToken( buf, wasQuoted, wasConditional );
    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 ( !Q_stricmp( s, "#base" ) )
    else if ( !V_stricmp( s, "#base" ) )
    {
    s = ReadToken( buf, wasQuoted, wasConditional );
    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 = ReadToken( buf, wasQuoted, wasConditional );
    s = tokenReader.ReadToken( wasQuoted, wasConditional );

    if ( wasConditional )
    {
    bAccepted = EvaluateConditional( s, pfnEvaluateSymbolProc );

    // Now get the '{'
    s = ReadToken( buf, wasQuoted, wasConditional );
    s = tokenReader.ReadToken( wasQuoted, wasConditional );
    }

    if ( s && *s == '{' && !wasQuoted )
    {
    // header is valid so load the file
    pCurrentKey->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc );
    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 ];
    kv->deleteThis();
    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 ];
    kv->deleteThis();
    delete kv;
    }
    }

    bool bErrors = g_KeyValuesErrorStack.EncounteredAnyErrors();
    g_KeyValuesErrorStack.SetFilename( "" );

    return true;
    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, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc )
    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 = ReadToken( buf, wasQuoted, wasConditional );
    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 = CreateKey( name );
    KeyValues *dat = CreateKeyUsingKnownLastChild( name, pLastChild );

    errorKey.Reset( dat->GetNameSymbolCaseSensitive() );

    // get the value
    const char * value = ReadToken( buf, wasQuoted, wasConditional );
    const char * value = tokenReader.ReadToken( wasQuoted, wasConditional );

    bool bFoundConditional = wasConditional;
    if ( wasConditional && value )
    {
    bAccepted = EvaluateConditional( value, pfnEvaluateSymbolProc );

    // get the real value
    value = ReadToken( buf, wasQuoted, wasConditional );
    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, buf, pfnEvaluateSymbolProc );
    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 = Q_strlen( value );
    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

    int ival = strtol( value, &pIEnd, 10 );
    long lval = strtol( value, &pIEnd, 10 );
    float fval = (float)strtod( value, &pFEnd );
    bool bOverflow = ( ival == LONG_MAX || ival == LONG_MIN ) && errno == ERANGE;
    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 = ival;
    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];
    Q_memcpy( dat->m_sValue, value, len+1 );
    V_memcpy( dat->m_sValue, value, len+1 );
    }

    // Look ahead one token for a conditional tag
    int prevPos = buf.TellGet();
    const char *peek = ReadToken( buf, wasQuoted, wasConditional );
    const char *peek = tokenReader.ReadToken( wasQuoted, wasConditional );
    if ( wasConditional )
    {
    bAccepted = EvaluateConditional( peek, pfnEvaluateSymbolProc );
    }
    else
    {
    buf.SeekGet( CUtlBuffer::SEEK_HEAD, prevPos );
    tokenReader.SeekBackOneToken();
    }
    }

    if ( !bAccepted )
    Assert( dat->m_pPeer == NULL );
    if ( bAccepted )
    {
    Assert( pLastChild == NULL || pLastChild->m_pPeer == dat );
    pLastChild = dat;
    }
    else
    {
    this->RemoveSubKey( dat );
    dat->deleteThis();
    //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 ? Q_wcslen( dat->m_wsValue ) : 0;
    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:
    {
    buffer.PutUnsignedInt( (int)dat->m_pValue );
    #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 )
    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

    char token[KEYVALUES_TOKEN_SIZE];

    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;

    buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 );
    token[KEYVALUES_TOKEN_SIZE-1] = 0;
    {
    char token[ KEYVALUES_TOKEN_SIZE ];
    buffer.GetString( token, KEYVALUES_TOKEN_SIZE - 1 );
    token[ KEYVALUES_TOKEN_SIZE - 1 ] = 0;
    dat->SetName( token );
    }

    dat->SetName( token );

    switch ( type )
    {
    case TYPE_NONE:
    {
    dat->m_pSub = new KeyValues("");
    dat->m_pSub->ReadAsBinary( buffer );
    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 = Q_strlen( token );
    int len = V_strlen( token );
    dat->m_sValue = new char[len + 1];
    Q_memcpy( dat->m_sValue, token, 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:
    {
    dat->m_pValue = (void*)buffer.GetUnsignedInt();
    #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();
    }

    //-----------------------------------------------------------------------------
    // 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.
    // 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:
    {
    unsigned int stringKey = buffer.GetUnsignedInt();
    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:
    {
    dat->m_pValue = (void*)buffer.GetUnsignedInt();
    #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"

    //-----------------------------------------------------------------------------
    // Purpose: memory allocator
    //-----------------------------------------------------------------------------
    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;
    }

    #include "tier0/memdbgon.h"

    //-----------------------------------------------------------------------------
    // Purpose: deallocator
    //-----------------------------------------------------------------------------
    void KeyValues::operator delete( void *pMem )
    {
    KeyValuesSystem()->FreeKeyValuesMemory(pMem);
    KeyValuesSystem()->FreeKeyValuesMemory( (KeyValues *)pMem );
    }

    void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine )
    {
    KeyValuesSystem()->FreeKeyValuesMemory(pMem);
    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:
    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:
    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 ))
    ( 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:
    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 )
    ( 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:
    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 )
    ( sscanf(src_string,"%f %f",
    dest_f, dest_f + 1 )) != 2 )
    memset( dest_f, 0, 2 * sizeof( float ) );
    }
    break;

    case UNPACK_TYPE_STRING:
    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 );
    GetString( pUnpackTable->m_pKeyName, pDefault ),
    pUnpackTable->m_nFieldSize );
    }
    break;

    case UNPACK_TYPE_INT:
    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:
    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 ));
    & ( 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 ( Q_stristr( pSubKey->GetName(), pResString ) != NULL )
    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 = Q_stristr( normalKeyName, pResString );
    if ( pString && !Q_stricmp( pString, pResString ) )
    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 );
    pKey->deleteThis();
    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 );
    valStorage->deleteThis();
    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 );
    valStorage->deleteThis();
    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 );
    valStorage->deleteThis();
    delete valStorage;
    }
    }
    }
    @@ -3160,7 +3662,7 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal,
    return NULL;

    char chName[256] = {0};
    char chValue[256] = {0};
    char chValue[1024] = {0};

    for ( ; ; )
    {
    @@ -3175,7 +3677,7 @@ KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal,
    szStringVal = szVarName + 1;
    break;
    }
    Q_strncpy( chName, szVarName, MIN( sizeof( chName ), szEnd - szVarName + 1 ) );
    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;
    }
    Q_strncpy( chValue, szVarValue, MIN( sizeof( chValue ), szEnd - szVarValue + 1 ) );
    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 ( !Q_stricmp( szVarValue, "#empty#" ) )
    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 );
    Q_snprintf( chBuffer, 128, "int( %d = 0x%X )", n, n );
    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 );
    Q_snprintf( chBuffer, 128, "float( %f )", fl );
    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 );
    Q_snprintf( chBuffer, 128, "ptr( 0x%p )", ptr );
    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 = Q_wcslen( wsz );
    int nLen = V_wcslen( wsz );
    int numBytes = nLen*2 + 64;
    char *chBuffer = ( char * ) stackalloc( numBytes );
    Q_snprintf( chBuffer, numBytes, "%ls [wstring, len = %d]", wsz, nLen );
    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 );
    Q_snprintf( chBuffer, 128, "u64( %lld = 0x%llX )", n, n );
    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 );
    Q_snprintf( chBuffer, 128, "??kvtype[%d]", n );
    V_snprintf( chBuffer, 128, "??kvtype[%d]", n );
    if ( !KvWriteText( chBuffer ) )
    return false;
    }
    @@ -3423,4 +3984,3 @@ bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText )
    return true;
    }


  2. xPaw revised this gist Jan 19, 2021. 1 changed file with 954 additions and 737 deletions.
    1,691 changes: 954 additions & 737 deletions KeyValues.cpp
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    //========= Copyright Valve Corporation, All rights reserved. ============//
    //========= 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
    #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 <keyvalues.h>
    #include "filesystem.h"
    #include <vstdlib/IKeyValuesSystem.h>
    #include "tier0/icommandline.h"
    #include "tier0/vprof_telemetry.h"
    #include <Color.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 "utlvector.h"
    #include "utlqueue.h"
    #include "UtlSortVector.h"
    #include "convar.h"
    #include <vstdlib/vstrtools.h>

    // memdbgon must be the last include file in a .cpp file!!!
    #include <tier0/memdbgon.h>

    static const char * s_LastFileLoadingFrom = "unknown"; // just needed for error messages
    //////// 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 4096
    #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) );
    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 );
    Assert( stackLevel < m_errorIndex );
    if ( stackLevel < MAX_ERROR_STACK )
    m_errorStack[stackLevel] = 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 )
    {
    bool bSpewCR = false;

    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 ( m_errorStack[i] != INVALID_KEY_SYMBOL )
    {
    if ( i < m_errorIndex )
    {
    Warning( "%s, ", KeyValues::CallGetStringForSymbol(m_errorStack[i]) );
    Warning( "%s, ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) );
    }
    else
    {
    Warning( "(*%s*), ", KeyValues::CallGetStringForSymbol(m_errorStack[i]) );
    Warning( "(*%s*), ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) );
    }

    bSpewCR = true;
    }
    }

    if ( bSpewCR )
    Warning( "\n" );
    Warning( "\n" );
    }

    private:
    @@ -129,27 +131,18 @@ class CKeyValuesErrorStack
    class CKeyErrorContext
    {
    public:
    CKeyErrorContext( KeyValues *pKv )
    {
    Init( pKv->GetNameSymbol() );
    }

    ~CKeyErrorContext()
    {
    g_KeyValuesErrorStack.Pop();
    }
    CKeyErrorContext( int symName )
    explicit CKeyErrorContext( int symName )
    {
    Init( symName );
    }
    void Reset( int symName )
    {
    g_KeyValuesErrorStack.Reset( m_stackLevel, symName );
    }
    int GetStackLevel() const
    {
    return m_stackLevel;
    }
    private:
    void Init( int symName )
    {
    @@ -231,12 +224,8 @@ class CKeyValuesGrowableStringTable
    public:
    // Constructor
    CKeyValuesGrowableStringTable() :
    #ifdef PLATFORM_64BITS
    m_vecStrings( 0, 4 * 512 * 1024 )
    #else
    m_vecStrings( 0, 512 * 1024 )
    #endif
    , m_hashLookup( 2048, 0, 0, m_Functor, m_Functor )
    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);

    delete s_pGrowableStringTable;
    s_pGrowableStringTable = NULL;
    }
    }


    //-----------------------------------------------------------------------------
    // 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 = INVALID_KEY_SYMBOL;
    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 = false;
    m_bEvaluateConditionals = true;

    // for future proof
    memset( unused, 0, sizeof(unused) );
    m_bHasEscapeSequences = 0;
    }

    //-----------------------------------------------------------------------------
    @@ -508,9 +493,9 @@ void KeyValues::RemoveEverything()
    // Input : *f -
    //-----------------------------------------------------------------------------

    void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/ )
    void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel )
    {
    RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel, sortKeys, bAllowEmptyString );
    RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel );
    }

    //-----------------------------------------------------------------------------
    @@ -528,9 +513,23 @@ void KeyValues::ChainKeyValue( KeyValues* pChain )
    //-----------------------------------------------------------------------------
    const char *KeyValues::GetName( void ) const
    {
    return s_pfGetStringForSymbol( m_iKeyName );
    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 ) ) )
    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;
    }

    // break on whitespace
    if ( isspace(*c) )
    // 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: if parser should evaluate conditional blocks ( [$WINDOWS] etc. )
    //-----------------------------------------------------------------------------
    void KeyValues::UsesConditionals(bool state)
    {
    m_bEvaluateConditionals = state;
    }


    //-----------------------------------------------------------------------------
    // Purpose: Load keyValues from disk
    //-----------------------------------------------------------------------------
    bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool refreshCache )
    bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, GetSymbolProc_t pfnEvaluateSymbolProc )
    {
    TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
    TM_ZONE_DEFAULT_PARAM( TELEMETRY_LEVEL0, resourceName );

    Assert(filesystem);
    #ifdef WIN32
    Assert( IsX360() || ( IsPC() && _heapchk() == _HEAPOK ) );
    #if defined( _WIN32 )
    Assert( IsGameConsole() || ( _heapchk() == _HEAPOK ) );
    #endif

    #ifdef STAGING_ONLY
    static bool s_bCacheEnabled = !!CommandLine()->FindParm( "-enable_keyvalues_cache" );
    const bool bUseCache = s_bCacheEnabled && ( s_pfGetSymbolForString == KeyValues::GetSymbolForStringClassic );
    #else
    /*
    People are cheating with the keyvalue cache enabled by doing the below, so disable it.
    For example if one is to allow a blue demoman texture on sv_pure they
    change it to this, "$basetexture" "temp/demoman_blue". Remember to move the
    demoman texture to the temp folder in the materials folder. It will likely
    not be there so make a new folder for it. Once the directory in the
    demoman_blue vmt is changed to the temp folder and the vtf texture is in
    the temp folder itself you are finally done.
    I packed my mods into a vpk but I don't think it's required. Once in game
    you must create a server via the create server button and select the map
    that will load the custom texture before you join a valve server. I suggest
    you only do this with player textures and such as they are always loaded.
    After you load the map you join the valve server and the textures should
    appear and work on valve servers.
    This can be done on any sv_pure 1 server but it depends on what is type of
    files are allowed. All valve servers allow temp files so that is the
    example I used here."
    So all vmt's files can bypass sv_pure 1. And I believe this mod is mostly
    made of vmt files, so valve's sv_pure 1 bull is pretty redundant.
    */
    const bool bUseCache = false;
    #endif

    // If pathID is null, we cannot cache the result because that has a weird iterate-through-a-bunch-of-locations behavior.
    const bool bUseCacheForRead = bUseCache && !refreshCache && pathID != NULL;
    const bool bUseCacheForWrite = bUseCache && pathID != NULL;

    COM_TimestampedLog( "KeyValues::LoadFromFile(%s%s%s): Begin", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "" );

    // Keep a cache of keyvalues, try to load it here.
    if ( bUseCacheForRead && KeyValuesSystem()->LoadFileKeyValuesFromCache( this, resourceName, pathID, filesystem ) ) {
    COM_TimestampedLog( "KeyValues::LoadFromFile(%s%s%s): End / CacheHit", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "" );
    return true;
    }

    FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
    if ( !f )
    {
    COM_TimestampedLog("KeyValues::LoadFromFile(%s%s%s): End / FileNotFound", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
    return false;
    }

    s_LastFileLoadingFrom = (char*)resourceName;

    @@ -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
    bRetOK = LoadFromBuffer( resourceName, buffer, filesystem );
    }

    // The cache relies on the KeyValuesSystem string table, which will only be valid if we're
    // using classic mode.
    if ( bUseCacheForWrite && bRetOK )
    {
    KeyValuesSystem()->AddFileKeyValuesToCache( this, resourceName, pathID );
    }

    ( (IFileSystem *)filesystem )->FreeOptimalReadBuffer( buffer );
    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 );
    }

    COM_TimestampedLog("KeyValues::LoadFromFile(%s%s%s): End / Success", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
    ((IFileSystem *)filesystem)->FreeOptimalReadBuffer( buffer );

    return bRetOK;
    }

    //-----------------------------------------------------------------------------
    // Purpose: Save the keyvalues to disk
    // Creates the path to the file if it doesn't exist
    // Creates the path to the file if it doesn't exist
    //-----------------------------------------------------------------------------
    bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, bool sortKeys /*= false*/, bool bAllowEmptyString /*= false*/, bool bCacheResult /*= false*/ )
    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(1, "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n",
    DevMsg( "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n",
    resourceName?resourceName:"NULL", pathID?pathID:"NULL" );
    return false;
    }

    KeyValuesSystem()->InvalidateCacheForFile( resourceName, pathID );
    if ( bCacheResult ) {
    KeyValuesSystem()->AddFileKeyValuesToCache( this, resourceName, pathID );
    }
    RecursiveSaveToFile(filesystem, f, NULL, 0, sortKeys, bAllowEmptyString );
    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);
    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, Q_strlen(convertedString));
    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, bool sortKeys, bool bAllowEmptyString )
    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
    if ( sortKeys )
    for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer )
    {
    CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedKeys;

    for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer )
    {
    vecSortedKeys.InsertNoSort(dat);
    }
    vecSortedKeys.RedoSort();

    FOR_EACH_VEC( vecSortedKeys, i )
    if ( dat->m_pSub )
    {
    SaveKeyToFile( vecSortedKeys[i], filesystem, f, pBuf, indentLevel, sortKeys, bAllowEmptyString );
    dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1 );
    }
    }
    else
    {
    for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer )
    SaveKeyToFile( dat, filesystem, f, pBuf, indentLevel, sortKeys, bAllowEmptyString );
    }

    // write tail
    WriteIndents(filesystem, f, pBuf, indentLevel);
    INTERNALWRITE("}\n", 2);
    }

    void KeyValues::SaveKeyToFile( KeyValues *dat, IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel, bool sortKeys, bool bAllowEmptyString )
    {
    if ( dat->m_pSub )
    {
    dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1, sortKeys, bAllowEmptyString );
    }
    else
    {
    // only write non-empty keys

    switch (dat->m_iDataType)
    else
    {
    case TYPE_STRING:
    {
    if ( dat->m_sValue && ( bAllowEmptyString || *(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);
    // only write non-empty keys

    INTERNALWRITE("\"\n", 2);
    }
    break;
    }
    case TYPE_WSTRING:
    switch (dat->m_iDataType)
    {
    if ( dat->m_wsValue )
    case TYPE_STRING:
    {
    static char buf[KEYVALUES_TOKEN_SIZE];
    // make sure we have enough space
    int result = Q_UnicodeToUTF8( dat->m_wsValue, buf, KEYVALUES_TOKEN_SIZE);
    if (result)
    if (dat->m_sValue && *(dat->m_sValue))
    {
    WriteIndents(filesystem, f, pBuf, indentLevel + 1);
    INTERNALWRITE("\"", 1);
    INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName()));
    WriteConvertedString(filesystem, f, pBuf, dat->GetName());
    INTERNALWRITE("\"\t\t\"", 4);

    WriteConvertedString(filesystem, f, pBuf, buf);
    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;
    }
    break;
    }

    case TYPE_INT:
    {
    WriteIndents(filesystem, f, pBuf, indentLevel + 1);
    INTERNALWRITE("\"", 1);
    INTERNALWRITE(dat->GetName(), Q_strlen(dat->GetName()));
    INTERNALWRITE("\"\t\t\"", 4);
    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);
    char buf[32];
    Q_snprintf(buf, sizeof( buf ), "%d", dat->m_iValue);

    INTERNALWRITE(buf, Q_strlen(buf));
    INTERNALWRITE("\"\n", 2);
    break;
    }
    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);
    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
    #ifdef WIN32
    Q_snprintf( buf, sizeof( buf ), "0x%016I64X", *( (uint64 *)dat->m_sValue ) );
    #else
    Q_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) );
    #endif
    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;
    }
    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);
    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);
    char buf[48];
    Q_snprintf(buf, sizeof( buf ), "%f", dat->m_flValue);

    INTERNALWRITE(buf, Q_strlen(buf));
    INTERNALWRITE("\"\n", 2);
    INTERNALWRITE(buf, Q_strlen(buf));
    INTERNALWRITE("\"\n", 2);
    break;
    }
    case TYPE_COLOR:
    DevMsg( "KeyValues::RecursiveSaveToFile: TODO, missing code for TYPE_COLOR.\n" );
    break;
    }
    case TYPE_COLOR:
    DevMsg(1, "KeyValues::RecursiveSaveToFile: TODO, missing code for TYPE_COLOR.\n");
    break;

    default:
    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 = m_pSub; dat != NULL; dat = dat->m_pPeer)
    for (KeyValues *dat = this ? m_pSub : NULL; dat != NULL; dat = dat->m_pPeer)
    {
    if (dat->m_iKeyName == keySymbol)
    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
    HKeySymbol iSearchStr = s_pfGetSymbolForString( searchStr, bCreate );

    // 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 == iSearchStr)
    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);

    dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent
    dat->UsesConditionals( m_bEvaluateConditionals != 0 );

    // 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
    KeyValues *pLastChild = NULL;
    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;
    }

    pLastChild = dat;
    }

    char buf[12];
    Q_snprintf( buf, sizeof(buf), "%d", newID );

    return CreateKeyUsingKnownLastChild( buf, pLastChild );
    return CreateKey( buf );
    }


    @@ -1112,65 +1036,24 @@ KeyValues *KeyValues::CreateNewKey()
    //-----------------------------------------------------------------------------
    KeyValues* KeyValues::CreateKey( const char *keyName )
    {
    KeyValues *pLastChild = FindLastSubKey();
    return CreateKeyUsingKnownLastChild( keyName, pLastChild );
    }

    //-----------------------------------------------------------------------------
    KeyValues* KeyValues::CreateKeyUsingKnownLastChild( const char *keyName, KeyValues *pLastChild )
    {
    // Create a new key
    // 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
    dat->UsesConditionals( m_bEvaluateConditionals != 0 );

    // add into subkey list
    AddSubkeyUsingKnownLastChild( dat, pLastChild );
    AddSubKey( dat );

    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 );

    // // In debug, make sure that they really do know which child is the last one
    // #ifdef _DEBUG
    // KeyValues *pTempDat = m_pSub;
    // while ( pTempDat->GetNextKey() != NULL )
    // {
    // pTempDat = pTempDat->GetNextKey();
    // }
    // Assert( pTempDat == pLastChild );
    // #endif

    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
    @@ -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: Locate last child. Returns NULL if we have no children
    // Purpose: Return the first subkey in the list
    //-----------------------------------------------------------------------------
    KeyValues *KeyValues::FindLastSubKey()
    KeyValues *KeyValues::GetFirstSubKey()
    {
    return this ? m_pSub : NULL;
    }

    // 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 next subkey
    //-----------------------------------------------------------------------------
    KeyValues *KeyValues::GetNextKey()
    {
    return this ? m_pPeer : NULL;
    }

    //-----------------------------------------------------------------------------
    @@ -1254,7 +1249,7 @@ void KeyValues::SetNextKey( KeyValues *pDat )

    KeyValues* KeyValues::GetFirstTrueSubKey()
    {
    KeyValues *pRet = m_pSub;
    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 = m_pPeer;
    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 = m_pSub;
    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 = m_pPeer;
    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:
    return (uint64)Q_atoi64(dat->m_sValue);
    {
    uint64 uiResult = 0ull;
    sscanf( dat->m_sValue, "%lld", &uiResult );
    return uiResult;
    }
    case TYPE_WSTRING:
    return _wtoi64(dat->m_wsValue);
    {
    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
    Assert( !"impl me" );
    return 0.0;
    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_PTR:
    Q_snprintf( buf, sizeof( buf ), "%lld", (int64)(size_t)dat->m_pValue );
    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 = Q_UnicodeToUTF8(dat->m_wsValue, 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_PTR:
    swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%lld", (int64)(size_t)dat->m_pValue );
    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 = Q_UTF8ToUnicode(dat->m_sValue, pWBuf, bufSize * sizeof( wchar_t ) );
    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: Get a bool interpretation of the key.
    //-----------------------------------------------------------------------------
    bool KeyValues::GetBool( const char *keyName, bool defaultValue, bool* optGotDefault )
    {
    if ( FindKey( keyName ) )
    {
    if ( optGotDefault )
    (*optGotDefault) = false;
    return 0 != GetInt( keyName, 0 );
    }

    if ( optGotDefault )
    (*optGotDefault) = true;

    return defaultValue;
    }

    //-----------------------------------------------------------------------------
    // Purpose: Gets a color
    //-----------------------------------------------------------------------------
    Color KeyValues::GetColor( const char *keyName )
    Color KeyValues::GetColor( const char *keyName , const Color& defaultColor )
    {
    Color color(0, 0, 0, 0);
    KeyValues *dat = FindKey( keyName, false );
    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] = dat->m_flValue;
    color[0] = (unsigned char)dat->m_flValue;
    }
    else if ( dat->m_iDataType == TYPE_INT )
    {
    color[0] = dat->m_iValue;
    color[0] = (unsigned char)dat->m_iValue;
    }
    else if ( dat->m_iDataType == TYPE_STRING )
    {
    // parse the colors out of the string
    float a = 0.0f, b = 0.0f, c = 0.0f, d = 0.0f;
    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 )
    {
    KeyValues *dat = FindKey( keyName, true );

    if ( dat )
    if ( KeyValues *dat = FindKey( keyName, true ) )
    {
    if ( dat->m_iDataType == TYPE_STRING && dat->m_sValue == value )
    {
    return;
    }

    // delete the old value
    delete [] dat->m_sValue;
    // make sure we're not storing the WSTRING - as we're converting over to STRING
    delete [] dat->m_wsValue;
    dat->m_wsValue = NULL;

    if (!value)
    {
    // ensure a valid value
    value = "";
    }

    // allocate memory for the new value and copy it in
    int len = Q_strlen( value );
    dat->m_sValue = new char[len + 1];
    Q_memcpy( dat->m_sValue, value, len+1 );

    dat->m_iDataType = TYPE_STRING;
    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 = Q_wcslen( value );
    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 )
    {
    m_iKeyName = s_pfGetSymbolForString( setName, true );
    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 )
    }
    }

    //-----------------------------------------------------------------------------
    // Purpose: Copies the tree from the other KeyValues into this one, recursively
    // beginning with the root specified by rootSrc.
    //-----------------------------------------------------------------------------
    void KeyValues::CopyKeyValuesFromRecursive( const KeyValues& rootSrc )
    void KeyValues::RecursiveCopyKeyValues( KeyValues& src )
    {
    // This code used to be recursive, which was more elegant. Unfortunately, it also blew the stack for large
    // KeyValues. So now we have the iterative version which is uglier but doesn't blow the stack.
    // This uses breadth-first traversal.

    struct CopyStruct
    {
    KeyValues* dst;
    const KeyValues* src;
    };

    char tmp[256];
    KeyValues* localDst = NULL;

    CUtlQueue<CopyStruct> nodeQ;
    nodeQ.Insert({ this, &rootSrc });
    // 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;

    while ( nodeQ.Count() > 0 )
    if( !src.m_pSub )
    {
    CopyStruct cs = nodeQ.RemoveAtHead();

    // Process all the siblings of the current node. If anyone has a child, add it to the queue.
    while (cs.src)
    m_iDataType = src.m_iDataType;
    char buf[256];
    switch( src.m_iDataType )
    {
    Assert( (cs.src != NULL) == (cs.dst != NULL) );

    // Copy the node contents
    cs.dst->CopyKeyValue( *cs.src, sizeof(tmp), tmp );

    // Add children to the queue to process later.
    if (cs.src->m_pSub) {
    cs.dst->m_pSub = localDst = new KeyValues( NULL );
    nodeQ.Insert({ localDst, cs.src->m_pSub });
    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 );
    }

    // Process siblings until we hit the end of the line.
    if (cs.src->m_pPeer) {
    cs.dst->m_pPeer = new KeyValues( NULL );
    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 );
    }
    else {
    cs.dst->m_pPeer = NULL;
    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 );
    }

    // Advance to the next peer.
    cs.src = cs.src->m_pPeer;
    cs.dst = cs.dst->m_pPeer;
    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;
    }
    }
    }

    //-----------------------------------------------------------------------------
    // Purpose: Copies a single KeyValue from src to this, using the provided temporary
    // buffer if the keytype requires it. Does NOT recurse.
    //-----------------------------------------------------------------------------
    void KeyValues::CopyKeyValue( const KeyValues& src, size_t tmpBufferSizeB, char* tmpBuffer )
    {
    m_iKeyName = src.GetNameSymbol();

    if ( src.m_pSub )
    return;

    m_iDataType = src.m_iDataType;

    switch( src.m_iDataType )
    }
    #if 0
    KeyValues *pDst = this;
    for ( KeyValues *pSrc = src.m_pSub; pSrc; pSrc = pSrc->m_pPeer )
    {
    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( tmpBuffer, tmpBufferSizeB, "%d", m_iValue );
    int len = Q_strlen(tmpBuffer) + 1;
    m_sValue = new char[len];
    Q_strncpy( m_sValue, tmpBuffer, len );
    }
    break;
    case TYPE_FLOAT:
    {
    m_flValue = src.m_flValue;
    Q_snprintf( tmpBuffer, tmpBufferSizeB, "%f", m_flValue );
    int len = Q_strlen(tmpBuffer) + 1;
    m_sValue = new char[len];
    Q_strncpy( m_sValue, tmpBuffer, len );
    }
    break;
    case TYPE_PTR:
    if ( pSrc->m_pSub )
    {
    m_pValue = src.m_pValue;
    pDst->m_pSub = new KeyValues( pSrc->m_pSub->getName() );
    pDst->m_pSub->RecursiveCopyKeyValues( *pSrc->m_pSub );
    }
    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:
    else
    {
    // do nothing . .what the heck is this?
    Assert( 0 );
    // copy non-empty keys
    if ( pSrc->m_sValue && *(pSrc->m_sValue) )
    {
    pDst->m_pPeer = new KeyValues(
    }
    }
    break;
    }
    #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=( const KeyValues& src )
    KeyValues& KeyValues::operator=( KeyValues& src )
    {
    RemoveEverything();
    Init(); // reset all values
    CopyKeyValuesFromRecursive( src );
    RecursiveCopyKeyValues( src );
    return *this;
    }

    @@ -1918,9 +1855,6 @@ KeyValues *KeyValues::MakeCopy( void ) const
    {
    KeyValues *newKeyValue = new KeyValues(GetName());

    newKeyValue->UsesEscapeSequences( m_bHasEscapeSequences != 0 );
    newKeyValue->UsesConditionals( m_bEvaluateConditionals != 0 );

    // copy data
    newKeyValue->m_iDataType = m_iDataType;
    switch ( m_iDataType )
    @@ -1940,7 +1874,7 @@ KeyValues *KeyValues::MakeCopy( void ) const
    {
    if ( m_wsValue )
    {
    int len = Q_wcslen( 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:
    //-----------------------------------------------------------------------------
    KeyValues *KeyValues::MakeCopy( bool copySiblings ) const
    {
    KeyValues* rootDest = MakeCopy();
    if ( !copySiblings )
    return rootDest;

    const KeyValues* curSrc = GetNextKey();
    KeyValues* curDest = rootDest;
    while (curSrc) {
    curDest->SetNextKey( curSrc->MakeCopy() );
    curDest = curDest->GetNextKey();
    curSrc = curSrc->GetNextKey();
    }

    return rootDest;
    }

    //-----------------------------------------------------------------------------
    // Purpose: Check if a keyName has no value assigned to it.
    @@ -2049,13 +1964,14 @@ void KeyValues::deleteThis()
    void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys )
    {
    // Append any included keys, too...
    KeyValues *insertSpot = this;
    int includeCount = includedKeys.Count();
    for ( int i = 0; i < includeCount; i++ )
    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 )
    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 );
    for (;;)
    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
    newKV->UsesConditionals( m_bEvaluateConditionals != 0 );

    if ( newKV->LoadFromFile( pFileSystem, fullpath, pPathID ) )
    if ( newKV->LoadFromFile( pFileSystem, fullpath, pPathID, pfnEvaluateSymbolProc ) )
    {
    includedKeys.AddToTail( newKV );
    }
    @@ -2180,48 +2096,48 @@ void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV )
    }

    //-----------------------------------------------------------------------------
    // Returns whether a keyvalues conditional evaluates to true or false
    // Needs more flexibility with conditionals, checking convars would be nice.
    // Returns whether a keyvalues conditional expression string evaluates to true or false
    //-----------------------------------------------------------------------------
    bool EvaluateConditional( const char *str )
    bool KeyValues::EvaluateConditional( const char *pExpressionString, GetSymbolProc_t pfnEvaluateSymbolProc )
    {
    if ( !str )
    return false;

    if ( *str == '[' )
    str++;

    bool bNot = false; // should we negate this command?
    if ( *str == '!' )
    bNot = true;

    if ( Q_stristr( str, "$X360" ) )
    return IsX360() ^ bNot;

    if ( Q_stristr( str, "$WIN32" ) )
    return IsPC() ^ bNot; // hack hack - for now WIN32 really means IsPC

    if ( Q_stristr( str, "$WINDOWS" ) )
    return IsWindows() ^ bNot;

    if ( Q_stristr( str, "$OSX" ) )
    return IsOSX() ^ bNot;

    if ( Q_stristr( str, "$LINUX" ) )
    return IsLinux() ^ bNot;
    // 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" );
    }

    if ( Q_stristr( str, "$POSIX" ) )
    return IsPosix() ^ bNot;

    return false;
    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 )
    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 || *s == 0 )
    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 );
    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 );
    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
    pCurrentKey->UsesConditionals( m_bEvaluateConditionals != 0 );

    if ( pPreviousKey )
    {
    @@ -2294,7 +2216,7 @@ bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBase

    if ( wasConditional )
    {
    bAccepted = !m_bEvaluateConditionals || EvaluateConditional( s );
    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 );
    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 )
    bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc )
    {
    if ( !pBuffer )
    return true;

    COM_TimestampedLog("KeyValues::LoadFromBuffer(%s%s%s): Begin", pPathID ? pPathID : "", pPathID && resourceName ? "/" : "", resourceName ? resourceName : "");
    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 = Q_strlen( pBuffer );
    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 );
    }

    bool retVal = LoadFromBuffer( resourceName, buf, pFileSystem, pPathID );

    COM_TimestampedLog("KeyValues::LoadFromBuffer(%s%s%s): End", pPathID ? pPathID : "", pPathID && resourceName ? "/" : "", resourceName ? resourceName : "");

    return retVal;
    return LoadFromBuffer( resourceName, buf, pFileSystem, pPathID, pfnEvaluateSymbolProc );
    }

    //-----------------------------------------------------------------------------
    // Purpose:
    //-----------------------------------------------------------------------------
    void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf )
    void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc )
    {
    CKeyErrorContext errorReport(this);
    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;
    @@ -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 = CreateKeyUsingKnownLastChild( name, pLastChild );
    KeyValues *dat = CreateKey( name );

    errorKey.Reset( dat->GetNameSymbol() );
    errorKey.Reset( dat->GetNameSymbolCaseSensitive() );

    // get the value
    const char * value = ReadToken( buf, wasQuoted, wasConditional );

    if ( wasConditional && value )
    {
    bAccepted = !m_bEvaluateConditionals || EvaluateConditional( 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 );
    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 = !m_bEvaluateConditionals || EvaluateConditional( peek );
    bAccepted = EvaluateConditional( peek, pfnEvaluateSymbolProc );
    }
    else
    {
    buf.SeekGet( CUtlBuffer::SEEK_HEAD, prevPos );
    }
    }

    Assert( dat->m_pPeer == NULL );
    if ( bAccepted )
    {
    Assert( pLastChild == NULL || pLastChild->m_pPeer == dat );
    pLastChild = dat;
    }
    else
    if ( !bAccepted )
    {
    //this->RemoveSubKey( dat );
    if ( pLastChild == NULL )
    {
    Assert( m_pSub == dat );
    m_pSub = NULL;
    }
    else
    {
    Assert( pLastChild->m_pPeer == dat );
    pLastChild->m_pPeer = NULL;
    }

    this->RemoveSubKey( dat );
    dat->deleteThis();
    dat = NULL;
    }
    }
    }



    // writes KeyValue as binary data to buffer
    bool KeyValues::WriteAsBinary( CUtlBuffer &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 ( KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer )
    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:
    {
    Assert( !"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.PutDouble( *((double *)dat->m_sValue) );
    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, int nStackDepth )
    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

    if ( nStackDepth > 100 )
    {
    AssertMsgOnce( false, "KeyValues::ReadAsBinary() stack depth > 100\n" );
    return false;
    }

    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;

    {
    char token[KEYVALUES_TOKEN_SIZE];
    buffer.GetString( token );
    token[KEYVALUES_TOKEN_SIZE-1] = 0;
    dat->SetName( token );
    }
    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 );
    dat->m_pSub->ReadAsBinary( buffer );
    break;
    }
    case TYPE_STRING:
    {
    char token[KEYVALUES_TOKEN_SIZE];
    buffer.GetString( token );
    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:
    {
    Assert( !"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( (int)iAllocSize );
    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( (int)iAllocSize );
    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, size_t DestSizeInBytes )
    void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest )
    {
    #ifdef DBGFLAG_ASSERT
    void *pDestEnd = ( char * )pDest + DestSizeInBytes + 1;
    #endif

    uint8 *dest=(uint8 *) pDest;
    uint8 *dest = ( uint8 * ) pDest;
    while( pUnpackTable->m_pKeyName )
    {
    uint8 *dest_field=dest+pUnpackTable->m_nFieldOffset;
    KeyValues *find_it=FindKey( 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:
    {
    Assert( dest_field + sizeof( float ) < pDestEnd );

    float default_value=(pUnpackTable->m_pKeyDefault)?atof(pUnpackTable->m_pKeyDefault):0.0;
    *( ( float *) dest_field)=GetFloat( pUnpackTable->m_pKeyName, default_value );
    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:
    {
    Assert( dest_field + sizeof( Vector ) < pDestEnd );

    Vector *dest_v=(Vector *) dest_field;
    char const *src_string=
    Vector *dest_v = ( Vector * ) dest_field;
    char const *src_string =
    GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault );
    if ( (!src_string) ||
    if ( ( ! src_string ) ||
    ( sscanf(src_string,"%f %f %f",
    &(dest_v->x), &(dest_v->y), &(dest_v->z)) != 3))
    & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z )) != 3 ))
    dest_v->Init( 0, 0, 0 );
    }
    break;

    case UNPACK_TYPE_FOUR_FLOATS:
    {
    Assert( dest_field + sizeof( float ) * 4 < pDestEnd );

    float *dest_f=(float *) dest_field;
    char const *src_string=
    float *dest_f = ( float * ) dest_field;
    char const *src_string =
    GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault );
    if ( (!src_string) ||
    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) );
    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:
    {
    Assert( dest_field + sizeof( float ) * 2 < pDestEnd );

    float *dest_f=(float *) dest_field;
    char const *src_string=
    float *dest_f = ( float * ) dest_field;
    char const *src_string =
    GetString( pUnpackTable->m_pKeyName, pUnpackTable->m_pKeyDefault );
    if ( (!src_string) ||
    if ( ( ! src_string ) ||
    ( sscanf(src_string,"%f %f",
    dest_f,dest_f+1)) != 2)
    memset( dest_f, 0, 2*sizeof(float) );
    dest_f, dest_f + 1 )) != 2 )
    memset( dest_f, 0, 2 * sizeof( float ) );
    }
    break;

    case UNPACK_TYPE_STRING:
    {
    Assert( dest_field + pUnpackTable->m_nFieldSize < pDestEnd );

    char *dest_s=(char *) dest_field;
    strncpy( dest_s, GetString( pUnpackTable->m_pKeyName,
    pUnpackTable->m_pKeyDefault ),
    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:
    {
    Assert( dest_field + sizeof( int ) < pDestEnd );

    int *dest_i=(int *) dest_field;
    int default_int=0;
    if ( pUnpackTable->m_pKeyDefault)
    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 );
    *( dest_i ) = GetInt( pUnpackTable->m_pKeyName, default_int );
    }
    break;

    case UNPACK_TYPE_VECTOR_COLOR:
    {
    Assert( dest_field + sizeof( Vector ) < pDestEnd );

    Vector *dest_v=(Vector *) dest_field;
    if (find_it)
    Vector *dest_v = ( Vector * ) dest_field;
    if ( find_it )
    {
    Color c=GetColor( pUnpackTable->m_pKeyName );
    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));
    & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z ));
    else
    dest_v->Init( 0, 0, 0 );
    }
    *(dest_v) *= (1.0/255);
    *( 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 dumping implementation
    // KeyValues from string parsing
    //
    bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */, bool bSorted /*= false*/ )

    static char const * ParseStringToken( char const *szStringVal, char const **ppEndOfParse )
    {
    if ( !pDump->KvBeginKey( this, nIndentLevel ) )
    return false;
    // Eat whitespace
    while ( V_isspace( *szStringVal ) )
    ++ szStringVal;

    char const *pszResult = szStringVal;

    if ( bSorted )
    while ( *szStringVal && !V_isspace( *szStringVal ) )
    ++ szStringVal;

    if ( ppEndOfParse )
    {
    CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedKeys;

    // Dump values
    for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() )
    *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 == '}' )
    {
    vecSortedKeys.InsertNoSort( val );
    szStringVal = szVarName + 1;
    break;
    }
    vecSortedKeys.RedoSort();
    Q_strncpy( chName, szVarName, MIN( sizeof( chName ), szEnd - szVarName + 1 ) );
    szVarName = chName;
    szStringVal = szEnd;

    FOR_EACH_VEC( vecSortedKeys, i )
    if ( *szVarName == '{' )
    {
    if ( !pDump->KvWriteValue( vecSortedKeys[i], nIndentLevel + 1 ) )
    return false;
    szVarName = "";
    goto do_sub_key;
    }

    vecSortedKeys.Purge();

    // Dump subkeys
    for ( KeyValues *sub = this ? GetFirstTrueSubKey() : NULL; sub; sub = sub->GetNextTrueSubKey() )
    szVarValue = ParseStringToken( szStringVal, &szEnd );
    if ( *szVarValue == '}' )
    {
    vecSortedKeys.InsertNoSort( sub );
    szStringVal = szVarValue + 1;
    kv->SetString( szVarName, "" );
    break;
    }
    vecSortedKeys.RedoSort();
    Q_strncpy( chValue, szVarValue, MIN( sizeof( chValue ), szEnd - szVarValue + 1 ) );
    szVarValue = chValue;
    szStringVal = szEnd;

    FOR_EACH_VEC( vecSortedKeys, i )
    if ( *szVarValue == '{' )
    {
    if ( !vecSortedKeys[i]->Dump( pDump, nIndentLevel + 1, bSorted ) )
    return false;
    goto do_sub_key;
    }
    }
    else
    {
    // Dump values
    for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() )

    // Try to recognize some known types
    if ( char const *szInt = StringAfterPrefix( szVarValue, "#int#" ) )
    {
    if ( !pDump->KvWriteValue( val, nIndentLevel + 1 ) )
    return false;
    kv->SetInt( szVarName, atoi( szInt ) );
    }
    else if ( !Q_stricmp( szVarValue, "#empty#" ) )
    {
    kv->SetString( szVarName, "" );
    }
    else
    {
    kv->SetString( szVarName, szVarValue );
    }
    continue;

    // Dump subkeys
    for ( KeyValues *sub = this ? GetFirstTrueSubKey() : NULL; sub; sub = sub->GetNextTrueSubKey() )
    do_sub_key:
    {
    if ( !sub->Dump( pDump, nIndentLevel + 1 ) )
    return false;
    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" ) &&
    KvWriteIndent( nIndentLevel ) &&
    KvWriteText( "{\n" );
    KvWriteText( " {\n" );
    }
    else
    {
    @@ -3098,7 +3311,7 @@ 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 );
    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 );
    V_snprintf( chBuffer, 128, "float( %f )", fl );
    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 );
    V_snprintf( chBuffer, 128, "ptr( 0x%p )", ptr );
    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 = V_wcslen( wsz );
    int nLen = Q_wcslen( wsz );
    int numBytes = nLen*2 + 64;
    char *chBuffer = ( char * ) stackalloc( numBytes );
    V_snprintf( chBuffer, numBytes, "%ls [wstring, len = %d]", wsz, nLen );
    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 );
    V_snprintf( chBuffer, 128, "u64( %lld = 0x%llX )", n, n );
    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 );
    V_snprintf( chBuffer, 128, "??kvtype[%d]", n );
    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( m_nDeveloperLevel, "%s", szText );
    DevMsg( "%s", szText );
    }
    else
    {
    Msg( "%s", szText );
    }
    return true;
    }


  3. xPaw created this gist Jan 19, 2021.
    3,209 changes: 3,209 additions & 0 deletions KeyValues.cpp
    3,209 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.