Read Lite-On raw DVD buffer

This commit is contained in:
Rebecca Wallander
2025-03-15 21:53:45 +01:00
parent 56a81aaf10
commit 1ab023bbc6
17 changed files with 7629 additions and 11046 deletions

View File

@@ -779,6 +779,9 @@ public class TestedMedia
[DisplayName("Can read scrambled DVD sectors using HL-DT-ST cache trick")] [DisplayName("Can read scrambled DVD sectors using HL-DT-ST cache trick")]
public bool? SupportsHLDTSTReadRawDVD { get; set; } public bool? SupportsHLDTSTReadRawDVD { get; set; }
[DisplayName("Can read scrambled DVD sectors using Lite-On cache trick")]
public bool? SupportsLiteOnReadRawDVD { get; set; }
[DisplayName("Supports NEC READ CD-DA command")] [DisplayName("Supports NEC READ CD-DA command")]
public bool? SupportsNECReadCDDA { get; set; } public bool? SupportsNECReadCDDA { get; set; }
@@ -1155,6 +1158,9 @@ public class TestedMedia
[DisplayName("Data from HL-DT-ST's scrambled DVD reading trick")] [DisplayName("Data from HL-DT-ST's scrambled DVD reading trick")]
public byte[] HLDTSTReadRawDVDData { get; set; } public byte[] HLDTSTReadRawDVDData { get; set; }
[DisplayName("Data from Lite-On's scrambled DVD reading trick")]
public byte[] LiteOnReadRawDVDData { get; set; }
#endregion #endregion
} }

View File

@@ -175,7 +175,7 @@ partial class Dump
// Write empty data // Write empty data
_writeStopwatch.Restart(); _writeStopwatch.Restart();
outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); outputFormat.WriteSectorsLong(new byte[blockSize * _skip], i, _skip);
imageWriteDuration += _writeStopwatch.Elapsed.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);

View File

@@ -59,6 +59,7 @@ using DVDDecryption = Aaru.Decryption.DVD.Dump;
using Track = Aaru.CommonTypes.Structs.Track; using Track = Aaru.CommonTypes.Structs.Track;
using TrackType = Aaru.CommonTypes.Enums.TrackType; using TrackType = Aaru.CommonTypes.Enums.TrackType;
using Version = Aaru.CommonTypes.Interop.Version; using Version = Aaru.CommonTypes.Interop.Version;
using Aaru.Decoders.DVD;
// ReSharper disable JoinDeclarationAndInitializer // ReSharper disable JoinDeclarationAndInitializer
@@ -332,11 +333,19 @@ partial class Dump
else else
{ {
// Only a block will be read, but it contains 16 sectors and command expect sector number not block number // Only a block will be read, but it contains 16 sectors and command expect sector number not block number
blocksToRead = (uint)(longBlockSize == 37856 ? 16 : 1); blocksToRead = 16;
mediaTags.TryGetValue(MediaTagType.DVD_PFI, out byte[] pfi);
PFI.PhysicalFormatInformation? decodedPfi = PFI.Decode(pfi, dskType);
scsiReader.layerbreak = decodedPfi?.Layer0EndPSN ?? 0;
scsiReader.otp = decodedPfi is { Layers: 1, TrackPath: false };
if(scsiReader.HldtstReadRaw) blocksToRead = 1;
UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_0_raw_bytes_1_cooked_bytes_per_sector, UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_0_raw_bytes_1_cooked_bytes_per_sector,
longBlockSize, longBlockSize,
blockSize * blocksToRead)); blockSize));
physicalBlockSize = longBlockSize; physicalBlockSize = longBlockSize;
blockSize = longBlockSize; blockSize = longBlockSize;
@@ -835,7 +844,7 @@ partial class Dump
{ {
mediaTags.TryGetValue(MediaTagType.DVD_DiscKey_Decrypted, out byte[] discKey); mediaTags.TryGetValue(MediaTagType.DVD_DiscKey_Decrypted, out byte[] discKey);
if(scsiReader.HldtstReadRaw) if(scsiReader.HldtstReadRaw || scsiReader.LiteOnReadRaw)
{ {
ReadCacheData(blocks, ReadCacheData(blocks,
blocksToRead, blocksToRead,
@@ -913,6 +922,8 @@ partial class Dump
#region Trimming #region Trimming
mediaTags.TryGetValue(MediaTagType.DVD_DiscKey_Decrypted, out byte[] mediaTag);
if(_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim) if(_resume.BadBlocks.Count > 0 && !_aborted && _trim && newTrim)
{ {
_trimStopwatch.Restart(); _trimStopwatch.Restart();
@@ -921,7 +932,7 @@ partial class Dump
InitProgress?.Invoke(); InitProgress?.Invoke();
TrimSbcData(scsiReader, extents, currentTry, blankExtents); TrimSbcData(scsiReader, extents, currentTry, blankExtents, mediaTag ?? null);
EndProgress?.Invoke(); EndProgress?.Invoke();
_trimStopwatch.Stop(); _trimStopwatch.Stop();
@@ -940,14 +951,18 @@ partial class Dump
#region Error handling #region Error handling
if(_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0) if(_resume.BadBlocks.Count > 0 && !_aborted && _retryPasses > 0)
RetrySbcData(scsiReader, currentTry, extents, ref totalDuration, blankExtents); RetrySbcData(scsiReader, currentTry, extents, ref totalDuration, blankExtents, mediaTag ?? null);
if(_resume.MissingTitleKeys?.Count > 0 && if(_resume.MissingTitleKeys?.Count > 0 &&
!_aborted && !_aborted &&
_retryPasses > 0 && _retryPasses > 0 &&
Settings.Settings.Current.EnableDecryption && Settings.Settings.Current.EnableDecryption &&
_titleKeys && _titleKeys &&
mediaTags.TryGetValue(MediaTagType.DVD_DiscKey_Decrypted, out byte[] mediaTag))
// Unnecessary since keys are already in raw data
!scsiReader.LiteOnReadRaw &&
!scsiReader.HldtstReadRaw &&
mediaTag is not null)
RetryTitleKeys(dvdDecrypt, mediaTag, ref totalDuration); RetryTitleKeys(dvdDecrypt, mediaTag, ref totalDuration);
#endregion Error handling #endregion Error handling

View File

@@ -54,7 +54,7 @@ partial class Dump
/// <param name="scsiReader">SCSI reader</param> /// <param name="scsiReader">SCSI reader</param>
/// <param name="blankExtents">Blank extents</param> /// <param name="blankExtents">Blank extents</param>
void RetrySbcData(Reader scsiReader, DumpHardware currentTry, ExtentsULong extents, ref double totalDuration, void RetrySbcData(Reader scsiReader, DumpHardware currentTry, ExtentsULong extents, ref double totalDuration,
ExtentsULong blankExtents) ExtentsULong blankExtents, byte[] discKey)
{ {
var pass = 1; var pass = 1;
var forward = true; var forward = true;
@@ -312,7 +312,51 @@ partial class Dump
{ {
_resume.BadBlocks.Remove(badSector); _resume.BadBlocks.Remove(badSector);
extents.Add(badSector); extents.Add(badSector);
outputFormat.WriteSector(buffer, badSector);
if(scsiReader.LiteOnReadRaw || scsiReader.HldtstReadRaw)
{
byte[] cmi = new byte[1];
byte[] key = buffer.Skip(7).Take(5).ToArray();
if(key.All(static k => k == 0))
{
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], badSector, SectorTagType.DvdTitleKeyDecrypted);
_resume.MissingTitleKeys?.Remove(badSector);
}
else
{
CSS.DecryptTitleKey(discKey, key, out byte[] tmpBuf);
outputFormat.WriteSectorTag(tmpBuf, badSector, SectorTagType.DvdTitleKeyDecrypted);
_resume.MissingTitleKeys?.Remove(badSector);
cmi[0] = buffer[6];
}
if(!_storeEncrypted)
{
ErrorNumber errno =
outputFormat.ReadSectorsTag(badSector,
1,
SectorTagType.DvdTitleKeyDecrypted,
out byte[] titleKey);
if(errno != ErrorNumber.NoError)
{
ErrorMessage?.Invoke(string.Format(Localization.Core.Error_retrieving_title_key_for_sector_0,
badSector));
}
else
buffer = CSS.DecryptSectorLong(buffer, titleKey, cmi);
}
_resume.BadBlocks.Remove(badSector);
outputFormat.WriteSectorLong(buffer, badSector);
}
else
outputFormat.WriteSector(buffer, badSector);
_mediaGraph?.PaintSectorGood(badSector); _mediaGraph?.PaintSectorGood(badSector);
UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_block_0_in_pass_1, UpdateStatus?.Invoke(string.Format(Localization.Core.Correctly_retried_block_0_in_pass_1,

View File

@@ -28,9 +28,12 @@
// ReSharper disable InlineOutVariableDeclaration // ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope // ReSharper disable TooWideLocalVariableScope
using System.Linq;
using Aaru.CommonTypes.AaruMetadata; using Aaru.CommonTypes.AaruMetadata;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Extents; using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Interfaces;
using Aaru.Decryption.DVD;
namespace Aaru.Core.Devices.Dumping; namespace Aaru.Core.Devices.Dumping;
@@ -41,7 +44,7 @@ partial class Dump
/// <param name="extents">Correctly dump extents</param> /// <param name="extents">Correctly dump extents</param>
/// <param name="currentTry">Resume information</param> /// <param name="currentTry">Resume information</param>
/// <param name="blankExtents">Blank extents</param> /// <param name="blankExtents">Blank extents</param>
void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardware currentTry, ExtentsULong blankExtents) void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardware currentTry, ExtentsULong blankExtents, byte[] discKey)
{ {
ulong[] tmpArray = _resume.BadBlocks.ToArray(); ulong[] tmpArray = _resume.BadBlocks.ToArray();
bool sense; bool sense;
@@ -86,9 +89,61 @@ partial class Dump
if((sense || _dev.Error) && !recoveredError) continue; if((sense || _dev.Error) && !recoveredError) continue;
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 _);
_resume.BadBlocks.Remove(badSector); _resume.BadBlocks.Remove(badSector);
extents.Add(badSector); extents.Add(badSector);
outputFormat.WriteSector(buffer, badSector);
if(scsiReader.LiteOnReadRaw || scsiReader.HldtstReadRaw)
{
byte[] cmi = new byte[1];
byte[] key = buffer.Skip(7).Take(5).ToArray();
if(key.All(static k => k == 0))
{
outputFormat.WriteSectorTag([0, 0, 0, 0, 0], badSector, SectorTagType.DvdTitleKeyDecrypted);
_resume.MissingTitleKeys?.Remove(badSector);
}
else
{
CSS.DecryptTitleKey(discKey, key, out byte[] tmpBuf);
outputFormat.WriteSectorTag(tmpBuf, badSector, SectorTagType.DvdTitleKeyDecrypted);
_resume.MissingTitleKeys?.Remove(badSector);
cmi[0] = buffer[6];
}
if(!_storeEncrypted)
{
ErrorNumber errno =
outputFormat.ReadSectorsTag(badSector,
1,
SectorTagType.DvdTitleKeyDecrypted,
out byte[] titleKey);
if(errno != ErrorNumber.NoError)
{
ErrorMessage?.Invoke(string.Format(Localization.Core.Error_retrieving_title_key_for_sector_0,
badSector));
}
else
buffer = CSS.DecryptSectorLong(buffer, titleKey, cmi);
}
_resume.BadBlocks.Remove(badSector);
outputFormat.WriteSectorLong(buffer, badSector);
}
else
outputFormat.WriteSector(buffer, badSector);
_mediaGraph?.PaintSectorGood(badSector); _mediaGraph?.PaintSectorGood(badSector);
} }

View File

@@ -54,6 +54,9 @@ sealed partial class Reader
// TODO: Raw reading // TODO: Raw reading
public bool HldtstReadRaw; public bool HldtstReadRaw;
public bool LiteOnReadRaw;
public uint layerbreak;
public bool otp;
ulong ScsiGetBlocks() => ScsiGetBlockSize() ? 0 : Blocks; ulong ScsiGetBlocks() => ScsiGetBlockSize() ? 0 : Blocks;
@@ -570,16 +573,22 @@ sealed partial class Reader
switch(_dev.Manufacturer) switch(_dev.Manufacturer)
{ {
case "HL-DT-ST": case "HL-DT-ST":
HldtstReadRaw = !_dev.HlDtStReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); HldtstReadRaw =
!_dev.HlDtStReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _, layerbreak, otp);
break; break;
case "PLEXTOR": case "PLEXTOR":
_plextorReadRaw = !_dev.PlextorReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); _plextorReadRaw = !_dev.PlextorReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _);
break;
case "LITE-ON":
LiteOnReadRaw =
!_dev.LiteOnReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _, layerbreak, otp);
break; break;
} }
if(HldtstReadRaw || _plextorReadRaw) if(HldtstReadRaw || _plextorReadRaw || LiteOnReadRaw)
{ {
CanReadRaw = true; CanReadRaw = true;
LongBlockSize = 2064; LongBlockSize = 2064;
@@ -612,7 +621,9 @@ sealed partial class Reader
AaruConsole.WriteLine(Localization.Core.Using_SyQuest_READ_LONG_6_command); AaruConsole.WriteLine(Localization.Core.Using_SyQuest_READ_LONG_6_command);
else if(HldtstReadRaw) else if(HldtstReadRaw)
AaruConsole.WriteLine(Localization.Core.Using_HL_DT_ST_raw_DVD_reading); AaruConsole.WriteLine(Localization.Core.Using_HL_DT_ST_raw_DVD_reading);
else if(_plextorReadRaw) AaruConsole.WriteLine(Localization.Core.Using_Plextor_raw_DVD_reading); else if(_plextorReadRaw)
AaruConsole.WriteLine(Localization.Core.Using_Plextor_raw_DVD_reading);
else if(LiteOnReadRaw) AaruConsole.WriteLine(Localization.Core.Using_Lite_On_raw_DVD_reading);
} }
else if(_read6) else if(_read6)
AaruConsole.WriteLine(Localization.Core.Using_SCSI_READ_6_command); AaruConsole.WriteLine(Localization.Core.Using_SCSI_READ_6_command);
@@ -674,7 +685,7 @@ sealed partial class Reader
while(true) while(true)
{ {
if(HldtstReadRaw) if(HldtstReadRaw || LiteOnReadRaw)
BlocksToRead = 1; BlocksToRead = 1;
else if(_read6) else if(_read6)
{ {
@@ -804,24 +815,14 @@ sealed partial class Reader
} }
else if(HldtstReadRaw) else if(HldtstReadRaw)
{ {
// We need to fill the buffer before reading it with the HL-DT-ST command. We don't care about sense, sense = _dev.HlDtStReadRawDvd(out buffer,
// because the data can be wrong anyway, so we need to check the buffer data instead. out senseBuf,
_dev.Read12(out buffer, (uint)block,
out senseBuf, count,
0, _timeout,
false, out duration,
false, layerbreak,
false, otp);
false,
(uint)block,
LogicalBlockSize,
0,
16,
false,
_timeout,
out duration);
sense = _dev.HlDtStReadRawDvd(out buffer, out senseBuf, (uint)block, count, _timeout, out duration);
} }
else if(_plextorReadRaw) else if(_plextorReadRaw)
{ {
@@ -832,6 +833,17 @@ sealed partial class Reader
_timeout, _timeout,
out duration); out duration);
} }
else if(LiteOnReadRaw)
{
sense = _dev.LiteOnReadRawDvd(out buffer,
out senseBuf,
(uint)block,
count,
_timeout,
out duration,
layerbreak,
otp);
}
else else
return true; return true;
} }

View File

@@ -577,7 +577,7 @@ public sealed partial class DeviceReport
/// <param name="tryMediaTekF106">Try MediaTek vendor commands</param> /// <param name="tryMediaTekF106">Try MediaTek vendor commands</param>
/// <returns></returns> /// <returns></returns>
public TestedMedia ReportMmcMedia(string mediaType, bool tryPlextor, bool tryPioneer, bool tryNec, bool tryHldtst, public TestedMedia ReportMmcMedia(string mediaType, bool tryPlextor, bool tryPioneer, bool tryNec, bool tryHldtst,
bool tryMediaTekF106) bool tryMediaTekF106, bool tryLiteOn)
{ {
var sense = true; var sense = true;
byte[] buffer = []; byte[] buffer = [];
@@ -2743,24 +2743,8 @@ public sealed partial class DeviceReport
{ {
ctx.AddTask(Localization.Core.Trying_HL_DT_ST_aka_LG_trick_to_raw_read_DVDs).IsIndeterminate(); ctx.AddTask(Localization.Core.Trying_HL_DT_ST_aka_LG_trick_to_raw_read_DVDs).IsIndeterminate();
// We need to fill the buffer before reading it with the HL-DT-ST command. We don't care about sense,
// because the data can be wrong anyway, so we need to check the buffer data later instead.
_dev.Read10(out buffer,
out _,
0,
false,
false,
false,
false,
0,
2048,
0,
1,
_dev.Timeout,
out _);
mediaTest.SupportsHLDTSTReadRawDVD = mediaTest.SupportsHLDTSTReadRawDVD =
!_dev.HlDtStReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); !_dev.HlDtStReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _, 0xffff, false);
}); });
AaruConsole.DebugWriteLine(SCSI_MODULE_NAME, AaruConsole.DebugWriteLine(SCSI_MODULE_NAME,
@@ -2773,6 +2757,26 @@ public sealed partial class DeviceReport
if(mediaTest.SupportsHLDTSTReadRawDVD == true) mediaTest.HLDTSTReadRawDVDData = buffer; if(mediaTest.SupportsHLDTSTReadRawDVD == true) mediaTest.HLDTSTReadRawDVDData = buffer;
} }
if(tryLiteOn)
{
Spectre.ProgressSingleSpinner(ctx =>
{
ctx.AddTask(Localization.Core.Trying_Lite_On_trick_to_raw_read_DVDs).IsIndeterminate();
mediaTest.SupportsLiteOnReadRawDVD =
!_dev.LiteOnReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _, 0xffff, false);
});
AaruConsole.DebugWriteLine(SCSI_MODULE_NAME,
Localization.Core.Sense_equals_0,
!mediaTest.SupportsLiteOnReadRawDVD);
if(mediaTest.SupportsLiteOnReadRawDVD == true)
mediaTest.SupportsLiteOnReadRawDVD = !ArrayHelpers.ArrayIsNullOrEmpty(buffer);
if(mediaTest.SupportsLiteOnReadRawDVD == true) mediaTest.LiteOnReadRawDVDData = buffer;
}
if(tryMediaTekF106) if(tryMediaTekF106)
{ {
var triedLba0 = false; var triedLba0 = false;

View File

@@ -50,9 +50,15 @@ public partial class Device
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</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="lba">Start block address.</param>
/// <param name="transferLength">How many blocks to read.</param> /// <param name="transferLength">How many blocks to read.</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 HlDtStReadRawDvd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, uint timeout, public bool HlDtStReadRawDvd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, uint timeout,
out double duration) out double duration, uint layerbreak, bool otp)
{ {
// We need to fill the buffer before reading it with the HL-DT-ST command. We don't care about sense,
// 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);
senseBuffer = new byte[64]; senseBuffer = new byte[64];
var cdb = new byte[12]; var cdb = new byte[12];
buffer = new byte[2064 * transferLength]; buffer = new byte[2064 * transferLength];
@@ -83,7 +89,7 @@ public partial class Device
AaruConsole.DebugWriteLine(SCSI_MODULE_NAME, Localization.HL_DT_ST_READ_DVD_RAW_took_0_ms, duration); AaruConsole.DebugWriteLine(SCSI_MODULE_NAME, Localization.HL_DT_ST_READ_DVD_RAW_took_0_ms, duration);
if(!CheckSectorNumber(buffer, lba, transferLength)) return true; if(!CheckSectorNumber(buffer, lba, transferLength, layerbreak, otp)) return true;
if(_decoding.Scramble(buffer, transferLength, out byte[] scrambledBuffer) != ErrorNumber.NoError) return true; if(_decoding.Scramble(buffer, transferLength, out byte[] scrambledBuffer) != ErrorNumber.NoError) return true;
@@ -97,19 +103,67 @@ public partial class Device
/// </summary> /// </summary>
/// <param name="buffer">Data buffer</param> /// <param name="buffer">Data buffer</param>
/// <param name="firstLba">First consecutive LBA of the buffer</param> /// <param name="firstLba">First consecutive LBA of the buffer</param>
/// <param name="transferLength">How many blocks to in buffer</param> /// <param name="transferLength">How many blocks in buffer</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>
/// <returns><c>false</c> if any sector is not matching expected value, else <c>true</c></returns> /// <returns><c>false</c> if any sector is not matching expected value, else <c>true</c></returns>
static bool CheckSectorNumber(IReadOnlyList<byte> buffer, uint firstLba, uint transferLength) static bool CheckSectorNumber(IReadOnlyList<byte> buffer, uint firstLba, uint transferLength, uint layerbreak,
bool otp)
{ {
for(var i = 0; i < transferLength; i++) for(var i = 0; i < transferLength; i++)
{ {
byte layer = (byte)(buffer[0 + 2064 * i] & 0x1);
byte[] sectorBuffer = [0x0, buffer[1 + 2064 * i], buffer[2 + 2064 * i], buffer[3 + 2064 * i]]; byte[] sectorBuffer = [0x0, buffer[1 + 2064 * i], buffer[2 + 2064 * i], buffer[3 + 2064 * i]];
var sectorNumber = BigEndianBitConverter.ToUInt32(sectorBuffer, 0); var sectorNumber = BigEndianBitConverter.ToUInt32(sectorBuffer, 0);
if(sectorNumber != firstLba + i + 0x30000) return false;
if(otp)
{
if(!IsCorrectDlOtpPsn(sectorNumber, (ulong)(firstLba + i), layer, layerbreak)) return false;
}
else
{
if(!IsCorrectSlPsn(sectorNumber, (ulong)(firstLba + i))) return false;
}
} }
return true; return true;
} }
/// <summary>
/// Checks if the PSN for a raw sector matches the expected LBA for a single layer DVD
/// </summary>
/// <param name="sectorNumber">The Sector Number from Identification Data (ID) </param>
/// <param name="lba">The expected LBA</param>
/// <returns><c>false</c> if the sector is not matching expected value, else <c>true</c></returns>
private static bool IsCorrectSlPsn(uint sectorNumber, ulong lba) => sectorNumber == lba + 0x30000;
/// <summary>
/// Checks if the PSN for a raw sector matches the expected LBA for a dual layer DVD with parallel track path
/// </summary>
/// <param name="sectorNumber">The Sector Number from Identification Data (ID) </param>
/// <param name="lba">The expected LBA</param>
/// <returns><c>false</c> if the sector is not matching expected value, else <c>true</c></returns>
private static bool IsCorrectDlPtpPsn(uint sectorNumber, ulong lba, byte layer, uint layerbreak)
{
if(layer != 1) return IsCorrectSlPsn(sectorNumber, lba);
return sectorNumber == (lba - layerbreak) + 0x30000;
}
/// <summary>
/// Checks if the PSN for a raw sector matches the expected LBA for a dual layer DVD with opposite track path
/// </summary>
/// <param name="sectorNumber">The Sector Number from Identification Data (ID) </param>
/// <param name="lba">The expected LBA</param>
/// <returns><c>false</c> if the sector is not matching expected value, else <c>true</c></returns>
private static bool IsCorrectDlOtpPsn(uint sectorNumber, ulong lba, byte layer, uint layerbreak)
{
if(layer != 1) return IsCorrectSlPsn(sectorNumber, lba);
ulong n = ~(layerbreak + 1 + (layerbreak - (lba + 0x30000))) & 0x00ffffff;
return sectorNumber == n;
}
} }

View File

@@ -0,0 +1,303 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : LiteOn.cs
// Author(s) : Rebecca Wallander <sakcheen@gmail.com>
//
// Component : LiteOn vendor commands.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains vendor commands for Lite-On SCSI devices.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2025 Rebecca Wallander
// ****************************************************************************/
using System;
using Aaru.CommonTypes.Enums;
using Aaru.Console;
namespace Aaru.Devices;
public partial class Device
{
private uint _bufferOffset = 0;
/// <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>
/// <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="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 byte[] senseBuffer, uint lba, uint transferLength, uint timeout,
out double duration, uint layerbreak, bool otp)
{
_bufferOffset %= 714;
bool sense;
if(layerbreak > 0 && transferLength > 1 && lba + 0x30000 > layerbreak - 256 && lba + 0x30000 < layerbreak + 256)
{
buffer = new byte[transferLength * 2064];
duration = 0;
senseBuffer = new byte[64];
return true;
}
if(714 - _bufferOffset < transferLength)
{
sense = LiteOnReadSectorsAcrossBufferBorder(out buffer,
out senseBuffer,
lba,
transferLength,
timeout,
out duration,
layerbreak,
otp);
}
else
{
sense = LiteOnReadSectorsFromBuffer(out buffer,
out senseBuffer,
lba,
transferLength,
timeout,
out duration,
layerbreak,
otp);
}
Error = LastError != 0;
AaruConsole.DebugWriteLine(SCSI_MODULE_NAME, Localization.LiteOn_READ_DVD_RAW_took_0_ms, duration);
return sense;
}
/// <summary>
/// Reads the Lite-On device's memory buffer and returns raw sector data
/// </summary>
/// <param name="buffer">Buffer where the ReadBuffer (RAW) response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
/// <param name="bufferOffset">The offset to read the buffer at</param>
/// <param name="transferLength"></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>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
private bool LiteOnReadBuffer(out byte[] buffer, out byte[] senseBuffer, uint bufferOffset, uint transferLength,
uint timeout, out double duration, uint lba)
{
// We need to fill the buffer before reading it with the ReadBuffer command. We don't care about sense,
// 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);
senseBuffer = new byte[64];
var cdb = new byte[10];
buffer = new byte[transferLength];
cdb[0] = (byte)ScsiCommands.ReadBuffer;
cdb[1] = 0x01;
cdb[2] = 0x01;
cdb[3] = (byte)((bufferOffset & 0xFF0000) >> 16);
cdb[4] = (byte)((bufferOffset & 0xFF00) >> 8);
cdb[5] = (byte)(bufferOffset & 0xFF);
cdb[6] = (byte)((buffer.Length & 0xFF0000) >> 16);
cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8);
cdb[8] = (byte)(buffer.Length & 0xFF);
LastError = SendScsiCommand(cdb,
ref buffer,
out senseBuffer,
timeout,
ScsiDirection.In,
out duration,
out bool sense);
return sense;
}
/// <summary>
/// Reads raw sectors from the device's memory
/// </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 (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="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>
private bool LiteOnReadSectorsFromBuffer(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength,
uint timeout, out double duration, uint layerbreak, bool otp)
{
bool sense = LiteOnReadBuffer(out buffer,
out senseBuffer,
_bufferOffset * 2384,
transferLength * 2384,
timeout,
out duration,
lba);
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength);
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, true))
{
// Buffer offset lost, try to find it again
int offset = FindBufferOffset(lba, timeout, layerbreak, otp);
if(offset == -1) return true;
_bufferOffset = (uint)offset;
sense = LiteOnReadBuffer(out buffer,
out senseBuffer,
_bufferOffset * 2384,
transferLength * 2384,
timeout,
out duration,
lba);
deinterleaved = DeinterleaveEccBlock(buffer, transferLength);
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, otp)) return true;
}
if(_decoding.Scramble(deinterleaved, transferLength, out byte[] scrambledBuffer) != ErrorNumber.NoError)
return true;
buffer = scrambledBuffer;
_bufferOffset += transferLength;
return sense;
}
/// <summary>
/// Reads raw sectors when they cross the device's memory border
/// </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 (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="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>
private bool LiteOnReadSectorsAcrossBufferBorder(out byte[] buffer, out byte[] senseBuffer, uint lba,
uint transferLength, uint timeout, out double duration,
uint layerbreak, bool otp)
{
uint newTransferLength1 = 714 - _bufferOffset;
uint newTransferLength2 = transferLength - newTransferLength1;
bool sense1 = LiteOnReadBuffer(out byte[] buffer1,
out byte[] _,
_bufferOffset * 2384,
newTransferLength1 * 2384,
timeout,
out double duration1,
lba);
bool sense2 = LiteOnReadBuffer(out byte[] buffer2,
out byte[] _,
0,
newTransferLength2 * 2384,
timeout,
out double duration2,
lba);
senseBuffer = new byte[64]; // TODO
buffer = new byte[2384 * transferLength];
Array.Copy(buffer1, buffer, buffer1.Length);
Array.Copy(buffer2, 0, buffer, buffer1.Length, buffer2.Length);
duration = duration1 + duration2;
byte[] deinterleaved = DeinterleaveEccBlock(buffer, transferLength);
if(!CheckSectorNumber(deinterleaved, lba, transferLength, layerbreak, otp)) return true;
if(_decoding.Scramble(deinterleaved, transferLength, out byte[] scrambledBuffer) != ErrorNumber.NoError)
return true;
buffer = scrambledBuffer;
_bufferOffset = newTransferLength2;
return sense1 && sense2;
}
/// <summary>
/// Sometimes the offset on the drive memory can get lost. This tries to find it again.
/// </summary>
/// <param name="lba">The expected LBA</param>
/// <param name="timeout">Timeout in seconds.</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>
/// <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++)
{
LiteOnReadBuffer(out byte[] buffer, out byte[] _, i * 2384, 2384, timeout, out double _, lba);
if(CheckSectorNumber(buffer, lba, 1, layerbreak, otp))
{
return (int)i;
}
}
return -1;
}
/// <summary>
/// Deinterleave the ECC block stored within a 2384 byte raw sector
/// </summary>
/// <param name="buffer">Data buffer</param>
/// <param name="transferLength">How many blocks in buffer</param>
/// <param name="deinterleaved"></param>
/// <returns>The deinterleaved sectors</returns>
private static byte[] DeinterleaveEccBlock(byte[] buffer, uint transferLength)
{
// TODO: Save ECC instead of just throwing it away
var deinterleaved = new byte[2064 * transferLength];
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);
}
}
return deinterleaved;
}
}

View File

@@ -110,9 +110,9 @@ public partial class Device
cdb[3] = (byte)((lba & 0xFF0000) >> 16); cdb[3] = (byte)((lba & 0xFF0000) >> 16);
cdb[4] = (byte)((lba & 0xFF00) >> 8); cdb[4] = (byte)((lba & 0xFF00) >> 8);
cdb[5] = (byte)(lba & 0xFF); cdb[5] = (byte)(lba & 0xFF);
cdb[3] = (byte)((buffer.Length & 0xFF0000) >> 16); cdb[6] = (byte)((buffer.Length & 0xFF0000) >> 16);
cdb[4] = (byte)((buffer.Length & 0xFF00) >> 8); cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8);
cdb[5] = (byte)(buffer.Length & 0xFF); cdb[8] = (byte)(buffer.Length & 0xFF);
LastError = SendScsiCommand(cdb, LastError = SendScsiCommand(cdb,
ref buffer, ref buffer,

File diff suppressed because it is too large Load Diff

View File

@@ -508,4 +508,7 @@
<data name="Remote_error_0_in_OS_Read" xml:space="preserve"> <data name="Remote_error_0_in_OS_Read" xml:space="preserve">
<value>Remote error {0} in OS Read...</value> <value>Remote error {0} in OS Read...</value>
</data> </data>
<data name="LiteOn_READ_DVD_RAW_took_0_ms" xml:space="preserve">
<value>Lite-On READ DVD (RAW) took {0} ms.</value>
</data>
</root> </root>

View File

@@ -298,7 +298,11 @@ public sealed partial class AaruFormat
/// <summary>DVD ID Error Detection Code (IED)</summary> /// <summary>DVD ID Error Detection Code (IED)</summary>
DvdSectorIed = 84, DvdSectorIed = 84,
/// <summary>DVD Error Detection Code (EDC)</summary> /// <summary>DVD Error Detection Code (EDC)</summary>
DvdSectorEdc = 85 DvdSectorEdc = 85,
/// <summary>DVD Error Correction Code (ECC) Parity of Inner Code (PI)</summary>
DvdSectorEccPi = 86,
/// <summary>DVD Error Correction Code (ECC) Parity of Outer Code (PO)</summary>
DvdEccBlockPo = 87,
} }
#endregion #endregion

File diff suppressed because it is too large Load Diff

View File

@@ -2202,6 +2202,9 @@ Do you want to proceed? (Y/N): </value>
</data> </data>
<data name="Trying_HL_DT_ST_aka_LG_trick_to_raw_read_DVDs" xml:space="preserve"> <data name="Trying_HL_DT_ST_aka_LG_trick_to_raw_read_DVDs" xml:space="preserve">
<value>Trying HL-DT-ST (aka LG) trick to raw read DVDs...</value> <value>Trying HL-DT-ST (aka LG) trick to raw read DVDs...</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>
<data name="Trying_MediaTek_READ_DRAM_command" xml:space="preserve"> <data name="Trying_MediaTek_READ_DRAM_command" xml:space="preserve">
<value>Trying MediaTek READ DRAM command...</value> <value>Trying MediaTek READ DRAM command...</value>
@@ -3494,6 +3497,9 @@ It has no sense to do it, and it will put too much strain on the tape.</value>
<value>This image contains low-level flux captures.</value> <value>This image contains low-level flux captures.</value>
</data> </data>
<data name="Archive_Information_With_Markup" xml:space="preserve"> <data name="Archive_Information_With_Markup" xml:space="preserve">
<value>[bold]Archive information:[/]</value> <value>[bold]Archive information:[/]</value>
</data> </data>
<data name="Using_Lite-On_raw_DVD_reading" xml:space="preserve">
<value>Using Lite-On raw DVD reading.</value>
</data>
</root> </root>

View File

@@ -150,7 +150,9 @@ static class HlDtSt
lba, lba,
count, count,
dev.Timeout, dev.Timeout,
out double duration); out double duration,
0xffffff,
false);
menu: menu:
AaruConsole.WriteLine(Localization.Device_0, devPath); AaruConsole.WriteLine(Localization.Device_0, devPath);

View File

@@ -685,7 +685,8 @@ sealed class DeviceReportCommand : Command
tryHldtst = false, tryHldtst = false,
tryPioneer = false, tryPioneer = false,
tryNec = false, tryNec = false,
tryMediaTekF106 = false; tryMediaTekF106 = false,
tryLiteOn = false;
tryPlextor |= tryPlextor |=
dev.Manufacturer.Equals("plextor", StringComparison.InvariantCultureIgnoreCase); dev.Manufacturer.Equals("plextor", StringComparison.InvariantCultureIgnoreCase);
@@ -698,6 +699,8 @@ sealed class DeviceReportCommand : Command
tryNec |= dev.Manufacturer.Equals("nec", StringComparison.InvariantCultureIgnoreCase); tryNec |= dev.Manufacturer.Equals("nec", StringComparison.InvariantCultureIgnoreCase);
tryLiteOn |= dev.Manufacturer.Equals("lite-on", StringComparison.InvariantCultureIgnoreCase);
if(!iomegaRev) if(!iomegaRev)
{ {
if(!tryPlextor) if(!tryPlextor)
@@ -831,7 +834,8 @@ sealed class DeviceReportCommand : Command
tryPioneer, tryPioneer,
tryNec, tryNec,
tryHldtst, tryHldtst,
tryMediaTekF106); tryMediaTekF106,
tryLiteOn);
if(mediaTest is null) continue; if(mediaTest is null) continue;