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.

489 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "dmxloader/dmxelement.h"
  7. #include "dmxloader/dmxattribute.h"
  8. #include "tier1/utlbuffer.h"
  9. #include "mathlib/ssemath.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. //-----------------------------------------------------------------------------
  13. // globals
  14. //-----------------------------------------------------------------------------
  15. CUtlSymbolTableMT CDmxElement::s_TypeSymbols;
  16. //-----------------------------------------------------------------------------
  17. // Creates a dmx element
  18. //-----------------------------------------------------------------------------
  19. CDmxElement* CreateDmxElement( const char *pType )
  20. {
  21. return new CDmxElement( pType );
  22. }
  23. //-----------------------------------------------------------------------------
  24. // Constructor, destructor
  25. //-----------------------------------------------------------------------------
  26. CDmxElement::CDmxElement( const char *pType )
  27. {
  28. m_Type = s_TypeSymbols.AddString( pType );
  29. m_nLockCount = 0;
  30. m_bResortNeeded = false;
  31. m_bIsMarkedForDeletion = false;
  32. CreateUniqueId( &m_Id );
  33. }
  34. CDmxElement::~CDmxElement()
  35. {
  36. CDmxElementModifyScope modify( this );
  37. RemoveAllAttributes();
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Utility method for getting at the type
  41. //-----------------------------------------------------------------------------
  42. CUtlSymbol CDmxElement::GetType() const
  43. {
  44. return m_Type;
  45. }
  46. const char* CDmxElement::GetTypeString() const
  47. {
  48. return s_TypeSymbols.String( m_Type );
  49. }
  50. const char* CDmxElement::GetName() const
  51. {
  52. return GetValue< CUtlString >( "name" );
  53. }
  54. const DmObjectId_t &CDmxElement::GetId() const
  55. {
  56. return m_Id;
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Sets the object id
  60. //-----------------------------------------------------------------------------
  61. void CDmxElement::SetId( const DmObjectId_t &id )
  62. {
  63. CopyUniqueId( id, &m_Id );
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Sorts the vector when a change has occurred
  67. //-----------------------------------------------------------------------------
  68. void CDmxElement::Resort( ) const
  69. {
  70. if ( m_bResortNeeded )
  71. {
  72. AttributeList_t *pAttributes = const_cast< AttributeList_t *>( &m_Attributes );
  73. pAttributes->RedoSort();
  74. m_bResortNeeded = false;
  75. // NOTE: This checks for duplicate attribute names
  76. int nCount = m_Attributes.Count();
  77. for ( int i = nCount; --i >= 1; )
  78. {
  79. if ( m_Attributes[i]->GetNameSymbol() == m_Attributes[i-1]->GetNameSymbol() )
  80. {
  81. Warning( "Duplicate attribute name %s encountered!\n", m_Attributes[i]->GetName() );
  82. pAttributes->Remove(i);
  83. Assert( 0 );
  84. }
  85. }
  86. }
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Enables modification of the DmxElement
  90. //-----------------------------------------------------------------------------
  91. void CDmxElement::LockForChanges( bool bLock )
  92. {
  93. if ( bLock )
  94. {
  95. ++m_nLockCount;
  96. }
  97. else
  98. {
  99. if ( --m_nLockCount == 0 )
  100. {
  101. Resort();
  102. }
  103. Assert( m_nLockCount >= 0 );
  104. }
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Adds, removes attributes
  108. //-----------------------------------------------------------------------------
  109. CDmxAttribute *CDmxElement::AddAttribute( const char *pAttributeName )
  110. {
  111. int nIndex = FindAttribute( pAttributeName );
  112. if ( nIndex >= 0 )
  113. return m_Attributes[nIndex];
  114. CDmxElementModifyScope modify( this );
  115. m_bResortNeeded = true;
  116. CDmxAttribute *pAttribute = new CDmxAttribute( pAttributeName );
  117. m_Attributes.InsertNoSort( pAttribute );
  118. return pAttribute;
  119. }
  120. void CDmxElement::RemoveAttribute( const char *pAttributeName )
  121. {
  122. CDmxElementModifyScope modify( this );
  123. int idx = FindAttribute( pAttributeName );
  124. if ( idx >= 0 )
  125. {
  126. delete m_Attributes[idx];
  127. m_Attributes.Remove( idx );
  128. }
  129. }
  130. void CDmxElement::RemoveAttributeByPtr( CDmxAttribute *pAttribute )
  131. {
  132. CDmxElementModifyScope modify( this );
  133. int nCount = m_Attributes.Count();
  134. for ( int i = 0; i < nCount; ++i )
  135. {
  136. if ( m_Attributes[i] != pAttribute )
  137. continue;
  138. delete pAttribute;
  139. m_Attributes.Remove( i );
  140. break;
  141. }
  142. }
  143. void CDmxElement::RemoveAllAttributes()
  144. {
  145. int nCount = m_Attributes.Count();
  146. for ( int i = 0; i < nCount; ++i )
  147. {
  148. delete m_Attributes[i];
  149. }
  150. m_Attributes.RemoveAll();
  151. m_bResortNeeded = false;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Rename an attribute
  155. //-----------------------------------------------------------------------------
  156. void CDmxElement::RenameAttribute( const char *pAttributeName, const char *pNewName )
  157. {
  158. CDmxElementModifyScope modify( this );
  159. // No change...
  160. if ( !Q_stricmp( pAttributeName, pNewName ) )
  161. return;
  162. int idx = FindAttribute( pAttributeName );
  163. if ( idx < 0 )
  164. return;
  165. if ( HasAttribute( pNewName ) )
  166. {
  167. Warning( "Tried to rename from \"%s\" to \"%s\", but \"%s\" already exists!\n",
  168. pAttributeName, pNewName, pNewName );
  169. return;
  170. }
  171. m_bResortNeeded = true;
  172. m_Attributes[ idx ]->SetName( pNewName );
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Find an attribute by name-based lookup
  176. //-----------------------------------------------------------------------------
  177. int CDmxElement::FindAttribute( const char *pAttributeName ) const
  178. {
  179. // FIXME: The cost here is O(log M) + O(log N)
  180. // where log N is the binary search for the symbol match
  181. // and log M is the binary search for the attribute name->symbol
  182. // We can eliminate log M by using a hash table in the symbol lookup
  183. Resort();
  184. CDmxAttribute search( pAttributeName );
  185. return m_Attributes.Find( &search );
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Find an attribute by name-based lookup
  189. //-----------------------------------------------------------------------------
  190. int CDmxElement::FindAttribute( CUtlSymbol attributeName ) const
  191. {
  192. Resort();
  193. CDmxAttribute search( attributeName );
  194. return m_Attributes.Find( &search );
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Attribute finding
  198. //-----------------------------------------------------------------------------
  199. bool CDmxElement::HasAttribute( const char *pAttributeName ) const
  200. {
  201. int idx = FindAttribute( pAttributeName );
  202. return ( idx >= 0 );
  203. }
  204. CDmxAttribute *CDmxElement::GetAttribute( const char *pAttributeName )
  205. {
  206. int idx = FindAttribute( pAttributeName );
  207. if ( idx >= 0 )
  208. return m_Attributes[ idx ];
  209. return NULL;
  210. }
  211. const CDmxAttribute *CDmxElement::GetAttribute( const char *pAttributeName ) const
  212. {
  213. int idx = FindAttribute( pAttributeName );
  214. if ( idx >= 0 )
  215. return m_Attributes[ idx ];
  216. return NULL;
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Attribute interation
  220. //-----------------------------------------------------------------------------
  221. int CDmxElement::AttributeCount() const
  222. {
  223. return m_Attributes.Count();
  224. }
  225. CDmxAttribute *CDmxElement::GetAttribute( int nIndex )
  226. {
  227. return m_Attributes[ nIndex ];
  228. }
  229. const CDmxAttribute *CDmxElement::GetAttribute( int nIndex ) const
  230. {
  231. return m_Attributes[ nIndex ];
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Removes all elements recursively
  235. //-----------------------------------------------------------------------------
  236. void CDmxElement::AddElementsToDelete( CUtlVector< CDmxElement * >& elementsToDelete )
  237. {
  238. if ( m_bIsMarkedForDeletion )
  239. return;
  240. m_bIsMarkedForDeletion = true;
  241. elementsToDelete.AddToTail( this );
  242. int nCount = AttributeCount();
  243. for ( int i = 0; i < nCount; ++i )
  244. {
  245. CDmxAttribute *pAttribute = GetAttribute(i);
  246. if ( pAttribute->GetType() == AT_ELEMENT )
  247. {
  248. CDmxElement *pElement = pAttribute->GetValue< CDmxElement* >();
  249. if ( pElement )
  250. {
  251. pElement->AddElementsToDelete( elementsToDelete );
  252. }
  253. continue;
  254. }
  255. if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
  256. {
  257. const CUtlVector< CDmxElement * > &elements = pAttribute->GetArray< CDmxElement* >();
  258. int nElementCount = elements.Count();
  259. for ( int j = 0; j < nElementCount; ++j )
  260. {
  261. if ( elements[j] )
  262. {
  263. elements[j]->AddElementsToDelete( elementsToDelete );
  264. }
  265. }
  266. continue;
  267. }
  268. }
  269. }
  270. //-----------------------------------------------------------------------------
  271. // Removes all elements recursively
  272. //-----------------------------------------------------------------------------
  273. void CDmxElement::RemoveAllElementsRecursive()
  274. {
  275. CUtlVector< CDmxElement * > elementsToDelete;
  276. AddElementsToDelete( elementsToDelete );
  277. int nCount = elementsToDelete.Count();
  278. for ( int i = 0; i < nCount; ++i )
  279. {
  280. delete elementsToDelete[i];
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Method to unpack data into a structure
  285. //-----------------------------------------------------------------------------
  286. void CDmxElement::UnpackIntoStructure( void *pData, size_t DestSizeInBytes, const DmxElementUnpackStructure_t *pUnpack ) const
  287. {
  288. void *pDataEnd = ( char * )pData + DestSizeInBytes;
  289. for ( ; pUnpack->m_AttributeType != AT_UNKNOWN; ++pUnpack )
  290. {
  291. char *pDest = (char*)pData + pUnpack->m_nOffset;
  292. // NOTE: This does not work with array data at the moment
  293. if ( IsArrayType( pUnpack->m_AttributeType ) )
  294. {
  295. AssertMsg( 0, ( "CDmxElement::UnpackIntoStructure: Array attribute types not currently supported!\n" ) );
  296. continue;
  297. }
  298. if ( pUnpack->m_AttributeType == AT_VOID )
  299. {
  300. AssertMsg( 0, ( "CDmxElement::UnpackIntoStructure: Binary blob attribute types not currently supported!\n" ) );
  301. continue;
  302. }
  303. CDmxAttribute temp( NULL );
  304. const CDmxAttribute *pAttribute = GetAttribute( pUnpack->m_pAttributeName );
  305. if ( !pAttribute )
  306. {
  307. if ( !pUnpack->m_pDefaultString )
  308. continue;
  309. // Convert the default string into the target
  310. int nLen = Q_strlen( pUnpack->m_pDefaultString );
  311. if ( nLen > 0 )
  312. {
  313. CUtlBuffer buf( pUnpack->m_pDefaultString, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
  314. temp.Unserialize( pUnpack->m_AttributeType, buf );
  315. }
  316. else
  317. {
  318. CUtlBuffer buf;
  319. temp.Unserialize( pUnpack->m_AttributeType, buf );
  320. }
  321. pAttribute = &temp;
  322. }
  323. if ( pUnpack->m_AttributeType != pAttribute->GetType() )
  324. {
  325. Warning( "CDmxElement::UnpackIntoStructure: Mismatched attribute type in attribute \"%s\"!\n", pUnpack->m_pAttributeName );
  326. continue;
  327. }
  328. if ( pAttribute->GetType() == AT_STRING )
  329. {
  330. if ( pDest + pUnpack->m_nSize > pDataEnd )
  331. {
  332. Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure string buffer overrun!\n" );
  333. continue;
  334. }
  335. // Strings get special treatment: they are stored as in-line arrays of chars
  336. Q_strncpy( pDest, pAttribute->GetValueString(), pUnpack->m_nSize );
  337. continue;
  338. }
  339. // special case - if data type is float, but dest size == 16, we are unpacking into simd by
  340. // replication
  341. if ( ( pAttribute->GetType() == AT_FLOAT ) && ( pUnpack->m_nSize == sizeof( fltx4 ) ) )
  342. {
  343. if ( pDest + 4 * sizeof( float ) > pDataEnd )
  344. {
  345. Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure float buffer overrun!\n" );
  346. continue;
  347. }
  348. memcpy( pDest + 0 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
  349. memcpy( pDest + 1 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
  350. memcpy( pDest + 2 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
  351. memcpy( pDest + 3 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) );
  352. }
  353. else
  354. {
  355. if ( pDest + pUnpack->m_nSize > pDataEnd )
  356. {
  357. Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure memcpy buffer overrun!\n" );
  358. continue;
  359. }
  360. AssertMsg( pUnpack->m_nSize == CDmxAttribute::AttributeDataSize( pAttribute->GetType() ),
  361. "CDmxElement::UnpackIntoStructure: Incorrect size to unpack data into in attribute \"%s\"!\n", pUnpack->m_pAttributeName );
  362. memcpy( pDest, pAttribute->m_pData, pUnpack->m_nSize );
  363. }
  364. }
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Creates attributes based on the unpack structure
  368. //-----------------------------------------------------------------------------
  369. void CDmxElement::AddAttributesFromStructure_Internal( const void *pData, size_t byteCount, const DmxElementUnpackStructure_t *pUnpack )
  370. {
  371. for ( ; pUnpack->m_AttributeType != AT_UNKNOWN; ++pUnpack )
  372. {
  373. const char *pSrc = (const char*)pData + pUnpack->m_nOffset;
  374. // NOTE: This does not work with array data at the moment
  375. if ( IsArrayType( pUnpack->m_AttributeType ) )
  376. {
  377. AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Array attribute types not currently supported!\n" );
  378. continue;
  379. }
  380. if ( pUnpack->m_AttributeType == AT_VOID )
  381. {
  382. AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Binary blob attribute types not currently supported!\n" );
  383. continue;
  384. }
  385. if ( HasAttribute( pUnpack->m_pAttributeName ) )
  386. {
  387. AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Attribute %s already exists!\n", pUnpack->m_pAttributeName );
  388. continue;
  389. }
  390. {
  391. if ( (size_t)(pUnpack->m_nOffset + pUnpack->m_nSize) > byteCount )
  392. {
  393. Msg( "Buffer underread! Mismatched type/type-descriptor.\n" );
  394. }
  395. CDmxElementModifyScope modify( this );
  396. CDmxAttribute *pAttribute = AddAttribute( pUnpack->m_pAttributeName );
  397. if ( pUnpack->m_AttributeType == AT_STRING )
  398. {
  399. pAttribute->SetValue( pSrc );
  400. }
  401. else
  402. {
  403. int nSize = pUnpack->m_nSize;
  404. // handle float attrs stored as replicated fltx4's
  405. if ( ( pUnpack->m_AttributeType == AT_FLOAT ) && ( nSize == sizeof( fltx4 ) ) )
  406. {
  407. nSize = sizeof( float );
  408. }
  409. AssertMsg( nSize == CDmxAttribute::AttributeDataSize( pUnpack->m_AttributeType ),
  410. "CDmxElement::UnpackIntoStructure: Incorrect size to unpack data into in attribute \"%s\"!\n", pUnpack->m_pAttributeName );
  411. pAttribute->SetValue( pUnpack->m_AttributeType, pSrc, nSize );
  412. }
  413. }
  414. }
  415. }