You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
669 lines
17 KiB
669 lines
17 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
|
|
#include "dt.h" |
|
#include "dt_recv_eng.h" |
|
#include "dt_encode.h" |
|
#include "dt_instrumentation.h" |
|
#include "dt_stack.h" |
|
#include "utllinkedlist.h" |
|
#include "tier0/dbg.h" |
|
#include "dt_recv_decoder.h" |
|
#include "tier1/strtools.h" |
|
#include "tier0/icommandline.h" |
|
#include "dt_common_eng.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
class CClientSendTable; |
|
|
|
|
|
// Testing out this pattern.. you can write simple code blocks inside of |
|
// codeToRun. The thing that sucks is that you can't access your function's |
|
// local variables inside of codeToRun. |
|
// |
|
// If it used an iterator class, it could access local function variables, |
|
// but the iterator class might be more trouble than it's worth. |
|
#define FOR_EACH_PROP_R( TableType, pTablePointer, tableCode, propCode ) \ |
|
class CPropVisitor \ |
|
{ \ |
|
public: \ |
|
static void Visit_R( TableType *pTable ) \ |
|
{ \ |
|
tableCode; \ |
|
\ |
|
for ( int i=0; i < pTable->GetNumProps(); i++ ) \ |
|
{ \ |
|
TableType::PropType *pProp = pTable->GetProp( i ); \ |
|
\ |
|
propCode; \ |
|
\ |
|
if ( pProp->GetType() == DPT_DataTable ) \ |
|
Visit_R( pProp->GetDataTable() ); \ |
|
} \ |
|
} \ |
|
}; \ |
|
CPropVisitor::Visit_R( pTablePointer ); |
|
|
|
#define SENDPROP_VISIT( pTablePointer, tableCode, propCode ) FOR_EACH_PROP_R( SendTable, pTablePointer, tableCode, propCode ) |
|
#define RECVPROP_VISIT( pTablePointer, tableCode, propCode ) FOR_EACH_PROP_R( RecvTable, pTablePointer, tableCode, propCode ) |
|
#define SETUP_VISIT() class CDummyClass {} // Workaround for parser bug in VC7.1 |
|
|
|
// ------------------------------------------------------------------------------------ // |
|
// Globals. |
|
// ------------------------------------------------------------------------------------ // |
|
|
|
CUtlLinkedList< RecvTable*, unsigned short > g_RecvTables; |
|
CUtlLinkedList< CRecvDecoder *, unsigned short > g_RecvDecoders; |
|
CUtlLinkedList< CClientSendTable*, unsigned short > g_ClientSendTables; |
|
|
|
int g_nPropsDecoded = 0; |
|
|
|
|
|
// ------------------------------------------------------------------------------------ // |
|
// Static helper functions. |
|
// ------------------------------------------------------------------------------------ // |
|
|
|
RecvTable* FindRecvTable( const char *pName ) |
|
{ |
|
FOR_EACH_LL( g_RecvTables, i ) |
|
{ |
|
if ( stricmp( g_RecvTables[i]->GetName(), pName ) == 0 ) |
|
return g_RecvTables[i]; |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
static CClientSendTable* FindClientSendTable( const char *pName ) |
|
{ |
|
FOR_EACH_LL( g_ClientSendTables, i ) |
|
{ |
|
CClientSendTable *pTable = g_ClientSendTables[i]; |
|
|
|
if ( stricmp( pTable->GetName(), pName ) == 0 ) |
|
return pTable; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
// Find all child datatable properties for the send tables. |
|
bool SetupClientSendTableHierarchy() |
|
{ |
|
FOR_EACH_LL( g_ClientSendTables, iClientTable ) |
|
{ |
|
CClientSendTable *pTable = g_ClientSendTables[iClientTable]; |
|
|
|
// For each datatable property, find the table it references. |
|
for ( int iProp=0; iProp < pTable->GetNumProps(); iProp++ ) |
|
{ |
|
CClientSendProp *pClientProp = pTable->GetClientProp( iProp ); |
|
SendProp *pProp = &pTable->m_SendTable.m_pProps[iProp]; |
|
|
|
if ( pProp->m_Type == DPT_DataTable ) |
|
{ |
|
const char *pTableName = pClientProp->GetTableName(); |
|
ErrorIfNot( pTableName, |
|
("SetupClientSendTableHierarchy: missing table name for prop '%s'.", pProp->GetName()) |
|
); |
|
|
|
CClientSendTable *pChild = FindClientSendTable( pTableName ); |
|
if ( !pChild ) |
|
{ |
|
DataTable_Warning( "SetupClientSendTableHierarchy: missing SendTable '%s' (referenced by '%s').\n", pTableName, pTable->GetName() ); |
|
return false; |
|
} |
|
|
|
pProp->SetDataTable( &pChild->m_SendTable ); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
static RecvProp* FindRecvProp( RecvTable *pTable, const char *pName ) |
|
{ |
|
for ( int i=0; i < pTable->GetNumProps(); i++ ) |
|
{ |
|
RecvProp *pProp = pTable->GetProp( i ); |
|
|
|
if ( stricmp( pProp->GetName(), pName ) == 0 ) |
|
return pProp; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
// See if the RecvProp is fit to receive the SendProp's data. |
|
bool CompareRecvPropToSendProp( const RecvProp *pRecvProp, const SendProp *pSendProp ) |
|
{ |
|
while ( 1 ) |
|
{ |
|
ErrorIfNot( pRecvProp && pSendProp, |
|
("CompareRecvPropToSendProp: missing a property.") |
|
); |
|
|
|
if ( pRecvProp->GetType() != pSendProp->GetType() || pRecvProp->IsInsideArray() != pSendProp->IsInsideArray() ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( pRecvProp->GetType() == DPT_Array ) |
|
{ |
|
if ( pRecvProp->GetNumElements() != pSendProp->GetNumElements() ) |
|
return false; |
|
|
|
pRecvProp = pRecvProp->GetArrayProp(); |
|
pSendProp = pSendProp->GetArrayProp(); |
|
} |
|
else |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
struct MatchingProp_t |
|
{ |
|
SendProp *m_pProp; |
|
RecvProp *m_pMatchingRecvProp; |
|
|
|
static bool LessFunc( const MatchingProp_t& lhs, const MatchingProp_t& rhs ) |
|
{ |
|
return lhs.m_pProp < rhs.m_pProp; |
|
} |
|
}; |
|
|
|
static bool MatchRecvPropsToSendProps_R( CUtlRBTree< MatchingProp_t, unsigned short >& lookup, char const *sendTableName, SendTable *pSendTable, RecvTable *pRecvTable, bool bAllowMismatches, bool *pAnyMismatches ) |
|
{ |
|
for ( int i=0; i < pSendTable->m_nProps; i++ ) |
|
{ |
|
SendProp *pSendProp = &pSendTable->m_pProps[i]; |
|
|
|
if ( pSendProp->IsExcludeProp() || pSendProp->IsInsideArray() ) |
|
continue; |
|
|
|
// Find a RecvProp by the same name and type. |
|
RecvProp *pRecvProp = 0; |
|
if ( pRecvTable ) |
|
pRecvProp = FindRecvProp( pRecvTable, pSendProp->GetName() ); |
|
|
|
if ( pRecvProp ) |
|
{ |
|
if ( !CompareRecvPropToSendProp( pRecvProp, pSendProp ) ) |
|
{ |
|
Warning( "RecvProp type doesn't match server type for %s/%s\n", pSendTable->GetName(), pSendProp->GetName() ); |
|
return false; |
|
} |
|
|
|
MatchingProp_t info; |
|
info.m_pProp = pSendProp; |
|
info.m_pMatchingRecvProp = pRecvProp; |
|
|
|
lookup.Insert( info ); |
|
} |
|
else |
|
{ |
|
if ( pAnyMismatches ) |
|
{ |
|
*pAnyMismatches = true; |
|
} |
|
|
|
Warning( "Missing RecvProp for %s - %s/%s\n", sendTableName, pSendTable->GetName(), pSendProp->GetName() ); |
|
if ( !bAllowMismatches ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
// Recurse. |
|
if ( pSendProp->GetType() == DPT_DataTable ) |
|
{ |
|
if ( !MatchRecvPropsToSendProps_R( lookup, sendTableName, pSendProp->GetDataTable(), FindRecvTable( pSendProp->GetDataTable()->m_pNetTableName ), bAllowMismatches, pAnyMismatches ) ) |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
// ------------------------------------------------------------------------------------ // |
|
// Interface functions. |
|
// ------------------------------------------------------------------------------------ // |
|
bool RecvTable_Init( RecvTable **pTables, int nTables ) |
|
{ |
|
SETUP_VISIT(); |
|
|
|
for ( int i=0; i < nTables; i++ ) |
|
{ |
|
RECVPROP_VISIT( pTables[i], |
|
{ |
|
if ( pTable->IsInMainList() ) |
|
return; |
|
|
|
// Shouldn't have a decoder yet. |
|
ErrorIfNot( !pTable->m_pDecoder, |
|
("RecvTable_Init: table '%s' has a decoder already.", pTable->GetName())); |
|
|
|
pTable->SetInMainList( true ); |
|
g_RecvTables.AddToTail( pTable ); |
|
}, |
|
|
|
{} |
|
); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
void RecvTable_Term( bool clearall /*= true*/ ) |
|
{ |
|
DTI_Term(); |
|
|
|
SETUP_VISIT(); |
|
|
|
FOR_EACH_LL( g_RecvTables, i ) |
|
{ |
|
RECVPROP_VISIT( g_RecvTables[i], |
|
{ |
|
if ( !pTable->IsInMainList() ) |
|
return; |
|
|
|
pTable->SetInMainList( false ); |
|
pTable->m_pDecoder = 0; |
|
}, |
|
|
|
{} |
|
); |
|
} |
|
|
|
if ( clearall ) |
|
{ |
|
g_RecvTables.Purge(); |
|
} |
|
g_RecvDecoders.PurgeAndDeleteElements(); |
|
g_ClientSendTables.PurgeAndDeleteElements(); |
|
} |
|
|
|
void RecvTable_FreeSendTable( SendTable *pTable ) |
|
{ |
|
for ( int iProp=0; iProp < pTable->m_nProps; iProp++ ) |
|
{ |
|
SendProp *pProp = &pTable->m_pProps[iProp]; |
|
|
|
delete [] pProp->m_pVarName; |
|
|
|
if ( pProp->m_pExcludeDTName ) |
|
delete [] pProp->m_pExcludeDTName; |
|
} |
|
|
|
if ( pTable->m_pProps ) |
|
delete [] pTable->m_pProps; |
|
|
|
delete pTable; |
|
} |
|
|
|
|
|
SendTable *RecvTable_ReadInfos( bf_read *pBuf, int nDemoProtocol ) |
|
{ |
|
SendTable *pTable = new SendTable; |
|
|
|
pTable->m_pNetTableName = pBuf->ReadAndAllocateString(); |
|
|
|
// Read the property list. |
|
pTable->m_nProps = pBuf->ReadUBitLong( PROPINFOBITS_NUMPROPS ); |
|
pTable->m_pProps = pTable->m_nProps ? new SendProp[ pTable->m_nProps ] : NULL; |
|
|
|
for ( int iProp=0; iProp < pTable->m_nProps; iProp++ ) |
|
{ |
|
SendProp *pProp = &pTable->m_pProps[iProp]; |
|
|
|
pProp->m_Type = (SendPropType)pBuf->ReadUBitLong( PROPINFOBITS_TYPE ); |
|
pProp->m_pVarName = pBuf->ReadAndAllocateString(); |
|
|
|
int nFlagsBits = PROPINFOBITS_FLAGS; |
|
|
|
// HACK to playback old demos. SPROP_NUMFLAGBITS was 11, now 13 |
|
// old nDemoProtocol was 2 |
|
if ( nDemoProtocol == 2 ) |
|
{ |
|
nFlagsBits = 11; |
|
} |
|
|
|
pProp->SetFlags( pBuf->ReadUBitLong( nFlagsBits ) ); |
|
|
|
if ( pProp->m_Type == DPT_DataTable ) |
|
{ |
|
pProp->m_pExcludeDTName = pBuf->ReadAndAllocateString(); |
|
} |
|
else |
|
{ |
|
if ( pProp->IsExcludeProp() ) |
|
{ |
|
pProp->m_pExcludeDTName = pBuf->ReadAndAllocateString(); |
|
} |
|
else if ( pProp->GetType() == DPT_Array ) |
|
{ |
|
pProp->SetNumElements( pBuf->ReadUBitLong( PROPINFOBITS_NUMELEMENTS ) ); |
|
} |
|
else |
|
{ |
|
pProp->m_fLowValue = pBuf->ReadBitFloat(); |
|
pProp->m_fHighValue = pBuf->ReadBitFloat(); |
|
pProp->m_nBits = pBuf->ReadUBitLong( PROPINFOBITS_NUMBITS ); |
|
} |
|
} |
|
} |
|
|
|
return pTable; |
|
} |
|
|
|
bool RecvTable_RecvClassInfos( bf_read *pBuf, bool bNeedsDecoder, int nDemoProtocol ) |
|
{ |
|
SendTable *pSendTable = RecvTable_ReadInfos( pBuf, nDemoProtocol ); |
|
|
|
if ( !pSendTable ) |
|
return false; |
|
|
|
bool ret = DataTable_SetupReceiveTableFromSendTable( pSendTable, bNeedsDecoder ); |
|
|
|
RecvTable_FreeSendTable( pSendTable ); |
|
|
|
return ret; |
|
} |
|
|
|
static void CopySendPropsToRecvProps( |
|
CUtlRBTree< MatchingProp_t, unsigned short >& lookup, |
|
const CUtlVector<const SendProp*> &sendProps, |
|
CUtlVector<const RecvProp*> &recvProps |
|
) |
|
{ |
|
recvProps.SetSize( sendProps.Count() ); |
|
for ( int iSendProp=0; iSendProp < sendProps.Count(); iSendProp++ ) |
|
{ |
|
const SendProp *pSendProp = sendProps[iSendProp]; |
|
MatchingProp_t search; |
|
search.m_pProp = (SendProp *)pSendProp; |
|
int idx = lookup.Find( search ); |
|
if ( idx == lookup.InvalidIndex() ) |
|
{ |
|
recvProps[iSendProp] = 0; |
|
} |
|
else |
|
{ |
|
recvProps[iSendProp] = lookup[ idx ].m_pMatchingRecvProp; |
|
} |
|
} |
|
} |
|
|
|
bool RecvTable_CreateDecoders( const CStandardSendProxies *pSendProxies, bool bAllowMismatches, bool *pAnyMismatches ) |
|
{ |
|
DTI_Init(); |
|
|
|
SETUP_VISIT(); |
|
|
|
if ( pAnyMismatches ) |
|
{ |
|
*pAnyMismatches = false; |
|
} |
|
|
|
// First, now that we've supposedly received all the SendTables that we need, |
|
// set their datatable child pointers. |
|
if ( !SetupClientSendTableHierarchy() ) |
|
return false; |
|
|
|
FOR_EACH_LL( g_RecvDecoders, i ) |
|
{ |
|
CRecvDecoder *pDecoder = g_RecvDecoders[i]; |
|
|
|
|
|
// It should already have been linked to its ClientSendTable. |
|
Assert( pDecoder->m_pClientSendTable ); |
|
if ( !pDecoder->m_pClientSendTable ) |
|
return false; |
|
|
|
|
|
// For each decoder, precalculate the SendTable's flat property list. |
|
if ( !pDecoder->m_Precalc.SetupFlatPropertyArray() ) |
|
return false; |
|
|
|
CUtlRBTree< MatchingProp_t, unsigned short > PropLookup( 0, 0, MatchingProp_t::LessFunc ); |
|
|
|
// Now match RecvProp with SendProps. |
|
if ( !MatchRecvPropsToSendProps_R( PropLookup, pDecoder->GetSendTable()->m_pNetTableName, pDecoder->GetSendTable(), FindRecvTable( pDecoder->GetSendTable()->m_pNetTableName ), bAllowMismatches, pAnyMismatches ) ) |
|
return false; |
|
|
|
// Now fill out the matching RecvProp array. |
|
CSendTablePrecalc *pPrecalc = &pDecoder->m_Precalc; |
|
CopySendPropsToRecvProps( PropLookup, pPrecalc->m_Props, pDecoder->m_Props ); |
|
CopySendPropsToRecvProps( PropLookup, pPrecalc->m_DatatableProps, pDecoder->m_DatatableProps ); |
|
|
|
DTI_HookRecvDecoder( pDecoder ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
bool RecvTable_Decode( |
|
RecvTable *pTable, |
|
void *pStruct, |
|
bf_read *pIn, |
|
int objectID, |
|
bool updateDTI |
|
) |
|
{ |
|
CRecvDecoder *pDecoder = pTable->m_pDecoder; |
|
ErrorIfNot( pDecoder, |
|
("RecvTable_Decode: table '%s' missing a decoder.", pTable->GetName()) |
|
); |
|
|
|
// While there are properties, decode them.. walk the stack as you go. |
|
CClientDatatableStack theStack( pDecoder, (unsigned char*)pStruct, objectID ); |
|
|
|
theStack.Init(); |
|
int iStartBit = 0, nIndexBits = 0, iLastBit = pIn->GetNumBitsRead(); |
|
unsigned int iProp; |
|
CDeltaBitsReader deltaBitsReader( pIn ); |
|
while ( (iProp = deltaBitsReader.ReadNextPropIndex()) < MAX_DATATABLE_PROPS ) |
|
{ |
|
theStack.SeekToProp( iProp ); |
|
|
|
const RecvProp *pProp = pDecoder->GetProp( iProp ); |
|
|
|
// Instrumentation (store the # bits for the prop index). |
|
if ( g_bDTIEnabled ) |
|
{ |
|
iStartBit = pIn->GetNumBitsRead(); |
|
nIndexBits = iStartBit - iLastBit; |
|
} |
|
|
|
DecodeInfo decodeInfo; |
|
decodeInfo.m_pStruct = theStack.GetCurStructBase(); |
|
|
|
if ( pProp ) |
|
{ |
|
decodeInfo.m_pData = theStack.GetCurStructBase() + pProp->GetOffset(); |
|
} |
|
else |
|
{ |
|
// They're allowed to be missing props here if they're playing back a demo. |
|
// This allows us to change the datatables and still preserve old demos. |
|
decodeInfo.m_pData = NULL; |
|
} |
|
|
|
decodeInfo.m_pRecvProp = theStack.IsCurProxyValid() ? pProp : NULL; // Just skip the data if the proxies are screwed. |
|
decodeInfo.m_pProp = pDecoder->GetSendProp( iProp ); |
|
decodeInfo.m_pIn = pIn; |
|
decodeInfo.m_ObjectID = objectID; |
|
|
|
g_PropTypeFns[ decodeInfo.m_pProp->GetType() ].Decode( &decodeInfo ); |
|
++g_nPropsDecoded; |
|
|
|
// Instrumentation (store # bits for the encoded property). |
|
if ( updateDTI && g_bDTIEnabled ) |
|
{ |
|
iLastBit = pIn->GetNumBitsRead(); |
|
DTI_HookDeltaBits( pDecoder, iProp, iLastBit - iStartBit, nIndexBits ); |
|
} |
|
} |
|
|
|
return !pIn->IsOverflowed(); |
|
} |
|
|
|
|
|
void RecvTable_DecodeZeros( RecvTable *pTable, void *pStruct, int objectID ) |
|
{ |
|
CRecvDecoder *pDecoder = pTable->m_pDecoder; |
|
ErrorIfNot( pDecoder, |
|
("RecvTable_DecodeZeros: table '%s' missing a decoder.", pTable->GetName()) |
|
); |
|
|
|
// While there are properties, decode them.. walk the stack as you go. |
|
CClientDatatableStack theStack( pDecoder, (unsigned char*)pStruct, objectID ); |
|
|
|
theStack.Init(); |
|
|
|
for ( int iProp=0; iProp < pDecoder->GetNumProps(); iProp++ ) |
|
{ |
|
theStack.SeekToProp( iProp ); |
|
|
|
// They're allowed to be missing props here if they're playing back a demo. |
|
// This allows us to change the datatables and still preserve old demos. |
|
const RecvProp *pProp = pDecoder->GetProp( iProp ); |
|
if ( !pProp ) |
|
continue; |
|
|
|
DecodeInfo decodeInfo; |
|
decodeInfo.m_pStruct = theStack.GetCurStructBase(); |
|
decodeInfo.m_pData = theStack.GetCurStructBase() + pProp->GetOffset(); |
|
decodeInfo.m_pRecvProp = theStack.IsCurProxyValid() ? pProp : NULL; // Just skip the data if the proxies are screwed. |
|
decodeInfo.m_pProp = pDecoder->GetSendProp( iProp ); |
|
decodeInfo.m_pIn = NULL; |
|
decodeInfo.m_ObjectID = objectID; |
|
|
|
g_PropTypeFns[pProp->GetType()].DecodeZero( &decodeInfo ); |
|
} |
|
} |
|
|
|
|
|
|
|
int RecvTable_MergeDeltas( |
|
RecvTable *pTable, |
|
|
|
bf_read *pOldState, // this can be null |
|
bf_read *pNewState, |
|
|
|
bf_write *pOut, |
|
|
|
int objectID, |
|
int *pChangedProps, |
|
bool updateDTI |
|
) |
|
{ |
|
ErrorIfNot( pTable && pNewState && pOut, |
|
("RecvTable_MergeDeltas: invalid parameters passed.") |
|
); |
|
|
|
CRecvDecoder *pDecoder = pTable->m_pDecoder; |
|
ErrorIfNot( pDecoder, ("RecvTable_MergeDeltas: table '%s' is missing its decoder.", pTable->GetName()) ); |
|
|
|
int nChanged = 0; |
|
|
|
// Setup to read the delta bits from each buffer. |
|
CDeltaBitsReader oldStateReader( pOldState ); |
|
CDeltaBitsReader newStateReader( pNewState ); |
|
|
|
// Setup to write delta bits into the output. |
|
CDeltaBitsWriter deltaBitsWriter( pOut ); |
|
|
|
unsigned int iOldProp = ~0u; |
|
if ( pOldState ) |
|
iOldProp = oldStateReader.ReadNextPropIndex(); |
|
|
|
int iStartBit = 0, nIndexBits = 0, iLastBit = pNewState->GetNumBitsRead(); |
|
|
|
unsigned int iNewProp = newStateReader.ReadNextPropIndex(); |
|
|
|
while ( 1 ) |
|
{ |
|
// Write any properties in the previous state that aren't in the new state. |
|
while ( iOldProp < iNewProp ) |
|
{ |
|
deltaBitsWriter.WritePropIndex( iOldProp ); |
|
oldStateReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pDecoder->GetSendProp( iOldProp ) ); |
|
iOldProp = oldStateReader.ReadNextPropIndex(); |
|
} |
|
|
|
// Check if we're at the end here so the while() statement above can seek the old buffer |
|
// to its end too. |
|
if ( iNewProp >= MAX_DATATABLE_PROPS ) |
|
break; |
|
|
|
// If the old state has this property too, then just skip over its data. |
|
if ( iOldProp == iNewProp ) |
|
{ |
|
oldStateReader.SkipPropData( pDecoder->GetSendProp( iOldProp ) ); |
|
iOldProp = oldStateReader.ReadNextPropIndex(); |
|
} |
|
|
|
// Instrumentation (store the # bits for the prop index). |
|
if ( updateDTI && g_bDTIEnabled ) |
|
{ |
|
iStartBit = pNewState->GetNumBitsRead(); |
|
nIndexBits = iStartBit - iLastBit; |
|
} |
|
|
|
// Now write the new state's value. |
|
deltaBitsWriter.WritePropIndex( iNewProp ); |
|
newStateReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pDecoder->GetSendProp( iNewProp ) ); |
|
|
|
if ( pChangedProps ) |
|
{ |
|
pChangedProps[nChanged] = iNewProp; |
|
} |
|
|
|
nChanged++; |
|
|
|
// Instrumentation (store # bits for the encoded property). |
|
if ( updateDTI && g_bDTIEnabled ) |
|
{ |
|
iLastBit = pNewState->GetNumBitsRead(); |
|
DTI_HookDeltaBits( pDecoder, iNewProp, iLastBit - iStartBit, nIndexBits ); |
|
} |
|
|
|
iNewProp = newStateReader.ReadNextPropIndex(); |
|
} |
|
|
|
Assert( nChanged <= MAX_DATATABLE_PROPS ); |
|
|
|
ErrorIfNot( |
|
!(pOldState && pOldState->IsOverflowed()) && !pNewState->IsOverflowed() && !pOut->IsOverflowed(), |
|
("RecvTable_MergeDeltas: overflowed in RecvTable '%s'.", pTable->GetName()) |
|
); |
|
|
|
return nChanged; |
|
} |
|
|
|
|
|
void RecvTable_CopyEncoding( RecvTable *pTable, bf_read *pIn, bf_write *pOut, int objectID ) |
|
{ |
|
RecvTable_MergeDeltas( pTable, NULL, pIn, pOut, objectID ); |
|
} |
|
|
|
|
|
|
|
|