text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: Controls the loading, parsing and creation of the entities from the BSP.
  //
  //=============================================================================//
  
  
  #include "cbase.h"
  #include "mapentities_shared.h"
  
  // memdbgon must be the last include file in a .cpp file!!!
  #include "tier0/memdbgon.h"
  
  #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  
  static const char *s_BraceChars = "{}()\'";
  static bool s_BraceCharacters[256];
  static bool s_BuildReverseMap = true;
  
  bool MapEntity_ExtractValue( const char *pEntData, const char *keyName, char Value[MAPKEY_MAXLENGTH] )
  {
  	char token[MAPKEY_MAXLENGTH];
  	const char *inputData = pEntData;
  
  	while ( inputData )
  	{
  		inputData = MapEntity_ParseToken( inputData, token );	// get keyname
  		if ( token[0] == '}' )									// end of entity?
  			break;												// must not have seen the classname
  
  		// is this the right key?
  		if ( !strcmp(token, keyName) )
  		{
  			inputData = MapEntity_ParseToken( inputData, token );	// get value and return it
  			Q_strncpy( Value, token, MAPKEY_MAXLENGTH );
  			return true;
  		}
  
  		inputData = MapEntity_ParseToken( inputData, token );	// skip over value
  	}
  
  	return false;
  }
  
  int MapEntity_GetNumKeysInEntity( const char *pEntData )
  {
  	char token[MAPKEY_MAXLENGTH];
  	const char *inputData = pEntData;
  	int iNumKeys = 0;
  
  	while ( inputData )
  	{
  		inputData = MapEntity_ParseToken( inputData, token );	// get keyname
  		if ( token[0] == '}' )									// end of entity?
  			break;												// must not have seen the classname
  
  		iNumKeys++;
  
  		inputData = MapEntity_ParseToken( inputData, token );	// skip over value
  	}
  
  	return iNumKeys;
  }
  
  
  // skips to the beginning of the next entity in the data block
  // returns NULL if no more entities
  const char *MapEntity_SkipToNextEntity( const char *pMapData, char *pWorkBuffer )
  {
  	if ( !pMapData )
  		return NULL;
  
  	// search through the map string for the next matching '{'
  	int openBraceCount = 1;
  	while ( pMapData != NULL )
  	{
  		pMapData = MapEntity_ParseToken( pMapData, pWorkBuffer );
  
  		if ( FStrEq(pWorkBuffer, "{") )
  		{
  			openBraceCount++;
  		}
  		else if ( FStrEq(pWorkBuffer, "}") )
  		{
  			if ( --openBraceCount == 0 )
  			{
  				// we've found the closing brace, so return the next character
  				return pMapData;
  			}
  		}
  	}
  
  	// eof hit
  	return NULL;
  }
  
  
  //-----------------------------------------------------------------------------
  // Purpose: parses a token out of a char data block
  //			the token gets fully read no matter what the length, but only MAPKEY_MAXLENGTH 
  //			characters are written into newToken
  // Input  : char *data - the data to parse
  //			char *newToken - the buffer into which the new token is written
  //			char *braceChars - a string of characters that constitute braces.  this pointer needs to be
  //			distince for each set of braceChars, since the usage is cached.
  // Output : const char * - returns a pointer to the position in the data following the newToken
  //-----------------------------------------------------------------------------
  const char *MapEntity_ParseToken( const char *data, char *newToken )
  {
  	int             c;
  	int             len;
  		
  	len = 0;
  	newToken[0] = 0;
  	
  	if (!data)
  		return NULL;
  
  	// build the new table if we have to
  	if ( s_BuildReverseMap )
  	{
  		s_BuildReverseMap = false; 
  
  		Q_memset( s_BraceCharacters, 0, sizeof(s_BraceCharacters) );
  
  		for ( const char *c = s_BraceChars; *c; c++ )
  		{
  			s_BraceCharacters[(unsigned)*c] = true;
  		}
  	}
  		
  // skip whitespace
  skipwhite:
  	while ( (c = *data) <= ' ')
  	{
  		if (c == 0)
  			return NULL;                    // end of file;
  		data++;
  	}
  	
  // skip // comments
  	if (c=='/' && data[1] == '/')
  	{
  		while (*data && *data != '\n')
  			data++;
  		goto skipwhite;
  	}
  	
  
  // handle quoted strings specially
  	if (c == '\"')
  	{
  		data++;
  		while ( len < MAPKEY_MAXLENGTH )
  		{
  			c = *data++;
  			if (c=='\"' || !c)
  			{
  				newToken[len] = 0;
  				return data;
  			}
  			newToken[len] = c;
  			len++;
  		}
  
  		if ( len >= MAPKEY_MAXLENGTH )
  		{
  			len--;
  			newToken[len] = 0;
  		}
  	}
  
  // parse single characters
  	if ( s_BraceCharacters[c]/*c=='{' || c=='}'|| c==')'|| c=='(' || c=='\''*/ )
  	{
  		newToken[len] = c;
  		len++;
  		newToken[len] = 0;
  		return data+1;
  	}
  
  // parse a regular word
  	do
  	{
  		newToken[len] = c;
  		data++;
  		len++;
  		c = *data;
  		if ( s_BraceCharacters[c] /*c=='{' || c=='}'|| c==')'|| c=='(' || c=='\''*/ )
  			break;
  
  		if ( len >= MAPKEY_MAXLENGTH )
  		{
  			len--;
  			newToken[len] = 0;
  		}
  
  	} while (c>32);
  	
  	newToken[len] = 0;
  	return data;
  }
  
  #endif // !STATIC_LINKED || CLIENT_DLL
  
  /* ================= CEntityMapData definition ================ */
  
  bool CEntityMapData::ExtractValue( const char *keyName, char *value )
  {
  	return MapEntity_ExtractValue( m_pEntData, keyName, value );
  }
  
  bool CEntityMapData::GetFirstKey( char *keyName, char *value )
  {
  	m_pCurrentKey = m_pEntData; // reset the status pointer
  	return GetNextKey( keyName, value );
  }
  
  const char *CEntityMapData::CurrentBufferPosition( void )
  {
  	return m_pCurrentKey;
  }
  
  bool CEntityMapData::GetNextKey( char *keyName, char *value )
  {
  	char token[MAPKEY_MAXLENGTH];
  
  	// parse key
  	char *pPrevKey = m_pCurrentKey;
  	m_pCurrentKey = (char*)MapEntity_ParseToken( m_pCurrentKey, token );
  	if ( token[0] == '}' )
  	{
  		// step back
  		m_pCurrentKey = pPrevKey;
  		return false;
  	}
  
  	if ( !m_pCurrentKey )
  	{
  		Warning( "CEntityMapData::GetNextKey: EOF without closing brace\n" );
  		Assert(0);
  		return false;
  	}
  	
  	Q_strncpy( keyName, token, MAPKEY_MAXLENGTH );
  
  	// fix up keynames with trailing spaces
  	int n = strlen(keyName);
  	while (n && keyName[n-1] == ' ')
  	{
  		keyName[n-1] = 0;
  		n--;
  	}
  
  	// parse value	
  	m_pCurrentKey = (char*)MapEntity_ParseToken( m_pCurrentKey, token );
  	if ( !m_pCurrentKey )
  	{
  		Warning( "CEntityMapData::GetNextKey: EOF without closing brace\n" );
  		Assert(0);
  		return false;
  	}
  	if ( token[0] == '}' )
  	{
  		Warning( "CEntityMapData::GetNextKey: closing brace without data\n" );
  		Assert(0);
  		return false;
  	}
  
  	// value successfully found
  	Q_strncpy( value, token, MAPKEY_MAXLENGTH );
  	return true;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: find the keyName in the endata and change its value to specified one
  //-----------------------------------------------------------------------------
  bool CEntityMapData::SetValue( const char *keyName, char *NewValue, int nKeyInstance )
  {
  	// If this is -1, the size of the string is unknown and cannot be safely modified!
  	Assert( m_nEntDataSize != -1 );
  	if ( m_nEntDataSize == -1 )
  		return false;
  
  	char token[MAPKEY_MAXLENGTH];
  	char *inputData = m_pEntData;
  	char *prevData;
  
  	char newvaluebuf[ 1024 ];
  	int nCurrKeyInstance = 0;
  
  	while ( inputData )
  	{
  		inputData = (char*)MapEntity_ParseToken( inputData, token );	// get keyname
  		if ( token[0] == '}' )									// end of entity?
  			break;												// must not have seen the classname
  
  		// is this the right key?
  		if ( !strcmp(token, keyName) )
  		{
  			++nCurrKeyInstance;
  			if ( nCurrKeyInstance > nKeyInstance )
  			{
  				// Find the start & end of the token we're going to replace
  				int entLen = strlen(m_pEntData);
  				char *postData = new char[entLen];
  				prevData = inputData;
  				inputData = (char*)MapEntity_ParseToken( inputData, token );	// get keyname
  				Q_strncpy( postData, inputData, entLen );
  
  				// Insert quotes if caller didn't
  				if ( NewValue[0] != '\"' )
  				{
  					Q_snprintf( newvaluebuf, sizeof( newvaluebuf ), "\"%s\"", NewValue );
  				}
  				else
  				{
  					Q_strncpy( newvaluebuf, NewValue, sizeof( newvaluebuf ) );
  				}
  
  				int iNewValueLen = Q_strlen(newvaluebuf);
  				int iPadding = iNewValueLen - Q_strlen( token ) - 2;	// -2 for the quotes (token doesn't have them)
  
  				// prevData has a space at the start, seperating the value from the key.
  				// Add 1 to prevData when pasting in the new Value, to account for the space.
  				Q_strncpy( prevData+1, newvaluebuf, iNewValueLen+1 );	// +1 for the null terminator
  				Q_strcat( prevData, postData, m_nEntDataSize - ((prevData-m_pEntData)+1) );
  
  				m_pCurrentKey += iPadding;
  				delete [] postData;
  				return true;
  			}
  		}
  
  		inputData = (char*)MapEntity_ParseToken( inputData, token );	// skip over value
  	}
  
  	return false;
  }
  