RageFileBasic *RageFileDriverZip::Open( const CString &sPath, int iMode, int &iErr )
{
	if( iMode & RageFile::WRITE )
	{
		iErr = ERROR_WRITING_NOT_SUPPORTED;
		return NULL;
	}

	FileInfo *info = (FileInfo *) FDB->GetFilePriv( sPath );
	if( info == NULL )
	{
		iErr = ENOENT;
		return NULL;
	}

	m_Mutex.Lock();

	/* If we havn't figured out the offset to the real data yet, do so now. */
	if( info->m_iDataOffset == -1 )
	{
		if( !ReadLocalFileHeader(*info) )
		{
			m_Mutex.Unlock();
			return NULL;
		}
	}

	/* We won't do any further access to zip, except to copy it (which is
	 * threadsafe), so we can unlock now. */
	m_Mutex.Unlock();

	RageFileDriverSlice *pSlice = new RageFileDriverSlice( m_pZip->Copy(), info->m_iDataOffset, info->m_iCompressedSize );
	pSlice->DeleteFileWhenFinished();
	
	switch( info->m_iCompressionMethod )
	{
	case STORED:
		return pSlice;
	case DEFLATED:
	{
		RageFileObjInflate *pInflate = new RageFileObjInflate( pSlice, info->m_iUncompressedSize );
		pInflate->DeleteFileWhenFinished();
		return pInflate;
	}
	default:
		/* unknown compression method */
		iErr = ENOSYS;
		return NULL;
	}
}
/*
 * 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 )
{
	auto_ptr<RageFileBasic> pFile(pFile_);

	sError = "";

	pFile->Seek(0);
	pFile->EnableCRC32( true );

	{
		char magic[2];
		FileReading::ReadBytes( *pFile, magic, 2, sError );
		if( sError != "" )
			return NULL;

		if( magic[0] != '\x1f' || magic[1] != '\x8b' )
		{
			sError = "Not a gzipped file";
			return NULL;
		}
	}

	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 != "" )
		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( *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 != "" )
			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. */
	pFile->EnableCRC32( false );

	if( sError != "" )
		return NULL;

	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 );
	if( iCRC32 != NULL )
		*iCRC32 = iExpectedCRC32;
	
	FileReading::Seek( *pFile, iDataPos, sError );
	
	if( sError != "" )
		return NULL;
	
	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. */
	if( iCRC32 != NULL )
		pInflateFile->EnableCRC32();

	return pInflateFile;
}