summaryrefslogtreecommitdiff
path: root/dmxloader/dmxloadertext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dmxloader/dmxloadertext.cpp')
-rw-r--r--dmxloader/dmxloadertext.cpp1442
1 files changed, 1442 insertions, 0 deletions
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 );
+}