diff --git a/.idea/.idea.Aaru/.idea/contentModel.xml b/.idea/.idea.Aaru/.idea/contentModel.xml
index b038b7f44..da89e13c4 100644
--- a/.idea/.idea.Aaru/.idea/contentModel.xml
+++ b/.idea/.idea.Aaru/.idea/contentModel.xml
@@ -1,27 +1,28 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
@@ -70,6 +71,13 @@
+
+
+
+
+
+
+
@@ -80,18 +88,12 @@
-
-
-
-
-
-
@@ -100,13 +102,6 @@
-
-
-
-
-
-
-
@@ -114,12 +109,19 @@
+
+
+
+
+
+
+
+
-
@@ -178,13 +180,6 @@
-
-
-
-
-
-
-
@@ -205,10 +200,19 @@
+
+
+
+
+
+
+
+
+
@@ -217,7 +221,6 @@
-
@@ -235,7 +238,6 @@
-
@@ -256,8 +258,8 @@
-
+
@@ -267,11 +269,11 @@
-
+
-
+
@@ -285,17 +287,17 @@
-
-
+
+
-
+
@@ -315,19 +317,12 @@
-
+
-
-
-
-
-
-
-
@@ -344,61 +339,68 @@
+
+
+
+
+
+
+
+
-
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
@@ -423,6 +425,7 @@
+
@@ -434,12 +437,11 @@
-
-
+
@@ -461,9 +463,9 @@
-
+
@@ -493,13 +495,6 @@
-
-
-
-
-
-
-
@@ -563,11 +558,11 @@
-
+
@@ -587,10 +582,17 @@
+
+
+
+
+
+
+
+
-
@@ -647,13 +649,6 @@
-
-
-
-
-
-
-
@@ -666,17 +661,28 @@
-
+
+
+
+
+
+
+
+
+
-
+
+
+
+
@@ -684,16 +690,13 @@
-
-
-
+
+
-
-
@@ -729,13 +732,11 @@
-
-
-
+
@@ -743,14 +744,11 @@
+
-
-
-
-
@@ -790,8 +788,8 @@
-
+
@@ -800,8 +798,8 @@
-
+
@@ -810,8 +808,8 @@
-
+
@@ -838,15 +836,8 @@
-
-
-
-
-
-
-
-
+
@@ -864,11 +855,11 @@
+
-
@@ -891,17 +882,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
@@ -909,18 +917,12 @@
-
-
-
-
-
-
@@ -1014,12 +1016,6 @@
-
-
-
-
-
-
@@ -1044,6 +1040,12 @@
+
+
+
+
+
+
@@ -1126,8 +1128,6 @@
-
-
@@ -1143,18 +1143,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1167,6 +1155,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1175,14 +1175,14 @@
+
+
-
-
@@ -1205,10 +1205,12 @@
+
+
+
-
@@ -1241,26 +1243,19 @@
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
@@ -1329,6 +1324,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -1337,7 +1343,6 @@
-
@@ -1345,6 +1350,10 @@
+
+
+
+
@@ -1352,9 +1361,6 @@
-
-
-
@@ -1437,7 +1443,16 @@
-
+
+
+
+
+
+
+
+
+
+
@@ -1459,16 +1474,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1502,6 +1507,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1539,16 +1554,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1639,8 +1644,8 @@
-
+
@@ -1674,17 +1679,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -1695,12 +1689,16 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -1884,16 +1882,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1904,6 +1892,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -1922,6 +1920,14 @@
+
+
+
+
+
+
+
+
@@ -1929,7 +1935,6 @@
-
@@ -1937,13 +1942,6 @@
-
-
-
-
-
-
-
@@ -1953,11 +1951,20 @@
-
+
+
+
+
+
+
+
+
+
+
@@ -1966,13 +1973,19 @@
-
-
-
+
-
+
+
+
+
+
+
+
+
+
@@ -2006,8 +2019,8 @@
-
+
@@ -2015,7 +2028,7 @@
-
+
@@ -2038,14 +2051,7 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Aaru.Gui/Models/FileSystemModel.cs b/Aaru.Gui/Models/FileSystemModel.cs
new file mode 100644
index 000000000..a1652bc28
--- /dev/null
+++ b/Aaru.Gui/Models/FileSystemModel.cs
@@ -0,0 +1,11 @@
+using Aaru.CommonTypes.Interfaces;
+
+namespace Aaru.Gui.Models
+{
+ public class FileSystemModel : RootModel
+ {
+ public string VolumeName { get; set; }
+ public IFilesystem Filesystem { get; set; }
+ public IReadOnlyFilesystem ReadOnlyFilesystem { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Gui/Models/ImageModel.cs b/Aaru.Gui/Models/ImageModel.cs
index ab5208dad..a78cf0bfe 100644
--- a/Aaru.Gui/Models/ImageModel.cs
+++ b/Aaru.Gui/Models/ImageModel.cs
@@ -1,4 +1,17 @@
+using System.Collections.ObjectModel;
+using Aaru.CommonTypes.Interfaces;
+using Avalonia.Media.Imaging;
+
namespace Aaru.Gui.Models
{
- public class ImageModel {}
+ public class ImageModel
+ {
+ public ImageModel() => PartitionSchemesOrFileSystems = new ObservableCollection();
+
+ public string Path { get; set; }
+ public string FileName { get; set; }
+ public Bitmap Icon { get; set; }
+ public ObservableCollection PartitionSchemesOrFileSystems { get; }
+ public IMediaImage Image { get; set; }
+ }
}
\ No newline at end of file
diff --git a/Aaru.Gui/Models/PartitionModel.cs b/Aaru.Gui/Models/PartitionModel.cs
new file mode 100644
index 000000000..0b6e6feb7
--- /dev/null
+++ b/Aaru.Gui/Models/PartitionModel.cs
@@ -0,0 +1,16 @@
+using System.Collections.ObjectModel;
+using Aaru.CommonTypes;
+using Avalonia.Media.Imaging;
+
+namespace Aaru.Gui.Models
+{
+ public class PartitionModel
+ {
+ public PartitionModel() => FileSystems = new ObservableCollection();
+
+ public string Name { get; set; }
+ public Bitmap Icon { get; set; }
+ public ObservableCollection FileSystems { get; }
+ public Partition Partition { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Gui/Models/PartitionSchemeModel.cs b/Aaru.Gui/Models/PartitionSchemeModel.cs
new file mode 100644
index 000000000..eeed77b3d
--- /dev/null
+++ b/Aaru.Gui/Models/PartitionSchemeModel.cs
@@ -0,0 +1,13 @@
+using System.Collections.ObjectModel;
+using Avalonia.Media.Imaging;
+
+namespace Aaru.Gui.Models
+{
+ public class PartitionSchemeModel : RootModel
+ {
+ public PartitionSchemeModel() => Partitions = new ObservableCollection();
+
+ public Bitmap Icon { get; set; }
+ public ObservableCollection Partitions { get; }
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Gui/ViewModels/MainWindowViewModel.cs b/Aaru.Gui/ViewModels/MainWindowViewModel.cs
index 9e7573131..86f9221c7 100644
--- a/Aaru.Gui/ViewModels/MainWindowViewModel.cs
+++ b/Aaru.Gui/ViewModels/MainWindowViewModel.cs
@@ -1,21 +1,39 @@
-using System.Collections.ObjectModel;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
using System.Linq;
using System.Reactive;
+using Aaru.CommonTypes;
+using Aaru.CommonTypes.Enums;
+using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Interop;
+using Aaru.CommonTypes.Structs;
+using Aaru.Console;
+using Aaru.Core;
using Aaru.Database;
using Aaru.Gui.Models;
using Aaru.Gui.Views;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
using MessageBox.Avalonia;
+using MessageBox.Avalonia.Enums;
using ReactiveUI;
+using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
namespace Aaru.Gui.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
+ readonly IAssetLoader _assets;
readonly DevicesRootModel _devicesRoot;
+ readonly Bitmap _genericFolderIcon;
+ readonly Bitmap _genericHddIcon;
+ readonly Bitmap _genericOpticalIcon;
+ readonly Bitmap _genericTapeIcon;
readonly ImagesRootModel _imagesRoot;
readonly MainWindow _view;
ConsoleWindow _consoleWindow;
@@ -30,8 +48,10 @@ namespace Aaru.Gui.ViewModels
ExitCommand = ReactiveCommand.Create(ExecuteExitCommand);
SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand);
ConsoleCommand = ReactiveCommand.Create(ExecuteConsoleCommand);
+ OpenCommand = ReactiveCommand.Create(ExecuteOpenCommand);
_view = view;
TreeRoot = new ObservableCollection();
+ _assets = AvaloniaLocator.Current.GetService();
_imagesRoot = new ImagesRootModel
{
@@ -55,6 +75,18 @@ namespace Aaru.Gui.ViewModels
break;
}
+
+ _genericHddIcon =
+ new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png")));
+
+ _genericOpticalIcon =
+ new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png")));
+
+ _genericTapeIcon =
+ new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-tape.png")));
+
+ _genericFolderIcon =
+ new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png")));
}
public bool DevicesSupported
@@ -76,6 +108,7 @@ namespace Aaru.Gui.ViewModels
public ReactiveCommand StatisticsCommand { get; }
public ReactiveCommand ExitCommand { get; }
public ReactiveCommand SettingsCommand { get; }
+ public ReactiveCommand OpenCommand { get; }
internal void ExecuteAboutCommand()
{
@@ -140,5 +173,239 @@ namespace Aaru.Gui.ViewModels
_consoleWindow.Show();
}
+
+ async void ExecuteOpenCommand()
+ {
+ // TODO: Extensions
+ var dlgOpenImage = new OpenFileDialog
+ {
+ Title = "Choose image to open", AllowMultiple = false
+ };
+
+ string[] result = await dlgOpenImage.ShowAsync(_view);
+
+ if(result?.Length != 1)
+ return;
+
+ var filtersList = new FiltersList();
+ IFilter inputFilter = filtersList.GetFilter(result[0]);
+
+ if(inputFilter == null)
+ {
+ MessageBoxManager.GetMessageBoxStandardWindow("Error", "Cannot open specified file.", ButtonEnum.Ok,
+ Icon.Error);
+
+ return;
+ }
+
+ try
+ {
+ IMediaImage imageFormat = ImageFormat.Detect(inputFilter);
+
+ if(imageFormat == null)
+ {
+ MessageBoxManager.GetMessageBoxStandardWindow("Error", "Image format not identified.",
+ ButtonEnum.Ok, Icon.Error);
+
+ return;
+ }
+
+ AaruConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id);
+
+ try
+ {
+ if(!imageFormat.Open(inputFilter))
+ {
+ MessageBoxManager.GetMessageBoxStandardWindow("Error", "Unable to open image format.",
+ ButtonEnum.Ok, Icon.Error);
+
+ AaruConsole.ErrorWriteLine("Unable to open image format");
+ AaruConsole.ErrorWriteLine("No error given");
+
+ return;
+ }
+
+ var mediaResource =
+ new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png");
+
+ var imageModel = new ImageModel
+ {
+ Path = result[0], Icon = _assets.Exists(mediaResource)
+ ? new Bitmap(_assets.Open(mediaResource))
+ : imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia
+ ? _genericHddIcon
+ : imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc
+ ? _genericOpticalIcon
+ : _genericFolderIcon,
+ FileName = Path.GetFileName(result[0]), Image = imageFormat
+ };
+
+ // TODO: pnlImageInfo
+
+ List partitions = Core.Partitions.GetAll(imageFormat);
+ Core.Partitions.AddSchemesToStats(partitions);
+
+ bool checkraw = false;
+ List idPlugins;
+ IFilesystem plugin;
+ PluginBase plugins = GetPluginBase.Instance;
+
+ if(partitions.Count == 0)
+ {
+ AaruConsole.DebugWriteLine("Analyze command", "No partitions found");
+
+ checkraw = true;
+ }
+ else
+ {
+ AaruConsole.WriteLine("{0} partitions found.", partitions.Count);
+
+ foreach(string scheme in partitions.Select(p => p.Scheme).Distinct().OrderBy(s => s))
+ {
+ // TODO: Add icons to partition schemes
+ var schemeModel = new PartitionSchemeModel
+ {
+ Name = scheme
+ };
+
+ foreach(Partition partition in partitions.
+ Where(p => p.Scheme == scheme).OrderBy(p => p.Start))
+ {
+ // TODO: pnlPartition
+ var partitionModel = new PartitionModel
+ {
+ // TODO: Add icons to partition types
+ Name = $"{partition.Name} ({partition.Type})", Partition = partition
+ };
+
+ AaruConsole.WriteLine("Identifying filesystem on partition");
+
+ Core.Filesystems.Identify(imageFormat, out idPlugins, partition);
+
+ if(idPlugins.Count == 0)
+ AaruConsole.WriteLine("Filesystem not identified");
+ else
+ {
+ AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");
+
+ foreach(string pluginName in idPlugins)
+ if(plugins.PluginsList.TryGetValue(pluginName, out plugin))
+ {
+ plugin.GetInformation(imageFormat, partition, out string information, null);
+
+ var fsPlugin = plugin as IReadOnlyFilesystem;
+
+ if(fsPlugin != null)
+ {
+ Errno error =
+ fsPlugin.Mount(imageFormat, partition, null,
+ new Dictionary(), null);
+
+ if(error != Errno.NoError)
+ fsPlugin = null;
+ }
+
+ var filesystemModel = new FileSystemModel
+ {
+ VolumeName =
+ plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}"
+ : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
+ Filesystem = plugin, ReadOnlyFilesystem = fsPlugin
+ };
+
+ /* TODO: Trap expanding item
+ if(fsPlugin != null)
+ Statistics.AddCommand("ls");
+ */
+
+ Statistics.AddFilesystem(plugin.XmlFsType.Type);
+ partitionModel.FileSystems.Add(filesystemModel);
+ }
+ }
+
+ schemeModel.Partitions.Add(partitionModel);
+ }
+
+ imageModel.PartitionSchemesOrFileSystems.Add(schemeModel);
+ }
+ }
+
+ if(checkraw)
+ {
+ var wholePart = new Partition
+ {
+ Name = "Whole device", Length = imageFormat.Info.Sectors,
+ Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize
+ };
+
+ Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart);
+
+ if(idPlugins.Count == 0)
+ AaruConsole.WriteLine("Filesystem not identified");
+ else
+ {
+ AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins");
+
+ foreach(string pluginName in idPlugins)
+ if(plugins.PluginsList.TryGetValue(pluginName, out plugin))
+ {
+ plugin.GetInformation(imageFormat, wholePart, out string information, null);
+
+ var fsPlugin = plugin as IReadOnlyFilesystem;
+
+ if(fsPlugin != null)
+ {
+ Errno error = fsPlugin.Mount(imageFormat, wholePart, null,
+ new Dictionary(), null);
+
+ if(error != Errno.NoError)
+ fsPlugin = null;
+ }
+
+ var filesystemModel = new FileSystemModel
+ {
+ VolumeName = plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}"
+ : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})",
+ Filesystem = plugin, ReadOnlyFilesystem = fsPlugin
+ };
+
+ /* TODO: Trap expanding item
+ if(fsPlugin != null)
+ Statistics.AddCommand("ls");
+ */
+
+ Statistics.AddFilesystem(plugin.XmlFsType.Type);
+ imageModel.PartitionSchemesOrFileSystems.Add(filesystemModel);
+ }
+ }
+ }
+
+ Statistics.AddMediaFormat(imageFormat.Format);
+ Statistics.AddMedia(imageFormat.Info.MediaType, false);
+ Statistics.AddFilter(inputFilter.Name);
+
+ _imagesRoot.Images.Add(imageModel);
+ }
+ catch(Exception ex)
+ {
+ MessageBoxManager.GetMessageBoxStandardWindow("Error", "Unable to open image format.",
+ ButtonEnum.Ok, Icon.Error);
+
+ AaruConsole.ErrorWriteLine("Unable to open image format");
+ AaruConsole.ErrorWriteLine("Error: {0}", ex.Message);
+ AaruConsole.DebugWriteLine("Image-info command", "Stack trace: {0}", ex.StackTrace);
+ }
+ }
+ catch(Exception ex)
+ {
+ MessageBoxManager.GetMessageBoxStandardWindow("Error", "Exception reading file.", ButtonEnum.Ok,
+ Icon.Error);
+
+ AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}");
+ AaruConsole.DebugWriteLine("Image-info command", ex.StackTrace);
+ }
+
+ Statistics.AddCommand("image-info");
+ }
}
}
\ No newline at end of file
diff --git a/Aaru.Gui/Views/MainWindow.xaml b/Aaru.Gui/Views/MainWindow.xaml
index 1f94fa78c..1fa34b5f2 100644
--- a/Aaru.Gui/Views/MainWindow.xaml
+++ b/Aaru.Gui/Views/MainWindow.xaml
@@ -10,7 +10,7 @@