simObjectMemento.cpp
Engine/source/console/simObjectMemento.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 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 "platform/platform.h" 25#include "console/simObjectMemento.h" 26 27#include "console/simObject.h" 28#include "console/simDatablock.h" 29#include "core/stream/memStream.h" 30 31 32SimObjectMemento::SimObjectMemento() 33 : mState( NULL ), 34 mIsDatablock( false ) 35{ 36} 37 38SimObjectMemento::~SimObjectMemento() 39{ 40 dFree( mState ); 41} 42 43void SimObjectMemento::save( SimObject *object ) 44{ 45 // Cleanup any existing state data. 46 dFree( mState ); 47 mObjectName = String::EmptyString; 48 49 // Use a stream to save the state. 50 MemStream stream( 256 ); 51 52 U32 writeFlags = 0; 53 SimDataBlock* db = dynamic_cast<SimDataBlock*>(object); 54 if( !db ) 55 stream.write( sizeof( "return " ) - 1, "return " ); 56 else 57 { 58 mIsDatablock = true; 59 60 // Cull the datablock name from the output so that 61 // we can easily replace it in case the datablock's name 62 // is already taken when we call restore(). We can't use the same 63 // setup as with non-datablock classes as the return semantics 64 // are not the same. 65 66 writeFlags |= SimObject::NoName; 67 } 68 69 object->write( stream, 0, writeFlags ); 70 stream.write( (UTF8)0 ); 71 72 // Steal the data away from the stream. 73 mState = (UTF8*)stream.takeBuffer(); 74 mObjectName = object->getName(); 75} 76 77SimObject *SimObjectMemento::restore() const 78{ 79 // Make sure we have data to restore. 80 if ( !mState ) 81 return NULL; 82 83 // TODO: We could potentially make this faster by 84 // caching the CodeBlock generated from the string 85 86 SimObject* object; 87 if( !mIsDatablock ) 88 { 89 // Set the redefine behavior to automatically giving 90 // the new objects unique names. This will restore the 91 // old names if they are still available or give reasonable 92 // approximations if not. 93 94 const char* oldRedefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); 95 Con::setVariable( "$Con::redefineBehavior", "renameNew" ); 96 97 // Read the object. 98 99 const UTF8* result = Con::evaluate( mState ); 100 101 // Restore the redefine behavior. 102 103 Con::setVariable( "$Con::redefineBehavior", oldRedefineBehavior ); 104 105 if ( !result || !result[ 0 ] ) 106 return NULL; 107 108 // Look up the object. 109 110 U32 objectId = dAtoi( result ); 111 object = Sim::findObject( objectId ); 112 } 113 else 114 { 115 String objectName = mObjectName; 116 117 // For datablocks, it's getting a little complicated. Datablock definitions cannot be used 118 // as expressions and thus we can't get to the datablock object we create by using the 119 // Con::evaluate() return value. Instead, we need to rely on the object name. However, if 120 // the name is already taken and needs to be changed, we need to manually do that. To complicate 121 // this further, we cannot rely on automatic renaming since then we don't know by what name 122 // the newly created object actually goes. So what we do is we alter the source text snapshot 123 // and substitute a name in case the old object name is actually taken now. 124 125 char* tempBuffer; 126 if( !Sim::findObject( objectName ) ) 127 tempBuffer = mState; 128 else 129 { 130 String uniqueName = Sim::getUniqueName( objectName ); 131 U32 uniqueNameLen = uniqueName.length(); 132 133 char* pLeftParen = dStrchr( mState, '(' ); 134 if( pLeftParen == NULL ) 135 return NULL; 136 U32 numCharsToLeftParen = pLeftParen - mState; 137 138 dsize_t tempBufferLen = dStrlen(mState) + uniqueNameLen + 1; 139 tempBuffer = ( char* ) dMalloc( tempBufferLen ); 140 dMemcpy( tempBuffer, mState, numCharsToLeftParen ); 141 dMemcpy( &tempBuffer[ numCharsToLeftParen ], uniqueName, uniqueNameLen ); 142 dStrcpy( &tempBuffer[ numCharsToLeftParen + uniqueNameLen ], &mState[ numCharsToLeftParen ], tempBufferLen - numCharsToLeftParen - uniqueNameLen ); 143 } 144 145 Con::evaluate( tempBuffer ); 146 147 if( tempBuffer != mState ) 148 dFree( tempBuffer ); 149 150 if( objectName == String::EmptyString ) 151 return NULL; 152 153 object = Sim::findObject( objectName ); 154 } 155 156 return object; 157} 158