rewrite ZIP parsing. only a tiny bit of code was used from InfoZIP, causing
us to have to lug around a proportionately unreasonably big chunk of licensing text. (this code is smaller, too.)
This commit is contained in:
+192
-315
@@ -1,8 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Derived from Info-ZIP 5.50. Heavily stripped and rewritten. Only retains
|
* Ref: http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
|
||||||
* STORE and DEFLATE decompression types; that's all that's in use these days.
|
|
||||||
* Could probably readd SHRINK easily.
|
|
||||||
* - Glenn
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
@@ -14,55 +11,40 @@
|
|||||||
#include "RageUtil_FileDB.h"
|
#include "RageUtil_FileDB.h"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
#define INBUFSIZE 1024*4
|
|
||||||
|
|
||||||
static struct FileDriverEntry_ZIP: public FileDriverEntry
|
static struct FileDriverEntry_ZIP: public FileDriverEntry
|
||||||
{
|
{
|
||||||
FileDriverEntry_ZIP(): FileDriverEntry( "ZIP" ) { }
|
FileDriverEntry_ZIP(): FileDriverEntry( "ZIP" ) { }
|
||||||
RageFileDriver *Create( CString Root ) const { return new RageFileDriverZip( Root ); }
|
RageFileDriver *Create( CString Root ) const { return new RageFileDriverZip( Root ); }
|
||||||
} const g_RegisterDriver;
|
} const g_RegisterDriver;
|
||||||
|
|
||||||
|
enum ZipCompressionMethod { STORED = 0, DEFLATED = 8 };
|
||||||
|
|
||||||
struct FileInfo
|
struct FileInfo
|
||||||
{
|
{
|
||||||
CString fn;
|
CString m_sName;
|
||||||
long offset;
|
int m_iOffset;
|
||||||
long data_offset;
|
int m_iDataOffset;
|
||||||
|
|
||||||
long extra_field_length;
|
ZipCompressionMethod m_iCompressionMethod;
|
||||||
long filename_length;
|
int m_iCRC32;
|
||||||
unsigned short compression_method;
|
int m_iCompressedSize, m_iUncompressedSize;
|
||||||
unsigned long crc; /* crc (needed if extended header) */
|
|
||||||
unsigned long compr_size; /* compressed size (needed if extended header) */
|
|
||||||
unsigned long uncompr_size; /* uncompressed size (needed if extended header) */
|
|
||||||
unsigned short diskstart; /* no of volume where this entry starts */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct end_central_dir_record
|
RageFileDriverZip::RageFileDriverZip( CString sPath ):
|
||||||
{
|
|
||||||
unsigned short number_this_disk;
|
|
||||||
unsigned short num_disk_start_cdir;
|
|
||||||
unsigned short num_entries_centrl_dir_ths_disk;
|
|
||||||
unsigned short total_entries_central_dir;
|
|
||||||
unsigned long size_central_directory;
|
|
||||||
unsigned long offset_start_central_directory;
|
|
||||||
unsigned short zipfile_comment_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define STORED 0
|
|
||||||
#define DEFLATED 8
|
|
||||||
|
|
||||||
RageFileDriverZip::RageFileDriverZip( CString path ):
|
|
||||||
RageFileDriver( new NullFilenameDB ),
|
RageFileDriver( new NullFilenameDB ),
|
||||||
m_Mutex( ssprintf("RageFileDriverZip(%s)", path.c_str()) )
|
m_Mutex( ssprintf("RageFileDriverZip(%s)", sPath.c_str()) )
|
||||||
{
|
{
|
||||||
m_bFileOwned = true;
|
m_bFileOwned = true;
|
||||||
m_sPath = path;
|
m_sPath = sPath;
|
||||||
|
|
||||||
RageFile *pFile = new RageFile;
|
RageFile *pFile = new RageFile;
|
||||||
m_pZip = pFile;
|
m_pZip = pFile;
|
||||||
|
|
||||||
if( !pFile->Open(path) )
|
if( !pFile->Open(sPath) )
|
||||||
RageException::Throw( "Couldn't open %s: %s", path.c_str(), pFile->GetError().c_str() );
|
{
|
||||||
|
LOG->Warn( "Couldn't open %s: %s", sPath.c_str(), pFile->GetError().c_str() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ParseZipfile();
|
ParseZipfile();
|
||||||
}
|
}
|
||||||
@@ -79,250 +61,159 @@ RageFileDriverZip::RageFileDriverZip( RageFileBasic *pFile ):
|
|||||||
ParseZipfile();
|
ParseZipfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ECREC_SIZE 18
|
|
||||||
|
|
||||||
static CString central_hdr_sig = "\x50\x4B\x01\x02";
|
bool RageFileDriverZip::ReadEndCentralRecord( int &iTotalEntries, int &iCentralDirectoryOffset )
|
||||||
static CString local_hdr_sig = "\x50\x4B\x03\x04";
|
|
||||||
static CString end_central_sig = "\x50\x4B\x05\x06";
|
|
||||||
|
|
||||||
|
|
||||||
/* XXX */
|
|
||||||
static unsigned short makeword(const unsigned char *b)
|
|
||||||
{
|
{
|
||||||
return (unsigned short)((b[1] << 8) | b[0]);
|
CString sError;
|
||||||
|
CString sSig = FileReading::ReadString( *m_pZip, 4, sError );
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip number of this disk */
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip disk with central directory */
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip number of entries on this disk */
|
||||||
|
iTotalEntries = FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
FileReading::read_32_le( *m_pZip, sError ); /* skip size of the central directory */
|
||||||
|
iCentralDirectoryOffset = FileReading::read_32_le( *m_pZip, sError );
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip zipfile comment length */
|
||||||
|
|
||||||
|
if( sError != "" )
|
||||||
|
{
|
||||||
|
LOG->Warn( "%s: %s", m_sPath.c_str(), sError.c_str() );
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
static unsigned long makelong(const unsigned char *sig)
|
|
||||||
{
|
|
||||||
return (((unsigned long)sig[3]) << 24)
|
|
||||||
+ (((unsigned long)sig[2]) << 16)
|
|
||||||
+ (((unsigned long)sig[1]) << 8)
|
|
||||||
+ ((unsigned long)sig[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RageFileDriverZip::ReadEndCentralRecord( end_central_dir_record &ec )
|
/* Find the end of central directory record, and seek to it. */
|
||||||
|
bool RageFileDriverZip::SeekToEndCentralRecord()
|
||||||
{
|
{
|
||||||
const int OrigPos = m_pZip->Tell();
|
const int iSearchTo = max( m_pZip->GetFileSize() - 1024*32, 0 );
|
||||||
typedef unsigned char ec_byte_rec[ ECREC_SIZE+4 ];
|
int iRealPos = m_pZip->GetFileSize();
|
||||||
#define NUMBER_THIS_DISK 4
|
|
||||||
#define NUM_DISK_WITH_START_CENTRAL_DIR 6
|
|
||||||
#define NUM_ENTRIES_CENTRL_DIR_THS_DISK 8
|
|
||||||
#define TOTAL_ENTRIES_CENTRAL_DIR 10
|
|
||||||
#define SIZE_CENTRAL_DIRECTORY 12
|
|
||||||
#define OFFSET_START_CENTRAL_DIRECTORY 16
|
|
||||||
#define ZIPFILE_COMMENT_LENGTH 20
|
|
||||||
|
|
||||||
ec_byte_rec byterec;
|
while( iRealPos > 0 && iRealPos >= iSearchTo )
|
||||||
const int got = m_pZip->Read( byterec, ECREC_SIZE+4 );
|
{
|
||||||
if( got == -1 )
|
/* Move back in the file; leave some overlap between checks, to handle
|
||||||
RageException::Throw( "Couldn't open %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() );
|
* the case where the signature crosses the block boundary. */
|
||||||
if ( got != ECREC_SIZE+4 )
|
char buf[1024*4];
|
||||||
RageException::Throw( "%s: unexpected EOF", m_sPath.c_str() );
|
iRealPos -= sizeof(buf) - 4;
|
||||||
|
iRealPos = max( 0, iRealPos );
|
||||||
|
m_pZip->Seek( iRealPos );
|
||||||
|
|
||||||
ec.number_this_disk = makeword(&byterec[NUMBER_THIS_DISK]);
|
int iGot = m_pZip->Read( buf, sizeof(buf) );
|
||||||
ec.num_disk_start_cdir = makeword(&byterec[NUM_DISK_WITH_START_CENTRAL_DIR]);
|
if( iGot == -1 )
|
||||||
ec.num_entries_centrl_dir_ths_disk = makeword(&byterec[NUM_ENTRIES_CENTRL_DIR_THS_DISK]);
|
{
|
||||||
ec.total_entries_central_dir = makeword(&byterec[TOTAL_ENTRIES_CENTRAL_DIR]);
|
LOG->Warn( "%s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() );
|
||||||
ec.size_central_directory = makelong(&byterec[SIZE_CENTRAL_DIRECTORY]);
|
return false;
|
||||||
ec.offset_start_central_directory = makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]);
|
}
|
||||||
ec.zipfile_comment_length = makeword(&byterec[ZIPFILE_COMMENT_LENGTH]);
|
|
||||||
|
|
||||||
const int expect_ecrec_offset = ec.offset_start_central_directory + ec.size_central_directory;
|
for( int iPos = iGot; iPos >= 0; --iPos )
|
||||||
if( expect_ecrec_offset > OrigPos )
|
{
|
||||||
RageException::Throw( "Couldn't open %s: missing %ld bytes in zipfile", m_sPath.c_str(),
|
if( memcmp(buf + iPos, "\x50\x4B\x05\x06", 4) )
|
||||||
expect_ecrec_offset - OrigPos );
|
continue;
|
||||||
|
|
||||||
|
m_pZip->Seek( iRealPos + iPos );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RageFileDriverZip::ParseZipfile()
|
void RageFileDriverZip::ParseZipfile()
|
||||||
{
|
{
|
||||||
/* Look for the end-central record. */
|
if( !SeekToEndCentralRecord() )
|
||||||
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 zim_pZip for signature, but may want to do so if testing. */
|
|
||||||
|
|
||||||
int real_ecrec_offset = -1;
|
|
||||||
|
|
||||||
char tmp[INBUFSIZE];
|
|
||||||
int tmp_used = 0;
|
|
||||||
while( real_ecrec_offset == -1 && realpos > 0 && realpos >= Size-searchlen )
|
|
||||||
{
|
{
|
||||||
realpos -= INBUFSIZE;
|
LOG->Warn( "Couldn't open %s: couldn't find end of central directory record", m_sPath.c_str() );
|
||||||
realpos = max( 0, realpos );
|
return;
|
||||||
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", 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 zim_pZip */
|
|
||||||
for( int pos = got-(ECREC_SIZE+4); real_ecrec_offset == -1 && pos >= 0; --pos )
|
|
||||||
{
|
|
||||||
const char *p = tmp+pos;
|
|
||||||
if ( *p != (unsigned char)0x50 ) /* ASCII 'P' */
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( strncmp((char *)p, end_central_sig, 4))
|
|
||||||
continue;
|
|
||||||
real_ecrec_offset = realpos + pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sig may span block boundary: */
|
/* Read the end of central directory record. */
|
||||||
memcpy((char *)tmp+INBUFSIZE-3, (char *)tmp, 3);
|
int iTotalEntries, iCentralDirectoryOffset;
|
||||||
tmp_used = 3;
|
if( !ReadEndCentralRecord(iTotalEntries, iCentralDirectoryOffset) )
|
||||||
}
|
return; /* warned already */
|
||||||
|
|
||||||
if( real_ecrec_offset == -1 )
|
/* Seek to the start of the central file directory. */
|
||||||
RageException::Throw( "Couldn't open %s: End-of-central-directory signature not found", m_sPath.c_str() );
|
m_pZip->Seek( iCentralDirectoryOffset );
|
||||||
|
|
||||||
m_pZip->Seek( real_ecrec_offset );
|
|
||||||
|
|
||||||
/* Get the end-central data. */
|
|
||||||
end_central_dir_record 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", m_sPath.c_str() );
|
|
||||||
|
|
||||||
/* Seek to where the start of central directory should be, and make
|
|
||||||
* sure it's there. */
|
|
||||||
m_pZip->Seek( ec.offset_start_central_directory );
|
|
||||||
|
|
||||||
CString sig;
|
|
||||||
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", m_sPath.c_str() );
|
|
||||||
|
|
||||||
m_pZip->Seek( ec.offset_start_central_directory );
|
|
||||||
|
|
||||||
/* Loop through files in central directory. */
|
/* Loop through files in central directory. */
|
||||||
while(1)
|
for( int i = 0; i < iTotalEntries; ++i )
|
||||||
{
|
{
|
||||||
CString sig;
|
|
||||||
if ( m_pZip->Read( sig, 4 ) != 4 )
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Is it a new entry? */
|
|
||||||
if( sig != central_hdr_sig )
|
|
||||||
{
|
|
||||||
/* Not a new central directory entry. Is the number of processed entries
|
|
||||||
* compatible with the number of entries as stored in the end_central record? */
|
|
||||||
if( Files.size() == (unsigned)ec.total_entries_central_dir )
|
|
||||||
{
|
|
||||||
/* Are we at the end_central record? */
|
|
||||||
if( sig != end_central_sig )
|
|
||||||
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", m_sPath.c_str() );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
info.data_offset = -1;
|
info.m_iDataOffset = -1;
|
||||||
int got = ProcessCdirFileHdr( info );
|
int got = ProcessCdirFileHdr( info );
|
||||||
if( got == -1 ) /* error */
|
if( got == -1 ) /* error */
|
||||||
break;
|
break;
|
||||||
if( got == 0 ) /* skip */
|
if( got == 0 ) /* skip */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_pZip->Seek( m_pZip->Tell() + info.extra_field_length );
|
|
||||||
|
|
||||||
FileInfo *pInfo = new FileInfo( info );
|
FileInfo *pInfo = new FileInfo( info );
|
||||||
Files.push_back( pInfo );
|
m_pFiles.push_back( pInfo );
|
||||||
FDB->AddFile( pInfo->fn, pInfo->uncompr_size, pInfo->crc, pInfo );
|
FDB->AddFile( pInfo->m_sName, pInfo->m_iUncompressedSize, pInfo->m_iCRC32, pInfo );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( m_pFiles.size() == 0 )
|
||||||
|
LOG->Warn( "%s: no files found in central file header", m_sPath.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CREC_SIZE 42
|
|
||||||
typedef unsigned char cdir_byte_hdr[ CREC_SIZE ];
|
|
||||||
#define C_VERSION_MADE_BY_0 0
|
|
||||||
#define C_VERSION_MADE_BY_1 1
|
|
||||||
#define C_VERSION_NEEDED_TO_EXTRACT_0 2
|
|
||||||
#define C_VERSION_NEEDED_TO_EXTRACT_1 3
|
|
||||||
#define C_GENERAL_PURPOSE_BIT_FLAG 4
|
|
||||||
#define C_COMPRESSION_METHOD 6
|
|
||||||
#define C_LAST_MOD_DOS_DATETIME 8
|
|
||||||
#define C_CRC32 12
|
|
||||||
#define C_COMPRESSED_SIZE 16
|
|
||||||
#define C_UNCOMPRESSED_SIZE 20
|
|
||||||
#define C_FILENAME_LENGTH 24
|
|
||||||
#define C_EXTRA_FIELD_LENGTH 26
|
|
||||||
#define C_FILE_COMMENT_LENGTH 28
|
|
||||||
#define C_DISK_NUMBER_START 30
|
|
||||||
#define C_INTERNAL_FILE_ATTRIBUTES 32
|
|
||||||
#define C_EXTERNAL_FILE_ATTRIBUTES 34
|
|
||||||
#define C_RELATIVE_OFFSET_LOCAL_HEADER 38
|
|
||||||
int RageFileDriverZip::ProcessCdirFileHdr( FileInfo &info )
|
int RageFileDriverZip::ProcessCdirFileHdr( FileInfo &info )
|
||||||
{
|
{
|
||||||
/* Read the next central directory entry and do any necessary machine-type
|
CString sError;
|
||||||
* conversions (byte ordering, structure padding compensation--do so by
|
CString sSig = FileReading::ReadString( *m_pZip, 4, sError );
|
||||||
* copying the data from the array into which it was read (byterec) to the
|
if( sSig != "\x50\x4B\x01\x02" )
|
||||||
* usable struct. */
|
|
||||||
cdir_byte_hdr byterec;
|
|
||||||
int got = m_pZip->Read( (char *)byterec, CREC_SIZE );
|
|
||||||
if ( got == -1 )
|
|
||||||
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", m_sPath.c_str() );
|
LOG->Warn( "%s: central directory record signature not found", m_sPath.c_str() );
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// crec.version_made_by[0] = byterec[C_VERSION_MADE_BY_0];
|
FileReading::read_16_le( *m_pZip, sError ); /* skip version made by */
|
||||||
// crec.version_made_by[1] = byterec[C_VERSION_MADE_BY_1];
|
FileReading::read_16_le( *m_pZip, sError ); /* skip version needed to extract */
|
||||||
const int version_needed_to_extract = byterec[C_VERSION_NEEDED_TO_EXTRACT_0];
|
int iGeneralPurpose = FileReading::read_16_le( *m_pZip, sError );
|
||||||
if( version_needed_to_extract > 20 ) /* compatible with PKUNZIP 2.0 */
|
info.m_iCompressionMethod = (ZipCompressionMethod) FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip last mod file time */
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip last mod file date */
|
||||||
|
info.m_iCRC32 = FileReading::read_32_le( *m_pZip, sError );
|
||||||
|
info.m_iCompressedSize = FileReading::read_32_le( *m_pZip, sError );
|
||||||
|
info.m_iUncompressedSize = FileReading::read_32_le( *m_pZip, sError );
|
||||||
|
int iFilenameLength = FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
int iExtraFieldLength = FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
int iFileCommentLength = FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* relative offset of local header */
|
||||||
|
FileReading::read_16_le( *m_pZip, sError ); /* skip internal file attributes */
|
||||||
|
int iExternalFileAttributes = FileReading::read_32_le( *m_pZip, sError );
|
||||||
|
info.m_iOffset = FileReading::read_32_le( *m_pZip, sError );
|
||||||
|
|
||||||
|
/* Check for errors before reading variable-length fields. */
|
||||||
|
if( sError != "" )
|
||||||
{
|
{
|
||||||
LOG->Warn( "File \"%s\" in \"%s\" uses unsupported ZIP version %i.%i",
|
LOG->Warn( "%s: %s", m_sPath.c_str(), sError.c_str() );
|
||||||
info.fn.c_str(), m_sPath.c_str(),
|
return -1;
|
||||||
version_needed_to_extract / 10, version_needed_to_extract % 10 );
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int general_purpose_bit_flag = makeword(&byterec[C_GENERAL_PURPOSE_BIT_FLAG]);
|
info.m_sName = FileReading::ReadString( *m_pZip, iFilenameLength, sError );
|
||||||
if( general_purpose_bit_flag & 1 )
|
FileReading::SkipBytes( *m_pZip, iExtraFieldLength, sError ); /* skip extra field */
|
||||||
|
FileReading::SkipBytes( *m_pZip, iFileCommentLength, sError ); /* skip file comment */
|
||||||
|
|
||||||
|
if( sError != "" )
|
||||||
{
|
{
|
||||||
LOG->Warn( "Skipped encrypted \"%s\" in \"%s\"", info.fn.c_str(), m_sPath.c_str() );
|
LOG->Warn( "%s: %s", m_sPath.c_str(), sError.c_str() );
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.compression_method = makeword(&byterec[C_COMPRESSION_METHOD]);
|
/* Check usability last, so we always read past the whole entry and don't leave the
|
||||||
// int last_mod_dos_datetime = makelong(&byterec[C_LAST_MOD_DOS_DATETIME]);
|
* file pointer in the middle of a record. */
|
||||||
info.crc = makelong(&byterec[C_CRC32]);
|
if( iGeneralPurpose & 1 )
|
||||||
info.compr_size = makelong(&byterec[C_COMPRESSED_SIZE]);
|
{
|
||||||
info.uncompr_size = makelong(&byterec[C_UNCOMPRESSED_SIZE]);
|
LOG->Warn( "Skipped encrypted \"%s\" in \"%s\"", info.m_sName.c_str(), m_sPath.c_str() );
|
||||||
info.filename_length = makeword(&byterec[C_FILENAME_LENGTH]);
|
return 0;
|
||||||
info.extra_field_length = makeword(&byterec[C_EXTRA_FIELD_LENGTH]);
|
}
|
||||||
// int file_comment_length = makeword(&byterec[C_FILE_COMMENT_LENGTH]);
|
|
||||||
info.diskstart = makeword(&byterec[C_DISK_NUMBER_START]);
|
|
||||||
// int internal_file_attributes = makeword(&byterec[C_INTERNAL_FILE_ATTRIBUTES]);
|
|
||||||
int external_file_attributes = makelong(&byterec[C_EXTERNAL_FILE_ATTRIBUTES]); /* LONG, not word! */
|
|
||||||
info.offset = makelong(&byterec[C_RELATIVE_OFFSET_LOCAL_HEADER]);
|
|
||||||
|
|
||||||
/* Skip directories. */
|
/* Skip directories. */
|
||||||
if( external_file_attributes & 0x08 )
|
if( iExternalFileAttributes & (1<<4) )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
got = m_pZip->Read( info.fn, info.filename_length );
|
if( info.m_iCompressionMethod != STORED && info.m_iCompressionMethod != DEFLATED )
|
||||||
if( got == -1 )
|
|
||||||
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", 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",
|
LOG->Warn( "File \"%s\" in \"%s\" uses unsupported compression method %i",
|
||||||
info.fn.c_str(), m_sPath.c_str(), info.compression_method );
|
info.m_sName.c_str(), m_sPath.c_str(), info.m_iCompressionMethod );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -330,84 +221,98 @@ int RageFileDriverZip::ProcessCdirFileHdr( FileInfo &info )
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RageFileDriverZip::ReadLocalFileHeader( FileInfo &info )
|
||||||
|
{
|
||||||
|
/* Seek to and read the local file header. */
|
||||||
|
m_pZip->Seek( info.m_iOffset );
|
||||||
|
|
||||||
|
CString sError;
|
||||||
|
CString sSig = FileReading::ReadString( *m_pZip, 4, sError );
|
||||||
|
|
||||||
|
if( sError != "" )
|
||||||
|
{
|
||||||
|
LOG->Warn( "%s: error opening \"%s\": %s", m_sPath.c_str(), info.m_sName.c_str(), sError.c_str() );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sSig != "\x50\x4B\x03\x04" )
|
||||||
|
{
|
||||||
|
LOG->Warn( "%s: local file header not found for \"%s\"", m_sPath.c_str(), info.m_sName.c_str() );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileReading::SkipBytes( *m_pZip, 22, sError ); /* skip most of the local file header */
|
||||||
|
|
||||||
|
const int iFilenameLength = FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
const int iExtraFieldLength = FileReading::read_16_le( *m_pZip, sError );
|
||||||
|
info.m_iDataOffset = m_pZip->Tell() + iFilenameLength + iExtraFieldLength;
|
||||||
|
|
||||||
|
if( sError != "" )
|
||||||
|
{
|
||||||
|
LOG->Warn( "%s: %s", m_sPath.c_str(), sError.c_str() );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
RageFileDriverZip::~RageFileDriverZip()
|
RageFileDriverZip::~RageFileDriverZip()
|
||||||
{
|
{
|
||||||
for( unsigned i = 0; i < Files.size(); ++i )
|
for( unsigned i = 0; i < m_pFiles.size(); ++i )
|
||||||
delete Files[i];
|
delete m_pFiles[i];
|
||||||
|
|
||||||
if( m_bFileOwned )
|
if( m_bFileOwned )
|
||||||
delete m_pZip;
|
delete m_pZip;
|
||||||
}
|
}
|
||||||
|
|
||||||
RageFileBasic *RageFileDriverZip::Open( const CString &path, int mode, int &err )
|
RageFileBasic *RageFileDriverZip::Open( const CString &sPath, int iMode, int &iErr )
|
||||||
{
|
{
|
||||||
if( mode == RageFile::WRITE )
|
if( iMode & RageFile::WRITE )
|
||||||
{
|
{
|
||||||
err = ERROR_WRITING_NOT_SUPPORTED;
|
iErr = ERROR_WRITING_NOT_SUPPORTED;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileInfo *info = (FileInfo *) FDB->GetFilePriv( path );
|
FileInfo *info = (FileInfo *) FDB->GetFilePriv( sPath );
|
||||||
if( info == NULL )
|
if( info == NULL )
|
||||||
{
|
{
|
||||||
err = ENOENT;
|
iErr = ENOENT;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Mutex.Lock();
|
m_Mutex.Lock();
|
||||||
|
|
||||||
/* If we havn't figured out the offset to the real data yet, do so now. */
|
/* If we havn't figured out the offset to the real data yet, do so now. */
|
||||||
if( info->data_offset == -1 )
|
if( info->m_iDataOffset == -1 )
|
||||||
{
|
{
|
||||||
m_pZip->Seek( info->offset );
|
if( !ReadLocalFileHeader(*info) )
|
||||||
|
{
|
||||||
/* Should be in proper position now, so check for sig. */
|
m_Mutex.Unlock();
|
||||||
CString sig;
|
return NULL;
|
||||||
int got = m_pZip->Read( sig, 4 );
|
}
|
||||||
if( got == -1 )
|
|
||||||
RageException::Throw( "Read error in %s: %s", m_sPath.c_str(), m_pZip->GetError().c_str() );
|
|
||||||
if( got != 4 )
|
|
||||||
RageException::Throw( "%s: unexpected EOF", m_sPath.c_str() );
|
|
||||||
|
|
||||||
if( sig != local_hdr_sig )
|
|
||||||
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 = m_pZip->Read( (char *)byterec, LREC_SIZE );
|
|
||||||
if( got == -1 )
|
|
||||||
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", 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 = m_pZip->Tell() + filename_length + extra_field_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We won't do any further access to zip, except to copy it (which is
|
/* We won't do any further access to zip, except to copy it (which is
|
||||||
* threadsafe), so we can unlock now. */
|
* threadsafe), so we can unlock now. */
|
||||||
m_Mutex.Unlock();
|
m_Mutex.Unlock();
|
||||||
|
|
||||||
RageFileDriverSlice *pSlice = new RageFileDriverSlice( m_pZip->Copy(), info->data_offset, info->compr_size );
|
RageFileDriverSlice *pSlice = new RageFileDriverSlice( m_pZip->Copy(), info->m_iDataOffset, info->m_iCompressedSize );
|
||||||
pSlice->DeleteFileWhenFinished();
|
pSlice->DeleteFileWhenFinished();
|
||||||
|
|
||||||
switch( info->compression_method )
|
switch( info->m_iCompressionMethod )
|
||||||
{
|
{
|
||||||
case STORED:
|
case STORED:
|
||||||
return pSlice;
|
return pSlice;
|
||||||
case DEFLATED:
|
case DEFLATED:
|
||||||
{
|
{
|
||||||
RageFileObjInflate *pInflate = new RageFileObjInflate( pSlice, info->uncompr_size );
|
RageFileObjInflate *pInflate = new RageFileObjInflate( pSlice, info->m_iUncompressedSize );
|
||||||
pInflate->DeleteFileWhenFinished();
|
pInflate->DeleteFileWhenFinished();
|
||||||
return pInflate;
|
return pInflate;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
/* unknown compression method */
|
/* unknown compression method */
|
||||||
ASSERT( 0 );
|
ASSERT( 0 );
|
||||||
err = EINVAL;
|
iErr = EINVAL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,54 +324,26 @@ void RageFileDriverZip::FlushDirCache( const CString &sPath )
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
|
* Copyright (c) 2003-2005 Glenn Maynard. All rights reserved.
|
||||||
* Copyright (c) 2003-2004 Glenn Maynard. All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* For the purposes of this copyright and license, "Info-ZIP" is defined as
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* the following set of individuals:
|
* 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.
|
||||||
*
|
*
|
||||||
* Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
* Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
* Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum,
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||||
* Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller,
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||||
* Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel,
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||||
* Steve Salisbury, Dave Smith, Christian Spieler, Antoine Verheijen,
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
* Paul von Behren, Rich Wales, Mike White
|
* 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
|
||||||
* This software is provided "as is," without warranty of any kind, express
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
* or implied. In no event shall Info-ZIP or its contributors be held liable
|
|
||||||
* for any direct, indirect, incidental, special or consequential damages
|
|
||||||
* arising out of the use of or inability to use this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* definition, disclaimer, and this list of conditions.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form (compiled executables) must reproduce
|
|
||||||
* the above copyright notice, definition, disclaimer, and this list of
|
|
||||||
* conditions in documentation and/or other materials provided with the
|
|
||||||
* distribution. The sole exception to this condition is redistribution
|
|
||||||
* of a standard UnZipSFX binary as part of a self-extracting archive;
|
|
||||||
* that is permitted without inclusion of this license, as long as the
|
|
||||||
* normal UnZipSFX banner has not been removed from the binary or disabled.
|
|
||||||
*
|
|
||||||
* 3. Altered versions--including, but not limited to, ports to new operating
|
|
||||||
* systems, existing ports with new graphical interfaces, and dynamic,
|
|
||||||
* shared, or static library versions--must be plainly marked as such
|
|
||||||
* and must not be misrepresented as being the original source. Such
|
|
||||||
* altered versions also must not be misrepresented as being Info-ZIP
|
|
||||||
* releases--including, but not limited to, labeling of the altered
|
|
||||||
* versions with the names "Info-ZIP" (or any variation thereof, including,
|
|
||||||
* but not limited to, different capitalizations), "Pocket UnZip," "WiZ"
|
|
||||||
* or "MacZip" without the explicit permission of Info-ZIP. Such altered
|
|
||||||
* versions are further prohibited from misrepresentative use of the
|
|
||||||
* Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s).
|
|
||||||
*
|
|
||||||
* 4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip,"
|
|
||||||
* "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its
|
|
||||||
* own source and binary releases.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ struct end_central_dir_record;
|
|||||||
class RageFileDriverZip: public RageFileDriver
|
class RageFileDriverZip: public RageFileDriver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RageFileDriverZip( CString path );
|
RageFileDriverZip( CString sPath );
|
||||||
RageFileDriverZip( RageFileBasic *pFile );
|
RageFileDriverZip( RageFileBasic *pFile );
|
||||||
virtual ~RageFileDriverZip();
|
virtual ~RageFileDriverZip();
|
||||||
|
|
||||||
RageFileBasic *Open( const CString &path, int mode, int &err );
|
RageFileBasic *Open( const CString &sPath, int iMode, int &iErr );
|
||||||
void FlushDirCache( const CString &sPath );
|
void FlushDirCache( const CString &sPath );
|
||||||
|
|
||||||
void DeleteFileWhenFinished() { m_bFileOwned = true; }
|
void DeleteFileWhenFinished() { m_bFileOwned = true; }
|
||||||
@@ -24,7 +24,7 @@ private:
|
|||||||
bool m_bFileOwned;
|
bool m_bFileOwned;
|
||||||
|
|
||||||
RageFileBasic *m_pZip;
|
RageFileBasic *m_pZip;
|
||||||
vector<FileInfo *> Files;
|
vector<FileInfo *> m_pFiles;
|
||||||
|
|
||||||
CString m_sPath;
|
CString m_sPath;
|
||||||
|
|
||||||
@@ -33,61 +33,35 @@ private:
|
|||||||
RageMutex m_Mutex;
|
RageMutex m_Mutex;
|
||||||
|
|
||||||
void ParseZipfile();
|
void ParseZipfile();
|
||||||
void ReadEndCentralRecord( end_central_dir_record &ec );
|
bool ReadEndCentralRecord( int &total_entries_central_dir, int &offset_start_central_directory );
|
||||||
int ProcessCdirFileHdr( FileInfo &info );
|
int ProcessCdirFileHdr( FileInfo &info );
|
||||||
|
bool SeekToEndCentralRecord();
|
||||||
|
bool ReadLocalFileHeader( FileInfo &info );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
|
* Copyright (c) 2003-2005 Glenn Maynard. All rights reserved.
|
||||||
* Copyright (c) 2003-2004 Glenn Maynard. All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* For the purposes of this copyright and license, "Info-ZIP" is defined as
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* the following set of individuals:
|
* 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.
|
||||||
*
|
*
|
||||||
* Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
* Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
* Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum,
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||||
* Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller,
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||||
* Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel,
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||||
* Steve Salisbury, Dave Smith, Christian Spieler, Antoine Verheijen,
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
* Paul von Behren, Rich Wales, Mike White
|
* 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
|
||||||
* This software is provided "as is," without warranty of any kind, express
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
* or implied. In no event shall Info-ZIP or its contributors be held liable
|
|
||||||
* for any direct, indirect, incidental, special or consequential damages
|
|
||||||
* arising out of the use of or inability to use this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* definition, disclaimer, and this list of conditions.
|
|
||||||
*
|
|
||||||
* 2. Redistributions in binary form (compiled executables) must reproduce
|
|
||||||
* the above copyright notice, definition, disclaimer, and this list of
|
|
||||||
* conditions in documentation and/or other materials provided with the
|
|
||||||
* distribution. The sole exception to this condition is redistribution
|
|
||||||
* of a standard UnZipSFX binary as part of a self-extracting archive;
|
|
||||||
* that is permitted without inclusion of this license, as long as the
|
|
||||||
* normal UnZipSFX banner has not been removed from the binary or disabled.
|
|
||||||
*
|
|
||||||
* 3. Altered versions--including, but not limited to, ports to new operating
|
|
||||||
* systems, existing ports with new graphical interfaces, and dynamic,
|
|
||||||
* shared, or static library versions--must be plainly marked as such
|
|
||||||
* and must not be misrepresented as being the original source. Such
|
|
||||||
* altered versions also must not be misrepresented as being Info-ZIP
|
|
||||||
* releases--including, but not limited to, labeling of the altered
|
|
||||||
* versions with the names "Info-ZIP" (or any variation thereof, including,
|
|
||||||
* but not limited to, different capitalizations), "Pocket UnZip," "WiZ"
|
|
||||||
* or "MacZip" without the explicit permission of Info-ZIP. Such altered
|
|
||||||
* versions are further prohibited from misrepresentative use of the
|
|
||||||
* Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s).
|
|
||||||
*
|
|
||||||
* 4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip,"
|
|
||||||
* "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its
|
|
||||||
* own source and binary releases.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user