26inline uint16 readUnalignedLittleEndianShort (
const void* buffer)
28 auto data = readUnaligned<uint16> (buffer);
32inline uint32 readUnalignedLittleEndianInt (
const void* buffer)
34 auto data = readUnaligned<uint32> (buffer);
42 isCompressed = readUnalignedLittleEndianShort (buffer + 10) != 0;
43 entry.
fileTime = parseFileTime (readUnalignedLittleEndianShort (buffer + 12),
44 readUnalignedLittleEndianShort (buffer + 14));
45 compressedSize = (
int64) readUnalignedLittleEndianInt (buffer + 20);
47 streamOffset = (
int64) readUnalignedLittleEndianInt (buffer + 42);
61 int hours = time >> 11;
62 int minutes = (time >> 5) & 63;
63 int seconds = (
int) ((time & 31) << 1);
69 int64 streamOffset, compressedSize;
78 in.setPosition (
in.getTotalLength());
79 auto pos =
in.getPosition();
85 in.setPosition (pos - 22);
86 pos =
in.getPosition();
87 memcpy (buffer + 22, buffer, 4);
89 if (
in.read (buffer, 22) != 22)
92 for (
int i = 0; i < 22; ++i)
94 if (readUnalignedLittleEndianInt (buffer + i) == 0x06054b50)
96 in.setPosition (pos + i);
98 numEntries = readUnalignedLittleEndianShort (buffer + 10);
99 auto offset = (int64) readUnalignedLittleEndianInt (buffer + 16);
103 in.setPosition (offset);
108 if (
in.readInt() != 0x02014b50)
110 in.setPosition (offset - 4);
112 if (
in.readInt() == 0x02014b50)
130 zipEntryHolder (
zei),
131 inputStream (
zf.inputStream)
133 if (
zf.inputSource !=
nullptr)
135 streamToDelete.reset (file.inputSource->createInputStream());
136 inputStream = streamToDelete.get();
141 zf.streamCounter.numOpenStreams++;
147 if (inputStream !=
nullptr
149 && inputStream->
read (buffer, 30) == 30
160 if (inputStream !=
nullptr && inputStream == file.inputStream)
161 file.streamCounter.numOpenStreams--;
167 return zipEntryHolder.compressedSize;
177 if (inputStream ==
nullptr)
182 if (inputStream == file.inputStream)
185 inputStream->
setPosition (pos + zipEntryHolder.streamOffset + headerSize);
190 inputStream->
setPosition (pos + zipEntryHolder.streamOffset + headerSize);
210 pos = jlimit ((int64) 0, zipEntryHolder.compressedSize,
newPos);
220 std::unique_ptr<InputStream> streamToDelete;
228 : inputStream (stream)
231 streamToDelete.reset (inputStream);
257ZipFile::OpenStreamCounter::~OpenStreamCounter()
271 return entries.
size();
276 if (
auto*
zei = entries[index])
277 return &(
zei->entry);
284 for (
int i = 0; i < entries.size(); ++i)
298 return getEntry (getIndexOfFileName (
fileName, ignoreCase));
305 if (
auto*
zei = entries[index])
309 if (
zei->isCompressed)
312 GZIPDecompressorInputStream::deflateFormat,
313 zei->entry.uncompressedSize);
325 for (
int i = 0; i < entries.
size(); ++i)
334 std::sort (entries.
begin(), entries.
end(),
341 std::unique_ptr<InputStream>
toDelete;
344 if (inputSource !=
nullptr)
346 in = inputSource->createInputStream();
353 auto centralDirectoryPos = findCentralDirectoryFileHeader (*in, numEntries);
355 if (centralDirectoryPos >= 0 && centralDirectoryPos < in->getTotalLength())
357 auto size = (size_t) (in->
getTotalLength() - centralDirectoryPos);
360 MemoryBlock headerData;
366 for (
int i = 0; i < numEntries; ++i)
371 auto* buffer =
static_cast<const char*
> (headerData.getData()) + pos;
372 auto fileNameLen = readUnalignedLittleEndianShort (buffer + 28);
374 if (pos + 46 + fileNameLen > size)
377 entries.
add (
new ZipEntryHolder (buffer, fileNameLen));
379 pos += 46 + fileNameLen
380 + readUnalignedLittleEndianShort (buffer + 30)
381 + readUnalignedLittleEndianShort (buffer + 32);
391 for (
int i = 0; i < entries.
size(); ++i)
409 auto entryPath =
zei->entry.filename.replaceCharacter (
'\\',
'/');
418 return targetFile.createDirectory();
423 return Result::fail (
"Failed to open the zip file for reading");
425 if (targetFile.exists())
430 if (! targetFile.deleteFile())
431 return Result::fail (
"Failed to write to target file: " + targetFile.getFullPathName());
434 if (! targetFile.getParentDirectory().createDirectory())
435 return Result::fail (
"Failed to create target folder: " + targetFile.getParentDirectory().getFullPathName());
437 if (
zei->entry.isSymbolicLink)
449 if (
out.failedToOpen())
450 return Result::fail (
"Failed to write to target file: " + targetFile.getFullPathName());
455 targetFile.setCreationTime (
zei->entry.fileTime);
456 targetFile.setLastModificationTime (
zei->entry.fileTime);
457 targetFile.setLastAccessTime (
zei->entry.fileTime);
469 symbolicLink = (file.exists() && file.isSymbolicLink());
482 checksum = zlibNamespace::crc32 (0, (
uint8_t*)
relativePath.toRawUTF8(), (
unsigned int) uncompressedSize);
485 else if (compressionLevel > 0)
488 GZIPCompressorOutputStream::windowBitsRaw);
502 writeFlagsAndSizes (target);
503 target << storedPathname
512 target.
writeShort (symbolicLink ? 0x0314 : 0x0014);
513 writeFlagsAndSizes (target);
517 target.
writeInt ((
int) (symbolicLink ? 0xA1ED0000 : 0));
519 target << storedPathname;
526 std::unique_ptr<InputStream> stream;
529 int64 compressedSize = 0, uncompressedSize = 0, headerStart = 0;
530 int compressionLevel = 0;
531 unsigned long checksum = 0;
532 bool symbolicLink =
false;
536 target.
writeShort ((
short) (
t.getSeconds() + (
t.getMinutes() << 5) + (
t.getHours() << 11)));
537 target.
writeShort ((
short) (
t.getDayOfMonth() + ((
t.getMonth() + 1) << 5) + ((
t.getYear() - 1980) << 9)));
542 if (stream ==
nullptr)
546 if (stream ==
nullptr)
551 uncompressedSize = 0;
552 const int bufferSize = 4096;
555 while (! stream->isExhausted())
557 auto bytesRead = stream->read (buffer, bufferSize);
562 checksum = zlibNamespace::crc32 (checksum, buffer, (
unsigned int)
bytesRead);
575 target.
writeShort ((! symbolicLink && compressionLevel > 0) ? (
short) 8 : (
short) 0);
576 writeTimeAndDate (target, fileTime);
584 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (
Item)
600 jassert (stream !=
nullptr);
609 for (
int i = 0; i < items.size(); ++i)
611 if (progress !=
nullptr)
612 *progress = (i + 0.5) / items.
size();
614 if (! items.getUnchecked (i)->writeData (target,
fileStart))
620 for (
auto* item : items)
621 if (! item->writeDirectoryEntry (target))
635 if (progress !=
nullptr)
648 void runTest()
override
654 HashMap<String, MemoryBlock> blocks;
656 for (
auto& entryName : entryNames)
659 MemoryOutputStream mo (block,
false);
662 builder.
addEntry (
new MemoryInputStream (block,
false), 9, entryName, Time::getCurrentTime());
666 MemoryOutputStream mo (data,
false);
668 MemoryInputStream mi (data,
false);
672 expectEquals (zip.getNumEntries(), entryNames.size());
674 for (
auto& entryName : entryNames)
676 auto* entry = zip.getEntry (entryName);
677 std::unique_ptr<InputStream> input (zip.createStreamForEntry (*entry));
683static ZIPTests zipTests;
Holds a resizable array of primitive or copy-by-value objects.
ElementType getUnchecked(int index) const
Returns one of the elements in the array, without checking the index passed in.
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
int size() const noexcept
Returns the current number of elements in the array.
ElementType * begin() const noexcept
Returns a pointer to the first element in the array.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
ElementType & getReference(int index) const noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in.
void clear()
Removes all elements from the array.
ElementType * end() const noexcept
Returns a pointer to the element which follows the last element in the array.
static JUCE_CONSTEXPR uint16 littleEndianShort(const void *bytes) noexcept
Turns 2 bytes into a little-endian integer.
static JUCE_CONSTEXPR uint32 littleEndianInt(const void *bytes) noexcept
Turns 4 bytes into a little-endian integer.
size_t sizeInBytes() const noexcept
Returns the number of bytes that are used to represent this string.
An output stream that writes into a local file.
Represents a local file or directory.
Time getLastModificationTime() const
Returns the last modification time of this file.
String getFileName() const
Returns the last section of the pathname.
bool createSymbolicLink(const File &linkFileToCreate, bool overwriteExisting) const
Tries to create a symbolic link and returns a boolean to indicate success.
static juce_wchar getSeparatorChar()
The system-specific file separator character.
FileInputStream * createInputStream() const
Creates a stream to read from this file.
A stream which uses zlib to compress the data written into it.
Writes data to an internal memory buffer, which grows as required.
The base class for streams that write data to some kind of destination.
virtual bool write(const void *dataToWrite, size_t numberOfBytes)=0
Writes a block of data to the stream.
virtual int64 getPosition()=0
Returns the stream's current position.
virtual bool writeShort(short value)
Writes a 16-bit integer to the stream in a little-endian byte order.
virtual bool writeInt(int value)
Writes a 32-bit integer to the stream in a little-endian byte order.
Represents the 'success' or 'failure' of an operation, and holds an associated error message to descr...
static Result fail(const String &errorMessage) noexcept
Creates a 'failure' result.
static Result ok() noexcept
Creates and returns a 'successful' result.
A special array for holding a list of strings.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
CharPointer_UTF8 toUTF8() const
Returns a pointer to a UTF-8 version of this string.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Holds an absolute date and time.
This is a base class for classes that perform a unit test.
Used to create a new zip file.
Builder()
Creates an empty builder object.
void addEntry(InputStream *streamToRead, int compressionLevel, const String &storedPathName, Time fileModificationTime)
Adds a stream to the list of items which will be added to the archive.
bool writeToStream(OutputStream &target, double *progress) const
Generates the zip file, writing it to the specified stream.
void addFile(const File &fileToAdd, int compressionLevel, const String &storedPathName=String())
Adds a file to the list of items which will be added to the archive.
Decodes a ZIP file from a stream.
Result uncompressTo(const File &targetDirectory, bool shouldOverwriteFiles=true)
Uncompresses all of the files in the zip file.
InputStream * createStreamForEntry(int index)
Creates a stream that can read from one of the zip file's entries.
const ZipEntry * getEntry(int index) const noexcept
Returns a structure that describes one of the entries in the zip file.
int getNumEntries() const noexcept
Returns the number of items in the zip file.
ZipFile(const File &file)
Creates a ZipFile to read a specific file.
Result uncompressEntry(int index, const File &targetDirectory, bool shouldOverwriteFiles=true)
Uncompresses one of the entries from the zip file.
int getIndexOfFileName(const String &fileName, bool ignoreCase=false) const noexcept
Returns the index of the first entry with a given filename.
void sortEntriesByFilename()
Sorts the list of entries, based on the filename.
String filename
The name of the file, which may also include a partial pathname.
int64 uncompressedSize
The file's original size.
bool isSymbolicLink
True if the zip entry is a symbolic link.
Time fileTime
The last time the file was modified.
Contains information about one of the entries in a ZipFile.