1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
#import "XADStuffItSplitParser.h"
#import "CSMultiHandle.h"
#import "CSFileHandle.h"
#import "NSDateXAD.h"
@implementation XADStuffItSplitParser
+(int)requiredHeaderSize { return 100; }
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
{
const uint8_t *bytes=[data bytes];
int length=[data length];
if(length<100) return NO;
if(bytes[0]!=0xb0) return NO;
if(bytes[1]!=0x56) return NO;
if(bytes[2]!=0x00) return NO; // Assume there are less than 256 parts.
if(bytes[4]==0) return NO;
if(bytes[4]>63) return NO;
for(int i=0;i<bytes[4];i++) if(bytes[5+i]==0) return NO;
return YES;
}
+(NSArray *)volumesForHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
{
const uint8_t *bytes=[data bytes];
NSString *basename=[[name lastPathComponent] stringByDeletingPathExtension];
NSString *dirname=[name stringByDeletingLastPathComponent];
#if MAC_OS_X_VERSION_MIN_REQUIRED>=1050
NSArray *dircontents=[[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirname error:NULL];
#else
NSArray *dircontents=[[NSFileManager defaultManager] directoryContentsAtPath:dirname];
#endif
NSString *parts[256]={nil};
NSEnumerator *enumerator=[dircontents objectEnumerator];
NSString *filename;
while((filename=[enumerator nextObject]))
{
if(![filename hasPrefix:basename]) continue;
NSString *fullpath=[dirname stringByAppendingPathComponent:filename];
@try
{
CSFileHandle *filehandle=[CSFileHandle fileHandleForReadingAtPath:fullpath];
uint8_t header[100];
int actual=[filehandle readAtMost:sizeof(header) toBuffer:header];
if(actual<sizeof(header)) continue;
if(header[0]!=0xb0) continue;
if(header[1]!=0x56) continue;
if(header[2]!=0x00) continue;
if(header[4]!=bytes[4]) continue;
if(memcmp(&header[5],&bytes[5],header[4])!=0) continue;
if(memcmp(&header[68],&bytes[68],26)!=0) continue;
int partnum=header[3];
parts[partnum]=fullpath;
[filehandle close];
}
@catch(id e) {}
}
NSMutableArray *volumes=[NSMutableArray array];
for(int i=1;i<256;i++)
{
if(!parts[i]) break;
[volumes addObject:parts[i]];
}
return volumes;
}
-(void)parse
{
CSHandle *fh=[self handle];
NSArray *handles=[self volumes];
if(!handles) handles=[NSArray arrayWithObject:fh];
XADSkipHandle *sh=[self skipHandle];
off_t curroffset=0;
NSEnumerator *enumerator=[handles objectEnumerator];
CSHandle *handle;
while((handle=[enumerator nextObject]))
{
[sh addSkipFrom:curroffset length:100];
off_t volumesize=[handle fileSize];
curroffset+=volumesize;
}
[fh skipBytes:4];
int namelength=[fh readUInt8];
NSData *namedata=[fh readDataOfLength:namelength];
[fh seekToFileOffset:68];
uint32_t type=[fh readUInt32BE];
uint32_t creator=[fh readUInt32BE];
int finderflags=[fh readUInt16BE];
uint32_t creation=[fh readUInt32BE];
uint32_t modification=[fh readUInt32BE];
uint32_t rsrclength=[fh readUInt32BE];
uint32_t datalength=[fh readUInt32BE];
BOOL isarchive=NO;
const uint8_t *namebytes=[namedata bytes];
if(namelength>4)
if(namebytes[namelength-4]=='.')
if(namebytes[namelength-3]=='s'||namebytes[namelength-3]=='S')
if(namebytes[namelength-2]=='i'||namebytes[namelength-2]=='I')
if(namebytes[namelength-1]=='t'||namebytes[namelength-1]=='T')
isarchive=YES;
if(namelength>4)
if(namebytes[namelength-4]=='.')
if(namebytes[namelength-3]=='s'||namebytes[namelength-3]=='S')
if(namebytes[namelength-2]=='e'||namebytes[namelength-2]=='E')
if(namebytes[namelength-1]=='a'||namebytes[namelength-1]=='A')
isarchive=YES;
if(rsrclength)
{
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
[self XADPathWithData:namedata separators:XADNoPathSeparator],XADFileNameKey,
[NSNumber numberWithLongLong:rsrclength],XADFileSizeKey,
[NSNumber numberWithLongLong:curroffset*rsrclength/(datalength+rsrclength)],XADCompressedSizeKey,
[NSNumber numberWithLongLong:0],XADSkipOffsetKey,
[NSNumber numberWithLongLong:rsrclength],XADSkipLengthKey,
[NSNumber numberWithUnsignedInt:type],XADFileTypeKey,
[NSNumber numberWithUnsignedInt:creator],XADFileCreatorKey,
[NSNumber numberWithInt:finderflags],XADFinderFlagsKey,
[NSDate XADDateWithTimeIntervalSince1904:creation],XADCreationDateKey,
[NSDate XADDateWithTimeIntervalSince1904:modification],XADLastModificationDateKey,
[NSNumber numberWithBool:YES],XADIsResourceForkKey,
nil];
if(isarchive) [dict setObject:[NSNumber numberWithBool:YES] forKey:XADIsArchiveKey];
[self addEntryWithDictionary:dict];
}
if(datalength || !rsrclength)
{
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
[self XADPathWithData:namedata separators:XADNoPathSeparator],XADFileNameKey,
[NSNumber numberWithLongLong:datalength],XADFileSizeKey,
[NSNumber numberWithLongLong:curroffset*datalength/(datalength+rsrclength)],XADCompressedSizeKey,
[NSNumber numberWithLongLong:rsrclength],XADSkipOffsetKey,
[NSNumber numberWithLongLong:datalength],XADSkipLengthKey,
[NSNumber numberWithUnsignedInt:type],XADFileTypeKey,
[NSNumber numberWithUnsignedInt:creator],XADFileCreatorKey,
[NSNumber numberWithInt:finderflags],XADFinderFlagsKey,
[NSDate XADDateWithTimeIntervalSince1904:creation],XADCreationDateKey,
[NSDate XADDateWithTimeIntervalSince1904:modification],XADLastModificationDateKey,
nil];
if(isarchive) [dict setObject:[NSNumber numberWithBool:YES] forKey:XADIsArchiveKey];
[self addEntryWithDictionary:dict];
}
}
-(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
{
CSHandle *handle=[self handleAtDataOffsetForDictionary:dict];
return handle;
}
-(NSString *)formatName { return @"StuffIt split file"; }
@end
|