Files
itgmania212121/stepmania/src/RageFileDriverZip.cpp
T

685 lines
21 KiB
C++
Raw Normal View History

2003-12-10 07:08:22 +00:00
/*
Derived from Info-ZIP 5.50. Heavily stripped and rewritten. Only retains
STORE and DEFLATE decompression types; that's all that's in use these days.
Could probably readd SHRINK easily.
- Glenn
Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
For the purposes of this copyright and license, "Info-ZIP" is defined as
the following set of individuals:
Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois,
Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase,
Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum,
Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller,
Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel,
Steve Salisbury, Dave Smith, Christian Spieler, Antoine Verheijen,
Paul von Behren, Rich Wales, Mike White
This software is provided "as is," without warranty of any kind, express
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.
*/
2003-12-08 00:08:20 +00:00
#include "global.h"
#include "RageFileDriverZip.h"
2003-12-08 03:20:26 +00:00
#include "RageLog.h"
2003-12-08 00:08:20 +00:00
#include "RageUtil.h"
#include "RageUtil_FileDB.h"
2003-12-10 07:08:22 +00:00
2003-12-08 00:08:20 +00:00
#include <errno.h>
2004-01-17 05:21:24 +00:00
#if defined(_WINDOWS) || defined(_XBOX)
2003-12-10 07:32:40 +00:00
#include "zlib/zlib.h"
2003-12-10 07:08:22 +00:00
#pragma comment(lib, "zlib/zdll.lib")
2003-12-10 07:32:40 +00:00
#else
#include <zlib.h>
2003-12-08 00:08:20 +00:00
#endif
2003-12-10 07:08:22 +00:00
#define INBUFSIZE 1024*4
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
struct FileInfo
2003-12-08 03:20:26 +00:00
{
2003-12-10 07:08:22 +00:00
CString fn;
long offset;
long data_offset;
long extra_field_length;
long filename_length;
unsigned short compression_method;
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 */
};
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
struct end_central_dir_record
2003-12-08 03:20:26 +00:00
{
2003-12-10 07:08:22 +00:00
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;
};
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
#define STORED 0
#define DEFLATED 8
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
class RageFileObjZipDeflated: public RageFileObj
2003-12-08 03:20:26 +00:00
{
2003-12-10 07:08:22 +00:00
private:
const FileInfo &info;
RageFile zip;
int CFilePos, UFilePos;
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
z_stream dstrm;
char decomp_buf[INBUFSIZE], *decomp_buf_ptr;
int decomp_buf_avail;
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
public:
RageFileObjZipDeflated( const RageFile &f, const FileInfo &info, RageFile &p );
RageFileObjZipDeflated( const RageFileObjZipDeflated &cpy, RageFile &p );
~RageFileObjZipDeflated();
int Read(void *buffer, size_t bytes);
int Write(const void *buffer, size_t bytes) { SetError( "Not implemented" ); return -1; }
void Rewind();
int Seek( int offset );
int GetFileSize() { return info.uncompr_size; }
RageFileObj *Copy( RageFile &p ) const
{
2003-12-10 10:06:32 +00:00
RageException::Throw( "Loading ZIPs from deflated ZIPs is currently disabled; see RageFileObjZipDeflated" );
// return new RageFileObjZipDeflated( *this, p );
2003-12-10 07:08:22 +00:00
}
};
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
class RageFileObjZipStored: public RageFileObj
2003-12-08 00:08:20 +00:00
{
private:
2003-12-10 07:08:22 +00:00
const FileInfo &info;
RageFile zip;
int FilePos;
2003-12-08 00:08:20 +00:00
public:
2003-12-10 07:08:22 +00:00
RageFileObjZipStored( const RageFile &f, const FileInfo &info, RageFile &p );
int Read(void *buffer, size_t bytes);
int Write(const void *buffer, size_t bytes) { SetError( "Not implemented" ); return -1; }
void Rewind()
{
zip.Seek( info.data_offset );
FilePos = 0;
}
int Seek( int offset );
int GetFileSize() { return info.uncompr_size; }
RageFileObj *Copy( RageFile &p ) const
{
return new RageFileObjZipStored( zip, info, p );
}
2003-12-08 00:08:20 +00:00
};
class ZipFilenameDB: public FilenameDB
{
protected:
void PopulateFileSet( FileSet &fs, const CString &sPath ) { }
public:
ZipFilenameDB() { ExpireSeconds = -1; }
};
RageFileDriverZip::RageFileDriverZip( CString path ):
RageFileDriver( new ZipFilenameDB )
{
2003-12-10 07:08:22 +00:00
if( !zip.Open(path) )
RageException::Throw( "Couldn't open %s: %s", path.c_str(), zip.GetError().c_str() );
ParseZipfile();
}
#define ECREC_SIZE 18
static CString central_hdr_sig = "\x50\x4B\x01\x02";
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]);
}
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( RageFile &zip, end_central_dir_record &ec )
{
const int OrigPos = zip.Tell();
typedef unsigned char ec_byte_rec[ ECREC_SIZE+4 ];
#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;
const int got = zip.Read( byterec, ECREC_SIZE+4 );
if( got == -1 )
RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() );
if ( got != ECREC_SIZE+4 )
RageException::Throw( "%s: unexpected EOF", zip.GetPath().c_str() );
ec.number_this_disk = makeword(&byterec[NUMBER_THIS_DISK]);
ec.num_disk_start_cdir = makeword(&byterec[NUM_DISK_WITH_START_CENTRAL_DIR]);
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]);
ec.size_central_directory = makelong(&byterec[SIZE_CENTRAL_DIRECTORY]);
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;
if( expect_ecrec_offset > OrigPos )
RageException::Throw( "Couldn't open %s: missing %ld bytes in zipfile", zip.GetPath().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();
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. */
int real_ecrec_offset = -1;
char tmp[INBUFSIZE];
int tmp_used = 0;
while( real_ecrec_offset == -1 && realpos >= Size-searchlen )
2003-12-08 03:20:26 +00:00
{
2003-12-10 07:08:22 +00:00
realpos -= INBUFSIZE;
zip.Seek( max( 0, realpos ) );
int got = zip.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() );
if( got == 0 )
break; /* fall through and fail */
got += tmp_used;
/* 'P' must be at least (ECREC_SIZE+4) bytes from end of zipfile */
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: */
memcpy((char *)tmp+INBUFSIZE-3, (char *)tmp, 3);
tmp_used = 3;
}
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
if( real_ecrec_offset == -1 )
RageException::Throw( "Couldn't open %s: End-of-central-directory signature not found", zip.GetPath().c_str() );
zip.Seek( real_ecrec_offset );
/* Get the end-central data. */
end_central_dir_record ec;
ReadEndCentralRecord( zip, 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() );
/* Seek to where the start of central directory should be, and make
* sure it's there. */
zip.Seek( ec.offset_start_central_directory );
CString sig;
int got = zip.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() );
zip.Seek( ec.offset_start_central_directory );
bool endsig_found = false;
/* Loop through files in central directory. */
while(1)
{
CString sig;
if ( zip.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 )
{
/* yes, so look if we ARE back at the end_central record */
endsig_found = ( sig == end_central_sig );
} else {
LOG->Warn( "%s: expected central file header signature not found", zip.GetPath().c_str() );
}
break;
}
FileInfo info;
info.data_offset = -1;
int got = ProcessCdirFileHdr(zip, info);
if( got == -1 ) /* error */
break;
if( got == 0 ) /* skip */
continue;
zip.SeekCur( info.extra_field_length );
FileInfo *pInfo = new FileInfo(info);
Files.push_back( pInfo );
FDB->AddFile( pInfo->fn, pInfo->uncompr_size, pInfo->crc, pInfo );
2003-12-08 03:20:26 +00:00
}
2003-12-10 07:08:22 +00:00
}
2003-12-08 03:20:26 +00:00
2003-12-10 07:08:22 +00:00
#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( RageFile &zip, 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 );
if ( got == -1 )
RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() );
if ( got != CREC_SIZE )
{
LOG->Warn( "%s: unexpected EOF", zip.GetPath().c_str() );
return -1;
}
2003-12-08 00:08:20 +00:00
2003-12-10 07:08:22 +00:00
// crec.version_made_by[0] = byterec[C_VERSION_MADE_BY_0];
// crec.version_made_by[1] = byterec[C_VERSION_MADE_BY_1];
const int version_needed_to_extract = byterec[C_VERSION_NEEDED_TO_EXTRACT_0];
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(),
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]);
if( general_purpose_bit_flag & 1 )
{
LOG->Warn( "Skipped encrypted \"%s\" in \"%s\"",
info.fn.c_str(), zip.GetRealPath().c_str() );
return 0;
}
info.compression_method = makeword(&byterec[C_COMPRESSION_METHOD]);
// int last_mod_dos_datetime = makelong(&byterec[C_LAST_MOD_DOS_DATETIME]);
info.crc = makelong(&byterec[C_CRC32]);
info.compr_size = makelong(&byterec[C_COMPRESSED_SIZE]);
info.uncompr_size = makelong(&byterec[C_UNCOMPRESSED_SIZE]);
info.filename_length = makeword(&byterec[C_FILENAME_LENGTH]);
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. */
if( external_file_attributes & 0x08 )
return 0;
got = zip.Read( info.fn, info.filename_length );
if( got == -1 )
RageException::Throw( "Couldn't open %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() );
if( got != info.filename_length )
{
2003-12-10 07:32:40 +00:00
LOG->Warn( "%s: bad filename length %li", zip.GetPath().c_str(), info.filename_length );
2003-12-10 07:08:22 +00:00
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 );
2003-12-08 00:08:20 +00:00
2003-12-10 07:08:22 +00:00
return 0;
}
return 1;
2003-12-08 00:08:20 +00:00
}
RageFileDriverZip::~RageFileDriverZip()
{
2003-12-10 07:08:22 +00:00
for( unsigned i = 0; i < Files.size(); ++i )
delete Files[i];
2003-12-08 00:08:20 +00:00
}
RageFileObj *RageFileDriverZip::Open( const CString &path, int mode, RageFile &p, int &err )
2003-12-08 00:08:20 +00:00
{
if( mode == RageFile::WRITE )
{
err = EINVAL;
return NULL;
}
2003-12-10 07:08:22 +00:00
File *f = FDB->GetFile( path );
if( f == NULL )
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
err = ENOENT;
2003-12-08 00:08:20 +00:00
return NULL;
}
2003-12-10 07:08:22 +00:00
/* If we havn't figured out the offset to the real data yet, do so now. */
FileInfo *info = (FileInfo *) f->priv;
if( info->data_offset == -1 )
{
zip.Seek( info->offset );
/* Should be in proper position now, so check for sig. */
CString sig;
int got = zip.Read( sig, 4 );
if( got == -1 )
RageException::Throw( "Read error in %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() );
if( got != 4 )
RageException::Throw( "%s: unexpected EOF", zip.GetPath().c_str() );
if( sig != local_hdr_sig )
RageException::Throw( "%s: bad zipfile offset", zip.GetPath().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 );
if( got == -1 )
RageException::Throw( "Read error in %s: %s", zip.GetPath().c_str(), zip.GetError().c_str() );
if( got != LREC_SIZE )
RageException::Throw( "%s: unexpected EOF", zip.GetPath().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;
}
2003-12-08 00:08:20 +00:00
2003-12-10 07:08:22 +00:00
zip.Seek( info->data_offset );
switch( info->compression_method )
{
case STORED:
return new RageFileObjZipStored( zip, *info, p );
case DEFLATED:
return new RageFileObjZipDeflated( zip, *info, p );
default:
/* unknown compression method */
ASSERT( 0 );
err = EINVAL;
return NULL;
}
2003-12-08 00:08:20 +00:00
}
/* NOP for now. This could check to see if the ZIP's mtime has changed, and reload. */
void RageFileDriverZip::FlushDirCache( const CString &sPath )
{
}
2003-12-10 07:08:22 +00:00
/* We make a copy of the RageFile: multiple files may read from the same ZIP at once;
* this way, we don't have to keep seeking around. */
RageFileObjZipDeflated::RageFileObjZipDeflated( const RageFile &f, const FileInfo &info_, RageFile &p ):
RageFileObj( p ),
2003-12-10 07:32:40 +00:00
info(info_),
zip( f )
2003-12-08 00:08:20 +00:00
{
2003-12-10 11:01:44 +00:00
decomp_buf_avail = 0;
2003-12-10 07:08:22 +00:00
dstrm.zalloc = Z_NULL;
dstrm.zfree = Z_NULL;
int err = inflateInit2( &dstrm, -MAX_WBITS );
if( err == Z_MEM_ERROR )
RageException::Throw( "inflateInit2( %i ): out of memory", -MAX_WBITS );
if( err != Z_OK )
LOG->Trace( "Huh? inflateInit2() err = %i", err );
decomp_buf_ptr = decomp_buf;
CFilePos = UFilePos = 0;
2003-12-08 00:08:20 +00:00
}
2003-12-10 07:08:22 +00:00
RageFileObjZipDeflated::RageFileObjZipDeflated( const RageFileObjZipDeflated &cpy, RageFile &p ):
RageFileObj( p ),
2003-12-10 07:32:40 +00:00
info( cpy.info ),
zip( cpy.zip )
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
/* XXX completely untested */
/* Copy the entire decode state. */
2003-12-10 11:01:44 +00:00
/* inflateInit2 isn't widespread yet */
ASSERT( 0 );
/*
2003-12-10 07:08:22 +00:00
inflateCopy( &dstrm, const_cast<z_streamp>(&cpy.dstrm) );
decomp_buf_ptr = decomp_buf + (cpy.decomp_buf_ptr - cpy.decomp_buf);
decomp_buf_avail = cpy.decomp_buf_avail;
CFilePos = cpy.CFilePos;
UFilePos = cpy.UFilePos;
2003-12-10 11:01:44 +00:00
*/
2003-12-08 00:08:20 +00:00
}
2003-12-10 07:08:22 +00:00
RageFileObjZipDeflated::~RageFileObjZipDeflated()
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
int err = inflateReset( &dstrm );
if( err != Z_OK )
LOG->Trace( "Huh? inflateReset() err = %i", err );
}
2003-12-08 00:08:20 +00:00
2003-12-10 07:08:22 +00:00
int RageFileObjZipDeflated::Read( void *buf, size_t bytes )
{
bool done=false;
int ret = 0;
while( bytes && CFilePos < (int) info.compr_size && !done )
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
if ( !decomp_buf_avail )
{
decomp_buf_ptr = decomp_buf;
decomp_buf_avail = 0;
int got = zip.Read( decomp_buf, sizeof(decomp_buf) );
if( got == -1 )
{
SetError( zip.GetError() );
return -1;
}
if( got == 0 )
break;
decomp_buf_avail = got;
}
dstrm.next_in = (Bytef *) decomp_buf_ptr;
dstrm.avail_in = decomp_buf_avail;
dstrm.next_out = (Bytef *) buf;
dstrm.avail_out = bytes;
int err = inflate(&dstrm, Z_PARTIAL_FLUSH);
switch( err )
{
case Z_DATA_ERROR:
SetError( "Data error" );
return -1;
case Z_MEM_ERROR:
SetError( "out of memory" );
return -1;
case Z_STREAM_END:
done = true;
break;
case Z_OK:
break;
default:
LOG->Trace( "Huh? inflate err %i", err );
}
const int used = (char *)dstrm.next_in - decomp_buf_ptr;
CFilePos += used;
decomp_buf_ptr += used;
decomp_buf_avail -= used;
const int got = (char *)dstrm.next_out - (char *)buf;
UFilePos += got;
ret += got;
buf = (char *)buf + got;
bytes -= got;
2003-12-08 00:08:20 +00:00
}
return ret;
}
2003-12-10 07:08:22 +00:00
void RageFileObjZipDeflated::Rewind()
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
inflateReset( &dstrm );
decomp_buf_ptr = decomp_buf;
decomp_buf_avail = 0;
zip.Seek( info.data_offset );
CFilePos = 0;
UFilePos = 0;
2003-12-08 00:08:20 +00:00
}
2003-12-10 07:08:22 +00:00
int RageFileObjZipDeflated::Seek( int offset )
2003-12-08 00:08:20 +00:00
{
2004-01-11 22:01:49 +00:00
/* Optimization: if offset is the end of the file, it's a lseek(0,SEEK_END). Don't
* decode anything. */
if( offset == (int) info.uncompr_size )
2003-12-10 07:08:22 +00:00
{
2004-01-11 22:01:49 +00:00
UFilePos = info.uncompr_size;
CFilePos = info.compr_size;
zip.Seek( info.data_offset + info.compr_size );
decomp_buf_ptr = decomp_buf;
decomp_buf_avail = 0;
inflateReset( &dstrm );
return offset;
2003-12-10 07:08:22 +00:00
}
2004-01-11 22:01:49 +00:00
/* Can this be optimized? */
return RageFileObj::Seek( offset );
2003-12-08 00:08:20 +00:00
}
2003-12-10 07:08:22 +00:00
RageFileObjZipStored::RageFileObjZipStored( const RageFile &f, const FileInfo &info_, RageFile &p ):
RageFileObj( p ),
2003-12-10 07:32:40 +00:00
info(info_),
zip( f )
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
FilePos = 0;
2003-12-08 00:08:20 +00:00
}
2003-12-10 07:08:22 +00:00
int RageFileObjZipStored::Read( void *buf, size_t bytes )
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
const int bytes_left = info.compr_size-this->FilePos;
const int got = zip.Read( buf, min( (int) bytes, bytes_left ) );
if( got == -1 )
{
SetError( zip.GetError() );
return -1;
}
FilePos += got;
return got;
2003-12-08 00:08:20 +00:00
}
2003-12-10 07:08:22 +00:00
int RageFileObjZipStored::Seek( int offset )
2003-12-08 00:08:20 +00:00
{
2003-12-10 07:08:22 +00:00
ASSERT( offset >= 0 );
offset = min( (unsigned) offset, info.compr_size );
int ret = zip.Seek( info.data_offset + offset );
if( ret == -1 )
{
SetError( zip.GetError() );
return -1;
}
ret -= info.data_offset;
ASSERT( ret >= 0 );
FilePos = ret;
2003-12-08 00:08:20 +00:00
return ret;
}