Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "foundrydoc.h"
  9. #include "tier1/KeyValues.h"
  10. #include "tier1/utlbuffer.h"
  11. #include "datamodel/dmelement.h"
  12. #include "toolutils/enginetools_int.h"
  13. #include "filesystem.h"
  14. #include "foundrytool.h"
  15. #include "toolframework/ienginetool.h"
  16. #include "dmevmfentity.h"
  17. //-----------------------------------------------------------------------------
  18. // Constructor
  19. //-----------------------------------------------------------------------------
  20. CFoundryDoc::CFoundryDoc( IFoundryDocCallback *pCallback ) : m_pCallback( pCallback )
  21. {
  22. m_hRoot = NULL;
  23. m_pBSPFileName[0] = 0;
  24. m_pVMFFileName[0] = 0;
  25. m_bDirty = false;
  26. g_pDataModel->InstallNotificationCallback( this );
  27. }
  28. CFoundryDoc::~CFoundryDoc()
  29. {
  30. g_pDataModel->RemoveNotificationCallback( this );
  31. }
  32. //-----------------------------------------------------------------------------
  33. // Inherited from INotifyUI
  34. //-----------------------------------------------------------------------------
  35. void CFoundryDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
  36. {
  37. OnDataChanged( pReason, nNotifySource, nNotifyFlags );
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Gets the file name
  41. //-----------------------------------------------------------------------------
  42. const char *CFoundryDoc::GetBSPFileName()
  43. {
  44. return m_pBSPFileName;
  45. }
  46. const char *CFoundryDoc::GetVMFFileName()
  47. {
  48. return m_pVMFFileName;
  49. }
  50. void CFoundryDoc::SetVMFFileName( const char *pFileName )
  51. {
  52. Q_strncpy( m_pVMFFileName, pFileName, sizeof( m_pVMFFileName ) );
  53. Q_FixSlashes( m_pVMFFileName );
  54. SetDirty( true );
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Dirty bits
  58. //-----------------------------------------------------------------------------
  59. void CFoundryDoc::SetDirty( bool bDirty )
  60. {
  61. m_bDirty = bDirty;
  62. }
  63. bool CFoundryDoc::IsDirty() const
  64. {
  65. return m_bDirty;
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Saves/loads from file
  69. //-----------------------------------------------------------------------------
  70. bool CFoundryDoc::LoadFromFile( const char *pFileName )
  71. {
  72. Assert( !m_hRoot.Get() );
  73. // This is not undoable
  74. CAppDisableUndoScopeGuard guard( "CFoundryDoc::LoadFromFile", 0 );
  75. SetDirty( false );
  76. if ( !pFileName[0] )
  77. return false;
  78. // Store the BSP file name
  79. Q_strncpy( m_pBSPFileName, pFileName, sizeof( m_pBSPFileName ) );
  80. // Construct VMF file name from the BSP
  81. const char *pGame = Q_stristr( pFileName, "\\game\\" );
  82. if ( !pGame )
  83. return false;
  84. // Compute the map name
  85. char mapname[ 256 ];
  86. const char *pMaps = Q_stristr( pFileName, "\\maps\\" );
  87. if ( !pMaps )
  88. return false;
  89. Q_strncpy( mapname, pMaps + 6, sizeof( mapname ) );
  90. int nLen = (int)( (size_t)pGame - (size_t)pFileName ) + 1;
  91. Q_strncpy( m_pVMFFileName, pFileName, nLen );
  92. Q_strncat( m_pVMFFileName, "\\content\\", sizeof(m_pVMFFileName) );
  93. Q_strncat( m_pVMFFileName, pGame + 6, sizeof(m_pVMFFileName) );
  94. Q_SetExtension( m_pVMFFileName, ".vmf", sizeof(m_pVMFFileName) );
  95. CDmElement *pVMF = NULL;
  96. if ( g_pDataModel->RestoreFromFile( m_pVMFFileName, NULL, "vmf", &pVMF ) == DMFILEID_INVALID )
  97. {
  98. m_pBSPFileName[0] = 0;
  99. m_pVMFFileName[0] = 0;
  100. return false;
  101. }
  102. m_hRoot = pVMF;
  103. guard.Release();
  104. SetDirty( false );
  105. char cmd[ 256 ];
  106. Q_snprintf( cmd, sizeof( cmd ), "disconnect; map %s\n", mapname );
  107. enginetools->Command( cmd );
  108. enginetools->Execute( );
  109. return true;
  110. }
  111. void CFoundryDoc::SaveToFile( )
  112. {
  113. if ( m_hRoot.Get() && m_pVMFFileName && m_pVMFFileName[0] )
  114. {
  115. g_pDataModel->SaveToFile( m_pVMFFileName, NULL, "keyvalues", "vmf", m_hRoot );
  116. }
  117. SetDirty( false );
  118. }
  119. //-----------------------------------------------------------------------------
  120. // Returns the root object
  121. //-----------------------------------------------------------------------------
  122. CDmElement *CFoundryDoc::GetRootObject()
  123. {
  124. return m_hRoot;
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Returns the entity list
  128. //-----------------------------------------------------------------------------
  129. CDmAttribute *CFoundryDoc::GetEntityList()
  130. {
  131. return m_hRoot ? m_hRoot->GetAttribute( "entities", AT_ELEMENT_ARRAY ) : NULL;
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Deletes an entity
  135. //-----------------------------------------------------------------------------
  136. void CFoundryDoc::DeleteEntity( CDmeVMFEntity *pEntity )
  137. {
  138. CDmrElementArray<> entities( GetEntityList() );
  139. if ( !entities.IsValid() )
  140. return;
  141. int nCount = entities.Count();
  142. for ( int i = 0; i < nCount; ++i )
  143. {
  144. if ( pEntity == CastElement< CDmeVMFEntity >( entities[i] ) )
  145. {
  146. entities.FastRemove( i );
  147. return;
  148. }
  149. }
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Called when data changes
  153. //-----------------------------------------------------------------------------
  154. void CFoundryDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
  155. {
  156. SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false );
  157. m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags );
  158. }
  159. //-----------------------------------------------------------------------------
  160. // List of all entity classnames to copy over from the original block
  161. //-----------------------------------------------------------------------------
  162. static const char *s_pUseOriginalClasses[] =
  163. {
  164. "worldspawn",
  165. "func_occluder",
  166. NULL
  167. };
  168. //-----------------------------------------------------------------------------
  169. // Always copy the worldspawn and other entities that had data built into them by VBSP out
  170. //-----------------------------------------------------------------------------
  171. void CFoundryDoc::AddOriginalEntities( CUtlBuffer &entityBuf, const char *pActualEntityData )
  172. {
  173. while ( *pActualEntityData )
  174. {
  175. pActualEntityData = strchr( pActualEntityData, '{' );
  176. if ( !pActualEntityData )
  177. break;
  178. const char *pBlockStart = pActualEntityData;
  179. pActualEntityData = strstr( pActualEntityData, "\"classname\"" );
  180. if ( !pActualEntityData )
  181. break;
  182. // Skip "classname"
  183. pActualEntityData += 11;
  184. pActualEntityData = strchr( pActualEntityData, '\"' );
  185. if ( !pActualEntityData )
  186. break;
  187. // Skip "
  188. ++pActualEntityData;
  189. char pClassName[512];
  190. int j = 0;
  191. while (*pActualEntityData != 0 && *pActualEntityData != '\"' )
  192. {
  193. pClassName[j++] = *pActualEntityData++;
  194. }
  195. pClassName[j] = 0;
  196. pActualEntityData = strchr( pActualEntityData, '}' );
  197. if ( !pActualEntityData )
  198. break;
  199. // Skip }
  200. ++pActualEntityData;
  201. for ( int i = 0; s_pUseOriginalClasses[i]; ++i )
  202. {
  203. if ( !Q_stricmp( pClassName, s_pUseOriginalClasses[i] ) )
  204. {
  205. // Found one we need to keep, add it to the buffer
  206. int nBytes = (int)( (size_t)pActualEntityData - (size_t)pBlockStart );
  207. entityBuf.Put( pBlockStart, nBytes );
  208. entityBuf.PutChar( '\n' );
  209. break;
  210. }
  211. }
  212. }
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Copy in other entities from the editable VMF
  216. //-----------------------------------------------------------------------------
  217. void CFoundryDoc::AddVMFEntities( CUtlBuffer &entityBuf, const char *pActualEntityData )
  218. {
  219. const CDmrElementArray<CDmElement> entityArray( m_hRoot, "entities" );
  220. if ( !entityArray.IsValid() )
  221. return;
  222. int nCount = entityArray.Count();
  223. for ( int iEntity = 0; iEntity < nCount; ++iEntity )
  224. {
  225. CDmElement *pEntity = entityArray[iEntity];
  226. const char *pClassName = pEntity->GetValueString( "classname" );
  227. if ( !pClassName || !pClassName[0] )
  228. continue;
  229. // Don't spawn those classes we grab from the actual compiled map
  230. bool bDontUse = false;
  231. for ( int i = 0; s_pUseOriginalClasses[i]; ++i )
  232. {
  233. if ( !Q_stricmp( pClassName, s_pUseOriginalClasses[i] ) )
  234. {
  235. bDontUse = true;
  236. break;
  237. }
  238. }
  239. if ( bDontUse )
  240. continue;
  241. entityBuf.PutString( "{\n" );
  242. entityBuf.Printf( "\"id\" \"%d\"\n", atol( pEntity->GetName() ) );
  243. for( CDmAttribute *pAttribute = pEntity->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
  244. {
  245. if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
  246. continue;
  247. if ( IsArrayType( pAttribute->GetType() ) )
  248. continue;
  249. if ( !Q_stricmp( pAttribute->GetName(), "editorType" ) || !Q_stricmp( pAttribute->GetName(), "editor" ) )
  250. continue;
  251. entityBuf.Printf( "\"%s\" ", pAttribute->GetName() );
  252. // FIXME: Set up standard delimiters
  253. entityBuf.PutChar( '\"' );
  254. pAttribute->Serialize( entityBuf );
  255. entityBuf.PutString( "\"\n" );
  256. }
  257. entityBuf.PutString( "}\n" );
  258. }
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Create a text block the engine can parse containing the entity data to spawn
  262. //-----------------------------------------------------------------------------
  263. const char* CFoundryDoc::GenerateEntityData( const char *pActualEntityData )
  264. {
  265. if ( !m_hRoot.Get() )
  266. return pActualEntityData;
  267. // Contains the text block the engine can parse containing the entity data to spawn
  268. static CUtlBuffer entityBuf( 2048, 2048, CUtlBuffer::TEXT_BUFFER );
  269. entityBuf.Clear();
  270. // Always copy the worldspawn and other entities that had data built into them by VBSP out
  271. AddOriginalEntities( entityBuf, pActualEntityData );
  272. // Copy in other entities from the editable VMF
  273. AddVMFEntities( entityBuf, pActualEntityData );
  274. return (const char*)entityBuf.Base();
  275. }