//=============================================================================
//
// File : kvi_packagefile.cpp
// Created on Tue 26 Dec 2006 05:33:33 by Szymon Stefanek
//
// This file is part of the KVIrc IRC Client distribution
// Copyright (C) 2006 Szymon Stefanek <pragma at kvirc dot net>
//
// This program is FREE software. You can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your opinion) any later version.
//
// This program is distributed in the HOPE that it will be USEFUL,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, write to the Free Software Foundation,
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================
# define __KVILIB__
# include "kvi_packagefile.h"
# include "kvi_file.h"
# include "kvi_fileutils.h"
# include "kvi_locale.h"
# include "kvi_inttypes.h"
# include <tqprogressdialog.h>
# include <tqlabel.h>
# include <tqdir.h>
# ifdef COMPILE_ZLIB_SUPPORT
# include <zlib.h>
# endif
//
// A KVIrc Package File is basically a simple zip file with some additional meta-data.
// The package file has the following format
//
// Field Type Bytes Description
//-------------------------------------------------------------------------------
// Package:
// PackageHeader
// PackageInfo
// PackageData
// PackageHeader:
// Magic Bytes 4 'KVPF': Signature for the Kvirc Package File
// Version uint32 4 0x00000001: Version of this package file
// Flags uint32 4 0x00000000: Flags, in version 1 is reserved and must be zero
//
// PackageInfo:
// InfoFieldCount uint32 4 Number of package info fields
// InfoField InfoField Variable A list of informational name-value pairs
// InfoField InfoField Variable A list of informational name-value pairs
// InfoField InfoField Variable A list of informational name-value pairs
// .... .... ....
// PackageData:
// DataField DataField Variable A list of data fields with format defined below
// DataField DataField Variable A list of data fields with format defined below
// DataField DataField Variable A list of data fields with format defined below
// .... .... ....
// InfoField:
// Name UniString Variable The "name" element of the info field
// ValueType uint32 4 The type of the following ValueData field
// ValueData ValueData Variable
// ValueData for ValueType 1 (string field)
// Value UniString Variable The value element of type string of the the info field
// ValueData for ValueType 2 (binary buffer field)
// BufferLen uint32 4 The length of the binary buffer
// BufferData Bytes Variable The data for the binary buffer
// UniString:
// StringLen uint32 4 The length of the string data in BYTES (null terminator NOT included)
// StringData Bytes StringLen An utf8 encoded string (do NOT write the NULL terminator)
// Bytes:
// Byte uint8 1 A byte
// Byte uint8 1 A byte
// .... .... ....
// DataField:
// FieldType uint32 4 The type of the field, see below for defined values
// FieldLen uint32 4 FieldData length in bytes (useful for skipping a field if unsupported)
// FieldData Variable FieldLen The data of the field, see below for defined values
// FieldData for FieldType 1 (file field)
// Flags uint32 4 Bitmask. Bits: 1=FileIsDeflated
// Path UniString Variable A relative path expressed as utf8 string. \ AND / are considered to be separators
// Size uint32 4 Size of the following file data
// FilePayload Bytes Variable
// Everything is stored in LITTLE ENDIAN byte order.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Da Base Engine
KviPackageIOEngine : : KviPackageIOEngine ( )
{
m_pProgressDialog = 0 ;
m_pStringInfoFields = new KviPointerHashTable < TQString , TQString > ( ) ;
m_pStringInfoFields - > setAutoDelete ( true ) ;
m_pBinaryInfoFields = new KviPointerHashTable < TQString , TQByteArray > ( ) ;
m_pBinaryInfoFields - > setAutoDelete ( true ) ;
}
KviPackageIOEngine : : ~ KviPackageIOEngine ( )
{
if ( m_pProgressDialog ) delete m_pProgressDialog ;
delete m_pStringInfoFields ;
delete m_pBinaryInfoFields ;
}
bool KviPackageIOEngine : : updateProgress ( int iProgress , const TQString & szLabel )
{
if ( ! m_pProgressDialog ) return true ;
# ifdef COMPILE_USE_QT4
m_pProgressDialog - > setValue ( iProgress ) ;
# else
m_pProgressDialog - > setProgress ( iProgress ) ;
# endif
m_pProgressDialogLabel - > setText ( szLabel ) ;
tqApp - > processEvents ( ) ;
if ( m_pProgressDialog - > wasCanceled ( ) )
{
setLastError ( __tr2qs ( " Operation cancelled " ) ) ;
return false ;
}
return true ;
}
void KviPackageIOEngine : : showProgressDialog ( const TQString & szCaption , int iTotalSteps )
{
# ifdef COMPILE_USE_QT4
m_pProgressDialog = new TQProgressDialog ( TQString ( " " ) , __tr2qs ( " Cancel " ) , 0 , iTotalSteps , 0 ) ;
m_pProgressDialog - > setModal ( true ) ;
m_pProgressDialog - > setWindowTitle ( szCaption ) ;
# else
m_pProgressDialog = new TQProgressDialog ( TQString ( " " ) , __tr2qs ( " Cancel " ) , iTotalSteps , 0 , " " , true ) ;
m_pProgressDialog - > setCaption ( szCaption ) ;
# endif
m_pProgressDialogLabel = new TQLabel ( m_pProgressDialog ) ;
m_pProgressDialogLabel - > setMaximumSize ( 500 , 300 ) ;
m_pProgressDialog - > setLabel ( m_pProgressDialogLabel ) ;
}
void KviPackageIOEngine : : hideProgressDialog ( )
{
if ( ! m_pProgressDialog ) return ;
delete m_pProgressDialog ;
m_pProgressDialog = 0 ;
}
bool KviPackageIOEngine : : writeError ( )
{
setLastError ( __tr2qs ( " File write error " ) ) ;
return false ;
}
bool KviPackageIOEngine : : readError ( )
{
setLastError ( __tr2qs ( " File read error " ) ) ;
return false ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Da Writer
KviPackageWriter : : KviPackageWriter ( )
: KviPackageIOEngine ( )
{
m_pDataFields = new KviPointerList < DataField > ( ) ;
m_pDataFields - > setAutoDelete ( true ) ;
}
KviPackageWriter : : ~ KviPackageWriter ( )
{
delete m_pDataFields ;
}
void KviPackageWriter : : addInfoField ( const TQString & szName , const TQString & szValue )
{
m_pStringInfoFields - > replace ( szName , new TQString ( szValue ) ) ;
}
void KviPackageWriter : : addInfoField ( const TQString & szName , TQByteArray * pValue )
{
m_pBinaryInfoFields - > replace ( szName , pValue ) ;
}
bool KviPackageWriter : : addFile ( const TQString & szLocalFileName , const TQString & szTargetFileName , kvi_u32_t uAddFileFlags )
{
TQFileInfo fi ( szLocalFileName ) ;
return addFileInternal ( & fi , szLocalFileName , szTargetFileName , uAddFileFlags ) ;
}
bool KviPackageWriter : : addFileInternal ( const TQFileInfo * fi , const TQString & szLocalFileName , const TQString & szTargetFileName , kvi_u32_t uAddFileFlags )
{
if ( ! ( fi - > isFile ( ) & & fi - > isReadable ( ) ) )
return false ;
if ( ! ( uAddFileFlags & FollowSymLinks ) )
{
if ( fi - > isSymLink ( ) )
return true ; // do NOT add a symlink
}
DataField * f = new DataField ( ) ;
f - > m_uType = KVI_PACKAGE_DATAFIELD_TYPE_FILE ;
f - > m_bFileAllowCompression = ! ( uAddFileFlags & NoCompression ) ;
f - > m_szFileLocalName = szLocalFileName ;
f - > m_szFileTargetName = szTargetFileName ;
m_pDataFields - > append ( f ) ;
return true ;
}
bool KviPackageWriter : : addDirectory ( const TQString & szLocalDirectoryName , const TQString & szTargetDirectoryPrefix , kvi_u32_t uAddFileFlags )
{
TQDir d ( szLocalDirectoryName ) ;
# ifdef COMPILE_USE_QT4
TQDir : : Filters iFlags ;
# else
int iFlags ;
# endif
iFlags = TQDir : : Files | TQDir : : Readable ;
if ( ! ( uAddFileFlags & FollowSymLinks ) )
iFlags | = TQDir : : NoSymLinks ;
// QT4SUX: Because the TQDir::entryInfoList() breaks really a lot of code by returning an object that behaves in a _totally_ different way.. it's also much slower
# ifdef COMPILE_USE_QT4
int j ;
TQFileInfoList sl = d . entryInfoList ( iFlags ) ;
for ( j = 0 ; j < sl . size ( ) ; j + + )
{
# else
const TQFileInfoList * sl = d . entryInfoList ( iFlags ) ;
if ( ! sl ) return false ;
TQFileInfoListIterator it ( * sl ) ;
while ( TQFileInfo * fi = it . current ( ) )
{
# endif
TQString szSFileName = szLocalDirectoryName ;
KviTQString : : ensureLastCharIs ( szSFileName , TQChar ( KVI_PATH_SEPARATOR_CHAR ) ) ;
# ifdef COMPILE_USE_QT4
TQFileInfo slowCopy = sl . at ( j ) ;
szSFileName + = slowCopy . fileName ( ) ;
# else
szSFileName + = fi - > fileName ( ) ;
# endif
TQString szDFileName = szTargetDirectoryPrefix ;
KviTQString : : ensureLastCharIs ( szDFileName , TQChar ( KVI_PATH_SEPARATOR_CHAR ) ) ;
# ifdef COMPILE_USE_QT4
szDFileName + = slowCopy . fileName ( ) ;
if ( ! addFileInternal ( & slowCopy , szSFileName , szDFileName , uAddFileFlags ) )
return false ;
# else
szDFileName + = fi - > fileName ( ) ;
if ( ! addFileInternal ( fi , szSFileName , szDFileName , uAddFileFlags ) )
return false ;
# endif
# ifndef COMPILE_USE_QT4
+ + it ;
# endif
}
iFlags = TQDir : : Dirs | TQDir : : Readable ;
if ( ! ( uAddFileFlags & FollowSymLinks ) )
iFlags | = TQDir : : NoSymLinks ;
sl = d . entryInfoList ( iFlags ) ;
# ifdef COMPILE_USE_QT4
for ( j = 0 ; j < sl . size ( ) ; j + + )
{
TQString szDir = sl . at ( j ) . fileName ( ) ;
# else
if ( ! sl ) return false ;
TQFileInfoListIterator it2 ( * sl ) ;
while ( TQFileInfo * fi2 = it2 . current ( ) )
{
TQString szDir = fi2 - > fileName ( ) ;
# endif
if ( ! KviTQString : : equalCS ( szDir , " .. " ) & & ! KviTQString : : equalCS ( szDir , " . " ) )
{
TQString szSDirName = szLocalDirectoryName ;
KviTQString : : ensureLastCharIs ( szSDirName , TQChar ( KVI_PATH_SEPARATOR_CHAR ) ) ;
szSDirName + = szDir ;
TQString szDDirName = szTargetDirectoryPrefix ;
KviTQString : : ensureLastCharIs ( szDDirName , TQChar ( KVI_PATH_SEPARATOR_CHAR ) ) ;
szDDirName + = szDir ;
if ( ! addDirectory ( szSDirName , szDDirName , uAddFileFlags ) )
return false ;
}
# ifndef COMPILE_USE_QT4
+ + it2 ;
# endif
}
return true ;
}
# define BUFFER_SIZE 32768
bool KviPackageWriter : : packFile ( KviFile * pFile , DataField * pDataField )
{
TQString szProgressText ;
KviTQString : : sprintf ( szProgressText , __tr2qs ( " Packaging file %Q " ) , & ( pDataField - > m_szFileLocalName ) ) ;
if ( ! updateProgress ( m_iCurrentProgress , szProgressText ) )
return false ; // aborted
KviFile source ( pDataField - > m_szFileLocalName ) ;
if ( ! source . openForReading ( ) )
{
setLastError ( __tr2qs ( " Failed to open a source file for reading " ) ) ;
return false ;
}
kvi_u32_t uSize = source . size ( ) ;
// Flags
# ifdef COMPILE_ZLIB_SUPPORT
kvi_u32_t uFlags = pDataField - > m_bFileAllowCompression ?
( uSize > 64 ? KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE : 0 )
: 0 ;
# else
kvi_u32_t uFlags = 0 ;
# endif
if ( ! pFile - > save ( uFlags ) ) return writeError ( ) ;
KviTQCString szTargetFileName = KviTQString : : toUtf8 ( pDataField - > m_szFileTargetName ) ;
// Path
if ( ! pFile - > save ( szTargetFileName ) ) return writeError ( ) ;
kvi_file_offset_t savedSizeOffset = pFile - > pos ( ) ;
// Size : will update it if compression is requested
if ( ! pFile - > save ( uSize ) ) return writeError ( ) ;
pDataField - > m_uWrittenFieldLength = 4 + 4 + 4 + szTargetFileName . length ( ) ; // sizeof(flags + uncompressed size + path len + path)
// FilePayload
# ifdef COMPILE_ZLIB_SUPPORT
if ( uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE )
{
unsigned char ibuffer [ BUFFER_SIZE ] ;
unsigned char obuffer [ BUFFER_SIZE ] ;
kvi_i32_t iReaded = source . readBlock ( ( char * ) ibuffer , BUFFER_SIZE ) ;
if ( iReaded < 0 )
return readError ( ) ;
z_stream zstr ;
zstr . zalloc = Z_NULL ;
zstr . zfree = Z_NULL ;
zstr . opaque = Z_NULL ;
zstr . next_in = ibuffer ;
zstr . avail_in = iReaded ;
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
if ( deflateInit ( & zstr , 9 ) ! = Z_OK )
{
setLastError ( __tr2qs ( " Compression library initialization error " ) ) ;
return false ;
}
while ( iReaded > 0 )
{
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
if ( deflate ( & zstr , Z_NO_FLUSH ) ! = Z_OK )
{
setLastError ( __tr2qs ( " Compression library error " ) ) ;
return false ;
}
if ( zstr . avail_out < BUFFER_SIZE )
{
int iCompressed = zstr . next_out - obuffer ;
pDataField - > m_uWrittenFieldLength + = iCompressed ;
if ( pFile - > writeBlock ( ( char * ) obuffer , iCompressed ) ! = iCompressed )
{
deflateEnd ( & zstr ) ;
return writeError ( ) ;
}
}
if ( zstr . avail_in < BUFFER_SIZE )
{
int iDataToRead = BUFFER_SIZE - zstr . avail_in ;
if ( iDataToRead < BUFFER_SIZE )
{
if ( ibuffer ! = zstr . next_in )
{
// hum, there is still some data in the buffer to be readed
// and it is not at the beginning...move it to the beginning of ibuffer
memmove ( ibuffer , zstr . next_in , zstr . avail_in ) ;
}
}
iReaded = source . readBlock ( ( char * ) ( ibuffer + zstr . avail_in ) , iDataToRead ) ;
if ( iReaded < 0 )
{
deflateEnd ( & zstr ) ;
return readError ( ) ;
}
zstr . avail_in + = iReaded ;
zstr . next_in = ibuffer ;
if ( ( zstr . total_in % 2000000 ) = = 0 )
{
TQString szTmp ;
KviTQString : : sprintf ( szTmp , TQString ( " (%d of %d bytes) " ) , zstr . total_in , uSize ) ;
TQString szPrg = szProgressText + szTmp ;
if ( ! updateProgress ( m_iCurrentProgress , szPrg ) )
return false ; // aborted
}
}
}
// flush pending output
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
int ret ;
do
{
ret = deflate ( & zstr , Z_FINISH ) ;
if ( ( ret = = Z_OK ) | | ( ret = = Z_STREAM_END ) )
{
if ( zstr . avail_out < BUFFER_SIZE )
{
int iCompressed = zstr . next_out - obuffer ;
pDataField - > m_uWrittenFieldLength + = iCompressed ;
if ( pFile - > writeBlock ( ( char * ) obuffer , iCompressed ) ! = iCompressed )
{
deflateEnd ( & zstr ) ;
return writeError ( ) ;
}
} else {
deflateEnd ( & zstr ) ;
setLastError ( __tr2qs ( " Compression library internal error " ) ) ;
return false ;
}
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
}
} while ( ret = = Z_OK ) ;
// store the compressed data size
kvi_file_offset_t here = pFile - > pos ( ) ;
pFile - > seek ( savedSizeOffset ) ;
uSize = zstr . total_out ;
deflateEnd ( & zstr ) ;
if ( ! pFile - > save ( uSize ) ) return writeError ( ) ;
if ( ret ! = Z_STREAM_END )
{
setLastError ( __tr2qs ( " Error while compressing a file stream " ) ) ;
return false ;
}
pFile - > seek ( here ) ;
} else {
# endif
unsigned char buffer [ BUFFER_SIZE ] ;
int iTotalFileSize = 0 ;
kvi_i32_t iReaded = source . readBlock ( ( char * ) buffer , BUFFER_SIZE ) ;
if ( iReaded < 0 )
return readError ( ) ;
while ( iReaded > 0 )
{
iTotalFileSize + = iReaded ;
if ( ( iTotalFileSize % 1000000 ) = = 0 )
{
TQString szTmp ;
KviTQString : : sprintf ( szTmp , TQString ( " (%d of %d bytes) " ) , iTotalFileSize , uSize ) ;
TQString szPrg = szProgressText + szTmp ;
if ( ! updateProgress ( m_iCurrentProgress , szPrg ) )
return false ; // aborted
}
pDataField - > m_uWrittenFieldLength + = iReaded ;
if ( pFile - > writeBlock ( ( char * ) buffer , iReaded ) ! = iReaded )
return writeError ( ) ;
iReaded = source . readBlock ( ( char * ) buffer , BUFFER_SIZE ) ;
}
# ifdef COMPILE_ZLIB_SUPPORT
}
# endif
source . close ( ) ;
return true ;
}
bool KviPackageWriter : : pack ( const TQString & szFileName , kvi_u32_t uPackFlags )
{
m_iCurrentProgress = 0 ;
if ( ! ( uPackFlags & NoProgressDialog ) )
{
showProgressDialog ( __tr2qs ( " Creating package... " ) , 100 ) ;
updateProgress ( m_iCurrentProgress , __tr2qs ( " Writing package header " ) ) ;
}
bool bRet = packInternal ( szFileName , uPackFlags ) ;
hideProgressDialog ( ) ;
return bRet ;
}
bool KviPackageWriter : : packInternal ( const TQString & szFileName , kvi_u32_t uPackFlags )
{
KviFile f ( szFileName ) ;
if ( ! f . openForWriting ( ) )
{
setLastError ( __tr2qs ( " Can't open file for writing " ) ) ;
return false ;
}
// write the PackageHeader
// Magic
char magic [ 4 ] ;
magic [ 0 ] = ' K ' ;
magic [ 1 ] = ' V ' ;
magic [ 2 ] = ' P ' ;
magic [ 3 ] = ' F ' ;
if ( f . writeBlock ( magic , 4 ) ! = 4 ) return writeError ( ) ;
// Version
kvi_u32_t uVersion = 0x1 ;
if ( ! f . save ( uVersion ) ) return writeError ( ) ;
// Flags
kvi_u32_t uFlags = 0x0 ;
if ( ! f . save ( uFlags ) ) return writeError ( ) ;
// write PackageInfo
// InfoFieldCount
kvi_u32_t uCount = m_pStringInfoFields - > count ( ) + m_pBinaryInfoFields - > count ( ) ;
if ( ! f . save ( uCount ) ) return writeError ( ) ;
m_iCurrentProgress = 5 ;
if ( ! updateProgress ( m_iCurrentProgress , __tr2qs ( " Writing informational fields " ) ) )
return false ; // aborted
// InfoFields (string)
KviPointerHashTableIterator < TQString , TQString > it ( * m_pStringInfoFields ) ;
while ( TQString * s = it . current ( ) )
{
if ( ! f . save ( it . currentKey ( ) ) ) return writeError ( ) ;
kvi_u32_t uType = KVI_PACKAGE_INFOFIELD_TYPE_STRING ;
if ( ! f . save ( uType ) ) return writeError ( ) ;
if ( ! f . save ( * s ) ) return writeError ( ) ;
+ + it ;
}
// InfoFields (binary)
KviPointerHashTableIterator < TQString , TQByteArray > it2 ( * m_pBinaryInfoFields ) ;
while ( TQByteArray * b = it2 . current ( ) )
{
if ( ! f . save ( it2 . currentKey ( ) ) ) return writeError ( ) ;
kvi_u32_t uType = KVI_PACKAGE_INFOFIELD_TYPE_BINARYBUFFER ;
if ( ! f . save ( uType ) ) return writeError ( ) ;
if ( ! f . save ( * b ) ) return writeError ( ) ;
+ + it2 ;
}
m_iCurrentProgress = 10 ;
if ( ! updateProgress ( m_iCurrentProgress , __tr2qs ( " Writing package data " ) ) )
return false ; // aborted
// write PackageData
int iIdx = 0 ;
for ( DataField * pDataField = m_pDataFields - > first ( ) ; pDataField ; pDataField = m_pDataFields - > next ( ) )
{
kvi_u32_t uDataFieldType = pDataField - > m_uType ;
if ( ! f . save ( uDataFieldType ) ) return writeError ( ) ;
kvi_file_offset_t savedLenOffset = f . pos ( ) ;
// here we will store the length of the field once it's written
if ( ! f . save ( uDataFieldType ) ) return writeError ( ) ;
m_iCurrentProgress = 10 + ( ( 90 * iIdx ) / m_pDataFields - > count ( ) ) ;
switch ( pDataField - > m_uType )
{
case KVI_PACKAGE_DATAFIELD_TYPE_FILE :
if ( ! packFile ( & f , pDataField ) )
return false ;
break ;
default :
setLastError ( __tr2qs ( " Internal error " ) ) ;
return false ;
break ;
}
kvi_file_offset_t savedEndOffset = f . pos ( ) ;
f . seek ( savedLenOffset ) ;
if ( ! f . save ( pDataField - > m_uWrittenFieldLength ) )
return writeError ( ) ;
f . seek ( savedEndOffset ) ;
iIdx + + ;
}
return true ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Da Reader
KviPackageReader : : KviPackageReader ( )
: KviPackageIOEngine ( )
{
}
KviPackageReader : : ~ KviPackageReader ( )
{
}
bool KviPackageReader : : readHeaderInternal ( KviFile * pFile , const TQString & szLocalFileName )
{
// read the PackageHeader
// Magic
char magic [ 4 ] ;
if ( pFile - > readBlock ( magic , 4 ) ! = 4 ) return readError ( ) ;
if ( ( magic [ 0 ] ! = ' K ' ) | | ( magic [ 1 ] ! = ' V ' ) | | ( magic [ 2 ] ! = ' P ' ) | | ( magic [ 3 ] ! = ' F ' ) )
{
setLastError ( __tr2qs ( " The file specified is not a valid KVIrc package " ) ) ;
return false ;
}
// Version
kvi_u32_t uVersion ;
if ( ! pFile - > load ( uVersion ) ) return readError ( ) ;
if ( uVersion ! = 0x1 )
{
setLastError ( __tr2qs ( " The package has an invalid version number, it might have been created by a newer KVIrc " ) ) ;
return false ;
}
// Flags
kvi_u32_t uFlags ;
if ( ! pFile - > load ( uFlags ) ) return readError ( ) ;
// we ignore them at the moment
// read PackageInfo
// InfoFieldCount
kvi_u32_t uCount ;
if ( ! pFile - > load ( uCount ) ) return writeError ( ) ;
m_pStringInfoFields - > clear ( ) ;
m_pBinaryInfoFields - > clear ( ) ;
kvi_u32_t uIdx = 0 ;
while ( uIdx < uCount )
{
TQString szKey ;
if ( ! pFile - > load ( szKey ) ) return readError ( ) ;
kvi_u32_t uFieldType ;
if ( ! pFile - > load ( uFieldType ) ) return readError ( ) ;
switch ( uFieldType )
{
case KVI_PACKAGE_INFOFIELD_TYPE_STRING :
{
TQString szValue ;
if ( ! pFile - > load ( szValue ) ) return readError ( ) ;
m_pStringInfoFields - > replace ( szKey , new TQString ( szValue ) ) ;
}
break ;
case KVI_PACKAGE_INFOFIELD_TYPE_BINARYBUFFER :
{
TQByteArray * pbValue = new TQByteArray ( ) ;
if ( ! pFile - > load ( * pbValue ) )
{
delete pbValue ;
return readError ( ) ;
}
m_pBinaryInfoFields - > replace ( szKey , pbValue ) ;
}
break ;
default :
setLastError ( __tr2qs ( " Invalid info field: the package is probably corrupt " ) ) ;
break ;
}
uIdx + + ;
}
return true ;
}
bool KviPackageReader : : readHeader ( const TQString & szLocalFileName )
{
KviFile f ( szLocalFileName ) ;
if ( ! f . openForReading ( ) )
{
setLastError ( __tr2qs ( " Can't open file for reading " ) ) ;
return false ;
}
return readHeaderInternal ( & f , szLocalFileName ) ;
}
bool KviPackageReader : : unpackFile ( KviFile * pFile , const TQString & szUnpackPath )
{
// Flags
kvi_u32_t uFlags ;
if ( ! pFile - > load ( uFlags ) ) return readError ( ) ;
# ifndef COMPILE_ZLIB_SUPPORT
if ( uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE )
{
setLastError ( __tr2qs ( " The package contains compressed data but this executable does not support compression " ) ) ;
return false ;
}
# endif
// Path
TQString szPath ;
if ( ! pFile - > load ( szPath ) ) return readError ( ) ;
TQString szFileName = szUnpackPath ;
KviTQString : : ensureLastCharIs ( szFileName , TQChar ( KVI_PATH_SEPARATOR_CHAR ) ) ;
szFileName + = szPath ;
// no attacks please :)
szFileName . replace ( TQString ( " .. \\ " ) , TQString ( " " ) ) ;
szFileName . replace ( TQString ( " ..// " ) , TQString ( " " ) ) ;
KviFileUtils : : adjustFilePath ( szFileName ) ;
int idx = KviTQString : : findRev ( szFileName , TQChar ( KVI_PATH_SEPARATOR_CHAR ) ) ;
if ( idx ! = - 1 )
{
TQString szPrefixPath = szFileName . left ( idx ) ;
if ( ! KviFileUtils : : makeDir ( szPrefixPath ) )
{
setLastError ( __tr2qs ( " Failed to create the target directory " ) ) ;
return false ;
}
}
KviFile dest ( szFileName ) ;
if ( ! dest . openForWriting ( ) )
{
setLastError ( __tr2qs ( " Failed to open a source file for reading " ) ) ;
return false ;
}
TQString szProgressText ;
KviTQString : : sprintf ( szProgressText , __tr2qs ( " Unpacking file %Q " ) , & szFileName ) ;
if ( ! updateProgress ( pFile - > pos ( ) , szProgressText ) )
return false ; // aborted
// Size
kvi_u32_t uSize ;
if ( ! pFile - > load ( uSize ) ) return readError ( ) ;
// FilePayload
# ifdef COMPILE_ZLIB_SUPPORT
if ( uFlags & KVI_PACKAGE_DATAFIELD_FLAG_FILE_DEFLATE )
{
int iRemainingSize = uSize ;
unsigned char ibuffer [ BUFFER_SIZE ] ;
unsigned char obuffer [ BUFFER_SIZE ] ;
int iToRead = iRemainingSize ;
if ( iToRead > BUFFER_SIZE ) iToRead = BUFFER_SIZE ;
int iReaded = pFile - > readBlock ( ( char * ) ibuffer , iToRead ) ;
iRemainingSize - = iReaded ;
z_stream zstr ;
zstr . zalloc = Z_NULL ;
zstr . zfree = Z_NULL ;
zstr . opaque = Z_NULL ;
zstr . next_in = ibuffer ;
zstr . avail_in = iReaded ;
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
if ( inflateInit ( & zstr ) ! = Z_OK )
{
setLastError ( __tr2qs ( " Compression library initialization error " ) ) ;
return false ;
}
while ( ( iReaded > 0 ) & & ( iRemainingSize > 0 ) )
{
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
if ( inflate ( & zstr , Z_NO_FLUSH ) ! = Z_OK )
{
setLastError ( __tr2qs ( " Compression library error " ) ) ;
return false ;
}
if ( zstr . avail_out < BUFFER_SIZE )
{
int iDecompressed = zstr . next_out - obuffer ;
if ( dest . writeBlock ( ( char * ) obuffer , iDecompressed ) ! = iDecompressed )
{
inflateEnd ( & zstr ) ;
return writeError ( ) ;
}
}
if ( zstr . avail_in < BUFFER_SIZE )
{
int iDataToRead = BUFFER_SIZE - zstr . avail_in ;
if ( iDataToRead < BUFFER_SIZE )
{
if ( ibuffer ! = zstr . next_in )
{
// hum, there is still some data in the buffer to be readed
// and it is not at the beginning...move it to the beginning of ibuffer
memmove ( ibuffer , zstr . next_in , zstr . avail_in ) ;
}
}
if ( iDataToRead > iRemainingSize )
iDataToRead = iRemainingSize ;
iReaded = pFile - > readBlock ( ( char * ) ( ibuffer + zstr . avail_in ) , iDataToRead ) ;
if ( iReaded < 0 )
{
inflateEnd ( & zstr ) ;
return readError ( ) ;
}
iRemainingSize - = iReaded ;
zstr . avail_in + = iReaded ;
zstr . next_in = ibuffer ;
if ( ( zstr . total_in % 2000000 ) = = 0 )
{
TQString szTmp ;
KviTQString : : sprintf ( szTmp , TQString ( " (%d of %d bytes) " ) , zstr . total_in , uSize ) ;
TQString szPrg = szProgressText + szTmp ;
if ( ! updateProgress ( pFile - > pos ( ) , szPrg ) )
return false ; // aborted
}
}
}
// flush pending output
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
int ret ;
do {
ret = inflate ( & zstr , Z_FINISH ) ;
if ( ( ret = = Z_OK ) | | ( ret = = Z_STREAM_END ) | | ( ret = = Z_BUF_ERROR ) )
{
if ( zstr . avail_out < BUFFER_SIZE )
{
int iDecompressed = zstr . next_out - obuffer ;
if ( dest . writeBlock ( ( char * ) obuffer , iDecompressed ) ! = iDecompressed )
{
inflateEnd ( & zstr ) ;
return writeError ( ) ;
}
} /* else { THIS HAPPENS FOR ZERO SIZE FILES
tqDebug ( " hum.... internal, rEWq (ret = %d) (avail_out = %d) " , ret , zstr . avail_out ) ;
inflateEnd ( & zstr ) ;
setLastError ( __tr2qs ( " Compression library internal error " ) ) ;
return false ;
} */
zstr . next_out = obuffer ;
zstr . avail_out = BUFFER_SIZE ;
}
} while ( ( ret = = Z_OK ) | | ( ret = = Z_BUF_ERROR ) ) ;
inflateEnd ( & zstr ) ;
if ( ret ! = Z_STREAM_END )
{
setLastError ( __tr2qs ( " Error in compressed file stream " ) ) ;
return false ;
}
} else {
# endif
unsigned char buffer [ BUFFER_SIZE ] ;
int iTotalFileSize = 0 ;
int iRemainingData = uSize ;
int iToRead = iRemainingData ;
if ( iToRead > BUFFER_SIZE ) iToRead = BUFFER_SIZE ;
int iReaded = 1 ;
while ( ( iReaded > 0 ) & & ( iToRead > 0 ) )
{
iReaded = pFile - > readBlock ( ( char * ) buffer , iToRead ) ;
if ( iReaded > 0 )
{
iTotalFileSize + = iReaded ;
iRemainingData - = iReaded ;
if ( ( iTotalFileSize % 3000000 ) = = 0 )
{
TQString szTmp ;
KviTQString : : sprintf ( szTmp , TQString ( " (%d of %d bytes) " ) , iTotalFileSize , uSize ) ;
TQString szPrg = szProgressText + szTmp ;
if ( ! updateProgress ( pFile - > pos ( ) , szPrg ) )
return false ; // aborted
}
if ( dest . writeBlock ( ( char * ) buffer , iReaded ) ! = iReaded )
return writeError ( ) ;
}
int iToRead = iRemainingData ;
if ( iToRead > BUFFER_SIZE ) iToRead = BUFFER_SIZE ;
}
# ifdef COMPILE_ZLIB_SUPPORT
}
# endif
dest . close ( ) ;
return true ;
}
bool KviPackageReader : : getStringInfoField ( const TQString & szName , TQString & szBuffer )
{
TQString * pVal = m_pStringInfoFields - > find ( szName ) ;
if ( ! pVal ) return false ;
szBuffer = * pVal ;
return true ;
}
bool KviPackageReader : : unpack ( const TQString & szLocalFileName , const TQString & szUnpackPath , kvi_u32_t uUnpackFlags )
{
bool bRet = unpackInternal ( szLocalFileName , szUnpackPath , uUnpackFlags ) ;
hideProgressDialog ( ) ;
return bRet ;
}
bool KviPackageReader : : unpackInternal ( const TQString & szLocalFileName , const TQString & szUnpackPath , kvi_u32_t uUnpackFlags )
{
KviFile f ( szLocalFileName ) ;
if ( ! f . openForReading ( ) )
{
setLastError ( __tr2qs ( " Can't open file for reading " ) ) ;
return false ;
}
kvi_file_offset_t size = f . size ( ) ;
if ( ! ( uUnpackFlags & NoProgressDialog ) )
{
showProgressDialog ( __tr2qs ( " Reading package... " ) , size ) ;
updateProgress ( 0 , __tr2qs ( " Reading package header " ) ) ;
}
if ( ! readHeaderInternal ( & f , szLocalFileName ) )
return false ;
if ( ! updateProgress ( f . pos ( ) , __tr2qs ( " Reading package data " ) ) )
return false ; // aborted
while ( ! f . atEnd ( ) )
{
// DataFieldType
kvi_u32_t uDataFieldType ;
if ( ! f . load ( uDataFieldType ) ) return readError ( ) ;
// DataFieldLen
kvi_u32_t uDataFieldLen ;
if ( ! f . load ( uDataFieldLen ) ) return readError ( ) ;
switch ( uDataFieldType )
{
case KVI_PACKAGE_DATAFIELD_TYPE_FILE :
if ( ! unpackFile ( & f , szUnpackPath ) )
return false ;
break ;
default :
setLastError ( __tr2qs ( " Invalid data field: the package is probably corrupt " ) ) ;
return false ;
break ;
}
}
return true ;
}