summaryrefslogtreecommitdiff
path: root/dmxloader/dmxloader.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /dmxloader/dmxloader.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'dmxloader/dmxloader.cpp')
-rw-r--r--dmxloader/dmxloader.cpp697
1 files changed, 697 insertions, 0 deletions
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();
+ }
+}