// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : BlockMedia.cs // Author(s) : Natalia Portillo // // 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 . // // ---------------------------------------------------------------------------- // 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 { /// Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick) /// Image /// Filter uuid /// Image path /// Image file information /// Image plugins /// List of image checksums /// Metadata sidecar /// Encoding to be used for filesystem plugins void BlockMedia(IMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginRegister plugins, List imgChecksums, ref Metadata sidecar, Encoding encoding) { if(_aborted) return; sidecar.BlockMedias = new List { 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(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 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 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 partitions = Partitions.GetAll(image); Partitions.AddSchemesToStats(partitions); sidecar.BlockMedias[0].FileSystemInformation = new List(); 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 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 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 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 kfBlockTrackTypes = new(); ulong currentSector = 0; foreach(KeyValuePair 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 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 } }