// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : LeadOuts.cs // Author(s) : Natalia Portillo // // Component : CompactDisc dumping. // // --[ Description ] ---------------------------------------------------------- // // Dumps CompactDisc Lead-Out. // // --[ 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-2022 Natalia Portillo // ****************************************************************************/ // ReSharper disable JoinDeclarationAndInitializer // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope namespace Aaru.Core.Devices.Dumping; using System; using System.Collections.Generic; using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Extents; using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Aaru.Core.Logging; using Aaru.Devices; using Schemas; partial class Dump { /// Dumps inter-session lead-outs /// Size of the read sector in bytes /// Current read speed /// Current dump hardware try /// Extents /// IMGBurn log /// Duration of image write /// Lead-out extents /// Maximum speed /// MHDD log /// Minimum speed /// Device supports READ(6) /// Device supports READ(10) /// Device supports READ(12) /// Device supports READ(16) /// Device supports READ CD /// Drive's maximum supported subchannel /// Subchannel size in bytes /// Total commands duration /// Disc tracks /// Subchannel log /// Subchannel desired to save /// List of disc ISRCs /// Disc media catalogue number /// List of subchannels not yet dumped correctly /// List of smallest pregap relative address per track void DumpCdLeadOuts(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, MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary isrcs, ref string mcn, Track[] tracks, HashSet subchannelExtents, Dictionary smallestPregapLbaPerTrack) { byte[] cmdBuf = null; // Data buffer const uint sectorSize = 2352; // Full sector size var sense = true; // Sense indicator byte[] senseBuf = null; var outputOptical = _outputPlugin as IWritableOpticalImage; UpdateStatus?.Invoke("Reading lead-outs"); _dumpLog.WriteLine("Reading lead-outs"); InitProgress?.Invoke(); foreach((ulong item1, ulong item2) in leadOutExtents.ToArray()) for(ulong i = item1; i <= item2; i++) { if(_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } double cmdDuration = 0; if(currentSpeed > maxSpeed && currentSpeed > 0) maxSpeed = currentSpeed; if(currentSpeed < minSpeed && currentSpeed > 0) minSpeed = currentSpeed; PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)"); if(readcd) { sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 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, 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, 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, blockSize, 0, 1, _dev.Timeout, out cmdDuration); else if(read6) sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration); if(!sense && !_dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); extents.Add(i, _maximumReadable, true); leadOutExtents.Remove(i); DateTime writeStart = DateTime.Now; if(supportedSubchannel != MmcSubchannel.None) { var data = new byte[sectorSize * _maximumReadable]; var sub = new byte[subSize * _maximumReadable]; for(var b = 0; b < _maximumReadable; b++) { Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, sectorSize * b, sectorSize); Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize); } outputOptical.WriteSectorsLong(data, i, _maximumReadable); bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); // Set tracks and go back if(indexesChanged) { outputOptical.SetTracks(tracks.ToList()); i--; continue; } } else outputOptical.WriteSectors(cmdBuf, i, _maximumReadable); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else { _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); // TODO: Reset device after X errors if(_stopOnError) return; // TODO: Return more cleanly // Write empty data DateTime writeStart = DateTime.Now; if(supportedSubchannel != MmcSubchannel.None) { outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1); outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1, SectorTagType.CdSectorSubchannel); } else outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000); if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; _resume.NextBlock = i + 1; } EndProgress?.Invoke(); } /// Retries inter-session lead-outs /// Size of the read sector in bytes /// Current read speed /// Current dump hardware try /// Extents /// IMGBurn log /// Duration of image write /// Lead-out extents /// Maximum speed /// MHDD log /// Minimum speed /// Device supports READ(6) /// Device supports READ(10) /// Device supports READ(12) /// Device supports READ(16) /// Device supports READ CD /// Drive's maximum supported subchannel /// Subchannel size in bytes /// Total commands duration /// Disc tracks /// Subchannel log /// Subchannel desired to save /// List of disc ISRCs /// Disc media catalogue number /// List of subchannels not yet dumped correctly /// List of smallest pregap relative address per track void RetryCdLeadOuts(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, MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary isrcs, ref string mcn, Track[] tracks, HashSet subchannelExtents, Dictionary smallestPregapLbaPerTrack) { byte[] cmdBuf = null; // Data buffer const uint sectorSize = 2352; // Full sector size var sense = true; // Sense indicator byte[] senseBuf = null; var outputOptical = _outputPlugin as IWritableOpticalImage; _dumpLog.WriteLine("Retrying lead-outs"); InitProgress?.Invoke(); foreach((ulong item1, ulong item2) in leadOutExtents.ToArray()) for(ulong i = item1; i <= item2; i++) { if(_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); _dumpLog.WriteLine("Aborted!"); break; } double cmdDuration = 0; if(currentSpeed > maxSpeed && currentSpeed > 0) maxSpeed = currentSpeed; if(currentSpeed < minSpeed && currentSpeed > 0) minSpeed = currentSpeed; PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)"); if(readcd) { sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 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, 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, 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, blockSize, 0, 1, _dev.Timeout, out cmdDuration); else if(read6) sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, out cmdDuration); if(!sense && !_dev.Error) { mhddLog.Write(i, cmdDuration); ibgLog.Write(i, currentSpeed * 1024); extents.Add(i, _maximumReadable, true); leadOutExtents.Remove(i); DateTime writeStart = DateTime.Now; if(supportedSubchannel != MmcSubchannel.None) { var data = new byte[sectorSize * _maximumReadable]; var sub = new byte[subSize * _maximumReadable]; for(var b = 0; b < _maximumReadable; b++) { Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, sectorSize * b, sectorSize); Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize); } outputOptical.WriteSectorsLong(data, i, _maximumReadable); bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); // Set tracks and go back if(indexesChanged) { outputOptical.SetTracks(tracks.ToList()); i--; continue; } } else outputOptical.WriteSectors(cmdBuf, i, _maximumReadable); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else { _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); // TODO: Reset device after X errors if(_stopOnError) return; // TODO: Return more cleanly // Write empty data DateTime writeStart = DateTime.Now; if(supportedSubchannel != MmcSubchannel.None) { outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1); if(desiredSubchannel != MmcSubchannel.None) outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1, SectorTagType.CdSectorSubchannel); } else outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); ibgLog.Write(i, 0); } double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000); if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed; } EndProgress?.Invoke(); } }