2019-12-31 19:47:18 +00:00
// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
2017-05-31 01:00:58 +01:00
// ----------------------------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Filename : Dump.cs
2017-12-19 03:50:57 +00:00
// Author(s) : Natalia Portillo <claunia@claunia.com>
2017-05-31 01:00:58 +01:00
//
2020-03-11 21:56:55 +00:00
// Component : CompactDisc dumping.
2017-05-31 01:00:58 +01:00
//
// --[ Description ] ----------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Dumps CompactDiscs.
2017-05-31 01:00:58 +01:00
//
// --[ 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
2017-05-31 01:00:58 +01:00
// ****************************************************************************/
2017-12-19 03:50:57 +00:00
2017-05-31 01:00:58 +01:00
using System ;
using System.Collections.Generic ;
2018-01-20 17:12:01 +00:00
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 ;
2018-01-20 17:12:01 +00:00
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
2019-12-14 17:48:32 +00:00
// ReSharper disable JoinDeclarationAndInitializer
2019-12-14 18:17:17 +00:00
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
2017-05-31 01:00:58 +01:00
2020-02-27 00:33:26 +00:00
namespace Aaru.Core.Devices.Dumping
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
/// <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
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
/// <summary>Dumps a compact disc</summary>
2020-01-30 23:04:37 +00:00
void CompactDisc ( )
2019-12-14 14:33:23 +00:00
{
2020-05-05 20:26:18 +01: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-25 01:13:02 +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 > ( ) ;
bool cdiReadyReadAsAudio = false ;
2019-12-25 21:03:21 +00:00
2019-12-25 18:07:05 +00:00
Dictionary < MediaTagType , byte [ ] > mediaTags = new Dictionary < MediaTagType , byte [ ] > ( ) ; // Media tags
2020-07-18 20:43:37 +01:00
Dictionary < byte , int > smallestPregapLbaPerTrack = new Dictionary < byte , int > ( ) ;
2019-12-25 16:53:29 +00:00
2020-01-30 23:04:37 +00:00
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 ;
}
2019-12-14 17:48:32 +00:00
2020-01-01 15:34:03 +00:00
// Check subchannels support
2020-01-02 18:31:49 +00:00
supportsPqSubchannel = SupportsPqSubchannel ( _dev , _dumpLog , UpdateStatus ) ;
supportsRwSubchannel = SupportsRwSubchannel ( _dev , _dumpLog , UpdateStatus ) ;
2020-01-01 15:34:03 +00:00
2020-05-05 13:28:34 +01:00
if ( supportsRwSubchannel )
supportedSubchannel = MmcSubchannel . Raw ;
else if ( supportsPqSubchannel )
supportedSubchannel = MmcSubchannel . Q16 ;
else
supportedSubchannel = MmcSubchannel . None ;
2019-12-26 03:14:12 +00:00
switch ( _subchannel )
{
case DumpSubchannel . Any :
2020-01-01 15:34:03 +00:00
if ( supportsRwSubchannel )
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . Raw ;
2020-01-01 15:34:03 +00:00
else if ( supportsPqSubchannel )
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . Q16 ;
2019-12-26 03:14:12 +00:00
else
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . None ;
2019-12-14 19:37:52 +00:00
2019-12-26 03:14:12 +00:00
break ;
case DumpSubchannel . Rw :
2020-01-01 15:34:03 +00:00
if ( supportsRwSubchannel )
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . Raw ;
2019-12-26 03:14:12 +00:00
else
{
_dumpLog . WriteLine ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-14 19:37:52 +00:00
2019-12-26 03:14:12 +00:00
StoppingErrorMessage ? .
Invoke ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-14 19:37:52 +00:00
2019-12-26 03:14:12 +00:00
return ;
}
2019-12-14 19:37:52 +00:00
2019-12-26 03:14:12 +00:00
break ;
case DumpSubchannel . RwOrPq :
2020-01-01 15:34:03 +00:00
if ( supportsRwSubchannel )
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . Raw ;
2020-01-01 15:34:03 +00:00
else if ( supportsPqSubchannel )
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . Q16 ;
2019-12-26 03:14:12 +00:00
else
{
_dumpLog . WriteLine ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-14 19:37:52 +00:00
2019-12-26 03:14:12 +00:00
StoppingErrorMessage ? .
Invoke ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-14 19:37:52 +00:00
2019-12-26 03:14:12 +00:00
return ;
}
break ;
case DumpSubchannel . Pq :
2020-01-01 15:34:03 +00:00
if ( supportsPqSubchannel )
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . Q16 ;
2019-12-26 03:14:12 +00:00
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 :
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . None ;
2019-12-26 03:14:12 +00:00
break ;
default : throw new ArgumentOutOfRangeException ( ) ;
}
2020-05-05 13:28:34 +01:00
if ( desiredSubchannel = = MmcSubchannel . Q16 & & supportsPqSubchannel )
supportedSubchannel = MmcSubchannel . Q16 ;
2019-12-26 03:14:12 +00:00
// Check if output format supports subchannels
if ( ! _outputPlugin . SupportedSectorTags . Contains ( SectorTagType . CdSectorSubchannel ) & &
2020-05-05 13:28:34 +01:00
desiredSubchannel ! = MmcSubchannel . None )
2019-12-26 03:14:12 +00:00
{
2020-01-02 18:31:49 +00:00
if ( _force | | _subchannel = = DumpSubchannel . None )
2019-12-26 03:14:12 +00:00
{
_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
{
2019-12-26 03:14:12 +00:00
_dumpLog . WriteLine ( "Output format does not support subchannels, not continuing..." ) ;
StoppingErrorMessage ? . Invoke ( "Output format does not support subchannels, not continuing..." ) ;
return ;
}
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . None ;
2019-12-26 03:14:12 +00:00
}
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..." ) ;
2020-01-01 19:07:52 +00:00
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)..." ) ;
2020-01-01 19:07:52 +00:00
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)..." ) ;
2020-01-01 19:07:52 +00:00
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)..." ) ;
2020-01-01 19:07:52 +00:00
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)..." ) ;
2020-01-01 19:07:52 +00:00
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)..." ) ;
}
}
2019-12-26 03:14:12 +00:00
_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 :
2019-12-26 03:14:12 +00:00
_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 ;
2019-12-26 03:14:12 +00:00
subSize = 96 ;
readcd = true ;
2019-12-14 19:51:52 +00:00
break ;
case MmcSubchannel . Q16 :
2019-12-26 03:14:12 +00:00
_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 ;
2019-12-26 03:14:12 +00:00
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 ;
}
2020-06-14 22:53:52 +01:00
switch ( desiredSubchannel )
{
case MmcSubchannel . None :
subType = TrackSubchannelType . None ;
break ;
case MmcSubchannel . Raw :
case MmcSubchannel . Q16 :
2020-06-14 23:45:41 +01:00
subType = TrackSubchannelType . Raw ;
2020-06-14 22:53:52 +01:00
break ;
}
2019-12-26 03:14:12 +00:00
blockSize = sectorSize + subSize ;
2020-05-05 17:53:06 +01:00
// Check if subchannel is BCD
if ( supportedSubchannel ! = MmcSubchannel . None )
{
2020-05-05 18:55:40 +01:00
sense = _dev . ReadCd ( out cmdBuf , out _ , 35 , blockSize , 1 , MmcSectorTypes . AllTypes , false , false , true ,
2020-05-05 17:53:06 +01:00
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..." ) ;
}
}
}
2020-01-02 18:31:49 +00:00
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
2019-12-31 21:02:22 +00:00
if ( tracks is null )
return ;
2019-12-14 20:39:15 +00:00
2020-05-05 23:25:15 +01:00
_dumpLog . WriteLine ( "Calculating pregaps, can take some time..." ) ;
UpdateStatus ? . Invoke ( "Calculating pregaps, can take some time..." ) ;
2020-01-06 18:36:11 +00:00
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
2020-04-22 00:10:06 +01:00
if ( ! ( _outputPlugin as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
2020-07-18 20:43:37 +01:00
CanStorePregaps ) & &
2020-04-22 00:28:49 +01:00
tracks . Where ( track = > track . TrackSequence ! =
tracks . First ( t = > t . TrackSession = = track . TrackSession ) . TrackSequence ) .
Any ( track = > track . TrackPregap > 0 ) )
2020-04-22 00:10:06 +01:00
{
2020-04-22 00:28:49 +01:00
if ( ! _force )
2020-04-22 00:10:06 +01:00
{
_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 ;
}
2020-04-22 00:28:49 +01:00
_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..." ) ;
2020-04-22 00:10:06 +01:00
}
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 ,
2020-03-11 15:28:04 +00:00
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
2019-12-14 21:06:16 +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 ) )
2019-12-14 21:06:16 +00:00
continue ;
2020-01-01 23:06:21 +00:00
if ( _force )
2019-12-14 21:06:16 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Output format does not support {0}, continuing..." , tag ) ;
2019-12-14 21:06:16 +00:00
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 ) ;
2019-12-14 21:06:16 +00:00
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 ) ;
}
2019-12-14 21:03:52 +00:00
2020-01-01 18:06:31 +00:00
_dumpLog . WriteLine ( "Detecting disc type..." ) ;
UpdateStatus ? . Invoke ( "Detecting disc type..." ) ;
2019-12-14 21:03:52 +00:00
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
{
2020-04-24 13:28:49 +01: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 ,
2020-04-24 13:28:49 +01:00
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
}
2020-01-11 15:53:07 +00:00
if ( tracks . Any ( t = > t . TrackType = = TrackType . Audio ) & &
2020-05-05 13:28:34 +01:00
desiredSubchannel ! = MmcSubchannel . Raw )
2020-01-11 15:53:07 +00:00
{
_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!" ) ;
}
2018-01-20 17:12:01 +00:00
// Check mode for tracks
2019-11-18 20:59:16 +00:00
for ( int t = 0 ; t < tracks . Length ; t + + )
2018-01-20 17:12:01 +00:00
{
2019-11-18 20:59:16 +00:00
if ( ! readcd )
2018-06-19 22:17:20 +01:00
{
tracks [ t ] . TrackType = TrackType . CdMode1 ;
2019-11-18 20:59:16 +00:00
2018-06-19 22:17:20 +01:00
continue ;
}
2019-11-18 20:59:16 +00:00
if ( tracks [ t ] . TrackType = = TrackType . Audio )
continue ;
2018-06-19 22:48:51 +01:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Checking mode for track {0}..." , tracks [ t ] . TrackSequence ) ;
2019-04-19 20:13:17 +01:00
UpdateStatus ? . Invoke ( $"Checking mode for track {tracks[t].TrackSequence}..." ) ;
2018-01-20 17:12:01 +00:00
2020-05-05 23:25:15 +01:00
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 _ ) ;
2018-01-20 17:12:01 +00:00
2020-04-24 13:28:49 +01:00
if ( sense )
2017-06-07 22:37:05 +01:00
{
2020-05-05 23:25:15 +01:00
_dumpLog . WriteLine ( "Unable to guess mode for track {0}, continuing..." , tracks [ t ] . TrackSequence ) ;
2020-04-24 13:28:49 +01:00
2020-05-05 23:25:15 +01:00
UpdateStatus ? . Invoke ( $"Unable to guess mode for track {tracks[t].TrackSequence}, continuing..." ) ;
2020-04-24 13:28:49 +01:00
2020-05-05 23:25:15 +01:00
continue ;
2017-06-07 22:37:05 +01:00
}
2018-01-20 17:12:01 +00:00
2019-12-14 19:37:52 +00:00
switch ( cmdBuf [ 15 ] )
2017-06-07 22:37:05 +01:00
{
2018-01-20 17:12:01 +00:00
case 1 :
2019-04-19 20:13:17 +01:00
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 ) ;
2018-01-20 17:12:01 +00:00
tracks [ t ] . TrackType = TrackType . CdMode1 ;
2019-11-18 20:59:16 +00:00
2018-01-20 17:12:01 +00:00
break ;
case 2 :
2019-11-18 20:59:16 +00:00
if ( dskType = = MediaType . CDI | |
dskType = = MediaType . CDIREADY )
2018-06-24 10:57:58 +01:00
{
2019-04-19 20:13:17 +01:00
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 ) ;
2018-06-24 10:57:58 +01:00
tracks [ t ] . TrackType = TrackType . CdMode2Formless ;
2019-11-18 20:59:16 +00:00
2018-06-24 10:57:58 +01:00
break ;
}
2019-12-14 19:37:52 +00:00
if ( ( cmdBuf [ 0x012 ] & 0x20 ) = = 0x20 ) // mode 2 form 2
2018-06-24 10:57:58 +01:00
{
2019-04-19 20:13:17 +01:00
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 ) ;
2018-06-24 10:57:58 +01:00
tracks [ t ] . TrackType = TrackType . CdMode2Form2 ;
2019-11-18 20:59:16 +00:00
2018-06-24 10:57:58 +01:00
break ;
}
2019-04-19 20:13:17 +01:00
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 ) ;
2018-06-24 10:57:58 +01:00
tracks [ t ] . TrackType = TrackType . CdMode2Form1 ;
2019-08-15 18:35:38 +01:00
// These media type specifications do not legally allow mode 2 tracks to be present
2019-11-18 20:59:16 +00:00
if ( dskType = = MediaType . CDROM | |
dskType = = MediaType . CDPLUS | |
dskType = = MediaType . CDV )
2019-08-15 18:35:38 +01:00
dskType = MediaType . CD ;
2018-01-20 17:12:01 +00:00
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 ] ) ;
2019-11-18 20:59:16 +00:00
2018-01-20 17:12:01 +00:00
break ;
2017-06-07 22:37:05 +01:00
}
}
2019-12-14 21:13:31 +00:00
2019-12-25 18:07:05 +00:00
if ( _outputPlugin . Id = = new Guid ( "12345678-AAAA-BBBB-CCCC-123456789000" ) )
2019-12-14 21:13:31 +00:00
{
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..." ) ;
2019-12-14 21:13:31 +00:00
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..." ) ;
2019-12-14 21:13:31 +00:00
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..." ) ;
2019-12-14 21:13:31 +00:00
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 )
2017-11-29 15:14:21 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _dev . PlatformId = = PlatformID . FreeBSD & &
! _dev . IsRemote )
2018-01-20 17:12:01 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _force )
2019-04-19 20:13:17 +01:00
{
2019-12-25 18:07:05 +00:00
_dumpLog .
2019-11-18 20:59:16 +00:00
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" ) ;
2019-04-19 20:13:17 +01:00
}
else
{
2019-12-25 18:07:05 +00:00
_dumpLog .
2019-11-18 20:59:16 +00:00
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" ) ;
2019-04-19 20:13:17 +01:00
return ;
}
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
_dumpFirstTrackPregap = false ;
2018-01-20 17:12:01 +00:00
}
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
if ( ! _outputPlugin . SupportedMediaTags . Contains ( MediaTagType . CD_FirstTrackPregap ) )
2018-01-20 17:12:01 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _force )
2019-04-19 20:13:17 +01:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Output format does not support CD first track pregap, continuing..." ) ;
2019-04-19 20:13:17 +01:00
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..." ) ;
2019-11-18 20:59:16 +00:00
StoppingErrorMessage ? .
Invoke ( "Output format does not support CD first track pregap, not continuing..." ) ;
2019-04-19 20:13:17 +01:00
return ;
}
2018-01-20 17:12:01 +00:00
2019-12-25 18:07:05 +00:00
_dumpFirstTrackPregap = false ;
2018-01-20 17:12:01 +00:00
}
}
2019-01-27 17:47:40 +00:00
2018-01-20 17:12:01 +00:00
// Try how many blocks are readable at once
2019-11-18 20:59:16 +00:00
while ( true )
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
if ( readcd )
2017-05-31 01:00:58 +01:00
{
2020-01-01 19:07:52 +00:00
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-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Error | | sense )
2019-12-26 01:34:24 +00:00
_maximumReadable / = 2 ;
2017-05-31 01:00:58 +01:00
}
2019-11-18 20:59:16 +00:00
else if ( read16 )
2018-06-19 22:17:20 +01:00
{
2020-01-01 19:07:52 +00:00
sense = _dev . Read16 ( out cmdBuf , out _ , 0 , false , true , false , 0 , blockSize , 0 , _maximumReadable ,
false , _dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Error | | sense )
2019-12-26 01:34:24 +00:00
_maximumReadable / = 2 ;
2018-06-19 22:17:20 +01:00
}
2019-11-18 20:59:16 +00:00
else if ( read12 )
2018-06-19 22:17:20 +01:00
{
2020-01-01 19:07:52 +00:00
sense = _dev . Read12 ( out cmdBuf , out _ , 0 , false , true , false , false , 0 , blockSize , 0 ,
2019-12-26 01:34:24 +00:00
_maximumReadable , false , _dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Error | | sense )
2019-12-26 01:34:24 +00:00
_maximumReadable / = 2 ;
2018-06-19 22:17:20 +01:00
}
2019-11-18 20:59:16 +00:00
else if ( read10 )
2018-06-19 22:17:20 +01:00
{
2020-01-01 19:07:52 +00:00
sense = _dev . Read10 ( out cmdBuf , out _ , 0 , false , true , false , false , 0 , blockSize , 0 ,
2019-12-26 01:34:24 +00:00
( ushort ) _maximumReadable , _dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Error | | sense )
2019-12-26 01:34:24 +00:00
_maximumReadable / = 2 ;
2018-06-19 22:17:20 +01:00
}
2019-11-18 20:59:16 +00:00
else if ( read6 )
2018-06-19 22:17:20 +01:00
{
2020-01-01 19:07:52 +00:00
sense = _dev . Read6 ( out cmdBuf , out _ , 0 , blockSize , ( byte ) _maximumReadable , _dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Error | | sense )
2019-12-26 01:34:24 +00:00
_maximumReadable / = 2 ;
2018-06-19 22:17:20 +01:00
}
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
if ( ! _dev . Error | |
2019-12-26 01:34:24 +00:00
_maximumReadable = = 1 )
2019-11-18 20:59:16 +00:00
break ;
2017-05-31 01:00:58 +01:00
}
2019-12-25 18:07:05 +00:00
if ( _dev . Error | | sense )
2017-05-31 01:00:58 +01:00
{
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." ) ;
2017-05-31 01:00:58 +01:00
}
2019-12-14 21:38:18 +00:00
2020-03-09 20:23:21 +00:00
// Try to read the first track pregap
if ( _dumpFirstTrackPregap & & readcd )
ReadCdFirstTrackPregap ( blockSize , ref currentSpeed , mediaTags , supportedSubchannel , ref totalDuration ) ;
2019-12-26 01:34:24 +00:00
_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 ) ;
2019-12-26 01:34:24 +00:00
_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
2019-12-26 01:34:24 +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)." ) ;
2019-12-26 01:34:24 +00:00
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 ) ;
2020-07-14 14:24:25 +01:00
return ;
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 & &
2020-05-05 13:28:34 +01:00
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." +
2020-04-24 13:28:49 +01:00
Environment . NewLine + _outputPlugin . ErrorMessage ) ;
2019-12-14 21:41:43 +00:00
return ;
}
2019-11-18 20:59:16 +00:00
2018-01-20 17:12:01 +00:00
// If a subchannel is supported, check if output plugin allows us to write it.
2020-05-05 16:58:26 +01:00
if ( desiredSubchannel ! = MmcSubchannel . None & &
! ( _outputPlugin as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
2020-07-18 20:43:37 +01:00
CanStoreSubchannelRw ) )
2017-05-31 01:00:58 +01:00
{
2020-05-05 16:58:26 +01:00
_dumpLog . WriteLine ( "Output image does not support subchannels, {0}continuing..." , _force ? "" : "not " ) ;
2017-11-20 05:07:16 +00:00
2020-05-05 16:58:26 +01:00
if ( _force )
ErrorMessage ? . Invoke ( "Output image does not support subchannels, continuing..." ) ;
else
2017-06-07 23:25:06 +01:00
{
2020-05-05 16:58:26 +01:00
StoppingErrorMessage ? . Invoke ( "Output image does not support subchannels, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2020-05-05 16:58:26 +01:00
return ;
2017-06-07 23:25:06 +01:00
}
2018-01-20 17:12:01 +00:00
}
2019-12-14 21:45:23 +00:00
2020-04-27 02:29:49 +01:00
if ( supportedSubchannel ! = MmcSubchannel . None )
{
_dumpLog . WriteLine ( $"Creating subchannel log in {_outputPrefix + " . sub . log "}" ) ;
2020-05-05 17:53:06 +01:00
subLog = new SubchannelLog ( _outputPrefix + ".sub.log" , bcdSubchannel ) ;
2020-04-27 02:29:49 +01:00
}
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
2020-06-14 23:45:26 +01:00
} , kvp . Key , SectorTagType . CdTrackFlags ) ;
2019-12-14 21:45:23 +00:00
}
2019-12-14 21:46:58 +00:00
// Set MCN
2020-05-05 21:08:41 +01:00
if ( supportedSubchannel = = MmcSubchannel . None )
2019-12-14 21:46:58 +00:00
{
2020-05-05 21:08:41 +01: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
2020-05-05 20:26:18 +01:00
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
2020-05-05 20:26:18 +01:00
if ( sense | |
isrc = = null | |
isrc = = "000000000000" )
continue ;
2019-12-14 21:47:26 +00:00
2020-05-05 20:26:18 +01:00
isrcs [ ( byte ) trk . TrackSequence ] = isrc ;
2020-05-05 21:08:41 +01:00
UpdateStatus ? . Invoke ( $"Found ISRC for track {trk.TrackSequence}: {mcn}" ) ;
_dumpLog . WriteLine ( $"Found ISRC for track {trk.TrackSequence}: {mcn}" ) ;
2020-05-05 20:26:18 +01:00
}
2019-12-14 21:51:51 +00:00
2020-06-13 19:15:27 +01:00
if ( supportedSubchannel ! = MmcSubchannel . None & &
desiredSubchannel ! = MmcSubchannel . None )
{
2020-06-14 18:42:33 +01:00
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 )
2020-06-14 18:42:33 +01:00
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
}
2019-12-26 01:34:24 +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
2019-12-25 18:25:25 +00:00
// 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 ;
}
2019-12-25 18:25:25 +00:00
}
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-07-13 19:41:42 +01:00
// Search for read offset in main database
2020-01-06 22:29:01 +00:00
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 ,
2020-03-02 23:58:20 +00:00
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 ;
}
2020-03-11 15:28:04 +00:00
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 ) ;
2018-01-20 17:12:01 +00:00
2019-12-25 21:03:21 +00:00
audioExtents = new ExtentsULong ( ) ;
foreach ( Track audioTrack in tracks . Where ( t = > t . TrackType = = TrackType . Audio ) )
{
audioExtents . Add ( audioTrack . TrackStartSector , audioTrack . TrackEndSector ) ;
}
2019-12-26 03:45:09 +00:00
// Set speed
if ( _speedMultiplier > = 0 )
{
2020-03-03 03:04:01 +00:00
_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 ")}." ) ;
2019-12-26 03:45:09 +00:00
_speed * = _speedMultiplier ;
if ( _speed = = 0 | |
_speed > 0xFFFF )
_speed = 0xFFFF ;
2020-03-09 20:23:21 +00:00
_dev . SetCdSpeed ( out _ , RotationalControl . ClvAndImpureCav , ( ushort ) _speed , 0 , _dev . Timeout , out _ ) ;
2019-12-26 03:45:09 +00:00
}
2018-01-20 17:12:01 +00:00
// Start reading
2020-01-01 18:31:14 +00:00
start = DateTime . UtcNow ;
2019-11-18 20:59:16 +00:00
2020-06-25 01:13:02 +01:00
if ( dskType = = MediaType . CDIREADY )
2020-06-17 01:53:14 +01:00
{
2020-06-25 01:13:02 +01:00
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 ,
2020-07-18 20:43:37 +01:00
subchannelExtents , blocks , cdiReadyReadAsAudio , offsetBytes , sectorsForOffset ,
smallestPregapLbaPerTrack ) ;
2020-06-17 01:53:14 +01:00
}
2020-01-01 18:31:14 +00:00
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 ,
2020-01-02 18:31:49 +00:00
readcd , sectorsForOffset , subSize , supportedSubchannel , supportsLongSectors , ref totalDuration ,
2020-07-18 20:43:37 +01:00
tracks , subLog , desiredSubchannel , isrcs , ref mcn , subchannelExtents , smallestPregapLbaPerTrack ) ;
2019-04-19 20:13:17 +01:00
2018-06-23 01:31:43 +01:00
// TODO: Enable when underlying images support lead-outs
2019-12-31 21:39:18 +00:00
/ *
2019-12-31 20:26:27 +00:00
DumpCdLeadOuts ( blocks , blockSize , ref currentSpeed , currentTry , extents , ibgLog , ref imageWriteDuration ,
leadOutExtents , ref maxSpeed , mhddLog , ref minSpeed , read6 , read10 , read12 , read16 , readcd ,
2020-07-18 20:43:37 +01:00
supportedSubchannel , subSize , ref totalDuration , subLog , desiredSubchannel , isrcs , ref mcn , tracks ,
smallestPregapLbaPerTrack ) ;
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 ,
2020-04-24 13:28:49 +01:00
( 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
2020-01-01 18:44:27 +00:00
TrimCdUserData ( audioExtents , blockSize , currentTry , extents , newTrim , offsetBytes , read6 , read10 , read12 ,
read16 , readcd , sectorsForOffset , subSize , supportedSubchannel , supportsLongSectors ,
2020-07-18 20:43:37 +01:00
ref totalDuration , subLog , desiredSubchannel , tracks , isrcs , ref mcn , subchannelExtents ,
smallestPregapLbaPerTrack ) ;
2018-04-10 02:39:41 +01:00
2020-01-01 19:07:52 +00:00
RetryCdUserData ( audioExtents , blockSize , currentTry , extents , offsetBytes , readcd , sectorsForOffset ,
2020-05-05 21:08:41 +01:00
subSize , supportedSubchannel , ref totalDuration , subLog , desiredSubchannel , tracks , isrcs ,
2020-07-18 20:43:37 +01:00
ref mcn , subchannelExtents , smallestPregapLbaPerTrack ) ;
2020-06-13 19:15:27 +01:00
2020-06-17 02:03:01 +01:00
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 ,
2020-07-18 20:43:37 +01:00
tracks , isrcs , ref mcn , subchannelExtents , smallestPregapLbaPerTrack ) ;
2017-12-19 20:33:03 +00:00
2018-01-20 17:12:01 +00:00
// Write media tags to image
2019-12-25 18:07:05 +00:00
if ( ! _aborted )
2019-11-18 20:59:16 +00:00
foreach ( KeyValuePair < MediaTagType , byte [ ] > tag in mediaTags )
2017-06-07 22:37:05 +01:00
{
2019-11-18 20:59:16 +00:00
if ( tag . Value is null )
2019-08-10 14:35:14 +01:00
{
2020-02-27 23:48:41 +00:00
AaruConsole . ErrorWriteLine ( "Error: Tag type {0} is null, skipping..." , tag . Key ) ;
2019-11-18 20:59:16 +00:00
2019-08-10 14:35:14 +01:00
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 )
2019-11-18 20:59:16 +00:00
continue ;
2017-05-31 01:00:58 +01:00
2018-01-20 17:12:01 +00:00
// 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-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-20 17:12:01 +00:00
}
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
_resume . BadBlocks . Sort ( ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
foreach ( ulong bad in _resume . BadBlocks )
_dumpLog . WriteLine ( "Sector {0} could not be read." , bad ) ;
2019-11-18 20:59:16 +00:00
2018-02-01 01:20:41 +00:00
currentTry . Extents = ExtentsConverter . ToMetadata ( extents ) ;
2020-06-13 19:15:27 +01:00
_resume . BadSubchannels = new List < int > ( ) ;
2020-06-14 18:42:33 +01:00
_resume . BadSubchannels . AddRange ( subchannelExtents ) ;
2020-06-13 19:15:27 +01:00
_resume . BadSubchannels . Sort ( ) ;
2020-07-13 22:32:47 +01:00
if ( _generateSubchannels & & _outputPlugin . SupportedSectorTags . Contains ( SectorTagType . CdSectorSubchannel ) )
2020-07-14 01:06:23 +01:00
Media . CompactDisc . GenerateSubchannels ( subchannelExtents , tracks , trackFlags , blocks , subLog , _dumpLog ,
InitProgress , UpdateProgress , EndProgress , _outputPlugin ) ;
2020-07-13 22:32:47 +01:00
2020-01-09 18:01:43 +00:00
// TODO: Disc ID
var metadata = new CommonTypes . Structs . ImageInfo
{
2020-02-27 14:06:36 +00:00
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-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _preSidecar ! = null )
_outputPlugin . SetCicmMetadata ( _preSidecar ) ;
2019-11-18 20:59:16 +00:00
2020-05-05 20:26:18 +01:00
foreach ( KeyValuePair < byte , string > isrc in isrcs )
{
// TODO: Track tags
2020-06-14 23:45:26 +01:00
if ( ! _outputPlugin . WriteSectorTag ( Encoding . ASCII . GetBytes ( isrc . Value ) , isrc . Key ,
2020-05-05 20:26:18 +01:00
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 ) ;
}
2020-05-05 21:08:41 +01:00
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." ) ;
2019-04-19 20:13:17 +01:00
UpdateStatus ? . Invoke ( "Closing output file." ) ;
2019-11-18 20:59:16 +00:00
DateTime closeStart = DateTime . Now ;
2019-12-25 18:07:05 +00:00
_outputPlugin . Close ( ) ;
2019-11-18 20:59:16 +00:00
DateTime closeEnd = DateTime . Now ;
2019-04-19 20:13:17 +01:00
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 )
2018-01-20 17:12:01 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Aborted!" ) ;
2019-11-18 20:59:16 +00:00
2018-01-20 17:12:01 +00:00
return ;
}
2017-05-31 01:00:58 +01:00
2018-02-02 22:03:19 +00:00
double totalChkDuration = 0 ;
2019-11-18 20:59:16 +00:00
2020-01-04 02:28:17 +00:00
if ( _metadata )
2020-01-06 23:41:56 +00:00
WriteOpticalSidecar ( blockSize , blocks , dskType , null , mediaTags , sessions , out totalChkDuration ,
discOffset ) ;
2018-01-20 17:12:01 +00:00
2019-12-14 22:02:36 +00:00
end = DateTime . UtcNow ;
2019-04-19 20:13:17 +01:00
UpdateStatus ? . Invoke ( "" ) ;
2019-11-18 20:59:16 +00:00
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)." ) ;
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
2019-12-14 18:17:17 +00:00
Invoke ( $"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec." ) ;
2019-11-18 20:59:16 +00:00
2020-06-25 01:47:25 +01:00
if ( maxSpeed > 0 )
UpdateStatus ? . Invoke ( $"Fastest speed burst: {maxSpeed:F3} MiB/sec." ) ;
if ( minSpeed > 0 & &
minSpeed < double . MaxValue )
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." ) ;
2019-04-19 20:13:17 +01:00
UpdateStatus ? . Invoke ( "" ) ;
2017-06-20 05:48:09 +01:00
2018-01-20 17:12:01 +00:00
Statistics . AddMedia ( dskType , true ) ;
2017-05-31 01:00:58 +01:00
}
}
2017-12-19 20:33:03 +00:00
}