Merge pull request #25959 from neo1973/TagLib_deprecation_warnings
[xbmc.git] / lib / libUPnP / Neptune / Source / Core / NptZip.cpp
blob0ee50fdf6dc7177bc3e401e52bcfd2661e3cf854
1 /*****************************************************************
3 | Neptune - Zip Support
5 | Copyright (c) 2002-2008, Axiomatic Systems, LLC.
6 | All rights reserved.
8 | Redistribution and use in source and binary forms, with or without
9 | modification, are permitted provided that the following conditions are met:
10 | * Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 | * Neither the name of Axiomatic Systems nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
19 | THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY
20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 ****************************************************************/
33 /*----------------------------------------------------------------------
34 | includes
35 +---------------------------------------------------------------------*/
36 #include "NptConfig.h"
37 #include "NptZip.h"
38 #include "NptLogging.h"
39 #include "NptUtils.h"
41 #if defined(NPT_CONFIG_ENABLE_ZIP)
42 #include "zlib.h"
43 #endif
45 /*----------------------------------------------------------------------
46 | logging
47 +---------------------------------------------------------------------*/
48 NPT_SET_LOCAL_LOGGER("neptune.zip")
50 /*----------------------------------------------------------------------
51 | constants
52 +---------------------------------------------------------------------*/
53 static const NPT_UInt32 NPT_ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;
54 static const NPT_UInt32 NPT_ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06064b50;
55 static const NPT_UInt32 NPT_ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE = 0x07064b50;
56 static const NPT_UInt32 NPT_ZIP_CENTRAL_FILE_HEADER_SIGNATURE = 0x02014b50;
57 //static const NPT_UInt32 NPT_ZIP_LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
58 static const NPT_UInt16 NPT_ZIP_EXT_DATA_TYPE_ZIP64 = 0x0001;
60 static const NPT_UInt32 NPT_ZIP_MAX_DIRECTORY_SIZE = 0x1000000; // 16 MB
61 static const NPT_UInt32 NPT_ZIP_MAX_ENTRY_COUNT = 0x100000; // 1M entries
63 /*----------------------------------------------------------------------
64 | NPT_ZipFile::NPT_ZipFile
65 +---------------------------------------------------------------------*/
66 NPT_ZipFile::NPT_ZipFile()
70 /*----------------------------------------------------------------------
71 | NPT_ZipFile::Parse
72 +---------------------------------------------------------------------*/
73 NPT_Result
74 NPT_ZipFile::Parse(NPT_InputStream& stream, NPT_ZipFile*& file)
76 // defautl return value
77 file = NULL;
79 // check that we know the size of the stream
80 NPT_LargeSize stream_size = 0;
81 NPT_Result result = stream.GetSize(stream_size);
82 if (NPT_FAILED(result)) {
83 NPT_LOG_WARNING_1("cannot get stream size (%d)", result);
84 return result;
86 if (stream_size < 22) {
87 NPT_LOG_WARNING("input stream too short");
88 return NPT_ERROR_INVALID_FORMAT;
91 // seek to the most likely start of the end of central directory record
92 unsigned int max_eocdr_size = 22+65536;
93 if (max_eocdr_size > stream_size) {
94 max_eocdr_size = (unsigned int)stream_size;
96 unsigned char eocdr[22];
97 bool record_found = false;
98 NPT_Position position = 0;
99 for (unsigned int i=0; i<max_eocdr_size; i++) {
100 position = stream_size-22-i;
101 result = stream.Seek(position);
102 if (NPT_FAILED(result)) {
103 NPT_LOG_WARNING_1("seek failed (%d)", result);
104 return result;
106 result = stream.ReadFully(eocdr, 22);
107 if (NPT_FAILED(result)) {
108 NPT_LOG_WARNING_1("read failed (%d)", result);
109 return result;
111 NPT_UInt32 signature = NPT_BytesToInt32Le(eocdr);
112 if (signature == NPT_ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
113 record_found = true;
114 break;
117 if (!record_found) {
118 NPT_LOG_WARNING("eocd record not found at end of stream");
119 return NPT_ERROR_INVALID_FORMAT;
122 // parse the eocdr
123 NPT_UInt32 this_disk = NPT_BytesToInt16Le(&eocdr[ 4]);
124 NPT_UInt32 start_disk = NPT_BytesToInt16Le(&eocdr[ 6]);
125 NPT_UInt64 this_disk_entry_count = NPT_BytesToInt16Le(&eocdr[ 8]);
126 NPT_UInt64 total_entry_count = NPT_BytesToInt16Le(&eocdr[10]);
127 NPT_UInt64 central_directory_size = NPT_BytesToInt32Le(&eocdr[12]);
128 NPT_Position central_directory_offset = NPT_BytesToInt32Le(&eocdr[16]);
130 // format check
131 if (this_disk != 0 || start_disk != 0) {
132 return NPT_ERROR_NOT_SUPPORTED;
134 if (this_disk_entry_count != total_entry_count) {
135 return NPT_ERROR_NOT_SUPPORTED;
138 // check if this is a zip64 file
139 if (central_directory_offset == 0xFFFFFFFF) {
140 unsigned char zip64_locator[20];
141 result = stream.Seek(position-20);
142 if (NPT_FAILED(result)) {
143 NPT_LOG_WARNING_1("seek failed (%d)", result);
144 return result;
146 result = stream.ReadFully(zip64_locator, 20);
147 if (NPT_FAILED(result)) {
148 NPT_LOG_WARNING_1("read failed (%d)", result);
149 return result;
152 NPT_UInt32 signature = NPT_BytesToInt32Le(&zip64_locator[0]);
153 if (signature != NPT_ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE) {
154 NPT_LOG_WARNING("zip64 directory locator signature not found");
155 return NPT_ERROR_INVALID_FORMAT;
157 NPT_UInt32 zip64_disk_start = NPT_BytesToInt32Le(&zip64_locator[ 4]);
158 NPT_UInt64 zip64_directory_offset = NPT_BytesToInt64Le(&zip64_locator[ 8]);
159 NPT_UInt32 zip64_disk_count = NPT_BytesToInt32Le(&zip64_locator[16]);
161 // format check
162 if (zip64_disk_start != 0 || zip64_disk_count != 1) {
163 return NPT_ERROR_NOT_SUPPORTED;
166 // size check
167 if (zip64_directory_offset > stream_size) {
168 NPT_LOG_WARNING("zip64 directory offset too large");
169 return NPT_ERROR_INVALID_FORMAT;
172 // load and parse the eocdr64
173 unsigned char eocdr64[56];
174 result = stream.Seek(zip64_directory_offset);
175 if (NPT_FAILED(result)) {
176 NPT_LOG_WARNING_1("seek failed (%d)", result);
177 return result;
179 result = stream.ReadFully(eocdr64, 56);
180 if (NPT_FAILED(result)) {
181 NPT_LOG_WARNING_1("read failed (%d)", result);
182 return result;
185 signature = NPT_BytesToInt32Le(&eocdr64[0]);
186 if (signature != NPT_ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
187 NPT_LOG_WARNING("zip64 directory signature not found");
188 return NPT_ERROR_INVALID_FORMAT;
191 this_disk = NPT_BytesToInt32Le(&eocdr64[16]);
192 start_disk = NPT_BytesToInt32Le(&eocdr64[20]);
193 this_disk_entry_count = NPT_BytesToInt64Le(&eocdr64[24]);
194 total_entry_count = NPT_BytesToInt64Le(&eocdr64[32]);
195 central_directory_size = NPT_BytesToInt64Le(&eocdr64[40]);
196 central_directory_offset = NPT_BytesToInt64Le(&eocdr64[48]);
199 // format check
200 if (this_disk != 0 || start_disk != 0) {
201 return NPT_ERROR_NOT_SUPPORTED;
203 if (this_disk_entry_count != total_entry_count) {
204 return NPT_ERROR_NOT_SUPPORTED;
207 // check that the size looks reasonable
208 if (central_directory_size > NPT_ZIP_MAX_DIRECTORY_SIZE) {
209 NPT_LOG_WARNING("central directory larger than max supported");
210 return NPT_ERROR_OUT_OF_RANGE;
212 if (total_entry_count > NPT_ZIP_MAX_ENTRY_COUNT) {
213 NPT_LOG_WARNING("central directory larger than max supported");
214 return NPT_ERROR_OUT_OF_RANGE;
217 // read the central directory
218 NPT_DataBuffer central_directory_buffer;
219 result = central_directory_buffer.SetDataSize((NPT_Size)central_directory_size);
220 if (NPT_FAILED(result)) {
221 NPT_LOG_WARNING_1("central directory too large (%lld)", central_directory_size);
222 return result;
224 result = stream.Seek(central_directory_offset);
225 if (NPT_FAILED(result)) {
226 NPT_LOG_WARNING_1("seek failed (%d)", result);
227 return result;
229 result = stream.ReadFully(central_directory_buffer.UseData(), (NPT_Size)central_directory_size);
230 if (NPT_FAILED(result)) {
231 NPT_LOG_WARNING_1("failed to read central directory (%d)", result);
232 return result;
235 // create a new file object
236 file = new NPT_ZipFile();
237 file->m_Entries.Reserve((NPT_Cardinal)total_entry_count);
239 // parse all entries
240 const unsigned char* buffer = (const unsigned char*)central_directory_buffer.GetData();
241 NPT_Size buffer_size = central_directory_buffer.GetDataSize();
242 for (unsigned int i=0; i<total_entry_count; i++) {
243 if (buffer_size < 4) break;
244 NPT_UInt32 signature = NPT_BytesToInt32Le(buffer);
245 if (signature != NPT_ZIP_CENTRAL_FILE_HEADER_SIGNATURE) {
246 NPT_LOG_WARNING("unexpected signature in central directory");
247 break;
250 NPT_ZipFile::Entry entry(buffer, buffer_size);
251 if (entry.m_DirectoryEntrySize == 0) {
252 NPT_LOG_WARNING("invalid entry data");
253 break;
255 if (entry.m_DirectoryEntrySize > central_directory_size ||
256 entry.m_DirectoryEntrySize > buffer_size) {
257 NPT_LOG_WARNING_1("entry size too large (%d)", entry.m_DirectoryEntrySize);
258 break;
261 file->GetEntries().Add(entry);
263 central_directory_size -= entry.m_DirectoryEntrySize;
264 buffer += entry.m_DirectoryEntrySize;
265 buffer_size -= entry.m_DirectoryEntrySize;
268 return NPT_SUCCESS;
271 /*----------------------------------------------------------------------
272 | NPT_ZipFile::GetInputStream
273 +---------------------------------------------------------------------*/
274 NPT_Result
275 NPT_ZipFile::GetInputStream(Entry& entry, NPT_InputStreamReference& zip_stream, NPT_InputStream*& file_stream)
277 // default return value
278 file_stream = NULL;
280 // we don't support encrypted files
281 if (entry.m_Flags & NPT_ZIP_FILE_FLAG_ENCRYPTED) {
282 return NPT_ERROR_NOT_SUPPORTED;
285 // check that we support the compression method
286 #if NPT_CONFIG_ENABLE_ZIP
287 if (entry.m_CompressionMethod != NPT_ZIP_FILE_COMPRESSION_METHOD_NONE &&
288 entry.m_CompressionMethod != NPT_ZIP_FILE_COMPRESSION_METHOD_DEFLATE) {
289 return NPT_ERROR_NOT_SUPPORTED;
291 #else
292 if (entry.m_CompressionMethod != NPT_ZIP_FILE_COMPRESSION_METHOD_NONE) {
293 return NPT_ERROR_NOT_SUPPORTED;
295 #endif
297 // seek to the start of the file entry
298 NPT_Result result = zip_stream->Seek(entry.m_RelativeOffset);
299 if (NPT_FAILED(result)) {
300 NPT_LOG_WARNING_1("seek failed (%d)", result);
301 return result;
304 // read the fixed part of the header
305 unsigned char header[30];
306 result = zip_stream->ReadFully(header, 30);
307 if (NPT_FAILED(result)) {
308 NPT_LOG_WARNING_1("read failed (%d)", result);
309 return result;
312 NPT_UInt16 file_name_length = NPT_BytesToInt16Le(&header[26]);
313 NPT_UInt16 extra_field_length = NPT_BytesToInt16Le(&header[28]);
315 unsigned int header_size = 30+file_name_length+extra_field_length;
316 NPT_LargeSize zip_stream_size = 0;
317 zip_stream->GetSize(zip_stream_size);
318 if (entry.m_RelativeOffset+header_size+entry.m_CompressedSize > zip_stream_size) {
319 // something's wrong here
320 return NPT_ERROR_INVALID_FORMAT;
323 file_stream = new NPT_SubInputStream(zip_stream, entry.m_RelativeOffset+header_size, entry.m_CompressedSize);
325 #if NPT_CONFIG_ENABLE_ZIP
326 if (entry.m_CompressionMethod == NPT_ZIP_FILE_COMPRESSION_METHOD_DEFLATE) {
327 NPT_InputStreamReference file_stream_ref(file_stream);
328 file_stream = new NPT_ZipInflatingInputStream(file_stream_ref, true);
330 #endif
332 return NPT_SUCCESS;
335 /*----------------------------------------------------------------------
336 | NPT_ZipFile::Entry::Entry
337 +---------------------------------------------------------------------*/
338 NPT_ZipFile::Entry::Entry(const unsigned char* data, NPT_Size data_available) :
339 m_Flags(0),
340 m_CompressionMethod(0),
341 m_Crc32(0),
342 m_CompressedSize(0),
343 m_UncompressedSize(0),
344 m_DiskNumber(0),
345 m_InternalFileAttributes(0),
346 m_ExternalFileAttributes(0),
347 m_RelativeOffset(0),
348 m_DirectoryEntrySize(0)
350 if (data_available < 46) return;
352 m_Flags = NPT_BytesToInt16Le(data+ 8);
353 m_CompressionMethod = NPT_BytesToInt16Le(data+10);
354 m_Crc32 = NPT_BytesToInt32Le(data+16);
355 m_CompressedSize = NPT_BytesToInt32Le(data+20);
356 m_UncompressedSize = NPT_BytesToInt32Le(data+24);
357 m_DiskNumber = NPT_BytesToInt16Le(data+34);
358 m_InternalFileAttributes = NPT_BytesToInt32Le(data+36);
359 m_ExternalFileAttributes = NPT_BytesToInt32Le(data+38);
360 m_RelativeOffset = NPT_BytesToInt32Le(data+42);
362 NPT_UInt16 file_name_length = NPT_BytesToInt16Le(data+28);
363 NPT_UInt16 extra_field_length = NPT_BytesToInt16Le(data+30);
364 NPT_UInt16 file_comment_length = NPT_BytesToInt16Le(data+32);
366 m_DirectoryEntrySize = 46+file_name_length+extra_field_length+file_comment_length;
367 if (m_DirectoryEntrySize > data_available) {
368 m_DirectoryEntrySize = 0;
369 return;
372 // extract the file name
373 m_Name.Assign((const char*)data+46, file_name_length);
375 // check for a zip64 extension
376 const unsigned char* ext_data = data+46+file_name_length;
377 unsigned int ext_data_size = extra_field_length;
378 while (ext_data_size >= 4) {
379 unsigned int ext_id = NPT_BytesToInt16Le(ext_data);
380 unsigned int ext_size = NPT_BytesToInt16Le(ext_data+2);
382 if (ext_id == NPT_ZIP_EXT_DATA_TYPE_ZIP64) {
383 const unsigned char* local_data = ext_data+4;
384 if (m_UncompressedSize == 0xFFFFFFFF) {
385 m_UncompressedSize = NPT_BytesToInt64Le(local_data);
386 local_data += 8;
388 if (m_CompressedSize == 0xFFFFFFFF) {
389 m_CompressedSize = NPT_BytesToInt64Le(local_data);
390 local_data += 8;
392 if (m_RelativeOffset == 0xFFFFFFFF) {
393 m_RelativeOffset = NPT_BytesToInt64Le(local_data);
394 local_data += 8;
396 if (m_DiskNumber == 0xFFFF) {
397 m_DiskNumber = NPT_BytesToInt32Le(local_data);
398 local_data += 4;
402 ext_data += 4+ext_size;
403 if (ext_data_size >= 4+ext_size) {
404 ext_data_size -= 4+ext_size;
405 } else {
406 ext_data_size = 0;
411 #if defined(NPT_CONFIG_ENABLE_ZIP)
413 /*----------------------------------------------------------------------
414 | constants
415 +---------------------------------------------------------------------*/
416 const unsigned int NPT_ZIP_DEFAULT_BUFFER_SIZE = 4096;
418 /*----------------------------------------------------------------------
419 | NPT_Zip::MapError
420 +---------------------------------------------------------------------*/
421 NPT_Result
422 NPT_Zip::MapError(int err)
424 switch (err) {
425 case Z_OK: return NPT_SUCCESS;
426 case Z_STREAM_END: return NPT_ERROR_EOS;
427 case Z_DATA_ERROR:
428 case Z_STREAM_ERROR: return NPT_ERROR_INVALID_FORMAT;
429 case Z_MEM_ERROR: return NPT_ERROR_OUT_OF_MEMORY;
430 case Z_VERSION_ERROR: return NPT_ERROR_INTERNAL;
431 case Z_NEED_DICT: return NPT_ERROR_NOT_SUPPORTED;
432 default: return NPT_FAILURE;
436 /*----------------------------------------------------------------------
437 | NPT_ZipInflateState
438 +---------------------------------------------------------------------*/
439 class NPT_ZipInflateState {
440 public:
441 NPT_ZipInflateState(bool raw = false);
442 ~NPT_ZipInflateState();
443 z_stream m_Stream;
446 /*----------------------------------------------------------------------
447 | NPT_ZipInflateState::NPT_ZipInflateState
448 +---------------------------------------------------------------------*/
449 NPT_ZipInflateState::NPT_ZipInflateState(bool raw)
451 // initialize the state
452 NPT_SetMemory(&m_Stream, 0, sizeof(m_Stream));
454 // initialize the decompressor
455 inflateInit2(&m_Stream, raw?-15:15+32); // 15 = default window bits, +32 = automatic header
458 /*----------------------------------------------------------------------
459 | NPT_ZipInflateState::~NPT_ZipInflateState
460 +---------------------------------------------------------------------*/
461 NPT_ZipInflateState::~NPT_ZipInflateState()
463 inflateEnd(&m_Stream);
466 /*----------------------------------------------------------------------
467 | NPT_ZipDeflateState
468 +---------------------------------------------------------------------*/
469 class NPT_ZipDeflateState {
470 public:
471 NPT_ZipDeflateState(int compression_level,
472 NPT_Zip::Format format);
473 ~NPT_ZipDeflateState();
474 z_stream m_Stream;
477 /*----------------------------------------------------------------------
478 | NPT_ZipDeflateState::NPT_ZipDeflateState
479 +---------------------------------------------------------------------*/
480 NPT_ZipDeflateState::NPT_ZipDeflateState(int compression_level,
481 NPT_Zip::Format format)
483 // check parameters
484 if (compression_level < NPT_ZIP_COMPRESSION_LEVEL_DEFAULT ||
485 compression_level > NPT_ZIP_COMPRESSION_LEVEL_MAX) {
486 compression_level = NPT_ZIP_COMPRESSION_LEVEL_DEFAULT;
489 // initialize the state
490 NPT_SetMemory(&m_Stream, 0, sizeof(m_Stream));
492 // initialize the compressor
493 deflateInit2(&m_Stream,
494 compression_level,
495 Z_DEFLATED,
496 15 + (format == NPT_Zip::GZIP ? 16 : 0),
498 Z_DEFAULT_STRATEGY);
501 /*----------------------------------------------------------------------
502 | NPT_ZipDeflateState::~NPT_ZipDeflateState
503 +---------------------------------------------------------------------*/
504 NPT_ZipDeflateState::~NPT_ZipDeflateState()
506 deflateEnd(&m_Stream);
509 /*----------------------------------------------------------------------
510 | NPT_ZipInflatingInputStream::NPT_ZipInflatingInputStream
511 +---------------------------------------------------------------------*/
512 NPT_ZipInflatingInputStream::NPT_ZipInflatingInputStream(NPT_InputStreamReference& source, bool raw) :
513 m_Source(source),
514 m_Position(0),
515 m_State(new NPT_ZipInflateState(raw)),
516 m_Buffer(NPT_ZIP_DEFAULT_BUFFER_SIZE)
520 /*----------------------------------------------------------------------
521 | NPT_ZipInflatingInputStream::~NPT_ZipInflatingInputStream
522 +---------------------------------------------------------------------*/
523 NPT_ZipInflatingInputStream::~NPT_ZipInflatingInputStream()
525 delete m_State;
528 /*----------------------------------------------------------------------
529 | NPT_ZipInflatingInputStream::Read
530 +---------------------------------------------------------------------*/
531 NPT_Result
532 NPT_ZipInflatingInputStream::Read(void* buffer,
533 NPT_Size bytes_to_read,
534 NPT_Size* bytes_read)
536 // check state and parameters
537 if (m_State == NULL) return NPT_ERROR_INVALID_STATE;
538 if (buffer == NULL) return NPT_ERROR_INVALID_PARAMETERS;
539 if (bytes_to_read == 0) return NPT_SUCCESS;
541 // default values
542 if (bytes_read) *bytes_read = 0;
544 // setup the output buffer
545 m_State->m_Stream.next_out = (Bytef*)buffer;
546 m_State->m_Stream.avail_out = (uInt)bytes_to_read;
548 while (m_State->m_Stream.avail_out) {
549 // decompress what we can
550 int err = inflate(&m_State->m_Stream, Z_NO_FLUSH);
552 if (err == Z_STREAM_END) {
553 // we decompressed everything
554 break;
555 } else if (err == Z_OK) {
556 // we got something
557 continue;
558 } else if (err == Z_BUF_ERROR) {
559 // we need more input data
560 NPT_Size input_bytes_read = 0;
561 NPT_Result result = m_Source->Read(m_Buffer.UseData(), m_Buffer.GetBufferSize(), &input_bytes_read);
562 if (NPT_FAILED(result)) return result;
564 // setup the input buffer
565 m_Buffer.SetDataSize(input_bytes_read);
566 m_State->m_Stream.next_in = m_Buffer.UseData();
567 m_State->m_Stream.avail_in = m_Buffer.GetDataSize();
569 } else {
570 return NPT_Zip::MapError(err);
574 // report how much we could decompress
575 NPT_Size progress = bytes_to_read - m_State->m_Stream.avail_out;
576 if (bytes_read) {
577 *bytes_read = progress;
579 m_Position += progress;
581 return progress == 0 ? NPT_ERROR_EOS:NPT_SUCCESS;
584 /*----------------------------------------------------------------------
585 | NPT_ZipInflatingInputStream::Seek
586 +---------------------------------------------------------------------*/
587 NPT_Result
588 NPT_ZipInflatingInputStream::Seek(NPT_Position /* offset */)
590 // we can't seek
591 return NPT_ERROR_NOT_SUPPORTED;
594 /*----------------------------------------------------------------------
595 | NPT_ZipInflatingInputStream::Tell
596 +---------------------------------------------------------------------*/
597 NPT_Result
598 NPT_ZipInflatingInputStream::Tell(NPT_Position& offset)
600 offset = m_Position;
601 return NPT_SUCCESS;
604 /*----------------------------------------------------------------------
605 | NPT_ZipInflatingInputStream::GetSize
606 +---------------------------------------------------------------------*/
607 NPT_Result
608 NPT_ZipInflatingInputStream::GetSize(NPT_LargeSize& size)
610 // the size is not predictable
611 size = 0;
612 return NPT_ERROR_NOT_SUPPORTED;
615 /*----------------------------------------------------------------------
616 | NPT_ZipInflatingInputStream::GetAvailable
617 +---------------------------------------------------------------------*/
618 NPT_Result
619 NPT_ZipInflatingInputStream::GetAvailable(NPT_LargeSize& available)
621 // we don't know
622 available = 0;
623 return NPT_SUCCESS;
626 /*----------------------------------------------------------------------
627 | NPT_ZipDeflatingInputStream::NPT_ZipDeflatingInputStream
628 +---------------------------------------------------------------------*/
629 NPT_ZipDeflatingInputStream::NPT_ZipDeflatingInputStream(
630 NPT_InputStreamReference& source,
631 int compression_level,
632 NPT_Zip::Format format) :
633 m_Source(source),
634 m_Position(0),
635 m_Eos(false),
636 m_State(new NPT_ZipDeflateState(compression_level, format)),
637 m_Buffer(NPT_ZIP_DEFAULT_BUFFER_SIZE)
641 /*----------------------------------------------------------------------
642 | NPT_ZipDeflatingInputStream::~NPT_ZipDeflatingInputStream
643 +---------------------------------------------------------------------*/
644 NPT_ZipDeflatingInputStream::~NPT_ZipDeflatingInputStream()
646 delete m_State;
649 /*----------------------------------------------------------------------
650 | NPT_ZipDeflatingInputStream::Read
651 +---------------------------------------------------------------------*/
652 NPT_Result
653 NPT_ZipDeflatingInputStream::Read(void* buffer,
654 NPT_Size bytes_to_read,
655 NPT_Size* bytes_read)
657 // check state and parameters
658 if (m_State == NULL) return NPT_ERROR_INVALID_STATE;
659 if (buffer == NULL) return NPT_ERROR_INVALID_PARAMETERS;
660 if (bytes_to_read == 0) return NPT_SUCCESS;
662 // default values
663 if (bytes_read) *bytes_read = 0;
665 // setup the output buffer
666 m_State->m_Stream.next_out = (Bytef*)buffer;
667 m_State->m_Stream.avail_out = (uInt)bytes_to_read;
669 while (m_State->m_Stream.avail_out) {
670 // compress what we can
671 int err = deflate(&m_State->m_Stream, m_Eos?Z_FINISH:Z_NO_FLUSH);
673 if (err == Z_STREAM_END) {
674 // we compressed everything
675 break;
676 } else if (err == Z_OK) {
677 // we got something
678 continue;
679 } else if (err == Z_BUF_ERROR) {
680 // we need more input data
681 NPT_Size input_bytes_read = 0;
682 NPT_Result result = m_Source->Read(m_Buffer.UseData(), m_Buffer.GetBufferSize(), &input_bytes_read);
683 if (result == NPT_ERROR_EOS) {
684 m_Eos = true;
685 } else {
686 if (NPT_FAILED(result)) return result;
689 // setup the input buffer
690 m_Buffer.SetDataSize(input_bytes_read);
691 m_State->m_Stream.next_in = m_Buffer.UseData();
692 m_State->m_Stream.avail_in = m_Buffer.GetDataSize();
694 } else {
695 return NPT_Zip::MapError(err);
699 // report how much we could compress
700 NPT_Size progress = bytes_to_read - m_State->m_Stream.avail_out;
701 if (bytes_read) {
702 *bytes_read = progress;
704 m_Position += progress;
706 return progress == 0 ? NPT_ERROR_EOS:NPT_SUCCESS;
709 /*----------------------------------------------------------------------
710 | NPT_ZipDeflatingInputStream::Seek
711 +---------------------------------------------------------------------*/
712 NPT_Result
713 NPT_ZipDeflatingInputStream::Seek(NPT_Position /* offset */)
715 // we can't seek
716 return NPT_ERROR_NOT_SUPPORTED;
719 /*----------------------------------------------------------------------
720 | NPT_ZipDeflatingInputStream::Tell
721 +---------------------------------------------------------------------*/
722 NPT_Result
723 NPT_ZipDeflatingInputStream::Tell(NPT_Position& offset)
725 offset = m_Position;
726 return NPT_SUCCESS;
729 /*----------------------------------------------------------------------
730 | NPT_ZipDeflatingInputStream::GetSize
731 +---------------------------------------------------------------------*/
732 NPT_Result
733 NPT_ZipDeflatingInputStream::GetSize(NPT_LargeSize& size)
735 // the size is not predictable
736 size = 0;
737 return NPT_ERROR_NOT_SUPPORTED;
740 /*----------------------------------------------------------------------
741 | NPT_ZipDeflatingInputStream::GetAvailable
742 +---------------------------------------------------------------------*/
743 NPT_Result
744 NPT_ZipDeflatingInputStream::GetAvailable(NPT_LargeSize& available)
746 // we don't know
747 available = 0;
748 return NPT_SUCCESS;
751 /*----------------------------------------------------------------------
752 | NPT_Zip::Deflate
753 +---------------------------------------------------------------------*/
754 NPT_Result
755 NPT_Zip::Deflate(const NPT_DataBuffer& in,
756 NPT_DataBuffer& out,
757 int compression_level,
758 Format format /* = ZLIB */)
760 // default return state
761 out.SetDataSize(0);
763 // check parameters
764 if (compression_level < NPT_ZIP_COMPRESSION_LEVEL_DEFAULT ||
765 compression_level > NPT_ZIP_COMPRESSION_LEVEL_MAX) {
766 return NPT_ERROR_INVALID_PARAMETERS;
769 // setup the stream
770 z_stream stream;
771 NPT_SetMemory(&stream, 0, sizeof(stream));
772 stream.next_in = (Bytef*)in.GetData();
773 stream.avail_in = (uInt)in.GetDataSize();
775 // setup the memory functions
776 stream.zalloc = (alloc_func)0;
777 stream.zfree = (free_func)0;
778 stream.opaque = (voidpf)0;
780 // initialize the compressor
781 int err = deflateInit2(&stream,
782 compression_level,
783 Z_DEFLATED,
784 15 + (format == GZIP ? 16 : 0),
786 Z_DEFAULT_STRATEGY);
787 if (err != Z_OK) return MapError(err);
789 // reserve an output buffer known to be large enough
790 out.Reserve((NPT_Size)deflateBound(&stream, stream.avail_in) + (format==GZIP?10:0));
791 stream.next_out = out.UseData();
792 stream.avail_out = out.GetBufferSize();
794 // decompress
795 err = deflate(&stream, Z_FINISH);
796 if (err != Z_STREAM_END) {
797 deflateEnd(&stream);
798 return MapError(err);
801 // update the output size
802 out.SetDataSize((NPT_Size)stream.total_out);
804 // cleanup
805 err = deflateEnd(&stream);
806 return MapError(err);
809 /*----------------------------------------------------------------------
810 | NPT_Zip::Inflate
811 +---------------------------------------------------------------------*/
812 NPT_Result
813 NPT_Zip::Inflate(const NPT_DataBuffer& in,
814 NPT_DataBuffer& out,
815 bool raw)
817 // assume an output buffer twice the size of the input plus a bit
818 NPT_CHECK_WARNING(out.Reserve(32+2*in.GetDataSize()));
820 // setup the stream
821 z_stream stream;
822 stream.next_in = (Bytef*)in.GetData();
823 stream.avail_in = (uInt)in.GetDataSize();
824 stream.next_out = out.UseData();
825 stream.avail_out = (uInt)out.GetBufferSize();
827 // setup the memory functions
828 stream.zalloc = (alloc_func)0;
829 stream.zfree = (free_func)0;
830 stream.opaque = (voidpf)0;
832 // initialize the decompressor
833 int err = inflateInit2(&stream, raw?-15:15+32); // 15 = default window bits, +32 = automatic header
834 if (err != Z_OK) return MapError(err);
836 // decompress until the end
837 do {
838 err = inflate(&stream, Z_SYNC_FLUSH);
839 if (err == Z_STREAM_END || err == Z_OK || err == Z_BUF_ERROR) {
840 out.SetDataSize((NPT_Size)stream.total_out);
841 if ((err == Z_OK && stream.avail_out == 0) || err == Z_BUF_ERROR) {
842 // grow the output buffer
843 out.Reserve(out.GetBufferSize()*2);
844 stream.next_out = out.UseData()+stream.total_out;
845 stream.avail_out = out.GetBufferSize()-(NPT_Size)stream.total_out;
848 } while (err == Z_OK);
850 // check for errors
851 if (err != Z_STREAM_END) {
852 inflateEnd(&stream);
853 return MapError(err);
856 // cleanup
857 err = inflateEnd(&stream);
858 return MapError(err);
862 /*----------------------------------------------------------------------
863 | NPT_Zip::Deflate
864 +---------------------------------------------------------------------*/
865 NPT_Result
866 NPT_Zip::Deflate(NPT_File& in,
867 NPT_File& out,
868 int compression_level,
869 Format format /* = ZLIB */)
871 // check parameters
872 if (compression_level < NPT_ZIP_COMPRESSION_LEVEL_DEFAULT ||
873 compression_level > NPT_ZIP_COMPRESSION_LEVEL_MAX) {
874 return NPT_ERROR_INVALID_PARAMETERS;
877 NPT_InputStreamReference input;
878 NPT_CHECK(in.GetInputStream(input));
879 NPT_OutputStreamReference output;
880 NPT_CHECK(out.GetOutputStream(output));
882 NPT_ZipDeflatingInputStream deflating_stream(input, compression_level, format);
883 return NPT_StreamToStreamCopy(deflating_stream, *output.AsPointer());
886 #endif // NPT_CONFIG_ENABLE_ZIP