2025-10-16 16:39:31 +01:00
|
|
|
// /***************************************************************************
|
|
|
|
|
// Aaru Data Preservation Suite
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
//
|
|
|
|
|
// Component : Text User Interface.
|
|
|
|
|
//
|
|
|
|
|
// --[ License ] --------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as
|
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Copyright © 2011-2025 Natalia Portillo
|
|
|
|
|
// ****************************************************************************/
|
|
|
|
|
|
2025-10-16 11:43:03 +01:00
|
|
|
using System.Collections.ObjectModel;
|
2025-10-16 12:55:52 +01:00
|
|
|
using System.Text;
|
2025-10-16 11:03:35 +01:00
|
|
|
using System.Windows.Input;
|
2025-10-16 11:43:03 +01:00
|
|
|
using Aaru.CommonTypes;
|
2025-10-16 11:03:35 +01:00
|
|
|
using Aaru.CommonTypes.Interfaces;
|
2025-10-16 11:43:03 +01:00
|
|
|
using Aaru.Tui.Models;
|
2025-11-18 04:47:22 +00:00
|
|
|
using Aaru.Tui.ViewModels.Dialogs;
|
|
|
|
|
using Aaru.Tui.Views.Dialogs;
|
2025-11-18 04:36:49 +00:00
|
|
|
using Aaru.Tui.Views.Windows;
|
2025-10-16 11:03:35 +01:00
|
|
|
using Avalonia;
|
2025-11-18 04:47:22 +00:00
|
|
|
using Avalonia.Controls;
|
2025-10-16 11:03:35 +01:00
|
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
2025-10-16 11:43:03 +01:00
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
2025-10-16 11:03:35 +01:00
|
|
|
using CommunityToolkit.Mvvm.Input;
|
2025-10-16 12:36:27 +01:00
|
|
|
using Humanizer;
|
2025-11-18 04:47:22 +00:00
|
|
|
using Prism.DryIoc;
|
2025-10-16 12:55:52 +01:00
|
|
|
using Partition = Aaru.CommonTypes.Partition;
|
2025-10-16 11:03:35 +01:00
|
|
|
|
|
|
|
|
namespace Aaru.Tui.ViewModels.Windows;
|
|
|
|
|
|
|
|
|
|
public sealed partial class ImageWindowViewModel : ViewModelBase
|
|
|
|
|
{
|
2025-11-18 04:36:49 +00:00
|
|
|
readonly IRegionManager _regionManager;
|
2025-10-16 11:43:03 +01:00
|
|
|
[ObservableProperty]
|
2025-10-16 12:06:45 +01:00
|
|
|
public string _filePath;
|
|
|
|
|
[ObservableProperty]
|
2025-10-16 12:55:52 +01:00
|
|
|
string _filesystemInformation;
|
2025-11-18 04:36:49 +00:00
|
|
|
IMediaImage _imageFormat;
|
2025-10-16 12:55:52 +01:00
|
|
|
[ObservableProperty]
|
|
|
|
|
bool _isFilesystemInformationVisible;
|
|
|
|
|
[ObservableProperty]
|
2025-10-16 12:36:27 +01:00
|
|
|
bool _isPartitionInformationVisible;
|
|
|
|
|
[ObservableProperty]
|
2025-10-16 11:43:03 +01:00
|
|
|
bool _isStatusVisible;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
ObservableCollection<FileSystemModelNode> _nodes;
|
|
|
|
|
[ObservableProperty]
|
2025-10-16 12:36:27 +01:00
|
|
|
string _partitionDescription;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionLength;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionName;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionOffset;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionScheme;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionSequence;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionSize;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionStart;
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
string _partitionType;
|
|
|
|
|
[ObservableProperty]
|
2025-10-16 11:43:03 +01:00
|
|
|
string? _status;
|
2025-10-16 11:03:35 +01:00
|
|
|
|
2025-11-18 04:36:49 +00:00
|
|
|
public ImageWindowViewModel(IRegionManager regionManager)
|
2025-10-16 11:03:35 +01:00
|
|
|
{
|
2025-11-18 04:36:49 +00:00
|
|
|
_regionManager = regionManager;
|
|
|
|
|
ExitCommand = new RelayCommand(Exit);
|
|
|
|
|
BackCommand = new RelayCommand(Back);
|
|
|
|
|
HelpCommand = new AsyncRelayCommand(HelpAsync);
|
2025-10-16 11:03:35 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:36:27 +01:00
|
|
|
public FileSystemModelNode? SelectedNode
|
|
|
|
|
{
|
2025-11-24 01:40:50 +00:00
|
|
|
get;
|
2025-10-16 12:36:27 +01:00
|
|
|
set
|
|
|
|
|
{
|
2025-11-24 01:40:50 +00:00
|
|
|
SetProperty(ref field, value);
|
2025-10-16 12:36:27 +01:00
|
|
|
|
2025-11-24 01:40:50 +00:00
|
|
|
if(field is null) return;
|
2025-10-16 12:36:27 +01:00
|
|
|
|
2025-11-24 01:40:50 +00:00
|
|
|
if(field.Partition is not null && field.Filesystem is null)
|
2025-10-16 12:36:27 +01:00
|
|
|
{
|
|
|
|
|
IsPartitionInformationVisible = true;
|
|
|
|
|
|
2025-11-24 01:40:50 +00:00
|
|
|
PartitionSequence = field.Partition.Value.Sequence.ToString();
|
|
|
|
|
PartitionName = field.Partition.Value.Name;
|
|
|
|
|
PartitionType = field.Partition.Value.Type;
|
|
|
|
|
PartitionStart = field.Partition.Value.Start.ToString();
|
|
|
|
|
PartitionOffset = ByteSize.FromBytes(field.Partition.Value.Offset).Humanize();
|
2025-10-18 13:36:08 +01:00
|
|
|
|
2025-11-24 01:40:50 +00:00
|
|
|
PartitionLength = string.Format(Localization.Resources._0_sectors, field.Partition.Value.Length);
|
2025-10-18 13:36:08 +01:00
|
|
|
|
2025-11-24 01:40:50 +00:00
|
|
|
PartitionSize = ByteSize.FromBytes(field.Partition.Value.Size).Humanize();
|
|
|
|
|
PartitionScheme = field.Partition.Value.Scheme;
|
|
|
|
|
PartitionDescription = field.Partition.Value.Description;
|
2025-10-16 12:36:27 +01:00
|
|
|
|
|
|
|
|
OnPropertyChanged(nameof(PartitionSequence));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionName));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionType));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionStart));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionOffset));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionLength));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionSize));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionScheme));
|
|
|
|
|
OnPropertyChanged(nameof(PartitionDescription));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
IsPartitionInformationVisible = false;
|
|
|
|
|
|
2025-11-24 01:40:50 +00:00
|
|
|
if(field.Filesystem is not null)
|
2025-10-16 12:55:52 +01:00
|
|
|
{
|
|
|
|
|
IsFilesystemInformationVisible = true;
|
2025-11-24 01:40:50 +00:00
|
|
|
FilesystemInformation = field.FilesystemInformation ?? "";
|
2025-10-16 12:55:52 +01:00
|
|
|
|
|
|
|
|
OnPropertyChanged(nameof(FilesystemInformation));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
IsFilesystemInformationVisible = false;
|
|
|
|
|
|
2025-10-16 12:36:27 +01:00
|
|
|
OnPropertyChanged(nameof(IsPartitionInformationVisible));
|
2025-10-16 12:55:52 +01:00
|
|
|
OnPropertyChanged(nameof(IsFilesystemInformationVisible));
|
2025-10-16 12:36:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-18 11:29:36 +01:00
|
|
|
public ICommand BackCommand { get; }
|
|
|
|
|
public ICommand HelpCommand { get; }
|
|
|
|
|
public ICommand ExitCommand { get; }
|
2025-10-16 11:03:35 +01:00
|
|
|
|
|
|
|
|
void Back()
|
|
|
|
|
{
|
2025-11-18 04:36:49 +00:00
|
|
|
IRegion? region = _regionManager.Regions["ContentRegion"];
|
|
|
|
|
IRegionNavigationService? navigationService = region.NavigationService;
|
|
|
|
|
|
|
|
|
|
if(navigationService?.Journal.CanGoBack == true)
|
|
|
|
|
navigationService.Journal.GoBack();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No history - navigate directly to FileView
|
|
|
|
|
_regionManager.RequestNavigate("ContentRegion", nameof(FileView));
|
|
|
|
|
}
|
2025-10-16 11:03:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Exit()
|
|
|
|
|
{
|
|
|
|
|
var lifetime = Application.Current!.ApplicationLifetime as IControlledApplicationLifetime;
|
|
|
|
|
lifetime!.Shutdown();
|
|
|
|
|
}
|
2025-10-16 11:43:03 +01:00
|
|
|
|
2025-11-18 04:36:49 +00:00
|
|
|
/// <inheritdoc />
|
|
|
|
|
public override void OnNavigatedTo(NavigationContext navigationContext)
|
2025-10-16 11:43:03 +01:00
|
|
|
{
|
2025-11-18 04:36:49 +00:00
|
|
|
_imageFormat = navigationContext.Parameters.GetValue<IMediaImage>("imageFormat");
|
|
|
|
|
FilePath = navigationContext.Parameters.GetValue<string>("filePath");
|
|
|
|
|
|
2025-10-16 11:43:03 +01:00
|
|
|
_ = Task.Run(Worker);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-18 04:47:22 +00:00
|
|
|
Task HelpAsync()
|
|
|
|
|
{
|
|
|
|
|
AvaloniaObject? view = (Application.Current as PrismApplication)?.MainWindow;
|
|
|
|
|
|
|
|
|
|
if(view is null) return Task.CompletedTask;
|
|
|
|
|
|
2025-10-16 21:20:20 +01:00
|
|
|
var dialog = new ImageHelpDialog
|
|
|
|
|
{
|
|
|
|
|
DataContext = new ImageHelpDialogViewModel(null!)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Set the dialog reference after creation
|
|
|
|
|
((ImageHelpDialogViewModel)dialog.DataContext!)._dialog = dialog;
|
|
|
|
|
|
2025-11-18 04:47:22 +00:00
|
|
|
return dialog.ShowDialog(view as Window);
|
|
|
|
|
}
|
2025-10-16 21:20:20 +01:00
|
|
|
|
2025-10-16 11:43:03 +01:00
|
|
|
void Worker()
|
|
|
|
|
{
|
|
|
|
|
IsStatusVisible = true;
|
2025-10-18 13:36:08 +01:00
|
|
|
Status = Localization.Resources.Loading_partitions;
|
2025-10-16 11:43:03 +01:00
|
|
|
|
2025-10-16 12:06:45 +01:00
|
|
|
Nodes = [];
|
2025-10-16 11:43:03 +01:00
|
|
|
|
|
|
|
|
List<Partition>? partitionsList = Core.Partitions.GetAll(_imageFormat);
|
|
|
|
|
|
|
|
|
|
if(partitionsList.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
partitionsList.Add(new Partition
|
|
|
|
|
{
|
2025-10-18 13:36:08 +01:00
|
|
|
Name = Aaru.Localization.Core.Whole_device,
|
2025-10-16 11:43:03 +01:00
|
|
|
Length = _imageFormat.Info.Sectors,
|
|
|
|
|
Size = _imageFormat.Info.Sectors * _imageFormat.Info.SectorSize
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sequence = 0;
|
|
|
|
|
|
2025-10-18 13:36:08 +01:00
|
|
|
Status = Localization.Resources.Loading_filesystems;
|
2025-10-16 11:57:23 +01:00
|
|
|
|
|
|
|
|
PluginRegister plugins = PluginRegister.Singleton;
|
|
|
|
|
|
2025-10-16 11:43:03 +01:00
|
|
|
foreach(Partition partition in partitionsList)
|
|
|
|
|
{
|
2025-10-18 13:36:08 +01:00
|
|
|
var node = new FileSystemModelNode(partition.Name ??
|
|
|
|
|
string.Format(Localization.Resources.Partition_0, sequence))
|
2025-10-16 11:43:03 +01:00
|
|
|
{
|
|
|
|
|
Partition = partition
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-16 11:57:23 +01:00
|
|
|
Core.Filesystems.Identify(_imageFormat, out List<string>? idPlugins, partition);
|
|
|
|
|
|
|
|
|
|
if(idPlugins.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var subNodes = new ObservableCollection<FileSystemModelNode>();
|
|
|
|
|
|
|
|
|
|
foreach(string pluginName in idPlugins)
|
|
|
|
|
{
|
|
|
|
|
if(!plugins.Filesystems.TryGetValue(pluginName, out IFilesystem? fs)) continue;
|
|
|
|
|
if(fs is null) continue;
|
|
|
|
|
|
2025-10-16 12:06:45 +01:00
|
|
|
var fsNode = new FileSystemModelNode(fs.Name)
|
|
|
|
|
{
|
2025-10-16 15:20:06 +01:00
|
|
|
Partition = partition,
|
2025-10-16 12:06:45 +01:00
|
|
|
Filesystem = fs
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-16 12:55:52 +01:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
fs.GetInformation(_imageFormat, partition, Encoding.ASCII, out string? information, out _);
|
|
|
|
|
|
|
|
|
|
fsNode.FilesystemInformation = information;
|
|
|
|
|
}
|
|
|
|
|
catch(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 11:57:23 +01:00
|
|
|
subNodes.Add(fsNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node.SubNodes = subNodes;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 11:43:03 +01:00
|
|
|
Nodes.Add(node);
|
|
|
|
|
sequence++;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-18 13:36:08 +01:00
|
|
|
Status = Localization.Resources.Done;
|
2025-10-16 11:43:03 +01:00
|
|
|
IsStatusVisible = false;
|
|
|
|
|
}
|
2025-10-16 11:03:35 +01:00
|
|
|
}
|