/* ================ idEvent::Restore ================ */ void idEvent::Restore( idRestoreGame *savefile ) { int i; int num; int argsize; idStr name; idEvent *event; savefile->ReadInt( num ); for ( i = 0; i < num; i++ ) { if ( FreeEvents.IsListEmpty() ) { gameLocal.Error( "idEvent::Restore : No more free events" ); } event = FreeEvents.Next(); event->eventNode.Remove(); event->eventNode.AddToEnd( EventQueue ); savefile->ReadInt( event->time ); // read the event name savefile->ReadString( name ); event->eventdef = idEventDef::FindEvent( name ); if ( !event->eventdef ) { savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() ); } // read the classtype savefile->ReadString( name ); event->typeinfo = idClass::GetClass( name ); if ( !event->typeinfo ) { savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() ); } savefile->ReadObject( event->object ); assert( event->object ); // read the args savefile->ReadInt( argsize ); if ( argsize != event->eventdef->GetArgSize() ) { savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() ); } if ( argsize ) { event->data = eventDataAllocator.Alloc( argsize ); savefile->Read( event->data, argsize ); } else { event->data = NULL; } } }
/* ================ idEvent::Restore ================ */ void idEvent::Restore( idRestoreGame *savefile ) { char *str; int num, argsize, i, j, size; idStr name; byte *dataPtr; idEvent *event; const char *format; savefile->ReadInt( num ); for ( i = 0; i < num; i++ ) { if ( FreeEvents.IsListEmpty() ) { gameLocal.Error( "idEvent::Restore : No more free events" ); } event = FreeEvents.Next(); event->eventNode.Remove(); event->eventNode.AddToEnd( EventQueue ); savefile->ReadInt( event->time ); // read the event name savefile->ReadString( name ); event->eventdef = idEventDef::FindEvent( name ); if ( !event->eventdef ) { savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() ); } // read the classtype savefile->ReadString( name ); event->typeinfo = idClass::GetClass( name ); if ( !event->typeinfo ) { savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() ); } savefile->ReadObject( event->object ); // read the args savefile->ReadInt( argsize ); if ( ((size_t)argsize) != event->eventdef->GetArgSize() ) { savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() ); } if ( argsize ) { event->data = eventDataAllocator.Alloc( argsize ); format = event->eventdef->GetArgFormat(); assert( format ); for ( j = 0, size = 0; j < event->eventdef->GetNumArgs(); ++j) { dataPtr = &event->data[ event->eventdef->GetArgOffset( j ) ]; switch( format[ j ] ) { case D_EVENT_FLOAT : savefile->ReadFloat( *reinterpret_cast<float *>( dataPtr ) ); size += sizeof( float ); break; case D_EVENT_INTEGER : case D_EVENT_ENTITY : case D_EVENT_ENTITY_NULL : savefile->ReadInt( *reinterpret_cast<int *>( dataPtr ) ); size += sizeof( int ); break; case D_EVENT_VECTOR : savefile->ReadVec3( *reinterpret_cast<idVec3 *>( dataPtr ) ); size += sizeof( idVec3 ); break; case D_EVENT_TRACE : savefile->ReadBool( *reinterpret_cast<bool *>( dataPtr ) ); size += sizeof( bool ); if ( *reinterpret_cast<bool *>( dataPtr ) ) { size += sizeof( trace_t ); trace_t &t = *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ); RestoreTrace( savefile, t) ; if ( t.c.material ) { size += MAX_STRING_LEN; str = reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ); savefile->Read( str, MAX_STRING_LEN ); } } break; default: break; } } assert( size == event->eventdef->GetArgSize() ); } else { event->data = NULL; } } }
/* ================ idEvent::ServiceEvents ================ */ void idEvent::ServiceEvents( void ) { idEvent *event; int num; int args[ D_EVENT_MAXARGS ]; int offset; int i; int numargs; const char *formatspec; trace_t **tracePtr; const idEventDef *ev; byte *data; const char *materialName; num = 0; while( !EventQueue.IsListEmpty() ) { event = EventQueue.Next(); assert( event ); if ( event->time > gameLocal.time ) { break; } // copy the data into the local args array and set up pointers ev = event->eventdef; formatspec = ev->GetArgFormat(); numargs = ev->GetNumArgs(); for( i = 0; i < numargs; i++ ) { offset = ev->GetArgOffset( i ); data = event->data; switch( formatspec[ i ] ) { case D_EVENT_FLOAT : case D_EVENT_INTEGER : args[ i ] = *reinterpret_cast<int *>( &data[ offset ] ); break; case D_EVENT_VECTOR : *reinterpret_cast<idVec3 **>( &args[ i ] ) = reinterpret_cast<idVec3 *>( &data[ offset ] ); break; case D_EVENT_STRING : *reinterpret_cast<const char **>( &args[ i ] ) = reinterpret_cast<const char *>( &data[ offset ] ); break; case D_EVENT_ENTITY : case D_EVENT_ENTITY_NULL : *reinterpret_cast<idEntity **>( &args[ i ] ) = reinterpret_cast< idEntityPtr<idEntity> * >( &data[ offset ] )->GetEntity(); break; case D_EVENT_TRACE : tracePtr = reinterpret_cast<trace_t **>( &args[ i ] ); if ( *reinterpret_cast<bool *>( &data[ offset ] ) ) { *tracePtr = reinterpret_cast<trace_t *>( &data[ offset + sizeof( bool ) ] ); if ( ( *tracePtr )->c.material != NULL ) { // look up the material name to get the material pointer materialName = reinterpret_cast<const char *>( &data[ offset + sizeof( bool ) + sizeof( trace_t ) ] ); ( *tracePtr )->c.material = declManager->FindMaterial( materialName, true ); } } else { *tracePtr = NULL; } break; default: gameLocal.Error( "idEvent::ServiceEvents : Invalid arg format '%s' string for '%s' event.", formatspec, ev->GetName() ); } } // the event is removed from its list so that if then object // is deleted, the event won't be freed twice event->eventNode.Remove(); assert( event->object ); event->object->ProcessEventArgPtr( ev, args ); #if 0 // event functions may never leave return values on the FPU stack // enable this code to check if any event call left values on the FPU stack if ( !sys->FPU_StackIsEmpty() ) { gameLocal.Error( "idEvent::ServiceEvents %d: %s left a value on the FPU stack\n", num, ev->GetName() ); } #endif // return the event to the free list event->Free(); // Don't allow ourselves to stay in here too long. An abnormally high number // of events being processed is evidence of an infinite loop of events. num++; if ( num > MAX_EVENTSPERFRAME ) { gameLocal.Error( "Event overflow. Possible infinite loop in script." ); } } }
/* ================ idEvent::Alloc ================ */ idEvent *idEvent::Alloc( const idEventDef *evdef, int numargs, va_list args ) { idEvent *ev; size_t size; const char *format; idEventArg *arg; byte *dataPtr; int i; const char *materialName; if ( FreeEvents.IsListEmpty() ) { gameLocal.Error( "idEvent::Alloc : No more free events" ); } ev = FreeEvents.Next(); ev->eventNode.Remove(); ev->eventdef = evdef; if ( numargs != evdef->GetNumArgs() ) { gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() ); } size = evdef->GetArgSize(); if ( size ) { ev->data = eventDataAllocator.Alloc( size ); memset( ev->data, 0, size ); } else { ev->data = NULL; } format = evdef->GetArgFormat(); for( i = 0; i < numargs; i++ ) { arg = va_arg( args, idEventArg * ); if ( format[ i ] != arg->type ) { // when NULL is passed in for an entity, it gets cast as an integer 0, so don't give an error when it happens if ( !( ( ( format[ i ] == D_EVENT_TRACE ) || ( format[ i ] == D_EVENT_ENTITY ) ) && ( arg->type == 'd' ) && ( arg->value == 0 ) ) ) { gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() ); } } dataPtr = &ev->data[ evdef->GetArgOffset( i ) ]; switch( format[ i ] ) { case D_EVENT_FLOAT : case D_EVENT_INTEGER : *reinterpret_cast<int *>( dataPtr ) = arg->value; break; case D_EVENT_VECTOR : if ( arg->value ) { *reinterpret_cast<idVec3 *>( dataPtr ) = *reinterpret_cast<const idVec3 *>( arg->value ); } break; case D_EVENT_STRING : if ( arg->value ) { idStr::Copynz( reinterpret_cast<char *>( dataPtr ), reinterpret_cast<const char *>( arg->value ), MAX_STRING_LEN ); } break; case D_EVENT_ENTITY : case D_EVENT_ENTITY_NULL : *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value ); break; case D_EVENT_TRACE : if ( arg->value ) { *reinterpret_cast<bool *>( dataPtr ) = true; *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t *>( arg->value ); // save off the material as a string since the pointer won't be valid in save games. // since we save off the entire trace_t structure, if the material is NULL here, // it will be NULL when we process it, so we don't need to save off anything in that case. if ( reinterpret_cast<const trace_t *>( arg->value )->c.material ) { materialName = reinterpret_cast<const trace_t *>( arg->value )->c.material->GetName(); idStr::Copynz( reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ), materialName, MAX_STRING_LEN ); } } else { *reinterpret_cast<bool *>( dataPtr ) = false; } break; default : gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() ); break; } } return ev; }
/* ================ idEvent::Restore ================ */ void idEvent::Restore( idRestoreGame* savefile ) { char* str; int num, argsize, i, j, size; idStr name; byte* dataPtr; idEvent* event; const char* format; // RB: for missing D_EVENT_STRING idStr s; // RB end savefile->ReadInt( num ); for( i = 0; i < num; i++ ) { if( FreeEvents.IsListEmpty() ) { gameLocal.Error( "idEvent::Restore : No more free events" ); } event = FreeEvents.Next(); event->eventNode.Remove(); event->eventNode.AddToEnd( EventQueue ); savefile->ReadInt( event->time ); // read the event name savefile->ReadString( name ); event->eventdef = idEventDef::FindEvent( name ); if( event->eventdef == NULL ) { savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() ); return; } // read the classtype savefile->ReadString( name ); event->typeinfo = idClass::GetClass( name ); if( event->typeinfo == NULL ) { savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() ); return; } savefile->ReadObject( event->object ); // read the args savefile->ReadInt( argsize ); if( argsize != ( int )event->eventdef->GetArgSize() ) { // RB: fixed wrong formatting savefile->Error( "idEvent::Restore: arg size (%zd) doesn't match saved arg size(%zd) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() ); // RB end } if( argsize ) { event->data = eventDataAllocator.Alloc( argsize ); format = event->eventdef->GetArgFormat(); assert( format ); for( j = 0, size = 0; j < event->eventdef->GetNumArgs(); ++j ) { dataPtr = &event->data[ event->eventdef->GetArgOffset( j ) ]; switch( format[ j ] ) { case D_EVENT_FLOAT : savefile->ReadFloat( *reinterpret_cast<float*>( dataPtr ) ); // RB: 64 bit fix, changed sizeof( float ) to sizeof( intptr_t ) size += sizeof( intptr_t ); // RB end break; case D_EVENT_INTEGER : // RB: 64 bit fix savefile->ReadInt( *reinterpret_cast<int*>( dataPtr ) ); size += sizeof( intptr_t ); break; // RB end case D_EVENT_ENTITY : case D_EVENT_ENTITY_NULL : // RB: 64 bit fix, changed alignment to sizeof( intptr_t ) reinterpret_cast<idEntityPtr<idEntity> *>( dataPtr )->Restore( savefile ); size += sizeof( intptr_t ); // RB end break; case D_EVENT_VECTOR : savefile->ReadVec3( *reinterpret_cast<idVec3*>( dataPtr ) ); // RB: 64 bit fix, changed sizeof( int ) to E_EVENT_SIZEOF_VEC size += E_EVENT_SIZEOF_VEC; // RB end break; #if 1 // RB: added missing D_EVENT_STRING case case D_EVENT_STRING : savefile->ReadString( s ); //idStr::Copynz(reinterpret_cast<char *>( dataPtr ), s, s.Length() ); //size += s.Length(); idStr::Copynz( reinterpret_cast<char*>( dataPtr ), s, MAX_STRING_LEN ); size += MAX_STRING_LEN; break; // RB end #endif case D_EVENT_TRACE : savefile->ReadBool( *reinterpret_cast<bool*>( dataPtr ) ); size += sizeof( bool ); if( *reinterpret_cast<bool*>( dataPtr ) ) { size += sizeof( trace_t ); trace_t& t = *reinterpret_cast<trace_t*>( dataPtr + sizeof( bool ) ); RestoreTrace( savefile, t ) ; if( t.c.material ) { size += MAX_STRING_LEN; str = reinterpret_cast<char*>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ); savefile->Read( str, MAX_STRING_LEN ); } } break; default: break; } } assert( size == ( int )event->eventdef->GetArgSize() ); } else { event->data = NULL; } } // Restore the Fast EventQueue savefile->ReadInt( num ); for( i = 0; i < num; i++ ) { if( FreeEvents.IsListEmpty() ) { gameLocal.Error( "idEvent::Restore : No more free events" ); } event = FreeEvents.Next(); event->eventNode.Remove(); event->eventNode.AddToEnd( FastEventQueue ); savefile->ReadInt( event->time ); // read the event name savefile->ReadString( name ); event->eventdef = idEventDef::FindEvent( name ); if( event->eventdef == NULL ) { savefile->Error( "idEvent::Restore: unknown event '%s'", name.c_str() ); return; } // read the classtype savefile->ReadString( name ); event->typeinfo = idClass::GetClass( name ); if( event->typeinfo == NULL ) { savefile->Error( "idEvent::Restore: unknown class '%s' on event '%s'", name.c_str(), event->eventdef->GetName() ); return; } savefile->ReadObject( event->object ); // read the args savefile->ReadInt( argsize ); if( argsize != ( int )event->eventdef->GetArgSize() ) { savefile->Error( "idEvent::Restore: arg size (%d) doesn't match saved arg size(%d) on event '%s'", event->eventdef->GetArgSize(), argsize, event->eventdef->GetName() ); } if( argsize ) { event->data = eventDataAllocator.Alloc( argsize ); savefile->Read( event->data, argsize ); } else { event->data = NULL; } } }
/* ================ idEvent::Alloc ================ */ idEvent *idEvent::Alloc( const idEventDef *evdef, int numargs, va_list args ) { idEvent *ev; size_t size; const char *format; idEventArg *arg; byte *dataPtr; int i; const char *materialName; if ( FreeEvents.IsListEmpty() ) { WriteDebugInfo( ); gameLocal.Error( "idEvent::Alloc : No more free events for '%s' event.", evdef->GetName() ); } ev = FreeEvents.Next(); ev->eventNode.Remove(); ev->eventdef = evdef; if ( numargs != evdef->GetNumArgs() ) { gameLocal.Error( "idEvent::Alloc : Wrong number of args for '%s' event.", evdef->GetName() ); } size = evdef->GetArgSize(); if ( size ) { ev->data = eventDataAllocator.Alloc( size ); memset( ev->data, 0, size ); } else { ev->data = NULL; } format = evdef->GetArgFormat(); for( i = 0; i < numargs; i++ ) { arg = va_arg( args, idEventArg * ); if ( format[ i ] != arg->type ) { // RAVEN BEGIN // abahr: type checking change as per Jim D. if ( ( format[ i ] == D_EVENT_ENTITY_NULL ) && ( arg->type == D_EVENT_ENTITY ) ) { // these types are identical, so allow them } else if ( ( arg->type == D_EVENT_INTEGER ) && ( arg->value == 0 ) ) { if ( ( format[ i ] == D_EVENT_ENTITY ) || ( format[ i ] == D_EVENT_ENTITY_NULL ) || ( format[ i ] == D_EVENT_TRACE ) ) { // when NULL is passed in for an entity or trace, it gets cast as an integer 0, so don't give an error when it happens } else { gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() ); } } else { gameLocal.Error( "idEvent::Alloc : Wrong type passed in for arg # %d on '%s' event.", i, evdef->GetName() ); } // RAVEN END } dataPtr = &ev->data[ evdef->GetArgOffset( i ) ]; switch( format[ i ] ) { case D_EVENT_FLOAT : case D_EVENT_INTEGER : *reinterpret_cast<int *>( dataPtr ) = arg->value; break; case D_EVENT_VECTOR : if ( arg->value ) { *reinterpret_cast<idVec3 *>( dataPtr ) = *reinterpret_cast<const idVec3 *>( arg->value ); } break; case D_EVENT_STRING : if ( arg->value ) { idStr::Copynz( reinterpret_cast<char *>( dataPtr ), reinterpret_cast<const char *>( arg->value ), MAX_STRING_LEN ); } break; // RAVEN BEGIN // abahr: type checking change as per Jim D. // jshepard: TODO FIXME HACK this never ever produces desired, positive results. Events should be built to prepare for null entities, especially when dealing with // script events. This will throw a warning, and events should be prepared to deal with null entities. case D_EVENT_ENTITY : if ( reinterpret_cast<idEntity *>( arg->value ) == NULL ) { gameLocal.Warning( "idEvent::Alloc : NULL entity passed in to event function that expects a non-NULL pointer on arg # %d on '%s' event.", i, evdef->GetName() ); } *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value ); break; case D_EVENT_ENTITY_NULL : *reinterpret_cast< idEntityPtr<idEntity> * >( dataPtr ) = reinterpret_cast<idEntity *>( arg->value ); break; //RAVEN END case D_EVENT_TRACE : if ( arg->value ) { *reinterpret_cast<bool *>( dataPtr ) = true; *reinterpret_cast<trace_t *>( dataPtr + sizeof( bool ) ) = *reinterpret_cast<const trace_t *>( arg->value ); // save off the material as a string since the pointer won't be valid in save games. // since we save off the entire trace_t structure, if the material is NULL here, // it will be NULL when we process it, so we don't need to save off anything in that case. if ( reinterpret_cast<const trace_t *>( arg->value )->c.material ) { materialName = reinterpret_cast<const trace_t *>( arg->value )->c.material->GetName(); idStr::Copynz( reinterpret_cast<char *>( dataPtr + sizeof( bool ) + sizeof( trace_t ) ), materialName, MAX_STRING_LEN ); } } else { *reinterpret_cast<bool *>( dataPtr ) = false; } break; default : gameLocal.Error( "idEvent::Alloc : Invalid arg format '%s' string for '%s' event.", format, evdef->GetName() ); break; } } return ev; }