mirror of
https://github.com/ioquake/jedi-academy.git
synced 2024-11-29 07:22:23 +00:00
832b4342a8
CVE-2006-2236 Buffer overflow in the Quake 3 Engine, as used by (1) ET 2.60, (2) Return to Castle Wolfenstein 1.41, and (3) Quake III Arena 1.32b allows remote attackers to execute arbitrary commands via a long remapShader command. from Thilo Schulz in ioquake3 svn 765 git d21411452ef32b86c0b79ddcaf49221701dcdb07 Add string length checking to function COM_StripExtension. This fixes the R_RemapShader buffer overflow exploit that can be found here: http://milw0rm.com/exploits/1750
733 lines
17 KiB
C++
733 lines
17 KiB
C++
//Anything above this #include will be ignored by the compiler
|
|
#include "../qcommon/exe_headers.h"
|
|
|
|
// ICARUS Utility functions
|
|
//rww - mangled to work in server exe setting.
|
|
|
|
//#include "Q3_Interface.h"
|
|
//#include "g_roff.h"
|
|
#include "../game/g_public.h"
|
|
#include "../server/server.h"
|
|
#include "interface.h"
|
|
#include "GameInterface.h"
|
|
#include "../qcommon/RoffSystem.h"
|
|
#include "Q3_Interface.h"
|
|
|
|
ICARUS_Instance *iICARUS;
|
|
bufferlist_t ICARUS_BufferList;
|
|
entlist_t ICARUS_EntList;
|
|
|
|
extern unsigned Com_BlockChecksum (const void *buffer, int length);
|
|
extern void Q3_DebugPrint( int level, const char *format, ... );
|
|
|
|
int ICARUS_entFilter = -1;
|
|
|
|
/*
|
|
=============
|
|
ICARUS_GetScript
|
|
|
|
gets the named script from the cache or disk if not already loaded
|
|
=============
|
|
*/
|
|
|
|
int ICARUS_GetScript( const char *name, char **buf )
|
|
{
|
|
bufferlist_t::iterator ei;
|
|
//Make sure the caller is valid
|
|
|
|
//Attempt to retrieve a precached script
|
|
ei = ICARUS_BufferList.find( (char *) name );
|
|
|
|
//Not found, check the disk
|
|
if ( ei == ICARUS_BufferList.end() )
|
|
{
|
|
if ( ICARUS_RegisterScript( name ) == false )
|
|
return 0;
|
|
|
|
//Script is now inserted, retrieve it and pass through
|
|
ei = ICARUS_BufferList.find( (char *) name );
|
|
|
|
if ( ei == ICARUS_BufferList.end() )
|
|
{
|
|
//NOTENOTE: This is an internal error in STL if this happens...
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
*buf = (*ei).second->buffer;
|
|
return (*ei).second->length;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ICARUS_RunScript
|
|
|
|
Runs the script by the given name
|
|
=============
|
|
*/
|
|
int ICARUS_RunScript( sharedEntity_t *ent, const char *name )
|
|
{
|
|
char *buf;
|
|
int len;
|
|
|
|
//Make sure the caller is valid
|
|
if ( gSequencers[ent->s.number] == NULL )
|
|
{
|
|
//Com_Printf( "%s : entity is not a valid script user\n", ent->classname );
|
|
return false;
|
|
}
|
|
#ifdef _HACK_FOR_TESTING_ONLY_1
|
|
char namex[1024];
|
|
char *blah = strstr(name, "stu/");
|
|
int r = blah - name;
|
|
if (blah)
|
|
{
|
|
int i = 0;
|
|
while (i < r)
|
|
{
|
|
namex[i] = name[i];
|
|
i++;
|
|
}
|
|
namex[i] = 0;
|
|
strcat(namex, "ignorethisfolder/");
|
|
i = strlen(namex);
|
|
while (name[r] != '/')
|
|
{
|
|
r++;
|
|
}
|
|
r++;
|
|
|
|
while (name[r])
|
|
{
|
|
namex[i] = name[r];
|
|
r++;
|
|
i++;
|
|
}
|
|
namex[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(namex, name);
|
|
}
|
|
|
|
len = ICARUS_GetScript (namex, &buf);
|
|
#else
|
|
len = ICARUS_GetScript (name, &buf);
|
|
#endif
|
|
if (len == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Attempt to run the script
|
|
if S_FAILED(gSequencers[ent->s.number]->Run( buf, len ))
|
|
return false;
|
|
|
|
if ( ( ICARUS_entFilter == -1 ) || ( ICARUS_entFilter == ent->s.number ) )
|
|
{
|
|
Q3_DebugPrint( WL_VERBOSE, "%d Script %s executed by %s %s\n", svs.time, (char *) name, ent->classname, ent->targetname );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
ICARUS_Init
|
|
|
|
Allocates a new ICARUS instance
|
|
=================
|
|
*/
|
|
|
|
void ICARUS_Init( void )
|
|
{
|
|
//Link all interface functions
|
|
Interface_Init( &interface_export );
|
|
|
|
//Create the ICARUS instance for this session
|
|
iICARUS = ICARUS_Instance::Create( &interface_export );
|
|
|
|
if ( iICARUS == NULL )
|
|
{
|
|
Com_Error( ERR_DROP, "Unable to initialize ICARUS instance\n" );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
ICARUS_Shutdown
|
|
|
|
Frees up ICARUS resources from all entities
|
|
=================
|
|
*/
|
|
|
|
void ICARUS_Shutdown( void )
|
|
{
|
|
bufferlist_t::iterator ei;
|
|
sharedEntity_t *ent = SV_GentityNum(0);
|
|
|
|
//Release all ICARUS resources from the entities
|
|
for ( int i = 0; i < /*globals.num_entities*/MAX_GENTITIES; i++ )
|
|
{
|
|
ent = SV_GentityNum(i);
|
|
|
|
if (gSequencers[i])
|
|
{
|
|
if (ent->s.number >= MAX_GENTITIES ||
|
|
ent->s.number < 0)
|
|
{
|
|
ent->s.number = i;
|
|
assert(0);
|
|
}
|
|
ICARUS_FreeEnt( ent );
|
|
}
|
|
}
|
|
|
|
//Clear out all precached scripts
|
|
for ( ei = ICARUS_BufferList.begin(); ei != ICARUS_BufferList.end(); ei++ )
|
|
{
|
|
//gi.Free( (*ei).second->buffer );
|
|
ICARUS_Free((*ei).second->buffer);
|
|
delete (*ei).second;
|
|
}
|
|
|
|
ICARUS_BufferList.clear();
|
|
|
|
//Clear the name map
|
|
ICARUS_EntList.clear();
|
|
|
|
//Free this instance
|
|
if ( iICARUS )
|
|
{
|
|
iICARUS->Delete();
|
|
iICARUS = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ICARUS_FreeEnt
|
|
|
|
Frees all ICARUS resources on an entity
|
|
|
|
WARNING!!! DO NOT DO THIS WHILE RUNNING A SCRIPT, ICARUS WILL CRASH!!!
|
|
FIXME: shouldn't ICARUS handle this internally?
|
|
|
|
==============
|
|
*/
|
|
void ICARUS_FreeEnt( sharedEntity_t *ent )
|
|
{
|
|
assert( iICARUS );
|
|
|
|
if (ent->s.number >= MAX_GENTITIES ||
|
|
ent->s.number < 0)
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
//Make sure the ent is valid
|
|
if ( gSequencers[ent->s.number] == NULL )
|
|
return;
|
|
|
|
//Remove them from the ICARUSE_EntList list so that when their g_entity index is reused, ICARUS doesn't try to affect the new (incorrect) ent.
|
|
if VALIDSTRING( ent->script_targetname )
|
|
{
|
|
char temp[1024];
|
|
|
|
strncpy( (char *) temp, ent->script_targetname, 1023 );
|
|
temp[ 1023 ] = 0;
|
|
|
|
entlist_t::iterator it = ICARUS_EntList.find( Q_strupr(temp) );
|
|
|
|
if (it != ICARUS_EntList.end())
|
|
{
|
|
ICARUS_EntList.erase(it);
|
|
}
|
|
}
|
|
|
|
//Delete the sequencer and the task manager
|
|
iICARUS->DeleteSequencer( gSequencers[ent->s.number] );
|
|
|
|
//Clean up the pointers
|
|
gSequencers[ent->s.number] = NULL;
|
|
gTaskManagers[ent->s.number] = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
ICARUS_ValidEnt
|
|
|
|
Determines whether or not an entity needs ICARUS information
|
|
==============
|
|
*/
|
|
|
|
bool ICARUS_ValidEnt( sharedEntity_t *ent )
|
|
{
|
|
int i;
|
|
|
|
//Targeted by a script
|
|
if VALIDSTRING( ent->script_targetname )
|
|
return true;
|
|
|
|
//Potentially able to call a script
|
|
for ( i = 0; i < NUM_BSETS; i++ )
|
|
{
|
|
if VALIDSTRING( ent->behaviorSet[i] )
|
|
{
|
|
//Com_Printf( "WARNING: Entity %d (%s) has behaviorSet but no script_targetname -- using targetname\n", ent->s.number, ent->targetname );
|
|
|
|
//ent->script_targetname = ent->targetname;
|
|
//rww - You CANNOT do things like this now. We're switching memory around to be able to read this memory from vm land,
|
|
//and while this allows us to read it on our "fake" entity here, we can't modify pointers like this. We can however do
|
|
//something completely hackish such as the following.
|
|
assert(ent->s.number >= 0 && ent->s.number < MAX_GENTITIES);
|
|
sharedEntity_t *trueEntity = SV_GentityNum(ent->s.number);
|
|
//This works because we're modifying the actual shared game vm data and turning one pointer into another.
|
|
//While these pointers both look like garbage to us in here, they are not.
|
|
trueEntity->script_targetname = trueEntity->targetname;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ICARUS_AssociateEnt
|
|
|
|
Associate the entity's id and name so that it can be referenced later
|
|
==============
|
|
*/
|
|
|
|
void ICARUS_AssociateEnt( sharedEntity_t *ent )
|
|
{
|
|
char temp[1024];
|
|
|
|
if ( VALIDSTRING( ent->script_targetname ) == false )
|
|
return;
|
|
|
|
strncpy( (char *) temp, ent->script_targetname, 1023 );
|
|
temp[ 1023 ] = 0;
|
|
|
|
ICARUS_EntList[ Q_strupr( (char *) temp ) ] = ent->s.number;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ICARUS_RegisterScript
|
|
|
|
Loads and caches a script
|
|
==============
|
|
*/
|
|
|
|
bool ICARUS_RegisterScript( const char *name, qboolean bCalledDuringInterrogate /* = false */ )
|
|
{
|
|
bufferlist_t::iterator ei;
|
|
pscript_t *pscript;
|
|
char newname[MAX_FILENAME_LENGTH];
|
|
char *buffer = NULL; // lose compiler warning about uninitialised vars
|
|
long length;
|
|
|
|
//Make sure this isn't already cached
|
|
ei = ICARUS_BufferList.find( (char *) name );
|
|
|
|
// note special return condition here, if doing interrogate and we already have this file then we MUST return
|
|
// false (which stops the interrogator proceeding), this not only saves some time, but stops a potential
|
|
// script recursion bug which could lock the program in an infinite loop... Return TRUE for normal though!
|
|
//
|
|
if ( ei != ICARUS_BufferList.end() )
|
|
return (bCalledDuringInterrogate)?false:true;
|
|
|
|
sprintf((char *) newname, "%s%s", name, IBI_EXT );
|
|
|
|
|
|
// small update here, if called during interrogate, don't let gi.FS_ReadFile() complain because it can't
|
|
// find stuff like BS_RUN_AND_SHOOT as scriptname... During FINALBUILD the message won't appear anyway, hence
|
|
// the ifndef, this just cuts down on internal error reports while testing release mode...
|
|
//
|
|
qboolean qbIgnoreFileRead = qfalse;
|
|
//
|
|
// NOTENOTE: For the moment I've taken this back out, to avoid doubling the number of fopen()'s per file.
|
|
#if 0//#ifndef FINAL_BUILD
|
|
if (bCalledDuringInterrogate)
|
|
{
|
|
fileHandle_t file;
|
|
|
|
gi.FS_FOpenFile( newname, &file, FS_READ );
|
|
|
|
if ( file == NULL )
|
|
{
|
|
qbIgnoreFileRead = qtrue; // warn disk code further down not to try FS_ReadFile()
|
|
}
|
|
else
|
|
{
|
|
gi.FS_FCloseFile( file );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
length = qbIgnoreFileRead ? -1 : FS_ReadFile( newname, (void **) &buffer );
|
|
|
|
if ( length <= 0 )
|
|
{
|
|
// File not found, but keep quiet during interrogate stage, because of stuff like BS_RUN_AND_SHOOT as scriptname
|
|
//
|
|
if (!bCalledDuringInterrogate)
|
|
{
|
|
Com_Printf(S_COLOR_RED"Could not open file '%s'\n", newname );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pscript = new pscript_t;
|
|
|
|
pscript->buffer = (char *) ICARUS_Malloc(length);//gi.Malloc(length, TAG_ICARUS, qfalse);
|
|
memcpy (pscript->buffer, buffer, length);
|
|
pscript->length = length;
|
|
|
|
FS_FreeFile( buffer );
|
|
|
|
ICARUS_BufferList[ name ] = pscript;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ICARUS_SoundPrecache(const char *filename)
|
|
{
|
|
T_G_ICARUS_SOUNDINDEX *sharedMem = (T_G_ICARUS_SOUNDINDEX *)sv.mSharedMemory;
|
|
|
|
strcpy(sharedMem->filename, filename);
|
|
|
|
VM_Call(gvm, GAME_ICARUS_SOUNDINDEX);
|
|
}
|
|
|
|
int ICARUS_GetIDForString( const char *string )
|
|
{
|
|
T_G_ICARUS_GETSETIDFORSTRING *sharedMem = (T_G_ICARUS_GETSETIDFORSTRING *)sv.mSharedMemory;
|
|
|
|
strcpy(sharedMem->string, string);
|
|
|
|
return VM_Call(gvm, GAME_ICARUS_GETSETIDFORSTRING);
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
ICARUS_InterrogateScript
|
|
-------------------------
|
|
*/
|
|
|
|
// at this point the filename should have had the "scripts" (Q3_SCRIPT_DIR) added to it (but not the IBI extension)
|
|
//
|
|
void ICARUS_InterrogateScript( const char *filename )
|
|
{
|
|
CBlockStream stream;
|
|
CBlockMember *blockMember;
|
|
CBlock block;
|
|
|
|
if (!Q_stricmp(filename,"NULL") || !Q_stricmp(filename,"default"))
|
|
return;
|
|
|
|
//////////////////////////////////
|
|
//
|
|
// ensure "scripts" (Q3_SCRIPT_DIR), which will be missing if this was called recursively...
|
|
//
|
|
char sFilename[MAX_FILENAME_LENGTH]; // should really be MAX_QPATH (and 64 bytes instead of 1024), but this fits the rest of the code
|
|
|
|
if (!Q_stricmpn(filename,Q3_SCRIPT_DIR,strlen(Q3_SCRIPT_DIR)))
|
|
{
|
|
Q_strncpyz(sFilename,filename,sizeof(sFilename));
|
|
}
|
|
else
|
|
{
|
|
Q_strncpyz(sFilename,va("%s/%s",Q3_SCRIPT_DIR,filename),sizeof(sFilename));
|
|
}
|
|
//
|
|
//////////////////////////////////
|
|
|
|
|
|
//Attempt to register this script
|
|
if ( ICARUS_RegisterScript( sFilename, qtrue ) == false ) // true = bCalledDuringInterrogate
|
|
return;
|
|
|
|
char *buf;
|
|
long len;
|
|
|
|
//Attempt to retrieve the new script data
|
|
if ( ( len = ICARUS_GetScript ( sFilename, &buf ) ) == 0 )
|
|
return;
|
|
|
|
//Open the stream
|
|
if ( stream.Open( buf, len ) == qfalse )
|
|
return;
|
|
|
|
const char *sVal1, *sVal2;
|
|
char temp[MAX_QPATH];
|
|
int setID;
|
|
|
|
//Now iterate through all blocks of the script, searching for keywords
|
|
while ( stream.BlockAvailable() )
|
|
{
|
|
//Get a block
|
|
if ( stream.ReadBlock( &block ) == qfalse )
|
|
return;
|
|
|
|
//Determine what type of block this is
|
|
switch( block.GetBlockID() )
|
|
{
|
|
case ID_CAMERA: // to cache ROFF files
|
|
{
|
|
float f = *(float *) block.GetMemberData( 0 );
|
|
|
|
if (f == TYPE_PATH)
|
|
{
|
|
sVal1 = (const char *) block.GetMemberData( 1 );
|
|
|
|
//we can do this I guess since the roff is loaded on the server.
|
|
theROFFSystem.Cache((char *)sVal1, qfalse);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_PLAY: // to cache ROFF files
|
|
|
|
sVal1 = (const char *) block.GetMemberData( 0 );
|
|
|
|
if (!Q_stricmp(sVal1,"PLAY_ROFF"))
|
|
{
|
|
sVal1 = (const char *) block.GetMemberData( 1 );
|
|
|
|
//we can do this I guess since the roff is loaded on the server.
|
|
theROFFSystem.Cache((char *)sVal1, qfalse);
|
|
}
|
|
break;
|
|
|
|
//Run commands
|
|
case ID_RUN:
|
|
|
|
sVal1 = (const char *) block.GetMemberData( 0 );
|
|
|
|
COM_StripExtension( sVal1, (char *) temp, sizeof(temp) );
|
|
ICARUS_InterrogateScript( (const char *) &temp );
|
|
|
|
break;
|
|
|
|
case ID_SOUND:
|
|
//We can't just call over to S_RegisterSound or whatever because this is on the server.
|
|
sVal1 = (const char *) block.GetMemberData( 1 ); //0 is channel, 1 is filename
|
|
ICARUS_SoundPrecache(sVal1);
|
|
break;
|
|
|
|
case ID_SET:
|
|
blockMember = block.GetMember( 0 );
|
|
|
|
//NOTENOTE: This will not catch special case get() inlines! (There's not really a good way to do that)
|
|
|
|
//Make sure we're testing against strings
|
|
if ( blockMember->GetID() == TK_STRING )
|
|
{
|
|
sVal1 = (const char *) block.GetMemberData( 0 );
|
|
sVal2 = (const char *) block.GetMemberData( 1 );
|
|
|
|
//Get the id for this set identifier
|
|
setID = ICARUS_GetIDForString( sVal1 );
|
|
|
|
//Check against valid types
|
|
switch ( setID )
|
|
{
|
|
case SET_SPAWNSCRIPT:
|
|
case SET_USESCRIPT:
|
|
case SET_AWAKESCRIPT:
|
|
case SET_ANGERSCRIPT:
|
|
case SET_ATTACKSCRIPT:
|
|
case SET_VICTORYSCRIPT:
|
|
case SET_LOSTENEMYSCRIPT:
|
|
case SET_PAINSCRIPT:
|
|
case SET_FLEESCRIPT:
|
|
case SET_DEATHSCRIPT:
|
|
case SET_DELAYEDSCRIPT:
|
|
case SET_BLOCKEDSCRIPT:
|
|
case SET_FFIRESCRIPT:
|
|
case SET_FFDEATHSCRIPT:
|
|
case SET_MINDTRICKSCRIPT:
|
|
case SET_CINEMATIC_SKIPSCRIPT:
|
|
//Recursively obtain all embedded scripts
|
|
ICARUS_InterrogateScript( sVal2 );
|
|
break;
|
|
case SET_LOOPSOUND: //like ID_SOUND, but set's looping
|
|
ICARUS_SoundPrecache(sVal2);
|
|
break;
|
|
case SET_VIDEO_PLAY: //in game cinematic
|
|
//do nothing for MP.
|
|
break;
|
|
case SET_ADDRHANDBOLT_MODEL:
|
|
case SET_ADDLHANDBOLT_MODEL:
|
|
//do nothing for MP
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//Clean out the block for the next pass
|
|
block.Free();
|
|
}
|
|
|
|
//All done
|
|
stream.Free();
|
|
}
|
|
|
|
#ifdef _XBOX // We borrow the one in NPC_stats.c
|
|
extern stringID_table_t BSTable[];
|
|
#else
|
|
stringID_table_t BSTable[] =
|
|
{
|
|
ENUM2STRING(BS_DEFAULT),//# default behavior for that NPC
|
|
ENUM2STRING(BS_ADVANCE_FIGHT),//# Advance to captureGoal and shoot enemies if you can
|
|
ENUM2STRING(BS_SLEEP),//# Play awake script when startled by sound
|
|
ENUM2STRING(BS_FOLLOW_LEADER),//# Follow your leader and shoot any enemies you come across
|
|
ENUM2STRING(BS_JUMP),//# Face navgoal and jump to it.
|
|
ENUM2STRING(BS_SEARCH),//# Using current waypoint as a base), search the immediate branches of waypoints for enemies
|
|
ENUM2STRING(BS_WANDER),//# Wander down random waypoint paths
|
|
ENUM2STRING(BS_NOCLIP),//# Moves through walls), etc.
|
|
ENUM2STRING(BS_REMOVE),//# Waits for player to leave PVS then removes itself
|
|
ENUM2STRING(BS_CINEMATIC),//# Does nothing but face it's angles and move to a goal if it has one
|
|
//the rest are internal only
|
|
"", -1,
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
==============
|
|
ICARUS_PrecacheEnt
|
|
|
|
Precache all scripts being used by the entity
|
|
==============
|
|
*/
|
|
|
|
void ICARUS_PrecacheEnt( sharedEntity_t *ent )
|
|
{
|
|
char newname[MAX_FILENAME_LENGTH];
|
|
int i;
|
|
|
|
for ( i = 0; i < NUM_BSETS; i++ )
|
|
{
|
|
if ( ent->behaviorSet[i] == NULL )
|
|
continue;
|
|
|
|
if ( GetIDForString( BSTable, ent->behaviorSet[i] ) == -1 )
|
|
{//not a behavior set
|
|
sprintf((char *) newname, "%s/%s", Q3_SCRIPT_DIR, ent->behaviorSet[i] );
|
|
|
|
//Precache this, and all internally referenced scripts
|
|
ICARUS_InterrogateScript( newname );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ICARUS_InitEnt
|
|
|
|
Allocates a sequencer and task manager only if an entity is a potential script user
|
|
==============
|
|
*/
|
|
|
|
void Q3_TaskIDClear( int *taskID );
|
|
void ICARUS_InitEnt( sharedEntity_t *ent )
|
|
{
|
|
//Make sure this is a fresh ent
|
|
assert( iICARUS );
|
|
assert( gTaskManagers[ent->s.number] == NULL );
|
|
assert( gSequencers[ent->s.number] == NULL );
|
|
|
|
if ( gSequencers[ent->s.number] != NULL )
|
|
return;
|
|
|
|
if ( gTaskManagers[ent->s.number] != NULL )
|
|
return;
|
|
|
|
//Create the sequencer and setup the task manager
|
|
gSequencers[ent->s.number] = iICARUS->GetSequencer( ent->s.number );
|
|
gTaskManagers[ent->s.number] = gSequencers[ent->s.number]->GetTaskManager();
|
|
|
|
//Initialize all taskIDs to -1
|
|
memset( &ent->taskID, -1, sizeof( ent->taskID ) );
|
|
|
|
//Add this entity to a map of valid associated ents for quick retrieval later
|
|
ICARUS_AssociateEnt( ent );
|
|
|
|
//Precache all the entity's scripts
|
|
ICARUS_PrecacheEnt( ent );
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
ICARUS_LinkEntity
|
|
-------------------------
|
|
*/
|
|
|
|
int ICARUS_LinkEntity( int entID, CSequencer *sequencer, CTaskManager *taskManager )
|
|
{
|
|
sharedEntity_t *ent = SV_GentityNum(entID);
|
|
|
|
if ( ent == NULL )
|
|
return false;
|
|
|
|
gSequencers[ent->s.number] = sequencer;
|
|
gTaskManagers[ent->s.number] = taskManager;
|
|
|
|
ICARUS_AssociateEnt( ent );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
Svcmd_ICARUS_f
|
|
-------------------------
|
|
*/
|
|
|
|
void Svcmd_ICARUS_f( void )
|
|
{
|
|
//rwwFIXMEFIXME: Do something with this for debugging purposes at some point.
|
|
/*
|
|
char *cmd = Cmd_Argv( 1 );
|
|
|
|
if ( Q_stricmp( cmd, "log" ) == 0 )
|
|
{
|
|
//g_ICARUSDebug->integer = WL_DEBUG;
|
|
if ( VALIDSTRING( Cmd_Argv( 2 ) ) )
|
|
{
|
|
sharedEntity_t *ent = G_Find( NULL, FOFS( script_targetname ), gi.argv(2) );
|
|
|
|
if ( ent == NULL )
|
|
{
|
|
Com_Printf( "Entity \"%s\" not found!\n", gi.argv(2) );
|
|
return;
|
|
}
|
|
|
|
//Start logging
|
|
Com_Printf("Logging ICARUS info for entity %s\n", gi.argv(2) );
|
|
|
|
ICARUS_entFilter = ( ent->s.number == ICARUS_entFilter ) ? -1 : ent->s.number;
|
|
|
|
return;
|
|
}
|
|
|
|
Com_Printf("Logging ICARUS info for all entities\n");
|
|
|
|
return;
|
|
}
|
|
*/
|
|
return;
|
|
}
|