Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show file hashes in properties like HashTab. #3722 #4300

Closed
wants to merge 12 commits into from
7 changes: 7 additions & 0 deletions Files/Files.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@
<Compile Include="ViewModels\SettingsViewModels\OnStartupViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModels\PreferencesViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModels\WidgetsViewModel.cs" />
<Compile Include="Views\Pages\PropertiesHashes.xaml.cs">
<DependentUpon>PropertiesHashes.xaml</DependentUpon>
</Compile>
<Compile Include="Views\PaneHolderPage.xaml.cs">
<DependentUpon>PaneHolderPage.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -858,6 +861,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\Pages\PropertiesHashes.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\PaneHolderPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down
101 changes: 95 additions & 6 deletions Files/Interacts/Interaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ public void UnpinItem_Click(object sender, RoutedEventArgs e)
{
oleitao marked this conversation as resolved.
Show resolved Hide resolved
foreach (ListedItem listedItem in AssociatedInstance.ContentPage.SelectedItems)
{
App.SidebarPinnedController.Model.RemoveItem(listedItem.ItemPath);
App.SidebarPinnedController.Model.RemoveItem(listedItem.ItemPath);
}
}
}
Expand Down Expand Up @@ -1379,10 +1379,9 @@ private void JumpTimer_Tick(object sender, object e)
jumpTimer.Stop();
}

public async Task<string> GetHashForFileAsync(ListedItem fileItem, string nameOfAlg, CancellationToken token, Microsoft.UI.Xaml.Controls.ProgressBar progress)
public async Task<string> GetHashForFileAsync(StorageFile itemFromPath, string nameOfAlg, CancellationToken token, Microsoft.UI.Xaml.Controls.ProgressBar progress)
{
HashAlgorithmProvider algorithmProvider = HashAlgorithmProvider.OpenAlgorithm(nameOfAlg);
StorageFile itemFromPath = await AssociatedInstance.FilesystemViewModel.GetFileFromPathAsync((fileItem as ShortcutItem)?.TargetPath ?? fileItem.ItemPath);
if (itemFromPath == null)
{
return "";
Expand All @@ -1409,7 +1408,7 @@ public async Task<string> GetHashForFileAsync(ListedItem fileItem, string nameOf

Windows.Storage.Streams.Buffer buffer = new Windows.Storage.Streams.Buffer(capacity);
var hash = algorithmProvider.CreateHash();
while (!token.IsCancellationRequested)
while (token.IsCancellationRequested)
{
await inputStream.ReadAsync(buffer, capacity, InputStreamOptions.None);
if (buffer.Length > 0)
Expand All @@ -1427,11 +1426,43 @@ public async Task<string> GetHashForFileAsync(ListedItem fileItem, string nameOf
}
inputStream.Dispose();
stream.Dispose();
if (token.IsCancellationRequested)

return CryptographicBuffer.EncodeToHexString(hash.GetValueAndReset()).ToLower();
}

public async Task<string> GetCRC32HashForFileAsync(StorageFile itemFromPath, Microsoft.UI.Xaml.Controls.ProgressBar progress)
{
if (itemFromPath == null)
{
return "";
}
return CryptographicBuffer.EncodeToHexString(hash.GetValueAndReset()).ToLower();

Stream stream = await FilesystemTasks.Wrap(() => itemFromPath.OpenStreamForReadAsync());
if (stream == null)
{
return "";
}

byte[] fileBytes = await GetBytesAsync(itemFromPath);
var crc32 = new Crc32();

return crc32.Get(fileBytes).ToString("X");
}

public static async Task<byte[]> GetBytesAsync(StorageFile file)
{
byte[] fileBytes = null;
if (file == null) return null;
using (var stream = await file.OpenReadAsync())
{
fileBytes = new byte[stream.Size];
using (var reader = new DataReader(stream))
{
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(fileBytes);
}
}
return fileBytes;
}

public static async Task EjectDeviceAsync(string path)
Expand Down Expand Up @@ -1484,4 +1515,62 @@ await DialogDisplayHelper.ShowDialogAsync(
}
}
}

/// <summary>
/// https://rosettacode.org/wiki/CRC-32#C.23
/// </summary>

public class Crc32
oleitao marked this conversation as resolved.
Show resolved Hide resolved
{
#region Constants
/// <summary>
/// Generator polynomial (modulo 2) for the reversed CRC32 algorithm.
/// </summary>
private const UInt32 s_generator = 0xEDB88320;
#endregion

#region Constructors
/// <summary>
/// Creates a new instance of the Crc32 class.
/// </summary>
public Crc32()
{
// Constructs the checksum lookup table. Used to optimize the checksum.
m_checksumTable = Enumerable.Range(0, 256).Select(i =>
{
var tableEntry = (uint)i;
for (var j = 0; j < 8; ++j)
{
tableEntry = ((tableEntry & 1) != 0)
? (s_generator ^ (tableEntry >> 1))
: (tableEntry >> 1);
}
return tableEntry;
}).ToArray();
}
#endregion

#region Methods
/// <summary>
/// Calculates the checksum of the byte stream.
/// </summary>
/// <param name="byteStream">The byte stream to calculate the checksum for.</param>
/// <returns>A 32-bit reversed checksum.</returns>
public UInt32 Get<T>(IEnumerable<T> byteStream)
{
// Initialize checksumRegister to 0xFFFFFFFF and calculate the checksum.
return ~byteStream.Aggregate(0xFFFFFFFF, (checksumRegister, currentByte) =>
(m_checksumTable[(checksumRegister & 0xFF) ^ Convert.ToByte(currentByte)] ^ (checksumRegister >> 8)));
}
#endregion

#region Fields
/// <summary>
/// Contains a cache of calculated checksum chunks.
/// </summary>
private readonly UInt32[] m_checksumTable;

#endregion
}

}
94 changes: 91 additions & 3 deletions Files/ViewModels/Properties/FileProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,85 @@ public override async void GetSpecialProperties()
GetOtherProperties(file.Properties);

// Get file MD5 hash
var hashAlgTypeName = HashAlgorithmNames.Md5;
ViewModel.ItemMD5HashProgressVisibility = Visibility.Visible;
ViewModel.ItemMD5HashVisibility = Visibility.Visible;

// Get file SHA1 hash
ViewModel.ItemSHA1HashProgressVisibility = Visibility.Visible;
ViewModel.ItemSHA1HashVisibility = Visibility.Visible;

// Get file CRC32 hash
ViewModel.ItemCRC32HashProgressVisibility = Visibility.Visible;
ViewModel.ItemCRC32HashVisibility = Visibility.Visible;

try
{
ViewModel.ItemMD5Hash = await AppInstance.InteractionOperations
.GetHashForFileAsync(Item, hashAlgTypeName, TokenSource.Token, ProgressBar);
TokenSource.Cancel();
ViewModel.ItemMD5Hash = await AppInstance.InteractionOperations.GetHashForFileAsync(file, HashAlgorithmNames.Md5, TokenSource.Token, ProgressBar);
ViewModel.ItemSHA1Hash = await AppInstance.InteractionOperations.GetHashForFileAsync(file, HashAlgorithmNames.Sha1, TokenSource.Token, ProgressBar);
ViewModel.ItemCRC32Hash = await AppInstance.InteractionOperations.GetCRC32HashForFileAsync(file, ProgressBar);
}
catch (Exception ex)
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, ex.Message);
ViewModel.ItemMD5HashCalcError = true;
ViewModel.ItemSHA1HashCalcError = true;
ViewModel.ItemCRC32HashCalcError = true;
}
}

public async Task<string> GenerateMD5Compare(StorageFile file)
{
string hash;
// Get file MD5 hash
try
{
TokenSource.Cancel();
hash = await AppInstance.InteractionOperations.GetHashForFileAsync(file, HashAlgorithmNames.Md5, TokenSource.Token, ProgressBar);
}
catch (Exception ex)
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, ex.Message);
hash = string.Empty;
}

return hash;
}

public async Task<string> GenerateSHA1Compare(StorageFile file)
{
string hash;
// Get file SHA1 hash
try
{
TokenSource.Cancel();
hash = await AppInstance.InteractionOperations.GetHashForFileAsync(file, HashAlgorithmNames.Sha1, TokenSource.Token, ProgressBar);
}
catch (Exception ex)
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, ex.Message);
hash = string.Empty;
}

return hash;
}

public async Task<string> GenerateCRC32Compare(StorageFile file)
{
string hash;
// Get file CRC32 hash
try
{
TokenSource.Cancel();
hash = await AppInstance.InteractionOperations.GetCRC32HashForFileAsync(file, ProgressBar);
}
catch (Exception ex)
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, ex.Message);
hash = string.Empty;
}

return hash;
}

public async void GetSystemFileProperties()
Expand All @@ -178,6 +244,28 @@ public async void GetSystemFileProperties()
ViewModel.FileProperties = new ObservableCollection<FileProperty>(list.Where(i => i.Value != null));
}

public async Task<string> GetSystemFileHashes(string hashType, StorageFile file)
{
if (file == null)
{
// Could not access file, can't show any other property
return "";
}

if (hashType.Equals(HashAlgorithmNames.Md5))
{
return await GenerateMD5Compare(file);
}
else if (hashType.Equals(HashAlgorithmNames.Sha1))
{
return await GenerateSHA1Compare(file);
}
else
{
return await GenerateCRC32Compare(file);
}
}

public static async Task<string> GetAddressFromCoordinatesAsync(double? Lat, double? Lon)
{
if (!Lat.HasValue || !Lon.HasValue)
Expand Down
78 changes: 78 additions & 0 deletions Files/ViewModels/SelectedItemsPropertiesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,84 @@ public Visibility ItemMD5HashProgressVisibility
set => SetProperty(ref itemMD5HashProgressVisibiity, value);
}

public string itemSHA1Hash;

public string ItemSHA1Hash
{
get => itemSHA1Hash;
set
{
if (!string.IsNullOrEmpty(value) && value != itemSHA1Hash)
{
SetProperty(ref itemSHA1Hash, value);
ItemSHA1HashProgressVisibility = Visibility.Collapsed;
}
}
}

private bool itemSHA1HashCalcError;

public bool ItemSHA1HashCalcError
{
get => itemSHA1HashCalcError;
set => SetProperty(ref itemSHA1HashCalcError, value);
}

public Visibility itemSHA1HashVisibility = Visibility.Collapsed;

public Visibility ItemSHA1HashVisibility
{
get => itemSHA1HashVisibility;
set => SetProperty(ref itemSHA1HashVisibility, value);
}

public Visibility itemSHA1HashProgressVisibiity = Visibility.Collapsed;

public Visibility ItemSHA1HashProgressVisibility
{
get => itemSHA1HashProgressVisibiity;
set => SetProperty(ref itemSHA1HashProgressVisibiity, value);
}

public string itemCRC32Hash;

public string ItemCRC32Hash
{
get => itemCRC32Hash;
set
{
if (!string.IsNullOrEmpty(value) && value != itemCRC32Hash)
{
SetProperty(ref itemCRC32Hash, value);
ItemCRC32HashProgressVisibility = Visibility.Collapsed;
}
}
}

private bool itemCRC32HashCalcError;

public bool ItemCRC32HashCalcError
{
get => itemCRC32HashCalcError;
set => SetProperty(ref itemCRC32HashCalcError, value);
}

public Visibility itemCRC32HashVisibility = Visibility.Collapsed;

public Visibility ItemCRC32HashVisibility
{
get => itemCRC32HashVisibility;
set => SetProperty(ref itemCRC32HashVisibility, value);
}

public Visibility itemCRC32HashProgressVisibiity = Visibility.Collapsed;

public Visibility ItemCRC32HashProgressVisibility
{
get => itemCRC32HashProgressVisibiity;
set => SetProperty(ref itemCRC32HashProgressVisibiity, value);
}

public int foldersCount;

public int FoldersCount
Expand Down
9 changes: 9 additions & 0 deletions Files/Views/Pages/Properties.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@
<FontIcon FontFamily="{StaticResource FluentGlyphs}" Glyph="&#xE7C3;" />
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem
x:Name="TabHashes"
x:Uid="PropertiesDialogTabHashes"
Content="Hashes"
Tag="Hashes">
<NavigationViewItem.Icon>
<FontIcon FontFamily="{StaticResource FluentGlyphs}" Glyph="&#xE946;" />
oleitao marked this conversation as resolved.
Show resolved Hide resolved
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem
x:Name="TabShorcut"
x:Uid="PropertiesDialogTabShortcut"
Expand Down
4 changes: 4 additions & 0 deletions Files/Views/Pages/Properties.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ private void NavigationView_SelectionChanged(NavigationView sender, NavigationVi
case "Details":
contentFrame.Navigate(typeof(PropertiesDetails), navParam, args.RecommendedNavigationTransitionInfo);
break;

case "Hashes":
contentFrame.Navigate(typeof(PropertiesHashes), navParam, args.RecommendedNavigationTransitionInfo);
break;
}
}

Expand Down
Loading