#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif using int64 = System.Int64; using System; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { private void 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,ref BlockHeader) || !ReadTables(Inp,ref BlockHeader, ref 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,ref BlockHeader) || !ReadTables(Inp, ref BlockHeader, ref 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)) << (int)DBits; } if (DBits>0) { if (DBits>=4) { if (DBits>4) { Distance+=((Inp.getbits32()>>(int)(36-DBits))<<4); Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(Inp,BlockTables.LDD); Distance+=LowDist; } else { Distance+=Inp.getbits32()>>(int)(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,ref UnpPtr,MaxWinMask); else CopyString(Length,Distance); continue; } if (MainSlot==256) { UnpackFilter Filter = new UnpackFilter(); if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) break; continue; } if (MainSlot==257) { if (LastLength!=0) if (Fragmented) FragWindow.CopyString(LastLength,OldDist[0],ref 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,ref UnpPtr,MaxWinMask); else CopyString(Length,Distance); continue; } } UnpWriteBuf(); } private uint ReadFilterData(BitInput Inp) { uint ByteCount=(Inp.fgetbits()>>14)+1; Inp.addbits(2); uint Data=0; for (uint I=0;I>8)<<(int)(I*8); Inp.addbits(8); } return Data; } private bool 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=(byte)(Inp.fgetbits()>>13); Inp.faddbits(3); if (Filter.Type==FILTER_DELTA) { Filter.Channels=(byte)((Inp.fgetbits()>>11)+1); Inp.faddbits(5); } return true; } private bool AddFilter(UnpackFilter Filter) { if (Filters.Count>=MAX_UNPACK_FILTERS) { UnpWriteBuf(); // Write data, apply and flush filters. if (Filters.Count>=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.Add(Filter); return true; } private bool UnpReadBuf() { int DataSize=ReadTop-Inp.InAddr; // Data left to process. if (DataSize<0) return false; BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; if (Inp.InAddr>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) //x memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); Buffer.BlockCopy(Inp.InBuf, Inp.InAddr, Inp.InBuf, 0, DataSize); Inp.InAddr=0; ReadTop=DataSize; } else DataSize=ReadTop; int ReadCode=0; if (MAX_SIZE!=DataSize) ReadCode=UnpIO_UnpRead(Inp.InBuf,DataSize,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=Math.Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); } return ReadCode!=-1; } private void UnpWriteBuf() { size_t WrittenBorder=WrPtr; size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; size_t WriteSizeLeft=FullWriteSize; bool NotAllFiltersProcessed=false; //for (size_t I=0;I int for (int I=0;I0) // We set it to 0 also for invalid filters. { uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; //x FilterSrcMemory.Alloc(BlockLength); FilterSrcMemory = EnsureCapacity(FilterSrcMemory, checked((int)BlockLength)); byte[] Mem= FilterSrcMemory; if (BlockStart int for (int J=I;J int int EmptyCount=0; // sharpcompress: size_t -> int for (int I=0;I0) Filters[I-EmptyCount]=Filters[I]; if (Filters[I].Type==FILTER_NONE) EmptyCount++; } if (EmptyCount>0) //Filters.Alloc(Filters.Count-EmptyCount); Filters.RemoveRange(Filters.Count-EmptyCount, 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+Math.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; } private byte[] ApplyFilter(byte[] __d,uint DataSize,UnpackFilter Flt) { int Data = 0; byte[] SrcData=__d; switch(Flt.Type) { case FILTER_E8: case FILTER_E8E9: { uint FileOffset=(uint)WrittenFileSize; const uint FileSize=0x1000000; byte CmpByte2=Flt.Type==FILTER_E8E9 ? (byte)0xe9 : (byte)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,__d,Data); } else if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); __d[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; //x FilterDstMemory.Alloc(DataSize); FilterDstMemory = EnsureCapacity(FilterDstMemory, checked((int)DataSize)); byte[] DstData=FilterDstMemory; // 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); FragWindow.GetBuffer(StartPtr, out var __buffer, out var __offset); UnpWriteData(__buffer, __offset, 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, offset, WriteSize); WrittenFileSize+=Size; } private void UnpInitData50(bool Solid) { if (!Solid) TablesRead5=false; } private bool ReadBlockHeader(BitInput Inp,ref UnpackBlockHeader Header) { Header.HeaderSize=0; if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) if (!UnpReadBuf()) return false; Inp.faddbits((uint)((8-Inp.InBit)&7)); byte BlockFlags=(byte)(Inp.fgetbits()>>8); Inp.faddbits(8); uint ByteCount=(uint)(((BlockFlags>>3)&3)+1); // Block size byte count. if (ByteCount==4) return false; Header.HeaderSize=(int)(2+ByteCount); Header.BlockBitSize=(BlockFlags&7)+1; byte SavedCheckSum=(byte)(Inp.fgetbits()>>8); Inp.faddbits(8); int BlockSize=0; for (uint I=0;I>8)<<(int)(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=Math.Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); Header.LastBlockInFile=(BlockFlags & 0x40)!=0; Header.TablePresent=(BlockFlags & 0x80)!=0; return true; } private bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) { if (!Header.TablePresent) return true; if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) if (!UnpReadBuf()) return false; byte[] BitLength = new byte[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]=(byte)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, (int)NC,Tables.DD,DC); MakeDecodeTables(Table, (int)(NC+DC),Tables.LDD,LDC); MakeDecodeTables(Table, (int)(NC+DC+LDC),Tables.RD,RC); return true; } private void InitFilters() { //Filters.SoftReset(); Filters.Clear(); } } }