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.
571 lines
13 KiB
571 lines
13 KiB
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// $Header: $
|
|
// $NoKeywords: $
|
|
//
|
|
// Serialization buffer
|
|
//===========================================================================//
|
|
|
|
#pragma warning (disable : 4514)
|
|
|
|
#include "tier1/utlbufferutil.h"
|
|
#include "tier1/utlbuffer.h"
|
|
#include "mathlib/vector.h"
|
|
#include "mathlib/vector2d.h"
|
|
#include "mathlib/vector4d.h"
|
|
#include "mathlib/vmatrix.h"
|
|
#include "color.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include "tier1/utlstring.h"
|
|
#include "tier1/strtools.h"
|
|
#include "tier1/characterset.h"
|
|
#include "tier1/utlsymbollarge.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// For serialization, set the delimiter rules
|
|
//-----------------------------------------------------------------------------
|
|
CUtlCharConversion *s_pConv = NULL;
|
|
const char *s_pUtlBufferUtilArrayDelim = NULL;
|
|
void SetSerializationDelimiter( CUtlCharConversion *pConv )
|
|
{
|
|
s_pConv = pConv;
|
|
}
|
|
|
|
void SetSerializationArrayDelimiter( const char *pDelimiter )
|
|
{
|
|
s_pUtlBufferUtilArrayDelim = pDelimiter;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Serialize a floating point number in text mode in a readably friendly fashion
|
|
//-----------------------------------------------------------------------------
|
|
static void SerializeFloat( CUtlBuffer &buf, float f )
|
|
{
|
|
Assert( buf.IsText() );
|
|
|
|
// FIXME: Print this in a way that we never lose precision
|
|
char pTemp[256];
|
|
int nLen = Q_snprintf( pTemp, sizeof(pTemp), "%.10f", f );
|
|
while ( nLen > 0 && pTemp[nLen-1] == '0' )
|
|
{
|
|
--nLen;
|
|
pTemp[nLen] = 0;
|
|
}
|
|
if ( nLen > 0 && pTemp[nLen-1] == '.' )
|
|
{
|
|
--nLen;
|
|
pTemp[nLen] = 0;
|
|
}
|
|
buf.PutString( pTemp );
|
|
}
|
|
|
|
static void SerializeFloats( CUtlBuffer &buf, int nCount, const float *pFloats )
|
|
{
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
SerializeFloat( buf, pFloats[i] );
|
|
if ( i != nCount-1 )
|
|
{
|
|
buf.PutChar( ' ' );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Serialization methods for basic types
|
|
//-----------------------------------------------------------------------------
|
|
bool Serialize( CUtlBuffer &buf, const bool &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
buf.Printf( "%d", src );
|
|
}
|
|
else
|
|
{
|
|
buf.PutChar( src );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, bool &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
int nValue = 0;
|
|
int nRetVal = buf.Scanf( "%d", &nValue );
|
|
dest = ( nValue != 0 );
|
|
return (nRetVal == 1) && buf.IsValid();
|
|
}
|
|
|
|
dest = ( buf.GetChar( ) != 0 );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
|
|
bool Serialize( CUtlBuffer &buf, const int &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
buf.Printf( "%d", src );
|
|
}
|
|
else
|
|
{
|
|
buf.PutInt( src );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, int &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
int nRetVal = buf.Scanf( "%d", &dest );
|
|
return (nRetVal == 1) && buf.IsValid();
|
|
}
|
|
|
|
dest = buf.GetInt( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Serialize( CUtlBuffer &buf, const float &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
SerializeFloat( buf, src );
|
|
}
|
|
else
|
|
{
|
|
buf.PutFloat( src );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, float &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
// FIXME: Print this in a way that we never lose precision
|
|
int nRetVal = buf.Scanf( "%f", &dest );
|
|
return (nRetVal == 1) && buf.IsValid();
|
|
}
|
|
|
|
dest = buf.GetFloat( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Attribute types related to vector math
|
|
//-----------------------------------------------------------------------------
|
|
bool Serialize( CUtlBuffer &buf, const Vector2D &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
SerializeFloats( buf, 2, src.Base() );
|
|
}
|
|
else
|
|
{
|
|
buf.PutFloat( src.x );
|
|
buf.PutFloat( src.y );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, Vector2D &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
// FIXME: Print this in a way that we never lose precision
|
|
int nRetVal = buf.Scanf( "%f %f", &dest.x, &dest.y );
|
|
return (nRetVal == 2) && buf.IsValid();
|
|
}
|
|
|
|
dest.x = buf.GetFloat( );
|
|
dest.y = buf.GetFloat( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Serialize( CUtlBuffer &buf, const Vector &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
SerializeFloats( buf, 3, src.Base() );
|
|
}
|
|
else
|
|
{
|
|
buf.PutFloat( src.x );
|
|
buf.PutFloat( src.y );
|
|
buf.PutFloat( src.z );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, Vector &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
// FIXME: Print this in a way that we never lose precision
|
|
int nRetVal = buf.Scanf( "%f %f %f", &dest.x, &dest.y, &dest.z );
|
|
return (nRetVal == 3) && buf.IsValid();
|
|
}
|
|
|
|
dest.x = buf.GetFloat( );
|
|
dest.y = buf.GetFloat( );
|
|
dest.z = buf.GetFloat( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Serialize( CUtlBuffer &buf, const Vector4D &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
SerializeFloats( buf, 4, src.Base() );
|
|
}
|
|
else
|
|
{
|
|
buf.PutFloat( src.x );
|
|
buf.PutFloat( src.y );
|
|
buf.PutFloat( src.z );
|
|
buf.PutFloat( src.w );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, Vector4D &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
// FIXME: Print this in a way that we never lose precision
|
|
int nRetVal = buf.Scanf( "%f %f %f %f", &dest.x, &dest.y, &dest.z, &dest.w );
|
|
return (nRetVal == 4) && buf.IsValid();
|
|
}
|
|
|
|
dest.x = buf.GetFloat( );
|
|
dest.y = buf.GetFloat( );
|
|
dest.z = buf.GetFloat( );
|
|
dest.w = buf.GetFloat( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Serialize( CUtlBuffer &buf, const QAngle &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
SerializeFloats( buf, 3, src.Base() );
|
|
}
|
|
else
|
|
{
|
|
buf.PutFloat( src.x );
|
|
buf.PutFloat( src.y );
|
|
buf.PutFloat( src.z );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, QAngle &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
// FIXME: Print this in a way that we never lose precision
|
|
int nRetVal = buf.Scanf( "%f %f %f", &dest.x, &dest.y, &dest.z );
|
|
return (nRetVal == 3) && buf.IsValid();
|
|
}
|
|
|
|
dest.x = buf.GetFloat( );
|
|
dest.y = buf.GetFloat( );
|
|
dest.z = buf.GetFloat( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Serialize( CUtlBuffer &buf, const Quaternion &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
SerializeFloats( buf, 4, &src.x );
|
|
}
|
|
else
|
|
{
|
|
buf.PutFloat( src.x );
|
|
buf.PutFloat( src.y );
|
|
buf.PutFloat( src.z );
|
|
buf.PutFloat( src.w );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, Quaternion &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
// FIXME: Print this in a way that we never lose precision
|
|
int nRetVal = buf.Scanf( "%f %f %f %f", &dest.x, &dest.y, &dest.z, &dest.w );
|
|
QuaternionNormalize( dest );
|
|
return (nRetVal == 4) && buf.IsValid();
|
|
}
|
|
|
|
dest.x = buf.GetFloat( );
|
|
dest.y = buf.GetFloat( );
|
|
dest.z = buf.GetFloat( );
|
|
dest.w = buf.GetFloat( );
|
|
QuaternionNormalize( dest );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Serialize( CUtlBuffer &buf, const VMatrix &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
buf.Printf( "\n" );
|
|
SerializeFloats( buf, 4, src[0] );
|
|
buf.Printf( "\n" );
|
|
SerializeFloats( buf, 4, src[1] );
|
|
buf.Printf( "\n" );
|
|
SerializeFloats( buf, 4, src[2] );
|
|
buf.Printf( "\n" );
|
|
SerializeFloats( buf, 4, src[3] );
|
|
buf.Printf( "\n" );
|
|
}
|
|
else
|
|
{
|
|
buf.Put( &src, sizeof(VMatrix) );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, VMatrix &dest )
|
|
{
|
|
if ( !buf.IsValid() )
|
|
return false;
|
|
|
|
if ( buf.IsText() )
|
|
{
|
|
int nRetVal = buf.Scanf( "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
|
|
&dest[ 0 ][ 0 ], &dest[ 0 ][ 1 ], &dest[ 0 ][ 2 ], &dest[ 0 ][ 3 ],
|
|
&dest[ 1 ][ 0 ], &dest[ 1 ][ 1 ], &dest[ 1 ][ 2 ], &dest[ 1 ][ 3 ],
|
|
&dest[ 2 ][ 0 ], &dest[ 2 ][ 1 ], &dest[ 2 ][ 2 ], &dest[ 2 ][ 3 ],
|
|
&dest[ 3 ][ 0 ], &dest[ 3 ][ 1 ], &dest[ 3 ][ 2 ], &dest[ 3 ][ 3 ] );
|
|
return (nRetVal == 16);
|
|
}
|
|
|
|
buf.Get( &dest, sizeof(VMatrix) );
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Color attribute
|
|
//-----------------------------------------------------------------------------
|
|
bool Serialize( CUtlBuffer &buf, const Color &src )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
buf.Printf( "%d %d %d %d", src[0], src[1], src[2], src[3] );
|
|
}
|
|
else
|
|
{
|
|
buf.PutUnsignedChar( src[0] );
|
|
buf.PutUnsignedChar( src[1] );
|
|
buf.PutUnsignedChar( src[2] );
|
|
buf.PutUnsignedChar( src[3] );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, Color &dest )
|
|
{
|
|
if ( buf.IsText() )
|
|
{
|
|
int r = 0, g = 0, b = 0, a = 255;
|
|
int nRetVal = buf.Scanf( "%d %d %d %d", &r, &g, &b, &a );
|
|
dest.SetColor( r, g, b, a );
|
|
return (nRetVal == 4) && buf.IsValid();
|
|
}
|
|
|
|
dest[0] = buf.GetUnsignedChar( );
|
|
dest[1] = buf.GetUnsignedChar( );
|
|
dest[2] = buf.GetUnsignedChar( );
|
|
dest[3] = buf.GetUnsignedChar( );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
/*
|
|
//-----------------------------------------------------------------------------
|
|
// Object ID attribute
|
|
//-----------------------------------------------------------------------------
|
|
bool Serialize( CUtlBuffer &buf, const DmObjectId_t &src )
|
|
{
|
|
return g_pDataModel->Serialize( buf, src );
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, DmObjectId_t &dest )
|
|
{
|
|
return g_pDataModel->Unserialize( buf, &dest );
|
|
}
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Binary buffer attribute
|
|
//-----------------------------------------------------------------------------
|
|
bool Serialize( CUtlBuffer &buf, const CUtlBinaryBlock &src )
|
|
{
|
|
int nLength = src.Length();
|
|
if ( !buf.IsText() )
|
|
{
|
|
buf.PutInt( nLength );
|
|
if ( nLength != 0 )
|
|
{
|
|
buf.Put( src.Get(), nLength );
|
|
}
|
|
return buf.IsValid();
|
|
}
|
|
|
|
// Writes out uuencoded binaries
|
|
for ( int i = 0; i < nLength; ++i )
|
|
{
|
|
if ( (i % 40) == 0 )
|
|
{
|
|
buf.PutChar( '\n' );
|
|
}
|
|
|
|
char b1 = src[i] & 0xF;
|
|
char b2 = src[i] >> 4;
|
|
|
|
char c1 = ( b1 <= 9 ) ? b1 + '0' : b1 - 10 + 'A';
|
|
char c2 = ( b2 <= 9 ) ? b2 + '0' : b2 - 10 + 'A';
|
|
|
|
buf.PutChar( c2 );
|
|
buf.PutChar( c1 );
|
|
}
|
|
|
|
buf.PutChar( '\n' );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
static int CountBinaryBytes( CUtlBuffer &buf, int *pEndGet )
|
|
{
|
|
// This counts the number of bytes in the uuencoded text
|
|
int nStartGet = buf.TellGet();
|
|
buf.EatWhiteSpace();
|
|
*pEndGet = buf.TellGet();
|
|
int nByteCount = 0;
|
|
while ( buf.IsValid() )
|
|
{
|
|
char c1 = buf.GetChar();
|
|
char c2 = buf.GetChar();
|
|
|
|
bool bIsNum1 = ( c1 >= '0' ) && ( c1 <= '9' );
|
|
bool bIsNum2 = ( c2 >= '0' ) && ( c2 <= '9' );
|
|
|
|
bool bIsAlpha1 = (( c1 >= 'A' ) && ( c1 <= 'F' )) || (( c1 >= 'a' ) && ( c1 <= 'f' ));
|
|
bool bIsAlpha2 = (( c2 >= 'A' ) && ( c2 <= 'F' )) || (( c2 >= 'a' ) && ( c2 <= 'f' ));
|
|
|
|
if ( !(bIsNum1 || bIsAlpha1) || !(bIsNum2 || bIsAlpha2) )
|
|
break;
|
|
|
|
buf.EatWhiteSpace();
|
|
*pEndGet = buf.TellGet();
|
|
++nByteCount;
|
|
}
|
|
buf.SeekGet( CUtlBuffer::SEEK_HEAD, nStartGet );
|
|
return nByteCount;
|
|
}
|
|
|
|
inline static unsigned char HexCharToInt( int c1 )
|
|
{
|
|
if (( c1 >= '0' ) && ( c1 <= '9' ))
|
|
return c1 - '0';
|
|
|
|
if (( c1 >= 'A' ) && ( c1 <= 'F' ))
|
|
return 10 + c1 - 'A';
|
|
|
|
if (( c1 >= 'a' ) && ( c1 <= 'f' ))
|
|
return 10 + c1 - 'a';
|
|
|
|
return 0xFF;
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, CUtlBinaryBlock &dest )
|
|
{
|
|
if ( !buf.IsText() )
|
|
{
|
|
int nLen = buf.GetInt( );
|
|
dest.SetLength( nLen );
|
|
if ( dest.Length() != 0 )
|
|
{
|
|
buf.Get( dest.Get(), dest.Length() );
|
|
}
|
|
|
|
if ( nLen != dest.Length() )
|
|
{
|
|
buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nLen - dest.Length() );
|
|
return false;
|
|
}
|
|
|
|
return buf.IsValid();
|
|
}
|
|
|
|
int nEndGet = 0;
|
|
int nByteCount = CountBinaryBytes( buf, &nEndGet );
|
|
if ( nByteCount < 0 )
|
|
return false;
|
|
|
|
buf.EatWhiteSpace();
|
|
int nDest = 0;
|
|
dest.SetLength( nByteCount );
|
|
while( buf.TellGet() < nEndGet )
|
|
{
|
|
char c1 = buf.GetChar();
|
|
char c2 = buf.GetChar();
|
|
|
|
unsigned char b1 = HexCharToInt( c1 );
|
|
unsigned char b2 = HexCharToInt( c2 );
|
|
if ( b1 == 0xFF || b2 == 0xFF )
|
|
return false;
|
|
|
|
dest[ nDest++ ] = b2 | ( b1 << 4 );
|
|
buf.EatWhiteSpace();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// String attribute
|
|
//-----------------------------------------------------------------------------
|
|
bool Serialize( CUtlBuffer &buf, const CUtlString &src )
|
|
{
|
|
buf.PutDelimitedString( s_pConv, src.Get() );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
bool Unserialize( CUtlBuffer &buf, CUtlString &dest )
|
|
{
|
|
int nLen = buf.PeekDelimitedStringLength( s_pConv );
|
|
dest.SetLength( nLen - 1 ); // -1 because the length returned includes space for \0
|
|
buf.GetDelimitedString( s_pConv, dest.Get(), nLen );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
|
|
bool Serialize( CUtlBuffer &buf, const CUtlSymbolLarge &src )
|
|
{
|
|
// Serialization of symbols is allowed, but it writes that string of the symbol,
|
|
// not the symbol index. This is done so that text serialization of symbols will write
|
|
// the actual string and so that functions which rely on text serialization to convert
|
|
// an attribute value to text will get the proper string for the symbol.
|
|
buf.PutDelimitedString( s_pConv, src.String() );
|
|
return buf.IsValid();
|
|
}
|
|
|
|
|