Files
Aaru/Aaru.Gui/ViewModels/Panels/ImageInfoViewModel.cs

901 lines
41 KiB
C#
Raw Normal View History

2020-04-17 21:45:50 +01:00
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : ImageInfoViewModel.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : GUI view models.
//
// --[ Description ] ----------------------------------------------------------
//
// View model and code for the opened image information panel.
//
// --[ 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/>.
//
// ----------------------------------------------------------------------------
2022-12-03 16:07:10 +00:00
// Copyright © 2011-2023 Natalia Portillo
2020-04-17 21:45:50 +01:00
// ****************************************************************************/
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Text;
using Aaru.CommonTypes.AaruMetadata;
2020-02-27 00:33:26 +00:00
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Decoders.CD;
using Aaru.Decoders.DVD;
using Aaru.Decoders.SCSI;
using Aaru.Decoders.Xbox;
using Aaru.Gui.Models;
2020-04-16 20:40:25 +01:00
using Aaru.Gui.ViewModels.Tabs;
using Aaru.Gui.ViewModels.Windows;
using Aaru.Gui.Views.Tabs;
using Aaru.Gui.Views.Windows;
2020-07-20 15:43:52 +01:00
using Aaru.Helpers;
using Aaru.Localization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using ReactiveUI;
using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry;
using Session = Aaru.CommonTypes.Structs.Session;
using Track = Aaru.CommonTypes.Structs.Track;
namespace Aaru.Gui.ViewModels.Panels;
2022-03-06 13:29:38 +00:00
public sealed class ImageInfoViewModel : ViewModelBase
{
2022-03-06 13:29:38 +00:00
readonly IFilter _filter;
readonly IMediaImage _imageFormat;
readonly string _imagePath;
DecodeMediaTags _decodeMediaTags;
ImageChecksum _imageChecksum;
ImageConvert _imageConvert;
ImageEntropy _imageEntropy;
ImageSidecar _imageSidecar;
ImageVerify _imageVerify;
ViewSector _viewSector;
public ImageInfoViewModel(string imagePath, IFilter filter, IMediaImage imageFormat, Window view)
{
2022-03-06 13:29:38 +00:00
_imagePath = imagePath;
_filter = filter;
_imageFormat = imageFormat;
MediaTagsList = new ObservableCollection<string>();
SectorTagsList = new ObservableCollection<string>();
Sessions = new ObservableCollection<Session>();
Tracks = new ObservableCollection<Track>();
DumpHardwareList = new ObservableCollection<DumpHardwareModel>();
EntropyCommand = ReactiveCommand.Create(ExecuteEntropyCommand);
VerifyCommand = ReactiveCommand.Create(ExecuteVerifyCommand);
ChecksumCommand = ReactiveCommand.Create(ExecuteChecksumCommand);
ConvertCommand = ReactiveCommand.Create(ExecuteConvertCommand);
CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand);
ViewSectorsCommand = ReactiveCommand.Create(ExecuteViewSectorsCommand);
DecodeMediaTagCommand = ReactiveCommand.Create(ExecuteDecodeMediaTagCommand);
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
var genericHddIcon =
2023-09-25 22:58:48 +01:00
new Bitmap(AssetLoader.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png")));
2022-03-06 13:29:38 +00:00
var genericOpticalIcon =
2023-09-25 22:58:48 +01:00
new Bitmap(AssetLoader.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png")));
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
var genericFolderIcon =
2023-09-25 22:58:48 +01:00
new Bitmap(AssetLoader.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png")));
2022-03-06 13:29:38 +00:00
var mediaResource = new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png");
2023-09-25 22:58:48 +01:00
MediaLogo = AssetLoader.Exists(mediaResource)
? new Bitmap(AssetLoader.Open(mediaResource))
: imageFormat.Info.MetadataMediaType == MetadataMediaType.BlockMedia
2022-03-06 13:29:38 +00:00
? genericHddIcon
: imageFormat.Info.MetadataMediaType == MetadataMediaType.OpticalDisc
2022-03-06 13:29:38 +00:00
? genericOpticalIcon
: genericFolderIcon;
ImagePathText = string.Format(UI.Path_0, imagePath);
FilterText = string.Format(UI.Filter_0, filter.Name);
ImageIdentifiedText = string.Format(UI.Image_format_identified_by_0_1, imageFormat.Name, imageFormat.Id);
2022-03-06 13:29:38 +00:00
ImageFormatText = !string.IsNullOrWhiteSpace(imageFormat.Info.Version)
? string.Format(UI.Format_0_version_1, imageFormat.Format, imageFormat.Info.Version)
: string.Format(UI.Format_0, imageFormat.Format);
2022-03-06 13:29:38 +00:00
ImageSizeText = string.Format(Localization.Core.Image_without_headers_is_0_bytes_long,
imageFormat.Info.ImageSize);
2022-03-06 13:29:38 +00:00
SectorsText =
string.Format(Localization.Core.Contains_a_media_of_0_sectors_with_a_maximum_sector_size_of_1_bytes_etc,
imageFormat.Info.Sectors, imageFormat.Info.SectorSize,
imageFormat.Info.Sectors * imageFormat.Info.SectorSize);
2022-03-06 13:29:38 +00:00
MediaTypeText = string.Format(Localization.Core.Contains_a_media_of_type_0_and_XML_type_1,
imageFormat.Info.MediaType, imageFormat.Info.MetadataMediaType);
2022-03-06 13:29:38 +00:00
HasPartitionsText = imageFormat.Info.HasPartitions ? UI.Has_partitions : UI.Doesnt_have_partitions;
HasSessionsText = imageFormat.Info.HasSessions ? UI.Has_sessions : UI.Doesnt_have_sessions;
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.Application))
ApplicationText = !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion)
? string.Format(Localization.Core.Was_created_with_0_version_1,
imageFormat.Info.Application, imageFormat.Info.ApplicationVersion)
: string.Format(Localization.Core.Was_created_with_0, imageFormat.Info.Application);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.Creator))
CreatorText = string.Format(Localization.Core.Created_by_0, imageFormat.Info.Creator);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.CreationTime != DateTime.MinValue)
CreationTimeText = string.Format(Localization.Core.Created_on_0, imageFormat.Info.CreationTime);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.LastModificationTime != DateTime.MinValue)
LastModificationTimeText =
string.Format(Localization.Core.Last_modified_on_0, imageFormat.Info.LastModificationTime);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.MediaSequence != 0 &&
imageFormat.Info.LastMediaSequence != 0)
MediaSequenceText = string.Format(Localization.Core.Media_is_number_0_on_a_set_of_1_medias,
imageFormat.Info.MediaSequence, imageFormat.Info.LastMediaSequence);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle))
MediaTitleText = string.Format(UI.Media_title_0, imageFormat.Info.MediaTitle);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer))
MediaManufacturerText =
string.Format(Localization.Core.Media_manufacturer_0, imageFormat.Info.MediaManufacturer);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel))
MediaModelText = string.Format(UI.Media_model_0, imageFormat.Info.MediaModel);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber))
MediaSerialNumberText =
string.Format(Localization.Core.Media_serial_number_0, imageFormat.Info.MediaSerialNumber);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode))
MediaBarcodeText = string.Format(UI.Media_barcode_0, imageFormat.Info.MediaBarcode);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber))
MediaPartNumberText = string.Format(UI.Media_part_number_0, imageFormat.Info.MediaPartNumber);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer))
DriveManufacturerText = string.Format(UI.Drive_manufacturer_0, imageFormat.Info.DriveManufacturer);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel))
DriveModelText = string.Format(UI.Drive_model_0, imageFormat.Info.DriveModel);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber))
DriveSerialNumberText =
string.Format(Localization.Core.Drive_serial_number_0, imageFormat.Info.DriveSerialNumber);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision))
DriveFirmwareRevisionText = string.Format(UI.Drive_firmware_info_0, imageFormat.Info.DriveFirmwareRevision);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.Cylinders > 0 &&
imageFormat.Info is { Heads: > 0, SectorsPerTrack: > 0 } &&
imageFormat.Info.MetadataMediaType != MetadataMediaType.OpticalDisc &&
2022-11-14 01:49:10 +00:00
imageFormat is not ITapeImage { IsTape: true })
MediaGeometryText = string.Format(UI.Media_geometry_0_cylinders_1_heads_2_sectors_per_track,
imageFormat.Info.Cylinders, imageFormat.Info.Heads,
imageFormat.Info.SectorsPerTrack);
if(imageFormat.Info.ReadableMediaTags is { Count: > 0 })
2022-03-06 13:29:38 +00:00
foreach(MediaTagType tag in imageFormat.Info.ReadableMediaTags.OrderBy(t => t))
MediaTagsList.Add(tag.ToString());
2018-10-07 12:45:57 +01:00
if(imageFormat.Info.ReadableSectorTags is { Count: > 0 })
2022-03-06 13:29:38 +00:00
foreach(SectorTagType tag in imageFormat.Info.ReadableSectorTags.OrderBy(t => t))
SectorTagsList.Add(tag.ToString());
2018-10-07 12:45:57 +01:00
2022-03-06 13:29:38 +00:00
PeripheralDeviceTypes scsiDeviceType = PeripheralDeviceTypes.DirectAccess;
byte[] scsiInquiryData = null;
Inquiry? scsiInquiry = null;
Modes.DecodedMode? scsiMode = null;
byte[] scsiModeSense6 = null;
byte[] scsiModeSense10 = null;
ErrorNumber errno;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_INQUIRY) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_INQUIRY, out scsiInquiryData);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
{
scsiDeviceType = (PeripheralDeviceTypes)(scsiInquiryData[0] & 0x1F);
2022-03-06 13:29:38 +00:00
scsiInquiry = Inquiry.Decode(scsiInquiryData);
}
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_MODESENSE_6) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_MODESENSE_6, out scsiModeSense6);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
scsiMode = Modes.DecodeMode6(scsiModeSense6, scsiDeviceType);
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_MODESENSE_10) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_MODESENSE_10, out scsiModeSense10);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
scsiMode = Modes.DecodeMode10(scsiModeSense10, scsiDeviceType);
}
2022-03-06 13:29:38 +00:00
ScsiInfo = new ScsiInfo
{
DataContext = new ScsiInfoViewModel(scsiInquiryData, scsiInquiry, null, scsiMode, scsiDeviceType,
scsiModeSense6, scsiModeSense10, null, view)
};
2022-03-06 13:29:38 +00:00
byte[] ataIdentify = null;
byte[] atapiIdentify = null;
errno = ErrorNumber.NoData;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.ATA_IDENTIFY) == true)
errno = imageFormat.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out ataIdentify);
else if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.ATAPI_IDENTIFY) == true)
errno = imageFormat.ReadMediaTag(MediaTagType.ATAPI_IDENTIFY, out atapiIdentify);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
AtaInfo = new AtaInfo
{
2022-03-06 13:29:38 +00:00
DataContext = new AtaInfoViewModel(ataIdentify, atapiIdentify, null, view)
};
2022-03-06 13:29:38 +00:00
byte[] toc = null;
TOC.CDTOC? decodedToc = null;
byte[] fullToc = null;
FullTOC.CDFullTOC? decodedFullToc = null;
byte[] pma = null;
byte[] atip = null;
ATIP.CDATIP decodedAtip = null;
byte[] cdtext = null;
CDTextOnLeadIn.CDText? decodedCdText = null;
string mediaCatalogueNumber = null;
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_TOC) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.CD_TOC, out toc);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError &&
toc.Length > 0)
{
2022-03-06 13:29:38 +00:00
ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(toc, 0));
2022-03-06 13:29:38 +00:00
if(dataLen + 2 != toc.Length)
{
byte[] tmp = new byte[toc.Length + 2];
2022-03-06 13:29:38 +00:00
Array.Copy(toc, 0, tmp, 2, toc.Length);
tmp[0] = (byte)((toc.Length & 0xFF00) >> 8);
tmp[1] = (byte)(toc.Length & 0xFF);
toc = tmp;
}
2022-03-06 13:29:38 +00:00
decodedToc = TOC.Decode(toc);
}
2022-03-06 13:29:38 +00:00
}
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.CD_FullTOC, out fullToc);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError &&
fullToc.Length > 0)
{
2022-03-06 13:29:38 +00:00
ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(fullToc, 0));
2022-03-06 13:29:38 +00:00
if(dataLen + 2 != fullToc.Length)
{
byte[] tmp = new byte[fullToc.Length + 2];
2022-03-06 13:29:38 +00:00
Array.Copy(fullToc, 0, tmp, 2, fullToc.Length);
tmp[0] = (byte)((fullToc.Length & 0xFF00) >> 8);
tmp[1] = (byte)(fullToc.Length & 0xFF);
fullToc = tmp;
}
2022-03-06 13:29:38 +00:00
decodedFullToc = FullTOC.Decode(fullToc);
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_PMA) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.CD_PMA, out pma);
if(errno == ErrorNumber.NoError &&
pma.Length > 0)
{
2022-03-06 13:29:38 +00:00
ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(pma, 0));
2022-03-06 13:29:38 +00:00
if(dataLen + 2 != pma.Length)
{
byte[] tmp = new byte[pma.Length + 2];
2022-03-06 13:29:38 +00:00
Array.Copy(pma, 0, tmp, 2, pma.Length);
tmp[0] = (byte)((pma.Length & 0xFF00) >> 8);
tmp[1] = (byte)(pma.Length & 0xFF);
pma = tmp;
}
}
2022-03-06 13:29:38 +00:00
}
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_ATIP) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.CD_ATIP, out atip);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
{
2022-03-06 13:29:38 +00:00
uint dataLen = Swapping.Swap(BitConverter.ToUInt32(atip, 0));
2022-03-06 13:29:38 +00:00
if(dataLen + 4 != atip.Length)
{
byte[] tmp = new byte[atip.Length + 4];
2022-03-06 13:29:38 +00:00
Array.Copy(atip, 0, tmp, 4, atip.Length);
tmp[0] = (byte)((atip.Length & 0xFF000000) >> 24);
tmp[1] = (byte)((atip.Length & 0xFF0000) >> 16);
tmp[2] = (byte)((atip.Length & 0xFF00) >> 8);
tmp[3] = (byte)(atip.Length & 0xFF);
atip = tmp;
}
2022-03-06 13:29:38 +00:00
decodedAtip = ATIP.Decode(atip);
}
2022-03-06 13:29:38 +00:00
}
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_TEXT) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.CD_TEXT, out cdtext);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
{
2022-03-06 13:29:38 +00:00
uint dataLen = Swapping.Swap(BitConverter.ToUInt32(cdtext, 0));
2022-03-06 13:29:38 +00:00
if(dataLen + 4 != cdtext.Length)
{
byte[] tmp = new byte[cdtext.Length + 4];
2022-03-06 13:29:38 +00:00
Array.Copy(cdtext, 0, tmp, 4, cdtext.Length);
tmp[0] = (byte)((cdtext.Length & 0xFF000000) >> 24);
tmp[1] = (byte)((cdtext.Length & 0xFF0000) >> 16);
tmp[2] = (byte)((cdtext.Length & 0xFF00) >> 8);
tmp[3] = (byte)(cdtext.Length & 0xFF);
cdtext = tmp;
}
2022-03-06 13:29:38 +00:00
decodedCdText = CDTextOnLeadIn.Decode(cdtext);
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_MCN) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcn);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
mediaCatalogueNumber = Encoding.UTF8.GetString(mcn);
}
2022-03-06 13:29:38 +00:00
CompactDiscInfo = new CompactDiscInfo
{
DataContext = new CompactDiscInfoViewModel(toc, atip, null, null, fullToc, pma, cdtext, decodedToc,
decodedAtip, null, decodedFullToc, decodedCdText, null,
mediaCatalogueNumber, null, view)
};
byte[] dvdPfi = null;
byte[] dvdDmi = null;
byte[] dvdCmi = null;
byte[] hddvdCopyrightInformation = null;
byte[] dvdBca = null;
PFI.PhysicalFormatInformation? decodedPfi = null;
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_PFI) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.DVD_PFI, out dvdPfi);
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
decodedPfi = PFI.Decode(dvdPfi, imageFormat.Info.MediaType);
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_DMI) == true)
imageFormat.ReadMediaTag(MediaTagType.DVD_DMI, out dvdDmi);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_CMI) == true)
imageFormat.ReadMediaTag(MediaTagType.DVD_CMI, out dvdCmi);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.HDDVD_CPI) == true)
imageFormat.ReadMediaTag(MediaTagType.HDDVD_CPI, out hddvdCopyrightInformation);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_BCA) == true)
imageFormat.ReadMediaTag(MediaTagType.DVD_BCA, out dvdBca);
2022-03-06 13:29:38 +00:00
DvdInfo = new DvdInfo
{
DataContext = new DvdInfoViewModel(imageFormat.Info.MediaType, dvdPfi, dvdDmi, dvdCmi,
hddvdCopyrightInformation, dvdBca, null, decodedPfi, view)
};
2022-03-06 13:29:38 +00:00
byte[] dvdRamDds = null;
byte[] dvdRamCartridgeStatus = null;
byte[] dvdRamSpareArea = null;
byte[] lastBorderOutRmd = null;
byte[] dvdPreRecordedInfo = null;
byte[] dvdrMediaIdentifier = null;
byte[] dvdrPhysicalInformation = null;
byte[] hddvdrMediumStatus = null;
byte[] dvdrLayerCapacity = null;
byte[] dvdrDlMiddleZoneStart = null;
byte[] dvdrDlJumpIntervalSize = null;
byte[] dvdrDlManualLayerJumpStartLba = null;
byte[] dvdPlusAdip = null;
byte[] dvdPlusDcb = null;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_DDS) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDRAM_DDS, out dvdRamDds);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_MediumStatus) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDRAM_MediumStatus, out dvdRamCartridgeStatus);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_SpareArea) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDRAM_SpareArea, out dvdRamSpareArea);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_RMD) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDR_RMD, out lastBorderOutRmd);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_PreRecordedInfo) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDR_PreRecordedInfo, out dvdPreRecordedInfo);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_MediaIdentifier) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDR_MediaIdentifier, out dvdrMediaIdentifier);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_PFI) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDR_PFI, out dvdrPhysicalInformation);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.HDDVD_MediumStatus) == true)
imageFormat.ReadMediaTag(MediaTagType.HDDVD_MediumStatus, out hddvdrMediumStatus);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_LayerCapacity) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDDL_LayerCapacity, out dvdrLayerCapacity);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_MiddleZoneAddress) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDDL_MiddleZoneAddress, out dvdrDlMiddleZoneStart);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_JumpIntervalSize) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDDL_JumpIntervalSize, out dvdrDlJumpIntervalSize);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_ManualLayerJumpLBA) == true)
imageFormat.ReadMediaTag(MediaTagType.DVDDL_ManualLayerJumpLBA, out dvdrDlManualLayerJumpStartLba);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_ADIP) == true)
imageFormat.ReadMediaTag(MediaTagType.DVD_ADIP, out dvdPlusAdip);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DCB) == true)
imageFormat.ReadMediaTag(MediaTagType.DCB, out dvdPlusDcb);
2022-03-06 13:29:38 +00:00
DvdWritableInfo = new DvdWritableInfo
{
DataContext = new DvdWritableInfoViewModel(imageFormat.Info.MediaType, dvdRamDds, dvdRamCartridgeStatus,
dvdRamSpareArea, lastBorderOutRmd, dvdPreRecordedInfo,
2022-03-07 07:36:44 +00:00
dvdrMediaIdentifier, dvdrPhysicalInformation, hddvdrMediumStatus,
null, dvdrLayerCapacity, dvdrDlMiddleZoneStart,
dvdrDlJumpIntervalSize, dvdrDlManualLayerJumpStartLba, null,
dvdPlusAdip, dvdPlusDcb, view)
2022-03-06 13:29:38 +00:00
};
2022-03-06 13:29:38 +00:00
byte[] blurayBurstCuttingArea = null;
byte[] blurayCartridgeStatus = null;
byte[] blurayDds = null;
byte[] blurayDiscInformation = null;
byte[] blurayPowResources = null;
byte[] bluraySpareAreaInformation = null;
byte[] blurayTrackResources = null;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_BCA) == true)
imageFormat.ReadMediaTag(MediaTagType.BD_BCA, out blurayBurstCuttingArea);
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_CartridgeStatus) == true)
imageFormat.ReadMediaTag(MediaTagType.BD_CartridgeStatus, out blurayCartridgeStatus);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_DDS) == true)
imageFormat.ReadMediaTag(MediaTagType.BD_DDS, out blurayDds);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_DI) == true)
imageFormat.ReadMediaTag(MediaTagType.BD_DI, out blurayDiscInformation);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_POWResourcesInformation) == true)
imageFormat.ReadMediaTag(MediaTagType.MMC_POWResourcesInformation, out blurayPowResources);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_SpareArea) == true)
imageFormat.ReadMediaTag(MediaTagType.BD_SpareArea, out bluraySpareAreaInformation);
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_TrackResourcesInformation) == true)
imageFormat.ReadMediaTag(MediaTagType.MMC_TrackResourcesInformation, out blurayTrackResources);
2022-03-06 13:29:38 +00:00
BlurayInfo = new BlurayInfo
{
DataContext = new BlurayInfoViewModel(blurayDiscInformation, blurayBurstCuttingArea, blurayDds,
2022-03-07 07:36:44 +00:00
blurayCartridgeStatus, bluraySpareAreaInformation, blurayPowResources,
blurayTrackResources, null, null, view)
2022-03-06 13:29:38 +00:00
};
2022-03-06 13:29:38 +00:00
byte[] xboxDmi = null;
byte[] xboxSecuritySector = null;
SS.SecuritySector? decodedXboxSecuritySector = null;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.Xbox_DMI) == true)
imageFormat.ReadMediaTag(MediaTagType.Xbox_DMI, out xboxDmi);
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.Xbox_SecuritySector) == true)
{
errno = imageFormat.ReadMediaTag(MediaTagType.Xbox_SecuritySector, out xboxSecuritySector);
2022-03-06 13:29:38 +00:00
if(errno == ErrorNumber.NoError)
decodedXboxSecuritySector = SS.Decode(xboxSecuritySector);
}
2022-03-06 13:29:38 +00:00
XboxInfo = new XboxInfo
{
DataContext = new XboxInfoViewModel(null, xboxDmi, xboxSecuritySector, decodedXboxSecuritySector, view)
};
2022-03-06 13:29:38 +00:00
errno = ErrorNumber.NoData;
byte[] pcmciaCis = null;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.PCMCIA_CIS) == true)
errno = imageFormat.ReadMediaTag(MediaTagType.PCMCIA_CIS, out pcmciaCis);
if(errno == ErrorNumber.NoError)
PcmciaInfo = new PcmciaInfo
{
2022-03-06 13:29:38 +00:00
DataContext = new PcmciaInfoViewModel(pcmciaCis, view)
};
2022-03-06 13:29:38 +00:00
DeviceType deviceType = DeviceType.Unknown;
byte[] cid = null;
byte[] csd = null;
byte[] ocr = null;
byte[] extendedCsd = null;
byte[] scr = null;
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_CID) == true)
{
imageFormat.ReadMediaTag(MediaTagType.SD_CID, out cid);
deviceType = DeviceType.SecureDigital;
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_CSD) == true)
{
imageFormat.ReadMediaTag(MediaTagType.SD_CSD, out csd);
deviceType = DeviceType.SecureDigital;
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_OCR) == true)
{
imageFormat.ReadMediaTag(MediaTagType.SD_OCR, out ocr);
deviceType = DeviceType.SecureDigital;
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_SCR) == true)
{
imageFormat.ReadMediaTag(MediaTagType.SD_SCR, out scr);
deviceType = DeviceType.SecureDigital;
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_CID) == true)
{
imageFormat.ReadMediaTag(MediaTagType.MMC_CID, out cid);
deviceType = DeviceType.MMC;
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_CSD) == true)
{
imageFormat.ReadMediaTag(MediaTagType.MMC_CSD, out csd);
deviceType = DeviceType.MMC;
}
2022-03-06 13:29:38 +00:00
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_OCR) == true)
{
imageFormat.ReadMediaTag(MediaTagType.MMC_OCR, out ocr);
deviceType = DeviceType.MMC;
}
if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_ExtendedCSD) == true)
{
imageFormat.ReadMediaTag(MediaTagType.MMC_ExtendedCSD, out extendedCsd);
deviceType = DeviceType.MMC;
}
SdMmcInfo = new SdMmcInfo
{
DataContext = new SdMmcInfoViewModel(deviceType, cid, csd, ocr, extendedCsd, scr)
};
2022-03-06 13:29:38 +00:00
if(imageFormat is IOpticalMediaImage opticalMediaImage)
{
try
{
if(opticalMediaImage.Sessions is { Count: > 0 })
2022-03-06 13:29:38 +00:00
foreach(Session session in opticalMediaImage.Sessions)
Sessions.Add(session);
}
2022-03-06 13:29:38 +00:00
catch
{
2022-03-06 13:29:38 +00:00
// ignored
}
2022-03-06 13:29:38 +00:00
try
{
if(opticalMediaImage.Tracks is { Count: > 0 })
2022-03-06 13:29:38 +00:00
foreach(Track track in opticalMediaImage.Tracks)
Tracks.Add(track);
}
2022-03-06 13:29:38 +00:00
catch
{
2022-03-06 13:29:38 +00:00
// ignored
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(imageFormat.DumpHardware is null)
return;
foreach(DumpHardware dump in imageFormat.DumpHardware)
2022-03-06 13:29:38 +00:00
{
foreach(Extent extent in dump.Extents)
2022-03-06 13:29:38 +00:00
DumpHardwareList.Add(new DumpHardwareModel
{
2022-03-06 13:29:38 +00:00
Manufacturer = dump.Manufacturer,
Model = dump.Model,
Serial = dump.Serial,
SoftwareName = dump.Software.Name,
SoftwareVersion = dump.Software.Version,
OperatingSystem = dump.Software.OperatingSystem,
Start = extent.Start,
End = extent.End
});
}
}
2018-10-07 22:57:48 +01:00
2022-03-06 13:29:38 +00:00
public ScsiInfo ScsiInfo { get; }
public AtaInfo AtaInfo { get; }
public CompactDiscInfo CompactDiscInfo { get; }
public DvdInfo DvdInfo { get; }
public DvdWritableInfo DvdWritableInfo { get; }
public BlurayInfo BlurayInfo { get; }
public XboxInfo XboxInfo { get; }
public PcmciaInfo PcmciaInfo { get; }
public SdMmcInfo SdMmcInfo { get; }
public Bitmap MediaLogo { get; }
public string ImagePathText { get; }
public string FilterText { get; }
public string ImageIdentifiedText { get; }
public string MediaTypeText { get; set; }
public string SectorsText { get; set; }
public string HasPartitionsText { get; set; }
public string HasSessionsText { get; set; }
public string ApplicationText { get; set; }
public string CreatorText { get; set; }
public string CreationTimeText { get; set; }
public string LastModificationTimeText { get; set; }
public string MediaSequenceText { get; set; }
public string MediaTitleText { get; set; }
public string MediaManufacturerText { get; set; }
public string MediaModelText { get; set; }
public string MediaSerialNumberText { get; set; }
public string MediaBarcodeText { get; set; }
public string MediaPartNumberText { get; set; }
public string CommentsText => _imageFormat.Info.Comments;
public string DriveManufacturerText { get; set; }
public string DriveModelText { get; set; }
public string DriveSerialNumberText { get; set; }
public string DriveFirmwareRevisionText { get; set; }
public string MediaGeometryText { get; set; }
public ObservableCollection<string> MediaTagsList { get; }
public ObservableCollection<string> SectorTagsList { get; }
public string ImageSizeText { get; set; }
public string ImageFormatText { get; set; }
public ObservableCollection<Session> Sessions { get; }
public ObservableCollection<Track> Tracks { get; }
public ObservableCollection<DumpHardwareModel> DumpHardwareList { get; }
public ReactiveCommand<Unit, Unit> EntropyCommand { get; }
public ReactiveCommand<Unit, Unit> VerifyCommand { get; }
public ReactiveCommand<Unit, Unit> ChecksumCommand { get; }
public ReactiveCommand<Unit, Unit> ConvertCommand { get; }
public ReactiveCommand<Unit, Unit> CreateSidecarCommand { get; }
public ReactiveCommand<Unit, Unit> ViewSectorsCommand { get; }
public ReactiveCommand<Unit, Unit> DecodeMediaTagCommand { get; }
public bool DriveInformationVisible => DriveManufacturerText != null || DriveModelText != null ||
DriveSerialNumberText != null || DriveFirmwareRevisionText != null ||
MediaGeometryText != null;
public bool MediaInformationVisible => MediaSequenceText != null || MediaTitleText != null ||
MediaManufacturerText != null || MediaModelText != null ||
MediaSerialNumberText != null || MediaBarcodeText != null ||
MediaPartNumberText != null;
public string ImageInformationLabel => UI.Title_Image_information;
public string GeneralLabel => UI.Title_General;
public string CommentsLabel => UI.Title_Comments;
public string MediaInformationLabel => UI.Title_Media_information;
public string DriveInformationLabel => UI.Title_Drive_information;
public string ReadableMediaTagsLabel => UI.Title_Readable_media_tags;
public string TagLabel => UI.Title_Readable_media_tags;
public string ReadableSectorTagsLabel => UI.Title_Readable_sector_tags;
public string SessionsLabel => UI.Title_Sessions;
public string SessionLabel => Localization.Core.Title_Session;
public string FirstTrackLabel => Localization.Core.Title_First_track;
public string LastTrackLabel => Localization.Core.Title_Last_track;
public string StartLabel => Localization.Core.Title_Start;
public string EndLabel => Localization.Core.Title_End;
public string TracksLabel => UI.Title_Tracks;
public string TrackLabel => Localization.Core.Title_Track;
public string TypeLabel => UI.Title_Type;
public string BpsLabel => Localization.Core.Title_Bps;
public string RawBpsLabel => Localization.Core.Title_Raw_bps;
public string SubchannelLabel => Localization.Core.Title_Subchannel;
public string PregapLabel => Localization.Core.Title_Pregap;
public string DumpHardwareLabel => UI.Title_Dump_hardware;
public string ManufacturerLabel => UI.Title_Manufacturer;
public string ModelLabel => UI.Title_Model;
public string RevisionLabel => UI.Title_Revision;
public string SerialLabel => UI.Serial;
public string SoftwareLabel => UI.Title_Software;
public string VersionLabel => UI.Title_Version;
public string OperatingSystemLabel => UI.Title_Operating_system;
public string SCSILabel => UI.Title_SCSI;
public string ATA_ATAPILabel => UI.Title_ATA_ATAPI;
public string CompactDiscLabel => Localization.Core.Title_CompactDisc;
public string DVD_HD_DVDLabel => Localization.Core.Title_DVD_HD_DVD;
public string DVD_R_WLabel => Localization.Core.Title_DVD_Plus_Dash_R_W;
public string BluRayLabel => Localization.Core.Title_Blu_ray;
public string PCMCIALabel => UI.Title_PCMCIA;
public string SD_MMCLabel => UI.Title_SD_MMC;
public string XboxLabel => Localization.Core.Title_Xbox;
public string EntropyLabel => UI.ButtonLabel_Calculate_entropy;
public string VerifyLabel => UI.ButtonLabel_Verify;
public string ChecksumLabel => UI.ButtonLabel_Checksum;
public string ConvertLabel => UI.ButtonLabel_Convert_to;
public string CreateSidecarLabel => UI.ButtonLabel_Create_Aaru_Metadata_sidecar;
public string ViewSectorsLabel => UI.ButtonLabel_View_sectors;
public string DecodeMediaTagLabel => UI.ButtonLabel_Decode_media_tags;
2022-03-06 13:29:38 +00:00
void ExecuteEntropyCommand()
{
if(_imageEntropy != null)
{
_imageEntropy.Show();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
_imageEntropy = new ImageEntropy();
_imageEntropy.DataContext = new ImageEntropyViewModel(_imageFormat, _imageEntropy);
2020-02-29 18:03:35 +00:00
2022-03-16 00:31:33 +00:00
_imageEntropy.Closed += (_, _) => _imageEntropy = null;
2018-10-14 19:10:30 +01:00
2022-03-06 13:29:38 +00:00
_imageEntropy.Show();
}
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
void ExecuteVerifyCommand()
{
if(_imageVerify != null)
{
_imageVerify.Show();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
return;
2018-10-14 19:10:30 +01:00
}
2022-03-06 13:29:38 +00:00
_imageVerify = new ImageVerify();
_imageVerify.DataContext = new ImageVerifyViewModel(_imageFormat, _imageVerify);
2020-02-29 18:03:35 +00:00
2022-03-16 00:31:33 +00:00
_imageVerify.Closed += (_, _) => _imageVerify = null;
2018-10-17 22:12:51 +01:00
2022-03-06 13:29:38 +00:00
_imageVerify.Show();
}
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
void ExecuteChecksumCommand()
{
if(_imageChecksum != null)
{
_imageChecksum.Show();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
return;
2018-10-17 22:12:51 +01:00
}
2022-03-06 13:29:38 +00:00
_imageChecksum = new ImageChecksum();
_imageChecksum.DataContext = new ImageChecksumViewModel(_imageFormat, _imageChecksum);
2020-02-29 18:03:35 +00:00
2022-03-16 00:31:33 +00:00
_imageChecksum.Closed += (_, _) => _imageChecksum = null;
2018-10-18 22:24:40 +01:00
2022-03-06 13:29:38 +00:00
_imageChecksum.Show();
}
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
void ExecuteConvertCommand()
{
if(_imageConvert != null)
{
_imageConvert.Show();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
return;
2018-10-18 22:24:40 +01:00
}
2022-03-06 13:29:38 +00:00
_imageConvert = new ImageConvert();
_imageConvert.DataContext = new ImageConvertViewModel(_imageFormat, _imagePath, _imageConvert);
2020-02-29 18:03:35 +00:00
2022-03-16 00:31:33 +00:00
_imageConvert.Closed += (_, _) => _imageConvert = null;
2018-10-23 23:59:33 +01:00
2022-03-06 13:29:38 +00:00
_imageConvert.Show();
}
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
void ExecuteCreateSidecarCommand()
{
if(_imageSidecar != null)
{
_imageSidecar.Show();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
return;
2018-10-23 23:59:33 +01:00
}
2022-03-06 13:29:38 +00:00
_imageSidecar = new ImageSidecar();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
// TODO: Pass thru chosen default encoding
_imageSidecar.DataContext =
new ImageSidecarViewModel(_imageFormat, _imagePath, _filter.Id, null, _imageSidecar);
2022-03-16 00:31:33 +00:00
_imageSidecar.Closed += (_, _) => _imageSidecar = null;
2022-03-06 13:29:38 +00:00
_imageSidecar.Show();
}
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
void ExecuteViewSectorsCommand()
{
if(_viewSector != null)
{
_viewSector.Show();
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
_viewSector = new ViewSector
{
2022-03-06 13:29:38 +00:00
DataContext = new ViewSectorViewModel(_imageFormat)
};
2020-02-29 18:03:35 +00:00
2022-03-16 00:31:33 +00:00
_viewSector.Closed += (_, _) => _viewSector = null;
2022-03-06 13:29:38 +00:00
_viewSector.Show();
}
2020-02-29 18:03:35 +00:00
2022-03-06 13:29:38 +00:00
void ExecuteDecodeMediaTagCommand()
{
if(_decodeMediaTags != null)
{
_decodeMediaTags.Show();
2020-04-14 19:27:07 +01:00
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
_decodeMediaTags = new DecodeMediaTags
2018-11-18 23:01:16 +00:00
{
2022-03-06 13:29:38 +00:00
DataContext = new DecodeMediaTagsViewModel(_imageFormat)
};
2020-02-29 18:03:35 +00:00
2022-03-16 00:31:33 +00:00
_decodeMediaTags.Closed += (_, _) => _decodeMediaTags = null;
2018-11-18 23:01:16 +00:00
2022-03-06 13:29:38 +00:00
_decodeMediaTags.Show();
}
}