aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/saverestore.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/saverestore.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/saverestore.cpp')
-rw-r--r--mp/src/game/shared/saverestore.cpp6884
1 files changed, 3442 insertions, 3442 deletions
diff --git a/mp/src/game/shared/saverestore.cpp b/mp/src/game/shared/saverestore.cpp
index 708da993..8e182ff4 100644
--- a/mp/src/game/shared/saverestore.cpp
+++ b/mp/src/game/shared/saverestore.cpp
@@ -1,3442 +1,3442 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Helper classes and functions for the save/restore system.
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include <limits.h>
-#include "isaverestore.h"
-#include "saverestore.h"
-#include <stdarg.h>
-#include "shake.h"
-#include "decals.h"
-#include "gamerules.h"
-#include "bspfile.h"
-#include "mathlib/mathlib.h"
-#include "engine/IEngineSound.h"
-#include "saverestoretypes.h"
-#include "saverestore_utlvector.h"
-#include "model_types.h"
-#include "igamesystem.h"
-#include "interval.h"
-#include "vphysics/object_hash.h"
-#include "datacache/imdlcache.h"
-#include "tier0/vprof.h"
-
-#if !defined( CLIENT_DLL )
-
-#include "globalstate.h"
-#include "entitylist.h"
-
-#else
-
-#include "gamestringpool.h"
-
-#endif
-
-// HACKHACK: Builds a global list of entities that were restored from all levels
-#if !defined( CLIENT_DLL )
-void AddRestoredEntity( CBaseEntity *pEntity );
-#else
-void AddRestoredEntity( C_BaseEntity *pEntity );
-#endif
-
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define MAX_ENTITYARRAY 1024
-#define ZERO_TIME ((FLT_MAX*-0.5))
-// A bit arbitrary, but unlikely to collide with any saved games...
-#define TICK_NEVER_THINK_ENCODE ( INT_MAX - 3 )
-
-ASSERT_INVARIANT( sizeof(EHandlePlaceholder_t) == sizeof(EHANDLE) );
-
-//-----------------------------------------------------------------------------
-
-static int gSizes[FIELD_TYPECOUNT] =
-{
- FIELD_SIZE( FIELD_VOID ),
- FIELD_SIZE( FIELD_FLOAT ),
- FIELD_SIZE( FIELD_STRING ),
- FIELD_SIZE( FIELD_VECTOR ),
- FIELD_SIZE( FIELD_QUATERNION ),
- FIELD_SIZE( FIELD_INTEGER ),
- FIELD_SIZE( FIELD_BOOLEAN ),
- FIELD_SIZE( FIELD_SHORT ),
- FIELD_SIZE( FIELD_CHARACTER ),
- FIELD_SIZE( FIELD_COLOR32 ),
- FIELD_SIZE( FIELD_EMBEDDED ),
- FIELD_SIZE( FIELD_CUSTOM ),
-
- FIELD_SIZE( FIELD_CLASSPTR ),
- FIELD_SIZE( FIELD_EHANDLE ),
- FIELD_SIZE( FIELD_EDICT ),
-
- FIELD_SIZE( FIELD_POSITION_VECTOR ),
- FIELD_SIZE( FIELD_TIME ),
- FIELD_SIZE( FIELD_TICK ),
- FIELD_SIZE( FIELD_MODELNAME ),
- FIELD_SIZE( FIELD_SOUNDNAME ),
-
- FIELD_SIZE( FIELD_INPUT ),
- FIELD_SIZE( FIELD_FUNCTION ),
- FIELD_SIZE( FIELD_VMATRIX ),
- FIELD_SIZE( FIELD_VMATRIX_WORLDSPACE ),
- FIELD_SIZE( FIELD_MATRIX3X4_WORLDSPACE ),
- FIELD_SIZE( FIELD_INTERVAL ),
- FIELD_SIZE( FIELD_MODELINDEX ),
- FIELD_SIZE( FIELD_MATERIALINDEX ),
-
- FIELD_SIZE( FIELD_VECTOR2D ),
-};
-
-
-// helpers to offset worldspace matrices
-static void VMatrixOffset( VMatrix &dest, const VMatrix &matrixIn, const Vector &offset )
-{
- dest = matrixIn;
- dest.PostTranslate( offset );
-}
-
-static void Matrix3x4Offset( matrix3x4_t& dest, const matrix3x4_t& matrixIn, const Vector &offset )
-{
- MatrixCopy( matrixIn, dest );
- Vector out;
- MatrixGetColumn( matrixIn, 3, out );
- out += offset;
- MatrixSetColumn( out, 3, dest );
-}
-
-// This does the necessary casting / extract to grab a pointer to a member function as a void *
-// UNDONE: Cast to BASEPTR or something else here?
-#define EXTRACT_INPUTFUNC_FUNCTIONPTR(x) (*(inputfunc_t **)(&(x)))
-
-//-----------------------------------------------------------------------------
-// Purpose: Search this datamap for the name of this member function
-// This is used to save/restore function pointers (convert pointer to text)
-// Input : *function - pointer to member function
-// Output : const char * - function name
-//-----------------------------------------------------------------------------
-const char *UTIL_FunctionToName( datamap_t *pMap, inputfunc_t *function )
-{
- while ( pMap )
- {
- for ( int i = 0; i < pMap->dataNumFields; i++ )
- {
- if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE )
- {
-#ifdef WIN32
- Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) );
-#elif defined(POSIX)
- Assert( sizeof(pMap->dataDesc[i].inputFunc) == 8 );
-#else
-#error
-#endif
- inputfunc_t *pTest = EXTRACT_INPUTFUNC_FUNCTIONPTR(pMap->dataDesc[i].inputFunc);
-
- if ( pTest == function )
- return pMap->dataDesc[i].fieldName;
- }
- }
- pMap = pMap->baseMap;
- }
-
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Search the datamap for a function named pName
-// This is used to save/restore function pointers (convert text back to pointer)
-// Input : *pName - name of the member function
-//-----------------------------------------------------------------------------
-inputfunc_t *UTIL_FunctionFromName( datamap_t *pMap, const char *pName )
-{
- while ( pMap )
- {
- for ( int i = 0; i < pMap->dataNumFields; i++ )
- {
-#ifdef WIN32
- Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) );
-#elif defined(POSIX)
- Assert( sizeof(pMap->dataDesc[i].inputFunc) == 8 );
-#else
-#error
-#endif
-
- if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE )
- {
- if ( FStrEq( pName, pMap->dataDesc[i].fieldName ) )
- {
- return EXTRACT_INPUTFUNC_FUNCTIONPTR(pMap->dataDesc[i].inputFunc);
- }
- }
- }
- pMap = pMap->baseMap;
- }
-
- Msg( "Failed to find function %s\n", pName );
-
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-//
-// CSave
-//
-//-----------------------------------------------------------------------------
-
-CSave::CSave( CSaveRestoreData *pdata )
- : m_pData(pdata),
- m_pGameInfo( pdata ),
- m_bAsync( pdata->bAsync )
-{
- m_BlockStartStack.EnsureCapacity( 32 );
-
- // Logging.
- m_hLogFile = NULL;
-}
-
-//-------------------------------------
-
-inline int CSave::DataEmpty( const char *pdata, int size )
-{
- if ( size != 4 )
- {
- const char *pLimit = pdata + size;
- while ( pdata < pLimit )
- {
- if ( *pdata++ )
- return 0;
- }
- return 1;
- }
-
- return ( *((int *)pdata) == 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Start logging save data.
-//-----------------------------------------------------------------------------
-void CSave::StartLogging( const char *pszLogName )
-{
- m_hLogFile = filesystem->Open( pszLogName, "w" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Stop logging save data.
-//-----------------------------------------------------------------------------
-void CSave::EndLogging( void )
-{
- if ( m_hLogFile )
- {
- filesystem->Close( m_hLogFile );
- }
- m_hLogFile = NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Check to see if we are logging data.
-//-----------------------------------------------------------------------------
-bool CSave::IsLogging( void )
-{
- return ( m_hLogFile != NULL );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Log data.
-//-----------------------------------------------------------------------------
-void CSave::Log( const char *pName, fieldtype_t fieldType, void *value, int count )
-{
- // Check to see if we are logging.
- if ( !IsLogging() )
- return;
-
- static char szBuf[1024];
- static char szTempBuf[256];
-
- // Save the name.
- Q_snprintf( szBuf, sizeof( szBuf ), "%s ", pName );
-
- for ( int iCount = 0; iCount < count; ++iCount )
- {
- switch ( fieldType )
- {
- case FIELD_SHORT:
- {
- short *pValue = ( short* )( value );
- short nValue = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_FLOAT:
- {
- float *pValue = ( float* )( value );
- float flValue = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%f", flValue );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_BOOLEAN:
- {
- bool *pValue = ( bool* )( value );
- bool bValue = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", ( int )( bValue ) );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_INTEGER:
- {
- int *pValue = ( int* )( value );
- int nValue = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_STRING:
- {
- string_t *pValue = ( string_t* )( value );
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%s", ( char* )STRING( *pValue ) );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_VECTOR:
- {
- Vector *pValue = ( Vector* )( value );
- Vector vecValue = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f)", vecValue.x, vecValue.y, vecValue.z );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_QUATERNION:
- {
- Quaternion *pValue = ( Quaternion* )( value );
- Quaternion q = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f %f)", q[0], q[1], q[2], q[3] );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- break;
- }
- case FIELD_CHARACTER:
- {
- char *pValue = ( char* )( value );
- char chValue = pValue[iCount];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%c", chValue );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- }
- case FIELD_COLOR32:
- {
- byte *pValue = ( byte* )( value );
- byte *pColor = &pValue[iCount*4];
- Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%d %d %d %d)", ( int )pColor[0], ( int )pColor[1], ( int )pColor[2], ( int )pColor[3] );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- }
- case FIELD_EMBEDDED:
- case FIELD_CUSTOM:
- default:
- {
- break;
- }
- }
-
- // Add space data.
- if ( ( iCount + 1 ) != count )
- {
- Q_strncpy( szTempBuf, " ", sizeof( szTempBuf ) );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- }
- else
- {
- Q_strncpy( szTempBuf, "\n", sizeof( szTempBuf ) );
- Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
- }
- }
-
- int nLength = strlen( szBuf ) + 1;
- filesystem->Write( szBuf, nLength, m_hLogFile );
-}
-
-//-------------------------------------
-
-bool CSave::IsAsync()
-{
- return m_bAsync;
-}
-
-//-------------------------------------
-
-int CSave::GetWritePos() const
-{
- return m_pData->GetCurPos();
-}
-
-//-------------------------------------
-
-void CSave::SetWritePos(int pos)
-{
- m_pData->Seek(pos);
-}
-
-//-------------------------------------
-
-void CSave::WriteShort( const short *value, int count )
-{
- BufferData( (const char *)value, sizeof(short) * count );
-}
-
-//-------------------------------------
-
-void CSave::WriteInt( const int *value, int count )
-{
- BufferData( (const char *)value, sizeof(int) * count );
-}
-
-//-------------------------------------
-
-void CSave::WriteBool( const bool *value, int count )
-{
- COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) );
- BufferData( (const char *)value, sizeof(bool) * count );
-}
-
-//-------------------------------------
-
-void CSave::WriteFloat( const float *value, int count )
-{
- BufferData( (const char *)value, sizeof(float) * count );
-}
-
-//-------------------------------------
-
-void CSave::WriteData( const char *pdata , int size )
-{
- BufferData( pdata, size );
-}
-
-//-------------------------------------
-
-void CSave::WriteString( const char *pstring )
-{
- BufferData( pstring, strlen(pstring) + 1 );
-}
-
-//-------------------------------------
-
-void CSave::WriteString( const string_t *stringId, int count )
-{
- for ( int i = 0; i < count; i++ )
- {
- const char *pString = STRING(stringId[i]);
- BufferData( pString, strlen(pString)+1 );
- }
-}
-
-//-------------------------------------
-
-void CSave::WriteVector( const Vector &value )
-{
- BufferData( (const char *)&value, sizeof(Vector) );
-}
-
-//-------------------------------------
-
-void CSave::WriteVector( const Vector *value, int count )
-{
- BufferData( (const char *)value, sizeof(Vector) * count );
-}
-
-void CSave::WriteQuaternion( const Quaternion &value )
-{
- BufferData( (const char *)&value, sizeof(Quaternion) );
-}
-
-//-------------------------------------
-
-void CSave::WriteQuaternion( const Quaternion *value, int count )
-{
- BufferData( (const char *)value, sizeof(Quaternion) * count );
-}
-
-
-//-------------------------------------
-
-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::WriteBool( const char *pname, const bool *data, int count )
-{
- COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) );
- BufferField( pname, sizeof(bool) * 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::WriteString( const char *pname, const char *pdata )
-{
- BufferField( pname, strlen(pdata) + 1, pdata );
-}
-
-//-------------------------------------
-
-void CSave::WriteString( const char *pname, const string_t *stringId, int count )
-{
- int i, size;
-
- size = 0;
- for ( i = 0; i < count; i++ )
- size += strlen( STRING( stringId[i] ) ) + 1;
-
- WriteHeader( pname, size );
- WriteString( stringId, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteVector( const char *pname, const Vector &value )
-{
- WriteVector( pname, &value, 1 );
-}
-
-//-------------------------------------
-
-void CSave::WriteVector( const char *pname, const Vector *value, int count )
-{
- WriteHeader( pname, sizeof(Vector) * count );
- BufferData( (const char *)value, sizeof(Vector) * count );
-}
-
-void CSave::WriteQuaternion( const char *pname, const Quaternion &value )
-{
- WriteQuaternion( pname, &value, 1 );
-}
-
-//-------------------------------------
-
-void CSave::WriteQuaternion( const char *pname, const Quaternion *value, int count )
-{
- WriteHeader( pname, sizeof(Quaternion) * count );
- BufferData( (const char *)value, sizeof(Quaternion) * count );
-}
-
-
-//-------------------------------------
-
-void CSave::WriteVMatrix( const VMatrix *value, int count )
-{
- BufferData( (const char *)value, sizeof(VMatrix) * count );
-}
-
-//-------------------------------------
-
-void CSave::WriteVMatrix( const char *pname, const VMatrix *value, int count )
-{
- WriteHeader( pname, sizeof(VMatrix) * count );
- BufferData( (const char *)value, sizeof(VMatrix) * count );
-}
-
-//-------------------------------------
-
-void CSave::WriteVMatrixWorldspace( const VMatrix *value, int count )
-{
- for ( int i = 0; i < count; i++ )
- {
- VMatrix tmp;
- VMatrixOffset( tmp, value[i], -m_pGameInfo->GetLandmark() );
- BufferData( (const char *)&tmp, sizeof(VMatrix) );
- }
-}
-
-//-------------------------------------
-
-void CSave::WriteVMatrixWorldspace( const char *pname, const VMatrix *value, int count )
-{
- WriteHeader( pname, sizeof(VMatrix) * count );
- WriteVMatrixWorldspace( value, count );
-}
-
-void CSave::WriteMatrix3x4Worldspace( const matrix3x4_t *value, int count )
-{
- Vector offset = -m_pGameInfo->GetLandmark();
- for ( int i = 0; i < count; i++ )
- {
- matrix3x4_t tmp;
- Matrix3x4Offset( tmp, value[i], offset );
- BufferData( (const char *)value, sizeof(matrix3x4_t) );
- }
-}
-
-//-------------------------------------
-
-void CSave::WriteMatrix3x4Worldspace( const char *pname, const matrix3x4_t *value, int count )
-{
- WriteHeader( pname, sizeof(matrix3x4_t) * count );
- WriteMatrix3x4Worldspace( value, count );
-}
-
-void CSave::WriteInterval( const char *pname, const interval_t *value, int count )
-{
- WriteHeader( pname, sizeof( interval_t ) * count );
- WriteInterval( value, count );
-}
-
-void CSave::WriteInterval( const interval_t *value, int count )
-{
- BufferData( (const char *)value, count * sizeof( interval_t ) );
-}
-
-//-------------------------------------
-
-bool CSave::ShouldSaveField( const void *pData, typedescription_t *pField )
-{
- if ( !(pField->flags & FTYPEDESC_SAVE) || pField->fieldType == FIELD_VOID )
- return false;
-
- switch ( pField->fieldType )
- {
- case FIELD_EMBEDDED:
- {
- if ( pField->flags & FTYPEDESC_PTR )
- {
- AssertMsg( pField->fieldSize == 1, "Arrays of embedded pointer types presently unsupported by save/restore" );
- if ( pField->fieldSize != 1 )
- return false;
- }
-
- AssertMsg( pField->td != NULL, "Embedded type appears to have not had type description implemented" );
- if ( pField->td == NULL )
- return false;
-
- if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pData) )
- return false;
-
- // @TODO: need real logic for handling embedded types with base classes
- if ( pField->td->baseMap )
- {
- return true;
- }
-
- int nFieldCount = pField->fieldSize;
- char *pTestData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) );
- while ( --nFieldCount >= 0 )
- {
- typedescription_t *pTestField = pField->td->dataDesc;
- typedescription_t *pLimit = pField->td->dataDesc + pField->td->dataNumFields;
-
- for ( ; pTestField < pLimit; ++pTestField )
- {
- if ( ShouldSaveField( pTestData + pTestField->fieldOffset[ TD_OFFSET_NORMAL ], pTestField ) )
- return true;
- }
-
- pTestData += pField->fieldSizeInBytes;
- }
- return false;
- }
-
- case FIELD_CUSTOM:
- {
- // ask the data if it's empty
- SaveRestoreFieldInfo_t fieldInfo =
- {
- const_cast<void *>(pData),
- ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ],
- pField
- };
- if ( pField->pSaveRestoreOps->IsEmpty( fieldInfo ) )
- return false;
- }
- return true;
-
- case FIELD_EHANDLE:
- {
- if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) )
- {
- Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName );
- Assert( 0 );
- }
-
- int *pEHandle = (int *)pData;
- for ( int i = 0; i < pField->fieldSize; ++i, ++pEHandle )
- {
- if ( (*pEHandle) != 0xFFFFFFFF )
- return true;
- }
- }
- return false;
-
- default:
- {
- if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) )
- {
- Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName );
- Assert( 0 );
- }
-
- // old byte-by-byte null check
- if ( DataEmpty( (const char *)pData, pField->fieldSize * gSizes[pField->fieldType] ) )
- return false;
- }
- return true;
- }
-}
-
-//-------------------------------------
-// Purpose: Writes all the fields that are client neutral. In the event of
-// a librarization of save/restore, these would reside in the library
-//
-
-bool CSave::WriteBasicField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField )
-{
- switch( pField->fieldType )
- {
- case FIELD_FLOAT:
- WriteFloat( pField->fieldName, (float *)pData, pField->fieldSize );
- break;
-
- case FIELD_STRING:
- WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize );
- break;
-
- case FIELD_VECTOR:
- WriteVector( pField->fieldName, (Vector *)pData, pField->fieldSize );
- break;
-
- case FIELD_QUATERNION:
- WriteQuaternion( pField->fieldName, (Quaternion *)pData, pField->fieldSize );
- break;
-
- case FIELD_INTEGER:
- WriteInt( pField->fieldName, (int *)pData, pField->fieldSize );
- break;
-
- case FIELD_BOOLEAN:
- WriteBool( pField->fieldName, (bool *)pData, pField->fieldSize );
- break;
-
- case FIELD_SHORT:
- WriteData( pField->fieldName, 2 * pField->fieldSize, ((char *)pData) );
- break;
-
- case FIELD_CHARACTER:
- WriteData( pField->fieldName, pField->fieldSize, ((char *)pData) );
- break;
-
- case FIELD_COLOR32:
- WriteData( pField->fieldName, 4*pField->fieldSize, (char *)pData );
- break;
-
- case FIELD_EMBEDDED:
- {
- AssertMsg( ( (pField->flags & FTYPEDESC_PTR) == 0 ) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" );
- Assert( !(pField->flags & FTYPEDESC_PTR) || *((void **)pData) );
- int nFieldCount = pField->fieldSize;
- char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) );
-
- StartBlock( pField->fieldName );
-
- while ( --nFieldCount >= 0 )
- {
- WriteAll( pFieldData, pField->td );
- pFieldData += pField->fieldSizeInBytes;
- }
-
- EndBlock();
- break;
- }
-
- case FIELD_CUSTOM:
- {
- // Note it is up to the custom type implementor to handle arrays
- StartBlock( pField->fieldName );
-
- SaveRestoreFieldInfo_t fieldInfo =
- {
- pData,
- ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ],
- pField
- };
- pField->pSaveRestoreOps->Save( fieldInfo, this );
-
- EndBlock();
- break;
- }
-
- default:
- Warning( "Bad field type\n" );
- Assert(0);
- return false;
- }
-
- return true;
-}
-
-//-------------------------------------
-
-bool CSave::WriteField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField )
-{
-#ifdef _DEBUG
- Log( pname, (fieldtype_t)pField->fieldType, pData, pField->fieldSize );
-#endif
-
- if ( pField->fieldType <= FIELD_CUSTOM )
- {
- return WriteBasicField( pname, pData, pRootMap, pField );
- }
- return WriteGameField( pname, pData, pRootMap, pField );
-}
-
-//-------------------------------------
-
-int CSave::WriteFields( const char *pname, const void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount )
-{
- typedescription_t *pTest;
- int iHeaderPos = m_pData->GetCurPos();
- int count = -1;
- WriteInt( pname, &count, 1 );
-
- count = 0;
-
-#ifdef _X360
- __dcbt( 0, pBaseData );
- __dcbt( 128, pBaseData );
- __dcbt( 256, pBaseData );
- __dcbt( 512, pBaseData );
- void *pDest = m_pData->AccessCurPos();
- __dcbt( 0, pDest );
- __dcbt( 128, pDest );
- __dcbt( 256, pDest );
- __dcbt( 512, pDest );
-#endif
-
- for ( int i = 0; i < fieldCount; i++ )
- {
- pTest = &pFields[ i ];
- void *pOutputData = ( (char *)pBaseData + pTest->fieldOffset[ TD_OFFSET_NORMAL ] );
-
- if ( !ShouldSaveField( pOutputData, pTest ) )
- continue;
-
- if ( !WriteField( pname, pOutputData, pRootMap, pTest ) )
- break;
- count++;
- }
-
- int iCurPos = m_pData->GetCurPos();
- int iRewind = iCurPos - iHeaderPos;
- m_pData->Rewind( iRewind );
- WriteInt( pname, &count, 1 );
- iCurPos = m_pData->GetCurPos();
- m_pData->MoveCurPos( iRewind - ( iCurPos - iHeaderPos ) );
-
- return 1;
-}
-
-//-------------------------------------
-// Purpose: Recursively saves all the classes in an object, in reverse order (top down)
-// Output : int 0 on failure, 1 on success
-
-int CSave::DoWriteAll( const void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap )
-{
- // save base classes first
- if ( pCurMap->baseMap )
- {
- int status = DoWriteAll( pLeafObject, pLeafMap, pCurMap->baseMap );
- if ( !status )
- return status;
- }
-
- return WriteFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields );
-}
-
-//-------------------------------------
-
-void CSave::StartBlock( const char *pszBlockName )
-{
- WriteHeader( pszBlockName, 0 ); // placeholder
- m_BlockStartStack.AddToTail( GetWritePos() );
-}
-
-//-------------------------------------
-
-void CSave::StartBlock()
-{
- StartBlock( "" );
-}
-
-//-------------------------------------
-
-void CSave::EndBlock()
-{
- int endPos = GetWritePos();
- int startPos = m_BlockStartStack[ m_BlockStartStack.Count() - 1 ];
- short sizeBlock = endPos - startPos;
-
- m_BlockStartStack.Remove( m_BlockStartStack.Count() - 1 );
-
- // Move to the the location where the size of the block was written & rewrite the size
- SetWritePos( startPos - sizeof(SaveRestoreRecordHeader_t) );
- BufferData( (const char *)&sizeBlock, sizeof(short) );
-
- SetWritePos( endPos );
-}
-
-//-------------------------------------
-
-void CSave::BufferString( char *pdata, int len )
-{
- char c = 0;
-
- BufferData( pdata, len ); // Write the string
- BufferData( &c, 1 ); // Write a null terminator
-}
-
-//-------------------------------------
-
-void CSave::BufferField( const char *pname, int size, const char *pdata )
-{
- WriteHeader( pname, size );
- BufferData( pdata, size );
-}
-
-//-------------------------------------
-
-void CSave::WriteHeader( const char *pname, int size )
-{
- short shortSize = size;
- short hashvalue = m_pData->FindCreateSymbol( pname );
- if ( size > SHRT_MAX || size < 0 )
- {
- Warning( "CSave::WriteHeader() size parameter exceeds 'short'!\n" );
- Assert(0);
- }
-
- BufferData( (const char *)&shortSize, sizeof(short) );
- BufferData( (const char *)&hashvalue, sizeof(short) );
-}
-
-//-------------------------------------
-
-void CSave::BufferData( const char *pdata, int size )
-{
- if ( !m_pData )
- return;
-
- if ( !m_pData->Write( pdata, size ) )
- {
- Warning( "Save/Restore overflow!\n" );
- Assert(0);
- }
-}
-
-//---------------------------------------------------------
-//
-// Game centric save methods.
-//
-int CSave::EntityIndex( const edict_t *pentLookup )
-{
-#if !defined( CLIENT_DLL )
- if ( pentLookup == NULL )
- return -1;
- return EntityIndex( CBaseEntity::Instance(pentLookup) );
-#else
- Assert( !"CSave::EntityIndex( edict_t * ) not valid on client!" );
- return -1;
-#endif
-}
-
-
-//-------------------------------------
-
-int CSave::EntityIndex( const CBaseEntity *pEntity )
-{
- return m_pGameInfo->GetEntityIndex( pEntity );
-}
-
-//-------------------------------------
-
-int CSave::EntityFlagsSet( int entityIndex, int flags )
-{
- if ( !m_pGameInfo || entityIndex < 0 )
- return 0;
- if ( entityIndex > m_pGameInfo->NumEntities() )
- return 0;
-
- m_pGameInfo->GetEntityInfo( entityIndex )->flags |= flags;
-
- return m_pGameInfo->GetEntityInfo( entityIndex )->flags;
-}
-
-//-------------------------------------
-
-void CSave::WriteTime( const char *pname, const float *data, int count )
-{
- int i;
- float tmp;
-
- WriteHeader( pname, sizeof(float) * count );
- for ( i = 0; i < count; i++ )
- {
- // 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
- Assert( data[i] != ZERO_TIME );
-
- if ( data[i] == 0.0 )
- {
- tmp = ZERO_TIME;
- }
- else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX )
- {
- tmp = data[i];
- }
- else
- {
- tmp = data[i] - m_pGameInfo->GetBaseTime();
- if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing
- tmp = 0.001;
- }
-
- WriteData( (const char *)&tmp, sizeof(float) );
- }
-}
-
-//-------------------------------------
-
-void CSave::WriteTime( const float *data, int count )
-{
- int i;
- float tmp;
-
- for ( i = 0; i < count; i++ )
- {
- // 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 ( data[i] == 0.0 )
- {
- tmp = ZERO_TIME;
- }
- else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX )
- {
- tmp = data[i];
- }
- else
- {
- tmp = data[i] - m_pGameInfo->GetBaseTime();
- if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing
- tmp = 0.001;
- }
-
- WriteData( (const char *)&tmp, sizeof(float) );
- }
-}
-
-void CSave::WriteTick( const char *pname, const int *data, int count )
-{
- WriteHeader( pname, sizeof(int) * count );
- WriteTick( data, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteTick( const int *data, int count )
-{
- int i;
- int tmp;
-
- int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() );
-
- for ( i = 0; i < count; i++ )
- {
- // 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
- tmp = data[ i ];
- if ( data[ i ] == TICK_NEVER_THINK )
- {
- tmp = TICK_NEVER_THINK_ENCODE;
- }
- else
- {
- // Rebase it...
- tmp -= baseTick;
- }
- WriteData( (const char *)&tmp, sizeof(int) );
- }
-}
-//-------------------------------------
-
-void CSave::WritePositionVector( const char *pname, const Vector &value )
-{
- Vector tmp = value;
-
- if ( tmp != vec3_invalid )
- tmp -= m_pGameInfo->GetLandmark();
-
- WriteVector( pname, tmp );
-}
-
-//-------------------------------------
-
-void CSave::WritePositionVector( const Vector &value )
-{
- Vector tmp = value;
-
- if ( tmp != vec3_invalid )
- tmp -= m_pGameInfo->GetLandmark();
-
- WriteVector( tmp );
-}
-
-//-------------------------------------
-
-void CSave::WritePositionVector( const char *pname, const Vector *value, int count )
-{
- WriteHeader( pname, sizeof(Vector) * count );
- WritePositionVector( value, count );
-}
-
-//-------------------------------------
-
-void CSave::WritePositionVector( const Vector *value, int count )
-{
- int i;
- Vector tmp;
- for ( i = 0; i < count; i++ )
- {
- Vector tmp = value[i];
-
- if ( tmp != vec3_invalid )
- tmp -= m_pGameInfo->GetLandmark();
-
- WriteData( (const char *)&tmp.x, sizeof(Vector) );
- }
-}
-
-//-------------------------------------
-
-void CSave::WriteFunction( datamap_t *pRootMap, const char *pname, inputfunc_t **data, int count )
-{
- AssertMsg( count == 1, "Arrays of functions not presently supported" );
- const char *functionName = UTIL_FunctionToName( pRootMap, *data );
- if ( !functionName )
- {
- Warning( "Invalid function pointer in entity!\n" );
- Assert(0);
- functionName = "BADFUNCTIONPOINTER";
- }
-
- BufferField( pname, strlen(functionName) + 1, functionName );
-}
-
-//-------------------------------------
-
-void CSave::WriteEntityPtr( const char *pname, CBaseEntity **ppEntity, int count )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
- {
- entityArray[i] = EntityIndex( ppEntity[i] );
- }
- WriteInt( pname, entityArray, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteEntityPtr( CBaseEntity **ppEntity, int count )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
- {
- entityArray[i] = EntityIndex( ppEntity[i] );
- }
- WriteInt( entityArray, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteEdictPtr( const char *pname, edict_t **ppEdict, int count )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
- {
- entityArray[i] = EntityIndex( ppEdict[i] );
- }
- WriteInt( pname, entityArray, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteEdictPtr( edict_t **ppEdict, int count )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
- {
- entityArray[i] = EntityIndex( ppEdict[i] );
- }
- WriteInt( entityArray, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteEHandle( const char *pname, const EHANDLE *pEHandle, int count )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
- {
- entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast<EHANDLE *>(pEHandle)[i]) );
- }
- WriteInt( pname, entityArray, count );
-}
-
-//-------------------------------------
-
-void CSave::WriteEHandle( const EHANDLE *pEHandle, int count )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
- {
- entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast<EHANDLE *>(pEHandle)[i]) );
- }
- WriteInt( entityArray, count );
-}
-
-//-------------------------------------
-// Purpose: Writes all the fields that are not client neutral. In the event of
-// a librarization of save/restore, these would not reside in the library
-
-bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField )
-{
- switch( pField->fieldType )
- {
- case FIELD_CLASSPTR:
- WriteEntityPtr( pField->fieldName, (CBaseEntity **)pData, pField->fieldSize );
- break;
-
- case FIELD_EDICT:
- WriteEdictPtr( pField->fieldName, (edict_t **)pData, pField->fieldSize );
- break;
-
- case FIELD_EHANDLE:
- WriteEHandle( pField->fieldName, (EHANDLE *)pData, pField->fieldSize );
- break;
-
- case FIELD_POSITION_VECTOR:
- WritePositionVector( pField->fieldName, (Vector *)pData, pField->fieldSize );
- break;
-
- case FIELD_TIME:
- WriteTime( pField->fieldName, (float *)pData, pField->fieldSize );
- break;
-
- case FIELD_TICK:
- WriteTick( pField->fieldName, (int *)pData, pField->fieldSize );
- break;
-
- case FIELD_MODELINDEX:
- {
- int nModelIndex = *(int*)pData;
- string_t strModelName = NULL_STRING;
- const model_t *pModel = modelinfo->GetModel( nModelIndex );
- if ( pModel )
- {
- strModelName = AllocPooledString( modelinfo->GetModelName( pModel ) );
- }
- WriteString( pField->fieldName, (string_t *)&strModelName, pField->fieldSize );
- }
- break;
-
- case FIELD_MATERIALINDEX:
- {
- int nMateralIndex = *(int*)pData;
- string_t strMaterialName = NULL_STRING;
- const char *pMaterialName = GetMaterialNameFromIndex( nMateralIndex );
- if ( pMaterialName )
- {
- strMaterialName = MAKE_STRING( pMaterialName );
- }
- WriteString( pField->fieldName, (string_t *)&strMaterialName, pField->fieldSize );
- }
- break;
-
- case FIELD_MODELNAME:
- case FIELD_SOUNDNAME:
- WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize );
- break;
-
- // For now, just write the address out, we're not going to change memory while doing this yet!
- case FIELD_FUNCTION:
- WriteFunction( pRootMap, pField->fieldName, (inputfunc_t **)(char *)pData, pField->fieldSize );
- break;
-
- case FIELD_VMATRIX:
- WriteVMatrix( pField->fieldName, (VMatrix *)pData, pField->fieldSize );
- break;
- case FIELD_VMATRIX_WORLDSPACE:
- WriteVMatrixWorldspace( pField->fieldName, (VMatrix *)pData, pField->fieldSize );
- break;
-
- case FIELD_MATRIX3X4_WORLDSPACE:
- WriteMatrix3x4Worldspace( pField->fieldName, (const matrix3x4_t *)pData, pField->fieldSize );
- break;
-
- case FIELD_INTERVAL:
- WriteInterval( pField->fieldName, (interval_t *)pData, pField->fieldSize );
- break;
-
- default:
- Warning( "Bad field type\n" );
- Assert(0);
- return false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-//
-// CRestore
-//
-//-----------------------------------------------------------------------------
-
-CRestore::CRestore( CSaveRestoreData *pdata )
- : m_pData( pdata ),
- m_pGameInfo( pdata ),
- m_global( 0 ),
- m_precache( true )
-{
- m_BlockEndStack.EnsureCapacity( 32 );
-}
-
-//-------------------------------------
-
-int CRestore::GetReadPos() const
-{
- return m_pData->GetCurPos();
-}
-
-//-------------------------------------
-
-void CRestore::SetReadPos( int pos )
-{
- m_pData->Seek(pos);
-}
-
-//-------------------------------------
-
-const char *CRestore::StringFromHeaderSymbol( int symbol )
-{
- const char *pszResult = m_pData->StringFromSymbol( symbol );
- return ( pszResult ) ? pszResult : "";
-}
-
-//-------------------------------------
-// Purpose: Reads all the fields that are client neutral. In the event of
-// a librarization of save/restore, these would reside in the library
-
-void CRestore::ReadBasicField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField )
-{
- switch( pField->fieldType )
- {
- case FIELD_FLOAT:
- {
- ReadFloat( (float *)pDest, pField->fieldSize, header.size );
- break;
- }
- case FIELD_STRING:
- {
- ReadString( (string_t *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_VECTOR:
- {
- ReadVector( (Vector *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_QUATERNION:
- {
- ReadQuaternion( (Quaternion *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_INTEGER:
- {
- ReadInt( (int *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_BOOLEAN:
- {
- ReadBool( (bool *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_SHORT:
- {
- ReadShort( (short *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_CHARACTER:
- {
- ReadData( (char *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_COLOR32:
- {
- COMPILE_TIME_ASSERT( sizeof(color32) == sizeof(int) );
- ReadInt( (int *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_EMBEDDED:
- {
- AssertMsg( (( pField->flags & FTYPEDESC_PTR ) == 0) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" );
-#ifdef DBGFLAG_ASSERT
- int startPos = GetReadPos();
-#endif
- if ( !(pField->flags & FTYPEDESC_PTR) || *((void **)pDest) )
- {
- int nFieldCount = pField->fieldSize;
- char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pDest : *((void **)pDest) );
- while ( --nFieldCount >= 0 )
- {
- // No corresponding "block" (see write) as it was used as the header of the field
- ReadAll( pFieldData, pField->td );
- pFieldData += pField->fieldSizeInBytes;
- }
- Assert( GetReadPos() - startPos == header.size );
- }
- else
- {
- SetReadPos( GetReadPos() + header.size );
- Warning( "Attempted to restore FIELD_EMBEDDEDBYREF %s but there is no destination memory\n", pField->fieldName );
- }
- break;
-
- }
- case FIELD_CUSTOM:
- {
- // No corresponding "block" (see write) as it was used as the header of the field
- int posNextField = GetReadPos() + header.size;
-
- SaveRestoreFieldInfo_t fieldInfo =
- {
- pDest,
- ((char *)pDest) - pField->fieldOffset[ TD_OFFSET_NORMAL ],
- pField
- };
-
- pField->pSaveRestoreOps->Restore( fieldInfo, this );
-
- Assert( posNextField >= GetReadPos() );
- SetReadPos( posNextField );
- break;
- }
-
- default:
- Warning( "Bad field type\n" );
- Assert(0);
- }
-}
-
-//-------------------------------------
-
-void CRestore::ReadField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField )
-{
- if ( pField->fieldType <= FIELD_CUSTOM )
- ReadBasicField( header, pDest, pRootMap, pField );
- else
- ReadGameField( header, pDest, pRootMap, pField );
-}
-
-//-------------------------------------
-
-bool CRestore::ShouldReadField( typedescription_t *pField )
-{
- if ( (pField->flags & FTYPEDESC_SAVE) == 0 )
- return false;
-
- if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) )
- return false;
-
- return true;
-}
-
-//-------------------------------------
-
-typedescription_t *CRestore::FindField( const char *pszFieldName, typedescription_t *pFields, int fieldCount, int *pCookie )
-{
- int &fieldNumber = *pCookie;
- if ( pszFieldName )
- {
- typedescription_t *pTest;
-
- for ( int i = 0; i < fieldCount; i++ )
- {
- pTest = &pFields[fieldNumber];
-
- ++fieldNumber;
- if ( fieldNumber == fieldCount )
- fieldNumber = 0;
-
- if ( stricmp( pTest->fieldName, pszFieldName ) == 0 )
- return pTest;
- }
- }
-
- fieldNumber = 0;
- return NULL;
-}
-
-//-------------------------------------
-
-bool CRestore::ShouldEmptyField( typedescription_t *pField )
-{
- // don't clear out fields that don't get saved, or that are handled specially
- if ( !( pField->flags & FTYPEDESC_SAVE ) )
- return false;
-
- // Don't clear global fields
- if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) )
- return false;
-
- return true;
-}
-
-//-------------------------------------
-
-void CRestore::EmptyFields( void *pBaseData, typedescription_t *pFields, int fieldCount )
-{
- int i;
- for ( i = 0; i < fieldCount; i++ )
- {
- typedescription_t *pField = &pFields[i];
- if ( !ShouldEmptyField( pField ) )
- continue;
-
- void *pFieldData = (char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ];
- switch( pField->fieldType )
- {
- case FIELD_CUSTOM:
- {
- SaveRestoreFieldInfo_t fieldInfo =
- {
- pFieldData,
- pBaseData,
- pField
- };
- pField->pSaveRestoreOps->MakeEmpty( fieldInfo );
- }
- break;
-
- case FIELD_EMBEDDED:
- {
- if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pFieldData) )
- break;
-
- int nFieldCount = pField->fieldSize;
- char *pFieldMemory = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pFieldData : *((void **)pFieldData) );
- while ( --nFieldCount >= 0 )
- {
- EmptyFields( pFieldMemory, pField->td->dataDesc, pField->td->dataNumFields );
- pFieldMemory += pField->fieldSizeInBytes;
- }
- }
- break;
-
- default:
- // NOTE: If you hit this assertion, you've got a bug where you're using
- // the wrong field type for your field
- if ( pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType] )
- {
- Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName );
- Assert( 0 );
- }
- memset( pFieldData, (pField->fieldType != FIELD_EHANDLE) ? 0 : 0xFF, pField->fieldSize * gSizes[pField->fieldType] );
- break;
- }
- }
-}
-
-//-------------------------------------
-
-void CRestore::StartBlock( SaveRestoreRecordHeader_t *pHeader )
-{
- ReadHeader( pHeader );
- m_BlockEndStack.AddToTail( GetReadPos() + pHeader->size );
-}
-
-//-------------------------------------
-
-void CRestore::StartBlock( char szBlockName[] )
-{
- SaveRestoreRecordHeader_t header;
- StartBlock( &header );
- Q_strncpy( szBlockName, StringFromHeaderSymbol( header.symbol ), SIZE_BLOCK_NAME_BUF );
-}
-
-//-------------------------------------
-
-void CRestore::StartBlock()
-{
- char szBlockName[SIZE_BLOCK_NAME_BUF];
- StartBlock( szBlockName );
-}
-
-//-------------------------------------
-
-void CRestore::EndBlock()
-{
- int endPos = m_BlockEndStack[ m_BlockEndStack.Count() - 1 ];
- m_BlockEndStack.Remove( m_BlockEndStack.Count() - 1 );
- SetReadPos( endPos );
-}
-
-//-------------------------------------
-
-int CRestore::ReadFields( const char *pname, void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount )
-{
- static int lastName = -1;
- Verify( ReadShort() == sizeof(int) ); // First entry should be an int
- int symName = m_pData->FindCreateSymbol(pname);
-
- // Check the struct name
- int curSym = ReadShort();
- if ( curSym != symName ) // Field Set marker
- {
- const char *pLastName = m_pData->StringFromSymbol( lastName );
- const char *pCurName = m_pData->StringFromSymbol( curSym );
- Msg( "Expected %s found %s ( raw '%s' )! (prev: %s)\n", pname, pCurName, BufferPointer(), pLastName );
- Msg( "Field type name may have changed or inheritance graph changed, save file is suspect\n" );
- m_pData->Rewind( 2*sizeof(short) );
- return 0;
- }
- lastName = symName;
-
- // Clear out base data
- EmptyFields( pBaseData, pFields, fieldCount );
-
- // Skip over the struct name
- int i;
- int nFieldsSaved = ReadInt(); // Read field count
- int searchCookie = 0; // Make searches faster, most data is read/written in the same order
- SaveRestoreRecordHeader_t header;
-
- for ( i = 0; i < nFieldsSaved; i++ )
- {
- ReadHeader( &header );
-
- typedescription_t *pField = FindField( m_pData->StringFromSymbol( header.symbol ), pFields, fieldCount, &searchCookie);
- if ( pField && ShouldReadField( pField ) )
- {
- ReadField( header, ((char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ]), pRootMap, pField );
- }
- else
- {
- BufferSkipBytes( header.size ); // Advance to next field
- }
- }
-
- return 1;
-}
-
-//-------------------------------------
-
-void CRestore::ReadHeader( SaveRestoreRecordHeader_t *pheader )
-{
- if ( pheader != NULL )
- {
- Assert( pheader!=NULL );
- pheader->size = ReadShort(); // Read field size
- pheader->symbol = ReadShort(); // Read field name token
- }
- else
- {
- BufferSkipBytes( sizeof(short) * 2 );
- }
-}
-
-//-------------------------------------
-
-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;
-}
-
-//-------------------------------------
-// Purpose: Recursively restores all the classes in an object, in reverse order (top down)
-// Output : int 0 on failure, 1 on success
-
-int CRestore::DoReadAll( void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap )
-{
- // restore base classes first
- if ( pCurMap->baseMap )
- {
- int status = DoReadAll( pLeafObject, pLeafMap, pCurMap->baseMap );
- if ( !status )
- return status;
- }
-
- return ReadFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields );
-}
-
-//-------------------------------------
-
-char *CRestore::BufferPointer( void )
-{
- if ( !m_pData )
- return NULL;
-
- return m_pData->AccessCurPos();
-}
-
-//-------------------------------------
-
-void CRestore::BufferReadBytes( char *pOutput, int size )
-{
- Assert( m_pData !=NULL );
-
- if ( !m_pData || m_pData->BytesAvailable() == 0 )
- return;
-
- if ( !m_pData->Read( pOutput, size ) )
- {
- Warning( "Restore underflow!\n" );
- Assert(0);
- }
-}
-
-//-------------------------------------
-
-void CRestore::BufferSkipBytes( int bytes )
-{
- BufferReadBytes( NULL, bytes );
-}
-
-//-------------------------------------
-
-int CRestore::ReadShort( short *pValue, int nElems, int nBytesAvailable )
-{
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-//-------------------------------------
-
-int CRestore::ReadInt( int *pValue, int nElems, int nBytesAvailable )
-{
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-//-------------------------------------
-
-int CRestore::ReadBool( bool *pValue, int nElems, int nBytesAvailable )
-{
- COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) );
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-//-------------------------------------
-
-int CRestore::ReadFloat( float *pValue, int nElems, int nBytesAvailable )
-{
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-//-------------------------------------
-
-int CRestore::ReadData( char *pData, int size, int nBytesAvailable )
-{
- return ReadSimple( pData, size, nBytesAvailable );
-}
-
-//-------------------------------------
-
-void CRestore::ReadString( char *pDest, int nSizeDest, int nBytesAvailable )
-{
- const char *pString = BufferPointer();
- if ( !nBytesAvailable )
- nBytesAvailable = strlen( pString ) + 1;
- BufferSkipBytes( nBytesAvailable );
-
- Q_strncpy(pDest, pString, nSizeDest );
-}
-
-//-------------------------------------
-
-int CRestore::ReadString( string_t *pValue, int nElems, int nBytesAvailable )
-{
- AssertMsg( nBytesAvailable > 0, "CRestore::ReadString() implementation does not currently support unspecified bytes available");
-
- int i;
- char *pString = BufferPointer();
- char *pLimit = pString + nBytesAvailable;
- for ( i = 0; i < nElems && pString < pLimit; i++ )
- {
- if ( *((char *)pString) == 0 )
- pValue[i] = NULL_STRING;
- else
- pValue[i] = AllocPooledString( (char *)pString );
-
- while (*pString)
- pString++;
- pString++;
- }
-
- BufferSkipBytes( nBytesAvailable );
-
- return i;
-}
-
-//-------------------------------------
-
-int CRestore::ReadVector( Vector *pValue)
-{
- BufferReadBytes( (char *)pValue, sizeof(Vector) );
- return 1;
-}
-
-//-------------------------------------
-
-int CRestore::ReadVector( Vector *pValue, int nElems, int nBytesAvailable )
-{
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-int CRestore::ReadQuaternion( Quaternion *pValue)
-{
- BufferReadBytes( (char *)pValue, sizeof(Quaternion) );
- return 1;
-}
-
-//-------------------------------------
-
-int CRestore::ReadQuaternion( Quaternion *pValue, int nElems, int nBytesAvailable )
-{
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-//-------------------------------------
-int CRestore::ReadVMatrix( VMatrix *pValue, int nElems, int nBytesAvailable )
-{
- return ReadSimple( pValue, nElems, nBytesAvailable );
-}
-
-
-int CRestore::ReadVMatrixWorldspace( VMatrix *pValue, int nElems, int nBytesAvailable )
-{
- Vector basePosition = m_pGameInfo->GetLandmark();
- VMatrix tmp;
-
- for ( int i = 0; i < nElems; i++ )
- {
- BufferReadBytes( (char *)&tmp, sizeof(float)*16 );
-
- VMatrixOffset( pValue[i], tmp, basePosition );
- }
- return nElems;
-}
-
-
-int CRestore::ReadMatrix3x4Worldspace( matrix3x4_t *pValue, int nElems, int nBytesAvailable )
-{
- Vector basePosition = m_pGameInfo->GetLandmark();
- matrix3x4_t tmp;
-
- for ( int i = 0; i < nElems; i++ )
- {
- BufferReadBytes( (char *)&tmp, sizeof(matrix3x4_t) );
-
- Matrix3x4Offset( pValue[i], tmp, basePosition );
- }
- return nElems;
-}
-
-int CRestore::ReadInterval( interval_t *interval, int count, int nBytesAvailable )
-{
- return ReadSimple( interval, count, nBytesAvailable );
-}
-
-//---------------------------------------------------------
-//
-// Game centric restore methods
-//
-
-CBaseEntity *CRestore::EntityFromIndex( int entityIndex )
-{
- if ( !m_pGameInfo || entityIndex < 0 )
- return NULL;
-
- int i;
- entitytable_t *pTable;
-
- for ( i = 0; i < m_pGameInfo->NumEntities(); i++ )
- {
- pTable = m_pGameInfo->GetEntityInfo( i );
- if ( pTable->id == entityIndex )
- return pTable->hEnt;
- }
- return NULL;
-}
-
-//-------------------------------------
-
-int CRestore::ReadEntityPtr( CBaseEntity **ppEntity, int count, int nBytesAvailable )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
-
- int nRead = ReadInt( entityArray, count, nBytesAvailable );
-
- for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count
- {
- ppEntity[i] = EntityFromIndex( entityArray[i] );
- }
-
- if ( nRead < count)
- {
- memset( &ppEntity[nRead], 0, ( count - nRead ) * sizeof(ppEntity[0]) );
- }
-
- return nRead;
-}
-
-//-------------------------------------
-int CRestore::ReadEdictPtr( edict_t **ppEdict, int count, int nBytesAvailable )
-{
-#if !defined( CLIENT_DLL )
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
- CBaseEntity *pEntity;
-
- int nRead = ReadInt( entityArray, count, nBytesAvailable );
-
- for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count
- {
- pEntity = EntityFromIndex( entityArray[i] );
- ppEdict[i] = (pEntity) ? pEntity->edict() : NULL;
- }
-
- if ( nRead < count)
- {
- memset( &ppEdict[nRead], 0, ( count - nRead ) * sizeof(ppEdict[0]) );
- }
-
- return nRead;
-#else
- return 0;
-#endif
-}
-
-
-//-------------------------------------
-
-int CRestore::ReadEHandle( EHANDLE *pEHandle, int count, int nBytesAvailable )
-{
- AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
- int entityArray[MAX_ENTITYARRAY];
-
- int nRead = ReadInt( entityArray, count, nBytesAvailable );
-
- for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count
- {
- pEHandle[i] = EntityFromIndex( entityArray[i] );
- }
-
- if ( nRead < count)
- {
- memset( &pEHandle[nRead], 0xFF, ( count - nRead ) * sizeof(pEHandle[0]) );
- }
-
- return nRead;
-}
-
-//-------------------------------------
-// Purpose: Reads all the fields that are not client neutral. In the event of
-// a librarization of save/restore, these would NOT reside in the library
-
-void CRestore::ReadGameField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField )
-{
- switch( pField->fieldType )
- {
- case FIELD_POSITION_VECTOR:
- {
- ReadPositionVector( (Vector *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_TIME:
- {
- ReadTime( (float *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_TICK:
- {
- ReadTick( (int *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_FUNCTION:
- {
- ReadFunction( pRootMap, (inputfunc_t **)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_MODELINDEX:
- {
- int *pModelIndex = (int*)pDest;
- string_t *pModelName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) );
- int nRead = ReadString( pModelName, pField->fieldSize, header.size );
-
- for ( int i = 0; i < nRead; i++ )
- {
- if ( pModelName[i] == NULL_STRING )
- {
- pModelIndex[i] = -1;
- continue;
- }
-
- pModelIndex[i] = modelinfo->GetModelIndex( STRING( pModelName[i] ) );
-
-#if !defined( CLIENT_DLL )
- if ( m_precache )
- {
- CBaseEntity::PrecacheModel( STRING( pModelName[i] ) );
- }
-#endif
- }
- break;
- }
-
- case FIELD_MATERIALINDEX:
- {
- int *pMaterialIndex = (int*)pDest;
- string_t *pMaterialName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) );
- int nRead = ReadString( pMaterialName, pField->fieldSize, header.size );
-
- for ( int i = 0; i < nRead; i++ )
- {
- if ( pMaterialName[i] == NULL_STRING )
- {
- pMaterialIndex[i] = 0;
- continue;
- }
-
- pMaterialIndex[i] = GetMaterialIndex( STRING( pMaterialName[i] ) );
-
-#if !defined( CLIENT_DLL )
- if ( m_precache )
- {
- PrecacheMaterial( STRING( pMaterialName[i] ) );
- }
-#endif
- }
- break;
- }
-
- case FIELD_MODELNAME:
- case FIELD_SOUNDNAME:
- {
- string_t *pStringDest = (string_t *)pDest;
- int nRead = ReadString( pStringDest, pField->fieldSize, header.size );
- if ( m_precache )
- {
-#if !defined( CLIENT_DLL )
- // HACKHACK: Rewrite the .bsp models to match the map name in case the bugreporter renamed it
- if ( pField->fieldType == FIELD_MODELNAME && Q_stristr(pStringDest->ToCStr(), ".bsp") )
- {
- char buf[MAX_PATH];
- Q_strncpy( buf, "maps/", sizeof(buf) );
- Q_strncat( buf, gpGlobals->mapname.ToCStr(), sizeof(buf) );
- Q_strncat( buf, ".bsp", sizeof(buf) );
- *pStringDest = AllocPooledString( buf );
- }
-#endif
- for ( int i = 0; i < nRead; i++ )
- {
- if ( pStringDest[i] != NULL_STRING )
- {
-#if !defined( CLIENT_DLL )
- if ( pField->fieldType == FIELD_MODELNAME )
- {
- CBaseEntity::PrecacheModel( STRING( pStringDest[i] ) );
- }
- else if ( pField->fieldType == FIELD_SOUNDNAME )
- {
- CBaseEntity::PrecacheScriptSound( STRING( pStringDest[i] ) );
- }
-#endif
- }
- }
- }
- break;
- }
-
- case FIELD_CLASSPTR:
- ReadEntityPtr( (CBaseEntity **)pDest, pField->fieldSize, header.size );
- break;
-
- case FIELD_EDICT:
-#if !defined( CLIENT_DLL )
- ReadEdictPtr( (edict_t **)pDest, pField->fieldSize, header.size );
-#else
- Assert( !"FIELD_EDICT not valid for client .dll" );
-#endif
- break;
- case FIELD_EHANDLE:
- ReadEHandle( (EHANDLE *)pDest, pField->fieldSize, header.size );
- break;
-
- case FIELD_VMATRIX:
- {
- ReadVMatrix( (VMatrix *)pDest, pField->fieldSize, header.size );
- break;
- }
-
- case FIELD_VMATRIX_WORLDSPACE:
- ReadVMatrixWorldspace( (VMatrix *)pDest, pField->fieldSize, header.size );
- break;
-
- case FIELD_MATRIX3X4_WORLDSPACE:
- ReadMatrix3x4Worldspace( (matrix3x4_t *)pDest, pField->fieldSize, header.size );
- break;
-
- case FIELD_INTERVAL:
- ReadInterval( (interval_t *)pDest, pField->fieldSize, header.size );
- break;
-
- default:
- Warning( "Bad field type\n" );
- Assert(0);
- }
-}
-
-//-------------------------------------
-
-int CRestore::ReadTime( float *pValue, int count, int nBytesAvailable )
-{
- float baseTime = m_pGameInfo->GetBaseTime();
- int nRead = ReadFloat( pValue, count, nBytesAvailable );
-
- for ( int i = nRead - 1; i >= 0; i-- )
- {
- if ( pValue[i] == ZERO_TIME )
- pValue[i] = 0.0;
- else if ( pValue[i] != INVALID_TIME && pValue[i] != FLT_MAX )
- pValue[i] += baseTime;
- }
-
- return nRead;
-}
-
-int CRestore::ReadTick( int *pValue, int count, int nBytesAvailable )
-{
- // HACK HACK: Adding 0.1f here makes sure that all tick times read
- // from .sav file which are near the basetime will end up just ahead of
- // the base time, because we are restoring we'll have a slow frame of the
- // max frametime of 0.1 seconds and that could otherwise cause all of our
- // think times to get synchronized to each other... sigh. ywb...
- int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() + 0.1f );
- int nRead = ReadInt( pValue, count, nBytesAvailable );
-
- for ( int i = nRead - 1; i >= 0; i-- )
- {
- if ( pValue[ i ] != TICK_NEVER_THINK_ENCODE )
- {
- // Rebase it
- pValue[i] += baseTick;
- }
- else
- {
- // Slam to -1 value
- pValue[ i ] = TICK_NEVER_THINK;
- }
- }
-
- return nRead;
-}
-
-//-------------------------------------
-
-int CRestore::ReadPositionVector( Vector *pValue )
-{
- return ReadPositionVector( pValue, 1, sizeof(Vector) );
-}
-
-//-------------------------------------
-
-int CRestore::ReadPositionVector( Vector *pValue, int count, int nBytesAvailable )
-{
- Vector basePosition = m_pGameInfo->GetLandmark();
- int nRead = ReadVector( pValue, count, nBytesAvailable );
-
- for ( int i = nRead - 1; i >= 0; i-- )
- {
- if ( pValue[i] != vec3_invalid )
- pValue[i] += basePosition;
- }
-
- return nRead;
-}
-
-//-------------------------------------
-
-int CRestore::ReadFunction( datamap_t *pMap, inputfunc_t **pValue, int count, int nBytesAvailable )
-{
- AssertMsg( nBytesAvailable > 0, "CRestore::ReadFunction() implementation does not currently support unspecified bytes available");
-
- char *pszFunctionName = BufferPointer();
- BufferSkipBytes( nBytesAvailable );
-
- AssertMsg( count == 1, "Arrays of functions not presently supported" );
-
- if ( *pszFunctionName == 0 )
- *pValue = NULL;
- else
- *pValue = UTIL_FunctionFromName( pMap, pszFunctionName );
-
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-//
-// Entity data saving routines
-//
-//-----------------------------------------------------------------------------
-
-BEGIN_SIMPLE_DATADESC(entitytable_t)
- DEFINE_FIELD( id, FIELD_INTEGER ),
- DEFINE_FIELD( edictindex, FIELD_INTEGER ),
- DEFINE_FIELD( saveentityindex, FIELD_INTEGER ),
-// DEFINE_FIELD( restoreentityindex, FIELD_INTEGER ),
- // hEnt (not saved, this is the fixup)
- DEFINE_FIELD( location, FIELD_INTEGER ),
- DEFINE_FIELD( size, FIELD_INTEGER ),
- DEFINE_FIELD( flags, FIELD_INTEGER ),
- DEFINE_FIELD( classname, FIELD_STRING ),
- DEFINE_FIELD( globalname, FIELD_STRING ),
- DEFINE_FIELD( landmarkModelSpace, FIELD_VECTOR ),
- DEFINE_FIELD( modelname, FIELD_STRING ),
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Utilities entities can use when saving
-//-----------------------------------------------------------------------------
-class CEntitySaveUtils : public IEntitySaveUtils
-{
-public:
- // Call these in pre-save + post save
- void PreSave();
- void PostSave();
-
- // Methods of IEntitySaveUtils
- virtual void AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 );
- virtual int GetEntityDependencyCount( CBaseEntity *pEntity );
- virtual int GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList );
-
-private:
- IPhysicsObjectPairHash *m_pLevelAdjacencyDependencyHash;
-};
-
-
-//-----------------------------------------------------------------------------
-// Call these in pre-save + post save
-//-----------------------------------------------------------------------------
-void CEntitySaveUtils::PreSave()
-{
- Assert( !m_pLevelAdjacencyDependencyHash );
- MEM_ALLOC_CREDIT();
- m_pLevelAdjacencyDependencyHash = physics->CreateObjectPairHash();
-}
-
-void CEntitySaveUtils::PostSave()
-{
- physics->DestroyObjectPairHash( m_pLevelAdjacencyDependencyHash );
- m_pLevelAdjacencyDependencyHash = NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Gets the # of dependencies for a particular entity
-//-----------------------------------------------------------------------------
-int CEntitySaveUtils::GetEntityDependencyCount( CBaseEntity *pEntity )
-{
- return m_pLevelAdjacencyDependencyHash->GetPairCountForObject( pEntity );
-}
-
-
-//-----------------------------------------------------------------------------
-// Gets all dependencies for a particular entity
-//-----------------------------------------------------------------------------
-int CEntitySaveUtils::GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList )
-{
- return m_pLevelAdjacencyDependencyHash->GetPairListForObject( pEntity, nCount, (void**)ppEntList );
-}
-
-
-//-----------------------------------------------------------------------------
-// Methods of IEntitySaveUtils
-//-----------------------------------------------------------------------------
-void CEntitySaveUtils::AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 )
-{
- if ( pEntity1 != pEntity2 )
- {
- m_pLevelAdjacencyDependencyHash->AddObjectPair( pEntity1, pEntity2 );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Block handler for save/restore of entities
-//-----------------------------------------------------------------------------
-class CEntitySaveRestoreBlockHandler : public ISaveRestoreBlockHandler
-{
-public:
- const char *GetBlockName();
- void PreSave( CSaveRestoreData *pSaveData );
- void Save( ISave *pSave );
- void WriteSaveHeaders( ISave *pSave );
- virtual void PostSave();
- virtual void PreRestore();
- void ReadRestoreHeaders( IRestore *pRestore );
-
- void Restore( IRestore *pRestore, bool createPlayers );
- virtual void PostRestore();
-
- inline IEntitySaveUtils * GetEntitySaveUtils() { return &m_EntitySaveUtils; }
-
-private:
- friend int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask );
- bool SaveInitEntities( CSaveRestoreData *pSaveData );
- bool DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore );
- Vector ModelSpaceLandmark( int modelIndex );
- int RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo );
-
-#if !defined( CLIENT_DLL )
- // Find the matching global entity. Spit out an error if the designer made entities of
- // different classes with the same global name
- CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname );
-
- int RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo );
-#endif
-
-private:
- CEntitySaveUtils m_EntitySaveUtils;
-};
-
-
-//-----------------------------------------------------------------------------
-
-CEntitySaveRestoreBlockHandler g_EntitySaveRestoreBlockHandler;
-
-//-------------------------------------
-
-ISaveRestoreBlockHandler *GetEntitySaveRestoreBlockHandler()
-{
- return &g_EntitySaveRestoreBlockHandler;
-}
-
-IEntitySaveUtils *GetEntitySaveUtils()
-{
- return g_EntitySaveRestoreBlockHandler.GetEntitySaveUtils();
-}
-
-
-//-----------------------------------------------------------------------------
-// Implementation of the block handler for save/restore of entities
-//-----------------------------------------------------------------------------
-const char *CEntitySaveRestoreBlockHandler::GetBlockName()
-{
- return "Entities";
-}
-
-//---------------------------------
-
-void CEntitySaveRestoreBlockHandler::PreSave( CSaveRestoreData *pSaveData )
-{
- MDLCACHE_CRITICAL_SECTION();
- IGameSystem::OnSaveAllSystems();
-
- m_EntitySaveUtils.PreSave();
-
- // Allow the entities to do some work
- CBaseEntity *pEnt = NULL;
-#if !defined( CLIENT_DLL )
- while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL )
- {
- pEnt->OnSave( &m_EntitySaveUtils );
- }
-#else
- // Do this because it'll force entities to figure out their origins, and that requires
- // SetupBones in the case of aiments.
- {
- C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
-
- int last = ClientEntityList().GetHighestEntityIndex();
- ClientEntityHandle_t iter = ClientEntityList().FirstHandle();
-
- for ( int e = 0; e <= last; e++ )
- {
- pEnt = ClientEntityList().GetBaseEntity( e );
-
- if( !pEnt )
- continue;
-
- pEnt->OnSave();
- }
-
- while ( iter != ClientEntityList().InvalidHandle() )
- {
- pEnt = ClientEntityList().GetBaseEntityFromHandle( iter );
-
- if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE )
- {
- pEnt->OnSave();
- }
-
- iter = ClientEntityList().NextHandle( iter );
- }
- }
-#endif
- SaveInitEntities( pSaveData );
-}
-
-//---------------------------------
-
-void CEntitySaveRestoreBlockHandler::Save( ISave *pSave )
-{
- CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo();
-
- // write entity list that was previously built by SaveInitEntities()
- for ( int i = 0; i < pSaveData->NumEntities(); i++ )
- {
- entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i );
- pEntInfo->location = pSave->GetWritePos();
- pEntInfo->size = 0;
-
- CBaseEntity *pEnt = pEntInfo->hEnt;
- if ( pEnt && !( pEnt->ObjectCaps() & FCAP_DONT_SAVE ) )
- {
- MDLCACHE_CRITICAL_SECTION();
-#if !defined( CLIENT_DLL )
- AssertMsg( !pEnt->edict() || ( pEnt->m_iClassname != NULL_STRING &&
- (STRING(pEnt->m_iClassname)[0] != 0) &&
- FStrEq( STRING(pEnt->m_iClassname), pEnt->GetClassname()) ),
- "Saving entity with invalid classname" );
-#endif
-
- pSaveData->SetCurrentEntityContext( pEnt );
- pEnt->Save( *pSave );
- pSaveData->SetCurrentEntityContext( NULL );
-
- pEntInfo->size = pSave->GetWritePos() - pEntInfo->location; // Size of entity block is data size written to block
-
- pEntInfo->classname = pEnt->m_iClassname; // Remember entity class for respawn
-
-#if !defined( CLIENT_DLL )
- pEntInfo->globalname = pEnt->m_iGlobalname; // remember global name
- pEntInfo->landmarkModelSpace = ModelSpaceLandmark( pEnt->GetModelIndex() );
- int nEntIndex = pEnt->edict() ? ENTINDEX(pEnt->edict()) : -1;
- bool bIsPlayer = ( ( nEntIndex >= 1 ) && ( nEntIndex <= gpGlobals->maxClients ) ) ? true : false;
- if ( bIsPlayer )
- {
- pEntInfo->flags |= FENTTABLE_PLAYER;
- }
-#endif
- }
- }
-}
-
-//---------------------------------
-
-void CEntitySaveRestoreBlockHandler::WriteSaveHeaders( ISave *pSave )
-{
- CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo();
-
- int nEntities = pSaveData->NumEntities();
- pSave->WriteInt( &nEntities );
-
- for ( int i = 0; i < pSaveData->NumEntities(); i++ )
- pSave->WriteFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields );
-}
-
-//---------------------------------
-
-void CEntitySaveRestoreBlockHandler::PostSave()
-{
- m_EntitySaveUtils.PostSave();
-}
-
-//---------------------------------
-
-void CEntitySaveRestoreBlockHandler::PreRestore()
-{
-}
-
-//---------------------------------
-
-void CEntitySaveRestoreBlockHandler::ReadRestoreHeaders( IRestore *pRestore )
-{
- CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
-
- int nEntities;
- pRestore->ReadInt( &nEntities );
-
- entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * nEntities), sizeof(char) );
- if ( !pEntityTable )
- {
- return;
- }
-
- pSaveData->InitEntityTable( pEntityTable, nEntities );
-
- for ( int i = 0; i < pSaveData->NumEntities(); i++ )
- pRestore->ReadFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields );
-
-}
-
-//---------------------------------
-
-#if !defined( CLIENT_DLL )
-
-void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers )
-{
- entitytable_t *pEntInfo;
- CBaseEntity *pent;
-
- CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
-
- bool restoredWorld = false;
-
- // Create entity list
- int i;
- for ( i = 0; i < pSaveData->NumEntities(); i++ )
- {
- pEntInfo = pSaveData->GetEntityInfo( i );
-
- if ( pEntInfo->classname != NULL_STRING && pEntInfo->size && !(pEntInfo->flags & FENTTABLE_REMOVED) )
- {
- if ( pEntInfo->edictindex == 0 ) // worldspawn
- {
- Assert( i == 0 );
- pent = CreateEntityByName( STRING(pEntInfo->classname) );
- pRestore->SetReadPos( pEntInfo->location );
- if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 )
- {
- pEntInfo->hEnt = NULL;
- pEntInfo->restoreentityindex = -1;
- UTIL_RemoveImmediate( pent );
- }
- else
- {
- // force the entity to be relinked
- AddRestoredEntity( pent );
- }
- }
- else if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) )
- {
- if ( !(pEntInfo->flags & FENTTABLE_PLAYER) )
- {
- Warning( "ENTITY IS NOT A PLAYER: %d\n" , i );
- Assert(0);
- }
-
- edict_t *ed = INDEXENT( pEntInfo->edictindex );
-
- if ( ed && createPlayers )
- {
- // create the player
- pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed );
- }
- else
- pent = NULL;
- }
- else
- {
- pent = CreateEntityByName( STRING(pEntInfo->classname) );
- }
- pEntInfo->hEnt = pent;
- pEntInfo->restoreentityindex = pent ? pent->entindex() : - 1;
- if ( pent && pEntInfo->restoreentityindex == 0 )
- {
- if ( !FClassnameIs( pent, "worldspawn" ) )
- {
- pEntInfo->restoreentityindex = -1;
- }
- }
-
- if ( pEntInfo->restoreentityindex == 0 )
- {
- Assert( !restoredWorld );
- restoredWorld = true;
- }
- }
- else
- {
- pEntInfo->hEnt = NULL;
- pEntInfo->restoreentityindex = -1;
- }
- }
-
- // Now spawn entities
- for ( i = 0; i < pSaveData->NumEntities(); i++ )
- {
- pEntInfo = pSaveData->GetEntityInfo( i );
- if ( pEntInfo->edictindex != 0 )
- {
- pent = pEntInfo->hEnt;
- pRestore->SetReadPos( pEntInfo->location );
- if ( pent )
- {
- if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 )
- {
- pEntInfo->hEnt = NULL;
- pEntInfo->restoreentityindex = -1;
- UTIL_RemoveImmediate( pent );
- }
- else
- {
- AddRestoredEntity( pent );
- }
- }
- }
- }
-}
-
-#else // CLIENT DLL VERSION
-
-void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers )
-{
- entitytable_t *pEntInfo;
- CBaseEntity *pent;
-
- CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
-
- // Create entity list
- int i;
- bool restoredWorld = false;
-
- for ( i = 0; i < pSaveData->NumEntities(); i++ )
- {
- pEntInfo = pSaveData->GetEntityInfo( i );
- pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex );
- pEntInfo->hEnt = pent;
- }
-
- // Blast saved data into entities
- for ( i = 0; i < pSaveData->NumEntities(); i++ )
- {
- pEntInfo = pSaveData->GetEntityInfo( i );
-
- bool bRestoredCorrectly = false;
- // FIXME, need to translate save spot to real index here using lookup table transmitted from server
- //Assert( !"Need translation still" );
- if ( pEntInfo->restoreentityindex >= 0 )
- {
- if ( pEntInfo->restoreentityindex == 0 )
- {
- Assert( !restoredWorld );
- restoredWorld = true;
- }
-
- pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex );
- pRestore->SetReadPos( pEntInfo->location );
- if ( pent )
- {
- if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 )
- {
- // Call the OnRestore method
- AddRestoredEntity( pent );
- bRestoredCorrectly = true;
- }
- }
- }
- // BUGBUG: JAY: Disable ragdolls across transitions until PVS/solid check & client entity patch file are implemented
- else if ( !pSaveData->levelInfo.fUseLandmark )
- {
- if ( pEntInfo->classname != NULL_STRING )
- {
- pent = CreateEntityByName( STRING(pEntInfo->classname) );
- pent->InitializeAsClientEntity( NULL, RENDER_GROUP_OPAQUE_ENTITY );
-
- pRestore->SetReadPos( pEntInfo->location );
-
- if ( pent )
- {
- if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 )
- {
- pEntInfo->hEnt = pent;
- AddRestoredEntity( pent );
- bRestoredCorrectly = true;
- }
- }
- }
- }
-
- if ( !bRestoredCorrectly )
- {
- pEntInfo->hEnt = NULL;
- pEntInfo->restoreentityindex = -1;
- }
- }
-
- // Note, server does this after local player connects fully
- IGameSystem::OnRestoreAllSystems();
-
- // Tell hud elements to modify behavior based on game restoration, if applicable
- gHUD.OnRestore();
-}
-#endif
-
-void CEntitySaveRestoreBlockHandler::PostRestore()
-{
-}
-
-void SaveEntityOnTable( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, int &iSlot )
-{
- entitytable_t *pEntInfo = pSaveData->GetEntityInfo( iSlot );
- pEntInfo->id = iSlot;
-#if !defined( CLIENT_DLL )
- pEntInfo->edictindex = pEntity->RequiredEdictIndex();
-#else
- pEntInfo->edictindex = -1;
-#endif
- pEntInfo->modelname = pEntity->GetModelName();
- pEntInfo->restoreentityindex = -1;
- pEntInfo->saveentityindex = pEntity ? pEntity->entindex() : -1;
- pEntInfo->hEnt = pEntity;
- pEntInfo->flags = 0;
- pEntInfo->location = 0;
- pEntInfo->size = 0;
- pEntInfo->classname = NULL_STRING;
-
- iSlot++;
-}
-
-
-//---------------------------------
-
-bool CEntitySaveRestoreBlockHandler::SaveInitEntities( CSaveRestoreData *pSaveData )
-{
- int number_of_entities;
-
-#if !defined( CLIENT_DLL )
- number_of_entities = gEntList.NumberOfEntities();
-#else
- number_of_entities = ClientEntityList().NumberOfEntities( true );
-#endif
- entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * number_of_entities), sizeof(char) );
- if ( !pEntityTable )
- return false;
-
- pSaveData->InitEntityTable( pEntityTable, number_of_entities );
-
- // build the table of entities
- // this is used to turn pointers into savable indices
- // build up ID numbers for each entity, for use in pointer conversions
- // if an entity requires a certain edict number upon restore, save that as well
- CBaseEntity *pEnt = NULL;
- int i = 0;
-
-#if !defined( CLIENT_DLL )
- while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL )
- {
-#else
- int last = ClientEntityList().GetHighestEntityIndex();
-
- for ( int e = 0; e <= last; e++ )
- {
- pEnt = ClientEntityList().GetBaseEntity( e );
- if( !pEnt )
- continue;
-#endif
- SaveEntityOnTable( pEnt, pSaveData, i );
- }
-
-#if defined( CLIENT_DLL )
- ClientEntityHandle_t iter = ClientEntityList().FirstHandle();
-
- while ( iter != ClientEntityList().InvalidHandle() )
- {
- pEnt = ClientEntityList().GetBaseEntityFromHandle( iter );
-
- if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE )
- {
- SaveEntityOnTable( pEnt, pSaveData, i );
- }
-
- iter = ClientEntityList().NextHandle( iter );
- }
-#endif
-
- pSaveData->BuildEntityHash();
-
- Assert( i == pSaveData->NumEntities() );
- return ( i == pSaveData->NumEntities() );
-}
-
-//---------------------------------
-
-#if !defined( CLIENT_DLL )
-
-// Find the matching global entity. Spit out an error if the designer made entities of
-// different classes with the same global name
-CBaseEntity *CEntitySaveRestoreBlockHandler::FindGlobalEntity( string_t classname, string_t globalname )
-{
- CBaseEntity *pReturn = NULL;
-
- while ( (pReturn = gEntList.NextEnt( pReturn )) != NULL )
- {
- if ( FStrEq( STRING(pReturn->m_iGlobalname), STRING(globalname)) )
- break;
- }
-
- if ( pReturn )
- {
- if ( !FClassnameIs( pReturn, STRING(classname) ) )
- {
- Warning( "Global entity found %s, wrong class %s [expects class %s]\n", STRING(globalname), STRING(pReturn->m_iClassname), STRING(classname) );
- pReturn = NULL;
- }
- }
-
- return pReturn;
-}
-
-#endif // !defined( CLIENT_DLL )
-
-//---------------------------------
-
-bool CEntitySaveRestoreBlockHandler::DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore )
-{
- MDLCACHE_CRITICAL_SECTION();
-
- EHANDLE hEntity;
-
- hEntity = pEntity;
-
- pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( pEntity );
- pEntity->Restore( *pRestore );
- pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( NULL );
-
-#if !defined( CLIENT_DLL )
- if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN )
- {
- pEntity->Spawn();
- }
- else
- {
- pEntity->Precache( );
- }
-#endif
-
- // Above calls may have resulted in self destruction
- return ( hEntity != NULL );
-}
-
-//---------------------------------
-// Get a reference position in model space to compute
-// changes in model space for global brush entities (designer models them in different coords!)
-Vector CEntitySaveRestoreBlockHandler::ModelSpaceLandmark( int modelIndex )
-{
- const model_t *pModel = modelinfo->GetModel( modelIndex );
- if ( modelinfo->GetModelType( pModel ) != mod_brush )
- return vec3_origin;
-
- Vector mins, maxs;
- modelinfo->GetModelBounds( pModel, mins, maxs );
- return mins;
-}
-
-
-int CEntitySaveRestoreBlockHandler::RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo )
-{
- if ( !DoRestoreEntity( pEntity, pRestore ) )
- return 0;
-
-#if !defined( CLIENT_DLL )
- if ( pEntity->m_iGlobalname != NULL_STRING )
- {
- int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname );
- if ( globalIndex >= 0 )
- {
- // Already dead? delete
- if ( GlobalEntity_GetState( globalIndex ) == GLOBAL_DEAD )
- return -1;
- else if ( !FStrEq( STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex) ) )
- {
- pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
- }
- // In this level & not dead, continue on as normal
- }
- else
- {
- Warning( "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->m_iGlobalname), STRING(pEntity->m_iClassname) );
- // Spawned entities default to 'On'
- GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON );
- }
- }
-#endif
-
- return 0;
-}
-
-//---------------------------------
-
-#if !defined( CLIENT_DLL )
-
-int CEntitySaveRestoreBlockHandler::RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo )
-{
- Vector oldOffset;
- EHANDLE hEntitySafeHandle;
- hEntitySafeHandle = pEntity;
-
- oldOffset.Init();
- CRestore restoreHelper( pSaveData );
-
- string_t globalName = pEntInfo->globalname, className = pEntInfo->classname;
-
- // -------------------
-
- int globalIndex = GlobalEntity_GetIndex( globalName );
-
- // Don't overlay any instance of the global that isn't the latest
- // pSaveData->szCurrentMapName is the level this entity is coming from
- // pGlobal->levelName is the last level the global entity was active in.
- // If they aren't the same, then this global update is out of date.
- if ( !FStrEq( pSaveData->levelInfo.szCurrentMapName, GlobalEntity_GetMap(globalIndex) ) )
- {
- return 0;
- }
-
- // Compute the new global offset
- CBaseEntity *pNewEntity = FindGlobalEntity( className, globalName );
- if ( pNewEntity )
- {
-// Msg( "Overlay %s with %s\n", pNewEntity->GetClassname(), STRING(tmpEnt->classname) );
- // Tell the restore code we're overlaying a global entity from another level
- restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields
-
- pSaveData->modelSpaceOffset = pEntInfo->landmarkModelSpace - ModelSpaceLandmark( pNewEntity->GetModelIndex() );
-
- UTIL_Remove( pEntity );
- pEntity = pNewEntity;// we're going to restore this data OVER the old entity
- pEntInfo->hEnt = pEntity;
- // HACKHACK: Do we need system-wide support for removing non-global spawn allocated resources?
- pEntity->VPhysicsDestroyObject();
- Assert( pEntInfo->edictindex == -1 );
- // Update the global table to say that the global definition of this entity should come from this level
- GlobalEntity_SetMap( globalIndex, gpGlobals->mapname );
- }
- else
- {
- // This entity will be freed automatically by the engine-> If we don't do a restore on a matching entity (below)
- // or call EntityUpdate() to move it to this level, we haven't changed global state at all.
- DevMsg( "Warning: No match for global entity %s found in destination level\n", STRING(globalName) );
- return 0;
- }
-
- if ( !DoRestoreEntity( pEntity, &restoreHelper ) )
- {
- pEntity = NULL;
- }
-
- // Is this an overriding global entity (coming over the transition)
- pSaveData->modelSpaceOffset.Init();
- if ( pEntity )
- return 1;
- return 0;
-}
-
-#endif // !defined( CLIENT_DLL )
-
-
-
-//-----------------------------------------------------------------------------
-
-CSaveRestoreData *SaveInit( int size )
-{
- CSaveRestoreData *pSaveData;
-
-#if ( defined( CLIENT_DLL ) || defined( DISABLE_DEBUG_HISTORY ) )
- if ( size <= 0 )
- size = 2*1024*1024; // Reserve 2048K for now, UNDONE: Shrink this after compressing strings
-#else
- if ( size <= 0 )
- size = 3*1024*1024; // Reserve 3096K for now, UNDONE: Shrink this after compressing strings
-#endif
-
- int numentities;
-
-#if !defined( CLIENT_DLL )
- numentities = gEntList.NumberOfEntities();
-#else
- numentities = ClientEntityList().NumberOfEntities();
-#endif
-
- void *pSaveMemory = engine->SaveAllocMemory( sizeof(CSaveRestoreData) + (sizeof(entitytable_t) * numentities) + size, sizeof(char) );
- if ( !pSaveMemory )
- {
- return NULL;
- }
-
- pSaveData = MakeSaveRestoreData( pSaveMemory );
- pSaveData->Init( (char *)(pSaveData + 1), size ); // skip the save structure
-
- const int nTokens = 0xfff; // Assume a maximum of 4K-1 symbol table entries(each of some length)
- pSaveMemory = engine->SaveAllocMemory( nTokens, sizeof( char * ) );
- if ( !pSaveMemory )
- {
- engine->SaveFreeMemory( pSaveMemory );
- return NULL;
- }
-
- pSaveData->InitSymbolTable( (char **)pSaveMemory, nTokens );
-
- //---------------------------------
-
- pSaveData->levelInfo.time = gpGlobals->curtime; // Use DLL time
- pSaveData->levelInfo.vecLandmarkOffset = vec3_origin;
- pSaveData->levelInfo.fUseLandmark = false;
- pSaveData->levelInfo.connectionCount = 0;
-
- //---------------------------------
-
- gpGlobals->pSaveData = pSaveData;
-
- return pSaveData;
-}
-
-
-
-//-----------------------------------------------------------------------------
-//
-// ISaveRestoreBlockSet
-//
-// Purpose: Serves as holder for a group of sibling save sections. Takes
-// care of iterating over them, making sure read points are
-// queued up to the right spot (in case one section due to datadesc
-// changes reads less than expected, or doesn't leave the
-// read pointer at the right point), and ensuring the read pointer
-// is at the end of the entire set when the set read is done.
-//-----------------------------------------------------------------------------
-
-struct SaveRestoreBlockHeader_t
-{
- char szName[MAX_BLOCK_NAME_LEN + 1];
- int locHeader;
- int locBody;
-
- DECLARE_SIMPLE_DATADESC();
-};
-
-
-//-------------------------------------
-
-class CSaveRestoreBlockSet : public ISaveRestoreBlockSet
-{
-public:
- CSaveRestoreBlockSet( const char *pszName )
- {
- Q_strncpy( m_Name, pszName, sizeof(m_Name) );
- }
-
- const char *GetBlockName()
- {
- return m_Name;
- }
-
- //---------------------------------
-
- void PreSave( CSaveRestoreData *pData )
- {
- m_BlockHeaders.SetCount( m_Handlers.Count() );
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- Q_strncpy( m_BlockHeaders[i].szName, m_Handlers[i]->GetBlockName(), MAX_BLOCK_NAME_LEN + 1 );
- m_Handlers[i]->PreSave( pData );
- }
- }
-
- void Save( ISave *pSave )
- {
- int base = pSave->GetWritePos();
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- m_BlockHeaders[i].locBody = pSave->GetWritePos() - base;
- m_Handlers[i]->Save( pSave );
- }
- m_SizeBodies = pSave->GetWritePos() - base;
- }
-
- void WriteSaveHeaders( ISave *pSave )
- {
- int base = pSave->GetWritePos();
-
- //
- // Reserve space for a fully populated header
- //
- int dummyInt = -1;
- CUtlVector<SaveRestoreBlockHeader_t> dummyArr;
-
- dummyArr.SetCount( m_BlockHeaders.Count() );
- memset( &dummyArr[0], 0xff, dummyArr.Count() * sizeof(SaveRestoreBlockHeader_t) );
-
- pSave->WriteInt( &dummyInt ); // size all headers
- pSave->WriteInt( &dummyInt ); // size all bodies
- SaveUtlVector( pSave, &dummyArr, FIELD_EMBEDDED );
-
- //
- // Write the data
- //
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- m_BlockHeaders[i].locHeader = pSave->GetWritePos() - base;
- m_Handlers[i]->WriteSaveHeaders( pSave );
- }
-
- m_SizeHeaders = pSave->GetWritePos() - base;
-
- //
- // Write the actual header
- //
- int savedPos = pSave->GetWritePos();
- pSave->SetWritePos(base);
-
- pSave->WriteInt( &m_SizeHeaders );
- pSave->WriteInt( &m_SizeBodies );
- SaveUtlVector( pSave, &m_BlockHeaders, FIELD_EMBEDDED );
-
- pSave->SetWritePos(savedPos);
- }
-
- void PostSave()
- {
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- m_Handlers[i]->PostSave();
- }
- m_BlockHeaders.Purge();
- }
-
- //---------------------------------
-
- void PreRestore()
- {
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- m_Handlers[i]->PreRestore();
- }
- }
-
- void ReadRestoreHeaders( IRestore *pRestore )
- {
- int base = pRestore->GetReadPos();
-
- pRestore->ReadInt( &m_SizeHeaders );
- pRestore->ReadInt( &m_SizeBodies );
- RestoreUtlVector( pRestore, &m_BlockHeaders, FIELD_EMBEDDED );
-
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- int location = GetBlockHeaderLoc( m_Handlers[i]->GetBlockName() );
- if ( location != -1 )
- {
- pRestore->SetReadPos( base + location );
- m_Handlers[i]->ReadRestoreHeaders( pRestore );
- }
- }
-
- pRestore->SetReadPos( base + m_SizeHeaders );
- }
-
- void CallBlockHandlerRestore( ISaveRestoreBlockHandler *pHandler, int baseFilePos, IRestore *pRestore, bool fCreatePlayers )
- {
- int location = GetBlockBodyLoc( pHandler->GetBlockName() );
- if ( location != -1 )
- {
- pRestore->SetReadPos( baseFilePos + location );
- pHandler->Restore( pRestore, fCreatePlayers );
- }
- }
-
- void Restore( IRestore *pRestore, bool fCreatePlayers )
- {
- int base = pRestore->GetReadPos();
-
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- CallBlockHandlerRestore( m_Handlers[i], base, pRestore, fCreatePlayers );
- }
- pRestore->SetReadPos( base + m_SizeBodies );
- }
-
- void PostRestore()
- {
- for ( int i = 0; i < m_Handlers.Count(); i++ )
- {
- m_Handlers[i]->PostRestore();
- }
- m_BlockHeaders.Purge();
- }
-
- //---------------------------------
-
- void AddBlockHandler( ISaveRestoreBlockHandler *pHandler )
- {
- // Grody, but... while this class is still isolated in saverestore.cpp, this seems like a fine time to assert:
- AssertMsg( pHandler == &g_EntitySaveRestoreBlockHandler || (m_Handlers.Count() >= 1 && m_Handlers[0] == &g_EntitySaveRestoreBlockHandler), "Expected entity save load to always be first" );
-
- Assert( pHandler != this );
- m_Handlers.AddToTail( pHandler );
- }
-
- void RemoveBlockHandler( ISaveRestoreBlockHandler *pHandler )
- {
- m_Handlers.FindAndRemove( pHandler );
- }
-
- //---------------------------------
-
-private:
- int GetBlockBodyLoc( const char *pszName )
- {
- for ( int i = 0; i < m_BlockHeaders.Count(); i++ )
- {
- if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 )
- return m_BlockHeaders[i].locBody;
- }
- return -1;
- }
-
- int GetBlockHeaderLoc( const char *pszName )
- {
- for ( int i = 0; i < m_BlockHeaders.Count(); i++ )
- {
- if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 )
- return m_BlockHeaders[i].locHeader;
- }
- return -1;
- }
-
- char m_Name[MAX_BLOCK_NAME_LEN + 1];
- CUtlVector<ISaveRestoreBlockHandler *> m_Handlers;
-
- int m_SizeHeaders;
- int m_SizeBodies;
- CUtlVector<SaveRestoreBlockHeader_t> m_BlockHeaders;
-};
-
-//-------------------------------------
-
-BEGIN_SIMPLE_DATADESC( SaveRestoreBlockHeader_t )
- DEFINE_ARRAY(szName, FIELD_CHARACTER, MAX_BLOCK_NAME_LEN + 1),
- DEFINE_FIELD(locHeader, FIELD_INTEGER),
- DEFINE_FIELD(locBody, FIELD_INTEGER),
-END_DATADESC()
-
-//-------------------------------------
-
-CSaveRestoreBlockSet g_SaveRestoreBlockSet("Game");
-ISaveRestoreBlockSet *g_pGameSaveRestoreBlockSet = &g_SaveRestoreBlockSet;
-
-//=============================================================================
-#if !defined( CLIENT_DLL )
-
-//------------------------------------------------------------------------------
-// Creates all entities that lie in the transition list
-//------------------------------------------------------------------------------
-void CreateEntitiesInTransitionList( CSaveRestoreData *pSaveData, int levelMask )
-{
- CBaseEntity *pent;
- int i;
- for ( i = 0; i < pSaveData->NumEntities(); i++ )
- {
- entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i );
- pEntInfo->hEnt = NULL;
-
- if ( pEntInfo->size == 0 || pEntInfo->edictindex == 0 )
- continue;
-
- if ( pEntInfo->classname == NULL_STRING )
- {
- Warning( "Entity with data saved, but with no classname\n" );
- Assert(0);
- continue;
- }
-
- bool active = (pEntInfo->flags & levelMask) ? 1 : 0;
-
- // spawn players
- pent = NULL;
- if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) )
- {
- edict_t *ed = INDEXENT( pEntInfo->edictindex );
-
- if ( active && ed && !ed->IsFree() )
- {
- if ( !(pEntInfo->flags & FENTTABLE_PLAYER) )
- {
- Warning( "ENTITY IS NOT A PLAYER: %d\n" , i );
- Assert(0);
- }
-
- pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed );
- }
- }
- else if ( active )
- {
- pent = CreateEntityByName( STRING(pEntInfo->classname) );
- }
-
- pEntInfo->hEnt = pent;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask )
-{
- CBaseEntity *pent;
- entitytable_t *pEntInfo;
-
- // Create entity list
- CreateEntitiesInTransitionList( pSaveData, levelMask );
-
- // Now spawn entities
- CUtlVector<int> checkList;
-
- int i;
- int movedCount = 0;
- for ( i = 0; i < pSaveData->NumEntities(); i++ )
- {
- pEntInfo = pSaveData->GetEntityInfo( i );
- pent = pEntInfo->hEnt;
-// pSaveData->currentIndex = i;
- pSaveData->Seek( pEntInfo->location );
-
- // clear this out - it must be set on a per-entity basis
- pSaveData->modelSpaceOffset.Init();
-
- if ( pent && (pEntInfo->flags & levelMask) ) // Screen out the player if he's not to be spawned
- {
- if ( pEntInfo->flags & FENTTABLE_GLOBAL )
- {
- DevMsg( 2, "Merging changes for global: %s\n", STRING(pEntInfo->classname) );
-
- // -------------------------------------------------------------------------
- // Pass the "global" flag to the DLL to indicate this entity should only override
- // a matching entity, not be spawned
- if ( g_EntitySaveRestoreBlockHandler.RestoreGlobalEntity( pent, pSaveData, pEntInfo ) > 0 )
- {
- movedCount++;
- pEntInfo->restoreentityindex = pEntInfo->hEnt.Get()->entindex();
- AddRestoredEntity( pEntInfo->hEnt.Get() );
- }
- else
- {
- UTIL_RemoveImmediate( pEntInfo->hEnt.Get() );
- }
- // -------------------------------------------------------------------------
- }
- else
- {
- DevMsg( 2, "Transferring %s (%d)\n", STRING(pEntInfo->classname), pent->edict() ? ENTINDEX(pent->edict()) : -1 );
- CRestore restoreHelper( pSaveData );
- if ( g_EntitySaveRestoreBlockHandler.RestoreEntity( pent, &restoreHelper, pEntInfo ) < 0 )
- {
- UTIL_RemoveImmediate( pent );
- }
- else
- {
- // needs to be checked. Do this in a separate pass so that pointers & hierarchy can be traversed
- checkList.AddToTail(i);
- }
- }
-
- // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate()
- gEntList.CleanupDeleteList();
- }
- }
-
- for ( i = checkList.Count()-1; i >= 0; --i )
- {
- pEntInfo = pSaveData->GetEntityInfo( checkList[i] );
- pent = pEntInfo->hEnt;
-
- // NOTE: pent can be NULL because UTIL_RemoveImmediate (called below) removes all in hierarchy
- if ( !pent )
- continue;
-
- MDLCACHE_CRITICAL_SECTION();
-
- if ( !(pEntInfo->flags & FENTTABLE_PLAYER) && UTIL_EntityInSolid( pent ) )
- {
- // this can happen during normal processing - PVS is just a guess, some map areas won't exist in the new map
- DevMsg( 2, "Suppressing %s\n", STRING(pEntInfo->classname) );
- UTIL_RemoveImmediate( pent );
- // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate()
- gEntList.CleanupDeleteList();
- }
- else
- {
- movedCount++;
- pEntInfo->flags = FENTTABLE_REMOVED;
- pEntInfo->restoreentityindex = pent->entindex();
- AddRestoredEntity( pent );
- }
- }
-
- return movedCount;
-}
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Helper classes and functions for the save/restore system.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include <limits.h>
+#include "isaverestore.h"
+#include "saverestore.h"
+#include <stdarg.h>
+#include "shake.h"
+#include "decals.h"
+#include "gamerules.h"
+#include "bspfile.h"
+#include "mathlib/mathlib.h"
+#include "engine/IEngineSound.h"
+#include "saverestoretypes.h"
+#include "saverestore_utlvector.h"
+#include "model_types.h"
+#include "igamesystem.h"
+#include "interval.h"
+#include "vphysics/object_hash.h"
+#include "datacache/imdlcache.h"
+#include "tier0/vprof.h"
+
+#if !defined( CLIENT_DLL )
+
+#include "globalstate.h"
+#include "entitylist.h"
+
+#else
+
+#include "gamestringpool.h"
+
+#endif
+
+// HACKHACK: Builds a global list of entities that were restored from all levels
+#if !defined( CLIENT_DLL )
+void AddRestoredEntity( CBaseEntity *pEntity );
+#else
+void AddRestoredEntity( C_BaseEntity *pEntity );
+#endif
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define MAX_ENTITYARRAY 1024
+#define ZERO_TIME ((FLT_MAX*-0.5))
+// A bit arbitrary, but unlikely to collide with any saved games...
+#define TICK_NEVER_THINK_ENCODE ( INT_MAX - 3 )
+
+ASSERT_INVARIANT( sizeof(EHandlePlaceholder_t) == sizeof(EHANDLE) );
+
+//-----------------------------------------------------------------------------
+
+static int gSizes[FIELD_TYPECOUNT] =
+{
+ FIELD_SIZE( FIELD_VOID ),
+ FIELD_SIZE( FIELD_FLOAT ),
+ FIELD_SIZE( FIELD_STRING ),
+ FIELD_SIZE( FIELD_VECTOR ),
+ FIELD_SIZE( FIELD_QUATERNION ),
+ FIELD_SIZE( FIELD_INTEGER ),
+ FIELD_SIZE( FIELD_BOOLEAN ),
+ FIELD_SIZE( FIELD_SHORT ),
+ FIELD_SIZE( FIELD_CHARACTER ),
+ FIELD_SIZE( FIELD_COLOR32 ),
+ FIELD_SIZE( FIELD_EMBEDDED ),
+ FIELD_SIZE( FIELD_CUSTOM ),
+
+ FIELD_SIZE( FIELD_CLASSPTR ),
+ FIELD_SIZE( FIELD_EHANDLE ),
+ FIELD_SIZE( FIELD_EDICT ),
+
+ FIELD_SIZE( FIELD_POSITION_VECTOR ),
+ FIELD_SIZE( FIELD_TIME ),
+ FIELD_SIZE( FIELD_TICK ),
+ FIELD_SIZE( FIELD_MODELNAME ),
+ FIELD_SIZE( FIELD_SOUNDNAME ),
+
+ FIELD_SIZE( FIELD_INPUT ),
+ FIELD_SIZE( FIELD_FUNCTION ),
+ FIELD_SIZE( FIELD_VMATRIX ),
+ FIELD_SIZE( FIELD_VMATRIX_WORLDSPACE ),
+ FIELD_SIZE( FIELD_MATRIX3X4_WORLDSPACE ),
+ FIELD_SIZE( FIELD_INTERVAL ),
+ FIELD_SIZE( FIELD_MODELINDEX ),
+ FIELD_SIZE( FIELD_MATERIALINDEX ),
+
+ FIELD_SIZE( FIELD_VECTOR2D ),
+};
+
+
+// helpers to offset worldspace matrices
+static void VMatrixOffset( VMatrix &dest, const VMatrix &matrixIn, const Vector &offset )
+{
+ dest = matrixIn;
+ dest.PostTranslate( offset );
+}
+
+static void Matrix3x4Offset( matrix3x4_t& dest, const matrix3x4_t& matrixIn, const Vector &offset )
+{
+ MatrixCopy( matrixIn, dest );
+ Vector out;
+ MatrixGetColumn( matrixIn, 3, out );
+ out += offset;
+ MatrixSetColumn( out, 3, dest );
+}
+
+// This does the necessary casting / extract to grab a pointer to a member function as a void *
+// UNDONE: Cast to BASEPTR or something else here?
+#define EXTRACT_INPUTFUNC_FUNCTIONPTR(x) (*(inputfunc_t **)(&(x)))
+
+//-----------------------------------------------------------------------------
+// Purpose: Search this datamap for the name of this member function
+// This is used to save/restore function pointers (convert pointer to text)
+// Input : *function - pointer to member function
+// Output : const char * - function name
+//-----------------------------------------------------------------------------
+const char *UTIL_FunctionToName( datamap_t *pMap, inputfunc_t *function )
+{
+ while ( pMap )
+ {
+ for ( int i = 0; i < pMap->dataNumFields; i++ )
+ {
+ if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE )
+ {
+#ifdef WIN32
+ Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) );
+#elif defined(POSIX)
+ Assert( sizeof(pMap->dataDesc[i].inputFunc) == 8 );
+#else
+#error
+#endif
+ inputfunc_t *pTest = EXTRACT_INPUTFUNC_FUNCTIONPTR(pMap->dataDesc[i].inputFunc);
+
+ if ( pTest == function )
+ return pMap->dataDesc[i].fieldName;
+ }
+ }
+ pMap = pMap->baseMap;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Search the datamap for a function named pName
+// This is used to save/restore function pointers (convert text back to pointer)
+// Input : *pName - name of the member function
+//-----------------------------------------------------------------------------
+inputfunc_t *UTIL_FunctionFromName( datamap_t *pMap, const char *pName )
+{
+ while ( pMap )
+ {
+ for ( int i = 0; i < pMap->dataNumFields; i++ )
+ {
+#ifdef WIN32
+ Assert( sizeof(pMap->dataDesc[i].inputFunc) == sizeof(void *) );
+#elif defined(POSIX)
+ Assert( sizeof(pMap->dataDesc[i].inputFunc) == 8 );
+#else
+#error
+#endif
+
+ if ( pMap->dataDesc[i].flags & FTYPEDESC_FUNCTIONTABLE )
+ {
+ if ( FStrEq( pName, pMap->dataDesc[i].fieldName ) )
+ {
+ return EXTRACT_INPUTFUNC_FUNCTIONPTR(pMap->dataDesc[i].inputFunc);
+ }
+ }
+ }
+ pMap = pMap->baseMap;
+ }
+
+ Msg( "Failed to find function %s\n", pName );
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+//
+// CSave
+//
+//-----------------------------------------------------------------------------
+
+CSave::CSave( CSaveRestoreData *pdata )
+ : m_pData(pdata),
+ m_pGameInfo( pdata ),
+ m_bAsync( pdata->bAsync )
+{
+ m_BlockStartStack.EnsureCapacity( 32 );
+
+ // Logging.
+ m_hLogFile = NULL;
+}
+
+//-------------------------------------
+
+inline int CSave::DataEmpty( const char *pdata, int size )
+{
+ if ( size != 4 )
+ {
+ const char *pLimit = pdata + size;
+ while ( pdata < pLimit )
+ {
+ if ( *pdata++ )
+ return 0;
+ }
+ return 1;
+ }
+
+ return ( *((int *)pdata) == 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start logging save data.
+//-----------------------------------------------------------------------------
+void CSave::StartLogging( const char *pszLogName )
+{
+ m_hLogFile = filesystem->Open( pszLogName, "w" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop logging save data.
+//-----------------------------------------------------------------------------
+void CSave::EndLogging( void )
+{
+ if ( m_hLogFile )
+ {
+ filesystem->Close( m_hLogFile );
+ }
+ m_hLogFile = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if we are logging data.
+//-----------------------------------------------------------------------------
+bool CSave::IsLogging( void )
+{
+ return ( m_hLogFile != NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Log data.
+//-----------------------------------------------------------------------------
+void CSave::Log( const char *pName, fieldtype_t fieldType, void *value, int count )
+{
+ // Check to see if we are logging.
+ if ( !IsLogging() )
+ return;
+
+ static char szBuf[1024];
+ static char szTempBuf[256];
+
+ // Save the name.
+ Q_snprintf( szBuf, sizeof( szBuf ), "%s ", pName );
+
+ for ( int iCount = 0; iCount < count; ++iCount )
+ {
+ switch ( fieldType )
+ {
+ case FIELD_SHORT:
+ {
+ short *pValue = ( short* )( value );
+ short nValue = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_FLOAT:
+ {
+ float *pValue = ( float* )( value );
+ float flValue = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%f", flValue );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_BOOLEAN:
+ {
+ bool *pValue = ( bool* )( value );
+ bool bValue = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", ( int )( bValue ) );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_INTEGER:
+ {
+ int *pValue = ( int* )( value );
+ int nValue = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%d", nValue );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_STRING:
+ {
+ string_t *pValue = ( string_t* )( value );
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%s", ( char* )STRING( *pValue ) );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_VECTOR:
+ {
+ Vector *pValue = ( Vector* )( value );
+ Vector vecValue = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f)", vecValue.x, vecValue.y, vecValue.z );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_QUATERNION:
+ {
+ Quaternion *pValue = ( Quaternion* )( value );
+ Quaternion q = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%f %f %f %f)", q[0], q[1], q[2], q[3] );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ break;
+ }
+ case FIELD_CHARACTER:
+ {
+ char *pValue = ( char* )( value );
+ char chValue = pValue[iCount];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "%c", chValue );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ }
+ case FIELD_COLOR32:
+ {
+ byte *pValue = ( byte* )( value );
+ byte *pColor = &pValue[iCount*4];
+ Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%d %d %d %d)", ( int )pColor[0], ( int )pColor[1], ( int )pColor[2], ( int )pColor[3] );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ }
+ case FIELD_EMBEDDED:
+ case FIELD_CUSTOM:
+ default:
+ {
+ break;
+ }
+ }
+
+ // Add space data.
+ if ( ( iCount + 1 ) != count )
+ {
+ Q_strncpy( szTempBuf, " ", sizeof( szTempBuf ) );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ }
+ else
+ {
+ Q_strncpy( szTempBuf, "\n", sizeof( szTempBuf ) );
+ Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS );
+ }
+ }
+
+ int nLength = strlen( szBuf ) + 1;
+ filesystem->Write( szBuf, nLength, m_hLogFile );
+}
+
+//-------------------------------------
+
+bool CSave::IsAsync()
+{
+ return m_bAsync;
+}
+
+//-------------------------------------
+
+int CSave::GetWritePos() const
+{
+ return m_pData->GetCurPos();
+}
+
+//-------------------------------------
+
+void CSave::SetWritePos(int pos)
+{
+ m_pData->Seek(pos);
+}
+
+//-------------------------------------
+
+void CSave::WriteShort( const short *value, int count )
+{
+ BufferData( (const char *)value, sizeof(short) * count );
+}
+
+//-------------------------------------
+
+void CSave::WriteInt( const int *value, int count )
+{
+ BufferData( (const char *)value, sizeof(int) * count );
+}
+
+//-------------------------------------
+
+void CSave::WriteBool( const bool *value, int count )
+{
+ COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) );
+ BufferData( (const char *)value, sizeof(bool) * count );
+}
+
+//-------------------------------------
+
+void CSave::WriteFloat( const float *value, int count )
+{
+ BufferData( (const char *)value, sizeof(float) * count );
+}
+
+//-------------------------------------
+
+void CSave::WriteData( const char *pdata , int size )
+{
+ BufferData( pdata, size );
+}
+
+//-------------------------------------
+
+void CSave::WriteString( const char *pstring )
+{
+ BufferData( pstring, strlen(pstring) + 1 );
+}
+
+//-------------------------------------
+
+void CSave::WriteString( const string_t *stringId, int count )
+{
+ for ( int i = 0; i < count; i++ )
+ {
+ const char *pString = STRING(stringId[i]);
+ BufferData( pString, strlen(pString)+1 );
+ }
+}
+
+//-------------------------------------
+
+void CSave::WriteVector( const Vector &value )
+{
+ BufferData( (const char *)&value, sizeof(Vector) );
+}
+
+//-------------------------------------
+
+void CSave::WriteVector( const Vector *value, int count )
+{
+ BufferData( (const char *)value, sizeof(Vector) * count );
+}
+
+void CSave::WriteQuaternion( const Quaternion &value )
+{
+ BufferData( (const char *)&value, sizeof(Quaternion) );
+}
+
+//-------------------------------------
+
+void CSave::WriteQuaternion( const Quaternion *value, int count )
+{
+ BufferData( (const char *)value, sizeof(Quaternion) * count );
+}
+
+
+//-------------------------------------
+
+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::WriteBool( const char *pname, const bool *data, int count )
+{
+ COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) );
+ BufferField( pname, sizeof(bool) * 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::WriteString( const char *pname, const char *pdata )
+{
+ BufferField( pname, strlen(pdata) + 1, pdata );
+}
+
+//-------------------------------------
+
+void CSave::WriteString( const char *pname, const string_t *stringId, int count )
+{
+ int i, size;
+
+ size = 0;
+ for ( i = 0; i < count; i++ )
+ size += strlen( STRING( stringId[i] ) ) + 1;
+
+ WriteHeader( pname, size );
+ WriteString( stringId, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteVector( const char *pname, const Vector &value )
+{
+ WriteVector( pname, &value, 1 );
+}
+
+//-------------------------------------
+
+void CSave::WriteVector( const char *pname, const Vector *value, int count )
+{
+ WriteHeader( pname, sizeof(Vector) * count );
+ BufferData( (const char *)value, sizeof(Vector) * count );
+}
+
+void CSave::WriteQuaternion( const char *pname, const Quaternion &value )
+{
+ WriteQuaternion( pname, &value, 1 );
+}
+
+//-------------------------------------
+
+void CSave::WriteQuaternion( const char *pname, const Quaternion *value, int count )
+{
+ WriteHeader( pname, sizeof(Quaternion) * count );
+ BufferData( (const char *)value, sizeof(Quaternion) * count );
+}
+
+
+//-------------------------------------
+
+void CSave::WriteVMatrix( const VMatrix *value, int count )
+{
+ BufferData( (const char *)value, sizeof(VMatrix) * count );
+}
+
+//-------------------------------------
+
+void CSave::WriteVMatrix( const char *pname, const VMatrix *value, int count )
+{
+ WriteHeader( pname, sizeof(VMatrix) * count );
+ BufferData( (const char *)value, sizeof(VMatrix) * count );
+}
+
+//-------------------------------------
+
+void CSave::WriteVMatrixWorldspace( const VMatrix *value, int count )
+{
+ for ( int i = 0; i < count; i++ )
+ {
+ VMatrix tmp;
+ VMatrixOffset( tmp, value[i], -m_pGameInfo->GetLandmark() );
+ BufferData( (const char *)&tmp, sizeof(VMatrix) );
+ }
+}
+
+//-------------------------------------
+
+void CSave::WriteVMatrixWorldspace( const char *pname, const VMatrix *value, int count )
+{
+ WriteHeader( pname, sizeof(VMatrix) * count );
+ WriteVMatrixWorldspace( value, count );
+}
+
+void CSave::WriteMatrix3x4Worldspace( const matrix3x4_t *value, int count )
+{
+ Vector offset = -m_pGameInfo->GetLandmark();
+ for ( int i = 0; i < count; i++ )
+ {
+ matrix3x4_t tmp;
+ Matrix3x4Offset( tmp, value[i], offset );
+ BufferData( (const char *)value, sizeof(matrix3x4_t) );
+ }
+}
+
+//-------------------------------------
+
+void CSave::WriteMatrix3x4Worldspace( const char *pname, const matrix3x4_t *value, int count )
+{
+ WriteHeader( pname, sizeof(matrix3x4_t) * count );
+ WriteMatrix3x4Worldspace( value, count );
+}
+
+void CSave::WriteInterval( const char *pname, const interval_t *value, int count )
+{
+ WriteHeader( pname, sizeof( interval_t ) * count );
+ WriteInterval( value, count );
+}
+
+void CSave::WriteInterval( const interval_t *value, int count )
+{
+ BufferData( (const char *)value, count * sizeof( interval_t ) );
+}
+
+//-------------------------------------
+
+bool CSave::ShouldSaveField( const void *pData, typedescription_t *pField )
+{
+ if ( !(pField->flags & FTYPEDESC_SAVE) || pField->fieldType == FIELD_VOID )
+ return false;
+
+ switch ( pField->fieldType )
+ {
+ case FIELD_EMBEDDED:
+ {
+ if ( pField->flags & FTYPEDESC_PTR )
+ {
+ AssertMsg( pField->fieldSize == 1, "Arrays of embedded pointer types presently unsupported by save/restore" );
+ if ( pField->fieldSize != 1 )
+ return false;
+ }
+
+ AssertMsg( pField->td != NULL, "Embedded type appears to have not had type description implemented" );
+ if ( pField->td == NULL )
+ return false;
+
+ if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pData) )
+ return false;
+
+ // @TODO: need real logic for handling embedded types with base classes
+ if ( pField->td->baseMap )
+ {
+ return true;
+ }
+
+ int nFieldCount = pField->fieldSize;
+ char *pTestData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) );
+ while ( --nFieldCount >= 0 )
+ {
+ typedescription_t *pTestField = pField->td->dataDesc;
+ typedescription_t *pLimit = pField->td->dataDesc + pField->td->dataNumFields;
+
+ for ( ; pTestField < pLimit; ++pTestField )
+ {
+ if ( ShouldSaveField( pTestData + pTestField->fieldOffset[ TD_OFFSET_NORMAL ], pTestField ) )
+ return true;
+ }
+
+ pTestData += pField->fieldSizeInBytes;
+ }
+ return false;
+ }
+
+ case FIELD_CUSTOM:
+ {
+ // ask the data if it's empty
+ SaveRestoreFieldInfo_t fieldInfo =
+ {
+ const_cast<void *>(pData),
+ ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ],
+ pField
+ };
+ if ( pField->pSaveRestoreOps->IsEmpty( fieldInfo ) )
+ return false;
+ }
+ return true;
+
+ case FIELD_EHANDLE:
+ {
+ if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) )
+ {
+ Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName );
+ Assert( 0 );
+ }
+
+ int *pEHandle = (int *)pData;
+ for ( int i = 0; i < pField->fieldSize; ++i, ++pEHandle )
+ {
+ if ( (*pEHandle) != 0xFFFFFFFF )
+ return true;
+ }
+ }
+ return false;
+
+ default:
+ {
+ if ( (pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType]) )
+ {
+ Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName );
+ Assert( 0 );
+ }
+
+ // old byte-by-byte null check
+ if ( DataEmpty( (const char *)pData, pField->fieldSize * gSizes[pField->fieldType] ) )
+ return false;
+ }
+ return true;
+ }
+}
+
+//-------------------------------------
+// Purpose: Writes all the fields that are client neutral. In the event of
+// a librarization of save/restore, these would reside in the library
+//
+
+bool CSave::WriteBasicField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField )
+{
+ switch( pField->fieldType )
+ {
+ case FIELD_FLOAT:
+ WriteFloat( pField->fieldName, (float *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_STRING:
+ WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_VECTOR:
+ WriteVector( pField->fieldName, (Vector *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_QUATERNION:
+ WriteQuaternion( pField->fieldName, (Quaternion *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_INTEGER:
+ WriteInt( pField->fieldName, (int *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_BOOLEAN:
+ WriteBool( pField->fieldName, (bool *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_SHORT:
+ WriteData( pField->fieldName, 2 * pField->fieldSize, ((char *)pData) );
+ break;
+
+ case FIELD_CHARACTER:
+ WriteData( pField->fieldName, pField->fieldSize, ((char *)pData) );
+ break;
+
+ case FIELD_COLOR32:
+ WriteData( pField->fieldName, 4*pField->fieldSize, (char *)pData );
+ break;
+
+ case FIELD_EMBEDDED:
+ {
+ AssertMsg( ( (pField->flags & FTYPEDESC_PTR) == 0 ) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" );
+ Assert( !(pField->flags & FTYPEDESC_PTR) || *((void **)pData) );
+ int nFieldCount = pField->fieldSize;
+ char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pData : *((void **)pData) );
+
+ StartBlock( pField->fieldName );
+
+ while ( --nFieldCount >= 0 )
+ {
+ WriteAll( pFieldData, pField->td );
+ pFieldData += pField->fieldSizeInBytes;
+ }
+
+ EndBlock();
+ break;
+ }
+
+ case FIELD_CUSTOM:
+ {
+ // Note it is up to the custom type implementor to handle arrays
+ StartBlock( pField->fieldName );
+
+ SaveRestoreFieldInfo_t fieldInfo =
+ {
+ pData,
+ ((char *)pData) - pField->fieldOffset[ TD_OFFSET_NORMAL ],
+ pField
+ };
+ pField->pSaveRestoreOps->Save( fieldInfo, this );
+
+ EndBlock();
+ break;
+ }
+
+ default:
+ Warning( "Bad field type\n" );
+ Assert(0);
+ return false;
+ }
+
+ return true;
+}
+
+//-------------------------------------
+
+bool CSave::WriteField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField )
+{
+#ifdef _DEBUG
+ Log( pname, (fieldtype_t)pField->fieldType, pData, pField->fieldSize );
+#endif
+
+ if ( pField->fieldType <= FIELD_CUSTOM )
+ {
+ return WriteBasicField( pname, pData, pRootMap, pField );
+ }
+ return WriteGameField( pname, pData, pRootMap, pField );
+}
+
+//-------------------------------------
+
+int CSave::WriteFields( const char *pname, const void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount )
+{
+ typedescription_t *pTest;
+ int iHeaderPos = m_pData->GetCurPos();
+ int count = -1;
+ WriteInt( pname, &count, 1 );
+
+ count = 0;
+
+#ifdef _X360
+ __dcbt( 0, pBaseData );
+ __dcbt( 128, pBaseData );
+ __dcbt( 256, pBaseData );
+ __dcbt( 512, pBaseData );
+ void *pDest = m_pData->AccessCurPos();
+ __dcbt( 0, pDest );
+ __dcbt( 128, pDest );
+ __dcbt( 256, pDest );
+ __dcbt( 512, pDest );
+#endif
+
+ for ( int i = 0; i < fieldCount; i++ )
+ {
+ pTest = &pFields[ i ];
+ void *pOutputData = ( (char *)pBaseData + pTest->fieldOffset[ TD_OFFSET_NORMAL ] );
+
+ if ( !ShouldSaveField( pOutputData, pTest ) )
+ continue;
+
+ if ( !WriteField( pname, pOutputData, pRootMap, pTest ) )
+ break;
+ count++;
+ }
+
+ int iCurPos = m_pData->GetCurPos();
+ int iRewind = iCurPos - iHeaderPos;
+ m_pData->Rewind( iRewind );
+ WriteInt( pname, &count, 1 );
+ iCurPos = m_pData->GetCurPos();
+ m_pData->MoveCurPos( iRewind - ( iCurPos - iHeaderPos ) );
+
+ return 1;
+}
+
+//-------------------------------------
+// Purpose: Recursively saves all the classes in an object, in reverse order (top down)
+// Output : int 0 on failure, 1 on success
+
+int CSave::DoWriteAll( const void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap )
+{
+ // save base classes first
+ if ( pCurMap->baseMap )
+ {
+ int status = DoWriteAll( pLeafObject, pLeafMap, pCurMap->baseMap );
+ if ( !status )
+ return status;
+ }
+
+ return WriteFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields );
+}
+
+//-------------------------------------
+
+void CSave::StartBlock( const char *pszBlockName )
+{
+ WriteHeader( pszBlockName, 0 ); // placeholder
+ m_BlockStartStack.AddToTail( GetWritePos() );
+}
+
+//-------------------------------------
+
+void CSave::StartBlock()
+{
+ StartBlock( "" );
+}
+
+//-------------------------------------
+
+void CSave::EndBlock()
+{
+ int endPos = GetWritePos();
+ int startPos = m_BlockStartStack[ m_BlockStartStack.Count() - 1 ];
+ short sizeBlock = endPos - startPos;
+
+ m_BlockStartStack.Remove( m_BlockStartStack.Count() - 1 );
+
+ // Move to the the location where the size of the block was written & rewrite the size
+ SetWritePos( startPos - sizeof(SaveRestoreRecordHeader_t) );
+ BufferData( (const char *)&sizeBlock, sizeof(short) );
+
+ SetWritePos( endPos );
+}
+
+//-------------------------------------
+
+void CSave::BufferString( char *pdata, int len )
+{
+ char c = 0;
+
+ BufferData( pdata, len ); // Write the string
+ BufferData( &c, 1 ); // Write a null terminator
+}
+
+//-------------------------------------
+
+void CSave::BufferField( const char *pname, int size, const char *pdata )
+{
+ WriteHeader( pname, size );
+ BufferData( pdata, size );
+}
+
+//-------------------------------------
+
+void CSave::WriteHeader( const char *pname, int size )
+{
+ short shortSize = size;
+ short hashvalue = m_pData->FindCreateSymbol( pname );
+ if ( size > SHRT_MAX || size < 0 )
+ {
+ Warning( "CSave::WriteHeader() size parameter exceeds 'short'!\n" );
+ Assert(0);
+ }
+
+ BufferData( (const char *)&shortSize, sizeof(short) );
+ BufferData( (const char *)&hashvalue, sizeof(short) );
+}
+
+//-------------------------------------
+
+void CSave::BufferData( const char *pdata, int size )
+{
+ if ( !m_pData )
+ return;
+
+ if ( !m_pData->Write( pdata, size ) )
+ {
+ Warning( "Save/Restore overflow!\n" );
+ Assert(0);
+ }
+}
+
+//---------------------------------------------------------
+//
+// Game centric save methods.
+//
+int CSave::EntityIndex( const edict_t *pentLookup )
+{
+#if !defined( CLIENT_DLL )
+ if ( pentLookup == NULL )
+ return -1;
+ return EntityIndex( CBaseEntity::Instance(pentLookup) );
+#else
+ Assert( !"CSave::EntityIndex( edict_t * ) not valid on client!" );
+ return -1;
+#endif
+}
+
+
+//-------------------------------------
+
+int CSave::EntityIndex( const CBaseEntity *pEntity )
+{
+ return m_pGameInfo->GetEntityIndex( pEntity );
+}
+
+//-------------------------------------
+
+int CSave::EntityFlagsSet( int entityIndex, int flags )
+{
+ if ( !m_pGameInfo || entityIndex < 0 )
+ return 0;
+ if ( entityIndex > m_pGameInfo->NumEntities() )
+ return 0;
+
+ m_pGameInfo->GetEntityInfo( entityIndex )->flags |= flags;
+
+ return m_pGameInfo->GetEntityInfo( entityIndex )->flags;
+}
+
+//-------------------------------------
+
+void CSave::WriteTime( const char *pname, const float *data, int count )
+{
+ int i;
+ float tmp;
+
+ WriteHeader( pname, sizeof(float) * count );
+ for ( i = 0; i < count; i++ )
+ {
+ // 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
+ Assert( data[i] != ZERO_TIME );
+
+ if ( data[i] == 0.0 )
+ {
+ tmp = ZERO_TIME;
+ }
+ else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX )
+ {
+ tmp = data[i];
+ }
+ else
+ {
+ tmp = data[i] - m_pGameInfo->GetBaseTime();
+ if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing
+ tmp = 0.001;
+ }
+
+ WriteData( (const char *)&tmp, sizeof(float) );
+ }
+}
+
+//-------------------------------------
+
+void CSave::WriteTime( const float *data, int count )
+{
+ int i;
+ float tmp;
+
+ for ( i = 0; i < count; i++ )
+ {
+ // 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 ( data[i] == 0.0 )
+ {
+ tmp = ZERO_TIME;
+ }
+ else if ( data[i] == INVALID_TIME || data[i] == FLT_MAX )
+ {
+ tmp = data[i];
+ }
+ else
+ {
+ tmp = data[i] - m_pGameInfo->GetBaseTime();
+ if ( fabsf( tmp ) < 0.001 ) // never allow a time to become zero due to rebasing
+ tmp = 0.001;
+ }
+
+ WriteData( (const char *)&tmp, sizeof(float) );
+ }
+}
+
+void CSave::WriteTick( const char *pname, const int *data, int count )
+{
+ WriteHeader( pname, sizeof(int) * count );
+ WriteTick( data, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteTick( const int *data, int count )
+{
+ int i;
+ int tmp;
+
+ int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() );
+
+ for ( i = 0; i < count; i++ )
+ {
+ // 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
+ tmp = data[ i ];
+ if ( data[ i ] == TICK_NEVER_THINK )
+ {
+ tmp = TICK_NEVER_THINK_ENCODE;
+ }
+ else
+ {
+ // Rebase it...
+ tmp -= baseTick;
+ }
+ WriteData( (const char *)&tmp, sizeof(int) );
+ }
+}
+//-------------------------------------
+
+void CSave::WritePositionVector( const char *pname, const Vector &value )
+{
+ Vector tmp = value;
+
+ if ( tmp != vec3_invalid )
+ tmp -= m_pGameInfo->GetLandmark();
+
+ WriteVector( pname, tmp );
+}
+
+//-------------------------------------
+
+void CSave::WritePositionVector( const Vector &value )
+{
+ Vector tmp = value;
+
+ if ( tmp != vec3_invalid )
+ tmp -= m_pGameInfo->GetLandmark();
+
+ WriteVector( tmp );
+}
+
+//-------------------------------------
+
+void CSave::WritePositionVector( const char *pname, const Vector *value, int count )
+{
+ WriteHeader( pname, sizeof(Vector) * count );
+ WritePositionVector( value, count );
+}
+
+//-------------------------------------
+
+void CSave::WritePositionVector( const Vector *value, int count )
+{
+ int i;
+ Vector tmp;
+ for ( i = 0; i < count; i++ )
+ {
+ Vector tmp = value[i];
+
+ if ( tmp != vec3_invalid )
+ tmp -= m_pGameInfo->GetLandmark();
+
+ WriteData( (const char *)&tmp.x, sizeof(Vector) );
+ }
+}
+
+//-------------------------------------
+
+void CSave::WriteFunction( datamap_t *pRootMap, const char *pname, inputfunc_t **data, int count )
+{
+ AssertMsg( count == 1, "Arrays of functions not presently supported" );
+ const char *functionName = UTIL_FunctionToName( pRootMap, *data );
+ if ( !functionName )
+ {
+ Warning( "Invalid function pointer in entity!\n" );
+ Assert(0);
+ functionName = "BADFUNCTIONPOINTER";
+ }
+
+ BufferField( pname, strlen(functionName) + 1, functionName );
+}
+
+//-------------------------------------
+
+void CSave::WriteEntityPtr( const char *pname, CBaseEntity **ppEntity, int count )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
+ {
+ entityArray[i] = EntityIndex( ppEntity[i] );
+ }
+ WriteInt( pname, entityArray, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteEntityPtr( CBaseEntity **ppEntity, int count )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
+ {
+ entityArray[i] = EntityIndex( ppEntity[i] );
+ }
+ WriteInt( entityArray, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteEdictPtr( const char *pname, edict_t **ppEdict, int count )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
+ {
+ entityArray[i] = EntityIndex( ppEdict[i] );
+ }
+ WriteInt( pname, entityArray, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteEdictPtr( edict_t **ppEdict, int count )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
+ {
+ entityArray[i] = EntityIndex( ppEdict[i] );
+ }
+ WriteInt( entityArray, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteEHandle( const char *pname, const EHANDLE *pEHandle, int count )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
+ {
+ entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast<EHANDLE *>(pEHandle)[i]) );
+ }
+ WriteInt( pname, entityArray, count );
+}
+
+//-------------------------------------
+
+void CSave::WriteEHandle( const EHANDLE *pEHandle, int count )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ for ( int i = 0; i < count && i < MAX_ENTITYARRAY; i++ )
+ {
+ entityArray[i] = EntityIndex( (CBaseEntity *)(const_cast<EHANDLE *>(pEHandle)[i]) );
+ }
+ WriteInt( entityArray, count );
+}
+
+//-------------------------------------
+// Purpose: Writes all the fields that are not client neutral. In the event of
+// a librarization of save/restore, these would not reside in the library
+
+bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField )
+{
+ switch( pField->fieldType )
+ {
+ case FIELD_CLASSPTR:
+ WriteEntityPtr( pField->fieldName, (CBaseEntity **)pData, pField->fieldSize );
+ break;
+
+ case FIELD_EDICT:
+ WriteEdictPtr( pField->fieldName, (edict_t **)pData, pField->fieldSize );
+ break;
+
+ case FIELD_EHANDLE:
+ WriteEHandle( pField->fieldName, (EHANDLE *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_POSITION_VECTOR:
+ WritePositionVector( pField->fieldName, (Vector *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_TIME:
+ WriteTime( pField->fieldName, (float *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_TICK:
+ WriteTick( pField->fieldName, (int *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_MODELINDEX:
+ {
+ int nModelIndex = *(int*)pData;
+ string_t strModelName = NULL_STRING;
+ const model_t *pModel = modelinfo->GetModel( nModelIndex );
+ if ( pModel )
+ {
+ strModelName = AllocPooledString( modelinfo->GetModelName( pModel ) );
+ }
+ WriteString( pField->fieldName, (string_t *)&strModelName, pField->fieldSize );
+ }
+ break;
+
+ case FIELD_MATERIALINDEX:
+ {
+ int nMateralIndex = *(int*)pData;
+ string_t strMaterialName = NULL_STRING;
+ const char *pMaterialName = GetMaterialNameFromIndex( nMateralIndex );
+ if ( pMaterialName )
+ {
+ strMaterialName = MAKE_STRING( pMaterialName );
+ }
+ WriteString( pField->fieldName, (string_t *)&strMaterialName, pField->fieldSize );
+ }
+ break;
+
+ case FIELD_MODELNAME:
+ case FIELD_SOUNDNAME:
+ WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize );
+ break;
+
+ // For now, just write the address out, we're not going to change memory while doing this yet!
+ case FIELD_FUNCTION:
+ WriteFunction( pRootMap, pField->fieldName, (inputfunc_t **)(char *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_VMATRIX:
+ WriteVMatrix( pField->fieldName, (VMatrix *)pData, pField->fieldSize );
+ break;
+ case FIELD_VMATRIX_WORLDSPACE:
+ WriteVMatrixWorldspace( pField->fieldName, (VMatrix *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_MATRIX3X4_WORLDSPACE:
+ WriteMatrix3x4Worldspace( pField->fieldName, (const matrix3x4_t *)pData, pField->fieldSize );
+ break;
+
+ case FIELD_INTERVAL:
+ WriteInterval( pField->fieldName, (interval_t *)pData, pField->fieldSize );
+ break;
+
+ default:
+ Warning( "Bad field type\n" );
+ Assert(0);
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//
+// CRestore
+//
+//-----------------------------------------------------------------------------
+
+CRestore::CRestore( CSaveRestoreData *pdata )
+ : m_pData( pdata ),
+ m_pGameInfo( pdata ),
+ m_global( 0 ),
+ m_precache( true )
+{
+ m_BlockEndStack.EnsureCapacity( 32 );
+}
+
+//-------------------------------------
+
+int CRestore::GetReadPos() const
+{
+ return m_pData->GetCurPos();
+}
+
+//-------------------------------------
+
+void CRestore::SetReadPos( int pos )
+{
+ m_pData->Seek(pos);
+}
+
+//-------------------------------------
+
+const char *CRestore::StringFromHeaderSymbol( int symbol )
+{
+ const char *pszResult = m_pData->StringFromSymbol( symbol );
+ return ( pszResult ) ? pszResult : "";
+}
+
+//-------------------------------------
+// Purpose: Reads all the fields that are client neutral. In the event of
+// a librarization of save/restore, these would reside in the library
+
+void CRestore::ReadBasicField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField )
+{
+ switch( pField->fieldType )
+ {
+ case FIELD_FLOAT:
+ {
+ ReadFloat( (float *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+ case FIELD_STRING:
+ {
+ ReadString( (string_t *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_VECTOR:
+ {
+ ReadVector( (Vector *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_QUATERNION:
+ {
+ ReadQuaternion( (Quaternion *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_INTEGER:
+ {
+ ReadInt( (int *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_BOOLEAN:
+ {
+ ReadBool( (bool *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_SHORT:
+ {
+ ReadShort( (short *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_CHARACTER:
+ {
+ ReadData( (char *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_COLOR32:
+ {
+ COMPILE_TIME_ASSERT( sizeof(color32) == sizeof(int) );
+ ReadInt( (int *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_EMBEDDED:
+ {
+ AssertMsg( (( pField->flags & FTYPEDESC_PTR ) == 0) || (pField->fieldSize == 1), "Arrays of embedded pointer types presently unsupported by save/restore" );
+#ifdef DBGFLAG_ASSERT
+ int startPos = GetReadPos();
+#endif
+ if ( !(pField->flags & FTYPEDESC_PTR) || *((void **)pDest) )
+ {
+ int nFieldCount = pField->fieldSize;
+ char *pFieldData = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pDest : *((void **)pDest) );
+ while ( --nFieldCount >= 0 )
+ {
+ // No corresponding "block" (see write) as it was used as the header of the field
+ ReadAll( pFieldData, pField->td );
+ pFieldData += pField->fieldSizeInBytes;
+ }
+ Assert( GetReadPos() - startPos == header.size );
+ }
+ else
+ {
+ SetReadPos( GetReadPos() + header.size );
+ Warning( "Attempted to restore FIELD_EMBEDDEDBYREF %s but there is no destination memory\n", pField->fieldName );
+ }
+ break;
+
+ }
+ case FIELD_CUSTOM:
+ {
+ // No corresponding "block" (see write) as it was used as the header of the field
+ int posNextField = GetReadPos() + header.size;
+
+ SaveRestoreFieldInfo_t fieldInfo =
+ {
+ pDest,
+ ((char *)pDest) - pField->fieldOffset[ TD_OFFSET_NORMAL ],
+ pField
+ };
+
+ pField->pSaveRestoreOps->Restore( fieldInfo, this );
+
+ Assert( posNextField >= GetReadPos() );
+ SetReadPos( posNextField );
+ break;
+ }
+
+ default:
+ Warning( "Bad field type\n" );
+ Assert(0);
+ }
+}
+
+//-------------------------------------
+
+void CRestore::ReadField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField )
+{
+ if ( pField->fieldType <= FIELD_CUSTOM )
+ ReadBasicField( header, pDest, pRootMap, pField );
+ else
+ ReadGameField( header, pDest, pRootMap, pField );
+}
+
+//-------------------------------------
+
+bool CRestore::ShouldReadField( typedescription_t *pField )
+{
+ if ( (pField->flags & FTYPEDESC_SAVE) == 0 )
+ return false;
+
+ if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) )
+ return false;
+
+ return true;
+}
+
+//-------------------------------------
+
+typedescription_t *CRestore::FindField( const char *pszFieldName, typedescription_t *pFields, int fieldCount, int *pCookie )
+{
+ int &fieldNumber = *pCookie;
+ if ( pszFieldName )
+ {
+ typedescription_t *pTest;
+
+ for ( int i = 0; i < fieldCount; i++ )
+ {
+ pTest = &pFields[fieldNumber];
+
+ ++fieldNumber;
+ if ( fieldNumber == fieldCount )
+ fieldNumber = 0;
+
+ if ( stricmp( pTest->fieldName, pszFieldName ) == 0 )
+ return pTest;
+ }
+ }
+
+ fieldNumber = 0;
+ return NULL;
+}
+
+//-------------------------------------
+
+bool CRestore::ShouldEmptyField( typedescription_t *pField )
+{
+ // don't clear out fields that don't get saved, or that are handled specially
+ if ( !( pField->flags & FTYPEDESC_SAVE ) )
+ return false;
+
+ // Don't clear global fields
+ if ( m_global && (pField->flags & FTYPEDESC_GLOBAL) )
+ return false;
+
+ return true;
+}
+
+//-------------------------------------
+
+void CRestore::EmptyFields( void *pBaseData, typedescription_t *pFields, int fieldCount )
+{
+ int i;
+ for ( i = 0; i < fieldCount; i++ )
+ {
+ typedescription_t *pField = &pFields[i];
+ if ( !ShouldEmptyField( pField ) )
+ continue;
+
+ void *pFieldData = (char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ];
+ switch( pField->fieldType )
+ {
+ case FIELD_CUSTOM:
+ {
+ SaveRestoreFieldInfo_t fieldInfo =
+ {
+ pFieldData,
+ pBaseData,
+ pField
+ };
+ pField->pSaveRestoreOps->MakeEmpty( fieldInfo );
+ }
+ break;
+
+ case FIELD_EMBEDDED:
+ {
+ if ( (pField->flags & FTYPEDESC_PTR) && !*((void **)pFieldData) )
+ break;
+
+ int nFieldCount = pField->fieldSize;
+ char *pFieldMemory = (char *)( ( !(pField->flags & FTYPEDESC_PTR) ) ? pFieldData : *((void **)pFieldData) );
+ while ( --nFieldCount >= 0 )
+ {
+ EmptyFields( pFieldMemory, pField->td->dataDesc, pField->td->dataNumFields );
+ pFieldMemory += pField->fieldSizeInBytes;
+ }
+ }
+ break;
+
+ default:
+ // NOTE: If you hit this assertion, you've got a bug where you're using
+ // the wrong field type for your field
+ if ( pField->fieldSizeInBytes != pField->fieldSize * gSizes[pField->fieldType] )
+ {
+ Warning("WARNING! Field %s is using the wrong FIELD_ type!\nFix this or you'll see a crash.\n", pField->fieldName );
+ Assert( 0 );
+ }
+ memset( pFieldData, (pField->fieldType != FIELD_EHANDLE) ? 0 : 0xFF, pField->fieldSize * gSizes[pField->fieldType] );
+ break;
+ }
+ }
+}
+
+//-------------------------------------
+
+void CRestore::StartBlock( SaveRestoreRecordHeader_t *pHeader )
+{
+ ReadHeader( pHeader );
+ m_BlockEndStack.AddToTail( GetReadPos() + pHeader->size );
+}
+
+//-------------------------------------
+
+void CRestore::StartBlock( char szBlockName[] )
+{
+ SaveRestoreRecordHeader_t header;
+ StartBlock( &header );
+ Q_strncpy( szBlockName, StringFromHeaderSymbol( header.symbol ), SIZE_BLOCK_NAME_BUF );
+}
+
+//-------------------------------------
+
+void CRestore::StartBlock()
+{
+ char szBlockName[SIZE_BLOCK_NAME_BUF];
+ StartBlock( szBlockName );
+}
+
+//-------------------------------------
+
+void CRestore::EndBlock()
+{
+ int endPos = m_BlockEndStack[ m_BlockEndStack.Count() - 1 ];
+ m_BlockEndStack.Remove( m_BlockEndStack.Count() - 1 );
+ SetReadPos( endPos );
+}
+
+//-------------------------------------
+
+int CRestore::ReadFields( const char *pname, void *pBaseData, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount )
+{
+ static int lastName = -1;
+ Verify( ReadShort() == sizeof(int) ); // First entry should be an int
+ int symName = m_pData->FindCreateSymbol(pname);
+
+ // Check the struct name
+ int curSym = ReadShort();
+ if ( curSym != symName ) // Field Set marker
+ {
+ const char *pLastName = m_pData->StringFromSymbol( lastName );
+ const char *pCurName = m_pData->StringFromSymbol( curSym );
+ Msg( "Expected %s found %s ( raw '%s' )! (prev: %s)\n", pname, pCurName, BufferPointer(), pLastName );
+ Msg( "Field type name may have changed or inheritance graph changed, save file is suspect\n" );
+ m_pData->Rewind( 2*sizeof(short) );
+ return 0;
+ }
+ lastName = symName;
+
+ // Clear out base data
+ EmptyFields( pBaseData, pFields, fieldCount );
+
+ // Skip over the struct name
+ int i;
+ int nFieldsSaved = ReadInt(); // Read field count
+ int searchCookie = 0; // Make searches faster, most data is read/written in the same order
+ SaveRestoreRecordHeader_t header;
+
+ for ( i = 0; i < nFieldsSaved; i++ )
+ {
+ ReadHeader( &header );
+
+ typedescription_t *pField = FindField( m_pData->StringFromSymbol( header.symbol ), pFields, fieldCount, &searchCookie);
+ if ( pField && ShouldReadField( pField ) )
+ {
+ ReadField( header, ((char *)pBaseData + pField->fieldOffset[ TD_OFFSET_NORMAL ]), pRootMap, pField );
+ }
+ else
+ {
+ BufferSkipBytes( header.size ); // Advance to next field
+ }
+ }
+
+ return 1;
+}
+
+//-------------------------------------
+
+void CRestore::ReadHeader( SaveRestoreRecordHeader_t *pheader )
+{
+ if ( pheader != NULL )
+ {
+ Assert( pheader!=NULL );
+ pheader->size = ReadShort(); // Read field size
+ pheader->symbol = ReadShort(); // Read field name token
+ }
+ else
+ {
+ BufferSkipBytes( sizeof(short) * 2 );
+ }
+}
+
+//-------------------------------------
+
+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;
+}
+
+//-------------------------------------
+// Purpose: Recursively restores all the classes in an object, in reverse order (top down)
+// Output : int 0 on failure, 1 on success
+
+int CRestore::DoReadAll( void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap )
+{
+ // restore base classes first
+ if ( pCurMap->baseMap )
+ {
+ int status = DoReadAll( pLeafObject, pLeafMap, pCurMap->baseMap );
+ if ( !status )
+ return status;
+ }
+
+ return ReadFields( pCurMap->dataClassName, pLeafObject, pLeafMap, pCurMap->dataDesc, pCurMap->dataNumFields );
+}
+
+//-------------------------------------
+
+char *CRestore::BufferPointer( void )
+{
+ if ( !m_pData )
+ return NULL;
+
+ return m_pData->AccessCurPos();
+}
+
+//-------------------------------------
+
+void CRestore::BufferReadBytes( char *pOutput, int size )
+{
+ Assert( m_pData !=NULL );
+
+ if ( !m_pData || m_pData->BytesAvailable() == 0 )
+ return;
+
+ if ( !m_pData->Read( pOutput, size ) )
+ {
+ Warning( "Restore underflow!\n" );
+ Assert(0);
+ }
+}
+
+//-------------------------------------
+
+void CRestore::BufferSkipBytes( int bytes )
+{
+ BufferReadBytes( NULL, bytes );
+}
+
+//-------------------------------------
+
+int CRestore::ReadShort( short *pValue, int nElems, int nBytesAvailable )
+{
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+//-------------------------------------
+
+int CRestore::ReadInt( int *pValue, int nElems, int nBytesAvailable )
+{
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+//-------------------------------------
+
+int CRestore::ReadBool( bool *pValue, int nElems, int nBytesAvailable )
+{
+ COMPILE_TIME_ASSERT( sizeof(bool) == sizeof(char) );
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+//-------------------------------------
+
+int CRestore::ReadFloat( float *pValue, int nElems, int nBytesAvailable )
+{
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+//-------------------------------------
+
+int CRestore::ReadData( char *pData, int size, int nBytesAvailable )
+{
+ return ReadSimple( pData, size, nBytesAvailable );
+}
+
+//-------------------------------------
+
+void CRestore::ReadString( char *pDest, int nSizeDest, int nBytesAvailable )
+{
+ const char *pString = BufferPointer();
+ if ( !nBytesAvailable )
+ nBytesAvailable = strlen( pString ) + 1;
+ BufferSkipBytes( nBytesAvailable );
+
+ Q_strncpy(pDest, pString, nSizeDest );
+}
+
+//-------------------------------------
+
+int CRestore::ReadString( string_t *pValue, int nElems, int nBytesAvailable )
+{
+ AssertMsg( nBytesAvailable > 0, "CRestore::ReadString() implementation does not currently support unspecified bytes available");
+
+ int i;
+ char *pString = BufferPointer();
+ char *pLimit = pString + nBytesAvailable;
+ for ( i = 0; i < nElems && pString < pLimit; i++ )
+ {
+ if ( *((char *)pString) == 0 )
+ pValue[i] = NULL_STRING;
+ else
+ pValue[i] = AllocPooledString( (char *)pString );
+
+ while (*pString)
+ pString++;
+ pString++;
+ }
+
+ BufferSkipBytes( nBytesAvailable );
+
+ return i;
+}
+
+//-------------------------------------
+
+int CRestore::ReadVector( Vector *pValue)
+{
+ BufferReadBytes( (char *)pValue, sizeof(Vector) );
+ return 1;
+}
+
+//-------------------------------------
+
+int CRestore::ReadVector( Vector *pValue, int nElems, int nBytesAvailable )
+{
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+int CRestore::ReadQuaternion( Quaternion *pValue)
+{
+ BufferReadBytes( (char *)pValue, sizeof(Quaternion) );
+ return 1;
+}
+
+//-------------------------------------
+
+int CRestore::ReadQuaternion( Quaternion *pValue, int nElems, int nBytesAvailable )
+{
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+//-------------------------------------
+int CRestore::ReadVMatrix( VMatrix *pValue, int nElems, int nBytesAvailable )
+{
+ return ReadSimple( pValue, nElems, nBytesAvailable );
+}
+
+
+int CRestore::ReadVMatrixWorldspace( VMatrix *pValue, int nElems, int nBytesAvailable )
+{
+ Vector basePosition = m_pGameInfo->GetLandmark();
+ VMatrix tmp;
+
+ for ( int i = 0; i < nElems; i++ )
+ {
+ BufferReadBytes( (char *)&tmp, sizeof(float)*16 );
+
+ VMatrixOffset( pValue[i], tmp, basePosition );
+ }
+ return nElems;
+}
+
+
+int CRestore::ReadMatrix3x4Worldspace( matrix3x4_t *pValue, int nElems, int nBytesAvailable )
+{
+ Vector basePosition = m_pGameInfo->GetLandmark();
+ matrix3x4_t tmp;
+
+ for ( int i = 0; i < nElems; i++ )
+ {
+ BufferReadBytes( (char *)&tmp, sizeof(matrix3x4_t) );
+
+ Matrix3x4Offset( pValue[i], tmp, basePosition );
+ }
+ return nElems;
+}
+
+int CRestore::ReadInterval( interval_t *interval, int count, int nBytesAvailable )
+{
+ return ReadSimple( interval, count, nBytesAvailable );
+}
+
+//---------------------------------------------------------
+//
+// Game centric restore methods
+//
+
+CBaseEntity *CRestore::EntityFromIndex( int entityIndex )
+{
+ if ( !m_pGameInfo || entityIndex < 0 )
+ return NULL;
+
+ int i;
+ entitytable_t *pTable;
+
+ for ( i = 0; i < m_pGameInfo->NumEntities(); i++ )
+ {
+ pTable = m_pGameInfo->GetEntityInfo( i );
+ if ( pTable->id == entityIndex )
+ return pTable->hEnt;
+ }
+ return NULL;
+}
+
+//-------------------------------------
+
+int CRestore::ReadEntityPtr( CBaseEntity **ppEntity, int count, int nBytesAvailable )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+
+ int nRead = ReadInt( entityArray, count, nBytesAvailable );
+
+ for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count
+ {
+ ppEntity[i] = EntityFromIndex( entityArray[i] );
+ }
+
+ if ( nRead < count)
+ {
+ memset( &ppEntity[nRead], 0, ( count - nRead ) * sizeof(ppEntity[0]) );
+ }
+
+ return nRead;
+}
+
+//-------------------------------------
+int CRestore::ReadEdictPtr( edict_t **ppEdict, int count, int nBytesAvailable )
+{
+#if !defined( CLIENT_DLL )
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+ CBaseEntity *pEntity;
+
+ int nRead = ReadInt( entityArray, count, nBytesAvailable );
+
+ for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count
+ {
+ pEntity = EntityFromIndex( entityArray[i] );
+ ppEdict[i] = (pEntity) ? pEntity->edict() : NULL;
+ }
+
+ if ( nRead < count)
+ {
+ memset( &ppEdict[nRead], 0, ( count - nRead ) * sizeof(ppEdict[0]) );
+ }
+
+ return nRead;
+#else
+ return 0;
+#endif
+}
+
+
+//-------------------------------------
+
+int CRestore::ReadEHandle( EHANDLE *pEHandle, int count, int nBytesAvailable )
+{
+ AssertMsg( count <= MAX_ENTITYARRAY, "Array of entities or ehandles exceeds limit supported by save/restore" );
+ int entityArray[MAX_ENTITYARRAY];
+
+ int nRead = ReadInt( entityArray, count, nBytesAvailable );
+
+ for ( int i = 0; i < nRead; i++ ) // nRead is never greater than count
+ {
+ pEHandle[i] = EntityFromIndex( entityArray[i] );
+ }
+
+ if ( nRead < count)
+ {
+ memset( &pEHandle[nRead], 0xFF, ( count - nRead ) * sizeof(pEHandle[0]) );
+ }
+
+ return nRead;
+}
+
+//-------------------------------------
+// Purpose: Reads all the fields that are not client neutral. In the event of
+// a librarization of save/restore, these would NOT reside in the library
+
+void CRestore::ReadGameField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField )
+{
+ switch( pField->fieldType )
+ {
+ case FIELD_POSITION_VECTOR:
+ {
+ ReadPositionVector( (Vector *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_TIME:
+ {
+ ReadTime( (float *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_TICK:
+ {
+ ReadTick( (int *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_FUNCTION:
+ {
+ ReadFunction( pRootMap, (inputfunc_t **)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_MODELINDEX:
+ {
+ int *pModelIndex = (int*)pDest;
+ string_t *pModelName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) );
+ int nRead = ReadString( pModelName, pField->fieldSize, header.size );
+
+ for ( int i = 0; i < nRead; i++ )
+ {
+ if ( pModelName[i] == NULL_STRING )
+ {
+ pModelIndex[i] = -1;
+ continue;
+ }
+
+ pModelIndex[i] = modelinfo->GetModelIndex( STRING( pModelName[i] ) );
+
+#if !defined( CLIENT_DLL )
+ if ( m_precache )
+ {
+ CBaseEntity::PrecacheModel( STRING( pModelName[i] ) );
+ }
+#endif
+ }
+ break;
+ }
+
+ case FIELD_MATERIALINDEX:
+ {
+ int *pMaterialIndex = (int*)pDest;
+ string_t *pMaterialName = (string_t *)stackalloc( pField->fieldSize * sizeof(string_t) );
+ int nRead = ReadString( pMaterialName, pField->fieldSize, header.size );
+
+ for ( int i = 0; i < nRead; i++ )
+ {
+ if ( pMaterialName[i] == NULL_STRING )
+ {
+ pMaterialIndex[i] = 0;
+ continue;
+ }
+
+ pMaterialIndex[i] = GetMaterialIndex( STRING( pMaterialName[i] ) );
+
+#if !defined( CLIENT_DLL )
+ if ( m_precache )
+ {
+ PrecacheMaterial( STRING( pMaterialName[i] ) );
+ }
+#endif
+ }
+ break;
+ }
+
+ case FIELD_MODELNAME:
+ case FIELD_SOUNDNAME:
+ {
+ string_t *pStringDest = (string_t *)pDest;
+ int nRead = ReadString( pStringDest, pField->fieldSize, header.size );
+ if ( m_precache )
+ {
+#if !defined( CLIENT_DLL )
+ // HACKHACK: Rewrite the .bsp models to match the map name in case the bugreporter renamed it
+ if ( pField->fieldType == FIELD_MODELNAME && Q_stristr(pStringDest->ToCStr(), ".bsp") )
+ {
+ char buf[MAX_PATH];
+ Q_strncpy( buf, "maps/", sizeof(buf) );
+ Q_strncat( buf, gpGlobals->mapname.ToCStr(), sizeof(buf) );
+ Q_strncat( buf, ".bsp", sizeof(buf) );
+ *pStringDest = AllocPooledString( buf );
+ }
+#endif
+ for ( int i = 0; i < nRead; i++ )
+ {
+ if ( pStringDest[i] != NULL_STRING )
+ {
+#if !defined( CLIENT_DLL )
+ if ( pField->fieldType == FIELD_MODELNAME )
+ {
+ CBaseEntity::PrecacheModel( STRING( pStringDest[i] ) );
+ }
+ else if ( pField->fieldType == FIELD_SOUNDNAME )
+ {
+ CBaseEntity::PrecacheScriptSound( STRING( pStringDest[i] ) );
+ }
+#endif
+ }
+ }
+ }
+ break;
+ }
+
+ case FIELD_CLASSPTR:
+ ReadEntityPtr( (CBaseEntity **)pDest, pField->fieldSize, header.size );
+ break;
+
+ case FIELD_EDICT:
+#if !defined( CLIENT_DLL )
+ ReadEdictPtr( (edict_t **)pDest, pField->fieldSize, header.size );
+#else
+ Assert( !"FIELD_EDICT not valid for client .dll" );
+#endif
+ break;
+ case FIELD_EHANDLE:
+ ReadEHandle( (EHANDLE *)pDest, pField->fieldSize, header.size );
+ break;
+
+ case FIELD_VMATRIX:
+ {
+ ReadVMatrix( (VMatrix *)pDest, pField->fieldSize, header.size );
+ break;
+ }
+
+ case FIELD_VMATRIX_WORLDSPACE:
+ ReadVMatrixWorldspace( (VMatrix *)pDest, pField->fieldSize, header.size );
+ break;
+
+ case FIELD_MATRIX3X4_WORLDSPACE:
+ ReadMatrix3x4Worldspace( (matrix3x4_t *)pDest, pField->fieldSize, header.size );
+ break;
+
+ case FIELD_INTERVAL:
+ ReadInterval( (interval_t *)pDest, pField->fieldSize, header.size );
+ break;
+
+ default:
+ Warning( "Bad field type\n" );
+ Assert(0);
+ }
+}
+
+//-------------------------------------
+
+int CRestore::ReadTime( float *pValue, int count, int nBytesAvailable )
+{
+ float baseTime = m_pGameInfo->GetBaseTime();
+ int nRead = ReadFloat( pValue, count, nBytesAvailable );
+
+ for ( int i = nRead - 1; i >= 0; i-- )
+ {
+ if ( pValue[i] == ZERO_TIME )
+ pValue[i] = 0.0;
+ else if ( pValue[i] != INVALID_TIME && pValue[i] != FLT_MAX )
+ pValue[i] += baseTime;
+ }
+
+ return nRead;
+}
+
+int CRestore::ReadTick( int *pValue, int count, int nBytesAvailable )
+{
+ // HACK HACK: Adding 0.1f here makes sure that all tick times read
+ // from .sav file which are near the basetime will end up just ahead of
+ // the base time, because we are restoring we'll have a slow frame of the
+ // max frametime of 0.1 seconds and that could otherwise cause all of our
+ // think times to get synchronized to each other... sigh. ywb...
+ int baseTick = TIME_TO_TICKS( m_pGameInfo->GetBaseTime() + 0.1f );
+ int nRead = ReadInt( pValue, count, nBytesAvailable );
+
+ for ( int i = nRead - 1; i >= 0; i-- )
+ {
+ if ( pValue[ i ] != TICK_NEVER_THINK_ENCODE )
+ {
+ // Rebase it
+ pValue[i] += baseTick;
+ }
+ else
+ {
+ // Slam to -1 value
+ pValue[ i ] = TICK_NEVER_THINK;
+ }
+ }
+
+ return nRead;
+}
+
+//-------------------------------------
+
+int CRestore::ReadPositionVector( Vector *pValue )
+{
+ return ReadPositionVector( pValue, 1, sizeof(Vector) );
+}
+
+//-------------------------------------
+
+int CRestore::ReadPositionVector( Vector *pValue, int count, int nBytesAvailable )
+{
+ Vector basePosition = m_pGameInfo->GetLandmark();
+ int nRead = ReadVector( pValue, count, nBytesAvailable );
+
+ for ( int i = nRead - 1; i >= 0; i-- )
+ {
+ if ( pValue[i] != vec3_invalid )
+ pValue[i] += basePosition;
+ }
+
+ return nRead;
+}
+
+//-------------------------------------
+
+int CRestore::ReadFunction( datamap_t *pMap, inputfunc_t **pValue, int count, int nBytesAvailable )
+{
+ AssertMsg( nBytesAvailable > 0, "CRestore::ReadFunction() implementation does not currently support unspecified bytes available");
+
+ char *pszFunctionName = BufferPointer();
+ BufferSkipBytes( nBytesAvailable );
+
+ AssertMsg( count == 1, "Arrays of functions not presently supported" );
+
+ if ( *pszFunctionName == 0 )
+ *pValue = NULL;
+ else
+ *pValue = UTIL_FunctionFromName( pMap, pszFunctionName );
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Entity data saving routines
+//
+//-----------------------------------------------------------------------------
+
+BEGIN_SIMPLE_DATADESC(entitytable_t)
+ DEFINE_FIELD( id, FIELD_INTEGER ),
+ DEFINE_FIELD( edictindex, FIELD_INTEGER ),
+ DEFINE_FIELD( saveentityindex, FIELD_INTEGER ),
+// DEFINE_FIELD( restoreentityindex, FIELD_INTEGER ),
+ // hEnt (not saved, this is the fixup)
+ DEFINE_FIELD( location, FIELD_INTEGER ),
+ DEFINE_FIELD( size, FIELD_INTEGER ),
+ DEFINE_FIELD( flags, FIELD_INTEGER ),
+ DEFINE_FIELD( classname, FIELD_STRING ),
+ DEFINE_FIELD( globalname, FIELD_STRING ),
+ DEFINE_FIELD( landmarkModelSpace, FIELD_VECTOR ),
+ DEFINE_FIELD( modelname, FIELD_STRING ),
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Utilities entities can use when saving
+//-----------------------------------------------------------------------------
+class CEntitySaveUtils : public IEntitySaveUtils
+{
+public:
+ // Call these in pre-save + post save
+ void PreSave();
+ void PostSave();
+
+ // Methods of IEntitySaveUtils
+ virtual void AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 );
+ virtual int GetEntityDependencyCount( CBaseEntity *pEntity );
+ virtual int GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList );
+
+private:
+ IPhysicsObjectPairHash *m_pLevelAdjacencyDependencyHash;
+};
+
+
+//-----------------------------------------------------------------------------
+// Call these in pre-save + post save
+//-----------------------------------------------------------------------------
+void CEntitySaveUtils::PreSave()
+{
+ Assert( !m_pLevelAdjacencyDependencyHash );
+ MEM_ALLOC_CREDIT();
+ m_pLevelAdjacencyDependencyHash = physics->CreateObjectPairHash();
+}
+
+void CEntitySaveUtils::PostSave()
+{
+ physics->DestroyObjectPairHash( m_pLevelAdjacencyDependencyHash );
+ m_pLevelAdjacencyDependencyHash = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the # of dependencies for a particular entity
+//-----------------------------------------------------------------------------
+int CEntitySaveUtils::GetEntityDependencyCount( CBaseEntity *pEntity )
+{
+ return m_pLevelAdjacencyDependencyHash->GetPairCountForObject( pEntity );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets all dependencies for a particular entity
+//-----------------------------------------------------------------------------
+int CEntitySaveUtils::GetEntityDependencies( CBaseEntity *pEntity, int nCount, CBaseEntity **ppEntList )
+{
+ return m_pLevelAdjacencyDependencyHash->GetPairListForObject( pEntity, nCount, (void**)ppEntList );
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods of IEntitySaveUtils
+//-----------------------------------------------------------------------------
+void CEntitySaveUtils::AddLevelTransitionSaveDependency( CBaseEntity *pEntity1, CBaseEntity *pEntity2 )
+{
+ if ( pEntity1 != pEntity2 )
+ {
+ m_pLevelAdjacencyDependencyHash->AddObjectPair( pEntity1, pEntity2 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Block handler for save/restore of entities
+//-----------------------------------------------------------------------------
+class CEntitySaveRestoreBlockHandler : public ISaveRestoreBlockHandler
+{
+public:
+ const char *GetBlockName();
+ void PreSave( CSaveRestoreData *pSaveData );
+ void Save( ISave *pSave );
+ void WriteSaveHeaders( ISave *pSave );
+ virtual void PostSave();
+ virtual void PreRestore();
+ void ReadRestoreHeaders( IRestore *pRestore );
+
+ void Restore( IRestore *pRestore, bool createPlayers );
+ virtual void PostRestore();
+
+ inline IEntitySaveUtils * GetEntitySaveUtils() { return &m_EntitySaveUtils; }
+
+private:
+ friend int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask );
+ bool SaveInitEntities( CSaveRestoreData *pSaveData );
+ bool DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore );
+ Vector ModelSpaceLandmark( int modelIndex );
+ int RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo );
+
+#if !defined( CLIENT_DLL )
+ // Find the matching global entity. Spit out an error if the designer made entities of
+ // different classes with the same global name
+ CBaseEntity *FindGlobalEntity( string_t classname, string_t globalname );
+
+ int RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo );
+#endif
+
+private:
+ CEntitySaveUtils m_EntitySaveUtils;
+};
+
+
+//-----------------------------------------------------------------------------
+
+CEntitySaveRestoreBlockHandler g_EntitySaveRestoreBlockHandler;
+
+//-------------------------------------
+
+ISaveRestoreBlockHandler *GetEntitySaveRestoreBlockHandler()
+{
+ return &g_EntitySaveRestoreBlockHandler;
+}
+
+IEntitySaveUtils *GetEntitySaveUtils()
+{
+ return g_EntitySaveRestoreBlockHandler.GetEntitySaveUtils();
+}
+
+
+//-----------------------------------------------------------------------------
+// Implementation of the block handler for save/restore of entities
+//-----------------------------------------------------------------------------
+const char *CEntitySaveRestoreBlockHandler::GetBlockName()
+{
+ return "Entities";
+}
+
+//---------------------------------
+
+void CEntitySaveRestoreBlockHandler::PreSave( CSaveRestoreData *pSaveData )
+{
+ MDLCACHE_CRITICAL_SECTION();
+ IGameSystem::OnSaveAllSystems();
+
+ m_EntitySaveUtils.PreSave();
+
+ // Allow the entities to do some work
+ CBaseEntity *pEnt = NULL;
+#if !defined( CLIENT_DLL )
+ while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL )
+ {
+ pEnt->OnSave( &m_EntitySaveUtils );
+ }
+#else
+ // Do this because it'll force entities to figure out their origins, and that requires
+ // SetupBones in the case of aiments.
+ {
+ C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
+
+ int last = ClientEntityList().GetHighestEntityIndex();
+ ClientEntityHandle_t iter = ClientEntityList().FirstHandle();
+
+ for ( int e = 0; e <= last; e++ )
+ {
+ pEnt = ClientEntityList().GetBaseEntity( e );
+
+ if( !pEnt )
+ continue;
+
+ pEnt->OnSave();
+ }
+
+ while ( iter != ClientEntityList().InvalidHandle() )
+ {
+ pEnt = ClientEntityList().GetBaseEntityFromHandle( iter );
+
+ if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE )
+ {
+ pEnt->OnSave();
+ }
+
+ iter = ClientEntityList().NextHandle( iter );
+ }
+ }
+#endif
+ SaveInitEntities( pSaveData );
+}
+
+//---------------------------------
+
+void CEntitySaveRestoreBlockHandler::Save( ISave *pSave )
+{
+ CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo();
+
+ // write entity list that was previously built by SaveInitEntities()
+ for ( int i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i );
+ pEntInfo->location = pSave->GetWritePos();
+ pEntInfo->size = 0;
+
+ CBaseEntity *pEnt = pEntInfo->hEnt;
+ if ( pEnt && !( pEnt->ObjectCaps() & FCAP_DONT_SAVE ) )
+ {
+ MDLCACHE_CRITICAL_SECTION();
+#if !defined( CLIENT_DLL )
+ AssertMsg( !pEnt->edict() || ( pEnt->m_iClassname != NULL_STRING &&
+ (STRING(pEnt->m_iClassname)[0] != 0) &&
+ FStrEq( STRING(pEnt->m_iClassname), pEnt->GetClassname()) ),
+ "Saving entity with invalid classname" );
+#endif
+
+ pSaveData->SetCurrentEntityContext( pEnt );
+ pEnt->Save( *pSave );
+ pSaveData->SetCurrentEntityContext( NULL );
+
+ pEntInfo->size = pSave->GetWritePos() - pEntInfo->location; // Size of entity block is data size written to block
+
+ pEntInfo->classname = pEnt->m_iClassname; // Remember entity class for respawn
+
+#if !defined( CLIENT_DLL )
+ pEntInfo->globalname = pEnt->m_iGlobalname; // remember global name
+ pEntInfo->landmarkModelSpace = ModelSpaceLandmark( pEnt->GetModelIndex() );
+ int nEntIndex = pEnt->edict() ? ENTINDEX(pEnt->edict()) : -1;
+ bool bIsPlayer = ( ( nEntIndex >= 1 ) && ( nEntIndex <= gpGlobals->maxClients ) ) ? true : false;
+ if ( bIsPlayer )
+ {
+ pEntInfo->flags |= FENTTABLE_PLAYER;
+ }
+#endif
+ }
+ }
+}
+
+//---------------------------------
+
+void CEntitySaveRestoreBlockHandler::WriteSaveHeaders( ISave *pSave )
+{
+ CGameSaveRestoreInfo *pSaveData = pSave->GetGameSaveRestoreInfo();
+
+ int nEntities = pSaveData->NumEntities();
+ pSave->WriteInt( &nEntities );
+
+ for ( int i = 0; i < pSaveData->NumEntities(); i++ )
+ pSave->WriteFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields );
+}
+
+//---------------------------------
+
+void CEntitySaveRestoreBlockHandler::PostSave()
+{
+ m_EntitySaveUtils.PostSave();
+}
+
+//---------------------------------
+
+void CEntitySaveRestoreBlockHandler::PreRestore()
+{
+}
+
+//---------------------------------
+
+void CEntitySaveRestoreBlockHandler::ReadRestoreHeaders( IRestore *pRestore )
+{
+ CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
+
+ int nEntities;
+ pRestore->ReadInt( &nEntities );
+
+ entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * nEntities), sizeof(char) );
+ if ( !pEntityTable )
+ {
+ return;
+ }
+
+ pSaveData->InitEntityTable( pEntityTable, nEntities );
+
+ for ( int i = 0; i < pSaveData->NumEntities(); i++ )
+ pRestore->ReadFields( "ETABLE", pSaveData->GetEntityInfo( i ), NULL, entitytable_t::m_DataMap.dataDesc, entitytable_t::m_DataMap.dataNumFields );
+
+}
+
+//---------------------------------
+
+#if !defined( CLIENT_DLL )
+
+void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers )
+{
+ entitytable_t *pEntInfo;
+ CBaseEntity *pent;
+
+ CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
+
+ bool restoredWorld = false;
+
+ // Create entity list
+ int i;
+ for ( i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ pEntInfo = pSaveData->GetEntityInfo( i );
+
+ if ( pEntInfo->classname != NULL_STRING && pEntInfo->size && !(pEntInfo->flags & FENTTABLE_REMOVED) )
+ {
+ if ( pEntInfo->edictindex == 0 ) // worldspawn
+ {
+ Assert( i == 0 );
+ pent = CreateEntityByName( STRING(pEntInfo->classname) );
+ pRestore->SetReadPos( pEntInfo->location );
+ if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 )
+ {
+ pEntInfo->hEnt = NULL;
+ pEntInfo->restoreentityindex = -1;
+ UTIL_RemoveImmediate( pent );
+ }
+ else
+ {
+ // force the entity to be relinked
+ AddRestoredEntity( pent );
+ }
+ }
+ else if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) )
+ {
+ if ( !(pEntInfo->flags & FENTTABLE_PLAYER) )
+ {
+ Warning( "ENTITY IS NOT A PLAYER: %d\n" , i );
+ Assert(0);
+ }
+
+ edict_t *ed = INDEXENT( pEntInfo->edictindex );
+
+ if ( ed && createPlayers )
+ {
+ // create the player
+ pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed );
+ }
+ else
+ pent = NULL;
+ }
+ else
+ {
+ pent = CreateEntityByName( STRING(pEntInfo->classname) );
+ }
+ pEntInfo->hEnt = pent;
+ pEntInfo->restoreentityindex = pent ? pent->entindex() : - 1;
+ if ( pent && pEntInfo->restoreentityindex == 0 )
+ {
+ if ( !FClassnameIs( pent, "worldspawn" ) )
+ {
+ pEntInfo->restoreentityindex = -1;
+ }
+ }
+
+ if ( pEntInfo->restoreentityindex == 0 )
+ {
+ Assert( !restoredWorld );
+ restoredWorld = true;
+ }
+ }
+ else
+ {
+ pEntInfo->hEnt = NULL;
+ pEntInfo->restoreentityindex = -1;
+ }
+ }
+
+ // Now spawn entities
+ for ( i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ pEntInfo = pSaveData->GetEntityInfo( i );
+ if ( pEntInfo->edictindex != 0 )
+ {
+ pent = pEntInfo->hEnt;
+ pRestore->SetReadPos( pEntInfo->location );
+ if ( pent )
+ {
+ if ( RestoreEntity( pent, pRestore, pEntInfo ) < 0 )
+ {
+ pEntInfo->hEnt = NULL;
+ pEntInfo->restoreentityindex = -1;
+ UTIL_RemoveImmediate( pent );
+ }
+ else
+ {
+ AddRestoredEntity( pent );
+ }
+ }
+ }
+ }
+}
+
+#else // CLIENT DLL VERSION
+
+void CEntitySaveRestoreBlockHandler::Restore( IRestore *pRestore, bool createPlayers )
+{
+ entitytable_t *pEntInfo;
+ CBaseEntity *pent;
+
+ CGameSaveRestoreInfo *pSaveData = pRestore->GetGameSaveRestoreInfo();
+
+ // Create entity list
+ int i;
+ bool restoredWorld = false;
+
+ for ( i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ pEntInfo = pSaveData->GetEntityInfo( i );
+ pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex );
+ pEntInfo->hEnt = pent;
+ }
+
+ // Blast saved data into entities
+ for ( i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ pEntInfo = pSaveData->GetEntityInfo( i );
+
+ bool bRestoredCorrectly = false;
+ // FIXME, need to translate save spot to real index here using lookup table transmitted from server
+ //Assert( !"Need translation still" );
+ if ( pEntInfo->restoreentityindex >= 0 )
+ {
+ if ( pEntInfo->restoreentityindex == 0 )
+ {
+ Assert( !restoredWorld );
+ restoredWorld = true;
+ }
+
+ pent = ClientEntityList().GetBaseEntity( pEntInfo->restoreentityindex );
+ pRestore->SetReadPos( pEntInfo->location );
+ if ( pent )
+ {
+ if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 )
+ {
+ // Call the OnRestore method
+ AddRestoredEntity( pent );
+ bRestoredCorrectly = true;
+ }
+ }
+ }
+ // BUGBUG: JAY: Disable ragdolls across transitions until PVS/solid check & client entity patch file are implemented
+ else if ( !pSaveData->levelInfo.fUseLandmark )
+ {
+ if ( pEntInfo->classname != NULL_STRING )
+ {
+ pent = CreateEntityByName( STRING(pEntInfo->classname) );
+ pent->InitializeAsClientEntity( NULL, RENDER_GROUP_OPAQUE_ENTITY );
+
+ pRestore->SetReadPos( pEntInfo->location );
+
+ if ( pent )
+ {
+ if ( RestoreEntity( pent, pRestore, pEntInfo ) >= 0 )
+ {
+ pEntInfo->hEnt = pent;
+ AddRestoredEntity( pent );
+ bRestoredCorrectly = true;
+ }
+ }
+ }
+ }
+
+ if ( !bRestoredCorrectly )
+ {
+ pEntInfo->hEnt = NULL;
+ pEntInfo->restoreentityindex = -1;
+ }
+ }
+
+ // Note, server does this after local player connects fully
+ IGameSystem::OnRestoreAllSystems();
+
+ // Tell hud elements to modify behavior based on game restoration, if applicable
+ gHUD.OnRestore();
+}
+#endif
+
+void CEntitySaveRestoreBlockHandler::PostRestore()
+{
+}
+
+void SaveEntityOnTable( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, int &iSlot )
+{
+ entitytable_t *pEntInfo = pSaveData->GetEntityInfo( iSlot );
+ pEntInfo->id = iSlot;
+#if !defined( CLIENT_DLL )
+ pEntInfo->edictindex = pEntity->RequiredEdictIndex();
+#else
+ pEntInfo->edictindex = -1;
+#endif
+ pEntInfo->modelname = pEntity->GetModelName();
+ pEntInfo->restoreentityindex = -1;
+ pEntInfo->saveentityindex = pEntity ? pEntity->entindex() : -1;
+ pEntInfo->hEnt = pEntity;
+ pEntInfo->flags = 0;
+ pEntInfo->location = 0;
+ pEntInfo->size = 0;
+ pEntInfo->classname = NULL_STRING;
+
+ iSlot++;
+}
+
+
+//---------------------------------
+
+bool CEntitySaveRestoreBlockHandler::SaveInitEntities( CSaveRestoreData *pSaveData )
+{
+ int number_of_entities;
+
+#if !defined( CLIENT_DLL )
+ number_of_entities = gEntList.NumberOfEntities();
+#else
+ number_of_entities = ClientEntityList().NumberOfEntities( true );
+#endif
+ entitytable_t *pEntityTable = ( entitytable_t *)engine->SaveAllocMemory( (sizeof(entitytable_t) * number_of_entities), sizeof(char) );
+ if ( !pEntityTable )
+ return false;
+
+ pSaveData->InitEntityTable( pEntityTable, number_of_entities );
+
+ // build the table of entities
+ // this is used to turn pointers into savable indices
+ // build up ID numbers for each entity, for use in pointer conversions
+ // if an entity requires a certain edict number upon restore, save that as well
+ CBaseEntity *pEnt = NULL;
+ int i = 0;
+
+#if !defined( CLIENT_DLL )
+ while ( (pEnt = gEntList.NextEnt( pEnt )) != NULL )
+ {
+#else
+ int last = ClientEntityList().GetHighestEntityIndex();
+
+ for ( int e = 0; e <= last; e++ )
+ {
+ pEnt = ClientEntityList().GetBaseEntity( e );
+ if( !pEnt )
+ continue;
+#endif
+ SaveEntityOnTable( pEnt, pSaveData, i );
+ }
+
+#if defined( CLIENT_DLL )
+ ClientEntityHandle_t iter = ClientEntityList().FirstHandle();
+
+ while ( iter != ClientEntityList().InvalidHandle() )
+ {
+ pEnt = ClientEntityList().GetBaseEntityFromHandle( iter );
+
+ if ( pEnt && pEnt->ObjectCaps() & FCAP_SAVE_NON_NETWORKABLE )
+ {
+ SaveEntityOnTable( pEnt, pSaveData, i );
+ }
+
+ iter = ClientEntityList().NextHandle( iter );
+ }
+#endif
+
+ pSaveData->BuildEntityHash();
+
+ Assert( i == pSaveData->NumEntities() );
+ return ( i == pSaveData->NumEntities() );
+}
+
+//---------------------------------
+
+#if !defined( CLIENT_DLL )
+
+// Find the matching global entity. Spit out an error if the designer made entities of
+// different classes with the same global name
+CBaseEntity *CEntitySaveRestoreBlockHandler::FindGlobalEntity( string_t classname, string_t globalname )
+{
+ CBaseEntity *pReturn = NULL;
+
+ while ( (pReturn = gEntList.NextEnt( pReturn )) != NULL )
+ {
+ if ( FStrEq( STRING(pReturn->m_iGlobalname), STRING(globalname)) )
+ break;
+ }
+
+ if ( pReturn )
+ {
+ if ( !FClassnameIs( pReturn, STRING(classname) ) )
+ {
+ Warning( "Global entity found %s, wrong class %s [expects class %s]\n", STRING(globalname), STRING(pReturn->m_iClassname), STRING(classname) );
+ pReturn = NULL;
+ }
+ }
+
+ return pReturn;
+}
+
+#endif // !defined( CLIENT_DLL )
+
+//---------------------------------
+
+bool CEntitySaveRestoreBlockHandler::DoRestoreEntity( CBaseEntity *pEntity, IRestore *pRestore )
+{
+ MDLCACHE_CRITICAL_SECTION();
+
+ EHANDLE hEntity;
+
+ hEntity = pEntity;
+
+ pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( pEntity );
+ pEntity->Restore( *pRestore );
+ pRestore->GetGameSaveRestoreInfo()->SetCurrentEntityContext( NULL );
+
+#if !defined( CLIENT_DLL )
+ if ( pEntity->ObjectCaps() & FCAP_MUST_SPAWN )
+ {
+ pEntity->Spawn();
+ }
+ else
+ {
+ pEntity->Precache( );
+ }
+#endif
+
+ // Above calls may have resulted in self destruction
+ return ( hEntity != NULL );
+}
+
+//---------------------------------
+// Get a reference position in model space to compute
+// changes in model space for global brush entities (designer models them in different coords!)
+Vector CEntitySaveRestoreBlockHandler::ModelSpaceLandmark( int modelIndex )
+{
+ const model_t *pModel = modelinfo->GetModel( modelIndex );
+ if ( modelinfo->GetModelType( pModel ) != mod_brush )
+ return vec3_origin;
+
+ Vector mins, maxs;
+ modelinfo->GetModelBounds( pModel, mins, maxs );
+ return mins;
+}
+
+
+int CEntitySaveRestoreBlockHandler::RestoreEntity( CBaseEntity *pEntity, IRestore *pRestore, entitytable_t *pEntInfo )
+{
+ if ( !DoRestoreEntity( pEntity, pRestore ) )
+ return 0;
+
+#if !defined( CLIENT_DLL )
+ if ( pEntity->m_iGlobalname != NULL_STRING )
+ {
+ int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname );
+ if ( globalIndex >= 0 )
+ {
+ // Already dead? delete
+ if ( GlobalEntity_GetState( globalIndex ) == GLOBAL_DEAD )
+ return -1;
+ else if ( !FStrEq( STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex) ) )
+ {
+ pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
+ }
+ // In this level & not dead, continue on as normal
+ }
+ else
+ {
+ Warning( "Global Entity %s (%s) not in table!!!\n", STRING(pEntity->m_iGlobalname), STRING(pEntity->m_iClassname) );
+ // Spawned entities default to 'On'
+ GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON );
+ }
+ }
+#endif
+
+ return 0;
+}
+
+//---------------------------------
+
+#if !defined( CLIENT_DLL )
+
+int CEntitySaveRestoreBlockHandler::RestoreGlobalEntity( CBaseEntity *pEntity, CSaveRestoreData *pSaveData, entitytable_t *pEntInfo )
+{
+ Vector oldOffset;
+ EHANDLE hEntitySafeHandle;
+ hEntitySafeHandle = pEntity;
+
+ oldOffset.Init();
+ CRestore restoreHelper( pSaveData );
+
+ string_t globalName = pEntInfo->globalname, className = pEntInfo->classname;
+
+ // -------------------
+
+ int globalIndex = GlobalEntity_GetIndex( globalName );
+
+ // Don't overlay any instance of the global that isn't the latest
+ // pSaveData->szCurrentMapName is the level this entity is coming from
+ // pGlobal->levelName is the last level the global entity was active in.
+ // If they aren't the same, then this global update is out of date.
+ if ( !FStrEq( pSaveData->levelInfo.szCurrentMapName, GlobalEntity_GetMap(globalIndex) ) )
+ {
+ return 0;
+ }
+
+ // Compute the new global offset
+ CBaseEntity *pNewEntity = FindGlobalEntity( className, globalName );
+ if ( pNewEntity )
+ {
+// Msg( "Overlay %s with %s\n", pNewEntity->GetClassname(), STRING(tmpEnt->classname) );
+ // Tell the restore code we're overlaying a global entity from another level
+ restoreHelper.SetGlobalMode( 1 ); // Don't overwrite global fields
+
+ pSaveData->modelSpaceOffset = pEntInfo->landmarkModelSpace - ModelSpaceLandmark( pNewEntity->GetModelIndex() );
+
+ UTIL_Remove( pEntity );
+ pEntity = pNewEntity;// we're going to restore this data OVER the old entity
+ pEntInfo->hEnt = pEntity;
+ // HACKHACK: Do we need system-wide support for removing non-global spawn allocated resources?
+ pEntity->VPhysicsDestroyObject();
+ Assert( pEntInfo->edictindex == -1 );
+ // Update the global table to say that the global definition of this entity should come from this level
+ GlobalEntity_SetMap( globalIndex, gpGlobals->mapname );
+ }
+ else
+ {
+ // This entity will be freed automatically by the engine-> If we don't do a restore on a matching entity (below)
+ // or call EntityUpdate() to move it to this level, we haven't changed global state at all.
+ DevMsg( "Warning: No match for global entity %s found in destination level\n", STRING(globalName) );
+ return 0;
+ }
+
+ if ( !DoRestoreEntity( pEntity, &restoreHelper ) )
+ {
+ pEntity = NULL;
+ }
+
+ // Is this an overriding global entity (coming over the transition)
+ pSaveData->modelSpaceOffset.Init();
+ if ( pEntity )
+ return 1;
+ return 0;
+}
+
+#endif // !defined( CLIENT_DLL )
+
+
+
+//-----------------------------------------------------------------------------
+
+CSaveRestoreData *SaveInit( int size )
+{
+ CSaveRestoreData *pSaveData;
+
+#if ( defined( CLIENT_DLL ) || defined( DISABLE_DEBUG_HISTORY ) )
+ if ( size <= 0 )
+ size = 2*1024*1024; // Reserve 2048K for now, UNDONE: Shrink this after compressing strings
+#else
+ if ( size <= 0 )
+ size = 3*1024*1024; // Reserve 3096K for now, UNDONE: Shrink this after compressing strings
+#endif
+
+ int numentities;
+
+#if !defined( CLIENT_DLL )
+ numentities = gEntList.NumberOfEntities();
+#else
+ numentities = ClientEntityList().NumberOfEntities();
+#endif
+
+ void *pSaveMemory = engine->SaveAllocMemory( sizeof(CSaveRestoreData) + (sizeof(entitytable_t) * numentities) + size, sizeof(char) );
+ if ( !pSaveMemory )
+ {
+ return NULL;
+ }
+
+ pSaveData = MakeSaveRestoreData( pSaveMemory );
+ pSaveData->Init( (char *)(pSaveData + 1), size ); // skip the save structure
+
+ const int nTokens = 0xfff; // Assume a maximum of 4K-1 symbol table entries(each of some length)
+ pSaveMemory = engine->SaveAllocMemory( nTokens, sizeof( char * ) );
+ if ( !pSaveMemory )
+ {
+ engine->SaveFreeMemory( pSaveMemory );
+ return NULL;
+ }
+
+ pSaveData->InitSymbolTable( (char **)pSaveMemory, nTokens );
+
+ //---------------------------------
+
+ pSaveData->levelInfo.time = gpGlobals->curtime; // Use DLL time
+ pSaveData->levelInfo.vecLandmarkOffset = vec3_origin;
+ pSaveData->levelInfo.fUseLandmark = false;
+ pSaveData->levelInfo.connectionCount = 0;
+
+ //---------------------------------
+
+ gpGlobals->pSaveData = pSaveData;
+
+ return pSaveData;
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// ISaveRestoreBlockSet
+//
+// Purpose: Serves as holder for a group of sibling save sections. Takes
+// care of iterating over them, making sure read points are
+// queued up to the right spot (in case one section due to datadesc
+// changes reads less than expected, or doesn't leave the
+// read pointer at the right point), and ensuring the read pointer
+// is at the end of the entire set when the set read is done.
+//-----------------------------------------------------------------------------
+
+struct SaveRestoreBlockHeader_t
+{
+ char szName[MAX_BLOCK_NAME_LEN + 1];
+ int locHeader;
+ int locBody;
+
+ DECLARE_SIMPLE_DATADESC();
+};
+
+
+//-------------------------------------
+
+class CSaveRestoreBlockSet : public ISaveRestoreBlockSet
+{
+public:
+ CSaveRestoreBlockSet( const char *pszName )
+ {
+ Q_strncpy( m_Name, pszName, sizeof(m_Name) );
+ }
+
+ const char *GetBlockName()
+ {
+ return m_Name;
+ }
+
+ //---------------------------------
+
+ void PreSave( CSaveRestoreData *pData )
+ {
+ m_BlockHeaders.SetCount( m_Handlers.Count() );
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ Q_strncpy( m_BlockHeaders[i].szName, m_Handlers[i]->GetBlockName(), MAX_BLOCK_NAME_LEN + 1 );
+ m_Handlers[i]->PreSave( pData );
+ }
+ }
+
+ void Save( ISave *pSave )
+ {
+ int base = pSave->GetWritePos();
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ m_BlockHeaders[i].locBody = pSave->GetWritePos() - base;
+ m_Handlers[i]->Save( pSave );
+ }
+ m_SizeBodies = pSave->GetWritePos() - base;
+ }
+
+ void WriteSaveHeaders( ISave *pSave )
+ {
+ int base = pSave->GetWritePos();
+
+ //
+ // Reserve space for a fully populated header
+ //
+ int dummyInt = -1;
+ CUtlVector<SaveRestoreBlockHeader_t> dummyArr;
+
+ dummyArr.SetCount( m_BlockHeaders.Count() );
+ memset( &dummyArr[0], 0xff, dummyArr.Count() * sizeof(SaveRestoreBlockHeader_t) );
+
+ pSave->WriteInt( &dummyInt ); // size all headers
+ pSave->WriteInt( &dummyInt ); // size all bodies
+ SaveUtlVector( pSave, &dummyArr, FIELD_EMBEDDED );
+
+ //
+ // Write the data
+ //
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ m_BlockHeaders[i].locHeader = pSave->GetWritePos() - base;
+ m_Handlers[i]->WriteSaveHeaders( pSave );
+ }
+
+ m_SizeHeaders = pSave->GetWritePos() - base;
+
+ //
+ // Write the actual header
+ //
+ int savedPos = pSave->GetWritePos();
+ pSave->SetWritePos(base);
+
+ pSave->WriteInt( &m_SizeHeaders );
+ pSave->WriteInt( &m_SizeBodies );
+ SaveUtlVector( pSave, &m_BlockHeaders, FIELD_EMBEDDED );
+
+ pSave->SetWritePos(savedPos);
+ }
+
+ void PostSave()
+ {
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ m_Handlers[i]->PostSave();
+ }
+ m_BlockHeaders.Purge();
+ }
+
+ //---------------------------------
+
+ void PreRestore()
+ {
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ m_Handlers[i]->PreRestore();
+ }
+ }
+
+ void ReadRestoreHeaders( IRestore *pRestore )
+ {
+ int base = pRestore->GetReadPos();
+
+ pRestore->ReadInt( &m_SizeHeaders );
+ pRestore->ReadInt( &m_SizeBodies );
+ RestoreUtlVector( pRestore, &m_BlockHeaders, FIELD_EMBEDDED );
+
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ int location = GetBlockHeaderLoc( m_Handlers[i]->GetBlockName() );
+ if ( location != -1 )
+ {
+ pRestore->SetReadPos( base + location );
+ m_Handlers[i]->ReadRestoreHeaders( pRestore );
+ }
+ }
+
+ pRestore->SetReadPos( base + m_SizeHeaders );
+ }
+
+ void CallBlockHandlerRestore( ISaveRestoreBlockHandler *pHandler, int baseFilePos, IRestore *pRestore, bool fCreatePlayers )
+ {
+ int location = GetBlockBodyLoc( pHandler->GetBlockName() );
+ if ( location != -1 )
+ {
+ pRestore->SetReadPos( baseFilePos + location );
+ pHandler->Restore( pRestore, fCreatePlayers );
+ }
+ }
+
+ void Restore( IRestore *pRestore, bool fCreatePlayers )
+ {
+ int base = pRestore->GetReadPos();
+
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ CallBlockHandlerRestore( m_Handlers[i], base, pRestore, fCreatePlayers );
+ }
+ pRestore->SetReadPos( base + m_SizeBodies );
+ }
+
+ void PostRestore()
+ {
+ for ( int i = 0; i < m_Handlers.Count(); i++ )
+ {
+ m_Handlers[i]->PostRestore();
+ }
+ m_BlockHeaders.Purge();
+ }
+
+ //---------------------------------
+
+ void AddBlockHandler( ISaveRestoreBlockHandler *pHandler )
+ {
+ // Grody, but... while this class is still isolated in saverestore.cpp, this seems like a fine time to assert:
+ AssertMsg( pHandler == &g_EntitySaveRestoreBlockHandler || (m_Handlers.Count() >= 1 && m_Handlers[0] == &g_EntitySaveRestoreBlockHandler), "Expected entity save load to always be first" );
+
+ Assert( pHandler != this );
+ m_Handlers.AddToTail( pHandler );
+ }
+
+ void RemoveBlockHandler( ISaveRestoreBlockHandler *pHandler )
+ {
+ m_Handlers.FindAndRemove( pHandler );
+ }
+
+ //---------------------------------
+
+private:
+ int GetBlockBodyLoc( const char *pszName )
+ {
+ for ( int i = 0; i < m_BlockHeaders.Count(); i++ )
+ {
+ if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 )
+ return m_BlockHeaders[i].locBody;
+ }
+ return -1;
+ }
+
+ int GetBlockHeaderLoc( const char *pszName )
+ {
+ for ( int i = 0; i < m_BlockHeaders.Count(); i++ )
+ {
+ if ( strcmp( m_BlockHeaders[i].szName, pszName ) == 0 )
+ return m_BlockHeaders[i].locHeader;
+ }
+ return -1;
+ }
+
+ char m_Name[MAX_BLOCK_NAME_LEN + 1];
+ CUtlVector<ISaveRestoreBlockHandler *> m_Handlers;
+
+ int m_SizeHeaders;
+ int m_SizeBodies;
+ CUtlVector<SaveRestoreBlockHeader_t> m_BlockHeaders;
+};
+
+//-------------------------------------
+
+BEGIN_SIMPLE_DATADESC( SaveRestoreBlockHeader_t )
+ DEFINE_ARRAY(szName, FIELD_CHARACTER, MAX_BLOCK_NAME_LEN + 1),
+ DEFINE_FIELD(locHeader, FIELD_INTEGER),
+ DEFINE_FIELD(locBody, FIELD_INTEGER),
+END_DATADESC()
+
+//-------------------------------------
+
+CSaveRestoreBlockSet g_SaveRestoreBlockSet("Game");
+ISaveRestoreBlockSet *g_pGameSaveRestoreBlockSet = &g_SaveRestoreBlockSet;
+
+//=============================================================================
+#if !defined( CLIENT_DLL )
+
+//------------------------------------------------------------------------------
+// Creates all entities that lie in the transition list
+//------------------------------------------------------------------------------
+void CreateEntitiesInTransitionList( CSaveRestoreData *pSaveData, int levelMask )
+{
+ CBaseEntity *pent;
+ int i;
+ for ( i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ entitytable_t *pEntInfo = pSaveData->GetEntityInfo( i );
+ pEntInfo->hEnt = NULL;
+
+ if ( pEntInfo->size == 0 || pEntInfo->edictindex == 0 )
+ continue;
+
+ if ( pEntInfo->classname == NULL_STRING )
+ {
+ Warning( "Entity with data saved, but with no classname\n" );
+ Assert(0);
+ continue;
+ }
+
+ bool active = (pEntInfo->flags & levelMask) ? 1 : 0;
+
+ // spawn players
+ pent = NULL;
+ if ( (pEntInfo->edictindex > 0) && (pEntInfo->edictindex <= gpGlobals->maxClients) )
+ {
+ edict_t *ed = INDEXENT( pEntInfo->edictindex );
+
+ if ( active && ed && !ed->IsFree() )
+ {
+ if ( !(pEntInfo->flags & FENTTABLE_PLAYER) )
+ {
+ Warning( "ENTITY IS NOT A PLAYER: %d\n" , i );
+ Assert(0);
+ }
+
+ pent = CBasePlayer::CreatePlayer( STRING(pEntInfo->classname), ed );
+ }
+ }
+ else if ( active )
+ {
+ pent = CreateEntityByName( STRING(pEntInfo->classname) );
+ }
+
+ pEntInfo->hEnt = pent;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+int CreateEntityTransitionList( CSaveRestoreData *pSaveData, int levelMask )
+{
+ CBaseEntity *pent;
+ entitytable_t *pEntInfo;
+
+ // Create entity list
+ CreateEntitiesInTransitionList( pSaveData, levelMask );
+
+ // Now spawn entities
+ CUtlVector<int> checkList;
+
+ int i;
+ int movedCount = 0;
+ for ( i = 0; i < pSaveData->NumEntities(); i++ )
+ {
+ pEntInfo = pSaveData->GetEntityInfo( i );
+ pent = pEntInfo->hEnt;
+// pSaveData->currentIndex = i;
+ pSaveData->Seek( pEntInfo->location );
+
+ // clear this out - it must be set on a per-entity basis
+ pSaveData->modelSpaceOffset.Init();
+
+ if ( pent && (pEntInfo->flags & levelMask) ) // Screen out the player if he's not to be spawned
+ {
+ if ( pEntInfo->flags & FENTTABLE_GLOBAL )
+ {
+ DevMsg( 2, "Merging changes for global: %s\n", STRING(pEntInfo->classname) );
+
+ // -------------------------------------------------------------------------
+ // Pass the "global" flag to the DLL to indicate this entity should only override
+ // a matching entity, not be spawned
+ if ( g_EntitySaveRestoreBlockHandler.RestoreGlobalEntity( pent, pSaveData, pEntInfo ) > 0 )
+ {
+ movedCount++;
+ pEntInfo->restoreentityindex = pEntInfo->hEnt.Get()->entindex();
+ AddRestoredEntity( pEntInfo->hEnt.Get() );
+ }
+ else
+ {
+ UTIL_RemoveImmediate( pEntInfo->hEnt.Get() );
+ }
+ // -------------------------------------------------------------------------
+ }
+ else
+ {
+ DevMsg( 2, "Transferring %s (%d)\n", STRING(pEntInfo->classname), pent->edict() ? ENTINDEX(pent->edict()) : -1 );
+ CRestore restoreHelper( pSaveData );
+ if ( g_EntitySaveRestoreBlockHandler.RestoreEntity( pent, &restoreHelper, pEntInfo ) < 0 )
+ {
+ UTIL_RemoveImmediate( pent );
+ }
+ else
+ {
+ // needs to be checked. Do this in a separate pass so that pointers & hierarchy can be traversed
+ checkList.AddToTail(i);
+ }
+ }
+
+ // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate()
+ gEntList.CleanupDeleteList();
+ }
+ }
+
+ for ( i = checkList.Count()-1; i >= 0; --i )
+ {
+ pEntInfo = pSaveData->GetEntityInfo( checkList[i] );
+ pent = pEntInfo->hEnt;
+
+ // NOTE: pent can be NULL because UTIL_RemoveImmediate (called below) removes all in hierarchy
+ if ( !pent )
+ continue;
+
+ MDLCACHE_CRITICAL_SECTION();
+
+ if ( !(pEntInfo->flags & FENTTABLE_PLAYER) && UTIL_EntityInSolid( pent ) )
+ {
+ // this can happen during normal processing - PVS is just a guess, some map areas won't exist in the new map
+ DevMsg( 2, "Suppressing %s\n", STRING(pEntInfo->classname) );
+ UTIL_RemoveImmediate( pent );
+ // Remove any entities that were removed using UTIL_Remove() as a result of the above calls to UTIL_RemoveImmediate()
+ gEntList.CleanupDeleteList();
+ }
+ else
+ {
+ movedCount++;
+ pEntInfo->flags = FENTTABLE_REMOVED;
+ pEntInfo->restoreentityindex = pent->entindex();
+ AddRestoredEntity( pent );
+ }
+ }
+
+ return movedCount;
+}
+#endif