2015-10-13 20:19:36 +00:00
//-----------------------------------------------------------------------------
// 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"
//-----------------------------------------------------------------------------
2017-11-12 18:39:14 +00:00
IMPLEMENT_CONOBJECT ( Taml ) ;
2015-10-13 20:19:36 +00:00
//-----------------------------------------------------------------------------
2017-11-12 18:39:14 +00:00
StringTableEntry tamlRefIdName = StringTable - > insert ( " TamlId " ) ;
StringTableEntry tamlRefToIdName = StringTable - > insert ( " TamlRefId " ) ;
StringTableEntry tamlNamedObjectName = StringTable - > insert ( " Name " ) ;
2015-10-13 20:19:36 +00:00
//-----------------------------------------------------------------------------
typedef Taml : : TamlFormatMode _TamlFormatMode ;
2017-11-12 18:39:14 +00:00
ImplementEnumType ( _TamlFormatMode ,
2015-10-13 20:19:36 +00:00
" " )
{
2017-11-12 18:39:14 +00:00
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 ( ) :
2020-05-11 20:12:50 +00:00
mMasterNodeId ( 0 ) ,
2017-11-12 18:39:14 +00:00
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 ) ;
2015-10-13 20:19:36 +00:00
return false ;
2017-11-12 18:39:14 +00:00
}
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 )
2015-10-13 20:19:36 +00:00
return Taml : : XmlFormat ;
2017-11-12 18:39:14 +00:00
// Check for the Binary format.
if ( binaryExtensionLength < = filenameLength & & dStricmp ( pEndOfFilename - xmlExtensionLength , mAutoFormatBinaryExtension ) = = 0 )
return Taml : : BinaryFormat ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Check for the XML format.
if ( jsonExtensionLength < = filenameLength & & dStricmp ( pEndOfFilename - jsonExtensionLength , mAutoFormatJSONExtension ) = = 0 )
2015-10-13 20:19:36 +00:00
return Taml : : JSONFormat ;
2017-11-12 18:39:14 +00:00
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Use the explicitly specified format mode.
return mFormatMode ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
//-----------------------------------------------------------------------------
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
TamlWriteNode * Taml : : compileObject ( SimObject * pSimObject , const bool forceId )
{
// Debug Profiling.
PROFILE_SCOPE ( Taml_CompileObject ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Sanity!
AssertFatal ( pSimObject ! = NULL , " Taml::compileObject() - Cannot compile a NULL object. " ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch object Id.
const SimObjectId objectId = pSimObject - > getId ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Find a previously compiled node.
typeCompiledHash : : Iterator compiledItr = mCompiledObjects . find ( objectId ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// 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. " ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Yes, so fetch node.
TamlWriteNode * compiledNode = compiledItr - > value ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Is a reference Id already present?
if ( compiledNode - > mRefId = = 0 )
{
2015-10-13 20:19:36 +00:00
// No, so allocate one.
compiledNode - > mRefId = + + mMasterNodeId ;
2017-11-12 18:39:14 +00:00
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Create write node.
TamlWriteNode * pNewNode = new TamlWriteNode ( ) ;
pNewNode - > set ( pSimObject ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Set reference node.
pNewNode - > mRefToNode = compiledNode ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Push new node.
mCompiledNodes . push_back ( pNewNode ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
return pNewNode ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// No, so create write node.
TamlWriteNode * pNewNode = new TamlWriteNode ( ) ;
pNewNode - > set ( pSimObject ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Is an Id being forced for this object?
if ( forceId )
{
// Yes, so allocate one.
pNewNode - > mRefId = + + mMasterNodeId ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Push new node.
mCompiledNodes . push_back ( pNewNode ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Insert compiled object.
mCompiledObjects . insertUnique ( objectId , pNewNode ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Are there any Taml callbacks?
if ( pNewNode - > mpTamlCallbacks ! = NULL )
{
// Yes, so call it.
tamlPreWrite ( pNewNode - > mpTamlCallbacks ) ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Compile static and dynamic fields.
compileStaticFields ( pNewNode ) ;
compileDynamicFields ( pNewNode ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Compile children.
compileChildren ( pNewNode ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Compile custom state.
compileCustomState ( pNewNode ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Are there any Taml callbacks?
if ( pNewNode - > mpTamlCallbacks ! = NULL )
{
// Yes, so call it.
tamlPostWrite ( pNewNode - > mpTamlCallbacks ) ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
return pNewNode ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
//-----------------------------------------------------------------------------
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
void Taml : : compileStaticFields ( TamlWriteNode * pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE ( Taml_CompileStaticFields ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// 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. " ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch object.
SimObject * pSimObject = pTamlWriteNode - > mpSimObject ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch field list.
const AbstractClassRep : : FieldList & fieldList = pSimObject - > getFieldList ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch field count.
const U32 fieldCount = fieldList . size ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Iterate fields.
U8 arrayDepth = 0 ;
TamlCustomNode * currentArrayNode ;
for ( U32 index = 0 ; index < fieldCount ; + + index )
{
// Fetch field.
const AbstractClassRep : : Field * pField = & fieldList [ index ] ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Ignore if field not appropriate.
if ( pField - > type = = AbstractClassRep : : DeprecatedFieldType | |
2015-10-13 20:19:36 +00:00
pField - > type = = AbstractClassRep : : StartGroupFieldType | |
2017-11-12 18:39:14 +00:00
pField - > type = = AbstractClassRep : : EndGroupFieldType )
2015-10-13 20:19:36 +00:00
continue ;
2017-11-12 18:39:14 +00:00
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 ) )
2015-10-13 20:19:36 +00:00
continue ;
2017-11-12 18:39:14 +00:00
// Iterate elements.
for ( U32 elementIndex = 0 ; elementIndex < elementCount ; + + elementIndex )
{
2015-10-13 20:19:36 +00:00
char indexBuffer [ 8 ] ;
2017-11-12 18:39:14 +00:00
dSprintf ( indexBuffer , 8 , " %d " , elementIndex ) ;
2015-10-13 20:19:36 +00:00
// Fetch object field value.
const char * pFieldValue = pSimObject - > getPrefixedDataField ( fieldName , indexBuffer ) ;
2017-11-12 18:39:14 +00:00
if ( ! pFieldValue )
2015-10-13 20:19:36 +00:00
pFieldValue = StringTable - > EmptyString ( ) ;
2017-11-12 18:39:14 +00:00
if ( pField - > type = = TypeBool )
2015-10-13 20:19:36 +00:00
pFieldValue = dAtob ( pFieldValue ) ? " true " : " false " ;
2017-11-12 18:39:14 +00:00
U32 nBufferSize = dStrlen ( pFieldValue ) + 1 ;
FrameTemp < char > valueCopy ( nBufferSize ) ;
2018-03-06 06:59:05 +00:00
dStrcpy ( ( char * ) valueCopy , pFieldValue , nBufferSize ) ;
2015-10-13 20:19:36 +00:00
// Skip if field should not be written.
if ( ! pSimObject - > writeField ( fieldName , valueCopy ) )
2017-11-12 18:39:14 +00:00
continue ;
2015-10-13 20:19:36 +00:00
// Reassign field value.
pFieldValue = valueCopy ;
// Detect and collapse relative path information
char fnBuf [ 1024 ] ;
if ( ( S32 ) pField - > type = = TypeFilename )
{
2017-11-12 18:39:14 +00:00
Con : : collapseScriptFilename ( fnBuf , 1024 , pFieldValue ) ;
pFieldValue = fnBuf ;
2015-10-13 20:19:36 +00:00
}
// Save field/value.
2017-11-12 18:39:14 +00:00
if ( arrayDepth > 0 | | pField - > elementCount > 1 )
2015-10-13 20:19:36 +00:00
currentArrayNode - > getChildren ( ) [ elementIndex ] - > addField ( fieldName , pFieldValue ) ;
else
{
2017-11-12 18:39:14 +00:00
TamlWriteNode : : FieldValuePair * pFieldValuePair = new TamlWriteNode : : FieldValuePair ( fieldName , pFieldValue ) ;
pTamlWriteNode - > mFields . push_back ( pFieldValuePair ) ;
2015-10-13 20:19:36 +00:00
}
2017-11-12 18:39:14 +00:00
}
}
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
//-----------------------------------------------------------------------------
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
static S32 QSORT_CALLBACK compareFieldEntries ( const void * a , const void * b )
{
// Debug Profiling.
PROFILE_SCOPE ( Taml_CompareFieldEntries ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
SimFieldDictionary : : Entry * fa = * ( ( SimFieldDictionary : : Entry * * ) a ) ;
SimFieldDictionary : : Entry * fb = * ( ( SimFieldDictionary : : Entry * * ) b ) ;
return dStricmp ( fa - > slotName , fb - > slotName ) ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
//-----------------------------------------------------------------------------
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
void Taml : : compileDynamicFields ( TamlWriteNode * pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE ( Taml_CompileDynamicFields ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// 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. " ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch object.
SimObject * pSimObject = pTamlWriteNode - > mpSimObject ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch field dictionary.
SimFieldDictionary * pFieldDictionary = pSimObject - > getFieldDictionary ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Ignore if not writing dynamic fields.
if ( ! pFieldDictionary | | ! pSimObject - > getCanSaveDynamicFields ( ) )
return ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch field list.
const AbstractClassRep : : FieldList & fieldList = pSimObject - > getFieldList ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch field count.
const U32 fieldCount = fieldList . size ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
Vector < SimFieldDictionary : : Entry * > dynamicFieldList ( __FILE__ , __LINE__ ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// 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 )
{
2015-10-13 20:19:36 +00:00
// Iterate static fields.
U32 fieldIndex ;
2017-11-12 18:39:14 +00:00
for ( fieldIndex = 0 ; fieldIndex < fieldCount ; + + fieldIndex )
2015-10-13 20:19:36 +00:00
{
2017-11-12 18:39:14 +00:00
if ( fieldList [ fieldIndex ] . pFieldname = = pEntry - > slotName )
break ;
2015-10-13 20:19:36 +00:00
}
// Skip if found.
2017-11-12 18:39:14 +00:00
if ( fieldIndex ! = ( U32 ) fieldList . size ( ) )
continue ;
2015-10-13 20:19:36 +00:00
// Skip if not writing field.
2017-11-12 18:39:14 +00:00
if ( ! pSimObject - > writeField ( pEntry - > slotName , pEntry - > value ) )
continue ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
dynamicFieldList . push_back ( pEntry ) ;
}
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Sort Entries to prevent version control conflicts
if ( dynamicFieldList . size ( ) > 1 )
dQsort ( dynamicFieldList . address ( ) , dynamicFieldList . size ( ) , sizeof ( SimFieldDictionary : : Entry * ) , compareFieldEntries ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// 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 )
{
2015-10-13 20:19:36 +00:00
// Is this the type?
2017-11-12 18:39:14 +00:00
if ( dStricmp ( pClassRep - > getClassName ( ) , typeName ) = = 0 )
2015-10-13 20:19:36 +00:00
{
2017-11-12 18:39:14 +00:00
// Yes, so insert it.
typeItr = mClassMap . insertUnique ( typeName , pClassRep ) ;
break ;
2015-10-13 20:19:36 +00:00
}
// Next type.
pClassRep = pClassRep - > getNextClass ( ) ;
2017-11-12 18:39:14 +00:00
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Did we find the type?
if ( typeItr = = mClassMap . end ( ) )
{
2015-10-13 20:19:36 +00:00
// No, so warn and fail.
2017-11-12 18:39:14 +00:00
Con : : warnf ( " Taml: Failed to create type '%s' as such a registered type could not be found. " , typeName ) ;
2015-10-13 20:19:36 +00:00
return NULL ;
2017-11-12 18:39:14 +00:00
}
}
// 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 )
{
2015-10-13 20:19:36 +00:00
// No, so just set it to the progenitor file.
2017-11-12 18:39:14 +00:00
pSimObject - > setProgenitorFile ( pTaml - > getFilePathBuffer ( ) ) ;
}
else
{
2015-10-13 20:19:36 +00:00
// Yes, so format the progenitor buffer.
char progenitorBuffer [ 2048 ] ;
2017-11-12 18:39:14 +00:00
dSprintf ( progenitorBuffer , sizeof ( progenitorBuffer ) , " %s,%s " , pTaml - > getFilePathBuffer ( ) , pProgenitorSuffix ) ;
2015-10-13 20:19:36 +00:00
// Set the progenitor file.
2017-11-12 18:39:14 +00:00
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.
2018-03-13 22:30:33 +00:00
TiXmlComment * tComment = new TiXmlComment ( " Type Elements " ) ;
pSchemaElement - > LinkEndChild ( tComment ) ;
2017-11-12 18:39:14 +00:00
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 ( ) ) ;
2018-03-13 22:30:33 +00:00
TiXmlComment * ctComment = new TiXmlComment ( buffer ) ;
pSchemaElement - > LinkEndChild ( ctComment ) ;
2017-11-12 18:39:14 +00:00
// 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 )
{
2015-10-13 20:19:36 +00:00
// Yes, so add choice element.
2017-11-12 18:39:14 +00:00
TiXmlElement * pChoiceElement = new TiXmlElement ( " xs:choice " ) ;
pChoiceElement - > SetAttribute ( " minOccurs " , 0 ) ;
pChoiceElement - > SetAttribute ( " maxOccurs " , " unbounded " ) ;
pSequenceElement - > LinkEndChild ( pChoiceElement ) ;
2015-10-13 20:19:36 +00:00
// Find child group.
2017-11-12 18:39:14 +00:00
HashTable < AbstractClassRep * , StringTableEntry > : : Iterator childGroupItr = childGroups . find ( pContainerChildClass ) ;
2015-10-13 20:19:36 +00:00
// Does the group exist?
2017-11-12 18:39:14 +00:00
if ( childGroupItr = = childGroups . end ( ) )
2015-10-13 20:19:36 +00:00
{
2017-11-12 18:39:14 +00:00
// 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 ) ;
}
2015-10-13 20:19:36 +00:00
}
// Reference the child group.
2017-11-12 18:39:14 +00:00
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 ( ) )
{
2015-10-13 20:19:36 +00:00
// Fetch the types custom TAML schema function.
AbstractClassRep : : WriteCustomTamlSchema customSchemaFn = pCustomSchemaType - > getCustomTamlSchema ( ) ;
// Skip if no function avilable.
2017-11-12 18:39:14 +00:00
if ( customSchemaFn = = NULL )
continue ;
2015-10-13 20:19:36 +00:00
// Call schema generation function.
2017-11-12 18:39:14 +00:00
customSchemaFn ( pType , pSequenceElement ) ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// 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 ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetch field list.
const AbstractClassRep : : FieldList & fields = pType - > mFieldList ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Fetcj field count.
const S32 fieldCount = fields . size ( ) ;
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Iterate static fields (in reverse as most types are organized from the root-fields up).
for ( S32 index = fieldCount - 1 ; index > 0 ; - - index )
{
2015-10-13 20:19:36 +00:00
// Fetch field.
const AbstractClassRep : : Field & field = fields [ index ] ;
// Skip if not a data field.
2017-11-12 18:39:14 +00:00
if ( field . type = = AbstractClassRep : : DeprecatedFieldType | |
field . type = = AbstractClassRep : : StartGroupFieldType | |
field . type = = AbstractClassRep : : EndGroupFieldType )
continue ;
2015-10-13 20:19:36 +00:00
// Skip if the field root is not this type.
2017-11-12 18:39:14 +00:00
if ( pType - > findFieldRoot ( field . pFieldname ) ! = pType )
continue ;
2015-10-13 20:19:36 +00:00
// Add attribute element.
2017-11-12 18:39:14 +00:00
TiXmlElement * pAttributeElement = new TiXmlElement ( " xs:attribute " ) ;
pAttributeElement - > SetAttribute ( " name " , field . pFieldname ) ;
2015-10-13 20:19:36 +00:00
// Handle the console type appropriately.
const S32 fieldType = ( S32 ) field . type ;
/*
// Is the field an enumeration?
if ( fieldType = = TypeEnum )
{
2017-11-12 18:39:14 +00:00
// 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 ) ;
}
2015-10-13 20:19:36 +00:00
}
else
{ */
2017-11-12 18:39:14 +00:00
// 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 ) ;
2015-10-13 20:19:36 +00:00
//}
2017-11-12 18:39:14 +00:00
pAttributeElement - > SetAttribute ( " use " , " optional " ) ;
pFieldAttributeGroupElement - > LinkEndChild ( pAttributeElement ) ;
}
2015-10-13 20:19:36 +00:00
2017-11-12 18:39:14 +00:00
// Is this the SimObject Type?
if ( pType = = pSimObjectType )
{
2015-10-13 20:19:36 +00:00
// Yes, so add reserved Taml field attributes here...
// Add Taml "Name" attribute element.
2017-11-12 18:39:14 +00:00
TiXmlElement * pNameAttributeElement = new TiXmlElement ( " xs:attribute " ) ;
pNameAttributeElement - > SetAttribute ( " name " , tamlNamedObjectName ) ;
pNameAttributeElement - > SetAttribute ( " type " , " xs:normalizedString " ) ;
pFieldAttributeGroupElement - > LinkEndChild ( pNameAttributeElement ) ;
2015-10-13 20:19:36 +00:00
// Add Taml "TamlId" attribute element.
2017-11-12 18:39:14 +00:00
TiXmlElement * pTamlIdAttributeElement = new TiXmlElement ( " xs:attribute " ) ;
pTamlIdAttributeElement - > SetAttribute ( " name " , tamlRefIdName ) ;
pTamlIdAttributeElement - > SetAttribute ( " type " , " xs:nonNegativeInteger " ) ;
pFieldAttributeGroupElement - > LinkEndChild ( pTamlIdAttributeElement ) ;
2015-10-13 20:19:36 +00:00
// Add Taml "TamlRefId" attribute element.
2017-11-12 18:39:14 +00:00
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 ) ;
2018-03-06 06:59:05 +00:00
}