Files
Aaru/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs

1214 lines
54 KiB
C#
Raw Normal View History

// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Filename : Dump.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
2020-03-11 21:56:55 +00:00
// Component : CompactDisc dumping.
//
// --[ Description ] ----------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Dumps CompactDiscs.
//
// --[ 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
2020-01-03 17:51:30 +00:00
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
2020-02-27 00:33:26 +00:00
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Console;
2020-02-29 18:03:35 +00:00
using Aaru.Core.Logging;
using Aaru.Core.Media.Detection;
2020-02-27 00:33:26 +00:00
using Aaru.Database.Models;
using Aaru.Decoders.CD;
using Aaru.Devices;
using Schemas;
2020-02-27 00:33:26 +00:00
using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
using TrackType = Aaru.CommonTypes.Enums.TrackType;
using Version = Aaru.CommonTypes.Interop.Version;
2019-12-14 18:17:17 +00:00
// ReSharper disable JoinDeclarationAndInitializer
2019-12-14 18:17:17 +00:00
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
2020-02-27 00:33:26 +00:00
namespace Aaru.Core.Devices.Dumping
{
/// <summary>Implement dumping Compact Discs</summary>
2020-01-01 21:50:49 +00:00
// TODO: Barcode
2019-04-19 18:54:25 +01:00
partial class Dump
{
/// <summary>Dumps a compact disc</summary>
void CompactDisc()
2019-12-14 14:33:23 +00:00
{
ExtentsULong audioExtents; // Extents with audio sectors
ulong blocks; // Total number of positive sectors
uint blockSize; // Size of the read sector in bytes
CdOffset cdOffset; // Read offset from database
byte[] cmdBuf; // Data buffer
DumpHardwareType currentTry = null; // Current dump hardware try
double currentSpeed = 0; // Current read speed
int? discOffset = null; // Disc write offset
DateTime dumpStart = DateTime.UtcNow; // Time of dump start
DateTime end; // Time of operation end
ExtentsULong extents = null; // Extents
bool hiddenData; // Hidden track is data
IbgLog ibgLog; // IMGBurn log
double imageWriteDuration = 0; // Duration of image write
long lastSector; // Last sector number
var leadOutExtents = new ExtentsULong(); // Lead-out extents
Dictionary<int, long> leadOutStarts = new Dictionary<int, long>(); // Lead-out starts
double maxSpeed = double.MinValue; // Maximum speed
MhddLog mhddLog; // MHDD log
double minSpeed = double.MaxValue; // Minimum speed
bool newTrim; // Is trim a new one?
int offsetBytes = 0; // Read offset
bool read6 = false; // Device supports READ(6)
bool read10 = false; // Device supports READ(10)
bool read12 = false; // Device supports READ(12)
bool read16 = false; // Device supports READ(16)
bool readcd; // Device supports READ CD
bool ret; // Image writing return status
const uint sectorSize = 2352; // Full sector size
int sectorsForOffset = 0; // Sectors needed to fix offset
bool sense = true; // Sense indicator
int sessions; // Number of sessions in disc
DateTime start; // Start of operation
SubchannelLog subLog = null; // Subchannel log
uint subSize; // Subchannel size in bytes
TrackSubchannelType subType; // Track subchannel type
bool supportsLongSectors = true; // Supports reading EDC and ECC
bool supportsPqSubchannel; // Supports reading PQ subchannel
bool supportsRwSubchannel; // Supports reading RW subchannel
byte[] tmpBuf; // Temporary buffer
FullTOC.CDFullTOC? toc; // Full CD TOC
double totalDuration = 0; // Total commands duration
Dictionary<byte, byte> trackFlags = new Dictionary<byte, byte>(); // Track flags
Track[] tracks; // Tracks in disc
int firstTrackLastSession; // Number of first track in last session
bool hiddenTrack; // Disc has a hidden track before track 1
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
MmcSubchannel desiredSubchannel; // User requested subchannel
2020-06-13 19:15:27 +01:00
bool bcdSubchannel = false; // Subchannel positioning is in BCD
Dictionary<byte, string> isrcs = new Dictionary<byte, string>();
string mcn = null;
HashSet<int> subchannelExtents = new HashSet<int>();
2019-12-25 18:07:05 +00:00
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); // Media tags
MediaType dskType = MediaType.CD;
2019-12-14 18:57:54 +00:00
2019-12-25 18:07:05 +00:00
if(_dumpRaw)
2019-12-14 14:33:23 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Raw CD dumping not yet implemented");
2019-12-14 14:33:23 +00:00
StoppingErrorMessage?.Invoke("Raw CD dumping not yet implemented");
return;
}
// Check subchannels support
supportsPqSubchannel = SupportsPqSubchannel(_dev, _dumpLog, UpdateStatus);
supportsRwSubchannel = SupportsRwSubchannel(_dev, _dumpLog, UpdateStatus);
if(supportsRwSubchannel)
supportedSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
supportedSubchannel = MmcSubchannel.Q16;
else
supportedSubchannel = MmcSubchannel.None;
switch(_subchannel)
{
case DumpSubchannel.Any:
if(supportsRwSubchannel)
desiredSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
desiredSubchannel = MmcSubchannel.Q16;
else
desiredSubchannel = MmcSubchannel.None;
2019-12-14 19:37:52 +00:00
break;
case DumpSubchannel.Rw:
if(supportsRwSubchannel)
desiredSubchannel = MmcSubchannel.Raw;
else
{
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
2019-12-14 19:37:52 +00:00
StoppingErrorMessage?.
Invoke("Drive does not support the requested subchannel format, not continuing...");
2019-12-14 19:37:52 +00:00
return;
}
2019-12-14 19:37:52 +00:00
break;
case DumpSubchannel.RwOrPq:
if(supportsRwSubchannel)
desiredSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
desiredSubchannel = MmcSubchannel.Q16;
else
{
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
2019-12-14 19:37:52 +00:00
StoppingErrorMessage?.
Invoke("Drive does not support the requested subchannel format, not continuing...");
2019-12-14 19:37:52 +00:00
return;
}
break;
case DumpSubchannel.Pq:
if(supportsPqSubchannel)
desiredSubchannel = MmcSubchannel.Q16;
else
{
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
StoppingErrorMessage?.
Invoke("Drive does not support the requested subchannel format, not continuing...");
return;
}
break;
case DumpSubchannel.None:
desiredSubchannel = MmcSubchannel.None;
break;
default: throw new ArgumentOutOfRangeException();
}
if(desiredSubchannel == MmcSubchannel.Q16 && supportsPqSubchannel)
supportedSubchannel = MmcSubchannel.Q16;
// Check if output format supports subchannels
if(!_outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) &&
desiredSubchannel != MmcSubchannel.None)
{
if(_force || _subchannel == DumpSubchannel.None)
{
_dumpLog.WriteLine("Output format does not support subchannels, continuing...");
UpdateStatus?.Invoke("Output format does not support subchannels, continuing...");
2019-12-14 19:37:52 +00:00
}
else
{
_dumpLog.WriteLine("Output format does not support subchannels, not continuing...");
StoppingErrorMessage?.Invoke("Output format does not support subchannels, not continuing...");
return;
}
desiredSubchannel = MmcSubchannel.None;
}
switch(supportedSubchannel)
{
case MmcSubchannel.None:
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Checking if drive supports reading without subchannel...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Checking if drive supports reading without subchannel...");
readcd = !_dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 1, MmcSectorTypes.AllTypes, false, false,
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
2019-12-25 18:07:05 +00:00
supportedSubchannel, _dev.Timeout, out _);
2019-12-14 19:37:52 +00:00
if(!readcd)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Drive does not support READ CD, trying SCSI READ commands...");
2019-12-14 19:37:52 +00:00
ErrorMessage?.Invoke("Drive does not support READ CD, trying SCSI READ commands...");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Checking if drive supports READ(6)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Checking if drive supports READ(6)...");
read6 = !_dev.Read6(out cmdBuf, out _, 0, 2048, 1, _dev.Timeout, out _);
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Checking if drive supports READ(10)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Checking if drive supports READ(10)...");
read10 = !_dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1,
2019-12-25 18:07:05 +00:00
_dev.Timeout, out _);
2019-12-14 19:37:52 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Checking if drive supports READ(12)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Checking if drive supports READ(12)...");
read12 = !_dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, 2048, 0, 1, false,
_dev.Timeout, out _);
2019-12-14 19:37:52 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Checking if drive supports READ(16)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Checking if drive supports READ(16)...");
read16 = !_dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, 2048, 0, 1, false,
2019-12-25 18:07:05 +00:00
_dev.Timeout, out _);
2019-12-14 19:37:52 +00:00
if(!read6 &&
!read10 &&
!read12 &&
!read16)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Cannot read from disc, not continuing...");
2019-12-14 19:37:52 +00:00
StoppingErrorMessage?.Invoke("Cannot read from disc, not continuing...");
return;
}
if(read6)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Drive supports READ(6)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Drive supports READ(6)...");
}
if(read10)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Drive supports READ(10)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Drive supports READ(10)...");
}
if(read12)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Drive supports READ(12)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Drive supports READ(12)...");
}
if(read16)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Drive supports READ(16)...");
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke("Drive supports READ(16)...");
}
}
_dumpLog.WriteLine("Drive can read without subchannel...");
UpdateStatus?.Invoke("Drive can read without subchannel...");
2019-12-14 19:37:52 +00:00
subSize = 0;
2019-12-14 19:51:52 +00:00
subType = TrackSubchannelType.None;
break;
case MmcSubchannel.Raw:
_dumpLog.WriteLine("Full raw subchannel reading supported...");
UpdateStatus?.Invoke("Full raw subchannel reading supported...");
2019-12-14 19:51:52 +00:00
subType = TrackSubchannelType.Raw;
subSize = 96;
readcd = true;
2019-12-14 19:51:52 +00:00
break;
case MmcSubchannel.Q16:
_dumpLog.WriteLine("PQ subchannel reading supported...");
_dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
UpdateStatus?.Invoke("PQ subchannel reading supported...");
UpdateStatus?.
Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
2019-12-14 19:51:52 +00:00
subType = TrackSubchannelType.Q16;
subSize = 16;
readcd = true;
2019-12-14 19:51:52 +00:00
break;
default:
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel);
2019-12-14 19:51:52 +00:00
StoppingErrorMessage?.
Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting...");
return;
}
switch(desiredSubchannel)
{
case MmcSubchannel.None:
subType = TrackSubchannelType.None;
break;
case MmcSubchannel.Raw:
case MmcSubchannel.Q16:
subType = TrackSubchannelType.Raw;
break;
}
blockSize = sectorSize + subSize;
// Check if subchannel is BCD
if(supportedSubchannel != MmcSubchannel.None)
{
sense = _dev.ReadCd(out cmdBuf, out _, 35, blockSize, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel,
_dev.Timeout, out _);
if(!sense)
{
tmpBuf = new byte[subSize];
Array.Copy(cmdBuf, sectorSize, tmpBuf, 0, subSize);
if(supportedSubchannel == MmcSubchannel.Q16)
tmpBuf = Subchannel.ConvertQToRaw(tmpBuf);
tmpBuf = Subchannel.Deinterleave(tmpBuf);
// 9th Q subchannel is always FRAME when in user data area
// LBA 35 => MSF 00:02:35 => FRAME 35 (in hexadecimal 0x23)
// Sometimes drive returns a pregap here but MSF 00:02:3x => FRAME 3x (hexadecimal 0x20 to 0x27)
bcdSubchannel = (tmpBuf[21] & 0x30) > 0;
if(bcdSubchannel)
{
_dumpLog.WriteLine("Drive returns subchannel in BCD...");
UpdateStatus?.Invoke("Drive returns subchannel in BCD...");
}
else
{
_dumpLog.WriteLine("Drive does not returns subchannel in BCD...");
UpdateStatus?.Invoke("Drive does not returns subchannel in BCD...");
}
}
}
tracks = GetCdTracks(ref blockSize, _dev, dskType, _dumpLog, _force, out lastSector, leadOutStarts,
mediaTags, StoppingErrorMessage, subType, out toc, trackFlags, UpdateStatus);
2019-12-14 18:17:17 +00:00
if(tracks is null)
return;
2019-12-14 20:39:15 +00:00
_dumpLog.WriteLine("Calculating pregaps, can take some time...");
UpdateStatus?.Invoke("Calculating pregaps, can take some time...");
SolveTrackPregaps(_dev, _dumpLog, UpdateStatus, tracks, supportsPqSubchannel, supportsRwSubchannel, _dbDev,
out bool inexactPositioning);
if(inexactPositioning)
{
_dumpLog.WriteLine("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect.");
UpdateStatus?.
Invoke("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect.");
}
2020-01-01 21:50:49 +00:00
if(!(_outputPlugin as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities.
CanStorePregaps) &&
tracks.Where(track => track.TrackSequence !=
tracks.First(t => t.TrackSession == track.TrackSession).TrackSequence).
Any(track => track.TrackPregap > 0))
{
if(!_force)
{
_dumpLog.WriteLine("Output format does not support pregaps, this may end in a loss of data, not continuing...");
StoppingErrorMessage?.
Invoke("Output format does not support pregaps, this may end in a loss of data, not continuing...");
return;
}
_dumpLog.WriteLine("Output format does not support pregaps, this may end in a loss of data, continuing...");
ErrorMessage?.
Invoke("Output format does not support pregaps, this may end in a loss of data, continuing...");
}
2019-12-14 20:39:15 +00:00
for(int t = 1; t < tracks.Length; t++)
tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1;
2020-04-22 00:22:34 +01:00
tracks[^1].TrackEndSector = (ulong)lastSector;
blocks = (ulong)(lastSector + 1);
2019-12-14 20:39:15 +00:00
if(blocks == 0)
{
StoppingErrorMessage?.Invoke("Cannot dump blank media.");
return;
}
2019-12-25 18:07:05 +00:00
ResumeSupport.Process(true, true, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId,
ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private);
2019-12-14 21:27:15 +00:00
if(currentTry == null ||
extents == null)
{
StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");
return;
}
2020-01-01 16:06:56 +00:00
// Read media tags
ReadCdTags(ref dskType, mediaTags, out sessions, out firstTrackLastSession);
2019-12-14 20:56:39 +00:00
// Check if output format supports all disc tags we have retrieved so far
foreach(MediaTagType tag in mediaTags.Keys)
{
2019-12-25 18:07:05 +00:00
if(_outputPlugin.SupportedMediaTags.Contains(tag))
continue;
if(_force)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format does not support {0}, continuing...", tag);
ErrorMessage?.Invoke($"Output format does not support {tag}, continuing...");
}
else
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format does not support {0}, not continuing...", tag);
StoppingErrorMessage?.Invoke($"Output format does not support {tag}, not continuing...");
return;
}
}
2019-12-14 20:56:39 +00:00
if(leadOutStarts.Any())
{
UpdateStatus?.Invoke("Solving lead-outs...");
foreach(KeyValuePair<int, long> leadOuts in leadOutStarts)
for(int i = 0; i < tracks.Length; i++)
{
if(tracks[i].TrackSession != leadOuts.Key)
continue;
if(tracks[i].TrackEndSector >= (ulong)leadOuts.Value)
tracks[i].TrackEndSector = (ulong)leadOuts.Value - 1;
}
var dataExtents = new ExtentsULong();
foreach(Track trk in tracks)
dataExtents.Add(trk.TrackStartSector, trk.TrackEndSector);
Tuple<ulong, ulong>[] dataExtentsArray = dataExtents.ToArray();
for(int i = 0; i < dataExtentsArray.Length - 1; i++)
leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1);
}
2020-01-01 18:06:31 +00:00
_dumpLog.WriteLine("Detecting disc type...");
UpdateStatus?.Invoke("Detecting disc type...");
2020-01-01 18:06:31 +00:00
MMC.DetectDiscType(ref dskType, sessions, toc, _dev, out hiddenTrack, out hiddenData,
firstTrackLastSession);
2019-12-14 20:05:30 +00:00
2020-01-01 18:06:31 +00:00
if(hiddenTrack)
2019-12-14 20:05:30 +00:00
{
2020-01-01 18:06:31 +00:00
_dumpLog.WriteLine("Disc contains a hidden track...");
UpdateStatus?.Invoke("Disc contains a hidden track...");
2019-12-14 20:05:30 +00:00
2020-01-01 18:06:31 +00:00
List<Track> trkList = new List<Track>
2019-12-14 20:05:30 +00:00
{
2020-01-01 18:06:31 +00:00
new Track
2019-12-14 20:05:30 +00:00
{
TrackSequence = 0, TrackSession = 1, TrackType = hiddenData ? TrackType.Data : TrackType.Audio,
TrackStartSector = 0, TrackBytesPerSector = (int)sectorSize,
2020-01-01 18:06:31 +00:00
TrackRawBytesPerSector = (int)sectorSize, TrackSubchannelType = subType,
TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1
2019-12-14 20:05:30 +00:00
}
2020-01-01 18:06:31 +00:00
};
2019-12-14 21:09:16 +00:00
2020-01-01 18:06:31 +00:00
trkList.AddRange(tracks);
tracks = trkList.ToArray();
2019-12-14 20:05:30 +00:00
}
if(tracks.Any(t => t.TrackType == TrackType.Audio) &&
desiredSubchannel != MmcSubchannel.Raw)
{
_dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
UpdateStatus?.
Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
}
// Check mode for tracks
for(int t = 0; t < tracks.Length; t++)
{
if(!readcd)
{
tracks[t].TrackType = TrackType.CdMode1;
continue;
}
if(tracks[t].TrackType == TrackType.Audio)
continue;
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Checking mode for track {0}...", tracks[t].TrackSequence);
UpdateStatus?.Invoke($"Checking mode for track {tracks[t].TrackSequence}...");
sense = _dev.ReadCd(out cmdBuf, out _, (uint)(tracks[t].TrackStartSector + tracks[t].TrackPregap),
blockSize, 1, MmcSectorTypes.AllTypes, false, false, true,
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel,
_dev.Timeout, out _);
if(sense)
2017-06-07 22:37:05 +01:00
{
_dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", tracks[t].TrackSequence);
UpdateStatus?.Invoke($"Unable to guess mode for track {tracks[t].TrackSequence}, continuing...");
continue;
2017-06-07 22:37:05 +01:00
}
2019-12-14 19:37:52 +00:00
switch(cmdBuf[15])
2017-06-07 22:37:05 +01:00
{
case 1:
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE1");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Track {0} is MODE1", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode1;
break;
case 2:
if(dskType == MediaType.CDI ||
dskType == MediaType.CDIREADY)
{
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Track {0} is MODE2", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Formless;
break;
}
2019-12-14 19:37:52 +00:00
if((cmdBuf[0x012] & 0x20) == 0x20) // mode 2 form 2
{
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 2");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Track {0} is MODE2 FORM 2", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Form2;
break;
}
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 1");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Track {0} is MODE2 FORM 1", tracks[t].TrackSequence);
tracks[t].TrackType = TrackType.CdMode2Form1;
// These media type specifications do not legally allow mode 2 tracks to be present
if(dskType == MediaType.CDROM ||
dskType == MediaType.CDPLUS ||
dskType == MediaType.CDV)
dskType = MediaType.CD;
break;
default:
2019-12-14 19:37:52 +00:00
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is unknown mode {cmdBuf[15]}");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Track {0} is unknown mode {1}", tracks[t].TrackSequence, cmdBuf[15]);
break;
2017-06-07 22:37:05 +01:00
}
}
2019-12-25 18:07:05 +00:00
if(_outputPlugin.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000"))
{
if(tracks.Length > 1)
{
StoppingErrorMessage?.Invoke("Output format does not support more than 1 track, not continuing...");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format does not support more than 1 track, not continuing...");
return;
}
if(tracks.Any(t => t.TrackType == TrackType.Audio))
{
StoppingErrorMessage?.Invoke("Output format does not support audio tracks, not continuing...");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format does not support audio tracks, not continuing...");
return;
}
if(tracks.Any(t => t.TrackType != TrackType.CdMode1))
{
StoppingErrorMessage?.Invoke("Output format only supports MODE 1 tracks, not continuing...");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format only supports MODE 1 tracks, not continuing...");
return;
}
supportsLongSectors = false;
}
2017-12-19 20:33:03 +00:00
2018-12-31 21:16:52 +00:00
// Check if something prevents from dumping the first track pregap
2019-12-25 18:07:05 +00:00
if(_dumpFirstTrackPregap && readcd)
{
2019-12-25 18:07:05 +00:00
if(_dev.PlatformId == PlatformID.FreeBSD &&
!_dev.IsRemote)
{
2019-12-25 18:07:05 +00:00
if(_force)
{
2019-12-25 18:07:05 +00:00
_dumpLog.
WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing");
ErrorMessage?.
Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing");
}
else
{
2019-12-25 18:07:05 +00:00
_dumpLog.
WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing");
StoppingErrorMessage?.
Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing");
return;
}
2019-12-25 18:07:05 +00:00
_dumpFirstTrackPregap = false;
}
2019-12-25 18:07:05 +00:00
if(!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap))
{
2019-12-25 18:07:05 +00:00
if(_force)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format does not support CD first track pregap, continuing...");
ErrorMessage?.Invoke("Output format does not support CD first track pregap, continuing...");
}
else
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Output format does not support CD first track pregap, not continuing...");
StoppingErrorMessage?.
Invoke("Output format does not support CD first track pregap, not continuing...");
return;
}
2019-12-25 18:07:05 +00:00
_dumpFirstTrackPregap = false;
}
}
// Try how many blocks are readable at once
while(true)
{
if(readcd)
{
sense = _dev.ReadCd(out cmdBuf, out _, 0, blockSize, _maximumReadable, MmcSectorTypes.AllTypes,
false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
supportedSubchannel, _dev.Timeout, out _);
2019-12-25 18:07:05 +00:00
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read16)
{
sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, 0, blockSize, 0, _maximumReadable,
false, _dev.Timeout, out _);
2019-12-25 18:07:05 +00:00
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read12)
{
sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, 0, blockSize, 0,
_maximumReadable, false, _dev.Timeout, out _);
2019-12-25 18:07:05 +00:00
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read10)
{
sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, 0, blockSize, 0,
(ushort)_maximumReadable, _dev.Timeout, out _);
2019-12-25 18:07:05 +00:00
if(_dev.Error || sense)
_maximumReadable /= 2;
}
else if(read6)
{
sense = _dev.Read6(out cmdBuf, out _, 0, blockSize, (byte)_maximumReadable, _dev.Timeout, out _);
2019-12-25 18:07:05 +00:00
if(_dev.Error || sense)
_maximumReadable /= 2;
}
2019-12-25 18:07:05 +00:00
if(!_dev.Error ||
_maximumReadable == 1)
break;
}
2019-12-25 18:07:05 +00:00
if(_dev.Error || sense)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError);
StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");
}
2019-12-14 21:38:18 +00:00
// Try to read the first track pregap
if(_dumpFirstTrackPregap && readcd)
ReadCdFirstTrackPregap(blockSize, ref currentSpeed, mediaTags, supportedSubchannel, ref totalDuration);
_dumpLog.WriteLine("Reading {0} sectors at a time.", _maximumReadable);
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
_dumpLog.WriteLine("Device can read {0} blocks at a time.", _maximumReadable);
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
_dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
_dumpLog.WriteLine("Media identified as {0}.", dskType);
2019-12-14 21:38:18 +00:00
UpdateStatus?.Invoke($"Reading {_maximumReadable} sectors at a time.");
2019-12-14 21:38:18 +00:00
UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
UpdateStatus?.Invoke($"Device can read {_maximumReadable} blocks at a time.");
2019-12-14 21:38:18 +00:00
UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
2019-12-25 18:07:05 +00:00
UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
2019-12-14 21:38:18 +00:00
UpdateStatus?.Invoke($"Media identified as {dskType}.");
2019-12-14 21:41:18 +00:00
2019-12-25 18:07:05 +00:00
ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks,
supportsLongSectors ? blockSize : 2048);
2019-12-14 21:41:18 +00:00
// Cannot create image
if(!ret)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Error creating output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
2019-12-14 21:41:18 +00:00
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
2019-12-25 18:07:05 +00:00
_outputPlugin.ErrorMessage);
2019-12-14 21:41:18 +00:00
}
2019-12-14 21:41:43 +00:00
2019-12-14 22:55:26 +00:00
// Send track list to output plugin. This may fail if subchannel is set but unsupported.
2019-12-25 18:07:05 +00:00
ret = (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
2019-12-14 21:41:43 +00:00
if(!ret &&
desiredSubchannel == MmcSubchannel.None)
2019-12-14 21:41:43 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
2019-12-14 21:41:43 +00:00
StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing." +
Environment.NewLine + _outputPlugin.ErrorMessage);
2019-12-14 21:41:43 +00:00
return;
}
// If a subchannel is supported, check if output plugin allows us to write it.
if(desiredSubchannel != MmcSubchannel.None &&
!(_outputPlugin as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities.
CanStoreSubchannelRw))
{
_dumpLog.WriteLine("Output image does not support subchannels, {0}continuing...", _force ? "" : "not ");
2017-11-20 05:07:16 +00:00
if(_force)
ErrorMessage?.Invoke("Output image does not support subchannels, continuing...");
else
{
StoppingErrorMessage?.Invoke("Output image does not support subchannels, not continuing...");
return;
}
}
2019-12-14 21:45:23 +00:00
if(supportedSubchannel != MmcSubchannel.None)
{
_dumpLog.WriteLine($"Creating subchannel log in {_outputPrefix + ".sub.log"}");
subLog = new SubchannelLog(_outputPrefix + ".sub.log", bcdSubchannel);
}
2019-12-14 21:45:23 +00:00
// Set track flags
foreach(KeyValuePair<byte, byte> kvp in trackFlags)
{
Track track = tracks.FirstOrDefault(t => t.TrackSequence == kvp.Key);
2020-06-21 14:49:25 +01:00
if(track is null)
2019-12-14 21:45:23 +00:00
continue;
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Setting flags for track {0}...", track.TrackSequence);
2019-12-14 21:45:23 +00:00
UpdateStatus?.Invoke($"Setting flags for track {track.TrackSequence}...");
2019-12-25 18:07:05 +00:00
_outputPlugin.WriteSectorTag(new[]
2019-12-14 21:45:23 +00:00
{
kvp.Value
}, kvp.Key, SectorTagType.CdTrackFlags);
2019-12-14 21:45:23 +00:00
}
2019-12-14 21:46:58 +00:00
// Set MCN
if(supportedSubchannel == MmcSubchannel.None)
2019-12-14 21:46:58 +00:00
{
sense = _dev.ReadMcn(out mcn, out _, out _, _dev.Timeout, out _);
if(!sense &&
mcn != null &&
mcn != "0000000000000")
{
UpdateStatus?.Invoke($"Found Media Catalogue Number: {mcn}");
_dumpLog.WriteLine("Found Media Catalogue Number: {0}", mcn);
}
else
mcn = null;
2019-12-14 21:46:58 +00:00
}
2019-12-14 21:47:26 +00:00
// Set ISRCs
if(supportedSubchannel == MmcSubchannel.None)
foreach(Track trk in tracks)
{
sense = _dev.ReadIsrc((byte)trk.TrackSequence, out string isrc, out _, out _, _dev.Timeout, out _);
2019-12-14 21:47:26 +00:00
if(sense ||
isrc == null ||
isrc == "000000000000")
continue;
2019-12-14 21:47:26 +00:00
isrcs[(byte)trk.TrackSequence] = isrc;
UpdateStatus?.Invoke($"Found ISRC for track {trk.TrackSequence}: {mcn}");
_dumpLog.WriteLine($"Found ISRC for track {trk.TrackSequence}: {mcn}");
}
2019-12-14 21:51:51 +00:00
2020-06-13 19:15:27 +01:00
if(supportedSubchannel != MmcSubchannel.None &&
desiredSubchannel != MmcSubchannel.None)
{
subchannelExtents = new HashSet<int>();
2020-06-13 19:15:27 +01:00
_resume.BadSubchannels ??= new List<int>();
foreach(int sub in _resume.BadSubchannels)
subchannelExtents.Add(sub);
if(_resume.NextBlock < blocks)
for(ulong i = _resume.NextBlock; i < blocks; i++)
subchannelExtents.Add((int)i);
2020-06-13 19:15:27 +01:00
}
2019-12-25 18:07:05 +00:00
if(_resume.NextBlock > 0)
2019-12-14 21:51:51 +00:00
{
2019-12-25 18:07:05 +00:00
UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
2019-12-14 21:51:51 +00:00
}
if(_skip < _maximumReadable)
_skip = _maximumReadable;
2019-12-14 21:51:51 +00:00
#if DEBUG
foreach(Track trk in tracks)
UpdateStatus?.
Invoke($"Track {trk.TrackSequence} starts at LBA {trk.TrackStartSector} and ends at LBA {trk.TrackEndSector}");
#endif
if(dskType == MediaType.CDIREADY &&
!_skipCdireadyHole)
2019-12-14 21:51:51 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
2019-12-14 21:51:51 +00:00
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)
{
2020-01-06 22:29:01 +00:00
if(tracks.All(t => t.TrackType != TrackType.Audio))
{
// No audio tracks so no need to fix offset
_dumpLog.WriteLine("No audio tracks, disabling offset fix.");
UpdateStatus.Invoke("No audio tracks, disabling offset fix.");
_fixOffset = false;
}
if(!readcd)
{
_dumpLog.WriteLine("READ CD command is not supported, disabling offset fix. Dump may not be correct.");
UpdateStatus?.
Invoke("READ CD command is not supported, disabling offset fix. Dump may not be correct.");
_fixOffset = false;
}
}
else if(tracks.Any(t => t.TrackType == TrackType.Audio))
{
_dumpLog.WriteLine("There are audio tracks and offset fixing is disabled, dump may not be correct.");
UpdateStatus?.Invoke("There are audio tracks and offset fixing is disabled, dump may not be correct.");
}
2020-01-06 22:29:01 +00:00
// Search for read offset in master database
cdOffset = _ctx.CdOffsets.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model);
Media.Info.CompactDisc.GetOffset(cdOffset, _dbDev, _debug, _dev, dskType, _dumpLog, tracks, UpdateStatus,
out int? driveOffset, out int? combinedOffset, out _supportsPlextorD8);
2020-01-06 22:29:01 +00:00
if(combinedOffset is null)
{
if(driveOffset is null)
{
_dumpLog.WriteLine("Drive reading offset not found in database.");
UpdateStatus?.Invoke("Drive reading offset not found in database.");
_dumpLog.WriteLine("Disc offset cannot be calculated.");
UpdateStatus?.Invoke("Disc offset cannot be calculated.");
if(tracks.Any(t => t.TrackType == TrackType.Audio))
{
_dumpLog.WriteLine("Dump may not be correct.");
UpdateStatus?.Invoke("Dump may not be correct.");
}
if(_fixOffset)
_fixOffset = false;
}
else
{
_dumpLog.WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples).");
UpdateStatus?.Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples).");
_dumpLog.WriteLine("Disc write offset is unknown, dump may not be correct.");
UpdateStatus?.Invoke("Disc write offset is unknown, dump may not be correct.");
offsetBytes = driveOffset.Value;
sectorsForOffset = offsetBytes / (int)sectorSize;
if(sectorsForOffset < 0)
sectorsForOffset *= -1;
if(offsetBytes % sectorSize != 0)
sectorsForOffset++;
}
}
else
{
offsetBytes = combinedOffset.Value;
sectorsForOffset = offsetBytes / (int)sectorSize;
if(sectorsForOffset < 0)
sectorsForOffset *= -1;
if(offsetBytes % sectorSize != 0)
sectorsForOffset++;
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).");
2020-01-06 23:41:56 +00:00
discOffset = offsetBytes - driveOffset;
2020-01-06 22:29:01 +00:00
_dumpLog.WriteLine($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)");
UpdateStatus?.Invoke($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)");
}
}
if(!_fixOffset ||
tracks.All(t => t.TrackType != TrackType.Audio))
{
offsetBytes = 0;
sectorsForOffset = 0;
}
mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, _maximumReadable, _private);
2019-12-25 18:07:05 +00:00
ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008);
audioExtents = new ExtentsULong();
foreach(Track audioTrack in tracks.Where(t => t.TrackType == TrackType.Audio))
{
audioExtents.Add(audioTrack.TrackStartSector, audioTrack.TrackEndSector);
}
// Set speed
if(_speedMultiplier >= 0)
{
_dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX for data reading" : $"{_speed}x")}.");
UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX for data reading" : $"{_speed}x")}.");
_speed *= _speedMultiplier;
if(_speed == 0 ||
_speed > 0xFFFF)
_speed = 0xFFFF;
_dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _);
}
// Start reading
start = DateTime.UtcNow;
if(dskType == MediaType.CDIREADY && _skipCdireadyHole)
{
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);
}
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,
2020-06-13 19:15:27 +01:00
tracks, subLog, desiredSubchannel, isrcs, ref mcn, subchannelExtents);
// TODO: Enable when underlying images support lead-outs
2019-12-31 21:39:18 +00:00
/*
DumpCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
supportedSubchannel, subSize, ref totalDuration, subLog, desiredSubchannel, isrcs, ref mcn, tracks);
2019-12-31 21:39:18 +00:00
*/
2019-12-14 22:18:58 +00:00
end = DateTime.UtcNow;
mhddLog.Close();
2019-12-25 18:07:05 +00:00
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
(blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);
2019-12-14 22:18:58 +00:00
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
UpdateStatus?.
Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
UpdateStatus?.
Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
2019-12-14 22:18:58 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));
2019-12-14 22:18:58 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);
2019-12-14 22:15:05 +00:00
TrimCdUserData(audioExtents, blockSize, currentTry, extents, newTrim, offsetBytes, read6, read10, read12,
read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors,
2020-06-13 19:15:27 +01:00
ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, ref mcn, subchannelExtents);
RetryCdUserData(audioExtents, blockSize, currentTry, extents, offsetBytes, readcd, sectorsForOffset,
subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, tracks, isrcs,
2020-06-13 19:15:27 +01:00
ref mcn, subchannelExtents);
foreach(Tuple<ulong, ulong> leadoutExtent in leadOutExtents.ToArray())
{
for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++)
subchannelExtents.Remove((int)e);
}
2020-06-13 19:15:27 +01:00
if(subchannelExtents.Count > 0 &&
_retryPasses > 0 &&
_retrySubchannel)
RetrySubchannel(readcd, subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel,
tracks, isrcs, ref mcn, subchannelExtents);
2017-12-19 20:33:03 +00:00
// Write media tags to image
2019-12-25 18:07:05 +00:00
if(!_aborted)
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
2017-06-07 22:37:05 +01:00
{
if(tag.Value is null)
{
2020-02-27 23:48:41 +00:00
AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key);
continue;
}
2019-12-25 18:07:05 +00:00
ret = _outputPlugin.WriteMediaTag(tag.Value, tag.Key);
2017-06-07 22:37:05 +01:00
2019-12-25 18:07:05 +00:00
if(ret || _force)
continue;
// Cannot write tag to image
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
StoppingErrorMessage?.Invoke(_outputPlugin.ErrorMessage);
2019-04-20 14:02:25 +01:00
return;
}
2019-12-25 18:07:05 +00:00
_resume.BadBlocks.Sort();
2019-12-25 18:07:05 +00:00
foreach(ulong bad in _resume.BadBlocks)
_dumpLog.WriteLine("Sector {0} could not be read.", bad);
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
2020-06-13 19:15:27 +01:00
_resume.BadSubchannels = new List<int>();
_resume.BadSubchannels.AddRange(subchannelExtents);
2020-06-13 19:15:27 +01:00
_resume.BadSubchannels.Sort();
2020-01-09 18:01:43 +00:00
// TODO: Disc ID
var metadata = new CommonTypes.Structs.ImageInfo
{
Application = "Aaru", ApplicationVersion = Version.GetVersion()
2020-01-09 18:01:43 +00:00
};
if(!_outputPlugin.SetMetadata(metadata))
ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
_outputPlugin.ErrorMessage);
2019-12-25 18:07:05 +00:00
_outputPlugin.SetDumpHardware(_resume.Tries);
2019-12-25 18:07:05 +00:00
if(_preSidecar != null)
_outputPlugin.SetCicmMetadata(_preSidecar);
foreach(KeyValuePair<byte, string> isrc in isrcs)
{
// TODO: Track tags
if(!_outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc.Value), isrc.Key,
SectorTagType.CdTrackIsrc))
continue;
UpdateStatus?.Invoke($"Setting ISRC for track {isrc.Key} to {isrc.Value}");
_dumpLog.WriteLine("Setting ISRC for track {0} to {1}", isrc.Key, isrc.Value);
}
if(mcn != null &&
_outputPlugin.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN))
{
UpdateStatus?.Invoke($"Setting disc Media Catalogue Number to {mcn}");
_dumpLog.WriteLine("Setting disc Media Catalogue Number to {0}", mcn);
}
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Closing output file.");
UpdateStatus?.Invoke("Closing output file.");
DateTime closeStart = DateTime.Now;
2019-12-25 18:07:05 +00:00
_outputPlugin.Close();
DateTime closeEnd = DateTime.Now;
UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
2017-12-19 20:33:03 +00:00
2020-05-04 03:11:40 +01:00
subLog?.Close();
2019-12-25 18:07:05 +00:00
if(_aborted)
{
2019-12-25 18:07:05 +00:00
_dumpLog.WriteLine("Aborted!");
return;
}
double totalChkDuration = 0;
if(_metadata)
2020-01-06 23:41:56 +00:00
WriteOpticalSidecar(blockSize, blocks, dskType, null, mediaTags, sessions, out totalChkDuration,
discOffset);
2019-12-14 22:02:36 +00:00
end = DateTime.UtcNow;
UpdateStatus?.Invoke("");
UpdateStatus?.
2019-12-14 22:55:26 +00:00
Invoke($"Took a total of {(end - dumpStart).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
UpdateStatus?.
2019-12-14 18:17:17 +00:00
Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
2019-12-25 18:07:05 +00:00
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
2020-06-13 19:15:27 +01:00
UpdateStatus?.Invoke($"{_resume.BadSubchannels.Count} subchannels could not be read.");
UpdateStatus?.Invoke("");
2017-06-20 05:48:09 +01:00
Statistics.AddMedia(dskType, true);
}
}
2017-12-19 20:33:03 +00:00
}