Example #1
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 #2
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);
                }
            }
        }
        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 ulong[1] {
                    0
                },
                CRCs = new uint?[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 ulong[1] {
                        0
                    },
                    UnPackSizes = new ulong[1] {
                        0
                    },
                    UnPackCRC = 0
                }
            };

            // remember current offsets
            long outStreamStartOffset = 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);
                    var                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 = (ulong)ps.Folder.CodersInfo[0].Properties.Length;

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

                    // 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);
        }