//========= 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 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; } } int c; // 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; }