Allow to retry bad subchannel sectors.

This commit is contained in:
2020-06-13 19:15:27 +01:00
parent db97549306
commit 87dc8029b5
10 changed files with 223 additions and 33 deletions

View File

@@ -86,7 +86,7 @@ namespace Aaru.Core.Devices.Dumping
bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize,
MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration,
Track[] tracks, SubchannelLog subLog, MmcSubchannel desiredSubchannel,
Dictionary<byte, string> isrcs, ref string mcn)
Dictionary<byte, string> isrcs, ref string mcn, ExtentsInt subchannelExtents)
{
ulong sectorSpeedStart = 0; // Used to calculate correct speed
DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation
@@ -390,7 +390,8 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged =
WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i + r, 1,
subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks);
subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks,
subchannelExtents);
// Set tracks and go back
if(indexesChanged)
@@ -514,7 +515,8 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i,
blocksToRead, subLog, isrcs,
(byte)track.TrackSequence, ref mcn, tracks);
(byte)track.TrackSequence, ref mcn, tracks,
subchannelExtents);
// Set tracks and go back
if(indexesChanged)

View File

@@ -112,9 +112,10 @@ namespace Aaru.Core.Devices.Dumping
bool hiddenTrack; // Disc has a hidden track before track 1
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
MmcSubchannel desiredSubchannel; // User requested subchannel
bool bcdSubchannel = false; // Subchannel positioning is in BCD
Dictionary<byte, string> isrcs = new Dictionary<byte, string>();
string mcn = null;
bool bcdSubchannel = false; // Subchannel positioning is in BCD
Dictionary<byte, string> isrcs = new Dictionary<byte, string>();
string mcn = null;
var subchannelExtents = new ExtentsInt();
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); // Media tags
@@ -848,6 +849,20 @@ namespace Aaru.Core.Devices.Dumping
_dumpLog.WriteLine($"Found ISRC for track {trk.TrackSequence}: {mcn}");
}
if(supportedSubchannel != MmcSubchannel.None &&
desiredSubchannel != MmcSubchannel.None)
{
subchannelExtents = new ExtentsInt();
_resume.BadSubchannels ??= new List<int>();
foreach(int sub in _resume.BadSubchannels)
subchannelExtents.Add(sub);
if(_resume.NextBlock < blocks)
subchannelExtents.Add((int)_resume.NextBlock, (int)(blocks - 1));
}
if(_resume.NextBlock > 0)
{
UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
@@ -1015,7 +1030,7 @@ namespace Aaru.Core.Devices.Dumping
ref imageWriteDuration, lastSector, leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed,
out newTrim, tracks[0].TrackType != TrackType.Audio, offsetBytes, read6, read10, read12, read16,
readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, ref totalDuration,
tracks, subLog, desiredSubchannel, isrcs, ref mcn);
tracks, subLog, desiredSubchannel, isrcs, ref mcn, subchannelExtents);
// TODO: Enable when underlying images support lead-outs
/*
@@ -1048,11 +1063,17 @@ namespace Aaru.Core.Devices.Dumping
TrimCdUserData(audioExtents, blockSize, currentTry, extents, newTrim, offsetBytes, read6, read10, read12,
read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors,
ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, ref mcn);
ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, ref mcn, subchannelExtents);
RetryCdUserData(audioExtents, blockSize, currentTry, extents, offsetBytes, readcd, sectorsForOffset,
subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, tracks, isrcs,
ref mcn);
ref mcn, subchannelExtents);
if(subchannelExtents.Count > 0 &&
_retryPasses > 0 &&
_retrySubchannel)
RetrySubchannel(readcd, subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel,
tracks, isrcs, ref mcn, subchannelExtents);
// Write media tags to image
if(!_aborted)
@@ -1084,6 +1105,21 @@ namespace Aaru.Core.Devices.Dumping
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
_resume.BadSubchannels = new List<int>();
foreach(Tuple<int, int> extent in subchannelExtents.ToArray())
{
for(int sub = extent.Item1; sub <= extent.Item2; sub++)
{
if(sub >= (int)_resume.NextBlock)
continue;
_resume.BadSubchannels.Add(sub);
}
}
_resume.BadSubchannels.Sort();
// TODO: Disc ID
var metadata = new CommonTypes.Structs.ImageInfo
{
@@ -1153,6 +1189,7 @@ namespace Aaru.Core.Devices.Dumping
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
UpdateStatus?.Invoke($"{_resume.BadSubchannels.Count} subchannels could not be read.");
UpdateStatus?.Invoke("");
Statistics.AddMedia(dskType, true);

View File

@@ -55,7 +55,7 @@ namespace Aaru.Core.Devices.Dumping
ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize,
MmcSubchannel supportedSubchannel, ref double totalDuration, SubchannelLog subLog,
MmcSubchannel desiredSubchannel, Track[] tracks, Dictionary<byte, string> isrcs,
ref string mcn)
ref string mcn, ExtentsInt subchannelExtents)
{
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
@@ -290,7 +290,7 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector,
1, subLog, isrcs, (byte)track.TrackSequence, ref mcn,
tracks);
tracks, subchannelExtents);
// Set tracks and go back
if(indexesChanged)
@@ -402,7 +402,8 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub,
badSector, 1, subLog, isrcs,
(byte)track.TrackSequence, ref mcn, tracks);
(byte)track.TrackSequence, ref mcn, tracks,
subchannelExtents);
// Set tracks and go back
if(indexesChanged)
@@ -443,5 +444,135 @@ namespace Aaru.Core.Devices.Dumping
EndProgress?.Invoke();
}
void RetrySubchannel(bool readcd, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration,
SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks,
Dictionary<byte, string> isrcs, ref string mcn, ExtentsInt subchannelExtents)
{
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration; // Command execution time
const uint sectorSize = 2352; // Full sector size
byte[] senseBuf = null; // Sense buffer
PlextorSubchannel supportedPlextorSubchannel;
if(supportedSubchannel == MmcSubchannel.None ||
desiredSubchannel == MmcSubchannel.None)
return;
switch(supportedSubchannel)
{
case MmcSubchannel.None:
supportedPlextorSubchannel = PlextorSubchannel.None;
break;
case MmcSubchannel.Raw:
supportedPlextorSubchannel = PlextorSubchannel.All;
break;
case MmcSubchannel.Q16:
supportedPlextorSubchannel = PlextorSubchannel.Q16;
break;
case MmcSubchannel.Rw:
supportedPlextorSubchannel = PlextorSubchannel.Pack;
break;
default:
supportedPlextorSubchannel = PlextorSubchannel.None;
break;
}
if(_aborted)
return;
int pass = 1;
bool forward = true;
InitProgress?.Invoke();
cdRepeatRetry:
_resume.BadSubchannels = new List<int>();
foreach(Tuple<int, int> extent in subchannelExtents.ToArray())
{
for(int sub = extent.Item1; sub <= extent.Item2; sub++)
{
if(sub >= (int)_resume.NextBlock)
continue;
_resume.BadSubchannels.Add(sub);
}
}
_resume.BadSubchannels.Sort();
if(!forward)
_resume.BadSubchannels.Reverse();
int[] tmpArray = _resume.BadSubchannels.ToArray();
for(int i = 0; i < tmpArray.Length; i++)
{
uint badSector = (uint)tmpArray[i];
Track track = tracks.OrderBy(t => t.TrackStartSector).
LastOrDefault(t => badSector >= t.TrackStartSector);
if(_aborted)
{
_dumpLog.WriteLine("Aborted!");
break;
}
PulseProgress?.
Invoke($"Retrying sector {badSector} subchannel, pass {pass}, {(forward ? "forward" : "reverse")}");
uint startSector = badSector - 2;
if(_supportsPlextorD8)
{
sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, startSector, subSize, 5,
supportedPlextorSubchannel, 0, out cmdDuration);
totalDuration += cmdDuration;
}
else if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out senseBuf, startSector, subSize, 5, MmcSectorTypes.AllTypes,
false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out cmdDuration);
totalDuration += cmdDuration;
}
if(sense || _dev.Error)
continue;
WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5, subLog, isrcs,
(byte)track.TrackSequence, ref mcn, tracks, subchannelExtents);
if(subchannelExtents.Contains(tmpArray[i]))
continue;
UpdateStatus?.Invoke($"Correctly retried sector {badSector} subchannel in pass {pass}.");
_dumpLog.WriteLine("Correctly retried sector {0} subchannel in pass {1}.", badSector, pass);
}
if(pass < _retryPasses &&
!_aborted &&
subchannelExtents.Count > 0)
{
pass++;
forward = !forward;
goto cdRepeatRetry;
}
EndProgress?.Invoke();
}
}
}

View File

@@ -75,7 +75,7 @@ namespace Aaru.Core.Devices.Dumping
bool read6, bool read10, bool read12, bool read16, bool readcd,
MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration,
SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary<byte, string> isrcs,
ref string mcn, Track[] tracks)
ref string mcn, Track[] tracks, ExtentsInt subchannelExtents)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
@@ -160,7 +160,7 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i,
_maximumReadable, subLog, isrcs, 0xAA, ref mcn,
tracks);
tracks, subchannelExtents);
// Set tracks and go back
if(indexesChanged)
@@ -239,7 +239,7 @@ namespace Aaru.Core.Devices.Dumping
bool read6, bool read10, bool read12, bool read16, bool readcd,
MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration,
SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary<byte, string> isrcs,
ref string mcn, Track[] tracks)
ref string mcn, Track[] tracks, ExtentsInt subchannelExtents)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
@@ -324,7 +324,7 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i,
_maximumReadable, subLog, isrcs, 0xAA, ref mcn,
tracks);
tracks, subchannelExtents);
// Set tracks and go back
if(indexesChanged)

View File

@@ -34,6 +34,7 @@ using System;
using System.Collections.Generic;
using Aaru.Checksums;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Structs;
using Aaru.Core.Logging;
using Aaru.Decoders.CD;
@@ -70,7 +71,8 @@ namespace Aaru.Core.Devices.Dumping
// Return true if indexes have changed
bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, byte[] sub,
ulong sectorAddress, uint length, SubchannelLog subLog,
Dictionary<byte, string> isrcs, byte currentTrack, ref string mcn, Track[] tracks)
Dictionary<byte, string> isrcs, byte currentTrack, ref string mcn, Track[] tracks,
ExtentsInt subchannelExtents)
{
if(supportedSubchannel == MmcSubchannel.Q16)
sub = Subchannel.ConvertQToRaw(sub);
@@ -85,6 +87,10 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks);
if(!_fixSubchannelPosition ||
desiredSubchannel == MmcSubchannel.None)
return indexesChanged;
int prePos = int.MinValue;
// Check subchannel
@@ -96,12 +102,12 @@ namespace Aaru.Core.Devices.Dumping
CRC16CCITTContext.Data(q, 10, out byte[] crc);
bool crcOk = crc[0] == q[10] && crc[1] == q[11];
if(!_fixSubchannelPosition ||
desiredSubchannel == MmcSubchannel.None)
// TODO: Fix
// TODO: Check P
// TODO: Check R-W
if(!crcOk)
continue;
// TODO: Check CRC OK
int aPos = int.MinValue;
byte aframe = (byte)(((q[9] / 16) * 10) + (q[9] & 0x0F));
@@ -137,10 +143,9 @@ namespace Aaru.Core.Devices.Dumping
byte[] posSub = new byte[96];
Array.Copy(deSub, subPos, posSub, 0, 96);
posSub = Subchannel.Interleave(posSub);
_outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel);
if(!_fixSubchannelPosition &&
desiredSubchannel != MmcSubchannel.None)
_outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel);
subchannelExtents.Remove(aPos);
}
return indexesChanged;

View File

@@ -53,7 +53,7 @@ namespace Aaru.Core.Devices.Dumping
bool read16, bool readcd, int sectorsForOffset, uint subSize,
MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration,
SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks,
Dictionary<byte, string> isrcs, ref string mcn)
Dictionary<byte, string> isrcs, ref string mcn, ExtentsInt subchannelExtents)
{
DateTime start;
DateTime end;
@@ -190,7 +190,7 @@ namespace Aaru.Core.Devices.Dumping
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector,
1, subLog, isrcs, (byte)track.TrackSequence, ref mcn,
tracks);
tracks, subchannelExtents);
// Set tracks and go back
if(!indexesChanged)

View File

@@ -61,6 +61,7 @@ namespace Aaru.Core.Devices.Dumping
readonly DumpLog _dumpLog;
readonly bool _dumpRaw;
readonly Encoding _encoding;
readonly bool _fixSubchannelPosition;
readonly bool _force;
readonly Dictionary<string, string> _formatOptions;
readonly bool _metadata;
@@ -79,9 +80,9 @@ namespace Aaru.Core.Devices.Dumping
Database.Models.Device _dbDev; // Device database entry
bool _dumpFirstTrackPregap;
bool _fixOffset;
readonly bool _fixSubchannelPosition;
uint _maximumReadable; // Maximum number of sectors drive can read at once
Resume _resume;
readonly bool _retrySubchannel;
Sidecar _sidecarClass;
uint _skip;
int _speed;
@@ -114,7 +115,7 @@ namespace Aaru.Core.Devices.Dumping
Encoding encoding, string outputPrefix, string outputPath, Dictionary<string, string> formatOptions,
CICMMetadataType preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap,
bool fixOffset, bool debug, DumpSubchannel subchannel, int speed, bool @private,
bool fixSubchannelPosition)
bool fixSubchannelPosition, bool retrySubchannel)
{
_doResume = doResume;
_dev = dev;
@@ -145,6 +146,7 @@ namespace Aaru.Core.Devices.Dumping
_speed = speed;
_private = @private;
_fixSubchannelPosition = fixSubchannelPosition;
_retrySubchannel = retrySubchannel;
}
/// <summary>Starts dumping with the stablished fields and autodetecting the device type</summary>

View File

@@ -800,7 +800,7 @@ namespace Aaru.Gui.ViewModels.Windows
_dumper = new Dump(Resume, _dev, _devicePath, SelectedPlugin.Plugin, (ushort)Retries, Force, false,
Persistent, StopOnError, _resume, dumpLog, encoding, _outputPrefix, Destination,
parsedOptions, _sidecar, (uint)Skipped, ExistingMetadata == false, Trim == false,
Track1Pregap, true, false, DumpSubchannel.Any, 0, false, false);
Track1Pregap, true, false, DumpSubchannel.Any, 0, false, false, false);
new Thread(DoWork).Start();
}

View File

@@ -191,6 +191,14 @@ namespace Aaru.Commands.Media
Argument = new Argument<bool>(() => true), Required = false
});
Add(new Option(new[]
{
"--retry-subchannel"
}, "Retry subchannel. Implies fixing subchannel position..")
{
Argument = new Argument<bool>(() => true), Required = false
});
Handler = CommandHandler.Create(GetType().GetMethod(nameof(Invoke)));
}
@@ -198,7 +206,7 @@ namespace Aaru.Commands.Media
string encoding, bool firstPregap, bool fixOffset, bool force, bool metadata,
bool trim, string outputPath, string options, bool persistent, ushort retryPasses,
uint skip, byte speed, bool stopOnError, string format, string subchannel,
bool @private, bool fixSubchannelPosition)
bool @private, bool fixSubchannelPosition, bool retrySubchannel)
{
MainClass.PrintCopyright();
@@ -208,6 +216,9 @@ namespace Aaru.Commands.Media
if(verbose)
AaruConsole.VerboseWriteLineEvent += System.Console.WriteLine;
if(retrySubchannel)
fixSubchannelPosition = true;
Statistics.AddCommand("dump-media");
AaruConsole.DebugWriteLine("Dump-Media command", "--cicm-xml={0}", cicmXml);
@@ -231,6 +242,7 @@ namespace Aaru.Commands.Media
AaruConsole.DebugWriteLine("Dump-Media command", "--subchannel={0}", subchannel);
AaruConsole.DebugWriteLine("Dump-Media command", "--private={0}", @private);
AaruConsole.DebugWriteLine("Dump-Media command", "--fix-subchannel-position={0}", fixSubchannelPosition);
AaruConsole.DebugWriteLine("Dump-Media command", "--retry-subchannel={0}", retrySubchannel);
// TODO: Disabled temporarily
//AaruConsole.DebugWriteLine("Dump-Media command", "--raw={0}", raw);
@@ -344,7 +356,8 @@ namespace Aaru.Commands.Media
if(resumeClass != null &&
resumeClass.NextBlock > resumeClass.LastBlock &&
resumeClass.BadBlocks.Count == 0 &&
!resumeClass.Tape)
!resumeClass.Tape &&
(resumeClass.BadSubchannels is null || resumeClass.BadSubchannels.Count == 0))
{
AaruConsole.WriteLine("Media already dumped correctly, not continuing...");
@@ -428,7 +441,7 @@ namespace Aaru.Commands.Media
var dumper = new Dump(resume, dev, devicePath, outputFormat, retryPasses, force, false, persistent,
stopOnError, resumeClass, dumpLog, encodingClass, outputPrefix, outputPath,
parsedOptions, sidecar, skip, metadata, trim, firstPregap, fixOffset, debug,
wantedSubchannel, speed, @private, fixSubchannelPosition);
wantedSubchannel, speed, @private, fixSubchannelPosition, retrySubchannel);
dumper.UpdateStatus += Progress.UpdateStatus;
dumper.ErrorMessage += Progress.ErrorMessage;