using System.Collections.ObjectModel; using System.Reflection; using System.Text; using System.Windows.Input; using Aaru.CommonTypes; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Core; using Aaru.Helpers; using Aaru.Tui.Models; using Aaru.Tui.ViewModels.Dialogs; using Aaru.Tui.Views.Dialogs; using Aaru.Tui.Views.Windows; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Humanizer; using Prism.DryIoc; using Spectre.Console; using Color = Avalonia.Media.Color; namespace Aaru.Tui.ViewModels.Windows; public sealed partial class FileViewViewModel : ViewModelBase { readonly IRegionManager _regionManager; [ObservableProperty] string _copyright; [ObservableProperty] string _currentPath; [ObservableProperty] ObservableCollection _files = []; [ObservableProperty] string _informationalVersion; [ObservableProperty] bool _isStatusVisible; [ObservableProperty] string _status; public FileViewViewModel(IRegionManager regionManager) { ExitCommand = new RelayCommand(Exit); SectorViewCommand = new RelayCommand(SectorView); GoToPathCommand = new AsyncRelayCommand(GoToPathAsync); HelpCommand = new AsyncRelayCommand(HelpAsync); OpenSelectedFileCommand = new RelayCommand(OpenSelectedFile, CanOpenSelectedFile); _regionManager = regionManager; InformationalVersion = Assembly.GetExecutingAssembly() .GetCustomAttribute() ?.InformationalVersion ?? "?.?.?"; Copyright = Assembly.GetExecutingAssembly().GetCustomAttribute()?.Copyright ?? ""; Status = Localization.Resources.Loading; } public FileModel? SelectedFile { get; set { SetProperty(ref field, value); OnPropertyChanged(nameof(IsFileInfoAvailable)); OnPropertyChanged(nameof(SelectedFileIsNotDirectory)); OnPropertyChanged(nameof(SelectedFileLength)); OnPropertyChanged(nameof(SelectedFileCreationTime)); OnPropertyChanged(nameof(SelectedFileLastWriteTime)); OnPropertyChanged(nameof(SelectedFileAttributes)); OnPropertyChanged(nameof(SelectedFileUnixMode)); OnPropertyChanged(nameof(SelectedFileHasInformation)); OnPropertyChanged(nameof(SelectedFileInformation)); } } public ICommand OpenSelectedFileCommand { get; } public ICommand ExitCommand { get; } public ICommand SectorViewCommand { get; } public ICommand GoToPathCommand { get; } public ICommand HelpCommand { get; } public bool IsFileInfoAvailable => SelectedFile?.FileInfo != null; public bool SelectedFileIsNotDirectory => SelectedFile?.IsDirectory == false; public long? SelectedFileLength => SelectedFile?.IsDirectory == false ? SelectedFile?.FileInfo?.Length : 0; public DateTime? SelectedFileCreationTime => SelectedFile?.FileInfo?.CreationTime; public DateTime? SelectedFileLastWriteTime => SelectedFile?.FileInfo?.LastWriteTime; public string? SelectedFileAttributes => SelectedFile?.FileInfo?.Attributes.ToString(); public string? SelectedFileUnixMode => SelectedFile?.FileInfo?.UnixFileMode.ToString(); public bool SelectedFileHasInformation => SelectedFile?.Information != null; public string? SelectedFileInformation => SelectedFile?.Information; Task HelpAsync() { AvaloniaObject? view = (Application.Current as PrismApplication)?.MainWindow; if(view is null) return Task.CompletedTask; var dialog = new MainHelpDialog { DataContext = new MainHelpDialogViewModel(null!) }; // Set the dialog reference after creation ((MainHelpDialogViewModel)dialog.DataContext!)._dialog = dialog; return dialog.ShowDialog(view as Window); } async Task GoToPathAsync() { AvaloniaObject? view = (Application.Current as PrismApplication)?.MainWindow; if(view is null) return; var dialog = new GoToPathDialog { DataContext = new GoToPathDialogViewModel(null!) }; // Set the dialog reference after creation ((GoToPathDialogViewModel)dialog.DataContext!)._dialog = dialog; bool? result = await dialog.ShowDialog(view as Window); if(result == true) { var viewModel = (GoToPathDialogViewModel)dialog.DataContext; if(viewModel.Path is not null && Directory.Exists(viewModel.Path)) { Environment.CurrentDirectory = viewModel.Path; LoadFiles(); } } } void SectorView() { if(SelectedFile?.ImageFormat is null) return; var parameters = new NavigationParameters { { "imageFormat", SelectedFile.ImageFormat }, { "filePath", SelectedFile.Path } }; _regionManager.RequestNavigate("ContentRegion", nameof(HexViewWindow), parameters); } void Exit() { var lifetime = Application.Current!.ApplicationLifetime as IControlledApplicationLifetime; lifetime!.Shutdown(); } /// public override void OnNavigatedTo(NavigationContext navigationContext) { LoadFiles(); } public void LoadComplete() { LoadFiles(); } public void LoadFiles() { IsStatusVisible = true; Status = Localization.Resources.Loading; CurrentPath = Directory.GetCurrentDirectory(); Files.Clear(); var parentDirectory = new FileModel { Filename = "..", Path = Path.GetRelativePath(CurrentPath, ".."), ForegroundBrush = new SolidColorBrush(Color.Parse(DirColorsParser.Instance.DirectoryColor ?? DirColorsParser.Instance.NormalColor)), IsDirectory = true }; Files.Add(parentDirectory); foreach(FileModel model in Directory.GetDirectories(CurrentPath, "*", SearchOption.TopDirectoryOnly) .Select(static directory => new FileModel { Path = directory, Filename = Path.GetFileName(directory), ForegroundBrush = new SolidColorBrush(Color.Parse(DirColorsParser.Instance .DirectoryColor ?? DirColorsParser.Instance.NormalColor)), IsDirectory = true })) Files.Add(model); foreach(string file in Directory.GetFiles(CurrentPath, "*", SearchOption.TopDirectoryOnly)) { var model = new FileModel { Path = file, Filename = Path.GetFileName(file) }; string extension = Path.GetExtension(file); model.ForegroundBrush = new SolidColorBrush(Color.Parse(DirColorsParser.Instance.ExtensionColors.TryGetValue(extension, out string? hex) ? hex : DirColorsParser.Instance.NormalColor)); Files.Add(model); } _ = Task.Run(Worker); } void Worker() { IsStatusVisible = true; Status = Localization.Resources.Loading_file_information; foreach(FileModel file in Files) { try { file.FileInfo = new FileInfo(file.Path); IFilter inputFilter = PluginRegister.Singleton.GetFilter(file.Path); if(inputFilter is null) continue; IBaseImage imageFormat = ImageFormat.Detect(inputFilter); if(imageFormat is null) continue; ErrorNumber opened = imageFormat.Open(inputFilter); if(opened != ErrorNumber.NoError) continue; StringBuilder sb = new(); if(!string.IsNullOrWhiteSpace(imageFormat.Info.Version)) { sb.AppendFormat(Aaru.Localization.Core.Format_0_version_1_WithMarkup, Markup.Escape(imageFormat.Format), Markup.Escape(imageFormat.Info.Version)) .AppendLine(); } else { sb.AppendFormat(Aaru.Localization.Core.Format_0_WithMarkup, Markup.Escape(imageFormat.Format)) .AppendLine(); } switch(string.IsNullOrWhiteSpace(imageFormat.Info.Application)) { case false when !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion): sb.AppendFormat(Aaru.Localization.Core.Was_created_with_0_version_1_WithMarkup, Markup.Escape(imageFormat.Info.Application), Markup.Escape(imageFormat.Info.ApplicationVersion)) .AppendLine(); break; case false: sb.AppendFormat(Aaru.Localization.Core.Was_created_with_0_WithMarkup, Markup.Escape(imageFormat.Info.Application)) .AppendLine(); break; } sb.AppendFormat(Aaru.Localization.Core.Image_without_headers_is_0_bytes_long, imageFormat.Info.ImageSize) .AppendLine(); sb.AppendFormat(Aaru.Localization.Core.Contains_a_media_of_0_sectors, imageFormat.Info.Sectors) .AppendLine(); sb.AppendFormat(Aaru.Localization.Core.Maximum_sector_size_of_0_bytes, imageFormat.Info.SectorSize) .AppendLine(); sb.AppendFormat(Aaru.Localization.Core.Would_be_0_humanized, ByteSize.FromBytes(imageFormat.Info.Sectors * imageFormat.Info.SectorSize).Humanize()) .AppendLine(); if(!string.IsNullOrWhiteSpace(imageFormat.Info.Creator)) { sb.AppendFormat(Aaru.Localization.Core.Created_by_0_WithMarkup, Markup.Escape(imageFormat.Info.Creator)) .AppendLine(); } if(imageFormat.Info.CreationTime != DateTime.MinValue) sb.AppendFormat(Aaru.Localization.Core.Created_on_0, imageFormat.Info.CreationTime).AppendLine(); if(imageFormat.Info.LastModificationTime != DateTime.MinValue) { sb.AppendFormat(Aaru.Localization.Core.Last_modified_on_0, imageFormat.Info.LastModificationTime) .AppendLine(); } sb.AppendFormat(Aaru.Localization.Core.Contains_a_media_of_type_0, imageFormat.Info.MediaType.Humanize()) .AppendLine(); sb.AppendFormat(Aaru.Localization.Core.XML_type_0, imageFormat.Info.MetadataMediaType).AppendLine(); sb.AppendLine(imageFormat.Info.HasPartitions ? Aaru.Localization.Core.Has_partitions : Aaru.Localization.Core.Doesnt_have_partitions); sb.AppendLine(imageFormat.Info.HasSessions ? Aaru.Localization.Core.Has_sessions : Aaru.Localization.Core.Doesnt_have_sessions); if(!string.IsNullOrWhiteSpace(imageFormat.Info.Comments)) { sb.AppendFormat(Aaru.Localization.Core.Comments_0_WithMarkup, Markup.Escape(imageFormat.Info.Comments)) .AppendLine(); } if(imageFormat.Info.MediaSequence != 0 && imageFormat.Info.LastMediaSequence != 0) { sb.AppendFormat(Aaru.Localization.Core.Media_is_number_0_on_a_set_of_1_medias, imageFormat.Info.MediaSequence, imageFormat.Info.LastMediaSequence) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle)) { sb.AppendFormat(Aaru.Localization.Core.Media_title_0_WithMarkup, Markup.Escape(imageFormat.Info.MediaTitle)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer)) { sb.AppendFormat(Aaru.Localization.Core.Media_manufacturer_0_WithMarkup, Markup.Escape(imageFormat.Info.MediaManufacturer)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel)) { sb.AppendFormat(Aaru.Localization.Core.Media_model_0_WithMarkup, Markup.Escape(imageFormat.Info.MediaModel)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber)) { sb.AppendFormat(Aaru.Localization.Core.Media_serial_number_0_WithMarkup, Markup.Escape(imageFormat.Info.MediaSerialNumber)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode)) { sb.AppendFormat(Aaru.Localization.Core.Media_barcode_0_WithMarkup, Markup.Escape(imageFormat.Info.MediaBarcode)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber)) { sb.AppendFormat(Aaru.Localization.Core.Media_part_number_0_WithMarkup, Markup.Escape(imageFormat.Info.MediaPartNumber)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer)) { sb.AppendFormat(Aaru.Localization.Core.Drive_manufacturer_0_WithMarkup, Markup.Escape(imageFormat.Info.DriveManufacturer)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel)) { sb.AppendFormat(Aaru.Localization.Core.Drive_model_0_WithMarkup, Markup.Escape(imageFormat.Info.DriveModel)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber)) { sb.AppendFormat(Aaru.Localization.Core.Drive_serial_number_0_WithMarkup, Markup.Escape(imageFormat.Info.DriveSerialNumber)) .AppendLine(); } if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision)) { sb.AppendFormat(Aaru.Localization.Core.Drive_firmware_info_0_WithMarkup, Markup.Escape(imageFormat.Info.DriveFirmwareRevision)) .AppendLine(); } if(imageFormat.Info.Cylinders > 0 && imageFormat.Info is { Heads: > 0, SectorsPerTrack: > 0 } && imageFormat.Info.MetadataMediaType != MetadataMediaType.OpticalDisc && imageFormat is not ITapeImage { IsTape: true }) { sb.AppendFormat(Aaru.Localization.Core .Media_geometry_0_cylinders_1_heads_2_sectors_per_track_WithMarkup, imageFormat.Info.Cylinders, imageFormat.Info.Heads, imageFormat.Info.SectorsPerTrack) .AppendLine(); } if(imageFormat.Info.ReadableMediaTags is { Count: > 0 }) { sb.AppendFormat(Aaru.Localization.Core.Contains_0_readable_media_tags_WithMarkup, imageFormat.Info.ReadableMediaTags.Count) .AppendLine(); foreach(MediaTagType tag in imageFormat.Info.ReadableMediaTags.Order()) sb.Append($"[italic][rosybrown]{Markup.Escape(tag.ToString())}[/][/] "); sb.AppendLine(); } if(imageFormat.Info.ReadableSectorTags is { Count: > 0 }) { sb.AppendFormat(Aaru.Localization.Core.Contains_0_readable_sector_tags_WithMarkup, imageFormat.Info.ReadableSectorTags.Count) .AppendLine(); foreach(SectorTagType tag in imageFormat.Info.ReadableSectorTags.Order()) sb.Append($"[italic][rosybrown]{Markup.Escape(tag.ToString())}[/][/] "); sb.AppendLine(); } file.Information = sb.ToString(); file.ImageFormat = imageFormat as IMediaImage; } catch(Exception ex) { SentrySdk.CaptureException(ex); } } Status = Localization.Resources.Done; IsStatusVisible = false; } void OpenSelectedFile() { if(SelectedFile.IsDirectory) { CurrentPath = SelectedFile.Path; Environment.CurrentDirectory = CurrentPath; LoadFiles(); return; } if(SelectedFile.ImageFormat is null) return; var parameters = new NavigationParameters { { "imageFormat", SelectedFile.ImageFormat }, { "filePath", SelectedFile.Path } }; _regionManager.RequestNavigate("ContentRegion", nameof(ImageWindow), parameters); } bool CanOpenSelectedFile() => SelectedFile != null; }