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 namespace
00042 {
00043
00044 inline int zcompress(FILE *source, FILE *dest, int level)
00045 {
00046 const int CHUNK_SIZE = 128*1024;
00047 int ret, flush;
00048 unsigned have;
00049 z_stream strm;
00050 unsigned char in[CHUNK_SIZE];
00051 unsigned char out[CHUNK_SIZE];
00052
00053 strm.zalloc = Z_NULL;
00054 strm.zfree = Z_NULL;
00055 strm.opaque = Z_NULL;
00056 ret = deflateInit(&strm, level);
00057 if (ret != Z_OK)
00058 return ret;
00059
00060 do
00061 {
00062 strm.avail_in = (uInt)fread(in, 1, CHUNK_SIZE, source);
00063 if (ferror(source))
00064 {
00065 deflateEnd(&strm);
00066 return Z_ERRNO;
00067 }
00068 flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
00069 strm.next_in = in;
00070
00071 do
00072 {
00073 strm.avail_out = CHUNK_SIZE;
00074 strm.next_out = out;
00075
00076 ret = deflate(&strm, flush);
00077 VL_CHECK(ret != Z_STREAM_ERROR);
00078
00079 have = CHUNK_SIZE - strm.avail_out;
00080 if (fwrite(out, 1, have, dest) != have || ferror(dest))
00081 {
00082 deflateEnd(&strm);
00083 return Z_ERRNO;
00084 }
00085
00086 } while (strm.avail_out == 0);
00087
00088 VL_CHECK(strm.avail_in == 0);
00089
00090 } while (flush != Z_FINISH);
00091
00092 VL_CHECK(ret == Z_STREAM_END);
00093
00094 (void)deflateEnd(&strm);
00095 return Z_OK;
00096 }
00097
00098 inline int zdecompress(FILE *source, FILE *dest)
00099 {
00100 const int CHUNK_SIZE = 128*1024;
00101 int ret;
00102 unsigned have;
00103 z_stream strm;
00104 unsigned char in[CHUNK_SIZE];
00105 unsigned char out[CHUNK_SIZE];
00106
00107 strm.zalloc = Z_NULL;
00108 strm.zfree = Z_NULL;
00109 strm.opaque = Z_NULL;
00110 strm.avail_in = 0;
00111 strm.next_in = Z_NULL;
00112 ret = inflateInit(&strm);
00113 if (ret != Z_OK)
00114 return ret;
00115
00116 do
00117 {
00118 strm.avail_in = (uInt)fread(in, 1, CHUNK_SIZE, source);
00119 if (ferror(source))
00120 {
00121 inflateEnd(&strm);
00122 return Z_ERRNO;
00123 }
00124 if (strm.avail_in == 0)
00125 break;
00126 strm.next_in = in;
00127
00128 do
00129 {
00130 strm.avail_out = CHUNK_SIZE;
00131 strm.next_out = out;
00132
00133 ret = inflate(&strm, Z_NO_FLUSH);
00134 VL_CHECK(ret != Z_STREAM_ERROR);
00135 switch (ret)
00136 {
00137 case Z_NEED_DICT:
00138 ret = Z_DATA_ERROR;
00139 case Z_DATA_ERROR:
00140 case Z_MEM_ERROR:
00141 inflateEnd(&strm);
00142 return ret;
00143 }
00144
00145 have = CHUNK_SIZE - strm.avail_out;
00146 if (fwrite(out, 1, have, dest) != have || ferror(dest))
00147 {
00148 inflateEnd(&strm);
00149 return Z_ERRNO;
00150 }
00151 }
00152 while (strm.avail_out == 0);
00153
00154 VL_CHECK(strm.avail_in == 0);
00155
00156 } while (ret != Z_STREAM_END);
00157
00158 inflateEnd(&strm);
00159 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
00160 }
00161
00162 inline int zdecompress(VirtualFile *source, char *dest, unsigned int bytes_to_read)
00163 {
00164 const unsigned int CHUNK_SIZE = 128*1024;
00165 int ret;
00166 unsigned have;
00167 z_stream strm;
00168 unsigned char in[CHUNK_SIZE];
00169 unsigned char out[CHUNK_SIZE];
00170
00171 strm.zalloc = Z_NULL;
00172 strm.zfree = Z_NULL;
00173 strm.opaque = Z_NULL;
00174 strm.avail_in = 0;
00175 strm.next_in = Z_NULL;
00176
00177 ret = inflateInit2(&strm, -15);
00178 if (ret != Z_OK)
00179 return ret;
00180
00181 do
00182 {
00183 unsigned int byte_count = CHUNK_SIZE < bytes_to_read ? CHUNK_SIZE : bytes_to_read;
00184 strm.avail_in = (uInt)source->read(in, byte_count);
00185 bytes_to_read -= strm.avail_in;
00186
00187 if (strm.avail_in == 0)
00188 break;
00189 strm.next_in = in;
00190
00191 do
00192 {
00193 strm.avail_out = CHUNK_SIZE;
00194 strm.next_out = out;
00195
00196 ret = inflate(&strm, Z_NO_FLUSH);
00197 VL_CHECK(ret != Z_STREAM_ERROR);
00198 switch (ret)
00199 {
00200 case Z_NEED_DICT:
00201 ret = Z_DATA_ERROR;
00202 case Z_DATA_ERROR:
00203 case Z_MEM_ERROR:
00204 inflateEnd(&strm);
00205 return ret;
00206 }
00207
00208 have = CHUNK_SIZE - strm.avail_out;
00209 memcpy(dest, out, have);
00210 dest += have;
00211 }
00212 while (strm.avail_out == 0);
00213
00214 VL_CHECK(strm.avail_in == 0);
00215
00216 } while (ret != Z_STREAM_END);
00217
00218 inflateEnd(&strm);
00219 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
00220 }
00221 }
00222
00223
00224
00225 ZippedFile::ZippedFile()
00226 {
00227 mReadBytes = -1;
00228 mZStream = new z_stream_s;
00229 memset(mZStream, 0, sizeof(z_stream_s));
00230 }
00231
00232 ZippedFile::~ZippedFile()
00233 {
00234 close();
00235 mReadBytes = -1;
00236 delete mZStream; mZStream = NULL;
00237 }
00238
00239 ZippedFileInfo* ZippedFile::zippedFileInfo() const { return mZippedFileInfo.get(); }
00240
00241 void ZippedFile::setZippedFileInfo(ZippedFileInfo* info) { mZippedFileInfo = info; }
00242
00243 bool ZippedFile::exists() const
00244 {
00245 return zippedFileInfo() && zippedFileInfo()->sourceZipFile() && zippedFileInfo()->zippedFileOffset();
00246 }
00247
00248 bool ZippedFile::open(EOpenMode mode)
00249 {
00250 if ( zippedFileInfo()->versionNeeded() > 20 )
00251 {
00252 Log::error("ZippedFile::open(): unsupported archive version.\n");
00253 return false;
00254 }
00255
00256
00257
00258
00259
00260
00261
00262 if ( zippedFileInfo()->compressionMethod() != 8 && zippedFileInfo()->compressionMethod() != 0 )
00263 {
00264 Log::error("ZippedFile::open(): unsupported compression method.\n");
00265 return false;
00266 }
00267
00268 if ( isOpen() )
00269 {
00270 Log::error("ZippedFile::open(): file already open.\n");
00271 return false;
00272 }
00273
00274 if ( mode != OM_ReadOnly )
00275 {
00276 Log::error("ZippedFile::open(): only OM_ReadOnly mode is supported.\n");
00277 return false;
00278 }
00279
00280 if ( !zippedFileInfo()->sourceZipFile() )
00281 {
00282 Log::error("ZippedFile::open(): no source zip stream defined.\n");
00283 return false;
00284 }
00285
00286 if ( zippedFileInfo()->sourceZipFile()->isOpen() )
00287 {
00288 Log::error("ZippedFile::open(): source zip stream is already open. Only one ZippedFile at a time can read from the same source.\n");
00289 return false;
00290 }
00291
00292 if ( !zippedFileInfo()->sourceZipFile()->open(mode) )
00293 {
00294 Log::error("ZippedFile::open(): could not open source zip stream.\n");
00295 return false;
00296 }
00297
00298 if ( !zippedFileInfo()->sourceZipFile()->seekSet( zippedFileInfo()->zippedFileOffset() ) )
00299 {
00300 Log::error("ZippedFile::open(): error seeking beginning of compressed file.\n");
00301 zippedFileInfo()->sourceZipFile()->close();
00302 return false;
00303 }
00304
00305
00306 mZStream->zalloc = Z_NULL;
00307 mZStream->zfree = Z_NULL;
00308 mZStream->opaque = Z_NULL;
00309 mZStream->avail_in = 0;
00310 mZStream->next_in = Z_NULL;
00311 if ( zippedFileInfo()->compressionMethod() == 8 && inflateInit2(mZStream, -15) != Z_OK )
00312 {
00313 zippedFileInfo()->sourceZipFile()->close();
00314 return false;
00315 }
00316
00317 mReadBytes = 0;
00318 mUncompressedBufferPtr = 0;
00319 mUncompressedBuffer.clear();
00320
00321 return true;
00322 }
00323
00324 bool ZippedFile::isOpen() const { return mReadBytes != -1; }
00325
00326 void ZippedFile::close()
00327 {
00328 if ( mZStream->next_in != Z_NULL )
00329 inflateEnd(mZStream);
00330 memset(mZStream, 0, sizeof(z_stream_s));
00331 mReadBytes = -1;
00332 if (zippedFileInfo() && zippedFileInfo()->sourceZipFile())
00333 zippedFileInfo()->sourceZipFile()->close();
00334 mUncompressedBufferPtr = 0;
00335 mUncompressedBuffer.clear();
00336 }
00337
00338 long long ZippedFile::size() const
00339 {
00340 if ( mZippedFileInfo )
00341 return mZippedFileInfo->uncompressedSize();
00342 else
00343 return 0;
00344 }
00345
00346 void ZippedFile::resetStream()
00347 {
00348 close();
00349 open(OM_ReadOnly);
00350 }
00351
00352 bool ZippedFile::seekSet_Implementation(long long pos)
00353 {
00354 if (pos<position())
00355 resetStream();
00356
00357 unsigned char buffer[CHUNK_SIZE];
00358 long long remained = pos - position();
00359 long long eat_bytes = remained < CHUNK_SIZE ? remained : CHUNK_SIZE;
00360 while ( (remained -= read(buffer, eat_bytes)) )
00361 eat_bytes = remained < CHUNK_SIZE ? remained : CHUNK_SIZE;
00362
00363 return position() == pos;
00364 }
00365
00366 long long ZippedFile::read_Implementation(void* buffer, long long bytes_to_read)
00367 {
00368 if ( bytes_to_read < 1 )
00369 return 0;
00370
00371 if (!isOpen())
00372 return 0;
00373
00374 if ( mReadBytes >= zippedFileInfo()->uncompressedSize() )
00375 return 0;
00376
00377 if ( zippedFileInfo()->compressionMethod() == 0 )
00378 {
00379 long long bytes = zippedFileInfo()->uncompressedSize() - mReadBytes;
00380 bytes = bytes < bytes_to_read ? bytes : bytes_to_read;
00381 long long read_bytes = zippedFileInfo()->sourceZipFile()->read(buffer, bytes);
00382 mReadBytes += read_bytes;
00383 return read_bytes;
00384 }
00385 else
00386 if ( zippedFileInfo()->compressionMethod() == 8 )
00387 {
00388
00389 long long read_bytes = 0;
00390
00391 if ( mUncompressedBuffer.empty() )
00392 fillUncompressedBuffer();
00393
00394 do
00395 {
00396 long long bytes = bytes_to_read < (int)mUncompressedBuffer.size()-mUncompressedBufferPtr ? bytes_to_read : (int)mUncompressedBuffer.size()-mUncompressedBufferPtr;
00397
00398 VL_CHECK( mUncompressedBufferPtr < (int)mUncompressedBuffer.size() )
00399 if (bytes)
00400 memcpy((char*)buffer+read_bytes, &mUncompressedBuffer[0]+mUncompressedBufferPtr, (size_t)bytes);
00401
00402 mReadBytes += bytes;
00403 read_bytes += bytes;
00404 mUncompressedBufferPtr += (int)bytes;
00405 bytes_to_read -= bytes;
00406
00407
00408 if(mUncompressedBufferPtr == (int)mUncompressedBuffer.size())
00409 {
00410 mUncompressedBuffer.clear();
00411 mUncompressedBufferPtr = 0;
00412 }
00413
00414 } while( bytes_to_read && fillUncompressedBuffer() );
00415
00416 return read_bytes;
00417 }
00418 else
00419 return 0;
00420 }
00421
00422 bool ZippedFile::extract(char* destination, bool check_sum)
00423 {
00424 ZippedFileInfo* zfile_info = zippedFileInfo();
00425
00426 if ( zfile_info->mUncompressedSize == 0 )
00427 return true;
00428
00429 if (isOpen())
00430 {
00431 Log::error("ZippedFile::extract(): the file is already open.\n");
00432 return false;
00433 }
00434
00435 if ( zfile_info->versionNeeded() > 20 )
00436 {
00437 Log::error("ZippedFile::extract(): unsupported archive version.\n");
00438 return false;
00439 }
00440
00441 if ( zfile_info->generalPurposeFlag() & 1 )
00442 {
00443 Log::error("ZippedFile::extract(): encription not supported.\n");
00444 return false;
00445 }
00446
00447 ref<VirtualFile> zip = zfile_info->sourceZipFile();
00448
00449 if ( !zip )
00450 {
00451 Log::error("ZippedFile::extract(): no source zip stream defined.\n");
00452 return false;
00453 }
00454
00455 if ( zip->isOpen() )
00456 {
00457 Log::error("ZippedFile::extract(): source zip stream is already open. Only one ZippedFile at a time can read from the same source.\n");
00458 return false;
00459 }
00460
00461 if ( !zip->open(OM_ReadOnly) )
00462 {
00463 Log::error("ZippedFile::extract(): could not open source zip stream.\n");
00464 return false;
00465 }
00466
00467 if ( !zip->seekSet( zfile_info->zippedFileOffset() ) )
00468 {
00469 Log::error("ZippedFile::extract(): not a seek-able zip stream.\n");
00470 zip->close();
00471 return false;
00472 }
00473
00474 switch(zfile_info->mCompressionMethod)
00475 {
00476 default:
00477 Log::error("ZippedFile::extract(): unsupported compression method.\n");
00478 break;
00479 case 0:
00480 case 8:
00481 {
00482 if (zfile_info->mCompressionMethod == 8)
00483 zdecompress( zip.get(), destination, zfile_info->mCompressedSize );
00484 else
00485 if (zfile_info->mCompressionMethod == 0)
00486 zip->read( destination, zfile_info->mUncompressedSize );
00487
00488 if (check_sum)
00489 {
00490 CRC32CheckSum checksum;
00491 unsigned int crc = checksum.compute( destination, (int)zfile_info->mUncompressedSize );
00492
00493 VL_CHECK( zfile_info->mCRC32 == crc );
00494 if ( zfile_info->mCRC32 != crc )
00495 {
00496 zip->close();
00497 return false;
00498 }
00499 }
00500 }
00501 }
00502
00503 zip->close();
00504 return true;
00505 }
00506
00507 bool ZippedFile::fillUncompressedBuffer()
00508 {
00509 VL_CHECK(mUncompressedBufferPtr == (int)mUncompressedBuffer.size())
00510
00511 VL_CHECK( mUncompressedBuffer.empty() )
00512
00513 mUncompressedBufferPtr = 0;
00514
00515 if (!isOpen())
00516 return false;
00517
00518 if ( mReadBytes >= zippedFileInfo()->uncompressedSize() )
00519 return false;
00520
00521 int have = 0;
00522 int ret = 0;
00523
00524 unsigned int compressed_read_bytes = (int)(zippedFileInfo()->sourceZipFile()->position() - zippedFileInfo()->zippedFileOffset());
00525
00526 int bytes_to_read = (unsigned)CHUNK_SIZE < (zippedFileInfo()->compressedSize() - compressed_read_bytes)?
00527 (unsigned)CHUNK_SIZE : (zippedFileInfo()->compressedSize() - compressed_read_bytes);
00528 mZStream->avail_in = (uInt)zippedFileInfo()->sourceZipFile()->read(mZipBufferIn, bytes_to_read);
00529
00530 if (mZStream->avail_in == 0)
00531 return false;
00532 mZStream->next_in = mZipBufferIn;
00533
00534 do
00535 {
00536 mZStream->avail_out = CHUNK_SIZE;
00537 mZStream->next_out = mZipBufferOut;
00538
00539 ret = inflate(mZStream, Z_NO_FLUSH);
00540 VL_CHECK(ret != Z_STREAM_ERROR);
00541 switch (ret)
00542 {
00543 case Z_NEED_DICT:
00544 case Z_DATA_ERROR:
00545 case Z_MEM_ERROR:
00546 inflateEnd(mZStream);
00547 memset(mZStream, 0, sizeof(z_stream_s));
00548 Log::error("ZippedFile::read(): error reading zip stream.\n");
00549 return false;
00550 }
00551
00552 have = CHUNK_SIZE - mZStream->avail_out;
00553 if (!have)
00554 break;
00555 int start = (int)mUncompressedBuffer.size();
00556 mUncompressedBuffer.resize(start + have);
00557 memcpy(&mUncompressedBuffer[0] + start, mZipBufferOut, have);
00558 }
00559 while ( mZStream->avail_out == 0 );
00560
00561 return true;
00562 }
00563
00564 ref<VirtualFile> ZippedFile::clone() const
00565 {
00566 ref<ZippedFile> file = new ZippedFile;
00567 file->operator=(*this);
00568 return file;
00569 }
00570