Allocate SENSE buffer only once.

This commit is contained in:
2025-08-22 19:57:09 +01:00
parent 8e2fdd91a6
commit e4f55d3b3c
73 changed files with 1892 additions and 2565 deletions

View File

@@ -148,15 +148,15 @@ partial class Dump
bool cdiReadyReadAsAudio, int offsetBytes, int sectorsForOffset,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
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;
ulong sectorSpeedStart = 0; // Used to calculate correct speed
bool sense; // Sense indicator
byte[] cmdBuf; // Data buffer
ReadOnlySpan<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;
@@ -337,7 +337,7 @@ partial class Dump
}
else
{
_errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf.ToArray());
leadOutExtents.Add(i + r, firstTrack.EndSector);
@@ -469,7 +469,7 @@ partial class Dump
}
else
{
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf.ToArray());
_resume.NextBlock = firstTrack.EndSector + 1;

View File

@@ -101,13 +101,13 @@ partial class Dump
Dictionary<byte, string> isrcs, ref string mcn, HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
ulong sectorSpeedStart = 0; // Used to calculate correct speed
uint blocksToRead; // How many sectors to read at once
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
byte[] senseBuf = null; // Sense buffer
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
ulong sectorSpeedStart = 0; // Used to calculate correct speed
uint blocksToRead; // How many sectors to read at once
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
ReadOnlySpan<byte> senseBuf = null; // Sense buffer
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
newTrim = false;
PlextorSubchannel supportedPlextorSubchannel;
var outputFormat = _outputPlugin as IWritableImage;
@@ -859,7 +859,7 @@ partial class Dump
}
else
{
_errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf.ToArray());
// Write empty data
_writeStopwatch.Restart();
@@ -1054,7 +1054,7 @@ partial class Dump
continue;
}
_errorLog?.WriteLine(firstSectorToRead, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(firstSectorToRead, _dev.Error, _dev.LastError, senseBuf.ToArray());
// TODO: Reset device after X errors
if(_stopOnError) return; // TODO: Return more cleanly

View File

@@ -80,13 +80,13 @@ partial class Dump
ref string mcn, HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack, bool supportsLongSectors)
{
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration; // Command execution time
const uint sectorSize = 2352; // Full sector size
byte[] senseBuf = null; // Sense buffer
PlextorSubchannel supportedPlextorSubchannel;
var outputOptical = _outputPlugin as IWritableOpticalImage;
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration; // Command execution time
const uint sectorSize = 2352; // Full sector size
ReadOnlySpan<byte> senseBuf = null; // Sense buffer
PlextorSubchannel supportedPlextorSubchannel;
var outputOptical = _outputPlugin as IWritableOpticalImage;
supportedPlextorSubchannel = supportedSubchannel switch
{
@@ -457,7 +457,7 @@ partial class Dump
if(sense || _dev.Error)
{
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
if(!runningPersistent) continue;
@@ -465,9 +465,8 @@ partial class Dump
// 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
@@ -499,7 +498,7 @@ partial class Dump
sectorsNotEvenPartial.Remove(badSector);
}
else
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
if(supportedSubchannel != MmcSubchannel.None)
{
@@ -647,7 +646,7 @@ partial class Dump
if(sense || _dev.Error)
{
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
continue;
}
@@ -742,12 +741,12 @@ partial class Dump
Dictionary<byte, string> isrcs, ref string mcn, HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration; // Command execution time
byte[] senseBuf = null; // Sense buffer
PlextorSubchannel supportedPlextorSubchannel;
var outputOptical = _outputPlugin as IWritableOpticalImage;
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration; // Command execution time
ReadOnlySpan<byte> senseBuf = null; // Sense buffer
PlextorSubchannel supportedPlextorSubchannel;
var outputOptical = _outputPlugin as IWritableOpticalImage;
if(supportedSubchannel == MmcSubchannel.None || desiredSubchannel == MmcSubchannel.None) return;
@@ -836,7 +835,7 @@ partial class Dump
if(sense || _dev.Error)
{
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
continue;
}

View File

@@ -90,11 +90,11 @@ partial class Dump
ref string mcn, Track[] tracks, HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
byte[] senseBuf = null;
var outputOptical = _outputPlugin as IWritableOpticalImage;
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
ReadOnlySpan<byte> senseBuf = null;
var outputOptical = _outputPlugin as IWritableOpticalImage;
UpdateStatus?.Invoke(Localization.Core.Reading_lead_outs);
@@ -253,7 +253,7 @@ partial class Dump
}
else
{
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf.ToArray());
// TODO: Reset device after X errors
if(_stopOnError) return; // TODO: Return more cleanly
@@ -329,11 +329,11 @@ partial class Dump
ref string mcn, Track[] tracks, HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
byte[] senseBuf = null;
var outputOptical = _outputPlugin as IWritableOpticalImage;
byte[] cmdBuf = null; // Data buffer
const uint sectorSize = 2352; // Full sector size
bool sense = true; // Sense indicator
ReadOnlySpan<byte> senseBuf = null;
var outputOptical = _outputPlugin as IWritableOpticalImage;
AaruLogging.WriteLine("Retrying lead-outs");
@@ -492,7 +492,7 @@ partial class Dump
}
else
{
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf.ToArray());
// TODO: Reset device after X errors
if(_stopOnError) return; // TODO: Return more cleanly

View File

@@ -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 ReadOnlySpan<byte> senseBuf, uint firstSectorToRead,
uint blockSize, uint blocksToRead, PlextorSubchannel supportedPlextorSubchannel,
out double cmdDuration)
{
bool sense;
@@ -140,7 +140,7 @@ partial class Dump
cmdBuf = new byte[2352 * blocksToRead + subSize * blocksToRead];
for(var b = 0; b < blocksToRead; b++)
for(int 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);

View File

@@ -84,13 +84,13 @@ partial class Dump
Dictionary<byte, string> isrcs, ref string mcn, HashSet<int> subchannelExtents,
Dictionary<byte, int> smallestPregapLbaPerTrack)
{
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
PlextorSubchannel supportedPlextorSubchannel;
byte[] senseBuf = null;
var outputOptical = _outputPlugin as IWritableOpticalImage;
bool sense = true; // Sense indicator
byte[] cmdBuf = null; // Data buffer
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
PlextorSubchannel supportedPlextorSubchannel;
ReadOnlySpan<byte> senseBuf = null;
var outputOptical = _outputPlugin as IWritableOpticalImage;
supportedPlextorSubchannel = supportedSubchannel switch
{
@@ -387,7 +387,7 @@ partial class Dump
if(sense || _dev.Error)
{
_errorLog?.WriteLine(badSectorToRead, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(badSectorToRead, _dev.Error, _dev.LastError, senseBuf.ToArray());
continue;
}

View File

@@ -192,12 +192,12 @@ public partial class Dump
? MediaType.MegaDriveCartridge
: MediaType.SNESGamePak;
uint blocksToRead = 64;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
byte[] senseBuf;
uint blocksToRead = 64;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
ReadOnlySpan<byte> senseBuf;
if(_outputPlugin is not IByteAddressableImage outputBai || !outputBai.SupportedMediaTypes.Contains(mediaType))
{
@@ -232,7 +232,8 @@ public partial class Dump
uint romSectors = romSize / 512;
uint romRemaining = romSize % 512;
UpdateStatus?.Invoke(string.Format(Localization.Core.Cartridge_has_0_bytes_1, romSize,
UpdateStatus?.Invoke(string.Format(Localization.Core.Cartridge_has_0_bytes_1,
romSize,
ByteSize.FromBytes(romSize).ToString("0.000")));
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_identified_as_0, mediaType));

View File

@@ -573,7 +573,7 @@ partial class Dump
md6 = Modes.EncodeMode6(md, _dev.ScsiType);
UpdateStatus?.Invoke(Localization.Core.Sending_MODE_SELECT_to_drive_return_damaged_blocks);
sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _);
sense = _dev.ModeSelect(md6, out ReadOnlySpan<byte> senseBuf, true, false, _dev.Timeout, out _);
if(sense)
{

View File

@@ -59,16 +59,16 @@ public partial class Dump
[SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")]
void DumpMs()
{
const ushort sbcProfile = 0x0001;
const uint blockSize = 512;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
uint blocksToRead = 64;
MediaType dskType;
bool sense;
byte[] senseBuf;
const ushort sbcProfile = 0x0001;
const uint blockSize = 512;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
uint blocksToRead = 64;
MediaType dskType;
bool sense;
ReadOnlySpan<byte> senseBuf;
if(_outputPlugin is not IWritableImage outputFormat)
{

View File

@@ -58,14 +58,14 @@ public partial class Dump
[SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")]
void DumpUmd()
{
const uint blockSize = 2048;
const MediaType dskType = MediaType.UMD;
uint blocksToRead = 16;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
byte[] senseBuf;
const uint blockSize = 2048;
const MediaType dskType = MediaType.UMD;
uint blocksToRead = 16;
double totalDuration = 0;
double currentSpeed = 0;
double maxSpeed = double.MinValue;
double minSpeed = double.MaxValue;
ReadOnlySpan<byte> senseBuf;
if(_outputPlugin is not IWritableOpticalImage outputOptical)
{

View File

@@ -52,7 +52,7 @@ public partial class Dump
{
InitProgress?.Invoke();
deviceGotReset:
bool sense = _dev.ScsiTestUnitReady(out byte[] senseBuf, _dev.Timeout, out _);
bool sense = _dev.ScsiTestUnitReady(out ReadOnlySpan<byte> senseBuf, _dev.Timeout, out _);
if(sense)
{
@@ -198,7 +198,7 @@ public partial class Dump
}
default:
StoppingErrorMessage?.Invoke(string.Format(Localization.Core.Error_testing_unit_was_ready_0,
Sense.PrettifySense(senseBuf)));
Sense.PrettifySense(senseBuf.ToArray())));
return;
}

View File

@@ -74,8 +74,8 @@ partial class Dump
double minSpeed = double.MaxValue;
var outputTape = _outputPlugin as IWritableTapeImage;
_dev.RequestSense(out byte[] senseBuf, _dev.Timeout, out double duration);
decSense = Sense.Decode(senseBuf);
_dev.RequestSense(out byte[] buffer, _dev.Timeout, out double duration);
decSense = Sense.Decode(buffer);
InitProgress?.Invoke();
@@ -93,6 +93,8 @@ partial class Dump
return;
}
ReadOnlySpan<byte> senseBuf;
// Not in BOM/P
if(decSense is { ASC: 0x00 } &&
decSense?.ASCQ != 0x00 &&
@@ -109,12 +111,12 @@ partial class Dump
do
{
PulseProgress?.Invoke(Localization.Core.Rewinding_please_wait);
_dev.RequestSense(out senseBuf, _dev.Timeout, out duration);
decSense = Sense.Decode(senseBuf);
_dev.RequestSense(out buffer, _dev.Timeout, out duration);
decSense = Sense.Decode(buffer);
} while(decSense is { ASC: 0x00, ASCQ: 0x1A or not (0x04 and 0x00) });
_dev.RequestSense(out senseBuf, _dev.Timeout, out duration);
decSense = Sense.Decode(senseBuf);
_dev.RequestSense(out buffer, _dev.Timeout, out duration);
decSense = Sense.Decode(buffer);
// And yet, did not rewind!
if(decSense.HasValue &&
@@ -194,8 +196,8 @@ partial class Dump
{
Thread.Sleep(1000);
PulseProgress?.Invoke(Localization.Core.Rewinding_please_wait);
_dev.RequestSense(out senseBuf, _dev.Timeout, out duration);
decSense = Sense.Decode(senseBuf);
_dev.RequestSense(out buffer, _dev.Timeout, out duration);
decSense = Sense.Decode(buffer);
} while(decSense is { ASC: 0x00, ASCQ: 0x1A or 0x19 });
// And yet, did not rewind!
@@ -283,8 +285,9 @@ partial class Dump
Modes.DecodedMode? decMode = null;
if(!sense && !_dev.Error)
if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue)
decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType);
{
if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType);
}
UpdateStatus?.Invoke(Localization.Core.Requesting_MODE_SENSE_6);
@@ -312,8 +315,9 @@ partial class Dump
if(sense || _dev.Error) sense = _dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration);
if(!sense && !_dev.Error)
if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue)
decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType);
{
if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType);
}
// TODO: Check partitions page
if(decMode.HasValue)
@@ -786,8 +790,8 @@ partial class Dump
{
Thread.Sleep(1000);
PulseProgress?.Invoke(Localization.Core.Rewinding_please_wait);
_dev.RequestSense(out senseBuf, _dev.Timeout, out duration);
decSense = Sense.Decode(senseBuf);
_dev.RequestSense(out buffer, _dev.Timeout, out duration);
decSense = Sense.Decode(buffer);
} while(decSense is { ASC: 0x00, ASCQ: 0x1A or 0x19 });
// And yet, did not rewind!
@@ -944,7 +948,7 @@ partial class Dump
_speedStopwatch.Stop();
totalDuration += duration;
if(sense && senseBuf?.Length != 0 && !ArrayHelpers.ArrayIsNullOrEmpty(senseBuf))
if(sense && !senseBuf.IsEmpty)
{
decSense = Sense.Decode(senseBuf);
@@ -1064,7 +1068,7 @@ partial class Dump
.Drive_could_not_read_block_0_Sense_bytes_follow,
currentBlock));
AaruLogging.Information(PrintHex.ByteArrayToHexArrayString(senseBuf, 32));
AaruLogging.Information(PrintHex.ByteArrayToHexArrayString(senseBuf.ToArray(), 32));
}
else
{

View File

@@ -25,6 +25,7 @@
// Copyright © 2020-2025 Rebecca Wallander
// ****************************************************************************/
using System;
using System.Linq;
using Aaru.CommonTypes.AaruMetadata;
using Aaru.CommonTypes.Enums;
@@ -223,7 +224,7 @@ partial class Dump
}
UpdateStatus?.Invoke(Localization.Core.Sending_MODE_SELECT_to_drive_return_damaged_blocks);
sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _);
sense = _dev.ModeSelect(md6, out ReadOnlySpan<byte> senseBuf, true, false, _dev.Timeout, out _);
if(sense) sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _);

View File

@@ -9,7 +9,6 @@ using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Interfaces;
using Aaru.Core.Logging;
using Aaru.Decoders.SCSI;
using Aaru.Helpers;
using Aaru.Logging;
using Humanizer;
using Humanizer.Bytes;
@@ -62,7 +61,7 @@ partial class Dump
{
blankExtents = new ExtentsULong();
written = _dev.MediumScan(out buffer,
written = _dev.MediumScan(out ReadOnlySpan<byte> senseBuffer,
true,
false,
false,
@@ -76,7 +75,7 @@ partial class Dump
uint.MaxValue,
out _);
DecodedSense? decodedSense = Sense.Decode(buffer);
DecodedSense? decodedSense = Sense.Decode(senseBuffer.ToArray());
if(_dev.LastError != 0 || decodedSense?.SenseKey == SenseKeys.IllegalRequest)
{
@@ -87,8 +86,7 @@ partial class Dump
}
// TODO: Find a place where MEDIUM SCAN works properly
else if(buffer?.Length > 0 && !ArrayHelpers.ArrayIsNullOrEmpty(buffer))
AaruLogging.WriteLine(Localization.Core.MEDIUM_SCAN_github_plead_message);
else if(senseBuffer.IsEmpty) AaruLogging.WriteLine(Localization.Core.MEDIUM_SCAN_github_plead_message);
changingCounter = false;
changingWritten = false;
@@ -181,8 +179,9 @@ partial class Dump
writtenExtents.Add(0, blocks - 1);
foreach(Tuple<ulong, ulong> blank in blankExtents.ToArray())
for(ulong b = blank.Item1; b <= blank.Item2; b++)
writtenExtents.Remove(b);
{
for(ulong b = blank.Item1; b <= blank.Item2; b++) writtenExtents.Remove(b);
}
}
if(writtenExtents.Count == 0)

View File

@@ -103,7 +103,7 @@ partial class Dump
if(mediaTags.ContainsKey(MediaTagType.DVD_DMI)) mediaTags.Remove(MediaTagType.DVD_DMI);
// Drive shall move to lock state when a new disc is inserted. Old kreon versions do not lock correctly so save this
sense = _dev.ReadCapacity(out byte[] coldReadCapacity, out byte[] senseBuf, _dev.Timeout, out _);
sense = _dev.ReadCapacity(out byte[] coldReadCapacity, out ReadOnlySpan<byte> senseBuf, _dev.Timeout, out _);
if(sense)
{
@@ -894,7 +894,7 @@ partial class Dump
}
else
{
_errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf);
_errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
// TODO: Reset device after X errors
if(_stopOnError) return; // TODO: Return more cleanly
@@ -1050,8 +1050,9 @@ partial class Dump
List<ulong> tmpList = [];
foreach(ulong ur in _resume.BadBlocks)
for(ulong i = ur; i < ur + blocksToRead; i++)
tmpList.Add(i);
{
for(ulong i = ur; i < ur + blocksToRead; i++) tmpList.Add(i);
}
tmpList.Sort();
@@ -1223,7 +1224,8 @@ partial class Dump
totalDuration += cmdDuration;
if(sense || _dev.Error) _errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf);
if(sense || _dev.Error)
_errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf.ToArray());
if(!sense && !_dev.Error)
{