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.

518 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // The copyright to the contents herein is the property of Valve, L.L.C.
  4. // The contents may be used and/or copied only with the written permission of
  5. // Valve, L.L.C., or in accordance with the terms and conditions stipulated in
  6. // the agreement/contract under which the contents have been supplied.
  7. //
  8. // $Header: $
  9. // $NoKeywords: $
  10. //
  11. //=============================================================================
  12. // Valve includes
  13. #include "appframework/tier2app.h"
  14. #include "datamodel/idatamodel.h"
  15. #include "filesystem.h"
  16. #include "icommandline.h"
  17. #include "mathlib/mathlib.h"
  18. #include "vstdlib/vstdlib.h"
  19. #include "tier2/p4helpers.h"
  20. #include "p4lib/ip4.h"
  21. #include "tier1/utlbuffer.h"
  22. #include "tier1/utlstringmap.h"
  23. #include "datamodel/dmelement.h"
  24. #ifdef _DEBUG
  25. #include <windows.h>
  26. #endif
  27. //-----------------------------------------------------------------------------
  28. // Standard spew functions
  29. //-----------------------------------------------------------------------------
  30. static SpewRetval_t SpewStdout( SpewType_t spewType, char const *pMsg )
  31. {
  32. if ( !pMsg )
  33. return SPEW_CONTINUE;
  34. #ifdef _DEBUG
  35. OutputDebugString( pMsg );
  36. #endif
  37. printf( pMsg );
  38. fflush( stdout );
  39. return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE;
  40. }
  41. //-----------------------------------------------------------------------------
  42. // The application object
  43. //-----------------------------------------------------------------------------
  44. class CPCFFixApp : public CTier2DmSteamApp
  45. {
  46. typedef CTier2DmSteamApp BaseClass;
  47. public:
  48. // Methods of IApplication
  49. virtual bool Create();
  50. virtual bool PreInit( );
  51. virtual int Main();
  52. virtual void Destroy() {}
  53. void PrintHelp( );
  54. private:
  55. void FixupPCFFile( CDmElement *pRoot );
  56. };
  57. DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CPCFFixApp );
  58. //-----------------------------------------------------------------------------
  59. // The application object
  60. //-----------------------------------------------------------------------------
  61. bool CPCFFixApp::Create()
  62. {
  63. SpewOutputFunc( SpewStdout );
  64. AppSystemInfo_t appSystems[] =
  65. {
  66. { "p4lib.dll", P4_INTERFACE_VERSION },
  67. { "", "" } // Required to terminate the list
  68. };
  69. AddSystems( appSystems );
  70. return true;
  71. }
  72. //-----------------------------------------------------------------------------
  73. //
  74. //-----------------------------------------------------------------------------
  75. bool CPCFFixApp::PreInit( )
  76. {
  77. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  78. if ( !BaseClass::PreInit() )
  79. return false;
  80. if ( !g_pFullFileSystem || !g_pDataModel )
  81. {
  82. Error( "// ERROR: pcffix is missing a required interface!\n" );
  83. return false;
  84. }
  85. // Add paths...
  86. if ( !SetupSearchPaths( NULL, false, true ) )
  87. return false;
  88. return true;
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Returns true if an element is equal
  92. //-----------------------------------------------------------------------------
  93. static bool IsScalarAttributesEqual( CDmElement *pElement1, CDmElement *pElement2 )
  94. {
  95. int nCount = pElement1->AttributeCount();
  96. if ( nCount != pElement2->AttributeCount() )
  97. return false;
  98. for ( CDmAttribute *pAtt1 = pElement1->FirstAttribute(); pAtt1; pAtt1 = pAtt1->NextAttribute() )
  99. {
  100. CDmAttribute *pAtt2 = pElement2->GetAttribute( pAtt1->GetName() );
  101. if ( !pAtt2 )
  102. return false;
  103. if ( pAtt1->GetType() != pAtt2->GetType() )
  104. return false;
  105. if ( IsArrayType( pAtt1->GetType() ) )
  106. continue;
  107. switch( pAtt1->GetType() )
  108. {
  109. case AT_FLOAT:
  110. if ( pAtt1->GetValue<float>() != pAtt2->GetValue<float>() )
  111. return false;
  112. break;
  113. case AT_BOOL:
  114. if ( pAtt1->GetValue<bool>() != pAtt2->GetValue<bool>() )
  115. return false;
  116. break;
  117. case AT_INT:
  118. if ( pAtt1->GetValue<int>() != pAtt2->GetValue<int>() )
  119. return false;
  120. break;
  121. case AT_STRING:
  122. if ( Q_stricmp( pAtt1->GetValueString(), pAtt2->GetValueString() ) )
  123. return false;
  124. break;
  125. case AT_VECTOR2:
  126. if ( pAtt1->GetValue<Vector2D>() != pAtt2->GetValue<Vector2D>() )
  127. return false;
  128. break;
  129. case AT_VECTOR3:
  130. if ( pAtt1->GetValue<Vector>() != pAtt2->GetValue<Vector>() )
  131. return false;
  132. break;
  133. case AT_VECTOR4:
  134. if ( pAtt1->GetValue<Vector4D>() != pAtt2->GetValue<Vector4D>() )
  135. return false;
  136. break;
  137. case AT_COLOR:
  138. if ( pAtt1->GetValue<Color>() != pAtt2->GetValue<Color>() )
  139. return false;
  140. break;
  141. }
  142. }
  143. return true;
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Returns true if a array attribute is equal
  147. //-----------------------------------------------------------------------------
  148. static bool IsArrayEqual( CDmElement *pElement1, CDmElement *pElement2, const char *pArrayAttribute )
  149. {
  150. CDmrElementArray<> arr1( pElement1, pArrayAttribute );
  151. CDmrElementArray<> arr2( pElement2, pArrayAttribute );
  152. int nCount = arr1.IsValid() ? arr1.Count() : 0;
  153. int nCount2 = arr2.IsValid() ? arr2.Count() : 0;
  154. if ( nCount != nCount2 )
  155. return false;
  156. for ( int i = 0; i < nCount; ++i )
  157. {
  158. if ( !IsScalarAttributesEqual( arr1[i], arr2[i] ) )
  159. return false;
  160. }
  161. return true;
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Returns true if a def is equal
  165. //-----------------------------------------------------------------------------
  166. static bool IsDefEqual( CDmElement *pElement1, CDmElement *pElement2 )
  167. {
  168. if ( !IsScalarAttributesEqual( pElement1, pElement2 ) )
  169. return false;
  170. if ( !IsArrayEqual( pElement1, pElement2, "renderer" ) )
  171. return false;
  172. if ( !IsArrayEqual( pElement1, pElement2, "operator" ) )
  173. return false;
  174. if ( !IsArrayEqual( pElement1, pElement2, "initializer" ) )
  175. return false;
  176. if ( !IsArrayEqual( pElement1, pElement2, "emitter" ) )
  177. return false;
  178. if ( !IsArrayEqual( pElement1, pElement2, "forcegenerator" ) )
  179. return false;
  180. if ( !IsArrayEqual( pElement1, pElement2, "constraint" ) )
  181. return false;
  182. if ( !IsArrayEqual( pElement1, pElement2, "children" ) )
  183. return false;
  184. CDmrElementArray<> arr1( pElement1, "children" );
  185. CDmrElementArray<> arr2( pElement2, "children" );
  186. int nCount = arr1.IsValid() ? arr1.Count() : 0;
  187. int nCount2 = arr2.IsValid() ? arr2.Count() : 0;
  188. if ( nCount != nCount2 )
  189. return false;
  190. for ( int i = 0; i < nCount; ++i )
  191. {
  192. CDmElement *pChild1 = arr1[i]->GetValueElement<CDmElement>( "child" );
  193. CDmElement *pChild2 = arr2[i]->GetValueElement<CDmElement>( "child" );
  194. if ( !pChild1 || !pChild2 )
  195. {
  196. if ( pChild1 || pChild2 )
  197. return false;
  198. continue;
  199. }
  200. if ( !IsDefEqual( pChild1, pChild2 ) )
  201. return false;
  202. }
  203. return true;
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Finds or adds unique defs
  207. //-----------------------------------------------------------------------------
  208. static CDmElement* FindOrAddDef( CDmElement *pDef, CUtlVector< CDmElement* > &defs, CUtlStringMap< int > &uniqueNames )
  209. {
  210. int nCount = defs.Count();
  211. for ( int i = 0; i < nCount; ++i )
  212. {
  213. if ( Q_stricmp( pDef->GetName(), defs[i]->GetName() ) )
  214. continue;
  215. if ( IsDefEqual( pDef, defs[i] ) )
  216. return defs[i];
  217. char pTemp[128], pTemp2[128];
  218. UniqueIdToString( pDef->GetId(), pTemp, sizeof(pTemp) );
  219. UniqueIdToString( defs[i]->GetId(), pTemp2, sizeof(pTemp2) );
  220. Warning( "WARNING: Discovered duplicated systems with different values!\n" );
  221. Warning( "\tName %s\n\tid #1 %s\n\tid #2 %s\n", pDef->GetName(), pTemp, pTemp2 );
  222. }
  223. defs.AddToTail( pDef );
  224. if ( !uniqueNames.Defined( pDef->GetName() ) )
  225. {
  226. uniqueNames[ pDef->GetName() ] = 1;
  227. }
  228. else
  229. {
  230. int nCopyCount = ++uniqueNames[ pDef->GetName() ];
  231. char pNewName[512];
  232. Q_snprintf( pNewName, sizeof(pNewName), "%s Version #%d", pDef->GetName(), nCopyCount );
  233. pDef->SetName( pNewName );
  234. }
  235. return pDef;
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Replace references
  239. //-----------------------------------------------------------------------------
  240. static void ReplaceChildReferences( CDmElement *pElement, CDmElement *pOldVersion, CDmElement *pNewVersion )
  241. {
  242. for ( CDmAttribute *pAtt = pElement->FirstAttribute(); pAtt; pAtt = pAtt->NextAttribute() )
  243. {
  244. if ( pAtt->GetType() == AT_ELEMENT )
  245. {
  246. CDmElement *pCurrent = pAtt->GetValueElement<CDmElement>();
  247. if ( pCurrent == pOldVersion )
  248. {
  249. pAtt->SetValue( pNewVersion );
  250. }
  251. else
  252. {
  253. ReplaceChildReferences( pCurrent, pOldVersion, pNewVersion );
  254. }
  255. continue;
  256. }
  257. if ( pAtt->GetType() == AT_ELEMENT_ARRAY )
  258. {
  259. CDmrElementArray<> arr( pAtt );
  260. int nCount = arr.Count();
  261. for ( int i = 0; i < nCount; ++i )
  262. {
  263. if ( arr[i] == pOldVersion )
  264. {
  265. arr.Set( i, pNewVersion );
  266. }
  267. else
  268. {
  269. ReplaceChildReferences( arr[i], pOldVersion, pNewVersion );
  270. }
  271. }
  272. continue;
  273. }
  274. }
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Add unique particle defs
  278. //-----------------------------------------------------------------------------
  279. static void AddUniqueElementsToList( CDmElement *pElement, CUtlVector<CDmElement*> &list )
  280. {
  281. Assert( !Q_stricmp( pElement->GetTypeString(), "DmeParticleSystemDefinition" ) );
  282. int nCount = list.Count();
  283. for ( int i = 0; i < nCount; ++i )
  284. {
  285. if ( IsUniqueIdEqual( pElement->GetId(), list[i]->GetId() ) )
  286. {
  287. if ( pElement != list[i] )
  288. {
  289. Warning( "Encountered two different elements with the same unique id!!\n" );
  290. Warning( "\tElement #1: %s Element #2: %s\n", pElement->GetName(), list[i]->GetName() );
  291. }
  292. return;
  293. }
  294. }
  295. list.AddToTail( pElement );
  296. CDmrElementArray<> children( pElement, "children" );
  297. if ( !children.IsValid() )
  298. return;
  299. int nChildCount = children.Count();
  300. for ( int i = 0; i < nChildCount; ++i )
  301. {
  302. CDmElement *pChild = children[i]->GetValueElement<CDmElement>( "child" );
  303. if ( !pChild )
  304. continue;
  305. AddUniqueElementsToList( pChild, list );
  306. }
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Replace references
  310. //-----------------------------------------------------------------------------
  311. static void BuildStartingDefList( CDmElement *pElement, CDmElement *pOldVersion, CDmElement *pNewVersion )
  312. {
  313. for ( CDmAttribute *pAtt = pElement->FirstAttribute(); pAtt; pAtt = pAtt->NextAttribute() )
  314. {
  315. if ( pAtt->GetType() == AT_ELEMENT )
  316. {
  317. CDmElement *pCurrent = pAtt->GetValueElement<CDmElement>();
  318. if ( pCurrent == pOldVersion )
  319. {
  320. pAtt->SetValue( pNewVersion );
  321. }
  322. else
  323. {
  324. ReplaceChildReferences( pCurrent, pOldVersion, pNewVersion );
  325. }
  326. continue;
  327. }
  328. if ( pAtt->GetType() == AT_ELEMENT_ARRAY )
  329. {
  330. CDmrElementArray<> arr( pAtt );
  331. int nCount = arr.Count();
  332. for ( int i = 0; i < nCount; ++i )
  333. {
  334. if ( arr[i] == pOldVersion )
  335. {
  336. arr.Set( i, pNewVersion );
  337. }
  338. else
  339. {
  340. ReplaceChildReferences( arr[i], pOldVersion, pNewVersion );
  341. }
  342. }
  343. continue;
  344. }
  345. }
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Fixes up a PCF file
  349. //-----------------------------------------------------------------------------
  350. void CPCFFixApp::FixupPCFFile( CDmElement *pRoot )
  351. {
  352. CUtlVector< CDmElement* > startingDefs;
  353. CUtlVector< CDmElement* > defs;
  354. CUtlStringMap< int > uniqueNames;
  355. CDmrElementArray<> rootDefs( pRoot, "particleSystemDefinitions" );
  356. int nCount = rootDefs.Count();
  357. for ( int i = 0; i < nCount; ++i )
  358. {
  359. AddUniqueElementsToList( rootDefs[i], startingDefs );
  360. }
  361. nCount = startingDefs.Count();
  362. for ( int i = 0; i < nCount; ++i )
  363. {
  364. CDmElement *pActualDef = FindOrAddDef( startingDefs[i], defs, uniqueNames );
  365. if ( pActualDef == startingDefs[i] )
  366. continue;
  367. // Now replace all child references to startingDefs[i] with references to pActualDef instead.
  368. ReplaceChildReferences( pRoot, startingDefs[i], pActualDef );
  369. }
  370. // Reconstruct the definition list based on the actual unique ones
  371. rootDefs.RemoveAll();
  372. int nUniqueCount = defs.Count();
  373. for ( int i = 0; i < nUniqueCount; ++i )
  374. {
  375. rootDefs.AddToTail( defs[i] );
  376. }
  377. if ( nCount != nUniqueCount )
  378. {
  379. Warning( "*** Removed %d duplicated particle systems!\n", nCount - nUniqueCount );
  380. }
  381. else
  382. {
  383. Warning( "Removed no duplicated particle systems. This file started out ok.\n" );
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Print help
  388. //-----------------------------------------------------------------------------
  389. void CPCFFixApp::PrintHelp( )
  390. {
  391. Msg( "Usage: pcffix -i <in .pcf file> [-nop4]\n" );
  392. Msg( "\t-i\t: Source .PCF file to fix up (eliminate copied particle system definitions.)\n" );
  393. Msg( "\t-nop4\t: Disables auto perforce checkout/add.\n" );
  394. Msg( "\t-vproject\t: Specifies path to a gameinfo.txt file (which mod to build for).\n" );
  395. }
  396. //-----------------------------------------------------------------------------
  397. // The application object
  398. //-----------------------------------------------------------------------------
  399. int CPCFFixApp::Main()
  400. {
  401. g_pDataModel->SetUndoEnabled( false );
  402. g_pDataModel->OnlyCreateUntypedElements( true );
  403. g_pDataModel->SetDefaultElementFactory( NULL );
  404. // This bit of hackery allows us to access files on the harddrive
  405. g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
  406. if ( CommandLine()->CheckParm( "-h" ) || CommandLine()->CheckParm( "-help" ) )
  407. {
  408. PrintHelp();
  409. return 0;
  410. }
  411. // Do Perforce Stuff
  412. if ( CommandLine()->FindParm( "-nop4" ) )
  413. {
  414. g_p4factory->SetDummyMode( true );
  415. }
  416. g_p4factory->SetOpenFileChangeList( "Fixed PCF files" );
  417. const char *pPCFFile = CommandLine()->ParmValue( "-i" );
  418. if ( !pPCFFile )
  419. {
  420. PrintHelp();
  421. return 0;
  422. }
  423. CDmElement *pRoot;
  424. if ( g_pDataModel->RestoreFromFile( pPCFFile, NULL, "pcf", &pRoot, CR_DELETE_NEW ) == DMFILEID_INVALID )
  425. {
  426. Error( "Encountered an error reading file \"%s\"!\n", pPCFFile );
  427. return -1;
  428. }
  429. FixupPCFFile( pRoot );
  430. CP4AutoEditFile checkout( pPCFFile );
  431. const char *pOutEncoding = g_pDataModel->GetDefaultEncoding( "pcf" );
  432. if ( !g_pDataModel->SaveToFile( pPCFFile, NULL, pOutEncoding, "pcf", pRoot ) )
  433. {
  434. Error( "Encountered an error writing file \"%s\"!\n", pPCFFile );
  435. return -1;
  436. }
  437. g_pDataModel->RemoveFileId( pRoot->GetFileId() );
  438. return -1;
  439. }