mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-11 22:54:34 +00:00
Initial Implementation of the Taml, Asset and Modules systems.
Only has example and shape assets currently.
This commit is contained in:
parent
2044b2691e
commit
7a3b40a86d
123 changed files with 30435 additions and 181 deletions
429
Engine/source/persistence/taml/binary/tamlBinaryReader.cpp
Normal file
429
Engine/source/persistence/taml/binary/tamlBinaryReader.cpp
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/binary/tamlBinaryReader.h"
|
||||
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#include "core/util/zip/zipSubStream.h"
|
||||
#endif
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlBinaryReader::read( FileStream& stream )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_Read);
|
||||
|
||||
// Read Taml signature.
|
||||
StringTableEntry tamlSignature = stream.readSTString();
|
||||
|
||||
// Is the signature correct?
|
||||
if ( tamlSignature != StringTable->insert( TAML_SIGNATURE ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf("Taml: Cannot read binary file as signature is incorrect '%s'.", tamlSignature );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read version Id.
|
||||
U32 versionId;
|
||||
stream.read( &versionId );
|
||||
|
||||
// Read compressed flag.
|
||||
bool compressed;
|
||||
stream.read( &compressed );
|
||||
|
||||
SimObject* pSimObject = NULL;
|
||||
|
||||
// Is the stream compressed?
|
||||
if ( compressed )
|
||||
{
|
||||
// Yes, so attach zip stream.
|
||||
ZipSubRStream zipStream;
|
||||
zipStream.attachStream( &stream );
|
||||
|
||||
// Parse element.
|
||||
pSimObject = parseElement( zipStream, versionId );
|
||||
|
||||
// Detach zip stream.
|
||||
zipStream.detachStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so parse element.
|
||||
pSimObject = parseElement( stream, versionId );
|
||||
}
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::resetParse( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ResetParse);
|
||||
|
||||
// Clear object reference map.
|
||||
mObjectReferenceMap.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlBinaryReader::parseElement( Stream& stream, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseElement);
|
||||
|
||||
SimObject* pSimObject = NULL;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// Format the type location.
|
||||
char typeLocationBuffer[64];
|
||||
dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='binary' offset=%u]", stream.getPosition() );
|
||||
#endif
|
||||
|
||||
// Fetch element name.
|
||||
StringTableEntry typeName = stream.readSTString();
|
||||
|
||||
// Fetch object name.
|
||||
StringTableEntry objectName = stream.readSTString();
|
||||
|
||||
// Read references.
|
||||
U32 tamlRefId;
|
||||
U32 tamlRefToId;
|
||||
stream.read( &tamlRefId );
|
||||
stream.read( &tamlRefToId );
|
||||
|
||||
// Do we have a reference to Id?
|
||||
if ( tamlRefToId != 0 )
|
||||
{
|
||||
// Yes, so fetch reference.
|
||||
typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
|
||||
|
||||
// Did we find the reference?
|
||||
if ( referenceItr == mObjectReferenceMap.end() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Could not find a reference Id of '%d'", tamlRefToId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return referenceItr->value;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
|
||||
#else
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml );
|
||||
#endif
|
||||
|
||||
// Finish if we couldn't create the type.
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Find Taml callbacks.
|
||||
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPreRead( pCallbacks );
|
||||
}
|
||||
|
||||
// Parse attributes.
|
||||
parseAttributes( stream, pSimObject, versionId );
|
||||
|
||||
// Does the object require a name?
|
||||
if ( objectName == StringTable->EmptyString() )
|
||||
{
|
||||
// No, so just register anonymously.
|
||||
pSimObject->registerObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so register a named object.
|
||||
pSimObject->registerObject( objectName );
|
||||
|
||||
// Was the name assigned?
|
||||
if ( pSimObject->getName() != objectName )
|
||||
{
|
||||
// No, so warn that the name was rejected.
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists. '%s'", typeName, objectName, typeLocationBuffer );
|
||||
#else
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.", typeName, objectName );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( tamlRefId != 0 )
|
||||
{
|
||||
// Yes, so insert reference.
|
||||
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
|
||||
}
|
||||
|
||||
// Parse custom elements.
|
||||
TamlCustomNodes customProperties;
|
||||
|
||||
// Parse children.
|
||||
parseChildren( stream, pCallbacks, pSimObject, versionId );
|
||||
|
||||
// Parse custom elements.
|
||||
parseCustomElements( stream, pCallbacks, customProperties, versionId );
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPostRead( pCallbacks, customProperties );
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseAttributes);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
|
||||
|
||||
// Fetch attribute count.
|
||||
U32 attributeCount;
|
||||
stream.read( &attributeCount );
|
||||
|
||||
// Finish if no attributes.
|
||||
if ( attributeCount == 0 )
|
||||
return;
|
||||
|
||||
char valueBuffer[4096];
|
||||
|
||||
// Iterate attributes.
|
||||
for ( U32 index = 0; index < attributeCount; ++index )
|
||||
{
|
||||
// Fetch attribute.
|
||||
StringTableEntry attributeName = stream.readSTString();
|
||||
stream.readLongString( 4096, valueBuffer );
|
||||
|
||||
// We can assume this is a field for now.
|
||||
pSimObject->setPrefixedDataField(attributeName, NULL, valueBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseChildren);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Taml: Cannot parse children on a NULL object." );
|
||||
|
||||
// Fetch children count.
|
||||
U32 childrenCount;
|
||||
stream.read( &childrenCount );
|
||||
|
||||
// Finish if no children.
|
||||
if ( childrenCount == 0 )
|
||||
return;
|
||||
|
||||
// Fetch the Taml children.
|
||||
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
|
||||
|
||||
// Is this a sim set?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element found under parent but object cannot have children." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch any container child class specifier.
|
||||
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
|
||||
|
||||
// Iterate children.
|
||||
for ( U32 index = 0; index < childrenCount; ++ index )
|
||||
{
|
||||
// Parse child element.
|
||||
SimObject* pChildSimObject = parseElement( stream, versionId );
|
||||
|
||||
// Finish if child failed.
|
||||
if ( pChildSimObject == NULL )
|
||||
return;
|
||||
|
||||
// Do we have a container child class?
|
||||
if ( pContainerChildClass != NULL )
|
||||
{
|
||||
// Yes, so is the child object the correctly derived type?
|
||||
if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
|
||||
pChildSimObject->getClassName(),
|
||||
pSimObject->getClassName(),
|
||||
pContainerChildClass->getClassName() );
|
||||
|
||||
// NOTE: We can't delete the object as it may be referenced elsewhere!
|
||||
pChildSimObject = NULL;
|
||||
|
||||
// Skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add child.
|
||||
pChildren->addTamlChild( pChildSimObject );
|
||||
|
||||
// Find Taml callbacks for child.
|
||||
TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
|
||||
|
||||
// Do we have callbacks on the child?
|
||||
if ( pChildCallbacks != NULL )
|
||||
{
|
||||
// Yes, so perform callback.
|
||||
mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseCustomElement);
|
||||
|
||||
// Read custom node count.
|
||||
U32 customNodeCount;
|
||||
stream.read( &customNodeCount );
|
||||
|
||||
// Finish if no custom nodes.
|
||||
if ( customNodeCount == 0 )
|
||||
return;
|
||||
|
||||
// Iterate custom nodes.
|
||||
for ( U32 nodeIndex = 0; nodeIndex < customNodeCount; ++nodeIndex )
|
||||
{
|
||||
//Read custom node name.
|
||||
StringTableEntry nodeName = stream.readSTString();
|
||||
|
||||
// Add custom node.
|
||||
TamlCustomNode* pCustomNode = customNodes.addNode( nodeName );
|
||||
|
||||
// Parse the custom node.
|
||||
parseCustomNode( stream, pCustomNode, versionId );
|
||||
}
|
||||
|
||||
// Do we have callbacks?
|
||||
if ( pCallbacks == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Encountered custom data but object does not support custom data." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom read callback.
|
||||
mpTaml->tamlCustomRead( pCallbacks, customNodes );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId )
|
||||
{
|
||||
// Fetch if a proxy object.
|
||||
bool isProxyObject;
|
||||
stream.read( &isProxyObject );
|
||||
|
||||
// Is this a proxy object?
|
||||
if ( isProxyObject )
|
||||
{
|
||||
// Yes, so parse proxy object.
|
||||
SimObject* pProxyObject = parseElement( stream, versionId );
|
||||
|
||||
// Add child node.
|
||||
pCustomNode->addNode( pProxyObject );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so read custom node name.
|
||||
StringTableEntry nodeName = stream.readSTString();
|
||||
|
||||
// Add child node.
|
||||
TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
|
||||
|
||||
// Read child node text.
|
||||
char childNodeTextBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
|
||||
stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, childNodeTextBuffer );
|
||||
pChildNode->setNodeText( childNodeTextBuffer );
|
||||
|
||||
// Read child node count.
|
||||
U32 childNodeCount;
|
||||
stream.read( &childNodeCount );
|
||||
|
||||
// Do we have any children nodes?
|
||||
if ( childNodeCount > 0 )
|
||||
{
|
||||
// Yes, so parse children nodes.
|
||||
for( U32 childIndex = 0; childIndex < childNodeCount; ++childIndex )
|
||||
{
|
||||
// Parse child node.
|
||||
parseCustomNode( stream, pChildNode, versionId );
|
||||
}
|
||||
}
|
||||
|
||||
// Read child field count.
|
||||
U32 childFieldCount;
|
||||
stream.read( &childFieldCount );
|
||||
|
||||
// Do we have any child fields?
|
||||
if ( childFieldCount > 0 )
|
||||
{
|
||||
// Yes, so parse child fields.
|
||||
for( U32 childFieldIndex = 0; childFieldIndex < childFieldCount; ++childFieldIndex )
|
||||
{
|
||||
// Read field name.
|
||||
StringTableEntry fieldName = stream.readSTString();
|
||||
|
||||
// Read field value.
|
||||
char valueBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
|
||||
stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, valueBuffer );
|
||||
|
||||
// Add field.
|
||||
pChildNode->addField( fieldName, valueBuffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Engine/source/persistence/taml/binary/tamlBinaryReader.h
Normal file
68
Engine/source/persistence/taml/binary/tamlBinaryReader.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_BINARYREADER_H_
|
||||
#define _TAML_BINARYREADER_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlBinaryReader
|
||||
{
|
||||
public:
|
||||
TamlBinaryReader( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TamlBinaryReader() {}
|
||||
|
||||
/// Read.
|
||||
SimObject* read( FileStream& stream );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
|
||||
|
||||
typeObjectReferenceHash mObjectReferenceMap;
|
||||
|
||||
private:
|
||||
void resetParse( void );
|
||||
|
||||
SimObject* parseElement( Stream& stream, const U32 versionId );
|
||||
void parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId );
|
||||
void parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId );
|
||||
void parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId );
|
||||
void parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId );
|
||||
};
|
||||
|
||||
#endif // _TAML_BINARYREADER_H_
|
||||
297
Engine/source/persistence/taml/binary/tamlBinaryWriter.cpp
Normal file
297
Engine/source/persistence/taml/binary/tamlBinaryWriter.cpp
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/binary/tamlBinaryWriter.h"
|
||||
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#include "core/util/zip/zipSubStream.h"
|
||||
#endif
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlBinaryWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode, const bool compressed )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_Write);
|
||||
|
||||
// Write Taml signature.
|
||||
stream.writeString( StringTable->insert( TAML_SIGNATURE ) );
|
||||
|
||||
// Write version Id.
|
||||
stream.write( mVersionId );
|
||||
|
||||
// Write compressed flag.
|
||||
stream.write( compressed );
|
||||
|
||||
// Are we compressed?
|
||||
if ( compressed )
|
||||
{
|
||||
// yes, so attach zip stream.
|
||||
ZipSubWStream zipStream;
|
||||
zipStream.attachStream( &stream );
|
||||
|
||||
// Write element.
|
||||
writeElement( zipStream, pTamlWriteNode );
|
||||
|
||||
// Detach zip stream.
|
||||
zipStream.detachStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so write element.
|
||||
writeElement( stream, pTamlWriteNode );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteElement);
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch element name.
|
||||
const char* pElementName = pSimObject->getClassName();
|
||||
|
||||
// Write element name.
|
||||
stream.writeString( pElementName );
|
||||
|
||||
// Fetch object name.
|
||||
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
||||
|
||||
// Write object name.
|
||||
stream.writeString( pObjectName != NULL ? pObjectName : StringTable->EmptyString() );
|
||||
|
||||
// Fetch reference Id.
|
||||
const U32 tamlRefId = pTamlWriteNode->mRefId;
|
||||
|
||||
// Write reference Id.
|
||||
stream.write( tamlRefId );
|
||||
|
||||
// Do we have a reference to node?
|
||||
if ( pTamlWriteNode->mRefToNode != NULL )
|
||||
{
|
||||
// Yes, so fetch reference to Id.
|
||||
const U32 tamlRefToId = pTamlWriteNode->mRefToNode->mRefId;
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( tamlRefToId != 0, "Taml: Invalid reference to Id." );
|
||||
|
||||
// Write reference to Id.
|
||||
stream.write( tamlRefToId );
|
||||
|
||||
// Finished.
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so write no reference to Id.
|
||||
stream.write( 0 );
|
||||
|
||||
// Write attributes.
|
||||
writeAttributes( stream, pTamlWriteNode );
|
||||
|
||||
// Write children.
|
||||
writeChildren( stream, pTamlWriteNode );
|
||||
|
||||
// Write custom elements.
|
||||
writeCustomElements( stream, pTamlWriteNode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteAttributes);
|
||||
|
||||
// Fetch fields.
|
||||
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
||||
|
||||
// Write placeholder attribute count.
|
||||
stream.write( (U32)fields.size() );
|
||||
|
||||
// Finish if no fields.
|
||||
if ( fields.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
||||
{
|
||||
// Fetch field/value pair.
|
||||
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
||||
|
||||
// Write attribute.
|
||||
stream.writeString( pFieldValue->mName );
|
||||
stream.writeLongString( 4096, pFieldValue->mpValue );
|
||||
}
|
||||
}
|
||||
|
||||
void TamlBinaryWriter::writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteChildren);
|
||||
|
||||
// Fetch children.
|
||||
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
||||
|
||||
// Do we have any children?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so write no children.
|
||||
stream.write( (U32)0 );
|
||||
return;
|
||||
}
|
||||
|
||||
// Write children count.
|
||||
stream.write( (U32)pChildren->size() );
|
||||
|
||||
// Iterate children.
|
||||
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
||||
{
|
||||
// Write child.
|
||||
writeElement( stream, (*itr) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteCustomElements);
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
||||
|
||||
// Write custom node count.
|
||||
stream.write( (U32)nodes.size() );
|
||||
|
||||
// Finish if there are no nodes.
|
||||
if ( nodes.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate custom nodes.
|
||||
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
||||
{
|
||||
// Fetch the custom node.
|
||||
TamlCustomNode* pCustomNode = *customNodesItr;
|
||||
|
||||
// Write custom node name.
|
||||
stream.writeString( pCustomNode->getNodeName() );
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Write the custom node.
|
||||
writeCustomNode( stream, pChildNode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Is the node a proxy object?
|
||||
if ( pCustomNode->isProxyObject() )
|
||||
{
|
||||
// Yes, so flag as proxy object.
|
||||
stream.write( true );
|
||||
|
||||
// Write the element.
|
||||
writeElement( stream, pCustomNode->getProxyWriteNode() );
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so flag as custom node.
|
||||
stream.write( false );
|
||||
|
||||
// Write custom node name.
|
||||
stream.writeString( pCustomNode->getNodeName() );
|
||||
|
||||
// Write custom node text.
|
||||
stream.writeLongString(MAX_TAML_NODE_FIELDVALUE_LENGTH, pCustomNode->getNodeTextField().getFieldValue());
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Fetch child node count.
|
||||
const U32 childNodeCount = (U32)nodeChildren.size();
|
||||
|
||||
// Write custom node count.
|
||||
stream.write( childNodeCount );
|
||||
|
||||
// Do we have any children nodes.
|
||||
if ( childNodeCount > 0 )
|
||||
{
|
||||
// Yes, so iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Write the custom node.
|
||||
writeCustomNode( stream, pChildNode );
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch fields.
|
||||
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
||||
|
||||
// Fetch child field count.
|
||||
const U32 childFieldCount = (U32)fields.size();
|
||||
|
||||
// Write custom field count.
|
||||
stream.write( childFieldCount );
|
||||
|
||||
// Do we have any child fields?
|
||||
if ( childFieldCount > 0 )
|
||||
{
|
||||
// Yes, so iterate fields.
|
||||
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
||||
{
|
||||
// Fetch node field.
|
||||
const TamlCustomField* pField = *fieldItr;
|
||||
|
||||
// Write the node field.
|
||||
stream.writeString( pField->getFieldName() );
|
||||
stream.writeLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, pField->getFieldValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Engine/source/persistence/taml/binary/tamlBinaryWriter.h
Normal file
59
Engine/source/persistence/taml/binary/tamlBinaryWriter.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_BINARYWRITER_H_
|
||||
#define _TAML_BINARYWRITER_H_
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlBinaryWriter
|
||||
{
|
||||
public:
|
||||
TamlBinaryWriter( Taml* pTaml ) :
|
||||
mpTaml( pTaml ),
|
||||
mVersionId(2)
|
||||
{
|
||||
}
|
||||
virtual ~TamlBinaryWriter() {}
|
||||
|
||||
/// Write.
|
||||
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode, const bool compressed );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
const U32 mVersionId;
|
||||
|
||||
private:
|
||||
void writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode );
|
||||
};
|
||||
|
||||
#endif // _TAML_BINARYWRITER_H_
|
||||
747
Engine/source/persistence/taml/fsTinyXml.cpp
Normal file
747
Engine/source/persistence/taml/fsTinyXml.cpp
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "fsTinyXml.h"
|
||||
#include "console/console.h"
|
||||
|
||||
bool fsTiXmlDocument::LoadFile( const char * pFilename, TiXmlEncoding encoding )
|
||||
{
|
||||
// Expand the file-path.
|
||||
char filenameBuffer[1024];
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
#ifdef TORQUE_OS_ANDROID
|
||||
if (strlen(pFilename) > strlen(filenameBuffer)) {
|
||||
strcpy(filenameBuffer, pFilename);
|
||||
}
|
||||
#endif
|
||||
|
||||
// File open for read?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Read ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load document from stream.
|
||||
if ( !LoadFile( stream ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close the stream.
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fsTiXmlDocument::SaveFile( const char * pFilename ) const
|
||||
{
|
||||
// Expand the file-name into the file-path buffer.
|
||||
char filenameBuffer[1024];
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
// File opened?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Write ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = SaveFile(stream);
|
||||
|
||||
stream.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool fsTiXmlDocument::LoadFile( FileStream &stream, TiXmlEncoding encoding )
|
||||
{
|
||||
// Delete the existing data:
|
||||
Clear();
|
||||
//TODO: Can't clear location, investigate if this gives issues.
|
||||
//doc.location.Clear();
|
||||
|
||||
// Get the file size, so we can pre-allocate the string. HUGE speed impact.
|
||||
long length = stream.getStreamSize();
|
||||
|
||||
// Strange case, but good to handle up front.
|
||||
if ( length <= 0 )
|
||||
{
|
||||
SetError( TiXmlDocument::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subtle bug here. TinyXml did use fgets. But from the XML spec:
|
||||
// 2.11 End-of-Line Handling
|
||||
// <snip>
|
||||
// <quote>
|
||||
// ...the XML processor MUST behave as if it normalized all line breaks in external
|
||||
// parsed entities (including the document entity) on input, before parsing, by translating
|
||||
// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
|
||||
// a single #xA character.
|
||||
// </quote>
|
||||
//
|
||||
// It is not clear fgets does that, and certainly isn't clear it works cross platform.
|
||||
// Generally, you expect fgets to translate from the convention of the OS to the c/unix
|
||||
// convention, and not work generally.
|
||||
|
||||
/*
|
||||
while( fgets( buf, sizeof(buf), file ) )
|
||||
{
|
||||
data += buf;
|
||||
}
|
||||
*/
|
||||
|
||||
char* buf = new char[ length+1 ];
|
||||
buf[0] = 0;
|
||||
|
||||
if ( !stream.read( length, buf ) ) {
|
||||
delete [] buf;
|
||||
SetError( TiXmlDocument::TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process the buffer in place to normalize new lines. (See comment above.)
|
||||
// Copies from the 'p' to 'q' pointer, where p can advance faster if
|
||||
// a newline-carriage return is hit.
|
||||
//
|
||||
// Wikipedia:
|
||||
// Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
|
||||
// CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
|
||||
// * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
|
||||
// * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
|
||||
// * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
|
||||
|
||||
const char* p = buf; // the read head
|
||||
char* q = buf; // the write head
|
||||
const char CR = 0x0d;
|
||||
const char LF = 0x0a;
|
||||
|
||||
buf[length] = 0;
|
||||
while( *p ) {
|
||||
assert( p < (buf+length) );
|
||||
assert( q <= (buf+length) );
|
||||
assert( q <= p );
|
||||
|
||||
if ( *p == CR ) {
|
||||
*q++ = LF;
|
||||
p++;
|
||||
if ( *p == LF ) { // check for CR+LF (and skip LF)
|
||||
p++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*q++ = *p++;
|
||||
}
|
||||
}
|
||||
assert( q <= (buf+length) );
|
||||
*q = 0;
|
||||
|
||||
Parse( buf, 0, encoding );
|
||||
|
||||
delete [] buf;
|
||||
return !Error();
|
||||
}
|
||||
|
||||
bool fsTiXmlDocument::SaveFile( FileStream &stream ) const
|
||||
{
|
||||
if ( useMicrosoftBOM )
|
||||
{
|
||||
const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
|
||||
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
|
||||
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
|
||||
|
||||
stream.write( TIXML_UTF_LEAD_0 );
|
||||
stream.write( TIXML_UTF_LEAD_1 );
|
||||
stream.write( TIXML_UTF_LEAD_2 );
|
||||
}
|
||||
Print( stream, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
void fsTiXmlDocument::Print( FileStream& stream, int depth ) const
|
||||
{
|
||||
for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
|
||||
{
|
||||
//AttemptPrintTiNode(const_cast<TiXmlNode*>(node), stream, depth);
|
||||
dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth );
|
||||
stream.writeText( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlAttribute::Print( FileStream& stream, int depth, TIXML_STRING* str ) const
|
||||
{
|
||||
TIXML_STRING n, v;
|
||||
|
||||
TiXmlString value = TiXmlString(Value());
|
||||
|
||||
EncodeString( NameTStr(), &n );
|
||||
EncodeString( value, &v );
|
||||
|
||||
for ( int i=0; i< depth; i++ ) {
|
||||
stream.writeText( " " );
|
||||
}
|
||||
|
||||
if (value.find ('\"') == TIXML_STRING::npos) {
|
||||
const char* pValue = v.c_str();
|
||||
char buffer[4096];
|
||||
const S32 length = dSprintf(buffer, sizeof(buffer), "%s=\"%s\"", n.c_str(), pValue);
|
||||
stream.write(length, buffer);
|
||||
if ( str ) {
|
||||
(*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
|
||||
}
|
||||
}
|
||||
else {
|
||||
char buffer[4096];
|
||||
const S32 length = dSprintf(buffer, sizeof(buffer), "%s='%s'", n.c_str(), v.c_str());
|
||||
stream.write(length, buffer);
|
||||
if ( str ) {
|
||||
(*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlDeclaration::Print(FileStream& stream, int depth, TiXmlString* str) const
|
||||
{
|
||||
stream.writeStringBuffer( "<?xml " );
|
||||
if ( str ) (*str) += "<?xml ";
|
||||
|
||||
if ( !version.empty() ) {
|
||||
stream.writeFormattedBuffer( "version=\"%s\" ", version.c_str ());
|
||||
if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
|
||||
}
|
||||
if ( !encoding.empty() ) {
|
||||
stream.writeFormattedBuffer( "encoding=\"%s\" ", encoding.c_str ());
|
||||
if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
|
||||
}
|
||||
if ( !standalone.empty() ) {
|
||||
stream.writeFormattedBuffer( "standalone=\"%s\" ", standalone.c_str ());
|
||||
if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
|
||||
}
|
||||
stream.writeStringBuffer( "?>" );
|
||||
if ( str ) (*str) += "?>";
|
||||
}
|
||||
|
||||
void fsTiXmlElement::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
int i;
|
||||
for ( i=0; i<depth; i++ ) {
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
|
||||
stream.writeFormattedBuffer( "<%s", value.c_str() );
|
||||
|
||||
const TiXmlAttribute* attrib;
|
||||
for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
|
||||
{
|
||||
stream.writeStringBuffer( "\n" );
|
||||
dynamic_cast<const fsTiXmlAttribute*>(attrib)->Print( stream, depth+1 );
|
||||
}
|
||||
|
||||
// There are 3 different formatting approaches:
|
||||
// 1) An element without children is printed as a <foo /> node
|
||||
// 2) An element with only a text child is printed as <foo> text </foo>
|
||||
// 3) An element with children is printed on multiple lines.
|
||||
TiXmlNode* node;
|
||||
if ( !firstChild )
|
||||
{
|
||||
stream.writeStringBuffer( " />" );
|
||||
}
|
||||
else if ( firstChild == lastChild && firstChild->ToText() )
|
||||
{
|
||||
stream.writeStringBuffer( ">" );
|
||||
dynamic_cast<const fsTiXmlNode*>(firstChild)->Print( stream, depth + 1 );
|
||||
stream.writeFormattedBuffer( "</%s>", value.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.writeStringBuffer( ">" );
|
||||
|
||||
for ( node = firstChild; node; node=node->NextSibling() )
|
||||
{
|
||||
if ( !node->ToText() )
|
||||
{
|
||||
stream.writeStringBuffer( "\n" );
|
||||
}
|
||||
dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth+1 );
|
||||
}
|
||||
stream.writeStringBuffer( "\n" );
|
||||
for( i=0; i<depth; ++i ) {
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
stream.writeFormattedBuffer( "</%s>", value.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlComment::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
for ( int i=0; i<depth; i++ )
|
||||
{
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
stream.writeFormattedBuffer( "<!--%s-->", value.c_str() );
|
||||
}
|
||||
|
||||
void fsTiXmlText::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
if ( cdata )
|
||||
{
|
||||
int i;
|
||||
stream.writeStringBuffer( "\n" );
|
||||
for ( i=0; i<depth; i++ ) {
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
stream.writeFormattedBuffer( "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
|
||||
}
|
||||
else
|
||||
{
|
||||
TIXML_STRING buffer;
|
||||
EncodeString( value, &buffer );
|
||||
stream.writeFormattedBuffer( "%s", buffer.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlUnknown::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
for ( int i=0; i<depth; i++ )
|
||||
stream.writeStringBuffer( " " );
|
||||
|
||||
stream.writeFormattedBuffer( "<%s>", value.c_str() );
|
||||
}
|
||||
|
||||
static TiXmlNode* TiNodeIdentify( TiXmlNode* parent, const char* p, TiXmlEncoding encoding )
|
||||
{
|
||||
TiXmlNode* returnNode = 0;
|
||||
|
||||
p = TiXmlNode::SkipWhiteSpace( p, encoding );
|
||||
if( !p || !*p || *p != '<' )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = TiXmlNode::SkipWhiteSpace( p, encoding );
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// What is this thing?
|
||||
// - Elements start with a letter or underscore, but xml is reserved.
|
||||
// - Comments: <!--
|
||||
// - Decleration: <?xml
|
||||
// - Everthing else is unknown to tinyxml.
|
||||
//
|
||||
|
||||
const char* xmlHeader = { "<?xml" };
|
||||
const char* commentHeader = { "<!--" };
|
||||
const char* dtdHeader = { "<!" };
|
||||
const char* cdataHeader = { "<![CDATA[" };
|
||||
|
||||
if ( TiXmlNode::StringEqual( p, xmlHeader, true, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Declaration\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlDeclaration();
|
||||
}
|
||||
else if ( TiXmlNode::StringEqual( p, commentHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Comment\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlComment();
|
||||
}
|
||||
else if ( TiXmlNode::StringEqual( p, cdataHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing CDATA\n" );
|
||||
#endif
|
||||
TiXmlText* text = new fsTiXmlText( "" );
|
||||
text->SetCDATA( true );
|
||||
returnNode = text;
|
||||
}
|
||||
else if ( TiXmlNode::StringEqual( p, dtdHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(1)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
else if ( TiXmlNode::IsAlpha( *(p+1), encoding )
|
||||
|| *(p+1) == '_' )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Element\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlElement( "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(2)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
|
||||
if ( returnNode )
|
||||
{
|
||||
// Set the parent, so it can report errors
|
||||
returnNode->parent = parent;
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
TiXmlNode* fsTiXmlDocument::Identify( const char* p, TiXmlEncoding encoding )
|
||||
{
|
||||
return TiNodeIdentify(this, p, encoding);
|
||||
}
|
||||
|
||||
TiXmlNode* fsTiXmlElement::Identify( const char* p, TiXmlEncoding encoding )
|
||||
{
|
||||
return TiNodeIdentify(this, p, encoding);
|
||||
}
|
||||
|
||||
const char* fsTiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
|
||||
{
|
||||
p = SkipWhiteSpace( p, encoding );
|
||||
TiXmlDocument* document = GetDocument();
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
{
|
||||
data->Stamp( p, encoding );
|
||||
location = data->Cursor();
|
||||
}
|
||||
|
||||
if ( *p != '<' )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = SkipWhiteSpace( p+1, encoding );
|
||||
|
||||
// Read the name.
|
||||
const char* pErr = p;
|
||||
|
||||
p = ReadName( p, &value, encoding );
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIXML_STRING endTag ("</");
|
||||
endTag += value;
|
||||
|
||||
// Check for and read attributes. Also look for an empty
|
||||
// tag or an end tag.
|
||||
while ( p && *p )
|
||||
{
|
||||
pErr = p;
|
||||
p = SkipWhiteSpace( p, encoding );
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
if ( *p == '/' )
|
||||
{
|
||||
++p;
|
||||
// Empty tag.
|
||||
if ( *p != '>' )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
return (p+1);
|
||||
}
|
||||
else if ( *p == '>' )
|
||||
{
|
||||
// Done with attributes (if there were any.)
|
||||
// Read the value -- which can include other
|
||||
// elements -- read the end tag, and return.
|
||||
++p;
|
||||
p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
|
||||
if ( !p || !*p ) {
|
||||
// We were looking for the end tag, but found nothing.
|
||||
// Fix for [ 1663758 ] Failure to report error on bad XML
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We should find the end tag now
|
||||
// note that:
|
||||
// </foo > and
|
||||
// </foo>
|
||||
// are both valid end tags.
|
||||
if ( StringEqual( p, endTag.c_str(), false, encoding ) )
|
||||
{
|
||||
p += endTag.length();
|
||||
p = SkipWhiteSpace( p, encoding );
|
||||
if ( p && *p && *p == '>' ) {
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to read an attribute:
|
||||
TiXmlAttribute* attrib = new fsTiXmlAttribute();
|
||||
if ( !attrib )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
attrib->SetDocument( document );
|
||||
pErr = p;
|
||||
p = attrib->Parse( p, data, encoding );
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
|
||||
delete attrib;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle the strange case of double attributes:
|
||||
#ifdef TIXML_USE_STL
|
||||
TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
|
||||
#else
|
||||
TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
|
||||
#endif
|
||||
if ( node )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
|
||||
delete attrib;
|
||||
return 0;
|
||||
}
|
||||
|
||||
attributeSet.Add( attrib );
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
TiXmlNode* fsTiXmlNode::Identify(char const* p, TiXmlEncoding encoding)
|
||||
{
|
||||
TiXmlNode* returnNode = 0;
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
if( !p || !*p || *p != '<' )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// What is this thing?
|
||||
// - Elements start with a letter or underscore, but xml is reserved.
|
||||
// - Comments: <!--
|
||||
// - Decleration: <?xml
|
||||
// - Everthing else is unknown to tinyxml.
|
||||
//
|
||||
|
||||
const char* xmlHeader = { "<?xml" };
|
||||
const char* commentHeader = { "<!--" };
|
||||
const char* dtdHeader = { "<!" };
|
||||
const char* cdataHeader = { "<![CDATA[" };
|
||||
|
||||
if ( TiXmlBase::StringEqual( p, xmlHeader, true, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Declaration\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlDeclaration();
|
||||
}
|
||||
else if ( TiXmlBase::StringEqual( p, commentHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Comment\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlComment();
|
||||
}
|
||||
else if ( TiXmlBase::StringEqual( p, cdataHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing CDATA\n" );
|
||||
#endif
|
||||
fsTiXmlText* text = new fsTiXmlText( "" );
|
||||
text->SetCDATA( true );
|
||||
returnNode = text;
|
||||
}
|
||||
else if ( TiXmlBase::StringEqual( p, dtdHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(1)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
else if ( TiXmlBase::IsAlpha( *(p+1), encoding )
|
||||
|| *(p+1) == '_' )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Element\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlElement( "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(2)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
|
||||
if ( returnNode )
|
||||
{
|
||||
// Set the parent, so it can report errors
|
||||
returnNode->parent = this;
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
|
||||
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
|
||||
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
|
||||
|
||||
char const* fsTiXmlDocument::Parse(char const* p, TiXmlParsingData* prevData, TiXmlEncoding encoding)
|
||||
{
|
||||
ClearError();
|
||||
|
||||
// Parse away, at the document level. Since a document
|
||||
// contains nothing but other tags, most of what happens
|
||||
// here is skipping white space.
|
||||
if ( !p || !*p )
|
||||
{
|
||||
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that, for a document, this needs to come
|
||||
// before the while space skip, so that parsing
|
||||
// starts from the pointer we are given.
|
||||
location.Clear();
|
||||
if ( prevData )
|
||||
{
|
||||
location.row = prevData->Cursor().row;
|
||||
location.col = prevData->Cursor().col;
|
||||
}
|
||||
else
|
||||
{
|
||||
location.row = 0;
|
||||
location.col = 0;
|
||||
}
|
||||
TiXmlParsingData data( p, TabSize(), location.row, location.col );
|
||||
location = data.Cursor();
|
||||
|
||||
if ( encoding == TIXML_ENCODING_UNKNOWN )
|
||||
{
|
||||
// Check for the Microsoft UTF-8 lead bytes.
|
||||
const unsigned char* pU = (const unsigned char*)p;
|
||||
if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
|
||||
&& *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
|
||||
&& *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
|
||||
{
|
||||
encoding = TIXML_ENCODING_UTF8;
|
||||
useMicrosoftBOM = true;
|
||||
}
|
||||
}
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
if ( !p )
|
||||
{
|
||||
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ( p && *p )
|
||||
{
|
||||
TiXmlNode* node = fsTiXmlNode::Identify( p, encoding );
|
||||
if ( node )
|
||||
{
|
||||
p = node->Parse( p, &data, encoding );
|
||||
LinkEndChild( node );
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we get encoding info?
|
||||
if ( encoding == TIXML_ENCODING_UNKNOWN
|
||||
&& node->ToDeclaration() )
|
||||
{
|
||||
TiXmlDeclaration* dec = node->ToDeclaration();
|
||||
const char* enc = dec->Encoding();
|
||||
assert( enc );
|
||||
|
||||
if ( *enc == 0 )
|
||||
encoding = TIXML_ENCODING_UTF8;
|
||||
else if ( TiXmlBase::StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
|
||||
encoding = TIXML_ENCODING_UTF8;
|
||||
else if ( TiXmlBase::StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
|
||||
encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
|
||||
else
|
||||
encoding = TIXML_ENCODING_LEGACY;
|
||||
}
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
}
|
||||
|
||||
// Was this empty?
|
||||
if ( !firstChild ) {
|
||||
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// All is well.
|
||||
return p;
|
||||
}
|
||||
*/
|
||||
248
Engine/source/persistence/taml/fsTinyXml.h
Normal file
248
Engine/source/persistence/taml/fsTinyXml.h
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FSTINYXML_H_
|
||||
#define _FSTINYXML_H_
|
||||
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/stream/fileStream.h"
|
||||
#endif
|
||||
|
||||
class fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth ) const = 0;
|
||||
};
|
||||
|
||||
class fsTiXmlDocument : public TiXmlDocument, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
bool LoadFile( FileStream &stream, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
|
||||
bool SaveFile( FileStream &stream ) const;
|
||||
/// Load a file using the given filename. Returns true if successful.
|
||||
bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
|
||||
/// Save a file using the given filename. Returns true if successful.
|
||||
bool SaveFile( const char * filename ) const;
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlDocument* clone = new fsTiXmlDocument();
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
TiXmlNode* Identify( const char* p, TiXmlEncoding encoding );
|
||||
};
|
||||
|
||||
class fsTiXmlAttribute : public TiXmlAttribute, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth, TIXML_STRING* str ) const;
|
||||
virtual void Print( FileStream& stream, int depth) const
|
||||
{
|
||||
Print(stream, depth, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlDeclaration : public TiXmlDeclaration, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
fsTiXmlDeclaration(){};
|
||||
fsTiXmlDeclaration( const char* _version,
|
||||
const char* _encoding,
|
||||
const char* _standalone ) : TiXmlDeclaration(_version, _encoding, _standalone) { }
|
||||
|
||||
virtual void Print( FileStream& stream, int depth, TIXML_STRING* str ) const;
|
||||
virtual void Print( FileStream& stream, int depth) const
|
||||
{
|
||||
Print(stream, depth, 0);
|
||||
}
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
fsTiXmlDeclaration* clone = new fsTiXmlDeclaration();
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlElement : public TiXmlElement, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
fsTiXmlElement(const char* in_value) : TiXmlElement(in_value) { }
|
||||
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlElement* clone = new fsTiXmlElement( Value() );
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
|
||||
void SetAttribute( const char* name, const char * _value )
|
||||
{
|
||||
TiXmlAttribute* attrib = attributeSet.Find( name );
|
||||
if(!attrib)
|
||||
{
|
||||
attrib = new fsTiXmlAttribute();
|
||||
attributeSet.Add( attrib );
|
||||
attrib->SetName( name );
|
||||
}
|
||||
if ( attrib ) {
|
||||
attrib->SetValue( _value );
|
||||
}
|
||||
}
|
||||
void SetAttribute( const char * name, int value )
|
||||
{
|
||||
TiXmlAttribute* attrib = attributeSet.Find( name );
|
||||
if(!attrib)
|
||||
{
|
||||
attrib = new fsTiXmlAttribute();
|
||||
attributeSet.Add( attrib );
|
||||
attrib->SetName( name );
|
||||
}
|
||||
if ( attrib ) {
|
||||
attrib->SetIntValue( value );
|
||||
}
|
||||
}
|
||||
TiXmlNode* Identify( const char* p, TiXmlEncoding encoding );
|
||||
virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
|
||||
};
|
||||
|
||||
class fsTiXmlComment : public TiXmlComment, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlComment* clone = new fsTiXmlComment();
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlText : public TiXmlText, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
fsTiXmlText (const char * initValue ) : TiXmlText(initValue) { }
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlText* clone = 0;
|
||||
clone = new fsTiXmlText( "" );
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlUnknown : public TiXmlUnknown, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlUnknown* clone = new fsTiXmlUnknown();
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
static bool AttemptPrintTiNode(class fsTiXmlDocument* node, FileStream& stream, int depth)
|
||||
{
|
||||
fsTiXmlDocument* fsDoc = dynamic_cast<fsTiXmlDocument*>(node);
|
||||
if(fsDoc != NULL)
|
||||
{
|
||||
fsDoc->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlUnknown* fsUnk = dynamic_cast<fsTiXmlUnknown*>(node);
|
||||
if(fsUnk != NULL)
|
||||
{
|
||||
fsUnk->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlText* fsTxt = dynamic_cast<fsTiXmlText*>(node);
|
||||
if(fsTxt != NULL)
|
||||
{
|
||||
fsTxt->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlComment* fsCom = dynamic_cast<fsTiXmlComment*>(node);
|
||||
if(fsCom != NULL)
|
||||
{
|
||||
fsCom->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlElement* fsElm = dynamic_cast<fsTiXmlElement*>(node);
|
||||
if(fsElm != NULL)
|
||||
{
|
||||
fsElm->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlDeclaration* fsDec = dynamic_cast<fsTiXmlDeclaration*>(node);
|
||||
if(fsDec != NULL)
|
||||
{
|
||||
fsDec->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlAttribute* fsAtt = dynamic_cast<fsTiXmlAttribute*>(node);
|
||||
if(fsAtt != NULL)
|
||||
{
|
||||
fsAtt->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //_FSTINYXML_H_
|
||||
258
Engine/source/persistence/taml/json/tamlJSONParser.cpp
Normal file
258
Engine/source/persistence/taml/json/tamlJSONParser.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/json/tamlJSONParser.h"
|
||||
#include "persistence/taml/tamlVisitor.h"
|
||||
#include "console/console.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlJSONParser::accept( const char* pFilename, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONParser_Accept);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." );
|
||||
|
||||
// Expand the file-path.
|
||||
char filenameBuffer[1024];
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
// File open for read?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read JSON file.
|
||||
const U32 streamSize = stream.getStreamSize();
|
||||
FrameTemp<char> jsonText( streamSize + 1 );
|
||||
if ( !stream.read( streamSize, jsonText ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONParser::parse() - Could not load Taml JSON file from stream.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create JSON document.
|
||||
rapidjson::Document inputDocument;
|
||||
inputDocument.Parse<0>( jsonText );
|
||||
|
||||
// Close the stream.
|
||||
stream.close();
|
||||
|
||||
// Check the document is valid.
|
||||
if ( inputDocument.GetType() != rapidjson::kObjectType )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONParser::parse() - Load Taml JSON file from stream but was invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set parsing filename.
|
||||
setParsingFilename( filenameBuffer );
|
||||
|
||||
// Flag document as not dirty.
|
||||
mDocumentDirty = false;
|
||||
|
||||
// Fetch the root.
|
||||
rapidjson::Value::MemberIterator rootItr = inputDocument.MemberBegin();
|
||||
|
||||
// Parse root value.
|
||||
parseType( rootItr, visitor, true );
|
||||
|
||||
// Reset parsing filename.
|
||||
setParsingFilename( StringTable->EmptyString() );
|
||||
|
||||
// Finish if the document is not dirty.
|
||||
if ( !mDocumentDirty )
|
||||
return true;
|
||||
|
||||
// Open for write?
|
||||
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for write.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create output document.
|
||||
rapidjson::Document outputDocument;
|
||||
outputDocument.AddMember( rootItr->name, rootItr->value, outputDocument.GetAllocator() );
|
||||
|
||||
// Write document to stream.
|
||||
rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
|
||||
outputDocument.Accept( jsonStreamWriter );
|
||||
|
||||
// Close the stream.
|
||||
stream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlJSONParser::parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONParser_ParseType);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& typeName = memberItr->name;
|
||||
rapidjson::Value& typeValue = memberItr->value;
|
||||
|
||||
// Create a visitor property state.
|
||||
TamlVisitor::PropertyState propertyState;
|
||||
propertyState.setObjectName( typeName.GetString(), isRoot );
|
||||
|
||||
// Parse field members.
|
||||
for( rapidjson::Value::MemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr )
|
||||
{
|
||||
// Fetch value.
|
||||
const rapidjson::Value& fieldName = fieldMemberItr->name;
|
||||
rapidjson::Value& fieldValue = fieldMemberItr->value;
|
||||
|
||||
// Skip if not a field.
|
||||
if ( fieldValue.IsObject() )
|
||||
continue;
|
||||
|
||||
char valueBuffer[4096];
|
||||
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), fieldValue, fieldName.GetString() ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlJSONParser::parseTyoe() - Could not interpret value for field '%s'", fieldName.GetString() );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Configure property state.
|
||||
propertyState.setProperty( fieldName.GetString(), valueBuffer );
|
||||
|
||||
// Visit this attribute.
|
||||
const bool visitStatus = visitor.visit( *this, propertyState );
|
||||
|
||||
// Was the property value changed?
|
||||
if ( propertyState.getPropertyValueDirty() )
|
||||
{
|
||||
// Yes, so update the attribute.
|
||||
fieldValue.SetString( propertyState.getPropertyValue() );
|
||||
|
||||
// Flag the document as dirty.
|
||||
mDocumentDirty = true;
|
||||
}
|
||||
|
||||
// Finish if requested.
|
||||
if ( !visitStatus )
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish if only the root is needed.
|
||||
if ( visitor.wantsRootOnly() )
|
||||
return false;
|
||||
|
||||
// Parse children and custom node members.
|
||||
for( rapidjson::Value::MemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr )
|
||||
{
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& objectValue = objectMemberItr->value;
|
||||
|
||||
// Skip if not an object.
|
||||
if ( !objectValue.IsObject() )
|
||||
continue;
|
||||
|
||||
// Parse the type.
|
||||
if ( !parseType( objectMemberItr, visitor, false ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlJSONParser::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONParser_ParseStringValue);
|
||||
|
||||
// Handle field value appropriately.
|
||||
|
||||
if ( value.IsString() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%s", value.GetString() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsNumber() )
|
||||
{
|
||||
if ( value.IsInt() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsInt64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsDouble() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( value.IsBool() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetBool() );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to get value type.
|
||||
Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName );
|
||||
return false;
|
||||
}
|
||||
|
||||
57
Engine/source/persistence/taml/json/tamlJSONParser.h
Normal file
57
Engine/source/persistence/taml/json/tamlJSONParser.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_JSONPARSER_H_
|
||||
#define _TAML_JSONPARSER_H_
|
||||
|
||||
#ifndef _TAML_PARSER_H_
|
||||
#include "persistence/taml/tamlParser.h"
|
||||
#endif
|
||||
|
||||
/// RapidJson.
|
||||
#include "persistence/rapidjson/document.h"
|
||||
#include "persistence/rapidjson/prettywriter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlJSONParser : public TamlParser
|
||||
{
|
||||
public:
|
||||
TamlJSONParser() {}
|
||||
virtual ~TamlJSONParser() {}
|
||||
|
||||
/// Whether the parser can change a property or not.
|
||||
virtual bool canChangeProperty( void ) { return true; }
|
||||
|
||||
/// Accept visitor.
|
||||
virtual bool accept( const char* pFilename, TamlVisitor& visitor );
|
||||
|
||||
private:
|
||||
inline bool parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot );
|
||||
inline bool parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName );
|
||||
|
||||
bool mDocumentDirty;
|
||||
};
|
||||
|
||||
#endif // _TAML_JSONPARSER_H_
|
||||
686
Engine/source/persistence/taml/json/tamlJSONReader.cpp
Normal file
686
Engine/source/persistence/taml/json/tamlJSONReader.cpp
Normal file
|
|
@ -0,0 +1,686 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/json/tamlJSONReader.h"
|
||||
#include "persistence/taml/json/tamlJSONWriter.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/strings/stringUnit.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlJSONReader::read( FileStream& stream )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_Read);
|
||||
|
||||
// Read JSON file.
|
||||
const U32 streamSize = stream.getStreamSize();
|
||||
FrameTemp<char> jsonText( streamSize + 1 );
|
||||
if ( !stream.read( streamSize, jsonText ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONReader::read() - Could not load Taml JSON file from stream.");
|
||||
return NULL;
|
||||
}
|
||||
jsonText[streamSize] = '\0';
|
||||
|
||||
// Create JSON document.
|
||||
rapidjson::Document document;
|
||||
document.Parse<0>( jsonText );
|
||||
|
||||
// Check the document is valid.
|
||||
if ( document.GetType() != rapidjson::kObjectType )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONReader::read() - Load Taml JSON file from stream but was invalid.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse root value.
|
||||
SimObject* pSimObject = parseType( document.MemberBegin() );
|
||||
|
||||
// Reset parse.
|
||||
resetParse();
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONReader::resetParse( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ResetParse);
|
||||
|
||||
// Clear object reference map.
|
||||
mObjectReferenceMap.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlJSONReader::parseType( const rapidjson::Value::ConstMemberIterator& memberItr )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseType);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& typeName = memberItr->name;
|
||||
const rapidjson::Value& typeValue = memberItr->value;
|
||||
|
||||
// Is value an object?
|
||||
if ( !typeValue.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlJSONReader::parseType() - Cannot process type '%s' as it is not an object.", typeName.GetString() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Fetch engine type name (demangled).
|
||||
StringTableEntry engineTypeName = getDemangledName( typeName.GetString() );
|
||||
|
||||
// Fetch reference to Id.
|
||||
const U32 tamlRefToId = getTamlRefToId( typeValue );
|
||||
|
||||
// Do we have a reference to Id?
|
||||
if ( tamlRefToId != 0 )
|
||||
{
|
||||
// Yes, so fetch reference.
|
||||
typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
|
||||
|
||||
// Did we find the reference?
|
||||
if ( referenceItr == mObjectReferenceMap.end() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlJSONReader::parseType() - Could not find a reference Id of '%d'", tamlRefToId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return referenceItr->value;
|
||||
}
|
||||
|
||||
// No, so fetch reference Id.
|
||||
const U32 tamlRefId = getTamlRefId( typeValue );
|
||||
|
||||
// Create type.
|
||||
SimObject* pSimObject = Taml::createType( engineTypeName, mpTaml );
|
||||
|
||||
// Finish if we couldn't create the type.
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Find Taml callbacks.
|
||||
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
|
||||
|
||||
TamlCustomNodes customNodes;
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPreRead( pCallbacks );
|
||||
}
|
||||
|
||||
// Parse field members.
|
||||
for( rapidjson::Value::ConstMemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr )
|
||||
{
|
||||
// Fetch value.
|
||||
const rapidjson::Value& fieldValue = fieldMemberItr->value;
|
||||
|
||||
// Skip if not a field.
|
||||
if ( fieldValue.IsObject() )
|
||||
continue;
|
||||
|
||||
// Parse as field.
|
||||
parseField( fieldMemberItr, pSimObject );
|
||||
}
|
||||
|
||||
// Fetch object name.
|
||||
StringTableEntry objectName = StringTable->insert( getTamlObjectName( typeValue ) );
|
||||
|
||||
// Does the object require a name?
|
||||
if ( objectName == StringTable->EmptyString() )
|
||||
{
|
||||
// No, so just register anonymously.
|
||||
pSimObject->registerObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so register a named object.
|
||||
pSimObject->registerObject( objectName );
|
||||
|
||||
// Was the name assigned?
|
||||
if ( pSimObject->getName() != objectName )
|
||||
{
|
||||
// No, so warn that the name was rejected.
|
||||
Con::warnf( "Taml::parseType() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.",
|
||||
engineTypeName, objectName );
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( tamlRefId != 0 )
|
||||
{
|
||||
// Yes, so insert reference.
|
||||
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
|
||||
}
|
||||
|
||||
// Parse children and custom node members.
|
||||
for( rapidjson::Value::ConstMemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr )
|
||||
{
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& objectName = objectMemberItr->name;
|
||||
const rapidjson::Value& objectValue = objectMemberItr->value;
|
||||
|
||||
// Skip if not an object.
|
||||
if ( !objectValue.IsObject() )
|
||||
continue;
|
||||
|
||||
// Find the period character in the name.
|
||||
const char* pPeriod = dStrchr( objectName.GetString(), '.' );
|
||||
|
||||
// Did we find the period?
|
||||
if ( pPeriod == NULL )
|
||||
{
|
||||
// No, so parse child object.
|
||||
parseChild( objectMemberItr, pSimObject );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, so parse custom object.
|
||||
parseCustom( objectMemberItr, pSimObject, pPeriod+1, customNodes );
|
||||
}
|
||||
|
||||
// Call custom read.
|
||||
if ( pCallbacks )
|
||||
{
|
||||
mpTaml->tamlCustomRead( pCallbacks, customNodes );
|
||||
mpTaml->tamlPostRead( pCallbacks, customNodes );
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseField);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& name = memberItr->name;
|
||||
const rapidjson::Value& value = memberItr->value;
|
||||
|
||||
// Insert the field name.
|
||||
StringTableEntry fieldName = StringTable->insert( name.GetString() );
|
||||
|
||||
// Ignore if this is a Taml attribute.
|
||||
if ( fieldName == tamlRefIdName ||
|
||||
fieldName == tamlRefToIdName ||
|
||||
fieldName == tamlNamedObjectName )
|
||||
return;
|
||||
|
||||
// Get field value.
|
||||
char valueBuffer[4096];
|
||||
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), value, fieldName ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseField() Could not interpret value for field '%s'", fieldName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Set field.
|
||||
pSimObject->setPrefixedDataField(fieldName, NULL, valueBuffer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseChild);
|
||||
|
||||
// Fetch name.
|
||||
const rapidjson::Value& name = memberItr->name;
|
||||
|
||||
// Fetch the Taml children.
|
||||
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
|
||||
|
||||
// Is this a Taml child?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml::parseChild() - Child member '%s' found under parent '%s' but object cannot have children.",
|
||||
name.GetString(),
|
||||
pSimObject->getClassName() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch any container child class specifier.
|
||||
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
|
||||
|
||||
// Parse child member.
|
||||
SimObject* pChildSimObject = parseType( memberItr );
|
||||
|
||||
// Finish if the child was not created.
|
||||
if ( pChildSimObject == NULL )
|
||||
return;
|
||||
|
||||
// Do we have a container child class?
|
||||
if ( pContainerChildClass != NULL )
|
||||
{
|
||||
// Yes, so is the child object the correctly derived type?
|
||||
if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml::parseChild() - Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
|
||||
pChildSimObject->getClassName(),
|
||||
pSimObject->getClassName(),
|
||||
pContainerChildClass->getClassName() );
|
||||
|
||||
// NOTE: We can't delete the object as it may be referenced elsewhere!
|
||||
pChildSimObject = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add child.
|
||||
pChildren->addTamlChild( pChildSimObject );
|
||||
|
||||
// Find Taml callbacks for child.
|
||||
TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
|
||||
|
||||
// Do we have callbacks on the child?
|
||||
if ( pChildCallbacks != NULL )
|
||||
{
|
||||
// Yes, so perform callback.
|
||||
mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseCustom);
|
||||
|
||||
// Fetch value.
|
||||
const rapidjson::Value& value = memberItr->value;
|
||||
|
||||
// Add custom node.
|
||||
TamlCustomNode* pCustomNode = customNodes.addNode( pCustomNodeName );
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator customMemberItr = value.MemberBegin(); customMemberItr != value.MemberEnd(); ++customMemberItr )
|
||||
{
|
||||
// Fetch value.
|
||||
const rapidjson::Value& customValue = customMemberItr->value;
|
||||
|
||||
// Is the member an object?
|
||||
if ( !customValue.IsObject() && !customValue.IsArray() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::parseCustom() - Cannot process custom node name '%s' member as child value is not an object or array.", pCustomNodeName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse custom node.
|
||||
parseCustomNode( customMemberItr, pCustomNode );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseCustomNode);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& name = memberItr->name;
|
||||
const rapidjson::Value& value = memberItr->value;
|
||||
|
||||
// Is the value an object?
|
||||
if ( value.IsObject() )
|
||||
{
|
||||
// Yes, so is the node a proxy object?
|
||||
if ( getTamlRefId( value ) != 0 || getTamlRefToId( value ) != 0 )
|
||||
{
|
||||
// Yes, so parse proxy object.
|
||||
SimObject* pProxyObject = parseType( memberItr );
|
||||
|
||||
// Add child node.
|
||||
pCustomNode->addNode( pProxyObject );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char valueBuffer[4096];
|
||||
|
||||
// Fetch the node name.
|
||||
StringTableEntry nodeName = getDemangledName( name.GetString() );
|
||||
|
||||
// Yes, so add child node.
|
||||
TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
|
||||
|
||||
// Is the value an array?
|
||||
if ( value.IsArray() )
|
||||
{
|
||||
// Yes, so does it have a single entry?
|
||||
if ( value.Size() == 1 )
|
||||
{
|
||||
// Yes, so parse the node text.
|
||||
if ( parseStringValue( valueBuffer, sizeof(valueBuffer), value.Begin(), nodeName ) )
|
||||
{
|
||||
pChildNode->setNodeText( valueBuffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", nodeName );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", nodeName );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate child members.
|
||||
for( rapidjson::Value::ConstMemberIterator childMemberItr = value.MemberBegin(); childMemberItr != value.MemberEnd(); ++childMemberItr )
|
||||
{
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& childName = childMemberItr->name;
|
||||
const rapidjson::Value& childValue = childMemberItr->value;
|
||||
|
||||
// Fetch the field name.
|
||||
StringTableEntry fieldName = StringTable->insert( childName.GetString() );
|
||||
|
||||
// Is the value an array?
|
||||
if ( childValue.IsArray() )
|
||||
{
|
||||
// Yes, so does it have a single entry?
|
||||
if ( childValue.Size() == 1 )
|
||||
{
|
||||
// Yes, so parse the node text.
|
||||
if ( parseStringValue( valueBuffer, sizeof(valueBuffer), *childValue.Begin(), fieldName ) )
|
||||
{
|
||||
// Yes, so add sub-child node.
|
||||
TamlCustomNode* pSubChildNode = pChildNode->addNode( fieldName );
|
||||
|
||||
// Set sub-child text.
|
||||
pSubChildNode->setNodeText( valueBuffer );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", fieldName );
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", fieldName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Is the member an object?
|
||||
if ( childValue.IsObject() )
|
||||
{
|
||||
// Yes, so parse custom node.
|
||||
parseCustomNode( childMemberItr, pChildNode );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore if this is a Taml attribute.
|
||||
if ( fieldName == tamlRefIdName ||
|
||||
fieldName == tamlRefToIdName ||
|
||||
fieldName == tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Parse string value.
|
||||
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), childValue, childName.GetString() ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Could not interpret value for field '%s'", fieldName );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add node field.
|
||||
pChildNode->addField( fieldName, valueBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline StringTableEntry TamlJSONReader::getDemangledName( const char* pMangledName )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetDemangledName);
|
||||
|
||||
// Is the type name mangled?
|
||||
if ( StringUnit::getUnitCount( pMangledName, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) > 1 )
|
||||
{
|
||||
// Yes, so fetch type name portion.
|
||||
return StringTable->insert( StringUnit::getUnit( pMangledName, 0, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) );
|
||||
}
|
||||
|
||||
// No, so use all the type name.
|
||||
return StringTable->insert( pMangledName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlJSONReader::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseStringValue);
|
||||
|
||||
// Handle field value appropriately.
|
||||
|
||||
if ( value.IsString() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%s", value.GetString() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsNumber() )
|
||||
{
|
||||
if ( value.IsInt() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsInt64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsDouble() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( value.IsBool() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetBool() );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to get value type.
|
||||
Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName );
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline U32 TamlJSONReader::getTamlRefId( const rapidjson::Value& value )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetTamlRefId);
|
||||
|
||||
// Is the value an object?
|
||||
if ( !value.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefId() - Cannot get '%s' member as value is not an object.", tamlRefIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
|
||||
{
|
||||
// Insert member name.
|
||||
StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefIdName )
|
||||
continue;
|
||||
|
||||
// Is the value an integer?
|
||||
if ( !memberItr->value.IsInt() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefId() - Found '%s' member but it is not an integer.", tamlRefIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return it.
|
||||
return (U32)memberItr->value.GetInt();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline U32 TamlJSONReader::getTamlRefToId( const rapidjson::Value& value )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetTamlRefToId);
|
||||
|
||||
// Is the value an object?
|
||||
if ( !value.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefToId() - Cannot get '%s' member as value is not an object.", tamlRefToIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
|
||||
{
|
||||
// Insert member name.
|
||||
StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefToIdName )
|
||||
continue;
|
||||
|
||||
// Is the value an integer?
|
||||
if ( !memberItr->value.IsInt() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefToId() - Found '%s' member but it is not an integer.", tamlRefToIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return it.
|
||||
return (U32)memberItr->value.GetInt();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline const char* TamlJSONReader::getTamlObjectName( const rapidjson::Value& value )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetTamlObjectName);
|
||||
|
||||
// Is the value an object?
|
||||
if ( !value.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlObjectName() - Cannot get '%s' member as value is not an object.", tamlNamedObjectName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
|
||||
{
|
||||
// Insert member name.
|
||||
StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Is the value an integer?
|
||||
if ( !memberItr->value.IsString() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlObjectName() - Found '%s' member but it is not a string.", tamlNamedObjectName );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return it.
|
||||
return memberItr->value.GetString();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
Engine/source/persistence/taml/json/tamlJSONReader.h
Normal file
76
Engine/source/persistence/taml/json/tamlJSONReader.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_JSONREADER_H_
|
||||
#define _TAML_JSONREADER_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
/// RapidJson.
|
||||
#include "persistence/rapidjson/document.h"
|
||||
#include "persistence/rapidjson/prettywriter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlJSONReader
|
||||
{
|
||||
public:
|
||||
TamlJSONReader( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
|
||||
virtual ~TamlJSONReader() {}
|
||||
|
||||
/// Read.
|
||||
SimObject* read( FileStream& stream );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
|
||||
typeObjectReferenceHash mObjectReferenceMap;
|
||||
|
||||
private:
|
||||
void resetParse( void );
|
||||
|
||||
SimObject* parseType( const rapidjson::Value::ConstMemberIterator& memberItr );
|
||||
inline void parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject );
|
||||
inline void parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject );
|
||||
inline void parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes );
|
||||
inline void parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode );
|
||||
|
||||
inline StringTableEntry getDemangledName( const char* pMangledName );
|
||||
inline bool parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName );
|
||||
inline U32 getTamlRefId( const rapidjson::Value& value );
|
||||
inline U32 getTamlRefToId( const rapidjson::Value& value );
|
||||
inline const char* getTamlObjectName( const rapidjson::Value& value );
|
||||
};
|
||||
|
||||
#endif // _TAML_JSONREADER_H_
|
||||
367
Engine/source/persistence/taml/json/tamlJSONWriter.cpp
Normal file
367
Engine/source/persistence/taml/json/tamlJSONWriter.cpp
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/json/tamlJSONWriter.h"
|
||||
|
||||
#include "core/stringTable.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// These are the characters allowed that separate the type-name from the type.
|
||||
// These separators can be used to ensure that each member in an object has
|
||||
// a unique name.
|
||||
//
|
||||
// It is important to understand that TAML does not require entries to be unique
|
||||
// but technically the RFC4627 spec states it as "should" be seems to be taken
|
||||
// as MUST. See here: http://www.ietf.org/rfc/rfc4627.txt
|
||||
//
|
||||
// They can be placed as a suffix to the type-name. The very first occurance
|
||||
// is used to split the type-name from the suffix so you can form whatever
|
||||
// suffix you like to make entries unique.
|
||||
// Examples are "Sprite 0", "Sprite*0", "Sprite[0]", "Sprite,0" etc.
|
||||
//
|
||||
// Type-names can legally consist of a-z, A-Z, 0-9 and "_" (underscore) so these
|
||||
// characters cannot be used for mangling. Feel free to add any characters you
|
||||
// require to this list.
|
||||
StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS = StringTable->insert(" !$%^&*()-+{}[]@:~#|\\/?<>,.\n\r\t");
|
||||
|
||||
// This is the "dSprintf" format that the JSON writer uses to encode each
|
||||
// member if JSON_RFC4627 mode is on. You are free to change this as long as
|
||||
// you ensure that it starts with the "%s" character (which represents the type name)
|
||||
// and is immediately followed by at least a single mangling character and that the
|
||||
// "%d" is present as that represents the automatically-added member index.
|
||||
StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT = StringTable->insert( "%s[%d]" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlJSONWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_Write);
|
||||
|
||||
// Create document.
|
||||
rapidjson::Document document;
|
||||
document.SetObject();
|
||||
|
||||
// Compile the root type.
|
||||
rapidjson::Value rootValue(rapidjson::kObjectType);
|
||||
compileType( document, &rootValue, NULL, pTamlWriteNode, -1 );
|
||||
|
||||
// Write document to stream.
|
||||
rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
|
||||
document.Accept( jsonStreamWriter );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileType);
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch JSON strict flag (don't use it if member index is set to not use it).
|
||||
const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
|
||||
|
||||
// Fetch element name (mangled or not).
|
||||
StringTableEntry elementName = jsonStrict ? getManagedName( pSimObject->getClassName(), memberIndex ) : pSimObject->getClassName();
|
||||
|
||||
// Is there a parent value?
|
||||
if ( pParentValue == NULL )
|
||||
{
|
||||
// No, so add as document root value member.
|
||||
pTypeValue = &((document.AddMember( elementName, *pTypeValue, allocator ).MemberEnd()-1)->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so add as a parent value member.
|
||||
pTypeValue = &((pParentValue->AddMember( elementName, *pTypeValue, allocator ).MemberEnd()-1)->value);
|
||||
}
|
||||
|
||||
// Fetch reference Id.
|
||||
const U32 referenceId = pTamlWriteNode->mRefId;
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( referenceId != 0 )
|
||||
{
|
||||
// Yes, so set reference Id.
|
||||
rapidjson::Value value;
|
||||
value.SetInt( referenceId );
|
||||
pTypeValue->AddMember( tamlRefIdName, value, allocator );
|
||||
}
|
||||
// Do we have a reference to node?
|
||||
else if ( pTamlWriteNode->mRefToNode != NULL )
|
||||
{
|
||||
// Yes, so fetch reference to Id.
|
||||
const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId;
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." );
|
||||
|
||||
// Set reference to Id.
|
||||
rapidjson::Value value;
|
||||
value.SetInt( referenceToId );
|
||||
pTypeValue->AddMember( tamlRefToIdName, value, allocator );
|
||||
|
||||
// Finish because we're a reference to another object.
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch object name.
|
||||
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
||||
|
||||
// Do we have a name?
|
||||
if ( pObjectName != NULL )
|
||||
{
|
||||
// Yes, so set name.
|
||||
rapidjson::Value value;
|
||||
value.SetString( pObjectName, dStrlen(pObjectName), allocator );
|
||||
pTypeValue->AddMember( tamlNamedObjectName, value, allocator );
|
||||
}
|
||||
|
||||
// Compile field.
|
||||
compileFields( document, pTypeValue, pTamlWriteNode );
|
||||
|
||||
// Fetch children.
|
||||
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
||||
|
||||
// Do we have any children?
|
||||
if ( pChildren )
|
||||
{
|
||||
S32 childMemberIndex = 0;
|
||||
|
||||
// Yes, so iterate children.
|
||||
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
||||
{
|
||||
// Compile child type.
|
||||
rapidjson::Value childValue(rapidjson::kObjectType);
|
||||
compileType( document, &childValue, pTypeValue, (*itr), childMemberIndex++ );
|
||||
}
|
||||
}
|
||||
|
||||
// Compile custom.
|
||||
compileCustom( document, pTypeValue, pTamlWriteNode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileFields);
|
||||
|
||||
// Fetch fields.
|
||||
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
||||
|
||||
// Ignore if no fields.
|
||||
if ( fields.size() == 0 )
|
||||
return;
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Iterate fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
||||
{
|
||||
// Fetch field/value pair.
|
||||
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
||||
|
||||
// Set field attribute.
|
||||
rapidjson::Value value;
|
||||
value.SetString( pFieldValue->mpValue, dStrlen(pFieldValue->mpValue), allocator );
|
||||
pTypeValue->AddMember( pFieldValue->mName, value, allocator );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileCustom);
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
||||
|
||||
// Finish if no custom nodes to process.
|
||||
if ( nodes.size() == 0 )
|
||||
return;
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch element name.
|
||||
const char* pElementName = pSimObject->getClassName();
|
||||
|
||||
// Iterate custom nodes.
|
||||
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
||||
{
|
||||
// Fetch the custom node.
|
||||
TamlCustomNode* pCustomNode = *customNodesItr;
|
||||
|
||||
// Format extended element name.
|
||||
char extendedElementNameBuffer[256];
|
||||
dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pElementName, pCustomNode->getNodeName() );
|
||||
StringTableEntry elementNameEntry = StringTable->insert( extendedElementNameBuffer );
|
||||
|
||||
rapidjson::Value elementValue(rapidjson::kObjectType);
|
||||
rapidjson::Value* pElementValue = &((pTypeValue->AddMember( elementNameEntry, elementValue, allocator ).MemberEnd()-1)->value);
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
S32 childMemberIndex = 0;
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the custom node.
|
||||
compileCustomNode( document, pElementValue, pChildNode, childMemberIndex++ );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pTypeValue->MemberBegin() == pTypeValue->MemberEnd() )
|
||||
{
|
||||
// Yes, so delete the member.
|
||||
pElementValue->SetNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileCustomNode);
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
|
||||
return;
|
||||
|
||||
// Is the node a proxy object?
|
||||
if ( pCustomNode->isProxyObject() )
|
||||
{
|
||||
// Yes, so write the proxy object.
|
||||
rapidjson::Value proxyValue(rapidjson::kObjectType);
|
||||
compileType( document, &proxyValue, pParentValue, pCustomNode->getProxyWriteNode(), memberIndex );
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Fetch fields.
|
||||
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
||||
|
||||
// Fetch JSON strict flag (don't use it if member index is set to not use it).
|
||||
const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
|
||||
|
||||
// Fetch element name (mangled or not).
|
||||
StringTableEntry nodeName = jsonStrict ? getManagedName( pCustomNode->getNodeName(), memberIndex ) : pCustomNode->getNodeName();
|
||||
|
||||
// Is there any node text?
|
||||
if ( !pCustomNode->getNodeTextField().isValueEmpty() )
|
||||
{
|
||||
// Yes, so fetch text.
|
||||
const char* pNodeText = pCustomNode->getNodeTextField().getFieldValue();
|
||||
|
||||
// Create custom value.
|
||||
rapidjson::Value customTextValue(rapidjson::kArrayType);
|
||||
customTextValue.PushBack( pNodeText, allocator );
|
||||
pParentValue->AddMember( nodeName, customTextValue, allocator );
|
||||
return;
|
||||
}
|
||||
|
||||
// Create custom value.
|
||||
rapidjson::Value customValue(rapidjson::kObjectType);
|
||||
rapidjson::Value* pCustomValue = &((pParentValue->AddMember( nodeName, customValue, allocator ).MemberEnd()-1)->value);
|
||||
|
||||
// Iterate fields.
|
||||
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
||||
{
|
||||
// Fetch field.
|
||||
const TamlCustomField* pField = *fieldItr;
|
||||
|
||||
// Add a field.
|
||||
rapidjson::Value fieldValue;
|
||||
fieldValue.SetString( pField->getFieldValue(), dStrlen(pField->getFieldValue()), allocator );
|
||||
pCustomValue->AddMember( pField->getFieldName(), fieldValue, allocator );
|
||||
}
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
S32 childMemberIndex = 0;
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the child node.
|
||||
compileCustomNode( document, pCustomValue, pChildNode, childMemberIndex++ );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty (including fields).
|
||||
if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pCustomValue->MemberBegin() == pCustomValue->MemberEnd() )
|
||||
{
|
||||
// Yes, so delete the member.
|
||||
pCustomValue->SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline StringTableEntry TamlJSONWriter::getManagedName( const char* pName, const S32 memberIndex )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_getManagedName);
|
||||
|
||||
char nameBuffer[1024];
|
||||
|
||||
// Format mangled name.
|
||||
dSprintf( nameBuffer, sizeof(nameBuffer), JSON_RFC4627_NAME_MANGLING_FORMAT, pName, memberIndex );
|
||||
|
||||
return StringTable->insert( nameBuffer );
|
||||
}
|
||||
66
Engine/source/persistence/taml/json/tamlJSONWriter.h
Normal file
66
Engine/source/persistence/taml/json/tamlJSONWriter.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_JSONWRITER_H_
|
||||
#define _TAML_JSONWRITER_H_
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
/// RapidJson.
|
||||
#include "persistence/rapidjson/document.h"
|
||||
#include "persistence/rapidjson/prettywriter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS;
|
||||
extern StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlJSONWriter
|
||||
{
|
||||
public:
|
||||
TamlJSONWriter( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
virtual ~TamlJSONWriter() {}
|
||||
|
||||
/// Write.
|
||||
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
private:
|
||||
void compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex );
|
||||
void compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex );
|
||||
|
||||
inline StringTableEntry getManagedName(const char* pName, const S32 memberIndex );
|
||||
};
|
||||
|
||||
#endif // _TAML_JSONWRITER_H_
|
||||
1544
Engine/source/persistence/taml/taml.cpp
Normal file
1544
Engine/source/persistence/taml/taml.cpp
Normal file
File diff suppressed because it is too large
Load diff
218
Engine/source/persistence/taml/taml.h
Normal file
218
Engine/source/persistence/taml/taml.h
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#define _TAML_H_
|
||||
|
||||
#ifndef _TAML_CALLBACKS_H_
|
||||
#include "persistence/taml/tamlCallbacks.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_CUSTOM_H_
|
||||
#include "persistence/taml/tamlCustom.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_CHILDREN_H_
|
||||
#include "persistence/taml/tamlChildren.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_WRITE_NODE_H_
|
||||
#include "persistence/taml/tamlWriteNode.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_VISITOR_H_
|
||||
#include "persistence/taml/tamlVisitor.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/stream/fileStream.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern StringTableEntry tamlRefIdName;
|
||||
extern StringTableEntry tamlRefToIdName;
|
||||
extern StringTableEntry tamlNamedObjectName;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define TAML_SIGNATURE "Taml"
|
||||
#define TAML_SCHEMA_VARIABLE "$pref::T2D::TAMLSchema"
|
||||
#define TAML_JSON_STRICT_VARIBLE "$pref::T2D::JSONStrict"
|
||||
|
||||
class TiXmlElement;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class Taml : public SimObject
|
||||
{
|
||||
public:
|
||||
enum TamlFormatMode
|
||||
{
|
||||
InvalidFormat = 0,
|
||||
XmlFormat,
|
||||
BinaryFormat,
|
||||
JSONFormat,
|
||||
};
|
||||
|
||||
private:
|
||||
typedef SimObject Parent;
|
||||
typedef Vector<TamlWriteNode*> typeNodeVector;
|
||||
typedef HashTable<SimObjectId, TamlWriteNode*> typeCompiledHash;
|
||||
|
||||
typeNodeVector mCompiledNodes;
|
||||
typeCompiledHash mCompiledObjects;
|
||||
U32 mMasterNodeId;
|
||||
TamlFormatMode mFormatMode;
|
||||
StringTableEntry mAutoFormatXmlExtension;
|
||||
StringTableEntry mAutoFormatBinaryExtension;
|
||||
StringTableEntry mAutoFormatJSONExtension;
|
||||
bool mJSONStrict;
|
||||
bool mBinaryCompression;
|
||||
bool mAutoFormat;
|
||||
bool mWriteDefaults;
|
||||
bool mProgenitorUpdate;
|
||||
char mFilePathBuffer[1024];
|
||||
|
||||
private:
|
||||
void resetCompilation( void );
|
||||
|
||||
TamlWriteNode* compileObject( SimObject* pSimObject, const bool forceId = false );
|
||||
void compileStaticFields( TamlWriteNode* pTamlWriteNode );
|
||||
void compileDynamicFields( TamlWriteNode* pTamlWriteNode );
|
||||
void compileChildren( TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomState( TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomNodeState( TamlCustomNode* pCustomNode );
|
||||
|
||||
bool write( FileStream& stream, SimObject* pSimObject, const TamlFormatMode formatMode );
|
||||
SimObject* read( FileStream& stream, const TamlFormatMode formatMode );
|
||||
template<typename T> inline T* read( FileStream& stream, const TamlFormatMode formatMode )
|
||||
{
|
||||
SimObject* pSimObject = read( stream, formatMode );
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
T* pObj = dynamic_cast<T*>( pSimObject );
|
||||
if ( pObj != NULL )
|
||||
return pObj;
|
||||
pSimObject->deleteObject();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
Taml();
|
||||
virtual ~Taml() {}
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
/// Format mode.
|
||||
inline void setFormatMode( const TamlFormatMode formatMode ) { mFormatMode = formatMode != Taml::InvalidFormat ? formatMode : Taml::XmlFormat; }
|
||||
inline TamlFormatMode getFormatMode( void ) const { return mFormatMode; }
|
||||
|
||||
/// Auto-Format mode.
|
||||
inline void setAutoFormat( const bool autoFormat ) { mAutoFormat = autoFormat; }
|
||||
inline bool getAutoFormat( void ) const { return mAutoFormat; }
|
||||
|
||||
/// Write defaults.
|
||||
inline void setWriteDefaults( const bool writeDefaults ) { mWriteDefaults = writeDefaults; }
|
||||
inline bool getWriteDefaults( void ) const { return mWriteDefaults; }
|
||||
|
||||
/// Progenitor.
|
||||
inline void setProgenitorUpdate( const bool progenitorUpdate ) { mProgenitorUpdate = progenitorUpdate; }
|
||||
inline bool getProgenitorUpdate( void ) const { return mProgenitorUpdate; }
|
||||
|
||||
/// Auto-format extensions.
|
||||
inline void setAutoFormatXmlExtension( const char* pExtension ) { mAutoFormatXmlExtension = StringTable->insert( pExtension ); }
|
||||
inline StringTableEntry getAutoFormatXmlExtension( void ) const { return mAutoFormatXmlExtension; }
|
||||
inline void setAutoFormatBinaryExtension( const char* pExtension ) { mAutoFormatBinaryExtension = StringTable->insert( pExtension ); }
|
||||
inline StringTableEntry getAutoFormatBinaryExtension( void ) const { return mAutoFormatBinaryExtension; }
|
||||
|
||||
/// Compression.
|
||||
inline void setBinaryCompression( const bool compressed ) { mBinaryCompression = compressed; }
|
||||
inline bool getBinaryCompression( void ) const { return mBinaryCompression; }
|
||||
|
||||
/// JSON Strict RFC4627 mode.
|
||||
inline void setJSONStrict( const bool jsonStrict ) { mJSONStrict = jsonStrict; }
|
||||
inline bool getJSONStrict( void ) const { return mJSONStrict; }
|
||||
|
||||
TamlFormatMode getFileAutoFormatMode( const char* pFilename );
|
||||
|
||||
const char* getFilePathBuffer( void ) const { return mFilePathBuffer; }
|
||||
|
||||
/// Write.
|
||||
bool write( SimObject* pSimObject, const char* pFilename );
|
||||
|
||||
/// Read.
|
||||
template<typename T> inline T* read( const char* pFilename )
|
||||
{
|
||||
SimObject* pSimObject = read( pFilename );
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
T* pObj = dynamic_cast<T*>( pSimObject );
|
||||
if ( pObj != NULL )
|
||||
return pObj;
|
||||
pSimObject->deleteObject();
|
||||
return NULL;
|
||||
}
|
||||
SimObject* read( const char* pFilename );
|
||||
|
||||
/// Parse.
|
||||
bool parse( const char* pFilename, TamlVisitor& visitor );
|
||||
|
||||
/// Create type.
|
||||
static SimObject* createType( StringTableEntry typeName, const Taml* pTaml, const char* pProgenitorSuffix = NULL );
|
||||
|
||||
/// Schema generation.
|
||||
static bool generateTamlSchema();
|
||||
|
||||
/// Write a unrestricted custom Taml schema.
|
||||
static void WriteUnrestrictedCustomTamlSchema( const char* pCustomNodeName, const AbstractClassRep* pClassRep, TiXmlElement* pParentElement );
|
||||
|
||||
/// Get format mode info.
|
||||
static TamlFormatMode getFormatModeEnum( const char* label );
|
||||
static const char* getFormatModeDescription( const TamlFormatMode formatMode );
|
||||
|
||||
/// Taml callbacks.
|
||||
inline void tamlPreWrite( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPreWrite(); }
|
||||
inline void tamlPostWrite( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPostWrite(); }
|
||||
inline void tamlPreRead( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPreRead(); }
|
||||
inline void tamlPostRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes ) { pCallbacks->onTamlPostRead( customNodes ); }
|
||||
inline void tamlAddParent( TamlCallbacks* pCallbacks, SimObject* pParentObject ) { pCallbacks->onTamlAddParent( pParentObject ); }
|
||||
inline void tamlCustomWrite( TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes ) { pCallbacks->onTamlCustomWrite( customNodes ); }
|
||||
inline void tamlCustomRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes ) { pCallbacks->onTamlCustomRead( customNodes ); }
|
||||
|
||||
/// Declare Console Object.
|
||||
DECLARE_CONOBJECT( Taml );
|
||||
};
|
||||
|
||||
#endif // _TAML_H_
|
||||
61
Engine/source/persistence/taml/tamlCallbacks.h
Normal file
61
Engine/source/persistence/taml/tamlCallbacks.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_CALLBACKS_H_
|
||||
#define _TAML_CALLBACKS_H_
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomNodes;
|
||||
class SimObject;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCallbacks
|
||||
{
|
||||
friend class Taml;
|
||||
|
||||
private:
|
||||
/// Called prior to Taml writing the object.
|
||||
virtual void onTamlPreWrite( void ) = 0;
|
||||
|
||||
/// Called after Taml has finished writing the object.
|
||||
virtual void onTamlPostWrite( void ) = 0;
|
||||
|
||||
/// Called prior to Taml reading the object.
|
||||
virtual void onTamlPreRead( void ) = 0;
|
||||
|
||||
/// Called after Taml has finished reading the object.
|
||||
/// The custom properties is additionally passed here for object who want to process it at the end of reading.
|
||||
virtual void onTamlPostRead( const TamlCustomNodes& customNodes ) = 0;
|
||||
|
||||
/// Called after Taml has finished reading the object and has added the object to any parent.
|
||||
virtual void onTamlAddParent( SimObject* pParentObject ) = 0;
|
||||
|
||||
/// Called during the writing of the object to allow custom properties to be written.
|
||||
virtual void onTamlCustomWrite( TamlCustomNodes& customNodes ) = 0;
|
||||
|
||||
/// Called during the reading of the object to allow custom properties to be read.
|
||||
virtual void onTamlCustomRead( const TamlCustomNodes& customNodes ) = 0;
|
||||
};
|
||||
|
||||
#endif // _TAML_CALLBACKS_H_
|
||||
49
Engine/source/persistence/taml/tamlChildren.h
Normal file
49
Engine/source/persistence/taml/tamlChildren.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_CHILDREN_H_
|
||||
#define _TAML_CHILDREN_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class SimObject;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlChildren
|
||||
{
|
||||
public:
|
||||
/// Called when Taml attempts to compile a list of children.
|
||||
virtual U32 getTamlChildCount( void ) const = 0;
|
||||
|
||||
/// Called when Taml attempts to compile a list of children.
|
||||
virtual SimObject* getTamlChild( const U32 childIndex ) const = 0;
|
||||
|
||||
/// Called when Taml attempts to populate an objects children during a read.
|
||||
virtual void addTamlChild( SimObject* pSimObject ) = 0;
|
||||
};
|
||||
|
||||
#endif // _TAML_CHILDREN_H_
|
||||
72
Engine/source/persistence/taml/tamlCustom.cpp
Normal file
72
Engine/source/persistence/taml/tamlCustom.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/tamlCustom.h"
|
||||
|
||||
#ifndef _TAML_WRITE_NODE_H_
|
||||
#include "persistence/taml/tamlWriteNode.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FactoryCache<TamlCustomField> TamlCustomFieldFactory;
|
||||
FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlCustomField::set( const char* pFieldName, const char* pFieldValue )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pFieldName != NULL, "Field name cannot be NULL." );
|
||||
AssertFatal( pFieldValue != NULL, "Field value cannot be NULL." );
|
||||
|
||||
// Set field name.
|
||||
mFieldName = StringTable->insert( pFieldName );
|
||||
|
||||
#if TORQUE_DEBUG
|
||||
// Is the field value too big?
|
||||
if ( dStrlen(pFieldValue) >= sizeof(mFieldValue) )
|
||||
{
|
||||
// Yes, so warn!
|
||||
Con::warnf( "Taml property field '%s' has a value that exceeds then maximum length: '%s'", pFieldName, pFieldValue );
|
||||
AssertFatal( false, "Field value is too big!" );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Copy field value.
|
||||
dStrcpy( mFieldValue, pFieldValue );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlCustomNode::setWriteNode( TamlWriteNode* pWriteNode )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( mNodeName != StringTable->EmptyString(), "Cannot set write node with an empty node name." );
|
||||
AssertFatal( pWriteNode != NULL, "Write node cannot be NULL." );
|
||||
AssertFatal( pWriteNode->mpSimObject == mpProxyObject, "Write node does not match existing proxy object." );
|
||||
AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
|
||||
|
||||
// Set proxy write node.
|
||||
mpProxyWriteNode = pWriteNode;
|
||||
}
|
||||
|
||||
785
Engine/source/persistence/taml/tamlCustom.h
Normal file
785
Engine/source/persistence/taml/tamlCustom.h
Normal file
|
|
@ -0,0 +1,785 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_CUSTOM_H_
|
||||
#define _TAML_CUSTOM_H_
|
||||
|
||||
#ifndef _FACTORY_CACHE_H_
|
||||
#include "core/factoryCache.h"
|
||||
#endif
|
||||
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
|
||||
#ifndef _CONSOLE_H_
|
||||
#include "console/console.h"
|
||||
#endif
|
||||
|
||||
#ifndef _CONSOLETYPES_H_
|
||||
#include "console/consoleTypes.h"
|
||||
#endif
|
||||
|
||||
#ifndef B2_MATH_H
|
||||
//TODO: Look at this
|
||||
//#include "box2d/Common/b2Math.h"
|
||||
#endif
|
||||
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#include "math/mMath.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define MAX_TAML_NODE_FIELDVALUE_LENGTH 2048
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlWriteNode;
|
||||
class TamlCustomNode;
|
||||
class TamlCustomField;
|
||||
extern FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
|
||||
extern FactoryCache<TamlCustomField> TamlCustomFieldFactory;
|
||||
typedef Vector<TamlCustomNode*> TamlCustomNodeVector;
|
||||
typedef Vector<TamlCustomField*> TamlCustomFieldVector;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomField : public IFactoryObjectReset
|
||||
{
|
||||
public:
|
||||
TamlCustomField()
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
virtual ~TamlCustomField()
|
||||
{
|
||||
// Everything should already be cleared in a state reset.
|
||||
// Touching any memory here is dangerous as this type is typically
|
||||
// held in a static factory cache until shutdown at which point
|
||||
// pretty much anything or everything could be invalid!
|
||||
}
|
||||
|
||||
virtual void resetState( void )
|
||||
{
|
||||
mFieldName = StringTable->EmptyString();
|
||||
*mFieldValue = 0;
|
||||
}
|
||||
|
||||
void set( const char* pFieldName, const char* pFieldValue );
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const ColorI& fieldValue )
|
||||
{
|
||||
// Fetch the field value.
|
||||
const char* pFieldValue = Con::getData( TypeColorI, &const_cast<ColorI&>(fieldValue), 0 );
|
||||
|
||||
// Did we get a field value?
|
||||
if ( pFieldValue == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Failed to add node field name '%s' with ColorI value.", pFieldName );
|
||||
pFieldValue = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
set( pFieldName, pFieldValue );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const ColorF& fieldValue )
|
||||
{
|
||||
// Fetch the field value.
|
||||
const char* pFieldValue = Con::getData( TypeColorF, &const_cast<ColorF&>(fieldValue), 0 );
|
||||
|
||||
// Did we get a field value?
|
||||
if ( pFieldValue == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Failed to add node field name '%s' with ColorF value.", pFieldName );
|
||||
pFieldValue = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
set( pFieldName, pFieldValue );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point2I& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d", fieldValue.x, fieldValue.y );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point2F& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g", fieldValue.x, fieldValue.y );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point3I& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d %d", fieldValue.x, fieldValue.y, fieldValue.z );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point3F& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g", fieldValue.x, fieldValue.y, fieldValue.z );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const RectF& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g",
|
||||
fieldValue.point.x, fieldValue.point.y, fieldValue.extent.x, fieldValue.extent.y);
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const QuatF& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g", fieldValue.x, fieldValue.y, fieldValue.z, fieldValue.w );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const AngAxisF& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g", fieldValue.axis.x, fieldValue.axis.y, fieldValue.axis.z, fieldValue.angle );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const U32 fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const bool fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const S32 fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const float fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const char* fieldValue )
|
||||
{
|
||||
set( pFieldName, fieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( ColorF& fieldValue ) const
|
||||
{
|
||||
fieldValue.set( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
|
||||
// Set color.
|
||||
const char* argv = (char*)mFieldValue;
|
||||
Con::setData( TypeColorF, &fieldValue, 0, 1, &argv );
|
||||
}
|
||||
|
||||
inline void getFieldValue( ColorI& fieldValue ) const
|
||||
{
|
||||
fieldValue.set( 255, 255, 255, 255 );
|
||||
|
||||
// Set color.
|
||||
const char* argv = (char*)mFieldValue;
|
||||
Con::setData( TypeColorI, &fieldValue, 0, 1, &argv );
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point2I& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%d %d", &fieldValue.x, &fieldValue.y ) != 2 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point2I but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point2F& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g", &fieldValue.x, &fieldValue.y ) != 2 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point2F but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point3I& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%d %d %d", &fieldValue.x, &fieldValue.y, &fieldValue.z ) != 3 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point3I but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point3F& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g", &fieldValue.x, &fieldValue.y, &fieldValue.z ) != 3 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point3F but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( RectF& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.point.x, &fieldValue.point.y, &fieldValue.extent.x, &fieldValue.extent.y ) != 3 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading RectF but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( QuatF& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.x, &fieldValue.y, &fieldValue.z, &fieldValue.w ) != 4 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading QuatF but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( AngAxisF& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.axis.x, &fieldValue.axis.y, &fieldValue.axis.z, &fieldValue.angle ) != 4 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading AngAxisF but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( bool& fieldValue ) const
|
||||
{
|
||||
fieldValue = dAtob( mFieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( S32& fieldValue ) const
|
||||
{
|
||||
fieldValue = dAtoi( mFieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( U32& fieldValue ) const
|
||||
{
|
||||
fieldValue = (U32)dAtoi( mFieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( F32& fieldValue ) const
|
||||
{
|
||||
fieldValue = dAtof( mFieldValue );
|
||||
}
|
||||
|
||||
inline const char* getFieldValue( void ) const
|
||||
{
|
||||
return mFieldValue;
|
||||
}
|
||||
|
||||
inline StringTableEntry getFieldName( void ) const { return mFieldName; }
|
||||
|
||||
bool fieldNameBeginsWith( const char* pComparison ) const
|
||||
{
|
||||
const U32 comparisonLength = dStrlen( pComparison );
|
||||
const U32 fieldNameLength = dStrlen( mFieldName );
|
||||
|
||||
if ( comparisonLength == 0 || fieldNameLength == 0 || comparisonLength > fieldNameLength )
|
||||
return false;
|
||||
|
||||
StringTableEntry comparison = StringTable->insert( pComparison );
|
||||
|
||||
char fieldNameBuffer[1024];
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( fieldNameLength < sizeof(fieldNameBuffer), "TamlCustomField: Field name is too long." );
|
||||
|
||||
dStrcpy( fieldNameBuffer, mFieldName );
|
||||
fieldNameBuffer[fieldNameLength-1] = 0;
|
||||
StringTableEntry fieldName = StringTable->insert( fieldNameBuffer );
|
||||
|
||||
return ( fieldName == comparison );
|
||||
}
|
||||
|
||||
inline bool isValueEmpty( void ) const { return *mFieldValue == 0; }
|
||||
|
||||
private:
|
||||
StringTableEntry mFieldName;
|
||||
char mFieldValue[MAX_TAML_NODE_FIELDVALUE_LENGTH];
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomNode : public IFactoryObjectReset
|
||||
{
|
||||
public:
|
||||
TamlCustomNode()
|
||||
{
|
||||
// Reset proxy object.
|
||||
// NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
|
||||
mpProxyWriteNode = NULL;
|
||||
mpProxyObject = NULL;
|
||||
|
||||
resetState();
|
||||
}
|
||||
|
||||
virtual ~TamlCustomNode()
|
||||
{
|
||||
// Everything should already be cleared in a state reset.
|
||||
// Touching any memory here is dangerous as this type is typically
|
||||
// held in a static factory cache until shutdown at which point
|
||||
// pretty much anything or everything could be invalid!
|
||||
}
|
||||
|
||||
virtual void resetState( void )
|
||||
{
|
||||
// We don't need to delete the write node as it'll get destroyed when the compilation is reset!
|
||||
mpProxyWriteNode = NULL;
|
||||
mpProxyObject = NULL;
|
||||
|
||||
// Cache the children.
|
||||
while ( mChildren.size() > 0 )
|
||||
{
|
||||
TamlCustomNodeFactory.cacheObject( mChildren.back() );
|
||||
mChildren.pop_back();
|
||||
}
|
||||
|
||||
// Cache the fields.
|
||||
while( mFields.size() > 0 )
|
||||
{
|
||||
TamlCustomFieldFactory.cacheObject( mFields.back() );
|
||||
mFields.pop_back();
|
||||
}
|
||||
|
||||
// Reset the node name.
|
||||
mNodeName = StringTable->EmptyString();
|
||||
|
||||
// Reset node text.
|
||||
mNodeText.resetState();
|
||||
|
||||
// Reset the ignore empty flag.
|
||||
mIgnoreEmpty = false;
|
||||
}
|
||||
|
||||
inline TamlCustomNode* addNode( SimObject* pProxyObject )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pProxyObject != NULL, "Field object cannot be NULL." );
|
||||
AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
|
||||
|
||||
// Create a custom node.
|
||||
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
|
||||
|
||||
// Set node name.
|
||||
pCustomNode->setNodeName( pProxyObject->getClassName() );
|
||||
|
||||
// Set proxy object.
|
||||
pCustomNode->mpProxyObject = pProxyObject;
|
||||
|
||||
// Store node.
|
||||
mChildren.push_back( pCustomNode );
|
||||
|
||||
return pCustomNode;
|
||||
}
|
||||
|
||||
inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
|
||||
{
|
||||
// Create a custom node.
|
||||
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
|
||||
|
||||
// Fetch node name.
|
||||
pCustomNode->setNodeName( pNodeName );
|
||||
|
||||
// Set ignore-empty flag.
|
||||
pCustomNode->setIgnoreEmpty( ignoreEmpty );
|
||||
|
||||
// Store node.
|
||||
mChildren.push_back( pCustomNode );
|
||||
|
||||
return pCustomNode;
|
||||
}
|
||||
|
||||
inline void removeNode( const U32 index )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( index < (U32)mChildren.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
|
||||
|
||||
// Cache the custom node.
|
||||
TamlCustomNodeFactory.cacheObject( mChildren[index] );
|
||||
|
||||
// Remove it.
|
||||
mChildren.erase( index );
|
||||
}
|
||||
|
||||
inline const TamlCustomNode* findNode( const char* pNodeName ) const
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
|
||||
|
||||
// Fetch node name.
|
||||
StringTableEntry nodeName = StringTable->insert( pNodeName );
|
||||
|
||||
// Find node.
|
||||
for( Vector<TamlCustomNode*>::const_iterator nodeItr = mChildren.begin(); nodeItr != mChildren.end(); ++nodeItr )
|
||||
{
|
||||
if ( (*nodeItr)->getNodeName() == nodeName )
|
||||
return (*nodeItr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const ColorI& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const ColorF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point2I& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point2F& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point3I& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point3F& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const RectF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const QuatF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const AngAxisF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const U32 fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const bool fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const S32 fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const float fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const char* fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline const TamlCustomField* findField( const char* pFieldName ) const
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pFieldName != NULL, "Cannot find Taml field name that is NULL." );
|
||||
|
||||
// Fetch field name.
|
||||
StringTableEntry fieldName = StringTable->insert( pFieldName );
|
||||
|
||||
// Find node field.
|
||||
for( TamlCustomFieldVector::const_iterator fieldItr = mFields.begin(); fieldItr != mFields.end(); ++fieldItr )
|
||||
{
|
||||
if ( (*fieldItr)->getFieldName() == fieldName )
|
||||
return (*fieldItr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void setNodeName( const char* pNodeName )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pNodeName != NULL, "Cannot add a NULL node name." );
|
||||
|
||||
mNodeName = StringTable->insert( pNodeName );
|
||||
}
|
||||
|
||||
inline StringTableEntry getNodeName( void ) const { return mNodeName; }
|
||||
|
||||
void setWriteNode( TamlWriteNode* pWriteNode );
|
||||
|
||||
inline void setNodeText( const char* pNodeText )
|
||||
{
|
||||
AssertFatal( dStrlen( pNodeText ) < MAX_TAML_NODE_FIELDVALUE_LENGTH, "Custom node text is too long." );
|
||||
|
||||
mNodeText.set( StringTable->EmptyString(), pNodeText );
|
||||
}
|
||||
inline const TamlCustomField& getNodeTextField( void ) const { return mNodeText; }
|
||||
inline TamlCustomField& getNodeTextField( void ) { return mNodeText; }
|
||||
|
||||
inline const Vector<TamlCustomNode*>& getChildren( void ) const { return mChildren; }
|
||||
inline const TamlCustomFieldVector& getFields( void ) const { return mFields; }
|
||||
|
||||
inline bool isProxyObject( void ) const { return mpProxyObject != NULL; }
|
||||
template<typename T> T* getProxyObject( const bool deleteIfNotType ) const
|
||||
{
|
||||
// Return nothing if no proxy object.
|
||||
if ( mpProxyObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Cast object to specified type.
|
||||
T* pTypeCast = dynamic_cast<T*>( mpProxyObject );
|
||||
|
||||
// Destroy the object if not the specified type and requested to do so.
|
||||
if ( deleteIfNotType && pTypeCast == NULL )
|
||||
{
|
||||
mpProxyObject->deleteObject();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pTypeCast;
|
||||
}
|
||||
inline const TamlWriteNode* getProxyWriteNode( void ) const { return mpProxyWriteNode; }
|
||||
|
||||
inline bool isEmpty( void ) const { return mNodeText.isValueEmpty() && mFields.size() == 0 && mChildren.size() == 0; }
|
||||
|
||||
inline void setIgnoreEmpty( const bool ignoreEmpty ) { mIgnoreEmpty = ignoreEmpty; }
|
||||
inline bool getIgnoreEmpty( void ) const { return mIgnoreEmpty; }
|
||||
|
||||
private:
|
||||
inline TamlCustomField* registerField( TamlCustomField* pCustomField )
|
||||
{
|
||||
#if TORQUE_DEBUG
|
||||
// Ensure a field name conflict does not exist.
|
||||
for( Vector<TamlCustomField*>::iterator nodeFieldItr = mFields.begin(); nodeFieldItr != mFields.end(); ++nodeFieldItr )
|
||||
{
|
||||
// Skip if field name is not the same.
|
||||
if ( pCustomField->getFieldName() != (*nodeFieldItr)->getFieldName() )
|
||||
continue;
|
||||
|
||||
// Warn!
|
||||
Con::warnf("Conflicting Taml node field name of '%s' in node '%s'.", pCustomField->getFieldName(), mNodeName );
|
||||
|
||||
// Cache node field.
|
||||
TamlCustomFieldFactory.cacheObject( pCustomField );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure the field value is not too long.
|
||||
if ( dStrlen( pCustomField->getFieldValue() ) >= MAX_TAML_NODE_FIELDVALUE_LENGTH )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf("Taml field name '%s' has a field value that is too long (Max:%d): '%s'.",
|
||||
pCustomField->getFieldName(),
|
||||
MAX_TAML_NODE_FIELDVALUE_LENGTH,
|
||||
pCustomField->getFieldValue() );
|
||||
|
||||
// Cache node field.
|
||||
TamlCustomFieldFactory.cacheObject( pCustomField );
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
// Store node field.
|
||||
mFields.push_back( pCustomField );
|
||||
|
||||
return pCustomField;
|
||||
}
|
||||
|
||||
inline TamlCustomField* createField( void ) const { return TamlCustomFieldFactory.createObject(); }
|
||||
|
||||
private:
|
||||
StringTableEntry mNodeName;
|
||||
TamlCustomField mNodeText;
|
||||
Vector<TamlCustomNode*> mChildren;
|
||||
TamlCustomFieldVector mFields;
|
||||
bool mIgnoreEmpty;
|
||||
|
||||
SimObject* mpProxyObject;
|
||||
TamlWriteNode* mpProxyWriteNode;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomNodes : public IFactoryObjectReset
|
||||
{
|
||||
public:
|
||||
TamlCustomNodes()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TamlCustomNodes()
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
virtual void resetState( void )
|
||||
{
|
||||
// Cache the nodes.
|
||||
while ( mNodes.size() > 0 )
|
||||
{
|
||||
TamlCustomNodeFactory.cacheObject( mNodes.back() );
|
||||
mNodes.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
|
||||
{
|
||||
// Create a custom node.
|
||||
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
|
||||
|
||||
// Set node name.
|
||||
pCustomNode->setNodeName( pNodeName );
|
||||
|
||||
// Set ignore-empty flag.
|
||||
pCustomNode->setIgnoreEmpty( ignoreEmpty );
|
||||
|
||||
#if TORQUE_DEBUG
|
||||
// Ensure a node name conflict does not exist.
|
||||
for( TamlCustomNodeVector::iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
|
||||
{
|
||||
// Skip if node name is not the same.
|
||||
if ( pCustomNode->getNodeName() != (*nodeItr)->getNodeName() )
|
||||
continue;
|
||||
|
||||
// Warn!
|
||||
Con::warnf("Conflicting Taml custom node name of '%s'.", pNodeName );
|
||||
|
||||
// Cache node.
|
||||
TamlCustomNodeFactory.cacheObject( pCustomNode );
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
// Store node.
|
||||
mNodes.push_back( pCustomNode );
|
||||
|
||||
return pCustomNode;
|
||||
}
|
||||
|
||||
inline void removeNode( const U32 index )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( index < (U32)mNodes.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
|
||||
|
||||
// Cache the custom node.
|
||||
TamlCustomNodeFactory.cacheObject( mNodes[index] );
|
||||
|
||||
// Remove it.
|
||||
mNodes.erase( index );
|
||||
}
|
||||
|
||||
inline const TamlCustomNode* findNode( const char* pNodeName ) const
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
|
||||
|
||||
// Fetch node name.
|
||||
StringTableEntry nodeName = StringTable->insert( pNodeName );
|
||||
|
||||
// Find node.
|
||||
for( Vector<TamlCustomNode*>::const_iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
|
||||
{
|
||||
if ( (*nodeItr)->getNodeName() == nodeName )
|
||||
return (*nodeItr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const TamlCustomNodeVector& getNodes( void ) const { return mNodes; }
|
||||
|
||||
private:
|
||||
TamlCustomNodeVector mNodes;
|
||||
};
|
||||
|
||||
#endif // _TAML_CUSTOM_H_
|
||||
58
Engine/source/persistence/taml/tamlParser.h
Normal file
58
Engine/source/persistence/taml/tamlParser.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_PARSER_H_
|
||||
#define _TAML_PARSER_H_
|
||||
|
||||
#include "core/stringTable.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlVisitor;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlParser
|
||||
{
|
||||
public:
|
||||
TamlParser() :
|
||||
mParsingFilename(StringTable->EmptyString())
|
||||
{}
|
||||
virtual ~TamlParser() {}
|
||||
|
||||
/// Whether the parser can change a property or not.
|
||||
virtual bool canChangeProperty( void ) = 0;
|
||||
|
||||
/// Accept visitor.
|
||||
virtual bool accept( const char* pFilename, TamlVisitor& visitor ) = 0;
|
||||
|
||||
/// Filename.
|
||||
inline void setParsingFilename( const char* pFilename ) { mParsingFilename = StringTable->insert(pFilename); }
|
||||
inline StringTableEntry getParsingFilename( void ) const { return mParsingFilename; }
|
||||
|
||||
private:
|
||||
StringTableEntry mParsingFilename;
|
||||
};
|
||||
|
||||
#endif // _TAML_PARSER_H_
|
||||
111
Engine/source/persistence/taml/tamlVisitor.h
Normal file
111
Engine/source/persistence/taml/tamlVisitor.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_VISITOR_H_
|
||||
#define _TAML_VISITOR_H_
|
||||
|
||||
#include "core/stringTable.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlParser;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlVisitor
|
||||
{
|
||||
public:
|
||||
/// Visitor property state.
|
||||
struct PropertyState
|
||||
{
|
||||
private:
|
||||
StringTableEntry mObjectName;
|
||||
StringTableEntry mPropertyName;
|
||||
char mPropertyValue[4096];
|
||||
bool mPropertyValueDirty;
|
||||
bool mIsRootObject;
|
||||
|
||||
public:
|
||||
PropertyState()
|
||||
{
|
||||
mObjectName = StringTable->EmptyString();
|
||||
mPropertyName = StringTable->EmptyString();
|
||||
*mPropertyValue = 0;
|
||||
mPropertyValueDirty = false;
|
||||
mIsRootObject = false;
|
||||
}
|
||||
|
||||
inline void setObjectName( const char* pObjectName, const bool rootObject )
|
||||
{
|
||||
mObjectName = StringTable->insert( pObjectName );
|
||||
mIsRootObject = rootObject;
|
||||
}
|
||||
inline StringTableEntry getObjectName( void ) const { return mObjectName; }
|
||||
inline bool isRootObject( void ) const { return mIsRootObject; }
|
||||
|
||||
inline void setProperty( const char* pPropertyName, const char* pPropertyValue )
|
||||
{
|
||||
// Set property name.
|
||||
mPropertyName = StringTable->insert( pPropertyName );
|
||||
|
||||
// Format property value.
|
||||
dSprintf( mPropertyValue, sizeof(mPropertyValue), "%s", pPropertyValue );
|
||||
|
||||
// Flag as not dirty.
|
||||
mPropertyValueDirty = false;
|
||||
}
|
||||
|
||||
inline void updatePropertyValue( const char* pPropertyValue )
|
||||
{
|
||||
// Update property value.
|
||||
dSprintf( mPropertyValue, sizeof(mPropertyValue), "%s", pPropertyValue );
|
||||
|
||||
// Flag as dirty.
|
||||
mPropertyValueDirty = true;
|
||||
}
|
||||
|
||||
inline StringTableEntry getPropertyName( void ) const { return mPropertyName; }
|
||||
inline const char* getPropertyValue( void ) const { return mPropertyValue; }
|
||||
|
||||
inline void resetPropertyValueDirty( void ) { mPropertyValueDirty = false; }
|
||||
inline bool getPropertyValueDirty( void ) const { return mPropertyValueDirty; }
|
||||
};
|
||||
|
||||
public:
|
||||
TamlVisitor() {}
|
||||
virtual ~TamlVisitor() {}
|
||||
|
||||
/// Whether the visitor wants to perform property changes.
|
||||
/// This allows a check against the parser which may not allow changes.
|
||||
virtual bool wantsPropertyChanges( void ) = 0;
|
||||
|
||||
/// Whether the visitor wants to visit the root node only.
|
||||
virtual bool wantsRootOnly( void ) = 0;
|
||||
|
||||
/// The state of the visited property.
|
||||
virtual bool visit( const TamlParser& parser, PropertyState& propertyState ) = 0;
|
||||
};
|
||||
|
||||
#endif // _TAML_VISITOR_H_
|
||||
66
Engine/source/persistence/taml/tamlWriteNode.cpp
Normal file
66
Engine/source/persistence/taml/tamlWriteNode.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/tamlWriteNode.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlWriteNode::resetNode( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlWriteNode_ResetNode);
|
||||
|
||||
// Clear fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::iterator itr = mFields.begin(); itr != mFields.end(); ++itr )
|
||||
{
|
||||
delete (*itr)->mpValue;
|
||||
}
|
||||
mFields.clear();
|
||||
|
||||
// Clear children.
|
||||
if ( mChildren != NULL )
|
||||
{
|
||||
for( Vector<TamlWriteNode*>::iterator itr = mChildren->begin(); itr != mChildren->end(); ++itr )
|
||||
{
|
||||
(*itr)->resetNode();
|
||||
}
|
||||
|
||||
mChildren->clear();
|
||||
delete mChildren;
|
||||
mChildren = NULL;
|
||||
}
|
||||
|
||||
mRefId = 0;
|
||||
mRefToNode = NULL;
|
||||
mChildren = NULL;
|
||||
mpObjectName = NULL;
|
||||
mpSimObject = NULL;
|
||||
|
||||
// Reset callbacks.
|
||||
mpTamlCallbacks = NULL;
|
||||
|
||||
// Reset custom nodes.
|
||||
mCustomNodes.resetState();
|
||||
}
|
||||
116
Engine/source/persistence/taml/tamlWriteNode.h
Normal file
116
Engine/source/persistence/taml/tamlWriteNode.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_WRITE_NODE_H_
|
||||
#define _TAML_WRITE_NODE_H_
|
||||
|
||||
#ifndef _TAML_CUSTOM_H_
|
||||
#include "persistence/taml/tamlCustom.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _VECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCallbacks;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlWriteNode
|
||||
{
|
||||
public:
|
||||
class FieldValuePair
|
||||
{
|
||||
public:
|
||||
FieldValuePair( StringTableEntry name, const char* pValue )
|
||||
{
|
||||
// Set the field name.
|
||||
mName = name;
|
||||
|
||||
// Allocate and copy the value.
|
||||
mpValue = new char[ dStrlen(pValue)+1 ];
|
||||
dStrcpy( (char *)mpValue, pValue );
|
||||
}
|
||||
|
||||
|
||||
StringTableEntry mName;
|
||||
const char* mpValue;
|
||||
};
|
||||
|
||||
public:
|
||||
TamlWriteNode()
|
||||
{
|
||||
// NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
|
||||
mRefToNode = NULL;
|
||||
mpSimObject = NULL;
|
||||
mpTamlCallbacks = NULL;
|
||||
mpObjectName = NULL;
|
||||
mChildren = NULL;
|
||||
|
||||
resetNode();
|
||||
}
|
||||
|
||||
void set( SimObject* pSimObject )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Cannot set a write node with a NULL sim object." );
|
||||
|
||||
// Reset the node.
|
||||
resetNode();
|
||||
|
||||
// Set sim object.
|
||||
mpSimObject = pSimObject;
|
||||
|
||||
// Fetch name.
|
||||
const char* pObjectName = pSimObject->getName();
|
||||
|
||||
// Assign name usage.
|
||||
if ( pObjectName != NULL &&
|
||||
pObjectName != StringTable->EmptyString() &&
|
||||
dStrlen( pObjectName ) > 0 )
|
||||
{
|
||||
mpObjectName = pObjectName;
|
||||
}
|
||||
|
||||
// Find Taml callbacks.
|
||||
mpTamlCallbacks = dynamic_cast<TamlCallbacks*>( mpSimObject );
|
||||
}
|
||||
|
||||
void resetNode( void );
|
||||
|
||||
U32 mRefId;
|
||||
TamlWriteNode* mRefToNode;
|
||||
SimObject* mpSimObject;
|
||||
TamlCallbacks* mpTamlCallbacks;
|
||||
const char* mpObjectName;
|
||||
Vector<TamlWriteNode::FieldValuePair*> mFields;
|
||||
Vector<TamlWriteNode*>* mChildren;
|
||||
TamlCustomNodes mCustomNodes;
|
||||
};
|
||||
|
||||
#endif // _TAML_WRITE_NODE_H_
|
||||
311
Engine/source/persistence/taml/taml_ScriptBinding.h
Normal file
311
Engine/source/persistence/taml/taml_ScriptBinding.h
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _TAML_SCRIPTBINDING_H
|
||||
#define _TAML_SCRIPTBINDING_H
|
||||
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#include "persistence/taml/taml.h"
|
||||
|
||||
DefineEngineMethod(Taml, setFormat, void, (const char* formatName), , "(format) - Sets the format that Taml should use to read/write.\n"
|
||||
"@param format The format to use: 'xml' or 'binary'.\n"
|
||||
"@return No return value.")
|
||||
{
|
||||
// Fetch format mode.
|
||||
const Taml::TamlFormatMode formatMode = Taml::getFormatModeEnum(formatName);
|
||||
|
||||
// Was the format valid?
|
||||
if ( formatMode == Taml::InvalidFormat )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::setFormat() - Invalid format mode used: '%s'.", formatName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Set format mode.
|
||||
object->setFormatMode( formatMode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getFormat, const char*, (), , "() - Gets the format that Taml should use to read/write.\n"
|
||||
"@return The format that Taml should use to read/write.")
|
||||
{
|
||||
// Fetch format mode.
|
||||
return Taml::getFormatModeDescription(object->getFormatMode());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setAutoFormat, void, (bool autoFormat), , "(autoFormat) Sets whether the format type is automatically determined by the filename extension or not.\n"
|
||||
"@param autoFormat Whether the format type is automatically determined by the filename extension or not.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setAutoFormat( autoFormat );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getAutoFormat, bool, (), , "() Gets whether the format type is automatically determined by the filename extension or not.\n"
|
||||
"@return Whether the format type is automatically determined by the filename extension or not." )
|
||||
{
|
||||
return object->getAutoFormat();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setWriteDefaults, void, (bool writeDefaults), , "(writeDefaults) Sets whether to write static fields that are at their default or not.\n"
|
||||
"@param writeDefaults Whether to write static fields that are at their default or not.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setWriteDefaults( writeDefaults );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getWriteDefaults, bool, (), , "() Gets whether to write static fields that are at their default or not.\n"
|
||||
"@return Whether to write static fields that are at their default or not." )
|
||||
{
|
||||
return object->getWriteDefaults();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setProgenitorUpdate, void, (bool progenitorUpdate), , "(progenitorUpdate) Sets whether to update each type instances file-progenitor or not.\n"
|
||||
"If not updating then the progenitor stay as the script that executed the call to Taml.\n"
|
||||
"@param progenitorUpdate Whether to update each type instances file-progenitor or not.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setProgenitorUpdate( progenitorUpdate );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getProgenitorUpdate, bool, (), , "() Gets whether to update each type instances file-progenitor or not.\n"
|
||||
"@return Whether to update each type instances file-progenitor or not." )
|
||||
{
|
||||
return object->getProgenitorUpdate();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setAutoFormatXmlExtension, void, (const char* extension), , "(extension) Sets the extension (end of filename) used to detect the XML format.\n"
|
||||
"@param extension The extension (end of filename) used to detect the XML format.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setAutoFormatXmlExtension( extension );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getAutoFormatXmlExtension, const char*, (), , "() Gets the extension (end of filename) used to detect the XML format.\n"
|
||||
"@return The extension (end of filename) used to detect the XML format." )
|
||||
{
|
||||
return object->getAutoFormatXmlExtension();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setAutoFormatBinaryExtension, void, (const char* extension), , "(extension) Sets the extension (end of filename) used to detect the Binary format.\n"
|
||||
"@param extension The extension (end of filename) used to detect the Binary format.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setAutoFormatBinaryExtension( extension );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getAutoFormatBinaryExtension, const char*, (), , "() Gets the extension (end of filename) used to detect the Binary format.\n"
|
||||
"@return The extension (end of filename) used to detect the Binary format." )
|
||||
{
|
||||
return object->getAutoFormatBinaryExtension();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setBinaryCompression, void, (bool compressed), , "(compressed) - Sets whether ZIP compression is used on binary formatting or not.\n"
|
||||
"@param compressed Whether compression is on or off.\n"
|
||||
"@return No return value.")
|
||||
{
|
||||
// Set compression.
|
||||
object->setBinaryCompression( compressed );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getBinaryCompression, bool, (), , "() - Gets whether ZIP compression is used on binary formatting or not.\n"
|
||||
"@return Whether ZIP compression is used on binary formatting or not.")
|
||||
{
|
||||
// Fetch compression.
|
||||
return object->getBinaryCompression();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*! Sets whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
@param jsonStrict Whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
@return No return value.
|
||||
*/
|
||||
DefineEngineMethod(Taml, setJSONStrict, void, (bool strict), , "(jsonStrict) - Sets whether to write JSON that is strictly compatible with RFC4627 or not."
|
||||
"@param jsonStrict Whether to write JSON that is strictly compatible with RFC4627 or not."
|
||||
"@return No return value.")
|
||||
{
|
||||
// Set JSON Strict.
|
||||
object->setJSONStrict( strict );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*! Gets whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
@return whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
*/
|
||||
DefineEngineMethod(Taml, getJSONStrict, bool, (), , "() - Gets whether to write JSON that is strictly compatible with RFC4627 or not."
|
||||
"@return whether to write JSON that is strictly compatible with RFC4627 or not.")
|
||||
{
|
||||
// Get RFC strict.
|
||||
return object->getJSONStrict();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, write, bool, (SimObject* obj, const char* filename), , "(object, filename) - Writes an object to a file using Taml.\n"
|
||||
"@param object The object to write.\n"
|
||||
"@param filename The filename to write to.\n"
|
||||
"@return Whether the write was successful or not.")
|
||||
{
|
||||
// Did we find the object?
|
||||
if ( obj == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::write() - Tried to write a NULL object to file '%s'.", filename );
|
||||
return false;
|
||||
}
|
||||
|
||||
return object->write( obj, filename );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, read, SimObject*, (const char* filename), , "(filename) - Read an object from a file using Taml.\n"
|
||||
"@param filename The filename to read from.\n"
|
||||
"@return (Object) The object read from the file or an empty string if read failed.")
|
||||
{
|
||||
// Read object.
|
||||
SimObject* pSimObject = object->read( filename );
|
||||
|
||||
// Did we find the object?
|
||||
if ( pSimObject == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::read() - Could not read object from file '%s'.", filename );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(TamlWrite, bool, (SimObject* simObject, const char* filename, const char* format, bool compressed),
|
||||
("xml", true),
|
||||
"(object, filename, [format], [compressed]) - Writes an object to a file using Taml.\n"
|
||||
"@param object The object to write.\n"
|
||||
"@param filename The filename to write to.\n"
|
||||
"@param format The file format to use. Optional: Defaults to 'xml'. Can be set to 'binary'.\n"
|
||||
"@param compressed Whether ZIP compression is used on binary formatting or not. Optional: Defaults to 'true'.\n"
|
||||
"@return Whether the write was successful or not.")
|
||||
{
|
||||
|
||||
// Did we find the object?
|
||||
if ( simObject == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
//Con::warnf( "TamlWrite() - Could not find object '%s' to write to file '%s'.", simObject->getIdString(), filename );
|
||||
Con::warnf( "TamlWrite() - Could not find object to write to file '%s'.", filename );
|
||||
return false;
|
||||
}
|
||||
|
||||
Taml taml;
|
||||
|
||||
taml.setFormatMode( Taml::getFormatModeEnum(format) );
|
||||
|
||||
// Yes, so is the format mode binary?
|
||||
if ( taml.getFormatMode() == Taml::BinaryFormat )
|
||||
{
|
||||
// Yes, so set binary compression.
|
||||
taml.setBinaryCompression( compressed );
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlWrite() - Setting binary compression is only valid for XML formatting." );
|
||||
}
|
||||
|
||||
// Turn-off auto-formatting.
|
||||
taml.setAutoFormat( false );
|
||||
|
||||
// Write.
|
||||
return taml.write( simObject, filename );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(TamlRead, const char*, (const char* filename, const char* format), ("xml"), "(filename, [format]) - Read an object from a file using Taml.\n"
|
||||
"@param filename The filename to read from.\n"
|
||||
"@param format The file format to use. Optional: Defaults to 'xml'. Can be set to 'binary'.\n"
|
||||
"@return (Object) The object read from the file or an empty string if read failed.")
|
||||
{
|
||||
|
||||
// Set the format mode.
|
||||
Taml taml;
|
||||
|
||||
// Yes, so set it.
|
||||
taml.setFormatMode( Taml::getFormatModeEnum(format) );
|
||||
|
||||
// Turn-off auto-formatting.
|
||||
taml.setAutoFormat( false );
|
||||
|
||||
// Read object.
|
||||
SimObject* pSimObject = taml.read( filename );
|
||||
|
||||
// Did we find the object?
|
||||
if ( pSimObject == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlRead() - Could not read object from file '%s'.", filename );
|
||||
return StringTable->EmptyString();
|
||||
}
|
||||
|
||||
return pSimObject->getIdString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(GenerateTamlSchema, bool, (), , "() - Generate a TAML schema file of all engine types.\n"
|
||||
"The schema file is specified using the console variable '" TAML_SCHEMA_VARIABLE "'.\n"
|
||||
"@return Whether the schema file was writtent or not." )
|
||||
{
|
||||
// Generate the schema.
|
||||
return Taml::generateTamlSchema();
|
||||
}
|
||||
|
||||
#endif _TAML_SCRIPTBINDING_H
|
||||
193
Engine/source/persistence/taml/xml/tamlXmlParser.cpp
Normal file
193
Engine/source/persistence/taml/xml/tamlXmlParser.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/xml/tamlXmlParser.h"
|
||||
#include "persistence/taml/tamlVisitor.h"
|
||||
#include "console/console.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/stream/fileStream.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlXmlParser::accept( const char* pFilename, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlParser_Accept);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." );
|
||||
|
||||
// Expand the file-path.
|
||||
char filenameBuffer[1024];
|
||||
// TODO: Make sure this is a proper substitute for
|
||||
// Con::expandPath (T2D)
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
/** T2D uses a custom version of TinyXML that supports FileStream.
|
||||
* We don't so we can't do this
|
||||
*
|
||||
FileStream stream;
|
||||
|
||||
#ifdef TORQUE_OS_ANDROID
|
||||
if (strlen(pFilename) > strlen(filenameBuffer)) {
|
||||
strcpy(filenameBuffer, pFilename);
|
||||
}
|
||||
#endif
|
||||
|
||||
// File open for read?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Read ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
TiXmlDocument xmlDocument;
|
||||
|
||||
// Load document from stream.
|
||||
if ( !xmlDocument.LoadFile( filenameBuffer ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close the stream.
|
||||
// stream.close();
|
||||
|
||||
// Set parsing filename.
|
||||
setParsingFilename( filenameBuffer );
|
||||
|
||||
// Flag document as not dirty.
|
||||
mDocumentDirty = false;
|
||||
|
||||
// Parse root element.
|
||||
parseElement( xmlDocument.RootElement(), visitor );
|
||||
|
||||
// Reset parsing filename.
|
||||
setParsingFilename( StringTable->EmptyString() );
|
||||
|
||||
// Finish if the document is not dirty.
|
||||
if ( !mDocumentDirty )
|
||||
return true;
|
||||
|
||||
// Open for write?
|
||||
/*if ( !stream.open( filenameBuffer, FileStream::StreamWrite ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for write.", filenameBuffer );
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// Yes, so save the document.
|
||||
if ( !xmlDocument.SaveFile( filenameBuffer ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlXmlParser: Could not save Taml XML document.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close the stream.
|
||||
//stream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlXmlParser::parseElement( TiXmlElement* pXmlElement, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlParser_ParseElement);
|
||||
|
||||
// Parse attributes (stop processing if instructed).
|
||||
if ( !parseAttributes( pXmlElement, visitor ) )
|
||||
return false;
|
||||
|
||||
// Finish if only the root is needed.
|
||||
if ( visitor.wantsRootOnly() )
|
||||
return false;
|
||||
|
||||
// Fetch any children.
|
||||
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
// Do we have any element children?
|
||||
if ( pChildXmlNode != NULL && pChildXmlNode->Type() == TiXmlNode::TINYXML_ELEMENT )
|
||||
{
|
||||
// Iterate children.
|
||||
for ( TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode ); pChildXmlElement; pChildXmlElement = pChildXmlElement->NextSiblingElement() )
|
||||
{
|
||||
// Parse element (stop processing if instructed).
|
||||
if ( !parseElement( pChildXmlElement, visitor ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlXmlParser::parseAttributes( TiXmlElement* pXmlElement, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlParser_ParseAttribute);
|
||||
|
||||
// Calculate if element is at the root or not.
|
||||
const bool isRoot = pXmlElement->GetDocument()->RootElement() == pXmlElement;
|
||||
|
||||
// Create a visitor property state.
|
||||
TamlVisitor::PropertyState propertyState;
|
||||
propertyState.setObjectName( pXmlElement->Value(), isRoot );
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Configure property state.
|
||||
propertyState.setProperty( pAttribute->Name(), pAttribute->Value() );
|
||||
|
||||
// Visit this attribute.
|
||||
const bool visitStatus = visitor.visit( *this, propertyState );
|
||||
|
||||
// Was the property value changed?
|
||||
if ( propertyState.getPropertyValueDirty() )
|
||||
{
|
||||
// Yes, so update the attribute.
|
||||
pAttribute->SetValue( propertyState.getPropertyValue() );
|
||||
|
||||
// Flag the document as dirty.
|
||||
mDocumentDirty = true;
|
||||
}
|
||||
|
||||
// Finish if requested.
|
||||
if ( !visitStatus )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
57
Engine/source/persistence/taml/xml/tamlXmlParser.h
Normal file
57
Engine/source/persistence/taml/xml/tamlXmlParser.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_XMLPARSER_H_
|
||||
#define _TAML_XMLPARSER_H_
|
||||
|
||||
#ifndef _TAML_PARSER_H_
|
||||
#include "persistence/taml/tamlParser.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlXmlParser : public TamlParser
|
||||
{
|
||||
public:
|
||||
TamlXmlParser() {}
|
||||
virtual ~TamlXmlParser() {}
|
||||
|
||||
/// Whether the parser can change a property or not.
|
||||
virtual bool canChangeProperty( void ) { return true; }
|
||||
|
||||
/// Accept visitor.
|
||||
virtual bool accept( const char* pFilename, TamlVisitor& visitor );
|
||||
|
||||
private:
|
||||
inline bool parseElement( TiXmlElement* pXmlElement, TamlVisitor& visitor );
|
||||
inline bool parseAttributes( TiXmlElement* pXmlElement, TamlVisitor& visitor );
|
||||
|
||||
bool mDocumentDirty;
|
||||
};
|
||||
|
||||
#endif // _TAML_XMLPARSER_H_
|
||||
484
Engine/source/persistence/taml/xml/tamlXmlReader.cpp
Normal file
484
Engine/source/persistence/taml/xml/tamlXmlReader.cpp
Normal file
|
|
@ -0,0 +1,484 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/xml/tamlXmlReader.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
#include "persistence/taml/fsTinyxml.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlXmlReader::read( FileStream& stream )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_Read);
|
||||
|
||||
// Create document.
|
||||
fsTiXmlDocument xmlDocument;
|
||||
|
||||
// Load document from stream.
|
||||
if ( !xmlDocument.LoadFile( stream ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("Taml: Could not load Taml XML file from stream.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse root element.
|
||||
SimObject* pSimObject = parseElement( xmlDocument.RootElement() );
|
||||
|
||||
// Reset parse.
|
||||
resetParse();
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::resetParse( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ResetParse);
|
||||
|
||||
// Clear object reference map.
|
||||
mObjectReferenceMap.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlXmlReader::parseElement( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ParseElement);
|
||||
|
||||
SimObject* pSimObject = NULL;
|
||||
|
||||
// Fetch element name.
|
||||
StringTableEntry typeName = StringTable->insert( pXmlElement->Value() );
|
||||
|
||||
// Fetch reference to Id.
|
||||
const U32 tamlRefToId = getTamlRefToId( pXmlElement );
|
||||
|
||||
// Do we have a reference to Id?
|
||||
if ( tamlRefToId != 0 )
|
||||
{
|
||||
// Yes, so fetch reference.
|
||||
typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
|
||||
|
||||
// Did we find the reference?
|
||||
if ( referenceItr == mObjectReferenceMap.end() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Could not find a reference Id of '%d'", tamlRefToId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return referenceItr->value;
|
||||
}
|
||||
|
||||
// No, so fetch reference Id.
|
||||
const U32 tamlRefId = getTamlRefId( pXmlElement );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// Format the type location.
|
||||
char typeLocationBuffer[64];
|
||||
dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='xml' row=%d column=%d]", pXmlElement->Row(), pXmlElement->Column() );
|
||||
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
|
||||
#else
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml );
|
||||
#endif
|
||||
|
||||
|
||||
// Finish if we couldn't create the type.
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Find Taml callbacks.
|
||||
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPreRead( pCallbacks );
|
||||
}
|
||||
|
||||
// Parse attributes.
|
||||
parseAttributes( pXmlElement, pSimObject );
|
||||
|
||||
// Fetch object name.
|
||||
StringTableEntry objectName = StringTable->insert( getTamlObjectName( pXmlElement ) );
|
||||
|
||||
// Does the object require a name?
|
||||
if ( objectName == StringTable->EmptyString() )
|
||||
{
|
||||
// No, so just register anonymously.
|
||||
pSimObject->registerObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so register a named object.
|
||||
pSimObject->registerObject( objectName );
|
||||
|
||||
// Was the name assigned?
|
||||
if ( pSimObject->getName() != objectName )
|
||||
{
|
||||
// No, so warn that the name was rejected.
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists. '%s'", typeName, objectName, typeLocationBuffer );
|
||||
#else
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.", typeName, objectName );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( tamlRefId != 0 )
|
||||
{
|
||||
// Yes, so insert reference.
|
||||
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
|
||||
}
|
||||
|
||||
// Fetch any children.
|
||||
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
TamlCustomNodes customProperties;
|
||||
|
||||
// Do we have any element children?
|
||||
if ( pChildXmlNode != NULL )
|
||||
{
|
||||
// Fetch the Taml children.
|
||||
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
|
||||
|
||||
// Fetch any container child class specifier.
|
||||
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
|
||||
|
||||
// Iterate siblings.
|
||||
do
|
||||
{
|
||||
// Fetch element.
|
||||
TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
|
||||
|
||||
// Move to next sibling.
|
||||
pChildXmlNode = pChildXmlNode->NextSibling();
|
||||
|
||||
// Skip if this is not an element?
|
||||
if ( pChildXmlElement == NULL )
|
||||
continue;
|
||||
|
||||
// Is this a standard child element?
|
||||
if ( dStrchr( pChildXmlElement->Value(), '.' ) == NULL )
|
||||
{
|
||||
// Is this a Taml child?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element '%s' found under parent '%s' but object cannot have children.",
|
||||
pChildXmlElement->Value(),
|
||||
pXmlElement->Value() );
|
||||
|
||||
// Skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, so parse child element.
|
||||
SimObject* pChildSimObject = parseElement( pChildXmlElement );
|
||||
|
||||
// Skip if the child was not created.
|
||||
if ( pChildSimObject == NULL )
|
||||
continue;
|
||||
|
||||
// Do we have a container child class?
|
||||
if ( pContainerChildClass != NULL )
|
||||
{
|
||||
// Yes, so is the child object the correctly derived type?
|
||||
if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
|
||||
pChildSimObject->getClassName(),
|
||||
pSimObject->getClassName(),
|
||||
pContainerChildClass->getClassName() );
|
||||
|
||||
// NOTE: We can't delete the object as it may be referenced elsewhere!
|
||||
pChildSimObject = NULL;
|
||||
|
||||
// Skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add child.
|
||||
pChildren->addTamlChild( pChildSimObject );
|
||||
|
||||
// Find Taml callbacks for child.
|
||||
TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
|
||||
|
||||
// Do we have callbacks on the child?
|
||||
if ( pChildCallbacks != NULL )
|
||||
{
|
||||
// Yes, so perform callback.
|
||||
mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so parse custom element.
|
||||
parseCustomElement( pChildXmlElement, customProperties );
|
||||
}
|
||||
}
|
||||
while( pChildXmlNode != NULL );
|
||||
|
||||
// Call custom read.
|
||||
mpTaml->tamlCustomRead( pCallbacks, customProperties );
|
||||
}
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPostRead( pCallbacks, customProperties );
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ParseAttributes);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Ignore if this is a Taml attribute.
|
||||
if ( attributeName == tamlRefIdName ||
|
||||
attributeName == tamlRefToIdName ||
|
||||
attributeName == tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Set the field.
|
||||
pSimObject->setPrefixedDataField(attributeName, NULL, pAttribute->Value());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& customNodes )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ParseCustomElement);
|
||||
|
||||
// Is this a standard child element?
|
||||
const char* pPeriod = dStrchr( pXmlElement->Value(), '.' );
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pPeriod != NULL, "Parsing extended element but no period character found." );
|
||||
|
||||
// Fetch any custom XML node.
|
||||
TiXmlNode* pCustomXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
// Finish is no XML node exists.
|
||||
if ( pCustomXmlNode == NULL )
|
||||
return;
|
||||
|
||||
// Yes, so add custom node.
|
||||
TamlCustomNode* pCustomNode = customNodes.addNode( pPeriod+1 );
|
||||
|
||||
do
|
||||
{
|
||||
// Fetch element.
|
||||
TiXmlElement* pCustomXmlElement = dynamic_cast<TiXmlElement*>( pCustomXmlNode );
|
||||
|
||||
// Move to next sibling.
|
||||
pCustomXmlNode = pCustomXmlNode->NextSibling();
|
||||
|
||||
// Skip if this is not an element.
|
||||
if ( pCustomXmlElement == NULL )
|
||||
continue;
|
||||
|
||||
// Parse custom node.
|
||||
parseCustomNode( pCustomXmlElement, pCustomNode );
|
||||
}
|
||||
while ( pCustomXmlNode != NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Is the node a proxy object?
|
||||
if ( getTamlRefId( pXmlElement ) != 0 || getTamlRefToId( pXmlElement ) != 0 )
|
||||
{
|
||||
// Yes, so parse proxy object.
|
||||
SimObject* pProxyObject = parseElement( pXmlElement );
|
||||
|
||||
// Add child node.
|
||||
pCustomNode->addNode( pProxyObject );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Yes, so add child node.
|
||||
TamlCustomNode* pChildNode = pCustomNode->addNode( pXmlElement->Value() );
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if a Taml reference attribute.
|
||||
if ( attributeName == tamlRefIdName || attributeName == tamlRefToIdName )
|
||||
continue;
|
||||
|
||||
// Add node field.
|
||||
pChildNode->addField( attributeName, pAttribute->Value() );
|
||||
}
|
||||
|
||||
// Fetch any element text.
|
||||
const char* pElementText = pXmlElement->GetText();
|
||||
|
||||
// Do we have any element text?
|
||||
if ( pElementText != NULL )
|
||||
{
|
||||
// Yes, so store it.
|
||||
pChildNode->setNodeText( pElementText );
|
||||
}
|
||||
|
||||
// Fetch any children.
|
||||
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
// Do we have any element children?
|
||||
if ( pChildXmlNode != NULL )
|
||||
{
|
||||
do
|
||||
{
|
||||
// Yes, so fetch child element.
|
||||
TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
|
||||
|
||||
// Move to next sibling.
|
||||
pChildXmlNode = pChildXmlNode->NextSibling();
|
||||
|
||||
// Skip if this is not an element.
|
||||
if ( pChildXmlElement == NULL )
|
||||
continue;
|
||||
|
||||
// Parse custom node.
|
||||
parseCustomNode( pChildXmlElement, pChildNode );
|
||||
}
|
||||
while( pChildXmlNode != NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 TamlXmlReader::getTamlRefId( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_GetTamlRefId);
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefIdName )
|
||||
continue;
|
||||
|
||||
// Return it.
|
||||
return dAtoi( pAttribute->Value() );
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 TamlXmlReader::getTamlRefToId( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_GetTamlRefToId);
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefToIdName )
|
||||
continue;
|
||||
|
||||
// Return it.
|
||||
return dAtoi( pAttribute->Value() );
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const char* TamlXmlReader::getTamlObjectName( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_GetTamlObjectName);
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Return it.
|
||||
return pAttribute->Value();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
73
Engine/source/persistence/taml/xml/tamlXmlReader.h
Normal file
73
Engine/source/persistence/taml/xml/tamlXmlReader.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_XMLREADER_H_
|
||||
#define _TAML_XMLREADER_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlXmlReader
|
||||
{
|
||||
public:
|
||||
TamlXmlReader( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
|
||||
virtual ~TamlXmlReader() {}
|
||||
|
||||
/// Read.
|
||||
SimObject* read( FileStream& stream );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
|
||||
typeObjectReferenceHash mObjectReferenceMap;
|
||||
|
||||
private:
|
||||
void resetParse( void );
|
||||
|
||||
SimObject* parseElement( TiXmlElement* pXmlElement );
|
||||
void parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject );
|
||||
void parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& pCustomNode );
|
||||
void parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode );
|
||||
|
||||
U32 getTamlRefId( TiXmlElement* pXmlElement );
|
||||
U32 getTamlRefToId( TiXmlElement* pXmlElement );
|
||||
const char* getTamlObjectName( TiXmlElement* pXmlElement );
|
||||
};
|
||||
|
||||
#endif // _TAML_XMLREADER_H_
|
||||
301
Engine/source/persistence/taml/xml/tamlXmlWriter.cpp
Normal file
301
Engine/source/persistence/taml/xml/tamlXmlWriter.cpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "persistence/taml/xml/tamlXmlWriter.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
#include "persistence/taml/fsTinyxml.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlXmlWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_Write);
|
||||
|
||||
// Create document.
|
||||
fsTiXmlDocument xmlDocument;
|
||||
|
||||
// Compile the root element.
|
||||
TiXmlElement* pRootElement = compileElement( pTamlWriteNode );
|
||||
|
||||
// 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 )
|
||||
{
|
||||
// Yes, so add namespace attribute to root.
|
||||
pRootElement->SetAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
|
||||
|
||||
// Expand the file-path reference.
|
||||
char schemaFilePathBuffer[1024];
|
||||
Con::expandToolScriptFilename( schemaFilePathBuffer, sizeof(schemaFilePathBuffer), pTamlSchemaFile );
|
||||
|
||||
// Fetch the output path for the Taml file.
|
||||
char outputFileBuffer[1024];
|
||||
dSprintf( outputFileBuffer, sizeof(outputFileBuffer), "%s", mpTaml->getFilePathBuffer() );
|
||||
char* pFileStart = dStrrchr( outputFileBuffer, '/' );
|
||||
if ( pFileStart == NULL )
|
||||
*outputFileBuffer = 0;
|
||||
else
|
||||
*pFileStart = 0;
|
||||
|
||||
// Fetch the schema file-path relative to the output file.
|
||||
StringTableEntry relativeSchemaFilePath = Platform::makeRelativePathName( schemaFilePathBuffer, outputFileBuffer );
|
||||
|
||||
// Add schema location attribute to root.
|
||||
pRootElement->SetAttribute( "xsi:noNamespaceSchemaLocation", relativeSchemaFilePath );
|
||||
}
|
||||
|
||||
// Link the root element.
|
||||
xmlDocument.LinkEndChild( pRootElement );
|
||||
|
||||
// Save document to stream.
|
||||
return xmlDocument.SaveFile( stream );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
TiXmlElement* TamlXmlWriter::compileElement( const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_CompileElement);
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch element name.
|
||||
const char* pElementName = pSimObject->getClassName();
|
||||
|
||||
// Create element.
|
||||
TiXmlElement* pElement = new fsTiXmlElement( pElementName );
|
||||
|
||||
// Fetch reference Id.
|
||||
const U32 referenceId = pTamlWriteNode->mRefId;
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( referenceId != 0 )
|
||||
{
|
||||
// Yes, so set reference Id attribute.
|
||||
pElement->SetAttribute( tamlRefIdName, referenceId );
|
||||
}
|
||||
|
||||
// Do we have a reference to node?
|
||||
else if ( pTamlWriteNode->mRefToNode != NULL )
|
||||
{
|
||||
// Yes, so fetch reference to Id.
|
||||
const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId;
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." );
|
||||
|
||||
// Set reference to Id attribute.
|
||||
pElement->SetAttribute( tamlRefToIdName, referenceToId );
|
||||
|
||||
// Finish because we're a reference to another object.
|
||||
return pElement;
|
||||
}
|
||||
|
||||
// Fetch object name.
|
||||
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
||||
|
||||
// Do we have a name?
|
||||
if ( pObjectName != NULL )
|
||||
{
|
||||
// Yes, so set name attribute.
|
||||
pElement->SetAttribute( tamlNamedObjectName, pObjectName );
|
||||
}
|
||||
|
||||
// Compile attributes.
|
||||
compileAttributes( pElement, pTamlWriteNode );
|
||||
|
||||
// Fetch children.
|
||||
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
||||
|
||||
// Do we have any children?
|
||||
if ( pChildren )
|
||||
{
|
||||
// Yes, so iterate children.
|
||||
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
||||
{
|
||||
// Write child element.
|
||||
pElement->LinkEndChild( compileElement( (*itr) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Compile custom elements.
|
||||
compileCustomElements( pElement, pTamlWriteNode );
|
||||
|
||||
return pElement;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlWriter::compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_CompileAttributes);
|
||||
|
||||
// Fetch fields.
|
||||
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
||||
|
||||
// Ignore if no fields.
|
||||
if ( fields.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
||||
{
|
||||
// Fetch field/value pair.
|
||||
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
||||
|
||||
// Set field attribute.
|
||||
pXmlElement->SetAttribute( pFieldValue->mName, pFieldValue->mpValue );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlWriter::compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_CompileCustomElements);
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
||||
|
||||
// Finish if no custom nodes to process.
|
||||
if ( nodes.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate custom nodes.
|
||||
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
||||
{
|
||||
// Fetch the custom node.
|
||||
TamlCustomNode* pCustomNode = *customNodesItr;
|
||||
|
||||
// Format extended element name.
|
||||
char extendedElementNameBuffer[256];
|
||||
dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pXmlElement->Value(), pCustomNode->getNodeName() );
|
||||
StringTableEntry extendedElementName = StringTable->insert( extendedElementNameBuffer );
|
||||
|
||||
// Create element.
|
||||
TiXmlElement* pExtendedPropertyElement = new fsTiXmlElement( extendedElementName );
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the custom node.
|
||||
compileCustomNode( pExtendedPropertyElement, pChildNode );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pExtendedPropertyElement->NoChildren() )
|
||||
{
|
||||
// Yes, so delete the extended element.
|
||||
delete pExtendedPropertyElement;
|
||||
pExtendedPropertyElement = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so add elementt as child.
|
||||
pXmlElement->LinkEndChild( pExtendedPropertyElement );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlWriter::compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
|
||||
return;
|
||||
|
||||
// Is the node a proxy object?
|
||||
if ( pCustomNode->isProxyObject() )
|
||||
{
|
||||
// Yes, so write the proxy object.
|
||||
pXmlElement->LinkEndChild( compileElement( pCustomNode->getProxyWriteNode() ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Create element.
|
||||
TiXmlElement* pNodeElement = new fsTiXmlElement( pCustomNode->getNodeName() );
|
||||
|
||||
// Is there any node text?
|
||||
if ( !pCustomNode->getNodeTextField().isValueEmpty() )
|
||||
{
|
||||
// Yes, so add a text node.
|
||||
pNodeElement->LinkEndChild( new TiXmlText( pCustomNode->getNodeTextField().getFieldValue() ) );
|
||||
}
|
||||
|
||||
// Fetch fields.
|
||||
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
||||
|
||||
// Iterate fields.
|
||||
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
||||
{
|
||||
// Fetch field.
|
||||
const TamlCustomField* pField = *fieldItr;
|
||||
|
||||
// Set field.
|
||||
pNodeElement->SetAttribute( pField->getFieldName(), pField->getFieldValue() );
|
||||
}
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the child node.
|
||||
compileCustomNode( pNodeElement, pChildNode );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty (including fields).
|
||||
if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pNodeElement->NoChildren() )
|
||||
{
|
||||
// Yes, so delete the extended element.
|
||||
delete pNodeElement;
|
||||
pNodeElement = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add node element as child.
|
||||
pXmlElement->LinkEndChild( pNodeElement );
|
||||
}
|
||||
}
|
||||
59
Engine/source/persistence/taml/xml/tamlXmlWriter.h
Normal file
59
Engine/source/persistence/taml/xml/tamlXmlWriter.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_XMLWRITER_H_
|
||||
#define _TAML_XMLWRITER_H_
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlXmlWriter
|
||||
{
|
||||
public:
|
||||
TamlXmlWriter( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
virtual ~TamlXmlWriter() {}
|
||||
|
||||
/// Write.
|
||||
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
private:
|
||||
TiXmlElement* compileElement( const TamlWriteNode* pTamlWriteNode );
|
||||
void compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode );
|
||||
};
|
||||
|
||||
#endif // _TAML_XMLWRITER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue