1 /*****************************************************************
3 | Neptune - Zip Support
5 | Copyright (c) 2002-2008, Axiomatic Systems, LLC.
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 /*----------------------------------------------------------------------
35 +---------------------------------------------------------------------*/
36 #include "NptConfig.h"
38 #include "NptLogging.h"
41 #if defined(NPT_CONFIG_ENABLE_ZIP)
45 /*----------------------------------------------------------------------
47 +---------------------------------------------------------------------*/
48 NPT_SET_LOCAL_LOGGER("neptune.zip")
50 /*----------------------------------------------------------------------
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 /*----------------------------------------------------------------------
72 +---------------------------------------------------------------------*/
74 NPT_ZipFile::Parse(NPT_InputStream
& stream
, NPT_ZipFile
*& file
)
76 // defautl return value
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
);
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
);
106 result
= stream
.ReadFully(eocdr
, 22);
107 if (NPT_FAILED(result
)) {
108 NPT_LOG_WARNING_1("read failed (%d)", result
);
111 NPT_UInt32 signature
= NPT_BytesToInt32Le(eocdr
);
112 if (signature
== NPT_ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE
) {
118 NPT_LOG_WARNING("eocd record not found at end of stream");
119 return NPT_ERROR_INVALID_FORMAT
;
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]);
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
);
146 result
= stream
.ReadFully(zip64_locator
, 20);
147 if (NPT_FAILED(result
)) {
148 NPT_LOG_WARNING_1("read failed (%d)", 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]);
162 if (zip64_disk_start
!= 0 || zip64_disk_count
!= 1) {
163 return NPT_ERROR_NOT_SUPPORTED
;
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
);
179 result
= stream
.ReadFully(eocdr64
, 56);
180 if (NPT_FAILED(result
)) {
181 NPT_LOG_WARNING_1("read failed (%d)", 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]);
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
);
224 result
= stream
.Seek(central_directory_offset
);
225 if (NPT_FAILED(result
)) {
226 NPT_LOG_WARNING_1("seek failed (%d)", 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
);
235 // create a new file object
236 file
= new NPT_ZipFile();
237 file
->m_Entries
.Reserve((NPT_Cardinal
)total_entry_count
);
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");
250 NPT_ZipFile::Entry
entry(buffer
, buffer_size
);
251 if (entry
.m_DirectoryEntrySize
== 0) {
252 NPT_LOG_WARNING("invalid entry data");
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
);
261 file
->GetEntries().Add(entry
);
263 central_directory_size
-= entry
.m_DirectoryEntrySize
;
264 buffer
+= entry
.m_DirectoryEntrySize
;
265 buffer_size
-= entry
.m_DirectoryEntrySize
;
271 /*----------------------------------------------------------------------
272 | NPT_ZipFile::GetInputStream
273 +---------------------------------------------------------------------*/
275 NPT_ZipFile::GetInputStream(Entry
& entry
, NPT_InputStreamReference
& zip_stream
, NPT_InputStream
*& file_stream
)
277 // default return value
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
;
292 if (entry
.m_CompressionMethod
!= NPT_ZIP_FILE_COMPRESSION_METHOD_NONE
) {
293 return NPT_ERROR_NOT_SUPPORTED
;
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
);
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
);
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);
335 /*----------------------------------------------------------------------
336 | NPT_ZipFile::Entry::Entry
337 +---------------------------------------------------------------------*/
338 NPT_ZipFile::Entry::Entry(const unsigned char* data
, NPT_Size data_available
) :
340 m_CompressionMethod(0),
343 m_UncompressedSize(0),
345 m_InternalFileAttributes(0),
346 m_ExternalFileAttributes(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;
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
);
388 if (m_CompressedSize
== 0xFFFFFFFF) {
389 m_CompressedSize
= NPT_BytesToInt64Le(local_data
);
392 if (m_RelativeOffset
== 0xFFFFFFFF) {
393 m_RelativeOffset
= NPT_BytesToInt64Le(local_data
);
396 if (m_DiskNumber
== 0xFFFF) {
397 m_DiskNumber
= NPT_BytesToInt32Le(local_data
);
402 ext_data
+= 4+ext_size
;
403 if (ext_data_size
>= 4+ext_size
) {
404 ext_data_size
-= 4+ext_size
;
411 #if defined(NPT_CONFIG_ENABLE_ZIP)
413 /*----------------------------------------------------------------------
415 +---------------------------------------------------------------------*/
416 const unsigned int NPT_ZIP_DEFAULT_BUFFER_SIZE
= 4096;
418 /*----------------------------------------------------------------------
420 +---------------------------------------------------------------------*/
422 NPT_Zip::MapError(int err
)
425 case Z_OK
: return NPT_SUCCESS
;
426 case Z_STREAM_END
: return NPT_ERROR_EOS
;
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
{
441 NPT_ZipInflateState(bool raw
= false);
442 ~NPT_ZipInflateState();
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
{
471 NPT_ZipDeflateState(int compression_level
,
472 NPT_Zip::Format format
);
473 ~NPT_ZipDeflateState();
477 /*----------------------------------------------------------------------
478 | NPT_ZipDeflateState::NPT_ZipDeflateState
479 +---------------------------------------------------------------------*/
480 NPT_ZipDeflateState::NPT_ZipDeflateState(int compression_level
,
481 NPT_Zip::Format format
)
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
,
496 15 + (format
== NPT_Zip::GZIP
? 16 : 0),
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
) :
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()
528 /*----------------------------------------------------------------------
529 | NPT_ZipInflatingInputStream::Read
530 +---------------------------------------------------------------------*/
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
;
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
555 } else if (err
== Z_OK
) {
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();
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
;
577 *bytes_read
= progress
;
579 m_Position
+= progress
;
581 return progress
== 0 ? NPT_ERROR_EOS
:NPT_SUCCESS
;
584 /*----------------------------------------------------------------------
585 | NPT_ZipInflatingInputStream::Seek
586 +---------------------------------------------------------------------*/
588 NPT_ZipInflatingInputStream::Seek(NPT_Position
/* offset */)
591 return NPT_ERROR_NOT_SUPPORTED
;
594 /*----------------------------------------------------------------------
595 | NPT_ZipInflatingInputStream::Tell
596 +---------------------------------------------------------------------*/
598 NPT_ZipInflatingInputStream::Tell(NPT_Position
& offset
)
604 /*----------------------------------------------------------------------
605 | NPT_ZipInflatingInputStream::GetSize
606 +---------------------------------------------------------------------*/
608 NPT_ZipInflatingInputStream::GetSize(NPT_LargeSize
& size
)
610 // the size is not predictable
612 return NPT_ERROR_NOT_SUPPORTED
;
615 /*----------------------------------------------------------------------
616 | NPT_ZipInflatingInputStream::GetAvailable
617 +---------------------------------------------------------------------*/
619 NPT_ZipInflatingInputStream::GetAvailable(NPT_LargeSize
& available
)
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
) :
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()
649 /*----------------------------------------------------------------------
650 | NPT_ZipDeflatingInputStream::Read
651 +---------------------------------------------------------------------*/
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
;
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
676 } else if (err
== Z_OK
) {
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
) {
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();
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
;
702 *bytes_read
= progress
;
704 m_Position
+= progress
;
706 return progress
== 0 ? NPT_ERROR_EOS
:NPT_SUCCESS
;
709 /*----------------------------------------------------------------------
710 | NPT_ZipDeflatingInputStream::Seek
711 +---------------------------------------------------------------------*/
713 NPT_ZipDeflatingInputStream::Seek(NPT_Position
/* offset */)
716 return NPT_ERROR_NOT_SUPPORTED
;
719 /*----------------------------------------------------------------------
720 | NPT_ZipDeflatingInputStream::Tell
721 +---------------------------------------------------------------------*/
723 NPT_ZipDeflatingInputStream::Tell(NPT_Position
& offset
)
729 /*----------------------------------------------------------------------
730 | NPT_ZipDeflatingInputStream::GetSize
731 +---------------------------------------------------------------------*/
733 NPT_ZipDeflatingInputStream::GetSize(NPT_LargeSize
& size
)
735 // the size is not predictable
737 return NPT_ERROR_NOT_SUPPORTED
;
740 /*----------------------------------------------------------------------
741 | NPT_ZipDeflatingInputStream::GetAvailable
742 +---------------------------------------------------------------------*/
744 NPT_ZipDeflatingInputStream::GetAvailable(NPT_LargeSize
& available
)
751 /*----------------------------------------------------------------------
753 +---------------------------------------------------------------------*/
755 NPT_Zip::Deflate(const NPT_DataBuffer
& in
,
757 int compression_level
,
758 Format format
/* = ZLIB */)
760 // default return state
764 if (compression_level
< NPT_ZIP_COMPRESSION_LEVEL_DEFAULT
||
765 compression_level
> NPT_ZIP_COMPRESSION_LEVEL_MAX
) {
766 return NPT_ERROR_INVALID_PARAMETERS
;
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
,
784 15 + (format
== GZIP
? 16 : 0),
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();
795 err
= deflate(&stream
, Z_FINISH
);
796 if (err
!= Z_STREAM_END
) {
798 return MapError(err
);
801 // update the output size
802 out
.SetDataSize((NPT_Size
)stream
.total_out
);
805 err
= deflateEnd(&stream
);
806 return MapError(err
);
809 /*----------------------------------------------------------------------
811 +---------------------------------------------------------------------*/
813 NPT_Zip::Inflate(const NPT_DataBuffer
& in
,
817 // assume an output buffer twice the size of the input plus a bit
818 NPT_CHECK_WARNING(out
.Reserve(32+2*in
.GetDataSize()));
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
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
);
851 if (err
!= Z_STREAM_END
) {
853 return MapError(err
);
857 err
= inflateEnd(&stream
);
858 return MapError(err
);
862 /*----------------------------------------------------------------------
864 +---------------------------------------------------------------------*/
866 NPT_Zip::Deflate(NPT_File
& in
,
868 int compression_level
,
869 Format format
/* = ZLIB */)
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