mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-29 19:43:47 +02:00
314 lines
7.4 KiB
Objective-C
314 lines
7.4 KiB
Objective-C
/*
|
|
* XADXZHandle.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 "XADXZHandle.h"
|
|
#import "XADLZMA2Handle.h"
|
|
#import "XAD7ZipBranchHandles.h"
|
|
#import "XADDeltaHandle.h"
|
|
#import "XADException.h"
|
|
#import "CRC.h"
|
|
#import "Progress.h"
|
|
|
|
|
|
#define StreamHeaderState 0
|
|
#define BlockHeaderState 1
|
|
#define BlockDataState 2
|
|
#define BlockPaddingState 3
|
|
#define BlockChecksumState 4
|
|
#define StreamIndexState 5
|
|
#define StreamFooterState 6
|
|
#define StreamPaddingState 7
|
|
#define EndState 10
|
|
|
|
static uint64_t ParseInteger(CSHandle *fh);
|
|
|
|
@implementation XADXZHandle
|
|
|
|
-(id)initWithHandle:(CSHandle *)handle
|
|
{
|
|
return [self initWithHandle:handle length:CSHandleMaxLength];
|
|
}
|
|
|
|
-(id)initWithHandle:(CSHandle *)handle length:(off_t)length
|
|
{
|
|
if(self=[super initWithParentHandle:handle length:length])
|
|
{
|
|
startoffs=[parent offsetInFile];
|
|
currhandle=nil;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[currhandle release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(void)resetStream
|
|
{
|
|
[parent seekToFileOffset:startoffs];
|
|
|
|
[currhandle release];
|
|
currhandle=nil;
|
|
|
|
state=StreamHeaderState;
|
|
checksumscorrect=YES;
|
|
checksumflags=0;
|
|
}
|
|
|
|
-(int)streamAtMost:(int)num toBuffer:(void *)buffer
|
|
{
|
|
int bytesread=0;
|
|
uint8_t *bytebuf=buffer;
|
|
|
|
while(bytesread<num && state!=EndState) switch(state)
|
|
{
|
|
case StreamHeaderState:
|
|
{
|
|
uint8_t head[6];
|
|
[parent readBytes:6 toBuffer:head];
|
|
if(head[0]!=0xfd||head[1]!='7'||head[2]!='z'||head[3]!='X'||head[4]!='Z'||head[5]!=0)
|
|
[XADException raiseIllegalDataException];
|
|
|
|
int version=[parent readUInt8];
|
|
if(version!=0) [XADException raiseIllegalDataException];
|
|
|
|
checksumflags=[parent readUInt8];
|
|
if(checksumflags&0xf0) [XADException raiseIllegalDataException];
|
|
[parent skipBytes:4]; // skip CRC
|
|
|
|
state=BlockHeaderState;
|
|
}
|
|
break;
|
|
|
|
case BlockHeaderState:
|
|
{
|
|
int blockheadsize=[parent readUInt8];
|
|
if(blockheadsize==0)
|
|
{
|
|
state=StreamIndexState;
|
|
break;
|
|
}
|
|
|
|
off_t streamstart=[parent offsetInFile]+blockheadsize*4+3;
|
|
|
|
int blockflags=[parent readUInt8];
|
|
if(blockflags&0x3c) [XADException raiseIllegalDataException];
|
|
|
|
int numfilters=(blockflags&3)+1;
|
|
|
|
if(blockflags&0x40) ParseInteger(parent);
|
|
if(blockflags&0x80) ParseInteger(parent);
|
|
|
|
uint64_t ids[4];
|
|
NSData *properties[4];
|
|
|
|
for(int i=0;i<numfilters;i++)
|
|
{
|
|
ids[i]=ParseInteger(parent);
|
|
uint64_t size=ParseInteger(parent);
|
|
properties[i]=[parent readDataOfLength:(int)size];
|
|
}
|
|
|
|
[parent seekToFileOffset:streamstart];
|
|
|
|
CSHandle *handle=parent;
|
|
for(int i=numfilters-1;i>=0;i--)
|
|
{
|
|
switch(ids[i])
|
|
{
|
|
case 3: handle=[[[XADDeltaHandle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
|
|
case 4: handle=[[[XAD7ZipBCJHandle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
case 5: handle=[[[XAD7ZipPPCHandle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
case 6: handle=[[[XAD7ZipIA64Handle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
case 7: handle=[[[XAD7ZipARMHandle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
case 8: handle=[[[XAD7ZipThumbHandle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
case 9: handle=[[[XAD7ZipSPARCHandle alloc] initWithHandle:handle propertyData:properties[i]] autorelease]; break;
|
|
|
|
case 33:
|
|
{
|
|
XADLZMA2Handle *lh=[[[XADLZMA2Handle alloc] initWithHandle:handle propertyData:properties[i]] autorelease];
|
|
[lh setSeekBackAtEOF:YES];
|
|
handle=lh;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
currhandle=[handle retain];
|
|
|
|
//currhandle=[[self decompressHandleForHandleAtBlockHeader:parent] retain];
|
|
|
|
switch(checksumflags)
|
|
{
|
|
case 1: crc=0xffffffff; break;
|
|
case 4: crc=0xffffffffffffffff; break;
|
|
}
|
|
|
|
state=BlockDataState;
|
|
}
|
|
break;
|
|
|
|
case BlockDataState:
|
|
{
|
|
int actual=[currhandle readAtMost:num-bytesread toBuffer:&bytebuf[bytesread]];
|
|
|
|
switch(checksumflags)
|
|
{
|
|
case 1:
|
|
crc=XADCalculateCRC((uint32_t)crc,&bytebuf[bytesread],actual,XADCRCTable_edb88320);
|
|
break;
|
|
|
|
case 4:
|
|
crc=XADCalculateCRC64(crc,&bytebuf[bytesread],actual,XADCRCTable_c96c5795d7870f42);
|
|
break;
|
|
}
|
|
|
|
bytesread+=actual;
|
|
|
|
if([currhandle atEndOfFile])
|
|
{
|
|
[currhandle release];
|
|
currhandle=nil;
|
|
state=BlockPaddingState;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BlockPaddingState:
|
|
[parent skipBytes:(-([parent offsetInFile]-startoffs))&3];
|
|
state=BlockChecksumState;
|
|
break;
|
|
|
|
case BlockChecksumState:
|
|
switch(checksumflags)
|
|
{
|
|
case 0: break; // none
|
|
|
|
case 1: // crc32
|
|
{
|
|
uint32_t correctcrc=[parent readUInt32LE];
|
|
if((crc^0xffffffff)!=correctcrc) checksumscorrect=NO;
|
|
}
|
|
break;
|
|
|
|
case 4: // crc64
|
|
{
|
|
uint64_t correctcrc=[parent readUInt64LE];
|
|
if((crc^0xffffffffffffffff)!=correctcrc) checksumscorrect=NO;
|
|
}
|
|
break;
|
|
|
|
case 2: case 3: [parent skipBytes:4]; break;
|
|
case 5: case 6: [parent skipBytes:8]; break;
|
|
case 7: case 8: case 9: [parent skipBytes:16]; break;
|
|
case 10: case 11: case 12: [parent skipBytes:32]; break;
|
|
case 13: case 14: case 15: [parent skipBytes:64]; break;
|
|
}
|
|
state=BlockHeaderState;
|
|
break;
|
|
|
|
case StreamIndexState:
|
|
{
|
|
uint64_t numrecords=ParseInteger(parent);
|
|
for(uint64_t i=0;i<numrecords;i++)
|
|
{
|
|
// Just skip the index records
|
|
ParseInteger(parent);
|
|
ParseInteger(parent);
|
|
}
|
|
|
|
[parent skipBytes:(-([parent offsetInFile]-startoffs))&3];
|
|
[parent skipBytes:4]; // skip CRC
|
|
|
|
state=StreamFooterState;
|
|
}
|
|
break;
|
|
|
|
case StreamFooterState:
|
|
[parent skipBytes:8]; // skip CRC and backwards size
|
|
if([parent readUInt8]!=0) [XADException raiseIllegalDataException];
|
|
if([parent readUInt8]!=checksumflags) [XADException raiseIllegalDataException];
|
|
if([parent readUInt8]!='Y') [XADException raiseIllegalDataException];
|
|
if([parent readUInt8]!='Z') [XADException raiseIllegalDataException];
|
|
|
|
state=StreamPaddingState;
|
|
break;
|
|
|
|
case StreamPaddingState:
|
|
for(;;)
|
|
{
|
|
if([parent atEndOfFile])
|
|
{
|
|
state=EndState;
|
|
break;
|
|
}
|
|
uint32_t pad=[parent readUInt32BE];
|
|
if(pad=='\3757zX')
|
|
{
|
|
[parent skipBytes:-4];
|
|
state=StreamHeaderState;
|
|
break;
|
|
}
|
|
else if(pad!=0) [XADException raiseIllegalDataException];
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(state==EndState) [self endStream];
|
|
|
|
return bytesread;
|
|
}
|
|
|
|
-(BOOL)hasChecksum
|
|
{
|
|
return checksumflags==1||checksumflags==4;
|
|
}
|
|
|
|
-(BOOL)isChecksumCorrect
|
|
{
|
|
return checksumscorrect;
|
|
}
|
|
|
|
-(double)estimatedProgress { return [parent estimatedProgress]; } // TODO: better estimation using buffer?
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
static uint64_t ParseInteger(CSHandle *fh)
|
|
{
|
|
uint64_t res=0;
|
|
int pos=0;
|
|
uint8_t b;
|
|
|
|
do
|
|
{
|
|
b=[fh readUInt8];
|
|
res|=(b&0x7f)<<pos;
|
|
pos+=7;
|
|
}
|
|
while(b&0x80);
|
|
|
|
return res;
|
|
}
|