source-engine/public/saverestoretypes.h

540 lines
13 KiB
C++

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