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