Counter Strike : Global Offensive Source Code
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.

1939 lines
53 KiB

  1. //========= Copyright 1996-2006, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: VPC
  4. //
  5. //=====================================================================================//
  6. #include "vpc.h"
  7. #include "tier1/utldict.h"
  8. #include "tier1/keyvalues.h"
  9. #include "baseprojectdatacollector.h"
  10. void VPC_ParseFileSection( void )
  11. {
  12. while ( 1 )
  13. {
  14. const char *pToken = g_pVPC->GetScript().GetToken( true );
  15. if ( !pToken || !pToken[0] )
  16. break;
  17. if ( !V_stricmp( pToken, "}" ) )
  18. {
  19. // end of section
  20. break;
  21. }
  22. if ( !V_stricmp( pToken, "$configuration" ) )
  23. {
  24. VPC_Keyword_FileConfiguration( );
  25. }
  26. }
  27. }
  28. //-----------------------------------------------------------------------------
  29. // VPC_TrackSchemaFile
  30. //
  31. //-----------------------------------------------------------------------------
  32. void VPC_TrackSchemaFile( const char *pName, bool bRemove, const char *pFileFlag )
  33. {
  34. #ifdef STEAM
  35. return;
  36. #else
  37. if ( !bRemove && (!pFileFlag || !V_stristr( pFileFlag, "schema" )) )
  38. {
  39. // adding something that's not schema - ignore
  40. return;
  41. }
  42. for ( int i = 0; i < g_pVPC->m_SchemaFiles.Count(); i++ )
  43. {
  44. if ( !g_pVPC->m_SchemaFiles[i].String() )
  45. continue;
  46. if ( !V_stricmp( pName, g_pVPC->m_SchemaFiles[i].String() ) )
  47. {
  48. if ( bRemove )
  49. {
  50. g_pVPC->m_SchemaFiles.Remove( i );
  51. }
  52. return;
  53. }
  54. }
  55. if ( bRemove )
  56. {
  57. // not found, nothing to do
  58. return;
  59. }
  60. g_pVPC->m_SchemaFiles.AddToTail( pName );
  61. // suppress building of schematized cpp files
  62. // (they get #included by an auto-generated cpp that is built instead)
  63. const char *pExt = V_GetFileExtension(pName);
  64. if ( pExt && !V_stricmp( pExt, "cpp" ) )
  65. {
  66. CUtlVector< CUtlString > configurationNames;
  67. g_pVPC->GetProjectGenerator()->GetAllConfigurationNames( configurationNames );
  68. for ( int i=0; i < configurationNames.Count(); i++ )
  69. {
  70. g_pVPC->GetProjectGenerator()->StartConfigurationBlock( configurationNames[i].String(), true );
  71. g_pVPC->GetProjectGenerator()->FileIsSchema( true );
  72. g_pVPC->GetProjectGenerator()->FileExcludedFromBuild( true );
  73. g_pVPC->GetProjectGenerator()->EndConfigurationBlock();
  74. }
  75. }
  76. #endif
  77. }
  78. //-----------------------------------------------------------------------------
  79. // VPC_Keyword_AddFile
  80. //
  81. //-----------------------------------------------------------------------------
  82. void VPC_Keyword_AddFile( const char *pFileFlag = NULL )
  83. {
  84. bool bAllowNextLine = false;
  85. bool bResult = true;
  86. CUtlVector<CUtlString> files;
  87. while ( 1 )
  88. {
  89. const char *pToken = g_pVPC->GetScript().GetToken( bAllowNextLine );
  90. if ( !pToken || !pToken[0] )
  91. break;
  92. // Is this a conditional expression?
  93. if ( pToken[0] == '[' )
  94. {
  95. if ( files.Count() == 0 )
  96. {
  97. g_pVPC->VPCSyntaxError( "Conditional specified on a $File without any file preceding it." );
  98. }
  99. if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
  100. {
  101. files.Remove( files.Count() - 1 );
  102. }
  103. continue;
  104. }
  105. char szFilename[MAX_PATH];
  106. g_pVPC->ResolveMacrosInString( pToken, szFilename, sizeof( szFilename ) );
  107. if ( !V_stricmp( pToken, "\\" ) )
  108. {
  109. bAllowNextLine = true;
  110. continue;
  111. }
  112. else
  113. {
  114. bAllowNextLine = false;
  115. }
  116. V_FixSlashes( szFilename );
  117. CUtlString string = szFilename;
  118. files.AddToTail( string );
  119. // check for another optional file
  120. pToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
  121. if ( !pToken || !pToken[0] )
  122. break;
  123. }
  124. if ( !bResult )
  125. {
  126. // files have been conditionally removed
  127. files.Purge();
  128. }
  129. // check for optional section
  130. bool bHasSection = false;
  131. const char *pToken = g_pVPC->GetScript().PeekNextToken( true );
  132. if ( pToken && pToken[0] && !V_stricmp( pToken, "{" ) )
  133. {
  134. bHasSection = true;
  135. }
  136. // dynamic files need to opt out of strict file presence check
  137. bool bDynamicFile = pFileFlag && V_stristr( pFileFlag, "dynamic" );
  138. // need to check files early to handle possible rejected section
  139. if ( g_pVPC->IsCheckFiles() && !bDynamicFile )
  140. {
  141. for ( int i=0; i<files.Count(); i++ )
  142. {
  143. const char *pFilename = files[i].String();
  144. if ( !Sys_Exists( pFilename ) )
  145. {
  146. g_pVPC->VPCWarning( "File '%s' does not exist. Not adding to project.", pFilename );
  147. files.Remove( i );
  148. }
  149. }
  150. }
  151. if ( g_pVPC->IsShowCaseIssues() && !bDynamicFile )
  152. {
  153. for ( int i = 0; i < files.Count(); i++ )
  154. {
  155. const char *pFilename = files[i].String();
  156. char actualFilename[MAX_PATH];
  157. if ( !Sys_IsFilenameCaseConsistent( pFilename, actualFilename, sizeof( actualFilename ) ) )
  158. {
  159. g_pVPC->VPCWarning( "Case Consistency Issue! File '%s' specified in '%s' is inconsistent with OS version '%s'.", pFilename, g_pVPC->GetProjectName(), actualFilename );
  160. // need script stack to assist in tracking down missing file
  161. g_pVPC->GetScript().SpewScriptStack();
  162. }
  163. }
  164. }
  165. if ( !files.Count() && bHasSection )
  166. {
  167. // optional section has been conditionally removed
  168. g_pVPC->GetScript().SkipBracedSection();
  169. return;
  170. }
  171. if ( bHasSection )
  172. {
  173. // found optional section, parse opening brace
  174. pToken = g_pVPC->GetScript().GetToken( true );
  175. if ( !pToken || !pToken[0] || V_stricmp( pToken, "{" ) )
  176. g_pVPC->VPCSyntaxError();
  177. }
  178. // save parser state
  179. CScriptSource startingScriptSource = g_pVPC->GetScript().GetCurrentScript();
  180. for ( int i=0; i<files.Count(); i++ )
  181. {
  182. const char *pFilename = files[i].String();
  183. bool bAdded = g_pVPC->GetProjectGenerator()->StartFile( pFilename, true );
  184. // Lookup extension for a custom build script
  185. const char *pExtension = V_GetFileExtension( pFilename );
  186. if ( !pExtension )
  187. {
  188. pExtension = "";
  189. }
  190. int index = g_pVPC->m_CustomBuildSteps.Find( pExtension );
  191. if ( g_pVPC->m_CustomBuildSteps.IsValidIndex( index ) )
  192. {
  193. CUtlString buildsteps = g_pVPC->m_CustomBuildSteps[index];
  194. const char *pBuffer = buildsteps.Get();
  195. CUtlString scriptName;
  196. scriptName = pExtension;
  197. scriptName += " custom build step";
  198. // save parser state
  199. g_pVPC->GetScript().PushScript( g_pVPC->GetScript().GetName(), pBuffer );
  200. // parse injected buildstep
  201. VPC_ParseFileSection();
  202. // restore parser state
  203. g_pVPC->GetScript().PopScript();
  204. g_pVPC->GetScript().RestoreScript( startingScriptSource );
  205. }
  206. // apply optional section to each file
  207. if ( bHasSection && bAdded )
  208. {
  209. // restore parser state
  210. g_pVPC->GetScript().RestoreScript( startingScriptSource );
  211. VPC_ParseFileSection();
  212. }
  213. VPC_TrackSchemaFile( pFilename, false, pFileFlag );
  214. g_pVPC->GetProjectGenerator()->EndFile();
  215. }
  216. }
  217. // parse a list of filenames from the current token, handling conditionals, macro expansion, \\, fixslashes, etc
  218. static void VPC_ParseFileList( CUtlStringList &files )
  219. {
  220. bool bAllowNextLine = false;
  221. while ( 1 )
  222. {
  223. const char *pToken = g_pVPC->GetScript().GetToken( bAllowNextLine );
  224. if ( !pToken || !pToken[0] )
  225. g_pVPC->VPCSyntaxError();
  226. const char *pNextToken = g_pVPC->GetScript().PeekNextToken( false );
  227. if ( !pNextToken || !pNextToken[0] )
  228. {
  229. // current token is last token
  230. // last token can be optional conditional, need to identify
  231. // backup and reparse up to last token
  232. if ( pToken && pToken[0] == '[' )
  233. {
  234. if ( files.Count() == 0 )
  235. {
  236. g_pVPC->VPCSyntaxError( "Conditional specified on a file list without any file preceding it." );
  237. }
  238. // last token is an optional conditional
  239. bool bResult = g_pVPC->EvaluateConditionalExpression( pToken );
  240. if ( !bResult ) // was conditional false?
  241. {
  242. files.PurgeAndDeleteElements();
  243. }
  244. return;
  245. }
  246. }
  247. char szFilename[MAX_PATH];
  248. g_pVPC->ResolveMacrosInString( pToken, szFilename, sizeof( szFilename ) );
  249. V_FixSlashes( szFilename );
  250. if ( !V_stricmp( pToken, "\\" ) )
  251. {
  252. bAllowNextLine = true;
  253. continue;
  254. }
  255. else
  256. {
  257. bAllowNextLine = false;
  258. }
  259. files.CopyAndAddToTail( szFilename );
  260. // check for another optional file
  261. pToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
  262. if ( !pToken || !pToken[0] )
  263. break;
  264. }
  265. }
  266. // add or remove .lib or dll import files, automatically adding prefixes and suffices to the name
  267. static void VPC_HandleLibraryExpansion( char const *pDefaultPath, char const *pFileNamePrefix, char const *pSuffix, bool bRemove )
  268. {
  269. if ( !pFileNamePrefix )
  270. {
  271. pFileNamePrefix = "";
  272. }
  273. CUtlStringList impFiles;
  274. VPC_ParseFileList( impFiles );
  275. for( int i = 0; i < impFiles.Count(); i++ )
  276. {
  277. char szFilename[MAX_PATH];
  278. char const *pPathPrefixToUse = pDefaultPath;
  279. // do not add the path prefix if the filename contains path information already, or if null was passed.
  280. char impFile[MAX_PATH];
  281. V_strncpy( impFile, impFiles[i], sizeof( impFile ) );
  282. V_RemoveDotSlashes( impFile );
  283. char *pLastSlash = (char*)MAX( strrchr( impFile, '\\' ), strrchr( impFile, '/' ) );
  284. if ( pLastSlash )
  285. {
  286. *pLastSlash = 0;
  287. const char *pFilenamePart = pLastSlash + 1;
  288. sprintf( szFilename, "%s/%s%s%s", impFile, pFileNamePrefix, pFilenamePart, pSuffix );
  289. }
  290. else
  291. {
  292. sprintf( szFilename, "%s%s%s%s", pPathPrefixToUse, pFileNamePrefix, impFiles[i], pSuffix );
  293. }
  294. char szFilename1[MAX_PATH];
  295. g_pVPC->ResolveMacrosInString( szFilename, szFilename1, sizeof( szFilename1 ) );
  296. // Replace forward slashes with backslashes regardless of target platform
  297. V_FixSlashes( szFilename1 );
  298. V_RemoveDotSlashes( szFilename1 );
  299. if ( bRemove )
  300. {
  301. bool bSucc = g_pVPC->GetProjectGenerator()->RemoveFile( szFilename1 );
  302. if ( !bSucc )
  303. {
  304. g_pVPC->VPCError( "Broken $implib command. Failed to remove file %s from project.", szFilename1 );
  305. }
  306. }
  307. else
  308. {
  309. bool bAdded = g_pVPC->GetProjectGenerator()->StartFile( szFilename1, true );
  310. if ( !bAdded )
  311. {
  312. g_pVPC->VPCError( "couldn't add %s", szFilename1 );
  313. }
  314. g_pVPC->GetProjectGenerator()->EndFile();
  315. }
  316. }
  317. }
  318. static void VPC_Keyword_ImportLibrary( bool bRemove = false )
  319. {
  320. VPC_HandleLibraryExpansion( "$LIBPUBLIC\\", "$_IMPLIB_PREFIX", "$_IMPLIB_EXT", bRemove );
  321. }
  322. static void VPC_Keyword_LinkerLibrary( bool bRemove = false )
  323. {
  324. VPC_HandleLibraryExpansion( "$LIBPUBLIC\\", NULL, "$_STATICLIB_EXT", bRemove );
  325. }
  326. //-----------------------------------------------------------------------------
  327. // VPC_Keyword_RemoveFile
  328. //
  329. //-----------------------------------------------------------------------------
  330. void VPC_Keyword_RemoveFile()
  331. {
  332. CUtlStringList filesToRemove;
  333. VPC_ParseFileList( filesToRemove );
  334. for ( int i = 0; i < filesToRemove.Count(); i++ )
  335. {
  336. bool bSucc = g_pVPC->GetProjectGenerator()->RemoveFile( filesToRemove[i] );
  337. if ( !bSucc )
  338. {
  339. g_pVPC->VPCWarning( "Failed to remove file %s from project", filesToRemove[i] );
  340. }
  341. VPC_TrackSchemaFile( filesToRemove[i], true, NULL );
  342. }
  343. }
  344. //-----------------------------------------------------------------------------
  345. // VPC_Keyword_Folder
  346. //
  347. //-----------------------------------------------------------------------------
  348. void VPC_Keyword_Folder()
  349. {
  350. const char *pToken;
  351. char folderName[MAX_PATH];
  352. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, folderName, sizeof( folderName ) ) )
  353. {
  354. g_pVPC->GetScript().SkipBracedSection();
  355. return;
  356. }
  357. g_pVPC->GetProjectGenerator()->StartFolder( folderName );
  358. // Now parse all the files and subfolders..
  359. pToken = g_pVPC->GetScript().GetToken( true );
  360. if ( !pToken || !pToken[0] || V_stricmp( pToken, "{" ) )
  361. g_pVPC->VPCSyntaxError();
  362. while ( 1 )
  363. {
  364. pToken = g_pVPC->GetScript().GetToken( true );
  365. if ( !pToken || !pToken[0] )
  366. break;
  367. if ( !V_stricmp( pToken, "}" ) )
  368. {
  369. // pop
  370. break;
  371. }
  372. else if ( !V_stricmp( pToken, "$file" ) )
  373. {
  374. // add file
  375. VPC_Keyword_AddFile();
  376. }
  377. else if ( !V_stricmp( pToken, "$DynamicFile" ) )
  378. {
  379. // add file
  380. VPC_Keyword_AddFile( "dynamic" );
  381. }
  382. else if ( !V_stricmp( pToken, "$schemafile" ) )
  383. {
  384. // add file
  385. VPC_Keyword_AddFile( "schema" );
  386. }
  387. else if ( !V_stricmp( pToken, "$implib" ) )
  388. {
  389. // add file
  390. VPC_Keyword_ImportLibrary();
  391. }
  392. else if ( !V_stricmp( pToken, "-$implib" ) )
  393. {
  394. // remove file
  395. VPC_Keyword_ImportLibrary( true );
  396. }
  397. else if ( !V_stricmp( pToken, "$lib" ) )
  398. {
  399. // add file
  400. VPC_Keyword_LinkerLibrary();
  401. }
  402. else if ( !V_stricmp( pToken, "-$lib" ) )
  403. {
  404. // remove file
  405. VPC_Keyword_LinkerLibrary( true );
  406. }
  407. else if ( !V_stricmp( pToken, "-$file" ) )
  408. {
  409. // remove file
  410. VPC_Keyword_RemoveFile();
  411. }
  412. else if ( !V_stricmp( pToken, "$folder" ) )
  413. {
  414. // descend into subdirectory
  415. VPC_Keyword_Folder();
  416. }
  417. else
  418. {
  419. g_pVPC->VPCSyntaxError();
  420. }
  421. }
  422. g_pVPC->GetProjectGenerator()->EndFolder();
  423. }
  424. //-----------------------------------------------------------------------------
  425. // VPC_Keyword_Shaders
  426. //
  427. //-----------------------------------------------------------------------------
  428. void VPC_Keyword_Shaders( int depth )
  429. {
  430. const char *pToken;
  431. char shadersName[MAX_PATH];
  432. CUtlBuffer vpcBuffer;
  433. CUtlVector< CUtlString > fxcList;
  434. CUtlVector< CUtlString > vshList;
  435. CUtlVector< CUtlString > pshList;
  436. CUtlVector< CUtlString > vfxList;
  437. CUtlVector< CUtlString > otherList;
  438. int i;
  439. bool bIgnoreRedundancyWarning;
  440. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, shadersName, sizeof( shadersName ) ) )
  441. {
  442. return;
  443. }
  444. g_pVPC->VPCStatus( false, "Parsing: %s", shadersName );
  445. g_pVPC->GetScript().PushScript( shadersName );
  446. // parse the shader list file into types (fxc,vsh,psh)
  447. while ( 1 )
  448. {
  449. pToken = g_pVPC->GetScript().GetToken( true );
  450. if ( !pToken || !pToken[0] )
  451. {
  452. // end of file
  453. break;
  454. }
  455. if ( V_stristr( pToken, ".fxc" ) )
  456. {
  457. fxcList.AddToTail( pToken );
  458. }
  459. else if ( V_stristr( pToken, ".vsh" ) )
  460. {
  461. vshList.AddToTail( pToken );
  462. }
  463. else if ( V_stristr( pToken, ".psh" ) )
  464. {
  465. pshList.AddToTail( pToken );
  466. }
  467. else if ( V_stristr( pToken, ".vfx" ) )
  468. {
  469. vfxList.AddToTail( pToken );
  470. }
  471. else
  472. {
  473. otherList.AddToTail( pToken );
  474. }
  475. }
  476. g_pVPC->GetScript().PopScript();
  477. if ( !fxcList.Count() &&
  478. !vshList.Count() &&
  479. !pshList.Count() &&
  480. !vfxList.Count() &&
  481. !otherList.Count() )
  482. {
  483. g_pVPC->VPCWarning( "No shaders found in %s", shadersName );
  484. return;
  485. }
  486. // generate a vpc compatible file to generate the shader file hierarchy
  487. vpcBuffer.SetBufferType( true, true );
  488. vpcBuffer.Printf( "$Folder \"Shader Source\" \n" );
  489. vpcBuffer.Printf( "{\n" );
  490. // add the shader file as a convienence
  491. vpcBuffer.Printf( "$file \"%s\"\n", shadersName );
  492. vpcBuffer.Printf( "{\n" );
  493. vpcBuffer.Printf( "$Configuration\n" );
  494. vpcBuffer.Printf( "{\n" );
  495. vpcBuffer.Printf( "$ExcludedFromBuild \"Yes\"\n" );
  496. vpcBuffer.Printf( "}\n" );
  497. vpcBuffer.Printf( "}\n" );
  498. // fxc files
  499. if ( fxcList.Count() )
  500. {
  501. vpcBuffer.Printf( "$Folder \"fxc\" \n" );
  502. vpcBuffer.Printf( "{\n" );
  503. for ( i=0; i<fxcList.Count(); i++ )
  504. {
  505. vpcBuffer.Printf( "$file \"%s\"\n", fxcList[i].String() );
  506. vpcBuffer.Printf( "{\n" );
  507. vpcBuffer.Printf( "$Configuration\n" );
  508. vpcBuffer.Printf( "{\n" );
  509. vpcBuffer.Printf( "$ExcludedFromBuild \"Yes\"\n" );
  510. vpcBuffer.Printf( "}\n" );
  511. vpcBuffer.Printf( "}\n" );
  512. }
  513. vpcBuffer.Printf( "}\n" );
  514. }
  515. // vsh files
  516. if ( vshList.Count() )
  517. {
  518. vpcBuffer.Printf( "$Folder \"vsh\" \n" );
  519. vpcBuffer.Printf( "{\n" );
  520. for ( i=0; i<vshList.Count(); i++ )
  521. {
  522. vpcBuffer.Printf( "$file \"%s\"\n", vshList[i].String() );
  523. vpcBuffer.Printf( "{\n" );
  524. vpcBuffer.Printf( "$Configuration\n" );
  525. vpcBuffer.Printf( "{\n" );
  526. vpcBuffer.Printf( "$ExcludedFromBuild \"Yes\"\n" );
  527. vpcBuffer.Printf( "}\n" );
  528. vpcBuffer.Printf( "}\n" );
  529. }
  530. vpcBuffer.Printf( "}\n" );
  531. }
  532. // psh files
  533. if ( pshList.Count() )
  534. {
  535. vpcBuffer.Printf( "$Folder \"psh\" \n" );
  536. vpcBuffer.Printf( "{\n" );
  537. for ( i=0; i<pshList.Count(); i++ )
  538. {
  539. vpcBuffer.Printf( "$file \"%s\"\n", pshList[i].String() );
  540. vpcBuffer.Printf( "{\n" );
  541. vpcBuffer.Printf( "$Configuration\n" );
  542. vpcBuffer.Printf( "{\n" );
  543. vpcBuffer.Printf( "$ExcludedFromBuild \"Yes\"\n" );
  544. vpcBuffer.Printf( "}\n" );
  545. vpcBuffer.Printf( "}\n" );
  546. }
  547. vpcBuffer.Printf( "}\n" );
  548. }
  549. // vfx files
  550. if ( vfxList.Count() )
  551. {
  552. vpcBuffer.Printf( "$Folder \"vfx\" \n" );
  553. vpcBuffer.Printf( "{\n" );
  554. for ( i=0; i<vfxList.Count(); i++ )
  555. {
  556. vpcBuffer.Printf( "$file \"%s\"\n", vfxList[i].String() );
  557. vpcBuffer.Printf( "{\n" );
  558. vpcBuffer.Printf( "$Configuration\n" );
  559. vpcBuffer.Printf( "{\n" );
  560. vpcBuffer.Printf( "$ExcludedFromBuild \"Yes\"\n" );
  561. vpcBuffer.Printf( "}\n" );
  562. vpcBuffer.Printf( "}\n" );
  563. }
  564. vpcBuffer.Printf( "}\n" );
  565. }
  566. // other files
  567. if ( otherList.Count() )
  568. {
  569. // psh files
  570. vpcBuffer.Printf( "$Folder \"other\" \n" );
  571. vpcBuffer.Printf( "{\n" );
  572. for ( i=0; i<otherList.Count(); i++ )
  573. {
  574. vpcBuffer.Printf( "$file \"%s\"\n", otherList[i].String() );
  575. vpcBuffer.Printf( "{\n" );
  576. vpcBuffer.Printf( "$Configuration\n" );
  577. vpcBuffer.Printf( "{\n" );
  578. vpcBuffer.Printf( "$ExcludedFromBuild \"Yes\"\n" );
  579. vpcBuffer.Printf( "}\n" );
  580. vpcBuffer.Printf( "}\n" );
  581. }
  582. vpcBuffer.Printf( "}\n" );
  583. }
  584. // end of shader folder
  585. vpcBuffer.Printf( "}\n" );
  586. // save parser
  587. bIgnoreRedundancyWarning = g_pVPC->IsIgnoreRedundancyWarning();
  588. g_pVPC->SetIgnoreRedundancyWarning( true );
  589. g_pVPC->GetScript().PushScript( "Internal List", (char*)vpcBuffer.Base() );
  590. pToken = g_pVPC->GetScript().GetToken( true );
  591. if ( pToken && pToken[0] && !V_stricmp( pToken, "$folder" ) )
  592. {
  593. VPC_Keyword_Folder();
  594. }
  595. // restore parser
  596. g_pVPC->GetScript().PopScript();
  597. g_pVPC->SetIgnoreRedundancyWarning( bIgnoreRedundancyWarning );
  598. }
  599. //-----------------------------------------------------------------------------
  600. // VPC_Keyword_Macro
  601. //
  602. //-----------------------------------------------------------------------------
  603. enum MacroType_t { VPC_MACRO_VALUE, VPC_MACRO_EMPTY_STRING };
  604. void VPC_Keyword_Macro( MacroType_t eMacroType )
  605. {
  606. const char *pToken;
  607. char macro[MAX_SYSTOKENCHARS];
  608. char value[MAX_SYSTOKENCHARS];
  609. pToken = g_pVPC->GetScript().GetToken( false );
  610. if ( !pToken || !pToken[0] )
  611. g_pVPC->VPCSyntaxError();
  612. strcpy( macro, pToken );
  613. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, value, sizeof( value ) ) )
  614. {
  615. return;
  616. }
  617. char environmentValue[MAX_SYSTOKENCHARS];
  618. if ( Sys_EvaluateEnvironmentExpression( value, "", environmentValue, sizeof( environmentValue ) ) )
  619. {
  620. V_strncpy( value, environmentValue, sizeof( value ) );
  621. }
  622. g_pVPC->FindOrCreateMacro( macro, true, ( eMacroType == VPC_MACRO_VALUE ) ? value : "" );
  623. }
  624. //-----------------------------------------------------------------------------
  625. // $MacroRequired <Macro> [DefaultValue] [Condition]
  626. // $MacroRequiredAllowEmpty <Macro> [DefaultValue] [Condition]
  627. //
  628. // Forces a script to error if a macro that it depends on was not set.
  629. // The Default will be used if the macro was not defined, otherwise error.
  630. // This is to allow a required macro in a base script to have a concept
  631. // of a default initialization value.
  632. //-----------------------------------------------------------------------------
  633. enum MacroRequiredType_t { VPC_MACRO_REQUIRED_NOT_EMPTY, VPC_MACRO_REQUIRED_ALLOW_EMPTY };
  634. void VPC_Keyword_MacroRequired( MacroRequiredType_t eMacroRequiredType )
  635. {
  636. char macroName[MAX_SYSTOKENCHARS];
  637. char macroDefaultValue[MAX_SYSTOKENCHARS];
  638. const char *pToken;
  639. macroDefaultValue[0] = '\0';
  640. pToken = g_pVPC->GetScript().GetToken( false );
  641. if ( !pToken || !pToken[0] )
  642. {
  643. g_pVPC->VPCSyntaxError();
  644. }
  645. strcpy( macroName, pToken );
  646. // optional default macro value or conditional
  647. pToken = g_pVPC->GetScript().PeekNextToken( false );
  648. if ( pToken && pToken[0] )
  649. {
  650. if ( pToken[0] == '[' )
  651. {
  652. // evaulate argument as conditional
  653. if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
  654. {
  655. return;
  656. }
  657. }
  658. else
  659. {
  660. // argument is a default macro value
  661. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, macroDefaultValue, sizeof( macroDefaultValue ) ) )
  662. {
  663. return;
  664. }
  665. }
  666. }
  667. // find macro, needs to be present and non-empty
  668. macro_t *pMacro = g_pVPC->FindOrCreateMacro( macroName, false, NULL );
  669. if ( !pMacro || ( eMacroRequiredType == VPC_MACRO_REQUIRED_NOT_EMPTY && !strlen( pMacro->value.String() ) ) )
  670. {
  671. if ( macroDefaultValue[0] || ( eMacroRequiredType == VPC_MACRO_REQUIRED_ALLOW_EMPTY ) )
  672. {
  673. g_pVPC->FindOrCreateMacro( macroName, true, macroDefaultValue );
  674. }
  675. else
  676. {
  677. // In case we're in mksln showing a pacifier of dots. Make sure to show the error on a new line.
  678. g_pVPC->VPCSyntaxError( "\n\nRequired Macro '%s', not defined or empty", macroName );
  679. }
  680. }
  681. }
  682. //-----------------------------------------------------------------------------
  683. // VPC_Keyword_LoadAddressMacro
  684. //
  685. // $LoadAddressMacro <MacroName>
  686. // {
  687. // <ProjectName> <BaseAddress>
  688. // }
  689. //
  690. // Specialized instruction to populate the load address macro based on a project
  691. // name.
  692. //-----------------------------------------------------------------------------
  693. void VPC_Keyword_LoadAddressMacro( void )
  694. {
  695. char szProjectName[MAX_SYSTOKENCHARS];
  696. char szMacroName[MAX_SYSTOKENCHARS];
  697. char szBaseAddress[MAX_SYSTOKENCHARS];
  698. const char *pToken;
  699. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, szMacroName, sizeof( szMacroName ) ) )
  700. {
  701. g_pVPC->GetScript().SkipBracedSection();
  702. return;
  703. }
  704. pToken = g_pVPC->GetScript().GetToken( true );
  705. if ( !pToken || !pToken[0] || V_stricmp( pToken, "{" ) )
  706. {
  707. g_pVPC->VPCSyntaxError();
  708. }
  709. while ( 1 )
  710. {
  711. pToken = g_pVPC->GetScript().GetToken( true );
  712. if ( !pToken || !pToken[0] )
  713. {
  714. break;
  715. }
  716. strcpy( szProjectName, pToken );
  717. if ( !V_stricmp( pToken, "}" ) )
  718. {
  719. break;
  720. }
  721. else
  722. {
  723. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, szBaseAddress, sizeof( szBaseAddress ) ) )
  724. {
  725. continue;
  726. }
  727. if ( !V_stricmp( szProjectName, g_pVPC->GetLoadAddressName() ) )
  728. {
  729. // set Macro
  730. g_pVPC->FindOrCreateMacro( szMacroName, true, szBaseAddress );
  731. }
  732. }
  733. }
  734. }
  735. //-----------------------------------------------------------------------------
  736. // VPC_Keyword_LoadAddressMacroAlias
  737. //
  738. // $LoadAddressMacroAlias <Alias>
  739. // {
  740. // <ProjectName>
  741. // }
  742. //
  743. // When evaluating $LoadAddressMacro/$LoadAddressMacroAuto, substitute all listed <ProjectName> entries with <Alias>
  744. //-----------------------------------------------------------------------------
  745. void VPC_Keyword_LoadAddressMacroAlias( void )
  746. {
  747. char szProjectName[MAX_SYSTOKENCHARS];
  748. char szAlias[MAX_SYSTOKENCHARS];
  749. const char *pToken;
  750. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, szAlias, sizeof( szAlias ) ) )
  751. {
  752. g_pVPC->GetScript().SkipBracedSection();
  753. return;
  754. }
  755. pToken = g_pVPC->GetScript().GetToken( true );
  756. if ( !pToken || !pToken[0] || V_stricmp( pToken, "{" ) )
  757. {
  758. g_pVPC->VPCSyntaxError();
  759. }
  760. while ( 1 )
  761. {
  762. pToken = g_pVPC->GetScript().GetToken( true );
  763. if ( !pToken || !pToken[0] )
  764. {
  765. break;
  766. }
  767. strcpy( szProjectName, pToken );
  768. if ( !V_stricmp( pToken, "}" ) )
  769. {
  770. break;
  771. }
  772. else
  773. {
  774. if ( !V_stricmp( szProjectName, g_pVPC->GetProjectName() ) )
  775. {
  776. // set Macro and alias
  777. g_pVPC->FindOrCreateMacro( "LOADADDRESSNAME", true, szAlias );
  778. g_pVPC->SetLoadAddressName( szAlias );
  779. }
  780. }
  781. }
  782. }
  783. //-----------------------------------------------------------------------------
  784. // Internal_LoadAddressMacroAuto
  785. //
  786. // bPad - Differentiate between $LoadAddressMacroAuto and $LoadAddressMacroAuto_Padded
  787. // implementations
  788. //
  789. // Specialized instruction to populate the load address macro based on a project
  790. // name.
  791. //-----------------------------------------------------------------------------
  792. void Internal_LoadAddressMacroAuto( bool bPad )
  793. {
  794. char szProjectName[MAX_SYSTOKENCHARS];
  795. char szMacroName[MAX_SYSTOKENCHARS];
  796. char szBaseAddress[MAX_SYSTOKENCHARS];
  797. char szLength[MAX_SYSTOKENCHARS];
  798. const char *pToken;
  799. pToken = g_pVPC->GetScript().GetToken( false );
  800. if ( !pToken || !pToken[0] )
  801. {
  802. g_pVPC->VPCSyntaxError();
  803. }
  804. strcpy( szMacroName, pToken );
  805. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, szBaseAddress, sizeof( szBaseAddress ) ) )
  806. {
  807. g_pVPC->GetScript().SkipBracedSection();
  808. return;
  809. }
  810. unsigned int baseAddress = 0;
  811. sscanf( szBaseAddress, "%x", &baseAddress );
  812. unsigned int iInitialBaseAddress = baseAddress;
  813. macro_t *pMacro = NULL;
  814. int iSetEntryNum = 0;
  815. int iSetBaseAddress = 0;
  816. pToken = g_pVPC->GetScript().GetToken( true );
  817. if ( !pToken || !pToken[0] || V_stricmp( pToken, "{" ) )
  818. {
  819. g_pVPC->VPCSyntaxError();
  820. }
  821. int iEntryNum = 0;
  822. while ( 1 )
  823. {
  824. pToken = g_pVPC->GetScript().GetToken( true );
  825. if ( !pToken || !pToken[0] )
  826. {
  827. break;
  828. }
  829. strcpy( szProjectName, pToken );
  830. if ( !V_stricmp( szProjectName, g_pVPC->GetLoadAddressName() ) )
  831. {
  832. // set Macro
  833. char szMacroValue[MAX_SYSTOKENCHARS];
  834. sprintf( szMacroValue, "0x%8.8x", baseAddress );
  835. iSetEntryNum = iEntryNum;
  836. iSetBaseAddress = baseAddress;
  837. pMacro = g_pVPC->FindOrCreateMacro( szMacroName, true, szMacroValue );
  838. }
  839. if ( !V_stricmp( pToken, "}" ) )
  840. {
  841. break;
  842. }
  843. else
  844. {
  845. unsigned int dllLength = 0;
  846. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, szLength, sizeof( szLength ) ) )
  847. {
  848. continue;
  849. }
  850. if ( strstr( szLength, "." ) )
  851. {
  852. // assume float format
  853. float fLength = 0;
  854. sscanf( szLength, "%f", &fLength );
  855. dllLength = fLength * 1024.0f * 1024.0f;
  856. }
  857. else
  858. {
  859. sscanf( szLength, "%d", &dllLength );
  860. }
  861. if ( !bPad )
  862. {
  863. dllLength = AlignValue( dllLength, 64 * 1024 ); //will align later when we actually set the darn thing
  864. }
  865. if ( dllLength == 0 )
  866. {
  867. g_pVPC->VPCSyntaxError( "$LoadAddressMacroAuto no longer supports 0 size dlls. Use $LoadAddressMacroAlias to have two orthogonal projects load in the same space" );
  868. }
  869. baseAddress += dllLength;
  870. }
  871. ++iEntryNum;
  872. }
  873. if ( bPad && pMacro )
  874. {
  875. unsigned int iEndAddress;
  876. if ( (iInitialBaseAddress >= 0x82000000) && (iInitialBaseAddress < 0x8C000000) )
  877. {
  878. iEndAddress = 0x8BFFFFFF;
  879. }
  880. else
  881. {
  882. iEndAddress = 0x9BFFFFFF;
  883. }
  884. // compute leftover unused address space
  885. unsigned int iRemainingSpace = iEndAddress - baseAddress;
  886. int iPadPerEntry = iRemainingSpace / iEntryNum;
  887. //iPadPerEntry = iPadPerEntry & ~(64 * 1024); //align DOWN to 64k
  888. if ( iPadPerEntry > 0 )
  889. {
  890. // set the base address again with padding added
  891. iSetBaseAddress += iPadPerEntry * iSetEntryNum;
  892. iSetBaseAddress = AlignValue( iSetBaseAddress, 64 * 1024 );
  893. char szMacroValue[MAX_SYSTOKENCHARS];
  894. sprintf( szMacroValue, "0x%8.8x", iSetBaseAddress );
  895. pMacro->value = szMacroValue;
  896. }
  897. }
  898. }
  899. //-----------------------------------------------------------------------------
  900. // VPC_Keyword_LoadAddressMacroAuto
  901. //
  902. // $LoadAddressMacroAuto <MacroName> <BaseAddress>
  903. // {
  904. // <ProjectName> <Length>
  905. // }
  906. //
  907. // Specialized instruction to populate the load address macro based on a project
  908. // name.
  909. //-----------------------------------------------------------------------------
  910. void VPC_Keyword_LoadAddressMacroAuto( void )
  911. {
  912. Internal_LoadAddressMacroAuto( false );
  913. }
  914. //-----------------------------------------------------------------------------
  915. // VPC_Keyword_LoadAddressMacroAuto_Padded
  916. //
  917. // $LoadAddressMacroAuto_Padded <MacroName> <BaseAddress>
  918. // {
  919. // <ProjectName> <Length>
  920. // }
  921. //
  922. // Specialized instruction to populate the load address macro based on a project
  923. // name. Assumes the contained list is minimally packed and has free reign of
  924. // space up to the limit. Finds unused space spreads it out evenly between
  925. // each project
  926. //-----------------------------------------------------------------------------
  927. void VPC_Keyword_LoadAddressMacroAuto_Padded( void )
  928. {
  929. Internal_LoadAddressMacroAuto( true );
  930. }
  931. //-----------------------------------------------------------------------------
  932. //-----------------------------------------------------------------------------
  933. void VPC_Keyword_Conditional()
  934. {
  935. const char *pToken = g_pVPC->GetScript().GetToken( false );
  936. if ( !pToken || !pToken[0] )
  937. g_pVPC->VPCSyntaxError();
  938. char name[MAX_SYSTOKENCHARS];
  939. if ( pToken[0] == '$' )
  940. {
  941. // being nice to users, quietly remove the unwanted conditional prefix '$'
  942. pToken++;
  943. }
  944. strcpy( name, pToken );
  945. char value[MAX_SYSTOKENCHARS];
  946. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, value, sizeof( value ) ) )
  947. {
  948. return;
  949. }
  950. conditional_t *pConditional = g_pVPC->FindOrCreateConditional( name, true, CONDITIONAL_CUSTOM );
  951. if ( pConditional->type != CONDITIONAL_CUSTOM )
  952. {
  953. // scripts cannot change any platform or game conditionals
  954. g_pVPC->VPCSyntaxError( "$Conditional cannot be used on the reserved '$%s'", pConditional->upperCaseName.Get() );
  955. }
  956. char environmentValue[MAX_SYSTOKENCHARS];
  957. if ( Sys_EvaluateEnvironmentExpression( value, "0", environmentValue, sizeof( environmentValue ) ) )
  958. {
  959. V_strncpy( value, environmentValue, sizeof( value ) );
  960. }
  961. g_pVPC->SetConditional( name, Sys_StringToBool( value ) );
  962. }
  963. //-----------------------------------------------------------------------------
  964. // VPC_Keyword_IgnoreRedundancyWarning
  965. //
  966. //-----------------------------------------------------------------------------
  967. void VPC_Keyword_IgnoreRedundancyWarning( void )
  968. {
  969. char value[MAX_SYSTOKENCHARS];
  970. if ( !g_pVPC->GetScript().ParsePropertyValue( NULL, value, sizeof( value ) ) )
  971. {
  972. return;
  973. }
  974. bool bVal = Sys_StringToBool( value );
  975. g_pVPC->SetIgnoreRedundancyWarning( bVal );
  976. }
  977. //-----------------------------------------------------------------------------
  978. // VPC_Keyword_Linux
  979. //
  980. //-----------------------------------------------------------------------------
  981. void VPC_Keyword_Linux( void )
  982. {
  983. // always ignore everything in this block
  984. // parsed and processed by a different tool
  985. g_pVPC->GetScript().SkipBracedSection();
  986. }
  987. void VPC_PrepareToReadScript( const char *pInputScriptName, int depth, bool bQuiet, char* &pScriptBuffer, char szScriptName[MAX_PATH] )
  988. {
  989. if ( !depth )
  990. {
  991. // startup initialization
  992. g_pVPC->GetProjectGenerator()->StartProject();
  993. }
  994. V_strncpy( szScriptName, pInputScriptName, MAX_PATH );
  995. V_FixSlashes( szScriptName );
  996. // always spew the root script
  997. if ( !bQuiet )
  998. {
  999. bool bSpew = ( depth == 0 );
  1000. g_pVPC->VPCStatus( bSpew, "Parsing: %s", szScriptName );
  1001. }
  1002. // parse the text script
  1003. if ( !Sys_Exists( szScriptName ) )
  1004. {
  1005. g_pVPC->VPCError( "Cannot open %s", szScriptName );
  1006. }
  1007. int scriptLen = Sys_LoadTextFileWithIncludes( szScriptName, &pScriptBuffer );
  1008. if ( scriptLen < 0 )
  1009. {
  1010. // unexpected due to existence check
  1011. g_pVPC->VPCError( "Cannot open %s", szScriptName );
  1012. }
  1013. g_pVPC->AddScriptToCRCCheck( szScriptName, CRC32_ProcessSingleBuffer( pScriptBuffer, scriptLen ) );
  1014. g_pVPC->GetScript().PushScript( szScriptName, pScriptBuffer );
  1015. }
  1016. void VPC_HandleIncludeStatement( int depth, bool bQuiet, void (*CallbackFn)( const char *pScriptName, int depth, bool bQuiet ) )
  1017. {
  1018. char szBigBuffer[MAX_SYSTOKENCHARS];
  1019. if ( g_pVPC->GetScript().ParsePropertyValue( NULL, szBigBuffer, sizeof( szBigBuffer ) ) )
  1020. {
  1021. // recurse into and run
  1022. char *pScriptBuffer;
  1023. char szFixedScriptName[MAX_PATH];
  1024. VPC_PrepareToReadScript( szBigBuffer, depth+1, bQuiet, pScriptBuffer, szFixedScriptName );
  1025. g_pVPC->GetProjectGenerator()->StartFolder( "VPC Scripts" );
  1026. g_pVPC->GetProjectGenerator()->StartFile( g_pVPC->GetScript().GetName(), false );
  1027. g_pVPC->GetProjectGenerator()->EndFile();
  1028. g_pVPC->GetProjectGenerator()->EndFolder();
  1029. CallbackFn( szBigBuffer, depth+1, bQuiet );
  1030. free( pScriptBuffer );
  1031. // restore state
  1032. g_pVPC->GetScript().PopScript();
  1033. }
  1034. }
  1035. void VPC_HandleProjectCommands( const char *pUnusedScriptName, int depth, bool bQuiet )
  1036. {
  1037. const char *pToken;
  1038. while ( 1 )
  1039. {
  1040. pToken = g_pVPC->GetScript().GetToken( true );
  1041. if ( !pToken || !pToken[0] )
  1042. break;
  1043. if ( !V_stricmp( pToken, "}" ) )
  1044. {
  1045. break;
  1046. }
  1047. else if ( !V_stricmp( pToken, "$include" ) )
  1048. {
  1049. VPC_HandleIncludeStatement( depth, bQuiet, VPC_HandleProjectCommands );
  1050. }
  1051. else if ( !V_stricmp( pToken, "$Folder" ) )
  1052. {
  1053. // root level folder
  1054. VPC_Keyword_Folder();
  1055. }
  1056. else if ( !V_stricmp( pToken, "$File" ) )
  1057. {
  1058. // add root level file
  1059. VPC_Keyword_AddFile();
  1060. }
  1061. else if ( !V_stricmp( pToken, "$SchemaFile" ) )
  1062. {
  1063. // add root level file
  1064. VPC_Keyword_AddFile( "schema" );
  1065. }
  1066. else if ( !V_stricmp( pToken, "-$File" ) )
  1067. {
  1068. // remove root level file
  1069. VPC_Keyword_RemoveFile();
  1070. }
  1071. else if ( !V_stricmp( pToken, "$Shaders" ) )
  1072. {
  1073. // add root level shaders folder
  1074. VPC_Keyword_Shaders( 0 );
  1075. }
  1076. else
  1077. {
  1078. g_pVPC->VPCSyntaxError();
  1079. }
  1080. }
  1081. }
  1082. void WriteCRCCheckFile( const char *pVCProjFilename )
  1083. {
  1084. char szFilename[MAX_PATH];
  1085. V_snprintf( szFilename, sizeof( szFilename ), "%s." VPCCRCCHECK_FILE_EXTENSION, pVCProjFilename );
  1086. FILE *fp = fopen( szFilename, "wt" );
  1087. if ( !fp )
  1088. {
  1089. g_pVPC->VPCError( "Unable to open %s to write CRCs into.", szFilename );
  1090. }
  1091. fprintf( fp, "%s\n", VPCCRCCHECK_FILE_VERSION_STRING );
  1092. fprintf( fp, "%s\n", g_pVPC->GetCRCString() );
  1093. CUtlDict<int, int> filenameDict( k_eDictCompareTypeFilenames );
  1094. for ( int i=0; i < g_pVPC->m_ScriptList.Count(); i++ )
  1095. {
  1096. scriptList_t *pScript = &g_pVPC->m_ScriptList[i];
  1097. // Use the dictionary to prevent duplicate file CRCs being written in here.
  1098. if ( filenameDict.Find( pScript->m_scriptName.String() ) == filenameDict.InvalidIndex() )
  1099. {
  1100. filenameDict.Insert( pScript->m_scriptName.String(), 1 );
  1101. // [crc] [filename]
  1102. fprintf( fp, "%8.8x %s\n", ( unsigned int ) pScript->m_crc, pScript->m_scriptName.Get() );
  1103. }
  1104. }
  1105. fclose( fp );
  1106. }
  1107. //
  1108. // This is called when it's done parsing a project. If there were any $SchemaFile
  1109. // entries in the project, then we will make this project depend on schemacompiler
  1110. // (via $AdditionalProjectDependencies).
  1111. //
  1112. // This fixes full rebuild problems where it's building a project that uses schemacompiler
  1113. // at the same time as it's building schemacompiler. This usually screws up when
  1114. // it tries to copy the new schemacompiler.exe to game\bin but it's in use.
  1115. //
  1116. void VPC_ForceAdditionalSchemaDependencies( const char *pProjectName )
  1117. {
  1118. if ( g_pVPC->m_SchemaFiles.Count() == 0 )
  1119. return;
  1120. // Add "$BASE;SchemaCompiler" to $AdditionalProjectDependencies.
  1121. CUtlVector< CUtlString > configurationNames;
  1122. g_pVPC->GetProjectGenerator()->GetAllConfigurationNames( configurationNames );
  1123. for ( int i=0; i < configurationNames.Count(); i++ )
  1124. {
  1125. g_pVPC->GetProjectGenerator()->StartConfigurationBlock( configurationNames[i].String(), false );
  1126. g_pVPC->GetProjectGenerator()->StartPropertySection( KEYWORD_GENERAL, NULL );
  1127. g_pVPC->GetProjectGenerator()->HandleProperty( g_pOption_AdditionalProjectDependencies, "$BASE;SchemaCompiler" );
  1128. g_pVPC->GetProjectGenerator()->EndPropertySection( KEYWORD_GENERAL );
  1129. g_pVPC->GetProjectGenerator()->EndConfigurationBlock();
  1130. }
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // VPC_Keyword_Project
  1134. //
  1135. //-----------------------------------------------------------------------------
  1136. void VPC_Keyword_Project( int depth, bool bQuiet )
  1137. {
  1138. char szProjectName[MAX_PATH];
  1139. // check for optional project name
  1140. const char *pToken = g_pVPC->GetScript().PeekNextToken( false );
  1141. if ( pToken && pToken[0] && V_stricmp( pToken, "{" ) )
  1142. {
  1143. // get optional project name
  1144. pToken = g_pVPC->GetScript().GetToken( false );
  1145. if ( !pToken || !pToken[0] )
  1146. {
  1147. g_pVPC->VPCSyntaxError();
  1148. }
  1149. g_pVPC->ResolveMacrosInString( pToken, szProjectName, sizeof( szProjectName ) );
  1150. if ( g_pVPC->IsDecorateProject() )
  1151. {
  1152. // caller wants decorated project names, add the platform
  1153. macro_t *pMacro = g_pVPC->FindOrCreateMacro( "PLATFORM", false, NULL );
  1154. if ( pMacro )
  1155. {
  1156. char szPlatform[MAX_PATH];
  1157. sprintf( szPlatform, " (%s)", pMacro->value.String() );
  1158. strcat( szProjectName, szPlatform );
  1159. }
  1160. }
  1161. }
  1162. else
  1163. {
  1164. CUtlString strName = g_pVPC->GetProjectGenerator()->GetProjectName();
  1165. V_strncpy( szProjectName, strName.String(), sizeof( szProjectName ) );
  1166. }
  1167. pToken = g_pVPC->GetScript().GetToken( true );
  1168. if ( !pToken || !pToken[0] || V_stricmp( pToken, "{" ) )
  1169. g_pVPC->VPCSyntaxError();
  1170. VPC_HandleProjectCommands( NULL, depth, bQuiet );
  1171. // the unnamed project does not get written, once it is named it will be written on closing scope
  1172. if ( V_stricmp( szProjectName, "UNNAMED" ) )
  1173. {
  1174. VPC_ForceAdditionalSchemaDependencies( szProjectName );
  1175. // change name
  1176. g_pVPC->GetProjectGenerator()->SetProjectName( szProjectName );
  1177. g_pVPC->GetProjectGenerator()->EndProject();
  1178. g_pVPC->m_bGeneratedProject = true;
  1179. }
  1180. }
  1181. static const char* fileTypes[] =
  1182. {
  1183. "c", "cpp", "cxx", "cc",
  1184. "h", "hh", "hxx", ""
  1185. };
  1186. bool VPC_IsBuiltInFileType( const char *extension )
  1187. {
  1188. for (int i = 0; i < Q_ARRAYSIZE(fileTypes); i++)
  1189. {
  1190. if ( !V_stricmp( fileTypes[i], extension ) )
  1191. return true;
  1192. }
  1193. return false;
  1194. }
  1195. void VPC_Keyword_CustomBuildStep( void )
  1196. {
  1197. bool bAllowNextLine = false;
  1198. CUtlVector<CUtlString> extensions;
  1199. const char *pToken = NULL;
  1200. while ( 1 )
  1201. {
  1202. pToken = g_pVPC->GetScript().GetToken( bAllowNextLine );
  1203. if ( !pToken || !pToken[0] )
  1204. break;
  1205. // Is this a conditional expression?
  1206. if ( pToken[0] == '[' )
  1207. {
  1208. if ( extensions.Count() == 0 )
  1209. {
  1210. g_pVPC->VPCSyntaxError( "Conditional specified on a $CustomBuildStep without any extensions preceding it." );
  1211. }
  1212. if ( !g_pVPC->EvaluateConditionalExpression( pToken ) )
  1213. {
  1214. extensions.Remove( extensions.Count() - 1 );
  1215. }
  1216. continue;
  1217. }
  1218. if ( !V_stricmp( pToken, "\\" ) )
  1219. {
  1220. bAllowNextLine = true;
  1221. continue;
  1222. }
  1223. else
  1224. {
  1225. bAllowNextLine = false;
  1226. }
  1227. if ( VPC_IsBuiltInFileType( pToken ) )
  1228. {
  1229. g_pVPC->VPCSyntaxError( "Cannot define custom build steps for built in file type: %s", pToken);
  1230. }
  1231. CUtlString string = pToken;
  1232. extensions.AddToTail( string );
  1233. // check for another optional file
  1234. pToken = g_pVPC->GetScript().PeekNextToken( bAllowNextLine );
  1235. if ( !pToken || !pToken[0] )
  1236. break;
  1237. }
  1238. pToken = g_pVPC->GetScript().GetToken( true );
  1239. if ( !pToken || !pToken[0] || V_strcmp( pToken, "{" ) )
  1240. {
  1241. g_pVPC->VPCSyntaxError( "Missing section for CustomBuildStep" );
  1242. }
  1243. else if ( extensions.Count() == 0 )
  1244. {
  1245. g_pVPC->GetScript().SkipBracedSection();
  1246. return;
  1247. }
  1248. else
  1249. {
  1250. const char *pScriptSave = g_pVPC->GetScript().GetData();
  1251. while ( 1 )
  1252. {
  1253. pToken = g_pVPC->GetScript().GetToken( true );
  1254. if ( !pToken || !pToken[0] )
  1255. break;
  1256. if ( !V_stricmp( pToken, "}" ) )
  1257. {
  1258. // end of section
  1259. break;
  1260. }
  1261. }
  1262. CUtlString buildsteps;
  1263. if ( g_pVPC->GetScript().GetData() > pScriptSave )
  1264. {
  1265. CUtlString tempString;
  1266. tempString.SetDirect( pScriptSave, int( g_pVPC->GetScript().GetData() - pScriptSave - 1 ) );
  1267. buildsteps += "$Configuration\n{\n$CustomBuildStep\n{";
  1268. buildsteps += tempString + "}\n}\n";
  1269. }
  1270. if ( !buildsteps.IsEmpty() )
  1271. {
  1272. FOR_EACH_VEC( extensions, i )
  1273. {
  1274. g_pVPC->m_CustomBuildSteps.Insert( extensions[i], buildsteps );
  1275. }
  1276. }
  1277. }
  1278. }
  1279. void VPC_ParseProjectScriptParameters( const char *szScriptName, int depth, bool bQuiet )
  1280. {
  1281. while ( 1 )
  1282. {
  1283. const char *pToken = g_pVPC->GetScript().GetToken( true );
  1284. if ( !pToken || !pToken[0] )
  1285. {
  1286. // end of file
  1287. break;
  1288. }
  1289. if ( !V_stricmp( pToken, "$include" ) )
  1290. {
  1291. VPC_HandleIncludeStatement( depth, bQuiet, VPC_ParseProjectScriptParameters );
  1292. }
  1293. else if ( !V_stricmp( pToken, "$configuration" ) )
  1294. {
  1295. VPC_Keyword_Configuration();
  1296. }
  1297. else if ( !V_stricmp( pToken, "$project" ) )
  1298. {
  1299. VPC_Keyword_Project( depth, bQuiet );
  1300. }
  1301. else if ( !V_stricmp( pToken, "$macro" ) )
  1302. {
  1303. VPC_Keyword_Macro( VPC_MACRO_VALUE );
  1304. }
  1305. else if ( !V_stricmp( pToken, "$MacroEmptyString" ) )
  1306. {
  1307. VPC_Keyword_Macro( VPC_MACRO_EMPTY_STRING );
  1308. }
  1309. else if ( !V_stricmp( pToken, "$MacroRequired" ) )
  1310. {
  1311. VPC_Keyword_MacroRequired( VPC_MACRO_REQUIRED_NOT_EMPTY );
  1312. }
  1313. else if ( !V_stricmp( pToken, "$MacroRequiredAllowEmpty" ) )
  1314. {
  1315. VPC_Keyword_MacroRequired( VPC_MACRO_REQUIRED_ALLOW_EMPTY );
  1316. }
  1317. else if ( !V_stricmp( pToken, "$LoadAddressMacro" ) )
  1318. {
  1319. VPC_Keyword_LoadAddressMacro();
  1320. }
  1321. else if ( !V_stricmp( pToken, "$LoadAddressMacroAlias" ) )
  1322. {
  1323. VPC_Keyword_LoadAddressMacroAlias();
  1324. }
  1325. else if ( !V_stricmp( pToken, "$LoadAddressMacroAuto" ) )
  1326. {
  1327. VPC_Keyword_LoadAddressMacroAuto();
  1328. }
  1329. else if ( !V_stricmp( pToken, "$LoadAddressMacroAuto_Padded" ) )
  1330. {
  1331. VPC_Keyword_LoadAddressMacroAuto_Padded();
  1332. }
  1333. else if ( !V_stricmp( pToken, "$IgnoreRedundancyWarning" ) )
  1334. {
  1335. VPC_Keyword_IgnoreRedundancyWarning();
  1336. }
  1337. else if ( !V_stricmp( pToken, "$Linux" ) )
  1338. {
  1339. VPC_Keyword_Linux();
  1340. }
  1341. else if ( !V_stricmp( pToken, "$CustomBuildStep" ) )
  1342. {
  1343. VPC_Keyword_CustomBuildStep();
  1344. }
  1345. else if ( !V_stricmp( pToken, "$Conditional" ) )
  1346. {
  1347. VPC_Keyword_Conditional();
  1348. }
  1349. else
  1350. {
  1351. g_pVPC->VPCSyntaxError();
  1352. }
  1353. }
  1354. }
  1355. //-----------------------------------------------------------------------------
  1356. //-----------------------------------------------------------------------------
  1357. bool CVPC::ParseProjectScript( const char *pScriptName, int depth, bool bQuiet, bool bWriteCRCCheckFile )
  1358. {
  1359. char *pScriptBuffer;
  1360. char szScriptName[MAX_PATH];
  1361. VPC_PrepareToReadScript( pScriptName, depth, bQuiet, pScriptBuffer, szScriptName );
  1362. if ( !depth )
  1363. {
  1364. // create reserved $ROOTSCRIPT - tracks the root script
  1365. FindOrCreateMacro( "ROOTSCRIPT", true, szScriptName );
  1366. // create reserved $PROJECTNAME - tracks the undecorated pure project name
  1367. // $(ProjectName) can be auto-decorated, making it unuseable by scripts expecting a pure project name
  1368. FindOrCreateMacro( "PROJECTNAME", true, g_pVPC->GetProjectName() );
  1369. // create reserved $LOADADDRESSNAME - defaults to project name but can be aliased with $LoadAddressMacroAlias
  1370. FindOrCreateMacro( "LOADADDRESSNAME", true, g_pVPC->GetLoadAddressName() );
  1371. // create reserved $CRCCHECK
  1372. // The CRCs themselves are dumped into theproject.vcproj.vpc_crc (in VPC_WriteCRCCheckFile), so all this does
  1373. // is point vpccrccheck.exe at it with "vpccrccheck.exe -crc2 theproject.vcproj"
  1374. // scripts add the terminal /n if they append, after referencing $CRCCHECK
  1375. CUtlString crcCheckString;
  1376. crcCheckString = "if exist $SRCDIR\\devtools\\bin\\" VPCCRCCHECK_EXE_FILENAME " $SRCDIR\\devtools\\bin\\" VPCCRCCHECK_EXE_FILENAME " -crc2 ";
  1377. crcCheckString += g_pVPC->GetOutputFilename();
  1378. crcCheckString += "\nif ERRORLEVEL 1 exit 1";
  1379. g_pVPC->FindOrCreateMacro( "CRCCHECK", true, crcCheckString.Get() );
  1380. // create reserved $PROJECTDIR
  1381. char szProjectRootPath[MAX_PATH];
  1382. V_snprintf( szProjectRootPath, sizeof( szProjectRootPath ), "%s/..", g_pVPC->GetStartDirectory() );
  1383. V_RemoveDotSlashes( szProjectRootPath );
  1384. SetMacro( "PROJECTDIR", szProjectRootPath, true );
  1385. g_pVPC->GetProjectGenerator()->StartFolder( "VPC Scripts" );
  1386. g_pVPC->GetProjectGenerator()->StartFile( g_pVPC->GetScript().GetName(), false );
  1387. g_pVPC->GetProjectGenerator()->EndFile();
  1388. g_pVPC->GetProjectGenerator()->EndFolder();
  1389. // reset
  1390. g_pVPC->m_SchemaFiles.Purge();
  1391. }
  1392. VPC_ParseProjectScriptParameters( szScriptName, depth, bQuiet );
  1393. free( pScriptBuffer );
  1394. // for safety, force callers to restore to proper state
  1395. g_pVPC->GetScript().PopScript();
  1396. if ( !depth )
  1397. {
  1398. // at end of all processing
  1399. if ( bWriteCRCCheckFile )
  1400. {
  1401. // Finally write out the file with all the CRCs in it. This is referenced by the $CRCCHECK macro in the prebuild steps.
  1402. WriteCRCCheckFile( g_pVPC->GetOutputFilename() );
  1403. }
  1404. g_pVPC->m_ScriptList.Purge();
  1405. g_pVPC->RemoveScriptCreatedMacros(); // Remove any macros that came from the script file.
  1406. }
  1407. return true;
  1408. }
  1409. void CVPC::AddScriptToCRCCheck( const char *pScriptName, CRC32_t crc )
  1410. {
  1411. for ( int i = 0; i < m_ScriptList.Count(); i++ )
  1412. {
  1413. if ( !V_stricmp( m_ScriptList[i].m_scriptName, pScriptName ) )
  1414. {
  1415. // update
  1416. g_pVPC->m_ScriptList[i].m_crc = crc;
  1417. return;
  1418. }
  1419. }
  1420. int index = g_pVPC->m_ScriptList.AddToTail();
  1421. g_pVPC->m_ScriptList[index].m_scriptName = pScriptName;
  1422. g_pVPC->m_ScriptList[index].m_crc = crc;
  1423. }
  1424. //////////////////////////////////////////////////////////////////////////////////////////
  1425. //////////////////////////////////////////////////////////////////////////////////////////
  1426. //////////////////////////////////////////////////////////////////////////////////////////
  1427. //////////////////////////////////////////////////////////////////////////////////////////
  1428. //
  1429. // Schema Script Generation
  1430. //
  1431. // Temporary - Once the schema workflow settles down, this schema-specific code should be
  1432. // at minimum moved into its own file, and ideally generalized so that VPC
  1433. // has a minimum of embedded schema-specific logic.
  1434. //
  1435. //
  1436. //
  1437. //////////////////////////////////////////////////////////////////////////////////////////
  1438. //////////////////////////////////////////////////////////////////////////////////////////
  1439. //////////////////////////////////////////////////////////////////////////////////////////
  1440. //////////////////////////////////////////////////////////////////////////////////////////
  1441. KeyValues *ConfigPreprocessorSettingsAsKV( CSpecificConfig *pConfig );
  1442. struct SchemaFileInfo_t
  1443. {
  1444. bool bIsCppFile;
  1445. char szFile[MAX_PATH];
  1446. char szGeneratedFile[MAX_PATH];
  1447. };
  1448. void VPC_FakeKeyword_SchemaFolder( CBaseProjectDataCollector *pDataCollector )
  1449. {
  1450. if ( g_pVPC->m_SchemaFiles.Count() == 0 )
  1451. return;
  1452. macro_t *pMacro = g_pVPC->FindOrCreateMacro( "NOSCHEMACOMPILER", false, NULL );
  1453. if ( pMacro && atoi( pMacro->value.Get() ) )
  1454. return;
  1455. CUtlString platformName = g_pVPC->GetTargetPlatformName();
  1456. V_strlower( platformName.Get() );
  1457. const char *pPlatformName = platformName.String();
  1458. CUtlVector<SchemaFileInfo_t> schemaFileInfos;
  1459. schemaFileInfos.AddMultipleToTail( g_pVPC->m_SchemaFiles.Count() );
  1460. char schemaCompilerPath[MAX_PATH];
  1461. g_pVPC->ResolveMacrosInString( "$SRCDIR/devtools/bin/schemacompiler.dll", schemaCompilerPath, sizeof( schemaCompilerPath ) );
  1462. CUtlString schemaInputs;
  1463. CUtlString schemaOutputs;
  1464. char szExt[MAX_PATH];
  1465. schemaInputs += schemaCompilerPath;
  1466. for ( int i = 0; i < g_pVPC->m_SchemaFiles.Count(); i++ )
  1467. {
  1468. if ( !g_pVPC->m_SchemaFiles[i].String() )
  1469. continue;
  1470. schemaInputs += ";";
  1471. schemaInputs += g_pVPC->m_SchemaFiles[i];
  1472. V_ExtractFileExtension( g_pVPC->m_SchemaFiles[i], szExt, sizeof(szExt) );
  1473. SchemaFileInfo_t &fileInfo = schemaFileInfos[i];
  1474. V_strncpy( fileInfo.szFile, g_pVPC->m_SchemaFiles[i], sizeof(fileInfo.szFile) );
  1475. V_FixSlashes( fileInfo.szFile, '/' );
  1476. char szNoExt[MAX_PATH];
  1477. V_StripExtension( fileInfo.szFile, szNoExt, sizeof(szNoExt) );
  1478. char szBase[MAX_PATH];
  1479. V_FileBase( fileInfo.szFile, szBase, sizeof(szBase) );
  1480. char szPath[MAX_PATH];
  1481. V_ExtractFilePath( fileInfo.szFile, szPath, sizeof(szPath) );
  1482. char szGenRelPath[MAX_PATH];
  1483. if ( !V_stricmp( szExt, "cpp" ) )
  1484. {
  1485. fileInfo.bIsCppFile = true;
  1486. V_snprintf( szGenRelPath, sizeof(szGenRelPath),
  1487. "generated_code_%s_%s/%s_schema_cpp_gen.cpp", g_pVPC->GetProjectName(), pPlatformName, szBase );
  1488. }
  1489. else
  1490. {
  1491. fileInfo.bIsCppFile = false;
  1492. V_snprintf( szGenRelPath, sizeof(szGenRelPath),
  1493. "generated_code_%s_%s/%s_schema_gen.cpp", g_pVPC->GetProjectName(), pPlatformName, szBase );
  1494. }
  1495. V_strncpy( fileInfo.szGeneratedFile, szGenRelPath, sizeof(fileInfo.szGeneratedFile) );
  1496. V_FixSlashes( fileInfo.szGeneratedFile, '/' );
  1497. schemaOutputs += fileInfo.szGeneratedFile;
  1498. schemaOutputs += ";";
  1499. }
  1500. char szSchemaOutAnchorPath[MAX_PATH];
  1501. V_snprintf( szSchemaOutAnchorPath, sizeof( szSchemaOutAnchorPath ),
  1502. "generated_code_%s_%s/%s_schema_anchor.cpp", g_pVPC->GetProjectName(), pPlatformName, g_pVPC->GetProjectName() );
  1503. V_FixSlashes( szSchemaOutAnchorPath, '/' );
  1504. schemaOutputs += szSchemaOutAnchorPath;
  1505. schemaOutputs += ";";
  1506. char szSchemaPath[MAX_PATH];
  1507. V_snprintf( szSchemaPath, sizeof( szSchemaPath ), "%s_%s.schproj", g_pVPC->GetProjectName(), pPlatformName );
  1508. //////////////////////////////////////////////////////////////////////////
  1509. CUtlBuffer vpcBuffer;
  1510. char schemaCompilerExePath[MAX_PATH];
  1511. g_pVPC->ResolveMacrosInString( "$SRCDIR/devtools/bin/schemacompiler.exe", schemaCompilerExePath, sizeof( schemaCompilerExePath ) );
  1512. V_FixSlashes( schemaCompilerExePath );
  1513. vpcBuffer.SetBufferType( true, true );
  1514. vpcBuffer.Printf( "$Folder \"Autogenerated Schema Files\" \n" );
  1515. vpcBuffer.Printf( "{\n" );
  1516. vpcBuffer.Printf(
  1517. "$File \"%s\"\n"
  1518. "{\n"
  1519. " $Configuration\n"
  1520. " {\n"
  1521. " $CustomBuildStep\n"
  1522. " {\n"
  1523. " $CommandLine \"%s -schproj $(InputPath) -config $(ConfigurationName)\"\n"
  1524. " $Description \"Schema Compiler for [$(ConfigurationName)] $(InputName)\"\n"
  1525. " $Outputs \"%s\"\n"
  1526. " $AdditionalDependencies \"%s\"\n"
  1527. " }\n"
  1528. " }\n"
  1529. "}\n", szSchemaPath, schemaCompilerExePath, schemaOutputs.Get(), schemaInputs.Get() );
  1530. static const char *schemaConfiguration =
  1531. "{\n"
  1532. " $Configuration\n"
  1533. " {\n"
  1534. " $Compiler\n"
  1535. " {\n"
  1536. //" $PreprocessorDefinitions \"NO_MEMOVERRIDE_NEW_DELETE\"\n"
  1537. " $Create/UsePrecompiledHeader \"Not Using Precompiled Headers\"\n"
  1538. " }\n"
  1539. " }\n"
  1540. "}\n";
  1541. vpcBuffer.Printf( "$Folder \"Cpp Schema Wrappers\" \n" );
  1542. {
  1543. vpcBuffer.Printf( "{\n" );
  1544. for ( int i = 0; i < schemaFileInfos.Count(); ++i )
  1545. {
  1546. if ( schemaFileInfos[i].bIsCppFile )
  1547. {
  1548. vpcBuffer.Printf( "$File \"%s\"\n", schemaFileInfos[i].szGeneratedFile );
  1549. vpcBuffer.Printf( schemaConfiguration );
  1550. }
  1551. }
  1552. vpcBuffer.Printf( "}\n" );
  1553. }
  1554. vpcBuffer.Printf( "$Folder \"Header-Generated Files\" \n" );
  1555. {
  1556. vpcBuffer.Printf( "{\n" );
  1557. for ( int i = 0; i < schemaFileInfos.Count(); ++i )
  1558. {
  1559. if ( !schemaFileInfos[i].bIsCppFile )
  1560. {
  1561. vpcBuffer.Printf( "$File \"%s\"\n", schemaFileInfos[i].szGeneratedFile );
  1562. vpcBuffer.Printf( schemaConfiguration );
  1563. }
  1564. }
  1565. vpcBuffer.Printf( "}\n" );
  1566. }
  1567. vpcBuffer.Printf( "$File \"%s\"\n", szSchemaOutAnchorPath );
  1568. vpcBuffer.Printf( schemaConfiguration );
  1569. vpcBuffer.Printf( "}\n" );
  1570. // save parser
  1571. bool bIgnoreRedundancyWarning = g_pVPC->IsIgnoreRedundancyWarning();
  1572. g_pVPC->SetIgnoreRedundancyWarning( true );
  1573. g_pVPC->GetScript().PushScript( "Internal List [Schema]", (char*)vpcBuffer.Base() );
  1574. const char *pToken = g_pVPC->GetScript().GetToken( true );
  1575. if ( pToken && pToken[0] && !V_stricmp( pToken, "$folder" ) )
  1576. {
  1577. VPC_Keyword_Folder();
  1578. }
  1579. // restore parser
  1580. g_pVPC->GetScript().PopScript();
  1581. g_pVPC->SetIgnoreRedundancyWarning( bIgnoreRedundancyWarning );
  1582. //////////////////////////////////////////////////////////////////////////
  1583. KeyValues *pOutKeyValues = new KeyValues("schproj");
  1584. pOutKeyValues->SetString( "project_name", g_pVPC->GetProjectName() );
  1585. pOutKeyValues->SetString( "platform_name", pPlatformName );
  1586. pOutKeyValues->SetString( "anchor_path", szSchemaOutAnchorPath );
  1587. char dmeTargetFolder[MAX_PATH];
  1588. g_pVPC->ResolveMacrosInString( "$SRCDIR\\public", dmeTargetFolder, sizeof( dmeTargetFolder ) );
  1589. pOutKeyValues->SetString( "dme_target_folder", dmeTargetFolder );
  1590. KeyValues *pOutAllConfigs = new KeyValues( "configs" );
  1591. pOutKeyValues->AddSubKey( pOutAllConfigs );
  1592. for ( int i = pDataCollector->m_BaseConfigData.m_Configurations.First();
  1593. i != pDataCollector->m_BaseConfigData.m_Configurations.InvalidIndex();
  1594. i = pDataCollector->m_BaseConfigData.m_Configurations.Next( i ) )
  1595. {
  1596. CSpecificConfig *pConfig = pDataCollector->m_BaseConfigData.m_Configurations[i];
  1597. pOutAllConfigs->AddSubKey( ConfigPreprocessorSettingsAsKV( pConfig ) );
  1598. }
  1599. char szNum[64];
  1600. {
  1601. KeyValues *pOutInputs = new KeyValues( "inputs" );
  1602. int nInput = 0;
  1603. pOutKeyValues->AddSubKey( pOutInputs );
  1604. for ( int i = 0; i < schemaFileInfos.Count(); i++ )
  1605. {
  1606. V_snprintf( szNum, sizeof(szNum), "%03d", nInput++ );
  1607. SchemaFileInfo_t &fileInfo = schemaFileInfos[i];
  1608. KeyValues *pOutFile = new KeyValues( szNum );
  1609. pOutInputs->AddSubKey( pOutFile );
  1610. pOutFile->SetString( "generation_target", fileInfo.szGeneratedFile );
  1611. pOutFile->SetBool( "is_cpp", fileInfo.bIsCppFile );
  1612. pOutFile->SetString( "input", fileInfo.szFile );
  1613. }
  1614. }
  1615. CUtlBuffer tmpBuf;
  1616. tmpBuf.SetBufferType( true, true );
  1617. pOutKeyValues->RecursiveSaveToFile( tmpBuf, 0 );
  1618. pOutKeyValues->deleteThis();
  1619. FILE *fp = fopen( szSchemaPath, "wt" );
  1620. if ( !fp )
  1621. {
  1622. g_pVPC->VPCStatus( true, "Error Saving File: '%s'", szSchemaPath );
  1623. }
  1624. else
  1625. {
  1626. fwrite( tmpBuf.Base(), sizeof(char), tmpBuf.TellMaxPut(), fp );
  1627. fclose( fp );
  1628. }
  1629. }
  1630. KeyValues *ConfigPreprocessorSettingsAsKV( CSpecificConfig * pConfig )
  1631. {
  1632. KeyValues *pOutConfig = new KeyValues( pConfig->GetConfigName() );
  1633. char szNum[64];
  1634. KeyValues *pInConfigKV = pConfig->m_pKV;
  1635. //////////////////////////////////////////////////////////////////////////
  1636. // write defines
  1637. {
  1638. KeyValues *pOutDefines = new KeyValues( "defines" );
  1639. pOutConfig->AddSubKey( pOutDefines );
  1640. CSplitString outStrings( pInConfigKV->GetString( g_pOption_PreprocessorDefinitions ),
  1641. (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  1642. int nDefine = 0;
  1643. for ( int i=0; i < outStrings.Count(); i++ )
  1644. {
  1645. V_snprintf( szNum, sizeof(szNum), "%03d", nDefine++ );
  1646. pOutDefines->SetString( szNum, outStrings[i] );
  1647. }
  1648. for ( int i=0; i < g_pVPC->m_Macros.Count(); i++ )
  1649. {
  1650. macro_t *pMacro = &g_pVPC->m_Macros[i];
  1651. if ( pMacro->m_bSetupDefineInProjectFile )
  1652. {
  1653. V_snprintf( szNum, sizeof(szNum), "%03d", nDefine++ );
  1654. pOutDefines->SetString( szNum, outStrings[i] );
  1655. }
  1656. }
  1657. }
  1658. //////////////////////////////////////////////////////////////////////////
  1659. // write includes
  1660. KeyValues *pOutIncludes = new KeyValues( "includes" );
  1661. pOutConfig->AddSubKey( pOutIncludes );
  1662. int nInclude = 0;
  1663. CSplitString outStrings( pInConfigKV->GetString( g_pOption_AdditionalIncludeDirectories ), (const char**)g_IncludeSeparators, Q_ARRAYSIZE(g_IncludeSeparators) );
  1664. for ( int i=0; i < outStrings.Count(); i++ )
  1665. {
  1666. V_snprintf( szNum, sizeof(szNum), "%03d", nInclude++ );
  1667. char sDir[MAX_PATH];
  1668. V_StrSubst( outStrings[i], "$(IntDir)", "$(OBJ_DIR)", sDir, sizeof( sDir ) );
  1669. V_FixSlashes( sDir, '/' );
  1670. pOutIncludes->SetString( szNum, sDir );
  1671. }
  1672. return pOutConfig;
  1673. }