Browse Source

Merge branch 'development' of github.com:GarageGames/Torque2D into development

capnlove 12 years ago
parent
commit
74fef07
63 changed files with 5552 additions and 4898 deletions
  1. 1 1
      engine/compilers/Xcode/Torque2D.xcodeproj/xcshareddata/xcschemes/Torque2D.xcscheme
  2. 38 32
      engine/source/2d/assets/ImageAsset.cc
  3. 2 2
      engine/source/2d/assets/ImageAsset.h
  4. 4 4
      engine/source/2d/assets/ParticleAsset.cc
  5. 2 2
      engine/source/2d/assets/ParticleAsset.h
  6. 4 4
      engine/source/2d/assets/ParticleAssetEmitter.cc
  7. 2 2
      engine/source/2d/assets/ParticleAssetEmitter.h
  8. 74 39
      engine/source/2d/assets/ParticleAssetField.cc
  9. 2 2
      engine/source/2d/assets/ParticleAssetField.h
  10. 22 19
      engine/source/2d/assets/ParticleAssetFieldCollection.cc
  11. 2 2
      engine/source/2d/assets/ParticleAssetFieldCollection.h
  12. 1 1
      engine/source/2d/controllers/BuoyancyController.cc
  13. 1 1
      engine/source/2d/controllers/PointForceController.cc
  14. 18 15
      engine/source/2d/core/SpriteBatch.cc
  15. 2 2
      engine/source/2d/core/SpriteBatch.h
  16. 34 28
      engine/source/2d/core/SpriteBatchItem.cc
  17. 2 2
      engine/source/2d/core/SpriteBatchItem.h
  18. 1136 969
      engine/source/2d/scene/Scene.cc
  19. 3 3
      engine/source/2d/scene/Scene.h
  20. 14 14
      engine/source/2d/sceneobject/CompositeSprite.cc
  21. 2 2
      engine/source/2d/sceneobject/CompositeSprite.h
  22. 276 171
      engine/source/2d/sceneobject/SceneObject.cc
  23. 2 2
      engine/source/2d/sceneobject/SceneObject.h
  24. 1 1
      engine/source/2d/sceneobject/SceneObject_ScriptBinding.h
  25. 26 23
      engine/source/assets/assetQuery.cc
  26. 4 4
      engine/source/assets/assetQuery.h
  27. 45 39
      engine/source/assets/assetTagsManifest.cc
  28. 4 4
      engine/source/assets/assetTagsManifest.h
  29. 61 49
      engine/source/component/behaviors/behaviorComponent.cpp
  30. 4 4
      engine/source/component/behaviors/behaviorComponent.h
  31. 0 2
      engine/source/memory/frameAllocator.h
  32. 2454 2453
      engine/source/module/moduleManager.cc
  33. 59 62
      engine/source/persistence/taml/taml.cc
  34. 6 5
      engine/source/persistence/taml/taml.h
  35. 110 97
      engine/source/persistence/taml/tamlBinaryReader.cc
  36. 2 1
      engine/source/persistence/taml/tamlBinaryReader.h
  37. 116 83
      engine/source/persistence/taml/tamlBinaryWriter.cc
  38. 3 2
      engine/source/persistence/taml/tamlBinaryWriter.h
  39. 4 4
      engine/source/persistence/taml/tamlCallbacks.h
  40. 9 56
      engine/source/persistence/taml/tamlCustom.cc
  41. 352 254
      engine/source/persistence/taml/tamlCustom.h
  42. 2 2
      engine/source/persistence/taml/tamlWriteNode.cc
  43. 1 3
      engine/source/persistence/taml/tamlWriteNode.h
  44. 98 78
      engine/source/persistence/taml/tamlXmlReader.cc
  45. 2 1
      engine/source/persistence/taml/tamlXmlReader.h
  46. 110 73
      engine/source/persistence/taml/tamlXmlWriter.cc
  47. 2 1
      engine/source/persistence/taml/tamlXmlWriter.h
  48. 3 3
      engine/source/sim/simObject.h
  49. 5 5
      main.cs
  50. 0 0
      modules/AppCore/1/fonts/.gitignore
  51. 50 0
      modules/AppCore/1/main.cs
  52. 7 0
      modules/AppCore/1/module.taml
  53. 132 132
      modules/AppCore/1/scripts/canvas.cs
  54. 51 51
      modules/AppCore/1/scripts/constants.cs
  55. 67 0
      modules/AppCore/1/scripts/defaultPreferences.cs
  56. 63 63
      modules/AppCore/1/scripts/openal.cs
  57. 3 2
      modules/ConstantForceControllerToy/1/main.cs
  58. 1 1
      modules/PointForceControllerToy/1/main.cs
  59. 1 1
      modules/Sandbox/1/gui/ToolboxDialog.gui.taml
  60. 2 17
      modules/Sandbox/1/main.cs
  61. 2 1
      modules/Sandbox/1/module.taml
  62. 39 0
      modules/Sandbox/1/scripts/sandboxPreferences.cs
  63. 7 2
      modules/ToyAssets/1/assets/particles/bonfire.asset.taml

+ 1 - 1
engine/compilers/Xcode/Torque2D.xcodeproj/xcshareddata/xcschemes/Torque2D.xcscheme

@@ -44,7 +44,7 @@
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Debug"
+      buildConfiguration = "Release"
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">

+ 38 - 32
engine/source/2d/assets/ImageAsset.cc

@@ -109,8 +109,8 @@ IMPLEMENT_CONOBJECT(ImageAsset);
 
 static bool explicitCellPropertiesInitialized = false;
 
-static StringTableEntry cellCustomPropertyName;
-static StringTableEntry cellAliasName;
+static StringTableEntry cellCustomNodeName;
+static StringTableEntry cellNodeName;
 static StringTableEntry cellOffsetName;
 static StringTableEntry cellWidthName;
 static StringTableEntry cellHeightName;
@@ -184,8 +184,8 @@ ImageAsset::ImageAsset() :  mImageFile(StringTable->EmptyString),
     // Initialize explicit cell field names.
     if ( !explicitCellPropertiesInitialized )
     {
-        cellCustomPropertyName      = StringTable->insert( "Cells" );
-        cellAliasName               = StringTable->insert( "Cell" );
+        cellCustomNodeName          = StringTable->insert( "Cells" );
+        cellNodeName                = StringTable->insert( "Cell" );
         cellOffsetName              = StringTable->insert( "Offset" );
         cellWidthName               = StringTable->insert( "Width" );
         cellHeightName              = StringTable->insert( "Height" );
@@ -868,20 +868,20 @@ void ImageAsset::onTamlPostWrite( void )
 
 //------------------------------------------------------------------------------
 
-void ImageAsset::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ImageAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomWrite);
 
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Finish if not in explicit mode.
     if ( !mExplicitMode )
         return;
 
-    // Add cell custom property.
-    TamlCustomProperty* pCellProperty = customProperties.addProperty( cellCustomPropertyName );
+    // Add cell custom node.
+    TamlCustomNode* pCustomCellNodes = customNodes.addNode( cellCustomNodeName );
 
     // Iterate explicit frames.
     for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
@@ -890,49 +890,52 @@ void ImageAsset::onTamlCustomWrite( TamlCustomProperties& customProperties )
         const FrameArea::PixelArea& pixelArea = *frameItr;
 
         // Add cell alias.
-        TamlPropertyAlias* pCellAlias = pCellProperty->addAlias( cellAliasName );
+        TamlCustomNode* pCellNode = pCustomCellNodes->addNode( cellNodeName );
 
         // Add cell properties.
-        pCellAlias->addField( cellOffsetName, pixelArea.mPixelOffset );
-        pCellAlias->addField( cellWidthName, pixelArea.mPixelWidth );
-        pCellAlias->addField( cellHeightName, pixelArea.mPixelHeight );
+        pCellNode->addField( cellOffsetName, pixelArea.mPixelOffset );
+        pCellNode->addField( cellWidthName, pixelArea.mPixelWidth );
+        pCellNode->addField( cellHeightName, pixelArea.mPixelHeight );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void ImageAsset::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ImageAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
 
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find cell custom property
-    const TamlCustomProperty* pCellProperty = customProperties.findProperty( cellCustomPropertyName );
+    // Find cell custom node.
+    const TamlCustomNode* pCustomCellNodes = customNodes.findNode( cellCustomNodeName );
 
     // Finish if we don't have explicit cells.
-    if ( pCellProperty == NULL )
+    if ( pCustomCellNodes == NULL )
         return;
 
     // Set explicit mode.
     mExplicitMode = true;
 
+    // Fetch children cell nodes.
+    const TamlCustomNodeVector& cellNodes = pCustomCellNodes->getChildren();
+
     // Iterate cells.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pCellProperty->begin(); propertyAliasItr != pCellProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator cellNodeItr = cellNodes.begin(); cellNodeItr != cellNodes.end(); ++cellNodeItr )
     {
-        // Fetch property alias.
-        TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+        // Fetch cell node.
+        TamlCustomNode* pCellNode = *cellNodeItr;
 
-        // Fetch alias name.
-        StringTableEntry aliasName = pPropertyAlias->mAliasName;
+        // Fetch node name.
+        StringTableEntry nodeName = pCellNode->getNodeName();
 
         // Is this a valid alias?
-        if ( aliasName != cellAliasName )
+        if ( nodeName != cellNodeName )
         {
             // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom alias name of '%s'.  Only '%s' is valid.", aliasName, cellAliasName );
+            Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom name of '%s'.  Only '%s' is valid.", nodeName, cellNodeName );
             continue;
         }
 
@@ -940,27 +943,30 @@ void ImageAsset::onTamlCustomRead( const TamlCustomProperties& customProperties
         S32 cellWidth = 0;
         S32 cellHeight = 0;
 
+        // Fetch fields.
+        const TamlCustomFieldVector& fields = pCellNode->getFields();
+
         // Iterate property fields.
-        for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+        for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
         {
-            // Fetch property field.
-            TamlPropertyField* pPropertyField = *propertyFieldItr;
+            // Fetch field.
+            const TamlCustomField* pField = *fieldItr;
 
-            // Fetch property field name.
-            StringTableEntry fieldName = pPropertyField->getFieldName();
+            // Fetch field name.
+            StringTableEntry fieldName = pField->getFieldName();
 
             // Check common fields.
             if ( fieldName == cellOffsetName )
             {
-                pPropertyField->getFieldValue( cellOffset );
+                pField->getFieldValue( cellOffset );
             }
             else if ( fieldName == cellWidthName )
             {
-                pPropertyField->getFieldValue( cellWidth );
+                pField->getFieldValue( cellWidth );
             }
             else if ( fieldName == cellHeightName )
             {
-                pPropertyField->getFieldValue( cellHeight );
+                pField->getFieldValue( cellHeight );
             }
             else
             {

+ 2 - 2
engine/source/2d/assets/ImageAsset.h

@@ -243,8 +243,8 @@ protected:
     /// Taml callbacks.
     virtual void onTamlPreWrite( void );
     virtual void onTamlPostWrite( void );
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 
 protected:

+ 4 - 4
engine/source/2d/assets/ParticleAsset.cc

@@ -474,22 +474,22 @@ void ParticleAsset::moveEmitter( S32 fromIndex, S32 toIndex )
 
 //------------------------------------------------------------------------------
 
-void ParticleAsset::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ParticleAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAsset_OnTamlCustomWrite);
 
     // Write the fields.
-    mParticleFields.onTamlCustomWrite( customProperties );
+    mParticleFields.onTamlCustomWrite( customNodes );
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAsset::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ParticleAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAsset_OnTamlCustomRead);
 
     // Read the fields.
-    mParticleFields.onTamlCustomRead( customProperties );
+    mParticleFields.onTamlCustomRead( customNodes );
 }

+ 2 - 2
engine/source/2d/assets/ParticleAsset.h

@@ -160,8 +160,8 @@ public:
 protected:
     virtual void initializeAsset( void );
 
-    void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 protected:
     static bool setLifetime(void* obj, const char* data)                    { static_cast<ParticleAsset*>(obj)->setLifetime(dAtof(data)); return false; }

+ 4 - 4
engine/source/2d/assets/ParticleAssetEmitter.cc

@@ -489,23 +489,23 @@ void ParticleAssetEmitter::onAssetRefreshed( AssetPtrBase* pAssetPtrBase )
 
 //------------------------------------------------------------------------------
 
-void ParticleAssetEmitter::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ParticleAssetEmitter::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetEmitter_OnTamlCustomWrite);
 
     // Write the fields.
-    mParticleFields.onTamlCustomWrite( customProperties );
+    mParticleFields.onTamlCustomWrite( customNodes );
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAssetEmitter::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ParticleAssetEmitter::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetEmitter_OnTamlCustomRead);
 
     // Read the fields.
-    mParticleFields.onTamlCustomRead( customProperties );
+    mParticleFields.onTamlCustomRead( customNodes );
 }
 

+ 2 - 2
engine/source/2d/assets/ParticleAssetEmitter.h

@@ -260,8 +260,8 @@ private:
     virtual void onAssetRefreshed( AssetPtrBase* pAssetPtrBase );
 
 protected:
-    void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
     static bool     setEmitterName(void* obj, const char* data)                         { static_cast<ParticleAssetEmitter*>(obj)->setEmitterName( data ); return false; }
     static bool     setEmitterType(void* obj, const char* data)                         { static_cast<ParticleAssetEmitter*>(obj)->setEmitterType( getEmitterTypeEnum(data) ); return false; }

+ 74 - 39
engine/source/2d/assets/ParticleAssetField.cc

@@ -50,6 +50,10 @@ static StringTableEntry particleAssetFieldDefaultValueName;
 static StringTableEntry particleAssetFieldValueScaleName;
 static StringTableEntry particleAssetFieldDataKeysName;
 
+static StringTableEntry particleAssetFieldDataKeyName;
+static StringTableEntry particleAssetFieldDataKeyTimeName;
+static StringTableEntry particleAssetFieldDataKeyValueName;
+
 ParticleAssetField::DataKey ParticleAssetField::BadDataKey( -1.0f, 0.0f );
 
 //-----------------------------------------------------------------------------
@@ -78,6 +82,10 @@ ParticleAssetField::ParticleAssetField() :
         particleAssetFieldValueScaleName   = StringTable->insert( "ValueScale" );
         particleAssetFieldDataKeysName     = StringTable->insert( "Keys" );
 
+        particleAssetFieldDataKeyName      = StringTable->insert( "Key" );
+        particleAssetFieldDataKeyTimeName  = StringTable->insert( "Time" );
+        particleAssetFieldDataKeyValueName = StringTable->insert( "Value" );
+
         // Flag as initialized.
         particleAssetFieldPropertiesInitialized = true;
     }
@@ -509,34 +517,34 @@ F32 ParticleAssetField::calculateFieldBVLE( const ParticleAssetField& base, cons
 
 //------------------------------------------------------------------------------
 
-void ParticleAssetField::onTamlCustomWrite( TamlCustomProperty* pCustomProperty )
+void ParticleAssetField::onTamlCustomWrite( TamlCustomNode* pCustomNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetField_OnTamlCustomWrite);
 
-    // Add a alias (ignore it if there ends up being no properties).
-    TamlPropertyAlias* pPropertyAlias = pCustomProperty->addAlias( getFieldName(), true );
+    // Add a child (ignore it if there ends up being no children).
+    TamlCustomNode* pAssetField = pCustomNode->addNode( getFieldName(), true );
 
     // Sanity!
-    AssertFatal( pPropertyAlias != NULL, "ParticleAssetField::onTamlCustomWrite() - Could not create field alias." );
+    AssertFatal( pAssetField != NULL, "ParticleAssetField::onTamlCustomWrite() - Could not create field." );
 
     if ( mValueBoundsDirty && (mNotEqual( getMinValue(), 0.0f ) || mNotEqual( getMaxValue(), 0.0f )) )
     {
-        pPropertyAlias->addField( particleAssetFieldMinValueName, getMinValue() );
-        pPropertyAlias->addField( particleAssetFieldMaxValueName, getMaxValue() );
+        pAssetField->addField( particleAssetFieldMinValueName, getMinValue() );
+        pAssetField->addField( particleAssetFieldMaxValueName, getMaxValue() );
     }
     
     if ( mValueBoundsDirty && mNotEqual( getMaxTime(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldMaxTimeName, getMaxTime() );
+        pAssetField->addField( particleAssetFieldMaxTimeName, getMaxTime() );
 
     if ( mValueBoundsDirty && mNotEqual( getDefaultValue(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldDefaultValueName, getDefaultValue() );
+        pAssetField->addField( particleAssetFieldDefaultValueName, getDefaultValue() );
 
     if ( mNotEqual( getValueScale(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldValueScaleName, getValueScale() );
+        pAssetField->addField( particleAssetFieldValueScaleName, getValueScale() );
 
     if ( mNotEqual( getRepeatTime(), 1.0f ) )
-        pPropertyAlias->addField( particleAssetFieldRepeatTimeName, getRepeatTime() );
+        pAssetField->addField( particleAssetFieldRepeatTimeName, getRepeatTime() );
 
     // Fetch key count.
     const U32 keyCount = getDataKeyCount();
@@ -549,29 +557,24 @@ void ParticleAssetField::onTamlCustomWrite( TamlCustomProperty* pCustomProperty
     if ( keyCount == 1 && mIsEqual(mDataKeys[0].mTime, 0.0f) && mIsEqual(mDataKeys[0].mValue, mDefaultValue) )
         return;
 
-    // Format the keys,
-    char keysBuffer[MAX_TAML_PROPERTY_FIELDVALUE_LENGTH];
-    char* pKeysBuffer = keysBuffer;
-    S32 bufferSize = sizeof(keysBuffer);
-
     // Iterate the keys.
     for( U32 index = 0; index < keyCount; ++index )
     {
         // Fetch the data key.
         const DataKey& dataKey = mDataKeys[index];
 
-        // Format the key.
-        S32 written = dSprintf( pKeysBuffer, bufferSize, index == 0 ? "%.5g %.5g" : " %.5g %.5g", dataKey.mTime, dataKey.mValue );
-        pKeysBuffer += written;
-        bufferSize -= written;
-    }
+        // Add a key node.
+        TamlCustomNode* pKeyNode = pCustomNode->addNode( particleAssetFieldDataKeyName );
 
-    pPropertyAlias->addField( particleAssetFieldDataKeysName, keysBuffer );
+        // Add key fields.
+        pKeyNode->addField( particleAssetFieldDataKeyTimeName, dataKey.mTime );
+        pKeyNode->addField( particleAssetFieldDataKeyValueName, dataKey.mValue );
+    }
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAlias )
+void ParticleAssetField::onTamlCustomRead( const TamlCustomNode* pCustomNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetField_OnTamlCustomRead);
@@ -590,46 +593,49 @@ void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAli
     // Clear the existing keys.
     mDataKeys.clear();
 
-    // Iterate property fields.
-    for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+    // Fetch fields.
+    const TamlCustomFieldVector& fields = pCustomNode->getFields();
+
+    // Iterate fields.
+    for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
     {
-        // Fetch property field.
-        TamlPropertyField* pPropertyField = *propertyFieldItr;
+        // Fetch field.
+        TamlCustomField* pField = *fieldItr;
 
         // Fetch property field name.
-        StringTableEntry fieldName = pPropertyField->getFieldName();
+        StringTableEntry fieldName = pField->getFieldName();
 
         if ( fieldName == particleAssetFieldRepeatTimeName )
         {
-            pPropertyField->getFieldValue( repeatTime );
+            pField->getFieldValue( repeatTime );
         }
         else if ( fieldName == particleAssetFieldMaxTimeName )
         {
-            pPropertyField->getFieldValue( maxTime );
+            pField->getFieldValue( maxTime );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldMinValueName )
         {
-            pPropertyField->getFieldValue( minValue );
+            pField->getFieldValue( minValue );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldMaxValueName )
         {
-            pPropertyField->getFieldValue( maxValue );
+            pField->getFieldValue( maxValue );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldDefaultValueName )
         {
-            pPropertyField->getFieldValue( defaultValue );
+            pField->getFieldValue( defaultValue );
             mValueBoundsDirty = true;
         }
         else if ( fieldName == particleAssetFieldValueScaleName )
         {
-            pPropertyField->getFieldValue( valueScale );
+            pField->getFieldValue( valueScale );
         }
         else if ( fieldName == particleAssetFieldDataKeysName )
         {
-            const char* pDataKeys = pPropertyField->getFieldValue();
+            const char* pDataKeys = pField->getFieldValue();
             const S32 elementCount = StringUnit::getUnitCount( pDataKeys, " ,\t" );
 
             // Are there a valid number of elements?
@@ -652,6 +658,39 @@ void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAli
         }
     }
 
+    // Fetch any children.
+    const TamlCustomNodeVector& children = pCustomNode->getChildren();
+
+    // Iterate node children.
+    for( TamlCustomNodeVector::const_iterator childItr = children.begin(); childItr != children.end(); ++childItr )
+    {
+        // Fetch node.
+        TamlCustomNode* pKeyNode = *childItr;
+
+        // Ignore anything that isn't a key.
+        if ( pKeyNode->getNodeName() != particleAssetFieldDataKeyName )
+            continue;
+
+        // Fetch the fields.
+        const TamlCustomField* pTimeField = pKeyNode->findField( particleAssetFieldDataKeyTimeName );
+        const TamlCustomField* pValueField = pKeyNode->findField( particleAssetFieldDataKeyValueName );
+
+        // Did we find the fields?
+        if ( pTimeField == NULL || pValueField == NULL )
+        {
+            // No, so warn.
+            Con::warnf("ParticleAssetField::onTamlCustomRead() - Found a key but it did not have a time and value field." );
+
+            continue;
+        }
+
+        // Read key.
+        DataKey key;
+        pTimeField->getFieldValue( key.mTime );
+        pValueField->getFieldValue( key.mValue );
+        keys.push_back( key );
+    }
+
     // Set the value bounds.
     setValueBounds( maxTime, minValue, maxValue, defaultValue );
 
@@ -662,9 +701,5 @@ void ParticleAssetField::onTamlCustomRead( const TamlPropertyAlias* pPropertyAli
     setRepeatTime( repeatTime );
 
     // Set the data keys.
-    for ( S32 index = 0; index < keys.size(); ++index )
-    {
-        const DataKey& key = keys[index];
-        addDataKey( key.mTime, key.mValue );
-    }
+    mDataKeys = keys;
 }

+ 2 - 2
engine/source/2d/assets/ParticleAssetField.h

@@ -109,8 +109,8 @@ public:
     static F32 calculateFieldBVE( const ParticleAssetField& base, const ParticleAssetField& variation, const ParticleAssetField& effect, const F32 effectAge, const bool modulate = false, const F32 modulo = 0.0f );
     static F32 calculateFieldBVLE( const ParticleAssetField& base, const ParticleAssetField& variation, const ParticleAssetField& overlife, const ParticleAssetField& effect, const F32 effectTime, const F32 particleAge, const bool modulate = false, const F32 modulo = 0.0f );
 
-    void onTamlCustomWrite( TamlCustomProperty* pCustomProperty  );
-    void onTamlCustomRead( const TamlPropertyAlias* pPropertyAlias );
+    void onTamlCustomWrite( TamlCustomNode* pCustomNode  );
+    void onTamlCustomRead( const TamlCustomNode* pCustomNode );
 };
 
 //-----------------------------------------------------------------------------

+ 22 - 19
engine/source/2d/assets/ParticleAssetFieldCollection.cc

@@ -24,7 +24,7 @@
 
 //-----------------------------------------------------------------------------
 
-static StringTableEntry particleAssetFieldCollectionName;
+static StringTableEntry particleAssetFieldNodeName;
 
 //-----------------------------------------------------------------------------
 
@@ -32,7 +32,7 @@ ParticleAssetFieldCollection::ParticleAssetFieldCollection() :
                                     mpSelectedField( NULL )
 {
     // Set custom property name.
-    particleAssetFieldCollectionName = StringTable->insert("Fields");
+    particleAssetFieldNodeName = StringTable->insert("Fields");
 }
 
 //-----------------------------------------------------------------------------
@@ -422,7 +422,7 @@ F32 ParticleAssetFieldCollection::getValueScale( void ) const
 
 //------------------------------------------------------------------------------
 
-void ParticleAssetFieldCollection::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void ParticleAssetFieldCollection::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetFieldCollection_OnTamlCustomWrite);
@@ -431,52 +431,55 @@ void ParticleAssetFieldCollection::onTamlCustomWrite( TamlCustomProperties& cust
     if ( mFields.size() == 0 )
         return;
 
-    // Add particle asset custom property.
-    TamlCustomProperty* pParticleAssetCustomProperty = customProperties.addProperty( particleAssetFieldCollectionName );
+    // Add particle asset custom node.
+    TamlCustomNode* pParticleAssetCustomNode = customNodes.addNode( particleAssetFieldNodeName );
 
     // Iterate the fields.
     for( typeFieldHash::iterator fieldItr = mFields.begin(); fieldItr != mFields.end(); ++fieldItr )
     {
-        fieldItr->value->onTamlCustomWrite( pParticleAssetCustomProperty );
+        fieldItr->value->onTamlCustomWrite( pParticleAssetCustomNode );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void ParticleAssetFieldCollection::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void ParticleAssetFieldCollection::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ParticleAssetFieldCollection_OnTamlCustomRead);
 
-    // Find the particle asset custom property.
-    const TamlCustomProperty* pParticleAssetCustomProperty = customProperties.findProperty( particleAssetFieldCollectionName );
+    // Find the particle asset custom node.
+    const TamlCustomNode* pParticleAssetCustomNode = customNodes.findNode( particleAssetFieldNodeName );
 
-    // Finish if we don't have a custom property.
-    if ( pParticleAssetCustomProperty == NULL )
+    // Finish if we don't have a custom node.
+    if ( pParticleAssetCustomNode == NULL )
         return;
 
+    // Fetch children.
+    const TamlCustomNodeVector& children = pParticleAssetCustomNode->getChildren();
+
     // Iterate the custom properties.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pParticleAssetCustomProperty->begin(); propertyAliasItr != pParticleAssetCustomProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator childNodeItr = children.begin(); childNodeItr != children.end(); ++childNodeItr )
     {
-        // Fetch property alias.
-        TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+        // Fetch child node.
+        TamlCustomNode* pChildNode = *childNodeItr;
 
-        // Fetch alias name.
-        StringTableEntry aliasName = pPropertyAlias->mAliasName;
+        // Fetch node name.
+        StringTableEntry nodeName = pChildNode->getNodeName();
 
         // Find the field.
-        ParticleAssetField* pParticleAssetField = findField( aliasName );
+        ParticleAssetField* pParticleAssetField = findField( nodeName );
 
         // Did we find the field?
         if ( pParticleAssetField == NULL )
         {
             // No, so warn.
-            Con::warnf( "ParticleAssetFieldCollection::onTamlCustomRead() - Cannot find data field '%s'.", aliasName );
+            Con::warnf( "ParticleAssetFieldCollection::onTamlCustomRead() - Cannot find data field '%s'.", nodeName );
             continue;
         }
 
         // Read the alias.
-        pParticleAssetField->onTamlCustomRead( pPropertyAlias );
+        pParticleAssetField->onTamlCustomRead( pChildNode );
     }
 }
 

+ 2 - 2
engine/source/2d/assets/ParticleAssetFieldCollection.h

@@ -81,8 +81,8 @@ public:
     bool setValueScale( const F32 valueScale );
     F32 getValueScale( void ) const;    
 
-    void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    void onTamlCustomRead( const TamlCustomNodes& customNodes );
 };
 
 #endif // ParticleAssetFieldCollection

+ 1 - 1
engine/source/2d/controllers/BuoyancyController.cc

@@ -173,7 +173,7 @@ void BuoyancyController::integrate( Scene* pScene, const F32 totalTime, const F3
 
 		areaCenter.x /= area;
 		areaCenter.y /= area;
-        const b2Vec2 localCentroid = b2MulT(pSceneObject->getTransform(), areaCenter);
+        //const b2Vec2 localCentroid = b2MulT(pSceneObject->getTransform(), areaCenter);
 		massCenter.x /= mass;
 		massCenter.y /= mass;
 

+ 1 - 1
engine/source/2d/controllers/PointForceController.cc

@@ -104,7 +104,7 @@ void PointForceController::integrate( Scene* pScene, const F32 totalTime, const
     // Fetch results.
     typeWorldQueryResultVector& queryResults = pWorldQuery->getQueryResults();
 
-    // Add picked objects.
+    // Iterate the results.
     for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
     {
         // Fetch the scene object.

+ 18 - 15
engine/source/2d/core/SpriteBatch.cc

@@ -1160,7 +1160,7 @@ void SpriteBatch::destroySpriteBatchTree( void )
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::onTamlCustomWrite( TamlCustomProperty* pSpritesProperty )
+void SpriteBatch::onTamlCustomWrite( TamlCustomNode* pSpritesNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_TamlCustomWrite);
@@ -1171,38 +1171,41 @@ void SpriteBatch::onTamlCustomWrite( TamlCustomProperty* pSpritesProperty )
     // Write all sprites.
     for( typeSpriteBatchHash::iterator spriteItr = mSprites.begin(); spriteItr != mSprites.end(); ++spriteItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pSpriteAlias = pSpritesProperty->addAlias( spriteItemTypeName );
+        // Add sprite node.
+        TamlCustomNode* pNode = pSpritesNode->addNode( spriteItemTypeName );
         
         // Write type with sprite item.
-        spriteItr->value->onTamlCustomWrite( pSpriteAlias );
+        spriteItr->value->onTamlCustomWrite( pNode );
     }
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatch::onTamlCustomRead( const TamlCustomProperty* pSpritesProperty )
+void SpriteBatch::onTamlCustomRead( const TamlCustomNode* pSpritesNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SpriteBatch_TamlCustomRead);
 
-    // Fetch property names.
-    StringTableEntry spriteItemTypeName = StringTable->insert( "Sprite" );
+    // Fetch node name.
+    StringTableEntry spriteItemNodeName = StringTable->insert( "Sprite" );
+
+    // Fetch children nodes.
+    const TamlCustomNodeVector& spriteNodes = pSpritesNode->getChildren();
 
     // Iterate sprite item types.
-    for( TamlCustomProperty::const_iterator spriteAliasItr = pSpritesProperty->begin(); spriteAliasItr != pSpritesProperty->end(); ++spriteAliasItr )
+    for( TamlCustomNodeVector::const_iterator spriteItr = spriteNodes.begin(); spriteItr != spriteNodes.end(); ++spriteItr )
     {
-        // Fetch sprite alias.
-        TamlPropertyAlias* pSpriteAlias = *spriteAliasItr;
+        // Fetch sprite node.
+        TamlCustomNode* pNode = *spriteItr;
 
         // Fetch alias name.
-        StringTableEntry aliasName = pSpriteAlias->mAliasName;
+        StringTableEntry nodeName = pNode->getNodeName();
 
-        // Is this a known alias?
-        if ( aliasName != spriteItemTypeName )
+        // Is this a known node name?
+        if ( nodeName != spriteItemNodeName )
         {
             // No, so warn.
-            Con::warnf( "SpriteBatch - Unknown custom type '%s'.", aliasName );
+            Con::warnf( "SpriteBatch - Unknown custom type '%s'.", nodeName );
             continue;
         }
 
@@ -1210,7 +1213,7 @@ void SpriteBatch::onTamlCustomRead( const TamlCustomProperty* pSpritesProperty )
         SpriteBatchItem* pSpriteBatchItem = createSprite();
 
         // Read type with sprite item.
-        pSpriteBatchItem->onTamlCustomRead( pSpriteAlias );
+        pSpriteBatchItem->onTamlCustomRead( pNode );
 
         // Fetch logical position.
         const SpriteBatchItem::LogicalPosition& logicalPosition = pSpriteBatchItem->getLogicalPosition();

+ 2 - 2
engine/source/2d/core/SpriteBatch.h

@@ -209,8 +209,8 @@ protected:
     void createSpriteBatchTree( void );
     void destroySpriteBatchTree( void );
 
-    void onTamlCustomWrite( TamlCustomProperty* pSpritesProperty );
-    void onTamlCustomRead( const TamlCustomProperty* pSpritesProperty );
+    void onTamlCustomWrite( TamlCustomNode* pSpritesNode );
+    void onTamlCustomRead( const TamlCustomNode* pSpritesNode );
 
 private:
     bool destroySprite( const U32 batchId );

+ 34 - 28
engine/source/2d/core/SpriteBatchItem.cc

@@ -332,11 +332,11 @@ void SpriteBatchItem::updateWorldTransform( const U32 batchTransformId )
 
 //------------------------------------------------------------------------------
 
-void SpriteBatchItem::onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias )
+void SpriteBatchItem::onTamlCustomWrite( TamlCustomNode* pSpriteNode )
 {
     // Write name.
     if ( getName() != StringTable->EmptyString )
-        pSpriteAlias->addField( spriteNameName, getName() );
+        pSpriteNode->addField( spriteNameName, getName() );
 
     // Write asset.
     if ( isStaticMode() )
@@ -348,10 +348,10 @@ void SpriteBatchItem::onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias )
         if ( assetId != StringTable->EmptyString )
         {
             // Yes, so write image asset Id.
-            pSpriteAlias->addField( spriteImageName, assetId );
+            pSpriteNode->addField( spriteImageName, assetId );
 
             // Write image frame.
-            pSpriteAlias->addField( spriteImageFrameName, getImageFrame() );
+            pSpriteNode->addField( spriteImageFrameName, getImageFrame() );
         }
     }
     else
@@ -363,86 +363,89 @@ void SpriteBatchItem::onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias )
         if ( assetId != StringTable->EmptyString )
         {
             // Yes, so write animation asset Id.
-            pSpriteAlias->addField( spriteAnimationName, assetId );
+            pSpriteNode->addField( spriteAnimationName, assetId );
 
         }
     }
 
     // Write visible.
     if ( !mVisible )
-        pSpriteAlias->addField( spriteVisibleName, mVisible );
+        pSpriteNode->addField( spriteVisibleName, mVisible );
 
     // Write local position.
-    pSpriteAlias->addField( spriteLocalPositionName, mLocalPosition );
+    pSpriteNode->addField( spriteLocalPositionName, mLocalPosition );
 
     // Write local angle.
     if ( mNotZero(mLocalAngle) )
-        pSpriteAlias->addField( spriteLocalAngleName, mRadToDeg(mLocalAngle) );
+        pSpriteNode->addField( spriteLocalAngleName, mRadToDeg(mLocalAngle) );
 
     // Write size.
-    pSpriteAlias->addField( spriteSizeName, mSize );
+    pSpriteNode->addField( spriteSizeName, mSize );
 
     // Write depth.
     if ( mNotZero(mDepth) )
-        pSpriteAlias->addField( spriteDepthName, mDepth );
+        pSpriteNode->addField( spriteDepthName, mDepth );
 
     // Write flipX
     if ( mFlipX )
-        pSpriteAlias->addField( spriteFlipXName, mFlipX );
+        pSpriteNode->addField( spriteFlipXName, mFlipX );
 
     // Write flipY
     if ( mFlipY )
-        pSpriteAlias->addField( spriteFlipYName, mFlipY );
+        pSpriteNode->addField( spriteFlipYName, mFlipY );
 
     // Write sort point.
     if ( mSortPoint.notZero() )
-        pSpriteAlias->addField( spriteSortPointName, mSortPoint );
+        pSpriteNode->addField( spriteSortPointName, mSortPoint );
 
     // Write render group.
     if ( mRenderGroup != StringTable->EmptyString )
-        pSpriteAlias->addField( spriteRenderGroupName, mRenderGroup );
+        pSpriteNode->addField( spriteRenderGroupName, mRenderGroup );
 
     // Write blend mode.
     if ( !mBlendMode )
-        pSpriteAlias->addField( spriteBlendModeName, mBlendMode );
+        pSpriteNode->addField( spriteBlendModeName, mBlendMode );
 
     // Write source blend factor.
     if ( mBlendMode && mSrcBlendFactor != GL_SRC_ALPHA )
-        pSpriteAlias->addField( spriteBlendModeName, SceneObject::getSrcBlendFactorDescription(mSrcBlendFactor) );
+        pSpriteNode->addField( spriteBlendModeName, SceneObject::getSrcBlendFactorDescription(mSrcBlendFactor) );
         
     // Write destination blend factor.
     if ( mBlendMode && mDstBlendFactor != GL_ONE_MINUS_SRC_ALPHA )
-        pSpriteAlias->addField( spriteDstBlendFactorName, SceneObject::getDstBlendFactorDescription(mDstBlendFactor) );
+        pSpriteNode->addField( spriteDstBlendFactorName, SceneObject::getDstBlendFactorDescription(mDstBlendFactor) );
 
     // Write blend color.
     if ( mBlendMode && mBlendColor != ColorF(1.0f, 1.0f, 1.0f, 1.0f) )
-        pSpriteAlias->addField( spriteBlendColorName, mBlendColor );
+        pSpriteNode->addField( spriteBlendColorName, mBlendColor );
 
     // Write alpha test.
     if ( mBlendMode && mAlphaTest >= 0.0f )
-        pSpriteAlias->addField( spriteAlphaTestName, mAlphaTest );
+        pSpriteNode->addField( spriteAlphaTestName, mAlphaTest );
 
     // Write logical position.
     if ( getLogicalPosition().isValid() )
-        pSpriteAlias->addField( spriteLogicalPositionName, getLogicalPosition().getString() );
+        pSpriteNode->addField( spriteLogicalPositionName, getLogicalPosition().getString() );
 
     // Write data object.
     if ( getDataObject() != NULL )
-        pSpriteAlias->addField( spriteDataObjectName, getDataObject() );
+        pSpriteNode->addNode( getDataObject() );
 }
 
 //------------------------------------------------------------------------------
 
-void SpriteBatchItem::onTamlCustomRead( const TamlPropertyAlias* pSpriteAlias )
+void SpriteBatchItem::onTamlCustomRead( const TamlCustomNode* pSpriteNode )
 {
     // Sanity!
     AssertFatal( mSpriteBatch != NULL, "SpriteBatchItem::onTamlCustomRead() - Cannot read sprite batch item with sprite batch." );
 
+    // Fetch sprite fields.
+    const TamlCustomFieldVector& spriteField = pSpriteNode->getFields();
+
     // Iterate property fields.
-    for ( TamlPropertyAlias::const_iterator propertyFieldItr = pSpriteAlias->begin(); propertyFieldItr != pSpriteAlias->end(); ++propertyFieldItr )
+    for ( TamlCustomFieldVector::const_iterator fieldItr = spriteField.begin(); fieldItr != spriteField.end(); ++fieldItr )
     {
         // Fetch sprite field.
-        TamlPropertyField* pSpriteField = *propertyFieldItr;
+        TamlCustomField* pSpriteField = *fieldItr;
 
         // Fetch sprite field name.
         StringTableEntry fieldName = pSpriteField->getFieldName();
@@ -570,9 +573,12 @@ void SpriteBatchItem::onTamlCustomRead( const TamlPropertyAlias* pSpriteAlias )
             // Set logical position.
             setLogicalPosition( LogicalPosition( pLogicalPositionArgs ) );
         }
-        else if ( fieldName == spriteDataObjectName )
-        {            
-            setDataObject( pSpriteField->getFieldObject() );
-        }
     }
+
+    // Fetch sprite children.
+    const TamlCustomNodeVector& spriteChildren = pSpriteNode->getChildren();
+
+    // Set the data object if a single child exists.
+    if ( spriteChildren.size() == 1 )
+        setDataObject( spriteChildren[0]->getProxyObject<SimObject>(true) );
 }

+ 2 - 2
engine/source/2d/core/SpriteBatchItem.h

@@ -294,8 +294,8 @@ protected:
     void updateLocalTransform( void );
     void updateWorldTransform( const U32 batchTransformId );
 
-    void onTamlCustomWrite( TamlPropertyAlias* pSpriteAlias );
-    void onTamlCustomRead( const TamlPropertyAlias* pSpriteAlias );
+    void onTamlCustomWrite( TamlCustomNode* pSpriteNode );
+    void onTamlCustomRead( const TamlCustomNode* pSpriteNode );
 };
 
 //------------------------------------------------------------------------------  

File diff suppressed because it is too large
+ 1136 - 969
engine/source/2d/scene/Scene.cc


+ 3 - 3
engine/source/2d/scene/Scene.h

@@ -291,9 +291,9 @@ private:
 protected:
     /// Taml callbacks.
     virtual void            onTamlPreRead( void );
-    virtual void            onTamlPostRead( const TamlCustomProperties& customProperties );
-    virtual void            onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void            onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void            onTamlPostRead( const TamlCustomNodes& customNodes );
+    virtual void            onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void            onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 public:
     Scene();

+ 14 - 14
engine/source/2d/sceneobject/CompositeSprite.cc

@@ -381,10 +381,10 @@ SpriteBatchItem* CompositeSprite::createCustomLayout( const SpriteBatchItem::Log
 
 //-----------------------------------------------------------------------------
 
-void CompositeSprite::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void CompositeSprite::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Fetch sprite count.
     const U32 spriteCount = getSpriteCount();
@@ -393,27 +393,27 @@ void CompositeSprite::onTamlCustomWrite( TamlCustomProperties& customProperties
     if ( spriteCount == 0 )
         return;
 
-    // Add sprites property.
-    TamlCustomProperty* pSpritesProperty = customProperties.addProperty( StringTable->insert("Sprites") );
+    // Add sprites node.
+    TamlCustomNode* pSpritesNode = customNodes.addNode( StringTable->insert("Sprites") );
 
-    // Write property with sprite batch.
-    SpriteBatch::onTamlCustomWrite( pSpritesProperty );
+    // Write node with sprite batch.
+    SpriteBatch::onTamlCustomWrite( pSpritesNode );
 }
 
 //-----------------------------------------------------------------------------
 
-void CompositeSprite::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void CompositeSprite::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find sprites custom property.
-    const TamlCustomProperty* pSpritesProperty = customProperties.findProperty( StringTable->insert("Sprites") );
+    // Find sprites custom node.
+    const TamlCustomNode* pSpritesNode = customNodes.findNode( StringTable->insert("Sprites") );
 
-    // Finish if we don't have the property.
-    if ( pSpritesProperty == NULL )
+    // Finish if we don't have the node.
+    if ( pSpritesNode == NULL )
         return;
 
-    // Read property with sprite batch.
-    SpriteBatch::onTamlCustomRead( pSpritesProperty );
+    // Read node with sprite batch.
+    SpriteBatch::onTamlCustomRead( pSpritesNode );
 }

+ 2 - 2
engine/source/2d/sceneobject/CompositeSprite.h

@@ -85,8 +85,8 @@ protected:
     virtual SpriteBatchItem* createSpriteIsometricLayout( const SpriteBatchItem::LogicalPosition& logicalPosition );
     virtual SpriteBatchItem* createCustomLayout( const SpriteBatchItem::LogicalPosition& logicalPosition );
 
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 protected:
     static bool         setDefaultSpriteAngle(void* obj, const char* data)                  { STATIC_VOID_CAST_TO(CompositeSprite, SpriteBatch, obj)->setDefaultSpriteAngle(mDegToRad(dAtof(data))); return false; }

+ 276 - 171
engine/source/2d/sceneobject/SceneObject.cc

@@ -87,30 +87,22 @@ static U32 sSceneObjectMasterSerialId = 0;
 // Collision shape property names.
 static bool collisionShapePropertiesInitialized = false;
 
-static StringTableEntry shapeCustomPropertyName;
+static StringTableEntry shapeCustomNodeName;
 
 static StringTableEntry shapeDensityName;
 static StringTableEntry shapeFrictionName;
 static StringTableEntry shapeRestitutionName;
 static StringTableEntry shapeSensorName;
+static StringTableEntry shapePointName;
+static StringTableEntry shapePrevPointName;
+static StringTableEntry shapeNextPointName;
 
 static StringTableEntry circleTypeName;
 static StringTableEntry circleRadiusName;
 static StringTableEntry circleOffsetName;
-
 static StringTableEntry polygonTypeName;
-static StringTableEntry polygonPointName;
-
 static StringTableEntry chainTypeName;
-static StringTableEntry chainPointName;
-static StringTableEntry chainAdjacentStartName;
-static StringTableEntry chainAdjacentEndName;
-
 static StringTableEntry edgeTypeName;
-static StringTableEntry edgeStartName;
-static StringTableEntry edgeEndName;
-static StringTableEntry edgeAdjacentStartName;
-static StringTableEntry edgeAdjacentEndName;
 
 //------------------------------------------------------------------------------
 
@@ -208,30 +200,21 @@ SceneObject::SceneObject() :
     // Initialize collision shape field names.
     if ( !collisionShapePropertiesInitialized )
     {
-        shapeCustomPropertyName     = StringTable->insert( "CollisionShapes" );
+        shapeCustomNodeName     = StringTable->insert( "CollisionShapes" );
 
         shapeDensityName        = StringTable->insert( "Density" );
         shapeFrictionName       = StringTable->insert( "Friction" );
         shapeRestitutionName    = StringTable->insert( "Restitution" );
         shapeSensorName         = StringTable->insert( "Sensor" );
-
+        shapePointName          = StringTable->insert( "Point" );
+        shapePrevPointName      = StringTable->insert( "PreviousPoint" );
+        shapeNextPointName      = StringTable->insert( "NextPoint" );
         circleTypeName          = StringTable->insert( "Circle" );
         circleRadiusName        = StringTable->insert( "Radius" );
         circleOffsetName        = StringTable->insert( "Offset" );
-
         polygonTypeName         = StringTable->insert( "Polygon" );
-        polygonPointName        = StringTable->insert( "Point" );
-
         chainTypeName           = StringTable->insert( "Chain" );
-        chainPointName          = polygonPointName;
-        chainAdjacentStartName  = StringTable->insert( "AdjacentStartPoint" );
-        chainAdjacentEndName    = StringTable->insert( "AdjacentEndPoint" );
-
         edgeTypeName            = StringTable->insert( "Edge" );
-        edgeStartName           = StringTable->insert( "Point0" );
-        edgeEndName             = StringTable->insert( "Point1" );
-        edgeAdjacentStartName   = chainAdjacentStartName;
-        edgeAdjacentEndName     = chainAdjacentEndName;
 
         // Flag as initialized.
         collisionShapePropertiesInitialized = true;
@@ -3371,13 +3354,13 @@ U32 SceneObject::getGlobalSceneObjectCount( void )
 
 //-----------------------------------------------------------------------------
 
-void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void SceneObject::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SceneObject_OnTamlCustomWrite);
 
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Fetch collision shape count.
     const U32 collisionShapeCount = getCollisionShapeCount();
@@ -3386,8 +3369,8 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
     if ( collisionShapeCount == 0 )
         return;
 
-    // Add collision shape property.
-    TamlCustomProperty* pCollisionShapeProperty = customProperties.addProperty( shapeCustomPropertyName );
+    // Add collision shape node.
+    TamlCustomNode* pCustomCollisionShapes = customNodes.addNode( shapeCustomNodeName );
 
     // Iterate collision shapes.
     for ( U32 shapeIndex = 0; shapeIndex < collisionShapeCount; ++shapeIndex )
@@ -3395,30 +3378,30 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
         // Fetch collision shape definition.
         b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );
 
-        // Add collision shape alias.
-        // NOTE:    The name of the alias will get updated shortly.
-        TamlPropertyAlias* pCollisionShapeAlias = pCollisionShapeProperty->addAlias( StringTable->EmptyString );
+        // Add collision shape node.
+        // NOTE:    The name of the node will get updated shortly.
+        TamlCustomNode* pCollisionShapeNode = pCustomCollisionShapes->addNode( StringTable->EmptyString );
 
         // Add common collision shape fields.
         if ( mNotEqual( getDefaultDensity(), fixtureDef.density ) )
-            pCollisionShapeAlias->addField( shapeDensityName, fixtureDef.density );
+            pCollisionShapeNode->addField( shapeDensityName, fixtureDef.density );
 
         if ( mNotEqual( getDefaultFriction(), fixtureDef.friction ) )
-            pCollisionShapeAlias->addField( shapeFrictionName, fixtureDef.friction );
+            pCollisionShapeNode->addField( shapeFrictionName, fixtureDef.friction );
 
         if ( mNotEqual( getDefaultRestitution(), fixtureDef.restitution ) )
-            pCollisionShapeAlias->addField( shapeRestitutionName, fixtureDef.restitution );
+            pCollisionShapeNode->addField( shapeRestitutionName, fixtureDef.restitution );
 
         if ( fixtureDef.isSensor == true )
-            pCollisionShapeAlias->addField( shapeSensorName, fixtureDef.isSensor );
+            pCollisionShapeNode->addField( shapeSensorName, fixtureDef.isSensor );
 
         // Populate collision shape appropriately.
         switch( fixtureDef.shape->GetType() )
         {
         case b2Shape::e_circle:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( circleTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( circleTypeName );
 
                 // Fetch shape.
                 const b2CircleShape* pShape = dynamic_cast<const b2CircleShape*>( fixtureDef.shape );
@@ -3427,18 +3410,18 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid circle shape type returned." );
 
                 // Add radius property.
-                pCollisionShapeAlias->addField( circleRadiusName, pShape->m_radius );
+                pCollisionShapeNode->addField( circleRadiusName, pShape->m_radius );
 
                 // Add offset property (if not zero).
                 if ( !Vector2(pShape->m_p).isZero() )
-                    pCollisionShapeAlias->addField( circleOffsetName, pShape->m_p );
+                    pCollisionShapeNode->addField( circleOffsetName, pShape->m_p );
             }
             break;
 
         case b2Shape::e_polygon:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( polygonTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( polygonTypeName );
 
                 // Fetch shape.
                 const b2PolygonShape* pShape = dynamic_cast<const b2PolygonShape*>( fixtureDef.shape );
@@ -3452,20 +3435,22 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 // Add shape properties.
                 for ( U32 pointIndex = 0; pointIndex < pointCount; ++pointIndex )
                 {
-                    // Format point index name.
-                    char pointIndexBuffer[16];
-                    dSprintf( pointIndexBuffer, sizeof(pointIndexBuffer), "%s%d", polygonPointName, pointIndex );
-                    
-                    // Add point property.
-                    pCollisionShapeAlias->addField( pointIndexBuffer, pShape->GetVertex( pointIndex ) );
+                    // Add point node.
+                    TamlCustomNode* pPointNode = pCollisionShapeNode->addNode( shapePointName );
+
+                    // Fetch point.
+                    const b2Vec2& point = pShape->GetVertex( pointIndex );
+
+                    // Add point fields.
+                    pPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, point );
                 }
             }
             break;
 
         case b2Shape::e_chain:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( chainTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( chainTypeName );
 
                 // Fetch shape.
                 const b2ChainShape* pShape = dynamic_cast<const b2ChainShape*>( fixtureDef.shape );
@@ -3479,28 +3464,33 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 // Add shape properties.
                 for ( U32 pointIndex = 0; pointIndex < pointCount; ++pointIndex )
                 {
-                    // Format point index name.
-                    char pointIndexBuffer[16];
-                    dSprintf( pointIndexBuffer, sizeof(pointIndexBuffer), "%s%d", chainPointName, pointIndex );
-                    
-                    // Add point property.
-                    pCollisionShapeAlias->addField( pointIndexBuffer, pShape->m_vertices[pointIndex] );
+                    // Add point node.
+                    TamlCustomNode* pPointNode = pCollisionShapeNode->addNode( shapePointName );
+
+                    // Add point fields.
+                    pPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertices[pointIndex] );
                 }
 
                 // Add adjacent start point (if specified).
                 if ( pShape->m_hasPrevVertex )
-                    pCollisionShapeAlias->addField( chainAdjacentStartName, pShape->m_prevVertex );
+                {
+                    TamlCustomNode* pPrevPointNode = pCollisionShapeNode->addNode( shapePrevPointName );
+                    pPrevPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_prevVertex );
+                }
 
                 // Add adjacent end point (if specified).
                 if ( pShape->m_hasNextVertex )
-                    pCollisionShapeAlias->addField( chainAdjacentEndName, pShape->m_nextVertex );
+                {
+                    TamlCustomNode* pNextPointNode = pCollisionShapeNode->addNode( shapeNextPointName );
+                    pNextPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_nextVertex );
+                }
             }
             break;
 
         case b2Shape::e_edge:
             {
-                // Set alias name.
-                pCollisionShapeAlias->mAliasName = StringTable->insert( edgeTypeName );
+                // Set node name.
+                pCollisionShapeNode->setNodeName( edgeTypeName );
 
                 // Fetch shape.
                 const b2EdgeShape* pShape = dynamic_cast<const b2EdgeShape*>( fixtureDef.shape );
@@ -3508,17 +3498,27 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
                 // Sanity!
                 AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid edge shape type returned." );
 
-                // Add start/end points.
-                pCollisionShapeAlias->addField( edgeStartName, pShape->m_vertex1 );
-                pCollisionShapeAlias->addField( edgeEndName, pShape->m_vertex2 );
+                // Add start point.
+                TamlCustomNode* pStartPointNode = pCollisionShapeNode->addNode( shapePointName );
+                pStartPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex1 );
+
+                // Add end point.
+                TamlCustomNode* pEndPointNode = pCollisionShapeNode->addNode( shapePointName );
+                pEndPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex2 );
 
                 // Add adjacent start point (if specified).
                 if ( pShape->m_hasVertex0 )
-                    pCollisionShapeAlias->addField( edgeAdjacentStartName, pShape->m_vertex0 );
+                {
+                    TamlCustomNode* pPrevPointNode = pCollisionShapeNode->addNode( shapePrevPointName );
+                    pPrevPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex0 );
+                }
 
                 // Add adjacent end point (if specified).
                 if ( pShape->m_hasVertex3 )
-                    pCollisionShapeAlias->addField( edgeAdjacentEndName, pShape->m_vertex3 );
+                {
+                    TamlCustomNode* pNextPointNode = pCollisionShapeNode->addNode( shapeNextPointName );
+                    pNextPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex3 );
+                }
             }
             break;
 
@@ -3531,29 +3531,32 @@ void SceneObject::onTamlCustomWrite( TamlCustomProperties& customProperties )
 
 //-----------------------------------------------------------------------------
 
-void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void SceneObject::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(SceneObject_OnTamlCustomRead);
 
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find collision shape custom property.
-    const TamlCustomProperty* pCollisionShapeProperty = customProperties.findProperty( shapeCustomPropertyName );
+    // Find collision shape custom node.
+    const TamlCustomNode* pCustomCollisionShapes = customNodes.findNode( shapeCustomNodeName );
 
     // Finish if we don't have collision shapes.
-    if ( pCollisionShapeProperty == NULL )
+    if ( pCustomCollisionShapes == NULL )
         return;
 
+    // Fetch children shapes.
+    const TamlCustomNodeVector& collisionShapeChildren = pCustomCollisionShapes->getChildren();
+
     // Iterate collision shapes.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pCollisionShapeProperty->begin(); propertyAliasItr != pCollisionShapeProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator shapeNodeItr = collisionShapeChildren.begin(); shapeNodeItr != collisionShapeChildren.end(); ++shapeNodeItr )
     {
-        // Fetch property alias.
-        TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+        // Fetch shape node.
+        TamlCustomNode* pShapeNode = *shapeNodeItr;
 
-        // Fetch alias name.
-        StringTableEntry aliasName = pPropertyAlias->mAliasName;
+        // Fetch shape name.
+        StringTableEntry shapeName = pShapeNode->getNodeName();
 
         // Ready common fields.
         F32 shapeDensity     = getDefaultDensity();
@@ -3564,46 +3567,49 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
         S32 shapeIndex;
 
         // Is this a circle shape?
-        if ( aliasName == circleTypeName )
+        if ( shapeName == circleTypeName )
         {
             // Yes, so ready fields.
             F32 radius = 0.0f;
             b2Vec2 offset( 0.0f, 0.0f );
 
+            // Fetch shape children.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
+
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
+                    pField->getFieldValue( shapeSensor );
                 }
                 // Check circle fields.
                 else if ( fieldName == circleRadiusName )
                 {
-                    pPropertyField->getFieldValue( radius );
+                    pField->getFieldValue( radius );
                 }
                 else if ( fieldName == circleOffsetName )
                 {
-                    pPropertyField->getFieldValue( offset );
+                    pField->getFieldValue( offset );
                 }                   
             }
 
@@ -3621,51 +3627,68 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
             shapeIndex = createCircleCollisionShape( radius, offset );
         }
         // Is this a polygon shape?
-        else if ( aliasName == polygonTypeName )
+        else if ( shapeName == polygonTypeName )
         {
-            // Yes, so ready fields.
-            b2Vec2 points[b2_maxPolygonVertices];
-            U32 pointCount = 0;
+            // Yes, so fetch shape fields.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
 
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
+                    pField->getFieldValue( shapeSensor );
                 }
-                // Check polygon fields.
-                else if ( pPropertyField->fieldNameBeginsWith( polygonPointName ) )
+            }
+
+            // Fetch shape children.
+            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();
+
+            // Fetch shape children count.
+            const U32 shapeChildrenCount = (U32)shapeChildren.size();
+
+            // Reset points.
+            b2Vec2 points[b2_maxPolygonVertices];
+            U32 pointCount = 0;
+
+            // Do we have any shape children.
+            if ( shapeChildrenCount > 0 )
+            {
+                // Yes, so iterate them.
+                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                 {
-                    // Is the point count at maximum?
-                    if ( pointCount == b2_maxPolygonVertices )
-                    {
-                        // Yes, so warn.
-                        Con::warnf( "SceneObject::onTamlCustomRead() - Polygon point count exceed the maximum points '%d'.", b2_maxPolygonVertices );
+                    TamlCustomNode* pChildNode = *childItr;
+
+                    // Skip if it's not a point.
+                    if ( pChildNode->getNodeName() != shapePointName )
+                        continue;
+                    
+                    // Skip if it's empty.
+                    if ( pChildNode->getNodeTextField().isValueEmpty() )
                         continue;
-                    }
 
+                    // Read point.
                     b2Vec2 point;
-                    pPropertyField->getFieldValue( point );
+                    pChildNode->getNodeTextField().getFieldValue( point );
                     points[pointCount++] = point;
                 }
             }
@@ -3683,65 +3706,99 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
             shapeIndex = createPolygonCollisionShape( pointCount, points );
         }
         // Is this a chain shape?
-        else if ( aliasName == chainTypeName )
+        else if ( shapeName == chainTypeName )
         {
             // Yes, so ready fields.
             Vector<b2Vec2> points;
-            bool hasAdjacentStartPoint;
-            bool hasAdjacentEndPoint;
+            bool hasAdjacentStartPoint = false;
+            bool hasAdjacentEndPoint = false;
             b2Vec2 adjacentStartPoint;
             b2Vec2 adjacentEndPoint;
 
+            // Fetch shape children.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
+
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
-                }
-                // Check chain fields.
-                else if ( pPropertyField->fieldNameBeginsWith( chainPointName ) )
-                {
-                    b2Vec2 point;
-                    pPropertyField->getFieldValue( point );
-                    points.push_back( point );
-                }
-                else if ( fieldName == chainAdjacentStartName )
-                {
-                    pPropertyField->getFieldValue( adjacentStartPoint );
-                    hasAdjacentStartPoint = true;
+                    pField->getFieldValue( shapeSensor );
                 }
-                else if ( fieldName == chainAdjacentEndName )
+            }
+
+            // Fetch shape children.
+            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();
+
+            // Fetch shape children count.
+            const U32 shapeChildrenCount = (U32)shapeChildren.size();
+
+            // Do we have any shape children.
+            // NOTE: Only do this if the old methods has not been used.
+            if ( points.size() == 0 && shapeChildrenCount > 0 )
+            {
+                // Yes, so iterate them.
+                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                 {
-                    pPropertyField->getFieldValue( adjacentEndPoint );
-                    hasAdjacentEndPoint = true;
+                    TamlCustomNode* pChildNode = *childItr;
+
+                    // Fetch the node name.
+                    StringTableEntry nodeName = pChildNode->getNodeName();
+
+                    // Skip if it's not a point.
+                    if ( !(nodeName == shapePointName || nodeName == shapePrevPointName || nodeName == shapeNextPointName) )
+                        continue;
+                    
+                    // Skip if it's empty.
+                    if ( pChildNode->getNodeTextField().isValueEmpty() )
+                        continue;
+
+                    if ( nodeName == shapePointName )
+                    {
+                        // Read point.
+                        b2Vec2 point;
+                        pChildNode->getNodeTextField().getFieldValue( point );
+                        points.push_back( point );
+                    }
+                    else if ( nodeName == shapePrevPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentStartPoint );
+                        hasAdjacentStartPoint = true;
+                    }
+                    else if ( nodeName == shapeNextPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentEndPoint );
+                        hasAdjacentEndPoint = true;
+                    }
                 }
             }
 
             // Is point count valid?
-            if ( points.size() == 0 )
+            if ( points.size() == 0 || points.size() < 2 )
             {
                 // No, so warn.
-                Con::warnf( "SceneObject::onTamlCustomRead() - No points on chain collision shape." );
+                Con::warnf( "SceneObject::onTamlCustomRead() - No points (or less than two) on chain collision shape." );
 
                 continue;
             }
@@ -3750,63 +3807,111 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
             shapeIndex = createChainCollisionShape( points.size(), points.address(), hasAdjacentStartPoint, hasAdjacentEndPoint, adjacentStartPoint, adjacentEndPoint );
         }
         // Is this an edge shape?
-        else if ( aliasName == edgeTypeName )
+        else if ( shapeName == edgeTypeName )
         {
             // Yes, so ready fields.
             b2Vec2 point0;
             b2Vec2 point1;
-            bool hasAdjacentStartPoint;
-            bool hasAdjacentEndPoint;
+            U32 pointCount = 0;
+            bool hasAdjacentStartPoint = false;
+            bool hasAdjacentEndPoint = false;
             b2Vec2 adjacentStartPoint;
             b2Vec2 adjacentEndPoint;
 
+            // Fetch shape children.
+            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();
+
             // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                const TamlCustomField* pField = *shapeFieldItr;
 
                 // Fetch property field name.
-                StringTableEntry fieldName = pPropertyField->getFieldName();
+                StringTableEntry fieldName = pField->getFieldName();
 
                 // Check common fields.
                 if ( fieldName == shapeDensityName )
                 {
-                    pPropertyField->getFieldValue( shapeDensity );
+                    pField->getFieldValue( shapeDensity );
                 }
                 else if ( fieldName == shapeFrictionName )
                 {
-                    pPropertyField->getFieldValue( shapeFriction );
+                    pField->getFieldValue( shapeFriction );
                 }
                 else if ( fieldName == shapeRestitutionName )
                 {
-                    pPropertyField->getFieldValue( shapeRestitution );
+                    pField->getFieldValue( shapeRestitution );
                 }
                 else if ( fieldName == shapeSensorName )
                 {
-                    pPropertyField->getFieldValue( shapeSensor );
-                }
-                // Check edge fields.
-                else if ( fieldName == edgeStartName )
-                {
-                    pPropertyField->getFieldValue( point0 );
+                    pField->getFieldValue( shapeSensor );
                 }
-                else if ( fieldName == edgeEndName )
-                {
-                    pPropertyField->getFieldValue( point1 );
-                }
-                else if ( fieldName == edgeAdjacentStartName )
-                {
-                    pPropertyField->getFieldValue( adjacentStartPoint );
-                    hasAdjacentStartPoint = true;
-                }
-                else if ( fieldName == edgeAdjacentEndName )
+            }
+
+            // Fetch shape children.
+            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();
+
+            // Fetch shape children count.
+            const U32 shapeChildrenCount = (U32)shapeChildren.size();
+
+            // Do we have any shape children.
+            if ( shapeChildrenCount > 0 )
+            {
+                // Yes, so iterate them.
+                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                 {
-                    pPropertyField->getFieldValue( adjacentEndPoint );
-                    hasAdjacentEndPoint = true;
+                    TamlCustomNode* pChildNode = *childItr;
+
+                    // Fetch the node name.
+                    StringTableEntry nodeName = pChildNode->getNodeName();
+
+                    // Skip if it's not a point.
+                    if ( !(nodeName == shapePointName || nodeName == shapePrevPointName || nodeName == shapeNextPointName) )
+                        continue;
+                    
+                    // Skip if it's empty.
+                    if ( pChildNode->getNodeTextField().isValueEmpty() )
+                        continue;
+
+                    if ( nodeName == shapePointName )
+                    {
+                        // Ignore if too many points.
+                        if ( pointCount >= 2 )
+                            continue;
+
+                        // Read point.               
+                        if ( pointCount == 0 )
+                            pChildNode->getNodeTextField().getFieldValue( point0 );
+                        else
+                            pChildNode->getNodeTextField().getFieldValue( point1 );
+
+                        pointCount++;
+                    }
+                    else if ( nodeName == shapePrevPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentStartPoint );
+                        hasAdjacentStartPoint = true;
+                    }
+                    else if ( nodeName == shapeNextPointName )
+                    {
+                        // Read adjacent point.
+                        pChildNode->getNodeTextField().getFieldValue( adjacentEndPoint );
+                        hasAdjacentEndPoint = true;
+                    }
                 }
             }
 
+            // Is point count valid?
+            if ( pointCount == 0 || pointCount != 2 )
+            {
+                // No, so warn.
+                Con::warnf( "SceneObject::onTamlCustomRead() - No points (or not two points) on edge collision shape." );
+
+                continue;
+            }
+
             // Create shape.
             shapeIndex = createEdgeCollisionShape( point0, point1, hasAdjacentStartPoint, hasAdjacentEndPoint, adjacentStartPoint, adjacentEndPoint );
         }
@@ -3814,7 +3919,7 @@ void SceneObject::onTamlCustomRead( const TamlCustomProperties& customProperties
         else
         {
             // Warn.
-            Con::warnf( "Unknown shape type of '%s' encountered.", aliasName );
+            Con::warnf( "Unknown shape type of '%s' encountered.", shapeName );
 
             // Sanity!
             AssertFatal( false, "SceneObject::onTamlCustomRead() - Unknown shape type detected." );
@@ -3873,9 +3978,9 @@ S32 QSORT_CALLBACK SceneObject::sceneObjectLayerDepthSort(const void* a, const v
 
 static EnumTable::Enums bodyTypeLookup[] =
                 {
-                { b2_staticBody,    "static"    },
-                { b2_kinematicBody, "kinematic" },
-                { b2_dynamicBody,   "dynamic"   },
+                { b2_staticBody,    "Static"    },
+                { b2_kinematicBody, "Kinematic" },
+                { b2_dynamicBody,   "Dynamic"   },
                 };
 
 EnumTable bodyTypeTable(sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums), &bodyTypeLookup[0]);
@@ -3884,10 +3989,10 @@ EnumTable bodyTypeTable(sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums), &body
 
 static EnumTable::Enums collisionShapeTypeLookup[] =
                 {
-                { b2Shape::e_circle,             "circle"   },
-                { b2Shape::e_edge,               "edge"     },
-                { b2Shape::e_polygon,            "polygon"  },
-                { b2Shape::e_chain,              "chain"    },
+                { b2Shape::e_circle,             "Circle"   },
+                { b2Shape::e_edge,               "Edge"     },
+                { b2Shape::e_polygon,            "Polygon"  },
+                { b2Shape::e_chain,              "Chain"    },
                 };
 
 EnumTable collisionShapeTypeTable(sizeof(collisionShapeTypeLookup) / sizeof(EnumTable::Enums), &collisionShapeTypeLookup[0]);

+ 2 - 2
engine/source/2d/sceneobject/SceneObject.h

@@ -241,8 +241,8 @@ protected:
     void                    initializeContactGathering( void );
 
     /// Taml callbacks.
-    virtual void            onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void            onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void            onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void            onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 public:
     SceneObject();

+ 1 - 1
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -1617,7 +1617,7 @@ ConsoleMethod(SceneObject, getAngularDamping, F32, 2, 2, "() - Gets the angular
 
 //-----------------------------------------------------------------------------
 
-ConsoleMethod(SceneObject, moveTo, bool, 4, 7,  "(worldPoint X/Y, time, [autoStop = true], [warpToTarget = true]) - Moves the object to the specified world point.\n"
+ConsoleMethod(SceneObject, moveTo, bool, 4, 7,  "(worldPoint X/Y, speed, [autoStop = true], [warpToTarget = true]) - Moves the object to the specified world point.\n"
                                                 "The point is moved by calculating the initial linear velocity required and applies it.\n"
                                                 "The object may never reach the point if it has linear damping applied or collides with another object.\n"
                                                 "@param worldPoint/Y The world point to move the object to.\n"

+ 26 - 23
engine/source/assets/assetQuery.cc

@@ -51,13 +51,13 @@ void AssetQuery::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
-void AssetQuery::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void AssetQuery::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
-    // Add property.
-    TamlCustomProperty* pProperty = customProperties.addProperty( ASSETQUERY_CUSTO_PROPERTY_NAME );
+    // Add node.
+    TamlCustomNode* pCustomNode = customNodes.addNode( ASSETQUERY_RESULTS_NODE_NAME );
 
     // Finish if no assets.
     if ( size() == 0 )
@@ -66,43 +66,46 @@ void AssetQuery::onTamlCustomWrite( TamlCustomProperties& customProperties )
     // Iterate asset.
     for( Vector<StringTableEntry>::iterator assetItr = begin(); assetItr != end(); ++assetItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pAlias = pProperty->addAlias( ASSETQUERY_TYPE_NAME );
+        // Add asset node.
+        TamlCustomNode* pAssetNode = pCustomNode->addNode( ASSETQUERY_ASSETNODE_NAME );
 
-        // Add fields.
-        pAlias->addField( ASSETQUERY_ASSETID_FIELD_NAME, *assetItr );
+        // Add field.
+        pAssetNode->addField( ASSETQUERY_ASSETID_FIELD_NAME, *assetItr );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void AssetQuery::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void AssetQuery::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find custom property name.
-    const TamlCustomProperty* pProperty = customProperties.findProperty( ASSETQUERY_CUSTO_PROPERTY_NAME );
+    // Find custom node name.
+    const TamlCustomNode* pResultsNode = customNodes.findNode( ASSETQUERY_RESULTS_NODE_NAME );
 
-    // Finish if we don't have a property name.
-    if ( pProperty == NULL )
+    // Finish if we don't have a results name.
+    if ( pResultsNode == NULL )
         return;
 
-    // Fetch alias name.
-    StringTableEntry typeAliasName = StringTable->insert( ASSETQUERY_TYPE_NAME );
+    // Fetch node name.
+    StringTableEntry assetNodeName = StringTable->insert( ASSETQUERY_ASSETNODE_NAME );
 
-    // Iterate property alias.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pProperty->begin(); propertyAliasItr != pProperty->end(); ++propertyAliasItr )
+    // Fetch children asset nodes.
+    const TamlCustomNodeVector& assetNodes = pResultsNode->getChildren();
+
+    // Iterate asset nodes.
+    for( TamlCustomNodeVector::const_iterator assetNodeItr = assetNodes.begin(); assetNodeItr != assetNodes.end(); ++assetNodeItr )
     {
-        // Fetch property alias.
-        const TamlPropertyAlias* pAlias = *propertyAliasItr;
+        // Fetch asset node.
+        const TamlCustomNode* pAssetNode = *assetNodeItr;
 
-        // Skip if an unknown alias name.
-        if ( pAlias->mAliasName != typeAliasName )
+        // Skip if an unknown node name.
+        if ( pAssetNode->getNodeName() != assetNodeName )
             continue;
 
         // Fetch field.
-        const TamlPropertyField* pField = pAlias->findField( ASSETQUERY_ASSETID_FIELD_NAME );
+        const TamlCustomField* pField = pAssetNode->findField( ASSETQUERY_ASSETID_FIELD_NAME );
 
         // Do we find the field?
         if ( pField == NULL )

+ 4 - 4
engine/source/assets/assetQuery.h

@@ -37,8 +37,8 @@
 
 //-----------------------------------------------------------------------------
 
-#define ASSETQUERY_CUSTO_PROPERTY_NAME      "Results"
-#define ASSETQUERY_TYPE_NAME            "Asset"
+#define ASSETQUERY_RESULTS_NODE_NAME     "Results"
+#define ASSETQUERY_ASSETNODE_NAME        "Asset"
 #define ASSETQUERY_ASSETID_FIELD_NAME   "AssetId"
 
 //-----------------------------------------------------------------------------
@@ -49,8 +49,8 @@ private:
     typedef SimObject Parent;
 
 protected:
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
     static const char* getCount(void* obj, const char* data) { return Con::getIntArg(static_cast<AssetQuery*>(obj)->size()); }
     static bool writeCount( void* obj, StringTableEntry pFieldName ) { return false; }

+ 45 - 39
engine/source/assets/assetTagsManifest.cc

@@ -118,72 +118,75 @@ void AssetTagsManifest::renameAssetId( const char* pAssetIdFrom, const char* pAs
 
 //-----------------------------------------------------------------------------
 
-void AssetTagsManifest::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void AssetTagsManifest::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Finish if no tags.
     if ( mTagNameDatabase.size() == 0 )
         return;
 
-    // Add property.
-    TamlCustomProperty* pTagProperty = customProperties.addProperty( ASSETTAGS_TAGS_CUSTOMPROPERTY_NAME );
+    // Add node.
+    TamlCustomNode* pTagsNode = customNodes.addNode( ASSETTAGS_TAGS_NODE_NAME );
 
     // Iterate tags.
     for( typeTagNameHash::iterator tagItr = mTagNameDatabase.begin(); tagItr != mTagNameDatabase.end(); ++tagItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pAlias = pTagProperty->addAlias( ASSETTAGS_TAGS_TYPE_NAME );
+        // Add tag node.
+        TamlCustomNode* pTagNode = pTagsNode->addNode( ASSETTAGS_TAGS_TYPE_NAME );
 
         // Add fields.
-        pAlias->addField( ASSETTAGS_TAGS_NAME_FIELD, tagItr->key );
+        pTagNode->addField( ASSETTAGS_TAGS_NAME_FIELD, tagItr->key );
     }
 
-    // Add property.
-    TamlCustomProperty* pAssetTagProperty = customProperties.addProperty( ASSETTAGS_ASSETS_CUSTOMPROPERTY_NAME );
+    // Add node.
+    TamlCustomNode* pAssetTagsNode = customNodes.addNode( ASSETTAGS_ASSETS_NODE_NAME );
 
     // Iterate asset locations.
     for( typeAssetToTagHash::iterator assetTagItr = mAssetToTagDatabase.begin(); assetTagItr != mAssetToTagDatabase.end(); ++assetTagItr )
     {
-        // Add alias.
-        TamlPropertyAlias* pAlias = pAssetTagProperty->addAlias( ASSETTAGS_ASSETS_TYPE_NAME );
+        // Add asset tag node.
+        TamlCustomNode* pAssetNode = pAssetTagsNode->addNode( ASSETTAGS_ASSETS_TYPE_NAME );
 
         // Add fields.
-        pAlias->addField( ASSETTAGS_ASSETS_ASSETID_FIELD, assetTagItr->key );
-        pAlias->addField( ASSETTAGS_ASSETS_TAG_FIELD, assetTagItr->value->mTagName );
+        pAssetNode->addField( ASSETTAGS_ASSETS_ASSETID_FIELD, assetTagItr->key );
+        pAssetNode->addField( ASSETTAGS_ASSETS_TAG_FIELD, assetTagItr->value->mTagName );
     }
 }
 
 //-----------------------------------------------------------------------------
 
-void AssetTagsManifest::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void AssetTagsManifest::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find custom property name.
-    const TamlCustomProperty* pTagProperty = customProperties.findProperty( ASSETTAGS_TAGS_CUSTOMPROPERTY_NAME );
+    // Find tags nodes.
+    const TamlCustomNode* pTagProperty = customNodes.findNode( ASSETTAGS_TAGS_NODE_NAME );
 
-    // Finish if we don't have a property name.
+    // Finish if we don't have a tags node name.
     if ( pTagProperty == NULL )
         return;
 
-    // Fetch alias name.
-    StringTableEntry tagAliasName = StringTable->insert( ASSETTAGS_TAGS_TYPE_NAME );
+    // Fetch node name.
+    StringTableEntry tagNodeName = StringTable->insert( ASSETTAGS_TAGS_TYPE_NAME );
 
-    // Iterate property alias.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pTagProperty->begin(); propertyAliasItr != pTagProperty->end(); ++propertyAliasItr )
+    // Fetch children asset nodes.
+    const TamlCustomNodeVector& tagNodes = pTagProperty->getChildren();
+
+    // Iterate tag nodes.
+    for( TamlCustomNodeVector::const_iterator tagNodeItr = tagNodes.begin(); tagNodeItr != tagNodes.end(); ++tagNodeItr )
     {
-        // Fetch property alias.
-        const TamlPropertyAlias* pAlias = *propertyAliasItr;
+        // Fetch tag node.
+        const TamlCustomNode* pTagNode = *tagNodeItr;
 
-        // Skip if an unknown alias name.
-        if ( pAlias->mAliasName != tagAliasName )
+        // Skip if an unknown node name.
+        if ( pTagNode->getNodeName() != tagNodeName )
             continue;
 
         // Fetch "Name" field.
-        const TamlPropertyField* pTagNameField = pAlias->findField( ASSETTAGS_TAGS_NAME_FIELD );
+        const TamlCustomField* pTagNameField = pTagNode->findField( ASSETTAGS_TAGS_NAME_FIELD );
 
         // Do we find the field?
         if ( pTagNameField == NULL )
@@ -197,28 +200,31 @@ void AssetTagsManifest::onTamlCustomRead( const TamlCustomProperties& customProp
         createTag( pTagNameField->getFieldValue() );
     }
 
-    // Find custom property name.
-    const TamlCustomProperty* pAssetTagProperty = customProperties.findProperty( ASSETTAGS_ASSETS_CUSTOMPROPERTY_NAME );
+    // Find asset tags node.
+    const TamlCustomNode* pAssetTagProperty = customNodes.findNode( ASSETTAGS_ASSETS_NODE_NAME );
 
-    // Finish if we don't have a property name.
+    // Finish if we don't have an asset tags node name.
     if ( pAssetTagProperty == NULL )
         return;
 
-    // Fetch alias name.
-    StringTableEntry assetTagAliasName = StringTable->insert( ASSETTAGS_ASSETS_TYPE_NAME );
+    // Fetch node name.
+    StringTableEntry assetTagNodeName = StringTable->insert( ASSETTAGS_ASSETS_TYPE_NAME );
+
+    // Fetch children asset tag nodes.
+    const TamlCustomNodeVector& assetTagNodes = pAssetTagProperty->getChildren();
 
     // Iterate property alias.
-    for( TamlCustomProperty::const_iterator propertyAliasItr = pAssetTagProperty->begin(); propertyAliasItr != pAssetTagProperty->end(); ++propertyAliasItr )
+    for( TamlCustomNodeVector::const_iterator assetTagNodeItr = assetTagNodes.begin(); assetTagNodeItr != assetTagNodes.end(); ++assetTagNodeItr )
     {
-        // Fetch property alias.
-        const TamlPropertyAlias* pAlias = *propertyAliasItr;
+        // Fetch asset node.
+        const TamlCustomNode* pAssetTagNode = *assetTagNodeItr;
 
-        // Skip if an unknown alias name.
-        if ( pAlias->mAliasName != assetTagAliasName )
+        // Skip if an unknown node name.
+        if ( pAssetTagNode->getNodeName() != assetTagNodeName )
             continue;
 
         // Fetch "AssetId" field.
-        const TamlPropertyField* pAssetIdField = pAlias->findField( ASSETTAGS_ASSETS_ASSETID_FIELD );
+        const TamlCustomField* pAssetIdField = pAssetTagNode->findField( ASSETTAGS_ASSETS_ASSETID_FIELD );
 
         // Do we find the field?
         if ( pAssetIdField == NULL )
@@ -229,7 +235,7 @@ void AssetTagsManifest::onTamlCustomRead( const TamlCustomProperties& customProp
         }
 
         // Fetch "Tag" field.
-        const TamlPropertyField* pTagField = pAlias->findField( ASSETTAGS_ASSETS_TAG_FIELD );
+        const TamlCustomField* pTagField = pAssetTagNode->findField( ASSETTAGS_ASSETS_TAG_FIELD );
 
         // Do we find the field?
         if ( pTagField == NULL )

+ 4 - 4
engine/source/assets/assetTagsManifest.h

@@ -41,11 +41,11 @@
 
 //-----------------------------------------------------------------------------
 
-#define ASSETTAGS_TAGS_CUSTOMPROPERTY_NAME      "Tags"
+#define ASSETTAGS_TAGS_NODE_NAME                "Tags"
 #define ASSETTAGS_TAGS_TYPE_NAME                "Tag"
 #define ASSETTAGS_TAGS_NAME_FIELD               "Name"
 
-#define ASSETTAGS_ASSETS_CUSTOMPROPERTY_NAME    "Assets"
+#define ASSETTAGS_ASSETS_NODE_NAME              "Assets"
 #define ASSETTAGS_ASSETS_TYPE_NAME              "Tag"
 #define ASSETTAGS_ASSETS_ASSETID_FIELD          "AssetId"
 #define ASSETTAGS_ASSETS_TAG_FIELD              "Name"
@@ -120,8 +120,8 @@ private:
     void renameAssetId( const char* pAssetIdFrom, const char* pAssetIdTo );
 
 protected:
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 public:
     AssetTagsManifest();

+ 61 - 49
engine/source/component/behaviors/behaviorComponent.cpp

@@ -886,10 +886,10 @@ const BehaviorComponent::typePortConnectionVector* BehaviorComponent::getBehavio
 
 //-----------------------------------------------------------------------------
 
-void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customProperties )
+void BehaviorComponent::onTamlCustomWrite( TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomWrite( customProperties );
+    Parent::onTamlCustomWrite( customNodes );
 
     // Fetch behavior count.
     const U32 behaviorCount = (U32)mBehaviors.size();
@@ -901,8 +901,8 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
     // Fetch behavior template asset field type.
     StringTableEntry behaviorTemplateAssetFieldType = StringTable->insert( BEHAVIORTEMPLATE_ASSET_FIELDTYPE );
 
-    // Add behavior property.
-    TamlCustomProperty* pBehaviorProperty = customProperties.addProperty( BEHAVIOR_CUSTOMPROPERTY_NAME );
+    // Add custom behaviors node.
+    TamlCustomNode* pCustomBehaviorNode = customNodes.addNode( BEHAVIOR_NODE_NAME );
 
     // Iterate behaviors.
     for( SimSet::iterator behaviorItr = mBehaviors.begin(); behaviorItr != mBehaviors.end(); ++behaviorItr )
@@ -913,11 +913,11 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
         // Fetch template.
         BehaviorTemplate* pBehaviorTemplate = pBehaviorInstance->getTemplate();
 
-        // Add behavior alias.
-        TamlPropertyAlias* pBehaviorAlias = pBehaviorProperty->addAlias( pBehaviorInstance->getTemplateName() );
+        // Add behavior node.
+        TamlCustomNode* pBehaviorNode = pCustomBehaviorNode->addNode( pBehaviorInstance->getTemplateName() );
 
         // Add behavior Id field.
-        pBehaviorAlias->addField( BEHAVIOR_ID_FIELD_NAME, pBehaviorInstance->getBehaviorId() );
+        pBehaviorNode->addField( BEHAVIOR_ID_FIELD_NAME, pBehaviorInstance->getBehaviorId() );
 
         // Fetch field count,
         const U32 behaviorFieldCount = pBehaviorTemplate->getBehaviorFieldCount();
@@ -942,7 +942,7 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
             const char* pFieldValue = pBehaviorInstance->getPrefixedDynamicDataField( pBehaviorField->mName, NULL, fieldType );
 
             // Add behavior field.
-            pBehaviorAlias->addField( pBehaviorField->mName, pFieldValue );
+            pBehaviorNode->addField( pBehaviorField->mName, pFieldValue );
         }
     }
 
@@ -953,8 +953,8 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
     if ( behaviorConnectionCount == 0 )
         return;
 
-    // Add behavior connection property.
-    TamlCustomProperty* pConnectionProperty = customProperties.addProperty( BEHAVIOR_CONNECTION_CUSTOMPROPERTY_NAME );
+    // Add custom behavior connection property.
+    TamlCustomNode* pCustomConnection = customNodes.addNode( BEHAVIOR_CONNECTION_NODE_NAME );
     
     // Iterate instance connections.
     for( typeInstanceConnectionHash::iterator instanceItr = mBehaviorConnections.begin(); instanceItr != mBehaviorConnections.end(); ++instanceItr )
@@ -974,12 +974,12 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
                 // Fetch connection.
                 BehaviorPortConnection* pConnection = connectionItr;
 
-                // Add connection alias.
-                TamlPropertyAlias* pConnectionAlias = pConnectionProperty->addAlias( BEHAVIOR_CONNECTION_TYPE_NAME );
+                // Add connectionnode.
+                TamlCustomNode* pConnectionNode = pCustomConnection->addNode( BEHAVIOR_CONNECTION_TYPE_NAME );
 
                 // Add behavior field.
-                pConnectionAlias->addField( pConnection->mOutputName, pConnection->mOutputInstance->getBehaviorId() );
-                pConnectionAlias->addField( pConnection->mInputName, pConnection->mInputInstance->getBehaviorId() );
+                pConnectionNode->addField( pConnection->mOutputName, pConnection->mOutputInstance->getBehaviorId() );
+                pConnectionNode->addField( pConnection->mInputName, pConnection->mInputInstance->getBehaviorId() );
             }
         }
     }
@@ -987,16 +987,16 @@ void BehaviorComponent::onTamlCustomWrite( TamlCustomProperties& customPropertie
 
 //-----------------------------------------------------------------------------
 
-void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProperties )
+void BehaviorComponent::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Call parent.
-    Parent::onTamlCustomRead( customProperties );
+    Parent::onTamlCustomRead( customNodes );
 
-    // Find behavior custom property name.
-    const TamlCustomProperty* pCustomProperty = customProperties.findProperty( BEHAVIOR_CUSTOMPROPERTY_NAME );
+    // Find custom behaviors node.
+    const TamlCustomNode* pCustomBehaviorNode = customNodes.findNode( BEHAVIOR_NODE_NAME );
 
     // Do we have the property?
-    if ( pCustomProperty != NULL )
+    if ( pCustomBehaviorNode != NULL )
     {
         // Yes, so reset maximum behavior Id.
         S32 maximumBehaviorId = 0;
@@ -1007,23 +1007,26 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
         // Fetch behavior template asset field type.
         StringTableEntry behaviorTemplateAssetFieldType = StringTable->insert( BEHAVIORTEMPLATE_ASSET_FIELDTYPE );
 
-        // Iterate property alias.
-        for( TamlCustomProperty::const_iterator propertyAliasItr = pCustomProperty->begin(); propertyAliasItr != pCustomProperty->end(); ++propertyAliasItr )
+        // Fetch children behavior nodes.
+        const TamlCustomNodeVector& behaviorNodes = pCustomBehaviorNode->getChildren();
+
+        // Iterate behavior nodes.
+        for( TamlCustomNodeVector::const_iterator behaviorNodeItr = behaviorNodes.begin(); behaviorNodeItr != behaviorNodes.end(); ++behaviorNodeItr )
         {
-            // Fetch property alias.
-            TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+            // Fetch behavior node.
+            TamlCustomNode* pBehaviorNode = *behaviorNodeItr;
 
             // Fetch template.
-            BehaviorTemplate* pTemplate = dynamic_cast<BehaviorTemplate *>( Sim::findObject( pPropertyAlias->mAliasName ) );
+            BehaviorTemplate* pTemplate = dynamic_cast<BehaviorTemplate *>( Sim::findObject( pBehaviorNode->getNodeName() ) );
 
             // Find template?
             if( pTemplate == NULL )
             {
                 // No, so warn appropriately.
-                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Missing Behavior '%s'", pPropertyAlias->mAliasName );
+                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Missing Behavior '%s'", pBehaviorNode->getNodeName() );
 
                 if( isMethod( "onBehaviorMissing" ) )
-                    Con::executef( this, 2, "onBehaviorMissing", pPropertyAlias->mAliasName );
+                    Con::executef( this, 2, "onBehaviorMissing", pBehaviorNode->getNodeName() );
 
                 // Skip it.
                 continue;
@@ -1036,10 +1039,10 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
             if ( pBehaviorInstance == NULL )
             {
                 // No, so warn appropriately.
-                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Found behavior could not create an instance '%s'", pPropertyAlias->mAliasName );
+                Con::warnf( "BehaviorComponent::onTamlCustomRead() - Found behavior could not create an instance '%s'", pBehaviorNode->getNodeName() );
 
                 if( isMethod( "onBehaviorMissing" ) )
-                    Con::executef( this, 2, "onBehaviorMissing", pPropertyAlias->mAliasName );
+                    Con::executef( this, 2, "onBehaviorMissing", pBehaviorNode->getNodeName() );
 
                 // Skip it.
                 continue;
@@ -1047,17 +1050,20 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
 
             S32 behaviorId = 0;
 
-            // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
+            // Fetch field nodes.
+            const TamlCustomFieldVector& fields = pBehaviorNode->getFields();
+
+            // Iterate fields.
+            for ( TamlCustomFieldVector::const_iterator nodeFieldItr = fields.begin(); nodeFieldItr != fields.end(); ++nodeFieldItr )
             {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
+                // Fetch field.
+                TamlCustomField* pField = *nodeFieldItr;
 
                 // Fetch field name.
-                const char* pFieldName = pPropertyField->getFieldName();
+                const char* pFieldName = pField->getFieldName();
 
                 // Fetch field value.
-                const char* pFieldValue = pPropertyField->getFieldValue();
+                const char* pFieldValue = pField->getFieldValue();
 
                 // Is this the behavior field Id name?
                 if ( pFieldName == behaviorFieldIdName )
@@ -1071,7 +1077,7 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
                         // No, so warn.
                         Con::warnf( "BehaviorComponent::onTamlCustomRead() - Encountered an invalid behavior Id of '%d' on behavior '%s'.",
                             behaviorId,
-                            pPropertyAlias->mAliasName );
+                            pBehaviorNode->getNodeName() );
                     }
 
                     // Update maximum behavior Id found.
@@ -1096,7 +1102,7 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
                 }
 
                 // Set field.
-                pBehaviorInstance->setPrefixedDynamicDataField( pPropertyField->getFieldName(), NULL, pPropertyField->getFieldValue(), fieldType );
+                pBehaviorInstance->setPrefixedDynamicDataField( pField->getFieldName(), NULL, pField->getFieldValue(), fieldType );
             }
 
             // Add behavior.
@@ -1111,27 +1117,33 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
         mMasterBehaviorId = (U32)maximumBehaviorId+1;
     }
 
-    // Find behavior connections custom property name.
-    const TamlCustomProperty* pConnectionProperty = customProperties.findProperty( BEHAVIOR_CONNECTION_CUSTOMPROPERTY_NAME );
+    // Find behavior connections custom node.
+    const TamlCustomNode* pCustomConnectionNode = customNodes.findNode( BEHAVIOR_CONNECTION_NODE_NAME );
 
-    // Do we have the property?
-    if ( pConnectionProperty != NULL )
+    // Do we have the custom connection node?
+    if ( pCustomConnectionNode != NULL )
     {
-        // Yes, so insert connection alias.
-        StringTableEntry connectionAlias = StringTable->insert( BEHAVIOR_CONNECTION_TYPE_NAME );
+        // Yes, so fetch the connection node name..
+        StringTableEntry connectionNodeName = StringTable->insert( BEHAVIOR_CONNECTION_TYPE_NAME );
+
+        // Fetch children connection nodes.
+        const TamlCustomNodeVector& connectionNodes = pCustomConnectionNode->getChildren();
 
         // Iterate property alias.
-        for( TamlCustomProperty::const_iterator propertyAliasItr = pConnectionProperty->begin(); propertyAliasItr != pConnectionProperty->end(); ++propertyAliasItr )
+        for( TamlCustomNodeVector::const_iterator connectionNodeItr = connectionNodes.begin(); connectionNodeItr != connectionNodes.end(); ++connectionNodeItr )
         {
-            // Fetch property alias.
-            TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
+            // Fetch connection node.
+            TamlCustomNode* pConnectionNode = *connectionNodeItr;
 
             // Skip if the alias isn't a connection.
-            if ( pPropertyAlias->mAliasName != connectionAlias )
+            if ( pConnectionNode->getNodeName() != connectionNodeName )
                 continue;
 
+            // Fetch field nodes.
+            const TamlCustomFieldVector& connectionFieldNodes = pConnectionNode->getFields();
+
             // Are there two properties?
-            if ( pPropertyAlias->size() != 2 )
+            if ( connectionFieldNodes.size() != 2 )
             {
                 // No, so warn.
                 Con::warnf( "BehaviorComponent::onTamlCustomRead() - Encountered a behavior connection with more than two connection fields." );
@@ -1139,8 +1151,8 @@ void BehaviorComponent::onTamlCustomRead( const TamlCustomProperties& customProp
             }
 
             // Fetch property field #1.
-            TamlPropertyField* pPropertyField1 = *pPropertyAlias->begin();
-            TamlPropertyField* pPropertyField2 = *(pPropertyAlias->begin()+1);
+            TamlCustomField* pPropertyField1 = *connectionFieldNodes.begin();
+            TamlCustomField* pPropertyField2 = *(connectionFieldNodes.begin()+1);
            
             // Fetch behavior instances #1.
             BehaviorInstance* pBehaviorInstance1 = getBehaviorByInstanceId( dAtoi( pPropertyField1->getFieldValue() ) );

+ 4 - 4
engine/source/component/behaviors/behaviorComponent.h

@@ -34,8 +34,8 @@
 //-----------------------------------------------------------------------------
 
 #define BEHAVIOR_ID_FIELD_NAME                  "Id"
-#define BEHAVIOR_CUSTOMPROPERTY_NAME                "Behaviors"
-#define BEHAVIOR_CONNECTION_CUSTOMPROPERTY_NAME     "BehaviorConnections"
+#define BEHAVIOR_NODE_NAME                      "Behaviors"
+#define BEHAVIOR_CONNECTION_NODE_NAME           "BehaviorConnections"
 #define BEHAVIOR_CONNECTION_TYPE_NAME           "Connection"
 
 //-----------------------------------------------------------------------------
@@ -88,8 +88,8 @@ protected:
     virtual const char* _callMethod( U32 argc, const char *argv[], bool callThis = true );
 
     /// Taml callbacks.
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties );
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 private:
     void destroyBehaviorOutputConnections( BehaviorInstance* pOutputBehavior );

+ 0 - 2
engine/source/memory/frameAllocator.h

@@ -281,12 +281,10 @@ public:
 
    // This ifdef is to satisfy the ever so pedantic GCC compiler
    //  Which seems to upset visual studio.
-#if defined(TORQUE_COMPILER_GCC)   
    T& operator[]( const U32 idx ) { return mMemory[idx]; }
    const T& operator[]( const U32 idx ) const { return mMemory[idx]; }
    T& operator[]( const S32 idx ) { return mMemory[idx]; }
    const T& operator[]( const S32 idx ) const { return mMemory[idx]; }   
-#endif
 };
 
 //-----------------------------------------------------------------------------

+ 2454 - 2453
engine/source/module/moduleManager.cc

@@ -1,2453 +1,2454 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#include "moduleManager.h"
-
-#ifndef _MODULE_MERGE_DEFINITION_H
-#include "moduleMergeDefinition.h"
-#endif
-
-#ifndef _TAML_MODULE_ID_UPDATE_VISITOR_H_
-#include "tamlModuleIdUpdateVisitor.h"
-#endif
-
-#ifndef _MODULE_CALLBACKS_H_
-#include "moduleCallbacks.h"
-#endif
-
-#ifndef _CONSOLETYPES_H_
-#include "console/consoleTypes.h"
-#endif
-
-// Script bindings.
-#include "moduleManager_ScriptBinding.h"
-
-//-----------------------------------------------------------------------------
-
-IMPLEMENT_CONOBJECT( ModuleManager );
-
-//-----------------------------------------------------------------------------
-
-ModuleManager ModuleDatabase;
-
-//-----------------------------------------------------------------------------
-
-S32 QSORT_CALLBACK moduleDefinitionVersionIdSort( const void* a, const void* b )
-{
-    // Fetch module definitions.
-   ModuleDefinition* pDefinition1 = *(ModuleDefinition**)a;
-   ModuleDefinition* pDefinition2 = *(ModuleDefinition**)b;
-
-   // Fetch version Ids.
-   const U32 versionId1 = pDefinition1->getVersionId();
-   const U32 versionId2 = pDefinition2->getVersionId();
-
-   // We sort higher version Id first.
-   return versionId1 > versionId2 ? -1 : versionId1 < versionId2 ? 1 : 0;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleManager() :
-    mEnforceDependencies(true),
-    mEchoInfo(true),
-    mDatabaseLocks( 0 )
-{
-    // Set module extension.
-    dStrcpy( mModuleExtension, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::onAdd()
-{
-    if( !Parent::onAdd() )
-        return false;
-
-    // Register listeners.
-    mNotificationListeners.registerObject();
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::onRemove()
-{
-    // Clear database.
-    clearDatabase();
-
-    // Unregister object.
-    mNotificationListeners.unregisterObject();
-
-    // Call parent.
-    Parent::onRemove();
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::initPersistFields()
-{
-    // Call parent.
-    Parent::initPersistFields();
-
-    addField( "EnforceDependencies", TypeBool, Offset(mEnforceDependencies, ModuleManager), "Whether the module manager enforces any dependencies on module definitions it discovers or not." );
-    addField( "EchoInfo", TypeBool, Offset(mEchoInfo, ModuleManager), "Whether the module manager echos extra information to the console or not." );
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::onDeleteNotify( SimObject *object )
-{
-    // Cast to a module definition.
-    ModuleDefinition* pModuleDefinition = dynamic_cast<ModuleDefinition*>( object );
-
-    // Ignore if not appropriate.
-    if ( pModuleDefinition == NULL )
-        return;
-
-    // Warn.
-    Con::warnf( "Module Manager::onDeleteNotify() - Notified of a module definition deletion for module Id '%s' of version Id '%d' however this should not happen and can cause module database corruption.",
-        pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::setModuleExtension( const char* pExtension )
-{
-    // Sanity!
-    AssertFatal( pExtension != NULL, "Cannot set module extension with NULL extension." );
-
-    // Did we find an extension period?
-    if ( *pExtension == '.' )
-    {
-        // Yes, so warn.
-        Con::warnf("Module Manager: Failed to set extension as supplied extension contains an initial period: '%s'.", pExtension );
-        return false;
-    }
-
-    // Is the extension too large?
-    if ( dStrlen( pExtension ) > sizeof( mModuleExtension ) )
-    {
-        // Yes, so warn.
-        Con::warnf("Module Manager: Failed to set extension as supplied extension is too large: '%s'.", pExtension );
-        return false;
-    }
-
-    // Set module extension.
-    dStrcpy( mModuleExtension, pExtension );
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::scanModules( const char* pPath, const bool rootOnly )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pPath != NULL, "Cannot scan module with NULL path." );
-
-    // Expand module location.
-    char pathBuffer[1024];
-    Con::expandPath( pathBuffer, sizeof(pathBuffer), pPath );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Started scanning '%s'...", pathBuffer );
-    }
-
-    Vector<StringTableEntry> directories;
-
-    // Find directories.
-    if ( !Platform::dumpDirectories( pathBuffer, directories, 1 ) )
-    {
-        // Failed so warn.
-        Con::warnf( "Module Manager: Failed to scan module directories in path '%s'.", pathBuffer );
-        return false;
-    }
-
-    // Fetch extension length.
-    const U32 extensionLength = dStrlen( mModuleExtension );
-
-    Vector<Platform::FileInfo> files;
-
-    // Iterate directories.
-    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
-    {
-        // Fetch base path.
-        StringTableEntry basePath = *basePathItr;
-
-        // Skip if we're only processing the root and this is not the root.
-        if ( rootOnly && basePathItr != directories.begin() )
-            continue;
-
-        // Find files.
-        files.clear();
-        if ( !Platform::dumpPath( basePath, files, 0 ) )
-        {
-            // Failed so warn.
-            Con::warnf( "Module Manager: Failed to scan modules files in directory '%s'.", basePath );
-            return false;
-        }
-
-        // Iterate files.
-        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
-        {
-            // Fetch file info.
-            Platform::FileInfo* pFileInfo = fileItr;
-
-            // Fetch filename.
-            const char* pFilename = pFileInfo->pFileName;
-
-            // Find filename length.
-            const U32 filenameLength = dStrlen( pFilename );
-
-            // Skip if extension is longer than filename.
-            if ( extensionLength > filenameLength )
-                continue;
-
-            // Skip if extension not found.
-            if ( dStricmp( pFilename + filenameLength - extensionLength, mModuleExtension ) != 0 )
-                continue;
-
-            // Register module.
-            registerModule( basePath, pFileInfo->pFileName );
-        }
-
-        // Stop processing if we're only processing the root.
-        if ( rootOnly )
-            break;
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printf( "Module Manager: Finished scanning '%s'.", pathBuffer );
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::loadModuleGroup( const char* pModuleGroup )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleGroup != NULL, "Cannot load module group with NULL group name." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Loading group '%s':" ,moduleGroup );
-    }
-
-    // Is the module group already loaded?
-    if ( findGroupLoaded( moduleGroup ) != NULL )
-    {
-        // Yes, so warn.
-        Con::warnf( "Module Manager: Cannot load group '%s' as it is already loaded.", moduleGroup );
-        return false;
-    }
-
-    // Find module group.
-    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
-
-    // Did we find the module group?
-    if ( moduleGroupItr == mGroupModules.end() )
-    {
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
-            return true;
-        }
-    }
-
-    // Yes, so fetch the module Ids.
-    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
-
-    // Iterate module groups.
-    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
-    {
-        // Fetch module Id.
-        StringTableEntry moduleId = *moduleIdItr;
-
-        // Finish if we could not resolve the dependencies for module Id (of any version Id).
-        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-            return false;
-    }
-
-    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is already loaded but at version Id '%d'.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        // Info.
-        Con::printf( "Module Manager: Group '%s' and its dependencies is comprised of the following '%d' module(s):", moduleGroup, moduleReadyQueue.size() );
-
-        // Iterate the modules echoing them.
-        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-        {
-            // Fetch the ready entry.
-            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
-
-            // Info.
-            Con::printf( "> module Id '%s' at version Id '%d':", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
-        }
-    }
-
-    // Add module group.
-    mGroupsLoaded.push_back( moduleGroup );
-
-    // Reset modules loaded count.
-    U32 modulesLoadedCount = 0;
-
-    // Iterate the modules, executing their script files and call their create function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module already loaded.
-        if ( pLoadedEntry != NULL )
-        {
-            // Yes, so increase load count.
-            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
-
-            // Skip.
-            continue;
-        }
-
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printSeparator();
-            Con::printf( "Module Manager: Loading group '%s' : module Id '%s' at version Id '%d' in group '%s' using the script file '%s'.",
-                moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-        }
-
-        // Is the module deprecated?
-        if ( pLoadReadyModuleDefinition->getDeprecated() )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' in group '%s' is deprecated.  You should use a newer version!",
-                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
-        }
-
-        // Add the path expando for module.
-        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
-
-        // Create a scope set.
-        SimSet* pScopeSet = new SimSet;
-        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
-        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
-
-        // Increase load count.
-        pReadyEntry->mpModuleDefinition->increaseLoadCount();
-
-        // Queue module loaded.
-        mModulesLoaded.push_back( *pReadyEntry );
-
-        // Bump modules loaded count.
-        modulesLoadedCount++;
-
-        // Raise notifications.
-        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
-
-        // Do we have a script file-path specified?
-        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
-        {
-            // Yes, so execute the script file.
-            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
-
-            // Did we execute the script file?
-            if ( scriptFileExecuted )
-            {
-                // Yes, so is the create method available?
-                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
-                {
-                    // Yes, so call the create method.
-                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
-                }
-            }
-            else
-            {
-                // No, so warn.
-                Con::errorf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-            }
-        }
-
-        // Raise notifications.
-        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish loading '%d' module(s) for group '%s'.", modulesLoadedCount, moduleGroup );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::unloadModuleGroup( const char* pModuleGroup )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleGroup != NULL, "Cannot unload module group with NULL group name." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Unloading group '%s':" , moduleGroup );
-    }
-
-    // Find the group loaded iterator.
-    typeGroupVector::iterator groupLoadedItr = findGroupLoaded( moduleGroup );
-
-    // Is the module group already loaded?
-    if ( groupLoadedItr == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot unload group '%s' as it is not loaded.", moduleGroup );
-        return false;
-    }
-
-    // Find module group.
-    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
-
-    // Did we find the module group?
-    if ( moduleGroupItr == mGroupModules.end() )
-    {
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
-            return true;
-        }
-    }
-
-    // Yes, so fetch the module Ids.
-    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
-
-    // Iterate module groups.
-    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
-    {
-        // Fetch module Id.
-        StringTableEntry moduleId = *moduleIdItr;
-
-        // Finish if we could not resolve the dependencies for module Id (of any version Id).
-        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-            return false;
-    }
-
-    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot unload module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is loaded but at version Id '%d'.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Remove module group.
-    mGroupsLoaded.erase_fast( groupLoadedItr );
-
-    // Reset modules unloaded count.
-    U32 modulesUnloadedCount = 0;
-
-    // Iterate the modules in reverse order calling their destroy function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module loaded.
-        if ( pLoadedEntry == NULL )
-        {
-            // No, so warn.
-            if ( mEchoInfo )
-            {
-                Con::printf( "Module Manager: Unloading group '%s' but could not unload module Id '%s' at version Id '%d'.",
-                    moduleGroup, pLoadedEntry->mpModuleDefinition->getModuleId(), pLoadedEntry->mpModuleDefinition->getVersionId() );
-            }
-            // Skip.
-            continue;
-        }
-
-        // Reduce load count.
-        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
-
-        // Sanity!
-        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
-
-        // Do we need to unload?
-        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
-        {
-            // Yes, so info.
-            if ( mEchoInfo )
-            {
-                Con::printSeparator();
-                Con::printf( "Module Manager: Unload group '%s' with module Id '%s' at version Id '%d' in group '%s'.",
-                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
-            }
-
-            // Raise notifications.
-            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
-
-            // Fetch the module Id loaded entry.
-            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Sanity!
-            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleGroup() - Cannot find module to unload it." );
-
-            // Dequeue module loaded.
-            mModulesLoaded.erase_fast( moduleLoadedItr );
-
-            // Fetch scope set.
-            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
-
-            // Is the destroy method available?
-            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
-            {
-                // Yes, so call the destroy method.
-                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
-            }
-
-            // Remove scope set.
-            pScopeSet->deleteObjects();
-            pScopeSet->unregisterObject();
-            pLoadReadyModuleDefinition->mScopeSet = 0;
-
-            // Remove path expando for module.
-            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Bump modules unloaded count.
-            modulesUnloadedCount++;
-
-            // Raise notifications.
-            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish unloading '%d' module(s) for group '%s'.", modulesUnloadedCount, moduleGroup );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::loadModuleExplicit( const char* pModuleId, const U32 versionId )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot load explicit module Id with NULL module Id." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module Id.
-    StringTableEntry moduleId = StringTable->insert( pModuleId );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Did we find the module Id?
-    if ( pDefinitions == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot load explicit module Id '%s' as it does not exist.", moduleId );
-        return false;
-    }
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d':", moduleId, versionId );
-    }
-
-    // Finish if we could not resolve the dependencies for module Id (of any version Id).
-    if ( !resolveModuleDependencies( moduleId, versionId, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-        return false;
-
-    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as the module Id is already loaded but at version Id '%d'.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        // Info.
-        Con::printf( "Module Manager: Explicit load of module Id '%s' at version Id '%d' and its dependencies is comprised of the following '%d' module(s):", moduleId, versionId, moduleReadyQueue.size() );
-
-        // Iterate the modules echoing them.
-        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-        {
-            // Fetch the ready entry.
-            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
-
-            // Info.
-            Con::printf( "> module Id '%s' at version Id '%d'", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
-        }
-    }
-
-    // Reset modules loaded count.
-    U32 modulesLoadedCount = 0;
-
-    // Iterate the modules, executing their script files and call their create function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module already loaded.
-        if ( pLoadedEntry != NULL )
-        {
-            // Yes, so increase load count.
-            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
-
-            // Skip.
-            continue;
-        }
-
-        // No, so info.
-        if ( mEchoInfo )
-        {
-            Con::printSeparator();
-            Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d' using the script file '%s'.",
-                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-        }
-
-        // Is the module deprecated?
-        if ( pLoadReadyModuleDefinition->getDeprecated() )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' is deprecated,  You should use a newer version!",
-                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
-        }
-
-        // Add the path expando for module.
-        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
-
-        // Create a scope set.
-        SimSet* pScopeSet = new SimSet;
-        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
-        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
-
-        // Increase load count.
-        pReadyEntry->mpModuleDefinition->increaseLoadCount();
-
-        // Queue module loaded.
-        mModulesLoaded.push_back( *pReadyEntry );
-
-        // Bump modules loaded count.
-        modulesLoadedCount++;
-
-        // Raise notifications.
-        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
-
-        // Do we have a script file-path specified?
-        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
-        {
-            // Yes, so execute the script file.
-            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
-
-            // Did we execute the script file?
-            if ( scriptFileExecuted )
-            {
-                // Yes, so is the create method available?
-                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
-                {
-                    // Yes, so call the create method.
-                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
-                }
-            }
-            else
-            {
-                // No, so warn.
-                Con::errorf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
-            }
-        }
-
-        // Raise notifications.
-        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish loading '%d' explicit module(s).", modulesLoadedCount );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::unloadModuleExplicit( const char* pModuleId )
-{
-    // Lock database.
-    LockDatabase( this );
-
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot unload explicit module Id with NULL module Id." );
-
-    typeModuleLoadEntryVector   moduleResolvingQueue;
-    typeModuleLoadEntryVector   moduleReadyQueue;
-
-    // Fetch module Id.
-    StringTableEntry moduleId = StringTable->insert( pModuleId );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Did we find the module Id?
-    if ( pDefinitions == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it does not exist.", moduleId );
-        return false;
-    }
-
-    // Find if the module is actually loaded.
-    ModuleDefinition* pLoadedModule = findLoadedModule( moduleId );
-
-    // Is the module loaded?
-    if ( pLoadedModule == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it is not loaded.", moduleId );
-        return false;
-    }
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Unloading explicit module Id '%s':" , moduleId );
-    }
-
-    // Finish if we could not resolve the dependencies for module Id (of any version Id).
-    if ( !resolveModuleDependencies( moduleId, pLoadedModule->getVersionId(), moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
-        return false;
-
-    // Check the modules we want to unload to ensure that we do not have incompatible modules loaded already.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
-    {
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
-
-        // Fetch the module Id loaded entry.
-        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Did we find a loaded entry?
-        if ( pLoadedModuleEntry != NULL )
-        {
-            // Yes, so is it the one we need to load?
-            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
-            {
-                // Yes, so warn.
-                Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' at version Id '%d' as the module Id is loaded but at version Id '%d'.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
-                return false;
-            }
-        }
-    }
-
-    // Reset modules unloaded count.
-    U32 modulesUnloadedCount = 0;
-
-    // Iterate the modules in reverse order calling their destroy function.
-    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
-    {
-        // Fetch the ready entry.
-        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
-
-        // Fetch load ready module definition.
-        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
-
-        // Fetch any loaded entry for the module Id.
-        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-        // Is the module loaded.
-        if ( pLoadedEntry == NULL )
-        {
-            // No, so warn.
-            if ( mEchoInfo )
-            {
-                Con::printf( "Module Manager: Unloading explicit module Id '%s' at version Id '%d' but ignoring as it is not loaded.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
-            }
-
-            // Skip.
-            continue;
-        }
-
-        // Reduce load count.
-        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
-
-        // Sanity!
-        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
-
-        // Do we need to unload?
-        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
-        {
-            // Yes, so info.
-            if ( mEchoInfo )
-            {
-                Con::printSeparator();
-                Con::printf( "Module Manager: Unload explicit module Id '%s' at version Id '%d'.",
-                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
-            }
-
-            // Raise notifications.
-            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
-
-            // Fetch the module Id loaded entry.
-            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Sanity!
-            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleExplicit() - Cannot find module to unload it." );
-
-            // Dequeue module loaded.
-            mModulesLoaded.erase_fast( moduleLoadedItr );
-
-            // Fetch scope set.
-            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
-
-            // Is the destroy method available?
-            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
-            {
-                // Yes, so call the destroy method.
-                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
-            }
-
-            // Remove scope set.
-            pScopeSet->deleteObjects();
-            pScopeSet->unregisterObject();
-            pLoadReadyModuleDefinition->mScopeSet = 0;
-
-            // Remove path expando for module.
-            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
-
-            // Bump modules unloaded count.
-            modulesUnloadedCount++;
-
-            // Raise notifications.
-            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printSeparator();
-        Con::printf( "Module Manager: Finish unloading '%d' explicit module(s).", modulesUnloadedCount );
-        Con::printSeparator();
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleDefinition* ModuleManager::findModule( const char* pModuleId, const U32 versionId )
-{
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
-
-    // Find module definition.
-    ModuleDefinitionEntry::iterator moduleItr = findModuleDefinition( StringTable->insert( pModuleId ), versionId );
-
-     // Finish if module was not found.
-    if ( moduleItr == NULL )
-        return NULL;
-
-    return *moduleItr;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleDefinition* ModuleManager::findLoadedModule( const char* pModuleId )
-{
-    // Sanity!
-    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
-
-    // Fetch module Id.
-    StringTableEntry moduleId = StringTable->insert( pModuleId );
-
-    // Iterate loaded modules.
-    for ( typeModuleLoadEntryVector::iterator loadedModuleItr = mModulesLoaded.begin(); loadedModuleItr != mModulesLoaded.end(); ++loadedModuleItr )
-    {
-        // Skip if not the module.
-        if ( loadedModuleItr->mpModuleDefinition->getModuleId() != moduleId )
-            continue;
-
-        return loadedModuleItr->mpModuleDefinition;
-    }
-
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::findModules( const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
-{
-    // Iterate module Ids.
-    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
-    {
-        // Fetch module definition entry.
-        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
-
-        // Iterate module definitions.
-        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
-        {
-            // Fetch module definition.
-            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
-
-            // Are we searching for loaded modules only?
-            if ( loadedOnly )
-            {
-                // Yes, so skip if the module is not loaded.
-                if ( pModuleDefinition->getLoadCount() == 0 )
-                    continue;
-
-                // Use module definition.
-                moduleDefinitions.push_back( pModuleDefinition );
-
-                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
-                break;
-            }
-
-            // use module definition.
-            moduleDefinitions.push_back( pModuleDefinition );
-        }
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::findModuleTypes( const char* pModuleType, const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
-{
-    // Fetch module type.
-    StringTableEntry moduleType = StringTable->insert( pModuleType );
-
-    // Iterate module Ids.
-    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
-    {
-        // Fetch module definition entry.
-        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
-
-        // Skip if note the module type we're searching for.
-        if ( pModuleDefinitionEntry->mModuleType != moduleType )
-            continue;
-
-        // Iterate module definitions.
-        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
-        {
-            // Fetch module definition.
-            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
-
-            // Are we searching for loaded modules only?
-            if ( loadedOnly )
-            {
-                // Yes, so skip if the module is not loaded.
-                if ( pModuleDefinition->getLoadCount() == 0 )
-                    continue;
-
-                // Use module definition.
-                moduleDefinitions.push_back( pModuleDefinition );
-
-                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
-                break;
-            }
-
-            // use module definition.
-            moduleDefinitions.push_back( pModuleDefinition );
-        }
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-StringTableEntry ModuleManager::copyModule( ModuleDefinition* pSourceModuleDefinition, const char* pTargetModuleId, const char* pTargetPath, const bool useVersionPathing )
-{
-    // Sanity!
-    AssertFatal( pSourceModuleDefinition != NULL, "Cannot copy module using a NULL source module definition." );
-    AssertFatal( pTargetModuleId != NULL, "Cannot copy module using a NULL target module Id." );
-    AssertFatal( pTargetPath != NULL, "Cannot copy module using a NULL target path." );
-
-    // Fetch the source module Id.
-    StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
-
-    // Is the source module definition registered with this module manager?
-    if ( pSourceModuleDefinition->getModuleManager() != this )
-    {
-        // No, so warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' as it is not registered with this module manager.", sourceModuleId );
-        return StringTable->EmptyString;
-    }
-
-    // Fetch the target module Id.
-    StringTableEntry targetModuleId = StringTable->insert( pTargetModuleId );
-
-    // Extend moduleId/VersionId pathing.
-    char versionPathBuffer[1024];
-
-    // Are we using version pathing?
-    if ( useVersionPathing )
-    {
-        // Yes, so format it.
-        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s/%s/%d",
-            pTargetPath, targetModuleId, pSourceModuleDefinition->getVersionId() );
-    }
-    else
-    {
-        // No, so a straight copy.
-        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s", pTargetPath );
-    }
-
-    // Expand the path.
-    char targetPathBuffer[1024];
-    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), versionPathBuffer );
-    pTargetPath = targetPathBuffer;
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printf( "Module Manager: Started copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
-    }
-
-    // Is the target folder a directory?
-    if ( !Platform::isDirectory( pTargetPath ) )
-    {
-        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
-        char createDirectoryBuffer[1024];
-        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetPath, NULL, true );
-
-        // No, so can we create it?
-        if ( !Platform::createPath( createDirectoryBuffer ) )
-        {
-            // No, so warn.
-            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory was not found and could not be created.",
-                sourceModuleId, pTargetPath );
-            return StringTable->EmptyString;
-        }
-    }
-
-    // Copy the source module to the target folder.
-    if ( !Platform::pathCopy( pSourceModuleDefinition->getModulePath(), pTargetPath, false ) )
-    {
-        // Warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory copy failed.",
-            sourceModuleId, pTargetPath );
-        return StringTable->EmptyString;
-    }
-
-    // Format the new source module definition file-path.
-    char newModuleDefinitionSourceFileBuffer[1024];
-    dSprintf( newModuleDefinitionSourceFileBuffer, sizeof(newModuleDefinitionSourceFileBuffer), "%s/%s", pTargetPath, pSourceModuleDefinition->getModuleFile() );
-
-    // Finish if source/target module Ids are identical.
-    if ( sourceModuleId == targetModuleId )
-        return StringTable->insert( newModuleDefinitionSourceFileBuffer );
-
-    // Format the new target module definition file-path.
-    char newModuleDefinitionTargetFileBuffer[1024];
-    dSprintf( newModuleDefinitionTargetFileBuffer, sizeof(newModuleDefinitionTargetFileBuffer), "%s/%s.%s", pTargetPath, targetModuleId, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
-
-    // Rename the module definition.
-    if ( !Platform::fileRename( newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer ) )
-    {
-        // Warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as renaming the module from '%s' to '%s' failed.",
-            sourceModuleId, pTargetPath, newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer );
-        return StringTable->EmptyString;
-    }
-
-    Vector<StringTableEntry> directories;
-
-    // Find directories.
-    if ( !Platform::dumpDirectories( pTargetPath, directories, -1 ) )
-    {
-        // Warn.
-        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
-            sourceModuleId, pTargetPath );
-        return StringTable->EmptyString;
-    }
-
-    TamlModuleIdUpdateVisitor moduleIdUpdateVisitor;
-    moduleIdUpdateVisitor.setModuleIdFrom( sourceModuleId );
-    moduleIdUpdateVisitor.setModuleIdTo( targetModuleId );
-
-    Vector<Platform::FileInfo> files;
-
-    const char* pExtension = (const char*)"Taml";
-    const U32 extensionLength = dStrlen(pExtension);
-
-    // Iterate directories.
-    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
-    {
-        // Fetch base path.
-        StringTableEntry basePath = *basePathItr;
-
-        // Find files.
-        files.clear();
-        if ( !Platform::dumpPath( basePath, files, 0 ) )
-        {
-            // Warn.
-            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
-                sourceModuleId, pTargetPath );
-            return StringTable->EmptyString;
-        }
-
-        // Iterate files.
-        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
-        {
-            // Fetch file info.
-            Platform::FileInfo* pFileInfo = fileItr;
-
-            // Fetch filename.
-            const char* pFilename = pFileInfo->pFileName;
-
-            // Find filename length.
-            const U32 filenameLength = dStrlen( pFilename );
-
-            // Skip if extension is longer than filename.
-            if ( extensionLength >= filenameLength )
-                continue;
-
-            // Skip if extension not found.
-            if ( dStricmp( pFilename + filenameLength - extensionLength, pExtension ) != 0 )
-                continue;
-
-            char parseFileBuffer[1024];
-            dSprintf( parseFileBuffer, sizeof(parseFileBuffer), "%s/%s", pFileInfo->pFullPath, pFilename );
-
-            // Parse file.
-            if ( !moduleIdUpdateVisitor.parse( parseFileBuffer ) )
-            {
-                // Warn.
-                Con::warnf("Module Manager: Failed to parse file '%s' whilst copying module Id '%s' using target directory '%s'.",
-                    parseFileBuffer, sourceModuleId, pTargetPath );
-                return StringTable->EmptyString;
-            }
-        }
-    }
-
-    // Info.
-    if ( mEchoInfo )
-    {
-        Con::printf( "Module Manager: Finished copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
-    }
-
-    return StringTable->insert( newModuleDefinitionTargetFileBuffer );
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::synchronizeDependencies( ModuleDefinition* pRootModuleDefinition, const char* pTargetDependencyPath )
-{
-    // Sanity!
-    AssertFatal( pRootModuleDefinition != NULL, "Cannot synchronize dependencies with NULL root module definition." );
-    AssertFatal( pTargetDependencyPath != NULL, "Cannot synchronize dependencies with NULL target dependency path." );
-
-    // Fetch the root module Id.
-    StringTableEntry rootModuleId = pRootModuleDefinition->getModuleId();
-
-    // Is the root module definition registered with this module manager?
-    if ( pRootModuleDefinition->getModuleManager() != this )
-    {
-        // No, so warn.
-        Con::warnf("Cannot synchronize dependencies for module Id '%s' as it is not registered with this module manager.", rootModuleId );
-        return false;
-    }
-
-    // Expand the path.
-    char targetPathBuffer[1024];
-    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pTargetDependencyPath );
-    pTargetDependencyPath = targetPathBuffer;
-
-    // Is the target dependency folder a directory?
-    if ( !Platform::isDirectory( pTargetDependencyPath ) )
-    {
-        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
-        char createDirectoryBuffer[1024];
-        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetDependencyPath, NULL, true );
-
-        // No, so can we create it?
-        if ( !Platform::createPath( createDirectoryBuffer ) )
-        {
-            // No, so warn.
-            Con::warnf("Cannot synchronize dependencies for module Id '%s' using target directory '%s' as directory was not found and could not be created.",
-                rootModuleId, pTargetDependencyPath );
-            return false;
-        }
-    }
-
-    typeModuleLoadEntryVector       resolvingQueue;
-    typeModuleLoadEntryVector       sourceModulesNeeded;
-
-    // Could we resolve source dependencies?
-    if ( !resolveModuleDependencies( rootModuleId, pRootModuleDefinition->getVersionId(), pRootModuleDefinition->getModuleGroup(), true, resolvingQueue, sourceModulesNeeded ) )
-    {
-        // No, so warn.
-        Con::warnf("Cannot synchronize dependencies for root module Id '%s' as its dependencies could not be resolved.", rootModuleId );
-        return false;
-    }
-
-    // Sanity!
-    AssertFatal( sourceModulesNeeded.size() > 0, "Cannot synchronize dependencies as no modules were returned." );
-
-    // Remove the root module definition.
-    sourceModulesNeeded.pop_back();
-
-    // Initialize the target module manager and scan the target folder for modules.
-    ModuleManager targetModuleManager;
-    targetModuleManager.mEnforceDependencies = true;
-    targetModuleManager.mEchoInfo = false;
-    targetModuleManager.scanModules( pTargetDependencyPath );
-
-    char targetFolderGenerateBuffer[1024];
-
-    // Iterate module definitions.
-    for ( typeModuleLoadEntryVector::iterator sourceModuleItr = sourceModulesNeeded.begin(); sourceModuleItr != sourceModulesNeeded.end(); ++sourceModuleItr )
-    {
-        // Fetch module definition.
-        ModuleDefinition* pSourceModuleDefinition = sourceModuleItr->mpModuleDefinition;
-        
-        // Fetch the source module Id,
-        StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
-
-        // Fetch the source module version Id.
-        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
-
-        // Fetch the source module build Id.
-        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
-
-        // Fetch module definition entry for this module Id in the target.
-        ModuleDefinitionEntry* pDefinitions = targetModuleManager.findModuleId( sourceModuleId );
-
-        // Is the module Id present in the target?
-        if ( pDefinitions == NULL )
-        {
-            // No, so format module Id folder path.
-            dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/", pTargetDependencyPath, sourceModuleId );
-
-            // Create module Id folder.
-            if ( !Platform::createPath( targetFolderGenerateBuffer ) )
-            {
-                // Warn.
-                Con::warnf("Cannot synchronize dependencies for module Id '%s' as the target directory '%s' could not be created.", sourceModuleId, targetFolderGenerateBuffer );
-                return false;
-            }
-        }
-        else
-        {
-            // Yes, so fetch the module definition for this module Id and version Id in the target.
-            ModuleDefinitionEntry::iterator definitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
-
-            // Is the specific module definition present in the target?
-            if ( definitionItr != NULL )
-            {
-                // Yes, so fetch the module definition.
-                ModuleDefinition* pTargetModuleDefinition = *definitionItr;
-
-                // Fetch the target module build Id.
-                const U32 targetBuildId = pTargetModuleDefinition->getBuildId();
-
-                // Fetch the target module path.
-                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
-
-                // Remove the target module definition from the database.
-                targetModuleManager.removeModuleDefinition( pTargetModuleDefinition );
-
-                // Skip if the target definition is the same build Id.
-                if ( targetBuildId == sourceBuildId )
-                    continue;
-
-                // Delete the target module definition folder.
-                if ( !Platform::deleteDirectory( targetModulePath ) )
-                {
-                    // Warn.
-                    Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the old module at '%s' could not be deleted.",
-                        sourceModuleId, sourceVersionId, targetModulePath );
-                    return false;
-                }
-            }
-        }
-
-        // Format source module path.
-        char sourceFolderPath[1024];
-        Con::expandPath( sourceFolderPath, sizeof(sourceFolderPath), pSourceModuleDefinition->getModulePath() );
-
-        // Format target module path.
-        dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/%d", pTargetDependencyPath, sourceModuleId, sourceVersionId );
-
-        // Copy the source module to the target folder.
-        if ( !Platform::pathCopy( sourceFolderPath, targetFolderGenerateBuffer, false ) )
-        {
-            // Warn.
-            Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the module could not be copied to '%s'.",
-                sourceModuleId, sourceVersionId, targetFolderGenerateBuffer );
-            return false;
-        }
-    }
-
-    // Find any target modules left, These are orphaned modules not depended upon by any other module.
-    typeConstModuleDefinitionVector orphanedTargetModules;
-    targetModuleManager.findModules( false, orphanedTargetModules );
-
-    // Iterate module definitions.
-    for ( typeConstModuleDefinitionVector::const_iterator moduleDefinitionItr = orphanedTargetModules.begin(); moduleDefinitionItr != orphanedTargetModules.end(); ++moduleDefinitionItr )
-    {
-        // Fetch orphaned module definition.
-        const ModuleDefinition* pOrphanedModuleDefinition = *moduleDefinitionItr;
-       
-        // Delete the target module definition folder.
-        if ( !Platform::deleteDirectory( pOrphanedModuleDefinition->getModulePath() ) )
-        {
-            // Warn.
-            Con::warnf("Cannot delete orphaned module Id '%s' at version Id '%d' from '%s'.",
-                pOrphanedModuleDefinition->getModuleId(), pOrphanedModuleDefinition->getVersionId(), pOrphanedModuleDefinition->getModulePath() );
-        }
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::canMergeModules( const char* pMergeSourcePath )
-{
-    // Sanity!
-    AssertFatal( pMergeSourcePath != NULL, "Cannot check merge modules with NULL source path." );
-
-    // Expand the source path.
-    char sourcePathBuffer[1024];
-    Con::expandPath( sourcePathBuffer, sizeof(sourcePathBuffer), pMergeSourcePath );
-    pMergeSourcePath = sourcePathBuffer;
-
-    // Is the path a valid directory?
-    if ( !Platform::isDirectory( sourcePathBuffer ) )
-    {
-        // No, so warn.
-        Con::warnf( "Cannot check merge modules as path is invalid '%s'.", sourcePathBuffer );
-        return false;
-    }
-
-    // Initialize the source module manager and scan the source folder for modules.
-    ModuleManager mergeModuleManager;
-    mergeModuleManager.mEnforceDependencies = false;
-    mergeModuleManager.mEchoInfo = false;
-    mergeModuleManager.scanModules( pMergeSourcePath );
-
-    // Find all the merge modules.
-    typeConstModuleDefinitionVector mergeModules;
-    mergeModuleManager.findModules( false, mergeModules );
-
-    // Iterate found merge module definitions.
-    for ( typeConstModuleDefinitionVector::const_iterator mergeModuleItr = mergeModules.begin(); mergeModuleItr != mergeModules.end(); ++mergeModuleItr )
-    {
-        // Fetch module definition.
-        const ModuleDefinition* pMergeModuleDefinition = *mergeModuleItr;
-
-        // Fetch module Id.
-        StringTableEntry moduleId = pMergeModuleDefinition->getModuleId();
-
-        // Fetch version Id.
-        const U32 versionId = pMergeModuleDefinition->getVersionId();
-
-        // Fetch module group.
-        StringTableEntry moduleGroup = pMergeModuleDefinition->getModuleGroup();
-
-        // Cannot merge if module already exists.
-        if ( findModuleDefinition( moduleId, versionId ) != NULL )
-            return false;
-
-        // Cannot merge if module is part of a loaded group.
-        if ( findGroupLoaded( moduleGroup ) != NULL )
-            return false;
-    }
-
-    // Can merge modules.
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::mergeModules( const char* pMergeTargetPath, const bool removeMergeDefinition, const bool registerNewModules )
-{
-    // Sanity!
-    AssertFatal( pMergeTargetPath != NULL, "Cannot merge modules with a target path of NULL." );
-
-    // Is a module merge available?
-    if ( !isModuleMergeAvailable() )
-    {
-        // No, so warn.
-        Con::warnf( "Cannot merge modules as a module merge is not available." );
-        return false;
-    }
-
-    // Expand the target path.
-    char targetPathBuffer[1024];
-    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pMergeTargetPath );
-    pMergeTargetPath = targetPathBuffer;
-
-    // Fetch module merge file path.
-    StringTableEntry moduleMergeFilePath = getModuleMergeFilePath();
-
-    // Read module merge definition.
-    Taml taml;
-    ModuleMergeDefinition* pModuleMergeDefinition = taml.read<ModuleMergeDefinition>( moduleMergeFilePath );
-    
-    // Do we have a module merge definition.
-    if ( pModuleMergeDefinition == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Cannot merge modules as the module merge definition file failed to load '%s'.", moduleMergeFilePath );
-        return false;
-    }
-
-    // Fetch the merge source path.
-    StringTableEntry mergeSourcePath = pModuleMergeDefinition->getModuleMergePath();
-
-    // Remove the module merge definition.
-    pModuleMergeDefinition->deleteObject();
-    pModuleMergeDefinition = NULL;
-
-    // If we cannot merge the modules then we only process modules flagged as critical merge.
-    const bool criticalMergeOnly = !canMergeModules( mergeSourcePath );
-
-    // Initialize the target module manager and scan the target folder for modules.
-    ModuleManager targetModuleManager;
-    targetModuleManager.mEnforceDependencies = false;
-    targetModuleManager.mEchoInfo = false;
-    targetModuleManager.scanModules( pMergeTargetPath );
-
-    // Initialize the source module manager and scan the source folder for modules.
-    ModuleManager sourceModuleManager;
-    sourceModuleManager.mEnforceDependencies = false;
-    sourceModuleManager.mEchoInfo = false;
-    sourceModuleManager.scanModules( mergeSourcePath );
-
-    // Find all the source modules.
-    typeConstModuleDefinitionVector sourceModules;
-    sourceModuleManager.findModules( false, sourceModules );
-
-    // Iterate found merge module definitions.
-    for ( typeConstModuleDefinitionVector::const_iterator sourceModuleItr = sourceModules.begin(); sourceModuleItr != sourceModules.end(); ++sourceModuleItr )
-    {
-        // Fetch the source module definition.
-        const ModuleDefinition* pSourceModuleDefinition = *sourceModuleItr;
-
-        // Skip if we're performing a critical merge only and the module is not flagged as critical merge.
-        if ( criticalMergeOnly && pSourceModuleDefinition->getCriticalMerge() )
-            continue;
-
-        // Fetch source module Id.
-        const StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
-
-        // Fetch source version Id.
-        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
-
-        // Fetch source build Id.
-        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
-
-        // Format module Id folder path.
-        char targetModuleIdBuffer[1024];
-        dSprintf( targetModuleIdBuffer, sizeof(targetModuleIdBuffer), "%s/%s/", pMergeTargetPath, sourceModuleId );
-
-        // Flag to indicate if the merged module needs registering.
-        bool shouldRegisterModule;
-
-        // Does the module Id exist?
-        if ( targetModuleManager.findModuleId( sourceModuleId ) == NULL )
-        {
-            // No, so create module Id folder.
-            if ( !Platform::createPath( targetModuleIdBuffer ) )
-            {
-                // Warn.
-                Con::warnf("Cannot merge modules for module '%s' as the path '%s' could not be created.", sourceModuleId, targetModuleIdBuffer );
-                return false;
-            }
-
-            // Module Should be registered.
-            shouldRegisterModule = true;
-        }
-        else
-        {
-            // Yes, so find the target module definition that matches the source module definition.
-            ModuleDefinitionEntry::iterator targetModuleDefinitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
-
-            // Is there an existing target module definition entry?
-            if ( targetModuleDefinitionItr != NULL )
-            {
-                // Yes, so fetch the target module definition.
-                const ModuleDefinition* pTargetModuleDefinition = *targetModuleDefinitionItr;
-
-                // Fetch target module path.
-                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
-
-                // Yes, so we have to remove it first.
-                if ( !Platform::deleteDirectory( targetModulePath ) )
-                {
-                    // Module was not deleted so warn.
-                    Con::warnf( "Failed to remove module folder located at '%s'.  Module will be copied over.", targetModulePath );
-                }
-
-                // Is the build Id being downgraded?
-                if ( sourceBuildId < pTargetModuleDefinition->getBuildId() )
-                {
-                    // Yes, so warn.
-                    Con::warnf( "Encountered a downgraded build Id for module Id '%s' at version Id '%d'.", sourceModuleId, sourceBuildId );
-                }
-
-                // Module should not be registered.
-                shouldRegisterModule = false;
-            }
-            else
-            {
-                // Module Should be registered.
-                shouldRegisterModule = true;
-            }
-        }
-
-        // Fetch source module path.
-        StringTableEntry sourceModulePath = pSourceModuleDefinition->getModulePath();
-
-        // Format target version Id folder path.
-        char targetVersionIdBuffer[1024];
-        dSprintf( targetVersionIdBuffer, sizeof(targetVersionIdBuffer), "%s%d", targetModuleIdBuffer, sourceVersionId );
-
-        // Copy module (allow overwrites as we may have failed to remove the old folder in which case this is likely to fail as well).
-        if ( !Platform::pathCopy( sourceModulePath, targetVersionIdBuffer, false ) )
-        {
-            // Failed to copy module.
-            Con::warnf( "Failed to copy module folder located at '%s' to location '%s'.  The modules may now be corrupted.", sourceModulePath, targetVersionIdBuffer );
-        }
-
-        // Are we registering new modules and the module needs registering?
-        if ( registerNewModules && shouldRegisterModule )
-        {
-            // Yes, so scan module.
-            scanModules( targetVersionIdBuffer, true );
-        }
-
-        // Is the module part of a critical merge?
-        if ( criticalMergeOnly )
-        {
-            // Yes, so we need to remove the source module definition.
-            if ( !Platform::deleteDirectory( sourceModulePath ) )
-            {
-                // Module was not deleted so warn.
-                Con::warnf( "Failed to remove CRITICAL merge module folder located at '%s'.  Module will be copied over.", sourceModulePath );
-            }
-        }
-    }
-
-    // Do we need to remove the module merge definition file?
-    if ( removeMergeDefinition )
-    {
-        // Yes, so remove it.
-        Platform::fileDelete( moduleMergeFilePath );
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::addListener( SimObject* pListener )
-{
-    // Sanity!
-    AssertFatal( pListener != NULL, "Cannot add notifications to a NULL object." );
-
-    // Ignore if already added.
-    if ( mNotificationListeners.find( pListener ) != mNotificationListeners.end() )
-        return;
-        
-    // Add as a listener.
-    mNotificationListeners.addObject( pListener );
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::removeListener( SimObject* pListener )
-{
-    // Sanity!
-    AssertFatal( pListener != NULL, "Cannot remove notifications from a NULL object." );
-
-    // Remove as a listener.
-    mNotificationListeners.removeObject( pListener );
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::clearDatabase( void )
-{
-    // Lock database.
-    AssertFatal( mDatabaseLocks == 0, "Cannot clear database if database is locked." );
-
-    // Iterate groups loaded.
-    while ( mGroupsLoaded.size() > 0 )
-    {
-        // Unload module group.
-        unloadModuleGroup( *mGroupsLoaded.begin() );
-    }
-
-    // Iterate any other explicit modules that are loaded.
-    while ( mModulesLoaded.size() > 0 )
-    {
-        // Fetch module definition.
-        ModuleDefinition* pModuleDefinition = mModulesLoaded.begin()->mpModuleDefinition;
-
-        // Unload explicit module.
-        unloadModuleExplicit( pModuleDefinition->getModuleId() );
-    }
-
-    // Iterate modules to delete module definitions.
-    for ( typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.begin(); moduleItr != mModuleIdDatabase.end(); ++moduleItr )
-    {
-        // Fetch modules definitions.
-        ModuleDefinitionEntry* pDefinitions = moduleItr->value;
-
-        // Iterate module definitions.
-        for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
-        {
-            // Fetch module definition.
-            ModuleDefinition* pModuleDefinition = *definitionItr;
-
-            // Remove notification before we delete it.
-            clearNotify( pModuleDefinition );
-
-            // Delete module definition.
-            pModuleDefinition->deleteObject();
-        }
-
-        // Clear definitions.
-        delete pDefinitions;        
-    }
-
-    // Clear database.
-    mModuleIdDatabase.clear();
-
-    // Iterate module groups.
-    for ( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
-    {
-        // Delete module group vector.
-        delete moduleGroupItr->value;
-    }
-
-    // Clear module groups.
-    mGroupModules.clear();
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::removeModuleDefinition( ModuleDefinition* pModuleDefinition )
-{
-    // Sanity!
-    AssertFatal( pModuleDefinition != NULL, "Cannot remove module definition if it is NULL." );
-    
-    // Fetch module Id.
-    StringTableEntry moduleId = pModuleDefinition->getModuleId();
-
-    // Is the module definition registered with this module manager?
-    if ( pModuleDefinition->getModuleManager() != this )
-    {
-        // No, so warn.
-        Con::warnf("Cannot remove module definition '%s' as it is not registered with this module manager.", moduleId );
-        return false;
-    }
-
-    // Is the module definition loaded?
-    if ( pModuleDefinition->getLoadCount() > 0 )
-    {
-        // No, so warn.
-        Con::warnf("Cannot remove module definition '%s' as it is loaded.", moduleId );
-        return false;
-    }
-
-    // Find module Id.
-    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
-
-    // Sanity!
-    AssertFatal( moduleItr != mModuleIdDatabase.end(), "Failed to find module definition." );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = moduleItr->value;
-
-    // Fetch version Id.
-    const U32 versionId = pModuleDefinition->getVersionId();
-
-    // Iterate module definitions.
-    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
-    {
-        // Skip if this isn't the version Id we're searching for.
-        if ( versionId != (*definitionItr)->getVersionId() )
-            continue;
-
-        // Remove definition entry.
-        pDefinitions->erase( definitionItr ); 
-
-        // Remove notification before we delete it.
-        clearNotify( pModuleDefinition );
-
-        // Delete module definition.
-        pModuleDefinition->deleteObject();
-
-        return true;
-    }
-
-    // Sanity!
-    AssertFatal( false, "Failed to find module definition." );
-
-    return false;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::registerModule( const char* pModulePath, const char* pModuleFile )
-{
-    // Sanity!
-    AssertFatal( pModulePath != NULL, "Cannot scan module with NULL module path." );
-    AssertFatal( pModuleFile != NULL, "Cannot scan module with NULL module file." );
-
-    // Make the module path a full-path.
-    char fullPathBuffer[1024];
-    Platform::makeFullPathName( pModulePath, fullPathBuffer, sizeof(fullPathBuffer) );
-    pModulePath = fullPathBuffer;
-
-
-    char formatBuffer[1024];
-
-    // Fetch module path trail character.
-    char modulePathTrail = pModulePath[dStrlen(pModulePath) - 1];
-
-    // Format module file-path.
-    dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleFile );
-
-    // Read the module file.
-    ModuleDefinition* pModuleDefinition = mTaml.read<ModuleDefinition>( formatBuffer );
-
-    // Did we read a module definition?
-    if ( pModuleDefinition == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Failed to read module definition in file '%s'.", formatBuffer );
-        return false;
-    }
-
-    // Set the module manager.
-    pModuleDefinition->setModuleManager( this );
-
-    // Set module definition path.
-    pModuleDefinition->setModulePath( pModulePath );
-
-    // Set module file.
-    pModuleDefinition->setModuleFile( pModuleFile );
-
-    // Set module file-path.
-    pModuleDefinition->setModuleFilePath( formatBuffer );
-
-    // Fetch module Id.
-    StringTableEntry moduleId = pModuleDefinition->getModuleId();
-
-    // Fetch module version Id.
-    const U32 versionId = pModuleDefinition->getVersionId();
-
-    // Fetch module group.
-    StringTableEntry moduleGroup = pModuleDefinition->getModuleGroup();
-
-    // Fetch module type.
-    StringTableEntry moduleType = pModuleDefinition->getModuleType();
-
-    // Is the module enabled?
-    if ( !pModuleDefinition->getEnabled() )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Found module: '%s' but it is disabled.", pModuleDefinition->getModuleFilePath() );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Is the module Id valid?
-    if ( moduleId == StringTable->EmptyString )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Found module: '%s' but it has an unspecified module Id.",
-            pModuleDefinition->getModuleFilePath() );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Is the module version Id valid?
-    if ( versionId == 0 )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Found Manager: Registering module: '%s' but it has an invalid Version Id of '0'.",
-            pModuleDefinition->getModuleFilePath() );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Is the module group already loaded?
-    if ( findGroupLoaded( moduleGroup ) != NULL )
-    {
-        // Yes, so warn.
-        Con::warnf( "Module Manager: Found module: '%s' but it is in a module group '%s' which has already been loaded.",
-            pModuleDefinition->getModuleFilePath(),
-            moduleGroup );
-
-        // Destroy module definition and finish.
-        pModuleDefinition->deleteObject();
-        return false;
-    }
-
-    // Was a script-file specified?
-    if ( pModuleDefinition->getScriptFile() != StringTable->EmptyString )
-    {
-        // Yes, so format module script file-path.
-        dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleDefinition->getScriptFile() );
-        pModuleDefinition->setModuleScriptFilePath( formatBuffer );
-    }
-
-    // Format module signature,
-    dSprintf( formatBuffer, sizeof(formatBuffer), "%s_%d_%d", moduleId, versionId, pModuleDefinition->getBuildId() );
-    pModuleDefinition->setSignature( formatBuffer );
-
-    // Locked the module definition.
-    pModuleDefinition->setLocked( true );
-
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Did we find the module Id?
-    if ( pDefinitions != NULL )
-    {
-        // Yes, so find the module definition.
-        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
-
-        // Does this version Id already exist?
-        if ( definitionItr != NULL )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: Found module: '%s' but it is already registered as module Id '%s' at version Id '%d'.",
-                pModuleDefinition->getModuleFilePath(), moduleId, versionId );
-
-            // Destroy module definition and finish.
-            pModuleDefinition->deleteObject();
-            return false;
-        }
-
-        // Is the module group the same as the module definitions we already have?
-        if ( moduleGroup != pDefinitions->mModuleGroup )
-        {
-            // No, so warn.
-            Con::warnf( "Module Manager: Found module: '%s' but its module group '%s' is not the same as other module definitions of the same module Id.",
-                pModuleDefinition->getModuleFilePath(), moduleGroup );
-
-            // Destroy module definition and finish.
-            pModuleDefinition->deleteObject();
-            return false;
-        }
-
-        // Is the module type the same as the module definitions we already have?
-        if ( moduleType != pDefinitions->mModuleType )
-        {
-            // No, so warn.
-            Con::warnf( "Module Manager: Found module: '%s' but its module type '%s' is not the same as other module definitions of the same module Id.",
-                pModuleDefinition->getModuleFilePath(), moduleGroup );
-
-            // Destroy module definition and finish.
-            pModuleDefinition->deleteObject();
-            return false;
-        }
-    }
-    else
-    {
-        // No, so create a vector of definitions.
-        pDefinitions = new ModuleDefinitionEntry( moduleId, moduleGroup, moduleType );
-
-        // Insert module Id definitions.
-        mModuleIdDatabase.insert( moduleId, pDefinitions );
-    }
-    
-    // Add module definition.
-    pDefinitions->push_back( pModuleDefinition );
-
-    // Sort module definitions by version Id so that higher versions appear first.
-    dQsort( pDefinitions->address(), pDefinitions->size(), sizeof(ModuleDefinition*), moduleDefinitionVersionIdSort );
-
-    // Find module group.
-    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
-
-    // Did we find the module group?
-    if ( moduleGroupItr != mGroupModules.end() )
-    {
-        // Yes, so fetch module Ids.
-        typeModuleIdVector* pModuleIds = moduleGroupItr->value;
-
-        // Is the module Id already present?
-        bool moduleIdFound = false;
-        for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
-        {
-            // Skip if this isn't the Id.
-            if ( *moduleIdItr == moduleId )
-            {
-                moduleIdFound = true;
-                break;
-            }
-        }
-
-        // Add if module Id was not found.
-        if ( !moduleIdFound )
-            pModuleIds->push_back( moduleId );
-    }
-    else
-    {
-        // No, so insert a module Id vector.
-        moduleGroupItr = mGroupModules.insert( pModuleDefinition->getModuleGroup(), new typeModuleIdVector() );
-
-        // Add module Id.
-        moduleGroupItr->value->push_back( moduleId );
-    }
-
-    // Notify if the module definition is destroyed.
-    deleteNotify( pModuleDefinition );
-
-    // Info.
-    if ( mEchoInfo )
-    {
-#if 1
-        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s' ].",
-            pModuleDefinition->getModuleFilePath(),
-            pModuleDefinition->getModuleId(),
-            pModuleDefinition->getVersionId(),
-            pModuleDefinition->getBuildId(),
-            pModuleDefinition->getModuleDescription()
-            );
-#else
-        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s', Group='%s', Dependencies='%s', ScriptFile='%s', CreateFunction='%s', DestroyFunction='%s' ].",
-            pModuleDefinition->getModuleFilePath(),
-            pModuleDefinition->getModuleId(),
-            pModuleDefinition->getVersionId(),
-            pModuleDefinition->getBuildId(),
-            pModuleDefinition->getModuleDescription(),
-            pModuleDefinition->getModuleGroup(),
-            pModuleDefinition->getDataField( StringTable->insert("Dependencies"), NULL ),
-            pModuleDefinition->getScriptFile(),
-            pModuleDefinition->getCreateFunction(),
-            pModuleDefinition->getDestroyFunction()
-            );
-#endif
-    }
-
-    // Emit notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        Con::executef( *notifyItr, 2, "onModuleRegister", pModuleDefinition->getIdString() );
-    }
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePreLoadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePreLoad( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePreLoad" ) )
-            Con::executef( pListener, 2, "onModulePreLoad", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePostLoadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePostLoad( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePostLoad" ) )
-            Con::executef( pListener, 2, "onModulePostLoad", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePreUnloadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePreUnload( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePreUnload" ) )
-            Con::executef( pListener, 2, "onModulePreUnload", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-void ModuleManager::raiseModulePostUnloadNotifications( ModuleDefinition* pModuleDefinition )
-{
-    // Raise notifications.
-    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
-    {
-        // Fetch listener object.
-        SimObject* pListener = *notifyItr;
-
-        // Perform object callback.
-        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
-        if ( pCallbacks != NULL )
-            pCallbacks->onModulePostUnload( pModuleDefinition );             
-            
-        // Perform script callback.
-        if ( pListener->isMethod( "onModulePostUnload" ) )
-            Con::executef( pListener, 2, "onModulePostUnload", pModuleDefinition->getIdString() );
-    }
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleDefinitionEntry* ModuleManager::findModuleId( StringTableEntry moduleId )
-{
-    // Sanity!
-    AssertFatal( moduleId != NULL, "A module Id cannot be NULL." );
-
-    // Is the module Id valid?
-    if ( moduleId == StringTable->EmptyString )
-    {
-        // No, so warn.
-        Con::warnf( "Module Manager: Invalid Module Id." );
-        return NULL;
-    }
-
-    // Find module Id.
-    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
-
-    // Return appropriately.
-    return moduleItr != mModuleIdDatabase.end() ? moduleItr->value : NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleDefinitionEntry::iterator ModuleManager::findModuleDefinition( StringTableEntry moduleId, const U32 versionId )
-{
-    // Fetch modules definitions.
-    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
-
-    // Finish if no module definitions for the module Id.
-    if ( pDefinitions == NULL )
-        return NULL;
-
-    // Iterate module definitions.
-    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
-    {
-        // Skip if this isn't the version Id we're searching for.
-        if ( versionId != (*definitionItr)->getVersionId() )
-            continue;
-
-        // Return module definition iterator.
-        return definitionItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-bool ModuleManager::resolveModuleDependencies( StringTableEntry moduleId, const U32 versionId, StringTableEntry moduleGroup, bool synchronizedOnly, typeModuleLoadEntryVector& moduleResolvingQueue, typeModuleLoadEntryVector& moduleReadyQueue )
-{
-    // Fetch the module Id ready entry.
-    ModuleLoadEntry* pLoadReadyEntry = findModuleReady( moduleId, moduleReadyQueue );
-
-    // Is there a load entry?
-    if ( pLoadReadyEntry )
-    {
-        // Yes, so finish if the version Id is not important,
-        if ( versionId == 0 )
-            return true;
-
-        // Finish if the version Id are compatible.
-        if ( versionId == pLoadReadyEntry->mpModuleDefinition->getVersionId() )
-            return true;
-
-        // Is it a strict version Id?
-        if ( pLoadReadyEntry->mStrictVersionId )
-        {
-            // Yes, so warn.
-            Con::warnf( "Module Manager: A module dependency was detected loading module Id '%s' at version Id '%d' in group '%s' but an version Id '%d' is also required.",
-                moduleId, versionId, pLoadReadyEntry->mpModuleDefinition->getVersionId(), moduleGroup );
-            return false;
-        }
-
-        // No, so find the required module version Id.
-        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
-
-        // Did we find the requested module definition.
-        if ( definitionItr == NULL )
-        {
-            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
-            if ( !mEnforceDependencies )
-                return true;
-
-            // Warn!
-            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
-                moduleId, versionId, moduleGroup );
-            return false;
-        }
-
-        // Set the new module definition.
-        pLoadReadyEntry->mpModuleDefinition = *definitionItr;
-
-        // Set strict version Id.
-        pLoadReadyEntry->mStrictVersionId = true;
-                
-        return true;
-    }
-
-    // Is the module Id load resolving?
-    if ( findModuleResolving( moduleId, moduleResolvingQueue ) != NULL )
-    {
-        // Yes, so a cycle has been detected so warn.
-        Con::warnf( "Module Manager: A cyclic dependency was detected resolving module Id '%s' at version Id '%d' in group '%s'.",
-            moduleId, versionId, moduleGroup );
-        return false;
-    }
-
-    // Reset selected module definition.
-    ModuleDefinition* pSelectedModuleDefinition = NULL;
-
-    // Do we want the latest version Id?
-    if ( versionId == 0 )
-    {
-        // Yes, so find the module Id.
-        typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.find( moduleId );
-
-        // Did we find the module Id?
-        if ( moduleIdItr == mModuleIdDatabase.end() )
-        {
-            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
-            if ( !mEnforceDependencies )
-                return true;
-
-            // Warn!
-            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
-                moduleId, versionId, moduleGroup );
-            return false;
-        }
-
-        // Fetch first module definition which should be the highest version Id.
-        pSelectedModuleDefinition = (*moduleIdItr->value)[0];
-    }
-    else
-    {
-        // No, so find the module Id at the specific version Id.
-        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
-
-        // Did we find the module definition?
-        if ( definitionItr == NULL )
-        {
-            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
-            if ( !mEnforceDependencies )
-                return true;
-
-            // Warn!
-            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
-                moduleId, versionId, moduleGroup );
-            return false;
-        }
-
-        // Select the module definition.
-        pSelectedModuleDefinition = *definitionItr;
-    }
-
-    // If we're only resolving synchronized modules and the module is not synchronized then finish.
-    if ( synchronizedOnly && !pSelectedModuleDefinition->getSynchronized() )
-        return true;
-
-    // Create a load entry.
-    ModuleLoadEntry loadEntry( pSelectedModuleDefinition, false );
-
-    // Fetch module dependencies.
-    const ModuleDefinition::typeModuleDependencyVector& moduleDependencies = pSelectedModuleDefinition->getDependencies();
-
-    // Do we have any module dependencies?
-    if ( moduleDependencies.size() > 0 )
-    {
-        // Yes, so queue this module as resolving.
-        moduleResolvingQueue.push_back( loadEntry );
-
-        // Iterate module dependencies.
-        for( ModuleDefinition::typeModuleDependencyVector::const_iterator dependencyItr = moduleDependencies.begin(); dependencyItr != moduleDependencies.end(); ++dependencyItr )
-        {            
-            // Finish if we could not the dependent module Id at the version Id.
-            if ( !resolveModuleDependencies( dependencyItr->mModuleId, dependencyItr->mVersionId, moduleGroup, synchronizedOnly, moduleResolvingQueue, moduleReadyQueue ) )
-                return false;
-        }
-
-        // Remove module as resolving.
-        moduleResolvingQueue.pop_back();
-    }
-
-    // Queue module as ready.
-    moduleReadyQueue.push_back( loadEntry );        
-
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleLoadEntry* ModuleManager::findModuleResolving( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleResolvingQueue )
-{
-    // Iterate module load resolving queue.
-    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleResolvingQueue.begin(); loadEntryItr != moduleResolvingQueue.end(); ++loadEntryItr )
-    {
-        // Finish if found.
-        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
-            return loadEntryItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::ModuleLoadEntry* ModuleManager::findModuleReady( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleReadyQueue )
-{
-    // Iterate module load ready queue.
-    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleReadyQueue.begin(); loadEntryItr != moduleReadyQueue.end(); ++loadEntryItr )
-    {
-        // Finish if found.
-        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
-            return loadEntryItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::typeModuleLoadEntryVector::iterator ModuleManager::findModuleLoaded( StringTableEntry moduleId, const U32 versionId )
-{
-    // Iterate module loaded queue.
-    for( typeModuleLoadEntryVector::iterator loadEntryItr = mModulesLoaded.begin(); loadEntryItr != mModulesLoaded.end(); ++loadEntryItr )
-    {
-        // Skip if not the module Id we're looking for.
-        if ( moduleId != loadEntryItr->mpModuleDefinition->getModuleId() )
-            continue;
-
-        // Skip if we are searching for a specific version and it does not match.
-        if ( versionId != 0 && versionId != loadEntryItr->mpModuleDefinition->getVersionId() )
-            continue;
-
-        return loadEntryItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-ModuleManager::typeGroupVector::iterator ModuleManager::findGroupLoaded( StringTableEntry moduleGroup )
-{
-    // Iterate groups loaded queue.
-    for( typeGroupVector::iterator groupsLoadedItr = mGroupsLoaded.begin(); groupsLoadedItr != mGroupsLoaded.end(); ++groupsLoadedItr )
-    {
-        // Finish if found.
-        if ( moduleGroup == *groupsLoadedItr )
-            return groupsLoadedItr;
-    }
-
-    // Not found.
-    return NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-StringTableEntry ModuleManager::getModuleMergeFilePath( void ) const
-{
-    // Format merge file path.
-    char filePathBuffer[1024];
-    dSprintf( filePathBuffer, sizeof(filePathBuffer), "%s/%s", Platform::getExecutablePath(), MODULE_MANAGER_MERGE_FILE );
-
-    return StringTable->insert( filePathBuffer );
-}
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "moduleManager.h"
+
+#ifndef _MODULE_MERGE_DEFINITION_H
+#include "moduleMergeDefinition.h"
+#endif
+
+#ifndef _TAML_MODULE_ID_UPDATE_VISITOR_H_
+#include "tamlModuleIdUpdateVisitor.h"
+#endif
+
+#ifndef _MODULE_CALLBACKS_H_
+#include "moduleCallbacks.h"
+#endif
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+// Script bindings.
+#include "moduleManager_ScriptBinding.h"
+
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT( ModuleManager );
+
+//-----------------------------------------------------------------------------
+
+ModuleManager ModuleDatabase;
+
+//-----------------------------------------------------------------------------
+
+S32 QSORT_CALLBACK moduleDefinitionVersionIdSort( const void* a, const void* b )
+{
+    // Fetch module definitions.
+   ModuleDefinition* pDefinition1 = *(ModuleDefinition**)a;
+   ModuleDefinition* pDefinition2 = *(ModuleDefinition**)b;
+
+   // Fetch version Ids.
+   const U32 versionId1 = pDefinition1->getVersionId();
+   const U32 versionId2 = pDefinition2->getVersionId();
+
+   // We sort higher version Id first.
+   return versionId1 > versionId2 ? -1 : versionId1 < versionId2 ? 1 : 0;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleManager() :
+    mEnforceDependencies(true),
+    mEchoInfo(true),
+    mDatabaseLocks( 0 )
+{
+    // Set module extension.
+    dStrcpy( mModuleExtension, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::onAdd()
+{
+    if( !Parent::onAdd() )
+        return false;
+
+    // Register listeners.
+    mNotificationListeners.registerObject();
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::onRemove()
+{
+    // Clear database.
+    clearDatabase();
+
+    // Unregister object.
+    mNotificationListeners.unregisterObject();
+
+    // Call parent.
+    Parent::onRemove();
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+
+    addField( "EnforceDependencies", TypeBool, Offset(mEnforceDependencies, ModuleManager), "Whether the module manager enforces any dependencies on module definitions it discovers or not." );
+    addField( "EchoInfo", TypeBool, Offset(mEchoInfo, ModuleManager), "Whether the module manager echos extra information to the console or not." );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::onDeleteNotify( SimObject *object )
+{
+    // Cast to a module definition.
+    ModuleDefinition* pModuleDefinition = dynamic_cast<ModuleDefinition*>( object );
+
+    // Ignore if not appropriate.
+    if ( pModuleDefinition == NULL )
+        return;
+
+    // Warn.
+    Con::warnf( "Module Manager::onDeleteNotify() - Notified of a module definition deletion for module Id '%s' of version Id '%d' however this should not happen and can cause module database corruption.",
+        pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::setModuleExtension( const char* pExtension )
+{
+    // Sanity!
+    AssertFatal( pExtension != NULL, "Cannot set module extension with NULL extension." );
+
+    // Did we find an extension period?
+    if ( *pExtension == '.' )
+    {
+        // Yes, so warn.
+        Con::warnf("Module Manager: Failed to set extension as supplied extension contains an initial period: '%s'.", pExtension );
+        return false;
+    }
+
+    // Is the extension too large?
+    if ( dStrlen( pExtension ) > sizeof( mModuleExtension ) )
+    {
+        // Yes, so warn.
+        Con::warnf("Module Manager: Failed to set extension as supplied extension is too large: '%s'.", pExtension );
+        return false;
+    }
+
+    // Set module extension.
+    dStrcpy( mModuleExtension, pExtension );
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::scanModules( const char* pPath, const bool rootOnly )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pPath != NULL, "Cannot scan module with NULL path." );
+
+    // Expand module location.
+    char pathBuffer[1024];
+    Con::expandPath( pathBuffer, sizeof(pathBuffer), pPath );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Started scanning '%s'...", pathBuffer );
+    }
+
+    Vector<StringTableEntry> directories;
+
+    // Find directories.
+    if ( !Platform::dumpDirectories( pathBuffer, directories, 1 ) )
+    {
+        // Failed so warn.
+        Con::warnf( "Module Manager: Failed to scan module directories in path '%s'.", pathBuffer );
+        return false;
+    }
+
+    // Fetch extension length.
+    const U32 extensionLength = dStrlen( mModuleExtension );
+
+    Vector<Platform::FileInfo> files;
+
+    // Iterate directories.
+    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
+    {
+        // Fetch base path.
+        StringTableEntry basePath = *basePathItr;
+
+        // Skip if we're only processing the root and this is not the root.
+        if ( rootOnly && basePathItr != directories.begin() )
+            continue;
+
+        // Find files.
+        files.clear();
+        if ( !Platform::dumpPath( basePath, files, 0 ) )
+        {
+            // Failed so warn.
+            Con::warnf( "Module Manager: Failed to scan modules files in directory '%s'.", basePath );
+            return false;
+        }
+
+        // Iterate files.
+        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
+        {
+            // Fetch file info.
+            Platform::FileInfo* pFileInfo = fileItr;
+
+            // Fetch filename.
+            const char* pFilename = pFileInfo->pFileName;
+
+            // Find filename length.
+            const U32 filenameLength = dStrlen( pFilename );
+
+            // Skip if extension is longer than filename.
+            if ( extensionLength > filenameLength )
+                continue;
+
+            // Skip if extension not found.
+            if ( dStricmp( pFilename + filenameLength - extensionLength, mModuleExtension ) != 0 )
+                continue;
+
+            // Register module.
+            registerModule( basePath, pFileInfo->pFileName );
+        }
+
+        // Stop processing if we're only processing the root.
+        if ( rootOnly )
+            break;
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printf( "Module Manager: Finished scanning '%s'.", pathBuffer );
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::loadModuleGroup( const char* pModuleGroup )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleGroup != NULL, "Cannot load module group with NULL group name." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Loading group '%s':" ,moduleGroup );
+    }
+
+    // Is the module group already loaded?
+    if ( findGroupLoaded( moduleGroup ) != NULL )
+    {
+        // Yes, so warn.
+        Con::warnf( "Module Manager: Cannot load group '%s' as it is already loaded.", moduleGroup );
+        return false;
+    }
+
+    // Find module group.
+    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
+
+    // Did we find the module group?
+    if ( moduleGroupItr == mGroupModules.end() )
+    {
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
+        }
+        
+        return true;
+    }
+
+    // Yes, so fetch the module Ids.
+    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+    // Iterate module groups.
+    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+    {
+        // Fetch module Id.
+        StringTableEntry moduleId = *moduleIdItr;
+
+        // Finish if we could not resolve the dependencies for module Id (of any version Id).
+        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+            return false;
+    }
+
+    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is already loaded but at version Id '%d'.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        // Info.
+        Con::printf( "Module Manager: Group '%s' and its dependencies is comprised of the following '%d' module(s):", moduleGroup, moduleReadyQueue.size() );
+
+        // Iterate the modules echoing them.
+        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+        {
+            // Fetch the ready entry.
+            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
+
+            // Info.
+            Con::printf( "> module Id '%s' at version Id '%d':", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
+        }
+    }
+
+    // Add module group.
+    mGroupsLoaded.push_back( moduleGroup );
+
+    // Reset modules loaded count.
+    U32 modulesLoadedCount = 0;
+
+    // Iterate the modules, executing their script files and call their create function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module already loaded.
+        if ( pLoadedEntry != NULL )
+        {
+            // Yes, so increase load count.
+            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
+
+            // Skip.
+            continue;
+        }
+
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printSeparator();
+            Con::printf( "Module Manager: Loading group '%s' : module Id '%s' at version Id '%d' in group '%s' using the script file '%s'.",
+                moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+        }
+
+        // Is the module deprecated?
+        if ( pLoadReadyModuleDefinition->getDeprecated() )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' in group '%s' is deprecated.  You should use a newer version!",
+                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
+        }
+
+        // Add the path expando for module.
+        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
+
+        // Create a scope set.
+        SimSet* pScopeSet = new SimSet;
+        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
+        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
+
+        // Increase load count.
+        pReadyEntry->mpModuleDefinition->increaseLoadCount();
+
+        // Queue module loaded.
+        mModulesLoaded.push_back( *pReadyEntry );
+
+        // Bump modules loaded count.
+        modulesLoadedCount++;
+
+        // Raise notifications.
+        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
+
+        // Do we have a script file-path specified?
+        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
+        {
+            // Yes, so execute the script file.
+            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
+
+            // Did we execute the script file?
+            if ( scriptFileExecuted )
+            {
+                // Yes, so is the create method available?
+                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
+                {
+                    // Yes, so call the create method.
+                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
+                }
+            }
+            else
+            {
+                // No, so warn.
+                Con::errorf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+            }
+        }
+
+        // Raise notifications.
+        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish loading '%d' module(s) for group '%s'.", modulesLoadedCount, moduleGroup );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::unloadModuleGroup( const char* pModuleGroup )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleGroup != NULL, "Cannot unload module group with NULL group name." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Unloading group '%s':" , moduleGroup );
+    }
+
+    // Find the group loaded iterator.
+    typeGroupVector::iterator groupLoadedItr = findGroupLoaded( moduleGroup );
+
+    // Is the module group already unloaded?
+    if ( groupLoadedItr == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unload group '%s' as it is not loaded.", moduleGroup );
+        return false;
+    }
+
+    // Find module group.
+    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
+
+    // Did we find the module group?
+    if ( moduleGroupItr == mGroupModules.end() )
+    {
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
+            return true;
+        }
+    }
+
+    // Yes, so fetch the module Ids.
+    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+    // Iterate module groups.
+    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+    {
+        // Fetch module Id.
+        StringTableEntry moduleId = *moduleIdItr;
+
+        // Finish if we could not resolve the dependencies for module Id (of any version Id).
+        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+            return false;
+    }
+
+    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot unload module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is loaded but at version Id '%d'.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Remove module group.
+    mGroupsLoaded.erase_fast( groupLoadedItr );
+
+    // Reset modules unloaded count.
+    U32 modulesUnloadedCount = 0;
+
+    // Iterate the modules in reverse order calling their destroy function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module loaded.
+        if ( pLoadedEntry == NULL )
+        {
+            // No, so warn.
+            if ( mEchoInfo )
+            {
+                Con::printf( "Module Manager: Unloading group '%s' but could not unload module Id '%s' at version Id '%d'.",
+                    moduleGroup, pLoadedEntry->mpModuleDefinition->getModuleId(), pLoadedEntry->mpModuleDefinition->getVersionId() );
+            }
+            // Skip.
+            continue;
+        }
+
+        // Reduce load count.
+        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
+
+        // Sanity!
+        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
+
+        // Do we need to unload?
+        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
+        {
+            // Yes, so info.
+            if ( mEchoInfo )
+            {
+                Con::printSeparator();
+                Con::printf( "Module Manager: Unload group '%s' with module Id '%s' at version Id '%d' in group '%s'.",
+                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
+            }
+
+            // Raise notifications.
+            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
+
+            // Fetch the module Id loaded entry.
+            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Sanity!
+            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleGroup() - Cannot find module to unload it." );
+
+            // Dequeue module loaded.
+            mModulesLoaded.erase_fast( moduleLoadedItr );
+
+            // Fetch scope set.
+            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
+
+            // Is the destroy method available?
+            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
+            {
+                // Yes, so call the destroy method.
+                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
+            }
+
+            // Remove scope set.
+            pScopeSet->deleteObjects();
+            pScopeSet->unregisterObject();
+            pLoadReadyModuleDefinition->mScopeSet = 0;
+
+            // Remove path expando for module.
+            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Bump modules unloaded count.
+            modulesUnloadedCount++;
+
+            // Raise notifications.
+            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish unloading '%d' module(s) for group '%s'.", modulesUnloadedCount, moduleGroup );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::loadModuleExplicit( const char* pModuleId, const U32 versionId )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot load explicit module Id with NULL module Id." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Did we find the module Id?
+    if ( pDefinitions == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot load explicit module Id '%s' as it does not exist.", moduleId );
+        return false;
+    }
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d':", moduleId, versionId );
+    }
+
+    // Finish if we could not resolve the dependencies for module Id (of any version Id).
+    if ( !resolveModuleDependencies( moduleId, versionId, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+        return false;
+
+    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as the module Id is already loaded but at version Id '%d'.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        // Info.
+        Con::printf( "Module Manager: Explicit load of module Id '%s' at version Id '%d' and its dependencies is comprised of the following '%d' module(s):", moduleId, versionId, moduleReadyQueue.size() );
+
+        // Iterate the modules echoing them.
+        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+        {
+            // Fetch the ready entry.
+            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
+
+            // Info.
+            Con::printf( "> module Id '%s' at version Id '%d'", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
+        }
+    }
+
+    // Reset modules loaded count.
+    U32 modulesLoadedCount = 0;
+
+    // Iterate the modules, executing their script files and call their create function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module already loaded.
+        if ( pLoadedEntry != NULL )
+        {
+            // Yes, so increase load count.
+            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
+
+            // Skip.
+            continue;
+        }
+
+        // No, so info.
+        if ( mEchoInfo )
+        {
+            Con::printSeparator();
+            Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d' using the script file '%s'.",
+                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+        }
+
+        // Is the module deprecated?
+        if ( pLoadReadyModuleDefinition->getDeprecated() )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' is deprecated,  You should use a newer version!",
+                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
+        }
+
+        // Add the path expando for module.
+        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
+
+        // Create a scope set.
+        SimSet* pScopeSet = new SimSet;
+        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
+        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
+
+        // Increase load count.
+        pReadyEntry->mpModuleDefinition->increaseLoadCount();
+
+        // Queue module loaded.
+        mModulesLoaded.push_back( *pReadyEntry );
+
+        // Bump modules loaded count.
+        modulesLoadedCount++;
+
+        // Raise notifications.
+        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
+
+        // Do we have a script file-path specified?
+        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString )
+        {
+            // Yes, so execute the script file.
+            const bool scriptFileExecuted = dAtob( Con::executef(2, "exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
+
+            // Did we execute the script file?
+            if ( scriptFileExecuted )
+            {
+                // Yes, so is the create method available?
+                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
+                {
+                    // Yes, so call the create method.
+                    Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getCreateFunction() );
+                }
+            }
+            else
+            {
+                // No, so warn.
+                Con::errorf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
+            }
+        }
+
+        // Raise notifications.
+        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish loading '%d' explicit module(s).", modulesLoadedCount );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::unloadModuleExplicit( const char* pModuleId )
+{
+    // Lock database.
+    LockDatabase( this );
+
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot unload explicit module Id with NULL module Id." );
+
+    typeModuleLoadEntryVector   moduleResolvingQueue;
+    typeModuleLoadEntryVector   moduleReadyQueue;
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Did we find the module Id?
+    if ( pDefinitions == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it does not exist.", moduleId );
+        return false;
+    }
+
+    // Find if the module is actually loaded.
+    ModuleDefinition* pLoadedModule = findLoadedModule( moduleId );
+
+    // Is the module loaded?
+    if ( pLoadedModule == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it is not loaded.", moduleId );
+        return false;
+    }
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Unloading explicit module Id '%s':" , moduleId );
+    }
+
+    // Finish if we could not resolve the dependencies for module Id (of any version Id).
+    if ( !resolveModuleDependencies( moduleId, pLoadedModule->getVersionId(), moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
+        return false;
+
+    // Check the modules we want to unload to ensure that we do not have incompatible modules loaded already.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
+    {
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
+
+        // Fetch the module Id loaded entry.
+        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Did we find a loaded entry?
+        if ( pLoadedModuleEntry != NULL )
+        {
+            // Yes, so is it the one we need to load?
+            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
+            {
+                // Yes, so warn.
+                Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' at version Id '%d' as the module Id is loaded but at version Id '%d'.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
+                return false;
+            }
+        }
+    }
+
+    // Reset modules unloaded count.
+    U32 modulesUnloadedCount = 0;
+
+    // Iterate the modules in reverse order calling their destroy function.
+    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
+    {
+        // Fetch the ready entry.
+        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
+
+        // Fetch load ready module definition.
+        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
+
+        // Fetch any loaded entry for the module Id.
+        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+        // Is the module loaded.
+        if ( pLoadedEntry == NULL )
+        {
+            // No, so warn.
+            if ( mEchoInfo )
+            {
+                Con::printf( "Module Manager: Unloading explicit module Id '%s' at version Id '%d' but ignoring as it is not loaded.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
+            }
+
+            // Skip.
+            continue;
+        }
+
+        // Reduce load count.
+        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
+
+        // Sanity!
+        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
+
+        // Do we need to unload?
+        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
+        {
+            // Yes, so info.
+            if ( mEchoInfo )
+            {
+                Con::printSeparator();
+                Con::printf( "Module Manager: Unload explicit module Id '%s' at version Id '%d'.",
+                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
+            }
+
+            // Raise notifications.
+            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
+
+            // Fetch the module Id loaded entry.
+            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Sanity!
+            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleExplicit() - Cannot find module to unload it." );
+
+            // Dequeue module loaded.
+            mModulesLoaded.erase_fast( moduleLoadedItr );
+
+            // Fetch scope set.
+            SimSet* pScopeSet = Sim::findObject<SimSet>( pLoadReadyModuleDefinition->mScopeSet );
+
+            // Is the destroy method available?
+            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
+            {
+                // Yes, so call the destroy method.
+                Con::executef( pScopeSet, 1, pLoadReadyModuleDefinition->getDestroyFunction() );
+            }
+
+            // Remove scope set.
+            pScopeSet->deleteObjects();
+            pScopeSet->unregisterObject();
+            pLoadReadyModuleDefinition->mScopeSet = 0;
+
+            // Remove path expando for module.
+            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
+
+            // Bump modules unloaded count.
+            modulesUnloadedCount++;
+
+            // Raise notifications.
+            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printSeparator();
+        Con::printf( "Module Manager: Finish unloading '%d' explicit module(s).", modulesUnloadedCount );
+        Con::printSeparator();
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleDefinition* ModuleManager::findModule( const char* pModuleId, const U32 versionId )
+{
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
+
+    // Find module definition.
+    ModuleDefinitionEntry::iterator moduleItr = findModuleDefinition( StringTable->insert( pModuleId ), versionId );
+
+     // Finish if module was not found.
+    if ( moduleItr == NULL )
+        return NULL;
+
+    return *moduleItr;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleDefinition* ModuleManager::findLoadedModule( const char* pModuleId )
+{
+    // Sanity!
+    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
+
+    // Fetch module Id.
+    StringTableEntry moduleId = StringTable->insert( pModuleId );
+
+    // Iterate loaded modules.
+    for ( typeModuleLoadEntryVector::iterator loadedModuleItr = mModulesLoaded.begin(); loadedModuleItr != mModulesLoaded.end(); ++loadedModuleItr )
+    {
+        // Skip if not the module.
+        if ( loadedModuleItr->mpModuleDefinition->getModuleId() != moduleId )
+            continue;
+
+        return loadedModuleItr->mpModuleDefinition;
+    }
+
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::findModules( const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
+{
+    // Iterate module Ids.
+    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
+    {
+        // Fetch module definition entry.
+        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
+
+        // Iterate module definitions.
+        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
+        {
+            // Fetch module definition.
+            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
+
+            // Are we searching for loaded modules only?
+            if ( loadedOnly )
+            {
+                // Yes, so skip if the module is not loaded.
+                if ( pModuleDefinition->getLoadCount() == 0 )
+                    continue;
+
+                // Use module definition.
+                moduleDefinitions.push_back( pModuleDefinition );
+
+                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
+                break;
+            }
+
+            // use module definition.
+            moduleDefinitions.push_back( pModuleDefinition );
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::findModuleTypes( const char* pModuleType, const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
+{
+    // Fetch module type.
+    StringTableEntry moduleType = StringTable->insert( pModuleType );
+
+    // Iterate module Ids.
+    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
+    {
+        // Fetch module definition entry.
+        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
+
+        // Skip if note the module type we're searching for.
+        if ( pModuleDefinitionEntry->mModuleType != moduleType )
+            continue;
+
+        // Iterate module definitions.
+        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
+        {
+            // Fetch module definition.
+            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
+
+            // Are we searching for loaded modules only?
+            if ( loadedOnly )
+            {
+                // Yes, so skip if the module is not loaded.
+                if ( pModuleDefinition->getLoadCount() == 0 )
+                    continue;
+
+                // Use module definition.
+                moduleDefinitions.push_back( pModuleDefinition );
+
+                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
+                break;
+            }
+
+            // use module definition.
+            moduleDefinitions.push_back( pModuleDefinition );
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+StringTableEntry ModuleManager::copyModule( ModuleDefinition* pSourceModuleDefinition, const char* pTargetModuleId, const char* pTargetPath, const bool useVersionPathing )
+{
+    // Sanity!
+    AssertFatal( pSourceModuleDefinition != NULL, "Cannot copy module using a NULL source module definition." );
+    AssertFatal( pTargetModuleId != NULL, "Cannot copy module using a NULL target module Id." );
+    AssertFatal( pTargetPath != NULL, "Cannot copy module using a NULL target path." );
+
+    // Fetch the source module Id.
+    StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
+
+    // Is the source module definition registered with this module manager?
+    if ( pSourceModuleDefinition->getModuleManager() != this )
+    {
+        // No, so warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' as it is not registered with this module manager.", sourceModuleId );
+        return StringTable->EmptyString;
+    }
+
+    // Fetch the target module Id.
+    StringTableEntry targetModuleId = StringTable->insert( pTargetModuleId );
+
+    // Extend moduleId/VersionId pathing.
+    char versionPathBuffer[1024];
+
+    // Are we using version pathing?
+    if ( useVersionPathing )
+    {
+        // Yes, so format it.
+        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s/%s/%d",
+            pTargetPath, targetModuleId, pSourceModuleDefinition->getVersionId() );
+    }
+    else
+    {
+        // No, so a straight copy.
+        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s", pTargetPath );
+    }
+
+    // Expand the path.
+    char targetPathBuffer[1024];
+    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), versionPathBuffer );
+    pTargetPath = targetPathBuffer;
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printf( "Module Manager: Started copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
+    }
+
+    // Is the target folder a directory?
+    if ( !Platform::isDirectory( pTargetPath ) )
+    {
+        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
+        char createDirectoryBuffer[1024];
+        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetPath, NULL, true );
+
+        // No, so can we create it?
+        if ( !Platform::createPath( createDirectoryBuffer ) )
+        {
+            // No, so warn.
+            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory was not found and could not be created.",
+                sourceModuleId, pTargetPath );
+            return StringTable->EmptyString;
+        }
+    }
+
+    // Copy the source module to the target folder.
+    if ( !Platform::pathCopy( pSourceModuleDefinition->getModulePath(), pTargetPath, false ) )
+    {
+        // Warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory copy failed.",
+            sourceModuleId, pTargetPath );
+        return StringTable->EmptyString;
+    }
+
+    // Format the new source module definition file-path.
+    char newModuleDefinitionSourceFileBuffer[1024];
+    dSprintf( newModuleDefinitionSourceFileBuffer, sizeof(newModuleDefinitionSourceFileBuffer), "%s/%s", pTargetPath, pSourceModuleDefinition->getModuleFile() );
+
+    // Finish if source/target module Ids are identical.
+    if ( sourceModuleId == targetModuleId )
+        return StringTable->insert( newModuleDefinitionSourceFileBuffer );
+
+    // Format the new target module definition file-path.
+    char newModuleDefinitionTargetFileBuffer[1024];
+    dSprintf( newModuleDefinitionTargetFileBuffer, sizeof(newModuleDefinitionTargetFileBuffer), "%s/%s.%s", pTargetPath, targetModuleId, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
+
+    // Rename the module definition.
+    if ( !Platform::fileRename( newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer ) )
+    {
+        // Warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as renaming the module from '%s' to '%s' failed.",
+            sourceModuleId, pTargetPath, newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer );
+        return StringTable->EmptyString;
+    }
+
+    Vector<StringTableEntry> directories;
+
+    // Find directories.
+    if ( !Platform::dumpDirectories( pTargetPath, directories, -1 ) )
+    {
+        // Warn.
+        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
+            sourceModuleId, pTargetPath );
+        return StringTable->EmptyString;
+    }
+
+    TamlModuleIdUpdateVisitor moduleIdUpdateVisitor;
+    moduleIdUpdateVisitor.setModuleIdFrom( sourceModuleId );
+    moduleIdUpdateVisitor.setModuleIdTo( targetModuleId );
+
+    Vector<Platform::FileInfo> files;
+
+    const char* pExtension = (const char*)"Taml";
+    const U32 extensionLength = dStrlen(pExtension);
+
+    // Iterate directories.
+    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
+    {
+        // Fetch base path.
+        StringTableEntry basePath = *basePathItr;
+
+        // Find files.
+        files.clear();
+        if ( !Platform::dumpPath( basePath, files, 0 ) )
+        {
+            // Warn.
+            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
+                sourceModuleId, pTargetPath );
+            return StringTable->EmptyString;
+        }
+
+        // Iterate files.
+        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
+        {
+            // Fetch file info.
+            Platform::FileInfo* pFileInfo = fileItr;
+
+            // Fetch filename.
+            const char* pFilename = pFileInfo->pFileName;
+
+            // Find filename length.
+            const U32 filenameLength = dStrlen( pFilename );
+
+            // Skip if extension is longer than filename.
+            if ( extensionLength >= filenameLength )
+                continue;
+
+            // Skip if extension not found.
+            if ( dStricmp( pFilename + filenameLength - extensionLength, pExtension ) != 0 )
+                continue;
+
+            char parseFileBuffer[1024];
+            dSprintf( parseFileBuffer, sizeof(parseFileBuffer), "%s/%s", pFileInfo->pFullPath, pFilename );
+
+            // Parse file.
+            if ( !moduleIdUpdateVisitor.parse( parseFileBuffer ) )
+            {
+                // Warn.
+                Con::warnf("Module Manager: Failed to parse file '%s' whilst copying module Id '%s' using target directory '%s'.",
+                    parseFileBuffer, sourceModuleId, pTargetPath );
+                return StringTable->EmptyString;
+            }
+        }
+    }
+
+    // Info.
+    if ( mEchoInfo )
+    {
+        Con::printf( "Module Manager: Finished copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
+    }
+
+    return StringTable->insert( newModuleDefinitionTargetFileBuffer );
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::synchronizeDependencies( ModuleDefinition* pRootModuleDefinition, const char* pTargetDependencyPath )
+{
+    // Sanity!
+    AssertFatal( pRootModuleDefinition != NULL, "Cannot synchronize dependencies with NULL root module definition." );
+    AssertFatal( pTargetDependencyPath != NULL, "Cannot synchronize dependencies with NULL target dependency path." );
+
+    // Fetch the root module Id.
+    StringTableEntry rootModuleId = pRootModuleDefinition->getModuleId();
+
+    // Is the root module definition registered with this module manager?
+    if ( pRootModuleDefinition->getModuleManager() != this )
+    {
+        // No, so warn.
+        Con::warnf("Cannot synchronize dependencies for module Id '%s' as it is not registered with this module manager.", rootModuleId );
+        return false;
+    }
+
+    // Expand the path.
+    char targetPathBuffer[1024];
+    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pTargetDependencyPath );
+    pTargetDependencyPath = targetPathBuffer;
+
+    // Is the target dependency folder a directory?
+    if ( !Platform::isDirectory( pTargetDependencyPath ) )
+    {
+        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
+        char createDirectoryBuffer[1024];
+        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetDependencyPath, NULL, true );
+
+        // No, so can we create it?
+        if ( !Platform::createPath( createDirectoryBuffer ) )
+        {
+            // No, so warn.
+            Con::warnf("Cannot synchronize dependencies for module Id '%s' using target directory '%s' as directory was not found and could not be created.",
+                rootModuleId, pTargetDependencyPath );
+            return false;
+        }
+    }
+
+    typeModuleLoadEntryVector       resolvingQueue;
+    typeModuleLoadEntryVector       sourceModulesNeeded;
+
+    // Could we resolve source dependencies?
+    if ( !resolveModuleDependencies( rootModuleId, pRootModuleDefinition->getVersionId(), pRootModuleDefinition->getModuleGroup(), true, resolvingQueue, sourceModulesNeeded ) )
+    {
+        // No, so warn.
+        Con::warnf("Cannot synchronize dependencies for root module Id '%s' as its dependencies could not be resolved.", rootModuleId );
+        return false;
+    }
+
+    // Sanity!
+    AssertFatal( sourceModulesNeeded.size() > 0, "Cannot synchronize dependencies as no modules were returned." );
+
+    // Remove the root module definition.
+    sourceModulesNeeded.pop_back();
+
+    // Initialize the target module manager and scan the target folder for modules.
+    ModuleManager targetModuleManager;
+    targetModuleManager.mEnforceDependencies = true;
+    targetModuleManager.mEchoInfo = false;
+    targetModuleManager.scanModules( pTargetDependencyPath );
+
+    char targetFolderGenerateBuffer[1024];
+
+    // Iterate module definitions.
+    for ( typeModuleLoadEntryVector::iterator sourceModuleItr = sourceModulesNeeded.begin(); sourceModuleItr != sourceModulesNeeded.end(); ++sourceModuleItr )
+    {
+        // Fetch module definition.
+        ModuleDefinition* pSourceModuleDefinition = sourceModuleItr->mpModuleDefinition;
+        
+        // Fetch the source module Id,
+        StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
+
+        // Fetch the source module version Id.
+        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
+
+        // Fetch the source module build Id.
+        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
+
+        // Fetch module definition entry for this module Id in the target.
+        ModuleDefinitionEntry* pDefinitions = targetModuleManager.findModuleId( sourceModuleId );
+
+        // Is the module Id present in the target?
+        if ( pDefinitions == NULL )
+        {
+            // No, so format module Id folder path.
+            dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/", pTargetDependencyPath, sourceModuleId );
+
+            // Create module Id folder.
+            if ( !Platform::createPath( targetFolderGenerateBuffer ) )
+            {
+                // Warn.
+                Con::warnf("Cannot synchronize dependencies for module Id '%s' as the target directory '%s' could not be created.", sourceModuleId, targetFolderGenerateBuffer );
+                return false;
+            }
+        }
+        else
+        {
+            // Yes, so fetch the module definition for this module Id and version Id in the target.
+            ModuleDefinitionEntry::iterator definitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
+
+            // Is the specific module definition present in the target?
+            if ( definitionItr != NULL )
+            {
+                // Yes, so fetch the module definition.
+                ModuleDefinition* pTargetModuleDefinition = *definitionItr;
+
+                // Fetch the target module build Id.
+                const U32 targetBuildId = pTargetModuleDefinition->getBuildId();
+
+                // Fetch the target module path.
+                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
+
+                // Remove the target module definition from the database.
+                targetModuleManager.removeModuleDefinition( pTargetModuleDefinition );
+
+                // Skip if the target definition is the same build Id.
+                if ( targetBuildId == sourceBuildId )
+                    continue;
+
+                // Delete the target module definition folder.
+                if ( !Platform::deleteDirectory( targetModulePath ) )
+                {
+                    // Warn.
+                    Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the old module at '%s' could not be deleted.",
+                        sourceModuleId, sourceVersionId, targetModulePath );
+                    return false;
+                }
+            }
+        }
+
+        // Format source module path.
+        char sourceFolderPath[1024];
+        Con::expandPath( sourceFolderPath, sizeof(sourceFolderPath), pSourceModuleDefinition->getModulePath() );
+
+        // Format target module path.
+        dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/%d", pTargetDependencyPath, sourceModuleId, sourceVersionId );
+
+        // Copy the source module to the target folder.
+        if ( !Platform::pathCopy( sourceFolderPath, targetFolderGenerateBuffer, false ) )
+        {
+            // Warn.
+            Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the module could not be copied to '%s'.",
+                sourceModuleId, sourceVersionId, targetFolderGenerateBuffer );
+            return false;
+        }
+    }
+
+    // Find any target modules left, These are orphaned modules not depended upon by any other module.
+    typeConstModuleDefinitionVector orphanedTargetModules;
+    targetModuleManager.findModules( false, orphanedTargetModules );
+
+    // Iterate module definitions.
+    for ( typeConstModuleDefinitionVector::const_iterator moduleDefinitionItr = orphanedTargetModules.begin(); moduleDefinitionItr != orphanedTargetModules.end(); ++moduleDefinitionItr )
+    {
+        // Fetch orphaned module definition.
+        const ModuleDefinition* pOrphanedModuleDefinition = *moduleDefinitionItr;
+       
+        // Delete the target module definition folder.
+        if ( !Platform::deleteDirectory( pOrphanedModuleDefinition->getModulePath() ) )
+        {
+            // Warn.
+            Con::warnf("Cannot delete orphaned module Id '%s' at version Id '%d' from '%s'.",
+                pOrphanedModuleDefinition->getModuleId(), pOrphanedModuleDefinition->getVersionId(), pOrphanedModuleDefinition->getModulePath() );
+        }
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::canMergeModules( const char* pMergeSourcePath )
+{
+    // Sanity!
+    AssertFatal( pMergeSourcePath != NULL, "Cannot check merge modules with NULL source path." );
+
+    // Expand the source path.
+    char sourcePathBuffer[1024];
+    Con::expandPath( sourcePathBuffer, sizeof(sourcePathBuffer), pMergeSourcePath );
+    pMergeSourcePath = sourcePathBuffer;
+
+    // Is the path a valid directory?
+    if ( !Platform::isDirectory( sourcePathBuffer ) )
+    {
+        // No, so warn.
+        Con::warnf( "Cannot check merge modules as path is invalid '%s'.", sourcePathBuffer );
+        return false;
+    }
+
+    // Initialize the source module manager and scan the source folder for modules.
+    ModuleManager mergeModuleManager;
+    mergeModuleManager.mEnforceDependencies = false;
+    mergeModuleManager.mEchoInfo = false;
+    mergeModuleManager.scanModules( pMergeSourcePath );
+
+    // Find all the merge modules.
+    typeConstModuleDefinitionVector mergeModules;
+    mergeModuleManager.findModules( false, mergeModules );
+
+    // Iterate found merge module definitions.
+    for ( typeConstModuleDefinitionVector::const_iterator mergeModuleItr = mergeModules.begin(); mergeModuleItr != mergeModules.end(); ++mergeModuleItr )
+    {
+        // Fetch module definition.
+        const ModuleDefinition* pMergeModuleDefinition = *mergeModuleItr;
+
+        // Fetch module Id.
+        StringTableEntry moduleId = pMergeModuleDefinition->getModuleId();
+
+        // Fetch version Id.
+        const U32 versionId = pMergeModuleDefinition->getVersionId();
+
+        // Fetch module group.
+        StringTableEntry moduleGroup = pMergeModuleDefinition->getModuleGroup();
+
+        // Cannot merge if module already exists.
+        if ( findModuleDefinition( moduleId, versionId ) != NULL )
+            return false;
+
+        // Cannot merge if module is part of a loaded group.
+        if ( findGroupLoaded( moduleGroup ) != NULL )
+            return false;
+    }
+
+    // Can merge modules.
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::mergeModules( const char* pMergeTargetPath, const bool removeMergeDefinition, const bool registerNewModules )
+{
+    // Sanity!
+    AssertFatal( pMergeTargetPath != NULL, "Cannot merge modules with a target path of NULL." );
+
+    // Is a module merge available?
+    if ( !isModuleMergeAvailable() )
+    {
+        // No, so warn.
+        Con::warnf( "Cannot merge modules as a module merge is not available." );
+        return false;
+    }
+
+    // Expand the target path.
+    char targetPathBuffer[1024];
+    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pMergeTargetPath );
+    pMergeTargetPath = targetPathBuffer;
+
+    // Fetch module merge file path.
+    StringTableEntry moduleMergeFilePath = getModuleMergeFilePath();
+
+    // Read module merge definition.
+    Taml taml;
+    ModuleMergeDefinition* pModuleMergeDefinition = taml.read<ModuleMergeDefinition>( moduleMergeFilePath );
+    
+    // Do we have a module merge definition.
+    if ( pModuleMergeDefinition == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Cannot merge modules as the module merge definition file failed to load '%s'.", moduleMergeFilePath );
+        return false;
+    }
+
+    // Fetch the merge source path.
+    StringTableEntry mergeSourcePath = pModuleMergeDefinition->getModuleMergePath();
+
+    // Remove the module merge definition.
+    pModuleMergeDefinition->deleteObject();
+    pModuleMergeDefinition = NULL;
+
+    // If we cannot merge the modules then we only process modules flagged as critical merge.
+    const bool criticalMergeOnly = !canMergeModules( mergeSourcePath );
+
+    // Initialize the target module manager and scan the target folder for modules.
+    ModuleManager targetModuleManager;
+    targetModuleManager.mEnforceDependencies = false;
+    targetModuleManager.mEchoInfo = false;
+    targetModuleManager.scanModules( pMergeTargetPath );
+
+    // Initialize the source module manager and scan the source folder for modules.
+    ModuleManager sourceModuleManager;
+    sourceModuleManager.mEnforceDependencies = false;
+    sourceModuleManager.mEchoInfo = false;
+    sourceModuleManager.scanModules( mergeSourcePath );
+
+    // Find all the source modules.
+    typeConstModuleDefinitionVector sourceModules;
+    sourceModuleManager.findModules( false, sourceModules );
+
+    // Iterate found merge module definitions.
+    for ( typeConstModuleDefinitionVector::const_iterator sourceModuleItr = sourceModules.begin(); sourceModuleItr != sourceModules.end(); ++sourceModuleItr )
+    {
+        // Fetch the source module definition.
+        const ModuleDefinition* pSourceModuleDefinition = *sourceModuleItr;
+
+        // Skip if we're performing a critical merge only and the module is not flagged as critical merge.
+        if ( criticalMergeOnly && pSourceModuleDefinition->getCriticalMerge() )
+            continue;
+
+        // Fetch source module Id.
+        const StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
+
+        // Fetch source version Id.
+        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
+
+        // Fetch source build Id.
+        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
+
+        // Format module Id folder path.
+        char targetModuleIdBuffer[1024];
+        dSprintf( targetModuleIdBuffer, sizeof(targetModuleIdBuffer), "%s/%s/", pMergeTargetPath, sourceModuleId );
+
+        // Flag to indicate if the merged module needs registering.
+        bool shouldRegisterModule;
+
+        // Does the module Id exist?
+        if ( targetModuleManager.findModuleId( sourceModuleId ) == NULL )
+        {
+            // No, so create module Id folder.
+            if ( !Platform::createPath( targetModuleIdBuffer ) )
+            {
+                // Warn.
+                Con::warnf("Cannot merge modules for module '%s' as the path '%s' could not be created.", sourceModuleId, targetModuleIdBuffer );
+                return false;
+            }
+
+            // Module Should be registered.
+            shouldRegisterModule = true;
+        }
+        else
+        {
+            // Yes, so find the target module definition that matches the source module definition.
+            ModuleDefinitionEntry::iterator targetModuleDefinitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
+
+            // Is there an existing target module definition entry?
+            if ( targetModuleDefinitionItr != NULL )
+            {
+                // Yes, so fetch the target module definition.
+                const ModuleDefinition* pTargetModuleDefinition = *targetModuleDefinitionItr;
+
+                // Fetch target module path.
+                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
+
+                // Yes, so we have to remove it first.
+                if ( !Platform::deleteDirectory( targetModulePath ) )
+                {
+                    // Module was not deleted so warn.
+                    Con::warnf( "Failed to remove module folder located at '%s'.  Module will be copied over.", targetModulePath );
+                }
+
+                // Is the build Id being downgraded?
+                if ( sourceBuildId < pTargetModuleDefinition->getBuildId() )
+                {
+                    // Yes, so warn.
+                    Con::warnf( "Encountered a downgraded build Id for module Id '%s' at version Id '%d'.", sourceModuleId, sourceBuildId );
+                }
+
+                // Module should not be registered.
+                shouldRegisterModule = false;
+            }
+            else
+            {
+                // Module Should be registered.
+                shouldRegisterModule = true;
+            }
+        }
+
+        // Fetch source module path.
+        StringTableEntry sourceModulePath = pSourceModuleDefinition->getModulePath();
+
+        // Format target version Id folder path.
+        char targetVersionIdBuffer[1024];
+        dSprintf( targetVersionIdBuffer, sizeof(targetVersionIdBuffer), "%s%d", targetModuleIdBuffer, sourceVersionId );
+
+        // Copy module (allow overwrites as we may have failed to remove the old folder in which case this is likely to fail as well).
+        if ( !Platform::pathCopy( sourceModulePath, targetVersionIdBuffer, false ) )
+        {
+            // Failed to copy module.
+            Con::warnf( "Failed to copy module folder located at '%s' to location '%s'.  The modules may now be corrupted.", sourceModulePath, targetVersionIdBuffer );
+        }
+
+        // Are we registering new modules and the module needs registering?
+        if ( registerNewModules && shouldRegisterModule )
+        {
+            // Yes, so scan module.
+            scanModules( targetVersionIdBuffer, true );
+        }
+
+        // Is the module part of a critical merge?
+        if ( criticalMergeOnly )
+        {
+            // Yes, so we need to remove the source module definition.
+            if ( !Platform::deleteDirectory( sourceModulePath ) )
+            {
+                // Module was not deleted so warn.
+                Con::warnf( "Failed to remove CRITICAL merge module folder located at '%s'.  Module will be copied over.", sourceModulePath );
+            }
+        }
+    }
+
+    // Do we need to remove the module merge definition file?
+    if ( removeMergeDefinition )
+    {
+        // Yes, so remove it.
+        Platform::fileDelete( moduleMergeFilePath );
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::addListener( SimObject* pListener )
+{
+    // Sanity!
+    AssertFatal( pListener != NULL, "Cannot add notifications to a NULL object." );
+
+    // Ignore if already added.
+    if ( mNotificationListeners.find( pListener ) != mNotificationListeners.end() )
+        return;
+        
+    // Add as a listener.
+    mNotificationListeners.addObject( pListener );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::removeListener( SimObject* pListener )
+{
+    // Sanity!
+    AssertFatal( pListener != NULL, "Cannot remove notifications from a NULL object." );
+
+    // Remove as a listener.
+    mNotificationListeners.removeObject( pListener );
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::clearDatabase( void )
+{
+    // Lock database.
+    AssertFatal( mDatabaseLocks == 0, "Cannot clear database if database is locked." );
+
+    // Iterate groups loaded.
+    while ( mGroupsLoaded.size() > 0 )
+    {
+        // Unload module group.
+        unloadModuleGroup( *mGroupsLoaded.begin() );
+    }
+
+    // Iterate any other explicit modules that are loaded.
+    while ( mModulesLoaded.size() > 0 )
+    {
+        // Fetch module definition.
+        ModuleDefinition* pModuleDefinition = mModulesLoaded.begin()->mpModuleDefinition;
+
+        // Unload explicit module.
+        unloadModuleExplicit( pModuleDefinition->getModuleId() );
+    }
+
+    // Iterate modules to delete module definitions.
+    for ( typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.begin(); moduleItr != mModuleIdDatabase.end(); ++moduleItr )
+    {
+        // Fetch modules definitions.
+        ModuleDefinitionEntry* pDefinitions = moduleItr->value;
+
+        // Iterate module definitions.
+        for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
+        {
+            // Fetch module definition.
+            ModuleDefinition* pModuleDefinition = *definitionItr;
+
+            // Remove notification before we delete it.
+            clearNotify( pModuleDefinition );
+
+            // Delete module definition.
+            pModuleDefinition->deleteObject();
+        }
+
+        // Clear definitions.
+        delete pDefinitions;        
+    }
+
+    // Clear database.
+    mModuleIdDatabase.clear();
+
+    // Iterate module groups.
+    for ( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
+    {
+        // Delete module group vector.
+        delete moduleGroupItr->value;
+    }
+
+    // Clear module groups.
+    mGroupModules.clear();
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::removeModuleDefinition( ModuleDefinition* pModuleDefinition )
+{
+    // Sanity!
+    AssertFatal( pModuleDefinition != NULL, "Cannot remove module definition if it is NULL." );
+    
+    // Fetch module Id.
+    StringTableEntry moduleId = pModuleDefinition->getModuleId();
+
+    // Is the module definition registered with this module manager?
+    if ( pModuleDefinition->getModuleManager() != this )
+    {
+        // No, so warn.
+        Con::warnf("Cannot remove module definition '%s' as it is not registered with this module manager.", moduleId );
+        return false;
+    }
+
+    // Is the module definition loaded?
+    if ( pModuleDefinition->getLoadCount() > 0 )
+    {
+        // No, so warn.
+        Con::warnf("Cannot remove module definition '%s' as it is loaded.", moduleId );
+        return false;
+    }
+
+    // Find module Id.
+    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
+
+    // Sanity!
+    AssertFatal( moduleItr != mModuleIdDatabase.end(), "Failed to find module definition." );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = moduleItr->value;
+
+    // Fetch version Id.
+    const U32 versionId = pModuleDefinition->getVersionId();
+
+    // Iterate module definitions.
+    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
+    {
+        // Skip if this isn't the version Id we're searching for.
+        if ( versionId != (*definitionItr)->getVersionId() )
+            continue;
+
+        // Remove definition entry.
+        pDefinitions->erase( definitionItr ); 
+
+        // Remove notification before we delete it.
+        clearNotify( pModuleDefinition );
+
+        // Delete module definition.
+        pModuleDefinition->deleteObject();
+
+        return true;
+    }
+
+    // Sanity!
+    AssertFatal( false, "Failed to find module definition." );
+
+    return false;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::registerModule( const char* pModulePath, const char* pModuleFile )
+{
+    // Sanity!
+    AssertFatal( pModulePath != NULL, "Cannot scan module with NULL module path." );
+    AssertFatal( pModuleFile != NULL, "Cannot scan module with NULL module file." );
+
+    // Make the module path a full-path.
+    char fullPathBuffer[1024];
+    Platform::makeFullPathName( pModulePath, fullPathBuffer, sizeof(fullPathBuffer) );
+    pModulePath = fullPathBuffer;
+
+
+    char formatBuffer[1024];
+
+    // Fetch module path trail character.
+    char modulePathTrail = pModulePath[dStrlen(pModulePath) - 1];
+
+    // Format module file-path.
+    dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleFile );
+
+    // Read the module file.
+    ModuleDefinition* pModuleDefinition = mTaml.read<ModuleDefinition>( formatBuffer );
+
+    // Did we read a module definition?
+    if ( pModuleDefinition == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Failed to read module definition in file '%s'.", formatBuffer );
+        return false;
+    }
+
+    // Set the module manager.
+    pModuleDefinition->setModuleManager( this );
+
+    // Set module definition path.
+    pModuleDefinition->setModulePath( pModulePath );
+
+    // Set module file.
+    pModuleDefinition->setModuleFile( pModuleFile );
+
+    // Set module file-path.
+    pModuleDefinition->setModuleFilePath( formatBuffer );
+
+    // Fetch module Id.
+    StringTableEntry moduleId = pModuleDefinition->getModuleId();
+
+    // Fetch module version Id.
+    const U32 versionId = pModuleDefinition->getVersionId();
+
+    // Fetch module group.
+    StringTableEntry moduleGroup = pModuleDefinition->getModuleGroup();
+
+    // Fetch module type.
+    StringTableEntry moduleType = pModuleDefinition->getModuleType();
+
+    // Is the module enabled?
+    if ( !pModuleDefinition->getEnabled() )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Found module: '%s' but it is disabled.", pModuleDefinition->getModuleFilePath() );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Is the module Id valid?
+    if ( moduleId == StringTable->EmptyString )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Found module: '%s' but it has an unspecified module Id.",
+            pModuleDefinition->getModuleFilePath() );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Is the module version Id valid?
+    if ( versionId == 0 )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Found Manager: Registering module: '%s' but it has an invalid Version Id of '0'.",
+            pModuleDefinition->getModuleFilePath() );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Is the module group already loaded?
+    if ( findGroupLoaded( moduleGroup ) != NULL )
+    {
+        // Yes, so warn.
+        Con::warnf( "Module Manager: Found module: '%s' but it is in a module group '%s' which has already been loaded.",
+            pModuleDefinition->getModuleFilePath(),
+            moduleGroup );
+
+        // Destroy module definition and finish.
+        pModuleDefinition->deleteObject();
+        return false;
+    }
+
+    // Was a script-file specified?
+    if ( pModuleDefinition->getScriptFile() != StringTable->EmptyString )
+    {
+        // Yes, so format module script file-path.
+        dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleDefinition->getScriptFile() );
+        pModuleDefinition->setModuleScriptFilePath( formatBuffer );
+    }
+
+    // Format module signature,
+    dSprintf( formatBuffer, sizeof(formatBuffer), "%s_%d_%d", moduleId, versionId, pModuleDefinition->getBuildId() );
+    pModuleDefinition->setSignature( formatBuffer );
+
+    // Locked the module definition.
+    pModuleDefinition->setLocked( true );
+
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Did we find the module Id?
+    if ( pDefinitions != NULL )
+    {
+        // Yes, so find the module definition.
+        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
+
+        // Does this version Id already exist?
+        if ( definitionItr != NULL )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: Found module: '%s' but it is already registered as module Id '%s' at version Id '%d'.",
+                pModuleDefinition->getModuleFilePath(), moduleId, versionId );
+
+            // Destroy module definition and finish.
+            pModuleDefinition->deleteObject();
+            return false;
+        }
+
+        // Is the module group the same as the module definitions we already have?
+        if ( moduleGroup != pDefinitions->mModuleGroup )
+        {
+            // No, so warn.
+            Con::warnf( "Module Manager: Found module: '%s' but its module group '%s' is not the same as other module definitions of the same module Id.",
+                pModuleDefinition->getModuleFilePath(), moduleGroup );
+
+            // Destroy module definition and finish.
+            pModuleDefinition->deleteObject();
+            return false;
+        }
+
+        // Is the module type the same as the module definitions we already have?
+        if ( moduleType != pDefinitions->mModuleType )
+        {
+            // No, so warn.
+            Con::warnf( "Module Manager: Found module: '%s' but its module type '%s' is not the same as other module definitions of the same module Id.",
+                pModuleDefinition->getModuleFilePath(), moduleGroup );
+
+            // Destroy module definition and finish.
+            pModuleDefinition->deleteObject();
+            return false;
+        }
+    }
+    else
+    {
+        // No, so create a vector of definitions.
+        pDefinitions = new ModuleDefinitionEntry( moduleId, moduleGroup, moduleType );
+
+        // Insert module Id definitions.
+        mModuleIdDatabase.insert( moduleId, pDefinitions );
+    }
+    
+    // Add module definition.
+    pDefinitions->push_back( pModuleDefinition );
+
+    // Sort module definitions by version Id so that higher versions appear first.
+    dQsort( pDefinitions->address(), pDefinitions->size(), sizeof(ModuleDefinition*), moduleDefinitionVersionIdSort );
+
+    // Find module group.
+    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
+
+    // Did we find the module group?
+    if ( moduleGroupItr != mGroupModules.end() )
+    {
+        // Yes, so fetch module Ids.
+        typeModuleIdVector* pModuleIds = moduleGroupItr->value;
+
+        // Is the module Id already present?
+        bool moduleIdFound = false;
+        for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
+        {
+            // Skip if this isn't the Id.
+            if ( *moduleIdItr == moduleId )
+            {
+                moduleIdFound = true;
+                break;
+            }
+        }
+
+        // Add if module Id was not found.
+        if ( !moduleIdFound )
+            pModuleIds->push_back( moduleId );
+    }
+    else
+    {
+        // No, so insert a module Id vector.
+        moduleGroupItr = mGroupModules.insert( pModuleDefinition->getModuleGroup(), new typeModuleIdVector() );
+
+        // Add module Id.
+        moduleGroupItr->value->push_back( moduleId );
+    }
+
+    // Notify if the module definition is destroyed.
+    deleteNotify( pModuleDefinition );
+
+    // Info.
+    if ( mEchoInfo )
+    {
+#if 1
+        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s' ].",
+            pModuleDefinition->getModuleFilePath(),
+            pModuleDefinition->getModuleId(),
+            pModuleDefinition->getVersionId(),
+            pModuleDefinition->getBuildId(),
+            pModuleDefinition->getModuleDescription()
+            );
+#else
+        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s', Group='%s', Dependencies='%s', ScriptFile='%s', CreateFunction='%s', DestroyFunction='%s' ].",
+            pModuleDefinition->getModuleFilePath(),
+            pModuleDefinition->getModuleId(),
+            pModuleDefinition->getVersionId(),
+            pModuleDefinition->getBuildId(),
+            pModuleDefinition->getModuleDescription(),
+            pModuleDefinition->getModuleGroup(),
+            pModuleDefinition->getDataField( StringTable->insert("Dependencies"), NULL ),
+            pModuleDefinition->getScriptFile(),
+            pModuleDefinition->getCreateFunction(),
+            pModuleDefinition->getDestroyFunction()
+            );
+#endif
+    }
+
+    // Emit notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        Con::executef( *notifyItr, 2, "onModuleRegister", pModuleDefinition->getIdString() );
+    }
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePreLoadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePreLoad( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePreLoad" ) )
+            Con::executef( pListener, 2, "onModulePreLoad", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePostLoadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePostLoad( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePostLoad" ) )
+            Con::executef( pListener, 2, "onModulePostLoad", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePreUnloadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePreUnload( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePreUnload" ) )
+            Con::executef( pListener, 2, "onModulePreUnload", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void ModuleManager::raiseModulePostUnloadNotifications( ModuleDefinition* pModuleDefinition )
+{
+    // Raise notifications.
+    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
+    {
+        // Fetch listener object.
+        SimObject* pListener = *notifyItr;
+
+        // Perform object callback.
+        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
+        if ( pCallbacks != NULL )
+            pCallbacks->onModulePostUnload( pModuleDefinition );             
+            
+        // Perform script callback.
+        if ( pListener->isMethod( "onModulePostUnload" ) )
+            Con::executef( pListener, 2, "onModulePostUnload", pModuleDefinition->getIdString() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleDefinitionEntry* ModuleManager::findModuleId( StringTableEntry moduleId )
+{
+    // Sanity!
+    AssertFatal( moduleId != NULL, "A module Id cannot be NULL." );
+
+    // Is the module Id valid?
+    if ( moduleId == StringTable->EmptyString )
+    {
+        // No, so warn.
+        Con::warnf( "Module Manager: Invalid Module Id." );
+        return NULL;
+    }
+
+    // Find module Id.
+    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
+
+    // Return appropriately.
+    return moduleItr != mModuleIdDatabase.end() ? moduleItr->value : NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleDefinitionEntry::iterator ModuleManager::findModuleDefinition( StringTableEntry moduleId, const U32 versionId )
+{
+    // Fetch modules definitions.
+    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
+
+    // Finish if no module definitions for the module Id.
+    if ( pDefinitions == NULL )
+        return NULL;
+
+    // Iterate module definitions.
+    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
+    {
+        // Skip if this isn't the version Id we're searching for.
+        if ( versionId != (*definitionItr)->getVersionId() )
+            continue;
+
+        // Return module definition iterator.
+        return definitionItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ModuleManager::resolveModuleDependencies( StringTableEntry moduleId, const U32 versionId, StringTableEntry moduleGroup, bool synchronizedOnly, typeModuleLoadEntryVector& moduleResolvingQueue, typeModuleLoadEntryVector& moduleReadyQueue )
+{
+    // Fetch the module Id ready entry.
+    ModuleLoadEntry* pLoadReadyEntry = findModuleReady( moduleId, moduleReadyQueue );
+
+    // Is there a load entry?
+    if ( pLoadReadyEntry )
+    {
+        // Yes, so finish if the version Id is not important,
+        if ( versionId == 0 )
+            return true;
+
+        // Finish if the version Id are compatible.
+        if ( versionId == pLoadReadyEntry->mpModuleDefinition->getVersionId() )
+            return true;
+
+        // Is it a strict version Id?
+        if ( pLoadReadyEntry->mStrictVersionId )
+        {
+            // Yes, so warn.
+            Con::warnf( "Module Manager: A module dependency was detected loading module Id '%s' at version Id '%d' in group '%s' but an version Id '%d' is also required.",
+                moduleId, versionId, pLoadReadyEntry->mpModuleDefinition->getVersionId(), moduleGroup );
+            return false;
+        }
+
+        // No, so find the required module version Id.
+        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
+
+        // Did we find the requested module definition.
+        if ( definitionItr == NULL )
+        {
+            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
+            if ( !mEnforceDependencies )
+                return true;
+
+            // Warn!
+            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
+                moduleId, versionId, moduleGroup );
+            return false;
+        }
+
+        // Set the new module definition.
+        pLoadReadyEntry->mpModuleDefinition = *definitionItr;
+
+        // Set strict version Id.
+        pLoadReadyEntry->mStrictVersionId = true;
+                
+        return true;
+    }
+
+    // Is the module Id load resolving?
+    if ( findModuleResolving( moduleId, moduleResolvingQueue ) != NULL )
+    {
+        // Yes, so a cycle has been detected so warn.
+        Con::warnf( "Module Manager: A cyclic dependency was detected resolving module Id '%s' at version Id '%d' in group '%s'.",
+            moduleId, versionId, moduleGroup );
+        return false;
+    }
+
+    // Reset selected module definition.
+    ModuleDefinition* pSelectedModuleDefinition = NULL;
+
+    // Do we want the latest version Id?
+    if ( versionId == 0 )
+    {
+        // Yes, so find the module Id.
+        typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.find( moduleId );
+
+        // Did we find the module Id?
+        if ( moduleIdItr == mModuleIdDatabase.end() )
+        {
+            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
+            if ( !mEnforceDependencies )
+                return true;
+
+            // Warn!
+            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
+                moduleId, versionId, moduleGroup );
+            return false;
+        }
+
+        // Fetch first module definition which should be the highest version Id.
+        pSelectedModuleDefinition = (*moduleIdItr->value)[0];
+    }
+    else
+    {
+        // No, so find the module Id at the specific version Id.
+        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
+
+        // Did we find the module definition?
+        if ( definitionItr == NULL )
+        {
+            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
+            if ( !mEnforceDependencies )
+                return true;
+
+            // Warn!
+            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
+                moduleId, versionId, moduleGroup );
+            return false;
+        }
+
+        // Select the module definition.
+        pSelectedModuleDefinition = *definitionItr;
+    }
+
+    // If we're only resolving synchronized modules and the module is not synchronized then finish.
+    if ( synchronizedOnly && !pSelectedModuleDefinition->getSynchronized() )
+        return true;
+
+    // Create a load entry.
+    ModuleLoadEntry loadEntry( pSelectedModuleDefinition, false );
+
+    // Fetch module dependencies.
+    const ModuleDefinition::typeModuleDependencyVector& moduleDependencies = pSelectedModuleDefinition->getDependencies();
+
+    // Do we have any module dependencies?
+    if ( moduleDependencies.size() > 0 )
+    {
+        // Yes, so queue this module as resolving.
+        moduleResolvingQueue.push_back( loadEntry );
+
+        // Iterate module dependencies.
+        for( ModuleDefinition::typeModuleDependencyVector::const_iterator dependencyItr = moduleDependencies.begin(); dependencyItr != moduleDependencies.end(); ++dependencyItr )
+        {            
+            // Finish if we could not the dependent module Id at the version Id.
+            if ( !resolveModuleDependencies( dependencyItr->mModuleId, dependencyItr->mVersionId, moduleGroup, synchronizedOnly, moduleResolvingQueue, moduleReadyQueue ) )
+                return false;
+        }
+
+        // Remove module as resolving.
+        moduleResolvingQueue.pop_back();
+    }
+
+    // Queue module as ready.
+    moduleReadyQueue.push_back( loadEntry );        
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleLoadEntry* ModuleManager::findModuleResolving( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleResolvingQueue )
+{
+    // Iterate module load resolving queue.
+    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleResolvingQueue.begin(); loadEntryItr != moduleResolvingQueue.end(); ++loadEntryItr )
+    {
+        // Finish if found.
+        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
+            return loadEntryItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::ModuleLoadEntry* ModuleManager::findModuleReady( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleReadyQueue )
+{
+    // Iterate module load ready queue.
+    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleReadyQueue.begin(); loadEntryItr != moduleReadyQueue.end(); ++loadEntryItr )
+    {
+        // Finish if found.
+        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
+            return loadEntryItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::typeModuleLoadEntryVector::iterator ModuleManager::findModuleLoaded( StringTableEntry moduleId, const U32 versionId )
+{
+    // Iterate module loaded queue.
+    for( typeModuleLoadEntryVector::iterator loadEntryItr = mModulesLoaded.begin(); loadEntryItr != mModulesLoaded.end(); ++loadEntryItr )
+    {
+        // Skip if not the module Id we're looking for.
+        if ( moduleId != loadEntryItr->mpModuleDefinition->getModuleId() )
+            continue;
+
+        // Skip if we are searching for a specific version and it does not match.
+        if ( versionId != 0 && versionId != loadEntryItr->mpModuleDefinition->getVersionId() )
+            continue;
+
+        return loadEntryItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+ModuleManager::typeGroupVector::iterator ModuleManager::findGroupLoaded( StringTableEntry moduleGroup )
+{
+    // Iterate groups loaded queue.
+    for( typeGroupVector::iterator groupsLoadedItr = mGroupsLoaded.begin(); groupsLoadedItr != mGroupsLoaded.end(); ++groupsLoadedItr )
+    {
+        // Finish if found.
+        if ( moduleGroup == *groupsLoadedItr )
+            return groupsLoadedItr;
+    }
+
+    // Not found.
+    return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+StringTableEntry ModuleManager::getModuleMergeFilePath( void ) const
+{
+    // Format merge file path.
+    char filePathBuffer[1024];
+    dSprintf( filePathBuffer, sizeof(filePathBuffer), "%s/%s", Platform::getExecutablePath(), MODULE_MANAGER_MERGE_FILE );
+
+    return StringTable->insert( filePathBuffer );
+}

+ 59 - 62
engine/source/persistence/taml/taml.cc

@@ -379,7 +379,7 @@ Taml::TamlFormatMode Taml::getFileAutoFormatMode( const char* pFilename )
 
 //-----------------------------------------------------------------------------
 
-TamlWriteNode* Taml::compileObject( SimObject* pSimObject )
+TamlWriteNode* Taml::compileObject( SimObject* pSimObject, const bool forceId )
 {
     // Debug Profiling.
     PROFILE_SCOPE(Taml_CompileObject);
@@ -426,6 +426,13 @@ TamlWriteNode* Taml::compileObject( SimObject* pSimObject )
     TamlWriteNode* pNewNode = new TamlWriteNode();
     pNewNode->set( pSimObject );
 
+    // Is an Id being forced for this object?
+    if ( forceId )
+    {
+        // Yes, so allocate one.
+        pNewNode->mRefId = ++mMasterNodeId;
+    }
+
     // Push new node.
     mCompiledNodes.push_back( pNewNode );
 
@@ -446,8 +453,8 @@ TamlWriteNode* Taml::compileObject( SimObject* pSimObject )
     // Compile children.
     compileChildren( pNewNode );
 
-    // Compile custom properties.
-    compileCustomProperties( pNewNode );
+    // Compile custom state.
+    compileCustomState( pNewNode );
 
     // Are there any Taml callbacks?
     if ( pNewNode->mpTamlCallbacks != NULL )
@@ -661,93 +668,83 @@ void Taml::compileChildren( TamlWriteNode* pTamlWriteNode )
 
 //-----------------------------------------------------------------------------
 
-void Taml::compileCustomProperties( TamlWriteNode* pTamlWriteNode )
+void Taml::compileCustomState( TamlWriteNode* pTamlWriteNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(Taml_CompileCustomProperties);
 
     // Sanity!
-    AssertFatal( pTamlWriteNode != NULL, "Cannot compile custom properties on a NULL node." );
-    AssertFatal( pTamlWriteNode->mpSimObject != NULL, "Cannot compile custom properties on a node with no object." );
+    AssertFatal( pTamlWriteNode != NULL, "Cannot compile custom state on a NULL node." );
+    AssertFatal( pTamlWriteNode->mpSimObject != NULL, "Cannot compile custom state on a node with no object." );
 
-    // Fetch the custom properties on the write node.
-    TamlCustomProperties& customProperties = pTamlWriteNode->mCustomProperties;
+    // Fetch the custom node on the write node.
+    TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
 
     // Are there any Taml callbacks?
     if ( pTamlWriteNode->mpTamlCallbacks != NULL )
     {
         // Yes, so call it.
-        tamlCustomWrite( pTamlWriteNode->mpTamlCallbacks, customProperties );
+        tamlCustomWrite( pTamlWriteNode->mpTamlCallbacks, customNodes );
     }
 
-    // Finish if no custom properties to process.
-    if ( customProperties.size() == 0 )
-        return;
+    // Fetch custom nodes.
+    const TamlCustomNodeVector& nodes = customNodes.getNodes();
 
+    // Finish if no custom nodes to process.
+    if ( nodes.size() == 0 )
+        return;
+    
     // Iterate custom properties.
-    for( TamlCustomPropertyVector::iterator propertyItr = customProperties.begin(); propertyItr != customProperties.end(); ++propertyItr )
+    for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
     {
-        TamlCustomProperty* pCustomProperty = *propertyItr;
-
-        // Iterate alias.
-        for( TamlPropertyAliasVector::iterator typeAliasItr = pCustomProperty->begin(); typeAliasItr != pCustomProperty->end(); ++typeAliasItr )
-        {
-            TamlPropertyAlias* pAlias = *typeAliasItr;
+        // Fetch the custom node.
+        TamlCustomNode* pCustomNode = *customNodesItr;
 
-            // Iterate the fields.
-            for( TamlPropertyFieldVector::iterator fieldItr = pAlias->begin(); fieldItr != pAlias->end(); ++fieldItr )
-            {
-                TamlPropertyField* pPropertyField = *fieldItr;
+        // Compile custom node state.
+        compileCustomNodeState( pCustomNode );
+    }
+}
 
-                // Skip if not an object field.
-                if ( !pPropertyField->isObjectField() )
-                    continue;
+//-----------------------------------------------------------------------------
 
-                // Compile the object.
-                TamlWriteNode* pCompiledWriteNode = compileObject( pPropertyField->getFieldObject() );
+void Taml::compileCustomNodeState( TamlCustomNode* pCustomNode )
+{
+    // Sanity!
+    AssertFatal( pCustomNode != NULL, "Taml: Cannot compile NULL custom node state." );
 
-                // Set reference field.
-                pCompiledWriteNode->mRefField = pPropertyField->getFieldName();
+    // Fetch children.
+    const TamlCustomNodeVector& children = pCustomNode->getChildren();
 
-                // Set the write node for the property field.
-                pPropertyField->setWriteNode( pCompiledWriteNode );
-            }
-        }
-    }
+    // Fetch proxy object.
+    SimObject* pProxyObject = pCustomNode->getProxyObject<SimObject>(false);
 
-#if 0
-    // Iterate the custom properties removing ignored items.
-    for( S32 customPropertyIndex = 0; customPropertyIndex < customProperties.size(); ++customPropertyIndex )
+    // Do we have a proxy object?
+    if ( pProxyObject != NULL )
     {
-        // Fetch the custom property.
-        TamlCustomProperty* pCustomProperty = customProperties.at( customPropertyIndex );
-
-        // Skip if we are not ignoring the custom property if empty.
-        if ( !pCustomProperty->mIgnoreEmpty )
-            continue;
-
-        // Iterate the alias.
-        for ( S32 typeAliasIndex = 0; typeAliasIndex < pCustomProperty->size(); ++typeAliasIndex )
-        {
-            // Fetch the alias.
-            TamlPropertyAlias* pAlias = pCustomProperty->at( typeAliasIndex );
+        // Yes, so sanity!
+        AssertFatal( children.size() == 0, "Taml: Cannot compile a proxy object on a custom node that has children." );
 
-            // Skip If we're not ignoring the alias or the custom property is not empty.
-            if ( !pAlias->mIgnoreEmpty && pAlias->size() != 0 )
-                continue;
+        // Yes, so compile it.
+        // NOTE: We force an Id for custom compiled objects so we guarantee an Id.  The reason for this is fairly
+        // weak in that the XML parser currently has no way of distinguishing between a compiled object node
+        // and a custom node.  If the node has an Id or an Id-Ref then it's obviously an object and should be parsed as such.
+        pCustomNode->setWriteNode( compileObject( pProxyObject, true ) );
+        return;
+    }
 
-            // Remove the alias.
-            pCustomProperty->removeAlias( typeAliasIndex-- );
-        }
+    // Finish if no children.
+    if ( children.size() == 0 )
+        return;
 
-        // Skip if the custom property is not empty.
-        if ( pCustomProperty->size() != 0 )
-            continue;
+    // Iterate children.
+    for( TamlCustomNodeVector::const_iterator childItr = children.begin(); childItr != children.end(); ++childItr )
+    {
+        // Fetch shape node.
+        TamlCustomNode* pChildNode = *childItr;
 
-        // Remove the custom property.
-        customProperties.removeProperty( customPropertyIndex-- );
+        // Compile the child.
+        compileCustomNodeState( pChildNode );
     }
-#endif
 }
 
 //-----------------------------------------------------------------------------

+ 6 - 5
engine/source/persistence/taml/taml.h

@@ -103,11 +103,12 @@ private:
 private:
     void resetCompilation( void );
 
-    TamlWriteNode* compileObject( SimObject* pSimObject );
+    TamlWriteNode* compileObject( SimObject* pSimObject, const bool forceId = false );
     void compileStaticFields( TamlWriteNode* pTamlWriteNode );
     void compileDynamicFields( TamlWriteNode* pTamlWriteNode );
     void compileChildren( TamlWriteNode* pTamlWriteNode );
-    void compileCustomProperties( TamlWriteNode* pTamlWriteNode );
+    void compileCustomState( TamlWriteNode* pTamlWriteNode );
+    void compileCustomNodeState( TamlCustomNode* pCustomNode );
 
     bool write( FileStream& stream, SimObject* pSimObject, const TamlFormatMode formatMode );
     SimObject* read( FileStream& stream, const TamlFormatMode formatMode );
@@ -129,10 +130,10 @@ private:
     inline void tamlPreWrite( TamlCallbacks* pCallbacks )                                           { pCallbacks->onTamlPreWrite(); }
     inline void tamlPostWrite( TamlCallbacks* pCallbacks )                                          { pCallbacks->onTamlPostWrite(); }
     inline void tamlPreRead( TamlCallbacks* pCallbacks )                                            { pCallbacks->onTamlPreRead(); }
-    inline void tamlPostRead( TamlCallbacks* pCallbacks, const TamlCustomProperties& customProperties )   { pCallbacks->onTamlPostRead( customProperties ); }
+    inline void tamlPostRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes )       { pCallbacks->onTamlPostRead( customNodes ); }
     inline void tamlAddParent( TamlCallbacks* pCallbacks, SimObject* pParentObject )                { pCallbacks->onTamlAddParent( pParentObject ); }
-    inline void tamlCustomWrite( TamlCallbacks* pCallbacks, TamlCustomProperties& customProperties )      { pCallbacks->onTamlCustomWrite( customProperties ); }
-    inline void tamlCustomRead( TamlCallbacks* pCallbacks, const TamlCustomProperties& customProperties ) { pCallbacks->onTamlCustomRead( customProperties ); }
+    inline void tamlCustomWrite( TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes )          { pCallbacks->onTamlCustomWrite( customNodes ); }
+    inline void tamlCustomRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes )     { pCallbacks->onTamlCustomRead( customNodes ); }
 
 public:
     Taml();

+ 110 - 97
engine/source/persistence/taml/tamlBinaryReader.cc

@@ -191,7 +191,7 @@ SimObject* TamlBinaryReader::parseElement( Stream& stream, const U32 versionId )
     }
 
     // Parse custom elements.
-    TamlCustomProperties customProperties;
+    TamlCustomNodes customProperties;
 
     // Parse children.
     parseChildren( stream, pCallbacks, pSimObject, versionId );
@@ -212,102 +212,6 @@ SimObject* TamlBinaryReader::parseElement( Stream& stream, const U32 versionId )
 
 //-----------------------------------------------------------------------------
 
-void TamlBinaryReader::parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomProperties& customProperties, const U32 versionId )
-{
-    // Debug Profiling.
-    PROFILE_SCOPE(TamlBinaryReader_ParseCustomElement);
-
-    // Read custom element count.
-    U32 customPropertyCount;
-    stream.read( &customPropertyCount );
-
-    // Finish if no custom properties.
-    if ( customPropertyCount == 0 )
-        return;
-
-    // Iterate custom properties.
-    for ( U32 propertyIndex = 0; propertyIndex < customPropertyCount; ++propertyIndex )
-    {
-        // Read custom element name.
-        StringTableEntry propertyName = stream.readSTString();
-
-        // Add custom property.
-        TamlCustomProperty* pCustomProperty = customProperties.addProperty( propertyName );
-
-        // Read property alias count.
-        U32 propertyAliasCount;
-        stream.read( &propertyAliasCount );
-
-        // Skip if no property alias.
-        if ( propertyAliasCount == 0 )
-            continue;
-
-        // Iterate property alias.
-        for( U32 propertyAliasIndex = 0; propertyAliasIndex < propertyAliasCount; ++propertyAliasIndex )
-        {
-            // Read property alias name.
-            StringTableEntry propertyAliasName = stream.readSTString();
-
-            // Add property alias.
-            TamlPropertyAlias* pPropertyAlias = pCustomProperty->addAlias( propertyAliasName );
-
-            // Read property field count.
-            U32 propertyFieldCount;
-            stream.read( &propertyFieldCount );
-
-            // Skip if no property fields.
-            if ( propertyFieldCount == 0 )
-                continue;
-
-            // Iterate property fields.
-            for( U32 propertyFieldIndex = 0; propertyFieldIndex < propertyFieldCount; ++propertyFieldIndex )
-            {
-                // Read is object field flag.
-                bool isObjectField;
-                stream.read( &isObjectField );
-
-                // Is it an object field?
-                if ( isObjectField )
-                {
-                    // Yes, so read reference field.
-                    StringTableEntry fieldName = stream.readSTString();
-
-                    // Read field object.
-                    SimObject* pFieldObject = parseElement( stream, versionId );
-
-                    // Add property field.
-                    pPropertyAlias->addField( fieldName, pFieldObject );
-                }
-                else
-                {
-                    // No, so read field name.
-                    StringTableEntry propertyFieldName = stream.readSTString();
-
-                    // Read field value.
-                    char valueBuffer[MAX_TAML_PROPERTY_FIELDVALUE_LENGTH];
-                    stream.readLongString( MAX_TAML_PROPERTY_FIELDVALUE_LENGTH, valueBuffer );
-
-                    // Add property field.
-                    pPropertyAlias->addField( propertyFieldName, valueBuffer );
-                }
-            }
-        }
-    }
-
-    // Do we have callbacks?
-    if ( pCallbacks == NULL )
-    {
-        // No, so warn.
-        Con::warnf( "Taml: Encountered custom data but object does not support custom data." );
-        return;
-    }
-
-    // Custom read callback.
-    mpTaml->tamlCustomRead( pCallbacks, customProperties );
-}
-
-//-----------------------------------------------------------------------------
-
 void TamlBinaryReader::parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId )
 {
     // Debug Profiling.
@@ -391,3 +295,112 @@ void TamlBinaryReader::parseChildren( Stream& stream, TamlCallbacks* pCallbacks,
         }
     }
 }
+
+//-----------------------------------------------------------------------------
+
+void TamlBinaryReader::parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(TamlBinaryReader_ParseCustomElement);
+
+    // Read custom node count.
+    U32 customNodeCount;
+    stream.read( &customNodeCount );
+
+    // Finish if no custom nodes.
+    if ( customNodeCount == 0 )
+        return;
+
+    // Iterate custom nodes.
+    for ( U32 nodeIndex = 0; nodeIndex < customNodeCount; ++nodeIndex )
+    {
+        //Read custom node name.
+        StringTableEntry nodeName = stream.readSTString();
+
+        // Add custom node.
+        TamlCustomNode* pCustomNode = customNodes.addNode( nodeName );
+
+        // Parse the custom node.
+        parseCustomNode( stream, pCustomNode, versionId );
+    }
+
+    // Do we have callbacks?
+    if ( pCallbacks == NULL )
+    {
+        // No, so warn.
+        Con::warnf( "Taml: Encountered custom data but object does not support custom data." );
+        return;
+    }
+
+    // Custom read callback.
+    mpTaml->tamlCustomRead( pCallbacks, customNodes );
+}
+
+//-----------------------------------------------------------------------------
+
+void TamlBinaryReader::parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId )
+{
+    // Fetch if a proxy object.
+    bool isProxyObject;
+    stream.read( &isProxyObject );
+
+    // Is this a proxy object?
+    if ( isProxyObject )
+    {
+        // Yes, so parse proxy object.
+        SimObject* pProxyObject = parseElement( stream, versionId );
+
+        // Add child node.
+        pCustomNode->addNode( pProxyObject );
+
+        return;
+    }
+
+    // No, so read custom node name.
+    StringTableEntry nodeName = stream.readSTString();
+
+    // Add child node.
+    TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
+
+    // Read child node text.
+    char childNodeTextBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
+    stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, childNodeTextBuffer );
+    pChildNode->setNodeText( childNodeTextBuffer );
+
+    // Read child node count.
+    U32 childNodeCount;
+    stream.read( &childNodeCount );
+
+    // Do we have any children nodes?
+    if ( childNodeCount > 0 )
+    {
+        // Yes, so parse children nodes.
+        for( U32 childIndex = 0; childIndex < childNodeCount; ++childIndex )
+        {
+            // Parse child node.
+            parseCustomNode( stream, pChildNode, versionId );
+        }
+    }
+
+    // Read child field count.
+    U32 childFieldCount;
+    stream.read( &childFieldCount );
+
+    // Do we have any child fields?
+    if ( childFieldCount > 0 )
+    {
+        // Yes, so parse child fields.
+        for( U32 childFieldIndex = 0; childFieldIndex < childFieldCount; ++childFieldIndex )
+        {
+            // Read field name.
+            StringTableEntry fieldName = stream.readSTString();
+
+            // Read field value.
+            char valueBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
+            stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, valueBuffer );
+
+            // Add field.
+            pChildNode->addField( fieldName, valueBuffer );
+        }
+    }
+}

+ 2 - 1
engine/source/persistence/taml/tamlBinaryReader.h

@@ -58,9 +58,10 @@ private:
     void resetParse( void );
 
     SimObject* parseElement( Stream& stream, const U32 versionId );
-    void parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomProperties& customProperties, const U32 versionId );
     void parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId );
     void parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId );
+    void parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId );
+    void parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId );
 };
 
 #endif // _TAML_BINARYREADER_H_

+ 116 - 83
engine/source/persistence/taml/tamlBinaryWriter.cc

@@ -126,89 +126,6 @@ void TamlBinaryWriter::writeElement( Stream& stream, const TamlWriteNode* pTamlW
 
 //-----------------------------------------------------------------------------
 
-void TamlBinaryWriter::writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode )
-{
-    // Debug Profiling.
-    PROFILE_SCOPE(TamlBinaryWriter_WriteCustomElements);
-
-    // Fetch custom properties.
-    const TamlCustomProperties& customProperties = pTamlWriteNode->mCustomProperties;
-
-    // Write custom element count.
-    stream.write( (U32)customProperties.size() );
-
-    // Iterate custom properties.
-    for( TamlCustomProperties::const_iterator customPropertyItr = customProperties.begin(); customPropertyItr != customProperties.end(); ++customPropertyItr )
-    {
-        // Fetch custom property.
-        TamlCustomProperty* pCustomProperty = *customPropertyItr;
-
-        // Write custom element name.
-        stream.writeString( pCustomProperty->mPropertyName );
-
-        // Fetch property alias count.
-        U32 propertyAliasCount = (U32)pCustomProperty->size();
-
-        // Write property count.
-        stream.write( propertyAliasCount );
-
-        // Skip if no property alias.
-        if (propertyAliasCount == 0 )
-            continue;
-
-        // Iterate property alias.
-        for( TamlCustomProperty::const_iterator propertyAliasItr = pCustomProperty->begin(); propertyAliasItr != pCustomProperty->end(); ++propertyAliasItr )
-        {
-            // Fetch property alias.
-            TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
-
-            // Write property alias name.
-            stream.writeString( pPropertyAlias->mAliasName );
-
-            // Write property field count.
-            stream.write( (U32)pPropertyAlias->size() );
-
-            // Skip if no property fields.
-            if ( pPropertyAlias->size() == 0 )
-                continue;
-
-            // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
-            {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
-
-                // Fetch object field flag,
-                const bool isObjectField = pPropertyField->isObjectField();
-
-                // Write flag.
-                stream.write( isObjectField );
-
-                // Is it an object field?
-                if ( isObjectField )
-                {
-                    // Yes, so fetch write node.
-                    const TamlWriteNode* pObjectWriteField = pPropertyField->getWriteNode();
-
-                    // Write reference field.
-                    stream.writeString( pObjectWriteField->mRefField );
-
-                    // Write field object.
-                    writeElement( stream, pObjectWriteField );
-                }
-                else
-                {
-                    // No, so write property attribute.
-                    stream.writeString( pPropertyField->getFieldName() );
-                    stream.writeLongString( MAX_TAML_PROPERTY_FIELDVALUE_LENGTH, pPropertyField->getFieldValue() );
-                }
-            }
-        }
-    }
-}
-
-//-----------------------------------------------------------------------------
-
 void TamlBinaryWriter::writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode )
 {
     // Debug Profiling.
@@ -261,4 +178,120 @@ void TamlBinaryWriter::writeChildren( Stream& stream, const TamlWriteNode* pTaml
         // Write child.
         writeElement( stream, (*itr) );
     }
+}
+
+//-----------------------------------------------------------------------------
+
+void TamlBinaryWriter::writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(TamlBinaryWriter_WriteCustomElements);
+
+    // Fetch custom nodes.
+    const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
+
+    // Fetch custom nodes.
+    const TamlCustomNodeVector& nodes = customNodes.getNodes();
+
+    // Write custom node count.
+    stream.write( (U32)nodes.size() );
+
+    // Finish if there are no nodes.
+    if ( nodes.size() == 0 )
+        return;
+
+    // Iterate custom nodes.
+    for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
+    {
+        // Fetch the custom node.
+        TamlCustomNode* pCustomNode = *customNodesItr;
+
+        // Write custom node name.
+        stream.writeString( pCustomNode->getNodeName() );
+
+        // Fetch node children.
+        const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
+
+        // Iterate children nodes.
+        for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
+        {
+            // Fetch child node.
+            const TamlCustomNode* pChildNode = *childNodeItr;
+
+            // Write the custom node.
+            writeCustomNode( stream, pChildNode );
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void TamlBinaryWriter::writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode )
+{
+    // Is the node a proxy object?
+    if ( pCustomNode->isProxyObject() )
+    {
+        // Yes, so flag as proxy object.
+        stream.write( true );
+
+        // Write the element.
+        writeElement( stream, pCustomNode->getProxyWriteNode() );
+        return;
+    }
+
+    // No, so flag as custom node.
+    stream.write( false );
+
+    // Write custom node name.
+    stream.writeString( pCustomNode->getNodeName() );
+
+    // Write custom node text.
+    stream.writeString( pCustomNode->getNodeTextField().getFieldValue(), MAX_TAML_NODE_FIELDVALUE_LENGTH );
+
+    // Fetch node children.
+    const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
+
+    // Fetch child node count.
+    const U32 childNodeCount = (U32)nodeChildren.size();
+
+    // Write custom node count.
+    stream.write( childNodeCount );
+
+    // Do we have any children nodes.
+    if ( childNodeCount > 0 )
+    {
+        // Yes, so iterate children nodes.
+        for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
+        {
+            // Fetch child node.
+            const TamlCustomNode* pChildNode = *childNodeItr;
+
+            // Write the custom node.
+            writeCustomNode( stream, pChildNode );
+        }
+    }
+
+    // Fetch fields.
+    const TamlCustomFieldVector& fields = pCustomNode->getFields();
+
+    // Fetch child field count.
+    const U32 childFieldCount = (U32)fields.size();
+
+    // Write custom field count.
+    stream.write( childFieldCount );
+
+    // Do we have any child fields?
+    if ( childFieldCount > 0 )
+    {
+        // Yes, so iterate  fields.
+        for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
+        {
+            // Fetch node field.
+            const TamlCustomField* pField = *fieldItr;
+
+            // Write the node field.
+            stream.writeString( pField->getFieldName() );
+            stream.writeLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, pField->getFieldValue() );
+        }
+    }
 }

+ 3 - 2
engine/source/persistence/taml/tamlBinaryWriter.h

@@ -34,7 +34,7 @@ class TamlBinaryWriter
 public:
     TamlBinaryWriter( Taml* pTaml ) :
         mpTaml( pTaml ),
-        mVersionId(1)
+        mVersionId(2)
     {
     }
     virtual ~TamlBinaryWriter() {}
@@ -48,9 +48,10 @@ private:
 
 private:
     void writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode );
-    void writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode );
     void writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode );
     void writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode );
+    void writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode );
+    void writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode );
 };
 
 #endif // _TAML_BINARYWRITER_H_

+ 4 - 4
engine/source/persistence/taml/tamlCallbacks.h

@@ -25,7 +25,7 @@
 
 //-----------------------------------------------------------------------------
 
-class TamlCustomProperties;
+class TamlCustomNodes;
 class SimObject;
 
 //-----------------------------------------------------------------------------
@@ -46,16 +46,16 @@ private:
 
     /// Called after Taml has finished reading the object.
     /// The custom properties is additionally passed here for object who want to process it at the end of reading.
-    virtual void onTamlPostRead( const TamlCustomProperties& customProperties ) = 0;
+    virtual void onTamlPostRead( const TamlCustomNodes& customNodes ) = 0;
 
     /// Called after Taml has finished reading the object and has added the object to any parent.
     virtual void onTamlAddParent( SimObject* pParentObject ) = 0;
 
     /// Called during the writing of the object to allow custom properties to be written.
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties ) = 0;
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes ) = 0;
 
     /// Called during the reading of the object to allow custom properties to be read.
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties ) = 0;
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes ) = 0;
 };
 
 #endif // _TAML_CALLBACKS_H_

+ 9 - 56
engine/source/persistence/taml/tamlCustom.cc

@@ -28,19 +28,12 @@
 
 //-----------------------------------------------------------------------------
 
-void TamlPropertyField::resetState( void )
-{
-    mFieldName = StringTable->EmptyString;
-    *mFieldValue = 0;
-
-    // We don't need to delete the write node as it'll get destroyed when the compilation is reset!
-    mpFieldWriteNode = NULL;
-    mpFieldObject = NULL;
-}
+FactoryCache<TamlCustomField> TamlCustomFieldFactory;
+FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
 
 //-----------------------------------------------------------------------------
 
-void TamlPropertyField::set( const char* pFieldName, const char* pFieldValue )
+void TamlCustomField::set( const char* pFieldName, const char* pFieldValue )
 {
     // Sanity!
     AssertFatal( pFieldName != NULL, "Field name cannot be NULL." );
@@ -61,59 +54,19 @@ void TamlPropertyField::set( const char* pFieldName, const char* pFieldValue )
 #endif
     // Copy field value.
     dStrcpy( mFieldValue, pFieldValue );
-
-    // Reset field object.
-    mpFieldObject = NULL;
-    SAFE_DELETE( mpFieldWriteNode );
 }
 
 //-----------------------------------------------------------------------------
 
-void TamlPropertyField::set( const char* pFieldName, SimObject* pFieldObject )
+void TamlCustomNode::setWriteNode( TamlWriteNode* pWriteNode )
 {
     // Sanity!
-    AssertFatal( pFieldName != NULL, "Field name cannot be NULL." );
-    AssertFatal( pFieldObject != NULL, "Field object cannot be NULL." );
-    AssertFatal( mpFieldWriteNode == NULL, "Field write node must be NULL." );
-
-    // Set field name.
-    mFieldName = StringTable->insert( pFieldName );
-
-    mpFieldObject = pFieldObject;
-    SAFE_DELETE( mpFieldWriteNode );
-
-    // Reset field value.
-    *mFieldValue = 0;
-}
-
-//-----------------------------------------------------------------------------
-
-void TamlPropertyField::setWriteNode( TamlWriteNode* pWriteNode )
-{
-    // Sanity!
-    AssertFatal( mFieldName != StringTable->EmptyString, "Cannot set write node with an empty field name." );
+    AssertFatal( mNodeName != StringTable->EmptyString, "Cannot set write node with an empty node name." );
     AssertFatal( pWriteNode != NULL, "Write node cannot be NULL." );
-    AssertFatal( pWriteNode->mpSimObject == mpFieldObject, "Write node does not match existing field object." );
-    AssertFatal( mpFieldWriteNode == NULL, "Field write node must be NULL." );
-
-    // Set field object.
-    mpFieldWriteNode = pWriteNode;
-
-    // Reset field value.
-    *mFieldValue = 0;
-}
-
-//-----------------------------------------------------------------------------
+    AssertFatal( pWriteNode->mpSimObject == mpProxyObject, "Write node does not match existing proxy object." );
+    AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
 
-SimObject* TamlPropertyField::getFieldObject( void ) const
-{
-    return mpFieldObject != NULL ? mpFieldObject : NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-bool TamlPropertyField::isObjectField( void ) const
-{
-    return mpFieldObject != NULL;
+    // Set proxy write node.
+    mpProxyWriteNode = pWriteNode;
 }
 

+ 352 - 254
engine/source/persistence/taml/tamlCustom.h

@@ -51,28 +51,29 @@
 
 //-----------------------------------------------------------------------------
 
-#define MAX_TAML_PROPERTY_FIELDVALUE_LENGTH 2048
+#define MAX_TAML_NODE_FIELDVALUE_LENGTH 2048
 
 //-----------------------------------------------------------------------------
 
 class TamlWriteNode;
+class TamlCustomNode;
+class TamlCustomField;
+extern FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
+extern FactoryCache<TamlCustomField> TamlCustomFieldFactory;
+typedef Vector<TamlCustomNode*> TamlCustomNodeVector;
+typedef Vector<TamlCustomField*> TamlCustomFieldVector;
 
 //-----------------------------------------------------------------------------
 
-class TamlPropertyField : public IFactoryObjectReset
+class TamlCustomField : public IFactoryObjectReset
 {
 public:
-    TamlPropertyField()
+    TamlCustomField()
     {
-        // Reset field object.
-        // NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
-        mpFieldWriteNode = NULL;
-        mpFieldObject = NULL;
-
         resetState();
     }
 
-    virtual ~TamlPropertyField()
+    virtual ~TamlCustomField()
     {
         // Everything should already be cleared in a state reset.
         // Touching any memory here is dangerous as this type is typically
@@ -80,13 +81,99 @@ public:
         // pretty much anything or everything could be invalid!
     }
 
-    virtual void resetState( void );
+    virtual void resetState( void )
+    {
+        mFieldName = StringTable->EmptyString;
+        *mFieldValue = 0;
+    }
 
     void set( const char* pFieldName, const char* pFieldValue );
 
-    void set( const char* pFieldName, SimObject* pFieldObject );
+    inline void setFieldValue( const char* pFieldName, const ColorI& fieldValue )
+    {
+        // Fetch the field value.
+        const char* pFieldValue = Con::getData( TypeColorI, &const_cast<ColorI&>(fieldValue), 0 );
 
-    void setWriteNode( TamlWriteNode* pWriteNode );
+        // Did we get a field value?
+        if ( pFieldValue == NULL )
+        {
+            // No, so warn.
+            Con::warnf( "Taml: Failed to add node field name '%s' with ColorI value.", pFieldName );
+            pFieldValue = StringTable->EmptyString;
+        }
+
+        set( pFieldName, pFieldValue );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const ColorF& fieldValue )
+    {
+        // Fetch the field value.
+        const char* pFieldValue = Con::getData( TypeColorF, &const_cast<ColorF&>(fieldValue), 0 );
+
+        // Did we get a field value?
+        if ( pFieldValue == NULL )
+        {
+            // No, so warn.
+            Con::warnf( "Taml: Failed to add node field name '%s' with ColorF value.", pFieldName );
+            pFieldValue = StringTable->EmptyString;
+        }
+
+        set( pFieldName, pFieldValue );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const Point2I& fieldValue )
+    {
+        char fieldValueBuffer[32];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d", fieldValue.x, fieldValue.y );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const Point2F& fieldValue )
+    {
+        char fieldValueBuffer[32];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g", fieldValue.x, fieldValue.y );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const b2Vec2& fieldValue )
+    {
+        char fieldValueBuffer[32];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %.5g", fieldValue.x, fieldValue.y );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const U32 fieldValue )
+    {
+        char fieldValueBuffer[16];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const bool fieldValue )
+    {
+        char fieldValueBuffer[16];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const S32 fieldValue )
+    {
+        char fieldValueBuffer[16];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const float fieldValue )
+    {
+        char fieldValueBuffer[16];
+        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g", fieldValue );
+        set( pFieldName, fieldValueBuffer );
+    }
+
+    inline void setFieldValue( const char* pFieldName, const char* fieldValue )
+    {
+        set( pFieldName, fieldValue );
+    }
 
     inline void getFieldValue( ColorF& fieldValue ) const
     {
@@ -111,7 +198,7 @@ public:
         if ( dSscanf( mFieldValue, "%d %d", &fieldValue.x, &fieldValue.y ) != 2 )
         {
             // Warn.
-            Con::warnf( "TamlPropertyField - Reading point2I but it has an incorrect format: '%s'.", mFieldValue );
+            Con::warnf( "TamlCustomField - Reading point2I but it has an incorrect format: '%s'.", mFieldValue );
         }
     }
 
@@ -120,7 +207,7 @@ public:
         if ( dSscanf( mFieldValue, "%g %g", &fieldValue.x, &fieldValue.y ) != 2 )
         {
             // Warn.
-            Con::warnf( "TamlPropertyField - Reading point2F but it has an incorrect format: '%s'.", mFieldValue );
+            Con::warnf( "TamlCustomField - Reading point2F but it has an incorrect format: '%s'.", mFieldValue );
         }
     }
 
@@ -129,7 +216,7 @@ public:
         if ( dSscanf( mFieldValue, "%g %g", &fieldValue.x, &fieldValue.y ) != 2 )
         {
             // Warn.
-            Con::warnf( "TamlPropertyField - Reading vector but it has an incorrect format: '%s'.", mFieldValue );
+            Con::warnf( "TamlCustomField - Reading vector but it has an incorrect format: '%s'.", mFieldValue );
         }
     }
 
@@ -158,15 +245,9 @@ public:
         return mFieldValue;
     }
 
-    SimObject* getFieldObject( void ) const;
-
-    inline const TamlWriteNode* getWriteNode( void ) const { return mpFieldWriteNode; }
-
-    bool isObjectField( void ) const;
-
     inline StringTableEntry getFieldName( void ) const { return mFieldName; }
 
-    bool fieldNameBeginsWith( const char* pComparison )
+    bool fieldNameBeginsWith( const char* pComparison ) const
     {
         const U32 comparisonLength = dStrlen( pComparison );
         const U32 fieldNameLength = dStrlen( mFieldName );
@@ -179,7 +260,7 @@ public:
         char fieldNameBuffer[1024];
 
         // Sanity!
-        AssertFatal( fieldNameLength < sizeof(fieldNameBuffer), "TamlPropertyField: Field name is too long." );
+        AssertFatal( fieldNameLength < sizeof(fieldNameBuffer), "TamlCustomField: Field name is too long." );
 
         dStrcpy( fieldNameBuffer, mFieldName );
         fieldNameBuffer[fieldNameLength-1] = 0;
@@ -188,30 +269,29 @@ public:
         return ( fieldName == comparison );
     }
 
+    inline bool isValueEmpty( void ) const { return *mFieldValue == 0; }
+
 private:
     StringTableEntry    mFieldName;
-    char                mFieldValue[MAX_TAML_PROPERTY_FIELDVALUE_LENGTH];
-    SimObject*          mpFieldObject;
-    TamlWriteNode*      mpFieldWriteNode;
+    char                mFieldValue[MAX_TAML_NODE_FIELDVALUE_LENGTH];
 };
 
-static FactoryCache<TamlPropertyField> TamlPropertyFieldFactory;
-
 //-----------------------------------------------------------------------------
 
-typedef Vector<TamlPropertyField*> TamlPropertyFieldVector;
-
-class TamlPropertyAlias :
-    public TamlPropertyFieldVector,
-    public IFactoryObjectReset
+class TamlCustomNode : public IFactoryObjectReset
 {
 public:
-    TamlPropertyAlias()
+    TamlCustomNode()
     {
+        // Reset proxy object.
+        // NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
+        mpProxyWriteNode = NULL;
+        mpProxyObject = NULL;
+
         resetState();
     }
 
-    virtual ~TamlPropertyAlias()
+    virtual ~TamlCustomNode()
     {
         // Everything should already be cleared in a state reset.
         // Touching any memory here is dangerous as this type is typically
@@ -221,180 +301,174 @@ public:
 
     virtual void resetState( void )
     {
-        while( size() > 0 )
+        // We don't need to delete the write node as it'll get destroyed when the compilation is reset!
+        mpProxyWriteNode = NULL;
+        mpProxyObject = NULL;
+
+        // Cache the children.
+        while ( mChildren.size() > 0 )
+        {
+            TamlCustomNodeFactory.cacheObject( mChildren.back() );
+            mChildren.pop_back();
+        }
+
+        // Cache the fields.
+        while( mFields.size() > 0 )
         {
-            TamlPropertyFieldFactory.cacheObject( back() );
-            pop_back();
+            TamlCustomFieldFactory.cacheObject( mFields.back() );
+            mFields.pop_back();
         }
 
-        mAliasName = StringTable->EmptyString;
+        // Reset the node name.
+        mNodeName = StringTable->EmptyString;
+
+        // Reset node text.
+        mNodeText.resetState();
+
+        // Reset the ignore empty flag.
         mIgnoreEmpty = false;
     }
 
-    void set( const char* pAliasName )
+    inline TamlCustomNode* addNode( SimObject* pProxyObject )
     {
         // Sanity!
-        AssertFatal( pAliasName != NULL, "Type alias cannot be NULL." );
+        AssertFatal( pProxyObject != NULL, "Field object cannot be NULL." );
+        AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
+
+        // Create a custom node.
+        TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
 
-        mAliasName = StringTable->insert( pAliasName );
+        // Set node name.
+        pCustomNode->setNodeName( pProxyObject->getClassName() );
+
+        // Set proxy object.
+        pCustomNode->mpProxyObject = pProxyObject;
+
+        // Store node.
+        mChildren.push_back( pCustomNode );
+
+        return pCustomNode;
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const ColorI& fieldValue )
+    inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
     {
-        // Fetch the field value.
-        const char* pFieldValue = Con::getData( TypeColorI, &const_cast<ColorI&>(fieldValue), 0 );
+        // Create a custom node.
+        TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
 
-        // Did we get a field value?
-        if ( pFieldValue == NULL )
-        {
-            // No, so warn.
-            Con::warnf( "Taml: Failed to add property field name '%s' with ColorI value.", pFieldName );
-            pFieldValue = StringTable->EmptyString;
-        }
+        // Fetch node name.
+        pCustomNode->setNodeName( pNodeName );
+
+        // Set ignore-empty flag.
+        pCustomNode->setIgnoreEmpty( ignoreEmpty );
+
+        // Store node.
+        mChildren.push_back( pCustomNode );
 
-        return addField( pFieldName, pFieldValue );
+        return pCustomNode;
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const ColorF& fieldValue )
+    inline void removeNode( const U32 index )
     {
-        // Fetch the field value.
-        const char* pFieldValue = Con::getData( TypeColorF, &const_cast<ColorF&>(fieldValue), 0 );
+        // Sanity!
+        AssertFatal( index < (U32)mChildren.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
 
-        // Did we get a field value?
-        if ( pFieldValue == NULL )
+        // Cache the custom node.
+        TamlCustomNodeFactory.cacheObject( mChildren[index] );
+
+        // Remove it.
+        mChildren.erase( index );
+    }
+
+    inline const TamlCustomNode* findNode( const char* pNodeName ) const
+    {
+        // Sanity!
+        AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
+
+        // Fetch node name.
+        StringTableEntry nodeName = StringTable->insert( pNodeName );
+
+        // Find node.
+        for( Vector<TamlCustomNode*>::const_iterator nodeItr = mChildren.begin(); nodeItr != mChildren.end(); ++nodeItr )
         {
-            // No, so warn.
-            Con::warnf( "Taml: Failed to add property field name '%s' with ColorF value.", pFieldName );
-            pFieldValue = StringTable->EmptyString;
+            if ( (*nodeItr)->getNodeName() == nodeName )
+                return (*nodeItr);
         }
 
-        return addField( pFieldName, pFieldValue );
+        return NULL;
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const Point2I& fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const ColorI& fieldValue )
     {
-        char fieldValueBuffer[32];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d", fieldValue.x, fieldValue.y );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const Point2F& fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const ColorF& fieldValue )
     {
-        char fieldValueBuffer[32];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g", fieldValue.x, fieldValue.y );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const b2Vec2& fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const Point2I& fieldValue )
     {
-        char fieldValueBuffer[32];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %.5g", fieldValue.x, fieldValue.y );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
+
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const U32 fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const Point2F& fieldValue )
     {
-        char fieldValueBuffer[16];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const bool fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const b2Vec2& fieldValue )
     {
-        char fieldValueBuffer[16];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const S32 fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const U32 fieldValue )
     {
-        char fieldValueBuffer[16];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const float fieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const bool fieldValue )
     {
-        char fieldValueBuffer[16];
-        dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g", fieldValue );
-        return addField( pFieldName, fieldValueBuffer );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, const char* pFieldValue )
+    inline TamlCustomField* addField( const char* pFieldName, const S32 fieldValue )
     {
-        // Create a property field.
-        TamlPropertyField* pPropertyField = TamlPropertyFieldFactory.createObject();
-
-        // Set property field.
-        pPropertyField->set( pFieldName, pFieldValue );
-
-#if TORQUE_DEBUG
-        // Ensure a field name conflict does not exist.
-        for( Vector<TamlPropertyField*>::iterator propertyFieldItr = begin(); propertyFieldItr != end(); ++propertyFieldItr )
-        {
-            // Skip if field name is not the same.
-            if ( pPropertyField->getFieldName() != (*propertyFieldItr)->getFieldName() )
-                continue;
-
-            // Warn!
-            Con::warnf("Conflicting Taml property field name of '%s' in property alias of '%s'.", pFieldName, mAliasName );
-
-            // Cache property field.
-            TamlPropertyFieldFactory.cacheObject( pPropertyField );
-            return NULL;
-        }
-
-        // Ensure the field value is not too long.
-        if ( dStrlen( pFieldValue ) >= MAX_TAML_PROPERTY_FIELDVALUE_LENGTH )
-        {
-            // Warn.
-            Con::warnf("Taml field name '%s' has a field value that is too long (Max:%d): '%s'.",
-                pFieldName,
-                MAX_TAML_PROPERTY_FIELDVALUE_LENGTH,
-                pFieldValue );
-
-            // Cache property field.
-            TamlPropertyFieldFactory.cacheObject( pPropertyField );
-            return NULL;
-        }
-#endif
-        // Store property field.
-        push_back( pPropertyField );
-
-        return pPropertyField;
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    TamlPropertyField* addField( const char* pFieldName, SimObject* pFieldObject )
+    inline TamlCustomField* addField( const char* pFieldName, const float fieldValue )
     {
-        // Create a property field.
-        TamlPropertyField* pPropertyField = TamlPropertyFieldFactory.createObject();
-
-        // Set property field.
-        pPropertyField->set( pFieldName, pFieldObject );
-
-#if TORQUE_DEBUG
-        // Ensure a field name conflict does not exist.
-        for( TamlPropertyFieldVector::iterator propertyFieldItr = begin(); propertyFieldItr != end(); ++propertyFieldItr )
-        {
-            // Skip if field name is not the same.
-            if ( pPropertyField->getFieldName() != (*propertyFieldItr)->getFieldName() )
-                continue;
-
-            // Warn!
-            Con::warnf("Conflicting Taml property field name of '%s' in property alias of '%s'.", pFieldName, mAliasName );
-
-            // Cache property field.
-            TamlPropertyFieldFactory.cacheObject( pPropertyField );
-            return NULL;
-        }
-#endif
-        // Store property field.
-        push_back( pPropertyField );
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
+    }
 
-        return pPropertyField;
+    inline TamlCustomField* addField( const char* pFieldName, const char* fieldValue )
+    {
+        TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
+        pNodeField->setFieldValue( pFieldName, fieldValue );
+        return registerField( pNodeField );
     }
 
-    const TamlPropertyField* findField( const char* pFieldName ) const
+    inline const TamlCustomField* findField( const char* pFieldName ) const
     {
         // Sanity!
         AssertFatal( pFieldName != NULL, "Cannot find Taml field name that is NULL." );
@@ -402,8 +476,8 @@ public:
         // Fetch field name.
         StringTableEntry fieldName = StringTable->insert( pFieldName );
 
-        // Find property field.
-        for( TamlPropertyFieldVector::const_iterator fieldItr = begin(); fieldItr != end(); ++fieldItr )
+        // Find node field.
+        for( TamlCustomFieldVector::const_iterator fieldItr = mFields.begin(); fieldItr != mFields.end(); ++fieldItr )
         {
             if ( (*fieldItr)->getFieldName() == fieldName )
                 return (*fieldItr);
@@ -412,175 +486,199 @@ public:
         return NULL;
     }
 
-    StringTableEntry    mAliasName;
-    bool                mIgnoreEmpty;
-};
+    inline void setNodeName( const char* pNodeName )
+    {
+        // Sanity!
+        AssertFatal( pNodeName != NULL, "Cannot add a NULL node name." );
 
-static FactoryCache<TamlPropertyAlias> TamlPropertyAliasFactory;
+        mNodeName = StringTable->insert( pNodeName );
+    }
 
-//-----------------------------------------------------------------------------
+    inline StringTableEntry getNodeName( void ) const { return mNodeName; }
 
-typedef Vector<TamlPropertyAlias*> TamlPropertyAliasVector;
+    void setWriteNode( TamlWriteNode* pWriteNode );
 
-class TamlCustomProperty :
-    public TamlPropertyAliasVector,
-    public IFactoryObjectReset
-{
-public:
-    TamlCustomProperty()
+    inline void setNodeText( const char* pNodeText )
     {
-    }
+        AssertFatal( dStrlen( pNodeText ) < MAX_TAML_NODE_FIELDVALUE_LENGTH, "Custom node text is too long." );
 
-    virtual ~TamlCustomProperty()
-    {
-        // Everything should already be cleared in a state reset.
-        // Touching any memory here is dangerous as this type is typically
-        // held in a static factory cache until shutdown at which point
-        // pretty much anything or everything could be invalid!
+        mNodeText.set( StringTable->EmptyString, pNodeText );
     }
+    inline const TamlCustomField& getNodeTextField( void ) const { return mNodeText; }
+    inline TamlCustomField& getNodeTextField( void ) { return mNodeText; }
 
-    virtual void resetState( void )
+    inline const Vector<TamlCustomNode*>& getChildren( void ) const { return mChildren; }
+    inline const TamlCustomFieldVector& getFields( void ) const { return mFields; }
+
+    inline bool isProxyObject( void ) const { return mpProxyObject != NULL; }
+    template<typename T> T* getProxyObject( const bool deleteIfNotType ) const
     {
-        while( size() > 0 )
+        // Return nothing if no proxy object.
+        if ( mpProxyObject == NULL )
+            return NULL;
+
+        // Cast object to specified type.
+        T* pTypeCast = dynamic_cast<T*>( mpProxyObject );
+
+        // Destroy the object if not the specified type and requested to do so.
+        if ( deleteIfNotType && pTypeCast == NULL )
         {
-            TamlPropertyAliasFactory.cacheObject( back() );
-            pop_back();
+            mpProxyObject->deleteObject();
+            return NULL;
         }
-        mIgnoreEmpty = true;
-    }
 
-    void set( const char* pPropertyName )
-    {
-        // Sanity!
-        AssertFatal( pPropertyName != NULL, "TamlCustomProperty::set() - Property name cannot be NULL." );
-
-        mPropertyName = StringTable->insert( pPropertyName );
+        return pTypeCast;
     }
+    inline const TamlWriteNode* getProxyWriteNode( void ) const { return mpProxyWriteNode; }
 
-    TamlPropertyAlias* addAlias( const char* pAliasName, const bool ignoreEmpty = false )
-    {
-        // Create a alias.
-        TamlPropertyAlias* pAlias = TamlPropertyAliasFactory.createObject();
+    inline bool isEmpty( void ) const { return mNodeText.isValueEmpty() && mFields.size() == 0 && mChildren.size() == 0; }
 
-        // Set alias name.
-        pAlias->set( pAliasName );
+    inline void setIgnoreEmpty( const bool ignoreEmpty ) { mIgnoreEmpty = ignoreEmpty; }
+    inline bool getIgnoreEmpty( void ) const { return mIgnoreEmpty; }
 
-        // Set ignore-empty flag.
-        pAlias->mIgnoreEmpty = ignoreEmpty;
+private:
+    inline TamlCustomField* registerField( TamlCustomField* pCustomField )
+    {
+#if TORQUE_DEBUG
+        // Ensure a field name conflict does not exist.
+        for( Vector<TamlCustomField*>::iterator nodeFieldItr = mFields.begin(); nodeFieldItr != mFields.end(); ++nodeFieldItr )
+        {
+            // Skip if field name is not the same.
+            if ( pCustomField->getFieldName() != (*nodeFieldItr)->getFieldName() )
+                continue;
 
-        // Store alias.
-        push_back( pAlias );
+            // Warn!
+            Con::warnf("Conflicting Taml node field name of '%s' in node '%s'.", pCustomField->getFieldName(), mNodeName );
 
-        return pAlias;
-    }
+            // Cache node field.
+            TamlCustomFieldFactory.cacheObject( pCustomField );
+            return NULL;
+        }
 
-    void removeAlias( const U32 index )
-    {
-        // Sanity!
-        AssertFatal( index < (U32)size(), "TamlCustomProperty::removeAlias() - Index is out of bounds." );
+        // Ensure the field value is not too long.
+        if ( dStrlen( pCustomField->getFieldValue() ) >= MAX_TAML_NODE_FIELDVALUE_LENGTH )
+        {
+            // Warn.
+            Con::warnf("Taml field name '%s' has a field value that is too long (Max:%d): '%s'.",
+                pCustomField->getFieldName(),
+                MAX_TAML_NODE_FIELDVALUE_LENGTH,
+                pCustomField->getFieldValue() );
 
-        // Cache the alias.
-        TamlPropertyAliasFactory.cacheObject( at(index) );
+            // Cache node field.
+            TamlCustomFieldFactory.cacheObject( pCustomField );
+            return NULL;
+        }
+#endif
+        // Store node field.
+        mFields.push_back( pCustomField );
 
-        // Remove it.
-        erase( index );
+        return pCustomField;
     }
 
-    StringTableEntry mPropertyName;
-    bool mIgnoreEmpty;
-};
+    inline TamlCustomField* createField( void ) const { return TamlCustomFieldFactory.createObject(); }
 
-static FactoryCache<TamlCustomProperty> TamlCustomPropertyFactory;
+private:
+    StringTableEntry        mNodeName;
+    TamlCustomField         mNodeText;
+    Vector<TamlCustomNode*> mChildren;
+    TamlCustomFieldVector   mFields;
+    bool                    mIgnoreEmpty;
+
+    SimObject*              mpProxyObject;
+    TamlWriteNode*          mpProxyWriteNode;
+};
 
 //-----------------------------------------------------------------------------
 
-typedef Vector<TamlCustomProperty*> TamlCustomPropertyVector;
-
-class TamlCustomProperties :
-    public TamlCustomPropertyVector,
-    public IFactoryObjectReset
+class TamlCustomNodes : public IFactoryObjectReset
 {
 public:
-    TamlCustomProperties()
+    TamlCustomNodes()
     {
     }
 
-    virtual ~TamlCustomProperties()
+    virtual ~TamlCustomNodes()
     {
         resetState();
     }
 
     virtual void resetState( void )
     {
-        while( size() > 0 )
+        // Cache the nodes.
+        while ( mNodes.size() > 0 )
         {
-            TamlCustomPropertyFactory.cacheObject( back() );
-            pop_back();
+            TamlCustomNodeFactory.cacheObject( mNodes.back() );
+            mNodes.pop_back();
         }
     }
 
-    TamlCustomProperty* addProperty( const char* pPropertyName, const bool ignoreEmpty = true )
+    inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
     {
-        // Create a custom property.
-        TamlCustomProperty* pCustomProperty = TamlCustomPropertyFactory.createObject();
+        // Create a custom node.
+        TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
 
-        // Set property name.
-        pCustomProperty->set( pPropertyName );
+        // Set node name.
+        pCustomNode->setNodeName( pNodeName );
 
         // Set ignore-empty flag.
-        pCustomProperty->mIgnoreEmpty = ignoreEmpty;
+        pCustomNode->setIgnoreEmpty( ignoreEmpty );
 
 #if TORQUE_DEBUG
-        // Ensure an property name conflict does not exist.
-        for( TamlCustomPropertyVector::iterator propertyItr = begin(); propertyItr != end(); ++propertyItr )
+        // Ensure a node name conflict does not exist.
+        for( TamlCustomNodeVector::iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
         {
-            // Skip if property name is not the same.
-            if ( pCustomProperty->mPropertyName != (*propertyItr)->mPropertyName )
+            // Skip if node name is not the same.
+            if ( pCustomNode->getNodeName() != (*nodeItr)->getNodeName() )
                 continue;
 
             // Warn!
-            Con::warnf("Conflicting Taml custom property name of '%s'.", pPropertyName );
+            Con::warnf("Conflicting Taml custom node name of '%s'.", pNodeName );
 
-            // Cache property.
-            TamlCustomPropertyFactory.cacheObject( pCustomProperty );
+            // Cache node.
+            TamlCustomNodeFactory.cacheObject( pCustomNode );
             return NULL;
         }
 #endif
-        // Store property.
-        push_back( pCustomProperty );
+        // Store node.
+        mNodes.push_back( pCustomNode );
 
-        return pCustomProperty;
+        return pCustomNode;
     }
 
-    void removeProperty( const U32 index )
+    inline void removeNode( const U32 index )
     {
         // Sanity!
-        AssertFatal( index < (U32)size(), "TamlCustomProperty::removeProperty() - Index is out of bounds." );
+        AssertFatal( index < (U32)mNodes.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
 
-        // Cache the custom property.
-        TamlCustomPropertyFactory.cacheObject( at(index) );
+        // Cache the custom node.
+        TamlCustomNodeFactory.cacheObject( mNodes[index] );
 
         // Remove it.
-        erase( index );
+        mNodes.erase( index );
     }
 
-    const TamlCustomProperty* findProperty( const char* pPropertyName ) const
+    inline const TamlCustomNode* findNode( const char* pNodeName ) const
     {
         // Sanity!
-        AssertFatal( pPropertyName != NULL, "Cannot find Taml property name that is NULL." );
+        AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
 
-        // Fetch property name.
-        StringTableEntry propertyName = StringTable->insert( pPropertyName );
+        // Fetch node name.
+        StringTableEntry nodeName = StringTable->insert( pNodeName );
 
-        // Find property.
-        for( Vector<TamlCustomProperty*>::const_iterator propertyItr = begin(); propertyItr != end(); ++propertyItr )
+        // Find node.
+        for( Vector<TamlCustomNode*>::const_iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
         {
-            if ( (*propertyItr)->mPropertyName == propertyName )
-                return (*propertyItr);
+            if ( (*nodeItr)->getNodeName() == nodeName )
+                return (*nodeItr);
         }
 
         return NULL;
     }
+
+    inline const TamlCustomNodeVector& getNodes( void ) const { return mNodes; }
+
+private:
+    TamlCustomNodeVector mNodes;
 };
 
 #endif // _TAML_CUSTOM_H_

+ 2 - 2
engine/source/persistence/taml/tamlWriteNode.cc

@@ -61,6 +61,6 @@ void TamlWriteNode::resetNode( void )
     // Reset callbacks.
     mpTamlCallbacks = NULL;
 
-    // Reset custom properties.
-    mCustomProperties.resetState();
+    // Reset custom nodes.
+    mCustomNodes.resetState();
 }

+ 1 - 3
engine/source/persistence/taml/tamlWriteNode.h

@@ -67,7 +67,6 @@ public:
     {
         // NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
         mRefToNode = NULL;
-        mRefField = StringTable->EmptyString;
         mChildren = NULL;
         mpSimObject = NULL;
         mpTamlCallbacks = NULL;
@@ -107,13 +106,12 @@ public:
 
     U32                         mRefId;
     TamlWriteNode*              mRefToNode;
-    StringTableEntry            mRefField;
     SimObject*                  mpSimObject;
     TamlCallbacks*              mpTamlCallbacks;
     const char*                 mpObjectName;
     Vector<TamlWriteNode::FieldValuePair*> mFields;
     Vector<TamlWriteNode*>*     mChildren;
-    TamlCustomProperties        mCustomProperties;
+    TamlCustomNodes             mCustomNodes;
 };
 
 #endif // _TAML_WRITE_NODE_H_

+ 98 - 78
engine/source/persistence/taml/tamlXmlReader.cc

@@ -166,7 +166,7 @@ SimObject* TamlXmlReader::parseElement( TiXmlElement* pXmlElement )
     // Fetch any children.
     TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
 
-    TamlCustomProperties customProperties;
+    TamlCustomNodes customProperties;
 
     // Do we have any element children?
     if ( pChildXmlNode != NULL )
@@ -247,102 +247,91 @@ SimObject* TamlXmlReader::parseElement( TiXmlElement* pXmlElement )
 
 //-----------------------------------------------------------------------------
 
-void TamlXmlReader::parseCustomElement( TiXmlElement* pXmlElement, TamlCustomProperties& customProperties )
+void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject )
 {
     // Debug Profiling.
-    PROFILE_SCOPE(TamlXmlReader_ParseCustomElement);
-
-    // Is this a standard child element?
-    const char* pPeriod = dStrchr( pXmlElement->Value(), '.' );
+    PROFILE_SCOPE(TamlXmlReader_ParseAttributes);
 
     // Sanity!
-    AssertFatal( pPeriod != NULL, "Parsing extended element but no period character found." );
-
-    // Fetch any property alias.
-    TiXmlNode* pPropertyAliasXmlNode = pXmlElement->FirstChild();
+    AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
 
-    // Do we have any property alias?
-    if ( pPropertyAliasXmlNode != NULL )
+    // Iterate attributes.
+    for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
     {
-        // Yes, so add custom property.
-        TamlCustomProperty* pCustomProperty = customProperties.addProperty( pPeriod+1 );
-
-        do
-        {
-            // Fetch element.
-            TiXmlElement* pPropertyAliasXmlElement = dynamic_cast<TiXmlElement*>( pPropertyAliasXmlNode );
-
-            // Move to next sibling.
-            pPropertyAliasXmlNode = pPropertyAliasXmlNode->NextSibling();
+        // Insert attribute name.
+        StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
 
-            // Skip if this is not an element?
-            if ( pPropertyAliasXmlElement == NULL )
+        // Ignore if this is a Taml attribute.
+        if (    attributeName == mTamlRefId ||
+                attributeName == mTamlRefToId ||
+                attributeName == mTamlObjectName ||
+                attributeName == mTamlRefField )
                 continue;
 
-            // Add property alias.
-            TamlPropertyAlias* pPropertyAlias = pCustomProperty->addAlias( pPropertyAliasXmlElement->Value() );
+        // We can assume this is a field for now.
+        pSimObject->setPrefixedDataField( attributeName, NULL, pAttribute->Value() );
+    }
+}
 
-            // Iterate property field attributes.
-            for ( TiXmlAttribute* pAttribute = pPropertyAliasXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
-            {
-                // Insert attribute name.
-                StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
+//-----------------------------------------------------------------------------
 
-                // Add property field.
-                pPropertyAlias->addField( attributeName, pAttribute->Value() );
-            }
+void TamlXmlReader::parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& customNodes )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(TamlXmlReader_ParseCustomElement);
 
-            // Fetch any children.
-            TiXmlNode* pChildXmlNode = pPropertyAliasXmlElement->FirstChild();
+    // Is this a standard child element?
+    const char* pPeriod = dStrchr( pXmlElement->Value(), '.' );
 
-            // Do we have any element children?
-            if ( pChildXmlNode != NULL )
-            {
-                do
-                {
-                    // Fetch element.
-                    TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
+    // Sanity!
+    AssertFatal( pPeriod != NULL, "Parsing extended element but no period character found." );
 
-                    // Move to next sibling.
-                    pChildXmlNode = pChildXmlNode->NextSibling();
+    // Fetch any custom XML node.
+    TiXmlNode* pCustomXmlNode = pXmlElement->FirstChild();
 
-                    // Skip if this is not an element?
-                    if ( pChildXmlElement == NULL )
-                        continue;
+    // Finish is no XML node exists.
+    if ( pCustomXmlNode == NULL )
+        return;
 
-                    // Fetch the reference field.
-                    const char* pRefField = getTamlRefField( pChildXmlElement );
+    // Yes, so add custom node.
+    TamlCustomNode* pCustomNode = customNodes.addNode( pPeriod+1 );
 
-                    // Was a reference field found?
-                    if ( pRefField == NULL )
-                    {
-                        // No, so warn.
-                        Con::warnf( "Taml: Encountered a child element in a custom element but it did not have a field reference using '%s'.", mTamlRefField );
-                        continue;
-                    }
+    do
+    {
+        // Fetch element.
+        TiXmlElement* pCustomXmlElement = dynamic_cast<TiXmlElement*>( pCustomXmlNode );
 
-                    // Parse the child element.
-                    SimObject* pFieldObject = parseElement( pChildXmlElement );
+        // Move to next sibling.
+        pCustomXmlNode = pCustomXmlNode->NextSibling();
 
-                    // Add property field.
-                    pPropertyAlias->addField( pRefField, pFieldObject );
-                }
-                while( pChildXmlNode != NULL );
-            }
-        }
-        while ( pPropertyAliasXmlNode != NULL );
+        // Skip if this is not an element.
+        if ( pCustomXmlElement == NULL )
+            continue;
+
+        // Parse custom node.
+        parseCustomNode( pCustomXmlElement, pCustomNode );
     }
+    while ( pCustomXmlNode != NULL );
 }
 
 //-----------------------------------------------------------------------------
 
-void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject )
+void TamlXmlReader::parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode )
 {
-    // Debug Profiling.
-    PROFILE_SCOPE(TamlXmlReader_ParseAttributes);
+    // Is the node a proxy object?
+    if (  getTamlRefId( pXmlElement ) != 0 || getTamlRefToId( pXmlElement ) != 0 )
+    {
+        // Yes, so parse proxy object.
+        SimObject* pProxyObject = parseElement( pXmlElement );
 
-    // Sanity!
-    AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
+        // Add child node.
+        pCustomNode->addNode( pProxyObject );
+
+        return;
+    }
+
+    // Yes, so add child node.
+    TamlCustomNode* pChildNode = pCustomNode->addNode( pXmlElement->Value() );
 
     // Iterate attributes.
     for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
@@ -350,15 +339,46 @@ void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimO
         // Insert attribute name.
         StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
 
-        // Ignore if this is a Taml attribute.
-        if (    attributeName == mTamlRefId ||
-                attributeName == mTamlRefToId ||
-                attributeName == mTamlObjectName ||
-                attributeName == mTamlRefField )
+        // Skip if a Taml reference attribute.
+        if ( attributeName == mTamlRefId || attributeName == mTamlRefToId )
+            continue;
+
+        // Add node field.
+        pChildNode->addField( attributeName, pAttribute->Value() );
+    }
+
+    // Fetch any element text.
+    const char* pElementText = pXmlElement->GetText();
+
+    // Do we have any element text?
+    if ( pElementText != NULL )
+    {
+        // Yes, so store it.
+        pChildNode->setNodeText( pElementText );
+    }
+
+    // Fetch any children.
+    TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
+
+    // Do we have any element children?
+    if ( pChildXmlNode != NULL )
+    {
+        do
+        {
+            // Yes, so fetch child element.
+            TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
+
+            // Move to next sibling.
+            pChildXmlNode = pChildXmlNode->NextSibling();
+
+            // Skip if this is not an element.
+            if ( pChildXmlElement == NULL )
                 continue;
 
-        // We can assume this is a field for now.
-        pSimObject->setPrefixedDataField( attributeName, NULL, pAttribute->Value() );
+            // Parse custom node.
+            parseCustomNode( pChildXmlElement, pChildNode );
+        }
+        while( pChildXmlNode != NULL );
     }
 }
 

+ 2 - 1
engine/source/persistence/taml/tamlXmlReader.h

@@ -69,8 +69,9 @@ private:
     void resetParse( void );
 
     SimObject* parseElement( TiXmlElement* pXmlElement );
-    void parseCustomElement( TiXmlElement* pXmlElement, TamlCustomProperties& customProperties );
     void parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject );
+    void parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& pCustomNode );
+    void parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode );
 
     U32 getTamlRefId( TiXmlElement* pXmlElement );
     U32 getTamlRefToId( TiXmlElement* pXmlElement );

+ 110 - 73
engine/source/persistence/taml/tamlXmlWriter.cc

@@ -80,24 +80,10 @@ TiXmlNode* TamlXmlWriter::compileElement( const TamlWriteNode* pTamlWriteNode )
         // Set reference to Id attribute.
         pElement->SetAttribute( TAML_REFID_ATTRIBUTE_NAME, referenceToId );
 
-        // Do we have a reference field?
-        if ( pTamlWriteNode->mRefField != StringTable->EmptyString )
-        {
-            // Yes, so set attribute.
-            pElement->SetAttribute( TAML_REF_FIELD_NAME, pTamlWriteNode->mRefField );
-        }
-
         // Finish because we're a reference to another object.
         return pElement;
     }
 
-    // Do we have a reference field?
-    if ( pTamlWriteNode->mRefField != StringTable->EmptyString )
-    {
-        // Yes, so set attribute.
-        pElement->SetAttribute( TAML_REF_FIELD_NAME, pTamlWriteNode->mRefField );
-    }
-
     // Fetch object name.
     const char* pObjectName = pTamlWriteNode->mpObjectName;
 
@@ -133,70 +119,80 @@ TiXmlNode* TamlXmlWriter::compileElement( const TamlWriteNode* pTamlWriteNode )
 
 //-----------------------------------------------------------------------------
 
+void TamlXmlWriter::compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(TamlXmlWriter_CompileAttributes);
+
+    // Fetch fields.
+    const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
+
+    // Ignore if no fields.
+    if ( fields.size() == 0 )
+        return;
+
+    // Iterate fields.
+    for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
+    {
+        // Fetch field/value pair.
+        TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
+
+        // Set field attribute.
+        pXmlElement->SetAttribute( pFieldValue->mName, pFieldValue->mpValue );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
 void TamlXmlWriter::compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
 {
     // Debug Profiling.
     PROFILE_SCOPE(TamlXmlWriter_CompileCustomElements);
 
-    // Fetch custom properties.
-    const TamlCustomProperties& customProperties = pTamlWriteNode->mCustomProperties;
+    // Fetch custom nodes.
+    const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
+
+    // Fetch custom nodes.
+    const TamlCustomNodeVector& nodes = customNodes.getNodes();
 
-    // Finish if no custom properties exist.
-    if ( customProperties.size() == 0 )
+    // Finish if no custom nodes to process.
+    if ( nodes.size() == 0 )
         return;
 
-    // Iterate custom properties.
-    for( TamlCustomProperties::const_iterator customPropertyItr = customProperties.begin(); customPropertyItr != customProperties.end(); ++customPropertyItr )
+    // Iterate custom nodes.
+    for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
     {
-        // Fetch custom property.
-        TamlCustomProperty* pCustomProperty = *customPropertyItr;
+        // Fetch the custom node.
+        TamlCustomNode* pCustomNode = *customNodesItr;
 
         // Format extended element name.
         char extendedElementNameBuffer[256];
-        dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pXmlElement->Value(), pCustomProperty->mPropertyName );
+        dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pXmlElement->Value(), pCustomNode->getNodeName() );
         StringTableEntry extendedElementName = StringTable->insert( extendedElementNameBuffer );
 
+        if ( dStricmp(pCustomNode->getNodeName(), "controllers") == 0 )
+        {
+            S32 a = 0;
+        }
+
         // Create element.
         TiXmlElement* pExtendedPropertyElement = new TiXmlElement( extendedElementName );
 
-        // Iterate property alias.
-        for( TamlCustomProperty::const_iterator propertyAliasItr = pCustomProperty->begin(); propertyAliasItr != pCustomProperty->end(); ++propertyAliasItr )
+        // Fetch node children.
+        const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
+
+        // Iterate children nodes.
+        for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
         {
-            // Fetch property alias.
-            TamlPropertyAlias* pPropertyAlias = *propertyAliasItr;
-
-            // Skip if the alias is set to ignore no properties and there are none.
-            if ( pPropertyAlias->mIgnoreEmpty && pPropertyAlias->size() == 0 )
-                continue;
-
-            // Create element.
-            TiXmlElement* pPropertyElement = new TiXmlElement( pPropertyAlias->mAliasName );
-
-            // Iterate property fields.
-            for ( TamlPropertyAlias::const_iterator propertyFieldItr = pPropertyAlias->begin(); propertyFieldItr != pPropertyAlias->end(); ++propertyFieldItr )
-            {
-                // Fetch property field.
-                TamlPropertyField* pPropertyField = *propertyFieldItr;
-
-                // Is it an object field?
-                if ( pPropertyField->isObjectField() )
-                {
-                    // Yes, so write child element.
-                    pPropertyElement->LinkEndChild( compileElement( pPropertyField->getWriteNode() ) );
-                }
-                else
-                {
-                    // No, so set property attribute.
-                    pPropertyElement->SetAttribute( pPropertyField->getFieldName(), pPropertyField->getFieldValue() );
-                }
-            }
-
-            // Write property element as child.
-            pExtendedPropertyElement->LinkEndChild( pPropertyElement );
+            // Fetch child node.
+            const TamlCustomNode* pChildNode = *childNodeItr;
+
+            // Compile the custom node.
+            compileCustomNode( pExtendedPropertyElement, pChildNode );
         }
 
-        // Is the custom property set to ignore no alias' and there are none.
-        if ( pCustomProperty->mIgnoreEmpty && pExtendedPropertyElement->NoChildren() )
+        // Finish if the node is set to ignore if empty and it is empty.
+        if ( pCustomNode->getIgnoreEmpty() && pExtendedPropertyElement->NoChildren() )
         {
             // Yes, so delete the extended element.
             delete pExtendedPropertyElement;
@@ -204,7 +200,7 @@ void TamlXmlWriter::compileCustomElements( TiXmlElement* pXmlElement, const Taml
         }
         else
         {
-            // No, so write the extended property element as child.
+            // No, so add elementt as child.
             pXmlElement->LinkEndChild( pExtendedPropertyElement );
         }
     }
@@ -212,25 +208,66 @@ void TamlXmlWriter::compileCustomElements( TiXmlElement* pXmlElement, const Taml
 
 //-----------------------------------------------------------------------------
 
-void TamlXmlWriter::compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
+void TamlXmlWriter::compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode )
 {
-    // Debug Profiling.
-    PROFILE_SCOPE(TamlXmlWriter_CompileAttributes);
-
-    // Fetch fields.
-    const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
+    // Finish if the node is set to ignore if empty and it is empty.
+    if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
+        return;
 
-    // Ignore if no fields.
-    if ( fields.size() == 0 )
+    // Is the node a proxy object?
+    if ( pCustomNode->isProxyObject() )
+    {
+        // Yes, so write the proxy object.
+        pXmlElement->LinkEndChild( compileElement( pCustomNode->getProxyWriteNode() ) );
         return;
+    }
+
+    // Create element.
+    TiXmlElement* pNodeElement = new TiXmlElement( pCustomNode->getNodeName() );
+
+    // Is there any node text?
+    if ( !pCustomNode->getNodeTextField().isValueEmpty() )
+    {
+        // Yes, so add a text node.
+        pNodeElement->LinkEndChild( new TiXmlText( pCustomNode->getNodeTextField().getFieldValue() ) );
+    }
+
+    // Fetch fields.
+    const TamlCustomFieldVector& fields = pCustomNode->getFields();
 
     // Iterate fields.
-    for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
+    for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
     {
-        // Fetch field/value pair.
-        TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
+        // Fetch field.
+        const TamlCustomField* pField = *fieldItr;
 
-        // Set field attribute.
-        pXmlElement->SetAttribute( pFieldValue->mName, pFieldValue->mpValue );
+        // Set field.
+        pNodeElement->SetAttribute( pField->getFieldName(), pField->getFieldValue() );
     }
-}
+
+    // Fetch node children.
+    const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
+
+    // Iterate children nodes.
+    for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
+    {
+        // Fetch child node.
+        const TamlCustomNode* pChildNode = *childNodeItr;
+
+        // Compile the child node.
+        compileCustomNode( pNodeElement, pChildNode );
+    }
+
+    // Finish if the node is set to ignore if empty and it is empty (including fields).
+    if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pNodeElement->NoChildren() )
+    {
+        // Yes, so delete the extended element.
+        delete pNodeElement;
+        pNodeElement = NULL;
+    }
+    else
+    {
+        // Add node element as child.
+        pXmlElement->LinkEndChild( pNodeElement );
+    }
+}

+ 2 - 1
engine/source/persistence/taml/tamlXmlWriter.h

@@ -51,8 +51,9 @@ private:
 
 private:
     TiXmlNode* compileElement( const TamlWriteNode* pTamlWriteNode );
-    void compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
     void compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
+    void compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
+    void compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode );
 };
 
 #endif // _TAML_XMLWRITER_H_

+ 3 - 3
engine/source/sim/simObject.h

@@ -321,10 +321,10 @@ protected:
     virtual void onTamlPreWrite( void ) {}
     virtual void onTamlPostWrite( void ) {}
     virtual void onTamlPreRead( void ) {}
-    virtual void onTamlPostRead( const TamlCustomProperties& customProperties ) {}
+    virtual void onTamlPostRead( const TamlCustomNodes& customNodes ) {}
     virtual void onTamlAddParent( SimObject* pParentObject ) {}
-    virtual void onTamlCustomWrite( TamlCustomProperties& customProperties ) {}
-    virtual void onTamlCustomRead( const TamlCustomProperties& customProperties ) {}
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes ) {}
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes ) {}
     
 protected:
     bool	mCanSaveFieldDictionary; ///< true if dynamic fields (added at runtime) should be saved, defaults to true

+ 5 - 5
main.cs

@@ -41,7 +41,7 @@ $Scripts::ignoreDSOs = true;
 // The name of the game. Used to form the path to save preferences. Defaults to C++ engine define TORQUE_GAME_NAME
 // if not specified.
 // Appending version string to avoid conflicts with existing versions and other versions.
-setCompanyAndProduct("GarageGames", "Sandbox" );
+setCompanyAndProduct("GarageGames", "Torque 2D" );
 
 // Set module database information echo.
 ModuleDatabase.EchoInfo = false;
@@ -56,13 +56,13 @@ AssetDatabase.IgnoreAutoUnload = true;
 // Scan modules.
 ModuleDatabase.scanModules( "modules" );
 
-// Load sandbox module.
-ModuleDatabase.LoadExplicit( "Sandbox" );
+// Load AppCore module.
+ModuleDatabase.LoadExplicit( "AppCore" );
 
 //-----------------------------------------------------------------------------
 
 function onExit()
 {
-    // Unload the sandbox module.
-    ModuleDatabase.unloadExplicit( "Sandbox" );
+    // Unload the AppCore module.
+    ModuleDatabase.unloadExplicit( "AppCore" );
 }

+ 0 - 0
modules/Sandbox/1/fonts/.gitignore → modules/AppCore/1/fonts/.gitignore


+ 50 - 0
modules/AppCore/1/main.cs

@@ -0,0 +1,50 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+function AppCore::create( %this )
+{
+    // Load system scripts
+    exec("./scripts/constants.cs");
+    exec("./scripts/defaultPreferences.cs");
+    exec("./scripts/canvas.cs");
+    exec("./scripts/openal.cs");
+    
+    // Initialize the canvas
+    initializeCanvas("Torque 2D");
+    
+    // Set the canvas color
+    Canvas.BackgroundColor = "CornflowerBlue";
+    Canvas.UseBackgroundColor = false;
+    
+    // Initialize audio
+    initializeOpenAL();
+    
+    ModuleDatabase.loadGroup("gameBase");
+}
+
+//-----------------------------------------------------------------------------
+
+function AppCore::destroy( %this )
+{
+
+}
+

+ 7 - 0
modules/AppCore/1/module.taml

@@ -0,0 +1,7 @@
+<ModuleDefinition
+	ModuleId="AppCore"
+	VersionId="1"
+	Description="Barebones startup module"
+	ScriptFile="main.cs"
+	CreateFunction="create"
+	DestroyFunction="destroy" />

+ 132 - 132
modules/Sandbox/1/scripts/canvas.cs → modules/AppCore/1/scripts/canvas.cs

@@ -1,133 +1,133 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-//------------------------------------------------------------------------------
-// initializeCanvas
-// Constructs and initializes the default canvas window.
-//------------------------------------------------------------------------------
-$canvasCreated = false;
-function initializeCanvas(%windowName)
-{
-    // Don't duplicate the canvas.
-    if($canvasCreated)
-    {
-        error("Cannot instantiate more than one canvas!");
-        return;
-    }
-
-    videoSetGammaCorrection($pref::OpenGL::gammaCorrection);
-
-    if ( !createCanvas(%windowName) )
-    {
-        error("Canvas creation failed. Shutting down.");
-        quit();
-    }
-
-    $pref::iOS::ScreenDepth = 32;
-
-    if ( $pref::iOS::DeviceType !$= "" )
-    {
-        %resolution = iOSResolutionFromSetting($pref::iOS::DeviceType, $pref::iOS::ScreenOrientation);
-    }
-    else
-    {
-        if ( $pref::Video::windowedRes !$= "" )
-            %resolution = $pref::Video::windowedRes;
-        else
-            %resolution = $pref::Video::defaultResolution;
-    }
-
-    if ($platform $= "windows" || $platform $= "macos")
-    {
-        setScreenMode( GetWord( %resolution , 0 ), GetWord( %resolution, 1 ), GetWord( %resolution , 2 ), $pref::Video::fullScreen );
-    }
-    else
-    {
-        setScreenMode( GetWord( %resolution , 0 ), GetWord( %resolution, 1 ), GetWord( %resolution, 2 ), false );
-    }
-
-    $canvasCreated = true;
-}
-
-//------------------------------------------------------------------------------
-// resetCanvas
-// Forces the canvas to redraw itself.
-//------------------------------------------------------------------------------
-function resetCanvas()
-{
-    if (isObject(Canvas))
-        Canvas.repaint();
-}
-
-//------------------------------------------------------------------------------
-// iOSResolutionFromSetting
-// Helper function that grabs resolution strings based on device type
-//------------------------------------------------------------------------------
-function iOSResolutionFromSetting( %deviceType, %deviceScreenOrientation )
-{
-    // A helper function to get a string based resolution from the settings given.
-    %x = 0;
-    %y = 0;
-    
-    %scaleFactor = $pref::iOS::RetinaEnabled ? 2 : 1;
-
-    switch(%deviceType)
-    {
-        case $iOS::constant::iPhone:
-            if(%deviceScreenOrientation == $iOS::constant::Landscape)
-            {
-                %x =  $iOS::constant::iPhoneWidth * %scaleFactor;
-                %y =  $iOS::constant::iPhoneHeight * %scaleFactor;
-            }
-            else
-            {
-                %x =  $iOS::constant::iPhoneHeight * %scaleFactor;
-                %y =  $iOS::constant::iPhoneWidth * %scaleFactor;
-            }
-
-        case $iOS::constant::iPad:
-            if(%deviceScreenOrientation == $iOS::constant::Landscape)
-            {
-                %x =  $iOS::constant::iPadWidth * %scaleFactor;
-                %y =  $iOS::constant::iPadHeight * %scaleFactor;
-            }
-            else
-            {
-                %x =  $iOS::constant::iPadHeight * %scaleFactor;
-                %y =  $iOS::constant::iPadWidth * %scaleFactor;
-            }
-
-        case $iOS::constant::iPhone5:
-            if(%deviceScreenOrientation == $iOS::constant::Landscape)
-            {
-                %x =  $iOS::constant::iPhone5Width;
-                %y =  $iOS::constant::iPhone5Height;
-            }
-            else
-            {
-                %x =  $iOS::constant::iPhone5Height;
-                %y =  $iOS::constant::iPhone5Width;
-            }
-    }
-   
-    return %x @ " " @ %y;
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// initializeCanvas
+// Constructs and initializes the default canvas window.
+//------------------------------------------------------------------------------
+$canvasCreated = false;
+function initializeCanvas(%windowName)
+{
+    // Don't duplicate the canvas.
+    if($canvasCreated)
+    {
+        error("Cannot instantiate more than one canvas!");
+        return;
+    }
+
+    videoSetGammaCorrection($pref::OpenGL::gammaCorrection);
+
+    if ( !createCanvas(%windowName) )
+    {
+        error("Canvas creation failed. Shutting down.");
+        quit();
+    }
+
+    $pref::iOS::ScreenDepth = 32;
+
+    if ( $pref::iOS::DeviceType !$= "" )
+    {
+        %resolution = iOSResolutionFromSetting($pref::iOS::DeviceType, $pref::iOS::ScreenOrientation);
+    }
+    else
+    {
+        if ( $pref::Video::windowedRes !$= "" )
+            %resolution = $pref::Video::windowedRes;
+        else
+            %resolution = $pref::Video::defaultResolution;
+    }
+
+    if ($platform $= "windows" || $platform $= "macos")
+    {
+        setScreenMode( GetWord( %resolution , 0 ), GetWord( %resolution, 1 ), GetWord( %resolution , 2 ), $pref::Video::fullScreen );
+    }
+    else
+    {
+        setScreenMode( GetWord( %resolution , 0 ), GetWord( %resolution, 1 ), GetWord( %resolution, 2 ), false );
+    }
+
+    $canvasCreated = true;
+}
+
+//------------------------------------------------------------------------------
+// resetCanvas
+// Forces the canvas to redraw itself.
+//------------------------------------------------------------------------------
+function resetCanvas()
+{
+    if (isObject(Canvas))
+        Canvas.repaint();
+}
+
+//------------------------------------------------------------------------------
+// iOSResolutionFromSetting
+// Helper function that grabs resolution strings based on device type
+//------------------------------------------------------------------------------
+function iOSResolutionFromSetting( %deviceType, %deviceScreenOrientation )
+{
+    // A helper function to get a string based resolution from the settings given.
+    %x = 0;
+    %y = 0;
+    
+    %scaleFactor = $pref::iOS::RetinaEnabled ? 2 : 1;
+
+    switch(%deviceType)
+    {
+        case $iOS::constant::iPhone:
+            if(%deviceScreenOrientation == $iOS::constant::Landscape)
+            {
+                %x =  $iOS::constant::iPhoneWidth * %scaleFactor;
+                %y =  $iOS::constant::iPhoneHeight * %scaleFactor;
+            }
+            else
+            {
+                %x =  $iOS::constant::iPhoneHeight * %scaleFactor;
+                %y =  $iOS::constant::iPhoneWidth * %scaleFactor;
+            }
+
+        case $iOS::constant::iPad:
+            if(%deviceScreenOrientation == $iOS::constant::Landscape)
+            {
+                %x =  $iOS::constant::iPadWidth * %scaleFactor;
+                %y =  $iOS::constant::iPadHeight * %scaleFactor;
+            }
+            else
+            {
+                %x =  $iOS::constant::iPadHeight * %scaleFactor;
+                %y =  $iOS::constant::iPadWidth * %scaleFactor;
+            }
+
+        case $iOS::constant::iPhone5:
+            if(%deviceScreenOrientation == $iOS::constant::Landscape)
+            {
+                %x =  $iOS::constant::iPhone5Width;
+                %y =  $iOS::constant::iPhone5Height;
+            }
+            else
+            {
+                %x =  $iOS::constant::iPhone5Height;
+                %y =  $iOS::constant::iPhone5Width;
+            }
+    }
+   
+    return %x @ " " @ %y;
 }

+ 51 - 51
modules/Sandbox/1/scripts/constants.cs → modules/AppCore/1/scripts/constants.cs

@@ -1,51 +1,51 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-$iOS::constant::iPhone = 0;
-$iOS::constant::iPad = 1;
-$iOS::constant::iPhone5 = 2;
-
-$iOS::constant::Landscape = 0;
-$iOS::constant::Portrait = 1;
-$iOS::constant::ResolutionFull = 0;
-$iOS::constant::ResolutionSmall = 1;
-
-$iOS::constant::iPhoneWidth = 480;
-$iOS::constant::iPhoneHeight = 320;
-
-$iOS::constant::iPhone4Width = 960;
-$iOS::constant::iPhone4Height = 640;
-
-$iOS::constant::iPadWidth = 1024;
-$iOS::constant::iPadHeight = 768;
-
-$iOS::constant::NewiPadWidth = 2048;
-$iOS::constant::NewiPadHeight = 1536;
-
-$iOS::constant::iPhone5Width = 1136;
-$iOS::constant::iPhone5Height = 640;
-
-$iOS::constant::OrientationUnknown				= 0;
-$iOS::constant::OrientationLandscapeLeft		= 1;
-$iOS::constant::OrientationLandscapeRight		= 2;
-$iOS::constant::OrientationPortrait				= 3;
-$iOS::constant::OrientationPortraitUpsideDown	= 4;
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+$iOS::constant::iPhone = 0;
+$iOS::constant::iPad = 1;
+$iOS::constant::iPhone5 = 2;
+
+$iOS::constant::Landscape = 0;
+$iOS::constant::Portrait = 1;
+$iOS::constant::ResolutionFull = 0;
+$iOS::constant::ResolutionSmall = 1;
+
+$iOS::constant::iPhoneWidth = 480;
+$iOS::constant::iPhoneHeight = 320;
+
+$iOS::constant::iPhone4Width = 960;
+$iOS::constant::iPhone4Height = 640;
+
+$iOS::constant::iPadWidth = 1024;
+$iOS::constant::iPadHeight = 768;
+
+$iOS::constant::NewiPadWidth = 2048;
+$iOS::constant::NewiPadHeight = 1536;
+
+$iOS::constant::iPhone5Width = 1136;
+$iOS::constant::iPhone5Height = 640;
+
+$iOS::constant::OrientationUnknown = 0;
+$iOS::constant::OrientationLandscapeLeft = 1;
+$iOS::constant::OrientationLandscapeRight = 2;
+$iOS::constant::OrientationPortrait = 3;
+$iOS::constant::OrientationPortraitUpsideDown = 4;

+ 67 - 0
modules/AppCore/1/scripts/defaultPreferences.cs

@@ -0,0 +1,67 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+/// Game
+$Game::CompanyName              = "GarageGames LLC";
+$Game::ProductName              = "Torque 2D";
+
+/// iOS
+$pref::iOS::ScreenOrientation   = $iOS::constant::Landscape;
+$pref::iOS::ScreenDepth		    = 32;
+$pref::iOS::UseGameKit          = 0;
+$pref::iOS::UseMusic            = 0;
+$pref::iOS::UseMoviePlayer      = 0;
+$pref::iOS::UseAutoRotate       = 1;
+$pref::iOS::EnableOrientationRotation = 1;
+$pref::iOS::EnableOtherOrientationRotation = 1;   
+$pref::iOS::StatusBarType       = 0;
+
+/// Audio
+$pref::Audio::driver = "OpenAL";
+$pref::Audio::forceMaxDistanceUpdate = 0;
+$pref::Audio::environmentEnabled = 0;
+$pref::Audio::masterVolume   = 1.0;
+$pref::Audio::channelVolume1 = 1.0;
+$pref::Audio::channelVolume2 = 1.0;
+$pref::Audio::channelVolume3 = 1.0;
+$pref::Audio::sfxVolume = 1.0;
+$pref::Audio::musicVolume = 1.0;
+
+/// T2D
+$pref::T2D::ParticlePlayerEmissionRateScale = 1.0;
+$pref::T2D::ParticlePlayerSizeScale = 1.0;
+$pref::T2D::ParticlePlayerForceScale = 1.0;
+$pref::T2D::warnFileDeprecated = 1;
+$pref::T2D::warnSceneOccupancy = 1;
+
+/// Video
+$pref::Video::appliedPref = 0;
+$pref::Video::disableVerticalSync = 1;
+$pref::Video::displayDevice = "OpenGL";
+$pref::Video::preferOpenGL = 1;
+$pref::Video::fullScreen = 0;
+$pref::Video::defaultResolution = "1024 768";
+$pref::Video::windowedRes = "1024 768 32";
+$pref::OpenGL::gammaCorrection = 0.5;
+
+/// Fonts.
+$Gui::fontCacheDirectory = expandPath( "^AppCore/fonts" );

+ 63 - 63
modules/Sandbox/1/scripts/openal.cs → modules/AppCore/1/scripts/openal.cs

@@ -1,64 +1,64 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2013 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-//------------------------------------------------------------------------------
-// Audio channel descriptions.
-//------------------------------------------------------------------------------
-$musicAudioType = 1;
-$effectsAudioType = 2;
-
-//------------------------------------------------------------------------------
-// initializeOpenAL
-// Starts up the OpenAL driver.
-//------------------------------------------------------------------------------
-function initializeOpenAL()
-{
-    // Just in case it is already started.
-    shutdownOpenAL();
-
-    echo("OpenAL Driver Init");
-
-    if (!OpenALInitDriver())
-    {
-        echo("OpenALInitDriver() failed");
-        $Audio::initFailed = true;
-    }
-    else
-    {
-        // Set the master volume.
-        alxListenerf(AL_GAIN_LINEAR, $pref::Audio::masterVolume);
-
-        // Set the channel volumes.
-        for (%channel = 1; %channel <= 3; %channel++)
-            alxSetChannelVolume(%channel, $pref::Audio::channelVolume[%channel]);
-
-        echo("OpenAL Driver Init Success");
-    }
-}
-
-//------------------------------------------------------------------------------
-// shutdownOpenAL
-//------------------------------------------------------------------------------
-function shutdownOpenAL()
-{
-    OpenALShutdownDriver();
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Audio channel descriptions.
+//------------------------------------------------------------------------------
+$musicAudioType = 1;
+$effectsAudioType = 2;
+
+//------------------------------------------------------------------------------
+// initializeOpenAL
+// Starts up the OpenAL driver.
+//------------------------------------------------------------------------------
+function initializeOpenAL()
+{
+    // Just in case it is already started.
+    shutdownOpenAL();
+
+    echo("OpenAL Driver Init");
+
+    if (!OpenALInitDriver())
+    {
+        echo("OpenALInitDriver() failed");
+        $Audio::initFailed = true;
+    }
+    else
+    {
+        // Set the master volume.
+        alxListenerf(AL_GAIN_LINEAR, $pref::Audio::masterVolume);
+
+        // Set the channel volumes.
+        for (%channel = 1; %channel <= 3; %channel++)
+            alxSetChannelVolume(%channel, $pref::Audio::channelVolume[%channel]);
+
+        echo("OpenAL Driver Init Success");
+    }
+}
+
+//------------------------------------------------------------------------------
+// shutdownOpenAL
+//------------------------------------------------------------------------------
+function shutdownOpenAL()
+{
+    OpenALShutdownDriver();
 }

+ 3 - 2
modules/ConstantForceControllerToy/1/main.cs

@@ -36,7 +36,7 @@ function ConstantForceControllerToy::create( %this )
 
     // Add options.    
     addNumericOption("Force Angle", -359, 359, 1, "setForceAngle", ConstantForceControllerToy.ForceAngle, false, "The angle of the constant force.");   
-    addNumericOption("Force Magnitude", 1, 1000, 10, "setForceMagnitude", ConstantForceControllerToy.ForceMagnitude, false, "The magnitude of the constant force.");   
+    addNumericOption("Force Magnitude", 0, 1000, 10, "setForceMagnitude", ConstantForceControllerToy.ForceMagnitude, false, "The magnitude of the constant force.");   
     addNumericOption("Debris Count", 10, 1000, 10, "setDebrisCount", ConstantForceControllerToy.DebrisCount, true, "The amount of debris affected by the constant force controller.");
     
     // Reset the toy.
@@ -94,8 +94,9 @@ function ConstantForceControllerToy::createBackground( %this )
     // Create border collisions.
     %object.createEdgeCollisionShape( -50, -37.5, -50, 37.5 );
     %object.createEdgeCollisionShape( 50, -37.5, 50, 37.5 );
-    %object.createEdgeCollisionShape( -50, -37.5, 50, -37.5 );
     %object.createEdgeCollisionShape( -50, 37.5, 50, 37.5 );
+    %object.createEdgeCollisionShape( -50, -34.5, 50, -34.5 );
+    
            
     // Add the sprite to the scene.
     SandboxScene.add( %object );    

+ 1 - 1
modules/PointForceControllerToy/1/main.cs

@@ -94,8 +94,8 @@ function PointForceControllerToy::createBackground( %this )
     // Create border collisions.
     %object.createEdgeCollisionShape( -50, -37.5, -50, 37.5 );
     %object.createEdgeCollisionShape( 50, -37.5, 50, 37.5 );
-    %object.createEdgeCollisionShape( -50, -37.5, 50, -37.5 );
     %object.createEdgeCollisionShape( -50, 37.5, 50, 37.5 );
+    %object.createEdgeCollisionShape( -50, -34.5, 50, -34.5 );
            
     // Add the sprite to the scene.
     SandboxScene.add( %object );    

+ 1 - 1
modules/Sandbox/1/gui/ToolboxDialog.gui.taml

@@ -458,7 +458,7 @@
             Visible="1"
             Active="1"
             tooltipprofile="GuiToolTipProfile"
-            ToolTip="Whether to show the cdontrollers overlay or not."
+            ToolTip="Whether to show the controllers overlay or not."
             hovertime="100"
             text="Controllers"
             groupNum="-1"

+ 2 - 17
modules/Sandbox/1/main.cs

@@ -25,26 +25,11 @@ function Sandbox::create( %this )
     // Load the preferences.
     %this.loadPreferences();
     
-    // Load system scripts
-    exec( "./scripts/constants.cs");
-    exec( "./scripts/canvas.cs" );
-    exec( "./scripts/openal.cs" );
+    // Load Sandbox scripts.
     exec( "./scripts/console.cs" );
     exec( "./scripts/toolbox.cs" );    
     exec( "./scripts/customToolboxGui.cs" );
     exec( "./scripts/manipulation.cs" );
-
-    // Initialize the canvas.
-    initializeCanvas("Sandbox");
-    
-    // Set the canvas color.
-    Canvas.BackgroundColor = "CornflowerBlue";
-    Canvas.UseBackgroundColor = false;
-    
-    // Initialize audio.
-    initializeOpenAL();
-    
-    // Load Sandbox scripts.
     exec( "./scripts/scene.cs" );
     exec( "./scripts/toys.cs" );        
         
@@ -100,7 +85,7 @@ function Sandbox::destroy( %this )
 function Sandbox::loadPreferences( %this )
 {
     // Load the default preferences.
-    exec( "./scripts/defaultPreferences.cs" );
+    exec( "./scripts/sandboxPreferences.cs" );
     
     // Load the last session preferences if available.
     if ( isFile("preferences.cs") )

+ 2 - 1
modules/Sandbox/1/module.taml

@@ -1,7 +1,8 @@
 <ModuleDefinition
 	ModuleId="Sandbox"
 	VersionId="1"
-	Dependencies="ToyAssets=1"
+	Dependencies="AppCore=1,ToyAssets=1"
+    Group="gameBase"
 	Description="A sandbox for testing and prototyping."
 	ScriptFile="main.cs"
 	CreateFunction="create"

+ 39 - 0
modules/Sandbox/1/scripts/sandboxPreferences.cs

@@ -0,0 +1,39 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+// Sandbox.
+$pref::Sandbox::defaultToyId           = "TruckToy";
+$pref::Sandbox::defaultToyVersionId    = 1;
+$pref::Sandbox::defaultBackgroundColor = "Black";
+$pref::Sandbox::metricsOption   = false;
+$pref::Sandbox::fpsmetricsOption = true;
+$pref::Sandbox::controllersOption = false;
+$pref::Sandbox::jointsOption    = false;
+$pref::Sandbox::wireframeOption = false;
+$pref::Sandbox::aabbOption      = false;
+$pref::Sandbox::oobbOption      = false;
+$pref::Sandbox::sleepOption     = false;
+$pref::Sandbox::collisionOption = false;
+$pref::Sandbox::positionOption  = false;
+$pref::Sandbox::sortOption      = false;
+$pref::Sandbox::cameraMouseZoomRate = 0.1;
+$pref::Sandbox::cameraTouchZoomRate = 0.001;

+ 7 - 2
modules/ToyAssets/1/assets/particles/bonfire.asset.taml

@@ -26,8 +26,13 @@
 				Keys="0 0" />				
             <FixedForceVariation
                 Keys="0 0" />				
-            <SizeXLife
-                Keys="0 0 0.2 1 0.5 1 0.9 1 1 0" />
+            <SizeXLife>
+				<Key Time="0" Value="0"/>
+				<Key Time="0.2" Value="1"/>
+				<Key Time="0.5" Value="1"/>
+				<Key Time="0.9" Value="1"/>
+				<Key Time="1" Value="0"/>
+			</SizeXLife>
             <SizeXVariation
                 Keys="0 0.3" />				
             <SpinVariation

Some files were not shown because too many files changed in this diff