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

1016 lines
40 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 : Pregap.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
// Calculates CompactDisc track pregaps.
//
// --[ 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.IO;
2020-01-05 06:06:50 +00:00
using System.Linq;
2020-02-27 00:33:26 +00:00
using Aaru.Checksums;
2020-12-31 19:28:47 +00:00
using Aaru.CommonTypes;
2020-02-27 00:33:26 +00:00
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Structs;
using Aaru.Devices;
using Aaru.Logging;
2023-09-26 02:40:11 +01:00
using Humanizer;
using Humanizer.Bytes;
namespace Aaru.Core.Devices.Dumping;
2022-03-06 13:29:38 +00:00
partial class Dump
{
2022-03-06 13:29:38 +00:00
// TODO: Fix offset
/// <summary>Reads the first track pregap from a CompactDisc</summary>
/// <param name="blockSize">Block size in bytes</param>
/// <param name="currentSpeed">Current speed</param>
/// <param name="mediaTags">List of media tags</param>
/// <param name="supportedSubchannel">Subchannel the drive can read</param>
/// <param name="totalDuration">Total time spent sending commands to a drive</param>
void ReadCdFirstTrackPregap(uint blockSize, ref double currentSpeed, Dictionary<MediaTagType, byte[]> mediaTags,
MmcSubchannel supportedSubchannel, ref double totalDuration)
{
2023-10-03 22:57:50 +01:00
bool sense; // Sense indicator
byte[] cmdBuf; // Data buffer
double cmdDuration; // Command execution time
ulong sectorSpeedStart = 0; // Used to calculate correct speed
bool gotFirstTrackPregap = false;
int firstTrackPregapSectorsGood = 0;
2023-10-03 22:57:50 +01:00
var firstTrackPregapMs = new MemoryStream();
2022-03-06 13:29:38 +00:00
UpdateStatus?.Invoke(Localization.Core.Reading_first_track_pregap);
2022-03-06 13:29:38 +00:00
InitProgress?.Invoke();
_speedStopwatch.Restart();
2022-03-06 13:29:38 +00:00
2023-10-03 22:57:50 +01:00
for(int firstTrackPregapBlock = -150;
firstTrackPregapBlock < 0 && _resume.NextBlock == 0;
2022-03-06 13:29:38 +00:00
firstTrackPregapBlock++)
{
2022-03-06 13:29:38 +00:00
if(_aborted)
{
UpdateStatus?.Invoke(Localization.Core.Aborted);
2022-03-06 13:29:38 +00:00
break;
}
2023-09-26 02:40:11 +01:00
PulseProgress?.Invoke(string.Format(Localization.Core.Trying_to_read_first_track_pregap_sector_0_1,
firstTrackPregapBlock,
ByteSize.FromMegabytes(currentSpeed).Per(_oneSecond).Humanize()));
2022-03-06 13:29:38 +00:00
// ReSharper disable IntVariableOverflowInUncheckedContext
2024-05-01 04:05:22 +01:00
sense = _dev.ReadCd(out cmdBuf,
out _,
(uint)firstTrackPregapBlock,
blockSize,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
supportedSubchannel,
_dev.Timeout,
out cmdDuration);
2022-03-06 13:29:38 +00:00
// ReSharper restore IntVariableOverflowInUncheckedContext
2020-07-22 13:20:25 +01:00
if(!sense && !_dev.Error)
2022-03-06 13:29:38 +00:00
{
firstTrackPregapMs.Write(cmdBuf, 0, (int)blockSize);
gotFirstTrackPregap = true;
firstTrackPregapSectorsGood++;
totalDuration += cmdDuration;
}
else
{
// Write empty data
2024-05-01 04:05:22 +01:00
if(gotFirstTrackPregap) firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
sectorSpeedStart++;
double elapsed = _speedStopwatch.Elapsed.TotalSeconds;
2024-05-01 04:05:22 +01:00
if(elapsed <= 0) continue;
2022-03-06 13:29:38 +00:00
currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed);
sectorSpeedStart = 0;
_speedStopwatch.Restart();
2022-03-06 13:29:38 +00:00
}
_speedStopwatch.Stop();
2022-03-06 13:29:38 +00:00
if(firstTrackPregapSectorsGood > 0)
mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray());
2022-03-06 13:29:38 +00:00
EndProgress?.Invoke();
UpdateStatus?.Invoke(string.Format(Localization.Core.Got_0_first_track_pregap_sectors,
firstTrackPregapSectorsGood));
2022-03-06 13:29:38 +00:00
firstTrackPregapMs.Close();
}
/// <summary>Calculate track pregaps</summary>
/// <param name="dev">Device</param>
/// <param name="dumpLog">Dumping log</param>
/// <param name="updateStatus">Progress update callback</param>
/// <param name="tracks">List of tracks</param>
/// <param name="supportsPqSubchannel">Set if drive supports reading PQ subchannel</param>
/// <param name="supportsRwSubchannel">Set if drive supports reading RW subchannel</param>
/// <param name="dbDev">Database entry for device</param>
/// <param name="inexactPositioning">Set if we found the drive does not return the exact subchannel we requested</param>
/// <param name="dumping">Set if dumping, otherwise media info</param>
public static void SolveTrackPregaps(Device dev, UpdateStatusHandler updateStatus, Track[] tracks,
bool supportsPqSubchannel, bool supportsRwSubchannel,
2022-03-06 13:29:38 +00:00
Database.Models.Device dbDev, out bool inexactPositioning, bool dumping)
{
bool sense = true; // Sense indicator
byte[] subBuf = null;
int posQ;
uint retries;
bool? bcd = null;
byte[] crc;
Dictionary<uint, int> pregaps = new();
2022-03-06 13:29:38 +00:00
inexactPositioning = false;
2024-05-01 04:05:22 +01:00
if(!supportsPqSubchannel && !supportsRwSubchannel) return;
2022-03-06 13:29:38 +00:00
// Check if subchannel is BCD
for(retries = 0; retries < 10; retries++)
{
2023-10-03 22:57:50 +01:00
sense = supportsRwSubchannel
? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf, false)
2022-03-06 13:29:38 +00:00
: GetSectorForPregapQ16(dev, 11, out subBuf, false);
2024-05-01 04:05:22 +01:00
if(sense) continue;
2022-03-06 13:29:38 +00:00
bcd = (subBuf[9] & 0x10) > 0;
break;
}
2020-01-01 21:50:49 +00:00
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
bcd switch
{
true => Localization.Core.Subchannel_is_BCD,
false => Localization.Core.Subchannel_is_not_BCD,
_ => Localization.Core.Could_not_detect_drive_subchannel_BCD
});
2022-03-06 13:29:38 +00:00
if(bcd is null)
2020-01-01 21:50:49 +00:00
{
2024-05-01 04:05:22 +01:00
updateStatus?.Invoke(Localization.Core
.Could_not_detect_if_drive_subchannel_is_BCD_or_not_pregaps_could_not_be_calculated_dump_may_be_incorrect);
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
return;
}
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
// Initialize the dictionary
2024-05-01 04:05:22 +01:00
foreach(Track t in tracks) pregaps[t.Sequence] = 0;
2022-03-06 13:29:38 +00:00
for(int t = 0; t < tracks.Length; t++)
2022-03-06 13:29:38 +00:00
{
Track track = tracks[t];
int trackRetries = 0;
2022-03-06 13:29:38 +00:00
// First track of each session has at least 150 sectors of pregap and is not always readable
if(tracks.Where(trk => trk.Session == track.Session).MinBy(trk => trk.Sequence).Sequence == track.Sequence)
2022-03-06 13:29:38 +00:00
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME, Localization.Core.Skipping_track_0, track.Sequence);
2020-01-05 06:06:50 +00:00
2024-05-01 04:05:22 +01:00
if(track.Sequence > 1) pregaps[track.Sequence] = 150;
2022-03-06 13:29:38 +00:00
continue;
}
if(t > 0 && tracks[t - 1].Type == tracks[t].Type && dumping)
2020-01-05 06:06:50 +00:00
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME, Localization.Core.Skipping_track_0, track.Sequence);
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
continue;
}
if(dumping && dev.Manufacturer.ToLowerInvariant().StartsWith("plextor", StringComparison.Ordinal))
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core.Skipping_track_0_due_to_Plextor_firmware_bug,
track.Sequence);
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
continue;
2020-01-05 06:06:50 +00:00
}
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME, Localization.Core.Track_0, track.Sequence);
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
int lba = (int)track.StartSector - 1;
bool pregapFound = false;
2022-03-06 13:29:38 +00:00
Track previousTrack = tracks.FirstOrDefault(trk => trk.Sequence == track.Sequence - 1);
bool goneBack = false;
bool goFront = false;
bool forward = false;
bool crcOk = false;
bool previousPregapIsPreviousTrack = false;
2022-03-06 13:29:38 +00:00
// Check if pregap is 0
for(retries = 0; retries < 10 && !pregapFound; retries++)
2020-01-05 06:06:50 +00:00
{
2022-03-06 13:29:38 +00:00
sense = supportsRwSubchannel
2022-03-07 07:36:44 +00:00
? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, track.Type == TrackType.Audio)
: GetSectorForPregapQ16(dev, (uint)lba, out subBuf, track.Type == TrackType.Audio);
2022-03-06 13:29:38 +00:00
if(sense)
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core.LBA_0_Try_1_Sense_2,
lba,
retries + 1,
sense);
2020-01-05 06:06:50 +00:00
continue;
}
2024-05-01 04:05:22 +01:00
if(bcd == false) BinaryToBcdQ(subBuf);
2022-03-06 13:29:38 +00:00
2023-10-05 02:31:59 +01:00
CRC16CcittContext.Data(subBuf, 10, out crc);
2022-03-06 13:29:38 +00:00
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core
.LBA_0_Try_1_Sense_2_Q_3_4_5_6_7_8_9_10_11_12_CRC_13_14_Calculated_CRC_15_16,
lba,
retries + 1,
sense,
subBuf[0],
subBuf[1],
subBuf[2],
subBuf[3],
subBuf[4],
subBuf[5],
subBuf[6],
subBuf[7],
subBuf[8],
subBuf[9],
subBuf[10],
subBuf[11],
crc[0],
crc[1]);
2022-03-06 13:29:38 +00:00
crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];
// Try to do a simple correction
if(!crcOk)
{
2022-03-06 13:29:38 +00:00
// Data track cannot have 11xxb in CONTROL
2024-05-01 04:05:22 +01:00
if((subBuf[0] & 0x40) > 0) subBuf[0] &= 0x7F;
2022-03-06 13:29:38 +00:00
// ADR only uses two bits
subBuf[0] &= 0xF3;
// Don't care about other Q modes
if((subBuf[0] & 0xF) == 1)
{
// ZERO only used in DDCD
subBuf[6] = 0;
// Fix BCD numbering
for(int i = 1; i < 10; i++)
2022-03-06 13:29:38 +00:00
{
2024-05-01 04:05:22 +01:00
if((subBuf[i] & 0xF0) > 0xA0) subBuf[i] &= 0x7F;
2022-03-06 13:29:38 +00:00
2024-05-01 04:05:22 +01:00
if((subBuf[i] & 0x0F) > 0x0A) subBuf[i] &= 0xF7;
2022-03-06 13:29:38 +00:00
}
}
2023-10-05 02:31:59 +01:00
CRC16CcittContext.Data(subBuf, 10, out crc);
2022-03-06 13:29:38 +00:00
crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];
if(crcOk)
2023-10-03 22:57:50 +01:00
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core
.LBA_0_Try_1_Sense_2_Q_FIXED_3_4_5_6_7_8_9_10_11_12_CRC_13_14_Calculated_CRC_15_16,
lba,
retries + 1,
sense,
subBuf[0],
subBuf[1],
subBuf[2],
subBuf[3],
subBuf[4],
subBuf[5],
subBuf[6],
subBuf[7],
subBuf[8],
subBuf[9],
subBuf[10],
subBuf[11],
crc[0],
crc[1]);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
else
continue;
}
2022-03-06 13:29:38 +00:00
BcdToBinaryQ(subBuf);
2022-03-06 13:29:38 +00:00
// Q position
2024-05-01 04:05:22 +01:00
if((subBuf[0] & 0xF) != 1) continue;
2023-10-03 22:57:50 +01:00
posQ = subBuf[7] * 60 * 75 + subBuf[8] * 75 + subBuf[9] - 150;
2020-01-05 06:06:50 +00:00
2024-05-01 04:05:22 +01:00
if(subBuf[1] != track.Sequence - 1 || subBuf[2] == 0 || posQ != lba) break;
2022-03-06 13:29:38 +00:00
pregaps[track.Sequence] = 0;
pregapFound = true;
}
2020-01-05 06:06:50 +00:00
2024-05-01 04:05:22 +01:00
if(pregapFound) continue;
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
// Calculate pregap
lba = (int)track.StartSector - 150;
while(lba > (int)previousTrack.StartSector && lba <= (int)track.StartSector)
2022-03-06 13:29:38 +00:00
{
// Some drives crash if you try to read just before the previous read, so seek away first
if(!forward)
2023-10-03 22:57:50 +01:00
{
2022-03-06 13:29:38 +00:00
sense = supportsRwSubchannel
2024-05-01 04:05:22 +01:00
? GetSectorForPregapRaw(dev,
(uint)lba - 10,
dbDev,
out subBuf,
2022-03-06 13:29:38 +00:00
track.Type == TrackType.Audio)
2022-03-07 07:36:44 +00:00
: GetSectorForPregapQ16(dev, (uint)lba - 10, out subBuf, track.Type == TrackType.Audio);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
for(retries = 0; retries < 10; retries++)
2020-01-03 16:11:53 +00:00
{
sense = supportsRwSubchannel
2024-05-01 04:05:22 +01:00
? GetSectorForPregapRaw(dev,
(uint)lba,
dbDev,
out subBuf,
2021-09-14 21:18:28 +01:00
track.Type == TrackType.Audio)
2022-03-07 07:36:44 +00:00
: GetSectorForPregapQ16(dev, (uint)lba, out subBuf, track.Type == TrackType.Audio);
2020-01-03 16:11:53 +00:00
2024-05-01 04:05:22 +01:00
if(sense) continue;
2020-01-03 16:11:53 +00:00
2024-05-01 04:05:22 +01:00
if(bcd == false) BinaryToBcdQ(subBuf);
2020-01-03 16:11:53 +00:00
2023-10-05 02:31:59 +01:00
CRC16CcittContext.Data(subBuf, 10, out crc);
2020-01-03 16:11:53 +00:00
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core
.LBA_0_Try_1_Sense_2_Q_3_4_5_6_7_8_9_10_11_12_CRC_13_14_Calculated_CRC_15_16,
lba,
retries + 1,
sense,
subBuf[0],
subBuf[1],
subBuf[2],
subBuf[3],
subBuf[4],
subBuf[5],
subBuf[6],
subBuf[7],
subBuf[8],
subBuf[9],
subBuf[10],
subBuf[11],
crc[0],
crc[1]);
crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];
// Try to do a simple correction
if(!crcOk)
{
// Data track cannot have 11xxb in CONTROL
2024-05-01 04:05:22 +01:00
if((subBuf[0] & 0x40) > 0) subBuf[0] &= 0x7F;
// ADR only uses two bits
subBuf[0] &= 0xF3;
// Don't care about other Q modes
if((subBuf[0] & 0xF) == 1)
{
// ZERO only used in DDCD
subBuf[6] = 0;
// Fix BCD numbering
for(int i = 1; i < 10; i++)
{
2024-05-01 04:05:22 +01:00
if((subBuf[i] & 0xF0) > 0xA0) subBuf[i] &= 0x7F;
2024-05-01 04:05:22 +01:00
if((subBuf[i] & 0x0F) > 0x0A) subBuf[i] &= 0xF7;
}
}
2023-10-05 02:31:59 +01:00
CRC16CcittContext.Data(subBuf, 10, out crc);
crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11];
if(crcOk)
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core
.LBA_0_Try_1_Sense_2_Q_FIXED_3_4_5_6_7_8_9_10_11_12_CRC_13_14_Calculated_CRC_15_16,
lba,
retries + 1,
sense,
subBuf[0],
subBuf[1],
subBuf[2],
subBuf[3],
subBuf[4],
subBuf[5],
subBuf[6],
subBuf[7],
subBuf[8],
subBuf[9],
subBuf[10],
subBuf[11],
crc[0],
crc[1]);
2022-03-06 13:29:38 +00:00
break;
}
}
2020-01-03 16:11:53 +00:00
2024-05-01 04:05:22 +01:00
if(crcOk) break;
2020-01-03 16:11:53 +00:00
}
2022-03-06 13:29:38 +00:00
if(retries == 10)
2020-01-01 21:50:49 +00:00
{
2022-03-06 13:29:38 +00:00
if(sense)
2020-01-01 21:50:49 +00:00
{
2022-03-06 13:29:38 +00:00
trackRetries++;
2022-03-06 13:29:38 +00:00
if(trackRetries >= 10)
{
2022-03-06 13:29:38 +00:00
if(pregaps[track.Sequence] == 0)
{
2023-10-03 22:57:50 +01:00
if(previousTrack.Type == TrackType.Audio && track.Type != TrackType.Audio ||
previousTrack.Type != TrackType.Audio && track.Type == TrackType.Audio)
{
2024-05-01 04:05:22 +01:00
updateStatus?.Invoke(Localization.Core
.Could_not_read_subchannel_for_this_track_supposing_hundred_fifty_sectors);
}
else
{
2024-05-01 04:05:22 +01:00
updateStatus?.Invoke(Localization.Core
.Could_not_read_subchannel_for_this_track_supposing_zero_sectors);
}
}
2022-03-06 13:29:38 +00:00
else
{
2024-05-01 04:05:22 +01:00
updateStatus?.Invoke(string.Format(Localization.Core
.Could_not_read_subchannel_for_this_track_supposing_0_sectors,
pregaps[track.Sequence]));
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
break;
}
updateStatus?.Invoke(string.Format(Localization.Core.Could_not_read_subchannel_for_sector_0,
lba));
2022-03-06 13:29:38 +00:00
lba++;
forward = true;
2022-03-06 13:29:38 +00:00
continue;
}
updateStatus?.Invoke(string.Format(Localization.Core.Could_not_get_correct_subchannel_for_sector_0,
lba));
2022-03-06 13:29:38 +00:00
}
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
if(subBuf.All(b => b == 0))
{
inexactPositioning = true;
2020-01-01 21:50:49 +00:00
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME, Localization.Core.All_Q_empty_for_LBA_0, lba);
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
break;
}
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
BcdToBinaryQ(subBuf);
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
// If it's not Q position
if((subBuf[0] & 0xF) != 1)
{
// This means we already searched back, so search forward
if(goFront)
{
lba++;
2022-03-06 13:29:38 +00:00
forward = true;
2020-01-05 06:06:50 +00:00
2024-05-01 04:05:22 +01:00
if(lba == (int)previousTrack.StartSector) break;
2020-01-05 06:06:50 +00:00
continue;
2020-01-01 21:50:49 +00:00
}
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
// Search back
goneBack = true;
lba--;
forward = false;
2020-01-10 02:36:17 +00:00
2022-03-06 13:29:38 +00:00
continue;
}
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
// Previous track
if(subBuf[1] < track.Sequence)
{
lba++;
forward = true;
previousPregapIsPreviousTrack = true;
2020-01-10 02:36:17 +00:00
2022-03-06 13:29:38 +00:00
// Already gone back, so go forward
2024-05-01 04:05:22 +01:00
if(goneBack) goFront = true;
2022-03-06 13:29:38 +00:00
continue;
}
2022-03-06 13:29:38 +00:00
// Same track, but not pregap
if(subBuf[1] == track.Sequence && subBuf[2] > 0)
2022-03-06 13:29:38 +00:00
{
lba--;
forward = false;
2024-05-01 04:05:22 +01:00
if(previousPregapIsPreviousTrack) break;
2020-03-06 19:12:09 +00:00
2022-03-06 13:29:38 +00:00
continue;
}
2020-03-06 19:12:09 +00:00
2022-03-06 13:29:38 +00:00
previousPregapIsPreviousTrack = false;
2020-03-06 19:12:09 +00:00
2022-03-06 13:29:38 +00:00
// Pregap according to Q position
2023-10-03 22:57:50 +01:00
posQ = subBuf[7] * 60 * 75 + subBuf[8] * 75 + subBuf[9] - 150;
2022-03-06 13:29:38 +00:00
int diff = posQ - lba;
int pregapQ = (int)track.StartSector - lba;
2022-03-06 13:29:38 +00:00
if(diff != 0)
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core.Invalid_Q_position_for_LBA_0_got_1,
lba,
posQ);
2022-03-06 13:29:38 +00:00
inexactPositioning = true;
}
2022-03-06 13:29:38 +00:00
// Received a Q post the LBA we wanted, just go back. If we are already going forward, break
if(posQ > lba)
{
2024-05-01 04:05:22 +01:00
if(forward) break;
lba--;
2020-01-05 06:06:50 +00:00
continue;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
// Bigger than known change, otherwise we found it
if(pregapQ > pregaps[track.Sequence])
{
2022-03-06 13:29:38 +00:00
// If CRC is not OK, only accept pregaps less than 10 sectors longer than previously now
if(crcOk || pregapQ - pregaps[track.Sequence] < 10)
{
2025-08-17 06:11:22 +01:00
AaruLogging.Debug(PREGAP_MODULE_NAME,
Localization.Core.Pregap_for_track_0_1,
track.Sequence,
pregapQ);
2022-03-06 13:29:38 +00:00
pregaps[track.Sequence] = pregapQ;
}
2022-03-06 13:29:38 +00:00
// We are going forward, so we have already been in the previous track, so add 1 to pregap and get out of here
else if(forward)
{
2022-03-06 13:29:38 +00:00
pregaps[track.Sequence]++;
break;
}
}
2024-05-01 04:05:22 +01:00
else if(pregapQ == pregaps[track.Sequence]) break;
2022-03-06 13:29:38 +00:00
lba--;
forward = false;
2020-01-05 06:06:50 +00:00
}
}
2020-01-01 21:50:49 +00:00
2022-03-06 13:29:38 +00:00
foreach(Track trk in tracks)
2020-01-05 06:06:50 +00:00
{
2022-03-06 13:29:38 +00:00
trk.Pregap = (ulong)pregaps[trk.Sequence];
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
// Do not reduce pregap, or starting position of session's first track
2024-05-01 04:05:22 +01:00
if(tracks.Where(t => t.Session == trk.Session).MinBy(t => t.Sequence).Sequence == trk.Sequence) continue;
2022-03-06 13:29:38 +00:00
if(dumping)
{
2022-03-06 13:29:38 +00:00
// Minus five, to ensure dumping will fix if there is a pregap LBA 0
int red = 5;
2020-01-05 06:06:50 +00:00
while(trk.Pregap > 0 && red > 0)
2022-03-06 13:29:38 +00:00
{
trk.Pregap--;
red--;
}
}
2022-03-06 13:29:38 +00:00
trk.StartSector -= trk.Pregap;
2024-05-01 04:05:22 +01:00
#if DEBUG
updateStatus?.Invoke(string.Format(Localization.Core.Track_0_pregap_is_1_sectors,
trk.Sequence,
trk.Pregap));
2024-05-01 04:05:22 +01:00
#endif
2022-03-06 13:29:38 +00:00
}
}
/// <summary>Reads a RAW subchannel sector for pregap calculation</summary>
/// <param name="dev">Device</param>
/// <param name="lba">LBA</param>
/// <param name="dbDev">Database entry for device</param>
/// <param name="subBuf">Read subchannel</param>
/// <param name="audioTrack">Set if it is an audio track</param>
/// <returns><c>true</c> if read correctly, <c>false</c> otherwise</returns>
static bool GetSectorForPregapRaw(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf,
2023-10-03 22:57:50 +01:00
bool audioTrack)
2022-03-06 13:29:38 +00:00
{
byte[] cmdBuf;
bool sense;
subBuf = null;
if(audioTrack)
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2448,
1,
MmcSectorTypes.Cdda,
false,
false,
false,
MmcHeaderCodes.None,
true,
false,
MmcErrorField.None,
MmcSubchannel.Raw,
dev.Timeout,
2022-03-06 13:29:38 +00:00
out _);
if(sense)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2448,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
MmcSubchannel.Raw,
dev.Timeout,
out _);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
}
else
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2448,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
MmcSubchannel.Raw,
dev.Timeout,
out _);
2022-03-06 13:29:38 +00:00
if(sense)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2448,
1,
MmcSectorTypes.Cdda,
false,
false,
false,
MmcHeaderCodes.None,
true,
false,
MmcErrorField.None,
MmcSubchannel.Raw,
dev.Timeout,
2022-03-07 07:36:44 +00:00
out _);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(!sense)
{
byte[] tmpBuf = new byte[96];
2022-03-06 13:29:38 +00:00
Array.Copy(cmdBuf, 2352, tmpBuf, 0, 96);
subBuf = DeinterleaveQ(tmpBuf);
}
else
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
96,
1,
MmcSectorTypes.AllTypes,
false,
false,
false,
MmcHeaderCodes.None,
false,
false,
MmcErrorField.None,
MmcSubchannel.Raw,
dev.Timeout,
2022-03-07 07:36:44 +00:00
out _);
2022-03-06 13:29:38 +00:00
if(sense)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
96,
1,
MmcSectorTypes.Cdda,
false,
false,
false,
MmcHeaderCodes.None,
false,
false,
MmcErrorField.None,
MmcSubchannel.Raw,
dev.Timeout,
out _);
2023-10-03 22:57:50 +01:00
}
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
if(!sense)
subBuf = DeinterleaveQ(cmdBuf);
else if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true ||
dev.Manufacturer.Equals("plextor", StringComparison.InvariantCultureIgnoreCase))
2022-03-07 07:36:44 +00:00
sense = dev.PlextorReadCdDa(out cmdBuf, out _, lba, 96, 1, PlextorSubchannel.All, dev.Timeout, out _);
2020-01-05 06:06:50 +00:00
2022-03-06 13:29:38 +00:00
{
2024-05-01 04:05:22 +01:00
if(!sense) subBuf = DeinterleaveQ(cmdBuf);
2020-01-01 21:50:49 +00:00
}
}
2022-03-06 13:29:38 +00:00
return sense;
}
2022-03-06 13:29:38 +00:00
/// <summary>Reads a Q16 subchannel sector for pregap calculation</summary>
/// <param name="dev">Device</param>
/// <param name="lba">LBA</param>
/// <param name="subBuf">Read subchannel</param>
/// <param name="audioTrack">Set if it is an audio track</param>
/// <returns><c>true</c> if read correctly, <c>false</c> otherwise</returns>
static bool GetSectorForPregapQ16(Device dev, uint lba, out byte[] subBuf, bool audioTrack)
{
byte[] cmdBuf;
bool sense;
subBuf = null;
2022-03-06 13:29:38 +00:00
if(audioTrack)
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2368,
1,
MmcSectorTypes.Cdda,
false,
false,
false,
MmcHeaderCodes.None,
true,
false,
MmcErrorField.None,
MmcSubchannel.Q16,
dev.Timeout,
2022-03-06 13:29:38 +00:00
out _);
if(sense)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2368,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
MmcSubchannel.Q16,
dev.Timeout,
out _);
2023-10-03 22:57:50 +01:00
}
2022-03-06 13:29:38 +00:00
}
else
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2368,
1,
MmcSectorTypes.AllTypes,
false,
false,
true,
MmcHeaderCodes.AllHeaders,
true,
true,
MmcErrorField.None,
MmcSubchannel.Q16,
dev.Timeout,
out _);
2022-03-06 13:29:38 +00:00
if(sense)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
2368,
1,
MmcSectorTypes.Cdda,
false,
false,
false,
MmcHeaderCodes.None,
true,
false,
MmcErrorField.None,
MmcSubchannel.Q16,
dev.Timeout,
2022-03-07 07:36:44 +00:00
out _);
2023-10-03 22:57:50 +01:00
}
}
2022-03-06 13:29:38 +00:00
if(!sense)
{
2022-03-06 13:29:38 +00:00
subBuf = new byte[16];
Array.Copy(cmdBuf, 2352, subBuf, 0, 16);
}
else
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
16,
1,
MmcSectorTypes.AllTypes,
false,
false,
false,
MmcHeaderCodes.None,
false,
false,
MmcErrorField.None,
MmcSubchannel.Q16,
dev.Timeout,
2022-03-07 07:36:44 +00:00
out _);
2022-03-06 13:29:38 +00:00
if(sense)
2023-10-03 22:57:50 +01:00
{
2024-05-01 04:05:22 +01:00
sense = dev.ReadCd(out cmdBuf,
out _,
lba,
16,
1,
MmcSectorTypes.Cdda,
false,
false,
false,
MmcHeaderCodes.None,
false,
false,
MmcErrorField.None,
MmcSubchannel.Q16,
dev.Timeout,
out _);
2023-10-03 22:57:50 +01:00
}
2024-05-01 04:05:22 +01:00
if(!sense) subBuf = cmdBuf;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
return sense;
}
2022-03-06 13:29:38 +00:00
/// <summary>De-interleaves Q subchannel</summary>
/// <param name="subchannel">Interleaved subchannel</param>
/// <returns>De-interleaved Q subchannel</returns>
static byte[] DeinterleaveQ(byte[] subchannel)
{
int[] q = new int[subchannel.Length / 8];
2022-03-06 13:29:38 +00:00
// De-interlace Q subchannel
for(int iq = 0; iq < subchannel.Length; iq += 8)
2020-01-05 06:06:50 +00:00
{
2022-03-06 13:29:38 +00:00
q[iq / 8] = (subchannel[iq] & 0x40) << 1;
q[iq / 8] += subchannel[iq + 1] & 0x40;
q[iq / 8] += (subchannel[iq + 2] & 0x40) >> 1;
q[iq / 8] += (subchannel[iq + 3] & 0x40) >> 2;
q[iq / 8] += (subchannel[iq + 4] & 0x40) >> 3;
q[iq / 8] += (subchannel[iq + 5] & 0x40) >> 4;
q[iq / 8] += (subchannel[iq + 6] & 0x40) >> 5;
q[iq / 8] += (subchannel[iq + 7] & 0x40) >> 6;
2020-01-05 06:06:50 +00:00
}
byte[] deQ = new byte[q.Length];
2022-03-06 13:29:38 +00:00
for(int iq = 0; iq < q.Length; iq++) deQ[iq] = (byte)q[iq];
2022-03-06 13:29:38 +00:00
return deQ;
}
/// <summary>In place converts Q subchannel from binary to BCD numbering</summary>
/// <param name="q">Q subchannel</param>
static void BinaryToBcdQ(byte[] q)
{
2023-10-03 22:57:50 +01:00
q[1] = (byte)((q[1] / 10 << 4) + q[1] % 10);
q[2] = (byte)((q[2] / 10 << 4) + q[2] % 10);
q[3] = (byte)((q[3] / 10 << 4) + q[3] % 10);
q[4] = (byte)((q[4] / 10 << 4) + q[4] % 10);
q[5] = (byte)((q[5] / 10 << 4) + q[5] % 10);
q[6] = (byte)((q[6] / 10 << 4) + q[6] % 10);
q[7] = (byte)((q[7] / 10 << 4) + q[7] % 10);
q[8] = (byte)((q[8] / 10 << 4) + q[8] % 10);
q[9] = (byte)((q[9] / 10 << 4) + q[9] % 10);
2022-03-06 13:29:38 +00:00
}
/// <summary>In place converts Q subchannel from BCD to binary numbering</summary>
/// <param name="q">Q subchannel</param>
static void BcdToBinaryQ(byte[] q)
{
2023-10-03 22:57:50 +01:00
q[1] = (byte)(q[1] / 16 * 10 + (q[1] & 0x0F));
q[2] = (byte)(q[2] / 16 * 10 + (q[2] & 0x0F));
q[3] = (byte)(q[3] / 16 * 10 + (q[3] & 0x0F));
q[4] = (byte)(q[4] / 16 * 10 + (q[4] & 0x0F));
q[5] = (byte)(q[5] / 16 * 10 + (q[5] & 0x0F));
q[6] = (byte)(q[6] / 16 * 10 + (q[6] & 0x0F));
q[7] = (byte)(q[7] / 16 * 10 + (q[7] & 0x0F));
q[8] = (byte)(q[8] / 16 * 10 + (q[8] & 0x0F));
q[9] = (byte)(q[9] / 16 * 10 + (q[9] & 0x0F));
}
}