taml.cpp

Engine/source/persistence/taml/taml.cpp

More...

Public Typedefs

_TamlFormatMode 

Detailed Description

Public Typedefs

typedef Taml::TamlFormatMode _TamlFormatMode 

Public Variables

StringTableEntry tamlNamedObjectName 
StringTableEntry tamlRefIdName 
StringTableEntry tamlRefToIdName 

Public Functions

compareFieldEntries(const void * a, const void * b)

IMPLEMENT_CONOBJECT(Taml )

ImplementEnumType(_TamlFormatMode , "" )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2013 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "taml.h"
  25
  26#ifndef _TAML_XMLWRITER_H_
  27#include "persistence/taml/xml/tamlXmlWriter.h"
  28#endif
  29
  30#ifndef _TAML_XMLREADER_H_
  31#include "persistence/taml/xml/tamlXmlReader.h"
  32#endif
  33
  34#ifndef _TAML_XMLPARSER_H_
  35#include "persistence/taml/xml/tamlXmlParser.h"
  36#endif
  37
  38#ifndef _TAML_BINARYWRITER_H_
  39#include "persistence/taml/binary/tamlBinaryWriter.h"
  40#endif
  41
  42#ifndef _TAML_BINARYREADER_H_
  43#include "persistence/taml/binary/tamlBinaryReader.h"
  44#endif
  45
  46/*#ifndef _TAML_JSONWRITER_H_
  47#include "taml/json/tamlJSONWriter.h"
  48#endif
  49
  50#ifndef _TAML_JSONREADER_H_
  51#include "taml/json/tamlJSONReader.h"
  52#endif
  53
  54#ifndef _TAML_JSONPARSER_H_
  55#include "taml/json/tamlJSONParser.h"
  56#endif*/
  57
  58#ifndef _FRAMEALLOCATOR_H_
  59#include "core/frameAllocator.h"
  60#endif
  61
  62#ifndef _SIMBASE_H_
  63#include "console/simBase.h"
  64#endif
  65
  66#ifndef _MATHTYPES_H_
  67#include "math/mathTypes.h"
  68#endif
  69
  70#ifndef _MPOINT2_H_
  71#include "math/mPoint2.h"
  72#endif
  73
  74#ifndef _ASSET_BASE_H_
  75#include "assets/assetBase.h"
  76#endif
  77
  78// Script bindings.
  79#include "taml_ScriptBinding.h"
  80
  81// Debug Profiling.
  82#include "platform/profiler.h"
  83
  84//-----------------------------------------------------------------------------
  85
  86IMPLEMENT_CONOBJECT(Taml);
  87
  88//-----------------------------------------------------------------------------
  89
  90StringTableEntry tamlRefIdName = StringTable->insert("TamlId");
  91StringTableEntry tamlRefToIdName = StringTable->insert("TamlRefId");
  92StringTableEntry tamlNamedObjectName = StringTable->insert("Name");
  93
  94//-----------------------------------------------------------------------------
  95
  96typedef Taml::TamlFormatMode _TamlFormatMode;
  97ImplementEnumType(_TamlFormatMode,
  98   "")
  99{
 100   Taml::XmlFormat, "xml"
 101},
 102{ Taml::BinaryFormat, "binary" }//,
 103                                //{ Taml::JSONFormat, "json" }
 104   EndImplementEnumType;
 105
 106   //-----------------------------------------------------------------------------
 107
 108   Taml::TamlFormatMode Taml::getFormatModeEnum(const char* label)
 109   {
 110      // Search for Mnemonic.
 111      for (U32 i = 0; i < (sizeof(__TamlFormatMode::_sEnums) / sizeof(EnumTable::Value)); i++)
 112      {
 113         if (dStricmp(__TamlFormatMode::_sEnumTable[i].getName(), label) == 0)
 114            return (TamlFormatMode)__TamlFormatMode::_sEnumTable[i].getInt();
 115      }
 116
 117      // Warn.
 118      Con::warnf("Taml::getFormatModeEnum() - Invalid format of '%s'.", label);
 119
 120      return Taml::InvalidFormat;
 121   }
 122
 123   //-----------------------------------------------------------------------------
 124
 125   const char* Taml::getFormatModeDescription(const Taml::TamlFormatMode formatMode)
 126   {
 127      // Search for Mnemonic.
 128      for (U32 i = 0; i < (sizeof(__TamlFormatMode::_sEnums) / sizeof(EnumTable::Value)); i++)
 129      {
 130         if (__TamlFormatMode::_sEnumTable[i].getInt() == (S32)formatMode)
 131            return __TamlFormatMode::_sEnumTable[i].getName();
 132      }
 133
 134      // Warn.
 135      Con::warnf("Taml::getFormatModeDescription() - Invalid format mode.");
 136
 137      return StringTable->EmptyString();
 138   }
 139
 140   //-----------------------------------------------------------------------------
 141
 142   // The string-table-entries are set to string literals below because Taml is used in a static scope and the string-table cannot currently be used like that.
 143   Taml::Taml() :
 144      mMasterNodeId(0),
 145      mFormatMode(XmlFormat),
 146      mJSONStrict(true),
 147      mBinaryCompression(true),
 148      mWriteDefaults(false),
 149      mAutoFormatXmlExtension("taml"),
 150      mAutoFormat(true),
 151      mProgenitorUpdate(true),
 152      mAutoFormatBinaryExtension("baml"),
 153      mAutoFormatJSONExtension("json")
 154   {
 155      // Reset the file-path buffer.
 156      mFilePathBuffer[0] = 0;
 157   }
 158
 159   //-----------------------------------------------------------------------------
 160
 161   void Taml::initPersistFields()
 162   {
 163      // Call parent.
 164      Parent::initPersistFields();
 165
 166      addField("Format", TYPEID<_TamlFormatMode>(), Offset(mFormatMode, Taml), "The read/write format that should be used.");
 167      addField("JSONStrict", TypeBool, Offset(mBinaryCompression, Taml), "Whether to write JSON that is strictly compatible with RFC4627 or not.\n");
 168      addField("BinaryCompression", TypeBool, Offset(mBinaryCompression, Taml), "Whether ZIP compression is used on binary formatting or not.\n");
 169      addField("WriteDefaults", TypeBool, Offset(mWriteDefaults, Taml), "Whether to write static fields that are at their default or not.\n");
 170      addField("ProgenitorUpdate", TypeBool, Offset(mProgenitorUpdate, Taml), "Whether to update each type instances file-progenitor or not.\n");
 171      addField("AutoFormat", TypeBool, Offset(mAutoFormat, Taml), "Whether the format type is automatically determined by the filename extension or not.\n");
 172      addField("AutoFormatXmlExtension", TypeString, Offset(mAutoFormatXmlExtension, Taml), "When using auto-format, this is the extension (end of filename) used to detect the XML format.\n");
 173      addField("AutoFormatBinaryExtension", TypeString, Offset(mAutoFormatBinaryExtension, Taml), "When using auto-format, this is the extension (end of filename) used to detect the BINARY format.\n");
 174      addField("AutoFormatJSONExtension", TypeString, Offset(mAutoFormatJSONExtension, Taml), "When using auto-format, this is the extension (end of filename) used to detect the JSON format.\n");
 175   }
 176
 177   //-----------------------------------------------------------------------------
 178
 179   bool Taml::onAdd()
 180   {
 181      // Call parent.
 182      if (!Parent::onAdd())
 183         return false;
 184
 185      // Set JSON strict mode.
 186      mJSONStrict = Con::getBoolVariable(TAML_JSON_STRICT_VARIBLE, true);
 187
 188      // Reset the compilation.
 189      resetCompilation();
 190
 191      return true;
 192   }
 193
 194   //-----------------------------------------------------------------------------
 195
 196   void Taml::onRemove()
 197   {
 198      // Reset the compilation.
 199      resetCompilation();
 200
 201      // Call parent.
 202      Parent::onRemove();
 203   }
 204
 205   //-----------------------------------------------------------------------------
 206
 207   bool Taml::write(SimObject* pSimObject, const char* pFilename)
 208   {
 209      // Debug Profiling.
 210      PROFILE_SCOPE(Taml_Write);
 211
 212      // Sanity!
 213      AssertFatal(pSimObject != NULL, "Cannot write a NULL object.");
 214      AssertFatal(pFilename != NULL, "Cannot write to a NULL filename.");
 215
 216      // Expand the file-name into the file-path buffer.
 217      Con::expandToolScriptFilename(mFilePathBuffer, sizeof(mFilePathBuffer), pFilename);
 218
 219      FileStream stream;
 220
 221      // File opened?
 222      if (!stream.open(mFilePathBuffer, Torque::FS::File::Write))
 223      {
 224         // No, so warn.
 225         Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", mFilePathBuffer);
 226         return false;
 227      }
 228
 229      // Get the file auto-format mode.
 230      const TamlFormatMode formatMode = getFileAutoFormatMode(mFilePathBuffer);
 231
 232      // Reset the compilation.
 233      resetCompilation();
 234
 235      // Write object.
 236      const bool status = write(stream, pSimObject, formatMode);
 237
 238      // Close file.
 239      stream.close();
 240
 241      // Reset the compilation.
 242      resetCompilation();
 243
 244      return status;
 245   }
 246
 247   //-----------------------------------------------------------------------------
 248
 249   SimObject* Taml::read(const char* pFilename)
 250   {
 251      // Debug Profiling.
 252      PROFILE_SCOPE(Taml_Read);
 253
 254      // Sanity!
 255      AssertFatal(pFilename != NULL, "Cannot read from a NULL filename.");
 256
 257      // Expand the file-name into the file-path buffer.
 258      Con::expandToolScriptFilename(mFilePathBuffer, sizeof(mFilePathBuffer), pFilename);
 259
 260      FileStream stream;
 261
 262      // File opened?
 263      if (!stream.open(mFilePathBuffer, Torque::FS::File::Read))
 264      {
 265         // No, so warn.
 266         Con::warnf("Taml::read() - Could not open filename '%s' for read.", mFilePathBuffer);
 267         return NULL;
 268      }
 269
 270      // Get the file auto-format mode.
 271      const TamlFormatMode formatMode = getFileAutoFormatMode(mFilePathBuffer);
 272
 273      // Reset the compilation.
 274      resetCompilation();
 275
 276      // Write object.
 277      SimObject* pSimObject = read(stream, formatMode);
 278
 279      // Close file.
 280      stream.close();
 281
 282      // Reset the compilation.
 283      resetCompilation();
 284
 285      // Did we generate an object?
 286      if (pSimObject == NULL)
 287      {
 288         // No, so warn.
 289         Con::warnf("Taml::read() - Failed to load an object from the file '%s'.", mFilePathBuffer);
 290      }
 291      else
 292      {
 293         pSimObject->onPostAdd();
 294      }
 295
 296      return pSimObject;
 297   }
 298
 299   //-----------------------------------------------------------------------------
 300
 301   bool Taml::write(FileStream& stream, SimObject* pSimObject, const TamlFormatMode formatMode)
 302   {
 303      // Sanity!
 304      AssertFatal(pSimObject != NULL, "Cannot write a NULL object.");
 305
 306      // Compile nodes.
 307      TamlWriteNode* pRootNode = compileObject(pSimObject);
 308
 309      // Format appropriately.
 310      switch (formatMode)
 311      {
 312         /// Xml.
 313      case XmlFormat:
 314      {
 315         // Create writer.
 316         TamlXmlWriter writer(this);
 317         // Write.
 318         return writer.write(stream, pRootNode);
 319      }
 320
 321      /// Binary.
 322      case BinaryFormat:
 323      {
 324         // Create writer.
 325         TamlBinaryWriter writer(this);
 326
 327         // Write.
 328         return writer.write(stream, pRootNode, mBinaryCompression);
 329      }
 330
 331      /// JSON.
 332      case JSONFormat:
 333      {
 334         // Create writer.
 335         //TamlJSONWriter writer( this );
 336
 337         // Write.
 338         //return writer.write( stream, pRootNode );
 339         return NULL;
 340      }
 341
 342      /// Invalid.
 343      case InvalidFormat:
 344      {
 345         // Warn.
 346         Con::warnf("Taml::write() - Cannot write, invalid format.");
 347         return false;
 348      }
 349      }
 350
 351      // Warn.
 352      Con::warnf("Taml::write() - Unknown format.");
 353      return false;
 354   }
 355
 356   //-----------------------------------------------------------------------------
 357
 358   SimObject* Taml::read(FileStream& stream, const TamlFormatMode formatMode)
 359   {
 360      // Format appropriately.
 361      switch (formatMode)
 362      {
 363         /// Xml.
 364      case XmlFormat:
 365      {
 366         // Create reader.
 367         TamlXmlReader reader(this);
 368
 369         // Read.
 370         return reader.read(stream);
 371      }
 372
 373      /// Binary.
 374      case BinaryFormat:
 375      {
 376         // Create reader.
 377         TamlBinaryReader reader(this);
 378
 379         // Read.
 380         return reader.read(stream);
 381      }
 382
 383      /// JSON.
 384      case JSONFormat:
 385      {
 386         // Create reader.
 387         //TamlJSONReader reader( this );
 388
 389         // Read.
 390         //return reader.read( stream );
 391         return NULL;
 392      }
 393
 394      /// Invalid.
 395      case InvalidFormat:
 396      {
 397         // Warn.
 398         Con::warnf("Taml::read() - Cannot read, invalid format.");
 399         return NULL;
 400      }
 401      }
 402
 403      // Warn.
 404      Con::warnf("Taml::read() - Unknown format.");
 405      return NULL;
 406   }
 407
 408   //-----------------------------------------------------------------------------
 409
 410   bool Taml::parse(const char* pFilename, TamlVisitor& visitor)
 411   {
 412      // Debug Profiling.
 413      PROFILE_SCOPE(Taml_Parse);
 414
 415      // Sanity!
 416      AssertFatal(pFilename != NULL, "Taml::parse() - Cannot parse a NULL filename.");
 417
 418      // Fetch format mode.
 419      const TamlFormatMode formatMode = getFileAutoFormatMode(pFilename);
 420
 421      // Handle format mode appropriately.
 422      switch (formatMode)
 423      {
 424      case XmlFormat:
 425      {
 426         // Parse with the visitor.
 427         TamlXmlParser parser;
 428
 429         // Are property changes needed but not supported?
 430         if (visitor.wantsPropertyChanges() && !parser.canChangeProperty())
 431         {
 432            // Yes, so warn.
 433            Con::warnf("Taml::parse() - Cannot parse '%s' file-type for filename '%s' as a specified visitor requires property changes which are not supported by the parser.", getFormatModeDescription(formatMode), pFilename);
 434            return false;
 435         }
 436
 437         return parser.accept(pFilename, visitor);
 438      }
 439
 440      case JSONFormat:
 441      {
 442         // Parse with the visitor.
 443         /*TamlJSONParser parser;
 444
 445         // Are property changes needed but not supported?
 446         if ( visitor.wantsPropertyChanges() && !parser.canChangeProperty() )
 447         {
 448         // Yes, so warn.
 449         Con::warnf( "Taml::parse() - Cannot parse '%s' file-type for filename '%s' as a specified visitor requires property changes which are not supported by the parser.", getFormatModeDescription(formatMode), pFilename );
 450         return false;
 451         }
 452
 453         return parser.accept( pFilename, visitor );       */
 454         return false;
 455      }
 456
 457      case BinaryFormat:
 458      default:
 459         break;
 460      }
 461
 462      // Warn.
 463      Con::warnf("Taml::parse() - Cannot parse '%s' file-type for filename '%s' as a required parser is not available.", getFormatModeDescription(formatMode), pFilename);
 464      return false;
 465   }
 466
 467   //-----------------------------------------------------------------------------
 468
 469   void Taml::resetCompilation(void)
 470   {
 471      // Debug Profiling.
 472      PROFILE_SCOPE(Taml_ResetCompilation);
 473
 474      // Clear compiled nodes.
 475      for (typeNodeVector::iterator itr = mCompiledNodes.begin(); itr != mCompiledNodes.end(); ++itr)
 476      {
 477         // Fetch node.
 478         TamlWriteNode* pNode = (*itr);
 479
 480         // Reset node.
 481         pNode->resetNode();
 482
 483         // Delete node.
 484         delete pNode;
 485      }
 486      mCompiledNodes.clear();
 487
 488      // Clear compiled objects.
 489      mCompiledObjects.clear();
 490
 491      // Reset master node Id.
 492      mMasterNodeId = 0;
 493   }
 494
 495   //-----------------------------------------------------------------------------
 496
 497   Taml::TamlFormatMode Taml::getFileAutoFormatMode(const char* pFilename)
 498   {
 499      // Sanity!
 500      AssertFatal(pFilename != NULL, "Taml::getFileAutoFormatMode() - Cannot auto-format using a NULL filename.");
 501
 502      // Is auto-format active?
 503      if (mAutoFormat)
 504      {
 505         // Yes, so fetch the extension lengths.
 506         const U32 xmlExtensionLength = dStrlen(mAutoFormatXmlExtension);
 507         const U32 binaryExtensionLength = dStrlen(mAutoFormatBinaryExtension);
 508         const U32 jsonExtensionLength = dStrlen(mAutoFormatJSONExtension);
 509
 510         // Fetch filename length.
 511         const U32 filenameLength = dStrlen(pFilename);
 512
 513         // Fetch end of filename,
 514         const char* pEndOfFilename = pFilename + filenameLength;
 515
 516         // Check for the XML format.
 517         if (xmlExtensionLength <= filenameLength && dStricmp(pEndOfFilename - xmlExtensionLength, mAutoFormatXmlExtension) == 0)
 518            return Taml::XmlFormat;
 519
 520         // Check for the Binary format.
 521         if (binaryExtensionLength <= filenameLength && dStricmp(pEndOfFilename - xmlExtensionLength, mAutoFormatBinaryExtension) == 0)
 522            return Taml::BinaryFormat;
 523
 524         // Check for the XML format.
 525         if (jsonExtensionLength <= filenameLength && dStricmp(pEndOfFilename - jsonExtensionLength, mAutoFormatJSONExtension) == 0)
 526            return Taml::JSONFormat;
 527      }
 528
 529      // Use the explicitly specified format mode.
 530      return mFormatMode;
 531   }
 532
 533   //-----------------------------------------------------------------------------
 534
 535   TamlWriteNode* Taml::compileObject(SimObject* pSimObject, const bool forceId)
 536   {
 537      // Debug Profiling.
 538      PROFILE_SCOPE(Taml_CompileObject);
 539
 540      // Sanity!
 541      AssertFatal(pSimObject != NULL, "Taml::compileObject() - Cannot compile a NULL object.");
 542
 543      // Fetch object Id.
 544      const SimObjectId objectId = pSimObject->getId();
 545
 546      // Find a previously compiled node.
 547      typeCompiledHash::Iterator compiledItr = mCompiledObjects.find(objectId);
 548
 549      // Have we already compiled this?
 550      if (compiledItr != mCompiledObjects.end())
 551      {
 552         // Yes, so sanity!
 553         AssertFatal(mCompiledNodes.size() != 0, "Taml::compileObject() - Found a compiled node at the root.");
 554
 555         // Yes, so fetch node.
 556         TamlWriteNode* compiledNode = compiledItr->value;
 557
 558         // Is a reference Id already present?
 559         if (compiledNode->mRefId == 0)
 560         {
 561            // No, so allocate one.
 562            compiledNode->mRefId = ++mMasterNodeId;
 563         }
 564
 565         // Create write node.
 566         TamlWriteNode* pNewNode = new TamlWriteNode();
 567         pNewNode->set(pSimObject);
 568
 569         // Set reference node.
 570         pNewNode->mRefToNode = compiledNode;
 571
 572         // Push new node.
 573         mCompiledNodes.push_back(pNewNode);
 574
 575         return pNewNode;
 576      }
 577
 578      // No, so create write node.
 579      TamlWriteNode* pNewNode = new TamlWriteNode();
 580      pNewNode->set(pSimObject);
 581
 582      // Is an Id being forced for this object?
 583      if (forceId)
 584      {
 585         // Yes, so allocate one.
 586         pNewNode->mRefId = ++mMasterNodeId;
 587      }
 588
 589      // Push new node.
 590      mCompiledNodes.push_back(pNewNode);
 591
 592      // Insert compiled object.
 593      mCompiledObjects.insertUnique(objectId, pNewNode);
 594
 595      // Are there any Taml callbacks?
 596      if (pNewNode->mpTamlCallbacks != NULL)
 597      {
 598         // Yes, so call it.
 599         tamlPreWrite(pNewNode->mpTamlCallbacks);
 600      }
 601
 602      // Compile static and dynamic fields.
 603      compileStaticFields(pNewNode);
 604      compileDynamicFields(pNewNode);
 605
 606      // Compile children.
 607      compileChildren(pNewNode);
 608
 609      // Compile custom state.
 610      compileCustomState(pNewNode);
 611
 612      // Are there any Taml callbacks?
 613      if (pNewNode->mpTamlCallbacks != NULL)
 614      {
 615         // Yes, so call it.
 616         tamlPostWrite(pNewNode->mpTamlCallbacks);
 617      }
 618
 619      return pNewNode;
 620   }
 621
 622   //-----------------------------------------------------------------------------
 623
 624   void Taml::compileStaticFields(TamlWriteNode* pTamlWriteNode)
 625   {
 626      // Debug Profiling.
 627      PROFILE_SCOPE(Taml_CompileStaticFields);
 628
 629      // Sanity!
 630      AssertFatal(pTamlWriteNode != NULL, "Cannot compile static fields on a NULL node.");
 631      AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile static fields on a node with no object.");
 632
 633      // Fetch object.
 634      SimObject* pSimObject = pTamlWriteNode->mpSimObject;
 635
 636      // Fetch field list.
 637      const AbstractClassRep::FieldList& fieldList = pSimObject->getFieldList();
 638
 639      // Fetch field count.
 640      const U32 fieldCount = fieldList.size();
 641
 642      // Iterate fields.
 643      U8 arrayDepth = 0;
 644      TamlCustomNode* currentArrayNode;
 645      for (U32 index = 0; index < fieldCount; ++index)
 646      {
 647         // Fetch field.
 648         const AbstractClassRep::Field* pField = &fieldList[index];
 649
 650         // Ignore if field not appropriate.
 651         if (pField->type == AbstractClassRep::DeprecatedFieldType ||
 652            pField->type == AbstractClassRep::StartGroupFieldType ||
 653            pField->type == AbstractClassRep::EndGroupFieldType)
 654            continue;
 655
 656         if (pField->type == AbstractClassRep::StartArrayFieldType)
 657         {
 658            TamlCustomNodes& pCustomNodes = pTamlWriteNode->mCustomNodes;
 659            currentArrayNode = pCustomNodes.addNode(pField->pGroupname);
 660            for (U16 idx = 0; idx < pField->elementCount; idx++)
 661               currentArrayNode->addNode(pField->pFieldname);
 662            arrayDepth++;
 663            continue;
 664         }
 665
 666         if (pField->type == AbstractClassRep::EndArrayFieldType)
 667         {
 668            arrayDepth--;
 669            continue;
 670         }
 671
 672         if (arrayDepth == 0 && pField->elementCount > 1)
 673         {
 674            TamlCustomNodes& pCustomNodes = pTamlWriteNode->mCustomNodes;
 675            char* niceFieldName = const_cast<char *>(pField->pFieldname);
 676            niceFieldName[0] = dToupper(niceFieldName[0]);
 677            String str_niceFieldName = String(niceFieldName);
 678            currentArrayNode = pCustomNodes.addNode(str_niceFieldName + "s");
 679            for (U16 idx = 0; idx < pField->elementCount; idx++)
 680               currentArrayNode->addNode(str_niceFieldName);
 681         }
 682
 683         // Fetch fieldname.
 684         StringTableEntry fieldName = StringTable->insert(pField->pFieldname);
 685
 686         // Fetch element count.
 687         const U32 elementCount = pField->elementCount;
 688
 689         // Skip if the field should not be written.
 690         // For now, we only deal with non-array fields.
 691         if (elementCount == 1 &&
 692            pField->setDataFn != NULL &&
 693            (!getWriteDefaults() && pField->writeDataFn(pSimObject, fieldName) == false))
 694            continue;
 695
 696         // Iterate elements.
 697         for (U32 elementIndex = 0; elementIndex < elementCount; ++elementIndex)
 698         {
 699            char indexBuffer[8];
 700            dSprintf(indexBuffer, 8, "%d", elementIndex);
 701
 702            // Fetch object field value.
 703            const char* pFieldValue = pSimObject->getPrefixedDataField(fieldName, indexBuffer);
 704
 705            if (!pFieldValue)
 706               pFieldValue = StringTable->EmptyString();
 707
 708            if (pField->type == TypeBool)
 709               pFieldValue = dAtob(pFieldValue) ? "true" : "false";
 710
 711            U32 nBufferSize = dStrlen(pFieldValue) + 1;
 712            FrameTemp<char> valueCopy(nBufferSize);
 713            dStrcpy((char *)valueCopy, pFieldValue, nBufferSize);
 714
 715            // Skip if field should not be written.
 716            if (!pSimObject->writeField(fieldName, valueCopy))
 717               continue;
 718
 719            // Reassign field value.
 720            pFieldValue = valueCopy;
 721
 722            // Detect and collapse relative path information
 723            char fnBuf[1024];
 724            if ((S32)pField->type == TypeFilename)
 725            {
 726               Con::collapseScriptFilename(fnBuf, 1024, pFieldValue);
 727               pFieldValue = fnBuf;
 728            }
 729
 730            // Save field/value.
 731            if (arrayDepth > 0 || pField->elementCount > 1)
 732               currentArrayNode->getChildren()[elementIndex]->addField(fieldName, pFieldValue);
 733            else
 734            {
 735               TamlWriteNode::FieldValuePair* pFieldValuePair = new TamlWriteNode::FieldValuePair(fieldName, pFieldValue);
 736               pTamlWriteNode->mFields.push_back(pFieldValuePair);
 737            }
 738         }
 739      }
 740   }
 741
 742   //-----------------------------------------------------------------------------
 743
 744   static S32 QSORT_CALLBACK compareFieldEntries(const void* a, const void* b)
 745   {
 746      // Debug Profiling.
 747      PROFILE_SCOPE(Taml_CompareFieldEntries);
 748
 749      SimFieldDictionary::Entry *fa = *((SimFieldDictionary::Entry **)a);
 750      SimFieldDictionary::Entry *fb = *((SimFieldDictionary::Entry **)b);
 751      return dStricmp(fa->slotName, fb->slotName);
 752   }
 753
 754   //-----------------------------------------------------------------------------
 755
 756   void Taml::compileDynamicFields(TamlWriteNode* pTamlWriteNode)
 757   {
 758      // Debug Profiling.
 759      PROFILE_SCOPE(Taml_CompileDynamicFields);
 760
 761      // Sanity!
 762      AssertFatal(pTamlWriteNode != NULL, "Cannot compile dynamic fields on a NULL node.");
 763      AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile dynamic fields on a node with no object.");
 764
 765      // Fetch object.
 766      SimObject* pSimObject = pTamlWriteNode->mpSimObject;
 767
 768      // Fetch field dictionary.
 769      SimFieldDictionary* pFieldDictionary = pSimObject->getFieldDictionary();
 770
 771      // Ignore if not writing dynamic fields.
 772      if (!pFieldDictionary || !pSimObject->getCanSaveDynamicFields())
 773         return;
 774
 775      // Fetch field list.
 776      const AbstractClassRep::FieldList& fieldList = pSimObject->getFieldList();
 777
 778      // Fetch field count.
 779      const U32 fieldCount = fieldList.size();
 780
 781      Vector<SimFieldDictionary::Entry*> dynamicFieldList(__FILE__, __LINE__);
 782
 783      // Ensure the dynamic field doesn't conflict with static field.
 784      for (U32 hashIndex = 0; hashIndex < SimFieldDictionary::HashTableSize; ++hashIndex)
 785      {
 786         for (SimFieldDictionary::Entry* pEntry = pFieldDictionary->mHashTable[hashIndex]; pEntry; pEntry = pEntry->next)
 787         {
 788            // Iterate static fields.
 789            U32 fieldIndex;
 790            for (fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex)
 791            {
 792               if (fieldList[fieldIndex].pFieldname == pEntry->slotName)
 793                  break;
 794            }
 795
 796            // Skip if found.
 797            if (fieldIndex != (U32)fieldList.size())
 798               continue;
 799
 800            // Skip if not writing field.
 801            if (!pSimObject->writeField(pEntry->slotName, pEntry->value))
 802               continue;
 803
 804            dynamicFieldList.push_back(pEntry);
 805         }
 806      }
 807
 808      // Sort Entries to prevent version control conflicts
 809      if (dynamicFieldList.size() > 1)
 810         dQsort(dynamicFieldList.address(), dynamicFieldList.size(), sizeof(SimFieldDictionary::Entry*), compareFieldEntries);
 811
 812      // Save the fields.
 813      for (Vector<SimFieldDictionary::Entry*>::iterator entryItr = dynamicFieldList.begin(); entryItr != dynamicFieldList.end(); ++entryItr)
 814      {
 815         // Fetch entry.
 816         SimFieldDictionary::Entry* pEntry = *entryItr;
 817
 818         // Save field/value.
 819         TamlWriteNode::FieldValuePair*  pFieldValuePair = new TamlWriteNode::FieldValuePair(pEntry->slotName, pEntry->value);
 820         pTamlWriteNode->mFields.push_back(pFieldValuePair);
 821      }
 822   }
 823
 824   //-----------------------------------------------------------------------------
 825
 826   void Taml::compileChildren(TamlWriteNode* pTamlWriteNode)
 827   {
 828      // Debug Profiling.
 829      PROFILE_SCOPE(Taml_CompileChildren);
 830
 831      // Sanity!
 832      AssertFatal(pTamlWriteNode != NULL, "Cannot compile children on a NULL node.");
 833      AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile children on a node with no object.");
 834
 835      // Fetch object.
 836      SimObject* pSimObject = pTamlWriteNode->mpSimObject;
 837
 838      // Fetch the Taml children.
 839      TamlChildren* pChildren = dynamic_cast<TamlChildren*>(pSimObject);
 840
 841      // Finish if object does not contain Taml children.
 842      if (pChildren == NULL || pChildren->getTamlChildCount() == 0)
 843         return;
 844
 845      // Create children vector.
 846      pTamlWriteNode->mChildren = new typeNodeVector();
 847
 848      // Fetch the child count.
 849      const U32 childCount = pChildren->getTamlChildCount();
 850
 851      // Iterate children.
 852      for (U32 childIndex = 0; childIndex < childCount; childIndex++)
 853      {
 854         // Compile object.
 855         TamlWriteNode* pChildTamlWriteNode = compileObject(pChildren->getTamlChild(childIndex));
 856
 857         // Save node.
 858         pTamlWriteNode->mChildren->push_back(pChildTamlWriteNode);
 859      }
 860   }
 861
 862   //-----------------------------------------------------------------------------
 863
 864   void Taml::compileCustomState(TamlWriteNode* pTamlWriteNode)
 865   {
 866      // Debug Profiling.
 867      PROFILE_SCOPE(Taml_CompileCustomProperties);
 868
 869      // Sanity!
 870      AssertFatal(pTamlWriteNode != NULL, "Cannot compile custom state on a NULL node.");
 871      AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile custom state on a node with no object.");
 872
 873      // Fetch the custom node on the write node.
 874      TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
 875
 876      // Are there any Taml callbacks?
 877      if (pTamlWriteNode->mpTamlCallbacks != NULL)
 878      {
 879         // Yes, so call it.
 880         tamlCustomWrite(pTamlWriteNode->mpTamlCallbacks, customNodes);
 881      }
 882
 883      // Fetch custom nodes.
 884      const TamlCustomNodeVector& nodes = customNodes.getNodes();
 885
 886      // Finish if no custom nodes to process.
 887      if (nodes.size() == 0)
 888         return;
 889
 890      // Iterate custom properties.
 891      for (TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr)
 892      {
 893         // Fetch the custom node.
 894         TamlCustomNode* pCustomNode = *customNodesItr;
 895
 896         // Compile custom node state.
 897         compileCustomNodeState(pCustomNode);
 898      }
 899   }
 900
 901   //-----------------------------------------------------------------------------
 902
 903   void Taml::compileCustomNodeState(TamlCustomNode* pCustomNode)
 904   {
 905      // Sanity!
 906      AssertFatal(pCustomNode != NULL, "Taml: Cannot compile NULL custom node state.");
 907
 908      // Fetch children.
 909      const TamlCustomNodeVector& children = pCustomNode->getChildren();
 910
 911      // Fetch proxy object.
 912      SimObject* pProxyObject = pCustomNode->getProxyObject<SimObject>(false);
 913
 914      // Do we have a proxy object?
 915      if (pProxyObject != NULL)
 916      {
 917         // Yes, so sanity!
 918         AssertFatal(children.size() == 0, "Taml: Cannot compile a proxy object on a custom node that has children.");
 919
 920         // Yes, so compile it.
 921         // NOTE: We force an Id for custom compiled objects so we guarantee an Id.  The reason for this is fairly
 922         // weak in that the XML parser currently has no way of distinguishing between a compiled object node
 923         // 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.
 924         pCustomNode->setWriteNode(compileObject(pProxyObject, true));
 925         return;
 926      }
 927
 928      // Finish if no children.
 929      if (children.size() == 0)
 930         return;
 931
 932      // Iterate children.
 933      for (TamlCustomNodeVector::const_iterator childItr = children.begin(); childItr != children.end(); ++childItr)
 934      {
 935         // Fetch shape node.
 936         TamlCustomNode* pChildNode = *childItr;
 937
 938         // Compile the child.
 939         compileCustomNodeState(pChildNode);
 940      }
 941   }
 942
 943   //-----------------------------------------------------------------------------
 944
 945   SimObject* Taml::createType(StringTableEntry typeName, const Taml* pTaml, const char* pProgenitorSuffix)
 946   {
 947      // Debug Profiling.
 948      PROFILE_SCOPE(Taml_CreateType);
 949
 950      typedef HashTable<StringTableEntry, AbstractClassRep*> typeClassHash;
 951      static typeClassHash mClassMap;
 952
 953      // Sanity!
 954      AssertFatal(typeName != NULL, "Taml: Type cannot be NULL");
 955
 956      // Find type.
 957      typeClassHash::Iterator typeItr = mClassMap.find(typeName);
 958
 959      // Found type?
 960      if (typeItr == mClassMap.end())
 961      {
 962         // No, so find type.
 963         AbstractClassRep* pClassRep = AbstractClassRep::getClassList();
 964         while (pClassRep)
 965         {
 966            // Is this the type?
 967            if (dStricmp(pClassRep->getClassName(), typeName) == 0)
 968            {
 969               // Yes, so insert it.
 970               typeItr = mClassMap.insertUnique(typeName, pClassRep);
 971               break;
 972            }
 973
 974            // Next type.
 975            pClassRep = pClassRep->getNextClass();
 976         }
 977
 978         // Did we find the type?
 979         if (typeItr == mClassMap.end())
 980         {
 981            // No, so warn and fail.
 982            Con::warnf("Taml: Failed to create type '%s' as such a registered type could not be found.", typeName);
 983            return NULL;
 984         }
 985      }
 986
 987      // Create the object.
 988      ConsoleObject* pConsoleObject = typeItr->value->create();
 989
 990      // NOTE: It is important that we don't register the object here as many objects rely on the fact that
 991      // fields are set prior to the object being registered.  Registering here will invalid those assumptions.
 992
 993      // Fetch the SimObject.
 994      SimObject* pSimObject = dynamic_cast<SimObject*>(pConsoleObject);
 995
 996      // Was it a SimObject?
 997      if (pSimObject == NULL)
 998      {
 999         // No, so warn.
1000         Con::warnf("Taml: Failed to create type '%s' as it is not a SimObject.", typeName);
1001
1002         // Destroy object and fail.
1003         delete pConsoleObject;
1004         return NULL;
1005      }
1006
1007      // Are we updating the file-progenitor?
1008      if (pTaml->getProgenitorUpdate())
1009      {
1010         // Yes, so do we have a progenitor suffix?
1011         if (pProgenitorSuffix == NULL)
1012         {
1013            // No, so just set it to the progenitor file.
1014            pSimObject->setProgenitorFile(pTaml->getFilePathBuffer());
1015         }
1016         else
1017         {
1018            // Yes, so format the progenitor buffer.
1019            char progenitorBuffer[2048];
1020            dSprintf(progenitorBuffer, sizeof(progenitorBuffer), "%s,%s", pTaml->getFilePathBuffer(), pProgenitorSuffix);
1021
1022            // Set the progenitor file.
1023            pSimObject->setProgenitorFile(progenitorBuffer);
1024         }
1025      }
1026
1027      return pSimObject;
1028   }
1029
1030   //-----------------------------------------------------------------------------
1031
1032   bool Taml::generateTamlSchema()
1033   {
1034      // Fetch any TAML Schema file reference.
1035      const char* pTamlSchemaFile = Con::getVariable(TAML_SCHEMA_VARIABLE);
1036
1037      // Do we have a schema file reference?
1038      if (pTamlSchemaFile == NULL || *pTamlSchemaFile == 0)
1039      {
1040         // No, so warn.
1041         Con::warnf("Taml::generateTamlSchema() - Cannot write a TAML schema as no schema variable is set ('%s').", TAML_SCHEMA_VARIABLE);
1042         return false;
1043      }
1044
1045      // Expand the file-name into the file-path buffer.
1046      char filePathBuffer[1024];
1047      Con::expandToolScriptFilename(filePathBuffer, sizeof(filePathBuffer), pTamlSchemaFile);
1048
1049      FileStream stream;
1050
1051      // File opened?
1052      /*if ( !stream.open( filePathBuffer, Torque::FS::File::Write ) )
1053      {
1054      // No, so warn.
1055      Con::warnf("Taml::GenerateTamlSchema() - Could not open filename '%s' for write.", filePathBuffer );
1056      return false;
1057      }*/
1058
1059      // Create document.
1060      TiXmlDocument schemaDocument;
1061
1062      // Add declaration.
1063      TiXmlDeclaration schemaDeclaration("1.0", "iso-8859-1", "no");
1064      schemaDocument.InsertEndChild(schemaDeclaration);
1065
1066      // Add schema element.
1067      TiXmlElement* pSchemaElement = new TiXmlElement("xs:schema");
1068      pSchemaElement->SetAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema");
1069      schemaDocument.LinkEndChild(pSchemaElement);
1070
1071      // Fetch class-rep root.
1072      AbstractClassRep* pRootType = AbstractClassRep::getClassList();
1073
1074      // Fetch SimObject class rep.
1075      AbstractClassRep* pSimObjectType = AbstractClassRep::findClassRep("SimObject");
1076      // Sanity!
1077      AssertFatal(pSimObjectType != NULL, "Taml::GenerateTamlSchema() - Could not find SimObject class rep.");
1078
1079      // Reset scratch state.
1080      char buffer[1024];
1081      HashTable<AbstractClassRep*, StringTableEntry> childGroups;
1082
1083      // *************************************************************
1084      // Generate console type elements.
1085      // *************************************************************
1086
1087      // Vector2.
1088      TiXmlComment* pVector2Comment = new TiXmlComment("Vector2 Console Type");
1089      pSchemaElement->LinkEndChild(pVector2Comment);
1090      TiXmlElement* pVector2TypeElement = new TiXmlElement("xs:simpleType");
1091      pVector2TypeElement->SetAttribute("name", "Vector2_ConsoleType");
1092      pSchemaElement->LinkEndChild(pVector2TypeElement);
1093      TiXmlElement* pVector2ElementA = new TiXmlElement("xs:restriction");
1094      pVector2ElementA->SetAttribute("base", "xs:string");
1095      pVector2TypeElement->LinkEndChild(pVector2ElementA);
1096      TiXmlElement* pVector2ElementB = new TiXmlElement("xs:pattern");
1097      pVector2ElementB->SetAttribute("value", "([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b");
1098      pVector2ElementA->LinkEndChild(pVector2ElementB);
1099
1100      // Point2F.
1101      TiXmlComment* pPoint2FComment = new TiXmlComment("Point2F Console Type");
1102      pSchemaElement->LinkEndChild(pPoint2FComment);
1103      TiXmlElement* pPoint2FTypeElement = new TiXmlElement("xs:simpleType");
1104      pPoint2FTypeElement->SetAttribute("name", "Point2F_ConsoleType");
1105      pSchemaElement->LinkEndChild(pPoint2FTypeElement);
1106      TiXmlElement* pPoint2FElementA = new TiXmlElement("xs:restriction");
1107      pPoint2FElementA->SetAttribute("base", "xs:string");
1108      pPoint2FTypeElement->LinkEndChild(pPoint2FElementA);
1109      TiXmlElement* pPoint2FElementB = new TiXmlElement("xs:pattern");
1110      pPoint2FElementB->SetAttribute("value", "([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b");
1111      pPoint2FElementA->LinkEndChild(pPoint2FElementB);
1112
1113      // Point2I.
1114      TiXmlComment* pPoint2IComment = new TiXmlComment("Point2I Console Type");
1115      pSchemaElement->LinkEndChild(pPoint2IComment);
1116      TiXmlElement* pPoint2ITypeElement = new TiXmlElement("xs:simpleType");
1117      pPoint2ITypeElement->SetAttribute("name", "Point2I_ConsoleType");
1118      pSchemaElement->LinkEndChild(pPoint2ITypeElement);
1119      TiXmlElement* pPoint2IElementA = new TiXmlElement("xs:restriction");
1120      pPoint2IElementA->SetAttribute("base", "xs:string");
1121      pPoint2ITypeElement->LinkEndChild(pPoint2IElementA);
1122      TiXmlElement* pPoint2IElementB = new TiXmlElement("xs:pattern");
1123      pPoint2IElementB->SetAttribute("value", "[-]?[0-9]* [-]?[0-9]*");
1124      pPoint2IElementA->LinkEndChild(pPoint2IElementB);
1125
1126      // b2AABB.
1127      TiXmlComment* pb2AABBComment = new TiXmlComment("b2AABB Console Type");
1128      pSchemaElement->LinkEndChild(pb2AABBComment);
1129      TiXmlElement* pb2AABBTypeElement = new TiXmlElement("xs:simpleType");
1130      pb2AABBTypeElement->SetAttribute("name", "b2AABB_ConsoleType");
1131      pSchemaElement->LinkEndChild(pb2AABBTypeElement);
1132      TiXmlElement* pb2AABBElementA = new TiXmlElement("xs:restriction");
1133      pb2AABBElementA->SetAttribute("base", "xs:string");
1134      pb2AABBTypeElement->LinkEndChild(pb2AABBElementA);
1135      TiXmlElement* pb2AABBElementB = new TiXmlElement("xs:pattern");
1136      pb2AABBElementB->SetAttribute("value", "([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b");
1137      pb2AABBElementA->LinkEndChild(pb2AABBElementB);
1138
1139      // RectI.
1140      TiXmlComment* pRectIComment = new TiXmlComment("RectI Console Type");
1141      pSchemaElement->LinkEndChild(pRectIComment);
1142      TiXmlElement* pRectITypeElement = new TiXmlElement("xs:simpleType");
1143      pRectITypeElement->SetAttribute("name", "RectI_ConsoleType");
1144      pSchemaElement->LinkEndChild(pRectITypeElement);
1145      TiXmlElement* pRectIElementA = new TiXmlElement("xs:restriction");
1146      pRectIElementA->SetAttribute("base", "xs:string");
1147      pRectITypeElement->LinkEndChild(pRectIElementA);
1148      TiXmlElement* pRectIElementB = new TiXmlElement("xs:pattern");
1149      pRectIElementB->SetAttribute("value", "[-]?[0-9]* [-]?[0-9]* [-]?[0-9]* [-]?[0-9]*");
1150      pRectIElementA->LinkEndChild(pRectIElementB);
1151
1152      // RectF.
1153      TiXmlComment* pRectFComment = new TiXmlComment("RectF Console Type");
1154      pSchemaElement->LinkEndChild(pRectFComment);
1155      TiXmlElement* pRectFTypeElement = new TiXmlElement("xs:simpleType");
1156      pRectFTypeElement->SetAttribute("name", "RectF_ConsoleType");
1157      pSchemaElement->LinkEndChild(pRectFTypeElement);
1158      TiXmlElement* pRectFElementA = new TiXmlElement("xs:restriction");
1159      pRectFElementA->SetAttribute("base", "xs:string");
1160      pRectFTypeElement->LinkEndChild(pRectFElementA);
1161      TiXmlElement* pRectFElementB = new TiXmlElement("xs:pattern");
1162      pRectFElementB->SetAttribute("value", "(\\b[-]?(b[0-9]+)?\\.)?[0-9]+\\b");
1163      pRectFElementA->LinkEndChild(pRectFElementB);
1164
1165      // AssetId.
1166      TiXmlComment* pAssetIdComment = new TiXmlComment("AssetId Console Type");
1167      pSchemaElement->LinkEndChild(pAssetIdComment);
1168      TiXmlElement* pAssetIdTypeElement = new TiXmlElement("xs:simpleType");
1169      pAssetIdTypeElement->SetAttribute("name", "AssetId_ConsoleType");
1170      pSchemaElement->LinkEndChild(pAssetIdTypeElement);
1171      TiXmlElement* pAssetIdElementA = new TiXmlElement("xs:restriction");
1172      pAssetIdElementA->SetAttribute("base", "xs:string");
1173      pAssetIdTypeElement->LinkEndChild(pAssetIdElementA);
1174      TiXmlElement* pAssetIdElementB = new TiXmlElement("xs:pattern");
1175      dSprintf(buffer, sizeof(buffer), "(%s)?\\b[a-zA-Z0-9]+\\b%s\\b[a-zA-Z0-9]+\\b", ASSET_ID_FIELD_PREFIX, ASSET_SCOPE_TOKEN);
1176      pAssetIdElementB->SetAttribute("value", buffer);
1177      pAssetIdElementA->LinkEndChild(pAssetIdElementB);
1178
1179      // Color Enums.
1180      TiXmlComment* pColorEnumsComment = new TiXmlComment("Color Enums");
1181      pSchemaElement->LinkEndChild(pColorEnumsComment);
1182      TiXmlElement* pColorEnumsTypeElement = new TiXmlElement("xs:simpleType");
1183      pColorEnumsTypeElement->SetAttribute("name", "Color_Enums");
1184      pSchemaElement->LinkEndChild(pColorEnumsTypeElement);
1185      TiXmlElement* pColorEnumsRestrictionElement = new TiXmlElement("xs:restriction");
1186      pColorEnumsRestrictionElement->SetAttribute("base", "xs:string");
1187      pColorEnumsTypeElement->LinkEndChild(pColorEnumsRestrictionElement);
1188      const S32 ColorEnumsCount = StockColor::getCount();
1189      for (S32 index = 0; index < ColorEnumsCount; ++index)
1190      {
1191         // Add enumeration element.
1192         TiXmlElement* pColorEnumsAttributeEnumerationElement = new TiXmlElement("xs:enumeration");
1193         pColorEnumsAttributeEnumerationElement->SetAttribute("value", StockColor::getColorItem(index)->getColorName());
1194         pColorEnumsRestrictionElement->LinkEndChild(pColorEnumsAttributeEnumerationElement);
1195      }
1196
1197      // LinearColorF.
1198      TiXmlComment* pColorFValuesComment = new TiXmlComment("LinearColorF Values");
1199      pSchemaElement->LinkEndChild(pColorFValuesComment);
1200      TiXmlElement* pColorFValuesTypeElement = new TiXmlElement("xs:simpleType");
1201      pColorFValuesTypeElement->SetAttribute("name", "ColorF_Values");
1202      pSchemaElement->LinkEndChild(pColorFValuesTypeElement);
1203      TiXmlElement* pColorFValuesElementA = new TiXmlElement("xs:restriction");
1204      pColorFValuesElementA->SetAttribute("base", "xs:string");
1205      pColorFValuesTypeElement->LinkEndChild(pColorFValuesElementA);
1206      TiXmlElement* pColorFValuesElementB = new TiXmlElement("xs:pattern");
1207      pColorFValuesElementB->SetAttribute("value", "([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b");
1208      pColorFValuesElementA->LinkEndChild(pColorFValuesElementB);
1209
1210      TiXmlComment* pColorFComment = new TiXmlComment("LinearColorF Console Type");
1211      pSchemaElement->LinkEndChild(pColorFComment);
1212      TiXmlElement* pColorFTypeElement = new TiXmlElement("xs:simpleType");
1213      pColorFTypeElement->SetAttribute("name", "ColorF_ConsoleType");
1214      pSchemaElement->LinkEndChild(pColorFTypeElement);
1215      TiXmlElement* pColorFUnionElement = new TiXmlElement("xs:union");
1216      pColorFUnionElement->SetAttribute("memberTypes", "ColorF_Values Color_Enums");
1217      pColorFTypeElement->LinkEndChild(pColorFUnionElement);
1218
1219      // ColorI.
1220      TiXmlComment* pColorIValuesComment = new TiXmlComment("ColorI Values");
1221      pSchemaElement->LinkEndChild(pColorIValuesComment);
1222      TiXmlElement* pColorIValuesTypeElement = new TiXmlElement("xs:simpleType");
1223      pColorIValuesTypeElement->SetAttribute("name", "ColorI_Values");
1224      pSchemaElement->LinkEndChild(pColorIValuesTypeElement);
1225      TiXmlElement* pColorIValuesElementA = new TiXmlElement("xs:restriction");
1226      pColorIValuesElementA->SetAttribute("base", "xs:string");
1227      pColorIValuesTypeElement->LinkEndChild(pColorIValuesElementA);
1228      TiXmlElement* pColorIValuesElementB = new TiXmlElement("xs:pattern");
1229      pColorIValuesElementB->SetAttribute("value", "[-]?[0-9]* [-]?[0-9]* [-]?[0-9]* [-]?[0-9]*");
1230      pColorIValuesElementA->LinkEndChild(pColorIValuesElementB);
1231
1232      TiXmlComment* pColorIComment = new TiXmlComment("ColorI Console Type");
1233      pSchemaElement->LinkEndChild(pColorIComment);
1234      TiXmlElement* pColorITypeElement = new TiXmlElement("xs:simpleType");
1235      pColorITypeElement->SetAttribute("name", "ColorI_ConsoleType");
1236      pSchemaElement->LinkEndChild(pColorITypeElement);
1237      TiXmlElement* pColorIUnionElement = new TiXmlElement("xs:union");
1238      pColorIUnionElement->SetAttribute("memberTypes", "ColorI_Values Color_Enums");
1239      pColorITypeElement->LinkEndChild(pColorIUnionElement);
1240
1241      // *************************************************************
1242      // Generate engine type elements.
1243      // *************************************************************
1244
1245      // Generate the engine type elements.
1246      TiXmlComment* tComment = new TiXmlComment("Type Elements");
1247      pSchemaElement->LinkEndChild(tComment);
1248      for (AbstractClassRep* pType = pRootType; pType != NULL; pType = pType->getNextClass())
1249      {
1250         // Add type.
1251         TiXmlElement* pTypeElement = new TiXmlElement("xs:element");
1252         pTypeElement->SetAttribute("name", pType->getClassName());
1253         dSprintf(buffer, sizeof(buffer), "%s_Type", pType->getClassName());
1254         pTypeElement->SetAttribute("type", buffer);
1255         pSchemaElement->LinkEndChild(pTypeElement);
1256      }
1257
1258      // *************************************************************
1259      // Generate the engine complex types.
1260      // *************************************************************
1261      for (AbstractClassRep* pType = pRootType; pType != NULL; pType = pType->getNextClass())
1262      {
1263         // Add complex type comment.
1264         dSprintf(buffer, sizeof(buffer), " %s Type ", pType->getClassName());
1265         TiXmlComment* ctComment = new TiXmlComment(buffer);
1266         pSchemaElement->LinkEndChild(ctComment);
1267
1268         // Add complex type.
1269         TiXmlElement* pComplexTypeElement = new TiXmlElement("xs:complexType");
1270         dSprintf(buffer, sizeof(buffer), "%s_Type", pType->getClassName());
1271         pComplexTypeElement->SetAttribute("name", buffer);
1272         pSchemaElement->LinkEndChild(pComplexTypeElement);
1273
1274         // Add sequence.
1275         TiXmlElement* pSequenceElement = new TiXmlElement("xs:sequence");
1276         pComplexTypeElement->LinkEndChild(pSequenceElement);
1277
1278         // Fetch container child class.
1279         AbstractClassRep* pContainerChildClass = pType->getContainerChildClass(true);
1280
1281         // Is the type allowed children?
1282         if (pContainerChildClass != NULL)
1283         {
1284            // Yes, so add choice element.
1285            TiXmlElement* pChoiceElement = new TiXmlElement("xs:choice");
1286            pChoiceElement->SetAttribute("minOccurs", 0);
1287            pChoiceElement->SetAttribute("maxOccurs", "unbounded");
1288            pSequenceElement->LinkEndChild(pChoiceElement);
1289
1290            // Find child group.
1291            HashTable<AbstractClassRep*, StringTableEntry>::Iterator childGroupItr = childGroups.find(pContainerChildClass);
1292
1293            // Does the group exist?
1294            if (childGroupItr == childGroups.end())
1295            {
1296               // No, so format group name.
1297               dSprintf(buffer, sizeof(buffer), "%s_ChildrenTypes", pContainerChildClass->getClassName());
1298
1299               // Insert into child group hash.
1300               childGroupItr = childGroups.insertUnique(pContainerChildClass, StringTable->insert(buffer));
1301
1302               // Add the group.
1303               TiXmlElement* pChildrenGroupElement = new TiXmlElement("xs:group");
1304               pChildrenGroupElement->SetAttribute("name", buffer);
1305               pSchemaElement->LinkEndChild(pChildrenGroupElement);
1306
1307               // Add choice element.
1308               TiXmlElement* pChildreGroupChoiceElement = new TiXmlElement("xs:choice");
1309               pChildrenGroupElement->LinkEndChild(pChildreGroupChoiceElement);
1310
1311               // Add choice members.
1312               for (AbstractClassRep* pChoiceType = pRootType; pChoiceType != NULL; pChoiceType = pChoiceType->getNextClass())
1313               {
1314                  // Skip if not derived from the container child class.
1315                  if (!pChoiceType->isClass(pContainerChildClass))
1316                     continue;
1317
1318                  // Add choice member.
1319                  TiXmlElement* pChildrenMemberElement = new TiXmlElement("xs:element");
1320                  pChildrenMemberElement->SetAttribute("name", pChoiceType->getClassName());
1321                  dSprintf(buffer, sizeof(buffer), "%s_Type", pChoiceType->getClassName());
1322                  pChildrenMemberElement->SetAttribute("type", buffer);
1323                  pChildreGroupChoiceElement->LinkEndChild(pChildrenMemberElement);
1324               }
1325
1326            }
1327
1328            // Reference the child group.
1329            TiXmlElement* pChoiceGroupReferenceElement = new TiXmlElement("xs:group");
1330            pChoiceGroupReferenceElement->SetAttribute("ref", childGroupItr->value);
1331            pChoiceGroupReferenceElement->SetAttribute("minOccurs", 0);
1332            pChoiceElement->LinkEndChild(pChoiceGroupReferenceElement);
1333         }
1334
1335         // Generate the custom Taml schema.
1336         for (AbstractClassRep* pCustomSchemaType = pType; pCustomSchemaType != NULL; pCustomSchemaType = pCustomSchemaType->getParentClass())
1337         {
1338            // Fetch the types custom TAML schema function.
1339            AbstractClassRep::WriteCustomTamlSchema customSchemaFn = pCustomSchemaType->getCustomTamlSchema();
1340
1341            // Skip if no function avilable.
1342            if (customSchemaFn == NULL)
1343               continue;
1344
1345            // Call schema generation function.
1346            customSchemaFn(pType, pSequenceElement);
1347         }
1348
1349         // Generate field attribute group.
1350         TiXmlElement* pFieldAttributeGroupElement = new TiXmlElement("xs:attributeGroup");
1351         dSprintf(buffer, sizeof(buffer), "%s_Fields", pType->getClassName());
1352         pFieldAttributeGroupElement->SetAttribute("name", buffer);
1353         pSchemaElement->LinkEndChild(pFieldAttributeGroupElement);
1354
1355         // Fetch field list.
1356         const AbstractClassRep::FieldList& fields = pType->mFieldList;
1357
1358         // Fetcj field count.
1359         const S32 fieldCount = fields.size();
1360
1361         // Iterate static fields (in reverse as most types are organized from the root-fields up).
1362         for (S32 index = fieldCount - 1; index > 0; --index)
1363         {
1364            // Fetch field.
1365            const AbstractClassRep::Field& field = fields[index];
1366
1367            // Skip if not a data field.
1368            if (field.type == AbstractClassRep::DeprecatedFieldType ||
1369               field.type == AbstractClassRep::StartGroupFieldType ||
1370               field.type == AbstractClassRep::EndGroupFieldType)
1371               continue;
1372
1373            // Skip if the field root is not this type.
1374            if (pType->findFieldRoot(field.pFieldname) != pType)
1375               continue;
1376
1377            // Add attribute element.
1378            TiXmlElement* pAttributeElement = new TiXmlElement("xs:attribute");
1379            pAttributeElement->SetAttribute("name", field.pFieldname);
1380
1381            // Handle the console type appropriately.
1382            const S32 fieldType = (S32)field.type;
1383
1384            /*
1385            // Is the field an enumeration?
1386            if ( fieldType == TypeEnum )
1387            {
1388            // Yes, so add attribute type.
1389            TiXmlElement* pAttributeSimpleTypeElement = new TiXmlElement( "xs:simpleType" );
1390            pAttributeElement->LinkEndChild( pAttributeSimpleTypeElement );
1391
1392            // Add restriction element.
1393            TiXmlElement* pAttributeRestrictionElement = new TiXmlElement( "xs:restriction" );
1394            pAttributeRestrictionElement->SetAttribute( "base", "xs:string" );
1395            pAttributeSimpleTypeElement->LinkEndChild( pAttributeRestrictionElement );
1396
1397            // Yes, so fetch enumeration count.
1398            const S32 enumCount = field.table->size;
1399
1400            // Iterate enumeration.
1401            for( S32 index = 0; index < enumCount; ++index )
1402            {
1403            // Add enumeration element.
1404            TiXmlElement* pAttributeEnumerationElement = new TiXmlElement( "xs:enumeration" );
1405            pAttributeEnumerationElement->SetAttribute( "value", field.table->table[index].label );
1406            pAttributeRestrictionElement->LinkEndChild( pAttributeEnumerationElement );
1407            }
1408            }
1409            else
1410            {*/
1411            // No, so assume it's a string type initially.
1412            const char* pFieldTypeDescription = "xs:string";
1413
1414            // Handle known types.
1415            if (fieldType == TypeF32)
1416            {
1417               pFieldTypeDescription = "xs:float";
1418            }
1419            else if (fieldType == TypeS8 || fieldType == TypeS32)
1420            {
1421               pFieldTypeDescription = "xs:int";
1422            }
1423            else if (fieldType == TypeBool || fieldType == TypeFlag)
1424            {
1425               pFieldTypeDescription = "xs:boolean";
1426            }
1427            else if (fieldType == TypePoint2F)
1428            {
1429               pFieldTypeDescription = "Point2F_ConsoleType";
1430            }
1431            else if (fieldType == TypePoint2I)
1432            {
1433               pFieldTypeDescription = "Point2I_ConsoleType";
1434            }
1435            else if (fieldType == TypeRectI)
1436            {
1437               pFieldTypeDescription = "RectI_ConsoleType";
1438            }
1439            else if (fieldType == TypeRectF)
1440            {
1441               pFieldTypeDescription = "RectF_ConsoleType";
1442            }
1443            else if (fieldType == TypeColorF)
1444            {
1445               pFieldTypeDescription = "ColorF_ConsoleType";
1446            }
1447            else if (fieldType == TypeColorI)
1448            {
1449               pFieldTypeDescription = "ColorI_ConsoleType";
1450            }
1451            else if (fieldType == TypeAssetId/* ||
1452                                             fieldType == TypeImageAssetPtr ||
1453                                             fieldType == TypeAnimationAssetPtr ||
1454                                             fieldType == TypeAudioAssetPtr*/)
1455            {
1456               pFieldTypeDescription = "AssetId_ConsoleType";
1457            }
1458
1459            // Set attribute type.
1460            pAttributeElement->SetAttribute("type", pFieldTypeDescription);
1461            //}
1462
1463            pAttributeElement->SetAttribute("use", "optional");
1464            pFieldAttributeGroupElement->LinkEndChild(pAttributeElement);
1465         }
1466
1467         // Is this the SimObject Type?
1468         if (pType == pSimObjectType)
1469         {
1470            // Yes, so add reserved Taml field attributes here...
1471
1472            // Add Taml "Name" attribute element.
1473            TiXmlElement* pNameAttributeElement = new TiXmlElement("xs:attribute");
1474            pNameAttributeElement->SetAttribute("name", tamlNamedObjectName);
1475            pNameAttributeElement->SetAttribute("type", "xs:normalizedString");
1476            pFieldAttributeGroupElement->LinkEndChild(pNameAttributeElement);
1477
1478            // Add Taml "TamlId" attribute element.
1479            TiXmlElement* pTamlIdAttributeElement = new TiXmlElement("xs:attribute");
1480            pTamlIdAttributeElement->SetAttribute("name", tamlRefIdName);
1481            pTamlIdAttributeElement->SetAttribute("type", "xs:nonNegativeInteger");
1482            pFieldAttributeGroupElement->LinkEndChild(pTamlIdAttributeElement);
1483
1484            // Add Taml "TamlRefId" attribute element.
1485            TiXmlElement* pTamlRefIdAttributeElement = new TiXmlElement("xs:attribute");
1486            pTamlRefIdAttributeElement->SetAttribute("name", tamlRefToIdName);
1487            pTamlRefIdAttributeElement->SetAttribute("type", "xs:nonNegativeInteger");
1488            pFieldAttributeGroupElement->LinkEndChild(pTamlRefIdAttributeElement);
1489         }
1490
1491         // Add attribute group types.
1492         for (AbstractClassRep* pAttributeGroupsType = pType; pAttributeGroupsType != NULL; pAttributeGroupsType = pAttributeGroupsType->getParentClass())
1493         {
1494            TiXmlElement* pFieldAttributeGroupRefElement = new TiXmlElement("xs:attributeGroup");
1495            dSprintf(buffer, sizeof(buffer), "%s_Fields", pAttributeGroupsType->getClassName());
1496            pFieldAttributeGroupRefElement->SetAttribute("ref", buffer);
1497            pComplexTypeElement->LinkEndChild(pFieldAttributeGroupRefElement);
1498         }
1499
1500         // Add "any" attribute element (dynamic fields).
1501         TiXmlElement* pAnyAttributeElement = new TiXmlElement("xs:anyAttribute");
1502         pAnyAttributeElement->SetAttribute("processContents", "skip");
1503         pComplexTypeElement->LinkEndChild(pAnyAttributeElement);
1504      }
1505
1506      // Write the schema document.
1507      schemaDocument.SaveFile(filePathBuffer);
1508
1509      // Close file.
1510      stream.close();
1511
1512      return true;
1513   }
1514
1515   //-----------------------------------------------------------------------------
1516
1517   void Taml::WriteUnrestrictedCustomTamlSchema(const char* pCustomNodeName, const AbstractClassRep* pClassRep, TiXmlElement* pParentElement)
1518   {
1519      // Sanity!
1520      AssertFatal(pCustomNodeName != NULL, "Taml::WriteDefaultCustomTamlSchema() - Node name cannot be NULL.");
1521      AssertFatal(pClassRep != NULL, "Taml::WriteDefaultCustomTamlSchema() - ClassRep cannot be NULL.");
1522      AssertFatal(pParentElement != NULL, "Taml::WriteDefaultCustomTamlSchema() - Parent Element cannot be NULL.");
1523
1524      char buffer[1024];
1525
1526      // Add custom type element.
1527      TiXmlElement* pCustomElement = new TiXmlElement("xs:element");
1528      dSprintf(buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), pCustomNodeName);
1529      pCustomElement->SetAttribute("name", buffer);
1530      pCustomElement->SetAttribute("minOccurs", 0);
1531      pCustomElement->SetAttribute("maxOccurs", 1);
1532      pParentElement->LinkEndChild(pCustomElement);
1533
1534      // Add complex type element.
1535      TiXmlElement* pComplexTypeElement = new TiXmlElement("xs:complexType");
1536      pCustomElement->LinkEndChild(pComplexTypeElement);
1537
1538      // Add choice element.
1539      TiXmlElement* pChoiceElement = new TiXmlElement("xs:choice");
1540      pChoiceElement->SetAttribute("minOccurs", 0);
1541      pChoiceElement->SetAttribute("maxOccurs", "unbounded");
1542      pComplexTypeElement->LinkEndChild(pChoiceElement);
1543
1544      // Add sequence element.
1545      TiXmlElement* pSequenceElement = new TiXmlElement("xs:sequence");
1546      pChoiceElement->LinkEndChild(pSequenceElement);
1547
1548      // Add "any" element.
1549      TiXmlElement* pAnyElement = new TiXmlElement("xs:any");
1550      pAnyElement->SetAttribute("processContents", "skip");
1551      pSequenceElement->LinkEndChild(pAnyElement);
1552   }
1553