mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
[GUI] Added a window to edit AaruFormat files basic metadata.
This commit is contained in:
359
Aaru.Gui/ViewModels/Windows/ImageMetadataViewModel.cs
Normal file
359
Aaru.Gui/ViewModels/Windows/ImageMetadataViewModel.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
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(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(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(Localization.Core.Error_0, ex.Message);
|
||||
AaruLogging.Exception(ex, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user