Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

1382 lines
34 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "quakedef.h"
#include "dt.h"
#include "dt_encode.h"
#include "coordsize.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern void DataTable_Warning( const char *pInMessage, ... );
extern bool ShouldWatchThisProp( const SendTable *pTable, int objectID, const char *pPropName );
// The engine implements this.
extern const char* GetObjectClassName( int objectID );
void EncodeFloat( const SendProp *pProp, float fVal, bf_write *pOut, int objectID )
{
// Check for special flags like SPROP_COORD, SPROP_NOSCALE, and SPROP_NORMAL.
int flags = pProp->GetFlags();
if ( flags & SPROP_COORD )
{
pOut->WriteBitCoord( fVal );
}
else if ( flags & ( SPROP_COORD_MP | SPROP_COORD_MP_LOWPRECISION | SPROP_COORD_MP_INTEGRAL ) )
{
COMPILE_TIME_ASSERT( SPROP_COORD_MP_INTEGRAL == (1<<15) );
COMPILE_TIME_ASSERT( SPROP_COORD_MP_LOWPRECISION == (1<<14) );
pOut->WriteBitCoordMP( fVal, ((flags >> 15) & 1), ((flags >> 14) & 1) );
}
else if ( flags & SPROP_NORMAL )
{
pOut->WriteBitNormal( fVal );
}
else // standard clamped-range float
{
unsigned long ulVal;
int nBits = pProp->m_nBits;
if ( flags & SPROP_NOSCALE )
{
union { float f; uint32 u; } convert = { fVal };
ulVal = convert.u;
nBits = 32;
}
else if( fVal < pProp->m_fLowValue )
{
// clamp < 0
ulVal = 0;
if(!(flags & SPROP_ROUNDUP))
{
DataTable_Warning("(class %s): Out-of-range value (%f / %f) in SendPropFloat '%s', clamping.\n",
GetObjectClassName( objectID ), fVal, pProp->m_fLowValue, pProp->m_pVarName );
}
}
else if( fVal > pProp->m_fHighValue )
{
// clamp > 1
ulVal = ((1 << pProp->m_nBits) - 1);
if(!(flags & SPROP_ROUNDDOWN))
{
DataTable_Warning("%s: Out-of-range value (%f/%f) in SendPropFloat '%s', clamping.\n",
GetObjectClassName( objectID ), fVal, pProp->m_fHighValue, pProp->m_pVarName );
}
}
else
{
float fRangeVal = (fVal - pProp->m_fLowValue) * pProp->m_fHighLowMul;
if ( pProp->m_nBits <= 22 )
{
// this is the case we always expect to hit
ulVal = FastFloatToSmallInt( fRangeVal );
}
else
{
// retain old logic just in case anyone relies on its behavior
ulVal = RoundFloatToUnsignedLong( fRangeVal );
}
}
pOut->WriteUBitLong(ulVal, nBits);
}
}
static float DecodeFloat(SendProp const *pProp, bf_read *pIn)
{
int flags = pProp->GetFlags();
if ( flags & SPROP_COORD )
{
return pIn->ReadBitCoord();
}
else if ( flags & ( SPROP_COORD_MP | SPROP_COORD_MP_LOWPRECISION | SPROP_COORD_MP_INTEGRAL ) )
{
return pIn->ReadBitCoordMP( (flags >> 15) & 1, (flags >> 14) & 1 );
}
else if ( flags & SPROP_NOSCALE )
{
return pIn->ReadBitFloat();
}
else if ( flags & SPROP_NORMAL )
{
return pIn->ReadBitNormal();
}
else // standard clamped-range float
{
unsigned long dwInterp = pIn->ReadUBitLong(pProp->m_nBits);
float fVal = (float)dwInterp / ((1 << pProp->m_nBits) - 1);
fVal = pProp->m_fLowValue + (pProp->m_fHighValue - pProp->m_fLowValue) * fVal;
return fVal;
}
}
static inline void DecodeVector(SendProp const *pProp, bf_read *pIn, float *v)
{
v[0] = DecodeFloat(pProp, pIn);
v[1] = DecodeFloat(pProp, pIn);
// Don't read in the third component for normals
if ((pProp->GetFlags() & SPROP_NORMAL) == 0)
{
v[2] = DecodeFloat(pProp, pIn);
}
else
{
int signbit = pIn->ReadOneBit();
float v0v0v1v1 = v[0] * v[0] +
v[1] * v[1];
if (v0v0v1v1 < 1.0f)
v[2] = sqrtf( 1.0f - v0v0v1v1 );
else
v[2] = 0.0f;
if (signbit)
v[2] *= -1.0f;
}
}
static inline void DecodeQuaternion(SendProp const *pProp, bf_read *pIn, float *v)
{
v[0] = DecodeFloat(pProp, pIn);
v[1] = DecodeFloat(pProp, pIn);
v[2] = DecodeFloat(pProp, pIn);
v[3] = DecodeFloat(pProp, pIn);
}
int DecodeBits( DecodeInfo *pInfo, unsigned char *pOut )
{
bf_read temp;
// Read the property in (note: we don't return the bits from here because Decode returns
// the decoded bits.. we're interested in getting the encoded bits).
temp = *pInfo->m_pIn;
pInfo->m_pRecvProp = NULL;
pInfo->m_pData = NULL;
g_PropTypeFns[pInfo->m_pProp->m_Type].Decode( pInfo );
// Return the encoded bits.
int nBits = pInfo->m_pIn->GetNumBitsRead() - temp.GetNumBitsRead();
temp.ReadBits(pOut, nBits);
return nBits;
}
// ---------------------------------------------------------------------------------------- //
// Most of the prop types can use this generic FastCopy version. Arrays are a bit of a pain.
// ---------------------------------------------------------------------------------------- //
inline void Generic_FastCopy(
const SendProp *pSendProp,
const RecvProp *pRecvProp,
const unsigned char *pSendData,
unsigned char *pRecvData,
int objectID )
{
// Get the data out of the ent.
CRecvProxyData recvProxyData;
pSendProp->GetProxyFn()(
pSendProp,
pSendData,
pSendData + pSendProp->GetOffset(),
&recvProxyData.m_Value,
0,
objectID
);
// Fill in the data for the recv proxy.
recvProxyData.m_pRecvProp = pRecvProp;
recvProxyData.m_iElement = 0;
recvProxyData.m_ObjectID = objectID;
pRecvProp->GetProxyFn()( &recvProxyData, pRecvData, pRecvData + pRecvProp->GetOffset() );
}
// ---------------------------------------------------------------------------------------- //
// DecodeInfo implementation.
// ---------------------------------------------------------------------------------------- //
void DecodeInfo::CopyVars( const DecodeInfo *pOther )
{
m_pStruct = pOther->m_pStruct;
m_pData = pOther->m_pData;
m_pRecvProp = pOther->m_pRecvProp;
m_pProp = pOther->m_pProp;
m_pIn = pOther->m_pIn;
m_ObjectID = pOther->m_ObjectID;
m_iElement = pOther->m_iElement;
}
// ---------------------------------------------------------------------------------------- //
// Int property type abstraction.
// ---------------------------------------------------------------------------------------- //
void Int_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
int nValue = pVar->m_Int;
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
pOut->WriteVarInt32( nValue );
}
else
{
pOut->WriteSignedVarInt32( nValue );
}
}
else
{
// If signed, preserve lower bits and then re-extend sign if nValue < 0;
// if unsigned, preserve all 32 bits no matter what. Bonus: branchless.
int nPreserveBits = ( 0x7FFFFFFF >> ( 32 - pProp->m_nBits ) );
nPreserveBits |= ( pProp->GetFlags() & SPROP_UNSIGNED ) ? 0xFFFFFFFF : 0;
int nSignExtension = ( nValue >> 31 ) & ~nPreserveBits;
nValue &= nPreserveBits;
nValue |= nSignExtension;
#ifdef DBGFLAG_ASSERT
// Assert that either the property is unsigned and in valid range,
// or signed with a consistent sign extension in the high bits
if ( pProp->m_nBits < 32 )
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
AssertMsg3( nValue == pVar->m_Int, "Unsigned prop %s needs more bits? Expected %i == %i", pProp->GetName(), nValue, pVar->m_Int );
}
else
{
AssertMsg3( nValue == pVar->m_Int, "Signed prop %s needs more bits? Expected %i == %i", pProp->GetName(), nValue, pVar->m_Int );
}
}
else
{
// This should never trigger, but I'm leaving it in for old-time's sake.
Assert( nValue == pVar->m_Int );
}
#endif
pOut->WriteUBitLong( nValue, pProp->m_nBits, false );
}
}
void Int_Decode( DecodeInfo *pInfo )
{
const SendProp *pProp = pInfo->m_pProp;
int flags = pProp->GetFlags();
if ( flags & SPROP_VARINT )
{
if ( flags & SPROP_UNSIGNED )
{
pInfo->m_Value.m_Int = (long)pInfo->m_pIn->ReadVarInt32();
}
else
{
pInfo->m_Value.m_Int = pInfo->m_pIn->ReadSignedVarInt32();
}
}
else
{
int bits = pProp->m_nBits;
pInfo->m_Value.m_Int = pInfo->m_pIn->ReadUBitLong(bits);
if( bits != 32 && (flags & SPROP_UNSIGNED) == 0 )
{
unsigned long highbit = 1ul << (pProp->m_nBits - 1);
if ( pInfo->m_Value.m_Int & highbit )
{
pInfo->m_Value.m_Int -= highbit; // strip high bit...
pInfo->m_Value.m_Int -= highbit; // ... then put it back with sign extension
}
}
}
if ( pInfo->m_pRecvProp )
{
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
}
int Int_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
return p1->ReadVarInt32() != p2->ReadVarInt32();
}
return p1->ReadSignedVarInt32() != p2->ReadSignedVarInt32();
}
return p1->CompareBits(p2, pProp->m_nBits);
}
const char* Int_GetTypeNameString()
{
return "DPT_Int";
}
bool Int_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
return (pVar->m_Int == 0);
}
void Int_DecodeZero( DecodeInfo *pInfo )
{
pInfo->m_Value.m_Int = 0;
if ( pInfo->m_pRecvProp )
{
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
}
bool Int_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
return pIn->ReadVarInt32() == 0;
}
return pIn->ReadSignedVarInt32() == 0;
}
return pIn->ReadUBitLong( pProp->m_nBits ) == 0;
}
void Int_SkipProp( const SendProp *pProp, bf_read *pIn )
{
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
pIn->ReadVarInt32();
}
else
{
pIn->ReadSignedVarInt32();
}
}
else
{
pIn->SeekRelative( pProp->m_nBits );
}
}
// ---------------------------------------------------------------------------------------- //
// Float type abstraction.
// ---------------------------------------------------------------------------------------- //
void Float_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
EncodeFloat( pProp, pVar->m_Float, pOut, objectID );
}
void Float_Decode( DecodeInfo *pInfo )
{
pInfo->m_Value.m_Float = DecodeFloat(pInfo->m_pProp, pInfo->m_pIn);
if ( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
int Float_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
int flags = pProp->GetFlags();
if ( flags & SPROP_COORD )
{
return p1->ReadBitCoordBits() != p2->ReadBitCoordBits();
}
else if ( flags & ( SPROP_COORD_MP | SPROP_COORD_MP_LOWPRECISION | SPROP_COORD_MP_INTEGRAL ) )
{
return p1->ReadBitCoordMPBits( (flags >> 15) & 1, (flags >> 14) & 1 )
!= p2->ReadBitCoordMPBits( (flags >> 15) & 1, (flags >> 14) & 1 );
}
else
{
int bits;
if ( flags & SPROP_NOSCALE )
bits = 32;
else if ( flags & SPROP_NORMAL )
bits = NORMAL_FRACTIONAL_BITS+1;
else
bits = pProp->m_nBits;
return p1->ReadUBitLong( bits ) != p2->ReadUBitLong( bits );
}
}
const char* Float_GetTypeNameString()
{
return "DPT_Float";
}
bool Float_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
return (pVar->m_Float == 0);
}
void Float_DecodeZero( DecodeInfo *pInfo )
{
pInfo->m_Value.m_Float = 0;
if ( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
bool Float_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
return DecodeFloat( pProp, pIn ) == 0.0f;
}
void Float_SkipProp( const SendProp *pProp, bf_read *pIn )
{
// Check for special flags..
if(pProp->GetFlags() & SPROP_COORD)
{
// Read the required integer and fraction flags
unsigned int val = pIn->ReadUBitLong(2);
// this reads two bits, the first bit (bit0 in this word) indicates integer part
// the second bit (bit1 in this word) indicates the fractional part
// If we got either parse them, otherwise it's a zero.
if ( val )
{
// sign bit
int seekDist = 1;
// If there's an integer, read it in
if ( val & 1 )
seekDist += COORD_INTEGER_BITS;
if ( val & 2 )
seekDist += COORD_FRACTIONAL_BITS;
pIn->SeekRelative( seekDist );
}
}
else if ( pProp->GetFlags() & SPROP_COORD_MP )
{
pIn->ReadBitCoordMP( false, false );
}
else if ( pProp->GetFlags() & SPROP_COORD_MP_LOWPRECISION )
{
pIn->ReadBitCoordMP( false, true );
}
else if ( pProp->GetFlags() & SPROP_COORD_MP_INTEGRAL )
{
pIn->ReadBitCoordMP( true, false );
}
else if(pProp->GetFlags() & SPROP_NOSCALE)
{
pIn->SeekRelative( 32 );
}
else if(pProp->GetFlags() & SPROP_NORMAL)
{
pIn->SeekRelative( NORMAL_FRACTIONAL_BITS + 1 );
}
else
{
pIn->SeekRelative( pProp->m_nBits );
}
}
// ---------------------------------------------------------------------------------------- //
// Vector type abstraction.
// ---------------------------------------------------------------------------------------- //
void Vector_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
EncodeFloat(pProp, pVar->m_Vector[0], pOut, objectID);
EncodeFloat(pProp, pVar->m_Vector[1], pOut, objectID);
// Don't write out the third component for normals
if ((pProp->GetFlags() & SPROP_NORMAL) == 0)
{
EncodeFloat(pProp, pVar->m_Vector[2], pOut, objectID);
}
else
{
// Write a sign bit for z instead!
int signbit = (pVar->m_Vector[2] <= -NORMAL_RESOLUTION);
pOut->WriteOneBit( signbit );
}
}
void Vector_Decode(DecodeInfo *pInfo)
{
DecodeVector( pInfo->m_pProp, pInfo->m_pIn, pInfo->m_Value.m_Vector );
if( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
int Vector_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
int c1 = Float_CompareDeltas( pProp, p1, p2 );
int c2 = Float_CompareDeltas( pProp, p1, p2 );
int c3;
if ( pProp->GetFlags() & SPROP_NORMAL )
{
c3 = p1->ReadOneBit() != p2->ReadOneBit();
}
else
{
c3 = Float_CompareDeltas( pProp, p1, p2 );
}
return c1 | c2 | c3;
}
const char* Vector_GetTypeNameString()
{
return "DPT_Vector";
}
bool Vector_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
return ( pVar->m_Vector[0] == 0 ) && ( pVar->m_Vector[1] == 0 ) && ( pVar->m_Vector[2] == 0 );
}
void Vector_DecodeZero( DecodeInfo *pInfo )
{
pInfo->m_Value.m_Vector[0] = 0;
pInfo->m_Value.m_Vector[1] = 0;
pInfo->m_Value.m_Vector[2] = 0;
if ( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
bool Vector_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
float v[3];
DecodeVector( pProp, pIn, v );
return ( v[0] == 0 ) && ( v[1] == 0 ) && ( v[2] == 0 );
}
void Vector_SkipProp( const SendProp *pProp, bf_read *pIn )
{
Float_SkipProp(pProp, pIn);
Float_SkipProp(pProp, pIn);
// Don't read in the third component for normals
if ( pProp->GetFlags() & SPROP_NORMAL )
{
pIn->SeekRelative( 1 );
}
else
{
Float_SkipProp(pProp, pIn);
}
}
// ---------------------------------------------------------------------------------------- //
// VectorXY type abstraction.
// ---------------------------------------------------------------------------------------- //
void VectorXY_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
EncodeFloat(pProp, pVar->m_Vector[0], pOut, objectID);
EncodeFloat(pProp, pVar->m_Vector[1], pOut, objectID);
}
void VectorXY_Decode(DecodeInfo *pInfo)
{
pInfo->m_Value.m_Vector[0] = DecodeFloat(pInfo->m_pProp, pInfo->m_pIn);
pInfo->m_Value.m_Vector[1] = DecodeFloat(pInfo->m_pProp, pInfo->m_pIn);
if( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
int VectorXY_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
int c1 = Float_CompareDeltas( pProp, p1, p2 );
int c2 = Float_CompareDeltas( pProp, p1, p2 );
return c1 | c2;
}
const char* VectorXY_GetTypeNameString()
{
return "DPT_VectorXY";
}
bool VectorXY_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
return ( pVar->m_Vector[0] == 0 ) && ( pVar->m_Vector[1] == 0 );
}
void VectorXY_DecodeZero( DecodeInfo *pInfo )
{
pInfo->m_Value.m_Vector[0] = 0;
pInfo->m_Value.m_Vector[1] = 0;
if ( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
bool VectorXY_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
float v[2];
v[0] = DecodeFloat(pProp, pIn);
v[1] = DecodeFloat(pProp, pIn);
return ( v[0] == 0 ) && ( v[1] == 0 );
}
void VectorXY_SkipProp( const SendProp *pProp, bf_read *pIn )
{
Float_SkipProp(pProp, pIn);
Float_SkipProp(pProp, pIn);
}
#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!!
// ---------------------------------------------------------------------------------------- //
// Quaternion type abstraction.
// ---------------------------------------------------------------------------------------- //
void Quaternion_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
EncodeFloat(pProp, pVar->m_Vector[0], pOut, objectID);
EncodeFloat(pProp, pVar->m_Vector[1], pOut, objectID);
EncodeFloat(pProp, pVar->m_Vector[2], pOut, objectID);
EncodeFloat(pProp, pVar->m_Vector[3], pOut, objectID);
}
void Quaternion_Decode(DecodeInfo *pInfo)
{
DecodeQuaternion( pInfo->m_pProp, pInfo->m_pIn, pInfo->m_Value.m_Vector );
if( pInfo->m_pRecvProp )
{
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
}
int Quaternion_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
int c1 = Float_CompareDeltas( pProp, p1, p2 );
int c2 = Float_CompareDeltas( pProp, p1, p2 );
int c3 = Float_CompareDeltas( pProp, p1, p2 );
int c4 = Float_CompareDeltas( pProp, p1, p2 );
return c1 | c2 | c3 | c4;
}
const char* Quaternion_GetTypeNameString()
{
return "DPT_Quaternion";
}
bool Quaternion_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
return ( pVar->m_Vector[0] == 0 ) && ( pVar->m_Vector[1] == 0 ) && ( pVar->m_Vector[2] == 0 ) && ( pVar->m_Vector[3] == 0 );
}
void Quaternion_DecodeZero( DecodeInfo *pInfo )
{
pInfo->m_Value.m_Vector[0] = 0;
pInfo->m_Value.m_Vector[1] = 0;
pInfo->m_Value.m_Vector[2] = 0;
pInfo->m_Value.m_Vector[3] = 0;
if ( pInfo->m_pRecvProp )
{
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
}
bool Quaternion_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
float v[4];
DecodeQuaternion( pProp, pIn, v );
return ( v[0] == 0 ) && ( v[1] == 0 ) && ( v[2] == 0 ) && ( v[3] == 0 );
}
void Quaternion_SkipProp( const SendProp *pProp, bf_read *pIn )
{
Float_SkipProp(pProp, pIn);
Float_SkipProp(pProp, pIn);
Float_SkipProp(pProp, pIn);
Float_SkipProp(pProp, pIn);
}
#endif
// ---------------------------------------------------------------------------------------- //
// String type abstraction.
// ---------------------------------------------------------------------------------------- //
void String_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
// First count the string length, then do one WriteBits call.
int len;
for ( len=0; len < DT_MAX_STRING_BUFFERSIZE-1; len++ )
{
if( pVar->m_pString[len] == 0 )
{
break;
}
}
// Optionally write the length here so deltas can be compared faster.
pOut->WriteUBitLong( len, DT_MAX_STRING_BITS );
pOut->WriteBits( pVar->m_pString, len * 8 );
}
void String_Decode(DecodeInfo *pInfo)
{
// Read it in.
int len = pInfo->m_pIn->ReadUBitLong( DT_MAX_STRING_BITS );
char *tempStr = pInfo->m_TempStr;
if ( len >= DT_MAX_STRING_BUFFERSIZE )
{
Warning( "String_Decode( %s ) invalid length (%d)\n", pInfo->m_pRecvProp->GetName(), len );
len = DT_MAX_STRING_BUFFERSIZE - 1;
}
pInfo->m_pIn->ReadBits( tempStr, len*8 );
tempStr[len] = 0;
pInfo->m_Value.m_pString = tempStr;
// Give it to the RecvProxy.
if ( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
// Compare the bits in pBuf1 and pBuf2 and return 1 if they are different.
// This must always seek both buffers to wherever they start at + nBits.
static inline int AreBitsDifferent( bf_read *pBuf1, bf_read *pBuf2, int nBits )
{
int nDWords = nBits >> 5;
int diff = 0;
for ( int iDWord=0; iDWord < nDWords; iDWord++ )
{
diff |= (pBuf1->ReadUBitLong(32) != pBuf2->ReadUBitLong(32));
}
int nRemainingBits = nBits - (nDWords<<5);
if (nRemainingBits > 0)
diff |= pBuf1->ReadUBitLong( nRemainingBits ) != pBuf2->ReadUBitLong( nRemainingBits );
return diff;
}
int String_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
int len1 = p1->ReadUBitLong( DT_MAX_STRING_BITS );
int len2 = p2->ReadUBitLong( DT_MAX_STRING_BITS );
if ( len1 == len2 )
{
// check if both strings are empty
if (len1 == 0)
return false;
// Ok, they're short and fast.
return AreBitsDifferent( p1, p2, len1*8 );
}
else
{
p1->SeekRelative( len1 * 8 );
p2->SeekRelative( len2 * 8 );
return true;
}
}
const char* String_GetTypeNameString()
{
return "DPT_String";
}
bool String_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
return ( pVar->m_pString[0] == 0 );
}
void String_DecodeZero( DecodeInfo *pInfo )
{
pInfo->m_Value.m_pString = pInfo->m_TempStr;
pInfo->m_TempStr[0] = 0;
if ( pInfo->m_pRecvProp )
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
bool String_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
// Read it in.
int len = pIn->ReadUBitLong( DT_MAX_STRING_BITS );
pIn->SeekRelative( len*8 );
return len == 0;
}
void String_SkipProp( const SendProp *pProp, bf_read *pIn )
{
int len = pIn->ReadUBitLong( DT_MAX_STRING_BITS );
pIn->SeekRelative( len*8 );
}
// ---------------------------------------------------------------------------------------- //
// Array abstraction.
// ---------------------------------------------------------------------------------------- //
int Array_GetLength( const unsigned char *pStruct, const SendProp *pProp, int objectID )
{
// Get the array length from the proxy.
ArrayLengthSendProxyFn proxy = pProp->GetArrayLengthProxy();
if ( proxy )
{
int nElements = proxy( pStruct, objectID );
// Make sure it's not too big.
if ( nElements > pProp->GetNumElements() )
{
Assert( false );
nElements = pProp->GetNumElements();
}
return nElements;
}
else
{
return pProp->GetNumElements();
}
}
void Array_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
SendProp *pArrayProp = pProp->GetArrayProp();
AssertMsg( pArrayProp, "Array_Encode: missing m_pArrayProp for SendProp '%s'.", pProp->m_pVarName );
int nElements = Array_GetLength( pStruct, pProp, objectID );
// Write the number of elements.
pOut->WriteUBitLong( nElements, pProp->GetNumArrayLengthBits() );
unsigned char *pCurStructOffset = (unsigned char*)pStruct + pArrayProp->GetOffset();
for ( int iElement=0; iElement < nElements; iElement++ )
{
DVariant var;
// Call the proxy to get the value, then encode.
pArrayProp->GetProxyFn()( pArrayProp, pStruct, pCurStructOffset, &var, iElement, objectID );
g_PropTypeFns[pArrayProp->GetType()].Encode( pStruct, &var, pArrayProp, pOut, objectID );
pCurStructOffset += pProp->GetElementStride();
}
}
void Array_Decode( DecodeInfo *pInfo )
{
SendProp *pArrayProp = pInfo->m_pProp->GetArrayProp();
AssertMsg( pArrayProp, ("Array_Decode: missing m_pArrayProp for a property.") );
// Setup a DecodeInfo that is used to decode each of the child properties.
DecodeInfo subDecodeInfo;
subDecodeInfo.CopyVars( pInfo );
subDecodeInfo.m_pProp = pArrayProp;
int elementStride = 0;
ArrayLengthRecvProxyFn lengthProxy = 0;
if ( pInfo->m_pRecvProp )
{
RecvProp *pArrayRecvProp = pInfo->m_pRecvProp->GetArrayProp();
subDecodeInfo.m_pRecvProp = pArrayRecvProp;
// Note we get the OFFSET from the array element property and the STRIDE from the array itself.
subDecodeInfo.m_pData = (char*)pInfo->m_pData + pArrayRecvProp->GetOffset();
elementStride = pInfo->m_pRecvProp->GetElementStride();
Assert( elementStride != -1 ); // (Make sure it was set..)
lengthProxy = pInfo->m_pRecvProp->GetArrayLengthProxy();
}
int nElements = pInfo->m_pIn->ReadUBitLong( pInfo->m_pProp->GetNumArrayLengthBits() );
if ( lengthProxy )
lengthProxy( pInfo->m_pStruct, pInfo->m_ObjectID, nElements );
for ( subDecodeInfo.m_iElement=0; subDecodeInfo.m_iElement < nElements; subDecodeInfo.m_iElement++ )
{
g_PropTypeFns[pArrayProp->GetType()].Decode( &subDecodeInfo );
subDecodeInfo.m_pData = (char*)subDecodeInfo.m_pData + elementStride;
}
}
int Array_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
SendProp *pArrayProp = pProp->GetArrayProp();
AssertMsg( pArrayProp, "Array_CompareDeltas: missing m_pArrayProp for SendProp '%s'.", pProp->m_pVarName );
int nLengthBits = pProp->GetNumArrayLengthBits();
int length1 = p1->ReadUBitLong( nLengthBits );
int length2 = p2->ReadUBitLong( nLengthBits );
int bDifferent = length1 != length2;
// Compare deltas on the props that are the same.
int nSame = min( length1, length2 );
for ( int iElement=0; iElement < nSame; iElement++ )
{
bDifferent |= g_PropTypeFns[pArrayProp->GetType()].CompareDeltas( pArrayProp, p1, p2 );
}
// Now just eat up the remaining properties in whichever buffer was larger.
if ( length1 != length2 )
{
bf_read *buffer = (length1 > length2) ? p1 : p2;
int nExtra = max( length1, length2 ) - nSame;
for ( int iEatUp=0; iEatUp < nExtra; iEatUp++ )
{
SkipPropData( buffer, pArrayProp );
}
}
return bDifferent;
}
void Array_FastCopy(
const SendProp *pSendProp,
const RecvProp *pRecvProp,
const unsigned char *pSendData,
unsigned char *pRecvData,
int objectID )
{
const RecvProp *pArrayRecvProp = pRecvProp->GetArrayProp();
const SendProp *pArraySendProp = pSendProp->GetArrayProp();
CRecvProxyData recvProxyData;
recvProxyData.m_pRecvProp = pArrayRecvProp;
recvProxyData.m_ObjectID = objectID;
// Find out the array length and call the RecvProp's array-length proxy.
int nElements = Array_GetLength( pSendData, pSendProp, objectID );
ArrayLengthRecvProxyFn lengthProxy = pRecvProp->GetArrayLengthProxy();
if ( lengthProxy )
lengthProxy( pRecvData, objectID, nElements );
const unsigned char *pCurSendPos = pSendData + pArraySendProp->GetOffset();
unsigned char *pCurRecvPos = pRecvData + pArrayRecvProp->GetOffset();
for ( recvProxyData.m_iElement=0; recvProxyData.m_iElement < nElements; recvProxyData.m_iElement++ )
{
// Get this array element out of the sender's data.
pArraySendProp->GetProxyFn()( pArraySendProp, pSendData, pCurSendPos, &recvProxyData.m_Value, recvProxyData.m_iElement, objectID );
pCurSendPos += pSendProp->GetElementStride();
// Write it into the receiver.
pArrayRecvProp->GetProxyFn()( &recvProxyData, pRecvData, pCurRecvPos );
pCurRecvPos += pRecvProp->GetElementStride();
}
}
const char* Array_GetTypeNameString()
{
return "DPT_Array";
}
bool Array_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
int nElements = Array_GetLength( pStruct, pProp, -1 );
return ( nElements == 0 );
}
void Array_DecodeZero( DecodeInfo *pInfo )
{
ArrayLengthRecvProxyFn lengthProxy = pInfo->m_pRecvProp->GetArrayLengthProxy();
if ( lengthProxy )
lengthProxy( pInfo->m_pStruct, pInfo->m_ObjectID, 0 );
}
bool Array_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
SendProp *pArrayProp = pProp->GetArrayProp();
AssertMsg( pArrayProp, ("Array_IsEncodedZero: missing m_pArrayProp for a property.") );
int nElements = pIn->ReadUBitLong( pProp->GetNumArrayLengthBits() );
for ( int i=0; i < nElements; i++ )
{
// skip over data
g_PropTypeFns[pArrayProp->GetType()].IsEncodedZero( pArrayProp, pIn );
}
return nElements == 0;;
}
void Array_SkipProp( const SendProp *pProp, bf_read *pIn )
{
SendProp *pArrayProp = pProp->GetArrayProp();
AssertMsg( pArrayProp, ("Array_SkipProp: missing m_pArrayProp for a property.") );
int nElements = pIn->ReadUBitLong( pProp->GetNumArrayLengthBits() );
for ( int i=0; i < nElements; i++ )
{
// skip over data
g_PropTypeFns[pArrayProp->GetType()].SkipProp( pArrayProp, pIn );
}
}
// ---------------------------------------------------------------------------------------- //
// Datatable type abstraction.
// ---------------------------------------------------------------------------------------- //
const char* DataTable_GetTypeNameString()
{
return "DPT_DataTable";
}
// ---------------------------------------------------------------------------------------- //
// Int 64 property type abstraction.
// ---------------------------------------------------------------------------------------- //
void Int64_Encode( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp, bf_write *pOut, int objectID )
{
#ifdef SUPPORTS_INT64
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
pOut->WriteVarInt64( pVar->m_Int64 );
}
else
{
pOut->WriteSignedVarInt32( pVar->m_Int64 );
}
}
else
{
bool bNeg = pVar->m_Int64 < 0;
int64 iCopy = bNeg ? -pVar->m_Int64 : pVar->m_Int64;
uint32 *pInt = (uint32*)&iCopy;
uint32 lowInt = *pInt++;
uint32 highInt = *pInt;
if( pProp->IsSigned() )
{
pOut->WriteOneBit( bNeg );
pOut->WriteUBitLong( (unsigned int)lowInt, 32 );
pOut->WriteUBitLong( (unsigned int)highInt, pProp->m_nBits - 32 - 1 ); // For the sign bit
}
else
{
pOut->WriteUBitLong( (unsigned int)lowInt, 32 );
pOut->WriteUBitLong( (unsigned int)highInt, pProp->m_nBits - 32 );
}
}
#endif
}
void Int64_Decode( DecodeInfo *pInfo )
{
#ifdef SUPPORTS_INT64
if ( pInfo->m_pProp->GetFlags() & SPROP_VARINT )
{
if ( pInfo->m_pProp->GetFlags() & SPROP_UNSIGNED )
{
pInfo->m_Value.m_Int64 = (int64)pInfo->m_pIn->ReadVarInt64();
}
else
{
pInfo->m_Value.m_Int64 = pInfo->m_pIn->ReadSignedVarInt64();
}
}
else
{
uint32 highInt = 0;
uint32 lowInt = 0;
bool bNeg = false;
if(pInfo->m_pProp->IsSigned())
{
bNeg = pInfo->m_pIn->ReadOneBit() != 0;
lowInt = pInfo->m_pIn->ReadUBitLong( 32 );
highInt = pInfo->m_pIn->ReadUBitLong( pInfo->m_pProp->m_nBits - 32 - 1 );
}
else
{
lowInt = pInfo->m_pIn->ReadUBitLong( 32 );
highInt = pInfo->m_pIn->ReadUBitLong( pInfo->m_pProp->m_nBits - 32 );
}
uint32 *pInt = (uint32*)&pInfo->m_Value.m_Int64;
*pInt++ = lowInt;
*pInt = highInt;
if ( bNeg )
{
pInfo->m_Value.m_Int64 = -pInfo->m_Value.m_Int64;
}
}
if ( pInfo->m_pRecvProp )
{
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
#endif
}
int Int64_CompareDeltas( const SendProp *pProp, bf_read *p1, bf_read *p2 )
{
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
return p1->ReadVarInt64() != p2->ReadVarInt64();
}
return p1->ReadSignedVarInt64() != p2->ReadSignedVarInt64();
}
uint32 highInt1 = p1->ReadUBitLong( pProp->m_nBits - 32 );
uint32 lowInt1 = p1->ReadUBitLong( 32 );
uint32 highInt2 = p2->ReadUBitLong( pProp->m_nBits - 32 );
uint32 lowInt2 = p2->ReadUBitLong( 32 );
return highInt1 != highInt2 || lowInt1 != lowInt2;
}
const char* Int64_GetTypeNameString()
{
return "DPT_Int64";
}
bool Int64_IsZero( const unsigned char *pStruct, DVariant *pVar, const SendProp *pProp )
{
#ifdef SUPPORTS_INT64
return (pVar->m_Int64 == 0);
#else
return false;
#endif
}
void Int64_DecodeZero( DecodeInfo *pInfo )
{
#ifdef SUPPORTS_INT64
pInfo->m_Value.m_Int64 = 0;
if ( pInfo->m_pRecvProp )
{
pInfo->m_pRecvProp->GetProxyFn()( pInfo, pInfo->m_pStruct, pInfo->m_pData );
}
#endif
}
bool Int64_IsEncodedZero( const SendProp *pProp, bf_read *pIn )
{
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
return pIn->ReadVarInt64() == 0;
}
return pIn->ReadSignedVarInt64() == 0;
}
uint32 highInt1 = pIn->ReadUBitLong( pProp->m_nBits - 32 );
uint32 lowInt1 = pIn->ReadUBitLong( 32 );
return (highInt1 == 0 && lowInt1 == 0);
}
void Int64_SkipProp( const SendProp *pProp, bf_read *pIn )
{
if ( pProp->GetFlags() & SPROP_VARINT)
{
if ( pProp->GetFlags() & SPROP_UNSIGNED )
{
pIn->ReadVarInt64();
}
else
{
pIn->ReadSignedVarInt64();
}
}
else
{
pIn->SeekRelative( pProp->m_nBits );
}
}
PropTypeFns g_PropTypeFns[DPT_NUMSendPropTypes] =
{
// DPT_Int
{
Int_Encode,
Int_Decode,
Int_CompareDeltas,
Generic_FastCopy,
Int_GetTypeNameString,
Int_IsZero,
Int_DecodeZero,
Int_IsEncodedZero,
Int_SkipProp,
},
// DPT_Float
{
Float_Encode,
Float_Decode,
Float_CompareDeltas,
Generic_FastCopy,
Float_GetTypeNameString,
Float_IsZero,
Float_DecodeZero,
Float_IsEncodedZero,
Float_SkipProp,
},
// DPT_Vector
{
Vector_Encode,
Vector_Decode,
Vector_CompareDeltas,
Generic_FastCopy,
Vector_GetTypeNameString,
Vector_IsZero,
Vector_DecodeZero,
Vector_IsEncodedZero,
Vector_SkipProp,
},
// DPT_VectorXY
{
VectorXY_Encode,
VectorXY_Decode,
VectorXY_CompareDeltas,
Generic_FastCopy,
VectorXY_GetTypeNameString,
VectorXY_IsZero,
VectorXY_DecodeZero,
VectorXY_IsEncodedZero,
VectorXY_SkipProp,
},
// DPT_String
{
String_Encode,
String_Decode,
String_CompareDeltas,
Generic_FastCopy,
String_GetTypeNameString,
String_IsZero,
String_DecodeZero,
String_IsEncodedZero,
String_SkipProp,
},
// DPT_Array
{
Array_Encode,
Array_Decode,
Array_CompareDeltas,
Array_FastCopy,
Array_GetTypeNameString,
Array_IsZero,
Array_DecodeZero,
Array_IsEncodedZero,
Array_SkipProp,
},
// DPT_DataTable
{
NULL,
NULL,
NULL,
NULL,
DataTable_GetTypeNameString,
NULL,
NULL,
NULL,
NULL,
},
#if 0 // We can't ship this since it changes the size of DTVariant to be 20 bytes instead of 16 and that breaks MODs!!!
// DPT_Quaternion
{
Quaternion_Encode,
Quaternion_Decode,
Quaternion_CompareDeltas,
Generic_FastCopy,
Quaternion_GetTypeNameString,
Quaternion_IsZero,
Quaternion_DecodeZero,
Quaternion_IsEncodedZero,
Quaternion_SkipProp,
},
#endif
#ifdef SUPPORTS_INT64
// DPT_Int64
{
Int64_Encode,
Int64_Decode,
Int64_CompareDeltas,
Generic_FastCopy,
Int64_GetTypeNameString,
Int64_IsZero,
Int64_DecodeZero,
Int64_IsEncodedZero,
Int64_SkipProp,
},
#endif
};