2010-07-21 18:05:38 -05:00
//
2010-08-19 15:27:48 -05:00
// SSZipArchive.m
// SSZipArchive
2010-07-21 18:05:38 -05:00
//
// Created by Sam Soffes on 7/21/10.
2013-01-29 01:53:49 -05:00
// Copyright (c) Sam Soffes 2010-2013. All rights reserved.
2010-07-21 18:05:38 -05:00
//
2010-08-19 15:27:48 -05:00
# import "SSZipArchive.h"
2010-07-21 18:05:38 -05:00
# include "minizip/zip.h"
# import "zlib.h"
# import "zconf.h"
2012-05-09 23:53:22 -04:00
# include <sys/stat.h>
2011-12-26 23:32:55 -05:00
# define CHUNK 16384
2011-10-03 23:28:54 -07:00
@interface SSZipArchive ( )
2012-05-07 22:27:45 -07:00
+ ( NSDate * ) _dateWithMSDOSFormat : ( UInt32 ) msdosDateTime ;
2010-07-21 18:05:38 -05:00
@end
2011-12-26 23:32:55 -05:00
@implementation SSZipArchive {
NSString * _path ;
NSString * _filename ;
zipFile _zip ;
}
2010-07-21 18:05:38 -05:00
2011-10-03 23:28:54 -07:00
# pragma mark - Unzipping
2010-07-21 18:05:38 -05:00
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination {
2012-01-14 20:46:32 +01:00
return [ self unzipFileAtPath : path toDestination : destination delegate : nil ] ;
2010-07-21 18:05:38 -05:00
}
2011-10-03 23:28:54 -07:00
2010-07-23 10:41:46 -05:00
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination overwrite: ( BOOL ) overwrite password: ( NSString * ) password error: ( NSError * * ) error {
2012-01-14 20:46:32 +01:00
return [ self unzipFileAtPath : path toDestination : destination overwrite : overwrite password : password error : error delegate : nil ] ;
}
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination delegate: ( id < SSZipArchiveDelegate > ) delegate {
return [ self unzipFileAtPath : path toDestination : destination overwrite : YES password : nil error : nil delegate : delegate ] ;
}
+ ( BOOL ) unzipFileAtPath: ( NSString * ) path toDestination: ( NSString * ) destination overwrite: ( BOOL ) overwrite password: ( NSString * ) password error: ( NSError * * ) error delegate: ( id < SSZipArchiveDelegate > ) delegate {
2010-07-21 18:05:38 -05:00
// Begin opening
zipFile zip = unzOpen ( ( const char * ) [ path UTF8String ] ) ;
if ( zip = = NULL ) {
2010-07-23 10:41:46 -05:00
NSDictionary * userInfo = [ NSDictionary dictionaryWithObject : @" failed to open zip file " forKey : NSLocalizedDescriptionKey ] ;
2010-07-26 15:18:32 -05:00
if ( error ) {
2010-08-19 15:27:48 -05:00
* error = [ NSError errorWithDomain : @" SSZipArchiveErrorDomain " code : - 1 userInfo : userInfo ] ;
2010-07-26 15:18:32 -05:00
}
2010-07-21 18:05:38 -05:00
return NO ;
}
2011-07-21 15:39:03 -07:00
unz_global_info globalInfo = { 0ul , 0ul } ;
2010-07-23 10:41:46 -05:00
unzGetGlobalInfo ( zip , & globalInfo ) ;
2010-07-21 18:05:38 -05:00
// Begin unzipping
if ( unzGoToFirstFile ( zip ) ! = UNZ_OK ) {
2010-07-23 10:41:46 -05:00
NSDictionary * userInfo = [ NSDictionary dictionaryWithObject : @" failed to open first file in zip file " forKey : NSLocalizedDescriptionKey ] ;
2010-07-26 15:18:32 -05:00
if ( error ) {
2010-08-19 15:27:48 -05:00
* error = [ NSError errorWithDomain : @" SSZipArchiveErrorDomain " code : - 2 userInfo : userInfo ] ;
2010-07-26 15:18:32 -05:00
}
2010-07-21 18:05:38 -05:00
return NO ;
}
BOOL success = YES ;
2012-05-07 22:27:45 -07:00
int ret = 0 ;
2010-07-21 18:05:38 -05:00
unsigned char buffer [ 4096 ] = { 0 } ;
NSFileManager * fileManager = [ NSFileManager defaultManager ] ;
2012-05-07 22:27:45 -07:00
NSMutableSet * directoriesModificationDates = [ [ NSMutableSet alloc ] init ] ;
2010-07-21 18:05:38 -05:00
2012-05-07 22:27:45 -07:00
// Message delegate
2012-05-07 23:05:06 -07:00
if ( [ delegate respondsToSelector : @selector ( zipArchiveWillUnzipArchiveAtPath : zipInfo : ) ] ) {
[ delegate zipArchiveWillUnzipArchiveAtPath : path zipInfo : globalInfo ] ;
2012-05-07 22:27:45 -07:00
}
2012-01-14 20:46:32 +01:00
NSInteger currentFileNumber = 0 ;
2010-07-21 18:05:38 -05:00
do {
2013-06-17 15:33:44 +02:00
@autoreleasepool {
if ( [ password length ] = = 0 ) {
ret = unzOpenCurrentFile ( zip ) ;
} else {
ret = unzOpenCurrentFilePassword ( zip , [ password cStringUsingEncoding : NSASCIIStringEncoding ] ) ;
}
if ( ret ! = UNZ_OK ) {
success = NO ;
break ;
}
// Reading data and write to file
unz_file_info fileInfo ;
memset ( & fileInfo , 0 , sizeof ( unz_file_info ) ) ;
ret = unzGetCurrentFileInfo ( zip , & fileInfo , NULL , 0 , NULL , 0 , NULL , 0 ) ;
if ( ret ! = UNZ_OK ) {
success = NO ;
unzCloseCurrentFile ( zip ) ;
break ;
}
// Message delegate
if ( [ delegate respondsToSelector : @selector ( zipArchiveWillUnzipFileAtIndex : totalFiles : archivePath : fileInfo : ) ] ) {
[ delegate zipArchiveWillUnzipFileAtIndex : currentFileNumber totalFiles : ( NSInteger ) globalInfo . number_entry
archivePath : path fileInfo : fileInfo ] ;
}
char * filename = ( char * ) malloc ( fileInfo . size_filename + 1 ) ;
unzGetCurrentFileInfo ( zip , & fileInfo , filename , fileInfo . size_filename + 1 , NULL , 0 , NULL , 0 ) ;
filename [ fileInfo . size_filename ] = ' \0 ' ;
//
// NOTE
// I used the ZIP spec from here:
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
//
// ...to deduce this method of detecting whether the file in the ZIP is a symbolic link.
// If it is, it is listed as a directory but has a data size greater than zero (real
// directories have it equal to 0) and the included, uncompressed data is the symbolic link path.
//
// ZIP files did not originally include support for symbolic links so the specification
// doesn't include anything in them that isn't part of a unix extension that isn't being used
// by the archivers we're testing. Most of this is figured out through trial and error and
// reading ZIP headers in hex editors. This seems to do the trick though.
//
const uLong ZipCompressionMethodStore = 0 ;
BOOL fileIsSymbolicLink = NO ;
if ( ( fileInfo . compression_method = = ZipCompressionMethodStore ) & & // Is it compressed?
( S_ISDIR ( fileInfo . external_fa ) ) & & // Is it marked as a directory
( fileInfo . compressed_size > 0 ) ) // Is there any data?
{
fileIsSymbolicLink = YES ;
}
// Check if it contains directory
NSString * strPath = [ NSString stringWithCString : filename encoding : NSUTF8StringEncoding ] ;
BOOL isDirectory = NO ;
if ( filename [ fileInfo . size_filename - 1 ] = = ' / ' | | filename [ fileInfo . size_filename - 1 ] = = ' \\ ' ) {
isDirectory = YES ;
}
free ( filename ) ;
// Contains a path
if ( [ strPath rangeOfCharacterFromSet : [ NSCharacterSet characterSetWithCharactersInString : @" / \\ " ] ] . location ! = NSNotFound ) {
strPath = [ strPath stringByReplacingOccurrencesOfString : @" \\ " withString : @" / " ] ;
}
NSString * fullPath = [ destination stringByAppendingPathComponent : strPath ] ;
NSError * err = nil ;
NSDate * modDate = [ [ self class ] _dateWithMSDOSFormat : ( UInt32 ) fileInfo . dosDate ] ;
NSDictionary * directoryAttr = [ NSDictionary dictionaryWithObjectsAndKeys : modDate , NSFileCreationDate , modDate , NSFileModificationDate , nil ] ;
if ( isDirectory ) {
[ fileManager createDirectoryAtPath : fullPath withIntermediateDirectories : YES attributes : directoryAttr error : & err ] ;
} else {
[ fileManager createDirectoryAtPath : [ fullPath stringByDeletingLastPathComponent ] withIntermediateDirectories : YES attributes : directoryAttr error : & err ] ;
}
if ( nil ! = err ) {
NSLog ( @" [SSZipArchive] Error: %@ " , err . localizedDescription ) ;
}
if ( ! fileIsSymbolicLink )
[ directoriesModificationDates addObject : [ NSDictionary dictionaryWithObjectsAndKeys : fullPath , @" path " , modDate , @" modDate " , nil ] ] ;
if ( [ fileManager fileExistsAtPath : fullPath ] & & ! isDirectory & & ! overwrite ) {
unzCloseCurrentFile ( zip ) ;
ret = unzGoToNextFile ( zip ) ;
continue ;
}
if ( ! fileIsSymbolicLink )
{
FILE * fp = fopen ( ( const char * ) [ fullPath UTF8String ] , " wb " ) ;
while ( fp ) {
int readBytes = unzReadCurrentFile ( zip , buffer , 4096 ) ;
if ( readBytes > 0 ) {
fwrite ( buffer , readBytes , 1 , fp ) ;
} else {
break ;
}
}
if ( fp ) {
fclose ( fp ) ;
// Set the original datetime property
if ( fileInfo . dosDate ! = 0 ) {
NSDate * orgDate = [ [ self class ] _dateWithMSDOSFormat : ( UInt32 ) fileInfo . dosDate ] ;
NSDictionary * attr = [ NSDictionary dictionaryWithObject : orgDate forKey : NSFileModificationDate ] ;
if ( attr ) {
if ( [ fileManager setAttributes : attr ofItemAtPath : fullPath error : nil ] = = NO ) {
// Can't set attributes
2013-06-25 17:11:08 +01:00
NSLog ( @" [SSZipArchive] Failed to set attributes - whilst setting modification date " ) ;
2013-06-17 15:33:44 +02:00
}
}
}
2013-06-25 17:11:08 +01:00
// Set the original permissions on the file
2013-07-01 04:55:42 -05:00
uLong permissions = fileInfo . external_fa > > 16 ;
if ( permissions ! = 0 ) {
2013-06-25 17:11:08 +01:00
// Store it into a NSNumber
NSNumber * permissionsValue = @( permissions ) ;
// Retrieve any existing attributes
NSMutableDictionary * attrs = [ [ NSMutableDictionary alloc ] initWithDictionary : [ fileManager attributesOfItemAtPath : fullPath error : nil ] ] ;
// Set the value in the attributes dict
attrs [ NSFilePosixPermissions ] = permissionsValue ;
// Update attributes
if ( [ fileManager setAttributes : attrs ofItemAtPath : fullPath error : nil ] = = NO ) {
// Unable to set the permissions attribute
NSLog ( @" [SSZipArchive] Failed to set attributes - whilst setting permissions " ) ;
}
}
2013-06-17 15:33:44 +02:00
}
}
else
{
// Get the path for the symbolic link
NSURL * symlinkURL = [ NSURL fileURLWithPath : fullPath ] ;
NSMutableString * destinationPath = [ NSMutableString string ] ;
int bytesRead = 0 ;
while ( ( bytesRead = unzReadCurrentFile ( zip , buffer , 4096 ) ) > 0 )
{
buffer [ bytesRead ] = 0 ;
[ destinationPath appendString : [ NSString stringWithUTF8String : ( const char * ) buffer ] ] ;
}
//NSLog(@"Symlinking to: %@", destinationPath);
NSURL * destinationURL = [ NSURL fileURLWithPath : destinationPath ] ;
// Create the symbolic link
NSError * symlinkError = nil ;
[ fileManager createSymbolicLinkAtURL : symlinkURL withDestinationURL : destinationURL error : & symlinkError ] ;
if ( symlinkError ! = nil )
{
NSLog ( @" Failed to create symbolic link at \" %@ \" to \" %@ \" . Error: %@ " , symlinkURL . absoluteString , destinationURL . absoluteString , symlinkError . localizedDescription ) ;
}
}
unzCloseCurrentFile ( zip ) ;
ret = unzGoToNextFile ( zip ) ;
// Message delegate
if ( [ delegate respondsToSelector : @selector ( zipArchiveDidUnzipFileAtIndex : totalFiles : archivePath : fileInfo : ) ] ) {
[ delegate zipArchiveDidUnzipFileAtIndex : currentFileNumber totalFiles : ( NSInteger ) globalInfo . number_entry
archivePath : path fileInfo : fileInfo ] ;
}
currentFileNumber + + ;
2012-05-07 22:27:45 -07:00
}
2013-02-24 10:40:21 -08:00
} while ( ret = = UNZ_OK & & ret ! = UNZ_END_OF_LIST_OF_FILE ) ;
2010-07-21 18:05:38 -05:00
// Close
unzClose ( zip ) ;
2012-05-07 22:27:45 -07:00
// The process of decompressing the .zip archive causes the modification times on the folders
// to be set to the present time. So, when we are done, they need to be explicitly set.
// set the modification date on all of the directories.
NSError * err = nil ;
for ( NSDictionary * d in directoriesModificationDates ) {
if ( ! [ [ NSFileManager defaultManager ] setAttributes : [ NSDictionary dictionaryWithObjectsAndKeys : [ d objectForKey : @" modDate " ] , NSFileModificationDate , nil ] ofItemAtPath : [ d objectForKey : @" path " ] error : & err ] ) {
NSLog ( @" [SSZipArchive] Set attributes failed for directory: %@. " , [ d objectForKey : @" path " ] ) ;
}
if ( err ) {
NSLog ( @" [SSZipArchive] Error setting directory file modification date attribute: %@ " , err . localizedDescription ) ;
}
}
# if !__has_feature(objc_arc)
[ directoriesModificationDates release ] ;
# endif
2012-05-07 23:05:06 -07:00
// Message delegate
if ( success & & [ delegate respondsToSelector : @selector ( zipArchiveDidUnzipArchiveAtPath : zipInfo : unzippedPath : ) ] ) {
[ delegate zipArchiveDidUnzipArchiveAtPath : path zipInfo : globalInfo unzippedPath : destination ] ;
}
2010-07-21 18:05:38 -05:00
return success ;
}
2011-12-20 09:24:56 +00:00
2011-12-26 23:32:55 -05:00
# pragma mark - Zipping
2011-12-20 09:24:56 +00:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withFilesAtPaths: ( NSArray * ) paths {
2011-12-26 23:32:55 -05:00
BOOL success = NO ;
SSZipArchive * zipArchive = [ [ SSZipArchive alloc ] initWithPath : path ] ;
if ( [ zipArchive open ] ) {
for ( NSString * path in paths ) {
[ zipArchive writeFile : path ] ;
}
success = [ zipArchive close ] ;
}
2012-05-07 22:27:45 -07:00
# if !__has_feature(objc_arc)
2011-12-26 23:32:55 -05:00
[ zipArchive release ] ;
2012-05-07 22:27:45 -07:00
# endif
2011-12-26 23:32:55 -05:00
return success ;
2011-12-20 09:24:56 +00:00
}
2011-12-26 23:32:55 -05:00
2013-03-09 05:36:20 -08:00
+ ( BOOL ) createZipFileAtPath: ( NSString * ) path withContentsOfDirectory: ( NSString * ) directoryPath {
BOOL success = NO ;
2013-05-01 12:03:48 +01:00
NSFileManager * fileManager = nil ;
2013-03-09 05:36:20 -08:00
SSZipArchive * zipArchive = [ [ SSZipArchive alloc ] initWithPath : path ] ;
if ( [ zipArchive open ] ) {
// use a local filemanager (queue/thread compatibility)
2013-05-01 12:03:48 +01:00
fileManager = [ [ NSFileManager alloc ] init ] ;
2013-03-09 05:36:20 -08:00
NSDirectoryEnumerator * dirEnumerator = [ fileManager enumeratorAtPath : directoryPath ] ;
NSString * fileName ;
2013-05-01 12:38:44 +01:00
while ( ( fileName = [ dirEnumerator nextObject ] ) ) {
2013-03-09 05:36:20 -08:00
BOOL isDir ;
NSString * fullFilePath = [ directoryPath stringByAppendingPathComponent : fileName ] ;
[ fileManager fileExistsAtPath : fullFilePath isDirectory : & isDir ] ;
if ( ! isDir ) {
[ zipArchive writeFileAtPath : fullFilePath withFileName : fileName ] ;
}
}
success = [ zipArchive close ] ;
}
# if !__has_feature(objc_arc)
2013-05-01 12:03:48 +01:00
[ fileManager release ] ;
2013-03-09 05:36:20 -08:00
[ zipArchive release ] ;
# endif
return success ;
}
2011-12-26 23:32:55 -05:00
- ( id ) initWithPath: ( NSString * ) path {
if ( ( self = [ super init ] ) ) {
_path = [ path copy ] ;
}
return self ;
2011-12-20 09:24:56 +00:00
}
2011-12-26 23:32:55 -05:00
2012-05-07 22:27:45 -07:00
# if !__has_feature(objc_arc)
2011-12-20 09:24:56 +00:00
- ( void ) dealloc {
2012-05-07 22:27:45 -07:00
[ _path release ] ;
2011-12-26 23:32:55 -05:00
[ super dealloc ] ;
2011-12-20 09:24:56 +00:00
}
2012-05-07 22:27:45 -07:00
# endif
2011-12-20 09:24:56 +00:00
2011-12-26 23:32:55 -05:00
- ( BOOL ) open {
NSAssert ( ( _zip = = NULL ) , @" Attempting open an archive which is already open " ) ;
_zip = zipOpen ( [ _path UTF8String ] , APPEND_STATUS_CREATE ) ;
return ( NULL ! = _zip ) ;
2011-12-20 09:24:56 +00:00
}
2011-12-26 23:32:55 -05:00
2012-05-07 22:12:09 -07:00
- ( void ) zipInfo: ( zip_fileinfo * ) zipInfo setDate: ( NSDate * ) date {
NSCalendar * currentCalendar = [ NSCalendar currentCalendar ] ;
2011-12-27 17:25:34 +01:00
uint flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ;
2012-05-07 22:12:09 -07:00
NSDateComponents * components = [ currentCalendar components : flags fromDate : date ] ;
zipInfo - > tmz_date . tm_sec = ( unsigned int ) components . second ;
zipInfo - > tmz_date . tm_min = ( unsigned int ) components . minute ;
zipInfo - > tmz_date . tm_hour = ( unsigned int ) components . hour ;
zipInfo - > tmz_date . tm_mday = ( unsigned int ) components . day ;
zipInfo - > tmz_date . tm_mon = ( unsigned int ) components . month - 1 ;
zipInfo - > tmz_date . tm_year = ( unsigned int ) components . year ;
2011-12-27 17:25:34 +01:00
}
2013-03-09 05:36:20 -08:00
- ( BOOL ) writeFile: ( NSString * ) path
{
return [ self writeFileAtPath : path withFileName : nil ] ;
}
2011-12-26 23:32:55 -05:00
2013-03-09 05:36:20 -08:00
// supports writing files with logical folder/directory structure
// *path* is the absolute path of the file that will be compressed
// *fileName* is the relative name of the file how it is stored within the zip e.g. /folder/subfolder/text1.txt
- ( BOOL ) writeFileAtPath: ( NSString * ) path withFileName: ( NSString * ) fileName {
NSAssert ( ( _zip ! = NULL ) , @" Attempting to write to an archive which was never opened " ) ;
2011-12-26 23:32:55 -05:00
FILE * input = fopen ( [ path UTF8String ] , " r " ) ;
if ( NULL = = input ) {
return NO ;
}
2013-03-09 05:36:20 -08:00
const char * afileName ;
if ( ! fileName ) {
afileName = [ path . lastPathComponent UTF8String ] ;
}
else {
afileName = [ fileName UTF8String ] ;
}
2013-05-01 12:36:32 +01:00
zip_fileinfo zipInfo = { { 0 } } ;
2013-04-19 15:05:54 +02:00
NSDictionary * attr = [ [ NSFileManager defaultManager ] attributesOfItemAtPath : path error : nil ] ;
if ( attr )
{
2013-06-25 17:11:08 +01:00
NSDate * fileDate = ( NSDate * ) [ attr objectForKey : NSFileModificationDate ] ;
if ( fileDate )
{
[ self zipInfo : & zipInfo setDate : fileDate ] ;
}
// Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727
// Get the permissions value from the files attributes
NSNumber * permissionsValue = ( NSNumber * ) [ attr objectForKey : NSFilePosixPermissions ] ;
if ( permissionsValue ) {
// Get the short value for the permissions
short permissionsShort = permissionsValue . shortValue ;
// Convert this into an octal by adding 010000, 010000 being the flag for a regular file
NSInteger permissionsOctal = 0100000 + permissionsShort ;
// Convert this into a long value
uLong permissionsLong = @( permissionsOctal ) . unsignedLongValue ;
// Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
zipInfo . external_fa = permissionsLong < < 16L ;
}
2013-04-19 15:05:54 +02:00
}
2013-06-25 13:45:30 +02:00
zipOpenNewFileInZip ( _zip , afileName , & zipInfo , NULL , 0 , NULL , 0 , NULL , Z_DEFLATED , Z_DEFAULT_COMPRESSION ) ;
2013-03-09 05:36:20 -08:00
2011-12-26 23:32:55 -05:00
void * buffer = malloc ( CHUNK ) ;
unsigned int len = 0 ;
2013-03-09 05:36:20 -08:00
while ( ! feof ( input ) )
{
2011-12-26 23:32:55 -05:00
len = ( unsigned int ) fread ( buffer , 1 , CHUNK , input ) ;
zipWriteInFileInZip ( _zip , buffer , len ) ;
}
2013-03-09 05:36:20 -08:00
2011-12-26 23:32:55 -05:00
zipCloseFileInZip ( _zip ) ;
free ( buffer ) ;
2013-09-07 08:22:56 -07:00
fclose ( input ) ;
2011-12-26 23:32:55 -05:00
return YES ;
2011-12-20 09:24:56 +00:00
}
2011-12-26 23:32:55 -05:00
2011-12-20 09:24:56 +00:00
- ( BOOL ) writeData: ( NSData * ) data filename: ( NSString * ) filename {
2011-12-27 17:25:34 +01:00
if ( ! _zip ) {
return NO ;
}
if ( ! data ) {
return NO ;
}
2012-05-09 11:17:44 +02:00
zip_fileinfo zipInfo = { { 0 , 0 , 0 , 0 , 0 , 0 } , 0 , 0 , 0 } ;
2011-12-27 17:25:34 +01:00
[ self zipInfo : & zipInfo setDate : [ NSDate date ] ] ;
2011-12-26 23:32:55 -05:00
2011-12-27 17:25:34 +01:00
zipOpenNewFileInZip ( _zip , [ filename UTF8String ] , & zipInfo , NULL , 0 , NULL , 0 , NULL , Z_DEFLATED , Z_DEFAULT_COMPRESSION ) ;
2011-12-26 23:32:55 -05:00
2012-05-07 22:12:09 -07:00
zipWriteInFileInZip ( _zip , data . bytes , ( unsigned int ) data . length ) ;
2011-12-26 23:32:55 -05:00
zipCloseFileInZip ( _zip ) ;
return YES ;
2011-12-20 09:24:56 +00:00
}
2012-05-07 22:27:45 -07:00
2011-12-20 09:24:56 +00:00
- ( BOOL ) close {
2012-05-07 22:27:45 -07:00
NSAssert ( ( _zip ! = NULL ) , @" [SSZipArchive] Attempting to close an archive which was never opened " ) ;
2011-12-26 23:32:55 -05:00
zipClose ( _zip , NULL ) ;
return YES ;
2011-12-20 09:24:56 +00:00
}
2010-07-21 18:05:38 -05:00
2011-12-26 23:32:55 -05:00
2011-10-03 23:28:54 -07:00
# pragma mark - Private
2012-05-07 22:27:45 -07:00
// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html
// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss
// YYYYYYY is years from 1980 = 0
// sssss is (seconds/2).
//
// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24
// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 2 = 14:33:06
+ ( NSDate * ) _dateWithMSDOSFormat: ( UInt32 ) msdosDateTime {
static const UInt32 kYearMask = 0xFE000000 ;
static const UInt32 kMonthMask = 0x1E00000 ;
static const UInt32 kDayMask = 0x1F0000 ;
static const UInt32 kHourMask = 0xF800 ;
static const UInt32 kMinuteMask = 0x7E0 ;
static const UInt32 kSecondMask = 0x1F ;
2013-07-12 19:31:18 -03:00
static NSCalendar * gregorian ;
2013-07-12 17:28:18 +09:00
static dispatch_once_t onceToken ;
dispatch_once ( & onceToken , ^ {
gregorian = [ [ NSCalendar alloc ] initWithCalendarIdentifier : NSGregorianCalendar ] ;
} ) ;
2012-05-07 22:27:45 -07:00
NSDateComponents * components = [ [ NSDateComponents alloc ] init ] ;
2012-05-07 22:27:45 -07:00
2012-05-07 22:27:45 -07:00
NSAssert ( 0xFFFFFFFF = = ( kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask ) , @" [SSZipArchive] MSDOS date masks don't add up " ) ;
[ components setYear : 1980 + ( ( msdosDateTime & kYearMask ) > > 25 ) ] ;
[ components setMonth : ( msdosDateTime & kMonthMask ) > > 21 ] ;
[ components setDay : ( msdosDateTime & kDayMask ) > > 16 ] ;
[ components setHour : ( msdosDateTime & kHourMask ) > > 11 ] ;
[ components setMinute : ( msdosDateTime & kMinuteMask ) > > 5 ] ;
[ components setSecond : ( msdosDateTime & kSecondMask ) * 2 ] ;
NSDate * date = [ NSDate dateWithTimeInterval : 0 sinceDate : [ gregorian dateFromComponents : components ] ] ;
# if !__has_feature(objc_arc)
[ components release ] ;
# endif
2010-07-21 18:05:38 -05:00
return date ;
}
@end