//========= 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
