more work on ripper

This commit is contained in:
chudov
2008-12-10 06:48:38 +00:00
parent e3f461e43c
commit 905414db84
11 changed files with 732 additions and 441 deletions

View File

@@ -46,8 +46,9 @@ namespace CUETools.Ripper.SCSI
int _correctionQuality = 1;
int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0;
const int CB_AUDIO = 4 * 588 + 2 + 294 + 16;
const int MAXSCANS = 20;
const int NSECTORS = 16;
const int MSECTORS = 10000000 / (4 * 588);
const int MSECTORS = 5*1024*1024 / (4 * 588);
int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1;
Logger m_logger;
CDImageLayout _toc;
@@ -56,17 +57,23 @@ namespace CUETools.Ripper.SCSI
int m_max_sectors;
int _timeout = 10;
Crc16Ccitt _crc;
List<ScanResults> _scanResults;
ScanResults _currentScan = null;
int _currentScan;
public byte[,,] UserData;
public byte[,,] C2Data;
public byte[,,] QData;
public long[] Quality;
BitArray _errors;
int _errorsCount;
int _crcErrorsCount = 0;
byte[] _currentData = new byte[MSECTORS * 4 * 588];
int[] valueScore = new int[256];
short[] _valueScore = new short[256];
bool _debugMessages = false;
ReadCDCommand _readCDCommand = ReadCDCommand.Unknown;
ReadCDCommand _forceReadCommand = ReadCDCommand.Unknown;
Device.MainChannelSelection _mainChannelMode = Device.MainChannelSelection.UserData;
Device.SubChannelMode _subChannelMode = Device.SubChannelMode.None;
Device.SubChannelMode _subChannelMode = Device.SubChannelMode.QOnly;
Device.C2ErrorMode _c2ErrorMode = Device.C2ErrorMode.Mode296;
string m_test_result;
string _autodetectResult;
byte[] _readBuffer = new byte[NSECTORS * CB_AUDIO];
byte[] _subchannelBuffer = new byte[NSECTORS * 16];
@@ -124,9 +131,36 @@ namespace CUETools.Ripper.SCSI
{
get
{
if (m_test_result == null)
TestReadCommand();
return m_test_result;
if (_autodetectResult != null || TestReadCommand())
return _autodetectResult;
string ret = _autodetectResult;
_autodetectResult = null;
return ret;
}
}
public bool ForceD8
{
get
{
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;
}
}
@@ -134,25 +168,24 @@ namespace CUETools.Ripper.SCSI
{
get
{
return string.Format("BEh, {0}, {1}, {2}, {3} blocks at a time", (_mainChannelMode == Device.MainChannelSelection.UserData ? "10h" : "F8h"),
return _readCDCommand == ReadCDCommand.Unknown ? "unknown" :
string.Format("{0}, {1}, {2}, {3}, {4} blocks at a time",
(_readCDCommand == ReadCDCommand.ReadCdBEh ? "BEh" : "D8h"),
(_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();
_crc = new Crc16Ccitt(InitialCrcValue.Zeros);
UserData = new byte[MAXSCANS, MSECTORS, 4 * 588];
C2Data = new byte[MAXSCANS, MSECTORS, 294];
QData = new byte[MAXSCANS, MSECTORS, 16];
Quality = new long[MAXSCANS];
}
public bool Open(char Drive)
@@ -197,6 +230,7 @@ namespace CUETools.Ripper.SCSI
//if (st != Device.CommandStatus.Success)
// throw new Exception("GetSpeed failed: SCSI error");
//m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, (ushort)(0x7fff), (ushort)(0x7fff));
//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)
@@ -253,34 +287,39 @@ namespace CUETools.Ripper.SCSI
int posCount = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
int q_pos = sector - _currentStart + iSector;
int ctl = _currentScan.QData[q_pos, 0] >> 4;
int adr = _currentScan.QData[q_pos, 0] & 7;
int q_pos = (sector - _currentStart + iSector);
int ctl = QData[_currentScan, q_pos, 0] >> 4;
int adr = QData[_currentScan, q_pos, 0] & 7;
bool preemph = (ctl & 1) == 1;
bool dcp = (ctl & 2) == 2;
switch (adr)
{
case 1: // current position
{
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 iTrack = fromBCD(QData[_currentScan, q_pos, 1]);
int iIndex = fromBCD(QData[_currentScan, q_pos, 2]);
int mm = fromBCD(QData[_currentScan, q_pos, 7]);
int ss = fromBCD(QData[_currentScan, q_pos, 8]);
int ff = fromBCD(QData[_currentScan, 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);
byte[] tmp = new byte[16];
for (int i = 0; i < 10; i++)
_subchannelBuffer[i] = _currentScan.QData[q_pos, i];
_subchannelBuffer[i] = QData[_currentScan, q_pos, i];
ushort crc = _crc.ComputeChecksum(_subchannelBuffer, 0, 10);
crc ^= 0xffff;
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 ((QData[_currentScan, q_pos, 10] != 0 || QData[_currentScan, q_pos, 11] != 0) &&
((byte)(crc & 0xff) != QData[_currentScan, q_pos, 11] || (byte)(crc >> 8) != QData[_currentScan, q_pos, 10])
)
{
if (_debugMessages)
System.Console.WriteLine("\nCRC error at {0}", CDImageLayout.TimeToString((uint)(sector + iSector)));
_crcErrorsCount ++;
if (_debugMessages && _crcErrorsCount < 4)
{
StringBuilder st = new StringBuilder();
for (int i = 0; i < 12; i++)
st.AppendFormat(" 0x{0:X2}", QData[_currentScan, q_pos, i]);
System.Console.WriteLine("\nCRC error at {0}:{1}", CDImageLayout.TimeToString((uint)(sector + iSector)), st.ToString());
}
continue;
}
if (iTrack == 110)
@@ -299,16 +338,33 @@ namespace CUETools.Ripper.SCSI
throw new Exception("strange track number encountred");
if (iTrack != _currentTrack)
{
if (_currentTrack != -1 && iTrack != _currentTrack + 1)
{
if (_debugMessages)
System.Console.WriteLine("\nNon-consequent track at {0}: {1} after {2}", CDImageLayout.TimeToString((uint)(sector + iSector)), iTrack, _currentTrack);
//throw new Exception("invalid track");
continue;
}
if (iIndex != 1 && iIndex != 0)
{
if (_debugMessages)
System.Console.WriteLine("\nInvalid track start index at {0}: {1}.{2}", CDImageLayout.TimeToString((uint)(sector + iSector)), iTrack, iIndex);
//throw new Exception("invalid index");
continue;
}
_currentTrack = iTrack;
_currentTrackActualStart = sec;
_currentIndex = iIndex;
if (_currentIndex != 1 && _currentIndex != 0)
throw new Exception("invalid index");
}
else if (iIndex != _currentIndex)
{
if (iIndex != _currentIndex + 1)
throw new Exception("invalid index");
{
if (_debugMessages)
System.Console.WriteLine("\nNon-consequent index at {0}: {1} after {2}", CDImageLayout.TimeToString((uint)(sector + iSector)), iIndex, _currentIndex);
//throw new Exception("invalid index");
continue;
}
_currentIndex = iIndex;
if (_currentIndex == 1)
{
@@ -321,32 +377,35 @@ namespace CUETools.Ripper.SCSI
}
if (preemph)
_toc[iTrack].PreEmphasis = true;
if (dcp)
_toc[iTrack].DCP = true;
break;
}
case 2: // catalog
if (_toc.Catalog == null)
if (updateMap && _toc.Catalog == null)
{
StringBuilder catalog = new StringBuilder();
for (int i = 1; i < 8; i++)
catalog.AppendFormat("{0:x2}", _currentScan.QData[q_pos, i]);
catalog.AppendFormat("{0:x2}", QData[_currentScan, q_pos, i]);
_toc.Catalog = catalog.ToString(0, 13);
}
break;
case 3: //isrc
if (_toc[_currentTrack].ISRC == null)
if (updateMap && _toc[_currentTrack].ISRC == null)
{
StringBuilder isrc = new StringBuilder();
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();
isrc.Append(from6bit(QData[_currentScan, q_pos, 1] >> 2));
isrc.Append(from6bit(((QData[_currentScan, q_pos, 1] & 0x3) << 4) + (0x0f & (QData[_currentScan, q_pos, 2] >> 4))));
isrc.Append(from6bit(((QData[_currentScan, q_pos, 2] & 0xf) << 2) + (0x03 & (QData[_currentScan, q_pos, 3] >> 6))));
isrc.Append(from6bit((QData[_currentScan, q_pos, 3] & 0x3f)));
isrc.Append(from6bit(QData[_currentScan, q_pos, 4] >> 2));
isrc.Append(from6bit(((QData[_currentScan, q_pos, 4] & 0x3) << 4) + (0x0f & (QData[_currentScan, q_pos, 5] >> 4))));
isrc.AppendFormat("{0:x}", QData[_currentScan, q_pos, 5] & 0xf);
isrc.AppendFormat("{0:x2}", QData[_currentScan, q_pos, 6]);
isrc.AppendFormat("{0:x2}", QData[_currentScan, q_pos, 7]);
isrc.AppendFormat("{0:x}", QData[_currentScan, q_pos, 8] >> 4);
if (!isrc.ToString().Contains("#") && isrc.ToString() != "0000000000")
_toc[_currentTrack].ISRC = isrc.ToString();
}
break;
}
@@ -356,38 +415,60 @@ namespace CUETools.Ripper.SCSI
public unsafe bool TestReadCommand()
{
Device.MainChannelSelection[] mainmode = { Device.MainChannelSelection.UserData, Device.MainChannelSelection.F8h };
Device.SubChannelMode[] submode = { Device.SubChannelMode.None, Device.SubChannelMode.QOnly, Device.SubChannelMode.RWMode };
//ReadCDCommand[] readmode = { ReadCDCommand.ReadCdBEh, ReadCDCommand.ReadCdD8h };
ReadCDCommand[] readmode = { ReadCDCommand.ReadCdD8h, ReadCDCommand.ReadCdBEh };
Device.SubChannelMode[] submode = { Device.SubChannelMode.QOnly, Device.SubChannelMode.None, Device.SubChannelMode.RWMode };
Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.Mode296, Device.C2ErrorMode.Mode294, Device.C2ErrorMode.None };
Device.MainChannelSelection[] mainmode = { Device.MainChannelSelection.UserData, Device.MainChannelSelection.F8h };
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;
}
_currentStart = 0;
_currentScan = 0;
_currentTrack = -1;
_currentIndex = -1;
m_max_sectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
int sector = 3;
for (int q = 0; q <= 1 && !found; q++)
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];
_subChannelMode = submode[q];
_c2ErrorMode = c2mode[c];
_mainChannelMode = mainmode[m];
if (_forceReadCommand != ReadCDCommand.Unknown && _readCDCommand != _forceReadCommand)
continue;
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;
Device.CommandStatus st = FetchSectors(sector, m_max_sectors, false, false);
TimeSpan delay = DateTime.Now - tm;
if (st == Device.CommandStatus.Success && _subChannelMode == Device.SubChannelMode.QOnly && ProcessSubchannel(sector, m_max_sectors, false) == 0)
{
_autodetectResult += string.Format("{0}: {1}\n", CurrentReadCommand, "Got no subchannel information");
continue;
}
_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 && _subChannelMode != Device.SubChannelMode.RWMode;// && _subChannelMode != Device.SubChannelMode.QOnly;
//sector += m_max_sectors;
}
//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;
// }
// }
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;
_autodetectResult += "Chosen " + CurrentReadCommand + "\n";
else
_readCDCommand = ReadCDCommand.Unknown;
_currentStart = -1;
return found;
}
@@ -395,26 +476,26 @@ namespace CUETools.Ripper.SCSI
{
int c2Size = _c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : 296;
int oldSize = 4 * 588 + c2Size + (_subChannelMode == Device.SubChannelMode.None ? 0 : 16);
fixed (byte* readBuf = _readBuffer, qBuf = _subchannelBuffer, userData = _currentScan.UserData, c2Data = _currentScan.C2Data, qData = _currentScan.QData)
fixed (byte* readBuf = _readBuffer, qBuf = _subchannelBuffer, userData = UserData, c2Data = C2Data, qData = 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;
byte* userDataPtr = userData + (_currentScan * MSECTORS + sector - _currentStart + iSector) * 4 * 588;
byte* c2DataPtr = c2Data + (_currentScan * MSECTORS + sector - _currentStart + iSector) * 294;
byte* qDataPtr = qData + (_currentScan * MSECTORS + 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];
c2DataPtr[c2] = sectorPtr[4 * 588 + c2Size - 294 + c2];
else
for (int c2 = 0; c2 < 294; c2++)
c2DataPtr[c2] = 0xff;
c2DataPtr[c2] = 0; // 0xff??
if (_subChannelMode != Device.SubChannelMode.None)
for (int qi = 0; qi < 16; qi++)
qDataPtr[qi] = sectorPtr[4 * 588 + c2Size];
qDataPtr[qi] = sectorPtr[4 * 588 + c2Size + qi];
else
for (int qi = 0; qi < 16; qi++)
qDataPtr[qi] = qBuf[iSector * 16 + qi];
@@ -422,47 +503,45 @@ namespace CUETools.Ripper.SCSI
}
}
private unsafe Device.CommandStatus FetchSectors(int sector, int Sectors2Read, bool abort)
private unsafe Device.CommandStatus FetchSectors(int sector, int Sectors2Read, bool abort, bool subchannel)
{
Device.CommandStatus st;
fixed (byte* data = _readBuffer)
{
st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout);
if (_readCDCommand == ReadCDCommand.ReadCdBEh)
st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _c2ErrorMode, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout);
else
st = m_device.ReadCDDA(_subChannelMode, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), _timeout);
}
if (st == Device.CommandStatus.Success && _subChannelMode == Device.SubChannelMode.None && subchannel)
st = m_device.ReadSubChannel(2, (uint)sector, (uint)Sectors2Read, ref _subchannelBuffer, _timeout);
if (st == Device.CommandStatus.Success)
{
if (_subChannelMode == Device.SubChannelMode.None)
{
st = m_device.ReadSubChannel(2, (uint)sector, (uint)Sectors2Read, ref _subchannelBuffer, _timeout);
if (st == Device.CommandStatus.Success)
{
ReorganiseSectors(sector, Sectors2Read);
return st;
}
}
else
{
ReorganiseSectors(sector, Sectors2Read);
return st;
}
ReorganiseSectors(sector, Sectors2Read);
return st;
}
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)
{
if (_debugMessages)
System.Console.WriteLine("\n{0}: retrying one sector at a time", ex.Message);
int iErrors = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
if (FetchSectors(sector + iSector, 1, false) != Device.CommandStatus.Success)
if (FetchSectors(sector + iSector, 1, false, subchannel) != Device.CommandStatus.Success)
{
iErrors ++;
for (int i = 0; i < 4 * 588; i++)
_currentScan.UserData[sector + iSector - _currentStart, i] = 0;
UserData[_currentScan, sector + iSector - _currentStart, i] = 0;
for (int i = 0; i < 294; i++)
_currentScan.C2Data[sector + iSector - _currentStart, i] = 0xff;
C2Data[_currentScan, sector + iSector - _currentStart, i] = 0xff;
for (int i = 0; i < 16; i++)
_currentScan.QData[sector + iSector - _currentStart, i] = 0;
QData[_currentScan, sector + iSector - _currentStart, i] = 0;
if (_debugMessages)
System.Console.WriteLine("\nSector lost");
}
@@ -470,46 +549,136 @@ namespace CUETools.Ripper.SCSI
if (iErrors < Sectors2Read)
return Device.CommandStatus.Success;
}
throw new Exception(ex.Message + "; Autodetect: " + m_test_result);
throw ex;
}
private unsafe void CorrectSectors(int sector, int Sectors2Read, bool findWorst, bool markErrors)
private unsafe void ZeroMemory(short *buf, int count)
{
if (IntPtr.Size == 4)
{
Int32* start = (Int32*)buf;
Int32* end = (Int32*)(buf + count);
while (start < end)
*(start++) = 0;
}
else if (IntPtr.Size == 8)
{
Int64* start = (Int64*)buf;
Int64* end = (Int64*)(buf + count);
while (start < end)
*(start++) = 0;
}
else
throw new Exception("wierd IntPtr.Size");
}
private unsafe void ZeroMemory(byte* buf, int count)
{
if (IntPtr.Size == 4)
{
Int32* start = (Int32*)buf;
Int32* end = (Int32*)(buf + count);
while (start < end)
*(start++) = 0;
for (int i = 0; i < (count & 3); i++)
buf[count - i - 1] = 0;
}
else if (IntPtr.Size == 8)
{
Int64* start = (Int64*)buf;
Int64* end = (Int64*)(buf + count);
while (start < end)
*(start++) = 0;
for (int i = 0; i < (count & 7); i++)
buf[count - i - 1] = 0;
}
else
throw new Exception("wierd IntPtr.Size");
}
private unsafe void CorrectSectors0(int sector, int Sectors2Read)
{
int c2Score = 10;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
int pos = sector - _currentStart + iSector;
for (int iPar = 0; iPar < 4 * 588; iPar++)
{
int pos = sector - _currentStart + iSector;
int c2Bit = 0x80 >> (iPar % 8);
byte bestValue = UserData[0, pos, iPar];
_currentErrorsCount += (C2Data[0, pos, iPar >> 3] >> (iPar & 7)) & 1;
_currentData[pos * 4 * 588 + iPar] = bestValue;
}
}
}
Array.Clear(valueScore, 0, 256);
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++)
private unsafe void CorrectSectors1(int sector, int Sectors2Read)
{
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
int pos = sector - _currentStart + iSector;
for (int iPar = 0; iPar < 4 * 588; iPar++)
{
byte val1 = UserData[0, pos, iPar];
byte val2 = UserData[1, pos, iPar];
int err1 = (C2Data[0, pos, iPar >> 3] >> (iPar & 7)) & 1;
int err2 = (C2Data[1, pos, iPar >> 3] >> (iPar & 7)) & 1;
_currentErrorsCount += err1 | err2 | (val1 != val2 ? 1 : 0);
_currentData[pos * 4 * 588 + iPar] = err1 != 0 ? val2 : val1;
}
}
}
private unsafe void CorrectSectors(int pass, int sector, int Sectors2Read, bool findWorst, bool markErrors)
{
if (pass == 0)
{
CorrectSectors0(sector, Sectors2Read);
return;
}
if (pass == 1)
{
CorrectSectors1(sector, Sectors2Read);
return;
}
short c2Score = 10;
fixed (short* valueScore = _valueScore)
{
fixed (byte* userData = UserData, c2Data = C2Data, qData = QData)
{
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
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;
}
bool fError = valueScore[bestValue] * 2 <= c2Score + totalScore;
if (fError)
_currentErrorsCount++;
_currentData[(sector - _currentStart + iSector) * 4 * 588 + iPar] = bestValue;
if (findWorst)
{
for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality += Math.Min(0, valueScore[_scanResults[result].UserData[pos, iPar]] - c2Score - 1);
_currentScan.Quality += Math.Min(0, valueScore[_currentScan.UserData[pos, iPar]] - c2Score - 1);
}
if (markErrors)
{
_errors[sector + iSector] = fError;
_errorsCount += fError ? 1 : 0;
int pos = sector - _currentStart + iSector;
for (int iPar = 0; iPar < 4 * 588; iPar++)
{
int c2Bit = 0x80 >> (iPar % 8);
ZeroMemory(valueScore, 256);
byte bestValue = 0;
short totalScore = 0;
for (int result = 0; result <= pass; result++)
{
int offs = (result * MSECTORS + pos) * 4 * 588 + iPar;
byte value = userData[offs];
short score = (short)(1 + (((c2Data[offs >> 3] & c2Bit) == 0) ? c2Score : (short)0));
valueScore[value] += score;
totalScore += score;
if (valueScore[value] > valueScore[bestValue])
bestValue = value;
}
bool fError = valueScore[bestValue] <= _correctionQuality + c2Score + totalScore / 2;
if (fError)
_currentErrorsCount++;
_currentData[(sector - _currentStart + iSector) * 4 * 588 + iPar] = bestValue;
if (findWorst)
{
for (int result = 0; result <= pass; result++)
Quality[result] += Math.Min(0, valueScore[userData[(result * MSECTORS + pos) * 4 * 588 + iPar]] - c2Score - 2);
}
if (markErrors)
{
_errors[sector + iSector] |= fError;
_errorsCount += fError ? 1 : 0;
}
}
}
}
}
@@ -569,20 +738,18 @@ namespace CUETools.Ripper.SCSI
// return realErrors;
//}
public void PrefetchSector(int iSector)
public unsafe void PrefetchSector(int iSector)
{
int nPasses = 16 + _correctionQuality * 2;
int nExtraPasses = 8 + _correctionQuality;
int nExtraPasses = MAXSCANS / 2 + _correctionQuality;
if (_currentStart == MSECTORS * (iSector / MSECTORS))
return;
if (m_test_result == null && !TestReadCommand())
throw new Exception("failed to autodetect read command: " + m_test_result);
if (_readCDCommand == ReadCDCommand.Unknown && !TestReadCommand())
throw new Exception("failed to autodetect read command: " + _autodetectResult);
_currentStart = MSECTORS * (iSector / MSECTORS);
_currentEnd = Math.Min(_currentStart + MSECTORS, (int)_toc.AudioLength);
_scanResults = new List<ScanResults>();
//FileStream correctFile = new FileStream("correct.wav", FileMode.Open);
//byte[] realData = new byte[MSECTORS * 4 * 588];
@@ -591,82 +758,80 @@ namespace CUETools.Ripper.SCSI
// throw new Exception("read");
//correctFile.Close();
for (int pass = 0; pass <= nPasses + nExtraPasses; pass++)
fixed (byte* userData = UserData, c2Data = C2Data, qData = QData)
{
DateTime PassTime = DateTime.Now, LastFetch = DateTime.Now;
_currentScan = new ScanResults(MSECTORS);
_currentErrorsCount = 0;
for (int sector = _currentStart; sector < _currentEnd; sector += m_max_sectors)
for (int pass = 0; pass < MAXSCANS + nExtraPasses; pass++)
{
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 * Sectors2Read / (75 * speed) - (int)((DateTime.Now - LastFetch).TotalMilliseconds)));
LastFetch = DateTime.Now;
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;
// TODO: move to TestReadCommand
FetchSectors(sector, Sectors2Read, true);
}
CorrectSectors(sector, Sectors2Read, pass > nPasses, pass == nPasses + nExtraPasses);
if (ReadProgress != null)
ReadProgress(this, new ReadProgressArgs(sector + Sectors2Read, pass, _currentStart, _currentEnd, _currentErrorsCount, PassTime));
}
//System.Console.WriteLine();
//if (CorrectSectorsTest(start, _currentEnd, 10, realData) == 0)
// break;
if (pass == nPasses + nExtraPasses)
break;
if (pass > nPasses)
{
int worstPass = -1;
for (int result = 0; result < _scanResults.Count; result++)
if (_scanResults[result].Quality < (worstPass < 0 ? _currentScan.Quality : _scanResults[worstPass].Quality))
worstPass = result;
//if (worstPass < 0)
// System.Console.WriteLine("bad scan");
//else
// System.Console.WriteLine("{0}->{1}, {2}->{3}", _scanResults[worstPass].Quality, _currentScan.Quality, CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, -1), CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, worstPass));
if (worstPass < 0)
_currentScan = null;
DateTime PassTime = DateTime.Now, LastFetch = DateTime.Now;
if (pass < MAXSCANS)
_currentScan = pass;
else
_scanResults[worstPass] = _currentScan;
for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality = 0;
continue;
}
if (_currentErrorsCount == 0 && pass >= _correctionQuality)
{
bool syncOk = true;
//if (pass == 0)
//{
// ScanResults saved = _currentScan;
// _currentScan = new ScanResults(_currentEnd - _currentStart, CB_AUDIO);
// for (int sector = _currentStart; sector < _currentEnd && syncOk; sector += 7)
// {
// FetchSectors(sector, 2);
// for (int i = 0; i < 2 * CB_AUDIO; i++)
// if (_currentScan.Data[(sector - _currentStart) * CB_AUDIO + i] != saved.Data[(sector - _currentStart) * CB_AUDIO + i])
// {
// System.Console.WriteLine("Lost Sync");
// syncOk = false;
// break;
// }
// }
// _currentScan = saved;
//}
if (syncOk)
{
_currentScan = 0;
for (int result = 1; result < MAXSCANS; result++)
if (Quality[result] < Quality[_currentScan])
_currentScan = result;
//if (worstPass < 0)
// System.Console.WriteLine("bad scan");
//else
// System.Console.WriteLine("{0}->{1}, {2}->{3}", _scanResults[worstPass].Quality, _currentScan.Quality, CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, -1), CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, worstPass));
}
for (int result = 0; result < MAXSCANS; result++)
Quality[result] = 0;
_currentErrorsCount = 0;
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;
FetchSectors(sector, Sectors2Read, true, pass == 0);
//TimeSpan delay1 = DateTime.Now - LastFetch;
if (pass == 0)
ProcessSubchannel(sector, Sectors2Read, true);
//DateTime LastFetched = DateTime.Now;
CorrectSectors(Math.Min(pass, MAXSCANS - 1), sector, Sectors2Read, pass >= MAXSCANS - 1, pass == MAXSCANS - 1 + nExtraPasses);
//TimeSpan delay2 = DateTime.Now - LastFetched;
//if (sector == _currentStart)
//System.Console.WriteLine("\n{0},{1}", delay1.TotalMilliseconds, delay2.TotalMilliseconds);
if (ReadProgress != null)
ReadProgress(this, new ReadProgressArgs(sector + Sectors2Read, pass, _currentStart, _currentEnd, _currentErrorsCount, PassTime));
}
//System.Console.WriteLine();
//if (CorrectSectorsTest(start, _currentEnd, 10, realData) == 0)
// break;
//if (pass == MAXSCANS - 1 + nExtraPasses)
// break;
if (_currentErrorsCount == 0 && pass >= _correctionQuality)
break;
}
_scanResults.Add(_currentScan);
//if (_currentErrorsCount == 0 && pass >= _correctionQuality)
//{
// bool syncOk = true;
// //if (pass == 0)
// //{
// // ScanResults saved = _currentScan;
// // _currentScan = new ScanResults(_currentEnd - _currentStart, CB_AUDIO);
// // for (int sector = _currentStart; sector < _currentEnd && syncOk; sector += 7)
// // {
// // FetchSectors(sector, 2);
// // for (int i = 0; i < 2 * CB_AUDIO; i++)
// // if (_currentScan.Data[(sector - _currentStart) * CB_AUDIO + i] != saved.Data[(sector - _currentStart) * CB_AUDIO + i])
// // {
// // System.Console.WriteLine("Lost Sync");
// // syncOk = false;
// // break;
// // }
// // }
// // _currentScan = saved;
// //}
// if (syncOk)
// break;
//}
}
}
_currentScan = null;
_scanResults = null;
}
public uint Read(int[,] buff, uint sampleCount)
@@ -806,9 +971,14 @@ namespace CUETools.Ripper.SCSI
}
set
{
if (_toc.AudioLength <= 0)
throw new Exception("no audio");
_currentTrack = -1;
_currentIndex = -1;
_sampleOffset = (int) value + _driveOffset;
_crcErrorsCount = 0;
_errorsCount = 0;
_errors = new BitArray((int)_toc.AudioLength);
_sampleOffset = (int)value + _driveOffset;
}
}
@@ -873,24 +1043,12 @@ namespace CUETools.Ripper.SCSI
}
}
internal class ScanResults
enum ReadCDCommand
{
public byte[,] UserData;
public byte[,] C2Data;
public byte[,] QData;
public long Quality;
public ScanResults(int msector)
{
//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;
}
}
ReadCdBEh,
ReadCdD8h,
Unknown
};
public sealed class SCSIException : Exception
{