More work on CUERipper

This commit is contained in:
chudov
2008-12-06 05:44:14 +00:00
parent a026df67a9
commit 764f9f2368
9 changed files with 635 additions and 185 deletions

View File

@@ -29,7 +29,7 @@ using System.Diagnostics;
namespace Bwg.Scsi
{
class Command : IDisposable
public class Command : IDisposable
{
[DllImport("ntdll.dll")]
internal static extern void RtlZeroMemory(IntPtr dest, int size);

View File

@@ -23,7 +23,7 @@
namespace Bwg.Scsi
{
enum ScsiCommandCode
public enum ScsiCommandCode
{
TestUnitReady = 0x00,
RequestSense = 0x03,

View File

@@ -939,7 +939,7 @@ namespace Bwg.Scsi
}
#endregion
private CommandStatus SendCommand(Command cmd)
public CommandStatus SendCommand(Command cmd)
{
return (m_ossize == 32) ? SendCommand32(cmd) : SendCommand64(cmd);
}
@@ -1971,7 +1971,7 @@ namespace Bwg.Scsi
/// <param name="data">The buffer to receive the data</param>
/// <param name="size">The size of the buffer given by the data parameter</param>
/// <returns></returns>
public CommandStatus Read(bool force, bool streaming, uint lba, uint length, IntPtr data, ushort size)
public CommandStatus Read(bool force, bool streaming, uint lba, uint length, IntPtr data, int size)
{
if (m_logger != null)
{
@@ -2134,15 +2134,16 @@ namespace Bwg.Scsi
/// <summary>
///
/// </summary>
/// <param name="mode">subchannel mode</param>
/// <param name="exp"></param>
/// <param name="mode">subchannel mode (+16 bytes if equals 2)</param>
/// <param name="c2">report C2 errors (+296 bytes if true)</param>
/// <param name="exp">expected sector type</param>
/// <param name="dap"></param>
/// <param name="start"></param>
/// <param name="length"></param>
/// <param name="data">the memory area </param>
/// <param name="size">the size of the memory area given by the data parameter</param>
/// <returns></returns>
public CommandStatus ReadCDAndSubChannel(byte mode, byte exp, bool dap, uint start, uint length, IntPtr data, int size)
public CommandStatus ReadCDAndSubChannel(byte mode, bool c2, byte exp, bool dap, uint start, uint length, IntPtr data, int size)
{
if (m_logger != null)
{
@@ -2161,10 +2162,13 @@ namespace Bwg.Scsi
byte b = (byte)((exp & 0x07) << 2);
if (dap)
b |= 0x02;
byte byte9 = 0x10;
if (c2)
byte9 |= 0x02;
cmd.SetCDB8(1, b);
cmd.SetCDB32(2, start);
cmd.SetCDB24(6, length);
cmd.SetCDB8(9, 0x10); // User data only
cmd.SetCDB8(9, byte9); // User data + possibly c2 errors
cmd.SetCDB8(10, mode); // Subchannel
CommandStatus st = SendCommand(cmd);

View File

@@ -52,12 +52,13 @@ namespace CUETools.CDImage
public class CDTrack : ICloneable
{
public CDTrack(uint number, uint start, uint length, bool isAudio)
public CDTrack(uint number, uint start, uint length, bool isAudio, bool preEmpasis)
{
_number = number;
_start = start;
_length = length;
_isAudio = isAudio;
_preEmphasis = preEmpasis;
_indexes = new List<CDTrackIndex>();
_indexes.Add(new CDTrackIndex(0, start));
_indexes.Add(new CDTrackIndex(1, start));
@@ -69,6 +70,7 @@ namespace CUETools.CDImage
_start = src._start;
_length = src._length;
_isAudio = src._isAudio;
_preEmphasis = src._preEmphasis;
_indexes = new List<CDTrackIndex>();
for (int i = 0; i < src._indexes.Count; i++)
_indexes.Add(new CDTrackIndex(src._indexes[i]));
@@ -187,6 +189,18 @@ namespace CUETools.CDImage
}
}
public bool PreEmphasis
{
get
{
return _preEmphasis;
}
set
{
_preEmphasis = value;
}
}
public void AddIndex(CDTrackIndex index)
{
if (index.Index < 2)
@@ -198,6 +212,7 @@ namespace CUETools.CDImage
IList<CDTrackIndex> _indexes;
string _isrc;
bool _isAudio;
bool _preEmphasis;
uint _start;
uint _length;
uint _number;

View File

@@ -27,13 +27,60 @@ using CUETools.Ripper.SCSI;
using CUETools.Codecs;
using CUETools.CDImage;
using CUETools.AccurateRip;
using FLACDotNet;
//using FLACDotNet;
using MusicBrainz;
namespace CUERipper
{
class ProgressMeter
{
public DateTime realStart;
public ProgressMeter()
{
realStart = DateTime.Now;
//TimeSpan lastPrint = TimeSpan.FromMilliseconds(0);
}
public void ReadProgress(object sender, ReadProgressArgs e)
{
CDDriveReader audioSource = (CDDriveReader)sender;
int processed = e.Position - e.PassStart;
TimeSpan elapsed = DateTime.Now - e.PassTime;
double speed = elapsed.TotalSeconds > 0 ? processed / elapsed.TotalSeconds / 75 : 1.0;
TimeSpan totalElapsed = DateTime.Now - realStart;
TimeSpan totalEstimated = TimeSpan.FromMilliseconds(totalElapsed.TotalMilliseconds / Math.Max(1, (e.PassStart + (processed + e.Pass * (e.PassEnd - e.PassStart)) / 16)) * audioSource.TOC.AudioLength);
// if ((elapsed - lastPrint).TotalMilliseconds > 60) ;
Console.Write("\r{9} : {0:00}%; {1:00.00}x; {2} ({10:0.00}%) errors; {3:d2}:{4:d2}:{5:d2}/{6:d2}:{7:d2}:{8:d2} ",
100.0 * e.Position / audioSource.TOC.AudioLength,
speed,
e.ErrorsCount,
totalElapsed.Hours, totalElapsed.Minutes, totalElapsed.Seconds,
totalEstimated.Hours, totalEstimated.Minutes, totalEstimated.Seconds,
e.Pass < 1 ? "Progress " : string.Format("Retry {0:00} ", e.Pass),
processed > 0 ? 100.0 * e.ErrorsCount / processed / (4 * 588) : 0
);
//lastPrint = elapsed;
}
}
class Program
{
static void Usage()
{
string drives = "";
char[] drivesAvailable = CDDriveReader.DrivesAvailable();
for (int i = 0; i < drivesAvailable.Length; i++)
drives += string.Format("{0}: ", drivesAvailable[i]);
Console.WriteLine("Usage : CUERipper.exe <options>");
Console.WriteLine();
//Console.WriteLine("-S, --secure secure mode, read each block twice;");
Console.WriteLine("-P, --paranoid maximum level of error correction;");
Console.WriteLine("-D, --drive <letter> use a specific CD drive, e.g. {0};", drives);
Console.WriteLine("-O, --offset <samples> use specific drive read offset;");
}
static void Main(string[] args)
{
string programVersion = "CUERipper v1.9.3 Copyright (C) 2008 Gregory S. Chudov";
@@ -42,25 +89,65 @@ namespace CUERipper
Console.WriteLine("This is free software under the GNU GPLv3+ license; There is NO WARRANTY, to");
Console.WriteLine("the extent permitted by law. <http://www.gnu.org/licenses/> for details.");
char[] drives = CDDriveReader.DrivesAvailable();
if (drives.Length < 1)
int correctionQuality = 1;
string driveLetter = null;
int driveOffset = 0;
for (int arg = 0; arg < args.Length; arg++)
{
Console.WriteLine("No CD drives found.");
return;
bool ok = true;
if (args[arg] == "-P" || args[arg] == "--paranoid")
correctionQuality = 4;
//else if (args[arg] == "-B" || args[arg] == "--burst")
// correctionQuality = 1;
else if ((args[arg] == "-D" || args[arg] == "--drive") && ++arg < args.Length)
driveLetter = args[arg];
else if ((args[arg] == "-O" || args[arg] == "--offset") && ++arg < args.Length)
ok = int.TryParse(args[arg], out driveOffset);
else
ok = false;
if (!ok)
{
Usage();
return;
}
}
char driveLetter = drives[0];
char[] drives;
if (driveLetter == null || driveLetter.Length < 1)
{
drives = CDDriveReader.DrivesAvailable();
if (drives.Length < 1)
{
Console.WriteLine("No CD drives found.");
return;
}
}
else
{
drives = new char[1];
drives[0] = driveLetter[0];
}
#if !DEBUG
try
#endif
{
CDDriveReader audioSource = new CDDriveReader();
audioSource.Open(driveLetter);
int driveOffset;
if (!AccurateRipVerify.FindDriveReadOffset(audioSource.ARName, out driveOffset))
throw new Exception("Failed to find drive read offset for drive" + audioSource.ARName);
audioSource.Open(drives[0]);
if (audioSource.TOC.AudioTracks < 1)
{
Console.WriteLine("{0}: CD does not contain any audio tracks.", audioSource.Path);
audioSource.Close();
return;
}
if (driveOffset == 0)
if (!AccurateRipVerify.FindDriveReadOffset(audioSource.ARName, out driveOffset))
Console.WriteLine("Unknown read offset for drive {0}!!!", audioSource.Path);
//throw new Exception("Failed to find drive read offset for drive" + audioSource.ARName);
audioSource.DriveOffset = driveOffset;
audioSource.CorrectionQuality = correctionQuality;
//bool toStdout = false;
AccurateRipVerify arVerify = new AccurateRipVerify(audioSource.TOC);
int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount];
string CDDBId = AccurateRipVerify.CalculateCDDBId(audioSource.TOC);
@@ -81,7 +168,8 @@ namespace CUERipper
release = null;
}
string destFile = (release == null) ? "cdimage.flac" : release.GetArtist() + " - " + release.GetTitle() + ".flac";
//string destFile = (release == null) ? "cdimage.flac" : release.GetArtist() + " - " + release.GetTitle() + ".flac";
string destFile = (release == null) ? "cdimage.wav" : release.GetArtist() + " - " + release.GetTitle() + ".wav";
Console.WriteLine("Drive : {0}", audioSource.Path);
Console.WriteLine("Read offset : {0}", audioSource.DriveOffset);
@@ -90,48 +178,53 @@ namespace CUERipper
Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus);
Console.WriteLine("MusicBrainz : {0}", release == null ? "not found" : release.GetArtist() + " - " + release.GetTitle());
IAudioDest audioDest = new FLACWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate);
//IAudioDest audioDest = new FLACWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate);
IAudioDest audioDest = new WAVWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate, null);
audioDest.FinalSampleCount = (long)audioSource.Length;
DateTime start = DateTime.Now;
TimeSpan lastPrint = TimeSpan.FromMilliseconds(0);
ProgressMeter meter = new ProgressMeter();
audioSource.ReadProgress += new EventHandler<ReadProgressArgs>(meter.ReadProgress);
do
{
uint samplesRead = audioSource.Read(buff, Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining));
uint toRead = Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining);
uint samplesRead = audioSource.Read(buff, toRead);
if (samplesRead == 0) break;
if (samplesRead != toRead)
throw new Exception("samples read != samples requested");
arVerify.Write(buff, samplesRead);
audioDest.Write(buff, samplesRead);
TimeSpan elapsed = DateTime.Now - start;
if ((elapsed - lastPrint).TotalMilliseconds > 60)
{
Console.Write("\rProgress : {0:00}%; {1:0.00}x; {2}/{3}",
100.0 * audioSource.Position / audioSource.Length,
audioSource.Position / elapsed.TotalSeconds / audioSource.SampleRate,
elapsed,
TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / audioSource.Position * audioSource.Length)
);
lastPrint = elapsed;
}
} while (true);
TimeSpan totalElapsed = DateTime.Now - start;
Console.Write("\r \r");
Console.WriteLine("Results : {0:0.00}x; {1}",
TimeSpan totalElapsed = DateTime.Now - meter.realStart;
Console.Write("\r \r");
Console.WriteLine("Results : {0:0.00}x; {1:d5} errors; {2:d2}:{3:d2}:{4:d2}",
audioSource.Length / totalElapsed.TotalSeconds / audioSource.SampleRate,
totalElapsed
audioSource.ErrorsCount,
totalElapsed.Hours, totalElapsed.Minutes, totalElapsed.Seconds
);
audioDest.Close();
StringWriter logWriter = new StringWriter();
logWriter.WriteLine("{0}", programVersion);
logWriter.WriteLine();
logWriter.WriteLine("Extraction logfile from {0}", DateTime.Now);
logWriter.WriteLine();
logWriter.WriteLine("Used drive : {0}", audioSource.Path);
logWriter.WriteLine();
logWriter.WriteLine("Read offset correction : {0}", audioSource.DriveOffset);
bool wereErrors = false;
for (int iTrack = 1; iTrack <= audioSource.TOC.AudioTracks; iTrack++)
for (uint iSector = audioSource.TOC[iTrack].Start; iSector <= audioSource.TOC[iTrack].End; iSector ++)
if (audioSource.Errors[(int)iSector])
{
if (!wereErrors)
{
logWriter.WriteLine();
logWriter.WriteLine("Errors detected");
logWriter.WriteLine();
}
wereErrors = true;
logWriter.WriteLine("Track {0} contains errors", iTrack);
break;
}
logWriter.WriteLine();
logWriter.WriteLine("TOC of the extracted CD");
logWriter.WriteLine();
@@ -180,6 +273,8 @@ namespace CUERipper
}
if (audioSource.TOC[track].ISRC != null)
cueWriter.WriteLine(" ISRC {0}", audioSource.TOC[track].ISRC);
if (audioSource.TOC[track].PreEmphasis)
cueWriter.WriteLine(" FLAGS PRE");
for (int index = audioSource.TOC[track].Pregap > 0 ? 0 : 1; index <= audioSource.TOC[track].LastIndex; index++)
cueWriter.WriteLine(" INDEX {0:00} {1}", index, audioSource.TOC[track][index].MSF);
}
@@ -190,10 +285,10 @@ namespace CUERipper
audioSource.Close();
FLACReader tagger = new FLACReader(destFile, null);
tagger.Tags.Add("CUESHEET", cueWriter.ToString());
tagger.Tags.Add("LOG", logWriter.ToString());
tagger.UpdateTags(false);
//FLACReader tagger = new FLACReader(destFile, null);
//tagger.Tags.Add("CUESHEET", cueWriter.ToString());
//tagger.Tags.Add("LOG", logWriter.ToString());
//tagger.UpdateTags(false);
}
#if !DEBUG
catch (Exception ex)
@@ -204,5 +299,17 @@ namespace CUERipper
}
#endif
}
//private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e)
//{
// if (this.CUEToolsProgress == null)
// return;
// _progress.percentDisk = (1.0 + _progress.percentDisk) / 2;
// _progress.percentTrack = 0;
// _progress.input = e.Uri.ToString();
// _progress.output = null;
// _progress.status = "Looking up album via MusicBrainz";
// this.CUEToolsProgress(this, _progress);
//}
}
}

View File

@@ -81,6 +81,10 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CRC16CCITT.cs" />
<Compile Include="Galois.cs" />
<Compile Include="RsDecode.cs" />
<Compile Include="RsEncode.cs" />
<Compile Include="SCSIDrive.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View File

@@ -21,6 +21,7 @@
using System;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
@@ -40,16 +41,29 @@ namespace CUETools.Ripper.SCSI
byte[] cdtext = null;
private Device m_device;
int _sampleOffset = 0;
uint _samplesInBuffer = 0;
uint _samplesBufferOffset = 0;
uint _samplesBufferSector = 0;
int _driveOffset = 0;
const int CB_AUDIO = 588 * 4 + 16;
const int NSECTORS = 32;
int _correctionQuality = 1;
int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0;
const bool DoC2 = true;
const int CB_AUDIO = 588 * 4 + 16 + (DoC2 ? 294 : 0);
//const int REDUNDANCY = 8;
const int NSECTORS = 64; //255 - REDUNDANCY;
const int MSECTORS = 10000000 / CB_AUDIO;
int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1;
Logger m_logger;
CDImageLayout _toc;
DeviceInfo m_info;
Crc16Ccitt _crc;
//RsEncode _rsEncoder;
//RsDecode _rsDecoder;
List<ScanResults> _scanResults;
ScanResults _currentScan;
BitArray _errors;
int _errorsCount;
byte[] _currentData = new byte[MSECTORS * 4 * 588];
int[] valueScore = new int[256];
public event EventHandler<ReadProgressArgs> ReadProgress;
public CDImageLayout TOC
{
@@ -59,9 +73,28 @@ namespace CUETools.Ripper.SCSI
}
}
public BitArray Errors
{
get
{
return _errors;
}
}
public int ErrorsCount
{
get
{
return _errorsCount;
}
}
public CDDriveReader()
{
m_logger = new Logger();
_crc = new Crc16Ccitt(InitialCrcValue.Zeros);
//_rsEncoder = new RsEncode(REDUNDANCY);
//_rsDecoder = new RsDecode(REDUNDANCY);
}
public bool Open(char Drive)
@@ -97,19 +130,28 @@ namespace CUETools.Ripper.SCSI
//ParseProfileList(f.Data);
// }
SpeedDescriptorList speed_list;
st = m_device.GetSpeed(out speed_list);
if (st != Device.CommandStatus.Success)
throw new Exception("GetSpeed failed: SCSI error");
//SpeedDescriptorList speed_list;
//st = m_device.GetSpeed(out speed_list);
//if (st != Device.CommandStatus.Success)
// throw new Exception("GetSpeed failed: SCSI error");
st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, 32767/*Device.OptimumSpeed*/, Device.OptimumSpeed);
if (st != Device.CommandStatus.Success)
throw new Exception("SetCdSpeed failed: SCSI error");
//int bytesPerSec = 4 * 588 * 75 * (pass > 8 ? 4 : pass > 4 ? 8 : pass > 0 ? 16 : 32);
//Device.CommandStatus st = m_device.SetStreaming(Device.RotationalControl.CLVandNonPureCav, start, end, bytesPerSec, 1, bytesPerSec, 1);
//if (st != Device.CommandStatus.Success)
// System.Console.WriteLine("SetStreaming: ", (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
//st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, (ushort)(bytesPerSec / 1024), (ushort)(bytesPerSec / 1024));
//if (st != Device.CommandStatus.Success)
// System.Console.WriteLine("SetCdSpeed: ", (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
//st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, 32767/*Device.OptimumSpeed*/, Device.OptimumSpeed);
//if (st != Device.CommandStatus.Success)
// throw new Exception("SetCdSpeed failed: SCSI error");
IList<TocEntry> toc;
st = m_device.ReadToc((byte)0, false, out toc);
if (st != Device.CommandStatus.Success)
throw new Exception("ReadToc failed: SCSI error");
throw new Exception("ReadTOC: " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
st = m_device.ReadCDText(out cdtext);
// new CDTextEncoderDecoder
@@ -119,10 +161,13 @@ namespace CUETools.Ripper.SCSI
_toc.AddTrack(new CDTrack((uint)iTrack + 1,
toc[iTrack].StartSector,
toc[iTrack + 1].StartSector - toc[iTrack].StartSector -
((toc[iTrack + 1].Control == 0 || iTrack + 1 == toc.Count - 1) ? 0U : 152U * 75U),
toc[iTrack].Control == 0));
((toc[iTrack + 1].Control < 4 || iTrack + 1 == toc.Count - 1) ? 0U : 152U * 75U),
toc[iTrack].Control < 4,
(toc[iTrack].Control & 1) == 1));
if (_toc[1].IsAudio)
_toc[1][0].Start = 0;
if (_toc.AudioLength > 0)
_errors = new BitArray((int)_toc.AudioLength); // !!!
return true;
}
@@ -137,32 +182,54 @@ namespace CUETools.Ripper.SCSI
{
get
{
return Math.Min(m_device.MaximumTransferLength / CB_AUDIO, NSECTORS) * 588;
return Math.Min(m_device.MaximumTransferLength / CB_AUDIO - 1, NSECTORS) * 588;
}
}
private void ProcessSubchannel(int sector, int Sectors2Read)
private void ProcessSubchannel(int sector, int Sectors2Read, bool updateMap)
{
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
int q_pos = (iSector + 1) * (588 * 4 + 16) - 16;
int ctl = _sectorBuffer[q_pos + 0] >> 4;
int adr = _sectorBuffer[q_pos + 0] & 7;
bool preemph = (ctl == 1);
int q_pos = (sector - _currentStart + iSector + 1) * CB_AUDIO - 16;
int ctl = _currentScan.Data[q_pos + 0] >> 4;
int adr = _currentScan.Data[q_pos + 0] & 7;
bool preemph = (ctl & 1) == 1;
switch (adr)
{
case 1: // current position
{
int iTrack = fromBCD(_sectorBuffer[q_pos + 1]);
int iIndex = fromBCD(_sectorBuffer[q_pos + 2]);
int iTrack = fromBCD(_currentScan.Data[q_pos + 1]);
int iIndex = fromBCD(_currentScan.Data[q_pos + 2]);
int mm = fromBCD(_currentScan.Data[q_pos + 7]);
int ss = fromBCD(_currentScan.Data[q_pos + 8]);
int ff = fromBCD(_currentScan.Data[q_pos + 9]);
int sec = ff + 75 * (ss + 60 * mm) - 150; // sector + iSector;
//if (sec != sector + iSector)
// System.Console.WriteLine("\rLost sync: {0} vs {1} ({2:X} vs {3:X})", CDImageLayout.TimeToString((uint)(sector + iSector)), CDImageLayout.TimeToString((uint)sec), sector + iSector, sec);
//ushort crc = _crc.ComputeChecksum(_currentScan.Data, q_pos, 10);
//crc ^= 0xffff;
//if (_currentScan.Data[q_pos + 10] != 0 && _currentScan.Data[q_pos + 11] != 0 &&
// ((crc & 0xff) != _currentScan.Data[q_pos + 11] ||
// (crc >> 8) != _currentScan.Data[q_pos + 10])
// )
//{
// System.Console.WriteLine("CRC error at {0}", CDImageLayout.TimeToString((uint)(sector + iSector)));
//}
if (iTrack == 110)
throw new Exception("lead out area encountred");
{
if (sector + iSector + 75 < _toc.AudioLength)
throw new Exception("lead out area encountred");
// make sure that data is zero?
return;
}
if (iTrack == 0)
throw new Exception("lead in area encountred");
if (!updateMap)
break;
if (iTrack != _currentTrack)
{
_currentTrack = iTrack;
_currentTrackActualStart = sector + iSector;
_currentTrackActualStart = sec;
_currentIndex = iIndex;
if (_currentIndex != 1 && _currentIndex != 0)
throw new Exception("invalid index");
@@ -174,13 +241,15 @@ namespace CUETools.Ripper.SCSI
_currentIndex = iIndex;
if (_currentIndex == 1)
{
uint pregap = (uint) (sector + iSector - _currentTrackActualStart);
uint pregap = (uint)(sec - _currentTrackActualStart);
_toc[iTrack][0].Start = _toc[iTrack].Start - pregap;
_currentTrackActualStart = sector + iSector;
_currentTrackActualStart = sec;
} else
_toc[iTrack].AddIndex(new CDTrackIndex((uint)iIndex, (uint)(_toc[iTrack].Start + sector + iSector - _currentTrackActualStart)));
_toc[iTrack].AddIndex(new CDTrackIndex((uint)iIndex, (uint)(_toc[iTrack].Start + sec - _currentTrackActualStart)));
_currentIndex = iIndex;
}
if (preemph)
_toc[iTrack].PreEmphasis = true;
break;
}
case 2: // catalog
@@ -188,7 +257,7 @@ namespace CUETools.Ripper.SCSI
{
StringBuilder catalog = new StringBuilder();
for (int i = 1; i < 8; i++)
catalog.AppendFormat("{0:x2}", _sectorBuffer[q_pos + i]);
catalog.AppendFormat("{0:x2}", _currentScan.Data[q_pos + i]);
_toc.Catalog = catalog.ToString(0, 13);
}
break;
@@ -197,16 +266,16 @@ namespace CUETools.Ripper.SCSI
{
StringBuilder isrc = new StringBuilder();
char[] ISRC6 = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '#', '#', '#', '#', '#', '#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
isrc.Append(ISRC6[_sectorBuffer[q_pos + 1] >> 2]);
isrc.Append(ISRC6[((_sectorBuffer[q_pos + 1] & 0x3) << 4) + (_sectorBuffer[q_pos + 2] >> 4)]);
isrc.Append(ISRC6[((_sectorBuffer[q_pos + 2] & 0xf) << 2) + (_sectorBuffer[q_pos + 3] >> 6)]);
isrc.Append(ISRC6[(_sectorBuffer[q_pos + 3] & 0x3f)]);
isrc.Append(ISRC6[_sectorBuffer[q_pos + 4] >> 2]);
isrc.Append(ISRC6[((_sectorBuffer[q_pos + 4] & 0x3) << 4) + (_sectorBuffer[q_pos + 5] >> 4)]);
isrc.AppendFormat("{0:x}", _sectorBuffer[q_pos + 5] & 0xf);
isrc.AppendFormat("{0:x2}", _sectorBuffer[q_pos + 6]);
isrc.AppendFormat("{0:x2}", _sectorBuffer[q_pos + 7]);
isrc.AppendFormat("{0:x}", _sectorBuffer[q_pos + 8] >> 4);
isrc.Append(ISRC6[_currentScan.Data[q_pos + 1] >> 2]);
isrc.Append(ISRC6[((_currentScan.Data[q_pos + 1] & 0x3) << 4) + (_currentScan.Data[q_pos + 2] >> 4)]);
isrc.Append(ISRC6[((_currentScan.Data[q_pos + 2] & 0xf) << 2) + (_currentScan.Data[q_pos + 3] >> 6)]);
isrc.Append(ISRC6[(_currentScan.Data[q_pos + 3] & 0x3f)]);
isrc.Append(ISRC6[_currentScan.Data[q_pos + 4] >> 2]);
isrc.Append(ISRC6[((_currentScan.Data[q_pos + 4] & 0x3) << 4) + (_currentScan.Data[q_pos + 5] >> 4)]);
isrc.AppendFormat("{0:x}", _currentScan.Data[q_pos + 5] & 0xf);
isrc.AppendFormat("{0:x2}", _currentScan.Data[q_pos + 6]);
isrc.AppendFormat("{0:x2}", _currentScan.Data[q_pos + 7]);
isrc.AppendFormat("{0:x}", _currentScan.Data[q_pos + 8] >> 4);
_toc[_currentTrack].ISRC = isrc.ToString();
}
break;
@@ -216,117 +285,294 @@ namespace CUETools.Ripper.SCSI
private unsafe void FetchSectors(int sector, int Sectors2Read)
{
fixed (byte* data = _sectorBuffer)
fixed (byte* data = _currentScan.Data)
{
Device.CommandStatus st = m_device.ReadCDAndSubChannel(2, 1, true, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), Sectors2Read * (2352 + 16));
//Device.CommandStatus st;
//using (Command cmd = new Command(ScsiCommandCode.Read12, 12, (IntPtr)((void*)data), Sectors2Read * 588 * 4, Command.CmdDirection.In, 5 * 60))
//{
// //cmd.SetCDB8(1, 4); // force from media
// cmd.SetCDB32(2, sector);
// cmd.SetCDB32(6, Sectors2Read);
// //cmd.SetCDB8(10, 0x80); // streaming
// st= m_device.SendCommand(cmd);
//}
Device.CommandStatus st = m_device.ReadCDAndSubChannel(2, DoC2, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)(data + (sector - _currentStart) * CB_AUDIO)), Sectors2Read * CB_AUDIO);
if (st != Device.CommandStatus.Success)
throw new Exception("ReadCDAndSubChannel failed: SCSI error");
{
if (st == Device.CommandStatus.DeviceFailed && m_device.GetSenseAsc() == 0x64 && m_device.GetSenseAscq() == 0x00)
{
int iErrors = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
st = m_device.ReadCDAndSubChannel(2, DoC2, 1, false, (uint)(sector + iSector), 1U, (IntPtr)((void*)(data + (sector + iSector - _currentStart) * CB_AUDIO)), CB_AUDIO);
if (st != Device.CommandStatus.Success)
{
iErrors ++;
for (int iPos = 0; iPos < CB_AUDIO; iPos++)
data[(sector + iSector - _currentStart) * CB_AUDIO + iPos] = (DoC2 && iPos >= 4 * 588 && iPos < 4 * 588 + 294) ? (byte)255 : (byte)0;
}
}
if (iErrors < Sectors2Read)
return;
}
string status = (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString());
st = m_device.ReadCDAndSubChannel(0, false, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)(data + (sector - _currentStart) * 4 * 588)), Sectors2Read * 4 * 588);
if (st != Device.CommandStatus.Success)
status += "; ReadWithoutSubchannel: " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString());
else
status += "; ReadWithoutSubchannel: might work";
throw new Exception("ReadCD: " + status);
}
//int localC2 = 0;
//if (DoC2)
// for (int iSector = 0; iSector < Sectors2Read; iSector++)
// {
// _currentScan.C2Errors[sector + iSector] = data[(iSector + 1) * CB_AUDIO - 18] != 0;
// localC2 += (data[(iSector + 1) * CB_AUDIO - 18] != 0) ? 1 : 0;
// }
//_currentScan.C2Count += localC2;
}
ProcessSubchannel(sector, Sectors2Read);
}
public unsafe uint Read(int[,] buff, uint sampleCount)
private unsafe void CorrectSectors(int sector, int Sectors2Read, bool findWorst, bool markErrors)
{
int c2Score = 10;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
for (int iPar = 0; iPar < 4 * 588; iPar++)
{
int dataPos = (sector - _currentStart + iSector) * CB_AUDIO + iPar;
int c2Pos = (sector - _currentStart + iSector) * CB_AUDIO + 4 * 588 + iPar / 8;
int c2Bit = 0x80 >> (iPar % 8);
Array.Clear(valueScore, 0, 256);
byte bestValue = _currentScan.Data[dataPos];
valueScore[bestValue] += 1 + (((_currentScan.Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
int totalScore = valueScore[bestValue];
for (int result = 0; result < _scanResults.Count; result++)
{
byte value = _scanResults[result].Data[dataPos];
valueScore[value] += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
totalScore += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
if (valueScore[value] > valueScore[bestValue])
bestValue = value;
}
bool fError = valueScore[bestValue] * 2 <= c2Score + totalScore;
if (fError)
_currentErrorsCount++;
_currentData[(sector - _currentStart + iSector) * 4 * 588 + iPar] = bestValue;
if (findWorst)
{
for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality += Math.Min(0, valueScore[_scanResults[result].Data[dataPos]] - c2Score - 1);
_currentScan.Quality += Math.Min(0, valueScore[_currentScan.Data[dataPos]] - c2Score - 1);
}
if (markErrors)
{
_errors[sector + iSector] = fError;
_errorsCount += fError ? 1 : 0;
}
}
}
}
//private unsafe int CorrectSectorsTest(int start, int end, int c2Score, byte[] realData, int worstScan)
//{
// int[] valueScore = new int[256];
// int[] scoreErrors = new int[256];
// int realErrors = 0;
// int bestScore = 0;
// int _errorsCaught = 0;
// int _falsePositives = 0;
// for (int iSector = 0; iSector < end - start; iSector++)
// {
// for (int iPar = 0; iPar < 4 * 588; iPar++)
// {
// int dataPos = iSector * CB_AUDIO + iPar;
// int c2Pos = iSector * CB_AUDIO + 4 * 588 + iPar / 8;
// int c2Bit = 0x80 >> (iPar % 8);
// Array.Clear(valueScore, 0, 256);
// byte bestValue = _currentScan.Data[dataPos];
// valueScore[bestValue] += 1 + (((_currentScan.Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
// int totalScore = valueScore[bestValue];
// for (int result = 0; result < _scanResults.Count; result++)
// {
// if (result == worstScan)
// continue;
// byte value = _scanResults[result].Data[dataPos];
// valueScore[value] += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
// totalScore += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
// if (valueScore[value] > valueScore[bestValue])
// bestValue = value;
// }
// if (valueScore[bestValue] < (1 + c2Score + totalScore) / 2)
// _currentErrorsCount++;
// //_currentData[iSector * 4 * 588 + iPar] = bestValue;
// if (realData[iSector * 4 * 588 + iPar] != bestValue)
// {
// if (valueScore[bestValue] > bestScore)
// scoreErrors[valueScore[bestValue]]++;
// realErrors++;
// if (valueScore[bestValue] * 2 <= c2Score + totalScore)
// _errorsCaught++;
// } else
// if (valueScore[bestValue] * 2 <= c2Score + totalScore)
// _falsePositives++;
// }
// }
// //string s = "";
// //for (int i = 0; i < 256; i++)
// // if (scoreErrors[i] > 0)
// // s += string.Format("[{0}]={1};", i, scoreErrors[i]);
// //System.Console.WriteLine("RE{0:000} EC{1} FP{2}", realErrors, _errorsCaught, _falsePositives);
// return realErrors;
//}
public void PrefetchSector(int iSector)
{
int nPasses = 16 + _correctionQuality * 2;
int nExtraPasses = 8 + _correctionQuality;
if (_currentStart == MSECTORS * (iSector / MSECTORS))
return;
_currentStart = MSECTORS * (iSector / MSECTORS);
_currentEnd = Math.Min(_currentStart + MSECTORS, (int)_toc.AudioLength);
_scanResults = new List<ScanResults>();
//FileStream correctFile = new FileStream("correct.wav", FileMode.Open);
//byte[] realData = new byte[MSECTORS * 4 * 588];
//correctFile.Seek(0x2C + start * 588 * 4, SeekOrigin.Begin);
//if (correctFile.Read(realData, 0, MSECTORS * 4 * 588) != MSECTORS * 4 * 588)
// throw new Exception("read");
//correctFile.Close();
for (int pass = 0; pass <= nPasses + nExtraPasses; pass++)
{
DateTime PassTime = DateTime.Now;
_currentScan = new ScanResults(MSECTORS, CB_AUDIO);
_currentErrorsCount = 0;
int nSectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
for (int sector = _currentStart; sector < _currentEnd; sector += nSectors)
{
int Sectors2Read = Math.Min(nSectors, _currentEnd - sector);
FetchSectors(sector, Sectors2Read);
CorrectSectors(sector, Sectors2Read, pass > nPasses, pass == nPasses + nExtraPasses);
ProcessSubchannel(sector, Sectors2Read, pass == 0);
if (ReadProgress != null)
ReadProgress(this, new ReadProgressArgs(sector + Sectors2Read, pass, _currentStart, _currentEnd, _currentErrorsCount, PassTime));
}
//System.Console.WriteLine();
//if (CorrectSectorsTest(start, _currentEnd, 10, realData) == 0)
// break;
if (pass == nPasses + nExtraPasses)
break;
if (pass > nPasses)
{
int worstPass = -1;
for (int result = 0; result < _scanResults.Count; result++)
if (_scanResults[result].Quality < (worstPass < 0 ? _currentScan.Quality : _scanResults[worstPass].Quality))
worstPass = result;
//if (worstPass < 0)
// System.Console.WriteLine("bad scan");
//else
// System.Console.WriteLine("{0}->{1}, {2}->{3}", _scanResults[worstPass].Quality, _currentScan.Quality, CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, -1), CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, worstPass));
if (worstPass < 0)
_currentScan = null;
else
_scanResults[worstPass] = _currentScan;
for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality = 0;
continue;
}
if (_currentErrorsCount == 0 && pass >= _correctionQuality)
{
bool syncOk = true;
//if (pass == 0)
//{
// ScanResults saved = _currentScan;
// _currentScan = new ScanResults(_currentEnd - _currentStart, CB_AUDIO);
// for (int sector = _currentStart; sector < _currentEnd && syncOk; sector += 7)
// {
// FetchSectors(sector, 2);
// for (int i = 0; i < 2 * CB_AUDIO; i++)
// if (_currentScan.Data[(sector - _currentStart) * CB_AUDIO + i] != saved.Data[(sector - _currentStart) * CB_AUDIO + i])
// {
// System.Console.WriteLine("Lost Sync");
// syncOk = false;
// break;
// }
// }
// _currentScan = saved;
//}
if (syncOk)
break;
}
_scanResults.Add(_currentScan);
}
_currentScan = null;
_scanResults = null;
}
public uint Read(int[,] buff, uint sampleCount)
{
if (_toc == null)
throw new Exception("Read: invalid TOC");
if (_sampleOffset - _driveOffset >= (uint)Length)
return 0;
return 0;
if (_sampleOffset > (uint)Length)
{
int samplesRead = _sampleOffset - (int)Length;
for (int i = 0; i < samplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[i, c] = 0;
_sampleOffset += samplesRead;
return 0;
int samplesRead = _sampleOffset - (int)Length;
for (int i = 0; i < samplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[i, c] = 0;
_sampleOffset += samplesRead;
return 0;
}
if ((uint)(_sampleOffset - _driveOffset + sampleCount) > Length)
sampleCount = (uint)((int)Length + _driveOffset - _sampleOffset);
sampleCount = (uint)((int)Length + _driveOffset - _sampleOffset);
int silenceCount = 0;
if ((uint)(_sampleOffset + sampleCount) > Length)
{
silenceCount = _sampleOffset + (int)sampleCount - (int)Length;
sampleCount -= (uint) silenceCount;
silenceCount = _sampleOffset + (int)sampleCount - (int)Length;
sampleCount -= (uint) silenceCount;
}
uint pos = 0;
if (_sampleOffset < 0)
{
uint nullSamplesRead = Math.Min((uint)-_sampleOffset, sampleCount);
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[i, c] = 0;
pos += nullSamplesRead;
sampleCount -= nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
if (sampleCount == 0)
return pos;
uint nullSamplesRead = Math.Min((uint)-_sampleOffset, sampleCount);
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[i, c] = 0;
pos += nullSamplesRead;
sampleCount -= nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
if (sampleCount == 0)
return pos;
}
if (_samplesInBuffer > 0)
{
uint samplesRead = Math.Min(_samplesInBuffer, sampleCount);
AudioSamples.BytesToFLACSamples_16(_sectorBuffer, (int)(_samplesBufferSector * (588 * 4 + 16) + _samplesBufferOffset * 4), buff, (int)pos, samplesRead, 2);
pos += samplesRead;
sampleCount -= samplesRead;
_sampleOffset += (int) samplesRead;
if (sampleCount == 0)
{
_samplesInBuffer -= samplesRead;
_samplesBufferOffset += samplesRead;
if (silenceCount > 0)
{
uint nullSamplesRead = (uint) silenceCount;
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0;
pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
}
return pos;
}
_samplesInBuffer = 0;
_samplesBufferOffset = 0;
_samplesBufferSector = 0;
}
// if (_sampleOffset < PreGapLength && !_overreadIntoPreGap ... ?
int firstSector = (int)_sampleOffset / 588;
int lastSector = (int)(_sampleOffset + sampleCount + 587) / 588;
for (int sector = firstSector; sector < lastSector; sector += NSECTORS)
int lastSector = Math.Min((int)(_sampleOffset + sampleCount + 587)/588, (int)_toc.AudioLength);
for (int sector = firstSector; sector < lastSector; sector ++)
{
int Sectors2Read = ((sector + NSECTORS) < lastSector) ? NSECTORS : (lastSector - sector);
FetchSectors(sector, Sectors2Read);
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
uint samplesRead = (uint) (Math.Min((int)sampleCount, 588) - (_sampleOffset % 588));
AudioSamples.BytesToFLACSamples_16(_sectorBuffer, iSector * (588 * 4 + 16) + ((int)_sampleOffset % 588) * 4, buff, (int)pos, samplesRead, 2);
pos += samplesRead;
sampleCount -= samplesRead;
_sampleOffset += (int) samplesRead;
if (sampleCount == 0)
{
_samplesBufferSector = (uint)iSector;
_samplesBufferOffset = samplesRead;
_samplesInBuffer = 588U - samplesRead;
if (silenceCount > 0)
{
uint nullSamplesRead = (uint)silenceCount;
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0;
pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
}
return pos;
}
}
PrefetchSector(sector);
uint samplesRead = (uint) (Math.Min((int)sampleCount, 588 - (_sampleOffset % 588)));
AudioSamples.BytesToFLACSamples_16(_currentData, (sector - _currentStart) * 4 * 588 + ((int)_sampleOffset % 588) * 4, buff, (int)pos, samplesRead, 2);
pos += samplesRead;
sampleCount -= samplesRead;
_sampleOffset += (int) samplesRead;
}
if (silenceCount > 0)
{
uint nullSamplesRead = (uint)silenceCount;
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0;
pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
uint nullSamplesRead = (uint)silenceCount;
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0;
pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
}
return pos;
}
@@ -430,14 +676,24 @@ namespace CUETools.Ripper.SCSI
}
}
public int CorrectionQuality
{
get
{
return _correctionQuality;
}
set
{
_correctionQuality = value;
}
}
public static string RipperVersion()
{
return "CUERipper v1.9.3 Copyright (C) 2008 Gregory S. Chudov";
// ripper.GetName().Name + " " + ripper.GetName().Version;
}
byte[] _sectorBuffer = new byte[CB_AUDIO * NSECTORS];
private int fromBCD(byte hex)
{
return (hex >> 4) * 10 + (hex & 15);
@@ -452,4 +708,35 @@ namespace CUETools.Ripper.SCSI
return result.ToArray();
}
}
internal class ScanResults
{
public byte[] Data;
public long Quality;
public ScanResults(int msector, int cbaudio)
{
Data = new byte[msector * cbaudio];
Quality = 0;
}
}
public sealed class ReadProgressArgs: EventArgs
{
public int Position;
public int Pass;
public int PassStart, PassEnd;
public int ErrorsCount;
public DateTime PassTime;
public ReadProgressArgs(int position, int pass, int passStart, int passEnd, int errorsCount, DateTime passTime)
{
Position = position;
Pass = pass;
PassStart = passStart;
PassEnd = passEnd;
ErrorsCount = errorsCount;
PassTime = passTime;
}
}
}

View File

@@ -20,6 +20,7 @@
// ****************************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
@@ -436,6 +437,7 @@ namespace CUETools.Processor
private bool _isCD;
private string _driveName;
private int _driveOffset;
private BitArray _cdErrors;
private bool _isArchive;
private List<string> _archiveContents;
private string _archiveCUEpath;
@@ -696,7 +698,7 @@ namespace CUETools.Processor
}
trackInfo = new TrackInfo();
_tracks.Add(trackInfo);
_toc.AddTrack(new CDTrack((uint)trackNumber, 0, 0, true));
_toc.AddTrack(new CDTrack((uint)trackNumber, 0, 0, true, false));
}
}
else if (seenDataTrack) {
@@ -892,7 +894,7 @@ namespace CUETools.Processor
uint trNo, trStart, trEnd;
if (n.Length == 5 && uint.TryParse(n[0], out trNo) && uint.TryParse(n[3], out trStart) && uint.TryParse(n[4], out trEnd))
tocFromLog.AddTrack(new CDTrack(trNo, trStart, trEnd + 1 - trStart,
tocFromLog.TrackCount < _toc.TrackCount || trStart != tocFromLog[tocFromLog.TrackCount].End + 1U + 152U * 75U));
tocFromLog.TrackCount < _toc.TrackCount || trStart != tocFromLog[tocFromLog.TrackCount].End + 1U + 152U * 75U, false));
} else
if (lineStr.StartsWith("TOC of the extracted CD")
|| lineStr.StartsWith("Exact Audio Copy")
@@ -906,7 +908,7 @@ namespace CUETools.Processor
if (_accurateRipId == null && _dataTrackLength != null)
{
CDImageLayout toc2 = new CDImageLayout(_toc);
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152U * 75U, _dataTrackLength.Value, false));
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152U * 75U, _dataTrackLength.Value, false, false));
_accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
}
@@ -1216,18 +1218,39 @@ namespace CUETools.Processor
_toc[track].LengthMSF,
_toc[track].Start,
_toc[track].End);
logWriter.WriteLine();
if (_accurateRipMode == AccurateRipMode.VerifyThenConvert)
for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++)
if (_arVerify.BackupCRC(iTrack) != _arVerify.CRC(iTrack))
logWriter.WriteLine("Track {0} CRC mismatch: test {1:X8} vs copy {2:X8}", iTrack + 1, _arVerify.BackupCRC(iTrack), _arVerify.CRC(iTrack));
bool wereErrors = false;
for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++)
{
int cdErrors = 0;
bool crcMismatch = _accurateRipMode == AccurateRipMode.VerifyThenConvert &&
_arVerify.BackupCRC(iTrack) != _arVerify.CRC(iTrack);
for (uint iSector = _toc[iTrack + 1].Start; iSector <= _toc[iTrack + 1].End; iSector++)
if (_cdErrors[(int)iSector])
cdErrors++;
if (crcMismatch || cdErrors != 0)
{
if (!wereErrors)
{
logWriter.WriteLine();
logWriter.WriteLine("Errors detected");
logWriter.WriteLine();
}
wereErrors = true;
if (crcMismatch)
logWriter.WriteLine("Track {0} contains {1} errors, CRC mismatch: test {2:X8} vs copy {3:X8}", iTrack + 1, cdErrors, _arVerify.BackupCRC(iTrack), _arVerify.CRC(iTrack));
else
logWriter.WriteLine("Track {0} contains {1} errors", iTrack + 1, cdErrors);
}
}
if (_accurateRipMode != AccurateRipMode.None)
{
logWriter.WriteLine();
logWriter.WriteLine("AccurateRip summary");
logWriter.WriteLine();
_arVerify.GenerateFullLog(logWriter, 0);
logWriter.WriteLine();
}
logWriter.WriteLine();
logWriter.WriteLine("End of status report");
logWriter.Close();
return logWriter.ToString();
@@ -1494,7 +1517,7 @@ namespace CUETools.Processor
{
uint minDTL = _minDataTrackLength.Value;
CDImageLayout toc2 = new CDImageLayout(_toc);
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, minDTL, false));
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, minDTL, false, false));
for (uint dtl = minDTL; dtl < minDTL + 75; dtl++)
{
toc2[toc2.TrackCount].Length = dtl;
@@ -1608,7 +1631,7 @@ namespace CUETools.Processor
{
WriteText(_cuePath, cueContents);
#if !MONO
if (needNewCRCs)
if (needNewCRCs && style != CUEStyle.SingleFile)
{
for (int iTrack = 0; iTrack < TrackCount; iTrack++)
{
@@ -1972,7 +1995,10 @@ namespace CUETools.Processor
{
#if !MONO
if (_isCD && audioSource != null && audioSource is CDDriveReader)
{
updatedTOC = ((CDDriveReader)audioSource).TOC;
_cdErrors = ((CDDriveReader)audioSource).Errors;
}
#endif
if (audioSource != null) audioSource.Close();
audioSource = GetAudioSource(++iSource);
@@ -2057,15 +2083,22 @@ namespace CUETools.Processor
#if !MONO
if (_isCD && audioSource != null && audioSource is CDDriveReader)
{
updatedTOC = ((CDDriveReader)audioSource).TOC;
_cdErrors = ((CDDriveReader)audioSource).Errors;
}
if (updatedTOC != null)
{
_toc = updatedTOC;
if (_toc.Catalog != null)
General.SetCUELine(_attributes, "CATALOG", _toc.Catalog, false);
for (iTrack = 1; iTrack <= _toc.TrackCount; iTrack++)
{
if (_toc[iTrack].IsAudio && _toc[iTrack].ISRC != null)
General.SetCUELine(_tracks[iTrack - 1].Attributes, "ISRC", _toc[iTrack].ISRC, false);
if (_toc[iTrack].IsAudio && _toc[iTrack].PreEmphasis)
General.SetCUELine(_tracks[iTrack - 1].Attributes, "FLAGS", "PRE", false);
}
}
#endif
@@ -2401,7 +2434,7 @@ namespace CUETools.Processor
{
_dataTrackLength = dtl;
CDImageLayout toc2 = new CDImageLayout(_toc);
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, dtl, false));
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, dtl, false, false));
_accurateRipIdActual = _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
}
}