Compression, fixes

This commit is contained in:
2022-02-17 14:40:38 +01:00
parent f110a8442b
commit ef5c3a9caf
22 changed files with 332 additions and 47 deletions

View File

@@ -7,6 +7,7 @@ namespace FileTime.App.Core.Command
AutoRefresh,
ChangeTimelineMode,
CloseTab,
Compress,
Copy,
CopyHash,
CopyPath,

View File

@@ -9,6 +9,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Smb\FileTime.Providers.Smb.csproj" />

View File

@@ -1,4 +1,5 @@
using AsyncEvent;
using FileTime.Core.Interactions;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
@@ -14,7 +15,7 @@ namespace FileTime.Core.Command
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
public IContainer? Target { get; set; }
public AbsolutePath? Target { get; set; }
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
@@ -25,6 +26,9 @@ namespace FileTime.Core.Command
public string DisplayLabel { get; } = "Copy";
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
public bool TargetIsContainer => true;
public List<InputElement> Inputs { get; } = new();
public List<object>? InputResults { get; set; }
private async Task UpdateProgress()
{
@@ -85,7 +89,8 @@ namespace FileTime.Core.Command
return (IContainer)(await newContainerDiff.AbsolutePath.ResolveAsync())!;
};
await TraverseTree(Sources, Target, TransportMode.Value);
var resolvedTarget = (IContainer)(await Target.ResolveAsync())!;
await TraverseTree(Sources, resolvedTarget, TransportMode.Value);
return startPoint.WithDifferences(newDiffs);
}
@@ -122,7 +127,8 @@ namespace FileTime.Core.Command
}
};
await TraverseTree(Sources, Target, TransportMode.Value);
var resolvedTarget = (IContainer)(await Target.ResolveAsync())!;
await TraverseTree(Sources, resolvedTarget, TransportMode.Value);
}
private async Task CalculateProgress()
@@ -150,7 +156,8 @@ namespace FileTime.Core.Command
}
};
await TraverseTree(Sources, Target, TransportMode.Value);
var resolvedTarget = (IContainer)(await Target.ResolveAsync())!;
await TraverseTree(Sources, resolvedTarget, TransportMode.Value);
_operationStatuses = operationStatuses;
}
@@ -198,7 +205,7 @@ namespace FileTime.Core.Command
var targetFolderPath = new AbsolutePath(target);
var targetElementPath = AbsolutePath.FromParentAndChildName(target, targetName, AbsolutePathType.Element);
foreach(var asd in _operationStatuses.Keys)
foreach (var asd in _operationStatuses.Keys)
{
var hash1 = asd.GetHashCode();
var hash2 = targetFolderPath.GetHashCode();

View File

@@ -1,3 +1,4 @@
using FileTime.Core.Interactions;
using FileTime.Core.Models;
namespace FileTime.Core.Command
@@ -5,7 +6,10 @@ namespace FileTime.Core.Command
public interface ITransportationCommand : ICommand
{
IList<AbsolutePath> Sources { get; }
IContainer? Target { get; set;}
AbsolutePath? Target { get; set; }
TransportMode? TransportMode { get; set; }
bool TargetIsContainer { get; }
List<InputElement> Inputs { get; }
List<object>? InputResults { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using AsyncEvent;
using FileTime.Core.Interactions;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
@@ -8,7 +9,7 @@ namespace FileTime.Core.Command
{
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
public IContainer? Target { get; set; }
public AbsolutePath? Target { get; set; }
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
public int Progress => 100;
@@ -16,6 +17,10 @@ namespace FileTime.Core.Command
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "MoveCommand";
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
public bool TargetIsContainer => true;
public List<InputElement> Inputs { get; } = new();
public List<object>? InputResults { get; set; }
public Task<CanCommandRun> CanRun(PointInTime startPoint)
{

View File

@@ -1,5 +1,6 @@
using AsyncEvent;
using FileTime.Core.Extensions;
using FileTime.Core.Interactions;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
@@ -28,7 +29,7 @@ namespace FileTime.Core.Command
if (itemToRename != null)
{
await itemToRename.Rename(Target);
if(timeRunner.RefreshContainer != null) await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!));
if (timeRunner.RefreshContainer != null) await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!));
}
}

View File

@@ -10,7 +10,7 @@ namespace FileTime.Core.CommandHandlers
{
if (command is not CopyCommand copyCommand) return false;
return (copyCommand.Target?.Provider.SupportsContentStreams ?? false)
return (copyCommand.Target?.ContentProvider.SupportsContentStreams ?? false)
&& copyCommand.Sources.All(p => p.ContentProvider.SupportsContentStreams);
}

View File

@@ -31,9 +31,9 @@ namespace FileTime.Core.Interactions
return new InputElement(label, InputType.Password, defaultValue);
}
public static InputElement ForOptions(string label, List<object> defaultValue)
public static InputElement ForOptions(string label, IEnumerable<object> defaultValue)
{
return new InputElement(label, InputType.Options, defaultValue);
return new InputElement(label, InputType.Options, new List<object>(defaultValue));
}
}
}

View File

@@ -1,16 +1,14 @@
using System.Threading.Tasks;
namespace FileTime.Core.Providers
{
public class ContentProviderStream : Stream
{
private readonly IContentReader? _contentReader;
private readonly IContentWriter? _contentWriter;
public override bool CanRead => _contentReader == null;
public override bool CanRead => _contentReader != null;
public override bool CanSeek => false;
public override bool CanSeek => _contentReader != null;
public override bool CanWrite => _contentWriter == null;
public override bool CanWrite => _contentWriter != null;
public override long Length => throw new NotImplementedException();
@@ -28,7 +26,8 @@ namespace FileTime.Core.Providers
public override void Flush()
{
throw new NotImplementedException();
if (_contentWriter == null) throw new NotSupportedException();
Task.Run(async () => await _contentWriter.FlushAsync()).Wait();
}
public override int Read(byte[] buffer, int offset, int count)
@@ -45,7 +44,16 @@ namespace FileTime.Core.Providers
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
if (_contentReader == null) throw new NotSupportedException();
var newPosition = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.Current => _contentReader.Position ?? 0 + offset,
_ => throw new NotSupportedException()
};
_contentReader.SetPosition(newPosition);
return newPosition;
}
public override void SetLength(long value)
@@ -55,7 +63,14 @@ namespace FileTime.Core.Providers
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
if (_contentWriter == null) throw new NotSupportedException();
var data = buffer;
if (buffer.Length != count)
{
data = new byte[count];
Array.Copy(buffer, data, count);
}
Task.Run(async () => await _contentWriter.WriteBytesAsync(data, offset)).Wait();
}
}
}

View File

@@ -3,7 +3,9 @@ namespace FileTime.Core.Providers
public interface IContentReader : IDisposable
{
int PreferredBufferSize { get; }
long? Position { get; }
Task<byte[]> ReadBytesAsync(int bufferSize, int? offset = null);
void SetPosition(long position);
}
}

View File

@@ -4,7 +4,7 @@ namespace FileTime.Core.Providers
{
int PreferredBufferSize { get; }
Task WriteBytesAsync(byte[] data);
Task WriteBytesAsync(byte[] data, int? index = null);
Task FlushAsync();
}
}

View File

@@ -151,11 +151,11 @@ namespace FileTime.Core.Timeline
}
finally
{
Task.Run(async () => await DisposeCommandThread(Thread.CurrentThread, commandToRun)).Wait();
Task.Run(async () => await DisposeCommandThread(commandToRun)).Wait();
}
}
private async Task DisposeCommandThread(Thread thread, CommandTimeState? command)
private async Task DisposeCommandThread(CommandTimeState? command)
{
await RunWithLockAsync(async () =>
{
@@ -170,7 +170,7 @@ namespace FileTime.Core.Timeline
}
}
var currentCommandRunner = _commandRunners.Find(r => r.Thread == thread);
var currentCommandRunner = _commandRunners.Find(r => r.Command == command);
if (currentCommandRunner != null) _commandRunners.Remove(currentCommandRunner);
await UpdateReadOnlyCommands();
StartCommandRunner();

View File

@@ -45,6 +45,7 @@ namespace FileTime.Avalonia.Configuration
new CommandBindingConfiguration(Commands.AutoRefresh, new KeyConfig(Key.R, shift: true)),
new CommandBindingConfiguration(Commands.ChangeTimelineMode, new[] { Key.T, Key.M }),
new CommandBindingConfiguration(Commands.CloseTab, Key.Q),
new CommandBindingConfiguration(Commands.Compress, new[] { Key.Y, Key.C }),
new CommandBindingConfiguration(Commands.Copy, new[] { Key.Y, Key.Y }),
new CommandBindingConfiguration(Commands.CopyHash, new[] { Key.C, Key.H }),
new CommandBindingConfiguration(Commands.CopyPath, new[] { Key.C, Key.P }),

View File

@@ -18,6 +18,7 @@ using FileTime.Core.Models;
using FileTime.Core.Providers;
using FileTime.Core.Timeline;
using FileTime.Providers.Local;
using FileTime.Tools.Compression.Command;
using Microsoft.Extensions.Logging;
namespace FileTime.Avalonia.Services
@@ -66,14 +67,15 @@ namespace FileTime.Avalonia.Services
{Commands.AutoRefresh, ToggleAutoRefresh},
{Commands.ChangeTimelineMode, ChangeTimelineMode},
{Commands.CloseTab, CloseTab},
{Commands.Compress, Compress},
{Commands.Copy, Copy},
{Commands.CopyHash, CopyHash},
{Commands.CopyPath, CopyPath},
{Commands.CreateContainer, CreateContainer},
{Commands.CreateElement, CreateElement},
{Commands.Cut, Cut},
{Commands.EnterRapidTravel, EnterRapidTravelMode},
{Commands.Edit, Edit},
{Commands.EnterRapidTravel, EnterRapidTravelMode},
{Commands.GoToHome, GotToHome},
{Commands.GoToPath, GoToContainer},
{Commands.GoToProvider, GotToProvider},
@@ -468,14 +470,37 @@ namespace FileTime.Avalonia.Services
command.Sources.Add(item);
}
_clipboard.Clear();
var currentLocation = _appState.SelectedTab.CurrentLocation.Container;
command.Target = currentLocation is VirtualContainer virtualContainer
currentLocation =
currentLocation is VirtualContainer virtualContainer
? virtualContainer.BaseContainer
: currentLocation;
if (!command.TargetIsContainer)
{
var handler = async (List<InputElementWrapper> inputs) =>
{
command.Target = AbsolutePath.FromParentAndChildName(currentLocation, inputs[0].Value, AbsolutePathType.Element);
command.InputResults = inputs.Skip(1).Select(i => i.Option ?? i.Value).ToList();
await AddCommand(command);
};
_clipboard.Clear();
var inputs = new List<InputElement>()
{
InputElement.ForText("Compressed element name")
};
inputs.AddRange(command.Inputs);
_dialogService.ReadInputs(inputs, handler);
}
else
{
command.Target = new AbsolutePath(currentLocation);
await AddCommand(command);
}
}
}
@@ -659,7 +684,6 @@ namespace FileTime.Avalonia.Services
foreach (var timelineBlock in _appState.TimelineCommands)
{
foreach (var command in timelineBlock.ParallelCommands)
{
if (command.IsSelected)
@@ -881,10 +905,34 @@ namespace FileTime.Avalonia.Services
_dialogService.ReadInputs(new List<InputElement>()
{
InputElement.ForOptions("Hash function", Enum.GetValues<HashFunction>().Cast<object>().ToList())
InputElement.ForOptions("Hash function", Enum.GetValues<HashFunction>().Cast<object>())
}, handler);
return Task.CompletedTask;
}
private async Task Compress()
{
_clipboard.Clear();
_clipboard.SetCommand<CompressCommand>();
var currentSelectedItems = await _appState.SelectedTab.TabState.GetCurrentMarkedItems();
if (currentSelectedItems.Count > 0)
{
foreach (var selectedItem in currentSelectedItems)
{
_clipboard.AddContent(selectedItem);
}
await _appState.SelectedTab.TabState.ClearCurrentMarkedItems();
}
else
{
var currentSelectedItem = _appState.SelectedTab.SelectedItem?.Item;
if (currentSelectedItem != null)
{
_clipboard.AddContent(new AbsolutePath(currentSelectedItem));
}
}
}
}
}

View File

@@ -1,4 +1,3 @@
using System.Threading.Tasks;
using FileTime.Core.Providers;
namespace FileTime.Providers.Local
@@ -7,10 +6,10 @@ namespace FileTime.Providers.Local
{
private readonly FileStream _readerStream;
private readonly BinaryReader _binaryReader;
private bool disposed;
private bool _disposed;
public int PreferredBufferSize => 1024 * 1024;
private long? _bytesRead;
public long? Position { get; private set; }
public LocalContentReader(FileStream readerStream)
{
@@ -24,10 +23,10 @@ namespace FileTime.Providers.Local
if (offset != null)
{
if (_bytesRead == null) _bytesRead = 0;
if (Position == null) Position = 0;
var buffer = new byte[max];
var bytesRead = _binaryReader.Read(buffer, offset.Value, max);
_bytesRead += bytesRead;
Position += bytesRead;
if (buffer.Length != bytesRead)
{
@@ -41,6 +40,11 @@ namespace FileTime.Providers.Local
}
}
public void SetPosition(long position)
{
Position = position;
}
~LocalContentReader()
{
Dispose(false);
@@ -54,7 +58,7 @@ namespace FileTime.Providers.Local
private void Dispose(bool disposing)
{
if (!disposed)
if (!_disposed)
{
if (disposing)
{
@@ -62,7 +66,7 @@ namespace FileTime.Providers.Local
_binaryReader.Dispose();
}
}
disposed = true;
_disposed = true;
}
}
}

View File

@@ -15,9 +15,16 @@ namespace FileTime.Providers.Local
_binaryWriter = new BinaryWriter(_writerStream);
}
public Task WriteBytesAsync(byte[] data)
public Task WriteBytesAsync(byte[] data, int? index = null)
{
if (index != null)
{
_binaryWriter.Write(data, index.Value, data.Length);
}
else
{
_binaryWriter.Write(data);
}
return Task.CompletedTask;
}

View File

@@ -10,7 +10,8 @@ namespace FileTime.Providers.Smb
private readonly object _fileHandle;
private readonly ISMBClient _client;
private bool _disposed;
private long _bytesRead;
private long _position;
public long? Position => _position;
public int PreferredBufferSize => (int)_client.MaxReadSize;
@@ -25,7 +26,7 @@ namespace FileTime.Providers.Smb
{
var max = bufferSize > 0 && bufferSize < (int)_client.MaxReadSize ? bufferSize : (int)_client.MaxReadSize;
var status = _smbFileStore.ReadFile(out byte[] data, _fileHandle, offset ?? _bytesRead, max);
var status = _smbFileStore.ReadFile(out byte[] data, _fileHandle, offset ?? _position, max);
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
{
throw new Exception("Failed to read from file");
@@ -35,11 +36,16 @@ namespace FileTime.Providers.Smb
{
return Task.FromResult(Array.Empty<byte>());
}
_bytesRead += data.Length;
_position += data.Length;
return Task.FromResult(data);
}
public void SetPosition(long position)
{
_position = position;
}
~SmbContentReader()
{
Dispose(false);

View File

@@ -26,14 +26,16 @@ namespace FileTime.Providers.Smb
return Task.CompletedTask;
}
public Task WriteBytesAsync(byte[] data)
public Task WriteBytesAsync(byte[] data, int? index = null)
{
var status = _smbFileStore.WriteFile(out int numberOfBytesWritten, _fileHandle, _writeOffset, data);
var status = _smbFileStore.WriteFile(out int numberOfBytesWritten, _fileHandle, index ?? _writeOffset, data);
if (status != NTStatus.STATUS_SUCCESS)
{
throw new Exception("Failed to write to file");
}
_writeOffset += numberOfBytesWritten;
_writeOffset = index == null
? _writeOffset + numberOfBytesWritten
: index.Value + numberOfBytesWritten;
return Task.CompletedTask;
}

View File

@@ -1,13 +1,20 @@
using AsyncEvent;
using FileTime.Core.Command;
using FileTime.Core.Extensions;
using FileTime.Core.Interactions;
using FileTime.Core.Models;
using FileTime.Core.Providers;
using FileTime.Core.Timeline;
using FileTime.Tools.Compression.Command.OperationHandlers;
using SharpCompress.Archives;
using SharpCompress.Common;
namespace FileTime.Tools.Compression.Command
{
public class CompressCommand : IExecutableCommand
public class CompressCommand : IExecutableCommand, ITransportationCommand
{
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
public AbsolutePath? Target { get; set; }
public string DisplayLabel { get; } = "Compress";
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
@@ -16,6 +23,18 @@ namespace FileTime.Tools.Compression.Command
public int CurrentProgress { get; }
public AsyncEventHandler ProgressChanged { get; } = new AsyncEventHandler();
public TransportMode? TransportMode { get; set; }
public bool TargetIsContainer => false;
public List<InputElement> Inputs { get; }
public List<object>? InputResults { get; set; }
public CompressCommand()
{
Inputs = new List<InputElement>()
{
InputElement.ForOptions("Compression method", Enum.GetValues<Models.CompressionType>().Cast<object>())
};
}
public Task<CanCommandRun> CanRun(PointInTime startPoint)
{
@@ -25,12 +44,89 @@ namespace FileTime.Tools.Compression.Command
public Task<PointInTime> SimulateCommand(PointInTime startPoint)
{
//TODO: implement
return Task.FromResult(startPoint.WithDifferences(new List<Difference>()));
}
public Task Execute(TimeRunner timeRunner)
public async Task Execute(TimeRunner timeRunner)
{
throw new NotImplementedException();
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
var disposables = Enumerable.Empty<IDisposable>();
ICompressOperation? compressOperation = null;
try
{
var compressionType =
InputResults?.Count > 0 && InputResults[0] is Models.CompressionType compType
? compType
: Models.CompressionType.Zip;
compressOperation = compressionType switch
{
Models.CompressionType.Gzip => SharpCompress.Archives.GZip.GZipArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.GZip)))),
Models.CompressionType.Zip => SharpCompress.Archives.Zip.ZipArchive.Create().Map(a => GetCompressOperation(a, a.SaveTo)),
Models.CompressionType.Tar => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.None)))),
Models.CompressionType.TarBz2 => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.BZip2)))),
Models.CompressionType.TarLz => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.LZip)))),
_ => throw new NotImplementedException()
};
disposables = await TraverseTree(Sources, "", compressOperation);
var resolvedParent = (IContainer)(await Target.GetParent().ResolveAsync())!;
var targetElement = await resolvedParent.CreateElementAsync(Target.GetName());
using var contentWriter = await targetElement.GetContentWriterAsync();
using var contentWriterStream = new ContentProviderStream(contentWriter);
compressOperation.SaveTo(contentWriterStream);
await contentWriterStream.FlushAsync();
}
finally
{
compressOperation?.Dispose();
foreach (var disposable in disposables)
{
disposable.Dispose();
}
}
}
private static async Task<IEnumerable<IDisposable>> TraverseTree(
IEnumerable<AbsolutePath> sources,
string basePath,
ICompressOperation operations)
{
var disposables = Enumerable.Empty<IDisposable>();
foreach (var source in sources)
{
var item = await source.ResolveAsync();
if (item is IContainer container)
{
var items = await container.GetItems();
if (items == null) continue;
var childItems = items.Select(i => new AbsolutePath(i)).ToList()!;
var path = string.IsNullOrEmpty(basePath) ? container.Name : basePath + "\\" + container.Name;
disposables = disposables.Concat(await TraverseTree(childItems, path, operations));
}
else if (item is IElement element)
{
var path = string.IsNullOrEmpty(basePath) ? element.Name : basePath + "\\" + element.Name;
disposables = disposables.Concat(await operations.CompressElement(element, path));
}
}
return disposables;
}
public static CompressOperation<TEntry, TVolume> GetCompressOperation<TEntry, TVolume>(AbstractWritableArchive<TEntry, TVolume> archive, Action<Stream> saveTo)
where TEntry : IArchiveEntry
where TVolume : IVolume
{
return new CompressOperation<TEntry, TVolume>(archive, saveTo);
}
}
}

View File

@@ -0,0 +1,64 @@
using FileTime.Core.Models;
using FileTime.Core.Providers;
using SharpCompress.Archives;
using SharpCompress.Common;
namespace FileTime.Tools.Compression.Command.OperationHandlers
{
public class CompressOperation<TEntry, TVolume> : ICompressOperation
where TEntry : IArchiveEntry
where TVolume : IVolume
{
private readonly AbstractWritableArchive<TEntry, TVolume> _archive;
private readonly Action<Stream> _saveTo;
private bool _disposed;
public CompressOperation(AbstractWritableArchive<TEntry, TVolume> archive, Action<Stream> saveTo)
{
_archive = archive;
_saveTo = saveTo;
}
public async Task<IEnumerable<IDisposable>> CompressElement(IElement element, string key)
{
if (element.Provider.SupportsContentStreams)
{
var contentReader = await element.GetContentReaderAsync();
var contentReaderStream = new ContentProviderStream(contentReader);
_archive.AddEntry(key, contentReaderStream);
return new IDisposable[] { contentReader, contentReaderStream };
}
return Enumerable.Empty<IDisposable>();
}
public void SaveTo(Stream stream)
{
_saveTo(stream);
}
~CompressOperation()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_archive.Dispose();
}
}
_disposed = true;
}
}
}

View File

@@ -0,0 +1,10 @@
using FileTime.Core.Models;
namespace FileTime.Tools.Compression.Command.OperationHandlers
{
public interface ICompressOperation : IDisposable
{
Task<IEnumerable<IDisposable>> CompressElement(IElement element, string key);
void SaveTo(Stream stream);
}
}

View File

@@ -0,0 +1,11 @@
namespace FileTime.Tools.Compression.Models
{
public enum CompressionType
{
Gzip,
Zip,
Tar,
TarBz2,
TarLz
}
}