mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
1552 lines
58 KiB
C++
1552 lines
58 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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() :
|
|
mMasterNodeId(0),
|
|
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<char *>(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<char> 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<SimFieldDictionary::Entry*> 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<SimFieldDictionary::Entry*>::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<TamlChildren*>(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<SimObject>(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<StringTableEntry, AbstractClassRep*> 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<SimObject*>(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<AbstractClassRep*, StringTableEntry> 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<AbstractClassRep*, StringTableEntry>::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);
|
|
}
|