Files
Aaru/Aaru.Core/Sidecar/BlockMedia.cs

1295 lines
50 KiB
C#

// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : BlockMedia.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Core algorithms.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains logic to create sidecar from a block media dump.
//
// --[ 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-2023 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Aaru.CommonTypes;
using Aaru.CommonTypes.AaruMetadata;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs.Devices.ATA;
using Aaru.Console;
using Aaru.Decoders.PCMCIA;
using Aaru.Filters;
using Aaru.Helpers;
using Aaru.Images;
using Directory = System.IO.Directory;
using File = System.IO.File;
using MediaType = Aaru.CommonTypes.Metadata.MediaType;
using Partition = Aaru.CommonTypes.Partition;
using Tuple = Aaru.Decoders.PCMCIA.Tuple;
namespace Aaru.Core;
public sealed partial class Sidecar
{
/// <summary>Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick)</summary>
/// <param name="image">Image</param>
/// <param name="filterId">Filter uuid</param>
/// <param name="imagePath">Image path</param>
/// <param name="fi">Image file information</param>
/// <param name="plugins">Image plugins</param>
/// <param name="imgChecksums">List of image checksums</param>
/// <param name="sidecar">Metadata sidecar</param>
/// <param name="encoding">Encoding to be used for filesystem plugins</param>
void BlockMedia(IMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginRegister plugins,
List<CommonTypes.AaruMetadata.Checksum> imgChecksums, ref Metadata sidecar, Encoding encoding)
{
if(_aborted)
return;
sidecar.BlockMedias = new List<BlockMedia>
{
new()
{
Checksums = imgChecksums,
Image = new Image
{
Format = image.Format,
Offset = 0,
Value = Path.GetFileName(imagePath)
},
Size = (ulong)fi.Length,
Sequence = new Sequence
{
Title = image.Info.MediaTitle
}
}
};
if(image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0)
{
sidecar.BlockMedias[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence;
sidecar.BlockMedias[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence;
}
else
{
sidecar.BlockMedias[0].Sequence.MediaSequence = 1;
sidecar.BlockMedias[0].Sequence.TotalMedia = 1;
}
UpdateStatus(Localization.Core.Hashing_media_tags);
ErrorNumber errno;
byte[] buffer;
foreach(MediaTagType tagType in image.Info.ReadableMediaTags)
{
if(_aborted)
return;
switch(tagType)
{
case MediaTagType.ATAPI_IDENTIFY:
errno = image.ReadMediaTag(MediaTagType.ATAPI_IDENTIFY, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].ATA = new ATA
{
Identify = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
}
};
break;
case MediaTagType.ATA_IDENTIFY:
errno = image.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].ATA = new ATA
{
Identify = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
}
};
break;
case MediaTagType.PCMCIA_CIS:
errno = image.ReadMediaTag(MediaTagType.PCMCIA_CIS, out byte[] cis);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].Pcmcia = new Pcmcia
{
Cis = new Dump
{
Checksums = Checksum.GetChecksums(cis),
Size = (ulong)cis.Length
}
};
Tuple[] tuples = CIS.GetTuples(cis);
if(tuples != null)
{
foreach(Tuple tuple in tuples)
{
switch(tuple.Code)
{
case TupleCodes.CISTPL_MANFID:
ManufacturerIdentificationTuple manfid =
CIS.DecodeManufacturerIdentificationTuple(tuple);
if(manfid != null)
{
sidecar.BlockMedias[0].Pcmcia.ManufacturerCode = manfid.ManufacturerID;
sidecar.BlockMedias[0].Pcmcia.CardCode = manfid.CardID;
}
break;
case TupleCodes.CISTPL_VERS_1:
Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);
if(vers != null)
{
sidecar.BlockMedias[0].Pcmcia.Manufacturer = vers.Manufacturer;
sidecar.BlockMedias[0].Pcmcia.ProductName = vers.Product;
sidecar.BlockMedias[0].Pcmcia.Compliance =
$"{vers.MajorVersion}.{vers.MinorVersion}";
sidecar.BlockMedias[0].Pcmcia.AdditionalInformation =
new List<string>(vers.AdditionalInformation);
}
break;
}
}
}
break;
case MediaTagType.SCSI_INQUIRY:
errno = image.ReadMediaTag(MediaTagType.SCSI_INQUIRY, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SCSI = new SCSI
{
Inquiry = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
}
};
break;
case MediaTagType.SD_CID:
errno = image.ReadMediaTag(MediaTagType.SD_CID, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SecureDigital ??= new SecureDigital();
sidecar.BlockMedias[0].SecureDigital.CID = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.SD_CSD:
errno = image.ReadMediaTag(MediaTagType.SD_CSD, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SecureDigital ??= new SecureDigital();
sidecar.BlockMedias[0].SecureDigital.CSD = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.SD_SCR:
errno = image.ReadMediaTag(MediaTagType.SD_SCR, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SecureDigital ??= new SecureDigital();
sidecar.BlockMedias[0].SecureDigital.SCR = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.SD_OCR:
errno = image.ReadMediaTag(MediaTagType.SD_OCR, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SecureDigital ??= new SecureDigital();
sidecar.BlockMedias[0].SecureDigital.OCR = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.MMC_CID:
errno = image.ReadMediaTag(MediaTagType.MMC_CID, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].MultiMediaCard ??= new MultiMediaCard();
sidecar.BlockMedias[0].MultiMediaCard.CID = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.MMC_CSD:
errno = image.ReadMediaTag(MediaTagType.MMC_CSD, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].MultiMediaCard ??= new MultiMediaCard();
sidecar.BlockMedias[0].MultiMediaCard.CSD = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.MMC_OCR:
errno = image.ReadMediaTag(MediaTagType.MMC_OCR, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].MultiMediaCard ??= new MultiMediaCard();
sidecar.BlockMedias[0].MultiMediaCard.OCR = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.MMC_ExtendedCSD:
errno = image.ReadMediaTag(MediaTagType.MMC_ExtendedCSD, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].MultiMediaCard ??= new MultiMediaCard();
sidecar.BlockMedias[0].MultiMediaCard.ExtendedCSD = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.USB_Descriptors:
errno = image.ReadMediaTag(MediaTagType.USB_Descriptors, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].Usb ??= new Usb();
sidecar.BlockMedias[0].Usb.Descriptors = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.SCSI_MODESENSE_6:
errno = image.ReadMediaTag(MediaTagType.SCSI_MODESENSE_6, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SCSI ??= new SCSI();
sidecar.BlockMedias[0].SCSI.ModeSense = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
case MediaTagType.SCSI_MODESENSE_10:
errno = image.ReadMediaTag(MediaTagType.SCSI_MODESENSE_10, out buffer);
if(errno != ErrorNumber.NoError)
break;
sidecar.BlockMedias[0].SCSI ??= new SCSI();
sidecar.BlockMedias[0].SCSI.ModeSense10 = new Dump
{
Checksums = Checksum.GetChecksums(buffer),
Size = (ulong)buffer.Length
};
break;
}
}
// If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum.
if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") &&
filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000"))
sidecar.BlockMedias[0].ContentChecksums = sidecar.BlockMedias[0].Checksums;
else
{
UpdateStatus(Localization.Core.Hashing_sectors);
var contentChkWorker = new Checksum();
// For fast debugging, skip checksum
//goto skipImageChecksum;
const uint sectorsToRead = 64;
ulong sectors = image.Info.Sectors;
ulong doneSectors = 0;
InitProgress2();
while(doneSectors < sectors)
{
if(_aborted)
{
EndProgress2();
return;
}
byte[] sector;
if(sectors - doneSectors >= sectorsToRead)
{
errno = image.ReadSectors(doneSectors, sectorsToRead, out sector);
if(errno != ErrorNumber.NoError)
{
UpdateStatus(string.Format(Localization.Core.Error_0_reading_sector_1, errno, doneSectors));
EndProgress2();
return;
}
UpdateProgress2(Localization.Core.Hashing_sector_0_of_1, (long)doneSectors, (long)sectors);
doneSectors += sectorsToRead;
}
else
{
errno = image.ReadSectors(doneSectors, (uint)(sectors - doneSectors), out sector);
if(errno != ErrorNumber.NoError)
{
UpdateStatus(string.Format(Localization.Core.Error_0_reading_sector_1, errno, doneSectors));
EndProgress2();
return;
}
UpdateProgress2(Localization.Core.Hashing_sector_0_of_1, (long)doneSectors, (long)sectors);
doneSectors += sectors - doneSectors;
}
contentChkWorker.Update(sector);
}
// For fast debugging, skip checksum
//skipImageChecksum:
sidecar.BlockMedias[0].ContentChecksums = contentChkWorker.End();
EndProgress2();
}
(string type, string subType) diskType = MediaType.MediaTypeToString(image.Info.MediaType);
sidecar.BlockMedias[0].MediaType = diskType.type;
sidecar.BlockMedias[0].MediaSubType = diskType.subType;
Statistics.AddMedia(image.Info.MediaType, false);
sidecar.BlockMedias[0].Dimensions = Dimensions.FromMediaType(image.Info.MediaType);
sidecar.BlockMedias[0].LogicalBlocks = image.Info.Sectors;
sidecar.BlockMedias[0].LogicalBlockSize = image.Info.SectorSize;
// TODO: Detect it
sidecar.BlockMedias[0].PhysicalBlockSize = image.Info.SectorSize;
if(image is ITapeImage { IsTape: true } tapeImage)
{
List<TapePartition> tapePartitions = new();
foreach(CommonTypes.Structs.TapePartition tapePartition in tapeImage.TapePartitions)
{
var thisPartition = new TapePartition
{
Image = sidecar.BlockMedias[0].Image,
Sequence = tapePartition.Number,
StartBlock = tapePartition.FirstBlock,
EndBlock = tapePartition.LastBlock
};
if(tapeImage.TapePartitions.Count == 1)
thisPartition.Checksums = sidecar.BlockMedias[0].ContentChecksums;
else
{
UpdateStatus(string.Format(Localization.Core.Hashing_partition_0, tapePartition.Number));
if(_aborted)
return;
var tapePartitionChk = new Checksum();
// For fast debugging, skip checksum
//goto skipImageChecksum;
const uint sectorsToRead = 64;
ulong sectors = tapePartition.LastBlock - tapePartition.FirstBlock + 1;
ulong doneSectors = 0;
InitProgress2();
while(doneSectors < sectors)
{
if(_aborted)
{
EndProgress2();
return;
}
byte[] sector;
if(sectors - doneSectors >= sectorsToRead)
{
errno = image.ReadSectors(tapePartition.FirstBlock + doneSectors, sectorsToRead,
out sector);
if(errno != ErrorNumber.NoError)
{
AaruConsole.ErrorWriteLine(string.Format(Localization.Core.Error_0_reading_sector_1,
errno,
tapePartition.FirstBlock + doneSectors));
EndProgress2();
return;
}
UpdateProgress2(Localization.Core.Hashing_blocks_0_of_1, (long)doneSectors, (long)sectors);
doneSectors += sectorsToRead;
}
else
{
errno = image.ReadSectors(tapePartition.FirstBlock + doneSectors,
(uint)(sectors - doneSectors), out sector);
if(errno != ErrorNumber.NoError)
{
AaruConsole.ErrorWriteLine(string.Format(Localization.Core.Error_0_reading_sector_1,
errno,
tapePartition.FirstBlock + doneSectors));
EndProgress2();
return;
}
UpdateProgress2(Localization.Core.Hashing_blocks_0_of_1, (long)doneSectors, (long)sectors);
doneSectors += sectors - doneSectors;
}
thisPartition.Size += (ulong)sector.LongLength;
tapePartitionChk.Update(sector);
}
// For fast debugging, skip checksum
//skipImageChecksum:
thisPartition.Checksums = tapePartitionChk.End();
EndProgress2();
}
List<TapeFile> filesInPartition = new();
foreach(CommonTypes.Structs.TapeFile tapeFile in
tapeImage.Files.Where(f => f.Partition == tapePartition.Number))
{
var thisFile = new TapeFile
{
Sequence = tapeFile.File,
StartBlock = tapeFile.FirstBlock,
EndBlock = tapeFile.LastBlock,
Image = sidecar.BlockMedias[0].Image,
Size = 0,
BlockSize = 0
};
if(tapeImage.Files.Count(f => f.Partition == tapePartition.Number) == 1)
{
thisFile.Checksums = thisPartition.Checksums;
thisFile.Size = thisPartition.Size;
}
else
{
UpdateStatus(string.Format(Localization.Core.Hashing_file_0, tapeFile.File));
if(_aborted)
return;
var tapeFileChk = new Checksum();
// For fast debugging, skip checksum
//goto skipImageChecksum;
const uint sectorsToRead = 64;
ulong sectors = tapeFile.LastBlock - tapeFile.FirstBlock + 1;
ulong doneSectors = 0;
InitProgress2();
while(doneSectors < sectors)
{
if(_aborted)
{
EndProgress2();
return;
}
byte[] sector;
if(sectors - doneSectors >= sectorsToRead)
{
errno = image.ReadSectors(tapeFile.FirstBlock + doneSectors, sectorsToRead, out sector);
if(errno != ErrorNumber.NoError)
{
AaruConsole.ErrorWriteLine(string.Format(Localization.Core.Error_0_reading_sector_1,
errno, tapeFile.FirstBlock + doneSectors));
EndProgress2();
return;
}
UpdateProgress2(Localization.Core.Hashing_blocks_0_of_1, (long)doneSectors,
(long)sectors);
doneSectors += sectorsToRead;
}
else
{
errno = image.ReadSectors(tapeFile.FirstBlock + doneSectors,
(uint)(sectors - doneSectors), out sector);
if(errno != ErrorNumber.NoError)
{
AaruConsole.ErrorWriteLine(string.Format(Localization.Core.Error_0_reading_sector_1,
errno, tapeFile.FirstBlock + doneSectors));
EndProgress2();
return;
}
UpdateProgress2(Localization.Core.Hashing_blocks_0_of_1, (long)doneSectors,
(long)sectors);
doneSectors += sectors - doneSectors;
}
if((ulong)sector.LongLength > thisFile.BlockSize)
thisFile.BlockSize = (ulong)sector.LongLength;
thisFile.Size += (ulong)sector.LongLength;
tapeFileChk.Update(sector);
}
// For fast debugging, skip checksum
//skipImageChecksum:
thisFile.Checksums = tapeFileChk.End();
EndProgress2();
}
filesInPartition.Add(thisFile);
}
thisPartition.Files = filesInPartition;
tapePartitions.Add(thisPartition);
}
sidecar.BlockMedias[0].TapeInformation = tapePartitions;
}
UpdateStatus(Localization.Core.Checking_filesystems);
if(_aborted)
return;
List<Partition> partitions = Partitions.GetAll(image);
Partitions.AddSchemesToStats(partitions);
sidecar.BlockMedias[0].FileSystemInformation = new List<CommonTypes.AaruMetadata.Partition>();
if(partitions.Count > 0)
{
foreach(Partition partition in partitions)
{
if(_aborted)
return;
var fsInfo = new CommonTypes.AaruMetadata.Partition
{
Description = partition.Description,
EndSector = partition.End,
Name = partition.Name,
Sequence = (uint)partition.Sequence,
StartSector = partition.Start,
Type = partition.Type
};
List<FileSystem> lstFs = new();
foreach(IFilesystem fs in plugins.Filesystems.Values)
{
try
{
if(_aborted)
return;
if(fs is null)
continue;
if(!fs.Identify(image, partition))
continue;
if(fs is IReadOnlyFilesystem rofs &&
rofs.Mount(image, partition, encoding, null, null) == ErrorNumber.NoError)
{
UpdateStatus(string.Format(Localization.Core.Mounting_0, rofs.Metadata.Type));
rofs.Metadata.Contents = Files(rofs);
lstFs.Add(rofs.Metadata);
Statistics.AddFilesystem(rofs.Metadata.Type);
rofs.Unmount();
}
else
{
fs.GetInformation(image, partition, encoding, out _, out FileSystem fsMetadata);
lstFs.Add(fsMetadata);
Statistics.AddFilesystem(fsMetadata.Type);
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
{
//AaruConsole.DebugWriteLine(MODULE_NAME, "Plugin {0} crashed", _plugin.Name);
}
}
if(lstFs.Count > 0)
fsInfo.FileSystems = lstFs;
sidecar.BlockMedias[0].FileSystemInformation.Add(fsInfo);
}
}
else
{
if(_aborted)
return;
var fsInfo = new CommonTypes.AaruMetadata.Partition
{
StartSector = 0,
EndSector = image.Info.Sectors - 1
};
var wholePart = new Partition
{
Name = Localization.Core.Whole_device,
Length = image.Info.Sectors,
Size = image.Info.Sectors * image.Info.SectorSize
};
List<FileSystem> lstFs = new();
foreach(IFilesystem fs in plugins.Filesystems.Values)
{
try
{
if(_aborted)
return;
if(fs is null)
continue;
if(!fs.Identify(image, wholePart))
continue;
if(fs is IReadOnlyFilesystem rofs &&
rofs.Mount(image, wholePart, encoding, null, null) == ErrorNumber.NoError)
{
UpdateStatus(string.Format(Localization.Core.Mounting_0, rofs.Metadata.Type));
rofs.Metadata.Contents = Files(rofs);
lstFs.Add(rofs.Metadata);
Statistics.AddFilesystem(rofs.Metadata.Type);
rofs.Unmount();
}
else
{
fs.GetInformation(image, wholePart, encoding, out _, out FileSystem fsMetadata);
lstFs.Add(fsMetadata);
Statistics.AddFilesystem(fsMetadata.Type);
}
}
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
catch
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
{
//AaruConsole.DebugWriteLine(MODULE_NAME, "Plugin {0} crashed", _plugin.Name);
}
}
if(lstFs.Count > 0)
fsInfo.FileSystems = lstFs;
sidecar.BlockMedias[0].FileSystemInformation.Add(fsInfo);
}
UpdateStatus(Localization.Core.Saving_metadata);
if(image.Info.Cylinders > 0 && image.Info is { Heads: > 0, SectorsPerTrack: > 0 })
{
sidecar.BlockMedias[0].Cylinders = image.Info.Cylinders;
sidecar.BlockMedias[0].Heads = (ushort)image.Info.Heads;
sidecar.BlockMedias[0].SectorsPerTrack = image.Info.SectorsPerTrack;
}
if(image.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
{
Identify.IdentifyDevice? ataId = null;
errno = image.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out buffer);
if(errno == ErrorNumber.NoError)
ataId = Identify.Decode(buffer);
switch(ataId)
{
case { CurrentCylinders: > 0, CurrentHeads: > 0, CurrentSectorsPerTrack: > 0 }:
sidecar.BlockMedias[0].Cylinders = ataId.Value.CurrentCylinders;
sidecar.BlockMedias[0].Heads = ataId.Value.CurrentHeads;
sidecar.BlockMedias[0].SectorsPerTrack = ataId.Value.CurrentSectorsPerTrack;
break;
case { Cylinders: > 0, Heads: > 0, SectorsPerTrack: > 0 }:
sidecar.BlockMedias[0].Cylinders = ataId.Value.Cylinders;
sidecar.BlockMedias[0].Heads = ataId.Value.Heads;
sidecar.BlockMedias[0].SectorsPerTrack = ataId.Value.SectorsPerTrack;
break;
}
}
sidecar.BlockMedias[0].DumpHardware = image.DumpHardware;
// TODO: This is more of a hack, redo it planned for >4.0
string trkFormat = null;
switch(image.Info.MediaType)
{
case CommonTypes.MediaType.Apple32SS:
case CommonTypes.MediaType.Apple32DS:
trkFormat = "Apple GCR (DOS 3.2)";
break;
case CommonTypes.MediaType.Apple33SS:
case CommonTypes.MediaType.Apple33DS:
trkFormat = "Apple GCR (DOS 3.3)";
break;
case CommonTypes.MediaType.AppleSonySS:
case CommonTypes.MediaType.AppleSonyDS:
trkFormat = "Apple GCR (Sony)";
break;
case CommonTypes.MediaType.AppleFileWare:
trkFormat = "Apple GCR (Twiggy)";
break;
case CommonTypes.MediaType.DOS_525_SS_DD_9:
case CommonTypes.MediaType.DOS_525_DS_DD_8:
case CommonTypes.MediaType.DOS_525_DS_DD_9:
case CommonTypes.MediaType.DOS_525_HD:
case CommonTypes.MediaType.DOS_35_SS_DD_8:
case CommonTypes.MediaType.DOS_35_SS_DD_9:
case CommonTypes.MediaType.DOS_35_DS_DD_8:
case CommonTypes.MediaType.DOS_35_DS_DD_9:
case CommonTypes.MediaType.DOS_35_HD:
case CommonTypes.MediaType.DOS_35_ED:
case CommonTypes.MediaType.DMF:
case CommonTypes.MediaType.DMF_82:
case CommonTypes.MediaType.XDF_525:
case CommonTypes.MediaType.XDF_35:
case CommonTypes.MediaType.IBM53FD_256:
case CommonTypes.MediaType.IBM53FD_512:
case CommonTypes.MediaType.IBM53FD_1024:
case CommonTypes.MediaType.RX02:
case CommonTypes.MediaType.RX03:
case CommonTypes.MediaType.RX50:
case CommonTypes.MediaType.ACORN_525_SS_DD_40:
case CommonTypes.MediaType.ACORN_525_SS_DD_80:
case CommonTypes.MediaType.ACORN_525_DS_DD:
case CommonTypes.MediaType.ACORN_35_DS_DD:
case CommonTypes.MediaType.ACORN_35_DS_HD:
case CommonTypes.MediaType.ATARI_525_ED:
case CommonTypes.MediaType.ATARI_525_DD:
case CommonTypes.MediaType.ATARI_35_SS_DD:
case CommonTypes.MediaType.ATARI_35_DS_DD:
case CommonTypes.MediaType.ATARI_35_SS_DD_11:
case CommonTypes.MediaType.ATARI_35_DS_DD_11:
case CommonTypes.MediaType.DOS_525_SS_DD_8:
case CommonTypes.MediaType.NEC_8_DD:
case CommonTypes.MediaType.NEC_525_SS:
case CommonTypes.MediaType.NEC_525_DS:
case CommonTypes.MediaType.NEC_525_HD:
case CommonTypes.MediaType.NEC_35_HD_8:
case CommonTypes.MediaType.NEC_35_HD_15:
case CommonTypes.MediaType.NEC_35_TD:
case CommonTypes.MediaType.FDFORMAT_525_DD:
case CommonTypes.MediaType.FDFORMAT_525_HD:
case CommonTypes.MediaType.FDFORMAT_35_DD:
case CommonTypes.MediaType.FDFORMAT_35_HD:
case CommonTypes.MediaType.Apricot_35:
case CommonTypes.MediaType.CompactFloppy:
case CommonTypes.MediaType.MetaFloppy_Mod_I:
case CommonTypes.MediaType.MetaFloppy_Mod_II:
trkFormat = "IBM MFM";
break;
case CommonTypes.MediaType.ATARI_525_SD:
case CommonTypes.MediaType.NEC_8_SD:
case CommonTypes.MediaType.ACORN_525_SS_SD_40:
case CommonTypes.MediaType.ACORN_525_SS_SD_80:
case CommonTypes.MediaType.RX01:
case CommonTypes.MediaType.IBM23FD:
case CommonTypes.MediaType.IBM33FD_128:
case CommonTypes.MediaType.IBM33FD_256:
case CommonTypes.MediaType.IBM33FD_512:
case CommonTypes.MediaType.IBM43FD_128:
case CommonTypes.MediaType.IBM43FD_256:
trkFormat = "IBM FM";
break;
case CommonTypes.MediaType.CBM_35_DD:
trkFormat = "Commodore MFM";
break;
case CommonTypes.MediaType.CBM_AMIGA_35_HD:
case CommonTypes.MediaType.CBM_AMIGA_35_DD:
trkFormat = "Amiga MFM";
break;
case CommonTypes.MediaType.CBM_1540:
case CommonTypes.MediaType.CBM_1540_Ext:
case CommonTypes.MediaType.CBM_1571:
trkFormat = "Commodore GCR";
break;
case CommonTypes.MediaType.SHARP_525_9:
case CommonTypes.MediaType.SHARP_35_9:
break;
case CommonTypes.MediaType.ECMA_99_15:
case CommonTypes.MediaType.ECMA_99_26:
case CommonTypes.MediaType.ECMA_99_8:
trkFormat = "ISO MFM";
break;
case CommonTypes.MediaType.ECMA_54:
case CommonTypes.MediaType.ECMA_59:
case CommonTypes.MediaType.ECMA_66:
case CommonTypes.MediaType.ECMA_69_8:
case CommonTypes.MediaType.ECMA_69_15:
case CommonTypes.MediaType.ECMA_69_26:
case CommonTypes.MediaType.ECMA_70:
case CommonTypes.MediaType.ECMA_78:
case CommonTypes.MediaType.ECMA_78_2:
trkFormat = "ISO FM";
break;
default:
trkFormat = "Unknown";
break;
}
#region SuperCardPro
string scpFilePath = Path.Combine(Path.GetDirectoryName(imagePath),
Path.GetFileNameWithoutExtension(imagePath) + ".scp");
if(_aborted)
return;
if(File.Exists(scpFilePath))
{
UpdateStatus(Localization.Core.Hashing_SuperCardPro_image);
var scpImage = new SuperCardPro();
var scpFilter = new ZZZNoFilter();
scpFilter.Open(scpFilePath);
if(image.Info.Heads <= 2 && scpImage.Identify(scpFilter))
{
try
{
scpImage.Open(scpFilter);
}
catch(NotImplementedException) {}
if(image.Info.Heads == 2 && scpImage.Header.heads == 0 ||
image.Info.Heads == 1 && scpImage.Header.heads is 1 or 2)
{
if(scpImage.Header.end + 1 >= image.Info.Cylinders)
{
List<BlockTrack> scpBlockTrackTypes = new();
ulong currentSector = 0;
Stream scpStream = scpFilter.GetDataForkStream();
for(byte t = scpImage.Header.start; t <= scpImage.Header.end; t++)
{
if(_aborted)
return;
var scpBlockTrackType = new BlockTrack
{
Cylinder = t / image.Info.Heads,
Head = (ushort)(t % image.Info.Heads),
Image = new Image
{
Format = scpImage.Format,
Value = Path.GetFileName(scpFilePath),
Offset = scpImage.Header.offsets[t]
}
};
if(scpBlockTrackType.Cylinder < image.Info.Cylinders)
{
scpBlockTrackType.StartSector = currentSector;
currentSector += image.Info.SectorsPerTrack;
scpBlockTrackType.EndSector = currentSector - 1;
scpBlockTrackType.Sectors = image.Info.SectorsPerTrack;
scpBlockTrackType.BytesPerSector = image.Info.SectorSize;
scpBlockTrackType.Format = trkFormat;
}
if(scpImage.ScpTracks.TryGetValue(t, out SuperCardPro.TrackHeader scpTrack))
{
var trackContents =
new byte[scpTrack.Entries.Last().dataOffset +
scpTrack.Entries.Last().trackLength -
scpImage.Header.offsets[t] +
1];
scpStream.Position = scpImage.Header.offsets[t];
scpStream.EnsureRead(trackContents, 0, trackContents.Length);
scpBlockTrackType.Size = (ulong)trackContents.Length;
scpBlockTrackType.Checksums = Checksum.GetChecksums(trackContents);
}
scpBlockTrackTypes.Add(scpBlockTrackType);
}
sidecar.BlockMedias[0].Track = scpBlockTrackTypes.OrderBy(t => t.Cylinder).
ThenBy(t => t.Head).
ToList();
}
else
{
AaruConsole.
ErrorWriteLine(Localization.Core.SCP_image_do_not_same_number_tracks_0_disk_image_1_ignoring,
scpImage.Header.end + 1, image.Info.Cylinders);
}
}
else
{
AaruConsole.
ErrorWriteLine(Localization.Core.SCP_image_do_not_same_number_heads_0_disk_image_1_ignoring, 2,
image.Info.Heads);
}
}
}
#endregion
#region KryoFlux
string kfFile = null;
string basename = Path.Combine(Path.GetDirectoryName(imagePath), Path.GetFileNameWithoutExtension(imagePath));
var kfDir = false;
if(_aborted)
return;
if(Directory.Exists(basename))
{
string[] possibleKfStarts = Directory.GetFiles(basename, "*.raw", SearchOption.TopDirectoryOnly);
if(possibleKfStarts.Length > 0)
{
kfFile = possibleKfStarts[0];
kfDir = true;
}
}
else if(File.Exists(basename + "00.0.raw"))
kfFile = basename + "00.0.raw";
else if(File.Exists(basename + "00.1.raw"))
kfFile = basename + "00.1.raw";
if(kfFile != null)
{
UpdateStatus(Localization.Core.Hashing_KryoFlux_images);
var kfImage = new KryoFlux();
var kfFilter = new ZZZNoFilter();
kfFilter.Open(kfFile);
if(image.Info.Heads <= 2 && kfImage.Identify(kfFilter))
{
try
{
kfImage.Open(kfFilter);
}
catch(NotImplementedException) {}
if(kfImage.Info.Heads == image.Info.Heads)
{
if(kfImage.Info.Cylinders >= image.Info.Cylinders)
{
List<BlockTrack> kfBlockTrackTypes = new();
ulong currentSector = 0;
foreach(KeyValuePair<byte, IFilter> kvp in kfImage.tracks)
{
if(_aborted)
return;
var kfBlockTrackType = new BlockTrack
{
Cylinder = kvp.Key / image.Info.Heads,
Head = (ushort)(kvp.Key % image.Info.Heads),
Image = new Image
{
Format = kfImage.Format,
Value = kfDir
? Path.
Combine(Path.GetFileName(Path.GetDirectoryName(kvp.Value.BasePath)),
kvp.Value.Filename)
: kvp.Value.Filename,
Offset = 0
}
};
if(kfBlockTrackType.Cylinder < image.Info.Cylinders)
{
kfBlockTrackType.StartSector = currentSector;
currentSector += image.Info.SectorsPerTrack;
kfBlockTrackType.EndSector = currentSector - 1;
kfBlockTrackType.Sectors = image.Info.SectorsPerTrack;
kfBlockTrackType.BytesPerSector = image.Info.SectorSize;
kfBlockTrackType.Format = trkFormat;
}
Stream kfStream = kvp.Value.GetDataForkStream();
var trackContents = new byte[kfStream.Length];
kfStream.Position = 0;
kfStream.EnsureRead(trackContents, 0, trackContents.Length);
kfBlockTrackType.Size = (ulong)trackContents.Length;
kfBlockTrackType.Checksums = Checksum.GetChecksums(trackContents);
kfBlockTrackTypes.Add(kfBlockTrackType);
}
sidecar.BlockMedias[0].Track = kfBlockTrackTypes.OrderBy(t => t.Cylinder).
ThenBy(t => t.Head).
ToList();
}
else
{
AaruConsole.
ErrorWriteLine(Localization.Core.KryoFlux_image_do_not_same_number_tracks_0_disk_image_1_ignoring,
kfImage.Info.Cylinders, image.Info.Cylinders);
}
}
else
{
AaruConsole.
ErrorWriteLine(Localization.Core.KryoFlux_image_do_not_same_number_heads_0_disk_image_1_ignoring,
kfImage.Info.Heads, image.Info.Heads);
}
}
}
#endregion
#region DiscFerret
string dfiFilePath = Path.Combine(Path.GetDirectoryName(imagePath),
Path.GetFileNameWithoutExtension(imagePath) + ".dfi");
if(_aborted)
return;
if(!File.Exists(dfiFilePath))
return;
var dfiImage = new DiscFerret();
var dfiFilter = new ZZZNoFilter();
dfiFilter.Open(dfiFilePath);
if(!dfiImage.Identify(dfiFilter))
return;
try
{
dfiImage.Open(dfiFilter);
}
catch(NotImplementedException) {}
UpdateStatus(Localization.Core.Hashing_DiscFerret_image);
if(image.Info.Heads == dfiImage.Info.Heads)
{
if(dfiImage.Info.Cylinders >= image.Info.Cylinders)
{
List<BlockTrack> dfiBlockTrackTypes = new();
ulong currentSector = 0;
Stream dfiStream = dfiFilter.GetDataForkStream();
foreach(int t in dfiImage.TrackOffsets.Keys)
{
if(_aborted)
return;
var dfiBlockTrackType = new BlockTrack
{
Cylinder = (uint)(t / image.Info.Heads),
Head = (ushort)(t % image.Info.Heads),
Image = new Image
{
Format = dfiImage.Format,
Value = Path.GetFileName(dfiFilePath)
}
};
if(dfiBlockTrackType.Cylinder < image.Info.Cylinders)
{
dfiBlockTrackType.StartSector = currentSector;
currentSector += image.Info.SectorsPerTrack;
dfiBlockTrackType.EndSector = currentSector - 1;
dfiBlockTrackType.Sectors = image.Info.SectorsPerTrack;
dfiBlockTrackType.BytesPerSector = image.Info.SectorSize;
dfiBlockTrackType.Format = trkFormat;
}
if(dfiImage.TrackOffsets.TryGetValue(t, out long offset) &&
dfiImage.TrackLengths.TryGetValue(t, out long length))
{
dfiBlockTrackType.Image.Offset = (ulong)offset;
var trackContents = new byte[length];
dfiStream.Position = offset;
dfiStream.EnsureRead(trackContents, 0, trackContents.Length);
dfiBlockTrackType.Size = (ulong)trackContents.Length;
dfiBlockTrackType.Checksums = Checksum.GetChecksums(trackContents);
}
dfiBlockTrackTypes.Add(dfiBlockTrackType);
}
sidecar.BlockMedias[0].Track = dfiBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToList();
}
else
{
AaruConsole.
ErrorWriteLine(Localization.Core.DiscFerret_image_do_not_same_number_tracks_0_disk_image_1_ignoring,
dfiImage.Info.Cylinders, image.Info.Cylinders);
}
}
else
{
AaruConsole.
ErrorWriteLine(Localization.Core.DiscFerret_image_do_not_same_number_heads_0_disk_image_1_ignoring,
dfiImage.Info.Heads, image.Info.Heads);
}
#endregion
// TODO: Implement support for getting CHS from SCSI mode pages
}
}