Counter Strike : Global Offensive Source Code
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.

564 lines
12 KiB

  1. //===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
  2. //
  3. //
  4. //
  5. //==================================================================================================
  6. #include "vpc.h"
  7. #define MAX_SCRIPT_STACK_SIZE 32
  8. CScript::CScript()
  9. {
  10. m_ScriptName = "(empty)";
  11. m_nScriptLine = 0;
  12. m_pScriptData = NULL;
  13. m_pScriptLine = &m_nScriptLine;
  14. m_Token[0] = '\0';
  15. m_PeekToken[0] = '\0';
  16. }
  17. //-----------------------------------------------------------------------------
  18. //-----------------------------------------------------------------------------
  19. const char *CScript::SkipWhitespace( const char *data, bool *pHasNewLines, int* pNumLines )
  20. {
  21. int c;
  22. while ( ( c = *data ) <= ' ' )
  23. {
  24. if ( c == '\n' )
  25. {
  26. if ( pNumLines )
  27. {
  28. (*pNumLines)++;
  29. }
  30. if ( pHasNewLines )
  31. {
  32. *pHasNewLines = true;
  33. }
  34. }
  35. else if ( !c )
  36. {
  37. return ( NULL );
  38. }
  39. data++;
  40. }
  41. return data;
  42. }
  43. //-----------------------------------------------------------------------------
  44. //-----------------------------------------------------------------------------
  45. const char *CScript::SkipToValidToken( const char *data, bool *pHasNewLines, int* pNumLines )
  46. {
  47. int c;
  48. for ( ;; )
  49. {
  50. data = SkipWhitespace( data, pHasNewLines, pNumLines );
  51. c = *data;
  52. if ( !c )
  53. {
  54. break;
  55. }
  56. if ( c == '/' && data[1] == '/' )
  57. {
  58. // skip double slash comments
  59. data += 2;
  60. while ( *data && *data != '\n' )
  61. {
  62. data++;
  63. }
  64. if ( *data && *data == '\n' )
  65. {
  66. data++;
  67. if ( pNumLines )
  68. {
  69. (*pNumLines)++;
  70. }
  71. if ( pHasNewLines )
  72. {
  73. *pHasNewLines = true;
  74. }
  75. }
  76. }
  77. else if ( c == '/' && data[1] == '*' )
  78. {
  79. // skip /* */ comments
  80. data += 2;
  81. while ( *data && ( *data != '*' || data[1] != '/' ) )
  82. {
  83. if ( *data == '\n' )
  84. {
  85. if ( pNumLines )
  86. {
  87. (*pNumLines)++;
  88. }
  89. if ( pHasNewLines )
  90. {
  91. *pHasNewLines = true;
  92. }
  93. }
  94. data++;
  95. }
  96. if ( *data )
  97. {
  98. data += 2;
  99. }
  100. }
  101. else
  102. {
  103. break;
  104. }
  105. }
  106. return data;
  107. }
  108. //-----------------------------------------------------------------------------
  109. // The next token should be an open brace.
  110. // Skips until a matching close brace is found.
  111. // Internal brace depths are properly skipped.
  112. //-----------------------------------------------------------------------------
  113. void CScript::SkipBracedSection( const char** dataptr, int* numlines )
  114. {
  115. const char* token;
  116. int depth;
  117. depth = 0;
  118. do
  119. {
  120. token = GetToken( dataptr, true, numlines );
  121. if ( token[1] == '\0' )
  122. {
  123. if ( token[0] == '{' )
  124. depth++;
  125. else if ( token[0] == '}' )
  126. depth--;
  127. }
  128. }
  129. while( depth && *dataptr );
  130. }
  131. //-----------------------------------------------------------------------------
  132. //-----------------------------------------------------------------------------
  133. void CScript::SkipRestOfLine( const char** dataptr, int* numlines )
  134. {
  135. const char* p;
  136. int c;
  137. p = *dataptr;
  138. while ( ( c = *p++ ) != '\0' )
  139. {
  140. if ( c == '\n' )
  141. {
  142. if ( numlines )
  143. ( *numlines )++;
  144. break;
  145. }
  146. }
  147. *dataptr = p;
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Does not corrupt results obtained with GetToken().
  151. //-----------------------------------------------------------------------------
  152. const char* CScript::PeekNextToken( const char *dataptr, bool bAllowLineBreaks )
  153. {
  154. // save the primary token, about to be corrupted
  155. char savedToken[MAX_SYSTOKENCHARS];
  156. V_strncpy( savedToken, m_Token, MAX_SYSTOKENCHARS );
  157. const char *pSaved = dataptr;
  158. const char *pToken = GetToken( &pSaved, bAllowLineBreaks, NULL );
  159. // restore
  160. V_strncpy( m_PeekToken, pToken, MAX_SYSTOKENCHARS );
  161. V_strncpy( m_Token, savedToken, MAX_SYSTOKENCHARS );
  162. return m_PeekToken;
  163. }
  164. //-----------------------------------------------------------------------------
  165. //-----------------------------------------------------------------------------
  166. const char *CScript::GetToken( const char **dataptr, bool allowLineBreaks, int *pNumLines )
  167. {
  168. char c;
  169. char endSymbol;
  170. int len;
  171. bool hasNewLines;
  172. const char* data;
  173. c = 0;
  174. data = *dataptr;
  175. len = 0;
  176. m_Token[0] = 0;
  177. hasNewLines = false;
  178. // make sure incoming data is valid
  179. if ( !data )
  180. {
  181. *dataptr = NULL;
  182. return m_Token;
  183. }
  184. for ( ;; )
  185. {
  186. // skip whitespace
  187. data = SkipWhitespace( data, &hasNewLines, pNumLines );
  188. if ( !data )
  189. {
  190. *dataptr = NULL;
  191. return m_Token;
  192. }
  193. if ( hasNewLines && !allowLineBreaks )
  194. {
  195. *dataptr = data;
  196. return m_Token;
  197. }
  198. c = *data;
  199. if ( c == '/' && data[1] == '/' )
  200. {
  201. // skip double slash comments
  202. data += 2;
  203. while ( *data && *data != '\n' )
  204. {
  205. data++;
  206. }
  207. if ( *data && *data == '\n' )
  208. {
  209. if ( !allowLineBreaks )
  210. continue;
  211. data++;
  212. if ( pNumLines )
  213. {
  214. (*pNumLines)++;
  215. }
  216. }
  217. }
  218. else if ( c =='/' && data[1] == '*' )
  219. {
  220. // skip /* */ comments
  221. data += 2;
  222. while ( *data && ( *data != '*' || data[1] != '/' ) )
  223. {
  224. if ( *data == '\n' && pNumLines )
  225. {
  226. (*pNumLines)++;
  227. }
  228. data++;
  229. }
  230. if ( *data )
  231. {
  232. data += 2;
  233. }
  234. }
  235. else
  236. break;
  237. }
  238. // handle scoped strings "???" <???> [???]
  239. if ( c == '\"' || c == '<' || c == '[')
  240. {
  241. bool bConditionalExpression = false;
  242. endSymbol = '\0';
  243. switch ( c )
  244. {
  245. case '\"':
  246. endSymbol = '\"';
  247. break;
  248. case '<':
  249. endSymbol = '>';
  250. break;
  251. case '[':
  252. bConditionalExpression = true;
  253. endSymbol = ']';
  254. break;
  255. }
  256. // want to preserve entire conditional expession [blah...blah...blah]
  257. // maintain a conditional's open/close scope characters
  258. if ( !bConditionalExpression )
  259. {
  260. // skip past scope character
  261. data++;
  262. }
  263. for ( ;; )
  264. {
  265. c = *data++;
  266. if ( c == endSymbol || !c )
  267. {
  268. if ( c == endSymbol && bConditionalExpression )
  269. {
  270. // keep end symbol
  271. m_Token[len++] = c;
  272. }
  273. m_Token[len] = 0;
  274. *dataptr = (char*)data;
  275. return m_Token;
  276. }
  277. if ( len < MAX_SYSTOKENCHARS-1 )
  278. {
  279. m_Token[len++] = c;
  280. }
  281. }
  282. }
  283. // parse a regular word
  284. do
  285. {
  286. if ( len < MAX_SYSTOKENCHARS )
  287. {
  288. m_Token[len++] = c;
  289. }
  290. data++;
  291. c = *data;
  292. }
  293. while ( c > ' ' );
  294. if ( len >= MAX_SYSTOKENCHARS )
  295. {
  296. len = 0;
  297. }
  298. m_Token[len] = '\0';
  299. *dataptr = (char*)data;
  300. return m_Token;
  301. }
  302. void CScript::PushScript( const char *pFilename )
  303. {
  304. // parse the text script
  305. if ( !Sys_Exists( pFilename ) )
  306. {
  307. g_pVPC->VPCError( "Cannot open %s", pFilename );
  308. }
  309. char *pScriptBuffer = NULL; // Sys_LoadTextFileWithIncludes does not unconditionally initialize this.
  310. Sys_LoadTextFileWithIncludes( pFilename, &pScriptBuffer );
  311. PushScript( pFilename, pScriptBuffer, 1, true );
  312. }
  313. void CScript::PushScript( const char *pScriptName, const char *pScriptData, int nScriptLine, bool bFreeScriptAtPop )
  314. {
  315. if ( m_ScriptStack.Count() > MAX_SCRIPT_STACK_SIZE )
  316. {
  317. g_pVPC->VPCError( "PushScript( scriptname=%s ) - stack overflow\n", pScriptName );
  318. }
  319. // Push the current state onto the stack.
  320. m_ScriptStack.Push( GetCurrentScript() );
  321. // Set their state as the current state.
  322. m_ScriptName = pScriptName;
  323. m_pScriptData = pScriptData;
  324. m_nScriptLine = nScriptLine;
  325. m_bFreeScriptAtPop = bFreeScriptAtPop;
  326. }
  327. void CScript::PushCurrentScript()
  328. {
  329. PushScript( m_ScriptName.Get(), m_pScriptData, m_nScriptLine, m_bFreeScriptAtPop );
  330. }
  331. CScriptSource CScript::GetCurrentScript()
  332. {
  333. return CScriptSource( m_ScriptName.Get(), m_pScriptData, m_nScriptLine, m_bFreeScriptAtPop );
  334. }
  335. void CScript::RestoreScript( const CScriptSource &scriptSource )
  336. {
  337. m_ScriptName = scriptSource.GetName();
  338. m_pScriptData = scriptSource.GetData();
  339. m_nScriptLine = scriptSource.GetLine();
  340. m_bFreeScriptAtPop = scriptSource.IsFreeScriptAtPop();
  341. }
  342. void CScript::PopScript()
  343. {
  344. if ( m_ScriptStack.Count() == 0 )
  345. {
  346. g_pVPC->VPCError( "PopScript(): stack is empty" );
  347. }
  348. if ( m_bFreeScriptAtPop && m_pScriptData )
  349. {
  350. free( (void *)m_pScriptData );
  351. }
  352. // Restore the top entry on the stack and pop it off.
  353. const CScriptSource &state = m_ScriptStack.Top();
  354. m_ScriptName = state.GetName();
  355. m_pScriptData = state.GetData();
  356. m_nScriptLine = state.GetLine();
  357. m_bFreeScriptAtPop = state.IsFreeScriptAtPop();
  358. m_ScriptStack.Pop();
  359. }
  360. void CScript::EnsureScriptStackEmpty()
  361. {
  362. if ( m_ScriptStack.Count() != 0 )
  363. {
  364. g_pVPC->VPCError( "EnsureScriptStackEmpty(): script stack is not empty!" );
  365. }
  366. }
  367. void CScript::SpewScriptStack()
  368. {
  369. if ( m_ScriptStack.Count() )
  370. {
  371. CUtlString str;
  372. // emit stack with current at top
  373. str += "Script Stack:\n";
  374. str += CFmtStr( " %s Line:%d\n", m_ScriptName.String(), m_nScriptLine );
  375. for ( int i = m_ScriptStack.Count() - 1; i >= 0; i-- )
  376. {
  377. if ( i == 0 && !m_ScriptStack[i].GetData() && m_ScriptStack[i].GetLine() <= 0 )
  378. {
  379. // ignore empty bottom of stack
  380. break;
  381. }
  382. str += CFmtStr( " %s Line:%d\n", m_ScriptStack[i].GetName(), m_ScriptStack[i].GetLine() );
  383. }
  384. str += "\n";
  385. Log_Msg( LOG_VPC, "%s", str.String() );
  386. }
  387. }
  388. const char *CScript::GetToken( bool bAllowLineBreaks )
  389. {
  390. return GetToken( &m_pScriptData, bAllowLineBreaks, m_pScriptLine );
  391. }
  392. const char *CScript::PeekNextToken( bool bAllowLineBreaks )
  393. {
  394. return PeekNextToken( m_pScriptData, bAllowLineBreaks );
  395. }
  396. void CScript::SkipRestOfLine()
  397. {
  398. SkipRestOfLine( &m_pScriptData, m_pScriptLine );
  399. }
  400. void CScript::SkipBracedSection()
  401. {
  402. SkipBracedSection( &m_pScriptData, m_pScriptLine );
  403. }
  404. void CScript::SkipToValidToken()
  405. {
  406. m_pScriptData = SkipToValidToken( m_pScriptData, NULL, m_pScriptLine );
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Handles expressions of the form <$BASE> <xxx> ... <xxx> [condition]
  410. // Output is a concatenated string.
  411. //
  412. // Returns true if expression should be used, false if it should be ignored due
  413. // to an optional condition that evaluated false.
  414. //-----------------------------------------------------------------------------
  415. bool CScript::ParsePropertyValue( const char *pBaseString, char *pOutBuff, int outBuffSize )
  416. {
  417. const char **pScriptData = &m_pScriptData;
  418. int *pScriptLine = m_pScriptLine;
  419. const char *pToken;
  420. const char *pNextToken;
  421. char *pOut = pOutBuff;
  422. int remaining = outBuffSize-1;
  423. int len;
  424. bool bAllowNextLine = false;
  425. char buffer1[MAX_SYSTOKENCHARS];
  426. char buffer2[MAX_SYSTOKENCHARS];
  427. bool bResult = true;
  428. while ( 1 )
  429. {
  430. pToken = GetToken( pScriptData, bAllowNextLine, pScriptLine );
  431. if ( !pToken || !pToken[0] )
  432. g_pVPC->VPCSyntaxError();
  433. pNextToken = PeekNextToken( *pScriptData, false );
  434. if ( !pNextToken || !pNextToken[0] )
  435. {
  436. // current token is last token
  437. // last token can be optional conditional, need to identify
  438. // backup and reparse up to last token
  439. if ( pToken && pToken[0] == '[' )
  440. {
  441. // last token is an optional conditional
  442. bResult = g_pVPC->EvaluateConditionalExpression( pToken );
  443. break;
  444. }
  445. }
  446. if ( !V_stricmp( pToken, "\\" ) )
  447. {
  448. bAllowNextLine = true;
  449. continue;
  450. }
  451. else
  452. {
  453. bAllowNextLine = false;
  454. }
  455. if ( !V_stricmp( pToken, "\\n" ) )
  456. {
  457. pToken = "\n";
  458. }
  459. if ( pToken[0] )
  460. {
  461. // handle reserved macro
  462. if ( !pBaseString )
  463. pBaseString = "";
  464. strcpy( buffer1, pToken );
  465. Sys_ReplaceString( buffer1, "$base", pBaseString, buffer2, sizeof( buffer2 ) );
  466. g_pVPC->ResolveMacrosInString( buffer2, buffer1, sizeof( buffer1 ) );
  467. len = strlen( buffer1 );
  468. if ( remaining < len )
  469. len = remaining;
  470. if ( len > 0 )
  471. {
  472. memcpy( pOut, buffer1, len );
  473. pOut += len;
  474. remaining -= len;
  475. }
  476. }
  477. pToken = PeekNextToken( *pScriptData, false );
  478. if ( !pToken || !pToken[0] )
  479. break;
  480. }
  481. *pOut++ = '\0';
  482. if ( !pOutBuff[0] )
  483. g_pVPC->VPCSyntaxError();
  484. return bResult;
  485. }