diff --git a/src/Files.App/Utils/Shell/ShellFolderExtensions.cs b/src/Files.App/Utils/Shell/ShellFolderExtensions.cs index d9c8dbdadbd8..8389d1d6629b 100644 --- a/src/Files.App/Utils/Shell/ShellFolderExtensions.cs +++ b/src/Files.App/Utils/Shell/ShellFolderExtensions.cs @@ -1,7 +1,9 @@ // Copyright (c) Files Community // Licensed under the MIT License. +using FluentFTP.Helpers; using System.IO; +using System.Text; using Vanara.PInvoke; using Vanara.Windows.Shell; @@ -42,6 +44,52 @@ private static T TryGetProperty(this ShellItemPropertyStore sip, Ole32.PROPER return value; } + private static string? GetOriginalPathFromRecycleBinFile(string? physicalPath) + { + if (string.IsNullOrEmpty(physicalPath)) + return null; + + var fileNameFromPath = Path.GetFileName(physicalPath); + + if (!fileNameFromPath.StartsWith("$R", StringComparison.OrdinalIgnoreCase) && + !fileNameFromPath.StartsWith("$I", StringComparison.OrdinalIgnoreCase)) + return null; + + try + { + string directory = Path.GetDirectoryName(physicalPath)!; + + string iFileName = fileNameFromPath.StartsWith("$R", StringComparison.OrdinalIgnoreCase) ? + "$I" + fileNameFromPath.RemovePrefix("$R") : fileNameFromPath.ToString(); + + string filePath = Path.Combine(directory, iFileName); + + if (!File.Exists(filePath) && !Directory.Exists(filePath)) + return null; + + + using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var reader = new BinaryReader(stream, Encoding.Unicode); + + // From the 24th to the 28th, contains the number of characters for the originalPath. + reader.BaseStream.Seek(24, SeekOrigin.Begin); + int pathCharCount = reader.ReadInt32(); + + // From the 28th byte, that's where the OriginalPath is. + // Windows is UTF-16, so each character takes 2 bytes, hence the multiplication. + byte[] pathBytes = reader.ReadBytes(pathCharCount * 2); + string originalPath = Encoding.Unicode.GetString(pathBytes).TrimEnd('\0'); + + return originalPath; + } + catch + { + + } + + return null; + } + public static ShellFileItem GetShellFileItem(ShellItem folderItem) { if (folderItem is null) @@ -57,6 +105,8 @@ public static ShellFileItem GetShellFileItem(ShellItem folderItem) // True path on disk parsingPath ??= folderItem.FileSystemPath; + string? recycleBinFileOriginalPath = GetOriginalPathFromRecycleBinFile(folderItem.FileSystemPath); + if (parsingPath is null || !Path.IsPathRooted(parsingPath)) { parsingPath = parsingPath switch @@ -110,7 +160,9 @@ public static ShellFileItem GetShellFileItem(ShellItem folderItem) string fileSize = fileSizeBytes is not null ? folderItem.Properties.GetPropertyString(Ole32.PROPERTYKEY.System.Size) : null; var fileType = folderItem.Properties.TryGetProperty(Ole32.PROPERTYKEY.System.ItemTypeText); - return new(isFolder, parsingPath, fileName, filePath, recycleDate, modifiedDate, createdDate, fileSize, fileSizeBytes ?? 0, fileType, folderItem.PIDL.GetBytes()); + string finalFilePath = string.IsNullOrEmpty(recycleBinFileOriginalPath) ? filePath : recycleBinFileOriginalPath; + + return new(isFolder, parsingPath, fileName, finalFilePath, recycleDate, modifiedDate, createdDate, fileSize, fileSizeBytes ?? 0, fileType, folderItem.PIDL.GetBytes()); } public static ShellLinkItem GetShellLinkItem(ShellLink linkItem) diff --git a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs index 4189cdac8474..aa52474b54d8 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs @@ -84,11 +84,12 @@ public static bool IsShellPath(string path) public static ShellStorageFolder FromShellItem(ShellFileItem item) { + if (item.RecyclePath.Contains("$Recycle.Bin", StringComparison.OrdinalIgnoreCase)) + return new BinStorageFolder(item); + if (item is ShellLinkItem linkItem) return new ShortcutStorageFolder(linkItem); - if (item.RecyclePath.Contains("$Recycle.Bin", StringComparison.OrdinalIgnoreCase)) - return new BinStorageFolder(item); return new ShellStorageFolder(item); }