Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8b50bdb
Initial archive extracting
d2dyno1 Jun 8, 2021
c3bd7ee
Added ZipHelpers
d2dyno1 Jun 9, 2021
249421a
Improve zip helpers
d2dyno1 Jun 9, 2021
a774f45
tmp rm
d2dyno1 Jun 10, 2021
20c6ec5
Improve decompressing operation
d2dyno1 Jun 10, 2021
d7e59b8
Small performance improvement
d2dyno1 Jun 10, 2021
a7b6e10
Added status banner
d2dyno1 Jun 10, 2021
f6288aa
Blazingly fast performance
d2dyno1 Jun 13, 2021
74a3b18
Improve extraction UI
d2dyno1 Jun 13, 2021
e3bc657
Remove status banner when completed
d2dyno1 Jun 13, 2021
8260f83
Added the option to cancel extracting operation
d2dyno1 Jun 13, 2021
9de0459
Merge branch 'main' into cmprs
d2dyno1 Jun 13, 2021
6a7f07a
Added glyph icon to context menu item
d2dyno1 Jun 13, 2021
0d11206
Improved dialog's design
d2dyno1 Jun 13, 2021
50d981d
Added check to open destination directory upon extraction
d2dyno1 Jun 13, 2021
3453949
Fix codestyle
d2dyno1 Jun 13, 2021
54e973b
Do everything in uwp & code simplify
gave92 Jun 13, 2021
91bb2ff
Use WindowsNameTransform to get item name
gave92 Jun 13, 2021
6be2919
Fix folder tree creation
gave92 Jun 13, 2021
0f8d5bf
Added strings
d2dyno1 Jun 15, 2021
f9ec75e
Merge branch 'main' into cmprs
d2dyno1 Jun 15, 2021
cc2052c
Added strings and more context menu options
d2dyno1 Jun 19, 2021
754ac3f
Fix conflicts
d2dyno1 Jun 19, 2021
2e89cb5
Some string changes
d2dyno1 Jun 19, 2021
59b27b9
Hide extract shell option & show extract for msix
gave92 Jun 20, 2021
a3f9c3b
Fix crash when destination folder already exists
gave92 Jun 20, 2021
9eaf45d
Merge branch 'cmprs' of https://github.com/d2dyno1/Files into rev_cmprs
gave92 Jun 20, 2021
ef731b6
Lower case extension checking
d2dyno1 Jun 20, 2021
f0a49ad
Fix nullreferenceexception when clicking on a folder
d2dyno1 Jun 20, 2021
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
50 changes: 50 additions & 0 deletions Files/Dialogs/DecompressArchiveDialog.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<ContentDialog
x:Class="Files.Dialogs.DecompressArchiveDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helpers="using:Files.Helpers"
xmlns:local="using:Files.Dialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Select a destination and extract files"
CornerRadius="{StaticResource OverlayCornerRadius}"
DefaultButton="Primary"
PrimaryButtonText="Extract"
RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}"
SecondaryButtonText="Cancel"
Style="{StaticResource DefaultContentDialogStyle}"
mc:Ignorable="d">

<Grid
MinWidth="400"
Margin="0,16,0,0"
ColumnSpacing="6"
RowSpacing="3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<TextBox
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsReadOnly="True"
Text="{x:Bind ViewModel.DestinationFolderPath, Mode=OneWay}" />

<Button
Grid.Column="1"
VerticalAlignment="Center"
Background="Transparent"
Command="{x:Bind ViewModel.SelectDestinationCommand}"
Content="Browse" />

<CheckBox
Grid.Row="1"
Content="Open destination folder when complete"
IsChecked="{x:Bind ViewModel.OpenDestinationFolderOnCompletion, Mode=TwoWay}" />
</Grid>
</ContentDialog>
34 changes: 34 additions & 0 deletions Files/Dialogs/DecompressArchiveDialog.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Files.ViewModels.Dialogs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

namespace Files.Dialogs
{
public sealed partial class DecompressArchiveDialog : ContentDialog
{
public DecompressArchiveDialogViewModel ViewModel
{
get => (DecompressArchiveDialogViewModel)DataContext;
set => DataContext = value;
}

public DecompressArchiveDialog()
{
this.InitializeComponent();
}
}
}
2 changes: 1 addition & 1 deletion Files/Enums/FileOperationType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public enum FileOperationType : byte
Move = 4,

/// <summary>
/// An item has been extracted
/// An archive has been extracted
/// </summary>
Extract = 5,

Expand Down
12 changes: 12 additions & 0 deletions Files/Files.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
<Compile Include="DataModels\FilesystemItemsOperationDataModel.cs" />
<Compile Include="DataModels\NavigationControlItems\WslDistroItem.cs" />
<Compile Include="DataModels\ShellNewEntry.cs" />
<Compile Include="Dialogs\DecompressArchiveDialog.xaml.cs">
<DependentUpon>DecompressArchiveDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\DynamicDialog.xaml.cs">
<DependentUpon>DynamicDialog.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -248,6 +251,7 @@
<Compile Include="Helpers\WallpaperHelpers.cs" />
<Compile Include="Helpers\WidgetsHelpers.cs" />
<Compile Include="Helpers\Win32Helpers.cs" />
<Compile Include="Helpers\ZipHelpers.cs" />
<Compile Include="Interacts\ItemManipulationModel.cs" />
<Compile Include="ISearchBox.cs" />
<Compile Include="UserControls\ArrangementOptions.xaml.cs">
Expand Down Expand Up @@ -335,6 +339,7 @@
<Compile Include="UserControls\Widgets\WidgetsListControl.xaml.cs">
<DependentUpon>WidgetsListControl.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\Dialogs\DecompressArchiveDialogViewModel.cs" />
<Compile Include="ViewModels\NavToolbarViewModel.cs" />
<Compile Include="ViewModels\Previews\CodePreviewViewModel.cs" />
<Compile Include="ViewModels\Properties\FileSystemProperties.cs" />
Expand Down Expand Up @@ -837,6 +842,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Dialogs\DecompressArchiveDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Dialogs\DynamicDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -1161,6 +1170,9 @@
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
</PackageReference>
<PackageReference Include="SharpZipLib">
<Version>1.3.2</Version>
</PackageReference>
<PackageReference Include="SQLitePCLRaw.bundle_green">
<Version>2.0.4</Version>
</PackageReference>
Expand Down
8 changes: 8 additions & 0 deletions Files/Helpers/ContextFlyoutItemHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,14 @@ public static List<ContextMenuFlyoutItemViewModel> GetBaseItemMenuItems(BaseLayo
IsPrimary = true,
},
new ContextMenuFlyoutItemViewModel()
{
Text = "Extract",
Glyph = "\xF11A",
Command = commandsViewModel.DecompressArchiveCommand,
ShowItem = selectedItems.Count == 1 && selectedItems.First().FileExtension == ".zip",
GlyphFontFamilyName = "CustomGlyph"
},
new ContextMenuFlyoutItemViewModel()
{
Text = "BaseLayoutItemContextFlyoutPinToSidebar/Text".GetLocalized(),
Glyph = "\uE840",
Expand Down
50 changes: 49 additions & 1 deletion Files/Helpers/NativeFileOperationsHelper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;

namespace Files.Helpers
{
Expand Down Expand Up @@ -66,6 +68,12 @@ public static extern IntPtr CreateFileFromApp(
IntPtr hTemplateFile
);

public static SafeFileHandle CreateFileForWrite(string filePath)
{
return new SafeFileHandle(CreateFileFromApp(filePath,
GENERIC_WRITE, 0, IntPtr.Zero, CREATE_ALWAYS, (uint)File_Attributes.BackupSemantics, IntPtr.Zero), true);
}

[DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
Expand All @@ -77,6 +85,14 @@ public static extern IntPtr CreateFile2FromApp(
IntPtr pCreateExParams
);

[DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
public static extern bool CreateDirectoryFromApp(
string lpPathName,
IntPtr SecurityAttributes
);

[DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
Expand Down Expand Up @@ -143,6 +159,18 @@ public unsafe static extern bool WriteFile(
IntPtr lpOverlapped
);

[DllImport("api-ms-win-core-file-l1-2-1.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
public static extern bool WriteFileEx(
IntPtr hFile,
byte[] lpBuffer,
uint nNumberOfBytesToWrite,
[In] ref NativeOverlapped lpOverlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

public delegate void LPOVERLAPPED_COMPLETION_ROUTINE(uint dwErrorCode, uint dwNumberOfBytesTransfered, ref NativeOverlapped lpOverlapped);

public enum GET_FILEEX_INFO_LEVELS
{
GetFileExInfoStandard,
Expand Down Expand Up @@ -236,5 +264,25 @@ public static bool WriteStringToFile(string filePath, string str)
CloseHandle(hStream);
return true;
}

public static bool WriteBufferToFileWithProgress(string filePath, byte[] buffer, LPOVERLAPPED_COMPLETION_ROUTINE callback)
{
using var hFile = CreateFileForWrite(filePath);

if (hFile.IsInvalid)
{
return false;
}

NativeOverlapped nativeOverlapped = new NativeOverlapped();
bool result = WriteFileEx(hFile.DangerousGetHandle(), buffer, (uint)buffer.LongLength, ref nativeOverlapped, callback);

if (!result)
{
System.Diagnostics.Debug.WriteLine(Marshal.GetLastWin32Error());
}

return result;
}
}
}
7 changes: 7 additions & 0 deletions Files/Helpers/StorageItemHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.FileProperties;

namespace Files.Helpers
{
Expand Down Expand Up @@ -101,6 +102,12 @@ async Task GetFolder()
}
}

public static async Task<long> GetFileSize(this IStorageFile file)
{
BasicProperties properties = await file.GetBasicPropertiesAsync();
return (long)properties.Size;
}

public static async Task<FilesystemResult<IStorageItem>> ToStorageItemResult(this IStorageItemWithPath item, IShellPage associatedInstance = null)
{
var returnedItem = new FilesystemResult<IStorageItem>(null, FileSystemStatusCode.Generic);
Expand Down
97 changes: 97 additions & 0 deletions Files/Helpers/ZipHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation.Collections;
using Windows.Storage;

namespace Files.Helpers
{
public static class ZipHelpers
{
public static async Task ExtractArchive(StorageFile archive, StorageFolder destinationFolder, IProgress<float> progressDelegate, CancellationToken cancellationToken)
{
using (ZipFile zipFile = new ZipFile(await archive.OpenStreamForReadAsync()))
{
zipFile.IsStreamOwner = true;

List<ZipEntry> directoryEntries = new List<ZipEntry>();
List<ZipEntry> fileEntries = new List<ZipEntry>();

foreach (ZipEntry entry in zipFile)
{
if (entry.IsFile)
{
fileEntries.Add(entry);
}
else
{
directoryEntries.Add(entry);
}
}

if (cancellationToken.IsCancellationRequested) // Check if cancelled
{
return;
}

var directories = new List<string>();
directories.AddRange(directoryEntries.Select((item) => Path.Combine(destinationFolder.Path, item.Name)));
directories.AddRange(fileEntries.Select((item) => Path.Combine(destinationFolder.Path, Path.GetDirectoryName(item.Name))));
foreach (var dir in directories.Distinct().OrderBy(x => x.Length))
{
NativeFileOperationsHelper.CreateDirectoryFromApp(dir, IntPtr.Zero);
}

if (cancellationToken.IsCancellationRequested) // Check if cancelled
{
return;
}

// Fill files

byte[] buffer = new byte[4096];
int entriesAmount = fileEntries.Count;
int entriesFinished = 0;

foreach (var entry in fileEntries)
{
if (cancellationToken.IsCancellationRequested) // Check if cancelled
{
return;
}

string filePath = Path.Combine(destinationFolder.Path, entry.Name.Replace('/', '\\'));

var hFile = NativeFileOperationsHelper.CreateFileForWrite(filePath);

using (FileStream destinationStream = new FileStream(hFile, FileAccess.ReadWrite))
{
int currentBlockSize = 0;

using (Stream entryStream = zipFile.GetInputStream(entry))
{
while ((currentBlockSize = await entryStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await destinationStream.WriteAsync(buffer, 0, buffer.Length);

if (cancellationToken.IsCancellationRequested) // Check if cancelled
{
return;
}
}
}
}
// We don't close handleContext because FileStream.Dispose() already does that

entriesFinished++;
float percentage = (float)((float)entriesFinished / (float)entriesAmount) * 100.0f;
progressDelegate?.Report(percentage);
}
}
}
}
}
Loading