2008-11-26 18:57:40 +00:00
|
|
|
// ****************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// CUERipper
|
|
|
|
|
// Copyright (C) 2008 Gregory S. Chudov (gchudov@gmail.com)
|
|
|
|
|
//
|
|
|
|
|
// 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;
|
2008-11-30 01:43:17 +00:00
|
|
|
using System.IO;
|
2008-11-26 18:57:40 +00:00
|
|
|
using Bwg.Scsi;
|
|
|
|
|
using Bwg.Logging;
|
2008-11-28 22:20:17 +00:00
|
|
|
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;
|
2009-01-17 04:09:38 +00:00
|
|
|
//const int MSECTORS = 5*1024*1024 / (4 * 588);
|
|
|
|
|
const int MSECTORS = 2400;
|
2008-11-26 18:57:40 +00:00
|
|
|
int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1;
|
2008-11-30 01:43:17 +00:00
|
|
|
Logger m_logger;
|
2008-11-28 22:20:17 +00:00
|
|
|
CDImageLayout _toc;
|
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 byte[,] QData;
|
|
|
|
|
public long[] byte2long;
|
2008-12-06 05:44:14 +00:00
|
|
|
BitArray _errors;
|
|
|
|
|
int _errorsCount;
|
2008-12-10 06:48:38 +00:00
|
|
|
int _crcErrorsCount = 0;
|
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;
|
2008-12-10 06:48:38 +00:00
|
|
|
Device.SubChannelMode _subChannelMode = Device.SubChannelMode.QOnly;
|
2008-12-07 00:33:20 +00:00
|
|
|
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];
|
|
|
|
|
byte[] _subchannelBuffer = new byte[NSECTORS * 16];
|
2008-12-10 21:42:37 +00:00
|
|
|
bool _qChannelInBCD = true;
|
2008-12-06 05:44:14 +00:00
|
|
|
|
|
|
|
|
public event EventHandler<ReadProgressArgs> ReadProgress;
|
2008-11-26 18:57:40 +00:00
|
|
|
|
2008-11-28 22:20:17 +00:00
|
|
|
public CDImageLayout TOC
|
2008-11-26 18:57:40 +00:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _toc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-06 05:44:14 +00:00
|
|
|
public BitArray Errors
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _errors;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ErrorsCount
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _errorsCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
{
|
2008-12-10 06:48:38 +00:00
|
|
|
if (_autodetectResult != null || TestReadCommand())
|
|
|
|
|
return _autodetectResult;
|
|
|
|
|
string ret = _autodetectResult;
|
|
|
|
|
_autodetectResult = null;
|
|
|
|
|
return ret;
|
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" :
|
2009-01-17 04:09:38 +00:00
|
|
|
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"),
|
2009-01-17 04:09:38 +00:00
|
|
|
(_mainChannelMode == Device.MainChannelSelection.UserData ? 0x10 : 0xF8) +
|
|
|
|
|
(_c2ErrorMode == Device.C2ErrorMode.None ? 0 : _c2ErrorMode == Device.C2ErrorMode.Mode294 ? 2 : 4),
|
2008-12-10 06:48:38 +00:00
|
|
|
(_subChannelMode == Device.SubChannelMode.None ? "00h" : _subChannelMode == Device.SubChannelMode.QOnly ? "02h" : "04h"),
|
2008-12-10 22:02:43 +00:00
|
|
|
_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()
|
|
|
|
|
{
|
2008-11-30 01:43:17 +00:00
|
|
|
m_logger = new Logger();
|
2008-12-06 05:44:14 +00:00
|
|
|
_crc = new Crc16Ccitt(InitialCrcValue.Zeros);
|
2010-02-28 21:04:34 +00:00
|
|
|
UserData = new long[MSECTORS, 2, 4 * 588];
|
|
|
|
|
C2Count = new byte[MSECTORS, 294];
|
2010-02-23 15:15:08 +00:00
|
|
|
QData = new byte[MSECTORS, 16];
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
// Open the base device
|
2008-12-07 23:12:01 +00:00
|
|
|
m_device_letter = Drive;
|
2009-01-17 04:09:38 +00:00
|
|
|
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))
|
2009-03-04 21:30:56 +00:00
|
|
|
throw new Exception("Open failed: " + 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 || !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");
|
2008-11-30 01:43:17 +00:00
|
|
|
|
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)
|
2009-01-17 04:09:38 +00:00
|
|
|
throw new SCSIException("ReadTOC", 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
|
|
|
|
|
|
|
|
st = m_device.ReadCDText(out cdtext);
|
|
|
|
|
// new CDTextEncoderDecoder
|
|
|
|
|
|
2008-11-30 00:03:49 +00:00
|
|
|
_toc = new CDImageLayout();
|
2008-11-26 18:57:40 +00:00
|
|
|
for (int iTrack = 0; iTrack < toc.Count - 1; iTrack++)
|
2008-11-30 00:03:49 +00:00
|
|
|
_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,
|
2009-01-28 04:53:13 +00:00
|
|
|
(toc[iTrack].Control & 1) == 1));
|
2008-12-06 05:44:14 +00:00
|
|
|
if (_toc.AudioLength > 0)
|
2009-01-28 04:53:13 +00:00
|
|
|
{
|
|
|
|
|
if (_toc[1].IsAudio)
|
|
|
|
|
_toc[1][0].Start = 0;
|
2009-01-17 04:09:38 +00:00
|
|
|
Position = 0;
|
2009-01-28 04:53:13 +00:00
|
|
|
}
|
2008-11-26 18:57:40 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Close()
|
|
|
|
|
{
|
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;
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-07 00:33:20 +00:00
|
|
|
private int ProcessSubchannel(int sector, int Sectors2Read, bool updateMap)
|
2008-11-26 20:20:41 +00:00
|
|
|
{
|
2008-12-07 00:33:20 +00:00
|
|
|
int posCount = 0;
|
2008-11-26 20:20:41 +00:00
|
|
|
for (int iSector = 0; iSector < Sectors2Read; iSector++)
|
|
|
|
|
{
|
2008-12-10 06:48:38 +00:00
|
|
|
int q_pos = (sector - _currentStart + iSector);
|
2010-02-23 15:15:08 +00:00
|
|
|
int ctl = QData[q_pos, 0] >> 4;
|
|
|
|
|
int adr = QData[q_pos, 0] & 7;
|
2008-12-06 05:44:14 +00:00
|
|
|
bool preemph = (ctl & 1) == 1;
|
2008-12-10 06:48:38 +00:00
|
|
|
bool dcp = (ctl & 2) == 2;
|
2008-12-10 21:42:37 +00:00
|
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
2010-02-23 15:15:08 +00:00
|
|
|
_subchannelBuffer[i] = QData[ q_pos, i];
|
2008-12-10 21:42:37 +00:00
|
|
|
if (!_qChannelInBCD && adr == 1)
|
|
|
|
|
{
|
|
|
|
|
_subchannelBuffer[3] = toBCD(_subchannelBuffer[3]);
|
|
|
|
|
_subchannelBuffer[4] = toBCD(_subchannelBuffer[4]);
|
|
|
|
|
_subchannelBuffer[5] = toBCD(_subchannelBuffer[5]);
|
|
|
|
|
_subchannelBuffer[7] = toBCD(_subchannelBuffer[7]);
|
|
|
|
|
_subchannelBuffer[8] = toBCD(_subchannelBuffer[8]);
|
|
|
|
|
_subchannelBuffer[9] = toBCD(_subchannelBuffer[9]);
|
|
|
|
|
}
|
|
|
|
|
ushort crc = _crc.ComputeChecksum(_subchannelBuffer, 0, 10);
|
|
|
|
|
crc ^= 0xffff;
|
2010-02-23 15:15:08 +00:00
|
|
|
if ((QData[q_pos, 10] != 0 || QData[q_pos, 11] != 0) &&
|
|
|
|
|
((byte)(crc & 0xff) != QData[q_pos, 11] || (byte)(crc >> 8) != QData[q_pos, 10])
|
2008-12-10 21:42:37 +00:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (!updateMap)
|
|
|
|
|
continue;
|
|
|
|
|
_crcErrorsCount++;
|
|
|
|
|
if (_debugMessages && _crcErrorsCount < 4)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder st = new StringBuilder();
|
|
|
|
|
for (int i = 0; i < 12; i++)
|
2010-02-23 15:15:08 +00:00
|
|
|
st.AppendFormat(",0x{0:X2}", QData[q_pos, i]);
|
2009-01-17 04:09:38 +00:00
|
|
|
System.Console.WriteLine("\rCRC error@{0}{1};", CDImageLayout.TimeToString((uint)(sector + iSector)), st.ToString());
|
2008-12-10 21:42:37 +00:00
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2008-11-26 20:20:41 +00:00
|
|
|
switch (adr)
|
|
|
|
|
{
|
|
|
|
|
case 1: // current position
|
|
|
|
|
{
|
2010-02-23 15:15:08 +00:00
|
|
|
int iTrack = fromBCD(QData[q_pos, 1]);
|
|
|
|
|
int iIndex = fromBCD(QData[q_pos, 2]);
|
|
|
|
|
int mm = _qChannelInBCD ? fromBCD(QData[q_pos, 7]) : QData[q_pos, 7];
|
|
|
|
|
int ss = _qChannelInBCD ? fromBCD(QData[q_pos, 8]) : QData[q_pos, 8];
|
|
|
|
|
int ff = _qChannelInBCD ? fromBCD(QData[q_pos, 9]) : QData[q_pos, 9];
|
2008-12-06 05:44:14 +00:00
|
|
|
//if (sec != sector + iSector)
|
2008-12-07 00:33:20 +00:00
|
|
|
// 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);
|
2008-11-28 22:20:17 +00:00
|
|
|
if (iTrack == 110)
|
2008-12-06 05:44:14 +00:00
|
|
|
{
|
|
|
|
|
if (sector + iSector + 75 < _toc.AudioLength)
|
|
|
|
|
throw new Exception("lead out area encountred");
|
|
|
|
|
// make sure that data is zero?
|
2008-12-07 00:33:20 +00:00
|
|
|
return posCount;
|
2008-12-06 05:44:14 +00:00
|
|
|
}
|
2008-11-28 22:20:17 +00:00
|
|
|
if (iTrack == 0)
|
|
|
|
|
throw new Exception("lead in area encountred");
|
2008-12-07 00:33:20 +00:00
|
|
|
posCount++;
|
2008-12-06 05:44:14 +00:00
|
|
|
if (!updateMap)
|
|
|
|
|
break;
|
2008-12-10 21:42:37 +00:00
|
|
|
int sec = ff + 75 * (ss + 60 * mm) - 150; // sector + iSector;
|
2009-01-28 04:53:13 +00:00
|
|
|
if (iTrack >= _toc.FirstAudio + _toc.AudioTracks)
|
2008-12-07 00:33:20 +00:00
|
|
|
throw new Exception("strange track number encountred");
|
2008-11-26 20:20:41 +00:00
|
|
|
if (iTrack != _currentTrack)
|
|
|
|
|
{
|
2008-12-10 06:48:38 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2008-11-26 20:20:41 +00:00
|
|
|
_currentTrack = iTrack;
|
2008-12-06 05:44:14 +00:00
|
|
|
_currentTrackActualStart = sec;
|
2008-11-26 20:20:41 +00:00
|
|
|
_currentIndex = iIndex;
|
|
|
|
|
}
|
2008-11-28 22:20:17 +00:00
|
|
|
else if (iIndex != _currentIndex)
|
|
|
|
|
{
|
|
|
|
|
if (iIndex != _currentIndex + 1)
|
2008-12-10 06:48:38 +00:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2008-11-28 22:20:17 +00:00
|
|
|
_currentIndex = iIndex;
|
|
|
|
|
if (_currentIndex == 1)
|
2008-11-26 20:20:41 +00:00
|
|
|
{
|
2008-12-06 05:44:14 +00:00
|
|
|
uint pregap = (uint)(sec - _currentTrackActualStart);
|
2008-12-01 16:28:23 +00:00
|
|
|
_toc[iTrack][0].Start = _toc[iTrack].Start - pregap;
|
2008-12-06 05:44:14 +00:00
|
|
|
_currentTrackActualStart = sec;
|
2008-12-01 16:28:23 +00:00
|
|
|
} else
|
2008-12-06 05:44:14 +00:00
|
|
|
_toc[iTrack].AddIndex(new CDTrackIndex((uint)iIndex, (uint)(_toc[iTrack].Start + sec - _currentTrackActualStart)));
|
2008-11-28 22:20:17 +00:00
|
|
|
_currentIndex = iIndex;
|
|
|
|
|
}
|
2008-12-06 05:44:14 +00:00
|
|
|
if (preemph)
|
|
|
|
|
_toc[iTrack].PreEmphasis = true;
|
2008-12-10 06:48:38 +00:00
|
|
|
if (dcp)
|
|
|
|
|
_toc[iTrack].DCP = true;
|
2008-11-26 20:20:41 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 2: // catalog
|
2008-12-10 06:48:38 +00:00
|
|
|
if (updateMap && _toc.Catalog == null)
|
2008-11-26 20:20:41 +00:00
|
|
|
{
|
|
|
|
|
StringBuilder catalog = new StringBuilder();
|
|
|
|
|
for (int i = 1; i < 8; i++)
|
2010-02-23 15:15:08 +00:00
|
|
|
catalog.AppendFormat("{0:x2}", QData[q_pos, i]);
|
2008-11-28 22:20:17 +00:00
|
|
|
_toc.Catalog = catalog.ToString(0, 13);
|
2008-11-26 20:20:41 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 3: //isrc
|
2008-12-10 06:48:38 +00:00
|
|
|
if (updateMap && _toc[_currentTrack].ISRC == null)
|
2008-11-28 22:20:17 +00:00
|
|
|
{
|
|
|
|
|
StringBuilder isrc = new StringBuilder();
|
2010-02-23 15:15:08 +00:00
|
|
|
isrc.Append(from6bit(QData[q_pos, 1] >> 2));
|
|
|
|
|
isrc.Append(from6bit(((QData[q_pos, 1] & 0x3) << 4) + (0x0f & (QData[q_pos, 2] >> 4))));
|
|
|
|
|
isrc.Append(from6bit(((QData[q_pos, 2] & 0xf) << 2) + (0x03 & (QData[q_pos, 3] >> 6))));
|
|
|
|
|
isrc.Append(from6bit((QData[q_pos, 3] & 0x3f)));
|
|
|
|
|
isrc.Append(from6bit(QData[q_pos, 4] >> 2));
|
|
|
|
|
isrc.Append(from6bit(((QData[q_pos, 4] & 0x3) << 4) + (0x0f & (QData[q_pos, 5] >> 4))));
|
|
|
|
|
isrc.AppendFormat("{0:x}", QData[q_pos, 5] & 0xf);
|
|
|
|
|
isrc.AppendFormat("{0:x2}", QData[q_pos, 6]);
|
|
|
|
|
isrc.AppendFormat("{0:x2}", QData[q_pos, 7]);
|
|
|
|
|
isrc.AppendFormat("{0:x}", QData[q_pos, 8] >> 4);
|
2008-12-10 06:48:38 +00:00
|
|
|
if (!isrc.ToString().Contains("#") && isrc.ToString() != "0000000000")
|
|
|
|
|
_toc[_currentTrack].ISRC = isrc.ToString();
|
2008-11-28 22:20:17 +00:00
|
|
|
}
|
2008-11-26 20:20:41 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-07 00:33:20 +00:00
|
|
|
return posCount;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-07 23:12:01 +00:00
|
|
|
public unsafe bool TestReadCommand()
|
2008-12-07 00:33:20 +00:00
|
|
|
{
|
2008-12-10 06:48:38 +00:00
|
|
|
//ReadCDCommand[] readmode = { ReadCDCommand.ReadCdBEh, ReadCDCommand.ReadCdD8h };
|
|
|
|
|
ReadCDCommand[] readmode = { ReadCDCommand.ReadCdD8h, ReadCDCommand.ReadCdBEh };
|
|
|
|
|
Device.SubChannelMode[] submode = { Device.SubChannelMode.QOnly, Device.SubChannelMode.None, Device.SubChannelMode.RWMode };
|
2008-12-07 23:12:01 +00:00
|
|
|
Device.C2ErrorMode[] c2mode = { Device.C2ErrorMode.Mode296, Device.C2ErrorMode.Mode294, 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;
|
2008-12-10 06:48:38 +00:00
|
|
|
_currentStart = 0;
|
|
|
|
|
_currentTrack = -1;
|
|
|
|
|
_currentIndex = -1;
|
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;
|
|
|
|
|
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;
|
2009-01-17 04:09:38 +00:00
|
|
|
if (_readCDCommand == ReadCDCommand.ReadCdD8h) // && (_c2ErrorMode != Device.C2ErrorMode.None || _mainChannelMode != Device.MainChannelSelection.UserData))
|
2008-12-10 06:48:38 +00:00
|
|
|
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;
|
2008-12-10 21:42:37 +00:00
|
|
|
if (st == Device.CommandStatus.Success && _subChannelMode == Device.SubChannelMode.QOnly)
|
2008-12-10 06:48:38 +00:00
|
|
|
{
|
2008-12-10 21:42:37 +00:00
|
|
|
_qChannelInBCD = false;
|
|
|
|
|
int sub1 = ProcessSubchannel(sector, m_max_sectors, false);
|
|
|
|
|
_qChannelInBCD = true;
|
|
|
|
|
int sub2 = ProcessSubchannel(sector, m_max_sectors, false);
|
|
|
|
|
_qChannelInBCD = sub2 >= sub1;
|
|
|
|
|
if (sub1 == 0 && sub2 == 0)
|
|
|
|
|
{
|
|
|
|
|
_autodetectResult += string.Format("{0}: {1}\n", CurrentReadCommand, "Got no subchannel information");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2008-12-10 06:48:38 +00:00
|
|
|
}
|
|
|
|
|
_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;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2008-12-07 23:12:01 +00:00
|
|
|
if (found)
|
2008-12-10 06:48:38 +00:00
|
|
|
_autodetectResult += "Chosen " + CurrentReadCommand + "\n";
|
|
|
|
|
else
|
|
|
|
|
_readCDCommand = ReadCDCommand.Unknown;
|
|
|
|
|
_currentStart = -1;
|
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;
|
2010-02-23 15:15:08 +00:00
|
|
|
int oldSize = 4 * 588 + c2Size + (_subChannelMode == Device.SubChannelMode.None ? 0 : 16);
|
2010-02-28 21:04:34 +00:00
|
|
|
fixed (byte* readBuf = _readBuffer, qBuf = _subchannelBuffer, qData = QData, c2Count = C2Count)
|
|
|
|
|
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;
|
2010-02-23 15:15:08 +00:00
|
|
|
byte* qDataPtr = qData + (sector - _currentStart + iSector) * 16;
|
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)
|
|
|
|
|
{
|
|
|
|
|
// sometimes sector C2 byte is placed after C2 info, not before!!
|
|
|
|
|
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 if (sectorPtr[4 * 588] == (c2 | sectorPtr[4 * 588 + 294] | sectorPtr[4 * 588 + 295]))
|
|
|
|
|
offs = 2;
|
|
|
|
|
else
|
|
|
|
|
throw new Exception("invalid C2 pointers");
|
|
|
|
|
}
|
|
|
|
|
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]] * 3;
|
|
|
|
|
}
|
2008-12-07 23:12:01 +00:00
|
|
|
if (_subChannelMode != Device.SubChannelMode.None)
|
|
|
|
|
for (int qi = 0; qi < 16; qi++)
|
2008-12-10 06:48:38 +00:00
|
|
|
qDataPtr[qi] = sectorPtr[4 * 588 + c2Size + qi];
|
2008-12-07 23:12:01 +00:00
|
|
|
else
|
|
|
|
|
for (int qi = 0; qi < 16; qi++)
|
|
|
|
|
qDataPtr[qi] = qBuf[iSector * 16 + qi];
|
|
|
|
|
}
|
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-02-28 21:04:34 +00:00
|
|
|
ZeroMemory((byte*)userData, 8 * 2 * 4 * 588 * Sectors2Read);
|
|
|
|
|
ZeroMemory(c2Count, 294 * Sectors2Read);
|
2010-02-23 15:15:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-10 06:48:38 +00:00
|
|
|
private unsafe Device.CommandStatus FetchSectors(int sector, int Sectors2Read, bool abort, bool subchannel)
|
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
|
|
|
{
|
2009-01-17 04:09:38 +00:00
|
|
|
if (_debugMessages)
|
|
|
|
|
{
|
|
|
|
|
int size = (4 * 588 +
|
|
|
|
|
(_subChannelMode == Device.SubChannelMode.QOnly ? 16 : _subChannelMode == Device.SubChannelMode.RWMode ? 96 : 0) +
|
|
|
|
|
(_c2ErrorMode == Device.C2ErrorMode.Mode294 ? 294 : _c2ErrorMode == Device.C2ErrorMode.Mode296 ? 296 : 0)) * (int)Sectors2Read;
|
|
|
|
|
MemSet(data, size, 0xff);
|
|
|
|
|
}
|
2008-12-10 06:48:38 +00:00
|
|
|
if (_readCDCommand == ReadCDCommand.ReadCdBEh)
|
2009-01-28 04:53:13 +00:00
|
|
|
st = m_device.ReadCDAndSubChannel(_mainChannelMode, _subChannelMode, _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
|
2009-01-28 04:53:13 +00:00
|
|
|
st = m_device.ReadCDDA(_subChannelMode, (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
|
|
|
|
|
|
|
|
if (st == Device.CommandStatus.Success && _subChannelMode == Device.SubChannelMode.None && subchannel)
|
2009-01-28 04:53:13 +00:00
|
|
|
st = m_device.ReadSubChannel(2, (uint)sector + _toc[_toc.FirstAudio][0].Start, (uint)Sectors2Read, ref _subchannelBuffer, _timeout);
|
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("ReadCD", m_device, st);
|
|
|
|
|
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
|
|
|
{
|
2008-12-10 06:48:38 +00:00
|
|
|
if (FetchSectors(sector + iSector, 1, false, subchannel) != 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
|
|
|
for (int i = 0; i < 16; i++)
|
2010-02-23 15:15:08 +00:00
|
|
|
QData[ sector + iSector - _currentStart, i] = 0;
|
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 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");
|
2008-12-06 05:44:14 +00:00
|
|
|
}
|
|
|
|
|
|
2008-12-10 06:48:38 +00:00
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-17 04:09:38 +00:00
|
|
|
private unsafe void MemSet(byte* buf, int count, byte val)
|
|
|
|
|
{
|
|
|
|
|
Int32 intVal = (((((val << 8) + val) << 8) + val) << 8) + val;
|
|
|
|
|
if (IntPtr.Size == 4)
|
|
|
|
|
{
|
|
|
|
|
Int32* start = (Int32*)buf;
|
|
|
|
|
Int32* end = (Int32*)(buf + count);
|
|
|
|
|
while (start < end)
|
|
|
|
|
*(start++) = intVal;
|
|
|
|
|
for (int i = 0; i < (count & 3); i++)
|
|
|
|
|
buf[count - i - 1] = val;
|
|
|
|
|
}
|
|
|
|
|
else if (IntPtr.Size == 8)
|
|
|
|
|
{
|
|
|
|
|
Int64 int64Val = ((Int64)intVal << 32) + intVal;
|
|
|
|
|
Int64* start = (Int64*)buf;
|
|
|
|
|
Int64* end = (Int64*)(buf + count);
|
|
|
|
|
while (start < end)
|
|
|
|
|
*(start++) = int64Val;
|
|
|
|
|
for (int i = 0; i < (count & 7); i++)
|
|
|
|
|
buf[count - i - 1] = val;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw new Exception("wierd IntPtr.Size");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PrintErrors(int pass, int sector, int Sectors2Read, byte[] realData)
|
|
|
|
|
{
|
2010-02-23 15:15:08 +00:00
|
|
|
//for (int iSector = 0; iSector < Sectors2Read; iSector++)
|
|
|
|
|
//{
|
|
|
|
|
// int pos = sector - _currentStart + iSector;
|
|
|
|
|
// if (_debugMessages)
|
|
|
|
|
// {
|
|
|
|
|
// StringBuilder st = new StringBuilder();
|
|
|
|
|
// for (int i = 0; i < 294; i++)
|
|
|
|
|
// if (C2Data[pos, i] != 0)
|
|
|
|
|
// {
|
|
|
|
|
// for (int j = i; j < i + 23; j++)
|
|
|
|
|
// if (j < 294)
|
|
|
|
|
// st.AppendFormat("{0:X2}", C2Data[_currentScan, pos, j]);
|
|
|
|
|
// else
|
|
|
|
|
// st.Append(" ");
|
|
|
|
|
// System.Console.WriteLine("\rC2 error @{0}[{1:000}]{2};", CDImageLayout.TimeToString((uint)(sector + iSector)), i, st.ToString());
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
2009-01-17 04:09:38 +00:00
|
|
|
//for (int i = 0; i < 4 * 588; i++)
|
|
|
|
|
// if (_currentData[pos * 4 * 588 + i] != realData[pos * 4 * 588 + i])
|
|
|
|
|
// {
|
|
|
|
|
// StringBuilder st = new StringBuilder();
|
|
|
|
|
// for (int j = i; j < i + 25; j++)
|
|
|
|
|
// if (j < 4 * 588)
|
|
|
|
|
// st.AppendFormat("{0:X2}", realData[pos * 4 * 588 + j]);
|
|
|
|
|
// else
|
|
|
|
|
// st.Append(" ");
|
|
|
|
|
// System.Console.WriteLine("\r{0}[--][{1:X3}]{2};", CDImageLayout.TimeToString((uint)(sector + iSector)), i, st.ToString());
|
|
|
|
|
// st.Length = 0;
|
|
|
|
|
// for (int result = 0; result <= pass; result++)
|
|
|
|
|
// {
|
|
|
|
|
// for (int j = i; j < i + 25; j++)
|
|
|
|
|
// if (j < 4 * 588)
|
|
|
|
|
// st.AppendFormat("{0:X2}", UserData[result, pos, j]);
|
|
|
|
|
// else
|
|
|
|
|
// st.Append(" ");
|
|
|
|
|
// System.Console.WriteLine("\r{0}[{3:X2}][{1:X3}]{2};", CDImageLayout.TimeToString((uint)(sector + iSector)), i, st.ToString(), result);
|
|
|
|
|
// st.Length = 0;
|
|
|
|
|
// //int c2Bit = 0x80 >> (i % 8);
|
|
|
|
|
// //byte value = UserData[result, pos, i];
|
|
|
|
|
// //short score = (short)(1 + (((C2Data[result, pos, i >> 3] & c2Bit) == 0) ? (short) 10 : (short)0));
|
|
|
|
|
// //st.AppendFormat("{0:X2}[{1:X2}]", value, score);
|
|
|
|
|
// }
|
|
|
|
|
// i += 25;
|
|
|
|
|
// //return;
|
|
|
|
|
// //while (st.Length < 46)
|
|
|
|
|
// // st.Append(' ');
|
|
|
|
|
// //System.Console.WriteLine("\rReal error @{0}[{1:000}]{2};", CDImageLayout.TimeToString((uint)(sector + iSector)), i, st.ToString());
|
|
|
|
|
// }
|
|
|
|
|
|
2010-02-23 15:15:08 +00:00
|
|
|
// }
|
|
|
|
|
//}
|
2008-12-10 06:48:38 +00:00
|
|
|
}
|
|
|
|
|
|
2010-02-23 15:15:08 +00:00
|
|
|
private unsafe void CorrectSectors(int pass, int sector, int Sectors2Read, bool markErrors)
|
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;
|
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
|
|
|
}
|
2010-02-28 21:04:34 +00:00
|
|
|
int newerr = (fError ? 1 : 0);
|
|
|
|
|
//_currentErrorsCount += newerr;
|
|
|
|
|
_currentErrorsCount += newerr - errtmp[pos];
|
|
|
|
|
errtmp[pos] = newerr;
|
2010-02-23 15:15:08 +00:00
|
|
|
if (markErrors)
|
|
|
|
|
{
|
|
|
|
|
_errors[sector + iSector] |= fError;
|
|
|
|
|
_errorsCount += fError ? 1 : 0;
|
2008-12-06 05:44:14 +00:00
|
|
|
}
|
2008-11-26 20:20:41 +00:00
|
|
|
}
|
2010-02-23 15:15:08 +00:00
|
|
|
|
2008-11-26 20:20:41 +00:00
|
|
|
}
|
|
|
|
|
|
2010-02-28 21:04:34 +00:00
|
|
|
int[] errtmp = new int[MSECTORS];
|
|
|
|
|
|
2008-12-06 05:44:14 +00:00
|
|
|
//private unsafe int CorrectSectorsTest(int start, int end, int c2Score, byte[] realData, int worstScan)
|
|
|
|
|
//{
|
|
|
|
|
// int[] valueScore = new int[256];
|
|
|
|
|
// int[] scoreErrors = new int[256];
|
|
|
|
|
// int realErrors = 0;
|
|
|
|
|
// int bestScore = 0;
|
|
|
|
|
// int _errorsCaught = 0;
|
|
|
|
|
// int _falsePositives = 0;
|
|
|
|
|
// for (int iSector = 0; iSector < end - start; iSector++)
|
|
|
|
|
// {
|
|
|
|
|
// for (int iPar = 0; iPar < 4 * 588; iPar++)
|
|
|
|
|
// {
|
|
|
|
|
// int dataPos = iSector * CB_AUDIO + iPar;
|
2008-12-07 00:33:20 +00:00
|
|
|
// int c2Pos = iSector * CB_AUDIO + 2 + 4 * 588 + iPar / 8;
|
2008-12-06 05:44:14 +00:00
|
|
|
// int c2Bit = 0x80 >> (iPar % 8);
|
|
|
|
|
|
|
|
|
|
// Array.Clear(valueScore, 0, 256);
|
|
|
|
|
|
|
|
|
|
// byte bestValue = _currentScan.Data[dataPos];
|
|
|
|
|
// valueScore[bestValue] += 1 + (((_currentScan.Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
|
|
|
|
|
// int totalScore = valueScore[bestValue];
|
|
|
|
|
// for (int result = 0; result < _scanResults.Count; result++)
|
|
|
|
|
// {
|
|
|
|
|
// if (result == worstScan)
|
|
|
|
|
// continue;
|
|
|
|
|
// byte value = _scanResults[result].Data[dataPos];
|
|
|
|
|
// valueScore[value] += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
|
|
|
|
|
// totalScore += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
|
|
|
|
|
// if (valueScore[value] > valueScore[bestValue])
|
|
|
|
|
// bestValue = value;
|
|
|
|
|
// }
|
|
|
|
|
// if (valueScore[bestValue] < (1 + c2Score + totalScore) / 2)
|
|
|
|
|
// _currentErrorsCount++;
|
|
|
|
|
// //_currentData[iSector * 4 * 588 + iPar] = bestValue;
|
|
|
|
|
// if (realData[iSector * 4 * 588 + iPar] != bestValue)
|
|
|
|
|
// {
|
|
|
|
|
// if (valueScore[bestValue] > bestScore)
|
|
|
|
|
// scoreErrors[valueScore[bestValue]]++;
|
|
|
|
|
// realErrors++;
|
|
|
|
|
// if (valueScore[bestValue] * 2 <= c2Score + totalScore)
|
|
|
|
|
// _errorsCaught++;
|
|
|
|
|
// } else
|
|
|
|
|
// if (valueScore[bestValue] * 2 <= c2Score + totalScore)
|
|
|
|
|
// _falsePositives++;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// //string s = "";
|
|
|
|
|
// //for (int i = 0; i < 256; i++)
|
|
|
|
|
// // if (scoreErrors[i] > 0)
|
|
|
|
|
// // s += string.Format("[{0}]={1};", i, scoreErrors[i]);
|
|
|
|
|
// //System.Console.WriteLine("RE{0:000} EC{1} FP{2}", realErrors, _errorsCaught, _falsePositives);
|
|
|
|
|
// return realErrors;
|
|
|
|
|
//}
|
|
|
|
|
|
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;
|
|
|
|
|
|
2008-12-10 06:48:38 +00:00
|
|
|
if (_readCDCommand == ReadCDCommand.Unknown && !TestReadCommand())
|
|
|
|
|
throw new Exception("failed to autodetect read command: " + _autodetectResult);
|
2008-12-07 23:12:01 +00:00
|
|
|
|
2008-12-06 05:44:14 +00:00
|
|
|
_currentStart = MSECTORS * (iSector / MSECTORS);
|
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];
|
2009-01-17 04:09:38 +00:00
|
|
|
//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;
|
|
|
|
|
for (int i = 0; i < MSECTORS; i++)
|
|
|
|
|
errtmp[i] = 0;
|
|
|
|
|
|
|
|
|
|
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, pass == 0);
|
|
|
|
|
//TimeSpan delay1 = DateTime.Now - LastFetch;
|
|
|
|
|
if (pass == 0)
|
|
|
|
|
ProcessSubchannel(sector, Sectors2Read, true);
|
|
|
|
|
//DateTime LastFetched = DateTime.Now;
|
2010-02-28 21:04:34 +00:00
|
|
|
if (pass >= _correctionQuality)
|
2008-12-10 06:48:38 +00:00
|
|
|
{
|
2010-02-28 21:04:34 +00:00
|
|
|
CorrectSectors(pass, sector, Sectors2Read, pass == max_scans - 1);
|
2010-02-23 15:15:08 +00:00
|
|
|
PrintErrors(pass, sector, Sectors2Read, /*realData*/null);
|
2008-12-10 06:48:38 +00:00
|
|
|
}
|
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)
|
|
|
|
|
ReadProgress(this, new ReadProgressArgs(sector + Sectors2Read, pass, _currentStart, _currentEnd, _currentErrorsCount, PassTime));
|
2008-12-10 06:48:38 +00:00
|
|
|
}
|
2010-02-23 15:15:08 +00:00
|
|
|
//System.Console.WriteLine();
|
|
|
|
|
//if (CorrectSectorsTest(start, _currentEnd, 10, realData) == 0)
|
|
|
|
|
// break;
|
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)
|
2009-01-17 04:09:38 +00:00
|
|
|
{
|
|
|
|
|
if (_toc == null)
|
|
|
|
|
throw new Exception("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;
|
|
|
|
|
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;
|
|
|
|
|
return buff.Length;
|
2008-11-26 20:20:41 +00:00
|
|
|
}
|
2010-02-06 23:17:07 +00:00
|
|
|
PrefetchSector(_sampleOffset / 588);
|
|
|
|
|
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 = ¤tData.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
|
|
|
}
|
|
|
|
|
|
2010-02-06 23:17:07 +00:00
|
|
|
public long Length
|
2008-11-26 18:57:40 +00:00
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_toc == null)
|
|
|
|
|
throw new Exception("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 + ": ";
|
|
|
|
|
result += "[" + m_inqury_result.VendorIdentification + " " +
|
|
|
|
|
m_inqury_result.ProductIdentification + " " +
|
|
|
|
|
m_inqury_result.FirmwareVersion + "]";
|
|
|
|
|
return result;
|
2008-11-26 18:57:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-01 21:54:55 +00:00
|
|
|
public string ARName
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2008-12-07 23:12:01 +00:00
|
|
|
return m_inqury_result.VendorIdentification.TrimEnd(' ', '\0').PadRight(8, ' ') + " - " + m_inqury_result.ProductIdentification.TrimEnd(' ', '\0');
|
2008-12-01 21:54:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-04 21:30:56 +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
|
|
|
|
|
{
|
2008-12-10 06:48:38 +00:00
|
|
|
if (_toc.AudioLength <= 0)
|
|
|
|
|
throw new Exception("no audio");
|
2008-12-09 07:25:48 +00:00
|
|
|
_currentTrack = -1;
|
|
|
|
|
_currentIndex = -1;
|
2008-12-10 06:48:38 +00:00
|
|
|
_crcErrorsCount = 0;
|
2010-02-28 21:04:34 +00:00
|
|
|
_errorsCount = 0;
|
|
|
|
|
_currentStart = -1;
|
|
|
|
|
_currentEnd = -1;
|
2009-01-17 04:09:38 +00:00
|
|
|
_errors = new BitArray((int)_toc.AudioLength); // !!!
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
return "CUERipper v2.0.5 Copyright (C) 2008-10 Gregory S. Chudov";
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
2008-11-30 01:43:17 +00:00
|
|
|
|
2008-12-10 21:42:37 +00:00
|
|
|
private byte toBCD(int val)
|
|
|
|
|
{
|
2008-12-10 22:02:43 +00:00
|
|
|
return (byte)(((val / 10) << 4) + (val % 10));
|
2008-12-10 21:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
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.LookupSenseError(device.GetSenseAsc(), device.GetSenseAscq()) : st.ToString()))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-11-26 18:57:40 +00:00
|
|
|
}
|