From 3a03e3121ef54f9bf61fbfc8d6aafbedee42a846 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 25 Aug 2014 05:00:25 +0100 Subject: [PATCH] Implements verification on all currently supported disk images. Implements DC42 CRC calculation. Calculates TeleDisk CRC for disk sectors. Resolves #2 and bumps version to 2.1. --- DiscImageChef.sln | 2 +- DiscImageChef/AssemblyInfo.cs | 2 +- DiscImageChef/Commands/Verify.cs | 208 +++++++++++++++++++++- DiscImageChef/DiscImageChef.csproj | 2 +- DiscImageChef/ImagePlugins/CDRWin.cs | 126 ++++++++++--- DiscImageChef/ImagePlugins/DiskCopy42.cs | 96 ++++++++++ DiscImageChef/ImagePlugins/ImagePlugin.cs | 66 +++++-- DiscImageChef/ImagePlugins/Nero.cs | 81 ++++++++- DiscImageChef/ImagePlugins/TeleDisk.cs | 193 +++++++++++++------- DiscImageChef/ImagePlugins/ZZZRawImage.cs | 41 +++++ Packages.mdproj | 2 +- README.md | 6 +- TODO | 3 - 13 files changed, 721 insertions(+), 107 deletions(-) diff --git a/DiscImageChef.sln b/DiscImageChef.sln index dfacf02f9..ccda734c5 100644 --- a/DiscImageChef.sln +++ b/DiscImageChef.sln @@ -27,6 +27,6 @@ Global GlobalSection(MonoDevelopProperties) = preSolution StartupItem = DiscImageChef\DiscImageChef.csproj description = The Disc Image Chef. - version = 2.0 + version = 2.1 EndGlobalSection EndGlobal diff --git a/DiscImageChef/AssemblyInfo.cs b/DiscImageChef/AssemblyInfo.cs index 927a2155c..69818c962 100644 --- a/DiscImageChef/AssemblyInfo.cs +++ b/DiscImageChef/AssemblyInfo.cs @@ -54,7 +54,7 @@ using System.Reflection; // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion("2.0.*")] +[assembly: AssemblyVersion("2.1.*")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. diff --git a/DiscImageChef/Commands/Verify.cs b/DiscImageChef/Commands/Verify.cs index c4cc0286c..18defc0bb 100644 --- a/DiscImageChef/Commands/Verify.cs +++ b/DiscImageChef/Commands/Verify.cs @@ -36,6 +36,8 @@ Copyright (C) 2011-2014 Claunia.com ****************************************************************************/ //$Id$ using System; +using DiscImageChef.ImagePlugins; +using System.Collections.Generic; namespace DiscImageChef.Commands { @@ -51,7 +53,211 @@ namespace DiscImageChef.Commands Console.WriteLine("--verify-disc={0}", options.VerifyDisc); Console.WriteLine("--verify-sectors={0}", options.VerifySectors); } - Console.WriteLine("Verifying not yet implemented."); return; + + ImagePlugin inputFormat = ImageFormat.Detect(options.InputFile); + + if (inputFormat == null) + { + Console.WriteLine("Unable to recognize image format, not verifying"); + return; + } + + inputFormat.OpenImage(options.InputFile); + + if (options.VerifyDisc) + { + DateTime StartCheck = DateTime.UtcNow; + bool? discCheckStatus = inputFormat.VerifyDiskImage(); + DateTime EndCheck = DateTime.UtcNow; + + TimeSpan CheckTime = EndCheck - StartCheck; + + switch (discCheckStatus) + { + case true: + Console.WriteLine("Disc image checksums are correct"); + break; + case false: + Console.WriteLine("Disc image checksums are incorrect"); + break; + case null: + Console.WriteLine("Disc image does not contain checksums"); + break; + } + + if (MainClass.isVerbose) + Console.WriteLine("Checking disc image checksums took {0} seconds", CheckTime.TotalSeconds); + } + + if (options.VerifySectors) + { + bool formatHasTracks; + try + { + List inputTracks = inputFormat.GetTracks(); + if (inputTracks.Count > 0) + formatHasTracks = true; + else + formatHasTracks = false; + } + catch + { + formatHasTracks = false; + } + + DateTime StartCheck; + DateTime EndCheck; + List FailingLBAs = new List(); + List UnknownLBAs = new List(); + bool? checkStatus = null; + + if (formatHasTracks) + { + List inputTracks = inputFormat.GetTracks(); + UInt64 currentSectorAll = 0; + + StartCheck = DateTime.UtcNow; + foreach (Track currentTrack in inputTracks) + { + UInt64 remainingSectors = currentTrack.TrackEndSector - currentTrack.TrackStartSector; + UInt64 currentSector = 0; + + while (remainingSectors > 0) + { + Console.Write("\rChecking sector {0} of {1}, on track {2}", currentSectorAll, inputFormat.GetSectors(), currentTrack.TrackSequence); + + List tempFailingLBAs; + List tempUnknownLBAs; + bool? tempStatus; + + if (remainingSectors < 512) + tempStatus = inputFormat.VerifySectors(currentSector, (uint)remainingSectors, currentTrack.TrackSequence, out tempFailingLBAs, out tempUnknownLBAs); + else + tempStatus = inputFormat.VerifySectors(currentSector, 512, currentTrack.TrackSequence, out tempFailingLBAs, out tempUnknownLBAs); + + if (checkStatus == null || tempStatus == null) + checkStatus = null; + else if (checkStatus == false || tempStatus == false) + checkStatus = false; + else if (checkStatus == true && tempStatus == true) + checkStatus = true; + else + checkStatus = null; + + foreach (UInt64 failLBA in tempFailingLBAs) + FailingLBAs.Add(failLBA); + + foreach (UInt64 unknownLBA in tempUnknownLBAs) + UnknownLBAs.Add(unknownLBA); + + if (remainingSectors < 512) + { + currentSector += remainingSectors; + currentSectorAll += remainingSectors; + remainingSectors = 0; + } + else + { + currentSector += 512; + currentSectorAll += 512; + remainingSectors -= 512; + } + + } + } + EndCheck = DateTime.UtcNow; + } + else + { + UInt64 remainingSectors = inputFormat.GetSectors(); + UInt64 currentSector = 0; + + StartCheck = DateTime.UtcNow; + while (remainingSectors > 0) + { + Console.Write("\rChecking sector {0} of {1}", currentSector, inputFormat.GetSectors()); + + List tempFailingLBAs; + List tempUnknownLBAs; + bool? tempStatus; + + if (remainingSectors < 512) + tempStatus = inputFormat.VerifySectors(currentSector, (uint)remainingSectors, out tempFailingLBAs, out tempUnknownLBAs); + else + tempStatus = inputFormat.VerifySectors(currentSector, 512, out tempFailingLBAs, out tempUnknownLBAs); + + if (checkStatus == null || tempStatus == null) + checkStatus = null; + else if (checkStatus == false || tempStatus == false) + checkStatus = false; + else if (checkStatus == true && tempStatus == true) + checkStatus = true; + else + checkStatus = null; + + foreach (UInt64 failLBA in tempFailingLBAs) + FailingLBAs.Add(failLBA); + + foreach (UInt64 unknownLBA in tempUnknownLBAs) + UnknownLBAs.Add(unknownLBA); + + if (remainingSectors < 512) + { + currentSector += remainingSectors; + remainingSectors = 0; + } + else + { + currentSector += 512; + remainingSectors -= 512; + } + + } + EndCheck = DateTime.UtcNow; + } + + TimeSpan CheckTime = EndCheck - StartCheck; + + Console.Write("\r"); + + switch (checkStatus) + { + case true: + Console.WriteLine("All sector checksums are correct"); + break; + case false: + Console.WriteLine("There is at least one sector with incorrect checksum or errors"); + break; + case null: + Console.WriteLine("There is at least one sector that does not contain a checksum"); + break; + } + + if (MainClass.isVerbose) + Console.WriteLine("Checking sector checksums took {0} seconds", CheckTime.TotalSeconds); + + if (MainClass.isVerbose) + { + Console.WriteLine("LBAs with error:"); + if (FailingLBAs.Count == (int)inputFormat.GetSectors()) + Console.WriteLine("\tall sectors."); + else + for (int i = 0; i < FailingLBAs.Count; i++) + Console.WriteLine("\t{0}", FailingLBAs[i]); + + Console.WriteLine("LBAs without checksum:"); + if (UnknownLBAs.Count == (int)inputFormat.GetSectors()) + Console.WriteLine("\tall sectors."); + else + for (int i = 0; i < UnknownLBAs.Count; i++) + Console.WriteLine("\t{0}", UnknownLBAs[i]); + } + + Console.WriteLine("Total sectors........... {0}", inputFormat.GetSectors()); + Console.WriteLine("Total errors............ {0}", FailingLBAs.Count); + Console.WriteLine("Total unknowns.......... {0}", UnknownLBAs.Count); + Console.WriteLine("Total errors+unknowns... {0}", FailingLBAs.Count + UnknownLBAs.Count); + } } } } diff --git a/DiscImageChef/DiscImageChef.csproj b/DiscImageChef/DiscImageChef.csproj index 9bcb39383..1851282c4 100644 --- a/DiscImageChef/DiscImageChef.csproj +++ b/DiscImageChef/DiscImageChef.csproj @@ -10,7 +10,7 @@ DiscImageChef DiscImageChef v3.5 - 2.0 + 2.1 True diff --git a/DiscImageChef/ImagePlugins/CDRWin.cs b/DiscImageChef/ImagePlugins/CDRWin.cs index e0856242a..3c9f37ede 100644 --- a/DiscImageChef/ImagePlugins/CDRWin.cs +++ b/DiscImageChef/ImagePlugins/CDRWin.cs @@ -1251,70 +1251,68 @@ namespace DiscImageChef.ImagePlugins _imageInfo.diskBarcode = discimage.barcode; _imageInfo.diskType = discimage.disktype; - foreach(CDRWinTrack track in discimage.tracks) + foreach (CDRWinTrack track in discimage.tracks) { - switch(track.tracktype) + switch (track.tracktype) { case CDRWinTrackTypeAudio: { - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) _imageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackFlags)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackFlags)) _imageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); break; } case CDRWinTrackTypeCDG: { - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) _imageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackFlags)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackFlags)) _imageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubchannel)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubchannel)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubchannel); break; } case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeCDI: { - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); break; } case CDRWinTrackTypeMode2Raw: case CDRWinTrackTypeCDIRaw: { - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorSync); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorHeader); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); break; } case CDRWinTrackTypeMode1Raw: { - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorSync); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorHeader); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorECC); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC_P)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC_P)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorECC_P); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC_Q)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC_Q)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorECC_Q); - if(!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) _imageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); break; } - default: - break; } } @@ -1928,6 +1926,8 @@ namespace DiscImageChef.ImagePlugins { List tracks = new List(); + UInt64 previousStartSector = 0; + foreach (CDRWinTrack cdr_track in discimage.tracks) { Track _track = new Track(); @@ -1936,6 +1936,7 @@ namespace DiscImageChef.ImagePlugins _track.TrackDescription = cdr_track.title; if (!cdr_track.indexes.TryGetValue(0, out _track.TrackStartSector)) cdr_track.indexes.TryGetValue(1, out _track.TrackStartSector); + _track.TrackStartSector += previousStartSector; _track.TrackEndSector = _track.TrackStartSector + cdr_track.sectors - 1; _track.TrackPregap = cdr_track.pregap; _track.TrackSession = cdr_track.session; @@ -1943,6 +1944,7 @@ namespace DiscImageChef.ImagePlugins _track.TrackType = CDRWinTrackTypeToTrackType(cdr_track.tracktype); tracks.Add(_track); + previousStartSector = _track.TrackEndSector + 1; } return tracks; @@ -1989,6 +1991,85 @@ namespace DiscImageChef.ImagePlugins return discimage.sessions; } + public override bool? VerifySector(UInt64 sectorAddress) + { + byte[] buffer = ReadSectorLong(sectorAddress); + return Checksums.CDChecksums.CheckCDSector(buffer); + } + + public override bool? VerifySector(UInt64 sectorAddress, UInt32 track) + { + byte[] buffer = ReadSectorLong(sectorAddress, track); + return Checksums.CDChecksums.CheckCDSector(buffer); + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List FailingLBAs, out List UnknownLBAs) + { + byte[] buffer = ReadSectorsLong(sectorAddress, length); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = Checksums.CDChecksums.CheckCDSector(sector); + + switch (sectorStatus) + { + case null: + UnknownLBAs.Add((ulong)i + sectorAddress); + break; + case false: + FailingLBAs.Add((ulong)i + sectorAddress); + break; + } + } + + if (UnknownLBAs.Count > 0) + return null; + if (FailingLBAs.Count > 0) + return false; + return true; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List FailingLBAs, out List UnknownLBAs) + { + byte[] buffer = ReadSectorsLong(sectorAddress, length, track); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = Checksums.CDChecksums.CheckCDSector(sector); + + switch (sectorStatus) + { + case null: + UnknownLBAs.Add((ulong)i + sectorAddress); + break; + case false: + FailingLBAs.Add((ulong)i + sectorAddress); + break; + } + } + + if (UnknownLBAs.Count > 0) + return null; + if (FailingLBAs.Count > 0) + return false; + return true; + } + + public override bool? VerifyDiskImage() + { + return null; + } + #endregion #region Private methods @@ -2168,6 +2249,7 @@ namespace DiscImageChef.ImagePlugins { return _imageInfo.imageCreator; } + #endregion } } diff --git a/DiscImageChef/ImagePlugins/DiskCopy42.cs b/DiscImageChef/ImagePlugins/DiskCopy42.cs index a8ec8cb94..2d2f173c5 100644 --- a/DiscImageChef/ImagePlugins/DiskCopy42.cs +++ b/DiscImageChef/ImagePlugins/DiskCopy42.cs @@ -373,6 +373,83 @@ namespace DiscImageChef.ImagePlugins return true; } + public override bool? VerifySector(UInt64 sectorAddress) + { + return null; + } + + public override bool? VerifySector(UInt64 sectorAddress, UInt32 track) + { + return null; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List FailingLBAs, out List UnknownLBAs) + { + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (UInt64 i = sectorAddress; i < sectorAddress + length; i++) + UnknownLBAs.Add(i); + + return null; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List FailingLBAs, out List UnknownLBAs) + { + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (UInt64 i = sectorAddress; i < sectorAddress + length; i++) + UnknownLBAs.Add(i); + + return null; + } + + public override bool? VerifyDiskImage() + { + byte[] data = new byte[header.dataSize]; + byte[] tags = new byte[header.tagSize]; + UInt32 dataChk; + UInt32 tagsChk = 0; + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (DC42 plugin): Reading data"); + FileStream datastream = new FileStream(dc42ImagePath, FileMode.Open, FileAccess.Read); + datastream.Seek((long)(dataOffset), SeekOrigin.Begin); + datastream.Read(data, 0, (int)header.dataSize); + datastream.Close(); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (DC42 plugin): Calculating data checksum"); + dataChk = DC42CheckSum(data); + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (DC42 plugin): Calculated data checksum = 0x{0:X8}", dataChk); + Console.WriteLine("DEBUG (DC42 plugin): Stored data checksum = 0x{0:X8}", header.dataChecksum); + } + + if (header.tagSize > 0) + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (DC42 plugin): Reading tags"); + FileStream tagstream = new FileStream(dc42ImagePath, FileMode.Open, FileAccess.Read); + tagstream.Seek((long)(tagOffset), SeekOrigin.Begin); + tagstream.Read(tags, 0, (int)header.tagSize); + tagstream.Close(); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (DC42 plugin): Calculating data checksum"); + tagsChk = DC42CheckSum(data); + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (DC42 plugin): Calculated data checksum = 0x{0:X8}", tagsChk); + Console.WriteLine("DEBUG (DC42 plugin): Stored data checksum = 0x{0:X8}", header.tagChecksum); + } + } + + return dataChk == header.dataChecksum && tagsChk == header.tagChecksum; + } + public override bool ImageHasPartitions() { return _imageInfo.imageHasPartitions; @@ -640,6 +717,25 @@ namespace DiscImageChef.ImagePlugins } #endregion Unsupported features + + #region Private methods + + private static UInt32 DC42CheckSum(byte[] buffer) + { + UInt32 dc42chk = 0; + if ((buffer.Length & 0x01) == 0x01) + return 0xFFFFFFFF; + + for (UInt32 i = 0; i < buffer.Length; i += 2) + { + dc42chk += (uint)(buffer[i] << 8); + dc42chk += buffer[i + 1]; + dc42chk = (dc42chk >> 1) | (dc42chk << 31); + } + return dc42chk; + } + + #endregion } } diff --git a/DiscImageChef/ImagePlugins/ImagePlugin.cs b/DiscImageChef/ImagePlugins/ImagePlugin.cs index f37382113..83d88fe9d 100644 --- a/DiscImageChef/ImagePlugins/ImagePlugin.cs +++ b/DiscImageChef/ImagePlugins/ImagePlugin.cs @@ -108,7 +108,6 @@ namespace DiscImageChef.ImagePlugins /// Tag type to read. public abstract byte[] ReadDiskTag(DiskTagType tag); - // Gets a disk tag /// /// Reads a sector's user data. /// @@ -370,7 +369,50 @@ namespace DiscImageChef.ImagePlugins /// /// The sessions. public abstract List GetSessions(); - // Returns disc sessions + + + /// + /// Verifies a sector. + /// + /// True if correct, false if incorrect, null if uncheckable. + /// Sector address (LBA). + public abstract bool? VerifySector(UInt64 sectorAddress); + + /// + /// Verifies a sector, relative to track. + /// + /// True if correct, false if incorrect, null if uncheckable. + /// Sector address (relative LBA). + /// Track. + public abstract bool? VerifySector(UInt64 sectorAddress, UInt32 track); + + /// + /// Verifies several sectors. + /// + /// True if all are correct, false if any is incorrect, null if any is uncheckable. + /// Starting sector address (LBA). + /// How many sectors to read. + /// List of incorrect sectors + /// List of uncheckable sectors + public abstract bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List FailingLBAs, out List UnknownLBAs); + + /// + /// Verifies several sectors, relative to track. + /// + /// True if all are correct, false if any is incorrect, null if any is uncheckable. + /// Starting sector address (relative LBA). + /// How many sectors to read. + /// Track. + /// List of incorrect sectors + /// List of uncheckable sectors + public abstract bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List FailingLBAs, out List UnknownLBAs); + + /// + /// Verifies disk image internal checksum. + /// + /// True if correct, false if incorrect, null if there is no internal checksum available + public abstract bool? VerifyDiskImage(); + // CD flags bitmask @@ -719,8 +761,9 @@ namespace DiscImageChef.ImagePlugins FDFORMAT_35_HD, // Generic hard disks - GENERIC_HDD - }; + GENERIC_HDD} + + ; /// /// Track (as partitioning element) types. @@ -738,8 +781,9 @@ namespace DiscImageChef.ImagePlugins /// Data track, compact disc mode 2, form 1 CDMode2Form1, /// Data track, compact disc mode 2, form 2 - CDMode2Form2 - }; + CDMode2Form2} + + ; /// /// Track defining structure. @@ -811,8 +855,9 @@ namespace DiscImageChef.ImagePlugins /// CD track flags, 1 byte CDTrackFlags, /// DVD sector copyright information - DVD_CMI - }; + DVD_CMI} + + ; /// /// Metadata present for each disk. @@ -834,8 +879,9 @@ namespace DiscImageChef.ImagePlugins /// DVD Copyright Management Information DVD_CMI, /// DVD Disc Manufacturer Information - DVD_DMI - }; + DVD_DMI} + + ; /// /// Feature is supported by image but not implemented yet. diff --git a/DiscImageChef/ImagePlugins/Nero.cs b/DiscImageChef/ImagePlugins/Nero.cs index 547a90f96..f74b93b30 100644 --- a/DiscImageChef/ImagePlugins/Nero.cs +++ b/DiscImageChef/ImagePlugins/Nero.cs @@ -2010,7 +2010,7 @@ namespace DiscImageChef.ImagePlugins } break; } - // TODO + // TODO case DAOMode.DataM2RawSub: throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); case DAOMode.DataRawSub: @@ -2291,6 +2291,85 @@ namespace DiscImageChef.ImagePlugins return imageSessions; } + public override bool? VerifySector(UInt64 sectorAddress) + { + byte[] buffer = ReadSectorLong(sectorAddress); + return Checksums.CDChecksums.CheckCDSector(buffer); + } + + public override bool? VerifySector(UInt64 sectorAddress, UInt32 track) + { + byte[] buffer = ReadSectorLong(sectorAddress, track); + return Checksums.CDChecksums.CheckCDSector(buffer); + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List FailingLBAs, out List UnknownLBAs) + { + byte[] buffer = ReadSectorsLong(sectorAddress, length); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = Checksums.CDChecksums.CheckCDSector(sector); + + switch (sectorStatus) + { + case null: + UnknownLBAs.Add((ulong)i + sectorAddress); + break; + case false: + FailingLBAs.Add((ulong)i + sectorAddress); + break; + } + } + + if (UnknownLBAs.Count > 0) + return null; + if (FailingLBAs.Count > 0) + return false; + return true; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List FailingLBAs, out List UnknownLBAs) + { + byte[] buffer = ReadSectorsLong(sectorAddress, length, track); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = Checksums.CDChecksums.CheckCDSector(sector); + + switch (sectorStatus) + { + case null: + UnknownLBAs.Add((ulong)i + sectorAddress); + break; + case false: + FailingLBAs.Add((ulong)i + sectorAddress); + break; + } + } + + if (UnknownLBAs.Count > 0) + return null; + if (FailingLBAs.Count > 0) + return false; + return true; + } + + public override bool? VerifyDiskImage() + { + return null; + } + #endregion #region Private methods diff --git a/DiscImageChef/ImagePlugins/TeleDisk.cs b/DiscImageChef/ImagePlugins/TeleDisk.cs index d9d76f3e7..51792505d 100644 --- a/DiscImageChef/ImagePlugins/TeleDisk.cs +++ b/DiscImageChef/ImagePlugins/TeleDisk.cs @@ -47,6 +47,7 @@ namespace DiscImageChef.ImagePlugins class TeleDisk : ImagePlugin { #region Internal Structures + struct TD0Header { // "TD" or "td" depending on compression @@ -122,7 +123,7 @@ namespace DiscImageChef.ImagePlugins } #endregion - + #region Internal Constants // "TD" as little endian uint. @@ -186,13 +187,18 @@ namespace DiscImageChef.ImagePlugins const byte dataBlockRLE = 0x02; #endregion - + #region Internal variables + TD0Header header; TDCommentBlockHeader commentHeader; byte[] commentBlock; - Dictionary sectorsData; // LBA, data + Dictionary sectorsData; + // LBA, data UInt32 totalDiskSize; + bool ADiskCRCHasFailed; + List SectorsWhereCRCHasFailed; + #endregion #region Accesible variables @@ -231,6 +237,8 @@ namespace DiscImageChef.ImagePlugins _imageInfo.driveManufacturer = null; _imageInfo.driveModel = null; _imageInfo.driveSerialNumber = null; + ADiskCRCHasFailed = false; + SectorsWhereCRCHasFailed = new List(); } public override bool IdentifyImage(string imagePath) @@ -295,7 +303,7 @@ namespace DiscImageChef.ImagePlugins return true; } - + public override bool OpenImage(string imagePath) { header = new TD0Header(); @@ -346,8 +354,12 @@ namespace DiscImageChef.ImagePlugins // This may deny legal images // That would be much of a coincidence - if (header.crc != calculatedHeaderCRC && MainClass.isDebug) - Console.WriteLine("DEBUG (TeleDisk plugin): Calculated CRC does not coincide with stored one."); + if (header.crc != calculatedHeaderCRC) + { + ADiskCRCHasFailed = true; + if (MainClass.isDebug) + Console.WriteLine("DEBUG (TeleDisk plugin): Calculated CRC does not coincide with stored one."); + } if (header.sequence != 0x00) return false; @@ -390,7 +402,7 @@ namespace DiscImageChef.ImagePlugins UInt16 cmtcrc = TeleDiskCRC(0, commentBlockForCRC); - if(MainClass.isDebug) + if (MainClass.isDebug) { Console.WriteLine("DEBUG (TeleDisk plugin): Comment header"); Console.WriteLine("DEBUG (TeleDisk plugin): \tcommentheader.crc = 0x{0:X4}", commentHeader.crc); @@ -404,23 +416,25 @@ namespace DiscImageChef.ImagePlugins Console.WriteLine("DEBUG (TeleDisk plugin): \tcommentheader.second = {0}", commentHeader.second); } - for(int i=0;i (ulong)sectorsData.Count - 1) @@ -643,7 +670,7 @@ namespace DiscImageChef.ImagePlugins byte[] sector; - if(!sectorsData.TryGetValue((uint)i, out sector)) + if (!sectorsData.TryGetValue((uint)i, out sector)) throw new ImageNotSupportedException(String.Format("Error reading sector {0}", i)); if (first) @@ -662,58 +689,97 @@ namespace DiscImageChef.ImagePlugins return data; } - + public override byte[] ReadSectorLong(UInt64 sectorAddress) { return ReadSectors(sectorAddress, 1); } - + public override byte[] ReadSectorsLong(UInt64 sectorAddress, UInt32 length) { return ReadSectors(sectorAddress, length); } - + public override string GetImageFormat() { return "Sydex TeleDisk"; } - + public override string GetImageVersion() { return _imageInfo.imageVersion; } - + public override string GetImageApplication() { return _imageInfo.imageApplication; } - + public override string GetImageApplicationVersion() { return _imageInfo.imageApplicationVersion; } - + public override DateTime GetImageCreationTime() { return _imageInfo.imageCreationTime; } - + public override DateTime GetImageLastModificationTime() { return _imageInfo.imageLastModificationTime; } - + public override string GetImageName() { return _imageInfo.imageName; } - + public override DiskType GetDiskType() { return _imageInfo.diskType; } + public override bool? VerifySector(UInt64 sectorAddress) + { + return !SectorsWhereCRCHasFailed.Contains(sectorAddress); + } + + public override bool? VerifySector(UInt64 sectorAddress, UInt32 track) + { + return null; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List FailingLBAs, out List UnknownLBAs) + { + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (UInt64 i = sectorAddress; i < sectorAddress + length; i++) + if (SectorsWhereCRCHasFailed.Contains(sectorAddress)) + FailingLBAs.Add(sectorAddress); + + return FailingLBAs.Count <= 0; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List FailingLBAs, out List UnknownLBAs) + { + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (UInt64 i = sectorAddress; i < sectorAddress + length; i++) + UnknownLBAs.Add(i); + + return null; + } + + public override bool? VerifyDiskImage() + { + return ADiskCRCHasFailed; + } + #region Private methods + static UInt16 TeleDiskCRC(UInt16 crc, byte[] buffer) { int counter = 0; @@ -782,7 +848,7 @@ namespace DiscImageChef.ImagePlugins repeatNumber = BitConverter.ToUInt16(encodedData, ins); Array.Copy(encodedData, ins + 2, repeatValue, 0, 2); - byte[] decodedPiece = new byte[repeatNumber*2]; + byte[] decodedPiece = new byte[repeatNumber * 2]; ArrayHelpers.ArrayFill(decodedPiece, repeatValue); Array.Copy(decodedPiece, 0, decodedData, outs, decodedPiece.Length); ins += 4; @@ -845,7 +911,7 @@ namespace DiscImageChef.ImagePlugins return decodedData; } - private DiskType DecodeTeleDiskDiskType() + DiskType DecodeTeleDiskDiskType() { switch (header.driveType) { @@ -858,7 +924,7 @@ namespace DiscImageChef.ImagePlugins case 163840: { // Acorn disk uses 256 bytes/sector - if(_imageInfo.sectorSize == 256) + if (_imageInfo.sectorSize == 256) return DiskType.ACORN_525_SS_DD_40; else // DOS disks use 512 bytes/sector return DiskType.DOS_525_SS_DD_8; @@ -866,7 +932,7 @@ namespace DiscImageChef.ImagePlugins case 184320: { // Atari disk uses 256 bytes/sector - if(_imageInfo.sectorSize == 256) + if (_imageInfo.sectorSize == 256) return DiskType.ATARI_525_DD; else // DOS disks use 512 bytes/sector return DiskType.DOS_525_SS_DD_9; @@ -874,7 +940,7 @@ namespace DiscImageChef.ImagePlugins case 327680: { // Acorn disk uses 256 bytes/sector - if(_imageInfo.sectorSize == 256) + if (_imageInfo.sectorSize == 256) return DiskType.ACORN_525_SS_DD_80; else // DOS disks use 512 bytes/sector return DiskType.DOS_525_DS_DD_8; @@ -994,7 +1060,7 @@ namespace DiscImageChef.ImagePlugins case 512512: { // DEC disk uses 256 bytes/sector - if(_imageInfo.sectorSize == 256) + if (_imageInfo.sectorSize == 256) return DiskType.RX02; else // ECMA disks use 128 bytes/sector return DiskType.ECMA_59; @@ -1024,140 +1090,141 @@ namespace DiscImageChef.ImagePlugins } } + #endregion #region Unsupported features - + public override byte[] ReadSectorTag(UInt64 sectorAddress, SectorTagType tag) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, SectorTagType tag) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadDiskTag(DiskTagType tag) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override string GetImageCreator() { return _imageInfo.imageCreator; } - + public override string GetImageComments() { return _imageInfo.imageComments; } - + public override string GetDiskManufacturer() { return _imageInfo.diskManufacturer; } - + public override string GetDiskModel() { return _imageInfo.diskModel; } - + public override string GetDiskSerialNumber() { return _imageInfo.diskSerialNumber; } - + public override string GetDiskBarcode() { return _imageInfo.diskBarcode; } - + public override string GetDiskPartNumber() { return _imageInfo.diskPartNumber; } - + public override int GetDiskSequence() { return _imageInfo.diskSequence; } - + public override int GetLastDiskSequence() { return _imageInfo.lastDiskSequence; } - + public override string GetDriveManufacturer() { return _imageInfo.driveManufacturer; } - + public override string GetDriveModel() { return _imageInfo.driveModel; } - + public override string GetDriveSerialNumber() { return _imageInfo.driveSerialNumber; } - + 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(UInt16 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 byte[] ReadSector(UInt64 sectorAddress, UInt32 track) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadSectorTag(UInt64 sectorAddress, UInt32 track, SectorTagType tag) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadSectors(UInt64 sectorAddress, UInt32 length, UInt32 track) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, UInt32 track, SectorTagType tag) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadSectorLong(UInt64 sectorAddress, UInt32 track) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + public override byte[] ReadSectorsLong(UInt64 sectorAddress, UInt32 length, UInt32 track) { throw new FeatureUnsupportedImageException("Feature not supported by image format"); } - + #endregion Unsupported features } } diff --git a/DiscImageChef/ImagePlugins/ZZZRawImage.cs b/DiscImageChef/ImagePlugins/ZZZRawImage.cs index a00bf2612..eeddf32d9 100644 --- a/DiscImageChef/ImagePlugins/ZZZRawImage.cs +++ b/DiscImageChef/ImagePlugins/ZZZRawImage.cs @@ -46,8 +46,10 @@ namespace DiscImageChef.ImagePlugins class ZZZRawImage : ImagePlugin { #region Internal variables + string rawImagePath; bool differentTrackZeroSize; + #endregion #region Accesible variables @@ -356,7 +358,45 @@ namespace DiscImageChef.ImagePlugins return _imageInfo.diskType; } + public override bool? VerifySector(UInt64 sectorAddress) + { + return null; + } + + public override bool? VerifySector(UInt64 sectorAddress, UInt32 track) + { + return null; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List FailingLBAs, out List UnknownLBAs) + { + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (UInt64 i = sectorAddress; i < sectorAddress + length; i++) + UnknownLBAs.Add(i); + + return null; + } + + public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List FailingLBAs, out List UnknownLBAs) + { + FailingLBAs = new List(); + UnknownLBAs = new List(); + + for (UInt64 i = sectorAddress; i < sectorAddress + length; i++) + UnknownLBAs.Add(i); + + return null; + } + + public override bool? VerifyDiskImage() + { + return null; + } + #region Private methods + private DiskType CalculateDiskType() { if (_imageInfo.sectorSize == 2048) @@ -492,6 +532,7 @@ namespace DiscImageChef.ImagePlugins } } } + #endregion #region Unsupported features diff --git a/Packages.mdproj b/Packages.mdproj index 2802dba88..cfcfcec85 100644 --- a/Packages.mdproj +++ b/Packages.mdproj @@ -41,6 +41,6 @@ - 2.0 + 2.1 \ No newline at end of file diff --git a/README.md b/README.md index 7aade5ae0..7f2f970a5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -DiscImageChef v2.00 +DiscImageChef v2.10 =================== Disc Image Chef (because "swiss-army-knife" is used too much) @@ -23,9 +23,9 @@ Features * Identifies HFS, HFS+, MFS, BeFS, ext/2/3/4, FAT12/16/32, FFS/UFS/UFS2, HPFS, ISO9660, LisaFS, MinixFS, NTFS, ODS11, Opera, PCEngine, SolarFS, System V and UnixWare boot filesystem. * Analyzes a disk image getting information about the disk itself and analyzes partitions and filesystems inside them * Can compare two disk images, even different formats, for different sectors and/or metadata -* Can verify sectors or disk images if supported by the underlying format (well, it will be able to in version 2.1) +* Can verify sectors or disk images if supported by the underlying format * Can checksum the disks (and if optical disc, separate tracks) user-data (tags and metadata coming soon) -* Supports CRC32 and CRC64 cyclic redundance checksums as well as MD5, RMD160, SHA1, SHA256, SHA384 and SHA512 hashes. +* Supports CRC16, CRC32 and CRC64 cyclic redundance checksums as well as MD5, RMD160, SHA1, SHA256, SHA384 and SHA512 hashes. Changelog ========= diff --git a/TODO b/TODO index 4bab4fd82..fd7143f00 100644 --- a/TODO +++ b/TODO @@ -74,9 +74,6 @@ Image comparison: --- Offer the option to see differing values --- Optimize and multithread -Image verification: ---- Implement verification on image plugins to implement this verb - Checksums: --- Implement SpamSum fuzzy hashing (aka ssdeep)