diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /dmxloader | |
| download | archived-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.cpp | 643 | ||||
| -rw-r--r-- | dmxloader/dmxelement.cpp | 489 | ||||
| -rw-r--r-- | dmxloader/dmxloader.cpp | 697 | ||||
| -rw-r--r-- | dmxloader/dmxloader.vpc | 41 | ||||
| -rw-r--r-- | dmxloader/dmxloadertext.cpp | 1442 | ||||
| -rw-r--r-- | dmxloader/dmxserializationdictionary.cpp | 188 | ||||
| -rw-r--r-- | dmxloader/dmxserializationdictionary.h | 76 |
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 |