XADMaster/XADLZXParser.m
2018-03-21 14:24:50 +02:00

197 lines
6.1 KiB
Objective-C

/*
* XADLZXParser.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 "XADLZXParser.h"
#import "XADLZXHandle.h"
#import "XADCRCHandle.h"
#import "NSDateXAD.h"
@implementation XADLZXParser
+(int)requiredHeaderSize { return 10; }
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
{
const uint8_t *bytes=[data bytes];
int length=[data length];
return length>=10&&bytes[0]=='L'&&bytes[1]=='Z'&&bytes[2]=='X';
}
-(void)parse
{
CSHandle *fh=[self handle];
[fh skipBytes:10];
NSMutableArray *solidfiles=[NSMutableArray array];
off_t solidsize=0;
while([self shouldKeepParsing])
{
int attributes;
@try { attributes=[fh readUInt16LE]; }
@catch(id e) { break; }
uint32_t filesize=[fh readUInt32LE];
uint32_t compsize=[fh readUInt32LE];
int os=[fh readUInt8];
int method=[fh readUInt8];
int flags=[fh readUInt16LE];
int commentlen=[fh readUInt8];
int version=[fh readUInt8];
[fh skipBytes:2];
uint32_t date=[fh readUInt32LE];
uint32_t datacrc=[fh readUInt32LE];
/*uint32_t headercrc=*/[fh readUInt32LE];
int namelen=[fh readUInt8];
NSData *namedata=[fh readDataOfLength:namelen];
NSData *commentdata=nil;
if(commentlen) commentdata=[fh readDataOfLength:commentlen];
off_t dataoffs=[fh offsetInFile];
int day=(date>>27)&31;
int month=((date>>23)&15)+1;
int year=((date>>17)&63)+1970;
int hour=(date>>12)&31;
int minute=(date>>6)&63;
int second=(date&63);
// From libxad LZX:
if(year>=2028) year+=2000-2028; // Original LZX
else if(year<1978) year+=2034-1970; // Dr.Titus
// Dates from 1978 to 1999 are correct
// Dates from 2000 to 2027 Mikolaj patch are correct
// Dates from 2000 to 2005 LZX/Dr.Titus patch are correct
// Dates from 2034 to 2041 Dr.Titus patch are correct
NSDate *dateobj=[NSDate XADDateWithYear:year month:month day:day hour:hour minute:minute second:second timeZone:nil];
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
[self XADPathWithData:namedata separators:XADUnixPathSeparator],XADFileNameKey,
[NSNumber numberWithUnsignedLong:filesize],XADFileSizeKey,
[NSNumber numberWithUnsignedLong:filesize],XADSolidLengthKey,
[NSNumber numberWithLongLong:solidsize],XADSolidOffsetKey,
//[NSNumber numberWithUnsignedLong:compsize],XADCompressedSizeKey,
dateobj,XADLastModificationDateKey,
[NSNumber numberWithInt:os],@"LZXOS",
[NSNumber numberWithInt:method],@"LZXMethod",
[NSNumber numberWithInt:flags],@"LZXFlags",
[NSNumber numberWithInt:version],@"LZXVersion",
[NSNumber numberWithInt:datacrc],@"LZXCRC32",
nil];
NSString *methodname=nil;
switch(method)
{
case 0: methodname=@"None"; break;
case 2: methodname=@"LZX"; break;
}
if(methodname) [dict setObject:[self XADStringWithString:methodname] forKey:XADCompressionNameKey];
NSString *osname=nil;
switch(os)
{
case 0: osname=@"MSDOS"; break;
case 1: osname=@"Windows"; break;
case 2: osname=@"OS/2"; break;
case 10: osname=@"Amiga"; break;
case 20: osname=@"Unix"; break;
}
if(osname) [dict setObject:[self XADStringWithString:osname] forKey:@"LZXOSName"];
if(os==10)
{
// Decode Amiga protection bits
int prot=0;
if(!(attributes&0x01)) prot|=0x08; // Read
if(!(attributes&0x02)) prot|=0x04; // Write
if(!(attributes&0x04)) prot|=0x01; // Delete
if(!(attributes&0x08)) prot|=0x02; // Execute
if(attributes&0x10) prot|=0x10; // Archive
if(attributes&0x20) prot|=0x80; // Hold
if(attributes&0x40) prot|=0x40; // Script
if(attributes&0x80) prot|=0x20; // Pure
[dict setObject:[NSNumber numberWithInt:prot] forKey:XADAmigaProtectionBitsKey];
}
[solidfiles addObject:dict];
solidsize+=filesize;
if(compsize)
{
NSMutableDictionary *solidobj=[NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:compsize],XADDataLengthKey,
[NSNumber numberWithLongLong:dataoffs],XADDataOffsetKey,
[NSNumber numberWithLongLong:solidsize],@"TotalSize",
[NSNumber numberWithInt:method],@"Method",
nil];
NSEnumerator *enumerator=[solidfiles objectEnumerator];
NSMutableDictionary *dict;
while((dict=[enumerator nextObject]))
{
off_t compsize=0;
if(solidsize!=0) compsize=([[dict objectForKey:XADFileSizeKey] longLongValue]*(off_t)compsize)/solidsize;
[dict setObject:solidobj forKey:XADSolidObjectKey];
[dict setObject:[NSNumber numberWithLongLong:compsize] forKey:XADCompressedSizeKey];
[self addEntryWithDictionary:dict];
}
[solidfiles removeAllObjects];
solidsize=0;
}
[fh seekToFileOffset:dataoffs+compsize];
}
}
-(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
{
CSHandle *handle=[self subHandleFromSolidStreamForEntryWithDictionary:dict];
if(checksum) handle=[XADCRCHandle IEEECRC32HandleWithHandle:handle length:[handle fileSize]
correctCRC:[[dict objectForKey:@"LZXCRC32"] unsignedIntValue] conditioned:YES];
return handle;
}
-(CSHandle *)handleForSolidStreamWithObject:(id)obj wantChecksum:(BOOL)checksum
{
CSHandle *handle=[self handleAtDataOffsetForDictionary:obj];
off_t length=[[obj objectForKey:@"TotalSize"] longLongValue];
int method=[[obj objectForKey:@"Method"] intValue];
switch(method)
{
case 0: return handle;
case 2: return [[[XADLZXHandle alloc] initWithHandle:handle length:length] autorelease];
default: return nil;
}
}
-(NSString *)formatName { return @"LZX"; }
@end