summaryrefslogtreecommitdiff
path: root/dmserializers
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 /dmserializers
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'dmserializers')
-rw-r--r--dmserializers/dmebaseimporter.cpp42
-rw-r--r--dmserializers/dmebaseimporter.h43
-rw-r--r--dmserializers/dmserializers.cpp163
-rw-r--r--dmserializers/dmserializers.h55
-rw-r--r--dmserializers/dmserializers.vpc46
-rw-r--r--dmserializers/importactbusy.cpp182
-rw-r--r--dmserializers/importkeyvaluebase.cpp292
-rw-r--r--dmserializers/importkeyvaluebase.h84
-rw-r--r--dmserializers/importsfmv1.cpp219
-rw-r--r--dmserializers/importsfmv2.cpp294
-rw-r--r--dmserializers/importsfmv3.cpp227
-rw-r--r--dmserializers/importsfmv4.cpp123
-rw-r--r--dmserializers/importsfmv5.cpp120
-rw-r--r--dmserializers/importsfmv6.cpp140
-rw-r--r--dmserializers/importsfmv7.cpp144
-rw-r--r--dmserializers/importsfmv8.cpp139
-rw-r--r--dmserializers/importsfmv9.cpp142
-rw-r--r--dmserializers/importvmf.cpp629
-rw-r--r--dmserializers/importvmt.cpp738
19 files changed, 3822 insertions, 0 deletions
diff --git a/dmserializers/dmebaseimporter.cpp b/dmserializers/dmebaseimporter.cpp
new file mode 100644
index 0000000..7fc10bc
--- /dev/null
+++ b/dmserializers/dmebaseimporter.cpp
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+
+CDmeBaseImporter::CDmeBaseImporter( char const *formatName, char const *nextFormatName ) :
+ m_pFormatName( formatName ),
+ m_pNextSerializer( nextFormatName )
+{
+}
+
+bool CDmeBaseImporter::IsLatestVersion() const
+{
+ return g_pDataModel->FindLegacyUpdater( m_pNextSerializer ) == NULL;
+}
+
+// Updates ppRoot to first non-legacy generic dmx format, returns false if the conversion fails
+bool CDmeBaseImporter::Update( CDmElement **ppRoot )
+{
+ if ( !DoFixup( *ppRoot ) )
+ return false;
+
+ if ( !m_pNextSerializer )
+ return true;
+
+ // Chain
+ IDmLegacyUpdater *pUpdater = g_pDataModel->FindLegacyUpdater( m_pNextSerializer );
+ if ( !pUpdater )
+ return true;
+
+ return pUpdater->Update( ppRoot );
+}
+
+
+
+CSFMBaseImporter::CSFMBaseImporter( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
diff --git a/dmserializers/dmebaseimporter.h b/dmserializers/dmebaseimporter.h
new file mode 100644
index 0000000..1a74d77
--- /dev/null
+++ b/dmserializers/dmebaseimporter.h
@@ -0,0 +1,43 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef DMEBASEIMPORTER_H
+#define DMEBASEIMPORTER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "datamodel/idatamodel.h"
+
+class CDmeBaseImporter : public IDmLegacyUpdater
+{
+ typedef IDmLegacyUpdater BaseClass;
+
+public:
+ CDmeBaseImporter( char const *formatName, char const *nextFormatName );
+
+ virtual const char *GetName() const { return m_pFormatName; }
+ virtual bool IsLatestVersion() const;
+
+ virtual bool Update( CDmElement **ppRoot );
+
+private:
+ virtual bool DoFixup( CDmElement *pRoot ) = 0;
+
+protected:
+ char const *m_pFormatName;
+ char const *m_pNextSerializer;
+};
+
+class CSFMBaseImporter : public CDmeBaseImporter
+{
+ typedef CDmeBaseImporter BaseClass;
+
+public:
+ CSFMBaseImporter( char const *formatName, char const *nextFormatName );
+};
+
+#endif // DMEBASEIMPORTER_H
diff --git a/dmserializers/dmserializers.cpp b/dmserializers/dmserializers.cpp
new file mode 100644
index 0000000..860bcf9
--- /dev/null
+++ b/dmserializers/dmserializers.cpp
@@ -0,0 +1,163 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Converts from any one DMX file format to another
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmserializers/idmserializers.h"
+#include "appframework/iappsystem.h"
+#include "filesystem.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "tier2/tier2.h"
+
+
+//-----------------------------------------------------------------------------
+// format updater macros
+//-----------------------------------------------------------------------------
+
+#define DECLARE_FORMAT_UPDATER( _name, _description, _extension, _version, _encoding ) \
+ class CDmFormatUpdater_ ## _name : public IDmFormatUpdater \
+ { \
+ public: \
+ CDmFormatUpdater_ ## _name() {} \
+ virtual const char *GetName() const { return #_name; } \
+ virtual const char *GetDescription() const { return _description; } \
+ virtual const char *GetExtension() const { return _extension; } \
+ virtual const char *GetDefaultEncoding() const { return _encoding; } \
+ virtual int GetCurrentVersion() const { return _version; } \
+ virtual bool Update( CDmElement **pRoot, int nSourceVersion ) { return true; } \
+ }; \
+ static CDmFormatUpdater_ ## _name s_FormatUpdater ## _name; \
+ void InstallFormatUpdater_ ## _name( IDataModel *pFactory ) \
+ { \
+ pFactory->AddFormatUpdater( &s_FormatUpdater ## _name ); \
+ }
+
+#define INSTALL_FORMAT_UPDATER( _name ) InstallFormatUpdater_ ## _name( g_pDataModel )
+
+
+//-----------------------------------------------------------------------------
+// format updaters
+//-----------------------------------------------------------------------------
+
+DECLARE_FORMAT_UPDATER( dmx, "Generic DMX", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( movieobjects, "Generic MovieObjects", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( sfm, "Generic SFM", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( sfm_session, "SFM Session", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( sfm_trackgroup, "SFM TrackGroup", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( pcf, "Particle Configuration File", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( preset, "Preset File", "dmx", 1, "keyvalues2" )
+DECLARE_FORMAT_UPDATER( facial_animation, "Facial Animation File", "dmx", 1, "binary" )
+DECLARE_FORMAT_UPDATER( model, "DMX Model", "dmx", 1, "binary" )
+//DECLARE_FORMAT_UPDATER( animation, "DMX Animation", "dmx", 1, "binary" )
+//DECLARE_FORMAT_UPDATER( dcc_makefile, "DMX Makefile", "dmx", 1, "keyvalues2" )
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CDmSerializers : public CBaseAppSystem< IDmSerializers >
+{
+ typedef CBaseAppSystem< IDmSerializers > BaseClass;
+
+public:
+ // Inherited from IAppSystem
+ virtual bool Connect( CreateInterfaceFn factory );
+ virtual void *QueryInterface( const char *pInterfaceName );
+ virtual InitReturnVal_t Init();
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton interface
+//-----------------------------------------------------------------------------
+static CDmSerializers g_DmSerializers;
+IDmSerializers *g_pDmSerializers = &g_DmSerializers;
+
+
+//-----------------------------------------------------------------------------
+// Here's where the app systems get to learn about each other
+//-----------------------------------------------------------------------------
+bool CDmSerializers::Connect( CreateInterfaceFn factory )
+{
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+ if ( !factory( FILESYSTEM_INTERFACE_VERSION, NULL ) )
+ {
+ Warning( "DmSerializers needs the file system to function" );
+ return false;
+ }
+
+ // Here's the main point where all DM element classes get installed
+ // Necessary to do it here so all type symbols for all DME classes are set
+ // up prior to install
+ InstallDmElementFactories( );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Here's where systems can access other interfaces implemented by this object
+//-----------------------------------------------------------------------------
+void *CDmSerializers::QueryInterface( const char *pInterfaceName )
+{
+ if ( !V_strcmp( pInterfaceName, DMSERIALIZERS_INTERFACE_VERSION ) )
+ return (IDmSerializers*)this;
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+InitReturnVal_t CDmSerializers::Init()
+{
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ // Install non-dmx importers
+ InstallActBusyImporter( g_pDataModel );
+ InstallVMTImporter( g_pDataModel );
+ InstallVMFImporter( g_pDataModel );
+
+ // Install legacy dmx importers
+ InstallSFMV1Importer( g_pDataModel );
+ InstallSFMV2Importer( g_pDataModel );
+ InstallSFMV3Importer( g_pDataModel );
+ InstallSFMV4Importer( g_pDataModel );
+ InstallSFMV5Importer( g_pDataModel );
+ InstallSFMV6Importer( g_pDataModel );
+ InstallSFMV7Importer( g_pDataModel );
+ InstallSFMV8Importer( g_pDataModel );
+ InstallSFMV9Importer( g_pDataModel );
+
+ // install dmx format updaters
+ INSTALL_FORMAT_UPDATER( dmx );
+ INSTALL_FORMAT_UPDATER( movieobjects );
+ INSTALL_FORMAT_UPDATER( sfm );
+ INSTALL_FORMAT_UPDATER( sfm_session );
+ INSTALL_FORMAT_UPDATER( sfm_trackgroup );
+ INSTALL_FORMAT_UPDATER( pcf );
+ INSTALL_FORMAT_UPDATER( preset );
+ INSTALL_FORMAT_UPDATER( facial_animation );
+ INSTALL_FORMAT_UPDATER( model );
+// INSTALL_FORMAT_UPDATER( animation );
+// INSTALL_FORMAT_UPDATER( dcc_makefile );
+
+ return INIT_OK;
+}
+
diff --git a/dmserializers/dmserializers.h b/dmserializers/dmserializers.h
new file mode 100644
index 0000000..24e07a2
--- /dev/null
+++ b/dmserializers/dmserializers.h
@@ -0,0 +1,55 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+// Main header file for the serializers DLL
+//
+//=============================================================================
+
+#ifndef DMSERIALIZERS_H
+#define DMSERIALIZERS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "datamodel/dmelement.h"
+#include "datamodel/dmattribute.h"
+#include "datamodel/dmattributevar.h"
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class IDataModel;
+
+
+//-----------------------------------------------------------------------------
+// Externally defined importers
+//-----------------------------------------------------------------------------
+void InstallActBusyImporter( IDataModel *pFactory );
+void InstallVMTImporter( IDataModel *pFactory );
+void InstallSFMV1Importer( IDataModel *pFactory );
+void InstallSFMV2Importer( IDataModel *pFactory );
+void InstallSFMV3Importer( IDataModel *pFactory );
+void InstallSFMV4Importer( IDataModel *pFactory );
+void InstallSFMV5Importer( IDataModel *pFactory );
+void InstallSFMV6Importer( IDataModel *pFactory );
+void InstallSFMV7Importer( IDataModel *pFactory );
+void InstallSFMV8Importer( IDataModel *pFactory );
+void InstallSFMV9Importer( IDataModel *pFactory );
+void InstallVMFImporter( IDataModel *pFactory );
+
+void InstallDMXUpdater( IDataModel *pFactory );
+void InstallSFMSessionUpdater( IDataModel *pFactory );
+void InstallPCFUpdater( IDataModel *pFactory );
+
+
+#endif // DMSERIALIZERS_H
+
+
diff --git a/dmserializers/dmserializers.vpc b/dmserializers/dmserializers.vpc
new file mode 100644
index 0000000..6e7226d
--- /dev/null
+++ b/dmserializers/dmserializers.vpc
@@ -0,0 +1,46 @@
+//-----------------------------------------------------------------------------
+// DMSERIALIZERS.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;DMSERIALIZERS_LIB"
+ }
+}
+
+$Project "Dmserializers"
+{
+ $Folder "Source Files"
+ {
+ $File "dmebaseimporter.cpp"
+ $File "dmserializers.cpp"
+ $File "importactbusy.cpp"
+ $File "importkeyvaluebase.cpp"
+ $File "importsfmv1.cpp"
+ $File "importsfmv2.cpp"
+ $File "importsfmv3.cpp"
+ $File "importsfmv4.cpp"
+ $File "importsfmv5.cpp"
+ $File "importsfmv6.cpp"
+ $File "importsfmv7.cpp"
+ $File "importsfmv8.cpp"
+ $File "importsfmv9.cpp"
+ $File "importvmf.cpp"
+ $File "importvmt.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "dmebaseimporter.h"
+ $File "dmserializers.h"
+ $File "$SRCDIR\public\dmserializers\idmserializers.h"
+ $File "importkeyvaluebase.h"
+ }
+}
diff --git a/dmserializers/importactbusy.cpp b/dmserializers/importactbusy.cpp
new file mode 100644
index 0000000..2ca816e
--- /dev/null
+++ b/dmserializers/importactbusy.cpp
@@ -0,0 +1,182 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "importkeyvaluebase.h"
+#include "dmserializers.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "datamodel/dmattribute.h"
+
+
+//-----------------------------------------------------------------------------
+// Serialization class for Key Values
+//-----------------------------------------------------------------------------
+class CImportActBusy : public CImportKeyValueBase
+{
+public:
+ virtual const char *GetName() const { return "actbusy"; }
+ virtual const char *GetDescription() const { return "ActBusy Script File"; }
+ virtual int GetCurrentVersion() const { return 0; } // doesn't store a version
+
+ bool Serialize( CUtlBuffer &outBuf, CDmElement *pRoot );
+ CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues );
+
+private:
+ // Reads a single element
+ bool UnserializeActBusyKey( CDmAttribute *pChildren, KeyValues *pKeyValues );
+
+ // Writes out the actbusy header
+ void SerializeHeader( CUtlBuffer &buf );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportActBusy s_ImportActBusy;
+
+void InstallActBusyImporter( IDataModel *pFactory )
+{
+ pFactory->AddSerializer( &s_ImportActBusy );
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out the actbusy header
+//-----------------------------------------------------------------------------
+void CImportActBusy::SerializeHeader( CUtlBuffer &buf )
+{
+ buf.Printf( "// \"act busy name\"\t\tThis is the name that a mapmaker must specify in the hint node.\n" );
+ buf.Printf( "// {\n" );
+ buf.Printf( "// \t\"busy_anim\"\t\t\t\"Activity Name\".\n" );
+ buf.Printf( "// \t\"entry_anim\"\t\t\"Activity Name\"\n" );
+ buf.Printf( "// \t\"exit_anim\"\t\t\t\"Activity Name\"\n" );
+ buf.Printf( "// \t\"busy_sequence\"\t\t\"Sequence Name\". If specified, this is used over the activity name. Specify it in the hint node.\n" );
+ buf.Printf( "// \t\"entry_sequence\"\t\"Sequence Name\". If specified, this is used over the entry anim.\n" );
+ buf.Printf( "// \t\"exit_sequence\"\t\t\"Sequence Name\". If specified, this is used over the exit anim.\n" );
+ buf.Printf( "// \t\"min_time\"\t\t\t\"Minimum time to spend in this busy anim\"\n" );
+ buf.Printf( "// \t\"max_time\"\t\t\t\"Maximum time to spend in this busy anim\" 0 = only stop when interrupted by external event\n" );
+ buf.Printf( "// \t\"interrupts\"\t\tOne of:\n" );
+ buf.Printf( "// \t\t\t\t\t\t\"BA_INT_NONE\"\t\tbreak out only when time runs out. No external influence will break me out.\n" );
+ buf.Printf( "// \t\t\t\t\t\t\"BA_INT_DANGER\"\t\tbreak out of this anim only if threatened\n" );
+ buf.Printf( "// \t\t\t\t\t\t\"BA_INT_PLAYER\"\t\tbreak out of this anim if I can see the player, or I'm threatened\n" );
+ buf.Printf( "// \t\t\t\t\t\t\"BA_INT_AMBUSH\"\t\tsomeone please define this - I have no idea what it does\n" );
+ buf.Printf( "// \t\t\t\t\t\t\"BA_INT_COMBAT\"\t\tbreak out of this anim if combat occurs in my line of sight (bullet hits, grenades, etc), -OR- the max time is reached\n" );
+ buf.Printf( "// }\n" );
+ buf.Printf( "//\n" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a new actbusy file
+//-----------------------------------------------------------------------------
+bool CImportActBusy::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ SerializeHeader( buf );
+ buf.Printf( "\"ActBusy.txt\"\n" );
+ buf.Printf( "{\n" );
+
+ CDmAttribute *pChildren = pRoot->GetAttribute( "children" );
+ if ( !pChildren || pChildren->GetType() != AT_ELEMENT_ARRAY )
+ return NULL;
+
+ CDmrElementArray<> children( pChildren );
+ int nCount = children.Count();
+
+ buf.PushTab();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = children[i];
+ buf.Printf( "\"%s\"\n", pChild->GetName() );
+ buf.Printf( "{\n" );
+
+ buf.PushTab();
+ PrintStringAttribute( pChild, buf, "busy_anim", true );
+ PrintStringAttribute( pChild, buf, "entry_anim", true );
+ PrintStringAttribute( pChild, buf, "exit_anim", true );
+ PrintStringAttribute( pChild, buf, "busy_sequence", true );
+ PrintStringAttribute( pChild, buf, "entry_sequence", true );
+ PrintStringAttribute( pChild, buf, "exit_sequence", true );
+ PrintFloatAttribute( pChild, buf, "min_time" );
+ PrintFloatAttribute( pChild, buf, "max_time" );
+ PrintStringAttribute( pChild, buf, "interrupts" );
+ buf.PopTab();
+
+ buf.Printf( "}\n" );
+ }
+ buf.PopTab();
+
+ buf.Printf( "}\n" );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a single element
+//-----------------------------------------------------------------------------
+bool CImportActBusy::UnserializeActBusyKey( CDmAttribute *pChildren, KeyValues *pKeyValues )
+{
+ CDmElement *pActBusy = CreateDmElement( "DmElement", pKeyValues->GetName(), NULL );
+ if ( !pActBusy )
+ return false;
+
+ // Each act busy needs to have an editortype associated with it so it displays nicely in editors
+ pActBusy->SetValue( "editorType", "actBusy" );
+
+ float flZero = 0.0f;
+ AddStringAttribute( pActBusy, pKeyValues, "busy_anim", "" );
+ AddStringAttribute( pActBusy, pKeyValues, "entry_anim", "" );
+ AddStringAttribute( pActBusy, pKeyValues, "exit_anim", "" );
+ AddStringAttribute( pActBusy, pKeyValues, "busy_sequence", "" );
+ AddStringAttribute( pActBusy, pKeyValues, "entry_sequence", "" );
+ AddStringAttribute( pActBusy, pKeyValues, "exit_sequence", "" );
+ AddFloatAttribute( pActBusy, pKeyValues, "min_time", &flZero );
+ AddFloatAttribute( pActBusy, pKeyValues, "max_time", &flZero );
+ AddStringAttribute( pActBusy, pKeyValues, "interrupts", "BA_INT_NONE" );
+
+ CDmrElementArray<> children( pChildren );
+ children.AddToTail( pActBusy );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+CDmElement* CImportActBusy::UnserializeFromKeyValues( KeyValues *pKeyValues )
+{
+ // Create the main element
+ CDmElement *pElement = CreateDmElement( "DmElement", "ActBusyList", NULL );
+ if ( !pElement )
+ return NULL;
+
+ // Each act busy list needs to have an editortype associated with it so it displays nicely in editors
+ pElement->SetValue( "editorType", "actBusyList" );
+
+ // All actbusy keys are elements of a single element array attribute 'children'
+ CDmAttribute *pChildren = pElement->AddAttribute( "children", AT_ELEMENT_ARRAY );
+ if ( !pChildren )
+ return NULL;
+
+ // Under the root are all the actbusy keys
+ for ( KeyValues *pActBusyKey = pKeyValues->GetFirstTrueSubKey(); pActBusyKey != NULL; pActBusyKey = pActBusyKey->GetNextTrueSubKey() )
+ {
+ if ( !UnserializeActBusyKey( pChildren, pActBusyKey ) )
+ {
+ Warning( "Error importing actbusy element %s\n", pActBusyKey->GetName() );
+ return NULL;
+ }
+ }
+
+ // Resolve all element references recursively
+ RecursivelyResolveElement( pElement );
+
+ return pElement;
+}
diff --git a/dmserializers/importkeyvaluebase.cpp b/dmserializers/importkeyvaluebase.cpp
new file mode 100644
index 0000000..88d2308
--- /dev/null
+++ b/dmserializers/importkeyvaluebase.cpp
@@ -0,0 +1,292 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "importkeyvaluebase.h"
+#include "dmserializers.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Default serialization method
+//-----------------------------------------------------------------------------
+bool CImportKeyValueBase::Serialize( CUtlBuffer &outBuf, CDmElement *pRoot )
+{
+ Warning( "Serialization not supported for importing from keyvalues files\n");
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a new element
+//-----------------------------------------------------------------------------
+CDmElement* CImportKeyValueBase::CreateDmElement( const char *pElementType, const char *pElementName, DmObjectId_t *pId )
+{
+ // See if we can create an element of that type
+ DmElementHandle_t hElement = g_pDataModel->CreateElement( pElementType, pElementName, DMFILEID_INVALID, pId );
+ if ( hElement == DMELEMENT_HANDLE_INVALID )
+ {
+ Warning("%s: Element uses unknown element type %s\n", m_pFileName, pElementType );
+ return NULL;
+ }
+
+ return g_pDataModel->GetElement( hElement );
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to output typed attributes to keyvalues
+//-----------------------------------------------------------------------------
+void CImportKeyValueBase::PrintBoolAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName )
+{
+ if ( pElement->HasAttribute( pKeyName ) )
+ {
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ if ( pAttribute->GetType() == AT_BOOL )
+ {
+ outBuf.Printf("\"%s\" \"%d\"\n", pKeyName, pAttribute->GetValue<bool>( ) );
+ }
+ }
+}
+
+void CImportKeyValueBase::PrintIntAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName )
+{
+ if ( pElement->HasAttribute( pKeyName ) )
+ {
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ if ( pAttribute->GetType() == AT_INT )
+ {
+ outBuf.Printf("\"%s\" \"%d\"\n", pKeyName, pAttribute->GetValue<int>( ) );
+ }
+ }
+}
+
+void CImportKeyValueBase::PrintFloatAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName )
+{
+ if ( pElement->HasAttribute( pKeyName ) )
+ {
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ if ( pAttribute->GetType() == AT_FLOAT )
+ {
+ outBuf.Printf("\"%s\" \"%.10f\"\n", pKeyName, pAttribute->GetValue<float>( ) );
+ }
+ }
+}
+
+void CImportKeyValueBase::PrintStringAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName, bool bSkipEmptryStrings, bool bPrintValueOnly )
+{
+ if ( pElement->HasAttribute( pKeyName ) )
+ {
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ if ( pAttribute->GetType() == AT_STRING )
+ {
+ const char *pValue = pAttribute->GetValueString();
+ if ( !bSkipEmptryStrings || pValue[0] )
+ {
+ if ( !bPrintValueOnly )
+ {
+ outBuf.Printf("\"%s\" \"%s\"\n", pKeyName, pValue );
+ }
+ else
+ {
+ outBuf.Printf("\"%s\"\n", pValue );
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to add typed attributes from keyvalues
+//-----------------------------------------------------------------------------
+bool CImportKeyValueBase::AddBoolAttribute( CDmElement* pElement, KeyValues *pKeyValues, const char *pKeyName, bool *pDefault )
+{
+ KeyValues *pKey = pKeyValues->FindKey( pKeyName );
+ bool bValue;
+ if ( pKey )
+ {
+ bValue = pKey->GetInt() != 0;
+ }
+ else
+ {
+ if ( !pDefault )
+ return true;
+ bValue = *pDefault;
+ }
+
+ return pElement->SetValue( pKeyName, bValue ) != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to add typed attributes from keyvalues
+//-----------------------------------------------------------------------------
+bool CImportKeyValueBase::AddIntAttribute( CDmElement* pElement, KeyValues *pKeyValues, const char *pKeyName, int *pDefault )
+{
+ KeyValues *pKey = pKeyValues->FindKey( pKeyName );
+ int nValue;
+ if ( pKey )
+ {
+ nValue = pKey->GetInt();
+ }
+ else
+ {
+ if ( !pDefault )
+ return true;
+ nValue = *pDefault;
+ }
+
+ return pElement->SetValue( pKeyName, nValue ) != NULL;
+}
+
+bool CImportKeyValueBase::AddFloatAttribute( CDmElement* pElement, KeyValues *pKeyValues, const char *pKeyName, float *pDefault )
+{
+ KeyValues *pKey = pKeyValues->FindKey( pKeyName );
+ float flValue;
+ if ( pKey )
+ {
+ flValue = pKey->GetFloat();
+ }
+ else
+ {
+ if ( !pDefault )
+ return true;
+ flValue = *pDefault;
+ }
+
+ return pElement->SetValue( pKeyName, flValue ) != NULL;
+}
+
+bool CImportKeyValueBase::AddStringAttribute( CDmElement* pElement, KeyValues *pKeyValues, const char *pKeyName, const char *pDefault )
+{
+ KeyValues *pKey = pKeyValues->FindKey( pKeyName );
+ const char *pValue = "";
+ if ( pKey )
+ {
+ pValue = pKey->GetString();
+ }
+ else
+ {
+ if ( !pDefault )
+ return true;
+ pValue = pDefault;
+ }
+
+ return pElement->SetValue( pKeyName, pValue ) != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to add typed attributes from keyvalues
+//-----------------------------------------------------------------------------
+bool CImportKeyValueBase::AddBoolAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, bool *pDefault )
+{
+ if ( !AddBoolAttribute( pElement, pKeyValue, pKeyName, pDefault ) )
+ return false;
+
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ pAttribute->AddFlag( nFlags );
+ return true;
+}
+
+bool CImportKeyValueBase::AddIntAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, int *pDefault )
+{
+ if ( !AddIntAttribute( pElement, pKeyValue, pKeyName, pDefault ) )
+ return false;
+
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ pAttribute->AddFlag( nFlags );
+ return true;
+}
+
+bool CImportKeyValueBase::AddFloatAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, float *pDefault )
+{
+ if ( !AddFloatAttribute( pElement, pKeyValue, pKeyName, pDefault ) )
+ return false;
+
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ pAttribute->AddFlag( nFlags );
+ return true;
+}
+
+bool CImportKeyValueBase::AddStringAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, const char *pDefault )
+{
+ if ( !AddStringAttribute( pElement, pKeyValue, pKeyName, pDefault ) )
+ return false;
+
+ CDmAttribute *pAttribute = pElement->GetAttribute( pKeyName );
+ pAttribute->AddFlag( nFlags );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively resolves all attributes pointing to elements
+//-----------------------------------------------------------------------------
+void CImportKeyValueBase::RecursivelyResolveElement( CDmElement* pElement )
+{
+ if ( !pElement )
+ return;
+
+ pElement->Resolve();
+
+ CDmAttribute *pAttribute = pElement->FirstAttribute();
+ while ( pAttribute )
+ {
+ switch ( pAttribute->GetType() )
+ {
+ case AT_ELEMENT:
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>();
+ RecursivelyResolveElement( pElementAt );
+ }
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pElementAt = array[ i ];
+ RecursivelyResolveElement( pElementAt );
+ }
+ }
+ break;
+ }
+
+ pAttribute = pAttribute->NextAttribute( );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+bool CImportKeyValueBase::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
+ const char *pSourceFormatName, int nSourceFormatVersion,
+ DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
+{
+ *ppRoot = NULL;
+ m_pFileName = g_pDataModel->GetFileName( fileid );
+
+ KeyValues *kv = new KeyValues( "dmx file" );
+ if ( !kv )
+ return false;
+
+ bool bOk = kv->LoadFromBuffer( "dmx file", buf );
+ if ( bOk )
+ {
+ *ppRoot = UnserializeFromKeyValues( kv );
+ }
+
+ kv->deleteThis();
+ return bOk;
+}
diff --git a/dmserializers/importkeyvaluebase.h b/dmserializers/importkeyvaluebase.h
new file mode 100644
index 0000000..771d048
--- /dev/null
+++ b/dmserializers/importkeyvaluebase.h
@@ -0,0 +1,84 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef IMPORTKEYVALUEBASE_H
+#define IMPORTKEYVALUEBASE_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "datamodel/idatamodel.h"
+
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class CUtlBuffer;
+class KeyValues;
+class CDmElement;
+
+
+//-----------------------------------------------------------------------------
+// Serialization class for Key Values
+//-----------------------------------------------------------------------------
+abstract_class CImportKeyValueBase : public IDmSerializer
+{
+public:
+ // Inherited from IDMSerializer
+ virtual bool StoresVersionInFile() const { return false; }
+ virtual bool IsBinaryFormat() const { return false; }
+ virtual bool Serialize( CUtlBuffer &buf, CDmElement *pRoot );
+ virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
+ const char *pSourceFormatName, int nSourceFormatVersion,
+ DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot );
+
+protected:
+ // Main entry point for derived classes to implement unserialization
+ virtual CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues ) = 0;
+
+ // Returns the file name associated with the unserialization
+ const char *FileName() const;
+
+ // Creates new elements
+ CDmElement* CreateDmElement( const char *pElementType, const char *pElementName, DmObjectId_t *pId );
+
+ // Recursively resolves all attributes pointing to elements
+ void RecursivelyResolveElement( CDmElement* pElement );
+
+ // Used to add typed attributes from keyvalues
+ bool AddBoolAttribute( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, bool *pDefault = NULL );
+ bool AddIntAttribute( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int *pDefault = NULL );
+ bool AddFloatAttribute( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, float *pDefault = NULL );
+ bool AddStringAttribute( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, const char *pDefault = NULL );
+
+ // Used to add typed attributes from keyvalues
+ bool AddBoolAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, bool *pDefault = NULL );
+ bool AddIntAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, int *pDefault = NULL );
+ bool AddFloatAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, float *pDefault = NULL );
+ bool AddStringAttributeFlags( CDmElement* pElement, KeyValues *pKeyValue, const char *pKeyName, int nFlags, const char *pDefault = NULL );
+
+ // Used to output typed attributes to keyvalues
+ void PrintBoolAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName );
+ void PrintIntAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName );
+ void PrintFloatAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName );
+ void PrintStringAttribute( CDmElement* pElement, CUtlBuffer &outBuf, const char *pKeyName, bool bSkipEmptryStrings = false, bool bPrintValueOnly = false );
+
+private:
+ const char *m_pFileName;
+};
+
+
+//-----------------------------------------------------------------------------
+// Returns the file name associated with the unserialization
+//-----------------------------------------------------------------------------
+inline const char *CImportKeyValueBase::FileName() const
+{
+ return m_pFileName;
+}
+
+
+#endif // IMPORTKEYVALUEBASE_H
diff --git a/dmserializers/importsfmv1.cpp b/dmserializers/importsfmv1.cpp
new file mode 100644
index 0000000..1da3f5e
--- /dev/null
+++ b/dmserializers/importsfmv1.cpp
@@ -0,0 +1,219 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmattribute.h"
+#include "datamodel/dmelement.h"
+
+#include <math.h>
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV1 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV1( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+ // Fixes up a single time attribute - converting from float seconds to int tenths-of-a-millisecond
+ void ConvertTimeAttribute( CDmElement *pElementInternal, const char *pOldName, const char *pNewName );
+
+ // Fixes up a single timeframe
+ void FixupTimeframe( CDmElement *pElementInternal );
+
+ // Fixes up a single log - converting from int milliseconds to int tenths-of-a-millisecond
+ void FixupLog( CDmElement *pElementInternal );
+
+ CUtlRBTree< CDmElement*, int > m_fixedElements;
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV1 s_ImportDmxV1( "sfm_v1", "sfm_v2" );
+
+void InstallSFMV1Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportDmxV1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV1::CImportSFMV1( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+ m_fixedElements.SetLessFunc( DefLessFunc( CDmElement * ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+bool CImportSFMV1::DoFixup( CDmElement *pElementInternal )
+{
+ if ( !pElementInternal )
+ return true;
+
+ if ( m_fixedElements.Find( pElementInternal ) != m_fixedElements.InvalidIndex() )
+ return true;
+
+ m_fixedElements.Insert( pElementInternal );
+
+ const char *pType = pElementInternal->GetTypeString();
+ if ( !Q_strcmp( pType, "DmeTimeFrame" ) )
+ {
+ FixupTimeframe( pElementInternal );
+ }
+ else if ( !Q_strcmp( pType, "DmeLog" ) ||
+ !Q_strcmp( pType, "DmeIntLog" ) ||
+ !Q_strcmp( pType, "DmeFloatLog" ) ||
+ !Q_strcmp( pType, "DmeBoolLog" ) ||
+ !Q_strcmp( pType, "DmeColorLog" ) ||
+ !Q_strcmp( pType, "DmeVector2Log" ) ||
+ !Q_strcmp( pType, "DmeVector3Log" ) ||
+ !Q_strcmp( pType, "DmeVector4Log" ) ||
+ !Q_strcmp( pType, "DmeQAngleLog" ) ||
+ !Q_strcmp( pType, "DmeQuaternionLog" ) ||
+ !Q_strcmp( pType, "DmeVMatrixLog" ) )
+ {
+ FixupLog( pElementInternal );
+ }
+
+
+ for ( CDmAttribute *pAttribute = pElementInternal->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElement = pAttribute->GetValueElement<CDmElement>( );
+ DoFixup( pElement );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ DoFixup( pChild );
+ }
+ continue;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Fixes up a single time attribute - converting from float seconds to int tenths-of-a-millisecond
+//-----------------------------------------------------------------------------
+void CImportSFMV1::ConvertTimeAttribute( CDmElement *pElementInternal, const char *pOldName, const char *pNewName )
+{
+ float time = 0.0f;
+ CDmAttribute *pOldAttr = pElementInternal->GetAttribute( pOldName );
+ if ( !pOldAttr )
+ {
+ Warning( "*** Problem in file encountered!\n" );
+ Warning( "*** TimeFrame \"%s\" is missing attribute \"%s\"!\n", pElementInternal->GetName(), pOldName );
+ Warning( "*** Setting new attribute \"%s\" to 0\n", pNewName );
+ }
+ else if ( pOldAttr->GetType() != AT_FLOAT )
+ {
+ Warning( "*** Problem in file encountered!\n" );
+ Warning( "*** TimeFrame \"%s\" has attribute \"%s\" with an unexpected type (expected float)!\n", pElementInternal->GetName(), pOldName );
+ }
+ else
+ {
+ time = pOldAttr->GetValue< float >();
+ pElementInternal->RemoveAttribute( pOldName );
+ }
+
+ CDmAttribute *pNewAttr = NULL;
+
+ // this is disabled because even dmxconvert installs *some* movieobjects factories, when it probably shouldn't
+ // the method of installing movieobjects factories will change at some point in the future, and we can turn on this safety check then
+#if 0
+ int i = g_pDataModel->GetFirstFactory();
+ if ( g_pDataModel->IsValidFactory( i ) )
+ {
+ // factories installed - most likely from within movieobjects.lib
+ // ie there may be different ways of allocating attributes, so it's not safe to add them here
+ pNewAttr = pElementInternal->GetAttribute( pNewName );
+ if ( !pNewAttr || pNewAttr->GetType() != AT_INT )
+ {
+ Assert( 0 );
+ Warning( "*** Converter error - expected element \"%s\" to contain int attribute \"%s\"!\n", pElementInternal->GetName(), pNewName );
+ Warning( "*** - if you get this error, the converter is out of sync with the element library!\n" );
+ return;
+ }
+ }
+ else
+ {
+#endif
+ // no factories installed - most likely from within dmxconvert.exe
+ // ie we're just working with CDmElement subclasses, so it's safe to add attributes
+ pNewAttr = pElementInternal->AddAttribute( pNewName, AT_INT );
+ if ( !pNewAttr )
+ {
+ Assert( 0 );
+ Warning( "*** Converter error - element \"%s\" already has a non-int attribute \"%s\"!\n", pElementInternal->GetName(), pNewName );
+ return;
+ }
+#if 0
+ }
+#endif
+
+ pNewAttr->SetValue< int >( floor( time * 10000 + 0.5f ) );
+}
+
+//-----------------------------------------------------------------------------
+// Fixes up a single timeframe
+//-----------------------------------------------------------------------------
+void CImportSFMV1::FixupTimeframe( CDmElement *pElementInternal )
+{
+ ConvertTimeAttribute( pElementInternal, "start", "startTime" );
+ ConvertTimeAttribute( pElementInternal, "duration", "durationTime" );
+ ConvertTimeAttribute( pElementInternal, "offset", "offsetTime" );
+}
+
+//-----------------------------------------------------------------------------
+// Fixes up a single log - converting from int milliseconds to int tenths-of-a-millisecond
+//-----------------------------------------------------------------------------
+void CImportSFMV1::FixupLog( CDmElement *pElementInternal )
+{
+ CDmAttribute *pAttr = pElementInternal->GetAttribute( "times" );
+ if ( !pAttr )
+ {
+ Warning( "*** Problem in file encountered!\n" );
+ Warning( "*** Log \"%s\" is missing attribute \"%s\"!\n", pElementInternal->GetName(), "times" );
+ return;
+ }
+
+ if ( pAttr->GetType() != AT_INT_ARRAY )
+ {
+ Warning( "*** Problem in file encountered!\n" );
+ Warning( "*** Log \"%s\" has attribute \"%s\" with an unexpected type (expected int array)!\n", pElementInternal->GetName(), "times" );
+ return;
+ }
+
+ CDmrArray<int> array( pAttr );
+ int c = array.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ // convert all log times from int milliseconds to int tenths-of-a-millisecond
+ array.Set( i, 10 * array[i] );
+ }
+}
diff --git a/dmserializers/importsfmv2.cpp b/dmserializers/importsfmv2.cpp
new file mode 100644
index 0000000..829ae0c
--- /dev/null
+++ b/dmserializers/importsfmv2.cpp
@@ -0,0 +1,294 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV2 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV2( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV2 s_ImportSFMV2( "sfm_v2", "sfm_v3" );
+
+void InstallSFMV2Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV2::CImportSFMV2( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+
+struct LayerType_t
+{
+ char const *loglayertype;
+ int datatype;
+ char const *logtype;
+};
+
+static LayerType_t g_LayerTypes[] =
+{
+ { "DmeIntLogLayer", AT_INT_ARRAY, "DmeIntLog" },
+ { "DmeFloatLogLayer", AT_FLOAT_ARRAY, "DmeFloatLog" },
+ { "DmeBoolLogLayer", AT_BOOL_ARRAY, "DmeBoolLog" },
+ // AT_STRING_ARRAY,
+ // AT_VOID_ARRAY,
+ // AT_OBJECTID_ARRAY,
+ { "DmeColorLogLayer", AT_COLOR_ARRAY, "DmeColorLog" },
+ { "DmeVector2LogLayer", AT_VECTOR2_ARRAY, "DmeVector2Log" },
+ { "DmeVector3LogLayer", AT_VECTOR3_ARRAY, "DmeVector3Log" },
+ { "DmeVector4LogLayer", AT_VECTOR4_ARRAY, "DmeVector4Log" },
+ { "DmeQAngleLogLayer", AT_QANGLE_ARRAY, "DmeQAngleLog" },
+ { "DmeQuaternionLogLayer", AT_QUATERNION_ARRAY, "DmeQuaternionLog" },
+ { "DmeVMatrixLogLayer", AT_VMATRIX_ARRAY, "DmeVMatrixLog" },
+ // AT_ELEMENT_ARRAY
+ // NO ARRAY TYPES EITHER!!!
+};
+
+int GetLogType( char const *type )
+{
+ int c = ARRAYSIZE( g_LayerTypes );
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( !Q_stricmp( type, g_LayerTypes[ i ].logtype ) )
+ return g_LayerTypes[ i ].datatype;
+ }
+ return AT_UNKNOWN;
+}
+
+char const *GetLogLayerType( int nDataType )
+{
+ int c = ARRAYSIZE( g_LayerTypes );
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( nDataType == g_LayerTypes[ i ].datatype )
+ return g_LayerTypes[ i ].loglayertype;
+ }
+ return NULL;
+}
+
+char const *GetLogLayerType( char const *logType )
+{
+ int c = ARRAYSIZE( g_LayerTypes );
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( !Q_stricmp( logType, g_LayerTypes[ i ].logtype ) )
+ return g_LayerTypes[ i ].loglayertype;
+ }
+ return NULL;
+}
+
+template< class T >
+void CopyValues( int layerType, CDmElement *pElement, CDmElement *pLayer, CDmAttribute *pInTimeAttribute, CDmAttribute *pInCurveTypeAttribute )
+{
+ CDmAttribute *pInValueAttribute = pElement->GetAttribute( "values" );
+ if ( !pInValueAttribute )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ CDmrArray<T> outValues( pLayer->AddAttribute( "values", (DmAttributeType_t)layerType ) );
+ CDmrArray<int> outTimes( pLayer->AddAttribute( "times", AT_INT_ARRAY ) );
+ CDmrArray<int> outCurveTypes;
+ if ( pInCurveTypeAttribute )
+ {
+ outCurveTypes.Init( pLayer->AddAttribute( "curvetypes", AT_INT_ARRAY ) );
+ }
+
+ CDmrArray<T> inValues( pInValueAttribute );
+ CDmrArray<int> inTimes( pInTimeAttribute );
+ CDmrArray<int> inCurveTypes( pInCurveTypeAttribute );
+
+ Assert( inValues.Count() == inTimes.Count() );
+ int c = inValues.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ outTimes.AddToTail( inTimes[ i ] );
+ outValues.AddToTail( inValues[ i ] );
+ if ( outCurveTypes.IsValid() )
+ {
+ outCurveTypes.AddToTail( inCurveTypes[ i ] );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV2::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ // Perform the fixup
+ const char *pType = pElement->GetTypeString();
+ int layerType = GetLogType( pType );
+ if ( layerType != AT_UNKNOWN )
+ {
+ /*
+ char buf[ 128 ];
+ g_pDataModel->ToString( pElement->GetId(), buf, sizeof( buf ) );
+
+ Msg( "Processing %s %s id %s\n",
+ pElement->GetTypeString(), pElement->GetName(), buf );
+ */
+
+ // Find attribute arrays for times, values and curvetypes
+ CDmAttribute *pTimes = pElement->GetAttribute( "times" );
+ CDmAttribute *pCurveTypes = NULL;
+
+ // FIX
+ CDmAttribute *pAttr = pElement->AddAttribute( "usecurvetypes", AT_BOOL );
+ if ( pAttr->GetValue<bool>() )
+ {
+ pCurveTypes = pElement->GetAttribute( "curvetypes" );
+ }
+
+ // Get the default layer (added when the new style log is created)
+ CDmrElementArray<> layers( pElement->AddAttribute( "layers", AT_ELEMENT_ARRAY ) );
+ CDmElement *layer = NULL;
+ if ( layers.Count() == 0 )
+ {
+ DmElementHandle_t hElement = g_pDataModel->CreateElement( GetLogLayerType( layerType ), GetLogLayerType( layerType ), pElement->GetFileId() );
+ layer = g_pDataModel->GetElement( hElement );
+ layers.AddToTail( layer );
+ }
+ else
+ {
+ Assert( layers.Count() == 1 );
+ layer = layers[ 0 ];
+ }
+
+ // Copy data
+ switch ( layerType )
+ {
+ default:
+ case AT_UNKNOWN:
+ break;
+ case AT_FLOAT_ARRAY:
+ CopyValues< float >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_INT_ARRAY:
+ CopyValues< int >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_BOOL_ARRAY:
+ CopyValues< bool >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_COLOR_ARRAY:
+ CopyValues< Color >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_VECTOR2_ARRAY:
+ CopyValues< Vector2D >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_VECTOR3_ARRAY:
+ CopyValues< Vector >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_VECTOR4_ARRAY:
+ CopyValues< Vector4D >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_QANGLE_ARRAY:
+ CopyValues< QAngle >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_QUATERNION_ARRAY:
+ CopyValues< Quaternion >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ case AT_VMATRIX_ARRAY:
+ CopyValues< VMatrix >( layerType, pElement, layer, pTimes, pCurveTypes );
+ break;
+ }
+
+ // Set the back pointer
+ CDmAttribute *ownerLog = layer->AddAttribute( "ownerlog", AT_ELEMENT );
+ Assert( ownerLog );
+ ownerLog->SetValue( pElement->GetHandle() );
+
+ // Delete the base attributes
+ pElement->RemoveAttribute( "times" );
+ pElement->RemoveAttribute( "values" );
+ pElement->RemoveAttribute( "curvetypes" );
+ }
+}
+
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV2::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descene to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV2::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv3.cpp b/dmserializers/importsfmv3.cpp
new file mode 100644
index 0000000..d421eb3
--- /dev/null
+++ b/dmserializers/importsfmv3.cpp
@@ -0,0 +1,227 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV3 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV3( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV3 s_ImportSFMV3( "sfm_v3", "sfm_v4" );
+
+void InstallSFMV3Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV3 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV3::CImportSFMV3( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+
+struct LogToCurveInfoTypeMap_t
+{
+ const char *pLogType;
+ const char *pLogLayerType;
+ const char *pCurveInfoType;
+};
+
+LogToCurveInfoTypeMap_t g_typeMap[] =
+{
+ { "DmeIntLog", "DmeIntLogLayer", "DmeIntCurveInfo" },
+ { "DmeFloatLog", "DmeFloatLogLayer", "DmeFloatCurveInfo" },
+ { "DmeBoolLog", "DmeBoolLogLayer", "DmeBoolCurveInfo" },
+ // string,
+ // void,
+ // objectid,
+ { "DmeColorLog", "DmeColorLogLayer", "DmeColorCurveInfo" },
+ { "DmeVector2Log", "DmeVector2LogLayer", "DmeVector2CurveInfo" },
+ { "DmeVector3Log", "DmeVector3LogLayer", "DmeVector3CurveInfo" },
+ { "DmeVector4Log", "DmeVector4LogLayer", "DmeVector4CurveInfo" },
+ { "DmeQAngleLog", "DmeQAngleLogLayer", "DmeQAngleCurveInfo" },
+ { "DmeQuaternionLog", "DmeQuaternionLogLayer","DmeQuaternionCurveInfo" },
+ { "DmeVMatrixLog", "DmeVMatrixLogLayer", "DmeVMatrixCurveInfo" },
+};
+
+const char *GetCurveInfoTypeFromLogType( const char *pLogType )
+{
+ int c = ARRAYSIZE( g_typeMap );
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( !Q_stricmp( pLogType, g_typeMap[ i ].pLogType ) )
+ return g_typeMap[ i ].pCurveInfoType;
+ }
+ return NULL;
+}
+
+bool IsLogLayerType( const char *pLogLayerType )
+{
+ int c = ARRAYSIZE( g_typeMap );
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( !Q_stricmp( pLogLayerType, g_typeMap[ i ].pLogLayerType ) )
+ return true;
+ }
+ return false;
+}
+
+void MoveAttribute( CDmElement *pFromElement, const char *pFromAttrName, CDmElement *pToElement = NULL, const char *pToAttrName = NULL, DmAttributeType_t toType = AT_UNKNOWN )
+{
+ if ( !pToAttrName )
+ {
+ pToAttrName = pFromAttrName;
+ }
+
+ if ( pToElement )
+ {
+ CDmAttribute *pFromAttr = pFromElement->GetAttribute( pFromAttrName );
+ const void *pValue = pFromAttr->GetValueUntyped();
+ DmAttributeType_t fromType = pFromAttr->GetType();
+ if ( toType == AT_UNKNOWN )
+ {
+ toType = fromType;
+ }
+
+ CDmAttribute *pToAttr = pToElement->AddAttribute( pToAttrName, toType );
+ if ( !pToAttr )
+ {
+ Warning( "*** Problem in converter encountered!\n" );
+ Warning( "*** Unable to find or add attribute \"%s\" to element \"%s\"!\n", pToAttrName, pToElement->GetName() );
+ }
+ else if ( fromType != toType )
+ {
+ Warning( "*** Problem in file encountered!\n" );
+ Warning( "*** Element \"%s\" has attribute \"%s\" with an unexpected type!\n", pFromElement->GetName(), pFromAttrName );
+ }
+ else
+ {
+ pToAttr->SetValue( toType, pValue );
+ }
+ }
+
+ pFromElement->RemoveAttribute( pFromAttrName );
+}
+
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV3::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+
+ // log layer
+ if ( IsLogLayerType( pType ) )
+ {
+ pElement->RemoveAttribute( "ownerlog" );
+ return;
+ }
+
+ // log
+ const char *pCurveInfoType = GetCurveInfoTypeFromLogType( pType );
+ if ( !pCurveInfoType )
+ return;
+
+ CDmElement *pCurveInfo = NULL;
+ CDmAttribute *pUseCurveTypeAttr = pElement->GetAttribute( "usecurvetypes" );
+ if ( pUseCurveTypeAttr && pUseCurveTypeAttr->GetValue<bool>() )
+ {
+ DmElementHandle_t hElement = g_pDataModel->CreateElement( "curve info", pCurveInfoType, pElement->GetFileId() );
+ pCurveInfo = g_pDataModel->GetElement( hElement );
+ }
+ pElement->RemoveAttribute( "usecurvetypes" );
+
+ MoveAttribute( pElement, "defaultcurvetype", pCurveInfo, "defaultCurveType", AT_INT );
+ MoveAttribute( pElement, "defaultedgezerovalue",pCurveInfo, "defaultEdgeZeroValue" );
+ MoveAttribute( pElement, "useedgeinfo", pCurveInfo, "useEdgeInfo", AT_BOOL );
+ MoveAttribute( pElement, "rightedgetime", pCurveInfo, "rightEdgeTime", AT_INT );
+ MoveAttribute( pElement, "left_edge_active", pCurveInfo, "leftEdgeActive", AT_BOOL );
+ MoveAttribute( pElement, "right_edge_active", pCurveInfo, "rightEdgeActive", AT_BOOL );
+ MoveAttribute( pElement, "left_edge_curvetype", pCurveInfo, "leftEdgeCurveType", AT_INT );
+ MoveAttribute( pElement, "right_edge_curvetype",pCurveInfo, "rightEdgeCurveType", AT_INT );
+ MoveAttribute( pElement, "left_edge_value", pCurveInfo, "leftEdgeValue" );
+ MoveAttribute( pElement, "right_edge_value", pCurveInfo, "rightEdgeValue" );
+}
+
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV3::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV3::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv4.cpp b/dmserializers/importsfmv4.cpp
new file mode 100644
index 0000000..278fe12
--- /dev/null
+++ b/dmserializers/importsfmv4.cpp
@@ -0,0 +1,123 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV4 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV4( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV4 s_ImportSFMV4( "sfm_v4", "sfm_v5" );
+
+void InstallSFMV4Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV4 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV4::CImportSFMV4( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV4::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+
+ if ( !V_stricmp( pType, "DmeCamera" ) )
+ {
+ CDmAttribute *pOldToneMapScaleAttr = pElement->GetAttribute( "toneMapScale" );
+ float fNewBloomScale = pOldToneMapScaleAttr->GetValue<float>( );
+
+ Assert( !pElement->HasAttribute("bloomScale") );
+
+ CDmAttribute *pNewBloomScaleAttr = pElement->AddAttribute( "bloomScale", AT_FLOAT );
+ pNewBloomScaleAttr->SetValue( fNewBloomScale );
+ pOldToneMapScaleAttr->SetValue( 1.0f );
+ }
+}
+
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV4::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV4::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv5.cpp b/dmserializers/importsfmv5.cpp
new file mode 100644
index 0000000..3c3d6db
--- /dev/null
+++ b/dmserializers/importsfmv5.cpp
@@ -0,0 +1,120 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV5 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV5( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV5 s_ImportSFMV5( "sfm_v5", "sfm_v6" );
+
+void InstallSFMV5Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV5 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV5::CImportSFMV5( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV5::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+
+ if ( !V_stricmp( pType, "DmeSpotLight" ) )
+ {
+ pElement->SetType( "DmeProjectedLight" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV5::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV5::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv6.cpp b/dmserializers/importsfmv6.cpp
new file mode 100644
index 0000000..bcceaa1
--- /dev/null
+++ b/dmserializers/importsfmv6.cpp
@@ -0,0 +1,140 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV6 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV6( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+ Quaternion DirectionToOrientation( const Vector &dir );
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV6 s_ImportSFMV6( "sfm_v6", "sfm_v7" );
+
+void InstallSFMV6Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV6 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV6::CImportSFMV6( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+Quaternion CImportSFMV6::DirectionToOrientation( const Vector &dir )
+{
+ Vector up( 0, 0, 1 );
+ Vector right = CrossProduct( dir, up );
+ if ( right.IsLengthLessThan( 0.001f ) )
+ {
+ up.Init( 1, 0, 0 );
+ right = CrossProduct( dir, up );
+ }
+ right.NormalizeInPlace();
+ up = CrossProduct( right, dir );
+
+ Quaternion q;
+ BasisToQuaternion( dir, right, up, q );
+ return q;
+}
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV6::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+
+ if ( !V_stricmp( pType, "DmeProjectedLight" ) )
+ {
+ Vector vDir = pElement->GetValue<Vector>( "direction" );
+ pElement->RemoveAttribute( "direction" );
+ Quaternion q = DirectionToOrientation( vDir );
+ pElement->SetValue<Quaternion>( "orientation", q );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV6::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV6::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv7.cpp b/dmserializers/importsfmv7.cpp
new file mode 100644
index 0000000..a66bf18
--- /dev/null
+++ b/dmserializers/importsfmv7.cpp
@@ -0,0 +1,144 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV7 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV7( char const *formatName, char const *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV7 s_ImportSFMV7( "sfm_v7", "sfm_v8" );
+
+void InstallSFMV7Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV7 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV7::CImportSFMV7( char const *formatName, char const *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV7::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+
+ if ( !V_stricmp( pType, "DmeAnimationSet" ) )
+ {
+ // Add a level of indirection in animation sets
+ // Modify the type of all controls from DmElement to DmeAnimationSetControl
+ CDmrElementArray<> srcPresets( pElement, "presets" );
+ if ( srcPresets.IsValid() )
+ {
+ CDmrElementArray<> presetGroupArray( pElement, "presetGroups", true );
+ CDmElement *pPresetGroup = CreateElement< CDmElement >( "custom", pElement->GetFileId() );
+ pPresetGroup->SetType( "DmePresetGroup" );
+ CDmrElementArray<> presets( pPresetGroup, "presets", true );
+ presetGroupArray.AddToTail( pPresetGroup );
+
+ int nCount = srcPresets.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pPreset = srcPresets[i];
+ if ( pPreset )
+ {
+ pPreset->SetType( "DmePreset" );
+ presets.AddToTail( pPreset );
+ }
+ }
+
+ srcPresets.RemoveAll();
+ }
+ pElement->RemoveAttribute( "presets" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV7::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV7::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv8.cpp b/dmserializers/importsfmv8.cpp
new file mode 100644
index 0000000..dda78c8
--- /dev/null
+++ b/dmserializers/importsfmv8.cpp
@@ -0,0 +1,139 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV8 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV8( const char *formatName, const char *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV8 s_ImportSFMV8( "sfm_v8", "sfm_v9" );
+
+void InstallSFMV8Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV8 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV8::CImportSFMV8( const char *formatName, const char *nextFormatName ) :
+ BaseClass( formatName, nextFormatName )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV8::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+
+ if ( !V_stricmp( pType, "DmeAnimationSet" ) )
+ {
+ // Remove 'midpoint' from all controls, and
+ // Add 'defaultBalance' and 'defaultMultilevel' to all non-transform controls
+ CDmrElementArray<> srcControls( pElement, "controls" );
+ if ( srcControls.IsValid() )
+ {
+ int nCount = srcControls.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pControl = srcControls[i];
+ if ( pControl )
+ {
+ if ( !pControl->GetValue<bool>( "transform" ) )
+ {
+ pControl->InitValue( "defaultBalance", 0.5f );
+ pControl->InitValue( "defaultMultilevel", 0.5f );
+ pControl->RemoveAttribute( "midpoint" );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV8::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV8::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importsfmv9.cpp b/dmserializers/importsfmv9.cpp
new file mode 100644
index 0000000..6a5fbf7
--- /dev/null
+++ b/dmserializers/importsfmv9.cpp
@@ -0,0 +1,142 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: fixed "color" attribute of lights to be of type Color, rather than Vector4
+// this should have been put in a *long* time ago, but I somehow missed creating the updater between 3 and 4
+// fortunately, since all updates happen on untyped elements, it's reasonably safe to do this out of order
+//
+//=============================================================================
+
+#include "dmserializers.h"
+#include "dmebaseimporter.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include <limits.h>
+
+
+//-----------------------------------------------------------------------------
+// Format converter
+//-----------------------------------------------------------------------------
+class CImportSFMV9 : public CSFMBaseImporter
+{
+ typedef CSFMBaseImporter BaseClass;
+public:
+ CImportSFMV9( const char *formatName, const char *nextFormatName );
+
+private:
+ virtual bool DoFixup( CDmElement *pSourceRoot );
+
+
+ void FixupElement( CDmElement *pElement );
+ // Fixes up all elements
+ void BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportSFMV9 s_ImportSFMV9( "sfm_v9", "sfm_v10" );
+
+void InstallSFMV9Importer( IDataModel *pFactory )
+{
+ pFactory->AddLegacyUpdater( &s_ImportSFMV9 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CImportSFMV9::CImportSFMV9( const char *formatName, const char *nextFormatName ) :
+BaseClass( formatName, nextFormatName )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV9::FixupElement( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ const char *pType = pElement->GetTypeString();
+ if ( !V_stricmp( pType, "DmeLight" ) ||
+ !V_stricmp( pType, "DmeDirectionalLight" ) ||
+ !V_stricmp( pType, "DmeProjectedLight" ) ||
+ !V_stricmp( pType, "DmePointLight" ) ||
+ !V_stricmp( pType, "DmeSpotLight" ) ||
+ !V_stricmp( pType, "DmeAmbientLight" ) )
+ {
+ const CDmAttribute *pOldAttr = pElement->GetAttribute( "color", AT_VECTOR4 );
+ if ( !pOldAttr )
+ return;
+
+ Color color;
+
+ { // scoping this section of code since vecColor is invalid after RemoveAttribute
+ const Vector4D &vecColor = pOldAttr->GetValue< Vector4D >();
+ for ( int i = 0; i < 4; ++i )
+ {
+ color[ i ] = ( int )clamp( vecColor[ i ], 0.0f, 255.0f );
+ }
+
+ pElement->RemoveAttribute( "color" );
+ }
+
+ CDmAttribute *pNewAttr = pElement->AddAttribute( "color", AT_COLOR );
+ pNewAttr->SetValue( color );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fixes up all elements
+//-----------------------------------------------------------------------------
+void CImportSFMV9::BuildList( CDmElement *pElement, CUtlRBTree< CDmElement *, int >& list )
+{
+ if ( !pElement )
+ return;
+
+ if ( list.Find( pElement ) != list.InvalidIndex() )
+ return;
+
+ list.Insert( pElement );
+
+ // Descend to bottom of tree, then do fixup coming back up the tree
+ for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->GetType() == AT_ELEMENT )
+ {
+ CDmElement *pElementAt = pAttribute->GetValueElement<CDmElement>( );
+ BuildList( pElementAt, list );
+ continue;
+ }
+
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ {
+ CDmrElementArray<> array( pAttribute );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pChild = array[ i ];
+ BuildList( pChild, list );
+ }
+ continue;
+ }
+ }
+}
+
+bool CImportSFMV9::DoFixup( CDmElement *pSourceRoot )
+{
+ CUtlRBTree< CDmElement *, int > fixlist( 0, 0, DefLessFunc( CDmElement * ) );
+ BuildList( pSourceRoot, fixlist );
+ for ( int i = fixlist.FirstInorder(); i != fixlist.InvalidIndex() ; i = fixlist.NextInorder( i ) )
+ {
+ // Search and replace in the entire tree!
+ FixupElement( fixlist[ i ] );
+ }
+ return true;
+}
diff --git a/dmserializers/importvmf.cpp b/dmserializers/importvmf.cpp
new file mode 100644
index 0000000..7f57c6d
--- /dev/null
+++ b/dmserializers/importvmf.cpp
@@ -0,0 +1,629 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "importkeyvaluebase.h"
+#include "dmserializers.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "datamodel/dmattribute.h"
+
+
+//-----------------------------------------------------------------------------
+// Serialization class for VMF files (map files)
+//-----------------------------------------------------------------------------
+class CImportVMF : public CImportKeyValueBase
+{
+public:
+ virtual const char *GetName() const { return "vmf"; }
+ virtual const char *GetDescription() const { return "Valve Map File"; }
+ virtual int GetCurrentVersion() const { return 0; } // doesn't store a version
+
+ bool Serialize( CUtlBuffer &outBuf, CDmElement *pRoot );
+ CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues );
+
+private:
+ // Reads a single entity
+ bool UnserializeEntityKey( CDmAttribute *pEntities, KeyValues *pKeyValues );
+
+ // Reads entity editor keys
+ bool UnserializeEntityEditorKey( CDmAttribute *pEditor, KeyValues *pKeyValues );
+
+ // Reads keys that we currently do nothing with
+ bool UnserializeUnusedKeys( DmElementHandle_t hOther, KeyValues *pKeyValues );
+
+ // Writes out all everything other than entities
+ bool SerializeOther( CUtlBuffer &buf, CDmAttribute *pOther, const char **ppFilter = 0 );
+
+ // Writes out all entities
+ bool SerializeEntities( CUtlBuffer &buf, CDmAttribute *pEntities );
+
+ // Writes out a single attribute recursively
+ bool SerializeAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, bool bElementArrays );
+
+ // Writes entity editor keys
+ bool SerializeEntityEditorKey( CUtlBuffer &buf, DmElementHandle_t hEditor );
+
+ // Updates the max hammer id
+ void UpdateMaxHammerId( KeyValues *pKeyValue );
+
+ // Max id read from the file
+ int m_nMaxHammerId;
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportVMF s_ImportVMF;
+
+void InstallVMFImporter( IDataModel *pFactory )
+{
+ pFactory->AddSerializer( &s_ImportVMF );
+}
+
+
+//-----------------------------------------------------------------------------
+// Deals with poorly-named key values for the DME system
+//-----------------------------------------------------------------------------
+static const char *s_pKeyRemapNames[][2] =
+{
+ { "id", "__id" },
+ { "name", "__name" },
+ { "type", "__type" },
+ { NULL, NULL },
+};
+
+
+//-----------------------------------------------------------------------------
+// Gets remap name for unserialization/serailzation
+//-----------------------------------------------------------------------------
+static const char *GetRemapName( const char *pName, bool bSerialization )
+{
+ for ( int i = 0; s_pKeyRemapNames[i][0]; ++i )
+ {
+ if ( !Q_stricmp( pName, s_pKeyRemapNames[i][bSerialization] ) )
+ return s_pKeyRemapNames[i][1 - bSerialization];
+ }
+ return pName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a single attribute recursively
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, bool bElementArrays )
+{
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD | FATTRIB_DONTSAVE ) )
+ return true;
+
+ const char *pFieldName = GetRemapName( pAttribute->GetName(), true );
+ if ( !Q_stricmp( pFieldName, "editorType" ) )
+ return true;
+
+ if ( !IsArrayType( pAttribute->GetType() ) )
+ {
+ if ( !bElementArrays )
+ {
+ buf.Printf( "\"%s\" ", pFieldName );
+ if ( pAttribute->GetType() != AT_STRING )
+ {
+ buf.Printf( "\"" );
+ }
+ g_pDataModel->SetSerializationDelimiter( GetCStringCharConversion() );
+ pAttribute->Serialize( buf );
+ g_pDataModel->SetSerializationDelimiter( NULL );
+ if ( pAttribute->GetType() != AT_STRING )
+ {
+ buf.Printf( "\"" );
+ }
+ buf.Printf( "\n" );
+ }
+ }
+ else
+ {
+ if ( bElementArrays )
+ {
+ Assert( pAttribute->GetType() == AT_ELEMENT_ARRAY );
+ if ( !SerializeOther( buf, pAttribute ) )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all everything other than entities
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeOther( CUtlBuffer &buf, CDmAttribute *pOther, const char **ppFilter )
+{
+ CDmrElementArray<> array( pOther );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pElement = array[i];
+ const char *pElementName = pElement->GetName();
+ if ( ppFilter )
+ {
+ int j;
+ for ( j = 0; ppFilter[j]; ++j )
+ {
+ if ( !Q_stricmp( pElementName, ppFilter[j] ) )
+ break;
+ }
+
+ if ( !ppFilter[j] )
+ continue;
+ }
+
+ int nLen = Q_strlen( pElementName ) + 1;
+ char *pTemp = (char*)_alloca( nLen );
+ Q_strncpy( pTemp, pElementName, nLen );
+ Q_strlower( pTemp );
+ buf.Printf( "%s\n", pTemp );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+
+ // Normal attributes first
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( !SerializeAttribute( buf, pAttribute, false ) )
+ return false;
+ }
+
+ // Subkeys later
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( !SerializeAttribute( buf, pAttribute, true ) )
+ return false;
+ }
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes entity editor keys
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeEntityEditorKey( CUtlBuffer &buf, DmElementHandle_t hEditor )
+{
+ CDmElement *pEditorElement = g_pDataModel->GetElement( hEditor );
+ if ( !pEditorElement )
+ return true;
+
+ buf.Printf( "editor\n" );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+
+ {
+ CDmAttribute *pAttribute = pEditorElement->GetAttribute( "color" );
+ if ( pAttribute )
+ {
+ Color c = pAttribute->GetValue<Color>();
+ buf.Printf( "\"color\" \"%d %d %d\"\n", c.r(), c.g(), c.b() );
+ }
+ }
+ PrintIntAttribute( pEditorElement, buf, "id" ); // FIXME - id is a DmObjectId_t!!! This should never print anything!
+ PrintStringAttribute( pEditorElement, buf, "comments" );
+ PrintBoolAttribute( pEditorElement, buf, "visgroupshown" );
+ PrintBoolAttribute( pEditorElement, buf, "visgroupautoshown" );
+
+ for ( CDmAttribute *pAttribute = pEditorElement->FirstAttribute(); pAttribute != NULL; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD | FATTRIB_DONTSAVE ) )
+ continue;
+
+ const char *pKeyName = pAttribute->GetName();
+ if ( Q_stricmp( pKeyName, "color" ) && Q_stricmp( pKeyName, "id" ) &&
+ Q_stricmp( pKeyName, "comments" ) && Q_stricmp( pKeyName, "visgroupshown" ) &&
+ Q_stricmp( pKeyName, "visgroupautoshown" ) )
+ {
+ PrintStringAttribute( pEditorElement, buf, pKeyName );
+ }
+ }
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all entities
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeEntities( CUtlBuffer &buf, CDmAttribute *pEntities )
+{
+ // FIXME: Make this serialize in the order in which it appears in the FGD
+ // to minimize diffs
+ CDmrElementArray<> array( pEntities );
+
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pElement = array[i];
+ buf.Printf( "entity\n" );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+ buf.Printf( "\"id\" \"%s\"\n", pElement->GetName() );
+
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ // Do 'editor' at the end to preserve ordering and not make terrible diffs
+ if ( !Q_stricmp( pAttribute->GetName(), "editor" ) )
+ continue;
+
+ if ( !SerializeAttribute( buf, pAttribute, false ) )
+ return false;
+ }
+
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ // Do 'editor' at the end to preserve ordering and not make terrible diffs
+ if ( !Q_stricmp( pAttribute->GetName(), "editor" ) )
+ continue;
+
+ if ( !SerializeAttribute( buf, pAttribute, true ) )
+ return false;
+ }
+
+ // Do the 'editor'
+ CDmAttribute *pEditor = pElement->GetAttribute( "editor" );
+ if ( pEditor )
+ {
+ SerializeEntityEditorKey( buf, pEditor->GetValue<DmElementHandle_t>() );
+ }
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a new VMF file
+//-----------------------------------------------------------------------------
+bool CImportVMF::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ // This is done in this strange way (namely, serializing other twice) to minimize diffs
+ const char *pOtherFilter1[] =
+ {
+ "versioninfo", "visgroups", "viewsettings", "world", NULL
+ };
+
+ const char *pOtherFilter2[] =
+ {
+ "cameras", "cordon", "hidden", NULL
+ };
+
+ CDmAttribute *pOther = pRoot->GetAttribute( "other" );
+ if ( pOther && pOther->GetType() == AT_ELEMENT_ARRAY )
+ {
+ if ( !SerializeOther( buf, pOther, pOtherFilter1 ) )
+ return false;
+ }
+
+ // Serialize entities
+ CDmAttribute *pEntities = pRoot->GetAttribute( "entities" );
+ if ( pEntities && pEntities->GetType() == AT_ELEMENT_ARRAY )
+ {
+ if ( !SerializeEntities( buf, pEntities ) )
+ return false;
+ }
+
+ if ( pOther && pOther->GetType() == AT_ELEMENT_ARRAY )
+ {
+ if ( !SerializeOther( buf, pOther, pOtherFilter2 ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the max hammer id
+//-----------------------------------------------------------------------------
+void CImportVMF::UpdateMaxHammerId( KeyValues *pField )
+{
+ if ( !Q_stricmp( pField->GetName(), "id" ) )
+ {
+ int nId = atoi( pField->GetString() );
+ if ( nId > m_nMaxHammerId )
+ {
+ m_nMaxHammerId = nId;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads entity editor keys
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeEntityEditorKey( CDmAttribute *pEditorAttribute, KeyValues *pKeyValues )
+{
+ CDmElement *pEditor;
+ DmElementHandle_t hEditor = pEditorAttribute->GetValue<DmElementHandle_t>();
+ if ( hEditor == DMELEMENT_HANDLE_INVALID )
+ {
+ pEditor = CreateDmElement( "DmElement", "editor", NULL );;
+ if ( !pEditor )
+ return false;
+ hEditor = pEditor->GetHandle();
+ pEditorAttribute->SetValue( hEditor );
+ }
+ else
+ {
+ pEditor = g_pDataModel->GetElement( hEditor );
+ }
+
+ int r, g, b;
+ if ( sscanf( pKeyValues->GetString( "color", "" ), "%d %d %d", &r, &g, &b ) == 3 )
+ {
+ Color c( r, g, b, 255 );
+ if ( !pEditor->SetValue( "color", c ) )
+ return false;
+ }
+ KeyValues *pIdKey = pKeyValues->FindKey( "id" );
+ if ( pIdKey )
+ {
+ UpdateMaxHammerId( pIdKey );
+ }
+ AddIntAttribute( pEditor, pKeyValues, "id" );
+ AddStringAttribute( pEditor, pKeyValues, "comments" );
+ AddBoolAttribute( pEditor, pKeyValues, "visgroupshown" );
+ AddBoolAttribute( pEditor, pKeyValues, "visgroupautoshown" );
+
+ for ( KeyValues *pUserKey = pKeyValues->GetFirstValue(); pUserKey != NULL; pUserKey = pUserKey->GetNextValue() )
+ {
+ const char *pKeyName = pUserKey->GetName();
+ if ( Q_stricmp( pKeyName, "color" ) && Q_stricmp( pKeyName, "id" ) &&
+ Q_stricmp( pKeyName, "comments" ) && Q_stricmp( pKeyName, "visgroupshown" ) &&
+ Q_stricmp( pKeyName, "visgroupautoshown" ) )
+ {
+ AddStringAttribute( pEditor, pKeyValues, pKeyName );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a single entity
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeEntityKey( CDmAttribute *pEntities, KeyValues *pKeyValues )
+{
+ CDmElement *pEntity = CreateDmElement( "DmeVMFEntity", pKeyValues->GetString( "id", "-1" ), NULL );
+ if ( !pEntity )
+ return false;
+
+ CDmrElementArray<> array( pEntities );
+ array.AddToTail( pEntity );
+
+ // Each act busy needs to have an editortype associated with it so it displays nicely in editors
+ pEntity->SetValue( "editorType", "vmfEntity" );
+
+ const char *pClassName = pKeyValues->GetString( "classname", NULL );
+ if ( !pClassName )
+ return false;
+
+ // Read the actual fields
+ for ( KeyValues *pField = pKeyValues->GetFirstValue(); pField != NULL; pField = pField->GetNextValue() )
+ {
+ // FIXME: Knowing the FGD here would be useful for type determination.
+ // Look up the field by name based on class name
+ // In the meantime, just use the keyvalues type?
+ char pFieldName[512];
+ Q_strncpy( pFieldName, pField->GetName(), sizeof(pFieldName) );
+ Q_strlower( pFieldName );
+
+ // Don't do id: it's used as the name
+ // Not to mention it's a protected name
+ if ( !Q_stricmp( pFieldName, "id" ) )
+ {
+ UpdateMaxHammerId( pField );
+ continue;
+ }
+
+ // Type, name, and editortype are protected names
+ Assert( Q_stricmp( pFieldName, "type" ) && Q_stricmp( pFieldName, "name" ) && Q_stricmp( pFieldName, "editortype" ) );
+
+ switch( pField->GetDataType() )
+ {
+ case KeyValues::TYPE_INT:
+ if ( !AddIntAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) )
+ return false;
+ break;
+
+ case KeyValues::TYPE_FLOAT:
+ if ( !AddFloatAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) )
+ return false;
+ break;
+
+ case KeyValues::TYPE_STRING:
+ {
+ const char* pString = pField->GetString();
+ if (!pString || !pString[0])
+ return false;
+
+ // Look for vectors
+ Vector4D v;
+ if ( sscanf( pString, "%f %f %f %f", &v.x, &v.y, &v.z, &v.w ) == 4 )
+ {
+ if ( !pEntity->SetValue( pFieldName, v ) )
+ return false;
+ CDmAttribute *pAttribute = pEntity->GetAttribute( pFieldName );
+ pAttribute->AddFlag( FATTRIB_USERDEFINED );
+ }
+ else if ( sscanf( pString, "%f %f %f", &v.x, &v.y, &v.z ) == 3 )
+ {
+ if ( !pEntity->SetValue( pFieldName, v.AsVector3D() ) )
+ {
+ QAngle ang( v.x, v.y, v.z );
+ if ( !pEntity->SetValue( pFieldName, ang ) )
+ return false;
+ }
+ CDmAttribute *pAttribute = pEntity->GetAttribute( pFieldName );
+ pAttribute->AddFlag( FATTRIB_USERDEFINED );
+ }
+ else
+ {
+ if ( !AddStringAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) )
+ return false;
+ }
+ }
+ break;
+ }
+ }
+
+ // Read the subkeys
+ CDmAttribute *pEditor = pEntity->AddAttribute( "editor", AT_ELEMENT );
+ CDmrElementArray<> otherKeys( pEntity->AddAttribute( "other", AT_ELEMENT_ARRAY ) );
+ for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ bool bOk = false;
+ if ( !Q_stricmp( pSubKey->GetName(), "editor" ) )
+ {
+ bOk = UnserializeEntityEditorKey( pEditor, pSubKey );
+ }
+ else
+ {
+ // We don't currently do anything with the other keys
+ CDmElement *pOther = CreateDmElement( "DmElement", pSubKey->GetName(), NULL );
+ otherKeys.AddToTail( pOther );
+ bOk = UnserializeUnusedKeys( pOther->GetHandle(), pSubKey );
+ }
+
+ if ( !bOk )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads keys that we currently do nothing with
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeUnusedKeys( DmElementHandle_t hOther, KeyValues *pKeyValues )
+{
+ CDmElement *pOther = g_pDataModel->GetElement( hOther );
+
+ // Read the actual fields
+ for ( KeyValues *pField = pKeyValues->GetFirstValue(); pField != NULL; pField = pField->GetNextValue() )
+ {
+ UpdateMaxHammerId( pField );
+ const char *pFieldName = GetRemapName( pField->GetName(), false );
+ pOther->SetValue( pFieldName, pField->GetString() );
+ }
+
+ // Read the subkeys
+ CDmrElementArray<> subKeys( pOther->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) );
+ for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ CDmElement *pSubElement = CreateDmElement( "DmElement", pSubKey->GetName(), NULL );
+ subKeys.AddToTail( pSubElement );
+ if ( !UnserializeUnusedKeys( pSubElement->GetHandle(), pSubKey ) )
+ return false;
+ }
+ return true;
+}
+
+
+/*
+//-----------------------------------------------------------------------------
+// Reads the cordon data
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeCordonKey( IDmAttributeElement *pCordon, KeyValues *pKeyValues )
+{
+ DmElementHandle_t hCordon = pCordon->GetValue().Get();
+ if ( hCordon == DMELEMENT_HANDLE_INVALID )
+ {
+ hCordon = CreateDmElement( "DmElement", "cordon", NULL );
+ if ( hCordon == DMELEMENT_HANDLE_INVALID )
+ return false;
+ pCordon->SetValue( hCordon );
+ }
+
+ AddBoolAttribute( hCordon, pKeyValues, "active" );
+
+ Vector v;
+ if ( sscanf( pKeyValues->GetString( "mins", "" ), "(%f %f %f)", &v.x, &v.y, &v.z ) == 3 )
+ {
+ if ( !DmElementAddAttribute( hCordon, "mins", v ) )
+ return false;
+ }
+ if ( sscanf( pKeyValues->GetString( "maxs", "" ), "(%f %f %f)", &v.x, &v.y, &v.z ) == 3 )
+ {
+ if ( !DmElementAddAttribute( hCordon, "maxs", v ) )
+ return false;
+ }
+ return true;
+}
+*/
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+CDmElement* CImportVMF::UnserializeFromKeyValues( KeyValues *pKeyValues )
+{
+ m_nMaxHammerId = 0;
+
+ // Create the main element
+ CDmElement *pElement = CreateDmElement( "DmElement", "VMF", NULL );
+ if ( !pElement )
+ return NULL;
+
+ // Each vmf needs to have an editortype associated with it so it displays nicely in editors
+ pElement->SetValue( "editorType", "VMF" );
+
+ // The VMF is a series of keyvalue blocks; either
+ // 'entity', 'cameras', 'cordon', 'world', 'versioninfo', or 'viewsettings'
+ CDmAttribute *pEntityArray = pElement->AddAttribute( "entities", AT_ELEMENT_ARRAY );
+
+ // All main keys are root keys
+ CDmrElementArray<> otherKeys( pElement->AddAttribute( "other", AT_ELEMENT_ARRAY ) );
+ for ( ; pKeyValues != NULL; pKeyValues = pKeyValues->GetNextKey() )
+ {
+ bool bOk = false;
+ if ( !Q_stricmp( pKeyValues->GetName(), "entity" ) )
+ {
+ bOk = UnserializeEntityKey( pEntityArray, pKeyValues );
+ }
+ else
+ {
+ // We don't currently do anything with
+ CDmElement *pOther = CreateDmElement( "DmElement", pKeyValues->GetName(), NULL );
+ otherKeys.AddToTail( pOther );
+ bOk = UnserializeUnusedKeys( pOther->GetHandle(), pKeyValues );
+ }
+
+ if ( !bOk )
+ {
+ Warning( "Error importing VMF element %s\n", pKeyValues->GetName() );
+ return NULL;
+ }
+ }
+
+ // Resolve all element references recursively
+ RecursivelyResolveElement( pElement );
+
+ // Add the max id read in from the file to the root entity
+ pElement->SetValue( "maxHammerId", m_nMaxHammerId );
+
+ return pElement;
+}
diff --git a/dmserializers/importvmt.cpp b/dmserializers/importvmt.cpp
new file mode 100644
index 0000000..bd29a52
--- /dev/null
+++ b/dmserializers/importvmt.cpp
@@ -0,0 +1,738 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "importkeyvaluebase.h"
+#include "dmserializers.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "datamodel/dmattribute.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+
+
+//-----------------------------------------------------------------------------
+// Serialization class for Key Values
+//-----------------------------------------------------------------------------
+class CImportVMT : public CImportKeyValueBase
+{
+public:
+ virtual const char *GetName() const { return "vmt"; }
+ virtual const char *GetDescription() const { return "Valve Material File"; }
+ virtual int GetCurrentVersion() const { return 0; } // doesn't store a version
+
+ bool Serialize( CUtlBuffer &outBuf, CDmElement *pRoot );
+ CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues );
+
+private:
+ // Unserialize fallbacks
+ bool UnserializeFallbacks( CDmElement *pRoot, KeyValues *pFallbackKeyValues );
+
+ // Unserialize proxies
+ bool UnserializeProxies( CDmElement *pRoot, KeyValues *pKeyValues );
+
+ // Creates a shader parameter from a key value
+ bool UnserializeShaderParam( CDmElement *pRoot, KeyValues* pKeyValue );
+
+ // Creates a matrix material var
+ bool CreateMatrixMaterialVarFromKeyValue( CDmElement *pRoot, const char *pParamName, const char *pString );
+
+ // Creates a vector shader parameter
+ bool CreateVectorMaterialVarFromKeyValue( CDmElement *pRoot, const char *pParamName, const char *pString );
+
+ // Writes out a single shader parameter
+ bool SerializeShaderParameter( CUtlBuffer &buf, CDmAttribute *pAttribute );
+
+ // Writes out all shader parameters
+ bool SerializeShaderParameters( CUtlBuffer &buf, CDmElement *pRoot );
+
+ // Writes out all shader fallbacks
+ bool SerializeFallbacks( CUtlBuffer &buf, CDmElement *pRoot );
+
+ // Writes out all material proxies
+ bool SerializeProxies( CUtlBuffer &buf, CDmElement *pRoot );
+
+ // Handle patch files
+ void ExpandPatchFile( KeyValues *pKeyValues );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportVMT s_ImportVMT;
+
+void InstallVMTImporter( IDataModel *pFactory )
+{
+ pFactory->AddSerializer( &s_ImportVMT );
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a single shader parameter
+//-----------------------------------------------------------------------------
+bool CImportVMT::SerializeShaderParameter( CUtlBuffer &buf, CDmAttribute *pAttribute )
+{
+ // We have a shader parameter at this point.
+ switch ( pAttribute->GetType() )
+ {
+ case AT_INT:
+ buf.Printf( "\"%s\" \"%d\"\n", pAttribute->GetName(), pAttribute->GetValue<int>( ) );
+ break;
+
+ case AT_BOOL:
+ buf.Printf( "\"%s\" \"%d\"\n", pAttribute->GetName(), pAttribute->GetValue<bool>( ) );
+ break;
+
+ case AT_FLOAT:
+ buf.Printf( "\"%s\" \"%f\"\n", pAttribute->GetName(), pAttribute->GetValue<float>( ) );
+ break;
+
+ case AT_STRING:
+ buf.Printf( "\"%s\" \"%s\"\n", pAttribute->GetName(), pAttribute->GetValue<CUtlString>( ).Get() );
+ break;
+
+ case AT_VECTOR2:
+ {
+ const Vector2D &vec = pAttribute->GetValue<Vector2D>( );
+ buf.Printf( "\"%s\" \"[ %f %f ]\"\n", pAttribute->GetName(), vec.x, vec.y );
+ }
+ break;
+
+ case AT_VECTOR3:
+ {
+ const Vector &vec = pAttribute->GetValue<Vector>( );
+ buf.Printf( "\"%s\" \"[ %f %f %f ]\"\n", pAttribute->GetName(), vec.x, vec.y, vec.z );
+ }
+ break;
+
+ case AT_VECTOR4:
+ {
+ const Vector4D &vec = pAttribute->GetValue<Vector4D>( );
+ buf.Printf( "\"%s\" \"[ %f %f %f %f ]\"\n", pAttribute->GetName(), vec.x, vec.y, vec.z, vec.w );
+ }
+ break;
+
+ case AT_COLOR:
+ {
+ // NOTE: VMTs only support 3 component color (no alpha)
+ const Color &color = pAttribute->GetValue<Color>( );
+ buf.Printf( "\"%s\" \"{ %d %d %d }\"\n", pAttribute->GetName(), color.r(), color.g(), color.b() );
+ }
+ break;
+
+ case AT_VMATRIX:
+ {
+ const VMatrix &mat = pAttribute->GetValue<VMatrix>( );
+ buf.Printf( "\"%s\" \"[ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]\"\n", pAttribute->GetName(),
+ mat[0][0], mat[0][1], mat[0][2], mat[0][3],
+ mat[1][0], mat[1][1], mat[1][2], mat[1][3],
+ mat[2][0], mat[2][1], mat[2][2], mat[2][3],
+ mat[3][0], mat[3][1], mat[3][2], mat[3][3] );
+ }
+ break;
+
+ default:
+ Warning( "Attempted to serialize an unsupported shader parameter type %s (%s)\n",
+ pAttribute->GetName(), g_pDataModel->GetAttributeNameForType( pAttribute->GetType() ) );
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all shader parameters
+//-----------------------------------------------------------------------------
+bool CImportVMT::SerializeShaderParameters( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ for ( CDmAttribute *pAttribute = pRoot->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ // Skip the standard attributes
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
+ continue;
+
+ // Skip the shader name
+ const char *pName = pAttribute->GetName();
+ if ( !Q_stricmp( pAttribute->GetName(), "shader" ) )
+ continue;
+
+ // Names that don't start with a $ or a % are not shader parameters
+ if ( pName[0] != '$' && pName[0] != '%' )
+ continue;
+
+ // Skip element array children; we'll handle them separately.
+ if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
+ continue;
+
+ // Write out the shader parameter
+ if ( !SerializeShaderParameter( buf, pAttribute ) )
+ return false;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all shader fallbacks
+//-----------------------------------------------------------------------------
+bool CImportVMT::SerializeFallbacks( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ if ( !pRoot->HasAttribute( "fallbacks" ) )
+ return true;
+
+ CDmAttribute *pFallbacks = pRoot->GetAttribute( "fallbacks" );
+ if ( pFallbacks->GetType() != AT_ELEMENT_ARRAY )
+ return false;
+
+ CDmrElementArray<> array( pFallbacks );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pFallback = array[i];
+ Assert( pFallback );
+
+ PrintStringAttribute( pFallback, buf, "shader", false, true );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+ if ( !SerializeShaderParameters( buf, pFallback ) )
+ return false;
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all material proxies
+//-----------------------------------------------------------------------------
+bool CImportVMT::SerializeProxies( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ if ( !pRoot->HasAttribute( "proxies" ) )
+ return true;
+
+ CDmAttribute *pProxies = pRoot->GetAttribute( "proxies" );
+ if ( pProxies->GetType() != AT_ELEMENT_ARRAY )
+ return false;
+
+ CDmrElementArray<> array( pProxies );
+ int nCount = array.Count();
+ if ( nCount == 0 )
+ return true;
+
+ buf.Printf( "\"Proxies\"\n" );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pProxy = array[i];
+ Assert( pProxy );
+
+ PrintStringAttribute( pProxy, buf, "proxyType", false, true );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+ if ( !SerializeShaderParameters( buf, pProxy ) )
+ return false;
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ }
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a new vmt file
+//-----------------------------------------------------------------------------
+bool CImportVMT::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ PrintStringAttribute( pRoot, buf, "shader", false, true );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+
+ if ( !SerializeShaderParameters( buf, pRoot ) )
+ return false;
+
+ if ( !SerializeFallbacks( buf, pRoot ) )
+ return false;
+
+ if ( !SerializeProxies( buf, pRoot ) )
+ return false;
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parser utilities
+//-----------------------------------------------------------------------------
+static inline bool IsWhitespace( char c )
+{
+ return c == ' ' || c == '\t';
+}
+
+static inline bool IsEndline( char c )
+{
+ return c == '\n' || c == '\0';
+}
+
+static inline bool IsVector( char const* v )
+{
+ while (IsWhitespace(*v))
+ {
+ ++v;
+ if (IsEndline(*v))
+ return false;
+ }
+ return *v == '[' || *v == '{';
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a vector material var
+//-----------------------------------------------------------------------------
+int ParseVectorFromKeyValueString( const char *pParamName, const char* pScan, const char *pMaterialName, float vecVal[4] )
+{
+ bool divideBy255 = false;
+
+ // skip whitespace
+ while( IsWhitespace(*pScan) )
+ {
+ ++pScan;
+ }
+
+ if( *pScan == '{' )
+ {
+ divideBy255 = true;
+ }
+ else
+ {
+ Assert( *pScan == '[' );
+ }
+
+ // skip the '['
+ ++pScan;
+ int i;
+ for( i = 0; i < 4; i++ )
+ {
+ // skip whitespace
+ while( IsWhitespace(*pScan) )
+ {
+ ++pScan;
+ }
+
+ if( IsEndline(*pScan) || *pScan == ']' || *pScan == '}' )
+ {
+ if (*pScan != ']' && *pScan != '}')
+ {
+ Warning( "Warning in .VMT file (%s): no ']' or '}' found in vector key \"%s\".\n"
+ "Did you forget to surround the vector with \"s?\n", pMaterialName, pParamName );
+ }
+
+ // allow for vec2's, etc.
+ vecVal[i] = 0.0f;
+ break;
+ }
+
+ char* pEnd;
+
+ vecVal[i] = strtod( pScan, &pEnd );
+ if (pScan == pEnd)
+ {
+ Warning( "Error in .VMT file: error parsing vector element \"%s\" in \"%s\"\n", pParamName, pMaterialName );
+ return 0;
+ }
+
+ pScan = pEnd;
+ }
+
+ if( divideBy255 )
+ {
+ vecVal[0] *= ( 1.0f / 255.0f );
+ vecVal[1] *= ( 1.0f / 255.0f );
+ vecVal[2] *= ( 1.0f / 255.0f );
+ vecVal[3] *= ( 1.0f / 255.0f );
+ }
+
+ return i;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets shader parameter attributes
+//-----------------------------------------------------------------------------
+template< class T >
+inline bool SetShaderParamAttribute( CDmElement *pElement, const char *pAttributeName, const T &value )
+{
+ if ( !pElement )
+ return false;
+
+ if ( !pElement->SetValue( pAttributeName, value ) )
+ return false;
+
+ CDmAttribute *pAttribute = pElement->GetAttribute( pAttributeName );
+ pAttribute->AddFlag( FATTRIB_USERDEFINED );
+ return true;
+}
+
+inline bool SetShaderParamAttribute( CDmElement *pElement, const char *pAttributeName, const char *value )
+{
+ if ( !pElement )
+ return false;
+
+ if ( !pElement->SetValue( pAttributeName, value ) )
+ return false;
+
+ CDmAttribute *pAttribute = pElement->GetAttribute( pAttributeName );
+ pAttribute->AddFlag( FATTRIB_USERDEFINED );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a vector shader parameter
+//-----------------------------------------------------------------------------
+bool CImportVMT::CreateVectorMaterialVarFromKeyValue( CDmElement *pElement, const char *pParamName, const char *pString )
+{
+ Vector4D vecVal;
+ int nDim = ParseVectorFromKeyValueString( pParamName, pString, FileName(), vecVal.Base() );
+ if ( nDim == 0 )
+ return false;
+
+ // Create the variable!
+ switch ( nDim )
+ {
+ case 1:
+ return SetShaderParamAttribute( pElement, pParamName, vecVal[0] );
+ case 2:
+ return SetShaderParamAttribute( pElement, pParamName, vecVal.AsVector2D() );
+ case 3:
+ return SetShaderParamAttribute( pElement, pParamName, vecVal.AsVector3D() );
+ case 4:
+ return SetShaderParamAttribute( pElement, pParamName, vecVal );
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a matrix shader parameter
+//-----------------------------------------------------------------------------
+bool CImportVMT::CreateMatrixMaterialVarFromKeyValue( CDmElement *pElement, const char *pParamName, const char *pScan )
+{
+ // Matrices can be specified one of two ways:
+ // [ # # # # # # # # # # # # # # # # ]
+ // or
+ // center # # scale # # rotate # translate # #
+
+ VMatrix mat;
+ int count = sscanf( pScan, " [ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ]",
+ &mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
+ &mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
+ &mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
+ &mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
+ if (count == 16)
+ {
+ return SetShaderParamAttribute( pElement, pParamName, mat );
+ }
+
+ Vector2D scale, center;
+ float angle;
+ Vector2D translation;
+ count = sscanf( pScan, " center %f %f scale %f %f rotate %f translate %f %f",
+ &center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
+ if (count != 7)
+ return false;
+
+ VMatrix temp;
+ MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
+ MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
+ MatrixMultiply( temp, mat, mat );
+ MatrixBuildRotateZ( temp, angle );
+ MatrixMultiply( temp, mat, mat );
+ MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
+ MatrixMultiply( temp, mat, mat );
+
+ // Create the variable!
+ return SetShaderParamAttribute( pElement, pParamName, mat );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a shader parameter from a key value
+//-----------------------------------------------------------------------------
+bool CImportVMT::UnserializeShaderParam( CDmElement *pRoot, KeyValues* pKeyValues )
+{
+ char pParamName[512];
+ Q_strncpy( pParamName, pKeyValues->GetName(), sizeof(pParamName) );
+ Q_strlower( pParamName );
+
+ switch( pKeyValues->GetDataType() )
+ {
+ case KeyValues::TYPE_INT:
+ return SetShaderParamAttribute( pRoot, pParamName, pKeyValues->GetInt() );
+
+ case KeyValues::TYPE_FLOAT:
+ return SetShaderParamAttribute( pRoot, pParamName, pKeyValues->GetFloat() );
+
+ case KeyValues::TYPE_STRING:
+ {
+ char const* pString = pKeyValues->GetString();
+
+ // Only valid if it's a texture attribute
+ if ( !pString || !pString[0] )
+ return SetShaderParamAttribute( pRoot, pParamName, pString );
+
+ // Look for matrices
+ if ( CreateMatrixMaterialVarFromKeyValue( pRoot, pParamName, pString ) )
+ return true;
+
+ // Look for vectors
+ if ( !IsVector( pString ) )
+ return SetShaderParamAttribute( pRoot, pParamName, pString );
+
+ // Parse the string as a vector...
+ return CreateVectorMaterialVarFromKeyValue( pRoot, pParamName, pString );
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialize proxies
+//-----------------------------------------------------------------------------
+bool CImportVMT::UnserializeProxies( CDmElement *pElement, KeyValues *pKeyValues )
+{
+ // Create a child element array to contain all material proxies
+ CDmAttribute *pProxies = pElement->AddAttribute( "proxies", AT_ELEMENT_ARRAY );
+ if ( !pProxies )
+ return false;
+
+ CDmrElementArray<> array( pProxies );
+
+ // Proxies are a list of sub-keys, the name is the proxy name, subkeys are values
+ for ( KeyValues *pProxy = pKeyValues->GetFirstTrueSubKey(); pProxy != NULL; pProxy = pProxy->GetNextTrueSubKey() )
+ {
+ CDmElement *pProxyElement = CreateDmElement( "DmElement", pProxy->GetName(), NULL );
+ array.AddToTail( pProxyElement );
+ pProxyElement->SetValue( "proxyType", pKeyValues->GetName() );
+ pProxyElement->SetValue( "editorType", "vmtProxy" );
+
+ // Normal keys are proxy parameters
+ for ( KeyValues *pProxyParam = pProxy->GetFirstValue(); pProxyParam != NULL; pProxyParam = pProxyParam->GetNextValue() )
+ {
+ switch( pProxyParam->GetDataType() )
+ {
+ case KeyValues::TYPE_INT:
+ pProxyElement->SetValue( pProxyParam->GetName(), pProxyParam->GetInt() );
+ return true;
+
+ case KeyValues::TYPE_FLOAT:
+ pProxyElement->SetValue( pProxyParam->GetName(), pProxyParam->GetFloat() );
+ return true;
+
+ case KeyValues::TYPE_STRING:
+ pProxyElement->SetValue( pProxyParam->GetName(), pProxyParam->GetString() );
+ return true;
+
+ default:
+ Warning( "Unhandled proxy keyvalues type (proxy %s var %s)\n", pProxy->GetName(), pProxyParam->GetName() );
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialize fallbacks
+//-----------------------------------------------------------------------------
+bool CImportVMT::UnserializeFallbacks( CDmElement *pElement, KeyValues *pFallbackKeyValues )
+{
+ // Create a child element array to contain all material proxies
+ CDmAttribute *pFallbacks = pElement->AddAttribute( "fallbacks", AT_ELEMENT_ARRAY );
+ if ( !pFallbacks )
+ return false;
+
+ CDmrElementArray<> array( pFallbacks );
+
+ CDmElement *pFallback = CreateDmElement( "DmElement", pFallbackKeyValues->GetName(), NULL );
+ array.AddToTail( pFallback );
+ pFallback->SetValue( "editorType", "vmtFallback" );
+
+ // Normal keys are shader parameters
+ for ( KeyValues *pShaderParam = pFallbackKeyValues->GetFirstValue(); pShaderParam != NULL; pShaderParam = pShaderParam->GetNextValue() )
+ {
+ if ( !UnserializeShaderParam( pFallback, pShaderParam ) )
+ {
+ Warning( "Error importing vmt shader parameter %s\n", pShaderParam->GetName() );
+ return NULL;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// VMT parser
+//-----------------------------------------------------------------------------
+void InsertKeyValues( KeyValues& dst, KeyValues& src, bool bCheckForExistence )
+{
+ KeyValues *pSrcVar = src.GetFirstSubKey();
+ while( pSrcVar )
+ {
+ if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
+ {
+ switch( pSrcVar->GetDataType() )
+ {
+ case KeyValues::TYPE_STRING:
+ dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
+ break;
+ case KeyValues::TYPE_INT:
+ dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
+ break;
+ case KeyValues::TYPE_FLOAT:
+ dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
+ break;
+ case KeyValues::TYPE_PTR:
+ dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
+ break;
+ }
+ }
+ pSrcVar = pSrcVar->GetNextKey();
+ }
+
+ if( bCheckForExistence )
+ {
+ for( KeyValues *pScan = dst.GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
+ {
+ KeyValues *pTmp = src.FindKey( pScan->GetName() );
+ if( !pTmp )
+ continue;
+ // make sure that this is a subkey.
+ if( pTmp->GetDataType() != KeyValues::TYPE_NONE )
+ continue;
+ InsertKeyValues( *pScan, *pTmp, bCheckForExistence );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle patch files
+//-----------------------------------------------------------------------------
+void CImportVMT::ExpandPatchFile( KeyValues *pKeyValues )
+{
+ int count = 0;
+ while( count < 10 && stricmp( pKeyValues->GetName(), "patch" ) == 0 )
+ {
+// WriteKeyValuesToFile( "patch.txt", keyValues );
+ const char *pIncludeFileName = pKeyValues->GetString( "include" );
+ if( pIncludeFileName )
+ {
+ KeyValues * includeKeyValues = new KeyValues( "vmt" );
+ bool success = includeKeyValues->LoadFromFile( g_pFullFileSystem, pIncludeFileName, IsX360() ? "GAME" : NULL );
+ if( success )
+ {
+ KeyValues *pInsertSection = pKeyValues->FindKey( "insert" );
+ if( pInsertSection )
+ {
+ InsertKeyValues( *includeKeyValues, *pInsertSection, false );
+ }
+
+ KeyValues *pReplaceSection = pKeyValues->FindKey( "replace" );
+ if( pReplaceSection )
+ {
+ InsertKeyValues( *includeKeyValues, *pReplaceSection, true );
+ }
+
+ *pKeyValues = *includeKeyValues;
+ includeKeyValues->deleteThis();
+ // Could add other commands here, like "delete", "rename", etc.
+ }
+ else
+ {
+ includeKeyValues->deleteThis();
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ count++;
+ }
+ if( count >= 10 )
+ {
+ Warning( "Infinite recursion in patch file?\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+CDmElement* CImportVMT::UnserializeFromKeyValues( KeyValues *pKeyValues )
+{
+ ExpandPatchFile( pKeyValues );
+
+ // Create the main element
+ CDmElement *pRoot = CreateDmElement( "DmElement", "VMT", NULL );
+ if ( !pRoot )
+ return NULL;
+
+ // Each material needs to have an editortype associated with it so it displays nicely in editors
+ pRoot->SetValue( "editorType", "vmt" );
+
+ // Each material needs a proxy list and a fallback list
+ if ( !pRoot->AddAttribute( "proxies", AT_ELEMENT_ARRAY ) )
+ return NULL;
+ if ( !pRoot->AddAttribute( "fallbacks", AT_ELEMENT_ARRAY ) )
+ return NULL;
+
+ // The keyvalues name is the shader name
+ pRoot->SetValue( "shader", pKeyValues->GetName() );
+
+ // Normal keys are shader parameters
+ for ( KeyValues *pShaderParam = pKeyValues->GetFirstValue(); pShaderParam != NULL; pShaderParam = pShaderParam->GetNextValue() )
+ {
+ if ( !UnserializeShaderParam( pRoot, pShaderParam ) )
+ {
+ Warning( "Error importing vmt shader parameter %s\n", pShaderParam->GetName() );
+ return NULL;
+ }
+ }
+
+ // Subkeys are either proxies or fallbacks
+ for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ if ( !Q_stricmp( pSubKey->GetName(), "Proxies" ) )
+ {
+ UnserializeProxies( pRoot, pSubKey );
+ }
+ else
+ {
+ UnserializeFallbacks( pRoot, pSubKey );
+ }
+ }
+
+ // Resolve all element references recursively
+ RecursivelyResolveElement( pRoot );
+
+ return pRoot;
+}