#include "rar.hpp"

#include "coder.cpp"
#include "suballoc.cpp"
#include "model.cpp"
#ifndef SFX_MODULE
#include "unpack15.cpp"
#include "unpack20.cpp"
#endif

Unpack::Unpack(ComprDataIO *DataIO)
	: VMCode( DataIO ), Filters( DataIO ), PrgStack( DataIO ), OldFilterLengths( DataIO ), ErrHandler( *DataIO )
{
	PPM.SubAlloc.ErrHandler = DataIO;
	LastStackFilter = NULL;
	UnpIO=DataIO;
	Window=NULL;
	ExternalWindow=false;
	UnpAllBuf=false;
	UnpSomeRead=false;
}


Unpack::~Unpack()
{
	if (Window!=NULL && !ExternalWindow)
		rarfree( Window );
	InitFilters();
}


void Unpack::Init(byte *Window)
{
	if (Window==NULL)
	{
		Unpack::Window = (byte*) rarmalloc( MAXWINSIZE );
		if (Unpack::Window==NULL)
			ErrHandler.MemoryError();
	}
	else
	{
		Unpack::Window=Window;
		ExternalWindow=true;
	}
	UnpInitData(false);
	BitInput::handle_mem_error( ErrHandler );
	Inp.handle_mem_error( ErrHandler );
	
	// Only check BitInput, as VM's memory isn't allocated yet
	VM.BitInput::handle_mem_error( ErrHandler );
	
#ifndef SFX_MODULE
	// RAR 1.5 decompression initialization
	OldUnpInitData(false);
	InitHuff();
#endif
}


void Unpack::DoUnpack(int Method,bool Solid)
{
	switch(Method)
	{
#ifndef SFX_MODULE
		case 15: // rar 1.5 compression
			Unpack15(Solid);
			break;
		case 20: // rar 2.x compression
		case 26: // files larger than 2GB
			Unpack20(Solid);
			break;
#endif
		case 29: // rar 3.x compression
		case 36: // alternative hash
			Unpack29(Solid);
			break;
	}
}


inline void Unpack::InsertOldDist(unsigned int Distance)
{
	OldDist[3]=OldDist[2];
	OldDist[2]=OldDist[1];
	OldDist[1]=OldDist[0];
	OldDist[0]=Distance;
}


inline void Unpack::InsertLastMatch(unsigned int Length,unsigned int Distance)
{
	LastDist=Distance;
	LastLength=Length;
}


// These optimizations give 22% speedup on x86, 44% speedup on PowerPC
void Unpack::CopyString(unsigned int Length,unsigned int Distance)
{
	unsigned    UnpPtr = this->UnpPtr; // cache in register
	byte* const Window = this->Window; // cache in register

	unsigned int DestPtr=UnpPtr-Distance;
	if (UnpPtr<MAXWINSIZE-260 && DestPtr<MAXWINSIZE-260)
	{
		this->UnpPtr += Length;
		if ( Distance < Length ) // can't use memcpy when source and dest overlap
		{
			// Length always >= 1
			do
			{
				Window[UnpPtr++]=Window[DestPtr++];
			}
			while (--Length>0)
			;
		}
		else
		{
			memcpy( &Window[UnpPtr], &Window[DestPtr], Length );
		}
	}
	else
	{
		while (Length--)
		{
			Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK];
			UnpPtr=(UnpPtr+1) & MAXWINMASK;
		}

		this->UnpPtr = UnpPtr;
	}
}


int Unpack::DecodeNumber(struct Decode *Dec)
{
	unsigned int Bits;
	unsigned int BitField=getbits() & 0xfffe;
	if (BitField<Dec->DecodeLen[8])
		if (BitField<Dec->DecodeLen[4])
			if (BitField<Dec->DecodeLen[2])
				if (BitField<Dec->DecodeLen[1])
					Bits=1;
				else
					Bits=2;
			else
				if (BitField<Dec->DecodeLen[3])
					Bits=3;
				else
					Bits=4;
		else
			if (BitField<Dec->DecodeLen[6])
				if (BitField<Dec->DecodeLen[5])
					Bits=5;
				else
					Bits=6;
			else
				if (BitField<Dec->DecodeLen[7])
					Bits=7;
				else
					Bits=8;
	else
		if (BitField<Dec->DecodeLen[12])
			if (BitField<Dec->DecodeLen[10])
				if (BitField<Dec->DecodeLen[9])
					Bits=9;
				else
					Bits=10;
			else
				if (BitField<Dec->DecodeLen[11])
					Bits=11;
				else
					Bits=12;
		else
			if (BitField<Dec->DecodeLen[14])
				if (BitField<Dec->DecodeLen[13])
					Bits=13;
				else
					Bits=14;
			else
				Bits=15;

	unsigned int N=Dec->DecodePos[Bits]+((BitField-Dec->DecodeLen[Bits-1])>>(16-Bits));
	if (N>=Dec->MaxNum)
		N=0;
	// do after reading values, to allow better instruction scheduling
	addbits(Bits);
	return(Dec->DecodeNum[N]);
}

const
static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224};
const
static unsigned char LBits[]=  {0,0,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};
static int DDecode[DC];
static byte DBits[DC];
const
static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12};
const
static unsigned char SDDecode[]={0,4,8,16,32,64,128,192};
const
static unsigned char SDBits[]=  {2,2,3, 4, 5, 6,  6,  6};

void Unpack::init_tables()
{
	if (DDecode[1]==0)
	{
		int Dist=0,BitLength=0,Slot=0;
		for (int I=0;I<sizeof(DBitLengthCounts)/sizeof(DBitLengthCounts[0]);I++,BitLength++)
			for (int J=0;J<DBitLengthCounts[I];J++,Slot++,Dist+=(1<<BitLength))
			{
				DDecode[Slot]=Dist;
				DBits[Slot]=BitLength;
			}
	}
}

void Unpack::Unpack29(bool Solid)
{
	// tables moved outside function
	
	unsigned int Bits;

	FileExtracted=true;

	if (!Suspended)
	{
		UnpInitData(Solid);
		if (!UnpReadBuf())
			return;
		if ((!Solid || !TablesRead) && !ReadTables())
			return;
	}

	while (true)
	{
		UnpPtr&=MAXWINMASK;

		if (InAddr>ReadBorder)
		{
			if (!UnpReadBuf())
				break;
		}
		if (((WrPtr-UnpPtr) & MAXWINMASK)<260 && WrPtr!=UnpPtr)
		{
			UnpWriteBuf();
			if (WrittenFileSize>DestUnpSize)
				return;
			if (Suspended)
			{
				FileExtracted=false;
				return;
			}
		}
		if (UnpBlockType==BLOCK_PPM)
		{
			int Ch=PPM.DecodeChar();
			if (Ch==-1)
			{
				PPM.CleanUp();

				// turn off PPM compression mode in case of error, so UnRAR will
				// call PPM.DecodeInit in case it needs to turn it on back later.
				UnpBlockType=BLOCK_LZ;
				break;
			}
			if (Ch==PPMEscChar)
			{
				int NextCh=PPM.DecodeChar();
				if (NextCh==0)
				{
					if (!ReadTables())
						break;
					continue;
				}
				if (NextCh==2 || NextCh==-1)
					break;
				if (NextCh==3)
				{
					if (!ReadVMCodePPM())
						break;
					continue;
				}
				if (NextCh==4)
				{
					unsigned int Distance=0,Length;
					Length = 0; // avoids warning
					bool Failed=false;
					for (int I=0;I<4 && !Failed;I++)
					{
						int Ch=PPM.DecodeChar();
						if (Ch==-1)
							Failed=true;
						else
							if (I==3)
								Length=(byte)Ch;
							else
								Distance=(Distance<<8)+(byte)Ch;
					}
					if (Failed)
						break;

#ifdef _MSC_VER
	// avoid a warning about uninitialized 'Length' variable
	#pragma warning( disable : 4701 )
#endif
					CopyString(Length+32,Distance+2);
					continue;
				}
				if (NextCh==5)
				{
					int Length=PPM.DecodeChar();
					if (Length==-1)
						break;
					CopyString(Length+4,1);
					continue;
				}
			}
			Window[UnpPtr++]=Ch;
			continue;
		}

		int Number=DecodeNumber((struct Decode *)&LD);
		if (Number<256)
		{
			Window[UnpPtr++]=(byte)Number;
			continue;
		}
		if (Number>=271)
		{
			int Length=LDecode[Number-=271]+3;
			if ((Bits=LBits[Number])>0)
			{
				Length+=getbits()>>(16-Bits);
				addbits(Bits);
			}

			int DistNumber=DecodeNumber((struct Decode *)&DD);
			unsigned int Distance=DDecode[DistNumber]+1;
			if ((Bits=DBits[DistNumber])>0)
			{
				if (DistNumber>9)
				{
					if (Bits>4)
					{
						Distance+=((getbits()>>(20-Bits))<<4);
						addbits(Bits-4);
					}
					if (LowDistRepCount>0)
					{
						LowDistRepCount--;
						Distance+=PrevLowDist;
					}
					else
					{
						int LowDist=DecodeNumber((struct Decode *)&LDD);
						if (LowDist==16)
						{
							LowDistRepCount=LOW_DIST_REP_COUNT-1;
							Distance+=PrevLowDist;
						}
						else
						{
							Distance+=LowDist;
							PrevLowDist=LowDist;
						}
					}
				}
				else
				{
					Distance+=getbits()>>(16-Bits);
					addbits(Bits);
				}
			}

			if (Distance>=0x2000)
			{
				Length++;
				if (Distance>=0x40000L)
					Length++;
			}

			InsertOldDist(Distance);
			InsertLastMatch(Length,Distance);
			CopyString(Length,Distance);
			continue;
		}
		if (Number==256)
		{
			if (!ReadEndOfBlock())
				break;
			continue;
		}
		if (Number==257)
		{
			if (!ReadVMCode())
				break;
			continue;
		}
		if (Number==258)
		{
			if (LastLength!=0)
				CopyString(LastLength,LastDist);
			continue;
		}
		if (Number<263)
		{
			int DistNum=Number-259;
			unsigned int Distance=OldDist[DistNum];
			for (int I=DistNum;I>0;I--)
				OldDist[I]=OldDist[I-1];
			OldDist[0]=Distance;

			int LengthNumber=DecodeNumber((struct Decode *)&RD);
			int Length=LDecode[LengthNumber]+2;
			if ((Bits=LBits[LengthNumber])>0)
			{
				Length+=getbits()>>(16-Bits);
				addbits(Bits);
			}
			InsertLastMatch(Length,Distance);
			CopyString(Length,Distance);
			continue;
		}
		if (Number<272)
		{
			unsigned int Distance=SDDecode[Number-=263]+1;
			if ((Bits=SDBits[Number])>0)
			{
				Distance+=getbits()>>(16-Bits);
				addbits(Bits);
			}
			InsertOldDist(Distance);
			InsertLastMatch(2,Distance);
			CopyString(2,Distance);
			continue;
		}
	}
	UnpWriteBuf();
}


bool Unpack::ReadEndOfBlock()
{
	unsigned int BitField=getbits();
	bool NewTable,NewFile=false;
	if (BitField & 0x8000)
	{
		NewTable=true;
		addbits(1);
	}
	else
	{
		NewFile=true;
		NewTable=(BitField & 0x4000);
		addbits(2);
	}
	TablesRead=!NewTable;
	return !(NewFile || NewTable && !ReadTables());
}


bool Unpack::ReadVMCode()
{
	unsigned int FirstByte=getbits()>>8;
	addbits(8);
	int Length=(FirstByte & 7)+1;
	if (Length==7)
	{
		Length=(getbits()>>8)+7;
		addbits(8);
	}
	else
		if (Length==8)
		{
			Length=getbits();
			addbits(16);
		}
	VMCode.Alloc( Length );
	for (int I=0;I<Length;I++)
	{
		if (InAddr>=ReadTop-1 && !UnpReadBuf() && I<Length-1)
			return(false);
		VMCode[I]=getbits()>>8;
		addbits(8);
	}
	return(AddVMCode(FirstByte,&VMCode[0],Length));
}


bool Unpack::ReadVMCodePPM()
{
	unsigned int FirstByte=PPM.DecodeChar();
	if ((int)FirstByte==-1)
		return(false);
	int Length=(FirstByte & 7)+1;
	if (Length==7)
	{
		int B1=PPM.DecodeChar();
		if (B1==-1)
			return(false);
		Length=B1+7;
	}
	else
		if (Length==8)
		{
			int B1=PPM.DecodeChar();
			if (B1==-1)
				return(false);
			int B2=PPM.DecodeChar();
			if (B2==-1)
				return(false);
			Length=B1*256+B2;
		}
	VMCode.Alloc( Length );
	for (int I=0;I<Length;I++)
	{
		int Ch=PPM.DecodeChar();
		if (Ch==-1)
			return(false);
		VMCode[I]=Ch;
	}
	return(AddVMCode(FirstByte,&VMCode[0],Length));
}


bool Unpack::AddVMCode(unsigned int FirstByte,byte *Code,int CodeSize)
{
	// TODO: auto clear object to free bit input on normal return?
	Inp.InitBitInput();
	memcpy(Inp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize));
	VMCode.Reset();
	VM.Init();
	VM.handle_mem_error( ErrHandler );

	uint FiltPos;
	if (FirstByte & 0x80)
	{
		FiltPos=RarVM::ReadData(Inp);
		if (FiltPos==0)
			InitFilters();
		else
			FiltPos--;
	}
	else
		FiltPos=LastFilter; // use the same filter as last time

	if (FiltPos>Filters.Size() || FiltPos>OldFilterLengths.Size())
		return(false);
	LastFilter=FiltPos;
	bool NewFilter=(FiltPos==Filters.Size());

	delete LastStackFilter;
	LastStackFilter = NULL;
	UnpackFilter *StackFilter=new UnpackFilter(&ErrHandler);
	LastStackFilter = StackFilter;
	if ( !StackFilter )
		ErrHandler.MemoryError();

	UnpackFilter *Filter;
	if (NewFilter) // new filter code, never used before since VM reset
	{
		// too many different filters, corrupt archive
		if (FiltPos>1024)
			return(false);

		Filters.Add(1);
		Filters[Filters.Size()-1]=Filter=new UnpackFilter(&ErrHandler);
		if ( !Filter )
			ErrHandler.MemoryError();
		StackFilter->ParentFilter=Filters.Size()-1;
		OldFilterLengths.Add(1);
		Filter->ExecCount=0;
	}
	else  // filter was used in the past
	{
		Filter=Filters[FiltPos];
		StackFilter->ParentFilter=FiltPos;
		Filter->ExecCount++;
	}

	int EmptyCount=0;
	{
		for (int I=0;I<PrgStack.Size();I++)
		{
			PrgStack[I-EmptyCount]=PrgStack[I];
			if (PrgStack[I]==NULL)
				EmptyCount++;
			if (EmptyCount>0)
				PrgStack[I]=NULL;
		}
	}
	if (EmptyCount==0)
	{
		PrgStack.Add(1);
		EmptyCount=1;
	}
	int StackPos=PrgStack.Size()-EmptyCount;
	PrgStack[StackPos]=StackFilter;
	LastStackFilter = NULL;
	StackFilter->ExecCount=Filter->ExecCount;

	uint BlockStart=RarVM::ReadData(Inp);
	if (FirstByte & 0x40)
		BlockStart+=258;
	StackFilter->BlockStart=(BlockStart+UnpPtr)&MAXWINMASK;
	if (FirstByte & 0x20)
		StackFilter->BlockLength=RarVM::ReadData(Inp);
	else
		StackFilter->BlockLength=FiltPos<OldFilterLengths.Size() ? OldFilterLengths[FiltPos]:0;
	StackFilter->NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MAXWINMASK)<=BlockStart;

//  DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart);

	OldFilterLengths[FiltPos]=StackFilter->BlockLength;

	memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR));
	StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR;
	StackFilter->Prg.InitR[4]=StackFilter->BlockLength;
	StackFilter->Prg.InitR[5]=StackFilter->ExecCount;

	if (FirstByte & 0x10)   // set registers to optional parameters if any
	{
		unsigned int InitMask=Inp.fgetbits()>>9;
		Inp.faddbits(7);
		for (int I=0;I<7;I++)
			if (InitMask & (1<<I))
				StackFilter->Prg.InitR[I]=RarVM::ReadData(Inp);
	}

	if (NewFilter)
	{
		uint VMCodeSize=RarVM::ReadData(Inp);
		if (VMCodeSize>=0x10000 || VMCodeSize==0)
			return(false);
		VMCode.Alloc( VMCodeSize );
		for (int I=0;I<VMCodeSize;I++)
		{
			if (Inp.Overflow(3))
				return(false);
			VMCode[I]=Inp.fgetbits()>>8;
			Inp.faddbits(8);
		}
		VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg);
		VMCode.Reset();
	}
	StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0];
	StackFilter->Prg.CmdCount=Filter->Prg.CmdCount;

	int StaticDataSize=Filter->Prg.StaticData.Size();
	if (StaticDataSize>0 && StaticDataSize<VM_GLOBALMEMSIZE)
	{
		// read statically defined data contained in DB commands
		StackFilter->Prg.StaticData.Add(StaticDataSize);
		memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize);
	}

	if (StackFilter->Prg.GlobalData.Size()<VM_FIXEDGLOBALSIZE)
	{
		StackFilter->Prg.GlobalData.Reset();
		StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE);
	}
	byte *GlobalData=&StackFilter->Prg.GlobalData[0];
	for (int I=0;I<7;I++)
		VM.SetLowEndianValue((uint *)&GlobalData[I*4],StackFilter->Prg.InitR[I]);
	VM.SetLowEndianValue((uint *)&GlobalData[0x1c],StackFilter->BlockLength);
	VM.SetLowEndianValue((uint *)&GlobalData[0x20],0);
	VM.SetLowEndianValue((uint *)&GlobalData[0x2c],StackFilter->ExecCount);
	memset(&GlobalData[0x30],0,16);

	if (FirstByte & 8) // put data block passed as parameter if any
	{
		if (Inp.Overflow(3))
			return(false);
		uint DataSize=RarVM::ReadData(Inp);
		if (DataSize>VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE)
			return(false);
		unsigned int CurSize=StackFilter->Prg.GlobalData.Size();
		if (CurSize<DataSize+VM_FIXEDGLOBALSIZE)
			StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize);
		byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE];
		for (int I=0;I<DataSize;I++)
		{
			if (Inp.Overflow(3))
				return(false);
			GlobalData[I]=Inp.fgetbits()>>8;
			Inp.faddbits(8);
		}
	}
	Inp.InitBitInput();
	return(true);
}


bool Unpack::UnpReadBuf()
{
	int DataSize=ReadTop-InAddr;
	if (DataSize<0)
		return(false);
	if (InAddr>BitInput::MAX_SIZE/2)
	{
		if (DataSize>0)
			memmove(InBuf,InBuf+InAddr,DataSize);
		InAddr=0;
		ReadTop=DataSize;
	}
	else
		DataSize=ReadTop;
	int ReadCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf);
	if (ReadCode>0)
		ReadTop+=ReadCode;
	ReadBorder=ReadTop-30;
	return(ReadCode!=-1);
}


void Unpack::UnpWriteBuf()
{
	unsigned int WrittenBorder=WrPtr;
	unsigned int WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK;
	for (int I=0;I<PrgStack.Size();I++)
	{
		UnpackFilter *flt=PrgStack[I];
		if (flt==NULL)
			continue;
		if (flt->NextWindow)
		{
			flt->NextWindow=false;
			continue;
		}
		unsigned int BlockStart=flt->BlockStart;
		unsigned int BlockLength=flt->BlockLength;
		if (((BlockStart-WrittenBorder)&MAXWINMASK)<WriteSize)
		{
			if (WrittenBorder!=BlockStart)
			{
				UnpWriteArea(WrittenBorder,BlockStart);
				WrittenBorder=BlockStart;
				WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK;
			}
			if (BlockLength<=WriteSize)
			{
				unsigned int BlockEnd=(BlockStart+BlockLength)&MAXWINMASK;
				if (BlockStart<BlockEnd || BlockEnd==0)
					VM.SetMemory(0,Window+BlockStart,BlockLength);
				else
				{
					unsigned int FirstPartLength=MAXWINSIZE-BlockStart;
					VM.SetMemory(0,Window+BlockStart,FirstPartLength);
					VM.SetMemory(FirstPartLength,Window,BlockEnd);
				}

				VM_PreparedProgram *ParentPrg=&Filters[flt->ParentFilter]->Prg;
				VM_PreparedProgram *Prg=&flt->Prg;

				if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
				{
					// copy global data from previous script execution if any
					Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size());
					memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
				}

				ExecuteCode(Prg);

				if (Prg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
				{
					// save global data for next script execution
					if (ParentPrg->GlobalData.Size()<Prg->GlobalData.Size())
						ParentPrg->GlobalData.Alloc(Prg->GlobalData.Size());
					memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
				}
				else
					ParentPrg->GlobalData.Reset();

				byte *FilteredData=Prg->FilteredData;
				unsigned int FilteredDataSize=Prg->FilteredDataSize;

				delete PrgStack[I];
				PrgStack[I]=NULL;
				while (I+1<PrgStack.Size())
				{
					UnpackFilter *NextFilter=PrgStack[I+1];
					if (NextFilter==NULL || NextFilter->BlockStart!=BlockStart ||
							NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow)
						break;

					// apply several filters to same data block

					VM.SetMemory(0,FilteredData,FilteredDataSize);

					VM_PreparedProgram *ParentPrg=&Filters[NextFilter->ParentFilter]->Prg;
					VM_PreparedProgram *NextPrg=&NextFilter->Prg;

					if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
					{
						// copy global data from previous script execution if any
						NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size());
						memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
					}

					ExecuteCode(NextPrg);

					if (NextPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE)
					{
					// save global data for next script execution
						if (ParentPrg->GlobalData.Size()<NextPrg->GlobalData.Size())
							ParentPrg->GlobalData.Alloc(NextPrg->GlobalData.Size());
						memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE);
					}
					else
						ParentPrg->GlobalData.Reset();

					FilteredData=NextPrg->FilteredData;
					FilteredDataSize=NextPrg->FilteredDataSize;
					I++;
					delete PrgStack[I];
					PrgStack[I]=NULL;
				}
				UnpIO->UnpWrite(FilteredData,FilteredDataSize);
				UnpSomeRead=true;
				WrittenFileSize+=FilteredDataSize;
				WrittenBorder=BlockEnd;
				WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK;
			}
			else
			{
				for (int J=I;J<PrgStack.Size();J++)
				{
					UnpackFilter *flt=PrgStack[J];
					if (flt!=NULL && flt->NextWindow)
						flt->NextWindow=false;
				}
				WrPtr=WrittenBorder;
				return;
			}
		}
	}

	UnpWriteArea(WrittenBorder,UnpPtr);
	WrPtr=UnpPtr;
}


void Unpack::ExecuteCode(VM_PreparedProgram *Prg)
{
	if (Prg->GlobalData.Size()>0)
	{
		Prg->InitR[6]=int64to32(WrittenFileSize);
		VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize));
		VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>31>>1));
		VM.Execute(Prg);
	}
}


void Unpack::UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr)
{
	if (EndPtr!=StartPtr)
		UnpSomeRead=true;
	if (EndPtr<StartPtr)
	{
		UnpWriteData(&Window[StartPtr],-StartPtr & MAXWINMASK);
		UnpWriteData(Window,EndPtr);
		UnpAllBuf=true;
	}
	else
		UnpWriteData(&Window[StartPtr],EndPtr-StartPtr);
}


void Unpack::UnpWriteData(byte *Data,int Size)
{
	if (WrittenFileSize>=DestUnpSize)
		return;
	int WriteSize=Size;
	Int64 LeftToWrite=DestUnpSize-WrittenFileSize;
	if (WriteSize>LeftToWrite)
		WriteSize=int64to32(LeftToWrite);
	UnpIO->UnpWrite(Data,WriteSize);
	WrittenFileSize+=Size;
}


bool Unpack::ReadTables()
{
	byte BitLength[BC];
	unsigned char Table[HUFF_TABLE_SIZE];
	if (InAddr>ReadTop-25)
		if (!UnpReadBuf())
			return(false);
	faddbits((8-InBit)&7);
	unsigned int BitField=fgetbits();
	if (BitField & 0x8000)
	{
		UnpBlockType=BLOCK_PPM;
		return(PPM.DecodeInit(this,PPMEscChar));
	}
	UnpBlockType=BLOCK_LZ;

	PrevLowDist=0;
	LowDistRepCount=0;

	if (!(BitField & 0x4000))
		memset(UnpOldTable,0,sizeof(UnpOldTable));
	faddbits(2);
	{
		for (int I=0;I<BC;I++)
		{
			int Length=(byte)(fgetbits() >> 12);
			faddbits(4);
			if (Length==15)
			{
				int ZeroCount=(byte)(fgetbits() >> 12);
				faddbits(4);
				if (ZeroCount==0)
					BitLength[I]=15;
				else
				{
					ZeroCount+=2;
					while (ZeroCount-- > 0 && I<sizeof(BitLength)/sizeof(BitLength[0]))
						BitLength[I++]=0;
					I--;
				}
			}
			else
				BitLength[I]=Length;
		}
	}
	MakeDecodeTables(BitLength,(struct Decode *)&BD,BC);

	const int TableSize=HUFF_TABLE_SIZE;
	for (int I=0;I<TableSize;)
	{
		if (InAddr>ReadTop-5)
			if (!UnpReadBuf())
				return(false);
		int Number=DecodeNumber((struct Decode *)&BD);
		if (Number<16)
		{
			Table[I]=(Number+UnpOldTable[I]) & 0xf;
			I++;
		}
		else
			if (Number<18)
			{
				int N;
				if (Number==16)
				{
					N=(fgetbits() >> 13)+3;
					faddbits(3);
				}
				else
				{
					N=(fgetbits() >> 9)+11;
					faddbits(7);
				}
				while (N-- > 0 && I<TableSize)
				{
					Table[I]=Table[I-1];
					I++;
				}
			}
			else
			{
				int N;
				if (Number==18)
				{
					N=(fgetbits() >> 13)+3;
					faddbits(3);
				}
				else
				{
					N=(fgetbits() >> 9)+11;
					faddbits(7);
				}
				while (N-- > 0 && I<TableSize)
					Table[I++]=0;
			}
	}
	TablesRead=true;
	if (InAddr>ReadTop)
		return(false);
	MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC);
	MakeDecodeTables(&Table[NC],(struct Decode *)&DD,DC);
	MakeDecodeTables(&Table[NC+DC],(struct Decode *)&LDD,LDC);
	MakeDecodeTables(&Table[NC+DC+LDC],(struct Decode *)&RD,RC);
	memcpy(UnpOldTable,Table,sizeof(UnpOldTable));
	return(true);
}


void Unpack::UnpInitData(int Solid)
{
	if (!Solid)
	{
		TablesRead=false;
		memset(OldDist,0,sizeof(OldDist));
		OldDistPtr=0;
		LastDist=LastLength=0;
//    memset(Window,0,MAXWINSIZE);
		memset(UnpOldTable,0,sizeof(UnpOldTable));
		memset(&LD,0,sizeof(LD));
		memset(&DD,0,sizeof(DD));
		memset(&LDD,0,sizeof(LDD));
		memset(&RD,0,sizeof(RD));
		memset(&BD,0,sizeof(BD));
		UnpPtr=WrPtr=0;
		PPMEscChar=2;
		UnpBlockType=BLOCK_LZ;

		InitFilters();
	}
	InitBitInput();
	WrittenFileSize=0;
	ReadTop=0;
	ReadBorder=0;
#ifndef SFX_MODULE
	UnpInitData20(Solid);
#endif
}


void Unpack::InitFilters()
{
	delete LastStackFilter;
	LastStackFilter = NULL;
	
	OldFilterLengths.Reset();
	LastFilter=0;
	{
		for (int I=0;I<Filters.Size();I++)
			delete Filters[I];
	}
	Filters.Reset();
	for (int I=0;I<PrgStack.Size();I++)
		delete PrgStack[I];
	PrgStack.Reset();
}


void Unpack::MakeDecodeTables(unsigned char *LenTab,struct Decode *Dec,int Size)
{
	int LenCount[16],TmpPos[16],I;
	long M,N;
	memset(LenCount,0,sizeof(LenCount));
	memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));
	for (I=0;I<Size;I++)
		LenCount[LenTab[I] & 0xF]++;

	LenCount[0]=0;
	for (TmpPos[0]=Dec->DecodePos[0]=Dec->DecodeLen[0]=0,N=0,I=1;I<16;I++)
	{
		N=2*(N+LenCount[I]);
		M=N<<(15-I);
		if (M>0xFFFF)
			M=0xFFFF;
		Dec->DecodeLen[I]=(unsigned int)M;
		TmpPos[I]=Dec->DecodePos[I]=Dec->DecodePos[I-1]+LenCount[I-1];
	}

	for (I=0;I<Size;I++)
		if (LenTab[I]!=0)
			Dec->DecodeNum[TmpPos[LenTab[I] & 0xF]++]=I;
	Dec->MaxNum=Size;
}
