//----------------------------------------------------------------------------- // 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 "taml.h" #ifndef _TAML_XMLWRITER_H_ #include "persistence/taml/xml/tamlXmlWriter.h" #endif #ifndef _TAML_XMLREADER_H_ #include "persistence/taml/xml/tamlXmlReader.h" #endif #ifndef _TAML_XMLPARSER_H_ #include "persistence/taml/xml/tamlXmlParser.h" #endif #ifndef _TAML_BINARYWRITER_H_ #include "persistence/taml/binary/tamlBinaryWriter.h" #endif #ifndef _TAML_BINARYREADER_H_ #include "persistence/taml/binary/tamlBinaryReader.h" #endif /*#ifndef _TAML_JSONWRITER_H_ #include "taml/json/tamlJSONWriter.h" #endif #ifndef _TAML_JSONREADER_H_ #include "taml/json/tamlJSONReader.h" #endif #ifndef _TAML_JSONPARSER_H_ #include "taml/json/tamlJSONParser.h" #endif*/ #ifndef _FRAMEALLOCATOR_H_ #include "core/frameAllocator.h" #endif #ifndef _SIMBASE_H_ #include "console/simBase.h" #endif #ifndef _MATHTYPES_H_ #include "math/mathTypes.h" #endif #ifndef _MPOINT2_H_ #include "math/mPoint2.h" #endif #ifndef _ASSET_BASE_H_ #include "assets/assetBase.h" #endif // Script bindings. #include "taml_ScriptBinding.h" // Debug Profiling. #include "platform/profiler.h" //----------------------------------------------------------------------------- IMPLEMENT_CONOBJECT(Taml); //----------------------------------------------------------------------------- StringTableEntry tamlRefIdName = StringTable->insert("TamlId"); StringTableEntry tamlRefToIdName = StringTable->insert("TamlRefId"); StringTableEntry tamlNamedObjectName = StringTable->insert("Name"); //----------------------------------------------------------------------------- typedef Taml::TamlFormatMode _TamlFormatMode; ImplementEnumType(_TamlFormatMode, "") { Taml::XmlFormat, "xml" }, { Taml::BinaryFormat, "binary" }//, //{ Taml::JSONFormat, "json" } EndImplementEnumType; //----------------------------------------------------------------------------- Taml::TamlFormatMode Taml::getFormatModeEnum(const char* label) { // Search for Mnemonic. for (U32 i = 0; i < (sizeof(__TamlFormatMode::_sEnums) / sizeof(EnumTable::Value)); i++) { if (dStricmp(__TamlFormatMode::_sEnumTable[i].getName(), label) == 0) return (TamlFormatMode)__TamlFormatMode::_sEnumTable[i].getInt(); } // Warn. Con::warnf("Taml::getFormatModeEnum() - Invalid format of '%s'.", label); return Taml::InvalidFormat; } //----------------------------------------------------------------------------- const char* Taml::getFormatModeDescription(const Taml::TamlFormatMode formatMode) { // Search for Mnemonic. for (U32 i = 0; i < (sizeof(__TamlFormatMode::_sEnums) / sizeof(EnumTable::Value)); i++) { if (__TamlFormatMode::_sEnumTable[i].getInt() == (S32)formatMode) return __TamlFormatMode::_sEnumTable[i].getName(); } // Warn. Con::warnf("Taml::getFormatModeDescription() - Invalid format mode."); return StringTable->EmptyString(); } //----------------------------------------------------------------------------- // 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. Taml::Taml() : mFormatMode(XmlFormat), mJSONStrict(true), mBinaryCompression(true), mWriteDefaults(false), mAutoFormatXmlExtension("taml"), mAutoFormat(true), mProgenitorUpdate(true), mAutoFormatBinaryExtension("baml"), mAutoFormatJSONExtension("json") { // Reset the file-path buffer. mFilePathBuffer[0] = 0; } //----------------------------------------------------------------------------- void Taml::initPersistFields() { // Call parent. Parent::initPersistFields(); addField("Format", TYPEID<_TamlFormatMode>(), Offset(mFormatMode, Taml), "The read/write format that should be used."); addField("JSONStrict", TypeBool, Offset(mBinaryCompression, Taml), "Whether to write JSON that is strictly compatible with RFC4627 or not.\n"); addField("BinaryCompression", TypeBool, Offset(mBinaryCompression, Taml), "Whether ZIP compression is used on binary formatting or not.\n"); addField("WriteDefaults", TypeBool, Offset(mWriteDefaults, Taml), "Whether to write static fields that are at their default or not.\n"); addField("ProgenitorUpdate", TypeBool, Offset(mProgenitorUpdate, Taml), "Whether to update each type instances file-progenitor or not.\n"); addField("AutoFormat", TypeBool, Offset(mAutoFormat, Taml), "Whether the format type is automatically determined by the filename extension or not.\n"); addField("AutoFormatXmlExtension", TypeString, Offset(mAutoFormatXmlExtension, Taml), "When using auto-format, this is the extension (end of filename) used to detect the XML format.\n"); addField("AutoFormatBinaryExtension", TypeString, Offset(mAutoFormatBinaryExtension, Taml), "When using auto-format, this is the extension (end of filename) used to detect the BINARY format.\n"); addField("AutoFormatJSONExtension", TypeString, Offset(mAutoFormatJSONExtension, Taml), "When using auto-format, this is the extension (end of filename) used to detect the JSON format.\n"); } //----------------------------------------------------------------------------- bool Taml::onAdd() { // Call parent. if (!Parent::onAdd()) return false; // Set JSON strict mode. mJSONStrict = Con::getBoolVariable(TAML_JSON_STRICT_VARIBLE, true); // Reset the compilation. resetCompilation(); return true; } //----------------------------------------------------------------------------- void Taml::onRemove() { // Reset the compilation. resetCompilation(); // Call parent. Parent::onRemove(); } //----------------------------------------------------------------------------- bool Taml::write(SimObject* pSimObject, const char* pFilename) { // Debug Profiling. PROFILE_SCOPE(Taml_Write); // Sanity! AssertFatal(pSimObject != NULL, "Cannot write a NULL object."); AssertFatal(pFilename != NULL, "Cannot write to a NULL filename."); // Expand the file-name into the file-path buffer. Con::expandToolScriptFilename(mFilePathBuffer, sizeof(mFilePathBuffer), pFilename); FileStream stream; // File opened? if (!stream.open(mFilePathBuffer, Torque::FS::File::Write)) { // No, so warn. Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", mFilePathBuffer); return false; } // Get the file auto-format mode. const TamlFormatMode formatMode = getFileAutoFormatMode(mFilePathBuffer); // Reset the compilation. resetCompilation(); // Write object. const bool status = write(stream, pSimObject, formatMode); // Close file. stream.close(); // Reset the compilation. resetCompilation(); return status; } //----------------------------------------------------------------------------- SimObject* Taml::read(const char* pFilename) { // Debug Profiling. PROFILE_SCOPE(Taml_Read); // Sanity! AssertFatal(pFilename != NULL, "Cannot read from a NULL filename."); // Expand the file-name into the file-path buffer. Con::expandToolScriptFilename(mFilePathBuffer, sizeof(mFilePathBuffer), pFilename); FileStream stream; // File opened? if (!stream.open(mFilePathBuffer, Torque::FS::File::Read)) { // No, so warn. Con::warnf("Taml::read() - Could not open filename '%s' for read.", mFilePathBuffer); return NULL; } // Get the file auto-format mode. const TamlFormatMode formatMode = getFileAutoFormatMode(mFilePathBuffer); // Reset the compilation. resetCompilation(); // Write object. SimObject* pSimObject = read(stream, formatMode); // Close file. stream.close(); // Reset the compilation. resetCompilation(); // Did we generate an object? if (pSimObject == NULL) { // No, so warn. Con::warnf("Taml::read() - Failed to load an object from the file '%s'.", mFilePathBuffer); } else { pSimObject->onPostAdd(); } return pSimObject; } //----------------------------------------------------------------------------- bool Taml::write(FileStream& stream, SimObject* pSimObject, const TamlFormatMode formatMode) { // Sanity! AssertFatal(pSimObject != NULL, "Cannot write a NULL object."); // Compile nodes. TamlWriteNode* pRootNode = compileObject(pSimObject); // Format appropriately. switch (formatMode) { /// Xml. case XmlFormat: { // Create writer. TamlXmlWriter writer(this); // Write. return writer.write(stream, pRootNode); } /// Binary. case BinaryFormat: { // Create writer. TamlBinaryWriter writer(this); // Write. return writer.write(stream, pRootNode, mBinaryCompression); } /// JSON. case JSONFormat: { // Create writer. //TamlJSONWriter writer( this ); // Write. //return writer.write( stream, pRootNode ); return NULL; } /// Invalid. case InvalidFormat: { // Warn. Con::warnf("Taml::write() - Cannot write, invalid format."); return false; } } // Warn. Con::warnf("Taml::write() - Unknown format."); return false; } //----------------------------------------------------------------------------- SimObject* Taml::read(FileStream& stream, const TamlFormatMode formatMode) { // Format appropriately. switch (formatMode) { /// Xml. case XmlFormat: { // Create reader. TamlXmlReader reader(this); // Read. return reader.read(stream); } /// Binary. case BinaryFormat: { // Create reader. TamlBinaryReader reader(this); // Read. return reader.read(stream); } /// JSON. case JSONFormat: { // Create reader. //TamlJSONReader reader( this ); // Read. //return reader.read( stream ); return NULL; } /// Invalid. case InvalidFormat: { // Warn. Con::warnf("Taml::read() - Cannot read, invalid format."); return NULL; } } // Warn. Con::warnf("Taml::read() - Unknown format."); return NULL; } //----------------------------------------------------------------------------- bool Taml::parse(const char* pFilename, TamlVisitor& visitor) { // Debug Profiling. PROFILE_SCOPE(Taml_Parse); // Sanity! AssertFatal(pFilename != NULL, "Taml::parse() - Cannot parse a NULL filename."); // Fetch format mode. const TamlFormatMode formatMode = getFileAutoFormatMode(pFilename); // Handle format mode appropriately. switch (formatMode) { case XmlFormat: { // Parse with the visitor. TamlXmlParser parser; // Are property changes needed but not supported? if (visitor.wantsPropertyChanges() && !parser.canChangeProperty()) { // Yes, so warn. 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); return false; } return parser.accept(pFilename, visitor); } case JSONFormat: { // Parse with the visitor. /*TamlJSONParser parser; // Are property changes needed but not supported? if ( visitor.wantsPropertyChanges() && !parser.canChangeProperty() ) { // Yes, so warn. 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 ); return false; } return parser.accept( pFilename, visitor ); */ return false; } case BinaryFormat: default: break; } // Warn. Con::warnf("Taml::parse() - Cannot parse '%s' file-type for filename '%s' as a required parser is not available.", getFormatModeDescription(formatMode), pFilename); return false; } //----------------------------------------------------------------------------- void Taml::resetCompilation(void) { // Debug Profiling. PROFILE_SCOPE(Taml_ResetCompilation); // Clear compiled nodes. for (typeNodeVector::iterator itr = mCompiledNodes.begin(); itr != mCompiledNodes.end(); ++itr) { // Fetch node. TamlWriteNode* pNode = (*itr); // Reset node. pNode->resetNode(); // Delete node. delete pNode; } mCompiledNodes.clear(); // Clear compiled objects. mCompiledObjects.clear(); // Reset master node Id. mMasterNodeId = 0; } //----------------------------------------------------------------------------- Taml::TamlFormatMode Taml::getFileAutoFormatMode(const char* pFilename) { // Sanity! AssertFatal(pFilename != NULL, "Taml::getFileAutoFormatMode() - Cannot auto-format using a NULL filename."); // Is auto-format active? if (mAutoFormat) { // Yes, so fetch the extension lengths. const U32 xmlExtensionLength = dStrlen(mAutoFormatXmlExtension); const U32 binaryExtensionLength = dStrlen(mAutoFormatBinaryExtension); const U32 jsonExtensionLength = dStrlen(mAutoFormatJSONExtension); // Fetch filename length. const U32 filenameLength = dStrlen(pFilename); // Fetch end of filename, const char* pEndOfFilename = pFilename + filenameLength; // Check for the XML format. if (xmlExtensionLength <= filenameLength && dStricmp(pEndOfFilename - xmlExtensionLength, mAutoFormatXmlExtension) == 0) return Taml::XmlFormat; // Check for the Binary format. if (binaryExtensionLength <= filenameLength && dStricmp(pEndOfFilename - xmlExtensionLength, mAutoFormatBinaryExtension) == 0) return Taml::BinaryFormat; // Check for the XML format. if (jsonExtensionLength <= filenameLength && dStricmp(pEndOfFilename - jsonExtensionLength, mAutoFormatJSONExtension) == 0) return Taml::JSONFormat; } // Use the explicitly specified format mode. return mFormatMode; } //----------------------------------------------------------------------------- TamlWriteNode* Taml::compileObject(SimObject* pSimObject, const bool forceId) { // Debug Profiling. PROFILE_SCOPE(Taml_CompileObject); // Sanity! AssertFatal(pSimObject != NULL, "Taml::compileObject() - Cannot compile a NULL object."); // Fetch object Id. const SimObjectId objectId = pSimObject->getId(); // Find a previously compiled node. typeCompiledHash::Iterator compiledItr = mCompiledObjects.find(objectId); // Have we already compiled this? if (compiledItr != mCompiledObjects.end()) { // Yes, so sanity! AssertFatal(mCompiledNodes.size() != 0, "Taml::compileObject() - Found a compiled node at the root."); // Yes, so fetch node. TamlWriteNode* compiledNode = compiledItr->value; // Is a reference Id already present? if (compiledNode->mRefId == 0) { // No, so allocate one. compiledNode->mRefId = ++mMasterNodeId; } // Create write node. TamlWriteNode* pNewNode = new TamlWriteNode(); pNewNode->set(pSimObject); // Set reference node. pNewNode->mRefToNode = compiledNode; // Push new node. mCompiledNodes.push_back(pNewNode); return pNewNode; } // No, so create write node. 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); // Insert compiled object. mCompiledObjects.insertUnique(objectId, pNewNode); // Are there any Taml callbacks? if (pNewNode->mpTamlCallbacks != NULL) { // Yes, so call it. tamlPreWrite(pNewNode->mpTamlCallbacks); } // Compile static and dynamic fields. compileStaticFields(pNewNode); compileDynamicFields(pNewNode); // Compile children. compileChildren(pNewNode); // Compile custom state. compileCustomState(pNewNode); // Are there any Taml callbacks? if (pNewNode->mpTamlCallbacks != NULL) { // Yes, so call it. tamlPostWrite(pNewNode->mpTamlCallbacks); } return pNewNode; } //----------------------------------------------------------------------------- void Taml::compileStaticFields(TamlWriteNode* pTamlWriteNode) { // Debug Profiling. PROFILE_SCOPE(Taml_CompileStaticFields); // Sanity! AssertFatal(pTamlWriteNode != NULL, "Cannot compile static fields on a NULL node."); AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile static fields on a node with no object."); // Fetch object. SimObject* pSimObject = pTamlWriteNode->mpSimObject; // Fetch field list. const AbstractClassRep::FieldList& fieldList = pSimObject->getFieldList(); // Fetch field count. const U32 fieldCount = fieldList.size(); // Iterate fields. U8 arrayDepth = 0; TamlCustomNode* currentArrayNode; for (U32 index = 0; index < fieldCount; ++index) { // Fetch field. const AbstractClassRep::Field* pField = &fieldList[index]; // Ignore if field not appropriate. if (pField->type == AbstractClassRep::DeprecatedFieldType || pField->type == AbstractClassRep::StartGroupFieldType || pField->type == AbstractClassRep::EndGroupFieldType) continue; if (pField->type == AbstractClassRep::StartArrayFieldType) { TamlCustomNodes& pCustomNodes = pTamlWriteNode->mCustomNodes; currentArrayNode = pCustomNodes.addNode(pField->pGroupname); for (U16 idx = 0; idx < pField->elementCount; idx++) currentArrayNode->addNode(pField->pFieldname); arrayDepth++; continue; } if (pField->type == AbstractClassRep::EndArrayFieldType) { arrayDepth--; continue; } if (arrayDepth == 0 && pField->elementCount > 1) { TamlCustomNodes& pCustomNodes = pTamlWriteNode->mCustomNodes; char* niceFieldName = const_cast(pField->pFieldname); niceFieldName[0] = dToupper(niceFieldName[0]); String str_niceFieldName = String(niceFieldName); currentArrayNode = pCustomNodes.addNode(str_niceFieldName + "s"); for (U16 idx = 0; idx < pField->elementCount; idx++) currentArrayNode->addNode(str_niceFieldName); } // Fetch fieldname. StringTableEntry fieldName = StringTable->insert(pField->pFieldname); // Fetch element count. const U32 elementCount = pField->elementCount; // Skip if the field should not be written. // For now, we only deal with non-array fields. if (elementCount == 1 && pField->setDataFn != NULL && (!getWriteDefaults() && pField->writeDataFn(pSimObject, fieldName) == false)) continue; // Iterate elements. for (U32 elementIndex = 0; elementIndex < elementCount; ++elementIndex) { char indexBuffer[8]; dSprintf(indexBuffer, 8, "%d", elementIndex); // Fetch object field value. const char* pFieldValue = pSimObject->getPrefixedDataField(fieldName, indexBuffer); if (!pFieldValue) pFieldValue = StringTable->EmptyString(); if (pField->type == TypeBool) pFieldValue = dAtob(pFieldValue) ? "true" : "false"; U32 nBufferSize = dStrlen(pFieldValue) + 1; FrameTemp valueCopy(nBufferSize); dStrcpy((char *)valueCopy, pFieldValue, nBufferSize); // Skip if field should not be written. if (!pSimObject->writeField(fieldName, valueCopy)) continue; // Reassign field value. pFieldValue = valueCopy; // Detect and collapse relative path information char fnBuf[1024]; if ((S32)pField->type == TypeFilename) { Con::collapseScriptFilename(fnBuf, 1024, pFieldValue); pFieldValue = fnBuf; } // Save field/value. if (arrayDepth > 0 || pField->elementCount > 1) currentArrayNode->getChildren()[elementIndex]->addField(fieldName, pFieldValue); else { TamlWriteNode::FieldValuePair* pFieldValuePair = new TamlWriteNode::FieldValuePair(fieldName, pFieldValue); pTamlWriteNode->mFields.push_back(pFieldValuePair); } } } } //----------------------------------------------------------------------------- static S32 QSORT_CALLBACK compareFieldEntries(const void* a, const void* b) { // Debug Profiling. PROFILE_SCOPE(Taml_CompareFieldEntries); SimFieldDictionary::Entry *fa = *((SimFieldDictionary::Entry **)a); SimFieldDictionary::Entry *fb = *((SimFieldDictionary::Entry **)b); return dStricmp(fa->slotName, fb->slotName); } //----------------------------------------------------------------------------- void Taml::compileDynamicFields(TamlWriteNode* pTamlWriteNode) { // Debug Profiling. PROFILE_SCOPE(Taml_CompileDynamicFields); // Sanity! AssertFatal(pTamlWriteNode != NULL, "Cannot compile dynamic fields on a NULL node."); AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile dynamic fields on a node with no object."); // Fetch object. SimObject* pSimObject = pTamlWriteNode->mpSimObject; // Fetch field dictionary. SimFieldDictionary* pFieldDictionary = pSimObject->getFieldDictionary(); // Ignore if not writing dynamic fields. if (!pFieldDictionary || !pSimObject->getCanSaveDynamicFields()) return; // Fetch field list. const AbstractClassRep::FieldList& fieldList = pSimObject->getFieldList(); // Fetch field count. const U32 fieldCount = fieldList.size(); Vector dynamicFieldList(__FILE__, __LINE__); // Ensure the dynamic field doesn't conflict with static field. for (U32 hashIndex = 0; hashIndex < SimFieldDictionary::HashTableSize; ++hashIndex) { for (SimFieldDictionary::Entry* pEntry = pFieldDictionary->mHashTable[hashIndex]; pEntry; pEntry = pEntry->next) { // Iterate static fields. U32 fieldIndex; for (fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex) { if (fieldList[fieldIndex].pFieldname == pEntry->slotName) break; } // Skip if found. if (fieldIndex != (U32)fieldList.size()) continue; // Skip if not writing field. if (!pSimObject->writeField(pEntry->slotName, pEntry->value)) continue; dynamicFieldList.push_back(pEntry); } } // Sort Entries to prevent version control conflicts if (dynamicFieldList.size() > 1) dQsort(dynamicFieldList.address(), dynamicFieldList.size(), sizeof(SimFieldDictionary::Entry*), compareFieldEntries); // Save the fields. for (Vector::iterator entryItr = dynamicFieldList.begin(); entryItr != dynamicFieldList.end(); ++entryItr) { // Fetch entry. SimFieldDictionary::Entry* pEntry = *entryItr; // Save field/value. TamlWriteNode::FieldValuePair* pFieldValuePair = new TamlWriteNode::FieldValuePair(pEntry->slotName, pEntry->value); pTamlWriteNode->mFields.push_back(pFieldValuePair); } } //----------------------------------------------------------------------------- void Taml::compileChildren(TamlWriteNode* pTamlWriteNode) { // Debug Profiling. PROFILE_SCOPE(Taml_CompileChildren); // Sanity! AssertFatal(pTamlWriteNode != NULL, "Cannot compile children on a NULL node."); AssertFatal(pTamlWriteNode->mpSimObject != NULL, "Cannot compile children on a node with no object."); // Fetch object. SimObject* pSimObject = pTamlWriteNode->mpSimObject; // Fetch the Taml children. TamlChildren* pChildren = dynamic_cast(pSimObject); // Finish if object does not contain Taml children. if (pChildren == NULL || pChildren->getTamlChildCount() == 0) return; // Create children vector. pTamlWriteNode->mChildren = new typeNodeVector(); // Fetch the child count. const U32 childCount = pChildren->getTamlChildCount(); // Iterate children. for (U32 childIndex = 0; childIndex < childCount; childIndex++) { // Compile object. TamlWriteNode* pChildTamlWriteNode = compileObject(pChildren->getTamlChild(childIndex)); // Save node. pTamlWriteNode->mChildren->push_back(pChildTamlWriteNode); } } //----------------------------------------------------------------------------- void Taml::compileCustomState(TamlWriteNode* pTamlWriteNode) { // Debug Profiling. PROFILE_SCOPE(Taml_CompileCustomProperties); // Sanity! 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 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, customNodes); } // Fetch custom nodes. const TamlCustomNodeVector& nodes = customNodes.getNodes(); // Finish if no custom nodes to process. if (nodes.size() == 0) return; // Iterate custom properties. for (TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr) { // Fetch the custom node. TamlCustomNode* pCustomNode = *customNodesItr; // Compile custom node state. compileCustomNodeState(pCustomNode); } } //----------------------------------------------------------------------------- void Taml::compileCustomNodeState(TamlCustomNode* pCustomNode) { // Sanity! AssertFatal(pCustomNode != NULL, "Taml: Cannot compile NULL custom node state."); // Fetch children. const TamlCustomNodeVector& children = pCustomNode->getChildren(); // Fetch proxy object. SimObject* pProxyObject = pCustomNode->getProxyObject(false); // Do we have a proxy object? if (pProxyObject != NULL) { // Yes, so sanity! AssertFatal(children.size() == 0, "Taml: Cannot compile a proxy object on a custom node that has children."); // 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; } // Finish if no children. if (children.size() == 0) return; // Iterate children. for (TamlCustomNodeVector::const_iterator childItr = children.begin(); childItr != children.end(); ++childItr) { // Fetch shape node. TamlCustomNode* pChildNode = *childItr; // Compile the child. compileCustomNodeState(pChildNode); } } //----------------------------------------------------------------------------- SimObject* Taml::createType(StringTableEntry typeName, const Taml* pTaml, const char* pProgenitorSuffix) { // Debug Profiling. PROFILE_SCOPE(Taml_CreateType); typedef HashTable typeClassHash; static typeClassHash mClassMap; // Sanity! AssertFatal(typeName != NULL, "Taml: Type cannot be NULL"); // Find type. typeClassHash::Iterator typeItr = mClassMap.find(typeName); // Found type? if (typeItr == mClassMap.end()) { // No, so find type. AbstractClassRep* pClassRep = AbstractClassRep::getClassList(); while (pClassRep) { // Is this the type? if (dStricmp(pClassRep->getClassName(), typeName) == 0) { // Yes, so insert it. typeItr = mClassMap.insertUnique(typeName, pClassRep); break; } // Next type. pClassRep = pClassRep->getNextClass(); } // Did we find the type? if (typeItr == mClassMap.end()) { // No, so warn and fail. Con::warnf("Taml: Failed to create type '%s' as such a registered type could not be found.", typeName); return NULL; } } // Create the object. ConsoleObject* pConsoleObject = typeItr->value->create(); // NOTE: It is important that we don't register the object here as many objects rely on the fact that // fields are set prior to the object being registered. Registering here will invalid those assumptions. // Fetch the SimObject. SimObject* pSimObject = dynamic_cast(pConsoleObject); // Was it a SimObject? if (pSimObject == NULL) { // No, so warn. Con::warnf("Taml: Failed to create type '%s' as it is not a SimObject.", typeName); // Destroy object and fail. delete pConsoleObject; return NULL; } // Are we updating the file-progenitor? if (pTaml->getProgenitorUpdate()) { // Yes, so do we have a progenitor suffix? if (pProgenitorSuffix == NULL) { // No, so just set it to the progenitor file. pSimObject->setProgenitorFile(pTaml->getFilePathBuffer()); } else { // Yes, so format the progenitor buffer. char progenitorBuffer[2048]; dSprintf(progenitorBuffer, sizeof(progenitorBuffer), "%s,%s", pTaml->getFilePathBuffer(), pProgenitorSuffix); // Set the progenitor file. pSimObject->setProgenitorFile(progenitorBuffer); } } return pSimObject; } //----------------------------------------------------------------------------- bool Taml::generateTamlSchema() { // Fetch any TAML Schema file reference. const char* pTamlSchemaFile = Con::getVariable(TAML_SCHEMA_VARIABLE); // Do we have a schema file reference? if (pTamlSchemaFile == NULL || *pTamlSchemaFile == 0) { // No, so warn. Con::warnf("Taml::generateTamlSchema() - Cannot write a TAML schema as no schema variable is set ('%s').", TAML_SCHEMA_VARIABLE); return false; } // Expand the file-name into the file-path buffer. char filePathBuffer[1024]; Con::expandToolScriptFilename(filePathBuffer, sizeof(filePathBuffer), pTamlSchemaFile); FileStream stream; // File opened? /*if ( !stream.open( filePathBuffer, Torque::FS::File::Write ) ) { // No, so warn. Con::warnf("Taml::GenerateTamlSchema() - Could not open filename '%s' for write.", filePathBuffer ); return false; }*/ // Create document. TiXmlDocument schemaDocument; // Add declaration. TiXmlDeclaration schemaDeclaration("1.0", "iso-8859-1", "no"); schemaDocument.InsertEndChild(schemaDeclaration); // Add schema element. TiXmlElement* pSchemaElement = new TiXmlElement("xs:schema"); pSchemaElement->SetAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema"); schemaDocument.LinkEndChild(pSchemaElement); // Fetch class-rep root. AbstractClassRep* pRootType = AbstractClassRep::getClassList(); // Fetch SimObject class rep. AbstractClassRep* pSimObjectType = AbstractClassRep::findClassRep("SimObject"); // Sanity! AssertFatal(pSimObjectType != NULL, "Taml::GenerateTamlSchema() - Could not find SimObject class rep."); // Reset scratch state. char buffer[1024]; HashTable childGroups; // ************************************************************* // Generate console type elements. // ************************************************************* // Vector2. TiXmlComment* pVector2Comment = new TiXmlComment("Vector2 Console Type"); pSchemaElement->LinkEndChild(pVector2Comment); TiXmlElement* pVector2TypeElement = new TiXmlElement("xs:simpleType"); pVector2TypeElement->SetAttribute("name", "Vector2_ConsoleType"); pSchemaElement->LinkEndChild(pVector2TypeElement); TiXmlElement* pVector2ElementA = new TiXmlElement("xs:restriction"); pVector2ElementA->SetAttribute("base", "xs:string"); pVector2TypeElement->LinkEndChild(pVector2ElementA); TiXmlElement* pVector2ElementB = new TiXmlElement("xs:pattern"); pVector2ElementB->SetAttribute("value", "([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b"); pVector2ElementA->LinkEndChild(pVector2ElementB); // Point2F. TiXmlComment* pPoint2FComment = new TiXmlComment("Point2F Console Type"); pSchemaElement->LinkEndChild(pPoint2FComment); TiXmlElement* pPoint2FTypeElement = new TiXmlElement("xs:simpleType"); pPoint2FTypeElement->SetAttribute("name", "Point2F_ConsoleType"); pSchemaElement->LinkEndChild(pPoint2FTypeElement); TiXmlElement* pPoint2FElementA = new TiXmlElement("xs:restriction"); pPoint2FElementA->SetAttribute("base", "xs:string"); pPoint2FTypeElement->LinkEndChild(pPoint2FElementA); TiXmlElement* pPoint2FElementB = new TiXmlElement("xs:pattern"); pPoint2FElementB->SetAttribute("value", "([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b ([-]?(\\b[0-9]+)?\\.)?[0-9]+\\b"); pPoint2FElementA->LinkEndChild(pPoint2FElementB); // Point2I. TiXmlComment* pPoint2IComment = new TiXmlComment("Point2I Console Type"); pSchemaElement->LinkEndChild(pPoint2IComment); TiXmlElement* pPoint2ITypeElement = new TiXmlElement("xs:simpleType"); pPoint2ITypeElement->SetAttribute("name", "Point2I_ConsoleType"); pSchemaElement->LinkEndChild(pPoint2ITypeElement); TiXmlElement* pPoint2IElementA = new TiXmlElement("xs:restriction"); pPoint2IElementA->SetAttribute("base", "xs:string"); pPoint2ITypeElement->LinkEndChild(pPoint2IElementA); TiXmlElement* pPoint2IElementB = new TiXmlElement("xs:pattern"); pPoint2IElementB->SetAttribute("value", "[-]?[0-9]* [-]?[0-9]*"); pPoint2IElementA->LinkEndChild(pPoint2IElementB); // b2AABB. TiXmlComment* pb2AABBComment = new TiXmlComment("b2AABB Console Type"); pSchemaElement->LinkEndChild(pb2AABBComment); TiXmlElement* pb2AABBTypeElement = new TiXmlElement("xs:simpleType"); pb2AABBTypeElement->SetAttribute("name", "b2AABB_ConsoleType"); pSchemaElement->LinkEndChild(pb2AABBTypeElement); TiXmlElement* pb2AABBElementA = new TiXmlElement("xs:restriction"); pb2AABBElementA->SetAttribute("base", "xs:string"); pb2AABBTypeElement->LinkEndChild(pb2AABBElementA); TiXmlElement* pb2AABBElementB = new TiXmlElement("xs:pattern"); 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"); pb2AABBElementA->LinkEndChild(pb2AABBElementB); // RectI. TiXmlComment* pRectIComment = new TiXmlComment("RectI Console Type"); pSchemaElement->LinkEndChild(pRectIComment); TiXmlElement* pRectITypeElement = new TiXmlElement("xs:simpleType"); pRectITypeElement->SetAttribute("name", "RectI_ConsoleType"); pSchemaElement->LinkEndChild(pRectITypeElement); TiXmlElement* pRectIElementA = new TiXmlElement("xs:restriction"); pRectIElementA->SetAttribute("base", "xs:string"); pRectITypeElement->LinkEndChild(pRectIElementA); TiXmlElement* pRectIElementB = new TiXmlElement("xs:pattern"); pRectIElementB->SetAttribute("value", "[-]?[0-9]* [-]?[0-9]* [-]?[0-9]* [-]?[0-9]*"); pRectIElementA->LinkEndChild(pRectIElementB); // RectF. TiXmlComment* pRectFComment = new TiXmlComment("RectF Console Type"); pSchemaElement->LinkEndChild(pRectFComment); TiXmlElement* pRectFTypeElement = new TiXmlElement("xs:simpleType"); pRectFTypeElement->SetAttribute("name", "RectF_ConsoleType"); pSchemaElement->LinkEndChild(pRectFTypeElement); TiXmlElement* pRectFElementA = new TiXmlElement("xs:restriction"); pRectFElementA->SetAttribute("base", "xs:string"); pRectFTypeElement->LinkEndChild(pRectFElementA); TiXmlElement* pRectFElementB = new TiXmlElement("xs:pattern"); pRectFElementB->SetAttribute("value", "(\\b[-]?(b[0-9]+)?\\.)?[0-9]+\\b"); pRectFElementA->LinkEndChild(pRectFElementB); // AssetId. TiXmlComment* pAssetIdComment = new TiXmlComment("AssetId Console Type"); pSchemaElement->LinkEndChild(pAssetIdComment); TiXmlElement* pAssetIdTypeElement = new TiXmlElement("xs:simpleType"); pAssetIdTypeElement->SetAttribute("name", "AssetId_ConsoleType"); pSchemaElement->LinkEndChild(pAssetIdTypeElement); TiXmlElement* pAssetIdElementA = new TiXmlElement("xs:restriction"); pAssetIdElementA->SetAttribute("base", "xs:string"); pAssetIdTypeElement->LinkEndChild(pAssetIdElementA); TiXmlElement* pAssetIdElementB = new TiXmlElement("xs:pattern"); 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); pAssetIdElementB->SetAttribute("value", buffer); pAssetIdElementA->LinkEndChild(pAssetIdElementB); // Color Enums. TiXmlComment* pColorEnumsComment = new TiXmlComment("Color Enums"); pSchemaElement->LinkEndChild(pColorEnumsComment); TiXmlElement* pColorEnumsTypeElement = new TiXmlElement("xs:simpleType"); pColorEnumsTypeElement->SetAttribute("name", "Color_Enums"); pSchemaElement->LinkEndChild(pColorEnumsTypeElement); TiXmlElement* pColorEnumsRestrictionElement = new TiXmlElement("xs:restriction"); pColorEnumsRestrictionElement->SetAttribute("base", "xs:string"); pColorEnumsTypeElement->LinkEndChild(pColorEnumsRestrictionElement); const S32 ColorEnumsCount = StockColor::getCount(); for (S32 index = 0; index < ColorEnumsCount; ++index) { // Add enumeration element. TiXmlElement* pColorEnumsAttributeEnumerationElement = new TiXmlElement("xs:enumeration"); pColorEnumsAttributeEnumerationElement->SetAttribute("value", StockColor::getColorItem(index)->getColorName()); pColorEnumsRestrictionElement->LinkEndChild(pColorEnumsAttributeEnumerationElement); } // LinearColorF. TiXmlComment* pColorFValuesComment = new TiXmlComment("LinearColorF Values"); pSchemaElement->LinkEndChild(pColorFValuesComment); TiXmlElement* pColorFValuesTypeElement = new TiXmlElement("xs:simpleType"); pColorFValuesTypeElement->SetAttribute("name", "ColorF_Values"); pSchemaElement->LinkEndChild(pColorFValuesTypeElement); TiXmlElement* pColorFValuesElementA = new TiXmlElement("xs:restriction"); pColorFValuesElementA->SetAttribute("base", "xs:string"); pColorFValuesTypeElement->LinkEndChild(pColorFValuesElementA); TiXmlElement* pColorFValuesElementB = new TiXmlElement("xs:pattern"); 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"); pColorFValuesElementA->LinkEndChild(pColorFValuesElementB); TiXmlComment* pColorFComment = new TiXmlComment("LinearColorF Console Type"); pSchemaElement->LinkEndChild(pColorFComment); TiXmlElement* pColorFTypeElement = new TiXmlElement("xs:simpleType"); pColorFTypeElement->SetAttribute("name", "ColorF_ConsoleType"); pSchemaElement->LinkEndChild(pColorFTypeElement); TiXmlElement* pColorFUnionElement = new TiXmlElement("xs:union"); pColorFUnionElement->SetAttribute("memberTypes", "ColorF_Values Color_Enums"); pColorFTypeElement->LinkEndChild(pColorFUnionElement); // ColorI. TiXmlComment* pColorIValuesComment = new TiXmlComment("ColorI Values"); pSchemaElement->LinkEndChild(pColorIValuesComment); TiXmlElement* pColorIValuesTypeElement = new TiXmlElement("xs:simpleType"); pColorIValuesTypeElement->SetAttribute("name", "ColorI_Values"); pSchemaElement->LinkEndChild(pColorIValuesTypeElement); TiXmlElement* pColorIValuesElementA = new TiXmlElement("xs:restriction"); pColorIValuesElementA->SetAttribute("base", "xs:string"); pColorIValuesTypeElement->LinkEndChild(pColorIValuesElementA); TiXmlElement* pColorIValuesElementB = new TiXmlElement("xs:pattern"); pColorIValuesElementB->SetAttribute("value", "[-]?[0-9]* [-]?[0-9]* [-]?[0-9]* [-]?[0-9]*"); pColorIValuesElementA->LinkEndChild(pColorIValuesElementB); TiXmlComment* pColorIComment = new TiXmlComment("ColorI Console Type"); pSchemaElement->LinkEndChild(pColorIComment); TiXmlElement* pColorITypeElement = new TiXmlElement("xs:simpleType"); pColorITypeElement->SetAttribute("name", "ColorI_ConsoleType"); pSchemaElement->LinkEndChild(pColorITypeElement); TiXmlElement* pColorIUnionElement = new TiXmlElement("xs:union"); pColorIUnionElement->SetAttribute("memberTypes", "ColorI_Values Color_Enums"); pColorITypeElement->LinkEndChild(pColorIUnionElement); // ************************************************************* // Generate engine type elements. // ************************************************************* // Generate the engine type elements. TiXmlComment* tComment = new TiXmlComment("Type Elements"); pSchemaElement->LinkEndChild(tComment); for (AbstractClassRep* pType = pRootType; pType != NULL; pType = pType->getNextClass()) { // Add type. TiXmlElement* pTypeElement = new TiXmlElement("xs:element"); pTypeElement->SetAttribute("name", pType->getClassName()); dSprintf(buffer, sizeof(buffer), "%s_Type", pType->getClassName()); pTypeElement->SetAttribute("type", buffer); pSchemaElement->LinkEndChild(pTypeElement); } // ************************************************************* // Generate the engine complex types. // ************************************************************* for (AbstractClassRep* pType = pRootType; pType != NULL; pType = pType->getNextClass()) { // Add complex type comment. dSprintf(buffer, sizeof(buffer), " %s Type ", pType->getClassName()); TiXmlComment* ctComment = new TiXmlComment(buffer); pSchemaElement->LinkEndChild(ctComment); // Add complex type. TiXmlElement* pComplexTypeElement = new TiXmlElement("xs:complexType"); dSprintf(buffer, sizeof(buffer), "%s_Type", pType->getClassName()); pComplexTypeElement->SetAttribute("name", buffer); pSchemaElement->LinkEndChild(pComplexTypeElement); // Add sequence. TiXmlElement* pSequenceElement = new TiXmlElement("xs:sequence"); pComplexTypeElement->LinkEndChild(pSequenceElement); // Fetch container child class. AbstractClassRep* pContainerChildClass = pType->getContainerChildClass(true); // Is the type allowed children? if (pContainerChildClass != NULL) { // Yes, so add choice element. TiXmlElement* pChoiceElement = new TiXmlElement("xs:choice"); pChoiceElement->SetAttribute("minOccurs", 0); pChoiceElement->SetAttribute("maxOccurs", "unbounded"); pSequenceElement->LinkEndChild(pChoiceElement); // Find child group. HashTable::Iterator childGroupItr = childGroups.find(pContainerChildClass); // Does the group exist? if (childGroupItr == childGroups.end()) { // No, so format group name. dSprintf(buffer, sizeof(buffer), "%s_ChildrenTypes", pContainerChildClass->getClassName()); // Insert into child group hash. childGroupItr = childGroups.insertUnique(pContainerChildClass, StringTable->insert(buffer)); // Add the group. TiXmlElement* pChildrenGroupElement = new TiXmlElement("xs:group"); pChildrenGroupElement->SetAttribute("name", buffer); pSchemaElement->LinkEndChild(pChildrenGroupElement); // Add choice element. TiXmlElement* pChildreGroupChoiceElement = new TiXmlElement("xs:choice"); pChildrenGroupElement->LinkEndChild(pChildreGroupChoiceElement); // Add choice members. for (AbstractClassRep* pChoiceType = pRootType; pChoiceType != NULL; pChoiceType = pChoiceType->getNextClass()) { // Skip if not derived from the container child class. if (!pChoiceType->isClass(pContainerChildClass)) continue; // Add choice member. TiXmlElement* pChildrenMemberElement = new TiXmlElement("xs:element"); pChildrenMemberElement->SetAttribute("name", pChoiceType->getClassName()); dSprintf(buffer, sizeof(buffer), "%s_Type", pChoiceType->getClassName()); pChildrenMemberElement->SetAttribute("type", buffer); pChildreGroupChoiceElement->LinkEndChild(pChildrenMemberElement); } } // Reference the child group. TiXmlElement* pChoiceGroupReferenceElement = new TiXmlElement("xs:group"); pChoiceGroupReferenceElement->SetAttribute("ref", childGroupItr->value); pChoiceGroupReferenceElement->SetAttribute("minOccurs", 0); pChoiceElement->LinkEndChild(pChoiceGroupReferenceElement); } // Generate the custom Taml schema. for (AbstractClassRep* pCustomSchemaType = pType; pCustomSchemaType != NULL; pCustomSchemaType = pCustomSchemaType->getParentClass()) { // Fetch the types custom TAML schema function. AbstractClassRep::WriteCustomTamlSchema customSchemaFn = pCustomSchemaType->getCustomTamlSchema(); // Skip if no function avilable. if (customSchemaFn == NULL) continue; // Call schema generation function. customSchemaFn(pType, pSequenceElement); } // Generate field attribute group. TiXmlElement* pFieldAttributeGroupElement = new TiXmlElement("xs:attributeGroup"); dSprintf(buffer, sizeof(buffer), "%s_Fields", pType->getClassName()); pFieldAttributeGroupElement->SetAttribute("name", buffer); pSchemaElement->LinkEndChild(pFieldAttributeGroupElement); // Fetch field list. const AbstractClassRep::FieldList& fields = pType->mFieldList; // Fetcj field count. const S32 fieldCount = fields.size(); // Iterate static fields (in reverse as most types are organized from the root-fields up). for (S32 index = fieldCount - 1; index > 0; --index) { // Fetch field. const AbstractClassRep::Field& field = fields[index]; // Skip if not a data field. if (field.type == AbstractClassRep::DeprecatedFieldType || field.type == AbstractClassRep::StartGroupFieldType || field.type == AbstractClassRep::EndGroupFieldType) continue; // Skip if the field root is not this type. if (pType->findFieldRoot(field.pFieldname) != pType) continue; // Add attribute element. TiXmlElement* pAttributeElement = new TiXmlElement("xs:attribute"); pAttributeElement->SetAttribute("name", field.pFieldname); // Handle the console type appropriately. const S32 fieldType = (S32)field.type; /* // Is the field an enumeration? if ( fieldType == TypeEnum ) { // Yes, so add attribute type. TiXmlElement* pAttributeSimpleTypeElement = new TiXmlElement( "xs:simpleType" ); pAttributeElement->LinkEndChild( pAttributeSimpleTypeElement ); // Add restriction element. TiXmlElement* pAttributeRestrictionElement = new TiXmlElement( "xs:restriction" ); pAttributeRestrictionElement->SetAttribute( "base", "xs:string" ); pAttributeSimpleTypeElement->LinkEndChild( pAttributeRestrictionElement ); // Yes, so fetch enumeration count. const S32 enumCount = field.table->size; // Iterate enumeration. for( S32 index = 0; index < enumCount; ++index ) { // Add enumeration element. TiXmlElement* pAttributeEnumerationElement = new TiXmlElement( "xs:enumeration" ); pAttributeEnumerationElement->SetAttribute( "value", field.table->table[index].label ); pAttributeRestrictionElement->LinkEndChild( pAttributeEnumerationElement ); } } else {*/ // No, so assume it's a string type initially. const char* pFieldTypeDescription = "xs:string"; // Handle known types. if (fieldType == TypeF32) { pFieldTypeDescription = "xs:float"; } else if (fieldType == TypeS8 || fieldType == TypeS32) { pFieldTypeDescription = "xs:int"; } else if (fieldType == TypeBool || fieldType == TypeFlag) { pFieldTypeDescription = "xs:boolean"; } else if (fieldType == TypePoint2F) { pFieldTypeDescription = "Point2F_ConsoleType"; } else if (fieldType == TypePoint2I) { pFieldTypeDescription = "Point2I_ConsoleType"; } else if (fieldType == TypeRectI) { pFieldTypeDescription = "RectI_ConsoleType"; } else if (fieldType == TypeRectF) { pFieldTypeDescription = "RectF_ConsoleType"; } else if (fieldType == TypeColorF) { pFieldTypeDescription = "ColorF_ConsoleType"; } else if (fieldType == TypeColorI) { pFieldTypeDescription = "ColorI_ConsoleType"; } else if (fieldType == TypeAssetId/* || fieldType == TypeImageAssetPtr || fieldType == TypeAnimationAssetPtr || fieldType == TypeAudioAssetPtr*/) { pFieldTypeDescription = "AssetId_ConsoleType"; } // Set attribute type. pAttributeElement->SetAttribute("type", pFieldTypeDescription); //} pAttributeElement->SetAttribute("use", "optional"); pFieldAttributeGroupElement->LinkEndChild(pAttributeElement); } // Is this the SimObject Type? if (pType == pSimObjectType) { // Yes, so add reserved Taml field attributes here... // Add Taml "Name" attribute element. TiXmlElement* pNameAttributeElement = new TiXmlElement("xs:attribute"); pNameAttributeElement->SetAttribute("name", tamlNamedObjectName); pNameAttributeElement->SetAttribute("type", "xs:normalizedString"); pFieldAttributeGroupElement->LinkEndChild(pNameAttributeElement); // Add Taml "TamlId" attribute element. TiXmlElement* pTamlIdAttributeElement = new TiXmlElement("xs:attribute"); pTamlIdAttributeElement->SetAttribute("name", tamlRefIdName); pTamlIdAttributeElement->SetAttribute("type", "xs:nonNegativeInteger"); pFieldAttributeGroupElement->LinkEndChild(pTamlIdAttributeElement); // Add Taml "TamlRefId" attribute element. TiXmlElement* pTamlRefIdAttributeElement = new TiXmlElement("xs:attribute"); pTamlRefIdAttributeElement->SetAttribute("name", tamlRefToIdName); pTamlRefIdAttributeElement->SetAttribute("type", "xs:nonNegativeInteger"); pFieldAttributeGroupElement->LinkEndChild(pTamlRefIdAttributeElement); } // Add attribute group types. for (AbstractClassRep* pAttributeGroupsType = pType; pAttributeGroupsType != NULL; pAttributeGroupsType = pAttributeGroupsType->getParentClass()) { TiXmlElement* pFieldAttributeGroupRefElement = new TiXmlElement("xs:attributeGroup"); dSprintf(buffer, sizeof(buffer), "%s_Fields", pAttributeGroupsType->getClassName()); pFieldAttributeGroupRefElement->SetAttribute("ref", buffer); pComplexTypeElement->LinkEndChild(pFieldAttributeGroupRefElement); } // Add "any" attribute element (dynamic fields). TiXmlElement* pAnyAttributeElement = new TiXmlElement("xs:anyAttribute"); pAnyAttributeElement->SetAttribute("processContents", "skip"); pComplexTypeElement->LinkEndChild(pAnyAttributeElement); } // Write the schema document. schemaDocument.SaveFile(filePathBuffer); // Close file. stream.close(); return true; } //----------------------------------------------------------------------------- void Taml::WriteUnrestrictedCustomTamlSchema(const char* pCustomNodeName, const AbstractClassRep* pClassRep, TiXmlElement* pParentElement) { // Sanity! AssertFatal(pCustomNodeName != NULL, "Taml::WriteDefaultCustomTamlSchema() - Node name cannot be NULL."); AssertFatal(pClassRep != NULL, "Taml::WriteDefaultCustomTamlSchema() - ClassRep cannot be NULL."); AssertFatal(pParentElement != NULL, "Taml::WriteDefaultCustomTamlSchema() - Parent Element cannot be NULL."); char buffer[1024]; // Add custom type element. TiXmlElement* pCustomElement = new TiXmlElement("xs:element"); dSprintf(buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), pCustomNodeName); pCustomElement->SetAttribute("name", buffer); pCustomElement->SetAttribute("minOccurs", 0); pCustomElement->SetAttribute("maxOccurs", 1); pParentElement->LinkEndChild(pCustomElement); // Add complex type element. TiXmlElement* pComplexTypeElement = new TiXmlElement("xs:complexType"); pCustomElement->LinkEndChild(pComplexTypeElement); // Add choice element. TiXmlElement* pChoiceElement = new TiXmlElement("xs:choice"); pChoiceElement->SetAttribute("minOccurs", 0); pChoiceElement->SetAttribute("maxOccurs", "unbounded"); pComplexTypeElement->LinkEndChild(pChoiceElement); // Add sequence element. TiXmlElement* pSequenceElement = new TiXmlElement("xs:sequence"); pChoiceElement->LinkEndChild(pSequenceElement); // Add "any" element. TiXmlElement* pAnyElement = new TiXmlElement("xs:any"); pAnyElement->SetAttribute("processContents", "skip"); pSequenceElement->LinkEndChild(pAnyElement); }