Files
Aaru/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs

1855 lines
79 KiB
C#
Raw Normal View History

// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Filename : Dump.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
2020-03-11 21:56:55 +00:00
// Component : CompactDisc dumping.
//
// --[ Description ] ----------------------------------------------------------
//
2020-03-11 21:56:55 +00:00
// Dumps CompactDiscs.
//
// --[ 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/>.
//
// ----------------------------------------------------------------------------
2024-12-19 10:45:18 +00:00
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
2022-03-07 07:36:44 +00:00
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
2020-02-27 00:33:26 +00:00
using Aaru.CommonTypes;
using Aaru.CommonTypes.AaruMetadata;
2020-02-27 00:33:26 +00:00
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Extents;
using Aaru.CommonTypes.Interfaces;
using Aaru.Core.Graphics;
2020-02-29 18:03:35 +00:00
using Aaru.Core.Logging;
using Aaru.Core.Media.Detection;
2020-02-27 00:33:26 +00:00
using Aaru.Database.Models;
using Aaru.Decoders.CD;
using Aaru.Devices;
using Aaru.Logging;
2023-09-26 02:40:11 +01:00
using Humanizer;
using Humanizer.Bytes;
2023-09-26 03:39:10 +01:00
using Humanizer.Localisation;
using Track = Aaru.CommonTypes.Structs.Track;
2020-02-27 00:33:26 +00:00
using TrackType = Aaru.CommonTypes.Enums.TrackType;
using Version = Aaru.CommonTypes.Interop.Version;
2019-12-14 18:17:17 +00:00
namespace Aaru.Core.Devices.Dumping;
2022-03-06 13:29:38 +00:00
/// <summary>Implement dumping Compact Discs</summary>
2022-03-06 13:29:38 +00:00
// TODO: Barcode
sealed partial class Dump
{
/// <summary>Dumps a compact disc</summary>
void CompactDisc()
{
2022-03-06 13:29:38 +00:00
ExtentsULong audioExtents; // Extents with audio sectors
ulong blocks; // Total number of positive sectors
uint blockSize; // Size of the read sector in bytes
CdOffset cdOffset; // Read offset from database
byte[] cmdBuf; // Data buffer
2024-05-01 23:52:03 +01:00
DumpHardware currentTry = null; // Current dump hardware try
2022-03-06 13:29:38 +00:00
double currentSpeed = 0; // Current read speed
int? discOffset = null; // Disc write offset
2024-05-01 23:52:03 +01:00
ExtentsULong extents = null; // Extents
2022-03-06 13:29:38 +00:00
bool hiddenData; // Hidden track is data
IbgLog ibgLog; // IMGBurn log
double imageWriteDuration = 0; // Duration of image write
long lastSector; // Last sector number
var leadOutExtents = new ExtentsULong(); // Lead-out extents
Dictionary<int, long> leadOutStarts = new(); // Lead-out starts
double maxSpeed = double.MinValue; // Maximum speed
MhddLog mhddLog; // MHDD log
double minSpeed = double.MaxValue; // Minimum speed
bool newTrim; // Is trim a new one?
int offsetBytes = 0; // Read offset
bool read6 = false; // Device supports READ(6)
bool read10 = false; // Device supports READ(10)
bool read12 = false; // Device supports READ(12)
bool read16 = false; // Device supports READ(16)
bool readcd = true; // Device supports READ CD
2022-03-06 13:29:38 +00:00
bool ret; // Image writing return status
const uint sectorSize = 2352; // Full sector size
int sectorsForOffset = 0; // Sectors needed to fix offset
bool sense = true; // Sense indicator
2022-03-06 13:29:38 +00:00
int sessions; // Number of sessions in disc
2024-05-01 23:52:03 +01:00
SubchannelLog subLog = null; // Subchannel log
uint subSize = 0; // Subchannel size in bytes
2022-03-06 13:29:38 +00:00
TrackSubchannelType subType; // Track subchannel type
bool supportsLongSectors = true; // Supports reading EDC and ECC
2022-03-06 13:29:38 +00:00
bool supportsPqSubchannel; // Supports reading PQ subchannel
bool supportsRwSubchannel; // Supports reading RW subchannel
byte[] tmpBuf; // Temporary buffer
FullTOC.CDFullTOC? toc; // Full CD TOC
double totalDuration = 0; // Total commands duration
Dictionary<byte, byte> trackFlags = new(); // Track flags
Track[] tracks; // Tracks in disc
int firstTrackLastSession; // Number of first track in last session
bool hiddenTrack; // Disc has a hidden track before track 1
MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel
MmcSubchannel desiredSubchannel; // User requested subchannel
bool bcdSubchannel = false; // Subchannel positioning is in BCD
2022-03-06 13:29:38 +00:00
Dictionary<byte, string> isrcs = new();
string mcn = null;
2024-05-01 04:39:38 +01:00
HashSet<int> subchannelExtents = [];
bool cdiReadyReadAsAudio = false;
2022-03-06 13:29:38 +00:00
uint firstLba;
var outputOptical = _outputPlugin as IWritableOpticalImage;
Dictionary<MediaTagType, byte[]> mediaTags = new(); // Media tags
Dictionary<byte, int> smallestPregapLbaPerTrack = new();
MediaType dskType = MediaType.CD;
if(_dumpRaw)
2019-12-14 14:33:23 +00:00
{
_dumpLog.WriteLine(Localization.Core.Raw_CD_dumping_not_yet_implemented);
StoppingErrorMessage?.Invoke(Localization.Core.Raw_CD_dumping_not_yet_implemented);
2022-03-06 13:29:38 +00:00
return;
}
2019-12-14 19:37:52 +00:00
2024-05-01 04:05:22 +01:00
tracks = GetCdTracks(_dev,
_dumpLog,
_force,
out lastSector,
leadOutStarts,
mediaTags,
StoppingErrorMessage,
out toc,
trackFlags,
UpdateStatus);
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
if(tracks is null)
{
_dumpLog.WriteLine(Localization.Core.Could_not_get_tracks);
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_get_tracks);
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
return;
}
firstLba = (uint)tracks.Min(t => t.StartSector);
2022-03-06 13:29:38 +00:00
// Check subchannels support
supportsPqSubchannel = SupportsPqSubchannel(_dev, _dumpLog, UpdateStatus, firstLba) ||
SupportsPqSubchannel(_dev, _dumpLog, UpdateStatus, firstLba + 5);
supportsRwSubchannel = SupportsRwSubchannel(_dev, _dumpLog, UpdateStatus, firstLba) ||
SupportsRwSubchannel(_dev, _dumpLog, UpdateStatus, firstLba + 5);
2022-03-06 13:29:38 +00:00
if(supportsRwSubchannel)
supportedSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
supportedSubchannel = MmcSubchannel.Q16;
else
supportedSubchannel = MmcSubchannel.None;
2022-03-06 13:29:38 +00:00
switch(_subchannel)
{
case DumpSubchannel.Any:
if(supportsRwSubchannel)
desiredSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
desiredSubchannel = MmcSubchannel.Q16;
else
desiredSubchannel = MmcSubchannel.None;
2022-03-06 13:29:38 +00:00
break;
case DumpSubchannel.Rw:
if(supportsRwSubchannel)
desiredSubchannel = MmcSubchannel.Raw;
else
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Drive_does_not_support_the_requested_subchannel_format_not_continuing);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Drive_does_not_support_the_requested_subchannel_format_not_continuing);
2022-03-06 13:29:38 +00:00
return;
2019-12-14 19:37:52 +00:00
}
2022-03-06 13:29:38 +00:00
break;
case DumpSubchannel.RwOrPq:
if(supportsRwSubchannel)
desiredSubchannel = MmcSubchannel.Raw;
else if(supportsPqSubchannel)
desiredSubchannel = MmcSubchannel.Q16;
2019-12-14 19:37:52 +00:00
else
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Drive_does_not_support_the_requested_subchannel_format_not_continuing);
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Drive_does_not_support_the_requested_subchannel_format_not_continuing);
return;
}
2022-03-06 13:29:38 +00:00
break;
case DumpSubchannel.Pq:
if(supportsPqSubchannel)
desiredSubchannel = MmcSubchannel.Q16;
else
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Drive_does_not_support_the_requested_subchannel_format_not_continuing);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Drive_does_not_support_the_requested_subchannel_format_not_continuing);
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
break;
case DumpSubchannel.None:
desiredSubchannel = MmcSubchannel.None;
2019-12-14 19:37:52 +00:00
2022-03-06 13:29:38 +00:00
break;
2023-10-03 22:57:50 +01:00
default:
throw new ArgumentOutOfRangeException();
2022-03-06 13:29:38 +00:00
}
2019-12-14 19:37:52 +00:00
2024-05-01 04:05:22 +01:00
if(desiredSubchannel == MmcSubchannel.Q16 && supportsPqSubchannel) supportedSubchannel = MmcSubchannel.Q16;
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
// Check if output format supports subchannels
if(!outputOptical.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) &&
desiredSubchannel != MmcSubchannel.None)
{
if(_force || _subchannel == DumpSubchannel.None)
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_subchannels_continuing);
UpdateStatus?.Invoke(Localization.Core.Output_format_does_not_support_subchannels_continuing);
2022-03-06 13:29:38 +00:00
}
else
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_subchannels_not_continuing);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_subchannels_not_continuing);
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
desiredSubchannel = MmcSubchannel.None;
}
2022-03-06 13:29:38 +00:00
switch(supportedSubchannel)
{
case MmcSubchannel.None:
_dumpLog.WriteLine(Localization.Core.Checking_if_drive_supports_reading_without_subchannel);
UpdateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_reading_without_subchannel);
2019-12-14 19:51:52 +00:00
2024-05-01 04:05:22 +01:00
readcd = !_dev.ReadCd(out cmdBuf,
out _,
firstLba,
sectorSize,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
supportedSubchannel,
_dev.Timeout,
out _) ||
!_dev.ReadCd(out cmdBuf,
out _,
firstLba + 5,
sectorSize,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
supportedSubchannel,
_dev.Timeout,
out _);
2019-12-14 19:51:52 +00:00
2022-03-06 13:29:38 +00:00
if(!readcd)
{
_dumpLog.WriteLine(Localization.Core.Drive_does_not_support_READ_CD_trying_SCSI_READ_commands);
ErrorMessage?.Invoke(Localization.Core.Drive_does_not_support_READ_CD_trying_SCSI_READ_commands);
2019-12-14 19:51:52 +00:00
_dumpLog.WriteLine(Localization.Core.Checking_if_drive_supports_READ_6);
UpdateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_READ_6);
2024-12-19 10:45:18 +00:00
read6 = !_dev.Read6(out cmdBuf, out _, firstLba, 2048, 1, _dev.Timeout, out _) ||
!_dev.Read6(out cmdBuf, out _, firstLba + 5, 2048, 1, _dev.Timeout, out _);
_dumpLog.WriteLine(Localization.Core.Checking_if_drive_supports_READ_10);
UpdateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_READ_10);
2019-12-14 19:51:52 +00:00
read10 =
!_dev.Read10(out cmdBuf,
out _,
0,
false,
true,
false,
false,
firstLba,
2048,
0,
1,
_dev.Timeout,
out _) ||
!_dev.Read10(out cmdBuf,
out _,
0,
false,
true,
false,
false,
firstLba + 5,
2048,
0,
1,
_dev.Timeout,
out _);
_dumpLog.WriteLine(Localization.Core.Checking_if_drive_supports_READ_12);
UpdateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_READ_12);
read12 =
!_dev.Read12(out cmdBuf,
out _,
0,
false,
true,
false,
false,
firstLba,
2048,
0,
1,
false,
_dev.Timeout,
out _) ||
!_dev.Read12(out cmdBuf,
out _,
0,
false,
true,
false,
false,
firstLba + 5,
2048,
0,
1,
false,
_dev.Timeout,
out _);
_dumpLog.WriteLine(Localization.Core.Checking_if_drive_supports_READ_16);
UpdateStatus?.Invoke(Localization.Core.Checking_if_drive_supports_READ_16);
read16 =
!_dev.Read16(out cmdBuf,
out _,
0,
false,
true,
false,
firstLba,
2048,
0,
1,
false,
_dev.Timeout,
out _) ||
!_dev.Read16(out cmdBuf,
out _,
0,
false,
true,
false,
firstLba + 5,
2048,
0,
1,
false,
_dev.Timeout,
out _);
2022-11-13 19:38:03 +00:00
switch(read6)
2022-03-06 13:29:38 +00:00
{
2022-11-13 19:38:03 +00:00
case false when !read10 && !read12 && !read16:
_dumpLog.WriteLine(Localization.Core.Cannot_read_from_disc_not_continuing);
StoppingErrorMessage?.Invoke(Localization.Core.Cannot_read_from_disc_not_continuing);
2022-11-13 19:38:03 +00:00
return;
case true:
_dumpLog.WriteLine(Localization.Core.Drive_supports_READ_6);
UpdateStatus?.Invoke(Localization.Core.Drive_supports_READ_6);
2022-11-13 19:38:03 +00:00
break;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(read10)
{
_dumpLog.WriteLine(Localization.Core.Drive_supports_READ_10);
UpdateStatus?.Invoke(Localization.Core.Drive_supports_READ_10);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(read12)
{
_dumpLog.WriteLine(Localization.Core.Drive_supports_READ_12);
UpdateStatus?.Invoke(Localization.Core.Drive_supports_READ_12);
}
2022-03-06 13:29:38 +00:00
if(read16)
{
_dumpLog.WriteLine(Localization.Core.Drive_supports_READ_16);
UpdateStatus?.Invoke(Localization.Core.Drive_supports_READ_16);
}
}
_dumpLog.WriteLine(Localization.Core.Drive_can_read_without_subchannel);
UpdateStatus?.Invoke(Localization.Core.Drive_can_read_without_subchannel);
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
subSize = 0;
2022-03-06 13:29:38 +00:00
break;
case MmcSubchannel.Raw:
_dumpLog.WriteLine(Localization.Core.Full_raw_subchannel_reading_supported);
UpdateStatus?.Invoke(Localization.Core.Full_raw_subchannel_reading_supported);
2022-03-06 13:29:38 +00:00
subSize = 96;
2022-03-06 13:29:38 +00:00
break;
case MmcSubchannel.Q16:
_dumpLog.WriteLine(Localization.Core.PQ_subchannel_reading_supported);
_dumpLog.WriteLine(Localization.Core.WARNING_If_disc_says_CDG_CDEG_CDMIDI_dump_will_be_incorrect);
UpdateStatus?.Invoke(Localization.Core.PQ_subchannel_reading_supported);
UpdateStatus?.Invoke(Localization.Core.WARNING_If_disc_says_CDG_CDEG_CDMIDI_dump_will_be_incorrect);
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
subSize = 16;
2022-03-06 13:29:38 +00:00
break;
}
subType = desiredSubchannel switch
{
MmcSubchannel.None => TrackSubchannelType.None,
2024-05-01 23:52:03 +01:00
MmcSubchannel.Raw or MmcSubchannel.Q16 => TrackSubchannelType.Raw,
_ => throw new ArgumentOutOfRangeException()
};
2022-03-06 13:29:38 +00:00
blockSize = sectorSize + subSize;
2022-03-06 13:29:38 +00:00
// Check if subchannel is BCD
if(supportedSubchannel != MmcSubchannel.None)
{
2024-05-01 04:05:22 +01:00
sense = _dev.ReadCd(out cmdBuf,
out _,
(firstLba / 75 + 1) * 75 + 35,
blockSize,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
supportedSubchannel,
_dev.Timeout,
out _);
2022-03-06 13:29:38 +00:00
if(!sense)
{
2022-03-06 13:29:38 +00:00
tmpBuf = new byte[subSize];
Array.Copy(cmdBuf, sectorSize, tmpBuf, 0, subSize);
2024-05-01 04:05:22 +01:00
if(supportedSubchannel == MmcSubchannel.Q16) tmpBuf = Subchannel.ConvertQToRaw(tmpBuf);
2022-03-06 13:29:38 +00:00
tmpBuf = Subchannel.Deinterleave(tmpBuf);
2022-03-06 13:29:38 +00:00
// 9th Q subchannel is always FRAME when in user data area
// LBA 35 => MSF 00:02:35 => FRAME 35 (in hexadecimal 0x23)
// Sometimes drive returns a pregap here but MSF 00:02:3x => FRAME 3x (hexadecimal 0x20 to 0x27)
bcdSubchannel = (tmpBuf[21] & 0x30) > 0;
2022-03-06 13:29:38 +00:00
if(bcdSubchannel)
{
_dumpLog.WriteLine(Localization.Core.Drive_returns_subchannel_in_BCD);
UpdateStatus?.Invoke(Localization.Core.Drive_returns_subchannel_in_BCD);
2022-03-06 13:29:38 +00:00
}
else
{
_dumpLog.WriteLine(Localization.Core.Drive_does_not_returns_subchannel_in_BCD);
UpdateStatus?.Invoke(Localization.Core.Drive_does_not_returns_subchannel_in_BCD);
2022-03-06 13:29:38 +00:00
}
}
2022-03-06 13:29:38 +00:00
}
2024-05-01 04:05:22 +01:00
foreach(Track trk in tracks) trk.SubchannelType = subType;
2019-12-14 20:39:15 +00:00
_dumpLog.WriteLine(Localization.Core.Calculating_pregaps__can_take_some_time);
UpdateStatus?.Invoke(Localization.Core.Calculating_pregaps__can_take_some_time);
2019-12-14 20:39:15 +00:00
2024-05-01 04:05:22 +01:00
SolveTrackPregaps(_dev,
_dumpLog,
UpdateStatus,
tracks,
supportsPqSubchannel,
supportsRwSubchannel,
_dbDev,
out bool inexactPositioning,
true);
2019-12-14 20:39:15 +00:00
2022-03-06 13:29:38 +00:00
if(inexactPositioning)
{
_dumpLog.WriteLine(Localization.Core.The_drive_has_returned_incorrect_Q_positioning_calculating_pregaps);
2019-12-14 20:39:15 +00:00
UpdateStatus?.Invoke(Localization.Core.The_drive_has_returned_incorrect_Q_positioning_calculating_pregaps);
2022-03-06 13:29:38 +00:00
}
2019-12-14 21:27:15 +00:00
2022-03-07 07:36:44 +00:00
if(!outputOptical.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreRawData))
2022-03-06 13:29:38 +00:00
{
if(!_force)
2019-12-14 21:27:15 +00:00
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_storing_raw_data_not_continuing);
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_storing_raw_data_not_continuing);
2019-12-14 21:27:15 +00:00
return;
}
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_storing_raw_data_continuing);
2022-03-06 13:29:38 +00:00
ErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_storing_raw_data_continuing);
2022-03-06 13:29:38 +00:00
}
2022-03-07 07:36:44 +00:00
if(!outputOptical.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreAudioTracks) &&
2022-03-06 13:29:38 +00:00
tracks.Any(track => track.Type == TrackType.Audio))
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_audio_tracks_cannot_continue);
2022-03-06 13:29:38 +00:00
StoppingErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_audio_tracks_cannot_continue);
2022-03-06 13:29:38 +00:00
return;
}
2019-12-14 20:56:39 +00:00
2022-03-07 07:36:44 +00:00
if(!outputOptical.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStorePregaps) &&
2024-05-01 04:05:22 +01:00
tracks.Where(track => track.Sequence != tracks.First(t => t.Session == track.Session).Sequence)
.Any(track => track.Pregap > 0))
2022-03-06 13:29:38 +00:00
{
if(!_force)
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_pregaps_not_continuing);
StoppingErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_pregaps_not_continuing);
return;
2022-03-06 13:29:38 +00:00
}
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_pregaps_continuing);
ErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_pregaps_continuing);
2022-03-06 13:29:38 +00:00
}
for(int t = 1; t < tracks.Length; t++) tracks[t - 1].EndSector = tracks[t].StartSector - 1;
tracks[^1].EndSector = (ulong)lastSector;
blocks = (ulong)(lastSector + 1);
2022-03-06 13:29:38 +00:00
if(blocks == 0)
{
StoppingErrorMessage?.Invoke(Localization.Core.Cannot_dump_blank_media);
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2019-12-14 20:56:39 +00:00
2024-05-01 04:05:22 +01:00
ResumeSupport.Process(true,
true,
blocks,
_dev.Manufacturer,
_dev.Model,
_dev.Serial,
_dev.PlatformId,
ref _resume,
ref currentTry,
ref extents,
_dev.FirmwareRevision,
_private,
_force);
2019-12-14 20:56:39 +00:00
if(currentTry == null || extents == null)
2022-03-06 13:29:38 +00:00
{
StoppingErrorMessage?.Invoke(Localization.Core.Could_not_process_resume_file_not_continuing);
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2019-12-14 20:56:39 +00:00
2022-03-06 13:29:38 +00:00
// Read media tags
ReadCdTags(ref dskType, mediaTags, out sessions, out firstTrackLastSession);
if(!outputOptical.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreSessions) && sessions > 1)
2022-03-06 13:29:38 +00:00
{
// TODO: Disabled until 6.0
/*if(!_force)
{*/
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_sessions);
StoppingErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_sessions);
2019-12-14 20:05:30 +00:00
2022-03-06 13:29:38 +00:00
return;
/*}
2019-12-14 20:05:30 +00:00
2022-03-06 13:29:38 +00:00
_dumpLog.WriteLine("Output format does not support sessions, this will end in a loss of data, continuing...");
2019-12-14 21:09:16 +00:00
2022-03-06 13:29:38 +00:00
ErrorMessage?.
Invoke("Output format does not support sessions, this will end in a loss of data, continuing...");*/
}
2019-12-14 20:05:30 +00:00
2022-03-06 13:29:38 +00:00
// Check if output format supports all disc tags we have retrieved so far
foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputOptical.SupportedMediaTags.Contains(tag)))
2023-10-03 22:57:50 +01:00
{
2022-03-06 13:29:38 +00:00
if(_force)
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_0_continuing, tag);
ErrorMessage?.Invoke(string.Format(Localization.Core.Output_format_does_not_support_0_continuing, tag));
2022-03-06 13:29:38 +00:00
}
else
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_0_not_continuing, tag);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(string.Format(Localization.Core
.Output_format_does_not_support_0_not_continuing,
tag));
2022-03-06 13:29:38 +00:00
return;
}
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
if(leadOutStarts.Any())
{
UpdateStatus?.Invoke(Localization.Core.Solving_lead_outs);
2022-03-06 13:29:38 +00:00
foreach(KeyValuePair<int, long> leadOuts in leadOutStarts)
2024-05-01 04:05:22 +01:00
{
foreach(Track trk in tracks.Where(trk => trk.Session == leadOuts.Key)
.Where(trk => trk.EndSector >= (ulong)leadOuts.Value))
trk.EndSector = (ulong)leadOuts.Value - 1;
}
2022-03-06 13:29:38 +00:00
var dataExtents = new ExtentsULong();
2024-05-01 04:05:22 +01:00
foreach(Track trk in tracks) dataExtents.Add(trk.StartSector, trk.EndSector);
2022-03-06 13:29:38 +00:00
Tuple<ulong, ulong>[] dataExtentsArray = dataExtents.ToArray();
for(int i = 0; i < dataExtentsArray.Length - 1; i++)
2022-03-06 13:29:38 +00:00
leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1);
}
_dumpLog.WriteLine(Localization.Core.Detecting_disc_type);
UpdateStatus?.Invoke(Localization.Core.Detecting_disc_type);
2024-05-01 04:05:22 +01:00
MMC.DetectDiscType(ref dskType,
sessions,
toc,
_dev,
out hiddenTrack,
out hiddenData,
firstTrackLastSession,
2022-03-06 13:29:38 +00:00
blocks);
// Fix CD-i discs with wrong Lead-Out type
if(dskType is MediaType.CDI or MediaType.CDIREADY && tracks.Length == 1 && tracks[0].Type == TrackType.Audio)
tracks[0].Type = TrackType.CdMode2Formless;
2022-03-06 13:29:38 +00:00
if(hiddenTrack || firstLba > 0)
{
_dumpLog.WriteLine(Localization.Core.Disc_contains_a_hidden_track);
UpdateStatus?.Invoke(Localization.Core.Disc_contains_a_hidden_track);
if(!outputOptical.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreHiddenTracks))
2022-03-06 13:29:38 +00:00
{
StoppingErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_hidden_tracks);
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_hidden_tracks);
return;
}
2024-05-01 04:39:38 +01:00
List<Track> trkList =
[
new()
2017-06-07 22:37:05 +01:00
{
Sequence = (uint)(tracks.Any(t => t.Sequence == 1) ? 0 : 1),
2022-03-06 13:29:38 +00:00
Session = 1,
Type = hiddenData ? TrackType.Data : TrackType.Audio,
StartSector = 0,
BytesPerSector = (int)sectorSize,
RawBytesPerSector = (int)sectorSize,
2022-03-06 13:29:38 +00:00
SubchannelType = subType,
EndSector = tracks.First(t => t.Sequence >= 1).StartSector - 1
2022-03-06 13:29:38 +00:00
}
2024-05-01 04:39:38 +01:00
];
2022-03-06 13:29:38 +00:00
trkList.AddRange(tracks);
tracks = trkList.ToArray();
}
if(tracks.Any(t => t.Type == TrackType.Audio) && desiredSubchannel != MmcSubchannel.Raw)
2022-03-06 13:29:38 +00:00
{
_dumpLog.WriteLine(Localization.Core.WARNING_If_disc_says_CDG_CDEG_CDMIDI_dump_will_be_incorrect);
UpdateStatus?.Invoke(Localization.Core.WARNING_If_disc_says_CDG_CDEG_CDMIDI_dump_will_be_incorrect);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
// Check mode for tracks
foreach(Track trk in tracks.Where(t => t.Type != TrackType.Audio))
{
if(!readcd)
{
2022-03-06 13:29:38 +00:00
trk.Type = TrackType.CdMode1;
2022-03-06 13:29:38 +00:00
continue;
}
_dumpLog.WriteLine(Localization.Core.Checking_mode_for_track_0, trk.Sequence);
UpdateStatus?.Invoke(string.Format(Localization.Core.Checking_mode_for_track_0, trk.Sequence));
2024-05-01 04:05:22 +01:00
sense = _dev.ReadCd(out cmdBuf,
out _,
(uint)(trk.StartSector + trk.Pregap),
blockSize,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
supportedSubchannel,
_dev.Timeout,
out _);
2022-03-06 13:29:38 +00:00
if(sense)
{
_dumpLog.WriteLine(Localization.Core.Unable_to_guess_mode_for_track_0_continuing, trk.Sequence);
UpdateStatus?.Invoke(string.Format(Localization.Core.Unable_to_guess_mode_for_track_0_continuing,
trk.Sequence));
2022-03-06 13:29:38 +00:00
continue;
}
2017-12-19 20:33:03 +00:00
int bufOffset = 0;
2022-03-06 13:29:38 +00:00
while(cmdBuf[0 + bufOffset] != 0x00 ||
cmdBuf[1 + bufOffset] != 0xFF ||
cmdBuf[2 + bufOffset] != 0xFF ||
cmdBuf[3 + bufOffset] != 0xFF ||
cmdBuf[4 + bufOffset] != 0xFF ||
cmdBuf[5 + bufOffset] != 0xFF ||
cmdBuf[6 + bufOffset] != 0xFF ||
cmdBuf[7 + bufOffset] != 0xFF ||
cmdBuf[8 + bufOffset] != 0xFF ||
cmdBuf[9 + bufOffset] != 0xFF ||
cmdBuf[10 + bufOffset] != 0xFF ||
cmdBuf[11 + bufOffset] != 0x00)
{
2024-05-01 04:05:22 +01:00
if(bufOffset + 12 >= cmdBuf.Length) break;
2022-03-06 13:29:38 +00:00
bufOffset++;
}
switch(cmdBuf[15 + bufOffset])
{
case 1:
case 0x61: // Scrambled
UpdateStatus?.Invoke(string.Format(Localization.Core.Track_0_is_MODE1, trk.Sequence));
_dumpLog.WriteLine(Localization.Core.Track_0_is_MODE1, trk.Sequence);
2022-03-06 13:29:38 +00:00
trk.Type = TrackType.CdMode1;
break;
case 2:
case 0x62: // Scrambled
2022-03-16 11:47:00 +00:00
if(dskType is MediaType.CDI or MediaType.CDIREADY)
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Track_0_is_MODE2, trk.Sequence));
_dumpLog.WriteLine(Localization.Core.Track_0_is_MODE2, trk.Sequence);
2022-03-06 13:29:38 +00:00
trk.Type = TrackType.CdMode2Formless;
break;
}
2022-03-06 13:29:38 +00:00
if((cmdBuf[0x012] & 0x20) == 0x20) // mode 2 form 2
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Track_0_is_MODE2_FORM_2, trk.Sequence));
_dumpLog.WriteLine(Localization.Core.Track_0_is_MODE2_FORM_2, trk.Sequence);
2022-03-06 13:29:38 +00:00
trk.Type = TrackType.CdMode2Form2;
2022-03-06 13:29:38 +00:00
break;
}
UpdateStatus?.Invoke(string.Format(Localization.Core.Track_0_is_MODE2_FORM_1, trk.Sequence));
_dumpLog.WriteLine(Localization.Core.Track_0_is_MODE2_FORM_1, trk.Sequence);
2022-03-06 13:29:38 +00:00
trk.Type = TrackType.CdMode2Form1;
// These media type specifications do not legally allow mode 2 tracks to be present
2024-05-01 04:05:22 +01:00
if(dskType is MediaType.CDROM or MediaType.CDPLUS or MediaType.CDV) dskType = MediaType.CD;
2022-03-06 13:29:38 +00:00
break;
default:
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Track_0_is_unknown_mode_1,
trk.Sequence,
cmdBuf[15]));
_dumpLog.WriteLine(Localization.Core.Track_0_is_unknown_mode_1, trk.Sequence, cmdBuf[15]);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(outputOptical.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000"))
{
if(tracks.Length > 1)
{
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_more_than_1_track_not_continuing);
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_more_than_1_track_not_continuing);
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
if(tracks.Any(t => t.Type == TrackType.Audio))
{
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_audio_tracks_not_continuing);
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_audio_tracks_not_continuing);
2022-03-06 13:29:38 +00:00
return;
}
if(tracks.Any(t => t.Type != TrackType.CdMode1))
{
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_only_supports_MODE_1_tracks_not_continuing);
_dumpLog.WriteLine(Localization.Core.Output_format_only_supports_MODE_1_tracks_not_continuing);
2022-03-06 13:29:38 +00:00
return;
}
supportsLongSectors = false;
}
// Check if something prevents from dumping the first track pregap
if(_dumpFirstTrackPregap && readcd)
2023-10-03 22:57:50 +01:00
{
2022-03-06 13:29:38 +00:00
if(!outputOptical.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap))
{
if(_force)
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Output_format_does_not_support_CD_first_track_pregap_continuing);
2024-05-01 04:05:22 +01:00
ErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_CD_first_track_pregap_continuing);
}
2022-03-06 13:29:38 +00:00
else
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Output_format_does_not_support_CD_first_track_pregap_not_continuing);
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_CD_first_track_pregap_not_continuing);
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
_dumpFirstTrackPregap = false;
}
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
// Try how many blocks are readable at once
while(true)
{
if(readcd)
{
2024-05-01 04:05:22 +01:00
sense = _dev.ReadCd(out cmdBuf,
out _,
firstLba,
blockSize,
_maximumReadable,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
supportedSubchannel,
_dev.Timeout,
out _);
if(_dev.Error || sense) _maximumReadable /= 2;
}
2022-03-06 13:29:38 +00:00
else if(read16)
{
2024-05-01 04:05:22 +01:00
sense = _dev.Read16(out cmdBuf,
out _,
0,
false,
true,
false,
firstLba,
blockSize,
0,
_maximumReadable,
false,
_dev.Timeout,
out _);
if(_dev.Error || sense) _maximumReadable /= 2;
2022-03-06 13:29:38 +00:00
}
else if(read12)
2019-12-14 21:41:18 +00:00
{
2024-05-01 04:05:22 +01:00
sense = _dev.Read12(out cmdBuf,
out _,
0,
false,
true,
false,
false,
firstLba,
blockSize,
0,
_maximumReadable,
false,
_dev.Timeout,
out _);
if(_dev.Error || sense) _maximumReadable /= 2;
2022-03-06 13:29:38 +00:00
}
else if(read10)
{
2024-05-01 04:05:22 +01:00
sense = _dev.Read10(out cmdBuf,
out _,
0,
false,
true,
false,
false,
firstLba,
blockSize,
0,
(ushort)_maximumReadable,
_dev.Timeout,
out _);
if(_dev.Error || sense) _maximumReadable /= 2;
2019-12-14 21:41:18 +00:00
}
2022-03-06 13:29:38 +00:00
else if(read6)
{
sense = _dev.Read6(out cmdBuf, out _, firstLba, blockSize, (byte)_maximumReadable, _dev.Timeout, out _);
2019-12-14 21:41:43 +00:00
2024-05-01 04:05:22 +01:00
if(_dev.Error || sense) _maximumReadable /= 2;
2022-03-06 13:29:38 +00:00
}
2024-05-01 04:05:22 +01:00
if(!_dev.Error || _maximumReadable == 1) break;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(_dev.Error || sense)
{
_dumpLog.WriteLine(Localization.Core.Device_error_0_trying_to_guess_ideal_transfer_length, _dev.LastError);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(string.Format(Localization.Core
.Device_error_0_trying_to_guess_ideal_transfer_length,
_dev.LastError));
2022-03-06 13:29:38 +00:00
}
bool cdiWithHiddenTrack1 = false;
if(dskType is MediaType.CDIREADY && tracks.Min(t => t.Sequence) == 1)
{
cdiWithHiddenTrack1 = true;
dskType = MediaType.CDI;
}
2022-03-06 13:29:38 +00:00
// Try to read the first track pregap
if(_dumpFirstTrackPregap && readcd)
ReadCdFirstTrackPregap(blockSize, ref currentSpeed, mediaTags, supportedSubchannel, ref totalDuration);
2023-10-03 22:57:50 +01:00
_dumpLog.WriteLine(Localization.Core.Reading_0_sectors_at_a_time, _maximumReadable);
_dumpLog.WriteLine(Localization.Core.Device_reports_0_blocks_1_bytes, blocks, blocks * blockSize);
_dumpLog.WriteLine(Localization.Core.Device_can_read_0_blocks_at_a_time, _maximumReadable);
_dumpLog.WriteLine(Localization.Core.Device_reports_0_bytes_per_logical_block, blockSize);
2023-10-03 22:57:50 +01:00
_dumpLog.WriteLine(Localization.Core.SCSI_device_type_0, _dev.ScsiType);
_dumpLog.WriteLine(Localization.Core.Media_identified_as_0, dskType);
UpdateStatus?.Invoke(string.Format(Localization.Core.Reading_0_sectors_at_a_time, _maximumReadable));
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_blocks_1_bytes,
blocks,
blocks * blockSize));
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_can_read_0_blocks_at_a_time, _maximumReadable));
UpdateStatus?.Invoke(string.Format(Localization.Core.Device_reports_0_bytes_per_logical_block, blockSize));
UpdateStatus?.Invoke(string.Format(Localization.Core.SCSI_device_type_0, _dev.ScsiType));
UpdateStatus?.Invoke(string.Format(Localization.Core.Media_identified_as_0, dskType));
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
ret = outputOptical.Create(_outputPath,
dskType,
_formatOptions,
blocks,
2022-03-06 13:29:38 +00:00
supportsLongSectors ? blockSize : 2048);
// Cannot create image
if(!ret)
{
_dumpLog.WriteLine(Localization.Core.Error_creating_output_image_not_continuing);
2022-03-06 13:29:38 +00:00
_dumpLog.WriteLine(outputOptical.ErrorMessage);
StoppingErrorMessage?.Invoke(Localization.Core.Error_creating_output_image_not_continuing +
Environment.NewLine +
2022-03-06 13:29:38 +00:00
outputOptical.ErrorMessage);
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
ErrorNumber errno = outputOptical.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcnBytes);
2024-05-01 04:05:22 +01:00
if(errno == ErrorNumber.NoError) mcn = Encoding.ASCII.GetString(mcnBytes);
2022-03-07 07:36:44 +00:00
if(outputOptical.Tracks != null)
2023-10-03 22:57:50 +01:00
{
2022-03-07 07:36:44 +00:00
foreach(Track imgTrack in outputOptical.Tracks)
2019-12-14 21:41:43 +00:00
{
2022-03-07 07:36:44 +00:00
errno = outputOptical.ReadSectorTag(imgTrack.Sequence, SectorTagType.CdTrackIsrc, out byte[] isrcBytes);
2019-12-14 21:41:43 +00:00
2024-05-01 04:05:22 +01:00
if(errno == ErrorNumber.NoError) isrcs[(byte)imgTrack.Sequence] = Encoding.ASCII.GetString(isrcBytes);
2019-12-14 21:41:43 +00:00
2022-03-06 13:29:38 +00:00
Track trk = tracks.FirstOrDefault(t => t.Sequence == imgTrack.Sequence);
2024-05-01 04:05:22 +01:00
if(trk == null) continue;
2017-11-20 05:07:16 +00:00
2022-03-06 13:29:38 +00:00
trk.Pregap = imgTrack.Pregap;
trk.StartSector = imgTrack.StartSector;
trk.EndSector = imgTrack.EndSector;
2024-05-01 04:05:22 +01:00
foreach(KeyValuePair<ushort, int> imgIdx in imgTrack.Indexes) trk.Indexes[imgIdx.Key] = imgIdx.Value;
}
2023-10-03 22:57:50 +01:00
}
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
// Send track list to output plugin. This may fail if subchannel is set but unsupported.
2022-03-07 07:36:44 +00:00
ret = outputOptical.SetTracks(tracks.ToList());
if(!ret && desiredSubchannel == MmcSubchannel.None)
2022-03-06 13:29:38 +00:00
{
_dumpLog.WriteLine(Localization.Core.Error_sending_tracks_to_output_image_not_continuing);
2022-03-06 13:29:38 +00:00
_dumpLog.WriteLine(outputOptical.ErrorMessage);
2019-12-14 21:45:23 +00:00
StoppingErrorMessage?.Invoke(Localization.Core.Error_sending_tracks_to_output_image_not_continuing +
Environment.NewLine +
2022-03-07 07:36:44 +00:00
outputOptical.ErrorMessage);
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2019-12-14 21:45:23 +00:00
2022-03-06 13:29:38 +00:00
// If a subchannel is supported, check if output plugin allows us to write it.
if(desiredSubchannel != MmcSubchannel.None &&
2022-03-07 07:36:44 +00:00
!outputOptical.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreSubchannelRw))
2022-03-06 13:29:38 +00:00
{
if(_force)
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_subchannels_continuing);
ErrorMessage?.Invoke(Localization.Core.Output_format_does_not_support_subchannels_continuing);
}
2022-03-06 13:29:38 +00:00
else
2019-12-14 21:46:58 +00:00
{
_dumpLog.WriteLine(Localization.Core.Output_format_does_not_support_subchannels_not_continuing);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Output_format_does_not_support_subchannels_not_continuing);
2022-03-06 13:29:38 +00:00
return;
2019-12-14 21:46:58 +00:00
}
2022-03-06 13:29:38 +00:00
}
2019-12-14 21:47:26 +00:00
2022-03-06 13:29:38 +00:00
if(supportedSubchannel != MmcSubchannel.None)
{
_dumpLog.WriteLine(string.Format(Localization.Core.Creating_subchannel_log_in_0,
_outputPrefix + ".sub.log"));
2022-03-06 13:29:38 +00:00
subLog = new SubchannelLog(_outputPrefix + ".sub.log", bcdSubchannel);
}
2019-12-14 21:47:26 +00:00
2022-03-06 13:29:38 +00:00
// Set track flags
foreach(KeyValuePair<byte, byte> kvp in trackFlags)
{
Track track = tracks.FirstOrDefault(t => t.Sequence == kvp.Key);
2019-12-14 21:47:26 +00:00
2024-05-01 04:05:22 +01:00
if(track is null) continue;
_dumpLog.WriteLine(Localization.Core.Setting_flags_for_track_0, track.Sequence);
UpdateStatus?.Invoke(string.Format(Localization.Core.Setting_flags_for_track_0, track.Sequence));
2019-12-14 21:51:51 +00:00
2024-05-01 04:39:38 +01:00
outputOptical.WriteSectorTag([kvp.Value], kvp.Key, SectorTagType.CdTrackFlags);
2022-03-06 13:29:38 +00:00
}
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
// Set MCN
if(supportedSubchannel == MmcSubchannel.None)
{
sense = _dev.ReadMcn(out mcn, out _, out _, _dev.Timeout, out _);
2020-06-13 19:15:27 +01:00
if(!sense && mcn != null && mcn != "0000000000000")
2022-03-06 13:29:38 +00:00
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Found_Media_Catalogue_Number_0, mcn));
_dumpLog.WriteLine(Localization.Core.Found_Media_Catalogue_Number_0, mcn);
2020-06-13 19:15:27 +01:00
}
2022-03-06 13:29:38 +00:00
else
mcn = null;
}
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
// Set ISRCs
if(supportedSubchannel == MmcSubchannel.None)
2023-10-03 22:57:50 +01:00
{
2022-03-06 13:29:38 +00:00
foreach(Track trk in tracks)
2019-12-14 21:51:51 +00:00
{
sense = _dev.ReadIsrc((byte)trk.Sequence, out string isrc, out _, out _, _dev.Timeout, out _);
2019-12-14 21:51:51 +00:00
2024-05-01 04:05:22 +01:00
if(sense || isrc is null or "000000000000") continue;
2019-12-14 21:51:51 +00:00
isrcs[(byte)trk.Sequence] = isrc;
2019-12-14 21:51:51 +00:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Found_ISRC_for_track_0_1, trk.Sequence, isrc));
2023-10-03 22:57:50 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core.Found_ISRC_for_track_0_1, trk.Sequence, isrc));
2022-03-06 13:29:38 +00:00
}
2023-10-03 22:57:50 +01:00
}
2020-01-06 22:29:01 +00:00
if(supportedSubchannel != MmcSubchannel.None && desiredSubchannel != MmcSubchannel.None)
2022-03-06 13:29:38 +00:00
{
2024-05-01 04:39:38 +01:00
subchannelExtents = [];
2020-01-06 22:29:01 +00:00
2024-05-01 04:39:38 +01:00
_resume.BadSubchannels ??= [];
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
foreach(int sub in _resume.BadSubchannels) subchannelExtents.Add(sub);
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if(_resume.NextBlock < blocks)
{
for(ulong i = _resume.NextBlock; i < blocks; i++) subchannelExtents.Add((int)i);
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(_resume.NextBlock > 0)
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Resuming_from_block_0, _resume.NextBlock));
_dumpLog.WriteLine(Localization.Core.Resuming_from_block_0, _resume.NextBlock);
2022-03-06 13:29:38 +00:00
}
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
if(_skip < _maximumReadable) _skip = _maximumReadable;
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
#if DEBUG
2022-03-06 13:29:38 +00:00
foreach(Track trk in tracks)
2023-10-03 22:57:50 +01:00
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Track_0_starts_at_LBA_1_and_ends_at_LBA_2,
2024-05-01 04:05:22 +01:00
trk.Sequence,
trk.StartSector,
trk.EndSector));
2023-10-03 22:57:50 +01:00
}
2024-05-01 04:05:22 +01:00
#endif
2022-03-06 13:29:38 +00:00
// Check offset
if(_fixOffset)
{
if(tracks.All(t => t.Type != TrackType.Audio))
2020-01-06 22:29:01 +00:00
{
2022-03-06 13:29:38 +00:00
// No audio tracks so no need to fix offset
_dumpLog.WriteLine(Localization.Core.No_audio_tracks_disabling_offset_fix);
UpdateStatus.Invoke(Localization.Core.No_audio_tracks_disabling_offset_fix);
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
_fixOffset = false;
}
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if(!readcd)
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.READ_CD_command_is_not_supported_disabling_offset_fix_Dump_may_not_be_correct);
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(Localization.Core
.READ_CD_command_is_not_supported_disabling_offset_fix_Dump_may_not_be_correct);
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
_fixOffset = false;
}
}
else if(tracks.Any(t => t.Type == TrackType.Audio))
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.There_are_audio_tracks_and_offset_fixing_is_disabled_dump_may_not_be_correct);
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(Localization.Core
.There_are_audio_tracks_and_offset_fixing_is_disabled_dump_may_not_be_correct);
2022-03-06 13:29:38 +00:00
}
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
// Search for read offset in main database
cdOffset =
_ctx.CdOffsets.FirstOrDefault(d => (d.Manufacturer == _dev.Manufacturer ||
d.Manufacturer == _dev.Manufacturer.Replace('/', '-')) &&
(d.Model == _dev.Model || d.Model == _dev.Model.Replace('/', '-')));
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
Media.Info.CompactDisc.GetOffset(cdOffset,
_dbDev,
_debug,
_dev,
dskType,
_dumpLog,
tracks,
UpdateStatus,
out int? driveOffset,
out int? combinedOffset,
out _supportsPlextorD8);
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if(combinedOffset is null)
{
if(driveOffset is null)
{
_dumpLog.WriteLine(Localization.Core.Drive_reading_offset_not_found_in_database);
UpdateStatus?.Invoke(Localization.Core.Drive_reading_offset_not_found_in_database);
_dumpLog.WriteLine(Localization.Core.Disc_offset_cannot_be_calculated);
UpdateStatus?.Invoke(Localization.Core.Disc_offset_cannot_be_calculated);
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if(tracks.Any(t => t.Type == TrackType.Audio))
{
_dumpLog.WriteLine(Localization.Core.Dump_may_not_be_correct);
2022-03-06 13:29:38 +00:00
UpdateStatus?.Invoke(Localization.Core.Dump_may_not_be_correct);
2020-01-06 22:29:01 +00:00
}
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
if(_fixOffset) _fixOffset = false;
2020-01-06 22:29:01 +00:00
}
else
{
_dumpLog.WriteLine(string.Format(Localization.Core.Drive_reading_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
driveOffset,
driveOffset / 4));
UpdateStatus?.Invoke(string.Format(Localization.Core.Drive_reading_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
driveOffset,
driveOffset / 4));
2022-03-06 13:29:38 +00:00
_dumpLog.WriteLine(Localization.Core.Disc_write_offset_is_unknown_dump_may_not_be_correct);
UpdateStatus?.Invoke(Localization.Core.Disc_write_offset_is_unknown_dump_may_not_be_correct);
2022-03-06 13:29:38 +00:00
offsetBytes = driveOffset.Value;
sectorsForOffset = offsetBytes / (int)sectorSize;
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
if(sectorsForOffset < 0) sectorsForOffset *= -1;
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
if(offsetBytes % sectorSize != 0) sectorsForOffset++;
2022-03-06 13:29:38 +00:00
}
}
else
{
offsetBytes = combinedOffset.Value;
sectorsForOffset = offsetBytes / (int)sectorSize;
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
if(sectorsForOffset < 0) sectorsForOffset *= -1;
2020-01-06 22:29:01 +00:00
2024-05-01 04:05:22 +01:00
if(offsetBytes % sectorSize != 0) sectorsForOffset++;
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
if(driveOffset is null)
{
_dumpLog.WriteLine(Localization.Core.Drive_reading_offset_not_found_in_database);
UpdateStatus?.Invoke(Localization.Core.Drive_reading_offset_not_found_in_database);
2020-01-06 22:29:01 +00:00
_dumpLog.WriteLine(string.Format(Localization.Core.Combined_disc_and_drive_offset_are_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
offsetBytes,
offsetBytes / 4));
2022-11-13 19:38:03 +00:00
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core
.Combined_disc_and_drive_offset_are_0_bytes_1_samples,
offsetBytes,
offsetBytes / 4));
2020-01-06 22:29:01 +00:00
}
2022-03-06 13:29:38 +00:00
else
2020-01-06 22:29:01 +00:00
{
_dumpLog.WriteLine(string.Format(Localization.Core.Drive_reading_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
driveOffset,
driveOffset / 4));
UpdateStatus?.Invoke(string.Format(Localization.Core.Drive_reading_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
driveOffset,
driveOffset / 4));
2020-01-06 22:29:01 +00:00
2022-03-06 13:29:38 +00:00
discOffset = offsetBytes - driveOffset;
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core.Disc_offset_is_0_bytes_1_samples,
discOffset,
discOffset / 4));
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Disc_offset_is_0_bytes_1_samples,
discOffset,
discOffset / 4));
}
2022-03-06 13:29:38 +00:00
}
2024-05-01 04:05:22 +01:00
mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin",
_dev,
blocks,
blockSize,
_maximumReadable,
_private,
_dimensions);
ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008);
if(_createGraph)
2022-03-06 13:29:38 +00:00
{
Spiral.DiscParameters discSpiralParameters = Spiral.DiscParametersFromMediaType(dskType);
if(discSpiralParameters is not null)
2022-12-05 20:11:59 +00:00
_mediaGraph = new Spiral((int)_dimensions, (int)_dimensions, discSpiralParameters, blocks);
2022-12-05 21:29:04 +00:00
else
_mediaGraph = new BlockMap((int)_dimensions, (int)_dimensions, blocks);
2022-12-05 21:29:04 +00:00
if(_mediaGraph is not null)
2023-10-03 22:57:50 +01:00
{
foreach(Tuple<ulong, ulong> e in extents.ToArray())
2022-12-05 20:11:59 +00:00
_mediaGraph?.PaintSectorsGood(e.Item1, (uint)(e.Item2 - e.Item1 + 2));
2023-10-03 22:57:50 +01:00
}
2022-12-05 21:29:04 +00:00
_mediaGraph?.PaintSectorsBad(_resume.BadBlocks);
}
2022-03-06 13:29:38 +00:00
audioExtents = new ExtentsULong();
2022-03-06 13:29:38 +00:00
foreach(Track audioTrack in tracks.Where(t => t.Type == TrackType.Audio))
audioExtents.Add(audioTrack.StartSector, audioTrack.EndSector);
2022-03-06 13:29:38 +00:00
// Set speed
if(_speedMultiplier >= 0)
{
2023-10-03 22:57:50 +01:00
_dumpLog.WriteLine(_speed == 0xFFFF
? Localization.Core.Setting_speed_to_MAX_for_data_reading
: string.Format(Localization.Core.Setting_speed_to_0_x_for_data_reading, _speed));
2023-10-03 22:57:50 +01:00
UpdateStatus?.Invoke(_speed == 0xFFFF
? Localization.Core.Setting_speed_to_MAX_for_data_reading
: string.Format(Localization.Core.Setting_speed_to_0_x_for_data_reading, _speed));
2022-03-06 13:29:38 +00:00
_speed *= _speedMultiplier;
2024-05-01 04:05:22 +01:00
if(_speed is 0 or > 0xFFFF) _speed = 0xFFFF;
_dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
// Start reading
_dumpStopwatch.Restart();
if(dskType == MediaType.CDIREADY || cdiWithHiddenTrack1)
2022-03-06 13:29:38 +00:00
{
Track track0 = tracks.FirstOrDefault(t => t.Sequence is 0 or 1);
2022-03-06 13:29:38 +00:00
track0.Type = TrackType.CdMode2Formless;
2022-03-06 13:29:38 +00:00
if(!supportsLongSectors)
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Dumping_CD_i_Ready_requires_the_output_image_format_to_support_long_sectors);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Dumping_CD_i_Ready_requires_the_output_image_format_to_support_long_sectors);
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
if(!readcd)
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Dumping_CD_i_Ready_requires_the_drive_to_support_the_READ_CD_command);
2024-05-01 04:05:22 +01:00
StoppingErrorMessage?.Invoke(Localization.Core
.Dumping_CD_i_Ready_requires_the_drive_to_support_the_READ_CD_command);
2022-03-06 13:29:38 +00:00
return;
}
2024-05-01 04:05:22 +01:00
_dev.ReadCd(out cmdBuf,
out _,
0,
2352,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
MmcSubchannel.None,
_dev.Timeout,
2022-03-06 13:29:38 +00:00
out _);
2022-03-06 13:29:38 +00:00
hiddenData = IsData(cmdBuf);
2022-03-06 13:29:38 +00:00
if(!hiddenData)
{
cdiReadyReadAsAudio = IsScrambledData(cmdBuf, 0, out combinedOffset);
2022-03-06 13:29:38 +00:00
if(cdiReadyReadAsAudio)
{
offsetBytes = combinedOffset.Value;
sectorsForOffset = offsetBytes / (int)sectorSize;
2024-05-01 04:05:22 +01:00
if(sectorsForOffset < 0) sectorsForOffset *= -1;
2024-05-01 04:05:22 +01:00
if(offsetBytes % sectorSize != 0) sectorsForOffset++;
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.Enabling_skipping_CD_i_Ready_hole_because_drive_returns_data_as_audio);
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(Localization.Core
.Enabling_skipping_CD_i_Ready_hole_because_drive_returns_data_as_audio);
2022-03-06 13:29:38 +00:00
_skipCdireadyHole = true;
2022-03-06 13:29:38 +00:00
if(driveOffset is null)
{
_dumpLog.WriteLine(Localization.Core.Drive_reading_offset_not_found_in_database);
UpdateStatus?.Invoke(Localization.Core.Drive_reading_offset_not_found_in_database);
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core
.Combined_disc_and_drive_offset_are_0_bytes_1_samples,
offsetBytes,
offsetBytes / 4));
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core
.Combined_disc_and_drive_offset_are_0_bytes_1_samples,
offsetBytes,
offsetBytes / 4));
}
2022-03-06 13:29:38 +00:00
else
{
_dumpLog.WriteLine(string.Format(Localization.Core.Drive_reading_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
driveOffset,
driveOffset / 4));
UpdateStatus?.Invoke(string.Format(Localization.Core.Drive_reading_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
driveOffset,
driveOffset / 4));
2022-03-06 13:29:38 +00:00
discOffset = offsetBytes - driveOffset;
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core.Disc_offset_is_0_bytes_1_samples,
discOffset,
discOffset / 4));
2022-03-06 13:29:38 +00:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Disc_offset_is_0_bytes_1_samples,
2024-05-01 04:05:22 +01:00
discOffset,
discOffset / 4));
2022-03-06 13:29:38 +00:00
}
}
2022-03-06 13:29:38 +00:00
}
if(!_skipCdireadyHole)
{
2024-05-01 04:05:22 +01:00
_dumpLog.WriteLine(Localization.Core
.There_will_be_thousand_of_errors_between_track_0_and_track_1_that_is_normal_and_you_can_ignore_them);
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(Localization.Core
.There_will_be_thousand_of_errors_between_track_0_and_track_1_that_is_normal_and_you_can_ignore_them);
}
2022-03-06 13:29:38 +00:00
if(_skipCdireadyHole)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
ReadCdiReady(blockSize,
ref currentSpeed,
currentTry,
extents,
ibgLog,
ref imageWriteDuration,
leadOutExtents,
ref maxSpeed,
mhddLog,
ref minSpeed,
subSize,
supportedSubchannel,
ref totalDuration,
tracks,
subLog,
desiredSubchannel,
isrcs,
ref mcn,
subchannelExtents,
blocks,
cdiReadyReadAsAudio,
offsetBytes,
sectorsForOffset,
smallestPregapLbaPerTrack);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
}
2024-05-01 04:05:22 +01:00
ReadCdData(audioExtents,
blocks,
blockSize,
ref currentSpeed,
currentTry,
extents,
ibgLog,
ref imageWriteDuration,
lastSector,
leadOutExtents,
ref maxSpeed,
mhddLog,
ref minSpeed,
out newTrim,
tracks[0].Type != TrackType.Audio,
offsetBytes,
read6,
read10,
read12,
read16,
readcd,
sectorsForOffset,
subSize,
supportedSubchannel,
supportsLongSectors,
ref totalDuration,
tracks,
subLog,
desiredSubchannel,
isrcs,
ref mcn,
subchannelExtents,
smallestPregapLbaPerTrack);
2019-12-14 22:18:58 +00:00
2022-03-06 13:29:38 +00:00
// TODO: Enable when underlying images support lead-outs
/*
DumpCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration,
leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd,
supportedSubchannel, subSize, ref totalDuration, subLog, desiredSubchannel, isrcs, ref mcn, tracks,
smallestPregapLbaPerTrack);
*/
2019-12-14 22:18:58 +00:00
_dumpStopwatch.Stop();
2022-03-06 13:29:38 +00:00
mhddLog.Close();
2019-12-14 22:18:58 +00:00
2024-05-01 04:05:22 +01:00
ibgLog.Close(_dev,
blocks,
blockSize,
_dumpStopwatch.Elapsed.TotalSeconds,
currentSpeed * 1024,
blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
_devicePath);
2019-12-14 22:18:58 +00:00
2023-09-26 03:39:10 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Dump_finished_in_0,
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
2019-12-14 22:18:58 +00:00
2023-09-26 02:40:11 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_dump_speed_0,
2024-04-26 03:18:47 +01:00
ByteSize.FromBytes(blockSize * (blocks + 1))
.Per(totalDuration.Milliseconds())
.Humanize()));
2019-12-14 22:18:58 +00:00
2023-09-26 02:40:11 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_write_speed_0,
2024-04-26 03:18:47 +01:00
ByteSize.FromBytes(blockSize * (blocks + 1))
.Per(imageWriteDuration.Seconds())
.Humanize()));
2019-12-14 22:18:58 +00:00
2023-09-26 03:39:10 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core.Dump_finished_in_0,
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
2019-12-14 22:18:58 +00:00
2023-09-26 02:40:11 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core.Average_dump_speed_0,
2024-04-26 03:18:47 +01:00
ByteSize.FromBytes(blockSize * (blocks + 1))
.Per(totalDuration.Milliseconds())
.Humanize()));
2019-12-14 22:15:05 +00:00
2023-09-26 02:40:11 +01:00
_dumpLog.WriteLine(string.Format(Localization.Core.Average_write_speed_0,
2024-04-26 03:18:47 +01:00
ByteSize.FromBytes(blockSize * (blocks + 1))
.Per(imageWriteDuration.Seconds())
.Humanize()));
2024-05-01 04:05:22 +01:00
TrimCdUserData(audioExtents,
blockSize,
currentTry,
extents,
newTrim,
offsetBytes,
read6,
read10,
read12,
read16,
readcd,
sectorsForOffset,
subSize,
supportedSubchannel,
supportsLongSectors,
ref totalDuration,
subLog,
desiredSubchannel,
tracks,
isrcs,
ref mcn,
subchannelExtents,
2022-03-06 13:29:38 +00:00
smallestPregapLbaPerTrack);
2020-06-13 19:15:27 +01:00
if(dskType is MediaType.CDR or MediaType.CDRW && _resume.BadBlocks.Count > 0 && _ignoreCdrRunOuts > 0)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
HandleCdrRunOutSectors(blocks,
desiredSubchannel,
extents,
subchannelExtents,
subLog,
supportsLongSectors,
trackFlags,
tracks);
2023-10-03 22:57:50 +01:00
}
2024-05-01 04:05:22 +01:00
RetryCdUserData(audioExtents,
blockSize,
currentTry,
extents,
offsetBytes,
readcd,
sectorsForOffset,
subSize,
supportedSubchannel,
ref totalDuration,
subLog,
desiredSubchannel,
tracks,
isrcs,
ref mcn,
subchannelExtents,
smallestPregapLbaPerTrack,
supportsLongSectors);
2022-03-06 13:29:38 +00:00
foreach(Tuple<ulong, ulong> leadoutExtent in leadOutExtents.ToArray())
{
for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++) subchannelExtents.Remove((int)e);
}
if(subchannelExtents.Count > 0 && _retryPasses > 0 && _retrySubchannel)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
RetrySubchannel(readcd,
subSize,
supportedSubchannel,
ref totalDuration,
subLog,
desiredSubchannel,
tracks,
isrcs,
ref mcn,
subchannelExtents,
smallestPregapLbaPerTrack);
2023-10-03 22:57:50 +01:00
}
2017-12-19 20:33:03 +00:00
2022-03-06 13:29:38 +00:00
// Write media tags to image
if(!_aborted)
2023-10-03 22:57:50 +01:00
{
2022-03-06 13:29:38 +00:00
foreach(KeyValuePair<MediaTagType, byte[]> tag in mediaTags)
{
if(tag.Value is null)
2017-06-07 22:37:05 +01:00
{
2025-08-17 06:11:22 +01:00
AaruLogging.Error(Localization.Core.Error_Tag_type_0_is_null_skipping, tag.Key);
2022-03-06 13:29:38 +00:00
continue;
}
2022-03-06 13:29:38 +00:00
ret = outputOptical.WriteMediaTag(tag.Value, tag.Key);
2017-06-07 22:37:05 +01:00
2024-05-01 04:05:22 +01:00
if(ret || _force) continue;
2022-03-06 13:29:38 +00:00
// Cannot write tag to image
_dumpLog.WriteLine(string.Format(Localization.Core.Cannot_write_tag_0, tag.Key));
2022-03-06 13:29:38 +00:00
StoppingErrorMessage?.Invoke(outputOptical.ErrorMessage);
2022-03-06 13:29:38 +00:00
return;
}
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
_resume.BadBlocks.Sort();
2024-05-01 04:05:22 +01:00
foreach(ulong bad in _resume.BadBlocks) _dumpLog.WriteLine(Localization.Core.Sector_0_could_not_be_read, bad);
2022-03-06 13:29:38 +00:00
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
2024-05-01 04:39:38 +01:00
_resume.BadSubchannels = [];
2022-03-06 13:29:38 +00:00
_resume.BadSubchannels.AddRange(subchannelExtents);
_resume.BadSubchannels.Sort();
2020-06-13 19:15:27 +01:00
2022-03-06 13:29:38 +00:00
if(_generateSubchannels &&
outputOptical.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) &&
!_aborted)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
Media.CompactDisc.GenerateSubchannels(subchannelExtents,
tracks,
trackFlags,
blocks,
subLog,
_dumpLog,
InitProgress,
UpdateProgress,
EndProgress,
outputOptical);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
// TODO: Disc ID
var metadata = new CommonTypes.Structs.ImageInfo
2022-03-06 13:29:38 +00:00
{
Application = "Aaru",
ApplicationVersion = Version.GetVersion()
};
2020-01-09 18:01:43 +00:00
if(!outputOptical.SetImageInfo(metadata))
2023-10-03 22:57:50 +01:00
{
ErrorMessage?.Invoke(Localization.Core.Error_0_setting_metadata +
Environment.NewLine +
2022-03-06 13:29:38 +00:00
outputOptical.ErrorMessage);
2023-10-03 22:57:50 +01:00
}
2020-01-09 18:01:43 +00:00
2022-03-06 13:29:38 +00:00
outputOptical.SetDumpHardware(_resume.Tries);
2024-05-01 04:05:22 +01:00
if(_preSidecar != null) outputOptical.SetMetadata(_preSidecar);
2022-03-06 13:29:38 +00:00
foreach(KeyValuePair<byte, string> isrc in isrcs)
{
// TODO: Track tags
2022-03-07 07:36:44 +00:00
if(!outputOptical.WriteSectorTag(Encoding.ASCII.GetBytes(isrc.Value), isrc.Key, SectorTagType.CdTrackIsrc))
2022-03-06 13:29:38 +00:00
continue;
UpdateStatus?.Invoke(string.Format(Localization.Core.Setting_ISRC_for_track_0_to_1, isrc.Key, isrc.Value));
_dumpLog.WriteLine(Localization.Core.Setting_ISRC_for_track_0_to_1, isrc.Key, isrc.Value);
2022-03-06 13:29:38 +00:00
}
if(mcn != null && outputOptical.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN))
2022-03-06 13:29:38 +00:00
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Setting_disc_Media_Catalogue_Number_to_0, mcn));
_dumpLog.WriteLine(Localization.Core.Setting_disc_Media_Catalogue_Number_to_0, mcn);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
foreach(Track trk in tracks)
{
// Fix track starts in each session's first track
if(tracks.Where(t => t.Session == trk.Session).MinBy(t => t.Sequence).Sequence == trk.Sequence)
{
2024-05-01 04:05:22 +01:00
if(trk.Sequence == 1) continue;
2022-03-06 13:29:38 +00:00
trk.StartSector -= trk.Pregap;
trk.Indexes[0] = (int)trk.StartSector;
2022-03-06 13:29:38 +00:00
continue;
}
if(trk.Indexes.TryGetValue(0, out int idx0) && trk.Indexes.TryGetValue(1, out int idx1) && idx0 == idx1)
2022-03-06 13:29:38 +00:00
trk.Indexes.Remove(0);
}
2022-03-07 07:36:44 +00:00
outputOptical.SetTracks(tracks.ToList());
2017-12-19 20:33:03 +00:00
_dumpLog.WriteLine(Localization.Core.Closing_output_file);
UpdateStatus?.Invoke(Localization.Core.Closing_output_file);
_imageCloseStopwatch.Restart();
2022-03-06 13:29:38 +00:00
outputOptical.Close();
_imageCloseStopwatch.Stop();
2023-09-26 03:39:10 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Closed_in_0,
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
2020-05-04 03:11:40 +01:00
2022-03-06 13:29:38 +00:00
subLog?.Close();
2022-03-06 13:29:38 +00:00
if(_aborted)
{
_dumpLog.WriteLine(Localization.Core.Aborted);
2022-03-06 13:29:38 +00:00
return;
}
2022-03-06 13:29:38 +00:00
double totalChkDuration = 0;
2022-03-06 13:29:38 +00:00
if(_metadata)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
WriteOpticalSidecar(blockSize,
blocks,
dskType,
null,
mediaTags,
sessions,
out totalChkDuration,
2022-03-06 13:29:38 +00:00
discOffset);
2023-10-03 22:57:50 +01:00
}
_dumpStopwatch.Stop();
2022-03-06 13:29:38 +00:00
UpdateStatus?.Invoke("");
2024-05-01 04:05:22 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core
.Took_a_total_of_0_1_processing_commands_2_checksumming_3_writing_4_closing,
_dumpStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second),
totalDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
totalChkDuration.Milliseconds().Humanize(minUnit: TimeUnit.Second),
imageWriteDuration.Seconds().Humanize(minUnit: TimeUnit.Second),
_imageCloseStopwatch.Elapsed.Humanize(minUnit: TimeUnit.Second)));
2023-09-26 02:40:11 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Average_speed_0,
2024-04-26 03:18:47 +01:00
ByteSize.FromBytes(blockSize * (blocks + 1))
.Per(totalDuration.Milliseconds())
.Humanize()));
2022-03-06 13:29:38 +00:00
if(maxSpeed > 0)
2023-10-03 22:57:50 +01:00
{
UpdateStatus?.Invoke(string.Format(Localization.Core.Fastest_speed_burst_0,
ByteSize.FromMegabytes(maxSpeed).Per(_oneSecond).Humanize()));
2023-10-03 22:57:50 +01:00
}
if(minSpeed is > 0 and < double.MaxValue)
2023-10-03 22:57:50 +01:00
{
2023-09-26 02:40:11 +01:00
UpdateStatus?.Invoke(string.Format(Localization.Core.Slowest_speed_burst_0,
ByteSize.FromMegabytes(minSpeed).Per(_oneSecond).Humanize()));
2023-10-03 22:57:50 +01:00
}
UpdateStatus?.Invoke(string.Format(Localization.Core._0_sectors_could_not_be_read, _resume.BadBlocks.Count));
UpdateStatus?.Invoke(string.Format(Localization.Core._0_subchannels_could_not_be_read,
_resume.BadSubchannels.Count));
2017-06-20 05:48:09 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus?.Invoke("");
Statistics.AddMedia(dskType, true);
}
2017-12-19 20:33:03 +00:00
}