This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View File

@ -0,0 +1,209 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "DmElementFramework.h"
#include "datamodel.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CDmElementFramework g_DmElementFramework;
CDmElementFramework *g_pDmElementFrameworkImp = &g_DmElementFramework;
IDmElementFramework *g_pDmElementFramework = &g_DmElementFramework;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CDmElementFramework::CDmElementFramework() : m_phase( PH_EDIT ), m_dirtyElements( 128, 256 )
{
}
//-----------------------------------------------------------------------------
// Methods of IAppSystem
//-----------------------------------------------------------------------------
bool CDmElementFramework::Connect( CreateInterfaceFn factory )
{
return true;
}
void CDmElementFramework::Disconnect()
{
}
void *CDmElementFramework::QueryInterface( const char *pInterfaceName )
{
if ( !V_strcmp( pInterfaceName, VDMELEMENTFRAMEWORK_VERSION ) )
return (IDmElementFramework*)this;
return NULL;
}
InitReturnVal_t CDmElementFramework::Init( )
{
return INIT_OK;
}
void CDmElementFramework::Shutdown()
{
m_dependencyGraph.Cleanup();
}
//-----------------------------------------------------------------------------
// element framework phase transition methods
//-----------------------------------------------------------------------------
void CDmElementFramework::EditApply()
{
g_pDataModelImp->RemoveUnreferencedElements();
}
void CDmElementFramework::Resolve( bool clearDirtyFlags )
{
int nCount = m_dirtyElements.Count();
for ( int ei = 0; ei < nCount; ++ei )
{
DmElementHandle_t h = m_dirtyElements[ ei ];
CDmElement *pElement = g_pDataModel->GetElement( h );
if ( !pElement )
continue;
pElement->Resolve();
if ( clearDirtyFlags )
{
CDmeElementAccessor::MarkDirty( pElement, false ); // marks element clean
CDmeElementAccessor::MarkAttributesClean( pElement ); // marks all attributes clean
}
}
if ( clearDirtyFlags )
{
m_dirtyElements.RemoveAll();
}
}
//-----------------------------------------------------------------------------
// Returns the current phase
//-----------------------------------------------------------------------------
DmPhase_t CDmElementFramework::GetPhase()
{
return FastGetPhase();
}
void CDmElementFramework::SetOperators( const CUtlVector< IDmeOperator* > &operators )
{
VPROF( "CDmElementFramework::SetOperators()" );
m_dependencyGraph.Reset( operators );
}
void CDmElementFramework::BeginEdit()
{
Assert( m_phase == PH_EDIT || m_phase == PH_OUTPUT );
if ( m_phase == PH_EDIT )
{
m_phase = PH_EDIT_APPLY;
EditApply();
m_phase = PH_EDIT_RESOLVE;
Resolve( false );
}
m_phase = PH_EDIT;
}
void CDmElementFramework::Operate( bool bResolve )
{
VPROF( "CDmElementFramework::Operate" );
Assert( m_phase == PH_EDIT || m_phase == PH_OUTPUT );
if ( m_phase == PH_EDIT )
{
{
VPROF( "CDmElementFramework::PH_EDIT_APPLY" );
m_phase = PH_EDIT_APPLY;
EditApply();
}
{
VPROF( "CDmElementFramework::PH_EDIT_RESOLVE" );
m_phase = PH_EDIT_RESOLVE;
Resolve( false );
}
}
{
VPROF( "CDmElementFramework::PH_DEPENDENCY" );
m_phase = PH_DEPENDENCY;
bool cycle = m_dependencyGraph.CullAndSortOperators();
if ( cycle )
{
Warning( "Operator cycle found during dependency graph traversal!\n" );
}
}
{
VPROF( "CDmElementFramework::PH_OPERATE" );
m_phase = PH_OPERATE;
const CUtlVector< IDmeOperator* > &operatorsToRun = m_dependencyGraph.GetSortedOperators();
uint on = operatorsToRun.Count();
for ( uint oi = 0; oi < on; ++oi )
{
operatorsToRun[ oi ]->Operate();
}
}
if ( bResolve )
{
VPROF( "CDmElementFramework::PH_OPERATE_RESOLVE" );
m_phase = PH_OPERATE_RESOLVE;
Resolve( true );
m_phase = PH_OUTPUT;
}
}
void CDmElementFramework::Resolve()
{
VPROF( "CDmElementFramework::Resolve" );
Assert( m_phase == PH_OPERATE );
m_phase = PH_OPERATE_RESOLVE;
Resolve( true );
m_phase = PH_OUTPUT;
}
void CDmElementFramework::AddElementToDirtyList( DmElementHandle_t hElement )
{
m_dirtyElements.AddToTail( hElement );
}
void CDmElementFramework::RemoveCleanElementsFromDirtyList()
{
int nCount = m_dirtyElements.Count();
while ( --nCount >= 0 )
{
DmElementHandle_t h = m_dirtyElements[ nCount ];
CDmElement *pElement = g_pDataModel->GetElement( h );
if ( !pElement )
continue;
if ( !CDmeElementAccessor::IsDirty( pElement ) )
{
m_dirtyElements.FastRemove( nCount );
}
}
}

View File

@ -0,0 +1,78 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DMELEMENTFRAMEWORK_H
#define DMELEMENTFRAMEWORK_H
#ifdef _WIN32
#pragma once
#endif
#include "datamodel/idatamodel.h"
#include "tier1/utlvector.h"
#include "dependencygraph.h"
//-----------------------------------------------------------------------------
// element framework implementation
//-----------------------------------------------------------------------------
class CDmElementFramework : public IDmElementFramework
{
public:
CDmElementFramework();
public:
// Methods of IAppSystem
virtual bool Connect( CreateInterfaceFn factory );
virtual void Disconnect();
virtual void *QueryInterface( const char *pInterfaceName );
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Methods of IDmElementFramework
virtual DmPhase_t GetPhase();
virtual void SetOperators( const CUtlVector< IDmeOperator* > &operators );
virtual void BeginEdit(); // ends in edit phase, forces apply/resolve if from edit phase
virtual void Operate( bool bResolve ); // ends in output phase
virtual void Resolve();
public:
// Other public methods
void AddElementToDirtyList( DmElementHandle_t hElement );
void RemoveCleanElementsFromDirtyList();
// Non-virtual methods of identical virtual functions
DmPhase_t FastGetPhase();
private:
void EditApply();
// Invoke the resolve method
void Resolve( bool clearDirtyFlags );
CDependencyGraph m_dependencyGraph;
CUtlVector< DmElementHandle_t > m_dirtyElements;
DmPhase_t m_phase;
};
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
extern CDmElementFramework *g_pDmElementFrameworkImp;
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline DmPhase_t CDmElementFramework::FastGetPhase()
{
return m_phase;
}
#endif // DMELEMENTFRAMEWORK_H

View File

@ -0,0 +1,169 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "clipboardmanager.h"
#include "datamodel.h"
#include "tier1/KeyValues.h"
#ifndef _LINUX
#define USE_WINDOWS_CLIPBOARD
#endif
#if defined( USE_WINDOWS_CLIPBOARD )
#include <windows.h>
#endif
CClipboardManager::CClipboardManager( ) :
m_pfnCleanup( NULL )
{
}
CClipboardManager::~CClipboardManager()
{
EmptyClipboard( false );
}
void CClipboardManager::EmptyClipboard( bool bClearWindowsClipboard )
{
// Call optional cleanup function if there is one...
if ( m_pfnCleanup )
{
m_pfnCleanup->ReleaseClipboardData( m_Data );
}
int c = m_Data.Count();
for ( int i = 0; i < c; ++i )
{
m_Data[ i ]->deleteThis();
}
m_Data.RemoveAll();
m_pfnCleanup = NULL;
#if defined( USE_WINDOWS_CLIPBOARD )
if ( bClearWindowsClipboard )
{
if ( ::OpenClipboard( ::GetDesktopWindow() ) )
{
::EmptyClipboard();
::CloseClipboard();
}
}
#endif
}
void CClipboardManager::SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction )
{
EmptyClipboard( true );
m_Data = data;
m_pfnCleanup = pfnOptionalCleanuFunction;
#if defined( USE_WINDOWS_CLIPBOARD )
if ( m_Data.Count() >= 0 )
{
// Only stick the first item's data into the clipboard
char const *text = m_Data[ 0 ]->GetString( "text", "" );
if ( text && text[ 0 ] )
{
int textLen = Q_strlen( text );
if ( ::OpenClipboard( ::GetDesktopWindow() ) )
{
HANDLE hmem = ::GlobalAlloc(GMEM_MOVEABLE, textLen + 1);
if (hmem)
{
void *ptr = ::GlobalLock( hmem );
if ( ptr )
{
Q_memset( ptr, 0, textLen + 1 );
Q_memcpy( ptr, text, textLen );
::GlobalUnlock( hmem );
::SetClipboardData( CF_TEXT, hmem );
}
}
::CloseClipboard();
}
}
}
#endif
}
void CClipboardManager::AddToClipboardData( KeyValues *add )
{
m_Data.AddToTail( add );
#if defined( USE_WINDOWS_CLIPBOARD )
if ( m_Data.Count() >= 0 )
{
// Only stick the first item's data into the clipboard
char const *text = m_Data[ 0 ]->GetString( "text", "" );
if ( text && text[ 0 ] )
{
int textLen = Q_strlen( text );
if ( ::OpenClipboard( ::GetDesktopWindow() ) )
{
::EmptyClipboard();
HANDLE hmem = ::GlobalAlloc(GMEM_MOVEABLE, textLen + 1);
if (hmem)
{
void *ptr = ::GlobalLock( hmem );
if ( ptr )
{
Q_memset( ptr, 0, textLen + 1 );
Q_memcpy( ptr, text, textLen );
::GlobalUnlock( hmem );
::SetClipboardData( CF_TEXT, hmem );
}
}
::CloseClipboard();
}
}
}
#endif
}
void CClipboardManager::GetClipboardData( CUtlVector< KeyValues * >& data )
{
data.RemoveAll();
data = m_Data;
#if defined( USE_WINDOWS_CLIPBOARD )
if ( data.Count() == 0 )
{
// See if windows has some text since we didn't have any internally
if ( ::OpenClipboard( ::GetDesktopWindow() ) )
{
HANDLE hmem = ::GetClipboardData( CF_TEXT );
if ( hmem )
{
int len = GlobalSize( hmem );
if ( len > 0 )
{
void *ptr = GlobalLock(hmem);
if ( ptr )
{
char buf[ 8192 ];
len = min( len, 8191 );
Q_memcpy( buf, ( char * )ptr, len );
buf[ 8191 ] = 0;
GlobalUnlock(hmem);
KeyValues *newData = new KeyValues( "ClipBoard", "text", buf );
data.AddToTail( newData );
}
}
}
::CloseClipboard();
}
}
#endif
}
bool CClipboardManager::HasClipboardData() const
{
return m_Data.Count() > 0 ? true : false;
}

View File

@ -0,0 +1,36 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef CLIPBOARDMANAGER_H
#define CLIPBOARDMANAGER_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
class KeyValues;
class IClipboardCleanup;
// Clipboard:
//
class CClipboardManager
{
public:
CClipboardManager();
~CClipboardManager();
void EmptyClipboard( bool bClearWindowsClipboard );
void SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction );
void AddToClipboardData( KeyValues *add );
void GetClipboardData( CUtlVector< KeyValues * >& data );
bool HasClipboardData() const;
private:
CUtlVector< KeyValues * > m_Data;
IClipboardCleanup *m_pfnCleanup;
};
#endif // CLIPBOARDMANAGER_H

2464
datamodel/datamodel.cpp Normal file

File diff suppressed because it is too large Load Diff

527
datamodel/datamodel.h Normal file
View File

@ -0,0 +1,527 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DATAMODEL_H
#define DATAMODEL_H
#ifdef _WIN32
#pragma once
#endif
#include "datamodel/dmattribute.h"
#include "datamodel/idatamodel.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmehandle.h"
#include "tier1/uniqueid.h"
#include "tier1/utlsymbol.h"
#include "tier1/utllinkedlist.h"
#include "tier1/utldict.h"
#include "tier1/utlstring.h"
#include "tier1/utlhandletable.h"
#include "tier1/utlhash.h"
#include "tier2/tier2.h"
#include "clipboardmanager.h"
#include "undomanager.h"
#include "tier1/convar.h"
#include "tier0/vprof.h"
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
class IDmElementFramework;
class IUndoElement;
class CDmElement;
enum DmHandleReleasePolicy
{
HR_ALWAYS,
HR_NEVER,
HR_IF_NOT_REFERENCED,
};
//-----------------------------------------------------------------------------
// memory categories
//-----------------------------------------------------------------------------
enum
{
MEMORY_CATEGORY_OUTER,
MEMORY_CATEGORY_ELEMENT_INTERNAL,
MEMORY_CATEGORY_DATAMODEL,
MEMORY_CATEGORY_REFERENCES,
MEMORY_CATEGORY_ATTRIBUTE_TREE,
MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD,
MEMORY_CATEGORY_ATTRIBUTE_DATA,
MEMORY_CATEGORY_ATTRIBUTE_COUNT,
MEMORY_CATEGORY_COUNT,
};
//-----------------------------------------------------------------------------
// hash map of id->element, with the id storage optimized out
//-----------------------------------------------------------------------------
class CElementIdHash : public CUtlHash< DmElementHandle_t >
{
public:
CElementIdHash( int nBucketCount = 0, int nGrowCount = 0, int nInitCount = 0 )
: CUtlHash< DmElementHandle_t >( nBucketCount, nGrowCount, nInitCount, CompareFunc, KeyFunc )
{
}
protected:
typedef CUtlHash< DmElementHandle_t > BaseClass;
static bool CompareFunc( DmElementHandle_t const& a, DmElementHandle_t const& b ) { return a == b; }
static bool IdCompareFunc( DmElementHandle_t const& hElement, DmObjectId_t const& id )
{
CDmElement *pElement = g_pDataModel->GetElement( hElement );
Assert( pElement );
if ( !pElement )
return false;
return IsUniqueIdEqual( id, pElement->GetId() );
}
static unsigned int KeyFunc( DmElementHandle_t const& hElement )
{
CDmElement *pElement = g_pDataModel->GetElement( hElement );
Assert( pElement );
if ( !pElement )
return 0;
return *( unsigned int* )&pElement->GetId();
}
static unsigned int IdKeyFunc( DmObjectId_t const &src )
{
return *(unsigned int*)&src;
}
protected:
bool DoFind( DmObjectId_t const &src, unsigned int *pBucket, int *pIndex )
{
// generate the data "key"
unsigned int key = IdKeyFunc( src );
// hash the "key" - get the correct hash table "bucket"
unsigned int ndxBucket;
if( m_bPowerOfTwo )
{
*pBucket = ndxBucket = ( key & m_ModMask );
}
else
{
int bucketCount = m_Buckets.Count();
*pBucket = ndxBucket = key % bucketCount;
}
int ndxKeyData;
CUtlVector< DmElementHandle_t > &bucket = m_Buckets[ndxBucket];
int keyDataCount = bucket.Count();
for( ndxKeyData = 0; ndxKeyData < keyDataCount; ndxKeyData++ )
{
if( IdCompareFunc( bucket.Element( ndxKeyData ), src ) )
break;
}
if( ndxKeyData == keyDataCount )
return false;
*pIndex = ndxKeyData;
return true;
}
public:
UtlHashHandle_t Find( DmElementHandle_t const &src ) { return BaseClass::Find( src ); }
UtlHashHandle_t Find( DmObjectId_t const &src )
{
unsigned int ndxBucket;
int ndxKeyData;
if ( DoFind( src, &ndxBucket, &ndxKeyData ) )
return BuildHandle( ndxBucket, ndxKeyData );
return InvalidHandle();
}
};
//-----------------------------------------------------------------------------
// struct to hold the set of elements in any given file
//-----------------------------------------------------------------------------
struct FileElementSet_t
{
FileElementSet_t( UtlSymId_t filename = UTL_INVAL_SYMBOL, UtlSymId_t format = UTL_INVAL_SYMBOL ) :
m_filename( filename ), m_format( format ),
m_hRoot( DMELEMENT_HANDLE_INVALID ),
m_bLoaded( true ),
m_nElements( 0 )
{
}
FileElementSet_t( const FileElementSet_t& that ) : m_filename( that.m_filename ), m_format( that.m_format ), m_hRoot( DMELEMENT_HANDLE_INVALID ), m_bLoaded( that.m_bLoaded ), m_nElements( that.m_nElements )
{
// the only time this should be copy constructed is when passing in an empty set to the parent array
// otherwise it could get prohibitively expensive time and memory wise
Assert( that.m_nElements == 0 );
}
UtlSymId_t m_filename;
UtlSymId_t m_format;
CDmeCountedHandle m_hRoot;
bool m_bLoaded;
int m_nElements;
};
//-----------------------------------------------------------------------------
// Purpose: Versionable factor for element types
//-----------------------------------------------------------------------------
class CDataModel : public CBaseAppSystem< IDataModel >
{
typedef CBaseAppSystem< IDataModel > BaseClass;
public:
CDataModel();
virtual ~CDataModel();
// External interface
public:
// Methods of IAppSystem
virtual bool Connect( CreateInterfaceFn factory );
virtual void *QueryInterface( const char *pInterfaceName );
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Methods of IDataModel
virtual void AddElementFactory( const char *pClassName, IDmElementFactory *pFactory );
virtual bool HasElementFactory( const char *pElementType ) const;
virtual void SetDefaultElementFactory( IDmElementFactory *pFactory );
virtual int GetFirstFactory() const;
virtual int GetNextFactory( int index ) const;
virtual bool IsValidFactory( int index ) const;
virtual char const *GetFactoryName( int index ) const;
virtual DmElementHandle_t CreateElement( UtlSymId_t typeSymbol, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID = NULL );
virtual DmElementHandle_t CreateElement( const char *pTypeName, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID = NULL );
virtual void DestroyElement( DmElementHandle_t hElement );
virtual CDmElement* GetElement( DmElementHandle_t hElement ) const;
virtual UtlSymId_t GetElementType( DmElementHandle_t hElement ) const;
virtual const char* GetElementName( DmElementHandle_t hElement ) const;
virtual const DmObjectId_t& GetElementId( DmElementHandle_t hElement ) const;
virtual const char *GetAttributeNameForType( DmAttributeType_t attType ) const;
virtual DmAttributeType_t GetAttributeTypeForName( const char *name ) const;
virtual void AddSerializer( IDmSerializer *pSerializer );
virtual void AddLegacyUpdater( IDmLegacyUpdater *pUpdater );
virtual void AddFormatUpdater( IDmFormatUpdater *pUpdater );
virtual const char* GetFormatExtension( const char *pFormatName );
virtual const char* GetFormatDescription( const char *pFormatName );
virtual int GetFormatCount() const;
virtual const char * GetFormatName( int i ) const;
virtual const char * GetDefaultEncoding( const char *pFormatName );
virtual int GetEncodingCount() const;
virtual const char * GetEncodingName( int i ) const;
virtual bool IsEncodingBinary( const char *pEncodingName ) const;
virtual bool DoesEncodingStoreVersionInFile( const char *pEncodingName ) const;
virtual void SetSerializationDelimiter( CUtlCharConversion *pConv );
virtual void SetSerializationArrayDelimiter( const char *pDelimiter );
virtual bool IsUnserializing();
virtual bool Serialize( CUtlBuffer &outBuf, const char *pEncodingName, const char *pFormatName, DmElementHandle_t hRoot );
virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, const char *pSourceFormatName, const char *pFormatHint,
const char *pFileName, DmConflictResolution_t idConflictResolution, DmElementHandle_t &hRoot );
virtual bool UpdateUnserializedElements( const char *pSourceFormatName, int nSourceFormatVersion,
DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot );
virtual IDmSerializer* FindSerializer( const char *pEncodingName ) const;
virtual IDmLegacyUpdater* FindLegacyUpdater( const char *pLegacyFormatName ) const;
virtual IDmFormatUpdater* FindFormatUpdater( const char *pFormatName ) const;
virtual bool SaveToFile( char const *pFileName, char const *pPathID, const char *pEncodingName, const char *pFormatName, CDmElement *pRoot );
virtual DmFileId_t RestoreFromFile( char const *pFileName, char const *pPathID, const char *pFormatHint, CDmElement **ppRoot, DmConflictResolution_t idConflictResolution = CR_DELETE_NEW, DmxHeader_t *pHeaderOut = NULL );
virtual void SetKeyValuesElementCallback( IElementForKeyValueCallback *pCallbackInterface );
virtual const char * GetKeyValuesElementName( const char *pszKeyName, int iNestingLevel );
virtual UtlSymId_t GetSymbol( const char *pString );
virtual const char * GetString( UtlSymId_t sym ) const;
virtual int GetElementsAllocatedSoFar();
virtual int GetMaxNumberOfElements();
virtual int GetAllocatedAttributeCount();
virtual int GetAllocatedElementCount();
virtual DmElementHandle_t FirstAllocatedElement();
virtual DmElementHandle_t NextAllocatedElement( DmElementHandle_t hElement );
virtual int EstimateMemoryUsage( DmElementHandle_t hElement, TraversalDepth_t depth = TD_DEEP );
virtual void SetUndoEnabled( bool enable );
virtual bool IsUndoEnabled() const;
virtual bool UndoEnabledForElement( const CDmElement *pElement ) const;
virtual bool IsDirty() const;
virtual bool CanUndo() const;
virtual bool CanRedo() const;
virtual void StartUndo( const char *undodesc, const char *redodesc, int nChainingID = 0 );
virtual void FinishUndo();
virtual void AbortUndoableOperation();
virtual void ClearRedo();
virtual const char *GetUndoDesc();
virtual const char *GetRedoDesc();
virtual void Undo();
virtual void Redo();
virtual void TraceUndo( bool state ); // if true, undo records spew as they are added
virtual void ClearUndo();
virtual void GetUndoInfo( CUtlVector< UndoInfo_t >& list );
virtual const char * GetUndoString( UtlSymId_t sym );
virtual void AddUndoElement( IUndoElement *pElement );
virtual UtlSymId_t GetUndoDescInternal( const char *context );
virtual UtlSymId_t GetRedoDescInternal( const char *context );
virtual void EmptyClipboard();
virtual void SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction = 0 );
virtual void AddToClipboardData( KeyValues *add );
virtual void GetClipboardData( CUtlVector< KeyValues * >& data );
virtual bool HasClipboardData() const;
virtual CDmAttribute * GetAttribute( DmAttributeHandle_t h );
virtual bool IsAttributeHandleValid( DmAttributeHandle_t h ) const;
virtual void OnlyCreateUntypedElements( bool bEnable );
virtual int NumFileIds();
virtual DmFileId_t GetFileId( int i );
virtual DmFileId_t FindOrCreateFileId( const char *pFilename );
virtual void RemoveFileId( DmFileId_t fileid );
virtual DmFileId_t GetFileId( const char *pFilename );
virtual const char * GetFileName( DmFileId_t fileid );
virtual void SetFileName( DmFileId_t fileid, const char *pFileName );
virtual const char * GetFileFormat( DmFileId_t fileid );
virtual void SetFileFormat( DmFileId_t fileid, const char *pFormat );
virtual DmElementHandle_t GetFileRoot( DmFileId_t fileid );
virtual void SetFileRoot( DmFileId_t fileid, DmElementHandle_t hRoot );
virtual bool IsFileLoaded( DmFileId_t fileid );
virtual void MarkFileLoaded( DmFileId_t fileid );
virtual void UnloadFile( DmFileId_t fileid );
virtual int NumElementsInFile( DmFileId_t fileid );
virtual void DontAutoDelete( DmElementHandle_t hElement );
virtual void MarkHandleInvalid( DmElementHandle_t hElement );
virtual void MarkHandleValid( DmElementHandle_t hElement );
virtual DmElementHandle_t FindElement( const DmObjectId_t &id );
virtual DmAttributeReferenceIterator_t FirstAttributeReferencingElement( DmElementHandle_t hElement );
virtual DmAttributeReferenceIterator_t NextAttributeReferencingElement( DmAttributeReferenceIterator_t hAttrIter );
virtual CDmAttribute * GetAttribute( DmAttributeReferenceIterator_t hAttrIter );
virtual bool InstallNotificationCallback( IDmNotify *pNotify );
virtual void RemoveNotificationCallback( IDmNotify *pNotify );
virtual bool IsSuppressingNotify( ) const;
virtual void SetSuppressingNotify( bool bSuppress );
virtual void PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags );
virtual void PopNotificationScope( bool bAbort );
virtual void SetUndoDepth( int nSize );
virtual void DisplayMemoryStats();
public:
// Internal public methods
int GetCurrentFormatVersion( const char *pFormatName );
// CreateElement references the attribute list passed in via ref, so don't edit or purge ref's attribute list afterwards
CDmElement* CreateElement( const DmElementReference_t &ref, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t *pObjectID );
void DeleteElement( DmElementHandle_t hElement, DmHandleReleasePolicy hrp = HR_ALWAYS );
// element handle related methods
DmElementHandle_t AcquireElementHandle();
void ReleaseElementHandle( DmElementHandle_t hElement );
// Handles to attributes
DmAttributeHandle_t AcquireAttributeHandle( CDmAttribute *pAttribute );
void ReleaseAttributeHandle( DmAttributeHandle_t hAttribute );
// remove orphaned element subtrees
void FindAndDeleteOrphanedElements();
// Event "mailing list"
DmMailingList_t CreateMailingList();
void DestroyMailingList( DmMailingList_t list );
void AddElementToMailingList( DmMailingList_t list, DmElementHandle_t h );
// Returns false if the mailing list is empty now
bool RemoveElementFromMailingList( DmMailingList_t list, DmElementHandle_t h );
// Returns false if the mailing list is empty now (can happen owing to stale attributes)
bool PostAttributeChanged( DmMailingList_t list, CDmAttribute *pAttribute );
void GetInvalidHandles( CUtlVector< DmElementHandle_t > &handles );
void MarkHandlesValid( CUtlVector< DmElementHandle_t > &handles );
void MarkHandlesInvalid( CUtlVector< DmElementHandle_t > &handles );
// search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
DmElementHandle_t FindOrCreateElementHandle( const DmObjectId_t &id );
// changes an element's id and associated mappings - generally during unserialization
DmElementHandle_t ChangeElementId( DmElementHandle_t hElement, const DmObjectId_t &oldId, const DmObjectId_t &newId );
DmElementReference_t *FindElementReference( DmElementHandle_t hElement, DmObjectId_t **ppId = NULL );
void RemoveUnreferencedElements();
void RemoveElementFromFile( DmElementHandle_t hElement, DmFileId_t fileid );
void AddElementToFile( DmElementHandle_t hElement, DmFileId_t fileid );
void NotifyState( int nNotifyFlags );
int EstimateMemoryOverhead() const;
bool IsCreatingUntypedElements() const { return m_bOnlyCreateUntypedElements; }
unsigned short GetSymbolCount() const;
private:
struct MailingList_t
{
CUtlVector<DmElementHandle_t> m_Elements;
};
struct ElementIdHandlePair_t
{
DmObjectId_t m_id;
DmElementReference_t m_ref;
ElementIdHandlePair_t() {}
explicit ElementIdHandlePair_t( const DmObjectId_t &id ) : m_ref()
{
CopyUniqueId( id, &m_id );
}
ElementIdHandlePair_t( const DmObjectId_t &id, const DmElementReference_t &ref ) : m_ref( ref )
{
CopyUniqueId( id, &m_id );
}
ElementIdHandlePair_t( const ElementIdHandlePair_t& that ) : m_ref( that.m_ref )
{
CopyUniqueId( that.m_id, &m_id );
}
ElementIdHandlePair_t &operator=( const ElementIdHandlePair_t &that )
{
CopyUniqueId( that.m_id, &m_id );
m_ref = that.m_ref;
return *this;
}
static unsigned int HashKey( const ElementIdHandlePair_t& that )
{
return *( unsigned int* )&that.m_id.m_Value;
}
static bool Compare( const ElementIdHandlePair_t& a, const ElementIdHandlePair_t& b )
{
return IsUniqueIdEqual( a.m_id, b.m_id );
}
};
private:
CDmElement *Unserialize( CUtlBuffer& buf );
void Serialize( CDmElement *element, CUtlBuffer& buf );
// Read the header, return the version (or false if it's not a DMX file)
bool ReadDMXHeader( CUtlBuffer &inBuf, DmxHeader_t *pHeader ) const;
const char *GetEncodingFromLegacyFormat( const char *pLegacyFormatName ) const;
bool IsValidNonDMXFormat( const char *pFormatName ) const;
bool IsLegacyFormat( const char *pFormatName ) const;
// Returns the current undo manager
CUndoManager* GetUndoMgr();
const CUndoManager* GetUndoMgr() const;
CClipboardManager *GetClipboardMgr();
const CClipboardManager *GetClipboardMgr() const;
void UnloadFile( DmFileId_t fileid, bool bDeleteElements );
friend class CDmeElementRefHelper;
friend class CDmAttribute;
template< class T > friend class CDmArrayAttributeOp;
void OnElementReferenceAdded ( DmElementHandle_t hElement, CDmAttribute *pAttribute );
void OnElementReferenceRemoved( DmElementHandle_t hElement, CDmAttribute *pAttribute );
void OnElementReferenceAdded ( DmElementHandle_t hElement, bool bRefCount );
void OnElementReferenceRemoved( DmElementHandle_t hElement, bool bRefCount );
private:
CUtlVector< IDmSerializer* > m_Serializers;
CUtlVector< IDmLegacyUpdater* > m_LegacyUpdaters;
CUtlVector< IDmFormatUpdater* > m_FormatUpdaters;
IDmElementFactory *m_pDefaultFactory;
CUtlDict< IDmElementFactory*, int > m_Factories;
CUtlSymbolTable m_SymbolTable;
CUtlHandleTable< CDmElement, 20 > m_Handles;
CUtlHandleTable< CDmAttribute, 20 > m_AttributeHandles;
CUndoManager m_UndoMgr;
CUtlLinkedList< MailingList_t, DmMailingList_t > m_MailingLists;
bool m_bIsUnserializing : 1;
bool m_bUnableToSetDefaultFactory : 1;
bool m_bOnlyCreateUntypedElements : 1;
bool m_bUnableToCreateOnlyUntypedElements : 1;
bool m_bDeleteOrphanedElements : 1;
CUtlHandleTable< FileElementSet_t, 20 > m_openFiles;
CElementIdHash m_elementIds;
CUtlHash< ElementIdHandlePair_t > m_unloadedIdElementMap;
CUtlVector< DmObjectId_t > m_unreferencedElementIds;
CUtlVector< DmElementHandle_t > m_unreferencedElementHandles;
CClipboardManager m_ClipboardMgr;
IElementForKeyValueCallback *m_pKeyvaluesCallbackInterface;
int m_nElementsAllocatedSoFar;
int m_nMaxNumberOfElements;
};
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
extern CDataModel *g_pDataModelImp;
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline CUndoManager* CDataModel::GetUndoMgr()
{
return &m_UndoMgr;
}
inline const CUndoManager* CDataModel::GetUndoMgr() const
{
return &m_UndoMgr;
}
inline void CDataModel::NotifyState( int nNotifyFlags )
{
GetUndoMgr()->NotifyState( nNotifyFlags );
}
inline CClipboardManager *CDataModel::GetClipboardMgr()
{
return &m_ClipboardMgr;
}
inline const CClipboardManager *CDataModel::GetClipboardMgr() const
{
return &m_ClipboardMgr;
}
//-----------------------------------------------------------------------------
// Methods of DmElement which are public to datamodel
//-----------------------------------------------------------------------------
class CDmeElementAccessor
{
public:
static void Purge( CDmElement *pElement ) { pElement->Purge(); }
static void SetId( CDmElement *pElement, const DmObjectId_t &id ) { pElement->SetId( id ); }
static bool IsDirty( const CDmElement *pElement ) { return pElement->IsDirty(); }
static void MarkDirty( CDmElement *pElement, bool dirty = true ) { pElement->MarkDirty( dirty ); }
static void MarkAttributesClean( CDmElement *pElement ) { pElement->MarkAttributesClean(); }
static void MarkBeingUnserialized( CDmElement *pElement, bool beingUnserialized = true ) { pElement->MarkBeingUnserialized( beingUnserialized ); }
static bool IsBeingUnserialized( const CDmElement *pElement ) { return pElement->IsBeingUnserialized(); }
static void AddAttributeByPtr( CDmElement *pElement, CDmAttribute *ptr ) { pElement->AddAttributeByPtr( ptr ); }
static void RemoveAttributeByPtrNoDelete( CDmElement *pElement, CDmAttribute *ptr ) { pElement->RemoveAttributeByPtrNoDelete( ptr); }
static void ChangeHandle( CDmElement *pElement, DmElementHandle_t handle ) { pElement->ChangeHandle( handle ); }
static DmElementReference_t *GetReference( CDmElement *pElement ) { return pElement->GetReference(); }
static void SetReference( CDmElement *pElement, const DmElementReference_t &ref ) { pElement->SetReference( ref ); }
static int EstimateMemoryUsage( CDmElement *pElement, CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ) { return pElement->EstimateMemoryUsage( visited, depth, pCategories ); }
static void PerformConstruction( CDmElement *pElement ) { pElement->PerformConstruction(); }
static void PerformDestruction( CDmElement *pElement ) { pElement->PerformDestruction(); }
};
#endif // DATAMODEL_H

76
datamodel/datamodel.vpc Normal file
View File

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// DATAMODEL.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;DATAMODEL_LIB"
$PrecompiledHeaderFile "$(IntDir)/datamodel.pch"
}
}
$Project "Datamodel"
{
$Folder "Source Files"
{
$File "clipboardmanager.cpp"
$File "datamodel.cpp"
$File "dependencygraph.cpp"
$File "dmattribute.cpp"
$File "dmelement.cpp"
$File "dmelementdictionary.cpp"
$File "dmelementfactoryhelper.cpp"
$File "DmElementFramework.cpp"
$File "dmserializerbinary.cpp"
$File "dmserializerkeyvalues.cpp"
$File "dmserializerkeyvalues2.cpp"
$File "undomanager.cpp"
}
$Folder "Header Files"
{
$File "clipboardmanager.h"
$File "datamodel.h"
$File "dependencygraph.h"
$File "dmattributeinternal.h"
$File "dmelementdictionary.h"
$File "$SRCDIR\public\datamodel\dmelementfactoryhelper.h"
$File "DmElementFramework.h"
$File "$SRCDIR\public\datamodel\dmelementhandle.h"
$File "dmserializerbinary.h"
$File "dmserializerkeyvalues.h"
$File "dmserializerkeyvalues2.h"
$File "undomanager.h"
}
$Folder "external"
{
$File "$SRCDIR\public\tier0\basetypes.h"
$File "$SRCDIR\public\tier0\commonmacros.h"
$File "$SRCDIR\public\tier0\dbg.h"
$File "$SRCDIR\public\tier0\fasttimer.h"
$File "$SRCDIR\public\appframework\IAppSystem.h"
$File "$SRCDIR\public\tier1\interface.h"
$File "$SRCDIR\public\tier0\platform.h"
$File "$SRCDIR\public\tier0\protected_things.h"
$File "$SRCDIR\public\string_t.h"
}
$Folder "Interface"
{
$File "$SRCDIR\public\datamodel\attributeflags.h"
$File "$SRCDIR\public\datamodel\dmattributetypes.h"
$File "$SRCDIR\public\datamodel\dmattributevar.h"
$File "$SRCDIR\public\datamodel\dmelement.h"
$File "$SRCDIR\public\datamodel\dmehandle.h"
$File "$SRCDIR\public\datamodel\idatamodel.h"
}
}

View File

@ -0,0 +1,331 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "dependencygraph.h"
#include "datamodel/idatamodel.h"
#include "datamodel/dmelement.h"
#include "mathlib/mathlib.h" // for swap
#include "datamodel/dmattribute.h"
#include "datamodel/dmattributevar.h"
#include "tier1/mempool.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Misc helper enums and classes for CDependencyGraph class
//-----------------------------------------------------------------------------
enum TraversalState_t
{
TS_NOT_VISITED,
TS_VISITING,
TS_VISITED,
};
struct COperatorNode
{
COperatorNode( IDmeOperator *pOp = NULL ) :
m_state( TS_NOT_VISITED ),
m_operator( pOp ),
m_bInList( false )
{
}
TraversalState_t m_state;
IDmeOperator *m_operator;
CUtlVector< CAttributeNode * > m_OutputAttributes;
bool m_bInList;
};
class CAttributeNode
{
public:
CAttributeNode( CDmAttribute *attribute = NULL ) :
m_attribute( attribute ),
m_bIsOutputToOperator( false )
{
}
CDmAttribute *m_attribute;
CUtlVector< COperatorNode * > m_InputDependentOperators;
bool m_bIsOutputToOperator;
};
CClassMemoryPool< CAttributeNode > g_AttrNodePool( 1000 );
CClassMemoryPool< COperatorNode > g_OperatorNodePool( 1000 );
bool HashEntryCompareFunc( CAttributeNode *const& lhs, CAttributeNode *const& rhs )
{
return lhs->m_attribute == rhs->m_attribute;
}
uint HashEntryKeyFunc( CAttributeNode *const& keyinfo )
{
uint i = (uint)keyinfo->m_attribute;
return i >> 2; // since memory is allocated on a 4-byte (at least!) boundary
}
//-----------------------------------------------------------------------------
// CDependencyGraph constructor - builds dependency graph from operators
//-----------------------------------------------------------------------------
CDependencyGraph::CDependencyGraph() :
m_attrNodes( 4096, 0, 0, HashEntryCompareFunc, HashEntryKeyFunc )
{
}
void CDependencyGraph::Reset( const CUtlVector< IDmeOperator * > &operators )
{
VPROF_BUDGET( "CDependencyGraph::Reset", VPROF_BUDGETGROUP_TOOLS );
Cleanup();
CUtlVector< CDmAttribute * > attrs; // moved outside the loop to function as a temporary memory pool for performance
int on = operators.Count();
CUtlRBTree< IDmeOperator * > operatorDict( 0, on * 2, DefLessFunc(IDmeOperator *) );
for ( int i = 0; i < on; ++i )
{
operatorDict.Insert( operators[i] );
}
m_opNodes.EnsureCapacity( on );
for ( int oi = 0; oi < on; ++oi )
{
IDmeOperator *pOp = operators[ oi ];
Assert( pOp );
if ( pOp == NULL )
continue;
COperatorNode *pOpNode = g_OperatorNodePool.Alloc();
pOpNode->m_operator = pOp;
attrs.RemoveAll();
pOp->GetInputAttributes( attrs );
int an = attrs.Count();
for ( int ai = 0; ai < an; ++ai )
{
CAttributeNode *pAttrNode = FindAttrNode( attrs[ ai ] );
pAttrNode->m_InputDependentOperators.AddToTail( pOpNode );
}
attrs.RemoveAll();
pOp->GetOutputAttributes( attrs );
an = attrs.Count();
for ( int ai = 0; ai < an; ++ai )
{
CAttributeNode *pAttrNode = FindAttrNode( attrs[ ai ] );
pAttrNode->m_bIsOutputToOperator = true;
pOpNode->m_OutputAttributes.AddToTail( pAttrNode );
#ifdef _DEBUG
// Look for dependent operators, add them if they are not in the array
// FIXME: Should this happen for input attributes too?
CDmElement* pElement = pAttrNode->m_attribute->GetOwner();
IDmeOperator *pOperator = dynamic_cast< IDmeOperator* >( pElement );
if ( pOperator )
{
// Look for the operator in the existing list
if ( operatorDict.Find( pOperator ) == operatorDict.InvalidIndex() )
{
CDmElement *pOp1 = dynamic_cast< CDmElement* >( pOperator );
CDmElement *pOp2 = dynamic_cast< CDmElement* >( pOp );
Warning( "Found dependent operator '%s' referenced by operator '%s' that wasn't in the scene or trackgroups!\n", pOp1->GetName(), pOp2->GetName() );
}
}
#endif
}
m_opNodes.AddToTail( pOpNode );
}
}
//-----------------------------------------------------------------------------
// CDependencyGraph destructor - releases temporary opNodes and attrNodes
//-----------------------------------------------------------------------------
CDependencyGraph::~CDependencyGraph()
{
Cleanup();
}
void CDependencyGraph::Cleanup()
{
VPROF_BUDGET( "CDependencyGraph::Cleanup", VPROF_BUDGETGROUP_TOOLS );
int on = m_opNodes.Count();
for ( int oi = 0; oi < on; ++oi )
{
g_OperatorNodePool.Free( m_opNodes[ oi ] );
}
UtlHashHandle_t h = m_attrNodes.GetFirstHandle();
for ( ; h != m_attrNodes.InvalidHandle(); h = m_attrNodes.GetNextHandle( h ) )
{
g_AttrNodePool.Free( m_attrNodes[ h ] );
}
m_opRoots.RemoveAll();
m_opNodes.RemoveAll();
m_attrNodes.RemoveAll();
m_operators.RemoveAll();
}
//-----------------------------------------------------------------------------
// caches changed operators as roots - typically once per frame, every frame
//-----------------------------------------------------------------------------
void CDependencyGraph::FindRoots()
{
m_opRoots.RemoveAll();
uint oi;
uint on = m_opNodes.Count();
for ( oi = 0; oi < on; ++oi )
{
COperatorNode *pOpNode = m_opNodes[ oi ];
pOpNode->m_bInList = false;
pOpNode->m_state = TS_NOT_VISITED;
IDmeOperator *pOp = pOpNode->m_operator;
if ( !pOp->IsDirty() )
continue;
m_opRoots.AddToTail( pOpNode );
pOpNode->m_bInList = true;
}
// Do we have an attribute which is an input to us which is not an output to some other op?
UtlHashHandle_t h = m_attrNodes.GetFirstHandle();
for ( ; h != m_attrNodes.InvalidHandle(); h = m_attrNodes.GetNextHandle( h ) )
{
CAttributeNode *pAttrNode = m_attrNodes[ h ];
//Msg( "attrib %s %p\n", pAttrNode->m_attribute->GetName(), pAttrNode->m_attribute );
if ( !pAttrNode->m_bIsOutputToOperator &&
pAttrNode->m_attribute->IsFlagSet( FATTRIB_OPERATOR_DIRTY ) )
{
on = pAttrNode->m_InputDependentOperators.Count();
for ( oi = 0; oi < on; ++oi )
{
COperatorNode *pOpNode = pAttrNode->m_InputDependentOperators[ oi ];
if ( !pOpNode->m_bInList )
{
m_opRoots.AddToTail( pOpNode );
pOpNode->m_bInList = true;
}
}
}
pAttrNode->m_attribute->RemoveFlag( FATTRIB_OPERATOR_DIRTY );
}
}
//-----------------------------------------------------------------------------
// returns only the operators that need to be evaluated, sorted by dependencies
//-----------------------------------------------------------------------------
bool CDependencyGraph::CullAndSortOperators()
{
FindRoots();
m_operators.RemoveAll();
bool cycle = GetOperatorOrdering( m_opRoots, m_operators ); // leaves to roots (outputs to inputs)
int on = m_operators.Count();
int oh = on / 2;
for ( int oi = 0; oi < oh; ++oi )
{
V_swap( m_operators[ oi ], m_operators[ on - oi - 1 ] );
}
return cycle;
// return GetOperatorOrdering( m_opLeaves, operators ); // roots to leaves (inputs to outputs)
}
//-----------------------------------------------------------------------------
// GetOperatorOrdering is just a recursive post-order depth-first-search
// since we have two types of nodes, we manually traverse to the opnodes grandchildren, which are opnodes
// (skipping children, which are attrnodes)
// returns true if a cycle found - in this case, an arbitrary link of the cycle will be ignored
//-----------------------------------------------------------------------------
bool CDependencyGraph::GetOperatorOrdering( CUtlVector< COperatorNode * > &pOpNodes, CUtlVector< IDmeOperator * > &operators )
{
bool cycle = false;
uint on = pOpNodes.Count();
for ( uint oi = 0; oi < on; ++oi )
{
COperatorNode *pOpNode = pOpNodes[ oi ];
if ( pOpNode->m_state != TS_NOT_VISITED )
{
if ( pOpNode->m_state == TS_VISITING )
{
cycle = true;
}
continue;
}
pOpNode->m_state = TS_VISITING; // mark as in being visited
// DBG_PrintOperator( pIndent, pOpNode->m_operator );
// leaves to roots (outputs to inputs)
uint an = pOpNode->m_OutputAttributes.Count();
for ( uint ai = 0; ai < an; ++ai )
{
CAttributeNode *pAttrNode = pOpNode->m_OutputAttributes[ ai ];
if ( GetOperatorOrdering( pAttrNode->m_InputDependentOperators, operators ) )
{
cycle = true;
}
}
operators.AddToTail( pOpNode->m_operator );
pOpNode->m_state = TS_VISITED; // mark as done visiting
}
return cycle;
}
//-----------------------------------------------------------------------------
// internal helper method - finds attrNode corresponding to pAttr
//-----------------------------------------------------------------------------
CAttributeNode *CDependencyGraph::FindAttrNode( CDmAttribute *pAttr )
{
VPROF_BUDGET( "CDependencyGraph::FindAttrNode", VPROF_BUDGETGROUP_TOOLS );
Assert( pAttr );
CAttributeNode search( pAttr );
UtlHashHandle_t idx = m_attrNodes.Find( &search );
if ( idx != m_attrNodes.InvalidHandle() )
{
return m_attrNodes.Element( idx );
}
CAttributeNode *pAttrNode = 0;
{
VPROF( "CDependencyGraph::FindAttrNode_Alloc" );
pAttrNode = g_AttrNodePool.Alloc();
pAttrNode->m_attribute = pAttr;
}
{
VPROF( "CDependencyGraph::FindAttrNode_Alloc2" );
m_attrNodes.Insert( pAttrNode );
}
return pAttrNode;
}
//-----------------------------------------------------------------------------
// temporary internal debugging function
//-----------------------------------------------------------------------------
void CDependencyGraph::DBG_PrintOperator( const char *pIndent, IDmeOperator *pOp )
{
CDmElement *pElement = dynamic_cast< CDmElement* >( pOp );
Msg( "%s%s <%s> {\n", pIndent, pElement->GetName(), pElement->GetTypeString() );
}

View File

@ -0,0 +1,62 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DEPENDENCYGRAPH_H
#define DEPENDENCYGRAPH_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
#include "tier1/utlhash.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CDmAttribute;
class IDmeOperator;
struct COperatorNode;
class CAttributeNode;
//-----------------------------------------------------------------------------
// CDependencyGraph class - sorts operators based upon the input/output graph
//-----------------------------------------------------------------------------
class CDependencyGraph
{
public:
CDependencyGraph();
~CDependencyGraph();
void Reset( const CUtlVector< IDmeOperator * > &operators );
// caches only the operators that need to be evaluated, sorted by dependencies
// returns true if a cycle found - in this case, an arbitrary link of the cycle will be ignored
bool CullAndSortOperators();
const CUtlVector< IDmeOperator* > &GetSortedOperators() const { return m_operators; }
private:
static bool GetOperatorOrdering( CUtlVector< COperatorNode* > &pOpNodes, CUtlVector< IDmeOperator * > &operators );
static void DBG_PrintOperator( const char *pIndent, IDmeOperator *pOp );
friend class CDmElementFramework;
void Cleanup();
void FindRoots();
CAttributeNode *FindAttrNode( CDmAttribute *pAttr );
CUtlVector< COperatorNode* > m_opRoots;
// CUtlVector< COperatorNode* > m_opLeaves;
CUtlVector< COperatorNode* > m_opNodes;
CUtlHash< CAttributeNode* > m_attrNodes;
CUtlVector< IDmeOperator* > m_operators;
};
#endif // DEPENDENCYGRAPH_H

3265
datamodel/dmattribute.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DMATTRIBUTEINTERNAL_H
#define DMATTRIBUTEINTERNAL_H
#ifdef _WIN32
#pragma once
#endif
#include "datamodel/dmattribute.h"
#include "wchar.h"
//-----------------------------------------------------------------------------
// Forward declarations:
//-----------------------------------------------------------------------------
class IDataModelFactory;
class CUtlBuffer;
class Vector;
class Color;
class CUtlCharConversion;
class CDmElement;
//-----------------------------------------------------------------------------
// Utility class to allow datamodel objects to access private members of CDmAttribute
//-----------------------------------------------------------------------------
class CDmAttributeAccessor
{
public:
static void OnChanged( CDmAttribute *pAttribute, bool bArrayCountChanged = false, bool bIsTopological = false )
{
pAttribute->OnChanged( bArrayCountChanged, bIsTopological );
}
static void DestroyAttribute( CDmAttribute *pOldAttribute )
{
CDmAttribute::DestroyAttribute( pOldAttribute );
}
static bool MarkDirty( CDmAttribute *pAttribute )
{
return pAttribute->MarkDirty();
}
};
//-----------------------------------------------------------------------------
// For serialization, set the delimiter rules
//-----------------------------------------------------------------------------
void SetSerializationDelimiter( CUtlCharConversion *pConv );
void SetSerializationArrayDelimiter( const char *pDelimiter );
//-----------------------------------------------------------------------------
// Skip unserialization for an attribute type (unserialize into a dummy variable)
//-----------------------------------------------------------------------------
bool SkipUnserialize( CUtlBuffer &buf, DmAttributeType_t type );
//-----------------------------------------------------------------------------
// Attribute names/types
//-----------------------------------------------------------------------------
const char *AttributeTypeName( DmAttributeType_t type );
DmAttributeType_t AttributeType( const char *pAttributeType );
//-----------------------------------------------------------------------------
// returns the number of attributes currently allocated
//-----------------------------------------------------------------------------
int GetAllocatedAttributeCount();
#endif // DMATTRIBUTEINTERNAL_H

1420
datamodel/dmelement.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,468 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "dmelementdictionary.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmattribute.h"
#include "datamodel/dmattributevar.h"
#include "datamodel/idatamodel.h"
#include "datamodel.h"
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CDmElementDictionary::CDmElementDictionary()
: m_idmap( 1024, 0, 0, DmIdPair_t::Compare, DmIdPair_t::HashKey )
{
}
//-----------------------------------------------------------------------------
// Clears the dictionary
//-----------------------------------------------------------------------------
void CDmElementDictionary::Clear()
{
m_Dict.Purge();
m_Attributes.Purge();
m_ArrayAttributes.Purge();
m_elementsToDelete.Purge();
}
//-----------------------------------------------------------------------------
// Inserts an element into the table
//-----------------------------------------------------------------------------
DmElementDictHandle_t CDmElementDictionary::InsertElement( CDmElement *pElement )
{
// Insert it into the reconnection table
return m_Dict.AddToTail( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID );
}
//-----------------------------------------------------------------------------
// Returns a particular element
//-----------------------------------------------------------------------------
CDmElement *CDmElementDictionary::GetElement( DmElementDictHandle_t handle )
{
if ( handle == ELEMENT_DICT_HANDLE_INVALID )
return NULL;
return g_pDataModel->GetElement( m_Dict[ handle ] );
}
//-----------------------------------------------------------------------------
// Adds an attribute to the fixup list
//-----------------------------------------------------------------------------
void CDmElementDictionary::AddAttribute( CDmAttribute *pAttribute, const DmObjectId_t &objectId )
{
if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
return; // don't add attributes if their element is being deleted
int i = m_Attributes.AddToTail();
m_Attributes[i].m_nType = AT_OBJECTID;
m_Attributes[i].m_pAttribute = pAttribute;
CopyUniqueId( objectId, &m_Attributes[i].m_ObjectId );
}
//-----------------------------------------------------------------------------
// Adds an element of an attribute array to the fixup list
//-----------------------------------------------------------------------------
void CDmElementDictionary::AddArrayAttribute( CDmAttribute *pAttribute, DmElementDictHandle_t hElement )
{
if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
return; // don't add attributes if their element is being deleted
int i = m_ArrayAttributes.AddToTail();
m_ArrayAttributes[i].m_nType = AT_ELEMENT;
m_ArrayAttributes[i].m_pAttribute = pAttribute;
m_ArrayAttributes[i].m_hElement = hElement;
}
void CDmElementDictionary::AddArrayAttribute( CDmAttribute *pAttribute, const DmObjectId_t &objectId )
{
if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
return; // don't add attributes if their element is being deleted
int i = m_ArrayAttributes.AddToTail();
m_ArrayAttributes[i].m_nType = AT_OBJECTID;
m_ArrayAttributes[i].m_pAttribute = pAttribute;
CopyUniqueId( objectId, &m_ArrayAttributes[i].m_ObjectId );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CDmElementDictionary::RemoveAttributeInfosOfElement( AttributeList_t &attributes, DmElementHandle_t hElement )
{
while ( attributes.Count() > 0 && attributes.Tail().m_pAttribute->GetOwner()->GetHandle() == hElement )
{
attributes.Remove( attributes.Count() - 1 );
}
}
DmElementHandle_t CDmElementDictionary::SetElementId( DmElementDictHandle_t hDictHandle, const DmObjectId_t &newId, DmConflictResolution_t idConflictResolution )
{
DmElementHandle_t hElement = m_Dict[ hDictHandle ];
CDmElement *pElement = g_pDataModel->GetElement( hElement );
Assert( pElement );
if ( !pElement )
return DMELEMENT_HANDLE_INVALID;
const DmObjectId_t &oldId = pElement->GetId();
if ( idConflictResolution == CR_FORCE_COPY )
{
m_idmap.Insert( DmIdPair_t( newId, oldId ) ); // map the newId back to the old id, and keep the old id
return hElement;
}
DmElementHandle_t newHandle = g_pDataModelImp->ChangeElementId( hElement, oldId, newId );
if ( newHandle != DMELEMENT_HANDLE_INVALID )
{
// if ChangeElementId returns a handle, the id has been changed
if ( newHandle != hElement )
{
int i = m_Dict.Find( hElement );
if ( i != m_Dict.InvalidIndex() )
{
m_Dict[ i ] = newHandle;
}
}
return newHandle; // either keeping the old handle, with the new id, or found a new handle associated with that new id
}
// id not changed because that id is already in use
if ( idConflictResolution == CR_DELETE_NEW )
{
DmElementHandle_t hExistingElement = g_pDataModel->FindElement( newId );
int i = m_elementsToDelete.AddToTail( );
m_elementsToDelete[i].m_hDictHandle = hDictHandle;
m_elementsToDelete[i].m_hElementToDelete = hElement;
m_elementsToDelete[i].m_hReplacementElement = hExistingElement;
// remove all element ref attributes read in before the id (typically none)
RemoveAttributeInfosOfElement( m_Attributes, hElement );
RemoveAttributeInfosOfElement( m_ArrayAttributes, hElement );
return DMELEMENT_HANDLE_INVALID;
}
if ( idConflictResolution == CR_DELETE_OLD )
{
DmElementHandle_t hExistingElement = g_pDataModel->FindElement( newId );
Assert( hExistingElement != DMELEMENT_HANDLE_INVALID );
if ( hExistingElement == DMELEMENT_HANDLE_INVALID )
return DMELEMENT_HANDLE_INVALID; // unexpected error in ChangeElementId (failed due to something other than a conflict)
g_pDataModelImp->DeleteElement( hExistingElement, HR_NEVER ); // need to keep the handle around until ChangeElemendId
newHandle = g_pDataModelImp->ChangeElementId( hElement, oldId, newId );
Assert( newHandle == hExistingElement );
int i = m_Dict.Find( hElement );
if ( i != m_Dict.InvalidIndex() )
{
m_Dict[ i ] = newHandle;
}
return newHandle;
}
if ( idConflictResolution == CR_COPY_NEW )
{
m_idmap.Insert( DmIdPair_t( newId, oldId ) ); // map the newId back to the old id, and keep the old id
return hElement;
}
Assert( 0 );
return DMELEMENT_HANDLE_INVALID;
}
//-----------------------------------------------------------------------------
// Finds an element into the table
//-----------------------------------------------------------------------------
DmElementDictHandle_t CDmElementDictionary::FindElement( CDmElement *pElement )
{
return m_Dict.Find( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID );
}
//-----------------------------------------------------------------------------
// Hook up all element references (which were unserialized as object ids)
//-----------------------------------------------------------------------------
void CDmElementDictionary::HookUpElementAttributes()
{
int n = m_Attributes.Count();
for ( int i = 0; i < n; ++i )
{
Assert( m_Attributes[i].m_pAttribute->GetType() == AT_ELEMENT );
Assert( m_Attributes[i].m_nType == AT_OBJECTID );
UtlHashHandle_t h = m_idmap.Find( DmIdPair_t( m_Attributes[i].m_ObjectId ) );
DmObjectId_t &id = h == m_idmap.InvalidHandle() ? m_Attributes[i].m_ObjectId : m_idmap[ h ].m_newId;
// search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
DmElementHandle_t hElement = g_pDataModelImp->FindOrCreateElementHandle( id );
m_Attributes[i].m_pAttribute->SetValue<DmElementHandle_t>( hElement );
}
}
//-----------------------------------------------------------------------------
// Hook up all element array references
//-----------------------------------------------------------------------------
void CDmElementDictionary::HookUpElementArrayAttributes()
{
// Find unique array attributes; we need to clear them all before adding stuff.
// This clears them of stuff added during their construction phase.
int n = m_ArrayAttributes.Count();
CUtlRBTree< CDmAttribute*, unsigned short > lookup( 0, n, DefLessFunc(CDmAttribute*) );
for ( int i = 0; i < n; ++i )
{
Assert( m_ArrayAttributes[i].m_pAttribute->GetType() == AT_ELEMENT_ARRAY );
CDmAttribute *pElementArray = m_ArrayAttributes[i].m_pAttribute;
CDmrElementArray<> array( pElementArray );
if ( lookup.Find( pElementArray ) == lookup.InvalidIndex() )
{
array.RemoveAll();
lookup.Insert( pElementArray );
}
}
for ( int i = 0; i < n; ++i )
{
Assert( m_ArrayAttributes[i].m_pAttribute->GetType() == AT_ELEMENT_ARRAY );
CDmrElementArray<> array( m_ArrayAttributes[i].m_pAttribute );
if ( m_ArrayAttributes[i].m_nType == AT_ELEMENT )
{
CDmElement *pElement = GetElement( m_ArrayAttributes[i].m_hElement );
array.AddToTail( pElement );
}
else
{
UtlHashHandle_t h = m_idmap.Find( DmIdPair_t( m_ArrayAttributes[i].m_ObjectId ) );
DmObjectId_t &id = ( h == m_idmap.InvalidHandle() ) ? m_ArrayAttributes[i].m_ObjectId : m_idmap[ h ].m_newId;
// search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
DmElementHandle_t hElement = g_pDataModelImp->FindOrCreateElementHandle( id );
int nIndex = array.AddToTail();
array.SetHandle( nIndex, hElement );
}
}
}
//-----------------------------------------------------------------------------
// Hook up all element references (which were unserialized as object ids)
//-----------------------------------------------------------------------------
void CDmElementDictionary::HookUpElementReferences()
{
int nElementsToDelete = m_elementsToDelete.Count();
for ( int i = 0; i < nElementsToDelete; ++i )
{
DmElementDictHandle_t hDictIndex = m_elementsToDelete[i].m_hDictHandle;
DmElementHandle_t hElement = m_Dict[ hDictIndex ];
g_pDataModelImp->DeleteElement( hElement );
m_Dict[ hDictIndex ] = m_elementsToDelete[i].m_hReplacementElement;
}
HookUpElementArrayAttributes();
HookUpElementAttributes();
}
//-----------------------------------------------------------------------------
//
// Element dictionary used in serialization
//
//-----------------------------------------------------------------------------
CDmElementSerializationDictionary::CDmElementSerializationDictionary() :
m_Dict( 1024, 0, CDmElementSerializationDictionary::LessFunc )
{
}
//-----------------------------------------------------------------------------
// Used to sort the list of elements
//-----------------------------------------------------------------------------
bool CDmElementSerializationDictionary::LessFunc( const ElementInfo_t &lhs, const ElementInfo_t &rhs )
{
return lhs.m_pElement < rhs.m_pElement;
}
//-----------------------------------------------------------------------------
// Finds the handle of the element
//-----------------------------------------------------------------------------
DmElementDictHandle_t CDmElementSerializationDictionary::Find( CDmElement *pElement )
{
ElementInfo_t find;
find.m_pElement = pElement;
return m_Dict.Find( find );
}
//-----------------------------------------------------------------------------
// Creates the list of all things to serialize
//-----------------------------------------------------------------------------
void CDmElementSerializationDictionary::BuildElementList_R( CDmElement *pElement, bool bFlatMode, bool bIsRoot )
{
if ( !pElement )
return;
// FIXME: Right here we should ask the element if it's an external
// file reference and exit immediately if so.
// This means we've already encountered this guy.
// Therefore, he can never be a root element
DmElementDictHandle_t h = Find( pElement );
if ( h != m_Dict.InvalidIndex() )
{
m_Dict[h].m_bRoot = true;
return;
}
ElementInfo_t info;
info.m_bRoot = bFlatMode || bIsRoot;
info.m_pElement = pElement;
m_Dict.Insert( info );
for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
{
if ( pAttribute->IsFlagSet( FATTRIB_DONTSAVE ) )
continue;
switch( pAttribute->GetType() )
{
case AT_ELEMENT:
{
CDmElement *pChild = pAttribute->GetValueElement<CDmElement>();
if ( !pChild || pChild->GetFileId() != pElement->GetFileId() )
break;
BuildElementList_R( pChild, bFlatMode, false );
}
break;
case AT_ELEMENT_ARRAY:
{
CDmrElementArray<> array( pAttribute );
int nCount = array.Count();
for ( int i = 0; i < nCount; ++i )
{
CDmElement *pChild = array[i];
if ( !pChild || pChild->GetFileId() != pElement->GetFileId() )
break;
BuildElementList_R( pChild, bFlatMode, false );
}
}
break;
}
}
}
void CDmElementSerializationDictionary::BuildElementList( CDmElement *pElement, bool bFlatMode )
{
BuildElementList_R( pElement, bFlatMode, true );
}
//-----------------------------------------------------------------------------
// Should I inline the serialization of this element?
//-----------------------------------------------------------------------------
bool CDmElementSerializationDictionary::ShouldInlineElement( CDmElement *pElement )
{
// This means we've already encountered this guy.
// Therefore, he can never be a root element
DmElementDictHandle_t h = Find( pElement );
if ( h != m_Dict.InvalidIndex() )
return !m_Dict[h].m_bRoot;
// If we didn't find the element, it means it's a reference to an external
// element (or it's NULL), so don't inline ie.
return false;
}
//-----------------------------------------------------------------------------
// Clears the dictionary
//-----------------------------------------------------------------------------
void CDmElementSerializationDictionary::Clear()
{
m_Dict.RemoveAll();
}
//-----------------------------------------------------------------------------
// How many root elements do we have?
//-----------------------------------------------------------------------------
int CDmElementSerializationDictionary::RootElementCount() const
{
int nCount = 0;
DmElementDictHandle_t h = m_Dict.FirstInorder();
while( h != m_Dict.InvalidIndex() )
{
if ( m_Dict[h].m_bRoot )
{
++nCount;
}
h = m_Dict.NextInorder( h );
}
return nCount;
}
//-----------------------------------------------------------------------------
// Iterates over all root elements to serialize
//-----------------------------------------------------------------------------
DmElementDictHandle_t CDmElementSerializationDictionary::FirstRootElement() const
{
// NOTE - this code only works with BlockMemory or Memory (NOT FixedMemory)
// NOTE: I don't have to use First/NextInorder here because there
// are guaranteed to be no removals from the dictionary.
// Also, using inorder traversal won't get my actual root element to be first in the file
int nCount = m_Dict.Count();
for ( DmElementDictHandle_t h = 0; h < nCount; ++h )
{
if ( m_Dict[h].m_bRoot )
return h;
}
return ELEMENT_DICT_HANDLE_INVALID;
}
DmElementDictHandle_t CDmElementSerializationDictionary::NextRootElement( DmElementDictHandle_t h ) const
{
// NOTE - this code only works with BlockMemory or Memory (NOT FixedMemory)
// NOTE: I don't have to use First/NextInorder here because there
// are guaranteed to be no removals from the dictionary.
// Also, using inorder traversal won't get my actual root element to be first in the file
++h;
int nCount = m_Dict.Count();
for ( ; h < nCount; ++h )
{
if ( m_Dict[h].m_bRoot )
return h;
}
return ELEMENT_DICT_HANDLE_INVALID;
}
CDmElement *CDmElementSerializationDictionary::GetRootElement( DmElementDictHandle_t h )
{
Assert( m_Dict[h].m_bRoot );
return m_Dict[h].m_pElement;
}

View File

@ -0,0 +1,184 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DMELEMENTDICTIONARY_H
#define DMELEMENTDICTIONARY_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
#include "datamodel/idatamodel.h"
#include "datamodel/dmattribute.h"
#include "tier1/utlrbtree.h"
#include "tier1/utlhash.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CDmElement;
class CDmAttribute;
//-----------------------------------------------------------------------------
// Element dictionary used in unserialization
//-----------------------------------------------------------------------------
typedef int DmElementDictHandle_t;
enum
{
ELEMENT_DICT_HANDLE_INVALID = (DmElementDictHandle_t)~0
};
class CDmElementDictionary
{
public:
CDmElementDictionary();
DmElementDictHandle_t InsertElement( CDmElement *pElement );
CDmElement *GetElement( DmElementDictHandle_t handle );
void AddAttribute( CDmAttribute *pAttribute, const DmObjectId_t &pElementId );
void AddArrayAttribute( CDmAttribute *pAttribute, DmElementDictHandle_t hChild );
void AddArrayAttribute( CDmAttribute *pAttribute, const DmObjectId_t &pElementId );
DmElementHandle_t SetElementId( DmElementDictHandle_t hDictHandle,
const DmObjectId_t &newId,
DmConflictResolution_t idConflictResolution );
// Finds an element into the table
DmElementDictHandle_t FindElement( CDmElement *pElement );
// Hook up all element references (which were unserialized as object ids)
void HookUpElementReferences();
// Clears the dictionary
void Clear();
// iteration through elements
DmElementDictHandle_t FirstElement() { return 0; }
DmElementDictHandle_t NextElement( DmElementDictHandle_t h )
{
return m_Dict.IsValidIndex( h+1 ) ? h+1 : ELEMENT_DICT_HANDLE_INVALID;
}
private:
struct AttributeInfo_t
{
CDmAttribute *m_pAttribute;
int m_nType; // AT_ELEMENT or AT_OBJECTID
union
{
DmElementDictHandle_t m_hElement;
DmObjectId_t m_ObjectId;
};
};
typedef CUtlVector<AttributeInfo_t> AttributeList_t;
struct DmIdPair_t
{
DmObjectId_t m_oldId;
DmObjectId_t m_newId;
DmIdPair_t() {}
DmIdPair_t( const DmObjectId_t &id )
{
CopyUniqueId( id, &m_oldId );
}
DmIdPair_t( const DmObjectId_t &oldId, const DmObjectId_t &newId )
{
CopyUniqueId( oldId, &m_oldId );
CopyUniqueId( newId, &m_newId );
}
DmIdPair_t &operator=( const DmIdPair_t &that )
{
CopyUniqueId( that.m_oldId, &m_oldId );
CopyUniqueId( that.m_newId, &m_newId );
return *this;
}
static unsigned int HashKey( const DmIdPair_t& that )
{
return *( unsigned int* )&that.m_oldId.m_Value;
}
static bool Compare( const DmIdPair_t& a, const DmIdPair_t& b )
{
return IsUniqueIdEqual( a.m_oldId, b.m_oldId );
}
};
struct DeletionInfo_t
{
DeletionInfo_t() {}
DeletionInfo_t( DmElementHandle_t hElement ) : m_hElementToDelete( hElement ) {}
bool operator==( const DeletionInfo_t& src ) const { return m_hElementToDelete == src.m_hElementToDelete; }
DmElementDictHandle_t m_hDictHandle;
DmElementHandle_t m_hElementToDelete;
DmElementHandle_t m_hReplacementElement;
};
// Hook up all element references (which were unserialized as object ids)
void HookUpElementAttributes();
void HookUpElementArrayAttributes();
void RemoveAttributeInfosOfElement( AttributeList_t &attributes, DmElementHandle_t hElement );
CUtlVector< DmElementHandle_t > m_Dict;
AttributeList_t m_Attributes;
AttributeList_t m_ArrayAttributes;
CUtlVector< DeletionInfo_t > m_elementsToDelete;
CUtlHash< DmIdPair_t > m_idmap;
};
//-----------------------------------------------------------------------------
// Element dictionary used in serialization
//-----------------------------------------------------------------------------
class CDmElementSerializationDictionary
{
public:
CDmElementSerializationDictionary();
// Creates the list of all things to serialize
void BuildElementList( CDmElement *pRoot, bool bFlatMode );
// Should I inline the serialization of this element?
bool ShouldInlineElement( CDmElement *pElement );
// Clears the dictionary
void Clear();
// Iterates over all root elements to serialize
DmElementDictHandle_t FirstRootElement() const;
DmElementDictHandle_t NextRootElement( DmElementDictHandle_t h ) const;
CDmElement* GetRootElement( DmElementDictHandle_t h );
// Finds the handle of the element
DmElementDictHandle_t Find( CDmElement *pElement );
// How many root elements do we have?
int RootElementCount() const;
private:
struct ElementInfo_t
{
bool m_bRoot;
CDmElement* m_pElement;
};
// Creates the list of all things to serialize
void BuildElementList_R( CDmElement *pRoot, bool bFlatMode, bool bIsRoot );
static bool LessFunc( const ElementInfo_t &lhs, const ElementInfo_t &rhs );
CUtlBlockRBTree< ElementInfo_t, DmElementDictHandle_t > m_Dict;
};
#endif // DMELEMENTDICTIONARY_H

View File

@ -0,0 +1,100 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "datamodel/dmelementfactoryhelper.h"
#include "tier0/dbg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CDmElementFactoryHelper *CDmElementFactoryHelper::s_pHelpers[2] = { NULL, NULL };
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CDmElementFactoryHelper::CDmElementFactoryHelper( const char *classname, IDmElementFactoryInternal *pFactory, bool bIsStandardFactory )
{
m_pNext = s_pHelpers[bIsStandardFactory];
s_pHelpers[bIsStandardFactory] = this;
// Set attributes
Assert( pFactory );
m_pFactory = pFactory;
Assert( classname );
m_pszClassname = classname;
}
//-----------------------------------------------------------------------------
// Purpose: Returns next object in list
// Output : CDmElementFactoryHelper
//-----------------------------------------------------------------------------
CDmElementFactoryHelper *CDmElementFactoryHelper::GetNext( void )
{
return m_pNext;
}
//-----------------------------------------------------------------------------
// Installs all factories into the datamodel system
//-----------------------------------------------------------------------------
// NOTE: The name of this extern is defined by the macro IMPLEMENT_ELEMENT_FACTORY
extern CDmElementFactoryHelper g_CDmElement_Helper;
void CDmElementFactoryHelper::InstallFactories( )
{
// Just set up the type symbols of the other factories
CDmElementFactoryHelper *p = s_pHelpers[0];
while ( p )
{
// Add factories to database
if ( !p->GetFactory()->IsAbstract() )
{
g_pDataModel->AddElementFactory( p->GetClassname(), p->GetFactory() );
}
// Set up the type symbol. Note this can't be done at
// constructor time since we don't have a DataModel pointer then
p->GetFactory()->SetElementTypeSymbol( g_pDataModel->GetSymbol( p->GetClassname() ) );
p = p->GetNext();
}
p = s_pHelpers[1];
while ( p )
{
// Add factories to database, but not if they've been overridden
if ( !g_pDataModel->HasElementFactory( p->GetClassname() ) )
{
if ( !p->GetFactory()->IsAbstract() )
{
g_pDataModel->AddElementFactory( p->GetClassname(), p->GetFactory() );
}
// Set up the type symbol. Note this can't be done at
// constructor time since we don't have a DataModel pointer then
// Backward compat--don't let the type symbol be 'DmeElement'
if ( Q_stricmp( p->GetClassname(), "DmeElement" ) )
{
p->GetFactory()->SetElementTypeSymbol( g_pDataModel->GetSymbol( p->GetClassname() ) );
}
}
p = p->GetNext();
}
// Also install the DmElement factory as the default factory
g_pDataModel->SetDefaultElementFactory( g_CDmElement_Helper.GetFactory() );
}
//-----------------------------------------------------------------------------
// Installs all DmElement factories
//-----------------------------------------------------------------------------
void InstallDmElementFactories( )
{
CDmElementFactoryHelper::InstallFactories( );
}

View File

@ -0,0 +1,587 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "dmserializerbinary.h"
#include "datamodel/idatamodel.h"
#include "datamodel.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmattributevar.h"
#include "dmattributeinternal.h"
#include "dmelementdictionary.h"
#include "tier1/utlbuffer.h"
#include "DmElementFramework.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CUtlBuffer;
class CBaseSceneObject;
//-----------------------------------------------------------------------------
// special element indices
//-----------------------------------------------------------------------------
enum
{
ELEMENT_INDEX_NULL = -1,
ELEMENT_INDEX_EXTERNAL = -2,
};
//-----------------------------------------------------------------------------
// Serialization class for Binary output
//-----------------------------------------------------------------------------
class CDmSerializerBinary : public IDmSerializer
{
public:
CDmSerializerBinary() {}
// Inherited from IDMSerializer
virtual const char *GetName() const { return "binary"; }
virtual const char *GetDescription() const { return "Binary"; }
virtual bool StoresVersionInFile() const { return true; }
virtual bool IsBinaryFormat() const { return true; }
virtual int GetCurrentVersion() const { return 2; }
virtual bool Serialize( CUtlBuffer &buf, CDmElement *pRoot );
virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
const char *pSourceFormatName, int nFormatVersion,
DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot );
private:
// Methods related to serialization
void SerializeElementIndex( CUtlBuffer& buf, CDmElementSerializationDictionary& list, DmElementHandle_t hElement, DmFileId_t fileid );
void SerializeElementAttribute( CUtlBuffer& buf, CDmElementSerializationDictionary& list, CDmAttribute *pAttribute );
void SerializeElementArrayAttribute( CUtlBuffer& buf, CDmElementSerializationDictionary& list, CDmAttribute *pAttribute );
bool SerializeAttributes( CUtlBuffer& buf, CDmElementSerializationDictionary& list, unsigned short *symbolToIndexMap, CDmElement *pElement );
bool SaveElementDict( CUtlBuffer& buf, unsigned short *symbolToIndexMap, CDmElement *pElement );
bool SaveElement( CUtlBuffer& buf, CDmElementSerializationDictionary& dict, unsigned short *symbolToIndexMap, CDmElement *pElement);
// Methods related to unserialization
DmElementHandle_t UnserializeElementIndex( CUtlBuffer &buf, CUtlVector<CDmElement*> &elementList );
void UnserializeElementAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, CUtlVector<CDmElement*> &elementList );
void UnserializeElementArrayAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, CUtlVector<CDmElement*> &elementList );
bool UnserializeAttributes( CUtlBuffer &buf, CDmElement *pElement, CUtlVector<CDmElement*> &elementList, UtlSymId_t *symbolTable );
bool UnserializeElements( CUtlBuffer &buf, DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot, UtlSymId_t *symbolTable );
};
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CDmSerializerBinary s_DMSerializerBinary;
void InstallBinarySerializer( IDataModel *pFactory )
{
pFactory->AddSerializer( &s_DMSerializerBinary );
}
//-----------------------------------------------------------------------------
// Write out the index of the element to avoid looks at read time
//-----------------------------------------------------------------------------
void CDmSerializerBinary::SerializeElementIndex( CUtlBuffer& buf, CDmElementSerializationDictionary& list, DmElementHandle_t hElement, DmFileId_t fileid )
{
if ( hElement == DMELEMENT_HANDLE_INVALID )
{
buf.PutInt( ELEMENT_INDEX_NULL ); // invalid handle
}
else
{
CDmElement *pElement = g_pDataModel->GetElement( hElement );
if ( pElement )
{
if ( pElement->GetFileId() == fileid )
{
buf.PutInt( list.Find( pElement ) );
}
else
{
buf.PutInt( ELEMENT_INDEX_EXTERNAL );
char idstr[ 40 ];
UniqueIdToString( pElement->GetId(), idstr, sizeof( idstr ) );
buf.PutString( idstr );
}
}
else
{
DmObjectId_t *pId = NULL;
DmElementReference_t *pRef = g_pDataModelImp->FindElementReference( hElement, &pId );
if ( pRef && pId )
{
buf.PutInt( ELEMENT_INDEX_EXTERNAL );
char idstr[ 40 ];
UniqueIdToString( *pId, idstr, sizeof( idstr ) );
buf.PutString( idstr );
}
else
{
buf.PutInt( ELEMENT_INDEX_NULL ); // invalid handle
}
}
}
}
//-----------------------------------------------------------------------------
// Writes out element attributes
//-----------------------------------------------------------------------------
void CDmSerializerBinary::SerializeElementAttribute( CUtlBuffer& buf, CDmElementSerializationDictionary& list, CDmAttribute *pAttribute )
{
SerializeElementIndex( buf, list, pAttribute->GetValue< DmElementHandle_t >(), pAttribute->GetOwner()->GetFileId() );
}
//-----------------------------------------------------------------------------
// Writes out element array attributes
//-----------------------------------------------------------------------------
void CDmSerializerBinary::SerializeElementArrayAttribute( CUtlBuffer& buf, CDmElementSerializationDictionary& list, CDmAttribute *pAttribute )
{
DmFileId_t fileid = pAttribute->GetOwner()->GetFileId();
CDmrElementArray<> vec( pAttribute );
int nCount = vec.Count();
buf.PutInt( nCount );
for ( int i = 0; i < nCount; ++i )
{
SerializeElementIndex( buf, list, vec.GetHandle(i), fileid );
}
}
//-----------------------------------------------------------------------------
// Writes out all attributes
//-----------------------------------------------------------------------------
bool CDmSerializerBinary::SerializeAttributes( CUtlBuffer& buf, CDmElementSerializationDictionary& list, unsigned short *symbolToIndexMap, CDmElement *pElement )
{
// Collect the attributes to be written
CDmAttribute **ppAttributes = ( CDmAttribute** )_alloca( pElement->AttributeCount() * sizeof( CDmAttribute* ) );
int nAttributes = 0;
for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
{
if ( pAttribute->IsFlagSet( FATTRIB_DONTSAVE | FATTRIB_STANDARD ) )
continue;
ppAttributes[ nAttributes++ ] = pAttribute;
}
// Now write them all out in reverse order, since FirstAttribute is actually the *last* attribute for perf reasons
buf.PutInt( nAttributes );
for ( int i = nAttributes - 1; i >= 0; --i )
{
CDmAttribute *pAttribute = ppAttributes[ i ];
Assert( pAttribute );
buf.PutShort( symbolToIndexMap[ pAttribute->GetNameSymbol() ] );
buf.PutChar( pAttribute->GetType() );
switch( pAttribute->GetType() )
{
default:
pAttribute->Serialize( buf );
break;
case AT_ELEMENT:
SerializeElementAttribute( buf, list, pAttribute );
break;
case AT_ELEMENT_ARRAY:
SerializeElementArrayAttribute( buf, list, pAttribute );
break;
}
}
return buf.IsValid();
}
bool CDmSerializerBinary::SaveElement( CUtlBuffer& buf, CDmElementSerializationDictionary& list, unsigned short *symbolToIndexMap, CDmElement *pElement )
{
SerializeAttributes( buf, list, symbolToIndexMap, pElement );
return buf.IsValid();
}
bool CDmSerializerBinary::SaveElementDict( CUtlBuffer& buf, unsigned short *symbolToIndexMap, CDmElement *pElement )
{
buf.PutShort( symbolToIndexMap[ pElement->GetType() ] );
buf.PutString( pElement->GetName() );
buf.Put( &pElement->GetId(), sizeof(DmObjectId_t) );
return buf.IsValid();
}
void MarkSymbol( UtlSymId_t *indexToSymbolMap, unsigned short *symbolToIndexMap, unsigned short &nSymbols, UtlSymId_t sym )
{
if ( symbolToIndexMap[ sym ] != 0xffff )
return;
symbolToIndexMap[ sym ] = nSymbols;
indexToSymbolMap[ nSymbols ] = sym;
nSymbols++;
}
void MarkSymbols( UtlSymId_t *indexToSymbolMap, unsigned short *symbolToIndexMap, unsigned short &nSymbols, CDmElement *pElement )
{
MarkSymbol( indexToSymbolMap, symbolToIndexMap, nSymbols, pElement->GetType() );
for ( CDmAttribute *pAttr = pElement->FirstAttribute(); pAttr; pAttr = pAttr->NextAttribute() )
{
MarkSymbol( indexToSymbolMap, symbolToIndexMap, nSymbols, pAttr->GetNameSymbol() );
}
}
bool CDmSerializerBinary::Serialize( CUtlBuffer &outBuf, CDmElement *pRoot )
{
// Save elements, attribute links
CDmElementSerializationDictionary dict;
dict.BuildElementList( pRoot, true );
// TODO - consider allowing dmxconvert to skip the collection step, since the only datamodel symbols will be the ones from the file
unsigned short nTotalSymbols = g_pDataModelImp->GetSymbolCount();
UtlSymId_t *indexToSymbolMap = ( UtlSymId_t * )stackalloc( nTotalSymbols * sizeof( UtlSymId_t ) );
unsigned short *symbolToIndexMap = ( unsigned short* )stackalloc( nTotalSymbols * sizeof( unsigned short ) );
V_memset( indexToSymbolMap, 0xff, nTotalSymbols * sizeof( UtlSymId_t ) );
V_memset( symbolToIndexMap, 0xff, nTotalSymbols * sizeof( unsigned short ) );
// collect list of attribute names and element types into string table
unsigned short nUsedSymbols = 0;
DmElementDictHandle_t i;
for ( i = dict.FirstRootElement(); i != ELEMENT_DICT_HANDLE_INVALID; i = dict.NextRootElement(i) )
{
MarkSymbols( indexToSymbolMap, symbolToIndexMap, nUsedSymbols, dict.GetRootElement( i ) );
}
Assert( nUsedSymbols <= nTotalSymbols );
// write out the symbol table for this file (may be significantly smaller than datamodel's full symbol table)
outBuf.PutShort( nUsedSymbols );
for ( int si = 0; si < nUsedSymbols; ++si )
{
UtlSymId_t sym = indexToSymbolMap[ si ];
const char *pStr = g_pDataModel->GetString( sym );
outBuf.PutString( pStr );
}
// First write out the dictionary of all elements (to avoid later stitching up in unserialize)
outBuf.PutInt( dict.RootElementCount() );
for ( i = dict.FirstRootElement(); i != ELEMENT_DICT_HANDLE_INVALID; i = dict.NextRootElement(i) )
{
SaveElementDict( outBuf, symbolToIndexMap, dict.GetRootElement( i ) );
}
// Now write out the attributes of each of those elements
for ( i = dict.FirstRootElement(); i != ELEMENT_DICT_HANDLE_INVALID; i = dict.NextRootElement(i) )
{
SaveElement( outBuf, dict, symbolToIndexMap, dict.GetRootElement( i ) );
}
return true;
}
//-----------------------------------------------------------------------------
// Reads an element index and converts it to a handle (local or external)
//-----------------------------------------------------------------------------
DmElementHandle_t CDmSerializerBinary::UnserializeElementIndex( CUtlBuffer &buf, CUtlVector<CDmElement*> &elementList )
{
int nElementIndex = buf.GetInt();
Assert( nElementIndex < elementList.Count() );
if ( nElementIndex == ELEMENT_INDEX_EXTERNAL )
{
char idstr[ 40 ];
buf.GetString( idstr );
DmObjectId_t id;
UniqueIdFromString( &id, idstr, sizeof( idstr ) );
return g_pDataModelImp->FindOrCreateElementHandle( id );
}
Assert( nElementIndex >= 0 || nElementIndex == ELEMENT_INDEX_NULL );
if ( nElementIndex < 0 || !elementList[ nElementIndex ] )
return DMELEMENT_HANDLE_INVALID;
return elementList[ nElementIndex ]->GetHandle();
}
//-----------------------------------------------------------------------------
// Reads an element attribute
//-----------------------------------------------------------------------------
void CDmSerializerBinary::UnserializeElementAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, CUtlVector<CDmElement*> &elementList )
{
DmElementHandle_t hElement = UnserializeElementIndex( buf, elementList );
if ( !pAttribute )
return;
pAttribute->SetValue( hElement );
}
//-----------------------------------------------------------------------------
// Reads an element array attribute
//-----------------------------------------------------------------------------
void CDmSerializerBinary::UnserializeElementArrayAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, CUtlVector<CDmElement*> &elementList )
{
int nElementCount = buf.GetInt();
if ( !pAttribute || pAttribute->GetType() != AT_ELEMENT_ARRAY )
{
// Parse past the data
for ( int i = 0; i < nElementCount; ++i )
{
UnserializeElementIndex( buf, elementList );
}
return;
}
CDmrElementArray<> array( pAttribute );
array.RemoveAll();
array.EnsureCapacity( nElementCount );
for ( int i = 0; i < nElementCount; ++i )
{
DmElementHandle_t hElement = UnserializeElementIndex( buf, elementList );
array.AddToTail( hElement );
}
}
//-----------------------------------------------------------------------------
// Reads a single element
//-----------------------------------------------------------------------------
bool CDmSerializerBinary::UnserializeAttributes( CUtlBuffer &buf, CDmElement *pElement, CUtlVector<CDmElement*> &elementList, UtlSymId_t *symbolTable )
{
char nameBuf[ 1024 ];
int nAttributeCount = buf.GetInt();
for ( int i = 0; i < nAttributeCount; ++i )
{
const char *pName = NULL;
if ( symbolTable )
{
unsigned short nName = buf.GetShort();
pName = g_pDataModel->GetString( symbolTable[ nName ] );
}
else
{
buf.GetString( nameBuf );
pName = nameBuf;
}
DmAttributeType_t nAttributeType = (DmAttributeType_t)buf.GetChar();
Assert( pName != NULL && pName[ 0 ] != '\0' );
Assert( nAttributeType != AT_UNKNOWN );
CDmAttribute *pAttribute = pElement ? pElement->AddAttribute( pName, nAttributeType ) : NULL;
if ( pElement && !pAttribute )
{
Warning("Dm: Attempted to read an attribute (\"%s\") of an inappropriate type!\n", pName );
return false;
}
switch( nAttributeType )
{
default:
if ( !pAttribute )
{
SkipUnserialize( buf, nAttributeType );
}
else
{
pAttribute->Unserialize( buf );
}
break;
case AT_ELEMENT:
UnserializeElementAttribute( buf, pAttribute, elementList );
break;
case AT_ELEMENT_ARRAY:
UnserializeElementArrayAttribute( buf, pAttribute, elementList );
break;
}
}
return buf.IsValid();
}
struct DmIdPair_t
{
DmObjectId_t m_oldId;
DmObjectId_t m_newId;
DmIdPair_t &operator=( const DmIdPair_t &that )
{
CopyUniqueId( that.m_oldId, &m_oldId );
CopyUniqueId( that.m_newId, &m_newId );
return *this;
}
static unsigned int HashKey( const DmIdPair_t& that )
{
return *( unsigned int* )&that.m_oldId.m_Value;
}
static bool Compare( const DmIdPair_t& a, const DmIdPair_t& b )
{
return IsUniqueIdEqual( a.m_oldId, b.m_oldId );
}
};
DmElementHandle_t CreateElementWithFallback( const char *pType, const char *pName, DmFileId_t fileid, const DmObjectId_t &id )
{
DmElementHandle_t hElement = g_pDataModel->CreateElement( pType, pName, fileid, &id );
if ( hElement == DMELEMENT_HANDLE_INVALID )
{
Warning("Binary: Element uses unknown element type %s\n", pType );
hElement = g_pDataModel->CreateElement( "DmElement", pName, fileid, &id );
Assert( hElement != DMELEMENT_HANDLE_INVALID );
}
return hElement;
}
//-----------------------------------------------------------------------------
// Main entry point for the unserialization
//-----------------------------------------------------------------------------
bool CDmSerializerBinary::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
const char *pSourceFormatName, int nSourceFormatVersion,
DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
{
Assert( !V_stricmp( pEncodingName, GetName() ) );
if ( V_stricmp( pEncodingName, GetName() ) != 0 )
return false;
Assert( nEncodingVersion >= 0 && nEncodingVersion <= 2 );
if ( nEncodingVersion < 0 || nEncodingVersion > 2 )
return false;
bool bReadSymbolTable = nEncodingVersion >= 2;
// Read string table
unsigned short nStrings = 0;
UtlSymId_t *symbolTable = NULL;
if ( bReadSymbolTable )
{
char stringBuf[ 256 ];
nStrings = buf.GetShort();
symbolTable = ( UtlSymId_t* )stackalloc( nStrings * sizeof( UtlSymId_t ) );
for ( int i = 0; i < nStrings; ++i )
{
buf.GetString( stringBuf );
symbolTable[ i ] = g_pDataModel->GetSymbol( stringBuf );
}
}
bool bSuccess = UnserializeElements( buf, fileid, idConflictResolution, ppRoot, symbolTable );
if ( !bSuccess )
return false;
return g_pDataModel->UpdateUnserializedElements( pSourceFormatName, nSourceFormatVersion, fileid, idConflictResolution, ppRoot );
}
bool CDmSerializerBinary::UnserializeElements( CUtlBuffer &buf, DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot, UtlSymId_t *symbolTable )
{
*ppRoot = NULL;
// Read in the element count.
int nElementCount = buf.GetInt();
if ( !nElementCount )
return true;
int nMaxIdConflicts = min( nElementCount, g_pDataModel->GetAllocatedElementCount() );
int nExpectedIdCopyConflicts = ( idConflictResolution == CR_FORCE_COPY || idConflictResolution == CR_COPY_NEW ) ? nMaxIdConflicts : 0;
int nBuckets = min( 0x10000, max( 16, nExpectedIdCopyConflicts / 16 ) ); // CUtlHash can only address up to 65k buckets
CUtlHash< DmIdPair_t > idmap( nBuckets, 0, 0, DmIdPair_t::Compare, DmIdPair_t::HashKey );
// Read + create all elements
CUtlVector<CDmElement*> elementList( 0, nElementCount );
for ( int i = 0; i < nElementCount; ++i )
{
char pName[2048];
DmObjectId_t id;
char typeBuf[ 256 ];
const char *pType = NULL;
if ( symbolTable )
{
unsigned short nType = buf.GetShort();
pType = g_pDataModel->GetString( symbolTable[ nType ] );
}
else
{
buf.GetString( typeBuf );
pType = typeBuf;
}
buf.GetString( pName );
buf.Get( &id, sizeof(DmObjectId_t) );
if ( idConflictResolution == CR_FORCE_COPY )
{
DmIdPair_t idpair;
CopyUniqueId( id, &idpair.m_oldId );
CreateUniqueId( &idpair.m_newId );
idmap.Insert( idpair );
CopyUniqueId( idpair.m_newId, &id );
}
DmElementHandle_t hElement = DMELEMENT_HANDLE_INVALID;
DmElementHandle_t hExistingElement = g_pDataModel->FindElement( id );
if ( hExistingElement != DMELEMENT_HANDLE_INVALID )
{
// id is already in use - need to resolve conflict
if ( idConflictResolution == CR_DELETE_NEW )
{
elementList.AddToTail( g_pDataModel->GetElement( hExistingElement ) );
continue; // just don't create this element
}
else if ( idConflictResolution == CR_DELETE_OLD )
{
g_pDataModelImp->DeleteElement( hExistingElement, HR_NEVER ); // keep the handle around until CreateElementWithFallback
hElement = CreateElementWithFallback( pType, pName, fileid, id );
Assert( hElement == hExistingElement );
}
else if ( idConflictResolution == CR_COPY_NEW )
{
DmIdPair_t idpair;
CopyUniqueId( id, &idpair.m_oldId );
CreateUniqueId( &idpair.m_newId );
idmap.Insert( idpair );
hElement = CreateElementWithFallback( pType, pName, fileid, idpair.m_newId );
}
else
Assert( 0 );
}
// if not found, then create it
if ( hElement == DMELEMENT_HANDLE_INVALID )
{
hElement = CreateElementWithFallback( pType, pName, fileid, id );
}
CDmElement *pElement = g_pDataModel->GetElement( hElement );
CDmeElementAccessor::MarkBeingUnserialized( pElement, true );
elementList.AddToTail( pElement );
}
// The root is the 0th element
*ppRoot = elementList[ 0 ];
// Now read all attributes
for ( int i = 0; i < nElementCount; ++i )
{
CDmElement *pInternal = elementList[ i ];
UnserializeAttributes( buf, pInternal->GetFileId() == fileid ? pInternal : NULL, elementList, symbolTable );
}
for ( int i = 0; i < nElementCount; ++i )
{
CDmElement *pElement = elementList[ i ];
if ( pElement->GetFileId() == fileid )
{
// mark all unserialized elements as done unserializing, and call Resolve()
CDmeElementAccessor::MarkBeingUnserialized( pElement, false );
}
}
g_pDmElementFrameworkImp->RemoveCleanElementsFromDirtyList( );
return buf.IsValid();
}

View File

@ -0,0 +1,27 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Outputs directly into a binary format
//
//=============================================================================
#ifndef DMSERIALIZERBINARY_H
#define DMSERIALIZERBINARY_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class IDataModel;
//-----------------------------------------------------------------------------
// Installation methods for standard serializers
//-----------------------------------------------------------------------------
void InstallBinarySerializer( IDataModel *pFactory );
#endif // DMSERIALIZERBINARY_H

View File

@ -0,0 +1,466 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "dmserializerkeyvalues.h"
#include "datamodel/idatamodel.h"
#include "datamodel.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmattributevar.h"
#include "dmattributeinternal.h"
#include "tier1/KeyValues.h"
#include "tier1/utlbuffer.h"
#include "tier1/utlvector.h"
#include <limits.h>
#include "DmElementFramework.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CUtlBuffer;
class CBaseSceneObject;
//-----------------------------------------------------------------------------
// Used to remap keyvalues names
//-----------------------------------------------------------------------------
struct AttributeRemap_t
{
const char *m_pKeyValuesName;
const char *m_pDmeName;
};
static AttributeRemap_t s_pAttributeRemap[] =
{
{ "type", "_type" }, // FIXME - remove this once we've made type no longer be an attribute
{ "name", "_name" },
{ "id", "_id" }, // FIXME - remove this once we've made id no longer be an attribute
{ NULL, NULL }
};
//-----------------------------------------------------------------------------
// Serialization class for Key Values
//-----------------------------------------------------------------------------
class CDmSerializerKeyValues : public IDmSerializer
{
public:
// Inherited from IDMSerializer
virtual const char *GetName() const { return "keyvalues"; }
virtual const char *GetDescription() const { return "KeyValues"; }
virtual bool StoresVersionInFile() const { return false; }
virtual bool IsBinaryFormat() const { return false; }
virtual int GetCurrentVersion() const { return 0; } // doesn't store a version
virtual bool Serialize( CUtlBuffer &buf, CDmElement *pRoot );
virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
const char *pSourceFormatName, int nSourceFormatVersion,
DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot );
private:
// Methods related to serialization
void SerializeSubKeys( CUtlBuffer& buf, CDmAttribute *pSubKeys );
bool SerializeAttributes( CUtlBuffer& buf, CDmElement *pElement );
bool SerializeElement( CUtlBuffer& buf, CDmElement *pElement );
// Methods related to unserialization
DmElementHandle_t UnserializeElement( KeyValues *pKeyValues, int iNestingLevel );
void UnserializeAttribute( CDmElement *pElement, KeyValues *pKeyValues );
DmElementHandle_t CreateDmElement( const char *pElementType, const char *pElementName );
CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues );
// Deterimines the attribute type of a keyvalue
DmAttributeType_t DetermineAttributeType( KeyValues *pKeyValues );
// For unserialization
CUtlVector<DmElementHandle_t> m_ElementList;
DmElementHandle_t m_hRoot;
DmFileId_t m_fileid;
};
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
static CDmSerializerKeyValues s_DMSerializerKeyValues;
void InstallKeyValuesSerializer( IDataModel *pFactory )
{
pFactory->AddSerializer( &s_DMSerializerKeyValues );
}
//-----------------------------------------------------------------------------
// Serializes a single element attribute
//-----------------------------------------------------------------------------
void CDmSerializerKeyValues::SerializeSubKeys( CUtlBuffer& buf, CDmAttribute *pSubKeys )
{
CDmrElementArray<> array( pSubKeys );
int c = array.Count();
for ( int i = 0; i < c; ++i )
{
CDmElement *pChild = array[i];
if ( pChild )
{
SerializeElement( buf, pChild );
}
}
}
//-----------------------------------------------------------------------------
// Serializes all attributes in an element
//-----------------------------------------------------------------------------
bool CDmSerializerKeyValues::SerializeAttributes( CUtlBuffer& buf, CDmElement *pElement )
{
// Collect the attributes to be written
CDmAttribute **ppAttributes = ( CDmAttribute** )_alloca( pElement->AttributeCount() * sizeof( CDmAttribute* ) );
int nAttributes = 0;
for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
{
if ( pAttribute->IsFlagSet( FATTRIB_DONTSAVE | FATTRIB_STANDARD ) )
continue;
ppAttributes[ nAttributes++ ] = pAttribute;
}
// Now write them all out in reverse order, since FirstAttribute is actually the *last* attribute for perf reasons
for ( int i = nAttributes - 1; i >= 0; --i )
{
CDmAttribute *pAttribute = ppAttributes[ i ];
Assert( pAttribute );
const char *pName = pAttribute->GetName();
// Rename "_type", "_name", or "_id" fields, since they are special fields
for ( int iAttr = 0; s_pAttributeRemap[i].m_pKeyValuesName; ++i )
{
if ( !Q_stricmp( pName, s_pAttributeRemap[iAttr].m_pDmeName ) )
{
pName = s_pAttributeRemap[iAttr].m_pKeyValuesName;
break;
}
}
DmAttributeType_t nAttrType = pAttribute->GetType();
if ( ( nAttrType == AT_ELEMENT_ARRAY ) && !Q_stricmp( pName, "subkeys" ) )
{
SerializeSubKeys( buf, pAttribute );
continue;
}
buf.Printf( "\"%s\" ", pName );
switch( nAttrType )
{
case AT_VOID:
case AT_STRING_ARRAY:
case AT_VOID_ARRAY:
case AT_ELEMENT:
case AT_ELEMENT_ARRAY:
Warning("KeyValues: Can't serialize attribute of type %s into KeyValues files!\n",
g_pDataModel->GetAttributeNameForType( nAttrType ) );
buf.PutChar( '\"' );
buf.PutChar( '\"' );
break;
case AT_FLOAT:
case AT_INT:
case AT_BOOL:
pAttribute->Serialize( buf );
break;
case AT_VECTOR4:
case AT_VECTOR3:
case AT_VECTOR2:
case AT_STRING:
default:
buf.PutChar( '\"' );
buf.PushTab();
pAttribute->Serialize( buf );
buf.PopTab();
buf.PutChar( '\"' );
break;
}
buf.PutChar( '\n' );
}
return true;
}
bool CDmSerializerKeyValues::SerializeElement( CUtlBuffer& buf, CDmElement *pElement )
{
buf.Printf( "\"%s\"\n{\n", pElement->GetName() );
buf.PushTab();
SerializeAttributes( buf, pElement );
buf.PopTab();
buf.Printf( "}\n" );
return true;
}
bool CDmSerializerKeyValues::Serialize( CUtlBuffer &outBuf, CDmElement *pRoot )
{
if ( !pRoot )
return true;
CDmAttribute* pSubKeys = pRoot->GetAttribute( "subkeys" );
if ( !pSubKeys )
return true;
//SetSerializationDelimiter( GetCStringCharConversion() );
SerializeSubKeys( outBuf, pSubKeys );
//SetSerializationDelimiter( NULL );
return true;
}
//-----------------------------------------------------------------------------
// Creates a scene object, adds it to the element dictionary
//-----------------------------------------------------------------------------
DmElementHandle_t CDmSerializerKeyValues::CreateDmElement( const char *pElementType, const char *pElementName )
{
// See if we can create an element of that type
DmElementHandle_t hElement = g_pDataModel->CreateElement( pElementType, pElementName, m_fileid );
if ( hElement == DMELEMENT_HANDLE_INVALID )
{
Warning("KeyValues: Element uses unknown element type %s\n", pElementType );
return DMELEMENT_HANDLE_INVALID;
}
m_ElementList.AddToTail( hElement );
CDmElement *pElement = g_pDataModel->GetElement( hElement );
CDmeElementAccessor::MarkBeingUnserialized( pElement, true );
return hElement;
}
//-----------------------------------------------------------------------------
// Deterimines the attribute type of a keyvalue
//-----------------------------------------------------------------------------
DmAttributeType_t CDmSerializerKeyValues::DetermineAttributeType( KeyValues *pKeyValues )
{
// FIXME: Add detection of vectors/matrices?
switch( pKeyValues->GetDataType() )
{
default:
case KeyValues::TYPE_NONE:
Assert( 0 );
return AT_UNKNOWN;
case KeyValues::TYPE_STRING:
{
float f1, f2, f3, f4;
if ( sscanf( pKeyValues->GetString(), "%f %f %f %f", &f1, &f2, &f3, &f4 ) == 4 )
return AT_VECTOR4;
if ( sscanf( pKeyValues->GetString(), "%f %f %f",&f1, &f2, &f3 ) == 3 )
return AT_VECTOR3;
if ( sscanf( pKeyValues->GetString(), "%f %f", &f1, &f2 ) == 2 )
return AT_VECTOR2;
int i = pKeyValues->GetInt( NULL, INT_MAX );
if ( ( sscanf( pKeyValues->GetString(), "%d", &i ) == 1 ) &&
( !strchr( pKeyValues->GetString(), '.' ) ) )
return AT_INT;
if ( sscanf( pKeyValues->GetString(), "%f", &f1 ) == 1 )
return AT_FLOAT;
return AT_STRING;
}
case KeyValues::TYPE_INT:
return AT_INT;
case KeyValues::TYPE_FLOAT:
return AT_FLOAT;
case KeyValues::TYPE_PTR:
return AT_VOID;
case KeyValues::TYPE_COLOR:
return AT_COLOR;
}
}
//-----------------------------------------------------------------------------
// Reads an attribute for an element
//-----------------------------------------------------------------------------
void CDmSerializerKeyValues::UnserializeAttribute( CDmElement *pElement, KeyValues *pKeyValues )
{
// It's an attribute
const char *pAttributeName = pKeyValues->GetName();
const char *pAttributeValue = pKeyValues->GetString();
// Convert to lower case
CUtlString pLowerName = pAttributeName;
pLowerName.ToLower();
// Rename "type", "name", or "id" fields, since they are special fields
for ( int i = 0; s_pAttributeRemap[i].m_pKeyValuesName; ++i )
{
if ( !Q_stricmp( pLowerName, s_pAttributeRemap[i].m_pKeyValuesName ) )
{
pLowerName = s_pAttributeRemap[i].m_pDmeName;
break;
}
}
// Element types are stored out by GUID, we need to hang onto the guid and
// link it back up once all elements have been loaded from the file
DmAttributeType_t type = DetermineAttributeType( pKeyValues );
// In this case, we have an inlined element or element array attribute
if ( type == AT_UNKNOWN )
{
// Assume this is an empty attribute or attribute array element
Warning("Dm Unserialize: Attempted to read an attribute (\"%s\") of an inappropriate type!\n", pLowerName.Get() );
return;
}
CDmAttribute *pAttribute = pElement->AddAttribute( pLowerName, type );
if ( !pAttribute )
{
Warning("Dm Unserialize: Attempted to read an attribute (\"%s\") of an inappropriate type!\n", pLowerName.Get() );
return;
}
switch( type )
{
case AT_STRING:
{
// Strings have different delimiter rules for KeyValues,
// so let's just directly copy the string instead of going through unserialize
pAttribute->SetValue( pAttributeValue );
}
break;
default:
{
int nLen = Q_strlen( pAttributeValue );
CUtlBuffer buf( pAttributeValue, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
pAttribute->Unserialize( buf );
}
break;
}
}
//-----------------------------------------------------------------------------
// Reads a single element
//-----------------------------------------------------------------------------
DmElementHandle_t CDmSerializerKeyValues::UnserializeElement( KeyValues *pKeyValues, int iNestingLevel )
{
const char *pElementName = pKeyValues->GetName( );
const char *pszKeyValuesElement = g_pDataModel->GetKeyValuesElementName( pElementName, iNestingLevel );
if ( !pszKeyValuesElement )
{
pszKeyValuesElement = "DmElement";
}
DmElementHandle_t handle = CreateDmElement( pszKeyValuesElement, pElementName );
Assert( handle != DMELEMENT_HANDLE_INVALID );
iNestingLevel++;
CDmElement *pElement = g_pDataModel->GetElement( handle );
CDmrElementArray<> subKeys;
for ( KeyValues *pSub = pKeyValues->GetFirstSubKey(); pSub != NULL ; pSub = pSub->GetNextKey() )
{
// Read in a subkey
if ( pSub->GetDataType() == KeyValues::TYPE_NONE )
{
if ( !subKeys.IsValid() )
{
subKeys.Init( pElement->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) );
}
DmElementHandle_t hChild = UnserializeElement( pSub, iNestingLevel );
if ( hChild != DMELEMENT_HANDLE_INVALID )
{
subKeys.AddToTail( hChild );
}
}
else
{
UnserializeAttribute( pElement, pSub );
}
}
return handle;
}
//-----------------------------------------------------------------------------
// Main entry point for the unserialization
//-----------------------------------------------------------------------------
CDmElement* CDmSerializerKeyValues::UnserializeFromKeyValues( KeyValues *pKeyValues )
{
m_ElementList.RemoveAll();
m_hRoot = CreateDmElement( "DmElement", "root" );
CDmElement *pRoot = g_pDataModel->GetElement( m_hRoot );
CDmrElementArray<> subkeys( pRoot->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) );
int iNestingLevel = 0;
for ( KeyValues *pElementKey = pKeyValues; pElementKey != NULL; pElementKey = pElementKey->GetNextKey() )
{
DmElementHandle_t hChild = UnserializeElement( pElementKey, iNestingLevel );
if ( hChild != DMELEMENT_HANDLE_INVALID )
{
subkeys.AddToTail( hChild );
}
}
// mark all unserialized elements as done unserializing, and call Resolve()
int c = m_ElementList.Count();
for ( int i = 0; i < c; ++i )
{
CDmElement *pElement = g_pDataModel->GetElement( m_ElementList[i] );
CDmeElementAccessor::MarkBeingUnserialized( pElement, false );
}
g_pDmElementFrameworkImp->RemoveCleanElementsFromDirtyList( );
m_ElementList.RemoveAll();
return pRoot;
}
//-----------------------------------------------------------------------------
// Main entry point for the unserialization
//-----------------------------------------------------------------------------
bool CDmSerializerKeyValues::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
const char *pSourceFormatName, int nSourceFormatVersion,
DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
{
Assert( !V_stricmp( pEncodingName, "keyvalues" ) );
*ppRoot = NULL;
KeyValues *kv = new KeyValues( "keyvalues file" );
if ( !kv )
return false;
m_fileid = fileid;
bool bOk = kv->LoadFromBuffer( "keyvalues file", buf );
if ( bOk )
{
//SetSerializationDelimiter( GetCStringCharConversion() );
*ppRoot = UnserializeFromKeyValues( kv );
//SetSerializationDelimiter( NULL );
}
m_fileid = DMFILEID_INVALID;
kv->deleteThis();
return bOk;
}

View File

@ -0,0 +1,27 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DMSERIALIZERKEYVALUES_H
#define DMSERIALIZERKEYVALUES_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class IDataModel;
//-----------------------------------------------------------------------------
// Installation methods for standard serializers
//-----------------------------------------------------------------------------
void InstallKeyValuesSerializer( IDataModel *pFactory );
#endif // DMSERIALIZER_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DMSERIALIZERKEYVALUES2_H
#define DMSERIALIZERKEYVALUES2_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class IDataModel;
//-----------------------------------------------------------------------------
// Installation methods for standard serializers
//-----------------------------------------------------------------------------
void InstallKeyValues2Serializer( IDataModel *pFactory );
#endif // DMSERIALIZERKEYVALUES2_H

444
datamodel/undomanager.cpp Normal file
View File

@ -0,0 +1,444 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "undomanager.h"
#include "datamodel.h"
extern CDataModel *g_pDataModelImp;
CUtlSymbolTableMT CUndoManager::s_UndoSymbolTable;
CUndoManager::CUndoManager( ) :
m_bEnabled( true ),
m_bDiscarded( false ),
m_nMaxUndoDepth( 4096 ),
m_nNesting( 0 ),
m_nNotifyNesting( 0 ),
m_bStreamStart( false ),
m_bTrace( false ),
m_bSuppressingNotify( false ),
m_nItemsAddedSinceStartOfStream( 0 ),
m_nNotifySource( 0 ),
m_nNotifyFlags( 0 ),
m_nChainingID( 0 ),
m_PreviousChainingID( 0 )
{
}
CUndoManager::~CUndoManager()
{
}
void CUndoManager::Shutdown()
{
WipeUndo();
WipeRedo();
}
bool CUndoManager::InstallNotificationCallback( IDmNotify *pNotify )
{
if ( m_Notifiers.Find( pNotify ) >= 0 )
return false;
m_Notifiers.AddToTail( pNotify );
return true;
}
void CUndoManager::RemoveNotificationCallback( IDmNotify *pNotify )
{
m_Notifiers.FindAndRemove( pNotify );
}
bool CUndoManager::IsSuppressingNotify( ) const
{
return m_bSuppressingNotify;
}
void CUndoManager::SetSuppressingNotify( bool bSuppress )
{
m_bSuppressingNotify = bSuppress;
}
void CUndoManager::Trace( const char *fmt, ... )
{
if ( !m_bTrace )
return;
char str[ 2048 ];
va_list argptr;
va_start( argptr, fmt );
_vsnprintf( str, sizeof( str ) - 1, fmt, argptr );
va_end( argptr );
str[ sizeof( str ) - 1 ] = 0;
char spaces[ 128 ];
Q_memset( spaces, 0, sizeof( spaces ) );
for ( int i = 0; i < ( m_nNesting * 3 ); ++i )
{
if ( i >= 127 )
break;
spaces[ i ] = ' ';
}
Msg( "%s%s", spaces, str );
}
void CUndoManager::SetUndoDepth( int nMaxUndoDepth )
{
Assert( !HasUndoData() );
m_nMaxUndoDepth = nMaxUndoDepth;
}
void CUndoManager::EnableUndo()
{
m_bEnabled = true;
}
void CUndoManager::DisableUndo()
{
m_bEnabled = false;
}
bool CUndoManager::HasUndoData() const
{
return m_UndoList.Count() != 0;
}
bool CUndoManager::UndoDataDiscarded() const
{
return m_bDiscarded;
}
bool CUndoManager::HasRedoData() const
{
return m_RedoStack.Count() > 0;
}
void CUndoManager::PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags )
{
if ( m_nNotifyNesting++ == 0 )
{
m_pNotifyReason = pReason;
m_nNotifySource = nNotifySource;
m_nNotifyFlags = nNotifyFlags;
}
}
void CUndoManager::PopNotificationScope( bool bAbort )
{
--m_nNotifyNesting;
Assert( m_nNotifyNesting >= 0 );
if ( m_nNotifyNesting == 0 )
{
if ( !m_bSuppressingNotify && ( ( m_nNotifyFlags & NOTIFY_CHANGE_MASK ) != 0 ) )
{
int nNotifyCount = m_Notifiers.Count();
for( int i = 0; i < nNotifyCount; ++i )
{
m_Notifiers[i]->NotifyDataChanged( m_pNotifyReason, m_nNotifySource, m_nNotifyFlags );
}
}
m_nNotifySource = 0;
m_nNotifyFlags = 0;
}
}
void CUndoManager::PushUndo( const char *udesc, const char *rdesc, int nChainingID )
{
if ( !IsEnabled() )
return;
Trace( "[%d] Pushing undo '%s'\n", m_nNesting + 1, udesc );
if ( m_nNesting++ == 0 )
{
m_PreviousChainingID = m_nChainingID;
m_nChainingID = nChainingID;
m_UndoDesc = s_UndoSymbolTable.AddString( udesc );
m_RedoDesc = ( udesc == rdesc ) ? m_UndoDesc : s_UndoSymbolTable.AddString( rdesc );
m_bStreamStart = true;
m_nItemsAddedSinceStartOfStream = 0;
}
}
void CUndoManager::PushRedo()
{
if ( !IsEnabled() )
return;
Trace( "[%d] Popping undo '%s'\n", m_nNesting, s_UndoSymbolTable.String( m_UndoDesc ) );
--m_nNesting;
Assert( m_nNesting >= 0 );
if ( m_nNesting == 0 )
{
if ( m_nItemsAddedSinceStartOfStream > 0 )
{
WipeRedo();
// Accumulate this operation into the previous "undo" operation if there is one
if ( m_nChainingID != 0 &&
m_PreviousChainingID == m_nChainingID )
{
// Walk undo list backward looking for previous end of stream and unmark that indicator
int i = m_UndoList.Tail();
while ( i != m_UndoList.InvalidIndex() )
{
IUndoElement *e = m_UndoList[ i ];
if ( e && e->IsEndOfStream() )
{
e->SetEndOfStream( false );
break;
}
i = m_UndoList.Previous( i );
}
}
}
m_nItemsAddedSinceStartOfStream = 0;
}
}
void CUndoManager::AbortUndoableOperation()
{
if ( !IsEnabled() )
return;
bool hasItems = m_nItemsAddedSinceStartOfStream > 0 ? true : false;
Trace( "[%d] Aborting undo '%s'\n", m_nNesting, s_UndoSymbolTable.String( m_UndoDesc ) );
// Close off context
PushRedo();
if ( m_nNesting == 0 && hasItems )
{
Undo();
WipeRedo();
}
}
void CUndoManager::WipeUndo()
{
CDisableUndoScopeGuard sg;
FOR_EACH_LL( m_UndoList, elem )
{
Trace( "WipeUndo '%s'\n", m_UndoList[ elem ]->GetDesc() );
m_UndoList[ elem ]->Release();
}
m_UndoList.RemoveAll();
m_PreviousChainingID = 0;
}
void CUndoManager::WipeRedo()
{
int c = m_RedoStack.Count();
if ( c == 0 )
return;
CUtlVector< DmElementHandle_t > handles;
g_pDataModelImp->GetInvalidHandles( handles );
g_pDataModelImp->MarkHandlesValid( handles );
CDisableUndoScopeGuard sg;
for ( int i = 0; i < c ; ++i )
{
IUndoElement *elem;
elem = m_RedoStack[ i ];
Trace( "WipeRedo '%s'\n", elem->GetDesc() );
elem->Release();
}
m_RedoStack.Clear();
g_pDataModelImp->MarkHandlesInvalid( handles );
}
void CUndoManager::AddUndoElement( IUndoElement *pElement )
{
Assert( IsEnabled() );
if ( !pElement )
return;
++m_nItemsAddedSinceStartOfStream;
WipeRedo();
/*
// For later
if ( m_UndoList.Count() >= m_nMaxUndos )
{
m_bDiscarded = true;
}
*/
Trace( "AddUndoElement '%s'\n", pElement->GetDesc() );
m_UndoList.AddToTail( pElement );
if ( m_bStreamStart )
{
pElement->SetEndOfStream( true );
m_bStreamStart = false;
}
}
void CUndoManager::Undo()
{
CNotifyScopeGuard notify( "CUndoManager::Undo", NOTIFY_SOURCE_UNDO, NOTIFY_SETDIRTYFLAG );
Trace( "Undo\n======\n" );
bool saveEnabled = m_bEnabled;
m_bEnabled = false;
bool bEndOfStream = false;
while ( !bEndOfStream && m_UndoList.Count() > 0 )
{
int i = m_UndoList.Tail();
IUndoElement *action = m_UndoList[ i ];
Assert( action );
Trace( " %s\n", action->GetDesc() );
action->Undo();
m_RedoStack.Push( action );
bEndOfStream = action->IsEndOfStream();
m_UndoList.Remove( i );
}
Trace( "======\n\n" );
m_bEnabled = saveEnabled;
m_PreviousChainingID = 0;
}
void CUndoManager::Redo()
{
CNotifyScopeGuard notify( "CUndoManager::Redo", NOTIFY_SOURCE_UNDO, NOTIFY_SETDIRTYFLAG );
Trace( "Redo\n======\n" );
bool saveEnabled = m_bEnabled;
m_bEnabled = false;
bool bEndOfStream = false;
while ( !bEndOfStream && m_RedoStack.Count() > 0 )
{
IUndoElement *action = NULL;
m_RedoStack.Pop( action );
Assert( action );
Trace( " %s\n", action->GetDesc() );
action->Redo();
m_UndoList.AddToTail( action );
if ( m_RedoStack.Count() > 0 )
{
action = m_RedoStack.Top();
bEndOfStream = action->IsEndOfStream();
}
}
Trace( "======\n\n" );
m_bEnabled = saveEnabled;
m_PreviousChainingID = 0;
}
const char *CUndoManager::UndoDesc() const
{
if ( m_UndoList.Count() <= 0 )
return "";
int i = m_UndoList.Tail();
IUndoElement *action = m_UndoList[ i ];
return action->UndoDesc();
}
const char *CUndoManager::RedoDesc() const
{
if ( m_RedoStack.Count() <= 0 )
{
return "";
}
IUndoElement *action = m_RedoStack.Top();
return action->RedoDesc();
}
UtlSymId_t CUndoManager::GetUndoDescInternal( const char *context )
{
if ( m_nNesting <= 0 )
{
static CUtlSymbolTable s_DescErrorsTable;
static CUtlVector< CUtlSymbol > s_DescErrors;
CUtlSymbol sym = s_DescErrorsTable.AddString( context );
if ( s_DescErrors.Find( sym ) == s_DescErrors.InvalidIndex() )
{
Warning( "CUndoManager::GetUndoDescInternal: undoable operation missing CUndoScopeGuard in application\nContext( %s )\n", context );
s_DescErrors.AddToTail( sym );
}
return s_UndoSymbolTable.AddString( context );
}
return m_UndoDesc;
}
UtlSymId_t CUndoManager::GetRedoDescInternal( const char *context )
{
if ( m_nNesting <= 0 )
{
// Warning( "CUndoManager::GetRedoDescInternal: undoable operation missing CUndoScopeGuard in application\nContext( %s )", context );
return s_UndoSymbolTable.AddString( context );
}
return m_RedoDesc;
}
void CUndoManager::GetUndoInfo( CUtlVector< UndoInfo_t >& list )
{
// Needs to persist after function returns...
static CUtlSymbolTable table;
int ops = 0;
for ( int i = m_UndoList.Tail(); i != m_UndoList.InvalidIndex(); i = m_UndoList.Previous( i ) )
{
++ops;
IUndoElement *action = m_UndoList[ i ];
Assert( action );
bool bEndOfStream = action->IsEndOfStream();
UndoInfo_t info;
info.undo = action->UndoDesc();
info.redo = action->RedoDesc();
// This is a hack because GetDesc() returns a static char buf[] and so the last one will clobber them all
// So we have the requester pass in a temporary string table so we can get a ptr to a CUtlSymbol in the table
// and use that. Sigh.
const char *desc = action->GetDesc();
CUtlSymbol sym = table.AddString( desc );
info.desc = table.String( sym );
info.terminator = bEndOfStream;
info.numoperations = bEndOfStream ? ops : 1;
list.AddToTail( info );
if ( bEndOfStream )
{
ops = 0;
}
}
}
void CUndoManager::TraceUndo( bool state )
{
m_bTrace = state;
}

124
datamodel/undomanager.h Normal file
View File

@ -0,0 +1,124 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef UNDOMANAGER_H
#define UNDOMANAGER_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlsymbol.h"
#include "tier1/utllinkedlist.h"
#include "tier1/utlstack.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class IUndoElement;
struct UndoInfo_t;
class IDmNotify;
//-----------------------------------------------------------------------------
// Undo/Redo stuff:
//-----------------------------------------------------------------------------
class CUndoManager
{
public:
CUndoManager();
~CUndoManager();
void Shutdown();
void SetUndoDepth( int nMaxUndoDepth );
void AddUndoElement( IUndoElement *pElement );
void Undo();
void Redo();
void TraceUndo( bool state );
void EnableUndo();
void DisableUndo();
bool IsEnabled() const;
bool HasUndoData() const;
bool UndoDataDiscarded() const;
bool HasRedoData() const;
void WipeUndo();
void WipeRedo();
const char *UndoDesc() const;
const char *RedoDesc() const;
void PushUndo( char const *udesc, char const *rdesc, int nChainingID );
void PushRedo();
void AbortUndoableOperation();
UtlSymId_t GetUndoDescInternal( const char *context );
UtlSymId_t GetRedoDescInternal( const char *context );
void GetUndoInfo( CUtlVector< UndoInfo_t >& list );
bool InstallNotificationCallback( IDmNotify *pNotify );
void RemoveNotificationCallback( IDmNotify *pNotify );
bool IsSuppressingNotify( ) const;
void SetSuppressingNotify( bool bSuppress );
void PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags );
void PopNotificationScope( bool bAbort );
void NotifyState( int nNotifyFlags );
static const char *GetUndoString( CUtlSymbol sym );
private:
void Trace( PRINTF_FORMAT_STRING const char *fmt, ... );
CUtlLinkedList< IUndoElement *, int > m_UndoList;
CUtlStack< IUndoElement * > m_RedoStack;
CUtlVector< IDmNotify* > m_Notifiers;
int m_nMaxUndoDepth;
int m_nNesting;
int m_nNotifyNesting;
CUtlSymbol m_UndoDesc;
CUtlSymbol m_RedoDesc;
int m_nNotifySource;
int m_nNotifyFlags;
const char* m_pNotifyReason;
int m_nItemsAddedSinceStartOfStream;
// Signals that next undo operation is the "Start" of a stream
bool m_bStreamStart : 1;
bool m_bTrace : 1;
bool m_bDiscarded : 1;
bool m_bEnabled : 1;
bool m_bSuppressingNotify : 1;
static CUtlSymbolTableMT s_UndoSymbolTable;
int m_nChainingID;
int m_PreviousChainingID;
};
//-----------------------------------------------------------------------------
// Is undo enabled
//-----------------------------------------------------------------------------
inline bool CUndoManager::IsEnabled() const
{
return m_bEnabled;
}
inline void CUndoManager::NotifyState( int nNotifyFlags )
{
// FIXME: Should suppress prevent notification being sent,
// or prevent notification flags from being set in the first place?
m_nNotifyFlags |= nNotifyFlags;
}
inline const char *CUndoManager::GetUndoString( CUtlSymbol sym )
{
return s_UndoSymbolTable.String( sym );
}
#endif // UNDOMANAGER_H