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.

1174 lines
31 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Generates a file list based on command line wildcard spec and drives
  4. // conversion routines based on file extension. The conversion routines should be
  5. // !!!elsewhere!!! in libraries that the game also uses at runtime to convert data.
  6. // This tool as spec'd should just be file iteration.
  7. //
  8. //=====================================================================================//
  9. #include "MakeGameData.h"
  10. // MAKESCENESIMAGE is defined for the external tool. In general, it only
  11. // supports the -pcscenes option. This gets built into MakeScenesImage.exe.
  12. //-----------------------------------------------------------------------------
  13. // The application object
  14. //-----------------------------------------------------------------------------
  15. class MakeGameDataApp : public CDefaultAppSystemGroup< CSteamAppSystemGroup >
  16. {
  17. public:
  18. // Methods of IApplication
  19. virtual bool Create();
  20. virtual bool PreInit( );
  21. virtual int Main();
  22. virtual void PostShutdown();
  23. };
  24. DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( MakeGameDataApp );
  25. char g_szSourcePath[MAX_PATH];
  26. char g_targetPath[MAX_PATH];
  27. char g_zipPath[MAX_PATH];
  28. bool g_bForce;
  29. bool g_bTest;
  30. bool g_bMakeZip;
  31. CXZipTool g_MasterXZip;
  32. DiskWriteMode_t g_WriteModeForConversions;
  33. char g_szGamePath[MAX_PATH];
  34. char g_szModPath[MAX_PATH];
  35. bool g_bModPathIsValid;
  36. bool g_bQuiet;
  37. bool g_bMakeScenes;
  38. bool g_bMakeScenesPC;
  39. bool g_bIsPlatformZip;
  40. bool g_bUseMapList;
  41. IPhysicsCollision *g_pPhysicsCollision;
  42. CSysModule *g_pPhysicsModule;
  43. CUtlVector< CUtlString > g_ValidMapList;
  44. CUtlVector< errorList_t > g_errorList;
  45. const char *g_GameNames[] =
  46. {
  47. "ep2",
  48. "episodic",
  49. "hl2",
  50. "portal",
  51. "platform",
  52. "tf",
  53. NULL
  54. };
  55. // all known languages
  56. const char *g_pLanguageSuffixes[] =
  57. {
  58. "_dannish",
  59. "_dutch",
  60. "_english",
  61. "_finnish",
  62. "_french",
  63. "_german",
  64. "_italian",
  65. "_japanese",
  66. "_korean",
  67. "_koreana",
  68. "_norwegian",
  69. "_polish",
  70. "_portuguese",
  71. "_russian",
  72. "_russion_buka",
  73. "_schinese",
  74. "_spanish",
  75. "_swedish",
  76. "_tchinese",
  77. "_thai",
  78. };
  79. // 360 is shipping with support for only these languages
  80. const char *g_pTargetLanguageSuffixes[] =
  81. {
  82. "_english.",
  83. "_french.",
  84. "_german.",
  85. };
  86. // Master list of files that can go into the zip
  87. static char *s_AllowedExtensionsInZip[] =
  88. {
  89. // Explicitly lacking from this list, thus excluded from the zip
  90. // .ain - AINs are external to the zip
  91. // .bsp - BSPs are external to the zip
  92. // .mp3 - MP3s aren't supported
  93. // Extensions with conversions
  94. // Purposely using .360 encoding to ensure pc versions don't leak in
  95. ".360.wav",
  96. ".360.vtf",
  97. ".360.mdl",
  98. ".360.ani",
  99. ".dx90.360.vtx",
  100. ".360.vvd",
  101. ".360.phy",
  102. ".360.dat",
  103. ".360.lst",
  104. ".360.vcs",
  105. ".360.image",
  106. ".360.pcf",
  107. // Extensions without conversions (taken as is)
  108. ".rc",
  109. ".txt",
  110. ".cfg",
  111. ".res",
  112. ".vfe",
  113. ".vbf",
  114. ".vmt",
  115. ".raw",
  116. ".lst",
  117. ".bns",
  118. };
  119. //-----------------------------------------------------------------------------
  120. // Determine game path
  121. //-----------------------------------------------------------------------------
  122. void GetGamePath()
  123. {
  124. GetCurrentDirectory( sizeof( g_szGamePath ), g_szGamePath );
  125. char szFullPath[MAX_PATH];
  126. if ( _fullpath( szFullPath, g_szGamePath, sizeof( szFullPath ) ) )
  127. {
  128. strcpy( g_szGamePath, szFullPath );
  129. }
  130. V_AppendSlash( g_szGamePath, sizeof( g_szGamePath ) );
  131. char *pGameDir = V_stristr( g_szGamePath, "game\\" );
  132. if ( !pGameDir )
  133. {
  134. Msg( "ERROR: Failed to determine game directory from current path. Expecting 'game' in current path." );
  135. exit( 1 );
  136. }
  137. // kill any trailing dirs
  138. pGameDir[4] = '\0';
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Determine mod path
  142. //-----------------------------------------------------------------------------
  143. bool GetModPath()
  144. {
  145. char szDirectory[MAX_PATH];
  146. char szLastDirectory[MAX_PATH];
  147. // non destructively determine the mod directory
  148. bool bFound = false;
  149. szLastDirectory[0] = '\0';
  150. GetCurrentDirectory( sizeof( szDirectory ), szDirectory );
  151. while ( 1 )
  152. {
  153. V_ComposeFileName( szDirectory, "gameinfo.txt", g_szModPath, sizeof( g_szModPath ) );
  154. struct _stat statBuf;
  155. if ( _stat( g_szModPath, &statBuf ) != -1 )
  156. {
  157. bFound = true;
  158. V_strncpy( g_szModPath, szDirectory, sizeof( g_szModPath ) );
  159. break;
  160. }
  161. // previous dir
  162. V_ComposeFileName( szDirectory, "..", g_szModPath, sizeof( g_szModPath ) );
  163. char fullPath[MAX_PATH];
  164. if ( _fullpath( fullPath, g_szModPath, sizeof( fullPath ) ) )
  165. {
  166. strcpy( szDirectory, fullPath );
  167. }
  168. if ( !V_stricmp( szDirectory, szLastDirectory ) )
  169. {
  170. // can back up no further
  171. break;
  172. }
  173. strcpy( szLastDirectory, szDirectory );
  174. }
  175. if ( !bFound )
  176. {
  177. // use current directory instead
  178. GetCurrentDirectory( sizeof( g_szModPath ), g_szModPath );
  179. }
  180. return bFound;
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Setup File system and search paths
  184. //-----------------------------------------------------------------------------
  185. bool SetupFileSystem()
  186. {
  187. if ( g_bModPathIsValid )
  188. {
  189. CFSSteamSetupInfo steamInfo;
  190. steamInfo.m_pDirectoryName = g_szModPath;
  191. steamInfo.m_bOnlyUseDirectoryName = true;
  192. steamInfo.m_bToolsMode = true;
  193. steamInfo.m_bSetSteamDLLPath = true;
  194. steamInfo.m_bSteam = false;
  195. if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
  196. return false;
  197. CFSMountContentInfo fsInfo;
  198. fsInfo.m_pFileSystem = g_pFullFileSystem;
  199. fsInfo.m_bToolsMode = true;
  200. fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
  201. if ( FileSystem_MountContent( fsInfo ) != FS_OK )
  202. return false;
  203. // Finally, load the search paths for the "GAME" path.
  204. CFSSearchPathsInit searchPathsInit;
  205. searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
  206. searchPathsInit.m_pFileSystem = fsInfo.m_pFileSystem;
  207. if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
  208. return false;
  209. char platform[MAX_PATH];
  210. Q_strncpy( platform, steamInfo.m_GameInfoPath, MAX_PATH );
  211. Q_StripTrailingSlash( platform );
  212. Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
  213. fsInfo.m_pFileSystem->AddSearchPath( platform, "PLATFORM" );
  214. }
  215. return true;
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose: Helper utility, read file into buffer
  219. //-----------------------------------------------------------------------------
  220. bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning )
  221. {
  222. return scriptlib->ReadFileToBuffer( pSourceName, buffer, bText, bNoOpenFailureWarning );
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Helper utility, Write buffer to file
  226. //-----------------------------------------------------------------------------
  227. bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, bool bWriteToZip, DiskWriteMode_t writeMode )
  228. {
  229. if ( g_bTest )
  230. return true;
  231. bool bSuccess = scriptlib->WriteBufferToFile( pTargetName, buffer, writeMode );
  232. bool bZipSuccess = true;
  233. if ( bSuccess && g_bMakeZip && !g_bTest && bWriteToZip )
  234. {
  235. if ( !g_MasterXZip.AddBuffer( pTargetName, buffer, true ) )
  236. {
  237. Msg( "WriteBufferToFile(): Error adding file %s\n", pTargetName );
  238. bZipSuccess = false;
  239. }
  240. }
  241. return bSuccess && bZipSuccess;
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Compress data
  245. //-----------------------------------------------------------------------------
  246. bool CompressCallback( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer )
  247. {
  248. if ( !inputBuffer.TellPut() )
  249. {
  250. // nothing to do
  251. return false;
  252. }
  253. unsigned int compressedSize;
  254. unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)inputBuffer.Base() + inputBuffer.TellGet(), inputBuffer.TellPut() - inputBuffer.TellGet(), &compressedSize );
  255. if ( pCompressedOutput )
  256. {
  257. outputBuffer.EnsureCapacity( compressedSize );
  258. outputBuffer.Put( pCompressedOutput, compressedSize );
  259. free( pCompressedOutput );
  260. return true;
  261. }
  262. return false;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Some converters need to run a final pass.
  266. //-----------------------------------------------------------------------------
  267. void DoPostProcessingFunctions( bool bWriteToZip )
  268. {
  269. if ( g_bMakeScenes || g_bMakeScenesPC )
  270. {
  271. // scenes are converted and aggregated into one image
  272. CreateSceneImageFile( g_szModPath, bWriteToZip, g_bMakeScenesPC, g_bQuiet, g_WriteModeForConversions );
  273. }
  274. ProcessDXSupportConfig( bWriteToZip );
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Startup any conversion modules.
  278. //-----------------------------------------------------------------------------
  279. bool InitConversionModules()
  280. {
  281. return true;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Shutdown and cleanup any conversion modules.
  285. //-----------------------------------------------------------------------------
  286. void ShutdownConversionModules()
  287. {
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose: Distribute to worker function
  291. //-----------------------------------------------------------------------------
  292. bool CreateTargetFile( const char *pSourceName, const char *pTargetName, fileType_e fileType, bool bWriteToZip )
  293. {
  294. // resolve relative source to absolute path
  295. // same workers use subdir name to determine conversion parameters
  296. char fullSourcePath[MAX_PATH];
  297. if ( _fullpath( fullSourcePath, pSourceName, sizeof( fullSourcePath ) ) )
  298. {
  299. pSourceName = fullSourcePath;
  300. }
  301. // distribute to actual worker
  302. // workers can expect exact final decorated filenames
  303. bool bSuccess = false;
  304. switch ( fileType )
  305. {
  306. case FILETYPE_UNKNOWN:
  307. break;
  308. case FILETYPE_VTF:
  309. bSuccess = CreateTargetFile_VTF( pSourceName, pTargetName, bWriteToZip );
  310. break;
  311. case FILETYPE_WAV:
  312. bSuccess = CreateTargetFile_WAV( pSourceName, pTargetName, bWriteToZip );
  313. break;
  314. case FILETYPE_MDL:
  315. case FILETYPE_ANI:
  316. case FILETYPE_VTX:
  317. case FILETYPE_VVD:
  318. case FILETYPE_PHY:
  319. bSuccess = CreateTargetFile_Model( pSourceName, pTargetName, bWriteToZip );
  320. break;
  321. case FILETYPE_BSP:
  322. bSuccess = CreateTargetFile_BSP( pSourceName, pTargetName, bWriteToZip );
  323. break;
  324. case FILETYPE_AIN:
  325. bSuccess = CreateTargetFile_AIN( pSourceName, pTargetName, bWriteToZip );
  326. break;
  327. case FILETYPE_CCDAT:
  328. bSuccess = CreateTargetFile_CCDAT( pSourceName, pTargetName, bWriteToZip );
  329. break;
  330. case FILETYPE_MP3:
  331. bSuccess = CreateTargetFile_MP3( pSourceName, pTargetName, bWriteToZip );
  332. break;
  333. case FILETYPE_RESLST:
  334. bSuccess = CreateTargetFile_RESLST( pSourceName, pTargetName, bWriteToZip );
  335. break;
  336. case FILETYPE_PCF:
  337. bSuccess = CreateTargetFile_PCF( pSourceName, pTargetName, bWriteToZip );
  338. break;
  339. // others...
  340. }
  341. return bSuccess;
  342. }
  343. //-----------------------------------------------------------------------------
  344. // Purpose: Determine file type based on source extension. Generate .360.xxx
  345. // target name.
  346. //-----------------------------------------------------------------------------
  347. fileType_e ResolveFileType( const char *pSourceName, char *pTargetName, int targetNameSize )
  348. {
  349. char szFullSourcePath[MAX_PATH];
  350. _fullpath( szFullSourcePath, pSourceName, sizeof( szFullSourcePath ) );
  351. char sourceExtension[MAX_PATH];
  352. V_ExtractFileExtension( pSourceName, sourceExtension, sizeof( sourceExtension ) );
  353. // default unrecognized
  354. fileType_e fileType = FILETYPE_UNKNOWN;
  355. if ( !V_stricmp( sourceExtension, "wav" ) )
  356. {
  357. fileType = FILETYPE_WAV;
  358. }
  359. else if ( !V_stricmp( sourceExtension, "vtf" ) )
  360. {
  361. fileType = FILETYPE_VTF;
  362. }
  363. else if ( !V_stricmp( sourceExtension, "mdl" ) )
  364. {
  365. fileType = FILETYPE_MDL;
  366. }
  367. else if ( !V_stricmp( sourceExtension, "ani" ) )
  368. {
  369. fileType = FILETYPE_ANI;
  370. }
  371. else if ( !V_stricmp( sourceExtension, "vvd" ) )
  372. {
  373. fileType = FILETYPE_VVD;
  374. }
  375. else if ( !V_stricmp( sourceExtension, "phy" ) )
  376. {
  377. fileType = FILETYPE_PHY;
  378. }
  379. else if ( !V_stricmp( sourceExtension, "bsp" ) )
  380. {
  381. fileType = FILETYPE_BSP;
  382. }
  383. else if ( !V_stricmp( sourceExtension, "ain" ) )
  384. {
  385. fileType = FILETYPE_AIN;
  386. }
  387. else if ( !V_stricmp( sourceExtension, "dat" ) )
  388. {
  389. if ( V_stristr( pSourceName, "closecaption_" ) )
  390. {
  391. // only want closecaption dat files, ignore all others
  392. fileType = FILETYPE_CCDAT;
  393. }
  394. }
  395. else if ( !V_stricmp( sourceExtension, "vtx" ) )
  396. {
  397. if ( V_stristr( pSourceName, ".dx90" ) )
  398. {
  399. // only want dx90 version, ignore all others
  400. fileType = FILETYPE_VTX;
  401. }
  402. }
  403. else if ( !V_stricmp( sourceExtension, "mp3" ) )
  404. {
  405. // mp3's are already pre-converted into .360.wav
  406. // slam the expected name here to simplify the external logic which will do the right thing
  407. V_StripExtension( pSourceName, pTargetName, targetNameSize );
  408. V_strcat( pTargetName, ".360.wav", targetNameSize );
  409. return FILETYPE_MP3;
  410. }
  411. else if ( !V_stricmp( sourceExtension, "lst" ) )
  412. {
  413. if ( V_stristr( szFullSourcePath, "reslists_xbox\\" ) )
  414. {
  415. // only want reslists map versions, due to special processing, ignore all others
  416. fileType = FILETYPE_RESLST;
  417. }
  418. }
  419. else if ( !V_stricmp( sourceExtension, "pcf" ) )
  420. {
  421. fileType = FILETYPE_PCF;
  422. }
  423. else if ( !V_stricmp( sourceExtension, "image" ) )
  424. {
  425. if ( V_stristr( szFullSourcePath, "scenes\\" ) )
  426. {
  427. // only want scene image, ignore all others
  428. fileType = FILETYPE_SCENEIMAGE;
  429. }
  430. }
  431. if ( fileType != FILETYPE_UNKNOWN && !V_stristr( pSourceName, ".360." ) )
  432. {
  433. char targetExtension[MAX_PATH];
  434. sprintf( targetExtension, ".360.%s", sourceExtension );
  435. V_StripExtension( pSourceName, pTargetName, targetNameSize );
  436. V_strcat( pTargetName, targetExtension, targetNameSize );
  437. }
  438. else
  439. {
  440. // unknown or already converted, target is same as input
  441. V_strncpy( pTargetName, pSourceName, targetNameSize );
  442. }
  443. return fileType;
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Returns TRUE if file is a known localized file.
  447. //-----------------------------------------------------------------------------
  448. bool IsLocalizedFile( const char *pFileName )
  449. {
  450. for ( int i = 0; i<ARRAYSIZE( g_pLanguageSuffixes ); i++ )
  451. {
  452. if ( V_stristr( pFileName, g_pLanguageSuffixes[i] ) )
  453. {
  454. // a localized file
  455. return true;
  456. }
  457. }
  458. // not a known localized file
  459. return false;
  460. }
  461. //-----------------------------------------------------------------------------
  462. // Purpose: Returns TRUE if file is a supported localization.
  463. //-----------------------------------------------------------------------------
  464. bool IsLocalizedFileValid( const char *pFileName, const char *pLanguageSuffix )
  465. {
  466. // file is a localized version
  467. if ( pLanguageSuffix )
  468. {
  469. if ( V_stristr( pFileName, pLanguageSuffix ) )
  470. {
  471. // allow it
  472. return true;
  473. }
  474. return false;
  475. }
  476. // must match the target supported languages
  477. for ( int i = 0; i < ARRAYSIZE( g_pTargetLanguageSuffixes ); i++ )
  478. {
  479. if ( V_stristr( pFileName, g_pTargetLanguageSuffixes[i] ) )
  480. {
  481. // allow it
  482. return true;
  483. }
  484. }
  485. // does not match a target language, not allowed
  486. return false;
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Purpose: Check against a list of allowed filetypes for inclusion in the zip
  490. //-----------------------------------------------------------------------------
  491. bool IncludeInZip( const char *pSourceName )
  492. {
  493. if ( g_bIsPlatformZip )
  494. {
  495. // only allow known valid platform directories
  496. if ( !V_stristr( pSourceName, "materials\\" ) &&
  497. !V_stristr( pSourceName, "resource\\" ) &&
  498. !V_stristr( pSourceName, "vgui\\" ) )
  499. {
  500. // exclude it
  501. return false;
  502. }
  503. }
  504. for ( int i = 0; i < ARRAYSIZE( s_AllowedExtensionsInZip ); ++i )
  505. {
  506. const char *pAllowedExtension = s_AllowedExtensionsInZip[i];
  507. if ( !V_stristr( pSourceName, pAllowedExtension ) )
  508. {
  509. continue;
  510. }
  511. if ( !V_stricmp( pAllowedExtension, ".lst" ) )
  512. {
  513. // only want ???_exclude.lst files
  514. if ( V_stristr( pSourceName, "_exclude.lst" ) )
  515. {
  516. return true;
  517. }
  518. return false;
  519. }
  520. if ( !V_stricmp( pAllowedExtension, ".txt" ) || !V_stricmp( pAllowedExtension, ".360.dat" ) )
  521. {
  522. if ( IsLocalizedFile( pSourceName ) && !IsLocalizedFileValid( pSourceName ) )
  523. {
  524. // exclude unsupported languages
  525. return false;
  526. }
  527. if ( !V_stricmp( pAllowedExtension, ".txt" ) && V_stristr( pSourceName, "closecaption_" ) )
  528. {
  529. // exclude all the closecaption_<language>.txt files
  530. return false;
  531. }
  532. }
  533. return true;
  534. }
  535. // exclude it
  536. return false;
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Returns true if map is in list, otherwise false
  540. //-----------------------------------------------------------------------------
  541. bool IsMapNameInList( const char *pMapName, CUtlVector< CUtlString > &mapList )
  542. {
  543. char szBaseName[MAX_PATH];
  544. V_FileBase( pMapName, szBaseName, sizeof( szBaseName ) );
  545. V_strlower( szBaseName );
  546. if ( mapList.Find( szBaseName ) != mapList.InvalidIndex() )
  547. {
  548. // found
  549. return true;
  550. }
  551. // not found
  552. return false;
  553. }
  554. //-----------------------------------------------------------------------------
  555. // Purpose: Get the list of valid BSPs
  556. //-----------------------------------------------------------------------------
  557. void BuildValidMapList( CUtlVector< CUtlString > &mapList )
  558. {
  559. char szFilename[MAX_PATH];
  560. V_ComposeFileName( g_szModPath, "maplist.txt", szFilename, sizeof( szFilename ) );
  561. CUtlBuffer buffer;
  562. if ( !ReadFileToBuffer( szFilename, buffer, true, true ) )
  563. {
  564. return;
  565. }
  566. characterset_t breakSet;
  567. CharacterSetBuild( &breakSet, "" );
  568. char szToken[MAX_PATH];
  569. char szMapName[MAX_PATH];
  570. for ( ;; )
  571. {
  572. int nTokenSize = buffer.ParseToken( &breakSet, szToken, sizeof( szToken ) );
  573. if ( nTokenSize <= 0 )
  574. {
  575. break;
  576. }
  577. // reslists are pc built, filenames can be sloppy
  578. V_FileBase( szToken, szMapName, sizeof( szMapName ) );
  579. V_strlower( szMapName );
  580. mapList.AddToTail( szMapName );
  581. }
  582. }
  583. #define DO_UPDATE 0x01
  584. #define DO_ZIP 0x02
  585. //-----------------------------------------------------------------------------
  586. // Purpose: drive the file creation
  587. //-----------------------------------------------------------------------------
  588. void GenerateTargetFiles( CUtlVector<fileList_t> &fileList )
  589. {
  590. char sourcePath[MAX_PATH];
  591. char sourceFile[MAX_PATH];
  592. char targetFile[MAX_PATH];
  593. struct _stat sourceStatBuf;
  594. struct _stat targetStatBuf;
  595. strcpy( sourcePath, g_szSourcePath );
  596. V_StripFilename( sourcePath );
  597. if ( !sourcePath[0] )
  598. strcpy( sourcePath, "." );
  599. V_AppendSlash( sourcePath, sizeof( sourcePath ) );
  600. // default is to update and zip
  601. CUtlVector< int > updateList;
  602. updateList.AddMultipleToTail( fileList.Count() );
  603. for ( int i=0; i<fileList.Count(); i++ )
  604. {
  605. updateList[i] = DO_UPDATE|DO_ZIP;
  606. }
  607. int numMatches = 0;
  608. // build update list
  609. for ( int i=0; i<fileList.Count(); i++ )
  610. {
  611. if ( fileList[i].fileName.IsEmpty() )
  612. {
  613. // ignore entries that have been culled
  614. updateList[i] = 0;
  615. continue;
  616. }
  617. const char *ptr = fileList[i].fileName.String();
  618. if ( !strnicmp( ptr, ".\\", 2 ) )
  619. ptr += 2;
  620. else if ( !strnicmp( ptr, sourcePath, strlen( sourcePath ) ) )
  621. ptr += strlen( sourcePath );
  622. strcpy( sourceFile, sourcePath );
  623. strcat( sourceFile, ptr );
  624. strcpy( targetFile, g_targetPath );
  625. strcat( targetFile, ptr );
  626. fileType_e fileType = ResolveFileType( sourceFile, targetFile, sizeof( targetFile ) );
  627. // check the target name for inclusion due to extension modifications
  628. if ( !IncludeInZip( targetFile ) )
  629. {
  630. // exclude from zip
  631. updateList[i] &= ~DO_ZIP;
  632. }
  633. if ( fileType == FILETYPE_UNKNOWN )
  634. {
  635. // No conversion function, can't do anything
  636. updateList[i] &= ~DO_UPDATE;
  637. continue;
  638. }
  639. else
  640. {
  641. // known filetype, which has a conversion
  642. // the wildcard match may catch existing converted files, which need to be rejected
  643. // cull exisiting conversions from the work list
  644. // the non-converted filename is part of the same wildcard match, gets caught and resolved
  645. // the non-converted filename will then go through the proper conversion path
  646. if ( V_stristr( sourceFile, ".360." ) )
  647. {
  648. // cull completely
  649. updateList[i] = 0;
  650. continue;
  651. }
  652. }
  653. if ( fileType == FILETYPE_BSP || fileType == FILETYPE_AIN || fileType == FILETYPE_RESLST )
  654. {
  655. if ( g_ValidMapList.Count() && !IsMapNameInList( sourceFile, g_ValidMapList ) )
  656. {
  657. // cull completely
  658. updateList[i] = 0;
  659. continue;
  660. }
  661. }
  662. int retVal = _stat( sourceFile, &sourceStatBuf );
  663. if ( retVal != 0 )
  664. {
  665. // couldn't get source, skip update or zip
  666. updateList[i] = 0;
  667. continue;
  668. }
  669. retVal = _stat( targetFile, &targetStatBuf );
  670. if ( retVal != 0 )
  671. {
  672. // target doesn't exit, update is required
  673. continue;
  674. }
  675. // track valid candidates
  676. numMatches++;
  677. if ( fileType == FILETYPE_MDL || fileType == FILETYPE_ANI || fileType == FILETYPE_VTX || fileType == FILETYPE_VVD || fileType == FILETYPE_PHY )
  678. {
  679. // models are converted in a pre-pass
  680. // let the update logic run and catch the conversions for zipping
  681. continue;
  682. }
  683. if ( !g_bForce )
  684. {
  685. if ( difftime( sourceStatBuf.st_mtime, targetStatBuf.st_mtime ) <= 0 )
  686. {
  687. // target is same or older, no update
  688. updateList[i] &= ~DO_UPDATE;
  689. }
  690. }
  691. }
  692. // cleanse and determine totals, makes succeeding logic simpler
  693. int numWorkItems = 0;
  694. int numFilesToUpdate = 0;
  695. int numFilesToZip = 0;
  696. for ( int i=0; i<fileList.Count(); i++ )
  697. {
  698. if ( updateList[i] & DO_UPDATE )
  699. {
  700. numFilesToUpdate++;
  701. }
  702. if ( g_bMakeZip && ( updateList[i] & DO_ZIP ) )
  703. {
  704. numFilesToZip++;
  705. }
  706. else
  707. {
  708. updateList[i] &= ~DO_ZIP;
  709. }
  710. if ( updateList[i] )
  711. {
  712. numWorkItems++;
  713. }
  714. }
  715. Msg( "\n" );
  716. Msg( "Matched %d/%d files.\n", numMatches, fileList.Count() );
  717. Msg( "Creating or Updating %d files.\n", numFilesToUpdate );
  718. if ( g_bMakeZip )
  719. {
  720. Msg( "Zipping %d files.\n", numFilesToZip );
  721. }
  722. InitConversionModules();
  723. if ( numFilesToZip && !g_bTest )
  724. {
  725. Msg( "Creating Zip: %s\n", g_zipPath );
  726. if ( !g_MasterXZip.Begin( g_zipPath, XBOX_DVD_SECTORSIZE ) )
  727. {
  728. Msg( "ERROR: Failed to open \"%s\" for writing.\n", g_zipPath );
  729. return;
  730. }
  731. else
  732. {
  733. SetupCriticalPreloadScript( g_szModPath );
  734. }
  735. }
  736. // iterate work list
  737. int progress = 0;
  738. for ( int i=0; i<fileList.Count(); i++ )
  739. {
  740. if ( !updateList[i] )
  741. {
  742. // no update or zip needed, skip
  743. continue;
  744. }
  745. const char *ptr = fileList[i].fileName.String();
  746. if ( !strnicmp( ptr, ".\\", 2 ) )
  747. ptr += 2;
  748. else if ( !strnicmp( ptr, sourcePath, strlen( sourcePath ) ) )
  749. ptr += strlen( sourcePath );
  750. strcpy( sourceFile, sourcePath );
  751. strcat( sourceFile, ptr );
  752. strcpy( targetFile, g_targetPath );
  753. strcat( targetFile, ptr );
  754. fileType_e fileType = ResolveFileType( sourceFile, targetFile, sizeof( targetFile ) );
  755. if ( !g_bQuiet )
  756. {
  757. Msg( "%d/%d:%s -> %s\n", progress+1, numWorkItems, sourceFile, targetFile );
  758. }
  759. bool bSuccess = true;
  760. if ( updateList[i] & DO_UPDATE )
  761. {
  762. // generate target file (and optionally zip output)
  763. bSuccess = CreateTargetFile( sourceFile, targetFile, fileType, (updateList[i] & DO_ZIP) != 0 );
  764. if ( !bSuccess )
  765. {
  766. // add to error list
  767. int error = g_errorList.AddToTail();
  768. g_errorList[error].result = false;
  769. g_errorList[error].fileName.Set( sourceFile );
  770. }
  771. }
  772. else if ( updateList[i] & DO_ZIP )
  773. {
  774. // existing target file is zipped
  775. CUtlBuffer targetBuffer;
  776. bSuccess = scriptlib->ReadFileToBuffer( targetFile, targetBuffer );
  777. if ( bSuccess )
  778. {
  779. if ( !g_bTest )
  780. {
  781. bSuccess = g_MasterXZip.AddBuffer( targetFile, targetBuffer, true );
  782. }
  783. }
  784. if ( !bSuccess )
  785. {
  786. // add to error list
  787. int error = g_errorList.AddToTail();
  788. g_errorList[error].result = false;
  789. g_errorList[error].fileName.Set( targetFile );
  790. }
  791. }
  792. progress++;
  793. }
  794. DoPostProcessingFunctions( !g_bTest );
  795. ShutdownConversionModules();
  796. if ( numFilesToZip && !g_bTest )
  797. {
  798. g_MasterXZip.End();
  799. }
  800. // iterate error list
  801. Msg( "\n" );
  802. for ( int i = 0; i < g_errorList.Count(); i++ )
  803. {
  804. Msg( "ERROR: could not process %s\n", g_errorList[i].fileName.String() );
  805. }
  806. }
  807. //-----------------------------------------------------------------------------
  808. // Purpose: Spew Usage
  809. //-----------------------------------------------------------------------------
  810. void Usage()
  811. {
  812. Msg( "usage: MakeGameData [filemask] [options]\n" );
  813. Msg( "options:\n" );
  814. Msg( "[-v] Version\n" );
  815. Msg( "[-q] Quiet (critical spew only)\n" );
  816. Msg( "[-h] [-help] [-?] Help\n" );
  817. Msg( "[-t targetPath] Alternate output path, will generate output at target\n" );
  818. Msg( "[-r] [-recurse] Recurse into source directory\n" );
  819. Msg( "[-f] [-force] Force update, otherwise checks timestamps\n" );
  820. Msg( "[-test] Skip writing to disk\n" );
  821. Msg( "[-z <zipname>] Generate zip file AND create or update stale conversions\n" );
  822. Msg( "[-zo <zipname>] Generate zip file ONLY (existing stale conversions get updated, no new conversions are written)\n" );
  823. Msg( "[-preloadinfo] Spew contents of preload section in zip\n" );
  824. Msg( "[-xmaquality <quality>] XMA Encoding quality override, [0-100]\n" );
  825. Msg( "[-scenes] Make 360 scene image cache.\n" );
  826. Msg( "[-pcscenes] Make PC scene image cache.\n" );
  827. Msg( "[-usemaplist] For BSP related conversions, restricts to maplist.txt.\n" );
  828. exit( 1 );
  829. }
  830. //-----------------------------------------------------------------------------
  831. // Purpose: default output func
  832. //-----------------------------------------------------------------------------
  833. SpewRetval_t OutputFunc( SpewType_t spewType, char const *pMsg )
  834. {
  835. printf( pMsg );
  836. if ( spewType == SPEW_ERROR )
  837. {
  838. return SPEW_ABORT;
  839. }
  840. return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE;
  841. }
  842. //-----------------------------------------------------------------------------
  843. // The application object
  844. //-----------------------------------------------------------------------------
  845. bool MakeGameDataApp::Create()
  846. {
  847. SpewOutputFunc( OutputFunc );
  848. AppSystemInfo_t appSystems[] =
  849. {
  850. { "mdllib.dll", MDLLIB_INTERFACE_VERSION },
  851. { "", "" } // Required to terminate the list
  852. };
  853. AddSystem( g_pDataModel, VDATAMODEL_INTERFACE_VERSION );
  854. AddSystem( g_pDmSerializers, DMSERIALIZERS_INTERFACE_VERSION );
  855. // Load vphysics.dll
  856. if ( !Sys_LoadInterface( "vphysics.dll", VPHYSICS_COLLISION_INTERFACE_VERSION, &g_pPhysicsModule, (void**)&g_pPhysicsCollision ) )
  857. {
  858. Msg( "Failed to load vphysics interface\n" );
  859. return false;
  860. }
  861. bool bOk = AddSystems( appSystems );
  862. if ( !bOk )
  863. return false;
  864. return true;
  865. }
  866. bool MakeGameDataApp::PreInit()
  867. {
  868. CreateInterfaceFn factory = GetFactory();
  869. ConnectTier1Libraries( &factory, 1 );
  870. ConnectTier2Libraries( &factory, 1 );
  871. if ( !g_pFullFileSystem || !g_pDataModel || !g_pPhysicsCollision || !mdllib )
  872. {
  873. Warning( "MakeGameData is missing a required interface!\n" );
  874. return false;
  875. }
  876. return true;
  877. }
  878. void MakeGameDataApp::PostShutdown()
  879. {
  880. if ( g_pPhysicsModule )
  881. {
  882. Sys_UnloadModule( g_pPhysicsModule );
  883. g_pPhysicsModule = NULL;
  884. g_pPhysicsCollision = NULL;
  885. }
  886. DisconnectTier2Libraries();
  887. DisconnectTier1Libraries();
  888. }
  889. //-----------------------------------------------------------------------------
  890. // main
  891. //
  892. //-----------------------------------------------------------------------------
  893. int MakeGameDataApp::Main()
  894. {
  895. int argnum;
  896. // set the valve library printer
  897. SpewOutputFunc( OutputFunc );
  898. Msg( "\nMAKEGAMEDATA - Valve Xbox 360 Game Data Compiler (Build: %s %s)\n", __DATE__, __TIME__ );
  899. Msg( "(C) Copyright 1996-2006, Valve Corporation, All rights reserved.\n\n" );
  900. if ( CommandLine()->FindParm( "-v" ) || CommandLine()->FindParm( "-version" ) )
  901. {
  902. // spew just the version, used by batches for logging
  903. return 0;
  904. }
  905. #ifndef MAKESCENESIMAGE
  906. if ( CommandLine()->ParmCount() < 2 || CommandLine()->FindParm( "?" ) || CommandLine()->FindParm( "-h" ) || CommandLine()->FindParm( "-help" ) )
  907. {
  908. Usage();
  909. }
  910. #endif // MAKESCENESIMAGE
  911. bool bHasFileMask = false;
  912. const char *pFirstArg = CommandLine()->GetParm( 1 );
  913. if ( pFirstArg[0] == '-' )
  914. {
  915. // first arg is an option, assume *.*
  916. strcpy( g_szSourcePath, "*.*" );
  917. }
  918. else
  919. {
  920. strcpy( g_szSourcePath, pFirstArg );
  921. bHasFileMask = true;
  922. }
  923. bool bRecurse = false;
  924. #ifndef MAKESCENESIMAGE
  925. bRecurse = CommandLine()->FindParm( "-recurse" ) || CommandLine()->FindParm( "-r" );
  926. g_bForce = CommandLine()->FindParm( "-force" ) || CommandLine()->FindParm( "-f" );
  927. g_bTest = CommandLine()->FindParm( "-test" ) != 0;
  928. g_bQuiet = CommandLine()->FindParm( "-quiet" ) || CommandLine()->FindParm( "-q" );
  929. g_bMakeScenes = CommandLine()->FindParm( "-scenes" ) != 0;
  930. g_bMakeScenesPC = CommandLine()->FindParm( "-pcscenes" ) != 0;
  931. #else
  932. g_bMakeScenesPC = true;
  933. #endif // MAKESCENESIMAGE
  934. #ifndef MAKESCENESIMAGE
  935. g_bUseMapList = CommandLine()->FindParm( "-usemaplist" ) != 0;
  936. #endif // MAKESCENESIMAGE
  937. // Set up zip file options
  938. g_WriteModeForConversions = WRITE_TO_DISK_ALWAYS;
  939. argnum = CommandLine()->FindParm( "-z" );
  940. if ( argnum )
  941. {
  942. strcpy( g_szSourcePath, "*.*" );
  943. g_bMakeZip = true;
  944. g_bMakeScenes = true;
  945. bRecurse = true;
  946. }
  947. else
  948. {
  949. argnum = CommandLine()->FindParm( "-zo" );
  950. if ( argnum )
  951. {
  952. strcpy( g_szSourcePath, "*.*" );
  953. g_bMakeZip = true;
  954. g_bMakeScenes = true;
  955. g_WriteModeForConversions = WRITE_TO_DISK_UPDATE;
  956. bRecurse = true;
  957. }
  958. }
  959. if ( g_bMakeZip )
  960. {
  961. strcat( g_zipPath, CommandLine()->GetParm( argnum + 1 ) );
  962. }
  963. // default target path is source
  964. strcpy( g_targetPath, g_szSourcePath );
  965. V_StripFilename( g_targetPath );
  966. if ( !g_targetPath[0] )
  967. {
  968. strcpy( g_targetPath, "." );
  969. }
  970. // override via command line
  971. argnum = CommandLine()->FindParm( "-t" );
  972. if ( argnum )
  973. {
  974. V_strcpy_safe( g_targetPath, CommandLine()->GetParm( argnum + 1 ) );
  975. }
  976. V_AppendSlash( g_targetPath, sizeof( g_targetPath ) );
  977. if ( CommandLine()->FindParm( "-preloadinfo" ) )
  978. {
  979. g_MasterXZip.SpewPreloadInfo( g_szSourcePath );
  980. return 0;
  981. }
  982. #ifndef MAKESCENESIMAGE
  983. GetGamePath();
  984. #endif // MAKESCENESIMAGE
  985. g_bModPathIsValid = GetModPath();
  986. if ( !SetupFileSystem() )
  987. {
  988. Msg( "ERROR: Failed to setup file system.\n" );
  989. exit( 1 );
  990. }
  991. // data model initialization
  992. g_pDataModel->SetUndoEnabled( false );
  993. g_pDataModel->OnlyCreateUntypedElements( true );
  994. g_pDataModel->SetDefaultElementFactory( NULL );
  995. g_bIsPlatformZip = g_bMakeZip && ( V_stristr( g_szModPath, "\\platform" ) != NULL );
  996. // cleanup any zombie temp files left from a possible prior abort or error
  997. scriptlib->DeleteTemporaryFiles( "mgd_*.tmp" );
  998. if ( g_bMakeZip || g_bUseMapList )
  999. {
  1000. // zips use the map list to narrow the bsp conversion to actual shipping maps
  1001. BuildValidMapList( g_ValidMapList );
  1002. }
  1003. CUtlVector<fileList_t> fileList;
  1004. if ( bHasFileMask || g_bMakeZip )
  1005. {
  1006. scriptlib->FindFiles( g_szSourcePath, bRecurse, fileList );
  1007. }
  1008. // model conversions require seperate pre-processing to achieve grouping
  1009. if ( !PreprocessModelFiles( fileList ) )
  1010. {
  1011. return 0;
  1012. }
  1013. GenerateTargetFiles( fileList );
  1014. return 0;
  1015. }