gzip/gunzip support

This commit is contained in:
Glenn Maynard
2004-12-13 04:18:04 +00:00
parent 1fdbf55723
commit 444c9e0b49
2 changed files with 215 additions and 0 deletions
+202
View File
@@ -1,5 +1,7 @@
#include "global.h"
#include "RageFileDriverDeflate.h"
#include "RageFileDriverSlice.h"
#include "RageFile.h"
#include "RageLog.h"
#include "RageUtil.h"
@@ -279,6 +281,206 @@ int RageFileObjDeflate::FlushInternal()
}
}
/*
* Parse a .gz file, check the header CRC16 if present, and return the data
* CRC32 and a decompressor.
*/
RageFileBasic *GunzipFile( RageFileBasic &file, CString &sError, uint32_t *iCRC32 )
{
sError = "";
file.Seek(0);
file.EnableCRC32( true );
{
char magic[2];
FileReading::ReadBytes( file, magic, 2, sError );
if( sError != "" )
return NULL;
if( magic[0] != '\x1f' || magic[1] != '\x8b' )
{
sError = "Not a gzip file";
return NULL;
}
}
uint8_t iCompressionMethod = FileReading::read_8( file, sError );
uint8_t iFlags = FileReading::read_8( file, sError );
FileReading::read_32_le( file, sError ); /* time */
FileReading::read_8( file, sError ); /* xfl */
FileReading::read_8( file, sError ); /* os */
if( sError != "" )
return NULL;
#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 );
return NULL;
}
/* Warning: flags other than FNAME are untested, since gzip doesn't
* actually output them. */
if( iFlags & UNSUPPORTED_MASK )
{
sError = ssprintf( "Unsupported flags: %x", iFlags );
return NULL;
}
if( iFlags & FEXTRA )
{
int16_t iSize = FileReading::read_16_le( file, sError );
FileReading::SkipBytes( file, iSize, sError );
}
if( iFlags & FNAME )
while( sError == "" && FileReading::read_8( file, sError ) != 0 )
;
if( iFlags & FCOMMENT )
while( sError == "" && FileReading::read_8( file, 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 = file.GetCRC32( &iActualCRC32 );
ASSERT( bOK );
uint16_t iExpectedCRC16 = FileReading::read_u16_le( file, sError );
uint16_t iActualCRC16 = int16_t( iActualCRC32 & 0xFFFF );
if( sError != "" )
return NULL;
if( iActualCRC16 != iExpectedCRC16 )
{
sError = "Header CRC error";
return NULL;
}
}
/* We only need CRC checking on the raw data for the header, so disable
* it. */
file.EnableCRC32( false );
if( sError != "" )
return NULL;
int iDataPos = file.Tell();
/* Seek to the end, and grab the uncompressed flie size and CRC. */
int iFooterPos = file.GetFileSize() - 8;
FileReading::Seek( file, iFooterPos, sError );
uint32_t iExpectedCRC32 = FileReading::read_u32_le( file, sError );
uint32_t iUncompressedSize = FileReading::read_u32_le( file, sError );
if( iCRC32 != NULL )
*iCRC32 = iExpectedCRC32;
FileReading::Seek( file, iDataPos, sError );
if( sError != "" )
return NULL;
RageFileDriverSlice *pSliceFile = new RageFileDriverSlice( &file, iDataPos, iFooterPos-iDataPos );
RageFileObjInflate *pInflateFile = new RageFileObjInflate( pSliceFile, iUncompressedSize );
pInflateFile->DeleteFileWhenFinished();
/* Enable CRC calculation only if the caller is interested. */
if( iCRC32 != NULL )
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
255 // 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;
}
return this->Flush();
}
/*
* Copyright (c) 2003-2004 Glenn Maynard
* All rights reserved.