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/>.
//
// ----------------------------------------------------------------------------
2022-02-18 10:02:53 +00:00
// Copyright © 2011-2022 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 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
2022-03-06 13:29:38 +00:00
namespace Aaru.Core.Devices.Dumping ;
/// <summary>Implement dumping Compact Discs</summary>
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
// TODO: Barcode
sealed partial class Dump
{
/// <summary>Dumps a compact disc</summary>
void CompactDisc ( )
2017-05-31 01:00:58 +01:00
{
2022-03-06 13:29:38 +00:00
ExtentsULong audioExtents ; // Extents with audio sectors
ulong blocks ; // Total number of positive sectors
uint blockSize ; // Size of the read sector in bytes
CdOffset cdOffset ; // Read offset from database
byte [ ] cmdBuf ; // Data buffer
DumpHardwareType currentTry = null ; // Current dump hardware try
double currentSpeed = 0 ; // Current read speed
int? discOffset = null ; // Disc write offset
DateTime dumpStart = DateTime . UtcNow ; // Time of dump start
DateTime end ; // Time of operation end
ExtentsULong extents = null ; // Extents
bool hiddenData ; // Hidden track is data
IbgLog ibgLog ; // IMGBurn log
double imageWriteDuration = 0 ; // Duration of image write
long lastSector ; // Last sector number
var leadOutExtents = new ExtentsULong ( ) ; // Lead-out extents
Dictionary < int , long > leadOutStarts = new ( ) ; // 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 ( ) ; // 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
bool bcdSubchannel = false ; // Subchannel positioning is in BCD
Dictionary < byte , string > isrcs = new ( ) ;
string mcn = null ;
HashSet < int > subchannelExtents = new ( ) ;
bool cdiReadyReadAsAudio = false ;
uint firstLba ;
var outputOptical = _outputPlugin as IWritableOpticalImage ;
Dictionary < MediaTagType , byte [ ] > mediaTags = new ( ) ; // Media tags
Dictionary < byte , int > smallestPregapLbaPerTrack = new ( ) ;
MediaType dskType = MediaType . CD ;
if ( _dumpRaw )
2019-12-14 14:33:23 +00:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Raw CD dumping not yet implemented" ) ;
StoppingErrorMessage ? . Invoke ( "Raw CD dumping not yet implemented" ) ;
2020-11-07 17:39:50 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
tracks = GetCdTracks ( _dev , _dumpLog , _force , out lastSector , leadOutStarts , mediaTags , StoppingErrorMessage ,
out toc , trackFlags , UpdateStatus ) ;
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
if ( tracks is null )
{
_dumpLog . WriteLine ( "Could not get tracks!" ) ;
StoppingErrorMessage ? . Invoke ( "Could not get tracks!" ) ;
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
firstLba = ( uint ) tracks . Min ( t = > t . StartSector ) ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
// Check subchannels support
supportsPqSubchannel = SupportsPqSubchannel ( _dev , _dumpLog , UpdateStatus , firstLba ) ;
supportsRwSubchannel = SupportsRwSubchannel ( _dev , _dumpLog , UpdateStatus , firstLba ) ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
if ( supportsRwSubchannel )
supportedSubchannel = MmcSubchannel . Raw ;
else if ( supportsPqSubchannel )
supportedSubchannel = MmcSubchannel . Q16 ;
else
supportedSubchannel = MmcSubchannel . None ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
switch ( _subchannel )
{
case DumpSubchannel . Any :
if ( supportsRwSubchannel )
desiredSubchannel = MmcSubchannel . Raw ;
else if ( supportsPqSubchannel )
desiredSubchannel = MmcSubchannel . Q16 ;
else
2020-05-05 13:28:34 +01:00
desiredSubchannel = MmcSubchannel . None ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
break ;
case DumpSubchannel . Rw :
if ( supportsRwSubchannel )
desiredSubchannel = MmcSubchannel . Raw ;
else
{
_dumpLog . WriteLine ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? .
Invoke ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2020-05-05 13:28:34 +01:00
2022-03-06 13:29:38 +00:00
return ;
2019-12-14 19:37:52 +00:00
}
2022-03-06 13:29:38 +00:00
break ;
case DumpSubchannel . RwOrPq :
if ( supportsRwSubchannel )
desiredSubchannel = MmcSubchannel . Raw ;
else if ( supportsPqSubchannel )
desiredSubchannel = MmcSubchannel . Q16 ;
2019-12-14 19:37:52 +00:00
else
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Drive does not support the requested subchannel format, not continuing..." ) ;
StoppingErrorMessage ? .
Invoke ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-26 03:14:12 +00:00
return ;
}
2022-03-06 13:29:38 +00:00
break ;
case DumpSubchannel . Pq :
if ( supportsPqSubchannel )
desiredSubchannel = MmcSubchannel . Q16 ;
else
{
_dumpLog . WriteLine ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? .
Invoke ( "Drive does not support the requested subchannel format, not continuing..." ) ;
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
break ;
case DumpSubchannel . None :
desiredSubchannel = MmcSubchannel . None ;
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
break ;
default : throw new ArgumentOutOfRangeException ( ) ;
}
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
if ( desiredSubchannel = = MmcSubchannel . Q16 & & supportsPqSubchannel )
supportedSubchannel = MmcSubchannel . Q16 ;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
// Check if output format supports subchannels
if ( ! outputOptical . SupportedSectorTags . Contains ( SectorTagType . CdSectorSubchannel ) & &
desiredSubchannel ! = MmcSubchannel . None )
{
if ( _force | | _subchannel = = DumpSubchannel . None )
{
_dumpLog . WriteLine ( "Output format does not support subchannels, continuing..." ) ;
UpdateStatus ? . Invoke ( "Output format does not support subchannels, continuing..." ) ;
}
else
{
_dumpLog . WriteLine ( "Output format does not support subchannels, not continuing..." ) ;
StoppingErrorMessage ? . Invoke ( "Output format does not support subchannels, not continuing..." ) ;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
desiredSubchannel = MmcSubchannel . None ;
}
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
switch ( supportedSubchannel )
{
case MmcSubchannel . None :
_dumpLog . WriteLine ( "Checking if drive supports reading without subchannel..." ) ;
UpdateStatus ? . Invoke ( "Checking if drive supports reading without subchannel..." ) ;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
readcd = ! _dev . ReadCd ( out cmdBuf , out _ , firstLba , sectorSize , 1 , MmcSectorTypes . AllTypes , false ,
false , true , MmcHeaderCodes . AllHeaders , true , true , MmcErrorField . None ,
supportedSubchannel , _dev . Timeout , out _ ) ;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
if ( ! readcd )
{
_dumpLog . WriteLine ( "Drive does not support READ CD, trying SCSI READ commands..." ) ;
ErrorMessage ? . Invoke ( "Drive does not support READ CD, trying SCSI READ commands..." ) ;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Checking if drive supports READ(6)..." ) ;
UpdateStatus ? . Invoke ( "Checking if drive supports READ(6)..." ) ;
read6 = ! _dev . Read6 ( out cmdBuf , out _ , firstLba , 2048 , 1 , _dev . Timeout , out _ ) ;
_dumpLog . WriteLine ( "Checking if drive supports READ(10)..." ) ;
UpdateStatus ? . Invoke ( "Checking if drive supports READ(10)..." ) ;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
read10 = ! _dev . Read10 ( out cmdBuf , out _ , 0 , false , true , false , false , firstLba , 2048 , 0 , 1 ,
_dev . Timeout , out _ ) ;
2020-06-14 22:53:52 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Checking if drive supports READ(12)..." ) ;
UpdateStatus ? . Invoke ( "Checking if drive supports READ(12)..." ) ;
2020-06-14 22:53:52 +01:00
2022-03-06 13:29:38 +00:00
read12 = ! _dev . Read12 ( out cmdBuf , out _ , 0 , false , true , false , false , firstLba , 2048 , 0 , 1 ,
false , _dev . Timeout , out _ ) ;
2020-06-14 22:53:52 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Checking if drive supports READ(16)..." ) ;
UpdateStatus ? . Invoke ( "Checking if drive supports READ(16)..." ) ;
2019-12-26 03:14:12 +00:00
2022-03-06 13:29:38 +00:00
read16 = ! _dev . Read16 ( out cmdBuf , out _ , 0 , false , true , false , firstLba , 2048 , 0 , 1 , false ,
_dev . Timeout , out _ ) ;
2020-05-05 17:53:06 +01:00
2022-03-06 13:29:38 +00:00
if ( ! read6 & &
! read10 & &
! read12 & &
! read16 )
{
_dumpLog . WriteLine ( "Cannot read from disc, not continuing..." ) ;
StoppingErrorMessage ? . Invoke ( "Cannot read from disc, not continuing..." ) ;
2020-05-05 17:53:06 +01:00
2022-03-06 13:29:38 +00:00
return ;
}
2020-05-05 17:53:06 +01:00
2022-03-06 13:29:38 +00:00
if ( read6 )
{
_dumpLog . WriteLine ( "Drive supports READ(6)..." ) ;
UpdateStatus ? . Invoke ( "Drive supports READ(6)..." ) ;
}
2020-05-05 17:53:06 +01:00
2022-03-06 13:29:38 +00:00
if ( read10 )
{
_dumpLog . WriteLine ( "Drive supports READ(10)..." ) ;
UpdateStatus ? . Invoke ( "Drive supports READ(10)..." ) ;
}
2020-05-05 17:53:06 +01:00
2022-03-06 13:29:38 +00:00
if ( read12 )
2020-05-05 17:53:06 +01:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Drive supports READ(12)..." ) ;
UpdateStatus ? . Invoke ( "Drive supports READ(12)..." ) ;
2020-05-05 17:53:06 +01:00
}
2022-03-06 13:29:38 +00:00
if ( read16 )
2020-05-05 17:53:06 +01:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Drive supports READ(16)..." ) ;
UpdateStatus ? . Invoke ( "Drive supports READ(16)..." ) ;
2020-05-05 17:53:06 +01:00
}
}
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Drive can read without subchannel..." ) ;
UpdateStatus ? . Invoke ( "Drive can read without subchannel..." ) ;
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
subSize = 0 ;
subType = TrackSubchannelType . None ;
2020-05-05 23:25:15 +01:00
2022-03-06 13:29:38 +00:00
break ;
case MmcSubchannel . Raw :
_dumpLog . WriteLine ( "Full raw subchannel reading supported..." ) ;
UpdateStatus ? . Invoke ( "Full raw subchannel reading supported..." ) ;
subType = TrackSubchannelType . Raw ;
subSize = 96 ;
readcd = true ;
2020-01-06 18:36:11 +00:00
2022-03-06 13:29:38 +00:00
break ;
case MmcSubchannel . Q16 :
_dumpLog . WriteLine ( "PQ subchannel reading supported..." ) ;
_dumpLog . WriteLine ( "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!" ) ;
UpdateStatus ? . Invoke ( "PQ subchannel reading supported..." ) ;
2020-01-06 18:36:11 +00:00
UpdateStatus ? .
2022-03-06 13:29:38 +00:00
Invoke ( "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!" ) ;
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
subType = TrackSubchannelType . Q16 ;
subSize = 16 ;
readcd = true ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
break ;
default :
_dumpLog . WriteLine ( "Handling subchannel type {0} not supported, exiting..." , supportedSubchannel ) ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? .
Invoke ( $"Handling subchannel type {supportedSubchannel} not supported, exiting..." ) ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
return ;
}
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
switch ( desiredSubchannel )
{
case MmcSubchannel . None :
subType = TrackSubchannelType . None ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
break ;
case MmcSubchannel . Raw :
case MmcSubchannel . Q16 :
subType = TrackSubchannelType . Raw ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
break ;
}
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
blockSize = sectorSize + subSize ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
// Check if subchannel is BCD
if ( supportedSubchannel ! = MmcSubchannel . None )
{
sense = _dev . ReadCd ( out cmdBuf , out _ , ( ( ( firstLba / 75 ) + 1 ) * 75 ) + 35 , blockSize , 1 ,
MmcSectorTypes . AllTypes , false , false , true , MmcHeaderCodes . AllHeaders , true , true ,
MmcErrorField . None , supportedSubchannel , _dev . Timeout , out _ ) ;
if ( ! sense )
2020-04-22 00:10:06 +01:00
{
2022-03-06 13:29:38 +00:00
tmpBuf = new byte [ subSize ] ;
Array . Copy ( cmdBuf , sectorSize , tmpBuf , 0 , subSize ) ;
2020-04-22 00:10:06 +01:00
2022-03-06 13:29:38 +00:00
if ( supportedSubchannel = = MmcSubchannel . Q16 )
tmpBuf = Subchannel . ConvertQToRaw ( tmpBuf ) ;
2020-04-22 00:10:06 +01:00
2022-03-06 13:29:38 +00:00
tmpBuf = Subchannel . Deinterleave ( tmpBuf ) ;
2020-04-22 00:28:49 +01:00
2022-03-06 13:29:38 +00:00
// 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 ;
2020-04-22 00:28:49 +01:00
2022-03-06 13:29:38 +00:00
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-04-22 00:10:06 +01:00
}
2022-03-06 13:29:38 +00:00
}
2020-04-22 00:10:06 +01:00
2022-03-06 13:29:38 +00:00
foreach ( Track trk in tracks )
trk . SubchannelType = subType ;
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Calculating pregaps, can take some time..." ) ;
UpdateStatus ? . Invoke ( "Calculating pregaps, can take some time..." ) ;
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
SolveTrackPregaps ( _dev , _dumpLog , UpdateStatus , tracks , supportsPqSubchannel , supportsRwSubchannel , _dbDev ,
out bool inexactPositioning , true ) ;
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
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." ) ;
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( "WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect." ) ;
}
2019-12-14 21:27:15 +00:00
2022-03-06 13:29:38 +00:00
if ( ! ( outputOptical as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
CanStoreRawData ) )
{
if ( ! _force )
2019-12-14 21:27:15 +00:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support storing raw data, this may end in a loss of data, not continuing..." ) ;
StoppingErrorMessage ? .
Invoke ( "Output format does not support storing raw data, this may end in a loss of data, not continuing..." ) ;
2019-12-14 21:27:15 +00:00
return ;
}
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support storing raw data, this may end in a loss of data, continuing..." ) ;
ErrorMessage ? .
Invoke ( "Output format does not support storing raw data, this may end in a loss of data, continuing..." ) ;
}
if ( ! ( outputOptical as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
CanStoreAudioTracks ) & &
tracks . Any ( track = > track . Type = = TrackType . Audio ) )
{
_dumpLog . WriteLine ( "Output format does not support audio tracks, cannot continue..." ) ;
StoppingErrorMessage ? . Invoke ( "Output format does not support audio tracks, cannot continue..." ) ;
return ;
}
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
if ( ! ( outputOptical as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
CanStorePregaps ) & &
tracks . Where ( track = > track . Sequence ! = tracks . First ( t = > t . Session = = track . Session ) . Sequence ) .
Any ( track = > track . Pregap > 0 ) )
{
if ( ! _force )
2020-10-23 04:06:10 +01:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support pregaps, this may end in a loss of data, not continuing..." ) ;
2020-10-23 04:06:10 +01:00
2021-07-12 18:21:22 +01:00
StoppingErrorMessage ? .
2022-03-06 13:29:38 +00:00
Invoke ( "Output format does not support pregaps, this may end in a loss of data, not continuing..." ) ;
2020-10-23 04:06:10 +01:00
2021-07-12 18:21:22 +01:00
return ;
2022-03-06 13:29:38 +00:00
}
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support pregaps, this may end in a loss of data, continuing..." ) ;
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
ErrorMessage ? .
Invoke ( "Output format does not support pregaps, this may end in a loss of data, continuing..." ) ;
}
2020-10-23 04:06:10 +01:00
2022-03-06 13:29:38 +00:00
for ( int t = 1 ; t < tracks . Length ; t + + )
tracks [ t - 1 ] . EndSector = tracks [ t ] . StartSector - 1 ;
2019-12-14 21:06:16 +00:00
2022-03-06 13:29:38 +00:00
tracks [ ^ 1 ] . EndSector = ( ulong ) lastSector ;
blocks = ( ulong ) ( lastSector + 1 ) ;
2019-12-14 21:06:16 +00:00
2022-03-06 13:29:38 +00:00
if ( blocks = = 0 )
{
StoppingErrorMessage ? . Invoke ( "Cannot dump blank media." ) ;
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
ResumeSupport . Process ( true , true , blocks , _dev . Manufacturer , _dev . Model , _dev . Serial , _dev . PlatformId ,
ref _resume , ref currentTry , ref extents , _dev . FirmwareRevision , _private , _force ) ;
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
if ( currentTry = = null | |
extents = = null )
{
StoppingErrorMessage ? . Invoke ( "Could not process resume file, not continuing..." ) ;
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
// Read media tags
ReadCdTags ( ref dskType , mediaTags , out sessions , out firstTrackLastSession ) ;
2019-12-14 21:03:52 +00:00
2022-03-06 13:29:38 +00:00
if ( ! ( outputOptical as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
CanStoreSessions ) & &
sessions > 1 )
{
// TODO: Disabled until 6.0
/ * if ( ! _force )
{ * /
_dumpLog . WriteLine ( "Output format does not support sessions, this will end in a loss of data, not continuing..." ) ;
2019-12-14 21:03:52 +00:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? .
Invoke ( "Output format does not support sessions, this will end in a loss of data, not continuing..." ) ;
2019-12-14 20:05:30 +00:00
2022-03-06 13:29:38 +00:00
return ;
/ * }
2019-12-14 20:05:30 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support sessions, this will end in a loss of data, continuing..." ) ;
2019-12-14 21:09:16 +00:00
2022-03-06 13:29:38 +00:00
ErrorMessage ? .
Invoke ( "Output format does not support sessions, this will end in a loss of data, continuing..." ) ; * /
}
2019-12-14 20:05:30 +00:00
2022-03-06 13:29:38 +00:00
// Check if output format supports all disc tags we have retrieved so far
foreach ( MediaTagType tag in mediaTags . Keys . Where ( tag = > ! outputOptical . SupportedMediaTags . Contains ( tag ) ) )
{
if ( _force )
2020-01-11 15:53:07 +00:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support {0}, continuing..." , tag ) ;
ErrorMessage ? . Invoke ( $"Output format does not support {tag}, continuing..." ) ;
}
else
{
_dumpLog . WriteLine ( "Output format does not support {0}, not continuing..." , tag ) ;
StoppingErrorMessage ? . Invoke ( $"Output format does not support {tag}, not continuing..." ) ;
2020-01-11 15:53:07 +00:00
2022-03-06 13:29:38 +00:00
return ;
2020-01-11 15:53:07 +00:00
}
2022-03-06 13:29:38 +00:00
}
2020-01-11 15:53:07 +00:00
2022-03-06 13:29:38 +00:00
if ( leadOutStarts . Any ( ) )
{
UpdateStatus ? . Invoke ( "Solving lead-outs..." ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
foreach ( KeyValuePair < int , long > leadOuts in leadOutStarts )
foreach ( Track trk in tracks . Where ( trk = > trk . Session = = leadOuts . Key ) .
Where ( trk = > trk . EndSector > = ( ulong ) leadOuts . Value ) )
trk . EndSector = ( ulong ) leadOuts . Value - 1 ;
2018-06-19 22:17:20 +01:00
2022-03-06 13:29:38 +00:00
var dataExtents = new ExtentsULong ( ) ;
2018-06-19 22:48:51 +01:00
2022-03-06 13:29:38 +00:00
foreach ( Track trk in tracks )
dataExtents . Add ( trk . StartSector , trk . EndSector ) ;
2018-01-20 17:12:01 +00:00
2022-03-06 13:29:38 +00:00
Tuple < ulong , ulong > [ ] dataExtentsArray = dataExtents . ToArray ( ) ;
2020-04-24 13:28:49 +01:00
2022-03-06 13:29:38 +00:00
for ( int i = 0 ; i < dataExtentsArray . Length - 1 ; i + + )
leadOutExtents . Add ( dataExtentsArray [ i ] . Item2 + 1 , dataExtentsArray [ i + 1 ] . Item1 - 1 ) ;
}
2020-04-24 13:28:49 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Detecting disc type..." ) ;
UpdateStatus ? . Invoke ( "Detecting disc type..." ) ;
2018-01-20 17:12:01 +00:00
2022-03-06 13:29:38 +00:00
MMC . DetectDiscType ( ref dskType , sessions , toc , _dev , out hiddenTrack , out hiddenData , firstTrackLastSession ,
blocks ) ;
2020-11-07 02:52:16 +00:00
2022-03-06 13:29:38 +00:00
if ( hiddenTrack | | firstLba > 0 )
{
_dumpLog . WriteLine ( "Disc contains a hidden track..." ) ;
UpdateStatus ? . Invoke ( "Disc contains a hidden track..." ) ;
2020-11-07 02:52:16 +00:00
2022-03-06 13:29:38 +00:00
List < Track > trkList = new ( )
{
new Track
2017-06-07 22:37:05 +01:00
{
2022-03-06 13:29:38 +00:00
Sequence = 0 ,
Session = 1 ,
Type = hiddenData ? TrackType . Data : TrackType . Audio ,
StartSector = 0 ,
BytesPerSector = ( int ) sectorSize ,
RawBytesPerSector = ( int ) sectorSize ,
SubchannelType = subType ,
EndSector = tracks . First ( t = > t . Sequence = = 1 ) . StartSector - 1
}
} ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
trkList . AddRange ( tracks ) ;
tracks = trkList . ToArray ( ) ;
}
2019-08-15 18:35:38 +01:00
2022-03-06 13:29:38 +00:00
if ( tracks . Any ( t = > t . Type = = TrackType . Audio ) & &
desiredSubchannel ! = MmcSubchannel . Raw )
{
_dumpLog . WriteLine ( "WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!" ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
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 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
// Check mode for tracks
foreach ( Track trk in tracks . Where ( t = > t . Type ! = TrackType . Audio ) )
{
if ( ! readcd )
2019-12-14 21:13:31 +00:00
{
2022-03-06 13:29:38 +00:00
trk . Type = TrackType . CdMode1 ;
2019-12-14 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
continue ;
}
2019-12-14 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Checking mode for track {0}..." , trk . Sequence ) ;
UpdateStatus ? . Invoke ( $"Checking mode for track {trk.Sequence}..." ) ;
2019-12-14 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
sense = _dev . ReadCd ( out cmdBuf , out _ , ( uint ) ( trk . StartSector + trk . Pregap ) , blockSize , 1 ,
MmcSectorTypes . AllTypes , false , false , true , MmcHeaderCodes . AllHeaders , true , true ,
MmcErrorField . None , supportedSubchannel , _dev . Timeout , out _ ) ;
2019-12-14 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
if ( sense )
{
_dumpLog . WriteLine ( "Unable to guess mode for track {0}, continuing..." , trk . Sequence ) ;
2019-12-14 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"Unable to guess mode for track {trk.Sequence}, continuing..." ) ;
2019-12-14 21:13:31 +00:00
2022-03-06 13:29:38 +00:00
continue ;
2019-12-14 21:13:31 +00:00
}
2017-12-19 20:33:03 +00:00
2022-03-06 13:29:38 +00:00
int bufOffset = 0 ;
while ( cmdBuf [ 0 + bufOffset ] ! = 0x00 | |
cmdBuf [ 1 + bufOffset ] ! = 0xFF | |
cmdBuf [ 2 + bufOffset ] ! = 0xFF | |
cmdBuf [ 3 + bufOffset ] ! = 0xFF | |
cmdBuf [ 4 + bufOffset ] ! = 0xFF | |
cmdBuf [ 5 + bufOffset ] ! = 0xFF | |
cmdBuf [ 6 + bufOffset ] ! = 0xFF | |
cmdBuf [ 7 + bufOffset ] ! = 0xFF | |
cmdBuf [ 8 + bufOffset ] ! = 0xFF | |
cmdBuf [ 9 + bufOffset ] ! = 0xFF | |
cmdBuf [ 10 + bufOffset ] ! = 0xFF | |
cmdBuf [ 11 + bufOffset ] ! = 0x00 )
2017-11-29 15:14:21 +00:00
{
2022-03-06 13:29:38 +00:00
if ( bufOffset + 12 > = cmdBuf . Length )
break ;
bufOffset + + ;
}
switch ( cmdBuf [ 15 + bufOffset ] )
{
case 1 :
case 0x61 : // Scrambled
UpdateStatus ? . Invoke ( $"Track {trk.Sequence} is MODE1" ) ;
_dumpLog . WriteLine ( "Track {0} is MODE1" , trk . Sequence ) ;
trk . Type = TrackType . CdMode1 ;
break ;
case 2 :
case 0x62 : // Scrambled
if ( dskType = = MediaType . CDI | |
dskType = = MediaType . CDIREADY )
2019-04-19 20:13:17 +01:00
{
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"Track {trk.Sequence} is MODE2" ) ;
_dumpLog . WriteLine ( "Track {0} is MODE2" , trk . Sequence ) ;
trk . Type = TrackType . CdMode2Formless ;
break ;
2019-04-19 20:13:17 +01:00
}
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
if ( ( cmdBuf [ 0x012 ] & 0x20 ) = = 0x20 ) // mode 2 form 2
{
UpdateStatus ? . Invoke ( $"Track {trk.Sequence} is MODE2 FORM 2" ) ;
_dumpLog . WriteLine ( "Track {0} is MODE2 FORM 2" , trk . Sequence ) ;
trk . Type = TrackType . CdMode2Form2 ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
break ;
2019-04-19 20:13:17 +01:00
}
2018-01-20 17:12:01 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"Track {trk.Sequence} is MODE2 FORM 1" ) ;
_dumpLog . WriteLine ( "Track {0} is MODE2 FORM 1" , trk . Sequence ) ;
trk . Type = TrackType . CdMode2Form1 ;
// These media type specifications do not legally allow mode 2 tracks to be present
if ( dskType = = MediaType . CDROM | |
dskType = = MediaType . CDPLUS | |
dskType = = MediaType . CDV )
dskType = MediaType . CD ;
break ;
default :
UpdateStatus ? . Invoke ( $"Track {trk.Sequence} is unknown mode {cmdBuf[15]}" ) ;
_dumpLog . WriteLine ( "Track {0} is unknown mode {1}" , trk . Sequence , cmdBuf [ 15 ] ) ;
break ;
2018-01-20 17:12:01 +00:00
}
2022-03-06 13:29:38 +00:00
}
2019-01-27 17:47:40 +00:00
2022-03-06 13:29:38 +00:00
if ( outputOptical . Id = = new Guid ( "12345678-AAAA-BBBB-CCCC-123456789000" ) )
{
if ( tracks . Length > 1 )
2017-05-31 01:00:58 +01:00
{
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? . Invoke ( "Output format does not support more than 1 track, not continuing..." ) ;
_dumpLog . WriteLine ( "Output format does not support more than 1 track, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
if ( tracks . Any ( t = > t . Type = = TrackType . Audio ) )
{
StoppingErrorMessage ? . Invoke ( "Output format does not support audio tracks, not continuing..." ) ;
_dumpLog . WriteLine ( "Output format does not support audio tracks, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
if ( tracks . Any ( t = > t . Type ! = TrackType . CdMode1 ) )
{
StoppingErrorMessage ? . Invoke ( "Output format only supports MODE 1 tracks, not continuing..." ) ;
_dumpLog . WriteLine ( "Output format only supports MODE 1 tracks, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
supportsLongSectors = false ;
}
// Check if something prevents from dumping the first track pregap
if ( _dumpFirstTrackPregap & & readcd )
{
if ( ! outputOptical . SupportedMediaTags . Contains ( MediaTagType . CD_FirstTrackPregap ) )
{
if ( _force )
{
_dumpLog . WriteLine ( "Output format does not support CD first track pregap, continuing..." ) ;
ErrorMessage ? . Invoke ( "Output format does not support CD first track pregap, continuing..." ) ;
2018-06-19 22:17:20 +01:00
}
2022-03-06 13:29:38 +00:00
else
2018-06-19 22:17:20 +01:00
{
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Output format does not support CD first track pregap, not continuing..." ) ;
StoppingErrorMessage ? .
Invoke ( "Output format does not support CD first track pregap, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
return ;
2018-06-19 22:17:20 +01:00
}
2017-05-31 01:00:58 +01:00
2022-03-06 13:29:38 +00:00
_dumpFirstTrackPregap = false ;
2017-05-31 01:00:58 +01:00
}
2022-03-06 13:29:38 +00:00
}
2017-05-31 01:00:58 +01:00
2022-03-06 13:29:38 +00:00
// Try how many blocks are readable at once
while ( true )
{
if ( readcd )
2017-05-31 01:00:58 +01:00
{
2022-03-06 13:29:38 +00:00
sense = _dev . ReadCd ( out cmdBuf , out _ , firstLba , blockSize , _maximumReadable ,
MmcSectorTypes . AllTypes , false , false , true , MmcHeaderCodes . AllHeaders , true ,
true , MmcErrorField . None , supportedSubchannel , _dev . Timeout , out _ ) ;
if ( _dev . Error | | sense )
_maximumReadable / = 2 ;
2017-05-31 01:00:58 +01:00
}
2022-03-06 13:29:38 +00:00
else if ( read16 )
{
sense = _dev . Read16 ( out cmdBuf , out _ , 0 , false , true , false , firstLba , blockSize , 0 ,
_maximumReadable , false , _dev . Timeout , out _ ) ;
2019-12-14 21:38:18 +00:00
2022-03-06 13:29:38 +00:00
if ( _dev . Error | | sense )
_maximumReadable / = 2 ;
}
else if ( read12 )
2019-12-14 21:41:18 +00:00
{
2022-03-06 13:29:38 +00:00
sense = _dev . Read12 ( out cmdBuf , out _ , 0 , false , true , false , false , firstLba , blockSize , 0 ,
_maximumReadable , false , _dev . Timeout , out _ ) ;
2019-12-14 21:41:18 +00:00
2022-03-06 13:29:38 +00:00
if ( _dev . Error | | sense )
_maximumReadable / = 2 ;
}
else if ( read10 )
{
sense = _dev . Read10 ( out cmdBuf , out _ , 0 , false , true , false , false , firstLba , blockSize , 0 ,
( ushort ) _maximumReadable , _dev . Timeout , out _ ) ;
2020-07-14 14:24:25 +01:00
2022-03-06 13:29:38 +00:00
if ( _dev . Error | | sense )
_maximumReadable / = 2 ;
2019-12-14 21:41:18 +00:00
}
2022-03-06 13:29:38 +00:00
else if ( read6 )
{
sense = _dev . Read6 ( out cmdBuf , out _ , firstLba , blockSize , ( byte ) _maximumReadable , _dev . Timeout ,
out _ ) ;
2019-12-14 21:41:43 +00:00
2022-03-06 13:29:38 +00:00
if ( _dev . Error | | sense )
_maximumReadable / = 2 ;
}
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
if ( ! _dev . Error | |
_maximumReadable = = 1 )
break ;
}
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
if ( _dev . Error | | sense )
{
_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." ) ;
}
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
// Try to read the first track pregap
if ( _dumpFirstTrackPregap & & readcd )
ReadCdFirstTrackPregap ( blockSize , ref currentSpeed , mediaTags , supportedSubchannel , ref totalDuration ) ;
_dumpLog . WriteLine ( "Reading {0} sectors at a time." , _maximumReadable ) ;
_dumpLog . WriteLine ( "Device reports {0} blocks ({1} bytes)." , blocks , blocks * blockSize ) ;
_dumpLog . WriteLine ( "Device can read {0} blocks at a time." , _maximumReadable ) ;
_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 ) ;
UpdateStatus ? . Invoke ( $"Reading {_maximumReadable} sectors at a time." ) ;
UpdateStatus ? . Invoke ( $"Device reports {blocks} blocks ({blocks * blockSize} bytes)." ) ;
UpdateStatus ? . Invoke ( $"Device can read {_maximumReadable} blocks at a time." ) ;
UpdateStatus ? . Invoke ( $"Device reports {blockSize} bytes per logical block." ) ;
UpdateStatus ? . Invoke ( $"SCSI device type: {_dev.ScsiType}." ) ;
UpdateStatus ? . Invoke ( $"Media identified as {dskType}." ) ;
ret = outputOptical . Create ( _outputPath , dskType , _formatOptions , blocks ,
supportsLongSectors ? blockSize : 2048 ) ;
// Cannot create image
if ( ! ret )
{
_dumpLog . WriteLine ( "Error creating output image, not continuing." ) ;
_dumpLog . WriteLine ( outputOptical . ErrorMessage ) ;
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? . Invoke ( "Error creating output image, not continuing." + Environment . NewLine +
outputOptical . ErrorMessage ) ;
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
ErrorNumber errno = outputOptical . ReadMediaTag ( MediaTagType . CD_MCN , out byte [ ] mcnBytes ) ;
2021-09-20 20:52:18 +01:00
2022-03-06 13:29:38 +00:00
if ( errno = = ErrorNumber . NoError )
mcn = Encoding . ASCII . GetString ( mcnBytes ) ;
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
if ( ( outputOptical as IWritableOpticalImage ) . Tracks ! = null )
foreach ( Track imgTrack in ( outputOptical as IWritableOpticalImage ) . Tracks )
2019-12-14 21:41:43 +00:00
{
2022-03-06 13:29:38 +00:00
errno = ( outputOptical as IWritableOpticalImage ) . ReadSectorTag ( imgTrack . Sequence ,
SectorTagType . CdTrackIsrc , out byte [ ] isrcBytes ) ;
2019-12-14 21:41:43 +00:00
2022-03-06 13:29:38 +00:00
if ( errno = = ErrorNumber . NoError )
isrcs [ ( byte ) imgTrack . Sequence ] = Encoding . ASCII . GetString ( isrcBytes ) ;
2019-12-14 21:41:43 +00:00
2022-03-06 13:29:38 +00:00
Track trk = tracks . FirstOrDefault ( t = > t . Sequence = = imgTrack . Sequence ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
if ( trk = = null )
continue ;
2017-11-20 05:07:16 +00:00
2022-03-06 13:29:38 +00:00
trk . Pregap = imgTrack . Pregap ;
trk . StartSector = imgTrack . StartSector ;
trk . EndSector = imgTrack . EndSector ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
foreach ( KeyValuePair < ushort , int > imgIdx in imgTrack . Indexes )
trk . Indexes [ imgIdx . Key ] = imgIdx . Value ;
2018-01-20 17:12:01 +00:00
}
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
// Send track list to output plugin. This may fail if subchannel is set but unsupported.
ret = ( outputOptical as IWritableOpticalImage ) . SetTracks ( tracks . ToList ( ) ) ;
2020-04-27 02:29:49 +01:00
2022-03-06 13:29:38 +00:00
if ( ! ret & &
desiredSubchannel = = MmcSubchannel . None )
{
_dumpLog . WriteLine ( "Error sending tracks to output image, not continuing." ) ;
_dumpLog . WriteLine ( outputOptical . ErrorMessage ) ;
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? . Invoke ( "Error sending tracks to output image, not continuing." +
Environment . NewLine + outputOptical . ErrorMessage ) ;
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
// If a subchannel is supported, check if output plugin allows us to write it.
if ( desiredSubchannel ! = MmcSubchannel . None & &
! ( outputOptical as IWritableOpticalImage ) . OpticalCapabilities . HasFlag ( OpticalImageCapabilities .
CanStoreSubchannelRw ) )
{
_dumpLog . WriteLine ( "Output image does not support subchannels, {0}continuing..." , _force ? "" : "not " ) ;
2019-12-14 21:46:58 +00:00
2022-03-06 13:29:38 +00:00
if ( _force )
ErrorMessage ? . Invoke ( "Output image does not support subchannels, continuing..." ) ;
else
2019-12-14 21:46:58 +00:00
{
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? . Invoke ( "Output image does not support subchannels, not continuing..." ) ;
2020-05-05 21:08:41 +01:00
2022-03-06 13:29:38 +00:00
return ;
2019-12-14 21:46:58 +00:00
}
2022-03-06 13:29:38 +00:00
}
2019-12-14 21:47:26 +00:00
2022-03-06 13:29:38 +00:00
if ( supportedSubchannel ! = MmcSubchannel . None )
{
_dumpLog . WriteLine ( $"Creating subchannel log in {_outputPrefix + " . sub . log "}" ) ;
subLog = new SubchannelLog ( _outputPrefix + ".sub.log" , bcdSubchannel ) ;
}
2019-12-14 21:47:26 +00:00
2022-03-06 13:29:38 +00:00
// Set track flags
foreach ( KeyValuePair < byte , byte > kvp in trackFlags )
{
Track track = tracks . FirstOrDefault ( t = > t . Sequence = = kvp . Key ) ;
2019-12-14 21:47:26 +00:00
2022-03-06 13:29:38 +00:00
if ( track is null )
continue ;
2020-05-05 21:08:41 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Setting flags for track {0}..." , track . Sequence ) ;
UpdateStatus ? . Invoke ( $"Setting flags for track {track.Sequence}..." ) ;
2019-12-14 21:51:51 +00:00
2022-03-06 13:29:38 +00:00
outputOptical . WriteSectorTag ( new [ ]
2020-06-13 19:15:27 +01:00
{
2022-03-06 13:29:38 +00:00
kvp . Value
} , kvp . Key , SectorTagType . CdTrackFlags ) ;
}
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
// Set MCN
if ( supportedSubchannel = = MmcSubchannel . None )
{
sense = _dev . ReadMcn ( out mcn , out _ , out _ , _dev . Timeout , out _ ) ;
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
if ( ! sense & &
mcn ! = null & &
mcn ! = "0000000000000" )
{
UpdateStatus ? . Invoke ( $"Found Media Catalogue Number: {mcn}" ) ;
_dumpLog . WriteLine ( "Found Media Catalogue Number: {0}" , mcn ) ;
2020-06-13 19:15:27 +01:00
}
2022-03-06 13:29:38 +00:00
else
mcn = null ;
}
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
// Set ISRCs
if ( supportedSubchannel = = MmcSubchannel . None )
foreach ( Track trk in tracks )
2019-12-14 21:51:51 +00:00
{
2022-03-06 13:29:38 +00:00
sense = _dev . ReadIsrc ( ( byte ) trk . Sequence , out string isrc , out _ , out _ , _dev . Timeout , out _ ) ;
2019-12-14 21:51:51 +00:00
2022-03-06 13:29:38 +00:00
if ( sense | |
isrc = = null | |
isrc = = "000000000000" )
continue ;
2019-12-14 21:51:51 +00:00
2022-03-06 13:29:38 +00:00
isrcs [ ( byte ) trk . Sequence ] = isrc ;
2019-12-14 21:51:51 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"Found ISRC for track {trk.Sequence}: {isrc}" ) ;
_dumpLog . WriteLine ( $"Found ISRC for track {trk.Sequence}: {isrc}" ) ;
}
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( supportedSubchannel ! = MmcSubchannel . None & &
desiredSubchannel ! = MmcSubchannel . None )
{
subchannelExtents = new HashSet < int > ( ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
_resume . BadSubchannels ? ? = new List < int > ( ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
foreach ( int sub in _resume . BadSubchannels )
subchannelExtents . Add ( sub ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( _resume . NextBlock < blocks )
for ( ulong i = _resume . NextBlock ; i < blocks ; i + + )
subchannelExtents . Add ( ( int ) i ) ;
}
2019-12-25 18:25:25 +00:00
2022-03-06 13:29:38 +00:00
if ( _resume . NextBlock > 0 )
{
UpdateStatus ? . Invoke ( $"Resuming from block {_resume.NextBlock}." ) ;
_dumpLog . WriteLine ( "Resuming from block {0}." , _resume . NextBlock ) ;
}
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( _skip < _maximumReadable )
_skip = _maximumReadable ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
#if DEBUG
foreach ( Track trk in tracks )
UpdateStatus ? .
Invoke ( $"Track {trk.Sequence} starts at LBA {trk.StartSector} and ends at LBA {trk.EndSector}" ) ;
#endif
// Check offset
if ( _fixOffset )
{
if ( tracks . All ( t = > t . Type ! = TrackType . Audio ) )
2020-01-06 22:29:01 +00:00
{
2022-03-06 13:29:38 +00:00
// 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." ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
_fixOffset = false ;
}
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( ! readcd )
{
_dumpLog . WriteLine ( "READ CD command is not supported, disabling offset fix. Dump may not be correct." ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( "READ CD command is not supported, disabling offset fix. Dump may not be correct." ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
_fixOffset = false ;
}
}
else if ( tracks . Any ( t = > t . Type = = TrackType . Audio ) )
{
_dumpLog . WriteLine ( "There are audio tracks and offset fixing is disabled, dump may not be correct." ) ;
UpdateStatus ? . Invoke ( "There are audio tracks and offset fixing is disabled, dump may not be correct." ) ;
}
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
// Search for read offset in main database
cdOffset =
_ctx . CdOffsets . FirstOrDefault ( d = > ( d . Manufacturer = = _dev . Manufacturer | |
d . Manufacturer = = _dev . Manufacturer . Replace ( '/' , '-' ) ) & &
( d . Model = = _dev . Model | | d . Model = = _dev . Model . Replace ( '/' , '-' ) ) ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
Media . Info . CompactDisc . GetOffset ( cdOffset , _dbDev , _debug , _dev , dskType , _dumpLog , tracks , UpdateStatus ,
out int? driveOffset , out int? combinedOffset , out _supportsPlextorD8 ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +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." ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( tracks . Any ( t = > t . Type = = TrackType . Audio ) )
{
_dumpLog . WriteLine ( "Dump may not be correct." ) ;
UpdateStatus ? . Invoke ( "Dump may not be correct." ) ;
2020-01-06 22:29:01 +00:00
}
2022-03-06 13:29:38 +00:00
if ( _fixOffset )
_fixOffset = false ;
2020-01-06 22:29:01 +00:00
}
else
{
2022-03-06 13:29:38 +00:00
_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 ;
2020-01-06 22:29:01 +00:00
sectorsForOffset = offsetBytes / ( int ) sectorSize ;
if ( sectorsForOffset < 0 )
sectorsForOffset * = - 1 ;
if ( offsetBytes % sectorSize ! = 0 )
sectorsForOffset + + ;
2022-03-06 13:29:38 +00:00
}
}
else
{
offsetBytes = combinedOffset . Value ;
sectorsForOffset = offsetBytes / ( int ) sectorSize ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( sectorsForOffset < 0 )
sectorsForOffset * = - 1 ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if ( offsetBytes % sectorSize ! = 0 )
sectorsForOffset + + ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
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)." ) ;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( $"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)." ) ;
2020-01-06 22:29:01 +00:00
}
2022-03-06 13:29:38 +00:00
else
2020-01-06 22:29:01 +00:00
{
2022-03-06 13:29:38 +00:00
_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 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
discOffset = offsetBytes - driveOffset ;
2018-01-20 17:12:01 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( $"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)" ) ;
2019-12-25 21:03:21 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)" ) ;
2019-12-25 21:03:21 +00:00
}
2022-03-06 13:29:38 +00:00
}
2019-12-25 21:03:21 +00:00
2022-03-06 13:29:38 +00:00
if ( ! _fixOffset | |
tracks . All ( t = > t . Type ! = TrackType . Audio ) )
{
offsetBytes = 0 ;
sectorsForOffset = 0 ;
}
2019-12-26 03:45:09 +00:00
2022-03-06 13:29:38 +00:00
mhddLog = new MhddLog ( _outputPrefix + ".mhddlog.bin" , _dev , blocks , blockSize , _maximumReadable , _private ) ;
ibgLog = new IbgLog ( _outputPrefix + ".ibg" , 0x0008 ) ;
2019-12-26 03:45:09 +00:00
2022-03-06 13:29:38 +00:00
audioExtents = new ExtentsULong ( ) ;
2019-12-26 03:45:09 +00:00
2022-03-06 13:29:38 +00:00
foreach ( Track audioTrack in tracks . Where ( t = > t . Type = = TrackType . Audio ) )
{
audioExtents . Add ( audioTrack . StartSector , audioTrack . EndSector ) ;
}
2019-12-26 03:45:09 +00:00
2022-03-06 13:29:38 +00:00
// Set speed
if ( _speedMultiplier > = 0 )
{
_dumpLog . WriteLine ( $"Setting speed to {(_speed == 0 ? " MAX for data reading " : $" { _speed } x ")}." ) ;
UpdateStatus ? . Invoke ( $"Setting speed to {(_speed == 0 ? " MAX for data reading " : $" { _speed } x ")}." ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
_speed * = _speedMultiplier ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( _speed = = 0 | |
_speed > 0xFFFF )
_speed = 0xFFFF ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
_dev . SetCdSpeed ( out _ , RotationalControl . ClvAndImpureCav , ( ushort ) _speed , 0 , _dev . Timeout , out _ ) ;
}
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
// Start reading
start = DateTime . UtcNow ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( dskType = = MediaType . CDIREADY )
{
Track track0 = tracks . FirstOrDefault ( t = > t . Sequence = = 0 ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
track0 . Type = TrackType . CdMode2Formless ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( ! supportsLongSectors )
{
_dumpLog . WriteLine ( "Dumping CD-i Ready requires the output image format to support long sectors." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? .
Invoke ( "Dumping CD-i Ready requires the output image format to support long sectors." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
return ;
}
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( ! readcd )
{
_dumpLog . WriteLine ( "Dumping CD-i Ready requires the drive to support the READ CD command." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
StoppingErrorMessage ? .
Invoke ( "Dumping CD-i Ready requires the drive to support the READ CD command." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
return ;
}
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
_dev . ReadCd ( out cmdBuf , out _ , 0 , 2352 , 1 , MmcSectorTypes . AllTypes , false , false , true ,
MmcHeaderCodes . AllHeaders , true , true , MmcErrorField . None , MmcSubchannel . None , _dev . Timeout ,
out _ ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
hiddenData = IsData ( cmdBuf ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( ! hiddenData )
{
cdiReadyReadAsAudio = IsScrambledData ( cmdBuf , 0 , out combinedOffset ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( cdiReadyReadAsAudio )
{
offsetBytes = combinedOffset . Value ;
sectorsForOffset = offsetBytes / ( int ) sectorSize ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( sectorsForOffset < 0 )
sectorsForOffset * = - 1 ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( offsetBytes % sectorSize ! = 0 )
sectorsForOffset + + ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Enabling skipping CD-i Ready hole because drive returns data as audio." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( "Enabling skipping CD-i Ready hole because drive returns data as audio." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
_skipCdireadyHole = true ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
if ( driveOffset is null )
{
_dumpLog . WriteLine ( "Drive reading offset not found in database." ) ;
UpdateStatus ? . Invoke ( "Drive reading offset not found in database." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog .
WriteLine ( $"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( $"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)." ) ;
2020-06-25 01:13:02 +01:00
}
2022-03-06 13:29:38 +00:00
else
{
_dumpLog .
WriteLine ( $"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( $"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)." ) ;
discOffset = offsetBytes - driveOffset ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( $"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)" ) ;
UpdateStatus ? . Invoke ( $"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)" ) ;
}
2020-06-25 01:13:02 +01:00
}
2022-03-06 13:29:38 +00:00
}
if ( ! _skipCdireadyHole )
{
_dumpLog . WriteLine ( "There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them." ) ;
2020-06-25 01:13:02 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( "There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them." ) ;
2020-06-17 01:53:14 +01:00
}
2022-03-06 13:29:38 +00:00
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 ,
subchannelExtents , blocks , cdiReadyReadAsAudio , offsetBytes , sectorsForOffset ,
smallestPregapLbaPerTrack ) ;
}
2019-04-19 20:13:17 +01:00
2022-03-06 13:29:38 +00:00
ReadCdData ( audioExtents , blocks , blockSize , ref currentSpeed , currentTry , extents , ibgLog ,
ref imageWriteDuration , lastSector , leadOutExtents , ref maxSpeed , mhddLog , ref minSpeed ,
out newTrim , tracks [ 0 ] . Type ! = TrackType . Audio , offsetBytes , read6 , read10 , read12 , read16 ,
readcd , sectorsForOffset , subSize , supportedSubchannel , supportsLongSectors , ref totalDuration ,
tracks , subLog , desiredSubchannel , isrcs , ref mcn , subchannelExtents , smallestPregapLbaPerTrack ) ;
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
// TODO: Enable when underlying images support lead-outs
/ *
DumpCdLeadOuts ( blocks , blockSize , ref currentSpeed , currentTry , extents , ibgLog , ref imageWriteDuration ,
leadOutExtents , ref maxSpeed , mhddLog , ref minSpeed , read6 , read10 , read12 , read16 , readcd ,
supportedSubchannel , subSize , ref totalDuration , subLog , desiredSubchannel , isrcs , ref mcn , tracks ,
smallestPregapLbaPerTrack ) ;
* /
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
end = DateTime . UtcNow ;
mhddLog . Close ( ) ;
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +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
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"Dump finished in {(end - start).TotalSeconds} seconds." ) ;
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( $"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec." ) ;
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( $"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec." ) ;
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Dump finished in {0} seconds." , ( end - start ) . TotalSeconds ) ;
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Average dump speed {0:F3} KiB/sec." ,
blockSize * ( double ) ( blocks + 1 ) / 1024 / ( totalDuration / 1000 ) ) ;
2019-12-14 22:15:05 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Average write speed {0:F3} KiB/sec." ,
blockSize * ( double ) ( blocks + 1 ) / 1024 / imageWriteDuration ) ;
2018-04-10 02:39:41 +01:00
2022-03-06 13:29:38 +00:00
TrimCdUserData ( audioExtents , blockSize , currentTry , extents , newTrim , offsetBytes , read6 , read10 , read12 ,
read16 , readcd , sectorsForOffset , subSize , supportedSubchannel , supportsLongSectors ,
ref totalDuration , subLog , desiredSubchannel , tracks , isrcs , ref mcn , subchannelExtents ,
smallestPregapLbaPerTrack ) ;
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
RetryCdUserData ( audioExtents , blockSize , currentTry , extents , offsetBytes , readcd , sectorsForOffset ,
subSize , supportedSubchannel , ref totalDuration , subLog , desiredSubchannel , tracks , isrcs ,
ref mcn , subchannelExtents , smallestPregapLbaPerTrack , supportsLongSectors ) ;
foreach ( Tuple < ulong , ulong > leadoutExtent in leadOutExtents . ToArray ( ) )
{
for ( ulong e = leadoutExtent . Item1 ; e < = leadoutExtent . Item2 ; e + + )
subchannelExtents . Remove ( ( int ) e ) ;
}
2020-06-17 02:03:01 +01:00
2022-03-06 13:29:38 +00:00
if ( subchannelExtents . Count > 0 & &
_retryPasses > 0 & &
_retrySubchannel )
RetrySubchannel ( readcd , subSize , supportedSubchannel , ref totalDuration , subLog , desiredSubchannel ,
tracks , isrcs , ref mcn , subchannelExtents , smallestPregapLbaPerTrack ) ;
2017-12-19 20:33:03 +00:00
2022-03-06 13:29:38 +00:00
// Write media tags to image
if ( ! _aborted )
foreach ( KeyValuePair < MediaTagType , byte [ ] > tag in mediaTags )
{
if ( tag . Value is null )
2017-06-07 22:37:05 +01:00
{
2022-03-06 13:29:38 +00:00
AaruConsole . ErrorWriteLine ( "Error: Tag type {0} is null, skipping..." , tag . Key ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
continue ;
}
2019-08-10 14:35:14 +01:00
2022-03-06 13:29:38 +00:00
ret = outputOptical . WriteMediaTag ( tag . Value , tag . Key ) ;
2017-06-07 22:37:05 +01:00
2022-03-06 13:29:38 +00:00
if ( ret | | _force )
continue ;
2017-05-31 01:00:58 +01:00
2022-03-06 13:29:38 +00:00
// Cannot write tag to image
_dumpLog . WriteLine ( $"Cannot write tag {tag.Key}." ) ;
StoppingErrorMessage ? . Invoke ( outputOptical . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
return ;
}
2017-05-31 01:00:58 +01:00
2022-03-06 13:29:38 +00:00
_resume . BadBlocks . Sort ( ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +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
2022-03-06 13:29:38 +00:00
currentTry . Extents = ExtentsConverter . ToMetadata ( extents ) ;
2018-02-01 01:20:41 +00:00
2022-03-06 13:29:38 +00:00
_resume . BadSubchannels = new List < int > ( ) ;
_resume . BadSubchannels . AddRange ( subchannelExtents ) ;
_resume . BadSubchannels . Sort ( ) ;
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
if ( _generateSubchannels & &
outputOptical . SupportedSectorTags . Contains ( SectorTagType . CdSectorSubchannel ) & &
! _aborted )
Media . CompactDisc . GenerateSubchannels ( subchannelExtents , tracks , trackFlags , blocks , subLog , _dumpLog ,
InitProgress , UpdateProgress , EndProgress , outputOptical ) ;
2020-07-13 22:32:47 +01:00
2022-03-06 13:29:38 +00:00
// TODO: Disc ID
var metadata = new CommonTypes . Structs . ImageInfo
{
Application = "Aaru" ,
ApplicationVersion = Version . GetVersion ( )
} ;
2020-01-09 18:01:43 +00:00
2022-03-06 13:29:38 +00:00
if ( ! outputOptical . SetMetadata ( metadata ) )
ErrorMessage ? . Invoke ( "Error {0} setting metadata, continuing..." + Environment . NewLine +
outputOptical . ErrorMessage ) ;
2020-01-09 18:01:43 +00:00
2022-03-06 13:29:38 +00:00
outputOptical . SetDumpHardware ( _resume . Tries ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
if ( _preSidecar ! = null )
outputOptical . SetCicmMetadata ( _preSidecar ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
foreach ( KeyValuePair < byte , string > isrc in isrcs )
{
// TODO: Track tags
if ( ! outputOptical . WriteSectorTag ( Encoding . ASCII . GetBytes ( isrc . Value ) , isrc . Key ,
SectorTagType . CdTrackIsrc ) )
continue ;
2020-05-05 20:26:18 +01:00
2022-03-06 13:29:38 +00:00
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 20:26:18 +01:00
2022-03-06 13:29:38 +00:00
if ( mcn ! = null & &
outputOptical . 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 ) ;
}
2020-05-05 21:08:41 +01:00
2022-03-06 13:29:38 +00:00
foreach ( Track trk in tracks )
{
// Fix track starts in each session's first track
if ( tracks . Where ( t = > t . Session = = trk . Session ) . OrderBy ( t = > t . Sequence ) . FirstOrDefault ( ) . Sequence = =
trk . Sequence )
2020-11-05 01:21:54 +00:00
{
2022-03-06 13:29:38 +00:00
if ( trk . Sequence = = 1 )
2020-12-03 15:52:06 +00:00
continue ;
2022-03-06 13:29:38 +00:00
trk . StartSector - = trk . Pregap ;
trk . Indexes [ 0 ] = ( int ) trk . StartSector ;
continue ;
2020-11-05 01:21:54 +00:00
}
2022-03-06 13:29:38 +00:00
if ( trk . Indexes . TryGetValue ( 0 , out int idx0 ) & &
trk . Indexes . TryGetValue ( 1 , out int idx1 ) & &
idx0 = = idx1 )
trk . Indexes . Remove ( 0 ) ;
}
2020-11-05 01:21:54 +00:00
2022-03-06 13:29:38 +00:00
( outputOptical as IWritableOpticalImage ) . SetTracks ( tracks . ToList ( ) ) ;
2017-12-19 20:33:03 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog . WriteLine ( "Closing output file." ) ;
UpdateStatus ? . Invoke ( "Closing output file." ) ;
DateTime closeStart = DateTime . Now ;
outputOptical . Close ( ) ;
DateTime closeEnd = DateTime . Now ;
UpdateStatus ? . Invoke ( $"Closed in {(closeEnd - closeStart).TotalSeconds} seconds." ) ;
2020-05-04 03:11:40 +01:00
2022-03-06 13:29:38 +00:00
subLog ? . Close ( ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
if ( _aborted )
{
_dumpLog . WriteLine ( "Aborted!" ) ;
2017-05-31 01:00:58 +01:00
2022-03-06 13:29:38 +00:00
return ;
}
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
double totalChkDuration = 0 ;
2018-01-20 17:12:01 +00:00
2022-03-06 13:29:38 +00:00
if ( _metadata )
WriteOpticalSidecar ( blockSize , blocks , dskType , null , mediaTags , sessions , out totalChkDuration ,
discOffset ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
end = DateTime . UtcNow ;
UpdateStatus ? . Invoke ( "" ) ;
2019-11-18 20:59:16 +00:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
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
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( $"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec." ) ;
2020-06-25 01:47:25 +01:00
2022-03-06 13:29:38 +00:00
if ( maxSpeed > 0 )
UpdateStatus ? . Invoke ( $"Fastest speed burst: {maxSpeed:F3} MiB/sec." ) ;
2020-06-25 01:47:25 +01:00
2022-03-06 13:29:38 +00:00
if ( minSpeed > 0 & &
minSpeed < double . MaxValue )
UpdateStatus ? . Invoke ( $"Slowest speed burst: {minSpeed:F3} MiB/sec." ) ;
2017-06-20 05:48:09 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? . Invoke ( $"{_resume.BadBlocks.Count} sectors could not be read." ) ;
UpdateStatus ? . Invoke ( $"{_resume.BadSubchannels.Count} subchannels could not be read." ) ;
UpdateStatus ? . Invoke ( "" ) ;
Statistics . AddMedia ( dskType , true ) ;
2017-05-31 01:00:58 +01:00
}
2017-12-19 20:33:03 +00:00
}