This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/server/global/saverestore.cpp

1292 lines
34 KiB
C++

//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "defaults.h"
#include "saverestore.h"
#include <time.h>
#include "shake.h"
#include "decals.h"
#include "player.h"
#include "baseweapon.h"
#include "gamerules.h"
#include "client.h"
#define ENTVARS_COUNT ( sizeof(gEntvarsDescription) / sizeof(gEntvarsDescription[0]) )
CGlobalState gGlobalState;
TYPEDESCRIPTION gEntvarsDescription[] =
{
DEFINE_ENTITY_FIELD( classname, FIELD_STRING ),
DEFINE_ENTITY_GLOBAL_FIELD( globalname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( origin, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_FIELD( oldorigin, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_FIELD( velocity, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( movedir, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( angles, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( avelocity, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( punchangle, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( viewangles, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( fixangle, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( ideal_pitch, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( pitch_speed, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( ideal_yaw, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( yaw_speed, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( modelindex, FIELD_INTEGER ),
DEFINE_ENTITY_GLOBAL_FIELD( model, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( viewmodel, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( weaponmodel, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( absmin, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_FIELD( absmax, FIELD_POSITION_VECTOR ),
DEFINE_ENTITY_GLOBAL_FIELD( mins, FIELD_VECTOR ),
DEFINE_ENTITY_GLOBAL_FIELD( maxs, FIELD_VECTOR ),
DEFINE_ENTITY_GLOBAL_FIELD( size, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( ltime, FIELD_TIME ),
DEFINE_ENTITY_FIELD( nextthink, FIELD_TIME ),
DEFINE_ENTITY_FIELD( solid, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( movetype, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( skin, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( body, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( effects, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( gravity, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( friction, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( frame, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( scale, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( sequence, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( animtime, FIELD_TIME ),
DEFINE_ENTITY_FIELD( framerate, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( controller, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( blending, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( rendermode, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( renderamt, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( rendercolor, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( renderfx, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( health, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( frags, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( weapons, FIELD_INTEGER64 ),
DEFINE_ENTITY_FIELD( takedamage, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( deadflag, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( view_ofs, FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( button, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( impulse, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( chain, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( dmg_inflictor, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( enemy, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( aiment, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( owner, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( groundentity, FIELD_EDICT ),
DEFINE_ENTITY_FIELD( spawnflags, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( flags, FIELD_INTEGER64 ),
#if 0
DEFINE_ENTITY_FIELD_ARRAY( m_pmatrix, FIELD_VECTOR, 3 ),
DEFINE_ENTITY_FIELD_ARRAY( m_pcentre, FIELD_VECTOR, 3 ),
#else
DEFINE_ENTITY_FIELD( m_pmatrix[0], FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( m_pmatrix[1], FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( m_pmatrix[2], FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( m_pcentre[0], FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( m_pcentre[1], FIELD_VECTOR ),
DEFINE_ENTITY_FIELD( m_pcentre[2], FIELD_VECTOR ),
#endif
DEFINE_ENTITY_FIELD( colormap, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( team, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( max_health, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( teleport_time, FIELD_TIME ),
DEFINE_ENTITY_FIELD( armortype, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( armorvalue, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( waterlevel, FIELD_INTEGER ),
DEFINE_ENTITY_FIELD( watertype, FIELD_INTEGER ),
// Having these fields be local to the individual levels makes it easier to test those levels individually.
DEFINE_ENTITY_GLOBAL_FIELD( target, FIELD_STRING ),
DEFINE_ENTITY_GLOBAL_FIELD( targetname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( netname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( message, FIELD_STRING ),
DEFINE_ENTITY_FIELD( dmg_take, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( dmg_save, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( dmg, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( dmgtime, FIELD_TIME ),
DEFINE_ENTITY_FIELD( fov, FIELD_FLOAT ),
DEFINE_ENTITY_FIELD( noise, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise1, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise2, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise3, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( speed, FIELD_FLOAT ),
};
// --------------------------------------------------------------
//
// CSave
//
// --------------------------------------------------------------
static int gSizes[FIELD_TYPECOUNT] =
{
sizeof(float), // FIELD_FLOAT
sizeof(int), // FIELD_STRING
sizeof(int), // FIELD_ENTITY
sizeof(int), // FIELD_CLASSPTR
sizeof(int), // FIELD_EHANDLE
sizeof(int), // FIELD_entvars_t
sizeof(int), // FIELD_EDICT
sizeof(float)*3, // FIELD_VECTOR
sizeof(float)*3, // FIELD_POSITION_VECTOR
sizeof(int *), // FIELD_POINTER
sizeof(int), // FIELD_INTEGER
sizeof(int *), // FIELD_FUNCTION
sizeof(int), // FIELD_BOOLEAN
sizeof(short), // FIELD_SHORT
sizeof(char), // FIELD_CHARACTER
sizeof(float), // FIELD_TIME
sizeof(int), // FIELD_MODELNAME
sizeof(int), // FIELD_SOUNDNAME
sizeof(float)*2, // FIELD_RANGE
sizeof(int64), // FIELD_INTEGER64
sizeof(double), // FIELD_DOUBLE
};
static const char *gNames[FIELD_TYPECOUNT] =
{
"float", // FIELD_FLOAT
"string", // FIELD_STRING
"entity", // FIELD_ENTITY
"classptr", // FIELD_CLASSPTR
"ehandle", // FIELD_EHANDLE
"entvars", // FIELD_entvars_t
"edict", // FIELD_EDICT
"vector", // FIELD_VECTOR
"position vector", // FIELD_POSITION_VECTOR
"pointer", // FIELD_POINTER
"integer", // FIELD_INTEGER
"function", // FIELD_FUNCTION
"boolean", // FIELD_BOOLEAN
"short", // FIELD_SHORT
"char", // FIELD_CHARACTER
"time", // FIELD_TIME
"modelname", // FIELD_MODELNAME
"soundname", // FIELD_SOUNDNAME
"random range", // FIELD_RANGE
"integer 64", // FIELD_INTEGER64
"double", // FIELD_DOUBLE
};
// Base class includes common SAVERESTOREDATA pointer, and manages the entity table
CSaveRestoreBuffer :: CSaveRestoreBuffer( void )
{
m_pdata = NULL;
}
CSaveRestoreBuffer :: CSaveRestoreBuffer( SAVERESTOREDATA *pdata )
{
m_pdata = pdata;
}
CSaveRestoreBuffer :: ~CSaveRestoreBuffer( void )
{
}
int CSaveRestoreBuffer :: EntityIndex( CBaseEntity *pEntity )
{
if ( pEntity == NULL )
return -1;
return EntityIndex( pEntity->pev );
}
int CSaveRestoreBuffer :: EntityIndex( entvars_t *pevLookup )
{
if ( pevLookup == NULL )
return -1;
return EntityIndex( ENT( pevLookup ) );
}
int CSaveRestoreBuffer :: EntityIndex( EOFFSET eoLookup )
{
return EntityIndex( ENT( eoLookup ) );
}
int CSaveRestoreBuffer :: EntityIndex( edict_t *pentLookup )
{
if ( !m_pdata || pentLookup == NULL )
return -1;
int i;
ENTITYTABLE *pTable;
for ( i = 0; i < m_pdata->tableCount; i++ )
{
pTable = m_pdata->pTable + i;
if ( pTable->pent == pentLookup )
return i;
}
return -1;
}
edict_t *CSaveRestoreBuffer :: EntityFromIndex( int entityIndex )
{
if ( !m_pdata || entityIndex < 0 )
return NULL;
int i;
ENTITYTABLE *pTable;
for ( i = 0; i < m_pdata->tableCount; i++ )
{
pTable = m_pdata->pTable + i;
if ( pTable->id == entityIndex )
return pTable->pent;
}
return NULL;
}
int CSaveRestoreBuffer :: EntityFlagsSet( int entityIndex, int flags )
{
if ( !m_pdata || entityIndex < 0 )
return 0;
if ( entityIndex > m_pdata->tableCount )
return 0;
m_pdata->pTable[ entityIndex ].flags |= flags;
return m_pdata->pTable[ entityIndex ].flags;
}
void CSaveRestoreBuffer :: BufferRewind( int size )
{
if ( !m_pdata )
return;
if ( m_pdata->size < size )
size = m_pdata->size;
m_pdata->pCurrentData -= size;
m_pdata->size -= size;
}
#ifndef _WIN32
extern "C" {
unsigned _rotr ( unsigned val, int shift)
{
register unsigned lobit; /* non-zero means lo bit set */
register unsigned num = val; /* number to rotate */
shift &= 0x1f; /* modulo 32 -- this will also make
negative shifts work */
while (shift--) {
lobit = num & 1; /* get high bit */
num >>= 1; /* shift right one bit */
if (lobit)
num |= 0x80000000; /* set hi bit if lo bit was set */
}
return num;
}
}
#endif
unsigned int CSaveRestoreBuffer :: HashString( const char *pszToken )
{
unsigned int hash = 0;
while ( *pszToken )
hash = _rotr( hash, 4 ) ^ *pszToken++;
return hash;
}
unsigned short CSaveRestoreBuffer :: TokenHash( const char *pszToken )
{
unsigned short hash = (unsigned short)(HashString( pszToken ) % (unsigned)m_pdata->tokenCount );
#if _DEBUG
static int tokensparsed = 0;
tokensparsed++;
if ( !m_pdata->tokenCount || !m_pdata->pTokens )
ALERT( at_error, "No token table array in TokenHash()!" );
#endif
for ( int i=0; i<m_pdata->tokenCount; i++ )
{
#if _DEBUG
static BOOL beentheredonethat = FALSE;
if ( i > 50 && !beentheredonethat )
{
beentheredonethat = TRUE;
ALERT( at_error, "CSaveRestoreBuffer :: TokenHash() is getting too full!" );
}
#endif
int index = hash + i;
if ( index >= m_pdata->tokenCount )
index -= m_pdata->tokenCount;
if ( !m_pdata->pTokens[index] || strcmp( pszToken, m_pdata->pTokens[index] ) == 0 )
{
m_pdata->pTokens[index] = (char *)pszToken;
return index;
}
}
// Token hash table full!!!
// [Consider doing overflow table(s) after the main table & limiting linear hash table search]
ALERT( at_error, "CSaveRestoreBuffer :: TokenHash() is COMPLETELY FULL!" );
return 0;
}
void CSave :: WriteData( const char *pname, int size, const char *pdata )
{
BufferField( pname, size, pdata );
}
void CSave :: WriteShort( const char *pname, const short *data, int count )
{
BufferField( pname, sizeof(short) * count, (const char *)data );
}
void CSave :: WriteInt( const char *pname, const int *data, int count )
{
BufferField( pname, sizeof(int) * count, (const char *)data );
}
void CSave :: WriteInt64( const char *pname, const int64 *data, int count )
{
BufferField( pname, sizeof(int64) * count, (const char *)data );
}
void CSave :: WriteFloat( const char *pname, const float *data, int count )
{
BufferField( pname, sizeof(float) * count, (const char *)data );
}
void CSave :: WriteDouble( const char *pname, const double *data, int count )
{
BufferField( pname, sizeof( double ) * count, (const char *)data );
}
void CSave :: WriteTime( const char *pname, const float *data, int count )
{
int i;
Vector tmp, input;
BufferHeader( pname, sizeof(float) * count );
for ( i = 0; i < count; i++ )
{
float tmp = data[0];
// Always encode time as a delta from the current time so it can be re-based if loaded in a new level
// Times of 0 are never written to the file, so they will be restored as 0, not a relative time
if( m_pdata ) tmp -= m_pdata->time;
BufferData( (const char *)&tmp, sizeof( float ));
data++;
}
}
void CSave :: WriteString( const char *pname, const char *pdata )
{
#ifdef TOKENIZE
short token = (short)TokenHash( pdata );
WriteShort( pname, &token, 1 );
#else
BufferField( pname, strlen(pdata) + 1, pdata );
#endif
}
void CSave :: WriteString( const char *pname, const int *stringId, int count )
{
int i, size;
#ifdef TOKENIZE
short token = (short)TokenHash( STRING( *stringId ) );
WriteShort( pname, &token, 1 );
#else
#if 0
if ( count != 1 )
ALERT( at_error, "No string arrays!\n" );
WriteString( pname, (char *)STRING(*stringId) );
#endif
size = 0;
for ( i = 0; i < count; i++ )
size += strlen( STRING( stringId[i] ) ) + 1;
BufferHeader( pname, size );
for ( i = 0; i < count; i++ )
{
const char *pString = STRING(stringId[i]);
BufferData( pString, strlen(pString)+1 );
}
#endif
}
void CSave :: WriteVector( const char *pname, const Vector &value )
{
WriteVector( pname, &value.x, 1 );
}
void CSave :: WriteRange( const char *pname, const RandomRange &value )
{
WriteRange( pname, &value.m_flMin, 1 );
}
void CSave :: WriteRange( const char *pname, const float *value, int count )
{
BufferHeader( pname, sizeof(float) * 2 * count );
BufferData( (const char *)value, sizeof(float) * 2 * count );
}
void CSave :: WriteVector( const char *pname, const float *value, int count )
{
BufferHeader( pname, sizeof(float) * 3 * count );
BufferData( (const char *)value, sizeof(float) * 3 * count );
}
void CSave :: WritePositionVector( const char *pname, const Vector &value )
{
if ( m_pdata && m_pdata->fUseLandmark )
{
Vector tmp = value - m_pdata->vecLandmarkOffset;
WriteVector( pname, tmp );
}
WriteVector( pname, value );
}
void CSave :: WritePositionVector( const char *pname, const float *value, int count )
{
int i;
Vector tmp, input;
BufferHeader( pname, sizeof(float) * 3 * count );
for ( i = 0; i < count; i++ )
{
Vector tmp( value[0], value[1], value[2] );
if ( m_pdata && m_pdata->fUseLandmark )
tmp = tmp - m_pdata->vecLandmarkOffset;
BufferData( (const char *)&tmp.x, sizeof(float) * 3 );
value += 3;
}
}
void CSave :: WriteFunction( const char* cname, const char *pname, const int *data, int count )
{
const char *functionName;
functionName = NAME_FOR_FUNCTION( *data );
if ( functionName )
BufferField( pname, strlen(functionName) + 1, functionName );
else ALERT( at_error, "Member \"%s\" of \"%s\" contains an invalid function pointer %p!", pname, cname, *data );
}
void EntvarsKeyvalue( entvars_t *pev, KeyValueData *pkvd )
{
int i;
TYPEDESCRIPTION *pField;
for ( i = 0; i < ENTVARS_COUNT; i++ )
{
pField = &gEntvarsDescription[i];
if ( !stricmp( pField->fieldName, pkvd->szKeyName ) )
{
switch( pField->fieldType )
{
case FIELD_MODELNAME:
case FIELD_SOUNDNAME:
case FIELD_STRING:
(*(int *)((char *)pev + pField->fieldOffset)) = ALLOC_STRING( pkvd->szValue );
break;
case FIELD_TIME:
case FIELD_FLOAT:
(*(float *)((char *)pev + pField->fieldOffset)) = atof( pkvd->szValue );
break;
case FIELD_DOUBLE:
(*(double *)((char *)pev + pField->fieldOffset)) = atof( pkvd->szValue );
break;
case FIELD_INTEGER:
(*(int *)((char *)pev + pField->fieldOffset)) = atoi( pkvd->szValue );
break;
case FIELD_INTEGER64:
(*(int64 *)((char *)pev + pField->fieldOffset)) = _atoi64( pkvd->szValue );
break;
case FIELD_POSITION_VECTOR:
case FIELD_VECTOR:
UTIL_StringToVector( (float *)((char *)pev + pField->fieldOffset), pkvd->szValue );
break;
default:
case FIELD_EVARS:
case FIELD_CLASSPTR:
case FIELD_EDICT:
case FIELD_ENTITY:
case FIELD_POINTER:
ALERT( at_error, "Bad field in entity!!\n" );
break;
}
pkvd->fHandled = TRUE;
return;
}
}
}
int CSave :: WriteEntVars( const char *pname, entvars_t *pev )
{
if (pev->targetname)
return WriteFields( STRING(pev->targetname), pname, pev, gEntvarsDescription, ENTVARS_COUNT );
else
return WriteFields( STRING(pev->classname), pname, pev, gEntvarsDescription, ENTVARS_COUNT );
}
int CSave :: WriteFields( const char *cname, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
{
int i, j, actualCount, emptyCount;
TYPEDESCRIPTION *pTest;
int entityArray[MAX_ENTITYARRAY];
ALERT( at_console, "CSave::WriteFields( %s [%i fields])\n", pname, fieldCount );
if( !strcmp( pname, "Save Header" ) || !strcmp( pname, "ADJACENCY" ) || !strcmp( pname, "Game Header" ))
{
for( i = 0; i < fieldCount; i++ )
ALERT( at_console, "FIELD: %s [%s][0x%x]\n", pFields[i].fieldName, gNames[pFields[i].fieldType], pFields[i].flags );
}
// precalculate the number of empty fields
emptyCount = 0;
for ( i = 0; i < fieldCount; i++ )
{
void *pOutputData;
pOutputData = ((char *)pBaseData + pFields[i].fieldOffset );
if ( DataEmpty( (const char *)pOutputData, pFields[i].fieldSize * gSizes[pFields[i].fieldType] ) )
emptyCount++;
}
// Empty fields will not be written, write out the actual number of fields to be written
actualCount = fieldCount - emptyCount;
WriteInt( pname, &actualCount, 1 );
for ( i = 0; i < fieldCount; i++ )
{
void *pOutputData;
pTest = &pFields[ i ];
pOutputData = ((char *)pBaseData + pTest->fieldOffset );
// UNDONE: Must we do this twice?
if ( DataEmpty( (const char *)pOutputData, pTest->fieldSize * gSizes[pTest->fieldType] ) )
continue;
switch( pTest->fieldType )
{
case FIELD_FLOAT:
WriteFloat( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
break;
case FIELD_DOUBLE:
WriteDouble( pTest->fieldName, (double *)pOutputData, pTest->fieldSize );
break;
case FIELD_TIME:
WriteTime( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
break;
case FIELD_MODELNAME:
case FIELD_SOUNDNAME:
case FIELD_STRING:
WriteString( pTest->fieldName, (int *)pOutputData, pTest->fieldSize );
break;
case FIELD_CLASSPTR:
case FIELD_EVARS:
case FIELD_EDICT:
case FIELD_ENTITY:
case FIELD_EHANDLE:
if( pTest->fieldSize > MAX_ENTITYARRAY )
ALERT( at_error, "Can't save more than %d entities in an array!!!\n", MAX_ENTITYARRAY );
for( j = 0; j < pTest->fieldSize; j++ )
{
switch( pTest->fieldType )
{
case FIELD_EVARS:
entityArray[j] = EntityIndex( ((entvars_t **)pOutputData)[j] );
break;
case FIELD_CLASSPTR:
entityArray[j] = EntityIndex( ((CBaseEntity **)pOutputData)[j] );
break;
case FIELD_EDICT:
entityArray[j] = EntityIndex( ((edict_t **)pOutputData)[j] );
break;
case FIELD_ENTITY:
entityArray[j] = EntityIndex( ((EOFFSET *)pOutputData)[j] );
break;
case FIELD_EHANDLE:
entityArray[j] = EntityIndex( (CBaseEntity *)(((EHANDLE *)pOutputData)[j]) );
break;
}
}
WriteInt( pTest->fieldName, entityArray, pTest->fieldSize );
break;
case FIELD_POSITION_VECTOR:
WritePositionVector( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
break;
case FIELD_VECTOR:
WriteVector( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
break;
case FIELD_RANGE:
WriteRange( pTest->fieldName, (float *)pOutputData, pTest->fieldSize );
break;
case FIELD_BOOLEAN:
case FIELD_INTEGER:
WriteInt( pTest->fieldName, (int *)pOutputData, pTest->fieldSize );
break;
case FIELD_INTEGER64:
WriteInt64( pTest->fieldName, (int64 *)pOutputData, pTest->fieldSize );
break;
case FIELD_SHORT:
WriteData( pTest->fieldName, 2 * pTest->fieldSize, ((char *)pOutputData) );
break;
case FIELD_CHARACTER:
WriteData( pTest->fieldName, pTest->fieldSize, ((char *)pOutputData) );
break;
// For now, just write the address out, we're not going to change memory while doing this yet!
case FIELD_POINTER:
WriteInt( pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize );
break;
case FIELD_FUNCTION:
WriteFunction( cname, pTest->fieldName, (int *)(char *)pOutputData, pTest->fieldSize );
break;
default:
ALERT( at_error, "Bad field type\n" );
break;
}
}
return 1;
}
void CSave :: BufferString( char *pdata, int len )
{
char c = 0;
BufferData( pdata, len ); // Write the string
BufferData( &c, 1 ); // Write a null terminator
}
int CSave :: DataEmpty( const char *pdata, int size )
{
for ( int i = 0; i < size; i++ )
{
if ( pdata[i] )
return 0;
}
return 1;
}
void CSave :: BufferField( const char *pname, int size, const char *pdata )
{
BufferHeader( pname, size );
BufferData( pdata, size );
}
void CSave :: BufferHeader( const char *pname, int size )
{
short hashvalue = TokenHash( pname );
if ( size > 1<<(sizeof(short)*8) )
ALERT( at_error, "CSave :: BufferHeader() size parameter exceeds 'short'!" );
BufferData( (const char *)&size, sizeof(short) );
BufferData( (const char *)&hashvalue, sizeof(short) );
}
void CSave :: BufferData( const char *pdata, int size )
{
if ( !m_pdata )
return;
if ( m_pdata->size + size > m_pdata->bufferSize )
{
ALERT( at_error, "Save/Restore overflow!" );
m_pdata->size = m_pdata->bufferSize;
return;
}
memcpy( m_pdata->pCurrentData, pdata, size );
m_pdata->pCurrentData += size;
m_pdata->size += size;
}
// --------------------------------------------------------------
//
// CRestore
//
// --------------------------------------------------------------
int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount, int startField, int size, char *pName, void *pData )
{
int i, j, stringCount, fieldNumber, entityIndex;
TYPEDESCRIPTION *pTest;
float time, timeData;
Vector position;
edict_t *pent;
char *pString;
time = 0;
position = Vector(0,0,0);
if( m_pdata )
{
time = m_pdata->time;
if ( m_pdata->fUseLandmark )
position = m_pdata->vecLandmarkOffset;
}
for( i = 0; i < fieldCount; i++ )
{
fieldNumber = (i + startField) % fieldCount;
pTest = &pFields[fieldNumber];
if( !stricmp( pTest->fieldName, pName ))
{
if ( !m_global || !(pTest->flags & FTYPEDESC_GLOBAL) )
{
for ( j = 0; j < pTest->fieldSize; j++ )
{
void *pOutputData = ((char *)pBaseData + pTest->fieldOffset + (j*gSizes[pTest->fieldType]) );
void *pInputData = (char *)pData + j * gSizes[pTest->fieldType];
switch( pTest->fieldType )
{
case FIELD_TIME:
timeData = *(float *)pInputData;
// re-base time variables
timeData += time;
*((float *)pOutputData) = timeData;
break;
case FIELD_FLOAT:
*((float *)pOutputData) = *(float *)pInputData;
break;
case FIELD_DOUBLE:
*((double *)pOutputData) = *(double *)pInputData;
break;
case FIELD_MODELNAME:
case FIELD_SOUNDNAME:
case FIELD_STRING:
// skip over j strings
pString = (char *)pData;
for ( stringCount = 0; stringCount < j; stringCount++ )
{
while( *pString )
pString++;
pString++;
}
pInputData = pString;
if( strlen((char *)pInputData ) == 0 )
*((int *)pOutputData) = 0;
else
{
int string;
string = ALLOC_STRING( (char *)pInputData );
*((int *)pOutputData) = string;
if ( !FStringNull( string ) && m_precache )
{
if ( pTest->fieldType == FIELD_MODELNAME )
UTIL_PrecacheModel( string );
else if ( pTest->fieldType == FIELD_SOUNDNAME )
UTIL_PrecacheSound( string );
}
}
break;
case FIELD_EVARS:
entityIndex = *( int *)pInputData;
pent = EntityFromIndex( entityIndex );
if ( pent ) *((entvars_t **)pOutputData) = VARS(pent);
else *((entvars_t **)pOutputData) = NULL;
break;
case FIELD_CLASSPTR:
entityIndex = *( int *)pInputData;
pent = EntityFromIndex( entityIndex );
if ( pent )
*((CBaseEntity **)pOutputData) = CBaseEntity::Instance(pent);
else
{
*((CBaseEntity **)pOutputData) = NULL;
if (entityIndex != -1) ALERT(at_console, "## Restore: invalid entitynum %d\n", entityIndex);
}
break;
case FIELD_EDICT:
entityIndex = *( int *)pInputData;
pent = EntityFromIndex( entityIndex );
*((edict_t **)pOutputData) = pent;
break;
case FIELD_EHANDLE:
// Input and Output sizes are different!
pOutputData = (char *)pOutputData + j*(sizeof(EHANDLE) - gSizes[pTest->fieldType]);
entityIndex = *( int *)pInputData;
pent = EntityFromIndex( entityIndex );
if ( pent )
*((EHANDLE *)pOutputData) = CBaseEntity::Instance(pent);
else
*((EHANDLE *)pOutputData) = NULL;
break;
case FIELD_ENTITY:
entityIndex = *( int *)pInputData;
pent = EntityFromIndex( entityIndex );
if ( pent )
*((EOFFSET *)pOutputData) = OFFSET(pent);
else
*((EOFFSET *)pOutputData) = 0;
break;
case FIELD_RANGE:
((float *)pOutputData)[0] = ((float *)pInputData)[0];
((float *)pOutputData)[1] = ((float *)pInputData)[1];
break;
case FIELD_VECTOR:
((float *)pOutputData)[0] = ((float *)pInputData)[0];
((float *)pOutputData)[1] = ((float *)pInputData)[1];
((float *)pOutputData)[2] = ((float *)pInputData)[2];
break;
case FIELD_POSITION_VECTOR:
((float *)pOutputData)[0] = ((float *)pInputData)[0] + position.x;
((float *)pOutputData)[1] = ((float *)pInputData)[1] + position.y;
((float *)pOutputData)[2] = ((float *)pInputData)[2] + position.z;
break;
case FIELD_BOOLEAN:
case FIELD_INTEGER:
*((int *)pOutputData) = *( int *)pInputData;
break;
case FIELD_INTEGER64:
*((int64 *)pOutputData) = *( int64 *)pInputData;
break;
case FIELD_SHORT:
*((short *)pOutputData) = *( short *)pInputData;
break;
case FIELD_CHARACTER:
*((char *)pOutputData) = *( char *)pInputData;
break;
case FIELD_POINTER:
*((int *)pOutputData) = *( int *)pInputData;
break;
case FIELD_FUNCTION:
if ( strlen( (char *)pInputData ) == 0 )
*((int *)pOutputData) = 0;
else
*((int *)pOutputData) = FUNCTION_FROM_NAME( (char *)pInputData );
break;
default:
ALERT( at_error, "Bad field type\n" );
break;
}
}
}
#if 0
else
{
ALERT( at_console, "Skipping global field %s\n", pName );
}
#endif
return fieldNumber;
}
}
return -1;
}
int CRestore::ReadEntVars( const char *pname, entvars_t *pev )
{
return ReadFields( pname, pev, gEntvarsDescription, ENTVARS_COUNT );
}
int CRestore::ReadFields( const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount )
{
unsigned short i, token;
int lastField, fileCount;
HEADER header;
i = ReadShort();
ASSERT( i == sizeof( int )); // First entry should be an int
token = ReadShort();
// Check the struct name
if( token != TokenHash( pname )) // Field Set marker
{
ALERT( at_error, "Expected %s found %s!\n", pname, BufferPointer() );
BufferRewind( 2 * sizeof( short ));
return 0;
}
ALERT( at_console, "CRestore:ReadFields: %s\n", pname );
// Skip over the struct name
fileCount = ReadInt(); // Read field count
lastField = 0; // Make searches faster, most data is read/written in the same order
// clear out base data
for( i = 0; i < fieldCount; i++ )
{
// Don't clear global fields
if ( !m_global || !(pFields[i].flags & FTYPEDESC_GLOBAL) )
memset( ((char *)pBaseData + pFields[i].fieldOffset), 0, pFields[i].fieldSize * gSizes[pFields[i].fieldType] );
}
for ( i = 0; i < fileCount; i++ )
{
BufferReadHeader( &header );
lastField = ReadField( pBaseData, pFields, fieldCount, lastField, header.size, m_pdata->pTokens[header.token], header.pData );
lastField++;
}
return 1;
}
void CRestore::BufferReadHeader( HEADER *pheader )
{
ASSERT( pheader!=NULL );
pheader->size = ReadShort(); // Read field size
pheader->token = ReadShort(); // Read field name token
pheader->pData = BufferPointer(); // Field Data is next
BufferSkipBytes( pheader->size ); // Advance to next field
}
short CRestore::ReadShort( void )
{
short tmp = 0;
BufferReadBytes( (char *)&tmp, sizeof(short) );
return tmp;
}
int CRestore::ReadInt( void )
{
int tmp = 0;
BufferReadBytes( (char *)&tmp, sizeof(int) );
return tmp;
}
int CRestore::ReadNamedInt( const char *pName )
{
HEADER header;
BufferReadHeader( &header );
return ((int *)header.pData)[0];
}
char *CRestore::ReadNamedString( const char *pName )
{
HEADER header;
BufferReadHeader( &header );
#ifdef TOKENIZE
return (char *)(m_pdata->pTokens[*(short *)header.pData]);
#else
return (char *)header.pData;
#endif
}
char *CRestore::BufferPointer( void )
{
if ( !m_pdata )
return NULL;
return m_pdata->pCurrentData;
}
void CRestore::BufferReadBytes( char *pOutput, int size )
{
ASSERT( m_pdata !=NULL );
if ( !m_pdata || Empty() )
return;
if ( (m_pdata->size + size) > m_pdata->bufferSize )
{
ALERT( at_error, "Restore overflow!" );
m_pdata->size = m_pdata->bufferSize;
return;
}
if ( pOutput )
memcpy( pOutput, m_pdata->pCurrentData, size );
m_pdata->pCurrentData += size;
m_pdata->size += size;
}
void CRestore::BufferSkipBytes( int bytes )
{
BufferReadBytes( NULL, bytes );
}
int CRestore::BufferSkipZString( void )
{
char *pszSearch;
int len;
if ( !m_pdata )
return 0;
int maxLen = m_pdata->bufferSize - m_pdata->size;
len = 0;
pszSearch = m_pdata->pCurrentData;
while ( *pszSearch++ && len < maxLen )
len++;
len++;
BufferSkipBytes( len );
return len;
}
int CRestore::BufferCheckZString( const char *string )
{
if ( !m_pdata )
return 0;
int maxLen = m_pdata->bufferSize - m_pdata->size;
int len = strlen( string );
if ( len <= maxLen )
{
if ( !strncmp( string, m_pdata->pCurrentData, len ) )
return 1;
}
return 0;
}
//=======================================================================
// global entity states
//=======================================================================
CGlobalState::CGlobalState( void )
{
Reset();
}
void CGlobalState::Reset( void )
{
m_pList = NULL;
m_listCount = 0;
}
globalentity_t *CGlobalState :: Find( string_t globalname )
{
if ( !globalname )
return NULL;
globalentity_t *pTest;
const char *pEntityName = STRING(globalname);
pTest = m_pList;
while ( pTest )
{
if ( FStrEq( pEntityName, pTest->name ) )
break;
pTest = pTest->pNext;
}
return pTest;
}
void CGlobalState :: DumpGlobals( void )
{
static char *estates[] = { "Off", "On", "Dead" };
globalentity_t *pTest;
ALERT( at_console, "-- Globals --\n" );
pTest = m_pList;
while ( pTest )
{
Msg( "%s: %s (%s)\n", pTest->name, pTest->levelName, estates[pTest->state] );
pTest = pTest->pNext;
}
}
void CGlobalState :: EntityAdd( string_t globalname, string_t mapName, GLOBALESTATE state )
{
ASSERT( !Find(globalname) );
globalentity_t *pNewEntity = (globalentity_t *)CALLOC( sizeof( globalentity_t ), 1 );
ASSERT( pNewEntity != NULL );
pNewEntity->pNext = m_pList;
m_pList = pNewEntity;
strcpy( pNewEntity->name, STRING( globalname ) );
strcpy( pNewEntity->levelName, STRING(mapName) );
pNewEntity->state = state;
m_listCount++;
}
void CGlobalState :: EntitySetState( string_t globalname, GLOBALESTATE state )
{
globalentity_t *pEnt = Find( globalname );
if ( pEnt ) pEnt->state = state;
}
const globalentity_t *CGlobalState :: EntityFromTable( string_t globalname )
{
globalentity_t *pEnt = Find( globalname );
return pEnt;
}
GLOBALESTATE CGlobalState :: EntityGetState( string_t globalname )
{
globalentity_t *pEnt = Find( globalname );
if ( pEnt ) return pEnt->state;
return GLOBAL_OFF;
}
TYPEDESCRIPTION CGlobalState::m_SaveData[] =
{
DEFINE_FIELD( CGlobalState, m_listCount, FIELD_INTEGER ),
};
TYPEDESCRIPTION gGlobalEntitySaveData[] =
{
DEFINE_ARRAY( globalentity_t, name, FIELD_CHARACTER, 64 ),
DEFINE_ARRAY( globalentity_t, levelName, FIELD_CHARACTER, 32 ),
DEFINE_FIELD( globalentity_t, state, FIELD_INTEGER ),
};
int CGlobalState::Save( CSave &save )
{
int i;
globalentity_t *pEntity;
if ( !save.WriteFields( "cGLOBAL", "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) )
return 0;
pEntity = m_pList;
for ( i = 0; i < m_listCount && pEntity; i++ )
{
if ( !save.WriteFields( "cGENT", "GENT", pEntity, gGlobalEntitySaveData, ARRAYSIZE(gGlobalEntitySaveData) ) )
return 0;
pEntity = pEntity->pNext;
}
return 1;
}
int CGlobalState::Restore( CRestore &restore )
{
int i, listCount;
globalentity_t tmpEntity;
ClearStates();
if ( !restore.ReadFields( "GLOBAL", this, m_SaveData, ARRAYSIZE(m_SaveData) ) ) return 0;
listCount = m_listCount; // Get new list count
m_listCount = 0; // Clear loaded data
for ( i = 0; i < listCount; i++ )
{
if ( !restore.ReadFields( "GENT", &tmpEntity, gGlobalEntitySaveData, ARRAYSIZE(gGlobalEntitySaveData) ) )
return 0;
EntityAdd( MAKE_STRING(tmpEntity.name), MAKE_STRING(tmpEntity.levelName), tmpEntity.state );
}
return 1;
}
void CGlobalState::EntityUpdate( string_t globalname, string_t mapname )
{
globalentity_t *pEnt = Find( globalname );
if ( pEnt ) strcpy( pEnt->levelName, STRING(mapname) );
}
void CGlobalState::ClearStates( void )
{
globalentity_t *pFree = m_pList;
while ( pFree )
{
globalentity_t *pNext = pFree->pNext;
FREE( pFree );
pFree = pNext;
}
Reset();
}
void SaveGlobalState( SAVERESTOREDATA *pSaveData )
{
CSave saveHelper( pSaveData );
gGlobalState.Save( saveHelper );
}
void RestoreGlobalState( SAVERESTOREDATA *pSaveData )
{
CRestore restoreHelper( pSaveData );
gGlobalState.Restore( restoreHelper );
}
void ResetGlobalState( void )
{
gGlobalState.ClearStates();
gInitHUD = TRUE; // Init the HUD on a new game / load game
}