//========= 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 // 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 { unsigned long 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() {} }; 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 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 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 "<>"; } /// 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 ) #include #endif #ifdef COMPILER_CLANG static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__)) _rotr(unsigned int _Value, int _Shift) { _Shift &= 0x1f; return _Shift ? (_Value >> _Shift) | (_Value << (32 - _Shift)) : _Value; } #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