From a430001f3316799294c404fa7573964a2056ec97 Mon Sep 17 00:00:00 2001 From: chudov Date: Sun, 7 Dec 2008 23:12:01 +0000 Subject: [PATCH] Ripping: read command autodetection --- CUETools.CDImage/CDImage.cs | 4 +- CUETools.Ripper.Console/Program.cs | 6 +- CUETools.Ripper.SCSI/SCSIDrive.cs | 359 ++++++++++++++++++----------- CUETools/frmBatch.cs | 4 +- CUETools/frmCUETools.cs | 6 +- CUEToolsLib/Main.cs | 54 +++-- 6 files changed, 275 insertions(+), 158 deletions(-) diff --git a/CUETools.CDImage/CDImage.cs b/CUETools.CDImage/CDImage.cs index 731651f..e859b6b 100644 --- a/CUETools.CDImage/CDImage.cs +++ b/CUETools.CDImage/CDImage.cs @@ -283,9 +283,7 @@ namespace CUETools.CDImage { get { - return _tracks[TrackCount - 1].IsAudio ? - _tracks[TrackCount - 1].End + 1 : - _tracks[TrackCount - 2].End + 1; + return AudioTracks > 0 ? _tracks[(int)_audioTracks - 1].End + 1U : 0U; } } diff --git a/CUETools.Ripper.Console/Program.cs b/CUETools.Ripper.Console/Program.cs index 08d0a8c..4d1eb92 100644 --- a/CUETools.Ripper.Console/Program.cs +++ b/CUETools.Ripper.Console/Program.cs @@ -49,7 +49,7 @@ namespace CUERipper 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); + 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) ; 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); if (test) { - Console.Write(audioSource.TestReadCommand()); + Console.Write(audioSource.AutoDetectReadCommand); return; } audioSource.DriveOffset = driveOffset; @@ -183,6 +183,8 @@ namespace CUERipper Console.WriteLine("Drive : {0}", audioSource.Path); 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("Disk length : {0}", CDImageLayout.TimeToString(audioSource.TOC.AudioLength)); Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus); diff --git a/CUETools.Ripper.SCSI/SCSIDrive.cs b/CUETools.Ripper.SCSI/SCSIDrive.cs index 390af4c..204f6f0 100644 --- a/CUETools.Ripper.SCSI/SCSIDrive.cs +++ b/CUETools.Ripper.SCSI/SCSIDrive.cs @@ -45,16 +45,19 @@ namespace CUETools.Ripper.SCSI int _driveOffset = 0; int _correctionQuality = 1; int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0; - const int CB_AUDIO = 588 * 4 + 2 + 294 + 16; - const int NSECTORS = 32; - const int MSECTORS = 10000000 / CB_AUDIO; + const int CB_AUDIO = 4 * 588 + 2 + 294 + 16; + const int NSECTORS = 16; + const int MSECTORS = 10000000 / (4 * 588); int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1; Logger m_logger; CDImageLayout _toc; - DeviceInfo m_info; + char m_device_letter; + InquiryResult m_inqury_result; + int m_max_sectors; + int _timeout = 10; Crc16Ccitt _crc; List _scanResults; - ScanResults _currentScan; + ScanResults _currentScan = null; BitArray _errors; int _errorsCount; byte[] _currentData = new byte[MSECTORS * 4 * 588]; @@ -63,6 +66,7 @@ namespace CUETools.Ripper.SCSI Device.MainChannelSelection _mainChannelMode = Device.MainChannelSelection.UserData; Device.SubChannelMode _subChannelMode = Device.SubChannelMode.None; Device.C2ErrorMode _c2ErrorMode = Device.C2ErrorMode.Mode296; + string m_test_result; byte[] _readBuffer = new byte[NSECTORS * CB_AUDIO]; 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 { 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() { m_logger = new Logger(); @@ -115,15 +160,19 @@ namespace CUETools.Ripper.SCSI Device.CommandStatus st; // Open the base device + m_device_letter = Drive; 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"); // Get device info - m_info = DeviceInfo.CreateDevice(Drive + ":"); - if (!m_info.ExtractInfo(m_device)) - throw new Exception("ExtractInfo failed: SCSI error"); + st = m_device.Inquiry(out m_inqury_result); + if (st != Device.CommandStatus.Success || !m_inqury_result.Valid) + 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 //Drive m_drive = new Drive(dev); //DiskOperationError status = m_drive.Initialize(); @@ -195,7 +244,7 @@ namespace CUETools.Ripper.SCSI { 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; for (int iSector = 0; iSector < Sectors2Read; iSector++) { - 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; + int q_pos = sector - _currentStart + iSector; + int ctl = _currentScan.QData[q_pos, 0] >> 4; + int adr = _currentScan.QData[q_pos, 0] & 7; bool preemph = (ctl & 1) == 1; switch (adr) { case 1: // current position { - 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 iTrack = fromBCD(_currentScan.QData[q_pos, 1]); + int iIndex = fromBCD(_currentScan.QData[q_pos, 2]); + int mm = fromBCD(_currentScan.QData[q_pos, 7]); + int ss = fromBCD(_currentScan.QData[q_pos, 8]); + int ff = fromBCD(_currentScan.QData[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); + 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; - 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]) + if (_currentScan.QData[q_pos, 10] != 0 && _currentScan.QData[q_pos, 11] != 0 && + ((crc & 0xff) != _currentScan.QData[q_pos, 11] || + (crc >> 8) != _currentScan.QData[q_pos, 10]) ) { if (_debugMessages) @@ -276,7 +328,7 @@ namespace CUETools.Ripper.SCSI { StringBuilder catalog = new StringBuilder(); 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); } break; @@ -284,16 +336,16 @@ namespace CUETools.Ripper.SCSI if (_toc[_currentTrack].ISRC == null) { StringBuilder isrc = new StringBuilder(); - isrc.Append(from6bit(_currentScan.Data[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.Data[q_pos + 2] & 0xf) << 2) + (0x03 & (_currentScan.Data[q_pos + 3] >> 6)))); - isrc.Append(from6bit((_currentScan.Data[q_pos + 3] & 0x3f))); - isrc.Append(from6bit(_currentScan.Data[q_pos + 4] >> 2)); - isrc.Append(from6bit(((_currentScan.Data[q_pos + 4] & 0x3) << 4) + (0x0f & (_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); + isrc.Append(from6bit(_currentScan.QData[q_pos, 1] >> 2)); + isrc.Append(from6bit(((_currentScan.QData[q_pos, 1] & 0x3) << 4) + (0x0f & (_currentScan.QData[q_pos, 2] >> 4)))); + isrc.Append(from6bit(((_currentScan.QData[q_pos, 2] & 0xf) << 2) + (0x03 & (_currentScan.QData[q_pos, 3] >> 6)))); + isrc.Append(from6bit((_currentScan.QData[q_pos, 3] & 0x3f))); + isrc.Append(from6bit(_currentScan.QData[q_pos, 4] >> 2)); + isrc.Append(from6bit(((_currentScan.QData[q_pos, 4] & 0x3) << 4) + (0x0f & (_currentScan.QData[q_pos, 5] >> 4)))); + isrc.AppendFormat("{0:x}", _currentScan.QData[q_pos, 5] & 0xf); + isrc.AppendFormat("{0:x2}", _currentScan.QData[q_pos, 6]); + isrc.AppendFormat("{0:x2}", _currentScan.QData[q_pos, 7]); + isrc.AppendFormat("{0:x}", _currentScan.QData[q_pos, 8] >> 4); _toc[_currentTrack].ISRC = isrc.ToString(); } break; @@ -302,105 +354,123 @@ namespace CUETools.Ripper.SCSI 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.SubChannelMode[] submode = { Device.SubChannelMode.None, Device.SubChannelMode.QOnly, Device.SubChannelMode.RWMode }; - Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.None, Device.C2ErrorMode.Mode294, Device.C2ErrorMode.Mode296 }; - Device.CommandStatus st = m_device.ReadCDAndSubChannel(mainmode[m], submode[q], c2mode[c], 1, false, (uint)0, (uint)1, (IntPtr)((void*)data), 3); - 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())); - } - } - return s; + Device.MainChannelSelection[] mainmode = { Device.MainChannelSelection.UserData, Device.MainChannelSelection.F8h }; + Device.SubChannelMode[] submode = { Device.SubChannelMode.None, Device.SubChannelMode.QOnly, Device.SubChannelMode.RWMode }; + Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.Mode296, Device.C2ErrorMode.Mode294, Device.C2ErrorMode.None }; + bool found = false; + ScanResults saved = _currentScan; + _currentScan = new ScanResults(MSECTORS); + for (int m = 0; m <= 1 && !found; m++) + for (int q = 0; q <= 2 && !found; q++) + for (int c = 0; c <= 2 && !found; c++) + { + _mainChannelMode = mainmode[m]; + _subChannelMode = submode[q]; + _c2ErrorMode = c2mode[c]; + 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; + } + m_max_sectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1); + if (found) + for (int n = 1; n <= m_max_sectors; n++) + { + Device.CommandStatus st = FetchSectors(0, n, false); + if (st != Device.CommandStatus.Success) + { + 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); + m_max_sectors = n - 1; + break; + } + } + m_test_result += "Chosen " + CurrentReadCommand + "\n"; + _currentScan = saved; + return found; } - - private void ReorganiseSectors(int sector, int Sectors2Read) + private unsafe void ReorganiseSectors(int sector, int Sectors2Read) { - if (_subChannelMode == Device.SubChannelMode.QOnly && _c2ErrorMode == Device.C2ErrorMode.Mode296) - { - Array.Copy(_readBuffer, 0, _currentScan.Data, (sector - _currentStart) * CB_AUDIO, Sectors2Read * CB_AUDIO); - return; - } - // fill Q subchannel - if (_subChannelMode == Device.SubChannelMode.None) - { - Device.CommandStatus st = m_device.ReadSubChannel(2, (uint)sector, (uint)Sectors2Read, ref _subchannelBuffer, 10); - 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++) - Array.Copy(_subchannelBuffer, iSector * 16, _currentScan.Data, (sector - _currentStart + iSector + 1) * CB_AUDIO - 16, 16); - } int c2Size = _c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : 296; int oldSize = 4 * 588 + c2Size + (_subChannelMode == Device.SubChannelMode.None ? 0 : 16); - // fill Data & C2 - // TODO: handle other c2 modes here... - for (int iSector = 0; iSector < Sectors2Read; iSector++) - Array.Copy(_readBuffer, iSector * oldSize, _currentScan.Data, (sector - _currentStart + iSector) * CB_AUDIO, 4 * 588 + 296); + fixed (byte* readBuf = _readBuffer, qBuf = _subchannelBuffer, userData = _currentScan.UserData, c2Data = _currentScan.C2Data, qData = _currentScan.QData) + { + for (int iSector = 0; iSector < Sectors2Read; iSector++) + { + 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) { - //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(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), 10); - if (st == Device.CommandStatus.Success) + st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout); + } + if (st == Device.CommandStatus.Success) + { + if (_subChannelMode == Device.SubChannelMode.None) { - ReorganiseSectors(sector, Sectors2Read); - return; - } - if (sector == 0 && _subChannelMode != Device.SubChannelMode.None) - { - _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); + st = m_device.ReadSubChannel(2, (uint)sector, (uint)Sectors2Read, ref _subchannelBuffer, _timeout); if (st == Device.CommandStatus.Success) { ReorganiseSectors(sector, Sectors2Read); - return; + return st; } } - if (sector != 0 && st == Device.CommandStatus.DeviceFailed && m_device.GetSenseAsc() == 0x64 && m_device.GetSenseAscq() == 0x00) + else { - int iErrors = 0; - 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 (st != Device.CommandStatus.Success) - { - iErrors ++; - for (int iPos = 0; iPos < CB_AUDIO; iPos++) - _currentScan.Data[(sector + iSector - _currentStart) * CB_AUDIO + iPos] = (iPos == 4 * 588 || (iPos >= 4 * 588 + 2 && iPos < 4 * 588 + 2 + 294)) ? (byte)255 : (byte)0; - if (_debugMessages) - System.Console.WriteLine("\nSector lost"); - } else - ReorganiseSectors(sector+iSector, 1); - } - if (iErrors < Sectors2Read) - return; + ReorganiseSectors(sector, Sectors2Read); + return st; } - 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); } + 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; + for (int iSector = 0; iSector < Sectors2Read; iSector++) + { + if (FetchSectors(sector + iSector, 1, false) != Device.CommandStatus.Success) + { + iErrors ++; + for (int i = 0; i < 4 * 588; i++) + _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) + System.Console.WriteLine("\nSector lost"); + } + } + if (iErrors < Sectors2Read) + return Device.CommandStatus.Success; + } + throw new Exception(ex.Message + "; Autodetect: " + m_test_result); } 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++) { - int dataPos = (sector - _currentStart + iSector) * CB_AUDIO + iPar; - int c2Pos = (sector - _currentStart + iSector) * CB_AUDIO + 2 + 4 * 588 + iPar / 8; + int pos = sector - _currentStart + iSector; 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); + byte bestValue = _currentScan.UserData[pos, iPar]; + valueScore[bestValue] += 1 + (((_currentScan.C2Data[pos, iPar/8] & 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); + byte value = _scanResults[result].UserData[pos, iPar]; + int score = 1 + (((_scanResults[result].C2Data[pos, iPar/8] & c2Bit) == 0) ? c2Score : 0); + valueScore[value] += score; + totalScore += score; if (valueScore[value] > valueScore[bestValue]) bestValue = value; } @@ -434,8 +503,8 @@ namespace CUETools.Ripper.SCSI 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); + _scanResults[result].Quality += Math.Min(0, valueScore[_scanResults[result].UserData[pos, iPar]] - c2Score - 1); + _currentScan.Quality += Math.Min(0, valueScore[_currentScan.UserData[pos, iPar]] - c2Score - 1); } if (markErrors) { @@ -508,6 +577,9 @@ namespace CUETools.Ripper.SCSI if (_currentStart == MSECTORS * (iSector / MSECTORS)) return; + if (m_test_result == null && !TestReadCommand()) + throw new Exception("failed to autodetect read command: " + m_test_result); + _currentStart = MSECTORS * (iSector / MSECTORS); _currentEnd = Math.Min(_currentStart + MSECTORS, (int)_toc.AudioLength); _scanResults = new List(); @@ -522,24 +594,24 @@ namespace CUETools.Ripper.SCSI for (int pass = 0; pass <= nPasses + nExtraPasses; pass++) { DateTime PassTime = DateTime.Now, LastFetch = DateTime.Now; - _currentScan = new ScanResults(MSECTORS, CB_AUDIO); + _currentScan = new ScanResults(MSECTORS); _currentErrorsCount = 0; - int nSectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1); - for (int sector = _currentStart; sector < _currentEnd; sector += nSectors) + for (int sector = _currentStart; sector < _currentEnd; sector += m_max_sectors) { - 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; 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; - FetchSectors(sector, Sectors2Read); + FetchSectors(sector, Sectors2Read, true); if (ProcessSubchannel(sector, Sectors2Read, pass == 0) == 0 && _subChannelMode != Device.SubChannelMode.None && sector == 0) { if (_debugMessages) System.Console.WriteLine("\nGot no subchannel using ReadCD. Switching to ReadSubchannel."); _subChannelMode = Device.SubChannelMode.None; - FetchSectors(sector, Sectors2Read); + // TODO: move to TestReadCommand + FetchSectors(sector, Sectors2Read, true); } CorrectSectors(sector, Sectors2Read, pass > nPasses, pass == nPasses + nExtraPasses); if (ReadProgress != null) @@ -710,7 +782,11 @@ namespace CUETools.Ripper.SCSI { 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 { - 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 { - public byte[] Data; + public byte[,] UserData; + public byte[,] C2Data; + public byte[,] QData; 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; } } + 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 int Position; diff --git a/CUETools/frmBatch.cs b/CUETools/frmBatch.cs index bc2505f..6847b95 100644 --- a/CUETools/frmBatch.cs +++ b/CUETools/frmBatch.cs @@ -76,8 +76,8 @@ namespace JDP Text = String.Format("{0}, ETA {1}:{2:00}.", e.status, (int)eta.TotalMinutes, eta.Seconds); } else Text = e.status; - progressBar1.Value = (int)e.percentTrack; - progressBar2.Value = (int)(e.percentDisk*100); + progressBar1.Value = Math.Max(0,Math.Min(100,(int)(e.percentTrck*100))); + progressBar2.Value = Math.Max(0,Math.Min(100,(int)(e.percentDisk*100))); string inputSuffix = e.output != null ? "=>" : ""; if (e.input == null) txtInputFile.Text = inputSuffix; diff --git a/CUETools/frmCUETools.cs b/CUETools/frmCUETools.cs index 9547794..47adc3b 100644 --- a/CUETools/frmCUETools.cs +++ b/CUETools/frmCUETools.cs @@ -316,6 +316,8 @@ namespace JDP { string outDir = Path.GetDirectoryName(pathOut); if (cueStyle == CUEStyle.SingleFileWithCUE) cueSheet.SingleFilename = Path.GetFileName(pathOut); + if (outDir == "") + outDir = "."; bool outputExists = false; if (outputCUE) @@ -430,8 +432,8 @@ namespace JDP { { this.BeginInvoke((MethodInvoker)delegate() { toolStripStatusLabel1.Text = e.status; - toolStripProgressBar1.Value = (int)e.percentTrack; - toolStripProgressBar2.Value = (int)(e.percentDisk*100); + toolStripProgressBar1.Value = Math.Max(0,Math.Min(100,(int)(e.percentTrck*100))); + toolStripProgressBar2.Value = Math.Max(0,Math.Min(100,(int)(e.percentDisk*100))); }); } diff --git a/CUEToolsLib/Main.cs b/CUEToolsLib/Main.cs index d88d8c9..06c8f80 100644 --- a/CUEToolsLib/Main.cs +++ b/CUEToolsLib/Main.cs @@ -393,7 +393,7 @@ namespace CUETools.Processor public class CUEToolsProgressEventArgs { public string status = string.Empty; - public uint percentTrack = 0; + public double percentTrck = 0; public double percentDisk = 0.0; public string input = 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) return; _progress.status = status; - _progress.percentTrack = percentTrack; + _progress.percentTrck = percentTrack; _progress.percentDisk = percentDisk; _progress.input = input; _progress.output = output; @@ -960,12 +960,36 @@ namespace CUETools.Processor } #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) { if (this.CUEToolsProgress == null) return; _progress.percentDisk = (1.0 + _progress.percentDisk) / 2; - _progress.percentTrack = 0; + _progress.percentTrck = 0; _progress.input = e.Uri.ToString(); _progress.output = null; _progress.status = "Looking up album via MusicBrainz"; @@ -976,7 +1000,7 @@ namespace CUETools.Processor { if (this.CUEToolsProgress == null) return; - _progress.percentTrack = (uint)Math.Round(e.PercentComplete); + _progress.percentTrck = e.PercentComplete/100; this.CUEToolsProgress(this, _progress); } @@ -1149,7 +1173,7 @@ namespace CUETools.Processor { IAudioSource audioSource; - ShowProgress("Analyzing input file...", 0, 0.0, path, null); + ShowProgress("Analyzing input file...", 0.0, 0.0, path, null); #if !MONO if (_isArchive) { @@ -1193,6 +1217,8 @@ namespace CUETools.Processor logWriter.WriteLine("Extraction logfile from : {0}", DateTime.Now); logWriter.WriteLine("Used drive : {0}", _driveName); logWriter.WriteLine("Read offset correction : {0}", _driveOffset); + //logWriter.WriteLine("Read command : {0}", ); + //logWriter.WriteLine("Secure mode : {0}", ); if (hdcdDecoder != null && hdcdDecoder.Detected) { hdcd_decoder_statistics stats; @@ -1949,7 +1975,6 @@ namespace CUETools.Processor for (iIndex = 0; iIndex <= _toc[iTrack + 1].LastIndex; iIndex++) { - uint trackPercent = 0, lastTrackPercent = 101; uint samplesRemIndex = _toc.IndexLength(iTrack + 1, iIndex) * 588; if (iIndex == 1) @@ -2007,15 +2032,13 @@ namespace CUETools.Processor uint copyCount = (uint)Math.Min(Math.Min(samplesRemIndex, samplesRemSource), buffLen); - if (trackLength > 0) + if (trackLength > 0 && !_isCD) { - trackPercent = (uint)(currentOffset / 0.01 / trackLength); - double diskPercent = ((float)diskOffset) / diskLength; - if (trackPercent != lastTrackPercent) - ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", iIndex > 0 ? iTrack + 1 : iTrack, trackPercent, - 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); - lastTrackPercent = trackPercent; + double trackPercent = (double)currentOffset / trackLength; + double diskPercent = (double)diskOffset / diskLength; + ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", iIndex > 0 ? iTrack + 1 : iTrack, (uint)(100*trackPercent), + 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); } if (audioSource.Read(sampleBuffer, copyCount) != copyCount) @@ -2301,6 +2324,7 @@ namespace CUETools.Processor CDDriveReader ripper = new CDDriveReader(); ripper.Open(sourceInfo.Path[0]); ripper.DriveOffset = _driveOffset; + ripper.ReadProgress += new EventHandler(CDReadProgress); audioSource = ripper; } else if (_isArchive)