1
This commit is contained in:
209
datamodel/DmElementFramework.cpp
Normal file
209
datamodel/DmElementFramework.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
78
datamodel/DmElementFramework.h
Normal file
78
datamodel/DmElementFramework.h
Normal 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
|
169
datamodel/clipboardmanager.cpp
Normal file
169
datamodel/clipboardmanager.cpp
Normal 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;
|
||||
}
|
36
datamodel/clipboardmanager.h
Normal file
36
datamodel/clipboardmanager.h
Normal 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
2464
datamodel/datamodel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
527
datamodel/datamodel.h
Normal file
527
datamodel/datamodel.h
Normal 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
76
datamodel/datamodel.vpc
Normal 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"
|
||||
}
|
||||
}
|
331
datamodel/dependencygraph.cpp
Normal file
331
datamodel/dependencygraph.cpp
Normal 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() );
|
||||
}
|
62
datamodel/dependencygraph.h
Normal file
62
datamodel/dependencygraph.h
Normal 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
3265
datamodel/dmattribute.cpp
Normal file
File diff suppressed because it is too large
Load Diff
75
datamodel/dmattributeinternal.h
Normal file
75
datamodel/dmattributeinternal.h
Normal 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
1420
datamodel/dmelement.cpp
Normal file
File diff suppressed because it is too large
Load Diff
468
datamodel/dmelementdictionary.cpp
Normal file
468
datamodel/dmelementdictionary.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
184
datamodel/dmelementdictionary.h
Normal file
184
datamodel/dmelementdictionary.h
Normal 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
|
100
datamodel/dmelementfactoryhelper.cpp
Normal file
100
datamodel/dmelementfactoryhelper.cpp
Normal 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( );
|
||||
}
|
587
datamodel/dmserializerbinary.cpp
Normal file
587
datamodel/dmserializerbinary.cpp
Normal 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();
|
||||
}
|
27
datamodel/dmserializerbinary.h
Normal file
27
datamodel/dmserializerbinary.h
Normal 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
|
466
datamodel/dmserializerkeyvalues.cpp
Normal file
466
datamodel/dmserializerkeyvalues.cpp
Normal 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;
|
||||
}
|
27
datamodel/dmserializerkeyvalues.h
Normal file
27
datamodel/dmserializerkeyvalues.h
Normal 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
|
1377
datamodel/dmserializerkeyvalues2.cpp
Normal file
1377
datamodel/dmserializerkeyvalues2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
27
datamodel/dmserializerkeyvalues2.h
Normal file
27
datamodel/dmserializerkeyvalues2.h
Normal 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
444
datamodel/undomanager.cpp
Normal 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
124
datamodel/undomanager.h
Normal 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
|
Reference in New Issue
Block a user