diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs b/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs
index 9dbfe59c4..bfa17a88b 100644
--- a/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs
+++ b/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs
@@ -37,6 +37,7 @@ using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Core.Logging;
+using Aaru.Decoders.CD;
using Aaru.Devices;
using Schemas;
@@ -48,6 +49,71 @@ namespace Aaru.Core.Devices.Dumping
{
partial class Dump
{
+ static bool IsData(byte[] sector)
+ {
+ if(sector?.Length != 2352)
+ return false;
+
+ byte[] syncMark =
+ {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
+ };
+
+ byte[] testMark = new byte[12];
+ Array.Copy(sector, 0, testMark, 0, 12);
+
+ return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2);
+ }
+
+ static bool IsScrambledData(byte[] sector, int wantedLba, out int? offset)
+ {
+ offset = 0;
+
+ if(sector?.Length != 2352)
+ return false;
+
+ byte[] syncMark =
+ {
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
+ };
+
+ byte[] testMark = new byte[12];
+
+ for(int i = 0; i <= 2336; i++)
+ {
+ Array.Copy(sector, i, testMark, 0, 12);
+
+ if(syncMark.SequenceEqual(testMark) &&
+ (sector[i + 0xF] == 0x60 || sector[i + 0xF] == 0x61 || sector[i + 0xF] == 0x62))
+
+ {
+ // De-scramble M and S
+ int minute = sector[i + 12] ^ 0x01;
+ int second = sector[i + 13] ^ 0x80;
+ int frame = sector[i + 14];
+
+ // Convert to binary
+ minute = ((minute / 16) * 10) + (minute & 0x0F);
+ second = ((second / 16) * 10) + (second & 0x0F);
+ frame = ((frame / 16) * 10) + (frame & 0x0F);
+
+ // Calculate the first found LBA
+ int lba = ((minute * 60 * 75) + (second * 75) + frame) - 150;
+
+ // Calculate the difference between the found LBA and the requested one
+ int diff = wantedLba - lba;
+
+ offset = i + (2352 * diff);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // TODO: Set pregap for Track 1
+ // TODO: Detect errors in sectors
/// Reads all CD user data
/// Extents with audio sectors
/// Total number of positive sectors
@@ -77,11 +143,11 @@ namespace Aaru.Core.Devices.Dumping
/// Total commands duration
void ReadCdiReady(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents,
IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents,
- ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10,
- bool read12, bool read16, bool readcd, uint subSize, MmcSubchannel supportedSubchannel,
- bool supportsLongSectors, ref double totalDuration, Track[] tracks, SubchannelLog subLog,
- MmcSubchannel desiredSubchannel, Dictionary isrcs, ref string mcn,
- HashSet subchannelExtents, ulong blocks)
+ ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, uint subSize,
+ MmcSubchannel supportedSubchannel, ref double totalDuration, Track[] tracks,
+ SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary isrcs,
+ ref string mcn, HashSet subchannelExtents, ulong blocks, bool cdiReadyReadAsAudio,
+ int offsetBytes, int sectorsForOffset)
{
ulong sectorSpeedStart = 0; // Used to calculate correct speed
DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation
@@ -91,13 +157,22 @@ namespace Aaru.Core.Devices.Dumping
double cmdDuration = 0; // Command execution time
const uint sectorSize = 2352; // Full sector size
Track firstTrack = tracks.FirstOrDefault(t => t.TrackSequence == 1);
+ uint blocksToRead = 0; // How many sectors to read at once
if(firstTrack is null)
return;
+ if(cdiReadyReadAsAudio)
+ {
+ _dumpLog.WriteLine("Setting speed to 8x for CD-i Ready reading as audio.");
+ UpdateStatus?.Invoke("Setting speed to 8x for CD-i Ready reading as audio.");
+
+ _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _);
+ }
+
InitProgress?.Invoke();
- for(ulong i = _resume.NextBlock; i < firstTrack.TrackStartSector; i += _maximumReadable)
+ for(ulong i = _resume.NextBlock; i < firstTrack.TrackStartSector; i += blocksToRead)
{
if(_aborted)
{
@@ -108,12 +183,24 @@ namespace Aaru.Core.Devices.Dumping
break;
}
- if(i >= firstTrack.TrackStartSector)
- break;
-
uint firstSectorToRead = (uint)i;
- Track track = tracks.OrderBy(t => t.TrackStartSector).LastOrDefault(t => i >= t.TrackStartSector);
+ blocksToRead = _maximumReadable;
+
+ if(blocksToRead == 1 && cdiReadyReadAsAudio)
+ blocksToRead += (uint)sectorsForOffset;
+
+ if(cdiReadyReadAsAudio)
+ {
+ // TODO: FreeBSD bug
+ if(offsetBytes < 0)
+ {
+ if(i == 0)
+ firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
+ else
+ firstSectorToRead -= (uint)sectorsForOffset;
+ }
+ }
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
@@ -133,34 +220,11 @@ namespace Aaru.Core.Devices.Dumping
UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
(long)blocks);
- if(readcd)
- {
- sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, _maximumReadable,
- MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
- true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
+ sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
+ MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
+ MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
- totalDuration += cmdDuration;
- }
- else if(read16)
- {
- sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, firstSectorToRead, blockSize,
- 0, _maximumReadable, false, _dev.Timeout, out cmdDuration);
- }
- else if(read12)
- {
- sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
- blockSize, 0, _maximumReadable, false, _dev.Timeout, out cmdDuration);
- }
- else if(read10)
- {
- sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
- blockSize, 0, (ushort)_maximumReadable, _dev.Timeout, out cmdDuration);
- }
- else if(read6)
- {
- sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)_maximumReadable,
- _dev.Timeout, out cmdDuration);
- }
+ totalDuration += cmdDuration;
double elapsed;
@@ -172,35 +236,12 @@ namespace Aaru.Core.Devices.Dumping
UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)",
(long)i + r, (long)blocks);
- if(readcd)
- {
- sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, 1,
- MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
- true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
- out cmdDuration);
+ sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize,
+ (uint)sectorsForOffset + 1, MmcSectorTypes.AllTypes, false, false, true,
+ MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
+ supportedSubchannel, _dev.Timeout, out cmdDuration);
- totalDuration += cmdDuration;
- }
- else if(read16)
- {
- sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i + r, blockSize, 0, 1,
- false, _dev.Timeout, out cmdDuration);
- }
- else if(read12)
- {
- sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)(i + r),
- blockSize, 0, 1, false, _dev.Timeout, out cmdDuration);
- }
- else if(read10)
- {
- sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)(i + r),
- blockSize, 0, 1, _dev.Timeout, out cmdDuration);
- }
- else if(read6)
- {
- sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, 1, _dev.Timeout,
- out cmdDuration);
- }
+ totalDuration += cmdDuration;
if(!sense &&
!_dev.Error)
@@ -210,6 +251,10 @@ namespace Aaru.Core.Devices.Dumping
extents.Add(i + r, 1, true);
DateTime writeStart = DateTime.Now;
+ if(cdiReadyReadAsAudio)
+ FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel,
+ ref blocksToRead, subSize, ref cmdBuf, blockSize, false);
+
if(supportedSubchannel != MmcSubchannel.None)
{
byte[] data = new byte[sectorSize];
@@ -219,12 +264,14 @@ namespace Aaru.Core.Devices.Dumping
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
+ if(cdiReadyReadAsAudio)
+ data = Sector.Scramble(data);
+
_outputPlugin.WriteSectorsLong(data, i + r, 1);
bool indexesChanged =
WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i + r, 1,
- subLog, isrcs, (byte)track.TrackSequence, ref mcn, tracks,
- subchannelExtents);
+ subLog, isrcs, 1, ref mcn, tracks, subchannelExtents);
// Set tracks and go back
if(indexesChanged)
@@ -237,25 +284,7 @@ namespace Aaru.Core.Devices.Dumping
}
else
{
- if(supportsLongSectors)
- {
- _outputPlugin.WriteSectorsLong(cmdBuf, i + r, 1);
- }
- else
- {
- if(cmdBuf.Length % sectorSize == 0)
- {
- byte[] data = new byte[2048];
-
- Array.Copy(cmdBuf, 16, data, 2048, 2048);
-
- _outputPlugin.WriteSectors(data, i + r, 1);
- }
- else
- {
- _outputPlugin.WriteSectorsLong(cmdBuf, i + r, 1);
- }
- }
+ _outputPlugin.WriteSectorsLong(cmdBuf, i + r, 1);
}
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
@@ -291,61 +320,68 @@ namespace Aaru.Core.Devices.Dumping
if(!sense &&
!_dev.Error)
{
+ if(cdiReadyReadAsAudio)
+ FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead,
+ subSize, ref cmdBuf, blockSize, false);
+
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
- extents.Add(i, _maximumReadable, true);
+ extents.Add(i, blocksToRead, true);
DateTime writeStart = DateTime.Now;
if(supportedSubchannel != MmcSubchannel.None)
{
- byte[] data = new byte[sectorSize * _maximumReadable];
- byte[] sub = new byte[subSize * _maximumReadable];
+ byte[] data = new byte[sectorSize * blocksToRead];
+ byte[] sub = new byte[subSize * blocksToRead];
+ byte[] tmpData = new byte[sectorSize];
- for(int b = 0; b < _maximumReadable; b++)
+ for(int b = 0; b < blocksToRead; b++)
{
- Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
+ if(cdiReadyReadAsAudio)
+ {
+ Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), tmpData, 0, sectorSize);
+ tmpData = Sector.Scramble(tmpData);
+ Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize);
+ }
+ else
+ Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
}
- _outputPlugin.WriteSectorsLong(data, i, _maximumReadable);
+ _outputPlugin.WriteSectorsLong(data, i, blocksToRead);
bool indexesChanged = WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i,
- _maximumReadable, subLog, isrcs,
- (byte)track.TrackSequence, ref mcn, tracks,
+ blocksToRead, subLog, isrcs, 1, ref mcn, tracks,
subchannelExtents);
// Set tracks and go back
if(indexesChanged)
{
(_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
- i -= _maximumReadable;
+ i -= blocksToRead;
continue;
}
}
else
{
- if(supportsLongSectors)
+ if(cdiReadyReadAsAudio)
{
- _outputPlugin.WriteSectorsLong(cmdBuf, i, _maximumReadable);
+ byte[] tmpData = new byte[sectorSize];
+ byte[] data = new byte[sectorSize * blocksToRead];
+
+ for(int b = 0; b < blocksToRead; b++)
+ {
+ Array.Copy(cmdBuf, (int)(b * sectorSize), tmpData, 0, sectorSize);
+ tmpData = Sector.Scramble(tmpData);
+ Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize);
+ }
+
+ _outputPlugin.WriteSectorsLong(data, i, blocksToRead);
}
else
- {
- if(cmdBuf.Length % sectorSize == 0)
- {
- byte[] data = new byte[2048 * _maximumReadable];
-
- for(int b = 0; b < _maximumReadable; b++)
- Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048);
-
- _outputPlugin.WriteSectors(data, i, _maximumReadable);
- }
- else
- {
- _outputPlugin.WriteSectorsLong(cmdBuf, i, _maximumReadable);
- }
- }
+ _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
}
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
@@ -357,9 +393,9 @@ namespace Aaru.Core.Devices.Dumping
break;
}
- sectorSpeedStart += _maximumReadable;
+ sectorSpeedStart += blocksToRead;
- _resume.NextBlock = i + _maximumReadable;
+ _resume.NextBlock = i + blocksToRead;
elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs
index 66b66b6e0..33c2379ee 100644
--- a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs
+++ b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs
@@ -112,10 +112,11 @@ namespace Aaru.Core.Devices.Dumping
bool hiddenTrack; // Disc has a hidden track before track 1
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
MmcSubchannel desiredSubchannel; // User requested subchannel
- bool bcdSubchannel = false; // Subchannel positioning is in BCD
- Dictionary isrcs = new Dictionary();
- string mcn = null;
- HashSet subchannelExtents = new HashSet();
+ bool bcdSubchannel = false; // Subchannel positioning is in BCD
+ Dictionary isrcs = new Dictionary();
+ string mcn = null;
+ HashSet subchannelExtents = new HashSet();
+ bool cdiReadyReadAsAudio = false;
Dictionary mediaTags = new Dictionary(); // Media tags
@@ -892,15 +893,6 @@ namespace Aaru.Core.Devices.Dumping
Invoke($"Track {trk.TrackSequence} starts at LBA {trk.TrackStartSector} and ends at LBA {trk.TrackEndSector}");
#endif
- if(dskType == MediaType.CDIREADY &&
- !_skipCdireadyHole)
- {
- _dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
-
- UpdateStatus?.
- Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
- }
-
// Check offset
if(_fixOffset)
{
@@ -1041,12 +1033,100 @@ namespace Aaru.Core.Devices.Dumping
// Start reading
start = DateTime.UtcNow;
- if(dskType == MediaType.CDIREADY && _skipCdireadyHole)
+ if(dskType == MediaType.CDIREADY)
{
- ReadCdiReady(blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
- leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
- subSize, supportedSubchannel, supportsLongSectors, ref totalDuration, tracks, subLog,
- desiredSubchannel, isrcs, ref mcn, subchannelExtents, blocks);
+ Track track0 = tracks.FirstOrDefault(t => t.TrackSequence == 0);
+
+ track0.TrackType = TrackType.CdMode2Formless;
+
+ if(!supportsLongSectors)
+ {
+ _dumpLog.WriteLine("Dumping CD-i Ready requires the output image format to support long sectors.");
+
+ StoppingErrorMessage?.
+ Invoke("Dumping CD-i Ready requires the output image format to support long sectors.");
+
+ return;
+ }
+
+ if(!readcd)
+ {
+ _dumpLog.WriteLine("Dumping CD-i Ready requires the drive to support the READ CD command.");
+
+ StoppingErrorMessage?.
+ Invoke("Dumping CD-i Ready requires the drive to support the READ CD command.");
+
+ return;
+ }
+
+ sense = _dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true,
+ MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None,
+ _dev.Timeout, out _);
+
+ hiddenData = IsData(cmdBuf);
+
+ if(!hiddenData)
+ {
+ cdiReadyReadAsAudio = IsScrambledData(cmdBuf, 0, out combinedOffset);
+
+ if(cdiReadyReadAsAudio)
+ {
+ offsetBytes = combinedOffset.Value;
+ sectorsForOffset = offsetBytes / (int)sectorSize;
+
+ if(sectorsForOffset < 0)
+ sectorsForOffset *= -1;
+
+ if(offsetBytes % sectorSize != 0)
+ sectorsForOffset++;
+
+ _dumpLog.WriteLine("Enabling skipping CD-i Ready hole because drive returns data as audio.");
+
+ UpdateStatus?.Invoke("Enabling skipping CD-i Ready hole because drive returns data as audio.");
+
+ _skipCdireadyHole = true;
+
+ if(driveOffset is null)
+ {
+ _dumpLog.WriteLine("Drive reading offset not found in database.");
+ UpdateStatus?.Invoke("Drive reading offset not found in database.");
+
+ _dumpLog.
+ WriteLine($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples).");
+
+ UpdateStatus?.
+ Invoke($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples).");
+ }
+ else
+ {
+ _dumpLog.
+ WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples).");
+
+ UpdateStatus?.
+ Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples).");
+
+ discOffset = offsetBytes - driveOffset;
+
+ _dumpLog.WriteLine($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)");
+
+ UpdateStatus?.Invoke($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)");
+ }
+ }
+ }
+
+ if(!_skipCdireadyHole)
+ {
+ _dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
+
+ UpdateStatus?.
+ Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
+ }
+
+ if(_skipCdireadyHole)
+ ReadCdiReady(blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
+ leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, subSize, supportedSubchannel,
+ ref totalDuration, tracks, subLog, desiredSubchannel, isrcs, ref mcn,
+ subchannelExtents, blocks, cdiReadyReadAsAudio, offsetBytes, sectorsForOffset);
}
ReadCdData(audioExtents, blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog,
diff --git a/Aaru.Core/Devices/Dumping/Dump.cs b/Aaru.Core/Devices/Dumping/Dump.cs
index 425d17f9c..33771bef1 100644
--- a/Aaru.Core/Devices/Dumping/Dump.cs
+++ b/Aaru.Core/Devices/Dumping/Dump.cs
@@ -87,7 +87,7 @@ namespace Aaru.Core.Devices.Dumping
Resume _resume;
Sidecar _sidecarClass;
uint _skip;
- readonly bool _skipCdireadyHole;
+ bool _skipCdireadyHole;
int _speed;
int _speedMultiplier;
bool _supportsPlextorD8;