Files
Aaru/Aaru.Gui/ViewModels/Windows/ImageMetadataViewModel.cs

359 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Core;
using Aaru.Gui.Views.Windows;
using Aaru.Images;
using Aaru.Localization;
using Aaru.Logging;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Humanizer;
using MsBox.Avalonia;
using MsBox.Avalonia.Base;
using MsBox.Avalonia.Enums;
using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo;
namespace Aaru.Gui.ViewModels.Windows;
public sealed partial class ImageMetadataViewModel : ViewModelBase
{
readonly ImageMetadata _view;
[ObservableProperty]
string _comments;
[ObservableProperty]
bool _commentsNotSet = true;
[ObservableProperty]
string _creator;
[ObservableProperty]
bool _creatorNotSet = true;
[ObservableProperty]
string _driveFirmwareRevision;
[ObservableProperty]
bool _driveFirmwareRevisionNotSet = true;
[ObservableProperty]
string _driveManufacturer;
[ObservableProperty]
bool _driveManufacturerNotSet = true;
[ObservableProperty]
string _driveModel;
[ObservableProperty]
bool _driveModelNotSet = true;
[ObservableProperty]
string _driveSerialNumber;
[ObservableProperty]
bool _driveSerialNumberNotSet = true;
AaruFormat _imageFormat;
[ObservableProperty]
string _imagePath;
IFilter _inputFilter;
[ObservableProperty]
bool _isOpened;
[ObservableProperty]
int _mediaLastSequence;
[ObservableProperty]
string _mediaManufacturer;
[ObservableProperty]
bool _mediaManufacturerNotSet = true;
[ObservableProperty]
string _mediaModel;
[ObservableProperty]
bool _mediaModelNotSet = true;
[ObservableProperty]
string _mediaPartNumber;
[ObservableProperty]
bool _mediaPartNumberNotSet = true;
[ObservableProperty]
int _mediaSequence;
[ObservableProperty]
string _mediaSerialNumber;
[ObservableProperty]
bool _mediaSerialNumberNotSet = true;
[ObservableProperty]
string _mediaTitle;
[ObservableProperty]
bool _mediaTitleNotSet = true;
[ObservableProperty]
string _mediaType;
[ObservableProperty]
bool _sequenceNotSet = true;
[ObservableProperty]
string _size;
public ImageMetadataViewModel(ImageMetadata view)
{
_view = view;
OpenImageCommand = new AsyncRelayCommand(OpenImageAsync);
LoadMetadataCommand = new RelayCommand(LoadMetadata);
SaveMetadataCommand = new AsyncRelayCommand(SaveMetadataAsync);
CloseCommand = new RelayCommand(Close);
}
public ICommand OpenImageCommand { get; }
public ICommand LoadMetadataCommand { get; }
public ICommand SaveMetadataCommand { get; }
public ICommand CloseCommand { get; }
static FilePickerFileType AaruFormatFiles { get; } = new(UI.AaruFormat_files)
{
Patterns = new AaruFormat().KnownExtensions.Select(static s => $"*{s}").ToList(),
MimeTypes = ["application/octet-stream"]
};
void Close()
{
CloseImage();
_view.Close();
}
public void CloseImage()
{
if(!IsOpened) return;
_imageFormat.Close();
IsOpened = false;
}
async Task SaveMetadataAsync()
{
if(!IsOpened) return;
var info = new ImageInfo
{
MediaSequence = SequenceNotSet ? 0 : MediaSequence,
LastMediaSequence = SequenceNotSet ? 0 : MediaLastSequence,
Creator = CreatorNotSet ? null : Creator,
Comments = CommentsNotSet ? null : Comments,
MediaTitle = MediaTitleNotSet ? null : MediaTitle,
MediaManufacturer = MediaManufacturerNotSet ? null : MediaManufacturer,
MediaModel = MediaModelNotSet ? null : MediaModel,
MediaSerialNumber = MediaSerialNumberNotSet ? null : MediaSerialNumber,
MediaPartNumber = MediaPartNumberNotSet ? null : MediaPartNumber,
DriveManufacturer = DriveManufacturerNotSet ? null : DriveManufacturer,
DriveModel = DriveModelNotSet ? null : DriveModel,
DriveSerialNumber = DriveSerialNumberNotSet ? null : DriveSerialNumber,
DriveFirmwareRevision = DriveFirmwareRevisionNotSet ? null : DriveFirmwareRevision
};
ulong sectors = _imageFormat.Info.Sectors;
MediaType mediaType = _imageFormat.Info.MediaType;
uint negativeSectors = _imageFormat.Info.NegativeSectors;
uint overflowSectors = _imageFormat.Info.OverflowSectors;
uint sectorSize = _imageFormat.Info.SectorSize;
// We close the read-only context and reopen it in resume mode
_imageFormat.Close();
bool ret = _imageFormat.Create(ImagePath, mediaType, [], sectors, negativeSectors, overflowSectors, sectorSize);
IMsBox<ButtonResult> msbox;
if(!ret)
{
AaruLogging.Error(UI.Error_reopening_image_for_writing);
AaruLogging.Error(_imageFormat.ErrorMessage);
msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.There_was_an_error_reopening_the_image_for_writing,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
_view.Close();
return;
}
// Now we set the metadata
_imageFormat.SetImageInfo(info);
// We close the image
_imageFormat.Close();
// And we re-open it in read-only mode
ErrorNumber errno = _imageFormat.Open(_inputFilter);
if(errno != ErrorNumber.NoError)
{
AaruLogging.Error(UI.Error_reopening_image_in_read_only_mode_after_writing_metadata);
AaruLogging.Error(Aaru.Localization.Core.Error_0, errno);
msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI
.There_was_an_error_reopening_the_image_in_read_only_mode_after_writing_metadata,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
_view.Close();
return;
}
msbox = MessageBoxManager.GetMessageBoxStandard(Aaru.Localization.Core.Success,
UI.Metadata_saved_successfully,
ButtonEnum.Ok,
Icon.Success);
await msbox.ShowAsync();
LoadMetadata();
}
void LoadMetadata()
{
if(!IsOpened) return;
MediaSequence = _imageFormat.Info.MediaSequence;
MediaLastSequence = _imageFormat.Info.LastMediaSequence;
Creator = _imageFormat.Info.Creator;
Comments = _imageFormat.Info.Comments;
MediaTitle = _imageFormat.Info.MediaTitle;
MediaManufacturer = _imageFormat.Info.MediaManufacturer;
MediaModel = _imageFormat.Info.MediaModel;
MediaSerialNumber = _imageFormat.Info.MediaSerialNumber;
MediaPartNumber = _imageFormat.Info.MediaPartNumber;
DriveManufacturer = _imageFormat.Info.DriveManufacturer;
DriveModel = _imageFormat.Info.DriveModel;
DriveSerialNumber = _imageFormat.Info.DriveSerialNumber;
DriveFirmwareRevision = _imageFormat.Info.DriveFirmwareRevision;
SequenceNotSet = MediaSequence == 0 || MediaLastSequence == 0;
CreatorNotSet = string.IsNullOrEmpty(Creator);
CommentsNotSet = string.IsNullOrEmpty(Comments);
MediaTitleNotSet = string.IsNullOrEmpty(MediaTitle);
MediaManufacturerNotSet = string.IsNullOrEmpty(MediaManufacturer);
MediaModelNotSet = string.IsNullOrEmpty(MediaModel);
MediaSerialNumberNotSet = string.IsNullOrEmpty(MediaSerialNumber);
MediaPartNumberNotSet = string.IsNullOrEmpty(MediaPartNumber);
DriveManufacturerNotSet = string.IsNullOrEmpty(DriveManufacturer);
DriveModelNotSet = string.IsNullOrEmpty(DriveModel);
DriveSerialNumberNotSet = string.IsNullOrEmpty(DriveSerialNumber);
DriveFirmwareRevisionNotSet = string.IsNullOrEmpty(DriveFirmwareRevision);
}
async Task OpenImageAsync()
{
IReadOnlyList<IStorageFile> result = await _view.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = UI.Dialog_Choose_image_to_open,
AllowMultiple = false,
FileTypeFilter = [AaruFormatFiles]
});
// Exit if user did not select exactly one file
if(result.Count != 1) return;
// Get the appropriate filter plugin for the selected file
IFilter inputFilter = PluginRegister.Singleton.GetFilter(result[0].Path.LocalPath);
// Show error if no suitable filter plugin is found
if(inputFilter == null)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.Cannot_open_specified_file,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
return;
}
try
{
// Detect the image format of the selected file
if(ImageFormat.Detect(inputFilter) is not AaruFormat imageFormat)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.File_is_not_an_AaruFormat_image,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
return;
}
try
{
// Open the image file
ErrorNumber opened = imageFormat.Open(inputFilter);
if(opened != ErrorNumber.NoError)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
string.Format(UI.Error_0_opening_image_format, opened),
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
AaruLogging.Error(UI.Unable_to_open_image_format);
AaruLogging.Error(UI.No_error_given);
return;
}
if(imageFormat.Info.Version.StartsWith("1.", StringComparison.OrdinalIgnoreCase))
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Warning,
UI.AaruFormat_images_version_1_x_are_read_only,
ButtonEnum.Ok,
Icon.Warning);
await msbox.ShowAsync();
return;
}
ImagePath = $"[lime]{result[0].Path.LocalPath}[/]";
MediaType = $"[orange]{imageFormat.Info.MediaType.Humanize()}[/]";
Size =
$"[teal]{ByteSize.FromBytes(imageFormat.Info.Sectors * imageFormat.Info.SectorSize).Humanize()}[/]";
_inputFilter = inputFilter;
_imageFormat = imageFormat;
IsOpened = true;
LoadMetadata();
}
catch(Exception ex)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.Unable_to_open_image_format,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
AaruLogging.Error(UI.Unable_to_open_image_format);
AaruLogging.Error(Aaru.Localization.Core.Error_0, ex.Message);
AaruLogging.Exception(ex, Aaru.Localization.Core.Error_0, ex.Message);
}
}
catch(Exception ex)
{
IMsBox<ButtonResult> msbox = MessageBoxManager.GetMessageBoxStandard(UI.Title_Error,
UI.Exception_reading_file,
ButtonEnum.Ok,
Icon.Error);
await msbox.ShowAsync();
AaruLogging.Error(string.Format(UI.Error_reading_file_0, ex.Message));
AaruLogging.Exception(ex, UI.Error_reading_file_0, ex.Message);
}
}
}