tamlBinaryReader.cpp
Engine/source/persistence/taml/binary/tamlBinaryReader.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2013 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "persistence/taml/binary/tamlBinaryReader.h" 25 26#ifndef _ZIPSUBSTREAM_H_ 27#include "core/util/zip/zipSubStream.h" 28#endif 29 30// Debug Profiling. 31#include "platform/profiler.h" 32 33//----------------------------------------------------------------------------- 34 35SimObject* TamlBinaryReader::read( FileStream& stream ) 36{ 37 // Debug Profiling. 38 PROFILE_SCOPE(TamlBinaryReader_Read); 39 40 // Read Taml signature. 41 StringTableEntry tamlSignature = stream.readSTString(); 42 43 // Is the signature correct? 44 if ( tamlSignature != StringTable->insert( TAML_SIGNATURE ) ) 45 { 46 // Warn. 47 Con::warnf("Taml: Cannot read binary file as signature is incorrect '%s'.", tamlSignature ); 48 return NULL; 49 } 50 51 // Read version Id. 52 U32 versionId; 53 stream.read( &versionId ); 54 55 // Read compressed flag. 56 bool compressed; 57 stream.read( &compressed ); 58 59 SimObject* pSimObject = NULL; 60 61 // Is the stream compressed? 62 if ( compressed ) 63 { 64 // Yes, so attach zip stream. 65 ZipSubRStream zipStream; 66 zipStream.attachStream( &stream ); 67 68 // Parse element. 69 pSimObject = parseElement( zipStream, versionId ); 70 71 // Detach zip stream. 72 zipStream.detachStream(); 73 } 74 else 75 { 76 // No, so parse element. 77 pSimObject = parseElement( stream, versionId ); 78 } 79 80 return pSimObject; 81} 82 83//----------------------------------------------------------------------------- 84 85void TamlBinaryReader::resetParse( void ) 86{ 87 // Debug Profiling. 88 PROFILE_SCOPE(TamlBinaryReader_ResetParse); 89 90 // Clear object reference map. 91 mObjectReferenceMap.clear(); 92} 93 94//----------------------------------------------------------------------------- 95 96SimObject* TamlBinaryReader::parseElement( Stream& stream, const U32 versionId ) 97{ 98 // Debug Profiling. 99 PROFILE_SCOPE(TamlBinaryReader_ParseElement); 100 101 SimObject* pSimObject = NULL; 102 103#ifdef TORQUE_DEBUG 104 // Format the type location. 105 char typeLocationBuffer[64]; 106 dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='binary' offset=%u]", stream.getPosition() ); 107#endif 108 109 // Fetch element name. 110 StringTableEntry typeName = stream.readSTString(); 111 112 // Fetch object name. 113 StringTableEntry objectName = stream.readSTString(); 114 115 // Read references. 116 U32 tamlRefId; 117 U32 tamlRefToId; 118 stream.read( &tamlRefId ); 119 stream.read( &tamlRefToId ); 120 121 // Do we have a reference to Id? 122 if ( tamlRefToId != 0 ) 123 { 124 // Yes, so fetch reference. 125 typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId ); 126 127 // Did we find the reference? 128 if ( referenceItr == mObjectReferenceMap.end() ) 129 { 130 // No, so warn. 131 Con::warnf( "Taml: Could not find a reference Id of '%d'", tamlRefToId ); 132 return NULL; 133 } 134 135 // Return object. 136 return referenceItr->value; 137 } 138 139#ifdef TORQUE_DEBUG 140 // Create type. 141 pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer ); 142#else 143 // Create type. 144 pSimObject = Taml::createType( typeName, mpTaml ); 145#endif 146 147 // Finish if we couldn't create the type. 148 if ( pSimObject == NULL ) 149 return NULL; 150 151 // Find Taml callbacks. 152 TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject ); 153 154 // Are there any Taml callbacks? 155 if ( pCallbacks != NULL ) 156 { 157 // Yes, so call it. 158 mpTaml->tamlPreRead( pCallbacks ); 159 } 160 161 // Parse attributes. 162 parseAttributes( stream, pSimObject, versionId ); 163 164 // Does the object require a name? 165 if ( objectName == StringTable->EmptyString() ) 166 { 167 // No, so just register anonymously. 168 pSimObject->registerObject(); 169 } 170 else 171 { 172 // Yes, so register a named object. 173 pSimObject->registerObject( objectName ); 174 175 // Was the name assigned? 176 if ( pSimObject->getName() != objectName ) 177 { 178 // No, so warn that the name was rejected. 179#ifdef TORQUE_DEBUG 180 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 ); 181#else 182 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 ); 183#endif 184 } 185 } 186 187 // Do we have a reference Id? 188 if ( tamlRefId != 0 ) 189 { 190 // Yes, so insert reference. 191 mObjectReferenceMap.insertUnique( tamlRefId, pSimObject ); 192 } 193 194 // Parse custom elements. 195 TamlCustomNodes customProperties; 196 197 // Parse children. 198 parseChildren( stream, pCallbacks, pSimObject, versionId ); 199 200 // Parse custom elements. 201 parseCustomElements( stream, pCallbacks, customProperties, versionId ); 202 203 // Are there any Taml callbacks? 204 if ( pCallbacks != NULL ) 205 { 206 // Yes, so call it. 207 mpTaml->tamlPostRead( pCallbacks, customProperties ); 208 } 209 210 // Return object. 211 return pSimObject; 212} 213 214//----------------------------------------------------------------------------- 215 216void TamlBinaryReader::parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId ) 217{ 218 // Debug Profiling. 219 PROFILE_SCOPE(TamlBinaryReader_ParseAttributes); 220 221 // Sanity! 222 AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." ); 223 224 // Fetch attribute count. 225 U32 attributeCount; 226 stream.read( &attributeCount ); 227 228 // Finish if no attributes. 229 if ( attributeCount == 0 ) 230 return; 231 232 char valueBuffer[4096]; 233 234 // Iterate attributes. 235 for ( U32 index = 0; index < attributeCount; ++index ) 236 { 237 // Fetch attribute. 238 StringTableEntry attributeName = stream.readSTString(); 239 stream.readLongString( 4096, valueBuffer ); 240 241 // We can assume this is a field for now. 242 pSimObject->setPrefixedDataField(attributeName, NULL, valueBuffer); 243 } 244} 245 246//----------------------------------------------------------------------------- 247 248void TamlBinaryReader::parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId ) 249{ 250 // Debug Profiling. 251 PROFILE_SCOPE(TamlBinaryReader_ParseChildren); 252 253 // Sanity! 254 AssertFatal( pSimObject != NULL, "Taml: Cannot parse children on a NULL object." ); 255 256 // Fetch children count. 257 U32 childrenCount; 258 stream.read( &childrenCount ); 259 260 // Finish if no children. 261 if ( childrenCount == 0 ) 262 return; 263 264 // Fetch the Taml children. 265 TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject ); 266 267 // Is this a sim set? 268 if ( pChildren == NULL ) 269 { 270 // No, so warn. 271 Con::warnf("Taml: Child element found under parent but object cannot have children." ); 272 return; 273 } 274 275 // Fetch any container child class specifier. 276 AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true ); 277 278 // Iterate children. 279 for ( U32 index = 0; index < childrenCount; ++ index ) 280 { 281 // Parse child element. 282 SimObject* pChildSimObject = parseElement( stream, versionId ); 283 284 // Finish if child failed. 285 if ( pChildSimObject == NULL ) 286 return; 287 288 // Do we have a container child class? 289 if ( pContainerChildClass != NULL ) 290 { 291 // Yes, so is the child object the correctly derived type? 292 if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) ) 293 { 294 // No, so warn. 295 Con::warnf("Taml: Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.", 296 pChildSimObject->getClassName(), 297 pSimObject->getClassName(), 298 pContainerChildClass->getClassName() ); 299 300 // NOTE: We can't delete the object as it may be referenced elsewhere! 301 pChildSimObject = NULL; 302 303 // Skip. 304 continue; 305 } 306 } 307 308 // Add child. 309 pChildren->addTamlChild( pChildSimObject ); 310 311 // Find Taml callbacks for child. 312 TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject ); 313 314 // Do we have callbacks on the child? 315 if ( pChildCallbacks != NULL ) 316 { 317 // Yes, so perform callback. 318 mpTaml->tamlAddParent( pChildCallbacks, pSimObject ); 319 } 320 } 321} 322 323//----------------------------------------------------------------------------- 324 325void TamlBinaryReader::parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId ) 326{ 327 // Debug Profiling. 328 PROFILE_SCOPE(TamlBinaryReader_ParseCustomElement); 329 330 // Read custom node count. 331 U32 customNodeCount; 332 stream.read( &customNodeCount ); 333 334 // Finish if no custom nodes. 335 if ( customNodeCount == 0 ) 336 return; 337 338 // Iterate custom nodes. 339 for ( U32 nodeIndex = 0; nodeIndex < customNodeCount; ++nodeIndex ) 340 { 341 //Read custom node name. 342 StringTableEntry nodeName = stream.readSTString(); 343 344 // Add custom node. 345 TamlCustomNode* pCustomNode = customNodes.addNode( nodeName ); 346 347 // Parse the custom node. 348 parseCustomNode( stream, pCustomNode, versionId ); 349 } 350 351 // Do we have callbacks? 352 if ( pCallbacks == NULL ) 353 { 354 // No, so warn. 355 Con::warnf( "Taml: Encountered custom data but object does not support custom data." ); 356 return; 357 } 358 359 // Custom read callback. 360 mpTaml->tamlCustomRead( pCallbacks, customNodes ); 361} 362 363//----------------------------------------------------------------------------- 364 365void TamlBinaryReader::parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId ) 366{ 367 // Fetch if a proxy object. 368 bool isProxyObject; 369 stream.read( &isProxyObject ); 370 371 // Is this a proxy object? 372 if ( isProxyObject ) 373 { 374 // Yes, so parse proxy object. 375 SimObject* pProxyObject = parseElement( stream, versionId ); 376 377 // Add child node. 378 pCustomNode->addNode( pProxyObject ); 379 380 return; 381 } 382 383 // No, so read custom node name. 384 StringTableEntry nodeName = stream.readSTString(); 385 386 // Add child node. 387 TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName ); 388 389 // Read child node text. 390 char childNodeTextBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH]; 391 stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, childNodeTextBuffer ); 392 pChildNode->setNodeText( childNodeTextBuffer ); 393 394 // Read child node count. 395 U32 childNodeCount; 396 stream.read( &childNodeCount ); 397 398 // Do we have any children nodes? 399 if ( childNodeCount > 0 ) 400 { 401 // Yes, so parse children nodes. 402 for( U32 childIndex = 0; childIndex < childNodeCount; ++childIndex ) 403 { 404 // Parse child node. 405 parseCustomNode( stream, pChildNode, versionId ); 406 } 407 } 408 409 // Read child field count. 410 U32 childFieldCount; 411 stream.read( &childFieldCount ); 412 413 // Do we have any child fields? 414 if ( childFieldCount > 0 ) 415 { 416 // Yes, so parse child fields. 417 for( U32 childFieldIndex = 0; childFieldIndex < childFieldCount; ++childFieldIndex ) 418 { 419 // Read field name. 420 StringTableEntry fieldName = stream.readSTString(); 421 422 // Read field value. 423 char valueBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH]; 424 stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, valueBuffer ); 425 426 // Add field. 427 pChildNode->addField( fieldName, valueBuffer ); 428 } 429 } 430} 431