Copy from ISO
This commit is contained in:
@@ -100,6 +100,9 @@ public class ContainerSizeSizeScanProvider : ContentProviderBase, IContainerSize
|
|||||||
|
|
||||||
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null;
|
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null;
|
||||||
|
|
||||||
|
public override ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath)
|
||||||
|
=> ValueTask.FromResult<NativePath?>(nativePath);
|
||||||
|
|
||||||
public ISizeScanTask StartSizeScan(IContainer scanSizeOf)
|
public ISizeScanTask StartSizeScan(IContainer scanSizeOf)
|
||||||
{
|
{
|
||||||
var searchTask = _serviceProvider
|
var searchTask = _serviceProvider
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace FileTime.App.Core.Services.UserCommandHandler;
|
|||||||
public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||||
{
|
{
|
||||||
private const int PageSize = 8;
|
private const int PageSize = 8;
|
||||||
|
|
||||||
private readonly IAppState _appState;
|
private readonly IAppState _appState;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly ILocalContentProvider _localContentProvider;
|
private readonly ILocalContentProvider _localContentProvider;
|
||||||
@@ -33,6 +34,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
private readonly IContentProviderRegistry _contentProviderRegistry;
|
private readonly IContentProviderRegistry _contentProviderRegistry;
|
||||||
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
||||||
private readonly ApplicationConfiguration _applicationConfiguration;
|
private readonly ApplicationConfiguration _applicationConfiguration;
|
||||||
|
|
||||||
private ITabViewModel? _selectedTab;
|
private ITabViewModel? _selectedTab;
|
||||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||||
@@ -147,9 +149,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task GoByFrequency()
|
private async Task GoByFrequency()
|
||||||
{
|
=> await _frequencyNavigationService.OpenNavigationWindow();
|
||||||
await _frequencyNavigationService.OpenNavigationWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GoToPath()
|
private async Task GoToPath()
|
||||||
{
|
{
|
||||||
@@ -166,6 +166,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolvedPath is IContainer container)
|
if (resolvedPath is IContainer container)
|
||||||
@@ -173,10 +174,10 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
await _userCommandHandlerService.HandleCommandAsync(
|
await _userCommandHandlerService.HandleCommandAsync(
|
||||||
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, container)));
|
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, container)));
|
||||||
}
|
}
|
||||||
else if (resolvedPath is IElement element)
|
else if (resolvedPath is IElement {Parent: { } parent})
|
||||||
{
|
{
|
||||||
await _userCommandHandlerService.HandleCommandAsync(
|
await _userCommandHandlerService.HandleCommandAsync(
|
||||||
new OpenContainerCommand(element.Parent!));
|
new OpenContainerCommand(parent));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -492,7 +493,10 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
|
|
||||||
private Task CloseTab()
|
private Task CloseTab()
|
||||||
{
|
{
|
||||||
if ((!_applicationConfiguration.AllowCloseLastTab && _appState.Tabs.Count < 2) || _selectedTab == null) return Task.CompletedTask;
|
if ((!_applicationConfiguration.AllowCloseLastTab && _appState.Tabs.Count < 2) || _selectedTab == null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
var tabToRemove = _selectedTab;
|
var tabToRemove = _selectedTab;
|
||||||
_appState.RemoveTab(tabToRemove!);
|
_appState.RemoveTab(tabToRemove!);
|
||||||
@@ -503,6 +507,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public class SearchContentProvider : ContentProviderBase, ISearchContentProvider
|
|||||||
|
|
||||||
public override Task<bool> CanHandlePathAsync(NativePath path) => Task.FromResult(path.Path.StartsWith(ContentProviderName));
|
public override Task<bool> CanHandlePathAsync(NativePath path) => Task.FromResult(path.Path.StartsWith(ContentProviderName));
|
||||||
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null;
|
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null;
|
||||||
|
public override ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath) => throw new NotImplementedException();
|
||||||
|
|
||||||
public async Task<ISearchTask> StartSearchAsync(ISearchMatcher matcher, IContainer searchIn)
|
public async Task<ISearchTask> StartSearchAsync(ISearchMatcher matcher, IContainer searchIn)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,4 +29,5 @@ public interface IContentProvider : IContainer, IOnContainerEnter
|
|||||||
Task<bool> CanHandlePathAsync(NativePath path);
|
Task<bool> CanHandlePathAsync(NativePath path);
|
||||||
Task<bool> CanHandlePathAsync(FullName path);
|
Task<bool> CanHandlePathAsync(FullName path);
|
||||||
VolumeSizeInfo? GetVolumeSizeInfo(FullName path);
|
VolumeSizeInfo? GetVolumeSizeInfo(FullName path);
|
||||||
|
ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath);
|
||||||
}
|
}
|
||||||
@@ -95,5 +95,6 @@ public abstract class ContentProviderBase : IContentProvider
|
|||||||
|
|
||||||
public abstract VolumeSizeInfo? GetVolumeSizeInfo(FullName path);
|
public abstract VolumeSizeInfo? GetVolumeSizeInfo(FullName path);
|
||||||
|
|
||||||
public IItem WithParent(AbsolutePath parent) => this;
|
public IItem WithParent(AbsolutePath parent) => this;
|
||||||
|
public abstract ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath);
|
||||||
}
|
}
|
||||||
@@ -86,6 +86,7 @@ public class RootContentProvider : IRootContentProvider
|
|||||||
|
|
||||||
public Task<bool> CanHandlePathAsync(FullName path) => throw new NotImplementedException();
|
public Task<bool> CanHandlePathAsync(FullName path) => throw new NotImplementedException();
|
||||||
public VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null;
|
public VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null;
|
||||||
|
public ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IItem WithParent(AbsolutePath parent) => this;
|
public IItem WithParent(AbsolutePath parent) => this;
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,14 @@ namespace FileTime.Core.ContentAccess;
|
|||||||
|
|
||||||
public abstract class SubContentProviderBase : ContentProviderBase
|
public abstract class SubContentProviderBase : ContentProviderBase
|
||||||
{
|
{
|
||||||
private readonly IContentProvider _parentContentProvider;
|
public IContentProvider ParentContentProvider { get; }
|
||||||
|
|
||||||
protected SubContentProviderBase(
|
protected SubContentProviderBase(
|
||||||
IContentProvider parentContentProvider,
|
IContentProvider parentContentProvider,
|
||||||
string name,
|
string name,
|
||||||
ITimelessContentProvider timelessContentProvider) : base(name, timelessContentProvider)
|
ITimelessContentProvider timelessContentProvider) : base(name, timelessContentProvider)
|
||||||
{
|
{
|
||||||
_parentContentProvider = parentContentProvider;
|
ParentContentProvider = parentContentProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<IItem> GetItemByNativePathAsync(
|
public override async Task<IItem> GetItemByNativePathAsync(
|
||||||
@@ -22,7 +22,7 @@ public abstract class SubContentProviderBase : ContentProviderBase
|
|||||||
bool forceResolve = false,
|
bool forceResolve = false,
|
||||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
ItemInitializationSettings itemInitializationSettings = default)
|
ItemInitializationSettings itemInitializationSettings = default)
|
||||||
=> await _parentContentProvider.GetItemByNativePathAsync(
|
=> await ParentContentProvider.GetItemByNativePathAsync(
|
||||||
nativePath,
|
nativePath,
|
||||||
pointInTime,
|
pointInTime,
|
||||||
forceResolve,
|
forceResolve,
|
||||||
@@ -30,17 +30,14 @@ public abstract class SubContentProviderBase : ContentProviderBase
|
|||||||
itemInitializationSettings);
|
itemInitializationSettings);
|
||||||
|
|
||||||
public override async ValueTask<NativePath> GetNativePathAsync(FullName fullName)
|
public override async ValueTask<NativePath> GetNativePathAsync(FullName fullName)
|
||||||
=> await _parentContentProvider.GetNativePathAsync(fullName);
|
=> await ParentContentProvider.GetNativePathAsync(fullName);
|
||||||
|
|
||||||
public override FullName GetFullName(NativePath nativePath)
|
public override FullName GetFullName(NativePath nativePath)
|
||||||
=> _parentContentProvider.GetFullName(nativePath);
|
=> ParentContentProvider.GetFullName(nativePath);
|
||||||
|
|
||||||
public override async Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
|
|
||||||
=> await _parentContentProvider.GetContentAsync(element, maxLength, cancellationToken);
|
|
||||||
|
|
||||||
public override async Task<bool> CanHandlePathAsync(NativePath path)
|
public override async Task<bool> CanHandlePathAsync(NativePath path)
|
||||||
=> await _parentContentProvider.CanHandlePathAsync(path);
|
=> await ParentContentProvider.CanHandlePathAsync(path);
|
||||||
|
|
||||||
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path)
|
public override async ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath)
|
||||||
=> _parentContentProvider.GetVolumeSizeInfo(path);
|
=> await ParentContentProvider.GetSupportedPathPart(nativePath);
|
||||||
}
|
}
|
||||||
@@ -164,6 +164,22 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath)
|
||||||
|
{
|
||||||
|
var path = nativePath.Path;
|
||||||
|
var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar)).ToArray();
|
||||||
|
|
||||||
|
for (var i = pathParts.Length - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
var possiblePath = string.Join(Path.DirectorySeparatorChar, pathParts.Take(i));
|
||||||
|
if (!File.Exists(possiblePath) && !Directory.Exists(possiblePath)) continue;
|
||||||
|
|
||||||
|
return ValueTask.FromResult<NativePath?>(new NativePath(possiblePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValueTask.FromResult<NativePath?>(null);
|
||||||
|
}
|
||||||
|
|
||||||
private Container CreateEmptyContainer(NativePath nativePath,
|
private Container CreateEmptyContainer(NativePath nativePath,
|
||||||
PointInTime pointInTime,
|
PointInTime pointInTime,
|
||||||
IEnumerable<Exception>? initialExceptions = null)
|
IEnumerable<Exception>? initialExceptions = null)
|
||||||
|
|||||||
@@ -40,10 +40,7 @@ public class LocalContentReader : IContentReader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPosition(long position)
|
public void SetPosition(long position) => Position = position;
|
||||||
{
|
|
||||||
Position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream AsStream() => _binaryReader.BaseStream;
|
public Stream AsStream() => _binaryReader.BaseStream;
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentP
|
|||||||
|
|
||||||
public override Task<bool> CanHandlePathAsync(NativePath path) => throw new NotImplementedException();
|
public override Task<bool> CanHandlePathAsync(NativePath path) => throw new NotImplementedException();
|
||||||
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => throw new NotImplementedException();
|
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => throw new NotImplementedException();
|
||||||
|
public override ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath) => throw new NotImplementedException();
|
||||||
|
|
||||||
private string ConvertLocalFullNameToRemote(FullName fullName)
|
private string ConvertLocalFullNameToRemote(FullName fullName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>FileTime.Tools.VirtualDiskSources</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public interface IVirtualDiskContentProvider : IContentProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public interface IVirtualDiskContentProviderFactory
|
||||||
|
{
|
||||||
|
IVirtualDiskContentProvider Create(IContentProvider parentContentProvider);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
namespace FileTime.Tools.VirtualDiskSources.Abstractions;
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
public interface IVirtualDiskSubContentProvider : ISubContentProvider
|
public interface IVirtualDiskSubContentProvider : ISubContentProvider
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Tools.VirtualDiskSources.Abstractions;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
@@ -13,6 +12,8 @@ public static class Startup
|
|||||||
services.TryAddSingleton<IVirtualDiskSubContentProvider, VirtualDiskSubContentProvider>();
|
services.TryAddSingleton<IVirtualDiskSubContentProvider, VirtualDiskSubContentProvider>();
|
||||||
services.AddSingleton<ISubContentProvider>(sp => sp.GetRequiredService<IVirtualDiskSubContentProvider>());
|
services.AddSingleton<ISubContentProvider>(sp => sp.GetRequiredService<IVirtualDiskSubContentProvider>());
|
||||||
services.AddSingleton<IPreStartupHandler, DiscUtilsInitializer>();
|
services.AddSingleton<IPreStartupHandler, DiscUtilsInitializer>();
|
||||||
|
services.TryAddSingleton<IVirtualDiskContentProviderFactory, VirtualDiskContentProviderFactory>();
|
||||||
|
services.TryAddSingleton<IContentReaderFactory<VirtualDiskContentProvider>, VirtualDiskContentReaderFactory>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,50 @@
|
|||||||
using FileTime.Core.ContentAccess;
|
using DiscUtils.Udf;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Tools.VirtualDiskSources;
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
public class VirtualDiskContentProvider : SubContentProviderBase
|
public class VirtualDiskContentProvider : SubContentProviderBase, IVirtualDiskContentProvider
|
||||||
{
|
{
|
||||||
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
|
|
||||||
public VirtualDiskContentProvider(
|
public VirtualDiskContentProvider(
|
||||||
IContentProvider parentContentProvider,
|
IContentProvider parentContentProvider,
|
||||||
ITimelessContentProvider timelessContentProvider)
|
ITimelessContentProvider timelessContentProvider,
|
||||||
|
IContentAccessorFactory contentAccessorFactory)
|
||||||
: base(parentContentProvider, "virtual-disk", timelessContentProvider)
|
: base(parentContentProvider, "virtual-disk", timelessContentProvider)
|
||||||
{
|
{
|
||||||
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var elementNativePath = element.NativePath!;
|
||||||
|
|
||||||
|
var supportedPath = await ParentContentProvider.GetSupportedPathPart(elementNativePath);
|
||||||
|
if (supportedPath is null) return null;
|
||||||
|
|
||||||
|
var parentItem = await ParentContentProvider.GetItemByNativePathAsync(supportedPath, element.PointInTime);
|
||||||
|
if (parentItem is not IElement parentElement) return null;
|
||||||
|
|
||||||
|
|
||||||
|
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
|
||||||
|
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
|
||||||
|
|
||||||
|
await using var readerStream = reader.AsStream();
|
||||||
|
using var discReader = new UdfReader(readerStream);
|
||||||
|
|
||||||
|
var subPath = elementNativePath.Path.Substring(supportedPath.Path.Length + 2 + Constants.SubContentProviderRootContainer.Length);
|
||||||
|
var fileInfo = discReader.GetFileInfo(subPath);
|
||||||
|
|
||||||
|
await using var contentReader = fileInfo.Open(FileMode.Open, FileAccess.Read);
|
||||||
|
var data = new byte[1024 * 1024];
|
||||||
|
var readAsync = await contentReader.ReadAsync(data, cancellationToken);
|
||||||
|
|
||||||
|
return data[0..readAsync].ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path)
|
||||||
|
=> ParentContentProvider.GetVolumeSizeInfo(path);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public class VirtualDiskContentProviderFactory : IVirtualDiskContentProviderFactory
|
||||||
|
{
|
||||||
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
|
|
||||||
|
public VirtualDiskContentProviderFactory(
|
||||||
|
ITimelessContentProvider timelessContentProvider,
|
||||||
|
IContentAccessorFactory contentAccessorFactory)
|
||||||
|
{
|
||||||
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IVirtualDiskContentProvider Create(IContentProvider parentContentProvider)
|
||||||
|
=> new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider, _contentAccessorFactory);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public class VirtualDiskContentReader : IContentReader
|
||||||
|
{
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly ICollection<IDisposable> _disposables;
|
||||||
|
public int PreferredBufferSize => 1024 * 1024;
|
||||||
|
public long? Position => _stream.Position;
|
||||||
|
|
||||||
|
public VirtualDiskContentReader(Stream stream, ICollection<IDisposable> disposables)
|
||||||
|
{
|
||||||
|
_stream = stream;
|
||||||
|
_disposables = disposables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]> ReadBytesAsync(int bufferSize, int? offset = null)
|
||||||
|
{
|
||||||
|
var data = new byte[bufferSize];
|
||||||
|
var read = await _stream.ReadAsync(data, offset ?? 0, bufferSize);
|
||||||
|
|
||||||
|
return data[..read].ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPosition(long position) => _stream.Seek(position, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
public Stream AsStream() => _stream;
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stream.Dispose();
|
||||||
|
foreach (var disposable in _disposables)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using DiscUtils.Udf;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
|
public class VirtualDiskContentReaderFactory : IContentReaderFactory<VirtualDiskContentProvider>
|
||||||
|
{
|
||||||
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
|
|
||||||
|
public VirtualDiskContentReaderFactory(IContentAccessorFactory contentAccessorFactory)
|
||||||
|
{
|
||||||
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IContentReader> CreateContentReaderAsync(IElement element)
|
||||||
|
{
|
||||||
|
if (element.Provider is not VirtualDiskContentProvider provider)
|
||||||
|
throw new ArgumentException(
|
||||||
|
"Provider must be VirtualDiskContentProvider, but it is " + element.Provider.GetType(),
|
||||||
|
nameof(element));
|
||||||
|
|
||||||
|
var elementNativePath = element.NativePath!;
|
||||||
|
|
||||||
|
var supportedPath = (await provider.ParentContentProvider.GetSupportedPathPart(elementNativePath))!;
|
||||||
|
|
||||||
|
var parentElement = (IElement) await provider.ParentContentProvider.GetItemByNativePathAsync(supportedPath, element.PointInTime);
|
||||||
|
|
||||||
|
|
||||||
|
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
|
||||||
|
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
|
||||||
|
|
||||||
|
var readerStream = reader.AsStream();
|
||||||
|
var discReader = new UdfReader(readerStream);
|
||||||
|
|
||||||
|
var subPath = elementNativePath.Path.Substring(supportedPath.Path.Length + 2 + Constants.SubContentProviderRootContainer.Length);
|
||||||
|
var fileInfo = discReader.GetFileInfo(subPath);
|
||||||
|
|
||||||
|
var contentReader = fileInfo.Open(FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
return new VirtualDiskContentReader(contentReader, new IDisposable[] {discReader, readerStream, contentReader});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using DiscUtils;
|
using DiscUtils;
|
||||||
using DiscUtils.Iso9660;
|
|
||||||
using DiscUtils.Udf;
|
using DiscUtils.Udf;
|
||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
using FileTime.Tools.VirtualDiskSources.Abstractions;
|
|
||||||
|
|
||||||
namespace FileTime.Tools.VirtualDiskSources;
|
namespace FileTime.Tools.VirtualDiskSources;
|
||||||
|
|
||||||
@@ -14,14 +12,17 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
{
|
{
|
||||||
private readonly IContentAccessorFactory _contentAccessorFactory;
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
private readonly IVirtualDiskContentProviderFactory _virtualDiskContentProviderFactory;
|
||||||
|
|
||||||
public VirtualDiskSubContentProvider(
|
public VirtualDiskSubContentProvider(
|
||||||
IContentAccessorFactory contentAccessorFactory,
|
IContentAccessorFactory contentAccessorFactory,
|
||||||
ITimelessContentProvider timelessContentProvider
|
ITimelessContentProvider timelessContentProvider,
|
||||||
|
IVirtualDiskContentProviderFactory virtualDiskContentProviderFactory
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_contentAccessorFactory = contentAccessorFactory;
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
_timelessContentProvider = timelessContentProvider;
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
_virtualDiskContentProviderFactory = virtualDiskContentProviderFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> CanHandleAsync(IElement parentElement)
|
public Task<bool> CanHandleAsync(IElement parentElement)
|
||||||
@@ -36,7 +37,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
{
|
{
|
||||||
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
|
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
|
||||||
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
|
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
|
||||||
|
|
||||||
await using var readerStream = reader.AsStream();
|
await using var readerStream = reader.AsStream();
|
||||||
var discReader = new UdfReader(readerStream);
|
var discReader = new UdfReader(readerStream);
|
||||||
|
|
||||||
@@ -48,15 +49,18 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
var rootFullName = new FullName(rootFullNameBase);
|
var rootFullName = new FullName(rootFullNameBase);
|
||||||
var rootNativePath = new NativePath(rootNativePathBase);
|
var rootNativePath = new NativePath(rootNativePathBase);
|
||||||
|
|
||||||
return CreateContainer(discReader.Root,
|
var container = CreateContainer(
|
||||||
|
discReader,
|
||||||
|
discReader.Root,
|
||||||
rootFullName,
|
rootFullName,
|
||||||
rootNativePath,
|
rootNativePath,
|
||||||
parentElement.Provider,
|
parentElement.Provider,
|
||||||
parentElement.Parent!,
|
parentElement.Parent!,
|
||||||
parentElement.PointInTime,
|
parentElement.PointInTime,
|
||||||
itemInitializationSettings);
|
itemInitializationSettings);
|
||||||
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolveNonRootChild(discReader, parentElement, itemPath, pointInTime, itemInitializationSettings);
|
return ResolveNonRootChild(discReader, parentElement, itemPath, pointInTime, itemInitializationSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
|
|
||||||
var childFullName = new FullName(childFullNameBase);
|
var childFullName = new FullName(childFullNameBase);
|
||||||
var childNativePath = new NativePath(childNativePathBase);
|
var childNativePath = new NativePath(childNativePathBase);
|
||||||
|
|
||||||
var parent = new AbsolutePath(_timelessContentProvider, pointInTime, childFullName.GetParent()!, AbsolutePathType.Container);
|
var parent = new AbsolutePath(_timelessContentProvider, pointInTime, childFullName.GetParent()!, AbsolutePathType.Container);
|
||||||
|
|
||||||
var container = discReader.Root;
|
var container = discReader.Root;
|
||||||
@@ -89,6 +93,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
if (container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childContainer)
|
if (container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childContainer)
|
||||||
{
|
{
|
||||||
return CreateContainer(
|
return CreateContainer(
|
||||||
|
discReader,
|
||||||
childContainer,
|
childContainer,
|
||||||
childFullName,
|
childFullName,
|
||||||
childNativePath,
|
childNativePath,
|
||||||
@@ -99,22 +104,26 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (container.GetFiles().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childElement)
|
if (container.GetFiles().FirstOrDefault(d => d.Name == pathParts[^1]) is not { } childElement)
|
||||||
{
|
{
|
||||||
return CreateElement(
|
return null;
|
||||||
childElement,
|
|
||||||
childFullName,
|
|
||||||
childNativePath,
|
|
||||||
parentElement.Provider,
|
|
||||||
parent,
|
|
||||||
pointInTime
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
var element = CreateElement(
|
||||||
|
childElement,
|
||||||
|
childFullName,
|
||||||
|
childNativePath,
|
||||||
|
parentElement.Provider,
|
||||||
|
parent,
|
||||||
|
pointInTime
|
||||||
|
);
|
||||||
|
|
||||||
|
discReader.Dispose();
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IContainer CreateContainer(
|
private IContainer CreateContainer(
|
||||||
|
UdfReader discReader,
|
||||||
DiscDirectoryInfo sourceContainer,
|
DiscDirectoryInfo sourceContainer,
|
||||||
FullName fullname,
|
FullName fullname,
|
||||||
NativePath nativePath,
|
NativePath nativePath,
|
||||||
@@ -138,7 +147,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
SupportsDelete.False,
|
SupportsDelete.False,
|
||||||
false,
|
false,
|
||||||
FormatAttributes(sourceContainer.Attributes),
|
FormatAttributes(sourceContainer.Attributes),
|
||||||
new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider),
|
_virtualDiskContentProviderFactory.Create(parentContentProvider),
|
||||||
false,
|
false,
|
||||||
pointInTime,
|
pointInTime,
|
||||||
exceptions,
|
exceptions,
|
||||||
@@ -148,7 +157,11 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
|
|
||||||
if (!initializationSettings.SkipChildInitialization)
|
if (!initializationSettings.SkipChildInitialization)
|
||||||
{
|
{
|
||||||
ThreadPool.QueueUserWorkItem(_ => LoadChildren(container, sourceContainer, children, pointInTime, exceptions));
|
ThreadPool.QueueUserWorkItem(_ =>
|
||||||
|
{
|
||||||
|
LoadChildren(container, sourceContainer, children, pointInTime, exceptions);
|
||||||
|
discReader.Dispose();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
@@ -204,7 +217,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
|||||||
false,
|
false,
|
||||||
FormatAttributes(childElement.Attributes),
|
FormatAttributes(childElement.Attributes),
|
||||||
childElement.Length,
|
childElement.Length,
|
||||||
new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider),
|
_virtualDiskContentProviderFactory.Create(parentContentProvider),
|
||||||
pointInTime,
|
pointInTime,
|
||||||
new ObservableCollection<Exception>(),
|
new ObservableCollection<Exception>(),
|
||||||
new ReadOnlyExtensionCollection(new ExtensionCollection())
|
new ReadOnlyExtensionCollection(new ExtensionCollection())
|
||||||
|
|||||||
Reference in New Issue
Block a user