2014-05-18 11:41:54 +00:00
/*
The MIT License ( MIT )
Copyright ( c ) 2013 The ioquake Group
Permission is hereby granted , free of charge , to any person obtaining a 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 , sublicense , and / or sell copies of
the Software , and to permit persons to whom the Software is furnished to do so ,
subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER
IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
# include <math.h>
# include <time.h>
# include <QFile>
# include <QFileInfo>
2014-05-28 08:30:02 +00:00
# include "minizip/unzip.h"
2014-05-18 11:41:54 +00:00
# include "fileextract.h"
FileExtractWorker : : FileExtractWorker ( const QString & archiveFilename , const QList < FileOperation > & filesToExtract ) : archiveFilename ( archiveFilename ) , filesToExtract ( filesToExtract ) , isCancelled ( false )
{
}
void FileExtractWorker : : extract ( )
{
qsrand ( time ( NULL ) ) ;
2014-05-28 08:30:02 +00:00
# ifdef Q_OS_WIN32
archiveFilename = archiveFilename . replace ( ' / ' , ' \\ ' ) ;
# endif
2014-05-18 11:41:54 +00:00
2014-05-28 08:30:02 +00:00
unzFile zipFile = unzOpen ( archiveFilename . toLatin1 ( ) . constData ( ) ) ;
2014-05-18 11:41:54 +00:00
2014-05-28 08:30:02 +00:00
if ( ! zipFile )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
emit errorMessage ( QString ( " Error opening '%1' for extraction " ) . arg ( archiveFilename ) ) ;
2014-05-18 11:41:54 +00:00
return ;
}
2014-05-28 08:30:02 +00:00
unz_global_info gi ;
2014-05-18 11:41:54 +00:00
2014-05-28 08:30:02 +00:00
if ( unzGetGlobalInfo ( zipFile , & gi ) ! = UNZ_OK )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
unzClose ( zipFile ) ;
emit errorMessage ( QString ( " Error getting global info from archive '%1' " ) . arg ( archiveFilename ) ) ;
2014-05-18 11:41:54 +00:00
return ;
}
const int bufferSize = 32 * 1024 ;
2014-05-28 08:30:02 +00:00
char buffer [ bufferSize ] ;
2014-05-18 11:41:54 +00:00
2014-05-28 08:30:02 +00:00
for ( unsigned int i = 0 ; i < gi . number_entry ; i + + )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
unz_file_info fi ;
char zipFilename [ 256 ] ;
2014-05-18 11:41:54 +00:00
2014-05-28 08:30:02 +00:00
if ( unzGetCurrentFileInfo ( zipFile , & fi , zipFilename , sizeof ( zipFilename ) , NULL , 0 , NULL , 0 ) ! = UNZ_OK )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
unzClose ( zipFile ) ;
emit errorMessage ( QString ( " Error getting file info from archive '%1' " ) . arg ( archiveFilename ) ) ;
return ;
2014-05-18 11:41:54 +00:00
}
// See if this is one of the files to be extracted.
int extractFileIndex = - 1 ;
2014-05-28 08:30:02 +00:00
for ( int j = 0 ; j < filesToExtract . size ( ) ; j + + )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
if ( filesToExtract . at ( j ) . source = = zipFilename )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
extractFileIndex = j ;
2014-05-18 11:41:54 +00:00
break ;
}
}
// Extract a file.
if ( extractFileIndex ! = - 1 )
{
2014-05-28 08:30:02 +00:00
if ( unzOpenCurrentFile ( zipFile ) ! = UNZ_OK )
{
unzClose ( zipFile ) ;
emit errorMessage ( QString ( " '%1': error opening '%2' " ) . arg ( archiveFilename ) . arg ( zipFilename ) ) ;
goto cleanup ;
}
2014-05-18 11:41:54 +00:00
// Write to the destination file if it doesn't exist, otherwise write to a uniquely named file.
QString filename ( filesToExtract . at ( extractFileIndex ) . dest ) ;
const bool fileExists = QFile : : exists ( filename ) ;
if ( fileExists )
{
filename = FileUtils : : uniqueFilename ( filesToExtract . at ( extractFileIndex ) . dest ) ;
}
QFile file ( filename ) ;
if ( ! file . open ( QIODevice : : WriteOnly ) )
{
2014-05-28 08:30:02 +00:00
unzCloseCurrentFile ( zipFile ) ;
unzClose ( zipFile ) ;
2014-05-18 11:41:54 +00:00
emit errorMessage ( QString ( " '%1': %2 " ) . arg ( file . fileName ( ) ) . arg ( file . errorString ( ) ) ) ;
goto cleanup ;
}
// Don't add a rename operation if the destination file doesn't exist - not trying to overwrite, don't need to rename anything to complete the transaction.
if ( fileExists )
{
FileOperation fo ;
fo . source = filename ;
fo . dest = filesToExtract . at ( extractFileIndex ) . dest ;
renameOperations . append ( fo ) ;
}
emit fileChanged ( QFileInfo ( file . fileName ( ) ) . fileName ( ) ) ;
qint64 totalBytesRead = 0 ;
for ( ; ; )
{
2014-05-28 08:30:02 +00:00
const int bytesRead = unzReadCurrentFile ( zipFile , buffer , bufferSize ) ;
2014-05-18 11:41:54 +00:00
2014-05-28 08:30:02 +00:00
if ( bytesRead < 0 )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
unzCloseCurrentFile ( zipFile ) ;
unzClose ( zipFile ) ;
emit errorMessage ( QString ( " '%1': error %2 extracting '%3' " ) . arg ( archiveFilename ) . arg ( bytesRead ) . arg ( zipFilename ) ) ;
goto cleanup ;
2014-05-18 11:41:54 +00:00
}
2014-05-28 08:30:02 +00:00
else if ( bytesRead = = 0 )
{
2014-05-18 11:41:54 +00:00
break ;
2014-05-28 08:30:02 +00:00
}
else
{
file . write ( buffer , bytesRead ) ;
totalBytesRead + = bytesRead ;
emit progressChanged ( totalBytesRead , fi . uncompressed_size ) ;
}
2014-05-18 11:41:54 +00:00
QMutexLocker locker ( & cancelMutex ) ;
if ( isCancelled )
2014-05-28 08:30:02 +00:00
{
unzCloseCurrentFile ( zipFile ) ;
unzClose ( zipFile ) ;
2014-05-18 11:41:54 +00:00
goto cleanup ;
2014-05-28 08:30:02 +00:00
}
2014-05-18 11:41:54 +00:00
}
2014-05-28 08:30:02 +00:00
unzCloseCurrentFile ( zipFile ) ;
2014-05-18 11:41:54 +00:00
}
2014-05-28 08:30:02 +00:00
if ( i + 1 < gi . number_entry & & unzGoToNextFile ( zipFile ) ! = UNZ_OK )
2014-05-18 11:41:54 +00:00
{
2014-05-28 08:30:02 +00:00
emit errorMessage ( QString ( " '%1': error getting next file' " ) . arg ( archiveFilename ) ) ;
unzClose ( zipFile ) ;
goto cleanup ;
2014-05-18 11:41:54 +00:00
}
}
2014-05-28 08:30:02 +00:00
unzClose ( zipFile ) ;
2014-05-18 11:41:54 +00:00
emit finished ( renameOperations ) ;
return ;
// Delete all the destination files.
cleanup :
for ( int i = 0 ; i < renameOperations . size ( ) ; i + + )
{
QFile : : remove ( renameOperations . at ( i ) . source ) ;
}
}
void FileExtractWorker : : cancel ( )
{
QMutexLocker locker ( & cancelMutex ) ;
isCancelled = true ;
}