Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2999 lines
85 KiB

  1. //====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "datamodel/idatamodel.h"
  7. #include "datamodel/dmattributevar.h"
  8. #include "datamodel/dmelementfactoryhelper.h"
  9. #include "datamodel.h"
  10. #include "dependencygraph.h"
  11. #include "dmattributeinternal.h"
  12. #include "dmserializerkeyvalues.h"
  13. #include "dmserializerkeyvalues2.h"
  14. #include "dmserializerbinary.h"
  15. #include "undomanager.h"
  16. #include "clipboardmanager.h"
  17. #include "DmElementFramework.h"
  18. #include "vstdlib/iprocessutils.h"
  19. #include "tier0/dbg.h"
  20. #include "tier1/utlvector.h"
  21. #include "tier1/utlqueue.h"
  22. #include "tier1/utlbuffer.h"
  23. #include "tier1/fmtstr.h"
  24. #include "tier2/utlstreambuffer.h"
  25. #include "tier2/fileutils.h"
  26. #include <time.h>
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. //-----------------------------------------------------------------------------
  30. // Forward declarations
  31. //-----------------------------------------------------------------------------
  32. class CUtlBuffer;
  33. class IDmEditMessage;
  34. class KeyValues;
  35. #define UNNAMED_ELEMENT_NAME "unnamed"
  36. //-----------------------------------------------------------------------------
  37. // Class factory for the default element
  38. //-----------------------------------------------------------------------------
  39. class CDmElementFactoryDefault : public IDmElementFactory
  40. {
  41. public:
  42. // Creation, destruction
  43. virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id )
  44. {
  45. return new CDmElement( handle, pElementType, id, pElementName, fileid );
  46. }
  47. virtual void Destroy( DmElementHandle_t hElement )
  48. {
  49. if ( hElement != DMELEMENT_HANDLE_INVALID )
  50. {
  51. CDmElement *pElement = g_pDataModel->GetElement( hElement );
  52. delete static_cast<CDmElement*>( pElement );
  53. }
  54. }
  55. virtual void AddOnElementCreatedCallback( IDmeElementCreated *callback ) {};
  56. virtual void RemoveOnElementCreatedCallback( IDmeElementCreated *callback ) {};
  57. virtual void OnElementCreated( CDmElement* pElement ) {};
  58. };
  59. static CDmElementFactoryDefault s_DefaultElementFactory;
  60. //-----------------------------------------------------------------------------
  61. // Singleton instance
  62. //-----------------------------------------------------------------------------
  63. static CDataModel g_DataModel;
  64. CDataModel *g_pDataModelImp = &g_DataModel;
  65. IDataModel *g_pDataModel = &g_DataModel;
  66. //-----------------------------------------------------------------------------
  67. // Constructor, destructor
  68. //-----------------------------------------------------------------------------
  69. CDataModel::CDataModel() :
  70. m_elementIds( 4096 ),
  71. m_unloadedIdElementMap( 16, 0, 0, ElementIdHandlePair_t::Compare, ElementIdHandlePair_t::HashKey )
  72. {
  73. m_pDefaultFactory = &s_DefaultElementFactory;
  74. m_bUnableToSetDefaultFactory = false;
  75. m_bOnlyCreateUntypedElements = false;
  76. m_bUnableToCreateOnlyUntypedElements = false;
  77. m_pKeyvaluesCallbackInterface = NULL;
  78. m_nElementsAllocatedSoFar = 0;
  79. m_nMaxNumberOfElements = 0;
  80. m_bIsUnserializing = false;
  81. m_bDeleteOrphanedElements = false;
  82. }
  83. CDataModel::~CDataModel()
  84. {
  85. m_UndoMgr.WipeUndo();
  86. if ( GetAllocatedElementCount() > 0 )
  87. {
  88. Warning( "Leaking %i elements\n", GetAllocatedElementCount() );
  89. }
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Methods of IAppSystem
  93. //-----------------------------------------------------------------------------
  94. bool CDataModel::Connect( CreateInterfaceFn factory )
  95. {
  96. if ( !BaseClass::Connect( factory ) )
  97. return false;
  98. if ( !factory( FILESYSTEM_INTERFACE_VERSION, NULL ) )
  99. {
  100. Warning( "DataModel needs the file system to function" );
  101. return false;
  102. }
  103. return true;
  104. }
  105. void *CDataModel::QueryInterface( const char *pInterfaceName )
  106. {
  107. if ( !V_strcmp( pInterfaceName, VDATAMODEL_INTERFACE_VERSION ) )
  108. return (IDataModel*)this;
  109. return NULL;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose:
  113. // Input : *databasePath -
  114. // Output : Returns true on success, false on failure.
  115. //-----------------------------------------------------------------------------
  116. InitReturnVal_t CDataModel::Init( )
  117. {
  118. InitReturnVal_t nRetVal = BaseClass::Init();
  119. if ( nRetVal != INIT_OK )
  120. return nRetVal;
  121. InstallKeyValuesSerializer( this );
  122. InstallKeyValues2Serializer( this );
  123. InstallBinarySerializer( this );
  124. m_UndoMgr.SetUndoDepth( 256 );
  125. return INIT_OK;
  126. }
  127. //#define _ELEMENT_HISTOGRAM_
  128. #ifdef _ELEMENT_HISTOGRAM_
  129. CUtlMap< CUtlSymbolLarge, int > g_typeHistogram( 0, 100, DefLessFunc( CUtlSymbolLarge ) );
  130. #endif _ELEMENT_HISTOGRAM_
  131. //-----------------------------------------------------------------------------
  132. // Purpose:
  133. //-----------------------------------------------------------------------------
  134. void CDataModel::Shutdown()
  135. {
  136. #ifdef _ELEMENT_HISTOGRAM_
  137. Msg( "element type histogram for %d elements allocated so far:\n", GetElementsAllocatedSoFar() );
  138. for ( int i = g_typeHistogram.FirstInorder(); g_typeHistogram.IsValidIndex( i ); i = g_typeHistogram.NextInorder( i ) )
  139. {
  140. Msg( "%d\t%s\n", g_typeHistogram.Element( i ), GetString( g_typeHistogram.Key( i ) ) );
  141. }
  142. Msg( "\n" );
  143. #endif _ELEMENT_HISTOGRAM_
  144. int c = GetAllocatedElementCount();
  145. if ( c > 0 )
  146. {
  147. Warning( "CDataModel: %i elements left in memory!!!\n", c );
  148. }
  149. m_Factories.Purge();
  150. m_Serializers.Purge();
  151. m_UndoMgr.Shutdown();
  152. BaseClass::Shutdown();
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Sets the undo context size
  156. //-----------------------------------------------------------------------------
  157. void CDataModel::SetUndoDepth( int nSize )
  158. {
  159. m_UndoMgr.SetUndoDepth( nSize );
  160. }
  161. //-----------------------------------------------------------------------------
  162. // force creation of untyped elements, ignoring type
  163. //-----------------------------------------------------------------------------
  164. void CDataModel::OnlyCreateUntypedElements( bool bEnable )
  165. {
  166. if ( m_bUnableToCreateOnlyUntypedElements )
  167. {
  168. Assert( 0 );
  169. return;
  170. }
  171. m_bOnlyCreateUntypedElements = bEnable;
  172. }
  173. int CDataModel::GetElementsAllocatedSoFar()
  174. {
  175. return m_nElementsAllocatedSoFar;
  176. }
  177. int CDataModel::GetMaxNumberOfElements()
  178. {
  179. return m_nMaxNumberOfElements;
  180. }
  181. int CDataModel::GetAllocatedAttributeCount()
  182. {
  183. return ::GetAllocatedAttributeCount();
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Returns the total number of elements allocated at the moment
  187. //-----------------------------------------------------------------------------
  188. int CDataModel::GetAllocatedElementCount()
  189. {
  190. return ( int )m_Handles.GetValidHandleCount();
  191. }
  192. DmElementHandle_t CDataModel::FirstAllocatedElement()
  193. {
  194. int nHandles = ( int )m_Handles.GetHandleCount();
  195. for ( int i = 0; i < nHandles; ++i )
  196. {
  197. DmElementHandle_t hElement = ( DmElementHandle_t )m_Handles.GetHandleFromIndex( i );
  198. if ( CDmElement *pElement = GetElement( hElement ) )
  199. return hElement;
  200. }
  201. return DMELEMENT_HANDLE_INVALID;
  202. }
  203. DmElementHandle_t CDataModel::NextAllocatedElement( DmElementHandle_t hElement )
  204. {
  205. int nHandles = ( int )m_Handles.GetHandleCount();
  206. for ( int i = m_Handles.GetIndexFromHandle( hElement ) + 1; i < nHandles; ++i )
  207. {
  208. DmElementHandle_t hElement = ( DmElementHandle_t )m_Handles.GetHandleFromIndex( i );
  209. if ( CDmElement *pElement = GetElement( hElement ) )
  210. return hElement;
  211. }
  212. return DMELEMENT_HANDLE_INVALID;
  213. }
  214. //-----------------------------------------------------------------------------
  215. // estimate memory overhead
  216. //-----------------------------------------------------------------------------
  217. int CDataModel::EstimateMemoryOverhead() const
  218. {
  219. int nHandlesOverhead = sizeof( int ) + sizeof( CDmElement* ); // m_Handles
  220. int nElementIdsOverhead = sizeof( DmElementHandle_t ); // this also has a 80k static overhead, since hash tables can't grow
  221. return nHandlesOverhead + nElementIdsOverhead;
  222. }
  223. static bool HandleCompare( const DmElementHandle_t & a, const DmElementHandle_t &b )
  224. {
  225. return a == b;
  226. }
  227. static unsigned int HandleHash( const DmElementHandle_t &h )
  228. {
  229. return (unsigned int)h;
  230. }
  231. int CDataModel::EstimateMemoryUsage( DmElementHandle_t hElement, TraversalDepth_t depth )
  232. {
  233. CUtlHash< DmElementHandle_t > visited( 1024, 0, 0, HandleCompare, HandleHash );
  234. CDmElement *pElement = m_Handles.GetHandle( hElement );
  235. if ( !pElement )
  236. return 0;
  237. return CDmeElementAccessor::EstimateMemoryUsage( pElement, visited, depth, NULL );
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Displays stats for datamodel
  241. //-----------------------------------------------------------------------------
  242. struct DmMemoryInfo_t
  243. {
  244. int m_nCount;
  245. int m_nSize;
  246. int m_pCategories[ MEMORY_CATEGORY_COUNT ];
  247. };
  248. struct DmMemorySortInfo_t
  249. {
  250. int m_nIndex;
  251. int m_nTotalSize;
  252. };
  253. int DmMemorySortFunc( const void * lhs, const void * rhs )
  254. {
  255. DmMemorySortInfo_t &info1 = *(DmMemorySortInfo_t*)lhs;
  256. DmMemorySortInfo_t &info2 = *(DmMemorySortInfo_t*)rhs;
  257. return info1.m_nTotalSize - info2.m_nTotalSize;
  258. }
  259. // this method assumes that all elements that should be traversed have a parity of !bParity
  260. static void GatherElements_R( CDmElement *pElement, CUtlVector< DmElementHandle_t > &list, CUtlHash< DmElementHandle_t > &visited )
  261. {
  262. DmElementHandle_t h = pElement->GetHandle();
  263. if ( visited.Find( h ) != visited.InvalidHandle() )
  264. return;
  265. visited.Insert( h );
  266. list.AddToTail( h );
  267. for ( const CDmAttribute *pAttr = pElement->FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  268. {
  269. if ( pAttr->GetType() == AT_ELEMENT )
  270. {
  271. CDmElement *pChild = pAttr->GetValueElement<CDmElement>();
  272. if ( !pChild )
  273. continue;
  274. GatherElements_R( pChild, list, visited );
  275. }
  276. else if ( pAttr->GetType() == AT_ELEMENT_ARRAY )
  277. {
  278. const CDmrElementArrayConst<> elementArrayAttr( pAttr );
  279. int nChildren = elementArrayAttr.Count();
  280. for ( int i = 0; i < nChildren; ++i )
  281. {
  282. CDmElement *pChild = elementArrayAttr[ i ];
  283. if ( !pChild )
  284. continue;
  285. GatherElements_R( pChild, list, visited );
  286. }
  287. }
  288. }
  289. }
  290. void CDataModel::BuildHistogramForHandles( CUtlMap< CUtlSymbolLarge, DmMemoryInfo_t > &typeHistogram, CUtlVector< DmElementHandle_t > &handles )
  291. {
  292. CUtlHash< DmElementHandle_t > visited( 1024, 0, 0, HandleCompare, HandleHash );
  293. int c = handles.Count();
  294. for ( int i = 0; i < c; ++i )
  295. {
  296. DmElementHandle_t h = handles[ i ];
  297. if ( h == DMELEMENT_HANDLE_INVALID )
  298. continue;
  299. CDmElement *pElement = m_Handles.GetHandle( h );
  300. if ( !pElement )
  301. continue;
  302. unsigned short j = typeHistogram.Find( pElement->GetType() );
  303. if ( !typeHistogram.IsValidIndex( j ) )
  304. {
  305. j = typeHistogram.Insert( pElement->GetType() );
  306. typeHistogram[j].m_nCount = 0;
  307. typeHistogram[j].m_nSize = 0;
  308. memset( typeHistogram[j].m_pCategories, 0, sizeof(typeHistogram[j].m_pCategories) );
  309. }
  310. int nMemory = CDmeElementAccessor::EstimateMemoryUsage( pElement, visited, TD_NONE, typeHistogram[j].m_pCategories );
  311. ++typeHistogram[j].m_nCount;
  312. typeHistogram[j].m_nSize += nMemory;
  313. }
  314. }
  315. void CDataModel::DisplayMemoryStats( DmElementHandle_t hElement /*= DMELEMENT_HANDLE_INVALID*/ )
  316. {
  317. CUtlMap< CUtlSymbolLarge, DmMemoryInfo_t > typeHistogram( 0, 100, DefLessFunc( CUtlSymbolLarge ) );
  318. CUtlHash< DmElementHandle_t > visited( 1024, 0, 0, HandleCompare, HandleHash );
  319. CUtlVector< DmElementHandle_t > handles;
  320. if ( hElement == DMELEMENT_HANDLE_INVALID )
  321. {
  322. int c = (int)m_Handles.GetHandleCount();
  323. for ( int i = 0; i < c; ++i )
  324. {
  325. DmElementHandle_t h = (DmElementHandle_t)m_Handles.GetHandleFromIndex( i );
  326. if ( !m_Handles.IsHandleValid( h ) )
  327. continue;
  328. handles.AddToTail( h );
  329. }
  330. }
  331. else
  332. {
  333. // Do a recursive build
  334. if ( !m_Handles.IsHandleValid( hElement ) )
  335. {
  336. Msg( "CDataModel::DisplayMemoryStats with invalid handle %u\n", (int)hElement );
  337. return;
  338. }
  339. CDmElement *pElement = m_Handles.GetHandle( hElement );
  340. if( pElement )
  341. {
  342. GatherElements_R( pElement, handles, visited );
  343. }
  344. }
  345. BuildHistogramForHandles( typeHistogram, handles );
  346. // Sort
  347. DmMemorySortInfo_t* pSortInfo = (DmMemorySortInfo_t*)_alloca( typeHistogram.Count() * sizeof(DmMemorySortInfo_t) );
  348. int nCount = 0;
  349. for ( int i = typeHistogram.FirstInorder(); typeHistogram.IsValidIndex( i ); i = typeHistogram.NextInorder( i ) )
  350. {
  351. pSortInfo[nCount].m_nIndex = i;
  352. pSortInfo[nCount].m_nTotalSize = typeHistogram.Element( i ).m_nSize;
  353. ++nCount;
  354. }
  355. qsort( pSortInfo, nCount, sizeof(DmMemorySortInfo_t), DmMemorySortFunc );
  356. int pTotals[ MEMORY_CATEGORY_COUNT ] = { 0 };
  357. int nLastTwoSize = 0;
  358. int nTotalSize = 0;
  359. int nTotalCount = 0;
  360. int nTotalData = 0;
  361. ConMsg( "Dm Memory usage: type\t\t\t\tcount\ttotalsize\twastage %%\touter\t\tinner\t\tdatamodel\trefs\t\ttree\t\tatts\t\tdata\t(att count)\n" );
  362. for ( int i = 0; i < nCount; ++i )
  363. {
  364. const DmMemoryInfo_t& info = typeHistogram.Element( pSortInfo[i].m_nIndex );
  365. float flPercentOverhead = 1.0f - ( ( info.m_nSize != 0 ) ? ( (float)info.m_pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] / (float)info.m_nSize ) : 0.0f );
  366. flPercentOverhead *= 100.0f;
  367. ConMsg( "%-40s\t%6d\t%9d\t\t%5.2f", typeHistogram.Key( pSortInfo[i].m_nIndex ).String(),
  368. info.m_nCount, info.m_nSize, flPercentOverhead );
  369. int nTotal = 0;
  370. for ( int j = 0; j < MEMORY_CATEGORY_COUNT; ++j )
  371. {
  372. ConColorMsg( Color( 255, 192, 0, 255 ), "\t%8d", info.m_pCategories[j] );
  373. if ( j != MEMORY_CATEGORY_ATTRIBUTE_COUNT )
  374. {
  375. nTotal += info.m_pCategories[j];
  376. }
  377. pTotals[j] += info.m_pCategories[j];
  378. }
  379. ConMsg( "\n" );
  380. Assert( nTotal == info.m_nSize );
  381. nTotalSize += info.m_nSize;
  382. nTotalCount += info.m_nCount;
  383. nTotalData += info.m_pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA];
  384. if ( i >= nCount - 2 )
  385. {
  386. nLastTwoSize += info.m_nSize;
  387. }
  388. }
  389. ConMsg( "\n" );
  390. ConMsg( "%-40s\t%6d\t%9d\t\t%5.2f", "Totals", nTotalCount, nTotalSize, 100.0f * ( 1.0f - (float)nTotalData / (float)nTotalSize ) );
  391. for ( int j = 0; j < MEMORY_CATEGORY_COUNT; ++j )
  392. {
  393. ConColorMsg( Color( 255, 192, 0, 255 ), "\t%8d", pTotals[j] );
  394. }
  395. ConMsg( "\n" );
  396. float flPercent = 0.0f;
  397. if ( nTotalSize > 0 )
  398. {
  399. flPercent = 100.0 *((float)nLastTwoSize/(float)nTotalSize);
  400. }
  401. ConMsg( "Last two rows as percentage of total size %f %%:\n", flPercent );
  402. uint64 symbolBytes = m_SymbolTable.GetMemoryUsage();
  403. int symbolCount = m_SymbolTable.GetNumStrings();
  404. // Estimate string table usage
  405. ConMsg( "Symbol table: %d entries %I64u bytes\n", symbolCount, symbolBytes );
  406. }
  407. void IncrementSymbolRefCount( CUtlSymbolLarge symbol, CUtlMap< CUtlSymbolLarge, int, int > &refCountMap )
  408. {
  409. int nIndex = refCountMap.Find( symbol );
  410. if ( nIndex == refCountMap.InvalidIndex() )
  411. {
  412. nIndex = refCountMap.Insert( symbol );
  413. refCountMap[ nIndex ] = 1;
  414. }
  415. else
  416. {
  417. refCountMap[ nIndex ]++;
  418. }
  419. }
  420. int CompareSymbolsAcending( const CUtlSymbolLarge *symA, const CUtlSymbolLarge *symB )
  421. {
  422. return V_stricmp( symA->String(), symB->String() );
  423. }
  424. // Dump the symbol table to the console
  425. void CDataModel::DumpSymbolTable()
  426. {
  427. uint64 symbolBytes = m_SymbolTable.GetMemoryUsage();
  428. int symbolCount = m_SymbolTable.GetNumStrings();
  429. // Estimate string table usage
  430. ConMsg( "Datamodel symbol table: %d entries %I64u bytes\n", symbolCount, symbolBytes );
  431. CUtlMap< CUtlSymbolLarge, int, int > symbolRefCountMap( 0, symbolCount, DefLessFunc( CUtlSymbolLarge ) );
  432. int nNumElements = (int)m_Handles.GetHandleCount();
  433. for ( int iElement = 0; iElement < nNumElements; ++iElement )
  434. {
  435. CDmElement *pElement = m_Handles.GetHandle( iElement );
  436. if ( pElement == NULL )
  437. continue;
  438. IncrementSymbolRefCount( pElement->GetType(), symbolRefCountMap );
  439. for ( CDmAttribute *pAttr = pElement->FirstAttribute(); pAttr; pAttr = pAttr->NextAttribute() )
  440. {
  441. IncrementSymbolRefCount( pAttr->GetNameSymbol(), symbolRefCountMap );
  442. if ( pAttr->GetType() == AT_STRING )
  443. {
  444. IncrementSymbolRefCount( pAttr->GetValue< CUtlSymbolLarge >(), symbolRefCountMap );
  445. }
  446. }
  447. }
  448. CUtlBuffer buf( 0, symbolBytes + ( symbolCount * 32 ), CUtlBuffer::TEXT_BUFFER );
  449. buf.Printf( "Datamodel symbol table: %d entries %I64u bytes\n", symbolCount, symbolBytes );
  450. CUtlVector< CUtlSymbolLarge > elements;
  451. elements.EnsureCount( symbolCount );
  452. m_SymbolTable.GetElements( 0, symbolCount, elements.Base() );
  453. elements.Sort( CompareSymbolsAcending );
  454. buf.Printf( "Index Address References String\n" );
  455. for ( int i = 0; i < symbolCount; ++i )
  456. {
  457. CUtlSymbolLarge symbol = elements[ i ];
  458. int nIndex = symbolRefCountMap.Find( symbol );
  459. int nRefCount = ( nIndex != symbolRefCountMap.InvalidIndex() ) ? symbolRefCountMap[ nIndex ] : 0;
  460. const char *pString = symbol.String();
  461. buf.Printf( "%05d, 0x%p, %5d, '%s'\n", i, pString, nRefCount, pString );
  462. }
  463. int nFileIndex = 0;
  464. int nMaxFileIndex = 1000;
  465. const char baseFilename[] = "DatamodelSymbols";
  466. CUtlString filename;
  467. char directory[ 256 ];
  468. g_pFullFileSystem->GetCurrentDirectory( directory, sizeof( directory ) );
  469. while ( nFileIndex < nMaxFileIndex )
  470. {
  471. filename = CFmtStr( "%s\\%s%i.txt", directory, baseFilename, nFileIndex );
  472. if ( !g_pFullFileSystem->FileExists( filename ) )
  473. break;
  474. ++nFileIndex;
  475. }
  476. if ( nFileIndex < nMaxFileIndex )
  477. {
  478. g_pFullFileSystem->WriteFile( filename, NULL, buf );
  479. Msg( "Wrote symbol stats to file: %s", filename.Get() );
  480. }
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Global symbol table for the datamodel system
  484. //-----------------------------------------------------------------------------
  485. CUtlSymbolLarge CDataModel::GetSymbol( const char *pString )
  486. {
  487. return m_SymbolTable.AddString( pString );
  488. }
  489. //-----------------------------------------------------------------------------
  490. // file format methods
  491. //-----------------------------------------------------------------------------
  492. const char* CDataModel::GetFormatExtension( const char *pFormatName )
  493. {
  494. IDmFormatUpdater *pUpdater = FindFormatUpdater( pFormatName );
  495. Assert( pUpdater );
  496. if ( !pUpdater )
  497. return NULL;
  498. return pUpdater->GetExtension();
  499. }
  500. const char* CDataModel::GetFormatDescription( const char *pFormatName )
  501. {
  502. IDmFormatUpdater *pUpdater = FindFormatUpdater( pFormatName );
  503. Assert( pUpdater );
  504. if ( !pUpdater )
  505. return NULL;
  506. return pUpdater->GetDescription();
  507. }
  508. int CDataModel::GetFormatCount() const
  509. {
  510. return m_FormatUpdaters.Count();
  511. }
  512. const char* CDataModel::GetFormatName( int i ) const
  513. {
  514. IDmFormatUpdater *pUpdater = m_FormatUpdaters[ i ];
  515. if ( !pUpdater )
  516. return NULL;
  517. return pUpdater->GetName();
  518. }
  519. const char *CDataModel::GetDefaultEncoding( const char *pFormatName )
  520. {
  521. IDmFormatUpdater *pUpdater = FindFormatUpdater( pFormatName );
  522. if ( !pUpdater )
  523. return NULL;
  524. return pUpdater->GetDefaultEncoding();
  525. }
  526. //-----------------------------------------------------------------------------
  527. // Adds various serializers
  528. //-----------------------------------------------------------------------------
  529. void CDataModel::AddSerializer( IDmSerializer *pSerializer )
  530. {
  531. Assert( Q_strlen( pSerializer->GetName() ) <= DMX_MAX_FORMAT_NAME_MAX_LENGTH );
  532. if ( FindSerializer( pSerializer->GetName() ) )
  533. {
  534. Warning("Attempted to add two serializers with the same file encoding (%s)!\n", pSerializer->GetName() );
  535. return;
  536. }
  537. m_Serializers.AddToTail( pSerializer );
  538. }
  539. void CDataModel::AddLegacyUpdater( IDmLegacyUpdater *pUpdater )
  540. {
  541. Assert( Q_strlen( pUpdater->GetName() ) <= DMX_MAX_FORMAT_NAME_MAX_LENGTH );
  542. if ( FindLegacyUpdater( pUpdater->GetName() ) )
  543. {
  544. Warning( "Attempted to add two legacy updaters with the same file format (%s)!\n", pUpdater->GetName() );
  545. return;
  546. }
  547. m_LegacyUpdaters.AddToTail( pUpdater );
  548. }
  549. void CDataModel::AddFormatUpdater( IDmFormatUpdater *pUpdater )
  550. {
  551. Assert( Q_strlen( pUpdater->GetName() ) <= DMX_MAX_FORMAT_NAME_MAX_LENGTH );
  552. if ( FindFormatUpdater( pUpdater->GetName() ) )
  553. {
  554. Warning( "Attempted to add two format updaters with the same file format (%s)!\n", pUpdater->GetName() );
  555. return;
  556. }
  557. m_FormatUpdaters.AddToTail( pUpdater );
  558. }
  559. //-----------------------------------------------------------------------------
  560. // encoding-related methods
  561. //-----------------------------------------------------------------------------
  562. int CDataModel::GetEncodingCount() const
  563. {
  564. return m_Serializers.Count();
  565. }
  566. const char *CDataModel::GetEncodingName( int i ) const
  567. {
  568. return m_Serializers[ i ]->GetName();
  569. }
  570. bool CDataModel::IsEncodingBinary( const char *pEncodingName ) const
  571. {
  572. IDmSerializer *pSerializer = FindSerializer( pEncodingName );
  573. if ( !pSerializer )
  574. {
  575. Warning("Serialize: File encoding %s is undefined!\n", pEncodingName );
  576. return false;
  577. }
  578. return pSerializer->IsBinaryFormat();
  579. }
  580. bool CDataModel::DoesEncodingStoreVersionInFile( const char *pEncodingName ) const
  581. {
  582. IDmSerializer *pSerializer = FindSerializer( pEncodingName );
  583. if ( !pSerializer )
  584. {
  585. Warning("Serialize: File encoding %s is undefined!\n", pEncodingName );
  586. return false;
  587. }
  588. return pSerializer->StoresVersionInFile();
  589. }
  590. IDmSerializer* CDataModel::FindSerializer( const char *pEncodingName ) const
  591. {
  592. int nSerializers = m_Serializers.Count();
  593. for ( int i = 0; i < nSerializers; ++i )
  594. {
  595. IDmSerializer *pSerializer = m_Serializers[ i ];
  596. Assert( pSerializer );
  597. if ( !pSerializer )
  598. continue;
  599. if ( !V_strcmp( pEncodingName, pSerializer->GetName() ) )
  600. return pSerializer;
  601. }
  602. return NULL;
  603. }
  604. IDmLegacyUpdater* CDataModel::FindLegacyUpdater( const char *pLegacyFormatName ) const
  605. {
  606. int nUpdaters = m_LegacyUpdaters.Count();
  607. for ( int i = 0; i < nUpdaters; ++i )
  608. {
  609. IDmLegacyUpdater *pUpdater = m_LegacyUpdaters[ i ];
  610. Assert( pUpdater );
  611. if ( !pUpdater )
  612. continue;
  613. if ( !V_strcmp( pLegacyFormatName, pUpdater->GetName() ) )
  614. return pUpdater;
  615. }
  616. return NULL;
  617. }
  618. IDmFormatUpdater* CDataModel::FindFormatUpdater( const char *pFormatName ) const
  619. {
  620. int nUpdaters = m_FormatUpdaters.Count();
  621. for ( int i = 0; i < nUpdaters; ++i )
  622. {
  623. IDmFormatUpdater *pUpdater = m_FormatUpdaters[ i ];
  624. Assert( pUpdater );
  625. if ( !pUpdater )
  626. continue;
  627. if ( !V_strcmp( pFormatName, pUpdater->GetName() ) )
  628. return pUpdater;
  629. }
  630. return NULL;
  631. }
  632. //-----------------------------------------------------------------------------
  633. // Purpose: Sets the name of the DME element to create in keyvalues serialization
  634. //-----------------------------------------------------------------------------
  635. void CDataModel::SetKeyValuesElementCallback( IElementForKeyValueCallback *pCallbackInterface )
  636. {
  637. m_pKeyvaluesCallbackInterface = pCallbackInterface;
  638. }
  639. //-----------------------------------------------------------------------------
  640. // Purpose:
  641. //-----------------------------------------------------------------------------
  642. const char *CDataModel::GetKeyValuesElementName( const char *pszKeyName, int iNestingLevel )
  643. {
  644. if ( m_pKeyvaluesCallbackInterface )
  645. return m_pKeyvaluesCallbackInterface->GetElementForKeyValue( pszKeyName, iNestingLevel );
  646. return NULL;
  647. }
  648. //-----------------------------------------------------------------------------
  649. // For serialization, set the delimiter rules
  650. //-----------------------------------------------------------------------------
  651. void CDataModel::SetSerializationDelimiter( CUtlCharConversion *pConv )
  652. {
  653. ::SetSerializationDelimiter( pConv );
  654. }
  655. void CDataModel::SetSerializationArrayDelimiter( const char *pDelimiter )
  656. {
  657. ::SetSerializationArrayDelimiter( pDelimiter );
  658. }
  659. bool CDataModel::SaveToFile( char const *pFileName, char const *pPathID, const char *pEncodingName, const char *pFormatName, CDmElement *pRoot )
  660. {
  661. // NOTE: This guarantees full path names for pathids
  662. char pFullPath[ MAX_PATH ];
  663. if ( !GenerateFullPath( pFileName, pPathID, pFullPath, sizeof( pFullPath ) ) )
  664. {
  665. Warning( "CDataModel: Unable to generate full path for file %s\n", pFileName );
  666. return false;
  667. }
  668. if ( g_pFullFileSystem->FileExists( pFullPath, pPathID ) )
  669. {
  670. if ( !g_pFullFileSystem->IsFileWritable( pFullPath, pPathID ) )
  671. {
  672. Warning( "CDataModel: Unable to overwrite readonly file %s\n", pFullPath );
  673. return false;
  674. }
  675. }
  676. if ( !pEncodingName )
  677. {
  678. pEncodingName = GetDefaultEncoding( pFormatName );
  679. }
  680. bool bIsBinary = IsEncodingBinary( pEncodingName );
  681. if ( bIsBinary )
  682. {
  683. CUtlStreamBuffer buf( pFullPath, pPathID, 0, true );
  684. if ( !buf.IsValid() )
  685. {
  686. Warning( "CDataModel: Unable to open file \"%s\"\n", pFullPath );
  687. return false;
  688. }
  689. bool bOk = Serialize( buf, pEncodingName, pFormatName, pRoot->GetHandle() );
  690. if ( !bOk )
  691. return false;
  692. }
  693. else
  694. {
  695. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  696. bool bOk = Serialize( buf, pEncodingName, pFormatName, pRoot->GetHandle() );
  697. if ( !bOk )
  698. return false;
  699. if ( !g_pFullFileSystem->WriteFile( pFullPath, pPathID, buf ) )
  700. {
  701. Warning( "CDataModel: Unable to open file \"%s\"\n", pFullPath );
  702. return false;
  703. }
  704. }
  705. DmFileId_t fileid = GetFileId( pFullPath );
  706. if ( fileid != DMFILEID_INVALID )
  707. {
  708. SetFileModificationUTCTime( fileid, GetCurrentUTCTime() );
  709. }
  710. return true;
  711. }
  712. DmFileId_t CDataModel::RestoreFromFile( char const *pFileName, char const *pPathID, const char *pEncodingHint, CDmElement **ppRoot, DmConflictResolution_t idConflictResolution /*= CR_DELETE_NEW*/, DmxHeader_t *pHeaderOut /*= NULL*/ )
  713. {
  714. // NOTE: This guarantees full path names for pathids
  715. char pFullPath[ MAX_PATH ];
  716. if ( !GenerateFullPath( pFileName, pPathID, pFullPath, sizeof( pFullPath ) ) )
  717. {
  718. Warning( "CDataModel: Unable to generate full path for file %s\n", pFileName );
  719. return DMFILEID_INVALID;
  720. }
  721. char *pTemp = (char*)_alloca( DMX_MAX_HEADER_LENGTH + 1 );
  722. CUtlBuffer typeBuf( pTemp, DMX_MAX_HEADER_LENGTH );
  723. if ( !g_pFullFileSystem->ReadFile( pFullPath, pPathID, typeBuf, DMX_MAX_HEADER_LENGTH ) )
  724. {
  725. Warning( "CDataModel: Unable to open file %s\n", pFullPath );
  726. return DMFILEID_INVALID;
  727. }
  728. DmxHeader_t _header;
  729. DmxHeader_t *pHeader = pHeaderOut ? pHeaderOut : &_header;
  730. bool bSuccess = ReadDMXHeader( typeBuf, pHeader );
  731. if ( !bSuccess )
  732. {
  733. if ( !pEncodingHint )
  734. {
  735. Warning( "CDataModel: Unable to determine DMX encoding for file %s\n", pFullPath );
  736. return DMFILEID_INVALID;
  737. }
  738. if ( !IsValidNonDMXFormat( pEncodingHint ) )
  739. {
  740. Warning( "CDataModel: Invalid DMX encoding hint '%s' for file %s\n", pEncodingHint, pFullPath );
  741. return DMFILEID_INVALID;
  742. }
  743. // non-dmx file importers don't have versions or formats, just encodings
  744. V_strncpy( pHeader->encodingName, pEncodingHint, sizeof( pHeader->encodingName ) );
  745. pHeader->formatName[0] = 0;
  746. }
  747. DmElementHandle_t hRootElement;
  748. bool bIsBinary = IsEncodingBinary( pHeader->encodingName );
  749. if ( bIsBinary )
  750. {
  751. CUtlStreamBuffer buf( pFullPath, pPathID, CUtlBuffer::READ_ONLY );
  752. if ( !buf.IsValid() )
  753. {
  754. Warning( "CDataModel: Unable to open file '%s'\n", pFullPath );
  755. return DMFILEID_INVALID;
  756. }
  757. if ( !Unserialize( buf, pHeader->encodingName, pHeader->formatName, pEncodingHint, pFullPath, idConflictResolution, hRootElement ) )
  758. return DMFILEID_INVALID;
  759. }
  760. else
  761. {
  762. CUtlBuffer buf( 0, 0, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
  763. if ( !g_pFullFileSystem->ReadFile( pFullPath, pPathID, buf ) )
  764. {
  765. Warning( "CDataModel: Unable to open file '%s'\n", pFullPath );
  766. return DMFILEID_INVALID;
  767. }
  768. if ( !Unserialize( buf, pHeader->encodingName, pHeader->formatName, pEncodingHint, pFullPath, idConflictResolution, hRootElement ) )
  769. return DMFILEID_INVALID;
  770. }
  771. *ppRoot = g_pDataModel->GetElement( hRootElement );
  772. DmFileId_t fileid = g_pDataModel->GetFileId( pFullPath );
  773. Assert( fileid != DMFILEID_INVALID );
  774. if ( fileid != DMFILEID_INVALID )
  775. {
  776. SetFileModificationUTCTime( fileid, g_pFullFileSystem->GetFileTime( pFullPath ) );
  777. }
  778. return fileid;
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Is this a DMX file format?
  782. //-----------------------------------------------------------------------------
  783. bool CDataModel::IsDMXFormat( CUtlBuffer &buf ) const
  784. {
  785. DmxHeader_t header;
  786. bool bSuccess = ReadDMXHeader( buf, &header );
  787. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  788. return bSuccess;
  789. }
  790. //-----------------------------------------------------------------------------
  791. // Serialization of a element tree into a utlbuffer
  792. //-----------------------------------------------------------------------------
  793. bool CDataModel::Serialize( CUtlBuffer &outBuf, const char *pEncodingName, const char *pFormatName, DmElementHandle_t hRoot )
  794. {
  795. if ( !pEncodingName )
  796. {
  797. pEncodingName = outBuf.IsText() ? "keyvalues2" : "binary";
  798. }
  799. // Find a serializer appropriate for the file format.
  800. IDmSerializer *pSerializer = FindSerializer( pEncodingName );
  801. if ( !pSerializer )
  802. {
  803. Warning("Serialize: File encoding '%s' is undefined!\n", pEncodingName );
  804. return false;
  805. }
  806. // Ensure the utlbuffer is in the appropriate format (binary/text)
  807. bool bIsText = outBuf.IsText();
  808. bool bIsCRLF = outBuf.ContainsCRLF();
  809. CUtlBuffer outTextBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
  810. CUtlBuffer *pActualOutBuf = &outBuf;
  811. if ( pSerializer->IsBinaryFormat() )
  812. {
  813. if ( outBuf.IsText() )
  814. {
  815. if ( !outBuf.ContainsCRLF() )
  816. {
  817. Warning( "Serialize: Format %s expects to be written to a binary format, but the buffer is a text-format buffer\n", pFormatName );
  818. return false;
  819. }
  820. outBuf.SetBufferType( false, false );
  821. }
  822. }
  823. else
  824. {
  825. // If we want text, but the binbuf is binary; recast it to a text buffer w/ CRLF
  826. if ( !outBuf.IsText() )
  827. {
  828. outBuf.SetBufferType( true, true );
  829. }
  830. if ( outBuf.ContainsCRLF() )
  831. {
  832. // If we want text, but the binbuf expects CRLF, then we must do a conversion pass
  833. pActualOutBuf = &outTextBuffer;
  834. }
  835. }
  836. if ( pSerializer->StoresVersionInFile() )
  837. {
  838. // Write the format name into the file using XML format so that
  839. // 3rd-party XML readers can read the file without fail
  840. pActualOutBuf->Printf( "%s encoding %s %d format %s %d %s\n",
  841. DMX_VERSION_STARTING_TOKEN, pEncodingName, pSerializer->GetCurrentVersion(),
  842. pFormatName, GetCurrentFormatVersion( pFormatName ), DMX_VERSION_ENDING_TOKEN );
  843. }
  844. // Now write the file using the appropriate format
  845. CDmElement *pRoot = GetElement( hRoot );
  846. bool bOk = pSerializer->Serialize( *pActualOutBuf, pRoot );
  847. if ( bOk )
  848. {
  849. if ( pActualOutBuf == &outTextBuffer )
  850. {
  851. outTextBuffer.ConvertCRLF( outBuf );
  852. }
  853. }
  854. outBuf.SetBufferType( bIsText, bIsCRLF );
  855. return bOk;
  856. }
  857. //-----------------------------------------------------------------------------
  858. // Read the header, return the version (or false if it's not a DMX file)
  859. //-----------------------------------------------------------------------------
  860. bool CDataModel::ReadDMXHeader( CUtlBuffer &inBuf, DmxHeader_t *pHeader ) const
  861. {
  862. Assert( pHeader );
  863. if ( !pHeader )
  864. return false;
  865. // Make the buffer capable of being read as text
  866. bool bIsText = inBuf.IsText();
  867. bool bHasCRLF = inBuf.ContainsCRLF();
  868. inBuf.SetBufferType( true, !bIsText || bHasCRLF );
  869. char headerStr[ DMX_MAX_HEADER_LENGTH ];
  870. bool bOk = inBuf.ParseToken( DMX_VERSION_STARTING_TOKEN, DMX_VERSION_ENDING_TOKEN, headerStr, sizeof( headerStr ) );
  871. if ( bOk )
  872. {
  873. #ifdef _WIN32
  874. int nAssigned = sscanf_s( headerStr, "encoding %s %d format %s %d\n",
  875. pHeader->encodingName, DMX_MAX_FORMAT_NAME_MAX_LENGTH, &( pHeader->nEncodingVersion ),
  876. pHeader->formatName, DMX_MAX_FORMAT_NAME_MAX_LENGTH, &( pHeader->nFormatVersion ) );
  877. #else
  878. int nAssigned = sscanf( headerStr, "encoding %s %d format %s %d\n",
  879. pHeader->encodingName, &( pHeader->nEncodingVersion ),
  880. pHeader->formatName, &( pHeader->nFormatVersion ) );
  881. #endif
  882. bOk = nAssigned == 4;
  883. }
  884. if ( !bOk )
  885. {
  886. inBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  887. bOk = inBuf.ParseToken( DMX_LEGACY_VERSION_STARTING_TOKEN, DMX_LEGACY_VERSION_ENDING_TOKEN, pHeader->formatName, DMX_MAX_FORMAT_NAME_MAX_LENGTH );
  888. if ( bOk )
  889. {
  890. const char *pEncoding = GetEncodingFromLegacyFormat( pHeader->formatName );
  891. if ( pEncoding )
  892. {
  893. V_strncpy( pHeader->encodingName, pEncoding, DMX_MAX_FORMAT_NAME_MAX_LENGTH );
  894. pHeader->nEncodingVersion = 0; // the first encoding version
  895. pHeader->nFormatVersion = -1; // this value is ignored for legacy formats
  896. }
  897. else
  898. {
  899. bOk = false;
  900. }
  901. }
  902. }
  903. inBuf.SetBufferType( bIsText, bHasCRLF );
  904. return bOk;
  905. }
  906. const char *CDataModel::GetEncodingFromLegacyFormat( const char *pLegacyFormatName ) const
  907. {
  908. if ( StringHasPrefixCaseSensitive( pLegacyFormatName, "binary_v" ) )
  909. return "binary";
  910. if ( StringHasPrefixCaseSensitive( pLegacyFormatName, "sfm_v" ) )
  911. return "binary";
  912. if ( StringHasPrefixCaseSensitive( pLegacyFormatName, "keyvalues2_v" ) )
  913. return "keyvalues2";
  914. if ( StringHasPrefixCaseSensitive( pLegacyFormatName, "keyvalues2_flat_v" ) )
  915. return "keyvalues2_flat";
  916. return NULL;
  917. }
  918. bool CDataModel::IsLegacyFormat( const char *pFormatName ) const
  919. {
  920. return GetEncodingFromLegacyFormat( pFormatName ) != NULL;
  921. }
  922. bool CDataModel::IsValidNonDMXFormat( const char *pEncodingName ) const
  923. {
  924. IDmSerializer *pSerializer = FindSerializer( pEncodingName );
  925. return pSerializer && !pSerializer->StoresVersionInFile();
  926. }
  927. // used to skip auto-creation of child elements during unserialization
  928. bool CDataModel::IsUnserializing()
  929. {
  930. return m_bIsUnserializing;
  931. }
  932. int CDataModel::GetCurrentFormatVersion( const char *pFormatName )
  933. {
  934. IDmFormatUpdater *pUpdater = FindFormatUpdater( pFormatName );
  935. if ( !pUpdater )
  936. return -1; // invalid version #
  937. return pUpdater->GetCurrentVersion();
  938. }
  939. //-----------------------------------------------------------------------------
  940. // Unserializes, returns the root of the unserialized tree in ppRoot
  941. //-----------------------------------------------------------------------------
  942. bool CDataModel::Unserialize( CUtlBuffer &inBuf, const char *pEncodingName, const char *pRequiredFormat, const char *pUnused,
  943. const char *pFileName, DmConflictResolution_t idConflictResolution, DmElementHandle_t &hRoot )
  944. {
  945. ClearUndo();
  946. CDisableUndoScopeGuard sg;
  947. if ( !pEncodingName )
  948. {
  949. pEncodingName = inBuf.IsText() ? "keyvalues2" : "binary";
  950. }
  951. // Find a serializer appropriate for the file format.
  952. IDmSerializer *pSerializer = FindSerializer( pEncodingName );
  953. if ( !pSerializer )
  954. {
  955. Warning( "Unerialize: DMX file encoding %s is undefined!\n", pEncodingName );
  956. return false;
  957. }
  958. g_pMemAlloc->heapchk();
  959. DmxHeader_t header;
  960. bool bStoresVersionInFile = pSerializer->StoresVersionInFile();
  961. bool bIsCurrentVersion = false; // for formats that don't store a format, files are currently always *not*
  962. // at the current version since they need to be converted to dmx
  963. const char *pDestFormatName;
  964. if ( bStoresVersionInFile )
  965. {
  966. bool bOk = ReadDMXHeader( inBuf, &header );
  967. if ( !bOk )
  968. {
  969. Warning( "Unserialize: unable to read DMX header!\n" );
  970. return false;
  971. }
  972. int nCurrentFormatVersion = GetCurrentFormatVersion( header.formatName );
  973. if ( header.nFormatVersion > nCurrentFormatVersion )
  974. {
  975. Warning( "Unserialize: file format newer than executable's!\n" );
  976. return false;
  977. }
  978. bIsCurrentVersion = !IsLegacyFormat( header.formatName ) && nCurrentFormatVersion == header.nFormatVersion;
  979. pDestFormatName = IsLegacyFormat( header.formatName ) ? GENERIC_DMX_FORMAT : header.formatName;
  980. }
  981. else
  982. {
  983. pDestFormatName = pSerializer->GetImportedFormat();
  984. Q_strncpy( header.formatName, pDestFormatName, sizeof(header.formatName) );
  985. header.nFormatVersion = pSerializer->GetImportedVersion();
  986. header.nEncodingVersion = pSerializer->GetCurrentVersion();
  987. Q_strncpy( header.encodingName, pSerializer->GetName(), sizeof(header.encodingName) );
  988. }
  989. // if we're not in dmxconvert, and we're not at the latest version, call dmxconvert and unserialize from the converted file
  990. if ( !m_bOnlyCreateUntypedElements && !bIsCurrentVersion )
  991. {
  992. char path[ MAX_PATH ];
  993. V_ExtractFilePath( pFileName, path, sizeof( path ) );
  994. if ( !V_IsAbsolutePath( path ) )
  995. {
  996. g_pFullFileSystem->GetCurrentDirectory( path, sizeof( path ) );
  997. }
  998. bool bFileFound = g_pFullFileSystem->FileExists( pFileName );
  999. if ( !bFileFound )
  1000. {
  1001. char *pTempFileName = ( char* )stackalloc( MAX_PATH ); // NOTE - stackalloc frees on function return, not scope return
  1002. V_ComposeFileName( path, "_temp_input_file_.dmx", pTempFileName, MAX_PATH );
  1003. pFileName = pTempFileName;
  1004. g_pFullFileSystem->WriteFile( pFileName, NULL, inBuf );
  1005. }
  1006. char tempFileName[ MAX_PATH ];
  1007. V_ComposeFileName( path, "_temp_conversion_file_.dmx", tempFileName, sizeof( tempFileName ) );
  1008. V_RemoveDotSlashes( tempFileName );
  1009. const char *pDestEncodingName = "binary";
  1010. char cmdline[ 2 * MAX_PATH + 256 ];
  1011. V_snprintf( cmdline, sizeof( cmdline ), "dmxconvert -allowdebug -i \"%s\" -ie %s -o \"%s\" -oe %s -of %s", pFileName, header.encodingName, tempFileName, pDestEncodingName, pDestFormatName );
  1012. IProcess *pProcess = g_pProcessUtils->StartProcess( cmdline, 0 );
  1013. if ( !pProcess )
  1014. {
  1015. Warning( "Unerialize: Unable to start dmxconvert process\n" );
  1016. return false;
  1017. }
  1018. pProcess->WaitUntilComplete();
  1019. pProcess->Release();
  1020. bool bSuccess;
  1021. {
  1022. if ( !inBuf.IsText() )
  1023. {
  1024. CUtlStreamBuffer buf( tempFileName, NULL, CUtlBuffer::READ_ONLY );
  1025. if ( !buf.IsValid() )
  1026. {
  1027. Warning( "Unerialize: Unable to open temp file \"%s\"\n", tempFileName );
  1028. return false;
  1029. }
  1030. // yes, this passes in pFileName, even though it read from tempFileName - pFileName is only used for marking debug messages and setting fileid
  1031. bSuccess = Unserialize( buf, pDestEncodingName, pDestFormatName, pDestFormatName, pFileName, idConflictResolution, hRoot );
  1032. }
  1033. else
  1034. {
  1035. CUtlBuffer buf( 0, 0, CUtlBuffer::READ_ONLY );
  1036. if ( !g_pFullFileSystem->ReadFile( tempFileName, NULL, buf ) )
  1037. {
  1038. Warning( "Unerialize: Unable to open temp file \"%s\"\n", tempFileName );
  1039. return false;
  1040. }
  1041. // yes, this passes in pFileName, even though it read from tempFileName - pFileName is only used for marking debug messages and setting fileid
  1042. bSuccess = Unserialize( buf, pDestEncodingName, pDestFormatName, pDestFormatName, pFileName, idConflictResolution, hRoot );
  1043. }
  1044. }
  1045. if ( !bFileFound )
  1046. {
  1047. g_pFullFileSystem->RemoveFile( pFileName );
  1048. }
  1049. g_pFullFileSystem->RemoveFile( tempFileName );
  1050. return bSuccess;
  1051. }
  1052. // advance the buffer the the end of the header
  1053. if ( bStoresVersionInFile )
  1054. {
  1055. if ( V_strcmp( pEncodingName, header.encodingName ) != 0 )
  1056. return false;
  1057. if ( pRequiredFormat && pRequiredFormat[0] )
  1058. {
  1059. if ( V_strcmp( pRequiredFormat, header.formatName ) != 0 )
  1060. {
  1061. Warning( "Unexpected format '%s' reading file '%s' - expected format '%s'\n", pRequiredFormat, pFileName, header.formatName );
  1062. }
  1063. }
  1064. if ( pSerializer->IsBinaryFormat() )
  1065. {
  1066. // For binary formats, we gotta keep reading until we hit the string terminator
  1067. // that occurred after the version line.
  1068. while( inBuf.GetChar() != 0 )
  1069. {
  1070. if ( !inBuf.IsValid() )
  1071. break;
  1072. }
  1073. }
  1074. }
  1075. m_bIsUnserializing = true;
  1076. DmFileId_t fileid = FindOrCreateFileId( pFileName );
  1077. // Now read the file using the appropriate format
  1078. CDmElement *pRoot;
  1079. bool bOk = pSerializer->Unserialize( inBuf, pEncodingName, header.nEncodingVersion, header.formatName, header.nFormatVersion,
  1080. fileid, idConflictResolution, &pRoot );
  1081. if ( bOk )
  1082. {
  1083. hRoot = pRoot ? pRoot->GetHandle() : DMELEMENT_HANDLE_INVALID;
  1084. SetFileModificationUTCTime( fileid, 0 );
  1085. SetFileFormat( fileid, header.formatName );
  1086. SetFileRoot( fileid, hRoot );
  1087. }
  1088. else
  1089. {
  1090. RemoveFileId( fileid );
  1091. }
  1092. m_bIsUnserializing = false;
  1093. return bOk;
  1094. }
  1095. bool CDataModel::UpdateUnserializedElements( const char *pSourceFormatName, int nSourceFormatVersion,
  1096. DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
  1097. {
  1098. if ( IsLegacyFormat( pSourceFormatName ) )
  1099. {
  1100. IDmLegacyUpdater *pLegacyUpdater = FindLegacyUpdater( pSourceFormatName );
  1101. if ( pLegacyUpdater )
  1102. {
  1103. if ( !pLegacyUpdater->Update( ppRoot ) )
  1104. return false;
  1105. }
  1106. // if there's no legacy updater found, then this is already the latest legacy format
  1107. pSourceFormatName = GENERIC_DMX_FORMAT;
  1108. }
  1109. IDmFormatUpdater *pFormatUpdater = FindFormatUpdater( pSourceFormatName );
  1110. if ( !pFormatUpdater )
  1111. return true; // Used to return false, if no format updater exists, simply means a format updater hasn't been required yet
  1112. return pFormatUpdater->Update( ppRoot, nSourceFormatVersion );
  1113. }
  1114. //-----------------------------------------------------------------------------
  1115. // file id reference methods
  1116. //-----------------------------------------------------------------------------
  1117. int CDataModel::NumFileIds()
  1118. {
  1119. return m_openFiles.GetHandleCount();
  1120. }
  1121. DmFileId_t CDataModel::GetFileId( int i )
  1122. {
  1123. Assert( i >= 0 && i < ( int )m_openFiles.GetHandleCount() );
  1124. if ( i < 0 || i >= ( int )m_openFiles.GetHandleCount() )
  1125. return DMFILEID_INVALID;
  1126. return ( DmFileId_t )m_openFiles.GetHandleFromIndex( i );
  1127. }
  1128. void CleanupFileName( char *pOutFileName, size_t nOutLen, const char *pInFileName )
  1129. {
  1130. char fixedFileName[ MAX_PATH ];
  1131. V_FixupPathName( fixedFileName, sizeof( fixedFileName ), pInFileName );
  1132. if ( !V_IsAbsolutePath( fixedFileName ) )
  1133. {
  1134. char currentDir[ MAX_PATH ];
  1135. g_pFullFileSystem->GetCurrentDirectory( currentDir, sizeof( currentDir ) );
  1136. V_MakeAbsolutePath( pOutFileName, nOutLen, fixedFileName, currentDir );
  1137. }
  1138. else
  1139. {
  1140. V_strncpy( pOutFileName, fixedFileName, nOutLen );
  1141. }
  1142. }
  1143. DmFileId_t CDataModel::FindOrCreateFileId( const char *pFileName )
  1144. {
  1145. Assert( pFileName && *pFileName );
  1146. if ( !pFileName || !*pFileName )
  1147. return DMFILEID_INVALID;
  1148. char fixedFileName[ MAX_PATH ];
  1149. CleanupFileName( fixedFileName, sizeof( fixedFileName ), pFileName );
  1150. DmFileId_t fileid = GetFileId( fixedFileName );
  1151. if ( fileid != DMFILEID_INVALID )
  1152. {
  1153. // Assert( IsFileLoaded( fileid ) );
  1154. MarkFileLoaded( fileid ); // this is sort of a hack, but I'm planning a rewrite phase on all this anyways - joe
  1155. return fileid;
  1156. }
  1157. fileid = ( DmFileId_t )m_openFiles.AddHandle();
  1158. m_openFiles.SetHandle( fileid, new FileElementSet_t( GetSymbol( fixedFileName ) ) );
  1159. return fileid;
  1160. }
  1161. void CDataModel::RemoveFileId( DmFileId_t fileid )
  1162. {
  1163. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1164. Assert( fes || fileid == DMFILEID_INVALID );
  1165. if ( !fes )
  1166. return;
  1167. if ( fes->m_bLoaded )
  1168. {
  1169. UnloadFile( fileid, true );
  1170. }
  1171. delete fes;
  1172. m_openFiles.RemoveHandle( fileid );
  1173. }
  1174. DmFileId_t CDataModel::GetFileId( const char *pFileName )
  1175. {
  1176. char fixedFileName[ MAX_PATH ];
  1177. CleanupFileName( fixedFileName, sizeof( fixedFileName ), pFileName );
  1178. CUtlSymbolLarge filenameSym = m_SymbolTable.Find( fixedFileName );
  1179. if ( filenameSym == UTL_INVAL_SYMBOL_LARGE )
  1180. return DMFILEID_INVALID;
  1181. int nFiles = m_openFiles.GetHandleCount();
  1182. for ( int i = 0; i < nFiles; ++i )
  1183. {
  1184. DmFileId_t fileid = ( DmFileId_t )m_openFiles.GetHandleFromIndex( i );
  1185. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1186. Assert( fes || !m_openFiles.IsHandleValid( fileid ) );
  1187. if ( fes && fes->m_filename == filenameSym )
  1188. return fileid;
  1189. }
  1190. return DMFILEID_INVALID;
  1191. }
  1192. const char *CDataModel::GetFileName( DmFileId_t fileid )
  1193. {
  1194. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1195. Assert( fes || fileid == DMFILEID_INVALID );
  1196. return fes ? fes->m_filename.String() : NULL;
  1197. }
  1198. void CDataModel::SetFileName( DmFileId_t fileid, const char *pFileName )
  1199. {
  1200. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1201. Assert( fes );
  1202. if ( !fes )
  1203. return;
  1204. char pFixedFileName[ MAX_PATH ];
  1205. CleanupFileName( pFixedFileName, sizeof( pFixedFileName ), pFileName );
  1206. fes->m_filename = GetSymbol( pFixedFileName );
  1207. }
  1208. const char *CDataModel::GetFileFormat( DmFileId_t fileid )
  1209. {
  1210. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1211. Assert( fes || fileid == DMFILEID_INVALID );
  1212. return fes ? fes->m_format.String() : NULL;
  1213. }
  1214. void CDataModel::SetFileFormat( DmFileId_t fileid, const char *pFormat )
  1215. {
  1216. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1217. Assert( fes );
  1218. if ( !fes )
  1219. return;
  1220. fes->m_format = GetSymbol( pFormat );
  1221. }
  1222. DmElementHandle_t CDataModel::GetFileRoot( DmFileId_t fileid )
  1223. {
  1224. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1225. Assert( fes || fileid == DMFILEID_INVALID );
  1226. return fes ? (DmElementHandle_t)fes->m_hRoot : DMELEMENT_HANDLE_INVALID;
  1227. }
  1228. void CDataModel::SetFileRoot( DmFileId_t fileid, DmElementHandle_t hRoot )
  1229. {
  1230. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1231. Assert( fes );
  1232. if ( !fes )
  1233. return;
  1234. if ( fes->m_hRoot == hRoot )
  1235. return;
  1236. fes->m_hRoot = hRoot;
  1237. }
  1238. long CDataModel::GetFileModificationUTCTime( DmFileId_t fileid )
  1239. {
  1240. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1241. Assert( fes || fileid == DMFILEID_INVALID );
  1242. return fes ? fes->m_fileModificationTime : 0;
  1243. }
  1244. void CDataModel::SetFileModificationUTCTime( DmFileId_t fileid, long fileModificationTime )
  1245. {
  1246. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1247. Assert( fes || fileid == DMFILEID_INVALID );
  1248. if ( !fes )
  1249. return;
  1250. #ifdef _DEBUG
  1251. char oldFileTime[ 256 ];
  1252. char newFileTime[ 256 ];
  1253. UTCTimeToString( oldFileTime, sizeof( oldFileTime ), fes->m_fileModificationTime );
  1254. UTCTimeToString( newFileTime, sizeof( newFileTime ), fileModificationTime );
  1255. Msg( "Setting file modification time for '%s' from %d: %s to %d: %s\n",
  1256. fes->m_filename.String(), fes->m_fileModificationTime, oldFileTime, fileModificationTime, newFileTime );
  1257. #endif // _DEBUG
  1258. fes->m_fileModificationTime = fileModificationTime;
  1259. }
  1260. long CDataModel::GetCurrentUTCTime()
  1261. {
  1262. return _time32( NULL );
  1263. }
  1264. void CDataModel::UTCTimeToString( char *pString, int maxChars, long fileTime )
  1265. {
  1266. return g_pFullFileSystem->FileTimeToString( pString, maxChars, fileTime );
  1267. }
  1268. bool CDataModel::IsFileLoaded( DmFileId_t fileid )
  1269. {
  1270. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1271. Assert( fes || fileid == DMFILEID_INVALID );
  1272. return fes ? fes->m_bLoaded : false;
  1273. }
  1274. void CDataModel::UnloadFile( DmFileId_t fileid, bool bDeleteElements )
  1275. {
  1276. ClearUndo();
  1277. CDisableUndoScopeGuard sg;
  1278. int nHandles = ( int )m_Handles.GetHandleCount();
  1279. for ( int i = 0; i < nHandles; ++i )
  1280. {
  1281. DmElementHandle_t hElement = ( DmElementHandle_t )m_Handles.GetHandleFromIndex( i );
  1282. if ( hElement == DMELEMENT_HANDLE_INVALID )
  1283. continue;
  1284. CDmElement *pElement = GetElement( hElement );
  1285. if ( !pElement || pElement->GetFileId() != fileid )
  1286. continue;
  1287. DeleteElement( hElement, bDeleteElements ? HR_ALWAYS : HR_IF_NOT_REFERENCED );
  1288. }
  1289. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1290. if ( fes )
  1291. {
  1292. fes->m_bLoaded = false;
  1293. }
  1294. }
  1295. void CDataModel::UnloadFile( DmFileId_t fileid )
  1296. {
  1297. UnloadFile( fileid, false );
  1298. }
  1299. void CDataModel::MarkFileLoaded( DmFileId_t fileid )
  1300. {
  1301. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1302. Assert( fes );
  1303. if ( !fes )
  1304. return;
  1305. fes->m_bLoaded = true;
  1306. }
  1307. int CDataModel::NumElementsInFile( DmFileId_t fileid )
  1308. {
  1309. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1310. Assert( fes );
  1311. if ( !fes )
  1312. return 0;
  1313. return fes->m_nElements;
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. // file id reference methods not in IDataModel
  1317. //-----------------------------------------------------------------------------
  1318. void CDataModel::RemoveElementFromFile( DmElementHandle_t hElement, DmFileId_t fileid )
  1319. {
  1320. if ( fileid == DMFILEID_INVALID )
  1321. return;
  1322. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1323. Assert( fes );
  1324. if ( !fes )
  1325. return;
  1326. --fes->m_nElements;
  1327. }
  1328. void CDataModel::AddElementToFile( DmElementHandle_t hElement, DmFileId_t fileid )
  1329. {
  1330. if ( fileid == DMFILEID_INVALID )
  1331. return;
  1332. FileElementSet_t *fes = m_openFiles.GetHandle( fileid );
  1333. Assert( fes );
  1334. if ( !fes )
  1335. return;
  1336. ++fes->m_nElements;
  1337. }
  1338. // search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
  1339. DmElementHandle_t CDataModel::FindOrCreateElementHandle( const DmObjectId_t &id )
  1340. {
  1341. UtlHashHandle_t h = m_elementIds.Find( id );
  1342. if ( h != m_elementIds.InvalidHandle() )
  1343. return m_elementIds[ h ];
  1344. h = m_unloadedIdElementMap.Find( ElementIdHandlePair_t( id ) ); // TODO - consider optimizing find to take just an id
  1345. if ( h != m_unloadedIdElementMap.InvalidHandle() )
  1346. return m_unloadedIdElementMap[ h ].m_ref.m_hElement;
  1347. DmElementHandle_t hElement = AcquireElementHandle();
  1348. m_unloadedIdElementMap.Insert( ElementIdHandlePair_t( id, DmElementReference_t( hElement ) ) );
  1349. MarkHandleInvalid( hElement );
  1350. return hElement;
  1351. }
  1352. // changes an element's id and associated mappings - generally during unserialization
  1353. DmElementHandle_t CDataModel::ChangeElementId( DmElementHandle_t hElement, const DmObjectId_t &oldId, const DmObjectId_t &newId )
  1354. {
  1355. UtlHashHandle_t oldHash = m_elementIds.Find( oldId );
  1356. Assert( oldHash != m_elementIds.InvalidHandle() );
  1357. if ( oldHash == m_elementIds.InvalidHandle() )
  1358. return hElement;
  1359. Assert( m_elementIds[ oldHash ] == hElement );
  1360. // can't change an element's id once it has attributes or handles linked to it
  1361. CDmElement *pElement = GetElement( hElement );
  1362. Assert( pElement );
  1363. if ( !pElement )
  1364. return DMELEMENT_HANDLE_INVALID;
  1365. Assert( !CDmeElementAccessor::GetReference( pElement )->IsWeaklyReferenced() );
  1366. UtlHashHandle_t newHash = m_elementIds.Find( newId );
  1367. if ( newHash != m_elementIds.InvalidHandle() )
  1368. return DMELEMENT_HANDLE_INVALID; // can't change an element's id to the id of an existing element
  1369. // remove old element entry
  1370. m_elementIds.Remove( oldHash );
  1371. // change the element id
  1372. CDmeElementAccessor::SetId( pElement, newId );
  1373. newHash = m_unloadedIdElementMap.Find( ElementIdHandlePair_t( newId ) );
  1374. if ( newHash == m_unloadedIdElementMap.InvalidHandle() )
  1375. {
  1376. // the newId has never been seen before - keep the element handle the same and rehash into the id->handle map
  1377. m_elementIds.Insert( hElement );
  1378. return hElement;
  1379. }
  1380. // else, the newId is being referenced by some other element
  1381. // change element to use newId and the associated handle from an element reference
  1382. DmElementReference_t &newRef = m_unloadedIdElementMap[ newHash ].m_ref;
  1383. DmElementHandle_t newHandle = newRef.m_hElement;
  1384. Assert( newHandle != hElement ); // no two ids should have the same handle
  1385. Assert( !m_Handles.IsHandleValid( newHandle ) ); // unloaded elements shouldn't have valid handles
  1386. m_Handles.SetHandle( newHandle, GetElement( hElement ) );
  1387. CDmeElementAccessor::ChangeHandle( pElement, newHandle );
  1388. CDmeElementAccessor::SetReference( pElement, newRef );
  1389. ReleaseElementHandle( hElement );
  1390. // move new element entry from the unloaded map to the loaded map
  1391. m_elementIds.Insert( newHandle );
  1392. m_unloadedIdElementMap.Remove( newHash );
  1393. return newHandle;
  1394. }
  1395. DmElementReference_t *CDataModel::FindElementReference( DmElementHandle_t hElement, DmObjectId_t **ppId /* = NULL */ )
  1396. {
  1397. if ( ppId )
  1398. {
  1399. *ppId = NULL;
  1400. }
  1401. CDmElement* pElement = GetElement( hElement );
  1402. if ( pElement )
  1403. return CDmeElementAccessor::GetReference( pElement );
  1404. for ( UtlHashHandle_t h = m_unloadedIdElementMap.GetFirstHandle(); h != m_unloadedIdElementMap.InvalidHandle(); h = m_unloadedIdElementMap.GetNextHandle( h ) )
  1405. {
  1406. DmElementReference_t &ref = m_unloadedIdElementMap[ h ].m_ref;
  1407. if ( ref.m_hElement == hElement )
  1408. {
  1409. if ( ppId )
  1410. {
  1411. *ppId = &m_unloadedIdElementMap[ h ].m_id;
  1412. }
  1413. return &ref;
  1414. }
  1415. }
  1416. return NULL;
  1417. }
  1418. void CDataModel::DontAutoDelete( DmElementHandle_t hElement )
  1419. {
  1420. // this artificially adds a strong reference to the element, so it won't ever get unref'ed to 0
  1421. // the only ways for this element to go away are explicit deletion, or file unload
  1422. OnElementReferenceAdded( hElement, HT_STRONG );
  1423. }
  1424. void CDataModel::OnElementReferenceAdded( DmElementHandle_t hElement, CDmAttribute *pAttribute )
  1425. {
  1426. Assert( pAttribute );
  1427. if ( !pAttribute )
  1428. return;
  1429. if ( hElement == DMELEMENT_HANDLE_INVALID )
  1430. return;
  1431. DmElementReference_t *pRef = FindElementReference( hElement );
  1432. if ( !pRef )
  1433. return;
  1434. // Msg( "OnElementReferenceAdded: %s 0x%x '%s' referenced by %s 0x%x '%s'\n",
  1435. // GetElementType( hElement ).String(), hElement, GetElementName( hElement ),
  1436. // pAttribute->GetOwner()->GetTypeString(), pAttribute->GetOwner()->GetHandle(), pAttribute->GetName() );
  1437. if ( pRef->m_bHasEverBeenReferenced && !pRef->IsStronglyReferenced() )
  1438. {
  1439. CDmElement *pElement = GetElement( hElement );
  1440. if ( !pElement || pElement->GetFileId() != DMFILEID_INVALID )
  1441. {
  1442. m_bDeleteOrphanedElements = true;
  1443. }
  1444. }
  1445. pRef->m_bHasEverBeenReferenced = true;
  1446. pRef->AddAttribute( pAttribute );
  1447. }
  1448. void CDataModel::OnElementReferenceAdded( DmElementHandle_t hElement, HandleType_t handleType )
  1449. {
  1450. if ( hElement == DMELEMENT_HANDLE_INVALID )
  1451. return;
  1452. DmElementReference_t *pRef = FindElementReference( hElement );
  1453. if ( !pRef )
  1454. return;
  1455. // if ( handleType != HT_WEAK )
  1456. // {
  1457. // int nCount = handleType == HT_STRONG ? pRef->m_nStrongHandleCount : pRef->m_nUndoHandleCount;
  1458. // Msg( "OnElementReferenceAdded: %s 0x%x \"%s\" referenced by %s handle\n",
  1459. // GetElementType( hElement ).String(), hElement, GetElementName( hElement ),
  1460. // handleType == HT_STRONG ? "strong" : "undo" );
  1461. // }
  1462. if ( handleType != HT_WEAK )
  1463. {
  1464. bool bUpdate = handleType == HT_STRONG ? pRef->m_bHasEverBeenReferenced && !pRef->IsStronglyReferenced() : !pRef->IsReferencedByUndo();
  1465. if ( bUpdate )
  1466. {
  1467. CDmElement *pElement = GetElement( hElement );
  1468. if ( !pElement || pElement->GetFileId() != DMFILEID_INVALID )
  1469. {
  1470. m_bDeleteOrphanedElements = true;
  1471. }
  1472. }
  1473. pRef->m_bHasEverBeenReferenced = true;
  1474. }
  1475. switch ( handleType )
  1476. {
  1477. case HT_WEAK: ++pRef->m_nWeakHandleCount; break;
  1478. case HT_STRONG: ++pRef->m_nStrongHandleCount; break;
  1479. case HT_UNDO: ++pRef->m_nUndoHandleCount; break;
  1480. }
  1481. }
  1482. void CDataModel::OnElementReferenceRemoved( DmElementHandle_t hElement, CDmAttribute *pAttribute )
  1483. {
  1484. MEM_ALLOC_CREDIT();
  1485. Assert( pAttribute );
  1486. if ( !pAttribute )
  1487. return;
  1488. if ( hElement == DMELEMENT_HANDLE_INVALID )
  1489. return;
  1490. DmElementReference_t *pRef = FindElementReference( hElement );
  1491. if ( !pRef )
  1492. return;
  1493. pRef->RemoveAttribute( pAttribute );
  1494. // Msg( "OnElementReferenceRemoved: %s 0x%x '%s' referenced by %s 0x%x '%s'\n",
  1495. // GetElementType( hElement ).String(), hElement, GetElementName( hElement ),
  1496. // pAttribute->GetOwner()->GetTypeString(), pAttribute->GetOwner()->GetHandle(), pAttribute->GetName() );
  1497. if ( !pRef->IsStronglyReferenced() )
  1498. {
  1499. CDmElement *pElement = GetElement( hElement );
  1500. if ( !pElement || pElement->GetFileId() != DMFILEID_INVALID )
  1501. {
  1502. m_bDeleteOrphanedElements = true;
  1503. }
  1504. }
  1505. }
  1506. void CDataModel::OnElementReferenceRemoved( DmElementHandle_t hElement, HandleType_t handleType )
  1507. {
  1508. MEM_ALLOC_CREDIT();
  1509. if ( hElement == DMELEMENT_HANDLE_INVALID )
  1510. return;
  1511. DmElementReference_t *pRef = FindElementReference( hElement );
  1512. if ( !pRef )
  1513. return;
  1514. switch ( handleType )
  1515. {
  1516. case HT_WEAK: --pRef->m_nWeakHandleCount; break;
  1517. case HT_STRONG: --pRef->m_nStrongHandleCount; break;
  1518. case HT_UNDO: --pRef->m_nUndoHandleCount; break;
  1519. }
  1520. // if ( handleType != HT_WEAK )
  1521. // {
  1522. // int nCount = handleType == HT_STRONG ? pRef->m_nStrongHandleCount : pRef->m_nUndoHandleCount;
  1523. // Msg( "OnElementReferenceRemoved: %s 0x%x \"%s\" referenced by %s handle\n",
  1524. // GetElementType( hElement ).String(), hElement, GetElementName( hElement ),
  1525. // handleType == HT_STRONG ? "strong" : "undo" );
  1526. // }
  1527. if ( handleType != HT_WEAK )
  1528. {
  1529. bool bUpdate = handleType == HT_STRONG ? !pRef->IsStronglyReferenced() : !pRef->IsReferencedByUndo();
  1530. if ( bUpdate )
  1531. {
  1532. CDmElement *pElement = GetElement( hElement );
  1533. if ( !pElement || pElement->GetFileId() != DMFILEID_INVALID )
  1534. {
  1535. m_bDeleteOrphanedElements = true;
  1536. }
  1537. }
  1538. }
  1539. }
  1540. void CDataModel::RemoveUnreferencedElements()
  1541. {
  1542. CDisableUndoScopeGuard sg;
  1543. if ( m_bDeleteOrphanedElements )
  1544. {
  1545. FindAndDeleteOrphanedElements();
  1546. m_bDeleteOrphanedElements = false;
  1547. }
  1548. }
  1549. void UpdateReferenceToElementsNotInUndoWithParityBitSet( CDmAttribute *pAttr, CDmElement *pChild, int nParityBit, bool bDetach )
  1550. {
  1551. if ( !pAttr || !pChild )
  1552. return;
  1553. if ( pChild->IsOnlyInUndo() || !pChild->GetParity( nParityBit ) )
  1554. return;
  1555. DmElementReference_t *pRef = CDmeElementAccessor::GetReference( pChild );
  1556. if ( bDetach )
  1557. {
  1558. // Msg( "removing " );
  1559. pRef->RemoveAttribute( pAttr );
  1560. }
  1561. else
  1562. {
  1563. Assert( !CDmeElementAccessor::GetReference( pChild )->FindAttribute( pAttr ) );
  1564. // Msg( "adding " );
  1565. pRef->AddAttribute( pAttr );
  1566. }
  1567. // CDmElement *pOwner = pAttr->GetOwner();
  1568. // Msg( "reference to %s 0x%x '%s' from %s 0x%x '%s' %s\n",
  1569. // pChild->GetTypeString(), pChild->GetHandle(), pChild->GetName(),
  1570. // pOwner->GetTypeString(), pOwner->GetHandle(), pOwner->GetName(),
  1571. // pAttr->GetName() );
  1572. }
  1573. void UpdateReferencesToElementsNotInUndoWithParityBitSet( CDmElement *pElement, int nParityBit, bool bDetach )
  1574. {
  1575. for ( CDmAttribute *pAttr = pElement->FirstAttribute(); pAttr; pAttr = pAttr->NextAttribute() )
  1576. {
  1577. DmAttributeType_t type = pAttr->GetType();
  1578. if ( type == AT_ELEMENT )
  1579. {
  1580. UpdateReferenceToElementsNotInUndoWithParityBitSet( pAttr, pAttr->GetValueElement< CDmElement >(), nParityBit, bDetach );
  1581. }
  1582. else if ( type == AT_ELEMENT_ARRAY )
  1583. {
  1584. const CDmrElementArrayConst<> elementArray( pAttr );
  1585. int nChildren = elementArray.Count();
  1586. for ( int i = 0; i < nChildren; ++i )
  1587. {
  1588. UpdateReferenceToElementsNotInUndoWithParityBitSet( pAttr, elementArray[ i ], nParityBit, bDetach );
  1589. }
  1590. }
  1591. }
  1592. }
  1593. void CDataModel::FindAndDeleteOrphanedElements()
  1594. {
  1595. // mark & sweep algorithm for elements
  1596. enum
  1597. {
  1598. BIT_STRONG = 0,
  1599. BIT_UNDO = 1,
  1600. };
  1601. // clear accessible flag from all elements
  1602. int nHandles = ( int )m_Handles.GetHandleCount();
  1603. for ( int i = 0; i < nHandles; ++i )
  1604. {
  1605. DmElementHandle_t hElement = ( DmElementHandle_t )m_Handles.GetHandleFromIndex( i );
  1606. CDmElement *pElement = m_Handles.GetHandle( hElement, false ); // walk through invalidated handles as well (such as deleted elements)
  1607. if ( !pElement )
  1608. continue;
  1609. DmFileId_t fileid = pElement->GetFileId();
  1610. if ( fileid == DMFILEID_INVALID )
  1611. continue;
  1612. pElement->SetParity( false, BIT_STRONG );
  1613. pElement->SetParity( false, BIT_UNDO );
  1614. }
  1615. // mark elements accessible from undo system
  1616. for ( int i = 0; i < nHandles; ++i )
  1617. {
  1618. DmElementHandle_t hElement = ( DmElementHandle_t )m_Handles.GetHandleFromIndex( i );
  1619. CDmElement *pElement = m_Handles.GetHandle( hElement, false ); // walk through invalidated handles as well (such as deleted elements)
  1620. if ( !pElement )
  1621. continue;
  1622. DmFileId_t fileid = pElement->GetFileId();
  1623. if ( fileid == DMFILEID_INVALID )
  1624. continue;
  1625. if ( CDmeElementAccessor::GetReference( pElement )->m_nUndoHandleCount == 0 )
  1626. continue;
  1627. pElement->SetParity( true, TD_ALL, BIT_UNDO );
  1628. }
  1629. // mark elements accessible from file roots
  1630. int nFiles = NumFileIds();
  1631. for ( int i = 0; i < nFiles; ++i )
  1632. {
  1633. DmFileId_t fileid = GetFileId( i );
  1634. if ( fileid == DMFILEID_INVALID )
  1635. continue;
  1636. DmElementHandle_t hRoot = GetFileRoot( fileid );
  1637. CDmElement *pRoot = GetElement( hRoot );
  1638. if ( !pRoot )
  1639. continue;
  1640. pRoot->SetParity( true, TD_ALL, BIT_STRONG );
  1641. }
  1642. // mark elements accessible from counted handles
  1643. for ( DmElementHandle_t hElement = FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = NextAllocatedElement( hElement ) )
  1644. {
  1645. CDmElement *pElement = GetElement( hElement );
  1646. if ( !pElement )
  1647. continue;
  1648. DmFileId_t fileid = pElement->GetFileId();
  1649. if ( fileid == DMFILEID_INVALID )
  1650. continue;
  1651. // an element is deleted when its strong handle count goes to zero, but if it never leaves zero, it shouldn't be deleted
  1652. DmElementReference_t *pRef = CDmeElementAccessor::GetReference( pElement );
  1653. if ( pRef->m_bHasEverBeenReferenced && pRef->m_nStrongHandleCount == 0 )
  1654. continue;
  1655. pElement->SetParity( true, TD_ALL, BIT_STRONG );
  1656. }
  1657. // BIT_STRONG, IsOnlyInUndo() => being moved out of undo
  1658. // BIT_STRONG, !IsOnlyInUndo() => remaining out of undo
  1659. // BIT_UNDO, IsOnlyInUndo() => remaining in undo
  1660. // BIT_UNDO, !IsOnlyInUndo() => being moved into undo
  1661. // update element references
  1662. for ( DmElementHandle_t hElement = FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = NextAllocatedElement( hElement ) )
  1663. {
  1664. CDmElement *pElement = GetElement( hElement );
  1665. if ( !pElement )
  1666. continue;
  1667. DmFileId_t fileid = pElement->GetFileId();
  1668. if ( fileid == DMFILEID_INVALID )
  1669. continue;
  1670. if ( pElement->GetParity( BIT_STRONG ) )
  1671. {
  1672. if ( pElement->IsOnlyInUndo() )
  1673. {
  1674. // this element is leaving the undo system - add references to elements that aren't in undo (and weren't before)
  1675. UpdateReferencesToElementsNotInUndoWithParityBitSet( pElement, BIT_STRONG, false );
  1676. }
  1677. }
  1678. else if ( pElement->GetParity( BIT_UNDO ) )
  1679. {
  1680. if ( !pElement->IsOnlyInUndo() )
  1681. {
  1682. // this element is entering the undo system - remove references to elements that aren't in undo (and weren't before)
  1683. UpdateReferencesToElementsNotInUndoWithParityBitSet( pElement, BIT_STRONG, true );
  1684. }
  1685. }
  1686. }
  1687. // delete elements that aren't accessible
  1688. for ( DmElementHandle_t hElement = FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = NextAllocatedElement( hElement ) )
  1689. {
  1690. CDmElement *pElement = GetElement( hElement );
  1691. if ( !pElement )
  1692. continue;
  1693. DmFileId_t fileid = pElement->GetFileId();
  1694. if ( fileid == DMFILEID_INVALID )
  1695. continue;
  1696. // Msg( "%s 0x%08x '%s': %s %s - %s\n",
  1697. // pElement->GetTypeString(), hElement, pElement->GetName(),
  1698. // pElement->GetParity( BIT_STRONG ) ? "strong" : "!strong",
  1699. // pElement->GetParity( BIT_UNDO ) ? "undo" : "!undo",
  1700. // pElement->IsOnlyInUndo() ? "only in undo" : "not only in undo" );
  1701. if ( pElement->GetParity( BIT_STRONG ) )
  1702. {
  1703. if ( pElement->IsOnlyInUndo() )
  1704. {
  1705. // Msg( "adopting %s 0x%08x '%s'\n", pElement->GetTypeString(), hElement, pElement->GetName() );
  1706. pElement->SetOnlyInUndo( false );
  1707. CDmeElementAccessor::OnAdoptedFromUndo( pElement );
  1708. }
  1709. }
  1710. else if ( pElement->GetParity( BIT_UNDO ) )
  1711. {
  1712. if ( !pElement->IsOnlyInUndo() )
  1713. {
  1714. // Msg( "orphaning %s 0x%08x '%s'\n", pElement->GetTypeString(), hElement, pElement->GetName() );
  1715. pElement->SetOnlyInUndo( true );
  1716. CDmeElementAccessor::OnOrphanedToUndo( pElement );
  1717. }
  1718. }
  1719. else
  1720. {
  1721. // Msg( "deleting %s 0x%08x '%s'\n", pElement->GetTypeString(), hElement, pElement->GetName() );
  1722. DeleteElement( hElement );
  1723. }
  1724. }
  1725. }
  1726. DmElementHandle_t CDataModel::FindElement( const DmObjectId_t &id )
  1727. {
  1728. UtlHashHandle_t h = m_elementIds.Find( id );
  1729. if ( h == m_elementIds.InvalidHandle() )
  1730. return DMELEMENT_HANDLE_INVALID;
  1731. return m_elementIds[ h ];
  1732. }
  1733. void CDataModel::GetExistingElements( CElementIdHash &hash ) const
  1734. {
  1735. DMX_PROFILE_SCOPE( GetExistingElements_Copy );
  1736. hash = m_elementIds;
  1737. }
  1738. DmAttributeReferenceIterator_t CDataModel::FirstAttributeReferencingElement( DmElementHandle_t hElement )
  1739. {
  1740. DmElementReference_t *pRef = FindElementReference( hElement );
  1741. if ( !pRef || pRef->m_attributes.m_hAttribute == DMATTRIBUTE_HANDLE_INVALID )
  1742. return DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
  1743. return ( DmAttributeReferenceIterator_t )( intp )&pRef->m_attributes;
  1744. }
  1745. DmAttributeReferenceIterator_t CDataModel::NextAttributeReferencingElement( DmAttributeReferenceIterator_t hAttrIter )
  1746. {
  1747. DmAttributeList_t *pList = ( DmAttributeList_t* )hAttrIter;
  1748. if ( !pList )
  1749. return DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
  1750. return ( DmAttributeReferenceIterator_t )( intp )pList->m_pNext;
  1751. }
  1752. CDmAttribute *CDataModel::GetAttribute( DmAttributeReferenceIterator_t hAttrIter )
  1753. {
  1754. DmAttributeList_t *pList = ( DmAttributeList_t* )hAttrIter;
  1755. if ( !pList )
  1756. return NULL;
  1757. return GetAttribute( pList->m_hAttribute );
  1758. }
  1759. //-----------------------------------------------------------------------------
  1760. // Purpose:
  1761. // Input : buf -
  1762. // Output : IDmElementInternal
  1763. //-----------------------------------------------------------------------------
  1764. CDmElement *CDataModel::Unserialize( CUtlBuffer& buf )
  1765. {
  1766. return NULL;
  1767. }
  1768. //-----------------------------------------------------------------------------
  1769. // Purpose:
  1770. // Input : *element -
  1771. // buf -
  1772. //-----------------------------------------------------------------------------
  1773. void CDataModel::Serialize( CDmElement *element, CUtlBuffer& buf )
  1774. {
  1775. }
  1776. //-----------------------------------------------------------------------------
  1777. // Sets a factory to use if the element type can't be found
  1778. //-----------------------------------------------------------------------------
  1779. void CDataModel::SetDefaultElementFactory( IDmElementFactory *pFactory )
  1780. {
  1781. if ( m_bUnableToSetDefaultFactory )
  1782. {
  1783. Assert( 0 );
  1784. return;
  1785. }
  1786. m_pDefaultFactory = pFactory ? pFactory : &s_DefaultElementFactory;
  1787. }
  1788. //-----------------------------------------------------------------------------
  1789. // Purpose:
  1790. // Input : *elementName -
  1791. // factory -
  1792. //-----------------------------------------------------------------------------
  1793. void CDataModel::AddElementFactory( CDmElementFactoryHelper *pFactoryHelper )
  1794. {
  1795. Assert( pFactoryHelper );
  1796. const char *pClassName = pFactoryHelper->GetClassname();
  1797. int idx = m_Factories.Find( pClassName );
  1798. if ( idx == m_Factories.InvalidIndex() )
  1799. {
  1800. m_Factories.Insert( pClassName, pFactoryHelper );
  1801. }
  1802. else
  1803. {
  1804. // Override the factory?
  1805. m_Factories[idx] = pFactoryHelper;
  1806. Warning( "Factory for element type '%s' already exists\n", pClassName );
  1807. }
  1808. }
  1809. CDmElementFactoryHelper *CDataModel::GetElementFactoryHelper( const char *pElementType )
  1810. {
  1811. int idx = m_Factories.Find( pElementType );
  1812. if ( idx == m_Factories.InvalidIndex() )
  1813. return NULL;
  1814. return m_Factories[ idx ];
  1815. }
  1816. bool CDataModel::HasElementFactory( const char *pElementType ) const
  1817. {
  1818. int idx = m_Factories.Find( pElementType );
  1819. return ( idx != m_Factories.InvalidIndex() );
  1820. }
  1821. int CDataModel::GetFirstFactory() const
  1822. {
  1823. return m_Factories.First();
  1824. }
  1825. int CDataModel::GetNextFactory( int index ) const
  1826. {
  1827. return m_Factories.Next( index );
  1828. }
  1829. bool CDataModel::IsValidFactory( int index ) const
  1830. {
  1831. return m_Factories.IsValidIndex( index );
  1832. }
  1833. char const *CDataModel::GetFactoryName( int index ) const
  1834. {
  1835. return m_Factories.GetElementName( index );
  1836. }
  1837. //-----------------------------------------------------------------------------
  1838. // Purpose: Creates a scene object
  1839. //-----------------------------------------------------------------------------
  1840. DmElementHandle_t CDataModel::CreateElement( CUtlSymbolLarge typeSymbol, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID )
  1841. {
  1842. return CreateElement( typeSymbol.String(), pElementName, fileid, pObjectID );
  1843. }
  1844. DmElementHandle_t CDataModel::CreateElement( const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID )
  1845. {
  1846. DMX_PROFILE_SCOPE( CDataModel_CreateElement1 );
  1847. Assert( !pObjectID || m_elementIds.Find( *pObjectID ) == m_elementIds.InvalidHandle() );
  1848. UtlHashHandle_t h = pObjectID ? m_unloadedIdElementMap.Find( ElementIdHandlePair_t( *pObjectID ) ) : m_unloadedIdElementMap.InvalidHandle();
  1849. if ( h != m_unloadedIdElementMap.InvalidHandle() )
  1850. {
  1851. CDmElement *pElement = CreateElement( m_unloadedIdElementMap[ h ].m_ref, pElementType, pElementName, fileid, pObjectID );
  1852. if ( pElement )
  1853. {
  1854. m_unloadedIdElementMap.Remove( h );
  1855. return pElement->GetHandle();
  1856. }
  1857. }
  1858. else
  1859. {
  1860. DmElementHandle_t hElement = AcquireElementHandle();
  1861. CDmElement *pElement = CreateElement( DmElementReference_t( hElement ), pElementType, pElementName, fileid, pObjectID );
  1862. if ( pElement )
  1863. return pElement->GetHandle();
  1864. ReleaseElementHandle( hElement );
  1865. }
  1866. return DMELEMENT_HANDLE_INVALID;
  1867. }
  1868. class CUndoCreateElement : public CUndoElement
  1869. {
  1870. typedef CUndoElement BaseClass;
  1871. public:
  1872. CUndoCreateElement() :
  1873. BaseClass( "CUndoCreateElement" ),
  1874. m_bKill( false ),
  1875. m_hElement()
  1876. {
  1877. }
  1878. ~CUndoCreateElement()
  1879. {
  1880. if ( m_bKill )
  1881. {
  1882. g_pDataModelImp->MarkHandleValid( m_hElement );
  1883. g_pDataModelImp->DeleteElement( m_hElement );
  1884. }
  1885. }
  1886. void SetElement( DmElementHandle_t hElement )
  1887. {
  1888. Assert( GetElement<CDmElement>( hElement ) && GetElement<CDmElement>( hElement )->GetFileId() != DMFILEID_INVALID );
  1889. m_hElement = hElement; // this has to be delayed so that the element's ref count can be incremented
  1890. }
  1891. virtual void Undo()
  1892. {
  1893. m_bKill = true;
  1894. g_pDataModelImp->MarkHandleInvalid( m_hElement );
  1895. }
  1896. virtual void Redo()
  1897. {
  1898. m_bKill = false;
  1899. g_pDataModelImp->MarkHandleValid( m_hElement );
  1900. }
  1901. private:
  1902. CDmeUndoHandle m_hElement;
  1903. bool m_bKill;
  1904. };
  1905. //-----------------------------------------------------------------------------
  1906. // CreateElement references the attribute list passed in via ref, so don't edit or purge ref's attribute list afterwards
  1907. // this is kosher because the ref either is created on the fly and has no attributes, or is being removed from m_unloadedIdElementMap
  1908. //-----------------------------------------------------------------------------
  1909. CDmElement* CDataModel::CreateElement( const DmElementReference_t &ref, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID )
  1910. {
  1911. DMX_PROFILE_SCOPE( CDataModel_CreateElement2 );
  1912. // Msg( "Creating %s 0x%x '%s' in file \"%s\" - %d elements loaded\n", pElementType, ref.m_hElement, pElementName ? pElementName : "", GetFileName( fileid ), m_elementIds.Count() );
  1913. MEM_ALLOC_CREDIT();
  1914. DmPhase_t phase = g_pDmElementFramework->GetPhase();
  1915. if ( phase != PH_EDIT )
  1916. {
  1917. Assert( 0 );
  1918. return NULL;
  1919. }
  1920. // Create a new id if we weren't given one to use
  1921. DmObjectId_t newId;
  1922. if ( !pObjectID )
  1923. {
  1924. CreateUniqueId( &newId );
  1925. pObjectID = &newId;
  1926. }
  1927. if ( !pElementName )
  1928. {
  1929. pElementName = UNNAMED_ELEMENT_NAME;
  1930. }
  1931. IDmElementFactory *pFactory = NULL;
  1932. if ( m_bOnlyCreateUntypedElements )
  1933. {
  1934. // As soon as we create something from the default factory,
  1935. // we can no longer change the default factory
  1936. m_bUnableToSetDefaultFactory = true;
  1937. pFactory = m_pDefaultFactory;
  1938. }
  1939. else
  1940. {
  1941. DMX_PROFILE_SCOPE( CreateElement_FindFactory );
  1942. int idx = m_Factories.Find( pElementType );
  1943. if ( idx == m_Factories.InvalidIndex() )
  1944. {
  1945. if ( !m_pDefaultFactory )
  1946. {
  1947. Warning( "Unable to create unknown element %s!\n", pElementType );
  1948. return NULL;
  1949. }
  1950. m_bUnableToSetDefaultFactory = true;
  1951. pFactory = m_pDefaultFactory;
  1952. }
  1953. else
  1954. {
  1955. m_bUnableToCreateOnlyUntypedElements = true;
  1956. pFactory = m_Factories[ idx ]->GetFactory();
  1957. }
  1958. }
  1959. Assert( pFactory );
  1960. // Create an undo element
  1961. CUndoCreateElement *pUndo = NULL;
  1962. {
  1963. if ( fileid != DMFILEID_INVALID && g_pDataModel->IsUndoEnabled() ) // elements not in any file don't participate in undo
  1964. {
  1965. DMX_PROFILE_SCOPE( CreateElement_Undo );
  1966. pUndo = new CUndoCreateElement();
  1967. g_pDataModel->AddUndoElement( pUndo );
  1968. }
  1969. }
  1970. CDisableUndoScopeGuard sg;
  1971. CDmElement *pElement = NULL;
  1972. {
  1973. DMX_PROFILE_SCOPE( CreateElement_pFactoryCreate );
  1974. pElement = pFactory->Create( ref.m_hElement, pElementType, pElementName, fileid, *pObjectID );
  1975. }
  1976. if ( pElement )
  1977. {
  1978. DMX_PROFILE_SCOPE( CreateElement_PerformConstruction );
  1979. ++m_nElementsAllocatedSoFar;
  1980. m_nMaxNumberOfElements = MAX( m_nMaxNumberOfElements, GetAllocatedElementCount() );
  1981. CDmeElementAccessor::SetReference( pElement, ref );
  1982. m_Handles.SetHandle( ref.m_hElement, pElement );
  1983. m_elementIds.Insert( ref.m_hElement );
  1984. CDmeElementAccessor::PerformConstruction( pElement );
  1985. CDmeElementAccessor::EnableOnChangedCallbacks( pElement );
  1986. if ( pUndo )
  1987. {
  1988. pUndo->SetElement( ref.m_hElement );
  1989. }
  1990. NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  1991. // Notify any supplied callbacks of this element's creation
  1992. pFactory->OnElementCreated( pElement );
  1993. #ifdef _ELEMENT_HISTOGRAM_
  1994. CUtlSymbolLarge typeSym = GetSymbol( pElementType );
  1995. short i = g_typeHistogram.Find( typeSym );
  1996. if ( g_typeHistogram.IsValidIndex( i ) )
  1997. {
  1998. ++g_typeHistogram[ i ];
  1999. }
  2000. else
  2001. {
  2002. g_typeHistogram.Insert( typeSym, 1 );
  2003. }
  2004. #endif _ELEMENT_HISTOGRAM_
  2005. }
  2006. return pElement;
  2007. }
  2008. void CDataModel::UpdateReferenceToElements( CDmAttribute *pAttr, CDmElement *pChild, bool bDetach )
  2009. {
  2010. if ( !pAttr || !pChild )
  2011. return;
  2012. if ( bDetach )
  2013. {
  2014. // Msg( "removing " );
  2015. OnElementReferenceRemoved( pChild->GetHandle(), pAttr );
  2016. }
  2017. else
  2018. {
  2019. Assert( !CDmeElementAccessor::GetReference( pChild )->FindAttribute( pAttr ) );
  2020. // Msg( "adding " );
  2021. OnElementReferenceAdded( pChild->GetHandle(), pAttr );
  2022. }
  2023. // CDmElement *pOwner = pAttr->GetOwner();
  2024. // Msg( "reference to %s 0x%x '%s' from %s 0x%x '%s' %s\n",
  2025. // pChild->GetTypeString(), pChild->GetHandle(), pChild->GetName(),
  2026. // pOwner->GetTypeString(), pOwner->GetHandle(), pOwner->GetName(),
  2027. // pAttr->GetName() );
  2028. }
  2029. void CDataModel::UpdateReferencesToElements( CDmElement *pElement, bool bDetach )
  2030. {
  2031. for ( CDmAttribute *pAttr = pElement->FirstAttribute(); pAttr; pAttr = pAttr->NextAttribute() )
  2032. {
  2033. DmAttributeType_t type = pAttr->GetType();
  2034. if ( type == AT_ELEMENT )
  2035. {
  2036. UpdateReferenceToElements( pAttr, pAttr->GetValueElement< CDmElement >(), bDetach );
  2037. }
  2038. else if ( type == AT_ELEMENT_ARRAY )
  2039. {
  2040. const CDmrElementArrayConst<> elementArray( pAttr );
  2041. int nChildren = elementArray.Count();
  2042. for ( int i = 0; i < nChildren; ++i )
  2043. {
  2044. UpdateReferenceToElements( pAttr, elementArray[ i ], bDetach );
  2045. }
  2046. }
  2047. }
  2048. }
  2049. class CUndoDestroyElement : public CUndoElement
  2050. {
  2051. typedef CUndoElement BaseClass;
  2052. public:
  2053. CUndoDestroyElement( DmElementHandle_t hElement ) :
  2054. BaseClass( "CUndoDestroyElement" ),
  2055. m_bKill( true ),
  2056. m_hElement( hElement )
  2057. {
  2058. CDmElement *pElement = GetElement<CDmElement>( hElement );
  2059. Assert( pElement && pElement->GetFileId() != DMFILEID_INVALID );
  2060. g_pDataModelImp->UpdateReferencesToElements( pElement, true );
  2061. pElement->SetOnlyInUndo( true );
  2062. CDmeElementAccessor::OnOrphanedToUndo( pElement );
  2063. g_pDataModelImp->MarkHandleInvalid( m_hElement );
  2064. }
  2065. ~CUndoDestroyElement()
  2066. {
  2067. if ( m_bKill )
  2068. {
  2069. g_pDataModelImp->MarkHandleValid( m_hElement );
  2070. g_pDataModelImp->DeleteElement( m_hElement );
  2071. }
  2072. }
  2073. virtual void Undo()
  2074. {
  2075. m_bKill = false;
  2076. g_pDataModelImp->MarkHandleValid( m_hElement );
  2077. CDmElement *pElement = GetElement<CDmElement>( m_hElement );
  2078. pElement->SetOnlyInUndo( false );
  2079. CDmeElementAccessor::OnAdoptedFromUndo( pElement );
  2080. g_pDataModelImp->UpdateReferencesToElements( pElement, false );
  2081. }
  2082. virtual void Redo()
  2083. {
  2084. m_bKill = true;
  2085. CDmElement *pElement = GetElement<CDmElement>( m_hElement );
  2086. g_pDataModelImp->UpdateReferencesToElements( pElement, true );
  2087. pElement->SetOnlyInUndo( true );
  2088. CDmeElementAccessor::OnOrphanedToUndo( pElement );
  2089. g_pDataModelImp->MarkHandleInvalid( m_hElement );
  2090. }
  2091. private:
  2092. CDmeUndoHandle m_hElement;
  2093. bool m_bKill;
  2094. };
  2095. //-----------------------------------------------------------------------------
  2096. // Purpose: Destroys a scene object
  2097. //-----------------------------------------------------------------------------
  2098. void CDataModel::DestroyElement( DmElementHandle_t hElement )
  2099. {
  2100. DmPhase_t phase = g_pDmElementFramework->GetPhase();
  2101. if ( phase != PH_EDIT && phase != PH_EDIT_APPLY ) // need to allow edit_apply to delete elements, so that cascading deletes can occur in one phase
  2102. {
  2103. Assert( 0 );
  2104. return;
  2105. }
  2106. if ( hElement == DMELEMENT_HANDLE_INVALID )
  2107. return;
  2108. CDmElement *pElement = m_Handles.GetHandle( hElement );
  2109. if ( pElement == NULL )
  2110. return;
  2111. // Create an undo element
  2112. if ( UndoEnabledForElement( pElement ) )
  2113. {
  2114. m_bDeleteOrphanedElements = true;
  2115. CUndoDestroyElement *pUndo = new CUndoDestroyElement( hElement );
  2116. g_pDataModel->AddUndoElement( pUndo );
  2117. NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  2118. return; // if undo is enabled, just toss this onto the undo stack, rather than actually destroying it
  2119. }
  2120. DeleteElement( hElement );
  2121. }
  2122. void CDataModel::DeleteElement( DmElementHandle_t hElement, DmHandleReleasePolicy hrp /* = HR_ALWAYS */ )
  2123. {
  2124. DmPhase_t phase = g_pDmElementFramework->GetPhase();
  2125. if ( phase != PH_EDIT && phase != PH_EDIT_APPLY)
  2126. {
  2127. Assert( 0 );
  2128. return;
  2129. }
  2130. if ( hElement == DMELEMENT_HANDLE_INVALID )
  2131. return;
  2132. CDmElement *pElement = m_Handles.GetHandle( hElement );
  2133. if ( pElement == NULL )
  2134. return;
  2135. // In order for DestroyElement to work, then, we need to cache off the element type
  2136. // because that's stored in an attribute
  2137. const char *pElementType = pElement->GetTypeString();
  2138. Assert( pElementType );
  2139. // Msg( "Deleting %s element 0x%x \'%s\' in file \"%s\" - %d elements loaded\n", pElementType, hElement, pElement->GetName(), GetFileName( pElement->GetFileId() ), m_elementIds.Count() );
  2140. UtlHashHandle_t h = m_elementIds.Find( pElement->GetId() );
  2141. Assert( h != m_elementIds.InvalidHandle() );
  2142. if ( h != m_elementIds.InvalidHandle() )
  2143. {
  2144. m_elementIds.Remove( h );
  2145. }
  2146. DmElementReference_t *pRef = CDmeElementAccessor::GetReference( pElement );
  2147. bool bReleaseHandle = hrp == HR_ALWAYS || ( hrp == HR_IF_NOT_REFERENCED && !pRef->IsWeaklyReferenced() );
  2148. if ( !bReleaseHandle )
  2149. {
  2150. m_unloadedIdElementMap.Insert( ElementIdHandlePair_t( GetElementId( hElement ), *pRef ) );
  2151. }
  2152. IDmElementFactory *pFactory = NULL;
  2153. if ( m_bOnlyCreateUntypedElements )
  2154. {
  2155. pFactory = m_pDefaultFactory;
  2156. }
  2157. else
  2158. {
  2159. int idx = m_Factories.Find( pElementType );
  2160. pFactory = idx == m_Factories.InvalidIndex() ? m_pDefaultFactory : m_Factories[ idx ]->GetFactory();
  2161. }
  2162. CDmeElementAccessor::PerformDestruction( pElement );
  2163. // NOTE: Attribute destruction has to happen before the containing object is destroyed
  2164. // because the inline optimization will crash otherwise, and after PerformDestruction
  2165. // or else PerformDestruction will crash
  2166. CDmeElementAccessor::Purge( pElement );
  2167. pFactory->Destroy( hElement );
  2168. if ( bReleaseHandle )
  2169. {
  2170. ReleaseElementHandle( hElement );
  2171. }
  2172. else
  2173. {
  2174. MarkHandleInvalid( hElement );
  2175. }
  2176. }
  2177. //-----------------------------------------------------------------------------
  2178. // handle-related methods
  2179. //-----------------------------------------------------------------------------
  2180. DmElementHandle_t CDataModel::AcquireElementHandle()
  2181. {
  2182. NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  2183. return ( DmElementHandle_t )m_Handles.AddHandle();
  2184. }
  2185. void CDataModel::ReleaseElementHandle( DmElementHandle_t hElement )
  2186. {
  2187. m_Handles.RemoveHandle( hElement );
  2188. NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  2189. }
  2190. void CDataModel::MarkHandleInvalid( DmElementHandle_t hElement )
  2191. {
  2192. m_Handles.MarkHandleInvalid( hElement );
  2193. NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  2194. }
  2195. void CDataModel::MarkHandleValid( DmElementHandle_t hElement )
  2196. {
  2197. m_Handles.MarkHandleValid( hElement );
  2198. NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  2199. }
  2200. void CDataModel::GetInvalidHandles( CUtlVector< DmElementHandle_t > &handles )
  2201. {
  2202. unsigned int nHandles = m_Handles.GetHandleCount();
  2203. for ( unsigned int i = 0; i < nHandles; ++i )
  2204. {
  2205. DmElementHandle_t h = ( DmElementHandle_t )m_Handles.GetHandleFromIndex( i );
  2206. if ( !m_Handles.IsHandleValid( h ) )
  2207. {
  2208. handles.AddToTail( h );
  2209. }
  2210. }
  2211. }
  2212. void CDataModel::MarkHandlesValid( CUtlVector< DmElementHandle_t > &handles )
  2213. {
  2214. int nHandles = handles.Count();
  2215. for ( int i = 0; i < nHandles; ++i )
  2216. {
  2217. m_Handles.MarkHandleValid( handles[ i ] );
  2218. }
  2219. }
  2220. void CDataModel::MarkHandlesInvalid( CUtlVector< DmElementHandle_t > &handles )
  2221. {
  2222. int nHandles = handles.Count();
  2223. for ( int i = 0; i < nHandles; ++i )
  2224. {
  2225. m_Handles.MarkHandleInvalid( handles[ i ] );
  2226. }
  2227. }
  2228. CDmElement *CDataModel::GetElement( DmElementHandle_t hElement ) const
  2229. {
  2230. return ( hElement != DMELEMENT_HANDLE_INVALID ) ? m_Handles.GetHandle( hElement ) : NULL;
  2231. }
  2232. CUtlSymbolLarge CDataModel::GetElementType( DmElementHandle_t hElement ) const
  2233. {
  2234. CDmElement *pElement = ( hElement != DMELEMENT_HANDLE_INVALID ) ? m_Handles.GetHandle( hElement ) : NULL;
  2235. if ( pElement == NULL )
  2236. return UTL_INVAL_SYMBOL_LARGE;
  2237. return pElement->GetType();
  2238. }
  2239. const char* CDataModel::GetElementName( DmElementHandle_t hElement ) const
  2240. {
  2241. CDmElement *pElement = ( hElement != DMELEMENT_HANDLE_INVALID ) ? m_Handles.GetHandle( hElement ) : NULL;
  2242. if ( pElement == NULL )
  2243. return "";
  2244. return pElement->GetName();
  2245. }
  2246. const DmObjectId_t& CDataModel::GetElementId( DmElementHandle_t hElement ) const
  2247. {
  2248. CDmElement *pElement = ( hElement != DMELEMENT_HANDLE_INVALID ) ? m_Handles.GetHandle( hElement ) : NULL;
  2249. if ( pElement == NULL )
  2250. {
  2251. static DmObjectId_t s_id;
  2252. InvalidateUniqueId( &s_id );
  2253. return s_id;
  2254. }
  2255. return pElement->GetId();
  2256. }
  2257. //-----------------------------------------------------------------------------
  2258. // Attribute types
  2259. //-----------------------------------------------------------------------------
  2260. const char *CDataModel::GetAttributeNameForType( DmAttributeType_t attType ) const
  2261. {
  2262. return AttributeTypeName( attType );
  2263. }
  2264. DmAttributeType_t CDataModel::GetAttributeTypeForName( const char *name ) const
  2265. {
  2266. return AttributeType( name );
  2267. }
  2268. //-----------------------------------------------------------------------------
  2269. //
  2270. // Methods related to notification callbacks
  2271. //
  2272. //-----------------------------------------------------------------------------
  2273. bool CDataModel::InstallNotificationCallback( IDmNotify *pNotify )
  2274. {
  2275. return m_UndoMgr.InstallNotificationCallback( pNotify );
  2276. }
  2277. void CDataModel::RemoveNotificationCallback( IDmNotify *pNotify )
  2278. {
  2279. m_UndoMgr.RemoveNotificationCallback( pNotify );
  2280. }
  2281. bool CDataModel::IsSuppressingNotify( ) const
  2282. {
  2283. return GetUndoMgr()->IsSuppressingNotify( );
  2284. }
  2285. void CDataModel::SetSuppressingNotify( bool bSuppress )
  2286. {
  2287. GetUndoMgr()->SetSuppressingNotify( bSuppress );
  2288. }
  2289. void CDataModel::PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags )
  2290. {
  2291. GetUndoMgr()->PushNotificationScope( pReason, nNotifySource, nNotifyFlags );
  2292. }
  2293. void CDataModel::PopNotificationScope( bool bAbort )
  2294. {
  2295. GetUndoMgr()->PopNotificationScope( bAbort );
  2296. }
  2297. //-----------------------------------------------------------------------------
  2298. //-----------------------------------------------------------------------------
  2299. // Undo/Redo support
  2300. //-----------------------------------------------------------------------------
  2301. void CDataModel::SetUndoEnabled( bool enable )
  2302. {
  2303. if ( enable )
  2304. {
  2305. GetUndoMgr()->EnableUndo();
  2306. }
  2307. else
  2308. {
  2309. GetUndoMgr()->DisableUndo();
  2310. }
  2311. }
  2312. bool CDataModel::IsUndoEnabled() const
  2313. {
  2314. return GetUndoMgr()->IsEnabled();
  2315. }
  2316. bool CDataModel::UndoEnabledForElement( const CDmElement *pElement ) const
  2317. {
  2318. // elements not in any file don't participate in undo
  2319. Assert( pElement );
  2320. return IsUndoEnabled() && pElement && pElement->GetFileId() != DMFILEID_INVALID;
  2321. }
  2322. bool CDataModel::IsDirty() const
  2323. {
  2324. return GetUndoMgr()->HasUndoData();
  2325. }
  2326. bool CDataModel::CanUndo() const
  2327. {
  2328. return GetUndoMgr()->HasUndoData();
  2329. }
  2330. bool CDataModel::CanRedo() const
  2331. {
  2332. return GetUndoMgr()->HasRedoData();
  2333. }
  2334. void CDataModel::StartUndo( const char *undodesc, const char *redodesc, int nChainingID /* = 0 */ )
  2335. {
  2336. GetUndoMgr()->PushUndo( undodesc, redodesc, nChainingID );
  2337. }
  2338. void CDataModel::FinishUndo()
  2339. {
  2340. GetUndoMgr()->PushRedo();
  2341. }
  2342. void CDataModel::AbortUndoableOperation()
  2343. {
  2344. GetUndoMgr()->AbortUndoableOperation();
  2345. }
  2346. void CDataModel::ClearRedo()
  2347. {
  2348. if ( GetUndoMgr()->HasRedoData() )
  2349. {
  2350. GetUndoMgr()->WipeRedo();
  2351. }
  2352. }
  2353. const char *CDataModel::GetUndoDesc()
  2354. {
  2355. return GetUndoMgr()->UndoDesc();
  2356. }
  2357. const char *CDataModel::GetRedoDesc()
  2358. {
  2359. return GetUndoMgr()->RedoDesc();
  2360. }
  2361. // From the UI, perform the Undo operation
  2362. void CDataModel::Undo()
  2363. {
  2364. GetUndoMgr()->Undo();
  2365. }
  2366. void CDataModel::Redo()
  2367. {
  2368. GetUndoMgr()->Redo();
  2369. }
  2370. // if true, undo records spew as they are added
  2371. void CDataModel::TraceUndo( bool state )
  2372. {
  2373. GetUndoMgr()->TraceUndo( state );
  2374. }
  2375. void CDataModel::AddUndoElement( IUndoElement *pElement )
  2376. {
  2377. GetUndoMgr()->AddUndoElement( pElement );
  2378. }
  2379. void CDataModel::ClearUndo()
  2380. {
  2381. GetUndoMgr()->WipeUndo();
  2382. GetUndoMgr()->WipeRedo();
  2383. m_bDeleteOrphanedElements = true; // next time we delete unreferenced elements, delete orphaned subtrees as well
  2384. }
  2385. CUtlSymbolLarge CDataModel::GetUndoDescInternal( const char *context )
  2386. {
  2387. return GetUndoMgr()->GetUndoDescInternal( context );
  2388. }
  2389. CUtlSymbolLarge CDataModel::GetRedoDescInternal( const char *context )
  2390. {
  2391. return GetUndoMgr()->GetRedoDescInternal( context );
  2392. }
  2393. void CDataModel::GetUndoInfo( CUtlVector< UndoInfo_t >& list )
  2394. {
  2395. GetUndoMgr()->GetUndoInfo( list );
  2396. }
  2397. const char *CDataModel::GetUndoString( CUtlSymbolLarge sym )
  2398. {
  2399. return sym.String();
  2400. }
  2401. //-----------------------------------------------------------------------------
  2402. //
  2403. // Methods related to attribute handles
  2404. //
  2405. //-----------------------------------------------------------------------------
  2406. DmAttributeHandle_t CDataModel::AcquireAttributeHandle( CDmAttribute *pAttribute )
  2407. {
  2408. DmAttributeHandle_t hAttribute = (DmAttributeHandle_t)m_AttributeHandles.AddHandle();
  2409. m_AttributeHandles.SetHandle( hAttribute, pAttribute );
  2410. return hAttribute;
  2411. }
  2412. void CDataModel::ReleaseAttributeHandle( DmAttributeHandle_t hAttribute )
  2413. {
  2414. if ( hAttribute != DMATTRIBUTE_HANDLE_INVALID )
  2415. {
  2416. m_AttributeHandles.RemoveHandle( hAttribute );
  2417. }
  2418. }
  2419. CDmAttribute *CDataModel::GetAttribute( DmAttributeHandle_t h )
  2420. {
  2421. return m_AttributeHandles.GetHandle( h );
  2422. }
  2423. bool CDataModel::IsAttributeHandleValid( DmAttributeHandle_t h ) const
  2424. {
  2425. return m_AttributeHandles.IsHandleValid( h );
  2426. }
  2427. //-----------------------------------------------------------------------------
  2428. //
  2429. // Methods related to clipboard contexts
  2430. //
  2431. //-----------------------------------------------------------------------------
  2432. void CDataModel::EmptyClipboard()
  2433. {
  2434. GetClipboardMgr()->EmptyClipboard( true );
  2435. }
  2436. void CDataModel::SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction /*= 0*/ )
  2437. {
  2438. GetClipboardMgr()->SetClipboardData( data, pfnOptionalCleanuFunction );
  2439. }
  2440. void CDataModel::AddToClipboardData( KeyValues *add )
  2441. {
  2442. GetClipboardMgr()->AddToClipboardData( add );
  2443. }
  2444. void CDataModel::GetClipboardData( CUtlVector< KeyValues * >& data )
  2445. {
  2446. GetClipboardMgr()->GetClipboardData( data );
  2447. }
  2448. bool CDataModel::HasClipboardData() const
  2449. {
  2450. return GetClipboardMgr()->HasClipboardData();
  2451. }
  2452. // Commits symbols in symbol table
  2453. void CDataModel::CommitSymbols()
  2454. {
  2455. m_SymbolTable.Commit();
  2456. }
  2457. void CDataModel::AddOnElementCreatedCallback( const char *pElementType, IDmeElementCreated *callback )
  2458. {
  2459. IDmElementFactory *pFactory = NULL;
  2460. int idx = m_Factories.Find( pElementType );
  2461. if ( idx == m_Factories.InvalidIndex() ) return;
  2462. pFactory = m_Factories[ idx ]->GetFactory();
  2463. Assert( pFactory );
  2464. pFactory->AddOnElementCreatedCallback( callback );
  2465. }
  2466. void CDataModel::RemoveOnElementCreatedCallback( const char *pElementType, IDmeElementCreated *callback )
  2467. {
  2468. IDmElementFactory *pFactory = NULL;
  2469. int idx = m_Factories.Find( pElementType );
  2470. if ( idx == m_Factories.InvalidIndex() ) return;
  2471. pFactory = m_Factories[ idx ]->GetFactory();
  2472. Assert( pFactory );
  2473. pFactory->RemoveOnElementCreatedCallback( callback );
  2474. }