Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
8.1 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Controls the loading, parsing and creation of the entities from the BSP.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "mapentities_shared.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  11. static const char *s_BraceChars = "{}()\'";
  12. static bool s_BraceCharacters[256];
  13. static bool s_BuildReverseMap = true;
  14. bool MapEntity_ExtractValue( const char *pEntData, const char *keyName, char Value[MAPKEY_MAXLENGTH] )
  15. {
  16. char token[MAPKEY_MAXLENGTH];
  17. const char *inputData = pEntData;
  18. while ( inputData )
  19. {
  20. inputData = MapEntity_ParseToken( inputData, token ); // get keyname
  21. if ( token[0] == '}' ) // end of entity?
  22. break; // must not have seen the classname
  23. // is this the right key?
  24. if ( !strcmp(token, keyName) )
  25. {
  26. inputData = MapEntity_ParseToken( inputData, token ); // get value and return it
  27. Q_strncpy( Value, token, MAPKEY_MAXLENGTH );
  28. return true;
  29. }
  30. inputData = MapEntity_ParseToken( inputData, token ); // skip over value
  31. }
  32. return false;
  33. }
  34. int MapEntity_GetNumKeysInEntity( const char *pEntData )
  35. {
  36. char token[MAPKEY_MAXLENGTH];
  37. const char *inputData = pEntData;
  38. int iNumKeys = 0;
  39. while ( inputData )
  40. {
  41. inputData = MapEntity_ParseToken( inputData, token ); // get keyname
  42. if ( token[0] == '}' ) // end of entity?
  43. break; // must not have seen the classname
  44. iNumKeys++;
  45. inputData = MapEntity_ParseToken( inputData, token ); // skip over value
  46. }
  47. return iNumKeys;
  48. }
  49. // skips to the beginning of the next entity in the data block
  50. // returns NULL if no more entities
  51. const char *MapEntity_SkipToNextEntity( const char *pMapData, char *pWorkBuffer )
  52. {
  53. if ( !pMapData )
  54. return NULL;
  55. // search through the map string for the next matching '{'
  56. int openBraceCount = 1;
  57. while ( pMapData != NULL )
  58. {
  59. pMapData = MapEntity_ParseToken( pMapData, pWorkBuffer );
  60. if ( FStrEq(pWorkBuffer, "{") )
  61. {
  62. openBraceCount++;
  63. }
  64. else if ( FStrEq(pWorkBuffer, "}") )
  65. {
  66. if ( --openBraceCount == 0 )
  67. {
  68. // we've found the closing brace, so return the next character
  69. return pMapData;
  70. }
  71. }
  72. }
  73. // eof hit
  74. return NULL;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose: parses a token out of a char data block
  78. // the token gets fully read no matter what the length, but only MAPKEY_MAXLENGTH
  79. // characters are written into newToken
  80. // Input : char *data - the data to parse
  81. // char *newToken - the buffer into which the new token is written
  82. // char *braceChars - a string of characters that constitute braces. this pointer needs to be
  83. // distince for each set of braceChars, since the usage is cached.
  84. // Output : const char * - returns a pointer to the position in the data following the newToken
  85. //-----------------------------------------------------------------------------
  86. const char *MapEntity_ParseToken( const char *data, char *newToken )
  87. {
  88. int len;
  89. len = 0;
  90. newToken[0] = 0;
  91. if (!data)
  92. return NULL;
  93. // build the new table if we have to
  94. if ( s_BuildReverseMap )
  95. {
  96. s_BuildReverseMap = false;
  97. Q_memset( s_BraceCharacters, 0, sizeof(s_BraceCharacters) );
  98. for ( const char *c = s_BraceChars; *c; c++ )
  99. {
  100. s_BraceCharacters[(unsigned)*c] = true;
  101. }
  102. }
  103. int c;
  104. // skip whitespace
  105. skipwhite:
  106. while ( (c = *data) <= ' ')
  107. {
  108. if (c == 0)
  109. return NULL; // end of file;
  110. data++;
  111. }
  112. // skip // comments
  113. if (c=='/' && data[1] == '/')
  114. {
  115. while (*data && *data != '\n')
  116. data++;
  117. goto skipwhite;
  118. }
  119. // handle quoted strings specially
  120. if (c == '\"')
  121. {
  122. data++;
  123. while ( len < MAPKEY_MAXLENGTH )
  124. {
  125. c = *data++;
  126. if (c=='\"' || !c)
  127. {
  128. newToken[len] = 0;
  129. return data;
  130. }
  131. newToken[len] = c;
  132. len++;
  133. }
  134. if ( len >= MAPKEY_MAXLENGTH )
  135. {
  136. len--;
  137. newToken[len] = 0;
  138. }
  139. }
  140. // parse single characters
  141. if ( s_BraceCharacters[c]/*c=='{' || c=='}'|| c==')'|| c=='(' || c=='\''*/ )
  142. {
  143. newToken[len] = c;
  144. len++;
  145. newToken[len] = 0;
  146. return data+1;
  147. }
  148. // parse a regular word
  149. do
  150. {
  151. newToken[len] = c;
  152. data++;
  153. len++;
  154. c = *data;
  155. if ( s_BraceCharacters[c] /*c=='{' || c=='}'|| c==')'|| c=='(' || c=='\''*/ )
  156. break;
  157. if ( len >= MAPKEY_MAXLENGTH )
  158. {
  159. len--;
  160. newToken[len] = 0;
  161. }
  162. } while (c>32);
  163. newToken[len] = 0;
  164. return data;
  165. }
  166. #endif // !STATIC_LINKED || CLIENT_DLL
  167. /* ================= CEntityMapData definition ================ */
  168. bool CEntityMapData::ExtractValue( const char *keyName, char *value )
  169. {
  170. return MapEntity_ExtractValue( m_pEntData, keyName, value );
  171. }
  172. bool CEntityMapData::GetFirstKey( char *keyName, char *value )
  173. {
  174. m_pCurrentKey = m_pEntData; // reset the status pointer
  175. return GetNextKey( keyName, value );
  176. }
  177. const char *CEntityMapData::CurrentBufferPosition( void )
  178. {
  179. return m_pCurrentKey;
  180. }
  181. bool CEntityMapData::GetNextKey( char *keyName, char *value )
  182. {
  183. char token[MAPKEY_MAXLENGTH];
  184. // parse key
  185. char *pPrevKey = m_pCurrentKey;
  186. m_pCurrentKey = (char*)MapEntity_ParseToken( m_pCurrentKey, token );
  187. if ( token[0] == '}' )
  188. {
  189. // step back
  190. m_pCurrentKey = pPrevKey;
  191. return false;
  192. }
  193. if ( !m_pCurrentKey )
  194. {
  195. Warning( "CEntityMapData::GetNextKey: EOF without closing brace\n" );
  196. Assert(0);
  197. return false;
  198. }
  199. Q_strncpy( keyName, token, MAPKEY_MAXLENGTH );
  200. // fix up keynames with trailing spaces
  201. int n = strlen(keyName);
  202. while (n && keyName[n-1] == ' ')
  203. {
  204. keyName[n-1] = 0;
  205. n--;
  206. }
  207. // parse value
  208. m_pCurrentKey = (char*)MapEntity_ParseToken( m_pCurrentKey, token );
  209. if ( !m_pCurrentKey )
  210. {
  211. Warning( "CEntityMapData::GetNextKey: EOF without closing brace\n" );
  212. Assert(0);
  213. return false;
  214. }
  215. if ( token[0] == '}' )
  216. {
  217. Warning( "CEntityMapData::GetNextKey: closing brace without data\n" );
  218. Assert(0);
  219. return false;
  220. }
  221. // value successfully found
  222. Q_strncpy( value, token, MAPKEY_MAXLENGTH );
  223. return true;
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose: find the keyName in the endata and change its value to specified one
  227. //-----------------------------------------------------------------------------
  228. bool CEntityMapData::SetValue( const char *keyName, char *NewValue, int nKeyInstance )
  229. {
  230. // If this is -1, the size of the string is unknown and cannot be safely modified!
  231. Assert( m_nEntDataSize != -1 );
  232. if ( m_nEntDataSize == -1 )
  233. return false;
  234. char token[MAPKEY_MAXLENGTH];
  235. char *inputData = m_pEntData;
  236. char *prevData;
  237. char newvaluebuf[ 1024 ];
  238. int nCurrKeyInstance = 0;
  239. while ( inputData )
  240. {
  241. inputData = (char*)MapEntity_ParseToken( inputData, token ); // get keyname
  242. if ( token[0] == '}' ) // end of entity?
  243. break; // must not have seen the classname
  244. // is this the right key?
  245. if ( !strcmp(token, keyName) )
  246. {
  247. ++nCurrKeyInstance;
  248. if ( nCurrKeyInstance > nKeyInstance )
  249. {
  250. // Find the start & end of the token we're going to replace
  251. int entLen = strlen(m_pEntData);
  252. char *postData = new char[entLen];
  253. prevData = inputData;
  254. inputData = (char*)MapEntity_ParseToken( inputData, token ); // get keyname
  255. Q_strncpy( postData, inputData, entLen );
  256. // Insert quotes if caller didn't
  257. if ( NewValue[0] != '\"' )
  258. {
  259. Q_snprintf( newvaluebuf, sizeof( newvaluebuf ), "\"%s\"", NewValue );
  260. }
  261. else
  262. {
  263. Q_strncpy( newvaluebuf, NewValue, sizeof( newvaluebuf ) );
  264. }
  265. int iNewValueLen = Q_strlen(newvaluebuf);
  266. int iPadding = iNewValueLen - Q_strlen( token ) - 2; // -2 for the quotes (token doesn't have them)
  267. // prevData has a space at the start, seperating the value from the key.
  268. // Add 1 to prevData when pasting in the new Value, to account for the space.
  269. Q_strncpy( prevData+1, newvaluebuf, iNewValueLen+1 ); // +1 for the null terminator
  270. Q_strcat( prevData, postData, m_nEntDataSize - ((prevData-m_pEntData)+1) );
  271. m_pCurrentKey += iPadding;
  272. delete [] postData;
  273. return true;
  274. }
  275. }
  276. inputData = (char*)MapEntity_ParseToken( inputData, token ); // skip over value
  277. }
  278. return false;
  279. }