2019-12-31 19:47:18 +00:00
|
|
|
// /***************************************************************************
|
2017-05-31 01:00:58 +01:00
|
|
|
// The Disc Image Chef
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : CompactDisc.cs
|
2017-12-19 03:50:57 +00:00
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
2017-05-31 01:00:58 +01:00
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
// Component : Core algorithms.
|
2017-05-31 01:00:58 +01:00
|
|
|
//
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
// Dumps CDs and DDCDs.
|
2017-05-31 01:00:58 +01:00
|
|
|
//
|
|
|
|
|
// --[ License ] --------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as
|
|
|
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2018-12-29 17:34:38 +00:00
|
|
|
// Copyright © 2011-2019 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.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Xml.Serialization;
|
2018-06-25 19:08:16 +01:00
|
|
|
using DiscImageChef.CommonTypes;
|
|
|
|
|
using DiscImageChef.CommonTypes.Enums;
|
|
|
|
|
using DiscImageChef.CommonTypes.Extents;
|
|
|
|
|
using DiscImageChef.CommonTypes.Interfaces;
|
|
|
|
|
using DiscImageChef.CommonTypes.Metadata;
|
|
|
|
|
using DiscImageChef.CommonTypes.Structs;
|
2018-01-20 17:12:01 +00:00
|
|
|
using DiscImageChef.Console;
|
2017-05-31 01:00:58 +01:00
|
|
|
using DiscImageChef.Core.Logging;
|
2019-02-12 00:56:02 +00:00
|
|
|
using DiscImageChef.Core.Media.Detection;
|
2018-01-20 17:12:01 +00:00
|
|
|
using DiscImageChef.Decoders.CD;
|
|
|
|
|
using DiscImageChef.Decoders.SCSI;
|
|
|
|
|
using DiscImageChef.Decoders.SCSI.MMC;
|
2017-12-21 14:30:38 +00:00
|
|
|
using DiscImageChef.Devices;
|
2018-01-20 17:12:01 +00:00
|
|
|
using Schemas;
|
2019-12-14 17:48:32 +00:00
|
|
|
using CdOffset = DiscImageChef.Database.Models.CdOffset;
|
2017-12-21 14:30:38 +00:00
|
|
|
using MediaType = DiscImageChef.CommonTypes.MediaType;
|
2018-06-25 19:08:16 +01:00
|
|
|
using PlatformID = DiscImageChef.CommonTypes.Interop.PlatformID;
|
2018-01-20 17:12:01 +00:00
|
|
|
using Session = DiscImageChef.Decoders.CD.Session;
|
2018-06-25 19:08:16 +01:00
|
|
|
using TrackType = DiscImageChef.CommonTypes.Enums.TrackType;
|
2019-12-14 18:17:17 +00:00
|
|
|
|
2019-12-14 17:48:32 +00:00
|
|
|
// ReSharper disable JoinDeclarationAndInitializer
|
2019-12-14 18:17:17 +00:00
|
|
|
// ReSharper disable InlineOutVariableDeclaration
|
|
|
|
|
// ReSharper disable TooWideLocalVariableScope
|
2017-05-31 01:00:58 +01:00
|
|
|
|
|
|
|
|
namespace DiscImageChef.Core.Devices.Dumping
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
/// <summary>Implement dumping Compact Discs</summary>
|
|
|
|
|
|
2018-02-04 22:43:37 +00:00
|
|
|
// TODO: Barcode and pregaps
|
2019-12-26 00:48:21 +00:00
|
|
|
// TODO: Repetitive code
|
2019-04-19 18:54:25 +01:00
|
|
|
partial class Dump
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
/// <summary>Dumps a compact disc</summary>
|
2017-12-23 01:46:08 +00:00
|
|
|
/// <param name="dskType">Disc type as detected in MMC layer</param>
|
2019-12-14 22:55:26 +00:00
|
|
|
void CompactDisc(out MediaType dskType)
|
2019-12-14 14:33:23 +00:00
|
|
|
{
|
2019-12-25 21:03:21 +00:00
|
|
|
ExtentsULong audioExtents; // Extents with audio sectors
|
2019-12-25 16:53:29 +00:00
|
|
|
ulong blocks; // Total number of positive sectors
|
|
|
|
|
uint blockSize; // Size of the read sector in bytes
|
2019-12-25 21:03:21 +00:00
|
|
|
uint blocksToRead = 0; // How many sectors to read at once
|
2019-12-25 16:53:29 +00:00
|
|
|
CdOffset cdOffset; // Read offset from database
|
|
|
|
|
byte[] cmdBuf; // Data buffer
|
2019-12-26 01:34:24 +00:00
|
|
|
double cmdDuration = 0; // Command execution time
|
2019-12-25 16:53:29 +00:00
|
|
|
DumpHardwareType currentTry = null; // Current dump hardware try
|
|
|
|
|
double currentSpeed = 0; // Current read speed
|
2019-12-26 01:34:24 +00:00
|
|
|
DateTime dumpStart = DateTime.UtcNow; // Time of dump start
|
2019-12-25 16:53:29 +00:00
|
|
|
DateTime end; // Time of operation end
|
2019-12-31 01:35:49 +00:00
|
|
|
ExtentsULong extents = null; // Extents
|
2019-12-25 16:53:29 +00:00
|
|
|
IbgLog ibgLog; // IMGBurn log
|
|
|
|
|
double imageWriteDuration = 0; // Duration of image write
|
|
|
|
|
long lastSector = 0; // Last sector number
|
|
|
|
|
var leadOutExtents = new ExtentsULong(); // Lead-out extents
|
|
|
|
|
Dictionary<int, long> leadOutStarts = new Dictionary<int, long>(); // Lead-out starts
|
2019-12-31 01:35:49 +00:00
|
|
|
TrackType leadoutTrackType = TrackType.Audio; // Type of lead-out
|
2019-12-25 16:53:29 +00:00
|
|
|
double maxSpeed = double.MinValue; // Maximum speed
|
|
|
|
|
MhddLog mhddLog; // MHDD log
|
|
|
|
|
double minSpeed = double.MaxValue; // Minimum speed
|
|
|
|
|
bool newTrim = false; // Is trim a new one?
|
|
|
|
|
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
|
|
|
|
|
ulong sectorSpeedStart = 0; // Used to calculate correct speed
|
|
|
|
|
bool sense; // Sense indicator
|
|
|
|
|
byte[] senseBuf; // Sense buffer
|
|
|
|
|
int sessions = 1; // Number of sessions in disc
|
|
|
|
|
DateTime start; // Start of operation
|
|
|
|
|
uint subSize; // Subchannel size in bytes
|
|
|
|
|
TrackSubchannelType subType; // Track subchannel type
|
|
|
|
|
bool supportsLongSectors = true; // Supports reading EDC and ECC
|
|
|
|
|
byte[] tmpBuf; // Temporary buffer
|
|
|
|
|
FullTOC.CDFullTOC? toc = null; // Full CD TOC
|
|
|
|
|
double totalDuration = 0; // Total commands duration
|
|
|
|
|
Dictionary<byte, byte> trackFlags = new Dictionary<byte, byte>(); // Track flags
|
|
|
|
|
List<Track> trackList = new List<Track>(); // Tracks in disc
|
|
|
|
|
Track[] tracks; // Tracks in disc as array
|
2019-12-26 01:11:22 +00:00
|
|
|
int offsetBytes = 0; // Read offset
|
|
|
|
|
int sectorsForOffset = 0; // Sectors needed to fix offset
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
bool nextData; // Next cluster of sectors is all data;
|
2019-12-25 21:03:21 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
Dictionary<MediaTagType, byte[]> mediaTags = new Dictionary<MediaTagType, byte[]>(); // Media tags
|
2019-12-25 16:53:29 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
int firstTrackLastSession = 0; // Number of first track in last session
|
2019-12-25 16:53:29 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
|
2019-12-25 16:53:29 +00:00
|
|
|
|
|
|
|
|
DateTime timeSpeedStart; // Time of start for speed calculation
|
2019-12-14 17:48:32 +00:00
|
|
|
|
2019-12-14 18:57:54 +00:00
|
|
|
dskType = MediaType.CD;
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dumpRaw)
|
2019-12-14 14:33:23 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Raw CD dumping not yet implemented");
|
2019-12-14 14:33:23 +00:00
|
|
|
StoppingErrorMessage?.Invoke("Raw CD dumping not yet implemented");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-12-14 17:48:32 +00:00
|
|
|
|
|
|
|
|
// Search for read offset in master database
|
2019-12-26 01:34:24 +00:00
|
|
|
cdOffset = _ctx.CdOffsets.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model);
|
2019-12-14 17:48:32 +00:00
|
|
|
|
|
|
|
|
if(cdOffset is null)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("CD reading offset not found in database.");
|
2019-12-14 17:48:32 +00:00
|
|
|
UpdateStatus?.Invoke("CD reading offset not found in database.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine($"CD reading offset is {cdOffset.Offset} samples.");
|
2019-12-14 17:48:32 +00:00
|
|
|
UpdateStatus?.Invoke($"CD reading offset is {cdOffset.Offset} samples.");
|
|
|
|
|
}
|
2019-12-14 18:17:17 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
switch(_subchannel)
|
|
|
|
|
{
|
|
|
|
|
case DumpSubchannel.Any:
|
|
|
|
|
if(SupportsRwSubchannel())
|
|
|
|
|
supportedSubchannel = MmcSubchannel.Raw;
|
|
|
|
|
else if(SupportsPqSubchannel())
|
|
|
|
|
supportedSubchannel = MmcSubchannel.Q16;
|
|
|
|
|
else
|
|
|
|
|
supportedSubchannel = MmcSubchannel.None;
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
break;
|
|
|
|
|
case DumpSubchannel.Rw:
|
|
|
|
|
if(SupportsRwSubchannel())
|
|
|
|
|
supportedSubchannel = MmcSubchannel.Raw;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("Drive does not support the requested subchannel format, not continuing...");
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
break;
|
|
|
|
|
case DumpSubchannel.RwOrPq:
|
|
|
|
|
if(SupportsRwSubchannel())
|
|
|
|
|
supportedSubchannel = MmcSubchannel.Raw;
|
|
|
|
|
else if(SupportsPqSubchannel())
|
|
|
|
|
supportedSubchannel = MmcSubchannel.Q16;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("Drive does not support the requested subchannel format, not continuing...");
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case DumpSubchannel.Pq:
|
|
|
|
|
if(SupportsPqSubchannel())
|
|
|
|
|
supportedSubchannel = MmcSubchannel.Q16;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing...");
|
|
|
|
|
|
|
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("Drive does not support the requested subchannel format, not continuing...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case DumpSubchannel.None:
|
|
|
|
|
supportedSubchannel = MmcSubchannel.None;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
default: throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if output format supports subchannels
|
|
|
|
|
if(!_outputPlugin.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) &&
|
|
|
|
|
supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
if(!_force ||
|
|
|
|
|
_subchannel != DumpSubchannel.Any)
|
|
|
|
|
{
|
|
|
|
|
_dumpLog.WriteLine("Output format does not support subchannels, continuing...");
|
|
|
|
|
UpdateStatus?.Invoke("Output format does not support subchannels, continuing...");
|
2019-12-14 19:37:52 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-26 03:14:12 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support subchannels, not continuing...");
|
|
|
|
|
StoppingErrorMessage?.Invoke("Output format does not support subchannels, not continuing...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
supportedSubchannel = MmcSubchannel.None;
|
|
|
|
|
subSize = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(supportedSubchannel)
|
|
|
|
|
{
|
|
|
|
|
case MmcSubchannel.None:
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Checking if drive supports reading without subchannel...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Checking if drive supports reading without subchannel...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
readcd = !_dev.ReadCd(out cmdBuf, out senseBuf, 0, sectorSize, 1, MmcSectorTypes.AllTypes, false,
|
|
|
|
|
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
|
|
|
|
supportedSubchannel, _dev.Timeout, out _);
|
2019-12-14 19:37:52 +00:00
|
|
|
|
|
|
|
|
if(!readcd)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Drive does not support READ CD, trying SCSI READ commands...");
|
2019-12-14 19:37:52 +00:00
|
|
|
ErrorMessage?.Invoke("Drive does not support READ CD, trying SCSI READ commands...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Checking if drive supports READ(6)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Checking if drive supports READ(6)...");
|
2019-12-25 18:07:05 +00:00
|
|
|
read6 = !_dev.Read6(out cmdBuf, out senseBuf, 0, 2048, 1, _dev.Timeout, out _);
|
|
|
|
|
_dumpLog.WriteLine("Checking if drive supports READ(10)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Checking if drive supports READ(10)...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
read10 = !_dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, 0, 2048, 0, 1,
|
|
|
|
|
_dev.Timeout, out _);
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Checking if drive supports READ(12)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Checking if drive supports READ(12)...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
read12 = !_dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, 0, 2048, 0, 1,
|
|
|
|
|
false, _dev.Timeout, out _);
|
2019-12-14 19:37:52 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Checking if drive supports READ(16)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Checking if drive supports READ(16)...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
read16 = !_dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, 0, 2048, 0, 1, false,
|
|
|
|
|
_dev.Timeout, out _);
|
2019-12-14 19:37:52 +00:00
|
|
|
|
|
|
|
|
if(!read6 &&
|
|
|
|
|
!read10 &&
|
|
|
|
|
!read12 &&
|
|
|
|
|
!read16)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Cannot read from disc, not continuing...");
|
2019-12-14 19:37:52 +00:00
|
|
|
StoppingErrorMessage?.Invoke("Cannot read from disc, not continuing...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(read6)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Drive supports READ(6)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Drive supports READ(6)...");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(read10)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Drive supports READ(10)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Drive supports READ(10)...");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(read12)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Drive supports READ(12)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Drive supports READ(12)...");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(read16)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Drive supports READ(16)...");
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke("Drive supports READ(16)...");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
_dumpLog.WriteLine("Drive can read without subchannel...");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
|
2019-12-26 03:14:12 +00:00
|
|
|
UpdateStatus?.Invoke("Drive can read without subchannel...");
|
2019-12-14 19:37:52 +00:00
|
|
|
|
|
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
|
|
|
|
|
|
|
|
|
|
subSize = 0;
|
2019-12-14 19:51:52 +00:00
|
|
|
subType = TrackSubchannelType.None;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case MmcSubchannel.Raw:
|
2019-12-26 03:14:12 +00:00
|
|
|
_dumpLog.WriteLine("Full raw subchannel reading supported...");
|
|
|
|
|
UpdateStatus?.Invoke("Full raw subchannel reading supported...");
|
2019-12-14 19:51:52 +00:00
|
|
|
subType = TrackSubchannelType.Raw;
|
2019-12-26 03:14:12 +00:00
|
|
|
subSize = 96;
|
|
|
|
|
readcd = true;
|
2019-12-14 19:51:52 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case MmcSubchannel.Q16:
|
2019-12-26 03:14:12 +00:00
|
|
|
_dumpLog.WriteLine("PQ subchannel reading supported...");
|
|
|
|
|
_dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
|
|
|
|
|
UpdateStatus?.Invoke("PQ subchannel reading supported...");
|
|
|
|
|
|
|
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!");
|
|
|
|
|
|
2019-12-14 19:51:52 +00:00
|
|
|
subType = TrackSubchannelType.Q16;
|
2019-12-26 03:14:12 +00:00
|
|
|
subSize = 16;
|
|
|
|
|
readcd = true;
|
2019-12-14 19:51:52 +00:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel);
|
2019-12-14 19:51:52 +00:00
|
|
|
|
|
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 03:14:12 +00:00
|
|
|
blockSize = sectorSize + subSize;
|
|
|
|
|
|
2019-12-14 18:17:17 +00:00
|
|
|
// We discarded all discs that falsify a TOC before requesting a real TOC
|
|
|
|
|
// No TOC, no CD (or an empty one)
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading full TOC");
|
2019-12-14 18:17:17 +00:00
|
|
|
UpdateStatus?.Invoke("Reading full TOC");
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadRawToc(out cmdBuf, out senseBuf, 0, _dev.Timeout, out _);
|
2019-12-14 18:17:17 +00:00
|
|
|
|
|
|
|
|
if(!sense)
|
|
|
|
|
{
|
|
|
|
|
toc = FullTOC.Decode(cmdBuf);
|
|
|
|
|
|
|
|
|
|
if(toc.HasValue)
|
|
|
|
|
{
|
|
|
|
|
tmpBuf = new byte[cmdBuf.Length - 2];
|
|
|
|
|
Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2);
|
|
|
|
|
mediaTags.Add(MediaTagType.CD_FullTOC, tmpBuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-14 18:57:54 +00:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Building track map...");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Building track map...");
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(toc.HasValue)
|
2018-02-04 22:06:14 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
FullTOC.TrackDataDescriptor[] sortedTracks =
|
2018-02-04 22:06:14 +00:00
|
|
|
toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4))
|
|
|
|
|
if(trk.POINT >= 0x01 &&
|
|
|
|
|
trk.POINT <= 0x63)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2018-02-04 22:06:14 +00:00
|
|
|
trackList.Add(new Track
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
TrackSequence = trk.POINT, TrackSession = trk.SessionNumber,
|
|
|
|
|
TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
|
|
|
|
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
|
|
|
|
|
? TrackType.Data : TrackType.Audio,
|
2018-02-04 22:06:14 +00:00
|
|
|
TrackStartSector =
|
2019-12-14 18:17:17 +00:00
|
|
|
(ulong)(((trk.PHOUR * 3600 * 75) + (trk.PMIN * 60 * 75) + (trk.PSEC * 75) +
|
|
|
|
|
trk.PFRAME) - 150),
|
2019-12-14 22:55:26 +00:00
|
|
|
TrackBytesPerSector = (int)sectorSize,
|
|
|
|
|
TrackRawBytesPerSector = (int)sectorSize,
|
2019-12-14 18:17:17 +00:00
|
|
|
TrackSubchannelType = subType
|
2018-02-04 22:06:14 +00:00
|
|
|
});
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-02-04 22:06:14 +00:00
|
|
|
trackFlags.Add(trk.POINT, trk.CONTROL);
|
|
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(trk.POINT == 0xA2)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2018-02-04 22:06:14 +00:00
|
|
|
int phour, pmin, psec, pframe;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
if(trk.PFRAME == 0)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2018-02-04 22:06:14 +00:00
|
|
|
pframe = 74;
|
2017-06-07 22:37:05 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(trk.PSEC == 0)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2018-02-04 22:06:14 +00:00
|
|
|
psec = 59;
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(trk.PMIN == 0)
|
2018-02-04 22:06:14 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
pmin = 59;
|
2018-02-04 22:06:14 +00:00
|
|
|
phour = trk.PHOUR - 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
pmin = trk.PMIN - 1;
|
2018-02-04 22:06:14 +00:00
|
|
|
phour = trk.PHOUR;
|
|
|
|
|
}
|
2017-06-07 22:37:05 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
psec = trk.PSEC - 1;
|
|
|
|
|
pmin = trk.PMIN;
|
2017-06-07 22:37:05 +01:00
|
|
|
phour = trk.PHOUR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-02-04 22:06:14 +00:00
|
|
|
pframe = trk.PFRAME - 1;
|
2019-11-18 20:59:16 +00:00
|
|
|
psec = trk.PSEC;
|
|
|
|
|
pmin = trk.PMIN;
|
|
|
|
|
phour = trk.PHOUR;
|
2017-06-07 22:37:05 +01:00
|
|
|
}
|
2018-02-04 22:06:14 +00:00
|
|
|
|
2019-12-14 18:17:17 +00:00
|
|
|
lastSector = ((phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe) - 150;
|
|
|
|
|
leadOutStarts.Add(trk.SessionNumber, lastSector + 1);
|
2017-06-07 22:37:05 +01:00
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(trk.POINT == 0xA0 &&
|
|
|
|
|
trk.ADR == 1)
|
2017-12-21 07:08:26 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
switch(trk.PSEC)
|
2018-02-04 20:24:48 +00:00
|
|
|
{
|
|
|
|
|
case 0x10:
|
|
|
|
|
dskType = MediaType.CDI;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-02-04 20:24:48 +00:00
|
|
|
break;
|
|
|
|
|
case 0x20:
|
2019-11-18 20:59:16 +00:00
|
|
|
if(dskType == MediaType.CD ||
|
|
|
|
|
dskType == MediaType.CDROM)
|
|
|
|
|
dskType = MediaType.CDROMXA;
|
|
|
|
|
|
2018-02-04 20:24:48 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-06-07 22:37:05 +01:00
|
|
|
|
2019-12-31 01:35:49 +00:00
|
|
|
leadoutTrackType =
|
2019-11-18 20:59:16 +00:00
|
|
|
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
|
|
|
|
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
|
2018-02-04 22:06:14 +00:00
|
|
|
: TrackType.Audio;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2018-02-04 18:51:04 +00:00
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Cannot read RAW TOC, requesting processed one...");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Cannot read RAW TOC, requesting processed one...");
|
|
|
|
|
sense = _dev.ReadToc(out cmdBuf, out senseBuf, false, 0, _dev.Timeout, out _);
|
2018-02-04 22:06:14 +00:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
TOC.CDTOC? oldToc = TOC.Decode(cmdBuf);
|
|
|
|
|
|
2019-12-14 18:17:17 +00:00
|
|
|
if((sense || !oldToc.HasValue) &&
|
2019-12-25 18:07:05 +00:00
|
|
|
!_force)
|
2018-02-04 18:51:04 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000...");
|
|
|
|
|
|
2018-02-04 18:51:04 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc.
|
|
|
|
|
Value.TrackDescriptors.OrderBy(t => t.TrackNumber).
|
|
|
|
|
Where(trk => trk.ADR == 1 || trk.ADR == 4))
|
|
|
|
|
if(trk.TrackNumber >= 0x01 &&
|
|
|
|
|
trk.TrackNumber <= 0x63)
|
2018-02-04 22:06:14 +00:00
|
|
|
{
|
|
|
|
|
trackList.Add(new Track
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
TrackSequence = trk.TrackNumber, TrackSession = 1,
|
|
|
|
|
TrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
|
|
|
|
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental
|
|
|
|
|
? TrackType.Data : TrackType.Audio,
|
2019-12-14 22:55:26 +00:00
|
|
|
TrackStartSector = trk.TrackStartAddress, TrackBytesPerSector = (int)sectorSize,
|
|
|
|
|
TrackRawBytesPerSector = (int)sectorSize, TrackSubchannelType = subType
|
2018-02-04 22:06:14 +00:00
|
|
|
});
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-02-04 22:06:14 +00:00
|
|
|
trackFlags.Add(trk.TrackNumber, trk.CONTROL);
|
|
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(trk.TrackNumber == 0xAA)
|
2018-02-04 18:51:04 +00:00
|
|
|
{
|
2019-12-31 01:35:49 +00:00
|
|
|
leadoutTrackType =
|
2019-11-18 20:59:16 +00:00
|
|
|
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack ||
|
|
|
|
|
(TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data
|
2018-02-04 22:06:14 +00:00
|
|
|
: TrackType.Audio;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-02-04 22:06:14 +00:00
|
|
|
lastSector = trk.TrackStartAddress - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(trackList.Count == 0)
|
2018-02-04 22:06:14 +00:00
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("No tracks found, adding a single track from 0 to Lead-Out");
|
2018-02-04 22:06:14 +00:00
|
|
|
|
|
|
|
|
trackList.Add(new Track
|
|
|
|
|
{
|
2019-12-31 01:35:49 +00:00
|
|
|
TrackSequence = 1, TrackSession = 1, TrackType = leadoutTrackType,
|
2019-11-18 20:59:16 +00:00
|
|
|
TrackStartSector = 0,
|
2019-12-14 22:55:26 +00:00
|
|
|
TrackBytesPerSector = (int)sectorSize, TrackRawBytesPerSector = (int)sectorSize,
|
2019-10-26 02:21:41 +01:00
|
|
|
TrackSubchannelType = subType
|
2018-02-04 22:06:14 +00:00
|
|
|
});
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-31 01:35:49 +00:00
|
|
|
trackFlags.Add(1, (byte)(leadoutTrackType == TrackType.Audio ? 0 : 4));
|
2018-02-04 18:51:04 +00:00
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(lastSector == 0)
|
2018-02-04 18:51:38 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCapacity16(out cmdBuf, out senseBuf, _dev.Timeout, out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
if(!sense)
|
2018-02-04 22:06:14 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
byte[] temp = new byte[8];
|
2018-06-19 22:48:51 +01:00
|
|
|
|
|
|
|
|
Array.Copy(cmdBuf, 0, temp, 0, 8);
|
|
|
|
|
Array.Reverse(temp);
|
2019-11-18 20:59:16 +00:00
|
|
|
lastSector = (long)BitConverter.ToUInt64(temp, 0);
|
|
|
|
|
blockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]);
|
2018-06-19 22:48:51 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCapacity(out cmdBuf, out senseBuf, _dev.Timeout, out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
if(!sense)
|
2018-06-19 22:48:51 +01:00
|
|
|
{
|
2018-06-20 22:22:21 +01:00
|
|
|
lastSector = (cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3];
|
2019-11-18 20:59:16 +00:00
|
|
|
blockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]);
|
2018-06-19 22:48:51 +01:00
|
|
|
}
|
2018-02-04 22:06:14 +00:00
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(lastSector <= 0)
|
2018-06-19 22:48:51 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_force)
|
2018-06-19 22:48:51 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.
|
2019-11-18 20:59:16 +00:00
|
|
|
WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors...");
|
|
|
|
|
|
2018-06-19 22:48:51 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before...");
|
2018-06-19 22:48:51 +01:00
|
|
|
lastSector = 360000;
|
|
|
|
|
}
|
2018-02-04 18:51:38 +00:00
|
|
|
}
|
2018-02-04 22:06:14 +00:00
|
|
|
|
2019-12-14 20:39:15 +00:00
|
|
|
tracks = trackList.ToArray();
|
|
|
|
|
|
|
|
|
|
for(int t = 1; t < tracks.Length; t++)
|
|
|
|
|
tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1;
|
|
|
|
|
|
|
|
|
|
tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector;
|
|
|
|
|
blocks = (ulong)(lastSector + 1);
|
|
|
|
|
|
|
|
|
|
if(blocks == 0)
|
|
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Cannot dump blank media.");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ResumeSupport.Process(true, true, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId,
|
|
|
|
|
ref _resume, ref currentTry, ref extents);
|
2019-12-14 21:27:15 +00:00
|
|
|
|
|
|
|
|
if(currentTry == null ||
|
|
|
|
|
extents == null)
|
|
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-14 20:05:30 +00:00
|
|
|
// ATIP exists on blank CDs
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading ATIP");
|
2019-12-14 20:05:30 +00:00
|
|
|
UpdateStatus?.Invoke("Reading ATIP");
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadAtip(out cmdBuf, out senseBuf, _dev.Timeout, out _);
|
2019-12-14 20:05:30 +00:00
|
|
|
|
|
|
|
|
if(!sense)
|
|
|
|
|
{
|
|
|
|
|
ATIP.CDATIP? atip = ATIP.Decode(cmdBuf);
|
|
|
|
|
|
|
|
|
|
if(atip.HasValue)
|
|
|
|
|
{
|
|
|
|
|
// Only CD-R and CD-RW have ATIP
|
|
|
|
|
dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR;
|
|
|
|
|
|
|
|
|
|
tmpBuf = new byte[cmdBuf.Length - 4];
|
|
|
|
|
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
|
|
|
|
|
mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading Disc Information");
|
2019-12-14 20:05:30 +00:00
|
|
|
UpdateStatus?.Invoke("Reading Disc Information");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadDiscInformation(out cmdBuf, out senseBuf, MmcDiscInformationDataTypes.DiscInformation,
|
|
|
|
|
_dev.Timeout, out _);
|
2019-12-14 20:05:30 +00:00
|
|
|
|
|
|
|
|
if(!sense)
|
|
|
|
|
{
|
|
|
|
|
DiscInformation.StandardDiscInformation? discInfo = DiscInformation.Decode000b(cmdBuf);
|
|
|
|
|
|
|
|
|
|
if(discInfo.HasValue &&
|
|
|
|
|
dskType == MediaType.CD)
|
|
|
|
|
switch(discInfo.Value.DiscType)
|
|
|
|
|
{
|
|
|
|
|
case 0x10:
|
|
|
|
|
dskType = MediaType.CDI;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case 0x20:
|
|
|
|
|
dskType = MediaType.CDROMXA;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading PMA");
|
2019-12-14 20:05:30 +00:00
|
|
|
UpdateStatus?.Invoke("Reading PMA");
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadPma(out cmdBuf, out senseBuf, _dev.Timeout, out _);
|
2019-12-14 20:05:30 +00:00
|
|
|
|
|
|
|
|
if(!sense &&
|
|
|
|
|
PMA.Decode(cmdBuf).HasValue)
|
|
|
|
|
{
|
|
|
|
|
tmpBuf = new byte[cmdBuf.Length - 4];
|
|
|
|
|
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
|
|
|
|
|
mediaTags.Add(MediaTagType.CD_PMA, tmpBuf);
|
|
|
|
|
}
|
2019-12-14 20:30:35 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading Session Information");
|
2019-12-14 20:30:35 +00:00
|
|
|
UpdateStatus?.Invoke("Reading Session Information");
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadSessionInfo(out cmdBuf, out senseBuf, _dev.Timeout, out _);
|
2019-12-14 20:30:35 +00:00
|
|
|
|
|
|
|
|
if(!sense)
|
|
|
|
|
{
|
|
|
|
|
Session.CDSessionInfo? session = Session.Decode(cmdBuf);
|
|
|
|
|
|
|
|
|
|
if(session.HasValue)
|
|
|
|
|
{
|
|
|
|
|
sessions = session.Value.LastCompleteSession;
|
|
|
|
|
firstTrackLastSession = session.Value.TrackDescriptors[0].TrackNumber;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-14 20:50:18 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading CD-Text from Lead-In");
|
2019-12-14 20:50:18 +00:00
|
|
|
UpdateStatus?.Invoke("Reading CD-Text from Lead-In");
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCdText(out cmdBuf, out senseBuf, _dev.Timeout, out _);
|
2019-12-14 20:50:18 +00:00
|
|
|
|
|
|
|
|
if(!sense &&
|
|
|
|
|
CDTextOnLeadIn.Decode(cmdBuf).HasValue)
|
|
|
|
|
{
|
|
|
|
|
tmpBuf = new byte[cmdBuf.Length - 4];
|
|
|
|
|
Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4);
|
|
|
|
|
mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf);
|
|
|
|
|
}
|
2019-12-14 20:56:39 +00:00
|
|
|
|
2019-12-14 21:06:16 +00:00
|
|
|
// Check if output format supports all disc tags we have retrieved so far
|
|
|
|
|
foreach(MediaTagType tag in mediaTags.Keys)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_outputPlugin.SupportedMediaTags.Contains(tag))
|
2019-12-14 21:06:16 +00:00
|
|
|
continue;
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_force)
|
2019-12-14 21:06:16 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support {0}, continuing...", tag);
|
2019-12-14 21:06:16 +00:00
|
|
|
ErrorMessage?.Invoke($"Output format does not support {tag}, continuing...");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support {0}, not continuing...", tag);
|
2019-12-14 21:06:16 +00:00
|
|
|
StoppingErrorMessage?.Invoke($"Output format does not support {tag}, not continuing...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-14 20:56:39 +00:00
|
|
|
if(leadOutStarts.Any())
|
|
|
|
|
{
|
|
|
|
|
UpdateStatus?.Invoke("Solving lead-outs...");
|
|
|
|
|
|
|
|
|
|
foreach(KeyValuePair<int, long> leadOuts in leadOutStarts)
|
|
|
|
|
for(int i = 0; i < tracks.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if(tracks[i].TrackSession != leadOuts.Key)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if(tracks[i].TrackEndSector >= (ulong)leadOuts.Value)
|
|
|
|
|
tracks[i].TrackEndSector = (ulong)leadOuts.Value - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dataExtents = new ExtentsULong();
|
|
|
|
|
|
|
|
|
|
foreach(Track trk in tracks)
|
|
|
|
|
dataExtents.Add(trk.TrackStartSector, trk.TrackEndSector);
|
|
|
|
|
|
|
|
|
|
Tuple<ulong, ulong>[] dataExtentsArray = dataExtents.ToArray();
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < dataExtentsArray.Length - 1; i++)
|
|
|
|
|
leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1);
|
|
|
|
|
}
|
2019-12-14 21:03:52 +00:00
|
|
|
|
|
|
|
|
// Check for hidden data before start of track 1
|
|
|
|
|
if(tracks.First(t => t.TrackSequence == 1).TrackStartSector > 0 && readcd)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("First track starts after sector 0, checking for a hidden track...");
|
2019-12-14 21:03:52 +00:00
|
|
|
UpdateStatus?.Invoke("First track starts after sector 0, checking for a hidden track...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, false,
|
|
|
|
|
true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
|
|
|
|
supportedSubchannel, _dev.Timeout, out _);
|
2019-12-14 21:03:52 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2019-12-14 21:03:52 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Could not read sector 0, continuing...");
|
2019-12-14 21:03:52 +00:00
|
|
|
UpdateStatus?.Invoke("Could not read sector 0, continuing...");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
byte[] syncMark =
|
|
|
|
|
{
|
|
|
|
|
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] cdiMark =
|
|
|
|
|
{
|
|
|
|
|
0x01, 0x43, 0x44, 0x2D
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] testMark = new byte[12];
|
|
|
|
|
Array.Copy(cmdBuf, 0, testMark, 0, 12);
|
|
|
|
|
|
|
|
|
|
bool hiddenData = syncMark.SequenceEqual(testMark) &&
|
|
|
|
|
(cmdBuf[0xF] == 0 || cmdBuf[0xF] == 1 || cmdBuf[0xF] == 2);
|
|
|
|
|
|
|
|
|
|
if(hiddenData && cmdBuf[0xF] == 2)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, 16, blockSize, 1, MmcSectorTypes.AllTypes, false,
|
|
|
|
|
false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
|
|
|
|
supportedSubchannel, _dev.Timeout, out _);
|
2019-12-14 21:03:52 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_dev.Error &&
|
2019-12-14 21:03:52 +00:00
|
|
|
!sense)
|
|
|
|
|
{
|
|
|
|
|
testMark = new byte[4];
|
|
|
|
|
Array.Copy(cmdBuf, 24, testMark, 0, 4);
|
|
|
|
|
|
|
|
|
|
if(cdiMark.SequenceEqual(testMark))
|
|
|
|
|
dskType = MediaType.CDIREADY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<Track> trkList = new List<Track>
|
|
|
|
|
{
|
|
|
|
|
new Track
|
|
|
|
|
{
|
|
|
|
|
TrackSequence = 0,
|
|
|
|
|
TrackSession = 1,
|
|
|
|
|
TrackType = hiddenData ? TrackType.Data : TrackType.Audio,
|
|
|
|
|
TrackStartSector = 0,
|
2019-12-14 22:55:26 +00:00
|
|
|
TrackBytesPerSector = (int)sectorSize,
|
|
|
|
|
TrackRawBytesPerSector = (int)sectorSize,
|
2019-12-14 21:03:52 +00:00
|
|
|
TrackSubchannelType = subType,
|
|
|
|
|
TrackEndSector = tracks.First(t => t.TrackSequence == 1).TrackStartSector - 1
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
trkList.AddRange(tracks);
|
|
|
|
|
tracks = trkList.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-14 20:05:30 +00:00
|
|
|
|
|
|
|
|
if(dskType == MediaType.CD ||
|
|
|
|
|
dskType == MediaType.CDROMXA)
|
|
|
|
|
{
|
2019-12-14 21:09:16 +00:00
|
|
|
// TODO: Add other detectors here
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Detecting disc type...");
|
2019-12-14 21:09:16 +00:00
|
|
|
UpdateStatus?.Invoke("Detecting disc type...");
|
|
|
|
|
|
2019-12-14 20:05:30 +00:00
|
|
|
bool hasDataTrack = false;
|
|
|
|
|
bool hasAudioTrack = false;
|
|
|
|
|
bool allFirstSessionTracksAreAudio = true;
|
|
|
|
|
bool hasVideoTrack = false;
|
|
|
|
|
|
|
|
|
|
foreach(FullTOC.TrackDataDescriptor track in toc.Value.TrackDescriptors)
|
|
|
|
|
{
|
|
|
|
|
if(track.TNO == 1 &&
|
|
|
|
|
((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
|
|
|
|
|
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental))
|
|
|
|
|
allFirstSessionTracksAreAudio &= firstTrackLastSession != 1;
|
|
|
|
|
|
|
|
|
|
if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack ||
|
|
|
|
|
(TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)
|
|
|
|
|
{
|
|
|
|
|
hasDataTrack = true;
|
|
|
|
|
allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
hasAudioTrack = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasVideoTrack |= track.ADR == 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(hasDataTrack &&
|
|
|
|
|
hasAudioTrack &&
|
|
|
|
|
allFirstSessionTracksAreAudio &&
|
|
|
|
|
sessions == 2)
|
|
|
|
|
dskType = MediaType.CDPLUS;
|
|
|
|
|
|
|
|
|
|
if(!hasDataTrack &&
|
|
|
|
|
hasAudioTrack &&
|
|
|
|
|
sessions == 1)
|
|
|
|
|
dskType = MediaType.CDDA;
|
|
|
|
|
|
|
|
|
|
if(hasDataTrack &&
|
|
|
|
|
!hasAudioTrack &&
|
|
|
|
|
sessions == 1)
|
|
|
|
|
dskType = MediaType.CDROM;
|
|
|
|
|
|
|
|
|
|
if(hasVideoTrack &&
|
|
|
|
|
!hasDataTrack &&
|
|
|
|
|
sessions == 1)
|
|
|
|
|
dskType = MediaType.CDV;
|
|
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
byte[] videoNowColorFrame = new byte[9 * sectorSize];
|
2019-12-14 20:05:30 +00:00
|
|
|
|
2019-12-14 21:09:16 +00:00
|
|
|
for(int i = 0; i < 9; i++)
|
2019-12-14 20:05:30 +00:00
|
|
|
{
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, sectorSize, 1, MmcSectorTypes.AllTypes,
|
|
|
|
|
false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
2019-12-25 18:07:05 +00:00
|
|
|
MmcSubchannel.None, _dev.Timeout, out _);
|
2019-12-14 20:05:30 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(sense || _dev.Error)
|
2019-12-14 20:05:30 +00:00
|
|
|
{
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, sectorSize, 1, MmcSectorTypes.Cdda,
|
|
|
|
|
false, false, true, MmcHeaderCodes.None, true, true, MmcErrorField.None,
|
2019-12-25 18:07:05 +00:00
|
|
|
MmcSubchannel.None, _dev.Timeout, out _);
|
2019-12-14 20:05:30 +00:00
|
|
|
|
2019-12-26 01:24:57 +00:00
|
|
|
if(sense || _dev.Error)
|
2019-12-14 21:09:16 +00:00
|
|
|
{
|
|
|
|
|
videoNowColorFrame = null;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-12-14 20:05:30 +00:00
|
|
|
}
|
2019-12-14 21:09:16 +00:00
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(cmdBuf, 0, videoNowColorFrame, i * sectorSize, sectorSize);
|
2019-12-14 20:05:30 +00:00
|
|
|
}
|
|
|
|
|
|
2019-12-14 21:09:16 +00:00
|
|
|
if(MMC.IsVideoNowColor(videoNowColorFrame))
|
|
|
|
|
dskType = MediaType.VideoNowColor;
|
2019-12-14 20:05:30 +00:00
|
|
|
}
|
|
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
// Check mode for tracks
|
2019-11-18 20:59:16 +00:00
|
|
|
for(int t = 0; t < tracks.Length; t++)
|
2018-01-20 17:12:01 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
if(!readcd)
|
2018-06-19 22:17:20 +01:00
|
|
|
{
|
|
|
|
|
tracks[t].TrackType = TrackType.CdMode1;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-06-19 22:17:20 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(tracks[t].TrackType == TrackType.Audio)
|
|
|
|
|
continue;
|
2018-06-19 22:48:51 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Checking mode for track {0}...", tracks[t].TrackSequence);
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Checking mode for track {tracks[t].TrackSequence}...");
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = !_dev.ReadCd(out cmdBuf, out senseBuf, (uint)tracks[t].TrackStartSector, blockSize, 1,
|
|
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true,
|
|
|
|
|
MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _);
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-14 21:03:52 +00:00
|
|
|
if(!sense)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", tracks[t].TrackSequence);
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Unable to guess mode for track {tracks[t].TrackSequence}, continuing...");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
continue;
|
2017-06-07 22:37:05 +01:00
|
|
|
}
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-14 19:37:52 +00:00
|
|
|
switch(cmdBuf[15])
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2018-01-20 17:12:01 +00:00
|
|
|
case 1:
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE1");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Track {0} is MODE1", tracks[t].TrackSequence);
|
2018-01-20 17:12:01 +00:00
|
|
|
tracks[t].TrackType = TrackType.CdMode1;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
break;
|
|
|
|
|
case 2:
|
2019-11-18 20:59:16 +00:00
|
|
|
if(dskType == MediaType.CDI ||
|
|
|
|
|
dskType == MediaType.CDIREADY)
|
2018-06-24 10:57:58 +01:00
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Track {0} is MODE2", tracks[t].TrackSequence);
|
2018-06-24 10:57:58 +01:00
|
|
|
tracks[t].TrackType = TrackType.CdMode2Formless;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-06-24 10:57:58 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-14 19:37:52 +00:00
|
|
|
if((cmdBuf[0x012] & 0x20) == 0x20) // mode 2 form 2
|
2018-06-24 10:57:58 +01:00
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 2");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Track {0} is MODE2 FORM 2", tracks[t].TrackSequence);
|
2018-06-24 10:57:58 +01:00
|
|
|
tracks[t].TrackType = TrackType.CdMode2Form2;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-06-24 10:57:58 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is MODE2 FORM 1");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Track {0} is MODE2 FORM 1", tracks[t].TrackSequence);
|
2018-06-24 10:57:58 +01:00
|
|
|
tracks[t].TrackType = TrackType.CdMode2Form1;
|
2019-08-15 18:35:38 +01:00
|
|
|
|
|
|
|
|
// These media type specifications do not legally allow mode 2 tracks to be present
|
2019-11-18 20:59:16 +00:00
|
|
|
if(dskType == MediaType.CDROM ||
|
|
|
|
|
dskType == MediaType.CDPLUS ||
|
|
|
|
|
dskType == MediaType.CDV)
|
2019-08-15 18:35:38 +01:00
|
|
|
dskType = MediaType.CD;
|
|
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2019-12-14 19:37:52 +00:00
|
|
|
UpdateStatus?.Invoke($"Track {tracks[t].TrackSequence} is unknown mode {cmdBuf[15]}");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Track {0} is unknown mode {1}", tracks[t].TrackSequence, cmdBuf[15]);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
break;
|
2017-06-07 22:37:05 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-12-14 21:13:31 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_outputPlugin.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000"))
|
2019-12-14 21:13:31 +00:00
|
|
|
{
|
|
|
|
|
if(tracks.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Output format does not support more than 1 track, not continuing...");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support more than 1 track, not continuing...");
|
2019-12-14 21:13:31 +00:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(tracks.Any(t => t.TrackType == TrackType.Audio))
|
|
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Output format does not support audio tracks, not continuing...");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support audio tracks, not continuing...");
|
2019-12-14 21:13:31 +00:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(tracks.Any(t => t.TrackType != TrackType.CdMode1))
|
|
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Output format only supports MODE 1 tracks, not continuing...");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format only supports MODE 1 tracks, not continuing...");
|
2019-12-14 21:13:31 +00:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
supportsLongSectors = false;
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-12-31 21:16:52 +00:00
|
|
|
// Check if something prevents from dumping the first track pregap
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dumpFirstTrackPregap && readcd)
|
2017-11-29 15:14:21 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.PlatformId == PlatformID.FreeBSD &&
|
|
|
|
|
!_dev.IsRemote)
|
2018-01-20 17:12:01 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_force)
|
2019-04-19 20:13:17 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.
|
2019-11-18 20:59:16 +00:00
|
|
|
WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing");
|
|
|
|
|
|
|
|
|
|
ErrorMessage?.
|
|
|
|
|
Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. continuing");
|
2019-04-19 20:13:17 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.
|
2019-11-18 20:59:16 +00:00
|
|
|
WriteLine("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing");
|
|
|
|
|
|
|
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("FreeBSD panics when reading CD first track pregap, see upstream bug #224253. Not continuing");
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpFirstTrackPregap = false;
|
2018-01-20 17:12:01 +00:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap))
|
2018-01-20 17:12:01 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_force)
|
2019-04-19 20:13:17 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support CD first track pregap, continuing...");
|
2019-04-19 20:13:17 +01:00
|
|
|
ErrorMessage?.Invoke("Output format does not support CD first track pregap, continuing...");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Output format does not support CD first track pregap, not continuing...");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
StoppingErrorMessage?.
|
|
|
|
|
Invoke("Output format does not support CD first track pregap, not continuing...");
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpFirstTrackPregap = false;
|
2018-01-20 17:12:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-01-27 17:47:40 +00:00
|
|
|
|
2018-12-31 21:16:52 +00:00
|
|
|
// Try to read the first track pregap
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dumpFirstTrackPregap && readcd)
|
2017-12-18 21:31:41 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
bool gotFirstTrackPregap = false;
|
|
|
|
|
int firstTrackPregapSectorsGood = 0;
|
|
|
|
|
var firstTrackPregapMs = new MemoryStream();
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-14 19:37:52 +00:00
|
|
|
cmdBuf = null;
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Reading first track pregap");
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Reading first track pregap");
|
|
|
|
|
InitProgress?.Invoke();
|
2019-12-14 22:02:36 +00:00
|
|
|
timeSpeedStart = DateTime.UtcNow;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
for(int firstTrackPregapBlock = -150; firstTrackPregapBlock < 0 && _resume.NextBlock == 0;
|
2018-12-31 21:16:52 +00:00
|
|
|
firstTrackPregapBlock++)
|
2017-11-20 05:07:16 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_aborted)
|
2017-12-18 21:31:41 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Aborted!");
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Aborted!");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2017-12-18 21:31:41 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
PulseProgress?.
|
|
|
|
|
Invoke($"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)");
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)firstTrackPregapBlock, blockSize, 1,
|
|
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
|
|
|
|
|
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out 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-14 19:37:52 +00:00
|
|
|
firstTrackPregapMs.Write(cmdBuf, 0, (int)blockSize);
|
2018-12-31 21:16:52 +00:00
|
|
|
gotFirstTrackPregap = true;
|
|
|
|
|
firstTrackPregapSectorsGood++;
|
2019-12-14 22:55:26 +00:00
|
|
|
totalDuration += cmdDuration;
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
2017-12-18 21:31:41 +00:00
|
|
|
else
|
|
|
|
|
{
|
2018-01-20 17:12:01 +00:00
|
|
|
// Write empty data
|
2019-11-18 20:59:16 +00:00
|
|
|
if(gotFirstTrackPregap)
|
|
|
|
|
firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize);
|
2017-12-18 21:31:41 +00:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-01-27 17:47:40 +00:00
|
|
|
sectorSpeedStart++;
|
|
|
|
|
|
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-12-14 18:17:17 +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-12-18 21:31:41 +00:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(firstTrackPregapSectorsGood > 0)
|
2018-12-31 21:16:52 +00:00
|
|
|
mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray());
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
EndProgress?.Invoke();
|
|
|
|
|
UpdateStatus?.Invoke($"Got {firstTrackPregapSectorsGood} first track pregap sectors.");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Got {0} first track pregap sectors.", firstTrackPregapSectorsGood);
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2018-12-31 21:16:52 +00:00
|
|
|
firstTrackPregapMs.Close();
|
2017-12-18 21:31:41 +00:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
// Try how many blocks are readable at once
|
2019-11-18 20:59:16 +00:00
|
|
|
while(true)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
if(readcd)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2019-12-26 01:34:24 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, 0, blockSize, _maximumReadable,
|
2019-12-25 21:03:21 +00:00
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
|
|
|
|
|
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable /= 2;
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read16)
|
2018-06-19 22:17:20 +01:00
|
|
|
{
|
2019-12-25 21:03:21 +00:00
|
|
|
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, 0, blockSize, 0,
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable, false, _dev.Timeout, out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable /= 2;
|
2018-06-19 22:17:20 +01:00
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read12)
|
2018-06-19 22:17:20 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, 0, blockSize, 0,
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable, false, _dev.Timeout, out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable /= 2;
|
2018-06-19 22:17:20 +01:00
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read10)
|
2018-06-19 22:17:20 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, 0, blockSize, 0,
|
2019-12-26 01:34:24 +00:00
|
|
|
(ushort)_maximumReadable, _dev.Timeout, out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable /= 2;
|
2018-06-19 22:17:20 +01:00
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read6)
|
2018-06-19 22:17:20 +01:00
|
|
|
{
|
2019-12-26 01:34:24 +00:00
|
|
|
sense = _dev.Read6(out cmdBuf, out senseBuf, 0, blockSize, (byte)_maximumReadable, _dev.Timeout,
|
2019-12-25 21:03:21 +00:00
|
|
|
out _);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable /= 2;
|
2018-06-19 22:17:20 +01:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_dev.Error ||
|
2019-12-26 01:34:24 +00:00
|
|
|
_maximumReadable == 1)
|
2019-11-18 20:59:16 +00:00
|
|
|
break;
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_dev.Error || sense)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError);
|
|
|
|
|
StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length.");
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
2019-12-14 21:38:18 +00:00
|
|
|
|
2019-12-26 01:34:24 +00:00
|
|
|
_dumpLog.WriteLine("Reading {0} sectors at a time.", _maximumReadable);
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
|
2019-12-26 01:34:24 +00:00
|
|
|
_dumpLog.WriteLine("Device can read {0} blocks at a time.", _maximumReadable);
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
|
|
|
|
|
_dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType);
|
|
|
|
|
_dumpLog.WriteLine("Media identified as {0}.", dskType);
|
2019-12-14 21:38:18 +00:00
|
|
|
|
2019-12-26 01:34:24 +00:00
|
|
|
UpdateStatus?.Invoke($"Reading {_maximumReadable} sectors at a time.");
|
2019-12-14 21:38:18 +00:00
|
|
|
UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");
|
2019-12-26 01:34:24 +00:00
|
|
|
UpdateStatus?.Invoke($"Device can read {_maximumReadable} blocks at a time.");
|
2019-12-14 21:38:18 +00:00
|
|
|
UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
|
2019-12-25 18:07:05 +00:00
|
|
|
UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}.");
|
2019-12-14 21:38:18 +00:00
|
|
|
UpdateStatus?.Invoke($"Media identified as {dskType}.");
|
2019-12-14 21:41:18 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ret = _outputPlugin.Create(_outputPath, dskType, _formatOptions, blocks,
|
|
|
|
|
supportsLongSectors ? blockSize : 2048);
|
2019-12-14 21:41:18 +00:00
|
|
|
|
|
|
|
|
// Cannot create image
|
|
|
|
|
if(!ret)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Error creating output image, not continuing.");
|
|
|
|
|
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
|
2019-12-14 21:41:18 +00:00
|
|
|
|
|
|
|
|
StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine +
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.ErrorMessage);
|
2019-12-14 21:41:18 +00:00
|
|
|
}
|
2019-12-14 21:41:43 +00:00
|
|
|
|
2019-12-14 22:55:26 +00:00
|
|
|
// Send track list to output plugin. This may fail if subchannel is set but unsupported.
|
2019-12-25 18:07:05 +00:00
|
|
|
ret = (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
|
2019-12-14 21:41:43 +00:00
|
|
|
|
|
|
|
|
if(!ret &&
|
|
|
|
|
supportedSubchannel == MmcSubchannel.None)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
|
|
|
|
|
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
|
2019-12-14 21:41:43 +00:00
|
|
|
|
|
|
|
|
StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing." +
|
|
|
|
|
Environment.NewLine +
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.ErrorMessage);
|
2019-12-14 21:41:43 +00:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
// If a subchannel is supported, check if output plugin allows us to write it.
|
2019-11-18 20:59:16 +00:00
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dev.ReadCd(out cmdBuf, out senseBuf, 0, blockSize, 1, MmcSectorTypes.AllTypes, false, false, true,
|
|
|
|
|
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel,
|
|
|
|
|
_dev.Timeout, out _);
|
2017-11-20 05:07:16 +00:00
|
|
|
|
2019-12-14 18:17:17 +00:00
|
|
|
tmpBuf = new byte[subSize];
|
2019-12-14 22:55:26 +00:00
|
|
|
Array.Copy(cmdBuf, sectorSize, tmpBuf, 0, subSize);
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ret = _outputPlugin.WriteSectorTag(tmpBuf, 0, SectorTagType.CdSectorSubchannel);
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(!ret)
|
2017-06-07 23:25:06 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_force)
|
2019-04-19 20:13:17 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Error writing subchannel to output image, {0}continuing...",
|
|
|
|
|
_force ? "" : "not ");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
ErrorMessage?.Invoke("Error writing subchannel to output image, continuing..." +
|
2019-11-18 20:59:16 +00:00
|
|
|
Environment.NewLine +
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.ErrorMessage);
|
2019-04-19 20:13:17 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Error writing subchannel to output image, not continuing..." +
|
2019-11-18 20:59:16 +00:00
|
|
|
Environment.NewLine +
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.ErrorMessage);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2018-04-02 23:08:26 +01:00
|
|
|
supportedSubchannel = MmcSubchannel.None;
|
2019-11-18 20:59:16 +00:00
|
|
|
subSize = 0;
|
2019-12-14 22:55:26 +00:00
|
|
|
blockSize = sectorSize + subSize;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
for(int t = 0; t < tracks.Length; t++)
|
|
|
|
|
tracks[t].TrackSubchannelType = TrackSubchannelType.None;
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ret = (_outputPlugin as IWritableOpticalImage).SetTracks(tracks.ToList());
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
if(!ret)
|
2018-01-20 17:12:01 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
|
|
|
|
|
_dumpLog.WriteLine(_outputPlugin.ErrorMessage);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing..." +
|
2019-11-18 20:59:16 +00:00
|
|
|
Environment.NewLine +
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.ErrorMessage);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2017-06-07 23:25:06 +01:00
|
|
|
}
|
2018-01-20 17:12:01 +00:00
|
|
|
}
|
2019-12-14 21:45:23 +00:00
|
|
|
|
|
|
|
|
// Set track flags
|
|
|
|
|
foreach(KeyValuePair<byte, byte> kvp in trackFlags)
|
|
|
|
|
{
|
|
|
|
|
Track track = tracks.FirstOrDefault(t => t.TrackSequence == kvp.Key);
|
|
|
|
|
|
|
|
|
|
if(track.TrackSequence == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Setting flags for track {0}...", track.TrackSequence);
|
2019-12-14 21:45:23 +00:00
|
|
|
UpdateStatus?.Invoke($"Setting flags for track {track.TrackSequence}...");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorTag(new[]
|
2019-12-14 21:45:23 +00:00
|
|
|
{
|
|
|
|
|
kvp.Value
|
|
|
|
|
}, track.TrackStartSector, SectorTagType.CdTrackFlags);
|
|
|
|
|
}
|
2019-12-14 21:46:58 +00:00
|
|
|
|
|
|
|
|
// Set MCN
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadMcn(out string mcn, out _, out _, _dev.Timeout, out _);
|
2019-12-14 21:46:58 +00:00
|
|
|
|
|
|
|
|
if(!sense &&
|
|
|
|
|
mcn != null &&
|
|
|
|
|
mcn != "0000000000000" &&
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN))
|
2019-12-14 21:46:58 +00:00
|
|
|
{
|
|
|
|
|
UpdateStatus?.Invoke($"Setting disc Media Catalogue Number to {mcn}");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Setting disc Media Catalogue Number to {0}", mcn);
|
2019-12-14 21:46:58 +00:00
|
|
|
}
|
2019-12-14 21:47:26 +00:00
|
|
|
|
|
|
|
|
// Set ISRCs
|
|
|
|
|
foreach(Track trk in tracks)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadIsrc((byte)trk.TrackSequence, out string isrc, out _, out _, _dev.Timeout, out _);
|
2019-12-14 21:47:26 +00:00
|
|
|
|
|
|
|
|
if(sense ||
|
|
|
|
|
isrc == null ||
|
|
|
|
|
isrc == "000000000000")
|
|
|
|
|
continue;
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc), trk.TrackStartSector,
|
|
|
|
|
SectorTagType.CdTrackIsrc))
|
2019-12-14 21:47:26 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
UpdateStatus?.Invoke($"Setting ISRC for track {trk.TrackSequence} to {isrc}");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Setting ISRC for track {0} to {1}", trk.TrackSequence, isrc);
|
2019-12-14 21:47:26 +00:00
|
|
|
}
|
2019-12-14 21:51:51 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_resume.NextBlock > 0)
|
2019-12-14 21:51:51 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
|
|
|
|
|
_dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
|
2019-12-14 21:51:51 +00:00
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:34:24 +00:00
|
|
|
if(_skip < _maximumReadable)
|
|
|
|
|
_skip = _maximumReadable;
|
2019-12-14 21:51:51 +00:00
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
foreach(Track trk in tracks)
|
|
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke($"Track {trk.TrackSequence} starts at LBA {trk.TrackStartSector} and ends at LBA {trk.TrackEndSector}");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if(dskType == MediaType.CDIREADY)
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
|
2019-12-14 21:51:51 +00:00
|
|
|
|
|
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them.");
|
|
|
|
|
}
|
2019-12-14 21:45:00 +00:00
|
|
|
|
2019-12-25 18:25:25 +00:00
|
|
|
// Check offset
|
|
|
|
|
if(_fixOffset)
|
|
|
|
|
{
|
2019-12-31 19:40:29 +00:00
|
|
|
_fixOffset = Media.Info.CompactDisc.GetOffset(cdOffset, _dbDev, _debug, _dev, dskType, _dumpLog,
|
|
|
|
|
out offsetBytes, readcd, out sectorsForOffset, tracks,
|
|
|
|
|
UpdateStatus);
|
2019-12-25 18:25:25 +00:00
|
|
|
}
|
|
|
|
|
else if(tracks.Any(t => t.TrackType == TrackType.Audio))
|
|
|
|
|
{
|
|
|
|
|
_dumpLog.WriteLine("There are audio tracks and offset fixing is disabled, dump may not be correct.");
|
|
|
|
|
UpdateStatus?.Invoke("There are audio tracks and offset fixing is disabled, dump may not be correct.");
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:34:24 +00:00
|
|
|
mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, _maximumReadable);
|
2019-12-25 18:07:05 +00:00
|
|
|
ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008);
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-25 21:03:21 +00:00
|
|
|
audioExtents = new ExtentsULong();
|
2019-12-31 01:35:49 +00:00
|
|
|
nextData = tracks[0].TrackType != TrackType.Audio;
|
2019-12-25 21:03:21 +00:00
|
|
|
|
|
|
|
|
foreach(Track audioTrack in tracks.Where(t => t.TrackType == TrackType.Audio))
|
|
|
|
|
{
|
|
|
|
|
audioExtents.Add(audioTrack.TrackStartSector, audioTrack.TrackEndSector);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 03:45:09 +00:00
|
|
|
// Set speed
|
|
|
|
|
if(_speedMultiplier >= 0)
|
|
|
|
|
{
|
2019-12-31 17:32:12 +00:00
|
|
|
_dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX" : $"{_speed}x")}.");
|
2019-12-31 01:37:41 +00:00
|
|
|
UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX" : $"{_speed}x")}.");
|
2019-12-26 03:45:09 +00:00
|
|
|
|
|
|
|
|
_speed *= _speedMultiplier;
|
|
|
|
|
|
|
|
|
|
if(_speed == 0 ||
|
|
|
|
|
_speed > 0xFFFF)
|
|
|
|
|
_speed = 0xFFFF;
|
|
|
|
|
|
|
|
|
|
_dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
// Start reading
|
2019-11-18 20:59:16 +00:00
|
|
|
start = DateTime.UtcNow;
|
|
|
|
|
currentSpeed = 0;
|
2019-01-27 17:47:40 +00:00
|
|
|
sectorSpeedStart = 0;
|
2019-11-18 20:59:16 +00:00
|
|
|
timeSpeedStart = DateTime.UtcNow;
|
2019-04-19 20:13:17 +01:00
|
|
|
InitProgress?.Invoke();
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
for(ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead)
|
2018-01-20 17:12:01 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_aborted)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2019-12-14 22:13:27 +00:00
|
|
|
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
|
|
|
|
UpdateStatus?.Invoke("Aborted!");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Aborted!");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-31 02:59:19 +00:00
|
|
|
while(leadOutExtents.Contains(i))
|
|
|
|
|
{
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((long)i > lastSector)
|
|
|
|
|
break;
|
|
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
uint firstSectorToRead = (uint)i;
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-26 01:34:24 +00:00
|
|
|
if((lastSector + 1) - (long)i < _maximumReadable)
|
|
|
|
|
_maximumReadable = (uint)((lastSector + 1) - (long)i);
|
2019-12-25 21:03:21 +00:00
|
|
|
|
|
|
|
|
blocksToRead = 0;
|
|
|
|
|
bool inData = nextData;
|
|
|
|
|
|
2019-12-26 01:34:24 +00:00
|
|
|
for(ulong j = i; j < i + _maximumReadable; j++)
|
2019-12-25 21:03:21 +00:00
|
|
|
{
|
|
|
|
|
if(nextData)
|
|
|
|
|
{
|
|
|
|
|
if(audioExtents.Contains(j))
|
|
|
|
|
{
|
|
|
|
|
nextData = false;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
blocksToRead++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(!audioExtents.Contains(j))
|
|
|
|
|
{
|
|
|
|
|
nextData = true;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
blocksToRead++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-31 02:47:34 +00:00
|
|
|
if(blocksToRead == 1)
|
|
|
|
|
blocksToRead = 2;
|
|
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
if(_fixOffset && !inData)
|
|
|
|
|
{
|
|
|
|
|
// TODO: FreeBSD bug
|
2019-12-31 17:52:09 +00:00
|
|
|
if(offsetBytes < 0)
|
2019-12-25 22:15:06 +00:00
|
|
|
{
|
|
|
|
|
if(i == 0)
|
2019-12-26 01:11:22 +00:00
|
|
|
firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
2019-12-25 22:15:06 +00:00
|
|
|
else
|
2019-12-26 01:11:22 +00:00
|
|
|
firstSectorToRead -= (uint)sectorsForOffset;
|
2019-12-25 22:15:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
|
2019-12-14 22:55:26 +00:00
|
|
|
|
|
|
|
|
// ReSharper disable CompareOfFloatsByEqualityOperator
|
2019-12-14 22:13:27 +00:00
|
|
|
if(currentSpeed > maxSpeed &&
|
|
|
|
|
currentSpeed != 0)
|
|
|
|
|
maxSpeed = currentSpeed;
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
if(currentSpeed < minSpeed &&
|
|
|
|
|
currentSpeed != 0)
|
|
|
|
|
minSpeed = currentSpeed;
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-14 22:55:26 +00:00
|
|
|
// ReSharper restore CompareOfFloatsByEqualityOperator
|
|
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
|
|
|
|
|
|
|
|
|
|
UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
|
|
|
|
|
(long)blocks);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
if(readcd)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead,
|
2019-12-25 18:07:05 +00:00
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true,
|
|
|
|
|
true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
totalDuration += cmdDuration;
|
|
|
|
|
}
|
|
|
|
|
else if(read16)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, firstSectorToRead, blockSize,
|
|
|
|
|
0, blocksToRead, false, _dev.Timeout, out cmdDuration);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
|
|
|
|
else if(read12)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
|
|
|
|
|
blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
|
|
|
|
else if(read10)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, firstSectorToRead,
|
|
|
|
|
blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
|
|
|
|
else if(read6)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead,
|
|
|
|
|
_dev.Timeout, out cmdDuration);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
// Because one block has been partially used to fix the offset
|
|
|
|
|
if(_fixOffset &&
|
|
|
|
|
!inData &&
|
|
|
|
|
offsetBytes != 0)
|
|
|
|
|
{
|
2019-12-31 17:52:09 +00:00
|
|
|
int offsetFix = offsetBytes < 0 ? offsetFix = (int)(sectorSize - (offsetBytes * -1))
|
|
|
|
|
: offsetFix = offsetBytes;
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
// Deinterleave subchannel
|
|
|
|
|
byte[] data = new byte[sectorSize * blocksToRead];
|
|
|
|
|
byte[] sub = new byte[subSize * blocksToRead];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < blocksToRead; b++)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
|
|
|
|
|
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:11:22 +00:00
|
|
|
tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)];
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
|
|
|
|
|
data = tmpBuf;
|
|
|
|
|
|
2019-12-26 01:11:22 +00:00
|
|
|
blocksToRead -= (uint)sectorsForOffset;
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
// Reinterleave subchannel
|
|
|
|
|
cmdBuf = new byte[blockSize * blocksToRead];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < blocksToRead; b++)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);
|
|
|
|
|
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-26 01:11:22 +00:00
|
|
|
tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)];
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
|
2019-12-26 01:11:22 +00:00
|
|
|
cmdBuf = tmpBuf;
|
|
|
|
|
blocksToRead -= (uint)sectorsForOffset;
|
2019-12-25 22:15:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
if(!sense &&
|
2019-12-25 18:07:05 +00:00
|
|
|
!_dev.Error)
|
2019-12-14 22:13:27 +00:00
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
mhddLog.Write(i, cmdDuration);
|
|
|
|
|
ibgLog.Write(i, currentSpeed * 1024);
|
|
|
|
|
extents.Add(i, blocksToRead, true);
|
2019-12-14 22:13:27 +00:00
|
|
|
DateTime writeStart = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2019-12-14 22:55:26 +00:00
|
|
|
byte[] data = new byte[sectorSize * blocksToRead];
|
|
|
|
|
byte[] sub = new byte[subSize * blocksToRead];
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
for(int b = 0; b < blocksToRead; b++)
|
2018-01-25 23:31:27 +00:00
|
|
|
{
|
2019-12-14 22:55:26 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
|
2018-01-25 23:31:27 +00:00
|
|
|
|
2019-12-14 22:55:26 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
2018-01-25 23:31:27 +00:00
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsLong(data, i, blocksToRead);
|
|
|
|
|
_outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(supportsLongSectors)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
|
2018-01-25 23:31:27 +00:00
|
|
|
}
|
2018-11-24 14:23:35 +00:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 22:15:06 +00:00
|
|
|
if(cmdBuf.Length % sectorSize == 0)
|
2019-10-26 02:21:41 +01:00
|
|
|
{
|
2019-12-14 22:13:27 +00:00
|
|
|
byte[] data = new byte[2048 * blocksToRead];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < blocksToRead; b++)
|
|
|
|
|
Array.Copy(cmdBuf, (int)(16 + (b * blockSize)), data, 2048 * b, 2048);
|
|
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectors(data, i, blocksToRead);
|
2019-10-26 02:21:41 +01:00
|
|
|
}
|
2018-11-24 14:23:35 +00:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsLong(cmdBuf, i, blocksToRead);
|
2018-11-24 14:23:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2017-06-07 22:37:05 +01:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// TODO: Reset device after X errors
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_stopOnError)
|
2019-12-14 22:13:27 +00:00
|
|
|
return; // TODO: Return more cleanly
|
2018-02-04 22:06:14 +00:00
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
if(i + _skip > blocks)
|
|
|
|
|
_skip = (uint)(blocks - i);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
// Write empty data
|
|
|
|
|
DateTime writeStart = DateTime.Now;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip);
|
2019-12-14 22:13:27 +00:00
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsTag(new byte[subSize * _skip], i, _skip,
|
2019-12-25 18:07:05 +00:00
|
|
|
SectorTagType.CdSectorSubchannel);
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(supportsLongSectors)
|
|
|
|
|
{
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip);
|
2017-06-07 23:25:06 +01:00
|
|
|
}
|
2018-11-24 14:23:35 +00:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 22:15:06 +00:00
|
|
|
if(cmdBuf.Length % sectorSize == 0)
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectors(new byte[2048 * _skip], i, _skip);
|
2018-11-24 14:23:35 +00:00
|
|
|
else
|
2019-12-25 19:19:22 +00:00
|
|
|
_outputPlugin.WriteSectorsLong(new byte[blockSize * _skip], i, _skip);
|
2018-11-24 14:23:35 +00:00
|
|
|
}
|
2019-12-14 22:13:27 +00:00
|
|
|
}
|
2018-02-02 18:44:28 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
for(ulong b = i; b < i + _skip; b++)
|
2019-12-25 18:07:05 +00:00
|
|
|
_resume.BadBlocks.Add(b);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
DicConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf));
|
2019-12-25 19:19:22 +00:00
|
|
|
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 19:19:22 +00: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-12-14 22:13:27 +00:00
|
|
|
newTrim = true;
|
|
|
|
|
}
|
2017-06-07 22:37:05 +01:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
sectorSpeedStart += blocksToRead;
|
2019-01-27 17:47:40 +00:00
|
|
|
|
2019-12-25 19:19:22 +00:00
|
|
|
_resume.NextBlock = i + blocksToRead;
|
2019-01-27 17:47:40 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
if(elapsed < 1)
|
|
|
|
|
continue;
|
2019-01-27 17:47:40 +00:00
|
|
|
|
2019-12-14 22:13:27 +00:00
|
|
|
currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
|
|
|
|
|
sectorSpeedStart = 0;
|
|
|
|
|
timeSpeedStart = DateTime.UtcNow;
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
EndProgress?.Invoke();
|
|
|
|
|
|
2018-06-23 01:31:43 +01:00
|
|
|
// TODO: Enable when underlying images support lead-outs
|
|
|
|
|
/*
|
|
|
|
|
if(persistent)
|
|
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Reading lead-outs");
|
2018-06-23 01:31:43 +01:00
|
|
|
dumpLog.WriteLine("Reading lead-outs");
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
InitProgress?.Invoke();
|
2018-06-23 01:31:43 +01:00
|
|
|
foreach(Tuple<ulong, ulong> leadout in leadOutExtents.ToArray())
|
2019-04-19 20:13:17 +01:00
|
|
|
for(ulong i = leadout.Item1; i <= leadout.Item2; i++)
|
|
|
|
|
{
|
|
|
|
|
if(aborted)
|
2018-06-23 01:31:43 +01:00
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
|
|
|
|
dumpLog.WriteLine("Aborted!");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
double cmdDuration = 0;
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
|
|
|
|
|
if(currentSpeed > maxSpeed && currentSpeed != 0) maxSpeed = currentSpeed;
|
|
|
|
|
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
|
|
|
|
|
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-20 23:05:49 +01:00
|
|
|
PulseProgress?.Invoke(string.Format("Reading sector {0} at lead-out ({1:F3} MiB/sec.)", i, blocks,
|
2019-04-19 20:13:17 +01:00
|
|
|
currentSpeed));
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
if(readcd)
|
|
|
|
|
{
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1,
|
2019-04-19 20:13:17 +01:00
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
|
|
|
|
|
true, true, MmcErrorField.None, supportedSubchannel, dev.Timeout,
|
|
|
|
|
out cmdDuration);
|
|
|
|
|
totalDuration += cmdDuration;
|
|
|
|
|
}
|
|
|
|
|
else if(read16)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1,
|
2019-04-19 20:13:17 +01:00
|
|
|
false, dev.Timeout, out cmdDuration);
|
|
|
|
|
else if(read12)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i,
|
2019-04-19 20:13:17 +01:00
|
|
|
blockSize, 0, 1, false, dev.Timeout, out cmdDuration);
|
|
|
|
|
else if(read10)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i,
|
2019-04-19 20:13:17 +01:00
|
|
|
blockSize, 0, 1, dev.Timeout, out cmdDuration);
|
|
|
|
|
else if(read6)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, dev.Timeout,
|
2019-04-19 20:13:17 +01:00
|
|
|
out cmdDuration);
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
if(!sense && !dev.Error)
|
|
|
|
|
{
|
|
|
|
|
mhddLog.Write(i, cmdDuration);
|
|
|
|
|
ibgLog.Write(i, currentSpeed * 1024);
|
|
|
|
|
extents.Add(i, blocksToRead, true);
|
|
|
|
|
leadOutExtents.Remove(i);
|
|
|
|
|
DateTime writeStart = DateTime.Now;
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
2018-06-23 01:31:43 +01:00
|
|
|
{
|
2019-04-19 20:13:17 +01:00
|
|
|
byte[] data = new byte[SECTOR_SIZE * blocksToRead];
|
|
|
|
|
byte[] sub = new byte[subSize * blocksToRead];
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
for(int b = 0; b < blocksToRead; b++)
|
2018-06-23 01:31:43 +01:00
|
|
|
{
|
2019-12-14 19:37:52 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, SECTOR_SIZE * b,
|
2019-04-19 20:13:17 +01:00
|
|
|
SECTOR_SIZE);
|
2019-12-14 19:37:52 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(SECTOR_SIZE + b * blockSize), sub, subSize * b,
|
2019-04-19 20:13:17 +01:00
|
|
|
subSize);
|
2018-06-23 01:31:43 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
outputPlugin.WriteSectorsLong(data, i, blocksToRead);
|
|
|
|
|
outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel);
|
|
|
|
|
}
|
2019-12-14 19:37:52 +00:00
|
|
|
else outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// TODO: Reset device after X errors
|
|
|
|
|
if(stopOnError) return; // TODO: Return more cleanly
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
// Write empty data
|
|
|
|
|
DateTime writeStart = DateTime.Now;
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
outputPlugin.WriteSectorsLong(new byte[SECTOR_SIZE * skip], i, 1);
|
|
|
|
|
outputPlugin.WriteSectorsTag(new byte[subSize * skip], i, 1,
|
|
|
|
|
SectorTagType.CdSectorSubchannel);
|
2018-06-23 01:31:43 +01:00
|
|
|
}
|
2019-04-19 20:13:17 +01:00
|
|
|
else outputPlugin.WriteSectors(new byte[blockSize * skip], i, 1);
|
|
|
|
|
|
|
|
|
|
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
|
|
|
|
|
|
|
|
|
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
2018-06-23 01:31:43 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
ibgLog.Write(i, 0);
|
2018-06-23 01:31:43 +01:00
|
|
|
}
|
2019-04-19 20:13:17 +01:00
|
|
|
|
|
|
|
|
double newSpeed =
|
|
|
|
|
(double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
|
|
|
|
|
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
|
|
|
|
|
resume.NextBlock = i + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EndProgress?.Invoke();
|
2018-06-23 01:31:43 +01:00
|
|
|
}*/
|
2019-12-14 22:18:58 +00:00
|
|
|
|
|
|
|
|
end = DateTime.UtcNow;
|
|
|
|
|
mhddLog.Close();
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
|
|
|
|
|
(blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000),
|
|
|
|
|
_devicePath);
|
2019-12-14 22:18:58 +00:00
|
|
|
|
|
|
|
|
UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");
|
|
|
|
|
|
|
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");
|
|
|
|
|
|
|
|
|
|
UpdateStatus?.
|
|
|
|
|
Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
|
2019-12-14 22:18:58 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
|
|
|
|
|
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));
|
2019-12-14 22:18:58 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
|
|
|
|
|
((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);
|
2019-12-14 22:15:05 +00:00
|
|
|
|
2018-04-10 02:39:41 +01:00
|
|
|
#region Compact Disc Error trimming
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_resume.BadBlocks.Count > 0 &&
|
|
|
|
|
!_aborted &&
|
|
|
|
|
!_notrim &&
|
2019-11-18 20:59:16 +00:00
|
|
|
newTrim)
|
2018-04-10 02:39:41 +01:00
|
|
|
{
|
|
|
|
|
start = DateTime.UtcNow;
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Trimming bad sectors");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Trimming bad sectors");
|
2018-04-10 02:39:41 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
2019-04-19 20:13:17 +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-19 20:13:17 +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-12-25 22:15:06 +00:00
|
|
|
byte sectorsToTrim = 1;
|
|
|
|
|
uint badSectorToRead = (uint)badSector;
|
|
|
|
|
|
|
|
|
|
if(_fixOffset &&
|
|
|
|
|
audioExtents.Contains(badSector) &&
|
|
|
|
|
offsetBytes != 0)
|
|
|
|
|
{
|
|
|
|
|
if(offsetBytes > 0)
|
|
|
|
|
{
|
2019-12-26 01:11:22 +00:00
|
|
|
badSectorToRead -= (uint)sectorsForOffset;
|
2019-12-25 22:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:11:22 +00:00
|
|
|
sectorsToTrim = (byte)(sectorsForOffset + 1);
|
2019-12-25 22:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(readcd)
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim,
|
2019-12-25 18:07:05 +00:00
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
|
|
|
|
|
true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
|
|
|
|
out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read16)
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, badSectorToRead, blockSize,
|
|
|
|
|
0, sectorsToTrim, false, _dev.Timeout, out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read12)
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead,
|
|
|
|
|
blockSize, 0, sectorsToTrim, false, _dev.Timeout, out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read10)
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead,
|
|
|
|
|
blockSize, 0, sectorsToTrim, _dev.Timeout, out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
else if(read6)
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.Read6(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim,
|
2019-12-25 18:07:05 +00:00
|
|
|
_dev.Timeout, out cmdDuration);
|
2018-06-20 22:22:21 +01:00
|
|
|
|
|
|
|
|
totalDuration += 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-11-18 20:59:16 +00:00
|
|
|
if(!sense &&
|
2019-12-25 18:07:05 +00:00
|
|
|
!_dev.Error)
|
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 22:15:06 +00:00
|
|
|
// Because one block has been partially used to fix the offset
|
|
|
|
|
if(_fixOffset &&
|
|
|
|
|
audioExtents.Contains(badSector) &&
|
|
|
|
|
offsetBytes != 0)
|
|
|
|
|
{
|
2019-12-31 17:52:09 +00:00
|
|
|
int offsetFix = offsetBytes < 0 ? offsetFix = (int)(sectorSize - (offsetBytes * -1))
|
|
|
|
|
: offsetFix = offsetBytes;
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
// Deinterleave subchannel
|
|
|
|
|
byte[] data = new byte[sectorSize * sectorsToTrim];
|
|
|
|
|
byte[] sub = new byte[subSize * sectorsToTrim];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < sectorsToTrim; b++)
|
|
|
|
|
{
|
2019-12-26 00:32:09 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:11:22 +00:00
|
|
|
tmpBuf = new byte[sectorSize * (sectorsToTrim - sectorsForOffset)];
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
|
|
|
|
|
data = tmpBuf;
|
|
|
|
|
|
|
|
|
|
// Reinterleave subchannel
|
|
|
|
|
cmdBuf = new byte[blockSize * sectorsToTrim];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < sectorsToTrim; b++)
|
|
|
|
|
{
|
2019-12-26 00:32:09 +00:00
|
|
|
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-26 01:11:22 +00:00
|
|
|
tmpBuf = new byte[blockSize * (sectorsToTrim - sectorsForOffset)];
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
|
|
|
|
|
cmdBuf = tmpBuf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
2018-04-10 02:39:41 +01:00
|
|
|
{
|
2019-12-14 22:55:26 +00:00
|
|
|
byte[] data = new byte[sectorSize];
|
2019-11-18 20:59:16 +00:00
|
|
|
byte[] sub = new byte[subSize];
|
2019-12-14 22:55:26 +00:00
|
|
|
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
|
|
|
|
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(data, badSector);
|
|
|
|
|
_outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
|
2018-04-10 02:39:41 +01:00
|
|
|
}
|
2018-11-24 14:23:35 +00:00
|
|
|
else
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
if(supportsLongSectors)
|
2019-10-26 02:21:41 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
|
2019-10-26 02:21:41 +01:00
|
|
|
}
|
2018-11-24 14:23:35 +00:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 22:15:06 +00:00
|
|
|
if(cmdBuf.Length % sectorSize == 0)
|
2018-11-24 14:23:35 +00:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
byte[] data = new byte[2048];
|
2019-12-25 21:03:21 +00:00
|
|
|
Array.Copy(cmdBuf, 16, data, 0, 2048);
|
2018-11-24 14:23:35 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSector(data, badSector);
|
2018-11-24 14:23:35 +00:00
|
|
|
}
|
2019-10-26 02:21:41 +01:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
|
2019-10-26 02:21:41 +01:00
|
|
|
}
|
2018-11-24 14:23:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2018-04-10 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
EndProgress?.Invoke();
|
2018-04-10 02:39:41 +01:00
|
|
|
end = DateTime.UtcNow;
|
2019-12-14 22:55:26 +00:00
|
|
|
UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds.");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds);
|
2018-04-10 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
#endregion Compact Disc Error trimming
|
|
|
|
|
|
2017-05-31 01:00:58 +01:00
|
|
|
#region Compact Disc 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
|
|
|
|
2018-04-02 23:08:26 +01:00
|
|
|
Modes.ModePage? currentModePage = null;
|
2019-11-18 20:59:16 +00:00
|
|
|
byte[] md6;
|
|
|
|
|
byte[] md10;
|
2018-04-02 23:08:26 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_persistent)
|
2018-04-02 23:08:26 +01:00
|
|
|
{
|
2018-04-10 03:37:52 +01:00
|
|
|
Modes.ModePage_01_MMC pgMmc;
|
2018-06-20 22:22:21 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ModeSense6(out cmdBuf, 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 cmdBuf, 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-11-18 20:59:16 +00:00
|
|
|
Modes.DecodedMode? dcMode10 =
|
2019-12-14 19:37:52 +00:00
|
|
|
Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
|
2018-06-20 22:22:21 +01:00
|
|
|
|
2019-12-14 22:55:26 +00:00
|
|
|
if(dcMode10?.Pages != null)
|
2019-11-18 20:59:16 +00:00
|
|
|
foreach(Modes.ModePage modePage in dcMode10.Value.Pages)
|
|
|
|
|
if(modePage.Page == 0x01 &&
|
|
|
|
|
modePage.Subpage == 0x00)
|
2018-06-20 22:22:21 +01:00
|
|
|
currentModePage = modePage;
|
2018-04-10 03:37:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-14 19:37:52 +00:00
|
|
|
Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice);
|
2018-04-10 03:37:52 +01:00
|
|
|
|
2019-12-14 22:55:26 +00:00
|
|
|
if(dcMode6?.Pages != null)
|
2019-11-18 20:59:16 +00:00
|
|
|
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-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-11-24 13:47:51 +00:00
|
|
|
Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
|
2018-04-10 03:37:52 +01:00
|
|
|
};
|
|
|
|
|
}
|
2018-06-20 22:22:21 +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 02:21:41 +01:00
|
|
|
var md = new Modes.DecodedMode
|
2018-04-02 23:08:26 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
Header = new Modes.ModeHeader(), Pages = new[]
|
2018-04-02 23:08:26 +01:00
|
|
|
{
|
|
|
|
|
new Modes.ModePage
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
|
2018-04-02 23:08:26 +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);
|
2018-04-02 23:08:26 +01:00
|
|
|
|
2019-04-19 20:13:17 +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 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.");
|
|
|
|
|
|
2018-04-02 23:08:26 +01:00
|
|
|
DicConsole.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 02:21:41 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
runningPersistent = true;
|
2018-04-02 23:08:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 23:40:37 +01:00
|
|
|
InitProgress?.Invoke();
|
2017-12-19 20:33:03 +00:00
|
|
|
cdRepeatRetry:
|
2019-12-25 18:07:05 +00:00
|
|
|
ulong[] tmpArray = _resume.BadBlocks.ToArray();
|
2019-11-18 20:59:16 +00:00
|
|
|
List<ulong> sectorsNotEvenPartial = new List<ulong>();
|
|
|
|
|
|
|
|
|
|
foreach(ulong badSector in tmpArray)
|
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-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-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, " : ""));
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
byte sectorsToReRead = 1;
|
|
|
|
|
uint badSectorToReRead = (uint)badSector;
|
|
|
|
|
|
|
|
|
|
if(_fixOffset &&
|
|
|
|
|
audioExtents.Contains(badSector) &&
|
|
|
|
|
offsetBytes != 0)
|
|
|
|
|
{
|
|
|
|
|
if(offsetBytes > 0)
|
|
|
|
|
{
|
2019-12-26 01:11:22 +00:00
|
|
|
badSectorToReRead -= (uint)sectorsForOffset;
|
2019-12-25 22:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:11:22 +00:00
|
|
|
sectorsToReRead = (byte)(sectorsForOffset + 1);
|
2019-12-25 22:15:06 +00:00
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(readcd)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2019-12-25 22:15:06 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead,
|
2019-12-25 18:07:05 +00:00
|
|
|
MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders,
|
|
|
|
|
true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout,
|
|
|
|
|
out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2017-05-31 01:00:58 +01:00
|
|
|
totalDuration += cmdDuration;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(sense || _dev.Error)
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
if(!runningPersistent)
|
|
|
|
|
continue;
|
2018-04-03 22:45:55 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
FixedSense? decSense = Sense.DecodeFixed(senseBuf);
|
2018-04-03 22:45:55 +01:00
|
|
|
|
|
|
|
|
// MEDIUM ERROR, retry with ignore error below
|
2019-11-18 20:59:16 +00:00
|
|
|
if(decSense.HasValue &&
|
|
|
|
|
decSense.Value.ASC == 0x11)
|
|
|
|
|
if(!sectorsNotEvenPartial.Contains(badSector))
|
2018-04-03 22:45:55 +01:00
|
|
|
sectorsNotEvenPartial.Add(badSector);
|
|
|
|
|
}
|
2017-12-21 06:06:19 +00:00
|
|
|
|
2019-12-25 22:15:06 +00:00
|
|
|
// Because one block has been partially used to fix the offset
|
|
|
|
|
if(_fixOffset &&
|
|
|
|
|
audioExtents.Contains(badSector) &&
|
|
|
|
|
offsetBytes != 0)
|
|
|
|
|
{
|
2019-12-31 17:52:09 +00:00
|
|
|
int offsetFix = offsetBytes > 0 ? offsetFix = (int)(sectorSize - (offsetBytes * -1))
|
|
|
|
|
: offsetFix = offsetBytes;
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
// Deinterleave subchannel
|
|
|
|
|
byte[] data = new byte[sectorSize * sectorsToReRead];
|
|
|
|
|
byte[] sub = new byte[subSize * sectorsToReRead];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < sectorsToReRead; b++)
|
|
|
|
|
{
|
2019-12-26 00:32:09 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize);
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize);
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-26 01:11:22 +00:00
|
|
|
tmpBuf = new byte[sectorSize * (sectorsToReRead - sectorsForOffset)];
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length);
|
|
|
|
|
data = tmpBuf;
|
|
|
|
|
|
|
|
|
|
// Reinterleave subchannel
|
|
|
|
|
cmdBuf = new byte[blockSize * sectorsToReRead];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < sectorsToReRead; b++)
|
|
|
|
|
{
|
2019-12-26 00:32:09 +00:00
|
|
|
Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize);
|
2019-12-25 22:15:06 +00:00
|
|
|
|
|
|
|
|
Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-12-26 01:11:22 +00:00
|
|
|
tmpBuf = new byte[blockSize * (sectorsToReRead - sectorsForOffset)];
|
2019-12-25 22:15:06 +00:00
|
|
|
Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length);
|
|
|
|
|
cmdBuf = tmpBuf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2017-12-21 06:06:19 +00:00
|
|
|
extents.Add(badSector);
|
2019-04-20 01:45:28 +01:00
|
|
|
UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}.");
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass);
|
2018-04-03 22:45:55 +01:00
|
|
|
sectorsNotEvenPartial.Remove(badSector);
|
2017-12-21 06:06:19 +00:00
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
2017-12-21 06:06:19 +00:00
|
|
|
{
|
2019-12-14 22:55:26 +00:00
|
|
|
byte[] data = new byte[sectorSize];
|
2019-11-18 20:59:16 +00:00
|
|
|
byte[] sub = new byte[subSize];
|
2019-12-14 22:55:26 +00:00
|
|
|
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
|
|
|
|
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(data, badSector);
|
|
|
|
|
_outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
2019-10-26 02:21:41 +01:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
|
2019-10-26 02:21:41 +01:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(pass < _retryPasses &&
|
|
|
|
|
!_aborted &&
|
|
|
|
|
_resume.BadBlocks.Count > 0)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
|
|
|
|
pass++;
|
|
|
|
|
forward = !forward;
|
2019-12-25 18:07:05 +00:00
|
|
|
_resume.BadBlocks.Sort();
|
|
|
|
|
_resume.BadBlocks.Reverse();
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2017-05-31 01:00:58 +01:00
|
|
|
goto cdRepeatRetry;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
EndProgress?.Invoke();
|
|
|
|
|
|
2018-04-03 22:45:55 +01:00
|
|
|
// Try to ignore read errors, on some drives this allows to recover partial even if damaged data
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_persistent && sectorsNotEvenPartial.Count > 0)
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
var pgMmc = new Modes.ModePage_01_MMC
|
|
|
|
|
{
|
|
|
|
|
PS = false, ReadRetryCount = 255, Parameter = 0x01
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-26 02:21:41 +01:00
|
|
|
var md = new Modes.DecodedMode
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
Header = new Modes.ModeHeader(), Pages = new[]
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
|
|
|
|
new Modes.ModePage
|
|
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
|
2018-04-03 22:45:55 +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);
|
2018-04-03 22:45:55 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Sending MODE SELECT to drive (ignore error correction).");
|
|
|
|
|
sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _);
|
2018-04-03 22:45:55 +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-03 22:45:55 +01:00
|
|
|
{
|
|
|
|
|
runningPersistent = true;
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
InitProgress?.Invoke();
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
foreach(ulong badSector in sectorsNotEvenPartial)
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_aborted)
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
|
|
|
|
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Aborted!");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-04-03 22:45:55 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-20 23:05:49 +01:00
|
|
|
PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}");
|
2018-04-03 22:45:55 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(readcd)
|
2018-04-03 22:45:55 +01:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1,
|
|
|
|
|
MmcSectorTypes.AllTypes, false, false, true,
|
|
|
|
|
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
|
|
|
|
supportedSubchannel, _dev.Timeout, out cmdDuration);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-04-03 22:45:55 +01:00
|
|
|
totalDuration += cmdDuration;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(sense || _dev.Error)
|
2019-11-18 20:59:16 +00:00
|
|
|
continue;
|
2018-04-03 22:45:55 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass);
|
2018-06-20 22:22:21 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
2018-06-20 22:22:21 +01:00
|
|
|
{
|
2019-12-14 22:55:26 +00:00
|
|
|
byte[] data = new byte[sectorSize];
|
2019-11-18 20:59:16 +00:00
|
|
|
byte[] sub = new byte[subSize];
|
2019-12-14 22:55:26 +00:00
|
|
|
Array.Copy(cmdBuf, 0, data, 0, sectorSize);
|
|
|
|
|
Array.Copy(cmdBuf, sectorSize, sub, 0, subSize);
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(data, badSector);
|
|
|
|
|
_outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel);
|
2018-04-03 22:45:55 +01:00
|
|
|
}
|
2019-10-26 02:21:41 +01:00
|
|
|
else
|
|
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.WriteSectorLong(cmdBuf, badSector);
|
2019-10-26 02:21:41 +01:00
|
|
|
}
|
2018-04-03 22:45:55 +01:00
|
|
|
}
|
2019-04-19 20:13:17 +01:00
|
|
|
|
|
|
|
|
EndProgress?.Invoke();
|
2018-04-03 22:45:55 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
if(runningPersistent && currentModePage.HasValue)
|
2017-05-31 01:00:58 +01:00
|
|
|
{
|
2018-06-23 01:31:43 +01:00
|
|
|
// TODO: Enable when underlying images support lead-outs
|
|
|
|
|
/*
|
|
|
|
|
dumpLog.WriteLine("Retrying lead-outs");
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
InitProgress?.Invoke();
|
2018-06-23 01:31:43 +01:00
|
|
|
foreach(Tuple<ulong, ulong> leadout in leadOutExtents.ToArray())
|
|
|
|
|
for(ulong i = leadout.Item1; i <= leadout.Item2; i++)
|
|
|
|
|
{
|
|
|
|
|
if(aborted)
|
|
|
|
|
{
|
|
|
|
|
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
|
|
|
|
dumpLog.WriteLine("Aborted!");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double cmdDuration = 0;
|
|
|
|
|
|
|
|
|
|
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
|
|
|
|
|
if(currentSpeed > maxSpeed && currentSpeed != 0) maxSpeed = currentSpeed;
|
|
|
|
|
if(currentSpeed < minSpeed && currentSpeed != 0) minSpeed = currentSpeed;
|
|
|
|
|
#pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator
|
|
|
|
|
|
2019-04-20 23:05:49 +01:00
|
|
|
PulseProgress?.Invoke(string.Format("Reading sector {0} at lead-out ({1:F3} MiB/sec.)", i,
|
2019-04-19 20:13:17 +01:00
|
|
|
blocks, currentSpeed));
|
2018-06-23 01:31:43 +01:00
|
|
|
|
|
|
|
|
if(readcd)
|
|
|
|
|
{
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1,
|
2018-06-23 01:31:43 +01:00
|
|
|
MmcSectorTypes.AllTypes, false, false, true,
|
|
|
|
|
MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None,
|
|
|
|
|
supportedSubchannel, dev.Timeout, out cmdDuration);
|
|
|
|
|
totalDuration += cmdDuration;
|
|
|
|
|
}
|
|
|
|
|
else if(read16)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0,
|
2018-06-23 01:31:43 +01:00
|
|
|
1, false, dev.Timeout, out cmdDuration);
|
|
|
|
|
else if(read12)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i,
|
2018-06-23 01:31:43 +01:00
|
|
|
blockSize, 0, 1, false, dev.Timeout, out cmdDuration);
|
|
|
|
|
else if(read10)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i,
|
2018-06-23 01:31:43 +01:00
|
|
|
blockSize, 0, 1, dev.Timeout, out cmdDuration);
|
|
|
|
|
else if(read6)
|
2019-12-14 19:37:52 +00:00
|
|
|
sense = dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, dev.Timeout,
|
2018-06-23 01:31:43 +01:00
|
|
|
out cmdDuration);
|
|
|
|
|
|
|
|
|
|
if(!sense && !dev.Error)
|
|
|
|
|
{
|
|
|
|
|
mhddLog.Write(i, cmdDuration);
|
|
|
|
|
ibgLog.Write(i, currentSpeed * 1024);
|
|
|
|
|
extents.Add(i, blocksToRead, true);
|
|
|
|
|
leadOutExtents.Remove(i);
|
|
|
|
|
DateTime writeStart = DateTime.Now;
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
byte[] data = new byte[SECTOR_SIZE * blocksToRead];
|
|
|
|
|
byte[] sub = new byte[subSize * blocksToRead];
|
|
|
|
|
|
|
|
|
|
for(int b = 0; b < blocksToRead; b++)
|
|
|
|
|
{
|
2019-12-14 19:37:52 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, SECTOR_SIZE * b,
|
2018-06-23 01:31:43 +01:00
|
|
|
SECTOR_SIZE);
|
2019-12-14 19:37:52 +00:00
|
|
|
Array.Copy(cmdBuf, (int)(SECTOR_SIZE + b * blockSize), sub, subSize * b,
|
2018-06-23 01:31:43 +01:00
|
|
|
subSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outputPlugin.WriteSectorsLong(data, i, blocksToRead);
|
|
|
|
|
outputPlugin.WriteSectorsTag(sub, i, blocksToRead,
|
|
|
|
|
SectorTagType.CdSectorSubchannel);
|
|
|
|
|
}
|
2019-12-14 19:37:52 +00:00
|
|
|
else outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
|
2018-06-23 01:31:43 +01:00
|
|
|
|
|
|
|
|
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// TODO: Reset device after X errors
|
|
|
|
|
if(stopOnError) return; // TODO: Return more cleanly
|
|
|
|
|
|
|
|
|
|
// Write empty data
|
|
|
|
|
DateTime writeStart = DateTime.Now;
|
|
|
|
|
if(supportedSubchannel != MmcSubchannel.None)
|
|
|
|
|
{
|
|
|
|
|
outputPlugin.WriteSectorsLong(new byte[SECTOR_SIZE * skip], i, 1);
|
|
|
|
|
outputPlugin.WriteSectorsTag(new byte[subSize * skip], i, 1,
|
|
|
|
|
SectorTagType.CdSectorSubchannel);
|
|
|
|
|
}
|
|
|
|
|
else outputPlugin.WriteSectors(new byte[blockSize * skip], i, 1);
|
|
|
|
|
|
|
|
|
|
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
|
|
|
|
|
|
|
|
|
|
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
|
|
|
|
|
|
|
|
|
|
ibgLog.Write(i, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double newSpeed =
|
|
|
|
|
(double)blockSize * blocksToRead / 1048576 / (cmdDuration / 1000);
|
|
|
|
|
if(!double.IsInfinity(newSpeed)) currentSpeed = newSpeed;
|
|
|
|
|
}
|
2019-04-19 20:13:17 +01:00
|
|
|
|
|
|
|
|
EndProgress?.Invoke();
|
2018-06-23 01:31:43 +01:00
|
|
|
*/
|
|
|
|
|
|
2019-10-26 02:21:41 +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-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status).");
|
|
|
|
|
sense = _dev.ModeSelect(md6, out senseBuf, 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 senseBuf, true, false, _dev.Timeout, out _);
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
EndProgress?.Invoke();
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
|
|
|
|
#endregion Compact Disc Error handling
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
// Write media tags to image
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_aborted)
|
2019-11-18 20:59:16 +00:00
|
|
|
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
|
2017-06-07 22:37:05 +01:00
|
|
|
{
|
2019-11-18 20:59:16 +00:00
|
|
|
if(tag.Value is null)
|
2019-08-10 14:35:14 +01:00
|
|
|
{
|
|
|
|
|
DicConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-08-10 14:35:14 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
ret = _outputPlugin.WriteMediaTag(tag.Value, tag.Key);
|
2017-06-07 22:37:05 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(ret || _force)
|
2019-11-18 20:59:16 +00:00
|
|
|
continue;
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
// Cannot write tag to image
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine($"Cannot write tag {tag.Key}.");
|
|
|
|
|
StoppingErrorMessage?.Invoke(_outputPlugin.ErrorMessage);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-04-20 14:02:25 +01:00
|
|
|
return;
|
2018-01-20 17:12:01 +00:00
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_resume.BadBlocks.Sort();
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
foreach(ulong bad in _resume.BadBlocks)
|
|
|
|
|
_dumpLog.WriteLine("Sector {0} could not be read.", bad);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-02-01 01:20:41 +00:00
|
|
|
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
|
|
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.SetDumpHardware(_resume.Tries);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_preSidecar != null)
|
|
|
|
|
_outputPlugin.SetCicmMetadata(_preSidecar);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Closing output file.");
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Closing output file.");
|
2019-11-18 20:59:16 +00:00
|
|
|
DateTime closeStart = DateTime.Now;
|
2019-12-25 18:07:05 +00:00
|
|
|
_outputPlugin.Close();
|
2019-11-18 20:59:16 +00:00
|
|
|
DateTime closeEnd = DateTime.Now;
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_aborted)
|
2018-01-20 17:12:01 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Aborted!");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2018-02-02 22:03:19 +00:00
|
|
|
double totalChkDuration = 0;
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
if(!_nometadata)
|
2018-02-02 22:03:19 +00:00
|
|
|
{
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Creating sidecar.");
|
2019-11-18 20:59:16 +00:00
|
|
|
var filters = new FiltersList();
|
2019-12-25 18:07:05 +00:00
|
|
|
IFilter filter = filters.GetFilter(_outputPath);
|
2019-11-18 20:59:16 +00:00
|
|
|
IMediaImage inputPlugin = ImageFormat.Detect(filter);
|
|
|
|
|
|
|
|
|
|
if(!inputPlugin.Open(filter))
|
2019-04-20 14:02:25 +01:00
|
|
|
{
|
|
|
|
|
StoppingErrorMessage?.Invoke("Could not open created image.");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-04-20 14:02:25 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2018-02-02 22:03:19 +00:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
DateTime chkStart = DateTime.UtcNow;
|
2019-12-14 22:55:26 +00:00
|
|
|
|
|
|
|
|
// ReSharper disable once UseObjectOrCollectionInitializer
|
2019-12-25 18:07:05 +00:00
|
|
|
_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();
|
2018-04-02 23:08:26 +01:00
|
|
|
end = DateTime.UtcNow;
|
2018-02-02 22:03:19 +00:00
|
|
|
|
2018-04-02 23:08:26 +01:00
|
|
|
totalChkDuration = (end - chkStart).TotalMilliseconds;
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds);
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
|
|
|
|
|
((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000));
|
2018-02-02 22:03:19 +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.OpticalDisc = sidecar.OpticalDisc;
|
|
|
|
|
sidecar = _preSidecar;
|
2018-02-02 22:03:19 +00:00
|
|
|
}
|
2017-06-07 22:37:05 +01:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
List<(ulong start, string type)> filesystems = new List<(ulong start, string type)>();
|
|
|
|
|
|
|
|
|
|
if(sidecar.OpticalDisc[0].Track != null)
|
2018-02-02 22:03:19 +00:00
|
|
|
filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track
|
2019-11-18 20:59:16 +00:00
|
|
|
where xmlTrack.FileSystemInformation != null
|
|
|
|
|
from partition in xmlTrack.FileSystemInformation
|
|
|
|
|
where partition.FileSystems != null from fileSystem in partition.FileSystems
|
|
|
|
|
select (partition.StartSector, fileSystem.Type));
|
|
|
|
|
|
|
|
|
|
if(filesystems.Count > 0)
|
|
|
|
|
foreach(var filesystem in filesystems.Select(o => new
|
|
|
|
|
{
|
|
|
|
|
o.start, o.type
|
|
|
|
|
}).Distinct())
|
2019-12-25 18:07:05 +00:00
|
|
|
_dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start);
|
2018-02-02 22:03:19 +00:00
|
|
|
|
|
|
|
|
sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType);
|
2019-11-18 20:59:16 +00:00
|
|
|
(string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType);
|
|
|
|
|
sidecar.OpticalDisc[0].DiscType = discType.type;
|
|
|
|
|
sidecar.OpticalDisc[0].DiscSubType = discType.subType;
|
2019-12-25 18:07:05 +00:00
|
|
|
sidecar.OpticalDisc[0].DumpHardwareArray = _resume.Tries.ToArray();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
2019-11-18 20:59:16 +00:00
|
|
|
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
|
2019-12-25 18:07:05 +00:00
|
|
|
if(_outputPlugin.SupportedMediaTags.Contains(tag.Key))
|
|
|
|
|
AddMediaTagToSidecar(_outputPath, tag, ref sidecar);
|
2017-05-31 01:00:58 +01:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("Writing metadata sidecar");
|
2018-02-02 22:03:19 +00:00
|
|
|
|
2019-12-25 18:07:05 +00:00
|
|
|
var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);
|
2018-01-28 21:18:52 +00:00
|
|
|
|
2019-10-26 02:21:41 +01:00
|
|
|
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
|
2018-02-02 22:03:19 +00:00
|
|
|
xmlSer.Serialize(xmlFs, sidecar);
|
|
|
|
|
xmlFs.Close();
|
|
|
|
|
}
|
2018-01-20 17:12:01 +00:00
|
|
|
|
2019-12-14 22:02:36 +00:00
|
|
|
end = DateTime.UtcNow;
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
UpdateStatus?.
|
2019-12-14 22:55:26 +00:00
|
|
|
Invoke($"Took a total of {(end - dumpStart).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
|
|
|
|
UpdateStatus?.
|
2019-12-14 18:17:17 +00:00
|
|
|
Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");
|
2019-11-18 20:59:16 +00:00
|
|
|
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
|
|
|
|
|
UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
|
2019-12-25 18:07:05 +00:00
|
|
|
UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");
|
2019-04-19 20:13:17 +01:00
|
|
|
UpdateStatus?.Invoke("");
|
2017-06-20 05:48:09 +01:00
|
|
|
|
2018-01-20 17:12:01 +00:00
|
|
|
Statistics.AddMedia(dskType, true);
|
2017-05-31 01:00:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
}
|