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.
345 lines
9.5 KiB
345 lines
9.5 KiB
//========= 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(); |
|
} |
|
|
|
|