diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index 0aa377413..dc9b1c985 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -234,6 +234,7 @@ + diff --git a/DiscImageChef.Core/Devices/Dumping/CompactDisc/Data.cs b/DiscImageChef.Core/Devices/Dumping/CompactDisc/Data.cs new file mode 100644 index 000000000..d29c4c760 --- /dev/null +++ b/DiscImageChef.Core/Devices/Dumping/CompactDisc/Data.cs @@ -0,0 +1,370 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : CompactDisc.cs +// Author(s) : Natalia Portillo +// +// Component : Core algorithms. +// +// --[ Description ] ---------------------------------------------------------- +// +// Dumps CDs and DDCDs. +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2019 Natalia Portillo +// ****************************************************************************/ + +using System; +using DiscImageChef.CommonTypes.Enums; +using DiscImageChef.CommonTypes.Extents; +using DiscImageChef.Console; +using DiscImageChef.Core.Logging; +using DiscImageChef.Decoders.SCSI; +using DiscImageChef.Devices; +using Schemas; + +// ReSharper disable JoinDeclarationAndInitializer +// ReSharper disable InlineOutVariableDeclaration +// ReSharper disable TooWideLocalVariableScope + +namespace DiscImageChef.Core.Devices.Dumping +{ + partial class Dump + { + /// Reads all CD user data + /// Extents with audio sectors + /// Total number of positive sectors + /// Size of the read sector in bytes + /// Current read speed + /// Current dump hardware try + /// Extents + /// IMGBurn log + /// Duration of image write + /// Last sector number + /// Lead-out extents + /// Maximum speed + /// MHDD log + /// Minimum speed + /// Is trim a new one? + /// Next cluster of sectors is all data + /// Read offset + /// Device supports READ(6) + /// Device supports READ(10) + /// Device supports READ(12) + /// Device supports READ(16) + /// Device supports READ CD + /// Sectors needed to fix offset + /// Subchannel size in bytes + /// Drive's maximum supported subchannel + /// Supports reading EDC and ECC + /// Total commands duration + void ReadCdData(ExtentsULong audioExtents, ulong blocks, uint blockSize, ref double currentSpeed, + DumpHardwareType currentTry, ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration, + long lastSector, ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, + ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10, + bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize, + MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration) + { + ulong sectorSpeedStart = 0; // Used to calculate correct speed + DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation + uint blocksToRead = 0; // How many sectors to read at once + bool sense = true; // Sense indicator + byte[] cmdBuf = null; // Data buffer + byte[] senseBuf = null; // Sense buffer + double cmdDuration = 0; // Command execution time + const uint sectorSize = 2352; // Full sector size + byte[] tmpBuf; // Temporary buffer + newTrim = false; + + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + while(leadOutExtents.Contains(i)) + { + i++; + } + + if((long)i > lastSector) + break; + + uint firstSectorToRead = (uint)i; + + if((lastSector + 1) - (long)i < _maximumReadable) + _maximumReadable = (uint)((lastSector + 1) - (long)i); + + blocksToRead = 0; + bool inData = nextData; + + for(ulong j = i; j < i + _maximumReadable; j++) + { + if(nextData) + { + if(audioExtents.Contains(j)) + { + nextData = false; + + break; + } + + blocksToRead++; + } + else + { + if(!audioExtents.Contains(j)) + { + nextData = true; + + break; + } + + blocksToRead++; + } + } + + if(blocksToRead == 1) + blocksToRead = 2; + + if(_fixOffset && !inData) + { + // 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 + + // ReSharper disable CompareOfFloatsByEqualityOperator + if(currentSpeed > maxSpeed && + currentSpeed != 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed != 0) + minSpeed = currentSpeed; + + // ReSharper restore CompareOfFloatsByEqualityOperator + + #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator + + 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, 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, blocksToRead, false, _dev.Timeout, out cmdDuration); + } + else if(read12) + { + sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead, + blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration); + } + else if(read10) + { + sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead, + blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration); + } + else if(read6) + { + sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead, + _dev.Timeout, out cmdDuration); + } + + // Because one block has been partially used to fix the offset + if(_fixOffset && + !inData && + offsetBytes != 0) + { + int offsetFix = offsetBytes < 0 ? offsetFix = (int)(sectorSize - (offsetBytes * -1)) + : offsetFix = offsetBytes; + + if(supportedSubchannel != MmcSubchannel.None) + { + // Deinterleave subchannel + byte[] data = new byte[sectorSize * blocksToRead]; + byte[] sub = new byte[subSize * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); + Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); + } + + tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)]; + Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length); + data = tmpBuf; + + blocksToRead -= (uint)sectorsForOffset; + + // Reinterleave subchannel + cmdBuf = new byte[blockSize * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize); + Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize); + } + } + else + { + tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)]; + Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length); + cmdBuf = tmpBuf; + blocksToRead -= (uint)sectorsForOffset; + } + } + + if(!sense && + !_dev.Error) + { + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + extents.Add(i, blocksToRead, true); + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize * blocksToRead]; + byte[] sub = new byte[subSize * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) + { + 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, blocksToRead); + _outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel); + } + else + { + if(supportsLongSectors) + { + _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead); + } + else + { + if(cmdBuf.Length % sectorSize == 0) + { + byte[] data = new byte[2048 * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) + Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048); + + _outputPlugin.WriteSectors(data, i, blocksToRead); + } + else + { + _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead); + } + } + } + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + } + else + { + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + _outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip); + + _outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, _skip, + SectorTagType.CdSectorSubchannel); + } + else + { + if(supportsLongSectors) + { + _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); + } + else + { + if(cmdBuf.Length % sectorSize == 0) + _outputPlugin.WriteSectors(new byte[2048 * _skip], i, _skip); + else + _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); + } + } + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; + } + + sectorSpeedStart += blocksToRead; + + _resume.NextBlock = i + blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed < 1) + continue; + + currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + EndProgress?.Invoke(); + } + } +} \ No newline at end of file diff --git a/DiscImageChef.Core/Devices/Dumping/CompactDisc/Dump.cs b/DiscImageChef.Core/Devices/Dumping/CompactDisc/Dump.cs index 47265c078..a1b358167 100644 --- a/DiscImageChef.Core/Devices/Dumping/CompactDisc/Dump.cs +++ b/DiscImageChef.Core/Devices/Dumping/CompactDisc/Dump.cs @@ -70,7 +70,6 @@ namespace DiscImageChef.Core.Devices.Dumping ExtentsULong audioExtents; // Extents with audio sectors ulong blocks; // Total number of positive sectors uint blockSize; // Size of the read sector in bytes - uint blocksToRead = 0; // How many sectors to read at once CdOffset cdOffset; // Read offset from database byte[] cmdBuf = null; // Data buffer double cmdDuration = 0; // Command execution time @@ -116,7 +115,6 @@ namespace DiscImageChef.Core.Devices.Dumping int firstTrackLastSession; // Number of first track in last session bool hiddenTrack; // Disc has a hidden track before track 1 - bool nextData; // Next cluster of sectors is all data; MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel DateTime timeSpeedStart; // Time of start for speed calculation @@ -919,7 +917,6 @@ namespace DiscImageChef.Core.Devices.Dumping ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008); audioExtents = new ExtentsULong(); - nextData = tracks[0].TrackType != TrackType.Audio; foreach(Track audioTrack in tracks.Where(t => t.TrackType == TrackType.Audio)) { @@ -942,283 +939,12 @@ namespace DiscImageChef.Core.Devices.Dumping } // Start reading - start = DateTime.UtcNow; - currentSpeed = 0; - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - InitProgress?.Invoke(); + start = DateTime.UtcNow; - for(ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - while(leadOutExtents.Contains(i)) - { - i++; - } - - if((long)i > lastSector) - break; - - uint firstSectorToRead = (uint)i; - - if((lastSector + 1) - (long)i < _maximumReadable) - _maximumReadable = (uint)((lastSector + 1) - (long)i); - - blocksToRead = 0; - bool inData = nextData; - - for(ulong j = i; j < i + _maximumReadable; j++) - { - if(nextData) - { - if(audioExtents.Contains(j)) - { - nextData = false; - - break; - } - - blocksToRead++; - } - else - { - if(!audioExtents.Contains(j)) - { - nextData = true; - - break; - } - - blocksToRead++; - } - } - - if(blocksToRead == 1) - blocksToRead = 2; - - if(_fixOffset && !inData) - { - // 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 - - // ReSharper disable CompareOfFloatsByEqualityOperator - if(currentSpeed > maxSpeed && - currentSpeed != 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed != 0) - minSpeed = currentSpeed; - - // ReSharper restore CompareOfFloatsByEqualityOperator - - #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator - - 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, 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, blocksToRead, false, _dev.Timeout, out cmdDuration); - } - else if(read12) - { - sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead, - blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration); - } - else if(read10) - { - sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead, - blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration); - } - else if(read6) - { - sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead, - _dev.Timeout, out cmdDuration); - } - - // Because one block has been partially used to fix the offset - if(_fixOffset && - !inData && - offsetBytes != 0) - { - int offsetFix = offsetBytes < 0 ? offsetFix = (int)(sectorSize - (offsetBytes * -1)) - : offsetFix = offsetBytes; - - if(supportedSubchannel != MmcSubchannel.None) - { - // Deinterleave subchannel - byte[] data = new byte[sectorSize * blocksToRead]; - byte[] sub = new byte[subSize * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); - } - - tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)]; - Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length); - data = tmpBuf; - - blocksToRead -= (uint)sectorsForOffset; - - // Reinterleave subchannel - cmdBuf = new byte[blockSize * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize); - Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize); - } - } - else - { - tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)]; - Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length); - cmdBuf = tmpBuf; - blocksToRead -= (uint)sectorsForOffset; - } - } - - if(!sense && - !_dev.Error) - { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - extents.Add(i, blocksToRead, true); - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize * blocksToRead]; - byte[] sub = new byte[subSize * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - { - 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, blocksToRead); - _outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel); - } - else - { - if(supportsLongSectors) - { - _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead); - } - else - { - if(cmdBuf.Length % sectorSize == 0) - { - byte[] data = new byte[2048 * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048); - - _outputPlugin.WriteSectors(data, i, blocksToRead); - } - else - { - _outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead); - } - } - } - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - } - else - { - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - _outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip); - - _outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, _skip, - SectorTagType.CdSectorSubchannel); - } - else - { - if(supportsLongSectors) - { - _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); - } - else - { - if(cmdBuf.Length % sectorSize == 0) - _outputPlugin.WriteSectors(new byte[2048 * _skip], i, _skip); - else - _outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); - } - } - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed < 1) - continue; - - currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - EndProgress?.Invoke(); + ReadCdData(audioExtents, blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, + ref imageWriteDuration, lastSector, leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, + out newTrim, tracks[0].TrackType != TrackType.Audio, offsetBytes, read6, read10, read12, read16, + readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, ref totalDuration); // TODO: Enable when underlying images support lead-outs /* diff --git a/DiscImageChef.Core/DiscImageChef.Core.csproj b/DiscImageChef.Core/DiscImageChef.Core.csproj index f6cda9ec6..922572b24 100644 --- a/DiscImageChef.Core/DiscImageChef.Core.csproj +++ b/DiscImageChef.Core/DiscImageChef.Core.csproj @@ -47,6 +47,7 @@ +