You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvilib/file/kvi_packagefile.cpp

1029 lines
27 KiB

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