2017-05-31 01:00:58 +01:00
// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
2017-05-31 01:00:58 +01:00
// ----------------------------------------------------------------------------
//
// Filename : SBC.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 SCSI Block devices.
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 ;
using System.IO ;
2017-12-21 07:08:26 +00:00
using System.Linq ;
2017-12-21 14:30:38 +00:00
using System.Xml.Serialization ;
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.Metadata ;
using Aaru.CommonTypes.Structs ;
using Aaru.CommonTypes.Structs.Devices.SCSI ;
using Aaru.Console ;
2020-02-29 18:03:35 +00:00
using Aaru.Core.Logging ;
2020-04-18 18:20:43 +01:00
using Aaru.Core.Media.Detection ;
2020-02-27 00:33:26 +00:00
using Aaru.Decoders.SCSI ;
2020-06-16 21:13:58 +01:00
using Aaru.Decoders.SCSI.MMC ;
2020-02-27 00:33:26 +00:00
using Aaru.Devices ;
2017-12-21 14:30:38 +00:00
using Schemas ;
2020-03-11 15:28:04 +00:00
using DeviceReport = Aaru . Core . Devices . Report . DeviceReport ;
2020-02-27 00:33:26 +00:00
using MediaType = Aaru . CommonTypes . MediaType ;
using TrackType = Aaru . CommonTypes . Enums . TrackType ;
using Version = Aaru . CommonTypes . Interop . Version ;
2017-05-31 01:00:58 +01:00
2020-02-27 00:33:26 +00:00
namespace Aaru.Core.Devices.Dumping
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
/// <summary>Implements dumping SCSI Block Commands and Reduced Block Commands devices</summary>
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 SCSI Block Commands device or a Reduced Block Commands devices</summary>
2017-12-23 01:46:08 +00:00
/// <param name="opticalDisc">If device contains an optical disc (e.g. DVD or BD)</param>
2018-01-19 01:21:01 +00:00
/// <param name="mediaTags">Media tags as retrieved in MMC layer</param>
2017-12-23 01:46:08 +00:00
/// <param name="dskType">Disc type as detected in SCSI or MMC layer</param>
2020-01-30 23:04:37 +00:00
internal void Sbc ( Dictionary < MediaTagType , byte [ ] > mediaTags , MediaType dskType , bool opticalDisc )
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
bool sense ;
byte scsiMediumType = 0 ;
byte scsiDensityCode = 0 ;
bool containsFloppyPage = false ;
const ushort SBC_PROFILE = 0x0001 ;
DateTime start ;
DateTime end ;
double totalDuration = 0 ;
double currentSpeed = 0 ;
double maxSpeed = double . MinValue ;
double minSpeed = double . MaxValue ;
byte [ ] readBuffer ;
2018-04-02 23:08:26 +01:00
Modes . DecodedMode ? decMode = null ;
2020-06-16 21:13:58 +01:00
bool ret ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( opticalDisc )
switch ( dskType )
2019-07-13 20:46:25 +01:00
{
case MediaType . REV35 :
case MediaType . REV70 :
case MediaType . REV120 :
opticalDisc = false ;
2019-11-18 20:59:16 +00:00
2019-07-13 20:46:25 +01:00
break ;
}
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Initializing reader." ) ;
2020-07-13 18:54:41 +01:00
var scsiReader = new Reader ( _dev , _dev . Timeout , null , _errorLog , _dumpRaw ) ;
2019-11-18 20:59:16 +00:00
ulong blocks = scsiReader . GetDeviceBlocks ( ) ;
uint blockSize = scsiReader . LogicalBlockSize ;
if ( ! opticalDisc )
2017-05-31 01:00:58 +01:00
{
2018-01-19 01:21:01 +00:00
mediaTags = new Dictionary < MediaTagType , byte [ ] > ( ) ;
2019-12-25 18:07:05 +00:00
if ( _dev . IsUsb & &
_dev . UsbDescriptors ! = null )
2019-11-18 20:59:16 +00:00
mediaTags . Add ( MediaTagType . USB_Descriptors , null ) ;
2019-12-25 18:07:05 +00:00
if ( _dev . Type = = DeviceType . ATAPI )
2019-11-18 20:59:16 +00:00
mediaTags . Add ( MediaTagType . ATAPI_IDENTIFY , null ) ;
2019-12-25 18:07:05 +00:00
if ( _dev . IsPcmcia & &
_dev . Cis ! = null )
2019-11-18 20:59:16 +00:00
mediaTags . Add ( MediaTagType . PCMCIA_CIS , null ) ;
2018-01-19 01:21:01 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ScsiInquiry ( out byte [ ] cmdBuf , out _ ) ;
2020-03-11 15:28:04 +00:00
if ( _private )
cmdBuf = DeviceReport . ClearInquiry ( cmdBuf ) ;
2018-01-19 01:21:01 +00:00
mediaTags . Add ( MediaTagType . SCSI_INQUIRY , cmdBuf ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense )
2017-05-31 01:00:58 +01:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting MODE SENSE (10)." ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting MODE SENSE (10)." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense10 ( out cmdBuf , out _ , false , true , ScsiModeSensePageControl . Current , 0x3F ,
0xFF , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense | |
2019-12-25 18:07:05 +00:00
_dev . Error )
sense = _dev . ModeSense10 ( out cmdBuf , out _ , false , true , ScsiModeSensePageControl . Current , 0x3F ,
0x00 , 5 , out _ ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
if ( Modes . DecodeMode10 ( cmdBuf , _dev . ScsiType ) . HasValue )
2017-05-31 01:00:58 +01:00
{
2018-01-19 01:21:01 +00:00
mediaTags . Add ( MediaTagType . SCSI_MODESENSE_10 , cmdBuf ) ;
2019-12-25 18:07:05 +00:00
decMode = Modes . DecodeMode10 ( cmdBuf , _dev . ScsiType ) ;
2017-05-31 01:00:58 +01:00
}
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting MODE SENSE (6)." ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting MODE SENSE (6)." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense6 ( out cmdBuf , out _ , false , ScsiModeSensePageControl . Current , 0x3F , 0x00 , 5 ,
out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
sense = _dev . ModeSense6 ( out cmdBuf , out _ , false , ScsiModeSensePageControl . Current , 0x3F , 0x00 ,
5 , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
sense = _dev . ModeSense ( out cmdBuf , out _ , 5 , out _ ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
if ( Modes . DecodeMode6 ( cmdBuf , _dev . ScsiType ) . HasValue )
2017-05-31 01:00:58 +01:00
{
2018-01-19 01:21:01 +00:00
mediaTags . Add ( MediaTagType . SCSI_MODESENSE_6 , cmdBuf ) ;
2019-12-25 18:07:05 +00:00
decMode = Modes . DecodeMode6 ( cmdBuf , _dev . ScsiType ) ;
2017-05-31 01:00:58 +01:00
}
2018-01-19 01:21:01 +00:00
2019-11-18 20:59:16 +00:00
if ( decMode . HasValue )
2018-01-19 01:21:01 +00:00
{
2019-11-18 20:59:16 +00:00
scsiMediumType = ( byte ) decMode . Value . Header . MediumType ;
if ( decMode . Value . Header . BlockDescriptors ! = null & &
decMode . Value . Header . BlockDescriptors . Length > = 1 )
scsiDensityCode = ( byte ) decMode . Value . Header . BlockDescriptors [ 0 ] . Density ;
2019-10-26 01:57:55 +01:00
containsFloppyPage = decMode . Value . Pages ! = null & &
decMode . Value . Pages . Aggregate ( containsFloppyPage ,
2019-11-18 20:59:16 +00:00
( current , modePage ) = >
current | ( modePage . Page = = 0x05 ) ) ;
2017-05-31 01:00:58 +01:00
}
}
}
2019-11-18 20:59:16 +00:00
if ( dskType = = MediaType . Unknown )
2020-01-31 23:13:11 +00:00
dskType = MediaTypeFromDevice . GetFromScsi ( ( byte ) _dev . ScsiType , _dev . Manufacturer , _dev . Model ,
2020-02-02 18:48:40 +00:00
scsiMediumType , scsiDensityCode , blocks + 1 , blockSize ) ;
2017-12-21 23:00:30 +00:00
2020-04-18 18:20:43 +01:00
if ( _dev . ScsiType = = PeripheralDeviceTypes . MultiMediaDevice )
MMC . DetectDiscType ( ref dskType , 1 , null , _dev , out _ , out _ , 0 ) ;
2020-01-30 23:16:28 +00:00
switch ( dskType )
{
// Hi-MD devices show the disks while in Hi-MD mode, but they cannot be read using any known command
// SonicStage changes the device mode, so it is no longer a mass storage device, and can only read
// tracks written by that same application ID (changes between computers).
case MediaType . MD :
_dumpLog . WriteLine ( "MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped." ) ;
StoppingErrorMessage ? .
Invoke ( "MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped." ) ;
return ;
case MediaType . Unknown when _dev . IsUsb & & containsFloppyPage :
dskType = MediaType . FlashDrive ;
break ;
}
2019-04-20 01:43:58 +01:00
2020-01-30 23:10:21 +00:00
if ( scsiReader . FindReadCommand ( ) )
{
_dumpLog . WriteLine ( "ERROR: Cannot find correct read command: {0}." , scsiReader . ErrorMessage ) ;
StoppingErrorMessage ? . Invoke ( "Unable to read medium." ) ;
return ;
}
if ( blocks ! = 0 & &
blockSize ! = 0 )
{
blocks + + ;
2020-07-12 15:15:22 +01:00
ulong totalSize = blocks * blockSize ;
if ( totalSize > 1099511627776 )
UpdateStatus ? .
Invoke ( $"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)" ) ;
else if ( totalSize > 1073741824 )
UpdateStatus ? .
Invoke ( $"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)" ) ;
else if ( totalSize > 1048576 )
UpdateStatus ? .
Invoke ( $"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)" ) ;
else if ( totalSize > 1024 )
UpdateStatus ? .
Invoke ( $"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)" ) ;
else
UpdateStatus ? .
Invoke ( $"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)" ) ;
2020-01-30 23:10:21 +00:00
}
// Check how many blocks to read, if error show and return
if ( scsiReader . GetBlocksToRead ( _maximumReadable ) )
{
_dumpLog . WriteLine ( "ERROR: Cannot get blocks to read: {0}." , scsiReader . ErrorMessage ) ;
StoppingErrorMessage ? . Invoke ( scsiReader . ErrorMessage ) ;
return ;
}
uint blocksToRead = scsiReader . BlocksToRead ;
uint logicalBlockSize = blockSize ;
uint physicalBlockSize = scsiReader . PhysicalBlockSize ;
if ( blocks = = 0 )
{
_dumpLog . WriteLine ( "ERROR: Unable to read medium or empty medium present..." ) ;
StoppingErrorMessage ? . Invoke ( "Unable to read medium or empty medium present..." ) ;
return ;
}
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"Device reports {blocks} blocks ({blocks * blockSize} bytes)." ) ;
UpdateStatus ? . Invoke ( $"Device can read {blocksToRead} blocks at a time." ) ;
UpdateStatus ? . Invoke ( $"Device reports {blockSize} bytes per logical block." ) ;
UpdateStatus ? . Invoke ( $"Device reports {scsiReader.LongBlockSize} bytes per physical block." ) ;
2019-12-25 18:07:05 +00:00
UpdateStatus ? . Invoke ( $"SCSI device type: {_dev.ScsiType}." ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"SCSI medium type: {scsiMediumType}." ) ;
UpdateStatus ? . Invoke ( $"SCSI density type: {scsiDensityCode}." ) ;
UpdateStatus ? . Invoke ( $"SCSI floppy mode page present: {containsFloppyPage}." ) ;
UpdateStatus ? . Invoke ( $"Media identified as {dskType}" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Device reports {0} blocks ({1} bytes)." , blocks , blocks * blockSize ) ;
_dumpLog . WriteLine ( "Device can read {0} blocks at a time." , blocksToRead ) ;
_dumpLog . WriteLine ( "Device reports {0} bytes per logical block." , blockSize ) ;
_dumpLog . WriteLine ( "Device reports {0} bytes per physical block." , scsiReader . LongBlockSize ) ;
_dumpLog . WriteLine ( "SCSI device type: {0}." , _dev . ScsiType ) ;
_dumpLog . WriteLine ( "SCSI medium type: {0}." , scsiMediumType ) ;
_dumpLog . WriteLine ( "SCSI density type: {0}." , scsiDensityCode ) ;
_dumpLog . WriteLine ( "SCSI floppy mode page present: {0}." , containsFloppyPage ) ;
_dumpLog . WriteLine ( "Media identified as {0}." , dskType ) ;
2017-12-21 23:00:30 +00:00
2019-11-18 20:59:16 +00:00
uint longBlockSize = scsiReader . LongBlockSize ;
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
if ( _dumpRaw )
2019-11-18 20:59:16 +00:00
if ( blockSize = = longBlockSize )
2017-05-31 01:00:58 +01:00
{
2019-04-20 01:43:58 +01:00
ErrorMessage ? . Invoke ( ! scsiReader . CanReadRaw
2019-11-18 20:59:16 +00:00
? "Device doesn't seem capable of reading raw data from media."
: "Device is capable of reading raw data but I've been unable to guess correct sector size." ) ;
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
if ( ! _force )
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
StoppingErrorMessage ? .
Invoke ( "Not continuing. If you want to continue reading cooked data when raw is not available use the force option." ) ;
2017-05-31 01:00:58 +01:00
// TODO: Exit more gracefully
return ;
}
2019-04-20 01:43:58 +01:00
ErrorMessage ? . Invoke ( "Continuing dumping cooked data." ) ;
2017-05-31 01:00:58 +01:00
}
else
{
2017-12-21 23:00:30 +00:00
// Only a block will be read, but it contains 16 sectors and command expect sector number not block number
2019-11-18 20:59:16 +00:00
blocksToRead = ( uint ) ( longBlockSize = = 37856 ? 16 : 1 ) ;
UpdateStatus ? .
Invoke ( $"Reading {longBlockSize} raw bytes ({blockSize * blocksToRead} cooked bytes) per sector." ) ;
2017-05-31 01:00:58 +01:00
physicalBlockSize = longBlockSize ;
2019-11-18 20:59:16 +00:00
blockSize = longBlockSize ;
2017-05-31 01:00:58 +01:00
}
2017-12-19 20:33:03 +00:00
2020-06-16 21:13:58 +01:00
ret = true ;
2018-01-19 01:21:01 +00:00
2019-11-18 20:59:16 +00:00
foreach ( MediaTagType tag in mediaTags . Keys )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _outputPlugin . SupportedMediaTags . Contains ( tag ) )
2019-11-18 20:59:16 +00:00
continue ;
2018-01-19 01:21:01 +00:00
ret = false ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( $"Output format does not support {tag}." ) ;
2019-04-20 01:43:58 +01:00
ErrorMessage ? . Invoke ( $"Output format does not support {tag}." ) ;
2018-01-19 01:21:01 +00:00
}
2019-11-18 20:59:16 +00:00
if ( ! ret )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _force )
2019-04-20 01:43:58 +01:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Several media tags not supported, continuing..." ) ;
2019-04-20 01:43:58 +01:00
ErrorMessage ? . Invoke ( "Several media tags not supported, continuing..." ) ;
}
else
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Several media tags not supported, not continuing..." ) ;
2019-04-20 01:43:58 +01:00
StoppingErrorMessage ? . Invoke ( "Several media tags not supported, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 01:43:58 +01:00
return ;
}
2018-01-19 01:21:01 +00:00
}
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"Reading {blocksToRead} sectors at a time." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Reading {0} sectors at a time." , blocksToRead ) ;
2017-05-31 01:00:58 +01:00
2020-03-11 15:28:04 +00:00
var mhddLog = new MhddLog ( _outputPrefix + ".mhddlog.bin" , _dev , blocks , blockSize , blocksToRead , _private ) ;
2019-12-25 18:07:05 +00:00
var ibgLog = new IbgLog ( _outputPrefix + ".ibg" , SBC_PROFILE ) ;
ret = _outputPlugin . Create ( _outputPath , dskType , _formatOptions , blocks , blockSize ) ;
2018-01-19 01:21:01 +00:00
// Cannot create image
2019-11-18 20:59:16 +00:00
if ( ! ret )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Error creating output image, not continuing." ) ;
_dumpLog . WriteLine ( _outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 01:43:58 +01:00
StoppingErrorMessage ? . Invoke ( "Error creating output image, not continuing." + Environment . NewLine +
2019-12-25 18:07:05 +00:00
_outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2018-01-19 01:21:01 +00:00
return ;
}
2017-05-31 01:00:58 +01:00
2018-04-02 23:08:26 +01:00
start = DateTime . UtcNow ;
2018-02-02 15:32:53 +00:00
double imageWriteDuration = 0 ;
2018-02-02 18:44:28 +00:00
2019-11-18 20:59:16 +00:00
if ( opticalDisc )
2019-01-20 20:11:10 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _outputPlugin is IWritableOpticalImage opticalPlugin )
2019-10-26 01:57:55 +01:00
{
2020-06-16 21:13:58 +01:00
sense = _dev . ReadDiscInformation ( out readBuffer , out _ , MmcDiscInformationDataTypes . DiscInformation ,
_dev . Timeout , out _ ) ;
if ( ! sense )
2018-01-19 01:21:01 +00:00
{
2020-06-16 21:13:58 +01:00
DiscInformation . StandardDiscInformation ? discInformation =
DiscInformation . Decode000b ( readBuffer ) ;
// This means the output image can store sessions that are not on a CD, like on a DVD or Blu-ray
bool canStoreNotCdSessions =
opticalPlugin . OpticalCapabilities . HasFlag ( OpticalImageCapabilities . CanStoreNotCdSessions ) ;
// This means the output image can store tracks that are not on a CD, like on a DVD or Blu-ray
bool canStoreNotCdTracks =
opticalPlugin . OpticalCapabilities . HasFlag ( OpticalImageCapabilities . CanStoreNotCdTracks ) ;
if ( discInformation . HasValue )
2019-01-20 20:11:10 +00:00
{
2020-06-16 21:13:58 +01:00
if ( discInformation ? . Sessions > 1 & &
! canStoreNotCdSessions )
{
if ( _force )
{
_dumpLog .
WriteLine ( "Image does not support multiple sessions in non Compact Disc dumps, continuing..." ) ;
ErrorMessage ? .
Invoke ( "Image does not support multiple sessions in non Compact Disc dumps, continuing..." ) ;
}
else
{
_dumpLog .
WriteLine ( "Image does not support multiple sessions in non Compact Disc dumps, not continuing..." ) ;
StoppingErrorMessage ? .
Invoke ( "Image does not support multiple sessions in non Compact Disc dumps, not continuing..." ) ;
return ;
}
}
if ( ( discInformation ? . LastTrackLastSession - discInformation ? . FirstTrackNumber > 0 | |
discInformation ? . FirstTrackNumber ! = 1 ) & &
! canStoreNotCdTracks )
{
if ( _force )
{
_dumpLog .
WriteLine ( "Image does not support multiple tracks in non Compact Disc dumps, continuing..." ) ;
ErrorMessage ? .
Invoke ( "Image does not support multiple tracks in non Compact Disc dumps, continuing..." ) ;
}
else
{
_dumpLog .
WriteLine ( "Image does not support multiple tracks in non Compact Disc dumps, not continuing..." ) ;
StoppingErrorMessage ? .
Invoke ( "Image does not support multiple tracks in non Compact Disc dumps, not continuing..." ) ;
return ;
}
}
UpdateStatus ? . Invoke ( "Building track map..." ) ;
_dumpLog . WriteLine ( "Building track map..." ) ;
List < Track > tracks = new List < Track > ( ) ;
for ( ushort tno = discInformation . Value . FirstTrackNumber ;
tno < = discInformation ? . LastTrackLastSession ; tno + + )
{
sense = _dev . ReadTrackInformation ( out readBuffer , out _ , false ,
TrackInformationType . LogicalTrackNumber , tno ,
_dev . Timeout , out _ ) ;
if ( sense )
continue ;
var trkInfo = TrackInformation . Decode ( readBuffer ) ;
if ( trkInfo is null )
continue ;
var track = new Track
{
TrackSequence = trkInfo . LogicalTrackNumber ,
TrackSession = ( ushort ) ( canStoreNotCdSessions ? trkInfo . SessionNumber : 1 ) ,
TrackType = TrackType . Data , TrackStartSector = trkInfo . LogicalTrackStartAddress ,
TrackEndSector = ( trkInfo . LogicalTrackSize + trkInfo . LogicalTrackStartAddress ) - 1 ,
TrackRawBytesPerSector = ( int ) blockSize , TrackBytesPerSector = ( int ) blockSize ,
TrackSubchannelType = TrackSubchannelType . None
} ;
tracks . Add ( track ) ;
}
tracks = tracks . OrderBy ( t = > t . TrackSequence ) . ToList ( ) ;
#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 ( canStoreNotCdTracks )
{
ret = opticalPlugin . SetTracks ( tracks ) ;
if ( ! ret )
{
_dumpLog . WriteLine ( "Error sending tracks to output image, not continuing." ) ;
_dumpLog . WriteLine ( opticalPlugin . ErrorMessage ) ;
StoppingErrorMessage ? .
Invoke ( "Error sending tracks to output image, not continuing." +
Environment . NewLine + opticalPlugin . ErrorMessage ) ;
return ;
}
}
else
opticalPlugin . SetTracks ( new List < Track >
{
new Track
{
TrackBytesPerSector = ( int ) blockSize , TrackEndSector = blocks - 1 ,
TrackSequence = 1 , TrackRawBytesPerSector = ( int ) blockSize ,
TrackSubchannelType = TrackSubchannelType . None , TrackSession = 1 ,
TrackType = TrackType . Data
}
} ) ;
2019-01-20 20:11:10 +00:00
}
2020-06-16 21:13:58 +01:00
}
2019-10-26 01:57:55 +01:00
}
2019-01-20 20:11:10 +00:00
else
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "The specified plugin does not support storing optical disc images.." ) ;
2019-04-20 01:43:58 +01:00
StoppingErrorMessage ? . Invoke ( "The specified plugin does not support storing optical disc images." ) ;
2019-11-18 20:59:16 +00:00
2019-01-20 20:11:10 +00:00
return ;
}
}
2019-11-18 20:59:16 +00:00
else if ( decMode ? . Pages ! = null )
2017-11-26 18:23:50 +00:00
{
2019-11-18 20:59:16 +00:00
bool setGeometry = false ;
2018-01-19 01:21:01 +00:00
2019-11-18 20:59:16 +00:00
foreach ( Modes . ModePage page in decMode . Value . Pages )
if ( page . Page = = 0x04 & &
page . Subpage = = 0x00 )
2018-01-19 01:21:01 +00:00
{
2019-11-18 20:59:16 +00:00
Modes . ModePage_04 ? rigidPage = Modes . DecodeModePage_04 ( page . PageResponse ) ;
if ( ! rigidPage . HasValue | | setGeometry )
continue ;
2018-01-19 01:21:01 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Setting geometry to {0} cylinders, {1} heads, {2} sectors per track" ,
rigidPage . Value . Cylinders , rigidPage . Value . Heads ,
( uint ) ( blocks / ( rigidPage . Value . Cylinders * rigidPage . Value . Heads ) ) ) ;
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
Invoke ( $"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track" ) ;
2019-12-25 18:07:05 +00:00
_outputPlugin . SetGeometry ( rigidPage . Value . Cylinders , rigidPage . Value . Heads ,
( uint ) ( blocks / ( rigidPage . Value . Cylinders * rigidPage . Value . Heads ) ) ) ;
2018-01-19 01:21:01 +00:00
setGeometry = true ;
}
2019-11-18 20:59:16 +00:00
else if ( page . Page = = 0x05 & &
page . Subpage = = 0x00 )
2018-01-19 01:21:01 +00:00
{
2019-11-18 20:59:16 +00:00
Modes . ModePage_05 ? flexiblePage = Modes . DecodeModePage_05 ( page . PageResponse ) ;
if ( ! flexiblePage . HasValue )
continue ;
2018-01-19 01:21:01 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Setting geometry to {0} cylinders, {1} heads, {2} sectors per track" ,
flexiblePage . Value . Cylinders , flexiblePage . Value . Heads ,
flexiblePage . Value . SectorsPerTrack ) ;
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
Invoke ( $"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track" ) ;
2019-12-25 18:07:05 +00:00
_outputPlugin . SetGeometry ( flexiblePage . Value . Cylinders , flexiblePage . Value . Heads ,
flexiblePage . Value . SectorsPerTrack ) ;
2019-11-18 20:59:16 +00:00
2018-01-19 01:21:01 +00:00
setGeometry = true ;
}
2017-11-26 18:23:50 +00:00
}
2017-12-19 20:33:03 +00:00
2017-06-20 05:48:09 +01:00
DumpHardwareType currentTry = null ;
2019-11-18 20:59:16 +00:00
ExtentsULong extents = null ;
2019-12-25 18:07:05 +00:00
ResumeSupport . Process ( true , _dev . IsRemovable , blocks , _dev . Manufacturer , _dev . Model , _dev . Serial ,
2020-03-11 15:28:04 +00:00
_dev . PlatformId , ref _resume , ref currentTry , ref extents , _dev . FirmwareRevision ,
_private ) ;
2019-11-18 20:59:16 +00:00
if ( currentTry = = null | |
extents = = null )
2019-04-20 14:02:25 +01:00
{
StoppingErrorMessage ? . Invoke ( "Could not process resume file, not continuing..." ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
}
2017-12-19 20:33:03 +00:00
2019-12-25 18:07:05 +00:00
if ( _resume . NextBlock > 0 )
2019-04-20 01:43:58 +01: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-04-20 01:43:58 +01:00
}
2017-06-20 05:48:09 +01:00
2019-12-26 03:45:09 +00:00
// Set speed
if ( _speedMultiplier > = 0 )
{
2020-03-09 21:25:18 +00:00
_dumpLog . WriteLine ( $"Setting speed to {(_speed == 0 ? " MAX . " : $" { _speed } x ")}." ) ;
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 _ ) ;
}
2019-11-18 20:59:16 +00:00
bool newTrim = false ;
DateTime timeSpeedStart = DateTime . UtcNow ;
ulong sectorSpeedStart = 0 ;
2019-04-20 01:43:58 +01:00
InitProgress ? . Invoke ( ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
for ( ulong i = _resume . NextBlock ; i < blocks ; i + = blocksToRead )
2017-05-31 01:00:58 +01:00
{
2019-12-25 18:07:05 +00:00
if ( _aborted )
2017-06-20 05:48:09 +01:00
{
2017-12-21 14:30:38 +00:00
currentTry . Extents = ExtentsConverter . ToMetadata ( extents ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Aborted!" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Aborted!" ) ;
2019-11-18 20:59:16 +00:00
2017-05-31 01:00:58 +01:00
break ;
2017-06-20 05:48:09 +01:00
}
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( blocks - i < blocksToRead )
blocksToRead = ( uint ) ( blocks - i ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
if ( currentSpeed > maxSpeed & &
currentSpeed ! = 0 )
maxSpeed = currentSpeed ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( currentSpeed < minSpeed & &
currentSpeed ! = 0 )
minSpeed = currentSpeed ;
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
UpdateProgress ? . Invoke ( $"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)" , ( long ) i ,
( long ) blocks ) ;
sense = scsiReader . ReadBlocks ( out readBuffer , i , blocksToRead , out double cmdDuration ) ;
2017-05-31 01:00:58 +01:00
totalDuration + = cmdDuration ;
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
2017-05-31 01:00:58 +01:00
{
mhddLog . Write ( i , cmdDuration ) ;
ibgLog . Write ( i , currentSpeed * 1024 ) ;
2019-11-18 20:59:16 +00:00
DateTime writeStart = DateTime . Now ;
2019-12-25 18:07:05 +00:00
_outputPlugin . WriteSectors ( readBuffer , i , blocksToRead ) ;
2018-02-02 15:32:53 +00:00
imageWriteDuration + = ( DateTime . Now - writeStart ) . TotalSeconds ;
2017-06-20 05:48:09 +01:00
extents . Add ( i , blocksToRead , true ) ;
2017-05-31 01:00:58 +01:00
}
else
{
2020-05-13 10:45:09 +00:00
if ( _dev . Manufacturer . ToLowerInvariant ( ) = = "insite" )
{
_resume . BadBlocks . Add ( i ) ;
_resume . NextBlock + + ;
_aborted = true ;
_dumpLog ? .
WriteLine ( "INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately." ) ;
UpdateStatus ? .
Invoke ( "INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropraitely" ) ;
continue ;
}
2017-05-31 01:00:58 +01:00
// TODO: Reset device after X errors
2019-12-25 18:07:05 +00:00
if ( _stopOnError )
2019-11-18 20:59:16 +00:00
return ; // TODO: Return more cleanly
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
if ( i + _skip > blocks )
_skip = ( uint ) ( blocks - i ) ;
2018-04-02 23:08:26 +01:00
2017-05-31 01:00:58 +01:00
// Write empty data
2019-11-18 20:59:16 +00:00
DateTime writeStart = DateTime . Now ;
2019-12-25 18:07:05 +00:00
_outputPlugin . WriteSectors ( new byte [ blockSize * _skip ] , i , _skip ) ;
2018-02-02 15:32:53 +00:00
imageWriteDuration + = ( DateTime . Now - writeStart ) . TotalSeconds ;
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
for ( ulong b = i ; b < i + _skip ; b + + )
_resume . BadBlocks . Add ( b ) ;
2017-12-19 20:33:03 +00:00
2017-12-21 23:00:30 +00:00
mhddLog . Write ( i , cmdDuration < 500 ? 65535 : cmdDuration ) ;
2017-05-31 01:00:58 +01:00
ibgLog . Write ( i , 0 ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Skipping {0} blocks from errored block {1}." , _skip , i ) ;
i + = _skip - blocksToRead ;
2019-11-18 20:59:16 +00:00
newTrim = true ;
2017-05-31 01:00:58 +01:00
}
2019-12-25 18:07:05 +00:00
sectorSpeedStart + = blocksToRead ;
_resume . NextBlock = i + blocksToRead ;
2019-11-18 20:59:16 +00:00
double elapsed = ( DateTime . UtcNow - timeSpeedStart ) . TotalSeconds ;
2019-01-27 17:47:40 +00:00
2019-11-18 20:59:16 +00:00
if ( elapsed < 1 )
continue ;
2019-01-27 17:47:40 +00:00
2019-12-25 18:07:05 +00:00
currentSpeed = ( sectorSpeedStart * blockSize ) / ( 1048576 * elapsed ) ;
2019-01-27 17:47:40 +00:00
sectorSpeedStart = 0 ;
2019-11-18 20:59:16 +00:00
timeSpeedStart = DateTime . UtcNow ;
2017-05-31 01:00:58 +01:00
}
2017-12-19 20:33:03 +00:00
2017-05-31 01:00:58 +01:00
end = DateTime . UtcNow ;
2019-04-20 01:43:58 +01:00
EndProgress ? . Invoke ( ) ;
2017-05-31 01:00:58 +01:00
mhddLog . Close ( ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
ibgLog . Close ( _dev , blocks , blockSize , ( end - start ) . TotalSeconds , currentSpeed * 1024 ,
2020-05-13 10:45:09 +00:00
( blockSize * ( double ) ( blocks + 1 ) ) / 1024 / ( totalDuration / 1000 ) , _devicePath ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"Dump finished in {(end - start).TotalSeconds} seconds." ) ;
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
2019-12-25 18:07:05 +00:00
Invoke ( $"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec." ) ;
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
2019-12-25 18:07:05 +00:00
Invoke ( $"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Dump finished in {0} seconds." , ( end - start ) . TotalSeconds ) ;
2019-11-18 20:59:16 +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-11-18 20:59:16 +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 ) ;
2017-05-31 01:00:58 +01:00
2018-04-10 02:39:41 +01:00
#region Trimming
2019-12-25 18:07:05 +00:00
if ( _resume . BadBlocks . Count > 0 & &
! _aborted & &
2020-01-04 02:28:17 +00:00
_trim & &
2019-11-18 20:59:16 +00:00
newTrim )
2018-04-10 02:39:41 +01:00
{
start = DateTime . UtcNow ;
2020-03-11 16:01:33 +00:00
UpdateStatus ? . Invoke ( "Trimming skipped sectors" ) ;
_dumpLog . WriteLine ( "Trimming skipped sectors" ) ;
2018-04-10 02:39:41 +01:00
2019-12-25 18:07:05 +00:00
ulong [ ] tmpArray = _resume . BadBlocks . ToArray ( ) ;
2019-04-20 01:43:58 +01:00
InitProgress ? . Invoke ( ) ;
2019-11-18 20:59:16 +00:00
foreach ( ulong badSector in tmpArray )
2018-04-10 02:39:41 +01:00
{
2019-12-25 18:07:05 +00:00
if ( _aborted )
2018-04-10 02:39:41 +01:00
{
currentTry . Extents = ExtentsConverter . ToMetadata ( extents ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Aborted!" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Aborted!" ) ;
2019-11-18 20:59:16 +00:00
2018-04-10 02:39:41 +01:00
break ;
}
2019-04-20 23:05:49 +01:00
PulseProgress ? . Invoke ( $"Trimming sector {badSector}" ) ;
2018-04-10 02:39:41 +01:00
2019-11-18 20:59:16 +00:00
sense = scsiReader . ReadBlock ( out readBuffer , badSector , out double cmdDuration ) ;
2018-04-10 02:39:41 +01:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
2019-11-18 20:59:16 +00:00
continue ;
2018-04-10 02:39:41 +01:00
2019-12-25 18:07:05 +00:00
_resume . BadBlocks . Remove ( badSector ) ;
2018-04-10 02:39:41 +01:00
extents . Add ( badSector ) ;
2019-12-25 18:07:05 +00:00
_outputPlugin . WriteSector ( readBuffer , badSector ) ;
2018-04-10 02:39:41 +01:00
}
2019-04-20 01:43:58 +01:00
EndProgress ? . Invoke ( ) ;
2018-04-10 02:39:41 +01:00
end = DateTime . UtcNow ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"Trimmming finished in {(end - start).TotalSeconds} seconds." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Trimmming finished in {0} seconds." , ( end - start ) . TotalSeconds ) ;
2018-04-10 02:39:41 +01:00
}
#endregion Trimming
2017-05-31 01:00:58 +01:00
#region Error handling
2019-12-25 18:07:05 +00:00
if ( _resume . BadBlocks . Count > 0 & &
! _aborted & &
_retryPasses > 0 )
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
int pass = 1 ;
bool forward = true ;
bool runningPersistent = false ;
2017-05-31 01:00:58 +01:00
2017-12-21 14:30:38 +00:00
Modes . ModePage ? currentModePage = null ;
2019-11-18 20:59:16 +00:00
byte [ ] md6 ;
byte [ ] md10 ;
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
if ( _persistent )
2017-05-31 01:00:58 +01:00
{
2018-04-10 03:37:52 +01:00
Modes . ModePage_01_MMC pgMmc ;
2019-11-18 20:59:16 +00:00
Modes . ModePage_01 pg ;
2018-06-22 08:08:38 +01:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense6 ( out readBuffer , out _ , false , ScsiModeSensePageControl . Current , 0x01 ,
_dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( sense )
2018-04-10 03:37:52 +01:00
{
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense10 ( out readBuffer , out _ , false , ScsiModeSensePageControl . Current , 0x01 ,
_dev . Timeout , out _ ) ;
2018-04-10 03:37:52 +01:00
2019-11-18 20:59:16 +00:00
if ( ! sense )
2018-04-10 03:37:52 +01:00
{
2019-12-25 18:07:05 +00:00
Modes . DecodedMode ? dcMode10 = Modes . DecodeMode10 ( readBuffer , _dev . ScsiType ) ;
2018-06-22 08:08:38 +01:00
2019-11-18 20:59:16 +00:00
if ( dcMode10 . HasValue & &
dcMode10 . Value . Pages ! = null )
foreach ( Modes . ModePage modePage in dcMode10 . Value . Pages )
if ( modePage . Page = = 0x01 & &
modePage . Subpage = = 0x00 )
2018-06-22 08:08:38 +01:00
currentModePage = modePage ;
2018-04-10 03:37:52 +01:00
}
}
else
{
2019-12-25 18:07:05 +00:00
Modes . DecodedMode ? dcMode6 = Modes . DecodeMode6 ( readBuffer , _dev . ScsiType ) ;
2018-04-10 03:37:52 +01:00
2019-11-18 20:59:16 +00:00
if ( dcMode6 . HasValue & &
dcMode6 . Value . Pages ! = null )
foreach ( Modes . ModePage modePage in dcMode6 . Value . Pages )
if ( modePage . Page = = 0x01 & &
modePage . Subpage = = 0x00 )
2018-04-10 03:37:52 +01:00
currentModePage = modePage ;
}
2019-11-18 20:59:16 +00:00
if ( currentModePage = = null )
2018-04-10 03:37:52 +01:00
{
2019-12-25 18:07:05 +00:00
if ( _dev . ScsiType = = PeripheralDeviceTypes . MultiMediaDevice )
2018-04-10 03:37:52 +01:00
{
2019-11-18 20:59:16 +00:00
pgMmc = new Modes . ModePage_01_MMC
{
PS = false , ReadRetryCount = 32 , Parameter = 0x00
} ;
2018-04-10 03:37:52 +01:00
currentModePage = new Modes . ModePage
{
2018-12-31 13:17:27 +00:00
Page = 0x01 , Subpage = 0x00 , PageResponse = Modes . EncodeModePage_01_MMC ( pgMmc )
2018-04-10 03:37:52 +01:00
} ;
}
else
{
pg = new Modes . ModePage_01
{
2019-11-18 20:59:16 +00:00
PS = false , AWRE = true , ARRE = true , TB = false ,
RC = false , EER = true , PER = false , DTE = true ,
DCR = false , ReadRetryCount = 32
2018-04-10 03:37:52 +01:00
} ;
2018-06-22 08:08:38 +01:00
currentModePage = new Modes . ModePage
{
2018-12-31 13:17:27 +00:00
Page = 0x01 , Subpage = 0x00 , PageResponse = Modes . EncodeModePage_01 ( pg )
2018-06-22 08:08:38 +01:00
} ;
2018-04-10 03:37:52 +01:00
}
}
2019-12-25 18:07:05 +00:00
if ( _dev . ScsiType = = PeripheralDeviceTypes . MultiMediaDevice )
2017-05-31 01:00:58 +01:00
{
2019-11-18 20:59:16 +00:00
pgMmc = new Modes . ModePage_01_MMC
{
PS = false , ReadRetryCount = 255 , Parameter = 0x20
} ;
2019-10-26 01:57:55 +01:00
var md = new Modes . DecodedMode
2017-06-08 21:12:05 +01:00
{
2019-11-18 20:59:16 +00:00
Header = new Modes . ModeHeader ( ) , Pages = new [ ]
2017-06-08 21:12:05 +01:00
{
2017-12-21 14:30:38 +00:00
new Modes . ModePage
2017-06-08 21:12:05 +01:00
{
2019-11-18 20:59:16 +00:00
Page = 0x01 , Subpage = 0x00 , PageResponse = Modes . EncodeModePage_01_MMC ( pgMmc )
2017-06-08 21:12:05 +01:00
}
}
} ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
md6 = Modes . EncodeMode6 ( md , _dev . ScsiType ) ;
md10 = Modes . EncodeMode10 ( md , _dev . ScsiType ) ;
2017-05-31 01:00:58 +01:00
}
else
{
2018-04-10 03:37:52 +01:00
pg = new Modes . ModePage_01
2017-06-08 21:12:05 +01:00
{
2019-11-18 20:59:16 +00:00
PS = false , AWRE = false , ARRE = false , TB = true ,
RC = false , EER = true , PER = false , DTE = false ,
DCR = false , ReadRetryCount = 255
2017-06-08 21:12:05 +01:00
} ;
2019-11-18 20:59:16 +00:00
2019-10-26 01:57:55 +01:00
var md = new Modes . DecodedMode
2017-06-08 21:12:05 +01:00
{
2019-11-18 20:59:16 +00:00
Header = new Modes . ModeHeader ( ) , Pages = new [ ]
2017-06-08 21:12:05 +01:00
{
2017-12-21 14:30:38 +00:00
new Modes . ModePage
2017-06-08 21:12:05 +01:00
{
2018-12-31 13:17:27 +00:00
Page = 0x01 , Subpage = 0x00 , PageResponse = Modes . EncodeModePage_01 ( pg )
2017-06-08 21:12:05 +01:00
}
}
} ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
md6 = Modes . EncodeMode6 ( md , _dev . ScsiType ) ;
md10 = Modes . EncodeMode10 ( md , _dev . ScsiType ) ;
2017-05-31 01:00:58 +01:00
}
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Sending MODE SELECT to drive (return damaged blocks)." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Sending MODE SELECT to drive (return damaged blocks)." ) ;
sense = _dev . ModeSelect ( md6 , out byte [ ] senseBuf , true , false , _dev . Timeout , out _ ) ;
2018-04-02 23:08:26 +01:00
2019-11-18 20:59:16 +00:00
if ( sense )
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSelect10 ( md10 , out senseBuf , true , false , _dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( sense )
2018-04-02 23:08:26 +01:00
{
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
Invoke ( "Drive did not accept MODE SELECT command for persistent error reading, try another drive." ) ;
2020-02-27 23:48:41 +00:00
AaruConsole . DebugWriteLine ( "Error: {0}" , Sense . PrettifySense ( senseBuf ) ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog .
2019-11-18 20:59:16 +00:00
WriteLine ( "Drive did not accept MODE SELECT command for persistent error reading, try another drive." ) ;
2019-10-26 01:57:55 +01:00
}
else
{
runningPersistent = true ;
2018-04-02 23:08:26 +01:00
}
}
2019-04-20 01:43:58 +01:00
InitProgress ? . Invoke ( ) ;
2018-04-02 23:08:26 +01:00
repeatRetry :
2019-12-25 18:07:05 +00:00
ulong [ ] tmpArray = _resume . BadBlocks . ToArray ( ) ;
2019-11-18 20:59:16 +00:00
foreach ( ulong badSector in tmpArray )
2018-04-02 23:08:26 +01:00
{
2019-12-25 18:07:05 +00:00
if ( _aborted )
2018-04-02 23:08:26 +01:00
{
currentTry . Extents = ExtentsConverter . ToMetadata ( extents ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Aborted!" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Aborted!" ) ;
2019-11-18 20:59:16 +00:00
2018-04-02 23:08:26 +01:00
break ;
}
2019-04-20 23:05:49 +01:00
PulseProgress ? . Invoke ( string . Format ( "Retrying sector {0}, pass {1}, {3}{2}" , badSector , pass ,
2019-11-18 20:59:16 +00:00
forward ? "forward" : "reverse" ,
runningPersistent ? "recovering partial data, " : "" ) ) ;
2018-04-02 23:08:26 +01:00
2019-11-18 20:59:16 +00:00
sense = scsiReader . ReadBlock ( out readBuffer , badSector , out double cmdDuration ) ;
2018-04-02 23:08:26 +01:00
totalDuration + = cmdDuration ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
2017-05-31 01:00:58 +01:00
{
2019-12-25 18:07:05 +00:00
_resume . BadBlocks . Remove ( badSector ) ;
2018-04-02 23:08:26 +01:00
extents . Add ( badSector ) ;
2019-12-25 18:07:05 +00:00
_outputPlugin . WriteSector ( readBuffer , badSector ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"Correctly retried block {badSector} in pass {pass}." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Correctly retried block {0} in pass {1}." , badSector , pass ) ;
2017-05-31 01:00:58 +01:00
}
2019-11-18 20:59:16 +00:00
else if ( runningPersistent )
2019-10-26 01:57:55 +01:00
{
2019-12-25 18:07:05 +00:00
_outputPlugin . WriteSector ( readBuffer , badSector ) ;
2019-10-26 01:57:55 +01:00
}
2017-05-31 01:00:58 +01:00
}
2018-04-02 23:08:26 +01:00
2019-12-25 18:07:05 +00:00
if ( pass < _retryPasses & &
! _aborted & &
_resume . BadBlocks . Count > 0 )
2018-04-02 23:08:26 +01:00
{
pass + + ;
forward = ! forward ;
2019-12-25 18:07:05 +00:00
_resume . BadBlocks . Sort ( ) ;
2020-06-14 19:08:12 +01:00
if ( ! forward )
_resume . BadBlocks . Reverse ( ) ;
2019-11-18 20:59:16 +00:00
2018-04-02 23:08:26 +01:00
goto repeatRetry ;
}
2019-11-18 20:59:16 +00:00
if ( runningPersistent & & currentModePage . HasValue )
2017-05-31 01:00:58 +01:00
{
2019-10-26 01:57:55 +01:00
var md = new Modes . DecodedMode
2017-06-08 21:12:05 +01:00
{
2019-11-18 20:59:16 +00:00
Header = new Modes . ModeHeader ( ) , Pages = new [ ]
{
currentModePage . Value
}
2017-06-08 21:12:05 +01:00
} ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
md6 = Modes . EncodeMode6 ( md , _dev . ScsiType ) ;
md10 = Modes . EncodeMode10 ( md , _dev . ScsiType ) ;
2017-05-31 01:00:58 +01:00
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Sending MODE SELECT to drive (return device to previous status)." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Sending MODE SELECT to drive (return device to previous status)." ) ;
sense = _dev . ModeSelect ( md6 , out _ , true , false , _dev . Timeout , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( sense )
2019-12-25 18:07:05 +00:00
_dev . ModeSelect10 ( md10 , out _ , true , false , _dev . Timeout , out _ ) ;
2017-05-31 01:00:58 +01:00
}
2019-04-20 01:43:58 +01:00
EndProgress ? . Invoke ( ) ;
2017-05-31 01:00:58 +01:00
}
#endregion Error handling
2017-12-19 20:33:03 +00:00
2019-12-25 18:07:05 +00:00
if ( ! _aborted )
2019-11-18 20:59:16 +00:00
if ( opticalDisc )
2019-10-26 01:57:55 +01:00
{
2019-11-18 20:59:16 +00:00
foreach ( KeyValuePair < MediaTagType , byte [ ] > tag in mediaTags )
2018-01-19 01:21:01 +00:00
{
2019-11-18 20:59:16 +00:00
if ( tag . Value is null )
2019-08-10 14:35:14 +01:00
{
2020-02-27 23:48:41 +00:00
AaruConsole . ErrorWriteLine ( "Error: Tag type {0} is null, skipping..." , tag . Key ) ;
2019-11-18 20:59:16 +00:00
2019-08-10 14:35:14 +01:00
continue ;
}
2019-12-25 18:07:05 +00:00
ret = _outputPlugin . WriteMediaTag ( tag . Value , tag . Key ) ;
2017-05-31 01:00:58 +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-19 01:21:01 +00:00
// Cannot write tag to image
2019-04-20 01:43:58 +01:00
StoppingErrorMessage ? . Invoke ( $"Cannot write tag {tag.Key}." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( $"Cannot write tag {tag.Key}." + Environment . NewLine +
_outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-19 01:21:01 +00:00
}
2019-10-26 01:57:55 +01:00
}
2018-01-19 01:21:01 +00:00
else
2017-11-20 05:07:16 +00:00
{
2019-12-25 18:07:05 +00:00
if ( ! _dev . IsRemovable | |
_dev . IsUsb )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _dev . IsUsb & &
_dev . UsbDescriptors ! = null )
2018-01-19 01:21:01 +00:00
{
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Reading USB descriptors." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Reading USB descriptors." ) ;
ret = _outputPlugin . WriteMediaTag ( _dev . UsbDescriptors , MediaTagType . USB_Descriptors ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! ret & &
2019-12-25 18:07:05 +00:00
! _force )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Cannot write USB descriptors." ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
StoppingErrorMessage ? . Invoke ( "Cannot write USB descriptors." + Environment . NewLine +
2019-12-25 18:07:05 +00:00
_outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-19 01:21:01 +00:00
}
}
2017-05-31 01:00:58 +01:00
2018-01-19 01:21:01 +00:00
byte [ ] cmdBuf ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Type = = DeviceType . ATAPI )
2018-01-19 01:21:01 +00:00
{
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting ATAPI IDENTIFY PACKET DEVICE." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting ATAPI IDENTIFY PACKET DEVICE." ) ;
sense = _dev . AtapiIdentify ( out cmdBuf , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense )
2018-01-19 01:21:01 +00:00
{
2020-03-11 15:28:04 +00:00
if ( _private )
cmdBuf = DeviceReport . ClearIdentify ( cmdBuf ) ;
2019-12-25 18:07:05 +00:00
ret = _outputPlugin . WriteMediaTag ( cmdBuf , MediaTagType . ATAPI_IDENTIFY ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! ret & &
2019-12-25 18:07:05 +00:00
! _force )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Cannot write ATAPI IDENTIFY PACKET DEVICE." ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
StoppingErrorMessage ? . Invoke ( "Cannot write ATAPI IDENTIFY PACKET DEVICE." +
2020-05-13 10:45:09 +00:00
Environment . NewLine + _outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-19 01:21:01 +00:00
}
}
}
2017-05-31 01:00:58 +01:00
2019-12-25 18:07:05 +00:00
sense = _dev . ScsiInquiry ( out cmdBuf , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense )
2018-01-19 01:21:01 +00:00
{
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting SCSI INQUIRY." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting SCSI INQUIRY." ) ;
ret = _outputPlugin . WriteMediaTag ( cmdBuf , MediaTagType . SCSI_INQUIRY ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! ret & &
2019-12-25 18:07:05 +00:00
! _force )
2018-01-19 01:21:01 +00:00
{
2019-04-20 01:43:58 +01:00
StoppingErrorMessage ? . Invoke ( "Cannot write SCSI INQUIRY." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Cannot write SCSI INQUIRY." + Environment . NewLine +
_outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-19 01:21:01 +00:00
}
2017-12-19 20:33:03 +00:00
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting MODE SENSE (10)." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting MODE SENSE (10)." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense10 ( out cmdBuf , out _ , false , true , ScsiModeSensePageControl . Current ,
0x3F , 0xFF , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense | |
2019-12-25 18:07:05 +00:00
_dev . Error )
sense = _dev . ModeSense10 ( out cmdBuf , out _ , false , true ,
ScsiModeSensePageControl . Current , 0x3F , 0x00 , 5 , out _ ) ;
2018-01-19 01:21:01 +00:00
decMode = null ;
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
if ( Modes . DecodeMode10 ( cmdBuf , _dev . ScsiType ) . HasValue )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
decMode = Modes . DecodeMode10 ( cmdBuf , _dev . ScsiType ) ;
ret = _outputPlugin . WriteMediaTag ( cmdBuf , MediaTagType . SCSI_MODESENSE_10 ) ;
2018-01-19 01:21:01 +00:00
2019-11-18 20:59:16 +00:00
if ( ! ret & &
2019-12-25 18:07:05 +00:00
! _force )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Cannot write SCSI MODE SENSE (10)." ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
StoppingErrorMessage ? . Invoke ( "Cannot write SCSI MODE SENSE (10)." +
2020-05-13 10:45:09 +00:00
Environment . NewLine + _outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-19 01:21:01 +00:00
}
}
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting MODE SENSE (6)." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting MODE SENSE (6)." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense6 ( out cmdBuf , out _ , false , ScsiModeSensePageControl . Current , 0x3F ,
0x00 , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
sense = _dev . ModeSense6 ( out cmdBuf , out _ , false , ScsiModeSensePageControl . Current ,
0x3F , 0x00 , 5 , out _ ) ;
2018-01-19 01:21:01 +00:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
sense = _dev . ModeSense ( out cmdBuf , out _ , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
if ( Modes . DecodeMode6 ( cmdBuf , _dev . ScsiType ) . HasValue )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
decMode = Modes . DecodeMode6 ( cmdBuf , _dev . ScsiType ) ;
ret = _outputPlugin . WriteMediaTag ( cmdBuf , MediaTagType . SCSI_MODESENSE_6 ) ;
2017-05-31 01:00:58 +01:00
2019-11-18 20:59:16 +00:00
if ( ! ret & &
2019-12-25 18:07:05 +00:00
! _force )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Cannot write SCSI MODE SENSE (6)." ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
StoppingErrorMessage ? . Invoke ( "Cannot write SCSI MODE SENSE (6)." +
2020-05-13 10:45:09 +00:00
Environment . NewLine + _outputPlugin . ErrorMessage ) ;
2019-11-18 20:59:16 +00:00
2019-04-20 14:02:25 +01:00
return ;
2018-01-19 01:21:01 +00: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
2020-01-09 18:01:43 +00:00
// TODO: Media Serial Number
// TODO: Non-removable drive information
var metadata = new CommonTypes . Structs . ImageInfo
{
2020-02-27 14:06:36 +00:00
Application = "Aaru" , ApplicationVersion = Version . GetVersion ( )
2020-01-09 18:01:43 +00:00
} ;
if ( ! _outputPlugin . SetMetadata ( metadata ) )
ErrorMessage ? . Invoke ( "Error {0} setting metadata, continuing..." + Environment . NewLine +
_outputPlugin . ErrorMessage ) ;
2019-12-25 18:07:05 +00:00
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-20 01:43:58 +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-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( $"Closed in {(closeEnd - closeStart).TotalSeconds} seconds." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Closed in {0} seconds." , ( closeEnd - closeStart ) . TotalSeconds ) ;
2018-01-19 01:21:01 +00:00
2019-12-25 18:07:05 +00:00
if ( _aborted )
2017-05-31 01:00:58 +01:00
{
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Aborted!" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Aborted!" ) ;
2019-11-18 20:59:16 +00:00
2017-05-31 01:00:58 +01:00
return ;
}
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 )
2018-02-02 22:03:19 +00:00
{
2019-12-31 21:27:16 +00:00
// TODO: Layers
2019-11-18 20:59:16 +00:00
if ( opticalDisc )
2020-01-06 23:41:56 +00:00
WriteOpticalSidecar ( blockSize , blocks , dskType , null , mediaTags , 1 , out totalChkDuration , null ) ;
2019-12-31 21:27:16 +00:00
else
2018-01-28 21:18:52 +00:00
{
2019-12-31 21:27:16 +00:00
UpdateStatus ? . Invoke ( "Creating sidecar." ) ;
_dumpLog . WriteLine ( "Creating sidecar." ) ;
var filters = new FiltersList ( ) ;
IFilter filter = filters . GetFilter ( _outputPath ) ;
IMediaImage inputPlugin = ImageFormat . Detect ( filter ) ;
if ( ! inputPlugin . Open ( filter ) )
2018-02-02 22:03:19 +00:00
{
2019-12-31 21:27:16 +00:00
StoppingErrorMessage ? . Invoke ( "Could not open created image." ) ;
return ;
2018-02-02 22:03:19 +00:00
}
2018-01-28 21:18:52 +00:00
2019-12-31 21:27:16 +00:00
DateTime chkStart = DateTime . UtcNow ;
_sidecarClass = new Sidecar ( inputPlugin , _outputPath , filter . Id , _encoding ) ;
_sidecarClass . InitProgressEvent + = InitProgress ;
_sidecarClass . UpdateProgressEvent + = UpdateProgress ;
_sidecarClass . EndProgressEvent + = EndProgress ;
_sidecarClass . InitProgressEvent2 + = InitProgress2 ;
_sidecarClass . UpdateProgressEvent2 + = UpdateProgress2 ;
_sidecarClass . EndProgressEvent2 + = EndProgress2 ;
_sidecarClass . UpdateStatusEvent + = UpdateStatus ;
CICMMetadataType sidecar = _sidecarClass . Create ( ) ;
end = DateTime . UtcNow ;
totalChkDuration = ( end - chkStart ) . TotalMilliseconds ;
UpdateStatus ? . Invoke ( $"Sidecar created in {(end - chkStart).TotalSeconds} seconds." ) ;
2019-11-18 20:59:16 +00:00
2019-12-31 21:27:16 +00:00
UpdateStatus ? .
Invoke ( $"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec." ) ;
2019-11-18 20:59:16 +00:00
2019-12-31 21:27:16 +00:00
_dumpLog . WriteLine ( "Sidecar created in {0} seconds." , ( end - chkStart ) . TotalSeconds ) ;
2019-04-20 01:43:58 +01:00
2019-12-31 21:27:16 +00:00
_dumpLog . WriteLine ( "Average checksum speed {0:F3} KiB/sec." ,
( ( double ) blockSize * ( double ) ( blocks + 1 ) ) / 1024 / ( totalChkDuration / 1000 ) ) ;
2018-01-28 21:18:52 +00:00
2019-12-25 18:07:05 +00:00
if ( _preSidecar ! = null )
2018-02-02 22:03:19 +00:00
{
2019-12-25 18:07:05 +00:00
_preSidecar . BlockMedia = sidecar . BlockMedia ;
sidecar = _preSidecar ;
2018-02-02 22:03:19 +00:00
}
2017-05-31 01:00:58 +01:00
2018-02-02 22:03:19 +00:00
// All USB flash drives report as removable, even if the media is not removable
2019-12-25 18:07:05 +00:00
if ( ! _dev . IsRemovable | |
_dev . IsUsb )
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
if ( _dev . IsUsb & &
_dev . UsbDescriptors ! = null )
if ( _outputPlugin . SupportedMediaTags . Contains ( MediaTagType . USB_Descriptors ) )
2018-02-02 22:03:19 +00:00
sidecar . BlockMedia [ 0 ] . USB = new USBType
2018-01-19 01:21:01 +00:00
{
2019-12-25 18:07:05 +00:00
ProductID = _dev . UsbProductId , VendorID = _dev . UsbVendorId , Descriptors =
new DumpType
{
Image = _outputPath , Size = ( ulong ) _dev . UsbDescriptors . Length ,
Checksums = Checksum . GetChecksums ( _dev . UsbDescriptors ) . ToArray ( )
}
2018-01-19 01:21:01 +00:00
} ;
2017-05-31 01:00:58 +01:00
2018-02-02 22:03:19 +00:00
byte [ ] cmdBuf ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . Type = = DeviceType . ATAPI )
2018-02-02 22:03:19 +00:00
{
2019-12-25 18:07:05 +00:00
sense = _dev . AtapiIdentify ( out cmdBuf , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense )
2019-12-25 18:07:05 +00:00
if ( _outputPlugin . SupportedMediaTags . Contains ( MediaTagType . ATAPI_IDENTIFY ) )
2018-02-02 22:03:19 +00:00
sidecar . BlockMedia [ 0 ] . ATA = new ATAType
{
Identify = new DumpType
{
2019-12-25 18:07:05 +00:00
Image = _outputPath , Size = ( ulong ) cmdBuf . Length ,
2018-02-02 22:03:19 +00:00
Checksums = Checksum . GetChecksums ( cmdBuf ) . ToArray ( )
}
} ;
}
2017-07-19 16:31:08 +01:00
2019-12-25 18:07:05 +00:00
sense = _dev . ScsiInquiry ( out cmdBuf , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense )
2017-05-31 01:00:58 +01:00
{
2019-12-25 18:07:05 +00:00
if ( _outputPlugin . SupportedMediaTags . Contains ( MediaTagType . SCSI_INQUIRY ) )
2018-02-02 22:03:19 +00:00
sidecar . BlockMedia [ 0 ] . SCSI = new SCSIType
{
Inquiry = new DumpType
{
2019-12-25 18:07:05 +00:00
Image = _outputPath , Size = ( ulong ) cmdBuf . Length ,
2018-02-02 22:03:19 +00:00
Checksums = Checksum . GetChecksums ( cmdBuf ) . ToArray ( )
}
} ;
2017-12-21 06:06:19 +00:00
2018-02-02 22:03:19 +00:00
// TODO: SCSI Extended Vendor Page descriptors
/ *
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Reading SCSI Extended Vendor Page Descriptors." ) ;
2018-02-02 22:03:19 +00:00
dumpLog . WriteLine ( "Reading SCSI Extended Vendor Page Descriptors." ) ;
sense = dev . ScsiInquiry ( out cmdBuf , out _ , 0x00 ) ;
if ( ! sense )
2017-12-23 17:41:23 +00:00
{
2018-02-02 22:03:19 +00:00
byte [ ] pages = EVPD . DecodePage00 ( cmdBuf ) ;
2018-12-29 17:34:38 +00:00
2018-02-02 22:03:19 +00:00
if ( pages ! = null )
2018-01-19 01:21:01 +00:00
{
2018-02-02 22:03:19 +00:00
List < EVPDType > evpds = new List < EVPDType > ( ) ;
foreach ( byte page in pages )
2018-01-19 01:21:01 +00:00
{
2018-02-02 22:03:19 +00:00
dumpLog . WriteLine ( "Requesting page {0:X2}h." , page ) ;
sense = dev . ScsiInquiry ( out cmdBuf , out _ , page ) ;
if ( sense ) continue ;
2018-12-29 17:34:38 +00:00
2018-02-02 22:03:19 +00:00
EVPDType evpd = new EVPDType
{
Image = $"{outputPrefix}.evpd_{page:X2}h.bin" ,
Checksums = Checksum . GetChecksums ( cmdBuf ) . ToArray ( ) ,
Size = cmdBuf . Length
} ;
evpd . Checksums = Checksum . GetChecksums ( cmdBuf ) . ToArray ( ) ;
DataFile . WriteTo ( "SCSI Dump" , evpd . Image , cmdBuf ) ;
evpds . Add ( evpd ) ;
}
2018-12-29 17:34:38 +00:00
2018-02-02 22:03:19 +00:00
if ( evpds . Count > 0 ) sidecar . BlockMedia [ 0 ] . SCSI . EVPD = evpds . ToArray ( ) ;
2018-01-19 01:21:01 +00:00
}
2017-05-31 01:00:58 +01:00
}
2018-02-02 22:03:19 +00:00
* /
2017-05-31 01:00:58 +01:00
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting MODE SENSE (10)." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting MODE SENSE (10)." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense10 ( out cmdBuf , out _ , false , true , ScsiModeSensePageControl . Current ,
0x3F , 0xFF , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
if ( ! sense | |
2019-12-25 18:07:05 +00:00
_dev . Error )
sense = _dev . ModeSense10 ( out cmdBuf , out _ , false , true ,
ScsiModeSensePageControl . Current , 0x3F , 0x00 , 5 , out _ ) ;
2017-05-31 01:00:58 +01:00
2018-02-02 22:03:19 +00:00
decMode = null ;
2017-12-19 20:33:03 +00:00
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
if ( Modes . DecodeMode10 ( cmdBuf , _dev . ScsiType ) . HasValue )
if ( _outputPlugin . SupportedMediaTags . Contains ( MediaTagType . SCSI_MODESENSE_10 ) )
2018-02-02 22:03:19 +00:00
sidecar . BlockMedia [ 0 ] . SCSI . ModeSense10 = new DumpType
{
2019-12-25 18:07:05 +00:00
Image = _outputPath , Size = ( ulong ) cmdBuf . Length ,
2018-02-02 22:03:19 +00:00
Checksums = Checksum . GetChecksums ( cmdBuf ) . ToArray ( )
} ;
2018-01-19 01:21:01 +00:00
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "Requesting MODE SENSE (6)." ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Requesting MODE SENSE (6)." ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
sense = _dev . ModeSense6 ( out cmdBuf , out _ , false , ScsiModeSensePageControl . Current , 0x3F ,
0x00 , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
sense = _dev . ModeSense6 ( out cmdBuf , out _ , false , ScsiModeSensePageControl . Current ,
0x3F , 0x00 , 5 , out _ ) ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( sense | | _dev . Error )
sense = _dev . ModeSense ( out cmdBuf , out _ , 5 , out _ ) ;
2018-01-19 01:21:01 +00:00
2019-11-18 20:59:16 +00:00
if ( ! sense & &
2019-12-25 18:07:05 +00:00
! _dev . Error )
if ( Modes . DecodeMode6 ( cmdBuf , _dev . ScsiType ) . HasValue )
if ( _outputPlugin . SupportedMediaTags . Contains ( MediaTagType . SCSI_MODESENSE_6 ) )
2018-02-02 22:03:19 +00:00
sidecar . BlockMedia [ 0 ] . SCSI . ModeSense = new DumpType
{
2019-12-25 18:07:05 +00:00
Image = _outputPath , Size = ( ulong ) cmdBuf . Length ,
2018-02-02 22:03:19 +00:00
Checksums = Checksum . GetChecksums ( cmdBuf ) . ToArray ( )
} ;
}
2018-01-19 01:21:01 +00:00
}
2018-02-02 22:03:19 +00:00
2019-11-18 20:59:16 +00:00
List < ( ulong start , string type ) > filesystems = new List < ( ulong start , string type ) > ( ) ;
if ( sidecar . BlockMedia [ 0 ] . FileSystemInformation ! = null )
2018-02-02 22:03:19 +00:00
filesystems . AddRange ( from partition in sidecar . BlockMedia [ 0 ] . FileSystemInformation
2019-11-18 20:59:16 +00:00
where partition . FileSystems ! = null
from fileSystem in partition . FileSystems
select ( partition . StartSector , fileSystem . Type ) ) ;
2018-02-02 22:03:19 +00:00
2019-11-18 20:59:16 +00:00
if ( filesystems . Count > 0 )
foreach ( var filesystem in filesystems . Select ( o = > new
{
o . start , o . type
} ) . Distinct ( ) )
2019-04-20 01:43:58 +01:00
{
UpdateStatus ? . Invoke ( $"Found filesystem {filesystem.type} at sector {filesystem.start}" ) ;
2019-12-25 18:07:05 +00:00
_dumpLog . WriteLine ( "Found filesystem {0} at sector {1}" , filesystem . type , filesystem . start ) ;
2019-04-20 01:43:58 +01:00
}
2018-02-02 22:03:19 +00:00
sidecar . BlockMedia [ 0 ] . Dimensions = Dimensions . DimensionsFromMediaType ( dskType ) ;
2019-11-18 20:59:16 +00:00
( string type , string subType ) xmlType = CommonTypes . Metadata . MediaType . MediaTypeToString ( dskType ) ;
sidecar . BlockMedia [ 0 ] . DiskType = xmlType . type ;
sidecar . BlockMedia [ 0 ] . DiskSubType = xmlType . subType ;
2018-02-02 22:03:19 +00:00
// TODO: Implement device firmware revision
2019-12-25 18:07:05 +00:00
if ( ! _dev . IsRemovable | |
_dev . IsUsb )
if ( _dev . Type = = DeviceType . ATAPI )
2019-11-18 20:59:16 +00:00
sidecar . BlockMedia [ 0 ] . Interface = "ATAPI" ;
2019-12-25 18:07:05 +00:00
else if ( _dev . IsUsb )
2019-11-18 20:59:16 +00:00
sidecar . BlockMedia [ 0 ] . Interface = "USB" ;
2019-12-25 18:07:05 +00:00
else if ( _dev . IsFireWire )
2019-11-18 20:59:16 +00:00
sidecar . BlockMedia [ 0 ] . Interface = "FireWire" ;
else
sidecar . BlockMedia [ 0 ] . Interface = "SCSI" ;
2018-02-02 22:03:19 +00:00
2019-11-18 20:59:16 +00:00
sidecar . BlockMedia [ 0 ] . LogicalBlocks = blocks ;
sidecar . BlockMedia [ 0 ] . PhysicalBlockSize = physicalBlockSize ;
sidecar . BlockMedia [ 0 ] . LogicalBlockSize = logicalBlockSize ;
2019-12-25 18:07:05 +00:00
sidecar . BlockMedia [ 0 ] . Manufacturer = _dev . Manufacturer ;
sidecar . BlockMedia [ 0 ] . Model = _dev . Model ;
2020-03-11 15:28:04 +00:00
if ( ! _private )
sidecar . BlockMedia [ 0 ] . Serial = _dev . Serial ;
sidecar . BlockMedia [ 0 ] . Size = blocks * blockSize ;
2019-11-18 20:59:16 +00:00
2019-12-25 18:07:05 +00:00
if ( _dev . IsRemovable )
sidecar . BlockMedia [ 0 ] . DumpHardwareArray = _resume . Tries . ToArray ( ) ;
2017-12-19 20:33:03 +00:00
2019-12-31 21:27:16 +00:00
UpdateStatus ? . Invoke ( "Writing metadata sidecar" ) ;
2018-02-02 22:03:19 +00:00
2019-12-31 21:27:16 +00:00
var xmlFs = new FileStream ( _outputPrefix + ".cicm.xml" , FileMode . Create ) ;
2018-02-02 22:03:19 +00:00
2019-12-31 21:27:16 +00:00
var xmlSer = new XmlSerializer ( typeof ( CICMMetadataType ) ) ;
xmlSer . Serialize ( xmlFs , sidecar ) ;
xmlFs . Close ( ) ;
}
2017-05-31 01:00:58 +01:00
}
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "" ) ;
2019-11-18 20:59:16 +00:00
UpdateStatus ? .
Invoke ( $"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)." ) ;
UpdateStatus ? .
2019-12-25 18:07:05 +00:00
Invoke ( $"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec." ) ;
2019-11-18 20:59:16 +00:00
2020-06-25 01:47:25 +01:00
if ( maxSpeed > 0 )
UpdateStatus ? . Invoke ( $"Fastest speed burst: {maxSpeed:F3} MiB/sec." ) ;
if ( minSpeed > 0 & &
minSpeed < double . MaxValue )
UpdateStatus ? . Invoke ( $"Slowest speed burst: {minSpeed:F3} MiB/sec." ) ;
2019-12-25 18:07:05 +00:00
UpdateStatus ? . Invoke ( $"{_resume.BadBlocks.Count} sectors could not be read." ) ;
2019-04-20 01:43:58 +01:00
UpdateStatus ? . Invoke ( "" ) ;
2017-05-31 01:00:58 +01:00
Statistics . AddMedia ( dskType , true ) ;
}
}
2017-12-19 20:33:03 +00:00
}