mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-28 19:13:49 +02:00
159 lines
4.3 KiB
Objective-C
159 lines
4.3 KiB
Objective-C
/*
|
|
* XADPowerPackerParser.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 "XADPowerPackerParser.h"
|
|
#import "CSMemoryHandle.h"
|
|
#import "XADException.h"
|
|
|
|
static NSData *PowerPackerUnpack(NSData *packeddata,int unpackedlength);
|
|
|
|
@implementation XADPowerPackerParser
|
|
|
|
+(int)requiredHeaderSize { return 4; }
|
|
|
|
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
|
|
{
|
|
int length=[data length];
|
|
const uint8_t *bytes=[data bytes];
|
|
|
|
return length>=8&&bytes[0]=='P'&&bytes[1]=='P'&&bytes[2]=='2'&&bytes[3]=='0';
|
|
}
|
|
|
|
-(void)parse
|
|
{
|
|
CSHandle *fh=[self handle];
|
|
|
|
[fh seekToEndOfFile];
|
|
|
|
off_t compsize=[fh offsetInFile]-4;
|
|
|
|
[fh skipBytes:-4];
|
|
|
|
int size=[fh readUInt8]<<16;
|
|
size|=[fh readUInt8]<<8;
|
|
size|=[fh readUInt8];
|
|
|
|
[self addEntryWithDictionary:[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
[self XADPathWithUnseparatedString:[[self name] stringByDeletingPathExtension]],XADFileNameKey,
|
|
[NSNumber numberWithLongLong:size],XADFileSizeKey,
|
|
[NSNumber numberWithLongLong:compsize],XADCompressedSizeKey,
|
|
[self XADStringWithString:@"PowerPacker"],XADCompressionNameKey,
|
|
|
|
[NSNumber numberWithLongLong:4],XADDataOffsetKey,
|
|
[NSNumber numberWithLongLong:compsize],XADDataLengthKey,
|
|
nil]];
|
|
}
|
|
|
|
-(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
|
|
{
|
|
NSData *data=[dict objectForKey:@"PowerPackerFileContents"];
|
|
if(!data)
|
|
{
|
|
CSHandle *handle=[self handleAtDataOffsetForDictionary:dict];
|
|
data=PowerPackerUnpack([handle remainingFileContents],[[dict objectForKey:XADFileSizeKey] intValue]);
|
|
[(NSMutableDictionary *)dict setObject:data forKey:@"PowerPackerFileContents"];
|
|
}
|
|
|
|
return [CSMemoryHandle memoryHandleForReadingData:data];
|
|
}
|
|
|
|
-(NSString *)formatName { return @"PowerPacker"; }
|
|
|
|
@end
|
|
|
|
static uint32_t GetBits(int n,const uint8_t *buffer,int *bitpos)
|
|
{
|
|
uint32_t result=0;
|
|
|
|
for(int i=0;i<n;i++)
|
|
{
|
|
(*bitpos)--;
|
|
if(*bitpos<0) [XADException raiseDecrunchException];
|
|
int currbyte=*bitpos/8;
|
|
int currbit=7-(*bitpos&7);
|
|
result=(result<<1)|((buffer[currbyte]>>currbit)&1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static NSData *PowerPackerUnpack(NSData *packeddata,int unpackedlength)
|
|
{
|
|
const uint8_t *packed=[packeddata bytes];
|
|
int packedlength=[packeddata length];
|
|
|
|
NSMutableData *unpackeddata=[NSMutableData dataWithLength:unpackedlength];
|
|
uint8_t *unpacked=[unpackeddata mutableBytes];
|
|
|
|
int bitpos=packedlength*8-32;
|
|
uint8_t *dest=unpacked+unpackedlength;
|
|
|
|
// Skip extra bits
|
|
GetBits(packed[packedlength-1],packed,&bitpos);
|
|
|
|
for(;;)
|
|
{
|
|
if(GetBits(1,packed,&bitpos)==0) // copy some bytes from the source
|
|
{
|
|
int add,numbytes=1;
|
|
do
|
|
{
|
|
add=GetBits(2,packed,&bitpos);
|
|
numbytes+=add;
|
|
} while(add==3);
|
|
|
|
for(int i=0;i<numbytes;i++)
|
|
{
|
|
if(dest<=unpacked) [XADException raiseDecrunchException];
|
|
*--dest=GetBits(8,packed,&bitpos);
|
|
}
|
|
if(dest==unpacked) return unpackeddata;
|
|
}
|
|
|
|
// decode what to copy from the destination file
|
|
int index=GetBits(2,packed,&bitpos);
|
|
int numbits=packed[index];
|
|
int numbytes=index+2;
|
|
int offset;
|
|
|
|
if(numbytes==5) // 5 means >=5
|
|
{
|
|
// and maybe a bigger offset
|
|
if(GetBits(1,packed,&bitpos)==0) offset=GetBits(7,packed,&bitpos);
|
|
else offset=GetBits(numbits,packed,&bitpos);
|
|
|
|
int add;
|
|
do {
|
|
add=GetBits(3,packed,&bitpos);
|
|
numbytes+=add;
|
|
} while(add==7);
|
|
}
|
|
else offset=GetBits(numbits,packed,&bitpos);
|
|
|
|
for(int i=0;i<numbytes;i++)
|
|
{
|
|
if(dest<=unpacked) [XADException raiseDecrunchException];
|
|
dest[-1]=dest[offset];
|
|
dest--;
|
|
}
|
|
if(dest==unpacked) return unpackeddata;
|
|
}
|
|
|
|
return nil; // can't happen
|
|
}
|