Try to identify stride

This commit is contained in:
Rebecca Wallander
2026-01-10 14:20:42 +01:00
parent 8f1a6b3f00
commit 8429941faf

View File

@@ -38,7 +38,19 @@ namespace Aaru.Devices;
public partial class Device
{
private enum LiteOnBufferFormat
{
Unknown = 0,
FullEccInterleaved,
PoOnly,
SectorDataOnly,
FullEccWithPadding
}
private uint _bufferOffset;
private uint _bufferStride;
private uint _bufferCapacityInSectors;
private LiteOnBufferFormat _bufferFormat;
/// <summary>Reads a "raw" sector from DVD on Lite-On drives.</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
@@ -53,7 +65,31 @@ public partial class Device
public bool LiteOnReadRawDvd(out byte[] buffer, out ReadOnlySpan<byte> senseBuffer, uint lba, uint transferLength,
uint timeout, out double duration, uint layerbreak, bool otp)
{
_bufferOffset %= 714;
// Detect stride and format on first call
if(_bufferStride == 0)
{
uint detectedStride = DetectBufferStride(lba, timeout, out double detectDuration);
if(detectedStride == 0 || detectedStride < 2064 || detectedStride > 10000)
{
// Detection failed, use default
_bufferStride = 2384;
_bufferFormat = LiteOnBufferFormat.FullEccInterleaved;
}
else
{
_bufferStride = detectedStride;
}
// Calculate buffer capacity in sectors
// Buffer size is approximately 1,700,576 bytes
// const uint BUFFER_SIZE = 1700576;
// _bufferCapacityInSectors = BUFFER_SIZE / _bufferStride;
// if(_bufferCapacityInSectors == 0) _bufferCapacityInSectors = 714; // Fallback to known value
_bufferCapacityInSectors = 714;
}
_bufferOffset %= _bufferCapacityInSectors;
bool sense;
@@ -66,7 +102,7 @@ public partial class Device
return true;
}
if(714 - _bufferOffset < transferLength)
if(_bufferCapacityInSectors - _bufferOffset < transferLength)
{
sense = LiteOnReadSectorsAcrossBufferBorder(out buffer,
out senseBuffer,
@@ -137,13 +173,13 @@ public partial class Device
{
bool sense = LiteOnReadBuffer(out buffer,
out senseBuffer,
_bufferOffset * 2384,
transferLength * 2384,
_bufferOffset * _bufferStride,
transferLength * _bufferStride,
timeout,
out duration,
lba);
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength);
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, true))
{
@@ -156,13 +192,13 @@ public partial class Device
sense = LiteOnReadBuffer(out buffer,
out senseBuffer,
_bufferOffset * 2384,
transferLength * 2384,
_bufferOffset * _bufferStride,
transferLength * _bufferStride,
timeout,
out duration,
lba);
deinterleaved = DeinterleaveEccBlock(buffer, transferLength);
deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, otp)) return true;
}
@@ -193,34 +229,34 @@ public partial class Device
uint transferLength, uint timeout, out double duration,
uint layerbreak, bool otp)
{
uint newTransferLength1 = 714 - _bufferOffset;
uint newTransferLength2 = transferLength - newTransferLength1;
uint newTransferLength1 = _bufferCapacityInSectors - _bufferOffset;
uint newTransferLength2 = transferLength - newTransferLength1;
bool sense1 = LiteOnReadBuffer(out byte[] buffer1,
out _,
_bufferOffset * 2384,
newTransferLength1 * 2384,
timeout,
out double duration1,
lba);
out _,
_bufferOffset * _bufferStride,
newTransferLength1 * _bufferStride,
timeout,
out double duration1,
lba);
bool sense2 = LiteOnReadBuffer(out byte[] buffer2,
out _,
0,
newTransferLength2 * 2384,
timeout,
out double duration2,
lba);
out _,
0,
newTransferLength2 * _bufferStride,
timeout,
out double duration2,
lba);
senseBuffer = SenseBuffer; // TODO
buffer = new byte[2384 * transferLength];
buffer = new byte[_bufferStride * transferLength];
Array.Copy(buffer1, buffer, buffer1.Length);
Array.Copy(buffer2, 0, buffer, buffer1.Length, buffer2.Length);
duration = duration1 + duration2;
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength);
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, otp)) return true;
@@ -244,24 +280,152 @@ public partial class Device
/// <returns>The offset on the device memory, or -1 if not found</returns>
private int FindBufferOffset(uint lba, uint timeout, uint layerbreak, bool otp)
{
for(uint i = 0; i < 714; i++)
for(uint i = 0; i < _bufferCapacityInSectors; i++)
{
LiteOnReadBuffer(out byte[] buffer, out _, i * 2384, 2384, timeout, out double _, lba);
LiteOnReadBuffer(out byte[] buffer, out _, i * _bufferStride, _bufferStride, timeout, out double _, lba);
if(CheckSectorNumber(buffer, lba, 1, layerbreak, otp)) return (int)i;
byte[] deinterleaved = DeinterleaveEccBlock(buffer, 1, _bufferStride, _bufferFormat);
if(CheckSectorNumber(deinterleaved, lba, 1, layerbreak, otp)) return (int)i;
}
return -1;
}
/// <summary>
/// Deinterleave the ECC block stored within a 2384 byte raw sector
/// 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>
/// <param name="buffer">Data buffer</param>
/// <param name="transferLength">How many blocks in buffer</param>
/// <param name="deinterleaved"></param>
/// <param name="stride">Bytes per sector in buffer</param>
/// <param name="format">Buffer format type</param>
/// <returns>The deinterleaved sectors</returns>
private static byte[] DeinterleaveEccBlock(byte[] buffer, uint transferLength)
private byte[] DeinterleaveEccBlock(byte[] buffer, uint transferLength, uint stride, LiteOnBufferFormat format)
{
return format switch
{
LiteOnBufferFormat.FullEccInterleaved => DeinterleaveFullEccInterleaved(buffer, transferLength, stride),
LiteOnBufferFormat.PoOnly => DeinterleavePoOnly(buffer, transferLength, stride),
LiteOnBufferFormat.SectorDataOnly => buffer, // No deinterleaving needed for sector-data-only format
LiteOnBufferFormat.FullEccWithPadding => DeinterleaveFullEccWithPadding(buffer, transferLength, stride),
_ => DeinterleaveFullEccInterleaved(buffer, transferLength, stride) // Default fallback
};
}
/// <summary>
/// Deinterleave the ECC block stored within a raw sector (backward compatibility wrapper)
/// </summary>
/// <param name="buffer">Data buffer</param>
/// <param name="transferLength">How many blocks in buffer</param>
/// <returns>The deinterleaved sectors</returns>
private byte[] DeinterleaveEccBlock(byte[] buffer, uint transferLength)
{
return DeinterleaveEccBlock(buffer, transferLength, _bufferStride, _bufferFormat);
}
/// <summary>
/// Deinterleave full ECC block with interleaved PI (e.g., 2384 bytes)
/// </summary>
/// <param name="buffer">Data buffer</param>
/// <param name="transferLength">How many blocks in buffer</param>
/// <param name="stride">Bytes per sector in buffer</param>
/// <returns>The deinterleaved sectors</returns>
private static byte[] DeinterleaveFullEccInterleaved(byte[] buffer, uint transferLength, uint stride)
{
// TODO: Save ECC instead of just throwing it away
@@ -269,9 +433,42 @@ public partial class Device
for(var j = 0; j < transferLength; j++)
{
for(var i = 0; i < 12; i++) Array.Copy(buffer, j * 2384 + i * 182, deinterleaved, j * 2064 + i * 172, 172);
for(var i = 0; i < 12; i++) Array.Copy(buffer, j * stride + i * 182, deinterleaved, j * 2064 + i * 172, 172);
}
return deinterleaved;
}
/// <summary>
/// Extract sector data from PO-only format (e.g., 2236 bytes)
/// </summary>
/// <param name="buffer">Data buffer</param>
/// <param name="transferLength">How many blocks in buffer</param>
/// <param name="stride">Bytes per sector in buffer</param>
/// <returns>The extracted sector data</returns>
private static byte[] DeinterleavePoOnly(byte[] buffer, uint transferLength, uint stride)
{
var deinterleaved = new byte[2064 * transferLength];
for(var j = 0; j < transferLength; j++)
{
Array.Copy(buffer, j * stride, deinterleaved, j * 2064, 2064);
}
return deinterleaved;
}
/// <summary>
/// Deinterleave full ECC block with padding (e.g., 2816 bytes)
/// </summary>
/// <param name="buffer">Data buffer</param>
/// <param name="transferLength">How many blocks in buffer</param>
/// <param name="stride">Bytes per sector in buffer</param>
/// <returns>The deinterleaved sectors</returns>
private static byte[] DeinterleaveFullEccWithPadding(byte[] buffer, uint transferLength, uint stride)
{
// Same as FullEccInterleaved, padding is ignored
return DeinterleaveFullEccInterleaved(buffer, transferLength, stride);
}
}