1 #include "lc_global.h"
2 #include "lc_zipfile.h"
3 #include "lc_file.h"
4 #include "lc_math.h"
5 #include <zlib.h>
6 #include <time.h>
7
8 #if MAX_MEM_LEVEL >= 8
9 # define DEF_MEM_LEVEL 8
10 #else
11 # define DEF_MEM_LEVEL MAX_MEM_LEVEL
12 #endif
13
lcZipFile()14 lcZipFile::lcZipFile()
15 {
16 mModified = false;
17 }
18
~lcZipFile()19 lcZipFile::~lcZipFile()
20 {
21 }
22
OpenRead(const QString & FileName)23 bool lcZipFile::OpenRead(const QString& FileName)
24 {
25 std::unique_ptr<lcDiskFile> File(new lcDiskFile(FileName));
26
27 if (!File->Open(QIODevice::ReadOnly))
28 return false;
29
30 return OpenRead(std::move(File));
31 }
32
OpenRead(std::unique_ptr<lcFile> File)33 bool lcZipFile::OpenRead(std::unique_ptr<lcFile> File)
34 {
35 mFile = std::move(File);
36
37 if (!Open())
38 {
39 mFile = nullptr;
40 return false;
41 }
42
43 return true;
44 }
45
OpenWrite(const QString & FileName)46 bool lcZipFile::OpenWrite(const QString& FileName)
47 {
48 std::unique_ptr<lcDiskFile> File(new lcDiskFile(FileName));
49
50 if (!File->Open(QIODevice::WriteOnly))
51 return false;
52
53 mFile = std::move(File);
54
55 mNumEntries = 0;
56 mCentralDirSize = 0;
57 mCentralDirOffset = 0;
58 mBytesBeforeZipFile = 0;
59 mCentralPos = 0;
60
61 return true;
62 }
63
SearchCentralDir()64 quint64 lcZipFile::SearchCentralDir()
65 {
66 quint64 SizeFile, MaxBack, BackRead, PosFound;
67 const int CommentBufferSize = 1024;
68 quint8 buf[CommentBufferSize + 4];
69
70 SizeFile = mFile->GetLength();
71 MaxBack = lcMin(SizeFile, 0xffffULL);
72 BackRead = 4;
73 PosFound = 0;
74
75 while (BackRead < MaxBack)
76 {
77 quint64 ReadPos, ReadSize;
78
79 if (BackRead + CommentBufferSize > MaxBack)
80 BackRead = MaxBack;
81 else
82 BackRead += CommentBufferSize;
83 ReadPos = SizeFile - BackRead;
84
85 ReadSize = ((CommentBufferSize + 4) < (SizeFile - ReadPos)) ? (CommentBufferSize + 4) : (SizeFile - ReadPos);
86 mFile->Seek(ReadPos, SEEK_SET);
87
88 if (mFile->ReadBuffer(buf, (long)ReadSize) != ReadSize)
89 break;
90
91 for (int i = (int)ReadSize - 3; (i--) > 0;)
92 {
93 if (((*(buf+i)) == 0x50) && ((*(buf+i+1)) == 0x4b) && ((*(buf+i+2)) == 0x05) && ((*(buf+i+3)) == 0x06))
94 {
95 PosFound = ReadPos + i;
96 break;
97 }
98 }
99
100 if (PosFound != 0)
101 break;
102 }
103
104 return PosFound;
105 }
106
SearchCentralDir64()107 quint64 lcZipFile::SearchCentralDir64()
108 {
109 quint64 SizeFile, MaxBack, BackRead, PosFound;
110 const int CommentBufferSize = 1024;
111 quint8 buf[CommentBufferSize + 4];
112
113 SizeFile = mFile->GetLength();
114 MaxBack = lcMin(SizeFile, 0xffffULL);
115 BackRead = 4;
116 PosFound = 0;
117
118 while (BackRead < MaxBack)
119 {
120 quint64 ReadPos, ReadSize;
121
122 if (BackRead + CommentBufferSize > MaxBack)
123 BackRead = MaxBack;
124 else
125 BackRead += CommentBufferSize;
126 ReadPos = SizeFile - BackRead;
127
128 ReadSize = ((CommentBufferSize + 4) < (SizeFile - ReadPos)) ? (CommentBufferSize + 4) : (SizeFile - ReadPos);
129 mFile->Seek(ReadPos, SEEK_SET);
130
131 if (mFile->ReadBuffer(buf, (long)ReadSize) != ReadSize)
132 break;
133
134 for (int i=(int)ReadSize - 3; (i--) > 0; )
135 {
136 if (((*(buf+i)) == 0x50) && ((*(buf+i+1)) == 0x4b) && ((*(buf+i+2)) == 0x06) && ((*(buf+i+3)) == 0x07))
137 {
138 PosFound = ReadPos + i;
139 break;
140 }
141 }
142
143 if (PosFound != 0)
144 break;
145 }
146
147 if (PosFound == 0)
148 return 0;
149
150 mFile->Seek(PosFound, SEEK_SET);
151
152 quint32 Number;
153 quint64 RelativeOffset;
154
155 // Signature.
156 if (mFile->ReadU32(&Number, 1) != 1)
157 return 0;
158
159 // Number of the disk with the start of the zip64 end of central directory.
160 if (mFile->ReadU32(&Number, 1) != 1)
161 return 0;
162
163 if (Number != 0)
164 return 0;
165
166 // Relative offset of the zip64 end of central directory record.
167 if (mFile->ReadU64(&RelativeOffset, 1) != 1)
168 return 0;
169
170 // Total number of disks.
171 if (mFile->ReadU32(&Number, 1) != 1)
172 return 0;
173
174 if (Number != 0)
175 return 0;
176
177 // Go to end of central directory record.
178 mFile->Seek(RelativeOffset, SEEK_SET);
179
180 // The signature.
181 if (mFile->ReadU32(&Number, 1) != 1)
182 return 0;
183
184 if (Number != 0x06064b50)
185 return 0;
186
187 return RelativeOffset;
188 }
189
CheckFileCoherencyHeader(int FileIndex,quint32 * SizeVar,quint64 * OffsetLocalExtraField,quint32 * SizeLocalExtraField)190 bool lcZipFile::CheckFileCoherencyHeader(int FileIndex, quint32* SizeVar, quint64* OffsetLocalExtraField, quint32* SizeLocalExtraField)
191 {
192 quint16 Number16, Flags;
193 quint32 Number32, Magic;
194 quint16 SizeFilename, SizeExtraField;
195 const lcZipFileInfo& FileInfo = mFiles[FileIndex];
196
197 *SizeVar = 0;
198 *OffsetLocalExtraField = 0;
199 *SizeLocalExtraField = 0;
200
201 mFile->Seek(FileInfo.offset_curfile + mBytesBeforeZipFile, SEEK_SET);
202
203 if (mFile->ReadU32(&Magic, 1) != 1 || Magic != 0x04034b50)
204 return false;
205
206 if (mFile->ReadU16(&Number16, 1) != 1)
207 return false;
208
209 if (mFile->ReadU16(&Flags, 1) != 1)
210 return false;
211
212 if (mFile->ReadU16(&Number16, 1) != 1 || Number16 != FileInfo.compression_method)
213 return false;
214
215 if (FileInfo.compression_method != 0 && FileInfo.compression_method != Z_DEFLATED)
216 return false;
217
218 if (mFile->ReadU32(&Number32, 1) != 1)
219 return false;
220
221 if (mFile->ReadU32(&Number32, 1) != 1 || ((Number32 != FileInfo.crc) && ((Flags & 8)==0)))
222 return false;
223
224 if (mFile->ReadU32(&Number32, 1) != 1 || (Number32 != 0xffffffffU && (Number32 != FileInfo.compressed_size) && ((Flags & 8)==0)))
225 return false;
226
227 if (mFile->ReadU32(&Number32, 1) != 1 || (Number32 != 0xffffffffU && (Number32 != FileInfo.uncompressed_size) && ((Flags & 8)==0)))
228 return false;
229
230 if (mFile->ReadU16(&SizeFilename, 1) != 1 || SizeFilename != FileInfo.size_filename)
231 return false;
232
233 *SizeVar += SizeFilename;
234
235 if (mFile->ReadU16(&SizeExtraField, 1) != 1)
236 return false;
237
238 *OffsetLocalExtraField= FileInfo.offset_curfile + 0x1e + SizeFilename;
239 *SizeLocalExtraField = SizeExtraField;
240
241 *SizeVar += SizeExtraField;
242
243 return true;
244 }
245
Open()246 bool lcZipFile::Open()
247 {
248 quint64 NumberEntriesCD, CentralPos;
249
250 CentralPos = SearchCentralDir64();
251
252 if (CentralPos)
253 {
254 quint32 NumberDisk, NumberDiskWithCD;
255
256 mZip64 = true;
257
258 // Skip signature, size and versions.
259 mFile->Seek(CentralPos + 4 + 8 + 2 + 2, SEEK_SET);
260
261 // Number of this disk.
262 if (mFile->ReadU32(&NumberDisk, 1) != 1)
263 return false;
264
265 // Number of the disk with the start of the central directory.
266 if (mFile->ReadU32(&NumberDiskWithCD, 1) != 1)
267 return false;
268
269 // Total number of entries in the central directory on this disk.
270 if (mFile->ReadU64(&mNumEntries, 1) != 1)
271 return false;
272
273 // Total number of entries in the central directory.
274 if (mFile->ReadU64(&NumberEntriesCD, 1) != 1)
275 return false;
276
277 if ((NumberEntriesCD != mNumEntries) || (NumberDiskWithCD != 0) || (NumberDisk != 0))
278 return false;
279
280 // Size of the central directory.
281 if (mFile->ReadU64(&mCentralDirSize, 1) != 1)
282 return false;
283
284 // Offset of start of central directory with respect to the starting disk number.
285 if (mFile->ReadU64(&mCentralDirOffset, 1) != 1)
286 return false;
287
288 // us.gi.size_comment = 0;
289 }
290 else
291 {
292 quint16 NumberDisk, NumberDiskWithCD;
293 quint16 Number16;
294 quint32 Number32;
295
296 CentralPos = SearchCentralDir();
297 if (CentralPos == 0)
298 return false;
299
300 mZip64 = false;
301
302 // Skip signature.
303 mFile->Seek(CentralPos + 4, SEEK_SET);
304
305 // Number of this disk.
306 if (mFile->ReadU16(&NumberDisk, 1) != 1)
307 return false;
308
309 // Number of the disk with the start of the central directory.
310 if (mFile->ReadU16(&NumberDiskWithCD, 1) != 1)
311 return false;
312
313 // Total number of entries in the central dir on this disk.
314 if (mFile->ReadU16(&Number16, 1) != 1)
315 return false;
316 mNumEntries = Number16;
317
318 // Total number of entries in the central dir.
319 if (mFile->ReadU16(&Number16, 1) != 1)
320 return false;
321 NumberEntriesCD = Number16;
322
323 if ((NumberEntriesCD != mNumEntries) || (NumberDiskWithCD != 0) || (NumberDisk != 0))
324 return false;
325
326 // Size of the central directory.
327 if (mFile->ReadU32(&Number32, 1) != 1)
328 return false;
329 mCentralDirSize = Number32;
330
331 // Offset of start of central directory with respect to the starting disk number.
332 if (mFile->ReadU32(&Number32, 1) != 1)
333 return false;
334 mCentralDirOffset= Number32;
335
336 // zipfile comment length.
337 // if (mFile->ReadU16(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=1)
338 // return false;
339 }
340
341 if (CentralPos < mCentralDirOffset + mCentralDirSize)
342 return false;
343
344 mBytesBeforeZipFile = CentralPos - (mCentralDirOffset + mCentralDirSize);
345 mCentralPos = CentralPos;
346
347 return ReadCentralDir();
348 }
349
ReadCentralDir()350 bool lcZipFile::ReadCentralDir()
351 {
352 quint64 PosInCentralDir = mCentralDirOffset;
353
354 mFile->Seek(PosInCentralDir + mBytesBeforeZipFile, SEEK_SET);
355 mFiles.AllocGrow((int)mNumEntries);
356
357 for (quint64 FileNum = 0; FileNum < mNumEntries; FileNum++)
358 {
359 quint32 Magic, Number32;
360 lcZipFileInfo& FileInfo = mFiles.Add();
361 long Seek = 0;
362
363 FileInfo.write_buffer = nullptr;
364 FileInfo.deleted = false;
365
366 if (mFile->ReadU32(&Magic, 1) != 1 || Magic != 0x02014b50)
367 return false;
368
369 if (mFile->ReadU16(&FileInfo.version, 1) != 1)
370 return false;
371
372 if (mFile->ReadU16(&FileInfo.version_needed, 1) != 1)
373 return false;
374
375 if (mFile->ReadU16(&FileInfo.flag, 1) != 1)
376 return false;
377
378 if (mFile->ReadU16(&FileInfo.compression_method, 1) != 1)
379 return false;
380
381 if (mFile->ReadU32(&FileInfo.dosDate, 1) != 1)
382 return false;
383
384 quint32 Date = FileInfo.dosDate >> 16;
385 FileInfo.tmu_date.tm_mday = (quint32)(Date & 0x1f);
386 FileInfo.tmu_date.tm_mon = (quint32)((((Date) & 0x1E0) / 0x20) - 1);
387 FileInfo.tmu_date.tm_year = (quint32)(((Date & 0x0FE00) / 0x0200) + 1980);
388
389 FileInfo.tmu_date.tm_hour = (quint32)((FileInfo.dosDate & 0xF800) / 0x800);
390 FileInfo.tmu_date.tm_min = (quint32)((FileInfo.dosDate & 0x7E0) / 0x20);
391 FileInfo.tmu_date.tm_sec = (quint32)(2*(FileInfo.dosDate & 0x1f));
392
393 if (mFile->ReadU32(&FileInfo.crc, 1) != 1)
394 return false;
395
396 if (mFile->ReadU32(&Number32, 1) != 1)
397 return false;
398 FileInfo.compressed_size = Number32;
399
400 if (mFile->ReadU32(&Number32, 1) != 1)
401 return false;
402 FileInfo.uncompressed_size = Number32;
403
404 if (mFile->ReadU16(&FileInfo.size_filename, 1) != 1)
405 return false;
406
407 if (mFile->ReadU16(&FileInfo.size_file_extra, 1) != 1)
408 return false;
409
410 if (mFile->ReadU16(&FileInfo.size_file_comment, 1) != 1)
411 return false;
412
413 if (mFile->ReadU16(&FileInfo.disk_num_start, 1) != 1)
414 return false;
415
416 if (mFile->ReadU16(&FileInfo.internal_fa, 1) != 1)
417 return false;
418
419 if (mFile->ReadU32(&FileInfo.external_fa, 1) != 1)
420 return false;
421
422 // relative offset of local header
423 if (mFile->ReadU32(&Number32, 1) != 1)
424 return false;
425 FileInfo.offset_curfile = Number32;
426
427 Seek += FileInfo.size_filename;
428
429 quint32 SizeRead;
430 if (FileInfo.size_filename < sizeof(FileInfo.file_name) - 1)
431 {
432 *(FileInfo.file_name + FileInfo.size_filename) = '\0';
433 SizeRead = FileInfo.size_filename;
434 }
435 else
436 {
437 *(FileInfo.file_name + sizeof(FileInfo.file_name) - 1) = '\0';
438 SizeRead = sizeof(FileInfo.file_name) - 1;
439 }
440
441 if (FileInfo.size_filename > 0)
442 if (mFile->ReadBuffer(FileInfo.file_name, SizeRead) != SizeRead)
443 return false;
444 Seek -= SizeRead;
445 /*
446 // Read extrafield
447 if ((err==UNZ_OK) && (extraField!=nullptr))
448 {
449 ZPOS64_T uSizeRead ;
450 if (file_info.size_file_extra<extraFieldBufferSize)
451 uSizeRead = file_info.size_file_extra;
452 else
453 uSizeRead = extraFieldBufferSize;
454
455 if (lSeek!=0)
456 {
457 if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
458 lSeek=0;
459 else
460 err=UNZ_ERRNO;
461 }
462
463 if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
464 if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead)
465 err=UNZ_ERRNO;
466
467 lSeek += file_info.size_file_extra - (uLong)uSizeRead;
468 }
469 else
470 lSeek += file_info.size_file_extra;
471 */
472 Seek += FileInfo.size_file_extra;
473
474 if (FileInfo.size_file_extra != 0)
475 {
476 quint32 acc = 0;
477
478 // since lSeek now points to after the extra field we need to move back
479 Seek -= FileInfo.size_file_extra;
480
481 if (Seek != 0)
482 {
483 mFile->Seek(Seek, SEEK_CUR);
484 Seek = 0;
485 }
486
487 while (acc < FileInfo.size_file_extra)
488 {
489 quint16 HeaderId, DataSize;
490
491 if (mFile->ReadU16(&HeaderId, 1) != 1)
492 return false;
493
494 if (mFile->ReadU16(&DataSize, 1) != 1)
495 return false;
496
497 // ZIP64 extra fields.
498 if (HeaderId == 0x0001)
499 {
500 if (FileInfo.uncompressed_size == (quint64)(unsigned long)-1)
501 {
502 if (mFile->ReadU64(&FileInfo.uncompressed_size, 1) != 1)
503 return false;
504 }
505
506 if (FileInfo.compressed_size == (quint64)(unsigned long)-1)
507 {
508 if (mFile->ReadU64(&FileInfo.compressed_size, 1) != 1)
509 return false;
510 }
511
512 if (FileInfo.offset_curfile == (quint64)-1)
513 {
514 // Relative Header offset.
515 if (mFile->ReadU64(&FileInfo.offset_curfile, 1) != 1)
516 return false;
517 }
518
519 if (FileInfo.disk_num_start == (quint16)-1)
520 {
521 // Disk Start Number.
522 if (mFile->ReadU32(&Number32, 1) != 1)
523 return false;
524 }
525 }
526 else
527 {
528 mFile->Seek(DataSize, SEEK_CUR);
529 }
530
531 acc += 2 + 2 + DataSize;
532 }
533 }
534 /*
535 if ((err==UNZ_OK) && (szComment!=nullptr))
536 {
537 uLong uSizeRead ;
538 if (file_info.size_file_comment<commentBufferSize)
539 {
540 *(szComment+file_info.size_file_comment)='\0';
541 uSizeRead = file_info.size_file_comment;
542 }
543 else
544 uSizeRead = commentBufferSize;
545
546 if (lSeek!=0)
547 {
548 if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
549 lSeek=0;
550 else
551 err=UNZ_ERRNO;
552 }
553
554 if ((file_info.size_file_comment>0) && (commentBufferSize>0))
555 if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
556 err=UNZ_ERRNO;
557 lSeek+=file_info.size_file_comment - uSizeRead;
558 }
559 else
560 lSeek+=file_info.size_file_comment;
561 */
562 Seek += FileInfo.size_file_comment;
563
564 mFile->Seek(Seek, SEEK_CUR);
565 }
566
567 return true;
568 }
569
ExtractFile(const char * FileName,lcMemFile & File,quint32 MaxLength)570 bool lcZipFile::ExtractFile(const char* FileName, lcMemFile& File, quint32 MaxLength)
571 {
572 for (int FileIdx = 0; FileIdx < mFiles.GetSize(); FileIdx++)
573 {
574 lcZipFileInfo& FileInfo = mFiles[FileIdx];
575
576 if (!qstricmp(FileInfo.file_name, FileName))
577 return ExtractFile(FileIdx, File, MaxLength);
578 }
579
580 return false;
581 }
582
ExtractFile(int FileIndex,lcMemFile & File,quint32 MaxLength)583 bool lcZipFile::ExtractFile(int FileIndex, lcMemFile& File, quint32 MaxLength)
584 {
585 QMutexLocker Lock(&mMutex);
586
587 quint32 SizeVar;
588 quint64 OffsetLocalExtraField;
589 quint32 SizeLocalExtraField;
590 const lcZipFileInfo& FileInfo = mFiles[FileIndex];
591
592 if (!CheckFileCoherencyHeader(FileIndex, &SizeVar, &OffsetLocalExtraField, &SizeLocalExtraField))
593 return false;
594
595 const int BufferSize = 16384;
596 char ReadBuffer[BufferSize];
597 z_stream Stream;
598 quint32 Crc32;
599 quint64 PosInZipfile;
600 quint64 RestReadCompressed;
601 quint64 RestReadUncompressed;
602
603 Crc32 = 0;
604 Stream.total_out = 0;
605
606 if (FileInfo.compression_method == Z_DEFLATED)
607 {
608 Stream.zalloc = (alloc_func)0;
609 Stream.zfree = (free_func)0;
610 Stream.opaque = (voidpf)0;
611 Stream.next_in = 0;
612 Stream.avail_in = 0;
613
614 int err = inflateInit2(&Stream, -MAX_WBITS);
615 if (err != Z_OK)
616 return false;
617 }
618
619 RestReadCompressed = FileInfo.compressed_size;
620 RestReadUncompressed = FileInfo.uncompressed_size;
621 PosInZipfile = FileInfo.offset_curfile + 0x1e + SizeVar;
622
623 Stream.avail_in = (uInt)0;
624
625 quint32 Length = lcMin((quint32)FileInfo.uncompressed_size, MaxLength);
626 File.SetLength(Length);
627 File.Seek(0, SEEK_SET);
628
629 Stream.next_in = (Bytef*)ReadBuffer;
630 Stream.next_out = (Bytef*)File.mBuffer;
631 Stream.avail_out = Length;
632
633 quint32 Read = 0;
634
635 while (Stream.avail_out > 0)
636 {
637 if ((Stream.avail_in == 0) && (RestReadCompressed > 0))
638 {
639 quint32 ReadThis = BufferSize;
640
641 if (RestReadCompressed < ReadThis)
642 ReadThis = (quint32)RestReadCompressed;
643
644 if (ReadThis == 0)
645 return false;
646
647 mFile->Seek(PosInZipfile + mBytesBeforeZipFile, SEEK_SET);
648 if (mFile->ReadBuffer(ReadBuffer, ReadThis) != ReadThis)
649 return false;
650
651 PosInZipfile += ReadThis;
652
653 RestReadCompressed -= ReadThis;
654
655 Stream.next_in = (Bytef*)ReadBuffer;
656 Stream.avail_in = (uInt)ReadThis;
657 }
658
659 if (FileInfo.compression_method == 0)
660 {
661 quint32 DoCopy, i;
662
663 if ((Stream.avail_in == 0) && (RestReadCompressed == 0))
664 return (Read == 0) ? false : true;
665
666 if (Stream.avail_out < Stream.avail_in)
667 DoCopy = Stream.avail_out;
668 else
669 DoCopy = Stream.avail_in;
670
671 for (i = 0; i < DoCopy; i++)
672 *(Stream.next_out+i) = *(Stream.next_in+i);
673
674 Crc32 = crc32(Crc32, Stream.next_out, DoCopy);
675 RestReadUncompressed -= DoCopy;
676 Stream.avail_in -= DoCopy;
677 Stream.avail_out -= DoCopy;
678 Stream.next_out += DoCopy;
679 Stream.next_in += DoCopy;
680 Stream.total_out += DoCopy;
681 Read += DoCopy;
682 }
683 else
684 {
685 quint64 TotalOutBefore, TotalOutAfter;
686 const Bytef *bufBefore;
687 quint64 OutThis;
688 int flush = Z_SYNC_FLUSH;
689
690 TotalOutBefore = Stream.total_out;
691 bufBefore = Stream.next_out;
692
693 int err = inflate(&Stream,flush);
694
695 if ((err >= 0) && (Stream.msg != nullptr))
696 err = Z_DATA_ERROR;
697
698 TotalOutAfter = Stream.total_out;
699 OutThis = TotalOutAfter - TotalOutBefore;
700
701 Crc32 = crc32(Crc32, bufBefore, (uInt)(OutThis));
702
703 RestReadUncompressed -= OutThis;
704
705 Read += (uInt)(TotalOutAfter - TotalOutBefore);
706
707 if (err != Z_OK)
708 {
709 inflateEnd(&Stream);
710
711 if (RestReadUncompressed == 0)
712 {
713 if (Crc32 != FileInfo.crc)
714 return false;
715 }
716
717 if (err == Z_STREAM_END)
718 return (Read == 0) ? false : true;
719
720 return false;
721 }
722 }
723 }
724
725 if (FileInfo.compression_method == Z_DEFLATED)
726 inflateEnd(&Stream);
727
728 return true;
729 }
730