//----------------------------------------------------------------------------- // // $Logfile:: /EF2/Code/DLLs/game/interpreter.cpp $ // $Revision:: 22 $ // $Date:: 9/26/03 2:36p $ // // Copyright (C) 1999 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: // #include "_pch_cpp.h" #include "interpreter.h" #include "program.h" #include "compiler.h" #include "scriptmaster.h" #include "scriptslave.h" #include "camera.h" CLASS_DECLARATION( Listener, Interpreter, NULL ) { { &EV_ScriptThread_Execute, &Interpreter::Execute }, { NULL, NULL } }; inline void Interpreter::Archive( Archiver &arc ) { int i; Listener::Archive( arc ); arc.ArchiveInteger( &pr_depth ); for ( i = 0; i < pr_depth; i++ ) { arc.ArchiveInteger( &pr_stack[i].s ); arc.ArchiveObjectPointer( ( Class ** )&pr_stack[i].f ); arc.ArchiveInteger( &pr_stack[i].stackbase ); } arc.ArchiveRaw( localstack_type, sizeof( localstack_type[0] ) * LOCALSTACK_SIZE ); arc.ArchiveRaw( localstack, sizeof( localstack[0] ) * LOCALSTACK_SIZE ); arc.ArchiveInteger( &localstack_used ); arc.ArchiveInteger( &localstack_base ); arc.ArchiveBool( &pr_trace ); arc.ArchiveObjectPointer( ( Class ** )&pr_xfunction ); arc.ArchiveInteger( &pr_xstatement ); // arc.ArchiveObjectPointer( ( Class ** )&program ); // shouldn't need this since only one program object arc.ArchiveInteger( &instruction_pointer ); arc.ArchiveBool( &doneProcessing ); arc.ArchiveBool( &threadDying ); updateList.Archive( arc ); arc.ArchiveInteger( &threadNum ); arc.ArchiveString( &threadName ); } /* ============ GlobalString Returns a string with a description and the contents of a global, padded to 20 field width ============ */ const char *Interpreter::GlobalString( int ofs ) { Q_UNUSED(ofs); /**************************************************************************** Squirrel : #if 0 / 1 block demoted to comment #if 0 char *s; int i; ddef_t *def; void *val; static char line[ 128 ]; val = ( void * )&pr_globals[ ofs ]; def = ED_GlobalAtOfs( ofs ); if (!def) sprintf (line,"%i(???)", ofs); else { s = PR_ValueString (def->type, val); sprintf (line,"%i(%s)%s", ofs, pr_strings + def->s_name, s); } i = strlen(line); for ( ; i<20 ; i++) strcat (line," "); strcat (line," "); return line; #else ****************************************************************************/ return ""; /**************************************************************************** Squirrel : #if 0 / 1 block demoted to comment #endif ****************************************************************************/ } const char *Interpreter::GlobalStringNoContents( int ofs ) { Q_UNUSED(ofs); /**************************************************************************** Squirrel : #if 0 / 1 block demoted to comment #if 0 int i; ddef_t *def; static char line[128]; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(???)", ofs); else sprintf (line,"%i(%s)", ofs, pr_strings + def->s_name); i = strlen(line); for ( ; i<20 ; i++) strcat (line," "); strcat (line," "); return line; #else ****************************************************************************/ return ""; /**************************************************************************** Squirrel : #if 0 / 1 block demoted to comment #endif ****************************************************************************/ } /* ================= PrintStatement ================= */ void Interpreter::PrintStatement( const dstatement_t *s ) { int i; if ( ( unsigned )s->op < ( sizeof( pr_opcodes ) / sizeof( pr_opcodes[ 0 ] ) ) ) { Com_Printf( "%s ", pr_opcodes[ s->op ].name ); i = strlen( pr_opcodes[ s->op ].name ); for( ; i < 10; i++ ) { Com_Printf( " " ); } } if ( ( s->op == OP_IF ) || ( s->op == OP_IFNOT ) ) { Com_Printf( "%sbranch %i", GlobalString( s->a ), s->b ); } else if ( s->op == OP_GOTO ) { Com_Printf( "branch %i", s->a ); } else if ( ( unsigned )( s->op - OP_STORE_F ) < 6 ) { Com_Printf( "%s", GlobalString( s->a ) ); Com_Printf( "%s", GlobalStringNoContents( s->b ) ); } else { if ( s->a ) { Com_Printf( "%s", GlobalString( s->a ) ); } if ( s->b ) { Com_Printf( "%s", GlobalString( s->b ) ); } if ( s->c ) { Com_Printf( "%s", GlobalStringNoContents( s->c ) ); } } Com_Printf( "\n" ); } /* ============ StackTrace ============ */ void Interpreter::StackTrace( void ) { dfunction_t *f; int i; // anything less than zero will be bad if ( pr_depth <= 0 ) { Com_Printf( "\n" ); return; } pr_stack[ pr_depth ].f = pr_xfunction; for( i = pr_depth; i >= 0; i-- ) { f = pr_stack[ i ].f; if ( !f ) { Com_Printf( "\n" ); } else { Com_Printf( "%12s : %s\n", f->s_file.c_str(), f->s_name.c_str() ); } } } /* ============ Profile ============ */ void Interpreter::Profile( void ) { dfunction_t *f; dfunction_t *best; int max; int num; int i; num = 0; do { max = 0; best = NULL; for( i = 0; i < program->numfunctions; i++ ) { f = &program->functions[ i ]; if ( f->profile > max ) { max = f->profile; best = f; } } if ( best ) { if ( num < 10 ) { Com_Printf( "%7i %s\n", best->profile, best->s_name.c_str() ); } num++; best->profile = 0; } } while( best ); } /* ============ RunError Aborts the currently executing function ============ */ void Interpreter::RunError( const char *error, ... ) { va_list argptr; char string[ 1024 ]; va_start( argptr, error ); vsprintf( string, error, argptr ); va_end( argptr ); //PrintStatement( program->statements + pr_xstatement ); StackTrace(); Com_Printf( "%s\n", string ); // dump the stack so host_error can shutdown functions pr_depth = 0; gi.Error( ERR_DROP, "Program error" ); } /* ==================== Interpreter::EntityError Prints an error message and stack trace when an entity error is found. ==================== */ inline void Interpreter::NullEntityError( const dstatement_t* statement ) { str filename( program->filenames[ statement->file - 1 ] ); Com_Printf( "===============================================\n\n" ); gi.WDPrintf( "Null entity referenced in %s on line %d\n", filename.c_str(), statement->linenumber ); Com_Printf( "Stack Trace follows:\n" ); StackTrace(); Com_Printf( "===============================================\n\n" ); } /* ==================== ThreadCall Copys the args from the calling thread's stack ==================== */ void Interpreter::ThreadCall( const Interpreter *source, dfunction_t *newf, int args ) { int start; int i; if ( scr_printfunccalls->integer ) gi.Printf( "thread call - %s (ignore previous and next func call)\n", newf->s_name.c_str() ); Reset(); start = source->localstack_used - args; for( i = 0; i < args; i++ ) { localstack_type[ i ] = source->localstack_type[ start + i ]; switch( localstack_type[ i ] ) { case ev_string : localstack[ i ] = program->AllocString(); program->strings[ localstack[ i ] ].s = program->strings[ source->localstack[ start + i ] ].s; break; case ev_vector : localstack[ i + 0 ] = source->localstack[ start + i + 0 ]; localstack[ i + 1 ] = source->localstack[ start + i + 1 ]; localstack[ i + 2 ] = source->localstack[ start + i + 2 ]; // set the type of the other two to float to be safe localstack_type[ i + 1 ] = ev_float; localstack_type[ i + 2 ] = ev_float; // only add 2 since for loop will add 1 i += 2; break; default : localstack[ i ] = source->localstack[ start + i ]; break; } } localstack_used = args; localstack_base = 0; instruction_pointer = EnterFunction( newf ); CancelEventsOfType( EV_ScriptThread_Execute ); PostEvent( EV_ScriptThread_Execute, 0.0f ); } /* ==================== EnterFunction Returns the new program statement counter ==================== */ int Interpreter::EnterFunction( dfunction_t *f ) { int i; int c; int off; int stackpos; etype_t type; if ( pr_depth >= MAX_STACK_DEPTH ) { RunError( "stack overflow" ); } if ( scr_printfunccalls->integer ) gi.Printf( "func call - %s\n", f->s_name.c_str() ); pr_stack[ pr_depth ].s = pr_xstatement; pr_stack[ pr_depth ].f = pr_xfunction; pr_stack[ pr_depth ].stackbase = localstack_base; pr_depth++; // allocate space on the stack for locals // parms are already on stack c = f->locals - f->parm_total; assert( c >= 0 ); if ( localstack_used + c > LOCALSTACK_SIZE ) { RunError( "EnterFuncton: locals stack overflow\n" ); } off = f->parm_start + f->parm_total; for( i = 0; i < c; i++ ) { stackpos = localstack_used + i; type = program->pr_global_defs[ off + i ]->type->type; localstack_type[ stackpos ] = type; switch( localstack_type[ stackpos ] ) { case ev_string : localstack[ stackpos ] = program->AllocString(); break; case ev_vector : localstack[ stackpos + 0 ] = program->getInteger( off + i + 0 ); localstack[ stackpos + 1 ] = program->getInteger( off + i + 1 ); localstack[ stackpos + 2 ] = program->getInteger( off + i + 2 ); // set the type of the other two to float to be safe localstack_type[ stackpos + 1 ] = ev_float; localstack_type[ stackpos + 2 ] = ev_float; // only add 2 since for loop will add 1 i += 2; break; default : localstack[ stackpos ] = program->getInteger( off + i ); break; } } localstack_used += c; localstack_base = localstack_used - f->locals - 1; pr_xfunction = f; // offset the s++ return f->first_statement - 1; } /* ==================== CleanupStack ==================== */ void Interpreter::CleanupStack( int localstack_used, int oldstacktop ) { int i; // delete any strings that were on the stack for( i = localstack_used; i < oldstacktop; i++ ) { if ( localstack_type[ i ] == ev_string ) program->FreeString( localstack[ i ] ); localstack_type[ i ] = ev_void; } } /* ==================== LeaveFunction ==================== */ int Interpreter::LeaveFunction( void ) { int c; int oldstacktop; if ( pr_depth <= 0 ) { gi.Error( ERR_DROP, "prog stack underflow" ); } // remove locals from the stack c = pr_xfunction->locals; oldstacktop = localstack_used; localstack_used -= c; if ( localstack_used < 0 ) { RunError( "LeaveFunction: locals stack underflow\n" ); } assert( localstack_used == ( localstack_base + 1 ) ); CleanupStack( localstack_used, oldstacktop ); // up stack pr_depth--; pr_xfunction = pr_stack[ pr_depth ].f; localstack_base = pr_stack[ pr_depth ].stackbase; return pr_stack[ pr_depth ].s; } const char *param_types[] = {"void", "string", "float", "vector", "entity", "function" }; Event *Interpreter::EventForFunction( const dfunction_t *func, int args ) { Event *ev; int i; eval_t *st; int pos; int start; ev = new Event( func->eventnum ); ev->SetSource( EV_FROM_SCRIPT ); ev->SetThread( ( CThread * )this ); ev->SetLineNumber( program->statements[ instruction_pointer ].linenumber ); start = localstack_used - args; for( i = 0, pos = 0; pos < args; i++ ) { st = ( eval_t * )&localstack[ start + pos ]; // remove this for shipping product. if ( (func->parm_type[i] != localstack_type[start + pos]) && (func->parm_type[i] != ev_vector) && (localstack_type[start + pos] != ev_float) ) { RunError ( "===============================\n" "Type mismatch on line %d. Func %s expected %s, got %s\n" "===============================\n", program->statements[ instruction_pointer ].linenumber, func->s_name.c_str(), param_types[func->parm_type[i]], param_types[localstack_type[start + pos]] ); } switch( func->parm_type[ i ] ) { case ev_string : ev->AddString( program->strings[ st->string ].s ); break; case ev_float : ev->AddFloat( st->_float ); break; case ev_vector : ev->AddVector( Vector( st->vector ) ); break; case ev_entity : if ( st->entity > 0 ) { if ( st->entity >= game.maxentities ) { gi.WDPrintf( "Bad entity number %d from line %d in the script\n", st->entity - 1, program->statements[ instruction_pointer ].linenumber ); } else { ev->AddEntity( G_GetEntity( st->entity - 1 ) ); } } else if ( st->entity < 0 ) { TargetList *list; list = world->GetTargetList( -st->entity ); if ( list ) { ev->AddEntity( list->GetNextEntity( NULL ) ); } else { ev->AddEntity( NULL ); } } else { ev->AddEntity( NULL ); } break; default: RunError( "Bad type on builtin call" ); } pos += func->parm_size[ i ]; } return ev; } void Interpreter::DoMove( void ) { } /* ==================== ExecuteProgram ==================== */ void Interpreter::Execute( Event * ) { eval_t *a; eval_t *b; eval_t *c; dstatement_t *st; dfunction_t *newf; int runaway; int exitdepth; Listener *obj; Entity *ent; Event *ev; char text[ 128 ]; int i; int n; TargetList *list; int oldstacktop; Interpreter *newThread; int stridx; if ( threadDying ) { return; } runaway = 100000; // clear the updateList so that all objects moved this frame are notified before they receive any commands // we have to do this here as well as in DoMove, since DoMove may not be called updateList.ClearObjectList(); // make a stack frame exitdepth = 0; doneProcessing = false; while( !doneProcessing && !threadDying ) { // next statement instruction_pointer++; st = &program->statements[ instruction_pointer ]; if ( st->a < 0 ) { a = ( eval_t * )&localstack[ localstack_base - st->a ]; } else { a = ( eval_t * )&program->pr_globals[ st->a ]; } if ( st->b < 0 ) { b = ( eval_t * )&localstack[ localstack_base - st->b ]; } else { b = ( eval_t * )&program->pr_globals[ st->b ]; } if ( st->c < 0 ) { c = ( eval_t * )&localstack[ localstack_base - st->c ]; } else { c = ( eval_t * )&program->pr_globals[ st->c ]; } if ( !--runaway ) { RunError( "runaway loop error" ); } pr_xfunction->profile++; pr_xstatement = instruction_pointer; if ( pr_trace ) { PrintStatement( st ); } switch( st->op ) { case OP_ADD_F: c->_float = a->_float + b->_float; break; case OP_ADD_V: VectorAdd( a->vector, b->vector, c->vector ); break; case OP_ADD_S: program->strings[ c->string ].s = program->strings[ a->string ].s + program->strings[ b->string ].s; break; case OP_ADD_FS: sprintf( text, "%g", a->_float ); program->strings[ c->string ].s = text + program->strings[ b->string ].s; break; case OP_ADD_SF: sprintf( text, "%g", b->_float ); program->strings[ c->string ].s = program->strings[ a->string ].s + text; break; case OP_ADD_VS: sprintf( text, "(%g %g %g)", a->vector[0], a->vector[1], a->vector[2]); program->strings[ c->string ].s = text + program->strings[ b->string ].s; break; case OP_ADD_SV: sprintf( text, "(%g %g %g)", b->vector[0], b->vector[1], b->vector[2]); program->strings[ c->string ].s = program->strings[ a->string ].s + text ; break ; case OP_SUB_F: c->_float = a->_float - b->_float; break; case OP_SUB_V: VectorSubtract( a->vector, b->vector, c->vector ); break; case OP_MUL_F: c->_float = a->_float * b->_float; break; case OP_MUL_V: c->_float = DotProduct( a->vector, b->vector ); break; case OP_MUL_FV: VectorScale( b->vector, a->_float, c->vector ); break; case OP_MUL_VF: VectorScale( a->vector, b->_float, c->vector ); break; case OP_DIV_F: if ( b->_float != 0.0f ) { c->_float = a->_float / b->_float; } else { assert( 0 ); c->_float = 0; } break; case OP_BITAND: c->_float = ( int )a->_float & ( int )b->_float; break; case OP_BITOR: c->_float = ( int )a->_float | ( int )b->_float; break; case OP_GE: c->_float = a->_float >= b->_float; break; case OP_LE: c->_float = a->_float <= b->_float; break; case OP_GT: c->_float = a->_float > b->_float; break; case OP_LT: c->_float = a->_float < b->_float; break; case OP_AND: c->_float = a->_float && b->_float; break; case OP_OR: c->_float = a->_float || b->_float; break; case OP_NOT_F: c->_float = !a->_float; break; case OP_NOT_V: c->_float = !a->vector[ 0 ] && !a->vector[ 1 ] && !a->vector[ 2 ]; break; case OP_NOT_S: c->_float = !&program->strings[ a->string ].s || ( program->strings[ a->string ].s.length() == 0 ); break; case OP_NOT_FNC: c->_float = !a->function; break; case OP_NOT_ENT: c->_float = !a->entity; break; case OP_EQ_F: c->_float = ( a->_float == b->_float ); break; case OP_EQ_V: c->_float = ( a->vector[ 0 ] == b->vector[ 0 ] ) && ( a->vector[ 1 ] == b->vector[ 1 ] ) && ( a->vector[ 2 ] == b->vector[ 2 ] ); break; case OP_EQ_S: c->_float = !program->strings[ a->string ].s.cmp( program->strings[ b->string ].s ); break; case OP_EQ_E: c->_float = ( a->_int == b->_int ); break; case OP_EQ_FNC: c->_float = ( a->function == b->function ); break; case OP_NE_F: c->_float = ( a->_float != b->_float ); break; case OP_NE_V: c->_float = ( a->vector[ 0 ] != b->vector[ 0 ] ) || ( a->vector[ 1 ] != b->vector[ 1 ] ) || ( a->vector[ 2 ] != b->vector[ 2 ] ); break; case OP_NE_S: c->_float = program->strings[ a->string ].s.cmp( program->strings[ b->string ].s ); break; case OP_NE_E: c->_float = ( a->_int != b->_int ); break; case OP_NE_FNC: c->_float = ( a->function != b->function ); break; case OP_UADD_F : b->_float += a->_float; break; case OP_USUB_F : b->_float -= a->_float; break; case OP_UMUL_F : b->_float *= a->_float; break; case OP_UDIV_F : if ( a->_float != 0.0f ) { b->_float /= a->_float; } else { assert( 0 ); b->_float = 0; } break; case OP_UOR_F : b->_float = ( int )b->_float | ( int )a->_float; break; case OP_UAND_F : b->_float = ( int )b->_float & ( int )a->_float; break; case OP_UINC_F : a->_float++; break; case OP_UDEC_F : a->_float--; break; case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FNC: // pointers b->_int = a->_int; break; case OP_STORE_S: if ( st->b == OFS_RETURN ) { // always use a static string for return values so that we // don't have to worry about freeing it up b->string = 0; } program->strings[ b->string ] = program->strings[ a->string ]; break; case OP_STORE_V: VectorCopy( a->vector, b->vector ); break; case OP_STORE_FTOS: if ( st->b == OFS_RETURN ) { // always use a static string for return values so that we // don't have to worry about freeing it up b->string = 0; } if ( a->_float == ( float )( int )a->_float ) { sprintf( text, "%d", ( int )a->_float ); } else { sprintf( text, "%f", a->_float ); } program->strings[ b->string ].s = text; break; case OP_IFNOT: if ( !a->_int ) { // offset the instruction_pointer++ instruction_pointer += st->b - 1; } break; case OP_IF: if ( a->_int ) { // offset the instruction_pointer++ instruction_pointer += st->b - 1; } break; case OP_GOTO: // offset the instruction_pointer++ instruction_pointer += st->a - 1; break; case OP_THREAD: if ( !a->function ) { RunError( "NULL function" ); } newf = &program->functions[ a->function ]; newThread = Director.CreateThread( newf->s_name ); newThread->ThreadCall( this, newf, st->b ); // return the thread number to the script program->pr_globals[ OFS_RETURN ] = newThread->ThreadNum(); program->pr_globals[ OFS_RETURN + 1 ] = 0; program->pr_globals[ OFS_RETURN + 2 ] = 0; // pop our parms off the stack oldstacktop = localstack_used; localstack_used -= st->b; if ( localstack_used < 0 ) { RunError( "Execute: locals stack underflow\n" ); } CleanupStack( localstack_used, oldstacktop ); break; case OP_CALL: if ( !a->function ) { RunError( "NULL function" ); } newf = &program->functions[ a->function ]; if ( newf->first_statement < 0 ) { // negative statements are events if ( ValidEvent( newf->eventnum ) ) { ev = EventForFunction( newf, st->b ); ProcessEvent( ev ); if ( scr_printeventcalls->integer ) gi.Printf( "event call - %s\n", ev->getName() ); } // pop our parms off the stack oldstacktop = localstack_used; localstack_used -= st->b; if ( localstack_used < 0 ) { RunError( "Execute: locals stack underflow\n" ); } CleanupStack( localstack_used, oldstacktop ); break; } instruction_pointer = EnterFunction( newf ); break; case OP_OCALL: if ( !a->function ) { RunError( "NULL function" ); } newf = &program->functions[ a->function ]; assert( newf->first_statement < 0 ); if ( ( st->b >= RESERVED_OFS ) && ( st->b < OFS_END ) ) { switch( st->b ) { case OFS_CAM : obj = &CameraMan; break; default : obj = NULL; // shutup compiler RunError( "Execute: Invalid object call\n" ); break; } if ( obj->ValidEvent( newf->eventnum ) ) { ev = EventForFunction( newf, st->c ); obj->ProcessEvent( ev ); if ( scr_printeventcalls->integer ) gi.Printf( "event call - %s\n", ev->getName() ); } } else if ( b->entity > 0 ) { ent = G_GetEntity( b->entity - 1 ); if ( ent && ent->ValidEvent( newf->eventnum ) ) { if ( !updateList.ObjectInList( ent->entnum ) ) { updateList.AddObject( ent->entnum ); // Tell the object that we're about to send it some orders ent->ProcessEvent( EV_ScriptSlave_NewOrders ); } ev = EventForFunction( newf, st->c ); ent->ProcessEvent( ev ); if ( scr_printeventcalls->integer ) gi.Printf( "event call - %s.%s\n", ent->targetname.c_str(), ev->getName() ); } } else if ( b->entity < 0 ) { list = world->GetTargetList( -b->entity ); if ( !list ) { NullEntityError( st ); } if ( list ) { n = list->list.NumObjects(); if ( !n ) { NullEntityError( st ); } if ( n ) { ev = EventForFunction( newf, st->c ); for( i = 1; i < n; i++ ) { ent = list->list.ObjectAt( i ); if ( ent->ValidEvent( newf->eventnum ) ) { if ( !updateList.ObjectInList( ent->entnum ) ) { updateList.AddObject( ent->entnum ); // Tell the object that we're about to send it some orders ent->ProcessEvent( EV_ScriptSlave_NewOrders ); } ent->ProcessEvent( new Event( ev ) ); if ( scr_printeventcalls->integer ) gi.Printf( "event call - %s.%s\n", ent->targetname.c_str(), ev->getName() ); } else { ev->Error( "Entity '%s'(%d) cannot process event %s\n", ent->targetname.c_str(), ent->entnum, ev->getName() ); } } ent = list->list.ObjectAt( i ); if ( ent->ValidEvent( newf->eventnum ) ) { if ( !updateList.ObjectInList( ent->entnum ) ) { updateList.AddObject( ent->entnum ); // Tell the object that we're about to send it some orders ent->ProcessEvent( EV_ScriptSlave_NewOrders ); } ent->ProcessEvent( ev ); if ( scr_printeventcalls->integer ) gi.Printf( "event call - %s.%s\n", ent->targetname.c_str(), ev->getName() ); } else { ev->Error( "Entity '%s'(%d) cannot process event %s\n", ent->targetname.c_str(), ent->entnum, ev->getName() ); delete ev; } } } } else { NullEntityError( st ); } // pop our parms off the stack oldstacktop = localstack_used; localstack_used -= st->c; if ( localstack_used < 0 ) { RunError( "Execute: locals stack underflow\n" ); } CleanupStack( localstack_used, oldstacktop ); break; case OP_PUSH_F : if ( localstack_used >= LOCALSTACK_SIZE ) { RunError( "Execute: locals stack overflow\n" ); } localstack_type[ localstack_used ] = ev_float; localstack[ localstack_used++ ] = a->_int; break; case OP_PUSH_FTOS : if ( localstack_used >= LOCALSTACK_SIZE ) { RunError( "Execute: locals stack overflow\n" ); } if ( a->_float == ( float )( int )a->_float ) { sprintf( text, "%d", ( int )a->_float ); } else { sprintf( text, "%f", a->_float ); } localstack_type[ localstack_used ] = ev_string; stridx = program->AllocString(); program->strings[ stridx ].s = text; localstack[ localstack_used++ ] = ( int )stridx; break; case OP_PUSH_ENT : if ( localstack_used >= LOCALSTACK_SIZE ) { RunError( "Execute: locals stack overflow\n" ); } localstack_type[ localstack_used ] = ev_entity; localstack[ localstack_used++ ] = a->_int; break; case OP_PUSH_FNC : if ( localstack_used >= LOCALSTACK_SIZE ) { RunError( "Execute: locals stack overflow\n" ); } localstack_type[ localstack_used ] = ev_function; localstack[ localstack_used++ ] = a->_int; break; case OP_PUSH_S : if ( localstack_used >= LOCALSTACK_SIZE ) { RunError( "Execute: locals stack overflow\n" ); } localstack_type[ localstack_used ] = ev_string; stridx = program->AllocString(); program->strings[ stridx ].s = program->strings[ a->string ].s; localstack[ localstack_used++ ] = ( int )stridx; break; case OP_PUSH_V : if ( localstack_used + 3 > LOCALSTACK_SIZE ) { RunError( "Execute: locals stack overflow\n" ); } localstack_type[ localstack_used ] = ev_float; localstack[ localstack_used++ ] = *( int * )&a->vector[ 0 ]; localstack_type[ localstack_used ] = ev_float; localstack[ localstack_used++ ] = *( int * )&a->vector[ 1 ]; localstack_type[ localstack_used ] = ev_float; localstack[ localstack_used++ ] = *( int * )&a->vector[ 2 ]; break; case OP_DONE: case OP_RETURN: if ( st->a < 0 ) { if ( localstack_type[ localstack_base - st->a ] == ev_string ) { // always use a static string for return values so that we // don't have to worry about freeing it up program->pr_globals[ OFS_RETURN ] = 0; } else { // wacky casting to prevent conversion from int to float program->pr_globals[ OFS_RETURN ] = *(float *)&localstack[ localstack_base - st->a ]; program->pr_globals[ OFS_RETURN + 1 ] = *(float *)&localstack[ localstack_base - st->a + 1 ]; program->pr_globals[ OFS_RETURN + 2 ] = *(float *)&localstack[ localstack_base - st->a + 2 ]; } } else if ( st->a != 0 ) { program->pr_globals[ OFS_RETURN ] = program->pr_globals[ st->a ]; program->pr_globals[ OFS_RETURN + 1 ] = program->pr_globals[ st->a + 1 ]; program->pr_globals[ OFS_RETURN + 2 ] = program->pr_globals[ st->a + 2 ]; } instruction_pointer = LeaveFunction(); if ( pr_depth == exitdepth ) { // all done DoMove(); Director.NotifyOtherThreads( threadNum ); PostEvent( EV_Remove, 0.0f ); doneProcessing = true; threadDying = true; } break; default: RunError( "Bad opcode %i", st->op ); } } } void Interpreter::Reset( void ) { CleanupStack( 0, localstack_used ); pr_depth = 0; localstack_used = 0; localstack_base = 0; pr_trace = false; pr_xfunction = NULL; pr_xstatement = -1; threadDying = false; doneProcessing = true; memset( localstack, 0, sizeof( localstack ) ); memset( pr_stack, 0, sizeof( pr_stack ) ); } Interpreter::Interpreter() { localstack_used = 0; // these need to be here for cleaupStack localstack_base = 0; threadNum = 0; program = &::program; Reset(); } Interpreter::~Interpreter() { Director.NotifyOtherThreads( threadNum ); Director.RemoveThread( threadNum ); CleanupStack( 0, localstack_used ); } qboolean Interpreter::labelExists( const char *label ) { func_t func; func = program->findFunction( label ); if ( func < 0 || ( func >= program->numfunctions ) ) { return false; } return true; } qboolean Interpreter::Goto( const char *label ) { func_t func; func = program->findFunction( label ); if ( ( func < 0 ) || ( func >= program->numfunctions ) ) { gi.WDPrintf( "Can't find function '%s' in program\n", label ); return false; } instruction_pointer = EnterFunction( &program->functions[ func ] ); CancelEventsOfType( EV_ScriptThread_Execute ); PostEvent( EV_ScriptThread_Execute, 0.0f ); return true; } qboolean Interpreter::Setup( int num, const char *label ) { Reset(); threadNum = num; threadName = label; return Goto( label ); }