zipSubStream.cpp
Engine/source/core/util/zip/zipSubStream.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "zlib.h" 25#include "core/util/zip/zipSubStream.h" 26 27 28const U32 ZipSubRStream::csm_streamCaps = U32(Stream::StreamRead) | U32(Stream::StreamPosition); 29const U32 ZipSubRStream::csm_inputBufferSize = 4096; 30 31const U32 ZipSubWStream::csm_streamCaps = U32(Stream::StreamWrite); 32const U32 ZipSubWStream::csm_bufferSize = (2048 * 1024); 33 34//-------------------------------------------------------------------------- 35//-------------------------------------- 36// 37ZipSubRStream::ZipSubRStream() 38 : m_pStream(NULL), 39 m_uncompressedSize(0), 40 m_currentPosition(0), 41 m_EOS(false), 42 m_pZipStream(NULL), 43 m_pInputBuffer(NULL), 44 m_originalSlavePosition(0), 45 m_lastBytesRead(0) 46{ 47 // 48} 49 50//-------------------------------------- 51ZipSubRStream::~ZipSubRStream() 52{ 53 detachStream(); 54} 55 56//-------------------------------------- 57bool ZipSubRStream::attachStream(Stream* io_pSlaveStream) 58{ 59 AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?"); 60 AssertFatal(m_pStream == NULL, "Already attached!"); 61 62 m_pStream = io_pSlaveStream; 63 m_originalSlavePosition = io_pSlaveStream->getPosition(); 64 m_uncompressedSize = 0; 65 m_currentPosition = 0; 66 m_EOS = false; 67 68 // Initialize zipStream state... 69 m_pZipStream = new z_stream_s; 70 m_pInputBuffer = new U8[csm_inputBufferSize]; 71 72 m_pZipStream->zalloc = Z_NULL; 73 m_pZipStream->zfree = Z_NULL; 74 m_pZipStream->opaque = Z_NULL; 75 76 U32 buffSize = fillBuffer(csm_inputBufferSize); 77 78 m_pZipStream->next_in = m_pInputBuffer; 79 m_pZipStream->avail_in = buffSize; 80 m_pZipStream->total_in = 0; 81 inflateInit2(m_pZipStream, -MAX_WBITS); 82 83 setStatus(Ok); 84 return true; 85} 86 87//-------------------------------------- 88void ZipSubRStream::detachStream() 89{ 90 if (m_pZipStream != NULL) 91 { 92 // close out zip stream... 93 inflateEnd(m_pZipStream); 94 95 delete [] m_pInputBuffer; 96 m_pInputBuffer = NULL; 97 delete m_pZipStream; 98 m_pZipStream = NULL; 99 } 100 101 m_pStream = NULL; 102 m_originalSlavePosition = 0; 103 m_uncompressedSize = 0; 104 m_currentPosition = 0; 105 m_EOS = false; 106 setStatus(Closed); 107} 108 109//-------------------------------------- 110Stream* ZipSubRStream::getStream() 111{ 112 return m_pStream; 113} 114 115//-------------------------------------- 116void ZipSubRStream::setUncompressedSize(const U32 in_uncSize) 117{ 118 AssertFatal(m_pStream != NULL, "error, no stream to set unc size for"); 119 120 m_uncompressedSize = in_uncSize; 121} 122 123//-------------------------------------- 124bool ZipSubRStream::_read(const U32 in_numBytes, void *out_pBuffer) 125{ 126 m_lastBytesRead = 0; 127 if (in_numBytes == 0) 128 return true; 129 130 AssertFatal(out_pBuffer != NULL, "NULL output buffer"); 131 if (getStatus() == Closed) { 132 AssertFatal(false, "Attempted read from closed stream"); 133 return false; 134 } 135 136 137 if (Ok != getStatus()) 138 return false; 139 140 if (m_EOS) 141 { 142 setStatus(EOS); 143 return true; 144 }; 145 146 // Ok, we need to call inflate() until the output buffer is full. 147 // first, set up the output portion of the z_stream 148 // 149 m_pZipStream->next_out = (Bytef*)out_pBuffer; 150 m_pZipStream->avail_out = in_numBytes; 151 m_pZipStream->total_out = 0; 152 153 while (m_pZipStream->avail_out != 0) 154 { 155 S32 retVal = Z_OK; 156 157 if(m_pZipStream->avail_in == 0) 158 { 159 // check if there is more output pending 160 inflate(m_pZipStream, Z_SYNC_FLUSH); 161 162 if(m_pZipStream->total_out != in_numBytes) 163 { 164 // Need to provide more input bytes for the stream to read... 165 U32 buffSize = fillBuffer(csm_inputBufferSize); 166 //AssertFatal(buffSize != 0, "Must find a more graceful way to handle this"); 167 168 m_pZipStream->next_in = m_pInputBuffer; 169 m_pZipStream->avail_in = buffSize; 170 m_pZipStream->total_in = 0; 171 } 172 } 173 174 // need to get more? 175 if(m_pZipStream->total_out != in_numBytes) 176 retVal = inflate(m_pZipStream, Z_SYNC_FLUSH); 177 178 AssertFatal(retVal != Z_BUF_ERROR, "Should never run into a buffer error"); 179 AssertFatal(retVal == Z_OK || retVal == Z_STREAM_END, "error in the stream"); 180 181 m_lastBytesRead = m_pZipStream->total_out; 182 183 if (retVal == Z_STREAM_END) 184 { 185 if (m_pZipStream->avail_out != 0) 186 m_EOS = true; 187 188 setStatus(EOS); 189 m_currentPosition += m_pZipStream->total_out; 190 return true; 191 } 192 } 193 AssertFatal(m_pZipStream->total_out == in_numBytes, 194 "Error, didn't finish the decompression!"); 195 196 // If we're here, everything went peachy... 197 setStatus(Ok); 198 m_currentPosition += m_pZipStream->total_out; 199 200 return true; 201} 202 203//-------------------------------------- 204bool ZipSubRStream::hasCapability(const Capability in_cap) const 205{ 206 return (csm_streamCaps & U32(in_cap)) != 0; 207} 208 209//-------------------------------------- 210U32 ZipSubRStream::getPosition() const 211{ 212 AssertFatal(m_pStream != NULL, "Error, not attached"); 213 214 return m_currentPosition; 215} 216 217//-------------------------------------- 218bool ZipSubRStream::setPosition(const U32 in_newPosition) 219{ 220 AssertFatal(m_pStream != NULL, "Error, not attached"); 221 222 if (in_newPosition == 0) 223 { 224 Stream* pStream = getStream(); 225 U32 resetPosition = m_originalSlavePosition; 226 U32 uncompressedSize = m_uncompressedSize; 227 detachStream(); 228 pStream->setPosition(resetPosition); 229 attachStream(pStream); 230 setUncompressedSize(uncompressedSize); 231 return true; 232 } 233 else 234 { 235 if (in_newPosition > m_uncompressedSize) 236 return false; 237 238 U32 newPosition = in_newPosition; 239 if (newPosition < m_currentPosition) 240 { 241 Stream* pStream = getStream(); 242 U32 resetPosition = m_originalSlavePosition; 243 U32 uncompressedSize = m_uncompressedSize; 244 detachStream(); 245 pStream->setPosition(resetPosition); 246 attachStream(pStream); 247 setUncompressedSize(uncompressedSize); 248 } 249 else 250 { 251 newPosition -= m_currentPosition; 252 } 253 254 bool bRet = true; 255 char *buffer = new char[2048]; 256 while (newPosition >= 2048) 257 { 258 newPosition -= 2048; 259 if (!_read(2048,buffer)) 260 { 261 bRet = false; 262 break; 263 } 264 }; 265 if (bRet && newPosition > 0) 266 { 267 if (!_read(newPosition,buffer)) 268 { 269 bRet = false; 270 }; 271 }; 272 273 delete [] buffer; 274 275 return bRet; 276 277 } 278} 279 280//-------------------------------------- 281U32 ZipSubRStream::getStreamSize() 282{ 283 AssertFatal(m_pStream != NULL, "No stream to size()"); 284 AssertFatal(m_uncompressedSize != 0, "No data? Properties probably not set..."); 285 286 return m_uncompressedSize; 287} 288 289//-------------------------------------- 290U32 ZipSubRStream::fillBuffer(const U32 in_attemptSize) 291{ 292 AssertFatal(m_pStream != NULL, "No stream to fill from?"); 293 AssertFatal(m_pStream->getStatus() != Stream::Closed, 294 "Fill from a closed stream?"); 295 296 U32 streamSize = m_pStream->getStreamSize(); 297 U32 currPos = m_pStream->getPosition(); 298 299 U32 actualReadSize; 300 if (in_attemptSize + currPos > streamSize) { 301 actualReadSize = streamSize - currPos; 302 } else { 303 actualReadSize = in_attemptSize; 304 } 305 306 if (m_pStream->read(actualReadSize, m_pInputBuffer) == true) { 307 return actualReadSize; 308 } else { 309 AssertWarn(false, "Read failed while trying to fill buffer"); 310 return 0; 311 } 312} 313 314 315//-------------------------------------------------------------------------- 316ZipSubWStream::ZipSubWStream() 317 : m_pStream(NULL), 318 m_pZipStream(NULL), 319 m_currPosition(0), 320 m_pOutputBuffer(NULL), 321 m_pInputBuffer(NULL), 322 m_lastBytesRead(0), 323 m_lastBytesWritten(0) 324{ 325 // 326} 327 328//-------------------------------------- 329ZipSubWStream::~ZipSubWStream() 330{ 331 detachStream(); 332} 333 334//-------------------------------------- 335bool ZipSubWStream::attachStream(Stream* io_pSlaveStream) 336{ 337 AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?"); 338 AssertFatal(m_pStream == NULL, "Already attached!"); 339 340 m_pStream = io_pSlaveStream; 341 m_currPosition = 0; 342 343 m_pOutputBuffer = new U8[csm_bufferSize]; 344 m_pInputBuffer = new U8[csm_bufferSize]; 345 346 // Initialize zipStream state... 347 m_pZipStream = new z_stream_s; 348 349 m_pZipStream->zalloc = Z_NULL; 350 m_pZipStream->zfree = Z_NULL; 351 m_pZipStream->opaque = Z_NULL; 352 353 m_pZipStream->next_in = m_pInputBuffer; 354 m_pZipStream->avail_in = csm_bufferSize; 355 m_pZipStream->total_in = 0; 356 m_pZipStream->next_out = m_pOutputBuffer; 357 m_pZipStream->avail_out = csm_bufferSize; 358 m_pZipStream->total_out = 0; 359 360 deflateInit2(m_pZipStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); 361 362 setStatus(Ok); 363 return true; 364} 365 366//-------------------------------------- 367void ZipSubWStream::detachStream() 368{ 369 // Must finish... 370 if (m_pZipStream != NULL) 371 { 372 m_pZipStream->avail_in = 0; 373 deflate(m_pZipStream, Z_FINISH); 374 375 // write the remainder 376 m_pStream->write(csm_bufferSize - m_pZipStream->avail_out, m_pOutputBuffer); 377 378 // close out zip stream... 379 deflateEnd(m_pZipStream); 380 381 delete m_pZipStream; 382 m_pZipStream = NULL; 383 384 delete [] m_pInputBuffer; 385 delete [] m_pOutputBuffer; 386 m_pInputBuffer = NULL; 387 m_pOutputBuffer = NULL; 388 } 389 390 m_pStream = NULL; 391 m_currPosition = 0; 392 setStatus(Closed); 393} 394 395//-------------------------------------- 396Stream* ZipSubWStream::getStream() 397{ 398 return m_pStream; 399} 400 401//-------------------------------------- 402bool ZipSubWStream::_read(const U32, void*) 403{ 404 AssertFatal(false, "Cannot read from a ZipSubWStream"); 405 406 setStatus(IllegalCall); 407 return false; 408} 409 410//-------------------------------------- 411bool ZipSubWStream::_write(const U32 numBytes, const void *pBuffer) 412{ 413 m_lastBytesWritten = 0; 414 if (numBytes == 0) 415 return true; 416 417 AssertFatal(pBuffer != NULL, "NULL input buffer"); 418 if (getStatus() == Closed) 419 { 420 AssertFatal(false, "Attempted write to a closed stream"); 421 return false; 422 } 423 424 m_pZipStream->next_in = (U8*)pBuffer; 425 m_pZipStream->avail_in = numBytes; 426 427 // write as many bufferSize chunks as possible 428 while(m_pZipStream->avail_in != 0) 429 { 430 if(m_pZipStream->avail_out == 0) 431 { 432 if(!m_pStream->write(csm_bufferSize, m_pOutputBuffer)) 433 return(false); 434 435 m_pZipStream->next_out = m_pOutputBuffer; 436 m_pZipStream->avail_out = csm_bufferSize; 437 } 438 439 S32 retVal = deflate(m_pZipStream, Z_NO_FLUSH); 440 AssertFatal(retVal != Z_BUF_ERROR, "ZipSubWStream::_write: invalid buffer"); 441 } 442 443 setStatus(Ok); 444 m_currPosition += m_pZipStream->total_out; 445 446 m_lastBytesWritten = m_pZipStream->total_out; 447 448 return true; 449} 450 451//-------------------------------------- 452bool ZipSubWStream::hasCapability(const Capability in_cap) const 453{ 454 return (csm_streamCaps & U32(in_cap)) != 0; 455} 456 457//-------------------------------------- 458U32 ZipSubWStream::getPosition() const 459{ 460 AssertFatal(m_pStream != NULL, "Error, not attached"); 461 462 return m_currPosition; 463} 464 465//-------------------------------------- 466bool ZipSubWStream::setPosition(const U32 /*in_newPosition*/) 467{ 468 AssertFatal(m_pStream != NULL, "Error, not attached"); 469 AssertFatal(false, "Not implemented!"); 470 471 // Erk. How do we do this. 472 return false; 473} 474 475U32 ZipSubWStream::getStreamSize() 476{ 477 AssertFatal(false, "Undecided how to implement this!"); 478 return 0; 479} 480 481