diff --git a/DiscImageChef.Core/Sidecar/BlockMedia.cs b/DiscImageChef.Core/Sidecar/BlockMedia.cs index 56f391a7..73b1be0b 100644 --- a/DiscImageChef.Core/Sidecar/BlockMedia.cs +++ b/DiscImageChef.Core/Sidecar/BlockMedia.cs @@ -713,6 +713,82 @@ namespace DiscImageChef.Core } #endregion + #region DiscFerret + string dfiFilePath = Path.Combine(Path.GetDirectoryName(imagePath), + Path.GetFileNameWithoutExtension(imagePath) + ".dfi"); + + if(File.Exists(dfiFilePath)) + { + ImagePlugins.DiscFerret dfiImage = new DiscFerret(); + Filters.ZZZNoFilter dfiFilter = new ZZZNoFilter(); + dfiFilter.Open(dfiFilePath); + + if(dfiImage.IdentifyImage(dfiFilter)) + { + + try + { + dfiImage.OpenImage(dfiFilter); + } + catch(NotImplementedException) + { + } + + if(image.ImageInfo.heads == dfiImage.ImageInfo.heads) + { + if(dfiImage.ImageInfo.cylinders >= image.ImageInfo.cylinders) + { + List dfiBlockTrackTypes = new List(); + long currentSector = 0; + Stream dfiStream = dfiFilter.GetDataForkStream(); + + foreach(int t in dfiImage.trackOffsets.Keys) + { + BlockTrackType dfiBlockTrackType = new BlockTrackType(); + dfiBlockTrackType.Cylinder = t / image.ImageInfo.heads; + dfiBlockTrackType.Head = t % image.ImageInfo.heads; + dfiBlockTrackType.Image = new ImageType(); + dfiBlockTrackType.Image.format = dfiImage.GetImageFormat(); + dfiBlockTrackType.Image.Value = Path.GetFileName(dfiFilePath); + + if(dfiBlockTrackType.Cylinder < image.ImageInfo.cylinders) + { + dfiBlockTrackType.StartSector = currentSector; + currentSector += image.ImageInfo.sectorsPerTrack; + dfiBlockTrackType.EndSector = currentSector - 1; + dfiBlockTrackType.Sectors = image.ImageInfo.sectorsPerTrack; + dfiBlockTrackType.BytesPerSector = (int)image.ImageInfo.sectorSize; + dfiBlockTrackType.Format = trkFormat; + } + + if(dfiImage.trackOffsets.TryGetValue(t, out long offset) && dfiImage.trackLengths.TryGetValue(t, out long length)) + { + dfiBlockTrackType.Image.offset = offset; + byte[] trackContents = new byte[length]; + dfiStream.Position = offset; + dfiStream.Read(trackContents, 0, trackContents.Length); + dfiBlockTrackType.Size = trackContents.Length; + dfiBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); + } + + dfiBlockTrackTypes.Add(dfiBlockTrackType); + } + + sidecar.BlockMedia[0].Track = + dfiBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToArray(); + } + else + DicConsole.ErrorWriteLine( + "DiscFerret image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + dfiImage.ImageInfo.cylinders, image.ImageInfo.cylinders); + } + else + DicConsole.ErrorWriteLine( + "DiscFerret image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + dfiImage.ImageInfo.heads, image.ImageInfo.heads); + } + } + #endregion // TODO: Implement support for getting CHS from SCSI mode pages } } diff --git a/DiscImageChef.DiscImages/DiscFerret.cs b/DiscImageChef.DiscImages/DiscFerret.cs new file mode 100644 index 00000000..6c115d37 --- /dev/null +++ b/DiscImageChef.DiscImages/DiscFerret.cs @@ -0,0 +1,430 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : DiscFerret.cs +// Author(s) : Natalia Portillo +// +// Component : Disc image plugins. +// +// --[ Description ] ---------------------------------------------------------- +// +// Manages DiscFerret disk images. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2017 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using DiscImageChef.CommonTypes; +using DiscImageChef.Console; +using DiscImageChef.Filters; + +namespace DiscImageChef.ImagePlugins +{ + public class DiscFerret : ImagePlugin + { + #region Internal Structures + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct DfiBlockHeader + { + public ushort cylinder; + public ushort head; + public ushort sector; + public uint length; + } + #endregion Internal Structures + + #region Internal Constants + /// + /// "DFER" + /// + const uint DfiMagic = 0x52454644; + /// + /// "DFE2" + /// + const uint DfiMagic2 = 0x32454644; + #endregion Internal Constants + + #region Internal variables + // TODO: These variables have been made public so create-sidecar can access to this information until I define an API >4.0 + public SortedDictionary trackOffsets; + public SortedDictionary trackLengths; + #endregion Internal variables + + public DiscFerret() + { + Name = "DiscFerret"; + PluginUUID = new Guid("70EA7B9B-5323-42EB-9B40-8DDA37C5EB4D"); + ImageInfo = new ImageInfo() + { + readableSectorTags = new List(), + readableMediaTags = new List(), + imageHasPartitions = false, + imageHasSessions = false, + imageVersion = null, + imageApplication = null, + imageApplicationVersion = null, + imageCreator = null, + imageComments = null, + mediaManufacturer = null, + mediaModel = null, + mediaSerialNumber = null, + mediaBarcode = null, + mediaPartNumber = null, + mediaSequence = 0, + lastMediaSequence = 0, + driveManufacturer = null, + driveModel = null, + driveSerialNumber = null, + driveFirmwareRevision = null + }; + } + + #region Public methods + public override bool IdentifyImage(Filter imageFilter) + { + byte[] magic_b = new byte[4]; + Stream stream = imageFilter.GetDataForkStream(); + stream.Read(magic_b, 0, 4); + uint magic = BitConverter.ToUInt32(magic_b, 0); + + return magic == DfiMagic || magic == DfiMagic2; + } + + public override bool OpenImage(Filter imageFilter) + { + byte[] magic_b = new byte[4]; + Stream stream = imageFilter.GetDataForkStream(); + stream.Read(magic_b, 0, 4); + uint magic = BitConverter.ToUInt32(magic_b, 0); + + if(magic != DfiMagic && magic != DfiMagic2) return false; + + trackOffsets = new SortedDictionary(); + trackLengths = new SortedDictionary(); + int t = -1; + ushort lastCylinder = 0, lastHead = 0; + bool endOfTrack = false; + long offset = 0; + + while(stream.Position < stream.Length) + { + long thisOffset = stream.Position; + + DfiBlockHeader blockHeader = new DfiBlockHeader(); + byte[] blk = new byte[Marshal.SizeOf(blockHeader)]; + stream.Read(blk, 0, Marshal.SizeOf(blockHeader)); + blockHeader = BigEndianMarshal.ByteArrayToStructureBigEndian(blk); + + DicConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.cylinder = {1}", thisOffset, + blockHeader.cylinder); + DicConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.head = {1}", thisOffset, blockHeader.head); + DicConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.sector = {1}", thisOffset, + blockHeader.sector); + DicConsole.DebugWriteLine("DiscFerret plugin", "block@{0}.length = {1}", thisOffset, + blockHeader.length); + + if(stream.Position + blockHeader.length > stream.Length) + { + DicConsole.DebugWriteLine("DiscFerret plugin", "Invalid track block found at {0}", thisOffset); + break; + } + + stream.Position += blockHeader.length; + + if(blockHeader.cylinder > 0 && blockHeader.cylinder > lastCylinder) + { + lastCylinder = blockHeader.cylinder; + lastHead = 0; + trackOffsets.Add(t, offset); + trackLengths.Add(t, thisOffset - offset + 1); + offset = thisOffset; + t++; + } + else if(blockHeader.head > 0 && blockHeader.head > lastHead) + { + lastHead = blockHeader.head; + trackOffsets.Add(t, offset); + trackLengths.Add(t, thisOffset - offset + 1); + offset = thisOffset; + t++; + } + + if(blockHeader.cylinder > ImageInfo.cylinders) + ImageInfo.cylinders = blockHeader.cylinder; + if(blockHeader.head> ImageInfo.heads) + ImageInfo.heads= blockHeader.head; + } + + ImageInfo.heads++; + ImageInfo.cylinders++; + + ImageInfo.imageApplication = "DiscFerret"; + if(magic == DfiMagic2) + ImageInfo.imageApplicationVersion = "2.0"; + else + ImageInfo.imageApplicationVersion = "1.0"; + + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override bool ImageHasPartitions() + { + return ImageInfo.imageHasPartitions; + } + + public override ulong GetImageSize() + { + return ImageInfo.imageSize; + } + + public override ulong GetSectors() + { + return ImageInfo.sectors; + } + + public override uint GetSectorSize() + { + return ImageInfo.sectorSize; + } + + public override byte[] ReadDiskTag(MediaTagType tag) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override byte[] ReadSector(ulong sectorAddress) + { + return ReadSectors(sectorAddress, 1); + } + + public override byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override byte[] ReadSectors(ulong sectorAddress, uint length) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override byte[] ReadSectorLong(ulong sectorAddress) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override byte[] ReadSectorLong(ulong sectorAddress, uint track) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override byte[] ReadSectorsLong(ulong sectorAddress, uint length) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override string GetImageFormat() + { + return "DiscFerret"; + } + + public override string GetImageVersion() + { + return ImageInfo.imageVersion; + } + + public override string GetImageApplication() + { + return ImageInfo.imageApplication; + } + + public override string GetImageApplicationVersion() + { + return ImageInfo.imageApplicationVersion; + } + + public override string GetImageCreator() + { + return ImageInfo.imageCreator; + } + + public override DateTime GetImageCreationTime() + { + return ImageInfo.imageCreationTime; + } + + public override DateTime GetImageLastModificationTime() + { + return ImageInfo.imageLastModificationTime; + } + + public override string GetImageName() + { + return ImageInfo.imageName; + } + + public override string GetImageComments() + { + return ImageInfo.imageComments; + } + + public override string GetMediaManufacturer() + { + return ImageInfo.mediaManufacturer; + } + + public override string GetMediaModel() + { + return ImageInfo.mediaModel; + } + + public override string GetMediaSerialNumber() + { + return ImageInfo.mediaSerialNumber; + } + + public override string GetMediaBarcode() + { + return ImageInfo.mediaBarcode; + } + + public override string GetMediaPartNumber() + { + return ImageInfo.mediaPartNumber; + } + + public override MediaType GetMediaType() + { + return ImageInfo.mediaType; + } + + public override int GetMediaSequence() + { + return ImageInfo.mediaSequence; + } + + public override int GetLastDiskSequence() + { + return ImageInfo.lastMediaSequence; + } + + public override string GetDriveManufacturer() + { + return ImageInfo.driveManufacturer; + } + + public override string GetDriveModel() + { + return ImageInfo.driveModel; + } + + public override string GetDriveSerialNumber() + { + return ImageInfo.driveSerialNumber; + } + + public override bool? VerifySector(ulong sectorAddress) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + + public override bool? VerifySectors(ulong sectorAddress, uint length, out List FailingLBAs, out List UnknownLBAs) + { + throw new NotImplementedException("Flux decoding is not yet implemented."); + } + #endregion Public methods + + #region Unsupported features + public override byte[] ReadSector(ulong sectorAddress, uint track) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + public override byte[] ReadSectors(ulong sectorAddress, uint length, uint track) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override List GetPartitions() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override List GetTracks() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override List GetSessionTracks(Session session) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override List GetSessionTracks(ushort session) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override List GetSessions() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override bool? VerifySector(ulong sectorAddress, uint track) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List FailingLBAs, out List UnknownLBAs) + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override bool? VerifyMediaImage() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + #endregion Unsupported features + } +} \ No newline at end of file diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj index 036108d9..e8b81c95 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj +++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj @@ -51,6 +51,7 @@ + diff --git a/README.md b/README.md index 9147f328..1782caa9 100644 --- a/README.md +++ b/README.md @@ -215,5 +215,6 @@ Supported filters Partially supported disk image formats ====================================== This disk image formats cannot be read, but their contents can be checksummed on sidecar creation +* DiscFerret * KryoFlux STREAM * SuperCardPro \ No newline at end of file