mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-28 19:13:49 +02:00
212 lines
6.4 KiB
Objective-C
212 lines
6.4 KiB
Objective-C
/*
|
|
* XADALZipParser.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 "XADALZipParser.h"
|
|
#import "CSZlibHandle.h"
|
|
#import "CSBzip2Handle.h"
|
|
#import "XADDeflateHandle.h"
|
|
#import "XADCRCHandle.h"
|
|
#import "XADRegex.h"
|
|
#import "NSDateXAD.h"
|
|
|
|
static off_t ParseNumber(CSHandle *handle,int size)
|
|
{
|
|
switch(size)
|
|
{
|
|
case 1: return [handle readUInt8];
|
|
case 2: return [handle readUInt16LE];
|
|
case 4: return [handle readUInt32LE];
|
|
case 8: return [handle readUInt64LE];
|
|
default: [XADException raiseIllegalDataException];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void CalculateSillyTable(int *table,int param)
|
|
{
|
|
for(int i=0;i<19;i++) table[i]=i;
|
|
for(int i=0;i<19;i++)
|
|
{
|
|
int swapindex=(i%6)*3+param;
|
|
if(swapindex>18) swapindex%=18;
|
|
if(swapindex!=i)
|
|
{
|
|
int tmp=table[i];
|
|
table[i]=table[swapindex];
|
|
table[swapindex]=tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
@implementation XADALZipParser
|
|
|
|
+(int)requiredHeaderSize { return 8; }
|
|
|
|
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
|
|
{
|
|
const uint8_t *bytes=[data bytes];
|
|
int length=[data length];
|
|
|
|
return length>=8&&bytes[0]=='A'&&bytes[1]=='L'&&bytes[2]=='Z'&&bytes[3]==1&&bytes[7]==0;
|
|
}
|
|
|
|
+(NSArray *)volumesForHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
|
|
{
|
|
NSArray *matches;
|
|
|
|
if((matches=[name substringsCapturedByPattern:@"^(.*)\\.(alz|a[0-9]{2}|b[0-9]{2})$" options:REG_ICASE]))
|
|
{
|
|
return [self scanForVolumesWithFilename:name
|
|
regex:[XADRegex regexWithPattern:[NSString stringWithFormat:@"^%@\\.(alz|a[0-9]{2}|b[0-9]{2})$",
|
|
[[matches objectAtIndex:1] escapedPattern]] options:REG_ICASE]
|
|
firstFileExtension:@"alz"];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
-(void)parse
|
|
{
|
|
XADSkipHandle *fh=[self skipHandle];
|
|
|
|
NSArray *volumesizes=[self volumeSizes];
|
|
if([volumesizes count]>1)
|
|
{
|
|
NSInteger count=[volumesizes count];
|
|
off_t offs=0;
|
|
for(NSInteger i=0;i<count-1;i++)
|
|
{
|
|
offs+=[[volumesizes objectAtIndex:i] longLongValue];
|
|
[fh addSkipFrom:offs-16 to:offs+8];
|
|
}
|
|
}
|
|
|
|
[fh skipBytes:8];
|
|
|
|
while([self shouldKeepParsing])
|
|
{
|
|
uint32_t signature=[fh readID];
|
|
|
|
if(signature=='BLZ\001')
|
|
{
|
|
int namelen=[fh readUInt16LE];
|
|
int attrs=[fh readUInt8];
|
|
uint32_t dostime=[fh readUInt32LE];
|
|
int flags=[fh readUInt8];
|
|
[fh skipBytes:1];
|
|
|
|
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
[NSDate XADDateWithMSDOSDateTime:dostime],XADLastModificationDateKey,
|
|
[NSNumber numberWithInt:attrs],@"ALZipAttributes",
|
|
[NSNumber numberWithInt:flags],@"ALZipFlags",
|
|
nil];
|
|
|
|
if(attrs&0x10) [dict setObject:[NSNumber numberWithBool:YES] forKey:XADIsDirectoryKey];
|
|
if(flags&0x01) [dict setObject:[NSNumber numberWithBool:YES] forKey:XADIsEncryptedKey];
|
|
|
|
off_t compsize=0;
|
|
|
|
int sizebytes=flags>>4;
|
|
if(sizebytes)
|
|
{
|
|
int method=[fh readUInt8];
|
|
[dict setObject:[NSNumber numberWithInt:method] forKey:@"ALZipCompressionMethod"];
|
|
|
|
NSString *compname=nil;
|
|
switch(method)
|
|
{
|
|
case 0: compname=@"None"; break;
|
|
case 1: compname=@"Bzip2"; break;
|
|
case 2: compname=@"Deflate"; break;
|
|
case 3: compname=@"Obfuscated deflate"; break;
|
|
}
|
|
if(compname) [dict setObject:[self XADStringWithString:compname] forKey:XADCompressionNameKey];
|
|
|
|
[fh skipBytes:1];
|
|
[dict setObject:[NSNumber numberWithUnsignedInt:[fh readUInt32LE]] forKey:@"ALZipCRC32"];
|
|
|
|
compsize=ParseNumber(fh,sizebytes);
|
|
off_t size=ParseNumber(fh,sizebytes);
|
|
|
|
[dict setObject:[NSNumber numberWithLongLong:compsize] forKey:XADCompressedSizeKey];
|
|
[dict setObject:[NSNumber numberWithLongLong:compsize] forKey:XADSkipLengthKey];
|
|
[dict setObject:[NSNumber numberWithLongLong:size] forKey:XADFileSizeKey];
|
|
}
|
|
|
|
// TODO: force korean encoding?
|
|
NSData *namedata=[fh readDataOfLength:namelen];
|
|
[dict setObject:[self XADPathWithData:namedata separators:XADEitherPathSeparator] forKey:XADFileNameKey];
|
|
|
|
[dict setObject:[NSNumber numberWithLongLong:[fh offsetInFile]] forKey:XADSkipOffsetKey];
|
|
|
|
off_t pos=[fh offsetInFile];
|
|
[self addEntryWithDictionary:dict];
|
|
[fh seekToFileOffset:pos+compsize];
|
|
}
|
|
else if(signature=='CLZ\001') break;
|
|
else if(signature=='ELZ\001') break; // give up on comment blocks, which are always at the end anyway
|
|
else [XADException raiseIllegalDataException];
|
|
}
|
|
}
|
|
|
|
-(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
|
|
{
|
|
CSHandle *handle=[self handleAtDataOffsetForDictionary:dict];
|
|
off_t size=[[dict objectForKey:XADFileSizeKey] longLongValue];
|
|
//off_t compsize=[[dict objectForKey:XADCompressedSizeKey] longLongValue];
|
|
uint32_t crc=[[dict objectForKey:@"ALZipCRC32"] unsignedIntValue];
|
|
|
|
if([dict objectForKey:XADIsEncryptedKey])
|
|
{
|
|
// TODO: encryption
|
|
[XADException raiseNotSupportedException];
|
|
/*handle=[[[XADZipCryptHandle alloc] initWithHandle:handle length:compsize
|
|
password:[self encodedPassword] testByte:crc>>24] autorelease];*/
|
|
}
|
|
|
|
int method=[[dict objectForKey:@"ALZipCompressionMethod"] intValue];
|
|
switch(method)
|
|
{
|
|
case 0: break; // No compression
|
|
case 1: handle=[CSBzip2Handle bzip2HandleWithHandle:handle length:size]; break;
|
|
case 2: handle=[CSZlibHandle deflateHandleWithHandle:handle length:size]; break;
|
|
case 3:
|
|
{
|
|
handle=[[[XADDeflateHandle alloc] initWithHandle:handle length:size] autorelease];
|
|
|
|
int order[19];
|
|
CalculateSillyTable(order,[[dict objectForKey:XADFileSizeKey] intValue]%16);
|
|
[(XADDeflateHandle *)handle setMetaTableOrder:order];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
[self reportInterestingFileWithReason:@"Unsupported compression method %d",method];
|
|
return nil;
|
|
}
|
|
|
|
if(checksum) handle=[XADCRCHandle IEEECRC32HandleWithHandle:handle length:size correctCRC:crc conditioned:YES];
|
|
|
|
return handle;
|
|
}
|
|
|
|
-(NSString *)formatName { return @"ALZip"; }
|
|
|
|
@end
|