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

388 lines
15 KiB

  1. //===== Copyright � 1996-2007, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: see CParticleSnapshot declaration (in particles.h)
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #include "particles/particles.h"
  8. #include "filesystem.h"
  9. #include "tier2/tier2.h"
  10. #include "tier2/fileutils.h"
  11. #include "tier1/utlbuffer.h"
  12. #include "tier1/UtlStringMap.h"
  13. #include "tier1/strtools.h"
  14. #include "dmxloader/dmxloader.h"
  15. #include "dmxloader/utlsoacontainer_serialization.h"
  16. #include "tier1/lzmaDecoder.h"
  17. #include "tier0/vprof.h"
  18. #include "particles_internal.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. // This macro is used to process the attribute mapping parameters, for both vararg versions of Init():
  22. #define ATTRIBUTE_MAPPING_LOOP( _lastarg_ ) \
  23. va_list args; \
  24. va_start( args, _lastarg_ ); \
  25. for(;;) \
  26. { \
  27. int nFieldNumber = va_arg( args, int ); \
  28. if ( nFieldNumber == -1 ) \
  29. break; \
  30. /* Associate nParticleAttribute with nFieldNumber */ \
  31. int nParticleAttribute = va_arg( args, int );
  32. //-----------------------------------------------------------------------------
  33. // Purge, clear the container back to its initial state
  34. //-----------------------------------------------------------------------------
  35. void CParticleSnapshot::Purge( void )
  36. {
  37. m_Container.Purge();
  38. m_pContainer = NULL;
  39. for ( int i = 0; i < ARRAYSIZE( m_ParticleAttributeToContainerAttribute ); i++ ) m_ParticleAttributeToContainerAttribute[ i ] = -1;
  40. for ( int i = 0; i < ARRAYSIZE( m_ContainerAttributeToParticleAttribute ); i++ ) m_ContainerAttributeToParticleAttribute[ i ] = -1;
  41. }
  42. //-----------------------------------------------------------------------------
  43. // AddAttributeMapping, add a new field/attribute pair
  44. //-----------------------------------------------------------------------------
  45. bool CParticleSnapshot::AddAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc )
  46. {
  47. if ( ( ( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != -1 ) && ( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != nFieldNumber ) ) ||
  48. ( ( m_ContainerAttributeToParticleAttribute[ nFieldNumber ] != -1 ) && ( m_ContainerAttributeToParticleAttribute[ nFieldNumber ] != nParticleAttribute ) ) )
  49. {
  50. Warning( "CParticleSnapshot::%s - Invalid attribute mapping specified (must be one-to-one)!\n", pFunc );
  51. Assert( 0 );
  52. Purge();
  53. return false;
  54. }
  55. m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] = nFieldNumber;
  56. m_ContainerAttributeToParticleAttribute[ nFieldNumber ] = nParticleAttribute;
  57. return true;
  58. }
  59. //-----------------------------------------------------------------------------
  60. // ValidateAttributeMapping, check datatypes for a field/attribute pair
  61. //-----------------------------------------------------------------------------
  62. bool CParticleSnapshot::ValidateAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc )
  63. {
  64. // Check the presence/datatype of each specified container field
  65. // TODO: support unallocated attributes (requires different 'copy' implementations in operators - can't use memcpy!)
  66. EAttributeDataType nExpectedDataType = g_pParticleSystemMgr->GetParticleAttributeDataType( nParticleAttribute );
  67. if ( ( m_pContainer->GetAttributeType( nFieldNumber ) != nExpectedDataType ) || !m_pContainer->HasAllocatedMemory( nFieldNumber ) )
  68. {
  69. Warning( "CParticleSnapshot::%s - Invalid attribute mapping specified for the provided container!\n", pFunc );
  70. if ( m_pContainer->GetAttributeType( nFieldNumber ) != nExpectedDataType )
  71. Warning( " (data type of container field %d does not match particle attribute %s)\n", nFieldNumber, g_pParticleSystemMgr->GetParticleAttributeName( nParticleAttribute ) );
  72. if ( !m_pContainer->HasAllocatedMemory( nFieldNumber ) )
  73. Warning( " (container field %d has no allocated data)\n", nFieldNumber );
  74. Assert( 0 );
  75. Purge();
  76. return false;
  77. }
  78. return true;
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Init, creating a new container with the specified attribute mapping
  82. //-----------------------------------------------------------------------------
  83. bool CParticleSnapshot::Init( int nX, int nY, int nZ, const AttributeMapVector &attributeMaps )
  84. {
  85. Assert( attributeMaps.Count() > 0 );
  86. if ( attributeMaps.Count() <= 0 )
  87. return false;
  88. Assert( ( nX >= 1 ) && ( nY >= 1 ) && ( nZ >= 1 ) );
  89. if ( ( nX < 1 ) || ( nY < 1 ) || ( nZ < 1 ) )
  90. return false;
  91. Purge();
  92. m_pContainer = &m_Container;
  93. for ( int i = 0; i < attributeMaps.Count(); i++ )
  94. {
  95. int nFieldNumber = attributeMaps[ i ].m_nContainerAttribute;
  96. int nParticleAttribute = attributeMaps[ i ].m_nParticleAttribute;
  97. if ( !AddAttributeMapping( nFieldNumber, nParticleAttribute, "Init" ) )
  98. return false;
  99. // Set the datatype of each specified container field
  100. EAttributeDataType nDataType = g_pParticleSystemMgr->GetParticleAttributeDataType( nParticleAttribute );
  101. m_Container.SetAttributeType( nFieldNumber, nDataType );
  102. }
  103. m_Container.AllocateData( nX, nY, nZ );
  104. return true;
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Init, creating a new container with the specified attribute mapping
  108. //-----------------------------------------------------------------------------
  109. bool CParticleSnapshot::Init( int nX, int nY, int nZ, ... )
  110. {
  111. AttributeMapVector attributeMaps;
  112. ATTRIBUTE_MAPPING_LOOP( nZ )
  113. //{
  114. attributeMaps.AddToTail( AttributeMap( nFieldNumber, nParticleAttribute ) );
  115. }
  116. return Init( nX, nY, nZ, attributeMaps );
  117. }
  118. //-----------------------------------------------------------------------------
  119. // Init using an existing container, with the specified attribute mapping
  120. //-----------------------------------------------------------------------------
  121. bool CParticleSnapshot::InitExternal( CSOAContainer *pContainer, const AttributeMapVector &attributeMaps )
  122. {
  123. Assert( attributeMaps.Count() > 0 );
  124. if ( attributeMaps.Count() <= 0 )
  125. return false;
  126. Purge();
  127. m_pContainer = pContainer;
  128. for ( int i = 0; i < attributeMaps.Count(); i++ )
  129. {
  130. int nFieldNumber = attributeMaps[ i ].m_nContainerAttribute;
  131. int nParticleAttribute = attributeMaps[ i ].m_nParticleAttribute;
  132. if ( !AddAttributeMapping( nFieldNumber, nParticleAttribute, "InitExternal" ) ||
  133. !ValidateAttributeMapping( nFieldNumber, nParticleAttribute, "InitExternal" ) )
  134. return false;
  135. }
  136. return true;
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Init using an existing container, with the specified attribute mapping
  140. //-----------------------------------------------------------------------------
  141. bool CParticleSnapshot::InitExternal( CSOAContainer *pContainer, ... )
  142. {
  143. AttributeMapVector attributeMaps;
  144. ATTRIBUTE_MAPPING_LOOP( pContainer )
  145. //{
  146. attributeMaps.AddToTail( AttributeMap( nFieldNumber, nParticleAttribute ) );
  147. }
  148. return InitExternal( pContainer, attributeMaps );
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Unpack structure for CParticleSnapshot
  152. //-----------------------------------------------------------------------------
  153. BEGIN_DMXELEMENT_UNPACK( CParticleSnapshot )
  154. DMXELEMENT_UNPACK_FIELD_ARRAY( "particle_attribute_to_container_attribute", "-1", int, m_ParticleAttributeToContainerAttribute )
  155. DMXELEMENT_UNPACK_FIELD_ARRAY( "container_attribute_to_particle_attribute", "-1", int, m_ContainerAttributeToParticleAttribute )
  156. END_DMXELEMENT_UNPACK( CParticleSnapshot, s_pParticleSnapshotUnpack )
  157. //-----------------------------------------------------------------------------
  158. // Check whether the particle system's defined attributes have changed
  159. //-----------------------------------------------------------------------------
  160. void CParticleSnapshot::CheckParticleAttributesForChanges( void )
  161. {
  162. // If this doesn't compile, then we need to bump the file version and perform fixup on files saved w/ the old attribute definitions:
  163. // TODO: store out an array of attribute names (g_pParticleSystemMgr->GetParticleAttributeName()) with the data so the fixup can be automatic and general
  164. COMPILE_TIME_ASSERT( MAX_PARTICLE_ATTRIBUTES == 24 );
  165. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_XYZ == 0 );
  166. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_LIFE_DURATION == 1 );
  167. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PREV_XYZ == 2 );
  168. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_RADIUS == 3 );
  169. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ROTATION == 4 );
  170. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ROTATION_SPEED == 5 );
  171. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_TINT_RGB == 6 );
  172. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ALPHA == 7 );
  173. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_CREATION_TIME == 8 );
  174. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER == 9 );
  175. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_TRAIL_LENGTH == 10 );
  176. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PARTICLE_ID == 11 );
  177. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_YAW == 12 );
  178. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1 == 13 );
  179. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_HITBOX_INDEX == 14 );
  180. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ == 15 );
  181. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ALPHA2 == 16 );
  182. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SCRATCH_VEC == 17 );
  183. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SCRATCH_FLOAT == 18 );
  184. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_UNUSED == 19 );
  185. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PITCH == 20 );
  186. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_NORMAL == 21 );
  187. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_GLOW_RGB == 22 );
  188. COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_GLOW_ALPHA == 23 );
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Init from a DMX (.psf) file
  192. //-----------------------------------------------------------------------------
  193. bool CParticleSnapshot::Unserialize( const char *pFullPath )
  194. {
  195. DECLARE_DMX_CONTEXT();
  196. CheckParticleAttributesForChanges();
  197. CDmxElement *pRootElement = NULL;
  198. bool bTextMode = true, bBinaryMode = false;
  199. if ( !UnserializeDMX( pFullPath, "GAME", bTextMode, &pRootElement ) &&
  200. !UnserializeDMX( pFullPath, "GAME", bBinaryMode, &pRootElement ) ) // TODO: shouldn't UnserializeDMX automatically detect text mode?
  201. {
  202. Warning( "ERROR: CParticleSnapshot::Unserialize - could not load file %s!\n", pFullPath );
  203. return false;
  204. }
  205. bool bSuccess = true;
  206. CDmxElement *pParticleSnapshotElement = pRootElement->GetValue< CDmxElement * >( "particle_snapshot" );
  207. if ( !pParticleSnapshotElement )
  208. {
  209. Warning( "ERROR: CParticleSnapshot::Unserialize - %s is not a particle snapshot (.psf) file!\n", pFullPath );
  210. bSuccess = false;
  211. }
  212. int nVersion = -1;
  213. if ( bSuccess )
  214. {
  215. // Read the version number
  216. nVersion = pParticleSnapshotElement->GetValue( "version", -1 );
  217. if ( nVersion == -1 )
  218. {
  219. Warning( "ERROR: CParticleSnapshot::Unserialize - missing version field in file %s\n", pFullPath );
  220. bSuccess = false;
  221. }
  222. }
  223. if ( bSuccess )
  224. {
  225. // Read the CParticleSnapshot structure
  226. Purge();
  227. pParticleSnapshotElement->UnpackIntoStructure( this, s_pParticleSnapshotUnpack );
  228. // Unserialize the embedded CSOAContainer:
  229. CDmxElement *pContainerElement = pParticleSnapshotElement->GetValue< CDmxElement * >( "container" );
  230. if ( !UnserializeCSOAContainer( &m_Container, pContainerElement ) )
  231. {
  232. Warning( "ERROR: CParticleSnapshot::Unserialize - error reading embedded CSOAContainer in file %s\n", pFullPath );
  233. bSuccess = false;
  234. }
  235. }
  236. if ( bSuccess )
  237. {
  238. // Update files saved in old versions
  239. switch( nVersion )
  240. {
  241. case PARTICLE_SNAPSHOT_DMX_VERSION:
  242. // Up to date - nothing to do.
  243. break;
  244. default:
  245. // The DMX unpack structure will set reasonable defaults or flag stuff that needs fixing up
  246. // TODO: add code when versions are bumped and fixup needs to happen
  247. bSuccess = false;
  248. break;
  249. }
  250. }
  251. if ( bSuccess )
  252. {
  253. m_pContainer = &m_Container;
  254. // Validate the attribute mapping (re-'add' it, in both directions, to be paranoid)
  255. int nForwardMaps = 0, nReverseMaps = 0;
  256. for ( int i = 0; i < ARRAYSIZE( m_ParticleAttributeToContainerAttribute ); i++ )
  257. {
  258. int nFieldNumber = m_ParticleAttributeToContainerAttribute[ i ];
  259. if ( nFieldNumber == -1 )
  260. continue;
  261. if ( !AddAttributeMapping( nFieldNumber, i, "Unserialize" ) ||
  262. !ValidateAttributeMapping( nFieldNumber, i, "Unserialize" ) )
  263. {
  264. bSuccess = false;
  265. break;
  266. }
  267. nForwardMaps++;
  268. }
  269. for ( int i = 0; i < ARRAYSIZE( m_ContainerAttributeToParticleAttribute ); i++ )
  270. {
  271. int nParticleAttribute = m_ContainerAttributeToParticleAttribute[ i ];
  272. if ( nParticleAttribute == -1 )
  273. continue;
  274. if ( !AddAttributeMapping( i, nParticleAttribute, "Unserialize" ) ||
  275. !ValidateAttributeMapping( i, nParticleAttribute, "Unserialize" ) )
  276. {
  277. bSuccess = false;
  278. break;
  279. }
  280. nReverseMaps++;
  281. }
  282. if ( bSuccess && ( !nForwardMaps || !nReverseMaps ) )
  283. {
  284. bSuccess = false;
  285. Warning( "ERROR: CParticleSnapshot::Unserialize - error in data in file %s (no attribute mapping specified)\n", pFullPath );
  286. Assert( 0 );
  287. }
  288. }
  289. if ( !bSuccess )
  290. {
  291. // Leave a beautiful corpse
  292. Purge();
  293. }
  294. CleanupDMX( pRootElement );
  295. return bSuccess;
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Write out to a DMX (.psf) file
  299. //-----------------------------------------------------------------------------
  300. bool CParticleSnapshot::Serialize( const char *pFullPath, bool bTextMode )
  301. {
  302. DECLARE_DMX_CONTEXT();
  303. if ( !IsValid() )
  304. {
  305. Warning( "ERROR: CParticleSnapshot::Serialize - cannot serialize an uninitialized CParticleSnapshot! (%s)\n", pFullPath );
  306. return false;
  307. }
  308. const char *pExtension = V_GetFileExtension( pFullPath );
  309. if ( !pExtension || Q_stricmp( pExtension, "psf" ) )
  310. {
  311. Warning( "ERROR: CParticleSnapshot::Serialize - file extension should be '.psf' (%s)\n", pFullPath );
  312. return false;
  313. }
  314. bool bSuccess = true;
  315. CDmxElement *pRootElement = CreateDmxElement( "CDmeElement" );
  316. {
  317. CDmxElementModifyScope modifyRoot( pRootElement );
  318. // Write the version number first
  319. CDmxElement *pParticleSnapshotElement = CreateDmxElement( "CDmeParticleSnapshot" );
  320. pRootElement->SetValue( "particle_snapshot", pParticleSnapshotElement );
  321. int nDmxVersion = PARTICLE_SNAPSHOT_DMX_VERSION;
  322. pParticleSnapshotElement->SetValue( "version", nDmxVersion );
  323. // Then all our member variables
  324. pParticleSnapshotElement->AddAttributesFromStructure( this, s_pParticleSnapshotUnpack );
  325. // Then the embedded container
  326. CDmxElement *pContainerElement = CreateDmxElement( "CDmeSOAContainer" );
  327. pParticleSnapshotElement->SetValue( "container", pContainerElement );
  328. if ( !SerializeCSOAContainer( m_pContainer, pContainerElement ) )
  329. {
  330. Warning( "ERROR: CParticleSnapshot::Serialize - error serializing embedded CSOAContainer for file %s\n", pFullPath );
  331. bSuccess = false;
  332. }
  333. }
  334. if ( bSuccess )
  335. {
  336. // Write out the file
  337. bSuccess = SerializeDMX( pFullPath, "GAME", bTextMode, pRootElement );
  338. }
  339. CleanupDMX( pRootElement );
  340. return bSuccess;
  341. }