OpenShot Audio Library | OpenShotAudio  0.6.0
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 
23 namespace juce
24 {
25 
26 inline uint16 readUnalignedLittleEndianShort (const void* buffer)
27 {
28  auto data = readUnaligned<uint16> (buffer);
29  return ByteOrder::littleEndianShort (&data);
30 }
31 
32 inline uint32 readUnalignedLittleEndianInt (const void* buffer)
33 {
34  auto data = readUnaligned<uint32> (buffer);
35  return ByteOrder::littleEndianInt (&data);
36 }
37 
38 struct 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 //==============================================================================
74 static 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 
125 static 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 //==============================================================================
139 struct 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 
227 private:
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 //==============================================================================
240 ZipFile::ZipFile (InputStream* stream, bool deleteStreamWhenDestroyed)
241  : inputStream (stream)
242 {
243  if (deleteStreamWhenDestroyed)
244  streamToDelete.reset (inputStream);
245 
246  init();
247 }
248 
249 ZipFile::ZipFile (InputStream& stream) : inputStream (&stream)
250 {
251  init();
252 }
253 
254 ZipFile::ZipFile (const File& file) : inputSource (new FileInputSource (file))
255 {
256  init();
257 }
258 
259 ZipFile::ZipFile (InputSource* source) : inputSource (source)
260 {
261  init();
262 }
263 
265 {
266  entries.clear();
267 }
268 
269 #if JUCE_DEBUG
270 ZipFile::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 //==============================================================================
282 int ZipFile::getNumEntries() const noexcept
283 {
284  return entries.size();
285 }
286 
287 const 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 
295 int 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 
309 const 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 //==============================================================================
352 void 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 
401 Result 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 
415 Result 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 
423 Result 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 //==============================================================================
491 struct 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 
551 private:
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 
618 void 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,
622  file.getLastModificationTime()));
623 }
624 
625 void 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 
632 bool 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 
673 struct 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 
767 static ZIPTests zipTests;
768 
769 #endif
770 
771 } // namespace juce
constexpr static uint32 littleEndianInt(const void *bytes) noexcept
constexpr static uint16 littleEndianShort(const void *bytes) noexcept
bool failedToOpen() const noexcept
Time getLastModificationTime() const
Definition: juce_File.cpp:538
String getFileName() const
Definition: juce_File.cpp:372
File getChildFile(StringRef relativeOrAbsolutePath) const
Definition: juce_File.cpp:420
bool createSymbolicLink(const File &linkFileToCreate, bool overwriteExisting) const
Definition: juce_File.cpp:1002
static juce_wchar getSeparatorChar()
File getParentDirectory() const
Definition: juce_File.cpp:358
ValueType & getReference(KeyTypeParameter keyToLookFor)
Definition: juce_HashMap.h:185
virtual bool setPosition(int64 newPosition)=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)
int size() const noexcept
void clear(bool deleteObjects=true)
ObjectClass * getUnchecked(int index) const noexcept
ObjectClass ** end() noexcept
ObjectClass ** begin() noexcept
ObjectClass * add(ObjectClass *newObject)
static Result fail(const String &errorMessage) noexcept
Definition: juce_Result.cpp:65
static Result ok() noexcept
Definition: juce_Result.h:61
bool isEmpty() const noexcept
Definition: juce_String.h:310
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
bool isNotEmpty() const noexcept
Definition: juce_String.h:316
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Definition: juce_Time.cpp:233
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()