|
|
|
//========= 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[*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;
|
|
|
|
}
|