From 2a9f5ff82866584a065c2263f87695ac882b7831 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 2 Apr 2018 23:08:26 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9BFix=20sending=20error=20recovery=20?= =?UTF-8?q?MODE=20page=20to=20devices=20before=20trying=20damaged=20sector?= =?UTF-8?q?s.=20Fixes=20#169?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Devices/Dumping/CompactDisc.cs | 172 ++++++++--------- DiscImageChef.Core/Devices/Dumping/SBC.cs | 182 +++++++++--------- 2 files changed, 174 insertions(+), 180 deletions(-) diff --git a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs index 98b8c43d..527723f2 100644 --- a/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs +++ b/DiscImageChef.Core/Devices/Dumping/CompactDisc.cs @@ -82,31 +82,31 @@ namespace DiscImageChef.Core.Devices.Dumping /// If trying to dump scrambled sectors /// If the resume file is invalid /// If the track type is unknown (never) - internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses, - bool force, bool dumpRaw, bool persistent, bool stopOnError, - ref MediaType dskType, - ref - Resume resume, ref DumpLog dumpLog, bool dumpLeadIn, - Encoding encoding, - string - outputPrefix, string outputPath, Dictionary formatOptions, - CICMMetadataType - preSidecar, uint skip, bool nometadata) + internal static void Dump(Device dev, string devicePath, + IWritableImage outputPlugin, ushort retryPasses, + bool force, bool dumpRaw, + bool persistent, bool stopOnError, ref MediaType dskType, + ref Resume resume, ref DumpLog dumpLog, + bool dumpLeadIn, Encoding encoding, + string outputPrefix, string outputPath, + Dictionary formatOptions, + CICMMetadataType preSidecar, uint skip, + bool nometadata) { uint subSize; DateTime start; DateTime end; bool readcd; - bool sense = false; - const uint SECTOR_SIZE = 2352; - FullTOC.CDFullTOC? toc = null; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - uint blocksToRead = 64; - bool aborted = false; - System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; + bool sense = false; + const uint SECTOR_SIZE = 2352; + FullTOC.CDFullTOC? toc = null; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + uint blocksToRead = 64; + bool aborted = false; + System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; Dictionary mediaTags = new Dictionary(); if(dumpRaw) @@ -125,7 +125,7 @@ namespace DiscImageChef.Core.Devices.Dumping toc = FullTOC.Decode(cmdBuf); if(toc.HasValue) { - byte[] tmpBuf = new byte[cmdBuf.Length - 2]; + byte[] tmpBuf = new byte[cmdBuf.Length - 2]; Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2); mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf); @@ -140,7 +140,7 @@ namespace DiscImageChef.Core.Devices.Dumping // Only CD-R and CD-RW have ATIP dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR; - tmpBuf = new byte[cmdBuf.Length - 4]; + tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf); } @@ -205,10 +205,10 @@ namespace DiscImageChef.Core.Devices.Dumping } if(hasDataTrack && hasAudioTrack && allFirstSessionTracksAreAudio && sessions == 2) - dskType = MediaType.CDPLUS; - if(!hasDataTrack && hasAudioTrack && sessions == 1) dskType = MediaType.CDDA; - if(hasDataTrack && !hasAudioTrack && sessions == 1) dskType = MediaType.CDROM; - if(hasVideoTrack && !hasDataTrack && sessions == 1) dskType = MediaType.CDV; + dskType = MediaType.CDPLUS; + if(!hasDataTrack && hasAudioTrack && sessions == 1) dskType = MediaType.CDDA; + if(hasDataTrack && !hasAudioTrack && sessions == 1) dskType = MediaType.CDROM; + if(hasVideoTrack && !hasDataTrack && sessions == 1) dskType = MediaType.CDV; } dumpLog.WriteLine("Reading PMA"); @@ -216,7 +216,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(!sense) if(PMA.Decode(cmdBuf).HasValue) { - tmpBuf = new byte[cmdBuf.Length - 4]; + tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); mediaTags.Add(MediaTagType.CD_PMA, tmpBuf); } @@ -226,7 +226,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(!sense) if(CDTextOnLeadIn.Decode(cmdBuf).HasValue) { - tmpBuf = new byte[cmdBuf.Length - 4]; + tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf); } @@ -335,14 +335,13 @@ namespace DiscImageChef.Core.Devices.Dumping toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) - if(trk.POINT >= 0x01 && - trk.POINT <= 0x63) + if(trk.POINT >= 0x01 && trk.POINT <= 0x63) { trackList.Add(new Track { TrackSequence = trk.POINT, TrackSession = trk.SessionNumber, - TrackType = + TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data @@ -430,14 +429,14 @@ namespace DiscImageChef.Core.Devices.Dumping foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc .Value.TrackDescriptors.OrderBy(t => t.TrackNumber) - .Where(trk => trk.ADR == 1 || trk.ADR == 4)) - if(trk.TrackNumber >= 0x01 && trk.TrackNumber <= 0x63) + .Where(trk => trk.ADR == 1 || trk.ADR == 4)) + if(trk.TrackNumber >= 0x01 && trk.TrackNumber <= 0x63) { trackList.Add(new Track { TrackSequence = trk.TrackNumber, TrackSession = 1, - TrackType = + TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data @@ -496,8 +495,8 @@ namespace DiscImageChef.Core.Devices.Dumping Track[] tracks = trackList.ToArray(); for(int t = 1; t < tracks.Length; t++) tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1; - tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector; - ulong blocks = (ulong)(lastSector + 1); + tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector; + ulong blocks = (ulong)(lastSector + 1); if(blocks == 0) { @@ -728,12 +727,11 @@ namespace DiscImageChef.Core.Devices.Dumping if(!force) return; - supportedSubchannel = MmcSubchannel.None; - subSize = 0; - blockSize = SECTOR_SIZE + subSize; + supportedSubchannel = MmcSubchannel.None; + subSize = 0; + blockSize = SECTOR_SIZE + subSize; for(int t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None; - ret = - outputPlugin.SetTracks(tracks.ToList()); + ret = outputPlugin.SetTracks(tracks.ToList()); if(!ret) { dumpLog.WriteLine("Error sending tracks to output image, not continuing."); @@ -803,7 +801,7 @@ namespace DiscImageChef.Core.Devices.Dumping double cmdDuration = 0; - if(tracks[t].TrackEndSector + 1 - i < blocksToRead) + if(tracks[t].TrackEndSector + 1 - i < blocksToRead) blocksToRead = (uint)(tracks[t].TrackEndSector + 1 - i); #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator @@ -860,7 +858,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(supportedSubchannel != MmcSubchannel.None) { outputPlugin.WriteSectorsLong(new byte[SECTOR_SIZE * skip], i, skip); - outputPlugin.WriteSectorsTag(new byte[subSize * skip], i, skip, + outputPlugin.WriteSectorsTag(new byte[subSize * skip], i, skip, SectorTagType.CdSectorSubchannel); } else outputPlugin.WriteSectorsLong(new byte[blockSize * skip], i, skip); @@ -880,7 +878,7 @@ namespace DiscImageChef.Core.Devices.Dumping double newSpeed = (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000); if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; - resume.NextBlock = i + blocksToRead; + resume.NextBlock = i + blocksToRead; } } @@ -888,10 +886,9 @@ namespace DiscImageChef.Core.Devices.Dumping end = DateTime.UtcNow; mhddLog.Close(); ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / - 1024 / (totalDuration / 1000), devicePath); - dumpLog.WriteLine("Dump finished in {0} seconds.", - (end - start).TotalSeconds); + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), + devicePath); + dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", @@ -906,6 +903,44 @@ namespace DiscImageChef.Core.Devices.Dumping bool forward = true; bool runningPersistent = false; + Modes.ModePage? currentModePage = null; + byte[] md6; + byte[] md10; + + if(persistent) + { + Modes.ModePage_01_MMC pgMmc = + new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20}; + Modes.DecodedMode md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + } + } + }; + md6 = Modes.EncodeMode6(md, dev.ScsiType); + md10 = Modes.EncodeMode10(md, dev.ScsiType); + + dumpLog.WriteLine("Sending MODE SELECT to drive."); + sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _); + if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _); + + if(sense) + { + DicConsole + .WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + DicConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); + dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + } + else runningPersistent = true; + } + cdRepeatRetry: ulong[] tmpArray = resume.BadBlocks.ToArray(); foreach(ulong badSector in tmpArray) @@ -960,42 +995,7 @@ namespace DiscImageChef.Core.Devices.Dumping goto cdRepeatRetry; } - Modes.ModePage? currentModePage = null; - byte[] md6; - byte[] md10; - - if(!runningPersistent && persistent) - { - Modes.ModePage_01_MMC pgMmc = - new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20}; - Modes.DecodedMode md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - } - } - }; - md6 = Modes.EncodeMode6(md, dev.ScsiType); - md10 = Modes.EncodeMode10(md, dev.ScsiType); - - dumpLog.WriteLine("Sending MODE SELECT to drive."); - sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _); - if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _); - - runningPersistent = true; - if(!sense && !dev.Error) - { - pass--; - goto cdRepeatRetry; - } - } - else if(runningPersistent && persistent && currentModePage.HasValue) + if(runningPersistent && currentModePage.HasValue) { Modes.DecodedMode md = new Modes.DecodedMode { @@ -1057,9 +1057,9 @@ namespace DiscImageChef.Core.Devices.Dumping DateTime chkStart = DateTime.UtcNow; CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding); - end = DateTime.UtcNow; + end = DateTime.UtcNow; - totalChkDuration = (end - chkStart).TotalMilliseconds; + totalChkDuration = (end - chkStart).TotalMilliseconds; dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); diff --git a/DiscImageChef.Core/Devices/Dumping/SBC.cs b/DiscImageChef.Core/Devices/Dumping/SBC.cs index 6f72f98b..a7299dc8 100644 --- a/DiscImageChef.Core/Devices/Dumping/SBC.cs +++ b/DiscImageChef.Core/Devices/Dumping/SBC.cs @@ -77,18 +77,17 @@ namespace DiscImageChef.Core.Devices.Dumping /// Path to output file /// Formats to pass to output file plugin /// If the resume file is invalid - internal static void Dump(Device dev, string devicePath, - IWritableImage outputPlugin, ushort retryPasses, - bool force, bool dumpRaw, - bool persistent, bool stopOnError, - Dictionary mediaTags, ref MediaType dskType, + internal static void Dump(Device dev, string devicePath, + IWritableImage outputPlugin, ushort retryPasses, + bool force, bool dumpRaw, + bool persistent, bool stopOnError, + Dictionary mediaTags, ref MediaType dskType, bool opticalDisc, - ref Resume resume, - ref DumpLog dumpLog, Encoding encoding, string outputPrefix, - string outputPath, - Dictionary formatOptions, - CICMMetadataType preSidecar, - uint skip, bool nometadata) + ref Resume resume, ref DumpLog dumpLog, + Encoding encoding, string outputPrefix, + string outputPath, Dictionary formatOptions, + CICMMetadataType preSidecar, uint skip, + bool nometadata) { bool sense; ulong blocks; @@ -101,20 +100,20 @@ namespace DiscImageChef.Core.Devices.Dumping const ushort SBC_PROFILE = 0x0001; DateTime start; DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; byte[] readBuffer; uint blocksToRead; - bool aborted = false; + bool aborted = false; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; - Modes.DecodedMode? decMode = null; + Modes.DecodedMode? decMode = null; dumpLog.WriteLine("Initializing reader."); Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw); - blocks = scsiReader.GetDeviceBlocks(); - blockSize = scsiReader.LogicalBlockSize; + blocks = scsiReader.GetDeviceBlocks(); + blockSize = scsiReader.LogicalBlockSize; if(scsiReader.FindReadCommand()) { dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); @@ -152,12 +151,9 @@ namespace DiscImageChef.Core.Devices.Dumping { mediaTags = new Dictionary(); - if(dev.IsUsb && dev.UsbDescriptors != null) - mediaTags.Add(MediaTagType.USB_Descriptors, null); - if(dev.Type == DeviceType.ATAPI) - mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null); - if(dev.IsPcmcia && dev.Cis != null) - mediaTags.Add(MediaTagType.PCMCIA_CIS, null); + if(dev.IsUsb && dev.UsbDescriptors != null) mediaTags.Add(MediaTagType.USB_Descriptors, null); + if(dev.Type == DeviceType.ATAPI) mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null); + if(dev.IsPcmcia && dev.Cis != null) mediaTags.Add(MediaTagType.PCMCIA_CIS, null); sense = dev.ScsiInquiry(out byte[] cmdBuf, out _); mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf); @@ -276,7 +272,7 @@ namespace DiscImageChef.Core.Devices.Dumping MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead); IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE); - ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, blockSize); + ret = outputPlugin.Create(outputPath, dskType, formatOptions, blocks, blockSize); // Cannot create image if(!ret) @@ -288,7 +284,7 @@ namespace DiscImageChef.Core.Devices.Dumping return; } - start = DateTime.UtcNow; + start = DateTime.UtcNow; double imageWriteDuration = 0; if(opticalDisc) @@ -302,7 +298,7 @@ namespace DiscImageChef.Core.Devices.Dumping TrackRawBytesPerSector = (int)blockSize, TrackSubchannelType = TrackSubchannelType.None, TrackSession = 1, - TrackType = TrackType.Data + TrackType = TrackType.Data } }); else if(decMode.HasValue) @@ -388,7 +384,7 @@ namespace DiscImageChef.Core.Devices.Dumping if(stopOnError) return; // TODO: Return more cleanly if(i + skip > blocks) skip = (uint)(blocks - i); - + // Write empty data DateTime writeStart = DateTime.Now; outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip); @@ -406,17 +402,16 @@ namespace DiscImageChef.Core.Devices.Dumping double newSpeed = (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000); if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; - resume.NextBlock = i + blocksToRead; + resume.NextBlock = i + blocksToRead; } end = DateTime.UtcNow; DicConsole.WriteLine(); mhddLog.Close(); ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / - 1024 / (totalDuration / 1000), devicePath); - dumpLog.WriteLine("Dump finished in {0} seconds.", - (end - start).TotalSeconds); + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), + devicePath); + dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", @@ -429,58 +424,20 @@ namespace DiscImageChef.Core.Devices.Dumping bool forward = true; bool runningPersistent = false; - repeatRetry: - ulong[] tmpArray = resume.BadBlocks.ToArray(); - foreach(ulong badSector in tmpArray) - { - if(aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - dumpLog.WriteLine("Aborted!"); - break; - } - - DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : ""); - - sense = scsiReader.ReadBlock(out readBuffer, badSector, out double cmdDuration); - totalDuration += cmdDuration; - - if(!sense && !dev.Error) - { - resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputPlugin.WriteSector(readBuffer, badSector); - dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - outputPlugin.WriteSector(readBuffer, badSector); - } - - if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - resume.BadBlocks.Sort(); - resume.BadBlocks.Reverse(); - goto repeatRetry; - } - Modes.ModePage? currentModePage = null; byte[] md6; byte[] md10; - if(!runningPersistent && persistent) + if(persistent) { if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { - Modes.ModePage_01_MMC pgMmc = + Modes.ModePage_01_MMC pgMmc = new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20}; - Modes.DecodedMode md = new Modes.DecodedMode + Modes.DecodedMode md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), - Pages = new[] + Pages = new[] { new Modes.ModePage { @@ -511,7 +468,7 @@ namespace DiscImageChef.Core.Devices.Dumping Modes.DecodedMode md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), - Pages = new[] + Pages = new[] { new Modes.ModePage { @@ -526,17 +483,57 @@ namespace DiscImageChef.Core.Devices.Dumping } dumpLog.WriteLine("Sending MODE SELECT to drive."); - sense = dev.ModeSelect(md6, out _, true, false, dev.Timeout, out _); - if(sense) sense = dev.ModeSelect10(md10, out _, true, false, dev.Timeout, out _); + sense = dev.ModeSelect(md6, out byte[] senseBuf, true, false, dev.Timeout, out _); + if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _); + + if(sense) + { + DicConsole + .WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + DicConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); + dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + } + else runningPersistent = true; + } + + repeatRetry: + ulong[] tmpArray = resume.BadBlocks.ToArray(); + foreach(ulong badSector in tmpArray) + { + if(aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + dumpLog.WriteLine("Aborted!"); + break; + } + + DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : ""); + + sense = scsiReader.ReadBlock(out readBuffer, badSector, out double cmdDuration); + totalDuration += cmdDuration; - runningPersistent = true; if(!sense && !dev.Error) { - pass--; - goto repeatRetry; + resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputPlugin.WriteSector(readBuffer, badSector); + dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } + else if(runningPersistent) outputPlugin.WriteSector(readBuffer, badSector); } - else if(runningPersistent && persistent && currentModePage.HasValue) + + if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0) + { + pass++; + forward = !forward; + resume.BadBlocks.Sort(); + resume.BadBlocks.Reverse(); + goto repeatRetry; + } + + if(runningPersistent && currentModePage.HasValue) { Modes.DecodedMode md = new Modes.DecodedMode { @@ -688,9 +685,9 @@ namespace DiscImageChef.Core.Devices.Dumping DateTime chkStart = DateTime.UtcNow; CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding); - end = DateTime.UtcNow; + end = DateTime.UtcNow; - totalChkDuration = (end - chkStart).TotalMilliseconds; + totalChkDuration = (end - chkStart).TotalMilliseconds; dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); @@ -741,8 +738,8 @@ namespace DiscImageChef.Core.Devices.Dumping if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) sidecar.BlockMedia[0].USB = new USBType { - ProductID = dev.UsbProductId, - VendorID = dev.UsbVendorId, + ProductID = dev.UsbProductId, + VendorID = dev.UsbVendorId, Descriptors = new DumpType { Image = outputPath, @@ -871,13 +868,10 @@ namespace DiscImageChef.Core.Devices.Dumping // TODO: Implement device firmware revision if(!dev.IsRemovable || dev.IsUsb) if(dev.Type == DeviceType.ATAPI) - sidecar.BlockMedia[0].Interface = "ATAPI"; - else if(dev.IsUsb) - sidecar.BlockMedia[0].Interface = "USB"; - else if(dev.IsFireWire) - sidecar.BlockMedia[0].Interface = "FireWire"; - else - sidecar.BlockMedia[0].Interface = "SCSI"; + sidecar.BlockMedia[0].Interface = "ATAPI"; + else if(dev.IsUsb) sidecar.BlockMedia[0].Interface = "USB"; + else if(dev.IsFireWire) sidecar.BlockMedia[0].Interface = "FireWire"; + else sidecar.BlockMedia[0].Interface = "SCSI"; sidecar.BlockMedia[0].LogicalBlocks = (long)blocks; sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalBlockSize; sidecar.BlockMedia[0].LogicalBlockSize = (int)logicalBlockSize;