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; }