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

//===== Copyright � 1996-2007, Valve Corporation, All rights reserved. ======//
//
// Purpose: see CParticleSnapshot declaration (in particles.h)
//
//===========================================================================//
#include "tier0/platform.h"
#include "particles/particles.h"
#include "filesystem.h"
#include "tier2/tier2.h"
#include "tier2/fileutils.h"
#include "tier1/utlbuffer.h"
#include "tier1/UtlStringMap.h"
#include "tier1/strtools.h"
#include "dmxloader/dmxloader.h"
#include "dmxloader/utlsoacontainer_serialization.h"
#include "tier1/lzmaDecoder.h"
#include "tier0/vprof.h"
#include "particles_internal.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// This macro is used to process the attribute mapping parameters, for both vararg versions of Init():
#define ATTRIBUTE_MAPPING_LOOP( _lastarg_ ) \
va_list args; \
va_start( args, _lastarg_ ); \
for(;;) \
{ \
int nFieldNumber = va_arg( args, int ); \
if ( nFieldNumber == -1 ) \
break; \
/* Associate nParticleAttribute with nFieldNumber */ \
int nParticleAttribute = va_arg( args, int );
//-----------------------------------------------------------------------------
// Purge, clear the container back to its initial state
//-----------------------------------------------------------------------------
void CParticleSnapshot::Purge( void )
{
m_Container.Purge();
m_pContainer = NULL;
for ( int i = 0; i < ARRAYSIZE( m_ParticleAttributeToContainerAttribute ); i++ ) m_ParticleAttributeToContainerAttribute[ i ] = -1;
for ( int i = 0; i < ARRAYSIZE( m_ContainerAttributeToParticleAttribute ); i++ ) m_ContainerAttributeToParticleAttribute[ i ] = -1;
}
//-----------------------------------------------------------------------------
// AddAttributeMapping, add a new field/attribute pair
//-----------------------------------------------------------------------------
bool CParticleSnapshot::AddAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc )
{
if ( ( ( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != -1 ) && ( m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] != nFieldNumber ) ) ||
( ( m_ContainerAttributeToParticleAttribute[ nFieldNumber ] != -1 ) && ( m_ContainerAttributeToParticleAttribute[ nFieldNumber ] != nParticleAttribute ) ) )
{
Warning( "CParticleSnapshot::%s - Invalid attribute mapping specified (must be one-to-one)!\n", pFunc );
Assert( 0 );
Purge();
return false;
}
m_ParticleAttributeToContainerAttribute[ nParticleAttribute ] = nFieldNumber;
m_ContainerAttributeToParticleAttribute[ nFieldNumber ] = nParticleAttribute;
return true;
}
//-----------------------------------------------------------------------------
// ValidateAttributeMapping, check datatypes for a field/attribute pair
//-----------------------------------------------------------------------------
bool CParticleSnapshot::ValidateAttributeMapping( int nFieldNumber, int nParticleAttribute, const char *pFunc )
{
// Check the presence/datatype of each specified container field
// TODO: support unallocated attributes (requires different 'copy' implementations in operators - can't use memcpy!)
EAttributeDataType nExpectedDataType = g_pParticleSystemMgr->GetParticleAttributeDataType( nParticleAttribute );
if ( ( m_pContainer->GetAttributeType( nFieldNumber ) != nExpectedDataType ) || !m_pContainer->HasAllocatedMemory( nFieldNumber ) )
{
Warning( "CParticleSnapshot::%s - Invalid attribute mapping specified for the provided container!\n", pFunc );
if ( m_pContainer->GetAttributeType( nFieldNumber ) != nExpectedDataType )
Warning( " (data type of container field %d does not match particle attribute %s)\n", nFieldNumber, g_pParticleSystemMgr->GetParticleAttributeName( nParticleAttribute ) );
if ( !m_pContainer->HasAllocatedMemory( nFieldNumber ) )
Warning( " (container field %d has no allocated data)\n", nFieldNumber );
Assert( 0 );
Purge();
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Init, creating a new container with the specified attribute mapping
//-----------------------------------------------------------------------------
bool CParticleSnapshot::Init( int nX, int nY, int nZ, const AttributeMapVector &attributeMaps )
{
Assert( attributeMaps.Count() > 0 );
if ( attributeMaps.Count() <= 0 )
return false;
Assert( ( nX >= 1 ) && ( nY >= 1 ) && ( nZ >= 1 ) );
if ( ( nX < 1 ) || ( nY < 1 ) || ( nZ < 1 ) )
return false;
Purge();
m_pContainer = &m_Container;
for ( int i = 0; i < attributeMaps.Count(); i++ )
{
int nFieldNumber = attributeMaps[ i ].m_nContainerAttribute;
int nParticleAttribute = attributeMaps[ i ].m_nParticleAttribute;
if ( !AddAttributeMapping( nFieldNumber, nParticleAttribute, "Init" ) )
return false;
// Set the datatype of each specified container field
EAttributeDataType nDataType = g_pParticleSystemMgr->GetParticleAttributeDataType( nParticleAttribute );
m_Container.SetAttributeType( nFieldNumber, nDataType );
}
m_Container.AllocateData( nX, nY, nZ );
return true;
}
//-----------------------------------------------------------------------------
// Init, creating a new container with the specified attribute mapping
//-----------------------------------------------------------------------------
bool CParticleSnapshot::Init( int nX, int nY, int nZ, ... )
{
AttributeMapVector attributeMaps;
ATTRIBUTE_MAPPING_LOOP( nZ )
//{
attributeMaps.AddToTail( AttributeMap( nFieldNumber, nParticleAttribute ) );
}
return Init( nX, nY, nZ, attributeMaps );
}
//-----------------------------------------------------------------------------
// Init using an existing container, with the specified attribute mapping
//-----------------------------------------------------------------------------
bool CParticleSnapshot::InitExternal( CSOAContainer *pContainer, const AttributeMapVector &attributeMaps )
{
Assert( attributeMaps.Count() > 0 );
if ( attributeMaps.Count() <= 0 )
return false;
Purge();
m_pContainer = pContainer;
for ( int i = 0; i < attributeMaps.Count(); i++ )
{
int nFieldNumber = attributeMaps[ i ].m_nContainerAttribute;
int nParticleAttribute = attributeMaps[ i ].m_nParticleAttribute;
if ( !AddAttributeMapping( nFieldNumber, nParticleAttribute, "InitExternal" ) ||
!ValidateAttributeMapping( nFieldNumber, nParticleAttribute, "InitExternal" ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Init using an existing container, with the specified attribute mapping
//-----------------------------------------------------------------------------
bool CParticleSnapshot::InitExternal( CSOAContainer *pContainer, ... )
{
AttributeMapVector attributeMaps;
ATTRIBUTE_MAPPING_LOOP( pContainer )
//{
attributeMaps.AddToTail( AttributeMap( nFieldNumber, nParticleAttribute ) );
}
return InitExternal( pContainer, attributeMaps );
}
//-----------------------------------------------------------------------------
// Unpack structure for CParticleSnapshot
//-----------------------------------------------------------------------------
BEGIN_DMXELEMENT_UNPACK( CParticleSnapshot )
DMXELEMENT_UNPACK_FIELD_ARRAY( "particle_attribute_to_container_attribute", "-1", int, m_ParticleAttributeToContainerAttribute )
DMXELEMENT_UNPACK_FIELD_ARRAY( "container_attribute_to_particle_attribute", "-1", int, m_ContainerAttributeToParticleAttribute )
END_DMXELEMENT_UNPACK( CParticleSnapshot, s_pParticleSnapshotUnpack )
//-----------------------------------------------------------------------------
// Check whether the particle system's defined attributes have changed
//-----------------------------------------------------------------------------
void CParticleSnapshot::CheckParticleAttributesForChanges( void )
{
// If this doesn't compile, then we need to bump the file version and perform fixup on files saved w/ the old attribute definitions:
// TODO: store out an array of attribute names (g_pParticleSystemMgr->GetParticleAttributeName()) with the data so the fixup can be automatic and general
COMPILE_TIME_ASSERT( MAX_PARTICLE_ATTRIBUTES == 24 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_XYZ == 0 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_LIFE_DURATION == 1 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PREV_XYZ == 2 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_RADIUS == 3 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ROTATION == 4 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ROTATION_SPEED == 5 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_TINT_RGB == 6 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ALPHA == 7 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_CREATION_TIME == 8 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER == 9 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_TRAIL_LENGTH == 10 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PARTICLE_ID == 11 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_YAW == 12 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1 == 13 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_HITBOX_INDEX == 14 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_HITBOX_RELATIVE_XYZ == 15 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_ALPHA2 == 16 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SCRATCH_VEC == 17 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_SCRATCH_FLOAT == 18 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_UNUSED == 19 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_PITCH == 20 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_NORMAL == 21 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_GLOW_RGB == 22 );
COMPILE_TIME_ASSERT( PARTICLE_ATTRIBUTE_GLOW_ALPHA == 23 );
}
//-----------------------------------------------------------------------------
// Init from a DMX (.psf) file
//-----------------------------------------------------------------------------
bool CParticleSnapshot::Unserialize( const char *pFullPath )
{
DECLARE_DMX_CONTEXT();
CheckParticleAttributesForChanges();
CDmxElement *pRootElement = NULL;
bool bTextMode = true, bBinaryMode = false;
if ( !UnserializeDMX( pFullPath, "GAME", bTextMode, &pRootElement ) &&
!UnserializeDMX( pFullPath, "GAME", bBinaryMode, &pRootElement ) ) // TODO: shouldn't UnserializeDMX automatically detect text mode?
{
Warning( "ERROR: CParticleSnapshot::Unserialize - could not load file %s!\n", pFullPath );
return false;
}
bool bSuccess = true;
CDmxElement *pParticleSnapshotElement = pRootElement->GetValue< CDmxElement * >( "particle_snapshot" );
if ( !pParticleSnapshotElement )
{
Warning( "ERROR: CParticleSnapshot::Unserialize - %s is not a particle snapshot (.psf) file!\n", pFullPath );
bSuccess = false;
}
int nVersion = -1;
if ( bSuccess )
{
// Read the version number
nVersion = pParticleSnapshotElement->GetValue( "version", -1 );
if ( nVersion == -1 )
{
Warning( "ERROR: CParticleSnapshot::Unserialize - missing version field in file %s\n", pFullPath );
bSuccess = false;
}
}
if ( bSuccess )
{
// Read the CParticleSnapshot structure
Purge();
pParticleSnapshotElement->UnpackIntoStructure( this, s_pParticleSnapshotUnpack );
// Unserialize the embedded CSOAContainer:
CDmxElement *pContainerElement = pParticleSnapshotElement->GetValue< CDmxElement * >( "container" );
if ( !UnserializeCSOAContainer( &m_Container, pContainerElement ) )
{
Warning( "ERROR: CParticleSnapshot::Unserialize - error reading embedded CSOAContainer in file %s\n", pFullPath );
bSuccess = false;
}
}
if ( bSuccess )
{
// Update files saved in old versions
switch( nVersion )
{
case PARTICLE_SNAPSHOT_DMX_VERSION:
// Up to date - nothing to do.
break;
default:
// The DMX unpack structure will set reasonable defaults or flag stuff that needs fixing up
// TODO: add code when versions are bumped and fixup needs to happen
bSuccess = false;
break;
}
}
if ( bSuccess )
{
m_pContainer = &m_Container;
// Validate the attribute mapping (re-'add' it, in both directions, to be paranoid)
int nForwardMaps = 0, nReverseMaps = 0;
for ( int i = 0; i < ARRAYSIZE( m_ParticleAttributeToContainerAttribute ); i++ )
{
int nFieldNumber = m_ParticleAttributeToContainerAttribute[ i ];
if ( nFieldNumber == -1 )
continue;
if ( !AddAttributeMapping( nFieldNumber, i, "Unserialize" ) ||
!ValidateAttributeMapping( nFieldNumber, i, "Unserialize" ) )
{
bSuccess = false;
break;
}
nForwardMaps++;
}
for ( int i = 0; i < ARRAYSIZE( m_ContainerAttributeToParticleAttribute ); i++ )
{
int nParticleAttribute = m_ContainerAttributeToParticleAttribute[ i ];
if ( nParticleAttribute == -1 )
continue;
if ( !AddAttributeMapping( i, nParticleAttribute, "Unserialize" ) ||
!ValidateAttributeMapping( i, nParticleAttribute, "Unserialize" ) )
{
bSuccess = false;
break;
}
nReverseMaps++;
}
if ( bSuccess && ( !nForwardMaps || !nReverseMaps ) )
{
bSuccess = false;
Warning( "ERROR: CParticleSnapshot::Unserialize - error in data in file %s (no attribute mapping specified)\n", pFullPath );
Assert( 0 );
}
}
if ( !bSuccess )
{
// Leave a beautiful corpse
Purge();
}
CleanupDMX( pRootElement );
return bSuccess;
}
//-----------------------------------------------------------------------------
// Write out to a DMX (.psf) file
//-----------------------------------------------------------------------------
bool CParticleSnapshot::Serialize( const char *pFullPath, bool bTextMode )
{
DECLARE_DMX_CONTEXT();
if ( !IsValid() )
{
Warning( "ERROR: CParticleSnapshot::Serialize - cannot serialize an uninitialized CParticleSnapshot! (%s)\n", pFullPath );
return false;
}
const char *pExtension = V_GetFileExtension( pFullPath );
if ( !pExtension || Q_stricmp( pExtension, "psf" ) )
{
Warning( "ERROR: CParticleSnapshot::Serialize - file extension should be '.psf' (%s)\n", pFullPath );
return false;
}
bool bSuccess = true;
CDmxElement *pRootElement = CreateDmxElement( "CDmeElement" );
{
CDmxElementModifyScope modifyRoot( pRootElement );
// Write the version number first
CDmxElement *pParticleSnapshotElement = CreateDmxElement( "CDmeParticleSnapshot" );
pRootElement->SetValue( "particle_snapshot", pParticleSnapshotElement );
int nDmxVersion = PARTICLE_SNAPSHOT_DMX_VERSION;
pParticleSnapshotElement->SetValue( "version", nDmxVersion );
// Then all our member variables
pParticleSnapshotElement->AddAttributesFromStructure( this, s_pParticleSnapshotUnpack );
// Then the embedded container
CDmxElement *pContainerElement = CreateDmxElement( "CDmeSOAContainer" );
pParticleSnapshotElement->SetValue( "container", pContainerElement );
if ( !SerializeCSOAContainer( m_pContainer, pContainerElement ) )
{
Warning( "ERROR: CParticleSnapshot::Serialize - error serializing embedded CSOAContainer for file %s\n", pFullPath );
bSuccess = false;
}
}
if ( bSuccess )
{
// Write out the file
bSuccess = SerializeDMX( pFullPath, "GAME", bTextMode, pRootElement );
}
CleanupDMX( pRootElement );
return bSuccess;
}