2011-03-17 01:47:30 -04:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "RageFileDriverDeflate.h"
|
|
|
|
|
#include "RageFileDriverSlice.h"
|
|
|
|
|
#include "RageFile.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
2011-03-19 19:28:57 -05:00
|
|
|
#if defined(_WINDOWS)
|
2011-07-20 19:49:37 -04:00
|
|
|
#include "zlib.h"
|
2011-03-19 19:28:57 -05:00
|
|
|
#if defined(_MSC_VER)
|
2014-10-17 21:23:00 -04:00
|
|
|
#if defined(BINARY_ZDL)
|
2011-07-20 19:49:37 -04:00
|
|
|
#pragma comment(lib, "zdll.lib")
|
2014-10-17 21:23:00 -04:00
|
|
|
// #else
|
|
|
|
|
// #pragma comment(lib, "zlib.lib")
|
|
|
|
|
#endif
|
2011-03-17 01:47:30 -04:00
|
|
|
#endif
|
|
|
|
|
#elif defined(MACOSX)
|
2011-07-20 19:49:37 -04:00
|
|
|
#include "zlib.h"
|
2011-03-17 01:47:30 -04:00
|
|
|
#else
|
2011-07-20 19:49:37 -04:00
|
|
|
#include <zlib.h>
|
2011-03-17 01:47:30 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
RageFileObjInflate::RageFileObjInflate( RageFileBasic *pFile, int iUncompressedSize )
|
|
|
|
|
{
|
|
|
|
|
m_bFileOwned = false;
|
|
|
|
|
m_pFile = pFile;
|
|
|
|
|
decomp_buf_avail = 0;
|
|
|
|
|
m_pInflate = new z_stream;
|
|
|
|
|
memset( m_pInflate, 0, sizeof(z_stream) );
|
|
|
|
|
|
|
|
|
|
m_iUncompressedSize = iUncompressedSize;
|
|
|
|
|
|
|
|
|
|
int err = inflateInit2( m_pInflate, -MAX_WBITS );
|
|
|
|
|
if( err == Z_MEM_ERROR )
|
|
|
|
|
RageException::Throw( "inflateInit2( %i ): out of memory.", -MAX_WBITS );
|
|
|
|
|
if( err != Z_OK )
|
|
|
|
|
WARN( ssprintf("Huh? inflateInit2() err = %i", err) );
|
|
|
|
|
|
|
|
|
|
decomp_buf_ptr = decomp_buf;
|
|
|
|
|
m_iFilePos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageFileObjInflate::RageFileObjInflate( const RageFileObjInflate &cpy ):
|
|
|
|
|
RageFileObj( cpy )
|
|
|
|
|
{
|
|
|
|
|
/* XXX completely untested */
|
|
|
|
|
/* Copy the entire decode state. */
|
|
|
|
|
m_pFile = cpy.m_pFile->Copy();
|
|
|
|
|
m_bFileOwned = true;
|
|
|
|
|
m_pInflate = new z_stream;
|
|
|
|
|
m_iUncompressedSize = cpy.m_iUncompressedSize;
|
|
|
|
|
m_iFilePos = cpy.m_iFilePos;
|
|
|
|
|
inflateCopy( m_pInflate, const_cast<z_stream*>(cpy.m_pInflate) );
|
|
|
|
|
|
|
|
|
|
decomp_buf_ptr = decomp_buf + (cpy.decomp_buf_ptr - cpy.decomp_buf);
|
|
|
|
|
decomp_buf_avail = cpy.decomp_buf_avail;
|
|
|
|
|
memcpy( decomp_buf, cpy.decomp_buf, decomp_buf_avail );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageFileObjInflate *RageFileObjInflate::Copy() const
|
|
|
|
|
{
|
|
|
|
|
return new RageFileObjInflate( *this );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RageFileObjInflate::~RageFileObjInflate()
|
|
|
|
|
{
|
|
|
|
|
if( m_bFileOwned )
|
|
|
|
|
delete m_pFile;
|
|
|
|
|
|
|
|
|
|
int err = inflateEnd( m_pInflate );
|
|
|
|
|
if( err != Z_OK )
|
|
|
|
|
WARN( ssprintf("Huh? inflateEnd() err = %i", err) );
|
|
|
|
|
|
|
|
|
|
delete m_pInflate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RageFileObjInflate::ReadInternal( void *buf, size_t bytes )
|
|
|
|
|
{
|
|
|
|
|
/* Don't read more than m_iUncompressedSize of data. If we don't do this, it's
|
|
|
|
|
* possible for a .gz to contain a header claiming 500k of data, but to actually
|
|
|
|
|
* contain much more deflated data. */
|
|
|
|
|
ASSERT_M( m_iFilePos <= m_iUncompressedSize, ssprintf("%i, %i",m_iFilePos, m_iUncompressedSize) );
|
2022-07-10 18:28:56 +03:00
|
|
|
bytes = std::min( bytes, size_t(m_iUncompressedSize-m_iFilePos) );
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
bool done=false;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
while( bytes && !done )
|
|
|
|
|
{
|
|
|
|
|
if ( !decomp_buf_avail )
|
|
|
|
|
{
|
|
|
|
|
decomp_buf_ptr = decomp_buf;
|
|
|
|
|
decomp_buf_avail = 0;
|
|
|
|
|
int got = m_pFile->Read( decomp_buf, sizeof(decomp_buf) );
|
|
|
|
|
if( got == -1 )
|
|
|
|
|
{
|
|
|
|
|
SetError( m_pFile->GetError() );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decomp_buf_avail = got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pInflate->next_in = (Bytef *) decomp_buf_ptr;
|
|
|
|
|
m_pInflate->avail_in = decomp_buf_avail;
|
|
|
|
|
m_pInflate->next_out = (Bytef *) buf;
|
|
|
|
|
m_pInflate->avail_out = bytes;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int err = inflate( m_pInflate, Z_SYNC_FLUSH );
|
|
|
|
|
switch( err )
|
|
|
|
|
{
|
|
|
|
|
case Z_DATA_ERROR:
|
|
|
|
|
SetError( "Data error" );
|
|
|
|
|
return -1;
|
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
|
SetError( "out of memory" );
|
|
|
|
|
return -1;
|
|
|
|
|
case Z_BUF_ERROR:
|
|
|
|
|
SetError( "file truncated" );
|
|
|
|
|
return -1;
|
|
|
|
|
case Z_STREAM_END:
|
|
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
case Z_OK:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
WARN( ssprintf("Huh? inflate err %i", err) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int used = (char *)m_pInflate->next_in - decomp_buf_ptr;
|
|
|
|
|
decomp_buf_ptr += used;
|
|
|
|
|
decomp_buf_avail -= used;
|
|
|
|
|
|
|
|
|
|
const int got = (char *)m_pInflate->next_out - (char *)buf;
|
|
|
|
|
m_iFilePos += got;
|
|
|
|
|
ret += got;
|
|
|
|
|
buf = (char *)buf + got;
|
|
|
|
|
bytes -= got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RageFileObjInflate::SeekInternal( int iPos )
|
|
|
|
|
{
|
|
|
|
|
/* Optimization: if offset is the end of the file, it's a lseek(0,SEEK_END). Don't
|
|
|
|
|
* decode anything. */
|
|
|
|
|
if( iPos >= m_iUncompressedSize )
|
|
|
|
|
{
|
|
|
|
|
m_iFilePos = m_iUncompressedSize;
|
|
|
|
|
m_pFile->Seek( m_pFile->GetFileSize() );
|
|
|
|
|
decomp_buf_ptr = decomp_buf;
|
|
|
|
|
decomp_buf_avail = 0;
|
|
|
|
|
inflateReset( m_pInflate );
|
|
|
|
|
return m_iUncompressedSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( iPos < m_iFilePos )
|
|
|
|
|
{
|
|
|
|
|
inflateReset( m_pInflate );
|
|
|
|
|
decomp_buf_ptr = decomp_buf;
|
|
|
|
|
decomp_buf_avail = 0;
|
|
|
|
|
|
|
|
|
|
m_pFile->Seek( 0 );
|
|
|
|
|
m_iFilePos = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int iOffset = iPos - m_iFilePos;
|
|
|
|
|
|
|
|
|
|
/* Can this be optimized? */
|
|
|
|
|
char buf[1024*4];
|
|
|
|
|
while( iOffset )
|
|
|
|
|
{
|
2022-07-10 18:28:56 +03:00
|
|
|
int got = ReadInternal( buf, std::min( (int) sizeof(buf), iOffset ) );
|
2011-03-17 01:47:30 -04:00
|
|
|
if( got == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if( got == 0 )
|
|
|
|
|
break;
|
|
|
|
|
iOffset -= got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m_iFilePos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageFileObjDeflate::RageFileObjDeflate( RageFileBasic *pFile )
|
|
|
|
|
{
|
|
|
|
|
m_pFile = pFile;
|
|
|
|
|
m_bFileOwned = false;
|
|
|
|
|
|
|
|
|
|
m_pDeflate = new z_stream;
|
|
|
|
|
memset( m_pDeflate, 0, sizeof(z_stream) );
|
|
|
|
|
|
|
|
|
|
int err = deflateInit2( m_pDeflate,
|
|
|
|
|
3,
|
|
|
|
|
Z_DEFLATED,
|
|
|
|
|
-15, // windowBits
|
|
|
|
|
8, // memLevel
|
|
|
|
|
Z_DEFAULT_STRATEGY );
|
|
|
|
|
|
|
|
|
|
if( err == Z_MEM_ERROR )
|
|
|
|
|
RageException::Throw( "inflateInit2( %i ): out of memory.", -MAX_WBITS );
|
|
|
|
|
if( err != Z_OK )
|
|
|
|
|
WARN( ssprintf("Huh? inflateInit2() err = %i", err) );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageFileObjDeflate::~RageFileObjDeflate()
|
|
|
|
|
{
|
|
|
|
|
FlushInternal();
|
|
|
|
|
|
|
|
|
|
if( m_bFileOwned )
|
|
|
|
|
delete m_pFile;
|
|
|
|
|
|
|
|
|
|
int err = deflateEnd( m_pDeflate );
|
|
|
|
|
if( err != Z_OK )
|
|
|
|
|
WARN( ssprintf("Huh? deflateEnd() err = %i", err) );
|
|
|
|
|
|
|
|
|
|
delete m_pDeflate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RageFileObjDeflate::WriteInternal( const void *pBuffer, size_t iBytes )
|
|
|
|
|
{
|
|
|
|
|
if( iBytes == 0 )
|
2016-03-27 21:52:05 -04:00
|
|
|
{
|
2011-03-17 01:47:30 -04:00
|
|
|
return 0;
|
2016-03-27 21:52:05 -04:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
m_pDeflate->next_in = (Bytef*) pBuffer;
|
|
|
|
|
m_pDeflate->avail_in = iBytes;
|
|
|
|
|
|
2016-03-27 21:52:05 -04:00
|
|
|
for(;;)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
char buf[1024*4];
|
|
|
|
|
m_pDeflate->next_out = (Bytef *) buf;
|
|
|
|
|
m_pDeflate->avail_out = sizeof(buf);
|
|
|
|
|
|
|
|
|
|
int err = deflate( m_pDeflate, Z_NO_FLUSH );
|
|
|
|
|
|
|
|
|
|
if( err != Z_OK )
|
2016-03-27 21:52:05 -04:00
|
|
|
{
|
2011-03-17 01:47:30 -04:00
|
|
|
FAIL_M( ssprintf("deflate: err %i", err) );
|
2016-03-27 21:52:05 -04:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
if( m_pDeflate->avail_out < sizeof(buf) )
|
|
|
|
|
{
|
|
|
|
|
int lBytes = sizeof(buf)-m_pDeflate->avail_out;
|
|
|
|
|
int iRet = m_pFile->Write( buf, lBytes );
|
|
|
|
|
if( iRet == -1 )
|
|
|
|
|
{
|
|
|
|
|
SetError( m_pFile->GetError() );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if( iRet < lBytes )
|
|
|
|
|
{
|
|
|
|
|
SetError( "Partial write" );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( m_pDeflate->avail_in == 0 && m_pDeflate->avail_out != 0 )
|
2016-03-27 21:52:05 -04:00
|
|
|
{
|
2011-03-17 01:47:30 -04:00
|
|
|
break;
|
2016-03-27 21:52:05 -04:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
return iBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Note that flushing clears compression state, so (unlike most Flush() calls)
|
|
|
|
|
* calling this *does* change the result of the output if you continue writing
|
|
|
|
|
* data. */
|
|
|
|
|
int RageFileObjDeflate::FlushInternal()
|
|
|
|
|
{
|
|
|
|
|
m_pDeflate->avail_in = 0;
|
|
|
|
|
|
2016-03-27 21:52:05 -04:00
|
|
|
for(;;)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
char buf[1024*4];
|
|
|
|
|
m_pDeflate->next_out = (Bytef *) buf;
|
|
|
|
|
m_pDeflate->avail_out = sizeof(buf);
|
|
|
|
|
|
|
|
|
|
int err = deflate( m_pDeflate, Z_FINISH );
|
|
|
|
|
if( err != Z_OK && err != Z_STREAM_END )
|
2016-03-27 21:52:05 -04:00
|
|
|
{
|
2011-03-17 01:47:30 -04:00
|
|
|
FAIL_M( ssprintf("deflate: err %i", err) );
|
2016-03-27 21:52:05 -04:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
if( m_pDeflate->avail_out < sizeof(buf) )
|
|
|
|
|
{
|
|
|
|
|
int iBytes = sizeof(buf)-m_pDeflate->avail_out;
|
|
|
|
|
int iRet = m_pFile->Write( buf, iBytes );
|
|
|
|
|
if( iRet == -1 )
|
|
|
|
|
{
|
|
|
|
|
SetError( m_pFile->GetError() );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if( iRet < iBytes )
|
|
|
|
|
{
|
|
|
|
|
SetError( "Partial write" );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( err == Z_STREAM_END && m_pDeflate->avail_out != 0 )
|
2016-03-27 21:52:05 -04:00
|
|
|
{
|
2011-03-17 01:47:30 -04:00
|
|
|
return m_pFile->Flush();
|
2016-03-27 21:52:05 -04:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parse a .gz file, check the header CRC16 if present, and return the data
|
|
|
|
|
* CRC32 and a decompressor. pFile will be deleted.
|
|
|
|
|
*/
|
|
|
|
|
RageFileObjInflate *GunzipFile( RageFileBasic *pFile_, RString &sError, uint32_t *iCRC32 )
|
|
|
|
|
{
|
2022-07-10 18:28:56 +03:00
|
|
|
std::unique_ptr<RageFileBasic> pFile(pFile_);
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
sError = "";
|
|
|
|
|
|
|
|
|
|
pFile->Seek(0);
|
|
|
|
|
pFile->EnableCRC32( true );
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
char magic[2];
|
|
|
|
|
FileReading::ReadBytes( *pFile, magic, 2, sError );
|
|
|
|
|
if( sError != "" )
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
if( magic[0] != '\x1f' || magic[1] != '\x8b' )
|
|
|
|
|
{
|
|
|
|
|
sError = "Not a gzipped file";
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t iCompressionMethod = FileReading::read_8( *pFile, sError );
|
|
|
|
|
uint8_t iFlags = FileReading::read_8( *pFile, sError );
|
|
|
|
|
FileReading::read_32_le( *pFile, sError ); /* time */
|
|
|
|
|
FileReading::read_8( *pFile, sError ); /* xfl */
|
|
|
|
|
FileReading::read_8( *pFile, sError ); /* os */
|
|
|
|
|
if( sError != "" )
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
#define FTEXT 1<<0
|
|
|
|
|
#define FHCRC 1<<1
|
|
|
|
|
#define FEXTRA 1<<2
|
|
|
|
|
#define FNAME 1<<3
|
|
|
|
|
#define FCOMMENT 1<<4
|
|
|
|
|
#define UNSUPPORTED_MASK ~((1<<5)-1)
|
|
|
|
|
if( iCompressionMethod != 8 )
|
|
|
|
|
{
|
|
|
|
|
sError = ssprintf( "Unsupported compression: %i", iCompressionMethod );
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Warning: flags other than FNAME are untested, since gzip doesn't
|
|
|
|
|
* actually output them. */
|
|
|
|
|
if( iFlags & UNSUPPORTED_MASK )
|
|
|
|
|
{
|
|
|
|
|
sError = ssprintf( "Unsupported flags: %x", iFlags );
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( iFlags & FEXTRA )
|
|
|
|
|
{
|
|
|
|
|
int16_t iSize = FileReading::read_16_le( *pFile, sError );
|
|
|
|
|
FileReading::SkipBytes( *pFile, iSize, sError );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( iFlags & FNAME )
|
|
|
|
|
while( sError == "" && FileReading::read_8( *pFile, sError ) != 0 )
|
|
|
|
|
;
|
|
|
|
|
if( iFlags & FCOMMENT )
|
|
|
|
|
while( sError == "" && FileReading::read_8( *pFile, sError ) != 0 )
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
if( iFlags & FHCRC )
|
|
|
|
|
{
|
|
|
|
|
/* Get the CRC of the data read so far. Be sure to do this before
|
|
|
|
|
* reading iExpectedCRC16. */
|
|
|
|
|
uint32_t iActualCRC32;
|
|
|
|
|
bool bOK = pFile->GetCRC32( &iActualCRC32 );
|
|
|
|
|
ASSERT( bOK );
|
|
|
|
|
|
|
|
|
|
uint16_t iExpectedCRC16 = FileReading::read_u16_le( *pFile, sError );
|
|
|
|
|
uint16_t iActualCRC16 = int16_t( iActualCRC32 & 0xFFFF );
|
|
|
|
|
if( sError != "" )
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
if( iActualCRC16 != iExpectedCRC16 )
|
|
|
|
|
{
|
|
|
|
|
sError = "Header CRC error";
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We only need CRC checking on the raw data for the header, so disable
|
|
|
|
|
* it. */
|
|
|
|
|
pFile->EnableCRC32( false );
|
|
|
|
|
|
|
|
|
|
if( sError != "" )
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
int iDataPos = pFile->Tell();
|
|
|
|
|
|
|
|
|
|
/* Seek to the end, and grab the uncompressed flie size and CRC. */
|
|
|
|
|
int iFooterPos = pFile->GetFileSize() - 8;
|
|
|
|
|
|
|
|
|
|
FileReading::Seek( *pFile, iFooterPos, sError );
|
|
|
|
|
|
|
|
|
|
uint32_t iExpectedCRC32 = FileReading::read_u32_le( *pFile, sError );
|
|
|
|
|
uint32_t iUncompressedSize = FileReading::read_u32_le( *pFile, sError );
|
2019-06-22 12:35:38 -07:00
|
|
|
if( iCRC32 != nullptr )
|
2011-03-17 01:47:30 -04:00
|
|
|
*iCRC32 = iExpectedCRC32;
|
|
|
|
|
|
|
|
|
|
FileReading::Seek( *pFile, iDataPos, sError );
|
|
|
|
|
|
|
|
|
|
if( sError != "" )
|
2019-06-22 12:35:38 -07:00
|
|
|
return nullptr;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
RageFileDriverSlice *pSliceFile = new RageFileDriverSlice( pFile.release(), iDataPos, iFooterPos-iDataPos );
|
|
|
|
|
pSliceFile->DeleteFileWhenFinished();
|
|
|
|
|
RageFileObjInflate *pInflateFile = new RageFileObjInflate( pSliceFile, iUncompressedSize );
|
|
|
|
|
pInflateFile->DeleteFileWhenFinished();
|
|
|
|
|
|
|
|
|
|
/* Enable CRC calculation only if the caller is interested. */
|
2019-06-22 12:35:38 -07:00
|
|
|
if( iCRC32 != nullptr )
|
2011-03-17 01:47:30 -04:00
|
|
|
pInflateFile->EnableCRC32();
|
|
|
|
|
|
|
|
|
|
return pInflateFile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Usage:
|
|
|
|
|
*
|
|
|
|
|
* RageFile output;
|
|
|
|
|
* output.Open( "hello.gz", RageFile::WRITE );
|
|
|
|
|
*
|
|
|
|
|
* RageFileObjGzip gzip( &output );
|
|
|
|
|
* gzip.Start();
|
|
|
|
|
* gzip.Write( "data" );
|
|
|
|
|
* gzip.Finish();
|
|
|
|
|
*/
|
|
|
|
|
RageFileObjGzip::RageFileObjGzip( RageFileBasic *pFile ):
|
|
|
|
|
RageFileObjDeflate( pFile )
|
|
|
|
|
{
|
|
|
|
|
m_iDataStartOffset = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write the gzip header. */
|
|
|
|
|
int RageFileObjGzip::Start()
|
|
|
|
|
{
|
|
|
|
|
/* We should be at the start of the file. */
|
|
|
|
|
ASSERT( this->Tell() == 0 );
|
|
|
|
|
|
|
|
|
|
static const char header[] =
|
|
|
|
|
{
|
|
|
|
|
'\x1f', '\x8b', // magic
|
|
|
|
|
8, // method: deflate
|
|
|
|
|
0, // no flags
|
|
|
|
|
0, 0, 0, 0, // no time
|
|
|
|
|
0, // no extra flags
|
|
|
|
|
'\xFF' // unknown os
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if( m_pFile->Write( header, sizeof(header) ) == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
m_iDataStartOffset = Tell();
|
|
|
|
|
|
|
|
|
|
/* Enable and reset the CRC32 for the uncompressed data about to be
|
|
|
|
|
* written to this file. */
|
|
|
|
|
this->EnableCRC32( true );
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write the gzip footer. */
|
|
|
|
|
int RageFileObjGzip::Finish()
|
|
|
|
|
{
|
|
|
|
|
/* We're about to write to the underlying file (so the footer isn't
|
|
|
|
|
* compressed). Flush the compressed data first. */
|
|
|
|
|
if( this->Flush() == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Read the CRC of the data that's been written. */
|
|
|
|
|
uint32_t iCRC;
|
|
|
|
|
bool bOK = this->GetCRC32( &iCRC );
|
|
|
|
|
ASSERT( bOK );
|
|
|
|
|
|
|
|
|
|
/* Figure out the size of the data. */
|
|
|
|
|
uint32_t iSize = Tell() - m_iDataStartOffset;
|
|
|
|
|
|
|
|
|
|
/* Write the CRC and size directly to the file, so they don't get compressed. */
|
|
|
|
|
iCRC = Swap32LE( iCRC );
|
|
|
|
|
if( m_pFile->Write( &iCRC, sizeof(iCRC) ) == -1 )
|
|
|
|
|
{
|
|
|
|
|
SetError( m_pFile->GetError() );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write the size. */
|
|
|
|
|
iSize = Swap32LE( iSize );
|
|
|
|
|
if( m_pFile->Write( &iSize, sizeof(iSize) ) == -1 )
|
|
|
|
|
{
|
|
|
|
|
SetError( m_pFile->GetError() );
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Flush the CRC and wize that we just wrote directly to the file. */
|
|
|
|
|
return m_pFile->Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include "RageFileDriverMemory.h"
|
|
|
|
|
|
|
|
|
|
void GzipString( const RString &sIn, RString &sOut )
|
|
|
|
|
{
|
|
|
|
|
/* Gzip it. */
|
|
|
|
|
RageFileObjMem mem;
|
|
|
|
|
RageFileObjGzip gzip( &mem );
|
|
|
|
|
gzip.Start();
|
|
|
|
|
gzip.Write( sIn );
|
|
|
|
|
gzip.Finish();
|
|
|
|
|
|
|
|
|
|
sOut = mem.GetString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GunzipString( const RString &sIn, RString &sOut, RString &sError )
|
|
|
|
|
{
|
|
|
|
|
RageFileObjMem *mem = new RageFileObjMem;
|
|
|
|
|
mem->PutString( sIn );
|
|
|
|
|
|
|
|
|
|
uint32_t iCRC32;
|
|
|
|
|
RageFileBasic *pFile = GunzipFile( mem, sError, &iCRC32 );
|
2019-06-22 12:35:38 -07:00
|
|
|
if( pFile == nullptr )
|
2011-03-17 01:47:30 -04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
pFile->Read( sOut );
|
|
|
|
|
|
|
|
|
|
/* Check the CRC. */
|
|
|
|
|
unsigned iRet;
|
|
|
|
|
ASSERT( pFile->GetCRC32( &iRet ) );
|
|
|
|
|
SAFE_DELETE( pFile );
|
|
|
|
|
|
|
|
|
|
if( iRet != iCRC32 )
|
|
|
|
|
{
|
|
|
|
|
sError = "CRC error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2003-2004 Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|