Ripping: read command autodetection

This commit is contained in:
chudov
2008-12-07 23:12:01 +00:00
parent 3fd37e6afe
commit a430001f33
6 changed files with 275 additions and 158 deletions

View File

@@ -283,9 +283,7 @@ namespace CUETools.CDImage
{ {
get get
{ {
return _tracks[TrackCount - 1].IsAudio ? return AudioTracks > 0 ? _tracks[(int)_audioTracks - 1].End + 1U : 0U;
_tracks[TrackCount - 1].End + 1 :
_tracks[TrackCount - 2].End + 1;
} }
} }

View File

@@ -49,7 +49,7 @@ namespace CUERipper
TimeSpan elapsed = DateTime.Now - e.PassTime; TimeSpan elapsed = DateTime.Now - e.PassTime;
double speed = elapsed.TotalSeconds > 0 ? processed / elapsed.TotalSeconds / 75 : 1.0; double speed = elapsed.TotalSeconds > 0 ? processed / elapsed.TotalSeconds / 75 : 1.0;
TimeSpan totalElapsed = DateTime.Now - realStart; 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); TimeSpan totalEstimated = TimeSpan.FromMilliseconds(totalElapsed.TotalMilliseconds / Math.Max(1, (e.PassStart + (processed + e.Pass * (e.PassEnd - e.PassStart)) / (audioSource.CorrectionQuality + 1))) * audioSource.TOC.AudioLength);
// if ((elapsed - lastPrint).TotalMilliseconds > 60) ; // 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} ", 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} ",
@@ -151,7 +151,7 @@ namespace CUERipper
//throw new Exception("Failed to find drive read offset for drive" + audioSource.ARName); //throw new Exception("Failed to find drive read offset for drive" + audioSource.ARName);
if (test) if (test)
{ {
Console.Write(audioSource.TestReadCommand()); Console.Write(audioSource.AutoDetectReadCommand);
return; return;
} }
audioSource.DriveOffset = driveOffset; audioSource.DriveOffset = driveOffset;
@@ -183,6 +183,8 @@ namespace CUERipper
Console.WriteLine("Drive : {0}", audioSource.Path); Console.WriteLine("Drive : {0}", audioSource.Path);
Console.WriteLine("Read offset : {0}", audioSource.DriveOffset); Console.WriteLine("Read offset : {0}", audioSource.DriveOffset);
Console.WriteLine("Read cmd : {0}", audioSource.ChosenReadCommand);
Console.WriteLine("Secure mode : {0}", audioSource.CorrectionQuality);
Console.WriteLine("Filename : {0}", destFile); Console.WriteLine("Filename : {0}", destFile);
Console.WriteLine("Disk length : {0}", CDImageLayout.TimeToString(audioSource.TOC.AudioLength)); Console.WriteLine("Disk length : {0}", CDImageLayout.TimeToString(audioSource.TOC.AudioLength));
Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus); Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus);

View File

@@ -45,16 +45,19 @@ namespace CUETools.Ripper.SCSI
int _driveOffset = 0; int _driveOffset = 0;
int _correctionQuality = 1; int _correctionQuality = 1;
int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0; int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0;
const int CB_AUDIO = 588 * 4 + 2 + 294 + 16; const int CB_AUDIO = 4 * 588 + 2 + 294 + 16;
const int NSECTORS = 32; const int NSECTORS = 16;
const int MSECTORS = 10000000 / CB_AUDIO; const int MSECTORS = 10000000 / (4 * 588);
int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1; int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1;
Logger m_logger; Logger m_logger;
CDImageLayout _toc; CDImageLayout _toc;
DeviceInfo m_info; char m_device_letter;
InquiryResult m_inqury_result;
int m_max_sectors;
int _timeout = 10;
Crc16Ccitt _crc; Crc16Ccitt _crc;
List<ScanResults> _scanResults; List<ScanResults> _scanResults;
ScanResults _currentScan; ScanResults _currentScan = null;
BitArray _errors; BitArray _errors;
int _errorsCount; int _errorsCount;
byte[] _currentData = new byte[MSECTORS * 4 * 588]; byte[] _currentData = new byte[MSECTORS * 4 * 588];
@@ -63,6 +66,7 @@ namespace CUETools.Ripper.SCSI
Device.MainChannelSelection _mainChannelMode = Device.MainChannelSelection.UserData; Device.MainChannelSelection _mainChannelMode = Device.MainChannelSelection.UserData;
Device.SubChannelMode _subChannelMode = Device.SubChannelMode.None; Device.SubChannelMode _subChannelMode = Device.SubChannelMode.None;
Device.C2ErrorMode _c2ErrorMode = Device.C2ErrorMode.Mode296; Device.C2ErrorMode _c2ErrorMode = Device.C2ErrorMode.Mode296;
string m_test_result;
byte[] _readBuffer = new byte[NSECTORS * CB_AUDIO]; byte[] _readBuffer = new byte[NSECTORS * CB_AUDIO];
byte[] _subchannelBuffer = new byte[NSECTORS * 16]; byte[] _subchannelBuffer = new byte[NSECTORS * 16];
@@ -92,6 +96,18 @@ namespace CUETools.Ripper.SCSI
} }
} }
public int Timeout
{
get
{
return _timeout;
}
set
{
_timeout = value;
}
}
public bool DebugMessages public bool DebugMessages
{ {
get get
@@ -104,6 +120,35 @@ namespace CUETools.Ripper.SCSI
} }
} }
public string AutoDetectReadCommand
{
get
{
if (m_test_result == null)
TestReadCommand();
return m_test_result;
}
}
public string CurrentReadCommand
{
get
{
return string.Format("BEh, {0}, {1}, {2}, {3} blocks at a time", (_mainChannelMode == Device.MainChannelSelection.UserData ? "10h" : "F8h"),
(_subChannelMode == Device.SubChannelMode.None ? "00h" : _subChannelMode == Device.SubChannelMode.QOnly ? "02h" : "04h"),
(_c2ErrorMode == Device.C2ErrorMode.None ? "00h" : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? "01h" : "04h"),
m_max_sectors);
}
}
public string ChosenReadCommand
{
get
{
return m_test_result == null && !TestReadCommand() ? "not detected" : CurrentReadCommand;
}
}
public CDDriveReader() public CDDriveReader()
{ {
m_logger = new Logger(); m_logger = new Logger();
@@ -115,15 +160,19 @@ namespace CUETools.Ripper.SCSI
Device.CommandStatus st; Device.CommandStatus st;
// Open the base device // Open the base device
m_device_letter = Drive;
m_device = new Device(m_logger); m_device = new Device(m_logger);
if (!m_device.Open(Drive)) if (!m_device.Open(m_device_letter))
throw new Exception("Open failed: SCSI error"); throw new Exception("Open failed: SCSI error");
// Get device info // Get device info
m_info = DeviceInfo.CreateDevice(Drive + ":"); st = m_device.Inquiry(out m_inqury_result);
if (!m_info.ExtractInfo(m_device)) if (st != Device.CommandStatus.Success || !m_inqury_result.Valid)
throw new Exception("ExtractInfo failed: SCSI error"); throw new SCSIException("Inquiry", m_device, st);
if (m_inqury_result.PeripheralQualifier != 0 || m_inqury_result.PeripheralDeviceType != Device.MMCDeviceType)
throw new Exception(Path + " is not an MMC device");
m_max_sectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
//// Open/Initialize the driver //// Open/Initialize the driver
//Drive m_drive = new Drive(dev); //Drive m_drive = new Drive(dev);
//DiskOperationError status = m_drive.Initialize(); //DiskOperationError status = m_drive.Initialize();
@@ -195,7 +244,7 @@ namespace CUETools.Ripper.SCSI
{ {
get get
{ {
return Math.Min(m_device.MaximumTransferLength / CB_AUDIO - 1, NSECTORS) * 588; return m_max_sectors * 588;
} }
} }
@@ -204,27 +253,30 @@ namespace CUETools.Ripper.SCSI
int posCount = 0; int posCount = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++) for (int iSector = 0; iSector < Sectors2Read; iSector++)
{ {
int q_pos = (sector - _currentStart + iSector + 1) * CB_AUDIO - 16; int q_pos = sector - _currentStart + iSector;
int ctl = _currentScan.Data[q_pos + 0] >> 4; int ctl = _currentScan.QData[q_pos, 0] >> 4;
int adr = _currentScan.Data[q_pos + 0] & 7; int adr = _currentScan.QData[q_pos, 0] & 7;
bool preemph = (ctl & 1) == 1; bool preemph = (ctl & 1) == 1;
switch (adr) switch (adr)
{ {
case 1: // current position case 1: // current position
{ {
int iTrack = fromBCD(_currentScan.Data[q_pos + 1]); int iTrack = fromBCD(_currentScan.QData[q_pos, 1]);
int iIndex = fromBCD(_currentScan.Data[q_pos + 2]); int iIndex = fromBCD(_currentScan.QData[q_pos, 2]);
int mm = fromBCD(_currentScan.Data[q_pos + 7]); int mm = fromBCD(_currentScan.QData[q_pos, 7]);
int ss = fromBCD(_currentScan.Data[q_pos + 8]); int ss = fromBCD(_currentScan.QData[q_pos, 8]);
int ff = fromBCD(_currentScan.Data[q_pos + 9]); int ff = fromBCD(_currentScan.QData[q_pos, 9]);
int sec = ff + 75 * (ss + 60 * mm) - 150; // sector + iSector; int sec = ff + 75 * (ss + 60 * mm) - 150; // sector + iSector;
//if (sec != 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); // 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); byte[] tmp = new byte[16];
for (int i = 0; i < 10; i++)
_subchannelBuffer[i] = _currentScan.QData[q_pos, i];
ushort crc = _crc.ComputeChecksum(_subchannelBuffer, 0, 10);
crc ^= 0xffff; crc ^= 0xffff;
if (_currentScan.Data[q_pos + 10] != 0 && _currentScan.Data[q_pos + 11] != 0 && if (_currentScan.QData[q_pos, 10] != 0 && _currentScan.QData[q_pos, 11] != 0 &&
((crc & 0xff) != _currentScan.Data[q_pos + 11] || ((crc & 0xff) != _currentScan.QData[q_pos, 11] ||
(crc >> 8) != _currentScan.Data[q_pos + 10]) (crc >> 8) != _currentScan.QData[q_pos, 10])
) )
{ {
if (_debugMessages) if (_debugMessages)
@@ -276,7 +328,7 @@ namespace CUETools.Ripper.SCSI
{ {
StringBuilder catalog = new StringBuilder(); StringBuilder catalog = new StringBuilder();
for (int i = 1; i < 8; i++) for (int i = 1; i < 8; i++)
catalog.AppendFormat("{0:x2}", _currentScan.Data[q_pos + i]); catalog.AppendFormat("{0:x2}", _currentScan.QData[q_pos, i]);
_toc.Catalog = catalog.ToString(0, 13); _toc.Catalog = catalog.ToString(0, 13);
} }
break; break;
@@ -284,16 +336,16 @@ namespace CUETools.Ripper.SCSI
if (_toc[_currentTrack].ISRC == null) if (_toc[_currentTrack].ISRC == null)
{ {
StringBuilder isrc = new StringBuilder(); StringBuilder isrc = new StringBuilder();
isrc.Append(from6bit(_currentScan.Data[q_pos + 1] >> 2)); isrc.Append(from6bit(_currentScan.QData[q_pos, 1] >> 2));
isrc.Append(from6bit(((_currentScan.Data[q_pos + 1] & 0x3) << 4) + (0x0f & (_currentScan.Data[q_pos + 2] >> 4)))); isrc.Append(from6bit(((_currentScan.QData[q_pos, 1] & 0x3) << 4) + (0x0f & (_currentScan.QData[q_pos, 2] >> 4))));
isrc.Append(from6bit(((_currentScan.Data[q_pos + 2] & 0xf) << 2) + (0x03 & (_currentScan.Data[q_pos + 3] >> 6)))); isrc.Append(from6bit(((_currentScan.QData[q_pos, 2] & 0xf) << 2) + (0x03 & (_currentScan.QData[q_pos, 3] >> 6))));
isrc.Append(from6bit((_currentScan.Data[q_pos + 3] & 0x3f))); isrc.Append(from6bit((_currentScan.QData[q_pos, 3] & 0x3f)));
isrc.Append(from6bit(_currentScan.Data[q_pos + 4] >> 2)); isrc.Append(from6bit(_currentScan.QData[q_pos, 4] >> 2));
isrc.Append(from6bit(((_currentScan.Data[q_pos + 4] & 0x3) << 4) + (0x0f & (_currentScan.Data[q_pos + 5] >> 4)))); isrc.Append(from6bit(((_currentScan.QData[q_pos, 4] & 0x3) << 4) + (0x0f & (_currentScan.QData[q_pos, 5] >> 4))));
isrc.AppendFormat("{0:x}", _currentScan.Data[q_pos + 5] & 0xf); isrc.AppendFormat("{0:x}", _currentScan.QData[q_pos, 5] & 0xf);
isrc.AppendFormat("{0:x2}", _currentScan.Data[q_pos + 6]); isrc.AppendFormat("{0:x2}", _currentScan.QData[q_pos, 6]);
isrc.AppendFormat("{0:x2}", _currentScan.Data[q_pos + 7]); isrc.AppendFormat("{0:x2}", _currentScan.QData[q_pos, 7]);
isrc.AppendFormat("{0:x}", _currentScan.Data[q_pos + 8] >> 4); isrc.AppendFormat("{0:x}", _currentScan.QData[q_pos, 8] >> 4);
_toc[_currentTrack].ISRC = isrc.ToString(); _toc[_currentTrack].ISRC = isrc.ToString();
} }
break; break;
@@ -302,105 +354,123 @@ namespace CUETools.Ripper.SCSI
return posCount; return posCount;
} }
public unsafe string TestReadCommand() public unsafe bool TestReadCommand()
{
byte[] test = new byte[CB_AUDIO];
string s = "";
fixed (byte* data = test)
{
for (int m = 0; m <= 1; m++)
for (int q = 0; q <= 2; q++)
for (int c = 0; c <= 2; c++)
{ {
Device.MainChannelSelection[] mainmode = { Device.MainChannelSelection.UserData, Device.MainChannelSelection.F8h }; Device.MainChannelSelection[] mainmode = { Device.MainChannelSelection.UserData, Device.MainChannelSelection.F8h };
Device.SubChannelMode[] submode = { Device.SubChannelMode.None, Device.SubChannelMode.QOnly, Device.SubChannelMode.RWMode }; Device.SubChannelMode[] submode = { Device.SubChannelMode.None, Device.SubChannelMode.QOnly, Device.SubChannelMode.RWMode };
Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.None, Device.C2ErrorMode.Mode294, Device.C2ErrorMode.Mode296 }; Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.Mode296, Device.C2ErrorMode.Mode294, Device.C2ErrorMode.None };
Device.CommandStatus st = m_device.ReadCDAndSubChannel(mainmode[m], submode[q], c2mode[c], 1, false, (uint)0, (uint)1, (IntPtr)((void*)data), 3); bool found = false;
s += string.Format("M{0}Q{1}C{2}: {3}\n", m, q, c, (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString())); ScanResults saved = _currentScan;
} _currentScan = new ScanResults(MSECTORS);
} for (int m = 0; m <= 1 && !found; m++)
return s; for (int q = 0; q <= 2 && !found; q++)
} for (int c = 0; c <= 2 && !found; c++)
private void ReorganiseSectors(int sector, int Sectors2Read)
{ {
if (_subChannelMode == Device.SubChannelMode.QOnly && _c2ErrorMode == Device.C2ErrorMode.Mode296) _mainChannelMode = mainmode[m];
{ _subChannelMode = submode[q];
Array.Copy(_readBuffer, 0, _currentScan.Data, (sector - _currentStart) * CB_AUDIO, Sectors2Read * CB_AUDIO); _c2ErrorMode = c2mode[c];
return; m_max_sectors = 1;
Device.CommandStatus st = FetchSectors(0, 1, false);
m_test_result += string.Format("{0}: {1}\n", CurrentReadCommand, (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
found = st == Device.CommandStatus.Success && _subChannelMode != Device.SubChannelMode.RWMode;
} }
// fill Q subchannel m_max_sectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
if (_subChannelMode == Device.SubChannelMode.None) if (found)
for (int n = 1; n <= m_max_sectors; n++)
{ {
Device.CommandStatus st = m_device.ReadSubChannel(2, (uint)sector, (uint)Sectors2Read, ref _subchannelBuffer, 10); Device.CommandStatus st = FetchSectors(0, n, false);
if (st != Device.CommandStatus.Success) if (st != Device.CommandStatus.Success)
throw new Exception("ReadSubChannel: " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString())); {
for (int iSector = 0; iSector < Sectors2Read; iSector++) m_test_result += string.Format("Maximum sectors: {0}, else {1}; max length {2}\n", n - 1, (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()), m_device.MaximumTransferLength);
Array.Copy(_subchannelBuffer, iSector * 16, _currentScan.Data, (sector - _currentStart + iSector + 1) * CB_AUDIO - 16, 16); m_max_sectors = n - 1;
break;
} }
}
m_test_result += "Chosen " + CurrentReadCommand + "\n";
_currentScan = saved;
return found;
}
private unsafe void ReorganiseSectors(int sector, int Sectors2Read)
{
int c2Size = _c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : 296; int c2Size = _c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : 296;
int oldSize = 4 * 588 + c2Size + (_subChannelMode == Device.SubChannelMode.None ? 0 : 16); int oldSize = 4 * 588 + c2Size + (_subChannelMode == Device.SubChannelMode.None ? 0 : 16);
// fill Data & C2 fixed (byte* readBuf = _readBuffer, qBuf = _subchannelBuffer, userData = _currentScan.UserData, c2Data = _currentScan.C2Data, qData = _currentScan.QData)
// TODO: handle other c2 modes here... {
for (int iSector = 0; iSector < Sectors2Read; iSector++) for (int iSector = 0; iSector < Sectors2Read; iSector++)
Array.Copy(_readBuffer, iSector * oldSize, _currentScan.Data, (sector - _currentStart + iSector) * CB_AUDIO, 4 * 588 + 296); {
byte* sectorPtr = readBuf + iSector * oldSize;
byte* userDataPtr = userData + (sector - _currentStart + iSector) * 4 * 588;
byte* c2DataPtr = c2Data + (sector - _currentStart + iSector) * 294;
byte* qDataPtr = qData + (sector - _currentStart + iSector) * 16;
for (int sample = 0; sample < 4 * 588; sample++)
userDataPtr[sample] = sectorPtr[sample];
if (_c2ErrorMode != Device.C2ErrorMode.None)
for (int c2 = 0; c2 < 294; c2++)
c2DataPtr[c2] = sectorPtr[4 * 588 + c2Size - 294];
else
for (int c2 = 0; c2 < 294; c2++)
c2DataPtr[c2] = 0xff;
if (_subChannelMode != Device.SubChannelMode.None)
for (int qi = 0; qi < 16; qi++)
qDataPtr[qi] = sectorPtr[4 * 588 + c2Size];
else
for (int qi = 0; qi < 16; qi++)
qDataPtr[qi] = qBuf[iSector * 16 + qi];
}
}
} }
private unsafe void FetchSectors(int sector, int Sectors2Read) private unsafe Device.CommandStatus FetchSectors(int sector, int Sectors2Read, bool abort)
{ {
Device.CommandStatus st;
fixed (byte* data = _readBuffer) fixed (byte* data = _readBuffer)
{ {
//Device.CommandStatus st; st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout);
//using (Command cmd = new Command(ScsiCommandCode.Read12, 12, (IntPtr)((void*)data), Sectors2Read * 588 * 4, Command.CmdDirection.In, 5 * 60)) }
//{ if (st == Device.CommandStatus.Success)
// //cmd.SetCDB8(1, 4); // force from media {
// cmd.SetCDB32(2, sector); if (_subChannelMode == Device.SubChannelMode.None)
// cmd.SetCDB32(6, Sectors2Read); {
// //cmd.SetCDB8(10, 0x80); // streaming st = m_device.ReadSubChannel(2, (uint)sector, (uint)Sectors2Read, ref _subchannelBuffer, _timeout);
// st= m_device.SendCommand(cmd);
//}
Device.CommandStatus st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), 10);
if (st == Device.CommandStatus.Success) if (st == Device.CommandStatus.Success)
{ {
ReorganiseSectors(sector, Sectors2Read); ReorganiseSectors(sector, Sectors2Read);
return; return st;
} }
if (sector == 0 && _subChannelMode != Device.SubChannelMode.None) }
{ else
_subChannelMode = Device.SubChannelMode.None;
if (_debugMessages)
System.Console.WriteLine("\nFailed to read CD data with subchannel. Switching to ReadCD without subchannel + ReadSubchannel.");
st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), 10);
if (st == Device.CommandStatus.Success)
{ {
ReorganiseSectors(sector, Sectors2Read); ReorganiseSectors(sector, Sectors2Read);
return; return st;
} }
} }
if (sector != 0 && st == Device.CommandStatus.DeviceFailed && m_device.GetSenseAsc() == 0x64 && m_device.GetSenseAscq() == 0x00) if (!abort)
return st;
SCSIException ex = new SCSIException("ReadCD", m_device, st);
if (sector != 0 && Sectors2Read > 1 && st == Device.CommandStatus.DeviceFailed && m_device.GetSenseAsc() == 0x64 && m_device.GetSenseAscq() == 0x00)
{ {
int iErrors = 0; int iErrors = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++) for (int iSector = 0; iSector < Sectors2Read; iSector++)
{ {
st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)(sector + iSector), 1U, (IntPtr)((void*)data), 5); if (FetchSectors(sector + iSector, 1, false) != Device.CommandStatus.Success)
if (st != Device.CommandStatus.Success)
{ {
iErrors ++; iErrors ++;
for (int iPos = 0; iPos < CB_AUDIO; iPos++) for (int i = 0; i < 4 * 588; i++)
_currentScan.Data[(sector + iSector - _currentStart) * CB_AUDIO + iPos] = (iPos == 4 * 588 || (iPos >= 4 * 588 + 2 && iPos < 4 * 588 + 2 + 294)) ? (byte)255 : (byte)0; _currentScan.UserData[sector + iSector - _currentStart, i] = 0;
for (int i = 0; i < 294; i++)
_currentScan.C2Data[sector + iSector - _currentStart, i] = 0xff;
for (int i = 0; i < 16; i++)
_currentScan.QData[sector + iSector - _currentStart, i] = 0;
if (_debugMessages) if (_debugMessages)
System.Console.WriteLine("\nSector lost"); System.Console.WriteLine("\nSector lost");
} else }
ReorganiseSectors(sector+iSector, 1);
} }
if (iErrors < Sectors2Read) if (iErrors < Sectors2Read)
return; return Device.CommandStatus.Success;
}
string status = (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString());
string test = TestReadCommand();
throw new Exception("ReadCD: " + status + "; Autodetect: " + test);
} }
throw new Exception(ex.Message + "; Autodetect: " + m_test_result);
} }
private unsafe void CorrectSectors(int sector, int Sectors2Read, bool findWorst, bool markErrors) private unsafe void CorrectSectors(int sector, int Sectors2Read, bool findWorst, bool markErrors)
@@ -410,20 +480,19 @@ namespace CUETools.Ripper.SCSI
{ {
for (int iPar = 0; iPar < 4 * 588; iPar++) for (int iPar = 0; iPar < 4 * 588; iPar++)
{ {
int dataPos = (sector - _currentStart + iSector) * CB_AUDIO + iPar; int pos = sector - _currentStart + iSector;
int c2Pos = (sector - _currentStart + iSector) * CB_AUDIO + 2 + 4 * 588 + iPar / 8;
int c2Bit = 0x80 >> (iPar % 8); int c2Bit = 0x80 >> (iPar % 8);
Array.Clear(valueScore, 0, 256); Array.Clear(valueScore, 0, 256);
byte bestValue = _currentScan.UserData[pos, iPar];
byte bestValue = _currentScan.Data[dataPos]; valueScore[bestValue] += 1 + (((_currentScan.C2Data[pos, iPar/8] & c2Bit) == 0) ? c2Score : 0);
valueScore[bestValue] += 1 + (((_currentScan.Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
int totalScore = valueScore[bestValue]; int totalScore = valueScore[bestValue];
for (int result = 0; result < _scanResults.Count; result++) for (int result = 0; result < _scanResults.Count; result++)
{ {
byte value = _scanResults[result].Data[dataPos]; byte value = _scanResults[result].UserData[pos, iPar];
valueScore[value] += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0); int score = 1 + (((_scanResults[result].C2Data[pos, iPar/8] & c2Bit) == 0) ? c2Score : 0);
totalScore += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0); valueScore[value] += score;
totalScore += score;
if (valueScore[value] > valueScore[bestValue]) if (valueScore[value] > valueScore[bestValue])
bestValue = value; bestValue = value;
} }
@@ -434,8 +503,8 @@ namespace CUETools.Ripper.SCSI
if (findWorst) if (findWorst)
{ {
for (int result = 0; result < _scanResults.Count; result++) for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality += Math.Min(0, valueScore[_scanResults[result].Data[dataPos]] - c2Score - 1); _scanResults[result].Quality += Math.Min(0, valueScore[_scanResults[result].UserData[pos, iPar]] - c2Score - 1);
_currentScan.Quality += Math.Min(0, valueScore[_currentScan.Data[dataPos]] - c2Score - 1); _currentScan.Quality += Math.Min(0, valueScore[_currentScan.UserData[pos, iPar]] - c2Score - 1);
} }
if (markErrors) if (markErrors)
{ {
@@ -508,6 +577,9 @@ namespace CUETools.Ripper.SCSI
if (_currentStart == MSECTORS * (iSector / MSECTORS)) if (_currentStart == MSECTORS * (iSector / MSECTORS))
return; return;
if (m_test_result == null && !TestReadCommand())
throw new Exception("failed to autodetect read command: " + m_test_result);
_currentStart = MSECTORS * (iSector / MSECTORS); _currentStart = MSECTORS * (iSector / MSECTORS);
_currentEnd = Math.Min(_currentStart + MSECTORS, (int)_toc.AudioLength); _currentEnd = Math.Min(_currentStart + MSECTORS, (int)_toc.AudioLength);
_scanResults = new List<ScanResults>(); _scanResults = new List<ScanResults>();
@@ -522,24 +594,24 @@ namespace CUETools.Ripper.SCSI
for (int pass = 0; pass <= nPasses + nExtraPasses; pass++) for (int pass = 0; pass <= nPasses + nExtraPasses; pass++)
{ {
DateTime PassTime = DateTime.Now, LastFetch = DateTime.Now; DateTime PassTime = DateTime.Now, LastFetch = DateTime.Now;
_currentScan = new ScanResults(MSECTORS, CB_AUDIO); _currentScan = new ScanResults(MSECTORS);
_currentErrorsCount = 0; _currentErrorsCount = 0;
int nSectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1); for (int sector = _currentStart; sector < _currentEnd; sector += m_max_sectors)
for (int sector = _currentStart; sector < _currentEnd; sector += nSectors)
{ {
int Sectors2Read = Math.Min(nSectors, _currentEnd - sector); int Sectors2Read = Math.Min(m_max_sectors, _currentEnd - sector);
int speed = pass == 4 || pass == 5 ? 4 : pass == 8 || pass == 9 ? 2 : pass == 17 || pass == 18 ? 1 : 0; int speed = pass == 4 || pass == 5 ? 4 : pass == 8 || pass == 9 ? 2 : pass == 17 || pass == 18 ? 1 : 0;
if (speed != 0) if (speed != 0)
Thread.Sleep(Math.Max(1, 1000 * nSectors / (75 * speed) - (int)((DateTime.Now - LastFetch).TotalMilliseconds))); Thread.Sleep(Math.Max(1, 1000 * Sectors2Read / (75 * speed) - (int)((DateTime.Now - LastFetch).TotalMilliseconds)));
LastFetch = DateTime.Now; LastFetch = DateTime.Now;
FetchSectors(sector, Sectors2Read); FetchSectors(sector, Sectors2Read, true);
if (ProcessSubchannel(sector, Sectors2Read, pass == 0) == 0 && _subChannelMode != Device.SubChannelMode.None && sector == 0) if (ProcessSubchannel(sector, Sectors2Read, pass == 0) == 0 && _subChannelMode != Device.SubChannelMode.None && sector == 0)
{ {
if (_debugMessages) if (_debugMessages)
System.Console.WriteLine("\nGot no subchannel using ReadCD. Switching to ReadSubchannel."); System.Console.WriteLine("\nGot no subchannel using ReadCD. Switching to ReadSubchannel.");
_subChannelMode = Device.SubChannelMode.None; _subChannelMode = Device.SubChannelMode.None;
FetchSectors(sector, Sectors2Read); // TODO: move to TestReadCommand
FetchSectors(sector, Sectors2Read, true);
} }
CorrectSectors(sector, Sectors2Read, pass > nPasses, pass == nPasses + nExtraPasses); CorrectSectors(sector, Sectors2Read, pass > nPasses, pass == nPasses + nExtraPasses);
if (ReadProgress != null) if (ReadProgress != null)
@@ -710,7 +782,11 @@ namespace CUETools.Ripper.SCSI
{ {
get get
{ {
return m_info.LongDesc; string result = m_device_letter + ": ";
result += "[" + m_inqury_result.VendorIdentification + " " +
m_inqury_result.ProductIdentification + " " +
m_inqury_result.FirmwareVersion + "]";
return result;
} }
} }
@@ -718,7 +794,7 @@ namespace CUETools.Ripper.SCSI
{ {
get get
{ {
return m_info.InquiryData.VendorIdentification.TrimEnd(' ','\0').PadRight(8,' ') + " - " + m_info.InquiryData.ProductIdentification.TrimEnd(' ','\0'); return m_inqury_result.VendorIdentification.TrimEnd(' ', '\0').PadRight(8, ' ') + " - " + m_inqury_result.ProductIdentification.TrimEnd(' ', '\0');
} }
} }
@@ -797,16 +873,31 @@ namespace CUETools.Ripper.SCSI
internal class ScanResults internal class ScanResults
{ {
public byte[] Data; public byte[,] UserData;
public byte[,] C2Data;
public byte[,] QData;
public long Quality; public long Quality;
public ScanResults(int msector, int cbaudio) public ScanResults(int msector)
{ {
Data = new byte[msector * cbaudio]; //byte[] a1 = new byte[msector * 4 * 588];
//byte[] a2 = new byte[msector * 294];
//byte[] a3 = new byte[msector * 16];
UserData = new byte[msector, 4 * 588];
C2Data = new byte[msector, 294];
QData = new byte[msector, 16];
Quality = 0; Quality = 0;
} }
} }
public sealed class SCSIException : Exception
{
public SCSIException(string args, Device device, Device.CommandStatus st)
: base(args + ": " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(device.GetSenseAsc(), device.GetSenseAscq()) : st.ToString()))
{
}
}
public sealed class ReadProgressArgs: EventArgs public sealed class ReadProgressArgs: EventArgs
{ {
public int Position; public int Position;

View File

@@ -76,8 +76,8 @@ namespace JDP
Text = String.Format("{0}, ETA {1}:{2:00}.", e.status, (int)eta.TotalMinutes, eta.Seconds); Text = String.Format("{0}, ETA {1}:{2:00}.", e.status, (int)eta.TotalMinutes, eta.Seconds);
} else } else
Text = e.status; Text = e.status;
progressBar1.Value = (int)e.percentTrack; progressBar1.Value = Math.Max(0,Math.Min(100,(int)(e.percentTrck*100)));
progressBar2.Value = (int)(e.percentDisk*100); progressBar2.Value = Math.Max(0,Math.Min(100,(int)(e.percentDisk*100)));
string inputSuffix = e.output != null ? "=>" : ""; string inputSuffix = e.output != null ? "=>" : "";
if (e.input == null) if (e.input == null)
txtInputFile.Text = inputSuffix; txtInputFile.Text = inputSuffix;

View File

@@ -316,6 +316,8 @@ namespace JDP {
string outDir = Path.GetDirectoryName(pathOut); string outDir = Path.GetDirectoryName(pathOut);
if (cueStyle == CUEStyle.SingleFileWithCUE) if (cueStyle == CUEStyle.SingleFileWithCUE)
cueSheet.SingleFilename = Path.GetFileName(pathOut); cueSheet.SingleFilename = Path.GetFileName(pathOut);
if (outDir == "")
outDir = ".";
bool outputExists = false; bool outputExists = false;
if (outputCUE) if (outputCUE)
@@ -430,8 +432,8 @@ namespace JDP {
{ {
this.BeginInvoke((MethodInvoker)delegate() { this.BeginInvoke((MethodInvoker)delegate() {
toolStripStatusLabel1.Text = e.status; toolStripStatusLabel1.Text = e.status;
toolStripProgressBar1.Value = (int)e.percentTrack; toolStripProgressBar1.Value = Math.Max(0,Math.Min(100,(int)(e.percentTrck*100)));
toolStripProgressBar2.Value = (int)(e.percentDisk*100); toolStripProgressBar2.Value = Math.Max(0,Math.Min(100,(int)(e.percentDisk*100)));
}); });
} }

View File

@@ -393,7 +393,7 @@ namespace CUETools.Processor
public class CUEToolsProgressEventArgs public class CUEToolsProgressEventArgs
{ {
public string status = string.Empty; public string status = string.Empty;
public uint percentTrack = 0; public double percentTrck = 0;
public double percentDisk = 0.0; public double percentDisk = 0.0;
public string input = string.Empty; public string input = string.Empty;
public string output = string.Empty; public string output = string.Empty;
@@ -947,12 +947,12 @@ namespace CUETools.Processor
} }
} }
private void ShowProgress(string status, uint percentTrack, double percentDisk, string input, string output) private void ShowProgress(string status, double percentTrack, double percentDisk, string input, string output)
{ {
if (this.CUEToolsProgress == null) if (this.CUEToolsProgress == null)
return; return;
_progress.status = status; _progress.status = status;
_progress.percentTrack = percentTrack; _progress.percentTrck = percentTrack;
_progress.percentDisk = percentDisk; _progress.percentDisk = percentDisk;
_progress.input = input; _progress.input = input;
_progress.output = output; _progress.output = output;
@@ -960,12 +960,36 @@ namespace CUETools.Processor
} }
#if !MONO #if !MONO
private void CDReadProgress(object sender, ReadProgressArgs e)
{
lock (this)
{
if (_stop)
throw new StopException();
if (_pause)
{
ShowProgress("Paused...", 0, 0, null, null);
Monitor.Wait(this);
}
}
if (this.CUEToolsProgress == null)
return;
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;
_progress.percentDisk = (double)(e.PassStart + (processed + e.Pass * (e.PassEnd - e.PassStart)) / (audioSource.CorrectionQuality + 1)) / audioSource.TOC.AudioLength;
_progress.percentTrck = (double) (e.Position - e.PassStart) / (e.PassEnd - e.PassStart);
_progress.status = string.Format("Ripping @{0:00.00}x {1}", speed, e.Pass > 0 ? " (Retry " + e.Pass.ToString() + ")" : "");
this.CUEToolsProgress(this, _progress);
}
private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e) private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e)
{ {
if (this.CUEToolsProgress == null) if (this.CUEToolsProgress == null)
return; return;
_progress.percentDisk = (1.0 + _progress.percentDisk) / 2; _progress.percentDisk = (1.0 + _progress.percentDisk) / 2;
_progress.percentTrack = 0; _progress.percentTrck = 0;
_progress.input = e.Uri.ToString(); _progress.input = e.Uri.ToString();
_progress.output = null; _progress.output = null;
_progress.status = "Looking up album via MusicBrainz"; _progress.status = "Looking up album via MusicBrainz";
@@ -976,7 +1000,7 @@ namespace CUETools.Processor
{ {
if (this.CUEToolsProgress == null) if (this.CUEToolsProgress == null)
return; return;
_progress.percentTrack = (uint)Math.Round(e.PercentComplete); _progress.percentTrck = e.PercentComplete/100;
this.CUEToolsProgress(this, _progress); this.CUEToolsProgress(this, _progress);
} }
@@ -1149,7 +1173,7 @@ namespace CUETools.Processor
{ {
IAudioSource audioSource; IAudioSource audioSource;
ShowProgress("Analyzing input file...", 0, 0.0, path, null); ShowProgress("Analyzing input file...", 0.0, 0.0, path, null);
#if !MONO #if !MONO
if (_isArchive) if (_isArchive)
{ {
@@ -1193,6 +1217,8 @@ namespace CUETools.Processor
logWriter.WriteLine("Extraction logfile from : {0}", DateTime.Now); logWriter.WriteLine("Extraction logfile from : {0}", DateTime.Now);
logWriter.WriteLine("Used drive : {0}", _driveName); logWriter.WriteLine("Used drive : {0}", _driveName);
logWriter.WriteLine("Read offset correction : {0}", _driveOffset); logWriter.WriteLine("Read offset correction : {0}", _driveOffset);
//logWriter.WriteLine("Read command : {0}", );
//logWriter.WriteLine("Secure mode : {0}", );
if (hdcdDecoder != null && hdcdDecoder.Detected) if (hdcdDecoder != null && hdcdDecoder.Detected)
{ {
hdcd_decoder_statistics stats; hdcd_decoder_statistics stats;
@@ -1949,7 +1975,6 @@ namespace CUETools.Processor
for (iIndex = 0; iIndex <= _toc[iTrack + 1].LastIndex; iIndex++) for (iIndex = 0; iIndex <= _toc[iTrack + 1].LastIndex; iIndex++)
{ {
uint trackPercent = 0, lastTrackPercent = 101;
uint samplesRemIndex = _toc.IndexLength(iTrack + 1, iIndex) * 588; uint samplesRemIndex = _toc.IndexLength(iTrack + 1, iIndex) * 588;
if (iIndex == 1) if (iIndex == 1)
@@ -2007,15 +2032,13 @@ namespace CUETools.Processor
uint copyCount = (uint)Math.Min(Math.Min(samplesRemIndex, samplesRemSource), buffLen); uint copyCount = (uint)Math.Min(Math.Min(samplesRemIndex, samplesRemSource), buffLen);
if (trackLength > 0) if (trackLength > 0 && !_isCD)
{ {
trackPercent = (uint)(currentOffset / 0.01 / trackLength); double trackPercent = (double)currentOffset / trackLength;
double diskPercent = ((float)diskOffset) / diskLength; double diskPercent = (double)diskOffset / diskLength;
if (trackPercent != lastTrackPercent) ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", iIndex > 0 ? iTrack + 1 : iTrack, (uint)(100*trackPercent),
ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", iIndex > 0 ? iTrack + 1 : iTrack, trackPercent,
noOutput ? "Verifying" : "Writing"), trackPercent, diskPercent, noOutput ? "Verifying" : "Writing"), trackPercent, diskPercent,
_isCD ? string.Format("{0}: {1:00} - {2}", audioSource.Path, iTrack + 1, _tracks[iTrack].Title) : audioSource.Path, discardOutput ? null : audioDest.Path); _isCD ? string.Format("{0}: {1:00} - {2}", audioSource.Path, iTrack + 1, _tracks[iTrack].Title) : audioSource.Path, discardOutput ? null : audioDest.Path);
lastTrackPercent = trackPercent;
} }
if (audioSource.Read(sampleBuffer, copyCount) != copyCount) if (audioSource.Read(sampleBuffer, copyCount) != copyCount)
@@ -2301,6 +2324,7 @@ namespace CUETools.Processor
CDDriveReader ripper = new CDDriveReader(); CDDriveReader ripper = new CDDriveReader();
ripper.Open(sourceInfo.Path[0]); ripper.Open(sourceInfo.Path[0]);
ripper.DriveOffset = _driveOffset; ripper.DriveOffset = _driveOffset;
ripper.ReadProgress += new EventHandler<ReadProgressArgs>(CDReadProgress);
audioSource = ripper; audioSource = ripper;
} else } else
if (_isArchive) if (_isArchive)