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.

1323 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10. #include <malloc.h>
  11. #ifdef _WIN32
  12. #include <process.h>
  13. #include <io.h>
  14. #endif
  15. #include <stddef.h>
  16. #include <fcntl.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include "tier1/utlbuffer.h"
  20. #include "tier1/strtools.h"
  21. #include "tier2/riff.h"
  22. #if defined( _WIN32 ) && !defined( _X360 )
  23. #include <windows.h>
  24. #endif
  25. #ifdef MAKE_GAMEDATA_TOOL
  26. #include "../public/materialsystem/shader_vcs_version.h"
  27. #include "../public/materialsystem/imaterial.h"
  28. #include "../public/materialsystem/hardwareverts.h"
  29. #include "../public/vtf/vtf.h"
  30. #else
  31. #include "materialsystem/shader_vcs_version.h"
  32. #include "materialsystem/imaterial.h"
  33. #include "materialsystem/hardwareverts.h"
  34. #endif
  35. #include "xwvfile.h"
  36. #include "xzp.h"
  37. CByteswap g_xzpSwap;
  38. extern IFileReadBinary *g_pSndIO;
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. //-----------------------------------------------------------------------------
  42. // Datadesc blocks for byteswapping:
  43. //-----------------------------------------------------------------------------
  44. BEGIN_BYTESWAP_DATADESC( xZipHeader_t )
  45. DEFINE_FIELD( Magic, FIELD_INTEGER ),
  46. DEFINE_FIELD( Version, FIELD_INTEGER ),
  47. DEFINE_FIELD( PreloadDirectoryEntries, FIELD_INTEGER ),
  48. DEFINE_FIELD( DirectoryEntries, FIELD_INTEGER ),
  49. DEFINE_FIELD( PreloadBytes, FIELD_INTEGER ),
  50. DEFINE_FIELD( HeaderLength, FIELD_INTEGER ),
  51. DEFINE_FIELD( FilenameEntries, FIELD_INTEGER ),
  52. DEFINE_FIELD( FilenameStringsOffset, FIELD_INTEGER ),
  53. DEFINE_FIELD( FilenameStringsLength, FIELD_INTEGER ),
  54. END_BYTESWAP_DATADESC()
  55. BEGIN_BYTESWAP_DATADESC( xZipDirectoryEntry_t )
  56. DEFINE_FIELD( FilenameCRC, FIELD_INTEGER ),
  57. DEFINE_FIELD( Length, FIELD_INTEGER ),
  58. DEFINE_FIELD( StoredOffset, FIELD_INTEGER ),
  59. END_BYTESWAP_DATADESC()
  60. BEGIN_BYTESWAP_DATADESC( xZipFilenameEntry_t )
  61. DEFINE_FIELD( FilenameCRC, FIELD_INTEGER ),
  62. DEFINE_FIELD( FilenameOffset, FIELD_INTEGER ),
  63. DEFINE_FIELD( TimeStamp, FIELD_INTEGER ),
  64. END_BYTESWAP_DATADESC()
  65. BEGIN_BYTESWAP_DATADESC( xZipFooter_t )
  66. DEFINE_FIELD( Size, FIELD_INTEGER ),
  67. DEFINE_FIELD( Magic, FIELD_INTEGER ),
  68. END_BYTESWAP_DATADESC()
  69. CXZip::CXZip()
  70. {
  71. // Ensure that the header doesn't contain a valid magic yet.
  72. m_Header.Magic = 0;
  73. m_pPreloadedData = NULL;
  74. m_nPreloadStart = 0;
  75. m_pDirectory = NULL;
  76. m_pPreloadDirectory = NULL;
  77. m_nRegular2PreloadEntryMapping = NULL;
  78. m_bByteSwapped = false;
  79. m_pFilenames = NULL;
  80. m_hZip = NULL;
  81. m_pRead = NULL;
  82. m_hUser = 0;
  83. m_nMonitorLevel = 0;
  84. }
  85. CXZip::CXZip( const char* filename )
  86. {
  87. // Ensure that the header doesn't contain a valid magic yet.
  88. m_Header.Magic = 0;
  89. m_nPreloadStart = 0;
  90. m_pPreloadedData = NULL;
  91. m_pDirectory = NULL;
  92. m_pPreloadDirectory = NULL;
  93. m_nRegular2PreloadEntryMapping = NULL;
  94. m_bByteSwapped = false;
  95. m_pFilenames = NULL;
  96. m_hZip = NULL;
  97. m_pRead = NULL;
  98. m_hUser = 0;
  99. m_nMonitorLevel = 0;
  100. Load( filename );
  101. }
  102. CXZip::CXZip( FILE* handle, int offset, int size ) // file handle and offset of the zip file
  103. {
  104. m_pRead = NULL;
  105. m_hUser = 0;
  106. m_nPreloadStart = 0;
  107. m_pDirectory = NULL;
  108. m_pPreloadDirectory = NULL;
  109. m_nRegular2PreloadEntryMapping = NULL;
  110. m_bByteSwapped = false;
  111. m_pFilenames = NULL;
  112. m_pPreloadedData = NULL;
  113. m_nMonitorLevel = 0;
  114. Load( handle, offset, size );
  115. }
  116. CXZip::~CXZip()
  117. {
  118. Unload();
  119. }
  120. bool CXZip::InstallAlternateIO( int (*read)( void* buffer, int offset, int length, int nDestLength, int hUser), int hUser )
  121. {
  122. m_pRead = read;
  123. m_hUser = hUser;
  124. return true;
  125. }
  126. // Loads an xZip file into memory:
  127. bool CXZip::Load( const char* filename, bool bPreload )
  128. {
  129. FILE* hZip = fopen( filename, "rb" );
  130. fseek(hZip,0,SEEK_END);
  131. int nSize = ftell( hZip );
  132. return Load( hZip, 0, nSize, bPreload );
  133. }
  134. bool CXZip::Load( FILE* handle, int nOffset, int nSize, bool bPreload ) // Load a pack file into this instance. Returns true on success.
  135. {
  136. Unload();
  137. m_bByteSwapped = false;
  138. m_hZip = handle;
  139. m_nOffset = nOffset;
  140. m_nSize = nSize;
  141. // Hacky, clean up:
  142. if( m_hZip && !m_pRead )
  143. {
  144. InstallAlternateIO( defaultRead, (int)m_hZip );
  145. }
  146. if( m_hZip == NULL && m_pRead == NULL )
  147. {
  148. return false;
  149. }
  150. // Read the header:
  151. m_pRead( &m_Header, 0, -1, sizeof(m_Header), m_hUser );
  152. // Validate the Magic number and at the same time determine if I am reading a regular or swappped xZip file:
  153. switch( m_Swap.SourceIsNativeEndian<int>( m_Header.Magic, xZipHeader_t::MAGIC ) )
  154. {
  155. // Does the magic match exactly?
  156. case 1:
  157. m_Swap.ActivateByteSwapping( false );
  158. m_bByteSwapped = false;
  159. break;
  160. // Does the magic match, but is swapped?
  161. case 0:
  162. m_bByteSwapped = true;
  163. m_Swap.ActivateByteSwapping( true ); // We must be reading the opposite endianness.
  164. m_Swap.SwapFieldsToTargetEndian<xZipHeader_t>( &m_Header );
  165. break;
  166. default:
  167. assert( 0 );
  168. // Fail gently in release:
  169. // The magic doesn't match in any respect:
  170. case -1:
  171. {
  172. printf("Invalid xZip file\n");
  173. if( m_hZip )
  174. {
  175. fclose( m_hZip );
  176. m_hZip = NULL;
  177. }
  178. return false;
  179. }
  180. }
  181. // Validate the archive version:
  182. if( m_Header.Version != xZipHeader_t::VERSION )
  183. {
  184. // Backward compatable support for version 1
  185. Msg("Incorrect xZip version found %u - expected %u\n", m_Header.Version, xZipHeader_t::VERSION );
  186. if( m_hZip )
  187. {
  188. fclose( m_hZip );
  189. m_hZip = NULL;
  190. }
  191. m_Header.Magic = xZipHeader_t::FREE;
  192. return false;
  193. }
  194. // Read the directory:
  195. {
  196. MEM_ALLOC_CREDIT();
  197. m_pDirectory = (xZipDirectoryEntry_t*)malloc( sizeof(xZipDirectoryEntry_t) * m_Header.DirectoryEntries );
  198. m_pRead( m_pDirectory, m_Header.HeaderLength, -1, sizeof( xZipDirectoryEntry_t ) * m_Header.DirectoryEntries, m_hUser );
  199. // Swap the directory entries if nessecary
  200. if( m_bByteSwapped )
  201. {
  202. for( unsigned nDirectoryEntry = 0; nDirectoryEntry < m_Header.DirectoryEntries; nDirectoryEntry++ )
  203. {
  204. m_Swap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>( &( m_pDirectory[nDirectoryEntry] ) );
  205. }
  206. }
  207. m_nPreloadStart = m_Header.HeaderLength + ( sizeof( xZipDirectoryEntry_t ) * m_Header.DirectoryEntries );
  208. }
  209. // Preload the preload chunk if desired:
  210. if( bPreload )
  211. {
  212. PreloadData();
  213. }
  214. return true;
  215. }
  216. void CXZip::Unload()
  217. {
  218. DiscardPreloadedData();
  219. // Dump the directory:
  220. if( m_pDirectory )
  221. {
  222. free( m_pDirectory );
  223. m_pDirectory = NULL;
  224. }
  225. if( m_pFilenames )
  226. {
  227. free( m_pFilenames );
  228. m_pFilenames = NULL;
  229. }
  230. // Invalidate the header:
  231. m_Header.Magic = 0;
  232. if( m_hZip )
  233. {
  234. fclose( m_hZip );
  235. m_hZip = NULL;
  236. }
  237. }
  238. //-----------------------------------------------------------------------------
  239. // CXZip::PreloadData
  240. //
  241. // Loads the preloaded data if it isn't already.
  242. //-----------------------------------------------------------------------------
  243. void CXZip::PreloadData()
  244. {
  245. Assert( IsValid() );
  246. // Ensure it isn't already preloaded
  247. if( m_pPreloadedData )
  248. return;
  249. // If I don't have a preloaded section, ignore the request.
  250. if( !m_Header.PreloadBytes || !m_Header.PreloadDirectoryEntries )
  251. return;
  252. // Allocate and read the data block in:
  253. #ifndef _X360
  254. MEM_ALLOC_CREDIT_( "xZip" );
  255. m_pPreloadedData = malloc( m_Header.PreloadBytes );
  256. // Just drop out if allocation fails;
  257. if ( !m_pPreloadedData )
  258. return;
  259. m_pRead( m_pPreloadedData, m_nPreloadStart, -1, m_Header.PreloadBytes, m_hUser );
  260. #else
  261. int nAlignedStart = AlignValue( ( m_nPreloadStart - XBOX_HDD_SECTORSIZE ) + 1, XBOX_HDD_SECTORSIZE );
  262. int nBytesToRead = AlignValue( ( m_nPreloadStart - nAlignedStart ) + m_Header.PreloadBytes, XBOX_HDD_SECTORSIZE );
  263. int nBytesBuffer = AlignValue( nBytesToRead, XBOX_HDD_SECTORSIZE );
  264. byte *pReadData = (byte *)malloc( nBytesBuffer );
  265. // Just drop out if allocation fails;
  266. if ( !pReadData )
  267. return;
  268. MEM_ALLOC_CREDIT_( "xZip" );
  269. m_pRead( pReadData, nAlignedStart, nBytesBuffer,nBytesToRead, m_hUser );
  270. m_pPreloadedData = pReadData + ( m_nPreloadStart - nAlignedStart );
  271. #endif
  272. // Set up the preload directory:
  273. m_pPreloadDirectory = (xZipDirectoryEntry_t*)m_pPreloadedData;
  274. // Swap the preload directory:
  275. if ( m_bByteSwapped )
  276. {
  277. for ( unsigned nDirectoryEntry = 0; nDirectoryEntry < m_Header.PreloadDirectoryEntries; nDirectoryEntry++ )
  278. {
  279. m_Swap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>( &( m_pPreloadDirectory[nDirectoryEntry] ) );
  280. }
  281. }
  282. // Set up the regular 2 preload mapping section:
  283. m_nRegular2PreloadEntryMapping = (unsigned short*)(((unsigned char*)m_pPreloadDirectory) + ( sizeof(xZipDirectoryEntry_t) * m_Header.PreloadDirectoryEntries ));
  284. // Swap the regular to preload mapping
  285. if ( m_bByteSwapped )
  286. {
  287. m_Swap.SwapBufferToTargetEndian<short>( (short *)m_nRegular2PreloadEntryMapping, (short *)m_nRegular2PreloadEntryMapping, m_Header.DirectoryEntries );
  288. }
  289. }
  290. //-----------------------------------------------------------------------------
  291. // CXZip::DiscardPreloadedData
  292. //
  293. // frees the preloaded data cache if it's present.
  294. //-----------------------------------------------------------------------------
  295. void CXZip::DiscardPreloadedData()
  296. {
  297. if ( m_pPreloadedData )
  298. {
  299. #ifndef _X360
  300. free( m_pPreloadedData );
  301. #else
  302. int nAlignedStart = AlignValue( ( m_nPreloadStart - XBOX_HDD_SECTORSIZE ) + 1, XBOX_HDD_SECTORSIZE );
  303. byte *pReadData = (byte *)m_pPreloadedData - ( m_nPreloadStart - nAlignedStart );
  304. free( pReadData );
  305. #endif
  306. m_pPreloadedData = NULL;
  307. m_pPreloadDirectory = NULL;
  308. m_nRegular2PreloadEntryMapping = NULL;
  309. }
  310. }
  311. int CXZip::defaultRead( void* buffer, int offset, int destLength, int length, int hUser)
  312. {
  313. fseek( (FILE*)hUser, offset, SEEK_SET );
  314. return fread( buffer, 1, length, (FILE*)hUser );
  315. }
  316. char* CXZip::GetEntryFileName( unsigned CRC, char* pDefault )
  317. {
  318. Assert( IsValid() );
  319. if( IsRetail() )
  320. {
  321. return pDefault;
  322. }
  323. else
  324. {
  325. // Make sure I have a filename section:
  326. if( m_Header.FilenameStringsOffset == 0 || m_Header.FilenameEntries == 0 || CRC == 0 )
  327. {
  328. return pDefault;
  329. }
  330. // If the filename chunk isn't here, load it up:
  331. if( !m_pFilenames )
  332. {
  333. MEM_ALLOC_CREDIT_("xZip");
  334. m_pFilenames = (xZipFilenameEntry_t*)malloc( m_Header.FilenameStringsLength );
  335. m_pRead( m_pFilenames, m_Header.FilenameStringsOffset, -1, m_Header.FilenameStringsLength, m_hUser );
  336. // TODO: Swap!
  337. for( unsigned int i=0; i< m_Header.FilenameEntries;i++ )
  338. {
  339. m_Swap.SwapFieldsToTargetEndian<xZipFilenameEntry_t>(&m_pFilenames[i]);
  340. }
  341. }
  342. // Find this entry in the preload directory
  343. xZipFilenameEntry_t entry;
  344. entry.FilenameCRC = CRC;
  345. xZipFilenameEntry_t* found = (xZipFilenameEntry_t*)bsearch( &entry, m_pFilenames, m_Header.FilenameEntries, sizeof(xZipFilenameEntry_t), xZipFilenameEntry_t::xZipFilenameEntryCompare );
  346. if( !found )
  347. return pDefault;
  348. return (((char*)m_pFilenames) + found->FilenameOffset) - m_Header.FilenameStringsOffset;
  349. }
  350. }
  351. // Sanity checks that the zip file is ready and readable:
  352. bool CXZip::IsValid()
  353. {
  354. if( m_Header.Magic != xZipHeader_t::MAGIC )
  355. return false;
  356. if( m_Header.Version > xZipHeader_t::VERSION )
  357. return false;
  358. if( !m_pDirectory )
  359. return false;
  360. return true;
  361. }
  362. void CXZip::WarningDir()
  363. {
  364. Assert( IsValid());
  365. for( unsigned i = 0; i< m_Header.DirectoryEntries; i++ )
  366. {
  367. Msg( GetEntryFileName( m_pDirectory[i].FilenameCRC ) );
  368. }
  369. }
  370. int CXZip::ReadIndex( int nEntryIndex, int nFileOffset, int nDestBytes, int nLength, void* pBuffer )
  371. {
  372. Assert( IsValid() );
  373. if( nLength <=0 || nEntryIndex < 0 )
  374. return 0;
  375. // HACK HACK HACK - convert the pack file index to a local file index (ie, assuming the full file index is being passed in)
  376. nFileOffset -= m_pDirectory[nEntryIndex].StoredOffset;
  377. // HACK HACK HACK
  378. // If I've got my preload section loaded, first check there:
  379. xZipDirectoryEntry_t* pPreloadEntry = GetPreloadEntry(nEntryIndex);
  380. if( pPreloadEntry )
  381. {
  382. Assert( pPreloadEntry->FilenameCRC == m_pDirectory[nEntryIndex].FilenameCRC );
  383. if( nFileOffset + nLength <= (int)pPreloadEntry->Length )
  384. {
  385. if( m_nMonitorLevel >= 2 )
  386. {
  387. char* filename = GetEntryFileName( m_pDirectory[nEntryIndex].FilenameCRC, "(!!! unknown !!!)" );
  388. Msg("PACK(preload) %s: length:%i offset:%i",filename,nLength, nFileOffset);
  389. }
  390. memcpy( pBuffer, (char*)m_pPreloadedData + pPreloadEntry->StoredOffset + nFileOffset - m_nPreloadStart, nLength );
  391. return nLength;
  392. }
  393. }
  394. // Offset int the zip to start the read:
  395. int ZipOffset = m_pDirectory[nEntryIndex].StoredOffset + nFileOffset;
  396. int nBytesRead = m_pRead( pBuffer, ZipOffset, nDestBytes, nLength, m_hUser);
  397. if( m_nMonitorLevel )
  398. {
  399. char* filename = GetEntryFileName( m_pDirectory[nEntryIndex].FilenameCRC, "(!!! unknown !!!)" );
  400. unsigned preload = 0;
  401. if( m_pPreloadedData && m_nRegular2PreloadEntryMapping[nEntryIndex] != 0xFFFF )
  402. {
  403. // Find this entry in the preload directory
  404. xZipDirectoryEntry_t* entry = &(m_pPreloadDirectory[m_nRegular2PreloadEntryMapping[nEntryIndex]]);
  405. Assert(entry->FilenameCRC == m_pDirectory[nEntryIndex].FilenameCRC);
  406. preload = entry->Length;
  407. }
  408. Msg("PACK %s: length:%i offset:%i (preload bytes:%i)",filename,nLength, nFileOffset, preload);
  409. }
  410. return nBytesRead;
  411. }
  412. bool CXZip::GetSimpleFileOffsetLength( const char* FileName, int& nBaseIndex, int &nFileOffset, int &nLength )
  413. {
  414. Assert( IsValid() );
  415. xZipDirectoryEntry_t entry;
  416. entry.FilenameCRC = xZipCRCFilename( FileName );
  417. xZipDirectoryEntry_t* found = (xZipDirectoryEntry_t*)bsearch( &entry, m_pDirectory, m_Header.DirectoryEntries, sizeof(xZipDirectoryEntry_t), xZipDirectoryEntry_t::xZipDirectoryEntryFindCompare );
  418. if( found == NULL )
  419. return false;
  420. nFileOffset = found[0].StoredOffset;
  421. nLength = found[0].Length;
  422. nBaseIndex = (((int)((char*)found - (char*)m_pDirectory))/sizeof(xZipDirectoryEntry_t));
  423. return true;
  424. }
  425. bool CXZip::ExtractFile( const char* FileName )
  426. {
  427. return false;
  428. }
  429. // Compares to xZipDirectoryEntries.
  430. //
  431. // Sorts in the following order:
  432. // FilenameCRC
  433. // FileOffset
  434. // Length
  435. // StoredOffset
  436. //
  437. // The sort function may look overly complex, but it is actually useful for locating different pieces of
  438. // the same file in a meaningful order.
  439. //
  440. int __cdecl xZipDirectoryEntry_t::xZipDirectoryEntrySortCompare( const void* left, const void* right )
  441. {
  442. xZipDirectoryEntry_t *l = (xZipDirectoryEntry_t*)left,
  443. *r = (xZipDirectoryEntry_t*)right;
  444. if( l->FilenameCRC < r->FilenameCRC )
  445. {
  446. return -1;
  447. }
  448. else if( l->FilenameCRC > r->FilenameCRC )
  449. {
  450. return 1;
  451. }
  452. // else l->FileOffset == r->FileOffset
  453. if( l->Length < r->Length )
  454. {
  455. return -1;
  456. }
  457. else if( l->Length > r->Length )
  458. {
  459. return 1;
  460. }
  461. // else l->Length == r->Length
  462. if( l->StoredOffset < r->StoredOffset )
  463. {
  464. return -1;
  465. }
  466. else if( l->StoredOffset > r->StoredOffset )
  467. {
  468. return 1;
  469. }
  470. // else everything is identical:
  471. return 0;
  472. }
  473. // Find an entry with matching CRC only
  474. int __cdecl xZipDirectoryEntry_t::xZipDirectoryEntryFindCompare( const void* left, const void* right )
  475. {
  476. xZipDirectoryEntry_t *l = (xZipDirectoryEntry_t*)left,
  477. *r = (xZipDirectoryEntry_t*)right;
  478. if( l->FilenameCRC < r->FilenameCRC )
  479. {
  480. return -1;
  481. }
  482. else if( l->FilenameCRC > r->FilenameCRC )
  483. {
  484. return 1;
  485. }
  486. return 0;
  487. }
  488. int __cdecl xZipFilenameEntry_t::xZipFilenameEntryCompare( const void* left, const void* right )
  489. {
  490. xZipFilenameEntry_t *l = (xZipFilenameEntry_t*)left,
  491. *r = (xZipFilenameEntry_t*)right;
  492. if( l->FilenameCRC < r->FilenameCRC )
  493. {
  494. return -1;
  495. }
  496. else if( l->FilenameCRC > r->FilenameCRC )
  497. {
  498. return 1;
  499. }
  500. return 0;
  501. }
  502. // CRC's an individual xZip filename:
  503. unsigned xZipCRCFilename( const char* filename )
  504. {
  505. unsigned hash = 0xAAAAAAAA; // Alternating 1's and 0's
  506. for( ; *filename ; filename++ )
  507. {
  508. char c = *filename;
  509. // Fix slashes
  510. if( c == '/' )
  511. c = '\\';
  512. else
  513. c = (char)tolower(c);
  514. hash = hash * 33 + c;
  515. }
  516. return hash;
  517. }
  518. #if defined( MAKE_GAMEDATA_TOOL )
  519. // ------------
  520. xZipHeader_t Header;
  521. xZipDirectoryEntry_t *pDirectoryEntries = NULL;
  522. xZipDirectoryEntry_t *pPreloadDirectoryEntries = NULL;
  523. xZipFilenameEntry_t *pFilenameEntries = NULL;
  524. char *pFilenameData = NULL;
  525. unsigned nFilenameDataLength = 0;
  526. unsigned InputFileBytes = 0;
  527. char* CleanFilename( char* filename )
  528. {
  529. // Trim leading white space:
  530. while( isspace(*filename) )
  531. filename++;
  532. // Trim trailing white space:
  533. while( isspace( filename[strlen(filename)-1] ) )
  534. {
  535. filename[strlen(filename)-1] = '\0';
  536. }
  537. return filename;
  538. }
  539. bool CopyFileBytes( FILE* hDestination, FILE* hSource, unsigned nBytes )
  540. {
  541. char buffer[16384];
  542. while( nBytes > 0 )
  543. {
  544. int nBytesRead = fread( buffer, 1, nBytes > sizeof(buffer) ? sizeof(buffer) : nBytes, hSource );
  545. fwrite(buffer, 1, nBytesRead, hDestination );
  546. nBytes -= nBytesRead;
  547. }
  548. return true;
  549. }
  550. bool WriteFileBytes( FILE* hDestination, CUtlBuffer &source, unsigned nBytes )
  551. {
  552. unsigned int nBytesWritten = fwrite(source.Base(), 1, nBytes, hDestination );
  553. return (nBytesWritten == nBytes);
  554. }
  555. void PadFileBytes(FILE* hFile, int nPreloadPadding )
  556. {
  557. if( nPreloadPadding < 0 || nPreloadPadding >= 512)
  558. {
  559. puts("Invalid padding");
  560. return;
  561. }
  562. char padding[512];
  563. memset(padding,0,nPreloadPadding);
  564. fwrite(padding,1,nPreloadPadding,hFile);
  565. }
  566. void AddFilename( const char* filename )
  567. {
  568. unsigned CRCfilename = xZipCRCFilename( filename );
  569. // If we already have this filename don't add it again:
  570. for( int i = 0; i < (int)Header.FilenameEntries; i++ )
  571. {
  572. if( pFilenameEntries[i].FilenameCRC == CRCfilename )
  573. {
  574. return;
  575. }
  576. }
  577. Header.FilenameEntries++;
  578. // Add the file to the file string table:
  579. pFilenameEntries = (xZipFilenameEntry_t*)realloc( pFilenameEntries, sizeof(xZipFilenameEntry_t) * Header.FilenameEntries );
  580. int filenameLength = (int)strlen(filename) + 1;
  581. pFilenameEntries[Header.FilenameEntries-1].FilenameCRC = CRCfilename;
  582. pFilenameEntries[Header.FilenameEntries-1].FilenameOffset = nFilenameDataLength;
  583. // Grab the timestamp for the file:
  584. struct stat buf;
  585. if( stat( filename, &buf ) != -1 )
  586. {
  587. pFilenameEntries[Header.FilenameEntries - 1].TimeStamp = buf.st_mtime;
  588. }
  589. else
  590. {
  591. pFilenameEntries[Header.FilenameEntries - 1].TimeStamp = 0;
  592. }
  593. nFilenameDataLength += filenameLength;
  594. pFilenameData = (char*)realloc(pFilenameData, nFilenameDataLength);
  595. memcpy(pFilenameData + nFilenameDataLength - filenameLength, filename, filenameLength);
  596. }
  597. FILE* hTempFilePreload;
  598. FILE* hTempFileData;
  599. FILE* hOutputFile;
  600. bool xZipAddFile( const char* filename, CUtlBuffer &fileBuff, bool bPrecacheEntireFile, bool bProcessPrecacheHeader, bool bProcessPrecacheHeaderOnly )
  601. {
  602. unsigned int fileSize = fileBuff.TellMaxPut();
  603. // Track total input bytes for stats reasons
  604. InputFileBytes += fileSize;
  605. unsigned customPreloadSize = 0;
  606. if( bPrecacheEntireFile )
  607. {
  608. customPreloadSize = fileSize;
  609. }
  610. else if( bProcessPrecacheHeader )
  611. {
  612. customPreloadSize = xZipComputeCustomPreloads( filename );
  613. }
  614. else if( bProcessPrecacheHeaderOnly )
  615. {
  616. customPreloadSize = xZipComputeCustomPreloads( filename );
  617. fileSize = min( fileSize, customPreloadSize );
  618. }
  619. unsigned CRC = xZipCRCFilename( filename );
  620. // Does this file have a split header?
  621. if( customPreloadSize > 0 )
  622. {
  623. // Initialize the entry header:
  624. xZipDirectoryEntry_t entry;
  625. memset( &entry, 0, sizeof( entry ) );
  626. entry.FilenameCRC = CRC;
  627. entry.Length = customPreloadSize;
  628. entry.StoredOffset = ftell(hTempFilePreload);
  629. // Add the directory entry to the preload table:
  630. Header.PreloadDirectoryEntries++;
  631. pPreloadDirectoryEntries = (xZipDirectoryEntry_t*)realloc( pPreloadDirectoryEntries, sizeof( xZipDirectoryEntry_t ) * Header.PreloadDirectoryEntries );
  632. memcpy( pPreloadDirectoryEntries + Header.PreloadDirectoryEntries - 1, &entry, sizeof( entry ) );
  633. // Concatenate the data in the preload file:
  634. fileBuff.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  635. WriteFileBytes( hTempFilePreload, fileBuff, entry.Length );
  636. fileBuff.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  637. // Add the filename entry:
  638. AddFilename( filename );
  639. // Spew it:
  640. printf("+Preload: \"%s\": Length:%u\n", filename, entry.Length );
  641. }
  642. // Copy the file to the regular data region:
  643. xZipDirectoryEntry_t entry;
  644. memset(&entry,0,sizeof(entry));
  645. entry.FilenameCRC = CRC;
  646. entry.Length = fileSize;
  647. entry.StoredOffset = ftell(hTempFileData);
  648. // Add the directory entry to the table:
  649. Header.DirectoryEntries++;
  650. pDirectoryEntries = (xZipDirectoryEntry_t*)realloc( pDirectoryEntries, sizeof( xZipDirectoryEntry_t ) * Header.DirectoryEntries );
  651. memcpy( pDirectoryEntries + Header.DirectoryEntries - 1, &entry, sizeof( entry ) );
  652. WriteFileBytes( hTempFileData, fileBuff, entry.Length );
  653. // Align the data region to a 512 byte boundry: (has to be on last entry as well to ensure enough space to perform the final read,
  654. // and initial alignment is taken careof by assembexzip)
  655. int nPadding = ( XBOX_HDD_SECTORSIZE - ( ftell( hTempFileData ) % XBOX_HDD_SECTORSIZE) ) % XBOX_HDD_SECTORSIZE;
  656. PadFileBytes( hTempFileData, nPadding );
  657. // Add the file to the file string table:
  658. AddFilename( filename );
  659. // Print a summary
  660. printf("+File: \"%s\": Length:%u Padding:%i\n", filename, entry.Length, nPadding );
  661. return true;
  662. }
  663. bool xZipAddFile( const char* zipname, bool bPrecacheEntireFile, bool bProcessPrecacheHeader, bool bProcessPrecacheHeaderOnly )
  664. {
  665. // Clean up the filename:
  666. char buffer[MAX_PATH];
  667. strcpy(buffer, zipname);
  668. // Fix slashes and convert it to lower case:
  669. char *filename;
  670. for( filename = buffer; *filename; filename++ )
  671. {
  672. if( *filename == '/' )
  673. *filename = '\\';
  674. else
  675. {
  676. *filename = (char)tolower(*filename);
  677. }
  678. }
  679. // Skip leading white space:
  680. for( filename = buffer; isspace(*filename); filename++ )
  681. ;
  682. // Obliterate trailing white space:
  683. for(;;)
  684. {
  685. int len = (int)strlen( filename );
  686. if( len <= 0 )
  687. {
  688. printf("!!!! BAD FILENAME: \"%s\"\n", filename );
  689. return false;
  690. }
  691. if( isspace( filename[len-1] ) )
  692. filename[len-1]='\0';
  693. else
  694. break;
  695. }
  696. // Ensure we don't already have this file:
  697. unsigned CRC = xZipCRCFilename( filename );
  698. for( unsigned i=0; i < Header.DirectoryEntries; i++ )
  699. {
  700. if( pDirectoryEntries[i].FilenameCRC == CRC )
  701. {
  702. printf("!!!! NOT ADDING DUPLICATE FILENAME: \"%s\"\n", filename );
  703. return false;
  704. }
  705. }
  706. // Attempt to open the file:
  707. FILE* hFile = fopen( filename, "rb" );
  708. if( !hFile )
  709. {
  710. printf("!!!! FAILED TO OPEN FILE: \"%s\"\n", filename );
  711. return false;
  712. }
  713. // Get the length of the file:
  714. fseek(hFile,0,SEEK_END);
  715. unsigned fileSize = ftell(hFile);
  716. fseek(hFile,0,SEEK_SET);
  717. CUtlBuffer fileBuff;
  718. fileBuff.EnsureCapacity( fileSize );
  719. fread( fileBuff.Base(), fileSize, 1, hFile );
  720. fclose( hFile );
  721. fileBuff.SeekPut( CUtlBuffer::SEEK_HEAD, fileSize );
  722. return xZipAddFile( zipname, fileBuff, bPrecacheEntireFile, bProcessPrecacheHeader, bProcessPrecacheHeaderOnly );
  723. }
  724. int xZipBegin( const char* fileNameXzip )
  725. {
  726. // Create and initialize the header:
  727. memset( &Header, 0, sizeof(Header) ); // Zero out the header:
  728. Header.Magic = xZipHeader_t::MAGIC;
  729. Header.Version = xZipHeader_t::VERSION;
  730. Header.HeaderLength = sizeof(Header);
  731. // Open the output file:
  732. hOutputFile = fopen(fileNameXzip,"wb+");
  733. if( !hOutputFile )
  734. {
  735. printf("Failed to open \"%s\" for writing.\n", fileNameXzip);
  736. exit( EXIT_FAILURE);
  737. }
  738. // Create a temporary file for storing the preloaded data:
  739. hTempFilePreload = tmpfile();
  740. if( !hTempFilePreload )
  741. {
  742. printf( "Error: failed to create temporary file\n" );
  743. return EXIT_FAILURE;
  744. }
  745. // Create a temporary file for storing the non preloaded data
  746. hTempFileData = tmpfile();
  747. if( !hTempFileData )
  748. {
  749. printf( "Error: failed to create temporary file\n");
  750. return EXIT_FAILURE;
  751. }
  752. return EXIT_SUCCESS;
  753. }
  754. bool xZipEnd()
  755. {
  756. int nPreloadDirectorySize = sizeof(xZipDirectoryEntry_t)*Header.PreloadDirectoryEntries;
  757. int nRegular2PreloadSize = sizeof(unsigned short) * Header.DirectoryEntries;
  758. // Compute the size of the preloaded section:
  759. if( Header.PreloadDirectoryEntries )
  760. {
  761. fseek( hTempFilePreload, 0, SEEK_END );
  762. Header.PreloadBytes = ftell(hTempFilePreload) + nPreloadDirectorySize + nRegular2PreloadSize; // Raw# of bytes to preload
  763. fseek( hTempFilePreload, 0, SEEK_SET );
  764. }
  765. else
  766. {
  767. Header.PreloadBytes = 0;
  768. }
  769. // Number of bytes preceeding the preloaded section:
  770. int nPreloadOffset = sizeof( Header ) + ( sizeof( xZipDirectoryEntry_t ) * Header.DirectoryEntries );
  771. // Number of bytes to pad between the end of the preload section and the start of the data section:
  772. int nPadding = ( 512 - ( ( nPreloadOffset + Header.PreloadBytes ) % 512) ) %512; // Number of alignment bytes after the preload section
  773. // Offset past the preload section:
  774. int nDataOffset = nPreloadOffset + Header.PreloadBytes + nPadding;
  775. // Write out the header: (will need to be rewritten at the end as well) - note: not even bothering to byteswap at this point
  776. fwrite(&Header,sizeof(Header),1,hOutputFile);
  777. // Fixup each of the directory entries to make them relative to the beginning of the file.
  778. for( unsigned i=0; i< Header.DirectoryEntries;i++ )
  779. {
  780. xZipDirectoryEntry_t* pDir = &(pDirectoryEntries[i]);
  781. // Adjust files in the regular data area:
  782. pDir->StoredOffset = nDataOffset + pDir->StoredOffset;
  783. }
  784. // Sort and write the directory:
  785. printf("Sorting and writing %i directory entries...\n",Header.DirectoryEntries);
  786. qsort(pDirectoryEntries,Header.DirectoryEntries,sizeof(xZipDirectoryEntry_t),&xZipDirectoryEntry_t::xZipDirectoryEntrySortCompare);
  787. // Swap the directory entries:
  788. for( unsigned i=0; i < Header.DirectoryEntries; i++ )
  789. {
  790. g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pDirectoryEntries[i]);
  791. }
  792. fwrite(pDirectoryEntries,Header.DirectoryEntries*sizeof(xZipDirectoryEntry_t),1, hOutputFile);
  793. // Swap the directory back for later use:
  794. for( unsigned i=0; i < Header.DirectoryEntries; i++ )
  795. {
  796. g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pDirectoryEntries[i]);
  797. }
  798. // Copy the preload section:
  799. if( Header.PreloadBytes > 0 )
  800. {
  801. printf("Generating the preload section...(%u)\n", Header.PreloadBytes);
  802. // Fixup each of the directory entries to make them relative to the beginning of the file.
  803. for( unsigned i=0; i< Header.PreloadDirectoryEntries;i++ )
  804. {
  805. xZipDirectoryEntry_t* pDir = &(pPreloadDirectoryEntries[i]);
  806. // Shift preload data down by preload bytes (and skipping over the directory):
  807. pDir->StoredOffset += nPreloadOffset + nPreloadDirectorySize + nRegular2PreloadSize;
  808. }
  809. printf("Sorting %u preload directory entries...\n",Header.PreloadDirectoryEntries);
  810. qsort(pPreloadDirectoryEntries,Header.PreloadDirectoryEntries,sizeof(xZipDirectoryEntry_t),&xZipDirectoryEntry_t::xZipDirectoryEntrySortCompare);
  811. printf("Building regular to preload mapping table for %u entries...\n", Header.DirectoryEntries );
  812. unsigned short* Regular2Preload = (unsigned short*)malloc( nRegular2PreloadSize );
  813. for( unsigned i = 0; i < Header.DirectoryEntries; i++ )
  814. {
  815. unsigned short j;
  816. for( j = 0; j < Header.PreloadDirectoryEntries; j++ )
  817. {
  818. if( pDirectoryEntries[i].FilenameCRC == pPreloadDirectoryEntries[j].FilenameCRC )
  819. break;
  820. }
  821. // If I couldn't find it mark it as non-existant:
  822. if( j == Header.PreloadDirectoryEntries )
  823. j = 0xFFFF;
  824. Regular2Preload[i] = j;
  825. }
  826. printf("Writing preloaded directory entreis...\n" );
  827. // Swap the preload directory entries:
  828. for( unsigned i=0; i < Header.PreloadDirectoryEntries; i++ )
  829. {
  830. g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pPreloadDirectoryEntries[i]);
  831. }
  832. fwrite( pPreloadDirectoryEntries, Header.PreloadDirectoryEntries*sizeof(xZipDirectoryEntry_t),1, hOutputFile );
  833. // Swap them back:
  834. for( unsigned i=0; i < Header.PreloadDirectoryEntries; i++ )
  835. {
  836. g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pPreloadDirectoryEntries[i]);
  837. }
  838. printf("Writing regular to preload mapping (%u bytes)...\n", sizeof(unsigned short)*Header.DirectoryEntries );
  839. // Swap regular to preload mapping:
  840. g_xzpSwap.SwapBufferToTargetEndian<short>((short*)Regular2Preload, (short*)Regular2Preload, nRegular2PreloadSize / sizeof(short) );
  841. fwrite( Regular2Preload, nRegular2PreloadSize,1,hOutputFile );
  842. // Swap it back
  843. g_xzpSwap.SwapBufferToTargetEndian<short>((short*)Regular2Preload, (short*)Regular2Preload, nRegular2PreloadSize / sizeof(short) );
  844. printf("Copying %u Preloadable Bytes...\n", Header.PreloadBytes - nPreloadDirectorySize - nRegular2PreloadSize );
  845. fseek(hTempFilePreload,0,SEEK_SET);
  846. CopyFileBytes(hOutputFile, hTempFilePreload, Header.PreloadBytes - nPreloadDirectorySize - nRegular2PreloadSize );
  847. }
  848. // Align the data section following the preload section:
  849. if( nPadding )
  850. {
  851. printf("Aligning Data Section Start by %u bytes...\n", nPadding );
  852. PadFileBytes(hOutputFile, nPadding );
  853. }
  854. // Copy the data section:
  855. fseek(hTempFileData, 0, SEEK_END );
  856. unsigned length = ftell( hTempFileData );
  857. fseek(hTempFileData, 0, SEEK_SET );
  858. printf("Copying %u Bytes...\n",length);
  859. CopyFileBytes(hOutputFile, hTempFileData, length);
  860. // Write out the filename data if present:
  861. if( nFilenameDataLength && Header.FilenameEntries )
  862. {
  863. Header.FilenameStringsOffset = ftell(hOutputFile);
  864. Header.FilenameStringsLength = (Header.FilenameEntries*sizeof(xZipFilenameEntry_t)) + nFilenameDataLength;
  865. // Adjust the offset in each of the filename offsets to absolute position in the file.
  866. for( unsigned i=0;i<Header.FilenameEntries;i++ )
  867. {
  868. pFilenameEntries[i].FilenameOffset += ( Header.FilenameStringsOffset + (Header.DirectoryEntries*sizeof(xZipFilenameEntry_t)));
  869. }
  870. printf("Sorting and writing %u filename directory entries...\n",Header.FilenameEntries);
  871. // Sort the data:
  872. qsort(pFilenameEntries,Header.FilenameEntries,sizeof(xZipFilenameEntry_t),&xZipFilenameEntry_t::xZipFilenameEntryCompare);
  873. // Write the data out:
  874. for( unsigned int i = 0; i < Header.FilenameEntries; i++ )
  875. {
  876. g_xzpSwap.SwapFieldsToTargetEndian<xZipFilenameEntry_t>(&pFilenameEntries[i]);
  877. }
  878. fwrite(pFilenameEntries,1,Header.FilenameEntries*sizeof(xZipFilenameEntry_t),hOutputFile);
  879. // Swap them back:
  880. for( unsigned int i = 0; i < Header.FilenameEntries; i++ )
  881. {
  882. g_xzpSwap.SwapFieldsToTargetEndian<xZipFilenameEntry_t>(&pFilenameEntries[i]);
  883. }
  884. printf("Writing %u bytes of filename data...\n",nFilenameDataLength);
  885. fwrite(pFilenameData,1,nFilenameDataLength,hOutputFile);
  886. }
  887. // Compute the total file size, including the size of the footer:
  888. unsigned OutputFileBytes = ftell(hOutputFile) + sizeof(xZipFooter_t);
  889. // Write the footer: (block used to keep possibly swapped footer from being used later)
  890. {
  891. xZipFooter_t footer;
  892. footer.Magic = xZipFooter_t::MAGIC;
  893. footer.Size = OutputFileBytes;
  894. g_xzpSwap.SwapFieldsToTargetEndian<xZipFooter_t>( &footer ); // Swap the footer
  895. fwrite( &footer, 1, sizeof(footer), hOutputFile );
  896. }
  897. // Seek back and rewrite the header (filename data changes it for example)
  898. fseek(hOutputFile,0,SEEK_SET);
  899. g_xzpSwap.SwapFieldsToTargetEndian<xZipHeader_t>( &Header ); // Swap it to write out:
  900. fwrite(&Header,1,sizeof(Header),hOutputFile);
  901. g_xzpSwap.SwapFieldsToTargetEndian<xZipHeader_t>( &Header ); // But then swap it back so we can use it in memory
  902. // Shut down
  903. fclose(hOutputFile);
  904. // Print the summary
  905. printf("\n\nSummary: Input:%u, XZip:%u, Directory Entries:%u (%u preloaded), Preloaded Bytes:%u\n\n",InputFileBytes,OutputFileBytes,Header.DirectoryEntries, Header.PreloadDirectoryEntries, Header.PreloadBytes);
  906. // Shut down:
  907. fclose(hTempFileData);
  908. fclose(hTempFilePreload);
  909. return true;
  910. }
  911. #define PADD_ID MAKEID('P','A','D','D')
  912. //-----------------------------------------------------------------------------
  913. // xZipComputeWAVPreload
  914. //
  915. // Returns the number of bytes from a xbox compliant WAV file that should go into
  916. // the preload section:
  917. //-----------------------------------------------------------------------------
  918. unsigned xZipComputeWAVPreload( char *pFileName )
  919. {
  920. InFileRIFF riff( pFileName, *g_pSndIO );
  921. if ( riff.RIFFName() != RIFF_WAVE )
  922. {
  923. return 0;
  924. }
  925. IterateRIFF walk( riff, riff.RIFFSize() );
  926. while ( walk.ChunkAvailable() )
  927. {
  928. // xbox compliant wavs have a single PADD chunk
  929. if ( walk.ChunkName() == PADD_ID )
  930. {
  931. // want to preload data up through PADD chunk header
  932. // and not the actual pad bytes
  933. return walk.ChunkFilePosition() + 2*sizeof( int );
  934. }
  935. walk.ChunkNext();
  936. }
  937. return 0;
  938. }
  939. //-----------------------------------------------------------------------------
  940. // xZipComputeXWVPreload
  941. //
  942. // Returns the number of bytes from a XWV file that should go into the preload
  943. // section:
  944. //-----------------------------------------------------------------------------
  945. unsigned xZipComputeXWVPreload( const char* filename )
  946. {
  947. FILE* hFile = fopen( filename, "rb" );
  948. if ( !hFile )
  949. {
  950. printf( "Failed to open xwv file: %s\n", filename );
  951. return 0;
  952. }
  953. // Read and validate the XWV header:
  954. xwvHeader_t header;
  955. memset( &header, 0, sizeof(header) );
  956. fread( &header, 1, sizeof(header), hFile );
  957. fclose( hFile );
  958. if ( header.id != XWV_ID || header.headerSize != sizeof(header) )
  959. return 0;
  960. return header.GetPreloadSize();
  961. }
  962. unsigned xZipComputeXTFPreload( const char* filename )
  963. {
  964. #if 0 // X360TBD: Not using XTF anymore
  965. FILE* hFile = fopen( filename, "rb" );
  966. if ( !hFile )
  967. {
  968. printf("Failed to open file: %s\n", filename);
  969. return 0;
  970. }
  971. XTFFileHeader_t header;
  972. memset( &header,0, sizeof( header ) );
  973. fread( &header,1,sizeof(header),hFile);
  974. fclose(hFile);
  975. if ( !strncmp( header.fileTypeString, "XTF", 4 ) )
  976. return header.preloadDataSize;
  977. #endif
  978. return 0;
  979. }
  980. // TODO: ONLY store them in the preload section:
  981. unsigned xZipComputeVMTPreload( const char* filename )
  982. {
  983. // Store VMT's entirely
  984. if ( !strstr(filename,".vmt") )
  985. return 0;
  986. FILE* hFile = fopen( filename, "rb" );
  987. if ( !hFile )
  988. {
  989. printf("Failed to open file: %s\n", filename);
  990. return 0;
  991. }
  992. fseek( hFile, 0, SEEK_END );
  993. unsigned offset = ftell( hFile );
  994. fclose( hFile );
  995. return offset;
  996. }
  997. // TODO: ONLY store them in the preload section:
  998. unsigned xZipComputeVHVPreload( const char* filename )
  999. {
  1000. // Store VMT's entirely
  1001. if ( !strstr(filename,".vhv") )
  1002. return 0;
  1003. FILE* hFile = fopen( filename, "rb" );
  1004. if ( !hFile )
  1005. {
  1006. printf("Failed to open file: %s\n", filename);
  1007. return 0;
  1008. }
  1009. fclose( hFile );
  1010. // Just load the header:
  1011. return sizeof(HardwareVerts::FileHeader_t);
  1012. }
  1013. unsigned xZipComputeXCSPreload( const char* filename )
  1014. {
  1015. if( !strstr(filename,".vcs") )
  1016. return 0;
  1017. FILE* hFile = fopen( filename, "rb" );
  1018. if ( !hFile )
  1019. {
  1020. printf("Failed to open file: %s\n", filename);
  1021. return 0;
  1022. }
  1023. XShaderHeader_t header;
  1024. fread(&header,1,sizeof(XShaderHeader_t), hFile);
  1025. fseek(hFile,0,SEEK_END);
  1026. fclose(hFile);
  1027. if (!header.IsValid())
  1028. return 0;
  1029. return header.BytesToPreload();
  1030. }
  1031. unsigned xZipComputeCustomPreloads( const char* filename )
  1032. {
  1033. // X360TBD: These all need to act on a utlbuffer
  1034. Assert( 0 );
  1035. return 0;
  1036. // strlwr(filename);
  1037. unsigned offset = xZipComputeXWVPreload( filename );
  1038. if ( offset )
  1039. return offset;
  1040. offset = xZipComputeVMTPreload( filename );
  1041. if ( offset )
  1042. return offset;
  1043. offset = xZipComputeXCSPreload( filename );
  1044. if ( offset )
  1045. return offset;
  1046. offset = xZipComputeVHVPreload( filename );
  1047. if ( offset )
  1048. return offset;
  1049. return xZipComputeXTFPreload( filename );
  1050. }
  1051. #endif // MAKE_GAMEDATA_TOOL