00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <vlCore/ZippedFile.hpp>
00033 #include <vlCore/CRC32CheckSum.hpp>
00034 #include <vlCore/Log.hpp>
00035 #include <vlCore/Say.hpp>
00036 #include <zlib.h>
00037 #include <stdio.h>
00038
00039 using namespace vl;
00040
00041 bool vl::compress(const void* data, size_t size, std::vector<unsigned char>& out_data, int level)
00042 {
00043 const size_t CHUNK_SIZE = 128*1024;
00044 int ret, flush;
00045 unsigned have;
00046 z_stream strm;
00047 const unsigned char* in = (const unsigned char*)data;
00048 unsigned char out[CHUNK_SIZE];
00049
00050 strm.zalloc = Z_NULL;
00051 strm.zfree = Z_NULL;
00052 strm.opaque = Z_NULL;
00053 ret = deflateInit(&strm, level);
00054 if (ret != Z_OK)
00055 return false;
00056
00057 size_t avail = size;
00058
00059 do
00060 {
00061 strm.avail_in = std::min(avail, CHUNK_SIZE);
00062 avail -= strm.avail_in;
00063 strm.next_in = (unsigned char*)in;
00064 in += strm.avail_in;
00065 flush = avail == 0 ? Z_FINISH : Z_NO_FLUSH;
00066
00067 do
00068 {
00069 strm.avail_out = CHUNK_SIZE;
00070 strm.next_out = out;
00071
00072 ret = deflate(&strm, flush);
00073 if(ret == Z_STREAM_ERROR)
00074 {
00075 deflateEnd(&strm);
00076 return false;
00077 }
00078
00079 have = CHUNK_SIZE - strm.avail_out;
00080 out_data.insert( out_data.end(), out, out+have );
00081 } while (strm.avail_out == 0);
00082
00083 VL_CHECK(strm.avail_in == 0);
00084
00085 } while (flush != Z_FINISH);
00086
00087 VL_CHECK(ret == Z_STREAM_END);
00088
00089 deflateEnd(&strm);
00090 return true;
00091 }
00092
00093 bool vl::decompress(const void* cdata, size_t csize, void* data_out)
00094 {
00095 const size_t CHUNK_SIZE = 128*1024;
00096 int ret;
00097 unsigned have;
00098 z_stream strm;
00099 unsigned char* in = (unsigned char*)cdata;
00100 unsigned char out[CHUNK_SIZE];
00101 char* out_ptr = (char*)data_out;
00102
00103 strm.zalloc = Z_NULL;
00104 strm.zfree = Z_NULL;
00105 strm.opaque = Z_NULL;
00106 strm.avail_in = 0;
00107 strm.next_in = Z_NULL;
00108 ret = inflateInit(&strm);
00109 if (ret != Z_OK)
00110 return false;
00111
00112 size_t avail = csize;
00113
00114 do
00115 {
00116 strm.avail_in = std::min(avail, CHUNK_SIZE);
00117 if (strm.avail_in == 0)
00118 break;
00119 avail -= strm.avail_in;
00120 strm.next_in = in;
00121 in += strm.avail_in;
00122
00123 do
00124 {
00125 strm.avail_out = CHUNK_SIZE;
00126 strm.next_out = out;
00127
00128 ret = inflate(&strm, Z_NO_FLUSH);
00129 VL_CHECK(ret != Z_STREAM_ERROR);
00130 switch (ret)
00131 {
00132 case Z_NEED_DICT:
00133 case Z_DATA_ERROR:
00134 case Z_MEM_ERROR:
00135 inflateEnd(&strm);
00136 return false;
00137 }
00138
00139 have = CHUNK_SIZE - strm.avail_out;
00140
00141 memcpy(out_ptr, out, have);
00142 out_ptr += have;
00143 }
00144 while (strm.avail_out == 0);
00145
00146 VL_CHECK(strm.avail_in == 0);
00147
00148 } while (ret != Z_STREAM_END);
00149
00150 inflateEnd(&strm);
00151
00152
00153 return ret == Z_STREAM_END;
00154 }
00155
00156 namespace
00157 {
00158
00159 inline int zcompress(FILE *source, FILE *dest, int level)
00160 {
00161 const int CHUNK_SIZE = 128*1024;
00162 int ret, flush;
00163 unsigned have;
00164 z_stream strm;
00165 unsigned char in[CHUNK_SIZE];
00166 unsigned char out[CHUNK_SIZE];
00167
00168 strm.zalloc = Z_NULL;
00169 strm.zfree = Z_NULL;
00170 strm.opaque = Z_NULL;
00171 ret = deflateInit(&strm, level);
00172 if (ret != Z_OK)
00173 return ret;
00174
00175 do
00176 {
00177 strm.avail_in = (uInt)fread(in, 1, CHUNK_SIZE, source);
00178 if (ferror(source))
00179 {
00180 deflateEnd(&strm);
00181 return Z_ERRNO;
00182 }
00183 flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
00184 strm.next_in = in;
00185
00186 do
00187 {
00188 strm.avail_out = CHUNK_SIZE;
00189 strm.next_out = out;
00190
00191 ret = deflate(&strm, flush);
00192 VL_CHECK(ret != Z_STREAM_ERROR);
00193
00194 have = CHUNK_SIZE - strm.avail_out;
00195 if (fwrite(out, 1, have, dest) != have || ferror(dest))
00196 {
00197 deflateEnd(&strm);
00198 return Z_ERRNO;
00199 }
00200
00201 } while (strm.avail_out == 0);
00202
00203 VL_CHECK(strm.avail_in == 0);
00204
00205 } while (flush != Z_FINISH);
00206
00207 VL_CHECK(ret == Z_STREAM_END);
00208
00209 (void)deflateEnd(&strm);
00210 return Z_OK;
00211 }
00212
00213 inline int zdecompress(FILE *source, FILE *dest)
00214 {
00215 const int CHUNK_SIZE = 128*1024;
00216 int ret;
00217 unsigned have;
00218 z_stream strm;
00219 unsigned char in[CHUNK_SIZE];
00220 unsigned char out[CHUNK_SIZE];
00221
00222 strm.zalloc = Z_NULL;
00223 strm.zfree = Z_NULL;
00224 strm.opaque = Z_NULL;
00225 strm.avail_in = 0;
00226 strm.next_in = Z_NULL;
00227 ret = inflateInit(&strm);
00228 if (ret != Z_OK)
00229 return ret;
00230
00231 do
00232 {
00233 strm.avail_in = (uInt)fread(in, 1, CHUNK_SIZE, source);
00234 if (ferror(source))
00235 {
00236 inflateEnd(&strm);
00237 return Z_ERRNO;
00238 }
00239 if (strm.avail_in == 0)
00240 break;
00241 strm.next_in = in;
00242
00243 do
00244 {
00245 strm.avail_out = CHUNK_SIZE;
00246 strm.next_out = out;
00247
00248 ret = inflate(&strm, Z_NO_FLUSH);
00249 VL_CHECK(ret != Z_STREAM_ERROR);
00250 switch (ret)
00251 {
00252 case Z_NEED_DICT:
00253 ret = Z_DATA_ERROR;
00254 case Z_DATA_ERROR:
00255 case Z_MEM_ERROR:
00256 inflateEnd(&strm);
00257 return ret;
00258 }
00259
00260 have = CHUNK_SIZE - strm.avail_out;
00261 if (fwrite(out, 1, have, dest) != have || ferror(dest))
00262 {
00263 inflateEnd(&strm);
00264 return Z_ERRNO;
00265 }
00266 }
00267 while (strm.avail_out == 0);
00268
00269 VL_CHECK(strm.avail_in == 0);
00270
00271 } while (ret != Z_STREAM_END);
00272
00273 inflateEnd(&strm);
00274 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
00275 }
00276
00277 inline int zdecompress(VirtualFile *source, char *dest, unsigned int bytes_to_read)
00278 {
00279 const unsigned int CHUNK_SIZE = 128*1024;
00280 int ret;
00281 unsigned have;
00282 z_stream strm;
00283 unsigned char in[CHUNK_SIZE];
00284 unsigned char out[CHUNK_SIZE];
00285
00286 strm.zalloc = Z_NULL;
00287 strm.zfree = Z_NULL;
00288 strm.opaque = Z_NULL;
00289 strm.avail_in = 0;
00290 strm.next_in = Z_NULL;
00291
00292 ret = inflateInit2(&strm, -15);
00293 if (ret != Z_OK)
00294 return ret;
00295
00296 do
00297 {
00298 unsigned int byte_count = CHUNK_SIZE < bytes_to_read ? CHUNK_SIZE : bytes_to_read;
00299 strm.avail_in = (uInt)source->read(in, byte_count);
00300 bytes_to_read -= strm.avail_in;
00301
00302 if (strm.avail_in == 0)
00303 break;
00304 strm.next_in = in;
00305
00306 do
00307 {
00308 strm.avail_out = CHUNK_SIZE;
00309 strm.next_out = out;
00310
00311 ret = inflate(&strm, Z_NO_FLUSH);
00312 VL_CHECK(ret != Z_STREAM_ERROR);
00313 switch (ret)
00314 {
00315 case Z_NEED_DICT:
00316 ret = Z_DATA_ERROR;
00317 case Z_DATA_ERROR:
00318 case Z_MEM_ERROR:
00319 inflateEnd(&strm);
00320 return ret;
00321 }
00322
00323 have = CHUNK_SIZE - strm.avail_out;
00324 memcpy(dest, out, have);
00325 dest += have;
00326 }
00327 while (strm.avail_out == 0);
00328
00329 VL_CHECK(strm.avail_in == 0);
00330
00331 } while (ret != Z_STREAM_END);
00332
00333 inflateEnd(&strm);
00334 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
00335 }
00336 }
00337
00338
00339
00340 ZippedFile::ZippedFile()
00341 {
00342 mReadBytes = -1;
00343 mZStream = new z_stream_s;
00344 memset(mZStream, 0, sizeof(z_stream_s));
00345 }
00346
00347 ZippedFile::~ZippedFile()
00348 {
00349 close();
00350 mReadBytes = -1;
00351 delete mZStream; mZStream = NULL;
00352 }
00353
00354 const ZippedFileInfo* ZippedFile::zippedFileInfo() const { return mZippedFileInfo.get(); }
00355
00356 ZippedFileInfo* ZippedFile::zippedFileInfo() { return mZippedFileInfo.get(); }
00357
00358 void ZippedFile::setZippedFileInfo(ZippedFileInfo* info) { mZippedFileInfo = info; }
00359
00360 bool ZippedFile::exists() const
00361 {
00362 return zippedFileInfo() && zippedFileInfo()->sourceZipFile() && zippedFileInfo()->zippedFileOffset();
00363 }
00364
00365 bool ZippedFile::open(EOpenMode mode)
00366 {
00367 if ( zippedFileInfo()->versionNeeded() > 20 )
00368 {
00369 Log::error("ZippedFile::open(): unsupported archive version.\n");
00370 return false;
00371 }
00372
00373
00374
00375
00376
00377
00378
00379 if ( zippedFileInfo()->compressionMethod() != 8 && zippedFileInfo()->compressionMethod() != 0 )
00380 {
00381 Log::error("ZippedFile::open(): unsupported compression method.\n");
00382 return false;
00383 }
00384
00385 if ( isOpen() )
00386 {
00387 Log::error("ZippedFile::open(): file already open.\n");
00388 return false;
00389 }
00390
00391 if ( mode != OM_ReadOnly )
00392 {
00393 Log::error("ZippedFile::open(): only OM_ReadOnly mode is supported.\n");
00394 return false;
00395 }
00396
00397 if ( !zippedFileInfo()->sourceZipFile() )
00398 {
00399 Log::error("ZippedFile::open(): no source zip stream defined.\n");
00400 return false;
00401 }
00402
00403 if ( zippedFileInfo()->sourceZipFile()->isOpen() )
00404 {
00405 Log::error("ZippedFile::open(): source zip stream is already open. Only one ZippedFile at a time can read from the same source.\n");
00406 return false;
00407 }
00408
00409 if ( !zippedFileInfo()->sourceZipFile()->open(mode) )
00410 {
00411 Log::error("ZippedFile::open(): could not open source zip stream.\n");
00412 return false;
00413 }
00414
00415 if ( !zippedFileInfo()->sourceZipFile()->seekSet( zippedFileInfo()->zippedFileOffset() ) )
00416 {
00417 Log::error("ZippedFile::open(): error seeking beginning of compressed file.\n");
00418 zippedFileInfo()->sourceZipFile()->close();
00419 return false;
00420 }
00421
00422
00423 mZStream->zalloc = Z_NULL;
00424 mZStream->zfree = Z_NULL;
00425 mZStream->opaque = Z_NULL;
00426 mZStream->avail_in = 0;
00427 mZStream->next_in = Z_NULL;
00428 if ( zippedFileInfo()->compressionMethod() == 8 && inflateInit2(mZStream, -15) != Z_OK )
00429 {
00430 zippedFileInfo()->sourceZipFile()->close();
00431 return false;
00432 }
00433
00434 mReadBytes = 0;
00435 mUncompressedBufferPtr = 0;
00436 mUncompressedBuffer.clear();
00437
00438 return true;
00439 }
00440
00441 bool ZippedFile::isOpen() const { return mReadBytes != -1; }
00442
00443 void ZippedFile::close()
00444 {
00445 if ( mZStream->next_in != Z_NULL )
00446 inflateEnd(mZStream);
00447 memset(mZStream, 0, sizeof(z_stream_s));
00448 mReadBytes = -1;
00449 if (zippedFileInfo() && zippedFileInfo()->sourceZipFile())
00450 zippedFileInfo()->sourceZipFile()->close();
00451 mUncompressedBufferPtr = 0;
00452 mUncompressedBuffer.clear();
00453 }
00454
00455 long long ZippedFile::size() const
00456 {
00457 if ( mZippedFileInfo )
00458 return mZippedFileInfo->uncompressedSize();
00459 else
00460 return 0;
00461 }
00462
00463 void ZippedFile::resetStream()
00464 {
00465 close();
00466 open(OM_ReadOnly);
00467 }
00468
00469 bool ZippedFile::seekSet_Implementation(long long pos)
00470 {
00471 if (pos<position())
00472 resetStream();
00473
00474 unsigned char buffer[CHUNK_SIZE];
00475 long long remained = pos - position();
00476 long long eat_bytes = remained < CHUNK_SIZE ? remained : CHUNK_SIZE;
00477 while ( (remained -= read(buffer, eat_bytes)) )
00478 eat_bytes = remained < CHUNK_SIZE ? remained : CHUNK_SIZE;
00479
00480 return position() == pos;
00481 }
00482
00483 long long ZippedFile::read_Implementation(void* buffer, long long bytes_to_read)
00484 {
00485 if ( bytes_to_read < 1 )
00486 return 0;
00487
00488 if (!isOpen())
00489 return 0;
00490
00491 if ( mReadBytes >= zippedFileInfo()->uncompressedSize() )
00492 return 0;
00493
00494 if ( zippedFileInfo()->compressionMethod() == 0 )
00495 {
00496 long long bytes = zippedFileInfo()->uncompressedSize() - mReadBytes;
00497 bytes = bytes < bytes_to_read ? bytes : bytes_to_read;
00498 long long read_bytes = zippedFileInfo()->sourceZipFile()->read(buffer, bytes);
00499 mReadBytes += read_bytes;
00500 return read_bytes;
00501 }
00502 else
00503 if ( zippedFileInfo()->compressionMethod() == 8 )
00504 {
00505
00506 long long read_bytes = 0;
00507
00508 if ( mUncompressedBuffer.empty() )
00509 fillUncompressedBuffer();
00510
00511 do
00512 {
00513 long long bytes = bytes_to_read < (int)mUncompressedBuffer.size()-mUncompressedBufferPtr ? bytes_to_read : (int)mUncompressedBuffer.size()-mUncompressedBufferPtr;
00514
00515 VL_CHECK( mUncompressedBufferPtr < (int)mUncompressedBuffer.size() )
00516 if (bytes)
00517 memcpy((char*)buffer+read_bytes, &mUncompressedBuffer[0]+mUncompressedBufferPtr, (size_t)bytes);
00518
00519 mReadBytes += bytes;
00520 read_bytes += bytes;
00521 mUncompressedBufferPtr += (int)bytes;
00522 bytes_to_read -= bytes;
00523
00524
00525 if(mUncompressedBufferPtr == (int)mUncompressedBuffer.size())
00526 {
00527 mUncompressedBuffer.clear();
00528 mUncompressedBufferPtr = 0;
00529 }
00530
00531 } while( bytes_to_read && fillUncompressedBuffer() );
00532
00533 return read_bytes;
00534 }
00535 else
00536 return 0;
00537 }
00538
00539 bool ZippedFile::extract(char* destination, bool check_sum)
00540 {
00541 ZippedFileInfo* zfile_info = zippedFileInfo();
00542
00543 if ( zfile_info->mUncompressedSize == 0 )
00544 return true;
00545
00546 if (isOpen())
00547 {
00548 Log::error("ZippedFile::extract(): the file is already open.\n");
00549 return false;
00550 }
00551
00552 if ( zfile_info->versionNeeded() > 20 )
00553 {
00554 Log::error("ZippedFile::extract(): unsupported archive version.\n");
00555 return false;
00556 }
00557
00558 if ( zfile_info->generalPurposeFlag() & 1 )
00559 {
00560 Log::error("ZippedFile::extract(): encription not supported.\n");
00561 return false;
00562 }
00563
00564 ref<VirtualFile> zip = zfile_info->sourceZipFile();
00565
00566 if ( !zip )
00567 {
00568 Log::error("ZippedFile::extract(): no source zip stream defined.\n");
00569 return false;
00570 }
00571
00572 if ( zip->isOpen() )
00573 {
00574 Log::error("ZippedFile::extract(): source zip stream is already open. Only one ZippedFile at a time can read from the same source.\n");
00575 return false;
00576 }
00577
00578 if ( !zip->open(OM_ReadOnly) )
00579 {
00580 Log::error("ZippedFile::extract(): could not open source zip stream.\n");
00581 return false;
00582 }
00583
00584 if ( !zip->seekSet( zfile_info->zippedFileOffset() ) )
00585 {
00586 Log::error("ZippedFile::extract(): not a seek-able zip stream.\n");
00587 zip->close();
00588 return false;
00589 }
00590
00591 switch(zfile_info->mCompressionMethod)
00592 {
00593 default:
00594 Log::error("ZippedFile::extract(): unsupported compression method.\n");
00595 break;
00596 case 0:
00597 case 8:
00598 {
00599 if (zfile_info->mCompressionMethod == 8)
00600 zdecompress( zip.get(), destination, zfile_info->mCompressedSize );
00601 else
00602 if (zfile_info->mCompressionMethod == 0)
00603 zip->read( destination, zfile_info->mUncompressedSize );
00604
00605 if (check_sum)
00606 {
00607 CRC32CheckSum checksum;
00608 unsigned int crc = checksum.compute( destination, (int)zfile_info->mUncompressedSize );
00609
00610 VL_CHECK( zfile_info->mCRC32 == crc );
00611 if ( zfile_info->mCRC32 != crc )
00612 {
00613 zip->close();
00614 return false;
00615 }
00616 }
00617 }
00618 }
00619
00620 zip->close();
00621 return true;
00622 }
00623
00624 bool ZippedFile::fillUncompressedBuffer()
00625 {
00626 VL_CHECK(mUncompressedBufferPtr == (int)mUncompressedBuffer.size())
00627
00628 VL_CHECK( mUncompressedBuffer.empty() )
00629
00630 mUncompressedBufferPtr = 0;
00631
00632 if (!isOpen())
00633 return false;
00634
00635 if ( mReadBytes >= zippedFileInfo()->uncompressedSize() )
00636 return false;
00637
00638 int have = 0;
00639 int ret = 0;
00640
00641 unsigned int compressed_read_bytes = (int)(zippedFileInfo()->sourceZipFile()->position() - zippedFileInfo()->zippedFileOffset());
00642
00643 int bytes_to_read = (unsigned)CHUNK_SIZE < (zippedFileInfo()->compressedSize() - compressed_read_bytes)?
00644 (unsigned)CHUNK_SIZE : (zippedFileInfo()->compressedSize() - compressed_read_bytes);
00645 mZStream->avail_in = (uInt)zippedFileInfo()->sourceZipFile()->read(mZipBufferIn, bytes_to_read);
00646
00647 if (mZStream->avail_in == 0)
00648 return false;
00649 mZStream->next_in = mZipBufferIn;
00650
00651 do
00652 {
00653 mZStream->avail_out = CHUNK_SIZE;
00654 mZStream->next_out = mZipBufferOut;
00655
00656 ret = inflate(mZStream, Z_NO_FLUSH);
00657 VL_CHECK(ret != Z_STREAM_ERROR);
00658 switch (ret)
00659 {
00660 case Z_NEED_DICT:
00661 case Z_DATA_ERROR:
00662 case Z_MEM_ERROR:
00663 inflateEnd(mZStream);
00664 memset(mZStream, 0, sizeof(z_stream_s));
00665 Log::error("ZippedFile::read(): error reading zip stream.\n");
00666 return false;
00667 }
00668
00669 have = CHUNK_SIZE - mZStream->avail_out;
00670 if (!have)
00671 break;
00672 int start = (int)mUncompressedBuffer.size();
00673 mUncompressedBuffer.resize(start + have);
00674 memcpy(&mUncompressedBuffer[0] + start, mZipBufferOut, have);
00675 }
00676 while ( mZStream->avail_out == 0 );
00677
00678 return true;
00679 }
00680
00681 ref<VirtualFile> ZippedFile::clone() const
00682 {
00683 ref<ZippedFile> file = new ZippedFile;
00684 file->operator=(*this);
00685 return file;
00686 }
00687