#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 System; using SharpCompress.Common; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal sealed partial class Unpack : BitInput { public Unpack(/* ComprDataIO *DataIO */) //:Inp(true),VMCodeInp(true) : base(true) { _UnpackCtor(); //UnpIO=DataIO; Window=null; Fragmented=false; Suspended=false; UnpAllBuf=false; UnpSomeRead=false; #if RarV2017_RAR_SMP MaxUserThreads=1; UnpThreadPool=CreateThreadPool(); ReadBufMT=null; UnpThreadData=null; #endif MaxWinSize=0; MaxWinMask=0; // Perform initialization, which should be done only once for all files. // It prevents crash if first DoUnpack call is later made with wrong // (true) 'Solid' value. UnpInitData(false); #if !RarV2017_SFX_MODULE // RAR 1.5 decompression initialization UnpInitData15(false); InitHuff(); #endif } // later: may need Dispose() if we support thread pool //Unpack::~Unpack() //{ // InitFilters30(false); // // if (Window!=null) // free(Window); //#if RarV2017_RAR_SMP // DestroyThreadPool(UnpThreadPool); // delete[] ReadBufMT; // delete[] UnpThreadData; //#endif //} private void Init(size_t WinSize,bool Solid) { // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size // will be 0 because of size_t overflow. Let's issue the memory error. if (WinSize==0) //ErrHandler.MemoryError(); throw new InvalidFormatException("invalid window size (possibly due to a rar file with a 4GB being unpacked on a 32-bit platform)"); // Minimum window size must be at least twice more than maximum possible // size of filter block, which is 0x10000 in RAR now. If window size is // smaller, we can have a block with never cleared flt->NextWindow flag // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's // use 0x40000 for extra safety and possible filter area size expansion. const size_t MinAllocSize=0x40000; if (WinSize>16)>0x10000) // Window size must not exceed 4 GB. return; // Archiving code guarantees that window size does not grow in the same // solid stream. So if we are here, we are either creating a new window // or increasing the size of non-solid window. So we could safely reject // current window data without copying them to a new window, though being // extra cautious, we still handle the solid window grow case below. bool Grow=Solid && (Window!=null || Fragmented); // We do not handle growth for existing fragmented window. if (Grow && Fragmented) //throw std::bad_alloc(); throw new InvalidFormatException("Grow && Fragmented"); byte[] NewWindow=Fragmented ? null : new byte[WinSize]; if (NewWindow==null) if (Grow || WinSize<0x1000000) { // We do not support growth for new fragmented window. // Also exclude RAR4 and small dictionaries. //throw std::bad_alloc(); throw new InvalidFormatException("Grow || WinSize<0x1000000"); } else { if (Window!=null) // If allocated by preceding files. { //free(Window); Window=null; } FragWindow.Init(WinSize); Fragmented=true; } if (!Fragmented) { // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access unused areas of sliding dictionary. // sharpcompress: don't need this, freshly allocated above //memset(NewWindow,0,WinSize); // If Window is not NULL, it means that window size has grown. // In solid streams we need to copy data to a new window in such case. // RAR archiving code does not allow it in solid streams now, // but let's implement it anyway just in case we'll change it sometimes. if (Grow) for (size_t I=1;I<=MaxWinSize;I++) NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)]; //if (Window!=null) // free(Window); Window=NewWindow; } MaxWinSize=WinSize; MaxWinMask=MaxWinSize-1; } private void DoUnpack(uint Method,bool Solid) { // Methods <50 will crash in Fragmented mode when accessing NULL Window. // They cannot be called in such mode now, but we check it below anyway // just for extra safety. switch(Method) { #if !RarV2017_SFX_MODULE case 15: // rar 1.5 compression if (!Fragmented) Unpack15(Solid); break; case 20: // rar 2.x compression case 26: // files larger than 2GB if (!Fragmented) Unpack20(Solid); break; #endif #if !RarV2017_RAR5ONLY case 29: // rar 3.x compression if (!Fragmented) throw new NotImplementedException(); break; #endif case 50: // RAR 5.0 compression algorithm. #if RarV2017_RAR_SMP if (MaxUserThreads>1) { // We do not use the multithreaded unpack routine to repack RAR archives // in 'suspended' mode, because unlike the single threaded code it can // write more than one dictionary for same loop pass. So we would need // larger buffers of unknown size. Also we do not support multithreading // in fragmented window mode. if (!Fragmented) { Unpack5MT(Solid); break; } } #endif Unpack5(Solid); break; #if !Rar2017_NOSTRICT default: throw new InvalidFormatException("unknown compression method " + Method); #endif } } private void UnpInitData(bool Solid) { if (!Solid) { Utility.Memset(OldDist, 0, OldDist.Length); OldDistPtr=0; LastDist=LastLength=0; // memset(Window,0,MaxWinSize); //memset(&BlockTables,0,sizeof(BlockTables)); BlockTables = new UnpackBlockTables(); // sharpcompress: no default ctor for struct BlockTables.Init(); UnpPtr=WrPtr=0; WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } // Filters never share several solid files, so we can safely reset them // even in solid archive. InitFilters(); Inp.InitBitInput(); WrittenFileSize=0; ReadTop=0; ReadBorder=0; //memset(&BlockHeader,0,sizeof(BlockHeader)); BlockHeader = new UnpackBlockHeader(); BlockHeader.BlockSize=-1; // '-1' means not defined yet. #if !RarV2017_SFX_MODULE UnpInitData20(Solid); #endif //UnpInitData30(Solid); UnpInitData50(Solid); } // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, private void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec,uint Size) { // Size of alphabet and DecodePos array. Dec.MaxNum=Size; // Calculate how many entries for every bit length in LengthTable we have. uint[] LengthCount = new uint[16]; //memset(LengthCount,0,sizeof(LengthCount)); for (size_t I=0;IDecodeNum,0,Size*sizeof(*Dec->DecodeNum)); Utility.FillFast(Dec.DecodeNum, 0); // Initialize not really used entry for zero length code. Dec.DecodePos[0]=0; // Start code for bit length 1 is 0. Dec.DecodeLen[0]=0; // Right aligned upper limit code for current bit length. uint UpperLimit=0; for (int I=1;I<16;I++) { // Adjust the upper limit code. UpperLimit+=LengthCount[I]; // Left aligned upper limit code. uint LeftAligned=UpperLimit<<(16-I); // Prepare the upper limit code for next bit length. UpperLimit*=2; // Store the left aligned upper limit code. Dec.DecodeLen[I]=(uint)LeftAligned; // Every item of this array contains the sum of all preceding items. // So it contains the start position in code list for every bit length. Dec.DecodePos[I]=Dec.DecodePos[I-1]+LengthCount[I-1]; } // Prepare the copy of DecodePos. We'll modify this copy below, // so we cannot use the original DecodePos. uint[] CopyDecodePos = new uint[Dec.DecodePos.Length]; //memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); Array.Copy(Dec.DecodePos, 0, CopyDecodePos, 0, CopyDecodePos.Length); // For every bit length in the bit length table and so for every item // of alphabet. for (uint I=0;I=Dec.DecodeLen[CurBitLength]) CurBitLength++; // Translation of right aligned bit string to bit length. Dec.QuickLen[Code]=CurBitLength; // Prepare the table for quick translation of position in code list // to position in alphabet. // Calculate the distance from the start code for current bit length. uint Dist=BitField-Dec.DecodeLen[CurBitLength-1]; // Right align the distance. Dist>>=(16-CurBitLength); // Now we can calculate the position in the code list. It is the sum // of first position for current bit length and right aligned distance // between our bit field and start code for current bit length. uint Pos; if (CurBitLength