//========= Copyright Valve Corporation, All rights reserved. ============// #include "vbsp.h" #include "map_shared.h" #include "fgdlib/fgdlib.h" #include "manifest.h" #include "windows.h" //----------------------------------------------------------------------------- // Purpose: default constructor //----------------------------------------------------------------------------- CManifestMap::CManifestMap( void ) { m_RelativeMapFileName[ 0 ] = 0; m_bTopLevelMap = false; } //----------------------------------------------------------------------------- // Purpose: default constructor //----------------------------------------------------------------------------- CManifest::CManifest( void ) { m_InstancePath[ 0 ] = 0; m_bIsCordoning = false; m_CordoningMapEnt = NULL; } //----------------------------------------------------------------------------- // Purpose: this function will parse through the known keys for the manifest map entry // Input : szKey - the key name // szValue - the value // pManifestMap - the manifest map this belongs to // Output : ChunkFileResult_t - result of the parsing //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap ) { if ( !stricmp( szKey, "Name" ) ) { // pManifestMap->m_FriendlyName = szValue; } else if ( !stricmp( szKey, "File" ) ) { strcpy( pManifestMap->m_RelativeMapFileName, szValue ); } else if ( !stricmp( szKey, "IsPrimary" ) ) { // pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 ); } else if ( !stricmp( szKey, "IsProtected" ) ) { // pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 ); } else if ( !stricmp( szKey, "TopLevel" ) ) { pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 ); } return ChunkFile_Ok; } //----------------------------------------------------------------------------- // Purpose: this function is responsible for setting up the manifest map about to be read in // Input : pFile - the chunk file being read // pDoc - the owning manifest document // Output : ChunkFileResult_t - result of the parsing //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest ) { CManifestMap *pManifestMap = new CManifestMap(); pManifest->m_Maps.AddToTail( pManifestMap ); ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap ); return( eResult ); } //----------------------------------------------------------------------------- // Purpose: this function will load the VMF chunk // Input : pFile - the chunk file being read // pDoc - the owning manifest document // Output : ChunkFileResult_t - result of the parsing //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest ) { CChunkHandlerMap Handlers; Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest ); pFile->PushHandlers(&Handlers); ChunkFileResult_t eResult = ChunkFile_Ok; eResult = pFile->ReadChunk(); pFile->PopHandlers(); return( eResult ); } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon ) { // Add a box to this cordon. pCordon->m_Boxes.AddToTail(); BoundBox &box = pCordon->m_Boxes.Tail(); // Fill it in with the data from the VMF. return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box ); } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox ) { if (!stricmp(szKey, "mins")) { CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins); } else if (!stricmp(szKey, "maxs")) { CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs); } return ChunkFile_Ok; } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon ) { if (!stricmp(szKey, "name")) { pCordon->m_szName.Set( szValue ); } // Whether this particular cordon volume is active. else if (!stricmp(szKey, "active")) { CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive); } return ChunkFile_Ok; } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest ) { // Add a new cordon which will be filled in by the key callback pManifest->m_Cordons.AddToTail(); Cordon_t &cordon = pManifest->m_Cordons.Tail(); CChunkHandlerMap Handlers; Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon ); pFile->PushHandlers(&Handlers); ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon ); pFile->PopHandlers(); return(eResult); } //----------------------------------------------------------------------------------------------------------- // Parses keys that are applicable to all cordons in the map. //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest ) { // Whether the cordoning system is enabled or disabled. if ( !stricmp( szKey, "active" ) ) { CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning ); } return ChunkFile_Ok; } //----------------------------------------------------------------------------- // Parses the VMF chunk that pertains to all the cordons in the map: // // cordons // { // "active" "true" // cordon // { // "active" "true" // "box" // { // "mins" "-1024, -1024, -1024" // "maxs" "1024, 1024, 1024" // } // ...may be more boxes... // } // ...may be more cordons... // } // //----------------------------------------------------------------------------- ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest ) { CChunkHandlerMap Handlers; Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest ); pFile->PushHandlers(&Handlers); ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest ); pFile->PopHandlers(); return(eResult); } extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity); ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc ) { pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities]; g_MainMap->num_entities++; memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) ); pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes; pDoc->m_CordoningMapEnt->numbrushes = 0; LoadEntity_t LoadEntity; LoadEntity.pEntity = pDoc->m_CordoningMapEnt; // No default flags/contents LoadEntity.nBaseFlags = 0; LoadEntity.nBaseContents = 0; // // Set up handlers for the subchunks that we are interested in. // CChunkHandlerMap Handlers; Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc ); Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity); pFile->PushHandlers(&Handlers); ChunkFileResult_t eResult = ChunkFile_Ok; eResult = pFile->ReadChunk(); pFile->PopHandlers(); return( eResult ); } //----------------------------------------------------------------------------- // Purpose: this function will create a new entity pair // Input : pKey - the key of the pair // pValue - the value of the pair // Output : returns a newly created epair structure //----------------------------------------------------------------------------- epair_t *CManifest::CreateEPair( char *pKey, char *pValue ) { epair_t *pEPair = new epair_t; pEPair->key = new char[ strlen( pKey ) + 1 ]; pEPair->value = new char[ strlen( pValue ) + 1 ]; strcpy( pEPair->key, pKey ); strcpy( pEPair->value, pValue ); return pEPair; } //----------------------------------------------------------------------------- // Purpose: this function will load in all of the submaps belonging to this manifest, // except for the top level map, which is loaded separately. // Input : pMapFile - the top level map that was previously loaded // pszFileName - the absolute file name of the top level map file // Output : returns true if all submaps were loaded //----------------------------------------------------------------------------- bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName ) { entity_t *InstanceEntity; epair_t *pEPair; InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ]; pMapFile->num_entities++; memset( InstanceEntity, 0, sizeof( *InstanceEntity ) ); InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f ); pEPair = CreateEPair( "classname", "worldspawn" ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; for( int i = 0; i < m_Maps.Count(); i++ ) { // if ( m_Maps[ i ]->m_bTopLevelMap == false ) { char FileName[ MAX_PATH ]; sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName ); InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ]; pMapFile->num_entities++; memset( InstanceEntity, 0, sizeof( *InstanceEntity ) ); InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f ); pEPair = CreateEPair( "angles", "0 0 0" ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; char temp[ 128 ]; sprintf( temp, "%d", GameData::NAME_FIXUP_NONE ); pEPair = CreateEPair( "fixup_style", temp ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; pEPair = CreateEPair( "classname", "func_instance" ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; if ( m_Maps[ i ]->m_bTopLevelMap == true ) { pEPair = CreateEPair( "toplevel", "1" ); pEPair->next = InstanceEntity->epairs; InstanceEntity->epairs = pEPair; } } } return true; } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName ) { char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ]; DWORD UserNameSize; UserNameSize = sizeof( UserName ); if ( GetUserName( UserName, &UserNameSize ) == 0 ) { strcpy( UserPrefsFileName, "default" ); } sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName ); V_StripExtension( pszFileName, FileName, sizeof( FileName ) ); strcat( FileName, UserPrefsFileName ); FILE *fp = fopen( FileName, "rb" ); if ( !fp ) { return false; } CChunkFile File; ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read ); if ( eResult == ChunkFile_Ok ) { // // Set up handlers for the subchunks that we are interested in. // CChunkHandlerMap Handlers; Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this ); // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this); File.PushHandlers(&Handlers); while( eResult == ChunkFile_Ok ) { eResult = File.ReadChunk(); } if ( eResult == ChunkFile_EOF ) { eResult = ChunkFile_Ok; } File.PopHandlers(); } if ( eResult == ChunkFile_Ok ) { } else { // no pref message for now // GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION ); } return true; } //----------------------------------------------------------------------------- // Purpose: Loads a .VMM file. // Input : pszFileName - Full path of the map file to load. //----------------------------------------------------------------------------- bool CManifest::LoadVMFManifest( const char *pszFileName ) { V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) ); strcat( m_InstancePath, "\\" ); CChunkFile File; ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read ); if ( eResult != ChunkFile_Ok ) { g_MapError.ReportError( File.GetErrorText( eResult ) ); return false; } CChunkHandlerMap Handlers; Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this ); File.PushHandlers(&Handlers); while (eResult == ChunkFile_Ok) { eResult = File.ReadChunk(); } if (eResult == ChunkFile_EOF) { eResult = ChunkFile_Ok; } File.PopHandlers(); if ( eResult == ChunkFile_Ok ) { int index = g_Maps.AddToTail( new CMapFile() ); g_LoadingMap = g_Maps[ index ]; if ( g_MainMap == NULL ) { g_MainMap = g_LoadingMap; } LoadSubMaps( g_LoadingMap, pszFileName ); LoadVMFManifestUserPrefs( pszFileName ); } return ( eResult == ChunkFile_Ok ); } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- void CManifest::CordonWorld( ) { if ( m_bIsCordoning == false ) { return; } for ( int i = 0; i < g_MainMap->num_entities; i++ ) { if ( i == 0 ) { // for world spawn, we look at brushes for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ ) { int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum; bool bRemove = true; for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ ) { if ( m_Cordons[ nCordon ].m_bActive == false ) { continue; } for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ ) { if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true ) { bRemove = false; break; } } if ( bRemove == false ) { break; } } if ( bRemove ) { int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] ); memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize ); g_MainMap->entities[ i ].numbrushes--; nBrushNum--; } } } else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt ) { // for all other entities, even if they include brushes, we look at origin if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL ) { continue; } bool bRemove = true; for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ ) { if ( m_Cordons[ nCordon ].m_bActive == false ) { continue; } for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ ) { if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true ) { bRemove = false; break; } } if ( bRemove == false ) { break; } } if ( bRemove ) { g_MainMap->entities[ i ].numbrushes = 0; g_MainMap->entities[ i ].epairs = NULL; } } } if ( m_CordoningMapEnt ) { g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt ); m_CordoningMapEnt->numbrushes = 0; m_CordoningMapEnt->epairs = NULL; } }