mirror of
https://github.com/QL-Win/QuickLook.git
synced 2026-01-13 07:05:24 +08:00
Introduces CompoundInfoPanel for viewing Compound File Binary archives (.cfb, .eif) in the ArchiveViewer plugin. Updates Plugin.cs to detect and use the new panel for these file types, enabling preview and information display for CFB-based archives.
158 lines
4.8 KiB
C#
158 lines
4.8 KiB
C#
using QuickLook.Common.ExtensionMethods;
|
|
using QuickLook.Common.Helpers;
|
|
using QuickLook.Plugin.ArchiveViewer.ArchiveFile;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices.ComTypes;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Controls;
|
|
|
|
namespace QuickLook.Plugin.ArchiveViewer.CompoundFileBinary;
|
|
|
|
public partial class CompoundInfoPanel : UserControl, IDisposable, INotifyPropertyChanged
|
|
{
|
|
private readonly Dictionary<string, ArchiveFileEntry> _fileEntries = [];
|
|
private bool _disposed;
|
|
private double _loadPercent;
|
|
private ulong _totalSize;
|
|
|
|
public CompoundInfoPanel(string path)
|
|
{
|
|
InitializeComponent();
|
|
|
|
// design-time only
|
|
Resources.MergedDictionaries.Clear();
|
|
|
|
BeginLoadArchive(path);
|
|
}
|
|
|
|
public double LoadPercent
|
|
{
|
|
get => _loadPercent;
|
|
private set
|
|
{
|
|
if (value == _loadPercent) return;
|
|
_loadPercent = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
GC.SuppressFinalize(this);
|
|
|
|
_disposed = true;
|
|
|
|
fileListView.Dispose();
|
|
}
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
private void BeginLoadArchive(string path)
|
|
{
|
|
new Task(() =>
|
|
{
|
|
_totalSize = (ulong)new FileInfo(path).Length;
|
|
|
|
var root = new ArchiveFileEntry(Path.GetFileName(path), true);
|
|
_fileEntries.Add(string.Empty, root);
|
|
|
|
try
|
|
{
|
|
LoadItemsFromArchive(path);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ProcessHelper.WriteLog(e.ToString());
|
|
Dispatcher.Invoke(() => { lblLoading.Content = "Preview failed. See log for more details."; });
|
|
return;
|
|
}
|
|
|
|
var folders = -1; // do not count root node
|
|
var files = 0;
|
|
ulong sizeU = 0L;
|
|
|
|
foreach (var item in _fileEntries)
|
|
{
|
|
if (item.Value.IsFolder)
|
|
folders++;
|
|
else
|
|
files++;
|
|
|
|
sizeU += item.Value.Size;
|
|
}
|
|
|
|
string t;
|
|
var d = folders != 0 ? $"{folders} folders" : string.Empty;
|
|
var f = files != 0 ? $"{files} files" : string.Empty;
|
|
if (!string.IsNullOrEmpty(d) && !string.IsNullOrEmpty(f))
|
|
t = $", {d} and {f}";
|
|
else if (string.IsNullOrEmpty(d) && string.IsNullOrEmpty(f))
|
|
t = string.Empty;
|
|
else
|
|
t = $", {d}{f}";
|
|
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
fileListView.SetDataContext(_fileEntries[string.Empty].Children.Keys);
|
|
archiveCount.Content = $"Compound File{t}";
|
|
archiveSizeC.Content = string.Empty;
|
|
archiveSizeU.Content = $"Total stream size {((long)sizeU).ToPrettySize(2)}";
|
|
});
|
|
|
|
LoadPercent = 100d;
|
|
}).Start();
|
|
}
|
|
|
|
private void LoadItemsFromArchive(string path)
|
|
{
|
|
using var storage = new DisposableIStorage(path, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero);
|
|
ProcessStorage(storage, string.Empty);
|
|
}
|
|
|
|
private void ProcessStorage(DisposableIStorage storage, string currentPath)
|
|
{
|
|
var enumerator = storage.EnumElements();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
if (_disposed) return;
|
|
|
|
var stat = enumerator.Current;
|
|
var name = stat.pwcsName;
|
|
var fullPath = string.IsNullOrEmpty(currentPath) ? name : currentPath + "\\" + name;
|
|
|
|
_fileEntries.TryGetValue(currentPath, out var parent);
|
|
|
|
if (stat.type == (int)STGTY.STGTY_STORAGE)
|
|
{
|
|
var entry = new ArchiveFileEntry(name, true, parent);
|
|
_fileEntries.Add(fullPath, entry);
|
|
|
|
using var subStorage = storage.OpenStorage(name, null, STGM.READ | STGM.SHARE_EXCLUSIVE, IntPtr.Zero);
|
|
ProcessStorage(subStorage, fullPath);
|
|
}
|
|
else if (stat.type == (int)STGTY.STGTY_STREAM)
|
|
{
|
|
long fileTime = ((long)stat.mtime.dwHighDateTime << 32) | (uint)stat.mtime.dwLowDateTime;
|
|
var entry = new ArchiveFileEntry(name, false, parent)
|
|
{
|
|
Size = (ulong)stat.cbSize,
|
|
ModifiedDate = DateTime.FromFileTimeUtc(fileTime).ToLocalTime()
|
|
};
|
|
_fileEntries.Add(fullPath, entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|