Files
cuetools.net/CUETools.Ripper.SCSI/SCSIDrive.cs

1310 lines
45 KiB
C#
Raw Normal View History

2008-11-26 18:57:40 +00:00
// ****************************************************************************
//
// CUERipper
Bump copyright year to 2020 The copyright year was last time updated in 2018. There is some cleanup involved in this commit and the next copyright year update is supposed to be simpler (i.e. substitute "-2020"). - Substitute occurrences of "-2018" with "-2020" using: git grep -I -l -e '-2018' -- ':(exclude)*.bak' | xargs \ sed -b -i -e 's/-2018/-2020/g' - Update special cases: CUEPlayer git grep -I -l -e 'Grigory Chudov 2010' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2010/2010-2020 Grigory Chudov/g' CUERipper git grep -I -l -e '2008-2009' -- | xargs \ sed -b -i -e 's/2008-2009/2008-2020/g' CUETools, CUETools.FLACCL.cmd git grep -I -l -e '2008-2010' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/2008-2010/2008-2020/g' git grep -I -l -e '2010-2013' -- | xargs \ sed -b -i -e 's/2010-2013/2010-2020/g' CUETools.ChaptersToCue git grep -I -l -e 'Grigory Chudov 2017' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2017/2017-2020 Grigory Chudov/g' CUETools.CTDB.EACPlugin git grep -I -l -e 'Grigory Chudov 2012' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2012/2012-2020 Grigory Chudov/g' git grep -I -l -e '2011-12' -- | xargs \ sed -b -i -e 's/2011-12/2011-2020/g' CUETools.Codecs.FLACCL git grep -I -l -e '2009-2010' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/2009-2010/2009-2020/g' CUETools.eac3ui (BluTools) git grep -I -l -e '© 2018' -- | xargs \ sed -b -i -e 's/© 2018/© 2018-2020 Grigory Chudov/g' CUETools.Flake git grep -I -l -e ' 2009-2014 Gr' -- | xargs \ sed -b -i -e 's/ 2009-2014 Gr/ 2009-2020 Gr/g' CUETools.Processor git grep -I -l -e ' 2008-2013 Gr' -- | xargs \ sed -b -i -e 's/ 2008-2013 Gr/ 2008-2020 Gr/g' CUETools.Ripper.Console git grep -I -l -e ' 2008-10 Gr' -- | xargs \ sed -b -i -e 's/ 2008-10 Gr/ 2008-2020 Gr/g' CUETools.Ripper.Console, CUETools.Ripper.SCSI git grep -I -l -e ' 2008-13 Gr' -- | xargs \ sed -b -i -e 's/ 2008-13 Gr/ 2008-2020 Gr/g' Single year entries: 2008, 2009, 2010, 2011, 2017, 2018 git grep -I -l -e ' 2008 Gr' -- | xargs \ sed -b -i -e 's/ 2008 Gr/ 2008-2020 Gr/g' git grep -I -l -e ' 2009 Gr' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/ 2009 Gr/ 2009-2020 Gr/g' git grep -I -l -e ' 2010 Gr' -- | xargs \ sed -b -i -e 's/ 2010 Gr/ 2010-2020 Gr/g' git grep -I -l -e ' 2011 Gr' -- | xargs \ sed -b -i -e 's/ 2011 Gr/ 2011-2020 Gr/g' git grep -I -l -e ' 2017 Gr' -- | xargs \ sed -b -i -e 's/ 2017 Gr/ 2017-2020 Gr/g' git grep -I -l -e ' 2018 Gr' -- | xargs \ sed -b -i -e 's/ 2018 Gr/ 2018-2020 Gr/g' Fix typo in copyright year of CUETools.Codecs.WMA/AudioDecoder.cs: Copyright (c) 20139 Grigory Chudov git grep -I -lw -e '20139' -- | xargs \ sed -b -i -e 's/20139/2013-2020/g'
2020-01-30 18:13:46 +01:00
// Copyright (C) 2008-2020 Grigory Chudov (gchudov@gmail.com)
2008-11-26 18:57:40 +00:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// ****************************************************************************
using System;
using System.Runtime.InteropServices;
2008-12-06 05:44:14 +00:00
using System.Collections;
2008-11-26 18:57:40 +00:00
using System.Collections.Generic;
using System.Text;
using System.IO;
2008-11-26 18:57:40 +00:00
using Bwg.Scsi;
using Bwg.Logging;
using CUETools.CDImage;
using CUETools.Codecs;
2010-02-08 01:29:31 +00:00
using CUETools.Ripper;
2008-12-07 00:33:20 +00:00
using System.Threading;
2008-11-26 18:57:40 +00:00
namespace CUETools.Ripper.SCSI
{
/// <summary>
///
/// </summary>
2010-02-08 01:29:31 +00:00
public class CDDriveReader : ICDRipper
2008-11-26 18:57:40 +00:00
{
byte[] cdtext = null;
private Device m_device;
2008-11-26 20:20:41 +00:00
int _sampleOffset = 0;
int _driveOffset = 0;
2008-12-06 05:44:14 +00:00
int _correctionQuality = 1;
int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0;
2008-12-07 23:12:01 +00:00
const int CB_AUDIO = 4 * 588 + 2 + 294 + 16;
const int NSECTORS = 16;
//const int MSECTORS = 5*1024*1024 / (4 * 588);
const int MSECTORS = 2400;
Logger m_logger;
CDImageLayout _toc;
CDImageLayout _toc2;
2008-12-07 23:12:01 +00:00
char m_device_letter;
InquiryResult m_inqury_result;
int m_max_sectors;
int _timeout = 10;
2008-12-06 05:44:14 +00:00
Crc16Ccitt _crc;
2010-02-28 21:04:34 +00:00
public long[,,] UserData;
public byte[,] C2Count;
2010-02-23 15:15:08 +00:00
public long[] byte2long;
BitArray m_failedSectors;
byte[] m_retryCount;
2010-02-28 21:04:34 +00:00
AudioBuffer currentData = new AudioBuffer(AudioPCMConfig.RedBook, MSECTORS * 588);
2008-12-10 06:48:38 +00:00
short[] _valueScore = new short[256];
2008-12-07 00:33:20 +00:00
bool _debugMessages = false;
2008-12-10 06:48:38 +00:00
ReadCDCommand _readCDCommand = ReadCDCommand.Unknown;
ReadCDCommand _forceReadCommand = ReadCDCommand.Unknown;
2008-12-07 00:33:20 +00:00
Device.MainChannelSelection _mainChannelMode = Device.MainChannelSelection.UserData;
Device.C2ErrorMode _c2ErrorMode = Device.C2ErrorMode.Mode296;
2008-12-10 06:48:38 +00:00
string _autodetectResult;
2008-12-07 00:33:20 +00:00
byte[] _readBuffer = new byte[NSECTORS * CB_AUDIO];
2012-01-07 00:17:00 +00:00
byte[] _subchannelBuffer = new byte[NSECTORS * CB_AUDIO];
bool _qChannelInBCD = true;
2008-12-06 05:44:14 +00:00
private ReadProgressArgs progressArgs = new ReadProgressArgs();
2008-12-06 05:44:14 +00:00
public event EventHandler<ReadProgressArgs> ReadProgress;
2008-11-26 18:57:40 +00:00
public IAudioDecoderSettings Settings => null;
public CDImageLayout TOC
2008-11-26 18:57:40 +00:00
{
get
{
return gapsDetected && _toc2 != null ? _toc2 : _toc;
2008-11-26 18:57:40 +00:00
}
}
public byte[] RetryCount
{
get
{
return m_retryCount;
}
}
2008-12-06 05:44:14 +00:00
public BitArray FailedSectors
{
get
{
if (m_failedSectors == null)
{
m_failedSectors = new BitArray((int)_toc.AudioLength);
for (int i = 0; i < m_failedSectors.Length; i++)
m_failedSectors[i] = m_retryCount[i] == (16 << _correctionQuality) + 1;
}
return m_failedSectors;
}
}
2008-12-06 05:44:14 +00:00
2008-12-07 23:12:01 +00:00
public int Timeout
{
get
{
return _timeout;
}
set
{
_timeout = value;
}
}
2008-12-07 00:33:20 +00:00
public bool DebugMessages
{
get
{
return _debugMessages;
}
set
{
_debugMessages = value;
}
}
2008-12-07 23:12:01 +00:00
public string AutoDetectReadCommand
{
get
{
TestReadCommand();
return _autodetectResult;
2008-12-07 23:12:01 +00:00
}
}
2008-12-10 06:48:38 +00:00
public bool ForceD8
2008-12-07 23:12:01 +00:00
{
get
{
2008-12-10 06:48:38 +00:00
return _forceReadCommand == ReadCDCommand.ReadCdD8h;
}
set
{
_forceReadCommand = value ? ReadCDCommand.ReadCdD8h : ReadCDCommand.Unknown;
}
}
public bool ForceBE
{
get
{
return _forceReadCommand == ReadCDCommand.ReadCdBEh;
}
set
{
_forceReadCommand = value ? ReadCDCommand.ReadCdBEh : ReadCDCommand.Unknown;
2008-12-07 23:12:01 +00:00
}
}
2008-12-10 06:48:38 +00:00
public string CurrentReadCommand
2008-12-07 23:12:01 +00:00
{
get
{
2008-12-10 06:48:38 +00:00
return _readCDCommand == ReadCDCommand.Unknown ? "unknown" :
string.Format("{0}, {1:X2}h, {2}{3}, {4} blocks at a time",
2008-12-10 06:48:38 +00:00
(_readCDCommand == ReadCDCommand.ReadCdBEh ? "BEh" : "D8h"),
(_mainChannelMode == Device.MainChannelSelection.UserData ? 0x10 : 0xF8) +
(_c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 2 : 4),
(_gapDetection == GapDetectionMethod.ReadCD ? "BEh" : _gapDetection == GapDetectionMethod.ReadSubchannel ? "42h" : ""),
_qChannelInBCD ? "" : "nonBCD",
2008-12-10 06:48:38 +00:00
m_max_sectors);
2008-12-07 23:12:01 +00:00
}
}
2008-11-26 18:57:40 +00:00
public CDDriveReader()
{
m_logger = new Logger();
2008-12-06 05:44:14 +00:00
_crc = new Crc16Ccitt(InitialCrcValue.Zeros);
2010-02-23 15:15:08 +00:00
byte2long = new long[256];
for (long i = 0; i < 256; i++)
{
long bl = 0;
for (int b = 0; b < 8; b++)
bl += ((i >> b) & 1) << (b << 3);
byte2long[i] = bl;
}
2008-11-26 18:57:40 +00:00
}
public bool Open(char Drive)
{
Device.CommandStatus st;
m_inqury_result = null;
2008-11-26 18:57:40 +00:00
// Open the base device
2008-12-07 23:12:01 +00:00
m_device_letter = Drive;
if (m_device != null)
Close();
2008-11-26 18:57:40 +00:00
m_device = new Device(m_logger);
2008-12-07 23:12:01 +00:00
if (!m_device.Open(m_device_letter))
throw new ReadCDException(Resource1.DeviceOpenError, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
//throw new ReadCDException(Resource1.DeviceOpenError + ": " + WinDev.Win32ErrorToString(m_device.LastError));
2008-11-26 18:57:40 +00:00
2008-11-30 23:47:13 +00:00
// Get device info
2008-12-07 23:12:01 +00:00
st = m_device.Inquiry(out m_inqury_result);
if (st != Device.CommandStatus.Success)
throw new SCSIException(Resource1.DeviceInquiryError, m_device, st);
if (!m_inqury_result.Valid || m_inqury_result.PeripheralQualifier != 0 || m_inqury_result.PeripheralDeviceType != Device.MMCDeviceType)
throw new ReadCDException(Resource1.DeviceNotMMC);
2008-12-07 23:12:01 +00:00
m_max_sectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
2008-11-26 18:57:40 +00:00
//// Open/Initialize the driver
//Drive m_drive = new Drive(dev);
//DiskOperationError status = m_drive.Initialize();
//if (status != null)
// throw new Exception("SCSI error");
// {
//Drive.FeatureState readfeature = m_drive.GetFeatureState(Feature.FeatureType.CDRead);
//if (readfeature == Drive.FeatureState.Error || readfeature == Drive.FeatureState.NotPresent)
// throw new Exception("SCSI error");
// }{
//st = m_device.GetConfiguration(Device.GetConfigType.OneFeature, 0, out flist);
//if (st != Device.CommandStatus.Success)
// return CreateErrorObject(st, m_device);
//Feature f = flist.Features[0];
//ParseProfileList(f.Data);
// }
2008-12-06 05:44:14 +00:00
//SpeedDescriptorList speed_list;
//st = m_device.GetSpeed(out speed_list);
//if (st != Device.CommandStatus.Success)
// throw new Exception("GetSpeed failed: SCSI error");
2008-12-10 06:48:38 +00:00
//m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, (ushort)(0x7fff), (ushort)(0x7fff));
2008-12-06 05:44:14 +00:00
//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()));
2008-11-26 18:57:40 +00:00
2008-12-06 05:44:14 +00:00
//st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, 32767/*Device.OptimumSpeed*/, Device.OptimumSpeed);
//if (st != Device.CommandStatus.Success)
// throw new Exception("SetCdSpeed failed: SCSI error");
2008-11-26 18:57:40 +00:00
IList<TocEntry> toc;
st = m_device.ReadToc((byte)0, false, out toc);
if (st != Device.CommandStatus.Success)
throw new SCSIException(Resource1.ReadTOCError, m_device, st);
//throw new Exception("ReadTOC: " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
2008-11-26 18:57:40 +00:00
//byte[] qdata = null;
//st = m_device.ReadPMA(out qdata);
//if (st != Device.CommandStatus.Success)
// throw new SCSIException("ReadPMA", m_device, st);
2010-03-30 02:41:43 +00:00
//st = m_device.ReadCDText(out cdtext, _timeout);
2008-11-26 18:57:40 +00:00
// new CDTextEncoderDecoder
_toc2 = null;
_toc = new CDImageLayout();
2008-11-26 18:57:40 +00:00
for (int iTrack = 0; iTrack < toc.Count - 1; iTrack++)
_toc.AddTrack(new CDTrack((uint)iTrack + 1,
toc[iTrack].StartSector,
toc[iTrack + 1].StartSector - toc[iTrack].StartSector -
2008-12-06 05:44:14 +00:00
((toc[iTrack + 1].Control < 4 || iTrack + 1 == toc.Count - 1) ? 0U : 152U * 75U),
toc[iTrack].Control < 4,
(toc[iTrack].Control & 1) == 1));
2008-12-06 05:44:14 +00:00
if (_toc.AudioLength > 0)
{
if (_toc[1].IsAudio)
_toc[1][0].Start = 0;
Position = 0;
} else
throw new ReadCDException(Resource1.NoAudio);
UserData = new long[MSECTORS, 2, 4 * 588];
C2Count = new byte[MSECTORS, 294];
2008-11-26 18:57:40 +00:00
return true;
}
public void Close()
{
UserData = null;
C2Count = null;
2009-03-22 16:59:05 +00:00
if (m_device != null)
m_device.Close();
2008-11-30 23:47:13 +00:00
m_device = null;
2008-11-26 18:57:40 +00:00
_toc = null;
_toc2 = null;
gapsDetected = false;
readCommandFound = false;
2010-02-28 21:04:34 +00:00
_currentStart = -1;
_currentEnd = -1;
2008-11-26 18:57:40 +00:00
}
2009-03-22 16:59:05 +00:00
public void Dispose()
{
Close();
}
2008-11-26 18:57:40 +00:00
public int BestBlockSize
{
get
{
2008-12-07 23:12:01 +00:00
return m_max_sectors * 588;
2008-11-26 18:57:40 +00:00
}
}
private enum GapDetectionMethod
2008-11-26 20:20:41 +00:00
{
ReadCD,
ReadSubchannel,
None
}
private GapDetectionMethod _gapDetection = GapDetectionMethod.None;
private unsafe void LocateLastSector(int sec0, int sec1, int iTrack, int iIndex, ref int maxIndex, out int pos)
{
if (sec1 <= sec0)
2008-11-26 20:20:41 +00:00
{
pos = Math.Min(sec0, sec1);
return;
}
int msector = (sec0 + sec1) / 2;
int fsector = Math.Max(sec0, msector - 1);
int lsector = Math.Min(sec1, msector + 3);
if (lsector >= fsector && _gapDetection == GapDetectionMethod.ReadCD)
{
Device.CommandStatus st = m_device.ReadSubChannel(2, (uint)fsector, (uint)(lsector - fsector + 1), ref _subchannelBuffer, _timeout);
if (st != Device.CommandStatus.Success)
lsector = fsector - 1;
}
fixed (byte * data = _subchannelBuffer)
for (int sector = fsector; sector <= lsector; sector++)
{
Device.CommandStatus st = Device.CommandStatus.Success;
int sTrack, sIndex, sPos, ctl;
switch (_gapDetection)
{
case GapDetectionMethod.ReadSubchannel:
2008-12-06 05:44:14 +00:00
{
// seek to given sector
if (_readCDCommand == ReadCDCommand.ReadCdBEh)
st = m_device.ReadCDAndSubChannel(_mainChannelMode, Device.SubChannelMode.None, _c2ErrorMode, 1, false, (uint)sector, 1, (IntPtr)((void*)data), _timeout);
else
st = m_device.ReadCDDA(Device.SubChannelMode.None, (uint)sector, 1, (IntPtr)((void*)data), _timeout);
if (st != Device.CommandStatus.Success)
continue;
st = m_device.ReadSubChannel42(1, 0, ref _subchannelBuffer, 0, _timeout);
// x x x x 01 adrctl tr ind abs abs abs rel rel rel
if (st != Device.CommandStatus.Success)
continue;
if (_subchannelBuffer[0] != 0 || _subchannelBuffer[2] != 0 || _subchannelBuffer[3] != 12 || _subchannelBuffer[4] != 1)
continue;
ctl = _subchannelBuffer[5] & 0xf;
int adr = (_subchannelBuffer[5] >> 4) & 0xf;
sTrack = _subchannelBuffer[6];
sIndex = _subchannelBuffer[7];
sPos = (_subchannelBuffer[8] << 24) | (_subchannelBuffer[9] << 16) | (_subchannelBuffer[10] << 8) | (_subchannelBuffer[11]);
//int sRel = (_subchannelBuffer[12] << 24) | (_subchannelBuffer[13] << 16) | (_subchannelBuffer[14] << 8) | (_subchannelBuffer[15]);
if (adr != 1)
continue;
if (sTrack < _toc2.FirstAudio || sTrack >= _toc2.FirstAudio + _toc2.AudioTracks)
continue;
2008-12-06 05:44:14 +00:00
break;
}
case GapDetectionMethod.ReadCD:
2008-11-26 20:20:41 +00:00
{
int offs = 16 * (sector - fsector);
ctl = _subchannelBuffer[offs + 0] >> 4;
int adr = _subchannelBuffer[offs + 0] & 7;
if (!_qChannelInBCD && adr == 1)
2008-12-10 06:48:38 +00:00
{
_subchannelBuffer[offs + 3] = toBCD(_subchannelBuffer[offs + 3]);
_subchannelBuffer[offs + 4] = toBCD(_subchannelBuffer[offs + 4]);
_subchannelBuffer[offs + 5] = toBCD(_subchannelBuffer[offs + 5]);
_subchannelBuffer[offs + 7] = toBCD(_subchannelBuffer[offs + 7]);
_subchannelBuffer[offs + 8] = toBCD(_subchannelBuffer[offs + 8]);
_subchannelBuffer[offs + 9] = toBCD(_subchannelBuffer[offs + 9]);
2008-12-10 06:48:38 +00:00
}
ushort crc = _crc.ComputeChecksum(_subchannelBuffer, offs, 10);
crc ^= 0xffff;
ushort scrc = (ushort)((_subchannelBuffer[offs + 10] << 8) | _subchannelBuffer[offs + 11]);
if (scrc != 0 && scrc != crc)
2008-12-10 06:48:38 +00:00
continue;
if (adr != 1)
continue;
sTrack = fromBCD(_subchannelBuffer[offs + 1]);
sIndex = fromBCD(_subchannelBuffer[offs + 2]);
if (sTrack < _toc2.FirstAudio || sTrack >= _toc2.FirstAudio + _toc2.AudioTracks)
continue;
int mm = fromBCD(_subchannelBuffer[offs + 7]);
int ss = fromBCD(_subchannelBuffer[offs + 8]);
int ff = fromBCD(_subchannelBuffer[offs + 9]);
sPos = ff + 75 * (ss + 60 * mm) - 150;
break;
2008-11-26 20:20:41 +00:00
}
default:
continue;
}
bool preemph = (ctl & 1) == 1;
bool dcp = (ctl & 2) == 2;
if (preemph)
_toc2[sTrack].PreEmphasis = true;
if (dcp)
_toc2[sTrack].DCP = true;
if (sPos <= sec0 || sPos > sec1)
continue;
if (sTrack > iTrack || (sTrack == iTrack && iIndex >= 0 && sIndex > iIndex))
{
LocateLastSector(sec0, sPos - 1, iTrack, iIndex, ref maxIndex, out pos);
return;
}
if (sTrack < iTrack || (sTrack == iTrack && (iIndex < 0 || sIndex <= iIndex)))
{
if (sTrack == iTrack && iIndex < 0)
maxIndex = sIndex;
LocateLastSector(sPos, sec1, iTrack, iIndex, ref maxIndex, out pos);
return;
}
}
if (sec1 <= sec0 + 16)
{
pos = Math.Min(sec0, sec1);
return;
}
// TODO: catch?
throw new Exception("gap detection failed");
}
private unsafe void TestGaps()
{
_gapDetection = GapDetectionMethod.None;
//st = m_device.Seek((uint)(sector + i * 33) + _toc[_toc.FirstAudio][0].Start);
//if (st != Device.CommandStatus.Success)
// break;
//bool ready;
//st = m_device.TestUnitReady(out ready);
//if (st != Device.CommandStatus.Success)
// break;
//if (!ready)
//{
// st = Device.CommandStatus.NotSupported;
// break;
//}
// try ReadCD:
Device.CommandStatus st;
int sector = 3;
if (_readCDCommand == ReadCDCommand.ReadCdBEh)
{
2012-01-07 00:17:00 +00:00
// PLEXTOR PX-W1210A always returns data, even if asked only for subchannel.
// So we fill the buffer with magic data, give extra space for command so it won't hang the drive,
// request subchannel data and check if magic data was overwritten.
bool overwritten = false;
for (int i = 0; i < 16; i++)
{
_subchannelBuffer[m_max_sectors * (588 * 4 + 16) - 16 + i] = (byte)(13 + i);
}
fixed (byte* data = _subchannelBuffer)
{
st = m_device.ReadCDAndSubChannel(Device.MainChannelSelection.None, Device.SubChannelMode.QOnly, Device.C2ErrorMode.None, 1, false, (uint)sector + _toc[_toc.FirstAudio][0].Start, (uint)m_max_sectors, (IntPtr)((void*)data), _timeout);
}
for (int i = 0; i < 16; i++)
{
if (_subchannelBuffer[m_max_sectors * (588 * 4 + 16) - 16 + i] != (byte)(13 + i))
overwritten = true;
}
if (overwritten)
st = Device.CommandStatus.NotSupported;
//else
// st = m_device.ReadSubChannel(2, (uint)sector + _toc[_toc.FirstAudio][0].Start, (uint)m_max_sectors, ref _subchannelBuffer, _timeout);
if (st == Device.CommandStatus.Success)
{
int[] goodsecs = new int[2];
for (int bcd = 1; bcd >= 0; bcd--)
{
for (int i = 0; i < m_max_sectors; i++)
{
int adr = _subchannelBuffer[i * 16 + 0] & 7;
if (bcd == 0 && adr == 1)
{
_subchannelBuffer[i * 16 + 3] = toBCD(_subchannelBuffer[i * 16 + 3]);
_subchannelBuffer[i * 16 + 4] = toBCD(_subchannelBuffer[i * 16 + 4]);
_subchannelBuffer[i * 16 + 5] = toBCD(_subchannelBuffer[i * 16 + 5]);
_subchannelBuffer[i * 16 + 7] = toBCD(_subchannelBuffer[i * 16 + 7]);
_subchannelBuffer[i * 16 + 8] = toBCD(_subchannelBuffer[i * 16 + 8]);
_subchannelBuffer[i * 16 + 9] = toBCD(_subchannelBuffer[i * 16 + 9]);
}
ushort crc = _crc.ComputeChecksum(_subchannelBuffer, i * 16, 10);
crc ^= 0xffff;
ushort scrc = (ushort)((_subchannelBuffer[i * 16 + 10] << 8) | _subchannelBuffer[i * 16 + 11]);
if (scrc != 0 && scrc != crc)
continue;
if (adr != 1)
continue;
int sTrack = fromBCD(_subchannelBuffer[i * 16 + 1]);
int sIndex = fromBCD(_subchannelBuffer[i * 16 + 2]);
if (sTrack == 0 || sTrack == 110)
continue;
int mm = fromBCD(_subchannelBuffer[i * 16 + 7]);
int ss = fromBCD(_subchannelBuffer[i * 16 + 8]);
int ff = fromBCD(_subchannelBuffer[i * 16 + 9]);
int sPos = ff + 75 * (ss + 60 * mm) - 150;
if (sPos < sector + i - 8 || sPos > sector + i + 8)
continue;
goodsecs[bcd]++;
}
}
if (goodsecs[0] > 0 || goodsecs[1] > 0)
{
_qChannelInBCD = goodsecs[1] >= goodsecs[0];
_gapDetection = GapDetectionMethod.ReadCD;
}
}
}
if (_gapDetection == GapDetectionMethod.None)
{
fixed (byte* data = _subchannelBuffer)
{
// seek to given sector
if (_readCDCommand == ReadCDCommand.ReadCdBEh)
st = m_device.ReadCDAndSubChannel(_mainChannelMode, Device.SubChannelMode.None, _c2ErrorMode, 1, false, (uint)sector + _toc[_toc.FirstAudio][0].Start, 1, (IntPtr)((void*)data), _timeout);
else
st = m_device.ReadCDDA(Device.SubChannelMode.None, (uint)sector + _toc[_toc.FirstAudio][0].Start, 1, (IntPtr)((void*)data), _timeout);
}
if (st == Device.CommandStatus.Success)
{
st = m_device.ReadSubChannel42(1, 0, ref _subchannelBuffer, 0, _timeout);
if (st == Device.CommandStatus.Success)
{
if (_subchannelBuffer[0] == 0 && _subchannelBuffer[2] == 0 && _subchannelBuffer[3] == 12 && _subchannelBuffer[4] == 1)
{
int ctl = _subchannelBuffer[5] & 0xf;
int adr = (_subchannelBuffer[5] >> 4) & 0xf;
if (adr == 1)
{
_gapDetection = GapDetectionMethod.ReadSubchannel;
}
2008-11-26 20:20:41 +00:00
}
}
}
}
}
public bool GapsDetected
{
get
{
return gapsDetected;
}
}
private void DoEjectDisk(Device.StartState action)
{
EventStatusNotification status;
m_device.GetEventStatusNotification(true, Device.NotificationClass.Media, out status);
if (status.Valid && status.EventAvailable && status.EventData.Length > 1)
{
// 0 - tray closed no disc
// 1 - tray open
// 2 - tray closed, disc
action = status.EventData[1] == 1 ? Device.StartState.LoadDisk : Device.StartState.EjectDisk;
}
m_device.StartStopUnit(true, Device.PowerControl.NoChange, action);
}
public void EjectDisk()
2013-04-28 16:14:58 -04:00
{
if (m_device != null)
{
DoEjectDisk(Device.StartState.EjectDisk);
2013-04-28 16:14:58 -04:00
}
else
{
try
{
m_device = new Device(m_logger);
if (m_device.Open(m_device_letter))
{
try
{
DoEjectDisk(Device.StartState.LoadDisk);
2013-04-28 16:14:58 -04:00
}
finally
{
m_device.Close();
}
}
}
finally
{
m_device = null;
}
}
}
bool gapsDetected = false;
public unsafe bool DetectGaps()
{
if (!TestReadCommand())
throw new ReadCDException(Resource1.AutodetectReadCommandFailed + ":\n" + _autodetectResult);
if (_gapDetection == GapDetectionMethod.None)
{
gapsDetected = false;
return false;
}
if (gapsDetected)
return true;
_toc2 = (CDImageLayout)_toc.Clone();
if (_gapDetection == GapDetectionMethod.ReadSubchannel)
{
Device.CommandStatus st = m_device.ReadSubChannel42(2, 0, ref _subchannelBuffer, 0, _timeout);
if (st == Device.CommandStatus.Success)
if (_subchannelBuffer[0] == 0 && _subchannelBuffer[2] == 0 && _subchannelBuffer[3] == 20
&& _subchannelBuffer[4] == 2 && _subchannelBuffer[8] == 0x80)
{
string catalog = Encoding.ASCII.GetString(_subchannelBuffer, 9, 13);
if (catalog.ToString() != "0000000000000")
_toc2.Barcode = catalog.ToString();
}
}
int sec0 = (int)_toc2[_toc2.FirstAudio][0].Start, disc1 = (int)(_toc2[_toc2.FirstAudio][0].Start + _toc2.AudioLength) - 1;
for (int iTrack = _toc2.FirstAudio; iTrack < _toc2.FirstAudio + _toc2.AudioTracks; iTrack++)
{
if (ReadProgress != null)
{
2010-03-25 05:04:45 +00:00
progressArgs.Action = Resource1.StatusDetectingGaps;
progressArgs.Pass = -1;
progressArgs.Position = (iTrack - _toc2.FirstAudio) * 3;
progressArgs.PassStart = 0;
progressArgs.PassEnd = _toc2.TrackCount * 3 - 1;
progressArgs.ErrorsCount = 0;
progressArgs.PassTime = DateTime.Now;
ReadProgress(this, progressArgs);
}
int sec1, idx1 = 1;
LocateLastSector(sec0, Math.Min(disc1, (int)_toc[iTrack].End + 16), iTrack, -1, ref idx1, out sec1);
int isec0 = sec0;
for (int idx = 0; idx <= idx1; idx++)
{
int isec1 = sec1, iidx1 = 1;
if (idx < idx1)
{
if (ReadProgress != null)
{
progressArgs.Position = (iTrack - _toc2.FirstAudio) * 3 + 1;
progressArgs.PassTime = DateTime.Now;
ReadProgress(this, progressArgs);
}
LocateLastSector(isec0, sec1, iTrack, idx, ref iidx1, out isec1);
}
if (isec1 > isec0)
{
if (idx == 0 && iTrack > 1)
_toc2[iTrack][0].Start = _toc2[iTrack].Start - (uint)(isec1 - isec0 + 1);
if (idx > 1)
_toc2[iTrack].AddIndex(new CDTrackIndex((uint)idx, (uint)(_toc2[iTrack][0].Start + isec0 - sec0)));
}
isec0 = isec1 + 1;
}
if (ReadProgress != null)
{
progressArgs.Position = (iTrack - _toc2.FirstAudio) * 3 + 2;
progressArgs.PassTime = DateTime.Now;
ReadProgress(this, progressArgs);
}
if (_gapDetection == GapDetectionMethod.ReadSubchannel)
{
Device.CommandStatus st = m_device.ReadSubChannel42(3, iTrack, ref _subchannelBuffer, 0, _timeout);
if (st == Device.CommandStatus.Success)
if (_subchannelBuffer[0] == 0 && _subchannelBuffer[2] == 0 && _subchannelBuffer[3] == 20
&& _subchannelBuffer[4] == 3 && _subchannelBuffer[8] == 0x80) //&& _subchannelBuffer[6] == iTrack)
2008-11-26 20:20:41 +00:00
{
string isrc = Encoding.ASCII.GetString(_subchannelBuffer, 9, 12);
if (!isrc.ToString().Contains("#") && isrc.ToString() != "000000000000")
_toc2[iTrack].ISRC = isrc.ToString();
2008-11-26 20:20:41 +00:00
}
}
if (_gapDetection == GapDetectionMethod.ReadCD)
{
Device.CommandStatus st = m_device.ReadSubChannel(2, _toc2[iTrack].Start + 16, 100, ref _subchannelBuffer, _timeout);
if (st == Device.CommandStatus.Success)
{
for (int offs = 0; offs < 100 * 16; offs += 16)
{
int ctl = _subchannelBuffer[offs + 0] >> 4;
int adr = _subchannelBuffer[offs + 0] & 7;
if (adr != 2 && adr != 3)
continue;
ushort crc = _crc.ComputeChecksum(_subchannelBuffer, offs, 10);
crc ^= 0xffff;
ushort scrc = (ushort)((_subchannelBuffer[offs + 10] << 8) | _subchannelBuffer[offs + 11]);
if (scrc != 0 && scrc != crc)
continue;
if (adr == 3 && _toc2[iTrack].ISRC == null)
{
StringBuilder isrc = new StringBuilder();
isrc.Append(from6bit(_subchannelBuffer[offs + 1] >> 2));
isrc.Append(from6bit(((_subchannelBuffer[offs + 1] & 0x3) << 4) + (0x0f & (_subchannelBuffer[offs + 2] >> 4))));
isrc.Append(from6bit(((_subchannelBuffer[offs + 2] & 0xf) << 2) + (0x03 & (_subchannelBuffer[offs + 3] >> 6))));
isrc.Append(from6bit((_subchannelBuffer[offs + 3] & 0x3f)));
isrc.Append(from6bit(_subchannelBuffer[offs + 4] >> 2));
isrc.Append(from6bit(((_subchannelBuffer[offs + 4] & 0x3) << 4) + (0x0f & (_subchannelBuffer[offs + 5] >> 4))));
isrc.AppendFormat("{0:x}", _subchannelBuffer[offs + 5] & 0xf);
isrc.AppendFormat("{0:x2}", _subchannelBuffer[offs + 6]);
isrc.AppendFormat("{0:x2}", _subchannelBuffer[offs + 7]);
isrc.AppendFormat("{0:x}", _subchannelBuffer[offs + 8] >> 4);
if (!isrc.ToString().Contains("#") && isrc.ToString() != "000000000000")
_toc2[iTrack].ISRC = isrc.ToString();
}
if (adr == 2 && _toc2.Barcode == null)
{
StringBuilder barcode = new StringBuilder();
for (int i = 1; i < 8; i++)
barcode.AppendFormat("{0:x2}", _subchannelBuffer[offs + i]);
if (barcode.ToString(0, 13) != "0000000000000")
_toc2.Barcode = barcode.ToString(0, 13);
}
}
}
2008-11-26 20:20:41 +00:00
}
sec0 = sec1 + 1;
2008-11-26 20:20:41 +00:00
}
gapsDetected = true;
return true;
2008-12-07 00:33:20 +00:00
}
bool readCommandFound = false;
2008-12-07 23:12:01 +00:00
public unsafe bool TestReadCommand()
2008-12-07 00:33:20 +00:00
{
if (readCommandFound)
return true;
2008-12-10 06:48:38 +00:00
//ReadCDCommand[] readmode = { ReadCDCommand.ReadCdBEh, ReadCDCommand.ReadCdD8h };
2012-01-07 00:17:00 +00:00
ReadCDCommand[] readmode = { ReadCDCommand.ReadCdBEh, ReadCDCommand.ReadCdD8h };
Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.Mode294, Device.C2ErrorMode.Mode296, Device.C2ErrorMode.None };
2008-12-10 06:48:38 +00:00
Device.MainChannelSelection[] mainmode = { Device.MainChannelSelection.UserData, Device.MainChannelSelection.F8h };
2008-12-07 23:12:01 +00:00
bool found = false;
_autodetectResult = "";
2008-12-10 06:48:38 +00:00
_currentStart = 0;
2008-12-07 23:12:01 +00:00
m_max_sectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
2008-12-10 06:48:38 +00:00
int sector = 3;
int pass = 0;
2010-03-25 05:04:45 +00:00
for (int c = 0; c <= 2 && !found; c++)
for (int r = 0; r <= 1 && !found; r++)
for (int m = 0; m <= 1 && !found; m++)
{
_readCDCommand = readmode[r];
_c2ErrorMode = c2mode[c];
_mainChannelMode = mainmode[m];
if (_forceReadCommand != ReadCDCommand.Unknown && _readCDCommand != _forceReadCommand)
continue;
2012-01-07 00:17:00 +00:00
if (_readCDCommand == ReadCDCommand.ReadCdD8h && (_c2ErrorMode != Device.C2ErrorMode.None || _mainChannelMode != Device.MainChannelSelection.UserData))
continue;
Array.Clear(_readBuffer, 0, _readBuffer.Length); // fill with something nasty instead?
DateTime tm = DateTime.Now;
if (ReadProgress != null)
2008-12-10 06:48:38 +00:00
{
2010-03-25 05:04:45 +00:00
progressArgs.Action = Resource1.StatusDetectingDriveFeatures;
progressArgs.Pass = -1;
progressArgs.Position = pass++;
progressArgs.PassStart = 0;
progressArgs.PassEnd = 2 * 3 * 2 - 1;
progressArgs.ErrorsCount = 0;
progressArgs.PassTime = tm;
ReadProgress(this, progressArgs);
2008-12-10 06:48:38 +00:00
}
2010-03-25 05:04:45 +00:00
System.Diagnostics.Trace.WriteLine("Trying " + CurrentReadCommand);
Device.CommandStatus st = FetchSectors(sector, m_max_sectors, false);
TimeSpan delay = DateTime.Now - tm;
_autodetectResult += string.Format("{0}: {1} ({2}ms)\n", CurrentReadCommand, (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()), delay.TotalMilliseconds);
found = st == Device.CommandStatus.Success;
2010-03-25 05:04:45 +00:00
//sector += m_max_sectors;
}
2008-12-10 06:48:38 +00:00
//if (found)
// for (int n = 1; n <= m_max_sectors; n++)
// {
// Device.CommandStatus st = FetchSectors(0, n, false, false);
// if (st != Device.CommandStatus.Success)
// {
// _autodetectResult += 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;
// }
// }
2012-01-07 00:17:00 +00:00
if (found)
{
TestGaps();
_autodetectResult += "Chosen " + CurrentReadCommand + "\n";
}
else
{
_gapDetection = GapDetectionMethod.None;
_readCDCommand = ReadCDCommand.Unknown;
}
2008-12-10 06:48:38 +00:00
_currentStart = -1;
_currentEnd = -1;
readCommandFound = found;
2008-12-07 23:12:01 +00:00
return found;
2008-12-07 00:33:20 +00:00
}
2010-02-28 21:04:34 +00:00
//int dbg_pass;
//FileStream fs_d, fs_c;
2010-02-23 15:15:08 +00:00
private unsafe void ReorganiseSectors(int sector, int Sectors2Read)
2008-12-07 00:33:20 +00:00
{
2008-12-07 23:12:01 +00:00
int c2Size = _c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : 296;
int oldSize = 4 * 588 + c2Size;
fixed (byte* readBuf = _readBuffer, c2Count = C2Count)
2010-02-28 21:04:34 +00:00
fixed (long* userData = UserData)
2008-12-07 00:33:20 +00:00
{
for (int iSector = 0; iSector < Sectors2Read; iSector++)
2008-12-07 23:12:01 +00:00
{
byte* sectorPtr = readBuf + iSector * oldSize;
2010-02-28 21:04:34 +00:00
long* userDataPtr = userData + (sector - _currentStart + iSector) * 8 * 588;
byte* c2CountPtr = c2Count + (sector - _currentStart + iSector) * 294;
2008-12-07 23:12:01 +00:00
2010-02-28 21:04:34 +00:00
//if (_currentStart > 0)
//{
// string nm_d = string.Format("Y:\\Temp\\dbg\\{0:x}-{1:00}.bin", _currentStart, dbg_pass);
// string nm_c = string.Format("Y:\\Temp\\dbg\\{0:x}-{1:00}.c2", _currentStart, dbg_pass);
// if (fs_d != null && fs_d.Name != nm_d) { fs_d.Close(); fs_d = null; }
// if (fs_c != null && fs_c.Name != nm_c) { fs_c.Close(); fs_c = null; }
// if (fs_d == null) fs_d = new FileStream(nm_d, FileMode.Create);
// if (fs_c == null) fs_c = new FileStream(nm_c, FileMode.Create);
// fs_d.Seek((sector - _currentStart + iSector) * 4 * 588, SeekOrigin.Begin);
// fs_d.Write(_readBuffer, iSector * oldSize, 4 * 588);
// fs_c.Seek((sector - _currentStart + iSector) * 296, SeekOrigin.Begin);
// fs_c.Write(_readBuffer, iSector * oldSize + 4 * 588, 296);
//}
2008-12-07 23:12:01 +00:00
if (_c2ErrorMode != Device.C2ErrorMode.None)
2010-02-23 15:15:08 +00:00
{
2010-02-28 21:04:34 +00:00
int offs = 0;
if (c2Size == 296)
{
// TODO: sometimes (e.g on PIONEER 215D) sector C2 byte is placed after C2 info,
// not before, like it says in mmc6r02g.pdf !!
2010-02-28 21:04:34 +00:00
int c2 = 0;
for (int pos = 2; pos < 294; pos++)
c2 |= sectorPtr[4 * 588 + pos];
if (sectorPtr[4 * 588 + 294] == (c2 | sectorPtr[4 * 588 + 0] | sectorPtr[4 * 588 + 1]))
offs = 0;
else
2010-02-28 21:04:34 +00:00
offs = 2;
// TSSTcorp CDDVDW SH-S203B SB03
// TSSTcorpCD/DVDW SH-W162C TS12
// Both produced Exception("invalid C2 pointers");
//else if (sectorPtr[4 * 588] == (c2 | sectorPtr[4 * 588 + 294] | sectorPtr[4 * 588 + 295]))
// offs = 2;
//else
// throw new Exception("invalid C2 pointers");
2010-02-28 21:04:34 +00:00
}
for (int pos = 0; pos < 294; pos++)
2010-02-23 15:15:08 +00:00
{
2010-02-28 21:04:34 +00:00
int c2d = sectorPtr[4 * 588 + pos + offs];
int c2 = ((-c2d) >> 31) & 1;
c2CountPtr[pos] += (byte)c2;
int sample = pos << 3;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]]; sample++;
userDataPtr[sample + c2 * 4 * 588] += byte2long[sectorPtr[sample]];
2010-02-23 15:15:08 +00:00
}
}
2010-02-28 21:04:34 +00:00
else
{
for (int sample = 0; sample < 4 * 588; sample++)
userDataPtr[sample] += byte2long[sectorPtr[sample]];
2010-02-28 21:04:34 +00:00
}
2008-12-07 23:12:01 +00:00
}
2008-12-07 00:33:20 +00:00
}
2008-11-26 20:20:41 +00:00
}
2010-02-23 15:15:08 +00:00
private unsafe void ClearSectors(int sector, int Sectors2Read)
{
2010-02-28 21:04:34 +00:00
fixed (long* userData = &UserData[sector - _currentStart, 0, 0])
fixed (byte* c2Count = &C2Count[sector - _currentStart, 0])
2010-02-23 15:15:08 +00:00
{
2010-03-30 02:41:43 +00:00
AudioSamples.MemSet((byte*)userData, 0, 8 * 2 * 4 * 588 * Sectors2Read);
AudioSamples.MemSet(c2Count, 0, 294 * Sectors2Read);
2010-02-23 15:15:08 +00:00
}
}
private unsafe Device.CommandStatus FetchSectors(int sector, int Sectors2Read, bool abort)
2008-11-26 20:20:41 +00:00
{
2008-12-07 23:12:01 +00:00
Device.CommandStatus st;
2008-12-07 00:33:20 +00:00
fixed (byte* data = _readBuffer)
2008-11-26 20:20:41 +00:00
{
if (_debugMessages)
{
int size = (4 * 588 + (_c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : _c2ErrorMode == Device.C2ErrorMode.Mode296 ? 296 : 0))
* (int)Sectors2Read;
2010-03-30 02:41:43 +00:00
AudioSamples.MemSet(data, 0xff, size);
}
2008-12-10 06:48:38 +00:00
if (_readCDCommand == ReadCDCommand.ReadCdBEh)
st = m_device.ReadCDAndSubChannel(_mainChannelMode, Device.SubChannelMode.None, _c2ErrorMode, 1, false, (uint)sector + _toc[_toc.FirstAudio][0].Start, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout);
2008-12-10 06:48:38 +00:00
else
st = m_device.ReadCDDA(Device.SubChannelMode.None, (uint)sector + _toc[_toc.FirstAudio][0].Start, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout);
2008-12-07 23:12:01 +00:00
}
2008-12-10 06:48:38 +00:00
2008-12-07 23:12:01 +00:00
if (st == Device.CommandStatus.Success)
{
2008-12-10 06:48:38 +00:00
ReorganiseSectors(sector, Sectors2Read);
return st;
2008-12-07 23:12:01 +00:00
}
2008-12-10 06:48:38 +00:00
2008-12-07 23:12:01 +00:00
if (!abort)
return st;
SCSIException ex = new SCSIException(Resource1.ReadCDError, m_device, st);
2008-12-07 23:12:01 +00:00
if (sector != 0 && Sectors2Read > 1 && st == Device.CommandStatus.DeviceFailed && m_device.GetSenseAsc() == 0x64 && m_device.GetSenseAscq() == 0x00)
{
2008-12-10 06:48:38 +00:00
if (_debugMessages)
System.Console.WriteLine("\n{0}: retrying one sector at a time", ex.Message);
2008-12-07 23:12:01 +00:00
int iErrors = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
2008-12-06 05:44:14 +00:00
{
if (FetchSectors(sector + iSector, 1, false) != Device.CommandStatus.Success)
2008-12-06 05:44:14 +00:00
{
2008-12-07 23:12:01 +00:00
iErrors ++;
for (int i = 0; i < 294; i++)
2010-02-28 21:04:34 +00:00
C2Count[sector + iSector - _currentStart, i] ++;
2008-12-07 23:12:01 +00:00
if (_debugMessages)
System.Console.WriteLine("\nSector lost");
2008-12-06 05:44:14 +00:00
}
}
2008-12-07 23:12:01 +00:00
if (iErrors < Sectors2Read)
return Device.CommandStatus.Success;
2008-12-06 05:44:14 +00:00
}
2008-12-10 06:48:38 +00:00
throw ex;
}
private unsafe void CorrectSectors(int pass, int sector, int Sectors2Read)
2008-12-10 06:48:38 +00:00
{
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
int pos = sector - _currentStart + iSector;
2010-02-23 15:15:08 +00:00
// avg - pass + 1
// p a l o
// 0 1 1 2
// 2 4 3 3
// 4 7 2 4
// 6 10 5
//16 25 10
bool fError = false;
2010-02-28 21:04:34 +00:00
const byte c2div = 128;
int er_limit = c2div * (1 + _correctionQuality) - 1;
// need at least 1 + _correctionQuality good passes
2008-12-10 06:48:38 +00:00
for (int iPar = 0; iPar < 4 * 588; iPar++)
{
2010-02-28 21:04:34 +00:00
long val = UserData[pos, 0, iPar];
long val1 = UserData[pos, 1, iPar];
byte c2 = C2Count[pos, iPar >> 3];
int ave = (pass + 1 - c2) * c2div + c2;
2010-02-23 15:15:08 +00:00
int bestValue = 0;
for (int i = 0; i < 8; i++)
2008-12-06 05:44:14 +00:00
{
2010-02-28 21:04:34 +00:00
int sum = ave - 2 * (int)((val & 0xff) * c2div + (val1 & 0xff));
2010-02-23 15:15:08 +00:00
int sig = sum >> 31; // bit value
fError |= (sum ^ sig) < er_limit;
bestValue += sig & (1 << i);
val >>= 8;
val1 >>= 8;
2008-12-06 05:44:14 +00:00
}
2010-02-28 21:04:34 +00:00
currentData.Bytes[pos * 4 * 588 + iPar] = (byte)bestValue;
2010-02-23 15:15:08 +00:00
}
if (pass > _correctionQuality || fError)
{
int olderr = pass > _correctionQuality && m_retryCount[sector + iSector] == pass + 1 ? 1 : 0;
int newerr = fError ? 1 : 0;
_currentErrorsCount += newerr - olderr;
if (fError) m_retryCount[sector + iSector] = (byte)(pass + 2);
}
2008-11-26 20:20:41 +00:00
}
}
2008-12-10 06:48:38 +00:00
public unsafe void PrefetchSector(int iSector)
2008-12-06 05:44:14 +00:00
{
2010-02-28 21:04:34 +00:00
if (iSector >= _currentStart && iSector < _currentEnd)
2008-12-06 05:44:14 +00:00
return;
if (!TestReadCommand())
throw new ReadCDException(Resource1.AutodetectReadCommandFailed + "\n" + _autodetectResult);
2008-12-07 23:12:01 +00:00
_currentStart = iSector;
2010-02-28 21:04:34 +00:00
_currentEnd = _currentStart + MSECTORS;
if (_currentEnd > (int)_toc.AudioLength)
{
_currentEnd = (int)_toc.AudioLength;
_currentStart = Math.Max(0, _currentEnd - MSECTORS);
}
int neededSize = (_currentEnd - _currentStart) * 588;
if (currentData.Size < neededSize)
currentData.Prepare(new byte[neededSize * 4], neededSize);
currentData.Length = neededSize;
2008-12-06 05:44:14 +00:00
//FileStream correctFile = new FileStream("correct.wav", FileMode.Open);
//byte[] realData = new byte[MSECTORS * 4 * 588];
//correctFile.Seek(0x2C + _currentStart * 588 * 4, SeekOrigin.Begin);
//if (correctFile.Read(realData, _driveOffset * 4, MSECTORS * 4 * 588 - _driveOffset * 4) != MSECTORS * 4 * 588 - _driveOffset * 4)
2008-12-06 05:44:14 +00:00
// throw new Exception("read");
//correctFile.Close();
2010-02-28 21:04:34 +00:00
_currentErrorsCount = 0;
//Device.CommandStatus st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, (ushort)(176 * 4), 65535);
//if (st != Device.CommandStatus.Success)
// System.Console.WriteLine("SetCdSpeed: {0}", (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
// TODO:
//int max_scans = 1 << _correctionQuality;
2010-02-28 21:04:34 +00:00
int max_scans = 16 << _correctionQuality;
for (int pass = 0; pass < max_scans; pass++)
2008-12-06 05:44:14 +00:00
{
2010-02-28 21:04:34 +00:00
// dbg_pass = pass;
2010-02-23 15:15:08 +00:00
DateTime PassTime = DateTime.Now, LastFetch = DateTime.Now;
2008-12-10 06:48:38 +00:00
2010-02-23 15:15:08 +00:00
for (int sector = _currentStart; sector < _currentEnd; sector += m_max_sectors)
{
int Sectors2Read = Math.Min(m_max_sectors, _currentEnd - sector);
int speed = pass == 5 ? 300 : pass == 6 ? 150 : pass == 7 ? 75 : 32500; // sectors per second
int msToSleep = 1000 * Sectors2Read / speed - (int)((DateTime.Now - LastFetch).TotalMilliseconds);
//if (msToSleep > 0) Thread.Sleep(msToSleep);
LastFetch = DateTime.Now;
if (pass == 0)
ClearSectors(sector, Sectors2Read);
FetchSectors(sector, Sectors2Read, true);
2010-02-23 15:15:08 +00:00
//TimeSpan delay1 = DateTime.Now - LastFetch;
//DateTime LastFetched = DateTime.Now;
2010-02-28 21:04:34 +00:00
if (pass >= _correctionQuality)
CorrectSectors(pass, sector, Sectors2Read);
2010-02-23 15:15:08 +00:00
//TimeSpan delay2 = DateTime.Now - LastFetched;
//if (sector == _currentStart)
//System.Console.WriteLine("\n{0},{1}", delay1.TotalMilliseconds, delay2.TotalMilliseconds);
if (ReadProgress != null)
{
2010-03-25 05:04:45 +00:00
progressArgs.Action = Resource1.StatusRipping;
progressArgs.Position = sector + Sectors2Read;
progressArgs.Pass = pass;
progressArgs.PassStart = _currentStart;
progressArgs.PassEnd = _currentEnd;
progressArgs.ErrorsCount = _currentErrorsCount;
progressArgs.PassTime = PassTime;
ReadProgress(this, progressArgs);
}
2008-12-10 06:48:38 +00:00
}
2010-02-28 21:04:34 +00:00
if (pass >= _correctionQuality && _currentErrorsCount == 0)
2010-02-23 15:15:08 +00:00
break;
2008-12-06 05:44:14 +00:00
}
}
2010-02-06 23:17:07 +00:00
public unsafe int Read(AudioBuffer buff, int maxLength)
{
if (_toc == null)
throw new ReadCDException("Read: invalid TOC");
2010-02-06 23:17:07 +00:00
buff.Prepare(this, maxLength);
if (Position >= Length)
return 0;
if (_sampleOffset >= Length)
2008-11-26 20:20:41 +00:00
{
2010-02-06 23:17:07 +00:00
for (int i = 0; i < buff.ByteLength; i++)
buff.Bytes[i] = 0;
_sampleOffset += buff.Length;
2010-02-06 23:17:07 +00:00
return buff.Length; // == Remaining
2008-11-26 20:20:41 +00:00
}
if (_sampleOffset < 0)
{
2010-02-06 23:17:07 +00:00
buff.Length = Math.Min(buff.Length, -_sampleOffset);
for (int i = 0; i < buff.ByteLength; i++)
buff.Bytes[i] = 0;
_sampleOffset += buff.Length;
2010-02-06 23:17:07 +00:00
return buff.Length;
2008-11-26 20:20:41 +00:00
}
PrefetchSector(/*(int)_toc[_toc.FirstAudio][0].Start +*/ (_sampleOffset / 588));
2010-02-06 23:17:07 +00:00
buff.Length = Math.Min(buff.Length, (int)Length - _sampleOffset);
buff.Length = Math.Min(buff.Length, _currentEnd * 588 - _sampleOffset);
2010-02-28 21:04:34 +00:00
if ((_sampleOffset - _currentStart * 588) == 0 && (maxLength < 0 || (_currentEnd - _currentStart) * 588 <= buff.Length))
{
buff.Swap(currentData);
_currentStart = -1;
_currentEnd = -1;
}
else
fixed (byte* dest = buff.Bytes, src = &currentData.Bytes[(_sampleOffset - _currentStart * 588) * 4])
AudioSamples.MemCpy(dest, src, buff.ByteLength);
2010-02-06 23:17:07 +00:00
_sampleOffset += buff.Length;
return buff.Length;
2008-11-26 18:57:40 +00:00
}
public TimeSpan Duration => TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
public long Length
2008-11-26 18:57:40 +00:00
{
get
{
if (_toc == null)
throw new ReadCDException("invalid TOC");
2010-02-06 23:17:07 +00:00
return 588 * (int)_toc.AudioLength;
2008-11-26 18:57:40 +00:00
}
}
2010-02-06 23:17:07 +00:00
public AudioPCMConfig PCM
2008-11-26 18:57:40 +00:00
{
get
{
2010-02-06 23:17:07 +00:00
return AudioPCMConfig.RedBook;
2008-11-26 18:57:40 +00:00
}
}
public string Path
{
get
{
2008-12-07 23:12:01 +00:00
string result = m_device_letter + ": ";
if (m_inqury_result != null && m_inqury_result.Valid)
result += "[" + m_inqury_result.VendorIdentification + " " +
m_inqury_result.ProductIdentification + " " +
m_inqury_result.FirmwareVersion + "]";
2008-12-07 23:12:01 +00:00
return result;
2008-11-26 18:57:40 +00:00
}
}
2008-12-01 21:54:55 +00:00
public string ARName
{
get
{
return m_inqury_result == null || !m_inqury_result.Valid ? null :
m_inqury_result.VendorIdentification.TrimEnd(' ', '\0').PadRight(8, ' ') + " - " +
m_inqury_result.ProductIdentification.TrimEnd(' ', '\0');
2008-12-01 21:54:55 +00:00
}
}
public string EACName
{
get
{
return m_inqury_result.VendorIdentification.TrimEnd(' ', '\0') + " " + m_inqury_result.ProductIdentification.TrimEnd(' ', '\0');
}
}
2010-02-06 23:17:07 +00:00
public long Position
2008-11-26 18:57:40 +00:00
{
get
{
2010-02-06 23:17:07 +00:00
return _sampleOffset - _driveOffset;
2008-11-26 18:57:40 +00:00
}
set
{
2010-03-25 05:04:45 +00:00
if (_toc == null || _toc.AudioLength <= 0)
throw new ReadCDException(Resource1.NoAudio);
2010-02-28 21:04:34 +00:00
_currentStart = -1;
_currentEnd = -1;
m_retryCount = new byte[(int)_toc.AudioLength];
for (int i = 0; i < m_retryCount.Length; i++)
m_retryCount[i] = (byte)(_correctionQuality + 1);
m_failedSectors = null;
2008-12-10 06:48:38 +00:00
_sampleOffset = (int)value + _driveOffset;
2008-11-26 18:57:40 +00:00
}
}
2010-02-06 23:17:07 +00:00
public long Remaining
2008-11-26 18:57:40 +00:00
{
get
{
return Length - Position;
}
}
2008-11-26 20:20:41 +00:00
public int DriveOffset
{
get
{
return _driveOffset;
}
set
{
_driveOffset = value;
_sampleOffset = value;
}
}
2008-12-06 05:44:14 +00:00
public int CorrectionQuality
{
get
{
return _correctionQuality;
}
set
{
2010-02-28 21:04:34 +00:00
if (value < 0 || value > 3)
throw new Exception("invalid CorrectionQuality");
2008-12-06 05:44:14 +00:00
_correctionQuality = value;
for (int i = 0; i < m_retryCount.Length; i++)
m_retryCount[i] = (byte)(_correctionQuality + 1);
}
2008-12-06 05:44:14 +00:00
}
2010-02-08 01:29:31 +00:00
public string RipperVersion
2008-12-02 03:03:08 +00:00
{
2010-02-08 01:29:31 +00:00
get
{
Bump copyright year to 2020 The copyright year was last time updated in 2018. There is some cleanup involved in this commit and the next copyright year update is supposed to be simpler (i.e. substitute "-2020"). - Substitute occurrences of "-2018" with "-2020" using: git grep -I -l -e '-2018' -- ':(exclude)*.bak' | xargs \ sed -b -i -e 's/-2018/-2020/g' - Update special cases: CUEPlayer git grep -I -l -e 'Grigory Chudov 2010' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2010/2010-2020 Grigory Chudov/g' CUERipper git grep -I -l -e '2008-2009' -- | xargs \ sed -b -i -e 's/2008-2009/2008-2020/g' CUETools, CUETools.FLACCL.cmd git grep -I -l -e '2008-2010' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/2008-2010/2008-2020/g' git grep -I -l -e '2010-2013' -- | xargs \ sed -b -i -e 's/2010-2013/2010-2020/g' CUETools.ChaptersToCue git grep -I -l -e 'Grigory Chudov 2017' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2017/2017-2020 Grigory Chudov/g' CUETools.CTDB.EACPlugin git grep -I -l -e 'Grigory Chudov 2012' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2012/2012-2020 Grigory Chudov/g' git grep -I -l -e '2011-12' -- | xargs \ sed -b -i -e 's/2011-12/2011-2020/g' CUETools.Codecs.FLACCL git grep -I -l -e '2009-2010' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/2009-2010/2009-2020/g' CUETools.eac3ui (BluTools) git grep -I -l -e '© 2018' -- | xargs \ sed -b -i -e 's/© 2018/© 2018-2020 Grigory Chudov/g' CUETools.Flake git grep -I -l -e ' 2009-2014 Gr' -- | xargs \ sed -b -i -e 's/ 2009-2014 Gr/ 2009-2020 Gr/g' CUETools.Processor git grep -I -l -e ' 2008-2013 Gr' -- | xargs \ sed -b -i -e 's/ 2008-2013 Gr/ 2008-2020 Gr/g' CUETools.Ripper.Console git grep -I -l -e ' 2008-10 Gr' -- | xargs \ sed -b -i -e 's/ 2008-10 Gr/ 2008-2020 Gr/g' CUETools.Ripper.Console, CUETools.Ripper.SCSI git grep -I -l -e ' 2008-13 Gr' -- | xargs \ sed -b -i -e 's/ 2008-13 Gr/ 2008-2020 Gr/g' Single year entries: 2008, 2009, 2010, 2011, 2017, 2018 git grep -I -l -e ' 2008 Gr' -- | xargs \ sed -b -i -e 's/ 2008 Gr/ 2008-2020 Gr/g' git grep -I -l -e ' 2009 Gr' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/ 2009 Gr/ 2009-2020 Gr/g' git grep -I -l -e ' 2010 Gr' -- | xargs \ sed -b -i -e 's/ 2010 Gr/ 2010-2020 Gr/g' git grep -I -l -e ' 2011 Gr' -- | xargs \ sed -b -i -e 's/ 2011 Gr/ 2011-2020 Gr/g' git grep -I -l -e ' 2017 Gr' -- | xargs \ sed -b -i -e 's/ 2017 Gr/ 2017-2020 Gr/g' git grep -I -l -e ' 2018 Gr' -- | xargs \ sed -b -i -e 's/ 2018 Gr/ 2018-2020 Gr/g' Fix typo in copyright year of CUETools.Codecs.WMA/AudioDecoder.cs: Copyright (c) 20139 Grigory Chudov git grep -I -lw -e '20139' -- | xargs \ sed -b -i -e 's/20139/2013-2020/g'
2020-01-30 18:13:46 +01:00
return "CUERipper v2.1.7 Copyright (C) 2008-2020 Grigory Chudov";
2010-02-08 01:29:31 +00:00
// ripper.GetName().Name + " " + ripper.GetName().Version;
}
2008-12-02 03:03:08 +00:00
}
2008-11-26 18:57:40 +00:00
private int fromBCD(byte hex)
{
return (hex >> 4) * 10 + (hex & 15);
}
private byte toBCD(int val)
{
return (byte)(((val / 10) << 4) + (val % 10));
}
2008-12-07 00:33:20 +00:00
private char from6bit(int bcd)
{
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' };
bcd &= 0x3f;
return bcd >= ISRC6.Length ? '#' : ISRC6[bcd];
}
2008-11-26 18:57:40 +00:00
}
2008-12-06 05:44:14 +00:00
2008-12-10 06:48:38 +00:00
enum ReadCDCommand
2008-12-06 05:44:14 +00:00
{
2008-12-10 06:48:38 +00:00
ReadCdBEh,
ReadCdD8h,
Unknown
};
2008-12-06 05:44:14 +00:00
2008-12-07 23:12:01 +00:00
public sealed class SCSIException : Exception
{
public SCSIException(string args, Device device, Device.CommandStatus st)
: base(args + ": " + (st == Device.CommandStatus.DeviceFailed ? device.GetErrorString() : st.ToString()))
2008-12-07 23:12:01 +00:00
{
}
}
public sealed class ReadCDException : Exception
{
public ReadCDException(string args, Exception inner)
: base(args + ": " + inner.Message, inner) { }
public ReadCDException(string args)
: base(args) { }
}
2008-11-26 18:57:40 +00:00
}