diff options
Diffstat (limited to 'libraries/irrlicht-1.8/source/Irrlicht/CZipReader.cpp')
-rw-r--r-- | libraries/irrlicht-1.8/source/Irrlicht/CZipReader.cpp | 839 |
1 files changed, 839 insertions, 0 deletions
diff --git a/libraries/irrlicht-1.8/source/Irrlicht/CZipReader.cpp b/libraries/irrlicht-1.8/source/Irrlicht/CZipReader.cpp new file mode 100644 index 0000000..be29f98 --- /dev/null +++ b/libraries/irrlicht-1.8/source/Irrlicht/CZipReader.cpp | |||
@@ -0,0 +1,839 @@ | |||
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 | |||
5 | #include "CZipReader.h" | ||
6 | |||
7 | #include "os.h" | ||
8 | |||
9 | // This method is used for error output from bzip2. | ||
10 | extern "C" void bz_internal_error(int errorCode) | ||
11 | { | ||
12 | irr::os::Printer::log("Error in bzip2 handling", irr::core::stringc(errorCode), irr::ELL_ERROR); | ||
13 | } | ||
14 | |||
15 | #ifdef __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_ | ||
16 | |||
17 | #include "CFileList.h" | ||
18 | #include "CReadFile.h" | ||
19 | #include "coreutil.h" | ||
20 | |||
21 | #include "IrrCompileConfig.h" | ||
22 | #ifdef _IRR_COMPILE_WITH_ZLIB_ | ||
23 | #ifndef _IRR_USE_NON_SYSTEM_ZLIB_ | ||
24 | #include <zlib.h> // use system lib | ||
25 | #else | ||
26 | #include "zlib/zlib.h" | ||
27 | #endif | ||
28 | |||
29 | #ifdef _IRR_COMPILE_WITH_ZIP_ENCRYPTION_ | ||
30 | #include "aesGladman/fileenc.h" | ||
31 | #endif | ||
32 | #ifdef _IRR_COMPILE_WITH_BZIP2_ | ||
33 | #ifndef _IRR_USE_NON_SYSTEM_BZLIB_ | ||
34 | #include <bzlib.h> | ||
35 | #else | ||
36 | #include "bzip2/bzlib.h" | ||
37 | #endif | ||
38 | #endif | ||
39 | #ifdef _IRR_COMPILE_WITH_LZMA_ | ||
40 | #include "lzma/LzmaDec.h" | ||
41 | #endif | ||
42 | #endif | ||
43 | |||
44 | namespace irr | ||
45 | { | ||
46 | namespace io | ||
47 | { | ||
48 | |||
49 | |||
50 | // ----------------------------------------------------------------------------- | ||
51 | // zip loader | ||
52 | // ----------------------------------------------------------------------------- | ||
53 | |||
54 | //! Constructor | ||
55 | CArchiveLoaderZIP::CArchiveLoaderZIP(io::IFileSystem* fs) | ||
56 | : FileSystem(fs) | ||
57 | { | ||
58 | #ifdef _DEBUG | ||
59 | setDebugName("CArchiveLoaderZIP"); | ||
60 | #endif | ||
61 | } | ||
62 | |||
63 | //! returns true if the file maybe is able to be loaded by this class | ||
64 | bool CArchiveLoaderZIP::isALoadableFileFormat(const io::path& filename) const | ||
65 | { | ||
66 | return core::hasFileExtension(filename, "zip", "pk3") || | ||
67 | core::hasFileExtension(filename, "gz", "tgz"); | ||
68 | } | ||
69 | |||
70 | //! Check to see if the loader can create archives of this type. | ||
71 | bool CArchiveLoaderZIP::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const | ||
72 | { | ||
73 | return (fileType == EFAT_ZIP || fileType == EFAT_GZIP); | ||
74 | } | ||
75 | |||
76 | |||
77 | //! Creates an archive from the filename | ||
78 | /** \param file File handle to check. | ||
79 | \return Pointer to newly created archive, or 0 upon error. */ | ||
80 | IFileArchive* CArchiveLoaderZIP::createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const | ||
81 | { | ||
82 | IFileArchive *archive = 0; | ||
83 | io::IReadFile* file = FileSystem->createAndOpenFile(filename); | ||
84 | |||
85 | if (file) | ||
86 | { | ||
87 | archive = createArchive(file, ignoreCase, ignorePaths); | ||
88 | file->drop(); | ||
89 | } | ||
90 | |||
91 | return archive; | ||
92 | } | ||
93 | |||
94 | //! creates/loads an archive from the file. | ||
95 | //! \return Pointer to the created archive. Returns 0 if loading failed. | ||
96 | IFileArchive* CArchiveLoaderZIP::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const | ||
97 | { | ||
98 | IFileArchive *archive = 0; | ||
99 | if (file) | ||
100 | { | ||
101 | file->seek(0); | ||
102 | |||
103 | u16 sig; | ||
104 | file->read(&sig, 2); | ||
105 | |||
106 | #ifdef __BIG_ENDIAN__ | ||
107 | sig = os::Byteswap::byteswap(sig); | ||
108 | #endif | ||
109 | |||
110 | file->seek(0); | ||
111 | |||
112 | bool isGZip = (sig == 0x8b1f); | ||
113 | |||
114 | archive = new CZipReader(file, ignoreCase, ignorePaths, isGZip); | ||
115 | } | ||
116 | return archive; | ||
117 | } | ||
118 | |||
119 | //! Check if the file might be loaded by this class | ||
120 | /** Check might look into the file. | ||
121 | \param file File handle to check. | ||
122 | \return True if file seems to be loadable. */ | ||
123 | bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile* file) const | ||
124 | { | ||
125 | SZIPFileHeader header; | ||
126 | |||
127 | file->read( &header.Sig, 4 ); | ||
128 | #ifdef __BIG_ENDIAN__ | ||
129 | header.Sig = os::Byteswap::byteswap(header.Sig); | ||
130 | #endif | ||
131 | |||
132 | return header.Sig == 0x04034b50 || // ZIP | ||
133 | (header.Sig&0xffff) == 0x8b1f; // gzip | ||
134 | } | ||
135 | |||
136 | // ----------------------------------------------------------------------------- | ||
137 | // zip archive | ||
138 | // ----------------------------------------------------------------------------- | ||
139 | |||
140 | CZipReader::CZipReader(IReadFile* file, bool ignoreCase, bool ignorePaths, bool isGZip) | ||
141 | : CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), File(file), IsGZip(isGZip) | ||
142 | { | ||
143 | #ifdef _DEBUG | ||
144 | setDebugName("CZipReader"); | ||
145 | #endif | ||
146 | |||
147 | if (File) | ||
148 | { | ||
149 | File->grab(); | ||
150 | |||
151 | // load file entries | ||
152 | if (IsGZip) | ||
153 | while (scanGZipHeader()) { } | ||
154 | else | ||
155 | while (scanZipHeader()) { } | ||
156 | |||
157 | sort(); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | CZipReader::~CZipReader() | ||
162 | { | ||
163 | if (File) | ||
164 | File->drop(); | ||
165 | } | ||
166 | |||
167 | |||
168 | //! get the archive type | ||
169 | E_FILE_ARCHIVE_TYPE CZipReader::getType() const | ||
170 | { | ||
171 | return IsGZip ? EFAT_GZIP : EFAT_ZIP; | ||
172 | } | ||
173 | |||
174 | const IFileList* CZipReader::getFileList() const | ||
175 | { | ||
176 | return this; | ||
177 | } | ||
178 | |||
179 | |||
180 | //! scans for a local header, returns false if there is no more local file header. | ||
181 | //! The gzip file format seems to think that there can be multiple files in a gzip file | ||
182 | //! but none | ||
183 | bool CZipReader::scanGZipHeader() | ||
184 | { | ||
185 | SZipFileEntry entry; | ||
186 | entry.Offset = 0; | ||
187 | memset(&entry.header, 0, sizeof(SZIPFileHeader)); | ||
188 | |||
189 | // read header | ||
190 | SGZIPMemberHeader header; | ||
191 | if (File->read(&header, sizeof(SGZIPMemberHeader)) == sizeof(SGZIPMemberHeader)) | ||
192 | { | ||
193 | |||
194 | #ifdef __BIG_ENDIAN__ | ||
195 | header.sig = os::Byteswap::byteswap(header.sig); | ||
196 | header.time = os::Byteswap::byteswap(header.time); | ||
197 | #endif | ||
198 | |||
199 | // check header value | ||
200 | if (header.sig != 0x8b1f) | ||
201 | return false; | ||
202 | |||
203 | // now get the file info | ||
204 | if (header.flags & EGZF_EXTRA_FIELDS) | ||
205 | { | ||
206 | // read lenth of extra data | ||
207 | u16 dataLen; | ||
208 | |||
209 | File->read(&dataLen, 2); | ||
210 | |||
211 | #ifdef __BIG_ENDIAN__ | ||
212 | dataLen = os::Byteswap::byteswap(dataLen); | ||
213 | #endif | ||
214 | |||
215 | // skip it | ||
216 | File->seek(dataLen, true); | ||
217 | } | ||
218 | |||
219 | io::path ZipFileName = ""; | ||
220 | |||
221 | if (header.flags & EGZF_FILE_NAME) | ||
222 | { | ||
223 | c8 c; | ||
224 | File->read(&c, 1); | ||
225 | while (c) | ||
226 | { | ||
227 | ZipFileName.append(c); | ||
228 | File->read(&c, 1); | ||
229 | } | ||
230 | } | ||
231 | else | ||
232 | { | ||
233 | // no file name? | ||
234 | ZipFileName = Path; | ||
235 | core::deletePathFromFilename(ZipFileName); | ||
236 | |||
237 | // rename tgz to tar or remove gz extension | ||
238 | if (core::hasFileExtension(ZipFileName, "tgz")) | ||
239 | { | ||
240 | ZipFileName[ ZipFileName.size() - 2] = 'a'; | ||
241 | ZipFileName[ ZipFileName.size() - 1] = 'r'; | ||
242 | } | ||
243 | else if (core::hasFileExtension(ZipFileName, "gz")) | ||
244 | { | ||
245 | ZipFileName[ ZipFileName.size() - 3] = 0; | ||
246 | ZipFileName.validate(); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if (header.flags & EGZF_COMMENT) | ||
251 | { | ||
252 | c8 c='a'; | ||
253 | while (c) | ||
254 | File->read(&c, 1); | ||
255 | } | ||
256 | |||
257 | if (header.flags & EGZF_CRC16) | ||
258 | File->seek(2, true); | ||
259 | |||
260 | // we are now at the start of the data blocks | ||
261 | entry.Offset = File->getPos(); | ||
262 | |||
263 | entry.header.FilenameLength = ZipFileName.size(); | ||
264 | |||
265 | entry.header.CompressionMethod = header.compressionMethod; | ||
266 | entry.header.DataDescriptor.CompressedSize = (File->getSize() - 8) - File->getPos(); | ||
267 | |||
268 | // seek to file end | ||
269 | File->seek(entry.header.DataDescriptor.CompressedSize, true); | ||
270 | |||
271 | // read CRC | ||
272 | File->read(&entry.header.DataDescriptor.CRC32, 4); | ||
273 | // read uncompressed size | ||
274 | File->read(&entry.header.DataDescriptor.UncompressedSize, 4); | ||
275 | |||
276 | #ifdef __BIG_ENDIAN__ | ||
277 | entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32); | ||
278 | entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize); | ||
279 | #endif | ||
280 | |||
281 | // now we've filled all the fields, this is just a standard deflate block | ||
282 | addItem(ZipFileName, entry.Offset, entry.header.DataDescriptor.UncompressedSize, false, 0); | ||
283 | FileInfo.push_back(entry); | ||
284 | } | ||
285 | |||
286 | // there's only one block of data in a gzip file | ||
287 | return false; | ||
288 | } | ||
289 | |||
290 | //! scans for a local header, returns false if there is no more local file header. | ||
291 | bool CZipReader::scanZipHeader(bool ignoreGPBits) | ||
292 | { | ||
293 | io::path ZipFileName = ""; | ||
294 | SZipFileEntry entry; | ||
295 | entry.Offset = 0; | ||
296 | memset(&entry.header, 0, sizeof(SZIPFileHeader)); | ||
297 | |||
298 | File->read(&entry.header, sizeof(SZIPFileHeader)); | ||
299 | |||
300 | #ifdef __BIG_ENDIAN__ | ||
301 | entry.header.Sig = os::Byteswap::byteswap(entry.header.Sig); | ||
302 | entry.header.VersionToExtract = os::Byteswap::byteswap(entry.header.VersionToExtract); | ||
303 | entry.header.GeneralBitFlag = os::Byteswap::byteswap(entry.header.GeneralBitFlag); | ||
304 | entry.header.CompressionMethod = os::Byteswap::byteswap(entry.header.CompressionMethod); | ||
305 | entry.header.LastModFileTime = os::Byteswap::byteswap(entry.header.LastModFileTime); | ||
306 | entry.header.LastModFileDate = os::Byteswap::byteswap(entry.header.LastModFileDate); | ||
307 | entry.header.DataDescriptor.CRC32 = os::Byteswap::byteswap(entry.header.DataDescriptor.CRC32); | ||
308 | entry.header.DataDescriptor.CompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.CompressedSize); | ||
309 | entry.header.DataDescriptor.UncompressedSize = os::Byteswap::byteswap(entry.header.DataDescriptor.UncompressedSize); | ||
310 | entry.header.FilenameLength = os::Byteswap::byteswap(entry.header.FilenameLength); | ||
311 | entry.header.ExtraFieldLength = os::Byteswap::byteswap(entry.header.ExtraFieldLength); | ||
312 | #endif | ||
313 | |||
314 | if (entry.header.Sig != 0x04034b50) | ||
315 | return false; // local file headers end here. | ||
316 | |||
317 | // read filename | ||
318 | { | ||
319 | c8 *tmp = new c8 [ entry.header.FilenameLength + 2 ]; | ||
320 | File->read(tmp, entry.header.FilenameLength); | ||
321 | tmp[entry.header.FilenameLength] = 0; | ||
322 | ZipFileName = tmp; | ||
323 | delete [] tmp; | ||
324 | } | ||
325 | |||
326 | #ifdef _IRR_COMPILE_WITH_ZIP_ENCRYPTION_ | ||
327 | // AES encryption | ||
328 | if ((entry.header.GeneralBitFlag & ZIP_FILE_ENCRYPTED) && (entry.header.CompressionMethod == 99)) | ||
329 | { | ||
330 | s16 restSize = entry.header.ExtraFieldLength; | ||
331 | SZipFileExtraHeader extraHeader; | ||
332 | while (restSize) | ||
333 | { | ||
334 | File->read(&extraHeader, sizeof(extraHeader)); | ||
335 | #ifdef __BIG_ENDIAN__ | ||
336 | extraHeader.ID = os::Byteswap::byteswap(extraHeader.ID); | ||
337 | extraHeader.Size = os::Byteswap::byteswap(extraHeader.Size); | ||
338 | #endif | ||
339 | restSize -= sizeof(extraHeader); | ||
340 | if (extraHeader.ID==(s16)0x9901) | ||
341 | { | ||
342 | SZipFileAESExtraData data; | ||
343 | File->read(&data, sizeof(data)); | ||
344 | #ifdef __BIG_ENDIAN__ | ||
345 | data.Version = os::Byteswap::byteswap(data.Version); | ||
346 | data.CompressionMode = os::Byteswap::byteswap(data.CompressionMode); | ||
347 | #endif | ||
348 | restSize -= sizeof(data); | ||
349 | if (data.Vendor[0]=='A' && data.Vendor[1]=='E') | ||
350 | { | ||
351 | // encode values into Sig | ||
352 | // AE-Version | Strength | ActualMode | ||
353 | entry.header.Sig = | ||
354 | ((data.Version & 0xff) << 24) | | ||
355 | (data.EncryptionStrength << 16) | | ||
356 | (data.CompressionMode); | ||
357 | File->seek(restSize, true); | ||
358 | break; | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | // move forward length of extra field. | ||
364 | else | ||
365 | #endif | ||
366 | if (entry.header.ExtraFieldLength) | ||
367 | File->seek(entry.header.ExtraFieldLength, true); | ||
368 | |||
369 | // if bit 3 was set, use CentralDirectory for setup | ||
370 | if (!ignoreGPBits && entry.header.GeneralBitFlag & ZIP_INFO_IN_DATA_DESCRIPTOR) | ||
371 | { | ||
372 | SZIPFileCentralDirEnd dirEnd; | ||
373 | FileInfo.clear(); | ||
374 | Files.clear(); | ||
375 | // First place where the end record could be stored | ||
376 | File->seek(File->getSize()-22); | ||
377 | const char endID[] = {0x50, 0x4b, 0x05, 0x06, 0x0}; | ||
378 | char tmp[5]={'\0'}; | ||
379 | bool found=false; | ||
380 | // search for the end record ID | ||
381 | while (!found && File->getPos()>0) | ||
382 | { | ||
383 | int seek=8; | ||
384 | File->read(tmp, 4); | ||
385 | switch (tmp[0]) | ||
386 | { | ||
387 | case 0x50: | ||
388 | if (!strcmp(endID, tmp)) | ||
389 | { | ||
390 | seek=4; | ||
391 | found=true; | ||
392 | } | ||
393 | break; | ||
394 | case 0x4b: | ||
395 | seek=5; | ||
396 | break; | ||
397 | case 0x05: | ||
398 | seek=6; | ||
399 | break; | ||
400 | case 0x06: | ||
401 | seek=7; | ||
402 | break; | ||
403 | } | ||
404 | File->seek(-seek, true); | ||
405 | } | ||
406 | File->read(&dirEnd, sizeof(dirEnd)); | ||
407 | #ifdef __BIG_ENDIAN__ | ||
408 | dirEnd.NumberDisk = os::Byteswap::byteswap(dirEnd.NumberDisk); | ||
409 | dirEnd.NumberStart = os::Byteswap::byteswap(dirEnd.NumberStart); | ||
410 | dirEnd.TotalDisk = os::Byteswap::byteswap(dirEnd.TotalDisk); | ||
411 | dirEnd.TotalEntries = os::Byteswap::byteswap(dirEnd.TotalEntries); | ||
412 | dirEnd.Size = os::Byteswap::byteswap(dirEnd.Size); | ||
413 | dirEnd.Offset = os::Byteswap::byteswap(dirEnd.Offset); | ||
414 | dirEnd.CommentLength = os::Byteswap::byteswap(dirEnd.CommentLength); | ||
415 | #endif | ||
416 | FileInfo.reallocate(dirEnd.TotalEntries); | ||
417 | File->seek(dirEnd.Offset); | ||
418 | while (scanCentralDirectoryHeader()) { } | ||
419 | return false; | ||
420 | } | ||
421 | |||
422 | // store position in file | ||
423 | entry.Offset = File->getPos(); | ||
424 | // move forward length of data | ||
425 | File->seek(entry.header.DataDescriptor.CompressedSize, true); | ||
426 | |||
427 | #ifdef _DEBUG | ||
428 | //os::Debuginfo::print("added file from archive", ZipFileName.c_str()); | ||
429 | #endif | ||
430 | |||
431 | addItem(ZipFileName, entry.Offset, entry.header.DataDescriptor.UncompressedSize, ZipFileName.lastChar()=='/', FileInfo.size()); | ||
432 | FileInfo.push_back(entry); | ||
433 | |||
434 | return true; | ||
435 | } | ||
436 | |||
437 | |||
438 | //! scans for a local header, returns false if there is no more local file header. | ||
439 | bool CZipReader::scanCentralDirectoryHeader() | ||
440 | { | ||
441 | io::path ZipFileName = ""; | ||
442 | SZIPFileCentralDirFileHeader entry; | ||
443 | File->read(&entry, sizeof(SZIPFileCentralDirFileHeader)); | ||
444 | |||
445 | #ifdef __BIG_ENDIAN__ | ||
446 | entry.Sig = os::Byteswap::byteswap(entry.Sig); | ||
447 | entry.VersionMadeBy = os::Byteswap::byteswap(entry.VersionMadeBy); | ||
448 | entry.VersionToExtract = os::Byteswap::byteswap(entry.VersionToExtract); | ||
449 | entry.GeneralBitFlag = os::Byteswap::byteswap(entry.GeneralBitFlag); | ||
450 | entry.CompressionMethod = os::Byteswap::byteswap(entry.CompressionMethod); | ||
451 | entry.LastModFileTime = os::Byteswap::byteswap(entry.LastModFileTime); | ||
452 | entry.LastModFileDate = os::Byteswap::byteswap(entry.LastModFileDate); | ||
453 | entry.CRC32 = os::Byteswap::byteswap(entry.CRC32); | ||
454 | entry.CompressedSize = os::Byteswap::byteswap(entry.CompressedSize); | ||
455 | entry.UncompressedSize = os::Byteswap::byteswap(entry.UncompressedSize); | ||
456 | entry.FilenameLength = os::Byteswap::byteswap(entry.FilenameLength); | ||
457 | entry.ExtraFieldLength = os::Byteswap::byteswap(entry.ExtraFieldLength); | ||
458 | entry.FileCommentLength = os::Byteswap::byteswap(entry.FileCommentLength); | ||
459 | entry.DiskNumberStart = os::Byteswap::byteswap(entry.DiskNumberStart); | ||
460 | entry.InternalFileAttributes = os::Byteswap::byteswap(entry.InternalFileAttributes); | ||
461 | entry.ExternalFileAttributes = os::Byteswap::byteswap(entry.ExternalFileAttributes); | ||
462 | entry.RelativeOffsetOfLocalHeader = os::Byteswap::byteswap(entry.RelativeOffsetOfLocalHeader); | ||
463 | #endif | ||
464 | |||
465 | if (entry.Sig != 0x02014b50) | ||
466 | return false; // central dir headers end here. | ||
467 | |||
468 | const long pos = File->getPos(); | ||
469 | File->seek(entry.RelativeOffsetOfLocalHeader); | ||
470 | scanZipHeader(true); | ||
471 | File->seek(pos+entry.FilenameLength+entry.ExtraFieldLength+entry.FileCommentLength); | ||
472 | FileInfo.getLast().header.DataDescriptor.CompressedSize=entry.CompressedSize; | ||
473 | FileInfo.getLast().header.DataDescriptor.UncompressedSize=entry.UncompressedSize; | ||
474 | FileInfo.getLast().header.DataDescriptor.CRC32=entry.CRC32; | ||
475 | Files.getLast().Size=entry.UncompressedSize; | ||
476 | return true; | ||
477 | } | ||
478 | |||
479 | |||
480 | //! opens a file by file name | ||
481 | IReadFile* CZipReader::createAndOpenFile(const io::path& filename) | ||
482 | { | ||
483 | s32 index = findFile(filename, false); | ||
484 | |||
485 | if (index != -1) | ||
486 | return createAndOpenFile(index); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | #ifdef _IRR_COMPILE_WITH_LZMA_ | ||
492 | //! Used for LZMA decompression. The lib has no default memory management | ||
493 | namespace | ||
494 | { | ||
495 | void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } | ||
496 | void SzFree(void *p, void *address) { p = p; free(address); } | ||
497 | ISzAlloc lzmaAlloc = { SzAlloc, SzFree }; | ||
498 | } | ||
499 | #endif | ||
500 | |||
501 | //! opens a file by index | ||
502 | IReadFile* CZipReader::createAndOpenFile(u32 index) | ||
503 | { | ||
504 | // Irrlicht supports 0, 8, 12, 14, 99 | ||
505 | //0 - The file is stored (no compression) | ||
506 | //1 - The file is Shrunk | ||
507 | //2 - The file is Reduced with compression factor 1 | ||
508 | //3 - The file is Reduced with compression factor 2 | ||
509 | //4 - The file is Reduced with compression factor 3 | ||
510 | //5 - The file is Reduced with compression factor 4 | ||
511 | //6 - The file is Imploded | ||
512 | //7 - Reserved for Tokenizing compression algorithm | ||
513 | //8 - The file is Deflated | ||
514 | //9 - Reserved for enhanced Deflating | ||
515 | //10 - PKWARE Date Compression Library Imploding | ||
516 | //12 - bzip2 - Compression Method from libbz2, WinZip 10 | ||
517 | //14 - LZMA - Compression Method, WinZip 12 | ||
518 | //96 - Jpeg compression - Compression Method, WinZip 12 | ||
519 | //97 - WavPack - Compression Method, WinZip 11 | ||
520 | //98 - PPMd - Compression Method, WinZip 10 | ||
521 | //99 - AES encryption, WinZip 9 | ||
522 | |||
523 | const SZipFileEntry &e = FileInfo[Files[index].ID]; | ||
524 | wchar_t buf[64]; | ||
525 | s16 actualCompressionMethod=e.header.CompressionMethod; | ||
526 | IReadFile* decrypted=0; | ||
527 | u8* decryptedBuf=0; | ||
528 | u32 decryptedSize=e.header.DataDescriptor.CompressedSize; | ||
529 | #ifdef _IRR_COMPILE_WITH_ZIP_ENCRYPTION_ | ||
530 | if ((e.header.GeneralBitFlag & ZIP_FILE_ENCRYPTED) && (e.header.CompressionMethod == 99)) | ||
531 | { | ||
532 | os::Printer::log("Reading encrypted file."); | ||
533 | u8 salt[16]={0}; | ||
534 | const u16 saltSize = (((e.header.Sig & 0x00ff0000) >>16)+1)*4; | ||
535 | File->seek(e.Offset); | ||
536 | File->read(salt, saltSize); | ||
537 | char pwVerification[2]; | ||
538 | char pwVerificationFile[2]; | ||
539 | File->read(pwVerification, 2); | ||
540 | fcrypt_ctx zctx; // the encryption context | ||
541 | int rc = fcrypt_init( | ||
542 | (e.header.Sig & 0x00ff0000) >>16, | ||
543 | (const unsigned char*)Password.c_str(), // the password | ||
544 | Password.size(), // number of bytes in password | ||
545 | salt, // the salt | ||
546 | (unsigned char*)pwVerificationFile, // on return contains password verifier | ||
547 | &zctx); // encryption context | ||
548 | if (strncmp(pwVerificationFile, pwVerification, 2)) | ||
549 | { | ||
550 | os::Printer::log("Wrong password"); | ||
551 | return 0; | ||
552 | } | ||
553 | decryptedSize= e.header.DataDescriptor.CompressedSize-saltSize-12; | ||
554 | decryptedBuf= new u8[decryptedSize]; | ||
555 | u32 c = 0; | ||
556 | while ((c+32768)<=decryptedSize) | ||
557 | { | ||
558 | File->read(decryptedBuf+c, 32768); | ||
559 | fcrypt_decrypt( | ||
560 | decryptedBuf+c, // pointer to the data to decrypt | ||
561 | 32768, // how many bytes to decrypt | ||
562 | &zctx); // decryption context | ||
563 | c+=32768; | ||
564 | } | ||
565 | File->read(decryptedBuf+c, decryptedSize-c); | ||
566 | fcrypt_decrypt( | ||
567 | decryptedBuf+c, // pointer to the data to decrypt | ||
568 | decryptedSize-c, // how many bytes to decrypt | ||
569 | &zctx); // decryption context | ||
570 | |||
571 | char fileMAC[10]; | ||
572 | char resMAC[10]; | ||
573 | rc = fcrypt_end( | ||
574 | (unsigned char*)resMAC, // on return contains the authentication code | ||
575 | &zctx); // encryption context | ||
576 | if (rc != 10) | ||
577 | { | ||
578 | os::Printer::log("Error on encryption closing"); | ||
579 | delete [] decryptedBuf; | ||
580 | return 0; | ||
581 | } | ||
582 | File->read(fileMAC, 10); | ||
583 | if (strncmp(fileMAC, resMAC, 10)) | ||
584 | { | ||
585 | os::Printer::log("Error on encryption check"); | ||
586 | delete [] decryptedBuf; | ||
587 | return 0; | ||
588 | } | ||
589 | decrypted = io::createMemoryReadFile(decryptedBuf, decryptedSize, Files[index].FullName, true); | ||
590 | actualCompressionMethod = (e.header.Sig & 0xffff); | ||
591 | #if 0 | ||
592 | if ((e.header.Sig & 0xff000000)==0x01000000) | ||
593 | { | ||
594 | } | ||
595 | else if ((e.header.Sig & 0xff000000)==0x02000000) | ||
596 | { | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | os::Printer::log("Unknown encryption method"); | ||
601 | return 0; | ||
602 | } | ||
603 | #endif | ||
604 | } | ||
605 | #endif | ||
606 | switch(actualCompressionMethod) | ||
607 | { | ||
608 | case 0: // no compression | ||
609 | { | ||
610 | if (decrypted) | ||
611 | return decrypted; | ||
612 | else | ||
613 | return createLimitReadFile(Files[index].FullName, File, e.Offset, decryptedSize); | ||
614 | } | ||
615 | case 8: | ||
616 | { | ||
617 | #ifdef _IRR_COMPILE_WITH_ZLIB_ | ||
618 | |||
619 | const u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize; | ||
620 | c8* pBuf = new c8[ uncompressedSize ]; | ||
621 | if (!pBuf) | ||
622 | { | ||
623 | swprintf ( buf, 64, L"Not enough memory for decompressing %s", Files[index].FullName.c_str() ); | ||
624 | os::Printer::log( buf, ELL_ERROR); | ||
625 | if (decrypted) | ||
626 | decrypted->drop(); | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | u8 *pcData = decryptedBuf; | ||
631 | if (!pcData) | ||
632 | { | ||
633 | pcData = new u8[decryptedSize]; | ||
634 | if (!pcData) | ||
635 | { | ||
636 | swprintf ( buf, 64, L"Not enough memory for decompressing %s", Files[index].FullName.c_str() ); | ||
637 | os::Printer::log( buf, ELL_ERROR); | ||
638 | delete [] pBuf; | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | //memset(pcData, 0, decryptedSize); | ||
643 | File->seek(e.Offset); | ||
644 | File->read(pcData, decryptedSize); | ||
645 | } | ||
646 | |||
647 | // Setup the inflate stream. | ||
648 | z_stream stream; | ||
649 | s32 err; | ||
650 | |||
651 | stream.next_in = (Bytef*)pcData; | ||
652 | stream.avail_in = (uInt)decryptedSize; | ||
653 | stream.next_out = (Bytef*)pBuf; | ||
654 | stream.avail_out = uncompressedSize; | ||
655 | stream.zalloc = (alloc_func)0; | ||
656 | stream.zfree = (free_func)0; | ||
657 | |||
658 | // Perform inflation. wbits < 0 indicates no zlib header inside the data. | ||
659 | err = inflateInit2(&stream, -MAX_WBITS); | ||
660 | if (err == Z_OK) | ||
661 | { | ||
662 | err = inflate(&stream, Z_FINISH); | ||
663 | inflateEnd(&stream); | ||
664 | if (err == Z_STREAM_END) | ||
665 | err = Z_OK; | ||
666 | err = Z_OK; | ||
667 | inflateEnd(&stream); | ||
668 | } | ||
669 | |||
670 | if (decrypted) | ||
671 | decrypted->drop(); | ||
672 | else | ||
673 | delete[] pcData; | ||
674 | |||
675 | if (err != Z_OK) | ||
676 | { | ||
677 | swprintf ( buf, 64, L"Error decompressing %s", Files[index].FullName.c_str() ); | ||
678 | os::Printer::log( buf, ELL_ERROR); | ||
679 | delete [] pBuf; | ||
680 | return 0; | ||
681 | } | ||
682 | else | ||
683 | return io::createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true); | ||
684 | |||
685 | #else | ||
686 | return 0; // zlib not compiled, we cannot decompress the data. | ||
687 | #endif | ||
688 | } | ||
689 | case 12: | ||
690 | { | ||
691 | #ifdef _IRR_COMPILE_WITH_BZIP2_ | ||
692 | |||
693 | const u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize; | ||
694 | c8* pBuf = new c8[ uncompressedSize ]; | ||
695 | if (!pBuf) | ||
696 | { | ||
697 | swprintf ( buf, 64, L"Not enough memory for decompressing %s", Files[index].FullName.c_str() ); | ||
698 | os::Printer::log( buf, ELL_ERROR); | ||
699 | if (decrypted) | ||
700 | decrypted->drop(); | ||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | u8 *pcData = decryptedBuf; | ||
705 | if (!pcData) | ||
706 | { | ||
707 | pcData = new u8[decryptedSize]; | ||
708 | if (!pcData) | ||
709 | { | ||
710 | swprintf ( buf, 64, L"Not enough memory for decompressing %s", Files[index].FullName.c_str() ); | ||
711 | os::Printer::log( buf, ELL_ERROR); | ||
712 | delete [] pBuf; | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | //memset(pcData, 0, decryptedSize); | ||
717 | File->seek(e.Offset); | ||
718 | File->read(pcData, decryptedSize); | ||
719 | } | ||
720 | |||
721 | bz_stream bz_ctx={0}; | ||
722 | /* use BZIP2's default memory allocation | ||
723 | bz_ctx->bzalloc = NULL; | ||
724 | bz_ctx->bzfree = NULL; | ||
725 | bz_ctx->opaque = NULL; | ||
726 | */ | ||
727 | int err = BZ2_bzDecompressInit(&bz_ctx, 0, 0); /* decompression */ | ||
728 | if(err != BZ_OK) | ||
729 | { | ||
730 | os::Printer::log("bzip2 decompression failed. File cannot be read.", ELL_ERROR); | ||
731 | return 0; | ||
732 | } | ||
733 | bz_ctx.next_in = (char*)pcData; | ||
734 | bz_ctx.avail_in = decryptedSize; | ||
735 | /* pass all input to decompressor */ | ||
736 | bz_ctx.next_out = pBuf; | ||
737 | bz_ctx.avail_out = uncompressedSize; | ||
738 | err = BZ2_bzDecompress(&bz_ctx); | ||
739 | err = BZ2_bzDecompressEnd(&bz_ctx); | ||
740 | |||
741 | if (decrypted) | ||
742 | decrypted->drop(); | ||
743 | else | ||
744 | delete[] pcData; | ||
745 | |||
746 | if (err != BZ_OK) | ||
747 | { | ||
748 | swprintf ( buf, 64, L"Error decompressing %s", Files[index].FullName.c_str() ); | ||
749 | os::Printer::log( buf, ELL_ERROR); | ||
750 | delete [] pBuf; | ||
751 | return 0; | ||
752 | } | ||
753 | else | ||
754 | return io::createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true); | ||
755 | |||
756 | #else | ||
757 | os::Printer::log("bzip2 decompression not supported. File cannot be read.", ELL_ERROR); | ||
758 | return 0; | ||
759 | #endif | ||
760 | } | ||
761 | case 14: | ||
762 | { | ||
763 | #ifdef _IRR_COMPILE_WITH_LZMA_ | ||
764 | |||
765 | u32 uncompressedSize = e.header.DataDescriptor.UncompressedSize; | ||
766 | c8* pBuf = new c8[ uncompressedSize ]; | ||
767 | if (!pBuf) | ||
768 | { | ||
769 | swprintf ( buf, 64, L"Not enough memory for decompressing %s", Files[index].FullName.c_str() ); | ||
770 | os::Printer::log( buf, ELL_ERROR); | ||
771 | if (decrypted) | ||
772 | decrypted->drop(); | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | u8 *pcData = decryptedBuf; | ||
777 | if (!pcData) | ||
778 | { | ||
779 | pcData = new u8[decryptedSize]; | ||
780 | if (!pcData) | ||
781 | { | ||
782 | swprintf ( buf, 64, L"Not enough memory for decompressing %s", Files[index].FullName.c_str() ); | ||
783 | os::Printer::log( buf, ELL_ERROR); | ||
784 | delete [] pBuf; | ||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | //memset(pcData, 0, decryptedSize); | ||
789 | File->seek(e.Offset); | ||
790 | File->read(pcData, decryptedSize); | ||
791 | } | ||
792 | |||
793 | ELzmaStatus status; | ||
794 | SizeT tmpDstSize = uncompressedSize; | ||
795 | SizeT tmpSrcSize = decryptedSize; | ||
796 | |||
797 | unsigned int propSize = (pcData[3]<<8)+pcData[2]; | ||
798 | int err = LzmaDecode((Byte*)pBuf, &tmpDstSize, | ||
799 | pcData+4+propSize, &tmpSrcSize, | ||
800 | pcData+4, propSize, | ||
801 | e.header.GeneralBitFlag&0x1?LZMA_FINISH_END:LZMA_FINISH_ANY, &status, | ||
802 | &lzmaAlloc); | ||
803 | uncompressedSize = tmpDstSize; // may be different to expected value | ||
804 | |||
805 | if (decrypted) | ||
806 | decrypted->drop(); | ||
807 | else | ||
808 | delete[] pcData; | ||
809 | |||
810 | if (err != SZ_OK) | ||
811 | { | ||
812 | os::Printer::log( "Error decompressing", Files[index].FullName, ELL_ERROR); | ||
813 | delete [] pBuf; | ||
814 | return 0; | ||
815 | } | ||
816 | else | ||
817 | return io::createMemoryReadFile(pBuf, uncompressedSize, Files[index].FullName, true); | ||
818 | |||
819 | #else | ||
820 | os::Printer::log("lzma decompression not supported. File cannot be read.", ELL_ERROR); | ||
821 | return 0; | ||
822 | #endif | ||
823 | } | ||
824 | case 99: | ||
825 | // If we come here with an encrypted file, decryption support is missing | ||
826 | os::Printer::log("Decryption support not enabled. File cannot be read.", ELL_ERROR); | ||
827 | return 0; | ||
828 | default: | ||
829 | swprintf ( buf, 64, L"file has unsupported compression method. %s", Files[index].FullName.c_str() ); | ||
830 | os::Printer::log( buf, ELL_ERROR); | ||
831 | return 0; | ||
832 | }; | ||
833 | |||
834 | } | ||
835 | |||
836 | } // end namespace io | ||
837 | } // end namespace irr | ||
838 | |||
839 | #endif // __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_ | ||