You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
9.5 KiB
346 lines
9.5 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "foundrydoc.h"
|
||
|
#include "tier1/KeyValues.h"
|
||
|
#include "tier1/utlbuffer.h"
|
||
|
#include "datamodel/dmelement.h"
|
||
|
#include "toolutils/enginetools_int.h"
|
||
|
#include "filesystem.h"
|
||
|
#include "foundrytool.h"
|
||
|
#include "toolframework/ienginetool.h"
|
||
|
#include "dmevmfentity.h"
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Constructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CFoundryDoc::CFoundryDoc( IFoundryDocCallback *pCallback ) : m_pCallback( pCallback )
|
||
|
{
|
||
|
m_hRoot = NULL;
|
||
|
m_pBSPFileName[0] = 0;
|
||
|
m_pVMFFileName[0] = 0;
|
||
|
m_bDirty = false;
|
||
|
g_pDataModel->InstallNotificationCallback( this );
|
||
|
}
|
||
|
|
||
|
CFoundryDoc::~CFoundryDoc()
|
||
|
{
|
||
|
g_pDataModel->RemoveNotificationCallback( this );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Inherited from INotifyUI
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CFoundryDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
|
||
|
{
|
||
|
OnDataChanged( pReason, nNotifySource, nNotifyFlags );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Gets the file name
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CFoundryDoc::GetBSPFileName()
|
||
|
{
|
||
|
return m_pBSPFileName;
|
||
|
}
|
||
|
|
||
|
const char *CFoundryDoc::GetVMFFileName()
|
||
|
{
|
||
|
return m_pVMFFileName;
|
||
|
}
|
||
|
|
||
|
void CFoundryDoc::SetVMFFileName( const char *pFileName )
|
||
|
{
|
||
|
Q_strncpy( m_pVMFFileName, pFileName, sizeof( m_pVMFFileName ) );
|
||
|
Q_FixSlashes( m_pVMFFileName );
|
||
|
SetDirty( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Dirty bits
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CFoundryDoc::SetDirty( bool bDirty )
|
||
|
{
|
||
|
m_bDirty = bDirty;
|
||
|
}
|
||
|
|
||
|
bool CFoundryDoc::IsDirty() const
|
||
|
{
|
||
|
return m_bDirty;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Saves/loads from file
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CFoundryDoc::LoadFromFile( const char *pFileName )
|
||
|
{
|
||
|
Assert( !m_hRoot.Get() );
|
||
|
|
||
|
// This is not undoable
|
||
|
CAppDisableUndoScopeGuard guard( "CFoundryDoc::LoadFromFile", 0 );
|
||
|
SetDirty( false );
|
||
|
|
||
|
if ( !pFileName[0] )
|
||
|
return false;
|
||
|
|
||
|
// Store the BSP file name
|
||
|
Q_strncpy( m_pBSPFileName, pFileName, sizeof( m_pBSPFileName ) );
|
||
|
|
||
|
// Construct VMF file name from the BSP
|
||
|
const char *pGame = Q_stristr( pFileName, "\\game\\" );
|
||
|
if ( !pGame )
|
||
|
return false;
|
||
|
|
||
|
// Compute the map name
|
||
|
char mapname[ 256 ];
|
||
|
const char *pMaps = Q_stristr( pFileName, "\\maps\\" );
|
||
|
if ( !pMaps )
|
||
|
return false;
|
||
|
|
||
|
Q_strncpy( mapname, pMaps + 6, sizeof( mapname ) );
|
||
|
|
||
|
int nLen = (int)( (size_t)pGame - (size_t)pFileName ) + 1;
|
||
|
Q_strncpy( m_pVMFFileName, pFileName, nLen );
|
||
|
Q_strncat( m_pVMFFileName, "\\content\\", sizeof(m_pVMFFileName) );
|
||
|
Q_strncat( m_pVMFFileName, pGame + 6, sizeof(m_pVMFFileName) );
|
||
|
Q_SetExtension( m_pVMFFileName, ".vmf", sizeof(m_pVMFFileName) );
|
||
|
|
||
|
CDmElement *pVMF = NULL;
|
||
|
if ( g_pDataModel->RestoreFromFile( m_pVMFFileName, NULL, "vmf", &pVMF ) == DMFILEID_INVALID )
|
||
|
{
|
||
|
m_pBSPFileName[0] = 0;
|
||
|
m_pVMFFileName[0] = 0;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_hRoot = pVMF;
|
||
|
|
||
|
guard.Release();
|
||
|
SetDirty( false );
|
||
|
|
||
|
char cmd[ 256 ];
|
||
|
Q_snprintf( cmd, sizeof( cmd ), "disconnect; map %s\n", mapname );
|
||
|
enginetools->Command( cmd );
|
||
|
enginetools->Execute( );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CFoundryDoc::SaveToFile( )
|
||
|
{
|
||
|
if ( m_hRoot.Get() && m_pVMFFileName && m_pVMFFileName[0] )
|
||
|
{
|
||
|
g_pDataModel->SaveToFile( m_pVMFFileName, NULL, "keyvalues", "vmf", m_hRoot );
|
||
|
}
|
||
|
|
||
|
SetDirty( false );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Returns the root object
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CDmElement *CFoundryDoc::GetRootObject()
|
||
|
{
|
||
|
return m_hRoot;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Returns the entity list
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CDmAttribute *CFoundryDoc::GetEntityList()
|
||
|
{
|
||
|
return m_hRoot ? m_hRoot->GetAttribute( "entities", AT_ELEMENT_ARRAY ) : NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Deletes an entity
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CFoundryDoc::DeleteEntity( CDmeVMFEntity *pEntity )
|
||
|
{
|
||
|
CDmrElementArray<> entities( GetEntityList() );
|
||
|
if ( !entities.IsValid() )
|
||
|
return;
|
||
|
|
||
|
int nCount = entities.Count();
|
||
|
for ( int i = 0; i < nCount; ++i )
|
||
|
{
|
||
|
if ( pEntity == CastElement< CDmeVMFEntity >( entities[i] ) )
|
||
|
{
|
||
|
entities.FastRemove( i );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Called when data changes
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CFoundryDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
|
||
|
{
|
||
|
SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false );
|
||
|
m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// List of all entity classnames to copy over from the original block
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static const char *s_pUseOriginalClasses[] =
|
||
|
{
|
||
|
"worldspawn",
|
||
|
"func_occluder",
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Always copy the worldspawn and other entities that had data built into them by VBSP out
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CFoundryDoc::AddOriginalEntities( CUtlBuffer &entityBuf, const char *pActualEntityData )
|
||
|
{
|
||
|
while ( *pActualEntityData )
|
||
|
{
|
||
|
pActualEntityData = strchr( pActualEntityData, '{' );
|
||
|
if ( !pActualEntityData )
|
||
|
break;
|
||
|
|
||
|
const char *pBlockStart = pActualEntityData;
|
||
|
|
||
|
pActualEntityData = strstr( pActualEntityData, "\"classname\"" );
|
||
|
if ( !pActualEntityData )
|
||
|
break;
|
||
|
|
||
|
// Skip "classname"
|
||
|
pActualEntityData += 11;
|
||
|
|
||
|
pActualEntityData = strchr( pActualEntityData, '\"' );
|
||
|
if ( !pActualEntityData )
|
||
|
break;
|
||
|
|
||
|
// Skip "
|
||
|
++pActualEntityData;
|
||
|
|
||
|
char pClassName[512];
|
||
|
int j = 0;
|
||
|
while (*pActualEntityData != 0 && *pActualEntityData != '\"' )
|
||
|
{
|
||
|
pClassName[j++] = *pActualEntityData++;
|
||
|
}
|
||
|
pClassName[j] = 0;
|
||
|
|
||
|
pActualEntityData = strchr( pActualEntityData, '}' );
|
||
|
if ( !pActualEntityData )
|
||
|
break;
|
||
|
|
||
|
// Skip }
|
||
|
++pActualEntityData;
|
||
|
|
||
|
for ( int i = 0; s_pUseOriginalClasses[i]; ++i )
|
||
|
{
|
||
|
if ( !Q_stricmp( pClassName, s_pUseOriginalClasses[i] ) )
|
||
|
{
|
||
|
// Found one we need to keep, add it to the buffer
|
||
|
int nBytes = (int)( (size_t)pActualEntityData - (size_t)pBlockStart );
|
||
|
entityBuf.Put( pBlockStart, nBytes );
|
||
|
entityBuf.PutChar( '\n' );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Copy in other entities from the editable VMF
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CFoundryDoc::AddVMFEntities( CUtlBuffer &entityBuf, const char *pActualEntityData )
|
||
|
{
|
||
|
const CDmrElementArray<CDmElement> entityArray( m_hRoot, "entities" );
|
||
|
if ( !entityArray.IsValid() )
|
||
|
return;
|
||
|
|
||
|
int nCount = entityArray.Count();
|
||
|
for ( int iEntity = 0; iEntity < nCount; ++iEntity )
|
||
|
{
|
||
|
CDmElement *pEntity = entityArray[iEntity];
|
||
|
const char *pClassName = pEntity->GetValueString( "classname" );
|
||
|
if ( !pClassName || !pClassName[0] )
|
||
|
continue;
|
||
|
|
||
|
// Don't spawn those classes we grab from the actual compiled map
|
||
|
bool bDontUse = false;
|
||
|
for ( int i = 0; s_pUseOriginalClasses[i]; ++i )
|
||
|
{
|
||
|
if ( !Q_stricmp( pClassName, s_pUseOriginalClasses[i] ) )
|
||
|
{
|
||
|
bDontUse = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( bDontUse )
|
||
|
continue;
|
||
|
|
||
|
entityBuf.PutString( "{\n" );
|
||
|
entityBuf.Printf( "\"id\" \"%d\"\n", atol( pEntity->GetName() ) );
|
||
|
|
||
|
for( CDmAttribute *pAttribute = pEntity->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
|
||
|
{
|
||
|
if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
|
||
|
continue;
|
||
|
|
||
|
if ( IsArrayType( pAttribute->GetType() ) )
|
||
|
continue;
|
||
|
|
||
|
if ( !Q_stricmp( pAttribute->GetName(), "editorType" ) || !Q_stricmp( pAttribute->GetName(), "editor" ) )
|
||
|
continue;
|
||
|
|
||
|
entityBuf.Printf( "\"%s\" ", pAttribute->GetName() );
|
||
|
|
||
|
// FIXME: Set up standard delimiters
|
||
|
entityBuf.PutChar( '\"' );
|
||
|
pAttribute->Serialize( entityBuf );
|
||
|
entityBuf.PutString( "\"\n" );
|
||
|
}
|
||
|
|
||
|
entityBuf.PutString( "}\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Create a text block the engine can parse containing the entity data to spawn
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char* CFoundryDoc::GenerateEntityData( const char *pActualEntityData )
|
||
|
{
|
||
|
if ( !m_hRoot.Get() )
|
||
|
return pActualEntityData;
|
||
|
|
||
|
// Contains the text block the engine can parse containing the entity data to spawn
|
||
|
static CUtlBuffer entityBuf( 2048, 2048, CUtlBuffer::TEXT_BUFFER );
|
||
|
entityBuf.Clear();
|
||
|
|
||
|
// Always copy the worldspawn and other entities that had data built into them by VBSP out
|
||
|
AddOriginalEntities( entityBuf, pActualEntityData );
|
||
|
|
||
|
// Copy in other entities from the editable VMF
|
||
|
AddVMFEntities( entityBuf, pActualEntityData );
|
||
|
|
||
|
return (const char*)entityBuf.Base();
|
||
|
}
|
||
|
|