2019-12-31 19:47:18 +00:00
// /***************************************************************************
2017-05-31 01:00:58 +01:00
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : CompactDisc.cs
2017-12-19 03:50:57 +00:00
// Author(s) : Natalia Portillo <claunia@claunia.com>
2017-05-31 01:00:58 +01:00
//
2017-12-19 03:50:57 +00:00
// Component : Core algorithms.
2017-05-31 01:00:58 +01:00
//
// --[ Description ] ----------------------------------------------------------
//
2017-12-19 03:50:57 +00:00
// Dumps CDs and DDCDs.
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 ;
2018-06-25 19:08:16 +01:00
using DiscImageChef.CommonTypes ;
using DiscImageChef.CommonTypes.Enums ;
using DiscImageChef.CommonTypes.Extents ;
using DiscImageChef.CommonTypes.Interfaces ;
using DiscImageChef.CommonTypes.Structs ;
2018-01-20 17:12:01 +00:00
using DiscImageChef.Console ;
2017-05-31 01:00:58 +01:00
using DiscImageChef.Core.Logging ;
2019-02-12 00:56:02 +00:00
using DiscImageChef.Core.Media.Detection ;
2019-12-31 21:27:16 +00:00
using DiscImageChef.Database.Models ;
2018-01-20 17:12:01 +00:00
using DiscImageChef.Decoders.CD ;
2017-12-21 14:30:38 +00:00
using DiscImageChef.Devices ;
2018-01-20 17:12:01 +00:00
using Schemas ;
2018-06-25 19:08:16 +01:00
using PlatformID = DiscImageChef . CommonTypes . Interop . PlatformID ;
using TrackType = DiscImageChef . CommonTypes . Enums . TrackType ;
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
namespace DiscImageChef.Core.Devices.Dumping
{
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>
2017-12-23 01:46:08 +00:00
/// <param name="dskType">Disc type as detected in MMC layer</param>
2019-12-14 22:55:26 +00:00
void CompactDisc ( out MediaType dskType )
2019-12-14 14:33:23 +00:00
{
2019-12-31 21:02:22 +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
2020-01-01 19:07:52 +00:00
byte [ ] cmdBuf ; // Data buffer
2019-12-31 21:02:22 +00:00
DumpHardwareType currentTry = null ; // Current dump hardware try
double currentSpeed = 0 ; // Current read speed
DateTime dumpStart = DateTime . UtcNow ; // Time of dump start
DateTime end ; // Time of operation end
ExtentsULong extents = null ; // Extents
2020-01-01 18:06:31 +00:00
bool hiddenData ; // Hidden track is data
2019-12-31 21:02:22 +00:00
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
2020-01-01 19:07:52 +00:00
double minSpeed = double . MaxValue ; // Minimum speed
bool newTrim ; // Is trim a new one?
2020-01-01 15:34:03 +00:00
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)
2019-12-31 21:02:22 +00:00
bool readcd ; // Device supports READ CD
bool ret ; // Image writing return status
const uint sectorSize = 2352 ; // Full sector size
2020-01-01 15:34:03 +00:00
int sectorsForOffset = 0 ; // Sectors needed to fix offset
2020-01-01 18:06:31 +00:00
bool sense = true ; // Sense indicator
2020-01-01 16:06:56 +00:00
int sessions ; // Number of sessions in disc
2019-12-31 21:02:22 +00:00
DateTime start ; // Start of operation
uint subSize ; // Subchannel size in bytes
TrackSubchannelType subType ; // Track subchannel type
bool supportsLongSectors = true ; // Supports reading EDC and ECC
2020-01-01 15:34:03 +00:00
bool supportsPqSubchannel ; // Supports reading PQ subchannel
bool supportsRwSubchannel ; // Supports reading RW subchannel
2019-12-31 21:02:22 +00:00
byte [ ] tmpBuf ; // Temporary buffer
2020-01-01 19:07:52 +00:00
FullTOC . CDFullTOC ? toc ; // Full CD TOC
2019-12-31 21:02:22 +00:00
double totalDuration = 0 ; // Total commands duration
Dictionary < byte , byte > trackFlags = new Dictionary < byte , byte > ( ) ; // Track flags
Track [ ] tracks ; // Tracks in disc
2019-12-14 19:37:52 +00:00
2020-01-01 18:06:31 +00:00
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
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
2019-12-25 16:53:29 +00:00
2019-12-14 18:57:54 +00:00
dskType = MediaType . CD ;
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
// Search for read offset in master database
2019-12-26 01:34:24 +00:00
cdOffset = _ctx . CdOffsets . FirstOrDefault ( d = > d . Manufacturer = = _dev . Manufacturer & & d . Model = = _dev . Model ) ;
2019-12-14 17:48:32 +00:00
if ( cdOffset is null )
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "CD reading offset not found in database." ) ;
2019-12-14 17:48:32 +00:00
UpdateStatus ? . Invoke ( "CD reading offset not found in database." ) ;
}
else
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( $"CD reading offset is {cdOffset.Offset} samples." ) ;
2019-12-14 17:48:32 +00:00
UpdateStatus ? . Invoke ( $"CD reading offset is {cdOffset.Offset} samples." ) ;
}
2019-12-14 18:17:17 +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
2019-12-26 03:14:12 +00:00
switch ( _subchannel )
{
case DumpSubchannel . Any :
2020-01-01 15:34:03 +00:00
if ( supportsRwSubchannel )
2019-12-26 03:14:12 +00:00
supportedSubchannel = MmcSubchannel . Raw ;
2020-01-01 15:34:03 +00:00
else if ( supportsPqSubchannel )
2019-12-26 03:14:12 +00:00
supportedSubchannel = MmcSubchannel . Q16 ;
else
supportedSubchannel = 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 )
2019-12-26 03:14:12 +00:00
supportedSubchannel = MmcSubchannel . Raw ;
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 )
2019-12-26 03:14:12 +00:00
supportedSubchannel = MmcSubchannel . Raw ;
2020-01-01 15:34:03 +00:00
else if ( supportsPqSubchannel )
2019-12-26 03:14:12 +00:00
supportedSubchannel = MmcSubchannel . Q16 ;
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 )
2019-12-26 03:14:12 +00:00
supportedSubchannel = 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 :
supportedSubchannel = MmcSubchannel . None ;
break ;
default : throw new ArgumentOutOfRangeException ( ) ;
}
// Check if output format supports subchannels
if ( ! _outputPlugin . SupportedSectorTags . Contains ( SectorTagType . CdSectorSubchannel ) & &
supportedSubchannel ! = MmcSubchannel . None )
{
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 ;
}
supportedSubchannel = 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..." ) ;
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..." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!" ) ;
2019-12-26 03:14:12 +00:00
UpdateStatus ? . Invoke ( "Drive can read without subchannel..." ) ;
2019-12-14 19:37:52 +00:00
UpdateStatus ? .
Invoke ( "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!" ) ;
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 ;
}
2019-12-26 03:14:12 +00:00
blockSize = sectorSize + subSize ;
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-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
2019-12-14 20:39:15 +00:00
for ( int t = 1 ; t < tracks . Length ; t + + )
tracks [ t - 1 ] . TrackEndSector = tracks [ t ] . TrackStartSector - 1 ;
tracks [ tracks . Length - 1 ] . TrackEndSector = ( ulong ) lastSector ;
blocks = ( ulong ) ( lastSector + 1 ) ;
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 ) ;
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-01-01 18:06:31 +00:00
TrackSequence = 0 , TrackSession = 1 ,
TrackType = hiddenData ? TrackType . Data : TrackType . Audio ,
TrackStartSector = 0 , TrackBytesPerSector = ( int ) sectorSize ,
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
}
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-01-01 19:07:52 +00:00
sense = ! _dev . ReadCd ( out cmdBuf , out _ , ( uint ) tracks [ t ] . TrackStartSector , blockSize , 1 ,
2019-12-25 18:07:05 +00:00
MmcSectorTypes . AllTypes , false , false , true , MmcHeaderCodes . AllHeaders , true , true ,
MmcErrorField . None , supportedSubchannel , _dev . Timeout , out _ ) ;
2018-01-20 17:12:01 +00:00
2019-12-14 21:03:52 +00:00
if ( ! sense )
2017-06-07 22:37:05 +01:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Unable to guess mode for track {0}, continuing..." , tracks [ t ] . TrackSequence ) ;
2019-04-19 20:13:17 +01:00
UpdateStatus ? . Invoke ( $"Unable to guess mode for track {tracks[t].TrackSequence}, continuing..." ) ;
2019-11-18 20:59:16 +00:00
2018-01-20 17:12:01 +00: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-12-31 21:16:52 +00:00
// Try to read the first track pregap
2019-12-25 18:07:05 +00:00
if ( _dumpFirstTrackPregap & & readcd )
2020-01-01 19:15:32 +00:00
ReadCdFirstTrackPregap ( blockSize , ref currentSpeed , mediaTags , supportedSubchannel , ref totalDuration ) ;
2017-05-31 01:00:58 +01: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
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 ) ;
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 & &
supportedSubchannel = = MmcSubchannel . None )
{
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 +
2019-12-25 18:07:05 +00:00
_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.
2019-11-18 20:59:16 +00:00
if ( supportedSubchannel ! = MmcSubchannel . None )
2017-05-31 01:00:58 +01:00
{
2020-01-01 19:07:52 +00:00
_dev . ReadCd ( out cmdBuf , out _ , 0 , blockSize , 1 , MmcSectorTypes . AllTypes , false , false , true ,
2019-12-25 18:07:05 +00:00
MmcHeaderCodes . AllHeaders , true , true , MmcErrorField . None , supportedSubchannel ,
_dev . Timeout , out _ ) ;
2017-11-20 05:07:16 +00:00
2019-12-14 18:17:17 +00:00
tmpBuf = new byte [ subSize ] ;
2019-12-14 22:55:26 +00:00
Array . Copy ( cmdBuf , sectorSize , tmpBuf , 0 , subSize ) ;
2018-01-20 17:12:01 +00:00
2019-12-25 18:07:05 +00:00
ret = _outputPlugin . WriteSectorTag ( tmpBuf , 0 , SectorTagType . CdSectorSubchannel ) ;
2018-01-20 17:12:01 +00:00
2019-11-18 20:59:16 +00:00
if ( ! ret )
2017-06-07 23:25:06 +01: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 ( "Error writing subchannel to output image, {0}continuing..." ,
_force ? "" : "not " ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( _outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-19 20:13:17 +01:00
ErrorMessage ? . Invoke ( "Error writing subchannel to output image, continuing..." +
2019-11-18 20:59:16 +00:00
Environment . NewLine +
2019-12-25 18:07:05 +00:00
_outputPlugin . ErrorMessage ) ;
2019-04-19 20:13:17 +01:00
}
else
{
StoppingErrorMessage ? . Invoke ( "Error writing subchannel to output image, not continuing..." +
2019-11-18 20:59:16 +00:00
Environment . NewLine +
2019-12-25 18:07:05 +00:00
_outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-19 20:13:17 +01:00
return ;
}
2018-01-20 17:12:01 +00:00
2018-04-02 23:08:26 +01:00
supportedSubchannel = MmcSubchannel . None ;
2019-11-18 20:59:16 +00:00
subSize = 0 ;
2019-12-14 22:55:26 +00:00
blockSize = sectorSize + subSize ;
2019-11-18 20:59:16 +00:00
for ( int t = 0 ; t < tracks . Length ; t + + )
tracks [ t ] . TrackSubchannelType = TrackSubchannelType . None ;
2019-12-25 18:07:05 +00:00
ret = ( _outputPlugin as IWritableOpticalImage ) . SetTracks ( tracks . ToList ( ) ) ;
2019-11-18 20:59:16 +00:00
if ( ! ret )
2018-01-20 17:12:01 +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-11-18 20:59:16 +00:00
2019-04-19 20:13:17 +01:00
StoppingErrorMessage ? . Invoke ( "Error sending tracks to output image, not continuing..." +
2019-11-18 20:59:16 +00:00
Environment . NewLine +
2019-12-25 18:07:05 +00:00
_outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2018-01-20 17:12:01 +00: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
// Set track flags
foreach ( KeyValuePair < byte , byte > kvp in trackFlags )
{
Track track = tracks . FirstOrDefault ( t = > t . TrackSequence = = kvp . Key ) ;
if ( track . TrackSequence = = 0 )
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
} , track . TrackStartSector , SectorTagType . CdTrackFlags ) ;
}
2019-12-14 21:46:58 +00:00
// Set MCN
2019-12-25 18:07:05 +00:00
sense = _dev . ReadMcn ( out string mcn , out _ , out _ , _dev . Timeout , out _ ) ;
2019-12-14 21:46:58 +00:00
if ( ! sense & &
mcn ! = null & &
mcn ! = "0000000000000" & &
2019-12-25 18:07:05 +00:00
_outputPlugin . WriteMediaTag ( Encoding . ASCII . GetBytes ( mcn ) , MediaTagType . CD_MCN ) )
2019-12-14 21:46:58 +00:00
{
UpdateStatus ? . Invoke ( $"Setting disc Media Catalogue Number to {mcn}" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Setting disc Media Catalogue Number to {0}" , mcn ) ;
2019-12-14 21:46:58 +00:00
}
2019-12-14 21:47:26 +00:00
// Set ISRCs
foreach ( Track trk in tracks )
{
2019-12-25 18:07:05 +00:00
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-25 18:07:05 +00:00
if ( ! _outputPlugin . WriteSectorTag ( Encoding . ASCII . GetBytes ( isrc ) , trk . TrackStartSector ,
SectorTagType . CdTrackIsrc ) )
2019-12-14 21:47:26 +00:00
continue ;
UpdateStatus ? . Invoke ( $"Setting ISRC for track {trk.TrackSequence} to {isrc}" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Setting ISRC for track {0} to {1}" , trk . TrackSequence , isrc ) ;
2019-12-14 21:47:26 +00:00
}
2019-12-14 21:51:51 +00: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
if ( dskType = = MediaType . CDIREADY )
{
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." ) ;
}
2019-12-14 21:45:00 +00:00
2019-12-25 18:25:25 +00:00
// Check offset
if ( _fixOffset )
{
2019-12-31 19:40:29 +00:00
_fixOffset = Media . Info . CompactDisc . GetOffset ( cdOffset , _dbDev , _debug , _dev , dskType , _dumpLog ,
out offsetBytes , readcd , out sectorsForOffset , tracks ,
UpdateStatus ) ;
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." ) ;
}
2019-12-26 01:34:24 +00:00
mhddLog = new MhddLog ( _outputPrefix + ".mhddlog.bin" , _dev , blocks , blockSize , _maximumReadable ) ;
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 )
{
2019-12-31 17:32:12 +00:00
_dumpLog . WriteLine ( $"Setting speed to {(_speed == 0 ? " MAX " : $" { _speed } x ")}." ) ;
2019-12-31 01:37:41 +00:00
UpdateStatus ? . Invoke ( $"Setting speed to {(_speed == 0 ? " MAX " : $" { _speed } x ")}." ) ;
2019-12-26 03:45:09 +00:00
_speed * = _speedMultiplier ;
if ( _speed = = 0 | |
_speed > 0xFFFF )
_speed = 0xFFFF ;
_dev . SetCdSpeed ( out _ , RotationalControl . ClvAndImpureCav , ( ushort ) _speed , 0 , _dev . Timeout , out _ ) ;
}
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-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 ,
tracks ) ;
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 ,
supportedSubchannel , subSize , ref totalDuration ) ;
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
2020-01-01 18:44:27 +00:00
TrimCdUserData ( audioExtents , blockSize , currentTry , extents , newTrim , offsetBytes , read6 , read10 , read12 ,
read16 , readcd , sectorsForOffset , subSize , supportedSubchannel , supportsLongSectors ,
ref totalDuration ) ;
2018-04-10 02:39:41 +01:00
2020-01-01 19:07:52 +00:00
RetryCdUserData ( audioExtents , blockSize , currentTry , extents , offsetBytes , readcd , sectorsForOffset ,
subSize , supportedSubchannel , ref totalDuration ) ;
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
{
DicConsole . 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 ) ;
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
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
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 )
2019-12-31 21:27:16 +00:00
WriteOpticalSidecar ( blockSize , blocks , dskType , null , mediaTags , sessions , out totalChkDuration ) ;
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
2019-04-19 20:13:17 +01:00
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." ) ;
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
}