summaryrefslogtreecommitdiff
path: root/dmxloader
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /dmxloader
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'dmxloader')
-rw-r--r--dmxloader/dmxattribute.cpp643
-rw-r--r--dmxloader/dmxelement.cpp489
-rw-r--r--dmxloader/dmxloader.cpp697
-rw-r--r--dmxloader/dmxloader.vpc41
-rw-r--r--dmxloader/dmxloadertext.cpp1442
-rw-r--r--dmxloader/dmxserializationdictionary.cpp188
-rw-r--r--dmxloader/dmxserializationdictionary.h76
7 files changed, 3576 insertions, 0 deletions
diff --git a/dmxloader/dmxattribute.cpp b/dmxloader/dmxattribute.cpp
new file mode 100644
index 0000000..aea474c
--- /dev/null
+++ b/dmxloader/dmxattribute.cpp
@@ -0,0 +1,643 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmxloader/dmxattribute.h"
+#include "tier1/utlbufferutil.h"
+#include "tier1/uniqueid.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// globals
+//-----------------------------------------------------------------------------
+CUtlSymbolTableMT CDmxAttribute::s_AttributeNameSymbols;
+
+
+//-----------------------------------------------------------------------------
+// Attribute size
+//-----------------------------------------------------------------------------
+static size_t s_pAttributeSize[AT_TYPE_COUNT] =
+{
+ 0, // AT_UNKNOWN,
+ sizeof(CDmxElement*), // AT_ELEMENT,
+ sizeof(int), // AT_INT,
+ sizeof(float), // AT_FLOAT,
+ sizeof(bool), // AT_BOOL,
+ sizeof(CUtlString), // AT_STRING,
+ sizeof(CUtlBinaryBlock), // AT_VOID,
+ sizeof(DmObjectId_t), // AT_OBJECTID,
+ sizeof(Color), // AT_COLOR,
+ sizeof(Vector2D), // AT_VECTOR2,
+ sizeof(Vector), // AT_VECTOR3,
+ sizeof(Vector4D), // AT_VECTOR4,
+ sizeof(QAngle), // AT_QANGLE,
+ sizeof(Quaternion), // AT_QUATERNION,
+ sizeof(VMatrix), // AT_VMATRIX,
+ sizeof(CUtlVector<CDmxElement*>), // AT_ELEMENT_ARRAY,
+ sizeof(CUtlVector<int>), // AT_INT_ARRAY,
+ sizeof(CUtlVector<float>), // AT_FLOAT_ARRAY,
+ sizeof(CUtlVector<bool>), // AT_BOOL_ARRAY,
+ sizeof(CUtlVector<CUtlString>), // AT_STRING_ARRAY,
+ sizeof(CUtlVector<CUtlBinaryBlock>), // AT_VOID_ARRAY,
+ sizeof(CUtlVector<DmObjectId_t>), // AT_OBJECTID_ARRAY,
+ sizeof(CUtlVector<Color>), // AT_COLOR_ARRAY,
+ sizeof(CUtlVector<Vector2D>), // AT_VECTOR2_ARRAY,
+ sizeof(CUtlVector<Vector>), // AT_VECTOR3_ARRAY,
+ sizeof(CUtlVector<Vector4D>), // AT_VECTOR4_ARRAY,
+ sizeof(CUtlVector<QAngle>), // AT_QANGLE_ARRAY,
+ sizeof(CUtlVector<Quaternion>), // AT_QUATERNION_ARRAY,
+ sizeof(CUtlVector<VMatrix>), // AT_VMATRIX_ARRAY,
+};
+
+
+//-----------------------------------------------------------------------------
+// make sure that the attribute data type sizes are what we think they are to choose the right allocator
+//-----------------------------------------------------------------------------
+struct CSizeTest
+{
+ CSizeTest()
+ {
+ // test internal value attribute sizes
+ COMPILE_TIME_ASSERT( sizeof( int ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( float ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( bool ) <= 4 );
+ COMPILE_TIME_ASSERT( sizeof( Color ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( Vector2D ) == 8 );
+ COMPILE_TIME_ASSERT( sizeof( Vector ) == 12 );
+ COMPILE_TIME_ASSERT( sizeof( Vector4D ) == 16 );
+ COMPILE_TIME_ASSERT( sizeof( QAngle ) == 12 );
+ COMPILE_TIME_ASSERT( sizeof( Quaternion ) == 16 );
+ COMPILE_TIME_ASSERT( sizeof( VMatrix ) == 64 );
+ COMPILE_TIME_ASSERT( sizeof( CUtlString ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( CUtlBinaryBlock ) == 16 );
+ COMPILE_TIME_ASSERT( sizeof( DmObjectId_t ) == 16 );
+ };
+};
+static CSizeTest g_sizeTest;
+
+
+//-----------------------------------------------------------------------------
+// Returns attribute name for type
+//-----------------------------------------------------------------------------
+const char *g_pAttributeTypeName[AT_TYPE_COUNT] =
+{
+ "unknown", // AT_UNKNOWN
+ CDmAttributeInfo<DmElementHandle_t>::AttributeTypeName(), // AT_ELEMENT,
+ CDmAttributeInfo<int>::AttributeTypeName(), // AT_INT,
+ CDmAttributeInfo<float>::AttributeTypeName(), // AT_FLOAT,
+ CDmAttributeInfo<bool>::AttributeTypeName(), // AT_BOOL,
+ CDmAttributeInfo<CUtlString>::AttributeTypeName(), // AT_STRING,
+ CDmAttributeInfo<CUtlBinaryBlock>::AttributeTypeName(), // AT_VOID,
+ CDmAttributeInfo<DmObjectId_t>::AttributeTypeName(), // AT_OBJECTID,
+ CDmAttributeInfo<Color>::AttributeTypeName(), // AT_COLOR,
+ CDmAttributeInfo<Vector2D>::AttributeTypeName(), // AT_VECTOR2,
+ CDmAttributeInfo<Vector>::AttributeTypeName(), // AT_VECTOR3,
+ CDmAttributeInfo<Vector4D>::AttributeTypeName(), // AT_VECTOR4,
+ CDmAttributeInfo<QAngle>::AttributeTypeName(), // AT_QANGLE,
+ CDmAttributeInfo<Quaternion>::AttributeTypeName(), // AT_QUATERNION,
+ CDmAttributeInfo<VMatrix>::AttributeTypeName(), // AT_VMATRIX,
+ CDmAttributeInfo< CUtlVector< DmElementHandle_t > >::AttributeTypeName(), // AT_ELEMENT_ARRAY,
+ CDmAttributeInfo< CUtlVector< int > >::AttributeTypeName(), // AT_INT_ARRAY,
+ CDmAttributeInfo< CUtlVector< float > >::AttributeTypeName(), // AT_FLOAT_ARRAY,
+ CDmAttributeInfo< CUtlVector< bool > >::AttributeTypeName(), // AT_BOOL_ARRAY,
+ CDmAttributeInfo< CUtlVector< CUtlString > >::AttributeTypeName(), // AT_STRING_ARRAY,
+ CDmAttributeInfo< CUtlVector< CUtlBinaryBlock > >::AttributeTypeName(), // AT_VOID_ARRAY,
+ CDmAttributeInfo< CUtlVector< DmObjectId_t > >::AttributeTypeName(), // AT_OBJECTID_ARRAY,
+ CDmAttributeInfo< CUtlVector< Color > >::AttributeTypeName(), // AT_COLOR_ARRAY,
+ CDmAttributeInfo< CUtlVector< Vector2D > >::AttributeTypeName(), // AT_VECTOR2_ARRAY,
+ CDmAttributeInfo< CUtlVector< Vector > >::AttributeTypeName(), // AT_VECTOR3_ARRAY,
+ CDmAttributeInfo< CUtlVector< Vector4D > >::AttributeTypeName(), // AT_VECTOR4_ARRAY,
+ CDmAttributeInfo< CUtlVector< QAngle > >::AttributeTypeName(), // AT_QANGLE_ARRAY,
+ CDmAttributeInfo< CUtlVector< Quaternion > >::AttributeTypeName(), // AT_QUATERNION_ARRAY,
+ CDmAttributeInfo< CUtlVector< VMatrix > >::AttributeTypeName(), // AT_VMATRIX_ARRAY,
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CDmxAttribute::CDmxAttribute( const char *pAttributeName )
+{
+ m_Name = s_AttributeNameSymbols.AddString( pAttributeName );
+ m_Type = AT_UNKNOWN;
+ m_pData = NULL;
+}
+
+CDmxAttribute::CDmxAttribute( CUtlSymbol attributeName )
+{
+ m_Name = attributeName;
+ m_Type = AT_UNKNOWN;
+ m_pData = NULL;
+}
+
+CDmxAttribute::~CDmxAttribute()
+{
+ FreeDataMemory();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the size of the variables storing the various attribute types
+//-----------------------------------------------------------------------------
+int CDmxAttribute::AttributeDataSize( DmAttributeType_t type )
+{
+ return s_pAttributeSize[type];
+}
+
+
+//-----------------------------------------------------------------------------
+// Allocate, free memory for data
+//-----------------------------------------------------------------------------
+void CDmxAttribute::AllocateDataMemory( DmAttributeType_t type )
+{
+ FreeDataMemory();
+
+ m_Type = type;
+ m_pData = DMXAlloc( s_pAttributeSize[m_Type] );
+}
+
+#define DESTRUCT_ARRAY( _dataType ) \
+case CDmAttributeInfo< CUtlVector< _dataType > >::ATTRIBUTE_TYPE: \
+ Destruct( ( CUtlVector< _dataType >* )m_pData ); \
+ break;
+
+void CDmxAttribute::FreeDataMemory()
+{
+ if ( m_Type != AT_UNKNOWN )
+ {
+ Assert( m_pData != NULL );
+ if ( !IsArrayType( m_Type ) )
+ {
+ if ( m_Type == AT_STRING )
+ {
+ Destruct( (CUtlString*)m_pData );
+ }
+ else if ( m_Type == AT_VOID )
+ {
+ Destruct( (CUtlBinaryBlock*)m_pData );
+ }
+ }
+ else
+ {
+ switch ( m_Type )
+ {
+ DESTRUCT_ARRAY( int );
+ DESTRUCT_ARRAY( float );
+ DESTRUCT_ARRAY( bool );
+ DESTRUCT_ARRAY( CUtlString );
+ DESTRUCT_ARRAY( CUtlBinaryBlock );
+ DESTRUCT_ARRAY( DmObjectId_t );
+ DESTRUCT_ARRAY( Color );
+ DESTRUCT_ARRAY( Vector2D );
+ DESTRUCT_ARRAY( Vector );
+ DESTRUCT_ARRAY( Vector4D );
+ DESTRUCT_ARRAY( QAngle );
+ DESTRUCT_ARRAY( Quaternion );
+ DESTRUCT_ARRAY( VMatrix );
+ DESTRUCT_ARRAY( CDmxElement* );
+ }
+ }
+ m_Type = AT_UNKNOWN;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns attribute type string
+//-----------------------------------------------------------------------------
+inline const char* CDmxAttribute::GetTypeString() const
+{
+ return g_pAttributeTypeName[ m_Type ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns attribute name
+//-----------------------------------------------------------------------------
+const char *CDmxAttribute::GetName() const
+{
+ return s_AttributeNameSymbols.String( m_Name );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the size of an array, returns 0 if it's not an array type
+//-----------------------------------------------------------------------------
+#define GET_ARRAY_COUNT( _dataType ) \
+ case CDmAttributeInfo< CUtlVector< _dataType > >::ATTRIBUTE_TYPE: \
+ { \
+ const CUtlVector< _dataType > &array = *( CUtlVector< _dataType >* )m_pData; \
+ return array.Count(); \
+ }
+
+int CDmxAttribute::GetArrayCount() const
+{
+ if ( !IsArrayType( m_Type ) || !m_pData )
+ return 0;
+
+ switch( m_Type )
+ {
+ GET_ARRAY_COUNT( int );
+ GET_ARRAY_COUNT( float );
+ GET_ARRAY_COUNT( bool );
+ GET_ARRAY_COUNT( CUtlString );
+ GET_ARRAY_COUNT( CUtlBinaryBlock );
+ GET_ARRAY_COUNT( DmObjectId_t );
+ GET_ARRAY_COUNT( Color );
+ GET_ARRAY_COUNT( Vector2D );
+ GET_ARRAY_COUNT( Vector );
+ GET_ARRAY_COUNT( Vector4D );
+ GET_ARRAY_COUNT( QAngle );
+ GET_ARRAY_COUNT( Quaternion );
+ GET_ARRAY_COUNT( VMatrix );
+ GET_ARRAY_COUNT( CDmxElement* );
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the size of an array, returns 0 if it's not an array type
+//-----------------------------------------------------------------------------
+#define SERIALIZES_ON_MULTIPLE_LINES( _dataType ) \
+ case CDmAttributeInfo< _dataType >::ATTRIBUTE_TYPE: \
+ return ::SerializesOnMultipleLines< _dataType >(); \
+ case CDmAttributeInfo< CUtlVector< _dataType > >::ATTRIBUTE_TYPE: \
+ return ::SerializesOnMultipleLines< CUtlVector< _dataType > >()
+
+bool CDmxAttribute::SerializesOnMultipleLines() const
+{
+ switch( m_Type )
+ {
+ SERIALIZES_ON_MULTIPLE_LINES( CDmxElement* );
+ SERIALIZES_ON_MULTIPLE_LINES( int );
+ SERIALIZES_ON_MULTIPLE_LINES( float );
+ SERIALIZES_ON_MULTIPLE_LINES( bool );
+ SERIALIZES_ON_MULTIPLE_LINES( CUtlString );
+ SERIALIZES_ON_MULTIPLE_LINES( CUtlBinaryBlock );
+ SERIALIZES_ON_MULTIPLE_LINES( DmObjectId_t );
+ SERIALIZES_ON_MULTIPLE_LINES( Color );
+ SERIALIZES_ON_MULTIPLE_LINES( Vector2D );
+ SERIALIZES_ON_MULTIPLE_LINES( Vector );
+ SERIALIZES_ON_MULTIPLE_LINES( Vector4D );
+ SERIALIZES_ON_MULTIPLE_LINES( QAngle );
+ SERIALIZES_ON_MULTIPLE_LINES( Quaternion );
+ SERIALIZES_ON_MULTIPLE_LINES( VMatrix );
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Write to file
+//-----------------------------------------------------------------------------
+#define SERIALIZE_TYPE( _buf, _attributeType, _dataType ) \
+ case _attributeType: \
+ { \
+ if ( m_pData ) \
+ return ::Serialize( _buf, *(_dataType*)m_pData ); \
+ _dataType temp; \
+ CDmAttributeInfo< _dataType >::SetDefaultValue( temp ); \
+ return ::Serialize( _buf, temp ); \
+ }
+
+#define SERIALIZE_ARRAY_TYPE( _buf, _attributeType, _dataType ) \
+ case _attributeType: \
+ { \
+ if ( m_pData ) \
+ return ::Serialize( _buf, *(CUtlVector< _dataType >*)m_pData ); \
+ CUtlVector< _dataType > temp; \
+ CDmAttributeInfo< CUtlVector< _dataType > >::SetDefaultValue( temp ); \
+ return ::Serialize( _buf, temp ); \
+ }
+
+bool CDmxAttribute::Serialize( CUtlBuffer &buf ) const
+{
+ switch( m_Type )
+ {
+ SERIALIZE_TYPE( buf, AT_INT, int );
+ SERIALIZE_TYPE( buf, AT_FLOAT, float );
+ SERIALIZE_TYPE( buf, AT_BOOL, bool );
+ SERIALIZE_TYPE( buf, AT_STRING, CUtlString );
+ SERIALIZE_TYPE( buf, AT_VOID, CUtlBinaryBlock );
+ SERIALIZE_TYPE( buf, AT_OBJECTID, DmObjectId_t );
+ SERIALIZE_TYPE( buf, AT_COLOR, Color );
+ SERIALIZE_TYPE( buf, AT_VECTOR2, Vector2D );
+ SERIALIZE_TYPE( buf, AT_VECTOR3, Vector );
+ SERIALIZE_TYPE( buf, AT_VECTOR4, Vector4D );
+ SERIALIZE_TYPE( buf, AT_QANGLE, QAngle );
+ SERIALIZE_TYPE( buf, AT_QUATERNION, Quaternion );
+ SERIALIZE_TYPE( buf, AT_VMATRIX, VMatrix );
+ SERIALIZE_ARRAY_TYPE( buf, AT_INT_ARRAY, int );
+ SERIALIZE_ARRAY_TYPE( buf, AT_FLOAT_ARRAY, float );
+ SERIALIZE_ARRAY_TYPE( buf, AT_BOOL_ARRAY, bool );
+ SERIALIZE_ARRAY_TYPE( buf, AT_STRING_ARRAY, CUtlString );
+ SERIALIZE_ARRAY_TYPE( buf, AT_VOID_ARRAY, CUtlBinaryBlock );
+ SERIALIZE_ARRAY_TYPE( buf, AT_OBJECTID_ARRAY, DmObjectId_t );
+ SERIALIZE_ARRAY_TYPE( buf, AT_COLOR_ARRAY, Color );
+ SERIALIZE_ARRAY_TYPE( buf, AT_VECTOR2_ARRAY, Vector2D );
+ SERIALIZE_ARRAY_TYPE( buf, AT_VECTOR3_ARRAY, Vector );
+ SERIALIZE_ARRAY_TYPE( buf, AT_VECTOR4_ARRAY, Vector4D );
+ SERIALIZE_ARRAY_TYPE( buf, AT_QANGLE_ARRAY, QAngle );
+ SERIALIZE_ARRAY_TYPE( buf, AT_QUATERNION_ARRAY, Quaternion );
+ SERIALIZE_ARRAY_TYPE( buf, AT_VMATRIX_ARRAY, VMatrix );
+ default:
+ AssertMsg( 0, "Cannot serialize elements or element arrays!\n" );
+ return false;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Serialize a single element in an array attribute
+//-----------------------------------------------------------------------------
+#define SERIALIZE_TYPED_ELEMENT( _index, _buf, _attributeType, _dataType ) \
+ case _attributeType: \
+ { \
+ if ( m_pData ) \
+ { \
+ const CUtlVector< _dataType > &array = *( CUtlVector< _dataType >* )m_pData;\
+ return ::Serialize( buf, array[_index] ); \
+ } \
+ _dataType temp; \
+ CDmAttributeInfo< _dataType >::SetDefaultValue( temp ); \
+ return ::Serialize( _buf, temp ); \
+ }
+
+bool CDmxAttribute::SerializeElement( int nIndex, CUtlBuffer &buf ) const
+{
+ if ( !IsArrayType( m_Type ) )
+ return false;
+
+ switch( m_Type )
+ {
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_INT_ARRAY, int );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_FLOAT_ARRAY, float );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_BOOL_ARRAY, bool );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_STRING_ARRAY, CUtlString );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_VOID_ARRAY, CUtlBinaryBlock );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_OBJECTID_ARRAY, DmObjectId_t );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_COLOR_ARRAY, Color );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_VECTOR2_ARRAY, Vector2D );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_VECTOR3_ARRAY, Vector );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_VECTOR4_ARRAY, Vector4D );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_QANGLE_ARRAY, QAngle );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_QUATERNION_ARRAY, Quaternion );
+ SERIALIZE_TYPED_ELEMENT( nIndex, buf, AT_VMATRIX_ARRAY, VMatrix );
+
+ default:
+ AssertMsg( 0, "Cannot serialize elements!\n" );
+ return false;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Read from file
+//-----------------------------------------------------------------------------
+#define UNSERIALIZE_TYPE( _buf, _attributeType, _dataType ) \
+ case _attributeType: \
+ Construct( (_dataType*)m_pData ); \
+ return ::Unserialize( _buf, *(_dataType*)m_pData )
+
+#define UNSERIALIZE_ARRAY_TYPE( _buf, _attributeType, _dataType ) \
+ case _attributeType: \
+ Construct( (CUtlVector< _dataType > *)m_pData ); \
+ return ::Unserialize( _buf, *(CUtlVector< _dataType >*)m_pData )
+
+bool CDmxAttribute::Unserialize( DmAttributeType_t type, CUtlBuffer &buf )
+{
+ AllocateDataMemory( type );
+ switch( type )
+ {
+ UNSERIALIZE_TYPE( buf, AT_INT, int );
+ UNSERIALIZE_TYPE( buf, AT_FLOAT, float );
+ UNSERIALIZE_TYPE( buf, AT_BOOL, bool );
+ UNSERIALIZE_TYPE( buf, AT_STRING, CUtlString );
+ UNSERIALIZE_TYPE( buf, AT_VOID, CUtlBinaryBlock );
+ UNSERIALIZE_TYPE( buf, AT_OBJECTID, DmObjectId_t );
+ UNSERIALIZE_TYPE( buf, AT_COLOR, Color );
+ UNSERIALIZE_TYPE( buf, AT_VECTOR2, Vector2D );
+ UNSERIALIZE_TYPE( buf, AT_VECTOR3, Vector );
+ UNSERIALIZE_TYPE( buf, AT_VECTOR4, Vector4D );
+ UNSERIALIZE_TYPE( buf, AT_QANGLE, QAngle );
+ UNSERIALIZE_TYPE( buf, AT_QUATERNION, Quaternion );
+ UNSERIALIZE_TYPE( buf, AT_VMATRIX, VMatrix );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_INT_ARRAY, int );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_FLOAT_ARRAY, float );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_BOOL_ARRAY, bool );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_STRING_ARRAY, CUtlString );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_VOID_ARRAY, CUtlBinaryBlock );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_OBJECTID_ARRAY, DmObjectId_t );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_COLOR_ARRAY, Color );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_VECTOR2_ARRAY, Vector2D );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_VECTOR3_ARRAY, Vector );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_VECTOR4_ARRAY, Vector4D );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_QANGLE_ARRAY, QAngle );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_QUATERNION_ARRAY, Quaternion );
+ UNSERIALIZE_ARRAY_TYPE( buf, AT_VMATRIX_ARRAY, VMatrix );
+ default:
+ AssertMsg( 0, "Cannot unserialize elements or element arrays!\n" );
+ return false;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Read element from file
+//-----------------------------------------------------------------------------
+#define UNSERIALIZE_TYPED_ELEMENT( _buf, _attributeType, _dataType ) \
+ case _attributeType: \
+ { \
+ if ( !bIsDataMemoryAllocated ) \
+ { \
+ Construct( (CUtlVector< _dataType > *)m_pData ); \
+ } \
+ _dataType temp; \
+ bool bReadElement = ::Unserialize( _buf, temp ); \
+ if ( bReadElement ) \
+ { \
+ GetArrayForEdit< _dataType >().AddToTail( temp ); \
+ } \
+ return bReadElement; \
+ }
+
+bool CDmxAttribute::UnserializeElement( DmAttributeType_t type, CUtlBuffer &buf )
+{
+ if ( !IsArrayType( type ) )
+ return false;
+
+ bool bIsDataMemoryAllocated = ( m_Type == type );
+ if ( !bIsDataMemoryAllocated )
+ {
+ AllocateDataMemory( type );
+ }
+ switch( type )
+ {
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_INT_ARRAY, int );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_FLOAT_ARRAY, float );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_BOOL_ARRAY, bool );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_STRING_ARRAY, CUtlString );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_VOID_ARRAY, CUtlBinaryBlock );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_OBJECTID_ARRAY, DmObjectId_t );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_COLOR_ARRAY, Color );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_VECTOR2_ARRAY, Vector2D );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_VECTOR3_ARRAY, Vector );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_VECTOR4_ARRAY, Vector4D );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_QANGLE_ARRAY, QAngle );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_QUATERNION_ARRAY, Quaternion );
+ UNSERIALIZE_TYPED_ELEMENT( buf, AT_VMATRIX_ARRAY, VMatrix );
+ default:
+ AssertMsg( 0, "Cannot unserialize elements!\n" );
+ return false;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets name
+//-----------------------------------------------------------------------------
+void CDmxAttribute::SetName( const char *pAttributeName )
+{
+ m_Name = s_AttributeNameSymbols.Find( pAttributeName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets values
+//-----------------------------------------------------------------------------
+void CDmxAttribute::SetValue( const char *pString )
+{
+ AllocateDataMemory( AT_STRING );
+ CUtlString* pUtlString = (CUtlString*)m_pData;
+ Construct( pUtlString );
+ pUtlString->Set( pString );
+}
+
+void CDmxAttribute::SetValue( const void *pBuffer, size_t nLen )
+{
+ AllocateDataMemory( AT_VOID );
+ CUtlBinaryBlock* pBlob = (CUtlBinaryBlock*)m_pData;
+ Construct( pBlob );
+ pBlob->Set( pBuffer, nLen );
+}
+
+// Untyped method for setting used by unpack
+void CDmxAttribute::SetValue( DmAttributeType_t type, const void *pSrc, int nLen )
+{
+ if ( m_Type != type )
+ {
+ AllocateDataMemory( type );
+ }
+ Assert( nLen == CDmxAttribute::AttributeDataSize( type ) );
+ if ( nLen > CDmxAttribute::AttributeDataSize( type ) )
+ {
+ nLen = CDmxAttribute::AttributeDataSize( type );
+ }
+ memcpy( m_pData, pSrc, nLen );
+}
+
+
+void CDmxAttribute::SetValue( const CDmxAttribute *pAttribute )
+{
+ DmAttributeType_t type = pAttribute->GetType();
+ if ( !IsArrayType( type ) )
+ {
+ SetValue( type, pAttribute->m_pData, CDmxAttribute::AttributeDataSize( type ) );
+ }
+ else
+ {
+ // Copying array attributes not done yet..
+ Assert( 0 );
+ }
+}
+
+// Sets the attribute to its default value based on its type
+#define SET_DEFAULT_VALUE( _dataType ) \
+ case CDmAttributeInfo< _dataType >::ATTRIBUTE_TYPE: \
+ CDmAttributeInfo< _dataType >::SetDefaultValue( *reinterpret_cast< _dataType* >( m_pData ) ); \
+ break; \
+ case CDmAttributeInfo< CUtlVector< _dataType > >::ATTRIBUTE_TYPE: \
+ CDmAttributeInfo< CUtlVector< _dataType > >::SetDefaultValue( *reinterpret_cast< CUtlVector< _dataType > * >( m_pData ) ); \
+ break
+
+void CDmxAttribute::SetToDefaultValue()
+{
+ switch( GetType() )
+ {
+ SET_DEFAULT_VALUE( int );
+ SET_DEFAULT_VALUE( float );
+ SET_DEFAULT_VALUE( bool );
+ SET_DEFAULT_VALUE( CUtlString );
+ SET_DEFAULT_VALUE( CUtlBinaryBlock );
+ SET_DEFAULT_VALUE( DmObjectId_t );
+ SET_DEFAULT_VALUE( Color );
+ SET_DEFAULT_VALUE( Vector2D );
+ SET_DEFAULT_VALUE( Vector );
+ SET_DEFAULT_VALUE( Vector4D );
+ SET_DEFAULT_VALUE( QAngle );
+ SET_DEFAULT_VALUE( Quaternion );
+ SET_DEFAULT_VALUE( VMatrix );
+ SET_DEFAULT_VALUE( CDmxElement* );
+
+ default:
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert to and from string
+//-----------------------------------------------------------------------------
+void CDmxAttribute::SetValueFromString( const char *pValue )
+{
+ switch ( GetType() )
+ {
+ case AT_UNKNOWN:
+ break;
+
+ case AT_STRING:
+ SetValue( pValue );
+ break;
+
+ default:
+ {
+ int nLen = pValue ? Q_strlen( pValue ) : 0;
+ if ( nLen == 0 )
+ {
+ SetToDefaultValue();
+ break;
+ }
+
+ CUtlBuffer buf( pValue, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ if ( !Unserialize( GetType(), buf ) )
+ {
+ SetToDefaultValue();
+ }
+ }
+ break;
+ }
+}
+
+const char *CDmxAttribute::GetValueAsString( char *pBuffer, size_t nBufLen ) const
+{
+ Assert( pBuffer );
+ CUtlBuffer buf( pBuffer, nBufLen, CUtlBuffer::TEXT_BUFFER );
+ Serialize( buf );
+ return pBuffer;
+}
diff --git a/dmxloader/dmxelement.cpp b/dmxloader/dmxelement.cpp
new file mode 100644
index 0000000..7537590
--- /dev/null
+++ b/dmxloader/dmxelement.cpp
@@ -0,0 +1,489 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmxloader/dmxelement.h"
+#include "dmxloader/dmxattribute.h"
+#include "tier1/utlbuffer.h"
+#include "mathlib/ssemath.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// globals
+//-----------------------------------------------------------------------------
+CUtlSymbolTableMT CDmxElement::s_TypeSymbols;
+
+
+//-----------------------------------------------------------------------------
+// Creates a dmx element
+//-----------------------------------------------------------------------------
+CDmxElement* CreateDmxElement( const char *pType )
+{
+ return new CDmxElement( pType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CDmxElement::CDmxElement( const char *pType )
+{
+ m_Type = s_TypeSymbols.AddString( pType );
+ m_nLockCount = 0;
+ m_bResortNeeded = false;
+ m_bIsMarkedForDeletion = false;
+ CreateUniqueId( &m_Id );
+}
+
+CDmxElement::~CDmxElement()
+{
+ CDmxElementModifyScope modify( this );
+ RemoveAllAttributes();
+}
+
+
+//-----------------------------------------------------------------------------
+// Utility method for getting at the type
+//-----------------------------------------------------------------------------
+CUtlSymbol CDmxElement::GetType() const
+{
+ return m_Type;
+}
+
+const char* CDmxElement::GetTypeString() const
+{
+ return s_TypeSymbols.String( m_Type );
+}
+
+const char* CDmxElement::GetName() const
+{
+ return GetValue< CUtlString >( "name" );
+}
+
+const DmObjectId_t &CDmxElement::GetId() const
+{
+ return m_Id;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the object id
+//-----------------------------------------------------------------------------
+void CDmxElement::SetId( const DmObjectId_t &id )
+{
+ CopyUniqueId( id, &m_Id );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sorts the vector when a change has occurred
+//-----------------------------------------------------------------------------
+void CDmxElement::Resort( ) const
+{
+ if ( m_bResortNeeded )
+ {
+ AttributeList_t *pAttributes = const_cast< AttributeList_t *>( &m_Attributes );
+ pAttributes->RedoSort();
+ m_bResortNeeded = false;
+
+ // NOTE: This checks for duplicate attribute names
+ int nCount = m_Attributes.Count();
+ for ( int i = nCount; --i >= 1; )
+ {
+ if ( m_Attributes[i]->GetNameSymbol() == m_Attributes[i-1]->GetNameSymbol() )
+ {
+ Warning( "Duplicate attribute name %s encountered!\n", m_Attributes[i]->GetName() );
+ pAttributes->Remove(i);
+ Assert( 0 );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Enables modification of the DmxElement
+//-----------------------------------------------------------------------------
+void CDmxElement::LockForChanges( bool bLock )
+{
+ if ( bLock )
+ {
+ ++m_nLockCount;
+ }
+ else
+ {
+ if ( --m_nLockCount == 0 )
+ {
+ Resort();
+ }
+ Assert( m_nLockCount >= 0 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds, removes attributes
+//-----------------------------------------------------------------------------
+CDmxAttribute *CDmxElement::AddAttribute( const char *pAttributeName )
+{
+ int nIndex = FindAttribute( pAttributeName );
+ if ( nIndex >= 0 )
+ return m_Attributes[nIndex];
+
+ CDmxElementModifyScope modify( this );
+ m_bResortNeeded = true;
+ CDmxAttribute *pAttribute = new CDmxAttribute( pAttributeName );
+ m_Attributes.InsertNoSort( pAttribute );
+ return pAttribute;
+}
+
+void CDmxElement::RemoveAttribute( const char *pAttributeName )
+{
+ CDmxElementModifyScope modify( this );
+ int idx = FindAttribute( pAttributeName );
+ if ( idx >= 0 )
+ {
+ delete m_Attributes[idx];
+ m_Attributes.Remove( idx );
+ }
+}
+
+void CDmxElement::RemoveAttributeByPtr( CDmxAttribute *pAttribute )
+{
+ CDmxElementModifyScope modify( this );
+ int nCount = m_Attributes.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Attributes[i] != pAttribute )
+ continue;
+
+ delete pAttribute;
+ m_Attributes.Remove( i );
+ break;
+ }
+}
+
+void CDmxElement::RemoveAllAttributes()
+{
+ int nCount = m_Attributes.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ delete m_Attributes[i];
+ }
+ m_Attributes.RemoveAll();
+ m_bResortNeeded = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Rename an attribute
+//-----------------------------------------------------------------------------
+void CDmxElement::RenameAttribute( const char *pAttributeName, const char *pNewName )
+{
+ CDmxElementModifyScope modify( this );
+
+ // No change...
+ if ( !Q_stricmp( pAttributeName, pNewName ) )
+ return;
+
+ int idx = FindAttribute( pAttributeName );
+ if ( idx < 0 )
+ return;
+
+ if ( HasAttribute( pNewName ) )
+ {
+ Warning( "Tried to rename from \"%s\" to \"%s\", but \"%s\" already exists!\n",
+ pAttributeName, pNewName, pNewName );
+ return;
+ }
+
+ m_bResortNeeded = true;
+ m_Attributes[ idx ]->SetName( pNewName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Find an attribute by name-based lookup
+//-----------------------------------------------------------------------------
+int CDmxElement::FindAttribute( const char *pAttributeName ) const
+{
+ // FIXME: The cost here is O(log M) + O(log N)
+ // where log N is the binary search for the symbol match
+ // and log M is the binary search for the attribute name->symbol
+ // We can eliminate log M by using a hash table in the symbol lookup
+ Resort();
+ CDmxAttribute search( pAttributeName );
+ return m_Attributes.Find( &search );
+}
+
+
+//-----------------------------------------------------------------------------
+// Find an attribute by name-based lookup
+//-----------------------------------------------------------------------------
+int CDmxElement::FindAttribute( CUtlSymbol attributeName ) const
+{
+ Resort();
+ CDmxAttribute search( attributeName );
+ return m_Attributes.Find( &search );
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute finding
+//-----------------------------------------------------------------------------
+bool CDmxElement::HasAttribute( const char *pAttributeName ) const
+{
+ int idx = FindAttribute( pAttributeName );
+ return ( idx >= 0 );
+}
+
+CDmxAttribute *CDmxElement::GetAttribute( const char *pAttributeName )
+{
+ int idx = FindAttribute( pAttributeName );
+ if ( idx >= 0 )
+ return m_Attributes[ idx ];
+ return NULL;
+}
+
+const CDmxAttribute *CDmxElement::GetAttribute( const char *pAttributeName ) const
+{
+ int idx = FindAttribute( pAttributeName );
+ if ( idx >= 0 )
+ return m_Attributes[ idx ];
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute interation
+//-----------------------------------------------------------------------------
+int CDmxElement::AttributeCount() const
+{
+ return m_Attributes.Count();
+}
+
+CDmxAttribute *CDmxElement::GetAttribute( int nIndex )
+{
+ return m_Attributes[ nIndex ];
+}
+
+const CDmxAttribute *CDmxElement::GetAttribute( int nIndex ) const
+{
+ return m_Attributes[ nIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all elements recursively
+//-----------------------------------------------------------------------------
+void CDmxElement::AddElementsToDelete( CUtlVector< CDmxElement * >& elementsToDelete )
+{
+ if ( m_bIsMarkedForDeletion )
+ return;
+
+ m_bIsMarkedForDeletion = true;
+ elementsToDelete.AddToTail( this );
+
+ int nCount = AttributeCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxAttribute *pAttribute = GetAttribute(i);
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmxElement *pElement = pAttribute->GetValue< CDmxElement* >();
+ if ( pElement )
+ {
+ pElement->AddElementsToDelete( elementsToDelete );
+ }
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ const CUtlVector< CDmxElement * > &elements = pAttribute->GetArray< CDmxElement* >();
+ int nElementCount = elements.Count();
+ for ( int j = 0; j < nElementCount; ++j )
+ {
+ if ( elements[j] )
+ {
+ elements[j]->AddElementsToDelete( elementsToDelete );
+ }
+ }
+ continue;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all elements recursively
+//-----------------------------------------------------------------------------
+void CDmxElement::RemoveAllElementsRecursive()
+{
+ CUtlVector< CDmxElement * > elementsToDelete;
+ AddElementsToDelete( elementsToDelete );
+ int nCount = elementsToDelete.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ delete elementsToDelete[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to unpack data into a structure
+//-----------------------------------------------------------------------------
+void CDmxElement::UnpackIntoStructure( void *pData, size_t DestSizeInBytes, const DmxElementUnpackStructure_t *pUnpack ) const
+{
+ void *pDataEnd = ( char * )pData + DestSizeInBytes;
+
+ for ( ; pUnpack->m_AttributeType != AT_UNKNOWN; ++pUnpack )
+ {
+ char *pDest = (char*)pData + pUnpack->m_nOffset;
+
+ // NOTE: This does not work with array data at the moment
+ if ( IsArrayType( pUnpack->m_AttributeType ) )
+ {
+ AssertMsg( 0, ( "CDmxElement::UnpackIntoStructure: Array attribute types not currently supported!\n" ) );
+ continue;
+ }
+
+ if ( pUnpack->m_AttributeType == AT_VOID )
+ {
+ AssertMsg( 0, ( "CDmxElement::UnpackIntoStructure: Binary blob attribute types not currently supported!\n" ) );
+ continue;
+ }
+
+ CDmxAttribute temp( NULL );
+ const CDmxAttribute *pAttribute = GetAttribute( pUnpack->m_pAttributeName );
+ if ( !pAttribute )
+ {
+ if ( !pUnpack->m_pDefaultString )
+ continue;
+
+ // Convert the default string into the target
+ int nLen = Q_strlen( pUnpack->m_pDefaultString );
+ if ( nLen > 0 )
+ {
+ CUtlBuffer buf( pUnpack->m_pDefaultString, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
+ temp.Unserialize( pUnpack->m_AttributeType, buf );
+ }
+ else
+ {
+ CUtlBuffer buf;
+ temp.Unserialize( pUnpack->m_AttributeType, buf );
+ }
+ pAttribute = &temp;
+ }
+
+ if ( pUnpack->m_AttributeType != pAttribute->GetType() )
+ {
+ Warning( "CDmxElement::UnpackIntoStructure: Mismatched attribute type in attribute \"%s\"!\n", pUnpack->m_pAttributeName );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_STRING )
+ {
+ if ( pDest + pUnpack->m_nSize > pDataEnd )
+ {
+ Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure string buffer overrun!\n" );
+ continue;
+ }
+
+ // Strings get special treatment: they are stored as in-line arrays of chars
+ Q_strncpy( pDest, pAttribute->GetValueString(), pUnpack->m_nSize );
+ continue;
+ }
+
+ // special case - if data type is float, but dest size == 16, we are unpacking into simd by
+ // replication
+ if ( ( pAttribute->GetType() == AT_FLOAT ) && ( pUnpack->m_nSize == sizeof( fltx4 ) ) )
+ {
+ if ( pDest + 4 * sizeof( float ) > pDataEnd )
+ {
+ Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure float buffer overrun!\n" );
+ continue;
+ }
+
+ memcpy( pDest + 0 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
+ memcpy( pDest + 1 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
+ memcpy( pDest + 2 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
+ memcpy( pDest + 3 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
+ }
+ else
+ {
+ if ( pDest + pUnpack->m_nSize > pDataEnd )
+ {
+ Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure memcpy buffer overrun!\n" );
+ continue;
+ }
+
+ AssertMsg( pUnpack->m_nSize == CDmxAttribute::AttributeDataSize( pAttribute->GetType() ),
+ "CDmxElement::UnpackIntoStructure: Incorrect size to unpack data into in attribute \"%s\"!\n", pUnpack->m_pAttributeName );
+ memcpy( pDest, pAttribute->m_pData, pUnpack->m_nSize );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates attributes based on the unpack structure
+//-----------------------------------------------------------------------------
+void CDmxElement::AddAttributesFromStructure_Internal( const void *pData, size_t byteCount, const DmxElementUnpackStructure_t *pUnpack )
+{
+ for ( ; pUnpack->m_AttributeType != AT_UNKNOWN; ++pUnpack )
+ {
+ const char *pSrc = (const char*)pData + pUnpack->m_nOffset;
+
+ // NOTE: This does not work with array data at the moment
+ if ( IsArrayType( pUnpack->m_AttributeType ) )
+ {
+ AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Array attribute types not currently supported!\n" );
+ continue;
+ }
+
+ if ( pUnpack->m_AttributeType == AT_VOID )
+ {
+ AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Binary blob attribute types not currently supported!\n" );
+ continue;
+ }
+
+ if ( HasAttribute( pUnpack->m_pAttributeName ) )
+ {
+ AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Attribute %s already exists!\n", pUnpack->m_pAttributeName );
+ continue;
+ }
+
+ {
+ if ( (size_t)(pUnpack->m_nOffset + pUnpack->m_nSize) > byteCount )
+ {
+ Msg( "Buffer underread! Mismatched type/type-descriptor.\n" );
+ }
+ CDmxElementModifyScope modify( this );
+ CDmxAttribute *pAttribute = AddAttribute( pUnpack->m_pAttributeName );
+ if ( pUnpack->m_AttributeType == AT_STRING )
+ {
+ pAttribute->SetValue( pSrc );
+ }
+ else
+ {
+ int nSize = pUnpack->m_nSize;
+
+ // handle float attrs stored as replicated fltx4's
+ if ( ( pUnpack->m_AttributeType == AT_FLOAT ) && ( nSize == sizeof( fltx4 ) ) )
+ {
+ nSize = sizeof( float );
+ }
+
+ AssertMsg( nSize == CDmxAttribute::AttributeDataSize( pUnpack->m_AttributeType ),
+ "CDmxElement::UnpackIntoStructure: Incorrect size to unpack data into in attribute \"%s\"!\n", pUnpack->m_pAttributeName );
+ pAttribute->SetValue( pUnpack->m_AttributeType, pSrc, nSize );
+ }
+ }
+ }
+}
diff --git a/dmxloader/dmxloader.cpp b/dmxloader/dmxloader.cpp
new file mode 100644
index 0000000..2083f3f
--- /dev/null
+++ b/dmxloader/dmxloader.cpp
@@ -0,0 +1,697 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmxloader/dmxelement.h"
+#include "tier1/utlbuffer.h"
+#include "tier2/tier2.h"
+#include "tier2/utlstreambuffer.h"
+#include "filesystem.h"
+#include "datamodel/idatamodel.h" // for the file format #defines
+#include "dmxserializationdictionary.h"
+#include "tier1/memstack.h"
+
+
+//-----------------------------------------------------------------------------
+// DMX elements/attributes can only be accessed inside a dmx context
+//-----------------------------------------------------------------------------
+static int s_bInDMXContext;
+CMemoryStack s_DMXAllocator;
+static bool s_bAllocatorInitialized;
+
+void BeginDMXContext( )
+{
+ Assert( !s_bInDMXContext );
+
+ if ( !s_bAllocatorInitialized )
+ {
+ s_DMXAllocator.Init( 2 * 1024 * 1024, 0, 0, 4 );
+ s_bAllocatorInitialized = true;
+ }
+
+ s_bInDMXContext = true;
+}
+
+void EndDMXContext( bool bDecommitMemory )
+{
+ Assert( s_bInDMXContext );
+ s_bInDMXContext = false;
+ s_DMXAllocator.FreeAll( bDecommitMemory );
+}
+
+void DecommitDMXMemory()
+{
+ s_DMXAllocator.FreeAll( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Used for allocation. All will be freed when we leave the DMX context
+//-----------------------------------------------------------------------------
+void* DMXAlloc( size_t size )
+{
+ Assert( s_bInDMXContext );
+ if ( !s_bInDMXContext )
+ return 0;
+ return s_DMXAllocator.Alloc( size, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+bool UnserializeTextDMX( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot );
+bool SerializeTextDMX( const char *pFileName, CUtlBuffer &buf, CDmxElement *pRoot );
+
+
+//-----------------------------------------------------------------------------
+// special element indices
+//-----------------------------------------------------------------------------
+enum
+{
+ ELEMENT_INDEX_NULL = -1,
+ ELEMENT_INDEX_EXTERNAL = -2,
+};
+
+
+//-----------------------------------------------------------------------------
+// Serialization class for Binary output
+//-----------------------------------------------------------------------------
+class CDmxSerializer
+{
+public:
+ bool Unserialize( CUtlBuffer &buf, int nEncodingVersion, CDmxElement **ppRoot );
+ bool Serialize( CUtlBuffer &buf, CDmxElement *pRoot, const char *pFileName );
+
+private:
+ // Methods related to serialization
+ bool ShouldWriteAttribute( const char *pAttributeName, CDmxAttribute *pAttribute );
+ void SerializeElementIndex( CUtlBuffer& buf, CDmxSerializationDictionary& list, CDmxElement *pElement );
+ void SerializeElementAttribute( CUtlBuffer& buf, CDmxSerializationDictionary& list, CDmxAttribute *pAttribute );
+ void SerializeElementArrayAttribute( CUtlBuffer& buf, CDmxSerializationDictionary& list, CDmxAttribute *pAttribute );
+ bool SaveElementDict( CUtlBuffer& buf, CUtlRBTree< const char* > &stringTable, CDmxElement *pElement );
+ bool SaveElement( CUtlBuffer& buf, CDmxSerializationDictionary& dict, CUtlRBTree< const char* > &stringTable, CDmxElement *pElement);
+
+ // Methods related to unserialization
+ CDmxElement* UnserializeElementIndex( CUtlBuffer &buf, CUtlVector<CDmxElement*> &elementList );
+ void UnserializeElementAttribute( CUtlBuffer &buf, CDmxAttribute *pAttribute, CUtlVector<CDmxElement*> &elementList );
+ void UnserializeElementArrayAttribute( CUtlBuffer &buf, CDmxAttribute *pAttribute, CUtlVector<CDmxElement*> &elementList );
+ bool UnserializeAttributes( CUtlBuffer &buf, CDmxElement *pElement, CUtlVector<CDmxElement*> &elementList, int nStrings, int *offsetTable, char *stringTable );
+ int GetStringOffsetTable( CUtlBuffer &buf, int *offsetTable, int nStrings );
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Should we write out the attribute?
+//-----------------------------------------------------------------------------
+bool CDmxSerializer::ShouldWriteAttribute( const char *pAttributeName, CDmxAttribute *pAttribute )
+{
+ if ( !pAttribute )
+ return false;
+
+ // These are already written in the initial element dictionary
+ if ( !Q_stricmp( pAttributeName, "name" ) )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Write out the index of the element to avoid looks at read time
+//-----------------------------------------------------------------------------
+void CDmxSerializer::SerializeElementIndex( CUtlBuffer& buf, CDmxSerializationDictionary& list, CDmxElement *pElement )
+{
+ if ( !pElement )
+ {
+ buf.PutInt( ELEMENT_INDEX_NULL ); // invalid handle
+ return;
+ }
+
+ buf.PutInt( list.Find( pElement ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out element attributes
+//-----------------------------------------------------------------------------
+void CDmxSerializer::SerializeElementAttribute( CUtlBuffer& buf, CDmxSerializationDictionary& list, CDmxAttribute *pAttribute )
+{
+ SerializeElementIndex( buf, list, pAttribute->GetValue<CDmxElement*>() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out element array attributes
+//-----------------------------------------------------------------------------
+void CDmxSerializer::SerializeElementArrayAttribute( CUtlBuffer& buf, CDmxSerializationDictionary& list, CDmxAttribute *pAttribute )
+{
+ const CUtlVector<CDmxElement*> &vec = pAttribute->GetArray<CDmxElement*>();
+
+ int nCount = vec.Count();
+ buf.PutInt( nCount );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ SerializeElementIndex( buf, list, vec[ i ] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all attributes
+//-----------------------------------------------------------------------------
+bool CDmxSerializer::SaveElement( CUtlBuffer& buf, CDmxSerializationDictionary& list, CUtlRBTree< const char* > &stringTable, CDmxElement *pElement )
+{
+ int nAttributesToSave = 0;
+
+ // Count the attributes...
+ int nCount = pElement->AttributeCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxAttribute *pAttribute = pElement->GetAttribute( i );
+ const char *pName = pAttribute->GetName( );
+ if ( !ShouldWriteAttribute( pName, pAttribute ) )
+ continue;
+
+ ++nAttributesToSave;
+ }
+
+ // Now write them all out.
+ buf.PutInt( nAttributesToSave );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxAttribute *pAttribute = pElement->GetAttribute( i );
+ const char *pName = pAttribute->GetName();
+ if ( !ShouldWriteAttribute( pName, pAttribute ) )
+ continue;
+
+ unsigned short sym = stringTable.Find( pName );
+ if ( sym == stringTable.InvalidIndex() )
+ return false;
+
+ buf.PutShort( sym );
+ buf.PutChar( pAttribute->GetType() );
+ switch( pAttribute->GetType() )
+ {
+ default:
+ pAttribute->Serialize( buf );
+ break;
+
+ case AT_ELEMENT:
+ SerializeElementAttribute( buf, list, pAttribute );
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ SerializeElementArrayAttribute( buf, list, pAttribute );
+ break;
+ }
+ }
+
+ return buf.IsValid();
+}
+
+bool CDmxSerializer::SaveElementDict( CUtlBuffer& buf, CUtlRBTree< const char* > &stringTable, CDmxElement *pElement )
+{
+ unsigned short sym = stringTable.Find( pElement->GetTypeString() );
+ if ( sym == stringTable.InvalidIndex() )
+ return false;
+
+ buf.PutShort( sym );
+ buf.PutString( pElement->GetName() );
+ buf.Put( &pElement->GetId(), sizeof(DmObjectId_t) );
+ return buf.IsValid();
+}
+
+//-----------------------------------------------------------------------------
+// Main entry point for serialization
+//-----------------------------------------------------------------------------
+bool CDmxSerializer::Serialize( CUtlBuffer &buf, CDmxElement *pRoot, const char *pFileName )
+{
+ // Save elements, attribute links
+ CDmxSerializationDictionary dict;
+ dict.BuildElementList( pRoot, true );
+
+ // collect list of attribute names and element types into string table
+ CUtlRBTree< const char* > stringTable( CaselessStringLessThan );
+ DmxSerializationHandle_t i;
+ for ( i = dict.FirstRootElement(); i != DMX_SERIALIZATION_HANDLE_INVALID; i = dict.NextRootElement(i) )
+ {
+ CDmxElement *pElement = dict.GetRootElement( i );
+ if ( !pElement )
+ return false;
+ stringTable.InsertIfNotFound( pElement->GetTypeString() );
+ int nAttributes = pElement->AttributeCount();
+ for ( int ai = 0; ai < nAttributes; ++ai )
+ {
+ CDmxAttribute *pAttr = pElement->GetAttribute( ai );
+ if ( !pAttr )
+ return false;
+ stringTable.InsertIfNotFound( pAttr->GetName() );
+ }
+ }
+
+ // write out the string table
+ int nStrings = stringTable.Count();
+ if ( nStrings > 65535 )
+ return false;
+ buf.PutShort( nStrings );
+ for ( int si = 0; si < nStrings; ++si )
+ {
+ buf.PutString( stringTable[ si ] );
+ }
+
+ // First write out the dictionary of all elements (to avoid later stitching up in unserialize)
+ buf.PutInt( dict.RootElementCount() );
+ for ( i = dict.FirstRootElement(); i != DMX_SERIALIZATION_HANDLE_INVALID; i = dict.NextRootElement(i) )
+ {
+ if ( !SaveElementDict( buf, stringTable, dict.GetRootElement( i ) ) )
+ return false;
+ }
+
+ // Now write out the attributes of each of those elements
+ for ( i = dict.FirstRootElement(); i != DMX_SERIALIZATION_HANDLE_INVALID; i = dict.NextRootElement(i) )
+ {
+ if ( !SaveElement( buf, dict, stringTable, dict.GetRootElement( i ) ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an element index and converts it to a handle (local or external)
+//-----------------------------------------------------------------------------
+CDmxElement* CDmxSerializer::UnserializeElementIndex( CUtlBuffer &buf, CUtlVector<CDmxElement*> &elementList )
+{
+ int nElementIndex = buf.GetInt();
+ if ( nElementIndex == ELEMENT_INDEX_EXTERNAL )
+ {
+ Warning( "Reading externally referenced elements is not supported!\n" );
+ return NULL;
+ }
+
+ Assert( nElementIndex < elementList.Count() );
+ Assert( nElementIndex >= 0 || nElementIndex == ELEMENT_INDEX_NULL );
+ if ( nElementIndex < 0 || !elementList[ nElementIndex ] )
+ return NULL;
+
+ return elementList[ nElementIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an element attribute
+//-----------------------------------------------------------------------------
+void CDmxSerializer::UnserializeElementAttribute( CUtlBuffer &buf, CDmxAttribute *pAttribute, CUtlVector<CDmxElement*> &elementList )
+{
+ CDmxElement *pElement = UnserializeElementIndex( buf, elementList );
+ pAttribute->SetValue( pElement );
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an element array attribute
+//-----------------------------------------------------------------------------
+void CDmxSerializer::UnserializeElementArrayAttribute( CUtlBuffer &buf, CDmxAttribute *pAttribute, CUtlVector<CDmxElement*> &elementList )
+{
+ int nElementCount = buf.GetInt();
+ CUtlVector< CDmxElement* >& elementArray = pAttribute->GetArrayForEdit< CDmxElement* >();
+ elementArray.EnsureCapacity( nElementCount );
+
+ for ( int i = 0; i < nElementCount; ++i )
+ {
+ CDmxElement *pElement = UnserializeElementIndex( buf, elementList );
+ elementArray.AddToTail( pElement );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a single element
+//-----------------------------------------------------------------------------
+bool CDmxSerializer::UnserializeAttributes( CUtlBuffer &buf, CDmxElement *pElement, CUtlVector<CDmxElement*> &elementList, int nStrings, int *offsetTable, char *stringTable )
+{
+ CDmxElementModifyScope modify( pElement );
+
+ char nameBuf[ 1024 ];
+ int nAttributeCount = buf.GetInt();
+ for ( int i = 0; i < nAttributeCount; ++i )
+ {
+ const char *pName = NULL;
+ if ( stringTable )
+ {
+ int si = buf.GetShort();
+ if ( si >= nStrings )
+ return false;
+ pName = stringTable + offsetTable[ si ];
+ }
+ else
+ {
+ buf.GetString( nameBuf );
+ pName = nameBuf;
+ }
+ DmAttributeType_t nAttributeType = (DmAttributeType_t)buf.GetChar();
+
+ CDmxAttribute *pAttribute = pElement->AddAttribute( pName );
+ if ( !pAttribute )
+ return false;
+
+ switch( nAttributeType )
+ {
+ default:
+ pAttribute->Unserialize( nAttributeType, buf );
+ break;
+
+ case AT_ELEMENT:
+ UnserializeElementAttribute( buf, pAttribute, elementList );
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ UnserializeElementArrayAttribute( buf, pAttribute, elementList );
+ break;
+ }
+ }
+
+ return buf.IsValid();
+}
+
+
+int CDmxSerializer::GetStringOffsetTable( CUtlBuffer &buf, int *offsetTable, int nStrings )
+{
+ int nBytes = buf.GetBytesRemaining();
+ char *pBegin = ( char* )buf.PeekGet();
+ char *pBytes = pBegin;
+ for ( int i = 0; i < nStrings; ++i )
+ {
+ offsetTable[ i ] = pBytes - pBegin;
+
+ do
+ {
+ // grow/shift utlbuffer if it hasn't loaded the entire string table into memory
+ if ( pBytes - pBegin >= nBytes )
+ {
+ pBegin = ( char* )buf.PeekGet( nBytes + 1, 0 );
+ if ( !pBegin )
+ return false;
+ pBytes = pBegin + nBytes;
+ nBytes = buf.GetBytesRemaining();
+ }
+ }
+ while ( *pBytes++ );
+ }
+
+ return pBytes - pBegin;
+}
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+bool CDmxSerializer::Unserialize( CUtlBuffer &buf, int nEncodingVersion, CDmxElement **ppRoot )
+{
+ if ( nEncodingVersion < 0 || nEncodingVersion > 2 )
+ return false;
+
+ bool bReadStringTable = nEncodingVersion >= 2;
+
+ // Keep reading until we read a NULL terminator
+ while( buf.GetChar() != 0 )
+ {
+ if ( !buf.IsValid() )
+ return false;
+ }
+
+ // Read string table
+ int nStrings = 0;
+ int *offsetTable = NULL;
+ char *stringTable = NULL;
+ if ( bReadStringTable )
+ {
+ nStrings = buf.GetShort();
+ if ( nStrings > 0 )
+ {
+ offsetTable = ( int* )stackalloc( nStrings * sizeof( int ) );
+
+ // this causes entire string table to be mapped in memory at once
+ int nStringMemoryUsage = GetStringOffsetTable( buf, offsetTable, nStrings );
+ stringTable = ( char* )stackalloc( nStringMemoryUsage * sizeof( char ) );
+ buf.Get( stringTable, nStringMemoryUsage );
+ }
+ }
+
+ // Read in the element count.
+ int nElementCount = buf.GetInt();
+ if ( !nElementCount )
+ {
+ // Empty (but valid) file
+ return true;
+ }
+
+ if ( nElementCount < 0 || ( bReadStringTable && !stringTable ) )
+ {
+ // Invalid file. Non-empty files with a string table need at least one to associate with elements.
+ return false;
+ }
+
+ char pTypeBuf[256];
+ char pName[2048];
+ DmObjectId_t id;
+
+ // Read + create all elements
+ CUtlVector<CDmxElement*> elementList( 0, nElementCount );
+ for ( int i = 0; i < nElementCount; ++i )
+ {
+ const char *pType = NULL;
+ if ( stringTable )
+ {
+ int si = buf.GetShort();
+ if ( si >= nStrings )
+ return false;
+ pType = stringTable + offsetTable[ si ];
+ }
+ else
+ {
+ buf.GetString( pTypeBuf );
+ pType = pTypeBuf;
+ }
+ buf.GetString( pName );
+ buf.Get( &id, sizeof(DmObjectId_t) );
+
+ CDmxElement *pElement = new CDmxElement( pType );
+ {
+ CDmxElementModifyScope modify( pElement );
+ CDmxAttribute *pAttribute = pElement->AddAttribute( "name" );
+ pAttribute->SetValue( (char const *) pName );
+ pElement->SetId( id );
+ }
+ elementList.AddToTail( pElement );
+ }
+
+ // The root is the 0th element
+ *ppRoot = elementList[ 0 ];
+
+ // Now read all attributes
+ for ( int i = 0; i < nElementCount; ++i )
+ {
+ UnserializeAttributes( buf, elementList[ i ], elementList, nStrings, offsetTable, stringTable );
+ }
+
+ return buf.IsValid();
+}
+
+
+//-----------------------------------------------------------------------------
+// Serialization main entry point
+//-----------------------------------------------------------------------------
+bool SerializeDMX( CUtlBuffer &buf, CDmxElement *pRoot, const char *pFileName )
+{
+ // Write the format name into the file using XML format so that
+ // 3rd-party XML readers can read the file without fail
+ const char *pEncodingName = buf.IsText() ? "keyvalues2" : "binary";
+ int nEncodingVersion = buf.IsText() ? 1 : 2; // HACK - we should have some way of automatically updating this when the encoding version changes!
+ const char *pFormatName = GENERIC_DMX_FORMAT;
+ int nFormatVersion = 1; // HACK - we should have some way of automatically updating this when the encoding version changes!
+ buf.Printf( "%s encoding %s %d format %s %d %s\n", DMX_VERSION_STARTING_TOKEN, pEncodingName, nEncodingVersion, pFormatName, nFormatVersion, DMX_VERSION_ENDING_TOKEN );
+
+ if ( buf.IsText() )
+ return SerializeTextDMX( pFileName ? pFileName : "<no file>", buf, pRoot );
+
+ CDmxSerializer dmxSerializer;
+ return dmxSerializer.Serialize( buf, pRoot, pFileName );
+}
+
+bool SerializeDMX( const char *pFileName, const char *pPathID, bool bTextMode, CDmxElement *pRoot )
+{
+ // NOTE: This guarantees full path names for pathids
+ char pBuf[MAX_PATH];
+ const char *pFullPath = pFileName;
+ if ( !Q_IsAbsolutePath( pFullPath ) && !pPathID )
+ {
+ char pDir[MAX_PATH];
+ if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof(pDir) ) )
+ {
+ Q_ComposeFileName( pDir, pFileName, pBuf, sizeof(pBuf) );
+ Q_RemoveDotSlashes( pBuf );
+ pFullPath = pBuf;
+ }
+ }
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ g_pFullFileSystem->ReadFile( pFullPath, pPathID, buf );
+
+ if ( !buf.IsValid() )
+ {
+ Warning( "SerializeDMX: Unable to open file \"%s\"\n", pFullPath );
+ return DMFILEID_INVALID;
+ }
+
+ return SerializeDMX( buf, pRoot, pFullPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Read the header, return the version (or false if it's not a DMX file)
+//-----------------------------------------------------------------------------
+bool ReadDMXHeader( CUtlBuffer &buf, char *pEncodingName, int nEncodingNameLen, int &nEncodingVersion, char *pFormatName, int nFormatNameLen, int &nFormatVersion )
+{
+ // Make the buffer capable of being read as text
+ bool bBufIsText = buf.IsText();
+ bool bBufHasCRLF = buf.ContainsCRLF();
+ buf.SetBufferType( true, !bBufIsText || bBufHasCRLF );
+
+ char header[ DMX_MAX_HEADER_LENGTH ] = { 0 };
+ bool bOk = buf.ParseToken( DMX_VERSION_STARTING_TOKEN, DMX_VERSION_ENDING_TOKEN, header, sizeof( header ) );
+ if ( bOk )
+ {
+#ifdef _WIN32
+ int nAssigned = sscanf_s( header, "encoding %s %d format %s %d\n", pEncodingName, nEncodingNameLen, &nEncodingVersion, pFormatName, nFormatNameLen, &nFormatVersion );
+#else
+ // sscanf considered harmful. We don't have POSIX 2008 support on OS X and "C11 Annex K" is optional... (optional specs considered useful)
+ char pTmpEncodingName[ sizeof( header ) ] = { 0 };
+ char pTmpFormatName [ sizeof( header ) ] = { 0 };
+ int nAssigned = sscanf( header, "encoding %s %d format %s %d\n", pTmpEncodingName, &nEncodingVersion, pTmpFormatName, &nFormatVersion );
+ bOk = ( V_strlen( pTmpEncodingName ) < nEncodingNameLen ) && ( V_strlen( pTmpFormatName ) < nFormatNameLen );
+ V_strncpy( pEncodingName, pTmpEncodingName, nEncodingNameLen );
+ V_strncpy( pFormatName, pTmpFormatName, nFormatNameLen );
+#endif
+ bOk = bOk && ( nAssigned == 4 );
+ if ( bOk )
+ {
+ bOk = !V_stricmp( pEncodingName, bBufIsText ? "keyvalues2" : "binary" );
+ }
+ }
+
+ // TODO - retire legacy format version reading
+ if ( !bOk )
+ {
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ bOk = buf.ParseToken( DMX_LEGACY_VERSION_STARTING_TOKEN, DMX_LEGACY_VERSION_ENDING_TOKEN, pFormatName, sizeof( pFormatName ) );
+ if ( bOk )
+ {
+ nEncodingVersion = 0;
+ nFormatVersion = 0; // format version is encoded in the format name string
+
+ if ( !V_stricmp( pFormatName, "binary_v1" ) || !V_stricmp( pFormatName, "binary_v2" ) )
+ {
+ bOk = !bBufIsText;
+ V_strncpy( pEncodingName, "binary", nEncodingNameLen );
+ }
+ else if ( !V_stricmp( pFormatName, "keyvalues2_v1" ) || !V_stricmp( pFormatName, "keyvalues2_flat_v1" ) )
+ {
+ bOk = bBufIsText;
+ V_strncpy( pEncodingName, "keyvalues2", nEncodingNameLen );
+ }
+ else
+ {
+ bOk = false;
+ }
+ }
+ }
+
+ // Restore the buffer type
+ buf.SetBufferType( bBufIsText, bBufHasCRLF );
+ return bOk && buf.IsValid();
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialization main entry point
+//-----------------------------------------------------------------------------
+bool UnserializeDMX( CUtlBuffer &buf, CDmxElement **ppRoot, const char *pFileName )
+{
+ // NOTE: Checking the format name string for a version check here is how you'd do it
+ *ppRoot = NULL;
+
+ // Read the standard buffer header
+ int nEncodingVersion, nFormatVersion;
+ char pEncodingName[ DMX_MAX_FORMAT_NAME_MAX_LENGTH ];
+ char pFormatName [ DMX_MAX_FORMAT_NAME_MAX_LENGTH ];
+ if ( !ReadDMXHeader( buf,
+ pEncodingName, sizeof( pEncodingName ), nEncodingVersion,
+ pFormatName, sizeof( pFormatName ), nFormatVersion ) )
+ return false;
+
+ // TODO - retire legacy format version reading
+ if ( nFormatVersion == 0 ) // legacy formats store format version in their format name string
+ {
+ Warning( "reading file '%s' of legacy format '%s' - dmxconvert this file to a newer format!\n", pFileName ? pFileName : "<no file>", pFormatName );
+ }
+
+ // Only allow binary protocol files
+ bool bIsBinary = ( buf.GetFlags() & CUtlBuffer::TEXT_BUFFER ) == 0;
+ if ( bIsBinary )
+ {
+ CDmxSerializer dmxUnserializer;
+ return dmxUnserializer.Unserialize( buf, nEncodingVersion, ppRoot );
+ }
+ return UnserializeTextDMX( pFileName ? pFileName : "<no file>", buf, ppRoot );
+}
+
+bool UnserializeDMX( const char *pFileName, const char *pPathID, bool bTextMode, CDmxElement **ppRoot )
+{
+ // NOTE: This guarantees full path names for pathids
+ char pBuf[MAX_PATH];
+ const char *pFullPath = pFileName;
+ if ( !Q_IsAbsolutePath( pFullPath ) && !pPathID )
+ {
+ char pDir[MAX_PATH];
+ if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof(pDir) ) )
+ {
+ Q_ComposeFileName( pDir, pFileName, pBuf, sizeof(pBuf) );
+ Q_RemoveDotSlashes( pBuf );
+ pFullPath = pBuf;
+ }
+ }
+
+ int nFlags = CUtlBuffer::READ_ONLY;
+ if ( bTextMode )
+ {
+ nFlags |= CUtlBuffer::TEXT_BUFFER;
+ }
+
+ CUtlBuffer buf( 0, 0, nFlags );
+ g_pFullFileSystem->ReadFile( pFullPath, pPathID, buf );
+
+ if ( !buf.IsValid() )
+ {
+ Warning( "UnserializeDMX: Unable to open file \"%s\"\n", pFullPath );
+ return false;
+ }
+
+ return UnserializeDMX( buf, ppRoot, pFullPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up read-in elements
+//-----------------------------------------------------------------------------
+void CleanupDMX( CDmxElement *pRoot )
+{
+ if ( pRoot )
+ {
+ pRoot->RemoveAllElementsRecursive();
+ }
+}
diff --git a/dmxloader/dmxloader.vpc b/dmxloader/dmxloader.vpc
new file mode 100644
index 0000000..d3972c3
--- /dev/null
+++ b/dmxloader/dmxloader.vpc
@@ -0,0 +1,41 @@
+//-----------------------------------------------------------------------------
+// DMXLOADER.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+
+$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;DMXLOADER_LIB"
+ }
+}
+
+$Project "Dmxloader"
+{
+ $Folder "Source Files"
+ {
+ $File "dmxattribute.cpp"
+ $File "dmxelement.cpp"
+ $File "dmxloader.cpp"
+ $File "dmxloadertext.cpp"
+ $File "dmxserializationdictionary.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "dmxserializationdictionary.h"
+ }
+
+ $Folder "Interface"
+ {
+ $File "$SRCDIR\public\dmxloader\dmxattribute.h"
+ $File "$SRCDIR\public\dmxloader\dmxelement.h"
+ $File "$SRCDIR\public\dmxloader\dmxloader.h"
+ }
+}
diff --git a/dmxloader/dmxloadertext.cpp b/dmxloader/dmxloadertext.cpp
new file mode 100644
index 0000000..ba4ea7f
--- /dev/null
+++ b/dmxloader/dmxloadertext.cpp
@@ -0,0 +1,1442 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmxloader/dmxelement.h"
+#include <ctype.h>
+#include "tier1/utlbuffer.h"
+#include "tier1/utlbufferutil.h"
+#include <limits.h>
+#include "dmxserializationdictionary.h"
+
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class CUtlBuffer;
+extern const char *g_pAttributeTypeName[AT_TYPE_COUNT];
+
+
+//-----------------------------------------------------------------------------
+// a simple class to keep track of a stack of valid parsed symbols
+//-----------------------------------------------------------------------------
+class CDmxKeyValues2ErrorStack
+{
+public:
+ CDmxKeyValues2ErrorStack();
+
+ // Sets the filename to report with errors; sets the line number to 0
+ void SetFilename( const char *pFilename );
+
+ // Current line control
+ void IncrementCurrentLine();
+ void SetCurrentLine( int nLine );
+ int GetCurrentLine() const;
+
+ // entering a new keyvalues block, save state for errors
+ // Not save symbols instead of pointers because the pointers can move!
+ int Push( CUtlSymbol symName );
+
+ // exiting block, error isn't in this block, remove.
+ void Pop();
+
+ // Allows you to keep the same stack level, but change the name as you parse peers
+ void Reset( int stackLevel, CUtlSymbol symName );
+
+ // Hit an error, report it and the parsing stack for context
+ void ReportError( const char *pError, ... );
+
+ static CUtlSymbolTable& GetSymbolTable() { return m_ErrorSymbolTable; }
+
+private:
+ enum
+ {
+ MAX_ERROR_STACK = 64
+ };
+
+ CUtlSymbol m_errorStack[MAX_ERROR_STACK];
+ const char *m_pFilename;
+ int m_nFileLine;
+ int m_errorIndex;
+ int m_maxErrorIndex;
+
+ static CUtlSymbolTable m_ErrorSymbolTable;
+};
+
+
+CUtlSymbolTable CDmxKeyValues2ErrorStack::m_ErrorSymbolTable;
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CDmxKeyValues2ErrorStack g_KeyValues2ErrorStack;
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDmxKeyValues2ErrorStack::CDmxKeyValues2ErrorStack() :
+ m_pFilename("NULL"), m_errorIndex(0), m_maxErrorIndex(0), m_nFileLine(1)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the filename
+//-----------------------------------------------------------------------------
+void CDmxKeyValues2ErrorStack::SetFilename( const char *pFilename )
+{
+ m_pFilename = pFilename;
+ m_maxErrorIndex = 0;
+ m_nFileLine = 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Current line control
+//-----------------------------------------------------------------------------
+void CDmxKeyValues2ErrorStack::IncrementCurrentLine()
+{
+ ++m_nFileLine;
+}
+
+void CDmxKeyValues2ErrorStack::SetCurrentLine( int nLine )
+{
+ m_nFileLine = nLine;
+}
+
+int CDmxKeyValues2ErrorStack::GetCurrentLine() const
+{
+ return m_nFileLine;
+}
+
+
+//-----------------------------------------------------------------------------
+// entering a new keyvalues block, save state for errors
+// Not save symbols instead of pointers because the pointers can move!
+//-----------------------------------------------------------------------------
+int CDmxKeyValues2ErrorStack::Push( CUtlSymbol symName )
+{
+ if ( m_errorIndex < MAX_ERROR_STACK )
+ {
+ m_errorStack[m_errorIndex] = symName;
+ }
+ m_errorIndex++;
+ m_maxErrorIndex = max( m_maxErrorIndex, (m_errorIndex-1) );
+ return m_errorIndex-1;
+}
+
+
+//-----------------------------------------------------------------------------
+// exiting block, error isn't in this block, remove.
+//-----------------------------------------------------------------------------
+void CDmxKeyValues2ErrorStack::Pop()
+{
+ m_errorIndex--;
+ Assert(m_errorIndex>=0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Allows you to keep the same stack level, but change the name as you parse peers
+//-----------------------------------------------------------------------------
+void CDmxKeyValues2ErrorStack::Reset( int stackLevel, CUtlSymbol symName )
+{
+ Assert( stackLevel >= 0 && stackLevel < m_errorIndex );
+ m_errorStack[stackLevel] = symName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hit an error, report it and the parsing stack for context
+//-----------------------------------------------------------------------------
+void CDmxKeyValues2ErrorStack::ReportError( const char *pFmt, ... )
+{
+ char temp[2048];
+
+ va_list args;
+ va_start( args, pFmt );
+ Q_vsnprintf( temp, sizeof( temp ), pFmt, args );
+ va_end( args );
+
+ Warning( "%s(%d) : %s\n", m_pFilename, m_nFileLine, temp );
+
+ for ( int i = 0; i < m_maxErrorIndex; i++ )
+ {
+ if ( !m_errorStack[i].IsValid() )
+ continue;
+
+ if ( i < m_errorIndex )
+ {
+ Warning( "%s, ", GetSymbolTable().String( m_errorStack[i] ) );
+ }
+ else
+ {
+ Warning( "(*%s*), ", GetSymbolTable().String( m_errorStack[i] ) );
+ }
+ }
+ Warning( "\n" );
+}
+
+
+//-----------------------------------------------------------------------------
+// a simple helper that creates stack entries as it goes in & out of scope
+//-----------------------------------------------------------------------------
+class CKeyValues2ErrorContext
+{
+public:
+ CKeyValues2ErrorContext( const char *pSymName )
+ {
+ Init( CDmxKeyValues2ErrorStack::GetSymbolTable().AddString( pSymName ) );
+ }
+
+ CKeyValues2ErrorContext( CUtlSymbol symName )
+ {
+ Init( symName );
+ }
+
+ ~CKeyValues2ErrorContext()
+ {
+ g_KeyValues2ErrorStack.Pop();
+ }
+
+ void Reset( CUtlSymbol symName )
+ {
+ g_KeyValues2ErrorStack.Reset( m_stackLevel, symName );
+ }
+
+private:
+ void Init( CUtlSymbol symName )
+ {
+ m_stackLevel = g_KeyValues2ErrorStack.Push( symName );
+ }
+
+ int m_stackLevel;
+};
+
+
+//-----------------------------------------------------------------------------
+// Element dictionary used in unserialization
+//-----------------------------------------------------------------------------
+typedef int DmxElementDictHandle_t;
+enum
+{
+ ELEMENT_DICT_HANDLE_INVALID = (DmxElementDictHandle_t)~0
+};
+
+
+class CDmxElementDictionary
+{
+public:
+ CDmxElementDictionary();
+
+ DmxElementDictHandle_t InsertElement( CDmxElement *pElement );
+ CDmxElement *GetElement( DmxElementDictHandle_t handle );
+ void AddAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &pElementId );
+ void AddArrayAttribute( CDmxAttribute *pAttribute, DmxElementDictHandle_t hChild );
+ void AddArrayAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &pElementId );
+
+ // Finds an element into the table
+ DmxElementDictHandle_t FindElement( CDmxElement *pElement );
+ DmxElementDictHandle_t FindElement( const DmObjectId_t &objectId );
+
+ // Sets the element id for an element
+ void SetElementId( DmxElementDictHandle_t hElement, const DmObjectId_t &objectId );
+
+ // Hook up all element references (which were unserialized as object ids)
+ void HookUpElementReferences();
+
+ // Clears the dictionary
+ void Clear();
+
+ // iteration through elements
+ DmxElementDictHandle_t FirstElement() { return 0; }
+ DmxElementDictHandle_t NextElement( DmxElementDictHandle_t h )
+ {
+ return m_Dict.IsValidIndex( h+1 ) ? h+1 : ELEMENT_DICT_HANDLE_INVALID;
+ }
+
+private:
+ struct DictInfo_t
+ {
+ CDmxElement *m_pElement;
+ DmObjectId_t m_Id;
+ };
+
+ struct AttributeInfo_t
+ {
+ CDmxAttribute *m_pAttribute;
+ DmAttributeType_t m_nType; // AT_ELEMENT or AT_OBJECTID
+ union
+ {
+ DmxElementDictHandle_t m_hElement;
+ DmObjectId_t m_ObjectId;
+ };
+ };
+ typedef CUtlVector<AttributeInfo_t> AttributeList_t;
+
+ // Hook up all element references (which were unserialized as object ids)
+ void HookUpElementAttributes();
+ void HookUpElementArrayAttributes();
+
+ CUtlVector< DictInfo_t > m_Dict;
+ AttributeList_t m_Attributes;
+ AttributeList_t m_ArrayAttributes;
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDmxElementDictionary::CDmxElementDictionary()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Clears the dictionary
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::Clear()
+{
+ m_Dict.Purge();
+ m_Attributes.Purge();
+ m_ArrayAttributes.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+// Inserts an element into the table
+//-----------------------------------------------------------------------------
+DmxElementDictHandle_t CDmxElementDictionary::InsertElement( CDmxElement *pElement )
+{
+ // Insert it into the reconnection table
+ DmxElementDictHandle_t h = m_Dict.AddToTail( );
+ m_Dict[h].m_pElement = pElement;
+ InvalidateUniqueId( &m_Dict[h].m_Id );
+ return h;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the element id for an element
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::SetElementId( DmxElementDictHandle_t hElement, const DmObjectId_t &objectId )
+{
+ Assert( hElement != ELEMENT_DICT_HANDLE_INVALID );
+ CopyUniqueId( objectId, &m_Dict[hElement].m_Id );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a particular element
+//-----------------------------------------------------------------------------
+CDmxElement *CDmxElementDictionary::GetElement( DmxElementDictHandle_t handle )
+{
+ if ( handle == ELEMENT_DICT_HANDLE_INVALID )
+ return NULL;
+
+ return m_Dict[ handle ].m_pElement;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an attribute to the fixup list
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::AddAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &objectId )
+{
+ int i = m_Attributes.AddToTail();
+ m_Attributes[i].m_nType = AT_OBJECTID;
+ m_Attributes[i].m_pAttribute = pAttribute;
+ CopyUniqueId( objectId, &m_Attributes[i].m_ObjectId );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an element of an attribute array to the fixup list
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::AddArrayAttribute( CDmxAttribute *pAttribute, DmxElementDictHandle_t hElement )
+{
+ int i = m_ArrayAttributes.AddToTail();
+ m_ArrayAttributes[i].m_nType = AT_ELEMENT;
+ m_ArrayAttributes[i].m_pAttribute = pAttribute;
+ m_ArrayAttributes[i].m_hElement = hElement;
+}
+
+void CDmxElementDictionary::AddArrayAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &objectId )
+{
+ int i = m_ArrayAttributes.AddToTail();
+ m_ArrayAttributes[i].m_nType = AT_OBJECTID;
+ m_ArrayAttributes[i].m_pAttribute = pAttribute;
+ CopyUniqueId( objectId, &m_ArrayAttributes[i].m_ObjectId );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an element into the table
+//-----------------------------------------------------------------------------
+DmxElementDictHandle_t CDmxElementDictionary::FindElement( CDmxElement *pElement )
+{
+ int nCount = m_Dict.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( pElement == m_Dict[i].m_pElement )
+ return i;
+ }
+ return ELEMENT_DICT_HANDLE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an element into the table
+//-----------------------------------------------------------------------------
+DmxElementDictHandle_t CDmxElementDictionary::FindElement( const DmObjectId_t &objectId )
+{
+ int nCount = m_Dict.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( IsUniqueIdEqual( objectId, m_Dict[i].m_Id ) )
+ return i;
+ }
+ return ELEMENT_DICT_HANDLE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook up all element references (which were unserialized as object ids)
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::HookUpElementAttributes()
+{
+ int n = m_Attributes.Count();
+ for ( int i = 0; i < n; ++i )
+ {
+ Assert( m_Attributes[i].m_nType == AT_OBJECTID );
+
+ DmxElementDictHandle_t hElement = FindElement( m_Attributes[i].m_ObjectId );
+ CDmxElement *pElement = GetElement( hElement );
+ m_Attributes[i].m_pAttribute->SetValue( pElement );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook up all element array references
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::HookUpElementArrayAttributes()
+{
+ int n = m_ArrayAttributes.Count();
+ for ( int i = 0; i < n; ++i )
+ {
+ CUtlVector< CDmxElement* > &array = m_ArrayAttributes[i].m_pAttribute->GetArrayForEdit<CDmxElement*>();
+
+ if ( m_ArrayAttributes[i].m_nType == AT_ELEMENT )
+ {
+ CDmxElement *pElement = GetElement( m_ArrayAttributes[i].m_hElement );
+ array.AddToTail( pElement );
+ }
+ else
+ {
+ // search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
+ DmxElementDictHandle_t hElement = FindElement( m_ArrayAttributes[i].m_ObjectId );
+ CDmxElement *pElement = GetElement( hElement );
+ array.AddToTail( pElement );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook up all element references (which were unserialized as object ids)
+//-----------------------------------------------------------------------------
+void CDmxElementDictionary::HookUpElementReferences()
+{
+ HookUpElementArrayAttributes();
+ HookUpElementAttributes();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Unserialization class for Key Values 2
+//-----------------------------------------------------------------------------
+class CDmxSerializerKeyValues2
+{
+public:
+ bool Unserialize( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot );
+ bool Serialize( CUtlBuffer &buf, CDmxElement *pRoot, const char *pFileName );
+
+private:
+ enum TokenType_t
+ {
+ TOKEN_INVALID = -1, // A bogus token
+ TOKEN_OPEN_BRACE, // {
+ TOKEN_CLOSE_BRACE, // }
+ TOKEN_OPEN_BRACKET, // [
+ TOKEN_CLOSE_BRACKET, // ]
+ TOKEN_COMMA, // ,
+// TOKEN_STRING, // Any non-quoted string
+ TOKEN_DELIMITED_STRING, // Any quoted string
+ TOKEN_INCLUDE, // #include
+ TOKEN_EOF, // End of buffer
+ };
+
+ // Methods related to unserialization
+ void EatWhitespacesAndComments( CUtlBuffer &buf );
+ TokenType_t ReadToken( CUtlBuffer &buf, CUtlBuffer &token );
+ DmxElementDictHandle_t CreateDmxElement( const char *pElementType );
+ bool UnserializeAttributeValueFromToken( CDmxAttribute *pAttribute, DmAttributeType_t type, CUtlBuffer &tokenBuf );
+ bool UnserializeElementAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, const char *pElementType );
+ bool UnserializeElementArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName );
+ bool UnserializeArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType );
+ bool UnserializeAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType );
+ bool UnserializeElement( CUtlBuffer &buf, const char *pElementType, DmxElementDictHandle_t *pHandle );
+ bool UnserializeElement( CUtlBuffer &buf, DmxElementDictHandle_t *pHandle );
+
+ // Methods related to serialization
+ void SerializeArrayAttribute( CUtlBuffer& buf, CDmxAttribute *pAttribute );
+ void SerializeElementAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute );
+ void SerializeElementArrayAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute );
+ bool SerializeAttributes( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement );
+ bool SaveElement( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement, bool bWriteDelimiters = true );
+
+ // For unserialization
+ CDmxElementDictionary m_ElementDict;
+ DmxElementDictHandle_t m_hRoot;
+};
+
+
+//-----------------------------------------------------------------------------
+// Serializes a single element attribute
+//-----------------------------------------------------------------------------
+void CDmxSerializerKeyValues2::SerializeElementAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute )
+{
+ CDmxElement *pElement = pAttribute->GetValue< CDmxElement* >();
+ if ( dict.ShouldInlineElement( pElement ) )
+ {
+ buf.Printf( "\"%s\"\n{\n", pElement->GetTypeString() );
+ if ( pElement )
+ {
+ SaveElement( buf, dict, pElement, false );
+ }
+ buf.Printf( "}\n" );
+ }
+ else
+ {
+ buf.Printf( "\"%s\" \"", g_pAttributeTypeName[ AT_ELEMENT ] );
+ if ( pElement )
+ {
+ ::Serialize( buf, pElement->GetId() );
+ }
+ buf.PutChar( '\"' );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Serializes an array element attribute
+//-----------------------------------------------------------------------------
+void CDmxSerializerKeyValues2::SerializeElementArrayAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute )
+{
+ const CUtlVector<CDmxElement*> &array = pAttribute->GetArray< CDmxElement* >();
+
+ buf.Printf( "\n[\n" );
+ buf.PushTab();
+
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxElement *pElement = array[i];
+ if ( dict.ShouldInlineElement( pElement ) )
+ {
+ buf.Printf( "\"%s\"\n{\n", pElement->GetTypeString() );
+ if ( pElement )
+ {
+ SaveElement( buf, dict, pElement, false );
+ }
+ buf.PutChar( '}' );
+ }
+ else
+ {
+ const char *pAttributeType = g_pAttributeTypeName[ AT_ELEMENT ];
+ buf.Printf( "\"%s\" \"", pAttributeType );
+ if ( pElement )
+ {
+ ::Serialize( buf, pElement->GetId() );
+ }
+ buf.PutChar( '\"' );
+ }
+
+ if ( i != nCount - 1 )
+ {
+ buf.PutChar( ',' );
+ }
+ buf.PutChar( '\n' );
+ }
+
+ buf.PopTab();
+ buf.Printf( "]" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Serializes array attributes
+//-----------------------------------------------------------------------------
+void CDmxSerializerKeyValues2::SerializeArrayAttribute( CUtlBuffer& buf, CDmxAttribute *pAttribute )
+{
+ int nCount = pAttribute->GetArrayCount();
+
+ buf.PutString( "\n[\n" );
+ buf.PushTab();
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( pAttribute->GetType() != AT_STRING_ARRAY )
+ {
+ buf.PutChar( '\"' );
+ buf.PushTab();
+ }
+
+ pAttribute->SerializeElement( i, buf );
+
+ if ( pAttribute->GetType() != AT_STRING_ARRAY )
+ {
+ buf.PopTab();
+ buf.PutChar( '\"' );
+ }
+
+ if ( i != nCount - 1 )
+ {
+ buf.PutChar( ',' );
+ }
+ buf.PutChar( '\n' );
+ }
+ buf.PopTab();
+ buf.PutChar( ']' );
+}
+
+
+//-----------------------------------------------------------------------------
+// Serializes all attributes in an element
+//-----------------------------------------------------------------------------
+static int SortAttributeByName(const void *p1, const void *p2 )
+{
+ const CDmxAttribute **ppAtt1 = (const CDmxAttribute**)p1;
+ const CDmxAttribute **ppAtt2 = (const CDmxAttribute**)p2;
+ const char *pAttName1 = (*ppAtt1)->GetName();
+ const char *pAttName2 = (*ppAtt2)->GetName();
+ return Q_stricmp( pAttName1, pAttName2 );
+}
+
+bool CDmxSerializerKeyValues2::SerializeAttributes( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement )
+{
+ int nCount = pElement->AttributeCount();
+ CDmxAttribute **ppAttributes = (CDmxAttribute**)stackalloc( nCount * sizeof(CDmxAttribute*) );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ ppAttributes[i] = pElement->GetAttribute( i );
+ }
+
+ // Sort by name
+ qsort( ppAttributes, nCount, sizeof(CDmxAttribute*), SortAttributeByName );
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxAttribute *pAttribute = ppAttributes[ i ];
+
+ const char *pName = pAttribute->GetName( );
+ DmAttributeType_t nAttrType = pAttribute->GetType();
+ if ( nAttrType != AT_ELEMENT )
+ {
+ buf.Printf( "\"%s\" \"%s\" ", pName, g_pAttributeTypeName[ nAttrType ] );
+ }
+ else
+ {
+ // Elements either serialize their type name or "element" depending on whether they are inlined
+ buf.Printf( "\"%s\" ", pName );
+ }
+
+ switch( nAttrType )
+ {
+ default:
+ if ( nAttrType >= AT_FIRST_ARRAY_TYPE )
+ {
+ SerializeArrayAttribute( buf, pAttribute );
+ }
+ else
+ {
+ if ( pAttribute->SerializesOnMultipleLines() )
+ {
+ buf.PutChar( '\n' );
+ }
+
+ buf.PutChar( '\"' );
+ buf.PushTab();
+ pAttribute->Serialize( buf );
+ buf.PopTab();
+ buf.PutChar( '\"' );
+ }
+ break;
+
+ case AT_STRING:
+ // Don't explicitly add string delimiters; serialization does that.
+ pAttribute->Serialize( buf );
+ break;
+
+ case AT_ELEMENT:
+ SerializeElementAttribute( buf, dict, pAttribute );
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ SerializeElementArrayAttribute( buf, dict, pAttribute );
+ break;
+ }
+
+ buf.PutChar( '\n' );
+ }
+
+ return true;
+}
+
+bool CDmxSerializerKeyValues2::SaveElement( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement, bool bWriteDelimiters )
+{
+ if ( bWriteDelimiters )
+ {
+ buf.Printf( "\"%s\"\n{\n", pElement->GetTypeString() );
+ }
+ buf.PushTab();
+
+ // explicitly serialize id, now that it's no longer an attribute
+ buf.Printf( "\"id\" \"%s\" ", g_pAttributeTypeName[ AT_OBJECTID ] );
+ buf.PutChar( '\"' );
+ ::Serialize( buf, pElement->GetId() );
+ buf.PutString( "\"\n" );
+
+ SerializeAttributes( buf, dict, pElement );
+
+ buf.PopTab();
+ if ( bWriteDelimiters )
+ {
+ buf.Printf( "}\n" );
+ }
+ return true;
+}
+
+bool CDmxSerializerKeyValues2::Serialize( CUtlBuffer &outBuf, CDmxElement *pRoot, const char *pFormatName )
+{
+ SetSerializationDelimiter( GetCStringCharConversion() );
+ SetSerializationArrayDelimiter( "," );
+
+ bool bFlatMode = !Q_stricmp( pFormatName, "keyvalues2_flat" );
+
+ // Save elements, attribute links
+ CDmxSerializationDictionary dict;
+ dict.BuildElementList( pRoot, bFlatMode );
+
+ // Save elements to buffer
+ DmxSerializationHandle_t i;
+ for ( i = dict.FirstRootElement(); i != ELEMENT_DICT_HANDLE_INVALID; i = dict.NextRootElement(i) )
+ {
+ SaveElement( outBuf, dict, dict.GetRootElement( i ) );
+ outBuf.PutChar( '\n' );
+ }
+
+ SetSerializationDelimiter( NULL );
+ SetSerializationArrayDelimiter( NULL );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Eats whitespaces and c++ style comments
+//-----------------------------------------------------------------------------
+#pragma warning (disable:4706)
+
+void CDmxSerializerKeyValues2::EatWhitespacesAndComments( CUtlBuffer &buf )
+{
+ // eating white spaces and remarks loop
+ int nMaxPut = buf.TellMaxPut() - buf.TellGet();
+ int nOffset = 0;
+ while ( nOffset < nMaxPut )
+ {
+ // Eat whitespaces, keep track of line count
+ const char *pPeek = NULL;
+ while ( (pPeek = (const char *)buf.PeekGet( sizeof(char), nOffset ) ) )
+ {
+ if ( !isspace( *pPeek ) )
+ break;
+
+ if ( *pPeek == '\n' )
+ {
+ g_KeyValues2ErrorStack.IncrementCurrentLine();
+ }
+ if ( ++nOffset >= nMaxPut )
+ break;
+ }
+
+ // If we don't have a a c++ style comment next, we're done
+ pPeek = (const char *)buf.PeekGet( 2 * sizeof(char), nOffset );
+ if ( ( nOffset >= nMaxPut ) || !pPeek || ( pPeek[0] != '/' ) || ( pPeek[1] != '/' ) )
+ break;
+
+ // Deal with c++ style comments
+ nOffset += 2;
+
+ // read complete line
+ while ( ( pPeek = (const char *)buf.PeekGet( sizeof(char), nOffset ) ) )
+ {
+ if ( *pPeek == '\n' )
+ break;
+ if ( ++nOffset >= nMaxPut )
+ break;
+ }
+
+ g_KeyValues2ErrorStack.IncrementCurrentLine();
+ }
+
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nOffset );
+}
+
+#pragma warning (default:4706)
+
+//-----------------------------------------------------------------------------
+// Reads a single token, points the token utlbuffer at it
+//-----------------------------------------------------------------------------
+CDmxSerializerKeyValues2::TokenType_t CDmxSerializerKeyValues2::ReadToken( CUtlBuffer &buf, CUtlBuffer &token )
+{
+ EatWhitespacesAndComments( buf );
+
+ // if message text buffers go over this size
+ // change this value to make sure they will fit
+ // affects loading of last active chat window
+ if ( !buf.IsValid() || ( buf.TellGet() == buf.TellMaxPut() ) )
+ return TOKEN_EOF;
+
+ // Compute token length and type
+ int nLength = 0;
+ TokenType_t t = TOKEN_INVALID;
+ char c = *((const char *)buf.PeekGet());
+ switch( c )
+ {
+ case '{':
+ nLength = 1;
+ t = TOKEN_OPEN_BRACE;
+ break;
+
+ case '}':
+ nLength = 1;
+ t = TOKEN_CLOSE_BRACE;
+ break;
+
+ case '[':
+ nLength = 1;
+ t = TOKEN_OPEN_BRACKET;
+ break;
+
+ case ']':
+ nLength = 1;
+ t = TOKEN_CLOSE_BRACKET;
+ break;
+
+ case ',':
+ nLength = 1;
+ t = TOKEN_COMMA;
+ break;
+
+ case '\"':
+ // NOTE: The -1 is because peek includes room for the /0
+ nLength = buf.PeekDelimitedStringLength( GetCStringCharConversion(), false ) - 1;
+ if ( (nLength <= 1) || ( *(const char *)buf.PeekGet( nLength - 1 ) != '\"' ))
+ {
+ g_KeyValues2ErrorStack.ReportError( "Unexpected EOF in quoted string" );
+ t = TOKEN_INVALID;
+ }
+ else
+ {
+ t = TOKEN_DELIMITED_STRING;
+ }
+ break;
+
+ default:
+ t = TOKEN_INVALID;
+ break;
+ }
+
+ // Point the token buffer to the token + update the original buffer get index
+ token.SetExternalBuffer( (void*)buf.PeekGet(), nLength, nLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nLength );
+
+ // Count the number of crs in the token + update the current line
+ const char *pMem = (const char *)token.Base();
+ for ( int i = 0; i < nLength; ++i )
+ {
+ if ( pMem[i] == '\n' )
+ {
+ g_KeyValues2ErrorStack.IncrementCurrentLine();
+ }
+ }
+
+ return t;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a scene object, adds it to the element dictionary
+//-----------------------------------------------------------------------------
+DmxElementDictHandle_t CDmxSerializerKeyValues2::CreateDmxElement( const char *pElementType )
+{
+ // See if we can create an element of that type
+ CDmxElement *pElement = new CDmxElement( pElementType );
+ return m_ElementDict.InsertElement( pElement );
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an attribute for an element
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeElementAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, const char *pElementType )
+{
+ CDmxElement *pElement = m_ElementDict.GetElement( hElement );
+ if ( pElement->HasAttribute( pAttributeName ) )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Attribute \"%s\" was defined more than once.\n", pAttributeName );
+ return false;
+ }
+
+ CDmxAttribute *pAttribute;
+ {
+ CDmxElementModifyScope modify( pElement );
+ pAttribute = pElement->AddAttribute( pAttributeName );
+ }
+
+ DmxElementDictHandle_t h;
+ bool bOk = UnserializeElement( buf, pElementType, &h );
+ if ( bOk )
+ {
+ CDmxElement *pNewElement = m_ElementDict.GetElement( h );
+ pAttribute->SetValue( pNewElement );
+ }
+ return bOk;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an attribute for an element array
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeElementArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName )
+{
+ CDmxElement *pElement = m_ElementDict.GetElement( hElement );
+ if ( pElement->HasAttribute( pAttributeName ) )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Attribute \"%s\" was defined more than once.\n", pAttributeName );
+ return false;
+ }
+
+ CDmxAttribute *pAttribute;
+ {
+ CDmxElementModifyScope modify( pElement );
+ pAttribute = pElement->AddAttribute( pAttributeName );
+ }
+
+ // Arrays first must have a '[' specified
+ TokenType_t token;
+ CUtlBuffer tokenBuf;
+ CUtlCharConversion *pConv;
+ token = ReadToken( buf, tokenBuf );
+ if ( token != TOKEN_OPEN_BRACKET )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting '[', didn't find it!" );
+ return false;
+ }
+
+ int nElementIndex = 0;
+
+ // Now read a list of array values, separated by commas
+ while ( buf.IsValid() )
+ {
+ token = ReadToken( buf, tokenBuf );
+ if ( token == TOKEN_INVALID || token == TOKEN_EOF )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting ']', didn't find it!" );
+ return false;
+ }
+
+ // Then, keep reading until we hit a ']'
+ if ( token == TOKEN_CLOSE_BRACKET )
+ break;
+
+ // If we've already read in an array value, we need to read a comma next
+ if ( nElementIndex > 0 )
+ {
+ if ( token != TOKEN_COMMA )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting ',', didn't find it!" );
+ return false;
+ }
+
+ // Read in the next thing, which should be a value
+ token = ReadToken( buf, tokenBuf );
+ }
+
+ // Ok, we must be reading an array type value
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting element type, didn't find it!" );
+ return false;
+ }
+
+ // Get the element type out
+ pConv = GetCStringCharConversion();
+ int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pElementType = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pElementType, nLength );
+
+ // Use the element type to figure out if we're using a element reference or an inlined element
+ if ( !Q_strncmp( pElementType, g_pAttributeTypeName[AT_ELEMENT], nLength ) )
+ {
+ token = ReadToken( buf, tokenBuf );
+
+ // Ok, we must be reading an array type value
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting element reference, didn't find it!" );
+ return false;
+ }
+
+ // Get the element type out
+ pConv = GetCStringCharConversion();
+ nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pElementId = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pElementId, nLength );
+
+ DmObjectId_t id;
+ if ( !UniqueIdFromString( &id, pElementId ) )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Encountered invalid element ID data!" );
+ return false;
+ }
+
+ Assert( IsUniqueIdValid( id ) );
+ m_ElementDict.AddArrayAttribute( pAttribute, id );
+ }
+ else
+ {
+ DmxElementDictHandle_t hArrayElement;
+ bool bOk = UnserializeElement( buf, pElementType, &hArrayElement );
+ if ( !bOk )
+ return false;
+ m_ElementDict.AddArrayAttribute( pAttribute, hArrayElement );
+ }
+
+ // Ok, we've read in another value
+ ++nElementIndex;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserializes an attribute from a token buffer
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeAttributeValueFromToken( CDmxAttribute *pAttribute, DmAttributeType_t type, CUtlBuffer &tokenBuf )
+{
+ // NOTE: This code is necessary because the attribute code is using Scanf
+ // which is not really friendly toward delimiters, so we must pass in
+ // non-delimited buffers. Sucky. There must be a better way of doing this
+ const char *pBuf = (const char*)tokenBuf.Base();
+ int nLength = tokenBuf.TellMaxPut();
+ char *pTemp = (char*)stackalloc( nLength + 1 );
+
+ bool bIsString = ( type == AT_STRING ) || ( type == AT_STRING_ARRAY );
+ if ( !bIsString )
+ {
+ nLength = tokenBuf.PeekDelimitedStringLength( GetCStringCharConversion() );
+ tokenBuf.GetDelimitedString( GetCStringCharConversion(), pTemp, nLength + 1 );
+ pBuf = pTemp;
+ }
+ else
+ {
+ SetSerializationDelimiter( GetCStringCharConversion() );
+ }
+
+ bool bOk;
+ CUtlBuffer buf( pBuf, nLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ if ( type < AT_FIRST_ARRAY_TYPE )
+ {
+ bOk = pAttribute->Unserialize( type, buf );
+ }
+ else
+ {
+ bOk = pAttribute->UnserializeElement( type, buf );
+ }
+
+ if ( bIsString )
+ {
+ SetSerializationDelimiter( NULL );
+ }
+
+ return bOk;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an attribute for an element array
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType )
+{
+ CDmxElement *pElement = m_ElementDict.GetElement( hElement );
+ if ( pElement->HasAttribute( pAttributeName ) )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Encountered duplicate attribute definition for attribute \"%s\"!", pAttributeName );
+ return false;
+ }
+
+ CDmxAttribute *pAttribute;
+ {
+ CDmxElementModifyScope modify( pElement );
+ pAttribute = pElement->AddAttribute( pAttributeName );
+ }
+
+ // Arrays first must have a '[' specified
+ TokenType_t token;
+ CUtlBuffer tokenBuf;
+ token = ReadToken( buf, tokenBuf );
+ if ( token != TOKEN_OPEN_BRACKET )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting '[', didn't find it!" );
+ return false;
+ }
+
+ int nElementIndex = 0;
+
+ // Now read a list of array values, separated by commas
+ while ( buf.IsValid() )
+ {
+ token = ReadToken( buf, tokenBuf );
+ if ( token == TOKEN_INVALID || token == TOKEN_EOF )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting ']', didn't find it!" );
+ return false;
+ }
+
+ // Then, keep reading until we hit a ']'
+ if ( token == TOKEN_CLOSE_BRACKET )
+ break;
+
+ // If we've already read in an array value, we need to read a comma next
+ if ( nElementIndex > 0 )
+ {
+ if ( token != TOKEN_COMMA )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting ',', didn't find it!" );
+ return false;
+ }
+
+ // Read in the next thing, which should be a value
+ token = ReadToken( buf, tokenBuf );
+ }
+
+ // Ok, we must be reading an attributearray value
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting array attribute value, didn't find it!" );
+ return false;
+ }
+
+ if ( !UnserializeAttributeValueFromToken( pAttribute, nAttrType, tokenBuf ) )
+ {
+ g_KeyValues2ErrorStack.ReportError("Error reading in array attribute \"%s\" element %d", pAttributeName, nElementIndex );
+ return false;
+ }
+
+ // Ok, we've read in another value
+ ++nElementIndex;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads an attribute for an element
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeAttribute( CUtlBuffer &buf,
+ DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType )
+{
+ // Read the attribute value
+ CUtlBuffer tokenBuf;
+ TokenType_t token = ReadToken( buf, tokenBuf );
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting quoted attribute value for attribute \"%s\", didn't find one!", pAttributeName );
+ return false;
+ }
+
+ CDmxElement *pElement = m_ElementDict.GetElement( hElement );
+ if ( ( nAttrType == AT_OBJECTID ) && !Q_strnicmp( pAttributeName, "id", 3 ) )
+ {
+ CUtlCharConversion *pConv = GetCStringCharConversion();
+ int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pElementId = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pElementId, nLength );
+
+ DmObjectId_t id;
+ if ( !UniqueIdFromString( &id, pElementId ) )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Encountered invalid element ID data!" );
+ return false;
+ }
+
+ m_ElementDict.SetElementId( hElement, id );
+ pElement->SetId( id );
+ return true;
+ }
+
+ if ( pElement->HasAttribute( pAttributeName ) )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Encountered duplicate attribute definition for attribute \"%s\"!", pAttributeName );
+ return false;
+ }
+
+ CDmxAttribute *pAttribute;
+ {
+ CDmxElementModifyScope modify( pElement );
+ pAttribute = pElement->AddAttribute( pAttributeName );
+ }
+
+ switch( nAttrType )
+ {
+ case AT_ELEMENT:
+ {
+ // Get the attribute value out
+ CUtlCharConversion *pConv = GetCStringCharConversion();
+ int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pAttributeValue = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pAttributeValue, nLength );
+
+ // No string? that's ok, it means we have a NULL pointer
+ if ( !pAttributeValue[0] )
+ return true;
+
+ DmObjectId_t id;
+ if ( !UniqueIdFromString( &id, pAttributeValue ) )
+ {
+ g_KeyValues2ErrorStack.ReportError("Invalid format for element ID encountered for attribute \"%s\"", pAttributeName );
+ return false;
+ }
+
+ m_ElementDict.AddAttribute( pAttribute, id );
+ }
+ return true;
+
+ default:
+ if ( UnserializeAttributeValueFromToken( pAttribute, nAttrType, tokenBuf ) )
+ return true;
+
+ g_KeyValues2ErrorStack.ReportError("Error reading attribute \"%s\"", pAttributeName );
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserializes a single element given the type name
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeElement( CUtlBuffer &buf, const char *pElementType, DmxElementDictHandle_t *pHandle )
+{
+ *pHandle = ELEMENT_DICT_HANDLE_INVALID;
+
+ // Create the element
+ DmxElementDictHandle_t hElement = CreateDmxElement( pElementType );
+
+ // Report errors relative to this type name
+ CKeyValues2ErrorContext errorReport( pElementType );
+
+ TokenType_t token;
+ CUtlBuffer tokenBuf;
+ CUtlCharConversion *pConv;
+ int nLength;
+
+ // Then we expect a '{'
+ token = ReadToken( buf, tokenBuf );
+ if ( token != TOKEN_OPEN_BRACE )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting '{', didn't find it!" );
+ return false;
+ }
+
+ while ( buf.IsValid() )
+ {
+ token = ReadToken( buf, tokenBuf );
+ if ( token == TOKEN_INVALID || token == TOKEN_EOF )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting '}', didn't find it!" );
+ return false;
+ }
+
+ // Then, keep reading until we hit a '}'
+ if ( token == TOKEN_CLOSE_BRACE )
+ break;
+
+ // Ok, we must be reading an attribute
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting attribute name, didn't find it!" );
+ return false;
+ }
+
+ // First, read an attribute name
+ pConv = GetCStringCharConversion();
+ nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pAttributeName = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pAttributeName, nLength );
+
+ // Next, read an attribute type
+ token = ReadToken( buf, tokenBuf );
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting attribute type for attribute %s, didn't find it!", pAttributeName );
+ return false;
+ }
+
+ pConv = GetCStringCharConversion();
+ nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pAttributeType = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pAttributeType, nLength );
+
+ DmAttributeType_t nAttrType = AT_UNKNOWN;
+ for ( int i = 0; i < AT_TYPE_COUNT; ++i )
+ {
+ if ( !Q_stricmp( g_pAttributeTypeName[i], pAttributeType ) )
+ {
+ nAttrType = (DmAttributeType_t)i;
+ break;
+ }
+ }
+
+ // Next, read an attribute value
+ bool bOk = true;
+ switch( nAttrType )
+ {
+ case AT_UNKNOWN:
+ bOk = UnserializeElementAttribute( buf, hElement, pAttributeName, pAttributeType );
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ bOk = UnserializeElementArrayAttribute( buf, hElement, pAttributeName );
+ break;
+
+ default:
+ if ( nAttrType >= AT_FIRST_ARRAY_TYPE )
+ {
+ bOk = UnserializeArrayAttribute( buf, hElement, pAttributeName, nAttrType );
+ }
+ else
+ {
+ bOk = UnserializeAttribute( buf, hElement, pAttributeName, nAttrType );
+ }
+ break;
+ }
+
+ if ( !bOk )
+ return false;
+ }
+
+ *pHandle = hElement;
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserializes a single element
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::UnserializeElement( CUtlBuffer &buf, DmxElementDictHandle_t *pHandle )
+{
+ *pHandle = ELEMENT_DICT_HANDLE_INVALID;
+
+ // First, read the type name
+ CUtlBuffer tokenBuf;
+ CUtlCharConversion* pConv;
+
+ TokenType_t token = ReadToken( buf, tokenBuf );
+ if ( token == TOKEN_INVALID )
+ return false;
+
+ if ( token == TOKEN_EOF )
+ return true;
+
+ // Get the type name out
+ if ( token != TOKEN_DELIMITED_STRING )
+ {
+ g_KeyValues2ErrorStack.ReportError( "Expecting element type name, didn't find it!" );
+ return false;
+ }
+
+ pConv = GetCStringCharConversion();
+ int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
+ char *pTypeName = (char*)stackalloc( nLength * sizeof(char) );
+ tokenBuf.GetDelimitedString( pConv, pTypeName, nLength );
+
+ return UnserializeElement( buf, pTypeName, pHandle );
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+bool CDmxSerializerKeyValues2::Unserialize( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot )
+{
+ *ppRoot = NULL;
+
+ g_KeyValues2ErrorStack.SetFilename( pFileName );
+ m_hRoot = ELEMENT_DICT_HANDLE_INVALID;
+ m_ElementDict.Clear();
+
+ bool bOk = true;
+ while ( buf.IsValid() )
+ {
+ DmxElementDictHandle_t h;
+ bOk = UnserializeElement( buf, &h );
+ if ( !bOk || ( h == ELEMENT_DICT_HANDLE_INVALID ) )
+ break;
+
+ if ( m_hRoot == ELEMENT_DICT_HANDLE_INVALID )
+ {
+ m_hRoot = h;
+ }
+ }
+
+ // do this *before* getting the root, since the first element might be deleted due to id conflicts
+ m_ElementDict.HookUpElementReferences();
+
+ *ppRoot = m_ElementDict.GetElement( m_hRoot );
+ m_ElementDict.Clear();
+
+ if ( !bOk )
+ {
+ CleanupDMX( *ppRoot );
+ *ppRoot = NULL;
+ }
+
+ return bOk;
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialization entry point for text files (assumes version has been stripped)
+//-----------------------------------------------------------------------------
+bool UnserializeTextDMX( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot )
+{
+ CDmxSerializerKeyValues2 dmxUnserializer;
+ return dmxUnserializer.Unserialize( pFileName, buf, ppRoot );
+}
+
+
+bool SerializeTextDMX( const char *pFileName, CUtlBuffer &buf, CDmxElement *pRoot )
+{
+ CDmxSerializerKeyValues2 dmxSerializer;
+ return dmxSerializer.Serialize( buf, pRoot, pFileName );
+}
diff --git a/dmxloader/dmxserializationdictionary.cpp b/dmxloader/dmxserializationdictionary.cpp
new file mode 100644
index 0000000..4e30efa
--- /dev/null
+++ b/dmxloader/dmxserializationdictionary.cpp
@@ -0,0 +1,188 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmxserializationdictionary.h"
+#include "dmxloader/dmxelement.h"
+#include "dmxloader/dmxattribute.h"
+
+
+//-----------------------------------------------------------------------------
+//
+// Element dictionary used in serialization
+//
+//-----------------------------------------------------------------------------
+CDmxSerializationDictionary::CDmxSerializationDictionary( int nElementsHint /* = 0 */ ) :
+ m_Dict( 0, nElementsHint, CDmxSerializationDictionary::LessFunc )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to sort the list of elements
+//-----------------------------------------------------------------------------
+bool CDmxSerializationDictionary::LessFunc( const DmxElementInfo_t &lhs, const DmxElementInfo_t &rhs )
+{
+ return lhs.m_pElement < rhs.m_pElement;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the handle of the element
+//-----------------------------------------------------------------------------
+DmxSerializationHandle_t CDmxSerializationDictionary::Find( CDmxElement *pElement )
+{
+ DmxElementInfo_t find;
+ find.m_pElement = pElement;
+ return m_Dict.Find( find );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates the list of all things to serialize
+//-----------------------------------------------------------------------------
+void CDmxSerializationDictionary::BuildElementList_R( CDmxElement *pElement, bool bFlatMode, bool bIsRoot )
+{
+ if ( !pElement )
+ return;
+
+ // FIXME: Right here we should ask the element if it's an external
+ // file reference and exit immediately if so.
+
+ // This means we've already encountered this guy.
+ // Therefore, he can never be a root element
+ DmxSerializationHandle_t h = Find( pElement );
+ if ( h != m_Dict.InvalidIndex() )
+ {
+ m_Dict[h].m_bRoot = true;
+ return;
+ }
+
+ DmxElementInfo_t info;
+ info.m_bRoot = bFlatMode || bIsRoot;
+ info.m_pElement = pElement;
+ m_Dict.Insert( info );
+
+ int nCount = pElement->AttributeCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmxAttribute *pAttribute = pElement->GetAttribute(i);
+ switch( pAttribute->GetType() )
+ {
+ case AT_ELEMENT:
+ {
+ CDmxElement *pChild = pAttribute->GetValue<CDmxElement*>();
+ if ( !pChild )
+ break;
+
+ BuildElementList_R( pChild, bFlatMode, false );
+ }
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ {
+ const CUtlVector<CDmxElement*> &array = pAttribute->GetArray<CDmxElement*>( );
+ int nCountArray = array.Count();
+ for ( int j = 0; j < nCountArray; ++j )
+ {
+ CDmxElement *pChild = array[ j ];
+ if ( !pChild )
+ break;
+
+ BuildElementList_R( pChild, bFlatMode, false );
+ }
+ }
+ break;
+ }
+ }
+}
+
+void CDmxSerializationDictionary::BuildElementList( CDmxElement *pElement, bool bFlatMode )
+{
+ BuildElementList_R( pElement, bFlatMode, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Should I inline the serialization of this element?
+//-----------------------------------------------------------------------------
+bool CDmxSerializationDictionary::ShouldInlineElement( CDmxElement *pElement )
+{
+ // This means we've already encountered this guy.
+ // Therefore, he can never be a root element
+ DmxSerializationHandle_t h = Find( pElement );
+ if ( h != m_Dict.InvalidIndex() )
+ return !m_Dict[h].m_bRoot;
+
+ // If we didn't find the element, it means it's a reference to an external
+ // element (or it's NULL), so don't inline ie.
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Clears the dictionary
+//-----------------------------------------------------------------------------
+void CDmxSerializationDictionary::Clear()
+{
+ m_Dict.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// How many root elements do we have?
+//-----------------------------------------------------------------------------
+int CDmxSerializationDictionary::RootElementCount() const
+{
+ int nCount = 0;
+ DmxSerializationHandle_t h = m_Dict.FirstInorder();
+ while( h != m_Dict.InvalidIndex() )
+ {
+ if ( m_Dict[h].m_bRoot )
+ {
+ ++nCount;
+ }
+ h = m_Dict.NextInorder( h );
+ }
+ return nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterates over all root elements to serialize
+//-----------------------------------------------------------------------------
+DmxSerializationHandle_t CDmxSerializationDictionary::FirstRootElement() const
+{
+ // NOTE: I don't have to use First/NextInorder here because there
+ // are guaranteed to be no removals from the dictionary.
+ // Also, using inorder traversal won't get my actual root element to be first in the file
+ int nCount = m_Dict.Count();
+ for ( DmxSerializationHandle_t h = 0; h < nCount; ++h )
+ {
+ if ( m_Dict[h].m_bRoot )
+ return h;
+ }
+ return DMX_SERIALIZATION_HANDLE_INVALID;
+}
+
+DmxSerializationHandle_t CDmxSerializationDictionary::NextRootElement( DmxSerializationHandle_t h ) const
+{
+ ++h;
+ int nCount = m_Dict.Count();
+ for ( ; h < nCount; ++h )
+ {
+ if ( m_Dict[h].m_bRoot )
+ return h;
+ }
+ return DMX_SERIALIZATION_HANDLE_INVALID;
+}
+
+CDmxElement *CDmxSerializationDictionary::GetRootElement( DmxSerializationHandle_t h )
+{
+ Assert( m_Dict[h].m_bRoot );
+ return m_Dict[h].m_pElement;
+}
+
+
diff --git a/dmxloader/dmxserializationdictionary.h b/dmxloader/dmxserializationdictionary.h
new file mode 100644
index 0000000..547aa10
--- /dev/null
+++ b/dmxloader/dmxserializationdictionary.h
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef DMXSERIALIZATIONDICTIONARY_H
+#define DMXSERIALIZATIONDICTIONARY_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tier1/utlrbtree.h"
+
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class CDmxElement;
+
+
+//-----------------------------------------------------------------------------
+// Element dictionary used in unserialization
+//-----------------------------------------------------------------------------
+typedef int DmxSerializationHandle_t;
+enum
+{
+ DMX_SERIALIZATION_HANDLE_INVALID = (DmxSerializationHandle_t)~0
+};
+
+
+//-----------------------------------------------------------------------------
+// Element dictionary used in serialization
+//-----------------------------------------------------------------------------
+class CDmxSerializationDictionary
+{
+public:
+ CDmxSerializationDictionary( int nElementsHint = 0 );
+
+ // Creates the list of all things to serialize
+ void BuildElementList( CDmxElement *pRoot, bool bFlatMode );
+
+ // Should I inline the serialization of this element?
+ bool ShouldInlineElement( CDmxElement *pElement );
+
+ // Clears the dictionary
+ void Clear();
+
+ // Iterates over all root elements to serialize
+ DmxSerializationHandle_t FirstRootElement() const;
+ DmxSerializationHandle_t NextRootElement( DmxSerializationHandle_t h ) const;
+ CDmxElement* GetRootElement( DmxSerializationHandle_t h );
+
+ // Finds the handle of the element
+ DmxSerializationHandle_t Find( CDmxElement *pElement );
+
+ // How many root elements do we have?
+ int RootElementCount() const;
+
+private:
+ struct DmxElementInfo_t
+ {
+ CDmxElement* m_pElement;
+ bool m_bRoot;
+ };
+
+ // Creates the list of all things to serialize
+ void BuildElementList_R( CDmxElement *pRoot, bool bFlatMode, bool bIsRoot );
+ static bool LessFunc( const DmxElementInfo_t &lhs, const DmxElementInfo_t &rhs );
+
+ CUtlRBTree< DmxElementInfo_t, DmxSerializationHandle_t > m_Dict;
+};
+
+
+#endif // DMXSERIALIZATIONDICTIONARY_H