mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 18:16:24 +00:00
Enable other drives that can read raw
This commit is contained in:
@@ -582,6 +582,7 @@ sealed partial class Reader
|
||||
|
||||
break;
|
||||
case "LITE-ON":
|
||||
case "TSSTcorp":
|
||||
LiteOnReadRaw =
|
||||
!_dev.LiteOnReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _, layerbreak, otp);
|
||||
|
||||
|
||||
@@ -590,10 +590,10 @@ public sealed partial class DeviceReport
|
||||
/// <param name="tryNec">Try NEC vendor commands</param>
|
||||
/// <param name="tryHldtst">Try HL-DT-ST vendor commands</param>
|
||||
/// <param name="tryMediaTekF106">Try MediaTek vendor commands</param>
|
||||
/// <param name="tryLiteOn">Try Lite-On vendor commands</param>
|
||||
/// <param name="tryReadBuffer3C">Try ReadBuffer 3C vendor commands</param>
|
||||
/// <returns></returns>
|
||||
public TestedMedia ReportMmcMedia(string mediaType, bool tryPlextor, bool tryPioneer, bool tryNec, bool tryHldtst,
|
||||
bool tryMediaTekF106, bool tryLiteOn)
|
||||
bool tryMediaTekF106, bool tryReadBuffer3C)
|
||||
{
|
||||
var sense = true;
|
||||
byte[] buffer = [];
|
||||
@@ -2754,11 +2754,11 @@ public sealed partial class DeviceReport
|
||||
if(mediaTest.SupportsHLDTSTReadRawDVD == true) mediaTest.HLDTSTReadRawDVDData = buffer;
|
||||
}
|
||||
|
||||
if(tryLiteOn)
|
||||
if(tryReadBuffer3C)
|
||||
{
|
||||
Spectre.ProgressSingleSpinner(ctx =>
|
||||
{
|
||||
ctx.AddTask(Localization.Core.Trying_Lite_On_trick_to_raw_read_DVDs).IsIndeterminate();
|
||||
ctx.AddTask(Localization.Core.Trying_ReadBuffer_3C_trick_to_raw_read_DVDs).IsIndeterminate();
|
||||
|
||||
mediaTest.SupportsLiteOnReadRawDVD =
|
||||
!_dev.LiteOnReadRawDvd(out buffer, out _, 16, 1, _dev.Timeout, out _, 0xffff, false);
|
||||
@@ -2775,14 +2775,14 @@ public sealed partial class DeviceReport
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: try multiple ReadBuffer variants
|
||||
// Fallback: try multiple ReadBuffer 3C variants
|
||||
mediaTest.LiteOnReadBufferData = new List<CompressedBufferRead>();
|
||||
|
||||
// Try variant 3c 00 00
|
||||
// First fill buffer with Read12
|
||||
Spectre.ProgressSingleSpinner(ctx =>
|
||||
{
|
||||
ctx.AddTask("Trying Lite-On ReadBuffer variant 3c 00 00").IsIndeterminate();
|
||||
ctx.AddTask("Trying ReadBuffer 3C variant 3c 00 00").IsIndeterminate();
|
||||
|
||||
_dev.Read12(out _, out _, 0, false, false, false, false, 16, 2048, 0, 16, false,
|
||||
_dev.Timeout, out _);
|
||||
@@ -2804,7 +2804,7 @@ public sealed partial class DeviceReport
|
||||
// Try variant 3c 01 00
|
||||
Spectre.ProgressSingleSpinner(ctx =>
|
||||
{
|
||||
ctx.AddTask("Trying Lite-On ReadBuffer variant 3c 01 00").IsIndeterminate();
|
||||
ctx.AddTask("Trying ReadBuffer 3C variant 3c 01 00").IsIndeterminate();
|
||||
|
||||
bool success = !_dev.ScsiReadBuffer(out buffer, out _, 0, 61440,
|
||||
_dev.Timeout, out _, 0x01, 0x00);
|
||||
@@ -2823,7 +2823,7 @@ public sealed partial class DeviceReport
|
||||
// Try variant 3c 01 01
|
||||
Spectre.ProgressSingleSpinner(ctx =>
|
||||
{
|
||||
ctx.AddTask("Trying Lite-On ReadBuffer variant 3c 01 01").IsIndeterminate();
|
||||
ctx.AddTask("Trying ReadBuffer 3C variant 3c 01 01").IsIndeterminate();
|
||||
|
||||
bool success = !_dev.ScsiReadBuffer(out buffer, out _, 0, 61440,
|
||||
_dev.Timeout, out _, 0x01, 0x01);
|
||||
@@ -2842,7 +2842,7 @@ public sealed partial class DeviceReport
|
||||
// Try variant 3c 01 02
|
||||
Spectre.ProgressSingleSpinner(ctx =>
|
||||
{
|
||||
ctx.AddTask("Trying Lite-On ReadBuffer variant 3c 01 02").IsIndeterminate();
|
||||
ctx.AddTask("Trying ReadBuffer 3C variant 3c 01 02").IsIndeterminate();
|
||||
|
||||
bool success = !_dev.ScsiReadBuffer(out buffer, out _, 0, 61440,
|
||||
_dev.Timeout, out _, 0x01, 0x02);
|
||||
@@ -2861,7 +2861,7 @@ public sealed partial class DeviceReport
|
||||
// Try variant 3c 02 00
|
||||
Spectre.ProgressSingleSpinner(ctx =>
|
||||
{
|
||||
ctx.AddTask("Trying Lite-On ReadBuffer variant 3c 02 00").IsIndeterminate();
|
||||
ctx.AddTask("Trying ReadBuffer 3C variant 3c 02 00").IsIndeterminate();
|
||||
|
||||
bool success = !_dev.ScsiReadBuffer(out buffer, out _, 0, 61440,
|
||||
_dev.Timeout, out _, 0x02, 0x00);
|
||||
|
||||
@@ -48,7 +48,6 @@ public partial class Device
|
||||
}
|
||||
|
||||
private uint _bufferOffset;
|
||||
private uint _bufferStride;
|
||||
private uint _bufferCapacityInSectors;
|
||||
private LiteOnBufferFormat _bufferFormat;
|
||||
|
||||
@@ -56,37 +55,57 @@ public partial class Device
|
||||
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
|
||||
/// <param name="buffer">Buffer where the ReadBuffer (RAW) response will be stored</param>
|
||||
/// <param name="senseBuffer">Sense buffer.</param>
|
||||
/// <param name="timeout">Timeout in seconds.</param>
|
||||
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
|
||||
/// <param name="lba">Start block address.</param>
|
||||
/// <param name="transferLength">How many blocks to read.</param>
|
||||
/// <param name="timeout">Timeout in seconds.</param>
|
||||
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
|
||||
/// <param name="layerbreak">The address in which the layerbreak occur</param>
|
||||
/// <param name="otp">Set to <c>true</c> if disk is Opposite Track Path (OTP)</param>
|
||||
public bool LiteOnReadRawDvd(out byte[] buffer, out ReadOnlySpan<byte> senseBuffer, uint lba, uint transferLength,
|
||||
uint timeout, out double duration, uint layerbreak, bool otp)
|
||||
{
|
||||
// Detect stride and format on first call
|
||||
if(_bufferStride == 0)
|
||||
// Detect ReadBuffer 3C variant and stride on first call
|
||||
if(!_readBuffer3CDetected)
|
||||
{
|
||||
uint detectedStride = DetectBufferStride(lba, timeout, out double detectDuration);
|
||||
|
||||
if(detectedStride == 0 || detectedStride < 2064 || detectedStride > 10000)
|
||||
bool detected = DetectReadBuffer3C(lba, timeout, out double detectDuration);
|
||||
|
||||
if(!detected || _detectedBufferStride == 0 || _detectedBufferStride < 2064 || _detectedBufferStride > 10000)
|
||||
{
|
||||
// Detection failed, use default
|
||||
_bufferStride = 2384;
|
||||
_bufferFormat = LiteOnBufferFormat.FullEccInterleaved;
|
||||
// Detection failed - raw reading is not supported on this drive
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer 3C detection failed - raw reading is not supported on this drive");
|
||||
|
||||
buffer = Array.Empty<byte>();
|
||||
senseBuffer = SenseBuffer;
|
||||
duration = detectDuration;
|
||||
Error = true;
|
||||
_readBuffer3CDetected = true;
|
||||
|
||||
return true; // Return failure - raw reading not supported
|
||||
}
|
||||
else
|
||||
|
||||
// Detect format based on stride (Lite-On specific)
|
||||
_bufferFormat = _detectedBufferStride switch
|
||||
{
|
||||
_bufferStride = detectedStride;
|
||||
}
|
||||
|
||||
// Calculate buffer capacity in sectors
|
||||
// Buffer size is approximately 1,700,576 bytes
|
||||
2064 => LiteOnBufferFormat.SectorDataOnly,
|
||||
2236 => LiteOnBufferFormat.PoOnly,
|
||||
2384 => LiteOnBufferFormat.FullEccInterleaved,
|
||||
> 2384 => LiteOnBufferFormat.FullEccWithPadding,
|
||||
_ => LiteOnBufferFormat.FullEccInterleaved // Default for backward compatibility
|
||||
};
|
||||
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"LiteOn buffer format detected based on stride: {0}, format: {1}",
|
||||
_detectedBufferStride, _bufferFormat);
|
||||
|
||||
// TODO: Calculate buffer capacity in sectors
|
||||
// Buffer size is approximately 1,700,576 bytes but need to test on other drives to get the correct value
|
||||
// It might also be the case that the buffer overflow works differently on different drives, so we need to test that as well.
|
||||
// const uint BUFFER_SIZE = 1700576;
|
||||
// _bufferCapacityInSectors = BUFFER_SIZE / _bufferStride;
|
||||
// _bufferCapacityInSectors = BUFFER_SIZE / _detectedBufferStride;
|
||||
// if(_bufferCapacityInSectors == 0) _bufferCapacityInSectors = 714; // Fallback to known value
|
||||
_bufferCapacityInSectors = 714;
|
||||
_readBuffer3CDetected = true;
|
||||
}
|
||||
|
||||
_bufferOffset %= _bufferCapacityInSectors;
|
||||
@@ -150,9 +169,9 @@ public partial class Device
|
||||
// because the data can be wrong anyway, so we check the buffer data later instead.
|
||||
Read12(out _, out _, 0, false, false, false, false, lba, 2048, 0, 16, false, timeout, out duration);
|
||||
|
||||
// Use generic ReadBuffer method with Lite-On specific mode (0x01) and buffer ID (0x01)
|
||||
return ScsiReadBuffer(out buffer, out senseBuffer, bufferOffset, transferLength, timeout, out duration, 0x01,
|
||||
0x01);
|
||||
// Use generic ReadBuffer method with detected variant
|
||||
return ScsiReadBuffer(out buffer, out senseBuffer, bufferOffset, transferLength, timeout, out duration,
|
||||
_detectedReadBufferMode, _detectedReadBufferId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -173,13 +192,13 @@ public partial class Device
|
||||
{
|
||||
bool sense = LiteOnReadBuffer(out buffer,
|
||||
out senseBuffer,
|
||||
_bufferOffset * _bufferStride,
|
||||
transferLength * _bufferStride,
|
||||
_bufferOffset * _detectedBufferStride,
|
||||
transferLength * _detectedBufferStride,
|
||||
timeout,
|
||||
out duration,
|
||||
lba);
|
||||
|
||||
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
|
||||
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _detectedBufferStride, _bufferFormat);
|
||||
|
||||
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, true))
|
||||
{
|
||||
@@ -192,13 +211,13 @@ public partial class Device
|
||||
|
||||
sense = LiteOnReadBuffer(out buffer,
|
||||
out senseBuffer,
|
||||
_bufferOffset * _bufferStride,
|
||||
transferLength * _bufferStride,
|
||||
_bufferOffset * _detectedBufferStride,
|
||||
transferLength * _detectedBufferStride,
|
||||
timeout,
|
||||
out duration,
|
||||
lba);
|
||||
|
||||
deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
|
||||
deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _detectedBufferStride, _bufferFormat);
|
||||
|
||||
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, otp)) return true;
|
||||
}
|
||||
@@ -230,12 +249,12 @@ public partial class Device
|
||||
uint layerbreak, bool otp)
|
||||
{
|
||||
uint newTransferLength1 = _bufferCapacityInSectors - _bufferOffset;
|
||||
uint newTransferLength2 = transferLength - newTransferLength1;
|
||||
uint newTransferLength2 = transferLength - newTransferLength1;
|
||||
|
||||
bool sense1 = LiteOnReadBuffer(out byte[] buffer1,
|
||||
out _,
|
||||
_bufferOffset * _bufferStride,
|
||||
newTransferLength1 * _bufferStride,
|
||||
_bufferOffset * _detectedBufferStride,
|
||||
newTransferLength1 * _detectedBufferStride,
|
||||
timeout,
|
||||
out double duration1,
|
||||
lba);
|
||||
@@ -243,20 +262,20 @@ public partial class Device
|
||||
bool sense2 = LiteOnReadBuffer(out byte[] buffer2,
|
||||
out _,
|
||||
0,
|
||||
newTransferLength2 * _bufferStride,
|
||||
newTransferLength2 * _detectedBufferStride,
|
||||
timeout,
|
||||
out double duration2,
|
||||
lba);
|
||||
|
||||
senseBuffer = SenseBuffer; // TODO
|
||||
|
||||
buffer = new byte[_bufferStride * transferLength];
|
||||
buffer = new byte[_detectedBufferStride * transferLength];
|
||||
Array.Copy(buffer1, buffer, buffer1.Length);
|
||||
Array.Copy(buffer2, 0, buffer, buffer1.Length, buffer2.Length);
|
||||
|
||||
duration = duration1 + duration2;
|
||||
|
||||
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
|
||||
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _detectedBufferStride, _bufferFormat);
|
||||
|
||||
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, otp)) return true;
|
||||
|
||||
@@ -282,9 +301,10 @@ public partial class Device
|
||||
{
|
||||
for(uint i = 0; i < _bufferCapacityInSectors; i++)
|
||||
{
|
||||
LiteOnReadBuffer(out byte[] buffer, out _, i * _bufferStride, _bufferStride, timeout, out double _, lba);
|
||||
LiteOnReadBuffer(out byte[] buffer, out _, i * _detectedBufferStride, _detectedBufferStride, timeout,
|
||||
out double _, lba);
|
||||
|
||||
byte[] deinterleaved = DeinterleaveEccBlock(buffer, 1, _bufferStride, _bufferFormat);
|
||||
byte[] deinterleaved = DeinterleaveEccBlock(buffer, 1, _detectedBufferStride, _bufferFormat);
|
||||
|
||||
if(CheckSectorNumber(deinterleaved, lba, 1, layerbreak, otp)) return (int)i;
|
||||
}
|
||||
@@ -292,101 +312,6 @@ public partial class Device
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects the stride (bytes per sector) in the Lite-On buffer by searching for
|
||||
/// the 00 03 00 pattern that appears at the start of each sector
|
||||
/// </summary>
|
||||
/// <param name="lba">LBA to use for filling the buffer (sectors 0-16)</param>
|
||||
/// <param name="timeout">Timeout in seconds</param>
|
||||
/// <param name="duration">Duration in milliseconds</param>
|
||||
/// <returns>Detected stride in bytes, or 0 if detection failed</returns>
|
||||
private uint DetectBufferStride(uint lba, uint timeout, out double duration)
|
||||
{
|
||||
// Fill buffer with sectors 0-16
|
||||
Read12(out _, out _, 0, false, false, false, false, lba, 2048, 0, 16, false, timeout, out duration);
|
||||
|
||||
// Read a large buffer chunk (enough for 16+ sectors)
|
||||
uint readSize = 16 * 3000; // Enough for 16 sectors even with large stride
|
||||
bool sense = ScsiReadBuffer(out byte[] buffer, out _, 0, readSize, timeout, out double readDuration, 0x01, 0x01);
|
||||
duration += readDuration;
|
||||
|
||||
if(sense || buffer == null || buffer.Length < 2236 * 3) // Need at least 3 sectors worth
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "LiteOn buffer stride detection failed, sense or buffer too small, using default");
|
||||
_bufferFormat = LiteOnBufferFormat.FullEccInterleaved;
|
||||
return 2384; // Default to known value
|
||||
}
|
||||
|
||||
// Search for pattern 00 03 00 starting from beginning
|
||||
// Find first occurrence
|
||||
int firstOffset = -1;
|
||||
for(int i = 0; i < buffer.Length - 3; i++)
|
||||
{
|
||||
if(buffer[i] == 0x00 && buffer[i + 1] == 0x03 && buffer[i + 2] == 0x00)
|
||||
{
|
||||
firstOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(firstOffset != 0)
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "LiteOn buffer stride detection failed, pattern not at start, using default");
|
||||
_bufferFormat = LiteOnBufferFormat.FullEccInterleaved;
|
||||
return 2384; // Pattern not at start, use default
|
||||
}
|
||||
|
||||
// Find second occurrence to calculate stride
|
||||
int secondOffset = -1;
|
||||
for(int i = firstOffset + 2064; i < Math.Min(firstOffset + 2500, buffer.Length - 3); i++)
|
||||
{
|
||||
if(buffer[i] == 0x00 && buffer[i + 1] == 0x03 && buffer[i + 2] == 0x00)
|
||||
{
|
||||
secondOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(secondOffset == -1)
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "LiteOn buffer stride detection failed, couldn't find second sector, using default");
|
||||
_bufferFormat = LiteOnBufferFormat.FullEccInterleaved;
|
||||
return 2384; // Couldn't find second sector, use default
|
||||
}
|
||||
|
||||
uint stride = (uint)(secondOffset - firstOffset);
|
||||
|
||||
// Verify stride by checking 3rd and 4th sectors
|
||||
for(int sectorNum = 2; sectorNum <= 3; sectorNum++)
|
||||
{
|
||||
int expectedOffset = (int)(firstOffset + stride * sectorNum);
|
||||
if(expectedOffset + 3 >= buffer.Length) break;
|
||||
|
||||
if(buffer[expectedOffset] != 0x00 ||
|
||||
buffer[expectedOffset + 1] != 0x03 ||
|
||||
buffer[expectedOffset + 2] != 0x00)
|
||||
{
|
||||
_bufferFormat = LiteOnBufferFormat.FullEccInterleaved;
|
||||
return 2384; // Verification failed, use default
|
||||
}
|
||||
}
|
||||
|
||||
// Detect format based on stride
|
||||
_bufferFormat = stride switch
|
||||
{
|
||||
2064 => LiteOnBufferFormat.SectorDataOnly,
|
||||
2236 => LiteOnBufferFormat.PoOnly,
|
||||
2384 => LiteOnBufferFormat.FullEccInterleaved,
|
||||
> 2384 => LiteOnBufferFormat.FullEccWithPadding,
|
||||
_ => LiteOnBufferFormat.FullEccInterleaved // Default for backward compatibility
|
||||
};
|
||||
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "LiteOn buffer stride detection succeeded, stride: {0}, format: {1}", stride,
|
||||
_bufferFormat);
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deinterleave the ECC block based on detected format
|
||||
/// </summary>
|
||||
|
||||
@@ -33,11 +33,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Aaru.Helpers;
|
||||
using Aaru.Logging;
|
||||
|
||||
namespace Aaru.Devices;
|
||||
|
||||
public partial class Device
|
||||
{
|
||||
// ReadBuffer 3C detection fields
|
||||
private byte _detectedReadBufferMode; // Detected buffer mode (0x00, 0x01, 0x02)
|
||||
private byte _detectedReadBufferId; // Detected buffer ID (0x00, 0x01, 0x02)
|
||||
private uint _detectedBufferStride; // Detected stride in bytes per sector
|
||||
private bool _readBuffer3CDetected; // Flag to track if detection has been performed
|
||||
/// <summary>Reads from device buffer using SCSI READ BUFFER command with specified variant</summary>
|
||||
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
|
||||
/// <param name="buffer">Buffer where the ReadBuffer response will be stored</param>
|
||||
@@ -196,5 +202,212 @@ public partial class Device
|
||||
// Same as FullEccInterleaved, padding is ignored
|
||||
return DeinterleaveFullEccInterleaved(buffer, transferLength, stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects which ReadBuffer 3C command variant works for this drive
|
||||
/// Tries variants in order: 3c0000, 3c0100, 3c0101, 3c0102, 3c0200
|
||||
/// </summary>
|
||||
/// <param name="lba">LBA to use for filling the buffer</param>
|
||||
/// <param name="timeout">Timeout in seconds</param>
|
||||
/// <param name="duration">Duration in milliseconds</param>
|
||||
/// <returns>Tuple with (mode, bufferId) if a working variant is found, null otherwise</returns>
|
||||
private (byte mode, byte bufferId)? DetectReadBufferVariant(uint lba, uint timeout, out double duration)
|
||||
{
|
||||
duration = 0;
|
||||
|
||||
// Variants to try in order: 3c0000, 3c0100, 3c0101, 3c0102, 3c0200
|
||||
(byte mode, byte bufferId)[] variants =
|
||||
[
|
||||
(0x00, 0x00), // 3c0000
|
||||
(0x01, 0x00), // 3c0100
|
||||
(0x01, 0x01), // 3c0101
|
||||
(0x01, 0x02), // 3c0102
|
||||
(0x02, 0x00) // 3c0200
|
||||
];
|
||||
|
||||
// Fill buffer with sectors 0-16
|
||||
Read12(out _, out _, 0, false, false, false, false, lba, 2048, 0, 16, false, timeout, out double readDuration);
|
||||
duration += readDuration;
|
||||
|
||||
foreach((byte mode, byte bufferId) variant in variants)
|
||||
{
|
||||
// Try to read buffer with this variant
|
||||
// Read enough for at least 3 sectors (minimum needed for stride detection)
|
||||
uint readSize = 3 * 3000; // Enough for 3 sectors even with large stride
|
||||
bool sense = ScsiReadBuffer(out byte[] buffer, out _, 0, readSize, timeout, out double readBufferDuration,
|
||||
variant.mode, variant.bufferId);
|
||||
duration += readBufferDuration;
|
||||
|
||||
// Check if command succeeded, returned valid data, and has correct sector header (00 03 00)
|
||||
if(!sense && buffer != null && buffer.Length >= 2236 * 3)
|
||||
{
|
||||
// Validate that the data starts with the expected DVD sector header pattern
|
||||
if(buffer.Length >= 3 &&
|
||||
buffer[0] == 0x00 &&
|
||||
buffer[1] == 0x03 &&
|
||||
buffer[2] == 0x00)
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "ReadBuffer 3C variant {0:x2}{1:x2} detected", variant.mode,
|
||||
variant.bufferId);
|
||||
|
||||
return (variant.mode, variant.bufferId);
|
||||
}
|
||||
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer 3C variant {0:x2}{1:x2} returned data but header pattern incorrect",
|
||||
variant.mode, variant.bufferId);
|
||||
}
|
||||
}
|
||||
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "No working ReadBuffer 3C variant found");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects the stride (bytes per sector) in the buffer by searching for
|
||||
/// the 00 03 00 pattern that appears at the start of each sector
|
||||
/// </summary>
|
||||
/// <param name="lba">LBA to use for filling the buffer (sectors 0-16)</param>
|
||||
/// <param name="timeout">Timeout in seconds</param>
|
||||
/// <param name="mode">Buffer mode to use</param>
|
||||
/// <param name="bufferId">Buffer ID to use</param>
|
||||
/// <param name="duration">Duration in milliseconds</param>
|
||||
/// <returns>Detected stride in bytes, or 0 if detection failed</returns>
|
||||
private uint DetectBufferStride(uint lba, uint timeout, byte mode, byte bufferId, out double duration)
|
||||
{
|
||||
// Fill buffer with sectors 0-16
|
||||
Read12(out _, out _, 0, false, false, false, false, lba, 2048, 0, 16, false, timeout, out duration);
|
||||
|
||||
// Read a large buffer chunk (enough for 16+ sectors)
|
||||
uint readSize = 16 * 3000; // Enough for 16 sectors even with large stride
|
||||
bool sense = ScsiReadBuffer(out byte[] buffer, out _, 0, readSize, timeout, out double readDuration, mode,
|
||||
bufferId);
|
||||
duration += readDuration;
|
||||
|
||||
if(sense || buffer == null || buffer.Length < 2236 * 3) // Need at least 3 sectors worth
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer stride detection failed, sense or buffer too small, using default");
|
||||
|
||||
return 0; // Detection failed
|
||||
}
|
||||
|
||||
// Search for pattern 00 03 00 starting from beginning
|
||||
// Find first occurrence
|
||||
int firstOffset = -1;
|
||||
for(int i = 0; i < buffer.Length - 3; i++)
|
||||
{
|
||||
if(buffer[i] == 0x00 && buffer[i + 1] == 0x03 && buffer[i + 2] == 0x00)
|
||||
{
|
||||
firstOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(firstOffset != 0)
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer stride detection failed, pattern not at start, using default");
|
||||
|
||||
return 0; // Pattern not at start
|
||||
}
|
||||
|
||||
// Find second occurrence to calculate stride
|
||||
int secondOffset = -1;
|
||||
for(int i = firstOffset + 2064; i < Math.Min(firstOffset + 2500, buffer.Length - 3); i++)
|
||||
{
|
||||
if(buffer[i] == 0x00 && buffer[i + 1] == 0x03 && buffer[i + 2] == 0x00)
|
||||
{
|
||||
secondOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(secondOffset == -1)
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer stride detection failed, couldn't find second sector, using default");
|
||||
|
||||
return 0; // Couldn't find second sector
|
||||
}
|
||||
|
||||
uint stride = (uint)(secondOffset - firstOffset);
|
||||
|
||||
// Verify stride by checking 3rd and 4th sectors
|
||||
for(int sectorNum = 2; sectorNum <= 3; sectorNum++)
|
||||
{
|
||||
int expectedOffset = (int)(firstOffset + stride * sectorNum);
|
||||
if(expectedOffset + 3 >= buffer.Length) break;
|
||||
|
||||
if(buffer[expectedOffset] != 0x00 ||
|
||||
buffer[expectedOffset + 1] != 0x03 ||
|
||||
buffer[expectedOffset + 2] != 0x00)
|
||||
{
|
||||
return 0; // Verification failed
|
||||
}
|
||||
}
|
||||
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME, "ReadBuffer stride detection succeeded, stride: {0}", stride);
|
||||
|
||||
return stride;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects ReadBuffer 3C support: finds working variant and stride
|
||||
/// </summary>
|
||||
/// <param name="lba">LBA to use for detection</param>
|
||||
/// <param name="timeout">Timeout in seconds</param>
|
||||
/// <param name="duration">Duration in milliseconds</param>
|
||||
/// <returns><c>true</c> if detection succeeded, <c>false</c> otherwise</returns>
|
||||
private bool DetectReadBuffer3C(uint lba, uint timeout, out double duration)
|
||||
{
|
||||
duration = 0;
|
||||
|
||||
// If already detected, return success
|
||||
if(_readBuffer3CDetected)
|
||||
{
|
||||
if(_detectedBufferStride == 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to detect variant
|
||||
(byte mode, byte bufferId)? variant = DetectReadBufferVariant(lba, timeout, out double variantDuration);
|
||||
duration += variantDuration;
|
||||
|
||||
if(!variant.HasValue)
|
||||
{
|
||||
_readBuffer3CDetected = true; // Mark as attempted even if failed
|
||||
return false;
|
||||
}
|
||||
|
||||
_detectedReadBufferMode = variant.Value.mode;
|
||||
_detectedReadBufferId = variant.Value.bufferId;
|
||||
|
||||
// Detect stride using the found variant
|
||||
uint stride = DetectBufferStride(lba, timeout, _detectedReadBufferMode, _detectedReadBufferId,
|
||||
out double strideDuration);
|
||||
duration += strideDuration;
|
||||
|
||||
if(stride == 0 || stride < 2064 || stride > 10000)
|
||||
{
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer 3C stride detection failed or invalid stride: {0}, using default", stride);
|
||||
|
||||
_detectedBufferStride = 2384; // Default to known value
|
||||
_readBuffer3CDetected = true;
|
||||
|
||||
return false; // Detection partially succeeded but stride failed
|
||||
}
|
||||
|
||||
_detectedBufferStride = stride;
|
||||
_readBuffer3CDetected = true;
|
||||
|
||||
AaruLogging.Debug(SCSI_MODULE_NAME,
|
||||
"ReadBuffer 3C detection succeeded, variant: {0:x2}{1:x2}, stride: {2}",
|
||||
_detectedReadBufferMode, _detectedReadBufferId, _detectedBufferStride);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
Aaru.Localization/Core.Designer.cs
generated
6
Aaru.Localization/Core.Designer.cs
generated
@@ -4340,6 +4340,12 @@ namespace Aaru.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
public static string Trying_ReadBuffer_3C_trick_to_raw_read_DVDs {
|
||||
get {
|
||||
return ResourceManager.GetString("Trying_ReadBuffer_3C_trick_to_raw_read_DVDs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string Trying_MediaTek_READ_DRAM_command {
|
||||
get {
|
||||
return ResourceManager.GetString("Trying_MediaTek_READ_DRAM_command", resourceCulture);
|
||||
|
||||
@@ -2190,6 +2190,9 @@ Do you want to proceed? (Y/N): </value>
|
||||
</data>
|
||||
<data name="Trying_Lite_On_trick_to_raw_read_DVDs" xml:space="preserve">
|
||||
<value>Trying Lite-On trick to raw read DVDs...</value>
|
||||
</data>
|
||||
<data name="Trying_ReadBuffer_3C_trick_to_raw_read_DVDs" xml:space="preserve">
|
||||
<value>Trying ReadBuffer 3C trick to raw read DVDs...</value>
|
||||
</data>
|
||||
<data name="Trying_MediaTek_READ_DRAM_command" xml:space="preserve">
|
||||
<value>Trying MediaTek READ DRAM command...</value>
|
||||
|
||||
6
Aaru.Localization/UI.Designer.cs
generated
6
Aaru.Localization/UI.Designer.cs
generated
@@ -579,6 +579,12 @@ namespace Aaru.Localization {
|
||||
}
|
||||
}
|
||||
|
||||
public static string Do_you_want_to_try_ReadBuffer3C_commands {
|
||||
get {
|
||||
return ResourceManager.GetString("Do_you_want_to_try_ReadBuffer3C_commands", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string This_is_dangerous {
|
||||
get {
|
||||
return ResourceManager.GetString("This_is_dangerous", resourceCulture);
|
||||
|
||||
@@ -291,6 +291,9 @@ In you are unsure, please press N to not continue.</value>
|
||||
</data>
|
||||
<data name="Do_you_want_to_try_LiteOn_commands" xml:space="preserve">
|
||||
<value>Do you want to try Lite-On vendor commands?</value>
|
||||
</data>
|
||||
<data name="Do_you_want_to_try_ReadBuffer3C_commands" xml:space="preserve">
|
||||
<value>Do you want to try ReadBuffer 3C commands?</value>
|
||||
</data>
|
||||
<data name="This_is_dangerous" xml:space="preserve">
|
||||
<value>THIS IS DANGEROUS AND CAN IRREVERSIBLY DESTROY YOUR DRIVE (IF IN DOUBT PRESS 'N')</value>
|
||||
|
||||
@@ -649,7 +649,7 @@ sealed class DeviceReportCommand : AsyncCommand<DeviceReportCommand.Settings>
|
||||
tryPioneer = false,
|
||||
tryNec = false,
|
||||
tryMediaTekF106 = false,
|
||||
tryLiteOn = false;
|
||||
tryReadBuffer3C = false;
|
||||
|
||||
tryPlextor |=
|
||||
dev.Manufacturer.Equals("plextor", StringComparison.InvariantCultureIgnoreCase);
|
||||
@@ -662,9 +662,6 @@ sealed class DeviceReportCommand : AsyncCommand<DeviceReportCommand.Settings>
|
||||
|
||||
tryNec |= dev.Manufacturer.Equals("nec", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
tryLiteOn |=
|
||||
dev.Manufacturer.Equals("lite-on", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
if(!iomegaRev)
|
||||
{
|
||||
if(!tryPlextor)
|
||||
@@ -699,13 +696,10 @@ sealed class DeviceReportCommand : AsyncCommand<DeviceReportCommand.Settings>
|
||||
false);
|
||||
}
|
||||
|
||||
if(!tryLiteOn)
|
||||
{
|
||||
tryLiteOn |=
|
||||
AnsiConsole
|
||||
.Confirm($"[italic]{UI.Do_you_want_to_try_LiteOn_commands} [red]{UI.This_is_dangerous}[/][/]",
|
||||
false);
|
||||
}
|
||||
tryReadBuffer3C =
|
||||
AnsiConsole
|
||||
.Confirm($"[italic]{UI.Do_you_want_to_try_ReadBuffer3C_commands} [red]{UI.This_is_dangerous}[/][/]",
|
||||
false);
|
||||
|
||||
tryMediaTekF106 =
|
||||
AnsiConsole
|
||||
@@ -809,7 +803,7 @@ sealed class DeviceReportCommand : AsyncCommand<DeviceReportCommand.Settings>
|
||||
tryNec,
|
||||
tryHldtst,
|
||||
tryMediaTekF106,
|
||||
tryLiteOn);
|
||||
tryReadBuffer3C);
|
||||
|
||||
if(mediaTest is null) continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user