From 382db74f8f7c34407ad0a7097fd55050d266508e Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Thu, 24 Mar 2005 09:40:56 +0000 Subject: [PATCH] generalize zip reading --- stepmania/src/RageFileDriverZip.cpp | 124 ++++++++++++++++------------ stepmania/src/RageFileDriverZip.h | 13 ++- 2 files changed, 82 insertions(+), 55 deletions(-) diff --git a/stepmania/src/RageFileDriverZip.cpp b/stepmania/src/RageFileDriverZip.cpp index db024cfe26..65707dcd65 100644 --- a/stepmania/src/RageFileDriverZip.cpp +++ b/stepmania/src/RageFileDriverZip.cpp @@ -55,9 +55,27 @@ RageFileDriverZip::RageFileDriverZip( CString path ): RageFileDriver( new NullFilenameDB ), m_Mutex( ssprintf("RageFileDriverZip(%s)", path.c_str()) ) { - if( !zip.Open(path) ) - RageException::Throw( "Couldn't open %s: %s", path.c_str(), zip.GetError().c_str() ); + m_bFileOwned = true; + m_sPath = path; + RageFile *pFile = new RageFile; + m_pZip = pFile; + + if( !pFile->Open(path) ) + RageException::Throw( "Couldn't open %s: %s", path.c_str(), pFile->GetError().c_str() ); + + ParseZipfile(); +} + +RageFileDriverZip::RageFileDriverZip( RageFileBasic *pFile ): + RageFileDriver( new NullFilenameDB ), + m_Mutex( ssprintf("RageFileDriverZip(%p)", pFile) ) +{ + m_sPath = ssprintf("%p", pFile); + + m_bFileOwned = false; + m_pZip = pFile; + ParseZipfile(); } @@ -83,9 +101,9 @@ static unsigned long makelong(const unsigned char *sig) + ((unsigned long)sig[0]); } -void RageFileDriverZip::ReadEndCentralRecord( RageFile &zip, end_central_dir_record &ec ) +void RageFileDriverZip::ReadEndCentralRecord( end_central_dir_record &ec ) { - const int OrigPos = zip.Tell(); + const int OrigPos = m_pZip->Tell(); typedef unsigned char ec_byte_rec[ ECREC_SIZE+4 ]; #define NUMBER_THIS_DISK 4 #define NUM_DISK_WITH_START_CENTRAL_DIR 6 @@ -96,11 +114,11 @@ void RageFileDriverZip::ReadEndCentralRecord( RageFile &zip, end_central_dir_rec #define ZIPFILE_COMMENT_LENGTH 20 ec_byte_rec byterec; - const int got = zip.Read( byterec, ECREC_SIZE+4 ); + const int got = m_pZip->Read( byterec, ECREC_SIZE+4 ); if( got == -1 ) - RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() ); + RageException::Throw( "Couldn't open %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() ); if ( got != ECREC_SIZE+4 ) - RageException::Throw( "%s: unexpected EOF", zip.GetPath().c_str() ); + RageException::Throw( "%s: unexpected EOF", m_sPath.c_str() ); ec.number_this_disk = makeword(&byterec[NUMBER_THIS_DISK]); ec.num_disk_start_cdir = makeword(&byterec[NUM_DISK_WITH_START_CENTRAL_DIR]); @@ -112,19 +130,19 @@ void RageFileDriverZip::ReadEndCentralRecord( RageFile &zip, end_central_dir_rec const int expect_ecrec_offset = ec.offset_start_central_directory + ec.size_central_directory; if( expect_ecrec_offset > OrigPos ) - RageException::Throw( "Couldn't open %s: missing %ld bytes in zipfile", zip.GetPath().c_str(), + RageException::Throw( "Couldn't open %s: missing %ld bytes in zipfile", m_sPath.c_str(), expect_ecrec_offset - OrigPos ); } void RageFileDriverZip::ParseZipfile() { /* Look for the end-central record. */ - const int searchlen = min( zip.GetFileSize(), 66000 ); - const int Size = zip.GetFileSize(); + const int searchlen = min( m_pZip->GetFileSize(), 66000 ); + const int Size = m_pZip->GetFileSize(); int realpos = Size; /* Loop through blocks of data, starting at the end. In general, need not - * check whole zipfile for signature, but may want to do so if testing. */ + * check whole zim_pZip for signature, but may want to do so if testing. */ int real_ecrec_offset = -1; @@ -134,15 +152,15 @@ void RageFileDriverZip::ParseZipfile() { realpos -= INBUFSIZE; realpos = max( 0, realpos ); - zip.Seek( realpos ); - int got = zip.Read( tmp+tmp_used, sizeof(tmp)-tmp_used ); + m_pZip->Seek( realpos ); + int got = m_pZip->Read( tmp+tmp_used, sizeof(tmp)-tmp_used ); if( got == -1 ) - RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() ); + RageException::Throw( "Couldn't open %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() ); if( got == 0 ) break; /* fall through and fail */ got += tmp_used; - /* 'P' must be at least (ECREC_SIZE+4) bytes from end of zipfile */ + /* 'P' must be at least (ECREC_SIZE+4) bytes from end of zim_pZip */ for( int pos = got-(ECREC_SIZE+4); real_ecrec_offset == -1 && pos >= 0; --pos ) { const char *p = tmp+pos; @@ -160,34 +178,34 @@ void RageFileDriverZip::ParseZipfile() } if( real_ecrec_offset == -1 ) - RageException::Throw( "Couldn't open %s: End-of-central-directory signature not found", zip.GetPath().c_str() ); + RageException::Throw( "Couldn't open %s: End-of-central-directory signature not found", m_sPath.c_str() ); - zip.Seek( real_ecrec_offset ); + m_pZip->Seek( real_ecrec_offset ); /* Get the end-central data. */ end_central_dir_record ec; - ReadEndCentralRecord( zip, ec ); + ReadEndCentralRecord( ec ); bool something = ec.number_this_disk == 1 && ec.num_disk_start_cdir == 1; if (!something && ec.number_this_disk != 0) - RageException::Throw( "Couldn't open %s: zipfile is part of multi-disk archive", zip.GetPath().c_str() ); + RageException::Throw( "Couldn't open %s: zipfile is part of multi-disk archive", m_sPath.c_str() ); /* Seek to where the start of central directory should be, and make * sure it's there. */ - zip.Seek( ec.offset_start_central_directory ); + m_pZip->Seek( ec.offset_start_central_directory ); CString sig; - int got = zip.Read( sig, 4 ); + int got = m_pZip->Read( sig, 4 ); if( got != 4 || sig != central_hdr_sig ) - RageException::Throw( "Couldn't open %s: start of central directory not found; zipfile corrupt", zip.GetPath().c_str() ); + RageException::Throw( "Couldn't open %s: start of central directory not found; zipfile corrupt", m_sPath.c_str() ); - zip.Seek( ec.offset_start_central_directory ); + m_pZip->Seek( ec.offset_start_central_directory ); /* Loop through files in central directory. */ while(1) { CString sig; - if ( zip.Read( sig, 4 ) != 4 ) + if ( m_pZip->Read( sig, 4 ) != 4 ) break; /* Is it a new entry? */ @@ -199,22 +217,22 @@ void RageFileDriverZip::ParseZipfile() { /* Are we at the end_central record? */ if( sig != end_central_sig ) - LOG->Warn( "%s: expected end central file header signature not found", zip.GetPath().c_str() ); + LOG->Warn( "%s: expected end central file header signature not found", m_sPath.c_str() ); } else { - LOG->Warn( "%s: expected central file header signature not found", zip.GetPath().c_str() ); + LOG->Warn( "%s: expected central file header signature not found", m_sPath.c_str() ); } break; } FileInfo info; info.data_offset = -1; - int got = ProcessCdirFileHdr(zip, info); + int got = ProcessCdirFileHdr( info ); if( got == -1 ) /* error */ break; if( got == 0 ) /* skip */ continue; - zip.Seek( zip.Tell() + info.extra_field_length ); + m_pZip->Seek( m_pZip->Tell() + info.extra_field_length ); FileInfo *pInfo = new FileInfo(info); Files.push_back( pInfo ); @@ -241,19 +259,19 @@ typedef unsigned char cdir_byte_hdr[ CREC_SIZE ]; #define C_INTERNAL_FILE_ATTRIBUTES 32 #define C_EXTERNAL_FILE_ATTRIBUTES 34 #define C_RELATIVE_OFFSET_LOCAL_HEADER 38 -int RageFileDriverZip::ProcessCdirFileHdr( RageFile &zip, FileInfo &info ) +int RageFileDriverZip::ProcessCdirFileHdr( FileInfo &info ) { /* Read the next central directory entry and do any necessary machine-type * conversions (byte ordering, structure padding compensation--do so by * copying the data from the array into which it was read (byterec) to the * usable struct. */ cdir_byte_hdr byterec; - int got = zip.Read( (char *)byterec, CREC_SIZE ); + int got = m_pZip->Read( (char *)byterec, CREC_SIZE ); if ( got == -1 ) - RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() ); + RageException::Throw( "Couldn't open %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() ); if ( got != CREC_SIZE ) { - LOG->Warn( "%s: unexpected EOF", zip.GetPath().c_str() ); + LOG->Warn( "%s: unexpected EOF", m_sPath.c_str() ); return -1; } @@ -263,7 +281,7 @@ int RageFileDriverZip::ProcessCdirFileHdr( RageFile &zip, FileInfo &info ) if( version_needed_to_extract > 20 ) /* compatible with PKUNZIP 2.0 */ { LOG->Warn( "File \"%s\" in \"%s\" uses unsupported ZIP version %i.%i", - info.fn.c_str(), zip.GetRealPath().c_str(), + info.fn.c_str(), m_sPath.c_str(), version_needed_to_extract / 10, version_needed_to_extract % 10 ); return 0; } @@ -271,8 +289,7 @@ int RageFileDriverZip::ProcessCdirFileHdr( RageFile &zip, FileInfo &info ) const int general_purpose_bit_flag = makeword(&byterec[C_GENERAL_PURPOSE_BIT_FLAG]); if( general_purpose_bit_flag & 1 ) { - LOG->Warn( "Skipped encrypted \"%s\" in \"%s\"", - info.fn.c_str(), zip.GetRealPath().c_str() ); + LOG->Warn( "Skipped encrypted \"%s\" in \"%s\"", info.fn.c_str(), m_sPath.c_str() ); return 0; } @@ -293,19 +310,19 @@ int RageFileDriverZip::ProcessCdirFileHdr( RageFile &zip, FileInfo &info ) if( external_file_attributes & 0x08 ) return 0; - got = zip.Read( info.fn, info.filename_length ); + got = m_pZip->Read( info.fn, info.filename_length ); if( got == -1 ) - RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() ); + RageException::Throw( "Couldn't open %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() ); if( got != info.filename_length ) { - LOG->Warn( "%s: bad filename length %li", zip.GetPath().c_str(), info.filename_length ); + LOG->Warn( "%s: bad filename length %li", m_sPath.c_str(), info.filename_length ); return 0; } if( info.compression_method != STORED && info.compression_method != DEFLATED ) { LOG->Warn( "File \"%s\" in \"%s\" uses unsupported compression method %i", - info.fn.c_str(), zip.GetRealPath().c_str(), info.compression_method ); + info.fn.c_str(), m_sPath.c_str(), info.compression_method ); return 0; } @@ -317,6 +334,9 @@ RageFileDriverZip::~RageFileDriverZip() { for( unsigned i = 0; i < Files.size(); ++i ) delete Files[i]; + + if( m_bFileOwned ) + delete m_pZip; } RageFileBasic *RageFileDriverZip::Open( const CString &path, int mode, int &err ) @@ -339,48 +359,48 @@ RageFileBasic *RageFileDriverZip::Open( const CString &path, int mode, int &err /* If we havn't figured out the offset to the real data yet, do so now. */ if( info->data_offset == -1 ) { - zip.Seek( info->offset ); + m_pZip->Seek( info->offset ); /* Should be in proper position now, so check for sig. */ CString sig; - int got = zip.Read( sig, 4 ); + int got = m_pZip->Read( sig, 4 ); if( got == -1 ) - RageException::Throw( "Read error in %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() ); + RageException::Throw( "Read error in %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() ); if( got != 4 ) - RageException::Throw( "%s: unexpected EOF", zip.GetPath().c_str() ); + RageException::Throw( "%s: unexpected EOF", m_sPath.c_str() ); if( sig != local_hdr_sig ) - RageException::Throw( "%s: bad zipfile offset", zip.GetPath().c_str() ); + RageException::Throw( "%s: bad zipfile offset", m_sPath.c_str() ); #define LREC_SIZE 26 /* lengths of local file headers, central */ #define L_FILENAME_LENGTH 22 #define L_EXTRA_FIELD_LENGTH 24 unsigned char byterec[LREC_SIZE]; - got = zip.Read( (char *)byterec, LREC_SIZE ); + got = m_pZip->Read( (char *)byterec, LREC_SIZE ); if( got == -1 ) - RageException::Throw( "Read error in %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() ); + RageException::Throw( "Read error in %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() ); if( got != LREC_SIZE ) - RageException::Throw( "%s: unexpected EOF", zip.GetPath().c_str() ); + RageException::Throw( "%s: unexpected EOF", m_sPath.c_str() ); const int filename_length = makeword(&byterec[L_FILENAME_LENGTH]); const int extra_field_length = makeword(&byterec[L_EXTRA_FIELD_LENGTH]); - info->data_offset = zip.Tell() + filename_length + extra_field_length; + info->data_offset = m_pZip->Tell() + filename_length + extra_field_length; } /* 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 *pFile = new RageFileDriverSlice( zip.Copy(), info->data_offset, info->compr_size ); - pFile->DeleteFileWhenFinished(); + RageFileDriverSlice *pSlice = new RageFileDriverSlice( m_pZip->Copy(), info->data_offset, info->compr_size ); + pSlice->DeleteFileWhenFinished(); switch( info->compression_method ) { case STORED: - return pFile; + return pSlice; case DEFLATED: { - RageFileObjInflate *pInflate = new RageFileObjInflate( pFile, info->uncompr_size ); + RageFileObjInflate *pInflate = new RageFileObjInflate( pSlice, info->uncompr_size ); pInflate->DeleteFileWhenFinished(); return pInflate; } diff --git a/stepmania/src/RageFileDriverZip.h b/stepmania/src/RageFileDriverZip.h index b1ca96e632..ebc01c5039 100644 --- a/stepmania/src/RageFileDriverZip.h +++ b/stepmania/src/RageFileDriverZip.h @@ -12,22 +12,29 @@ class RageFileDriverZip: public RageFileDriver { public: RageFileDriverZip( CString path ); + RageFileDriverZip( RageFileBasic *pFile ); virtual ~RageFileDriverZip(); RageFileBasic *Open( const CString &path, int mode, int &err ); void FlushDirCache( const CString &sPath ); + void DeleteFileWhenFinished() { m_bFileOwned = true; } + private: - RageFile zip; + bool m_bFileOwned; + + RageFileBasic *m_pZip; vector Files; + CString m_sPath; + /* Open() must be threadsafe. Mutex access to "zip", since we seek * around in it when reading files. */ RageMutex m_Mutex; void ParseZipfile(); - static void ReadEndCentralRecord( RageFile &zip, end_central_dir_record &ec ); - static int ProcessCdirFileHdr( RageFile &zip, FileInfo &info ); + void ReadEndCentralRecord( end_central_dir_record &ec ); + int ProcessCdirFileHdr( FileInfo &info ); }; #endif