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.

614 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: .360 Creation for all studiomdl generated files (mdl, vvd, vtx, ani, phy)
  4. //
  5. //=====================================================================================//
  6. #include "MakeGameData.h"
  7. #include "studiobyteswap.h"
  8. #include "studio.h"
  9. #include "vphysics_interface.h"
  10. #include "materialsystem/IMaterial.h"
  11. #include "materialsystem/hardwareverts.h"
  12. #include "optimize.h"
  13. //-----------------------------------------------------------------------------
  14. // Models are already converted in a pre-pass.
  15. //-----------------------------------------------------------------------------
  16. bool CreateTargetFile_Model( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
  17. {
  18. // model component should be present
  19. CUtlBuffer targetBuffer;
  20. if ( !scriptlib->ReadFileToBuffer( pTargetName, targetBuffer ) )
  21. {
  22. return false;
  23. }
  24. // no conversion to write, but possibly zipped
  25. bool bSuccess = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, WRITE_TO_DISK_NEVER );
  26. return bSuccess;
  27. }
  28. //-----------------------------------------------------------------------------
  29. // Load necessary dlls
  30. //-----------------------------------------------------------------------------
  31. bool InitStudioByteSwap( void )
  32. {
  33. StudioByteSwap::SetVerbose( false );
  34. StudioByteSwap::ActivateByteSwapping( true );
  35. StudioByteSwap::SetCollisionInterface( g_pPhysicsCollision );
  36. return true;
  37. }
  38. //----------------------------------------------------------------------
  39. // Get list of files that a model requires.
  40. //----------------------------------------------------------------------
  41. bool GetDependants_MDL( const char *pModelName, CUtlVector< CUtlString > *pList )
  42. {
  43. if ( !g_bModPathIsValid )
  44. {
  45. Msg( "Indeterminate mod path, Cannot perform BSP conversion\n" );
  46. return false;
  47. }
  48. CUtlBuffer sourceBuf;
  49. if ( !g_pFullFileSystem->ReadFile( pModelName, "GAME", sourceBuf ) )
  50. {
  51. Msg( "Error! Couldn't open file '%s'!\n", pModelName );
  52. return false;
  53. }
  54. studiohdr_t *pStudioHdr = (studiohdr_t *)sourceBuf.Base();
  55. Studio_ConvertStudioHdrToNewVersion( pStudioHdr );
  56. if ( pStudioHdr->version != STUDIO_VERSION )
  57. {
  58. Msg( "Error! Bad Model '%s', Expecting Version (%d), got (%d)\n", pModelName, STUDIO_VERSION, pStudioHdr->version );
  59. return false;
  60. }
  61. char szOutName[MAX_PATH];
  62. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_OBSOLETE )
  63. {
  64. V_strncpy( szOutName, "materials/sprites/obsolete.vmt", sizeof( szOutName ) );
  65. V_FixSlashes( szOutName );
  66. pList->AddToTail( szOutName );
  67. }
  68. else if ( pStudioHdr->textureindex != 0 )
  69. {
  70. // iterate each texture
  71. int i;
  72. int j;
  73. for ( i = 0; i < pStudioHdr->numtextures; i++ )
  74. {
  75. // iterate through all directories until a valid material is found
  76. bool bFound = false;
  77. for ( j = 0; j < pStudioHdr->numcdtextures; j++ )
  78. {
  79. char szPath[MAX_PATH];
  80. V_ComposeFileName( "materials", pStudioHdr->pCdtexture( j ), szPath, sizeof( szPath ) );
  81. // should have been fixed in studiomdl
  82. // some mdls are ending up with double slashes, borking loads
  83. int len = strlen( szPath );
  84. if ( len > 2 && szPath[len-2] == '\\' && szPath[len-1] == '\\' )
  85. {
  86. szPath[len-1] = '\0';
  87. }
  88. V_ComposeFileName( szPath, pStudioHdr->pTexture( i )->pszName(), szOutName, sizeof( szOutName ) );
  89. V_SetExtension( szOutName, ".vmt", sizeof( szOutName ) );
  90. if ( g_pFullFileSystem->FileExists( szOutName, "GAME" ) )
  91. {
  92. bFound = true;
  93. break;
  94. }
  95. }
  96. if ( bFound )
  97. {
  98. pList->AddToTail( szOutName );
  99. }
  100. }
  101. }
  102. return true;
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Get the preload data for a vhv file
  106. //-----------------------------------------------------------------------------
  107. bool GetPreloadData_VHV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
  108. {
  109. HardwareVerts::FileHeader_t *pHeader = (HardwareVerts::FileHeader_t *)fileBufferIn.Base();
  110. unsigned int version = BigLong( pHeader->m_nVersion );
  111. // ensure caller's buffer is clean
  112. // caller determines preload size, via TellMaxPut()
  113. preloadBufferOut.Purge();
  114. if ( version != VHV_VERSION )
  115. {
  116. // bad version
  117. Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, VHV_VERSION, version );
  118. return false;
  119. }
  120. unsigned int nPreloadSize = sizeof( HardwareVerts::FileHeader_t );
  121. preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
  122. return true;
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Get the preload data for a vtx file
  126. //-----------------------------------------------------------------------------
  127. bool GetPreloadData_VTX( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
  128. {
  129. OptimizedModel::FileHeader_t *pHeader = (OptimizedModel::FileHeader_t *)fileBufferIn.Base();
  130. unsigned int version = BigLong( pHeader->version );
  131. // ensure caller's buffer is clean
  132. // caller determines preload size, via TellMaxPut()
  133. preloadBufferOut.Purge();
  134. if ( version != OPTIMIZED_MODEL_FILE_VERSION )
  135. {
  136. // bad version
  137. Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, OPTIMIZED_MODEL_FILE_VERSION, version );
  138. return false;
  139. }
  140. unsigned int nPreloadSize = sizeof( OptimizedModel::FileHeader_t );
  141. preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
  142. return true;
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Get the preload data for a vvd file
  146. //-----------------------------------------------------------------------------
  147. bool GetPreloadData_VVD( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
  148. {
  149. vertexFileHeader_t *pHeader = (vertexFileHeader_t *)fileBufferIn.Base();
  150. unsigned int id = BigLong( pHeader->id );
  151. unsigned int version = BigLong( pHeader->version );
  152. // ensure caller's buffer is clean
  153. // caller determines preload size, via TellMaxPut()
  154. preloadBufferOut.Purge();
  155. if ( id != MODEL_VERTEX_FILE_ID )
  156. {
  157. // bad version
  158. Msg( "Can't preload: '%s', expecting id %d got id %d\n", pFilename, MODEL_VERTEX_FILE_ID, id );
  159. return false;
  160. }
  161. if ( version != MODEL_VERTEX_FILE_VERSION )
  162. {
  163. // bad version
  164. Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, MODEL_VERTEX_FILE_VERSION, version );
  165. return false;
  166. }
  167. unsigned int nPreloadSize = sizeof( vertexFileHeader_t );
  168. preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
  169. return true;
  170. }
  171. bool CompressFunc( const void *pInput, int inputSize, void **pOutput, int *pOutputSize )
  172. {
  173. *pOutput = NULL;
  174. *pOutputSize = 0;
  175. if ( !inputSize )
  176. {
  177. // nothing to do
  178. return false;
  179. }
  180. unsigned int compressedSize;
  181. unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)pInput, inputSize, &compressedSize );
  182. if ( pCompressedOutput )
  183. {
  184. *pOutput = pCompressedOutput;
  185. *pOutputSize = compressedSize;
  186. return true;
  187. }
  188. return false;
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Rebuilds all of a MDL's components.
  192. //-----------------------------------------------------------------------------
  193. static bool GenerateModelFiles( const char *pMdlFilename )
  194. {
  195. CUtlBuffer tempBuffer;
  196. int fileSize;
  197. int paddedSize;
  198. int swappedSize;
  199. // .mdl
  200. CUtlBuffer mdlBuffer;
  201. if ( !scriptlib->ReadFileToBuffer( pMdlFilename, mdlBuffer ) )
  202. {
  203. return false;
  204. }
  205. if ( !Studio_ConvertStudioHdrToNewVersion( (studiohdr_t *)mdlBuffer.Base() ))
  206. {
  207. Msg("%s needs to be recompiled\n", pMdlFilename );
  208. }
  209. // .vtx
  210. char szVtxFilename[MAX_PATH];
  211. V_StripExtension( pMdlFilename, szVtxFilename, sizeof( szVtxFilename ) );
  212. V_strncat( szVtxFilename, ".dx90.vtx", sizeof( szVtxFilename ) );
  213. CUtlBuffer vtxBuffer;
  214. bool bHasVtx = ReadFileToBuffer( szVtxFilename, vtxBuffer, false, true );
  215. // .vvd
  216. char szVvdFilename[MAX_PATH];
  217. V_StripExtension( pMdlFilename, szVvdFilename, sizeof( szVvdFilename ) );
  218. V_strncat( szVvdFilename, ".vvd", sizeof( szVvdFilename ) );
  219. CUtlBuffer vvdBuffer;
  220. bool bHasVvd = ReadFileToBuffer( szVvdFilename, vvdBuffer, false, true );
  221. if ( bHasVtx != bHasVvd )
  222. {
  223. // paired resources, either mandates the other
  224. return false;
  225. }
  226. // a .mdl file that has .vtx/.vvd gets re-processed to cull lod data
  227. if ( bHasVtx && bHasVvd )
  228. {
  229. // cull lod if needed
  230. IMdlStripInfo *pStripInfo = NULL;
  231. bool bResult = mdllib->StripModelBuffers( mdlBuffer, vvdBuffer, vtxBuffer, &pStripInfo );
  232. if ( !bResult )
  233. {
  234. return false;
  235. }
  236. if ( pStripInfo )
  237. {
  238. // .vsi
  239. CUtlBuffer vsiBuffer;
  240. pStripInfo->Serialize( vsiBuffer );
  241. pStripInfo->DeleteThis();
  242. // save strip info for later processing
  243. char szVsiFilename[MAX_PATH];
  244. V_StripExtension( pMdlFilename, szVsiFilename, sizeof( szVsiFilename ) );
  245. V_strncat( szVsiFilename, ".vsi", sizeof( szVsiFilename ) );
  246. WriteBufferToFile( szVsiFilename, vsiBuffer, false, WRITE_TO_DISK_ALWAYS );
  247. }
  248. }
  249. // .ani processing may further update .mdl buffer
  250. char szAniFilename[MAX_PATH];
  251. V_StripExtension( pMdlFilename, szAniFilename, sizeof( szAniFilename ) );
  252. V_strncat( szAniFilename, ".ani", sizeof( szAniFilename ) );
  253. CUtlBuffer aniBuffer;
  254. bool bHasAni = ReadFileToBuffer( szAniFilename, aniBuffer, false, true );
  255. if ( bHasAni )
  256. {
  257. // Some vestigal .ani files exist in the tree, only process valid .ani
  258. if ( ((studiohdr_t*)mdlBuffer.Base())->numanimblocks != 0 )
  259. {
  260. // .ani processing modifies .mdl buffer
  261. fileSize = aniBuffer.TellPut();
  262. paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
  263. aniBuffer.EnsureCapacity( paddedSize );
  264. tempBuffer.EnsureCapacity( paddedSize );
  265. V_StripExtension( pMdlFilename, szAniFilename, sizeof( szAniFilename ) );
  266. V_strncat( szAniFilename, ".360.ani", sizeof( szAniFilename ) );
  267. swappedSize = StudioByteSwap::ByteswapStudioFile( szAniFilename, tempBuffer.Base(), aniBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
  268. if ( swappedSize > 0 )
  269. {
  270. // .ani buffer is replaced with swapped data
  271. aniBuffer.Purge();
  272. aniBuffer.Put( tempBuffer.Base(), swappedSize );
  273. WriteBufferToFile( szAniFilename, aniBuffer, false, WRITE_TO_DISK_ALWAYS );
  274. }
  275. else
  276. {
  277. return false;
  278. }
  279. }
  280. }
  281. // .phy
  282. char szPhyFilename[MAX_PATH];
  283. V_StripExtension( pMdlFilename, szPhyFilename, sizeof( szPhyFilename ) );
  284. V_strncat( szPhyFilename, ".phy", sizeof( szPhyFilename ) );
  285. CUtlBuffer phyBuffer;
  286. bool bHasPhy = ReadFileToBuffer( szPhyFilename, phyBuffer, false, true );
  287. if ( bHasPhy )
  288. {
  289. fileSize = phyBuffer.TellPut();
  290. paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
  291. phyBuffer.EnsureCapacity( paddedSize );
  292. tempBuffer.EnsureCapacity( paddedSize );
  293. V_StripExtension( pMdlFilename, szPhyFilename, sizeof( szPhyFilename ) );
  294. V_strncat( szPhyFilename, ".360.phy", sizeof( szPhyFilename ) );
  295. swappedSize = StudioByteSwap::ByteswapStudioFile( szPhyFilename, tempBuffer.Base(), phyBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
  296. if ( swappedSize > 0 )
  297. {
  298. // .phy buffer is replaced with swapped data
  299. phyBuffer.Purge();
  300. phyBuffer.Put( tempBuffer.Base(), swappedSize );
  301. WriteBufferToFile( szPhyFilename, phyBuffer, false, WRITE_TO_DISK_ALWAYS );
  302. }
  303. else
  304. {
  305. return false;
  306. }
  307. }
  308. if ( bHasVtx )
  309. {
  310. fileSize = vtxBuffer.TellPut();
  311. paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
  312. vtxBuffer.EnsureCapacity( paddedSize );
  313. tempBuffer.EnsureCapacity( paddedSize );
  314. V_StripExtension( pMdlFilename, szVtxFilename, sizeof( szVtxFilename ) );
  315. V_strncat( szVtxFilename, ".dx90.360.vtx", sizeof( szVtxFilename ) );
  316. swappedSize = StudioByteSwap::ByteswapStudioFile( szVtxFilename, tempBuffer.Base(), vtxBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
  317. if ( swappedSize > 0 )
  318. {
  319. // .vtx buffer is replaced with swapped data
  320. vtxBuffer.Purge();
  321. vtxBuffer.Put( tempBuffer.Base(), swappedSize );
  322. WriteBufferToFile( szVtxFilename, vtxBuffer, false, WRITE_TO_DISK_ALWAYS );
  323. }
  324. else
  325. {
  326. return false;
  327. }
  328. }
  329. if ( bHasVvd )
  330. {
  331. fileSize = vvdBuffer.TellPut();
  332. paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
  333. vvdBuffer.EnsureCapacity( paddedSize );
  334. tempBuffer.EnsureCapacity( paddedSize );
  335. V_StripExtension( pMdlFilename, szVvdFilename, sizeof( szVvdFilename ) );
  336. V_strncat( szVvdFilename, ".360.vvd", sizeof( szVvdFilename ) );
  337. swappedSize = StudioByteSwap::ByteswapStudioFile( szVvdFilename, tempBuffer.Base(), vvdBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
  338. if ( swappedSize > 0 )
  339. {
  340. // .vvd buffer is replaced with swapped data
  341. vvdBuffer.Purge();
  342. vvdBuffer.Put( tempBuffer.Base(), swappedSize );
  343. WriteBufferToFile( szVvdFilename, vvdBuffer, false, WRITE_TO_DISK_ALWAYS );
  344. }
  345. else
  346. {
  347. return false;
  348. }
  349. }
  350. // swap and write final .mdl
  351. fileSize = mdlBuffer.TellPut();
  352. paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
  353. mdlBuffer.EnsureCapacity( paddedSize );
  354. tempBuffer.EnsureCapacity( paddedSize );
  355. char szMdlFilename[MAX_PATH];
  356. V_StripExtension( pMdlFilename, szMdlFilename, sizeof( szMdlFilename ) );
  357. V_strncat( szMdlFilename, ".360.mdl", sizeof( szMdlFilename ) );
  358. swappedSize = StudioByteSwap::ByteswapStudioFile( szMdlFilename, tempBuffer.Base(), mdlBuffer.PeekGet(), fileSize, NULL, CompressFunc );
  359. if ( swappedSize > 0 )
  360. {
  361. // .mdl buffer is replaced with swapped data
  362. mdlBuffer.Purge();
  363. mdlBuffer.Put( tempBuffer.Base(), swappedSize );
  364. WriteBufferToFile( szMdlFilename, mdlBuffer, false, WRITE_TO_DISK_ALWAYS );
  365. }
  366. else
  367. {
  368. return false;
  369. }
  370. return true;
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Returns true if specified model path has a dirty sub-component, and requires
  374. // update.
  375. //-----------------------------------------------------------------------------
  376. static bool ModelNeedsUpdate( const char *pMdlSourcePath )
  377. {
  378. struct ModelExtensions_t
  379. {
  380. const char *pSourceExtension;
  381. const char *pTargetExtension;
  382. bool bSourceMustExist; // if source exists, so must target
  383. };
  384. ModelExtensions_t pExtensions[] =
  385. {
  386. { ".mdl", ".360.mdl", true },
  387. { ".dx90.vtx", ".dx90.360.vtx", false },
  388. { ".vvd", ".360.vvd", false },
  389. { ".phy", ".360.phy", false },
  390. { ".ani", ".360.ani", false },
  391. // vtx/vvd generate a vsi, vsi must be fresher to be valid
  392. { ".dx90.vtx", ".vsi", false },
  393. { ".vvd", ".vsi", false },
  394. };
  395. if ( g_bForce )
  396. {
  397. return true;
  398. }
  399. for ( int i = 0; i < ARRAYSIZE( pExtensions ); i++ )
  400. {
  401. char szSourcePath[MAX_PATH];
  402. struct _stat sourceStatBuf;
  403. V_strncpy( szSourcePath, pMdlSourcePath, sizeof( szSourcePath ) );
  404. V_SetExtension( szSourcePath, pExtensions[i].pSourceExtension, sizeof( szSourcePath ) );
  405. int retVal = _stat( szSourcePath, &sourceStatBuf );
  406. if ( retVal != 0 )
  407. {
  408. // couldn't get source
  409. if ( pExtensions[i].bSourceMustExist )
  410. {
  411. return true;
  412. }
  413. else
  414. {
  415. // source is optional
  416. continue;
  417. }
  418. }
  419. char szTargetPath[MAX_PATH];
  420. struct _stat targetStatBuf;
  421. V_strncpy( szTargetPath, pMdlSourcePath, sizeof( szTargetPath ) );
  422. V_SetExtension( szTargetPath, pExtensions[i].pTargetExtension, sizeof( szTargetPath ) );
  423. if ( _stat( szTargetPath, &targetStatBuf ) != 0 )
  424. {
  425. // target doesn't exist
  426. return true;
  427. }
  428. if ( difftime( sourceStatBuf.st_mtime, targetStatBuf.st_mtime ) > 0 )
  429. {
  430. // source is older (thus newer), update required
  431. return true;
  432. }
  433. }
  434. return false;
  435. }
  436. static bool ModelNamesLessFunc( CUtlString const &pLHS, CUtlString const &pRHS )
  437. {
  438. return CaselessStringLessThan( pLHS.Get(), pRHS.Get() );
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Models require specialized group handling to generate intermediate lod culled
  442. // versions that are then used as the the source for target conversion.
  443. //-----------------------------------------------------------------------------
  444. bool PreprocessModelFiles( CUtlVector<fileList_t> &fileList )
  445. {
  446. if ( !InitStudioByteSwap() )
  447. {
  448. return false;
  449. }
  450. CUtlVector< CUtlString > updateList;
  451. CUtlRBTree< CUtlString, int > visitedModels( 0, 0, ModelNamesLessFunc );
  452. char szSourcePath[MAX_PATH];
  453. strcpy( szSourcePath, g_szSourcePath );
  454. V_StripFilename( szSourcePath );
  455. if ( !szSourcePath[0] )
  456. strcpy( szSourcePath, "." );
  457. V_AppendSlash( szSourcePath, sizeof( szSourcePath ) );
  458. char szModelName[MAX_PATH];
  459. for ( int i=0; i<fileList.Count(); i++ )
  460. {
  461. V_strncpy( szModelName, fileList[i].fileName.String(), sizeof( szModelName ) );
  462. if ( V_stristr( szModelName, ".360." ) )
  463. {
  464. // must ignore any target files
  465. continue;
  466. }
  467. // want only model related files
  468. char *pExtension = V_stristr( szModelName, ".mdl" );
  469. if ( !pExtension )
  470. {
  471. pExtension = V_stristr( szModelName, ".dx90.vtx" );
  472. if ( !pExtension )
  473. {
  474. pExtension = V_stristr( szModelName, ".vvd" );
  475. if ( !pExtension )
  476. {
  477. pExtension = V_stristr( szModelName, ".ani" );
  478. if ( !pExtension )
  479. {
  480. pExtension = V_stristr( szModelName, ".phy" );
  481. if ( !pExtension )
  482. {
  483. pExtension = V_stristr( szModelName, ".vsi" );
  484. if ( !pExtension )
  485. {
  486. continue;
  487. }
  488. }
  489. }
  490. }
  491. }
  492. }
  493. *pExtension = '\0';
  494. V_strncat( szModelName, ".mdl", sizeof( szModelName ) );
  495. if ( visitedModels.Find( szModelName ) != visitedModels.InvalidIndex() )
  496. {
  497. // already processed
  498. continue;
  499. }
  500. visitedModels.Insert( szModelName );
  501. // resolve to full source path
  502. const char *ptr = szModelName;
  503. if ( !strnicmp( ptr, ".\\", 2 ) )
  504. ptr += 2;
  505. else if ( !strnicmp( ptr, szSourcePath, strlen( szSourcePath ) ) )
  506. ptr += strlen( szSourcePath );
  507. char szCleanName[MAX_PATH];
  508. strcpy( szCleanName, szSourcePath );
  509. strcat( szCleanName, ptr );
  510. char szFullSourcePath[MAX_PATH];
  511. _fullpath( szFullSourcePath, szCleanName, sizeof( szFullSourcePath ) );
  512. // any one dirty component generates the set of all expected files
  513. if ( ModelNeedsUpdate( szFullSourcePath ) )
  514. {
  515. int index = updateList.AddToTail();
  516. updateList[index].Set( szFullSourcePath );
  517. }
  518. }
  519. Msg( "\n" );
  520. Msg( "Model Pre Pass: Updating %d Models.\n", updateList.Count() );
  521. for ( int i = 0; i < updateList.Count(); i++ )
  522. {
  523. if ( !GenerateModelFiles( updateList[i].String() ) )
  524. {
  525. int error = g_errorList.AddToTail();
  526. g_errorList[error].result = false;
  527. g_errorList[error].fileName.Set( updateList[i].String() );
  528. }
  529. }
  530. // iterate error list
  531. if ( g_errorList.Count() )
  532. {
  533. Msg( "\n" );
  534. for ( int i = 0; i < g_errorList.Count(); i++ )
  535. {
  536. Msg( "ERROR: could not pre-process model: %s\n", g_errorList[i].fileName.String() );
  537. }
  538. }
  539. return true;
  540. }