mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-04-25 23:49:47 +00:00
SevenZipReader
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
Quick reference for commonly used SharpCompress APIs.
|
||||
|
||||
Refer to [INTERFACES.md](INTERFACES.md) for more details.
|
||||
|
||||
## Factory Methods
|
||||
|
||||
### Opening Archives
|
||||
|
||||
@@ -537,7 +537,7 @@ await writer.WriteAsync("file.txt", contentStream, DateTime.Now);
|
||||
| **Zip** | ✅ | ✅ | ✅ | ✅ |
|
||||
| **Tar** | ✅ | ✅ | ✅ | ✅ |
|
||||
| **GZip** | ✅ | ✅ | ✅ | ✅ |
|
||||
| **7Zip** | ✅ | ❌ (sequential only) | ❌ | ❌ |
|
||||
| **7Zip** | ✅ | ❌ (sequential only) | ✅ (sequential only) | ❌ |
|
||||
| **Rar** | ✅ | ✅ | ✅ (read-only) | ❌ |
|
||||
| **Ace** | ❌ | ❌ | ✅ (read-only) | ❌ |
|
||||
| **Arc** | ❌ | ❌ | ✅ (read-only) | ❌ |
|
||||
|
||||
@@ -7,6 +7,7 @@ using SharpCompress.Common;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.SevenZip;
|
||||
|
||||
namespace SharpCompress.Archives.SevenZip;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.Compressors.LZMA.Utilities;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.SevenZip;
|
||||
|
||||
namespace SharpCompress.Archives.SevenZip;
|
||||
|
||||
@@ -18,6 +19,7 @@ public partial class SevenZipArchive
|
||||
ISevenZipAsyncArchive
|
||||
{
|
||||
private ArchiveDatabase? _database;
|
||||
internal ArchiveDatabase? Database => _database;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
@@ -106,111 +108,6 @@ public partial class SevenZipArchive
|
||||
public override long TotalSize =>
|
||||
_database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0;
|
||||
|
||||
internal sealed class SevenZipReader
|
||||
: AbstractReader<SevenZipEntry, SevenZipVolume>,
|
||||
ISevenZipReader,
|
||||
ISevenZipAsyncReader
|
||||
{
|
||||
private readonly SevenZipArchive _archive;
|
||||
private SevenZipEntry? _currentEntry;
|
||||
private Stream? _currentFolderStream;
|
||||
private CFolder? _currentFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Enables internal diagnostics for tests.
|
||||
/// When disabled (default), diagnostics properties return null to avoid exposing internal state.
|
||||
/// </summary>
|
||||
internal bool DiagnosticsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current folder instance used to decide whether the solid folder stream should be reused.
|
||||
/// Only available when <see cref="DiagnosticsEnabled"/> is true.
|
||||
/// </summary>
|
||||
internal object? DiagnosticsCurrentFolder => DiagnosticsEnabled ? _currentFolder : null;
|
||||
|
||||
/// <summary>
|
||||
/// Current shared folder stream instance.
|
||||
/// Only available when <see cref="DiagnosticsEnabled"/> is true.
|
||||
/// </summary>
|
||||
internal Stream? DiagnosticsCurrentFolderStream =>
|
||||
DiagnosticsEnabled ? _currentFolderStream : null;
|
||||
|
||||
internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive)
|
||||
: base(readerOptions, ArchiveType.SevenZip, false) => this._archive = archive;
|
||||
|
||||
public override SevenZipVolume Volume => _archive.Volumes.Single();
|
||||
|
||||
protected override IEnumerable<SevenZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
var entries = _archive.Entries.ToList();
|
||||
stream.Position = 0;
|
||||
foreach (var dir in entries.Where(x => x.IsDirectory))
|
||||
{
|
||||
_currentEntry = dir;
|
||||
yield return dir;
|
||||
}
|
||||
// For solid archives (entries in the same folder share a compressed stream),
|
||||
// we must iterate entries sequentially and maintain the folder stream state
|
||||
// across entries in the same folder to avoid recreating the decompression
|
||||
// stream for each file, which breaks contiguous streaming.
|
||||
foreach (var entry in entries.Where(x => !x.IsDirectory))
|
||||
{
|
||||
_currentEntry = entry;
|
||||
yield return entry;
|
||||
}
|
||||
}
|
||||
|
||||
protected override EntryStream GetEntryStream()
|
||||
{
|
||||
var entry = _currentEntry.NotNull("currentEntry is not null");
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
return CreateEntryStream(Stream.Null);
|
||||
}
|
||||
|
||||
var folder = entry.FilePart.Folder;
|
||||
|
||||
// If folder is null (empty stream entry), return empty stream
|
||||
if (folder is null)
|
||||
{
|
||||
return CreateEntryStream(Stream.Null);
|
||||
}
|
||||
|
||||
// Check if we're starting a new folder - dispose old folder stream if needed
|
||||
if (folder != _currentFolder)
|
||||
{
|
||||
_currentFolderStream?.Dispose();
|
||||
_currentFolderStream = null;
|
||||
_currentFolder = folder;
|
||||
}
|
||||
|
||||
// Create the folder stream once per folder
|
||||
if (_currentFolderStream is null)
|
||||
{
|
||||
_currentFolderStream = _archive._database!.GetFolderStream(
|
||||
_archive.Volumes.Single().Stream,
|
||||
folder!,
|
||||
_archive._database.PasswordProvider
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true)
|
||||
);
|
||||
}
|
||||
|
||||
protected override ValueTask<EntryStream> GetEntryStreamAsync(
|
||||
CancellationToken cancellationToken = default
|
||||
) => new(GetEntryStream());
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_currentFolderStream?.Dispose();
|
||||
_currentFolderStream = null;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WORKAROUND: Forces async operations to use synchronous equivalents.
|
||||
/// This is necessary because the LZMA decoder has bugs in its async implementation
|
||||
|
||||
@@ -7,13 +7,14 @@ using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.SevenZip;
|
||||
|
||||
namespace SharpCompress.Factories;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the foundation factory of 7Zip archive.
|
||||
/// </summary>
|
||||
public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
|
||||
public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory
|
||||
{
|
||||
#region IFactory
|
||||
|
||||
@@ -110,11 +111,18 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
|
||||
SharpCompressStream sharpCompressStream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
{
|
||||
reader = null;
|
||||
return false;
|
||||
}
|
||||
) => base.TryOpenReader(sharpCompressStream, options, out reader);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
|
||||
SevenZipReader.OpenReader(stream, options);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
Stream stream,
|
||||
ReaderOptions? options,
|
||||
CancellationToken cancellationToken = default
|
||||
) => SevenZipReader.OpenAsyncReader(stream, options, cancellationToken);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public static partial class ReaderFactory
|
||||
}
|
||||
|
||||
throw new InvalidFormatException(
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard"
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Arj, Zip, GZip, BZip2, Tar, Rar, SevenZip, LZip, XZ, ZStandard"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public static partial class ReaderFactory
|
||||
}
|
||||
|
||||
throw new InvalidFormatException(
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Ace, Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, Lzw, XZ, ZStandard"
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Ace, Arc, Arj, Zip, GZip, BZip2, Tar, Rar, SevenZip, LZip, Lzw, XZ, ZStandard"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives.SevenZip;
|
||||
namespace SharpCompress.Readers.SevenZip;
|
||||
|
||||
/// <summary>
|
||||
/// Reader for 7Zip archives - supports sequential extraction only.
|
||||
221
src/SharpCompress/Readers/SevenZip/SevenZipReader.cs
Normal file
221
src/SharpCompress/Readers/SevenZip/SevenZipReader.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers.SevenZip;
|
||||
|
||||
/// <summary>
|
||||
/// Public 7Zip reader entry point for sequential extraction.
|
||||
/// </summary>
|
||||
public sealed class SevenZipReader
|
||||
: AbstractReader<SevenZipEntry, SevenZipVolume>,
|
||||
ISevenZipReader,
|
||||
ISevenZipAsyncReader
|
||||
{
|
||||
private readonly SevenZipArchive _archive;
|
||||
private readonly bool _disposeArchive;
|
||||
private SevenZipEntry? _currentEntry;
|
||||
private Stream? _currentFolderStream;
|
||||
private CFolder? _currentFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Enables internal diagnostics for tests.
|
||||
/// When disabled (default), diagnostics properties return null to avoid exposing internal state.
|
||||
/// </summary>
|
||||
internal bool DiagnosticsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current folder instance used to decide whether the solid folder stream should be reused.
|
||||
/// Only available when <see cref="DiagnosticsEnabled"/> is true.
|
||||
/// </summary>
|
||||
internal object? DiagnosticsCurrentFolder => DiagnosticsEnabled ? _currentFolder : null;
|
||||
|
||||
/// <summary>
|
||||
/// Current shared folder stream instance.
|
||||
/// Only available when <see cref="DiagnosticsEnabled"/> is true.
|
||||
/// </summary>
|
||||
internal Stream? DiagnosticsCurrentFolderStream =>
|
||||
DiagnosticsEnabled ? _currentFolderStream : null;
|
||||
internal SevenZipReader(
|
||||
ReaderOptions readerOptions,
|
||||
SevenZipArchive archive,
|
||||
bool disposeArchive = false
|
||||
)
|
||||
: base(readerOptions, ArchiveType.SevenZip, disposeVolume: false)
|
||||
{
|
||||
_archive = archive;
|
||||
_disposeArchive = disposeArchive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a 7Zip reader from a file path.
|
||||
/// </summary>
|
||||
public static ISevenZipReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenReader(new FileInfo(filePath), readerOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a 7Zip reader from a file.
|
||||
/// </summary>
|
||||
public static ISevenZipReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfo.NotNull(nameof(fileInfo));
|
||||
var options = readerOptions ?? ReaderOptions.ForOwnedFile;
|
||||
return OpenReader(fileInfo.OpenRead(), options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a 7Zip reader from a stream.
|
||||
/// </summary>
|
||||
public static ISevenZipReader OpenReader(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
var options = readerOptions ?? ReaderOptions.ForExternalStream;
|
||||
return new SevenZipReader(
|
||||
options,
|
||||
(SevenZipArchive)SevenZipArchive.OpenArchive(stream, options),
|
||||
disposeArchive: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a 7Zip reader from a file path asynchronously.
|
||||
/// </summary>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
string filePath,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenAsyncReader(new FileInfo(filePath), readerOptions, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a 7Zip reader from a file asynchronously.
|
||||
/// </summary>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
fileInfo.NotNull(nameof(fileInfo));
|
||||
return OpenAsyncReader(
|
||||
fileInfo.OpenRead(),
|
||||
readerOptions ?? ReaderOptions.ForOwnedFile,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a 7Zip reader from a stream asynchronously.
|
||||
/// </summary>
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return new((IAsyncReader)OpenReader(stream, readerOptions));
|
||||
}
|
||||
|
||||
public override SevenZipVolume Volume => _archive.Volumes.Single();
|
||||
|
||||
protected override IEnumerable<SevenZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
var entries = _archive.Entries.ToList();
|
||||
stream.Position = 0;
|
||||
foreach (var dir in entries.Where(x => x.IsDirectory))
|
||||
{
|
||||
_currentEntry = dir;
|
||||
yield return dir;
|
||||
}
|
||||
// For solid archives (entries in the same folder share a compressed stream),
|
||||
// we must iterate entries sequentially and maintain the folder stream state
|
||||
// across entries in the same folder to avoid recreating the decompression
|
||||
// stream for each file, which breaks contiguous streaming.
|
||||
foreach (var entry in entries.Where(x => !x.IsDirectory))
|
||||
{
|
||||
_currentEntry = entry;
|
||||
yield return entry;
|
||||
}
|
||||
}
|
||||
|
||||
protected override EntryStream GetEntryStream()
|
||||
{
|
||||
var entry = _currentEntry.NotNull("currentEntry is not null");
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
return CreateEntryStream(Stream.Null);
|
||||
}
|
||||
|
||||
var folder = entry.FilePart.Folder;
|
||||
|
||||
// If folder is null (empty stream entry), return empty stream
|
||||
if (folder is null)
|
||||
{
|
||||
return CreateEntryStream(Stream.Null);
|
||||
}
|
||||
|
||||
// Check if we're starting a new folder - dispose old folder stream if needed
|
||||
if (folder != _currentFolder)
|
||||
{
|
||||
_currentFolderStream?.Dispose();
|
||||
_currentFolderStream = null;
|
||||
_currentFolder = folder;
|
||||
}
|
||||
|
||||
// Create the folder stream once per folder
|
||||
if (_currentFolderStream is null)
|
||||
{
|
||||
var database = _archive.Database.NotNull("database is not loaded");
|
||||
_currentFolderStream = database.GetFolderStream(
|
||||
_archive.Volumes.Single().Stream,
|
||||
folder,
|
||||
database.PasswordProvider
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true)
|
||||
);
|
||||
}
|
||||
|
||||
protected override ValueTask<EntryStream> GetEntryStreamAsync(
|
||||
CancellationToken cancellationToken = default
|
||||
) => new(GetEntryStream());
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_currentFolderStream?.Dispose();
|
||||
_currentFolderStream = null;
|
||||
base.Dispose();
|
||||
if (_disposeArchive)
|
||||
{
|
||||
_archive.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
_currentFolderStream?.Dispose();
|
||||
_currentFolderStream = null;
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
if (_disposeArchive)
|
||||
{
|
||||
await _archive.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Readers;
|
||||
using Xunit;
|
||||
using PublicISevenZipAsyncReader = SharpCompress.Readers.SevenZip.ISevenZipAsyncReader;
|
||||
using PublicSevenZipReader = SharpCompress.Readers.SevenZip.SevenZipReader;
|
||||
|
||||
namespace SharpCompress.Test.SevenZip;
|
||||
|
||||
@@ -39,6 +41,24 @@ public class SevenZipArchiveAsyncTests : ArchiveTests
|
||||
await ExtractAllEntriesSequentiallyAsync(testArchive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SevenZipReader_OpenAsyncReader_ReturnsSevenZipReader()
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z"));
|
||||
await using var reader = await PublicSevenZipReader.OpenAsyncReader(stream);
|
||||
|
||||
Assert.IsAssignableFrom<PublicISevenZipAsyncReader>(reader);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReaderFactory_OpenAsyncReader_SevenZip_ReturnsSevenZipReader()
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z"));
|
||||
await using var reader = await ReaderFactory.OpenAsyncReader(stream);
|
||||
|
||||
Assert.IsAssignableFrom<PublicISevenZipAsyncReader>(reader);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SevenZipArchive_PPMd_AsyncStreamExtraction()
|
||||
{
|
||||
@@ -78,7 +98,7 @@ public class SevenZipArchiveAsyncTests : ArchiveTests
|
||||
|
||||
await using var reader = await archive.ExtractAllEntriesAsync();
|
||||
|
||||
var sevenZipReader = Assert.IsType<SevenZipArchive.SevenZipReader>(reader);
|
||||
var sevenZipReader = Assert.IsType<PublicSevenZipReader>(reader);
|
||||
sevenZipReader.DiagnosticsEnabled = true;
|
||||
|
||||
Stream? currentFolderStreamInstance = null;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives;
|
||||
@@ -7,6 +6,7 @@ using SharpCompress.Common;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.SevenZip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.SevenZip;
|
||||
@@ -63,6 +63,24 @@ public class SevenZipArchiveTests : ArchiveTests
|
||||
public void SevenZipArchive_LZMA2_EXE_PathRead() =>
|
||||
ArchiveFileRead("7Zip.LZMA2.exe", new() { LookForHeader = true }, new SevenZipFactory());
|
||||
|
||||
[Fact]
|
||||
public void SevenZipReader_OpenReader_StreamRead()
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z"));
|
||||
using var reader = SevenZipReader.OpenReader(stream);
|
||||
|
||||
Assert.IsAssignableFrom<ISevenZipReader>(reader);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderFactory_OpenReader_SevenZip_ReturnsSevenZipReader()
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "7Zip.LZMA.7z"));
|
||||
using var reader = ReaderFactory.OpenReader(stream);
|
||||
|
||||
Assert.IsAssignableFrom<ISevenZipReader>(reader);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_LZMA2AES_StreamRead() =>
|
||||
ArchiveStreamRead("7Zip.LZMA2.Aes.7z", new ReaderOptions { Password = "testpassword" });
|
||||
@@ -289,7 +307,8 @@ public class SevenZipArchiveTests : ArchiveTests
|
||||
|
||||
using var reader = archive.ExtractAllEntries();
|
||||
|
||||
var sevenZipReader = Assert.IsType<SevenZipArchive.SevenZipReader>(reader);
|
||||
Assert.IsType<ISevenZipReader>(reader);
|
||||
SevenZipReader sevenZipReader = (SevenZipReader)reader;
|
||||
sevenZipReader.DiagnosticsEnabled = true;
|
||||
|
||||
Stream? currentFolderStreamInstance = null;
|
||||
|
||||
Reference in New Issue
Block a user