XADMaster/XADUnarchiver.m

786 lines
23 KiB
Objective-C

/*
* XADUnarchiver.m
*
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#import "XADUnarchiver.h"
#import "XADPlatform.h"
#import "XADAppleDouble.h"
#import "CSFileHandle.h"
#import "Progress.h"
@implementation XADUnarchiver
+(XADUnarchiver *)unarchiverForArchiveParser:(XADArchiveParser *)archiveparser
{
return [[[self alloc] initWithArchiveParser:archiveparser] autorelease];
}
+(XADUnarchiver *)unarchiverForPath:(NSString *)path
{
return [self unarchiverForPath:path error:NULL];
}
+(XADUnarchiver *)unarchiverForPath:(NSString *)path error:(XADError *)errorptr
{
XADArchiveParser *archiveparser=[XADArchiveParser archiveParserForPath:path error:errorptr];
if(!archiveparser) return nil;
return [[[self alloc] initWithArchiveParser:archiveparser] autorelease];
}
-(id)initWithArchiveParser:(XADArchiveParser *)archiveparser
{
if((self=[super init]))
{
parser=[archiveparser retain];
destination=nil;
forkstyle=XADDefaultForkStyle;
preservepermissions=NO;
updateinterval=0.1;
delegate=nil;
shouldstop=NO;
deferreddirectories=[NSMutableArray new];
deferredlinks=[NSMutableArray new];
}
return self;
}
-(void)dealloc
{
[parser release];
[destination release];
[deferreddirectories release];
[deferredlinks release];
[super dealloc];
}
-(XADArchiveParser *)archiveParser { return parser; }
-(id)delegate { return delegate; }
-(void)setDelegate:(id)newdelegate { delegate=newdelegate; }
-(NSString *)destination { return destination; }
-(void)setDestination:(NSString *)destpath
{
[destination autorelease];
destination=[destpath retain];
}
-(int)macResourceForkStyle { return forkstyle; }
-(void)setMacResourceForkStyle:(int)style { forkstyle=style; }
-(BOOL)preservesPermissions { return preservepermissions; }
-(void)setPreserevesPermissions:(BOOL)preserveflag { preservepermissions=preserveflag; }
-(double)updateInterval { return updateinterval; }
-(void)setUpdateInterval:(double)interval { updateinterval=interval; }
-(XADError)parseAndUnarchive
{
id olddelegate=[parser delegate];
[parser setDelegate:self];
XADError error=[parser parseWithoutExceptions];
[parser setDelegate:olddelegate];
if(error) return error;
if([self _shouldStop]) return XADBreakError;
error=[self finishExtractions];
if(error) return error;
error=[parser testChecksumWithoutExceptions];
if(error) return error;
return XADNoError;
}
-(void)archiveParser:(XADArchiveParser *)parser foundEntryWithDictionary:(NSDictionary *)dict
{
//if([self _shouldStop]) return; // Unnecessary - XADArchiveParser handles it.
[self extractEntryWithDictionary:dict];
}
-(BOOL)archiveParsingShouldStop:(XADArchiveParser *)parser
{
return [self _shouldStop];
}
-(void)archiveParserNeedsPassword:(XADArchiveParser *)parser
{
[delegate unarchiverNeedsPassword:self];
}
-(void)archiveParser:(XADArchiveParser *)parser findsFileInterestingForReason:(NSString *)reason
{
[delegate unarchiver:self findsFileInterestingForReason:reason];
}
-(XADError)extractEntryWithDictionary:(NSDictionary *)dict
{
return [self extractEntryWithDictionary:dict as:nil forceDirectories:NO];
}
-(XADError)extractEntryWithDictionary:(NSDictionary *)dict forceDirectories:(BOOL)force
{
return [self extractEntryWithDictionary:dict as:nil forceDirectories:force];
}
-(XADError)extractEntryWithDictionary:(NSDictionary *)dict as:(NSString *)path
{
return [self extractEntryWithDictionary:dict as:path forceDirectories:NO];
}
-(XADError)extractEntryWithDictionary:(NSDictionary *)dict as:(NSString *)path forceDirectories:(BOOL)force
{
NSAutoreleasePool *pool=[NSAutoreleasePool new];
NSNumber *dirnum=[dict objectForKey:XADIsDirectoryKey];
NSNumber *linknum=[dict objectForKey:XADIsLinkKey];
NSNumber *resnum=[dict objectForKey:XADIsResourceForkKey];
NSNumber *archivenum=[dict objectForKey:XADIsArchiveKey];
BOOL isdir=dirnum&&[dirnum boolValue];
BOOL islink=linknum&&[linknum boolValue];
BOOL isres=resnum&&[resnum boolValue];
BOOL isarchive=archivenum&&[archivenum boolValue];
// If we were not given a path, pick one ourselves.
if(!path)
{
XADPath *name=[dict objectForKey:XADFileNameKey];
NSString *namestring=[name sanitizedPathString];
if(destination) path=[destination stringByAppendingPathComponent:namestring];
else path=namestring;
// Adjust path for resource forks.
path=[self adjustPathString:path forEntryWithDictionary:dict];
}
// Ask for permission and possibly a path, and report that we are starting.
if(delegate)
{
if(![delegate unarchiver:self shouldExtractEntryWithDictionary:dict suggestedPath:&path])
{
[pool release];
return XADNoError;
}
[delegate unarchiver:self willExtractEntryWithDictionary:dict to:path];
}
XADError error;
error=[self _ensureDirectoryExists:[path stringByDeletingLastPathComponent]];
if(error) goto end;
// Attempt to extract embedded archives if requested.
if(isarchive&&delegate)
{
NSString *unarchiverpath=[path stringByDeletingLastPathComponent];
if([delegate unarchiver:self shouldExtractArchiveEntryWithDictionary:dict to:unarchiverpath])
{
error=[self _extractArchiveEntryWithDictionary:dict to:unarchiverpath name:[path lastPathComponent]];
// If extraction was attempted, and succeeded for failed, skip everything else.
// Otherwise, if the archive couldn't be opened, fall through and extract normally.
if(error!=XADSubArchiveError) goto end;
}
}
// Extract normally.
if(isres)
{
switch(forkstyle)
{
case XADIgnoredForkStyle:
break;
case XADMacOSXForkStyle:
if(!isdir)
error=[XADPlatform extractResourceForkEntryWithDictionary:dict unarchiver:self toPath:path];
break;
case XADHiddenAppleDoubleForkStyle:
case XADVisibleAppleDoubleForkStyle:
error=[self _extractResourceForkEntryWithDictionary:dict asAppleDoubleFile:path];
break;
case XADHFVExplorerAppleDoubleForkStyle:
// We need to make sure there is an empty file for the data fork in all
// cases, so just try to recover the original filename and create an empty
// file there in case one doesn't exist, and this isn't a directory.
// Kludge in the same file attributes as the resource fork. If there is
// an actual data fork later, it will overwrite this file. There special-case
// code to avoid collision warnings.
if(![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:NULL] && !isdir)
{
NSString *dirpart=[path stringByDeletingLastPathComponent];
NSString *namepart=[path lastPathComponent];
if([namepart hasPrefix:@"%"])
{
NSString *originalname=[namepart substringFromIndex:1];
NSString *datapath=[dirpart stringByAppendingPathComponent:originalname];
[[NSData data] writeToFile:datapath atomically:NO];
[self _updateFileAttributesAtPath:datapath forEntryWithDictionary:dict deferDirectories:!force];
}
}
error=[self _extractResourceForkEntryWithDictionary:dict asAppleDoubleFile:path];
break;
default:
// TODO: better error
error=XADBadParametersError;
break;
}
}
else if(isdir)
{
error=[self _extractDirectoryEntryWithDictionary:dict as:path];
}
else if(islink)
{
error=[self _extractLinkEntryWithDictionary:dict as:path];
}
else
{
error=[self _extractFileEntryWithDictionary:dict as:path];
}
if(!error)
{
error=[self _updateFileAttributesAtPath:path forEntryWithDictionary:dict deferDirectories:!force];
}
// Report success or failure
end:
if(delegate)
{
[delegate unarchiver:self didExtractEntryWithDictionary:dict to:path error:error];
}
[pool release];
return error;
}
static NSComparisonResult SortDirectoriesByDepthAndResource(id entry1,id entry2,void *context)
{
NSDictionary *dict1=[entry1 objectAtIndex:1];
NSDictionary *dict2=[entry2 objectAtIndex:1];
XADPath *path1=[dict1 objectForKey:XADFileNameKey];
XADPath *path2=[dict2 objectForKey:XADFileNameKey];
int depth1=[path1 depth];
int depth2=[path2 depth];
if(depth1>depth2) return NSOrderedAscending;
else if(depth1<depth2) return NSOrderedDescending;
NSNumber *resnum1=[dict1 objectForKey:XADIsResourceForkKey];
NSNumber *resnum2=[dict2 objectForKey:XADIsResourceForkKey];
BOOL isres1=resnum1&&[resnum1 boolValue];
BOOL isres2=resnum2&&[resnum2 boolValue];
if(!isres1&&isres2) return NSOrderedAscending;
else if(isres1&&!isres2) return NSOrderedDescending;
return NSOrderedSame;
}
-(XADError)finishExtractions
{
XADError error;
error=[self _fixDeferredLinks];
if(error) return error;
error=[self _fixDeferredDirectories];
if(error) return error;
return XADNoError;
}
-(XADError)_fixDeferredLinks
{
NSEnumerator *enumerator=[deferredlinks objectEnumerator];
NSArray *entry;
while((entry=[enumerator nextObject]))
{
NSString *path=[entry objectAtIndex:0];
NSString *linkdest=[entry objectAtIndex:1];
NSDictionary *dict=[entry objectAtIndex:2];
XADError error;
error=[XADPlatform createLinkAtPath:path withDestinationPath:linkdest];
if(error) return error;
error=[self _updateFileAttributesAtPath:path forEntryWithDictionary:dict deferDirectories:NO];
if(error) return error;
}
[deferredlinks removeAllObjects];
return XADNoError;
}
-(XADError)_fixDeferredDirectories
{
[deferreddirectories sortUsingFunction:SortDirectoriesByDepthAndResource context:NULL];
NSEnumerator *enumerator=[deferreddirectories objectEnumerator];
NSArray *entry;
while((entry=[enumerator nextObject]))
{
NSString *path=[entry objectAtIndex:0];
NSDictionary *dict=[entry objectAtIndex:1];
XADError error=[self _updateFileAttributesAtPath:path forEntryWithDictionary:dict deferDirectories:NO];
if(error) return error;
}
[deferreddirectories removeAllObjects];
return XADNoError;
}
-(XADUnarchiver *)unarchiverForEntryWithDictionary:(NSDictionary *)dict
wantChecksum:(BOOL)checksum error:(XADError *)errorptr
{
return [self unarchiverForEntryWithDictionary:dict resourceForkDictionary:nil
wantChecksum:checksum error:errorptr];
}
-(XADUnarchiver *)unarchiverForEntryWithDictionary:(NSDictionary *)dict
resourceForkDictionary:(NSDictionary *)forkdict wantChecksum:(BOOL)checksum error:(XADError *)errorptr
{
XADArchiveParser *subparser=[XADArchiveParser
archiveParserForEntryWithDictionary:dict
resourceForkDictionary:forkdict
archiveParser:parser wantChecksum:checksum error:errorptr];
if(!subparser) return nil;
XADUnarchiver *subunarchiver=[XADUnarchiver unarchiverForArchiveParser:subparser];
[subunarchiver setDelegate:delegate];
[subunarchiver setDestination:destination];
[subunarchiver setMacResourceForkStyle:forkstyle];
[subunarchiver setPreserevesPermissions:preservepermissions];
[subunarchiver setUpdateInterval:updateinterval];
return subunarchiver;
}
-(XADError)_extractFileEntryWithDictionary:(NSDictionary *)dict as:(NSString *)destpath
{
CSHandle *fh;
@try { fh=[CSFileHandle fileHandleForWritingAtPath:destpath]; }
@catch(id e) { return XADOpenFileError; }
XADError err=[self runExtractorWithDictionary:dict outputHandle:fh];
[fh close];
return err;
}
-(XADError)_extractDirectoryEntryWithDictionary:(NSDictionary *)dict as:(NSString *)destpath
{
return [self _ensureDirectoryExists:destpath];
}
-(XADError)_extractLinkEntryWithDictionary:(NSDictionary *)dict as:(NSString *)destpath
{
XADError error;
XADString *link=[parser linkDestinationForDictionary:dict error:&error];
if(!link) return error;
NSString *linkdest=nil;
if(delegate) linkdest=[delegate unarchiver:self destinationForLink:link from:destpath];
// linkdest can be empty or nil if the link points to a deleted file.
// linkdest must have a value to be used in the fileSystemRepresentation, otherwise it will crash.
if(!linkdest || linkdest.length == 0) return XADNoError; // Handle nil returns as a request to skip.
// Check if the link destination is an absolute path, or if it contains
// any .. path components.
if([linkdest hasPrefix:@"/"] || [linkdest isEqual:@".."] ||
[linkdest hasPrefix:@"../"] || [linkdest hasSuffix:@"/.."] ||
[linkdest rangeOfString:@"/../"].location!=NSNotFound)
{
// If so, consider it unsafe, and create a placeholder file instead,
// and create the real link only in finishExtractions.
CSHandle *fh;
@try { fh=[CSFileHandle fileHandleForWritingAtPath:destpath]; }
@catch(id e)
{
unlink([destpath fileSystemRepresentation]);
@try { fh=[CSFileHandle fileHandleForWritingAtPath:destpath]; }
@catch(id e) { return XADOpenFileError; }
}
[fh close];
[deferredlinks addObject:[NSArray arrayWithObjects:destpath,linkdest,dict,nil]];
return XADNoError;
}
else
{
return [XADPlatform createLinkAtPath:destpath withDestinationPath:linkdest];
}
}
-(XADError)_extractArchiveEntryWithDictionary:(NSDictionary *)dict to:(NSString *)destpath name:(NSString *)filename
{
XADError error;
XADUnarchiver *subunarchiver=[self unarchiverForEntryWithDictionary:dict
wantChecksum:YES error:&error];
if(!subunarchiver)
{
if(error) return error;
else return XADSubArchiveError;
}
[subunarchiver setDestination:destpath];
[delegate unarchiver:self willExtractArchiveEntryWithDictionary:dict
withUnarchiver:subunarchiver to:destpath];
error=[subunarchiver parseAndUnarchive];
[delegate unarchiver:self didExtractArchiveEntryWithDictionary:dict
withUnarchiver:subunarchiver to:destpath error:error];
return error;
}
-(XADError)_extractResourceForkEntryWithDictionary:(NSDictionary *)dict asAppleDoubleFile:(NSString *)destpath
{
CSHandle *fh;
@try { fh=[CSFileHandle fileHandleForWritingAtPath:destpath]; }
@catch(id e) { return XADOpenFileError; }
off_t ressize=0;
NSNumber *sizenum=[dict objectForKey:XADFileSizeKey];
if(sizenum) ressize=[sizenum longLongValue];
NSDictionary *extattrs=[parser extendedAttributesForDictionary:dict];
@try
{
// TODO: Should this function handle exceptions itself?
[XADAppleDouble writeAppleDoubleHeaderToHandle:fh resourceForkSize:(int)ressize
extendedAttributes:extattrs];
}
@catch(id e) { return [XADException parseException:e]; }
// Write resource fork.
XADError error=XADNoError;
if(ressize) error=[self runExtractorWithDictionary:dict outputHandle:fh];
[fh close];
return error;
}
-(XADError)_updateFileAttributesAtPath:(NSString *)path forEntryWithDictionary:(NSDictionary *)dict
deferDirectories:(BOOL)defer
{
if(defer)
{
NSNumber *dirnum=[dict objectForKey:XADIsDirectoryKey];
if(dirnum&&[dirnum boolValue])
{
[deferreddirectories addObject:[NSArray arrayWithObjects:path,dict,nil]];
return XADNoError;
}
}
return [XADPlatform updateFileAttributesAtPath:path forEntryWithDictionary:dict
parser:parser preservePermissions:preservepermissions];
}
-(XADError)_ensureDirectoryExists:(NSString *)path
{
if([path length]==0) return XADNoError;
NSFileManager *manager=[NSFileManager defaultManager];
BOOL isdir;
if([manager fileExistsAtPath:path isDirectory:&isdir])
{
if(isdir) return XADNoError;
if(!delegate) return XADMakeDirectoryError;
if(![delegate unarchiver:self shouldDeleteFileAndCreateDirectory:path]) return XADMakeDirectoryError;
if(![XADPlatform removeItemAtPath:path]) return XADMakeDirectoryError;
}
else
{
XADError error=[self _ensureDirectoryExists:[path stringByDeletingLastPathComponent]];
if(error) return error;
if(delegate)
{
if(![delegate unarchiver:self shouldCreateDirectory:path]) return XADMakeDirectoryError;
}
}
#if MAC_OS_X_VERSION_MIN_REQUIRED>=1050 || __IPHONE_OS_VERSION_MIN_REQUIRED>__IPHONE_2_0
if([manager createDirectoryAtPath:path
withIntermediateDirectories:NO attributes:nil error:NULL]) {
if (delegate) {
[delegate unarchiver:self didCreateDirectory:path];
}
return XADNoError;
}
#else
if([manager createDirectoryAtPath:path attributes:nil]) {
if (delegate) {
[delegate unarchiver:self didCreateDirectory:path];
}
return XADNoError;
}
#endif
else return XADMakeDirectoryError;
}
-(XADError)runExtractorWithDictionary:(NSDictionary *)dict outputHandle:(CSHandle *)handle
{
return [self runExtractorWithDictionary:dict outputTarget:self
selector:@selector(_outputToHandle:bytes:length:) argument:handle];
}
-(XADError)_outputToHandle:(CSHandle *)handle bytes:(uint8_t *)bytes length:(int)length
{
// TODO: combine the exception parsing for input and output
@try { [handle writeBytes:length fromBuffer:bytes]; }
@catch(id e) { return XADOutputError; }
return XADNoError;
}
-(XADError)runExtractorWithDictionary:(NSDictionary *)dict
outputTarget:(id)target selector:(SEL)selector argument:(id)argument
{
XADError (*outputfunc)(id,SEL,id,uint8_t *,int);
outputfunc=(void *)[target methodForSelector:selector];
uint8_t *buf=NULL;
@try
{
// Send a progress report to show that we are starting.
[delegate unarchiver:self extractionProgressForEntryWithDictionary:dict
fileFraction:0 estimatedTotalFraction:[[parser handle] estimatedProgress]];
// Try to find the size of this entry.
NSNumber *sizenum=[dict objectForKey:XADFileSizeKey];
off_t size=0;
if(sizenum)
{
size=[sizenum longLongValue];
// If this file is empty, don't bother reading anything, just
// call the output function once with 0 bytes and return.
if(size==0) return outputfunc(target,selector,argument,(uint8_t *)"",0);
}
// Create handle and start unpacking.
CSHandle *srchandle=[parser handleForEntryWithDictionary:dict wantChecksum:YES];
if(!srchandle) return XADNotSupportedError;
off_t done=0;
double updatetime=0;
const int bufsize=0x40000;
buf=malloc(bufsize);
if(!buf) [XADException raiseOutOfMemoryException];
for(;;)
{
if([self _shouldStop]) return XADBreakError;
// Read some data, and send it to the output function.
// Stop if no more data was available.
int actual=[srchandle readAtMost:bufsize toBuffer:buf];
if(actual)
{
XADError error=outputfunc(target,selector,argument,buf,actual);
if(error) return error;
}
else break;
done+=actual;
// Occasionally, send a progress message.
double currtime=[XADPlatform currentTimeInSeconds];
if(currtime-updatetime>updateinterval)
{
updatetime=currtime;
double progress;
if(sizenum) progress=(double)done/(double)size;
else progress=[srchandle estimatedProgress];
[delegate unarchiver:self extractionProgressForEntryWithDictionary:dict
fileFraction:progress estimatedTotalFraction:[[parser handle] estimatedProgress]];
}
}
// Check if the file has already been marked as corrupt, and
// give up without testing checksum if so.
NSNumber *iscorrupt=[dict objectForKey:XADIsCorruptedKey];
if(iscorrupt&&[iscorrupt boolValue]) return XADDecrunchError;
// If the file has a checksum, check it. Otherwise, if it has a
// size, check that the size ended up correct.
if([srchandle hasChecksum])
{
if(![srchandle isChecksumCorrect]) return XADChecksumError;
}
else
{
if(sizenum&&done!=size) return XADDecrunchError; // kind of hacky
}
// Send a final progress report.
[delegate unarchiver:self extractionProgressForEntryWithDictionary:dict
fileFraction:1 estimatedTotalFraction:[[parser handle] estimatedProgress]];
}
@catch(id e)
{
return [XADException parseException:e];
}
free(buf);
return XADNoError;
}
-(NSString *)adjustPathString:(NSString *)path forEntryWithDictionary:(NSDictionary *)dict
{
// If we are unpacking a resource fork, we may need to modify the path.
NSNumber *resnum=[dict objectForKey:XADIsResourceForkKey];
if(resnum&&[resnum boolValue])
{
switch(forkstyle)
{
case XADHiddenAppleDoubleForkStyle:
// TODO: is this path generation correct?
return [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:
[@"._" stringByAppendingString:[path lastPathComponent]]];
break;
case XADVisibleAppleDoubleForkStyle:
return [path stringByAppendingPathExtension:@"rsrc"];
break;
case XADHFVExplorerAppleDoubleForkStyle:
// TODO: is this path generation correct?
return [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:
[@"%" stringByAppendingString:[path lastPathComponent]]];
break;
}
}
return path;
}
-(BOOL)_shouldStop
{
if(!delegate) return NO;
if(shouldstop) return YES;
return shouldstop=[delegate extractionShouldStopForUnarchiver:self];
}
@end
@implementation NSObject (XADUnarchiverDelegate)
-(void)unarchiverNeedsPassword:(XADUnarchiver *)unarchiver {}
-(NSString *)unarchiver:(XADUnarchiver *)unarchiver pathForExtractingEntryWithDictionary:(NSDictionary *)dict { return nil; }
-(BOOL)unarchiver:(XADUnarchiver *)unarchiver shouldExtractEntryWithDictionary:(NSDictionary *)dict suggestedPath:(NSString **)pathptr
{
// Kludge to handle old-style interface.
if([self respondsToSelector:@selector(unarchiver:shouldExtractEntryWithDictionary:to:)])
{
NSString *path=[self unarchiver:unarchiver pathForExtractingEntryWithDictionary:dict];
if(path) *pathptr=path;
return [self unarchiver:unarchiver shouldExtractEntryWithDictionary:dict to:*pathptr];
}
else return YES;
}
-(void)unarchiver:(XADUnarchiver *)unarchiver willExtractEntryWithDictionary:(NSDictionary *)dict to:(NSString *)path {}
-(void)unarchiver:(XADUnarchiver *)unarchiver didExtractEntryWithDictionary:(NSDictionary *)dict to:(NSString *)path error:(XADError)error {}
-(BOOL)unarchiver:(XADUnarchiver *)unarchiver shouldCreateDirectory:(NSString *)directory { return YES; }
-(void)unarchiver:(XADUnarchiver *)unarchiver didCreateDirectory:(NSString *)directory { }
-(BOOL)unarchiver:(XADUnarchiver *)unarchiver shouldDeleteFileAndCreateDirectory:(NSString *)directory { return NO; }
-(BOOL)unarchiver:(XADUnarchiver *)unarchiver shouldExtractArchiveEntryWithDictionary:(NSDictionary *)dict to:(NSString *)path { return NO; }
-(void)unarchiver:(XADUnarchiver *)unarchiver willExtractArchiveEntryWithDictionary:(NSDictionary *)dict withUnarchiver:(XADUnarchiver *)subunarchiver to:(NSString *)path {}
-(void)unarchiver:(XADUnarchiver *)unarchiver didExtractArchiveEntryWithDictionary:(NSDictionary *)dict withUnarchiver:(XADUnarchiver *)subunarchiver to:(NSString *)path error:(XADError)error {}
-(NSString *)unarchiver:(XADUnarchiver *)unarchiver destinationForLink:(XADString *)link from:(NSString *)path
{
// Kludge to handle old-style interface.
if([self respondsToSelector:@selector(unarchiver:linkDestinationForEntryWithDictionary:from:)])
{
return [self unarchiver:unarchiver linkDestinationForEntryWithDictionary:
[NSMutableDictionary dictionaryWithObjectsAndKeys:
link,XADLinkDestinationKey,
[NSNumber numberWithBool:YES],XADIsLinkKey,
nil] from:path];
}
else return [link string];
}
-(BOOL)extractionShouldStopForUnarchiver:(XADUnarchiver *)unarchiver { return NO; }
-(void)unarchiver:(XADUnarchiver *)unarchiver extractionProgressForEntryWithDictionary:(NSDictionary *)dict
fileFraction:(double)fileprogress estimatedTotalFraction:(double)totalprogress {}
-(void)unarchiver:(XADUnarchiver *)unarchiver findsFileInterestingForReason:(NSString *)reason {}
@end