mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-29 03:23:48 +02:00
351 lines
8.2 KiB
Objective-C
351 lines
8.2 KiB
Objective-C
/*
|
|
* XADRARFilters.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 "XADRARFilters.h"
|
|
#import "XADException.h"
|
|
#import "RARAudioDecoder.h"
|
|
|
|
static void RARDeltaFilter(uint8_t *src,uint8_t *dest,size_t length,int numchannels);
|
|
static void RARE8E9Filter(uint8_t *memory,size_t length,off_t filepos,bool handlee9,bool wrapposition);
|
|
static void RARARMFilter(uint8_t *memory,size_t length,off_t filepos);
|
|
|
|
@implementation XADRAR30Filter
|
|
|
|
+(XADRAR30Filter *)filterForProgramInvocation:(XADRARProgramInvocation *)program
|
|
startPosition:(off_t)startpos length:(int)length
|
|
{
|
|
//NSLog(@"%010qx",[[program programCode] fingerprint]);
|
|
|
|
Class class;
|
|
switch([[program programCode] fingerprint])
|
|
{
|
|
case 0x1d0e06077d: class=[XADRAR30DeltaFilter class]; break;
|
|
case 0xd8bc85e701: class=[XADRAR30AudioFilter class]; break;
|
|
case 0x35ad576887: class=[XADRAR30E8Filter class]; break;
|
|
case 0x393cd7e57e: class=[XADRAR30E8E9Filter class]; break;
|
|
default: class=[XADRAR30Filter class]; break;
|
|
}
|
|
|
|
return [[[class alloc] initWithProgramInvocation:program startPosition:startpos length:length] autorelease];
|
|
}
|
|
|
|
-(id)initWithProgramInvocation:(XADRARProgramInvocation *)program
|
|
startPosition:(off_t)startpos length:(int)length
|
|
{
|
|
if((self=[super init]))
|
|
{
|
|
invocation=[program retain];
|
|
blockstartpos=startpos;
|
|
blocklength=length;
|
|
|
|
filteredblockaddress=filteredblocklength=0;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[invocation release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(off_t)startPosition { return blockstartpos; }
|
|
|
|
-(int)length { return blocklength; }
|
|
|
|
-(uint32_t)filteredBlockAddress { return filteredblockaddress; }
|
|
|
|
-(uint32_t)filteredBlockLength { return filteredblocklength; }
|
|
|
|
-(void)executeOnVirtualMachine:(XADRARVirtualMachine *)vm atPosition:(off_t)pos
|
|
{
|
|
[invocation restoreGlobalDataIfAvailable]; // This is silly, but RAR does it.
|
|
|
|
[invocation setInitialRegisterState:6 toValue:(uint32_t)pos];
|
|
[invocation setGlobalValueAtOffset:0x24 toValue:(uint32_t)pos];
|
|
[invocation setGlobalValueAtOffset:0x28 toValue:(uint32_t)(pos>>32)];
|
|
|
|
if(![invocation executeOnVitualMachine:vm]) [XADException raiseIllegalDataException];
|
|
|
|
filteredblockaddress=[vm readWordAtAddress:RARProgramSystemGlobalAddress+0x20]&RARProgramMemoryMask;
|
|
filteredblocklength=[vm readWordAtAddress:RARProgramSystemGlobalAddress+0x1c]&RARProgramMemoryMask;
|
|
|
|
if(filteredblockaddress+filteredblocklength>=RARProgramMemorySize) filteredblockaddress=filteredblocklength=0;
|
|
|
|
[invocation backupGlobalData]; // Also silly.
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation XADRAR30DeltaFilter
|
|
|
|
-(void)executeOnVirtualMachine:(XADRARVirtualMachine *)vm atPosition:(off_t)pos
|
|
{
|
|
int length=[invocation initialRegisterState:4]; // should really be blocklength, but, RAR.
|
|
int numchannels=[invocation initialRegisterState:0];
|
|
uint8_t *memory=[vm memory];
|
|
|
|
if(length>RARProgramWorkSize/2) return;
|
|
|
|
filteredblockaddress=length;
|
|
filteredblocklength=length;
|
|
|
|
uint8_t *src=&memory[0];
|
|
uint8_t *dest=&memory[filteredblockaddress];
|
|
|
|
RARDeltaFilter(src,dest,length,numchannels);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation XADRAR30AudioFilter
|
|
|
|
-(void)executeOnVirtualMachine:(XADRARVirtualMachine *)vm atPosition:(off_t)pos
|
|
{
|
|
int length=[invocation initialRegisterState:4]; // should really be blocklength, but, RAR.
|
|
int numchannels=[invocation initialRegisterState:0];
|
|
uint8_t *memory=[vm memory];
|
|
|
|
if(length>RARProgramWorkSize/2) return;
|
|
|
|
filteredblockaddress=length;
|
|
filteredblocklength=length;
|
|
|
|
uint8_t *src=&memory[0];
|
|
uint8_t *dest=&memory[filteredblockaddress];
|
|
for(int i=0;i<numchannels;i++)
|
|
{
|
|
RAR30AudioState state;
|
|
memset(&state,0,sizeof(state));
|
|
|
|
for(int destoffs=i;destoffs<length;destoffs+=numchannels)
|
|
{
|
|
dest[destoffs]=DecodeRAR30Audio(&state,*src++);
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation XADRAR30E8Filter
|
|
|
|
-(void)executeOnVirtualMachine:(XADRARVirtualMachine *)vm atPosition:(off_t)pos
|
|
{
|
|
int length=[invocation initialRegisterState:4];
|
|
uint8_t *memory=[vm memory];
|
|
|
|
if(length>RARProgramWorkSize || length<4) return;
|
|
|
|
filteredblockaddress=0;
|
|
filteredblocklength=length;
|
|
|
|
RARE8E9Filter(memory,length,pos,false,false);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation XADRAR30E8E9Filter
|
|
|
|
-(void)executeOnVirtualMachine:(XADRARVirtualMachine *)vm atPosition:(off_t)pos
|
|
{
|
|
int length=[invocation initialRegisterState:4];
|
|
uint8_t *memory=[vm memory];
|
|
|
|
if(length>RARProgramWorkSize || length<4) return;
|
|
|
|
filteredblockaddress=0;
|
|
filteredblocklength=length;
|
|
|
|
RARE8E9Filter(memory,length,pos,true,false);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation XADRAR50Filter
|
|
|
|
-(id)initWithStart:(off_t)filterstart length:(uint32_t)filterlength
|
|
{
|
|
if(self=[super init])
|
|
{
|
|
start=filterstart;
|
|
length=filterlength;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(off_t)start { return start; }
|
|
|
|
-(uint32_t)length { return length; }
|
|
|
|
-(void)runOnData:(NSMutableData *)data fileOffset:(off_t)pos { }
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation XADRAR50DeltaFilter
|
|
|
|
-(id)initWithStart:(off_t)filterstart length:(uint32_t)filterlength numberOfChannels:(int)numberofchannels
|
|
{
|
|
if(self=[super initWithStart:filterstart length:filterlength])
|
|
{
|
|
numchannels=numberofchannels;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)runOnData:(NSMutableData *)data fileOffset:(off_t)pos
|
|
{
|
|
uint8_t *memory=[data mutableBytes];
|
|
size_t memlength=[data length];
|
|
|
|
NSMutableData *destdata=[[NSMutableData alloc] initWithLength:memlength];
|
|
uint8_t *destmem=[destdata mutableBytes];
|
|
|
|
RARDeltaFilter(memory,destmem,memlength,numchannels);
|
|
|
|
memcpy(memory,destmem,memlength);
|
|
|
|
[destdata release];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation XADRAR50E8E9Filter
|
|
|
|
-(id)initWithStart:(off_t)filterstart length:(uint32_t)filterlength handleE9:(BOOL)shouldhandlee9
|
|
{
|
|
if(self=[super initWithStart:filterstart length:filterlength])
|
|
{
|
|
handlee9=shouldhandlee9;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
-(void)runOnData:(NSMutableData *)data fileOffset:(off_t)pos
|
|
{
|
|
uint8_t *memory=data.mutableBytes;
|
|
size_t memlength=data.length;
|
|
|
|
RARE8E9Filter(memory,memlength,pos,handlee9,true);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation XADRAR50ARMFilter
|
|
|
|
-(void)runOnData:(NSMutableData *)data fileOffset:(off_t)pos
|
|
{
|
|
uint8_t *memory=data.mutableBytes;
|
|
size_t memlength=data.length;
|
|
|
|
RARARMFilter(memory,memlength,pos);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
static void RARDeltaFilter(uint8_t *src,uint8_t *dest,size_t length,int numchannels)
|
|
{
|
|
for(int i=0;i<numchannels;i++)
|
|
{
|
|
uint8_t lastbyte=0;
|
|
for(int destoffs=i;destoffs<length;destoffs+=numchannels)
|
|
{
|
|
uint8_t newbyte=lastbyte-*src++;
|
|
dest[destoffs]=newbyte;
|
|
lastbyte=newbyte;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void RARE8E9Filter(uint8_t *memory,size_t length,off_t filepos,bool handlee9,bool wrapposition)
|
|
{
|
|
if (length < 5)
|
|
{
|
|
// This probably, worth throwing an exception, but it's hard to tell if this should be skipped or error should be thrown
|
|
return;
|
|
}
|
|
|
|
int32_t filesize=0x1000000;
|
|
|
|
for(size_t i=0;i<=length-5;i++)
|
|
{
|
|
if(memory[i]==0xe8 || (handlee9 && memory[i]==0xe9))
|
|
{
|
|
int32_t currpos=(int32_t)filepos+i+1;
|
|
if(wrapposition) currpos%=filesize;
|
|
int32_t address=CSInt32LE(&memory[i+1]);
|
|
if(address<0)
|
|
{
|
|
if(address+currpos>=0) CSSetUInt32LE(&memory[i+1],address+filesize);
|
|
}
|
|
else
|
|
{
|
|
if(address<filesize) CSSetUInt32LE(&memory[i+1],address-currpos);
|
|
}
|
|
|
|
i+=4;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void RARARMFilter(uint8_t *memory,size_t length,off_t filepos)
|
|
{
|
|
if (length < 4)
|
|
{
|
|
// This probably, worth throwing an exception, but it's hard to tell if this should be skipped or error should be thrown
|
|
return;
|
|
}
|
|
for(size_t i=0;i<=length-4;i+=4)
|
|
{
|
|
if(memory[i+3]==0xeb)
|
|
{
|
|
uint32_t offset=memory[i]+(memory[i+1]<<8)+(memory[i+2]<<16);
|
|
offset-=((uint32_t)filepos+i)/4;
|
|
memory[i]=offset;
|
|
memory[i+1]=offset>>8;
|
|
memory[i+2]=offset>>16;
|
|
}
|
|
}
|
|
}
|