Merge branch 'devel' into rebecca/first-lba-broken-workaround

This commit is contained in:
2024-10-20 00:59:38 +01:00
committed by GitHub
2265 changed files with 423246 additions and 126945 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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 _);
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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)));
}
}