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)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>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;IType==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)0) // We set it to 0 also for invalid filters. { uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; FilterSrcMemory.Alloc(BlockLength); byte *Mem=&FilterSrcMemory[0]; if (BlockStartUnpWrite(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;JType!=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;I0) 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=0 RawPut4(Addr+FileSize,Data); } else if (((Addr-FileSize) & 0x80000000)!=0) // Addr>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;CurChannel0) { size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); UnpWriteData(&FragWindow[StartPtr],BlockSize); SizeToWrite-=BlockSize; StartPtr=(StartPtr+BlockSize) & MaxWinMask; } } else if (EndPtr=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>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> 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 && IReadTop-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> 13)+3; Inp.faddbits(3); } else { N=(Inp.fgetbits() >> 9)+11; Inp.faddbits(7); } while (N-- > 0 && IReadTop) 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(); }