Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Twig.sln
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "twig", "twig\twig.csproj",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmark", "Benchmark", "{F31E0279-AD0D-4F66-8CA5-BFBF802CAAAB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "twig.Benchmark", "twig.Benchmark\twig.Benchmark.csproj", "{2157DB4A-9343-4EA2-BD8C-DB9B885E7B8B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "twig.Benchmark", "twig.Benchmark\twig.Benchmark.csproj", "{2157DB4A-9343-4EA2-BD8C-DB9B885E7B8B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
13 changes: 11 additions & 2 deletions src/twig.Benchmark/ArchiverBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,29 @@ public class ArchiverBenchmark
RandomDataHelper.TempDirectory,
RandomDataHelper.FileName);

private DefaultCommand.Settings Settings { get; set; } = new ();

[ParamsSource(nameof(ValuesForLevel))]
public int Level { get; set; }
public IEnumerable<int> ValuesForLevel => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };

[Benchmark]
public async Task Compress()
{
await Archiver.CompressAsync(_toCompressPath, Level, true, false, false, false, Path.Combine(_toCompressPath + Level), false, false);
Settings.Path = _toCompressPath;
Settings.CompressionLevel = Level;
Settings.Overwrite = true;
Settings.OutputPath = Path.Combine(_toCompressPath + Level);

await Archiver.CompressAsync(Settings);
}

[Benchmark]
public async Task Decompress()
{
await Archiver.DecompressAsync(Path.Combine(_toCompressPath + Level), true, false, false, "", false, false);
Settings.Path = Path.Combine(_toCompressPath + Level);
Settings.Overwrite = true;
await Archiver.DecompressAsync(Settings);
}
}
}
50 changes: 34 additions & 16 deletions src/twig/Archiver/Archiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@ public static IDisposable ReportProgress(ProgressTask task)
return new ProgressBarDisposable(task);
}

public static async Task CompressAsync(string path, int compressionLevel, bool overwrite, bool subfolder, bool replicate, bool verbose, string output, bool remove, bool automaticMode = false, long size = 0)
public static async Task CompressAsync(DefaultCommand.Settings settings, bool automaticMode = false, long size = 0)
{
var path = settings.Path;
var compressionLevel = settings.CompressionLevel;
var overwrite = settings.Overwrite;
var subfolder = settings.Subfolder;
var replicate = settings.Replicate;
var verbose = settings.Verbose;
var output = settings.OutputPath;
var remove = settings.Remove;


using var options = new CompressionOptions(compressionLevel);
using var compressor = new Compressor(options);
var attr = File.GetAttributes(path);
Expand Down Expand Up @@ -96,8 +106,15 @@ private static async Task WriteCompressedDataAsync(string path, Compressor compr
}
}

public static async Task DecompressAsync(string path, bool overwrite, bool subfolder, bool replicate, string output, bool remove, bool automaticMode = false, long size = 0)
public static async Task DecompressAsync(DefaultCommand.Settings settings, bool automaticMode = false, long size = 0)
{
var path = settings.Path;
var overwrite = settings.Overwrite;
var subfolder = settings.Subfolder;
var replicate = settings.Replicate;
var output = settings.OutputPath;
var remove = settings.Remove;

using var decompressor = new Decompressor();
FileAttributes attr = File.GetAttributes(path);
if (attr.HasFlag(FileAttributes.Directory))
Expand Down Expand Up @@ -173,42 +190,43 @@ public static void RemoveOriginal(string path, bool remove)
}
}

public static async Task RunArchiver(string path, int compressionLevel, bool overwrite, bool subfolder, bool replicate, bool verbose, string output, bool remove, ProgressTask task)
public static async Task RunArchiver(DefaultCommand.Settings settings, ProgressTask task)
{
if (!File.GetAttributes(path).HasFlag(FileAttributes.Directory) && path.EndsWith(".zs"))
if (!File.GetAttributes(settings.Path).HasFlag(FileAttributes.Directory) && settings.Path.EndsWith(".zs"))
{
await DecompressAsync(path, overwrite, subfolder, replicate, output, remove);
await DecompressAsync(settings);
return;
}
if (!File.GetAttributes(path).HasFlag(FileAttributes.Directory) && !path.EndsWith(".zs"))
if (!File.GetAttributes(settings.Path).HasFlag(FileAttributes.Directory) && !settings.Path.EndsWith(".zs"))
{
await CompressAsync(path, compressionLevel, overwrite, subfolder, replicate, verbose, output, remove);
await CompressAsync(settings);
return;
}

var size = FileHelper.GetTotalSize(path, subfolder);
var size = FileHelper.GetTotalSize(settings.Path, settings.Subfolder);
ProgressStart?.Invoke(null, new ProgressStartEventArgs(size));

if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
if (File.GetAttributes(settings.Path).HasFlag(FileAttributes.Directory))
{
var paths = Directory.GetFiles(path);
if (subfolder)
var paths = Directory.GetFiles(settings.Path);
if (settings.Subfolder)
{
paths = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
paths = Directory.GetFiles(settings.Path, "*.*", SearchOption.AllDirectories);
}
foreach (var p in paths)
{
var dir = Path.GetDirectoryName(p) == path ? "" : Path.GetFileName(Path.GetDirectoryName(p));
var outputPath = replicate ? Path.Combine(output, dir) : output;
settings.Path = p;
var dir = Path.GetDirectoryName(p) == settings.Path ? "" : Path.GetFileName(Path.GetDirectoryName(p));
settings.OutputPath = settings.Replicate ? Path.Combine(settings.OutputPath, dir) : settings.OutputPath;

if (p.EndsWith(".zs"))
{
await DecompressAsync(p, overwrite, subfolder, replicate, outputPath, remove, true);
await DecompressAsync(settings, true);
}

if (!p.EndsWith(".zs"))
{
await CompressAsync(p, compressionLevel, overwrite, subfolder, replicate, verbose, outputPath, remove, true);
await CompressAsync(settings, true);
}
}
ProgressFinish?.Invoke(null, new ProgressFinishEventArgs());
Expand Down
37 changes: 4 additions & 33 deletions src/twig/Commands/DefaultCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,57 +140,28 @@ public override async Task<int> ExecuteAsync(CommandContext context, Settings se
if (settings.IsCompressionMode)
{
await AnsiConsole.Progress()
.StartExecuteAsync("Compressing...", async (task) => await Archiver.CompressAsync(
settings.Path,
settings.CompressionLevel,
settings.Overwrite,
settings.Subfolder,
settings.Replicate,
settings.Verbose,
settings.OutputPath,
settings.Remove
)
.StartExecuteAsync("Compressing...", async (task) => await Archiver.CompressAsync(settings)
);
}

if (settings.IsDecompressionMode)
{
await AnsiConsole.Progress()
.StartExecuteAsync("Decompressing...", async (task) => await Archiver.DecompressAsync(
settings.Path,
settings.Overwrite,
settings.Subfolder,
settings.Replicate,
settings.OutputPath,
settings.Remove
)
.StartExecuteAsync("Decompressing...", async (task) => await Archiver.DecompressAsync(settings)
);
}

if (settings.AdviseDuration == 0 && !settings.IsCompressionMode && !settings.IsDecompressionMode)
{
await AnsiConsole.Progress()
.StartExecuteAsync("Processing...", async (task) => await Archiver.RunArchiver(
settings.Path,
settings.CompressionLevel,
settings.Overwrite,
settings.Subfolder,
settings.Replicate,
settings.Verbose,
settings.OutputPath,
settings.Remove,
task
)
.StartExecuteAsync("Processing...", async (task) => await Archiver.RunArchiver(settings, task)
);
}

if (settings.AdviseDuration > 0 && !settings.IsCompressionMode && !settings.IsDecompressionMode)
{
AnsiConsole.WriteLine("Looking for the best compression level for given duration. Please wait...") ;
await AdviseLogger.CheckForBestLevel(
settings.AdviseDuration,
settings.Path
);
await AdviseLogger.CheckForBestLevel(settings);
}

_console.WriteLine("Task completed.");
Expand Down
13 changes: 7 additions & 6 deletions src/twig/Helpers/FileAssociationHelper.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
namespace twig
{
using System;
using System.Dynamic;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Spectre.Console;

public static class FileAssociationHelper
{
public static void AddContextMenuOption(string subKey, string value)
{
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var appPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
var key = Registry.CurrentUser.CreateSubKey(subKey, true);
key.SetValue("", value);
key.SetValue("Icon", $"\"{basePath}Resources\\Icons\\tw.ico\"");
var newSubKey = key.CreateSubKey("command");
newSubKey.SetValue("", appPath + " \"%1\"");
newSubKey.Close();
Expand All @@ -24,11 +23,15 @@ public static void AddContextMenuOption(string subKey, string value)

public static void RegisterForFileExtension(string extension)
{
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var appPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
var key = Registry.CurrentUser.CreateSubKey("Software\\Classes\\" + extension);
var subKey = key.CreateSubKey("shell\\open\\command");
subKey.SetValue("", appPath + " \"%1\"");
var iconKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\.zs\\DefaultIcon");
iconKey.SetValue("",$"\"{basePath}Resources\\Icons\\compressed.ico\"");
subKey.Close();
iconKey.Close();
key.Close();

SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
Expand All @@ -43,9 +46,7 @@ public static void RemoveContextMenuOption(string subKey)

public static void UnregisterForFileExtension(string extension)
{
var key = Registry.CurrentUser.OpenSubKey("Software\\Classes\\" + extension, true);
key.DeleteSubKey("shell\\open\\command");
key.Close();
Microsoft.Win32.Registry.CurrentUser.DeleteSubKeyTree("SOFTWARE\\Classes\\.zs");

SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
Expand Down
12 changes: 8 additions & 4 deletions src/twig/Logging/AdviseLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@

public static class AdviseLogger
{
public static async Task CheckForBestLevel(int duration, string path)
public static async Task CheckForBestLevel(DefaultCommand.Settings settings)
{
var bestLevel = 1;
var appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var tempDirectory = "WildGums\\twig\\temp";
var tempPath = Path.Combine(appdataPath, tempDirectory);
settings.OutputPath = Path.Combine(appdataPath, tempDirectory);
settings.Overwrite = true;
var path = settings.Path;
var duration = settings.AdviseDuration;
try
{
for (int level = 1; level < 22; level++)
{
settings.CompressionLevel = level;
var watch = Stopwatch.StartNew();
await Archiver.CompressAsync(path, level, true, false, false, false, tempPath, false);
await Archiver.CompressAsync(settings);
watch.Stop();
if (watch.ElapsedMilliseconds <= duration)
{
Expand All @@ -35,7 +39,7 @@ public static async Task CheckForBestLevel(int duration, string path)
}
finally
{
Directory.Delete(tempPath, true);
Directory.Delete(settings.OutputPath, true);
}

AnsiConsole.MarkupLine($"[green] The best compression level for {path} and duration {duration} is: {bestLevel}. [/]");
Expand Down
Binary file added src/twig/Resources/Icons/compressed.ico
Binary file not shown.
Binary file added src/twig/Resources/Icons/compressed_zs.ico
Binary file not shown.
Binary file added src/twig/Resources/Icons/tw.ico
Binary file not shown.
26 changes: 18 additions & 8 deletions src/twig/twig.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="MSBuild.Sdk.Extras">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>twig</AssemblyName>
Expand All @@ -10,27 +10,25 @@
<PackageTags>console;compress;zstd;WildGums</PackageTags>
</PropertyGroup>

<PropertyGroup>
<UseWpf>false</UseWpf>
<ExtrasEnableImplicitWpfReferences>false</ExtrasEnableImplicitWpfReferences>
</PropertyGroup>

<PropertyGroup>
<!-- SonarQube requires a project guid -->
<ProjectGuid>FD86BFB0-057F-4E66-A55C-C070724FD9D7</ProjectGuid>
<ApplicationIcon />
<ApplicationIcon>Resources\Icons\tw.ico</ApplicationIcon>
<OutputType>Exe</OutputType>
<StartupObject />
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>

<ItemGroup>
<Compile Remove="..\MethodTimeLogger.cs" />
</ItemGroup>

<!-- Note: all should be private assets (embedded using Costura) except for the app API -->
<ItemGroup>
<PackageReference Include="Catel.Core" Version="5.12.17" />
<PackageReference Include="Costura.Fody" Version="5.1.0" PrivateAssets="all" />
<PackageReference Include="Fody" Version="6.5.1" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Expand All @@ -46,4 +44,16 @@
</ItemGroup>

<Import Project="$(MSBuildProjectDirectory)\..\Directory.build.shared.explicit.props" Condition="Exists('$(MSBuildProjectDirectory)\..\Directory.build.shared.explicit.props')" />

<ItemGroup>
<EmbeddedResource Include="Resources\Icons\compressed.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Icons\compressed_zs.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Icons\tw.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>