Team Fortress 2 Source Code as on 22/4/2020
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.

2464 lines
69 KiB

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