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