ef2-sdk/dlls/game/program.cpp
2003-11-05 00:00:00 +00:00

1121 lines
23 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /EF2/Code/DLLs/game/program.cpp $
// $Revision:: 23 $
// $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 "program.h"
#include "compiler.h"
inline void type_t::Archive( Archiver &arc )
{
int i;
Class::Archive( arc );
ArchiveEnum( type, etype_t );
if ( arc.Loading() )
{
bool onList;
arc.ArchiveBool( &onList );
if ( !onList )
{
def = new def_t;
arc.ArchiveObject( ( Class * )def );
}
else
{
arc.ArchiveObjectPointer( ( Class ** )&def );
}
}
else
{
arc.ArchiveBool( &def->_onDefList );
if ( !def->_onDefList )
{
arc.ArchiveObject( ( Class * )def );
}
else
{
arc.ArchiveObjectPointer( ( Class ** )&def );
}
}
/* if ( arc.Loading() )
def = new def_t;
arc.ArchiveObject( ( Class * )def ); */
arc.ArchiveObjectPointer( ( Class ** )&def );
arc.ArchiveObjectPointer( ( Class ** )&aux_type );
arc.ArchiveInteger( &num_parms );
arc.ArchiveInteger( &min_parms );
for ( i = 0; i < num_parms; i++ )
arc.ArchiveObjectPointer( ( Class ** )&parm_types[i] );
}
inline void def_t::Archive( Archiver &arc )
{
Class::Archive( arc );
arc.ArchiveObjectPointer( ( Class ** )&type );
arc.ArchiveString( &name );
// arc.ArchiveObjectPointer( ( Class ** )&next );
arc.ArchiveInteger( &ofs );
arc.ArchiveInteger( &localofs );
arc.ArchiveObjectPointer( ( Class ** )&scope );
arc.ArchiveInteger( &initialized );
arc.ArchiveBool( &caseSensitive );
arc.ArchiveBool( &_onDefList );
//arc.ArchiveObjectPointer( ( Class ** )&type );
}
inline void dfunction_t::Archive( Archiver &arc )
{
Class::Archive( arc );
arc.ArchiveInteger( &eventnum );
arc.ArchiveInteger( &first_statement );
arc.ArchiveInteger( &parm_start );
arc.ArchiveInteger( &parm_total );
arc.ArchiveInteger( &locals );
arc.ArchiveInteger( &profile );
arc.ArchiveString( &s_name );
arc.ArchiveString( &s_file );
arc.ArchiveInteger( &numparms );
arc.ArchiveInteger( &minparms );
if ( arc.Loading() )
{
memset( &parm_size, 0, sizeof( parm_size[0] ) * MAX_PARMS );
memset( &parm_type, 0, sizeof( parm_type[0] ) * MAX_PARMS );
}
arc.ArchiveRaw( parm_size, sizeof( parm_size[0] ) * numparms );
arc.ArchiveRaw( parm_type, sizeof( parm_type[0] ) * numparms );
}
// These two pointers are dummy pointers for load/save games. They MUST stay global because of how
// loadgames change the pointer at a later time, hence if they are on the stack it will corrupt the stack.
//type_t *forceTypeSave;
//def_t *forceDefSave;
inline void Program::Archive( Archiver &arc )
{
int i, num;
type_t *curtype, *newtype;
def_t *curdef, *newdef;
Class::Archive( arc );
/* // Force all of the defs to have indexes
forceDefSave = &def_void;
arc.ArchiveObjectPointer( ( Class ** )&forceDefSave );
forceDefSave = &def_string;
arc.ArchiveObjectPointer( ( Class ** )&forceDefSave );
forceDefSave = &def_float;
arc.ArchiveObjectPointer( ( Class ** )&forceDefSave );
forceDefSave = &def_vector;
arc.ArchiveObjectPointer( ( Class ** )&forceDefSave );
forceDefSave = &def_entity;
arc.ArchiveObjectPointer( ( Class ** )&forceDefSave );
forceDefSave = &def_function;
arc.ArchiveObjectPointer( ( Class ** )&forceDefSave );
// Force all of the types to have indexes
forceTypeSave = &type_void;
arc.ArchiveObjectPointer( ( Class ** )&forceTypeSave );
forceTypeSave = &type_string;
arc.ArchiveObjectPointer( ( Class ** )&forceTypeSave );
forceTypeSave = &type_float;
arc.ArchiveObjectPointer( ( Class ** )&forceTypeSave );
forceTypeSave = &type_vector;
arc.ArchiveObjectPointer( ( Class ** )&forceTypeSave );
forceTypeSave = &type_entity;
arc.ArchiveObjectPointer( ( Class ** )&forceTypeSave );
forceTypeSave = &type_function;
arc.ArchiveObjectPointer( ( Class ** )&forceTypeSave ); */
// NOTE: must archive global data for pointer fixups
arc.ArchiveObject( &def_void );
arc.ArchiveObject( &def_string );
arc.ArchiveObject( &def_float );
arc.ArchiveObject( &def_vector );
arc.ArchiveObject( &def_entity );
arc.ArchiveObject( &def_function );
arc.ArchiveObject( &def_ret );
arc.ArchiveObject( &junkdef );
arc.ArchiveObject( &type_void );
arc.ArchiveObject( &type_string );
arc.ArchiveObject( &type_float );
arc.ArchiveObject( &type_vector );
arc.ArchiveObject( &type_entity );
arc.ArchiveObject( &type_function );
arc.ArchiveInteger( &numpr_globals );
if ( arc.Loading() )
{
memset( pr_globals, 0, sizeof( pr_globals[0] ) * MAX_REGS );
}
arc.ArchiveRaw( pr_globals, sizeof( pr_globals[0] ) * numpr_globals );
arc.ArchiveInteger( &locals_start );
arc.ArchiveInteger( &locals_end );
for ( i = 0; i < MAX_STRINGS; i++ )
{
arc.ArchiveBool( &strings[i].inuse );
if ( strings[i].inuse )
{
arc.ArchiveString( &strings[i].s );
}
else
{
strings[i].s = "";
}
}
arc.ArchiveInteger( &numstatements );
arc.ArchiveRaw( statements, sizeof( statements[0] ) * numstatements );
arc.ArchiveInteger( &numfunctions );
for ( i = 0; i < numfunctions; i++ )
arc.ArchiveObject( ( Class * )&functions[i] );
// archive types
if ( arc.Saving() )
{
for ( curtype = types, num = 0; curtype; curtype = curtype->next )
{
num++;
}
// Don't count type_function
num--;
}
arc.ArchiveInteger( &num );
if ( arc.Saving() )
{
for ( curtype = types; curtype; curtype = curtype->next )
{
// Skip type_function (we archive it seperately above)
if ( curtype == &type_function )
continue;
arc.ArchiveObject( ( Class * )curtype );
}
}
else
{
curtype = types;
for ( i = 0; i < num; i++ )
{
newtype = new type_t;
arc.ArchiveObject( ( Class * )newtype );
newtype->next = NULL;
curtype->next = newtype;
curtype = newtype;
}
}
// archive defs
if ( arc.Saving() ) {
for ( curdef = def_head.next, num = 0; curdef; curdef = curdef->next )
num++;
}
arc.ArchiveInteger( &num );
if ( arc.Saving() ) {
for ( curdef = def_head.next; curdef; curdef = curdef->next )
arc.ArchiveObject( ( Class * )curdef );
}
else {
def_tail = &def_head;
curdef = def_tail;
for ( i = 0; i < num; i++ ) {
newdef = new def_t;
arc.ArchiveObject( ( Class * )newdef );
newdef->next = NULL;
curdef->next = newdef;
curdef = newdef;
}
}
arc.ArchiveInteger( &pr_error_count );
filenames.Archive( arc );
arc.ArchiveString( &s_file );
if ( arc.Loading() )
{
memset( pr_global_defs, 0, sizeof( pr_global_defs ) );
}
for ( i = 0; i < numpr_globals; i++ )
{
arc.ArchiveObjectPointer( ( Class ** )&pr_global_defs[i] );
}
}
CLASS_DECLARATION( Class, dfunction_t, NULL )
{
{ NULL, NULL }
};
CLASS_DECLARATION( Class, type_t, NULL )
{
{ NULL, NULL }
};
CLASS_DECLARATION( Class, def_t, NULL )
{
{ NULL, NULL }
};
CLASS_DECLARATION( Class, Program, NULL )
{
{ NULL, NULL }
};
// defined as a str so that it is only allocated once
const str complextypestring( "COMPLEX TYPE" );
const str emptystring;
const str resultstring( "<RESULT>" );
const str immediatestring( "IMMEDIATE" );
def_t::def_t()
{
type = NULL;
next = NULL;
ofs = 0;
localofs = 0;
scope = NULL;
initialized = false;
caseSensitive = true;
}
/*
============
FindType
Returns a preexisting complex type that matches the parm, or allocates
a new one and copies it out.
============
*/
type_t *Program::FindType( const type_t *type )
{
def_t *def;
type_t *check;
int i;
for( check = types; check != NULL; check = check->next )
{
if ( ( check->type != type->type ) || ( check->aux_type != type->aux_type ) )
{
continue;
}
if ( check->min_parms == -1 )
{
// non-event functions
if ( check->num_parms != type->num_parms )
{
continue;
}
}
else
{
// event functions
if ( ( check->min_parms != type->min_parms ) || ( check->num_parms != type->num_parms ) )
{
continue;
}
}
for( i = 0; i < type->num_parms; i++ )
{
if ( check->parm_types[ i ] != type->parm_types[ i ] )
{
break;
}
}
if ( i == type->num_parms )
{
return check;
}
}
// allocate a new one
check = new type_t;
*check = *type;
check->next = types;
types = check;
// allocate a generic def for the type, so fields can reference it
def = new def_t;
def->name = complextypestring;
def->type = check;
def->_onDefList = false;
check->def = def;
return check;
}
/*
============
GetDef
If type is NULL, it will match any type
If allocate is true, a new def will be allocated if it can't be found
============
*/
def_t *Program::GetDef( type_t *type, const char *name, def_t *scope, bool allocate, Lexer *lex )
{
def_t *def;
char element[ MAX_NAME ];
// see if the name is already in use
for( def = def_head.next; def; def = def->next )
{
if ( ( !def->caseSensitive && ( stricmp( def->name, name ) == 0 ) ) || ( strcmp( def->name, name ) == 0 ) )
{
if ( def->scope && ( def->scope != scope ) )
{
// in a different function
continue;
}
if ( type && ( def->type != type ) && lex )
{
lex->ParseError( "Type mismatch on redeclaration of %s", name );
}
return def;
}
}
if ( !allocate )
{
return NULL;
}
// allocate a new def
def = new def_t;
def->next = NULL;
def_tail->next = def;
def_tail = def;
def->_onDefList = true;
def->name = name;
def->type = type;
def->scope = scope;
def->ofs = numpr_globals;
if ( scope )
{
// since we don't know how many local variables there are,
// we have to have them go backwards on the stack
def->localofs = -( numpr_globals - locals_start ) - 1;
}
else
{
def->localofs = def->ofs;
}
pr_global_defs[ numpr_globals ] = def;
//
// make automatic defs for the vectors elements
// .origin can be accessed as .origin_x, .origin_y, and .origin_z
//
if ( type->type == ev_vector )
{
sprintf( element, "%s_x", name );
GetDef( &type_float, element, scope, true, lex );
sprintf( element, "%s_y", name );
GetDef( &type_float, element, scope, true, lex );
sprintf( element, "%s_z", name );
GetDef( &type_float, element, scope, true, lex );
}
else
{
numpr_globals += type_size[ type->type ];
}
return def;
}
void Program::CreateDefForEvent( Event *ev )
{
type_t newtype;
type_t *type;
const char *name;
def_t *def;
EventArgDef *arg;
int num;
int i;
dfunction_t *df;
qboolean hitoptional;
num = ev->getNumArgDefs();
if ( num > MAX_PARMS )
{
gi.WDPrintf( "Event '%s' has too many arguments for function call.\n", ev->getName() );
return;
}
if ( numfunctions >= MAX_FUNCTIONS )
{
gi.Error( ERR_DROP, "Exceeded max functions while declaring events." );
}
df = &functions[ numfunctions ];
df->parm_total = 0;
df->parm_start = numpr_globals;
memset( &newtype, 0, sizeof( newtype ) );
newtype.type = ev_function;
// set the return type
arg = ev->getReturnType();
if ( arg )
{
switch( arg->getType() )
{
case IS_STRING :
newtype.aux_type = &type_string;
break;
case IS_VECTOR :
newtype.aux_type = &type_vector;
break;
case IS_ENTITY :
newtype.aux_type = &type_entity;
break;
case IS_BOOLEAN :
case IS_INTEGER :
case IS_FLOAT :
default:
newtype.aux_type = &type_float;
break;
}
}
else
{
newtype.aux_type = &type_void;
}
newtype.num_parms = 0;
newtype.min_parms = 0;
hitoptional = false;
for( i = 1; i <= num; i++ )
{
arg = ev->getArgDef( i );
if ( !hitoptional )
{
hitoptional = arg->isOptional();
if ( !hitoptional )
{
newtype.min_parms++;
}
}
switch( arg->getType() )
{
case IS_STRING :
type = &type_string;
break;
case IS_VECTOR :
type = &type_vector;
break;
case IS_ENTITY :
type = &type_entity;
break;
case IS_BOOLEAN :
case IS_INTEGER :
case IS_FLOAT :
default:
type = &type_float;
break;
}
df->parm_total += type_size[ type->type ];
df->parm_size[ newtype.num_parms ] = type_size[ type->type ];
df->parm_type[ newtype.num_parms ] = type->type;
newtype.parm_types[ newtype.num_parms ] = type;
newtype.num_parms++;
}
type = FindType( &newtype );
name = ev->getName();
def = GetDef( type, name, NULL, true, NULL );
def->initialized = 1;
def->caseSensitive = false;
setFunction( def->ofs, numfunctions );
// fill in the dfunction
df->eventnum = int( *ev );
df->first_statement = -1;
df->s_name = def->name;
df->s_file = s_file;
df->numparms = def->type->num_parms;
df->minparms = def->type->min_parms;
df->locals = 0;
numfunctions++;
}
void Program::CreateEventDefs( void )
{
int num;
int i;
num = Event::NumEventDefs();
for( i = 1; i <= num; i++ )
{
CreateDefForEvent( Event::GetEventDef( i ) );
}
}
/*
============
CopyString
returns an offset from the string heap
============
*/
int Program::CopyString( const char *str )
{
int idx;
idx = AllocString();
if ( str )
{
strings[ idx ].s = str;
}
else
{
strings[ idx ].s = emptystring;
}
return idx;
}
/*
============
AllocString
returns an unused string index
============
*/
int Program::AllocString()
{
int i;
for ( i = 0; i < MAX_STRINGS; i++ )
{
if ( !strings[i].inuse ) {
strings[i].inuse = true;
strings[i].s.CapLength( 0 );
return i;
}
}
gi.Error( ERR_DROP, "Program::GetFreeString : Too many strings allocated!\n" );
return 0;
}
/*
============
FreeString
============
*/
void Program::FreeString( int idx )
{
assert( ( idx > 0 ) && ( idx < MAX_STRINGS ) );
assert( strings[idx].inuse );
strings[idx].inuse = false;
strings[idx].s.CapLength( 0 );
}
/*
============
CountStrings
============
*/
int Program::CountUsedStrings()
{
int i, count = 0;
for ( i = 0; i < MAX_STRINGS; i++ ) {
if ( strings[i].inuse )
count++;
}
return count;
}
/*
==============
BeginCompilation
called before compiling a batch of files, clears the pr struct
==============
*/
void Program::BeginCompilation( void )
{
int i;
numpr_globals = RESERVED_OFS;
locals_end = numpr_globals;
locals_start = numpr_globals;
numstatements = 0;
numfunctions = 1;
for ( i = 0; i < MAX_STRINGS; i++ ) {
strings[i].inuse = false;
strings[i].s.CapLength( 0 );
}
strings[0].inuse = true; // used as NULL
strings[1].inuse = true; // always used as the return string
def_tail = &def_head;
for( i = 0; i < RESERVED_OFS; i++ )
{
pr_global_defs[ i ] = &def_void;
}
// link the function type in so state forward declarations match proper type
types = &type_function;
type_function.next = NULL;
pr_error_count = 0;
// define any predefined objects
GetDef( &type_void, "cam", NULL, true, NULL );
}
/*
==============
FinishCompilation
called after all files are compiled to check for errors
Returns false if errors were detected.
==============
*/
bool Program::FinishCompilation( void )
{
def_t *d;
bool errors;
errors = false;
// check to make sure all functions prototyped have code
for( d = def_head.next; d; d = d->next )
{
if ( ( d->type->type == ev_function ) && !d->scope )
{
// function parms are ok
if (!d->initialized)
{
gi.WPrintf( "function %s was not defined\n", d->name.c_str() );
errors = true;
}
}
}
// also bail if there were ANY errors; the above block may be unneccessary with this in place.
if( program.pr_error_count > 0 )
{
errors = true;
}
return !errors;
}
void Program::Compile( const char *filename )
{
char *src;
Compiler compiler( *this );
int filenum;
str oldfile( s_file );
s_file = filename;
if ( gi.FS_ReadFile( filename, ( void ** )&src, true ) < 0 )
{
s_file = oldfile;
gi.WPrintf( "***\n***\n***\n*** Couldn't load %s\n***\n***\n***\n", filename );
throw "Error";
}
filenum = filenames.AddObject( s_file );
//if ( !compiler.CompileFile( src, filename, filenum ) )
if ( !compiler.CompileFile( src, filenum ) )
{
s_file = oldfile;
gi.FS_FreeFile( src );
gi.WPrintf( "Compile failed.\n" );
throw "Error";
}
gi.FS_FreeFile( src );
s_file = oldfile;
}
void Program::Load( const char *filename )
{
FreeData();
BeginCompilation();
CreateEventDefs();
try
{
Compile( filename );
}
catch( ... )
{
};
if ( !FinishCompilation() )
{
gi.Error( ERR_DROP, "Compile failed." );
}
}
func_t Program::findFunction( const char *name )
{
int i;
assert( name );
for( i = 0; i < numfunctions; i++ )
{
if ( !functions[ i ].s_name.cmp( name ) )
{
return i;
}
}
return -1;
}
void Program::FreeData( void )
{
type_t *curtype;
type_t *prevtype;
def_t *curdef;
def_t *prevdef;
int i;
for( curtype = types; curtype != &type_function; curtype = prevtype )
{
prevtype = curtype->next;
delete curtype->def;
delete curtype;
}
// link the function type in so state forward declarations match proper type
types = &type_function;
type_function.next = NULL;
for( curdef = def_head.next; curdef != NULL; curdef = prevdef )
{
prevdef = curdef->next;
delete curdef;
}
def_head.next = NULL;
def_tail = NULL;
// cause all our strings to share their data
for( i = 0; i < MAX_STRINGS; i++ ) {
strings[ i ].inuse = false;
strings[ i ].s = emptystring;
}
strings[0].inuse = true; // always used as return string
memset( pr_global_defs, 0, sizeof( pr_global_defs ) );
memset( pr_globals, 0, sizeof( pr_globals ) );
numpr_globals = 0;
locals_start = 0;
locals_end = 0;
memset( statements, 0, sizeof( statements ) );
numstatements = 0;
for( i = 0; i < MAX_FUNCTIONS; i++ )
{
functions[ i ].eventnum = 0;
functions[ i ].first_statement = 0;
functions[ i ].parm_start = 0;
functions[ i ].parm_total = 0;
functions[ i ].locals = 0;
functions[ i ].profile = 0;
functions[ i ].s_name = emptystring;
functions[ i ].s_file = emptystring;
functions[ i ].numparms = 0;
functions[ i ].minparms = 0;
memset( functions[ i ].parm_size, 0, sizeof( functions[ i ].parm_size ) );
memset( functions[ i ].parm_type, 0, sizeof( functions[ i ].parm_type ) );
}
numfunctions = 1;
pr_error_count = 0;
s_file = emptystring;
}
Program::Program()
{
int i;
str tempstring( "temp" );
def_void.type = &type_void;
def_void.name = tempstring;
def_string.type = &type_string;
def_string.name = tempstring;
def_float.type = &type_float;
def_float.name = tempstring;
def_vector.type = &type_vector;
def_vector.name = tempstring;
def_entity.type = &type_entity;
def_entity.name = tempstring;
def_function.type = &type_function;
def_function.name = tempstring;
def_tail = NULL;
// link the function type in so state forward declarations match proper type
types = &type_function;
type_function.next = NULL;
// cause all our strings to share their data
for( i = 0; i < MAX_STRINGS; i++ ) {
strings[ i ].inuse = false;
strings[ i ].s = "";
}
strings[0].inuse = true; // always used as return string
filenames.FreeObjectList();
numpr_globals = 0;
numstatements = 0;
numfunctions = 1;
locals_start = 0;
locals_end = 0;
pr_error_count = 0;
}
Program::~Program()
{
FreeData();
}
// Stuff From .h File
float Program::getFloat( int offset )
{
return pr_globals[ offset ];
}
int Program::getInteger( int offset )
{
return *( int * )&pr_globals[ offset ];
}
float *Program::getVector( int offset )
{
return &pr_globals[ offset ];
}
const char *Program::getString( int offset )
{
return strings[ *( int * )&pr_globals[ offset ] ].s.c_str();
// return ( ( str * )&pr_globals[ offset ] ] ] )->c_str();
// return ( ( str * )( &pr_globals[ offset ] ) )->getString();
}
func_t Program::getFunction( int offset )
{
return *( func_t * )&pr_globals[ offset ];
}
Entity *Program::getEntity( int offset )
{
Entity *ent;
int entnum;
entnum = *( int * )&pr_globals[ offset ];
if ( entnum > 0 )
{
ent = G_GetEntity( entnum - 1 );
return ent;
}
return NULL;
}
TargetList *Program::getTargetList( int offset )
{
int entnum;
entnum = *( int * )&pr_globals[ offset ];
if ( entnum < 0 )
{
return world->GetTargetList( -entnum );
}
return NULL;
}
gentity_t *Program::getEdict( int offset )
{
int entnum;
Entity *ent;
entnum = *( int * )&pr_globals[ offset ];
if ( entnum > 0 )
{
ent = G_GetEntity( entnum - 1 );
if ( ent )
{
return ent->edict;
}
}
else if ( entnum < 0 )
{
}
return NULL;
}
void Program::setFunction( int offset, func_t func )
{
*( func_t * )&pr_globals[ offset ] = func;
}
void Program::setEntity( int offset, const Entity *ent )
{
if ( ent )
{
*( int * )&pr_globals[ offset ] = ent->entnum + 1;
}
else
{
*( int * )&pr_globals[ offset ] = 0;
}
}
void Program::setTargetList( int offset, const TargetList *list )
{
if ( list )
{
*( int * )&pr_globals[ offset ] = -list->index;
}
else
{
*( int * )&pr_globals[ offset ] = 0;
}
}
void Program::setString( int offset, const char *text )
{
strings[ offset ].s = text;
*( int * )&pr_globals[ offset ] = offset;
}
void Program::setFloat( int offset, float value )
{
pr_globals[ offset ] = value;
}
void Program::setInteger( int offset, int value )
{
*( int * )&pr_globals[ offset ] = value;
}
void Program::setVector( int offset, const Vector &vec )
{
*( Vector * )&pr_globals[ offset ] = vec;
}
const char *Program::GetFilename( int num )
{
return filenames.ObjectAt( num );
}