|
|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
|
|
//
|
|
|
|
// Purpose:
|
|
|
|
//
|
|
|
|
// $NoKeywords: $
|
|
|
|
//
|
|
|
|
// @Note (toml 12-02-02): For now, all of the methods in the types defined here
|
|
|
|
// are inline to permit simple cross-DLL usage
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
#ifndef SAVERESTORETYPES_H
|
|
|
|
#define SAVERESTORETYPES_H
|
|
|
|
|
|
|
|
#if defined( _WIN32 )
|
|
|
|
#pragma once
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "tier1/utlhash.h"
|
|
|
|
|
|
|
|
#include <string_t.h> // NULL_STRING define
|
|
|
|
struct edict_t;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef EHANDLE_H // not available to engine
|
|
|
|
#define SR_ENTS_VISIBLE 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// class CSaveRestoreSegment
|
|
|
|
//
|
|
|
|
|
|
|
|
class CSaveRestoreSegment
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CSaveRestoreSegment();
|
|
|
|
|
|
|
|
//---------------------------------
|
|
|
|
// Buffer operations
|
|
|
|
//
|
|
|
|
void Init( void *pNewBase, int nBytes );
|
|
|
|
void Rebase();
|
|
|
|
void Rewind( int nBytes );
|
|
|
|
char *GetBuffer();
|
|
|
|
int BytesAvailable() const;
|
|
|
|
int SizeBuffer() const;
|
|
|
|
bool Write( const void *pData, int nBytes );
|
|
|
|
bool Read( void *pOutput, int nBytes );
|
|
|
|
int GetCurPos();
|
|
|
|
char *AccessCurPos();
|
|
|
|
bool Seek( int absPosition );
|
|
|
|
void MoveCurPos( int nBytes );
|
|
|
|
|
|
|
|
//---------------------------------
|
|
|
|
// Symbol table operations
|
|
|
|
//
|
|
|
|
void InitSymbolTable( char **pNewTokens, int sizeTable);
|
|
|
|
char **DetachSymbolTable();
|
|
|
|
int SizeSymbolTable();
|
|
|
|
bool DefineSymbol( const char *pszToken, int token );
|
|
|
|
unsigned short FindCreateSymbol( const char *pszToken );
|
|
|
|
const char *StringFromSymbol( int token );
|
|
|
|
|
|
|
|
private:
|
|
|
|
unsigned int HashString( const char *pszToken );
|
|
|
|
|
|
|
|
//---------------------------------
|
|
|
|
// Buffer data
|
|
|
|
//
|
|
|
|
char *pBaseData; // Start of all entity save data
|
|
|
|
char *pCurrentData; // Current buffer pointer for sequential access
|
|
|
|
int size; // Current data size, aka, pCurrentData - pBaseData
|
|
|
|
int bufferSize; // Total space for data
|
|
|
|
|
|
|
|
//---------------------------------
|
|
|
|
// Symbol table
|
|
|
|
//
|
|
|
|
int tokenCount; // Number of elements in the pTokens table
|
|
|
|
char **pTokens; // Hash table of entity strings (sparse)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// class CGameSaveRestoreInfo
|
|
|
|
//
|
|
|
|
|
|
|
|
struct levellist_t
|
|
|
|
{
|
|
|
|
DECLARE_SIMPLE_DATADESC();
|
|
|
|
|
|
|
|
char mapName[ MAX_MAP_NAME_SAVE ];
|
|
|
|
char landmarkName[ 32 ];
|
|
|
|
edict_t *pentLandmark;
|
|
|
|
Vector vecLandmarkOrigin;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define MAX_LEVEL_CONNECTIONS 16 // These are encoded in the lower 16bits of entitytable_t->flags
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
struct EHandlePlaceholder_t // Engine does some of the game writing (alas, probably shouldn't), but can't see ehandle.h
|
|
|
|
{
|
|
|
|
uintp i;
|
|
|
|
};
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
struct entitytable_t
|
|
|
|
{
|
|
|
|
void Clear()
|
|
|
|
{
|
|
|
|
id = -1;
|
|
|
|
edictindex = -1;
|
|
|
|
saveentityindex = -1;
|
|
|
|
restoreentityindex = -1;
|
|
|
|
location = 0;
|
|
|
|
size = 0;
|
|
|
|
flags = 0;
|
|
|
|
classname = NULL_STRING;
|
|
|
|
globalname = NULL_STRING;
|
|
|
|
landmarkModelSpace.Init();
|
|
|
|
modelname = NULL_STRING;
|
|
|
|
}
|
|
|
|
|
|
|
|
int id; // Ordinal ID of this entity (used for entity <--> pointer conversions)
|
|
|
|
int edictindex; // saved for if the entity requires a certain edict number when restored (players, world)
|
|
|
|
|
|
|
|
int saveentityindex; // the entity index the entity had at save time ( for fixing up client side entities )
|
|
|
|
int restoreentityindex; // the entity index given to this entity at restore time
|
|
|
|
|
|
|
|
#ifdef SR_ENTS_VISIBLE
|
|
|
|
EHANDLE hEnt; // Pointer to the in-game entity
|
|
|
|
#else
|
|
|
|
EHandlePlaceholder_t hEnt;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int location; // Offset from the base data of this entity
|
|
|
|
int size; // Byte size of this entity's data
|
|
|
|
int flags; // This could be a short -- bit mask of transitions that this entity is in the PVS of
|
|
|
|
string_t classname; // entity class name
|
|
|
|
string_t globalname; // entity global name
|
|
|
|
Vector landmarkModelSpace; // a fixed position in model space for comparison
|
|
|
|
// NOTE: Brush models can be built in different coordiante systems
|
|
|
|
// in different levels, so this fixes up local quantities to match
|
|
|
|
// those differences.
|
|
|
|
string_t modelname;
|
|
|
|
|
|
|
|
DECLARE_SIMPLE_DATADESC();
|
|
|
|
};
|
|
|
|
|
|
|
|
#define FENTTABLE_PLAYER 0x80000000
|
|
|
|
#define FENTTABLE_REMOVED 0x40000000
|
|
|
|
#define FENTTABLE_MOVEABLE 0x20000000
|
|
|
|
#define FENTTABLE_GLOBAL 0x10000000
|
|
|
|
#define FENTTABLE_PLAYERCHILD 0x08000000 // this entity is connected to a player, so it must be spawned with it
|
|
|
|
#define FENTTABLE_LEVELMASK 0x0000FFFF // reserve bits for 16 level connections
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
struct saverestorelevelinfo_t
|
|
|
|
{
|
|
|
|
int connectionCount;// Number of elements in the levelList[]
|
|
|
|
levellist_t levelList[ MAX_LEVEL_CONNECTIONS ]; // List of connections from this level
|
|
|
|
|
|
|
|
// smooth transition
|
|
|
|
int fUseLandmark;
|
|
|
|
char szLandmarkName[20]; // landmark we'll spawn near in next level
|
|
|
|
Vector vecLandmarkOffset; // for landmark transitions
|
|
|
|
float time;
|
|
|
|
char szCurrentMapName[MAX_MAP_NAME_SAVE]; // To check global entities
|
|
|
|
int mapVersion;
|
|
|
|
};
|
|
|
|
|
|
|
|
//-------------------------------------
|
|
|
|
|
|
|
|
class CGameSaveRestoreInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CGameSaveRestoreInfo()
|
|
|
|
: tableCount( 0 ), pTable( 0 ), m_pCurrentEntity( 0 ), m_EntityToIndex( 1024 )
|
|
|
|
{
|
|
|
|
memset( &levelInfo, 0, sizeof( levelInfo ) );
|
|
|
|
modelSpaceOffset.Init( 0, 0, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
void InitEntityTable( entitytable_t *pNewTable = NULL, int size = 0 )
|
|
|
|
{
|
|
|
|
pTable = pNewTable;
|
|
|
|
tableCount = size;
|
|
|
|
|
|
|
|
for ( int i = 0; i < NumEntities(); i++ )
|
|
|
|
{
|
|
|
|
GetEntityInfo( i )->Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
entitytable_t *DetachEntityTable()
|
|
|
|
{
|
|
|
|
entitytable_t *pReturn = pTable;
|
|
|
|
pTable = NULL;
|
|
|
|
tableCount = 0;
|
|
|
|
return pReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
CBaseEntity *GetCurrentEntityContext() { return m_pCurrentEntity; }
|
|
|
|
void SetCurrentEntityContext(CBaseEntity *pEntity) { m_pCurrentEntity = pEntity; }
|
|
|
|
|
|
|
|
int NumEntities() { return tableCount; }
|
|
|
|
entitytable_t *GetEntityInfo( int i ) { return (pTable + i); }
|
|
|
|
float GetBaseTime() const { return levelInfo.time; }
|
|
|
|
Vector GetLandmark() const { return ( levelInfo.fUseLandmark ) ? levelInfo.vecLandmarkOffset : vec3_origin; }
|
|
|
|
|
|
|
|
void BuildEntityHash()
|
|
|
|
{
|
|
|
|
#ifdef GAME_DLL
|
|
|
|
int i;
|
|
|
|
entitytable_t *pTable;
|
|
|
|
int nEntities = NumEntities();
|
|
|
|
|
|
|
|
for ( i = 0; i < nEntities; i++ )
|
|
|
|
{
|
|
|
|
pTable = GetEntityInfo( i );
|
|
|
|
m_EntityToIndex.Insert( CHashElement( pTable->hEnt.Get(), i ) );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void PurgeEntityHash()
|
|
|
|
{
|
|
|
|
m_EntityToIndex.Purge();
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetEntityIndex( const CBaseEntity *pEntity )
|
|
|
|
{
|
|
|
|
#ifdef SR_ENTS_VISIBLE
|
|
|
|
if ( pEntity )
|
|
|
|
{
|
|
|
|
if ( m_EntityToIndex.Count() )
|
|
|
|
{
|
|
|
|
UtlHashHandle_t hElement = m_EntityToIndex.Find( CHashElement( pEntity ) );
|
|
|
|
if ( hElement != m_EntityToIndex.InvalidHandle() )
|
|
|
|
{
|
|
|
|
return m_EntityToIndex.Element( hElement ).index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
entitytable_t *pEntTable;
|
|
|
|
|
|
|
|
int nEntities = NumEntities();
|
|
|
|
for ( i = 0; i < nEntities; i++ )
|
|
|
|
{
|
|
|
|
pEntTable = GetEntityInfo( i );
|
|
|
|
if ( pEntTable->hEnt == pEntity )
|
|
|
|
return pEntTable->id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
saverestorelevelinfo_t levelInfo;
|
|
|
|
Vector modelSpaceOffset; // used only for globaly entity brushes modelled in different coordinate systems.
|
|
|
|
|
|
|
|
private:
|
|
|
|
int tableCount; // Number of elements in the entity table
|
|
|
|
entitytable_t *pTable; // Array of entitytable_t elements (1 for each entity)
|
|
|
|
CBaseEntity *m_pCurrentEntity; // only valid during the save functions of this entity, NULL otherwise
|
|
|
|
|
|
|
|
|
|
|
|
struct CHashElement
|
|
|
|
{
|
|
|
|
const CBaseEntity *pEntity;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
CHashElement( const CBaseEntity *pEntity, int index) : pEntity(pEntity), index(index) {}
|
|
|
|
CHashElement( const CBaseEntity *pEntity ) : pEntity(pEntity) {}
|
|
|
|
CHashElement() = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CHashFuncs
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CHashFuncs( int ) {}
|
|
|
|
|
|
|
|
// COMPARE
|
|
|
|
bool operator()( const CHashElement &lhs, const CHashElement &rhs ) const
|
|
|
|
{
|
|
|
|
return lhs.pEntity == rhs.pEntity;
|
|
|
|
}
|
|
|
|
|
|
|
|
// HASH
|
|
|
|
unsigned int operator()( const CHashElement &item ) const
|
|
|
|
{
|
|
|
|
return HashItem( item.pEntity );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef CUtlHash<CHashElement, CHashFuncs, CHashFuncs> CEntityToIndexHash;
|
|
|
|
|
|
|
|
CEntityToIndexHash m_EntityToIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
class CSaveRestoreData : public CSaveRestoreSegment,
|
|
|
|
public CGameSaveRestoreInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CSaveRestoreData() : bAsync( false ) {}
|
|
|
|
|
|
|
|
|
|
|
|
bool bAsync;
|
|
|
|
};
|
|
|
|
|
|
|
|
inline CSaveRestoreData *MakeSaveRestoreData( void *pMemory )
|
|
|
|
{
|
|
|
|
return new (pMemory) CSaveRestoreData;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// class CSaveRestoreSegment, inline functions
|
|
|
|
//
|
|
|
|
|
|
|
|
inline CSaveRestoreSegment::CSaveRestoreSegment()
|
|
|
|
{
|
|
|
|
memset( this, 0, sizeof(*this) );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void CSaveRestoreSegment::Init( void *pNewBase, int nBytes )
|
|
|
|
{
|
|
|
|
pCurrentData = pBaseData = (char *)pNewBase;
|
|
|
|
size = 0;
|
|
|
|
bufferSize = nBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void CSaveRestoreSegment::MoveCurPos( int nBytes )
|
|
|
|
{
|
|
|
|
pCurrentData += nBytes;
|
|
|
|
size += nBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void CSaveRestoreSegment::Rebase()
|
|
|
|
{
|
|
|
|
pBaseData = pCurrentData;
|
|
|
|
bufferSize -= size;
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void CSaveRestoreSegment::Rewind( int nBytes )
|
|
|
|
{
|
|
|
|
if ( size < nBytes )
|
|
|
|
nBytes = size;
|
|
|
|
|
|
|
|
MoveCurPos( -nBytes );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline char *CSaveRestoreSegment::GetBuffer()
|
|
|
|
{
|
|
|
|
return pBaseData;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int CSaveRestoreSegment::BytesAvailable() const
|
|
|
|
{
|
|
|
|
return (bufferSize - size);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int CSaveRestoreSegment::SizeBuffer() const
|
|
|
|
{
|
|
|
|
return bufferSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool CSaveRestoreSegment::Write( const void *pData, int nBytes )
|
|
|
|
{
|
|
|
|
if ( nBytes > BytesAvailable() )
|
|
|
|
{
|
|
|
|
size = bufferSize;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy( pCurrentData, pData, nBytes );
|
|
|
|
MoveCurPos( nBytes );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool CSaveRestoreSegment::Read( void *pOutput, int nBytes )
|
|
|
|
{
|
|
|
|
if ( !BytesAvailable() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( nBytes > BytesAvailable() )
|
|
|
|
{
|
|
|
|
size = bufferSize;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( pOutput )
|
|
|
|
memcpy( pOutput, pCurrentData, nBytes );
|
|
|
|
MoveCurPos( nBytes );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int CSaveRestoreSegment::GetCurPos()
|
|
|
|
{
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline char *CSaveRestoreSegment::AccessCurPos()
|
|
|
|
{
|
|
|
|
return pCurrentData;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool CSaveRestoreSegment::Seek( int absPosition )
|
|
|
|
{
|
|
|
|
if ( absPosition < 0 || absPosition >= bufferSize )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
size = absPosition;
|
|
|
|
pCurrentData = pBaseData + size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void CSaveRestoreSegment::InitSymbolTable( char **pNewTokens, int sizeTable)
|
|
|
|
{
|
|
|
|
Assert( !pTokens );
|
|
|
|
tokenCount = sizeTable;
|
|
|
|
pTokens = pNewTokens;
|
|
|
|
memset( pTokens, 0, sizeTable * sizeof( pTokens[0]) );
|
|
|
|
}
|
|
|
|
|
|
|
|
inline char **CSaveRestoreSegment::DetachSymbolTable()
|
|
|
|
{
|
|
|
|
char **pResult = pTokens;
|
|
|
|
tokenCount = 0;
|
|
|
|
pTokens = NULL;
|
|
|
|
return pResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int CSaveRestoreSegment::SizeSymbolTable()
|
|
|
|
{
|
|
|
|
return tokenCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool CSaveRestoreSegment::DefineSymbol( const char *pszToken, int token )
|
|
|
|
{
|
|
|
|
if ( pTokens[token] == NULL )
|
|
|
|
{
|
|
|
|
pTokens[token] = (char *)pszToken;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Assert( 0 );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline unsigned short CSaveRestoreSegment::FindCreateSymbol( const char *pszToken )
|
|
|
|
{
|
|
|
|
unsigned short hash = (unsigned short)(HashString( pszToken ) % (unsigned)tokenCount );
|
|
|
|
|
|
|
|
#if _DEBUG
|
|
|
|
static int tokensparsed = 0;
|
|
|
|
tokensparsed++;
|
|
|
|
if ( !tokenCount || !pTokens )
|
|
|
|
{
|
|
|
|
AssertMsg( 0, ("No token table array in TokenHash()!") );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for ( int i=0; i<tokenCount; i++ )
|
|
|
|
{
|
|
|
|
#if _DEBUG
|
|
|
|
static bool beentheredonethat = false;
|
|
|
|
if ( i > 50 && !beentheredonethat )
|
|
|
|
{
|
|
|
|
beentheredonethat = true;
|
|
|
|
AssertMsg( 0, ("CSaveRestoreBuffer::TokenHash() is getting too full!" ) );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int index = hash + i;
|
|
|
|
if ( index >= tokenCount )
|
|
|
|
index -= tokenCount;
|
|
|
|
|
|
|
|
if ( !pTokens[index] || strcmp( pszToken, pTokens[index] ) == 0 )
|
|
|
|
{
|
|
|
|
pTokens[index] = (char *)pszToken;
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Token hash table full!!!
|
|
|
|
// [Consider doing overflow table(s) after the main table & limiting linear hash table search]
|
|
|
|
Warning( "CSaveRestoreBuffer::TokenHash() is COMPLETELY FULL!" );
|
|
|
|
Assert( 0 );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const char *CSaveRestoreSegment::StringFromSymbol( int token )
|
|
|
|
{
|
|
|
|
if ( token >= 0 && token < tokenCount )
|
|
|
|
return pTokens[token];
|
|
|
|
Assert( 0 );
|
|
|
|
return "<<illegal>>";
|
|
|
|
}
|
|
|
|
|
|
|
|
/// XXX(JohnS): I'm not sure using an intrinsic has any value here, just doing the shift should be recognized by most
|
|
|
|
/// compilers. Either way, there's no portable intrinsic.
|
|
|
|
|
|
|
|
// Newer GCC versions provide this in this header, older did by default.
|
|
|
|
#if !defined( _rotr ) && defined( COMPILER_GCC ) && !defined( __arm__ ) && !defined( __aarch64__ )
|
|
|
|
#include <x86intrin.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined ( _rotr )
|
|
|
|
inline unsigned _rotr(unsigned x, unsigned n) {
|
|
|
|
return (x >> n % 32) | (x << (32-n) % 32);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
inline unsigned int CSaveRestoreSegment::HashString( const char *pszToken )
|
|
|
|
{
|
|
|
|
COMPILE_TIME_ASSERT( sizeof( unsigned int ) == 4 );
|
|
|
|
unsigned int hash = 0;
|
|
|
|
|
|
|
|
while ( *pszToken )
|
|
|
|
{
|
|
|
|
hash = _rotr( hash, 4 ) ^ *pszToken++;
|
|
|
|
}
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#endif // SAVERESTORETYPES_H
|