Example #1
0
        public IExtractor ExtractFile(UInt64 index, string outputDirectory)
        {
            if (index >= (ulong)_Files.LongLength)
            {
                throw new ArgumentOutOfRangeException($"Index `{index}` is out of range.");
            }

            SevenZipArchiveFile file = _Files[index];

            if (!preProcessFile(outputDirectory, file))
            {
                string fullPath = Path.Combine(outputDirectory, PreserveDirectoryStructure ? file.Name : Path.GetFileName(file.Name));

                // progress provider
                SevenZipProgressProvider szpp = null;
                if (ProgressDelegate != null)
                {
                    szpp = new SevenZipProgressProvider(_Files, new[] { index }, ProgressDelegate);
                }

                // extraction
                Trace.TraceInformation($"Filename: `{file.Name}`, file size: `{file.Size} bytes`.");
                var sx = new SevenZipStreamsExtractor(stream, header.RawHeader.MainStreamsInfo, Password);
                using (Stream fileStream = File.Create(fullPath))
                    sx.Extract((UInt64)file.UnPackIndex, fileStream, szpp);
                if (file.Time != null)
                {
                    File.SetLastWriteTimeUtc(fullPath, (DateTime)file.Time);
                }
            }

            return(this);
        }
Example #2
0
        public IExtractor ExtractFile(UInt64 index, Stream outputStream)
        {
            if (index >= (ulong)_Files.LongLength)
            {
                throw new ArgumentOutOfRangeException($"Index `{index}` is out of range.");
            }
            if (outputStream == null || !outputStream.CanWrite)
            {
                throw new ArgumentException($"Stream `{nameof(outputStream)}` is invalid or cannot be written to.");
            }

            SevenZipArchiveFile file = _Files[index];

            if (file.IsEmpty)
            {
                Trace.TraceWarning($"Filename: {file.Name} is a directory, empty file or anti file, nothing to output to stream.");
            }
            else
            {
                // progress provider
                SevenZipProgressProvider szpp = null;
                if (ProgressDelegate != null)
                {
                    szpp = new SevenZipProgressProvider(_Files, new[] { index }, ProgressDelegate);
                }

                // extraction
                Trace.TraceInformation($"Filename: `{file.Name}`, file size: `{file.Size} bytes`.");
                Trace.TraceInformation("Extracting...");
                var sx = new SevenZipStreamsExtractor(stream, header.RawHeader.MainStreamsInfo, Password);
                sx.Extract((UInt64)file.UnPackIndex, outputStream, szpp);
            }

            return(this);
        }
Example #3
0
        public ICompressor Finalize()
        {
            if (this.stream == null || this.header == null)
            {
                throw new SevenZipException("Compressor object has already been finalized.");
            }

            Trace.TraceInformation($"Compressing files.");
            Trace.Indent();
            try
            {
                buildFilesInfo();

                // index files
                var   streamToFileIndex = new Dictionary <ulong, ulong>();
                ulong streamIndex       = 0;
                for (long i = 0; i < _Files.LongCount(); ++i)
                {
                    if (!_Files[(int)i].IsEmpty)
                    {
                        _Files[(int)i].UnPackIndex       = streamIndex;
                        streamToFileIndex[streamIndex++] = (ulong)i;
                    }
                }

                // progress object
                SevenZipProgressProvider szpp = null;
                if (ProgressDelegate != null)
                {
                    szpp = new SevenZipProgressProvider(_Files, new ulong[0], ProgressDelegate);
                }

                // compress files
                this.header.RawHeader.MainStreamsInfo = new SevenZipHeader.StreamsInfo();
                if (Solid && streamIndex > 1)
                {
                    compressFilesSolid(streamIndex, streamToFileIndex, szpp);
                }
                else
                {
                    compressFilesNonSolid(streamIndex, streamToFileIndex, szpp);
                }

                // write headers
                writeHeaders();
            }
            finally
            {
                Trace.Unindent();
                Trace.TraceInformation("Done compressing files.");
            }

            this.stream = null;
            this.header = null;
            return(this);
        }
Example #4
0
        private void compressFilesNonSolid(ulong numStreams, Dictionary <ulong, ulong> streamToFileIndex, SevenZipProgressProvider progressProvider)
        {
            var sc = new SevenZipStreamsCompressor(stream);

            sc.Method = Compression.Registry.Method.LZMA;

            // actual compression (into a single packed stream per file)
            SevenZipStreamsCompressor.PackedStream[] css = new SevenZipStreamsCompressor.PackedStream[numStreams];
            ulong numPackStreams = 0;

            for (ulong i = 0; i < numStreams; ++i)
            {
                SevenZipArchiveFile file = _Files[(int)streamToFileIndex[i]];

                Trace.TraceInformation($"Compressing `{file.Name}`, Size: `{file.Size} bytes`...");
                using (Stream source = file.Source.Get(FileAccess.Read))
                    css[i] = sc.Compress(source, progressProvider);

                numPackStreams += css[i].NumStreams;
            }

            // build headers
            var streamsInfo = this.header.RawHeader.MainStreamsInfo;

            streamsInfo.PackInfo = new SevenZipHeader.PackInfo()
            {
                NumPackStreams = numPackStreams,
                PackPos        = 0,
                Sizes          = new UInt64[numPackStreams],
                Digests        = new SevenZipHeader.Digests(numPackStreams)
            };
            streamsInfo.UnPackInfo = new SevenZipHeader.UnPackInfo()
            {
                NumFolders = numStreams,
                Folders    = new SevenZipHeader.Folder[numStreams]
            };

            streamsInfo.SubStreamsInfo = new SevenZipHeader.SubStreamsInfo(streamsInfo.UnPackInfo)
            {
                NumUnPackStreamsInFolders = Enumerable.Repeat((UInt64)1, (int)numStreams).ToArray(),
                NumUnPackStreamsTotal     = numStreams,
                UnPackSizes = new List <UInt64>((int)numStreams),
                Digests     = new SevenZipHeader.Digests(numStreams)
            };

            for (ulong i = 0, k = 0; i < numStreams; ++i)
            {
                for (ulong j = 0; j < css[i].NumStreams; ++j, ++k)
                {
                    streamsInfo.PackInfo.Sizes[k]        = css[i].Sizes[j];
                    streamsInfo.PackInfo.Digests.CRCs[k] = css[i].CRCs[j];
                }
                streamsInfo.SubStreamsInfo.UnPackSizes.Add((UInt64)css[i].Folder.GetUnPackSize());
                streamsInfo.SubStreamsInfo.Digests.CRCs[i] = css[i].Folder.UnPackCRC;
                css[i].Folder.UnPackCRC           = null;
                streamsInfo.UnPackInfo.Folders[i] = css[i].Folder;
            }
        }
Example #5
0
        private void compressFilesSolid(ulong numStreams, Dictionary <ulong, ulong> streamToFileIndex, SevenZipProgressProvider progressProvider)
        {
            var sc = new SevenZipStreamsCompressor(stream);

            sc.Method = Compression.Registry.Method.LZMA;

            Trace.TraceInformation($"Compressing `{numStreams} files` into a solid block...");

            // actual compression using a sequence file stream and stream compressor
            var inputStream = new MultiFileStream(
                FileAccess.Read,
                streamToFileIndex.Select(sfi => _Files[(int)sfi.Value].Source).ToArray());

            SevenZipStreamsCompressor.PackedStream cs = sc.Compress(inputStream, progressProvider);

            // build headers
            var streamsInfo = this.header.RawHeader.MainStreamsInfo;

            streamsInfo.PackInfo = new SevenZipHeader.PackInfo()
            {
                NumPackStreams = cs.NumStreams,
                PackPos        = 0,
                Sizes          = cs.Sizes,
                Digests        = new SevenZipHeader.Digests(cs.NumStreams)
                {
                    CRCs = cs.CRCs
                }
            };
            streamsInfo.UnPackInfo = new SevenZipHeader.UnPackInfo()
            {
                NumFolders = 1,
                Folders    = new SevenZipHeader.Folder[1]
                {
                    cs.Folder
                }
            };
            streamsInfo.UnPackInfo.Folders[0].UnPackCRC = null;
            streamsInfo.SubStreamsInfo = new SevenZipHeader.SubStreamsInfo(streamsInfo.UnPackInfo)
            {
                NumUnPackStreamsInFolders = new UInt64[1]
                {
                    numStreams
                },
                NumUnPackStreamsTotal = numStreams,
                UnPackSizes           = new List <UInt64>((int)numStreams),
                Digests = new SevenZipHeader.Digests(numStreams)
            };
            for (ulong i = 0; i < numStreams; ++i)
            {
                streamsInfo.SubStreamsInfo.UnPackSizes.Add((UInt64)inputStream.Sizes[i]);
                streamsInfo.SubStreamsInfo.Digests.CRCs[i] = inputStream.CRCs[i];
            }
        }
        internal PackedStream Compress(Stream inputStream, SevenZipProgressProvider progressProvider)
        {
            // Compression method
            if (!Method.HasValue || !SevenZipMethods.Lookup.ContainsKey(Method.Value))
            {
                throw new SevenZipException("Undefined compression method.");
            }
            var MethodID = SevenZipMethods.Lookup[Method.Value];

            // create compressed stream information structure
            var ps = new PackedStream()
            {
                NumStreams = 1,
                Sizes      = new UInt64[1] {
                    0
                },
                CRCs = new UInt32?[1] {
                    null
                },
                Folder = new SevenZipHeader.Folder()
                {
                    NumCoders  = 1,
                    CodersInfo = new SevenZipHeader.CoderInfo[1]
                    {
                        new SevenZipHeader.CoderInfo()
                        {
                            Attributes    = (Byte)MethodID.Raw.Length,
                            CodecId       = MethodID.Raw.ToArray(),
                            NumInStreams  = 1,
                            NumOutStreams = 1
                        }
                    },
                    NumInStreamsTotal  = 1,
                    NumOutStreamsTotal = 1,
                    PackedIndices      = new UInt64[1] {
                        0
                    },
                    UnPackSizes = new UInt64[1] {
                        0
                    },
                    UnPackCRC = 0
                }
            };

            // remember current offsets
            long outStreamStartOffset = this.stream.Position;
            long inStreamStartOffset  = inputStream.Position;

            // encode while calculating CRCs
            using (var inCRCStream = new CRCStream(inputStream))
                using (var outCRCStream = new CRCStream(stream))
                {
                    LZMA.CLzmaEnc      encoder      = LZMA.LzmaEnc_Create(LZMA.ISzAlloc.SmallAlloc);
                    LZMA.CLzmaEncProps encoderProps = LZMA.CLzmaEncProps.LzmaEncProps_Init();
                    LZMA.CSeqOutStream outputHelper;
                    LZMA.CSeqInStream  inputHelper;
                    LZMA.SRes          res = LZMA.SZ_OK;

                    // prepare encoder settings
                    res = encoder.LzmaEnc_SetProps(encoderProps);
                    if (res != LZMA.SZ_OK)
                    {
                        throw new SevenZipException("Error setting LZMA encoder properties.");
                    }
                    byte[] properties         = new byte[LZMA.LZMA_PROPS_SIZE];
                    long   binarySettingsSize = LZMA.LZMA_PROPS_SIZE;
                    res = encoder.LzmaEnc_WriteProperties(properties, ref binarySettingsSize);
                    if (res != LZMA.SZ_OK)
                    {
                        throw new SevenZipException("Error writing LZMA encoder properties.");
                    }
                    if (binarySettingsSize != LZMA.LZMA_PROPS_SIZE)
                    {
                        throw new NotSupportedException();
                    }

                    // read/write helpers
                    outputHelper = new LZMA.CSeqOutStream(
                        (P <byte> buf, long sz) => {
                        outCRCStream.Write(buf.mBuffer, buf.mOffset, checked ((int)sz));
                    });

                    inputHelper = new LZMA.CSeqInStream(
                        (P <byte> buf, long sz) => {
                        return(inCRCStream.Read(buf.mBuffer, buf.mOffset, checked ((int)sz)));
                    });

                    // encode
                    res = encoder.LzmaEnc_Encode(outputHelper, inputHelper, progressProvider, LZMA.ISzAlloc.SmallAlloc, LZMA.ISzAlloc.BigAlloc);
                    if (res != LZMA.SZ_OK)
                    {
                        throw new InvalidOperationException();
                    }

                    // cleanup
                    encoder.LzmaEnc_Destroy(LZMA.ISzAlloc.SmallAlloc, LZMA.ISzAlloc.BigAlloc);

                    // keep settings in header
                    ps.Folder.CodersInfo[0].Attributes    |= (Byte)SevenZipHeader.CoderInfo.AttrHasAttributes;
                    ps.Folder.CodersInfo[0].Properties     = properties.ToArray();
                    ps.Folder.CodersInfo[0].PropertiesSize = (UInt64)ps.Folder.CodersInfo[0].Properties.Length;

                    // store sizes and checksums
                    ps.Folder.UnPackSizes[0] = (UInt64)(inputStream.Position - inStreamStartOffset);
                    ps.Folder.UnPackCRC      = inCRCStream.Result;
                    ps.Sizes[0] = (UInt64)(this.stream.Position - outStreamStartOffset);
                    ps.CRCs[0]  = outCRCStream.Result;

                    // handle progress offsets (in case compressor is called multiple times, with non-solid archives for instance)
                    if (progressProvider != null)
                    {
                        progressProvider.IncreaseOffsetBy((long)ps.Folder.UnPackSizes[0], (long)ps.Sizes[0]);
                        progressProvider.SetProgress(0, 0);
                    }
                }

            return(ps);
        }
Example #7
0
        public IExtractor ExtractFiles(UInt64[] indices, Func <ArchiveFile, Stream> onStreamRequest, Action <ArchiveFile, Stream> onStreamClose = null)
        {
            if (indices.Any(index => index >= (ulong)_Files.LongLength))
            {
                throw new ArgumentOutOfRangeException("An index given in `indices[]` array is out of range.");
            }

            // preprocess files and keep track of streams to decompress
            var   streamToFileIndex = new Dictionary <ulong, ulong>();
            var   streamIndices     = new List <ulong>();
            ulong streamIndex       = 0;

            for (ulong i = 0; i < (ulong)_Files.LongLength; ++i)
            {
                if (!indices.Any() || Array.IndexOf(indices, i) != -1)
                {
                    if (_Files[i].IsEmpty)
                    {
                        using (Stream s = onStreamRequest(_Files[i]))
                            if (s != null)
                            {
                                onStreamClose?.Invoke(_Files[i], s);
                            }
                    }
                    else if (indices.Any())
                    {
                        streamIndices.Add(streamIndex);
                    }
                }
                if (!_Files[i].IsEmpty)
                {
                    streamToFileIndex[streamIndex++] = i;
                }
            }

            // no file to decompress
            if (!streamToFileIndex.Any())
            {
                Trace.TraceWarning("ExtractFiles: No decoding required.");
                return(this);
            }

            // progress provider
            SevenZipProgressProvider szpp = null;

            if (ProgressDelegate != null)
            {
                szpp = new SevenZipProgressProvider(_Files, indices, ProgressDelegate);
            }

            // extraction
            Trace.TraceInformation("Extracting...");
            var sx = new SevenZipStreamsExtractor(stream, header.RawHeader.MainStreamsInfo, Password);

            sx.ExtractMultiple(
                streamIndices.ToArray(),
                (ulong index) => onStreamRequest(_Files[streamToFileIndex[index]]),
                (ulong index, Stream stream) => onStreamClose?.Invoke(_Files[streamToFileIndex[index]], stream),
                szpp);

            return(this);
        }
Example #8
0
        public IExtractor ExtractFiles(UInt64[] indices, string outputDirectory)
        {
            if (indices.Any(index => index >= (ulong)_Files.LongLength))
            {
                throw new ArgumentOutOfRangeException("An index given in `indices[]` array is out of range.");
            }

            // preprocess files and keep track of streams to decompress
            var   streamToFileIndex = new Dictionary <ulong, ulong>();
            var   streamIndices     = new List <ulong>();
            ulong streamIndex       = 0;

            for (ulong i = 0; i < (ulong)_Files.LongLength; ++i)
            {
                if (!indices.Any() || Array.IndexOf(indices, i) != -1)
                {
                    if (!preProcessFile(outputDirectory, _Files[i]))
                    {
                        streamIndices.Add(streamIndex);
                    }
                }
                if (!_Files[i].IsEmpty)
                {
                    streamToFileIndex[streamIndex++] = i;
                }
            }

            // no file to decompress
            if (!streamIndices.Any())
            {
                Trace.TraceWarning("ExtractFiles: No decoding required.");
                return(this);
            }

            // progress provider
            SevenZipProgressProvider szpp = null;

            if (ProgressDelegate != null)
            {
                szpp = new SevenZipProgressProvider(_Files, indices, ProgressDelegate);
            }

            // extraction
            Trace.TraceInformation("Extracting...");
            var sx = new SevenZipStreamsExtractor(stream, header.RawHeader.MainStreamsInfo, Password);

            sx.ExtractMultiple(
                streamIndices.ToArray(),
                (ulong index) => {
                SevenZipArchiveFile file = _Files[streamToFileIndex[index]];
                string fullPath          = Path.Combine(outputDirectory, PreserveDirectoryStructure ? file.Name : Path.GetFileName(file.Name));

                Trace.TraceInformation($"File index {index}, filename: {file.Name}, file size: {file.Size}");
                return(new FileStream(fullPath,
                                      FileMode.Create,
                                      FileAccess.Write,
                                      FileShare.None,
                                      bufferSize));
            },
                (ulong index, Stream stream) => {
                stream.Close();
                SevenZipArchiveFile file = _Files[streamToFileIndex[index]];
                string fullPath          = Path.Combine(outputDirectory, PreserveDirectoryStructure ? file.Name : Path.GetFileName(file.Name));
                if (file.Time != null)
                {
                    File.SetLastWriteTimeUtc(fullPath, (DateTime)file.Time);
                }
            },
                szpp);

            return(this);
        }
Example #9
0
        internal void ExtractMultiple(ulong[] outStreamIndices, Func <ulong, Stream> onStreamRequest, Action <ulong, Stream> onStreamClose, SevenZipProgressProvider progressProvider)
        {
            // simplify
            if (outStreamIndices.Any() && streamsInfo.SubStreamsInfo != null && ((ulong)outStreamIndices.LongLength == streamsInfo.SubStreamsInfo.NumUnPackStreamsTotal))
            {
                outStreamIndices = new ulong[0];
            }

            // sequentially scan through folders and unpacked streams
            ulong index     = 0;
            ulong packIndex = 0;

            for (ulong i = 0; i < streamsInfo.UnPackInfo.NumFolders; ++i)
            {
                bool processed = false;

                // only one unpack stream in folder
                if (streamsInfo.SubStreamsInfo == null || !streamsInfo.SubStreamsInfo.NumUnPackStreamsInFolders.Any())
                {
                    if (!outStreamIndices.Any() || outStreamIndices.Contains(index))
                    {
                        extractMultipleFromFolder(index, null, i, packIndex, onStreamRequest, onStreamClose, progressProvider);
                        processed = true;
                    }

                    ++index;
                }
                else // or multiple streams
                {
                    ulong numStreams = streamsInfo.SubStreamsInfo.NumUnPackStreamsInFolders[i];
                    ulong osiOffset  = index;

                    bool[] matches = new bool[numStreams];
                    for (ulong j = 0; j < numStreams; ++j, ++index)
                    {
                        matches[j] = (!outStreamIndices.Any() || outStreamIndices.Contains(index));
                    }

                    if (matches.Any(m => m == true))
                    {
                        extractMultipleFromFolder(osiOffset, matches, i, packIndex, onStreamRequest, onStreamClose, progressProvider);
                        processed = true;
                    }
                }
                packIndex += streamsInfo.UnPackInfo.Folders[i].NumPackedStreams;

                // add unpack size to offset to progress provider if folder was skipped entirely
                if (!processed && progressProvider != null)
                {
                    progressProvider.IncreaseOffsetBy((long)streamsInfo.UnPackInfo.Folders[i].GetUnPackSize(), 0);
                    progressProvider.SetProgress(0, 0);
                }
            }
        }
Example #10
0
 internal void ExtractAll(Func <ulong, Stream> onStreamRequest, Action <ulong, Stream> onStreamClose, SevenZipProgressProvider progressProvider)
 {
     ExtractMultiple(new ulong[0], onStreamRequest, onStreamClose, progressProvider);
 }
Example #11
0
 internal void Extract(ulong outStreamIndex, Stream outStream, SevenZipProgressProvider progressProvider)
 {
     ExtractMultiple(new ulong[] { outStreamIndex }, (ulong i) => outStream, null, progressProvider);
 }
Example #12
0
        private void extractMultipleFromFolder(ulong outputStreamIndexOffset, bool[] matches, ulong folderIndex, ulong packIndex, Func <ulong, Stream> onStreamRequest, Action <ulong, Stream> onStreamClose, SevenZipProgressProvider progressProvider)
        {
            using (var decoder = createDecoderStream(folderIndex, packIndex))
            {
                ulong unPackSize       = streamsInfo.UnPackInfo.Folders[folderIndex].GetUnPackSize();
                ulong neededUnPackSize = 0;

                // define output stream
                Stream outStream = null;
                if (matches == null)
                {
                    // single stream
                    outStream        = onStreamRequest(outputStreamIndexOffset);
                    neededUnPackSize = unPackSize;
                }
                else
                {
                    // find actual number of needed streams
                    ulong numStreams = streamsInfo.SubStreamsInfo.NumUnPackStreamsInFolders[folderIndex];
                    ulong lastStream = checked ((ulong)Array.LastIndexOf(matches, true));
                    numStreams = lastStream + 1;

                    // create complex multistream
                    MultiStream multi = new MultiStream(numStreams,
                                                        (ulong innerIndex) =>
                    {
                        Stream innerStream = null;
                        if (matches[innerIndex])
                        {
                            innerStream = onStreamRequest(outputStreamIndexOffset + innerIndex);
                            if (innerStream == null)
                            {
                                matches[innerIndex] = false;
                            }
                        }

                        return(innerStream ??
                               new NullStream((long)streamsInfo.SubStreamsInfo.UnPackSizes[(int)(outputStreamIndexOffset + innerIndex)]));
                    },
                                                        (ulong innerIndex, Stream stream) =>
                    {
                        if (onStreamClose != null && matches[innerIndex])
                        {
                            onStreamClose(outputStreamIndexOffset + innerIndex, stream);
                        }
                    });

                    // set sizes in multistream and define needed data size
                    for (ulong i = 0; i < numStreams; i++)
                    {
                        ulong ss = streamsInfo.SubStreamsInfo.UnPackSizes[(int)(outputStreamIndexOffset + i)];
                        multi.Sizes[i]    = (long)ss;
                        neededUnPackSize += ss;
                    }

                    // set new stream as output stream
                    outStream = multi;
                }

                // actual extraction is done here (some decoder streams require knowing output size in advance, like PPMd)
                Util.TransferTo(decoder, outStream, (long)neededUnPackSize, progressProvider);
                if (progressProvider != null)
                {
                    progressProvider.IncreaseOffsetBy((long)unPackSize, 0);
                    progressProvider.SetProgress(0, 0);
                }

                // call stream close delegate if only one stream and delegate present
                if (matches == null)
                {
                    onStreamClose?.Invoke(outputStreamIndexOffset, outStream);
                }
            }
        }