|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "dmxloader/dmxelement.h"
#include "dmxloader/dmxattribute.h"
#include "tier1/utlbuffer.h"
#include "mathlib/ssemath.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// globals
//-----------------------------------------------------------------------------
CUtlSymbolTableMT CDmxElement::s_TypeSymbols;
//-----------------------------------------------------------------------------
// Creates a dmx element
//-----------------------------------------------------------------------------
CDmxElement* CreateDmxElement( const char *pType ) { return new CDmxElement( pType ); }
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CDmxElement::CDmxElement( const char *pType ) { m_Type = s_TypeSymbols.AddString( pType ); m_nLockCount = 0; m_bResortNeeded = false; m_bIsMarkedForDeletion = false; CreateUniqueId( &m_Id ); }
CDmxElement::~CDmxElement() { CDmxElementModifyScope modify( this ); RemoveAllAttributes(); }
//-----------------------------------------------------------------------------
// Utility method for getting at the type
//-----------------------------------------------------------------------------
CUtlSymbol CDmxElement::GetType() const { return m_Type; }
const char* CDmxElement::GetTypeString() const { return s_TypeSymbols.String( m_Type ); }
const char* CDmxElement::GetName() const { return GetValue< CUtlString >( "name" ); }
const DmObjectId_t &CDmxElement::GetId() const { return m_Id; }
//-----------------------------------------------------------------------------
// Sets the object id
//-----------------------------------------------------------------------------
void CDmxElement::SetId( const DmObjectId_t &id ) { CopyUniqueId( id, &m_Id ); }
//-----------------------------------------------------------------------------
// Sorts the vector when a change has occurred
//-----------------------------------------------------------------------------
void CDmxElement::Resort( ) const { if ( m_bResortNeeded ) { AttributeList_t *pAttributes = const_cast< AttributeList_t *>( &m_Attributes ); pAttributes->RedoSort(); m_bResortNeeded = false;
// NOTE: This checks for duplicate attribute names
int nCount = m_Attributes.Count(); for ( int i = nCount; --i >= 1; ) { if ( m_Attributes[i]->GetNameSymbol() == m_Attributes[i-1]->GetNameSymbol() ) { Warning( "Duplicate attribute name %s encountered!\n", m_Attributes[i]->GetName() ); pAttributes->Remove(i); Assert( 0 ); } } } }
//-----------------------------------------------------------------------------
// Enables modification of the DmxElement
//-----------------------------------------------------------------------------
void CDmxElement::LockForChanges( bool bLock ) { if ( bLock ) { ++m_nLockCount; } else { if ( --m_nLockCount == 0 ) { Resort(); } Assert( m_nLockCount >= 0 ); } }
//-----------------------------------------------------------------------------
// Adds, removes attributes
//-----------------------------------------------------------------------------
CDmxAttribute *CDmxElement::AddAttribute( const char *pAttributeName ) { int nIndex = FindAttribute( pAttributeName ); if ( nIndex >= 0 ) return m_Attributes[nIndex];
CDmxElementModifyScope modify( this ); m_bResortNeeded = true; CDmxAttribute *pAttribute = new CDmxAttribute( pAttributeName ); m_Attributes.InsertNoSort( pAttribute ); return pAttribute; }
void CDmxElement::RemoveAttribute( const char *pAttributeName ) { CDmxElementModifyScope modify( this ); int idx = FindAttribute( pAttributeName ); if ( idx >= 0 ) { delete m_Attributes[idx]; m_Attributes.Remove( idx ); } }
void CDmxElement::RemoveAttributeByPtr( CDmxAttribute *pAttribute ) { CDmxElementModifyScope modify( this ); int nCount = m_Attributes.Count(); for ( int i = 0; i < nCount; ++i ) { if ( m_Attributes[i] != pAttribute ) continue;
delete pAttribute; m_Attributes.Remove( i ); break; } }
void CDmxElement::RemoveAllAttributes() { int nCount = m_Attributes.Count(); for ( int i = 0; i < nCount; ++i ) { delete m_Attributes[i]; } m_Attributes.RemoveAll(); m_bResortNeeded = false; }
//-----------------------------------------------------------------------------
// Rename an attribute
//-----------------------------------------------------------------------------
void CDmxElement::RenameAttribute( const char *pAttributeName, const char *pNewName ) { CDmxElementModifyScope modify( this );
// No change...
if ( !Q_stricmp( pAttributeName, pNewName ) ) return;
int idx = FindAttribute( pAttributeName ); if ( idx < 0 ) return;
if ( HasAttribute( pNewName ) ) { Warning( "Tried to rename from \"%s\" to \"%s\", but \"%s\" already exists!\n", pAttributeName, pNewName, pNewName ); return; }
m_bResortNeeded = true; m_Attributes[ idx ]->SetName( pNewName ); }
//-----------------------------------------------------------------------------
// Find an attribute by name-based lookup
//-----------------------------------------------------------------------------
int CDmxElement::FindAttribute( const char *pAttributeName ) const { // FIXME: The cost here is O(log M) + O(log N)
// where log N is the binary search for the symbol match
// and log M is the binary search for the attribute name->symbol
// We can eliminate log M by using a hash table in the symbol lookup
Resort(); CDmxAttribute search( pAttributeName ); return m_Attributes.Find( &search ); }
//-----------------------------------------------------------------------------
// Find an attribute by name-based lookup
//-----------------------------------------------------------------------------
int CDmxElement::FindAttribute( CUtlSymbol attributeName ) const { Resort(); CDmxAttribute search( attributeName ); return m_Attributes.Find( &search ); }
//-----------------------------------------------------------------------------
// Attribute finding
//-----------------------------------------------------------------------------
bool CDmxElement::HasAttribute( const char *pAttributeName ) const { int idx = FindAttribute( pAttributeName ); return ( idx >= 0 ); }
CDmxAttribute *CDmxElement::GetAttribute( const char *pAttributeName ) { int idx = FindAttribute( pAttributeName ); if ( idx >= 0 ) return m_Attributes[ idx ]; return NULL; }
const CDmxAttribute *CDmxElement::GetAttribute( const char *pAttributeName ) const { int idx = FindAttribute( pAttributeName ); if ( idx >= 0 ) return m_Attributes[ idx ]; return NULL; }
//-----------------------------------------------------------------------------
// Attribute interation
//-----------------------------------------------------------------------------
int CDmxElement::AttributeCount() const { return m_Attributes.Count(); }
CDmxAttribute *CDmxElement::GetAttribute( int nIndex ) { return m_Attributes[ nIndex ]; }
const CDmxAttribute *CDmxElement::GetAttribute( int nIndex ) const { return m_Attributes[ nIndex ]; }
//-----------------------------------------------------------------------------
// Removes all elements recursively
//-----------------------------------------------------------------------------
void CDmxElement::AddElementsToDelete( CUtlVector< CDmxElement * >& elementsToDelete ) { if ( m_bIsMarkedForDeletion ) return;
m_bIsMarkedForDeletion = true; elementsToDelete.AddToTail( this );
int nCount = AttributeCount(); for ( int i = 0; i < nCount; ++i ) { CDmxAttribute *pAttribute = GetAttribute(i); if ( pAttribute->GetType() == AT_ELEMENT ) { CDmxElement *pElement = pAttribute->GetValue< CDmxElement* >(); if ( pElement ) { pElement->AddElementsToDelete( elementsToDelete ); } continue; }
if ( pAttribute->GetType() == AT_ELEMENT_ARRAY ) { const CUtlVector< CDmxElement * > &elements = pAttribute->GetArray< CDmxElement* >(); int nElementCount = elements.Count(); for ( int j = 0; j < nElementCount; ++j ) { if ( elements[j] ) { elements[j]->AddElementsToDelete( elementsToDelete ); } } continue; } } }
//-----------------------------------------------------------------------------
// Removes all elements recursively
//-----------------------------------------------------------------------------
void CDmxElement::RemoveAllElementsRecursive() { CUtlVector< CDmxElement * > elementsToDelete; AddElementsToDelete( elementsToDelete ); int nCount = elementsToDelete.Count(); for ( int i = 0; i < nCount; ++i ) { delete elementsToDelete[i]; } }
//-----------------------------------------------------------------------------
// Method to unpack data into a structure
//-----------------------------------------------------------------------------
void CDmxElement::UnpackIntoStructure( void *pData, size_t DestSizeInBytes, const DmxElementUnpackStructure_t *pUnpack ) const { void *pDataEnd = ( char * )pData + DestSizeInBytes;
for ( ; pUnpack->m_AttributeType != AT_UNKNOWN; ++pUnpack ) { char *pDest = (char*)pData + pUnpack->m_nOffset;
// NOTE: This does not work with array data at the moment
if ( IsArrayType( pUnpack->m_AttributeType ) ) { AssertMsg( 0, ( "CDmxElement::UnpackIntoStructure: Array attribute types not currently supported!\n" ) ); continue; }
if ( pUnpack->m_AttributeType == AT_VOID ) { AssertMsg( 0, ( "CDmxElement::UnpackIntoStructure: Binary blob attribute types not currently supported!\n" ) ); continue; }
CDmxAttribute temp( NULL ); const CDmxAttribute *pAttribute = GetAttribute( pUnpack->m_pAttributeName ); if ( !pAttribute ) { if ( !pUnpack->m_pDefaultString ) continue;
// Convert the default string into the target
int nLen = Q_strlen( pUnpack->m_pDefaultString ); if ( nLen > 0 ) { CUtlBuffer buf( pUnpack->m_pDefaultString, nLen, CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); temp.Unserialize( pUnpack->m_AttributeType, buf ); } else { CUtlBuffer buf; temp.Unserialize( pUnpack->m_AttributeType, buf ); } pAttribute = &temp; }
if ( pUnpack->m_AttributeType != pAttribute->GetType() ) { Warning( "CDmxElement::UnpackIntoStructure: Mismatched attribute type in attribute \"%s\"!\n", pUnpack->m_pAttributeName ); continue; }
if ( pAttribute->GetType() == AT_STRING ) { if ( pDest + pUnpack->m_nSize > pDataEnd ) { Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure string buffer overrun!\n" ); continue; }
// Strings get special treatment: they are stored as in-line arrays of chars
Q_strncpy( pDest, pAttribute->GetValueString(), pUnpack->m_nSize ); continue; }
// special case - if data type is float, but dest size == 16, we are unpacking into simd by
// replication
if ( ( pAttribute->GetType() == AT_FLOAT ) && ( pUnpack->m_nSize == sizeof( fltx4 ) ) ) { if ( pDest + 4 * sizeof( float ) > pDataEnd ) { Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure float buffer overrun!\n" ); continue; }
memcpy( pDest + 0 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) ); memcpy( pDest + 1 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) ); memcpy( pDest + 2 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) ); memcpy( pDest + 3 * sizeof( float ) , pAttribute->m_pData, sizeof( float ) ); } else { if ( pDest + pUnpack->m_nSize > pDataEnd ) { Warning( "ERROR Memory corruption: CDmxElement::UnpackIntoStructure memcpy buffer overrun!\n" ); continue; }
AssertMsg( pUnpack->m_nSize == CDmxAttribute::AttributeDataSize( pAttribute->GetType() ), "CDmxElement::UnpackIntoStructure: Incorrect size to unpack data into in attribute \"%s\"!\n", pUnpack->m_pAttributeName ); memcpy( pDest, pAttribute->m_pData, pUnpack->m_nSize ); } } }
//-----------------------------------------------------------------------------
// Creates attributes based on the unpack structure
//-----------------------------------------------------------------------------
void CDmxElement::AddAttributesFromStructure_Internal( const void *pData, size_t byteCount, const DmxElementUnpackStructure_t *pUnpack ) { for ( ; pUnpack->m_AttributeType != AT_UNKNOWN; ++pUnpack ) { const char *pSrc = (const char*)pData + pUnpack->m_nOffset;
// NOTE: This does not work with array data at the moment
if ( IsArrayType( pUnpack->m_AttributeType ) ) { AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Array attribute types not currently supported!\n" ); continue; }
if ( pUnpack->m_AttributeType == AT_VOID ) { AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Binary blob attribute types not currently supported!\n" ); continue; }
if ( HasAttribute( pUnpack->m_pAttributeName ) ) { AssertMsg( 0, "CDmxElement::AddAttributesFromStructure: Attribute %s already exists!\n", pUnpack->m_pAttributeName ); continue; }
{ if ( (size_t)(pUnpack->m_nOffset + pUnpack->m_nSize) > byteCount ) { Msg( "Buffer underread! Mismatched type/type-descriptor.\n" ); } CDmxElementModifyScope modify( this ); CDmxAttribute *pAttribute = AddAttribute( pUnpack->m_pAttributeName ); if ( pUnpack->m_AttributeType == AT_STRING ) { pAttribute->SetValue( pSrc ); } else { int nSize = pUnpack->m_nSize;
// handle float attrs stored as replicated fltx4's
if ( ( pUnpack->m_AttributeType == AT_FLOAT ) && ( nSize == sizeof( fltx4 ) ) ) { nSize = sizeof( float ); }
AssertMsg( nSize == CDmxAttribute::AttributeDataSize( pUnpack->m_AttributeType ), "CDmxElement::UnpackIntoStructure: Incorrect size to unpack data into in attribute \"%s\"!\n", pUnpack->m_pAttributeName ); pAttribute->SetValue( pUnpack->m_AttributeType, pSrc, nSize ); } } } }
|