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:
File diff suppressed because it is too large
Load Diff
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -27,27 +27,29 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2020-2022 Rebecca Wallander
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2020-2024 Rebecca Wallander
|
||||
// ****************************************************************************/
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using System.Text.Json;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Database;
|
||||
using Aaru.Devices;
|
||||
using Aaru.Settings;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
/// <summary>Subchannel requested to dump</summary>
|
||||
public enum DumpSubchannel
|
||||
@@ -66,49 +68,61 @@ public enum DumpSubchannel
|
||||
|
||||
public partial class Dump
|
||||
{
|
||||
readonly bool _debug;
|
||||
readonly Device _dev;
|
||||
readonly string _devicePath;
|
||||
readonly bool _doResume;
|
||||
readonly DumpLog _dumpLog;
|
||||
readonly bool _dumpRaw;
|
||||
readonly Encoding _encoding;
|
||||
readonly ErrorLog _errorLog;
|
||||
readonly bool _fixSubchannel;
|
||||
readonly bool _fixSubchannelCrc;
|
||||
readonly bool _fixSubchannelPosition;
|
||||
readonly bool _force;
|
||||
readonly Dictionary<string, string> _formatOptions;
|
||||
readonly bool _generateSubchannels;
|
||||
readonly bool _metadata;
|
||||
readonly string _outputPath;
|
||||
readonly IBaseWritableImage _outputPlugin;
|
||||
readonly string _outputPrefix;
|
||||
readonly bool _persistent;
|
||||
readonly CICMMetadataType _preSidecar;
|
||||
readonly bool _private;
|
||||
readonly ushort _retryPasses;
|
||||
readonly bool _retrySubchannel;
|
||||
readonly bool _stopOnError;
|
||||
readonly bool _storeEncrypted;
|
||||
readonly DumpSubchannel _subchannel;
|
||||
readonly bool _titleKeys;
|
||||
readonly bool _trim;
|
||||
bool _aborted;
|
||||
AaruContext _ctx; // Main database context
|
||||
Database.Models.Device _dbDev; // Device database entry
|
||||
bool _dumpFirstTrackPregap;
|
||||
bool _fixOffset;
|
||||
readonly uint _ignoreCdrRunOuts;
|
||||
uint _maximumReadable; // Maximum number of sectors drive can read at once
|
||||
Resume _resume;
|
||||
Sidecar _sidecarClass;
|
||||
uint _skip;
|
||||
bool _skipCdireadyHole;
|
||||
int _speed;
|
||||
int _speedMultiplier;
|
||||
bool _supportsPlextorD8;
|
||||
bool _useBufferedReads;
|
||||
const string PREGAP_MODULE_NAME = "Pregap calculator";
|
||||
const string MODULE_NAME = "Media dumping";
|
||||
static readonly TimeSpan _oneSecond = 1.Seconds();
|
||||
readonly bool _createGraph;
|
||||
readonly bool _debug;
|
||||
readonly Device _dev;
|
||||
readonly string _devicePath;
|
||||
readonly uint _dimensions;
|
||||
readonly bool _doResume;
|
||||
readonly DumpLog _dumpLog;
|
||||
readonly bool _dumpRaw;
|
||||
readonly Stopwatch _dumpStopwatch;
|
||||
readonly Encoding _encoding;
|
||||
readonly ErrorLog _errorLog;
|
||||
readonly bool _fixSubchannel;
|
||||
readonly bool _fixSubchannelCrc;
|
||||
readonly bool _fixSubchannelPosition;
|
||||
readonly bool _force;
|
||||
readonly Dictionary<string, string> _formatOptions;
|
||||
readonly bool _generateSubchannels;
|
||||
readonly uint _ignoreCdrRunOuts;
|
||||
readonly Stopwatch _imageCloseStopwatch;
|
||||
readonly bool _metadata;
|
||||
readonly string _outputPath;
|
||||
readonly IBaseWritableImage _outputPlugin;
|
||||
readonly string _outputPrefix;
|
||||
readonly bool _persistent;
|
||||
readonly Metadata _preSidecar;
|
||||
readonly bool _private;
|
||||
readonly ushort _retryPasses;
|
||||
readonly bool _retrySubchannel;
|
||||
readonly Stopwatch _sidecarStopwatch;
|
||||
readonly Stopwatch _speedStopwatch;
|
||||
readonly bool _stopOnError;
|
||||
readonly bool _storeEncrypted;
|
||||
readonly DumpSubchannel _subchannel;
|
||||
readonly bool _titleKeys;
|
||||
readonly bool _trim;
|
||||
readonly Stopwatch _trimStopwatch;
|
||||
readonly Stopwatch _writeStopwatch;
|
||||
bool _aborted;
|
||||
AaruContext _ctx; // Main database context
|
||||
Database.Models.Device _dbDev; // Device database entry
|
||||
bool _dumpFirstTrackPregap;
|
||||
bool _fixOffset;
|
||||
uint _maximumReadable; // Maximum number of sectors drive can read at once
|
||||
IMediaGraph _mediaGraph;
|
||||
Resume _resume;
|
||||
Sidecar _sidecarClass;
|
||||
uint _skip;
|
||||
bool _skipCdireadyHole;
|
||||
int _speed;
|
||||
int _speedMultiplier;
|
||||
bool _supportsPlextorD8;
|
||||
bool _useBufferedReads;
|
||||
|
||||
/// <summary>Initializes dumpers</summary>
|
||||
/// <param name="doResume">Should resume?</param>
|
||||
@@ -151,14 +165,16 @@ public partial class Dump
|
||||
/// <param name="storeEncrypted">Store encrypted data as is</param>
|
||||
/// <param name="titleKeys">Dump DVD CSS title keys</param>
|
||||
/// <param name="ignoreCdrRunOuts">How many CD-R(W) run end sectors to ignore and regenerate</param>
|
||||
public Dump(bool doResume, Device dev, string devicePath, IBaseWritableImage outputPlugin, ushort retryPasses,
|
||||
bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog,
|
||||
/// <param name="createGraph">If set to <c>true</c> creates a graph of the dump.</param>
|
||||
/// <param name="dimensions">Dimensions of graph in pixels for a square</param>
|
||||
public Dump(bool doResume, Device dev, string devicePath, IBaseWritableImage outputPlugin, ushort retryPasses,
|
||||
bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog,
|
||||
Encoding encoding, string outputPrefix, string outputPath, Dictionary<string, string> formatOptions,
|
||||
CICMMetadataType preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap,
|
||||
bool fixOffset, bool debug, DumpSubchannel subchannel, int speed, bool @private,
|
||||
bool fixSubchannelPosition, bool retrySubchannel, bool fixSubchannel, bool fixSubchannelCrc,
|
||||
bool skipCdireadyHole, ErrorLog errorLog, bool generateSubchannels, uint maximumReadable,
|
||||
bool useBufferedReads, bool storeEncrypted, bool titleKeys, uint ignoreCdrRunOuts)
|
||||
Metadata preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap, bool fixOffset,
|
||||
bool debug, DumpSubchannel subchannel, int speed, bool @private, bool fixSubchannelPosition,
|
||||
bool retrySubchannel, bool fixSubchannel, bool fixSubchannelCrc, bool skipCdireadyHole,
|
||||
ErrorLog errorLog, bool generateSubchannels, uint maximumReadable, bool useBufferedReads,
|
||||
bool storeEncrypted, bool titleKeys, uint ignoreCdrRunOuts, bool createGraph, uint dimensions)
|
||||
{
|
||||
_doResume = doResume;
|
||||
_dev = dev;
|
||||
@@ -199,32 +215,39 @@ public partial class Dump
|
||||
_storeEncrypted = storeEncrypted;
|
||||
_titleKeys = titleKeys;
|
||||
_ignoreCdrRunOuts = ignoreCdrRunOuts;
|
||||
_createGraph = createGraph;
|
||||
_dimensions = dimensions;
|
||||
_dumpStopwatch = new Stopwatch();
|
||||
_sidecarStopwatch = new Stopwatch();
|
||||
_speedStopwatch = new Stopwatch();
|
||||
_trimStopwatch = new Stopwatch();
|
||||
_writeStopwatch = new Stopwatch();
|
||||
_imageCloseStopwatch = new Stopwatch();
|
||||
}
|
||||
|
||||
/// <summary>Starts dumping with the established fields and autodetecting the device type</summary>
|
||||
public void Start()
|
||||
{
|
||||
// Open main database
|
||||
_ctx = AaruContext.Create(Settings.MainDbPath);
|
||||
_ctx = AaruContext.Create(Settings.Settings.MainDbPath);
|
||||
|
||||
// Search for device in main database
|
||||
_dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model &&
|
||||
_dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer &&
|
||||
d.Model == _dev.Model &&
|
||||
d.Revision == _dev.FirmwareRevision);
|
||||
|
||||
if(_dbDev is null)
|
||||
{
|
||||
_dumpLog.WriteLine("Device not in database, please create a device report and attach it to a Github issue.");
|
||||
_dumpLog.WriteLine(Localization.Core.Device_not_in_database);
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke("Device not in database, please create a device report and attach it to a Github issue.");
|
||||
UpdateStatus?.Invoke(Localization.Core.Device_not_in_database);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dumpLog.WriteLine($"Device in database since {_dbDev.LastSynchronized}.");
|
||||
UpdateStatus?.Invoke($"Device in database since {_dbDev.LastSynchronized}.");
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Device_in_database_since_0, _dbDev.LastSynchronized));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_in_database_since_0, _dbDev.LastSynchronized));
|
||||
|
||||
if(_dbDev.OptimalMultipleSectorsRead > 0)
|
||||
_maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead;
|
||||
if(_dbDev.OptimalMultipleSectorsRead > 0) _maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead;
|
||||
}
|
||||
|
||||
switch(_dev.IsUsb)
|
||||
@@ -259,9 +282,9 @@ public partial class Dump
|
||||
|
||||
break;
|
||||
default:
|
||||
_dumpLog.WriteLine("Unknown device type.");
|
||||
_dumpLog.WriteLine(Localization.Core.Unknown_device_type);
|
||||
_dumpLog.Close();
|
||||
StoppingErrorMessage?.Invoke("Unknown device type.");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Unknown_device_type);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -272,19 +295,31 @@ public partial class Dump
|
||||
_errorLog.Close();
|
||||
_dumpLog.Close();
|
||||
|
||||
if(_resume == null ||
|
||||
!_doResume)
|
||||
return;
|
||||
if(_resume == null || !_doResume) return;
|
||||
|
||||
_resume.LastWriteDate = DateTime.UtcNow;
|
||||
_resume.BadBlocks.Sort();
|
||||
|
||||
if(File.Exists(_outputPrefix + ".resume.xml"))
|
||||
File.Delete(_outputPrefix + ".resume.xml");
|
||||
if(_createGraph && _mediaGraph is not null)
|
||||
{
|
||||
_mediaGraph?.PaintSectorsBad(_resume.BadBlocks);
|
||||
_mediaGraph?.WriteTo($"{_outputPrefix}.graph.png");
|
||||
}
|
||||
|
||||
if(File.Exists(_outputPrefix + ".resume.xml")) File.Delete(_outputPrefix + ".resume.xml");
|
||||
|
||||
if(File.Exists(_outputPrefix + ".resume.json")) File.Delete(_outputPrefix + ".resume.json");
|
||||
|
||||
var fs = new FileStream(_outputPrefix + ".resume.json", FileMode.Create, FileAccess.ReadWrite);
|
||||
|
||||
JsonSerializer.Serialize(fs,
|
||||
new ResumeJson
|
||||
{
|
||||
Resume = _resume
|
||||
},
|
||||
typeof(ResumeJson),
|
||||
ResumeJsonContext.Default);
|
||||
|
||||
var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite);
|
||||
var xs = new XmlSerializer(_resume.GetType());
|
||||
xs.Serialize(fs, _resume);
|
||||
fs.Close();
|
||||
}
|
||||
|
||||
@@ -297,22 +332,31 @@ public partial class Dump
|
||||
|
||||
/// <summary>Event raised when the progress bar is not longer needed</summary>
|
||||
public event EndProgressHandler EndProgress;
|
||||
|
||||
/// <summary>Event raised when a progress bar is needed</summary>
|
||||
public event InitProgressHandler InitProgress;
|
||||
|
||||
/// <summary>Event raised to report status updates</summary>
|
||||
public event UpdateStatusHandler UpdateStatus;
|
||||
|
||||
/// <summary>Event raised to report a non-fatal error</summary>
|
||||
public event ErrorMessageHandler ErrorMessage;
|
||||
|
||||
/// <summary>Event raised to report a fatal error that stops the dumping operation and should call user's attention</summary>
|
||||
public event ErrorMessageHandler StoppingErrorMessage;
|
||||
|
||||
/// <summary>Event raised to update the values of a determinate progress bar</summary>
|
||||
public event UpdateProgressHandler UpdateProgress;
|
||||
|
||||
/// <summary>Event raised to update the status of an undeterminate progress bar</summary>
|
||||
public event PulseProgressHandler PulseProgress;
|
||||
|
||||
/// <summary>Event raised when the progress bar is not longer needed</summary>
|
||||
public event EndProgressHandler2 EndProgress2;
|
||||
|
||||
/// <summary>Event raised when a progress bar is needed</summary>
|
||||
public event InitProgressHandler2 InitProgress2;
|
||||
|
||||
/// <summary>Event raised to update the values of a determinate progress bar</summary>
|
||||
public event UpdateProgressHandler2 UpdateProgress2;
|
||||
}
|
||||
@@ -27,66 +27,53 @@
|
||||
// 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 System.Linq;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Core.Graphics;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using Humanizer.Localisation;
|
||||
using Version = Aaru.CommonTypes.Interop.Version;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
public partial class Dump
|
||||
{
|
||||
static readonly byte[] _sfcExtension =
|
||||
{
|
||||
0x53, 0x46, 0x43
|
||||
};
|
||||
|
||||
static readonly byte[] _genesisExtension =
|
||||
{
|
||||
0x42, 0x49, 0x4E
|
||||
};
|
||||
|
||||
static readonly byte[] _smsExtension =
|
||||
{
|
||||
0x53, 0x4D, 0x53
|
||||
};
|
||||
|
||||
static readonly byte[] _z64Extension =
|
||||
{
|
||||
0x5A, 0x36, 0x34
|
||||
};
|
||||
|
||||
static readonly byte[] _gbExtension =
|
||||
{
|
||||
0x47, 0x42, 0x20
|
||||
};
|
||||
|
||||
static readonly byte[] _gbcExtension =
|
||||
{
|
||||
0x47, 0x42, 0x43
|
||||
};
|
||||
|
||||
static readonly byte[] _gbaExtension =
|
||||
{
|
||||
0x47, 0x42, 0x41
|
||||
};
|
||||
static readonly byte[] _sfcExtension = "SFC"u8.ToArray();
|
||||
static readonly byte[] _genesisExtension = "BIN"u8.ToArray();
|
||||
static readonly byte[] _smsExtension = "SMS"u8.ToArray();
|
||||
static readonly byte[] _z64Extension = "Z64"u8.ToArray();
|
||||
static readonly byte[] _gbExtension = "GB "u8.ToArray();
|
||||
static readonly byte[] _gbcExtension = "GBC"u8.ToArray();
|
||||
static readonly byte[] _gbaExtension = "GBA"u8.ToArray();
|
||||
|
||||
/// <summary>Dumps a game cartridge using a Retrode adapter</summary>
|
||||
void Retrode()
|
||||
{
|
||||
bool sense = _dev.Read10(out byte[] buffer, out _, 0, false, true, false, false, 0, 512, 0, 1, _dev.Timeout,
|
||||
bool sense = _dev.Read10(out byte[] buffer,
|
||||
out _,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -98,8 +85,8 @@ public partial class Dump
|
||||
// UMDs are stored inside a FAT16 volume
|
||||
if(!tmp.SequenceEqual(_fatSignature))
|
||||
{
|
||||
_dumpLog.WriteLine("Retrode partition not recognized, not dumping...");
|
||||
StoppingErrorMessage?.Invoke("Retrode partition not recognized, not dumping...");
|
||||
_dumpLog.WriteLine(Localization.Core.Retrode_partition_not_recognized_not_dumping);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Retrode_partition_not_recognized_not_dumping);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -110,15 +97,15 @@ public partial class Dump
|
||||
var rootSize = (ushort)(((buffer[0x12] << 8) + buffer[0x11]) * 32 / 512);
|
||||
byte sectorsPerCluster = buffer[0x0D];
|
||||
|
||||
UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}...");
|
||||
_dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_root_directory_in_sector_0, rootStart));
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_root_directory_in_sector_0, rootStart);
|
||||
|
||||
sense = _dev.Read10(out buffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, _dev.Timeout, out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -179,21 +166,17 @@ public partial class Dump
|
||||
break;
|
||||
}
|
||||
|
||||
if(tmp.SequenceEqual(_gbaExtension))
|
||||
{
|
||||
gbaFound = true;
|
||||
if(!tmp.SequenceEqual(_gbaExtension)) continue;
|
||||
|
||||
break;
|
||||
}
|
||||
gbaFound = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(!sfcFound &&
|
||||
!genesisFound &&
|
||||
!smsFound &&
|
||||
!n64Found)
|
||||
if(!sfcFound && !genesisFound && !smsFound && !n64Found && !gbaFound && !gbFound && !gbcFound)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("No cartridge found, not dumping...");
|
||||
_dumpLog.WriteLine("No cartridge found, not dumping...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.No_cartridge_found_not_dumping);
|
||||
_dumpLog.WriteLine(Localization.Core.No_cartridge_found_not_dumping);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -220,22 +203,34 @@ public partial class Dump
|
||||
double minSpeed = double.MaxValue;
|
||||
byte[] senseBuf;
|
||||
|
||||
if(_outputPlugin is not IByteAddressableImage outputBai ||
|
||||
!outputBai.SupportedMediaTypes.Contains(mediaType))
|
||||
if(_outputPlugin is not IByteAddressableImage outputBai || !outputBai.SupportedMediaTypes.Contains(mediaType))
|
||||
{
|
||||
_dumpLog.WriteLine("The specified format does not support the inserted cartridge...");
|
||||
StoppingErrorMessage?.Invoke("The specified format does not support the inserted cartridge...");
|
||||
_dumpLog.WriteLine(Localization.Core.The_specified_format_does_not_support_the_inserted_cartridge);
|
||||
|
||||
StoppingErrorMessage?.Invoke(Localization.Core
|
||||
.The_specified_format_does_not_support_the_inserted_cartridge);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sense = _dev.Read10(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, _dev.Timeout,
|
||||
sense = _dev.Read10(out byte[] readBuffer,
|
||||
out _,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -244,183 +239,243 @@ public partial class Dump
|
||||
uint romSectors = romSize / 512;
|
||||
uint romRemaining = romSize % 512;
|
||||
|
||||
if(romSize > 1073741824)
|
||||
UpdateStatus?.Invoke($"Cartridge has {romSize} bytes ({romSize / 1073741824d:F3} GiB)");
|
||||
else if(romSize > 1048576)
|
||||
UpdateStatus?.Invoke($"Cartridge has {romSize} bytes ({romSize / 1048576d:F3} MiB)");
|
||||
else if(romSize > 1024)
|
||||
UpdateStatus?.Invoke($"Cartridge has {romSize} bytes ({romSize / 1024d:F3} KiB)");
|
||||
else
|
||||
UpdateStatus?.Invoke($"Cartridge has {romSize} bytes");
|
||||
switch(romSize)
|
||||
{
|
||||
case > 1073741824:
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Cartridge_has_0_bytes_1_GiB,
|
||||
romSize,
|
||||
romSize / 1073741824d));
|
||||
|
||||
UpdateStatus?.Invoke($"Media identified as {mediaType}.");
|
||||
_dumpLog.WriteLine("Media identified as {0}.", mediaType);
|
||||
break;
|
||||
case > 1048576:
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Cartridge_has_0_bytes_1_MiB,
|
||||
romSize,
|
||||
romSize / 1048576d));
|
||||
|
||||
break;
|
||||
case > 1024:
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Cartridge_has_0_bytes_1_KiB,
|
||||
romSize,
|
||||
romSize / 1024d));
|
||||
|
||||
break;
|
||||
default:
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Cartridge_has_0_bytes, romSize));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_identified_as_0, mediaType));
|
||||
_dumpLog.WriteLine(Localization.Core.Media_identified_as_0, mediaType);
|
||||
|
||||
ErrorNumber ret = outputBai.Create(_outputPath, mediaType, _formatOptions, romSize);
|
||||
|
||||
// Cannot create image
|
||||
if(ret != ErrorNumber.NoError)
|
||||
{
|
||||
_dumpLog.WriteLine("Error {0} creating output image, not continuing.", ret);
|
||||
_dumpLog.WriteLine(Localization.Core.Error_0_creating_output_image_not_continuing, ret);
|
||||
_dumpLog.WriteLine(outputBai.ErrorMessage);
|
||||
|
||||
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Error_creating_output_image_not_continuing +
|
||||
Environment.NewLine +
|
||||
outputBai.ErrorMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime start = DateTime.UtcNow;
|
||||
double imageWriteDuration = 0;
|
||||
if(_createGraph) _mediaGraph = new BlockMap((int)_dimensions, (int)_dimensions, romSectors);
|
||||
|
||||
DateTime timeSpeedStart = DateTime.UtcNow;
|
||||
ulong sectorSpeedStart = 0;
|
||||
_dumpStopwatch.Restart();
|
||||
double imageWriteDuration = 0;
|
||||
|
||||
_speedStopwatch.Restart();
|
||||
ulong sectorSpeedStart = 0;
|
||||
InitProgress?.Invoke();
|
||||
|
||||
for(ulong i = 0; i < romSectors; i += blocksToRead)
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(romSectors - i < blocksToRead)
|
||||
blocksToRead = (uint)(romSectors - i);
|
||||
if(romSectors - i < blocksToRead) blocksToRead = (uint)(romSectors - i);
|
||||
|
||||
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 byte {i * 512} of {romSize} ({currentSpeed:F3} MiB/sec.)", (long)i * 512,
|
||||
UpdateProgress?.Invoke(string.Format(Localization.Core.Reading_byte_0_of_1_2,
|
||||
i * 512,
|
||||
romSize,
|
||||
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()),
|
||||
(long)i * 512,
|
||||
romSize);
|
||||
|
||||
sense = _dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)(startSector + i),
|
||||
512, 0, (ushort)blocksToRead, _dev.Timeout, out double cmdDuration);
|
||||
_speedStopwatch.Start();
|
||||
|
||||
sense = _dev.Read10(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)(startSector + i),
|
||||
512,
|
||||
0,
|
||||
(ushort)blocksToRead,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
DateTime writeStart = DateTime.Now;
|
||||
outputBai.WriteBytes(readBuffer, 0, readBuffer.Length, out _);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
_mediaGraph.PaintSectorsGood(i, blocksToRead);
|
||||
}
|
||||
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
|
||||
|
||||
_dumpLog.WriteLine("Skipping {0} bytes from errored byte {1}.", _skip * 512, i * 512);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_bytes_from_errored_byte_1, _skip * 512, i * 512);
|
||||
i += _skip - blocksToRead;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
|
||||
sectorSpeedStart += blocksToRead;
|
||||
|
||||
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
double elapsed = _speedStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
if(elapsed <= 0)
|
||||
continue;
|
||||
if(elapsed <= 0 || sectorSpeedStart * 512 < 524288) continue;
|
||||
|
||||
currentSpeed = sectorSpeedStart * 512 / (1048576 * elapsed);
|
||||
sectorSpeedStart = 0;
|
||||
timeSpeedStart = DateTime.UtcNow;
|
||||
_speedStopwatch.Reset();
|
||||
}
|
||||
|
||||
if(romRemaining > 0 &&
|
||||
!_aborted)
|
||||
_speedStopwatch.Stop();
|
||||
|
||||
if(romRemaining > 0 && !_aborted)
|
||||
{
|
||||
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 byte {romSectors * 512} of {romSize} ({currentSpeed:F3} MiB/sec.)",
|
||||
(long)romSectors * 512, romSize);
|
||||
UpdateProgress?.Invoke(string.Format(Localization.Core.Reading_byte_0_of_1_2,
|
||||
romSectors * 512,
|
||||
romSize,
|
||||
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()),
|
||||
(long)romSectors * 512,
|
||||
romSize);
|
||||
|
||||
sense = _dev.Read10(out readBuffer, out senseBuf, 0, false, true, false, false, romSectors, 512, 0, 1,
|
||||
_dev.Timeout, out double cmdDuration);
|
||||
sense = _dev.Read10(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
romSectors,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputBai.WriteBytes(readBuffer, 0, (int)romRemaining, out _);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
_writeStopwatch.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorLog?.WriteLine(romSectors, _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
|
||||
|
||||
_dumpLog.WriteLine("Skipping {0} bytes from errored byte {1}.", _skip * 512, romSectors * 512);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_bytes_from_errored_byte_1,
|
||||
_skip * 512,
|
||||
romSectors * 512);
|
||||
}
|
||||
}
|
||||
|
||||
DateTime end = DateTime.UtcNow;
|
||||
EndProgress?.Invoke();
|
||||
|
||||
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Dump_finished_in_0,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average dump speed {512 * (double)(romSectors + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_dump_speed_0,
|
||||
ByteSize.FromBytes(512 * (romSectors + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average write speed {512 * (double)(romSectors + 1) / 1024 / imageWriteDuration:F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_write_speed_0,
|
||||
ByteSize.FromBytes(512 * (romSectors + 1))
|
||||
.Per(imageWriteDuration.Seconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Dump_finished_in_0,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
|
||||
512 * (double)(romSectors + 1) / 1024 / (totalDuration / 1000));
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Average_dump_speed_0,
|
||||
ByteSize.FromBytes(512 * (romSectors + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
|
||||
512 * (double)(romSectors + 1) / 1024 / imageWriteDuration);
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Average_write_speed_0,
|
||||
ByteSize.FromBytes(512 * (romSectors + 1))
|
||||
.Per(imageWriteDuration.Seconds())
|
||||
.Humanize()));
|
||||
|
||||
var metadata = new ImageInfo
|
||||
var metadata = new CommonTypes.Structs.ImageInfo
|
||||
{
|
||||
Application = "Aaru",
|
||||
ApplicationVersion = Version.GetVersion()
|
||||
};
|
||||
|
||||
if(!outputBai.SetMetadata(metadata))
|
||||
ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
|
||||
if(!outputBai.SetImageInfo(metadata))
|
||||
{
|
||||
ErrorMessage?.Invoke(Localization.Core.Error_0_setting_metadata +
|
||||
Environment.NewLine +
|
||||
outputBai.ErrorMessage);
|
||||
}
|
||||
|
||||
// TODO: Set dump hardware
|
||||
//outputBAI.SetDumpHardware();
|
||||
|
||||
if(_preSidecar != null)
|
||||
outputBai.SetCicmMetadata(_preSidecar);
|
||||
if(_preSidecar != null) outputBai.SetMetadata(_preSidecar);
|
||||
|
||||
_dumpLog.WriteLine("Closing output file.");
|
||||
UpdateStatus?.Invoke("Closing output file.");
|
||||
DateTime closeStart = DateTime.Now;
|
||||
_dumpLog.WriteLine(Localization.Core.Closing_output_file);
|
||||
UpdateStatus?.Invoke(Localization.Core.Closing_output_file);
|
||||
_imageCloseStopwatch.Restart();
|
||||
outputBai.Close();
|
||||
DateTime closeEnd = DateTime.Now;
|
||||
_dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
|
||||
_imageCloseStopwatch.Stop();
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Closed_in_0,
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
|
||||
|
||||
if(_aborted)
|
||||
{
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -433,17 +488,30 @@ public partial class Dump
|
||||
*/
|
||||
UpdateStatus?.Invoke("");
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core
|
||||
.Took_a_total_of_0_1_processing_commands_2_checksumming_3_writing_4_closing,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second),
|
||||
totalDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
|
||||
totalChkDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
|
||||
imageWriteDuration.Seconds().Humanize(minUnit: TimeUnit.Second),
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average speed: {512 * (double)(romSectors + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_speed_0,
|
||||
ByteSize.FromBytes(512 * (romSectors + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
if(maxSpeed > 0)
|
||||
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
|
||||
{
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Fastest_speed_burst_0,
|
||||
ByteSize.FromMegabytes(maxSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
if(minSpeed is > 0 and < double.MaxValue)
|
||||
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
|
||||
{
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Slowest_speed_burst_0,
|
||||
ByteSize.FromMegabytes(minSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke("");
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,22 +27,22 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using System.Text.Json;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Schemas;
|
||||
using MediaType = Aaru.CommonTypes.MediaType;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using Humanizer.Localisation;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -55,18 +55,17 @@ partial class Dump
|
||||
/// <param name="sessions">Disc sessions</param>
|
||||
/// <param name="totalChkDuration">Total time spent doing checksums</param>
|
||||
/// <param name="discOffset">Disc write offset</param>
|
||||
void WriteOpticalSidecar(uint blockSize, ulong blocks, MediaType mediaType, LayersType layers,
|
||||
void WriteOpticalSidecar(uint blockSize, ulong blocks, MediaType mediaType, Layers layers,
|
||||
Dictionary<MediaTagType, byte[]> mediaTags, int sessions, out double totalChkDuration,
|
||||
int? discOffset)
|
||||
{
|
||||
_dumpLog.WriteLine("Creating sidecar.");
|
||||
var filters = new FiltersList();
|
||||
IFilter filter = filters.GetFilter(_outputPath);
|
||||
_dumpLog.WriteLine(Localization.Core.Creating_sidecar);
|
||||
IFilter filter = PluginRegister.Singleton.GetFilter(_outputPath);
|
||||
totalChkDuration = 0;
|
||||
|
||||
if(ImageFormat.Detect(filter) is not IMediaImage inputPlugin)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Could not detect image format.");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_detect_image_format);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -75,12 +74,12 @@ partial class Dump
|
||||
|
||||
if(opened != ErrorNumber.NoError)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke($"Error {opened} opening created image.");
|
||||
StoppingErrorMessage?.Invoke(string.Format(Localization.Core.Error_0_opening_created_image, opened));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime chkStart = DateTime.UtcNow;
|
||||
_sidecarStopwatch.Restart();
|
||||
|
||||
// ReSharper disable once UseObjectOrCollectionInitializer
|
||||
_sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
|
||||
@@ -91,66 +90,79 @@ partial class Dump
|
||||
_sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
|
||||
_sidecarClass.EndProgressEvent2 += EndProgress2;
|
||||
_sidecarClass.UpdateStatusEvent += UpdateStatus;
|
||||
CICMMetadataType sidecar = _sidecarClass.Create();
|
||||
DateTime end = DateTime.UtcNow;
|
||||
Metadata sidecar = _sidecarClass.Create();
|
||||
_sidecarStopwatch.Stop();
|
||||
|
||||
if(_aborted)
|
||||
return;
|
||||
if(_aborted) return;
|
||||
|
||||
totalChkDuration = (end - chkStart).TotalMilliseconds;
|
||||
_dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
|
||||
totalChkDuration = _sidecarStopwatch.Elapsed.TotalMilliseconds;
|
||||
|
||||
_dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
|
||||
_dumpLog.WriteLine(Localization.Core.Sidecar_created_in_0,
|
||||
_sidecarStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Average_checksum_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalChkDuration.Milliseconds())
|
||||
.Humanize());
|
||||
|
||||
if(_preSidecar != null)
|
||||
{
|
||||
_preSidecar.OpticalDisc = sidecar.OpticalDisc;
|
||||
sidecar = _preSidecar;
|
||||
_preSidecar.OpticalDiscs = sidecar.OpticalDiscs;
|
||||
sidecar = _preSidecar;
|
||||
}
|
||||
|
||||
List<(ulong start, string type)> filesystems = new();
|
||||
List<(ulong start, string type)> filesystems = [];
|
||||
|
||||
if(sidecar.OpticalDisc[0].Track != null)
|
||||
filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track
|
||||
where xmlTrack.FileSystemInformation != null
|
||||
from partition in xmlTrack.FileSystemInformation where partition.FileSystems != null
|
||||
if(sidecar.OpticalDiscs[0].Track != null)
|
||||
{
|
||||
filesystems.AddRange(from xmlTrack in sidecar.OpticalDiscs[0].Track
|
||||
where xmlTrack.FileSystemInformation != null
|
||||
from partition in xmlTrack.FileSystemInformation
|
||||
where partition.FileSystems != null
|
||||
from fileSystem in partition.FileSystems
|
||||
select (partition.StartSector, fileSystem.Type));
|
||||
|
||||
if(filesystems.Count > 0)
|
||||
foreach(var filesystem in filesystems.Select(o => new
|
||||
{
|
||||
o.start,
|
||||
o.type
|
||||
}).Distinct())
|
||||
_dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
|
||||
|
||||
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(mediaType);
|
||||
(string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType);
|
||||
sidecar.OpticalDisc[0].DiscType = discType.type;
|
||||
sidecar.OpticalDisc[0].DiscSubType = discType.subType;
|
||||
sidecar.OpticalDisc[0].DumpHardwareArray = _resume.Tries.ToArray();
|
||||
sidecar.OpticalDisc[0].Sessions = (uint)sessions;
|
||||
sidecar.OpticalDisc[0].Layers = layers;
|
||||
|
||||
if(discOffset.HasValue)
|
||||
{
|
||||
sidecar.OpticalDisc[0].Offset = (int)(discOffset / 4);
|
||||
sidecar.OpticalDisc[0].OffsetSpecified = true;
|
||||
}
|
||||
|
||||
if(filesystems.Count > 0)
|
||||
{
|
||||
foreach(var filesystem in filesystems.Select(o => new
|
||||
{
|
||||
o.start,
|
||||
o.type
|
||||
})
|
||||
.Distinct())
|
||||
_dumpLog.WriteLine(Localization.Core.Found_filesystem_0_at_sector_1, filesystem.type, filesystem.start);
|
||||
}
|
||||
|
||||
sidecar.OpticalDiscs[0].Dimensions = Dimensions.FromMediaType(mediaType);
|
||||
(string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType);
|
||||
sidecar.OpticalDiscs[0].DiscType = discType.type;
|
||||
sidecar.OpticalDiscs[0].DiscSubType = discType.subType;
|
||||
sidecar.OpticalDiscs[0].DumpHardware = _resume.Tries;
|
||||
sidecar.OpticalDiscs[0].Sessions = (uint)sessions;
|
||||
sidecar.OpticalDiscs[0].Layers = layers;
|
||||
|
||||
if(discOffset.HasValue) sidecar.OpticalDiscs[0].Offset = (int)(discOffset / 4);
|
||||
|
||||
if(mediaTags != null)
|
||||
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags.Where(tag => _outputPlugin.SupportedMediaTags.
|
||||
Contains(tag.Key)))
|
||||
{
|
||||
foreach(KeyValuePair<MediaTagType, byte[]> tag in
|
||||
mediaTags.Where(tag => _outputPlugin.SupportedMediaTags.Contains(tag.Key)))
|
||||
AddMediaTagToSidecar(_outputPath, tag.Key, tag.Value, ref sidecar);
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke("Writing metadata sidecar");
|
||||
UpdateStatus?.Invoke(Localization.Core.Writing_metadata_sidecar);
|
||||
|
||||
var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);
|
||||
var jsonFs = new FileStream(_outputPrefix + ".metadata.json", FileMode.Create);
|
||||
|
||||
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
|
||||
xmlSer.Serialize(xmlFs, sidecar);
|
||||
xmlFs.Close();
|
||||
JsonSerializer.Serialize(jsonFs,
|
||||
new MetadataJson
|
||||
{
|
||||
AaruMetadata = sidecar
|
||||
},
|
||||
typeof(MetadataJson),
|
||||
MetadataJsonContext.Default);
|
||||
|
||||
jsonFs.Close();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
@@ -37,5 +37,5 @@ namespace Aaru.Core.Devices.Dumping;
|
||||
public partial class Dump
|
||||
{
|
||||
/// <summary>Dumps an NVMe device</summary>
|
||||
public void NVMe() => StoppingErrorMessage?.Invoke("NVMe devices not yet supported.");
|
||||
public void NVMe() => StoppingErrorMessage?.Invoke(Localization.Core.NVMe_devices_not_yet_supported);
|
||||
}
|
||||
@@ -28,31 +28,32 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using System.Text.Json;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Console;
|
||||
using Aaru.Core.Graphics;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.SCSI;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using MediaType = Aaru.CommonTypes.MediaType;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using Humanizer.Localisation;
|
||||
using Version = Aaru.CommonTypes.Interop.Version;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
public partial class Dump
|
||||
{
|
||||
[SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")]
|
||||
@@ -65,15 +66,13 @@ public partial class Dump
|
||||
double maxSpeed = double.MinValue;
|
||||
double minSpeed = double.MaxValue;
|
||||
uint blocksToRead = 64;
|
||||
DateTime start;
|
||||
DateTime end;
|
||||
MediaType dskType;
|
||||
bool sense;
|
||||
byte[] senseBuf;
|
||||
|
||||
if(_outputPlugin is not IWritableImage outputFormat)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Image is not writable, aborting...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Image_is_not_writable_aborting);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -82,8 +81,8 @@ public partial class Dump
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not detect capacity...");
|
||||
StoppingErrorMessage?.Invoke("Could not detect capacity...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_detect_capacity);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_detect_capacity);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -92,93 +91,117 @@ public partial class Dump
|
||||
|
||||
blocks++;
|
||||
|
||||
ulong totalSize = blocks * (ulong)blockSize;
|
||||
|
||||
if(totalSize > 1099511627776)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)");
|
||||
else if(totalSize > 1073741824)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)");
|
||||
else if(totalSize > 1048576)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)");
|
||||
else if(totalSize > 1024)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)");
|
||||
else
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_has_0_blocks_of_1_bytes_each_for_a_total_of_2,
|
||||
blocks,
|
||||
blockSize,
|
||||
ByteSize.FromBytes(blocks * blockSize).ToString("0.000")));
|
||||
|
||||
if(blocks == 0)
|
||||
{
|
||||
_dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present...");
|
||||
StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present...");
|
||||
_dumpLog.WriteLine(Localization.Core.ERROR_Unable_to_read_medium_or_empty_medium_present);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Unable_to_read_medium_or_empty_medium_present);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
|
||||
UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
|
||||
UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
|
||||
UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_blocks_1_bytes,
|
||||
blocks,
|
||||
blocks * blockSize));
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_can_read_0_blocks_at_a_time, blocksToRead));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_bytes_per_logical_block, blockSize));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.SCSI_device_type_0, _dev.ScsiType));
|
||||
|
||||
if(blocks > 262144)
|
||||
{
|
||||
dskType = MediaType.MemoryStickProDuo;
|
||||
_dumpLog.WriteLine("Media detected as MemoryStick Pro Duo...");
|
||||
UpdateStatus?.Invoke("Media detected as MemoryStick Pro Duo...");
|
||||
_dumpLog.WriteLine(Localization.Core.Media_detected_as_MemoryStick_Pro_Duo);
|
||||
UpdateStatus?.Invoke(Localization.Core.Media_detected_as_MemoryStick_Pro_Duo);
|
||||
}
|
||||
else
|
||||
{
|
||||
dskType = MediaType.MemoryStickDuo;
|
||||
_dumpLog.WriteLine("Media detected as MemoryStick Duo...");
|
||||
UpdateStatus?.Invoke("Media detected as MemoryStick Duo...");
|
||||
_dumpLog.WriteLine(Localization.Core.Media_detected_as_MemoryStick_Duo);
|
||||
UpdateStatus?.Invoke(Localization.Core.Media_detected_as_MemoryStick_Duo);
|
||||
}
|
||||
|
||||
bool ret;
|
||||
|
||||
var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private);
|
||||
var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile);
|
||||
var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin",
|
||||
_dev,
|
||||
blocks,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
_private,
|
||||
_dimensions);
|
||||
|
||||
var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile);
|
||||
ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize);
|
||||
|
||||
// Cannot create image
|
||||
if(!ret)
|
||||
{
|
||||
_dumpLog.WriteLine("Error creating output image, not continuing.");
|
||||
_dumpLog.WriteLine(Localization.Core.Error_creating_output_image_not_continuing);
|
||||
_dumpLog.WriteLine(outputFormat.ErrorMessage);
|
||||
|
||||
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Error_creating_output_image_not_continuing +
|
||||
Environment.NewLine +
|
||||
outputFormat.ErrorMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
start = DateTime.UtcNow;
|
||||
_dumpStopwatch.Restart();
|
||||
double imageWriteDuration = 0;
|
||||
|
||||
DumpHardwareType currentTry = null;
|
||||
ExtentsULong extents = null;
|
||||
DumpHardware currentTry = null;
|
||||
ExtentsULong extents = null;
|
||||
|
||||
ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial,
|
||||
_dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision,
|
||||
_private, _force);
|
||||
ResumeSupport.Process(true,
|
||||
_dev.IsRemovable,
|
||||
blocks,
|
||||
_dev.Manufacturer,
|
||||
_dev.Model,
|
||||
_dev.Serial,
|
||||
_dev.PlatformId,
|
||||
ref _resume,
|
||||
ref currentTry,
|
||||
ref extents,
|
||||
_dev.FirmwareRevision,
|
||||
_private,
|
||||
_force);
|
||||
|
||||
if(currentTry == null ||
|
||||
extents == null)
|
||||
if(currentTry == null || extents == null)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_process_resume_file_not_continuing);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(_resume.NextBlock > 0)
|
||||
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
|
||||
if(_resume.NextBlock > 0) _dumpLog.WriteLine(Localization.Core.Resuming_from_block_0, _resume.NextBlock);
|
||||
|
||||
if(_createGraph)
|
||||
{
|
||||
Spiral.DiscParameters discSpiralParameters = Spiral.DiscParametersFromMediaType(dskType);
|
||||
|
||||
if(discSpiralParameters is not null)
|
||||
_mediaGraph = new Spiral((int)_dimensions, (int)_dimensions, discSpiralParameters, blocks);
|
||||
else
|
||||
_mediaGraph = new BlockMap((int)_dimensions, (int)_dimensions, blocks);
|
||||
|
||||
if(_mediaGraph is not null)
|
||||
{
|
||||
foreach(Tuple<ulong, ulong> e in extents.ToArray())
|
||||
_mediaGraph?.PaintSectorsGood(e.Item1, (uint)(e.Item2 - e.Item1 + 2));
|
||||
}
|
||||
|
||||
_mediaGraph?.PaintSectorsBad(_resume.BadBlocks);
|
||||
}
|
||||
|
||||
var newTrim = false;
|
||||
|
||||
DateTime timeSpeedStart = DateTime.UtcNow;
|
||||
ulong sectorSpeedStart = 0;
|
||||
_speedStopwatch.Restart();
|
||||
ulong sectorSpeedStart = 0;
|
||||
InitProgress?.Invoke();
|
||||
|
||||
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
|
||||
@@ -186,114 +209,141 @@ public 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;
|
||||
}
|
||||
|
||||
if(blocks - i < blocksToRead)
|
||||
blocksToRead = (uint)(blocks - i);
|
||||
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
|
||||
|
||||
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, blocks);
|
||||
UpdateProgress?.Invoke(string.Format(Localization.Core.Reading_sector_0_of_1_2,
|
||||
i,
|
||||
blocks,
|
||||
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()),
|
||||
(long)i,
|
||||
blocks);
|
||||
|
||||
sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0,
|
||||
blocksToRead, false, _dev.Timeout, out double cmdDuration);
|
||||
_speedStopwatch.Start();
|
||||
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)i,
|
||||
blockSize,
|
||||
0,
|
||||
blocksToRead,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration, blocksToRead);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(readBuffer, i, blocksToRead);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
extents.Add(i, blocksToRead, true);
|
||||
_mediaGraph?.PaintSectorsGood(i, blocksToRead);
|
||||
}
|
||||
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
|
||||
|
||||
if(i + _skip > blocks)
|
||||
_skip = (uint)(blocks - i);
|
||||
if(i + _skip > blocks) _skip = (uint)(blocks - i);
|
||||
|
||||
// Write empty data
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
for(ulong b = i; b < i + _skip; b++)
|
||||
_resume.BadBlocks.Add(b);
|
||||
for(ulong b = i; b < i + _skip; b++) _resume.BadBlocks.Add(b);
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration, _skip);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_blocks_from_errored_block_1, _skip, i);
|
||||
i += _skip - blocksToRead;
|
||||
newTrim = true;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += blocksToRead;
|
||||
_resume.NextBlock = i + blocksToRead;
|
||||
|
||||
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
double 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();
|
||||
_resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();
|
||||
|
||||
end = DateTime.UtcNow;
|
||||
_dumpStopwatch.Stop();
|
||||
EndProgress?.Invoke();
|
||||
mhddLog.Close();
|
||||
|
||||
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath);
|
||||
ibgLog.Close(_dev,
|
||||
blocks,
|
||||
blockSize,
|
||||
_dumpStopwatch.Elapsed.TotalSeconds,
|
||||
currentSpeed * 1024,
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
|
||||
_devicePath);
|
||||
|
||||
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Dump_finished_in_0,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_dump_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_write_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(imageWriteDuration.Seconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Dump_finished_in_0,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Average_dump_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
|
||||
blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Average_write_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(imageWriteDuration.Seconds())
|
||||
.Humanize()));
|
||||
|
||||
#region Trimming
|
||||
if(_resume.BadBlocks.Count > 0 &&
|
||||
!_aborted &&
|
||||
_trim &&
|
||||
newTrim)
|
||||
#region Trimming
|
||||
|
||||
if(_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim)
|
||||
{
|
||||
start = DateTime.UtcNow;
|
||||
UpdateStatus?.Invoke("Trimming skipped sectors");
|
||||
_dumpLog.WriteLine("Trimming skipped sectors");
|
||||
_trimStopwatch.Restart();
|
||||
UpdateStatus?.Invoke(Localization.Core.Trimming_skipped_sectors);
|
||||
_dumpLog.WriteLine(Localization.Core.Trimming_skipped_sectors);
|
||||
|
||||
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
||||
InitProgress?.Invoke();
|
||||
@@ -303,16 +353,28 @@ public 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));
|
||||
|
||||
sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector,
|
||||
blockSize, 0, 1, false, _dev.Timeout, out double _);
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)badSector,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out double _);
|
||||
|
||||
if(sense || _dev.Error)
|
||||
{
|
||||
@@ -324,18 +386,21 @@ public partial class Dump
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
outputFormat.WriteSector(readBuffer, badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
end = DateTime.UtcNow;
|
||||
_dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
|
||||
}
|
||||
#endregion Trimming
|
||||
_trimStopwatch.Stop();
|
||||
|
||||
#region Error handling
|
||||
if(_resume.BadBlocks.Count > 0 &&
|
||||
!_aborted &&
|
||||
_retryPasses > 0)
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Trimming_finished_in_0,
|
||||
_trimStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
}
|
||||
|
||||
#endregion Trimming
|
||||
|
||||
#region Error handling
|
||||
|
||||
if(_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0)
|
||||
{
|
||||
var pass = 1;
|
||||
var forward = true;
|
||||
@@ -348,32 +413,47 @@ public partial class Dump
|
||||
{
|
||||
Modes.ModePage_01 pg;
|
||||
|
||||
sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
|
||||
_dev.Timeout, out _);
|
||||
sense = _dev.ModeSense6(out readBuffer,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
Modes.DecodedMode? dcMode6 = null;
|
||||
if(!sense) dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType);
|
||||
|
||||
if(sense || dcMode6 is null)
|
||||
{
|
||||
sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
|
||||
_dev.Timeout, out _);
|
||||
sense = _dev.ModeSense10(out readBuffer,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(!sense)
|
||||
{
|
||||
Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(readBuffer, _dev.ScsiType);
|
||||
|
||||
if(dcMode10.HasValue)
|
||||
{
|
||||
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(readBuffer, _dev.ScsiType);
|
||||
|
||||
if(dcMode6.HasValue)
|
||||
{
|
||||
foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage =>
|
||||
modePage.Page == 0x01 && modePage.Subpage == 0x00))
|
||||
modePage is { Page: 0x01, Subpage: 0x00 }))
|
||||
currentModePage = modePage;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentModePage == null)
|
||||
@@ -417,31 +497,32 @@ public 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(pg)
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
md6 = Modes.EncodeMode6(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)
|
||||
{
|
||||
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;
|
||||
@@ -456,46 +537,76 @@ public 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));
|
||||
}
|
||||
|
||||
sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector,
|
||||
blockSize, 0, 1, false, _dev.Timeout, out double cmdDuration);
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)badSector,
|
||||
blockSize,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(sense || _dev.Error)
|
||||
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
|
||||
if(sense || _dev.Error) _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
outputFormat.WriteSector(readBuffer, badSector);
|
||||
UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
|
||||
_dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_block_0_in_pass_1,
|
||||
badSector,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_block_0_in_pass_1, badSector, pass);
|
||||
}
|
||||
else if(runningPersistent)
|
||||
outputFormat.WriteSector(readBuffer, badSector);
|
||||
else if(runningPersistent) outputFormat.WriteSector(readBuffer, badSector);
|
||||
}
|
||||
|
||||
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 repeatRetry;
|
||||
}
|
||||
@@ -505,57 +616,60 @@ public 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);
|
||||
|
||||
UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status).");
|
||||
_dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
|
||||
UpdateStatus?.Invoke(Localization.Core.Sending_MODE_SELECT_to_drive_return_device_to_previous_status);
|
||||
_dumpLog.WriteLine(Localization.Core.Sending_MODE_SELECT_to_drive_return_device_to_previous_status);
|
||||
_dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _);
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
#endregion Error handling
|
||||
|
||||
#endregion Error handling
|
||||
|
||||
_resume.BadBlocks.Sort();
|
||||
|
||||
foreach(ulong bad in _resume.BadBlocks)
|
||||
_dumpLog.WriteLine("Sector {0} could not be read.", bad);
|
||||
foreach(ulong bad in _resume.BadBlocks) _dumpLog.WriteLine(Localization.Core.Sector_0_could_not_be_read, bad);
|
||||
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
|
||||
var metadata = new ImageInfo
|
||||
var metadata = new CommonTypes.Structs.ImageInfo
|
||||
{
|
||||
Application = "Aaru",
|
||||
ApplicationVersion = Version.GetVersion()
|
||||
};
|
||||
|
||||
if(!outputFormat.SetMetadata(metadata))
|
||||
ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
|
||||
if(!outputFormat.SetImageInfo(metadata))
|
||||
{
|
||||
ErrorMessage?.Invoke(Localization.Core.Error_0_setting_metadata +
|
||||
Environment.NewLine +
|
||||
outputFormat.ErrorMessage);
|
||||
}
|
||||
|
||||
outputFormat.SetDumpHardware(_resume.Tries);
|
||||
|
||||
if(_preSidecar != null)
|
||||
outputFormat.SetCicmMetadata(_preSidecar);
|
||||
if(_preSidecar != null) outputFormat.SetMetadata(_preSidecar);
|
||||
|
||||
_dumpLog.WriteLine("Closing output file.");
|
||||
UpdateStatus?.Invoke("Closing output file.");
|
||||
DateTime closeStart = DateTime.Now;
|
||||
_dumpLog.WriteLine(Localization.Core.Closing_output_file);
|
||||
UpdateStatus?.Invoke(Localization.Core.Closing_output_file);
|
||||
_imageCloseStopwatch.Restart();
|
||||
outputFormat.Close();
|
||||
DateTime closeEnd = DateTime.Now;
|
||||
UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
|
||||
_dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
|
||||
_imageCloseStopwatch.Stop();
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Closed_in_0,
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Closed_in_0,
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
|
||||
|
||||
if(_aborted)
|
||||
{
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -564,21 +678,20 @@ public partial class Dump
|
||||
|
||||
if(_metadata)
|
||||
{
|
||||
UpdateStatus?.Invoke("Creating sidecar.");
|
||||
_dumpLog.WriteLine("Creating sidecar.");
|
||||
var filters = new FiltersList();
|
||||
IFilter filter = filters.GetFilter(_outputPath);
|
||||
UpdateStatus?.Invoke(Localization.Core.Creating_sidecar);
|
||||
_dumpLog.WriteLine(Localization.Core.Creating_sidecar);
|
||||
IFilter filter = PluginRegister.Singleton.GetFilter(_outputPath);
|
||||
var inputPlugin = ImageFormat.Detect(filter) as IMediaImage;
|
||||
ErrorNumber opened = inputPlugin.Open(filter);
|
||||
|
||||
if(opened != ErrorNumber.NoError)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke($"Error {opened} opening created image.");
|
||||
StoppingErrorMessage?.Invoke(string.Format(Localization.Core.Error_0_opening_created_image, opened));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime chkStart = DateTime.UtcNow;
|
||||
_sidecarStopwatch.Restart();
|
||||
_sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);
|
||||
_sidecarClass.InitProgressEvent += InitProgress;
|
||||
_sidecarClass.UpdateProgressEvent += UpdateProgress;
|
||||
@@ -587,91 +700,125 @@ public partial class Dump
|
||||
_sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
|
||||
_sidecarClass.EndProgressEvent2 += EndProgress2;
|
||||
_sidecarClass.UpdateStatusEvent += UpdateStatus;
|
||||
CICMMetadataType sidecar = _sidecarClass.Create();
|
||||
end = DateTime.UtcNow;
|
||||
Metadata sidecar = _sidecarClass.Create();
|
||||
_sidecarStopwatch.Stop();
|
||||
|
||||
if(!_aborted)
|
||||
{
|
||||
totalChkDuration = (end - chkStart).TotalMilliseconds;
|
||||
UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds.");
|
||||
totalChkDuration = _sidecarStopwatch.Elapsed.TotalMilliseconds;
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Sidecar_created_in_0,
|
||||
_sidecarStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
_dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_checksum_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalChkDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));
|
||||
_dumpLog.WriteLine(Localization.Core.Sidecar_created_in_0,
|
||||
_sidecarStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Average_checksum_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalChkDuration.Milliseconds())
|
||||
.Humanize());
|
||||
|
||||
if(_preSidecar != null)
|
||||
{
|
||||
_preSidecar.BlockMedia = sidecar.BlockMedia;
|
||||
sidecar = _preSidecar;
|
||||
_preSidecar.BlockMedias = sidecar.BlockMedias;
|
||||
sidecar = _preSidecar;
|
||||
}
|
||||
|
||||
List<(ulong start, string type)> filesystems = new();
|
||||
List<(ulong start, string type)> filesystems = [];
|
||||
|
||||
if(sidecar.BlockMedia[0].FileSystemInformation != null)
|
||||
filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
|
||||
where partition.FileSystems != null from fileSystem in partition.FileSystems
|
||||
if(sidecar.BlockMedias[0].FileSystemInformation != null)
|
||||
{
|
||||
filesystems.AddRange(from partition in sidecar.BlockMedias[0].FileSystemInformation
|
||||
where partition.FileSystems != null
|
||||
from fileSystem in partition.FileSystems
|
||||
select (partition.StartSector, fileSystem.Type));
|
||||
}
|
||||
|
||||
if(filesystems.Count > 0)
|
||||
{
|
||||
foreach(var filesystem in filesystems.Select(o => new
|
||||
{
|
||||
o.start,
|
||||
o.type
|
||||
}).Distinct())
|
||||
{
|
||||
o.start,
|
||||
o.type
|
||||
})
|
||||
.Distinct())
|
||||
{
|
||||
UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");
|
||||
_dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Found_filesystem_0_at_sector_1,
|
||||
filesystem.type,
|
||||
filesystem.start));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Found_filesystem_0_at_sector_1,
|
||||
filesystem.type,
|
||||
filesystem.start);
|
||||
}
|
||||
}
|
||||
|
||||
sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
|
||||
sidecar.BlockMedias[0].Dimensions = Dimensions.FromMediaType(dskType);
|
||||
(string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType);
|
||||
sidecar.BlockMedia[0].DiskType = xmlType.type;
|
||||
sidecar.BlockMedia[0].DiskSubType = xmlType.subType;
|
||||
sidecar.BlockMedia[0].Interface = "USB";
|
||||
sidecar.BlockMedia[0].LogicalBlocks = blocks;
|
||||
sidecar.BlockMedia[0].PhysicalBlockSize = (int)blockSize;
|
||||
sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize;
|
||||
sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer;
|
||||
sidecar.BlockMedia[0].Model = _dev.Model;
|
||||
sidecar.BlockMedias[0].MediaType = xmlType.type;
|
||||
sidecar.BlockMedias[0].MediaSubType = xmlType.subType;
|
||||
sidecar.BlockMedias[0].Interface = "USB";
|
||||
sidecar.BlockMedias[0].LogicalBlocks = blocks;
|
||||
sidecar.BlockMedias[0].PhysicalBlockSize = (int)blockSize;
|
||||
sidecar.BlockMedias[0].LogicalBlockSize = (int)blockSize;
|
||||
sidecar.BlockMedias[0].Manufacturer = _dev.Manufacturer;
|
||||
sidecar.BlockMedias[0].Model = _dev.Model;
|
||||
|
||||
if(!_private)
|
||||
sidecar.BlockMedia[0].Serial = _dev.Serial;
|
||||
if(!_private) sidecar.BlockMedias[0].Serial = _dev.Serial;
|
||||
|
||||
sidecar.BlockMedia[0].Size = blocks * blockSize;
|
||||
sidecar.BlockMedias[0].Size = blocks * blockSize;
|
||||
|
||||
if(_dev.IsRemovable)
|
||||
sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray();
|
||||
if(_dev.IsRemovable) sidecar.BlockMedias[0].DumpHardware = _resume.Tries;
|
||||
|
||||
UpdateStatus?.Invoke("Writing metadata sidecar");
|
||||
UpdateStatus?.Invoke(Localization.Core.Writing_metadata_sidecar);
|
||||
|
||||
var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);
|
||||
var jsonFs = new FileStream(_outputPrefix + ".metadata.json", FileMode.Create);
|
||||
|
||||
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
|
||||
xmlSer.Serialize(xmlFs, sidecar);
|
||||
xmlFs.Close();
|
||||
JsonSerializer.Serialize(jsonFs,
|
||||
new MetadataJson
|
||||
{
|
||||
AaruMetadata = sidecar
|
||||
},
|
||||
typeof(MetadataJson),
|
||||
MetadataJsonContext.Default);
|
||||
|
||||
jsonFs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke("");
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core
|
||||
.Took_a_total_of_0_1_processing_commands_2_checksumming_3_writing_4_closing,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second),
|
||||
totalDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
|
||||
totalChkDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
|
||||
imageWriteDuration.Seconds().Humanize(minUnit: TimeUnit.Second),
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
if(maxSpeed > 0)
|
||||
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
|
||||
{
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Fastest_speed_burst_0,
|
||||
ByteSize.FromMegabytes(maxSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
if(minSpeed > 0 &&
|
||||
minSpeed < double.MaxValue)
|
||||
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
|
||||
if(minSpeed is > 0 and < double.MaxValue)
|
||||
{
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Slowest_speed_burst_0,
|
||||
ByteSize.FromMegabytes(minSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core._0_sectors_could_not_be_read, _resume.BadBlocks.Count));
|
||||
UpdateStatus?.Invoke("");
|
||||
|
||||
Statistics.AddMedia(dskType, true);
|
||||
|
||||
@@ -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;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes;
|
||||
@@ -40,16 +38,12 @@ using Aaru.CommonTypes.Structs.Devices.SCSI;
|
||||
using Aaru.Decoders.SCSI;
|
||||
using Aaru.Devices;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
public partial class Dump
|
||||
{
|
||||
static readonly byte[] _fatSignature =
|
||||
{
|
||||
0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20
|
||||
};
|
||||
static readonly byte[] _isoExtension =
|
||||
{
|
||||
0x49, 0x53, 0x4F
|
||||
};
|
||||
static readonly byte[] _fatSignature = "FAT16 "u8.ToArray();
|
||||
static readonly byte[] _isoExtension = "ISO"u8.ToArray();
|
||||
|
||||
/// <summary>Dumps a CFW PlayStation Portable UMD</summary>
|
||||
void PlayStationPortable()
|
||||
@@ -58,24 +52,30 @@ public partial class Dump
|
||||
!_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickProDuo) &&
|
||||
!_outputPlugin.SupportedMediaTypes.Contains(MediaType.UMD))
|
||||
{
|
||||
_dumpLog.WriteLine("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump...");
|
||||
_dumpLog.WriteLine(Localization.Core
|
||||
.Selected_output_format_does_not_support_MemoryStick_Duo_or_UMD_cannot_dump);
|
||||
|
||||
StoppingErrorMessage?.
|
||||
Invoke("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core
|
||||
.Selected_output_format_does_not_support_MemoryStick_Duo_or_UMD_cannot_dump);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke("Checking if media is UMD or MemoryStick...");
|
||||
_dumpLog.WriteLine("Checking if media is UMD or MemoryStick...");
|
||||
UpdateStatus?.Invoke(Localization.Core.Checking_if_media_is_UMD_or_MemoryStick);
|
||||
_dumpLog.WriteLine(Localization.Core.Checking_if_media_is_UMD_or_MemoryStick);
|
||||
|
||||
bool sense = _dev.ModeSense6(out byte[] buffer, out _, false, ScsiModeSensePageControl.Current, 0, _dev.Timeout,
|
||||
bool sense = _dev.ModeSense6(out byte[] buffer,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not get MODE SENSE...");
|
||||
StoppingErrorMessage?.Invoke("Could not get MODE SENSE...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_get_MODE_SENSE);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_get_MODE_SENSE);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -84,8 +84,8 @@ public partial class Dump
|
||||
|
||||
if(!decoded.HasValue)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not decode MODE SENSE...");
|
||||
StoppingErrorMessage?.Invoke("Could not decode MODE SENSE...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_decode_MODE_SENSE);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_decode_MODE_SENSE);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -102,8 +102,8 @@ public partial class Dump
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -124,16 +124,28 @@ public partial class Dump
|
||||
var sectorsPerFat = (ushort)((buffer[0x17] << 8) + buffer[0x16]);
|
||||
var rootStart = (ushort)(sectorsPerFat * 2 + fatStart);
|
||||
|
||||
UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}...");
|
||||
_dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_root_directory_in_sector_0, rootStart));
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_root_directory_in_sector_0, rootStart);
|
||||
|
||||
sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, _dev.Timeout,
|
||||
sense = _dev.Read12(out buffer,
|
||||
out _,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
rootStart,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -148,11 +160,14 @@ public partial class Dump
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke($"FAT starts at sector {fatStart} and runs for {sectorsPerFat} sectors...");
|
||||
_dumpLog.WriteLine("FAT starts at sector {0} and runs for {1} sectors...", fatStart, sectorsPerFat);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.FAT_starts_at_sector_0_and_runs_for_1_sectors,
|
||||
fatStart,
|
||||
sectorsPerFat));
|
||||
|
||||
UpdateStatus?.Invoke("Reading FAT...");
|
||||
_dumpLog.WriteLine("Reading FAT...");
|
||||
_dumpLog.WriteLine(Localization.Core.FAT_starts_at_sector_0_and_runs_for_1_sectors, fatStart, sectorsPerFat);
|
||||
|
||||
UpdateStatus?.Invoke(Localization.Core.Reading_FAT);
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_FAT);
|
||||
|
||||
var fat = new byte[sectorsPerFat * 512];
|
||||
|
||||
@@ -162,16 +177,27 @@ public partial class Dump
|
||||
{
|
||||
uint transfer = 64;
|
||||
|
||||
if(transfer + position > sectorsPerFat)
|
||||
transfer = sectorsPerFat - position;
|
||||
if(transfer + position > sectorsPerFat) transfer = sectorsPerFat - position;
|
||||
|
||||
sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, position + fatStart, 512, 0, transfer,
|
||||
false, _dev.Timeout, out _);
|
||||
sense = _dev.Read12(out buffer,
|
||||
out _,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
position + fatStart,
|
||||
512,
|
||||
0,
|
||||
transfer,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -181,8 +207,8 @@ public partial class Dump
|
||||
position += transfer;
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke("Traversing FAT...");
|
||||
_dumpLog.WriteLine("Traversing FAT...");
|
||||
UpdateStatus?.Invoke(Localization.Core.Traversing_FAT);
|
||||
_dumpLog.WriteLine(Localization.Core.Traversing_FAT);
|
||||
|
||||
var previousCluster = BitConverter.ToUInt16(fat, 4);
|
||||
|
||||
@@ -197,8 +223,7 @@ public partial class Dump
|
||||
continue;
|
||||
}
|
||||
|
||||
if(nextCluster == 0xFFFF)
|
||||
break;
|
||||
if(nextCluster == 0xFFFF) break;
|
||||
|
||||
DumpMs();
|
||||
|
||||
@@ -208,6 +233,6 @@ public partial class Dump
|
||||
if(_outputPlugin is IWritableOpticalImage)
|
||||
DumpUmd();
|
||||
else
|
||||
StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images.");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.The_specified_image_format_cannot_represent_optical_discs);
|
||||
}
|
||||
}
|
||||
@@ -27,29 +27,32 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Aaru.CommonTypes;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs;
|
||||
using Aaru.Console;
|
||||
using Aaru.Core.Graphics;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.SCSI;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using Humanizer.Localisation;
|
||||
using Track = Aaru.CommonTypes.Structs.Track;
|
||||
using TrackType = Aaru.CommonTypes.Enums.TrackType;
|
||||
using Version = Aaru.CommonTypes.Interop.Version;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
public partial class Dump
|
||||
{
|
||||
[SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")]
|
||||
@@ -62,24 +65,34 @@ public partial class Dump
|
||||
double currentSpeed = 0;
|
||||
double maxSpeed = double.MinValue;
|
||||
double minSpeed = double.MaxValue;
|
||||
DateTime start;
|
||||
DateTime end;
|
||||
byte[] senseBuf;
|
||||
|
||||
if(_outputPlugin is not IWritableOpticalImage outputOptical)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Image is not writable, aborting...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Image_is_not_writable_aborting);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool sense = _dev.Read12(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false,
|
||||
_dev.Timeout, out _);
|
||||
bool sense = _dev.Read12(out byte[] readBuffer,
|
||||
out _,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -90,16 +103,28 @@ public partial class Dump
|
||||
var rootSize = (ushort)(((readBuffer[0x12] << 8) + readBuffer[0x11]) * 32 / 512);
|
||||
var umdStart = (ushort)(rootStart + rootSize);
|
||||
|
||||
UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}...");
|
||||
_dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_root_directory_in_sector_0, rootStart));
|
||||
_dumpLog.WriteLine(Localization.Core.Reading_root_directory_in_sector_0, rootStart);
|
||||
|
||||
sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false,
|
||||
_dev.Timeout, out _);
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out _,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
rootStart,
|
||||
512,
|
||||
0,
|
||||
1,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
{
|
||||
_dumpLog.WriteLine("Could not read...");
|
||||
StoppingErrorMessage?.Invoke("Could not read...");
|
||||
_dumpLog.WriteLine(Localization.Core.Could_not_read);
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_read);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -108,93 +133,119 @@ public partial class Dump
|
||||
ulong blocks = umdSizeInBytes / blockSize;
|
||||
string mediaPartNumber = Encoding.ASCII.GetString(readBuffer, 0, 11).Trim();
|
||||
|
||||
ulong totalSize = blocks * blockSize;
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_has_0_blocks_of_1_bytes_each_for_a_total_of_2,
|
||||
blocks,
|
||||
blockSize,
|
||||
ByteSize.FromBytes(blocks * blockSize).ToString("0.000")));
|
||||
|
||||
if(totalSize > 1073741824)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)");
|
||||
else if(totalSize > 1048576)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)");
|
||||
else if(totalSize > 1024)
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)");
|
||||
else
|
||||
UpdateStatus?.
|
||||
Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_blocks_1_bytes,
|
||||
blocks,
|
||||
blocks * blockSize));
|
||||
|
||||
UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
|
||||
UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
|
||||
UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
|
||||
UpdateStatus?.Invoke($"Device reports {2048} bytes per physical block.");
|
||||
UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
|
||||
UpdateStatus?.Invoke($"Media identified as {dskType}.");
|
||||
UpdateStatus?.Invoke($"Media part number is {mediaPartNumber}.");
|
||||
_dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
|
||||
_dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
|
||||
_dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
|
||||
_dumpLog.WriteLine("Device reports {0} bytes per physical block.", 2048);
|
||||
_dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
|
||||
_dumpLog.WriteLine("Media identified as {0}.", dskType);
|
||||
_dumpLog.WriteLine("Media part number is {0}.", mediaPartNumber);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_can_read_0_blocks_at_a_time, blocksToRead));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_bytes_per_logical_block, blockSize));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_bytes_per_physical_block, blockSize));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.SCSI_device_type_0, _dev.ScsiType));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_identified_as_0, dskType));
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_part_number_is_0, mediaPartNumber));
|
||||
_dumpLog.WriteLine(Localization.Core.Device_reports_0_blocks_1_bytes, blocks, blocks * blockSize);
|
||||
_dumpLog.WriteLine(Localization.Core.Device_can_read_0_blocks_at_a_time, blocksToRead);
|
||||
_dumpLog.WriteLine(Localization.Core.Device_reports_0_bytes_per_logical_block, blockSize);
|
||||
_dumpLog.WriteLine(Localization.Core.Device_reports_0_bytes_per_physical_block, blockSize);
|
||||
_dumpLog.WriteLine(Localization.Core.SCSI_device_type_0, _dev.ScsiType);
|
||||
_dumpLog.WriteLine(Localization.Core.Media_identified_as_0, dskType);
|
||||
_dumpLog.WriteLine(Localization.Core.Media_part_number_is_0, mediaPartNumber);
|
||||
|
||||
bool ret;
|
||||
|
||||
var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private);
|
||||
var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010);
|
||||
var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin",
|
||||
_dev,
|
||||
blocks,
|
||||
blockSize,
|
||||
blocksToRead,
|
||||
_private,
|
||||
_dimensions);
|
||||
|
||||
var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010);
|
||||
ret = outputOptical.Create(_outputPath, dskType, _formatOptions, blocks, blockSize);
|
||||
|
||||
// Cannot create image
|
||||
if(!ret)
|
||||
{
|
||||
_dumpLog.WriteLine("Error creating output image, not continuing.");
|
||||
_dumpLog.WriteLine(Localization.Core.Error_creating_output_image_not_continuing);
|
||||
_dumpLog.WriteLine(outputOptical.ErrorMessage);
|
||||
|
||||
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Error_creating_output_image_not_continuing +
|
||||
Environment.NewLine +
|
||||
outputOptical.ErrorMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
start = DateTime.UtcNow;
|
||||
_dumpStopwatch.Restart();
|
||||
double imageWriteDuration = 0;
|
||||
|
||||
outputOptical.SetTracks(new List<Track>
|
||||
outputOptical.SetTracks([
|
||||
new Track
|
||||
{
|
||||
BytesPerSector = (int)blockSize,
|
||||
EndSector = blocks - 1,
|
||||
Sequence = 1,
|
||||
RawBytesPerSector = (int)blockSize,
|
||||
SubchannelType = TrackSubchannelType.None,
|
||||
Session = 1,
|
||||
Type = TrackType.Data
|
||||
}
|
||||
]);
|
||||
|
||||
DumpHardware currentTry = null;
|
||||
ExtentsULong extents = null;
|
||||
|
||||
ResumeSupport.Process(true,
|
||||
_dev.IsRemovable,
|
||||
blocks,
|
||||
_dev.Manufacturer,
|
||||
_dev.Model,
|
||||
_dev.Serial,
|
||||
_dev.PlatformId,
|
||||
ref _resume,
|
||||
ref currentTry,
|
||||
ref extents,
|
||||
_dev.FirmwareRevision,
|
||||
_private,
|
||||
_force);
|
||||
|
||||
if(currentTry == null || extents == null)
|
||||
{
|
||||
new()
|
||||
{
|
||||
BytesPerSector = (int)blockSize,
|
||||
EndSector = blocks - 1,
|
||||
Sequence = 1,
|
||||
RawBytesPerSector = (int)blockSize,
|
||||
SubchannelType = TrackSubchannelType.None,
|
||||
Session = 1,
|
||||
Type = TrackType.Data
|
||||
}
|
||||
});
|
||||
|
||||
DumpHardwareType currentTry = null;
|
||||
ExtentsULong extents = null;
|
||||
|
||||
ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial,
|
||||
_dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision,
|
||||
_private, _force);
|
||||
|
||||
if(currentTry == null ||
|
||||
extents == null)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_process_resume_file_not_continuing);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(_resume.NextBlock > 0)
|
||||
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
|
||||
if(_resume.NextBlock > 0) _dumpLog.WriteLine(Localization.Core.Resuming_from_block_0, _resume.NextBlock);
|
||||
|
||||
if(_createGraph)
|
||||
{
|
||||
Spiral.DiscParameters discSpiralParameters = Spiral.DiscParametersFromMediaType(dskType);
|
||||
|
||||
if(discSpiralParameters is not null)
|
||||
_mediaGraph = new Spiral((int)_dimensions, (int)_dimensions, discSpiralParameters, blocks);
|
||||
else
|
||||
_mediaGraph = new BlockMap((int)_dimensions, (int)_dimensions, blocks);
|
||||
|
||||
if(_mediaGraph is not null)
|
||||
{
|
||||
foreach(Tuple<ulong, ulong> e in extents.ToArray())
|
||||
_mediaGraph?.PaintSectorsGood(e.Item1, (uint)(e.Item2 - e.Item1 + 2));
|
||||
}
|
||||
|
||||
_mediaGraph?.PaintSectorsBad(_resume.BadBlocks);
|
||||
}
|
||||
|
||||
var newTrim = false;
|
||||
|
||||
DateTime timeSpeedStart = DateTime.UtcNow;
|
||||
ulong sectorSpeedStart = 0;
|
||||
_speedStopwatch.Reset();
|
||||
ulong sectorSpeedStart = 0;
|
||||
InitProgress?.Invoke();
|
||||
|
||||
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
|
||||
@@ -202,114 +253,139 @@ public 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;
|
||||
}
|
||||
|
||||
if(blocks - i < blocksToRead)
|
||||
blocksToRead = (uint)(blocks - i);
|
||||
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
|
||||
|
||||
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.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)(umdStart + i * 4),
|
||||
512, 0, blocksToRead * 4, false, _dev.Timeout, out double cmdDuration);
|
||||
_speedStopwatch.Start();
|
||||
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)(umdStart + i * 4),
|
||||
512,
|
||||
0,
|
||||
blocksToRead * 4,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration, blocksToRead);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
outputOptical.WriteSectors(readBuffer, i, blocksToRead);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
extents.Add(i, blocksToRead, true);
|
||||
_mediaGraph?.PaintSectorsGood(i, blocksToRead);
|
||||
}
|
||||
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
|
||||
|
||||
if(i + _skip > blocks)
|
||||
_skip = (uint)(blocks - i);
|
||||
if(i + _skip > blocks) _skip = (uint)(blocks - i);
|
||||
|
||||
// Write empty data
|
||||
DateTime writeStart = DateTime.Now;
|
||||
outputOptical.WriteSectors(new byte[blockSize * _skip], i, _skip);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
for(ulong b = i; b < i + _skip; b++)
|
||||
_resume.BadBlocks.Add(b);
|
||||
for(ulong b = i; b < i + _skip; b++) _resume.BadBlocks.Add(b);
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration, _skip);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_blocks_from_errored_block_1, _skip, i);
|
||||
i += _skip - blocksToRead;
|
||||
newTrim = true;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += blocksToRead;
|
||||
_resume.NextBlock = i + blocksToRead;
|
||||
|
||||
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
double 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();
|
||||
}
|
||||
|
||||
_resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();
|
||||
|
||||
end = DateTime.UtcNow;
|
||||
_dumpStopwatch.Stop();
|
||||
EndProgress?.Invoke();
|
||||
mhddLog.Close();
|
||||
|
||||
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath);
|
||||
ibgLog.Close(_dev,
|
||||
blocks,
|
||||
blockSize,
|
||||
_dumpStopwatch.Elapsed.TotalSeconds,
|
||||
currentSpeed * 1024,
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
|
||||
_devicePath);
|
||||
|
||||
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Dump_finished_in_0,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_dump_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_write_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(imageWriteDuration.Seconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Dump_finished_in_0,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
|
||||
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Average_dump_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
|
||||
blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Average_write_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(imageWriteDuration.Seconds())
|
||||
.Humanize()));
|
||||
|
||||
#region Trimming
|
||||
if(_resume.BadBlocks.Count > 0 &&
|
||||
!_aborted &&
|
||||
_trim &&
|
||||
newTrim)
|
||||
#region Trimming
|
||||
|
||||
if(_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim)
|
||||
{
|
||||
start = DateTime.UtcNow;
|
||||
_dumpLog.WriteLine("Trimming skipped sectors");
|
||||
_trimStopwatch.Restart();
|
||||
_dumpLog.WriteLine(Localization.Core.Trimming_skipped_sectors);
|
||||
|
||||
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
||||
InitProgress?.Invoke();
|
||||
@@ -319,15 +395,27 @@ public partial class Dump
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.Invoke($"Trimming sector {badSector}");
|
||||
PulseProgress?.Invoke(string.Format(Localization.Core.Trimming_sector_0, badSector));
|
||||
|
||||
sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false,
|
||||
(uint)(umdStart + badSector * 4), 512, 0, 4, false, _dev.Timeout, out double _);
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)(umdStart + badSector * 4),
|
||||
512,
|
||||
0,
|
||||
4,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out double _);
|
||||
|
||||
if(sense || _dev.Error)
|
||||
{
|
||||
@@ -339,18 +427,21 @@ public partial class Dump
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
outputOptical.WriteSector(readBuffer, badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
end = DateTime.UtcNow;
|
||||
_dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
|
||||
}
|
||||
#endregion Trimming
|
||||
_trimStopwatch.Stop();
|
||||
|
||||
#region Error handling
|
||||
if(_resume.BadBlocks.Count > 0 &&
|
||||
!_aborted &&
|
||||
_retryPasses > 0)
|
||||
_dumpLog.WriteLine(string.Format(Localization.Core.Trimming_finished_in_0,
|
||||
_trimStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
}
|
||||
|
||||
#endregion Trimming
|
||||
|
||||
#region Error handling
|
||||
|
||||
if(_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0)
|
||||
{
|
||||
var pass = 1;
|
||||
var forward = true;
|
||||
@@ -363,17 +454,24 @@ public partial class Dump
|
||||
{
|
||||
Modes.ModePage_01 pg;
|
||||
|
||||
sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01,
|
||||
_dev.Timeout, out _);
|
||||
sense = _dev.ModeSense6(out readBuffer,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(!sense)
|
||||
{
|
||||
Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType);
|
||||
|
||||
if(dcMode6.HasValue)
|
||||
{
|
||||
foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage =>
|
||||
modePage.Page == 0x01 && modePage.Subpage == 0x00))
|
||||
modePage is { Page: 0x01, Subpage: 0x00 }))
|
||||
currentModePage = modePage;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentModePage == null)
|
||||
@@ -417,30 +515,31 @@ public 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(pg)
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
md6 = Modes.EncodeMode6(md, _dev.ScsiType);
|
||||
|
||||
_dumpLog.WriteLine("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)
|
||||
{
|
||||
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;
|
||||
@@ -455,48 +554,76 @@ public partial class Dump
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.
|
||||
Invoke($"Retrying sector {badSector}, pass {pass}, {(runningPersistent ? "recovering partial data, " : "")}{(forward ? "forward" : "reverse")}");
|
||||
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));
|
||||
}
|
||||
|
||||
sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false,
|
||||
(uint)(umdStart + badSector * 4), 512, 0, 4, false, _dev.Timeout,
|
||||
sense = _dev.Read12(out readBuffer,
|
||||
out senseBuf,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
(uint)(umdStart + badSector * 4),
|
||||
512,
|
||||
0,
|
||||
4,
|
||||
false,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(sense || _dev.Error)
|
||||
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
|
||||
if(sense || _dev.Error) _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
outputOptical.WriteSector(readBuffer, badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
|
||||
UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_block_0_in_pass_1,
|
||||
badSector,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_block_0_in_pass_1, badSector, pass);
|
||||
}
|
||||
else if(runningPersistent)
|
||||
outputOptical.WriteSector(readBuffer, badSector);
|
||||
else if(runningPersistent) outputOptical.WriteSector(readBuffer, badSector);
|
||||
}
|
||||
|
||||
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 repeatRetry;
|
||||
}
|
||||
@@ -506,82 +633,94 @@ public 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);
|
||||
|
||||
_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);
|
||||
_dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _);
|
||||
}
|
||||
|
||||
EndProgress?.Invoke();
|
||||
AaruConsole.WriteLine();
|
||||
}
|
||||
#endregion Error handling
|
||||
|
||||
#endregion Error handling
|
||||
|
||||
_resume.BadBlocks.Sort();
|
||||
|
||||
foreach(ulong bad in _resume.BadBlocks)
|
||||
_dumpLog.WriteLine("Sector {0} could not be read.", bad);
|
||||
foreach(ulong bad in _resume.BadBlocks) _dumpLog.WriteLine(Localization.Core.Sector_0_could_not_be_read, bad);
|
||||
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
|
||||
var metadata = new ImageInfo
|
||||
var metadata = new CommonTypes.Structs.ImageInfo
|
||||
{
|
||||
Application = "Aaru",
|
||||
ApplicationVersion = Version.GetVersion(),
|
||||
MediaPartNumber = mediaPartNumber
|
||||
};
|
||||
|
||||
if(!outputOptical.SetMetadata(metadata))
|
||||
ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
|
||||
if(!outputOptical.SetImageInfo(metadata))
|
||||
{
|
||||
ErrorMessage?.Invoke(Localization.Core.Error_0_setting_metadata +
|
||||
Environment.NewLine +
|
||||
outputOptical.ErrorMessage);
|
||||
}
|
||||
|
||||
outputOptical.SetDumpHardware(_resume.Tries);
|
||||
|
||||
if(_preSidecar != null)
|
||||
outputOptical.SetCicmMetadata(_preSidecar);
|
||||
if(_preSidecar != null) outputOptical.SetMetadata(_preSidecar);
|
||||
|
||||
_dumpLog.WriteLine("Closing output file.");
|
||||
UpdateStatus?.Invoke("Closing output file.");
|
||||
DateTime closeStart = DateTime.Now;
|
||||
_dumpLog.WriteLine(Localization.Core.Closing_output_file);
|
||||
UpdateStatus?.Invoke(Localization.Core.Closing_output_file);
|
||||
_imageCloseStopwatch.Restart();
|
||||
outputOptical.Close();
|
||||
DateTime closeEnd = DateTime.Now;
|
||||
_dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);
|
||||
_imageCloseStopwatch.Stop();
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Closed_in_0,
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second));
|
||||
|
||||
if(_aborted)
|
||||
{
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
double totalChkDuration = 0;
|
||||
|
||||
if(_metadata)
|
||||
WriteOpticalSidecar(blockSize, blocks, dskType, null, null, 1, out totalChkDuration, null);
|
||||
if(_metadata) WriteOpticalSidecar(blockSize, blocks, dskType, null, null, 1, out totalChkDuration, null);
|
||||
|
||||
UpdateStatus?.Invoke("");
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core
|
||||
.Took_a_total_of_0_1_processing_commands_2_checksumming_3_writing_4_closing,
|
||||
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second),
|
||||
totalDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
|
||||
totalChkDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
|
||||
imageWriteDuration.Seconds().Humanize(minUnit: TimeUnit.Second),
|
||||
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_speed_0,
|
||||
ByteSize.FromBytes(blockSize * (blocks + 1))
|
||||
.Per(totalDuration.Milliseconds())
|
||||
.Humanize()));
|
||||
|
||||
if(maxSpeed > 0)
|
||||
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
|
||||
{
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Fastest_speed_burst_0,
|
||||
ByteSize.FromMegabytes(maxSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
if(minSpeed > 0 &&
|
||||
minSpeed < double.MaxValue)
|
||||
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
|
||||
if(minSpeed is > 0 and < double.MaxValue)
|
||||
{
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Slowest_speed_burst_0,
|
||||
ByteSize.FromMegabytes(minSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core._0_sectors_could_not_be_read, _resume.BadBlocks.Count));
|
||||
UpdateStatus?.Invoke("");
|
||||
|
||||
Statistics.AddMedia(dskType, true);
|
||||
|
||||
@@ -27,19 +27,18 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Metadata;
|
||||
using Schemas;
|
||||
using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
|
||||
using Version = Aaru.CommonTypes.Interop.Version;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
/// <summary>Implements resume support</summary>
|
||||
static class ResumeSupport
|
||||
{
|
||||
@@ -64,65 +63,95 @@ static class ResumeSupport
|
||||
/// progress dump
|
||||
/// </exception>
|
||||
internal static void Process(bool isLba, bool removable, ulong blocks, string manufacturer, string model,
|
||||
string serial, PlatformID platform, ref Resume resume, ref DumpHardwareType currentTry,
|
||||
string serial, PlatformID platform, ref Resume resume, ref DumpHardware currentTry,
|
||||
ref ExtentsULong extents, string firmware, bool @private, bool force,
|
||||
bool isTape = false)
|
||||
{
|
||||
if(@private)
|
||||
serial = null;
|
||||
if(@private) serial = null;
|
||||
|
||||
if(resume != null)
|
||||
{
|
||||
if(!isLba)
|
||||
throw new NotImplementedException("Resuming CHS devices is currently not supported.");
|
||||
throw new NotImplementedException(Localization.Core.Resuming_CHS_devices_is_currently_not_supported);
|
||||
|
||||
if(resume.Tape != isTape)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a {(resume.Tape ? "tape" : "not tape")} device but you're requesting to dump a {(isTape ? "tape" : "not tape")} device, not continuing...");
|
||||
|
||||
if(resume.Removable != removable &&
|
||||
!force)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a {(resume.Removable ? "removable" : "non removable")} device but you're requesting to dump a {(removable ? "removable" : "non removable")} device, not continuing...");
|
||||
|
||||
if(!isTape &&
|
||||
resume.LastBlock != blocks - 1 &&
|
||||
!force)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a device with {resume.LastBlock + 1} blocks but you're requesting to dump one with {blocks} blocks, not continuing...");
|
||||
|
||||
foreach(DumpHardwareType oldTry in resume.Tries)
|
||||
{
|
||||
if(!removable &&
|
||||
!force)
|
||||
if(resume.Tape)
|
||||
throw new InvalidOperationException(Localization.Core.Resume_specifies_tape_but_device_is_not_tape);
|
||||
|
||||
throw new InvalidOperationException(Localization.Core.Resume_specifies_not_tape_but_device_is_tape);
|
||||
}
|
||||
|
||||
if(resume.Removable != removable && !force)
|
||||
{
|
||||
if(resume.Removable)
|
||||
{
|
||||
throw new InvalidOperationException(Localization.Core
|
||||
.Resume_specifies_removable_but_device_is_non_removable);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(Localization.Core
|
||||
.Resume_specifies_non_removable_but_device_is_removable);
|
||||
}
|
||||
|
||||
if(!isTape && resume.LastBlock != blocks - 1 && !force)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(Localization.Core
|
||||
.Resume_file_different_number_of_blocks_not_continuing,
|
||||
resume.LastBlock + 1,
|
||||
blocks));
|
||||
}
|
||||
|
||||
foreach(DumpHardware oldTry in resume.Tries)
|
||||
{
|
||||
if(!removable && !force)
|
||||
{
|
||||
if(oldTry.Manufacturer != manufacturer)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a device manufactured by {oldTry.Manufacturer} but you're requesting to dump one by {manufacturer}, not continuing...");
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(Localization.Core
|
||||
.Resume_file_different_manufacturer_not_continuing,
|
||||
oldTry.Manufacturer,
|
||||
manufacturer));
|
||||
}
|
||||
|
||||
if(oldTry.Model != model)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a device model {oldTry.Model} but you're requesting to dump model {model}, not continuing...");
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(Localization.Core
|
||||
.Resume_file_different_model_not_continuing,
|
||||
oldTry.Model,
|
||||
model));
|
||||
}
|
||||
|
||||
if(oldTry.Serial != serial)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a device with serial {oldTry.Serial} but you're requesting to dump one with serial {serial}, not continuing...");
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(Localization.Core
|
||||
.Resume_file_different_serial_number_not_continuing,
|
||||
oldTry.Serial,
|
||||
serial));
|
||||
}
|
||||
|
||||
if(oldTry.Firmware != firmware)
|
||||
throw new
|
||||
InvalidOperationException($"Resume file specifies a device with firmware version {oldTry.Firmware} but you're requesting to dump one with firmware version {firmware}, not continuing...");
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(Localization.Core
|
||||
.Resume_file_different_firmware_revision_not_continuing,
|
||||
oldTry.Firmware,
|
||||
firmware));
|
||||
}
|
||||
}
|
||||
|
||||
if(oldTry.Software == null)
|
||||
throw new InvalidOperationException("Found corrupt resume file, cannot continue...");
|
||||
throw new InvalidOperationException(Localization.Core.Found_corrupt_resume_file_cannot_continue);
|
||||
|
||||
if(oldTry.Software.Name != "Aaru" ||
|
||||
oldTry.Software.OperatingSystem != platform.ToString() ||
|
||||
oldTry.Software.Version != Version.GetVersion())
|
||||
continue;
|
||||
|
||||
if(removable && (oldTry.Manufacturer != manufacturer || oldTry.Model != model ||
|
||||
oldTry.Serial != serial || oldTry.Firmware != firmware))
|
||||
if(removable &&
|
||||
(oldTry.Manufacturer != manufacturer ||
|
||||
oldTry.Model != model ||
|
||||
oldTry.Serial != serial ||
|
||||
oldTry.Firmware != firmware))
|
||||
continue;
|
||||
|
||||
currentTry = oldTry;
|
||||
@@ -131,12 +160,11 @@ static class ResumeSupport
|
||||
break;
|
||||
}
|
||||
|
||||
if(currentTry != null)
|
||||
return;
|
||||
if(currentTry != null) return;
|
||||
|
||||
currentTry = new DumpHardwareType
|
||||
currentTry = new DumpHardware
|
||||
{
|
||||
Software = CommonTypes.Metadata.Version.GetSoftwareType(),
|
||||
Software = CommonTypes.Metadata.Version.GetSoftware(),
|
||||
Manufacturer = manufacturer,
|
||||
Model = model,
|
||||
Serial = serial,
|
||||
@@ -150,16 +178,16 @@ static class ResumeSupport
|
||||
{
|
||||
resume = new Resume
|
||||
{
|
||||
Tries = new List<DumpHardwareType>(),
|
||||
Tries = [],
|
||||
CreationDate = DateTime.UtcNow,
|
||||
BadBlocks = new List<ulong>(),
|
||||
BadBlocks = [],
|
||||
LastBlock = isTape ? 0 : blocks - 1,
|
||||
Tape = isTape
|
||||
};
|
||||
|
||||
currentTry = new DumpHardwareType
|
||||
currentTry = new DumpHardware
|
||||
{
|
||||
Software = CommonTypes.Metadata.Version.GetSoftwareType(),
|
||||
Software = CommonTypes.Metadata.Version.GetSoftware(),
|
||||
Manufacturer = manufacturer,
|
||||
Model = model,
|
||||
Serial = serial,
|
||||
|
||||
@@ -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;
|
||||
using System.Threading;
|
||||
using Aaru.CommonTypes;
|
||||
@@ -39,6 +37,8 @@ using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.CommonTypes.Structs.Devices.SCSI;
|
||||
using Aaru.Decoders.SCSI;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
/// <summary>Implements dumping SCSI and ATAPI devices</summary>
|
||||
public partial class Dump
|
||||
{
|
||||
@@ -60,92 +60,107 @@ public partial class Dump
|
||||
|
||||
if(decSense.HasValue)
|
||||
{
|
||||
ErrorMessage?.
|
||||
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ));
|
||||
|
||||
_dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey,
|
||||
decSense.Value.ASC, decSense.Value.ASCQ);
|
||||
_dumpLog.WriteLine(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ);
|
||||
|
||||
// Just retry, for 5 times
|
||||
if(decSense.Value.ASC == 0x29)
|
||||
{
|
||||
resets++;
|
||||
|
||||
if(resets < 5)
|
||||
goto deviceGotReset;
|
||||
if(resets < 5) goto deviceGotReset;
|
||||
}
|
||||
|
||||
if(decSense.Value.ASC == 0x3A)
|
||||
switch(decSense.Value.ASC)
|
||||
{
|
||||
var leftRetries = 5;
|
||||
|
||||
while(leftRetries > 0)
|
||||
case 0x3A:
|
||||
{
|
||||
PulseProgress?.Invoke("Waiting for drive to become ready");
|
||||
Thread.Sleep(2000);
|
||||
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
|
||||
var leftRetries = 5;
|
||||
|
||||
if(!sense)
|
||||
break;
|
||||
|
||||
decSense = Sense.Decode(senseBuf);
|
||||
|
||||
if(decSense.HasValue)
|
||||
while(leftRetries > 0)
|
||||
{
|
||||
ErrorMessage?.
|
||||
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
|
||||
PulseProgress?.Invoke(Localization.Core.Waiting_for_drive_to_become_ready);
|
||||
Thread.Sleep(2000);
|
||||
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
|
||||
|
||||
_dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h",
|
||||
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
|
||||
if(!sense) break;
|
||||
|
||||
decSense = Sense.Decode(senseBuf);
|
||||
|
||||
if(decSense.HasValue)
|
||||
{
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ);
|
||||
}
|
||||
|
||||
leftRetries--;
|
||||
}
|
||||
|
||||
leftRetries--;
|
||||
}
|
||||
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Please insert media in drive");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(decSense.Value.ASC == 0x04 &&
|
||||
decSense.Value.ASCQ == 0x01)
|
||||
{
|
||||
var leftRetries = 50;
|
||||
|
||||
while(leftRetries > 0)
|
||||
{
|
||||
PulseProgress?.Invoke("Waiting for drive to become ready");
|
||||
Thread.Sleep(2000);
|
||||
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
|
||||
|
||||
if(!sense)
|
||||
break;
|
||||
|
||||
decSense = Sense.Decode(senseBuf);
|
||||
|
||||
if(decSense.HasValue)
|
||||
if(sense)
|
||||
{
|
||||
ErrorMessage?.
|
||||
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Please_insert_media_in_drive);
|
||||
|
||||
_dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
|
||||
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
|
||||
return;
|
||||
}
|
||||
|
||||
leftRetries--;
|
||||
break;
|
||||
}
|
||||
|
||||
if(sense)
|
||||
case 0x04 when decSense.Value.ASCQ == 0x01:
|
||||
{
|
||||
StoppingErrorMessage?.
|
||||
Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
|
||||
var leftRetries = 50;
|
||||
|
||||
return;
|
||||
while(leftRetries > 0)
|
||||
{
|
||||
PulseProgress?.Invoke(Localization.Core.Waiting_for_drive_to_become_ready);
|
||||
Thread.Sleep(2000);
|
||||
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
|
||||
|
||||
if(!sense) break;
|
||||
|
||||
decSense = Sense.Decode(senseBuf);
|
||||
|
||||
if(decSense.HasValue)
|
||||
{
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ);
|
||||
}
|
||||
|
||||
leftRetries--;
|
||||
}
|
||||
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke(string.Format(Localization.Core
|
||||
.Error_testing_unit_was_ready_0,
|
||||
Sense.PrettifySense(senseBuf)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00)
|
||||
/*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00)
|
||||
{
|
||||
if (!deviceReset)
|
||||
{
|
||||
@@ -158,52 +173,58 @@ public partial class Dump
|
||||
Decoders.SCSI.Sense.PrettifySense(senseBuf)));
|
||||
return;
|
||||
}*/
|
||||
// These should be trapped by the OS but seems in some cases they're not
|
||||
else if(decSense.Value.ASC == 0x28)
|
||||
{
|
||||
var leftRetries = 10;
|
||||
|
||||
while(leftRetries > 0)
|
||||
// These should be trapped by the OS but seems in some cases they're not
|
||||
case 0x28:
|
||||
{
|
||||
PulseProgress?.Invoke("Waiting for drive to become ready");
|
||||
Thread.Sleep(2000);
|
||||
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
|
||||
var leftRetries = 10;
|
||||
|
||||
if(!sense)
|
||||
break;
|
||||
|
||||
decSense = Sense.Decode(senseBuf);
|
||||
|
||||
if(decSense.HasValue)
|
||||
while(leftRetries > 0)
|
||||
{
|
||||
ErrorMessage?.
|
||||
Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h");
|
||||
PulseProgress?.Invoke(Localization.Core.Waiting_for_drive_to_become_ready);
|
||||
Thread.Sleep(2000);
|
||||
sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _);
|
||||
|
||||
_dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h",
|
||||
decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ);
|
||||
if(!sense) break;
|
||||
|
||||
decSense = Sense.Decode(senseBuf);
|
||||
|
||||
if(decSense.HasValue)
|
||||
{
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Device_not_ready_Sense,
|
||||
decSense.Value.SenseKey,
|
||||
decSense.Value.ASC,
|
||||
decSense.Value.ASCQ);
|
||||
}
|
||||
|
||||
leftRetries--;
|
||||
}
|
||||
|
||||
leftRetries--;
|
||||
}
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke(string.Format(Localization.Core
|
||||
.Error_testing_unit_was_ready_0,
|
||||
Sense.PrettifySense(senseBuf)));
|
||||
|
||||
if(sense)
|
||||
{
|
||||
StoppingErrorMessage?.
|
||||
Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
StoppingErrorMessage?.Invoke(string.Format(Localization.Core.Error_testing_unit_was_ready_0,
|
||||
Sense.PrettifySense(senseBuf)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StoppingErrorMessage?.Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Unknown testing unit was ready.");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Unknown_sense_testing_unit_was_ready);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -217,7 +238,7 @@ public partial class Dump
|
||||
case PeripheralDeviceTypes.SequentialAccess:
|
||||
if(_dumpRaw)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Tapes cannot be dumped raw.");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Tapes_cannot_be_dumped_raw);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -225,15 +246,20 @@ public partial class Dump
|
||||
if(_outputPlugin is IWritableTapeImage)
|
||||
Ssc();
|
||||
else
|
||||
StoppingErrorMessage?.
|
||||
Invoke("The specified plugin does not support storing streaming tape images.");
|
||||
{
|
||||
StoppingErrorMessage?.Invoke(Localization.Core
|
||||
.The_specified_image_format_cannot_represent_streaming_tapes);
|
||||
}
|
||||
|
||||
return;
|
||||
case PeripheralDeviceTypes.MultiMediaDevice:
|
||||
if(_outputPlugin is IWritableOpticalImage)
|
||||
Mmc();
|
||||
else
|
||||
StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images.");
|
||||
{
|
||||
StoppingErrorMessage?.Invoke(Localization.Core
|
||||
.The_specified_image_format_cannot_represent_optical_discs);
|
||||
}
|
||||
|
||||
return;
|
||||
case PeripheralDeviceTypes.BridgingExpander
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
209
Aaru.Core/Devices/Dumping/Sbc/Cache.cs
Normal file
209
Aaru.Core/Devices/Dumping/Sbc/Cache.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Cache.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2020-2024 Rebecca Wallander
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decryption.DVD;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
/// <summary>
|
||||
/// Dumps data when dumping from a SCSI Block Commands compliant device,
|
||||
/// and reads the data from the device cache
|
||||
/// </summary>
|
||||
/// <param name="blocks">Media blocks</param>
|
||||
/// <param name="maxBlocksToRead">Maximum number of blocks to read in a single command</param>
|
||||
/// <param name="blockSize">Block size in bytes</param>
|
||||
/// <param name="currentTry">Resume information</param>
|
||||
/// <param name="extents">Correctly dump extents</param>
|
||||
/// <param name="currentSpeed">Current speed</param>
|
||||
/// <param name="minSpeed">Minimum speed</param>
|
||||
/// <param name="maxSpeed">Maximum speed</param>
|
||||
/// <param name="totalDuration">Total time spent in commands</param>
|
||||
/// <param name="scsiReader">SCSI reader</param>
|
||||
/// <param name="mhddLog">MHDD log</param>
|
||||
/// <param name="ibgLog">ImgBurn log</param>
|
||||
/// <param name="imageWriteDuration">Total time spent writing to image</param>
|
||||
/// <param name="newTrim">Set if we need to start a trim</param>
|
||||
/// <param name="discKey">The DVD disc key</param>
|
||||
void ReadCacheData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardware currentTry,
|
||||
ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed,
|
||||
ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog,
|
||||
ref double imageWriteDuration, ref bool newTrim, byte[] discKey)
|
||||
{
|
||||
ulong sectorSpeedStart = 0;
|
||||
bool sense;
|
||||
byte[] buffer;
|
||||
uint blocksToRead = maxBlocksToRead;
|
||||
var outputFormat = _outputPlugin as IWritableImage;
|
||||
|
||||
InitProgress?.Invoke();
|
||||
|
||||
if(scsiReader.HldtstReadRaw && _resume.NextBlock > 0)
|
||||
|
||||
// The HL-DT-ST buffer is stored and read in 96-sector chunks. If we start to read at an LBA which is
|
||||
// not modulo 96, the data will not be correctly fetched. Therefore, we begin every resume read with
|
||||
// filling the buffer at a known offset.
|
||||
// TODO: This is very ugly and there probably exist a more elegant way to solve this issue.
|
||||
scsiReader.ReadBlock(out _, _resume.NextBlock - _resume.NextBlock % 96 + 1, out _, out _, out _);
|
||||
|
||||
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
|
||||
|
||||
if(currentSpeed > maxSpeed && currentSpeed > 0) maxSpeed = currentSpeed;
|
||||
|
||||
if(currentSpeed < minSpeed && currentSpeed > 0) minSpeed = currentSpeed;
|
||||
|
||||
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 = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _);
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i, cmdDuration, blocksToRead);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
byte[] tmpBuf;
|
||||
var cmi = new byte[blocksToRead];
|
||||
|
||||
for(uint j = 0; j < blocksToRead; j++)
|
||||
{
|
||||
byte[] key = buffer.Skip((int)(2064 * j + 7)).Take(5).ToArray();
|
||||
|
||||
if(key.All(static k => k == 0))
|
||||
{
|
||||
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
|
||||
_resume.MissingTitleKeys?.Remove(i + j);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CSS.DecryptTitleKey(discKey, key, out tmpBuf);
|
||||
outputFormat.WriteSectorTag(tmpBuf, i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
_resume.MissingTitleKeys?.Remove(i + j);
|
||||
|
||||
if(_storeEncrypted) continue;
|
||||
|
||||
cmi[j] = buffer[2064 * j + 6];
|
||||
}
|
||||
|
||||
// Todo: Flag in the outputFormat that a sector has been decrypted
|
||||
if(!_storeEncrypted)
|
||||
{
|
||||
ErrorNumber errno =
|
||||
outputFormat.ReadSectorsTag(i,
|
||||
blocksToRead,
|
||||
SectorTagType.DvdTitleKeyDecrypted,
|
||||
out byte[] titleKey);
|
||||
|
||||
if(errno != ErrorNumber.NoError)
|
||||
{
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core.Error_retrieving_title_key_for_sector_0,
|
||||
i));
|
||||
}
|
||||
else
|
||||
buffer = CSS.DecryptSectorLong(buffer, titleKey, cmi, blocksToRead);
|
||||
}
|
||||
|
||||
outputFormat.WriteSectorsLong(buffer, i, blocksToRead);
|
||||
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
extents.Add(i, blocksToRead, true);
|
||||
_mediaGraph?.PaintSectorsGood(i, blocksToRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Reset device after X errors
|
||||
if(_stopOnError) return; // TODO: Return more cleanly
|
||||
|
||||
if(i + _skip > blocks) _skip = (uint)(blocks - i);
|
||||
|
||||
// Write empty data
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip);
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
for(ulong b = i; b < i + _skip; b++) _resume.BadBlocks.Add(b);
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration, _skip);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_blocks_from_errored_block_1, _skip, i);
|
||||
i += _skip - blocksToRead;
|
||||
newTrim = true;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += blocksToRead;
|
||||
_resume.NextBlock = i + blocksToRead;
|
||||
|
||||
double elapsed = _speedStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
if(elapsed <= 0) continue;
|
||||
|
||||
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
|
||||
sectorSpeedStart = 0;
|
||||
_speedStopwatch.Restart();
|
||||
}
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
_resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();
|
||||
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -21,20 +21,13 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2020-2022 Rebecca Wallander
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2020-2024 Rebecca Wallander
|
||||
// ****************************************************************************/
|
||||
|
||||
using DVDDecryption = Aaru.Decryption.DVD.Dump;
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
@@ -42,8 +35,15 @@ using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.DVD;
|
||||
using Aaru.Decryption;
|
||||
using Aaru.Decryption.DVD;
|
||||
using Aaru.Settings;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
using DVDDecryption = Aaru.Decryption.DVD.Dump;
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -64,59 +64,56 @@ partial class Dump
|
||||
/// <param name="newTrim">Set if we need to start a trim</param>
|
||||
/// <param name="dvdDecrypt">DVD CSS decryption module</param>
|
||||
/// <param name="discKey">The DVD disc key</param>
|
||||
void ReadSbcData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry,
|
||||
void ReadSbcData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardware currentTry,
|
||||
ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed,
|
||||
ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog,
|
||||
ref double imageWriteDuration, ref bool newTrim, ref DVDDecryption dvdDecrypt, byte[] discKey)
|
||||
ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog,
|
||||
ref double imageWriteDuration, ref bool newTrim, ref DVDDecryption dvdDecrypt, byte[] discKey)
|
||||
{
|
||||
ulong sectorSpeedStart = 0;
|
||||
bool sense;
|
||||
DateTime timeSpeedStart = DateTime.UtcNow;
|
||||
byte[] buffer;
|
||||
uint blocksToRead = maxBlocksToRead;
|
||||
var outputFormat = _outputPlugin as IWritableImage;
|
||||
ulong sectorSpeedStart = 0;
|
||||
bool sense;
|
||||
byte[] buffer;
|
||||
uint blocksToRead = maxBlocksToRead;
|
||||
var outputFormat = _outputPlugin as IWritableImage;
|
||||
|
||||
InitProgress?.Invoke();
|
||||
_speedStopwatch.Reset();
|
||||
|
||||
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(blocks - i < blocksToRead)
|
||||
blocksToRead = (uint)(blocks - i);
|
||||
if(blocks - i < blocksToRead) blocksToRead = (uint)(blocks - i);
|
||||
|
||||
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);
|
||||
|
||||
_speedStopwatch.Start();
|
||||
sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _);
|
||||
totalDuration += cmdDuration;
|
||||
_speedStopwatch.Stop();
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
if(Settings.Current.EnableDecryption &&
|
||||
discKey != null &&
|
||||
_titleKeys)
|
||||
if(Settings.Settings.Current.EnableDecryption && discKey != null && _titleKeys)
|
||||
{
|
||||
for(ulong j = 0; j < blocksToRead; j++)
|
||||
{
|
||||
if(_aborted)
|
||||
break;
|
||||
if(_aborted) break;
|
||||
|
||||
if(!_resume.MissingTitleKeys.Contains(i + j))
|
||||
|
||||
@@ -125,65 +122,40 @@ partial class Dump
|
||||
|
||||
byte[] tmpBuf;
|
||||
|
||||
bool tmpSense = dvdDecrypt.ReadTitleKey(out tmpBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm,
|
||||
i + j, _dev.Timeout, out _);
|
||||
bool tmpSense = dvdDecrypt.ReadTitleKey(out tmpBuf,
|
||||
out _,
|
||||
DvdCssKeyClass.DvdCssCppmOrCprm,
|
||||
i + j,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(!tmpSense)
|
||||
if(tmpSense) continue;
|
||||
|
||||
CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(tmpBuf, dvdDecrypt.BusKey);
|
||||
|
||||
if(titleKey.HasValue)
|
||||
outputFormat.WriteSectorTag([titleKey.Value.CMI], i + j, SectorTagType.DvdSectorCmi);
|
||||
else
|
||||
continue;
|
||||
|
||||
// According to libdvdcss, if the key is all zeroes, the sector is actually
|
||||
// not encrypted even if the CMI says it is.
|
||||
if(titleKey.Value.Key.All(static k => k == 0))
|
||||
{
|
||||
CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(tmpBuf, dvdDecrypt.BusKey);
|
||||
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], i + j, SectorTagType.DvdSectorTitleKey);
|
||||
|
||||
if(titleKey.HasValue)
|
||||
outputFormat.WriteSectorTag(new[]
|
||||
{
|
||||
titleKey.Value.CMI
|
||||
}, i + j, SectorTagType.DvdCmi);
|
||||
else
|
||||
continue;
|
||||
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
|
||||
// If the CMI bit is 1, the sector is using copy protection, else it is not
|
||||
if((titleKey.Value.CMI & 0x80) >> 7 == 0)
|
||||
{
|
||||
// The CMI indicates this sector is not encrypted.
|
||||
outputFormat.WriteSectorTag(new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0
|
||||
}, i + j, SectorTagType.DvdTitleKey);
|
||||
_resume.MissingTitleKeys.Remove(i + j);
|
||||
|
||||
outputFormat.WriteSectorTag(new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0
|
||||
}, i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
|
||||
_resume.MissingTitleKeys.Remove(i + j);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// According to libdvdcss, if the key is all zeroes, the sector is actually
|
||||
// not encrypted even if the CMI says it is.
|
||||
if(titleKey.Value.Key.All(k => k == 0))
|
||||
{
|
||||
outputFormat.WriteSectorTag(new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0
|
||||
}, i + j, SectorTagType.DvdTitleKey);
|
||||
|
||||
outputFormat.WriteSectorTag(new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0
|
||||
}, i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
|
||||
_resume.MissingTitleKeys.Remove(i + j);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
outputFormat.WriteSectorTag(titleKey.Value.Key, i + j, SectorTagType.DvdTitleKey);
|
||||
_resume.MissingTitleKeys.Remove(i + j);
|
||||
|
||||
CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out tmpBuf);
|
||||
outputFormat.WriteSectorTag(tmpBuf, i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
continue;
|
||||
}
|
||||
|
||||
outputFormat.WriteSectorTag(titleKey.Value.Key, i + j, SectorTagType.DvdSectorTitleKey);
|
||||
_resume.MissingTitleKeys.Remove(i + j);
|
||||
|
||||
CSS.DecryptTitleKey(discKey, titleKey.Value.Key, out tmpBuf);
|
||||
outputFormat.WriteSectorTag(tmpBuf, i + j, SectorTagType.DvdTitleKeyDecrypted);
|
||||
}
|
||||
|
||||
if(!_storeEncrypted)
|
||||
@@ -191,84 +163,89 @@ partial class Dump
|
||||
// Todo: Flag in the outputFormat that a sector has been decrypted
|
||||
{
|
||||
ErrorNumber errno =
|
||||
outputFormat.ReadSectorsTag(i, blocksToRead, SectorTagType.DvdCmi, out byte[] cmi);
|
||||
outputFormat.ReadSectorsTag(i, blocksToRead, SectorTagType.DvdSectorCmi, out byte[] cmi);
|
||||
|
||||
if(errno != ErrorNumber.NoError)
|
||||
ErrorMessage?.Invoke($"Error retrieving CMI for sector {i}");
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core.Error_retrieving_CMI_for_sector_0, i));
|
||||
else
|
||||
{
|
||||
errno = outputFormat.ReadSectorsTag(i, blocksToRead, SectorTagType.DvdTitleKeyDecrypted,
|
||||
errno = outputFormat.ReadSectorsTag(i,
|
||||
blocksToRead,
|
||||
SectorTagType.DvdTitleKeyDecrypted,
|
||||
out byte[] titleKey);
|
||||
|
||||
if(errno != ErrorNumber.NoError)
|
||||
ErrorMessage?.Invoke($"Error retrieving title key for sector {i}");
|
||||
{
|
||||
ErrorMessage?.Invoke(string.Format(Localization.Core
|
||||
.Error_retrieving_title_key_for_sector_0,
|
||||
i));
|
||||
}
|
||||
else
|
||||
buffer = CSS.DecryptSector(buffer, cmi, titleKey, blocksToRead, blockSize);
|
||||
buffer = CSS.DecryptSector(buffer, titleKey, cmi, blocksToRead, blockSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration, blocksToRead);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(buffer, i, blocksToRead);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
extents.Add(i, blocksToRead, true);
|
||||
_mediaGraph?.PaintSectorsGood(i, blocksToRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_dev.Manufacturer.ToLowerInvariant() == "insite")
|
||||
if(_dev.Manufacturer.Equals("insite", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_resume.BadBlocks.Add(i);
|
||||
_resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();
|
||||
_resume.NextBlock++;
|
||||
_aborted = true;
|
||||
|
||||
_dumpLog?.
|
||||
WriteLine("INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately.");
|
||||
_dumpLog?.WriteLine(Localization.Core
|
||||
.INSITE_floptical_drives_get_crazy_on_the_SCSI_bus_when_an_error_is_found);
|
||||
|
||||
UpdateStatus?.
|
||||
Invoke("INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately");
|
||||
UpdateStatus?.Invoke(Localization.Core
|
||||
.INSITE_floptical_drives_get_crazy_on_the_SCSI_bus_when_an_error_is_found);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Reset device after X errors
|
||||
if(_stopOnError)
|
||||
return; // TODO: Return more cleanly
|
||||
if(_stopOnError) return; // TODO: Return more cleanly
|
||||
|
||||
if(i + _skip > blocks)
|
||||
_skip = (uint)(blocks - i);
|
||||
if(i + _skip > blocks) _skip = (uint)(blocks - i);
|
||||
|
||||
// Write empty data
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
for(ulong b = i; b < i + _skip; b++)
|
||||
_resume.BadBlocks.Add(b);
|
||||
for(ulong b = i; b < i + _skip; b++) _resume.BadBlocks.Add(b);
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration, _skip);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_blocks_from_errored_block_1, _skip, i);
|
||||
i += _skip - blocksToRead;
|
||||
newTrim = true;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += blocksToRead;
|
||||
_resume.NextBlock = i + blocksToRead;
|
||||
|
||||
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
double 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();
|
||||
_resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();
|
||||
|
||||
EndProgress?.Invoke();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,19 +21,12 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2022 Natalia Portillo
|
||||
// Copyright © 2020-2022 Rebecca Wallander
|
||||
// Copyright © 2011-2024 Natalia Portillo
|
||||
// Copyright © 2020-2024 Rebecca Wallander
|
||||
// ****************************************************************************/
|
||||
|
||||
using DVDDecryption = Aaru.Decryption.DVD.Dump;
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
@@ -44,7 +37,13 @@ using Aaru.Decoders.SCSI;
|
||||
using Aaru.Decryption;
|
||||
using Aaru.Decryption.DVD;
|
||||
using Aaru.Devices;
|
||||
using Schemas;
|
||||
using DVDDecryption = Aaru.Decryption.DVD.Dump;
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -54,7 +53,7 @@ partial class Dump
|
||||
/// <param name="totalDuration">Total time spent in commands</param>
|
||||
/// <param name="scsiReader">SCSI reader</param>
|
||||
/// <param name="blankExtents">Blank extents</param>
|
||||
void RetrySbcData(Reader scsiReader, DumpHardwareType currentTry, ExtentsULong extents, ref double totalDuration,
|
||||
void RetrySbcData(Reader scsiReader, DumpHardware currentTry, ExtentsULong extents, ref double totalDuration,
|
||||
ExtentsULong blankExtents)
|
||||
{
|
||||
var pass = 1;
|
||||
@@ -75,12 +74,25 @@ partial class Dump
|
||||
Modes.ModePage_01_MMC pgMmc;
|
||||
Modes.ModePage_01 pg;
|
||||
|
||||
sense = _dev.ModeSense6(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
|
||||
sense = _dev.ModeSense6(out buffer,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(sense)
|
||||
Modes.DecodedMode? dcMode6 = null;
|
||||
if(!sense) dcMode6 = Modes.DecodeMode6(buffer, _dev.ScsiType);
|
||||
|
||||
if(sense || dcMode6 is null)
|
||||
{
|
||||
sense = _dev.ModeSense10(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout,
|
||||
sense = _dev.ModeSense10(out buffer,
|
||||
out _,
|
||||
false,
|
||||
ScsiModeSensePageControl.Current,
|
||||
0x01,
|
||||
_dev.Timeout,
|
||||
out _);
|
||||
|
||||
if(!sense)
|
||||
@@ -88,19 +100,23 @@ partial class Dump
|
||||
Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(buffer, _dev.ScsiType);
|
||||
|
||||
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(buffer, _dev.ScsiType);
|
||||
|
||||
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)
|
||||
@@ -158,15 +174,15 @@ 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);
|
||||
@@ -191,36 +207,36 @@ 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(pg)
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
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 byte[] 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;
|
||||
@@ -235,17 +251,47 @@ 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(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));
|
||||
}
|
||||
|
||||
sense = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError,
|
||||
if(scsiReader.HldtstReadRaw)
|
||||
|
||||
// The HL-DT-ST buffer is stored and read in 96-sector chunks. If we start to read at an LBA which is
|
||||
// not modulo 96, the data will not be correctly fetched. Therefore, we begin every recovery read with
|
||||
// filling the buffer at a known offset.
|
||||
// TODO: This is very ugly and there probably exist a more elegant way to solve this issue.
|
||||
scsiReader.ReadBlock(out _, badSector - badSector % 96 + 1, out _, out _, out _);
|
||||
|
||||
sense = scsiReader.ReadBlock(out buffer,
|
||||
badSector,
|
||||
out double cmdDuration,
|
||||
out recoveredError,
|
||||
out blankCheck);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
@@ -256,8 +302,8 @@ partial class Dump
|
||||
blankExtents.Add(badSector, badSector);
|
||||
newBlank = true;
|
||||
|
||||
UpdateStatus?.Invoke($"Found blank block {badSector} in pass {pass}.");
|
||||
_dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector, pass);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Found_blank_block_0_in_pass_1, badSector, pass));
|
||||
_dumpLog.WriteLine(Localization.Core.Found_blank_block_0_in_pass_1, badSector, pass);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -267,23 +313,24 @@ partial class Dump
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
outputFormat.WriteSector(buffer, badSector);
|
||||
UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
|
||||
_dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_block_0_in_pass_1,
|
||||
badSector,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_block_0_in_pass_1, badSector, pass);
|
||||
}
|
||||
else if(runningPersistent)
|
||||
outputFormat.WriteSector(buffer, badSector);
|
||||
else if(runningPersistent) outputFormat.WriteSector(buffer, badSector);
|
||||
}
|
||||
|
||||
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 repeatRetry;
|
||||
}
|
||||
@@ -293,25 +340,20 @@ 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);
|
||||
|
||||
UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status).");
|
||||
_dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
|
||||
UpdateStatus?.Invoke(Localization.Core.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 _, true, false, _dev.Timeout, out _);
|
||||
|
||||
if(sense)
|
||||
_dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _);
|
||||
if(sense) _dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _);
|
||||
}
|
||||
|
||||
if(newBlank)
|
||||
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
|
||||
if(newBlank) _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents).ToArray();
|
||||
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
@@ -322,7 +364,8 @@ partial class Dump
|
||||
var forward = true;
|
||||
bool sense;
|
||||
byte[] buffer;
|
||||
var outputFormat = _outputPlugin as IWritableImage;
|
||||
|
||||
if(_outputPlugin is not IWritableImage outputFormat) return;
|
||||
|
||||
InitProgress?.Invoke();
|
||||
|
||||
@@ -333,79 +376,79 @@ partial class Dump
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PulseProgress?.Invoke(string.Format("Retrying title key {0}, pass {1}, {2}", missingKey, pass,
|
||||
forward ? "forward" : "reverse"));
|
||||
PulseProgress?.Invoke(forward
|
||||
? string.Format(Localization.Core.Retrying_title_key_0_pass_1_forward,
|
||||
missingKey,
|
||||
pass)
|
||||
: string.Format(Localization.Core.Retrying_title_key_0_pass_1_reverse,
|
||||
missingKey,
|
||||
pass));
|
||||
|
||||
sense = dvdDecrypt.ReadTitleKey(out buffer, out _, DvdCssKeyClass.DvdCssCppmOrCprm, missingKey,
|
||||
_dev.Timeout, out double cmdDuration);
|
||||
sense = dvdDecrypt.ReadTitleKey(out buffer,
|
||||
out _,
|
||||
DvdCssKeyClass.DvdCssCppmOrCprm,
|
||||
missingKey,
|
||||
_dev.Timeout,
|
||||
out double cmdDuration);
|
||||
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(sense || _dev.Error) continue;
|
||||
|
||||
CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(buffer, dvdDecrypt.BusKey);
|
||||
|
||||
if(!titleKey.HasValue) continue;
|
||||
|
||||
outputFormat.WriteSectorTag([titleKey.Value.CMI], missingKey, SectorTagType.DvdSectorCmi);
|
||||
|
||||
// If the CMI bit is 1, the sector is using copy protection, else it is not
|
||||
// If the decoded title key is zeroed, there should be no copy protection
|
||||
if((titleKey.Value.CMI & 0x80) >> 7 == 0 || titleKey.Value.Key.All(k => k == 0))
|
||||
{
|
||||
CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(buffer, dvdDecrypt.BusKey);
|
||||
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], missingKey, SectorTagType.DvdSectorTitleKey);
|
||||
|
||||
if(titleKey.HasValue)
|
||||
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], missingKey, SectorTagType.DvdTitleKeyDecrypted);
|
||||
|
||||
_resume.MissingTitleKeys.Remove(missingKey);
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_title_key_0_in_pass_1,
|
||||
missingKey,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_title_key_0_in_pass_1, missingKey, pass);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFormat.WriteSectorTag(titleKey.Value.Key, missingKey, SectorTagType.DvdSectorTitleKey);
|
||||
_resume.MissingTitleKeys.Remove(missingKey);
|
||||
|
||||
if(discKey != null)
|
||||
{
|
||||
outputFormat.WriteSectorTag(new[]
|
||||
{
|
||||
titleKey.Value.CMI
|
||||
}, missingKey, SectorTagType.DvdCmi);
|
||||
|
||||
// If the CMI bit is 1, the sector is using copy protection, else it is not
|
||||
// If the decoded title key is zeroed, there should be no copy protection
|
||||
if((titleKey.Value.CMI & 0x80) >> 7 == 0 ||
|
||||
titleKey.Value.Key.All(k => k == 0))
|
||||
{
|
||||
outputFormat.WriteSectorTag(new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0
|
||||
}, missingKey, SectorTagType.DvdTitleKey);
|
||||
|
||||
outputFormat.WriteSectorTag(new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0
|
||||
}, missingKey, SectorTagType.DvdTitleKeyDecrypted);
|
||||
|
||||
_resume.MissingTitleKeys.Remove(missingKey);
|
||||
UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}.");
|
||||
_dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputFormat.WriteSectorTag(titleKey.Value.Key, missingKey, SectorTagType.DvdTitleKey);
|
||||
_resume.MissingTitleKeys.Remove(missingKey);
|
||||
|
||||
if(discKey != null)
|
||||
{
|
||||
CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out buffer);
|
||||
outputFormat.WriteSectorTag(buffer, missingKey, SectorTagType.DvdTitleKeyDecrypted);
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}.");
|
||||
_dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass);
|
||||
}
|
||||
CSS.DecryptTitleKey(discKey, titleKey.Value.Key, out buffer);
|
||||
outputFormat.WriteSectorTag(buffer, missingKey, SectorTagType.DvdTitleKeyDecrypted);
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_title_key_0_in_pass_1,
|
||||
missingKey,
|
||||
pass));
|
||||
|
||||
_dumpLog.WriteLine(Localization.Core.Correctly_retried_title_key_0_in_pass_1, missingKey, pass);
|
||||
}
|
||||
}
|
||||
|
||||
if(pass < _retryPasses &&
|
||||
!_aborted &&
|
||||
_resume.MissingTitleKeys.Count > 0)
|
||||
if(pass < _retryPasses && !_aborted && _resume.MissingTitleKeys.Count > 0)
|
||||
{
|
||||
pass++;
|
||||
forward = !forward;
|
||||
_resume.MissingTitleKeys.Sort();
|
||||
|
||||
if(!forward)
|
||||
_resume.MissingTitleKeys.Reverse();
|
||||
if(!forward) _resume.MissingTitleKeys.Reverse();
|
||||
|
||||
goto repeatRetry;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
|
||||
|
||||
// ReSharper disable JoinDeclarationAndInitializer
|
||||
// ReSharper disable InlineOutVariableDeclaration
|
||||
// ReSharper disable TooWideLocalVariableScope
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Aaru.Console;
|
||||
using Aaru.Core.Logging;
|
||||
using Aaru.Decoders.SCSI;
|
||||
using Aaru.Helpers;
|
||||
using Schemas;
|
||||
using Humanizer;
|
||||
using Humanizer.Bytes;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -37,10 +37,10 @@ partial class Dump
|
||||
/// <param name="imageWriteDuration">Total time spent writing to image</param>
|
||||
/// <param name="newTrim">Set if we need to start a trim</param>
|
||||
/// <param name="blankExtents">Blank extents</param>
|
||||
void ReadOpticalData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry,
|
||||
void ReadOpticalData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardware currentTry,
|
||||
ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed,
|
||||
ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog,
|
||||
ref double imageWriteDuration, ref bool newTrim, ref ExtentsULong blankExtents)
|
||||
ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog,
|
||||
ref double imageWriteDuration, ref bool newTrim, ref ExtentsULong blankExtents)
|
||||
{
|
||||
const uint maxBlocks = 256;
|
||||
var writtenExtents = new ExtentsULong();
|
||||
@@ -53,7 +53,6 @@ partial class Dump
|
||||
bool sense;
|
||||
byte[] buffer;
|
||||
ulong sectorSpeedStart = 0;
|
||||
DateTime timeSpeedStart = DateTime.UtcNow;
|
||||
var canMediumScan = true;
|
||||
var outputFormat = _outputPlugin as IWritableImage;
|
||||
|
||||
@@ -63,40 +62,46 @@ partial class Dump
|
||||
{
|
||||
blankExtents = new ExtentsULong();
|
||||
|
||||
written = _dev.MediumScan(out buffer, true, false, false, false, false, 0, 1, 1, out _, out _,
|
||||
uint.MaxValue, out _);
|
||||
written = _dev.MediumScan(out buffer,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
out _,
|
||||
out _,
|
||||
uint.MaxValue,
|
||||
out _);
|
||||
|
||||
DecodedSense? decodedSense = Sense.Decode(buffer);
|
||||
|
||||
if(_dev.LastError != 0 ||
|
||||
decodedSense?.SenseKey == SenseKeys.IllegalRequest)
|
||||
if(_dev.LastError != 0 || decodedSense?.SenseKey == SenseKeys.IllegalRequest)
|
||||
{
|
||||
UpdateStatus?.
|
||||
Invoke("The current environment doesn't support the medium scan command, dump will take much longer than normal.");
|
||||
UpdateStatus?.Invoke(Localization.Core.The_current_environment_doesn_t_support_the_medium_scan_command);
|
||||
|
||||
canMediumScan = false;
|
||||
writtenExtents.Add(0, blocks - 1);
|
||||
}
|
||||
|
||||
// TODO: Find a place where MEDIUM SCAN works properly
|
||||
else if(buffer?.Length > 0 &&
|
||||
!ArrayHelpers.ArrayIsNullOrEmpty(buffer))
|
||||
AaruConsole.
|
||||
WriteLine("Please open a bug report in github with the manufacturer and model of this device, as well as your operating system name and version and this message: This environment correctly supports MEDIUM SCAN command.");
|
||||
else if(buffer?.Length > 0 && !ArrayHelpers.ArrayIsNullOrEmpty(buffer))
|
||||
AaruConsole.WriteLine(Localization.Core.MEDIUM_SCAN_github_plead_message);
|
||||
|
||||
changingCounter = false;
|
||||
changingWritten = false;
|
||||
|
||||
for(uint b = 0; b < blocks; b += c)
|
||||
{
|
||||
if(!canMediumScan)
|
||||
break;
|
||||
if(!canMediumScan) break;
|
||||
|
||||
if(_aborted)
|
||||
{
|
||||
_resume.BlankExtents = null;
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -114,15 +119,33 @@ partial class Dump
|
||||
changingCounter = false;
|
||||
}
|
||||
|
||||
if(b + c >= blocks)
|
||||
c = (uint)(blocks - b);
|
||||
if(b + c >= blocks) c = (uint)(blocks - b);
|
||||
|
||||
UpdateProgress?.
|
||||
Invoke($"Scanning for {c} {(written ? "written" : "blank")} blocks starting in block {b}", b,
|
||||
(long)blocks);
|
||||
UpdateProgress?.Invoke(written
|
||||
? string.Format(Localization.Core
|
||||
.Scanning_for_0_written_blocks_starting_in_block_1,
|
||||
c,
|
||||
b)
|
||||
: string.Format(Localization.Core
|
||||
.Scanning_for_0_blank_blocks_starting_in_block_1,
|
||||
c,
|
||||
b),
|
||||
b,
|
||||
(long)blocks);
|
||||
|
||||
conditionMet = _dev.MediumScan(out _, written, false, false, false, false, b, c, c, out _, out _,
|
||||
uint.MaxValue, out _);
|
||||
conditionMet = _dev.MediumScan(out _,
|
||||
written,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
b,
|
||||
c,
|
||||
c,
|
||||
out _,
|
||||
out _,
|
||||
uint.MaxValue,
|
||||
out _);
|
||||
|
||||
if(conditionMet)
|
||||
{
|
||||
@@ -131,8 +154,7 @@ partial class Dump
|
||||
else
|
||||
blankExtents.Add(b, c, true);
|
||||
|
||||
if(c < maxBlocks)
|
||||
changingWritten = true;
|
||||
if(c < maxBlocks) changingWritten = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -143,8 +165,7 @@ partial class Dump
|
||||
|
||||
changingCounter = true;
|
||||
|
||||
if(c != 0)
|
||||
continue;
|
||||
if(c != 0) continue;
|
||||
|
||||
written = !written;
|
||||
c = maxBlocks;
|
||||
@@ -152,7 +173,7 @@ partial class Dump
|
||||
}
|
||||
|
||||
if(_resume != null && canMediumScan)
|
||||
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
|
||||
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents).ToArray();
|
||||
|
||||
EndProgress?.Invoke();
|
||||
}
|
||||
@@ -167,8 +188,8 @@ partial class Dump
|
||||
|
||||
if(writtenExtents.Count == 0)
|
||||
{
|
||||
UpdateStatus?.Invoke("Cannot dump empty media!");
|
||||
_dumpLog.WriteLine("Cannot dump empty media!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Cannot_dump_empty_media);
|
||||
_dumpLog.WriteLine(Localization.Core.Cannot_dump_empty_media);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -179,91 +200,87 @@ partial class Dump
|
||||
|
||||
foreach(Tuple<ulong, ulong> extent in extentsToDump)
|
||||
{
|
||||
if(extent.Item2 < _resume.NextBlock)
|
||||
continue; // Skip this extent
|
||||
if(extent.Item2 < _resume.NextBlock) continue; // Skip this extent
|
||||
|
||||
ulong nextBlock = extent.Item1;
|
||||
|
||||
if(extent.Item1 < _resume.NextBlock)
|
||||
nextBlock = (uint)_resume.NextBlock;
|
||||
if(extent.Item1 < _resume.NextBlock) nextBlock = (uint)_resume.NextBlock;
|
||||
|
||||
_speedStopwatch.Restart();
|
||||
|
||||
for(ulong i = nextBlock; i <= extent.Item2; i += blocksToRead)
|
||||
{
|
||||
if(_aborted)
|
||||
{
|
||||
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
||||
UpdateStatus?.Invoke("Aborted!");
|
||||
_dumpLog.WriteLine("Aborted!");
|
||||
UpdateStatus?.Invoke(Localization.Core.Aborted);
|
||||
_dumpLog.WriteLine(Localization.Core.Aborted);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if(extent.Item2 + 1 - i < blocksToRead)
|
||||
blocksToRead = (uint)(extent.Item2 + 1 - i);
|
||||
if(extent.Item2 + 1 - i < blocksToRead) blocksToRead = (uint)(extent.Item2 + 1 - i);
|
||||
|
||||
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 = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _);
|
||||
totalDuration += cmdDuration;
|
||||
|
||||
if(!sense &&
|
||||
!_dev.Error)
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
mhddLog.Write(i, cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration, blocksToRead);
|
||||
ibgLog.Write(i, currentSpeed * 1024);
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(buffer, i, blocksToRead);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
extents.Add(i, blocksToRead, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Reset device after X errors
|
||||
if(_stopOnError)
|
||||
return; // TODO: Return more cleanly
|
||||
if(_stopOnError) return; // TODO: Return more cleanly
|
||||
|
||||
if(i + _skip > extent.Item2 + 1)
|
||||
_skip = (uint)(extent.Item2 + 1 - i);
|
||||
if(i + _skip > extent.Item2 + 1) _skip = (uint)(extent.Item2 + 1 - i);
|
||||
|
||||
// Write empty data
|
||||
DateTime writeStart = DateTime.Now;
|
||||
_writeStopwatch.Restart();
|
||||
outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip);
|
||||
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
for(ulong b = i; b < i + _skip; b++)
|
||||
_resume.BadBlocks.Add(b);
|
||||
for(ulong b = i; b < i + _skip; b++) _resume.BadBlocks.Add(b);
|
||||
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
||||
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration, _skip);
|
||||
|
||||
ibgLog.Write(i, 0);
|
||||
_dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
|
||||
_dumpLog.WriteLine(Localization.Core.Skipping_0_blocks_from_errored_block_1, _skip, i);
|
||||
i += _skip - blocksToRead;
|
||||
newTrim = true;
|
||||
}
|
||||
|
||||
_writeStopwatch.Stop();
|
||||
sectorSpeedStart += blocksToRead;
|
||||
_resume.NextBlock = i + blocksToRead;
|
||||
|
||||
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
||||
double elapsed = _speedStopwatch.Elapsed.TotalSeconds;
|
||||
|
||||
if(elapsed <= 0)
|
||||
continue;
|
||||
if(elapsed <= 0) continue;
|
||||
|
||||
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
|
||||
sectorSpeedStart = 0;
|
||||
timeSpeedStart = DateTime.UtcNow;
|
||||
_speedStopwatch.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
_resume.BadBlocks = _resume.BadBlocks.Distinct().ToList();
|
||||
|
||||
EndProgress?.Invoke();
|
||||
|
||||
@@ -21,20 +21,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 Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
using Aaru.CommonTypes.Interfaces;
|
||||
using Schemas;
|
||||
|
||||
namespace Aaru.Core.Devices.Dumping;
|
||||
|
||||
partial class Dump
|
||||
{
|
||||
@@ -43,18 +41,18 @@ partial class Dump
|
||||
/// <param name="extents">Correctly dump extents</param>
|
||||
/// <param name="currentTry">Resume information</param>
|
||||
/// <param name="blankExtents">Blank extents</param>
|
||||
void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardwareType currentTry, ExtentsULong blankExtents)
|
||||
void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardware currentTry, ExtentsULong blankExtents)
|
||||
{
|
||||
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
||||
bool sense;
|
||||
bool recoveredError;
|
||||
bool blankCheck;
|
||||
byte[] buffer;
|
||||
var newBlank = false;
|
||||
var newBlank = false;
|
||||
|
||||
if(_outputPlugin is not IWritableImage outputFormat)
|
||||
{
|
||||
StoppingErrorMessage?.Invoke("Image is not writable, aborting...");
|
||||
StoppingErrorMessage?.Invoke(Localization.Core.Image_is_not_writable_aborting);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -64,13 +62,13 @@ 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));
|
||||
|
||||
sense = scsiReader.ReadBlock(out buffer, badSector, out double _, out recoveredError, out blankCheck);
|
||||
|
||||
@@ -80,22 +78,20 @@ partial class Dump
|
||||
newBlank = true;
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
|
||||
UpdateStatus?.Invoke($"Found blank block {badSector}.");
|
||||
_dumpLog.WriteLine("Found blank block {0}.", badSector);
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core.Found_blank_block_0, badSector));
|
||||
_dumpLog.WriteLine(Localization.Core.Found_blank_block_0, badSector);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if((sense || _dev.Error) &&
|
||||
!recoveredError)
|
||||
continue;
|
||||
if((sense || _dev.Error) && !recoveredError) continue;
|
||||
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
outputFormat.WriteSector(buffer, badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
}
|
||||
|
||||
if(newBlank)
|
||||
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
|
||||
if(newBlank) _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents).ToArray();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user