// #included by unpack.cpp
#ifdef RAR_COMMON_HPP
#define STARTL1  2
const
static unsigned int DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00,
														 0xee00,0xf000,0xf200,0xf200,0xffff};
const
static unsigned int PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32};

#define STARTL2  3
const
static unsigned int DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00,
														 0xf000,0xf200,0xf240,0xffff};
const
static unsigned int PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36};

#define STARTHF0  4
const
static unsigned int DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200,
															0xf200,0xf200,0xffff};
const
static unsigned int PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33};


#define STARTHF1  5
const
static unsigned int DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200,
															0xf7e0,0xffff};
const
static unsigned int PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127};


#define STARTHF2  5
const
static unsigned int DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff,
															0xffff,0xffff};
const
static unsigned int PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0};


#define STARTHF3  6
const
static unsigned int DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff,
															0xffff};
const
static unsigned int PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0};


#define STARTHF4  8
const
static unsigned int DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff};
const
static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0};


void Unpack::Unpack15(bool Solid)
{
	if (Suspended)
		UnpPtr=WrPtr;
	else
	{
		UnpInitData(Solid);
		OldUnpInitData(Solid);
		UnpReadBuf();
		if (!Solid)
		{
			InitHuff();
			UnpPtr=0;
		}
		else
			UnpPtr=WrPtr;
		--DestUnpSize;
	}
	if (DestUnpSize>=0)
	{
		GetFlagsBuf();
		FlagsCnt=8;
	}

	while (DestUnpSize>=0)
	{
		UnpPtr&=MAXWINMASK;

		if (InAddr>ReadTop-30 && !UnpReadBuf())
			break;
		if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr)
		{
			OldUnpWriteBuf();
			if (Suspended)
				return;
		}
		if (StMode)
		{
			HuffDecode();
			continue;
		}

		if (--FlagsCnt < 0)
		{
			GetFlagsBuf();
			FlagsCnt=7;
		}

		if (FlagBuf & 0x80)
		{
			FlagBuf<<=1;
			if (Nlzb > Nhfb)
				LongLZ();
			else
				HuffDecode();
		}
		else
		{
			FlagBuf<<=1;
			if (--FlagsCnt < 0)
			{
				GetFlagsBuf();
				FlagsCnt=7;
			}
			if (FlagBuf & 0x80)
			{
				FlagBuf<<=1;
				if (Nlzb > Nhfb)
					HuffDecode();
				else
					LongLZ();
			}
			else
			{
				FlagBuf<<=1;
				ShortLZ();
			}
		}
	}
	OldUnpWriteBuf();
}


void Unpack::OldUnpWriteBuf()
{
	if (UnpPtr!=WrPtr)
		UnpSomeRead=true;
	if (UnpPtr<WrPtr)
	{
		UnpIO->UnpWrite(&Window[WrPtr],-WrPtr & MAXWINMASK);
		UnpIO->UnpWrite(Window,UnpPtr);
		UnpAllBuf=true;
	}
	else
		UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr);
	WrPtr=UnpPtr;
}


#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos])
#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos])

void Unpack::ShortLZ()
{
	const
	static unsigned int ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0};
	const
	static unsigned int ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe,
																	 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0};
	const
	static unsigned int ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0};
	const
	static unsigned int ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8,
																	 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0};


	unsigned int Length,SaveLength;
	unsigned int LastDistance;
	unsigned int Distance;
	int DistancePlace;
	NumHuf=0;

	unsigned int BitField=fgetbits();
	if (LCount==2)
	{
		faddbits(1);
		if (BitField >= 0x8000)
		{
			OldCopyString((unsigned int)LastDist,LastLength);
			return;
		}
		BitField <<= 1;
		LCount=0;
	}

	BitField>>=8;

//  not thread safe, replaced by GetShortLen1 and GetShortLen2 macro
//  ShortLen1[1]=ShortLen2[3]=Buf60+3;

	if (AvrLn1<37)
	{
		for (Length=0;;Length++)
			if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0)
				break;
		faddbits(GetShortLen1(Length));
	}
	else
	{
		for (Length=0;;Length++)
			if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0)
				break;
		faddbits(GetShortLen2(Length));
	}

	if (Length >= 9)
	{
		if (Length == 9)
		{
			LCount++;
			OldCopyString((unsigned int)LastDist,LastLength);
			return;
		}
		if (Length == 14)
		{
			LCount=0;
			Length=DecodeNum(fgetbits(),STARTL2,DecL2,PosL2)+5;
			Distance=(fgetbits()>>1) | 0x8000;
			faddbits(15);
			LastLength=Length;
			LastDist=Distance;
			OldCopyString(Distance,Length);
			return;
		}

		LCount=0;
		SaveLength=Length;
		Distance=OldDist[(OldDistPtr-(Length-9)) & 3];
		Length=DecodeNum(fgetbits(),STARTL1,DecL1,PosL1)+2;
		if (Length==0x101 && SaveLength==10)
		{
			Buf60 ^= 1;
			return;
		}
		if (Distance > 256)
			Length++;
		if (Distance >= MaxDist3)
			Length++;

		OldDist[OldDistPtr++]=Distance;
		OldDistPtr = OldDistPtr & 3;
		LastLength=Length;
		LastDist=Distance;
		OldCopyString(Distance,Length);
		return;
	}

	LCount=0;
	AvrLn1 += Length;
	AvrLn1 -= AvrLn1 >> 4;

	DistancePlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff;
	Distance=ChSetA[DistancePlace];
	if (--DistancePlace != -1)
	{
		PlaceA[Distance]--;
		LastDistance=ChSetA[DistancePlace];
		PlaceA[LastDistance]++;
		ChSetA[DistancePlace+1]=LastDistance;
		ChSetA[DistancePlace]=Distance;
	}
	Length+=2;
	OldDist[OldDistPtr++] = ++Distance;
	OldDistPtr = OldDistPtr & 3;
	LastLength=Length;
	LastDist=Distance;
	OldCopyString(Distance,Length);
}


void Unpack::LongLZ()
{
	unsigned int Length;
	unsigned int Distance;
	unsigned int DistancePlace,NewDistancePlace;
	unsigned int OldAvr2,OldAvr3;

	NumHuf=0;
	Nlzb+=16;
	if (Nlzb > 0xff)
	{
		Nlzb=0x90;
		Nhfb >>= 1;
	}
	OldAvr2=AvrLn2;

	unsigned int BitField=fgetbits();
	if (AvrLn2 >= 122)
		Length=DecodeNum(BitField,STARTL2,DecL2,PosL2);
	else
		if (AvrLn2 >= 64)
			Length=DecodeNum(BitField,STARTL1,DecL1,PosL1);
		else
			if (BitField < 0x100)
			{
				Length=BitField;
				faddbits(16);
			}
			else
			{
				for (Length=0;((BitField<<Length)&0x8000)==0;Length++)
					;
				faddbits(Length+1);
			}

	AvrLn2 += Length;
	AvrLn2 -= AvrLn2 >> 5;

	BitField=fgetbits();
	if (AvrPlcB > 0x28ff)
		DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2);
	else
		if (AvrPlcB > 0x6ff)
			DistancePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1);
		else
			DistancePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0);

	AvrPlcB += DistancePlace;
	AvrPlcB -= AvrPlcB >> 8;
	while (1)
	{
		Distance = ChSetB[DistancePlace & 0xff];
		NewDistancePlace = NToPlB[Distance++ & 0xff]++;
		if (!(Distance & 0xff))
			CorrHuff(ChSetB,NToPlB);
		else
			break;
	}

	ChSetB[DistancePlace]=ChSetB[NewDistancePlace];
	ChSetB[NewDistancePlace]=Distance;

	Distance=((Distance & 0xff00) | (fgetbits() >> 8)) >> 1;
	faddbits(7);

	OldAvr3=AvrLn3;
	if (Length!=1 && Length!=4)
		if (Length==0 && Distance <= MaxDist3)
		{
			AvrLn3++;
			AvrLn3 -= AvrLn3 >> 8;
		}
		else
			if (AvrLn3 > 0)
				AvrLn3--;
	Length+=3;
	if (Distance >= MaxDist3)
		Length++;
	if (Distance <= 256)
		Length+=8;
	if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40)
		MaxDist3=0x7f00;
	else
		MaxDist3=0x2001;
	OldDist[OldDistPtr++]=Distance;
	OldDistPtr = OldDistPtr & 3;
	LastLength=Length;
	LastDist=Distance;
	OldCopyString(Distance,Length);
}


void Unpack::HuffDecode()
{
	unsigned int CurByte,NewBytePlace;
	unsigned int Length;
	unsigned int Distance;
	int BytePlace;

	unsigned int BitField=fgetbits();

	if (AvrPlc > 0x75ff)
		BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4);
	else
		if (AvrPlc > 0x5dff)
			BytePlace=DecodeNum(BitField,STARTHF3,DecHf3,PosHf3);
		else
			if (AvrPlc > 0x35ff)
				BytePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2);
			else
				if (AvrPlc > 0x0dff)
					BytePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1);
				else
					BytePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0);
	BytePlace&=0xff;
	if (StMode)
	{
		if (BytePlace==0 && BitField > 0xfff)
			BytePlace=0x100;
		if (--BytePlace==-1)
		{
			BitField=fgetbits();
			faddbits(1);
			if (BitField & 0x8000)
			{
				NumHuf=StMode=0;
				return;
			}
			else
			{
				Length = (BitField & 0x4000) ? 4 : 3;
				faddbits(1);
				Distance=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2);
				Distance = (Distance << 5) | (fgetbits() >> 11);
				faddbits(5);
				OldCopyString(Distance,Length);
				return;
			}
		}
	}
	else
		if (NumHuf++ >= 16 && FlagsCnt==0)
			StMode=1;
	AvrPlc += BytePlace;
	AvrPlc -= AvrPlc >> 8;
	Nhfb+=16;
	if (Nhfb > 0xff)
	{
		Nhfb=0x90;
		Nlzb >>= 1;
	}

	Window[UnpPtr++]=(byte)(ChSet[BytePlace]>>8);
	--DestUnpSize;

	while (1)
	{
		CurByte=ChSet[BytePlace];
		NewBytePlace=NToPl[CurByte++ & 0xff]++;
		if ((CurByte & 0xff) > 0xa1)
			CorrHuff(ChSet,NToPl);
		else
			break;
	}

	ChSet[BytePlace]=ChSet[NewBytePlace];
	ChSet[NewBytePlace]=CurByte;
}


void Unpack::GetFlagsBuf()
{
	unsigned int Flags,NewFlagsPlace;
	unsigned int FlagsPlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2);

	while (1)
	{
		Flags=ChSetC[FlagsPlace];
		FlagBuf=Flags>>8;
		NewFlagsPlace=NToPlC[Flags++ & 0xff]++;
		if ((Flags & 0xff) != 0)
			break;
		CorrHuff(ChSetC,NToPlC);
	}

	ChSetC[FlagsPlace]=ChSetC[NewFlagsPlace];
	ChSetC[NewFlagsPlace]=Flags;
}


void Unpack::OldUnpInitData(int Solid)
{
	if (!Solid)
	{
		AvrPlcB=AvrLn1=AvrLn2=AvrLn3=NumHuf=Buf60=0;
		AvrPlc=0x3500;
		MaxDist3=0x2001;
		Nhfb=Nlzb=0x80;
	}
	FlagsCnt=0;
	FlagBuf=0;
	StMode=0;
	LCount=0;
	ReadTop=0;
}


void Unpack::InitHuff()
{
	for (unsigned int I=0;I<256;I++)
	{
		Place[I]=PlaceA[I]=PlaceB[I]=I;
		PlaceC[I]=(~I+1) & 0xff;
		ChSet[I]=ChSetB[I]=I<<8;
		ChSetA[I]=I;
		ChSetC[I]=((~I+1) & 0xff)<<8;
	}
	memset(NToPl,0,sizeof(NToPl));
	memset(NToPlB,0,sizeof(NToPlB));
	memset(NToPlC,0,sizeof(NToPlC));
	CorrHuff(ChSetB,NToPlB);
}


void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace)
{
	int I,J;
	for (I=7;I>=0;I--)
		for (J=0;J<32;J++,CharSet++)
			*CharSet=(*CharSet & ~0xff) | I;
	memset(NumToPlace,0,sizeof(NToPl));
	for (I=6;I>=0;I--)
		NumToPlace[I]=(7-I)*32;
}


void Unpack::OldCopyString(unsigned int Distance,unsigned int Length)
{
	DestUnpSize-=Length;
	while (Length--)
	{
		Window[UnpPtr]=Window[(UnpPtr-Distance) & MAXWINMASK];
		UnpPtr=(UnpPtr+1) & MAXWINMASK;
	}
}


unsigned int Unpack::DecodeNum(int Num,unsigned int StartPos,
			const unsigned int *DecTab,const unsigned int *PosTab)
{
	int I;
	for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++)
		StartPos++;
	faddbits(StartPos);
	return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]);
}
#endif
