mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
431 lines
13 KiB
C++
431 lines
13 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2013 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "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;
|
|
|
|
pSimObject->setFilename(mpTaml->getFilePathBuffer());
|
|
|
|
// 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 );
|
|
}
|
|
}
|
|
} |