Unzip.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. // unzip.cpp : Defines the entry point for the console application.
  13. //
  14. #include "stdh.h"
  15. #include <Engine/Base/Stream.h>
  16. #include <Engine/Base/FileName.h>
  17. #include <Engine/Base/Translation.h>
  18. #include <Engine/Base/ErrorReporting.h>
  19. #include <Engine/Base/Console.h>
  20. #include <Engine/Base/Synchronization.h>
  21. #include <Engine/Math/Functions.h>
  22. #include <Engine/Templates/StaticArray.cpp>
  23. #include <Engine/Templates/StaticStackArray.cpp>
  24. #include <Engine/zlib/zlib.h>
  25. extern CTCriticalSection zip_csLock; // critical section for access to zlib functions
  26. #pragma pack(1)
  27. // before each file in the zip
  28. #define SIGNATURE_LFH 0x04034b50
  29. struct LocalFileHeader {
  30. SWORD lfh_swVersionToExtract;
  31. SWORD lfh_swGPBFlag;
  32. SWORD lfh_swCompressionMethod;
  33. SWORD lfh_swModFileTime;
  34. SWORD lfh_swModFileDate;
  35. SLONG lfh_slCRC32;
  36. SLONG lfh_slCompressedSize;
  37. SLONG lfh_slUncompressedSize;
  38. SWORD lfh_swFileNameLen;
  39. SWORD lfh_swExtraFieldLen;
  40. // follows:
  41. // filename (variable size)
  42. // extra field (variable size)
  43. };
  44. // after file data, only if compressed from a non-seekable stream
  45. // this exists only if bit 3 in GPB flag is set
  46. #define SIGNATURE_DD 0x08074b50
  47. struct DataDescriptor {
  48. SLONG dd_slCRC32;
  49. SLONG dd_slCompressedSize;
  50. SLONG dd_slUncompressedSize;
  51. };
  52. // one file in central dir
  53. #define SIGNATURE_FH 0x02014b50
  54. struct FileHeader {
  55. SWORD fh_swVersionMadeBy;
  56. SWORD fh_swVersionToExtract;
  57. SWORD fh_swGPBFlag;
  58. SWORD fh_swCompressionMethod;
  59. SWORD fh_swModFileTime;
  60. SWORD fh_swModFileDate;
  61. SLONG fh_slCRC32;
  62. SLONG fh_slCompressedSize;
  63. SLONG fh_slUncompressedSize;
  64. SWORD fh_swFileNameLen;
  65. SWORD fh_swExtraFieldLen;
  66. SWORD fh_swFileCommentLen;
  67. SWORD fh_swDiskNoStart;
  68. SWORD fh_swInternalFileAttributes;
  69. SLONG fh_swExternalFileAttributes;
  70. SLONG fh_slLocalHeaderOffset;
  71. // follows:
  72. // filename (variable size)
  73. // extra field (variable size)
  74. // file comment (variable size)
  75. };
  76. // at the end of entire zip file
  77. #define SIGNATURE_EOD 0x06054b50
  78. struct EndOfDir {
  79. SWORD eod_swDiskNo;
  80. SWORD eod_swDirStartDiskNo;
  81. SWORD eod_swEntriesInDirOnThisDisk;
  82. SWORD eod_swEntriesInDir;
  83. SLONG eod_slSizeOfDir;
  84. SLONG eod_slDirOffsetInFile;
  85. SWORD eod_swCommentLenght;
  86. // follows:
  87. // zipfile comment (variable size)
  88. };
  89. #pragma pack()
  90. // one entry (a zipped file) in a zip archive
  91. class CZipEntry {
  92. public:
  93. CTFileName *ze_pfnmArchive; // path of the archive
  94. CTFileName ze_fnm; // file name with path inside archive
  95. SLONG ze_slCompressedSize; // size of file in the archive
  96. SLONG ze_slUncompressedSize; // size when uncompressed
  97. SLONG ze_slDataOffset; // position of compressed data inside archive
  98. ULONG ze_ulCRC; // checksum of the file
  99. BOOL ze_bStored; // set if file is not compressed, but stored
  100. BOOL ze_bMod; // set if from a mod's archive
  101. void Clear(void)
  102. {
  103. ze_pfnmArchive = NULL;
  104. ze_fnm.Clear();
  105. }
  106. };
  107. // an open instance of a file inside a zip
  108. class CZipHandle {
  109. public:
  110. BOOL zh_bOpen; // set if the handle is used
  111. CZipEntry zh_zeEntry; // the entry itself
  112. z_stream zh_zstream; // zlib filestream for decompression
  113. FILE *zh_fFile; // open handle of the archive
  114. #define BUF_SIZE 1024
  115. UBYTE *zh_pubBufIn; // input buffer
  116. CZipHandle(void);
  117. void Clear(void);
  118. void ThrowZLIBError_t(int ierr, const CTString &strDescription);
  119. };
  120. // get error string for a zlib error
  121. CTString GetZlibError(int ierr)
  122. {
  123. switch(ierr) {
  124. case Z_OK : return TRANS("Z_OK "); break;
  125. case Z_STREAM_END : return TRANS("Z_STREAM_END "); break;
  126. case Z_NEED_DICT : return TRANS("Z_NEED_DICT "); break;
  127. case Z_STREAM_ERROR : return TRANS("Z_STREAM_ERROR "); break;
  128. case Z_DATA_ERROR : return TRANS("Z_DATA_ERROR "); break;
  129. case Z_MEM_ERROR : return TRANS("Z_MEM_ERROR "); break;
  130. case Z_BUF_ERROR : return TRANS("Z_BUF_ERROR "); break;
  131. case Z_VERSION_ERROR: return TRANS("Z_VERSION_ERROR"); break;
  132. case Z_ERRNO : {
  133. CTString strError;
  134. strError.PrintF(TRANS("Z_ERRNO: %s"), strerror(errno));
  135. return strError;
  136. } break;
  137. default: {
  138. CTString strError;
  139. strError.PrintF(TRANS("Unknown ZLIB error: %d"), ierr);
  140. return strError;
  141. } break;
  142. }
  143. }
  144. CZipHandle::CZipHandle(void)
  145. {
  146. zh_bOpen = FALSE;
  147. zh_fFile = NULL;
  148. zh_pubBufIn = NULL;
  149. memset(&zh_zstream, 0, sizeof(zh_zstream));
  150. }
  151. void CZipHandle::Clear(void)
  152. {
  153. zh_bOpen = FALSE;
  154. zh_zeEntry.Clear();
  155. // clear the zlib stream
  156. CTSingleLock slZip(&zip_csLock, TRUE);
  157. inflateEnd(&zh_zstream);
  158. memset(&zh_zstream, 0, sizeof(zh_zstream));
  159. // free buffers
  160. if (zh_pubBufIn!=NULL) {
  161. FreeMemory(zh_pubBufIn);
  162. zh_pubBufIn = NULL;
  163. }
  164. // close the zip archive file
  165. if (zh_fFile!=NULL) {
  166. fclose(zh_fFile);
  167. zh_fFile = NULL;
  168. }
  169. }
  170. void CZipHandle::ThrowZLIBError_t(int ierr, const CTString &strDescription)
  171. {
  172. ThrowF_t(TRANS("(%s/%s) %s - ZLIB error: %s - %s"),
  173. (const CTString&)*zh_zeEntry.ze_pfnmArchive,
  174. (const CTString&)zh_zeEntry.ze_fnm,
  175. strDescription, GetZlibError(ierr), zh_zstream.msg);
  176. }
  177. // all files in all active zip archives
  178. static CStaticStackArray<CZipEntry> _azeFiles;
  179. // handles for currently open files
  180. static CStaticStackArray<CZipHandle> _azhHandles;
  181. // filenames of all archives
  182. static CStaticStackArray<CTFileName> _afnmArchives;
  183. // convert slashes to backslashes in a file path
  184. void ConvertSlashes(char *p)
  185. {
  186. while (*p!=0) {
  187. if (*p=='/') {
  188. *p = '\\';
  189. }
  190. p++;
  191. }
  192. }
  193. // read directory of a zip archive and add all files in it to active set
  194. void ReadZIPDirectory_t(CTFileName *pfnmZip)
  195. {
  196. FILE *f = fopen(*pfnmZip, "rb");
  197. if (f==NULL) {
  198. ThrowF_t(TRANS("%s: Cannot open file (%s)"), (CTString&)*pfnmZip, strerror(errno));
  199. }
  200. // start at the end of file, minus expected minimum overhead
  201. fseek(f, 0, SEEK_END);
  202. int iPos = ftell(f)-sizeof(long)-sizeof(EndOfDir)+2;
  203. // do not search more than 128k (should be around 65k at most)
  204. int iMinPos = iPos-128*1024;
  205. if (iMinPos<0) {
  206. iMinPos = 0;
  207. }
  208. EndOfDir eod;
  209. BOOL bEODFound = FALSE;
  210. // while not at beginning
  211. for(; iPos>iMinPos; iPos--) {
  212. // read signature
  213. fseek(f, iPos, SEEK_SET);
  214. int slSig;
  215. fread(&slSig, sizeof(slSig), 1, f);
  216. // if this is the sig
  217. if (slSig==SIGNATURE_EOD) {
  218. // read directory end
  219. fread(&eod, sizeof(eod), 1, f);
  220. // if multi-volume zip
  221. if (eod.eod_swDiskNo!=0||eod.eod_swDirStartDiskNo!=0
  222. ||eod.eod_swEntriesInDirOnThisDisk!=eod.eod_swEntriesInDir) {
  223. // fail
  224. ThrowF_t(TRANS("%s: Multi-volume zips are not supported"), (CTString&)*pfnmZip);
  225. }
  226. // check against empty zips
  227. if (eod.eod_swEntriesInDir<=0) {
  228. // fail
  229. ThrowF_t(TRANS("%s: Empty zip"), (CTString&)*pfnmZip);
  230. }
  231. // all ok
  232. bEODFound = TRUE;
  233. break;
  234. }
  235. }
  236. // if eod not found
  237. if (!bEODFound) {
  238. // fail
  239. ThrowF_t(TRANS("%s: Cannot find 'end of central directory'"), (CTString&)*pfnmZip);
  240. }
  241. // check if the zip is from a mod
  242. BOOL bMod =
  243. pfnmZip->HasPrefix(_fnmApplicationPath+"Mods\\") ||
  244. pfnmZip->HasPrefix(_fnmCDPath+"Mods\\");
  245. // go to the beginning of the central dir
  246. fseek(f, eod.eod_slDirOffsetInFile, SEEK_SET);
  247. INDEX ctFiles = 0;
  248. // for each file
  249. for (INDEX iFile=0; iFile<eod.eod_swEntriesInDir; iFile++) {
  250. // read the sig
  251. int slSig;
  252. fread(&slSig, sizeof(slSig), 1, f);
  253. // if this is not the expected sig
  254. if (slSig!=SIGNATURE_FH) {
  255. // fail
  256. ThrowF_t(TRANS("%s: Wrong signature for 'file header' number %d'"),
  257. (CTString&)*pfnmZip, iFile);
  258. }
  259. // read its header
  260. FileHeader fh;
  261. fread(&fh, sizeof(fh), 1, f);
  262. // read the filename
  263. const SLONG slMaxFileName = 512;
  264. char strBuffer[slMaxFileName+1];
  265. memset(strBuffer, 0, sizeof(strBuffer));
  266. if (fh.fh_swFileNameLen>slMaxFileName) {
  267. ThrowF_t(TRANS("%s: Too long filepath in zip"), (CTString&)*pfnmZip);
  268. }
  269. if (fh.fh_swFileNameLen<=0) {
  270. ThrowF_t(TRANS("%s: Invalid filepath length in zip"), (CTString&)*pfnmZip);
  271. }
  272. fread(strBuffer, fh.fh_swFileNameLen, 1, f);
  273. // skip eventual comment and extra fields
  274. if (fh.fh_swFileCommentLen+fh.fh_swExtraFieldLen>0) {
  275. fseek(f, fh.fh_swFileCommentLen+fh.fh_swExtraFieldLen, SEEK_CUR);
  276. }
  277. // if the file is directory
  278. if (strBuffer[strlen(strBuffer)-1]=='/') {
  279. // check size
  280. if (fh.fh_slUncompressedSize!=0
  281. ||fh.fh_slCompressedSize!=0) {
  282. ThrowF_t(TRANS("%s/%s: Invalid directory"),
  283. (CTString&)*pfnmZip, strBuffer);
  284. }
  285. // if the file is real file
  286. } else {
  287. ctFiles++;
  288. // convert filename
  289. ConvertSlashes(strBuffer);
  290. // create a new entry
  291. CZipEntry &ze = _azeFiles.Push();
  292. // remember the file's data
  293. ze.ze_fnm = CTString(strBuffer);
  294. ze.ze_pfnmArchive = pfnmZip;
  295. ze.ze_slCompressedSize = fh.fh_slCompressedSize;
  296. ze.ze_slUncompressedSize = fh.fh_slUncompressedSize;
  297. ze.ze_slDataOffset = fh.fh_slLocalHeaderOffset;
  298. ze.ze_ulCRC = fh.fh_slCRC32;
  299. ze.ze_bMod = bMod;
  300. // check for compressopn
  301. if (fh.fh_swCompressionMethod==0) {
  302. ze.ze_bStored = TRUE;
  303. } else if (fh.fh_swCompressionMethod==8) {
  304. ze.ze_bStored = FALSE;
  305. } else {
  306. ThrowF_t(TRANS("%s/%s: Only 'deflate' compression is supported"),
  307. (CTString&)*ze.ze_pfnmArchive, ze.ze_fnm);
  308. }
  309. }
  310. }
  311. // if error reading
  312. if (ferror(f)) {
  313. // fail
  314. ThrowF_t(TRANS("%s: Error reading central directory"), (CTString&)*pfnmZip);
  315. }
  316. // report that file was read
  317. CPrintF(TRANS(" %s: %d files\n"), (CTString&)*pfnmZip, ctFiles++);
  318. }
  319. // add one zip archive to current active set
  320. void UNZIPAddArchive(const CTFileName &fnm)
  321. {
  322. // remember its filename
  323. CTFileName &fnmNew = _afnmArchives.Push();
  324. fnmNew = fnm;
  325. }
  326. // read directory of an archive
  327. void ReadOneArchiveDir_t(CTFileName &fnm)
  328. {
  329. // remember current number of files
  330. INDEX ctOrgFiles = _azeFiles.Count();
  331. // try to
  332. try {
  333. // read the directory and add all files
  334. ReadZIPDirectory_t(&fnm);
  335. // if failed
  336. } catch (char *) {
  337. // if some files were added
  338. if (ctOrgFiles<_azeFiles.Count()) {
  339. // remove them
  340. if (ctOrgFiles==0) {
  341. _azeFiles.PopAll();
  342. } else {
  343. _azeFiles.PopUntil(ctOrgFiles-1);
  344. }
  345. }
  346. // cascade the error
  347. throw;
  348. }
  349. }
  350. int qsort_ArchiveCTFileName_reverse(const void *elem1, const void *elem2 )
  351. {
  352. // get the filenames
  353. const CTFileName &fnm1 = *(CTFileName *)elem1;
  354. const CTFileName &fnm2 = *(CTFileName *)elem2;
  355. // find if any is in a mod or on CD
  356. BOOL bMod1 = fnm1.HasPrefix(_fnmApplicationPath+"Mods\\");
  357. BOOL bCD1 = fnm1.HasPrefix(_fnmCDPath);
  358. BOOL bModCD1 = fnm1.HasPrefix(_fnmCDPath+"Mods\\");
  359. BOOL bMod2 = fnm2.HasPrefix(_fnmApplicationPath+"Mods\\");
  360. BOOL bCD2 = fnm2.HasPrefix(_fnmCDPath);
  361. BOOL bModCD2 = fnm2.HasPrefix(_fnmCDPath+"Mods\\");
  362. // calculate priorities based on location of gro file
  363. INDEX iPriority1 = 0;
  364. if (bMod1) {
  365. iPriority1 = 3;
  366. } else if (bModCD1) {
  367. iPriority1 = 2;
  368. } else if (bCD1) {
  369. iPriority1 = 0;
  370. } else {
  371. iPriority1 = 1;
  372. }
  373. INDEX iPriority2 = 0;
  374. if (bMod2) {
  375. iPriority2 = 3;
  376. } else if (bModCD2) {
  377. iPriority2 = 2;
  378. } else if (bCD2) {
  379. iPriority2 = 0;
  380. } else {
  381. iPriority2 = 1;
  382. }
  383. // find sorting order
  384. if (iPriority1<iPriority2) {
  385. return +1;
  386. } else if (iPriority1>iPriority2) {
  387. return -1;
  388. } else {
  389. return -stricmp(fnm1, fnm2);
  390. }
  391. }
  392. // read directories of all currently added archives, in reverse alphabetical order
  393. void UNZIPReadDirectoriesReverse_t(void)
  394. {
  395. // if no archives
  396. if (_afnmArchives.Count()==0) {
  397. // do nothing
  398. return;
  399. }
  400. // sort the archive filenames reversely
  401. qsort(&_afnmArchives[0], _afnmArchives.Count(), sizeof(CTFileName),
  402. qsort_ArchiveCTFileName_reverse);
  403. CTString strAllErrors = "";
  404. // for each archive
  405. for (INDEX iArchive=0; iArchive<_afnmArchives.Count(); iArchive++) {
  406. //try to
  407. try {
  408. // read its directory
  409. ReadOneArchiveDir_t(_afnmArchives[iArchive]);
  410. // if failed
  411. } catch (char *strError) {
  412. // remember the error
  413. strAllErrors += strError;
  414. strAllErrors += "\n";
  415. }
  416. }
  417. // if there were errors
  418. if (strAllErrors!="") {
  419. // report them
  420. ThrowF_t("%s", strAllErrors);
  421. }
  422. }
  423. // check if a zip file entry exists
  424. BOOL UNZIPFileExists(const CTFileName &fnm)
  425. {
  426. // for each file
  427. for(INDEX iFile=0; iFile<_azeFiles.Count(); iFile++) {
  428. // if it is that one
  429. if (_azeFiles[iFile].ze_fnm == fnm) {
  430. return TRUE;
  431. }
  432. }
  433. return FALSE;
  434. }
  435. // enumeration for all files in all zips
  436. INDEX UNZIPGetFileCount(void)
  437. {
  438. return _azeFiles.Count();
  439. }
  440. const CTFileName &UNZIPGetFileAtIndex(INDEX i)
  441. {
  442. return _azeFiles[i].ze_fnm;
  443. }
  444. BOOL UNZIPIsFileAtIndexMod(INDEX i)
  445. {
  446. return _azeFiles[i].ze_bMod;
  447. }
  448. // get index of a file (-1 for no file)
  449. INDEX UNZIPGetFileIndex(const CTFileName &fnm)
  450. {
  451. // for each file
  452. for(INDEX iFile=0; iFile<_azeFiles.Count(); iFile++) {
  453. // if it is that one
  454. if (_azeFiles[iFile].ze_fnm == fnm) {
  455. return iFile;
  456. }
  457. }
  458. return -1;
  459. }
  460. // get info on a zip file entry
  461. void UNZIPGetFileInfo(INDEX iHandle, CTFileName &fnmZip,
  462. SLONG &slOffset, SLONG &slSizeCompressed, SLONG &slSizeUncompressed,
  463. BOOL &bCompressed)
  464. {
  465. // check handle number
  466. if(iHandle<0 || iHandle>=_azhHandles.Count()) {
  467. ASSERT(FALSE);
  468. return;
  469. }
  470. // get the handle
  471. CZipHandle &zh = _azhHandles[iHandle];
  472. // check the handle
  473. if (!zh.zh_bOpen) {
  474. ASSERT(FALSE);
  475. return;
  476. }
  477. // get parameters
  478. fnmZip = *zh.zh_zeEntry.ze_pfnmArchive;
  479. bCompressed = !zh.zh_zeEntry.ze_bStored;
  480. slOffset = zh.zh_zeEntry.ze_slDataOffset;
  481. slSizeCompressed = zh.zh_zeEntry.ze_slCompressedSize;
  482. slSizeUncompressed = zh.zh_zeEntry.ze_slUncompressedSize;
  483. }
  484. // open a zip file entry for reading
  485. INDEX UNZIPOpen_t(const CTFileName &fnm)
  486. {
  487. CZipEntry *pze = NULL;
  488. // for each file
  489. for(INDEX iFile=0; iFile<_azeFiles.Count(); iFile++) {
  490. // if it is that one
  491. if (_azeFiles[iFile].ze_fnm == fnm) {
  492. // stop searching
  493. pze = &_azeFiles[iFile];
  494. break;
  495. }
  496. }
  497. // if not found
  498. if (pze==NULL) {
  499. // fail
  500. ThrowF_t(TRANS("File not found: %s"), (const CTString&)fnm);
  501. }
  502. // for each existing handle
  503. BOOL bHandleFound = FALSE;
  504. INDEX iHandle=1;
  505. for (; iHandle<_azhHandles.Count(); iHandle++) {
  506. // if unused
  507. if (!_azhHandles[iHandle].zh_bOpen) {
  508. // use that one
  509. bHandleFound = TRUE;
  510. break;
  511. }
  512. }
  513. // if no free handle found
  514. if (!bHandleFound) {
  515. // create a new one
  516. iHandle = _azhHandles.Count();
  517. _azhHandles.Push(1);
  518. }
  519. // get the handle
  520. CZipHandle &zh = _azhHandles[iHandle];
  521. ASSERT(!zh.zh_bOpen);
  522. zh.zh_zeEntry = *pze;
  523. // open zip archive for reading
  524. zh.zh_fFile = fopen(*pze->ze_pfnmArchive, "rb");
  525. // if failed to open it
  526. if (zh.zh_fFile==NULL) {
  527. // clear the handle
  528. zh.Clear();
  529. // fail
  530. ThrowF_t(TRANS("Cannot open '%s': %s"), (const CTString&)*pze->ze_pfnmArchive,
  531. strerror(errno));
  532. }
  533. // seek to the local header of the entry
  534. fseek(zh.zh_fFile, zh.zh_zeEntry.ze_slDataOffset, SEEK_SET);
  535. // read the sig
  536. int slSig;
  537. fread(&slSig, sizeof(slSig), 1, zh.zh_fFile);
  538. // if this is not the expected sig
  539. if (slSig!=SIGNATURE_LFH) {
  540. // fail
  541. ThrowF_t(TRANS("%s/%s: Wrong signature for 'local file header'"),
  542. (CTString&)*zh.zh_zeEntry.ze_pfnmArchive, zh.zh_zeEntry.ze_fnm);
  543. }
  544. // read the header
  545. LocalFileHeader lfh;
  546. fread(&lfh, sizeof(lfh), 1, zh.zh_fFile);
  547. // determine exact compressed data position
  548. zh.zh_zeEntry.ze_slDataOffset =
  549. ftell(zh.zh_fFile)+lfh.lfh_swFileNameLen+lfh.lfh_swExtraFieldLen;
  550. // seek there
  551. fseek(zh.zh_fFile, zh.zh_zeEntry.ze_slDataOffset, SEEK_SET);
  552. // allocate buffers
  553. zh.zh_pubBufIn = (UBYTE*)AllocMemory(BUF_SIZE);
  554. // initialize zlib stream
  555. CTSingleLock slZip(&zip_csLock, TRUE);
  556. zh.zh_zstream.next_out = NULL;
  557. zh.zh_zstream.avail_out = 0;
  558. zh.zh_zstream.next_in = NULL;
  559. zh.zh_zstream.avail_in = 0;
  560. zh.zh_zstream.zalloc = (alloc_func)Z_NULL;
  561. zh.zh_zstream.zfree = (free_func)Z_NULL;
  562. int err = inflateInit2(&zh.zh_zstream, -15); // 32k windows
  563. // if failed
  564. if (err!=Z_OK) {
  565. // clean up what is possible
  566. FreeMemory(zh.zh_pubBufIn );
  567. zh.zh_pubBufIn = NULL;
  568. fclose(zh.zh_fFile);
  569. zh.zh_fFile = NULL;
  570. // throw error
  571. zh.ThrowZLIBError_t(err, TRANS("Cannot init inflation"));
  572. }
  573. // return the handle successfully
  574. zh.zh_bOpen = TRUE;
  575. return iHandle;
  576. }
  577. // get uncompressed size of a file
  578. SLONG UNZIPGetSize(INDEX iHandle)
  579. {
  580. // check handle number
  581. if(iHandle<0 || iHandle>=_azhHandles.Count()) {
  582. ASSERT(FALSE);
  583. return 0;
  584. }
  585. // get the handle
  586. CZipHandle &zh = _azhHandles[iHandle];
  587. // check the handle
  588. if (!zh.zh_bOpen) {
  589. ASSERT(FALSE);
  590. return 0;
  591. }
  592. return zh.zh_zeEntry.ze_slUncompressedSize;
  593. }
  594. // get CRC of a file
  595. ULONG UNZIPGetCRC(INDEX iHandle)
  596. {
  597. // check handle number
  598. if(iHandle<0 || iHandle>=_azhHandles.Count()) {
  599. ASSERT(FALSE);
  600. return 0;
  601. }
  602. // get the handle
  603. CZipHandle &zh = _azhHandles[iHandle];
  604. // check the handle
  605. if (!zh.zh_bOpen) {
  606. ASSERT(FALSE);
  607. return 0;
  608. }
  609. return zh.zh_zeEntry.ze_ulCRC;
  610. }
  611. // read a block from zip file
  612. void UNZIPReadBlock_t(INDEX iHandle, UBYTE *pub, SLONG slStart, SLONG slLen)
  613. {
  614. // check handle number
  615. if(iHandle<0 || iHandle>=_azhHandles.Count()) {
  616. ASSERT(FALSE);
  617. return;
  618. }
  619. // get the handle
  620. CZipHandle &zh = _azhHandles[iHandle];
  621. // check the handle
  622. if (!zh.zh_bOpen) {
  623. ASSERT(FALSE);
  624. return;
  625. }
  626. // if behind the end of file
  627. if (slStart>=zh.zh_zeEntry.ze_slUncompressedSize) {
  628. // do nothing
  629. return;
  630. }
  631. // clamp length to end of the entry data
  632. slLen = Min(slLen, zh.zh_zeEntry.ze_slUncompressedSize-slStart);
  633. // if not compressed
  634. if (zh.zh_zeEntry.ze_bStored) {
  635. // just read from file
  636. fseek(zh.zh_fFile, zh.zh_zeEntry.ze_slDataOffset+slStart, SEEK_SET);
  637. fread(pub, 1, slLen, zh.zh_fFile);
  638. return;
  639. }
  640. CTSingleLock slZip(&zip_csLock, TRUE);
  641. // if behind the current pointer
  642. if (slStart<zh.zh_zstream.total_out) {
  643. // reset the zlib stream to beginning
  644. inflateReset(&zh.zh_zstream);
  645. zh.zh_zstream.avail_in = 0;
  646. zh.zh_zstream.next_in = NULL;
  647. // seek to start of zip entry data inside archive
  648. fseek(zh.zh_fFile, zh.zh_zeEntry.ze_slDataOffset, SEEK_SET);
  649. }
  650. // while ahead of the current pointer
  651. while (slStart>zh.zh_zstream.total_out) {
  652. // if zlib has no more input
  653. while(zh.zh_zstream.avail_in==0) {
  654. // read more to it
  655. SLONG slRead = fread(zh.zh_pubBufIn, 1, BUF_SIZE, zh.zh_fFile);
  656. if (slRead<=0) {
  657. return; // !!!!
  658. }
  659. // tell zlib that there is more to read
  660. zh.zh_zstream.next_in = zh.zh_pubBufIn;
  661. zh.zh_zstream.avail_in = slRead;
  662. }
  663. // read dummy data from the output
  664. #define DUMMY_SIZE 256
  665. UBYTE aubDummy[DUMMY_SIZE];
  666. // decode to output
  667. zh.zh_zstream.avail_out = Min(SLONG(slStart-zh.zh_zstream.total_out), SLONG(DUMMY_SIZE));
  668. zh.zh_zstream.next_out = aubDummy;
  669. int ierr = inflate(&zh.zh_zstream, Z_SYNC_FLUSH);
  670. if (ierr!=Z_OK && ierr!=Z_STREAM_END) {
  671. zh.ThrowZLIBError_t(ierr, TRANS("Error seeking in zip"));
  672. }
  673. }
  674. // if not streaming continuously
  675. if (slStart!=zh.zh_zstream.total_out) {
  676. // this should not happen
  677. ASSERT(FALSE);
  678. // read empty
  679. memset(pub, 0, slLen);
  680. return;
  681. }
  682. // set zlib for writing to the block
  683. zh.zh_zstream.avail_out = slLen;
  684. zh.zh_zstream.next_out = pub;
  685. // while there is something to write to given block
  686. while (zh.zh_zstream.avail_out>0) {
  687. // if zlib has no more input
  688. while(zh.zh_zstream.avail_in==0) {
  689. // read more to it
  690. SLONG slRead = fread(zh.zh_pubBufIn, 1, BUF_SIZE, zh.zh_fFile);
  691. if (slRead<=0) {
  692. return; // !!!!
  693. }
  694. // tell zlib that there is more to read
  695. zh.zh_zstream.next_in = zh.zh_pubBufIn;
  696. zh.zh_zstream.avail_in = slRead;
  697. }
  698. // decode to output
  699. int ierr = inflate(&zh.zh_zstream, Z_SYNC_FLUSH);
  700. if (ierr!=Z_OK && ierr!=Z_STREAM_END) {
  701. zh.ThrowZLIBError_t(ierr, TRANS("Error reading from zip"));
  702. }
  703. }
  704. }
  705. // close a zip file entry
  706. void UNZIPClose(INDEX iHandle)
  707. {
  708. // check handle number
  709. if(iHandle<0 || iHandle>=_azhHandles.Count()) {
  710. ASSERT(FALSE);
  711. return;
  712. }
  713. // get the handle
  714. CZipHandle &zh = _azhHandles[iHandle];
  715. // check the handle
  716. if (!zh.zh_bOpen) {
  717. ASSERT(FALSE);
  718. return;
  719. }
  720. // clear it
  721. zh.Clear();
  722. }