Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1754 lines
47 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include <tier0/platform.h>
  7. #ifdef IS_WINDOWS_PC
  8. #include <windows.h>
  9. #elif defined( _X360 )
  10. // already defined nothing
  11. #else
  12. #define INVALID_HANDLE_VALUE (void *)0
  13. #define FILE_BEGIN SEEK_SET
  14. #define FILE_END SEEK_END
  15. #endif
  16. #include "utlbuffer.h"
  17. #include "utllinkedlist.h"
  18. #include "zip_utils.h"
  19. #include "zip_uncompressed.h"
  20. #include "checksum_crc.h"
  21. #include "byteswap.h"
  22. #include "utlstring.h"
  23. // NOTE: This has to be the last file included!
  24. #include "tier0/memdbgon.h"
  25. // Data descriptions for byte swapping - only needed
  26. // for structures that are written to file for use by the game.
  27. BEGIN_BYTESWAP_DATADESC( ZIP_EndOfCentralDirRecord )
  28. DEFINE_FIELD( signature, FIELD_INTEGER ),
  29. DEFINE_FIELD( numberOfThisDisk, FIELD_SHORT ),
  30. DEFINE_FIELD( numberOfTheDiskWithStartOfCentralDirectory, FIELD_SHORT ),
  31. DEFINE_FIELD( nCentralDirectoryEntries_ThisDisk, FIELD_SHORT ),
  32. DEFINE_FIELD( nCentralDirectoryEntries_Total, FIELD_SHORT ),
  33. DEFINE_FIELD( centralDirectorySize, FIELD_INTEGER ),
  34. DEFINE_FIELD( startOfCentralDirOffset, FIELD_INTEGER ),
  35. DEFINE_FIELD( commentLength, FIELD_SHORT ),
  36. END_BYTESWAP_DATADESC()
  37. BEGIN_BYTESWAP_DATADESC( ZIP_FileHeader )
  38. DEFINE_FIELD( signature, FIELD_INTEGER ),
  39. DEFINE_FIELD( versionMadeBy, FIELD_SHORT ),
  40. DEFINE_FIELD( versionNeededToExtract, FIELD_SHORT ),
  41. DEFINE_FIELD( flags, FIELD_SHORT ),
  42. DEFINE_FIELD( compressionMethod, FIELD_SHORT ),
  43. DEFINE_FIELD( lastModifiedTime, FIELD_SHORT ),
  44. DEFINE_FIELD( lastModifiedDate, FIELD_SHORT ),
  45. DEFINE_FIELD( crc32, FIELD_INTEGER ),
  46. DEFINE_FIELD( compressedSize, FIELD_INTEGER ),
  47. DEFINE_FIELD( uncompressedSize, FIELD_INTEGER ),
  48. DEFINE_FIELD( fileNameLength, FIELD_SHORT ),
  49. DEFINE_FIELD( extraFieldLength, FIELD_SHORT ),
  50. DEFINE_FIELD( fileCommentLength, FIELD_SHORT ),
  51. DEFINE_FIELD( diskNumberStart, FIELD_SHORT ),
  52. DEFINE_FIELD( internalFileAttribs, FIELD_SHORT ),
  53. DEFINE_FIELD( externalFileAttribs, FIELD_INTEGER ),
  54. DEFINE_FIELD( relativeOffsetOfLocalHeader, FIELD_INTEGER ),
  55. END_BYTESWAP_DATADESC()
  56. BEGIN_BYTESWAP_DATADESC( ZIP_LocalFileHeader )
  57. DEFINE_FIELD( signature, FIELD_INTEGER ),
  58. DEFINE_FIELD( versionNeededToExtract, FIELD_SHORT ),
  59. DEFINE_FIELD( flags, FIELD_SHORT ),
  60. DEFINE_FIELD( compressionMethod, FIELD_SHORT ),
  61. DEFINE_FIELD( lastModifiedTime, FIELD_SHORT ),
  62. DEFINE_FIELD( lastModifiedDate, FIELD_SHORT ),
  63. DEFINE_FIELD( crc32, FIELD_INTEGER ),
  64. DEFINE_FIELD( compressedSize, FIELD_INTEGER ),
  65. DEFINE_FIELD( uncompressedSize, FIELD_INTEGER ),
  66. DEFINE_FIELD( fileNameLength, FIELD_SHORT ),
  67. DEFINE_FIELD( extraFieldLength, FIELD_SHORT ),
  68. END_BYTESWAP_DATADESC()
  69. BEGIN_BYTESWAP_DATADESC( ZIP_PreloadHeader )
  70. DEFINE_FIELD( Version, FIELD_INTEGER ),
  71. DEFINE_FIELD( DirectoryEntries, FIELD_INTEGER ),
  72. DEFINE_FIELD( PreloadDirectoryEntries, FIELD_INTEGER ),
  73. DEFINE_FIELD( Alignment, FIELD_INTEGER ),
  74. END_BYTESWAP_DATADESC()
  75. BEGIN_BYTESWAP_DATADESC( ZIP_PreloadDirectoryEntry )
  76. DEFINE_FIELD( Length, FIELD_INTEGER ),
  77. DEFINE_FIELD( DataOffset, FIELD_INTEGER ),
  78. END_BYTESWAP_DATADESC()
  79. #ifdef WIN32
  80. //-----------------------------------------------------------------------------
  81. // For >2 GB File Support
  82. //-----------------------------------------------------------------------------
  83. class CWin32File
  84. {
  85. public:
  86. static HANDLE CreateTempFile( CUtlString &WritePath, CUtlString &FileName )
  87. {
  88. char tempFileName[MAX_PATH];
  89. if ( WritePath.IsEmpty() )
  90. {
  91. // use a safe name in the cwd
  92. char *pBuffer = tmpnam( NULL );
  93. if ( !pBuffer )
  94. {
  95. return INVALID_HANDLE_VALUE;
  96. }
  97. if ( pBuffer[0] == '\\' )
  98. {
  99. pBuffer++;
  100. }
  101. if ( pBuffer[strlen( pBuffer )-1] == '.' )
  102. {
  103. pBuffer[strlen( pBuffer )-1] = '\0';
  104. }
  105. V_snprintf( tempFileName, sizeof( tempFileName ), "_%s.tmp", pBuffer );
  106. }
  107. else
  108. {
  109. // generate safe name at the desired prefix
  110. char uniqueFilename[MAX_PATH];
  111. SYSTEMTIME sysTime; \
  112. GetLocalTime( &sysTime );
  113. V_sprintf_safe( uniqueFilename, "%d_%d_%d_%d_%d.tmp", sysTime.wDay, sysTime.wHour, sysTime.wMinute, sysTime.wSecond, sysTime.wMilliseconds ); \
  114. V_ComposeFileName( WritePath.String(), uniqueFilename, tempFileName, sizeof( tempFileName ) );
  115. }
  116. FileName = tempFileName;
  117. HANDLE hFile = CreateFile( tempFileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  118. return hFile;
  119. }
  120. static unsigned int FileSeek( HANDLE hFile, unsigned int distance, DWORD MoveMethod )
  121. {
  122. LARGE_INTEGER li;
  123. li.QuadPart = distance;
  124. li.LowPart = SetFilePointer( hFile, li.LowPart, &li.HighPart, MoveMethod);
  125. if ( li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR )
  126. {
  127. li.QuadPart = -1;
  128. }
  129. return ( unsigned int )li.QuadPart;
  130. }
  131. static unsigned int FileTell( HANDLE hFile )
  132. {
  133. return FileSeek( hFile, 0, FILE_CURRENT );
  134. }
  135. static bool FileRead( HANDLE hFile, void *pBuffer, unsigned int size )
  136. {
  137. DWORD numBytesRead;
  138. BOOL bSuccess = ::ReadFile( hFile, pBuffer, size, &numBytesRead, NULL );
  139. return bSuccess && ( numBytesRead == size );
  140. }
  141. static bool FileWrite( HANDLE hFile, void *pBuffer, unsigned int size )
  142. {
  143. DWORD numBytesWritten;
  144. BOOL bSuccess = WriteFile( hFile, pBuffer, size, &numBytesWritten, NULL );
  145. return bSuccess && ( numBytesWritten == size );
  146. }
  147. };
  148. #else
  149. class CWin32File
  150. {
  151. public:
  152. static HANDLE CreateTempFile( CUtlString &WritePath, CUtlString &FileName )
  153. {
  154. char tempFileName[MAX_PATH];
  155. if ( WritePath.IsEmpty() )
  156. {
  157. // use a safe name in the cwd
  158. char *pBuffer = tmpnam( NULL );
  159. if ( !pBuffer )
  160. {
  161. return INVALID_HANDLE_VALUE;
  162. }
  163. if ( pBuffer[0] == '\\' )
  164. {
  165. pBuffer++;
  166. }
  167. if ( pBuffer[strlen( pBuffer )-1] == '.' )
  168. {
  169. pBuffer[strlen( pBuffer )-1] = '\0';
  170. }
  171. V_snprintf( tempFileName, sizeof( tempFileName ), "_%s.tmp", pBuffer );
  172. }
  173. else
  174. {
  175. char uniqueFilename[MAX_PATH];
  176. static int counter = 0;
  177. time_t now = time( NULL );
  178. struct tm *tm = localtime( &now );
  179. V_sprintf_safe( uniqueFilename, "%d_%d_%d_%d_%d.tmp", tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec, ++counter ); \
  180. V_ComposeFileName( WritePath.String(), uniqueFilename, tempFileName, sizeof( tempFileName ) );
  181. }
  182. FileName = tempFileName;
  183. FILE *hFile = fopen( tempFileName, "rw+" );
  184. return (HANDLE)hFile;
  185. }
  186. static unsigned int FileSeek( HANDLE hFile, unsigned int distance, DWORD MoveMethod )
  187. {
  188. return fseeko( (FILE *)hFile, distance, MoveMethod );
  189. }
  190. static unsigned int FileTell( HANDLE hFile )
  191. {
  192. return ftello( (FILE *)hFile );
  193. }
  194. static bool FileRead( HANDLE hFile, void *pBuffer, unsigned int size )
  195. {
  196. size_t bytesRead = fread( pBuffer, 1, size, (FILE *)hFile );
  197. return bytesRead == size;
  198. }
  199. static bool FileWrite( HANDLE hFile, void *pBuffer, unsigned int size )
  200. {
  201. size_t bytesWrtitten = fwrite( pBuffer, 1, size, (FILE *)hFile );
  202. return bytesWrtitten == size;
  203. }
  204. };
  205. #endif
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Interface to allow abstraction of zip file output methods, and
  208. // avoid duplication of code. Files may be written to a CUtlBuffer or a filestream
  209. //-----------------------------------------------------------------------------
  210. abstract_class IWriteStream
  211. {
  212. public:
  213. virtual void Put( const void* pMem, int size ) = 0;
  214. virtual unsigned int Tell( void ) = 0;
  215. };
  216. //-----------------------------------------------------------------------------
  217. // Purpose: Wrapper for CUtlBuffer methods
  218. //-----------------------------------------------------------------------------
  219. class CBufferStream : public IWriteStream
  220. {
  221. public:
  222. CBufferStream( CUtlBuffer& buff ) : IWriteStream(), m_buff( &buff ) {}
  223. // Implementing IWriteStream method
  224. virtual void Put( const void* pMem, int size ) { m_buff->Put( pMem, size ); }
  225. // Implementing IWriteStream method
  226. virtual unsigned int Tell( void ) { return m_buff->TellPut(); }
  227. private:
  228. CUtlBuffer *m_buff;
  229. };
  230. //-----------------------------------------------------------------------------
  231. // Purpose: Wrapper for file I/O methods
  232. //-----------------------------------------------------------------------------
  233. class CFileStream : public IWriteStream
  234. {
  235. public:
  236. CFileStream( FILE *fout ) : IWriteStream(), m_file( fout ), m_hFile( INVALID_HANDLE_VALUE ) {}
  237. CFileStream( HANDLE hOutFile ) : IWriteStream(), m_file( NULL ), m_hFile( hOutFile ) {}
  238. // Implementing IWriteStream method
  239. virtual void Put( const void* pMem, int size )
  240. {
  241. if ( m_file )
  242. {
  243. fwrite( pMem, size, 1, m_file );
  244. }
  245. #ifdef WIN32
  246. else
  247. {
  248. DWORD numBytesWritten;
  249. WriteFile( m_hFile, pMem, size, &numBytesWritten, NULL );
  250. }
  251. #endif
  252. }
  253. // Implementing IWriteStream method
  254. virtual unsigned int Tell( void )
  255. {
  256. if ( m_file )
  257. {
  258. return ftell( m_file );
  259. }
  260. else
  261. {
  262. #ifdef WIN32
  263. return CWin32File::FileTell( m_hFile );
  264. #else
  265. return 0;
  266. #endif
  267. }
  268. }
  269. private:
  270. FILE *m_file;
  271. HANDLE m_hFile;
  272. };
  273. //-----------------------------------------------------------------------------
  274. // Purpose: Container for modifiable pak file which is embedded inside the .bsp file
  275. // itself. It's used to allow one-off files to be stored local to the map and it is
  276. // hooked into the file system as an override for searching for named files.
  277. //-----------------------------------------------------------------------------
  278. class CZipFile
  279. {
  280. public:
  281. // Construction
  282. CZipFile( const char *pDiskCacheWritePath, bool bSortByName );
  283. ~CZipFile( void );
  284. // Public API
  285. // Clear all existing data
  286. void Reset( void );
  287. // Add file to zip under relative name
  288. void AddFileToZip( const char *relativename, const char *fullpath );
  289. // Delete file from zip
  290. void RemoveFileFromZip( const char *relativename );
  291. // Add buffer to zip as a file with given name
  292. void AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode );
  293. // Check if a file already exists in the zip.
  294. bool FileExistsInZip( const char *relativename );
  295. // Reads a file from a zip file
  296. bool ReadFileFromZip( const char *relativename, bool bTextMode, CUtlBuffer &buf );
  297. bool ReadFileFromZip( HANDLE hZipFile, const char *relativename, bool bTextMode, CUtlBuffer &buf );
  298. // Initialize the zip file from a buffer
  299. void ParseFromBuffer( void *buffer, int bufferlength );
  300. HANDLE ParseFromDisk( const char *pFilename );
  301. // Estimate the size of the zip file (including header, padding, etc.)
  302. unsigned int EstimateSize();
  303. // Print out a directory of files in the zip.
  304. void PrintDirectory( void );
  305. // Use to iterate directory, pass 0 for first element
  306. // returns nonzero element id with filled buffer, or -1 at list conclusion
  307. int GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize );
  308. // Write the zip to a buffer
  309. void SaveToBuffer( CUtlBuffer& buffer );
  310. // Write the zip to a filestream
  311. void SaveToDisk( FILE *fout );
  312. void SaveToDisk( HANDLE hOutFile );
  313. unsigned int CalculateSize( void );
  314. void ForceAlignment( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize );
  315. unsigned int GetAlignment();
  316. void SetBigEndian( bool bigEndian );
  317. void ActivateByteSwapping( bool bActivate );
  318. private:
  319. enum
  320. {
  321. MAX_FILES_IN_ZIP = 32768,
  322. };
  323. typedef struct
  324. {
  325. CUtlSymbol m_Name;
  326. unsigned int filepos;
  327. int filelen;
  328. } TmpFileInfo_t;
  329. CByteswap m_Swap;
  330. unsigned int m_AlignmentSize;
  331. bool m_bForceAlignment;
  332. bool m_bCompatibleFormat;
  333. unsigned short CalculatePadding( unsigned int filenameLen, unsigned int pos );
  334. void SaveDirectory( IWriteStream& stream );
  335. int MakeXZipCommentString( char *pComment );
  336. void ParseXZipCommentString( const char *pComment );
  337. // Internal entry for faster searching, etc.
  338. class CZipEntry
  339. {
  340. public:
  341. CZipEntry( void );
  342. ~CZipEntry( void );
  343. CZipEntry( const CZipEntry& src );
  344. // RB tree compare function
  345. static bool ZipFileLessFunc( CZipEntry const& src1, CZipEntry const& src2 );
  346. static bool ZipFileLessFunc_CaselessSort( CZipEntry const& src1, CZipEntry const& src2 );
  347. // Name of entry
  348. CUtlSymbol m_Name;
  349. // Lenth of data element
  350. int m_Length;
  351. // Raw data, could be null and data may be in disk write cache
  352. void *m_pData;
  353. // Offset in Zip ( set and valid during final write )
  354. unsigned int m_ZipOffset;
  355. // CRC of blob ( set and valid during final write )
  356. CRC32_t m_ZipCRC;
  357. // Location of data in disk cache
  358. unsigned int m_DiskCacheOffset;
  359. unsigned int m_SourceDiskOffset;
  360. };
  361. // For fast name lookup and sorting
  362. CUtlRBTree< CZipEntry, int > m_Files;
  363. // Used to buffer zip data, instead of ram
  364. bool m_bUseDiskCacheForWrites;
  365. HANDLE m_hDiskCacheWriteFile;
  366. CUtlString m_DiskCacheName;
  367. CUtlString m_DiskCacheWritePath;
  368. bool m_bIsUpdateFormat;
  369. };
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. //-----------------------------------------------------------------------------
  373. CZipFile::CZipEntry::CZipEntry( void )
  374. {
  375. m_Name = "";
  376. m_Length = 0;
  377. m_pData = NULL;
  378. m_ZipOffset = 0;
  379. m_ZipCRC = 0;
  380. m_DiskCacheOffset = 0;
  381. m_SourceDiskOffset = 0;
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. // Input : src -
  386. //-----------------------------------------------------------------------------
  387. CZipFile::CZipEntry::CZipEntry( const CZipFile::CZipEntry& src )
  388. {
  389. m_Name = src.m_Name;
  390. m_Length = src.m_Length;
  391. if ( src.m_Length > 0 && src.m_pData )
  392. {
  393. m_pData = malloc( src.m_Length );
  394. memcpy( m_pData, src.m_pData, src.m_Length );
  395. }
  396. else
  397. {
  398. m_pData = NULL;
  399. }
  400. m_ZipOffset = src.m_ZipOffset;
  401. m_ZipCRC = src.m_ZipCRC;
  402. m_DiskCacheOffset = src.m_DiskCacheOffset;
  403. m_SourceDiskOffset = src.m_SourceDiskOffset;
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose: Clear any leftover data
  407. //-----------------------------------------------------------------------------
  408. CZipFile::CZipEntry::~CZipEntry( void )
  409. {
  410. if ( m_pData )
  411. {
  412. free( m_pData );
  413. }
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose: Construction
  417. //-----------------------------------------------------------------------------
  418. CZipFile::CZipFile( const char *pDiskCacheWritePath, bool bSortByName )
  419. : m_Files( 0, 32 )
  420. {
  421. m_AlignmentSize = 0;
  422. m_bForceAlignment = false;
  423. m_bCompatibleFormat = true;
  424. m_bIsUpdateFormat = false;
  425. m_bUseDiskCacheForWrites = ( pDiskCacheWritePath != NULL );
  426. m_DiskCacheWritePath = pDiskCacheWritePath;
  427. m_hDiskCacheWriteFile = INVALID_HANDLE_VALUE;
  428. if ( bSortByName )
  429. {
  430. m_Files.SetLessFunc( CZipEntry::ZipFileLessFunc_CaselessSort );
  431. }
  432. else
  433. {
  434. m_Files.SetLessFunc( CZipEntry::ZipFileLessFunc );
  435. }
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Destroy zip data
  439. //-----------------------------------------------------------------------------
  440. CZipFile::~CZipFile( void )
  441. {
  442. m_bUseDiskCacheForWrites = false;
  443. Reset();
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Delete all current data
  447. //-----------------------------------------------------------------------------
  448. void CZipFile::Reset( void )
  449. {
  450. m_Files.RemoveAll();
  451. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  452. {
  453. #ifdef WIN32
  454. CloseHandle( m_hDiskCacheWriteFile );
  455. DeleteFile( m_DiskCacheName.String() );
  456. #else
  457. fclose( (FILE *)m_hDiskCacheWriteFile );
  458. unlink( m_DiskCacheName.String() );
  459. #endif
  460. m_hDiskCacheWriteFile = INVALID_HANDLE_VALUE;
  461. }
  462. if ( m_bUseDiskCacheForWrites )
  463. {
  464. m_hDiskCacheWriteFile = CWin32File::CreateTempFile( m_DiskCacheWritePath, m_DiskCacheName );
  465. }
  466. }
  467. //-----------------------------------------------------------------------------
  468. // Purpose: Comparison for sorting entries
  469. // Input : src1 -
  470. // src2 -
  471. // Output : Returns true on success, false on failure.
  472. //-----------------------------------------------------------------------------
  473. bool CZipFile::CZipEntry::ZipFileLessFunc( CZipEntry const& src1, CZipEntry const& src2 )
  474. {
  475. return ( src1.m_Name < src2.m_Name );
  476. }
  477. bool CZipFile::CZipEntry::ZipFileLessFunc_CaselessSort( CZipEntry const& src1, CZipEntry const& src2 )
  478. {
  479. return ( V_stricmp( src1.m_Name.String(), src2.m_Name.String() ) < 0 );
  480. }
  481. void CZipFile::ForceAlignment( bool bAligned, bool bCompatibleFormat, unsigned int alignment )
  482. {
  483. // special update format, force the args as desired
  484. m_bIsUpdateFormat = false;
  485. if ( alignment == 0xFFFFFFFF )
  486. {
  487. bAligned = false;
  488. alignment = 0;
  489. m_bIsUpdateFormat = true;
  490. }
  491. m_bForceAlignment = bAligned;
  492. m_AlignmentSize = alignment;
  493. m_bCompatibleFormat = bCompatibleFormat;
  494. if ( !bAligned )
  495. {
  496. m_AlignmentSize = 0;
  497. }
  498. else if ( !IsPowerOfTwo( m_AlignmentSize ) )
  499. {
  500. m_AlignmentSize = 0;
  501. }
  502. }
  503. unsigned int CZipFile::GetAlignment()
  504. {
  505. if ( !m_bForceAlignment || !m_AlignmentSize )
  506. {
  507. return 0;
  508. }
  509. return m_AlignmentSize;
  510. }
  511. void CZipFile::SetBigEndian( bool bigEndian )
  512. {
  513. m_Swap.SetTargetBigEndian( bigEndian );
  514. }
  515. void CZipFile::ActivateByteSwapping( bool bActivate )
  516. {
  517. m_Swap.ActivateByteSwapping( bActivate );
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose: Load pak file from raw buffer
  521. // Input : *buffer -
  522. // bufferlength -
  523. //-----------------------------------------------------------------------------
  524. void CZipFile::ParseFromBuffer( void *buffer, int bufferlength )
  525. {
  526. // Throw away old data
  527. Reset();
  528. // Initialize a buffer
  529. CUtlBuffer buf( 0, bufferlength +1 ); // +1 for null termination
  530. // need to swap bytes, so set the buffer opposite the machine's endian
  531. buf.ActivateByteSwapping( m_Swap.IsSwappingBytes() );
  532. buf.Put( buffer, bufferlength );
  533. buf.SeekGet( CUtlBuffer::SEEK_TAIL, 0 );
  534. unsigned int fileLen = buf.TellGet();
  535. // Start from beginning
  536. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  537. ZIP_EndOfCentralDirRecord rec = { 0 };
  538. bool bFoundEndOfCentralDirRecord = false;
  539. unsigned int offset = fileLen - sizeof( ZIP_EndOfCentralDirRecord );
  540. // If offset is ever greater than startOffset then it means that it has
  541. // wrapped. This used to be a tautological >= 0 test.
  542. for ( unsigned int startOffset = offset; offset <= startOffset; offset-- )
  543. {
  544. buf.SeekGet( CUtlBuffer::SEEK_HEAD, offset );
  545. buf.GetObjects( &rec );
  546. if ( rec.signature == PKID( 5, 6 ) )
  547. {
  548. bFoundEndOfCentralDirRecord = true;
  549. // Set any xzip configuration
  550. if ( rec.commentLength )
  551. {
  552. char commentString[128];
  553. int commentLength = MIN( rec.commentLength, sizeof( commentString ) );
  554. buf.Get( commentString, commentLength );
  555. commentString[commentLength] = '\0';
  556. ParseXZipCommentString( commentString );
  557. }
  558. break;
  559. }
  560. else
  561. {
  562. // wrong record
  563. rec.nCentralDirectoryEntries_Total = 0;
  564. }
  565. }
  566. Assert( bFoundEndOfCentralDirRecord );
  567. // Make sure there are some files to parse
  568. int numzipfiles = rec.nCentralDirectoryEntries_Total;
  569. if ( numzipfiles <= 0 )
  570. {
  571. // No files
  572. return;
  573. }
  574. buf.SeekGet( CUtlBuffer::SEEK_HEAD, rec.startOfCentralDirOffset );
  575. // Allocate space for directory
  576. TmpFileInfo_t *newfiles = new TmpFileInfo_t[numzipfiles];
  577. Assert( newfiles );
  578. // build directory
  579. int i;
  580. for ( i = 0; i < rec.nCentralDirectoryEntries_Total; i++ )
  581. {
  582. ZIP_FileHeader zipFileHeader;
  583. buf.GetObjects( &zipFileHeader );
  584. Assert( zipFileHeader.signature == PKID( 1, 2 ) );
  585. Assert( zipFileHeader.compressionMethod == 0 );
  586. char tmpString[1024];
  587. buf.Get( tmpString, zipFileHeader.fileNameLength );
  588. tmpString[zipFileHeader.fileNameLength] = '\0';
  589. Q_strlower( tmpString );
  590. // can determine actual filepos, assuming a well formed zip
  591. newfiles[i].m_Name = tmpString;
  592. newfiles[i].filelen = zipFileHeader.compressedSize;
  593. newfiles[i].filepos = zipFileHeader.relativeOffsetOfLocalHeader +
  594. sizeof( ZIP_LocalFileHeader ) +
  595. zipFileHeader.fileNameLength +
  596. zipFileHeader.extraFieldLength;
  597. int nextOffset;
  598. if ( m_bCompatibleFormat )
  599. {
  600. nextOffset = zipFileHeader.extraFieldLength + zipFileHeader.fileCommentLength;
  601. }
  602. else
  603. {
  604. nextOffset = 0;
  605. }
  606. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nextOffset );
  607. }
  608. // Insert current data into rb tree
  609. for ( i=0; i<numzipfiles; i++ )
  610. {
  611. CZipEntry e;
  612. e.m_Name = newfiles[i].m_Name;
  613. e.m_Length = newfiles[i].filelen;
  614. // Make sure length is reasonable
  615. if ( e.m_Length > 0 )
  616. {
  617. e.m_pData = malloc( e.m_Length );
  618. // Copy in data
  619. buf.SeekGet( CUtlBuffer::SEEK_HEAD, newfiles[i].filepos );
  620. buf.Get( e.m_pData, e.m_Length );
  621. }
  622. else
  623. {
  624. e.m_pData = NULL;
  625. }
  626. // Add to tree
  627. m_Files.Insert( e );
  628. }
  629. // Through away directory
  630. delete[] newfiles;
  631. }
  632. //-----------------------------------------------------------------------------
  633. // Purpose: Mount pak file from disk
  634. //-----------------------------------------------------------------------------
  635. HANDLE CZipFile::ParseFromDisk( const char *pFilename )
  636. {
  637. #ifdef WIN32
  638. HANDLE hFile = CreateFile( pFilename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  639. #else
  640. HANDLE hFile = fopen( pFilename, "rw+" );
  641. #endif
  642. if ( !hFile )
  643. {
  644. // not found
  645. return NULL;
  646. }
  647. unsigned int fileLen = CWin32File::FileSeek( hFile, 0, FILE_END );
  648. CWin32File::FileSeek( hFile, 0, FILE_BEGIN );
  649. if ( fileLen < sizeof( ZIP_EndOfCentralDirRecord ) )
  650. {
  651. // bad format
  652. #ifdef WIN32
  653. CloseHandle( hFile );
  654. #else
  655. fclose( (FILE *)hFile );
  656. #endif
  657. return NULL;
  658. }
  659. // need to get the central dir
  660. ZIP_EndOfCentralDirRecord rec = { 0 };
  661. unsigned int offset = fileLen - sizeof( ZIP_EndOfCentralDirRecord );
  662. // If offset is ever greater than startOffset then it means that it has
  663. // wrapped. This used to be a tautological >= 0 test.
  664. for ( unsigned int startOffset = offset; offset <= startOffset; offset-- )
  665. {
  666. CWin32File::FileSeek( hFile, offset, FILE_BEGIN );
  667. CWin32File::FileRead( hFile, &rec, sizeof( rec ) );
  668. m_Swap.SwapFieldsToTargetEndian( &rec );
  669. if ( rec.signature == PKID( 5, 6 ) )
  670. {
  671. // Set any xzip configuration
  672. if ( rec.commentLength )
  673. {
  674. char commentString[128];
  675. int commentLength = MIN( rec.commentLength, sizeof( commentString ) );
  676. CWin32File::FileRead( hFile, commentString, commentLength );
  677. commentString[commentLength] = '\0';
  678. ParseXZipCommentString( commentString );
  679. }
  680. break;
  681. }
  682. else
  683. {
  684. // wrong record
  685. rec.nCentralDirectoryEntries_Total = 0;
  686. }
  687. }
  688. // Make sure there are some files to parse
  689. int numZipFiles = rec.nCentralDirectoryEntries_Total;
  690. if ( numZipFiles <= 0 )
  691. {
  692. // No files
  693. #ifdef WIN32
  694. CloseHandle( hFile );
  695. #else
  696. fclose( (FILE *)hFile );
  697. #endif
  698. return NULL;
  699. }
  700. CWin32File::FileSeek( hFile, rec.startOfCentralDirOffset, FILE_BEGIN );
  701. // read entire central dir into memory
  702. CUtlBuffer zipDirBuff( 0, rec.centralDirectorySize, 0 );
  703. zipDirBuff.ActivateByteSwapping( m_Swap.IsSwappingBytes() );
  704. CWin32File::FileRead( hFile, zipDirBuff.Base(), rec.centralDirectorySize );
  705. zipDirBuff.SeekPut( CUtlBuffer::SEEK_HEAD, rec.centralDirectorySize );
  706. // build directory
  707. for ( int i = 0; i < numZipFiles; i++ )
  708. {
  709. ZIP_FileHeader zipFileHeader;
  710. zipDirBuff.GetObjects( &zipFileHeader );
  711. if ( zipFileHeader.signature != PKID( 1, 2 ) || zipFileHeader.compressionMethod != 0 )
  712. {
  713. // bad contents
  714. #ifdef WIN32
  715. CloseHandle( hFile );
  716. #else
  717. fclose( (FILE *)hFile );
  718. #endif
  719. return NULL;
  720. }
  721. char fileName[1024];
  722. zipDirBuff.Get( fileName, zipFileHeader.fileNameLength );
  723. fileName[zipFileHeader.fileNameLength] = '\0';
  724. Q_strlower( fileName );
  725. // can determine actual filepos, assuming a well formed zip
  726. CZipEntry e;
  727. e.m_Name = fileName;
  728. e.m_Length = zipFileHeader.compressedSize;
  729. e.m_SourceDiskOffset = zipFileHeader.relativeOffsetOfLocalHeader +
  730. sizeof( ZIP_LocalFileHeader ) +
  731. zipFileHeader.fileNameLength +
  732. zipFileHeader.extraFieldLength;
  733. // Add to tree
  734. m_Files.Insert( e );
  735. int nextOffset;
  736. if ( m_bCompatibleFormat )
  737. {
  738. nextOffset = zipFileHeader.extraFieldLength + zipFileHeader.fileCommentLength;
  739. }
  740. else
  741. {
  742. nextOffset = 0;
  743. }
  744. zipDirBuff.SeekGet( CUtlBuffer::SEEK_CURRENT, nextOffset );
  745. }
  746. return hFile;
  747. }
  748. static int GetLengthOfBinStringAsText( const char *pSrc, int srcSize )
  749. {
  750. const char *pSrcScan = pSrc;
  751. const char *pSrcEnd = pSrc + srcSize;
  752. int numChars = 0;
  753. for( ; pSrcScan < pSrcEnd; pSrcScan++ )
  754. {
  755. if( *pSrcScan == '\n' )
  756. {
  757. numChars += 2;
  758. }
  759. else
  760. {
  761. numChars++;
  762. }
  763. }
  764. return numChars;
  765. }
  766. //-----------------------------------------------------------------------------
  767. // Copies text data from a form appropriate for disk to a normal string
  768. //-----------------------------------------------------------------------------
  769. static void ReadTextData( const char *pSrc, int nSrcSize, CUtlBuffer &buf )
  770. {
  771. buf.EnsureCapacity( nSrcSize + 1 );
  772. const char *pSrcEnd = pSrc + nSrcSize;
  773. for ( const char *pSrcScan = pSrc; pSrcScan < pSrcEnd; ++pSrcScan )
  774. {
  775. if ( *pSrcScan == '\r' )
  776. {
  777. if ( pSrcScan[1] == '\n' )
  778. {
  779. buf.PutChar( '\n' );
  780. ++pSrcScan;
  781. continue;
  782. }
  783. }
  784. buf.PutChar( *pSrcScan );
  785. }
  786. // Null terminate
  787. buf.PutChar( '\0' );
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Copies text data into a form appropriate for disk
  791. //-----------------------------------------------------------------------------
  792. static void CopyTextData( char *pDst, const char *pSrc, int dstSize, int srcSize )
  793. {
  794. const char *pSrcScan = pSrc;
  795. const char *pSrcEnd = pSrc + srcSize;
  796. char *pDstScan = pDst;
  797. #ifdef DBGFLAG_ASSERT
  798. char *pDstEnd = pDst + dstSize;
  799. #endif
  800. for ( ; pSrcScan < pSrcEnd; pSrcScan++ )
  801. {
  802. if ( *pSrcScan == '\n' )
  803. {
  804. *pDstScan = '\r';
  805. pDstScan++;
  806. *pDstScan = '\n';
  807. pDstScan++;
  808. }
  809. else
  810. {
  811. *pDstScan = *pSrcScan;
  812. pDstScan++;
  813. }
  814. }
  815. Assert( pSrcScan == pSrcEnd );
  816. Assert( pDstScan == pDstEnd );
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose: Adds a new lump, or overwrites existing one
  820. // Input : *relativename -
  821. // *data -
  822. // length -
  823. //-----------------------------------------------------------------------------
  824. void CZipFile::AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode )
  825. {
  826. // Lower case only
  827. char name[512];
  828. Q_strcpy( name, relativename );
  829. Q_strlower( name );
  830. int dstLength = length;
  831. if ( bTextMode )
  832. {
  833. dstLength = GetLengthOfBinStringAsText( ( const char * )data, length );
  834. }
  835. // See if entry is in list already
  836. CZipEntry e;
  837. e.m_Name = name;
  838. int index = m_Files.Find( e );
  839. // If already existing, throw away old data and update data and length
  840. if ( index != m_Files.InvalidIndex() )
  841. {
  842. CZipEntry *update = &m_Files[ index ];
  843. if ( update->m_pData )
  844. {
  845. free( update->m_pData );
  846. }
  847. if ( bTextMode )
  848. {
  849. update->m_pData = malloc( dstLength );
  850. CopyTextData( ( char * )update->m_pData, ( char * )data, dstLength, length );
  851. update->m_Length = dstLength;
  852. }
  853. else
  854. {
  855. update->m_pData = malloc( length );
  856. memcpy( update->m_pData, data, length );
  857. update->m_Length = length;
  858. }
  859. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  860. {
  861. update->m_DiskCacheOffset = CWin32File::FileTell( m_hDiskCacheWriteFile );
  862. CWin32File::FileWrite( m_hDiskCacheWriteFile, update->m_pData, update->m_Length );
  863. free( update->m_pData );
  864. update->m_pData = NULL;
  865. }
  866. }
  867. else
  868. {
  869. // Create a new entry
  870. e.m_Length = dstLength;
  871. if ( dstLength > 0 )
  872. {
  873. if ( bTextMode )
  874. {
  875. e.m_pData = malloc( dstLength );
  876. CopyTextData( (char *)e.m_pData, ( char * )data, dstLength, length );
  877. }
  878. else
  879. {
  880. e.m_pData = malloc( length );
  881. memcpy( e.m_pData, data, length );
  882. }
  883. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  884. {
  885. e.m_DiskCacheOffset = CWin32File::FileTell( m_hDiskCacheWriteFile );
  886. CWin32File::FileWrite( m_hDiskCacheWriteFile, e.m_pData, e.m_Length );
  887. free( e.m_pData );
  888. e.m_pData = NULL;
  889. }
  890. }
  891. else
  892. {
  893. e.m_pData = NULL;
  894. }
  895. m_Files.Insert( e );
  896. }
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Reads a file from the zip
  900. //-----------------------------------------------------------------------------
  901. bool CZipFile::ReadFileFromZip( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf )
  902. {
  903. // Lower case only
  904. char pName[512];
  905. Q_strncpy( pName, pRelativeName, 512 );
  906. Q_strlower( pName );
  907. // See if entry is in list already
  908. CZipEntry e;
  909. e.m_Name = pName;
  910. int nIndex = m_Files.Find( e );
  911. if ( nIndex == m_Files.InvalidIndex() )
  912. {
  913. // not found
  914. return false;
  915. }
  916. CZipEntry *pEntry = &m_Files[ nIndex ];
  917. if ( bTextMode )
  918. {
  919. buf.SetBufferType( true, false );
  920. ReadTextData( (char*)pEntry->m_pData, pEntry->m_Length, buf );
  921. }
  922. else
  923. {
  924. buf.SetBufferType( false, false );
  925. buf.Put( pEntry->m_pData, pEntry->m_Length );
  926. }
  927. return true;
  928. }
  929. //-----------------------------------------------------------------------------
  930. // Reads a file from the zip
  931. //-----------------------------------------------------------------------------
  932. bool CZipFile::ReadFileFromZip( HANDLE hZipFile, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf )
  933. {
  934. // Lower case only
  935. char pName[512];
  936. Q_strncpy( pName, pRelativeName, 512 );
  937. Q_strlower( pName );
  938. // See if entry is in list already
  939. CZipEntry e;
  940. e.m_Name = pName;
  941. int nIndex = m_Files.Find( e );
  942. if ( nIndex == m_Files.InvalidIndex() )
  943. {
  944. // not found
  945. return false;
  946. }
  947. CZipEntry *pEntry = &m_Files[nIndex];
  948. void *pData = malloc( pEntry->m_Length );
  949. CWin32File::FileSeek( hZipFile, pEntry->m_SourceDiskOffset, FILE_BEGIN );
  950. if ( !CWin32File::FileRead( hZipFile, pData, pEntry->m_Length ) )
  951. {
  952. free( pData );
  953. return false;
  954. }
  955. if ( bTextMode )
  956. {
  957. buf.SetBufferType( true, false );
  958. ReadTextData( (const char *)pData, pEntry->m_Length, buf );
  959. }
  960. else
  961. {
  962. buf.SetBufferType( false, false );
  963. buf.Put( pData, pEntry->m_Length );
  964. }
  965. free( pData );
  966. return true;
  967. }
  968. //-----------------------------------------------------------------------------
  969. // Purpose: Check if a file already exists in the zip.
  970. // Input : *relativename -
  971. //-----------------------------------------------------------------------------
  972. bool CZipFile::FileExistsInZip( const char *pRelativeName )
  973. {
  974. // Lower case only
  975. char pName[512];
  976. Q_strncpy( pName, pRelativeName, 512 );
  977. Q_strlower( pName );
  978. // See if entry is in list already
  979. CZipEntry e;
  980. e.m_Name = pName;
  981. int nIndex = m_Files.Find( e );
  982. // If it is, then it exists in the pack!
  983. return nIndex != m_Files.InvalidIndex();
  984. }
  985. //-----------------------------------------------------------------------------
  986. // Purpose: Adds a new file to the zip.
  987. //-----------------------------------------------------------------------------
  988. void CZipFile::AddFileToZip( const char *relativename, const char *fullpath )
  989. {
  990. FILE *temp;
  991. #if defined( POSIX )
  992. temp = fopen( fullpath, "rb" );
  993. #else
  994. fopen_s( &temp, fullpath, "rb" );
  995. #endif
  996. if ( !temp )
  997. return;
  998. // Determine length
  999. fseek( temp, 0, SEEK_END );
  1000. int size = ftell( temp );
  1001. fseek( temp, 0, SEEK_SET );
  1002. byte *buf = (byte *)malloc( size + 1 );
  1003. // Read data
  1004. fread( buf, size, 1, temp );
  1005. fclose( temp );
  1006. // Now add as a buffer
  1007. AddBufferToZip( relativename, buf, size, false );
  1008. free( buf );
  1009. }
  1010. //-----------------------------------------------------------------------------
  1011. // Purpose: Removes a file from the zip.
  1012. //-----------------------------------------------------------------------------
  1013. void CZipFile::RemoveFileFromZip( const char *relativename )
  1014. {
  1015. CZipEntry e;
  1016. e.m_Name = relativename;
  1017. int index = m_Files.Find( e );
  1018. if ( index != m_Files.InvalidIndex() )
  1019. {
  1020. CZipEntry update = m_Files[index];
  1021. m_Files.Remove( update );
  1022. }
  1023. }
  1024. //---------------------------------------------------------------
  1025. // Purpose: Calculates how many bytes should be added to the extra field
  1026. // to push the start of the file data to the next aligned boundary
  1027. // Output: Required padding size
  1028. //---------------------------------------------------------------
  1029. unsigned short CZipFile::CalculatePadding( unsigned int filenameLen, unsigned int pos )
  1030. {
  1031. if ( m_AlignmentSize == 0 )
  1032. {
  1033. return 0;
  1034. }
  1035. unsigned int headerSize = sizeof( ZIP_LocalFileHeader ) + filenameLen;
  1036. return (unsigned short)( m_AlignmentSize - ( ( pos + headerSize ) % m_AlignmentSize ) );
  1037. }
  1038. //-----------------------------------------------------------------------------
  1039. // Purpose: Create the XZIP identifying comment string
  1040. // Output : Length
  1041. //-----------------------------------------------------------------------------
  1042. int CZipFile::MakeXZipCommentString( char *pCommentString )
  1043. {
  1044. char tempString[XZIP_COMMENT_LENGTH];
  1045. memset( tempString, 0, sizeof( tempString ) );
  1046. char cFormat = m_bCompatibleFormat ? '1' : '2';
  1047. if ( m_bCompatibleFormat )
  1048. {
  1049. cFormat = '1';
  1050. }
  1051. else if ( !m_bIsUpdateFormat )
  1052. {
  1053. cFormat = '2';
  1054. }
  1055. else
  1056. {
  1057. // update format
  1058. cFormat = '3';
  1059. }
  1060. V_snprintf( tempString, sizeof( tempString ), "XZP%c %d", cFormat, m_AlignmentSize );
  1061. if ( pCommentString )
  1062. {
  1063. memcpy( pCommentString, tempString, sizeof( tempString ) );
  1064. }
  1065. // expected fixed length
  1066. return XZIP_COMMENT_LENGTH;
  1067. }
  1068. //-----------------------------------------------------------------------------
  1069. // Purpose: An XZIP has its configuration in the ascii comment
  1070. //-----------------------------------------------------------------------------
  1071. void CZipFile::ParseXZipCommentString( const char *pCommentString )
  1072. {
  1073. if ( !V_strnicmp( pCommentString, "XZP", 3 ) )
  1074. {
  1075. m_bCompatibleFormat = true;
  1076. if ( pCommentString[3] == '2' )
  1077. {
  1078. m_bCompatibleFormat = false;
  1079. }
  1080. else if ( pCommentString[3] == '3' )
  1081. {
  1082. m_bCompatibleFormat = false;
  1083. m_bIsUpdateFormat = true;
  1084. }
  1085. // parse out the alignement configuration
  1086. if ( !m_bForceAlignment )
  1087. {
  1088. m_AlignmentSize = 0;
  1089. sscanf( pCommentString + 4, "%d", &m_AlignmentSize );
  1090. if ( !IsPowerOfTwo( m_AlignmentSize ) )
  1091. {
  1092. m_AlignmentSize = 0;
  1093. }
  1094. }
  1095. }
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Purpose: Calculate the exact size of zip file, with headers and padding
  1099. // Output : int
  1100. //-----------------------------------------------------------------------------
  1101. unsigned int CZipFile::CalculateSize( void )
  1102. {
  1103. unsigned int size = 0;
  1104. unsigned int dirHeaders = 0;
  1105. for ( int i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) )
  1106. {
  1107. CZipEntry *e = &m_Files[ i ];
  1108. if ( e->m_Length == 0 )
  1109. continue;
  1110. // local file header
  1111. size += sizeof( ZIP_LocalFileHeader );
  1112. size += strlen( e->m_Name.String() );
  1113. // every file has a directory header that duplicates the filename
  1114. dirHeaders += sizeof( ZIP_FileHeader ) + strlen( e->m_Name.String() );
  1115. // calculate padding
  1116. if ( m_AlignmentSize != 0 )
  1117. {
  1118. // round up to next boundary
  1119. unsigned int nextBoundary = ( size + m_AlignmentSize ) & ~( m_AlignmentSize - 1 );
  1120. // the directory header also duplicates the padding
  1121. dirHeaders += nextBoundary - size;
  1122. size = nextBoundary;
  1123. }
  1124. // data size
  1125. size += e->m_Length;
  1126. }
  1127. size += dirHeaders;
  1128. // All processed zip files will have a comment string
  1129. size += sizeof( ZIP_EndOfCentralDirRecord ) + MakeXZipCommentString( NULL );
  1130. return size;
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // Purpose: Print a directory of files in the zip
  1134. //-----------------------------------------------------------------------------
  1135. void CZipFile::PrintDirectory( void )
  1136. {
  1137. for ( int i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) )
  1138. {
  1139. CZipEntry *e = &m_Files[ i ];
  1140. Msg( "%s\n", e->m_Name.String() );
  1141. }
  1142. }
  1143. //-----------------------------------------------------------------------------
  1144. // Purpose: Iterate through directory
  1145. //-----------------------------------------------------------------------------
  1146. int CZipFile::GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize )
  1147. {
  1148. if ( id == -1 )
  1149. {
  1150. id = m_Files.FirstInorder();
  1151. }
  1152. else
  1153. {
  1154. id = m_Files.NextInorder( id );
  1155. }
  1156. if ( id == m_Files.InvalidIndex() )
  1157. {
  1158. // list is empty
  1159. return -1;
  1160. }
  1161. CZipEntry *e = &m_Files[id];
  1162. Q_strncpy( pBuffer, e->m_Name.String(), bufferSize );
  1163. fileSize = e->m_Length;
  1164. return id;
  1165. }
  1166. //-----------------------------------------------------------------------------
  1167. // Purpose: Store data out to disk
  1168. //-----------------------------------------------------------------------------
  1169. void CZipFile::SaveToDisk( FILE *fout )
  1170. {
  1171. CFileStream stream( fout );
  1172. SaveDirectory( stream );
  1173. }
  1174. void CZipFile::SaveToDisk( HANDLE hOutFile )
  1175. {
  1176. CFileStream stream( hOutFile );
  1177. SaveDirectory( stream );
  1178. }
  1179. //-----------------------------------------------------------------------------
  1180. // Purpose: Store data out to a CUtlBuffer
  1181. //-----------------------------------------------------------------------------
  1182. void CZipFile::SaveToBuffer( CUtlBuffer& buf )
  1183. {
  1184. CBufferStream stream( buf );
  1185. SaveDirectory( stream );
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. // Purpose: Store data back out to a stream (could be CUtlBuffer or filestream)
  1189. //-----------------------------------------------------------------------------
  1190. void CZipFile::SaveDirectory( IWriteStream& stream )
  1191. {
  1192. void *pPaddingBuffer = NULL;
  1193. if ( m_AlignmentSize )
  1194. {
  1195. // get a temp buffer for all padding work
  1196. pPaddingBuffer = malloc( m_AlignmentSize );
  1197. memset( pPaddingBuffer, 0x00, m_AlignmentSize );
  1198. }
  1199. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  1200. {
  1201. #ifdef WIN32
  1202. FlushFileBuffers( m_hDiskCacheWriteFile );
  1203. #else
  1204. fflush( (FILE *)m_hDiskCacheWriteFile );
  1205. #endif
  1206. }
  1207. bool bDataWritten = false;
  1208. int i;
  1209. for ( i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) )
  1210. {
  1211. CZipEntry *e = &m_Files[i];
  1212. Assert( e );
  1213. // Fix up the offset
  1214. e->m_ZipOffset = stream.Tell();
  1215. if ( e->m_Length > 0 && ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE ) )
  1216. {
  1217. // get the data back from the write cache
  1218. e->m_pData = malloc( e->m_Length );
  1219. if ( e->m_pData )
  1220. {
  1221. CWin32File::FileSeek( m_hDiskCacheWriteFile, e->m_DiskCacheOffset, FILE_BEGIN );
  1222. CWin32File::FileRead( m_hDiskCacheWriteFile, e->m_pData, e->m_Length );
  1223. }
  1224. }
  1225. if ( e->m_Length > 0 && e->m_pData != NULL )
  1226. {
  1227. ZIP_LocalFileHeader hdr = { 0 };
  1228. hdr.signature = PKID( 3, 4 );
  1229. hdr.versionNeededToExtract = 10; // This is the version that the winzip that I have writes.
  1230. hdr.flags = 0;
  1231. hdr.compressionMethod = 0; // NO COMPRESSION!
  1232. hdr.lastModifiedTime = 0;
  1233. hdr.lastModifiedDate = 0;
  1234. CRC32_Init( &e->m_ZipCRC );
  1235. CRC32_ProcessBuffer( &e->m_ZipCRC, e->m_pData, e->m_Length );
  1236. CRC32_Final( &e->m_ZipCRC );
  1237. hdr.crc32 = e->m_ZipCRC;
  1238. const char *pFilename = e->m_Name.String();
  1239. hdr.compressedSize = e->m_Length;
  1240. hdr.uncompressedSize = e->m_Length;
  1241. hdr.fileNameLength = strlen( pFilename );
  1242. hdr.extraFieldLength = CalculatePadding( hdr.fileNameLength, e->m_ZipOffset );
  1243. int extraFieldLength = hdr.extraFieldLength;
  1244. // Swap header in place
  1245. m_Swap.SwapFieldsToTargetEndian( &hdr );
  1246. stream.Put( &hdr, sizeof( hdr ) );
  1247. stream.Put( pFilename, strlen( pFilename ) );
  1248. stream.Put( pPaddingBuffer, extraFieldLength );
  1249. // An update format specifically does not place any files
  1250. // except the first file which should be the preload section.
  1251. // All files in an update zip, exist compressed in the preload section.
  1252. if ( m_bCompatibleFormat || !m_bIsUpdateFormat || !bDataWritten )
  1253. {
  1254. // write the data
  1255. stream.Put( e->m_pData, e->m_Length );
  1256. bDataWritten = true;
  1257. }
  1258. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  1259. {
  1260. free( e->m_pData );
  1261. // temp hackery for the logic below to succeed
  1262. e->m_pData = (void*)0xFFFFFFFF;
  1263. }
  1264. }
  1265. }
  1266. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  1267. {
  1268. CWin32File::FileSeek( m_hDiskCacheWriteFile, 0, FILE_END );
  1269. }
  1270. unsigned int centralDirStart = stream.Tell();
  1271. if ( m_AlignmentSize )
  1272. {
  1273. // align the central directory starting position
  1274. unsigned int newDirStart = AlignValue( centralDirStart, m_AlignmentSize );
  1275. int padLength = newDirStart - centralDirStart;
  1276. if ( padLength )
  1277. {
  1278. stream.Put( pPaddingBuffer, padLength );
  1279. centralDirStart = newDirStart;
  1280. }
  1281. }
  1282. int realNumFiles = 0;
  1283. for ( i = m_Files.FirstInorder(); i != m_Files.InvalidIndex(); i = m_Files.NextInorder( i ) )
  1284. {
  1285. CZipEntry *e = &m_Files[i];
  1286. Assert( e );
  1287. if ( e->m_Length > 0 && e->m_pData != NULL )
  1288. {
  1289. ZIP_FileHeader hdr = { 0 };
  1290. hdr.signature = PKID( 1, 2 );
  1291. hdr.versionMadeBy = 20; // This is the version that the winzip that I have writes.
  1292. hdr.versionNeededToExtract = 10; // This is the version that the winzip that I have writes.
  1293. hdr.flags = 0;
  1294. hdr.compressionMethod = 0;
  1295. hdr.lastModifiedTime = 0;
  1296. hdr.lastModifiedDate = 0;
  1297. hdr.crc32 = e->m_ZipCRC;
  1298. hdr.compressedSize = e->m_Length;
  1299. hdr.uncompressedSize = e->m_Length;
  1300. hdr.fileNameLength = strlen( e->m_Name.String() );
  1301. hdr.extraFieldLength = CalculatePadding( hdr.fileNameLength, e->m_ZipOffset );
  1302. hdr.fileCommentLength = 0;
  1303. hdr.diskNumberStart = 0;
  1304. hdr.internalFileAttribs = 0;
  1305. hdr.externalFileAttribs = 0; // This is usually something, but zero is OK as if the input came from stdin
  1306. hdr.relativeOffsetOfLocalHeader = e->m_ZipOffset;
  1307. int extraFieldLength = hdr.extraFieldLength;
  1308. // Swap the header in place
  1309. m_Swap.SwapFieldsToTargetEndian( &hdr );
  1310. stream.Put( &hdr, sizeof( hdr ) );
  1311. stream.Put( e->m_Name.String(), strlen( e->m_Name.String() ) );
  1312. if ( m_bCompatibleFormat )
  1313. {
  1314. stream.Put( pPaddingBuffer, extraFieldLength );
  1315. }
  1316. realNumFiles++;
  1317. if ( m_hDiskCacheWriteFile != INVALID_HANDLE_VALUE )
  1318. {
  1319. // clear out temp hackery
  1320. e->m_pData = NULL;
  1321. }
  1322. }
  1323. }
  1324. unsigned int centralDirEnd = stream.Tell();
  1325. if ( m_AlignmentSize )
  1326. {
  1327. // align the central directory starting position
  1328. unsigned int newDirEnd = AlignValue( centralDirEnd, m_AlignmentSize );
  1329. int padLength = newDirEnd - centralDirEnd;
  1330. if ( padLength )
  1331. {
  1332. stream.Put( pPaddingBuffer, padLength );
  1333. centralDirEnd = newDirEnd;
  1334. }
  1335. }
  1336. ZIP_EndOfCentralDirRecord rec = { 0 };
  1337. rec.signature = PKID( 5, 6 );
  1338. rec.numberOfThisDisk = 0;
  1339. rec.numberOfTheDiskWithStartOfCentralDirectory = 0;
  1340. rec.nCentralDirectoryEntries_ThisDisk = realNumFiles;
  1341. rec.nCentralDirectoryEntries_Total = realNumFiles;
  1342. rec.centralDirectorySize = centralDirEnd - centralDirStart;
  1343. rec.startOfCentralDirOffset = centralDirStart;
  1344. char commentString[128];
  1345. int commentLength = MakeXZipCommentString( commentString );
  1346. rec.commentLength = commentLength;
  1347. // Swap the header in place
  1348. m_Swap.SwapFieldsToTargetEndian( &rec );
  1349. stream.Put( &rec, sizeof( rec ) );
  1350. stream.Put( commentString, commentLength );
  1351. if ( pPaddingBuffer )
  1352. {
  1353. free( pPaddingBuffer );
  1354. }
  1355. }
  1356. class CZip : public IZip
  1357. {
  1358. public:
  1359. CZip( const char *pDiskCacheWritePath, bool bSortByName );
  1360. virtual ~CZip();
  1361. virtual void Reset();
  1362. // Add a single file to a zip - maintains the zip's previous alignment state
  1363. virtual void AddFileToZip( const char *relativename, const char *fullpath );
  1364. // Whether a file is contained in a zip - maintains alignment
  1365. virtual bool FileExistsInZip( const char *pRelativeName );
  1366. // Reads a file from the zip - maintains alignement
  1367. virtual bool ReadFileFromZip( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf );
  1368. virtual bool ReadFileFromZip( HANDLE hZipFile, const char *relativename, bool bTextMode, CUtlBuffer &buf );
  1369. // Removes a single file from the zip - maintains alignment
  1370. virtual void RemoveFileFromZip( const char *relativename );
  1371. // Gets next filename in zip, for walking the directory - maintains alignment
  1372. virtual int GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize );
  1373. // Prints the zip's contents - maintains alignment
  1374. virtual void PrintDirectory( void );
  1375. // Estimate the size of the Zip (including header, padding, etc.)
  1376. virtual unsigned int EstimateSize( void );
  1377. // Add buffer to zip as a file with given name - uses current alignment size, default 0 (no alignment)
  1378. virtual void AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode );
  1379. // Writes out zip file to a buffer - uses current alignment size
  1380. // (set by file's previous alignment, or a call to ForceAlignment)
  1381. virtual void SaveToBuffer( CUtlBuffer& outbuf );
  1382. // Writes out zip file to a filestream - uses current alignment size
  1383. // (set by file's previous alignment, or a call to ForceAlignment)
  1384. virtual void SaveToDisk( FILE *fout );
  1385. virtual void SaveToDisk( HANDLE hOutFile );
  1386. // Reads a zip file from a buffer into memory - sets current alignment size to
  1387. // the file's alignment size, unless overridden by a ForceAlignment call)
  1388. virtual void ParseFromBuffer( void *buffer, int bufferlength );
  1389. virtual HANDLE ParseFromDisk( const char *pFilename );
  1390. // Forces a specific alignment size for all subsequent file operations, overriding files' previous alignment size.
  1391. // Return to using files' individual alignment sizes by passing FALSE.
  1392. virtual void ForceAlignment( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize );
  1393. // Sets the endianess of the zip
  1394. virtual void SetBigEndian( bool bigEndian );
  1395. virtual void ActivateByteSwapping( bool bActivate );
  1396. virtual unsigned int GetAlignment();
  1397. private:
  1398. CZipFile m_ZipFile;
  1399. };
  1400. static CUtlLinkedList< CZip* > g_ZipUtils;
  1401. IZip *IZip::CreateZip( const char *pDiskCacheWritePath, bool bSortByName )
  1402. {
  1403. CZip *pZip = new CZip( pDiskCacheWritePath, bSortByName );
  1404. g_ZipUtils.AddToTail( pZip );
  1405. return pZip;
  1406. }
  1407. void IZip::ReleaseZip( IZip *pZip )
  1408. {
  1409. g_ZipUtils.FindAndRemove( (CZip *)pZip );
  1410. delete ((CZip *)pZip);
  1411. }
  1412. CZip::CZip( const char *pDiskCacheWritePath, bool bSortByName ) : m_ZipFile( pDiskCacheWritePath, bSortByName )
  1413. {
  1414. m_ZipFile.Reset();
  1415. }
  1416. CZip::~CZip()
  1417. {
  1418. }
  1419. void CZip::SetBigEndian( bool bigEndian )
  1420. {
  1421. m_ZipFile.SetBigEndian( bigEndian );
  1422. }
  1423. void CZip::ActivateByteSwapping( bool bActivate )
  1424. {
  1425. m_ZipFile.ActivateByteSwapping( bActivate );
  1426. }
  1427. void CZip::AddFileToZip( const char *relativename, const char *fullpath )
  1428. {
  1429. m_ZipFile.AddFileToZip( relativename, fullpath );
  1430. }
  1431. bool CZip::FileExistsInZip( const char *pRelativeName )
  1432. {
  1433. return m_ZipFile.FileExistsInZip( pRelativeName );
  1434. }
  1435. bool CZip::ReadFileFromZip( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf )
  1436. {
  1437. return m_ZipFile.ReadFileFromZip( pRelativeName, bTextMode, buf );
  1438. }
  1439. bool CZip::ReadFileFromZip( HANDLE hZipFile, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf )
  1440. {
  1441. return m_ZipFile.ReadFileFromZip( hZipFile, pRelativeName, bTextMode, buf );
  1442. }
  1443. void CZip::RemoveFileFromZip( const char *relativename )
  1444. {
  1445. m_ZipFile.RemoveFileFromZip( relativename );
  1446. }
  1447. int CZip::GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize )
  1448. {
  1449. return m_ZipFile.GetNextFilename( id, pBuffer, bufferSize, fileSize );
  1450. }
  1451. void CZip::PrintDirectory( void )
  1452. {
  1453. m_ZipFile.PrintDirectory();
  1454. }
  1455. void CZip::Reset()
  1456. {
  1457. m_ZipFile.Reset();
  1458. }
  1459. unsigned int CZip::EstimateSize( void )
  1460. {
  1461. return m_ZipFile.CalculateSize();
  1462. }
  1463. // Add buffer to zip as a file with given name
  1464. void CZip::AddBufferToZip( const char *relativename, void *data, int length, bool bTextMode )
  1465. {
  1466. m_ZipFile.AddBufferToZip( relativename, data, length, bTextMode );
  1467. }
  1468. void CZip::SaveToBuffer( CUtlBuffer& outbuf )
  1469. {
  1470. m_ZipFile.SaveToBuffer( outbuf );
  1471. }
  1472. void CZip::SaveToDisk( FILE *fout )
  1473. {
  1474. m_ZipFile.SaveToDisk( fout );
  1475. }
  1476. void CZip::SaveToDisk( HANDLE hOutFile )
  1477. {
  1478. m_ZipFile.SaveToDisk( hOutFile );
  1479. }
  1480. void CZip::ParseFromBuffer( void *buffer, int bufferlength )
  1481. {
  1482. m_ZipFile.Reset();
  1483. m_ZipFile.ParseFromBuffer( buffer, bufferlength );
  1484. }
  1485. HANDLE CZip::ParseFromDisk( const char *pFilename )
  1486. {
  1487. m_ZipFile.Reset();
  1488. return m_ZipFile.ParseFromDisk( pFilename );
  1489. }
  1490. void CZip::ForceAlignment( bool aligned, bool bCompatibleFormat, unsigned int alignmentSize )
  1491. {
  1492. m_ZipFile.ForceAlignment( aligned, bCompatibleFormat, alignmentSize );
  1493. }
  1494. unsigned int CZip::GetAlignment()
  1495. {
  1496. return m_ZipFile.GetAlignment();
  1497. }