diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs index f5072080d..c0b2b930e 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs @@ -31,6 +31,7 @@ // ****************************************************************************/ using System; +using System.Collections.Generic; using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Extents; @@ -83,7 +84,8 @@ namespace Aaru.Core.Devices.Dumping ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10, bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize, MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, - Track[] tracks, SubchannelLog subLog, MmcSubchannel desiredSubchannel) + Track[] tracks, SubchannelLog subLog, MmcSubchannel desiredSubchannel, + Dictionary isrcs) { ulong sectorSpeedStart = 0; // Used to calculate correct speed DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation @@ -385,15 +387,8 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i + r, 1); - if(desiredSubchannel != MmcSubchannel.None) - { - if(supportedSubchannel == MmcSubchannel.Q16) - sub = Subchannel.ConvertQToRaw(sub); - - _outputPlugin.WriteSectorsTag(sub, i + r, 1, SectorTagType.CdSectorSubchannel); - } - - subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)(i + r), 1); + WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i + r, 1, subLog, + isrcs, (byte)track.TrackSequence); } else { @@ -506,15 +501,8 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i, blocksToRead); - if(desiredSubchannel != MmcSubchannel.None) - { - if(supportedSubchannel == MmcSubchannel.Q16) - sub = Subchannel.ConvertQToRaw(sub); - - _outputPlugin.WriteSectorsTag(sub, i, blocksToRead, SectorTagType.CdSectorSubchannel); - } - - subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)i, blocksToRead); + WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, blocksToRead, subLog, + isrcs, (byte)track.TrackSequence); } else { diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs index 46f3ed055..6c7d5558e 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs @@ -64,55 +64,56 @@ namespace Aaru.Core.Devices.Dumping /// Dumps a compact disc void CompactDisc() { - ExtentsULong audioExtents; // Extents with audio sectors - ulong blocks; // Total number of positive sectors - uint blockSize; // Size of the read sector in bytes - CdOffset cdOffset; // Read offset from database - byte[] cmdBuf; // Data buffer - DumpHardwareType currentTry = null; // Current dump hardware try - double currentSpeed = 0; // Current read speed - int? discOffset = null; // Disc write offset - DateTime dumpStart = DateTime.UtcNow; // Time of dump start - DateTime end; // Time of operation end - ExtentsULong extents = null; // Extents - bool hiddenData; // Hidden track is data - IbgLog ibgLog; // IMGBurn log - double imageWriteDuration = 0; // Duration of image write - long lastSector; // Last sector number - var leadOutExtents = new ExtentsULong(); // Lead-out extents - Dictionary leadOutStarts = new Dictionary(); // Lead-out starts - double maxSpeed = double.MinValue; // Maximum speed - MhddLog mhddLog; // MHDD log - double minSpeed = double.MaxValue; // Minimum speed - bool newTrim; // Is trim a new one? - int offsetBytes = 0; // Read offset - bool read6 = false; // Device supports READ(6) - bool read10 = false; // Device supports READ(10) - bool read12 = false; // Device supports READ(12) - bool read16 = false; // Device supports READ(16) - bool readcd; // Device supports READ CD - bool ret; // Image writing return status - const uint sectorSize = 2352; // Full sector size - int sectorsForOffset = 0; // Sectors needed to fix offset - bool sense = true; // Sense indicator - int sessions; // Number of sessions in disc - DateTime start; // Start of operation - SubchannelLog subLog = null; // Subchannel log - uint subSize; // Subchannel size in bytes - TrackSubchannelType subType; // Track subchannel type - bool supportsLongSectors = true; // Supports reading EDC and ECC - bool supportsPqSubchannel; // Supports reading PQ subchannel - bool supportsRwSubchannel; // Supports reading RW subchannel - byte[] tmpBuf; // Temporary buffer - FullTOC.CDFullTOC? toc; // Full CD TOC - double totalDuration = 0; // Total commands duration - Dictionary trackFlags = new Dictionary(); // 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 + ExtentsULong audioExtents; // Extents with audio sectors + ulong blocks; // Total number of positive sectors + uint blockSize; // Size of the read sector in bytes + CdOffset cdOffset; // Read offset from database + byte[] cmdBuf; // Data buffer + DumpHardwareType currentTry = null; // Current dump hardware try + double currentSpeed = 0; // Current read speed + int? discOffset = null; // Disc write offset + DateTime dumpStart = DateTime.UtcNow; // Time of dump start + DateTime end; // Time of operation end + ExtentsULong extents = null; // Extents + bool hiddenData; // Hidden track is data + IbgLog ibgLog; // IMGBurn log + double imageWriteDuration = 0; // Duration of image write + long lastSector; // Last sector number + var leadOutExtents = new ExtentsULong(); // Lead-out extents + Dictionary leadOutStarts = new Dictionary(); // Lead-out starts + double maxSpeed = double.MinValue; // Maximum speed + MhddLog mhddLog; // MHDD log + double minSpeed = double.MaxValue; // Minimum speed + bool newTrim; // Is trim a new one? + int offsetBytes = 0; // Read offset + bool read6 = false; // Device supports READ(6) + bool read10 = false; // Device supports READ(10) + bool read12 = false; // Device supports READ(12) + bool read16 = false; // Device supports READ(16) + bool readcd; // Device supports READ CD + bool ret; // Image writing return status + const uint sectorSize = 2352; // Full sector size + int sectorsForOffset = 0; // Sectors needed to fix offset + bool sense = true; // Sense indicator + int sessions; // Number of sessions in disc + DateTime start; // Start of operation + SubchannelLog subLog = null; // Subchannel log + uint subSize; // Subchannel size in bytes + TrackSubchannelType subType; // Track subchannel type + bool supportsLongSectors = true; // Supports reading EDC and ECC + bool supportsPqSubchannel; // Supports reading PQ subchannel + bool supportsRwSubchannel; // Supports reading RW subchannel + byte[] tmpBuf; // Temporary buffer + FullTOC.CDFullTOC? toc; // Full CD TOC + double totalDuration = 0; // Total commands duration + Dictionary trackFlags = new Dictionary(); // Track flags + Track[] tracks; // Tracks in disc + int firstTrackLastSession; // Number of first track in last session + bool hiddenTrack; // Disc has a hidden track before track 1 + MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel + MmcSubchannel desiredSubchannel; // User requested subchannel + bool bcdSubchannel = false; // Subchannel positioning is in BCD + Dictionary isrcs = new Dictionary(); Dictionary mediaTags = new Dictionary(); // Media tags @@ -834,23 +835,18 @@ namespace Aaru.Core.Devices.Dumping } // Set ISRCs - // TODO: Use subchannels - foreach(Track trk in tracks) - { - sense = _dev.ReadIsrc((byte)trk.TrackSequence, out string isrc, out _, out _, _dev.Timeout, out _); + if(supportedSubchannel == MmcSubchannel.None) + foreach(Track trk in tracks) + { + sense = _dev.ReadIsrc((byte)trk.TrackSequence, out string isrc, out _, out _, _dev.Timeout, out _); - if(sense || - isrc == null || - isrc == "000000000000") - continue; + if(sense || + isrc == null || + isrc == "000000000000") + continue; - if(!_outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc), trk.TrackStartSector, - SectorTagType.CdTrackIsrc)) - continue; - - UpdateStatus?.Invoke($"Setting ISRC for track {trk.TrackSequence} to {isrc}"); - _dumpLog.WriteLine("Setting ISRC for track {0} to {1}", trk.TrackSequence, isrc); - } + isrcs[(byte)trk.TrackSequence] = isrc; + } if(_resume.NextBlock > 0) { @@ -1019,13 +1015,13 @@ namespace Aaru.Core.Devices.Dumping ref imageWriteDuration, lastSector, leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, out newTrim, tracks[0].TrackType != TrackType.Audio, offsetBytes, read6, read10, read12, read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, ref totalDuration, - tracks, subLog, desiredSubchannel); + tracks, subLog, desiredSubchannel, isrcs); // 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); + supportedSubchannel, subSize, ref totalDuration, subLog, desiredSubchannel, isrcs); */ end = DateTime.UtcNow; @@ -1052,10 +1048,10 @@ namespace Aaru.Core.Devices.Dumping TrimCdUserData(audioExtents, blockSize, currentTry, extents, newTrim, offsetBytes, read6, read10, read12, read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, - ref totalDuration, subLog, desiredSubchannel); + ref totalDuration, subLog, desiredSubchannel, tracks, isrcs); RetryCdUserData(audioExtents, blockSize, currentTry, extents, offsetBytes, readcd, sectorsForOffset, - subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel); + subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, tracks, isrcs); // Write media tags to image if(!_aborted) @@ -1102,6 +1098,19 @@ namespace Aaru.Core.Devices.Dumping if(_preSidecar != null) _outputPlugin.SetCicmMetadata(_preSidecar); + foreach(KeyValuePair isrc in isrcs) + { + // TODO: Track tags + Track track = tracks.First(t => t.TrackSequence == isrc.Key); + + if(!_outputPlugin.WriteSectorTag(Encoding.ASCII.GetBytes(isrc.Value), track.TrackStartSector, + SectorTagType.CdTrackIsrc)) + continue; + + UpdateStatus?.Invoke($"Setting ISRC for track {isrc.Key} to {isrc.Value}"); + _dumpLog.WriteLine("Setting ISRC for track {0} to {1}", isrc.Key, isrc.Value); + } + _dumpLog.WriteLine("Closing output file."); UpdateStatus?.Invoke("Closing output file."); DateTime closeStart = DateTime.Now; diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs index 3067516a7..117da5b9d 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs @@ -32,12 +32,12 @@ using System; using System.Collections.Generic; -using Aaru.CommonTypes.Enums; +using System.Linq; using Aaru.CommonTypes.Extents; +using Aaru.CommonTypes.Structs; using Aaru.CommonTypes.Structs.Devices.SCSI; using Aaru.Console; using Aaru.Core.Logging; -using Aaru.Decoders.CD; using Aaru.Decoders.SCSI; using Aaru.Devices; using Schemas; @@ -53,7 +53,7 @@ namespace Aaru.Core.Devices.Dumping void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration, SubchannelLog subLog, - MmcSubchannel desiredSubchannel) + MmcSubchannel desiredSubchannel, Track[] tracks, Dictionary isrcs) { bool sense = true; // Sense indicator byte[] cmdBuf = null; // Data buffer @@ -207,6 +207,9 @@ namespace Aaru.Core.Devices.Dumping forward ? "forward" : "reverse", runningPersistent ? "recovering partial data, " : "")); + Track track = tracks.OrderBy(t => t.TrackStartSector). + LastOrDefault(t => badSector >= t.TrackStartSector); + byte sectorsToReRead = 1; uint badSectorToReRead = (uint)badSector; @@ -281,15 +284,8 @@ namespace Aaru.Core.Devices.Dumping Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); _outputPlugin.WriteSectorLong(data, badSector); - if(desiredSubchannel != MmcSubchannel.None) - { - if(supportedSubchannel == MmcSubchannel.Q16) - sub = Subchannel.ConvertQToRaw(sub); - - _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel); - } - - subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)badSector, 1); + WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, isrcs, + (byte)track.TrackSequence); } else { @@ -364,6 +360,9 @@ namespace Aaru.Core.Devices.Dumping PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}"); + Track track = tracks.OrderBy(t => t.TrackStartSector). + LastOrDefault(t => badSector >= t.TrackStartSector); + if(readcd) { sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1, @@ -387,15 +386,8 @@ namespace Aaru.Core.Devices.Dumping Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); _outputPlugin.WriteSectorLong(data, badSector); - if(desiredSubchannel != MmcSubchannel.None) - { - if(supportedSubchannel == MmcSubchannel.Q16) - sub = Subchannel.ConvertQToRaw(sub); - - _outputPlugin.WriteSectorTag(sub, badSector, SectorTagType.CdSectorSubchannel); - } - - subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)badSector, 1); + WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, + isrcs, (byte)track.TrackSequence); } else { diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs b/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs index a27acc576..a63ae1c32 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs @@ -31,6 +31,7 @@ // ****************************************************************************/ using System; +using System.Collections.Generic; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Extents; using Aaru.Core.Logging; @@ -225,7 +226,7 @@ namespace Aaru.Core.Devices.Dumping ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, bool read12, bool read16, bool readcd, MmcSubchannel supportedSubchannel, uint subSize, ref double totalDuration, - SubchannelLog subLog, MmcSubchannel desiredSubchannel) + SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary isrcs) { byte[] cmdBuf = null; // Data buffer const uint sectorSize = 2352; // Full sector size @@ -308,6 +309,9 @@ namespace Aaru.Core.Devices.Dumping _outputPlugin.WriteSectorsLong(data, i, _maximumReadable); + WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, i, _maximumReadable, + subLog, isrcs, 0xAA); + if(desiredSubchannel != MmcSubchannel.None) { if(supportedSubchannel == MmcSubchannel.Q16) diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs index b2216a846..278f3f3fd 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs @@ -30,7 +30,12 @@ // Copyright © 2011-2020 Natalia Portillo // ****************************************************************************/ +using System; +using System.Collections.Generic; +using Aaru.Checksums; +using Aaru.CommonTypes.Enums; using Aaru.Core.Logging; +using Aaru.Decoders.CD; using Aaru.Devices; // ReSharper disable JoinDeclarationAndInitializer @@ -60,5 +65,60 @@ namespace Aaru.Core.Devices.Dumping MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); } + + void WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, byte[] sub, + ulong sectorAddress, uint length, SubchannelLog subLog, + Dictionary isrcs, byte currentTrack) + { + if(supportedSubchannel == MmcSubchannel.Q16) + sub = Subchannel.ConvertQToRaw(sub); + + if(desiredSubchannel != MmcSubchannel.None) + _outputPlugin.WriteSectorsTag(sub, sectorAddress, 1, SectorTagType.CdSectorSubchannel); + + subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, 1); + + byte[] deSub = Subchannel.Deinterleave(sub); + + // Check subchannel + for(int subPos = 0; subPos < deSub.Length; subPos += 96) + { + // ISRC + if((deSub[subPos + 12] & 0x3) == 3) + { + byte[] q = new byte[12]; + Array.Copy(deSub, subPos + 12, q, 0, 12); + string isrc = Subchannel.DecodeIsrc(q); + + if(isrc == null || + isrc == "000000000000") + continue; + + if(!isrcs.ContainsKey(currentTrack)) + { + _dumpLog?.WriteLine($"Found new ISRC {isrc} for track {currentTrack}."); + UpdateStatus?.Invoke($"Found new ISRC {isrc} for track {currentTrack}."); + } + else if(isrcs[currentTrack] != isrc) + { + CRC16CCITTContext.Data(q, 10, out byte[] crc); + + if(crc[0] != q[10] || + crc[1] != q[11]) + { + continue; + } + + _dumpLog?. + WriteLine($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); + + UpdateStatus?. + Invoke($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); + } + + isrcs[currentTrack] = isrc; + } + } + } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs index f999176af..0450e4386 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs @@ -31,8 +31,11 @@ // ****************************************************************************/ using System; +using System.Collections.Generic; +using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Extents; +using Aaru.CommonTypes.Structs; using Aaru.Core.Logging; using Aaru.Decoders.CD; using Aaru.Devices; @@ -50,7 +53,8 @@ namespace Aaru.Core.Devices.Dumping ExtentsULong extents, bool newTrim, int offsetBytes, bool read6, bool read10, bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize, MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, - SubchannelLog subLog, MmcSubchannel desiredSubchannel) + SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks, + Dictionary isrcs) { DateTime start; DateTime end; @@ -110,6 +114,9 @@ namespace Aaru.Core.Devices.Dumping PulseProgress?.Invoke($"Trimming sector {badSector}"); + Track track = tracks.OrderBy(t => t.TrackStartSector). + LastOrDefault(t => badSector >= t.TrackStartSector); + byte sectorsToTrim = 1; uint badSectorToRead = (uint)badSector; @@ -180,6 +187,9 @@ namespace Aaru.Core.Devices.Dumping Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); _outputPlugin.WriteSectorLong(data, badSector); + WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, sub, badSector, 1, subLog, isrcs, + (byte)track.TrackSequence); + if(desiredSubchannel != MmcSubchannel.None) { if(supportedSubchannel == MmcSubchannel.Q16)