//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/script.cpp $ // $Revision:: 18 $ // $Author:: Steven $ // $Date:: 10/13/03 9:11a $ // // Copyright (C) 1997 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // // DESCRIPTION: // C++ implementaion of tokenizing text interpretation. Class accepts filename // to load or pointer to preloaded text data. Standard tokenizing operations // such as skip white-space, get string, get integer, get float, get token, // and skip line are implemented. // // Note: all '//', '#', and ';' are treated as comments. Probably should // make this behaviour toggleable. // #include "_pch_cpp.h" #include "script.h" #include #define TOKENCOMMENT (';') #define TOKENCOMMENT2 ('#') #define TOKENEOL ('\n') //#define TOKENNULL ('\0') #define TOKENSPACE (' ') #define TOKENSPECIAL ('$') CLASS_DECLARATION( Class, Script, NULL ) { { NULL, NULL } }; Script::~Script() { Close(); } Script::Script(const char* filename /*= 0*/) { buffer = NULL; script_p = NULL; end_p = NULL; line = 0; length = 0; releaseBuffer = false; tokenready = false; memset( token, 0, sizeof( token ) ); if ( filename != 0 ) LoadFile(filename); } void Script::Close( void ) { if ( releaseBuffer && buffer ) { gi.Free( ( void * )buffer ); } buffer = NULL; script_p = NULL; end_p = NULL; line = 0; releaseBuffer = false; tokenready = false; memset( token, 0, sizeof( token ) ); //Loop Through the macro container and delete (del33t -hehe) them all for( int i = 1; i <= macrolist.NumObjects(); i++) { if (macrolist.ObjectAt( i ) ) { delete macrolist.ObjectAt( i ); macrolist.ObjectAt( i ) = 0; } } } /* ============== = = Filename = ============== */ const char *Script::Filename( void ) { return filename.c_str(); } /* ============== = = GetLineNumber = ============== */ int Script::GetLineNumber( void ) { return line; } /* ============== = = Reset = ============== */ void Script::Reset( void ) { script_p = buffer; line = 1; tokenready = false; } /* ============== = = MarkPosition = ============== */ void Script::MarkPosition( scriptmarker_t *mark ) { assert( mark ); mark->tokenready = tokenready; mark->offset = script_p - buffer; mark->line = line; strcpy( mark->token, token ); } /* ============== = = RestorePosition = ============== */ void Script::RestorePosition( const scriptmarker_t *mark ) { assert( mark ); tokenready = mark->tokenready; script_p = buffer + mark->offset; line = mark->line; strcpy( token, mark->token ); assert( script_p <= end_p ); if ( script_p > end_p ) { script_p = end_p; } } /* ============== = = SkipToEOL = ============== */ qboolean Script::SkipToEOL( void ) { if ( script_p >= end_p ) { return true; } while( *script_p != TOKENEOL ) { if ( script_p >= end_p ) { return true; } script_p++; } return false; } /* ============== = = CheckOverflow = ============== */ void Script::CheckOverflow( void ) { if ( script_p >= end_p ) { gi.Error( ERR_DROP, "End of token file reached prematurely reading %s\n", filename.c_str() ); } } /* ============== = = SkipWhiteSpace = ============== */ void Script::SkipWhiteSpace( qboolean crossline ) { // // skip space // CheckOverflow(); while( *script_p <= TOKENSPACE ) { if ( *script_p++ == TOKENEOL ) { if ( !crossline ) { gi.Error( ERR_DROP, "Line %i is incomplete in file %s\n", line, filename.c_str() ); } line++; } CheckOverflow(); } } qboolean Script::AtComment( void ) { if ( script_p >= end_p ) { return false; } if ( *script_p == TOKENCOMMENT ) { return true; } if ( *script_p == TOKENCOMMENT2 ) { return true; } // Two or more character comment specifiers if ( ( script_p + 1 ) >= end_p ) { return false; } if ( ( *script_p == '/' ) && ( *( script_p + 1 ) == '/' ) ) { return true; } return false; } /* ============== = = SkipNonToken = ============== */ void Script::SkipNonToken( qboolean crossline ) { // // skip space and comments // SkipWhiteSpace( crossline ); while( AtComment() ) { SkipToEOL(); SkipWhiteSpace( crossline ); } } /* ============================================================================= = = Token section = ============================================================================= */ /* ============== = = TokenAvailable = ============== */ qboolean Script::TokenAvailable( qboolean crossline ) { if ( script_p >= end_p ) { return false; } while ( 1 ) { while ( *script_p <= TOKENSPACE ) { if ( *script_p == TOKENEOL ) { if ( !crossline ) { return( false ); } line++; } script_p++; if ( script_p >= end_p ) { return false; } } if ( AtComment() ) { qboolean done; done = SkipToEOL(); if ( done ) { return false; } } else { break; } } return true; } /* ============== = = CommentAvailable = ============== */ qboolean Script::CommentAvailable( qboolean crossline ) { const char *searchptr; searchptr = script_p; if ( searchptr >= end_p ) { return false; } while ( *searchptr <= TOKENSPACE ) { if ( ( *searchptr == TOKENEOL ) && ( !crossline ) ) { return false; } searchptr++; if ( searchptr >= end_p ) { return false; } } return true; } /* ============== = = UnGet = = Signals that the current token was not used, and should be reported = for the next GetToken. Note that GetToken (true); UnGetToken (); GetToken (false); = could cross a line boundary. = ============== */ void Script::UnGetToken( void ) { tokenready = true; } /* ============== = = Get = ============== */ qboolean Script::AtString( qboolean crossline ) { // // skip space // SkipNonToken( crossline ); return ( *script_p == '"' ); } qboolean Script::AtOpenParen( qboolean crossline ) { // // skip space // SkipNonToken( crossline ); return ( *script_p == '(' ); } qboolean Script::AtCloseParen( qboolean crossline ) { // // skip space // SkipNonToken( crossline ); return ( *script_p == ')' ); } /* ============== = = Get = ============== */ const char *Script::GetToken( qboolean crossline ) { str token_p = token; qboolean is_Macro = false; // is a token already waiting? if ( tokenready ) { tokenready = false; return token; } is_Macro = isMacro(); token_p = GrabNextToken(crossline); if ( is_Macro && ( token_p != "$include" ) ) { //Check to see if we need to add any definitions while ( ( token_p == "$define" ) || ( token_p == "$Define" ) ) { AddMacroDefinition(crossline); is_Macro = isMacro(); //if ( !is_Macro ) // return ""; token_p = GrabNextToken(crossline); } //Check to see if we need return any defines strings if( is_Macro && ( token_p != "$include" ) && ( token_p[token_p.length() - 1] == '$' ) ) { return GetMacroString(token_p); } } return token; } /* ============== = = GrabNextToken = ============== */ const char *Script::GrabNextToken( qboolean crossline ) { char *token_p; // // skip space // SkipNonToken( crossline ); // // copy token // if ( *script_p == '"' ) { return GetString( crossline ); } token_p = token; while( ( *script_p > TOKENSPACE ) && !AtComment() ) { if ( ( *script_p == '\\' ) && ( script_p < ( end_p - 1 ) ) ) { script_p++; switch( *script_p ) { case 'n' : *token_p++ = '\n'; break; case 'r' : *token_p++ = '\n'; break; case '\'' : *token_p++ = '\''; break; case '\"' : *token_p++ = '\"'; break; case '\\' : *token_p++ = '\\'; break; default: *token_p++ = *script_p; break; } script_p++; } else { *token_p++ = *script_p++; } if ( token_p == &token[ MAXTOKEN ] ) { gi.Error( ERR_DROP, "Token too large on line %i in file %s\n", line, filename.c_str() ); } if ( script_p == end_p ) { break; } } *token_p = 0; return token; } /* ============== = = AddMacroDefinition = ============== */ void Script::AddMacroDefinition( qboolean crossline ) { macro *theMacro; //Create a new macro structure. This new macro will be deleted in the script close() theMacro = new macro; //Grab the macro name theMacro->macroName = '$'; theMacro->macroName.append(GrabNextToken(crossline)); theMacro->macroName.append('$'); //<-- Adding closing ($) to keep formatting consistant //Grab the macro string str tmpstr; tmpstr = GrabNextToken(crossline); //Check to see if we need return any defines strings if( ( tmpstr != "$include" ) && ( tmpstr[tmpstr.length() - 1] == '$' ) ) theMacro->macroText = GetMacroString(tmpstr); else theMacro->macroText = tmpstr; macrolist.AddObject( theMacro ); } /* ============== = = GetMacroString = ============== */ const char *Script::GetMacroString( const char *theMacroName ) { macro *theMacro =0; //Initialize this puppy for( int i = 1; i <= macrolist.NumObjects(); i++) { theMacro = macrolist.ObjectAt( i ); if(!theMacro->macroName.cmp(theMacro->macroName.c_str(), theMacroName)) { const char *text = theMacro->macroText.c_str(); // If our define value is another define... if( text[0] == '$' ) return EvaluateMacroString(text); else return text; } } char tmpstr[255], *sptr = tmpstr; strcpy(tmpstr, theMacroName); tmpstr[strlen(tmpstr)-1] = 0; sptr++; GameplayManager *gpm = GameplayManager::getTheGameplayManager(); if ( gpm->isDefined(sptr) ) return gpm->getDefine(sptr).c_str(); // We didn't find what we were looking for gi.Error( ERR_DROP, "No Macro Text found for %s in file %s\n", theMacroName, filename.c_str() ); return 0; } //================================================================ // Name: AddMacro // Class: Script // // Description: Adds a macro to the definitions list. // // Parameters: const char *name -- Name of the macro // const char *value -- Value // // Returns: None // //================================================================ void Script::AddMacro(const char *name, const char *value) { Q_UNUSED(name); Q_UNUSED(value); } /* ============== = = EvaluateMacroString = ============== */ char *Script::EvaluateMacroString( const char *theMacroString ) { static char evalText[255]; char buffer[255], *bufferptr = buffer, oper = '+', newoper = '+'; bool haveoper = false; float value = 0.0f, val = 0.0f; memset(buffer, 0, 255); for ( unsigned i=0;i<=strlen(theMacroString);i++ ) { if ( theMacroString[i] == '+' ) { haveoper = true; newoper = '+'; } if ( theMacroString[i] == '-' ) { haveoper = true; newoper = '-'; } if ( theMacroString[i] == '*' ) { haveoper = true; newoper = '*'; } if ( theMacroString[i] == '/' ) { haveoper = true; newoper = '/'; } if ( theMacroString[i] == 0 ) haveoper = true; if ( haveoper ) { if ( buffer[0] == '$' ) val = atof(GetMacroString(buffer)); else val = atof(buffer); value = EvaluateMacroMath(value, val, oper); oper = newoper; // Reset everything haveoper = false; memset(buffer, 0, 255); bufferptr = buffer; continue; } *bufferptr = theMacroString[i]; bufferptr++; } sprintf(evalText,"%f",value); return evalText; } /* ============== = = EvaluateMacroMath = ============== */ float Script::EvaluateMacroMath(float value, float newval, char oper) { switch ( oper ) { case '+' : value += newval; break; case '-' : value -= newval; break; case '*' : value *= newval; break; case '/' : value /= newval; break; } return value; } /* ============== = = isMacro = ============== */ qboolean Script::isMacro( void ) { if ( !TokenAvailable( true ) ) return false; SkipNonToken( true ); if ( *script_p == TOKENSPECIAL ) { return true; } return false; } /* ============== = = GetLine = ============== */ const char *Script::GetLine( qboolean crossline ) { const char *start; int size; // is a token already waiting? if ( tokenready ) { tokenready = false; return token; } // // skip space // SkipNonToken( crossline ); // // copy token // start = script_p; SkipToEOL(); size = script_p - start; if ( size < ( MAXTOKEN - 1 ) ) { memcpy( token, start, size ); token[ size ] = '\0'; } else { gi.Error( ERR_DROP, "Token too large on line %i in file %s\n", line, filename.c_str() ); } return token; } /* ============== = = GetRaw = ============== */ const char *Script::GetRaw( void ) { const char *start; int size; // // skip white space // SkipWhiteSpace( true ); // // copy token // start = script_p; SkipToEOL(); size = script_p - start; if ( size < ( MAXTOKEN - 1 ) ) { memset( token, 0, sizeof( token ) ); memcpy( token, start, size ); } else { gi.Error( ERR_DROP, "Token too large on line %i in file %s\n", line, filename.c_str() ); } return token; } /* ============== = = GetString = ============== */ const char *Script::GetString( qboolean crossline ) { int startline; char *token_p; // is a token already waiting? if ( tokenready ) { tokenready = false; return token; } // // skip space // SkipNonToken( crossline ); if ( *script_p != '"' ) { gi.Error( ERR_DROP, "Expecting string on line %i in file %s\n", line, filename.c_str() ); } script_p++; startline = line; token_p = token; while( *script_p != '"' ) { if ( *script_p == TOKENEOL ) { gi.Error( ERR_DROP, "Line %i is incomplete while reading string in file %s\n", line, filename.c_str() ); } if ( ( *script_p == '\\' ) && ( script_p < ( end_p - 1 ) ) ) { script_p++; switch( *script_p ) { case 'n' : *token_p++ = '\n'; break; case 'r' : *token_p++ = '\n'; break; case '\'' : *token_p++ = '\''; break; case '\"' : *token_p++ = '\"'; break; case '\\' : *token_p++ = '\\'; break; default: *token_p++ = *script_p; break; } script_p++; } else { *token_p++ = *script_p++; } if ( script_p >= end_p ) { gi.Error( ERR_DROP, "End of token file reached prematurely while reading string on\n" "line %d in file %s\n", startline, filename.c_str() ); } if ( token_p == &token[ MAXTOKEN ] ) { gi.Error( ERR_DROP, "String too large on line %i in file %s\n", line, filename.c_str() ); } } *token_p = 0; // skip last quote script_p++; return token; } /* ============== = = GetSpecific = ============== */ qboolean Script::GetSpecific( const char *string ) { do { if ( !TokenAvailable( true ) ) { return false; } GetToken( true ); } while( strcmp( token, string ) ); return true; } //=============================================================== // Name: GetBoolean // Class: Script // // Description: Retrieves the next boolean value in the token // stream. If the next token is either "true" // or "1", then it returns true. Otherwise, it // returns false. // // Parameters: qboolean -- determines if token parsing can cross newlines // // Returns: qboolean -- true if next token was "true" (or "1") // //=============================================================== qboolean Script::GetBoolean( qboolean crossline ) { GetToken( crossline ); if ( stricmp( token, "true" ) == 0 ) return qtrue ; else if ( stricmp( token, "1" ) == 0 ) return qtrue ; return qfalse ; } /* ============== = = GetInteger = ============== */ int Script::GetInteger( qboolean crossline ) { GetToken( crossline ); return atoi( token ); } /* ============== = = GetDouble = ============== */ double Script::GetDouble( qboolean crossline ) { GetToken( crossline ); return atof( token ); } /* ============== = = GetFloat = ============== */ float Script::GetFloat( qboolean crossline ) { return ( float )GetDouble( crossline ); } /* ============== = = GetVector = ============== */ Vector Script::GetVector( qboolean crossline ) { float x = GetFloat( crossline ); float y = GetFloat( crossline ); float z = GetFloat( crossline ); return Vector( x, y, z ); } /* =================== = = LinesInFile = =================== */ int Script::LinesInFile( void ) { qboolean temp_tokenready; const char *temp_script_p; int temp_line; char temp_token[ MAXTOKEN ]; int numentries; temp_tokenready = tokenready; temp_script_p = script_p; temp_line = line; strcpy( temp_token, token ); numentries = 0; Reset(); while( TokenAvailable( true ) ) { GetLine( true ); numentries++; } tokenready = temp_tokenready; script_p = temp_script_p; line = temp_line; strcpy( token, temp_token ); return numentries; } /* ============== = = Parse = ============== */ void Script::Parse( const char *data, int length, const char *name ) { Close(); buffer = data; Reset(); end_p = script_p + length; this->length = length; filename = name; } /* ============== = = Load = ============== */ void Script::LoadFile( const char *name ) { int length; byte *buffer; byte *tempbuf; const char *const_buffer; Close(); length = gi.FS_ReadFile( name, ( void ** )&tempbuf, true ); if ( length < 0 ) { error( "LoadFile", "Couldn't load %s\n", name ); } // create our own space buffer = ( byte * )gi.Malloc( length ); // copy the file over to our space memcpy( buffer, tempbuf, length ); // free the file gi.FS_FreeFile( tempbuf ); const_buffer = ( char * )buffer; Parse( const_buffer, length, name ); releaseBuffer = true; } const char *Script::Token( void ) { return token; }