OpenShot Audio Library | OpenShotAudio 0.4.0
Loading...
Searching...
No Matches
juce_ZipFile.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26inline uint16 readUnalignedLittleEndianShort (const void* buffer)
27{
28 auto data = readUnaligned<uint16> (buffer);
29 return ByteOrder::littleEndianShort (&data);
30}
31
32inline uint32 readUnalignedLittleEndianInt (const void* buffer)
33{
34 auto data = readUnaligned<uint32> (buffer);
35 return ByteOrder::littleEndianInt (&data);
36}
37
38struct ZipFile::ZipEntryHolder
39{
40 ZipEntryHolder (const char* buffer, int fileNameLen)
41 {
42 isCompressed = readUnalignedLittleEndianShort (buffer + 10) != 0;
43 entry.fileTime = parseFileTime (readUnalignedLittleEndianShort (buffer + 12),
44 readUnalignedLittleEndianShort (buffer + 14));
45 compressedSize = (int64) readUnalignedLittleEndianInt (buffer + 20);
46 entry.uncompressedSize = (int64) readUnalignedLittleEndianInt (buffer + 24);
47 streamOffset = (int64) readUnalignedLittleEndianInt (buffer + 42);
48
49 entry.externalFileAttributes = readUnalignedLittleEndianInt (buffer + 38);
50 auto fileType = (entry.externalFileAttributes >> 28) & 0xf;
51 entry.isSymbolicLink = (fileType == 0xA);
52
53 entry.filename = String::fromUTF8 (buffer + 46, fileNameLen);
54 }
55
56 static Time parseFileTime (uint32 time, uint32 date) noexcept
57 {
58 auto year = (int) (1980 + (date >> 9));
59 auto month = (int) (((date >> 5) & 15) - 1);
60 auto day = (int) (date & 31);
61 auto hours = (int) time >> 11;
62 auto minutes = (int) ((time >> 5) & 63);
63 auto seconds = (int) ((time & 31) << 1);
64
65 return { year, month, day, hours, minutes, seconds };
66 }
67
68 ZipEntry entry;
69 int64 streamOffset, compressedSize;
70 bool isCompressed;
71};
72
73//==============================================================================
74static int64 findCentralDirectoryFileHeader (InputStream& input, int& numEntries)
75{
76 BufferedInputStream in (input, 8192);
77
78 in.setPosition (in.getTotalLength());
79 auto pos = in.getPosition();
80 auto lowestPos = jmax ((int64) 0, pos - 1048576);
81 char buffer[32] = {};
82
83 while (pos > lowestPos)
84 {
85 in.setPosition (pos - 22);
86 pos = in.getPosition();
87 memcpy (buffer + 22, buffer, 4);
88
89 if (in.read (buffer, 22) != 22)
90 return 0;
91
92 for (int i = 0; i < 22; ++i)
93 {
94 if (readUnalignedLittleEndianInt (buffer + i) == 0x06054b50)
95 {
96 in.setPosition (pos + i);
97 in.read (buffer, 22);
98 numEntries = readUnalignedLittleEndianShort (buffer + 10);
99 auto offset = (int64) readUnalignedLittleEndianInt (buffer + 16);
100
101 if (offset >= 4)
102 {
103 in.setPosition (offset);
104
105 // This is a workaround for some zip files which seem to contain the
106 // wrong offset for the central directory - instead of including the
107 // header, they point to the byte immediately after it.
108 if (in.readInt() != 0x02014b50)
109 {
110 in.setPosition (offset - 4);
111
112 if (in.readInt() == 0x02014b50)
113 offset -= 4;
114 }
115 }
116
117 return offset;
118 }
119 }
120 }
121
122 return 0;
123}
124
125static bool hasSymbolicPart (const File& root, const File& f)
126{
127 jassert (root == f || f.isAChildOf (root));
128
129 for (auto p = f; p != root; p = p.getParentDirectory())
130 {
131 if (p.isSymbolicLink())
132 return true;
133 }
134
135 return false;
136}
137
138//==============================================================================
139struct ZipFile::ZipInputStream final : public InputStream
140{
141 ZipInputStream (ZipFile& zf, const ZipFile::ZipEntryHolder& zei)
142 : file (zf),
143 zipEntryHolder (zei),
144 inputStream (zf.inputStream)
145 {
146 if (zf.inputSource != nullptr)
147 {
148 streamToDelete.reset (file.inputSource->createInputStream());
149 inputStream = streamToDelete.get();
150 }
151 else
152 {
153 #if JUCE_DEBUG
154 zf.streamCounter.numOpenStreams++;
155 #endif
156 }
157
158 char buffer[30];
159
160 if (inputStream != nullptr
161 && inputStream->setPosition (zei.streamOffset)
162 && inputStream->read (buffer, 30) == 30
163 && ByteOrder::littleEndianInt (buffer) == 0x04034b50)
164 {
165 headerSize = 30 + ByteOrder::littleEndianShort (buffer + 26)
166 + ByteOrder::littleEndianShort (buffer + 28);
167 }
168 }
169
170 ~ZipInputStream() override
171 {
172 #if JUCE_DEBUG
173 if (inputStream != nullptr && inputStream == file.inputStream)
174 file.streamCounter.numOpenStreams--;
175 #endif
176 }
177
178 int64 getTotalLength() override
179 {
180 return zipEntryHolder.compressedSize;
181 }
182
183 int read (void* buffer, int howMany) override
184 {
185 if (headerSize <= 0)
186 return 0;
187
188 howMany = (int) jmin ((int64) howMany, zipEntryHolder.compressedSize - pos);
189
190 if (inputStream == nullptr)
191 return 0;
192
193 int num;
194
195 if (inputStream == file.inputStream)
196 {
197 const ScopedLock sl (file.lock);
198 inputStream->setPosition (pos + zipEntryHolder.streamOffset + headerSize);
199 num = inputStream->read (buffer, howMany);
200 }
201 else
202 {
203 inputStream->setPosition (pos + zipEntryHolder.streamOffset + headerSize);
204 num = inputStream->read (buffer, howMany);
205 }
206
207 pos += num;
208 return num;
209 }
210
211 bool isExhausted() override
212 {
213 return headerSize <= 0 || pos >= zipEntryHolder.compressedSize;
214 }
215
216 int64 getPosition() override
217 {
218 return pos;
219 }
220
221 bool setPosition (int64 newPos) override
222 {
223 pos = jlimit ((int64) 0, zipEntryHolder.compressedSize, newPos);
224 return true;
225 }
226
227private:
228 ZipFile& file;
229 ZipEntryHolder zipEntryHolder;
230 int64 pos = 0;
231 int headerSize = 0;
232 InputStream* inputStream;
233 std::unique_ptr<InputStream> streamToDelete;
234
235 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipInputStream)
236};
237
238
239//==============================================================================
240ZipFile::ZipFile (InputStream* stream, bool deleteStreamWhenDestroyed)
241 : inputStream (stream)
242{
243 if (deleteStreamWhenDestroyed)
244 streamToDelete.reset (inputStream);
245
246 init();
247}
248
249ZipFile::ZipFile (InputStream& stream) : inputStream (&stream)
250{
251 init();
252}
253
254ZipFile::ZipFile (const File& file) : inputSource (new FileInputSource (file))
255{
256 init();
257}
258
259ZipFile::ZipFile (InputSource* source) : inputSource (source)
260{
261 init();
262}
263
265{
266 entries.clear();
267}
268
269#if JUCE_DEBUG
270ZipFile::OpenStreamCounter::~OpenStreamCounter()
271{
272 /* If you hit this assertion, it means you've created a stream to read one of the items in the
273 zipfile, but you've forgotten to delete that stream object before deleting the file..
274 Streams can't be kept open after the file is deleted because they need to share the input
275 stream that is managed by the ZipFile object.
276 */
277 jassert (numOpenStreams == 0);
278}
279#endif
280
281//==============================================================================
282int ZipFile::getNumEntries() const noexcept
283{
284 return entries.size();
285}
286
287const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const noexcept
288{
289 if (auto* zei = entries[index])
290 return &(zei->entry);
291
292 return nullptr;
293}
294
295int ZipFile::getIndexOfFileName (const String& fileName, bool ignoreCase) const noexcept
296{
297 for (int i = 0; i < entries.size(); ++i)
298 {
299 auto& entryFilename = entries.getUnchecked (i)->entry.filename;
300
301 if (ignoreCase ? entryFilename.equalsIgnoreCase (fileName)
302 : entryFilename == fileName)
303 return i;
304 }
305
306 return -1;
307}
308
309const ZipFile::ZipEntry* ZipFile::getEntry (const String& fileName, bool ignoreCase) const noexcept
310{
311 return getEntry (getIndexOfFileName (fileName, ignoreCase));
312}
313
315{
316 InputStream* stream = nullptr;
317
318 if (auto* zei = entries[index])
319 {
320 stream = new ZipInputStream (*this, *zei);
321
322 if (zei->isCompressed)
323 {
324 stream = new GZIPDecompressorInputStream (stream, true,
325 GZIPDecompressorInputStream::deflateFormat,
326 zei->entry.uncompressedSize);
327
328 // (much faster to unzip in big blocks using a buffer..)
329 stream = new BufferedInputStream (stream, 32768, true);
330 }
331 }
332
333 return stream;
334}
335
337{
338 for (int i = 0; i < entries.size(); ++i)
339 if (&entries.getUnchecked (i)->entry == &entry)
340 return createStreamForEntry (i);
341
342 return nullptr;
343}
344
346{
347 std::sort (entries.begin(), entries.end(),
348 [] (const ZipEntryHolder* e1, const ZipEntryHolder* e2) { return e1->entry.filename < e2->entry.filename; });
349}
350
351//==============================================================================
352void ZipFile::init()
353{
354 std::unique_ptr<InputStream> toDelete;
355 InputStream* in = inputStream;
356
357 if (inputSource != nullptr)
358 {
359 in = inputSource->createInputStream();
360 toDelete.reset (in);
361 }
362
363 if (in != nullptr)
364 {
365 int numEntries = 0;
366 auto centralDirectoryPos = findCentralDirectoryFileHeader (*in, numEntries);
367
368 if (centralDirectoryPos >= 0 && centralDirectoryPos < in->getTotalLength())
369 {
370 auto size = (size_t) (in->getTotalLength() - centralDirectoryPos);
371
372 in->setPosition (centralDirectoryPos);
373 MemoryBlock headerData;
374
375 if (in->readIntoMemoryBlock (headerData, (ssize_t) size) == size)
376 {
377 size_t pos = 0;
378
379 for (int i = 0; i < numEntries; ++i)
380 {
381 if (pos + 46 > size)
382 break;
383
384 auto* buffer = static_cast<const char*> (headerData.getData()) + pos;
385 auto fileNameLen = readUnalignedLittleEndianShort (buffer + 28u);
386
387 if (pos + 46 + fileNameLen > size)
388 break;
389
390 entries.add (new ZipEntryHolder (buffer, fileNameLen));
391
392 pos += 46u + fileNameLen
393 + readUnalignedLittleEndianShort (buffer + 30u)
394 + readUnalignedLittleEndianShort (buffer + 32u);
395 }
396 }
397 }
398 }
399}
400
401Result ZipFile::uncompressTo (const File& targetDirectory,
402 const bool shouldOverwriteFiles)
403{
404 for (int i = 0; i < entries.size(); ++i)
405 {
406 auto result = uncompressEntry (i, targetDirectory, shouldOverwriteFiles);
407
408 if (result.failed())
409 return result;
410 }
411
412 return Result::ok();
413}
414
415Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool shouldOverwriteFiles)
416{
417 return uncompressEntry (index,
418 targetDirectory,
419 shouldOverwriteFiles ? OverwriteFiles::yes : OverwriteFiles::no,
420 FollowSymlinks::no);
421}
422
423Result ZipFile::uncompressEntry (int index, const File& targetDirectory, OverwriteFiles overwriteFiles, FollowSymlinks followSymlinks)
424{
425 auto* zei = entries.getUnchecked (index);
426
427 #if JUCE_WINDOWS
428 auto entryPath = zei->entry.filename;
429 #else
430 auto entryPath = zei->entry.filename.replaceCharacter ('\\', '/');
431 #endif
432
433 if (entryPath.isEmpty())
434 return Result::ok();
435
436 auto targetFile = targetDirectory.getChildFile (entryPath);
437
438 if (! targetFile.isAChildOf (targetDirectory))
439 return Result::fail ("Entry " + entryPath + " is outside the target directory");
440
441 if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\'))
442 return targetFile.createDirectory(); // (entry is a directory, not a file)
443
444 std::unique_ptr<InputStream> in (createStreamForEntry (index));
445
446 if (in == nullptr)
447 return Result::fail ("Failed to open the zip file for reading");
448
449 if (targetFile.exists())
450 {
451 if (overwriteFiles == OverwriteFiles::no)
452 return Result::ok();
453
454 if (! targetFile.deleteFile())
455 return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName());
456 }
457
458 if (followSymlinks == FollowSymlinks::no && hasSymbolicPart (targetDirectory, targetFile.getParentDirectory()))
459 return Result::fail ("Parent directory leads through symlink for target file: " + targetFile.getFullPathName());
460
461 if (! targetFile.getParentDirectory().createDirectory())
462 return Result::fail ("Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName());
463
464 if (zei->entry.isSymbolicLink)
465 {
466 String originalFilePath (in->readEntireStreamAsString()
467 .replaceCharacter (L'/', File::getSeparatorChar()));
468
469 if (! File::createSymbolicLink (targetFile, originalFilePath, true))
470 return Result::fail ("Failed to create symbolic link: " + originalFilePath);
471 }
472 else
473 {
474 FileOutputStream out (targetFile);
475
476 if (out.failedToOpen())
477 return Result::fail ("Failed to write to target file: " + targetFile.getFullPathName());
478
479 out << *in;
480 }
481
482 targetFile.setCreationTime (zei->entry.fileTime);
483 targetFile.setLastModificationTime (zei->entry.fileTime);
484 targetFile.setLastAccessTime (zei->entry.fileTime);
485
486 return Result::ok();
487}
488
489
490//==============================================================================
491struct ZipFile::Builder::Item
492{
493 Item (const File& f, InputStream* s, int compression, const String& storedPath, Time time)
494 : file (f), stream (s), storedPathname (storedPath), fileTime (time), compressionLevel (compression)
495 {
496 symbolicLink = (file.exists() && file.isSymbolicLink());
497 }
498
499 bool writeData (OutputStream& target, const int64 overallStartPosition)
500 {
501 MemoryOutputStream compressedData ((size_t) file.getSize());
502
503 if (symbolicLink)
504 {
505 auto relativePath = file.getNativeLinkedTarget().replaceCharacter (File::getSeparatorChar(), L'/');
506
507 uncompressedSize = relativePath.length();
508
509 checksum = zlibNamespace::crc32 (0, (uint8_t*) relativePath.toRawUTF8(), (unsigned int) uncompressedSize);
510 compressedData << relativePath;
511 }
512 else if (compressionLevel > 0)
513 {
514 GZIPCompressorOutputStream compressor (compressedData, compressionLevel,
515 GZIPCompressorOutputStream::windowBitsRaw);
516 if (! writeSource (compressor))
517 return false;
518 }
519 else
520 {
521 if (! writeSource (compressedData))
522 return false;
523 }
524
525 compressedSize = (int64) compressedData.getDataSize();
526 headerStart = target.getPosition() - overallStartPosition;
527
528 target.writeInt (0x04034b50);
529 writeFlagsAndSizes (target);
530 target << storedPathname
531 << compressedData;
532
533 return true;
534 }
535
536 bool writeDirectoryEntry (OutputStream& target)
537 {
538 target.writeInt (0x02014b50);
539 target.writeShort (symbolicLink ? 0x0314 : 0x0014);
540 writeFlagsAndSizes (target);
541 target.writeShort (0); // comment length
542 target.writeShort (0); // start disk num
543 target.writeShort (0); // internal attributes
544 target.writeInt ((int) (symbolicLink ? 0xA1ED0000 : 0)); // external attributes
545 target.writeInt ((int) (uint32) headerStart);
546 target << storedPathname;
547
548 return true;
549 }
550
551private:
552 const File file;
553 std::unique_ptr<InputStream> stream;
554 String storedPathname;
555 Time fileTime;
556 int64 compressedSize = 0, uncompressedSize = 0, headerStart = 0;
557 int compressionLevel = 0;
558 unsigned long checksum = 0;
559 bool symbolicLink = false;
560
561 static void writeTimeAndDate (OutputStream& target, Time t)
562 {
563 target.writeShort ((short) (t.getSeconds() + (t.getMinutes() << 5) + (t.getHours() << 11)));
564 target.writeShort ((short) (t.getDayOfMonth() + ((t.getMonth() + 1) << 5) + ((t.getYear() - 1980) << 9)));
565 }
566
567 bool writeSource (OutputStream& target)
568 {
569 if (stream == nullptr)
570 {
571 stream = file.createInputStream();
572
573 if (stream == nullptr)
574 return false;
575 }
576
577 checksum = 0;
578 uncompressedSize = 0;
579 const int bufferSize = 4096;
580 HeapBlock<unsigned char> buffer (bufferSize);
581
582 while (! stream->isExhausted())
583 {
584 auto bytesRead = stream->read (buffer, bufferSize);
585
586 if (bytesRead < 0)
587 return false;
588
589 checksum = zlibNamespace::crc32 (checksum, buffer, (unsigned int) bytesRead);
590 target.write (buffer, (size_t) bytesRead);
591 uncompressedSize += bytesRead;
592 }
593
594 stream.reset();
595 return true;
596 }
597
598 void writeFlagsAndSizes (OutputStream& target) const
599 {
600 target.writeShort (10); // version needed
601 target.writeShort ((short) (1 << 11)); // this flag indicates UTF-8 filename encoding
602 target.writeShort ((! symbolicLink && compressionLevel > 0) ? (short) 8 : (short) 0); //symlink target path is not compressed
603 writeTimeAndDate (target, fileTime);
604 target.writeInt ((int) checksum);
605 target.writeInt ((int) (uint32) compressedSize);
606 target.writeInt ((int) (uint32) uncompressedSize);
607 target.writeShort (static_cast<short> (storedPathname.toUTF8().sizeInBytes() - 1));
608 target.writeShort (0); // extra field length
609 }
610
611 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Item)
612};
613
614//==============================================================================
617
618void ZipFile::Builder::addFile (const File& file, int compression, const String& path)
619{
620 items.add (new Item (file, nullptr, compression,
621 path.isEmpty() ? file.getFileName() : path,
623}
624
625void ZipFile::Builder::addEntry (InputStream* stream, int compression, const String& path, Time time)
626{
627 jassert (stream != nullptr); // must not be null!
628 jassert (path.isNotEmpty());
629 items.add (new Item ({}, stream, compression, path, time));
630}
631
632bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progress) const
633{
634 auto fileStart = target.getPosition();
635
636 for (int i = 0; i < items.size(); ++i)
637 {
638 if (progress != nullptr)
639 *progress = (i + 0.5) / items.size();
640
641 if (! items.getUnchecked (i)->writeData (target, fileStart))
642 return false;
643 }
644
645 auto directoryStart = target.getPosition();
646
647 for (auto* item : items)
648 if (! item->writeDirectoryEntry (target))
649 return false;
650
651 auto directoryEnd = target.getPosition();
652
653 target.writeInt (0x06054b50);
654 target.writeShort (0);
655 target.writeShort (0);
656 target.writeShort ((short) items.size());
657 target.writeShort ((short) items.size());
658 target.writeInt ((int) (directoryEnd - directoryStart));
659 target.writeInt ((int) (directoryStart - fileStart));
660 target.writeShort (0);
661
662 if (progress != nullptr)
663 *progress = 1.0;
664
665 return true;
666}
667
668
669//==============================================================================
670//==============================================================================
671#if JUCE_UNIT_TESTS
672
673struct ZIPTests final : public UnitTest
674{
675 ZIPTests()
676 : UnitTest ("ZIP", UnitTestCategories::compression)
677 {}
678
679 static MemoryBlock createZipMemoryBlock (const StringArray& entryNames)
680 {
681 ZipFile::Builder builder;
683
684 for (auto& entryName : entryNames)
685 {
686 auto& block = blocks.getReference (entryName);
687 MemoryOutputStream mo (block, false);
688 mo << entryName;
689 mo.flush();
690 builder.addEntry (new MemoryInputStream (block, false), 9, entryName, Time::getCurrentTime());
691 }
692
693 MemoryBlock data;
694 MemoryOutputStream mo (data, false);
695 builder.writeToStream (mo, nullptr);
696
697 return data;
698 }
699
700 void runZipSlipTest()
701 {
702 const std::map<String, bool> testCases = { { "a", true },
703#if JUCE_WINDOWS
704 { "C:/b", false },
705#else
706 { "/b", false },
707#endif
708 { "c/d", true },
709 { "../e/f", false },
710 { "../../g/h", false },
711 { "i/../j", true },
712 { "k/l/../", true },
713 { "m/n/../../", false },
714 { "o/p/../../../", false } };
715
716 StringArray entryNames;
717
718 for (const auto& testCase : testCases)
719 entryNames.add (testCase.first);
720
721 TemporaryFile tmpDir;
722 tmpDir.getFile().createDirectory();
723 auto data = createZipMemoryBlock (entryNames);
724 MemoryInputStream mi (data, false);
725 ZipFile zip (mi);
726
727 for (int i = 0; i < zip.getNumEntries(); ++i)
728 {
729 const auto result = zip.uncompressEntry (i, tmpDir.getFile());
730 const auto caseIt = testCases.find (zip.getEntry (i)->filename);
731
732 if (caseIt != testCases.end())
733 {
734 expect (result.wasOk() == caseIt->second,
735 zip.getEntry (i)->filename + " was unexpectedly " + (result.wasOk() ? "OK" : "not OK"));
736 }
737 else
738 {
739 expect (false);
740 }
741 }
742 }
743
744 void runTest() override
745 {
746 beginTest ("ZIP");
747
748 StringArray entryNames { "first", "second", "third" };
749 auto data = createZipMemoryBlock (entryNames);
750 MemoryInputStream mi (data, false);
751 ZipFile zip (mi);
752
753 expectEquals (zip.getNumEntries(), entryNames.size());
754
755 for (auto& entryName : entryNames)
756 {
757 auto* entry = zip.getEntry (entryName);
758 std::unique_ptr<InputStream> input (zip.createStreamForEntry (*entry));
759 expectEquals (input->readEntireStreamAsString(), entryName);
760 }
761
762 beginTest ("ZipSlip");
763 runZipSlipTest();
764 }
765};
766
767static ZIPTests zipTests;
768
769#endif
770
771} // namespace juce
static constexpr uint32 littleEndianInt(const void *bytes) noexcept
static constexpr uint16 littleEndianShort(const void *bytes) noexcept
bool failedToOpen() const noexcept
bool isSymbolicLink() const
Time getLastModificationTime() const
int64 getSize() const
String getFileName() const
File getChildFile(StringRef relativeOrAbsolutePath) const
bool createSymbolicLink(const File &linkFileToCreate, bool overwriteExisting) const
static juce_wchar getSeparatorChar()
File getParentDirectory() const
String getNativeLinkedTarget() const
bool exists() const
ValueType & getReference(KeyTypeParameter keyToLookFor)
virtual int64 getPosition()=0
virtual bool setPosition(int64 newPosition)=0
virtual bool isExhausted()=0
virtual int64 getTotalLength()=0
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
virtual int64 getPosition()=0
virtual bool writeShort(short value)
virtual bool writeInt(int value)
ObjectClass * add(ObjectClass *newObject)
static Result fail(const String &errorMessage) noexcept
static Result ok() noexcept
Definition juce_Result.h:61
bool isEmpty() const noexcept
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
bool isNotEmpty() const noexcept
static Time JUCE_CALLTYPE getCurrentTime() noexcept
void addEntry(InputStream *streamToRead, int compressionLevel, const String &storedPathName, Time fileModificationTime)
bool writeToStream(OutputStream &target, double *progress) const
void addFile(const File &fileToAdd, int compressionLevel, const String &storedPathName=String())
Result uncompressTo(const File &targetDirectory, bool shouldOverwriteFiles=true)
InputStream * createStreamForEntry(int index)
const ZipEntry * getEntry(int index) const noexcept
int getNumEntries() const noexcept
ZipFile(const File &file)
Result uncompressEntry(int index, const File &targetDirectory, bool shouldOverwriteFiles=true)
int getIndexOfFileName(const String &fileName, bool ignoreCase=false) const noexcept
void sortEntriesByFilename()