Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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

//========= 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();
}