mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
🐛Fix sending error recovery MODE page to devices before trying damaged sectors. Fixes #169
This commit is contained in:
@@ -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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user