1 /* 2 ----------------------------------------------------------------------------- 3 This source file is part of OGRE 4 (Object-oriented Graphics Rendering Engine) 5 For the latest info, see http://www.ogre3d.org/ 6 7 Copyright (c) 2000-2013 Torus Knot Software Ltd 8 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 THE SOFTWARE. 26 ----------------------------------------------------------------------------- 27 */ 28 #include "OgreStableHeaders.h" 29 30 #if OGRE_NO_ZIP_ARCHIVE == 0 31 32 #include "OgreDeflate.h" 33 #include "OgreException.h" 34 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS || OGRE_PLATFORM == OGRE_PLATFORM_APPLE 35 #include "macUtils.h" 36 #endif 37 38 #include <zlib.h> 39 40 namespace Ogre 41 { 42 // memory implementations OgreZalloc(void * opaque,unsigned int items,unsigned int size)43 void* OgreZalloc(void* opaque, unsigned int items, unsigned int size) 44 { 45 return OGRE_MALLOC(items * size, MEMCATEGORY_GENERAL); 46 } OgreZfree(void * opaque,void * address)47 void OgreZfree(void* opaque, void* address) 48 { 49 OGRE_FREE(address, MEMCATEGORY_GENERAL); 50 } 51 #define OGRE_DEFLATE_TMP_SIZE 16384 52 //--------------------------------------------------------------------- DeflateStream(const DataStreamPtr & compressedStream,const String & tmpFileName,size_t avail_in)53 DeflateStream::DeflateStream(const DataStreamPtr& compressedStream, const String& tmpFileName, size_t avail_in) 54 : DataStream(compressedStream->getAccessMode()) 55 , mCompressedStream(compressedStream) 56 , mTempFileName(tmpFileName) 57 , mZStream(0) 58 , mCurrentPos(0) 59 , mAvailIn(avail_in) 60 , mTmp(0) 61 , mIsCompressedValid(true) 62 { 63 init(); 64 } 65 //--------------------------------------------------------------------- DeflateStream(const String & name,const DataStreamPtr & compressedStream,const String & tmpFileName,size_t avail_in)66 DeflateStream::DeflateStream(const String& name, const DataStreamPtr& compressedStream, const String& tmpFileName, size_t avail_in) 67 : DataStream(name, compressedStream->getAccessMode()) 68 , mCompressedStream(compressedStream) 69 , mTempFileName(tmpFileName) 70 , mZStream(0) 71 , mCurrentPos(0) 72 , mAvailIn(avail_in) 73 , mTmp(0) 74 , mIsCompressedValid(true) 75 { 76 init(); 77 } 78 //--------------------------------------------------------------------- getAvailInForSinglePass()79 size_t DeflateStream::getAvailInForSinglePass() 80 { 81 size_t ret = OGRE_DEFLATE_TMP_SIZE; 82 83 // if we are doing particial-uncompressing 84 if(mAvailIn>0) 85 { 86 if(mAvailIn<ret) 87 ret = mAvailIn; 88 mAvailIn -= ret; 89 } 90 91 return ret; 92 } 93 //--------------------------------------------------------------------- init()94 void DeflateStream::init() 95 { 96 mZStream = OGRE_ALLOC_T(z_stream, 1, MEMCATEGORY_GENERAL); 97 mZStream->zalloc = OgreZalloc; 98 mZStream->zfree = OgreZfree; 99 100 if (getAccessMode() == READ) 101 { 102 mTmp = (unsigned char*)OGRE_MALLOC(OGRE_DEFLATE_TMP_SIZE, MEMCATEGORY_GENERAL); 103 size_t restorePoint = mCompressedStream->tell(); 104 // read early chunk 105 mZStream->next_in = mTmp; 106 mZStream->avail_in = static_cast<uint>(mCompressedStream->read(mTmp, getAvailInForSinglePass())); 107 108 if (inflateInit(mZStream) != Z_OK) 109 { 110 mIsCompressedValid = false; 111 } 112 else 113 mIsCompressedValid = true; 114 115 if (mIsCompressedValid) 116 { 117 // in fact, inflateInit on some implementations doesn't try to read 118 // anything. We need to at least read something to test 119 Bytef testOut[4]; 120 size_t savedIn = mZStream->avail_in; 121 mZStream->avail_out = 4; 122 mZStream->next_out = testOut; 123 if (inflate(mZStream, Z_SYNC_FLUSH) != Z_OK) 124 mIsCompressedValid = false; 125 // restore for reading 126 mZStream->avail_in = static_cast<uint>(savedIn); 127 mZStream->next_in = mTmp; 128 129 inflateReset(mZStream); 130 } 131 132 if (!mIsCompressedValid) 133 { 134 // Not compressed data! 135 // Fail gracefully, fall back on reading the underlying stream direct 136 destroy(); 137 mCompressedStream->seek(restorePoint); 138 } 139 } 140 else 141 { 142 if(mTempFileName.empty()) 143 { 144 // Write to temp file 145 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 146 char* tmpname = _tempnam(".", "ogre"); 147 if (!tmpname) 148 { 149 // Having no file name here will cause various problems later. 150 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Temporary file name generation failed.", "DeflateStream::init"); 151 } 152 else 153 { 154 mTempFileName = tmpname; 155 free(tmpname); 156 } 157 #elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS || OGRE_PLATFORM == OGRE_PLATFORM_APPLE 158 mTempFileName = macTempFileName(); 159 #else 160 char tmpname[L_tmpnam]; 161 if (!tmpnam(tmpname)) 162 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Temporary file name generation failed.", "DeflateStream::init"); 163 164 mTempFileName = tmpname; 165 #endif 166 } 167 168 std::fstream *f = OGRE_NEW_T(std::fstream, MEMCATEGORY_GENERAL)(); 169 f->open(mTempFileName.c_str(), std::ios::binary | std::ios::out); 170 mTmpWriteStream = DataStreamPtr(OGRE_NEW FileStreamDataStream(f)); 171 172 } 173 174 } 175 //--------------------------------------------------------------------- destroy()176 void DeflateStream::destroy() 177 { 178 if (getAccessMode() == READ) 179 inflateEnd(mZStream); 180 181 OGRE_FREE(mZStream, MEMCATEGORY_GENERAL); 182 mZStream = 0; 183 OGRE_FREE(mTmp, MEMCATEGORY_GENERAL); 184 mTmp = 0; 185 } 186 //--------------------------------------------------------------------- ~DeflateStream()187 DeflateStream::~DeflateStream() 188 { 189 close(); 190 destroy(); 191 } 192 //--------------------------------------------------------------------- read(void * buf,size_t count)193 size_t DeflateStream::read(void* buf, size_t count) 194 { 195 if (!mIsCompressedValid) 196 { 197 return mCompressedStream->read(buf, count); 198 } 199 200 if (getAccessMode() & WRITE) 201 { 202 return mTmpWriteStream->read(buf, count); 203 } 204 else 205 { 206 207 size_t restorePoint = mCompressedStream->tell(); 208 // read from cache first 209 size_t cachereads = mReadCache.read(buf, count); 210 211 size_t newReadUncompressed = 0; 212 213 if (cachereads < count) 214 { 215 mZStream->avail_out = static_cast<uint>(count - cachereads); 216 mZStream->next_out = (Bytef*)buf + cachereads; 217 218 while (mZStream->avail_out) 219 { 220 // Pull next chunk of compressed data from the underlying stream 221 if (!mZStream->avail_in && !mCompressedStream->eof()) 222 { 223 mZStream->avail_in = static_cast<uint>(mCompressedStream->read(mTmp, getAvailInForSinglePass())); 224 mZStream->next_in = mTmp; 225 } 226 227 if (mZStream->avail_in) 228 { 229 int availpre = mZStream->avail_out; 230 int status = inflate(mZStream, Z_SYNC_FLUSH); 231 size_t readUncompressed = availpre - mZStream->avail_out; 232 newReadUncompressed += readUncompressed; 233 if (status != Z_OK) 234 { 235 // End of data, or error 236 if (status != Z_STREAM_END) 237 { 238 mCompressedStream->seek(restorePoint); 239 OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 240 "Error in compressed stream", 241 "DeflateStrea::read"); 242 } 243 else 244 { 245 // back up the stream so that it can be used from the end onwards 246 long unusedCompressed = mZStream->avail_in; 247 mCompressedStream->skip(-unusedCompressed); 248 } 249 250 break; 251 } 252 } 253 } 254 } 255 256 // Cache the last bytes read 257 mReadCache.cacheData((char*)buf + cachereads, newReadUncompressed); 258 259 mCurrentPos += newReadUncompressed + cachereads; 260 261 return newReadUncompressed + cachereads; 262 } 263 } 264 //--------------------------------------------------------------------- write(const void * buf,size_t count)265 size_t DeflateStream::write(const void* buf, size_t count) 266 { 267 if ((getAccessMode() & WRITE) == 0) 268 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 269 "Not a writable stream", "DeflateStream::write"); 270 271 return mTmpWriteStream->write(buf, count); 272 } 273 //--------------------------------------------------------------------- compressFinal()274 void DeflateStream::compressFinal() 275 { 276 // Close temp stream 277 mTmpWriteStream->close(); 278 279 // Copy & compress 280 // We do this rather than compress directly because some code seeks 281 // around while writing (e.g. to update size blocks) which is not 282 // possible when compressing on the fly 283 284 int ret, flush; 285 char in[OGRE_DEFLATE_TMP_SIZE]; 286 char out[OGRE_DEFLATE_TMP_SIZE]; 287 288 if (deflateInit(mZStream, Z_DEFAULT_COMPRESSION) != Z_OK) 289 { 290 destroy(); 291 OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 292 "Error initialising deflate compressed stream!", 293 "DeflateStream::init"); 294 } 295 296 std::ifstream inFile; 297 inFile.open(mTempFileName.c_str(), std::ios::in | std::ios::binary); 298 299 do 300 { 301 inFile.read(in, OGRE_DEFLATE_TMP_SIZE); 302 mZStream->avail_in = (uInt)inFile.gcount(); 303 if (inFile.bad()) 304 { 305 deflateEnd(mZStream); 306 OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 307 "Error reading temp uncompressed stream!", 308 "DeflateStream::init"); 309 } 310 flush = inFile.eof() ? Z_FINISH : Z_NO_FLUSH; 311 mZStream->next_in = (Bytef*)in; 312 313 /* run deflate() on input until output buffer not full, finish 314 compression if all of source has been read in */ 315 do 316 { 317 mZStream->avail_out = OGRE_DEFLATE_TMP_SIZE; 318 mZStream->next_out = (Bytef*)out; 319 ret = deflate(mZStream, flush); /* no bad return value */ 320 assert(ret != Z_STREAM_ERROR); /* state not clobbered */ 321 size_t compressed = OGRE_DEFLATE_TMP_SIZE - mZStream->avail_out; 322 mCompressedStream->write(out, compressed); 323 } while (mZStream->avail_out == 0); 324 assert(mZStream->avail_in == 0); /* all input will be used */ 325 326 /* done when last data in file processed */ 327 } while (flush != Z_FINISH); 328 assert(ret == Z_STREAM_END); /* stream will be complete */ 329 (void)ret; 330 deflateEnd(mZStream); 331 332 inFile.close(); 333 remove(mTempFileName.c_str()); 334 335 } 336 //--------------------------------------------------------------------- skip(long count)337 void DeflateStream::skip(long count) 338 { 339 if (!mIsCompressedValid) 340 { 341 mCompressedStream->skip(count); 342 return; 343 } 344 345 if (getAccessMode() & WRITE) 346 { 347 mTmpWriteStream->skip(count); 348 } 349 else 350 { 351 if (count > 0) 352 { 353 if (!mReadCache.ff(count)) 354 { 355 OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 356 "You can only skip within the cache range in a deflate stream.", 357 "DeflateStream::skip"); 358 } 359 } 360 else if (count < 0) 361 { 362 if (!mReadCache.rewind((size_t)(-count))) 363 { 364 OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 365 "You can only skip within the cache range in a deflate stream.", 366 "DeflateStream::skip"); 367 } 368 } 369 } 370 mCurrentPos = static_cast<size_t>(static_cast<long>(mCurrentPos) + count); 371 372 373 } 374 //--------------------------------------------------------------------- seek(size_t pos)375 void DeflateStream::seek( size_t pos ) 376 { 377 if (!mIsCompressedValid) 378 { 379 mCompressedStream->seek(pos); 380 return; 381 } 382 if (getAccessMode() & WRITE) 383 { 384 mTmpWriteStream->seek(pos); 385 } 386 else 387 { 388 if (pos == 0) 389 { 390 mCurrentPos = 0; 391 mZStream->next_in = mTmp; 392 mCompressedStream->seek(0); 393 mZStream->avail_in = static_cast<uint>(mCompressedStream->read(mTmp, getAvailInForSinglePass())); 394 inflateReset(mZStream); 395 } 396 else 397 { 398 skip(pos - tell()); 399 } 400 } 401 } 402 //--------------------------------------------------------------------- tell(void) const403 size_t DeflateStream::tell(void) const 404 { 405 if (!mIsCompressedValid) 406 { 407 return mCompressedStream->tell(); 408 } 409 else if(getAccessMode() & WRITE) 410 { 411 return mTmpWriteStream->tell(); 412 } 413 else 414 { 415 return mCurrentPos; 416 } 417 418 } 419 //--------------------------------------------------------------------- eof(void) const420 bool DeflateStream::eof(void) const 421 { 422 if (getAccessMode() & WRITE) 423 return mTmpWriteStream->eof(); 424 else 425 { 426 if (!mIsCompressedValid) 427 return mCompressedStream->eof(); 428 else 429 return mCompressedStream->eof() && mZStream->avail_in == 0; 430 } 431 } 432 //--------------------------------------------------------------------- close(void)433 void DeflateStream::close(void) 434 { 435 if (getAccessMode() & WRITE) 436 { 437 compressFinal(); 438 } 439 440 // don't close underlying compressed stream in case used for something else 441 } 442 //--------------------------------------------------------------------- 443 444 445 } 446 447 #endif 448