Browse ISOs
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
using FileTime.App.Core.Services;
|
||||
|
||||
namespace FileTime.Tools.VirtualDiskSources;
|
||||
|
||||
public class DiscUtilsInitializer : IPreStartupHandler
|
||||
{
|
||||
public Task InitAsync()
|
||||
{
|
||||
DiscUtils.Containers.SetupHelper.SetupContainers();
|
||||
DiscUtils.FileSystems.SetupHelper.SetupFileSystems();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Models\FileTime.Core.Models.csproj" />
|
||||
<ProjectReference Include="..\FileTime.Tools.VirtualDiskSources.Abstractions\FileTime.Tools.VirtualDiskSources.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LTRData.DiscUtils.Containers" Version="1.0.28" />
|
||||
<PackageReference Include="LTRData.DiscUtils.FileSystems" Version="1.0.28" />
|
||||
<PackageReference Include="LTRData.DiscUtils.OpticalDisk" Version="1.0.28" />
|
||||
<PackageReference Include="LTRData.DiscUtils.VirtualFileSystem" Version="1.0.28" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
18
src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs
Normal file
18
src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Tools.VirtualDiskSources.Abstractions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace FileTime.Tools.VirtualDiskSources;
|
||||
|
||||
public static class Startup
|
||||
{
|
||||
public static IServiceCollection AddVirtualDisk(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton<IVirtualDiskSubContentProvider, VirtualDiskSubContentProvider>();
|
||||
services.AddSingleton<ISubContentProvider>(sp => sp.GetRequiredService<IVirtualDiskSubContentProvider>());
|
||||
services.AddSingleton<IPreStartupHandler, DiscUtilsInitializer>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.Tools.VirtualDiskSources;
|
||||
|
||||
public class VirtualDiskContentProvider : SubContentProviderBase
|
||||
{
|
||||
public VirtualDiskContentProvider(
|
||||
IContentProvider parentContentProvider,
|
||||
ITimelessContentProvider timelessContentProvider)
|
||||
: base(parentContentProvider, "virtual-disk", timelessContentProvider)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using DiscUtils;
|
||||
using DiscUtils.Iso9660;
|
||||
using DiscUtils.Udf;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
using FileTime.Tools.VirtualDiskSources.Abstractions;
|
||||
|
||||
namespace FileTime.Tools.VirtualDiskSources;
|
||||
|
||||
public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
|
||||
{
|
||||
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
|
||||
public VirtualDiskSubContentProvider(
|
||||
IContentAccessorFactory contentAccessorFactory,
|
||||
ITimelessContentProvider timelessContentProvider
|
||||
)
|
||||
{
|
||||
_contentAccessorFactory = contentAccessorFactory;
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
}
|
||||
|
||||
public Task<bool> CanHandleAsync(IElement parentElement)
|
||||
=> Task.FromResult(parentElement.NativePath?.Path.EndsWith(".iso", StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
|
||||
public async Task<IItem?> GetItemByFullNameAsync(
|
||||
IElement parentElement,
|
||||
FullName itemPath,
|
||||
PointInTime pointInTime,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = default)
|
||||
{
|
||||
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
|
||||
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
|
||||
|
||||
await using var readerStream = reader.AsStream();
|
||||
var discReader = new UdfReader(readerStream);
|
||||
|
||||
if (itemPath.Path.Length == 0 || itemPath.Path == Constants.SubContentProviderRootContainer)
|
||||
{
|
||||
var rootFullNameBase = parentElement.FullName!.Path + Constants.SeparatorChar + Constants.SubContentProviderRootContainer;
|
||||
var rootNativePathBase = parentElement.NativePath!.Path + Constants.SeparatorChar + Constants.SubContentProviderRootContainer;
|
||||
|
||||
var rootFullName = new FullName(rootFullNameBase);
|
||||
var rootNativePath = new NativePath(rootNativePathBase);
|
||||
|
||||
return CreateContainer(discReader.Root,
|
||||
rootFullName,
|
||||
rootNativePath,
|
||||
parentElement.Provider,
|
||||
parentElement.Parent!,
|
||||
parentElement.PointInTime,
|
||||
itemInitializationSettings);
|
||||
}
|
||||
|
||||
return ResolveNonRootChild(discReader, parentElement, itemPath, pointInTime, itemInitializationSettings);
|
||||
}
|
||||
|
||||
private IItem? ResolveNonRootChild(
|
||||
UdfReader discReader,
|
||||
IElement parentElement,
|
||||
FullName itemPath,
|
||||
PointInTime pointInTime,
|
||||
ItemInitializationSettings itemInitializationSettings = default)
|
||||
{
|
||||
var pathParts = itemPath.Path.Split(Constants.SeparatorChar);
|
||||
|
||||
var childFullNameBase = parentElement.FullName!.Path + Constants.SeparatorChar + itemPath.Path;
|
||||
var childNativePathBase = parentElement.NativePath!.Path + Constants.SeparatorChar + itemPath.Path;
|
||||
|
||||
var childFullName = new FullName(childFullNameBase);
|
||||
var childNativePath = new NativePath(childNativePathBase);
|
||||
|
||||
var parent = new AbsolutePath(_timelessContentProvider, pointInTime, childFullName.GetParent()!, AbsolutePathType.Container);
|
||||
|
||||
var container = discReader.Root;
|
||||
for (var i = 1; i < pathParts.Length - 1; i++)
|
||||
{
|
||||
if (container is null) break;
|
||||
container = container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[i]);
|
||||
}
|
||||
|
||||
if (container is null) return null;
|
||||
|
||||
if (container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childContainer)
|
||||
{
|
||||
return CreateContainer(
|
||||
childContainer,
|
||||
childFullName,
|
||||
childNativePath,
|
||||
parentElement.Provider,
|
||||
parent,
|
||||
pointInTime,
|
||||
itemInitializationSettings
|
||||
);
|
||||
}
|
||||
|
||||
if (container.GetFiles().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childElement)
|
||||
{
|
||||
return CreateElement(
|
||||
childElement,
|
||||
childFullName,
|
||||
childNativePath,
|
||||
parentElement.Provider,
|
||||
parent,
|
||||
pointInTime
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IContainer CreateContainer(
|
||||
DiscDirectoryInfo sourceContainer,
|
||||
FullName fullname,
|
||||
NativePath nativePath,
|
||||
IContentProvider parentContentProvider,
|
||||
AbsolutePath parent,
|
||||
PointInTime pointInTime,
|
||||
ItemInitializationSettings initializationSettings)
|
||||
{
|
||||
var children = new ObservableCollection<AbsolutePath>();
|
||||
var exceptions = new ObservableCollection<Exception>();
|
||||
var container = new Container(
|
||||
sourceContainer.Name,
|
||||
sourceContainer.Name,
|
||||
fullname,
|
||||
nativePath,
|
||||
parent,
|
||||
true,
|
||||
true,
|
||||
sourceContainer.CreationTime,
|
||||
sourceContainer.LastWriteTime,
|
||||
SupportsDelete.False,
|
||||
false,
|
||||
FormatAttributes(sourceContainer.Attributes),
|
||||
new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider),
|
||||
false,
|
||||
pointInTime,
|
||||
exceptions,
|
||||
new ReadOnlyExtensionCollection(new ExtensionCollection()),
|
||||
children
|
||||
);
|
||||
|
||||
if (!initializationSettings.SkipChildInitialization)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(_ => LoadChildren(container, sourceContainer, children, pointInTime, exceptions));
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private void LoadChildren(
|
||||
Container container,
|
||||
DiscDirectoryInfo sourceContainer,
|
||||
ObservableCollection<AbsolutePath> children,
|
||||
PointInTime pointInTime,
|
||||
ObservableCollection<Exception> exceptions
|
||||
)
|
||||
{
|
||||
foreach (var discDirectoryInfo in sourceContainer.GetDirectories())
|
||||
{
|
||||
children.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
pointInTime,
|
||||
container.FullName.GetChild(discDirectoryInfo.Name),
|
||||
AbsolutePathType.Container)
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var fileInfo in sourceContainer.GetFiles())
|
||||
{
|
||||
children.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
pointInTime,
|
||||
container.FullName.GetChild(fileInfo.Name),
|
||||
AbsolutePathType.Element)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private IElement CreateElement(DiscFileInfo childElement,
|
||||
FullName fullname,
|
||||
NativePath nativePath,
|
||||
IContentProvider parentContentProvider,
|
||||
AbsolutePath parent,
|
||||
PointInTime pointInTime)
|
||||
{
|
||||
var element = new Element(
|
||||
childElement.Name,
|
||||
childElement.Name,
|
||||
fullname,
|
||||
nativePath,
|
||||
parent,
|
||||
true,
|
||||
true,
|
||||
childElement.CreationTime,
|
||||
childElement.LastWriteTime,
|
||||
SupportsDelete.False,
|
||||
false,
|
||||
FormatAttributes(childElement.Attributes),
|
||||
childElement.Length,
|
||||
new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider),
|
||||
pointInTime,
|
||||
new ObservableCollection<Exception>(),
|
||||
new ReadOnlyExtensionCollection(new ExtensionCollection())
|
||||
);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private string FormatAttributes(FileAttributes attributes)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user