//----------------------------------------------------------------------------- // // $Logfile:: /Quake 2 Engine/Sin/code/game/script.cpp $ // $Revision:: 13 $ // $Author:: Jimdose $ // $Date:: 10/07/98 11:58p $ // // 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. // // $Log:: /Quake 2 Engine/Sin/code/game/script.cpp $ // // 13 10/07/98 11:58p Jimdose // changed script_t to offset in script markers // // 12 6/09/98 4:21p Jimdose // Added close // filename is now a str // // 11 5/24/98 5:25p Jimdose // Changed char *'s to const char *'s // // 10 3/26/98 3:57p Jimdose // Made gettoken and getstring handle escape sequences (\n, \", etc.) // // 9 3/23/98 1:31p Jimdose // Revamped event and command system // // 8 3/02/98 8:49p Jimdose // Changed the classid parameter of CLASS_DECLARATION to a quoted string so // that you could have a NULL classid. // // 7 2/17/98 7:13p Jimdose // Added MarkPosition and RestorePosition // Added atString for determining if the next token is a quoted string // Made GetToken call GetString if the next token is a quoted string // Fixed bug where GetSpecific wasn't properly updating the current line number // // 6 2/03/98 10:54a Jimdose // Updated to work with Quake 2 engine // // 4 11/24/97 4:30p Markd // Added GetVector // // 3 10/27/97 3:29p Jimdose // Removed dependency on quakedef.h // // 2 9/26/97 6:14p Jimdose // Added standard Ritual headers // // 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 "g_local.h" #include "script.h" #define TOKENCOMMENT (';') #define TOKENCOMMENT2 ('#') #define TOKENEOL ('\n') #define TOKENNULL ('\0') #define TOKENSPACE (' ') CLASS_DECLARATION( Class, Script, NULL ); ResponseDef Script::Responses[] = { { NULL, NULL } }; Script::~Script() { Close(); } Script::Script() { buffer = NULL; script_p = NULL; end_p = NULL; line = 0; releaseBuffer = false; tokenready = false; memset( token, 0, sizeof( token ) ); } void Script::Close ( void ) { if ( releaseBuffer && buffer ) { gi.TagFree( ( void * )buffer ); } buffer = NULL; script_p = NULL; end_p = NULL; line = 0; releaseBuffer = false; tokenready = false; memset( token, 0, sizeof( token ) ); } /* ============== = = 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 ( 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( "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( "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==false ) { 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 == '"' ); } /* ============== = = Get = ============== */ const char *Script::GetToken ( qboolean crossline ) { char *token_p; // is a token already waiting? if ( tokenready ) { tokenready = false; return token; } // // 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( "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; } /* ============== = = 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( "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( "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( "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( "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( "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( "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; } /* ============== = = 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 ) { return Vector( GetFloat( crossline ), GetFloat( crossline ), GetFloat( crossline ) ); } /* =================== = = 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; const char *buffer; Close(); length = gi.LoadFile( name, ( void ** )&buffer, TAG_GAME ); if ( length < 0 ) { error( "LoadFile", "Couldn't load %s\n", name ); } Parse( buffer, length, name ); releaseBuffer = true; } const char *Script::Token ( void ) { return token; }