diff --git a/XADARCParser.h b/XADARCParser.h index 46e4a0c3..5a70044b 100644 --- a/XADARCParser.h +++ b/XADARCParser.h @@ -22,12 +22,14 @@ @interface XADARCParser:XADArchiveParser { + BOOL isARC7; } +(int)requiredHeaderSize; +(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name; +-(id)init; -(void)parse; -(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum; -(NSString *)formatName; diff --git a/XADARCParser.m b/XADARCParser.m index d816fe17..44b874e7 100644 --- a/XADARCParser.m +++ b/XADARCParser.m @@ -22,6 +22,7 @@ #import "XADARCCrunchHandle.h" #import "XADARCCrushHandle.h" #import "XADARCDistillHandle.h" +#import "XADLZHDynamicHandle.h" #import "XADRLE90Handle.h" #import "XADSqueezeHandle.h" #import "XADCompressHandle.h" @@ -93,6 +94,28 @@ static BOOL IsARCHeader(const uint8_t *bytes,int length,BOOL acceptloader) } } +static BOOL IsARC7Header(const uint8_t *bytes,int length) +{ + if(length<0x1d) return NO; + + // Check ID. + if(bytes[0x00]!=0x1a) return NO; + + // Method == 20 + if(bytes[0x01]!=0x14) return NO; + + // Name is an empty string + if(bytes[0x02]!=0) return NO; + + // Uncompressed size == 0 + if(bytes[25]!=0) return NO; + if(bytes[26]!=0) return NO; + if(bytes[27]!=0) return NO; + if(bytes[28]!=0) return NO; + + return YES; +} + @implementation XADARCParser +(int)requiredHeaderSize { return 0x1d; } @@ -103,7 +126,16 @@ +(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data const uint8_t *bytes=[data bytes]; int length=[data length]; - return IsARCHeader(bytes,length,NO); + return IsARC7Header(bytes,length) || IsARCHeader(bytes,length,NO); +} + +-(id)init +{ + if((self=[super init])) + { + isARC7 = NO; + } + return self; } -(void)parse @@ -177,6 +209,18 @@ -(void)parse parent=path; } + else if (method==0x14) + { + // Generated by SEA ARC 7; method 10 is Trim + if (namebuf[0]==0 && uncompsize==0) + isARC7 = YES; + [fh seekToFileOffset:dataoffset+compsize]; + } + else if (method==0x15) + { + // SEA ARC file comment + [fh seekToFileOffset:dataoffset+compsize]; + } else { NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys: @@ -202,7 +246,12 @@ -(void)parse case 0x07: methodname=@"Crunched (fast)"; break; case 0x08: methodname=@"Crunched (LZW)"; break; case 0x09: methodname=@"Squashed"; break; - case 0x0a: methodname=@"Crushed"; break; + case 0x0a: + if (isARC7) + methodname=@"Trimmed"; + else + methodname=@"Crushed"; + break; case 0x0b: methodname=@"Distilled"; break; case 0x7f: methodname=@"Compressed"; break; } @@ -298,9 +347,17 @@ -(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOO length:length flags:0x8d] autorelease]; break; - case 0x0a: // Crushed - handle=[[[XADARCCrushHandle alloc] initWithHandle:handle] autorelease]; - + case 0x0a: + if (isARC7) // Trimmed + // Pass 0xffffffff as the length because some files get + // truncated if we pass the actual length. We need to rely on + // the stop code in the compressed data. + handle=[[[XADLZHDynamicHandle alloc] initWithHandle:handle length:0xffffffff + hasStopCode:YES] autorelease]; + else // Crushed + handle=[[[XADARCCrushHandle alloc] initWithHandle:handle] autorelease]; + + // Both Trim and Crush use RLE90 on output handle=[[[XADRLE90Handle alloc] initWithHandle:handle length:length] autorelease]; break; @@ -381,7 +438,7 @@ +(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data { for(int i=2;i<=length-0x1d /*&& i<0x10000-0x1d*/;i++) { - if(IsARCHeader(&bytes[i],length-i,NO)) + if(IsARC7Header(&bytes[i],length-i) || IsARCHeader(&bytes[i],length-i,NO)) { [props setObject:[NSNumber numberWithInt:i] forKey:@"ARCSFXOffset"]; return YES; diff --git a/XADLZHDynamicHandle.h b/XADLZHDynamicHandle.h index 5105e65d..191aa4f6 100644 --- a/XADLZHDynamicHandle.h +++ b/XADLZHDynamicHandle.h @@ -33,9 +33,11 @@ struct XADLZHDynamicNode { XADPrefixCode *distancecode; XADLZHDynamicNode *nodes[314*2-1],nodestorage[314*2-1]; + BOOL hasstopcode; } -(id)initWithHandle:(CSHandle *)handle length:(off_t)length; +-(id)initWithHandle:(CSHandle *)handle length:(off_t)length hasStopCode:(BOOL)hasStopCode; -(void)dealloc; -(void)resetLZSSHandle; diff --git a/XADLZHDynamicHandle.m b/XADLZHDynamicHandle.m index 9501bcd8..e93ccdd6 100644 --- a/XADLZHDynamicHandle.m +++ b/XADLZHDynamicHandle.m @@ -24,6 +24,11 @@ @implementation XADLZHDynamicHandle -(id)initWithHandle:(CSHandle *)handle length:(off_t)length +{ + return [self initWithHandle:handle length:length hasStopCode:NO]; +} + +-(id)initWithHandle:(CSHandle *)handle length:(off_t)length hasStopCode:(BOOL)hasStopCode { if((self=[super initWithInputBufferForHandle:handle length:length windowSize:4096])) { @@ -37,6 +42,7 @@ -(id)initWithHandle:(CSHandle *)handle length:(off_t)length distancecode=[[XADPrefixCode alloc] initWithLengths:lengths numberOfSymbols:64 maximumLength:8 shortestCodeIsZeros:YES]; + hasstopcode=hasStopCode; } return self; } @@ -96,9 +102,13 @@ -(int)nextLiteralOrOffset:(int *)offset andLength:(int *)length atPosition:(off_ int lit=node->value; if(lit<0x100) return lit; + else if (lit==0x100 && hasstopcode) + return XADLZSSEnd; else { *length=lit-0x100+3; + if (hasstopcode) + --(*length); int highbits=CSInputNextSymbolUsingCode(input,distancecode); int lowbits=CSInputNextBitString(input,6);