{
	CDmAttribute *pControlsAttr = pAnimSet->GetAttribute( "controls" );
	if ( pControlsAttr == NULL )
		return NULL;

	CDmrElementArray<> controls( pControlsAttr );
	int nControls = controls.Count();
	for ( int iControl = 0; iControl < nControls; ++iControl )
	{
		CDmElement *pElement = controls[ iControl ];
		if ( pElement )
		{
			if ( V_stricmp( pchControlName, pElement->GetName() ) == 0 )
			{
				return pElement;
			}
		}
	}

	return NULL;
}

// 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.
// 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/dmattributevar.h"
#include "datamodel/dmelementfactoryhelper.h"
#include "tier2/tier2.h"
#include "tier1/timeutils.h"
#include "tier1/fmtstr.h"
#include "tier1/utldict.h"

// format updater macros
typedef bool (*FnUpdater)( CDmElement *pElement );

#define IMPLEMENT_UPDATER( n )											\
	bool Update##n##_R( CDmElement **ppElement, bool bParity )			\
	{																	\
		return UpdateElement_R( &Update##n, ppElement, bParity );		\
	}

#define DECLARE_FORMAT_UPDATER( _name, _description, _extension, _encoding, _updaters ) \
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 ARRAYSIZE( _updaters ); } \
		virtual bool Update( CDmElement **ppRoot, int nSourceVersion ) \
		{ \
			if ( !ppRoot || !*ppRoot ) \
				return false; \
			if ( nSourceVersion > GetCurrentVersion() ) \
				return false; \
			int nUpdater = MAX( 0, nSourceVersion - 1 ); \
			bool bParity = true; \
			while ( _updaters[ nUpdater ] ) \
			{ \
				if ( !_updaters[ nUpdater ]( ppRoot, bParity ) ) \
					return false; \
				bParity = !bParity; \
				++nUpdater; \
			} \
			return true; \
		} \
	}; \
	static CDmFormatUpdater_ ## _name s_FormatUpdater ## _name;

#define INSTALL_FORMAT_UPDATER( _name ) g_pDataModel->AddFormatUpdater( &s_FormatUpdater ## _name )

// updater helper functions

void ChangeAttributeType( CDmElement *pElement, const char *pAttrName, DmAttributeType_t type )
{
	CDmAttribute *pAttr = pElement->GetAttribute( pAttrName );
	Assert( pAttr );
	if ( !pAttr )
		return;

	pAttr->ChangeType_UNSAFE( type );
}

CDmElement *FindElementNamed( CDmrElementArray<> elements, const char *pTargetName )
{
	int nElements = elements.Count();
	for ( int i = 0; i < nElements; ++i )
	{
		CDmElement *pElement = elements[ i ];
		if ( pElement && !V_strcmp( pTargetName, pElement->GetName() ) )
			return pElement;
	}
	return NULL;
}

CDmElement *FindChannelsClipForChannel( CDmElement *pFilmClip, CDmElement *pChannel )
{
	const static CUtlSymbolLarge channelsClipSym = g_pDataModel->GetSymbol( "DmeChannelsClip" );
	const static CUtlSymbolLarge channelsSym = g_pDataModel->GetSymbol( "channels" );
	DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pChannel->GetHandle() );
	while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
	{
		CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
		CDmElement *pParent = pAttribute->GetOwner();
		if ( pParent && pParent->GetType() == channelsClipSym && pAttribute->GetNameSymbol() == channelsSym )
		{
			CUtlVector< ElementPathItem_t > path;
			if ( pParent->FindReferer( pFilmClip->GetHandle(), path, TD_ALL ) ) // NOTE - with non-typed elements, TD_SHALLOW == TD_NONE and TD_DEEP == TD_ALL
				return pParent;
		}

		i = g_pDataModel->NextAttributeReferencingElement( i );
	}

	return NULL;
}

CDmElement *FindChannelTargettingElement( CDmElement *pTarget, const char *pTargetAttr, bool bFromTarget )
{
	const static CUtlSymbolLarge channelSym = g_pDataModel->GetSymbol( "DmeChannel" );
	const static CUtlSymbolLarge fromElementSym = g_pDataModel->GetSymbol( bFromTarget ? "fromElement" : "toElement" );
	DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pTarget->GetHandle() );
	for( ; i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; i = g_pDataModel->NextAttributeReferencingElement( i ) )
	{
		CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
		if ( pAttribute->GetNameSymbol() != fromElementSym )
			continue;

		CDmElement *pParent = pAttribute->GetOwner();
		if ( !pParent || pParent->GetType() != channelSym )
			continue;

		if ( !V_strcmp( pParent->GetValueString( bFromTarget ? "fromAttribute" : "toAttribute" ), pTargetAttr ) )
			return pParent;
	}

	return NULL;
}

CDmElement *GetLogLayerFromChannel( CDmElement *pChannel )
{
	CDmElement *pLog = pChannel->GetValueElement< CDmElement >( "log" );
	if ( !pLog )
		return NULL;

	const CUtlVector< DmElementHandle_t > &layers = pLog->GetValue< CUtlVector< DmElementHandle_t > >( "layers" );
	if ( layers.Count() != 1 )
		return NULL;

	return g_pDataModel->GetElement( layers[ 0 ] );
}

void ConcatTransforms( Quaternion qParent, Vector vParent, Quaternion qLocal, Vector vLocal, Quaternion &qFinal, Vector &vFinal )
{
	matrix3x4_t mLocal, mParent, mFinal;
	QuaternionMatrix( qLocal, vLocal, mLocal );
	QuaternionMatrix( qParent, vParent, mParent );
	ConcatTransforms( mParent, mLocal, mFinal );
	MatrixQuaternion( mFinal, qFinal );
	MatrixPosition( mFinal, vFinal );
}

// format updater functions

// IMPORTANT: all elements created during update MUST have their parity set to the parity of the current updater
//            otherwise, they (and their children) won't be traversed during the next updater

// Update19 -- Removal of Control display sets. Removes all control display
// sets from animation sets and removes references to display sets from all
// controls. Also updates rigs to have hidden control groups instead of
// display sets.
void FixupRigGroups( CDmElement *pRig );

bool Update19( CDmElement *pElement )
{
	const static CUtlSymbolLarge symDmeControlDisplaySet = g_pDataModel->GetSymbol( "DmeControlDisplaySet" );
	const static CUtlSymbolLarge symDmeAnimationSet = g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge symDmeRig = g_pDataModel->GetSymbol( "DmeRig" );
	const static CUtlSymbolLarge symDisplaySetAttr = g_pDataModel->GetSymbol( "displaySet" );

	if ( pElement->GetType() == symDmeControlDisplaySet )
	{
		// This case shouldn't actually happen all paths which refer to display
		// sets should be removed before the display set can actually be reached.
		DestroyElement( pElement );
	}
	else if ( pElement->GetType() == symDmeAnimationSet )
	{
		pElement->RemoveAttribute( "displaySets" );
	}
	else if ( pElement->GetType() == symDmeRig )
	{
		if ( pElement->HasAttribute( "displaySetElements" ) )
		{
			FixupRigGroups( pElement );
			pElement->RemoveAttribute( "displaySetElements" );
		}
	}
	else
	{
		pElement->RemoveAttribute( "displaySet" );
	}

	return true;
}

CDmElement *FindChildControlGroup( CDmElement *pGroup, const char *pName, bool bRecursive )
{
	CDmAttribute *pChildrenAttr = pGroup->GetAttribute( "children", AT_ELEMENT_ARRAY );
	if ( pChildrenAttr == NULL )
		return NULL;

	CDmrElementArray< CDmElement > children( pChildrenAttr );
	int nNumChildren = children.Count();

	for ( int iChild = 0; iChild < nNumChildren; ++iChild )
	{
		CDmElement *pChild = children[ iChild ];
		if ( pChild )
		{
			if ( V_stricmp( pChild->GetName(), pName ) == 0 )
				return pChild;

			if ( bRecursive )
			{
				FindChildControlGroup( pChild, pName, true );
			}
		}
	}

	return NULL;
}

CDmElement *FindOrCreateControlGroup( CDmElement *pRig, CDmElement *pParentGroup, const char *pGroupName )
{
	CDmElement *pGroup = FindChildControlGroup( pParentGroup, pGroupName, true );

	if ( pGroup == NULL )
	{
		pGroup = CreateElement< CDmElement >( "DmeControlGroup", pGroupName, pParentGroup->GetFileId() );
		pGroup->SetParity( pRig->GetParity() );

		// Add the new group to the parent
		CDmAttribute *pChildrenAttr = pParentGroup->GetAttribute( "children", AT_ELEMENT_ARRAY );
		if ( pChildrenAttr )
		{
			CDmrElementArray< CDmElement > children( pChildrenAttr );
			children.AddToTail( pGroup );
		}
	}

	return pGroup;
}

void ReParentCongrolGroup( CDmElement *pControlGroup, CDmElement *pNewParent )
{
	const static CUtlSymbolLarge symChildren = g_pDataModel->GetSymbol( "children" );

	if ( ( pControlGroup == NULL ) || ( pNewParent == NULL ) )
		return;

	// Find the current parent of the control group
	CDmElement *pCurrentParent = FindReferringElement< CDmElement >( pControlGroup, symChildren );
	if ( pCurrentParent == pNewParent )
		return;

	// Add the group to the children of the new parent
	CDmAttribute *pNewChildrenAttr = pNewParent->GetAttribute( "children", AT_ELEMENT_ARRAY );
	if ( pNewChildrenAttr == NULL )
	{
		pNewChildrenAttr = pNewParent->AddAttributeElementArray< CDmElement >( "children" );
	}

	CDmrElementArray< CDmElement > newChildren( pNewChildrenAttr );
	newChildren.AddToTail( pControlGroup );

	// Remove the group from the children of the old parent
	if ( pCurrentParent )
	{
		CDmAttribute *pOldChildrenAttr = pCurrentParent->GetAttribute( "children", AT_ELEMENT_ARRAY );
		if ( pOldChildrenAttr )
		{
			CDmrElementArray< CDmElement > oldChildren( pOldChildrenAttr );
			int nIndex = oldChildren.Find( pControlGroup );
			oldChildren.Remove( nIndex );
		}
	}
}

bool IsControlGroupEmpty( CDmElement *pControlGroup )
{
	CDmAttribute *pControlsAttr = pControlGroup->GetAttribute( "controls" );
	if ( pControlsAttr )
	{
		CDmrElementArray< CDmElement > controls( pControlsAttr );
		if ( controls.Count() > 0 )
			return false;
	}

	CDmAttribute *pChildrenAttr = pControlGroup->GetAttribute( "children", AT_ELEMENT_ARRAY );
	if ( pChildrenAttr )
	{
		CDmrElementArray< CDmElement > children( pChildrenAttr );
		int nNumChildren = children.Count();
		for ( int iChild = 0; iChild < nNumChildren; ++iChild )
		{
			CDmElement *pChild = children[ iChild ];
			if ( pChild )
			{
				if ( IsControlGroupEmpty( pChild ) == false )
					return false;
			}
		}
	}

	return true;
}

void SetControlGroupState( CDmElement *pRootControlGroup, const char *pGroupName, bool bVisible, bool bSnappable )
{
	CDmElement *pControlGroup = FindChildControlGroup( pRootControlGroup, pGroupName, true );
	if ( pControlGroup == NULL )
		return;

	pControlGroup->SetValue< bool >( "visible", bVisible, true );
	pControlGroup->SetValue< bool >( "snappable", bSnappable, true );
}

void MoveRigControlsToGroup( CDmElement *pRig, CDmElement *pAnimSetElements, CDmElement *pRootGroup, const char *pSrcGroupName, const char *pDstGroupName, bool bTransformOnly )
{
	const static CUtlSymbolLarge symDmeTransformControl = g_pDataModel->GetSymbol( "DmeTransformControl" );
	const static CUtlSymbolLarge symElementList = g_pDataModel->GetSymbol( "elementList" );

	// First find the specified source control group
	CDmElement *pSrcGroup = pRootGroup;
	if ( pSrcGroupName )
	{
		pSrcGroup = FindChildControlGroup( pRootGroup, pSrcGroupName, true );
	}
	if ( pSrcGroup == NULL )
		return;

	// Iterate through the controls in the source control group,
	// if they are rig elements add them to the destination group.
	CDmAttribute *pControlsAttr = pSrcGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
	if ( pControlsAttr == NULL )
		return;

	CDmrElementArray< CDmElement > controls( pControlsAttr );
	int nNumControls = controls.Count();

	CDmElement *pDstGroup = NULL;
	CDmAttribute *pDstControlsAttr = NULL;

	for ( int iControl = 0; iControl < nNumControls; ++iControl )
	{
		CDmElement *pControl = controls[ iControl ];
		if ( pControl == NULL )
			continue;

		if ( bTransformOnly && ( pControl->GetType() != symDmeTransformControl ) )
			continue;

		CUtlVector< CDmElement* > referringElements( 0, 4 );
		FindReferringElements( referringElements, pControl, symElementList );
		if ( referringElements.Find( pAnimSetElements ) != referringElements.InvalidIndex() )
		{
			if ( pDstGroup == NULL )
			{
				pDstGroup = FindOrCreateControlGroup( pRig, pRootGroup, pDstGroupName );
			}

			if ( pDstControlsAttr == NULL )
			{
				pDstControlsAttr = pDstGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
				if ( pDstControlsAttr == NULL )
				{
					pDstControlsAttr = pDstGroup->AddAttributeElementArray< CDmElement >( "controls" );
				}
			}

			CDmrElementArray< CDmElement > dstControls( pDstControlsAttr );
			dstControls.AddToTail( pControl );
		}
	}

	// Now remove the rig controls from the source group
	if ( pDstControlsAttr != NULL )
	{
		CDmrElementArray< CDmElement > rigControls( pDstControlsAttr );
		int nNumRigControls = rigControls.Count();

		for ( int iControl = 0; iControl < nNumRigControls; ++iControl )
		{
			CDmElement *pControl = rigControls[ iControl ];
			int nIndex = controls.Find( pControl );
			if ( nIndex != controls.InvalidIndex() )
			{
				controls.Remove( nIndex );
			}
		}
	}
}

void RemoveInvalidTransformControls( CDmElement *pAnimSet )
{
	const static CUtlSymbolLarge symDmeTransformControl = g_pDataModel->GetSymbol( "DmeTransformControl" );
	const static CUtlSymbolLarge symControls = g_pDataModel->GetSymbol( "controls" );

	CDmAttribute *pControlsAttr = pAnimSet->GetAttribute( "controls", AT_ELEMENT_ARRAY );
	if ( pControlsAttr == NULL )
		return;

	CDmrElementArray< CDmElement > controls( pControlsAttr );
	int nNumControls = controls.Count();

	CUtlVector< CDmElement* > invalidControls( 0, nNumControls / 2 );
	for ( int iControl = 0; iControl < nNumControls; ++iControl )
	{
		CDmElement *pControl = controls[ iControl ];
		if ( pControl == NULL )
			continue;

		if ( pControl->GetType() == symDmeTransformControl )
		{
			CDmElement *pPosChannel = pControl->GetValueElement< CDmElement >( "positionChannel" );
			CDmElement *pRotChannel = pControl->GetValueElement< CDmElement >( "orientationChannel" );

			if ( ( pPosChannel == NULL ) && ( pRotChannel == NULL ) )
			{
				invalidControls.AddToTail( pControl );
			}
		}
	}

	int nNumInvalid = invalidControls.Count();
	for ( int iInvalid = 0; iInvalid < nNumInvalid; ++iInvalid )
	{
		CDmElement *pInvalidControl = invalidControls[ iInvalid ];
		if ( pInvalidControl == NULL )
			return;

		// Remove the control from the animation set
		int nIndex = controls.Find( pInvalidControl );
		if ( nIndex != controls.InvalidIndex() )
		{
			controls.Remove( nIndex );
		}

		// Remove the control from the control groups
		CUtlVector < CDmElement* > referringElements( 0, 4 );
		FindReferringElements( referringElements, pInvalidControl, symControls );
		int nNumReferringElements = referringElements.Count();

		for ( int iElement = 0; iElement < nNumReferringElements; ++iElement )
		{
			CDmElement *pElement = referringElements[ iElement ];
			if ( pElement == NULL )
				continue;

			CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY );
			if ( pControlsAttr == NULL )
				continue;

			CDmrElementArray< CDmElement > controls( pControlsAttr );
			nIndex = controls.Find( pInvalidControl );
			if ( nIndex != controls.InvalidIndex() )
			{
				controls.Remove( nIndex );
			}
		}
	}
}

void FixupRigGroupsForAnimationSet( CDmElement *pRig, CDmElement *pAnimSetElements )
{
	const static CUtlSymbolLarge symDmeAnimationSet = g_pDataModel->GetSymbol( "DmeAnimationSet" );

	// Get the animation set
	CDmElement *pAnimSet = pAnimSetElements->GetValueElement< CDmElement >( "animationSet" );
	if ( ( pAnimSet == NULL ) || ( pAnimSet->GetType() != symDmeAnimationSet ) )
		return;

	// First clean out any invalid controls from the animations set
	RemoveInvalidTransformControls( pAnimSet );

	// Get the root control group of the animation set
	CDmElement *pRootControlGroup = pAnimSet->GetValueElement< CDmElement >( "rootControlGroup" );
	if ( pRootControlGroup == NULL )
		return;

	// Move the rig controls into their own groups
	MoveRigControlsToGroup( pRig, pAnimSetElements, pRootControlGroup, NULL, "RigHelpers", false );
	MoveRigControlsToGroup( pRig, pAnimSetElements, pRootControlGroup, "Body", "RigBody", true );
	MoveRigControlsToGroup( pRig, pAnimSetElements, pRootControlGroup, "Arms", "RigArms", true );
	MoveRigControlsToGroup( pRig, pAnimSetElements, pRootControlGroup, "Legs", "RigLegs", true );
	MoveRigControlsToGroup( pRig, pAnimSetElements, pRootControlGroup, "Root", "RigRoot", true );

	// Move the fingers into their own groups
	CDmElement *pArmsGroup = FindChildControlGroup( pRootControlGroup, "Arms", true );
	if ( pArmsGroup )
	{
		// Find the fingers group and make sure it is a child of the root and then
		// make sure the left and right fingers groups children of the fingers group.
		CDmElement *pFingers = FindOrCreateControlGroup( pRig, pRootControlGroup, "Fingers" );
		if ( pFingers )
		{
			ReParentCongrolGroup( pFingers, pRootControlGroup );

			CDmElement *pFingersR = FindChildControlGroup( pArmsGroup, "Fingers_R", false );
			if ( pFingersR )
			{
				pFingersR->SetName( "RightFingers" );
				ReParentCongrolGroup( pFingersR, pFingers );
			}

			CDmElement *pFingersL = FindChildControlGroup( pArmsGroup, "Fingers_L", false );
			if ( pFingersL )
			{
				pFingersL->SetName( "LeftFingers" );
				ReParentCongrolGroup( pFingersL, pFingers );
			}
		}
	}

	// Hide the original body groups and rig utility groups
	SetControlGroupState( pRootControlGroup, "Arms",			false, true );
	SetControlGroupState( pRootControlGroup, "Legs",			false, true );
	SetControlGroupState( pRootControlGroup, "Body",			false, true );
	SetControlGroupState( pRootControlGroup, "Root",			false, true );
	SetControlGroupState( pRootControlGroup, "Other",			false, true );
	SetControlGroupState( pRootControlGroup, "Unknown",			false, true );
	SetControlGroupState( pRootControlGroup, "Attachments",		false, true );
	SetControlGroupState( pRootControlGroup, "RigHelpers",		false, false );
	SetControlGroupState( pRootControlGroup, "RigConstraints",	false, false );

	// Add the body groups to the list of groups hidden by the rig
	CDmAttribute *pHiddenGroupsAttr = pAnimSetElements->GetAttribute( "hiddenGroups", AT_STRING_ARRAY );
	if ( pHiddenGroupsAttr == NULL )
	{
		pHiddenGroupsAttr = pAnimSetElements->AddAttribute( "hiddenGroups", AT_STRING_ARRAY );
	}

	CDmrStringArray hiddenGroups( pHiddenGroupsAttr );
	hiddenGroups.AddToTail( "Body" );
	hiddenGroups.AddToTail( "Arms" );
	hiddenGroups.AddToTail( "Legs" );
	hiddenGroups.AddToTail( "Root" );
	hiddenGroups.AddToTail( "Other" );
	hiddenGroups.AddToTail( "Unknown" );

	// Set the color of all of the top level groups and remove any control groups which are empty
	CDmAttribute *pChildrenAttr = pRootControlGroup->GetAttribute( "children", AT_ELEMENT_ARRAY );
	if ( pChildrenAttr )
	{
		Color topLevelColor( 0, 128, 255, 255 );
		Color controlColor( 200, 200, 200, 255 );

		CDmrElementArray< CDmElement > children( pChildrenAttr );
		int nChildren = children.Count();

		CUtlVector< CDmElement* > emptyGroups( 0, nChildren );

		for ( int iChild = 0; iChild < nChildren; ++iChild )
		{
			CDmElement *pChildGroup = children[ iChild ];
			pChildGroup->SetValue< Color >( "groupColor", topLevelColor );
			pChildGroup->SetValue< Color >( "controlColor", controlColor );

			// If the child group is empty add it to the list of children to remove
			if ( IsControlGroupEmpty( pChildGroup ) )
			{
				emptyGroups.AddToTail( pChildGroup );
			}
		}

		// Remove the empty groups
		int nNumEmptyGroups = emptyGroups.Count();
		for ( int iEmptyGroup = 0; iEmptyGroup < nNumEmptyGroups; ++iEmptyGroup )
		{
			CDmElement *pEmptyGroup = emptyGroups[ iEmptyGroup ];
			int nIndex = children.Find( pEmptyGroup );
			if ( nIndex != children.InvalidIndex() )
			{
				children.Remove( nIndex );
			}
		}
	}
}

void FixupRigGroups( CDmElement *pRig )
{
	CDmAttribute *pAnimSetListAttr = pRig->GetAttribute( "animSetList", AT_ELEMENT_ARRAY );
	if ( pAnimSetListAttr == NULL )
		return;

	CDmrElementArray<> animSetList( pAnimSetListAttr );

	int nNumAnimSets = animSetList.Count();
	for ( int iAnimSet = 0; iAnimSet < nNumAnimSets; ++iAnimSet )
	{
		CDmElement *pAnimSetElements = animSetList[ iAnimSet ];
		if ( pAnimSetElements )
		{
			FixupRigGroupsForAnimationSet( pRig, pAnimSetElements );
		}
	}
}

// Update18 -- removing procedural presetgroup and presets (now exist only in code, and not as elements)

bool Update18( CDmElement *pElement )
{
	const static CUtlSymbolLarge symDmeAnimationSet = g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge symDmePreset = g_pDataModel->GetSymbol( "DmePreset" );

	if ( pElement->GetType() == symDmeAnimationSet )
	{
		CDmAttribute *pPresetGroupsAttr = pElement->GetAttribute( "presetGroups", AT_ELEMENT_ARRAY );
		if ( pPresetGroupsAttr )
		{
			CDmrElementArray<> presetGroups( pPresetGroupsAttr );
			int nNumPresetGroups = presetGroups.Count();
			for ( int i = nNumPresetGroups - 1; i >= 0; --i )
			{
				if ( CDmElement *pPresetGroup = presetGroups[ i ] )
				{
					if ( V_strcmp( pPresetGroup->GetName(), "procedural" ) != 0 && V_strcmp( pPresetGroup->GetName(), "Procedural" ) != 0 )
						continue;
				}

				presetGroups.Remove( i );
			}
		}
	}
	else if ( pElement->GetType() == symDmePreset )
	{
		pElement->RemoveAttribute( "procedural" );
	}

	return true;
}

// Update17 -- Transform control unification. Separate transform controls for
// position and orientation are combine in to a single transform control and
// the control group which previously contained the separate position and
// orientation controls is removed.
void UnifyTransformControl( CDmElement *pControl, CDmElement *pAnimSet );
void DestroyControl( CDmElement *pControl );
CDmElement *CollapseControlGroup( CDmElement *pControlGroup, CDmElement *pRootGroup );

bool Update17( CDmElement *pElement )
{
	const static CUtlSymbolLarge symDmeAnimationSet = g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge symDmePreset = g_pDataModel->GetSymbol( "DmePreset" );

	// If the element is an animation set iterate through its controls and unify the transform controls.
	// Note that while the transform controls themselves would be visited they may be visited from the
	// animation set or control group containing them, making removing the controls from the animation set
	// or control group problematic since the calling function is in the middle of walking the array that
	// will be modified.
	if ( pElement->GetType() == symDmeAnimationSet )
	{
		CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pControlsAttr )
		{
			// Make a copy of the controls list. A copy is must be made
			// since the actual list may be modified during iteration.
			CDmrElementArray<> controls( pControlsAttr );
			int nNumControls = controls.Count();

			CUtlVector< DmElementHandle_t > controlHandles;
			controlHandles.SetSize( nNumControls );
			for ( int iControl = 0; iControl < nNumControls; ++iControl )
			{
				controlHandles[ iControl ] = controls[ iControl ]->GetHandle();
			}

			// Iterate through the controls, unifying and of the transform controls which contain only a
			// position or rotation with the corresponding control containing the other component.
			for ( int iControl = 0; iControl < nNumControls; ++iControl )
			{
				CDmElement *pControl = g_pDataModel->GetElement( controlHandles[ iControl ] );
				if ( pControl )
				{
					UnifyTransformControl( pControl, pElement );
				}
			}
		}
	}
	else if ( pElement->GetType() == symDmePreset )
	{
		// For presets merge all control value elements that share the same base name
		CDmAttribute *pControlValuesAttr = pElement->GetAttribute( "controlValues", AT_ELEMENT_ARRAY );
		if ( pControlValuesAttr )
		{
			CDmrElementArray<> controlValueElements( pControlValuesAttr );
			int nControlValues = controlValueElements.Count();
			if ( nControlValues > 0 )
			{
				CUtlDict< CDmElement*, int > mergedControls;
				CUtlVector< DmElementHandle_t > validControlValues( 0, nControlValues );

				for ( int iValue = 0; iValue < nControlValues; ++iValue )
				{
					CDmElement *pControlValue = controlValueElements[ iValue ];
					if ( pControlValue == NULL )
						continue;

					bool bPos = pControlValue->HasAttribute( "valuePosition" );
					bool bRot = pControlValue->HasAttribute( "valueOrientation" );
					const char *pComponentStart = NULL;
					const char *pName = pControlValue->GetName();

					// Must have a position or rotation value, but not both
					if ( bPos && !bRot )
					{
						pComponentStart = V_strstr( pName, " - pos" );
					}
					else if ( bRot && !bPos )
					{
						pComponentStart = V_strstr( pName, " - rot" );
					}

					// If the control value will not be merged,
					// add it to the list of valid control values.
					if ( pComponentStart == NULL )
					{
						validControlValues.AddToTail( pControlValue->GetHandle() );
						continue;
					}

					// Construct the base name by removing the component extension
					char baseName[ 256 ];
					V_strncpy( baseName, pName, 256 );
					baseName[ pComponentStart - pName ] = 0;

					// Find or create the new control value
					CDmElement *pNewControlValue = NULL;
					int nIndex = mergedControls.Find( baseName );
					if ( nIndex == mergedControls.InvalidIndex() )
					{
						pNewControlValue = CreateElement< CDmElement >( "DmElement", baseName, pElement->GetFileId() );
						pNewControlValue->SetParity( pElement->GetParity() );
						nIndex = mergedControls.Insert( baseName, pNewControlValue );
						validControlValues.AddToTail( pNewControlValue->GetHandle() );
					}
					else
					{
						pNewControlValue = mergedControls.Element( nIndex );
					}

					// Copy the value from the old control value element
					if ( pNewControlValue )
					{
						if ( bPos )
						{
							Vector position = pControlValue->GetValue< Vector >( "valuePosition" );
							pNewControlValue->SetValue< Vector >( "valuePosition", position );
						}
						else
						{
							Quaternion orentation = pControlValue->GetValue< Quaternion >( "valueOrientation" );
							pNewControlValue->SetValue< Quaternion >( "valueOrientation", orentation );
						}
					}
				}

				// Replace the original list of control elements with
				// the list of currently valid control elements.
				controlValueElements = validControlValues;
			}
		}
	}

	return true;
}

void UnifyTransformControl( CDmElement *pTransformControl, CDmElement *pAnimSet )
{
	const static CUtlSymbolLarge symDmeTransformControl = g_pDataModel->GetSymbol( "DmeTransformControl" );
	const static CUtlSymbolLarge symDmeControlGroup = g_pDataModel->GetSymbol( "DmeControlGroup" );
	const static CUtlSymbolLarge symDmeAnimationSet = g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge symDmeRigAnimSetElements = g_pDataModel->GetSymbol( "DmeRigAnimSetElements" );
	const static CUtlSymbolLarge symDmeRig = g_pDataModel->GetSymbol( "DmeRig" );
	const static CUtlSymbolLarge symControls = g_pDataModel->GetSymbol( "controls" );
	const static CUtlSymbolLarge symElementList = g_pDataModel->GetSymbol( "elementList" );
	const static CUtlSymbolLarge symDisplaySetElements = g_pDataModel->GetSymbol( "displaySetElements" );

	if ( pTransformControl->GetType() == symDmeTransformControl )
	{
		const char *baseName = pTransformControl->GetValueString( "baseName" );

		// Find the control group containing the specified control
		CDmElement *pTransformControlGroup = NULL;
		CUtlVector< CDmElement* > controlGroups;
		FindReferringElements( controlGroups, pTransformControl, symControls );
		int nNumGroups = controlGroups.Count();
		for ( int iGroup = 0; iGroup < nNumGroups; ++iGroup )
		{
			CDmElement *pGroup = controlGroups[ iGroup ];
			if ( pGroup->GetType() == symDmeControlGroup )
			{
				pTransformControlGroup = pGroup;
				break;
			}
		}

		Assert( pTransformControlGroup );
		if ( pTransformControlGroup == NULL )
			return;

		// Find the position or orientation control that corresponds to the given control
		CDmElement *pPositionControl = NULL;
		CDmElement *pOrientationControl = NULL;
		CDmElement *pPositionChannel = NULL;
		CDmElement *pOrientationChannel = NULL;

		CDmAttribute *pControlsAttr = pTransformControlGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pControlsAttr )
		{
			CDmrElementArray<> controlList( pControlsAttr );
			for ( int iControl = 0; iControl < controlList.Count(); ++iControl )
			{
				CDmElement *pControl = controlList[ iControl ];
				if ( pControl->HasAttribute( "position", AT_ELEMENT ) )
				{
					pPositionControl = pControl;
					pPositionChannel = pPositionControl->GetValueElement< CDmElement >( "position" );
				}
				else if ( pControl->HasAttribute( "orientation", AT_ELEMENT ) )
				{
					pOrientationControl = pControl;
					pOrientationChannel = pOrientationControl->GetValueElement< CDmElement >( "orientation" );
				}

				if ( pPositionControl && pOrientationControl )
					break;
			}
		}

		// Make  sure we got the correct element
		if ( ( pPositionControl != pTransformControl ) && ( pOrientationControl != pTransformControl ) )
			return;

		// Get the position and value controls from the old element
		Vector position = pPositionControl ? pPositionControl->GetValue< Vector >( "valuePosition", vec3_origin ) : vec3_origin;
		Quaternion orientation = pOrientationControl ? pOrientationControl->GetValue< Quaternion >( "valueOrientation", quat_identity ) : quat_identity;

		// Create the new transform control
		CDmElement *pNewTransformControl = CreateElement< CDmElement >( "DmeTransformControl", baseName, pTransformControl->GetFileId() );
		pNewTransformControl->SetParity( pTransformControl->GetParity() );
		pNewTransformControl->SetValue( "positionChannel", pPositionChannel );
		pNewTransformControl->SetValue( "orientationChannel", pOrientationChannel );
		pNewTransformControl->SetValue( "valuePosition", position );
		pNewTransformControl->SetValue( "valueOrientation", orientation );

		if ( pPositionChannel )
		{
			pPositionChannel->SetValue( "fromElement", pNewTransformControl );
		}

		if ( pOrientationChannel )
		{
			pOrientationChannel->SetValue( "fromElement", pNewTransformControl );
		}

		// If the old transform control was in a display set, add the new one to the display set as well
		CDmElement *pDisplaySet = pTransformControl->GetValueElement< CDmElement >( "displaySet" );
		if ( pDisplaySet )
		{
			pNewTransformControl->SetValue( "displaySet", pDisplaySet );
		}

		// Add the new control to the animation set
		CDmAttribute *pAnimSetControlsAttr = pAnimSet->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pAnimSetControlsAttr )
		{
			CDmrElementArray<> animSetControls( pAnimSetControlsAttr );
			animSetControls.AddToTail( pNewTransformControl );
		}

		// If the old transform control was part of a rig, add the new transform control to the rig as well
		CUtlVector< CDmElement * > animSetRigList;
		FindReferringElements( animSetRigList, pTransformControl, symElementList );
		int nNumAnimSetRigs = animSetRigList.Count();
		for ( int iAnimSetRig = 0; iAnimSetRig < nNumAnimSetRigs; ++iAnimSetRig )
		{
			CDmElement *pAnimElements = animSetRigList[ iAnimSetRig ];
			if ( ( pAnimElements != NULL ) && ( pAnimElements->GetType() == symDmeRigAnimSetElements ) &&
				 ( pAnimElements->GetValueElement< CDmElement >( "animationSet" ) == pAnimSet ) )
			{
				CDmAttribute *pElementsAttr = pAnimElements->GetAttribute( "elementList", AT_ELEMENT_ARRAY );
				if ( pElementsAttr )
				{
					CDmrElementArray<> elementsList( pElementsAttr );
					elementsList.AddToTail( pNewTransformControl );
				}
			}
		}

		CUtlVector< CDmElement * > rigList;
		FindReferringElements( rigList, pTransformControl, symDisplaySetElements );
		int nNumRigs = rigList.Count();
		for ( int iRig = 0; iRig < nNumRigs; ++iRig )
		{
			CDmElement *pRig = rigList[ iRig ];
			if ( ( pRig != NULL ) && ( pRig->GetType() == symDmeRig ) )
			{
				CDmAttribute *pDisplaySetElementsAttr = pRig->GetAttribute( "displaySetElements", AT_ELEMENT_ARRAY );
				if ( pDisplaySetElementsAttr )
				{
					CDmrElementArray<> displaySetElements( pDisplaySetElementsAttr );
					displaySetElements.AddToTail( pNewTransformControl );
				}
			}
		}

		// Move all the controls and children from the transform control group up to
		// its parent and remove the the transform control group from its parent.
		CDmElement *pParentControlGroup = pAnimSet->GetValueElement< CDmElement >( "rootControlGroup" );
		if ( pTransformControlGroup )
		{
			pParentControlGroup = CollapseControlGroup( pTransformControlGroup, pParentControlGroup );
		}

		// Add the new control to parent control group
		if ( pParentControlGroup )
		{
			CDmAttribute *pParentControlsAttr = pParentControlGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
			if ( pParentControlsAttr )
			{
				CDmrElementArray<> parentControls( pParentControlsAttr );
				parentControls.AddToTail( pNewTransformControl );
			}
		}

		// Remove the old controls from all animation sets and
		// control groups they belong to, and then destroy them.
		DestroyControl( pPositionControl );
		DestroyControl( pOrientationControl );
	}
}

void DestroyControl( CDmElement *pControl )
{
	const static CUtlSymbolLarge symControls = g_pDataModel->GetSymbol( "controls" );
	const static CUtlSymbolLarge symElementList = g_pDataModel->GetSymbol( "elementList" );
	const static CUtlSymbolLarge symDisplaySetElements = g_pDataModel->GetSymbol( "displaySetElements" );

	if ( pControl == NULL )
		return;

	// Remove the control from any animation sets and control groups it belongs to
	CUtlVector< CDmElement* > ownerList;
	FindReferringElements( ownerList, pControl, symControls );
	Assert( ownerList.Count() == 2 );

	for ( int iElement = 0; iElement < ownerList.Count(); ++iElement )
	{
		CDmElement *pElement = ownerList[ iElement ];
		if ( pElement == NULL )
			continue;

		CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pControlsAttr == NULL )
			continue;

		CDmrElementArray<> controlList( pControlsAttr );
		int nIndex = controlList.Find( pControl );
		if ( nIndex != controlList.InvalidIndex() )
		{
			controlList.Remove( nIndex );
		}
	}

	// Remove the control from any rigs it is a part of
	CUtlVector< CDmElement * > animSetRigList;
	FindReferringElements( animSetRigList, pControl, symElementList );
	int nAimSetRigs = animSetRigList.Count();
	for ( int iAnimSetRig = 0; iAnimSetRig < nAimSetRigs; ++iAnimSetRig )
	{
		CDmElement *pElement = animSetRigList[ iAnimSetRig ];
		if ( pElement == NULL )
			continue;

		CDmAttribute *pElementsAttr = pElement->GetAttribute( "elementList", AT_ELEMENT_ARRAY );
		if ( pElementsAttr == NULL )
			continue;

		CDmrElementArray<> elementsList( pElementsAttr );
		int nIndex = elementsList.Find( pControl );
		if ( nIndex != elementsList.InvalidIndex() )
		{
			elementsList.Remove( nIndex );
		}
	}

	CUtlVector< CDmElement * > rigList;
	FindReferringElements( rigList, pControl, symDisplaySetElements );
	int nNumRigs = rigList.Count();
	for ( int iRig = 0; iRig < nNumRigs; ++iRig )
	{
		CDmElement *pRig = rigList[ iRig ];
		if ( pRig == NULL )
			continue;

		CDmAttribute *pDisplaySetElementsAttr = pRig->GetAttribute( "displaySetElements", AT_ELEMENT_ARRAY );
		if ( pDisplaySetElementsAttr == NULL )
			continue;

		CDmrElementArray<> displaySetElements( pDisplaySetElementsAttr );
		int nIndex = displaySetElements.Find( pControl );
		if ( nIndex != displaySetElements.InvalidIndex() )
		{
			displaySetElements.Remove( nIndex );
		}
	}

	// Destroy the control even if something is holding on to it
	DestroyElement( pControl );
}

CDmElement *CollapseControlGroup( CDmElement *pControlGroup, CDmElement *pRootGroup )
{
	const static CUtlSymbolLarge symDmeControlGroup = g_pDataModel->GetSymbol( "DmeControlGroup" );
	const static CUtlSymbolLarge symChildren = g_pDataModel->GetSymbol( "children" );

	if ( pControlGroup->GetType() != symDmeControlGroup )
		return NULL;

	if ( pRootGroup && ( pRootGroup->GetType() != symDmeControlGroup ) )
		return NULL;

	// Find the parent control group of the specified control group
	CDmElement *pParentControlGroup = NULL;
	CUtlVector< CDmElement* > parentList;
	FindReferringElements( parentList, pControlGroup, symChildren );
	for ( int i = 0; i < parentList.Count(); ++i )
	{
		CDmElement *pElement = parentList[ i ];
		if ( pElement->GetType() == symDmeControlGroup )
		{
			pParentControlGroup = pElement;
			break;
		}
	}

	Assert( pParentControlGroup );

	// If no parent was found, use the supplied root group
	if ( pParentControlGroup == NULL )
	{
		pParentControlGroup = pRootGroup;
	}

	if ( ( pParentControlGroup != NULL ) && ( pParentControlGroup != pControlGroup ) )
	{
		// Move add the controls of the child to its parent
		CDmAttribute *pChildControlsAttr = pControlGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		CDmAttribute *pParentControlsAttr = pParentControlGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pChildControlsAttr && pParentControlsAttr )
		{
			CDmrElementArray<> childControls( pChildControlsAttr );
			CDmrElementArray<> parentControls( pParentControlsAttr );
			for ( int iControl = 0; iControl < childControls.Count(); ++iControl )
			{
				parentControls.AddToTail( childControls[ iControl ] );
			}
			childControls.RemoveAll();
		}

		// Move the children of the child to its parent
		CDmAttribute *pChildChildrenAttr = pControlGroup->GetAttribute( "children", AT_ELEMENT_ARRAY );
		CDmAttribute *pParentChildrenAttr = pParentControlGroup->GetAttribute( "children", AT_ELEMENT_ARRAY );

		if ( pParentChildrenAttr )
		{
			CDmrElementArray<> parentChildren( pParentChildrenAttr );

			// Move the children from the control group to its parent
			if ( pChildChildrenAttr )
			{
				CDmrElementArray<> childChildren( pChildChildrenAttr );
				for ( int iChild = 0; iChild < childChildren.Count(); ++iChild )
				{
					parentChildren.AddToTail( childChildren[ iChild ] );
				}
				childChildren.RemoveAll();
			}

			// Remove the control group from its parent
			int nGroupIndex = parentChildren.Find( pControlGroup );
			parentChildren.Remove( nGroupIndex );
		}
	}

	return pParentControlGroup;
}

// Update16 -- remove settings from sfm sessions
bool Update16( CDmElement *pElement )
{
	// remove manipulatorsettings from sfm sessions
	if ( !V_strcmp( pElement->GetName(), "sessionSettings" ) )
	{
		pElement->RemoveAttribute( "manipulatorSettings" );

		CDmElement *pRenderSettings = pElement->GetValueElement< CDmElement >( "renderSettings" );
		if ( pRenderSettings )
		{
			CDmElement *pMovieSettings = pRenderSettings->GetValueElement< CDmElement >( "MovieSettings" );
			pElement->SetValue( "movieSettings", pMovieSettings ); // lower-casing first letter to match other settings
			pRenderSettings->RemoveAttribute( "MovieSettings" );

			if ( pMovieSettings )
			{
				pMovieSettings->RemoveAttribute( "shutterSpeed" );
				pMovieSettings->RemoveAttribute( "framerate" );
				pMovieSettings->RemoveAttribute( "nLayoffState" );
				pMovieSettings->RemoveAttribute( "startTime" );
				pMovieSettings->RemoveAttribute( "endTime" );
				pMovieSettings->RemoveAttribute( "renderOnlySelectedClips" );

				pMovieSettings->InitValue( "videoTarget", 2 ); // TGA
				pMovieSettings->InitValue( "audioTarget", 2 ); // WAV
				pMovieSettings->InitValue( "stereoscopic", false );
				pMovieSettings->InitValue( "stereoSingleFile", false );
				pMovieSettings->InitValue( "clearDecals", false );
				pMovieSettings->InitValue( "width", 1280 );
				pMovieSettings->InitValue( "height", 720 );
				pMovieSettings->InitValue( "filename", "" );
			}

			CDmElement *pPosterSettings = pRenderSettings->GetValueElement< CDmElement >( "PosterSettings" );
			pElement->SetValue( "posterSettings", pPosterSettings ); // lower-casing first letter to match other settings
			pRenderSettings->RemoveAttribute( "PosterSettings" );

			pRenderSettings->RemoveAttribute( "syncEngineFramerate" );
			pRenderSettings->RemoveAttribute( "showFocalPlane" );
			pRenderSettings->RemoveAttribute( "showCameraFrustum" );
			pRenderSettings->RemoveAttribute( "showViewTargets" );
			pRenderSettings->RemoveAttribute( "StereoFlip" );

			pRenderSettings->InitValue( "ambientOcclusionMode", 0 );		// Perform ambient occlusion and/or outlining
			pRenderSettings->InitValue( "showAmbientOcclusion", 0 );		// Show just AO/outlining (vs combined with normal scene rendering)
			pRenderSettings->InitValue( "drawGameRenderablesMask", 0xd8 );	// RENDER_GAME_ROPES | RENDER_GAME_BEAMS | RENDER_GAME_WORLD_BRUSHES | RENDER_GAME_WORLD_PROPS
			pRenderSettings->InitValue( "drawToolRenderablesMask", 0x0f );	// RENDER_TOOL_PUPPETS | RENDER_TOOL_EFFECTS | RENDER_TOOL_PARTICLE_SYSTEMS | RENDER_TOOL_LIGHTS
			pRenderSettings->InitValue( "toneMapScale", 1.0f );
		}
	}

	return true;
}

// Remove the "parentConstraint" attribute from all dmeDag elements and the
// "embeddedTarget" attribute from all operators.
bool IsRigConstraint( CDmElement *pElement )
{
	const static CUtlSymbolLarge rigSym[] =
	{
		g_pDataModel->GetSymbol( "DmeRigPointConstraintOperator" ),
		g_pDataModel->GetSymbol( "DmeRigOrientConstraintOperator" ),
		g_pDataModel->GetSymbol( "DmeRigAimConstraintOperator" ),
		g_pDataModel->GetSymbol( "DmeRigRotationConstraintOperator" ),
		g_pDataModel->GetSymbol( "DmeRigParentConstraintOperator" ),
		g_pDataModel->GetSymbol( "DmeRigIKConstraintOperator" ),
	};

	static const int nNumConstraintOperators = sizeof( rigSym ) / sizeof( CUtlSymbolLarge );

	CUtlSymbolLarge elementSym = pElement->GetType();
	for ( int i = 0; i < nNumConstraintOperators; ++i )
	{
		if ( elementSym == rigSym[ i ] )
			return true;
	}

	return false;
}

bool Update15( CDmElement *pElement )
{
	const static CUtlSymbolLarge dmeDagSym = g_pDataModel->GetSymbol( "DmeDag" );

	if ( pElement->GetType() == dmeDagSym )
	{
		pElement->RemoveAttribute( "parentConstraint" );
	}
	else if ( IsRigConstraint( pElement ) )
	{
		pElement->RemoveAttribute( "embeddedTarget" );
	}

	return true;
}

void CollectAnimSetsAndDestroyAnimSetGroups( CDmrElementArray< CDmElement > animationSets, CDmElement *pSrcAnimSetGroup )
{
	if ( !pSrcAnimSetGroup || !animationSets.IsValid() )
		return;

	CDmrElementArray< CDmElement > srcAnimationSets( pSrcAnimSetGroup, "animationSets" );
	int nAnimationSets = srcAnimationSets.IsValid() ? srcAnimationSets.Count() : 0;
	for ( int i = 0; i < nAnimationSets; ++i )
	{
		if ( CDmElement *pAnimSet = srcAnimationSets[ i ] )
		{
			animationSets.AddToTail( pAnimSet );
		}
	}

	CDmrElementArray< CDmElement > children( pSrcAnimSetGroup, "children" );
	int nChildren = children.IsValid() ? children.Count() : 0;
	for ( int i = 0; i < nChildren; ++i )
	{
		CollectAnimSetsAndDestroyAnimSetGroups( animationSets, children[ i ] );
	}

	DestroyElement( pSrcAnimSetGroup );
}

// eliminating animationsetsgroups and moving their animationsets to a single filmclip attribute
bool Update14( CDmElement *pElement )
{
	const static CUtlSymbolLarge filmClipSym = g_pDataModel->GetSymbol( "DmeFilmClip" );
	if ( pElement->GetType() == filmClipSym )
	{
		CDmrElementArray< CDmElement > animationSets( pElement, "animationSets", true );

		CDmrElementArray< CDmElement > animSetGroups( pElement, "animationSetGroups" );
		int nAnimSetGroups = animSetGroups.IsValid() ? animSetGroups.Count() : 0;
		for ( int i = 0; i < nAnimSetGroups; ++i )
		{
			CollectAnimSetsAndDestroyAnimSetGroups( animationSets, animSetGroups[ i ] );
		}

		pElement->RemoveAttribute( "animationSetGroups" );
	}

	return true;
}

// this works because both CDmeFilmClip and CDmeAnimationSetGroup have an element array called "bookmarks" that is being replaced
void CreateBookmarkSetAndMoveBookmarks( CDmrElementArray< CDmElement > bookmarkSets, CDmElement *pSrcElement, const char *pSetName, bool bForce )
{
	CDmAttribute *pSrcBookmarks = pSrcElement->GetAttribute( "bookmarks" );
	CDmrElementArray< CDmElement > srcBookmarks( pSrcBookmarks );
	if ( bForce || ( srcBookmarks.IsValid() && srcBookmarks.Count() > 0 ) )
	{
		CDmElement *pOwner = bookmarkSets.GetAttribute()->GetOwner();

		CDmElement *pBookmarkSet = CreateElement< CDmElement >( "DmeBookmarkSet", pSetName, pOwner->GetFileId() );
		pBookmarkSet->SetParity( pOwner->GetParity() );

		bookmarkSets.AddToTail( pBookmarkSet );

		CDmAttribute *pSetBookmarks = pBookmarkSet->AddAttribute( "bookmarks", AT_ELEMENT_ARRAY );
		if ( pSrcBookmarks && pSetBookmarks )
		{
			pSetBookmarks->SetValue( pSrcBookmarks );
		}
	}
	pSrcElement->RemoveAttributeByPtr( pSrcBookmarks );
}

void MoveBookmarksFromAnimSetGroups( CDmrElementArray< CDmElement > bookmarkSets, CDmElement *pSrcAnimSetGroup )
{
	if ( !pSrcAnimSetGroup || !bookmarkSets.IsValid() )
		return;

	CreateBookmarkSetAndMoveBookmarks( bookmarkSets, pSrcAnimSetGroup, pSrcAnimSetGroup->GetName(), false );

	CDmrElementArray< CDmElement > children( pSrcAnimSetGroup, "children" );
	int nChildren = children.IsValid() ? children.Count() : 0;
	for ( int i = 0; i < nChildren; ++i )
	{
		MoveBookmarksFromAnimSetGroups( bookmarkSets, children[ i ] );
	}
}

// moving bookmarks from filmclip and animsetionsetgroup to bookmarkset
// and adding bookmarksets to filmclip to allow named sets of bookmarks,
// and in preparation for removing animationsetgroups
bool Update13( CDmElement *pElement )
{
	const static CUtlSymbolLarge filmClipSym = g_pDataModel->GetSymbol( "DmeFilmClip" );
	if ( pElement->GetType() == filmClipSym )
	{
		pElement->SetValue( "activeBookmarkSet", 0 );
		CDmrElementArray< CDmElement > bookmarkSets( pElement, "bookmarkSets", true );
		CreateBookmarkSetAndMoveBookmarks( bookmarkSets, pElement, "default set", true );

		CDmrElementArray< CDmElement > animSetGroups( pElement, "animationSetGroups" );
		int nAnimSetGroups = animSetGroups.IsValid() ? animSetGroups.Count() : 0;
		for ( int i = 0; i < nAnimSetGroups; ++i )
		{
			MoveBookmarksFromAnimSetGroups( bookmarkSets, animSetGroups[ i ] );
		}
	}

	return true;
}

// moving displaySets from animsetionsetgroup to animationset in preparation for removing animationsetgroups
bool Update12( CDmElement *pElement )
{
	const static CUtlSymbolLarge animSetSym = g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge animSetGroupSym = g_pDataModel->GetSymbol( "DmeAnimationSetGroup" );
	const static CUtlSymbolLarge controlDisplaySetSym = g_pDataModel->GetSymbol( "DmeControlDisplaySet" );
	const static CUtlSymbolLarge displaySetSym = g_pDataModel->GetSymbol( "displaySet" );
	const static CUtlSymbolLarge controlsSym = g_pDataModel->GetSymbol( "controls" );

	if ( pElement->GetType() == animSetGroupSym )
	{
		CDmrElementArray<> displaySets( pElement, "displaySets" );
		int nDisplaySets = displaySets.IsValid() ? displaySets.Count() : 0;
		for ( int di = 0; di < nDisplaySets; ++di )
		{
			CDmElement *pDisplaySet = displaySets[ di ];
			if ( !pDisplaySet )
				continue;

			// gather controls referencing the displayset
			CUtlVector< CDmElement* > controls;
			FindReferringElements( controls, pDisplaySet, displaySetSym );

			// gather animationsets referencing those controls
			CUtlVector< CDmElement* > animsets;
			int nControls = controls.Count();
			for ( int ci = 0; ci < nControls; ++ci )
			{
				CDmElement *pControl = controls[ ci ];
				if ( !pControl )
					continue;

				CUtlVector< CDmElement* > controlGroupsAndAnimationSets;
				FindReferringElements( controlGroupsAndAnimationSets, pControl, controlsSym );

				int nControlGroupsAndAnimationSets = controlGroupsAndAnimationSets.Count();
				for ( int gi = 0; gi < nControlGroupsAndAnimationSets; ++gi )
				{
					CDmElement *pGroupOrAnimSet = controlGroupsAndAnimationSets[ gi ];
					if ( !pGroupOrAnimSet || pGroupOrAnimSet->GetType() != animSetSym )
						continue;

					if ( animsets.Find( pGroupOrAnimSet ) < 0 )
					{
						animsets.AddToTail( pGroupOrAnimSet );
					}
				}
			}

			// add displayset to gathered animationsets (and replace controls' displaysets with new copy if needed)
			int nAnimSets = animsets.Count();
			for ( int ai = 0; ai < nAnimSets; ++ai )
			{
				CDmElement *pAnimSet = animsets[ ai ];
				CDmElement *pNewDisplaySet = pDisplaySet;
				if ( ai > 0 )
				{
					// copy displayset
					pNewDisplaySet = pDisplaySet->Copy( TD_SHALLOW );
					pNewDisplaySet->SetParity( pElement->GetParity(), TD_SHALLOW );

					// replace displayset with copy on each control in this animationset
					CDmrElementArray<> controls( pAnimSet, "controls" );
					int nControls = controls.Count();
					for ( int ci = 0; ci < nControls; ++ci )
					{
						CDmElement *pControl = controls[ ci ];
						if ( !pControl )
							continue;

						if ( pControl->GetValueElement< CDmElement >( "displaySet" ) == pDisplaySet )
						{
							pControl->SetValue( "displaySet", pNewDisplaySet );
						}
					}
				}

				// add displayset (or copy) to animationset
				CDmrElementArray<> newDisplaySets( pAnimSet, "displaySets", true );
				newDisplaySets.AddToTail( pNewDisplaySet );
			}
		}

		// remove displaysets attribute from animationsetgroup
		pElement->RemoveAttribute( "displaySets" );
	}

	return true;
}

// Purpose: Remove redundant attributes in animationset controls to save memory
bool Update11( CDmElement *pElement )
{
	const static CUtlSymbolLarge animSetSym = g_pDataModel->GetSymbol( "DmeAnimationSet" );

	if ( pElement->GetType() == animSetSym )
	{
		CDmrElementArray<> controls( pElement, "controls" );
		Assert( controls.IsValid() );
		if ( !controls.IsValid() )
			return false;

		int nControls = controls.Count();
		for ( int i = 0; i < nControls; ++i )
		{
			CDmElement *pControl = controls[ i ];
			if ( !pControl )
				continue;

			if ( pControl->HasAttribute( "combo" ) )
			{
				if ( pControl->GetValue< bool >( "combo" ) )
				{
					float flDefaultLeftValue  = pControl->GetValue< float >( "defaultLeftValue" );
					float flDefaultRightValue = pControl->GetValue< float >( "defaultRightValue" );
					pControl->SetValue( "defaultValue", 0.5f * ( flDefaultLeftValue + flDefaultRightValue ) );
					pControl->RemoveAttribute( "value" );
					pControl->RemoveAttribute( "channel" );
				}
				else
				{
					pControl->RemoveAttribute( "rightValue" );
					pControl->RemoveAttribute( "leftValue" );
					pControl->RemoveAttribute( "rightvaluechannel" );
					pControl->RemoveAttribute( "leftvaluechannel" );
				}
				pControl->RemoveAttribute( "combo" );
				pControl->RemoveAttribute( "defaultLeftValue" );
				pControl->RemoveAttribute( "defaultRightValue" );
			}
			else
			{
				if ( !pControl->GetValue< bool >( "isPosition" ) )
				{
					pControl->RemoveAttribute( "position" );
					pControl->RemoveAttribute( "valuePosition" );
				}
				if ( !pControl->GetValue< bool >( "isOrientation" ) )
				{
					pControl->RemoveAttribute( "orientation" );
					pControl->RemoveAttribute( "valueOrientation" );
				}
				pControl->RemoveAttribute( "transform" );
				pControl->RemoveAttribute( "isPosition" );
				pControl->RemoveAttribute( "isOrientation" );
			}
		}
	}

	return true;
}

// Purpose: Create a control group with the specified name and add it to the
// provided parent if the parent is a DmeControlGroup.
CDmElement *CreateControlGroup( const char *pchName, CDmElement *pParent )
{
	const static CUtlSymbolLarge controlGroupSym = g_pDataModel->GetSymbol( "DmeControlGroup" );

	CDmElement *pGroup = CreateElement< CDmElement >( "DmeControlGroup", pchName, pParent->GetFileId() );
	pGroup->SetParity( pParent->GetParity() );

	pGroup->AddAttribute( "children",		AT_ELEMENT_ARRAY );
	pGroup->AddAttribute( "controls",		AT_ELEMENT_ARRAY );
	pGroup->SetValue< Color >( "groupColor", Color( 200, 200, 200, 255 ) );
	pGroup->SetValue< Color >( "controlColor", Color( 200, 200, 200, 255 ) );

	if ( pParent->GetType() == controlGroupSym )
	{
		CDmAttribute *pChildrenAttr = pParent->GetAttribute( "children", AT_ELEMENT_ARRAY );
		CDmrElementArray<> childList( pChildrenAttr );
		childList.AddToTail( pGroup );
	}

	return pGroup;
}

#define MULTI_CONTROL_FORMAT_STRING "multi_%s"

bool ConvertControlGroup_R( CDmrElementArray<> &animSetControls, CDmElement *pControlGroup )
{
	if ( !pControlGroup )
		return true;

	CDmrElementArray<> children( pControlGroup, "children" );
	int nChildren = children.Count();
	for ( int i = 0; i < nChildren; ++i )
	{
		ConvertControlGroup_R( animSetControls, children[ i ] );
	}

	CDmrElementArray<> controls( pControlGroup, "controls" );
	Assert( controls.IsValid() );
	if ( !controls.IsValid() )
		return true;

	int nControls = controls.Count();
	for ( int i = 0; i < nControls; ++i )
	{
		CDmElement *pControl = controls[ i ];
		if ( !pControl )
			continue;

		if ( pControl->GetValue< bool >( "multi" ) )
		{
			CDmElement *pMultiChannel = pControl->GetValueElement< CDmElement >( "multilevelchannel" );
			Assert( pMultiChannel );
			if ( pMultiChannel )
			{
				// create new multi control
				char multiName[ 256 ];
				V_snprintf( multiName, sizeof( multiName ), MULTI_CONTROL_FORMAT_STRING, pControl->GetName() );
				CDmElement *pMultiControl = CreateElement< CDmElement >( "DmElement", multiName, pControl->GetFileId() );
				pMultiControl->SetParity( pControlGroup->GetParity() );
				pMultiControl->SetValue( "value",			pControl->GetValue< float >( "multilevel" ) );
				pMultiControl->SetValue( "defaultValue",	pControl->GetValue< float >( "defaultMultilevel" ) );
				pMultiControl->SetValue( "channel",		pMultiChannel );

				// add multi control to animset's flat control list
				int j = animSetControls.Find( pControl );
				if ( j < 0 )
					return false;

				// WARNING: this isn't a great practice, since it's technically possible that we're iterating controls,
				// but some control's channel would have to reference a control group (with multi controls) for that to be true
				animSetControls.Remove( j );
				animSetControls.InsertBefore( 0, pControl );
				animSetControls.InsertBefore( 1, pMultiControl );

				// update channel to point to new element
				pMultiChannel->SetValue( "fromElement", pMultiControl );
				pMultiChannel->SetValue( "fromAttribute", "value" );

				// create control group to contain both controls and add it to the parent group
				CDmElement *pNewControlGroup = CreateControlGroup( pControl->GetName(), pControlGroup );

				// add original and multi control to new control group
				CDmrElementArray<> newControls( pNewControlGroup, "controls" );
				if ( !newControls.IsValid() )
					return false;

				newControls.AddToTail( pControl );
				newControls.AddToTail( pMultiControl );

				// remove original control from its old parent group
				controls.Remove( i );
				--i; --nControls;
			}
		}

		// remove multi-related attributes from original control
		pControl->RemoveAttribute( "multi" );
		pControl->RemoveAttribute( "multilevel" );
		pControl->RemoveAttribute( "defaultMultilevel" );
		pControl->RemoveAttribute( "multilevelchannel" );
	}

	return true;
}

// Purpose: Convert multi controls into a control group and two controls
bool Update10( CDmElement *pElement )
{
	const static CUtlSymbolLarge animSetSym = g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge presetSym = g_pDataModel->GetSymbol( "DmePreset" );

	if ( pElement->GetType() == animSetSym )
	{
		CDmrElementArray<> controls( pElement, "controls" );
		Assert( controls.IsValid() );
		if ( !controls.IsValid() )
			return false;

		ConvertControlGroup_R( controls, pElement->GetValueElement< CDmElement >( "rootControlGroup" ) );
	}
	else if ( pElement->GetType() == presetSym )
	{
		CDmrElementArray<> controlValues( pElement, "controlValues" );
		Assert( controlValues.IsValid() );
		if ( !controlValues.IsValid() )
			return true;

		int nControls = controlValues.Count();
		for ( int ci = 0; ci < nControls; ++ci )
		{
			CDmElement *pControlValue = controlValues[ ci ];
			if ( !pControlValue )
				continue;

			CDmAttribute *pAttr = pControlValue->GetAttribute( "multilevel", AT_FLOAT );
			if ( !pAttr )
				continue;

			// create new multi control value
			char multiName[ 256 ];
			V_snprintf( multiName, sizeof( multiName ), MULTI_CONTROL_FORMAT_STRING, pControlValue->GetName() );
			CDmElement *pMultiControlValue = CreateElement< CDmElement >( "DmElement", multiName, pControlValue->GetFileId() );
			pMultiControlValue->SetParity( pElement->GetParity() );
			pMultiControlValue->SetValue( "value", pAttr->GetValue< float >() );
			controlValues.InsertBefore( ci + 1, pMultiControlValue );
			++ci; ++nControls;

			// remove multilevel attribute from original control
			pControlValue->RemoveAttributeByPtr( pAttr );
		}
	}

	return true;
}

// Update9 -- AnimationSet DmElement to DmeControlGroup conversion

// Purpose: Find the control in the animation set with the specified name
CDmElement *FindAnimSetControl( CDmElement* pAnimSet, const char *pchControlName )

// Purpose: Recursively convert the generic DmElement selection group and its
// children into a DmeControlGroup
void ConvertSelectionGroup_R( CDmElement* pAnimSet, CDmElement *pParentControlGroup, CDmElement *pSelectionElement )
{
	if ( ( pAnimSet == NULL ) || ( pParentControlGroup == NULL ) || ( pSelectionElement == NULL ) )
		return;

	bool isGroup = pSelectionElement->GetValue< bool >( "isGroup" );
	if ( isGroup )
	{
		// Create a control group and add it to the parent group.
		CDmElement *pControlGroup = CreateControlGroup( pSelectionElement->GetName(), pParentControlGroup );
		if ( pControlGroup )
		{
			// Copy the tree color of the element to the group color of the control group
			if ( pSelectionElement->HasAttribute( "treeColor", AT_COLOR ) )
			{
				pControlGroup->SetValue< Color >( "groupColor", pSelectionElement->GetValue< Color >( "treeColor" ) );
			}

			// Process the children of the selection element group
			CDmAttribute *pSelectionControlsAttr = pSelectionElement->GetAttribute( "controls" );
			if ( pSelectionControlsAttr )
			{
				CDmrElementArray<> children( pSelectionControlsAttr );
				int nChildren = children.Count();
				for ( int iChild = 0; iChild < nChildren; ++iChild )
				{
					CDmElement *pChild = children[ iChild ];
					ConvertSelectionGroup_R( pAnimSet, pControlGroup, pChild );
				}
			}
		}
	}
	else
	{
		// If the selection element is not a group, it is a control selection element which shares
		// its name with the control it targets. Find that control and add it to the parent group.
		CDmElement *pControl = FindAnimSetControl( pAnimSet, pSelectionElement->GetName() );
		if ( pControl )
		{
			CDmAttribute *pControlsAttr = pParentControlGroup->GetAttribute( "controls" );
			if ( pControlsAttr )
			{
				CDmrElementArray<> controls( pControlsAttr );
				controls.AddToTail( pControl );
			}
		}
	}
}

// Purpose: Convert the animation set selection group DmElements into
// DmeControlGroups. Removes "selectionGroups" and creates "rootControlGroup"
bool Update9( CDmElement *pElement )
{
	const static CUtlSymbolLarge animSetSym = g_pDataModel->GetSymbol( "DmeAnimationSet" );

	if ( pElement->GetType() == animSetSym )
	{
		CDmElement* pAnimSet = pElement;

		// Create the root selection group
		CDmElement *pRootGroup = CreateControlGroup( "rootGroup", pAnimSet );
		pAnimSet->SetValue( "rootControlGroup", pRootGroup );

		// Iterate through the selection DmElement groups and recursively
		// convert them to them to the DmeControlGroup format.
		CDmAttribute *pSelectionGroupsAttr = pAnimSet->GetAttribute( "selectionGroups", AT_ELEMENT_ARRAY );
		if ( pSelectionGroupsAttr )
		{
			CDmrElementArray<> controls( pSelectionGroupsAttr );
			int nControls = controls.Count();
			for ( int ci = 0; ci < nControls; ++ci )
			{
				CDmElement *pSelectionElement = controls[ ci ];
				if ( !pSelectionElement )
					continue;

				// Convert the generic DmElement group into a DmeControlGroup
				ConvertSelectionGroup_R( pAnimSet, pRootGroup, pSelectionElement );

				// Destroy the generic DmElement group
				DestroyElement( pSelectionElement );
			}
		}

		// Remove the selection groups list
		pAnimSet->RemoveAttributeByPtr( pSelectionGroupsAttr );
	}

	return true;
}

// Purpose: Change DmeModel.jointTransforms to DmeModel.jointList
bool Update8( CDmElement *pElement )
{
	const static CUtlSymbolLarge modelSetSym = g_pDataModel->GetSymbol( "DmeModel" );

	CUtlSymbolLarge typeSym = pElement->GetType();

	if ( typeSym == modelSetSym )
	{
		CDmAttribute *pJointTransformsAttr = pElement->GetAttribute( "jointTransforms", AT_ELEMENT_ARRAY );
		if ( pJointTransformsAttr )
		{
			CDmAttribute *pJointListAttr = pElement->GetAttribute( "jointList", AT_ELEMENT_ARRAY );
			if ( !pJointListAttr )
			{
				pJointListAttr = pElement->AddAttribute( "jointList", AT_ELEMENT_ARRAY );
			}

			if ( pJointListAttr )
			{
				const static CUtlSymbolLarge transformSym = g_pDataModel->GetSymbol( "transform" );
				const static CUtlSymbolLarge dmeJointSym = g_pDataModel->GetSymbol( "DmeJoint" );
				const static CUtlSymbolLarge dmeDagSym = g_pDataModel->GetSymbol( "DmeDag" );

				CUtlVector< CDmElement * > refList;

				CDmrElementArray<> jt( pJointTransformsAttr );
				CDmrElementArray<> jl( pJointListAttr );

				jl.RemoveAll();
				const int nJointCount = jt.Count();
				for ( int ji = 0; ji < nJointCount; ++ji )
				{
					refList.RemoveAll();
					if ( FindReferringElements( refList, jt[ ji ], transformSym ) )
					{
						const int nRefCount = refList.Count();
						for ( int ri = 0; ri < nRefCount; ++ri )
						{
							typeSym = refList[ ri ]->GetType();
							if ( typeSym == dmeJointSym || typeSym == dmeDagSym )
							{
								jl.AddToTail( refList[ ri ] );
								break;
							}
						}
					}
				}

				pElement->RemoveAttribute( "jointTransforms" );
			}
		}
	}

	return true;
}

// Purpose: Convert base control elements with the "transform" flag set into
// CDmeTransformControl elements and remove the obsolete attributes from the
// element which are now part of the CDmeTransfrom control class.
bool Update7( CDmElement *pElement )
{
	const static CUtlSymbolLarge animSetSym = g_pDataModel->GetSymbol( "DmeAnimationSet" );

	CUtlSymbolLarge typeSym = pElement->GetType();

	if ( typeSym == animSetSym )
	{
		CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pControlsAttr )
		{
			CDmrElementArray<> controls( pControlsAttr );
			int nControls = controls.Count();
			for ( int ci = 0; ci < nControls; ++ci )
			{
				CDmElement *pControl = controls[ ci ];
				if ( !pControl )
					continue;

				if ( pControl->GetValue<bool>( "transform", false ) )
				{
					// Remove the obsolete attributes that are now part of the transform control class
					pControl->RemoveAttribute( "valueTransform" );
					pControl->RemoveAttribute( "valueDeltaRotation" );
					pControl->RemoveAttribute( "valuePivot" );
					pControl->RemoveAttribute( "pivotOffset" );
					pControl->RemoveAttribute( "refOrientation" );

					// Change the element type to specify a CDmeTransformControl
					pControl->SetType( "DmeTransformControl" );
				}
			}
		}
	}

	return true;
}

bool Update6( CDmElement *pElement )
{
	CUtlSymbolLarge typeSym = pElement->GetType();

	const static CUtlSymbolLarge grainFXClipSym = g_pDataModel->GetSymbol( "DmeHSLGrainFXClip" );
	if ( typeSym == grainFXClipSym )
	{
		pElement->RemoveAttribute( "scale" );
		pElement->RemoveAttribute( "left" );
		pElement->RemoveAttribute( "top" );
		pElement->RemoveAttribute( "width" );
		pElement->RemoveAttribute( "height" );
		return true;
	}

	const static CUtlSymbolLarge dirLightSym = g_pDataModel->GetSymbol( "DmeDirectionalLight" );
	const static CUtlSymbolLarge pointLightSym = g_pDataModel->GetSymbol( "DmePointLight" );
	const static CUtlSymbolLarge spotLightSym = g_pDataModel->GetSymbol( "DmeSpotLight" );
	const static CUtlSymbolLarge projLightSym = g_pDataModel->GetSymbol( "DmeProjectedLight" );
	if ( typeSym == dirLightSym || typeSym == pointLightSym || typeSym == spotLightSym || typeSym == projLightSym )
	{
		Vector pos = pElement->GetValue< Vector >( "position" );
		Vector dir = pElement->GetValue< Vector >( "direction" );
		Quaternion quat = pElement->GetValue< Quaternion >( "orientation" );
		if ( dir != vec3_origin )
		{
			// HACK - there's some question here as to whether to use VectorAngles, which assumes forward is 1,0,0,
			// or to compute the 0,0,1->dir quaternion manually, but since dirlights aren't used anyways, I'm not worrying about it
			QAngle angles;
			VectorAngles( dir, angles );
			AngleQuaternion( angles, quat );
		}

		CDmElement *pTransform = pElement->GetValueElement< CDmElement >( "transform" );
		if ( !pTransform )
		{
			pTransform = CreateElement< CDmElement >( "DmeTransform", "transform", pElement->GetFileId() );
			pTransform->SetParity( pElement->GetParity() );
			pElement->SetValue( "transform", pTransform );
		}

		Vector parentPos = pTransform->GetValue< Vector >( "position" );
		Quaternion parentQuat = pTransform->GetValue< Quaternion >( "orientation" );

		Vector finalPos;
		Quaternion finalQuat;
		ConcatTransforms( parentQuat, parentPos, quat, pos, finalQuat, finalPos );

		pTransform->SetValue( "position", finalPos );
		pTransform->SetValue( "orientation", finalQuat );

		// NOTE - not bothering to convert logs on transform, since up until this change, updating the light's transform didn't do anything

		CDmElement *pPosChannel = FindChannelTargettingElement( pElement, "position", false );
		CDmElement *pPosLog = pPosChannel ? GetLogLayerFromChannel( pPosChannel ) : NULL;
		if ( pPosLog )
		{
			Quaternion qDummy;
			Vector temp;
			CDmAttribute *pAttr = pPosLog->GetAttribute( "values", AT_VECTOR3_ARRAY );
			CDmrArray< Vector > values( pAttr );
			int nValues = values.Count();
			for ( int i = 0; i < nValues; ++i )
			{
				ConcatTransforms( parentQuat, parentPos, quat_identity, values[ i ], qDummy, temp );
				values.Set( i, temp );
			}
			pPosChannel->SetValue( "toElement", pTransform );
		}

		CDmElement *pRotChannel = FindChannelTargettingElement( pElement, "orientation", false );
		CDmElement *pRotLog = pRotChannel ? GetLogLayerFromChannel( pRotChannel ) : NULL;
		if ( pRotLog )
		{
			Quaternion temp;
			Vector vDummy;
			CDmAttribute *pAttr = pRotLog->GetAttribute( "values", AT_QUATERNION_ARRAY );
			CDmrArray< Quaternion > values( pAttr );
			int nValues = values.Count();
			for ( int i = 0; i < nValues; ++i )
			{
				ConcatTransforms( parentQuat, parentPos, values[ i ], vec3_origin, temp, vDummy );
				values.Set( i, temp );
			}
			pRotChannel->SetValue( "toElement", pTransform );
		}

		pElement->RemoveAttribute( "position" );
		pElement->RemoveAttribute( "direction" );
		pElement->RemoveAttribute( "orientation" );
		return true;
	}

	return true;
}

bool Update4( CDmElement *pElement )
{
	const static CUtlSymbolLarge filmClipSym = g_pDataModel->GetSymbol( "DmeFilmClip" );
	const static CUtlSymbolLarge presetGroupSym = g_pDataModel->GetSymbol( "DmePresetGroup" );

	CUtlSymbolLarge typeSym = pElement->GetType();
	if ( typeSym == presetGroupSym )
	{
		CDmElement *pPresetRemap = pElement->GetValueElement< CDmElement >( "presetRemap" );
		if ( pPresetRemap )
		{
			DestroyElement( pPresetRemap, TD_NONE );
		}
		pElement->RemoveAttribute( "presetRemap" );
	}

	if ( typeSym != filmClipSym )
		return true;

	CDmAttribute *pClipAnimSetsAttr = pElement->GetAttribute( "animationSets", AT_ELEMENT_ARRAY );
	Assert( pClipAnimSetsAttr );
	if ( !pClipAnimSetsAttr )
		return true;

	CDmrElementArray<> clipAnimSets( pClipAnimSetsAttr );

	CDmAttribute *pAnimSetGroupsAttr = pElement->AddAttribute( "animationSetGroups", AT_ELEMENT_ARRAY );
	Assert( pAnimSetGroupsAttr );
	if ( !pAnimSetGroupsAttr )
		return true;

	CDmrElementArray<> animSetGroups( pAnimSetGroupsAttr );

	int nClipAnimSets = clipAnimSets.Count();
	for ( int i = 0; i < nClipAnimSets; ++i )
	{
		CDmElement *pAnimSet = clipAnimSets[ i ];
		if ( !pAnimSet )
		{
			animSetGroups.AddToTail( NULL );
			continue;
		}

		CDmElement *pAnimSetGroup = CreateElement< CDmElement >( "DmeAnimationSetGroup", pAnimSet->GetName(), pAnimSet->GetFileId() );
		pAnimSetGroup->SetParity( pElement->GetParity() );
		animSetGroups.AddToTail( pAnimSetGroup );

		CDmAttribute *pBookmarksAttr = pAnimSetGroup->AddAttribute( "bookmarks",	 AT_ELEMENT_ARRAY );
		CDmAttribute *pAnimSetsAttr  = pAnimSetGroup->AddAttribute( "animationSets", AT_ELEMENT_ARRAY );
		/*CDmAttribute *pChildrenAttr*/pAnimSetGroup->AddAttribute( "children",		 AT_ELEMENT_ARRAY );

		CDmrElementArray<> animSets( pAnimSetsAttr );
		animSets.AddToTail( pAnimSet );

		CDmAttribute *pClipBookmarksAttr = pElement->GetAttribute( "bookmarks", AT_ELEMENT_ARRAY );
		Assert( pClipBookmarksAttr );
		if ( pClipBookmarksAttr )
		{
			CDmrElementArray<> clipBookmarks( pClipBookmarksAttr );
			CDmrElementArray<> bookmarks( pBookmarksAttr );

			int nBookmarks = clipBookmarks.Count();
			for ( int bi = 0; bi < nBookmarks; ++bi )
			{
				bookmarks.AddToTail( clipBookmarks[ bi ] );
			}
		}

		pAnimSet->RemoveAttribute( "bookmarks" );
	}

	pElement->RemoveAttribute( "animationSets" );

	return true;
}

inline void ValueBalanceToLeftRight( float *pLeft, float *pRight, float flValue, float flBalance, float flDefaultValue )
{
	*pLeft = ( flBalance <= 0.5f ) ? flValue : ( ( 1.0f - flBalance ) / 0.5f ) * ( flValue - flDefaultValue ) + flDefaultValue;
	*pRight = ( flBalance >= 0.5f ) ? flValue : ( flBalance / 0.5f ) * ( flValue - flDefaultValue ) + flDefaultValue;
}

bool ConvertIntArrayToTimeArray( CDmAttribute *pAttribute )
{
	if ( !pAttribute )
		return false;

	DmAttributeType_t type = pAttribute->GetType();
	if ( type == AT_TIME_ARRAY )
		return true;

	Assert( type == AT_INT_ARRAY );
	if ( type != AT_INT_ARRAY )
		return false;

	pAttribute->ChangeType_UNSAFE( AT_TIME_ARRAY );

	Assert( pAttribute->GetType() == AT_TIME_ARRAY );
	if ( pAttribute->GetType() != AT_TIME_ARRAY )
		return false;

	return true;
}

float GetDefaultControlValue( const char *pControlName )
{
	// HACK - this is nasty, (and not 100% correct - the engineer's BrowInV is 1-way control) but it's either that or manually fix every preset file
	// fortunately, even if this isn't perfect, it's almost undetectable
	// we had to search the movies to find a spot where 0.0f wasn't a good enough default for every control, and the difference wasn't a big deal
	const static char *s_2WayControls[] =
	{
		"TongueWidth",
		"TongueV",
		"TongueH",
		"TongueD",
		"TongueCurl",
		"ScalpD",
		"NostrilFlare",
		"NoseV",
		"LipUpV",
		"LipsV",
		"LipLoV",
		"JawV",
		"JawH",
		"JawD",
		"FoldLipUp",
		"FoldLipLo",
		"CheekH",
		"BrowInV",
		"CloseLid", // this is actually a multi (3-way) control, so it's defaultValue *should* be 0, but due to dmxedit's trickery it's (intentionally???) 0.5
	};

	for ( int i = 0; i < ARRAYSIZE( s_2WayControls ); ++i )
	{
		if ( !V_strcmp( pControlName, s_2WayControls[i] ) )
			return 0.5f;
	}
	return 0.0f;
}

bool UpdatePreset( CDmElement *pPreset )
{
	if ( !pPreset )
		return true;

	CDmAttribute *pControlValuesAttr = pPreset->GetAttribute( "controlValues", AT_ELEMENT_ARRAY );
	if ( !pControlValuesAttr )
		return true;

	const char *pPresetName = pPreset->GetName();
	bool bFixupBalance = !V_stricmp( pPresetName, "zero" ) || !V_stricmp( pPresetName, "one" );

	CDmrElementArray<> controlValues( pControlValuesAttr );
	int nControlValues = controlValues.Count();
	for ( int vi = 0; vi < nControlValues; ++vi )
	{
		CDmElement *pControlValue = controlValues[ vi ];
		if ( !pControlValue )
			return true;

		if ( pControlValue->HasAttribute( "balance" ) )
		{
			float flDefaultValue = GetDefaultControlValue( pControlValue->GetName() );

			float flValue   = pControlValue->GetValue< float >( "value" );
			float flBalance = pControlValue->GetValue< float >( "balance" );
			pControlValue->RemoveAttribute( "value" );
			pControlValue->RemoveAttribute( "balance" );

			if ( bFixupBalance )
			{
				flBalance = 0.5f;
			}

			float flLeft, flRight;
			ValueBalanceToLeftRight( &flLeft, &flRight, flValue, flBalance, flDefaultValue );

			pControlValue->SetValue( "leftValue",  flLeft );
			pControlValue->SetValue( "rightValue", flRight );
		}
	}

	return true;
}

bool Update3( CDmElement *pElement )
{
	const static CUtlSymbolLarge animSetSym			= g_pDataModel->GetSymbol( "DmeAnimationSet" );
	const static CUtlSymbolLarge presetSym			= g_pDataModel->GetSymbol( "DmePreset" );
	const static CUtlSymbolLarge comboOpSym			= g_pDataModel->GetSymbol( "DmeCombinationOperator" );

	const static CUtlSymbolLarge logLayerSym		= g_pDataModel->GetSymbol( "DmeLogLayer" );
	const static CUtlSymbolLarge intLogLayerSym		= g_pDataModel->GetSymbol( "DmeIntLogLayer" );
	const static CUtlSymbolLarge floatLogLayerSym	= g_pDataModel->GetSymbol( "DmeFloatLogLayer" );
	const static CUtlSymbolLarge boolLogLayerSym	= g_pDataModel->GetSymbol( "DmeBoolLogLayer" );
	const static CUtlSymbolLarge colorLogLayerSym	= g_pDataModel->GetSymbol( "DmeColorLogLayer" );
	const static CUtlSymbolLarge vec2LogLayerSym	= g_pDataModel->GetSymbol( "DmeVector2LogLayer" );
	const static CUtlSymbolLarge vec3LogLayerSym	= g_pDataModel->GetSymbol( "DmeVector3LogLayer" );
	const static CUtlSymbolLarge vec4LogLayerSym	= g_pDataModel->GetSymbol( "DmeVector4LogLayer" );
	const static CUtlSymbolLarge qangleLogLayerSym	= g_pDataModel->GetSymbol( "DmeQAngleLogLayer" );
	const static CUtlSymbolLarge quatLogLayerSym	= g_pDataModel->GetSymbol( "DmeQuaternionLogLayer" );
	const static CUtlSymbolLarge vmatrixLogLayerSym	= g_pDataModel->GetSymbol( "DmeVMatrixLogLayer" );
	const static CUtlSymbolLarge stringLogLayerSym	= g_pDataModel->GetSymbol( "DmeStringLogLayer" );
	const static CUtlSymbolLarge timeLogLayerSym	= g_pDataModel->GetSymbol( "DmeTimeLogLayer" );

	CUtlSymbolLarge typeSym = pElement->GetType();
	if ( typeSym == logLayerSym
		|| typeSym == intLogLayerSym
		|| typeSym == floatLogLayerSym
		|| typeSym == boolLogLayerSym
		|| typeSym == colorLogLayerSym
		|| typeSym == vec2LogLayerSym
		|| typeSym == vec3LogLayerSym
		|| typeSym == vec4LogLayerSym
		|| typeSym == qangleLogLayerSym
		|| typeSym == quatLogLayerSym
		|| typeSym == vmatrixLogLayerSym
		|| typeSym == stringLogLayerSym
		|| typeSym == timeLogLayerSym )
	{
		return ConvertIntArrayToTimeArray( pElement->GetAttribute( "times" ) );
	}

	if ( typeSym == comboOpSym )
	{
		CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		CDmAttribute *pControlValuesAttr = pElement->GetAttribute( "controlValues", AT_VECTOR3_ARRAY );
		CDmAttribute *pControlValuesLaggedAttr = pElement->GetAttribute( "controlValuesLagged", AT_VECTOR3_ARRAY );
		if ( !pControlsAttr || !pControlValuesAttr || !pControlValuesLaggedAttr )
			return false;

		CDmrElementArray<> controls( pControlsAttr );
		CDmrArray< Vector > controlValues( pControlValuesAttr );
		CDmrArray< Vector > controlValuesLagged( pControlValuesLaggedAttr );

		int nControls = controls.Count();
		for ( int i = 0; i < nControls; ++i )
		{
			CDmElement *pControl = controls[ i ];
			if ( !pControl || !pControl->GetValue<bool>( "stereo" ) )
				continue;

			CDmAttribute *pRawControlNames = pControl->GetAttribute( "rawControlNames", AT_STRING_ARRAY );
			if ( !pRawControlNames )
				continue;

			CDmrStringArray	rawControlNames( pRawControlNames );
			float flDefaultValue = rawControlNames.Count() == 2 ? 0.5f : 0.0f;

			float flLeftValue, flRightValue;

			Vector cv = controlValues[ i ];
			ValueBalanceToLeftRight( &flLeftValue, &flRightValue, cv.x, cv.y, flDefaultValue );
			controlValues.Set( i, Vector( flLeftValue, flRightValue, cv.z ) );

			Vector cvl = controlValuesLagged[ i ];
			ValueBalanceToLeftRight( &flLeftValue, &flRightValue, cvl.x, cvl.y, flDefaultValue );
			controlValuesLagged.Set( i, Vector( flLeftValue, flRightValue, cvl.z ) );
		}

		return true;
	}

	if ( typeSym == presetSym )
	{
		return UpdatePreset( pElement );
	}

	if ( typeSym == animSetSym )
	{
		CDmElement *pFilmClip = FindReferringElement< CDmElement >( pElement, "animationSets" );
		if ( !pFilmClip )
			return true;

		// remove multi from controls, and move multilevel, defaultMultilevel and multilevelChannel to new _alt control
		// split preset multilevel controlValues into new controlValue
		// put new _alt control into same selection group as original

		// convert stereo controls from value/balance to left/right
		// - convert old VB into new LR, remove VB attributes
		// - convert and copy old VB log data into LR log data, and delete VB channels and logs
		CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY );
		if ( pControlsAttr )
		{
			CDmrElementArray<> controls( pControlsAttr );
			int nControls = controls.Count();
			for ( int ci = 0; ci < nControls; ++ci )
			{
				CDmElement *pControl = controls[ ci ];
				if ( !pControl )
					continue;

				if ( !pControl->GetValue<bool>( "multi", false ) )
				{
					// these shouldn't be here anyways, but sometimes they are so...
					pControl->RemoveAttribute( "multilevel" );
					pControl->RemoveAttribute( "defaultMultilevel" );
					pControl->RemoveAttribute( "multilevelchannel" );
				}

				if ( !pControl->GetValue<bool>( "combo", false ) )
				{
					// these shouldn't be here anyways, but sometimes they are so...
					pControl->RemoveAttribute( "balance" );
					pControl->RemoveAttribute( "defaultBalance" );
					pControl->RemoveAttribute( "balancechannel" );
					continue;
				}

				float flValue   = pControl->GetValue<float>( "value" );
				float flBalance = pControl->GetValue<float>( "balance" );

				float flDefaultValue   = pControl->GetValue<float>( "defaultValue" );
				float flDefaultBalance = pControl->GetValue<float>( "defaultBalance" );

				CDmElement *pValueChannel   = pControl->GetValueElement< CDmElement >( "valuechannel" );
				CDmElement *pBalanceChannel = pControl->GetValueElement< CDmElement >( "balancechannel" );

				pControl->RemoveAttribute( "value" );
				pControl->RemoveAttribute( "balance" );
				pControl->RemoveAttribute( "defaultValue" );
				pControl->RemoveAttribute( "defaultBalance" );
				pControl->RemoveAttribute( "valuechannel" );
				pControl->RemoveAttribute( "balancechannel" );

				float flLeftValue, flRightValue;
				ValueBalanceToLeftRight( &flLeftValue, &flRightValue, flValue, flBalance, flDefaultValue );
				pControl->SetValue( "leftValue", flLeftValue );
				pControl->SetValue( "rightValue", flRightValue );

				float flLeftDefaultValue, flRightDefaultValue;
				ValueBalanceToLeftRight( &flLeftDefaultValue, &flRightDefaultValue, flDefaultValue, flDefaultBalance, 0.0f ); // this is kind of silly, since we expect flDefaultBalance to always be 0.5
				pControl->SetValue( "defaultLeftValue", flLeftDefaultValue );
				pControl->SetValue( "defaultRightValue", flRightDefaultValue );

				if ( !pValueChannel || !pBalanceChannel )
					continue;

				CDmElement *pChannelsClip = FindChannelsClipForChannel( pFilmClip, pValueChannel );
				Assert( pChannelsClip == FindChannelsClipForChannel( pFilmClip, pBalanceChannel ) );
				if ( !pChannelsClip )
					continue;

				CDmAttribute *pChannelsAttr = pChannelsClip->GetAttribute( "channels" );
				if ( !pChannelsAttr )
					continue;

				CDmrElementArray<> channels( pChannelsAttr );
				channels.Remove( channels.Find( pValueChannel ) );
				channels.Remove( channels.Find( pBalanceChannel ) );

				CDmElement *pVB2LRCalcOp = pValueChannel->GetValueElement< CDmElement >( "toElement" );
				Assert( pVB2LRCalcOp  == pBalanceChannel->GetValueElement< CDmElement >( "toElement" ) );

				CDmElement *pLeftChannel  = FindChannelTargettingElement( pVB2LRCalcOp, "result_left", true );
				CDmElement *pRightChannel = FindChannelTargettingElement( pVB2LRCalcOp, "result_right", true );
				if ( !pLeftChannel || !pRightChannel )
					return false;

				pControl->SetValue( "leftvaluechannel", pLeftChannel );
				pControl->SetValue( "rightvaluechannel", pRightChannel );

				int nChannelMode = pValueChannel->GetValue< int >( "mode" );
				pLeftChannel ->SetValue( "fromElement", pControl );
				pLeftChannel ->SetValue( "fromAttribute", "leftValue" );
				pLeftChannel ->SetValue( "mode", nChannelMode );
				pRightChannel->SetValue( "fromElement", pControl );
				pRightChannel->SetValue( "fromAttribute", "rightValue" );
				pRightChannel->SetValue( "mode", nChannelMode );

				CDmElement *pValueLog   = GetLogLayerFromChannel( pValueChannel );
				CDmElement *pBalanceLog = GetLogLayerFromChannel( pBalanceChannel );
				CDmElement *pLeftLog    = GetLogLayerFromChannel( pLeftChannel );
				CDmElement *pRightLog   = GetLogLayerFromChannel( pRightChannel );
				if ( !pValueLog || !pBalanceLog || !pLeftLog || !pRightLog )
					continue;

				// doing this on-demand, in case the loglayer wasn't reached directly yet
				if ( !ConvertIntArrayToTimeArray( pValueLog->GetAttribute( "times" ) ) ||
					!ConvertIntArrayToTimeArray( pBalanceLog->GetAttribute( "times" ) ) )
					return false;

				const CUtlVector< DmeTime_t > &valueTimes    = pValueLog  ->GetValue< CUtlVector< DmeTime_t > >( "times" );
				const CUtlVector< float     > &valueValues   = pValueLog  ->GetValue< CUtlVector< float     > >( "values" );
				const CUtlVector< DmeTime_t > &balanceTimes  = pBalanceLog->GetValue< CUtlVector< DmeTime_t > >( "times" );
				const CUtlVector< float     > &balanceValues = pBalanceLog->GetValue< CUtlVector< float     > >( "values" );

				CDmAttribute *pLeftTimesAttr   = pLeftLog ->GetAttribute( "times" );
				CDmAttribute *pLeftValuesAttr  = pLeftLog ->GetAttribute( "values", AT_FLOAT_ARRAY );
				CDmAttribute *pRightTimesAttr  = pRightLog->GetAttribute( "times" );
				CDmAttribute *pRightValuesAttr = pRightLog->GetAttribute( "values", AT_FLOAT_ARRAY );

				if ( !pLeftTimesAttr || !pLeftValuesAttr || !pRightTimesAttr || !pRightValuesAttr )
					return false;

				// doing this on-demand, in case the loglayer wasn't reached directly yet
				if ( !ConvertIntArrayToTimeArray( pLeftTimesAttr ) ||
					!ConvertIntArrayToTimeArray( pRightTimesAttr ) )
					return false;

				CDmrArray< DmeTime_t > leftTimes  ( pLeftTimesAttr );
				CDmrArray< float > leftValues ( pLeftValuesAttr );
				CDmrArray< DmeTime_t > rightTimes ( pRightTimesAttr );
				CDmrArray< float > rightValues( pRightValuesAttr );

				// convert and copy log data
				int vi = 0, bi = 0;
				int vn = valueTimes.Count(), bn = balanceTimes.Count();
				leftTimes  .EnsureCapacity( MAX( vn, bn ) );
				leftValues .EnsureCapacity( MAX( vn, bn ) );
				rightTimes .EnsureCapacity( MAX( vn, bn ) );
				rightValues.EnsureCapacity( MAX( vn, bn ) );
				while ( vi < vn || bi < bn )
				{
					float value, balance;
					DmeTime_t t;
					if ( vi >= vn )
					{
						t = balanceTimes[ bi ];
						value = vn > 0 ? valueValues[ vn - 1 ] : flDefaultValue;
						balance = balanceValues[ bi ];
						++bi;
					}
					else if ( bi >= bn )
					{
						t = valueTimes[ vi ];
						value = valueValues[ vi ];
						balance = bn > 0 ? balanceValues[ bn - 1 ] : flDefaultBalance;
						++vi;
					}
					else
					{
						DmeTime_t vt = valueTimes  [ vi ];
						DmeTime_t bt = balanceTimes[ bi ];
						value   = valueValues  [ vi ];
						balance = balanceValues[ bi ];
						if ( vt < bt )
						{
							t = vt;
							if ( bi > 0 )
							{
								float f = ( t - balanceTimes[ bi - 1 ] ) / ( bt - balanceTimes[ bi - 1 ] );
								balance = Lerp( f, balanceValues[ bi - 1 ], balance );
							}
							++vi;
						}
						else if ( bt < vt )
						{
							t = bt;
							if ( vi > 0 )
							{
								float f = ( t - valueTimes[ vi - 1 ] ) / ( vt - valueTimes[ vi - 1 ] );
								value = Lerp( f, valueValues[ vi - 1 ], value );
							}
							++bi;
						}
						else
						{
							t = vt;
							++vi;
							++bi;
						}
					}

					float left, right;
					ValueBalanceToLeftRight( &left, &right, value, balance, flDefaultValue );

					leftTimes .AddToTail( t );
					rightTimes.AddToTail( t );
					leftValues .AddToTail( left );
					rightValues.AddToTail( right );

				}

				// NOTE - with non-typed elements, TD_SHALLOW == TD_NONE and TD_DEEP == TD_ALL
				DestroyElement( pValueChannel, TD_NONE );
				DestroyElement( pValueLog, TD_ALL );
				DestroyElement( pBalanceChannel, TD_NONE );
				DestroyElement( pBalanceLog, TD_ALL );
			}
		}

		// remove and delete BalanceToStereoCalculatorOperators
		CDmAttribute *pOperatorsAttr = pElement->GetAttribute( "operators" );
		if ( pOperatorsAttr )
		{
			const static CUtlSymbolLarge balanceToStereoSym = g_pDataModel->GetSymbol( "DmeBalanceToStereoCalculatorOperator" );
			CDmrElementArray<> operators( pOperatorsAttr );
			int nOperators = operators.Count();
			for ( int i = nOperators-1; i >= 0; --i )
			{
				CDmElement *pOperator = operators[ i ];
				if ( pOperator && pOperator->GetType() != balanceToStereoSym )
					continue;

				DestroyElement( pOperator, TD_NONE ); // NOTE - with non-typed elements, TD_SHALLOW == TD_NONE and TD_DEEP == TD_ALL
				operators.Remove( i );
			}
		}

		// convert preset control values from VB to LR
		CDmAttribute *pPresetGroupsAttr = pElement->GetAttribute( "presetGroups", AT_ELEMENT_ARRAY );
		if ( pPresetGroupsAttr )
		{
			CDmrElementArray<> presetGroups( pPresetGroupsAttr );
			int nPresetGroups = presetGroups.Count();
			for ( int gi = 0; gi < nPresetGroups; ++gi )
			{
				CDmElement *pPresetGroup = presetGroups[ gi ];
				if ( !pPresetGroup )
					continue;

				CDmAttribute *pPresetsAttr = pPresetGroup->GetAttribute( "presets", AT_ELEMENT_ARRAY  );
				if ( !pPresetsAttr )
					continue;

				CDmrElementArray<> presets( pPresetsAttr );
				int nPresets = presets.Count();
				for ( int pi = 0; pi < nPresets; ++pi )
				{
					if ( !UpdatePreset( presets[ pi ] ) )
						return false;
				}
			}
		}
	}

	return true;
}

static CDmElement *FindAnimSetControlByName( CDmrElementArray<> &array, char const *pchControlName, int *pIndex )
{
	Assert( pIndex );
	*pIndex = array.InvalidIndex();

	for ( int i = 0; i < array.Count(); ++i )
	{
		CDmElement *e = array[ i ];
		if ( !Q_stricmp( e->GetName(), pchControlName ) )
		{
			*pIndex = i;
			return e;
		}
	}
	return NULL;
}

bool Update2( CDmElement *pElement )
{
	const char *g_pControlType[] = { "isPosition", "isOrientation" };
	const char *g_pSuffix[] = { "pos", "rot" };

	const static CUtlSymbolLarge animSetSym = g_pDataModel->GetSymbol( "DmeAnimationSet" );

	CUtlSymbolLarge typeSym = pElement->GetType();
	if ( typeSym == animSetSym )
	{
		// Split transform controls out from the groups
		// Add the isPosition/isOrientation and baseName bools/string
		// Split the actual controls for transforms

		// First find the selectionGroups attribute and the controls attribute
		CDmAttribute *pGroupsAttr = pElement->GetAttribute( "selectionGroups", AT_ELEMENT_ARRAY );
		CDmAttribute *pControlsAttr = pElement->GetAttribute( "controls", AT_ELEMENT_ARRAY  );

		if ( pGroupsAttr && pControlsAttr )
		{
			CDmrElementArray<> groups( pGroupsAttr );
			CDmrElementArray<> controls( pControlsAttr );

			// Walk through groups and for the controls which point to transforms, build the subgroups
			int c = groups.Count();
			for ( int i = 0; i < c; ++i )
			{
				CDmElement *pGroup = groups[ i ];
				CDmAttribute *temp = pGroup->GetAttribute( "selectedControls", AT_STRING_ARRAY );
				CDmrStringArray temp2( temp );

				pGroup->RenameAttribute( "selectedControls", "selectedControls2" );
				CDmAttribute *pControlsAttr = pGroup->GetAttribute( "selectedControls2", AT_STRING_ARRAY );
				// First convert all of the string array stuff into an element array
				if ( pControlsAttr )
				{
					CDmrStringArray controlsInGroup( pControlsAttr );

					CDmAttribute *pNewControlsAttr = pGroup->AddAttribute( "controls", AT_ELEMENT_ARRAY );
					CDmrElementArray< CDmElement > newControls( pNewControlsAttr );

					for ( int j = 0 ; j < controlsInGroup.Count(); ++j )
					{
						char const *pchControlName = controlsInGroup[ j ];

						CDmElement *pNewControl = CreateElement< CDmElement >( pchControlName, pElement->GetFileId() );
						pNewControl->SetParity( pElement->GetParity() );
						// Assume the new ones are not groups
						pNewControl->SetValue< bool >( "isGroup", false );
						newControls.AddToTail( pNewControl );
					}

					// Discard the old one
					pGroup->RemoveAttribute( "selectedControls2" );
				}

				pGroup->SetValue< bool >( "isGroup", true );
			}

			// In the 2nd pass, create the subgroups for the transforms and update the "controls" array accordingly
			for ( int i = 0; i < c; ++i )
			{
				CDmElement *pGroup = groups[ i ];
				CDmAttribute *pControlsAttr = pGroup->GetAttribute( "controls", AT_ELEMENT_ARRAY );
				if ( pControlsAttr )
				{
					CDmrElementArray< CDmElement > controlsInGroup( pControlsAttr );
					for ( int j = 0 ; j < controlsInGroup.Count(); ++j )
					{
						char const *pchControlName = controlsInGroup[ j ]->GetName();

						// Now that we have a control, we'll see if it corresponds to a transform
						int nIndex = controls.InvalidIndex();
						CDmElement *pControl = FindAnimSetControlByName( controls, pchControlName, &nIndex );
						if ( pControl && pControl->GetValue< bool >( "transform" ) )
						{
							controlsInGroup[ j ]->SetValue< bool >( "isGroup", true );
							CDmAttribute *pNewControlsAttr = controlsInGroup[ j ]->AddAttribute( "controls", AT_ELEMENT_ARRAY );
							CDmrElementArray< CDmElement > newControls( pNewControlsAttr );

							CUtlVector< CDmElement * > added;

							// Build the pos and rot versions
							for ( int k = 0; k < 2; ++k )
							{
								CDmElement *control = CreateElement< CDmElement >( CFmtStr( "%s - %s", pchControlName, g_pSuffix[ k ] ), pElement->GetFileId() );
								control->SetParity( pElement->GetParity() );
								control->SetValue< bool >( "transform", true );
								control->SetValue< bool >( g_pControlType[ k ], true );
								control->SetValue< bool >( g_pControlType[ 1 - k ], false );
								control->SetValue( "baseName", pchControlName );

								if ( k == 0 )
								{
									control->SetValue< Vector >( "valuePosition", pControl->GetValue< Vector >( "valuePosition" ) );

									CDmElement *p = pControl->GetValueElement< CDmElement >( "position" );
									if ( p )
									{
										control->SetValue< CDmElement >( "position", p );
										// Now find the position channel's "from" and set it to the new control
										p->SetValue< CDmElement >( "fromElement", control );
									}
								}
								else
								{
									control->SetValue< Quaternion >( "valueOrientation", pControl->GetValue< Quaternion >( "valueOrientation" ) );
									CDmElement *p = pControl->GetValueElement< CDmElement >( "orientation" );
									if ( p )
									{
										control->SetValue< CDmElement >( "orientation", p );
										// Now find the orientation channel's "from" and set it to the new control
										p->SetValue< CDmElement >( "fromElement", control );
									}
								}

								added.AddToTail( control );

								CDmElement *groupControl = CreateElement< CDmElement >( CFmtStr( "%s - %s", pchControlName, g_pSuffix[ k ] ), pElement->GetFileId() );
								groupControl->SetParity( pElement->GetParity() );
								groupControl->SetValue< bool >( "isGroup", false );
								newControls.AddToTail( groupControl );
							}

							// Remove the singular one and replace with paired ones
							controls.Remove( nIndex );

							for ( int k = 0; k < 2; ++k )
							{
								controls.AddToTail( added[ k ] );
							}
						}
					}
				}
			}
		}
	}

	return true;
}

bool Update1( CDmElement *pElement )
{
	// remove lights attribute from filmclips

	const static CUtlSymbolLarge projectedLightSym		= g_pDataModel->GetSymbol( "DmeProjectedLight" );
	const static CUtlSymbolLarge filmClipSym			= g_pDataModel->GetSymbol( "DmeFilmClip" );
	const static CUtlSymbolLarge bookmarkSym			= g_pDataModel->GetSymbol( "DmeBookmark" );
	const static CUtlSymbolLarge timeframeSym			= g_pDataModel->GetSymbol( "DmeTimeFrame" );
	const static CUtlSymbolLarge timeSelectionSym		= g_pDataModel->GetSymbol( "DmeTimeSelection" );
	const static CUtlSymbolLarge proceduralPresetSym	= g_pDataModel->GetSymbol( "DmeProceduralPresetSettings" );
	const static CUtlSymbolLarge gameParticleSysSym		= g_pDataModel->GetSymbol( "DmeGameParticleSystem" );
	const static CUtlSymbolLarge cameraSym				= g_pDataModel->GetSymbol( "DmeCamera" );

	const static CUtlSymbolLarge intCurveInfoSym		= g_pDataModel->GetSymbol( "DmeIntCurveInfo" );
	const static CUtlSymbolLarge floatCurveInfoSym		= g_pDataModel->GetSymbol( "DmeFloatCurveInfo" );
	const static CUtlSymbolLarge boolCurveInfoSym		= g_pDataModel->GetSymbol( "DmeBoolCurveInfo" );
	const static CUtlSymbolLarge colorCurveInfoSym		= g_pDataModel->GetSymbol( "DmeColorCurveInfo" );
	const static CUtlSymbolLarge vec2CurveInfoSym		= g_pDataModel->GetSymbol( "DmeVector2CurveInfo" );
	const static CUtlSymbolLarge vec3CurveInfoSym		= g_pDataModel->GetSymbol( "DmeVector3CurveInfo" );
	const static CUtlSymbolLarge vec4CurveInfoSym		= g_pDataModel->GetSymbol( "DmeVector4CurveInfo" );
	const static CUtlSymbolLarge qangleCurveInfoSym		= g_pDataModel->GetSymbol( "DmeQAngleCurveInfo" );
	const static CUtlSymbolLarge quatCurveInfoSym		= g_pDataModel->GetSymbol( "DmeQuaternionCurveInfo" );
	const static CUtlSymbolLarge vmatrixCurveInfoSym	= g_pDataModel->GetSymbol( "DmeVMatrixCurveInfo" );
	const static CUtlSymbolLarge stringCurveInfoSym		= g_pDataModel->GetSymbol( "DmeStringCurveInfo" );
	const static CUtlSymbolLarge timeCurveInfoSym		= g_pDataModel->GetSymbol( "DmeTimeCurveInfo" );

	CUtlSymbolLarge typeSym = pElement->GetType();
	if ( typeSym == projectedLightSym )
	{
		pElement->RemoveAttribute( "textureFrame" );

		ChangeAttributeType( pElement, "animationTime", AT_TIME );
	}
	else if ( typeSym == filmClipSym )
	{
		pElement->RemoveAttribute( "lights" );

		ChangeAttributeType( pElement, "fadeIn", AT_TIME );
		ChangeAttributeType( pElement, "fadeOut", AT_TIME );
	}
	else if ( typeSym == bookmarkSym )
	{
		ChangeAttributeType( pElement, "time", AT_TIME );
		ChangeAttributeType( pElement, "duration", AT_TIME );
	}
	else if ( typeSym == timeframeSym )
	{
		pElement->RenameAttribute( "startTime", "start" );
		pElement->RenameAttribute( "durationTime", "duration" );
		pElement->RenameAttribute( "offsetTime", "offset" );
		ChangeAttributeType( pElement, "start", AT_TIME );
		ChangeAttributeType( pElement, "duration", AT_TIME );
		ChangeAttributeType( pElement, "offset", AT_TIME );
	}
	else if ( typeSym == timeSelectionSym )
	{
		ChangeAttributeType( pElement, "falloff_left", AT_TIME );
		ChangeAttributeType( pElement, "falloff_right", AT_TIME );
		ChangeAttributeType( pElement, "hold_left", AT_TIME );
		ChangeAttributeType( pElement, "hold_right", AT_TIME );
		ChangeAttributeType( pElement, "resampleinterval", AT_TIME );
	}
	else if ( typeSym == proceduralPresetSym )
	{
		ChangeAttributeType( pElement, "staggerinterval", AT_TIME );
	}
	else if ( typeSym == gameParticleSysSym )
	{
		ChangeAttributeType( pElement, "simulationTime", AT_TIME );
		ChangeAttributeType( pElement, "startTime", AT_TIME );
		ChangeAttributeType( pElement, "emissionStopTime", AT_TIME );
		ChangeAttributeType( pElement, "endTime", AT_TIME );
	}
	else if ( typeSym == cameraSym )
	{
		ChangeAttributeType( pElement, "shutterSpeed", AT_TIME );
	}
	else if ( typeSym == intCurveInfoSym
		|| typeSym == floatCurveInfoSym
		|| typeSym == boolCurveInfoSym
		|| typeSym == colorCurveInfoSym
		|| typeSym == vec2CurveInfoSym
		|| typeSym == vec3CurveInfoSym
		|| typeSym == vec4CurveInfoSym
		|| typeSym == qangleCurveInfoSym
		|| typeSym == quatCurveInfoSym
		|| typeSym == vmatrixCurveInfoSym
		|| typeSym == stringCurveInfoSym
		|| typeSym == timeCurveInfoSym )
	{
		ChangeAttributeType( pElement, "rightEdgeTime", AT_TIME );
	}

	return true;
}

static char *s_RemapOperatorNameTable[]={
	"alpha_fade", "Alpha Fade and Decay",
	"alpha_fade_in_random", "Alpha Fade In Random",
	"alpha_fade_out_random", "Alpha Fade Out Random",
	"basic_movement", "Movement Basic",
	"color_fade", "Color Fade",
	"controlpoint_light", "Color Light From Control Point",
	"Dampen Movement Relative to Control Point", "Movement Dampen Relative to Control Point",
	"Distance Between Control Points Scale", "Remap Distance Between Two Control Points to Scalar",
	"Distance to Control Points Scale", "Remap Distance to Control Point to Scalar",
	"lifespan_decay", "Lifespan Decay",
	"lock to bone",	"Movement Lock to Bone",
	"postion_lock_to_controlpoint", "Movement Lock to Control Point",
	"maintain position along path", "Movement Maintain Position Along Path",
	"Match Particle Velocities", "Movement Match Particle Velocities",
	"Max Velocity", "Movement Max Velocity",
	"noise", "Noise Scalar",
	"vector noise", "Noise Vector",
	"oscillate_scalar", "Oscillate Scalar",
	"oscillate_vector", "Oscillate Vector",
	"Orient Rotation to 2D Direction", "Rotation Orient to 2D Direction",
	"radius_scale", "Radius Scale",
	"Random Cull", "Cull Random",
	"remap_scalar", "Remap Scalar",
	"rotation_movement", "Rotation Basic",
	"rotation_spin", "Rotation Spin Roll",
	"rotation_spin yaw", "Rotation Spin Yaw",
	"alpha_random", "Alpha Random",
	"color_random", "Color Random",
	"create from parent particles", "Position From Parent Particles",
	"Create In Hierarchy", "Position In CP Hierarchy",
	"random position along path", "Position Along Path Random",
	"random position on model", "Position on Model Random",
	"sequential position along path", "Position Along Path Sequential",
	"position_offset_random", "Position Modify Offset Random",
	"position_warp_random", "Position Modify Warp Random",
	"position_within_box", "Position Within Box Random",
	"position_within_sphere", "Position Within Sphere Random",
	"Inherit Velocity", "Velocity Inherit from Control Point",
	"Initial Repulsion Velocity", "Velocity Repulse from World",
	"Initial Velocity Noise", "Velocity Noise",
	"Initial Scalar Noise", "Remap Noise to Scalar",
	"Lifespan from distance to world", "Lifetime from Time to Impact",
	"Pre-Age Noise", "Lifetime Pre-Age Noise",
	"lifetime_random", "Lifetime Random",
	"radius_random", "Radius Random",
	"random yaw", "Rotation Yaw Random",
	"Randomly Flip Yaw", "Rotation Yaw Flip Random",
	"rotation_random", "Rotation Random",
	"rotation_speed_random", "Rotation Speed Random",
	"sequence_random", "Sequence Random",
	"second_sequence_random", "Sequence Two Random",
	"trail_length_random", "Trail Length Random",
	"velocity_random", "Velocity Random",
};

bool Update5( CDmElement *pElement )
{
	const static CUtlSymbolLarge ParticleOperatorSym	= g_pDataModel->GetSymbol( "DmeParticleOperator" );

	CUtlSymbolLarge typeSym = pElement->GetType();

	if ( typeSym == ParticleOperatorSym )
	{
		const char *pOpName = pElement->GetValueString( "functionName" );
		for( int i = 0 ; i < ARRAYSIZE( s_RemapOperatorNameTable ) ; i += 2 )
		{
			if ( Q_stricmp( pOpName, s_RemapOperatorNameTable[i] ) == 0 )
			{
				pElement->SetValueFromString( "functionName", s_RemapOperatorNameTable[i + 1 ] );
				pElement->SetName( s_RemapOperatorNameTable[i + 1 ] );
				break;
			}
		}
	}

	return true;
}

bool UpdateElement_R( FnUpdater pfnUpdate, CDmElement **ppElement, bool bParity )
{
	CDmElement *pElement = *ppElement;
	if ( pElement->GetParity() == bParity )
		return true; // already visited

	pElement->SetParity( bParity );

	if ( !pfnUpdate( pElement ) )
		return false;

	for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
	{
		if ( pAttribute->GetType() == AT_ELEMENT )
		{
			CDmElement *pElement = pAttribute->GetValueElement< CDmElement >();
			if ( pElement )
			{
				if ( !UpdateElement_R( pfnUpdate, &pElement, bParity ) )
					return false;
				pAttribute->SetValue< CDmElement >( 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 ];
				if ( pChild )
				{
					if ( !UpdateElement_R( pfnUpdate, &pChild, bParity ) )
						return false;
					array.Set( i, pChild );
				}
			}
			continue;
		}
	}

	return true;
}

IMPLEMENT_UPDATER( 1 );	// Creates Update1_R, which requires an Update1 func
IMPLEMENT_UPDATER( 2 );
IMPLEMENT_UPDATER( 3 );
IMPLEMENT_UPDATER( 4 );
IMPLEMENT_UPDATER( 5 );
IMPLEMENT_UPDATER( 6 );
IMPLEMENT_UPDATER( 7 );
IMPLEMENT_UPDATER( 8 );
IMPLEMENT_UPDATER( 9 );
IMPLEMENT_UPDATER( 10 );
IMPLEMENT_UPDATER( 11 );
IMPLEMENT_UPDATER( 12 );
IMPLEMENT_UPDATER( 13 );
IMPLEMENT_UPDATER( 14 );
IMPLEMENT_UPDATER( 15 );
IMPLEMENT_UPDATER( 16 );
IMPLEMENT_UPDATER( 17 );
IMPLEMENT_UPDATER( 18 );
IMPLEMENT_UPDATER( 19 );

typedef bool (*ElementUpdateFunction)( CDmElement **pRoot, bool bParity );
ElementUpdateFunction EmptyUpdateFunctionList[] = { NULL };
ElementUpdateFunction PresetUpdateFunctionList[] = { Update3_R, Update4_R, Update10_R, NULL };
ElementUpdateFunction MovieObjectsUpdateFunctionList[] =
{
	Update1_R, Update2_R, Update3_R, Update4_R, Update6_R, Update7_R, Update8_R, Update9_R,
	Update10_R, Update11_R, Update12_R, Update13_R, Update14_R, Update15_R, Update17_R, Update18_R, Update19_R, NULL
};
ElementUpdateFunction SFMSessionObjectsUpdateFunctionList[] =
{
	Update1_R, Update2_R, Update3_R, Update4_R, Update6_R, Update7_R, Update8_R, Update9_R,
	Update10_R, Update11_R, Update12_R, Update13_R, Update14_R, Update15_R, Update16_R, Update17_R, Update18_R, Update19_R,  NULL
};
ElementUpdateFunction ParticleUpdateFunctionList[] = { Update5_R, NULL };

// declare format updaters

DECLARE_FORMAT_UPDATER( dmx,				"Generic DMX",					"dmx", "binary",	MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( movieobjects,		"Generic MovieObjects",			"dmx", "binary",	MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( sfm,				"Generic SFM",					"dmx", "binary",	MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( sfm_settings,		"SFM Settings",					"dmx", "keyvalues2",MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( sfm_session,		"SFM Session",					"dmx", "binary",	SFMSessionObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( sfm_trackgroup,		"SFM TrackGroup",				"dmx", "binary",	MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( pcf,				"Particle Configuration File",	"pcf", "binary",	ParticleUpdateFunctionList )
DECLARE_FORMAT_UPDATER( preset,				"Preset File",					"dmx", "keyvalues2",PresetUpdateFunctionList )
DECLARE_FORMAT_UPDATER( facial_animation,	"Facial Animation File",		"dmx", "binary",	MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( model,				"DMX Model",					"dmx", "binary",	MovieObjectsUpdateFunctionList )
DECLARE_FORMAT_UPDATER( ved,				"Vgui editor file",				"ved", "keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mks,				"Make sheet file",				"mks", "keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_preprocess,		"DMX Model Pipeline Preprocess File",				"mpp",				"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_root,			"DMX Model Pipeline Root Script File",				"root",				"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_model,			"DMX Model Pipeline Model Script File",				"model",			"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_anim,			"DMX Model Pipeline Animation Script File",			"anim",				"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_physics,			"DMX Model Pipeline Physics Script File",			"physics",			"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_hitbox,			"DMX Model Pipeline Hitbox Script File",			"hitbox",			"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_materialgroup,	"DMX Model Pipeline Material Group Script File",	"materialgroup",	"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_keyvalues,		"DMX Model Pipeline KeyValues Script File",			"keyvalues",		"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_eyes,			"DMX Model Pipeline Eyes Script File",				"eyes",				"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( mp_bonemask,		"DMX Model Pipeline Bone Mask Script File",			"bonemask",			"keyvalues2",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( gui,				"Compiled GUI file",			"gui", "keyvalues2",EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( schema,				"Schema description file",		"sch", "keyvalues2",EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( tex,				"Texture Configuration File",	"tex", "keyvalues2",EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( world,				"World Files",					"wld", "binary",	EmptyUpdateFunctionList )
DECLARE_FORMAT_UPDATER( worldnode,			"World Node Files",				"wnd", "binary",	EmptyUpdateFunctionList )

// 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 );
	InstallCommentaryImporter( g_pDataModel );
	InstallVMTImporter( g_pDataModel );
	InstallVMFImporter( g_pDataModel );
	InstallMKSImporter( g_pDataModel );
	InstallTEXImporter( 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_settings );
	INSTALL_FORMAT_UPDATER( sfm_session );
	INSTALL_FORMAT_UPDATER( sfm_trackgroup );
	INSTALL_FORMAT_UPDATER( pcf );
	INSTALL_FORMAT_UPDATER( gui );
	INSTALL_FORMAT_UPDATER( schema );
	INSTALL_FORMAT_UPDATER( preset );
	INSTALL_FORMAT_UPDATER( facial_animation );
	INSTALL_FORMAT_UPDATER( model );
	INSTALL_FORMAT_UPDATER( ved );
	INSTALL_FORMAT_UPDATER( mks );
	INSTALL_FORMAT_UPDATER( mp_preprocess );
	INSTALL_FORMAT_UPDATER( mp_root );
	INSTALL_FORMAT_UPDATER( mp_model );
	INSTALL_FORMAT_UPDATER( mp_anim );
	INSTALL_FORMAT_UPDATER( mp_physics );
	INSTALL_FORMAT_UPDATER( mp_hitbox );
	INSTALL_FORMAT_UPDATER( mp_materialgroup );
	INSTALL_FORMAT_UPDATER( mp_keyvalues );
	INSTALL_FORMAT_UPDATER( mp_eyes );
	INSTALL_FORMAT_UPDATER( mp_bonemask );
	INSTALL_FORMAT_UPDATER( tex );
	INSTALL_FORMAT_UPDATER( world );
	INSTALL_FORMAT_UPDATER( worldnode );

	return INIT_OK;
}