CZipReader.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. // Copyright (C) 2002-2012 Nikolaus Gebhardt
  2. // This file is part of the "Irrlicht Engine".
  3. // For conditions of distribution and use, see copyright notice in irrlicht.h
  4. #include "CZipReader.h"
  5. #include "os.h"
  6. #include "CFileList.h"
  7. #include "CReadFile.h"
  8. #include "coreutil.h"
  9. #include <zlib.h> // use system lib
  10. namespace irr
  11. {
  12. namespace io
  13. {
  14. // -----------------------------------------------------------------------------
  15. // zip loader
  16. // -----------------------------------------------------------------------------
  17. //! Constructor
  18. CArchiveLoaderZIP::CArchiveLoaderZIP(io::IFileSystem *fs) :
  19. FileSystem(fs)
  20. {}
  21. //! returns true if the file maybe is able to be loaded by this class
  22. bool CArchiveLoaderZIP::isALoadableFileFormat(const io::path &filename) const
  23. {
  24. return core::hasFileExtension(filename, "zip", "pk3") ||
  25. core::hasFileExtension(filename, "gz", "tgz");
  26. }
  27. //! Check to see if the loader can create archives of this type.
  28. bool CArchiveLoaderZIP::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const
  29. {
  30. return (fileType == EFAT_ZIP || fileType == EFAT_GZIP);
  31. }
  32. //! Creates an archive from the filename
  33. /** \param file File handle to check.
  34. \return Pointer to newly created archive, or 0 upon error. */
  35. IFileArchive *CArchiveLoaderZIP::createArchive(const io::path &filename, bool ignoreCase, bool ignorePaths) const
  36. {
  37. IFileArchive *archive = 0;
  38. io::IReadFile *file = FileSystem->createAndOpenFile(filename);
  39. if (file) {
  40. archive = createArchive(file, ignoreCase, ignorePaths);
  41. file->drop();
  42. }
  43. return archive;
  44. }
  45. //! creates/loads an archive from the file.
  46. //! \return Pointer to the created archive. Returns 0 if loading failed.
  47. IFileArchive *CArchiveLoaderZIP::createArchive(io::IReadFile *file, bool ignoreCase, bool ignorePaths) const
  48. {
  49. IFileArchive *archive = 0;
  50. if (file) {
  51. file->seek(0);
  52. u16 sig;
  53. file->read(&sig, 2);
  54. #ifdef __BIG_ENDIAN__
  55. sig = os::Byteswap::byteswap(sig);
  56. #endif
  57. file->seek(0);
  58. bool isGZip = (sig == 0x8b1f);
  59. archive = new CZipReader(FileSystem, file, ignoreCase, ignorePaths, isGZip);
  60. }
  61. return archive;
  62. }
  63. //! Check if the file might be loaded by this class
  64. /** Check might look into the file.
  65. \param file File handle to check.
  66. \return True if file seems to be loadable. */
  67. bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile *file) const
  68. {
  69. SZIPFileHeader header;
  70. file->read(&header.Sig, 4);
  71. #ifdef __BIG_ENDIAN__
  72. header.Sig = os::Byteswap::byteswap(header.Sig);
  73. #endif
  74. return header.Sig == 0x04034b50 || // ZIP
  75. (header.Sig & 0xffff) == 0x8b1f; // gzip
  76. }
  77. // -----------------------------------------------------------------------------
  78. // zip archive
  79. // -----------------------------------------------------------------------------
  80. CZipReader::CZipReader(IFileSystem *fs, IReadFile *file, bool ignoreCase, bool ignorePaths, bool isGZip) :
  81. CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), FileSystem(fs), File(file), IsGZip(isGZip)
  82. {
  83. if (File) {
  84. File->grab();
  85. // load file entries
  86. if (IsGZip)
  87. while (scanGZipHeader()) {
  88. }
  89. else
  90. while (scanZipHeader()) {
  91. }
  92. sort();
  93. }
  94. }
  95. CZipReader::~CZipReader()
  96. {
  97. if (File)
  98. File->drop();
  99. }
  100. //! get the archive type
  101. E_FILE_ARCHIVE_TYPE CZipReader::getType() const
  102. {
  103. return IsGZip ? EFAT_GZIP : EFAT_ZIP;
  104. }
  105. const IFileList *CZipReader::getFileList() const
  106. {
  107. return this;
  108. }
  109. //! scans for a local header, returns false if there is no more local file header.
  110. //! The gzip file format seems to think that there can be multiple files in a gzip file
  111. //! but none
  112. bool CZipReader::scanGZipHeader()
  113. {
  114. SZipFileEntry entry;
  115. entry.Offset = 0;
  116. memset(&entry.header, 0, sizeof(SZIPFileHeader));
  117. // read header
  118. SGZIPMemberHeader header;
  119. if (File->read(&header, sizeof(SGZIPMemberHeader)) == sizeof(SGZIPMemberHeader)) {
  120. #ifdef __BIG_ENDIAN__
  121. header.sig = os::Byteswap::byteswap(header.sig);
  122. header.time = os::Byteswap::byteswap(header.time);
  123. #endif
  124. // check header value
  125. if (header.sig != 0x8b1f)
  126. return false;
  127. // now get the file info
  128. if (header.flags & EGZF_EXTRA_FIELDS) {
  129. // read lenth of extra data
  130. u16 dataLen;
  131. File->read(&dataLen, 2);
  132. #ifdef __BIG_ENDIAN__
  133. dataLen = os::Byteswap::byteswap(dataLen);
  134. #endif
  135. // skip it
  136. File->seek(dataLen, true);
  137. }
  138. io::path ZipFileName = "";
  139. if (header.flags & EGZF_FILE_NAME) {
  140. c8 c;
  141. File->read(&c, 1);
  142. while (c) {
  143. ZipFileName.append(c);
  144. File->read(&c, 1);
  145. }
  146. } else {
  147. // no file name?
  148. ZipFileName = core::deletePathFromFilename(Path);
  149. // rename tgz to tar or remove gz extension
  150. if (core::hasFileExtension(ZipFileName, "tgz")) {
  151. ZipFileName[ZipFileName.size() - 2] = 'a';
  152. ZipFileName[ZipFileName.size() - 1] = 'r';
  153. } else if (core::hasFileExtension(ZipFileName, "gz")) {
  154. ZipFileName[ZipFileName.size() - 3] = 0;
  155. ZipFileName.validate();
  156. }
  157. }
  158. if (header.flags & EGZF_COMMENT) {
  159. c8 c = 'a';
  160. while (c)
  161. File->read(&c, 1);
  162. }
  163. if (header.flags & EGZF_CRC16)
  164. File->seek(2, true);
  165. // we are now at the start of the data blocks
  166. entry.Offset = File->getPos();
  167. entry.header.FilenameLength = ZipFileName.size();
  168. entry.header.CompressionMethod = header.compressionMethod;
  169. entry.header.DataDescriptor.CompressedSize = (File->getSize() - 8) - File->getPos();
  170. // seek to file end
  171. File->seek(entry.header.DataDescriptor.CompressedSize, true);
  172. // read CRC
  173. File->read(&entry.header.DataDescriptor.CRC32, 4);
  174. // read uncompressed size
  175. File->read(&entry.header.DataDescriptor.UncompressedSize, 4);
  176. #ifdef __BIG_ENDIAN__
  177. entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
  178. entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
  179. #endif
  180. // now we've filled all the fields, this is just a standard deflate block
  181. addItem(ZipFileName, entry.Offset, entry.header.DataDescriptor.UncompressedSize, false, 0);
  182. FileInfo.push_back(entry);
  183. }
  184. // there's only one block of data in a gzip file
  185. return false;
  186. }
  187. //! scans for a local header, returns false if there is no more local file header.
  188. bool CZipReader::scanZipHeader(bool ignoreGPBits)
  189. {
  190. io::path ZipFileName = "";
  191. SZipFileEntry entry;
  192. entry.Offset = 0;
  193. memset(&entry.header, 0, sizeof(SZIPFileHeader));
  194. File->read(&entry.header, sizeof(SZIPFileHeader));
  195. #ifdef __BIG_ENDIAN__
  196. entry.header.Sig = os::Byteswap::byteswap(entry.header.Sig);
  197. entry.header.VersionToExtract = os::Byteswap::byteswap(entry.header.VersionToExtract);
  198. entry.header.GeneralBitFlag = os::Byteswap::byteswap(entry.header.GeneralBitFlag);
  199. entry.header.CompressionMethod = os::Byteswap::byteswap(entry.header.CompressionMethod);
  200. entry.header.LastModFileTime = os::Byteswap::byteswap(entry.header.LastModFileTime);
  201. entry.header.LastModFileDate = os::Byteswap::byteswap(entry.header.LastModFileDate);
  202. entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32);
  203. entry.header.DataDescriptor.CompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.CompressedSize);
  204. entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize);
  205. entry.header.FilenameLength = os::Byteswap::byteswap(entry.header.FilenameLength);
  206. entry.header.ExtraFieldLength = os::Byteswap::byteswap(entry.header.ExtraFieldLength);
  207. #endif
  208. if (entry.header.Sig != 0x04034b50)
  209. return false; // local file headers end here.
  210. // read filename
  211. {
  212. c8 *tmp = new c8[entry.header.FilenameLength + 2];
  213. File->read(tmp, entry.header.FilenameLength);
  214. tmp[entry.header.FilenameLength] = 0;
  215. ZipFileName = tmp;
  216. delete[] tmp;
  217. }
  218. if (entry.header.ExtraFieldLength)
  219. File->seek(entry.header.ExtraFieldLength, true);
  220. // if bit 3 was set, use CentralDirectory for setup
  221. if (!ignoreGPBits && entry.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRIPTOR) {
  222. SZIPFileCentralDirEnd dirEnd;
  223. FileInfo.clear();
  224. Files.clear();
  225. // First place where the end record could be stored
  226. File->seek(File->getSize() - 22);
  227. const char endID[] = {0x50, 0x4b, 0x05, 0x06, 0x0};
  228. char tmp[5] = {'\0'};
  229. bool found = false;
  230. // search for the end record ID
  231. while (!found && File->getPos() > 0) {
  232. int seek = 8;
  233. File->read(tmp, 4);
  234. switch (tmp[0]) {
  235. case 0x50:
  236. if (!strcmp(endID, tmp)) {
  237. seek = 4;
  238. found = true;
  239. }
  240. break;
  241. case 0x4b:
  242. seek = 5;
  243. break;
  244. case 0x05:
  245. seek = 6;
  246. break;
  247. case 0x06:
  248. seek = 7;
  249. break;
  250. }
  251. File->seek(-seek, true);
  252. }
  253. File->read(&dirEnd, sizeof(dirEnd));
  254. #ifdef __BIG_ENDIAN__
  255. dirEnd.NumberDisk = os::Byteswap::byteswap(dirEnd.NumberDisk);
  256. dirEnd.NumberStart = os::Byteswap::byteswap(dirEnd.NumberStart);
  257. dirEnd.TotalDisk = os::Byteswap::byteswap(dirEnd.TotalDisk);
  258. dirEnd.TotalEntries = os::Byteswap::byteswap(dirEnd.TotalEntries);
  259. dirEnd.Size = os::Byteswap::byteswap(dirEnd.Size);
  260. dirEnd.Offset = os::Byteswap::byteswap(dirEnd.Offset);
  261. dirEnd.CommentLength = os::Byteswap::byteswap(dirEnd.CommentLength);
  262. #endif
  263. FileInfo.reserve(dirEnd.TotalEntries);
  264. File->seek(dirEnd.Offset);
  265. while (scanCentralDirectoryHeader()) {
  266. }
  267. return false;
  268. }
  269. // store position in file
  270. entry.Offset = File->getPos();
  271. // move forward length of data
  272. File->seek(entry.header.DataDescriptor.CompressedSize, true);
  273. #ifdef _DEBUG
  274. // os::Debuginfo::print("added file from archive", ZipFileName.c_str());
  275. #endif
  276. addItem(ZipFileName, entry.Offset, entry.header.DataDescriptor.UncompressedSize, ZipFileName.lastChar() == '/', FileInfo.size());
  277. FileInfo.push_back(entry);
  278. return true;
  279. }
  280. //! scans for a local header, returns false if there is no more local file header.
  281. bool CZipReader::scanCentralDirectoryHeader()
  282. {
  283. io::path ZipFileName = "";
  284. SZIPFileCentralDirFileHeader entry;
  285. File->read(&entry, sizeof(SZIPFileCentralDirFileHeader));
  286. #ifdef __BIG_ENDIAN__
  287. entry.Sig = os::Byteswap::byteswap(entry.Sig);
  288. entry.VersionMadeBy = os::Byteswap::byteswap(entry.VersionMadeBy);
  289. entry.VersionToExtract = os::Byteswap::byteswap(entry.VersionToExtract);
  290. entry.GeneralBitFlag = os::Byteswap::byteswap(entry.GeneralBitFlag);
  291. entry.CompressionMethod = os::Byteswap::byteswap(entry.CompressionMethod);
  292. entry.LastModFileTime = os::Byteswap::byteswap(entry.LastModFileTime);
  293. entry.LastModFileDate = os::Byteswap::byteswap(entry.LastModFileDate);
  294. entry.CRC32 = os::Byteswap::byteswap(entry.CRC32);
  295. entry.CompressedSize = os::Byteswap::byteswap(entry.CompressedSize);
  296. entry.UncompressedSize = os::Byteswap::byteswap(entry.UncompressedSize);
  297. entry.FilenameLength = os::Byteswap::byteswap(entry.FilenameLength);
  298. entry.ExtraFieldLength = os::Byteswap::byteswap(entry.ExtraFieldLength);
  299. entry.FileCommentLength = os::Byteswap::byteswap(entry.FileCommentLength);
  300. entry.DiskNumberStart = os::Byteswap::byteswap(entry.DiskNumberStart);
  301. entry.InternalFileAttributes = os::Byteswap::byteswap(entry.InternalFileAttributes);
  302. entry.ExternalFileAttributes = os::Byteswap::byteswap(entry.ExternalFileAttributes);
  303. entry.RelativeOffsetOfLocalHeader = os::Byteswap::byteswap(entry.RelativeOffsetOfLocalHeader);
  304. #endif
  305. if (entry.Sig != 0x02014b50)
  306. return false; // central dir headers end here.
  307. const long pos = File->getPos();
  308. File->seek(entry.RelativeOffsetOfLocalHeader);
  309. scanZipHeader(true);
  310. File->seek(pos + entry.FilenameLength + entry.ExtraFieldLength + entry.FileCommentLength);
  311. auto &lastInfo = FileInfo.back();
  312. lastInfo.header.DataDescriptor.CompressedSize = entry.CompressedSize;
  313. lastInfo.header.DataDescriptor.UncompressedSize = entry.UncompressedSize;
  314. lastInfo.header.DataDescriptor.CRC32 = entry.CRC32;
  315. Files.getLast().Size = entry.UncompressedSize;
  316. return true;
  317. }
  318. //! opens a file by file name
  319. IReadFile *CZipReader::createAndOpenFile(const io::path &filename)
  320. {
  321. s32 index = findFile(filename, false);
  322. if (index != -1)
  323. return createAndOpenFile(index);
  324. return 0;
  325. }
  326. //! opens a file by index
  327. IReadFile *CZipReader::createAndOpenFile(u32 index)
  328. {
  329. // Irrlicht supports 0, 8, 12, 14, 99
  330. // 0 - The file is stored (no compression)
  331. // 1 - The file is Shrunk
  332. // 2 - The file is Reduced with compression factor 1
  333. // 3 - The file is Reduced with compression factor 2
  334. // 4 - The file is Reduced with compression factor 3
  335. // 5 - The file is Reduced with compression factor 4
  336. // 6 - The file is Imploded
  337. // 7 - Reserved for Tokenizing compression algorithm
  338. // 8 - The file is Deflated
  339. // 9 - Reserved for enhanced Deflating
  340. // 10 - PKWARE Date Compression Library Imploding
  341. // 12 - bzip2 - Compression Method from libbz2, WinZip 10
  342. // 14 - LZMA - Compression Method, WinZip 12
  343. // 96 - Jpeg compression - Compression Method, WinZip 12
  344. // 97 - WavPack - Compression Method, WinZip 11
  345. // 98 - PPMd - Compression Method, WinZip 10
  346. // 99 - AES encryption, WinZip 9
  347. const SZipFileEntry &e = FileInfo[Files[index].ID];
  348. char buf[64];
  349. s16 actualCompressionMethod = e.header.CompressionMethod;
  350. IReadFile *decrypted = 0;
  351. u8 *decryptedBuf = 0;
  352. u32 decryptedSize = e.header.DataDescriptor.CompressedSize;
  353. switch (actualCompressionMethod) {
  354. case 0: // no compression
  355. {
  356. if (decrypted)
  357. return decrypted;
  358. else
  359. return createLimitReadFile(Files[index].FullName, File, e.Offset, decryptedSize);
  360. }
  361. case 8: {
  362. const u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize;
  363. c8 *pBuf = new c8[uncompressedSize];
  364. if (!pBuf) {
  365. snprintf_irr(buf, 64, "Not enough memory for decompressing %s", Files[index].FullName.c_str());
  366. os::Printer::log(buf, ELL_ERROR);
  367. if (decrypted)
  368. decrypted->drop();
  369. return 0;
  370. }
  371. u8 *pcData = decryptedBuf;
  372. if (!pcData) {
  373. pcData = new u8[decryptedSize];
  374. if (!pcData) {
  375. snprintf_irr(buf, 64, "Not enough memory for decompressing %s", Files[index].FullName.c_str());
  376. os::Printer::log(buf, ELL_ERROR);
  377. delete[] pBuf;
  378. return 0;
  379. }
  380. // memset(pcData, 0, decryptedSize);
  381. File->seek(e.Offset);
  382. File->read(pcData, decryptedSize);
  383. }
  384. // Setup the inflate stream.
  385. z_stream stream;
  386. s32 err;
  387. stream.next_in = (Bytef *)pcData;
  388. stream.avail_in = (uInt)decryptedSize;
  389. stream.next_out = (Bytef *)pBuf;
  390. stream.avail_out = uncompressedSize;
  391. stream.zalloc = (alloc_func)0;
  392. stream.zfree = (free_func)0;
  393. // Perform inflation. wbits < 0 indicates no zlib header inside the data.
  394. err = inflateInit2(&stream, -MAX_WBITS);
  395. if (err == Z_OK) {
  396. err = inflate(&stream, Z_FINISH);
  397. inflateEnd(&stream);
  398. if (err == Z_STREAM_END)
  399. err = Z_OK;
  400. err = Z_OK;
  401. inflateEnd(&stream);
  402. }
  403. if (decrypted)
  404. decrypted->drop();
  405. else
  406. delete[] pcData;
  407. if (err != Z_OK) {
  408. snprintf_irr(buf, 64, "Error decompressing %s", Files[index].FullName.c_str());
  409. os::Printer::log(buf, ELL_ERROR);
  410. delete[] pBuf;
  411. return 0;
  412. } else
  413. return FileSystem->createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true);
  414. }
  415. case 12: {
  416. os::Printer::log("bzip2 decompression not supported. File cannot be read.", ELL_ERROR);
  417. return 0;
  418. }
  419. case 14: {
  420. os::Printer::log("lzma decompression not supported. File cannot be read.", ELL_ERROR);
  421. return 0;
  422. }
  423. case 99:
  424. // If we come here with an encrypted file, decryption support is missing
  425. os::Printer::log("Decryption support not enabled. File cannot be read.", ELL_ERROR);
  426. return 0;
  427. default:
  428. snprintf_irr(buf, 64, "file has unsupported compression method. %s", Files[index].FullName.c_str());
  429. os::Printer::log(buf, ELL_ERROR);
  430. return 0;
  431. };
  432. }
  433. } // end namespace io
  434. } // end namespace irr