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
489 lines
14 KiB
//========= 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 ); |
|
} |
|
} |
|
} |
|
}
|
|
|