diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6f67c023b..ec07825a4 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -26,6 +26,7 @@
+
@@ -90,4 +91,4 @@
-
\ No newline at end of file
+
diff --git a/src/Fallout.Utilities.IO.Compression/CompressionExtensions.cs b/src/Fallout.Utilities.IO.Compression/CompressionExtensions.cs
index 85c69b524..5b2705c25 100644
--- a/src/Fallout.Utilities.IO.Compression/CompressionExtensions.cs
+++ b/src/Fallout.Utilities.IO.Compression/CompressionExtensions.cs
@@ -3,40 +3,101 @@
using System.IO;
using System.IO.Compression;
using System.Linq;
+using Fallout.Common.Utilities.Collections;
using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
-using Fallout.Common.Utilities.Collections;
+using SharpCompress.Common;
+using SharpCompress.Readers;
+using ZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
namespace Fallout.Common.IO;
public static class CompressionExtensions
{
+ ///
+ /// Compresses into , choosing the compression format based on the
+ /// archive file's extension.
+ ///
+ /// The directory whose contents should be compressed.
+ ///
+ /// The archive file to create. Its extension determines the compression format (e.g. .zip,
+ /// .tar.gz, .tar.bz2).
+ ///
+ ///
+ /// An optional predicate used to filter which files are included in the archive. If null, all files are
+ /// included.
+ ///
public static void CompressTo(this AbsolutePath directory, AbsolutePath archiveFile, Func filter = null)
{
if (archiveFile.HasExtension(".zip"))
+ {
directory.ZipTo(archiveFile, filter);
+ }
else if (archiveFile.HasExtension(".tar.gz", ".tgz"))
+ {
directory.TarGZipTo(archiveFile, filter);
+ }
else if (archiveFile.HasExtension(".tar.bz2", ".tbz2", ".tbz"))
+ {
directory.TarBZip2To(archiveFile, filter);
+ }
+ else if (archiveFile.HasExtension(".tar.xz", ".txz"))
+ {
+ Assert.Fail(
+ $"Compressing a .tar.xz archive currently not supported. Archive file: '{Path.GetFileName(archiveFile)}'");
+ }
else
+ {
Assert.Fail($"Unknown archive extension for archive '{Path.GetFileName(archiveFile)}'");
+ }
}
+ ///
+ /// Uncompresses into , choosing the decompression format based on
+ /// the archive file's extension.
+ ///
+ ///
+ /// The archive file to extract. Its extension determines the decompression format (e.g. .zip,
+ /// .tar.gz, .tar.bz2, .tar.xz).
+ ///
+ /// The directory into which the archive's contents are extracted.
public static void UncompressTo(this AbsolutePath archiveFile, AbsolutePath directory)
{
if (archiveFile.HasExtension(".zip"))
+ {
archiveFile.UnZipTo(directory);
+ }
else if (archiveFile.HasExtension(".tar.gz", ".tgz"))
+ {
archiveFile.UnTarGZipTo(directory);
+ }
else if (archiveFile.HasExtension(".tar.bz2", ".tbz2", ".tbz"))
+ {
archiveFile.UnTarBZip2To(directory);
+ }
+ else if (archiveFile.HasExtension(".tar.xz", ".txz"))
+ {
+ archiveFile.UnTarXzTo(directory);
+ }
else
+ {
Assert.Fail($"Unknown archive extension for archive '{Path.GetFileName(archiveFile)}'");
+ }
}
+ ///
+ /// Compresses into a ZIP archive at .
+ ///
+ /// The directory whose contents should be added to the ZIP archive.
+ /// The ZIP archive file to create.
+ ///
+ /// An optional predicate used to filter which files are included in the archive. If null, all files are
+ /// included.
+ ///
+ /// The compression level to use for the archive entries.
+ /// The used to open the archive file.
public static void ZipTo(
this AbsolutePath directory,
AbsolutePath archiveFile,
@@ -47,50 +108,71 @@ public static void ZipTo(
archiveFile.Parent.CreateDirectory();
filter ??= _ => true;
- var files = directory.GetFiles(depth: int.MaxValue).Where(filter).ToList();
+ List files = directory.GetFiles(depth: int.MaxValue).Where(filter).ToList();
- using var fileStream = File.Open(archiveFile, fileMode, FileAccess.ReadWrite);
- using var zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create);
+ using FileStream fileStream = File.Open(archiveFile, fileMode, FileAccess.ReadWrite);
+ using ZipArchive zipArchive = new(fileStream, ZipArchiveMode.Create);
void AddFile(AbsolutePath file)
{
- var relativePath = directory.GetRelativePathTo(file);
- var entryName = ZipEntry.CleanName(relativePath);
+ RelativePath relativePath = directory.GetRelativePathTo(file);
+ string entryName = ZipEntry.CleanName(relativePath);
zipArchive.CreateEntryFromFile(file, entryName, compressionLevel);
}
files.ForEach(AddFile);
}
+ ///
+ /// Extracts the contents of a ZIP archive at into .
+ /// Destination directory is created, and conflicting files are overwritten.
+ ///
+ /// The ZIP archive file to extract.
+ /// The directory into which the archive's contents are extracted.
public static void UnZipTo(this AbsolutePath archiveFile, AbsolutePath directory)
{
- using var fileStream = File.OpenRead(archiveFile);
- using var zipFile = new ICSharpCode.SharpZipLib.Zip.ZipFile(fileStream);
+ using FileStream fileStream = File.OpenRead(archiveFile);
+ using ZipFile zipFile = new(fileStream);
- var entries = zipFile.Cast().Where(x => !x.IsDirectory);
+ IEnumerable entries = zipFile.Cast().Where(x => !x.IsDirectory);
void HandleEntry(ZipEntry entry)
{
- var file = directory / entry.Name;
+ AbsolutePath file = directory / entry.Name;
Directory.CreateDirectory(file.Parent.NotNull());
- using var entryStream = zipFile.GetInputStream(entry);
- using var outputStream = File.Open(file, FileMode.Create);
+ using Stream entryStream = zipFile.GetInputStream(entry);
+ using FileStream outputStream = File.Open(file, FileMode.Create);
entryStream.CopyTo(outputStream);
}
entries.ForEach(HandleEntry);
}
+ ///
+ /// Compresses the given into a gzip-compressed tar archive at .
+ ///
+ /// The base directory used to compute the relative entry names of the archived files.
+ /// The tar.gz archive file to create.
+ /// The files to add to the archive.
+ /// The used to open the archive file.
public static void TarGZipTo(
this AbsolutePath baseDirectory,
AbsolutePath archiveFile,
IEnumerable files,
- FileMode fileMode = FileMode.CreateNew)
- {
+ FileMode fileMode = FileMode.CreateNew) =>
CompressTar(baseDirectory, archiveFile, files.ToList(), fileMode, x => new GZipOutputStream(x));
- }
+ ///
+ /// Compresses into a gzip-compressed tar archive at .
+ ///
+ /// The directory whose contents should be added to the archive.
+ /// The tar.gz archive file to create.
+ ///
+ /// An optional predicate used to filter which files are included in the archive. If null, all files are
+ /// included.
+ ///
+ /// The used to open the archive file.
public static void TarGZipTo(
this AbsolutePath directory,
AbsolutePath archiveFile,
@@ -98,19 +180,34 @@ public static void TarGZipTo(
FileMode fileMode = FileMode.CreateNew)
{
filter ??= _ => true;
- var files = directory.GetFiles(depth: int.MaxValue).Where(filter);
+ IEnumerable files = directory.GetFiles(depth: int.MaxValue).Where(filter);
directory.TarGZipTo(archiveFile, files, fileMode);
}
+ ///
+ /// Compresses the given into a bzip2-compressed tar archive at .
+ ///
+ /// The base directory used to compute the relative entry names of the archived files.
+ /// The tar.bz2 archive file to create.
+ /// The files to add to the archive.
+ /// The used to open the archive file.
public static void TarBZip2To(
this AbsolutePath directory,
AbsolutePath archiveFile,
IEnumerable files,
- FileMode fileMode = FileMode.CreateNew)
- {
+ FileMode fileMode = FileMode.CreateNew) =>
CompressTar(directory, archiveFile, files.ToList(), fileMode, x => new BZip2OutputStream(x));
- }
+ ///
+ /// Compresses into a bzip2-compressed tar archive at .
+ ///
+ /// The directory whose contents should be added to the archive.
+ /// The tar.bz2 archive file to create.
+ ///
+ /// An optional predicate used to filter which files are included in the archive. If null, all files are
+ /// included.
+ ///
+ /// The used to open the archive file.
public static void TarBZip2To(
this AbsolutePath directory,
AbsolutePath archiveFile,
@@ -118,20 +215,63 @@ public static void TarBZip2To(
FileMode fileMode = FileMode.CreateNew)
{
filter ??= _ => true;
- var files = directory.GetFiles(depth: int.MaxValue).Where(filter);
+ IEnumerable files = directory.GetFiles(depth: int.MaxValue).Where(filter);
directory.TarBZip2To(archiveFile, files, fileMode);
}
- public static void UnTarGZipTo(this AbsolutePath archiveFile, AbsolutePath directory)
- {
+ ///
+ /// Extracts the contents of a gzip-compressed tar archive at into .
+ /// Destination directory is created, and conflicting files are overwritten.
+ ///
+ /// The tar.gz archive file to extract.
+ /// The directory into which the archive's contents are extracted.
+ public static void UnTarGZipTo(this AbsolutePath archiveFile, AbsolutePath directory) =>
UncompressTar(archiveFile, directory, x => new GZipInputStream(x));
- }
- public static void UnTarBZip2To(this AbsolutePath archiveFile, AbsolutePath directory)
- {
+ ///
+ /// Extracts the contents of a bzip2-compressed tar archive at into .
+ /// Destination directory is created, and conflicting files are overwritten.
+ ///
+ /// The tar.bz2 archive file to extract.
+ /// The directory into which the archive's contents are extracted.
+ public static void UnTarBZip2To(this AbsolutePath archiveFile, AbsolutePath directory) =>
UncompressTar(archiveFile, directory, x => new BZip2InputStream(x));
+
+ ///
+ /// Extracts the contents of an xz-compressed tar archive at into .
+ /// Destination directory is created, and conflicting files are skipped.
+ ///
+ /// The tar.xz archive file to extract.
+ /// The directory into which the archive's contents are extracted.
+ public static void UnTarXzTo(this AbsolutePath archive, AbsolutePath directory)
+ {
+ using Stream stream = File.OpenRead(archive);
+ using IReader reader = ReaderFactory.OpenReader(stream);
+
+ while (reader.MoveToNextEntry())
+ {
+ if (reader.Entry.IsDirectory)
+ {
+ continue;
+ }
+
+ reader.WriteEntryToDirectory(directory, new ExtractionOptions
+ {
+ ExtractFullPath = true,
+ Overwrite = true
+ });
+ }
}
+ ///
+ /// Compresses the given into a tar archive at , wrapping the underlying
+ /// file stream with the compression stream produced by .
+ ///
+ /// The base directory used to compute the relative entry names of the archived files.
+ /// The archive file to create.
+ /// The files to add to the archive.
+ /// The used to open the archive file.
+ /// A factory that wraps the raw archive file stream with the desired compression stream.
private static void CompressTar(
AbsolutePath baseDirectory,
AbsolutePath archiveFile,
@@ -141,26 +281,33 @@ private static void CompressTar(
{
archiveFile.Parent.CreateDirectory();
- using var fileStream = File.Open(archiveFile, fileMode, FileAccess.ReadWrite);
- using var outputStream = outputStreamFactory(fileStream);
- using var tarArchive = TarArchive.CreateOutputTarArchive(outputStream);
+ using FileStream fileStream = File.Open(archiveFile, fileMode, FileAccess.ReadWrite);
+ using Stream outputStream = outputStreamFactory(fileStream);
+ using TarArchive tarArchive = TarArchive.CreateOutputTarArchive(outputStream);
void AddFile(AbsolutePath file)
{
- var entry = TarEntry.CreateEntryFromFile(file);
+ TarEntry entry = TarEntry.CreateEntryFromFile(file);
entry.Name = baseDirectory.GetUnixRelativePathTo(file);
- tarArchive.WriteEntry(entry, recurse: false);
+ tarArchive.WriteEntry(entry, false);
}
files.ForEach(AddFile);
}
+ ///
+ /// Extracts the contents of a tar archive at into , wrapping the
+ /// underlying file stream with the decompression stream produced by .
+ ///
+ /// The archive file to extract.
+ /// The directory into which the archive's contents are extracted.
+ /// A factory that wraps the raw archive file stream with the desired decompression stream.
private static void UncompressTar(AbsolutePath archiveFile, AbsolutePath directory, Func inputStreamFactory)
{
- using var fileStream = File.OpenRead(archiveFile);
- using var inputStream = inputStreamFactory(fileStream);
- using var tarArchive = TarArchive.CreateInputTarArchive(inputStream, nameEncoding: null);
+ using FileStream fileStream = File.OpenRead(archiveFile);
+ using Stream inputStream = inputStreamFactory(fileStream);
+ using TarArchive tarArchive = TarArchive.CreateInputTarArchive(inputStream, null);
directory.CreateDirectory();
diff --git a/src/Fallout.Utilities.IO.Compression/Fallout.Utilities.IO.Compression.csproj b/src/Fallout.Utilities.IO.Compression/Fallout.Utilities.IO.Compression.csproj
index 456306f23..7b6c8420a 100644
--- a/src/Fallout.Utilities.IO.Compression/Fallout.Utilities.IO.Compression.csproj
+++ b/src/Fallout.Utilities.IO.Compression/Fallout.Utilities.IO.Compression.csproj
@@ -10,6 +10,7 @@
+