|
|
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
//
//
//
//==================================================================================================
#include "vpc.h"
#define MAX_SCRIPT_STACK_SIZE 32
CScript::CScript() { m_ScriptName = "(empty)"; m_nScriptLine = 0; m_pScriptData = NULL; m_pScriptLine = &m_nScriptLine;
m_Token[0] = '\0'; m_PeekToken[0] = '\0'; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char *CScript::SkipWhitespace( const char *data, bool *pHasNewLines, int* pNumLines ) { int c;
while ( ( c = *data ) <= ' ' ) { if ( c == '\n' ) { if ( pNumLines ) { (*pNumLines)++; }
if ( pHasNewLines ) { *pHasNewLines = true; } } else if ( !c ) { return ( NULL ); }
data++; }
return data; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char *CScript::SkipToValidToken( const char *data, bool *pHasNewLines, int* pNumLines ) { int c;
for ( ;; ) { data = SkipWhitespace( data, pHasNewLines, pNumLines );
c = *data; if ( !c ) { break; }
if ( c == '/' && data[1] == '/' ) { // skip double slash comments
data += 2; while ( *data && *data != '\n' ) { data++; } if ( *data && *data == '\n' ) { data++; if ( pNumLines ) { (*pNumLines)++; } if ( pHasNewLines ) { *pHasNewLines = true; } } } else if ( c == '/' && data[1] == '*' ) { // skip /* */ comments
data += 2; while ( *data && ( *data != '*' || data[1] != '/' ) ) { if ( *data == '\n' ) { if ( pNumLines ) { (*pNumLines)++; } if ( pHasNewLines ) { *pHasNewLines = true; } } data++; }
if ( *data ) { data += 2; } } else { break; } }
return data; }
//-----------------------------------------------------------------------------
// The next token should be an open brace.
// Skips until a matching close brace is found.
// Internal brace depths are properly skipped.
//-----------------------------------------------------------------------------
void CScript::SkipBracedSection( const char** dataptr, int* numlines ) { const char* token; int depth;
depth = 0; do { token = GetToken( dataptr, true, numlines ); if ( token[1] == '\0' ) { if ( token[0] == '{' ) depth++; else if ( token[0] == '}' ) depth--; } } while( depth && *dataptr ); }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CScript::SkipRestOfLine( const char** dataptr, int* numlines ) { const char* p; int c;
p = *dataptr; while ( ( c = *p++ ) != '\0' ) { if ( c == '\n' ) { if ( numlines ) ( *numlines )++; break; } } *dataptr = p; }
//-----------------------------------------------------------------------------
// Does not corrupt results obtained with GetToken().
//-----------------------------------------------------------------------------
const char* CScript::PeekNextToken( const char *dataptr, bool bAllowLineBreaks ) { // save the primary token, about to be corrupted
char savedToken[MAX_SYSTOKENCHARS]; V_strncpy( savedToken, m_Token, MAX_SYSTOKENCHARS );
const char *pSaved = dataptr; const char *pToken = GetToken( &pSaved, bAllowLineBreaks, NULL );
// restore
V_strncpy( m_PeekToken, pToken, MAX_SYSTOKENCHARS ); V_strncpy( m_Token, savedToken, MAX_SYSTOKENCHARS );
return m_PeekToken; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const char *CScript::GetToken( const char **dataptr, bool allowLineBreaks, int *pNumLines ) { char c; char endSymbol; int len; bool hasNewLines; const char* data;
c = 0; data = *dataptr; len = 0; m_Token[0] = 0; hasNewLines = false;
// make sure incoming data is valid
if ( !data ) { *dataptr = NULL; return m_Token; }
for ( ;; ) { // skip whitespace
data = SkipWhitespace( data, &hasNewLines, pNumLines ); if ( !data ) { *dataptr = NULL; return m_Token; } if ( hasNewLines && !allowLineBreaks ) { *dataptr = data; return m_Token; }
c = *data;
if ( c == '/' && data[1] == '/' ) { // skip double slash comments
data += 2; while ( *data && *data != '\n' ) { data++; } if ( *data && *data == '\n' ) { if ( !allowLineBreaks ) continue;
data++; if ( pNumLines ) { (*pNumLines)++; } } } else if ( c =='/' && data[1] == '*' ) { // skip /* */ comments
data += 2; while ( *data && ( *data != '*' || data[1] != '/' ) ) { if ( *data == '\n' && pNumLines ) { (*pNumLines)++; } data++; }
if ( *data ) { data += 2; } } else break; }
// handle scoped strings "???" <???> [???]
if ( c == '\"' || c == '<' || c == '[') { bool bConditionalExpression = false; endSymbol = '\0'; switch ( c ) { case '\"': endSymbol = '\"'; break; case '<': endSymbol = '>'; break; case '[': bConditionalExpression = true; endSymbol = ']'; break; }
// want to preserve entire conditional expession [blah...blah...blah]
// maintain a conditional's open/close scope characters
if ( !bConditionalExpression ) { // skip past scope character
data++; }
for ( ;; ) { c = *data++;
if ( c == endSymbol || !c ) { if ( c == endSymbol && bConditionalExpression ) { // keep end symbol
m_Token[len++] = c; } m_Token[len] = 0; *dataptr = (char*)data; return m_Token; }
if ( len < MAX_SYSTOKENCHARS-1 ) { m_Token[len++] = c; } } }
// parse a regular word
do { if ( len < MAX_SYSTOKENCHARS ) { m_Token[len++] = c; }
data++; c = *data; } while ( c > ' ' );
if ( len >= MAX_SYSTOKENCHARS ) { len = 0; }
m_Token[len] = '\0'; *dataptr = (char*)data; return m_Token; }
void CScript::PushScript( const char *pFilename ) { // parse the text script
if ( !Sys_Exists( pFilename ) ) { g_pVPC->VPCError( "Cannot open %s", pFilename ); }
char *pScriptBuffer = NULL; // Sys_LoadTextFileWithIncludes does not unconditionally initialize this.
Sys_LoadTextFileWithIncludes( pFilename, &pScriptBuffer );
PushScript( pFilename, pScriptBuffer, 1, true ); }
void CScript::PushScript( const char *pScriptName, const char *pScriptData, int nScriptLine, bool bFreeScriptAtPop ) { if ( m_ScriptStack.Count() > MAX_SCRIPT_STACK_SIZE ) { g_pVPC->VPCError( "PushScript( scriptname=%s ) - stack overflow\n", pScriptName ); }
// Push the current state onto the stack.
m_ScriptStack.Push( GetCurrentScript() );
// Set their state as the current state.
m_ScriptName = pScriptName; m_pScriptData = pScriptData; m_nScriptLine = nScriptLine; m_bFreeScriptAtPop = bFreeScriptAtPop; }
void CScript::PushCurrentScript() { PushScript( m_ScriptName.Get(), m_pScriptData, m_nScriptLine, m_bFreeScriptAtPop ); }
CScriptSource CScript::GetCurrentScript() { return CScriptSource( m_ScriptName.Get(), m_pScriptData, m_nScriptLine, m_bFreeScriptAtPop ); }
void CScript::RestoreScript( const CScriptSource &scriptSource ) { m_ScriptName = scriptSource.GetName(); m_pScriptData = scriptSource.GetData(); m_nScriptLine = scriptSource.GetLine(); m_bFreeScriptAtPop = scriptSource.IsFreeScriptAtPop(); }
void CScript::PopScript() { if ( m_ScriptStack.Count() == 0 ) { g_pVPC->VPCError( "PopScript(): stack is empty" ); }
if ( m_bFreeScriptAtPop && m_pScriptData ) { free( (void *)m_pScriptData ); }
// Restore the top entry on the stack and pop it off.
const CScriptSource &state = m_ScriptStack.Top(); m_ScriptName = state.GetName(); m_pScriptData = state.GetData(); m_nScriptLine = state.GetLine(); m_bFreeScriptAtPop = state.IsFreeScriptAtPop();
m_ScriptStack.Pop(); }
void CScript::EnsureScriptStackEmpty() { if ( m_ScriptStack.Count() != 0 ) { g_pVPC->VPCError( "EnsureScriptStackEmpty(): script stack is not empty!" ); } }
void CScript::SpewScriptStack() { if ( m_ScriptStack.Count() ) { CUtlString str;
// emit stack with current at top
str += "Script Stack:\n"; str += CFmtStr( " %s Line:%d\n", m_ScriptName.String(), m_nScriptLine ); for ( int i = m_ScriptStack.Count() - 1; i >= 0; i-- ) { if ( i == 0 && !m_ScriptStack[i].GetData() && m_ScriptStack[i].GetLine() <= 0 ) { // ignore empty bottom of stack
break; }
str += CFmtStr( " %s Line:%d\n", m_ScriptStack[i].GetName(), m_ScriptStack[i].GetLine() ); } str += "\n";
Log_Msg( LOG_VPC, "%s", str.String() ); } }
const char *CScript::GetToken( bool bAllowLineBreaks ) { return GetToken( &m_pScriptData, bAllowLineBreaks, m_pScriptLine ); }
const char *CScript::PeekNextToken( bool bAllowLineBreaks ) { return PeekNextToken( m_pScriptData, bAllowLineBreaks ); }
void CScript::SkipRestOfLine() { SkipRestOfLine( &m_pScriptData, m_pScriptLine ); }
void CScript::SkipBracedSection() { SkipBracedSection( &m_pScriptData, m_pScriptLine ); }
void CScript::SkipToValidToken() { m_pScriptData = SkipToValidToken( m_pScriptData, NULL, m_pScriptLine ); }
//-----------------------------------------------------------------------------
// Handles expressions of the form <$BASE> <xxx> ... <xxx> [condition]
// Output is a concatenated string.
//
// Returns true if expression should be used, false if it should be ignored due
// to an optional condition that evaluated false.
//-----------------------------------------------------------------------------
bool CScript::ParsePropertyValue( const char *pBaseString, char *pOutBuff, int outBuffSize ) { const char **pScriptData = &m_pScriptData; int *pScriptLine = m_pScriptLine;
const char *pToken; const char *pNextToken; char *pOut = pOutBuff; int remaining = outBuffSize-1; int len; bool bAllowNextLine = false; char buffer1[MAX_SYSTOKENCHARS]; char buffer2[MAX_SYSTOKENCHARS]; bool bResult = true;
while ( 1 ) { pToken = GetToken( pScriptData, bAllowNextLine, pScriptLine ); if ( !pToken || !pToken[0] ) g_pVPC->VPCSyntaxError();
pNextToken = PeekNextToken( *pScriptData, false ); if ( !pNextToken || !pNextToken[0] ) { // current token is last token
// last token can be optional conditional, need to identify
// backup and reparse up to last token
if ( pToken && pToken[0] == '[' ) { // last token is an optional conditional
bResult = g_pVPC->EvaluateConditionalExpression( pToken ); break; } }
if ( !V_stricmp( pToken, "\\" ) ) { bAllowNextLine = true; continue; } else { bAllowNextLine = false; }
if ( !V_stricmp( pToken, "\\n" ) ) { pToken = "\n"; }
if ( pToken[0] ) { // handle reserved macro
if ( !pBaseString ) pBaseString = ""; strcpy( buffer1, pToken ); Sys_ReplaceString( buffer1, "$base", pBaseString, buffer2, sizeof( buffer2 ) );
g_pVPC->ResolveMacrosInString( buffer2, buffer1, sizeof( buffer1 ) );
len = strlen( buffer1 ); if ( remaining < len ) len = remaining;
if ( len > 0 ) { memcpy( pOut, buffer1, len ); pOut += len; remaining -= len; } }
pToken = PeekNextToken( *pScriptData, false ); if ( !pToken || !pToken[0] ) break; }
*pOut++ = '\0';
if ( !pOutBuff[0] ) g_pVPC->VPCSyntaxError();
return bResult; }
|