688 lines
19 KiB
C++
688 lines
19 KiB
C++
|
void Unpack::Unpack5(bool Solid)
|
||
|
{
|
||
|
FileExtracted=true;
|
||
|
|
||
|
if (!Suspended)
|
||
|
{
|
||
|
UnpInitData(Solid);
|
||
|
if (!UnpReadBuf())
|
||
|
return;
|
||
|
|
||
|
// Check TablesRead5 to be sure that we read tables at least once
|
||
|
// regardless of current block header TablePresent flag.
|
||
|
// So we can safefly use these tables below.
|
||
|
if (!ReadBlockHeader(Inp,BlockHeader) ||
|
||
|
!ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
UnpPtr&=MaxWinMask;
|
||
|
|
||
|
if (Inp.InAddr>=ReadBorder)
|
||
|
{
|
||
|
bool FileDone=false;
|
||
|
|
||
|
// We use 'while', because for empty block containing only Huffman table,
|
||
|
// we'll be on the block border once again just after reading the table.
|
||
|
while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 ||
|
||
|
Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 &&
|
||
|
Inp.InBit>=BlockHeader.BlockBitSize)
|
||
|
{
|
||
|
if (BlockHeader.LastBlockInFile)
|
||
|
{
|
||
|
FileDone=true;
|
||
|
break;
|
||
|
}
|
||
|
if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables))
|
||
|
return;
|
||
|
}
|
||
|
if (FileDone || !UnpReadBuf())
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
|
||
|
{
|
||
|
UnpWriteBuf();
|
||
|
if (WrittenFileSize>DestUnpSize)
|
||
|
return;
|
||
|
if (Suspended)
|
||
|
{
|
||
|
FileExtracted=false;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint MainSlot=DecodeNumber(Inp,&BlockTables.LD);
|
||
|
if (MainSlot<256)
|
||
|
{
|
||
|
if (Fragmented)
|
||
|
FragWindow[UnpPtr++]=(byte)MainSlot;
|
||
|
else
|
||
|
Window[UnpPtr++]=(byte)MainSlot;
|
||
|
continue;
|
||
|
}
|
||
|
if (MainSlot>=262)
|
||
|
{
|
||
|
uint Length=SlotToLength(Inp,MainSlot-262);
|
||
|
|
||
|
uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD);
|
||
|
if (DistSlot<4)
|
||
|
{
|
||
|
DBits=0;
|
||
|
Distance+=DistSlot;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DBits=DistSlot/2 - 1;
|
||
|
Distance+=(2 | (DistSlot & 1)) << DBits;
|
||
|
}
|
||
|
|
||
|
if (DBits>0)
|
||
|
{
|
||
|
if (DBits>=4)
|
||
|
{
|
||
|
if (DBits>4)
|
||
|
{
|
||
|
Distance+=((Inp.getbits32()>>(36-DBits))<<4);
|
||
|
Inp.addbits(DBits-4);
|
||
|
}
|
||
|
uint LowDist=DecodeNumber(Inp,&BlockTables.LDD);
|
||
|
Distance+=LowDist;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Distance+=Inp.getbits32()>>(32-DBits);
|
||
|
Inp.addbits(DBits);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Distance>0x100)
|
||
|
{
|
||
|
Length++;
|
||
|
if (Distance>0x2000)
|
||
|
{
|
||
|
Length++;
|
||
|
if (Distance>0x40000)
|
||
|
Length++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
InsertOldDist(Distance);
|
||
|
LastLength=Length;
|
||
|
if (Fragmented)
|
||
|
FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask);
|
||
|
else
|
||
|
CopyString(Length,Distance);
|
||
|
continue;
|
||
|
}
|
||
|
if (MainSlot==256)
|
||
|
{
|
||
|
UnpackFilter Filter;
|
||
|
if (!ReadFilter(Inp,Filter) || !AddFilter(Filter))
|
||
|
break;
|
||
|
continue;
|
||
|
}
|
||
|
if (MainSlot==257)
|
||
|
{
|
||
|
if (LastLength!=0)
|
||
|
if (Fragmented)
|
||
|
FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask);
|
||
|
else
|
||
|
CopyString(LastLength,OldDist[0]);
|
||
|
continue;
|
||
|
}
|
||
|
if (MainSlot<262)
|
||
|
{
|
||
|
uint DistNum=MainSlot-258;
|
||
|
uint Distance=OldDist[DistNum];
|
||
|
for (uint I=DistNum;I>0;I--)
|
||
|
OldDist[I]=OldDist[I-1];
|
||
|
OldDist[0]=Distance;
|
||
|
|
||
|
uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD);
|
||
|
uint Length=SlotToLength(Inp,LengthSlot);
|
||
|
LastLength=Length;
|
||
|
if (Fragmented)
|
||
|
FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask);
|
||
|
else
|
||
|
CopyString(Length,Distance);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
UnpWriteBuf();
|
||
|
}
|
||
|
|
||
|
|
||
|
uint Unpack::ReadFilterData(BitInput &Inp)
|
||
|
{
|
||
|
uint ByteCount=(Inp.fgetbits()>>14)+1;
|
||
|
Inp.addbits(2);
|
||
|
|
||
|
uint Data=0;
|
||
|
for (uint I=0;I<ByteCount;I++)
|
||
|
{
|
||
|
Data+=(Inp.fgetbits()>>8)<<(I*8);
|
||
|
Inp.addbits(8);
|
||
|
}
|
||
|
return Data;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter)
|
||
|
{
|
||
|
if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16)
|
||
|
if (!UnpReadBuf())
|
||
|
return false;
|
||
|
|
||
|
Filter.BlockStart=ReadFilterData(Inp);
|
||
|
Filter.BlockLength=ReadFilterData(Inp);
|
||
|
if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE)
|
||
|
Filter.BlockLength=0;
|
||
|
|
||
|
Filter.Type=Inp.fgetbits()>>13;
|
||
|
Inp.faddbits(3);
|
||
|
|
||
|
if (Filter.Type==FILTER_DELTA)
|
||
|
{
|
||
|
Filter.Channels=(Inp.fgetbits()>>11)+1;
|
||
|
Inp.faddbits(5);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Unpack::AddFilter(UnpackFilter &Filter)
|
||
|
{
|
||
|
if (Filters.Size()>=MAX_UNPACK_FILTERS)
|
||
|
{
|
||
|
UnpWriteBuf(); // Write data, apply and flush filters.
|
||
|
if (Filters.Size()>=MAX_UNPACK_FILTERS)
|
||
|
InitFilters(); // Still too many filters, prevent excessive memory use.
|
||
|
}
|
||
|
|
||
|
// If distance to filter start is that large that due to circular dictionary
|
||
|
// mode now it points to old not written yet data, then we set 'NextWindow'
|
||
|
// flag and process this filter only after processing that older data.
|
||
|
Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart;
|
||
|
|
||
|
Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask);
|
||
|
Filters.Push(Filter);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Unpack::UnpReadBuf()
|
||
|
{
|
||
|
int DataSize=ReadTop-Inp.InAddr; // Data left to process.
|
||
|
if (DataSize<0)
|
||
|
return false;
|
||
|
BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart;
|
||
|
if (Inp.InAddr>BitInput::MAX_SIZE/2)
|
||
|
{
|
||
|
// If we already processed more than half of buffer, let's move
|
||
|
// remaining data into beginning to free more space for new data
|
||
|
// and ensure that calling function does not cross the buffer border
|
||
|
// even if we did not read anything here. Also it ensures that read size
|
||
|
// is not less than CRYPT_BLOCK_SIZE, so we can align it without risk
|
||
|
// to make it zero.
|
||
|
if (DataSize>0)
|
||
|
memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize);
|
||
|
Inp.InAddr=0;
|
||
|
ReadTop=DataSize;
|
||
|
}
|
||
|
else
|
||
|
DataSize=ReadTop;
|
||
|
int ReadCode=0;
|
||
|
if (BitInput::MAX_SIZE!=DataSize)
|
||
|
ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize);
|
||
|
if (ReadCode>0) // Can be also -1.
|
||
|
ReadTop+=ReadCode;
|
||
|
ReadBorder=ReadTop-30;
|
||
|
BlockHeader.BlockStart=Inp.InAddr;
|
||
|
if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet.
|
||
|
{
|
||
|
// We may need to quit from main extraction loop and read new block header
|
||
|
// and trees earlier than data in input buffer ends.
|
||
|
ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1);
|
||
|
}
|
||
|
return ReadCode!=-1;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Unpack::UnpWriteBuf()
|
||
|
{
|
||
|
size_t WrittenBorder=WrPtr;
|
||
|
size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask;
|
||
|
size_t WriteSizeLeft=FullWriteSize;
|
||
|
bool NotAllFiltersProcessed=false;
|
||
|
for (size_t I=0;I<Filters.Size();I++)
|
||
|
{
|
||
|
// Here we apply filters to data which we need to write.
|
||
|
// We always copy data to another memory block before processing.
|
||
|
// We cannot process them just in place in Window buffer, because
|
||
|
// these data can be used for future string matches, so we must
|
||
|
// preserve them in original form.
|
||
|
|
||
|
UnpackFilter *flt=&Filters[I];
|
||
|
if (flt->Type==FILTER_NONE)
|
||
|
continue;
|
||
|
if (flt->NextWindow)
|
||
|
{
|
||
|
// Here we skip filters which have block start in current data range
|
||
|
// due to address wrap around in circular dictionary, but actually
|
||
|
// belong to next dictionary block. If such filter start position
|
||
|
// is included to current write range, then we reset 'NextWindow' flag.
|
||
|
// In fact we can reset it even without such check, because current
|
||
|
// implementation seems to guarantee 'NextWindow' flag reset after
|
||
|
// buffer writing for all existing filters. But let's keep this check
|
||
|
// just in case. Compressor guarantees that distance between
|
||
|
// filter block start and filter storing position cannot exceed
|
||
|
// the dictionary size. So if we covered the filter block start with
|
||
|
// our write here, we can safely assume that filter is applicable
|
||
|
// to next block on no further wrap arounds is possible.
|
||
|
if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize)
|
||
|
flt->NextWindow=false;
|
||
|
continue;
|
||
|
}
|
||
|
uint BlockStart=flt->BlockStart;
|
||
|
uint BlockLength=flt->BlockLength;
|
||
|
if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSizeLeft)
|
||
|
{
|
||
|
if (WrittenBorder!=BlockStart)
|
||
|
{
|
||
|
UnpWriteArea(WrittenBorder,BlockStart);
|
||
|
WrittenBorder=BlockStart;
|
||
|
WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask;
|
||
|
}
|
||
|
if (BlockLength<=WriteSizeLeft)
|
||
|
{
|
||
|
if (BlockLength>0) // We set it to 0 also for invalid filters.
|
||
|
{
|
||
|
uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask;
|
||
|
|
||
|
FilterSrcMemory.Alloc(BlockLength);
|
||
|
byte *Mem=&FilterSrcMemory[0];
|
||
|
if (BlockStart<BlockEnd || BlockEnd==0)
|
||
|
{
|
||
|
if (Fragmented)
|
||
|
FragWindow.CopyData(Mem,BlockStart,BlockLength);
|
||
|
else
|
||
|
memcpy(Mem,Window+BlockStart,BlockLength);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size_t FirstPartLength=size_t(MaxWinSize-BlockStart);
|
||
|
if (Fragmented)
|
||
|
{
|
||
|
FragWindow.CopyData(Mem,BlockStart,FirstPartLength);
|
||
|
FragWindow.CopyData(Mem+FirstPartLength,0,BlockEnd);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memcpy(Mem,Window+BlockStart,FirstPartLength);
|
||
|
memcpy(Mem+FirstPartLength,Window,BlockEnd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
byte *OutMem=ApplyFilter(Mem,BlockLength,flt);
|
||
|
|
||
|
Filters[I].Type=FILTER_NONE;
|
||
|
|
||
|
if (OutMem!=NULL)
|
||
|
UnpIO->UnpWrite(OutMem,BlockLength);
|
||
|
|
||
|
UnpSomeRead=true;
|
||
|
WrittenFileSize+=BlockLength;
|
||
|
WrittenBorder=BlockEnd;
|
||
|
WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Current filter intersects the window write border, so we adjust
|
||
|
// the window border to process this filter next time, not now.
|
||
|
WrPtr=WrittenBorder;
|
||
|
|
||
|
// Since Filter start position can only increase, we quit processing
|
||
|
// all following filters for this data block and reset 'NextWindow'
|
||
|
// flag for them.
|
||
|
for (size_t J=I;J<Filters.Size();J++)
|
||
|
{
|
||
|
UnpackFilter *flt=&Filters[J];
|
||
|
if (flt->Type!=FILTER_NONE)
|
||
|
flt->NextWindow=false;
|
||
|
}
|
||
|
|
||
|
// Do not write data left after current filter now.
|
||
|
NotAllFiltersProcessed=true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remove processed filters from queue.
|
||
|
size_t EmptyCount=0;
|
||
|
for (size_t I=0;I<Filters.Size();I++)
|
||
|
{
|
||
|
if (EmptyCount>0)
|
||
|
Filters[I-EmptyCount]=Filters[I];
|
||
|
if (Filters[I].Type==FILTER_NONE)
|
||
|
EmptyCount++;
|
||
|
}
|
||
|
if (EmptyCount>0)
|
||
|
Filters.Alloc(Filters.Size()-EmptyCount);
|
||
|
|
||
|
if (!NotAllFiltersProcessed) // Only if all filters are processed.
|
||
|
{
|
||
|
// Write data left after last filter.
|
||
|
UnpWriteArea(WrittenBorder,UnpPtr);
|
||
|
WrPtr=UnpPtr;
|
||
|
}
|
||
|
|
||
|
// We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE
|
||
|
// instead of potentially huge MaxWinSize blocks. It also allows us
|
||
|
// to keep the size of Filters array reasonable.
|
||
|
WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask;
|
||
|
|
||
|
// Choose the nearest among WriteBorder and WrPtr actual written border.
|
||
|
// If border is equal to UnpPtr, it means that we have MaxWinSize data ahead.
|
||
|
if (WriteBorder==UnpPtr ||
|
||
|
WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask))
|
||
|
WriteBorder=WrPtr;
|
||
|
}
|
||
|
|
||
|
|
||
|
byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt)
|
||
|
{
|
||
|
byte *SrcData=Data;
|
||
|
switch(Flt->Type)
|
||
|
{
|
||
|
case FILTER_E8:
|
||
|
case FILTER_E8E9:
|
||
|
{
|
||
|
uint FileOffset=(uint)WrittenFileSize;
|
||
|
|
||
|
const uint FileSize=0x1000000;
|
||
|
byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8;
|
||
|
// DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4"
|
||
|
// to avoid overflow for DataSize<4.
|
||
|
for (uint CurPos=0;CurPos+4<DataSize;)
|
||
|
{
|
||
|
byte CurByte=*(Data++);
|
||
|
CurPos++;
|
||
|
if (CurByte==0xe8 || CurByte==CmpByte2)
|
||
|
{
|
||
|
uint Offset=(CurPos+FileOffset)%FileSize;
|
||
|
uint Addr=RawGet4(Data);
|
||
|
|
||
|
// We check 0x80000000 bit instead of '< 0' comparison
|
||
|
// not assuming int32 presence or uint size and endianness.
|
||
|
if ((Addr & 0x80000000)!=0) // Addr<0
|
||
|
{
|
||
|
if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0
|
||
|
RawPut4(Addr+FileSize,Data);
|
||
|
}
|
||
|
else
|
||
|
if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize
|
||
|
RawPut4(Addr-Offset,Data);
|
||
|
|
||
|
Data+=4;
|
||
|
CurPos+=4;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return SrcData;
|
||
|
case FILTER_ARM:
|
||
|
// 2019-11-15: we turned off ARM filter by default when compressing,
|
||
|
// mostly because it is inefficient for modern 64 bit ARM binaries.
|
||
|
// It was turned on by default in 5.0 - 5.80b3 , so we still need it
|
||
|
// here for compatibility with some of previously created archives.
|
||
|
{
|
||
|
uint FileOffset=(uint)WrittenFileSize;
|
||
|
// DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3"
|
||
|
// to avoid overflow for DataSize<3.
|
||
|
for (uint CurPos=0;CurPos+3<DataSize;CurPos+=4)
|
||
|
{
|
||
|
byte *D=Data+CurPos;
|
||
|
if (D[3]==0xeb) // BL command with '1110' (Always) condition.
|
||
|
{
|
||
|
uint Offset=D[0]+uint(D[1])*0x100+uint(D[2])*0x10000;
|
||
|
Offset-=(FileOffset+CurPos)/4;
|
||
|
D[0]=(byte)Offset;
|
||
|
D[1]=(byte)(Offset>>8);
|
||
|
D[2]=(byte)(Offset>>16);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return SrcData;
|
||
|
case FILTER_DELTA:
|
||
|
{
|
||
|
// Unlike RAR3, we do not need to reject excessive channel
|
||
|
// values here, since RAR5 uses only 5 bits to store channel.
|
||
|
uint Channels=Flt->Channels,SrcPos=0;
|
||
|
|
||
|
FilterDstMemory.Alloc(DataSize);
|
||
|
byte *DstData=&FilterDstMemory[0];
|
||
|
|
||
|
// Bytes from same channels are grouped to continual data blocks,
|
||
|
// so we need to place them back to their interleaving positions.
|
||
|
for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
|
||
|
{
|
||
|
byte PrevByte=0;
|
||
|
for (uint DestPos=CurChannel;DestPos<DataSize;DestPos+=Channels)
|
||
|
DstData[DestPos]=(PrevByte-=Data[SrcPos++]);
|
||
|
}
|
||
|
return DstData;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr)
|
||
|
{
|
||
|
if (EndPtr!=StartPtr)
|
||
|
UnpSomeRead=true;
|
||
|
if (EndPtr<StartPtr)
|
||
|
UnpAllBuf=true;
|
||
|
|
||
|
if (Fragmented)
|
||
|
{
|
||
|
size_t SizeToWrite=(EndPtr-StartPtr) & MaxWinMask;
|
||
|
while (SizeToWrite>0)
|
||
|
{
|
||
|
size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite);
|
||
|
UnpWriteData(&FragWindow[StartPtr],BlockSize);
|
||
|
SizeToWrite-=BlockSize;
|
||
|
StartPtr=(StartPtr+BlockSize) & MaxWinMask;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
if (EndPtr<StartPtr)
|
||
|
{
|
||
|
UnpWriteData(Window+StartPtr,MaxWinSize-StartPtr);
|
||
|
UnpWriteData(Window,EndPtr);
|
||
|
}
|
||
|
else
|
||
|
UnpWriteData(Window+StartPtr,EndPtr-StartPtr);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Unpack::UnpWriteData(byte *Data,size_t Size)
|
||
|
{
|
||
|
if (WrittenFileSize>=DestUnpSize)
|
||
|
return;
|
||
|
size_t WriteSize=Size;
|
||
|
int64 LeftToWrite=DestUnpSize-WrittenFileSize;
|
||
|
if ((int64)WriteSize>LeftToWrite)
|
||
|
WriteSize=(size_t)LeftToWrite;
|
||
|
UnpIO->UnpWrite(Data,WriteSize);
|
||
|
WrittenFileSize+=Size;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Unpack::UnpInitData50(bool Solid)
|
||
|
{
|
||
|
if (!Solid)
|
||
|
TablesRead5=false;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header)
|
||
|
{
|
||
|
Header.HeaderSize=0;
|
||
|
|
||
|
if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7)
|
||
|
if (!UnpReadBuf())
|
||
|
return false;
|
||
|
Inp.faddbits((8-Inp.InBit)&7);
|
||
|
|
||
|
byte BlockFlags=Inp.fgetbits()>>8;
|
||
|
Inp.faddbits(8);
|
||
|
uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count.
|
||
|
|
||
|
if (ByteCount==4)
|
||
|
return false;
|
||
|
|
||
|
Header.HeaderSize=2+ByteCount;
|
||
|
|
||
|
Header.BlockBitSize=(BlockFlags&7)+1;
|
||
|
|
||
|
byte SavedCheckSum=Inp.fgetbits()>>8;
|
||
|
Inp.faddbits(8);
|
||
|
|
||
|
int BlockSize=0;
|
||
|
for (uint I=0;I<ByteCount;I++)
|
||
|
{
|
||
|
BlockSize+=(Inp.fgetbits()>>8)<<(I*8);
|
||
|
Inp.addbits(8);
|
||
|
}
|
||
|
|
||
|
Header.BlockSize=BlockSize;
|
||
|
byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16));
|
||
|
if (CheckSum!=SavedCheckSum)
|
||
|
return false;
|
||
|
|
||
|
Header.BlockStart=Inp.InAddr;
|
||
|
ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1);
|
||
|
|
||
|
Header.LastBlockInFile=(BlockFlags & 0x40)!=0;
|
||
|
Header.TablePresent=(BlockFlags & 0x80)!=0;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables)
|
||
|
{
|
||
|
if (!Header.TablePresent)
|
||
|
return true;
|
||
|
|
||
|
if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25)
|
||
|
if (!UnpReadBuf())
|
||
|
return false;
|
||
|
|
||
|
byte BitLength[BC];
|
||
|
for (uint I=0;I<BC;I++)
|
||
|
{
|
||
|
uint Length=(byte)(Inp.fgetbits() >> 12);
|
||
|
Inp.faddbits(4);
|
||
|
if (Length==15)
|
||
|
{
|
||
|
uint ZeroCount=(byte)(Inp.fgetbits() >> 12);
|
||
|
Inp.faddbits(4);
|
||
|
if (ZeroCount==0)
|
||
|
BitLength[I]=15;
|
||
|
else
|
||
|
{
|
||
|
ZeroCount+=2;
|
||
|
while (ZeroCount-- > 0 && I<ASIZE(BitLength))
|
||
|
BitLength[I++]=0;
|
||
|
I--;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
BitLength[I]=Length;
|
||
|
}
|
||
|
|
||
|
MakeDecodeTables(BitLength,&Tables.BD,BC);
|
||
|
|
||
|
byte Table[HUFF_TABLE_SIZE];
|
||
|
const uint TableSize=HUFF_TABLE_SIZE;
|
||
|
for (uint I=0;I<TableSize;)
|
||
|
{
|
||
|
if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-5)
|
||
|
if (!UnpReadBuf())
|
||
|
return false;
|
||
|
uint Number=DecodeNumber(Inp,&Tables.BD);
|
||
|
if (Number<16)
|
||
|
{
|
||
|
Table[I]=Number;
|
||
|
I++;
|
||
|
}
|
||
|
else
|
||
|
if (Number<18)
|
||
|
{
|
||
|
uint N;
|
||
|
if (Number==16)
|
||
|
{
|
||
|
N=(Inp.fgetbits() >> 13)+3;
|
||
|
Inp.faddbits(3);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
N=(Inp.fgetbits() >> 9)+11;
|
||
|
Inp.faddbits(7);
|
||
|
}
|
||
|
if (I==0)
|
||
|
{
|
||
|
// We cannot have "repeat previous" code at the first position.
|
||
|
// Multiple such codes would shift Inp position without changing I,
|
||
|
// which can lead to reading beyond of Inp boundary in mutithreading
|
||
|
// mode, where Inp.ExternalBuffer disables bounds check and we just
|
||
|
// reserve a lot of buffer space to not need such check normally.
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
while (N-- > 0 && I<TableSize)
|
||
|
{
|
||
|
Table[I]=Table[I-1];
|
||
|
I++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint N;
|
||
|
if (Number==18)
|
||
|
{
|
||
|
N=(Inp.fgetbits() >> 13)+3;
|
||
|
Inp.faddbits(3);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
N=(Inp.fgetbits() >> 9)+11;
|
||
|
Inp.faddbits(7);
|
||
|
}
|
||
|
while (N-- > 0 && I<TableSize)
|
||
|
Table[I++]=0;
|
||
|
}
|
||
|
}
|
||
|
TablesRead5=true;
|
||
|
if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop)
|
||
|
return false;
|
||
|
MakeDecodeTables(&Table[0],&Tables.LD,NC);
|
||
|
MakeDecodeTables(&Table[NC],&Tables.DD,DC);
|
||
|
MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC);
|
||
|
MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Unpack::InitFilters()
|
||
|
{
|
||
|
Filters.SoftReset();
|
||
|
}
|