mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Merge branch 'devel' into rebecca/first-lba-broken-workaround
This commit is contained in:
@@ -27,25 +27,27 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.CD;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using Track = Aaru.CommonTypes.Structs.Track;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -54,13 +56,9 @@ partial class Dump
|
||||
/// <returns><c>true</c> if it contains Yellow Book data, <c>false</c> otherwise</returns>
|
||||
static bool IsData(byte[] sector)
|
||||
{
|
||||
if(sector?.Length != 2352)
|
||||
return false;
|
||||
if(sector?.Length != 2352) return false;
|
||||
|
||||
byte[] syncMark =
|
||||
{
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
||||
};
|
||||
byte[] syncMark = [0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
|
||||
|
||||
var testMark = new byte[12];
|
||||
Array.Copy(sector, 0, testMark, 0, 12);
|
||||
@@ -77,13 +75,9 @@ partial class Dump
|
||||
{
|
||||
offset = 0;
|
||||
|
||||
if(sector?.Length != 2352)
|
||||
return false;
|
||||
if(sector?.Length != 2352) return false;
|
||||
|
||||
byte[] syncMark =
|
||||
{
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
||||
};
|
||||
byte[] syncMark = [0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00];
|
||||
|
||||
var testMark = new byte[12];
|
||||
|
||||
@@ -146,7 +140,7 @@ partial class Dump
|
||||
/// <param name="mcn">Disc media catalogue number</param>
|
||||
/// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
|
||||
/// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
|
||||
void ReadCdiReady(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents,
|
||||
void ReadCdiReady(uint blockSize, ref double currentSpeed, DumpHardware currentTry, ExtentsULong extents,
|
||||
IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, ref double maxSpeed,
|
||||
MhddLog mhddLog, ref double minSpeed, uint subSize, MmcSubchannel supportedSubchannel,
|
||||
ref double totalDuration, Track[] tracks, SubchannelLog subLog, MmcSubchannel desiredSubchannel,
|
||||
@@ -154,24 +148,22 @@ partial class Dump
|
||||
bool cdiReadyReadAsAudio, int offsetBytes, int sectorsForOffset,
|
||||
Dictionary<byte, int> smallestPregapLbaPerTrack)
|
||||
{
|
||||
ulong sectorSpeedStart = 0; // Used to calculate correct speed
|
||||
DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation
|
||||
bool sense; // Sense indicator
|
||||
byte[] cmdBuf; // Data buffer
|
||||
byte[] senseBuf; // Sense buffer
|
||||
double cmdDuration; // Command execution time
|
||||
const uint sectorSize = 2352; // Full sector size
|
||||
ulong sectorSpeedStart = 0; // Used to calculate correct speed
|
||||
bool sense; // Sense indicator
|
||||
byte[] cmdBuf; // Data buffer
|
||||
byte[] senseBuf; // Sense buffer
|
||||
double cmdDuration; // Command execution time
|
||||
const uint sectorSize = 2352; // Full sector size
|
||||
Track firstTrack = tracks.FirstOrDefault();
|
||||
uint blocksToRead; // How many sectors to read at once
|
||||
var outputOptical = _outputPlugin as IWritableOpticalImage;
|
||||
|
||||
if(firstTrack is null)
|
||||
return;
|
||||
if(firstTrack is null) return;
|
||||
|
||||
if(cdiReadyReadAsAudio)
|
||||
{
|
||||
_dumpLog.WriteLine("Setting speed to 8x for CD-i Ready reading as audio.");
|
||||
UpdateStatus?.Invoke("Setting speed to 8x for CD-i Ready reading as audio.");
|
||||
_dumpLog.WriteLine(Localization.Core.Setting_speed_to_8x_for_CD_i_Ready_reading_as_audio);
|
||||
UpdateStatus?.Invoke(Localization.Core.Setting_speed_to_8x_for_CD_i_Ready_reading_as_audio);
|
||||
|
||||
_dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _);
|
||||
}
|
||||
@@ -183,8 +175,8 @@ partial class Dump
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -193,10 +185,10 @@ partial class Dump
|
||||
|
||||
blocksToRead = _maximumReadable;
|
||||
|
||||
if(blocksToRead == 1 && cdiReadyReadAsAudio)
|
||||
blocksToRead += (uint)sectorsForOffset;
|
||||
if(blocksToRead == 1 && cdiReadyReadAsAudio) blocksToRead += (uint)sectorsForOffset;
|
||||
|
||||
if(cdiReadyReadAsAudio)
|
||||
{
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(i == 0)
|
||||
@@ -204,50 +196,97 @@ partial class Dump
|
||||
else
|
||||
firstSectorToRead -= (uint)sectorsForOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentSpeed > maxSpeed &&
|
||||
currentSpeed > 0)
|
||||
maxSpeed = currentSpeed;
|
||||
if(currentSpeed > maxSpeed && currentSpeed > 0) maxSpeed = currentSpeed;
|
||||
|
||||
if(currentSpeed < minSpeed &&
|
||||
currentSpeed > 0)
|
||||
minSpeed = currentSpeed;
|
||||
if(currentSpeed < minSpeed && currentSpeed > 0) minSpeed = currentSpeed;
|
||||
|
||||
UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
|
||||
UpdateProgress?.Invoke(string.Format(Localization.Core.Reading_sector_0_of_1_2,
|
||||
i,
|
||||
blocks,
|
||||
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()),
|
||||
(long)i,
|
||||
(long)blocks);
|
||||
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
|
||||
MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
_speedStopwatch.Start();
|
||||
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
firstSectorToRead,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
double elapsed;
|
||||
|
||||
// Overcome the track mode change drive error
|
||||
if(sense)
|
||||
{
|
||||
for(uint r = 0; r < _maximumReadable; r++)
|
||||
{
|
||||
UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)",
|
||||
(long)i + r, (long)blocks);
|
||||
UpdateProgress?.Invoke(string.Format(Localization.Core.Reading_sector_0_of_1_2,
|
||||
i + r,
|
||||
blocks,
|
||||
ByteSize.FromMegabytes(currentSpeed)
|
||||
.Per(_oneSecond)
|
||||
.Humanize()),
|
||||
(long)i + r,
|
||||
(long)blocks);
|
||||
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, (uint)sectorsForOffset + 1,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
|
||||
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
_speedStopwatch.Start();
|
||||
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
(uint)(i + r),
|
||||
blockSize,
|
||||
(uint)sectorsForOffset + 1,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
_speedStopwatch.Stop();
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i + r, cmdDuration);
|
||||
ibgLog.Write(i + r, currentSpeed * 1024);
|
||||
extents.Add(i + r, 1, true);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(cdiReadyReadAsAudio)
|
||||
FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel,
|
||||
ref blocksToRead, subSize, ref cmdBuf, blockSize, false);
|
||||
{
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref blocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
}
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
@@ -258,16 +297,30 @@ partial class Dump
|
||||
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
|
||||
if(cdiReadyReadAsAudio)
|
||||
data = Sector.Scramble(data);
|
||||
if(cdiReadyReadAsAudio) data = Sector.Scramble(data);
|
||||
|
||||
outputOptical.WriteSectorsLong(data, i + r, 1);
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel, sub, i + r, 1, subLog, isrcs, 1, ref mcn, tracks,
|
||||
subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel,
|
||||
_fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true,
|
||||
out List<ulong> newPregapSectors);
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
i + r,
|
||||
1,
|
||||
subLog,
|
||||
isrcs,
|
||||
1,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out List<ulong> _);
|
||||
|
||||
// Set tracks and go back
|
||||
if(indexesChanged)
|
||||
@@ -281,7 +334,9 @@ partial class Dump
|
||||
else
|
||||
outputOptical.WriteSectorsLong(cmdBuf, i + r, 1);
|
||||
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
_mediaGraph?.PaintSectorGood(i + r);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -289,40 +344,52 @@ partial class Dump
|
||||
|
||||
leadOutExtents.Add(i + r, firstTrack.EndSector);
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Adding CD-i Ready hole from LBA {i + r} to {firstTrack.EndSector} inclusive.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core
|
||||
.Adding_CD_i_Ready_hole_from_LBA_0_to_1_inclusive,
|
||||
i + r,
|
||||
firstTrack.EndSector));
|
||||
|
||||
_dumpLog.WriteLine("Adding CD-i Ready hole from LBA {0} to {1} inclusive.", i + r,
|
||||
_dumpLog.WriteLine(Localization.Core.Adding_CD_i_Ready_hole_from_LBA_0_to_1_inclusive,
|
||||
i + r,
|
||||
firstTrack.EndSector);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += r;
|
||||
|
||||
_resume.NextBlock = i + r;
|
||||
|
||||
elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
elapsed = _speedStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
if(elapsed <= 0)
|
||||
continue;
|
||||
if(elapsed <= 0 || sectorSpeedStart * blockSize < 524288) continue;
|
||||
|
||||
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
|
||||
sectorSpeedStart = 0;
|
||||
timeSpeedStart = DateTime.UtcNow;
|
||||
_speedStopwatch.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
if(cdiReadyReadAsAudio)
|
||||
FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead,
|
||||
subSize, ref cmdBuf, blockSize, false);
|
||||
{
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref blocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
}
|
||||
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
extents.Add(i, blocksToRead, true);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
@@ -347,9 +414,24 @@ partial class Dump
|
||||
outputOptical.WriteSectorsLong(data, i, blocksToRead);
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, 1, ref mcn, tracks,
|
||||
subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel,
|
||||
_fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true,
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
i,
|
||||
blocksToRead,
|
||||
subLog,
|
||||
isrcs,
|
||||
1,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out List<ulong> newPregapSectors);
|
||||
|
||||
// Set tracks and go back
|
||||
@@ -357,16 +439,14 @@ partial class Dump
|
||||
{
|
||||
outputOptical.SetTracks(tracks.ToList());
|
||||
|
||||
foreach(ulong newPregapSector in newPregapSectors)
|
||||
_resume.BadBlocks.Add(newPregapSector);
|
||||
foreach(ulong newPregapSector in newPregapSectors) _resume.BadBlocks.Add(newPregapSector);
|
||||
|
||||
if(i >= blocksToRead)
|
||||
i -= blocksToRead;
|
||||
else
|
||||
i = 0;
|
||||
|
||||
if(i > 0)
|
||||
i--;
|
||||
if(i > 0) i--;
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -391,7 +471,9 @@ partial class Dump
|
||||
outputOptical.WriteSectorsLong(cmdBuf, i, blocksToRead);
|
||||
}
|
||||
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
_mediaGraph?.PaintSectorsGood(i, blocksToRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -402,20 +484,21 @@ partial class Dump
|
||||
break;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += blocksToRead;
|
||||
|
||||
_resume.NextBlock = i + blocksToRead;
|
||||
|
||||
elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
elapsed = _speedStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
if(elapsed <= 0)
|
||||
continue;
|
||||
if(elapsed <= 0 || sectorSpeedStart * blockSize < 524288) continue;
|
||||
|
||||
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
|
||||
sectorSpeedStart = 0;
|
||||
timeSpeedStart = DateTime.UtcNow;
|
||||
_speedStopwatch.Reset();
|
||||
}
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -27,30 +27,31 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Aaru.Checksums;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.CommonTypes.Structs.Devices.SCSI;
|
||||
using Aaru.Console;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.CD;
|
||||
using Aaru.Decoders.SCSI;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using Track = Aaru.CommonTypes.Structs.Track;
|
||||
using TrackType = Aaru.CommonTypes.Enums.TrackType;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>Retried errored sectors in CompactDisc</summary>
|
||||
@@ -72,7 +73,7 @@ partial class Dump
|
||||
/// <param name="mcn">Disc media catalogue number</param>
|
||||
/// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
|
||||
/// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
|
||||
void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, ExtentsULong extents,
|
||||
void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardware currentTry, 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,
|
||||
@@ -87,30 +88,15 @@ partial class Dump
|
||||
PlextorSubchannel supportedPlextorSubchannel;
|
||||
var outputOptical = _outputPlugin as IWritableOpticalImage;
|
||||
|
||||
switch(supportedSubchannel)
|
||||
{
|
||||
case MmcSubchannel.None:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.None;
|
||||
supportedPlextorSubchannel = supportedSubchannel switch
|
||||
{
|
||||
MmcSubchannel.None => PlextorSubchannel.None,
|
||||
MmcSubchannel.Raw => PlextorSubchannel.Pack,
|
||||
MmcSubchannel.Q16 => PlextorSubchannel.Q16,
|
||||
_ => PlextorSubchannel.None
|
||||
};
|
||||
|
||||
break;
|
||||
case MmcSubchannel.Raw:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.Pack;
|
||||
|
||||
break;
|
||||
case MmcSubchannel.Q16:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.Q16;
|
||||
|
||||
break;
|
||||
default:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.None;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(_resume.BadBlocks.Count <= 0 ||
|
||||
_aborted ||
|
||||
_retryPasses <= 0)
|
||||
return;
|
||||
if(_resume.BadBlocks.Count <= 0 || _aborted || _retryPasses <= 0) return;
|
||||
|
||||
var pass = 1;
|
||||
var forward = true;
|
||||
@@ -124,12 +110,25 @@ partial class Dump
|
||||
{
|
||||
Modes.ModePage_01_MMC pgMmc;
|
||||
|
||||
sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
|
||||
sense = _dev.ModeSense6(out cmdBuf,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
Modes.DecodedMode? dcMode6 = null;
|
||||
if(!sense) dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
|
||||
|
||||
if(sense || dcMode6 is null)
|
||||
{
|
||||
sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
|
||||
sense = _dev.ModeSense10(out cmdBuf,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(!sense)
|
||||
@@ -137,19 +136,23 @@ partial class Dump
|
||||
Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
|
||||
|
||||
if(dcMode10?.Pages != null)
|
||||
{
|
||||
foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage =>
|
||||
modePage.Page == 0x01 && modePage.Subpage == 0x00))
|
||||
modePage is { Page: 0x01, Subpage: 0x00 }))
|
||||
currentModePage = modePage;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
|
||||
|
||||
if(dcMode6?.Pages != null)
|
||||
foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => modePage.Page == 0x01 &&
|
||||
modePage.Subpage == 0x00))
|
||||
if(dcMode6.Value.Pages != null)
|
||||
{
|
||||
foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => modePage is
|
||||
{
|
||||
Page: 0x01, Subpage: 0x00
|
||||
}))
|
||||
currentModePage = modePage;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentModePage == null)
|
||||
@@ -179,35 +182,35 @@ partial class Dump
|
||||
var md = new Modes.DecodedMode
|
||||
{
|
||||
Header = new Modes.ModeHeader(),
|
||||
Pages = new[]
|
||||
{
|
||||
Pages =
|
||||
[
|
||||
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);
|
||||
|
||||
UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks).");
|
||||
_dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks).");
|
||||
UpdateStatus?.Invoke(Localization.Core.Sending_MODE_SELECT_to_drive_return_damaged_blocks);
|
||||
_dumpLog.WriteLine(Localization.Core.Sending_MODE_SELECT_to_drive_return_damaged_blocks);
|
||||
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) sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
UpdateStatus?.
|
||||
Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
|
||||
UpdateStatus?.Invoke(Localization.Core
|
||||
.Drive_did_not_accept_MODE_SELECT_command_for_persistent_error_reading);
|
||||
|
||||
AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));
|
||||
AaruConsole.DebugWriteLine(Localization.Core.Error_0, Sense.PrettifySense(senseBuf));
|
||||
|
||||
_dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
|
||||
_dumpLog.WriteLine(Localization.Core
|
||||
.Drive_did_not_accept_MODE_SELECT_command_for_persistent_error_reading);
|
||||
}
|
||||
else
|
||||
runningPersistent = true;
|
||||
@@ -215,8 +218,8 @@ partial class Dump
|
||||
|
||||
InitProgress?.Invoke();
|
||||
cdRepeatRetry:
|
||||
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
||||
var sectorsNotEvenPartial = new List<ulong>();
|
||||
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
||||
List<ulong> sectorsNotEvenPartial = [];
|
||||
|
||||
for(var i = 0; i < tmpArray.Length; i++)
|
||||
{
|
||||
@@ -225,34 +228,61 @@ partial class Dump
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass,
|
||||
forward ? "forward" : "reverse",
|
||||
runningPersistent ? "recovering partial data, " : ""));
|
||||
if(forward)
|
||||
{
|
||||
PulseProgress?.Invoke(runningPersistent
|
||||
? string.Format(Localization.Core
|
||||
.Retrying_sector_0_pass_1_recovering_partial_data_forward,
|
||||
badSector,
|
||||
pass)
|
||||
: string.Format(Localization.Core.Retrying_sector_0_pass_1_forward,
|
||||
badSector,
|
||||
pass));
|
||||
}
|
||||
else
|
||||
{
|
||||
PulseProgress?.Invoke(runningPersistent
|
||||
? string.Format(Localization.Core
|
||||
.Retrying_sector_0_pass_1_recovering_partial_data_reverse,
|
||||
badSector,
|
||||
pass)
|
||||
: string.Format(Localization.Core.Retrying_sector_0_pass_1_reverse,
|
||||
badSector,
|
||||
pass));
|
||||
}
|
||||
|
||||
Track track = tracks.OrderBy(t => t.StartSector).LastOrDefault(t => badSector >= t.StartSector);
|
||||
|
||||
byte sectorsToReRead = 1;
|
||||
var badSectorToReRead = (uint)badSector;
|
||||
|
||||
if(_fixOffset &&
|
||||
audioExtents.Contains(badSector) &&
|
||||
offsetBytes != 0)
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
{
|
||||
if(offsetBytes > 0)
|
||||
badSectorToReRead -= (uint)sectorsForOffset;
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(badSectorToReRead == 0)
|
||||
badSectorToReRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
||||
else
|
||||
badSectorToReRead -= (uint)sectorsForOffset;
|
||||
}
|
||||
|
||||
sectorsToReRead = (byte)(sectorsForOffset + 1);
|
||||
sectorsToReRead += (byte)sectorsForOffset;
|
||||
}
|
||||
|
||||
if(_supportsPlextorD8 && audioExtents.Contains(badSector))
|
||||
{
|
||||
sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToReRead, blockSize,
|
||||
sectorsToReRead, supportedPlextorSubchannel, out cmdDuration);
|
||||
sense = ReadPlextorWithSubchannel(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToReRead,
|
||||
blockSize,
|
||||
sectorsToReRead,
|
||||
supportedPlextorSubchannel,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
}
|
||||
@@ -260,21 +290,45 @@ partial class Dump
|
||||
{
|
||||
if(audioExtents.Contains(badSector))
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead,
|
||||
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false,
|
||||
MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToReRead,
|
||||
blockSize,
|
||||
sectorsToReRead,
|
||||
MmcSectorTypes.Cdda,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MmcHeaderCodes.None,
|
||||
true,
|
||||
false,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
DecodedSense? decSense = Sense.Decode(senseBuf);
|
||||
|
||||
// Try to workaround firmware
|
||||
if(decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05 ||
|
||||
decSense?.ASC == 0x64)
|
||||
if(decSense is { ASC: 0x11, ASCQ: 0x05 } || decSense?.ASC == 0x64)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out _, badSectorToReRead, blockSize, sectorsToReRead,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
|
||||
true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out _,
|
||||
badSectorToReRead,
|
||||
blockSize,
|
||||
sectorsToReRead,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration2);
|
||||
|
||||
cmdDuration += cmdDuration2;
|
||||
@@ -283,24 +337,121 @@ partial class Dump
|
||||
}
|
||||
else
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
|
||||
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToReRead,
|
||||
blockSize,
|
||||
sectorsToReRead,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
DecodedSense? decSense = Sense.Decode(senseBuf);
|
||||
|
||||
// Try to workaround firmware
|
||||
if(decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05 ||
|
||||
decSense?.ASC == 0x64)
|
||||
if(decSense is { ASC: 0x11, ASCQ: 0x05 } || decSense?.ASC == 0x64)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out _, badSectorToReRead, blockSize, sectorsToReRead,
|
||||
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true,
|
||||
false, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
||||
byte scrambledSectorsToReRead = sectorsToReRead;
|
||||
uint scrambledBadSectorToReRead = badSectorToReRead;
|
||||
|
||||
// Contrary to normal read, this must always be offset fixed, because it's data not audio
|
||||
if(offsetBytes != 0)
|
||||
{
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(scrambledBadSectorToReRead == 0)
|
||||
scrambledBadSectorToReRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
||||
else
|
||||
scrambledBadSectorToReRead -= (uint)sectorsForOffset;
|
||||
}
|
||||
|
||||
scrambledSectorsToReRead += (byte)sectorsForOffset;
|
||||
}
|
||||
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out _,
|
||||
scrambledBadSectorToReRead,
|
||||
blockSize,
|
||||
scrambledSectorsToReRead,
|
||||
MmcSectorTypes.Cdda,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MmcHeaderCodes.None,
|
||||
true,
|
||||
false,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration2);
|
||||
|
||||
cmdDuration += cmdDuration2;
|
||||
|
||||
if(!sense)
|
||||
{
|
||||
uint scrambledBlocksToReRead = scrambledSectorsToReRead;
|
||||
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref scrambledBlocksToReRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
|
||||
// Descramble
|
||||
cmdBuf = Sector.Scramble(cmdBuf);
|
||||
|
||||
// Check valid sector
|
||||
CdChecksums.CheckCdSector(cmdBuf,
|
||||
out bool? correctEccP,
|
||||
out bool? correctEccQ,
|
||||
out bool? correctEdc);
|
||||
|
||||
// Check mode, set sense if EDC/ECC validity is not correct
|
||||
switch(cmdBuf[15] & 0x03)
|
||||
{
|
||||
case 0:
|
||||
|
||||
for(var c = 16; c < 2352; c++)
|
||||
{
|
||||
if(cmdBuf[c] == 0x00) continue;
|
||||
|
||||
sense = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
sense = correctEdc != true || correctEccP != true || correctEccQ != true;
|
||||
|
||||
break;
|
||||
case 2:
|
||||
if((cmdBuf[18] & 0x20) != 0x20)
|
||||
{
|
||||
if(correctEccP != true) sense = true;
|
||||
|
||||
if(correctEccQ != true) sense = true;
|
||||
}
|
||||
|
||||
if(correctEdc != true) sense = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,35 +463,44 @@ partial class Dump
|
||||
{
|
||||
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
|
||||
|
||||
if(!runningPersistent)
|
||||
continue;
|
||||
if(!runningPersistent) continue;
|
||||
|
||||
DecodedSense? decSense = Sense.Decode(senseBuf);
|
||||
|
||||
// MEDIUM ERROR, retry with ignore error below
|
||||
if(decSense is { ASC: 0x11 })
|
||||
if(!sectorsNotEvenPartial.Contains(badSector))
|
||||
sectorsNotEvenPartial.Add(badSector);
|
||||
{
|
||||
if(!sectorsNotEvenPartial.Contains(badSector)) sectorsNotEvenPartial.Add(badSector);
|
||||
}
|
||||
}
|
||||
|
||||
// Because one block has been partially used to fix the offset
|
||||
if(_fixOffset &&
|
||||
audioExtents.Contains(badSector) &&
|
||||
offsetBytes != 0)
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
{
|
||||
uint blocksToRead = sectorsToReRead;
|
||||
|
||||
FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, subSize,
|
||||
ref cmdBuf, blockSize, false);
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref blocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
}
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}.");
|
||||
_dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_sector_0_in_pass_1,
|
||||
badSector,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_sector_0_in_pass_1, badSector, pass);
|
||||
sectorsNotEvenPartial.Remove(badSector);
|
||||
}
|
||||
else
|
||||
@@ -350,26 +510,37 @@ partial class Dump
|
||||
{
|
||||
var data = new byte[sectorSize];
|
||||
var sub = new byte[subSize];
|
||||
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
|
||||
if(supportsLongSectors)
|
||||
outputOptical.WriteSectorLong(data, badSector);
|
||||
else
|
||||
outputOptical.WriteSector(Sector.GetUserData(data), badSector);
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel,
|
||||
sub, badSector, 1, subLog, isrcs,
|
||||
(byte)track.Sequence, ref mcn, tracks,
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
badSector,
|
||||
1,
|
||||
subLog,
|
||||
isrcs,
|
||||
(byte)track.Sequence,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition, outputOptical,
|
||||
_fixSubchannel, _fixSubchannelCrc,
|
||||
_dumpLog, UpdateStatus,
|
||||
smallestPregapLbaPerTrack, true, out _);
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out _);
|
||||
|
||||
// Set tracks and go back
|
||||
if(!indexesChanged)
|
||||
continue;
|
||||
if(!indexesChanged) continue;
|
||||
|
||||
outputOptical.SetTracks(tracks.ToList());
|
||||
i--;
|
||||
@@ -383,16 +554,13 @@ partial class Dump
|
||||
}
|
||||
}
|
||||
|
||||
if(pass < _retryPasses &&
|
||||
!_aborted &&
|
||||
_resume.BadBlocks.Count > 0)
|
||||
if(pass < _retryPasses && !_aborted && _resume.BadBlocks.Count > 0)
|
||||
{
|
||||
pass++;
|
||||
forward = !forward;
|
||||
_resume.BadBlocks.Sort();
|
||||
|
||||
if(!forward)
|
||||
_resume.BadBlocks.Reverse();
|
||||
if(!forward) _resume.BadBlocks.Reverse();
|
||||
|
||||
goto cdRepeatRetry;
|
||||
}
|
||||
@@ -419,25 +587,24 @@ partial class Dump
|
||||
var md = new Modes.DecodedMode
|
||||
{
|
||||
Header = new Modes.ModeHeader(),
|
||||
Pages = new[]
|
||||
{
|
||||
Pages =
|
||||
[
|
||||
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 (ignore error correction).");
|
||||
_dumpLog.WriteLine(Localization.Core.Sending_MODE_SELECT_to_drive_ignore_error_correction);
|
||||
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) sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
|
||||
|
||||
if(!sense)
|
||||
{
|
||||
@@ -452,20 +619,33 @@ partial class Dump
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}");
|
||||
PulseProgress?.Invoke(string.Format(Localization.Core.Trying_to_get_partial_data_for_sector_0,
|
||||
badSector));
|
||||
|
||||
Track track = tracks.OrderBy(t => t.StartSector).LastOrDefault(t => badSector >= t.StartSector);
|
||||
|
||||
if(readcd)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
|
||||
true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
(uint)badSector,
|
||||
blockSize,
|
||||
1,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
@@ -478,14 +658,14 @@ partial class Dump
|
||||
continue;
|
||||
}
|
||||
|
||||
_dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass);
|
||||
_dumpLog.WriteLine(Localization.Core.Got_partial_data_for_sector_0_in_pass_1, badSector, pass);
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
var data = new byte[sectorSize];
|
||||
var sub = new byte[subSize];
|
||||
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
|
||||
if(supportsLongSectors)
|
||||
outputOptical.WriteSectorLong(data, badSector);
|
||||
@@ -493,13 +673,28 @@ partial class Dump
|
||||
outputOptical.WriteSector(Sector.GetUserData(data), badSector);
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, ref mcn,
|
||||
tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel,
|
||||
_fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true, out _);
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
badSector,
|
||||
1,
|
||||
subLog,
|
||||
isrcs,
|
||||
(byte)track.Sequence,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out _);
|
||||
|
||||
// Set tracks and go back
|
||||
if(!indexesChanged)
|
||||
continue;
|
||||
if(!indexesChanged) continue;
|
||||
|
||||
outputOptical.SetTracks(tracks.ToList());
|
||||
i--;
|
||||
@@ -522,20 +717,16 @@ partial class Dump
|
||||
var md = new Modes.DecodedMode
|
||||
{
|
||||
Header = new Modes.ModeHeader(),
|
||||
Pages = new[]
|
||||
{
|
||||
currentModePage.Value
|
||||
}
|
||||
Pages = [currentModePage.Value]
|
||||
};
|
||||
|
||||
md6 = Modes.EncodeMode6(md, _dev.ScsiType);
|
||||
md10 = Modes.EncodeMode10(md, _dev.ScsiType);
|
||||
|
||||
_dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
|
||||
_dumpLog.WriteLine(Localization.Core.Sending_MODE_SELECT_to_drive_return_device_to_previous_status);
|
||||
sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _);
|
||||
|
||||
if(sense)
|
||||
_dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
|
||||
if(sense) _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
@@ -565,36 +756,17 @@ partial class Dump
|
||||
PlextorSubchannel supportedPlextorSubchannel;
|
||||
var outputOptical = _outputPlugin as IWritableOpticalImage;
|
||||
|
||||
if(supportedSubchannel == MmcSubchannel.None ||
|
||||
desiredSubchannel == MmcSubchannel.None)
|
||||
return;
|
||||
if(supportedSubchannel == MmcSubchannel.None || desiredSubchannel == MmcSubchannel.None) return;
|
||||
|
||||
switch(supportedSubchannel)
|
||||
{
|
||||
case MmcSubchannel.None:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.None;
|
||||
supportedPlextorSubchannel = supportedSubchannel switch
|
||||
{
|
||||
MmcSubchannel.Raw => PlextorSubchannel.All,
|
||||
MmcSubchannel.Q16 => PlextorSubchannel.Q16,
|
||||
MmcSubchannel.Rw => PlextorSubchannel.Pack,
|
||||
_ => 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;
|
||||
if(_aborted) return;
|
||||
|
||||
var pass = 1;
|
||||
var forward = true;
|
||||
@@ -603,12 +775,11 @@ partial class Dump
|
||||
|
||||
cdRepeatRetry:
|
||||
|
||||
_resume.BadSubchannels = new List<int>();
|
||||
_resume.BadSubchannels = [];
|
||||
_resume.BadSubchannels.AddRange(subchannelExtents);
|
||||
_resume.BadSubchannels.Sort();
|
||||
|
||||
if(!forward)
|
||||
_resume.BadSubchannels.Reverse();
|
||||
if(!forward) _resume.BadSubchannels.Reverse();
|
||||
|
||||
int[] tmpArray = _resume.BadSubchannels.ToArray();
|
||||
|
||||
@@ -620,29 +791,52 @@ partial class Dump
|
||||
|
||||
if(_aborted)
|
||||
{
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.
|
||||
Invoke($"Retrying sector {badSector} subchannel, pass {pass}, {(forward ? "forward" : "reverse")}");
|
||||
PulseProgress?.Invoke(forward
|
||||
? string.Format(Localization.Core.Retrying_sector_0_subchannel_pass_1_forward,
|
||||
badSector,
|
||||
pass)
|
||||
: string.Format(Localization.Core.Retrying_sector_0_subchannel_pass_1_reverse,
|
||||
badSector,
|
||||
pass));
|
||||
|
||||
uint startSector = badSector - 2;
|
||||
|
||||
if(_supportsPlextorD8)
|
||||
{
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, startSector, subSize, 5,
|
||||
supportedPlextorSubchannel, 0, out cmdDuration);
|
||||
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,
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
startSector,
|
||||
subSize,
|
||||
5,
|
||||
track.Type == TrackType.Audio ? MmcSectorTypes.Cdda : MmcSectorTypes.AllTypes,
|
||||
false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None,
|
||||
supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MmcHeaderCodes.None,
|
||||
false,
|
||||
false,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
}
|
||||
@@ -654,22 +848,37 @@ partial class Dump
|
||||
continue;
|
||||
}
|
||||
|
||||
Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5,
|
||||
subLog, isrcs, (byte)track.Sequence, ref mcn, tracks,
|
||||
subchannelExtents, _fixSubchannelPosition, outputOptical,
|
||||
_fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus,
|
||||
smallestPregapLbaPerTrack, true, out _);
|
||||
Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel,
|
||||
cmdBuf,
|
||||
badSector,
|
||||
5,
|
||||
subLog,
|
||||
isrcs,
|
||||
(byte)track.Sequence,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out _);
|
||||
|
||||
if(subchannelExtents.Contains(bs))
|
||||
continue;
|
||||
if(subchannelExtents.Contains(bs)) continue;
|
||||
|
||||
UpdateStatus?.Invoke($"Correctly retried sector {badSector} subchannel in pass {pass}.");
|
||||
_dumpLog.WriteLine("Correctly retried sector {0} subchannel in pass {1}.", badSector, pass);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_sector_0_subchannel_in_pass_1,
|
||||
badSector,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_sector_0_subchannel_in_pass_1, badSector, pass);
|
||||
}
|
||||
|
||||
if(pass < _retryPasses &&
|
||||
!_aborted &&
|
||||
subchannelExtents.Count > 0)
|
||||
if(pass < _retryPasses && !_aborted && subchannelExtents.Count > 0)
|
||||
{
|
||||
pass++;
|
||||
forward = !forward;
|
||||
|
||||
@@ -27,25 +27,28 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using Track = Aaru.CommonTypes.Structs.Track;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -75,7 +78,10 @@ partial class Dump
|
||||
/// <param name="mcn">Disc media catalogue number</param>
|
||||
/// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
|
||||
/// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
|
||||
void DumpCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents,
|
||||
|
||||
// TODO: Use it
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
void DumpCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardware currentTry, ExtentsULong extents,
|
||||
IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, ref double maxSpeed,
|
||||
MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, bool read12, bool read16,
|
||||
bool readcd, MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration,
|
||||
@@ -89,62 +95,113 @@ partial class Dump
|
||||
byte[] senseBuf = null;
|
||||
var outputOptical = _outputPlugin as IWritableOpticalImage;
|
||||
|
||||
UpdateStatus?.Invoke("Reading lead-outs");
|
||||
_dumpLog.WriteLine("Reading lead-outs");
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_lead_outs);
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_lead_outs);
|
||||
|
||||
InitProgress?.Invoke();
|
||||
|
||||
foreach((ulong item1, ulong item2) in leadOutExtents.ToArray())
|
||||
{
|
||||
for(ulong i = item1; i <= item2; i++)
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
double cmdDuration = 0;
|
||||
|
||||
if(currentSpeed > maxSpeed &&
|
||||
currentSpeed > 0)
|
||||
maxSpeed = currentSpeed;
|
||||
if(currentSpeed > maxSpeed && currentSpeed > 0) maxSpeed = currentSpeed;
|
||||
|
||||
if(currentSpeed < minSpeed &&
|
||||
currentSpeed > 0)
|
||||
minSpeed = currentSpeed;
|
||||
if(currentSpeed < minSpeed && currentSpeed > 0) minSpeed = currentSpeed;
|
||||
|
||||
PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)");
|
||||
PulseProgress?.Invoke(string.Format(Localization.Core.Reading_sector_0_at_lead_out_1,
|
||||
i,
|
||||
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()));
|
||||
|
||||
if(readcd)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, false,
|
||||
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
||||
supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
1,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
}
|
||||
else if(read16)
|
||||
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false,
|
||||
_dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read16(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
i,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read12)
|
||||
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0,
|
||||
1, false, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read12(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read10)
|
||||
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0,
|
||||
1, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read10(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read6)
|
||||
sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration);
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
extents.Add(i, _maximumReadable, true);
|
||||
leadOutExtents.Remove(i);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
@@ -161,9 +218,25 @@ partial class Dump
|
||||
outputOptical.WriteSectorsLong(data, i, _maximumReadable);
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks,
|
||||
subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel,
|
||||
_fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true, out _);
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
i,
|
||||
_maximumReadable,
|
||||
subLog,
|
||||
isrcs,
|
||||
0xAA,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out _);
|
||||
|
||||
// Set tracks and go back
|
||||
if(indexesChanged)
|
||||
@@ -177,43 +250,45 @@ partial class Dump
|
||||
else
|
||||
outputOptical.WriteSectors(cmdBuf, i, _maximumReadable);
|
||||
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);
|
||||
|
||||
// TODO: Reset device after X errors
|
||||
if(_stopOnError)
|
||||
return; // TODO: Return more cleanly
|
||||
if(_stopOnError) return; // TODO: Return more cleanly
|
||||
|
||||
// Write empty data
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1);
|
||||
|
||||
outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1,
|
||||
outputOptical.WriteSectorsTag(new byte[subSize * _skip],
|
||||
i,
|
||||
1,
|
||||
SectorTagType.CdSectorSubchannel);
|
||||
}
|
||||
else
|
||||
outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1);
|
||||
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration, _skip);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000);
|
||||
|
||||
if(!double.IsInfinity(newSpeed))
|
||||
currentSpeed = newSpeed;
|
||||
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
|
||||
|
||||
_resume.NextBlock = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
@@ -244,7 +319,10 @@ partial class Dump
|
||||
/// <param name="mcn">Disc media catalogue number</param>
|
||||
/// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
|
||||
/// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
|
||||
void RetryCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents,
|
||||
|
||||
// TODO: Use it
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
void RetryCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardware currentTry, ExtentsULong extents,
|
||||
IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, ref double maxSpeed,
|
||||
MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, bool read12, bool read16,
|
||||
bool readcd, MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration,
|
||||
@@ -263,56 +341,107 @@ partial class Dump
|
||||
InitProgress?.Invoke();
|
||||
|
||||
foreach((ulong item1, ulong item2) in leadOutExtents.ToArray())
|
||||
{
|
||||
for(ulong i = item1; i <= item2; i++)
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
double cmdDuration = 0;
|
||||
|
||||
if(currentSpeed > maxSpeed &&
|
||||
currentSpeed > 0)
|
||||
maxSpeed = currentSpeed;
|
||||
if(currentSpeed > maxSpeed && currentSpeed > 0) maxSpeed = currentSpeed;
|
||||
|
||||
if(currentSpeed < minSpeed &&
|
||||
currentSpeed > 0)
|
||||
minSpeed = currentSpeed;
|
||||
if(currentSpeed < minSpeed && currentSpeed > 0) minSpeed = currentSpeed;
|
||||
|
||||
PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)");
|
||||
PulseProgress?.Invoke(string.Format(Localization.Core.Reading_sector_0_at_lead_out_1,
|
||||
i,
|
||||
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()));
|
||||
|
||||
if(readcd)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, false,
|
||||
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
||||
supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
1,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
}
|
||||
else if(read16)
|
||||
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false,
|
||||
_dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read16(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
i,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read12)
|
||||
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0,
|
||||
1, false, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read12(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read10)
|
||||
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0,
|
||||
1, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read10(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read6)
|
||||
sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration);
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration, _maximumReadable);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
extents.Add(i, _maximumReadable, true);
|
||||
leadOutExtents.Remove(i);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
@@ -329,9 +458,25 @@ partial class Dump
|
||||
outputOptical.WriteSectorsLong(data, i, _maximumReadable);
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks,
|
||||
subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel,
|
||||
_fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true, out _);
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
i,
|
||||
_maximumReadable,
|
||||
subLog,
|
||||
isrcs,
|
||||
0xAA,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out _);
|
||||
|
||||
// Set tracks and go back
|
||||
if(indexesChanged)
|
||||
@@ -345,42 +490,46 @@ partial class Dump
|
||||
else
|
||||
outputOptical.WriteSectors(cmdBuf, i, _maximumReadable);
|
||||
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);
|
||||
|
||||
// TODO: Reset device after X errors
|
||||
if(_stopOnError)
|
||||
return; // TODO: Return more cleanly
|
||||
if(_stopOnError) return; // TODO: Return more cleanly
|
||||
|
||||
// Write empty data
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1);
|
||||
|
||||
if(desiredSubchannel != MmcSubchannel.None)
|
||||
outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1,
|
||||
{
|
||||
outputOptical.WriteSectorsTag(new byte[subSize * _skip],
|
||||
i,
|
||||
1,
|
||||
SectorTagType.CdSectorSubchannel);
|
||||
}
|
||||
}
|
||||
else
|
||||
outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1);
|
||||
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000);
|
||||
|
||||
if(!double.IsInfinity(newSpeed))
|
||||
currentSpeed = newSpeed;
|
||||
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
|
||||
@@ -27,20 +27,18 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>Fix offset in audio/scrambled sectors</summary>
|
||||
@@ -57,8 +55,7 @@ partial class Dump
|
||||
ref uint blocksToRead, uint subSize, ref byte[] cmdBuf, uint blockSize,
|
||||
bool failedCrossingLeadOut)
|
||||
{
|
||||
if(cmdBuf.Length == 0)
|
||||
return;
|
||||
if(cmdBuf.Length == 0) return;
|
||||
|
||||
int offsetFix = offsetBytes < 0 ? (int)(sectorSize * sectorsForOffset + offsetBytes) : offsetBytes;
|
||||
|
||||
@@ -73,7 +70,7 @@ partial class Dump
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, sectorSize * b, sectorSize);
|
||||
Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize);
|
||||
Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize);
|
||||
}
|
||||
|
||||
if(failedCrossingLeadOut)
|
||||
@@ -100,7 +97,7 @@ partial class Dump
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + b * blockSize), sectorSize);
|
||||
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + b * blockSize), subSize);
|
||||
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + b * blockSize), subSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>Reads a sector using Plextor's D8h READ CDDA command with subchannel</summary>
|
||||
@@ -46,8 +46,8 @@ partial class Dump
|
||||
/// <param name="supportedPlextorSubchannel">Supported subchannel type</param>
|
||||
/// <param name="cmdDuration">Time spent sending commands to the drive</param>
|
||||
/// <returns><c>true</c> if an error occured, <c>false</c> otherwise</returns>
|
||||
bool ReadPlextorWithSubchannel(out byte[] cmdBuf, out byte[] senseBuf, uint firstSectorToRead, uint blockSize,
|
||||
uint blocksToRead, PlextorSubchannel supportedPlextorSubchannel,
|
||||
bool ReadPlextorWithSubchannel(out byte[] cmdBuf, out byte[] senseBuf, uint firstSectorToRead, uint blockSize,
|
||||
uint blocksToRead, PlextorSubchannel supportedPlextorSubchannel,
|
||||
out double cmdDuration)
|
||||
{
|
||||
bool sense;
|
||||
@@ -55,58 +55,95 @@ partial class Dump
|
||||
|
||||
if(supportedPlextorSubchannel == PlextorSubchannel.None)
|
||||
{
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
|
||||
supportedPlextorSubchannel, 0, out cmdDuration);
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf,
|
||||
out senseBuf,
|
||||
firstSectorToRead,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
supportedPlextorSubchannel,
|
||||
0,
|
||||
out cmdDuration);
|
||||
|
||||
if(!sense)
|
||||
return false;
|
||||
if(!sense) return false;
|
||||
|
||||
// As a workaround for some firmware bugs, seek far away.
|
||||
_dev.PlextorReadCdDa(out _, out senseBuf, firstSectorToRead - 32, blockSize, blocksToRead,
|
||||
supportedPlextorSubchannel, 0, out _);
|
||||
_dev.PlextorReadCdDa(out _,
|
||||
out senseBuf,
|
||||
firstSectorToRead - 32,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
supportedPlextorSubchannel,
|
||||
0,
|
||||
out _);
|
||||
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
|
||||
supportedPlextorSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf,
|
||||
out senseBuf,
|
||||
firstSectorToRead,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
supportedPlextorSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
return sense;
|
||||
}
|
||||
|
||||
byte[] subBuf;
|
||||
|
||||
uint subSize = supportedPlextorSubchannel == PlextorSubchannel.Q16 ? 16u : 96u;
|
||||
|
||||
if(supportedPlextorSubchannel is PlextorSubchannel.Q16 or PlextorSubchannel.Pack)
|
||||
{
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, 2352 + subSize, blocksToRead,
|
||||
supportedPlextorSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.PlextorReadCdDa(out cmdBuf,
|
||||
out senseBuf,
|
||||
firstSectorToRead,
|
||||
2352 + subSize,
|
||||
blocksToRead,
|
||||
supportedPlextorSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
if(!sense)
|
||||
return false;
|
||||
if(!sense) return false;
|
||||
}
|
||||
|
||||
// As a workaround for some firmware bugs, seek far away.
|
||||
_dev.PlextorReadCdDa(out _, out senseBuf, firstSectorToRead - 32, blockSize, blocksToRead,
|
||||
supportedPlextorSubchannel, 0, out _);
|
||||
_dev.PlextorReadCdDa(out _,
|
||||
out senseBuf,
|
||||
firstSectorToRead - 32,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
supportedPlextorSubchannel,
|
||||
0,
|
||||
out _);
|
||||
|
||||
sense = _dev.PlextorReadCdDa(out byte[] dataBuf, out senseBuf, firstSectorToRead, 2352, blocksToRead,
|
||||
PlextorSubchannel.None, 0, out cmdDuration);
|
||||
sense = _dev.PlextorReadCdDa(out byte[] dataBuf,
|
||||
out senseBuf,
|
||||
firstSectorToRead,
|
||||
2352,
|
||||
blocksToRead,
|
||||
PlextorSubchannel.None,
|
||||
0,
|
||||
out cmdDuration);
|
||||
|
||||
if(sense)
|
||||
return true;
|
||||
if(sense) return true;
|
||||
|
||||
sense = _dev.PlextorReadCdDa(out subBuf, out senseBuf, firstSectorToRead, subSize, blocksToRead,
|
||||
supportedPlextorSubchannel == PlextorSubchannel.Pack ? PlextorSubchannel.All
|
||||
: supportedPlextorSubchannel, 0, out cmdDuration);
|
||||
sense = _dev.PlextorReadCdDa(out byte[] subBuf,
|
||||
out senseBuf,
|
||||
firstSectorToRead,
|
||||
subSize,
|
||||
blocksToRead,
|
||||
supportedPlextorSubchannel == PlextorSubchannel.Pack
|
||||
? PlextorSubchannel.All
|
||||
: supportedPlextorSubchannel,
|
||||
0,
|
||||
out cmdDuration);
|
||||
|
||||
if(sense)
|
||||
return true;
|
||||
if(sense) return true;
|
||||
|
||||
cmdBuf = new byte[2352 * blocksToRead + subSize * blocksToRead];
|
||||
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
Array.Copy(dataBuf, 2352 * b, cmdBuf, (2352 + subSize) * b, 2352);
|
||||
Array.Copy(subBuf, subSize * b, cmdBuf, (2352 + subSize) * b + 2352, subSize);
|
||||
Array.Copy(dataBuf, 2352 * b, cmdBuf, (2352 + subSize) * b, 2352);
|
||||
Array.Copy(subBuf, subSize * b, cmdBuf, (2352 + subSize) * b + 2352, subSize);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,11 +27,9 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
@@ -42,57 +40,64 @@ using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.CD;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
void HandleCdrRunOutSectors(ulong blocks, MmcSubchannel desiredSubchannel, ExtentsULong extents,
|
||||
HashSet<int> subchannelExtents, SubchannelLog subLog, bool supportsLongSectors,
|
||||
Dictionary<byte, byte> trackFlags, Track[] tracks)
|
||||
{
|
||||
List<ulong> runOutSectors = new();
|
||||
List<ulong> runOutSectors = [];
|
||||
|
||||
if(_outputPlugin is not IWritableOpticalImage outputOptical)
|
||||
return;
|
||||
if(_outputPlugin is not IWritableOpticalImage outputOptical) return;
|
||||
|
||||
// Count how many run end sectors are detected as bad blocks
|
||||
for(ulong i = blocks - 1; i > blocks - 1 - _ignoreCdrRunOuts; i--)
|
||||
{
|
||||
if(_resume.BadBlocks.Contains(i))
|
||||
runOutSectors.Add(i);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(runOutSectors.Count == 0)
|
||||
return;
|
||||
if(runOutSectors.Count == 0) return;
|
||||
|
||||
_dumpLog.WriteLine($"{runOutSectors.Count} sectors at the end of the disc are unreadable. This is normal in CD-R(W) discs as these sectors are created by burning software as part of the recording process. Empty ones will be generated and stored in the image.");
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core._0_sectors_at_the_end_of_the_disc_are_unreadable,
|
||||
runOutSectors.Count));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"{runOutSectors.Count} sectors at the end of the disc are unreadable. This is normal in CD-R(W) discs as these sectors are created by burning software as part of the recording process. Empty ones will be generated and stored in the image.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core._0_sectors_at_the_end_of_the_disc_are_unreadable,
|
||||
runOutSectors.Count));
|
||||
|
||||
foreach(ulong s in runOutSectors)
|
||||
{
|
||||
Track track = tracks.FirstOrDefault(t => t.StartSector <= s && t.EndSector >= s);
|
||||
|
||||
if(track is null)
|
||||
continue;
|
||||
if(track is null) continue;
|
||||
|
||||
var sector = new byte[2352];
|
||||
|
||||
switch(track.Type)
|
||||
{
|
||||
case TrackType.Audio: break;
|
||||
case TrackType.Audio:
|
||||
break;
|
||||
case TrackType.Data:
|
||||
sector = new byte[2048];
|
||||
|
||||
break;
|
||||
case TrackType.CdMode1: break;
|
||||
case TrackType.CdMode2Formless: break;
|
||||
case TrackType.CdMode2Form1: break;
|
||||
case TrackType.CdMode2Form2: break;
|
||||
default: continue;
|
||||
case TrackType.CdMode1:
|
||||
break;
|
||||
case TrackType.CdMode2Formless:
|
||||
break;
|
||||
case TrackType.CdMode2Form1:
|
||||
break;
|
||||
case TrackType.CdMode2Form2:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if(track.Type != TrackType.Audio &&
|
||||
track.Type != TrackType.Data)
|
||||
if(track.Type != TrackType.Audio && track.Type != TrackType.Data)
|
||||
{
|
||||
SectorBuilder sb = new();
|
||||
sb.ReconstructPrefix(ref sector, track.Type, (long)s);
|
||||
@@ -107,8 +112,7 @@ partial class Dump
|
||||
_resume.BadBlocks.Remove(s);
|
||||
extents.Add(s);
|
||||
|
||||
if(desiredSubchannel == MmcSubchannel.None)
|
||||
continue;
|
||||
if(desiredSubchannel == MmcSubchannel.None) continue;
|
||||
|
||||
// Hidden track
|
||||
ulong trackStart;
|
||||
|
||||
@@ -27,21 +27,19 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>Check if the drive can read RW raw subchannel</summary>
|
||||
@@ -52,11 +50,24 @@ partial class Dump
|
||||
/// <returns><c>true</c> if read correctly, <c>false</c> otherwise</returns>
|
||||
public static bool SupportsRwSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, uint lba)
|
||||
{
|
||||
dumpLog?.WriteLine("Checking if drive supports full raw subchannel reading...");
|
||||
updateStatus?.Invoke("Checking if drive supports full raw subchannel reading...");
|
||||
dumpLog?.WriteLine(Localization.Core.Checking_if_drive_supports_full_raw_subchannel_reading);
|
||||
updateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_full_raw_subchannel_reading);
|
||||
|
||||
return !dev.ReadCd(out _, out _, lba, 2352 + 96, 1, MmcSectorTypes.AllTypes, false, false, true,
|
||||
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout,
|
||||
return !dev.ReadCd(out _,
|
||||
out _,
|
||||
lba,
|
||||
2352 + 96,
|
||||
1,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
MmcSubchannel.Raw,
|
||||
dev.Timeout,
|
||||
out _);
|
||||
}
|
||||
|
||||
@@ -68,11 +79,24 @@ partial class Dump
|
||||
/// <returns><c>true</c> if read correctly, <c>false</c> otherwise</returns>
|
||||
public static bool SupportsPqSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, uint lba)
|
||||
{
|
||||
dumpLog?.WriteLine("Checking if drive supports PQ subchannel reading...");
|
||||
updateStatus?.Invoke("Checking if drive supports PQ subchannel reading...");
|
||||
dumpLog?.WriteLine(Localization.Core.Checking_if_drive_supports_PQ_subchannel_reading);
|
||||
updateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_PQ_subchannel_reading);
|
||||
|
||||
return !dev.ReadCd(out _, out _, lba, 2352 + 16, 1, MmcSectorTypes.AllTypes, false, false, true,
|
||||
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout,
|
||||
return !dev.ReadCd(out _,
|
||||
out _,
|
||||
lba,
|
||||
2352 + 16,
|
||||
1,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
MmcSubchannel.Q16,
|
||||
dev.Timeout,
|
||||
out _);
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,13 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Aaru.CommonTypes;
|
||||
@@ -46,6 +42,8 @@ using Aaru.Decoders.CD;
|
||||
using Aaru.Decoders.SCSI.MMC;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>Reads media tags from Compact Disc media</summary>
|
||||
@@ -54,7 +52,7 @@ partial class Dump
|
||||
/// <param name="sessions">Sessions</param>
|
||||
/// <param name="firstTrackLastSession">First track in last session</param>
|
||||
void ReadCdTags(ref MediaType mediaType, Dictionary<MediaTagType, byte[]> mediaTags, out int sessions,
|
||||
out int firstTrackLastSession)
|
||||
out int firstTrackLastSession)
|
||||
{
|
||||
byte[] cmdBuf; // Data buffer
|
||||
bool sense; // Sense indicator
|
||||
@@ -63,8 +61,8 @@ partial class Dump
|
||||
firstTrackLastSession = 1;
|
||||
|
||||
// ATIP exists on blank CDs
|
||||
_dumpLog.WriteLine("Reading ATIP");
|
||||
UpdateStatus?.Invoke("Reading ATIP");
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_ATIP);
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_ATIP);
|
||||
sense = _dev.ReadAtip(out cmdBuf, out _, _dev.Timeout, out _);
|
||||
|
||||
if(!sense)
|
||||
@@ -79,48 +77,48 @@ partial class Dump
|
||||
tmpBuf = new byte[cmdBuf.Length - 4];
|
||||
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
|
||||
mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf);
|
||||
_mediaGraph?.PaintRecordableInformationGood();
|
||||
}
|
||||
}
|
||||
|
||||
_dumpLog.WriteLine("Reading Disc Information");
|
||||
UpdateStatus?.Invoke("Reading Disc Information");
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_Disc_Information);
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_Disc_Information);
|
||||
|
||||
sense = _dev.ReadDiscInformation(out cmdBuf, out _, MmcDiscInformationDataTypes.DiscInformation, _dev.Timeout,
|
||||
sense = _dev.ReadDiscInformation(out cmdBuf,
|
||||
out _,
|
||||
MmcDiscInformationDataTypes.DiscInformation,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(!sense)
|
||||
{
|
||||
DiscInformation.StandardDiscInformation? discInfo = DiscInformation.Decode000b(cmdBuf);
|
||||
|
||||
if(discInfo.HasValue &&
|
||||
mediaType == MediaType.CD)
|
||||
switch(discInfo.Value.DiscType)
|
||||
{
|
||||
case 0x10:
|
||||
mediaType = MediaType.CDI;
|
||||
|
||||
break;
|
||||
case 0x20:
|
||||
mediaType = MediaType.CDROMXA;
|
||||
|
||||
break;
|
||||
}
|
||||
if(discInfo.HasValue && mediaType == MediaType.CD)
|
||||
{
|
||||
mediaType = discInfo.Value.DiscType switch
|
||||
{
|
||||
0x10 => MediaType.CDI,
|
||||
0x20 => MediaType.CDROMXA,
|
||||
_ => mediaType
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_dumpLog.WriteLine("Reading PMA");
|
||||
UpdateStatus?.Invoke("Reading PMA");
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_PMA);
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_PMA);
|
||||
sense = _dev.ReadPma(out cmdBuf, out _, _dev.Timeout, out _);
|
||||
|
||||
if(!sense &&
|
||||
PMA.Decode(cmdBuf).HasValue)
|
||||
if(!sense && PMA.Decode(cmdBuf).HasValue)
|
||||
{
|
||||
tmpBuf = new byte[cmdBuf.Length - 4];
|
||||
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
|
||||
mediaTags.Add(MediaTagType.CD_PMA, tmpBuf);
|
||||
_mediaGraph?.PaintRecordableInformationGood();
|
||||
}
|
||||
|
||||
_dumpLog.WriteLine("Reading Session Information");
|
||||
UpdateStatus?.Invoke("Reading Session Information");
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_Session_Information);
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_Session_Information);
|
||||
sense = _dev.ReadSessionInfo(out cmdBuf, out _, _dev.Timeout, out _);
|
||||
|
||||
if(!sense)
|
||||
@@ -134,12 +132,11 @@ partial class Dump
|
||||
}
|
||||
}
|
||||
|
||||
_dumpLog.WriteLine("Reading CD-Text from Lead-In");
|
||||
UpdateStatus?.Invoke("Reading CD-Text from Lead-In");
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_CD_Text_from_Lead_In);
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_CD_Text_from_Lead_In);
|
||||
sense = _dev.ReadCdText(out cmdBuf, out _, _dev.Timeout, out _);
|
||||
|
||||
if(sense || !CDTextOnLeadIn.Decode(cmdBuf).HasValue)
|
||||
return;
|
||||
if(sense || !CDTextOnLeadIn.Decode(cmdBuf).HasValue) return;
|
||||
|
||||
tmpBuf = new byte[cmdBuf.Length - 4];
|
||||
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
|
||||
|
||||
@@ -27,15 +27,13 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -46,6 +44,8 @@ using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.CD;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>Reads the TOC, processes it, returns the track list and last sector</summary>
|
||||
@@ -60,24 +60,24 @@ partial class Dump
|
||||
/// <param name="trackFlags">Track flags</param>
|
||||
/// <param name="updateStatus">Update status handler</param>
|
||||
/// <returns>List of tracks</returns>
|
||||
public static Track[] GetCdTracks(Device dev, DumpLog dumpLog, bool force, out long lastSector,
|
||||
Dictionary<int, long> leadOutStarts, Dictionary<MediaTagType, byte[]> mediaTags,
|
||||
ErrorMessageHandler stoppingErrorMessage, out FullTOC.CDFullTOC? toc,
|
||||
public static Track[] GetCdTracks(Device dev, DumpLog dumpLog, bool force, out long lastSector,
|
||||
Dictionary<int, long> leadOutStarts, Dictionary<MediaTagType, byte[]> mediaTags,
|
||||
ErrorMessageHandler stoppingErrorMessage, out FullTOC.CDFullTOC? toc,
|
||||
Dictionary<byte, byte> trackFlags, UpdateStatusHandler updateStatus)
|
||||
{
|
||||
byte[] cmdBuf; // Data buffer
|
||||
const uint sectorSize = 2352; // Full sector size
|
||||
bool sense; // Sense indicator
|
||||
var trackList = new List<Track>(); // Tracks in disc
|
||||
byte[] tmpBuf; // Temporary buffer
|
||||
byte[] cmdBuf; // Data buffer
|
||||
const uint sectorSize = 2352; // Full sector size
|
||||
bool sense; // Sense indicator
|
||||
List<Track> trackList = []; // Tracks in disc
|
||||
byte[] tmpBuf; // Temporary buffer
|
||||
toc = null;
|
||||
lastSector = 0;
|
||||
TrackType leadoutTrackType = TrackType.Audio;
|
||||
|
||||
// We discarded all discs that falsify a TOC before requesting a real TOC
|
||||
// No TOC, no CD (or an empty one)
|
||||
dumpLog?.WriteLine("Reading full TOC");
|
||||
updateStatus?.Invoke("Reading full TOC");
|
||||
dumpLog?.WriteLine(Localization.Core.Reading_full_TOC);
|
||||
updateStatus?.Invoke(Localization.Core.Reading_full_TOC);
|
||||
sense = dev.ReadRawToc(out cmdBuf, out _, 0, dev.Timeout, out _);
|
||||
|
||||
if(!sense)
|
||||
@@ -92,8 +92,8 @@ partial class Dump
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus?.Invoke("Building track map...");
|
||||
dumpLog?.WriteLine("Building track map...");
|
||||
updateStatus?.Invoke(Localization.Core.Building_track_map);
|
||||
dumpLog?.WriteLine(Localization.Core.Building_track_map);
|
||||
|
||||
if(toc.HasValue)
|
||||
{
|
||||
@@ -101,126 +101,143 @@ partial class Dump
|
||||
toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();
|
||||
|
||||
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR is 1 or 4))
|
||||
if(trk.POINT >= 0x01 &&
|
||||
trk.POINT <= 0x63)
|
||||
{
|
||||
switch(trk.POINT)
|
||||
{
|
||||
trackList.Add(new Track
|
||||
{
|
||||
Sequence = trk.POINT,
|
||||
Session = trk.SessionNumber,
|
||||
Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
|
||||
: TrackType.Audio,
|
||||
StartSector =
|
||||
(ulong)(trk.PHOUR * 3600 * 75 + trk.PMIN * 60 * 75 + trk.PSEC * 75 + trk.PFRAME - 150),
|
||||
BytesPerSector = (int)sectorSize,
|
||||
RawBytesPerSector = (int)sectorSize
|
||||
});
|
||||
|
||||
trackFlags?.Add(trk.POINT, trk.CONTROL);
|
||||
}
|
||||
else if(trk.POINT == 0xA2)
|
||||
{
|
||||
int phour, pmin, psec, pframe;
|
||||
|
||||
if(trk.PFRAME == 0)
|
||||
{
|
||||
pframe = 74;
|
||||
|
||||
if(trk.PSEC == 0)
|
||||
case >= 0x01 and <= 0x63:
|
||||
trackList.Add(new Track
|
||||
{
|
||||
psec = 59;
|
||||
Sequence = trk.POINT,
|
||||
Session = trk.SessionNumber,
|
||||
Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
|
||||
? TrackType.Data
|
||||
: TrackType.Audio,
|
||||
StartSector =
|
||||
(ulong)(trk.PHOUR * 3600 * 75 + trk.PMIN * 60 * 75 + trk.PSEC * 75 + trk.PFRAME - 150),
|
||||
BytesPerSector = (int)sectorSize,
|
||||
RawBytesPerSector = (int)sectorSize
|
||||
});
|
||||
|
||||
if(trk.PMIN == 0)
|
||||
trackFlags?.Add(trk.POINT, trk.CONTROL);
|
||||
|
||||
break;
|
||||
case 0xA2:
|
||||
{
|
||||
int phour, pmin, psec, pframe;
|
||||
|
||||
if(trk.PFRAME == 0)
|
||||
{
|
||||
pframe = 74;
|
||||
|
||||
if(trk.PSEC == 0)
|
||||
{
|
||||
pmin = 59;
|
||||
phour = trk.PHOUR - 1;
|
||||
psec = 59;
|
||||
|
||||
if(trk.PMIN == 0)
|
||||
{
|
||||
pmin = 59;
|
||||
phour = trk.PHOUR - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pmin = trk.PMIN - 1;
|
||||
phour = trk.PHOUR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pmin = trk.PMIN - 1;
|
||||
psec = trk.PSEC - 1;
|
||||
pmin = trk.PMIN;
|
||||
phour = trk.PHOUR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
psec = trk.PSEC - 1;
|
||||
pmin = trk.PMIN;
|
||||
phour = trk.PHOUR;
|
||||
pframe = trk.PFRAME - 1;
|
||||
psec = trk.PSEC;
|
||||
pmin = trk.PMIN;
|
||||
phour = trk.PHOUR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pframe = trk.PFRAME - 1;
|
||||
psec = trk.PSEC;
|
||||
pmin = trk.PMIN;
|
||||
phour = trk.PHOUR;
|
||||
}
|
||||
|
||||
lastSector = phour * 3600 * 75 + pmin * 60 * 75 + psec * 75 + pframe - 150;
|
||||
leadOutStarts?.Add(trk.SessionNumber, lastSector + 1);
|
||||
lastSector = phour * 3600 * 75 + pmin * 60 * 75 + psec * 75 + pframe - 150;
|
||||
leadOutStarts?.Add(trk.SessionNumber, lastSector + 1);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0xA0 when trk.ADR == 1:
|
||||
leadoutTrackType =
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
|
||||
? TrackType.Data
|
||||
: TrackType.Audio;
|
||||
|
||||
break;
|
||||
}
|
||||
else if(trk.POINT == 0xA0 &&
|
||||
trk.ADR == 1)
|
||||
leadoutTrackType =
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
|
||||
: TrackType.Audio;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
updateStatus?.Invoke("Cannot read RAW TOC, requesting processed one...");
|
||||
dumpLog?.WriteLine("Cannot read RAW TOC, requesting processed one...");
|
||||
updateStatus?.Invoke(Localization.Core.Cannot_read_RAW_TOC_requesting_processed_one);
|
||||
dumpLog?.WriteLine(Localization.Core.Cannot_read_RAW_TOC_requesting_processed_one);
|
||||
sense = dev.ReadToc(out cmdBuf, out _, false, 0, dev.Timeout, out _);
|
||||
|
||||
TOC.CDTOC? oldToc = TOC.Decode(cmdBuf);
|
||||
|
||||
if((sense || !oldToc.HasValue) &&
|
||||
!force)
|
||||
if((sense || !oldToc.HasValue) && !force)
|
||||
{
|
||||
dumpLog?.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");
|
||||
dumpLog?.WriteLine(Localization.Core
|
||||
.Could_not_read_TOC_if_you_want_to_continue_use_force_and_will_try_from_LBA_0_to_360000);
|
||||
|
||||
stoppingErrorMessage?.
|
||||
Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");
|
||||
stoppingErrorMessage?.Invoke(Localization.Core
|
||||
.Could_not_read_TOC_if_you_want_to_continue_use_force_and_will_try_from_LBA_0_to_360000);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if(oldToc.HasValue)
|
||||
foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc.Value.TrackDescriptors.OrderBy(t => t.TrackNumber).
|
||||
Where(trk => trk.ADR is 1 or 4))
|
||||
if(trk.TrackNumber >= 0x01 &&
|
||||
trk.TrackNumber <= 0x63)
|
||||
{
|
||||
foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc.Value.TrackDescriptors.OrderBy(t => t.TrackNumber)
|
||||
.Where(trk => trk.ADR is 1 or 4))
|
||||
{
|
||||
switch(trk.TrackNumber)
|
||||
{
|
||||
trackList.Add(new Track
|
||||
{
|
||||
Sequence = trk.TrackNumber,
|
||||
Session = 1,
|
||||
Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
|
||||
: TrackType.Audio,
|
||||
StartSector = trk.TrackStartAddress,
|
||||
BytesPerSector = (int)sectorSize,
|
||||
RawBytesPerSector = (int)sectorSize
|
||||
});
|
||||
case >= 0x01 and <= 0x63:
|
||||
trackList.Add(new Track
|
||||
{
|
||||
Sequence = trk.TrackNumber,
|
||||
Session = 1,
|
||||
Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
|
||||
? TrackType.Data
|
||||
: TrackType.Audio,
|
||||
StartSector = trk.TrackStartAddress,
|
||||
BytesPerSector = (int)sectorSize,
|
||||
RawBytesPerSector = (int)sectorSize
|
||||
});
|
||||
|
||||
trackFlags?.Add(trk.TrackNumber, trk.CONTROL);
|
||||
}
|
||||
else if(trk.TrackNumber == 0xAA)
|
||||
{
|
||||
leadoutTrackType =
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
|
||||
: TrackType.Audio;
|
||||
trackFlags?.Add(trk.TrackNumber, trk.CONTROL);
|
||||
|
||||
lastSector = trk.TrackStartAddress - 1;
|
||||
break;
|
||||
case 0xAA:
|
||||
leadoutTrackType =
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
||||
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
|
||||
? TrackType.Data
|
||||
: TrackType.Audio;
|
||||
|
||||
lastSector = trk.TrackStartAddress - 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(trackList.Count == 0)
|
||||
{
|
||||
updateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out");
|
||||
dumpLog?.WriteLine("No tracks found, adding a single track from 0 to Lead-Out");
|
||||
updateStatus?.Invoke(Localization.Core.No_tracks_found_adding_a_single_track_from_zero_to_Lead_Out);
|
||||
dumpLog?.WriteLine(Localization.Core.No_tracks_found_adding_a_single_track_from_zero_to_Lead_Out);
|
||||
|
||||
trackList.Add(new Track
|
||||
{
|
||||
@@ -235,8 +252,7 @@ partial class Dump
|
||||
trackFlags?.Add(1, (byte)(leadoutTrackType == TrackType.Audio ? 0 : 4));
|
||||
}
|
||||
|
||||
if(lastSector != 0)
|
||||
return trackList.ToArray();
|
||||
if(lastSector != 0) return trackList.ToArray();
|
||||
|
||||
sense = dev.ReadCapacity16(out cmdBuf, out _, dev.Timeout, out _);
|
||||
|
||||
@@ -252,27 +268,24 @@ partial class Dump
|
||||
{
|
||||
sense = dev.ReadCapacity(out cmdBuf, out _, dev.Timeout, out _);
|
||||
|
||||
if(!sense)
|
||||
lastSector = ((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & 0xFFFFFFFF;
|
||||
if(!sense) lastSector = (cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3] & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if(lastSector > 0)
|
||||
return trackList.ToArray();
|
||||
if(lastSector > 0) return trackList.ToArray();
|
||||
|
||||
if(!force)
|
||||
{
|
||||
stoppingErrorMessage?.
|
||||
Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");
|
||||
stoppingErrorMessage?.Invoke(Localization.Core
|
||||
.Could_not_find_Lead_Out_if_you_want_to_continue_use_force_option);
|
||||
|
||||
dumpLog?.WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");
|
||||
dumpLog?.WriteLine(Localization.Core.Could_not_find_Lead_Out_if_you_want_to_continue_use_force_option);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
updateStatus?.
|
||||
Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
|
||||
updateStatus?.Invoke(Localization.Core.WARNING_Could_not_find_Lead_Out_start_will_try_to_read_up_to);
|
||||
|
||||
dumpLog?.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
|
||||
dumpLog?.WriteLine(Localization.Core.WARNING_Could_not_find_Lead_Out_start_will_try_to_read_up_to);
|
||||
lastSector = 360000;
|
||||
|
||||
return trackList.ToArray();
|
||||
|
||||
@@ -27,26 +27,29 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Aaru.Checksums;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.CD;
|
||||
using Aaru.Decoders.SCSI;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using Track = Aaru.CommonTypes.Structs.Track;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -74,15 +77,13 @@ partial class Dump
|
||||
/// <param name="mcn">Disc media catalogue number</param>
|
||||
/// <param name="subchannelExtents">List of subchannels not yet dumped correctly</param>
|
||||
/// <param name="smallestPregapLbaPerTrack">List of smallest pregap relative address per track</param>
|
||||
void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, ExtentsULong extents,
|
||||
void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardware currentTry, ExtentsULong extents,
|
||||
bool newTrim, int offsetBytes, bool read6, bool read10, bool read12, 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, HashSet<int> subchannelExtents,
|
||||
Dictionary<byte, int> smallestPregapLbaPerTrack)
|
||||
{
|
||||
DateTime start;
|
||||
DateTime end;
|
||||
var sense = true; // Sense indicator
|
||||
byte[] cmdBuf = null; // Data buffer
|
||||
double cmdDuration = 0; // Command execution time
|
||||
@@ -91,36 +92,20 @@ partial class Dump
|
||||
byte[] senseBuf = null;
|
||||
var outputOptical = _outputPlugin as IWritableOpticalImage;
|
||||
|
||||
switch(supportedSubchannel)
|
||||
{
|
||||
case MmcSubchannel.None:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.None;
|
||||
supportedPlextorSubchannel = supportedSubchannel switch
|
||||
{
|
||||
MmcSubchannel.None => PlextorSubchannel.None,
|
||||
MmcSubchannel.Raw => PlextorSubchannel.Pack,
|
||||
MmcSubchannel.Q16 => PlextorSubchannel.Q16,
|
||||
_ => PlextorSubchannel.None
|
||||
};
|
||||
|
||||
break;
|
||||
case MmcSubchannel.Raw:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.Pack;
|
||||
if(_resume.BadBlocks.Count <= 0 || _aborted || !_trim || !newTrim) return;
|
||||
|
||||
break;
|
||||
case MmcSubchannel.Q16:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.Q16;
|
||||
|
||||
break;
|
||||
default:
|
||||
supportedPlextorSubchannel = PlextorSubchannel.None;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(_resume.BadBlocks.Count <= 0 ||
|
||||
_aborted ||
|
||||
!_trim ||
|
||||
!newTrim)
|
||||
return;
|
||||
|
||||
start = DateTime.UtcNow;
|
||||
UpdateStatus?.Invoke("Trimming skipped sectors");
|
||||
_dumpLog.WriteLine("Trimming skipped sectors");
|
||||
UpdateStatus?.Invoke(Localization.Core.Trimming_skipped_sectors);
|
||||
_dumpLog.WriteLine(Localization.Core.Trimming_skipped_sectors);
|
||||
InitProgress?.Invoke();
|
||||
_trimStopwatch.Restart();
|
||||
|
||||
trimStart:
|
||||
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
||||
@@ -132,51 +117,85 @@ partial class Dump
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.Invoke($"Trimming sector {badSector}");
|
||||
PulseProgress?.Invoke(string.Format(Localization.Core.Trimming_sector_0, badSector));
|
||||
|
||||
Track track = tracks.OrderBy(t => t.StartSector).LastOrDefault(t => badSector >= t.StartSector);
|
||||
|
||||
byte sectorsToTrim = 1;
|
||||
var badSectorToRead = (uint)badSector;
|
||||
|
||||
if(_fixOffset &&
|
||||
audioExtents.Contains(badSector) &&
|
||||
offsetBytes != 0)
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
{
|
||||
if(offsetBytes > 0)
|
||||
badSectorToRead -= (uint)sectorsForOffset;
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(badSectorToRead == 0)
|
||||
badSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
||||
else
|
||||
badSectorToRead -= (uint)sectorsForOffset;
|
||||
}
|
||||
|
||||
sectorsToTrim = (byte)(sectorsForOffset + 1);
|
||||
sectorsToTrim += (byte)sectorsForOffset;
|
||||
}
|
||||
|
||||
if(_supportsPlextorD8 && audioExtents.Contains(badSector))
|
||||
sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim,
|
||||
supportedPlextorSubchannel, out cmdDuration);
|
||||
{
|
||||
sense = ReadPlextorWithSubchannel(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
sectorsToTrim,
|
||||
supportedPlextorSubchannel,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(readcd)
|
||||
{
|
||||
if(audioExtents.Contains(badSector))
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim,
|
||||
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false,
|
||||
MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
sectorsToTrim,
|
||||
MmcSectorTypes.Cdda,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MmcHeaderCodes.None,
|
||||
true,
|
||||
false,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
DecodedSense? decSense = Sense.Decode(senseBuf);
|
||||
|
||||
// Try to workaround firmware
|
||||
if(decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05 ||
|
||||
decSense?.ASC == 0x64)
|
||||
if(decSense is { ASC: 0x11, ASCQ: 0x05 } || decSense?.ASC == 0x64)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
|
||||
true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out _,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
sectorsToTrim,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration2);
|
||||
|
||||
cmdDuration += cmdDuration2;
|
||||
@@ -185,23 +204,121 @@ partial class Dump
|
||||
}
|
||||
else
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim,
|
||||
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
|
||||
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
sectorsToTrim,
|
||||
MmcSectorTypes.AllTypes,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
MmcHeaderCodes.AllHeaders,
|
||||
true,
|
||||
true,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
DecodedSense? decSense = Sense.Decode(senseBuf);
|
||||
|
||||
// Try to workaround firmware
|
||||
if(decSense?.ASC == 0x64)
|
||||
if(decSense is { ASC: 0x11, ASCQ: 0x05 } || decSense?.ASC == 0x64)
|
||||
{
|
||||
sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim,
|
||||
MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true,
|
||||
false, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
||||
byte scrambledSectorsToTrim = sectorsToTrim;
|
||||
uint scrambledBadSectorToRead = badSectorToRead;
|
||||
|
||||
// Contrary to normal read, this must always be offset fixed, because it's data not audio
|
||||
if(offsetBytes != 0)
|
||||
{
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(scrambledBadSectorToRead == 0)
|
||||
scrambledBadSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
||||
else
|
||||
scrambledBadSectorToRead -= (uint)sectorsForOffset;
|
||||
}
|
||||
|
||||
scrambledSectorsToTrim += (byte)sectorsForOffset;
|
||||
}
|
||||
|
||||
sense = _dev.ReadCd(out cmdBuf,
|
||||
out _,
|
||||
scrambledBadSectorToRead,
|
||||
blockSize,
|
||||
scrambledSectorsToTrim,
|
||||
MmcSectorTypes.Cdda,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
MmcHeaderCodes.None,
|
||||
true,
|
||||
false,
|
||||
MmcErrorField.None,
|
||||
supportedSubchannel,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration2);
|
||||
|
||||
cmdDuration += cmdDuration2;
|
||||
|
||||
if(!sense)
|
||||
{
|
||||
uint scrambledBlocksToRead = scrambledSectorsToTrim;
|
||||
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref scrambledBlocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
|
||||
// Descramble
|
||||
cmdBuf = Sector.Scramble(cmdBuf);
|
||||
|
||||
// Check valid sector
|
||||
CdChecksums.CheckCdSector(cmdBuf,
|
||||
out bool? correctEccP,
|
||||
out bool? correctEccQ,
|
||||
out bool? correctEdc);
|
||||
|
||||
// Check mode, set sense if EDC/ECC validity is not correct
|
||||
switch(cmdBuf[15] & 0x03)
|
||||
{
|
||||
case 0:
|
||||
|
||||
for(var c = 16; c < 2352; c++)
|
||||
{
|
||||
if(cmdBuf[c] == 0x00) continue;
|
||||
|
||||
sense = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
sense = correctEdc != true || correctEccP != true || correctEccQ != true;
|
||||
|
||||
break;
|
||||
case 2:
|
||||
if((cmdBuf[18] & 0x20) != 0x20)
|
||||
{
|
||||
if(correctEccP != true) sense = true;
|
||||
|
||||
if(correctEccQ != true) sense = true;
|
||||
}
|
||||
|
||||
if(correctEdc != true) sense = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,17 +326,64 @@ partial class Dump
|
||||
totalDuration += cmdDuration;
|
||||
}
|
||||
else if(read16)
|
||||
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, badSectorToRead, blockSize, 0,
|
||||
sectorsToTrim, false, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read16(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
0,
|
||||
sectorsToTrim,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read12)
|
||||
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead, blockSize,
|
||||
0, sectorsToTrim, false, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read12(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
0,
|
||||
sectorsToTrim,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read10)
|
||||
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead, blockSize,
|
||||
0, sectorsToTrim, _dev.Timeout, out cmdDuration);
|
||||
{
|
||||
sense = _dev.Read10(out cmdBuf,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
0,
|
||||
sectorsToTrim,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
else if(read6)
|
||||
sense = _dev.Read6(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, _dev.Timeout,
|
||||
{
|
||||
sense = _dev.Read6(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToRead,
|
||||
blockSize,
|
||||
sectorsToTrim,
|
||||
_dev.Timeout,
|
||||
out cmdDuration);
|
||||
}
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
@@ -230,30 +394,35 @@ partial class Dump
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
}
|
||||
|
||||
// Because one block has been partially used to fix the offset
|
||||
if(_fixOffset &&
|
||||
audioExtents.Contains(badSector) &&
|
||||
offsetBytes != 0)
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
{
|
||||
uint blocksToRead = sectorsToTrim;
|
||||
|
||||
FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, subSize,
|
||||
ref cmdBuf, blockSize, false);
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref blocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
}
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
var data = new byte[sectorSize];
|
||||
var sub = new byte[subSize];
|
||||
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
||||
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
||||
|
||||
if(supportsLongSectors)
|
||||
outputOptical.WriteSectorLong(data, badSector);
|
||||
@@ -262,23 +431,33 @@ partial class Dump
|
||||
|
||||
ulong trkStartBefore = track.StartSector;
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel,
|
||||
sub, badSector, 1, subLog, isrcs,
|
||||
(byte)track.Sequence, ref mcn, tracks,
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
badSector,
|
||||
1,
|
||||
subLog,
|
||||
isrcs,
|
||||
(byte)track.Sequence,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition, outputOptical,
|
||||
_fixSubchannel, _fixSubchannelCrc,
|
||||
_dumpLog, UpdateStatus,
|
||||
smallestPregapLbaPerTrack, true, out _);
|
||||
_fixSubchannelPosition,
|
||||
outputOptical,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
_dumpLog,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out _);
|
||||
|
||||
// Set tracks and go back
|
||||
if(!indexesChanged)
|
||||
continue;
|
||||
if(!indexesChanged) continue;
|
||||
|
||||
outputOptical.SetTracks(tracks.ToList());
|
||||
|
||||
if(track.StartSector != trkStartBefore &&
|
||||
!_resume.BadBlocks.Contains(track.StartSector))
|
||||
if(track.StartSector != trkStartBefore && !_resume.BadBlocks.Contains(track.StartSector))
|
||||
{
|
||||
_resume.BadBlocks.Add(track.StartSector);
|
||||
|
||||
@@ -296,9 +475,13 @@ partial class Dump
|
||||
outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector);
|
||||
}
|
||||
|
||||
_trimStopwatch.Stop();
|
||||
EndProgress?.Invoke();
|
||||
end = DateTime.UtcNow;
|
||||
UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds.");
|
||||
_dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Trimming_finished_in_0,
|
||||
_trimStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Trimming_finished_in_0,
|
||||
_trimStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user