//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#if defined( _WIN32 ) && !defined( _X360 )
#include <windows.h>		// for widechartomultibyte and multibytetowidechar
#elif defined(POSIX)
#include <wchar.h> // wcslen()
#define _alloca alloca
#define _wtoi(arg) wcstol(arg, NULL, 10)
#define _wtoi64(arg) wcstoll(arg, NULL, 10)
#endif

#include <keyvalues.h>
#include "filesystem.h"
#include <vstdlib/ikeyvaluessystem.h>

#include <color.h>
#include <stdlib.h>
#include <ctype.h>
#include "tier1/convar.h"
#include "tier0/dbg.h"
#include "tier0/mem.h"
#include "utlvector.h"
#include "utlbuffer.h"
#include "utlhash.h"
#include <vstdlib/vstrtools.h>

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

//////// VPROF? //////////////////
// For an example of how to mark up this file with VPROF nodes, see 
// changelist 702984. However, be aware that calls to FindKey and Init
// may occur outside of Vprof's usual hierarchy, which can cause strange
// duplicate KeyValues::FindKey nodes at the root level and other 
// confusing effects.
//////////////////////////////////

static char * s_LastFileLoadingFrom = "unknown"; // just needed for error messages

// Statics for the growable string table
int (*KeyValues::s_pfGetSymbolForString)( const char *name, bool bCreate ) = &KeyValues::GetSymbolForStringClassic;
const char *(*KeyValues::s_pfGetStringForSymbol)( int symbol ) = &KeyValues::GetStringForSymbolClassic;
CKeyValuesGrowableStringTable *KeyValues::s_pGrowableStringTable = NULL;

#define KEYVALUES_TOKEN_SIZE	1024
static char s_pTokenBuf[KEYVALUES_TOKEN_SIZE];

#define INTERNALWRITE( pData, len ) InternalWrite( filesystem, f, pBuf, pData, len )

#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( uint16 )x2) << 8 ) | (uint8)(x1))
#define SPLIT_3_BYTES_INTO_1_AND_2( x1, x2, x3 ) do { x1 = (uint8)(x3); x2 = (uint16)( (x3) >> 8 ); } while( 0 )

CExpressionEvaluator g_ExpressionEvaluator;

// a simple class to keep track of a stack of valid parsed symbols
const int MAX_ERROR_STACK = 64;
class CKeyValuesErrorStack
{
public:
	CKeyValuesErrorStack() : m_pFilename("NULL"), m_errorIndex(0), m_maxErrorIndex(0) {}

	void SetFilename( const char *pFilename )
	{
		m_pFilename = pFilename;
		m_maxErrorIndex = 0;
	}

	// entering a new keyvalues block, save state for errors
	// Not save symbols instead of pointers because the pointers can move!
	int Push( int symName )
	{
		if ( m_errorIndex < MAX_ERROR_STACK )
		{
			m_errorStack[m_errorIndex] = symName;
		}
		m_errorIndex++;
		m_maxErrorIndex = MAX( m_maxErrorIndex, (m_errorIndex-1) );
		return m_errorIndex-1;
	}

	// exiting block, error isn't in this block, remove.
	void Pop()
	{
		m_errorIndex--;
		Assert(m_errorIndex>=0);
	}

	// Allows you to keep the same stack level, but change the name as you parse peers
	void Reset( int stackLevel, int symName )
	{
		Assert( stackLevel >= 0 && stackLevel < m_errorIndex );
		m_errorStack[stackLevel] = symName;
	}

	// Hit an error, report it and the parsing stack for context
	void ReportError( const char *pError )
	{
		Warning( "KeyValues Error: %s in file %s\n", pError, m_pFilename );
		for ( int i = 0; i < m_maxErrorIndex; i++ )
		{
			if ( m_errorStack[i] != INVALID_KEY_SYMBOL )
			{
				if ( i < m_errorIndex )
				{
					Warning( "%s, ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) );
				}
				else
				{
					Warning( "(*%s*), ", KeyValuesSystem()->GetStringForSymbol(m_errorStack[i]) );
				}
			}
		}
		Warning( "\n" );
	}

private:
	int		m_errorStack[MAX_ERROR_STACK];
	const char *m_pFilename;
	int		m_errorIndex;
	int		m_maxErrorIndex;
} g_KeyValuesErrorStack;


// a simple helper that creates stack entries as it goes in & out of scope
class CKeyErrorContext
{
public:
	~CKeyErrorContext()
	{
		g_KeyValuesErrorStack.Pop();
	}
	explicit CKeyErrorContext( int symName )
	{
		Init( symName );
	}
	void Reset( int symName )
	{
		g_KeyValuesErrorStack.Reset( m_stackLevel, symName );
	}
private:
	void Init( int symName )
	{
		m_stackLevel = g_KeyValuesErrorStack.Push( symName );
	}

	int m_stackLevel;
};

// Uncomment this line to hit the ~CLeakTrack assert to see what's looking like it's leaking
// #define LEAKTRACK

#ifdef LEAKTRACK

class CLeakTrack
{
public:
	CLeakTrack()
	{
	}
	~CLeakTrack()
	{
		if ( keys.Count() != 0 )
		{
			Assert( 0 );
		}
	}

	struct kve
	{
		KeyValues *kv;
		char		name[ 256 ];
	};

	void AddKv( KeyValues *kv, char const *name )
	{
		kve k;
		V_strncpy( k.name, name ? name : "NULL", sizeof( k.name ) );
		k.kv = kv;

		keys.AddToTail( k );
	}

	void RemoveKv( KeyValues *kv )
	{
		int c = keys.Count();
		for ( int i = 0; i < c; i++ )
		{
			if ( keys[i].kv == kv )
			{
				keys.Remove( i );
				break;
			}
		}
	}

	CUtlVector< kve > keys;
};

static CLeakTrack track;

#define TRACK_KV_ADD( ptr, name )	track.AddKv( ptr, name )
#define TRACK_KV_REMOVE( ptr )		track.RemoveKv( ptr )

#else

#define TRACK_KV_ADD( ptr, name ) 
#define TRACK_KV_REMOVE( ptr )	

#endif


//-----------------------------------------------------------------------------
// Purpose: An arbitrarily growable string table for KeyValues key names. 
//	See the comment in the header for more info.
//-----------------------------------------------------------------------------
class CKeyValuesGrowableStringTable
{
public: 
	// Constructor
	CKeyValuesGrowableStringTable() :
	  m_vecStrings( 0, 512 * 1024 ),
	  m_hashLookup( 2048, 0, 0, m_Functor, m_Functor )
	{
		m_vecStrings.AddToTail( '\0' );
	}

	// Translates a string to an index
	int GetSymbolForString( const char *name, bool bCreate = true )
	{
		AUTO_LOCK( m_mutex );

		// Put the current details into our hash functor
		m_Functor.SetCurString( name );
		m_Functor.SetCurStringBase( (const char *)m_vecStrings.Base() );

		if ( bCreate )
		{
			bool bInserted = false;
			UtlHashHandle_t hElement = m_hashLookup.Insert( -1, &bInserted );
			if ( bInserted )
			{
				int iIndex = m_vecStrings.AddMultipleToTail( V_strlen( name ) + 1, name );
				m_hashLookup[ hElement ] = iIndex;
			}

			return m_hashLookup[ hElement ];
		}
		else
		{
			UtlHashHandle_t hElement = m_hashLookup.Find( -1 );
			if ( m_hashLookup.IsValidHandle( hElement ) )
				return m_hashLookup[ hElement ];
			else
				return -1;
		}
	}

	// Translates an index back to a string
	const char *GetStringForSymbol( int symbol )
	{
		return (const char *)m_vecStrings.Base() + symbol;
	}

private:
	
	// A class plugged into CUtlHash that allows us to change the behavior of the table
	// and store only the index in the table.
	class CLookupFunctor
	{
	public:
		CLookupFunctor() : m_pchCurString( NULL ), m_pchCurBase( NULL ) {}

		// Sets what we are currently inserting or looking for.
		void SetCurString( const char *pchCurString ) { m_pchCurString = pchCurString; }
		void SetCurStringBase( const char *pchCurBase ) { m_pchCurBase = pchCurBase; }

		// The compare function.
		bool operator()( int nLhs, int nRhs ) const
		{
			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 ) );
		}

		// The hash function.
		unsigned int operator()( int nItem ) const
		{
			return HashStringCaseless( m_pchCurString );
		}

	private:
		const char *m_pchCurString;
		const char *m_pchCurBase;
	};

	CThreadFastMutex m_mutex;
	CLookupFunctor	m_Functor;
	CUtlHash<int, CLookupFunctor &, CLookupFunctor &> m_hashLookup;
	CUtlVector<char> m_vecStrings;
};


//-----------------------------------------------------------------------------
// Purpose: Sets whether the KeyValues system should use an arbitrarily growable
//	string table. See the comment in the header for more info.
//-----------------------------------------------------------------------------
void KeyValues::SetUseGrowableStringTable( bool bUseGrowableTable )
{
	if ( bUseGrowableTable )
	{
		s_pfGetStringForSymbol = &(KeyValues::GetStringForSymbolGrowable);
		s_pfGetSymbolForString = &(KeyValues::GetSymbolForStringGrowable);

		if ( NULL == s_pGrowableStringTable )
		{
			s_pGrowableStringTable = new CKeyValuesGrowableStringTable;
		}
	}
	else
	{
		s_pfGetStringForSymbol = &(KeyValues::GetStringForSymbolClassic);
		s_pfGetSymbolForString = &(KeyValues::GetSymbolForStringClassic);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Bodys of the function pointers used for interacting with the key
//	name string table
//-----------------------------------------------------------------------------
int KeyValues::GetSymbolForStringClassic( const char *name, bool bCreate )
{
	return KeyValuesSystem()->GetSymbolForString( name, bCreate );
}

const char *KeyValues::GetStringForSymbolClassic( int symbol )
{
	return KeyValuesSystem()->GetStringForSymbol( symbol );
}

int KeyValues::GetSymbolForStringGrowable( const char *name, bool bCreate )
{
	return s_pGrowableStringTable->GetSymbolForString( name, bCreate );
}

const char *KeyValues::GetStringForSymbolGrowable( int symbol )
{
	return s_pGrowableStringTable->GetStringForSymbol( symbol );
}



//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
KeyValues::KeyValues( const char *setName )
{
	TRACK_KV_ADD( this, setName );

	Init();
	SetName ( setName );
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
KeyValues::KeyValues( const char *setName, const char *firstKey, const char *firstValue )
{
	TRACK_KV_ADD( this, setName );

	Init();
	SetName( setName );
	SetString( firstKey, firstValue );
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
KeyValues::KeyValues( const char *setName, const char *firstKey, const wchar_t *firstValue )
{
	TRACK_KV_ADD( this, setName );

	Init();
	SetName( setName );
	SetWString( firstKey, firstValue );
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
KeyValues::KeyValues( const char *setName, const char *firstKey, int firstValue )
{
	TRACK_KV_ADD( this, setName );

	Init();
	SetName( setName );
	SetInt( firstKey, firstValue );
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
KeyValues::KeyValues( const char *setName, const char *firstKey, const char *firstValue, const char *secondKey, const char *secondValue )
{
	TRACK_KV_ADD( this, setName );

	Init();
	SetName( setName );
	SetString( firstKey, firstValue );
	SetString( secondKey, secondValue );
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
KeyValues::KeyValues( const char *setName, const char *firstKey, int firstValue, const char *secondKey, int secondValue )
{
	TRACK_KV_ADD( this, setName );

	Init();
	SetName( setName );
	SetInt( firstKey, firstValue );
	SetInt( secondKey, secondValue );
}

//-----------------------------------------------------------------------------
// Purpose: Initialize member variables
//-----------------------------------------------------------------------------
void KeyValues::Init()
{
	m_iKeyName = 0;
	m_iKeyNameCaseSensitive1 = 0;
	m_iKeyNameCaseSensitive2 = 0;
	m_iDataType = TYPE_NONE;

	m_pSub = NULL;
	m_pPeer = NULL;
	m_pChain = NULL;

	m_sValue = NULL;
	m_wsValue = NULL;
	m_pValue = NULL;
	
	m_bHasEscapeSequences = 0;
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
KeyValues::~KeyValues()
{
	TRACK_KV_REMOVE( this );

	RemoveEverything();
}

//-----------------------------------------------------------------------------
// Purpose: remove everything
//-----------------------------------------------------------------------------
void KeyValues::RemoveEverything()
{
	KeyValues *dat;
	KeyValues *datNext = NULL;
	for ( dat = m_pSub; dat != NULL; dat = datNext )
	{
		datNext = dat->m_pPeer;
		dat->m_pPeer = NULL;
		delete dat;
	}

	for ( dat = m_pPeer; dat && dat != this; dat = datNext )
	{
		datNext = dat->m_pPeer;
		dat->m_pPeer = NULL;
		delete dat;
	}

	delete [] m_sValue;
	m_sValue = NULL;
	delete [] m_wsValue;
	m_wsValue = NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *f - 
//-----------------------------------------------------------------------------

void KeyValues::RecursiveSaveToFile( CUtlBuffer& buf, int indentLevel )
{
	RecursiveSaveToFile( NULL, FILESYSTEM_INVALID_HANDLE, &buf, indentLevel );
}

//-----------------------------------------------------------------------------
// Adds a chain... if we don't find stuff in this keyvalue, we'll look
// in the one we're chained to.
//-----------------------------------------------------------------------------

void KeyValues::ChainKeyValue( KeyValues* pChain )
{
	m_pChain = pChain;
}

//-----------------------------------------------------------------------------
// Purpose: Get the name of the current key section
//-----------------------------------------------------------------------------
const char *KeyValues::GetName( void ) const
{
	return this ? KeyValuesSystem()->GetStringForSymbol( MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) ) : "";
}

//-----------------------------------------------------------------------------
// Purpose: Get the symbol name of the current key section
//-----------------------------------------------------------------------------
int KeyValues::GetNameSymbol() const
{
	return this ? m_iKeyName : INVALID_KEY_SYMBOL;
}

int KeyValues::GetNameSymbolCaseSensitive() const
{
	return this ? MAKE_3_BYTES_FROM_1_AND_2( m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2 ) : INVALID_KEY_SYMBOL;
}


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

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

	// eating white spaces and remarks loop
	while ( true )
	{
		buf.EatWhiteSpace();
		if ( !buf.IsValid() )
			return NULL;	// file ends after reading whitespaces

		// stop if it's not a comment; a new token starts here
		if ( !buf.EatCPPComment() )
			break;
	}

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

	// read quoted strings specially
	if ( *c == '\"' )
	{
		wasQuoted = true;
		buf.GetDelimitedString( m_bHasEscapeSequences ? GetCStringCharConversion() : GetNoEscCharConversion(), 
			s_pTokenBuf, KEYVALUES_TOKEN_SIZE );
		return s_pTokenBuf;
	}

	if ( *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 );
		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 ) )
	{
		// end of file
		if ( *c == 0 )
			break;

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

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

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

		// conditionals need to get tokenized as delimited by []
		// othwerwise break on whitespace
		if ( V_isspace(*c) && !bConditionalStart )
			break;

		if (nCount < (KEYVALUES_TOKEN_SIZE-1) )
		{
			s_pTokenBuf[nCount++] = *c;	// add char to buffer
		}
		else if ( !bReportedError )
		{
			bReportedError = true;
			g_KeyValuesErrorStack.ReportError(" ReadToken overflow" );
		}

		buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 1 );
	}
	s_pTokenBuf[ nCount ] = 0;
	return s_pTokenBuf;
}
#pragma warning (default:4706)

	

//-----------------------------------------------------------------------------
// Purpose: if parser should translate escape sequences ( /n, /t etc), set to true
//-----------------------------------------------------------------------------
void KeyValues::UsesEscapeSequences(bool state)
{
	m_bHasEscapeSequences = state;
}


//-----------------------------------------------------------------------------
// Purpose: Load keyValues from disk
//-----------------------------------------------------------------------------
bool KeyValues::LoadFromFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID, GetSymbolProc_t pfnEvaluateSymbolProc )
{
#if defined( _WIN32 )
	Assert( IsGameConsole() || ( _heapchk() == _HEAPOK ) );
#endif
	FileHandle_t f = filesystem->Open(resourceName, "rb", pathID);
	if ( !f )
		return false;

	s_LastFileLoadingFrom = (char*)resourceName;

	// load file into a null-terminated buffer
	int fileSize = filesystem->Size( f );
	unsigned bufSize = ((IFileSystem *)filesystem)->GetOptimalReadSize( f, fileSize + 2 );

	char *buffer = (char*)((IFileSystem *)filesystem)->AllocOptimalReadBuffer( f, bufSize );
	Assert( buffer );
	
	// read into local buffer
	bool bRetOK = ( ((IFileSystem *)filesystem)->ReadEx( buffer, bufSize, fileSize, f ) != 0 );

	filesystem->Close( f );	// close file after reading

	if ( bRetOK )
	{
		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 = V_strlen( buffer );
			utlBuffer.SetExternalBuffer( buffer, bufSize, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
		}

		bRetOK = LoadFromBuffer( resourceName, utlBuffer, filesystem, pathID, pfnEvaluateSymbolProc );
	}

	((IFileSystem *)filesystem)->FreeOptimalReadBuffer( buffer );

	return bRetOK;
}

//-----------------------------------------------------------------------------
// Purpose: Save the keyvalues to disk
//			Creates the path to the file if it doesn't exist 
//-----------------------------------------------------------------------------
bool KeyValues::SaveToFile( IBaseFileSystem *filesystem, const char *resourceName, const char *pathID )
{
	// create a write file
	FileHandle_t f = filesystem->Open(resourceName, "wb", pathID);

	if ( f == FILESYSTEM_INVALID_HANDLE )
	{
		DevMsg( "KeyValues::SaveToFile: couldn't open file \"%s\" in path \"%s\".\n", 
			resourceName?resourceName:"NULL", pathID?pathID:"NULL" );
		return false;
	}

	RecursiveSaveToFile(filesystem, f, NULL, 0);
	filesystem->Close(f);

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Write out a set of indenting
//-----------------------------------------------------------------------------
void KeyValues::WriteIndents( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel )
{
	for ( int i = 0; i < indentLevel; i++ )
	{
		INTERNALWRITE( "\t", 1 );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Write out a string where we convert the double quotes to backslash double quote
//-----------------------------------------------------------------------------
void KeyValues::WriteConvertedString( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const char *pszString )
{
	// handle double quote chars within the string
	// the worst possible case is that the whole string is quotes
	int len = V_strlen(pszString);
	char *convertedString = (char *) alloca ((len + 1)  * sizeof(char) * 2);
	int j=0;
	for (int i=0; i <= len; i++)
	{
		if (pszString[i] == '\"')
		{
			convertedString[j] = '\\';
			j++;
		}
		else if ( m_bHasEscapeSequences && pszString[i] == '\\' )
		{
			convertedString[j] = '\\';
			j++;
		}
		convertedString[j] = pszString[i];
		j++;
	}		

	INTERNALWRITE(convertedString, strlen(convertedString));
}


void KeyValues::InternalWrite( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, const void *pData, int len )
{
	if ( filesystem )
	{
		filesystem->Write( pData, len, f );
	}

	if ( pBuf )
	{
		pBuf->Put( pData, len );
	}
} 


//-----------------------------------------------------------------------------
// Purpose: Save keyvalues from disk, if subkey values are detected, calls
//			itself to save those
//-----------------------------------------------------------------------------
void KeyValues::RecursiveSaveToFile( IBaseFileSystem *filesystem, FileHandle_t f, CUtlBuffer *pBuf, int indentLevel )
{
	// write header
	WriteIndents( filesystem, f, pBuf, indentLevel );
	INTERNALWRITE("\"", 1);
	WriteConvertedString(filesystem, f, pBuf, GetName());	
	INTERNALWRITE("\"\n", 2);
	WriteIndents( filesystem, f, pBuf, indentLevel );
	INTERNALWRITE("{\n", 2);

	// loop through all our keys writing them to disk
	for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer )
	{
		if ( dat->m_pSub )
		{
			dat->RecursiveSaveToFile( filesystem, f, pBuf, indentLevel + 1 );
		}
		else
		{
			// only write non-empty keys

			switch (dat->m_iDataType)
			{
			case TYPE_STRING:
				{
					if (dat->m_sValue && *(dat->m_sValue))
					{
						WriteIndents(filesystem, f, pBuf, indentLevel + 1);
						INTERNALWRITE("\"", 1);
						WriteConvertedString(filesystem, f, pBuf, dat->GetName());	
						INTERNALWRITE("\"\t\t\"", 4);

						WriteConvertedString(filesystem, f, pBuf, dat->m_sValue);	

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

							WriteConvertedString(filesystem, f, pBuf, buf);

							INTERNALWRITE("\"\n", 2);
						}
					}
					break;
				}

			case TYPE_INT:
				{
					WriteIndents(filesystem, f, pBuf, indentLevel + 1);
					INTERNALWRITE("\"", 1);
					INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName()));
					INTERNALWRITE("\"\t\t\"", 4);

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

					INTERNALWRITE(buf, V_strlen(buf));
					INTERNALWRITE("\"\n", 2);
					break;
				}

			case TYPE_UINT64:
				{
					WriteIndents(filesystem, f, pBuf, indentLevel + 1);
					INTERNALWRITE("\"", 1);
					INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName()));
					INTERNALWRITE("\"\t\t\"", 4);

					char buf[32];
					// write "0x" + 16 char 0-padded hex encoded 64 bit value
					V_snprintf( buf, sizeof( buf ), "0x%016llX", *( (uint64 *)dat->m_sValue ) );

					INTERNALWRITE(buf, V_strlen(buf));
					INTERNALWRITE("\"\n", 2);
					break;
				}

			case TYPE_FLOAT:
				{
					WriteIndents(filesystem, f, pBuf, indentLevel + 1);
					INTERNALWRITE("\"", 1);
					INTERNALWRITE(dat->GetName(), V_strlen(dat->GetName()));
					INTERNALWRITE("\"\t\t\"", 4);

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

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

			default:
				break;
			}
		}
	}

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

//-----------------------------------------------------------------------------
// Purpose: looks up a key by symbol name
//-----------------------------------------------------------------------------
KeyValues *KeyValues::FindKey(int keySymbol) const
{
	for (KeyValues *dat = this ? m_pSub : NULL; dat != NULL; dat = dat->m_pPeer)
	{
		if ( dat->m_iKeyName == (uint32) keySymbol )
			return dat;
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Find a keyValue, create it if it is not found.
//			Set bCreate to true to create the key if it doesn't already exist 
//			(which ensures a valid pointer will be returned)
//-----------------------------------------------------------------------------
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;

	// look for '/' characters deliminating sub fields
	char szBuf[256];
	const char *subStr = strchr(keyName, '/');
	const char *searchStr = keyName;

	// pull out the substring if it exists
	if (subStr)
	{
		int size = subStr - keyName;
		V_memcpy( szBuf, keyName, size );
		szBuf[size] = 0;
		searchStr = szBuf;
	}

	// lookup the symbol for the search string,
	// we do not need the case-sensitive symbol at this time
	// because if the key is found, then it will be found by case-insensitive lookup
	// if the key is not found and needs to be created we will pass the actual searchStr
	// and have the new KeyValues constructor get/create the case-sensitive symbol
	HKeySymbol iSearchStr = KeyValuesSystem()->GetSymbolForString( searchStr, bCreate );
	if ( iSearchStr == INVALID_KEY_SYMBOL )
	{
		// not found, couldn't possibly be in key value list
		return NULL;
	}

	KeyValues *lastItem = NULL;
	KeyValues *dat;
	// find the searchStr in the current peer list
	for (dat = m_pSub; dat != NULL; dat = dat->m_pPeer)
	{
		lastItem = dat;	// record the last item looked at (for if we need to append to the end of the list)

		// symbol compare
		if ( dat->m_iKeyName == ( uint32 ) iSearchStr )
		{
			break;
		}
	}

	if ( !dat && m_pChain )
	{
		dat = m_pChain->FindKey(keyName, false);
	}

	// make sure a key was found
	if (!dat)
	{
		if (bCreate)
		{
			// we need to create a new key
			dat = new KeyValues( searchStr );
//			Assert(dat != NULL);

			// insert new key at end of list
			if (lastItem)
			{
				lastItem->m_pPeer = dat;
			}
			else
			{
				m_pSub = dat;
			}
			dat->m_pPeer = NULL;

			// a key graduates to be a submsg as soon as it's m_pSub is set
			// this should be the only place m_pSub is set
			m_iDataType = TYPE_NONE;
		}
		else
		{
			return NULL;
		}
	}
	
	// if we've still got a subStr we need to keep looking deeper in the tree
	if ( subStr )
	{
		// recursively chain down through the paths in the string
		return dat->FindKey(subStr + 1, bCreate);
	}

	return dat;
}

//-----------------------------------------------------------------------------
// Purpose: Create a new key, with an autogenerated name.  
//			Name is guaranteed to be an integer, of value 1 higher than the highest 
//			other integer key name
//-----------------------------------------------------------------------------
KeyValues *KeyValues::CreateNewKey()
{
	int newID = 1;

	// search for any key with higher values
	for (KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer)
	{
		// case-insensitive string compare
		int val = atoi(dat->GetName());
		if (newID <= val)
		{
			newID = val + 1;
		}
	}

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

	return CreateKey( buf );
}


//-----------------------------------------------------------------------------
// Create a key
//-----------------------------------------------------------------------------
KeyValues* KeyValues::CreateKey( const char *keyName )
{
	// key wasn't found so just create a new one
	KeyValues* dat = new KeyValues( keyName );

	dat->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // use same format as parent does
	
	// add into subkey list
	AddSubKey( dat );

	return dat;
}


//-----------------------------------------------------------------------------
// Adds a subkey. Make sure the subkey isn't a child of some other keyvalues
//-----------------------------------------------------------------------------
void KeyValues::AddSubKey( KeyValues *pSubkey )
{
	// Make sure the subkey isn't a child of some other keyvalues
	Assert( pSubkey->m_pPeer == NULL );

	// add into subkey list
	if ( m_pSub == NULL )
	{
		m_pSub = pSubkey;
	}
	else
	{
		KeyValues *pTempDat = m_pSub;
		while ( pTempDat->GetNextKey() != NULL )
		{
			pTempDat = pTempDat->GetNextKey();
		}

		pTempDat->SetNextKey( pSubkey );
	}
}


	
//-----------------------------------------------------------------------------
// Purpose: Remove a subkey from the list
//-----------------------------------------------------------------------------
void KeyValues::RemoveSubKey(KeyValues *subKey)
{
	if (!subKey)
		return;

	// check the list pointer
	if (m_pSub == subKey)
	{
		m_pSub = subKey->m_pPeer;
	}
	else
	{
		// look through the list
		KeyValues *kv = m_pSub;
		while (kv->m_pPeer)
		{
			if (kv->m_pPeer == subKey)
			{
				kv->m_pPeer = subKey->m_pPeer;
				break;
			}
			
			kv = kv->m_pPeer;
		}
	}

	subKey->m_pPeer = NULL;
}

void KeyValues::InsertSubKey( int nIndex, KeyValues *pSubKey )
{
	// Sub key must be valid and not part of another chain
	Assert( pSubKey && pSubKey->m_pPeer == NULL );

	if ( nIndex == 0 )
	{
		pSubKey->m_pPeer = m_pSub;
		m_pSub = pSubKey;
		return;
	}
	else
	{
		int nCurrentIndex = 0;
		for ( KeyValues *pIter = GetFirstSubKey(); pIter != NULL; pIter = pIter->GetNextKey() )
		{
			++ nCurrentIndex;
			if ( nCurrentIndex == nIndex)
			{
				pSubKey->m_pPeer = pIter->m_pPeer;
				pIter->m_pPeer = pSubKey;
				return;
			}
		}
		// Index is out of range if we get here
		Assert( 0 );
		return;
	}
}

bool KeyValues::ContainsSubKey( KeyValues *pSubKey )
{
	for ( KeyValues *pIter = GetFirstSubKey(); pIter != NULL; pIter = pIter->GetNextKey() )
	{
		if ( pSubKey == pIter )
		{
			return true;
		}
	}
	return false;
}

void KeyValues::SwapSubKey( KeyValues *pExistingSubkey, KeyValues *pNewSubKey )
{
	Assert( pExistingSubkey != NULL && pNewSubKey != NULL );

	// Make sure the new sub key isn't a child of some other keyvalues
	Assert( pNewSubKey->m_pPeer == NULL );

	// Check the list pointer
	if ( m_pSub == pExistingSubkey )
	{
		pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer;
		pExistingSubkey->m_pPeer = NULL;
		m_pSub = pNewSubKey;
	}
	else
	{
		// Look through the list
		KeyValues *kv = m_pSub;
		while ( kv->m_pPeer )
		{
			if ( kv->m_pPeer == pExistingSubkey )
			{
				pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer;
				pExistingSubkey->m_pPeer = NULL;
				kv->m_pPeer = pNewSubKey;
				break;
			}

			kv = kv->m_pPeer;
		}
		// Existing sub key should always be found, otherwise it's a bug in the calling code.
		Assert( kv->m_pPeer != NULL );
	}
}

void KeyValues::ElideSubKey( KeyValues *pSubKey )
{
	// This pointer's "next" pointer needs to be fixed up when we elide the key
	KeyValues **ppPointerToFix = &m_pSub;
	for ( KeyValues *pKeyIter = m_pSub; pKeyIter != NULL; ppPointerToFix = &pKeyIter->m_pPeer, pKeyIter = pKeyIter->GetNextKey() )
	{
		if ( pKeyIter == pSubKey )
		{
			if ( pSubKey->m_pSub == NULL )
			{
				// No children, simply remove the key
				*ppPointerToFix = pSubKey->m_pPeer;
				pSubKey->deleteThis();
			}
			else
			{
				*ppPointerToFix = pSubKey->m_pSub;
				// Attach the remainder of this chain to the last child of pSubKey
				KeyValues *pChildIter = pSubKey->m_pSub;
				while ( pChildIter->m_pPeer != NULL )
				{
					pChildIter = pChildIter->m_pPeer;
				}
				// Now points to the last child of pSubKey
				pChildIter->m_pPeer = pSubKey->m_pPeer;
				// Detach the node to be elided
				pSubKey->m_pSub = NULL;
				pSubKey->m_pPeer = NULL;
				pSubKey->deleteThis();
			}
			return;		
		}
	}
	// Key not found; that's caller error.
	Assert( 0 );
}


//-----------------------------------------------------------------------------
// Purpose: Return the first subkey in the list
//-----------------------------------------------------------------------------
KeyValues *KeyValues::GetFirstSubKey()
{
	return this ? m_pSub : NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Return the next subkey
//-----------------------------------------------------------------------------
KeyValues *KeyValues::GetNextKey()
{
	return this ? m_pPeer : NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Sets this key's peer to the KeyValues passed in
//-----------------------------------------------------------------------------
void KeyValues::SetNextKey( KeyValues *pDat )
{
	m_pPeer = pDat;
}


KeyValues* KeyValues::GetFirstTrueSubKey()
{
	KeyValues *pRet = this ? m_pSub : NULL;
	while ( pRet && pRet->m_iDataType != TYPE_NONE )
		pRet = pRet->m_pPeer;

	return pRet;
}

KeyValues* KeyValues::GetNextTrueSubKey()
{
	KeyValues *pRet = this ? m_pPeer : NULL;
	while ( pRet && pRet->m_iDataType != TYPE_NONE )
		pRet = pRet->m_pPeer;

	return pRet;
}

KeyValues* KeyValues::GetFirstValue()
{
	KeyValues *pRet = this ? m_pSub : NULL;
	while ( pRet && pRet->m_iDataType == TYPE_NONE )
		pRet = pRet->m_pPeer;

	return pRet;
}

KeyValues* KeyValues::GetNextValue()
{
	KeyValues *pRet = this ? m_pPeer : NULL;
	while ( pRet && pRet->m_iDataType == TYPE_NONE )
		pRet = pRet->m_pPeer;

	return pRet;
}


//-----------------------------------------------------------------------------
// Purpose: Get the integer value of a keyName. Default value is returned
//			if the keyName can't be found.
//-----------------------------------------------------------------------------
int KeyValues::GetInt( const char *keyName, int defaultValue )
{
	KeyValues *dat = FindKey( keyName, false );
	if ( dat )
	{
		switch ( dat->m_iDataType )
		{
		case TYPE_STRING:
			return atoi(dat->m_sValue);
		case TYPE_WSTRING:
			return _wtoi(dat->m_wsValue);
		case TYPE_FLOAT:
			return (int)dat->m_flValue;
		case TYPE_UINT64:
			// can't convert, since it would lose data
			Assert(0);
			return 0;
		case TYPE_INT:
		case TYPE_PTR:
		default:
			return dat->m_iValue;
		};
	}
	return defaultValue;
}

//-----------------------------------------------------------------------------
// Purpose: Get the integer value of a keyName. Default value is returned
//			if the keyName can't be found.
//-----------------------------------------------------------------------------
uint64 KeyValues::GetUint64( const char *keyName, uint64 defaultValue )
{
	KeyValues *dat = FindKey( keyName, false );
	if ( dat )
	{
		switch ( dat->m_iDataType )
		{
		case TYPE_STRING:
			{
				uint64 uiResult = 0ull;
				sscanf( dat->m_sValue, "%lld", &uiResult );
				return uiResult;
			}
		case TYPE_WSTRING:
			{
				uint64 uiResult = 0ull;
				swscanf( dat->m_wsValue, L"%lld", &uiResult );
				return uiResult;
			}
		case TYPE_FLOAT:
			return (int)dat->m_flValue;
		case TYPE_UINT64:
			return *((uint64 *)dat->m_sValue);
		case TYPE_INT:
		case TYPE_PTR:
		default:
			return dat->m_iValue;
		};
	}
	return defaultValue;
}

//-----------------------------------------------------------------------------
// Purpose: Get the pointer value of a keyName. Default value is returned
//			if the keyName can't be found.
//-----------------------------------------------------------------------------
void *KeyValues::GetPtr( const char *keyName, void *defaultValue )
{
	KeyValues *dat = FindKey( keyName, false );
	if ( dat )
	{
		switch ( dat->m_iDataType )
		{
		case TYPE_PTR:
			return dat->m_pValue;

		case TYPE_WSTRING:
		case TYPE_STRING:
		case TYPE_FLOAT:
		case TYPE_INT:
		case TYPE_UINT64:
		default:
			return NULL;
		};
	}
	return defaultValue;
}

//-----------------------------------------------------------------------------
// Purpose: Get the float value of a keyName. Default value is returned
//			if the keyName can't be found.
//-----------------------------------------------------------------------------
float KeyValues::GetFloat( const char *keyName, float defaultValue )
{
	KeyValues *dat = FindKey( keyName, false );
	if ( dat )
	{
		switch ( dat->m_iDataType )
		{
		case TYPE_STRING:
			return (float)atof(dat->m_sValue);
		case TYPE_WSTRING:
#ifdef WIN32
			return (float) _wtof(dat->m_wsValue);		// no wtof
#else
			return (float) wcstof( dat->m_wsValue, (wchar_t **)NULL ); 
#endif
			case TYPE_FLOAT:
			return dat->m_flValue;
		case TYPE_INT:
			return (float)dat->m_iValue;
		case TYPE_UINT64:
			return (float)(*((uint64 *)dat->m_sValue));
		case TYPE_PTR:
		default:
			return 0.0f;
		};
	}
	return defaultValue;
}

//-----------------------------------------------------------------------------
// Purpose: Get the string pointer of a keyName. Default value is returned
//			if the keyName can't be found.
//-----------------------------------------------------------------------------
const char *KeyValues::GetString( const char *keyName, const char *defaultValue )
{
	KeyValues *dat = FindKey( keyName, false );
	if ( dat )
	{
		// convert the data to string form then return it
		char buf[64];
		switch ( dat->m_iDataType )
		{
		case TYPE_FLOAT:
			V_snprintf( buf, sizeof( buf ), "%f", dat->m_flValue );
			SetString( keyName, buf );
			break;
		case TYPE_INT:
		case TYPE_PTR:
			V_snprintf( buf, sizeof( buf ), "%d", dat->m_iValue );
			SetString( keyName, buf );
			break;
		case TYPE_UINT64:
			V_snprintf( buf, sizeof( buf ), "%lld", *((uint64 *)(dat->m_sValue)) );
			SetString( keyName, buf );
			break;

		case TYPE_WSTRING:
		{
			// convert the string to char *, set it for future use, and return it
			char wideBuf[512];
			int result = V_UnicodeToUTF8(dat->m_wsValue, wideBuf, 512);
			if ( result )
			{
				// note: this will copy wideBuf
				SetString( keyName, wideBuf );
			}
			else
			{
				return defaultValue;
			}
			break;
		}
		case TYPE_STRING:
			break;
		default:
			return defaultValue;
		};
		
		return dat->m_sValue;
	}
	return defaultValue;
}


const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaultValue)
{
	KeyValues *dat = FindKey( keyName, false );
	if ( dat )
	{
		wchar_t wbuf[64];
		switch ( dat->m_iDataType )
		{
		case TYPE_FLOAT:
			swprintf(wbuf, V_ARRAYSIZE(wbuf), L"%f", dat->m_flValue);
			SetWString( keyName, wbuf);
			break;
		case TYPE_INT:
		case TYPE_PTR:
			swprintf( wbuf, V_ARRAYSIZE(wbuf), L"%d", dat->m_iValue );
			SetWString( keyName, wbuf );
			break;
		case TYPE_UINT64:
			{
				swprintf( wbuf, V_ARRAYSIZE(wbuf), L"%lld", *((uint64 *)(dat->m_sValue)) );
				SetWString( keyName, wbuf );
			}
			break;

		case TYPE_WSTRING:
			break;
		case TYPE_STRING:
		{
			int bufSize = V_strlen(dat->m_sValue) + 1;
			wchar_t *pWBuf = new wchar_t[ bufSize ];
			int result = V_UTF8ToUnicode(dat->m_sValue, pWBuf, bufSize * sizeof( wchar_t ) );
			if ( result >= 0 ) // may be a zero length string
			{
				SetWString( keyName, pWBuf);
			}
			else
			{
				delete [] pWBuf;
				return defaultValue;
			}
			delete [] pWBuf;
			break;
		}
		default:
			return defaultValue;
		};
		
		return (const wchar_t* )dat->m_wsValue;
	}
	return defaultValue;
}

//-----------------------------------------------------------------------------
// Purpose: Gets a color
//-----------------------------------------------------------------------------
Color KeyValues::GetColor( const char *keyName , const Color& defaultColor )
{
	Color color = defaultColor;
	KeyValues *dat = FindKey( keyName , false );
	if ( dat )
	{
		if ( dat->m_iDataType == TYPE_COLOR )
		{
			color[0] = dat->m_Color[0];
			color[1] = dat->m_Color[1];
			color[2] = dat->m_Color[2];
			color[3] = dat->m_Color[3];
		}
		else if ( dat->m_iDataType == TYPE_FLOAT )
		{
			color[0] = (unsigned char)dat->m_flValue;
		}
		else if ( dat->m_iDataType == TYPE_INT )
		{
			color[0] = (unsigned char)dat->m_iValue;
		}
		else if ( dat->m_iDataType == TYPE_STRING )
		{
			// parse the colors out of the string
			float a, b, c, d;
			sscanf(dat->m_sValue, "%f %f %f %f", &a, &b, &c, &d);
			color[0] = (unsigned char)a;
			color[1] = (unsigned char)b;
			color[2] = (unsigned char)c;
			color[3] = (unsigned char)d;
		}
	}
	return color;
}

//-----------------------------------------------------------------------------
// Purpose: Sets a color
//-----------------------------------------------------------------------------
void KeyValues::SetColor( const char *keyName, Color value)
{
	KeyValues *dat = FindKey( keyName, true );

	if ( dat )
	{
		dat->m_iDataType = TYPE_COLOR;
		dat->m_Color[0] = value[0];
		dat->m_Color[1] = value[1];
		dat->m_Color[2] = value[2];
		dat->m_Color[3] = value[3];
	}
}

void KeyValues::SetStringValue( char const *strValue )
{
	// delete the old value
	delete [] m_sValue;
	// make sure we're not storing the WSTRING  - as we're converting over to STRING
	delete [] m_wsValue;
	m_wsValue = NULL;

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

	// allocate memory for the new value and copy it in
	int len = V_strlen( strValue );
	m_sValue = new char[len + 1];
	V_memcpy( m_sValue, strValue, len+1 );

	m_iDataType = TYPE_STRING;
}

//-----------------------------------------------------------------------------
// Purpose: Set the string value of a keyName. 
//-----------------------------------------------------------------------------
void KeyValues::SetString( const char *keyName, const char *value )
{
	if ( KeyValues *dat = FindKey( keyName, true ) )
	{
		dat->SetStringValue( value );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set the string value of a keyName. 
//-----------------------------------------------------------------------------
void KeyValues::SetWString( const char *keyName, const wchar_t *value )
{
	KeyValues *dat = FindKey( keyName, true );
	if ( dat )
	{
		// delete the old value
		delete [] dat->m_wsValue;
		// make sure we're not storing the STRING  - as we're converting over to WSTRING
		delete [] dat->m_sValue;
		dat->m_sValue = NULL;

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

		// allocate memory for the new value and copy it in
		int len = wcslen( value );
		dat->m_wsValue = new wchar_t[len + 1];
		V_memcpy( dat->m_wsValue, value, (len+1) * sizeof(wchar_t) );

		dat->m_iDataType = TYPE_WSTRING;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set the integer value of a keyName. 
//-----------------------------------------------------------------------------
void KeyValues::SetInt( const char *keyName, int value )
{
	KeyValues *dat = FindKey( keyName, true );

	if ( dat )
	{
		dat->m_iValue = value;
		dat->m_iDataType = TYPE_INT;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set the integer value of a keyName. 
//-----------------------------------------------------------------------------
void KeyValues::SetUint64( const char *keyName, uint64 value )
{
	KeyValues *dat = FindKey( keyName, true );

	if ( dat )
	{
		// 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;

		dat->m_sValue = new char[sizeof(uint64)];
		*((uint64 *)dat->m_sValue) = value;
		dat->m_iDataType = TYPE_UINT64;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set the float value of a keyName. 
//-----------------------------------------------------------------------------
void KeyValues::SetFloat( const char *keyName, float value )
{
	KeyValues *dat = FindKey( keyName, true );

	if ( dat )
	{
		dat->m_flValue = value;
		dat->m_iDataType = TYPE_FLOAT;
	}
}

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

//-----------------------------------------------------------------------------
// Purpose: Set the pointer value of a keyName. 
//-----------------------------------------------------------------------------
void KeyValues::SetPtr( const char *keyName, void *value )
{
	KeyValues *dat = FindKey( keyName, true );

	if ( dat )
	{
		dat->m_pValue = value;
		dat->m_iDataType = TYPE_PTR;
	}
}

void KeyValues::RecursiveCopyKeyValues( KeyValues& src )
{
	// garymcthack - need to check this code for possible buffer overruns.
	
	m_iKeyName = src.m_iKeyName;
	m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1;
	m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2;

	if( !src.m_pSub )
	{
		m_iDataType = src.m_iDataType;
		char buf[256];
		switch( src.m_iDataType )
		{
		case TYPE_NONE:
			break;
		case TYPE_STRING:
			if( src.m_sValue )
			{
				int len = V_strlen(src.m_sValue) + 1;
				m_sValue = new char[len];
				V_strncpy( m_sValue, src.m_sValue, len );
			}
			break;
		case TYPE_INT:
			{
				m_iValue = src.m_iValue;
				V_snprintf( buf,sizeof(buf), "%d", m_iValue );
				int len = V_strlen(buf) + 1;
				m_sValue = new char[len];
				V_strncpy( m_sValue, buf, len  );
			}
			break;
		case TYPE_FLOAT:
			{
				m_flValue = src.m_flValue;
				V_snprintf( buf,sizeof(buf), "%f", m_flValue );
				int len = V_strlen(buf) + 1;
				m_sValue = new char[len];
				V_strncpy( m_sValue, buf, len );
			}
			break;
		case TYPE_PTR:
			{
				m_pValue = src.m_pValue;
			}
			break;
		case TYPE_UINT64:
			{
				m_sValue = new char[sizeof(uint64)];
				V_memcpy( m_sValue, src.m_sValue, sizeof(uint64) );
			}
			break;
		case TYPE_COLOR:
			{
				m_Color[0] = src.m_Color[0];
				m_Color[1] = src.m_Color[1];
				m_Color[2] = src.m_Color[2];
				m_Color[3] = src.m_Color[3];
			}
			break;
			
		default:
			{
				// do nothing . .what the heck is this?
				Assert( 0 );
			}
			break;
		}

	}
#if 0
	KeyValues *pDst = this;
	for ( KeyValues *pSrc = src.m_pSub; pSrc; pSrc = pSrc->m_pPeer )
	{
		if ( pSrc->m_pSub )
		{
			pDst->m_pSub = new KeyValues( pSrc->m_pSub->getName() );
			pDst->m_pSub->RecursiveCopyKeyValues( *pSrc->m_pSub );
		}
		else
		{
			// copy non-empty keys
			if ( pSrc->m_sValue && *(pSrc->m_sValue) )
			{
				pDst->m_pPeer = new KeyValues( 
			}
		}
	}
#endif

	// Handle the immediate child
	if( src.m_pSub )
	{
		m_pSub = new KeyValues( NULL );
		m_pSub->RecursiveCopyKeyValues( *src.m_pSub );
	}

	// Handle the immediate peer
	if( src.m_pPeer )
	{
		m_pPeer = new KeyValues( NULL );
		m_pPeer->RecursiveCopyKeyValues( *src.m_pPeer );
	}
}

KeyValues& KeyValues::operator=( KeyValues& src )
{
	RemoveEverything();
	Init();	// reset all values
	RecursiveCopyKeyValues( src );
	return *this;
}


//-----------------------------------------------------------------------------
// Make a new copy of all subkeys, add them all to the passed-in keyvalues
//-----------------------------------------------------------------------------
void KeyValues::CopySubkeys( KeyValues *pParent ) const
{
	// recursively copy subkeys
	// Also maintain ordering....
	KeyValues *pPrev = NULL;
	for ( KeyValues *sub = m_pSub; sub != NULL; sub = sub->m_pPeer )
	{
		// take a copy of the subkey
		KeyValues *dat = sub->MakeCopy();
		 
		// add into subkey list
		if (pPrev)
		{
			pPrev->m_pPeer = dat;
		}
		else
		{
			pParent->m_pSub = dat;
		}
		dat->m_pPeer = NULL;
		pPrev = dat;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Makes a copy of the whole key-value pair set
//-----------------------------------------------------------------------------
KeyValues *KeyValues::MakeCopy( void ) const
{
	KeyValues *newKeyValue = new KeyValues(GetName());

	// copy data
	newKeyValue->m_iDataType = m_iDataType;
	switch ( m_iDataType )
	{
	case TYPE_STRING:
		{
			if ( m_sValue )
			{
				int len = V_strlen( m_sValue );
				Assert( !newKeyValue->m_sValue );
				newKeyValue->m_sValue = new char[len + 1];
				V_memcpy( newKeyValue->m_sValue, m_sValue, len+1 );
			}
		}
		break;
	case TYPE_WSTRING:
		{
			if ( m_wsValue )
			{
				int len = wcslen( m_wsValue );
				newKeyValue->m_wsValue = new wchar_t[len+1];
				V_memcpy( newKeyValue->m_wsValue, m_wsValue, (len+1)*sizeof(wchar_t));
			}
		}
		break;

	case TYPE_INT:
		newKeyValue->m_iValue = m_iValue;
		break;

	case TYPE_FLOAT:
		newKeyValue->m_flValue = m_flValue;
		break;

	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];
		newKeyValue->m_Color[2] = m_Color[2];
		newKeyValue->m_Color[3] = m_Color[3];
		break;

	case TYPE_UINT64:
		newKeyValue->m_sValue = new char[sizeof(uint64)];
		V_memcpy( newKeyValue->m_sValue, m_sValue, sizeof(uint64) );
		break;
	};

	// recursively copy subkeys
	CopySubkeys( newKeyValue );
	return newKeyValue;
}


//-----------------------------------------------------------------------------
// Purpose: Check if a keyName has no value assigned to it.
//-----------------------------------------------------------------------------
bool KeyValues::IsEmpty(const char *keyName)
{
	KeyValues *dat = FindKey(keyName, false);
	if (!dat)
		return true;

	if (dat->m_iDataType == TYPE_NONE && dat->m_pSub == NULL)
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Clear out all subkeys, and the current value
//-----------------------------------------------------------------------------
void KeyValues::Clear( void )
{
	delete m_pSub;
	m_pSub = NULL;
	m_iDataType = TYPE_NONE;
}

//-----------------------------------------------------------------------------
// Purpose: Get the data type of the value stored in a keyName
//-----------------------------------------------------------------------------
KeyValues::types_t KeyValues::GetDataType(const char *keyName)
{
	KeyValues *dat = FindKey(keyName, false);
	if (dat)
		return (types_t)dat->m_iDataType;

	return TYPE_NONE;
}

//-----------------------------------------------------------------------------
// Purpose: Deletion, ensures object gets deleted from correct heap
//-----------------------------------------------------------------------------
void KeyValues::deleteThis()
{
	delete this;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : includedKeys - 
//-----------------------------------------------------------------------------
void KeyValues::AppendIncludedKeys( CUtlVector< KeyValues * >& includedKeys )
{
	// Append any included keys, too...
	int includeCount = includedKeys.Count();
	int i;
	for ( i = 0; i < includeCount; i++ )
	{
		KeyValues *kv = includedKeys[ i ];
		Assert( kv );

		KeyValues *insertSpot = this;
		while ( insertSpot->GetNextKey() )
		{
			insertSpot = insertSpot->GetNextKey();
		}

		insertSpot->SetNextKey( kv );
	}
}

void KeyValues::ParseIncludedKeys( char const *resourceName, const char *filetoinclude, 
		IBaseFileSystem* pFileSystem, const char *pPathID, CUtlVector< KeyValues * >& includedKeys, GetSymbolProc_t pfnEvaluateSymbolProc )
{
	Assert( resourceName );
	Assert( filetoinclude );
	Assert( pFileSystem );
	
	// Load it...
	if ( !pFileSystem )
	{
		return;
	}

	// Get relative subdirectory
	char fullpath[ 512 ];
	V_strncpy( fullpath, resourceName, sizeof( fullpath ) );

	// Strip off characters back to start or first /
	bool done = false;
	int len = V_strlen( fullpath );
	while ( !done )
	{
		if ( len <= 0 )
		{
			break;
		}
		
		if ( fullpath[ len - 1 ] == '\\' || 
			 fullpath[ len - 1 ] == '/' )
		{
			break;
		}

		// zero it
		fullpath[ len - 1 ] = 0;
		--len;
	}

	// Append included file
	V_strncat( fullpath, filetoinclude, sizeof( fullpath ), COPY_ALL_CHARACTERS );

	KeyValues *newKV = new KeyValues( fullpath );

	// CUtlSymbol save = s_CurrentFileSymbol;	// did that had any use ???

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

	if ( newKV->LoadFromFile( pFileSystem, fullpath, pPathID, pfnEvaluateSymbolProc ) )
	{
		includedKeys.AddToTail( newKV );
	}
	else
	{
		DevMsg( "KeyValues::ParseIncludedKeys: Couldn't load included keyvalue file %s\n", fullpath );
		newKV->deleteThis();
	}

	// s_CurrentFileSymbol = save;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : baseKeys - 
//-----------------------------------------------------------------------------
void KeyValues::MergeBaseKeys( CUtlVector< KeyValues * >& baseKeys )
{
	int includeCount = baseKeys.Count();
	int i;
	for ( i = 0; i < includeCount; i++ )
	{
		KeyValues *kv = baseKeys[ i ];
		Assert( kv );

		RecursiveMergeKeyValues( kv );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : baseKV - keyvalues we're basing ourselves on
//-----------------------------------------------------------------------------
void KeyValues::RecursiveMergeKeyValues( KeyValues *baseKV )
{
	// Merge ourselves
	// we always want to keep our value, so nothing to do here

	// Now merge our children
	for ( KeyValues *baseChild = baseKV->m_pSub; baseChild != NULL; baseChild = baseChild->m_pPeer )
	{
		// for each child in base, see if we have a matching kv

		bool bFoundMatch = false;

		// If we have a child by the same name, merge those keys
		for ( KeyValues *newChild = m_pSub; newChild != NULL; newChild = newChild->m_pPeer )
		{
			if ( !V_strcmp( baseChild->GetName(), newChild->GetName() ) )
			{
				newChild->RecursiveMergeKeyValues( baseChild );
				bFoundMatch = true;
				break;
			}	
		}

		// If not merged, append this key
		if ( !bFoundMatch )
		{
			KeyValues *dat = baseChild->MakeCopy();
			Assert( dat );
			AddSubKey( dat );
		}
	}
}

//-----------------------------------------------------------------------------
// Returns whether a keyvalues conditional expression string evaluates to true or false
//-----------------------------------------------------------------------------
bool KeyValues::EvaluateConditional( const char *pExpressionString, GetSymbolProc_t pfnEvaluateSymbolProc )
{
	// evaluate the infix expression, calling the symbol proc to resolve each symbol's value
	bool bResult = false;
	bool bValid = g_ExpressionEvaluator.Evaluate( bResult, pExpressionString, pfnEvaluateSymbolProc );
	if ( !bValid )
	{
		g_KeyValuesErrorStack.ReportError( "KV Conditional Evaluation Error" );
	}

	return bResult;
}

// prevent two threads from entering this at the same time and trying to share the global error reporting and parse buffers
static CThreadFastMutex g_KVMutex;
//-----------------------------------------------------------------------------
// Read from a buffer...
//-----------------------------------------------------------------------------
bool KeyValues::LoadFromBuffer( char const *resourceName, CUtlBuffer &buf, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc )
{
	AUTO_LOCK_FM( g_KVMutex );

	if ( IsGameConsole() )
	{
		// Let's not crash if the buffer is empty
		unsigned char *pData = buf.Size() > 0 ? (unsigned char *)buf.PeekGet() : NULL;
		if ( pData && (unsigned int)pData[0] == KV_BINARY_POOLED_FORMAT )
		{
			// skip past binary marker
			buf.GetUnsignedChar();
			// get the pool identifier, allows the fs to bind the expected string pool
			unsigned int poolKey = buf.GetUnsignedInt();
	
			RemoveEverything();
			Init();

			return ReadAsBinaryPooledFormat( buf, pFileSystem, poolKey, pfnEvaluateSymbolProc );
		}
	}

	KeyValues *pPreviousKey = NULL;
	KeyValues *pCurrentKey = this;
	CUtlVector< KeyValues * > includedKeys;
	CUtlVector< KeyValues * > baseKeys;
	bool wasQuoted;
	bool wasConditional;
	g_KeyValuesErrorStack.SetFilename( resourceName );	
	do 
	{
		bool bAccepted = true;

		// the first thing must be a key
		const char *s = ReadToken( buf, wasQuoted, wasConditional );
		if ( !buf.IsValid() || !s )
			break;

		if ( !wasQuoted && *s == '\0' )
		{
			// non quoted empty strings stop parsing
			// quoted empty strings are allowed to support unnnamed KV sections
			break;
		}

		if ( !V_stricmp( s, "#include" ) )	// special include macro (not a key name)
		{
			s = ReadToken( buf, wasQuoted, wasConditional );
			// Name of subfile to load is now in s

			if ( !s || *s == 0 )
			{
				g_KeyValuesErrorStack.ReportError("#include is NULL " );
			}
			else
			{
				ParseIncludedKeys( resourceName, s, pFileSystem, pPathID, includedKeys, pfnEvaluateSymbolProc );
			}

			continue;
		}
		else if ( !V_stricmp( s, "#base" ) )
		{
			s = ReadToken( buf, wasQuoted, wasConditional );
			// Name of subfile to load is now in s

			if ( !s || *s == 0 )
			{
				g_KeyValuesErrorStack.ReportError("#base is NULL " );
			}
			else
			{
				ParseIncludedKeys( resourceName, s, pFileSystem, pPathID, baseKeys, pfnEvaluateSymbolProc );
			}

			continue;
		}

		if ( !pCurrentKey )
		{
			pCurrentKey = new KeyValues( s );
			Assert( pCurrentKey );

			pCurrentKey->UsesEscapeSequences( m_bHasEscapeSequences != 0 ); // same format has parent use

			if ( pPreviousKey )
			{
				pPreviousKey->SetNextKey( pCurrentKey );
			}
		}
		else
		{
			pCurrentKey->SetName( s );
		}

		// get the '{'
		s = ReadToken( buf, wasQuoted, wasConditional );

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

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

		if ( s && *s == '{' && !wasQuoted )
		{
			// header is valid so load the file
			pCurrentKey->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc );
		}
		else
		{
			g_KeyValuesErrorStack.ReportError("LoadFromBuffer: missing {" );
		}

		if ( !bAccepted )
		{
			if ( pPreviousKey )
			{
				pPreviousKey->SetNextKey( NULL );
			}
			pCurrentKey->Clear();
		}
		else
		{
			pPreviousKey = pCurrentKey;
			pCurrentKey = NULL;
		}
	} while ( buf.IsValid() );

	AppendIncludedKeys( includedKeys );
	{
		// delete included keys!
		int i;
		for ( i = includedKeys.Count() - 1; i > 0; i-- )
		{
			KeyValues *kv = includedKeys[ i ];
			kv->deleteThis();
		}
	}

	MergeBaseKeys( baseKeys );
	{
		// delete base keys!
		int i;
		for ( i = baseKeys.Count() - 1; i >= 0; i-- )
		{
			KeyValues *kv = baseKeys[ i ];
			kv->deleteThis();
		}
	}

	g_KeyValuesErrorStack.SetFilename( "" );	

	return true;
}


//-----------------------------------------------------------------------------
// Read from a buffer...
//-----------------------------------------------------------------------------
bool KeyValues::LoadFromBuffer( char const *resourceName, const char *pBuffer, IBaseFileSystem* pFileSystem, const char *pPathID, GetSymbolProc_t pfnEvaluateSymbolProc )
{
	if ( !pBuffer )
		return true;

	if ( IsGameConsole() && (unsigned int)((unsigned char *)pBuffer)[0] == KV_BINARY_POOLED_FORMAT )
	{
		// bad, got a binary compiled KV file through an unexpected text path
		// not all paths support binary compiled kv, needs to get fixed
		// need to have caller supply buffer length (strlen not valid), this interface change was never plumbed
		Warning( "ERROR! Binary compiled KV '%s' in an unexpected handler\n", resourceName );
		Assert( 0 );
		return false;
	}

	int nLen = V_strlen( pBuffer );
	CUtlBuffer buf( pBuffer, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
	// Translate Unicode files into UTF-8 before proceeding
	if ( nLen > 2 && (uint8)pBuffer[0] == 0xFF && (uint8)pBuffer[1] == 0xFE )
	{
		int nUTF8Len = V_UnicodeToUTF8( (wchar_t*)(pBuffer+2), NULL, 0 );
		char *pUTF8Buf = new char[nUTF8Len];
		V_UnicodeToUTF8( (wchar_t*)(pBuffer+2), pUTF8Buf, nUTF8Len );
		buf.AssumeMemory( pUTF8Buf, nUTF8Len, nUTF8Len, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
	}
	return LoadFromBuffer( resourceName, buf, pFileSystem, pPathID, pfnEvaluateSymbolProc );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void KeyValues::RecursiveLoadFromBuffer( char const *resourceName, CUtlBuffer &buf, GetSymbolProc_t pfnEvaluateSymbolProc )
{
	CKeyErrorContext errorReport( GetNameSymbolCaseSensitive() );
	bool wasQuoted;
	bool wasConditional;
	// keep this out of the stack until a key is parsed
	CKeyErrorContext errorKey( INVALID_KEY_SYMBOL );
	while ( 1 )
	{
		bool bAccepted = true;

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

		if ( !name )	// EOF stop reading
		{
			g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer:  got EOF instead of keyname" );
			break;
		}

		if ( !*name ) // empty token, maybe "" or EOF
		{
			g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer:  got empty keyname" );
			break;
		}

		if ( *name == '}' && !wasQuoted )	// top level closed, stop reading
			break;

		// Always create the key; note that this could potentially
		// cause some duplication, but that's what we want sometimes
		KeyValues *dat = CreateKey( name );

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

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

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

			// get the real value
			value = ReadToken( buf, wasQuoted, wasConditional );
		}

		if ( !value )
		{
			g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer:  got NULL key" );
			break;
		}
		
		if ( *value == '}' && !wasQuoted )
		{
			g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer:  got } in key" );
			break;
		}

		if ( *value == '{' && !wasQuoted )
		{
			// this isn't a key, it's a section
			errorKey.Reset( INVALID_KEY_SYMBOL );
			// sub value list
			dat->RecursiveLoadFromBuffer( resourceName, buf, pfnEvaluateSymbolProc );
		}
		else 
		{
			if ( wasConditional )
			{
				g_KeyValuesErrorStack.ReportError("RecursiveLoadFromBuffer:  got conditional between key and value" );
				break;
			}
			
			if (dat->m_sValue)
			{
				delete[] dat->m_sValue;
				dat->m_sValue = NULL;
			}

			int len = V_strlen( value );

			// Here, let's determine if we got a float or an int....
			char* pIEnd;	// pos where int scan ended
			char* pFEnd;	// pos where float scan ended
			const char* pSEnd = value + len ; // pos where token ends

			int ival = strtol( value, &pIEnd, 10 );
			float fval = (float)strtod( value, &pFEnd );
			bool bOverflow = ( ival == LONG_MAX || ival == 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
			if ( len > 1 &&  tolower(value[1]) == 'x' )
			{
				fval = 0.0f;
				pFEnd = (char *)value;
			}
#endif
				
			if ( *value == 0 )
			{
				dat->m_iDataType = TYPE_STRING;	
			}
			else if ( ( 18 == len ) && ( value[0] == '0' ) && ( value[1] == 'x' ) )
			{
				// an 18-byte value prefixed with "0x" (followed by 16 hex digits) is an int64 value
				int64 retVal = 0;
				for( int i=2; i < 2 + 16; i++ )
				{
					char digit = value[i];
					if ( digit >= 'a' ) 
						digit -= 'a' - ( '9' + 1 );
					else
						if ( digit >= 'A' )
							digit -= 'A' - ( '9' + 1 );
					retVal = ( retVal * 16 ) + ( digit - '0' );
				}
				dat->m_sValue = new char[sizeof(uint64)];
				*((uint64 *)dat->m_sValue) = retVal;
				dat->m_iDataType = TYPE_UINT64;
			}
			else if ( (pFEnd > pIEnd) && (pFEnd == pSEnd) )
			{
				dat->m_flValue = fval; 
				dat->m_iDataType = TYPE_FLOAT;
			}
			else if (pIEnd == pSEnd && !bOverflow)
			{
				dat->m_iValue = ival; 
				dat->m_iDataType = TYPE_INT;
			}
			else
			{
				dat->m_iDataType = TYPE_STRING;
			}

			if (dat->m_iDataType == TYPE_STRING)
			{
				// copy in the string information
				dat->m_sValue = new char[len+1];
				V_memcpy( dat->m_sValue, value, len+1 );
			}

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

		if ( !bAccepted )
		{
			this->RemoveSubKey( dat );
			dat->deleteThis();
			dat = NULL;
		}
	}
}

// writes KeyValue as binary data to buffer
bool KeyValues::WriteAsBinary( CUtlBuffer &buffer ) const
{
	if ( buffer.IsText() ) // must be a binary buffer
		return false;

	if ( !buffer.IsValid() ) // must be valid, no overflows etc
		return false;

	// Write subkeys:
	
	// loop through all our peers
	for ( const KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer )
	{
		// write type
		buffer.PutUnsignedChar( dat->m_iDataType );

		// write name
		buffer.PutString( dat->GetName() );

		// write type
		switch (dat->m_iDataType)
		{
		case TYPE_NONE:
			{
				dat->m_pSub->WriteAsBinary( buffer );
				break;
			}
		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 ? V_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:
			{
				buffer.PutUnsignedInt( (int)dat->m_pValue );
				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::ReadAsBinary( CUtlBuffer &buffer )
{
	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
	
	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;

		buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 );
		token[KEYVALUES_TOKEN_SIZE-1] = 0;

		dat->SetName( token );
		
		switch ( type )
		{
		case TYPE_NONE:
			{
				dat->m_pSub = new KeyValues("");
				dat->m_pSub->ReadAsBinary( buffer );
				break;
			}
		case TYPE_STRING:
			{
				buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 );
				token[KEYVALUES_TOKEN_SIZE-1] = 0;

				int len = V_strlen( token );
				dat->m_sValue = new char[len + 1];
				V_memcpy( dat->m_sValue, token, len+1 );
								
				break;
			}
		case TYPE_WSTRING:
			{
				int nLength = buffer.GetShort();
				
				dat->m_wsValue = new wchar_t[nLength + 1];
				
				for( int k = 0; k < nLength; ++ k )
				{
					dat->m_wsValue[k] = buffer.GetShort();
				}
				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;
			}

		default:
			break;
		}

		if ( !buffer.IsValid() ) // error occured
			return false;

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

//-----------------------------------------------------------------------------
// 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 = V_strlen( token );
				dat->m_sValue = new char[len + 1];
				V_memcpy( dat->m_sValue, token, len+1 );			
				break;
			}

		case TYPE_WSTRING:
			{
				int nLength = buffer.GetShort();
				dat->m_wsValue = new wchar_t[nLength + 1];
				for ( int k = 0; k < nLength; ++k )
				{
					dat->m_wsValue[k] = buffer.GetShort();
				}
				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"

//-----------------------------------------------------------------------------
// Purpose: memory allocator
//-----------------------------------------------------------------------------
void *KeyValues::operator new( size_t iAllocSize )
{
	MEM_ALLOC_CREDIT();
	return KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize);
}

void *KeyValues::operator new( size_t iAllocSize, int nBlockUse, const char *pFileName, int nLine )
{
	MemAlloc_PushAllocDbgInfo( pFileName, nLine );
	void *p = KeyValuesSystem()->AllocKeyValuesMemory(iAllocSize);
	MemAlloc_PopAllocDbgInfo();
	return p;
}

#include "tier0/memdbgon.h"

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

void KeyValues::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine )
{
	KeyValuesSystem()->FreeKeyValuesMemory(pMem);
}

void KeyValues::UnpackIntoStructure( KeyValuesUnpackStructure const *pUnpackTable, void *pDest )
{
	uint8 *dest = ( uint8 * ) pDest;
	while( pUnpackTable->m_pKeyName )
	{
		uint8 * dest_field = dest + pUnpackTable->m_nFieldOffset;
		KeyValues * find_it = FindKey( pUnpackTable->m_pKeyName );
		switch( pUnpackTable->m_eDataType )
		{
			case UNPACK_TYPE_FLOAT:
			{
				float default_value = ( pUnpackTable->m_pKeyDefault ) ? atof( pUnpackTable->m_pKeyDefault ) : 0.0;
				*( ( float * ) dest_field ) = GetFloat( pUnpackTable->m_pKeyName, default_value );
				break;
			}
			break;

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

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

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

			case UNPACK_TYPE_STRING:
			{
				char *dest_s = ( char * ) dest_field;
				char const *pDefault = "";
				if ( pUnpackTable->m_pKeyDefault )
				{
					pDefault = pUnpackTable->m_pKeyDefault;
				}
				strncpy( dest_s,
						 GetString( pUnpackTable->m_pKeyName, pDefault ),
						 pUnpackTable->m_nFieldSize );
			}
			break;

			case UNPACK_TYPE_INT:
			{
				int *dest_i = ( int * ) dest_field;
				int default_int = 0;
				if ( pUnpackTable->m_pKeyDefault )
					default_int = atoi( pUnpackTable->m_pKeyDefault );
				*( dest_i ) = GetInt( pUnpackTable->m_pKeyName, default_int );
			}
			break;

			case UNPACK_TYPE_VECTOR_COLOR:
			{
				Vector *dest_v = ( Vector * ) dest_field;
				if ( find_it )
				{
					Color c = GetColor( pUnpackTable->m_pKeyName );
					dest_v->x = c.r();
					dest_v->y = c.g();
					dest_v->z = c.b();
				}
				else
				{
					if ( pUnpackTable->m_pKeyDefault )
						sscanf(pUnpackTable->m_pKeyDefault,"%f %f %f",
							   & ( dest_v->x ), & ( dest_v->y ), & ( dest_v->z ));
					else
						dest_v->Init( 0, 0, 0 );
				}
				*( dest_v ) *= ( 1.0 / 255 );
			}
		}
		pUnpackTable++;
	}
}

//-----------------------------------------------------------------------------
// Helper function for processing a keyvalue tree for console resolution support.
// Alters key/values for easier console video resolution support. 
// If running SD (640x480), the presence of "???_lodef" creates or slams "???".
// If running HD (1280x720), the presence of "???_hidef" creates or slams "???".
//-----------------------------------------------------------------------------
bool KeyValues::ProcessResolutionKeys( const char *pResString )
{	
	if ( !pResString )
	{
		// not for pc, console only
		return false;
	}

	KeyValues *pSubKey = GetFirstSubKey();
	if ( !pSubKey )
	{
		// not a block
		return false;
	}

	for ( ; pSubKey != NULL; pSubKey = pSubKey->GetNextKey() )
	{
		// recursively descend each sub block
		pSubKey->ProcessResolutionKeys( pResString );

		// check to see if our substring is present
		if ( V_stristr( pSubKey->GetName(), pResString ) != NULL )
		{
			char normalKeyName[128];
			V_strncpy( normalKeyName, pSubKey->GetName(), sizeof( normalKeyName ) );

			// substring must match exactly, otherwise keys like "_lodef" and "_lodef_wide" would clash.
			char *pString = V_stristr( normalKeyName, pResString );
			if ( pString && !V_stricmp( pString, pResString ) )
			{
				*pString = '\0';

				// find and delete the original key (if any)
				KeyValues *pKey = FindKey( normalKeyName );
				if ( pKey )
				{		
					// remove the key
					RemoveSubKey( pKey );
					pKey->deleteThis();
				}

				// rename the marked key
				pSubKey->SetName( normalKeyName );
			}
		}
	}

	return true;
}

//
// KeyValues merge operations
//

void KeyValues::MergeFrom( KeyValues *kvMerge, MergeKeyValuesOp_t eOp /* = MERGE_KV_ALL */ )
{
	if ( !this || !kvMerge )
		return;

	switch ( eOp )
	{
	case MERGE_KV_ALL:
		MergeFrom( kvMerge->FindKey( "update" ), MERGE_KV_UPDATE );
		MergeFrom( kvMerge->FindKey( "delete" ), MERGE_KV_DELETE );
		MergeFrom( kvMerge->FindKey( "borrow" ), MERGE_KV_BORROW );
		return;

	case MERGE_KV_UPDATE:
		{
			for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() )
			{
				char const *szName = sub->GetName();

				KeyValues *subStorage = this->FindKey( szName, false );
				if ( !subStorage )
				{
					AddSubKey( sub->MakeCopy() );
				}
				else
				{
					subStorage->MergeFrom( sub, eOp );
				}
			}
			for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() )
			{
				char const *szName = val->GetName();

				if ( KeyValues *valStorage = this->FindKey( szName, false ) )
				{
					this->RemoveSubKey( valStorage );
					valStorage->deleteThis();
				}
				this->AddSubKey( val->MakeCopy() );
			}
		}
		return;

	case MERGE_KV_BORROW:
		{
			for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() )
			{
				char const *szName = sub->GetName();

				KeyValues *subStorage = this->FindKey( szName, false );
				if ( !subStorage )
					continue;

				subStorage->MergeFrom( sub, eOp );
			}
			for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() )
			{
				char const *szName = val->GetName();

				if ( KeyValues *valStorage = this->FindKey( szName, false ) )
				{
					this->RemoveSubKey( valStorage );
					valStorage->deleteThis();
				}
				else
					continue;

				this->AddSubKey( val->MakeCopy() );
			}
		}
		return;

	case MERGE_KV_DELETE:
		{
			for ( KeyValues *sub = kvMerge->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() )
			{
				char const *szName = sub->GetName();
				if ( KeyValues *subStorage = this->FindKey( szName, false ) )
				{
					subStorage->MergeFrom( sub, eOp );
				}
			}
			for ( KeyValues *val = kvMerge->GetFirstValue(); val; val = val->GetNextValue() )
			{
				char const *szName = val->GetName();

				if ( KeyValues *valStorage = this->FindKey( szName, false ) )
				{
					this->RemoveSubKey( valStorage );
					valStorage->deleteThis();
				}
			}
		}
		return;
	}
}


//
// KeyValues from string parsing
//

static char const * ParseStringToken( char const *szStringVal, char const **ppEndOfParse )
{
	// Eat whitespace
	while ( V_isspace( *szStringVal ) )
		++ szStringVal;

	char const *pszResult = szStringVal;

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

	if ( ppEndOfParse )
	{
		*ppEndOfParse = szStringVal;
	}

	return pszResult;
}

KeyValues * KeyValues::FromString( char const *szName, char const *szStringVal, char const **ppEndOfParse )
{
	if ( !szName )
		szName = "";

	if ( !szStringVal )
		szStringVal = "";

	KeyValues *kv = new KeyValues( szName );
	if ( !kv )
		return NULL;

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

	for ( ; ; )
	{
		char const *szEnd;

		char const *szVarValue = NULL;
		char const *szVarName = ParseStringToken( szStringVal, &szEnd );
		if ( !*szVarName )
			break;
		if ( *szVarName == '}' )
		{
			szStringVal = szVarName + 1;
			break;
		}
		V_strncpy( chName, szVarName, MIN( sizeof( chName ), szEnd - szVarName + 1 ) );
		szVarName = chName;
		szStringVal = szEnd;

		if ( *szVarName == '{' )
		{
			szVarName = "";
			goto do_sub_key;
		}

		szVarValue = ParseStringToken( szStringVal, &szEnd );
		if ( *szVarValue == '}' )
		{
			szStringVal = szVarValue + 1;
			kv->SetString( szVarName, "" );
			break;
		}
		V_strncpy( chValue, szVarValue, MIN( sizeof( chValue ), szEnd - szVarValue + 1 ) );
		szVarValue = chValue;
		szStringVal = szEnd;

		if ( *szVarValue == '{' )
		{
			goto do_sub_key;
		}

		// Try to recognize some known types
		if ( char const *szInt = StringAfterPrefix( szVarValue, "#int#" ) )
		{
			kv->SetInt( szVarName, atoi( szInt ) );
		}
		else if ( !V_stricmp( szVarValue, "#empty#" ) )
		{
			kv->SetString( szVarName, "" );
		}
		else
		{
			kv->SetString( szVarName, szVarValue );
		}
		continue;

do_sub_key:
		{
			KeyValues *pSubKey = KeyValues::FromString( szVarName, szStringVal, &szEnd );
			if ( pSubKey )
			{
				kv->AddSubKey( pSubKey );
			}
			szStringVal = szEnd;
			continue;
		}
	}

	if ( ppEndOfParse )
	{
		*ppEndOfParse = szStringVal;
	}

	return kv;
}



//
// KeyValues dumping implementation
//
bool KeyValues::Dump( IKeyValuesDumpContext *pDump, int nIndentLevel /* = 0 */ )
{
	if ( !pDump->KvBeginKey( this, nIndentLevel ) )
		return false;
	
	// Dump values
	for ( KeyValues *val = this ? GetFirstValue() : NULL; val; val = val->GetNextValue() )
	{
		if ( !pDump->KvWriteValue( val, nIndentLevel + 1 ) )
			return false;
	}

	// Dump subkeys
	for ( KeyValues *sub = this ? GetFirstTrueSubKey() : NULL; sub; sub = sub->GetNextTrueSubKey() )
	{
		if ( !sub->Dump( pDump, nIndentLevel + 1 ) )
			return false;
	}

	return pDump->KvEndKey( this, nIndentLevel );
}

bool IKeyValuesDumpContextAsText::KvBeginKey( KeyValues *pKey, int nIndentLevel )
{
	if ( pKey )
	{
		return
			KvWriteIndent( nIndentLevel ) &&
			KvWriteText( pKey->GetName() ) &&
			KvWriteText( " {\n" );
	}
	else
	{
		return
			KvWriteIndent( nIndentLevel ) &&
			KvWriteText( "<< NULL >>\n" );
	}
}

bool IKeyValuesDumpContextAsText::KvWriteValue( KeyValues *val, int nIndentLevel )
{
	if ( !val )
	{
		return
			KvWriteIndent( nIndentLevel ) &&
			KvWriteText( "<< NULL >>\n" );
	}

	if ( !KvWriteIndent( nIndentLevel ) )
		return false;

	if ( !KvWriteText( val->GetName() ) )
		return false;

	if ( !KvWriteText( " " ) )
		return false;

	switch ( val->GetDataType() )
	{
	case KeyValues::TYPE_STRING:
		{
			if ( !KvWriteText( val->GetString() ) )
				return false;
		}
		break;

	case KeyValues::TYPE_INT:
		{
			int n = val->GetInt();
			char *chBuffer = ( char * ) stackalloc( 128 );
			V_snprintf( chBuffer, 128, "int( %d = 0x%X )", n, n );
			if ( !KvWriteText( chBuffer ) )
				return false;
		}
		break;
	
	case KeyValues::TYPE_FLOAT:
		{
			float fl = val->GetFloat();
			char *chBuffer = ( char * ) stackalloc( 128 );
			V_snprintf( chBuffer, 128, "float( %f )", fl );
			if ( !KvWriteText( chBuffer ) )
				return false;
		}
		break;

	case KeyValues::TYPE_PTR:
		{
			void *ptr = val->GetPtr();
			char *chBuffer = ( char * ) stackalloc( 128 );
			V_snprintf( chBuffer, 128, "ptr( 0x%p )", ptr );
			if ( !KvWriteText( chBuffer ) )
				return false;
		}
		break;

	case KeyValues::TYPE_WSTRING:
		{
			wchar_t const *wsz = val->GetWString();
			int nLen = V_wcslen( wsz );
			int numBytes = nLen*2 + 64;
			char *chBuffer = ( char * ) stackalloc( numBytes );
			V_snprintf( chBuffer, numBytes, "%ls [wstring, len = %d]", wsz, nLen );
			if ( !KvWriteText( chBuffer ) )
				return false;
		}
		break;

	case KeyValues::TYPE_UINT64:
		{
			uint64 n = val->GetUint64();
			char *chBuffer = ( char * ) stackalloc( 128 );
			V_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 );
			if ( !KvWriteText( chBuffer ) )
				return false;
		}
		break;
#endif
	}

	return KvWriteText( "\n" );
}

bool IKeyValuesDumpContextAsText::KvEndKey( KeyValues *pKey, int nIndentLevel )
{
	if ( pKey )
	{
		return
			KvWriteIndent( nIndentLevel ) &&
			KvWriteText( "}\n" );
	}
	else
	{
		return true;
	}
}

bool IKeyValuesDumpContextAsText::KvWriteIndent( int nIndentLevel )
{
	int numIndentBytes = ( nIndentLevel * 2 + 1 );
	char *pchIndent = ( char * ) stackalloc( numIndentBytes );
	memset( pchIndent, ' ', numIndentBytes - 1 );
	pchIndent[ numIndentBytes - 1 ] = 0;
	return KvWriteText( pchIndent );
}


bool CKeyValuesDumpContextAsDevMsg::KvBeginKey( KeyValues *pKey, int nIndentLevel )
{
	static ConVarRef r_developer( "developer" );
	if ( r_developer.IsValid() && r_developer.GetInt() < m_nDeveloperLevel )
		// If "developer" is not the correct level, then avoid evaluating KeyValues tree early
		return false;
	else
		return IKeyValuesDumpContextAsText::KvBeginKey( pKey, nIndentLevel );
}

bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText )
{
	if ( m_nDeveloperLevel > 0 )
	{
		DevMsg( "%s", szText );
	}
	else
	{
		Msg( "%s", szText );
	}
	return true;
}