🐛Fix sending error recovery MODE page to devices before trying damaged sectors. Fixes #169

This commit is contained in:
2018-04-02 23:08:26 +01:00
parent 8d8735fdcb
commit 2a9f5ff828
2 changed files with 174 additions and 180 deletions

View File

@@ -82,31 +82,31 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception> /// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception> /// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
/// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception> /// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception>
internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses, internal static void Dump(Device dev, string devicePath,
bool force, bool dumpRaw, bool persistent, bool stopOnError, IWritableImage outputPlugin, ushort retryPasses,
ref MediaType dskType, bool force, bool dumpRaw,
ref bool persistent, bool stopOnError, ref MediaType dskType,
Resume resume, ref DumpLog dumpLog, bool dumpLeadIn, ref Resume resume, ref DumpLog dumpLog,
Encoding encoding, bool dumpLeadIn, Encoding encoding,
string string outputPrefix, string outputPath,
outputPrefix, string outputPath, Dictionary<string, string> formatOptions, Dictionary<string, string> formatOptions,
CICMMetadataType CICMMetadataType preSidecar, uint skip,
preSidecar, uint skip, bool nometadata) bool nometadata)
{ {
uint subSize; uint subSize;
DateTime start; DateTime start;
DateTime end; DateTime end;
bool readcd; bool readcd;
bool sense = false; bool sense = false;
const uint SECTOR_SIZE = 2352; const uint SECTOR_SIZE = 2352;
FullTOC.CDFullTOC? toc = null; FullTOC.CDFullTOC? toc = null;
double totalDuration = 0; double totalDuration = 0;
double currentSpeed = 0; double currentSpeed = 0;
double maxSpeed = double.MinValue; double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue; double minSpeed = double.MaxValue;
uint blocksToRead = 64; uint blocksToRead = 64;
bool aborted = false; bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>();
if(dumpRaw) if(dumpRaw)
@@ -125,7 +125,7 @@ namespace DiscImageChef.Core.Devices.Dumping
toc = FullTOC.Decode(cmdBuf); toc = FullTOC.Decode(cmdBuf);
if(toc.HasValue) 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); Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2);
mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf); mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf);
@@ -140,7 +140,7 @@ namespace DiscImageChef.Core.Devices.Dumping
// Only CD-R and CD-RW have ATIP // Only CD-R and CD-RW have ATIP
dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR; 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); Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf); mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf);
} }
@@ -205,10 +205,10 @@ namespace DiscImageChef.Core.Devices.Dumping
} }
if(hasDataTrack && hasAudioTrack && allFirstSessionTracksAreAudio && sessions == 2) if(hasDataTrack && hasAudioTrack && allFirstSessionTracksAreAudio && sessions == 2)
dskType = MediaType.CDPLUS; dskType = MediaType.CDPLUS;
if(!hasDataTrack && hasAudioTrack && sessions == 1) dskType = MediaType.CDDA; if(!hasDataTrack && hasAudioTrack && sessions == 1) dskType = MediaType.CDDA;
if(hasDataTrack && !hasAudioTrack && sessions == 1) dskType = MediaType.CDROM; if(hasDataTrack && !hasAudioTrack && sessions == 1) dskType = MediaType.CDROM;
if(hasVideoTrack && !hasDataTrack && sessions == 1) dskType = MediaType.CDV; if(hasVideoTrack && !hasDataTrack && sessions == 1) dskType = MediaType.CDV;
} }
dumpLog.WriteLine("Reading PMA"); dumpLog.WriteLine("Reading PMA");
@@ -216,7 +216,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense) if(!sense)
if(PMA.Decode(cmdBuf).HasValue) 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); Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
mediaTags.Add(MediaTagType.CD_PMA, tmpBuf); mediaTags.Add(MediaTagType.CD_PMA, tmpBuf);
} }
@@ -226,7 +226,7 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!sense) if(!sense)
if(CDTextOnLeadIn.Decode(cmdBuf).HasValue) 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); Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf); mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf);
} }
@@ -335,14 +335,13 @@ namespace DiscImageChef.Core.Devices.Dumping
toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4))
if(trk.POINT >= 0x01 && if(trk.POINT >= 0x01 && trk.POINT <= 0x63)
trk.POINT <= 0x63)
{ {
trackList.Add(new Track trackList.Add(new Track
{ {
TrackSequence = trk.POINT, TrackSequence = trk.POINT,
TrackSession = trk.SessionNumber, TrackSession = trk.SessionNumber,
TrackType = TrackType =
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
? TrackType.Data ? TrackType.Data
@@ -430,14 +429,14 @@ namespace DiscImageChef.Core.Devices.Dumping
foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc
.Value.TrackDescriptors.OrderBy(t => t.TrackNumber) .Value.TrackDescriptors.OrderBy(t => t.TrackNumber)
.Where(trk => trk.ADR == 1 || trk.ADR == 4)) .Where(trk => trk.ADR == 1 || trk.ADR == 4))
if(trk.TrackNumber >= 0x01 && trk.TrackNumber <= 0x63) if(trk.TrackNumber >= 0x01 && trk.TrackNumber <= 0x63)
{ {
trackList.Add(new Track trackList.Add(new Track
{ {
TrackSequence = trk.TrackNumber, TrackSequence = trk.TrackNumber,
TrackSession = 1, TrackSession = 1,
TrackType = TrackType =
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
? TrackType.Data ? TrackType.Data
@@ -496,8 +495,8 @@ namespace DiscImageChef.Core.Devices.Dumping
Track[] tracks = trackList.ToArray(); Track[] tracks = trackList.ToArray();
for(int t = 1; t < tracks.Length; t++) tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1; for(int t = 1; t < tracks.Length; t++) tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1;
tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector; tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector;
ulong blocks = (ulong)(lastSector + 1); ulong blocks = (ulong)(lastSector + 1);
if(blocks == 0) if(blocks == 0)
{ {
@@ -728,12 +727,11 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!force) return; if(!force) return;
supportedSubchannel = MmcSubchannel.None; supportedSubchannel = MmcSubchannel.None;
subSize = 0; subSize = 0;
blockSize = SECTOR_SIZE + subSize; blockSize = SECTOR_SIZE + subSize;
for(int t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None; for(int t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None;
ret = ret = outputPlugin.SetTracks(tracks.ToList());
outputPlugin.SetTracks(tracks.ToList());
if(!ret) if(!ret)
{ {
dumpLog.WriteLine("Error sending tracks to output image, not continuing."); dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
@@ -803,7 +801,7 @@ namespace DiscImageChef.Core.Devices.Dumping
double cmdDuration = 0; double cmdDuration = 0;
if(tracks[t].TrackEndSector + 1 - i < blocksToRead) if(tracks[t].TrackEndSector + 1 - i < blocksToRead)
blocksToRead = (uint)(tracks[t].TrackEndSector + 1 - i); blocksToRead = (uint)(tracks[t].TrackEndSector + 1 - i);
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator #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) if(supportedSubchannel != MmcSubchannel.None)
{ {
outputPlugin.WriteSectorsLong(new byte[SECTOR_SIZE * skip], i, skip); 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); SectorTagType.CdSectorSubchannel);
} }
else outputPlugin.WriteSectorsLong(new byte[blockSize * skip], i, skip); else outputPlugin.WriteSectorsLong(new byte[blockSize * skip], i, skip);
@@ -880,7 +878,7 @@ namespace DiscImageChef.Core.Devices.Dumping
double newSpeed = double newSpeed =
(double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000); (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; 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; end = DateTime.UtcNow;
mhddLog.Close(); mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
1024 / (totalDuration / 1000), devicePath); devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
@@ -906,6 +903,44 @@ namespace DiscImageChef.Core.Devices.Dumping
bool forward = true; bool forward = true;
bool runningPersistent = false; 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: cdRepeatRetry:
ulong[] tmpArray = resume.BadBlocks.ToArray(); ulong[] tmpArray = resume.BadBlocks.ToArray();
foreach(ulong badSector in tmpArray) foreach(ulong badSector in tmpArray)
@@ -960,42 +995,7 @@ namespace DiscImageChef.Core.Devices.Dumping
goto cdRepeatRetry; goto cdRepeatRetry;
} }
Modes.ModePage? currentModePage = null; if(runningPersistent && currentModePage.HasValue)
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)
{ {
Modes.DecodedMode md = new Modes.DecodedMode Modes.DecodedMode md = new Modes.DecodedMode
{ {
@@ -1057,9 +1057,9 @@ namespace DiscImageChef.Core.Devices.Dumping
DateTime chkStart = DateTime.UtcNow; DateTime chkStart = DateTime.UtcNow;
CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding); 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("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

View File

@@ -77,18 +77,17 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <param name="outputPath">Path to output file</param> /// <param name="outputPath">Path to output file</param>
/// <param name="formatOptions">Formats to pass to output file plugin</param> /// <param name="formatOptions">Formats to pass to output file plugin</param>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception> /// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
internal static void Dump(Device dev, string devicePath, internal static void Dump(Device dev, string devicePath,
IWritableImage outputPlugin, ushort retryPasses, IWritableImage outputPlugin, ushort retryPasses,
bool force, bool dumpRaw, bool force, bool dumpRaw,
bool persistent, bool stopOnError, bool persistent, bool stopOnError,
Dictionary<MediaTagType, byte[]> mediaTags, ref MediaType dskType, Dictionary<MediaTagType, byte[]> mediaTags, ref MediaType dskType,
bool opticalDisc, bool opticalDisc,
ref Resume resume, ref Resume resume, ref DumpLog dumpLog,
ref DumpLog dumpLog, Encoding encoding, string outputPrefix, Encoding encoding, string outputPrefix,
string outputPath, string outputPath, Dictionary<string, string> formatOptions,
Dictionary<string, string> formatOptions, CICMMetadataType preSidecar, uint skip,
CICMMetadataType preSidecar, bool nometadata)
uint skip, bool nometadata)
{ {
bool sense; bool sense;
ulong blocks; ulong blocks;
@@ -101,20 +100,20 @@ namespace DiscImageChef.Core.Devices.Dumping
const ushort SBC_PROFILE = 0x0001; const ushort SBC_PROFILE = 0x0001;
DateTime start; DateTime start;
DateTime end; DateTime end;
double totalDuration = 0; double totalDuration = 0;
double currentSpeed = 0; double currentSpeed = 0;
double maxSpeed = double.MinValue; double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue; double minSpeed = double.MaxValue;
byte[] readBuffer; byte[] readBuffer;
uint blocksToRead; uint blocksToRead;
bool aborted = false; bool aborted = false;
System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true; System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;
Modes.DecodedMode? decMode = null; Modes.DecodedMode? decMode = null;
dumpLog.WriteLine("Initializing reader."); dumpLog.WriteLine("Initializing reader.");
Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw); Reader scsiReader = new Reader(dev, dev.Timeout, null, dumpRaw);
blocks = scsiReader.GetDeviceBlocks(); blocks = scsiReader.GetDeviceBlocks();
blockSize = scsiReader.LogicalBlockSize; blockSize = scsiReader.LogicalBlockSize;
if(scsiReader.FindReadCommand()) if(scsiReader.FindReadCommand())
{ {
dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage);
@@ -152,12 +151,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
mediaTags = new Dictionary<MediaTagType, byte[]>(); mediaTags = new Dictionary<MediaTagType, byte[]>();
if(dev.IsUsb && dev.UsbDescriptors != null) if(dev.IsUsb && dev.UsbDescriptors != null) mediaTags.Add(MediaTagType.USB_Descriptors, null);
mediaTags.Add(MediaTagType.USB_Descriptors, null); if(dev.Type == DeviceType.ATAPI) mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null);
if(dev.Type == DeviceType.ATAPI) if(dev.IsPcmcia && dev.Cis != null) mediaTags.Add(MediaTagType.PCMCIA_CIS, null);
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 _); sense = dev.ScsiInquiry(out byte[] cmdBuf, out _);
mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf); 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); MhddLog mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
IbgLog ibgLog = new IbgLog(outputPrefix + ".ibg", SBC_PROFILE); 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 // Cannot create image
if(!ret) if(!ret)
@@ -288,7 +284,7 @@ namespace DiscImageChef.Core.Devices.Dumping
return; return;
} }
start = DateTime.UtcNow; start = DateTime.UtcNow;
double imageWriteDuration = 0; double imageWriteDuration = 0;
if(opticalDisc) if(opticalDisc)
@@ -302,7 +298,7 @@ namespace DiscImageChef.Core.Devices.Dumping
TrackRawBytesPerSector = (int)blockSize, TrackRawBytesPerSector = (int)blockSize,
TrackSubchannelType = TrackSubchannelType.None, TrackSubchannelType = TrackSubchannelType.None,
TrackSession = 1, TrackSession = 1,
TrackType = TrackType.Data TrackType = TrackType.Data
} }
}); });
else if(decMode.HasValue) else if(decMode.HasValue)
@@ -406,17 +402,16 @@ namespace DiscImageChef.Core.Devices.Dumping
double newSpeed = double newSpeed =
(double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000); (double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
resume.NextBlock = i + blocksToRead; resume.NextBlock = i + blocksToRead;
} }
end = DateTime.UtcNow; end = DateTime.UtcNow;
DicConsole.WriteLine(); DicConsole.WriteLine();
mhddLog.Close(); mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
1024 / (totalDuration / 1000), devicePath); devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
@@ -429,58 +424,20 @@ namespace DiscImageChef.Core.Devices.Dumping
bool forward = true; bool forward = true;
bool runningPersistent = false; 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; Modes.ModePage? currentModePage = null;
byte[] md6; byte[] md6;
byte[] md10; byte[] md10;
if(!runningPersistent && persistent) if(persistent)
{ {
if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) 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}; 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(), Header = new Modes.ModeHeader(),
Pages = new[] Pages = new[]
{ {
new Modes.ModePage new Modes.ModePage
{ {
@@ -511,7 +468,7 @@ namespace DiscImageChef.Core.Devices.Dumping
Modes.DecodedMode md = new Modes.DecodedMode Modes.DecodedMode md = new Modes.DecodedMode
{ {
Header = new Modes.ModeHeader(), Header = new Modes.ModeHeader(),
Pages = new[] Pages = new[]
{ {
new Modes.ModePage new Modes.ModePage
{ {
@@ -526,17 +483,57 @@ namespace DiscImageChef.Core.Devices.Dumping
} }
dumpLog.WriteLine("Sending MODE SELECT to drive."); dumpLog.WriteLine("Sending MODE SELECT to drive.");
sense = dev.ModeSelect(md6, 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 _, 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) if(!sense && !dev.Error)
{ {
pass--; resume.BadBlocks.Remove(badSector);
goto repeatRetry; 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 Modes.DecodedMode md = new Modes.DecodedMode
{ {
@@ -688,9 +685,9 @@ namespace DiscImageChef.Core.Devices.Dumping
DateTime chkStart = DateTime.UtcNow; DateTime chkStart = DateTime.UtcNow;
CICMMetadataType sidecar = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding); 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("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
@@ -741,8 +738,8 @@ namespace DiscImageChef.Core.Devices.Dumping
if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) if(outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
sidecar.BlockMedia[0].USB = new USBType sidecar.BlockMedia[0].USB = new USBType
{ {
ProductID = dev.UsbProductId, ProductID = dev.UsbProductId,
VendorID = dev.UsbVendorId, VendorID = dev.UsbVendorId,
Descriptors = new DumpType Descriptors = new DumpType
{ {
Image = outputPath, Image = outputPath,
@@ -871,13 +868,10 @@ namespace DiscImageChef.Core.Devices.Dumping
// TODO: Implement device firmware revision // TODO: Implement device firmware revision
if(!dev.IsRemovable || dev.IsUsb) if(!dev.IsRemovable || dev.IsUsb)
if(dev.Type == DeviceType.ATAPI) if(dev.Type == DeviceType.ATAPI)
sidecar.BlockMedia[0].Interface = "ATAPI"; sidecar.BlockMedia[0].Interface = "ATAPI";
else if(dev.IsUsb) else if(dev.IsUsb) sidecar.BlockMedia[0].Interface = "USB";
sidecar.BlockMedia[0].Interface = "USB"; else if(dev.IsFireWire) sidecar.BlockMedia[0].Interface = "FireWire";
else if(dev.IsFireWire) else sidecar.BlockMedia[0].Interface = "SCSI";
sidecar.BlockMedia[0].Interface = "FireWire";
else
sidecar.BlockMedia[0].Interface = "SCSI";
sidecar.BlockMedia[0].LogicalBlocks = (long)blocks; sidecar.BlockMedia[0].LogicalBlocks = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalBlockSize; sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalBlockSize;
sidecar.BlockMedia[0].LogicalBlockSize = (int)logicalBlockSize; sidecar.BlockMedia[0].LogicalBlockSize = (int)logicalBlockSize;