From 3f9986c13c973f5e9b8e08da8bfb5e8259044a44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 11:47:53 +0000 Subject: [PATCH 1/8] Initial plan From 8b95e0a76d6b387533175730e2895ccd16772d07 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 11:57:01 +0000 Subject: [PATCH 2/8] Standardize on WriteToDirectory naming and add async support Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../Archives/IArchiveExtensions.cs | 98 +++++++++++++++---- tests/SharpCompress.Test/ArchiveTests.cs | 2 +- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index d9701da30..628155995 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Readers; @@ -19,21 +20,38 @@ public static void WriteToDirectory( ExtractionOptions? options = null ) { - using var reader = archive.ExtractAllEntries(); - reader.WriteAllToDirectory(destinationDirectory, options); + // For solid archives (Rar, 7Zip), use the optimized reader-based approach + if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) + { + using var reader = archive.ExtractAllEntries(); + reader.WriteAllToDirectory(destinationDirectory, options); + } + else + { + // For non-solid archives, extract entries directly + foreach (var entry in archive.Entries) + { + if (!entry.IsDirectory) + { + entry.WriteToDirectory(destinationDirectory, options); + } + } + } } /// - /// Extracts the archive to the destination directory. Directories will be created as needed. + /// Extract to specific directory with progress reporting and cancellation support /// /// The archive to extract. - /// The folder to extract into. + /// The folder to extract into. + /// Extraction options. /// Optional progress report callback. /// Optional cancellation token. - public static void ExtractToDirectory( + public static void WriteToDirectory( this IArchive archive, - string destination, - Action? progressReport = null, + string destinationDirectory, + ExtractionOptions? options, + Action? progressReport, CancellationToken cancellationToken = default ) { @@ -51,7 +69,10 @@ public static void ExtractToDirectory( if (entry.IsDirectory) { - var dirPath = Path.Combine(destination, entry.Key.NotNull("Entry Key is null")); + var dirPath = Path.Combine( + destinationDirectory, + entry.Key.NotNull("Entry Key is null") + ); if ( Path.GetDirectoryName(dirPath + "/") is { } emptyDirectory && seenDirectories.Add(dirPath) @@ -62,20 +83,63 @@ public static void ExtractToDirectory( continue; } - // Create each directory if not already created - var path = Path.Combine(destination, entry.Key.NotNull("Entry Key is null")); - if (Path.GetDirectoryName(path) is { } directory) + // Use the entry's WriteToDirectory method which respects ExtractionOptions + entry.WriteToDirectory(destinationDirectory, options); + + // Update progress + bytesRead += entry.Size; + progressReport?.Invoke(bytesRead / (double)totalBytes); + } + } + + /// + /// Extract to specific directory asynchronously with progress reporting and cancellation support + /// + /// The archive to extract. + /// The folder to extract into. + /// Extraction options. + /// Optional progress report callback. + /// Optional cancellation token. + public static async Task WriteToDirectoryAsync( + this IArchive archive, + string destinationDirectory, + ExtractionOptions? options = null, + Action? progressReport = null, + CancellationToken cancellationToken = default + ) + { + // Prepare for progress reporting + var totalBytes = archive.TotalUncompressSize; + var bytesRead = 0L; + + // Tracking for created directories. + var seenDirectories = new HashSet(); + + // Extract + foreach (var entry in archive.Entries) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (entry.IsDirectory) { - if (!Directory.Exists(directory) && !seenDirectories.Contains(directory)) + var dirPath = Path.Combine( + destinationDirectory, + entry.Key.NotNull("Entry Key is null") + ); + if ( + Path.GetDirectoryName(dirPath + "/") is { } emptyDirectory + && seenDirectories.Add(dirPath) + ) { - Directory.CreateDirectory(directory); - seenDirectories.Add(directory); + Directory.CreateDirectory(emptyDirectory); } + continue; } - // Write file - using var fs = File.OpenWrite(path); - entry.WriteTo(fs); + // Use the entry's WriteToDirectoryAsync method which respects ExtractionOptions + await entry + .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken) + .ConfigureAwait(false); // Update progress bytesRead += entry.Size; diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 2f47e1215..916c9e2c0 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -261,7 +261,7 @@ protected void ArchiveExtractToDirectory( testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); using (var archive = ArchiveFactory.Open(new FileInfo(testArchive), readerOptions)) { - archive.ExtractToDirectory(SCRATCH_FILES_PATH); + archive.WriteToDirectory(SCRATCH_FILES_PATH); } VerifyFiles(); } From 0e59bf39f4bb77859416bad08c84bf8ff351b962 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:04:47 +0000 Subject: [PATCH 3/8] Add test for IArchive.WriteToDirectoryAsync Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs index 01fe9ca83..d964ddc71 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs @@ -194,4 +194,18 @@ await entry.WriteToDirectoryAsync( } VerifyFiles(); } + + [Fact] + public async Task Zip_Deflate_Archive_WriteToDirectoryAsync() + { + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"))) + using (var archive = ZipArchive.Open(stream)) + { + await archive.WriteToDirectoryAsync( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); + } + VerifyFiles(); + } } From 54b64a8c3b7956f745bea7b14eb76dd0b9934430 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:06:36 +0000 Subject: [PATCH 4/8] Fix misleading variable name: emptyDirectory -> parentDirectory Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Archives/IArchiveExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 628155995..23af9ecb8 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -74,11 +74,11 @@ public static void WriteToDirectory( entry.Key.NotNull("Entry Key is null") ); if ( - Path.GetDirectoryName(dirPath + "/") is { } emptyDirectory + Path.GetDirectoryName(dirPath + "/") is { } parentDirectory && seenDirectories.Add(dirPath) ) { - Directory.CreateDirectory(emptyDirectory); + Directory.CreateDirectory(parentDirectory); } continue; } @@ -127,11 +127,11 @@ public static async Task WriteToDirectoryAsync( entry.Key.NotNull("Entry Key is null") ); if ( - Path.GetDirectoryName(dirPath + "/") is { } emptyDirectory + Path.GetDirectoryName(dirPath + "/") is { } parentDirectory && seenDirectories.Add(dirPath) ) { - Directory.CreateDirectory(emptyDirectory); + Directory.CreateDirectory(parentDirectory); } continue; } From 2f0eb0bd4b733929397c68881ae21904ff42a0d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:25:36 +0000 Subject: [PATCH 5/8] Replace Action with IProgress for progress reporting Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- .../Archives/IArchiveExtensions.cs | 24 ++++++++++++++----- .../Zip/ZipArchiveAsyncTests.cs | 20 ++++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 23af9ecb8..a0ac9d19c 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -45,13 +45,13 @@ public static void WriteToDirectory( /// The archive to extract. /// The folder to extract into. /// Extraction options. - /// Optional progress report callback. + /// Optional progress reporter for tracking extraction progress. /// Optional cancellation token. public static void WriteToDirectory( this IArchive archive, string destinationDirectory, ExtractionOptions? options, - Action? progressReport, + IProgress? progress, CancellationToken cancellationToken = default ) { @@ -88,7 +88,13 @@ public static void WriteToDirectory( // Update progress bytesRead += entry.Size; - progressReport?.Invoke(bytesRead / (double)totalBytes); + progress?.Report( + new ProgressReport( + entry.Key ?? string.Empty, + bytesRead, + totalBytes + ) + ); } } @@ -98,13 +104,13 @@ public static void WriteToDirectory( /// The archive to extract. /// The folder to extract into. /// Extraction options. - /// Optional progress report callback. + /// Optional progress reporter for tracking extraction progress. /// Optional cancellation token. public static async Task WriteToDirectoryAsync( this IArchive archive, string destinationDirectory, ExtractionOptions? options = null, - Action? progressReport = null, + IProgress? progress = null, CancellationToken cancellationToken = default ) { @@ -143,7 +149,13 @@ await entry // Update progress bytesRead += entry.Size; - progressReport?.Invoke(bytesRead / (double)totalBytes); + progress?.Report( + new ProgressReport( + entry.Key ?? string.Empty, + bytesRead, + totalBytes + ) + ); } } } diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs index d964ddc71..cd93a3c18 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs @@ -208,4 +208,24 @@ await archive.WriteToDirectoryAsync( } VerifyFiles(); } + + [Fact] + public async Task Zip_Deflate_Archive_WriteToDirectoryAsync_WithProgress() + { + var progressReports = new System.Collections.Generic.List(); + var progress = new Progress(report => progressReports.Add(report)); + + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"))) + using (var archive = ZipArchive.Open(stream)) + { + await archive.WriteToDirectoryAsync( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true }, + progress + ); + } + + VerifyFiles(); + Assert.True(progressReports.Count > 0, "Progress reports should be generated"); + } } From c701bbbee3697693bfcf76f81b543df19c14c848 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:31:35 +0000 Subject: [PATCH 6/8] Run CSharpier formatting and document check-format process Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- AGENTS.md | 30 ++++++++++++++++--- .../Archives/IArchiveExtensions.cs | 16 ++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c7b4a5a63..ae36265aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,12 +32,34 @@ SharpCompress is a pure C# compression library supporting multiple archive forma - Use CSharpier for code formatting to ensure consistent style across the project - CSharpier is configured as a local tool in `.config/dotnet-tools.json` -- **To format code, run the task: `format` task (which runs `dotnet csharpier .` from project root)** -- Restore tools with: `dotnet tool restore` -- Format files from the project root with: `dotnet csharpier .` -- Configure your IDE to format on save using CSharpier for the best experience + +### Commands + +1. **Restore tools** (first time only): + ```bash + dotnet tool restore + ``` + +2. **Check if files are formatted correctly** (doesn't modify files): + ```bash + dotnet csharpier check . + ``` + - Exit code 0: All files are properly formatted + - Exit code 1: Some files need formatting (will show which files and differences) + +3. **Format files** (modifies files): + ```bash + dotnet csharpier format . + ``` + - Formats all files in the project to match CSharpier style + - Run from project root directory + +4. **Configure your IDE** to format on save using CSharpier for the best experience + +### Additional Notes - The project also uses `.editorconfig` for editor settings (indentation, encoding, etc.) - Let CSharpier handle code style while `.editorconfig` handles editor behavior +- Always run `dotnet csharpier check .` before committing to verify formatting ## Project Setup and Structure diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index a0ac9d19c..4ab4129bf 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -88,13 +88,7 @@ public static void WriteToDirectory( // Update progress bytesRead += entry.Size; - progress?.Report( - new ProgressReport( - entry.Key ?? string.Empty, - bytesRead, - totalBytes - ) - ); + progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)); } } @@ -149,13 +143,7 @@ await entry // Update progress bytesRead += entry.Size; - progress?.Report( - new ProgressReport( - entry.Key ?? string.Empty, - bytesRead, - totalBytes - ) - ); + progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)); } } } From d39299176476a3fd230bb4b9ed1dc51e95e2be7d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:40:22 +0000 Subject: [PATCH 7/8] Remove CancellationToken from synchronous WriteToDirectory overload Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> --- src/SharpCompress/Archives/IArchiveExtensions.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 4ab4129bf..00aae4686 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -40,19 +40,17 @@ public static void WriteToDirectory( } /// - /// Extract to specific directory with progress reporting and cancellation support + /// Extract to specific directory with progress reporting /// /// The archive to extract. /// The folder to extract into. /// Extraction options. /// Optional progress reporter for tracking extraction progress. - /// Optional cancellation token. public static void WriteToDirectory( this IArchive archive, string destinationDirectory, ExtractionOptions? options, - IProgress? progress, - CancellationToken cancellationToken = default + IProgress? progress ) { // Prepare for progress reporting @@ -65,8 +63,6 @@ public static void WriteToDirectory( // Extract foreach (var entry in archive.Entries) { - cancellationToken.ThrowIfCancellationRequested(); - if (entry.IsDirectory) { var dirPath = Path.Combine( From c2e01798f8bbe63fd4c3568d8e2d594d7c504ae9 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 19 Dec 2025 14:09:01 +0000 Subject: [PATCH 8/8] refactor archive extensions --- .../Archives/IArchiveExtensions.cs | 233 ++++++++++-------- 1 file changed, 128 insertions(+), 105 deletions(-) diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 00aae4686..0d39c6e2c 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; @@ -11,135 +10,159 @@ namespace SharpCompress.Archives; public static class IArchiveExtensions { - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteToDirectory( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options = null - ) + /// The archive to extract. + extension(IArchive archive) { - // For solid archives (Rar, 7Zip), use the optimized reader-based approach - if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) + /// + /// Extract to specific directory with progress reporting + /// + /// The folder to extract into. + /// Extraction options. + /// Optional progress reporter for tracking extraction progress. + public void WriteToDirectory( + string destinationDirectory, + ExtractionOptions? options = null, + IProgress? progress = null + ) { - using var reader = archive.ExtractAllEntries(); - reader.WriteAllToDirectory(destinationDirectory, options); + // For solid archives (Rar, 7Zip), use the optimized reader-based approach + if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) + { + using var reader = archive.ExtractAllEntries(); + reader.WriteAllToDirectory(destinationDirectory, options); + } + else + { + // For non-solid archives, extract entries directly + archive.WriteToDirectoryInternal(destinationDirectory, options, progress); + } } - else + + private void WriteToDirectoryInternal( + string destinationDirectory, + ExtractionOptions? options, + IProgress? progress + ) { - // For non-solid archives, extract entries directly + // Prepare for progress reporting + var totalBytes = archive.TotalUncompressSize; + var bytesRead = 0L; + + // Tracking for created directories. + var seenDirectories = new HashSet(); + + // Extract foreach (var entry in archive.Entries) { - if (!entry.IsDirectory) + if (entry.IsDirectory) { - entry.WriteToDirectory(destinationDirectory, options); + var dirPath = Path.Combine( + destinationDirectory, + entry.Key.NotNull("Entry Key is null") + ); + if ( + Path.GetDirectoryName(dirPath + "/") is { } parentDirectory + && seenDirectories.Add(dirPath) + ) + { + Directory.CreateDirectory(parentDirectory); + } + continue; } - } - } - } - /// - /// Extract to specific directory with progress reporting - /// - /// The archive to extract. - /// The folder to extract into. - /// Extraction options. - /// Optional progress reporter for tracking extraction progress. - public static void WriteToDirectory( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options, - IProgress? progress - ) - { - // Prepare for progress reporting - var totalBytes = archive.TotalUncompressSize; - var bytesRead = 0L; + // Use the entry's WriteToDirectory method which respects ExtractionOptions + entry.WriteToDirectory(destinationDirectory, options); - // Tracking for created directories. - var seenDirectories = new HashSet(); + // Update progress + bytesRead += entry.Size; + progress?.Report( + new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes) + ); + } + } - // Extract - foreach (var entry in archive.Entries) + /// + /// Extract to specific directory asynchronously with progress reporting and cancellation support + /// + /// The folder to extract into. + /// Extraction options. + /// Optional progress reporter for tracking extraction progress. + /// Optional cancellation token. + public async Task WriteToDirectoryAsync( + string destinationDirectory, + ExtractionOptions? options = null, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) { - if (entry.IsDirectory) + // For solid archives (Rar, 7Zip), use the optimized reader-based approach + if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) { - var dirPath = Path.Combine( + using var reader = archive.ExtractAllEntries(); + await reader.WriteAllToDirectoryAsync( destinationDirectory, - entry.Key.NotNull("Entry Key is null") + options, + cancellationToken + ); + } + else + { + // For non-solid archives, extract entries directly + await archive.WriteToDirectoryAsyncInternal( + destinationDirectory, + options, + progress, + cancellationToken ); - if ( - Path.GetDirectoryName(dirPath + "/") is { } parentDirectory - && seenDirectories.Add(dirPath) - ) - { - Directory.CreateDirectory(parentDirectory); - } - continue; } - - // Use the entry's WriteToDirectory method which respects ExtractionOptions - entry.WriteToDirectory(destinationDirectory, options); - - // Update progress - bytesRead += entry.Size; - progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)); } - } - - /// - /// Extract to specific directory asynchronously with progress reporting and cancellation support - /// - /// The archive to extract. - /// The folder to extract into. - /// Extraction options. - /// Optional progress reporter for tracking extraction progress. - /// Optional cancellation token. - public static async Task WriteToDirectoryAsync( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options = null, - IProgress? progress = null, - CancellationToken cancellationToken = default - ) - { - // Prepare for progress reporting - var totalBytes = archive.TotalUncompressSize; - var bytesRead = 0L; - // Tracking for created directories. - var seenDirectories = new HashSet(); - - // Extract - foreach (var entry in archive.Entries) + private async Task WriteToDirectoryAsyncInternal( + string destinationDirectory, + ExtractionOptions? options, + IProgress? progress, + CancellationToken cancellationToken + ) { - cancellationToken.ThrowIfCancellationRequested(); + // Prepare for progress reporting + var totalBytes = archive.TotalUncompressSize; + var bytesRead = 0L; + + // Tracking for created directories. + var seenDirectories = new HashSet(); - if (entry.IsDirectory) + // Extract + foreach (var entry in archive.Entries) { - var dirPath = Path.Combine( - destinationDirectory, - entry.Key.NotNull("Entry Key is null") - ); - if ( - Path.GetDirectoryName(dirPath + "/") is { } parentDirectory - && seenDirectories.Add(dirPath) - ) + cancellationToken.ThrowIfCancellationRequested(); + + if (entry.IsDirectory) { - Directory.CreateDirectory(parentDirectory); + var dirPath = Path.Combine( + destinationDirectory, + entry.Key.NotNull("Entry Key is null") + ); + if ( + Path.GetDirectoryName(dirPath + "/") is { } parentDirectory + && seenDirectories.Add(dirPath) + ) + { + Directory.CreateDirectory(parentDirectory); + } + continue; } - continue; - } - // Use the entry's WriteToDirectoryAsync method which respects ExtractionOptions - await entry - .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken) - .ConfigureAwait(false); + // Use the entry's WriteToDirectoryAsync method which respects ExtractionOptions + await entry + .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken) + .ConfigureAwait(false); - // Update progress - bytesRead += entry.Size; - progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)); + // Update progress + bytesRead += entry.Size; + progress?.Report( + new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes) + ); + } } } }