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

278 lines
7 KiB
Objective-C

/*
* XADQuantumHandle.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 "XADQuantumHandle.h"
#import "XADException.h"
static void InitQuantumCoder(QuantumCoder *self,CSInputBuffer *input);
static uint16_t GetQuantumFrequency(QuantumCoder *self,uint16_t totfreq);
static void RemoveQuantumCode(QuantumCoder *self,int cumfreqm1,int cumfreq,int totfreq);
static int NextQuantumSymbolForModel(QuantumCoder *self,QuantumModel *model);
static void InitQuantumModel(QuantumModel *self,int numsymbols);
static void UpdateQuantumModel(QuantumModel *model,int index);
@implementation XADQuantumHandle
-(id)initWithBlockReader:(XADCABBlockReader *)blockreader windowBits:(int)windowbits
{
if((self=[super initWithBlockReader:blockreader]))
{
[self setInputBuffer:CSInputBufferAllocEmpty()];
InitializeLZSS(&lzss,1<<windowbits);
numslots6=windowbits*2;
if(numslots6>36) numslots5=36;
else numslots5=numslots6;
if(numslots6>24) numslots4=24;
else numslots4=numslots6;
}
return self;
}
-(void)dealloc
{
CleanupLZSS(&lzss);
[super dealloc];
}
-(void)resetCABBlockHandle
{
InitQuantumModel(&selectormodel,7);
for(int i=0;i<4;i++) InitQuantumModel(&literalmodel[i],64);
InitQuantumModel(&offsetmodel4,numslots4);
InitQuantumModel(&offsetmodel5,numslots5);
InitQuantumModel(&offsetmodel6,numslots6);
InitQuantumModel(&lengthmodel6,27);
RestartLZSS(&lzss);
}
-(int)produceCABBlockWithInputBuffer:(uint8_t *)buffer length:(int)complength atOffset:(off_t)pos length:(int)uncomplength
{
static int OffsetBaseTable[]=
{
0x00000,0x00001,0x00002,0x00003,0x00004,0x00006,0x00008,0x0000c,
0x00010,0x00018,0x00020,0x00030,0x00040,0x00060,0x00080,0x000c0,
0x00100,0x00180,0x00200,0x00300,0x00400,0x00600,0x00800,0x00c00,
0x01000,0x01800,0x02000,0x03000,0x04000,0x06000,0x08000,0x0c000,
0x10000,0x18000,0x20000,0x30000,0x40000,0x60000,0x80000,0xc0000,
0x100000,0x180000
};
static int OffsetAdditionalBitsTable[]=
{
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,
11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19
};
static int LengthBaseTable[]=
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x08,0x0a,0x0c,0x0e,0x12,
0x16,0x1a,0x1e,0x26,0x2e,0x36,0x3e,0x4e,0x5e,0x6e,0x7e,0x9e,
0xbe,0xde,0xfe
};
static int LengthAdditionalBitsTable[] =
{
0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0
};
CSInputSetMemoryBuffer(input,buffer,complength,0);
InitQuantumCoder(&coder,input);
// int dicpos=pos&dictionarymask;
int end=(int)LZSSPosition(&lzss)+uncomplength;
[self setBlockPointer:CurrentLZSSWindowPointer(&lzss)];
while(LZSSPosition(&lzss)<end)
{
int selector=NextQuantumSymbolForModel(&coder,&selectormodel);
if(selector<4)
{
EmitLZSSLiteral(&lzss,NextQuantumSymbolForModel(&coder,&literalmodel[selector])+selector*64);
}
else
{
int offset,length;
if(selector==4)
{
length=3;
int offsetslot=NextQuantumSymbolForModel(&coder,&offsetmodel4);
offset=OffsetBaseTable[offsetslot]+
CSInputNextBitString(input,OffsetAdditionalBitsTable[offsetslot])+1;
}
else if(selector==5)
{
length=4;
int offsetslot=NextQuantumSymbolForModel(&coder,&offsetmodel5);
offset=OffsetBaseTable[offsetslot]+
CSInputNextBitString(input,OffsetAdditionalBitsTable[offsetslot])+1;
}
else //if(selector==6)
{
int lengthslot=NextQuantumSymbolForModel(&coder,&lengthmodel6);
length=LengthBaseTable[lengthslot]+
CSInputNextBitString(input,LengthAdditionalBitsTable[lengthslot])+5;
int offsetslot=NextQuantumSymbolForModel(&coder,&offsetmodel6);
offset=OffsetBaseTable[offsetslot]+
CSInputNextBitString(input,OffsetAdditionalBitsTable[offsetslot])+1;
}
EmitLZSSMatch(&lzss,offset,length);
}
}
return uncomplength;
}
@end
static void InitQuantumCoder(QuantumCoder *self,CSInputBuffer *input)
{
self->CS_L=0;
self->CS_H=0xffff;
self->CS_C=CSInputNextBitString(input,16);
self->input=input;
}
static uint16_t GetQuantumFrequency(QuantumCoder *self,uint16_t totfreq)
{
uint32_t range=((self->CS_H-self->CS_L)&0xffff)+1;
uint32_t freq=((self->CS_C-self->CS_L+1)*totfreq-1)/range;
return freq&0xffff;
}
static void RemoveQuantumCode(QuantumCoder *self,int cumfreqm1,int cumfreq,int totfreq)
{
uint32_t range=(self->CS_H-self->CS_L) + 1;
self->CS_H=self->CS_L+((cumfreqm1*range)/totfreq)-1;
self->CS_L=self->CS_L+(cumfreq*range)/totfreq;
for(;;)
{
if((self->CS_L&0x8000)!=(self->CS_H&0x8000))
{
if((self->CS_L&0x4000)&&!(self->CS_H&0x4000))
{
self->CS_C^=0x4000;
self->CS_L&=0x3fff;
self->CS_H|=0x4000;
}
else return;
}
self->CS_L<<=1;
self->CS_H=(self->CS_H<<1)|1;
self->CS_C=(self->CS_C<<1)|CSInputNextBit(self->input);
}
}
static int NextQuantumSymbolForModel(QuantumCoder *self,QuantumModel *model)
{
int freq=GetQuantumFrequency(self,model->symbols[0].cumfreq);
int i;
for(i=1;i<model->numsymbols;i++)
{
if(model->symbols[i].cumfreq<=freq) break;
}
int symbol=model->symbols[i-1].symbol;
RemoveQuantumCode(self,model->symbols[i-1].cumfreq,
model->symbols[i].cumfreq,model->symbols[0].cumfreq);
UpdateQuantumModel(model,i);
return symbol;
}
static void InitQuantumModel(QuantumModel *self,int numsymbols)
{
self->numsymbols=numsymbols;
self->shiftsleft=4;
for(int i=0;i<numsymbols;i++)
{
self->symbols[i].symbol=i;
self->symbols[i].cumfreq=numsymbols-i;
}
self->symbols[numsymbols].cumfreq=0;
}
static void UpdateQuantumModel(QuantumModel *self,int index)
{
for(int i=0;i<index;i++) self->symbols[i].cumfreq+=8;
if(self->symbols[0].cumfreq<=3800) return;
self->shiftsleft--;
if(self->shiftsleft)
{
for(int i=self->numsymbols-1;i>=0;i--)
{
self->symbols[i].cumfreq>>=1;
if(self->symbols[i].cumfreq<=self->symbols[i+1].cumfreq)
self->symbols[i].cumfreq=self->symbols[i+1].cumfreq+1;
}
}
else
{
self->shiftsleft=50;
for(int i=0;i<self->numsymbols;i++)
{
self->symbols[i].cumfreq-=self->symbols[i+1].cumfreq;
self->symbols[i].cumfreq++;
self->symbols[i].cumfreq>>=1;
}
for(int i=0;i<self->numsymbols-1;i++)
{
for(int j=i+1;j<self->numsymbols;j++)
{
if(self->symbols[i].cumfreq<self->symbols[j].cumfreq)
{
QuantumModelSymbol temp=self->symbols[i];
self->symbols[i]=self->symbols[j];
self->symbols[j]=temp;
}
}
}
for(int i=self->numsymbols-1;i>=0;i--)
self->symbols[i].cumfreq+=self->symbols[i+1].cumfreq;
}
}