vbAccelerator - Contents of code file: CDRip_AspiCD.cpp

This file is part of the download CDRip DLL Source, which is described in the article CD Ripping in VB Part 1.

/*
** Copyright (C) 1999 Albert L. Faber
**  
** 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.
*/

#include "StdAfx.h"
#include "AspiCD.h"
#include "AspiDebug.h"
#include "CDRip.h"
#include "CDRomSettings.h"
#include <math.h>
#include <assert.h>

// Toshiba Speed Control Parameters
struct TOSHIBA_MODE_PAGE_20 
{      
   BYTE      nPageCode;            // parsave & pagecode 
   BYTE      nPageLen;            // 0x01 = length of paramter block from this
    point 
   BYTE      bitSpeed:1;
   BYTE      bitRes   :7;
};




//#pragma optimize( "agptwy", on )
//#pragma optimize( "", off )


// CAspiCD constructor
CAspiCD::CAspiCD()
   :CDRomSettings()
{
   m_bAvailable = FALSE;

   // Set time out value to 10 seconds
   m_nTimeOut=10000;
   
   // Clear sense structure
   memset(m_SenseData,0x00,SENSE_LEN);

   // Clear last error value
   m_btLastError=SS_COMP;

   m_bUseNtScsi = ( TRANSPLAYER_NTSCSI == CR_GetTransportLayer() );

   // Load the ASPI dll;
   if ( InitAspiDll( m_bUseNtScsi ) == CDEX_OK ) 
   {
      m_bAvailable = TRUE;
   }
}


BYTE CAspiCD::GetDeviceType(BYTE btAdapterID,BYTE btTargetID,BYTE btLunID)
{
   // Create SRB_HAINQUIRY header
   SRB_GDEVBLOCK mySrb;

   // Clear all fields
   memset(&mySrb,0x00,sizeof(SRB_GDEVBLOCK));

   // Set SRB fields
   mySrb.SRB_Cmd=SC_GET_DEV_TYPE;
   mySrb.SRB_HaId=btAdapterID;
   mySrb.SRB_Target=btTargetID;
   mySrb.SRB_Lun=btLunID;

   // Send command
   SendASPI32Command((LPSRB)&mySrb);

   // Check aspi function call
   IsScsiError((LPSRB)&mySrb);

   m_btLastError=mySrb.SRB_Status;

   // Return device type
   return mySrb.SRB_DeviceType;
}

void CAspiCD::SetTimeOut(int nMilliSeconds)
{
   m_nTimeOut=nMilliSeconds;
}

void CAspiCD::GetDiskInfo()
{
   // Create SRB_GETDISKINFO header
   SRB_GETDISKINFO   mySrb;

   // Clear all fields
   memset(&mySrb,0x00,sizeof(SRB_GDEVBLOCK));

   // Set SRB fields
   mySrb.SRB_Cmd=SC_GET_DISK_INFO;

   mySrb.SRB_HaId=GetAdapterID();
   mySrb.SRB_Target=GetTargetID();
   mySrb.SRB_Lun=GetLunID();

   // Send command
   SendASPI32Command((LPSRB)&mySrb);

   m_btLastError=mySrb.SRB_Status;

   // Check aspi function call
   IsScsiError((LPSRB)&mySrb);
}

BYTE CAspiCD::ReadSubChannel(BYTE btDataFormat,BYTE* pbtBuffer,int
 nBufSize,BYTE btTrack)
{
   BYTE cmd [10];
   
   cmd[0]=      0x42;
   cmd[1]=      GetLunID()<<5;
   cmd[2]=      0;
   cmd[3]=      btDataFormat;
   cmd[4]=      0;
   cmd[5]=      0;
   cmd[6]=      btTrack;
   cmd[7]=      nBufSize>>8;
   cmd[8]=      nBufSize & 0xFF;
   cmd[9]=      0;

   switch (btDataFormat)
   {
      case 0x00:
      break;
      case 0x02:
      case 0x01:
//         ASSERT(nBufSize==0x10);
//         cmd[1]=0x02;
         cmd[2]=0x40;
      break;
      default:
         ASSERT(FALSE);
   }

   // Clear buffer
   memset(pbtBuffer,0x00,nBufSize);

   return IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,nBufSize);
}


BYTE CAspiCD::ReadSubChannelQ( BYTE      btDataFormat,
                        BYTE*   pbtBuffer,
                        int      nBufSize )
{
   BYTE cmd [12];
   
   cmd[0]=      0x42;
   cmd[1]=      GetLunID()<<5;
   cmd[2]=      0x40;
   cmd[3]=      btDataFormat;
   cmd[4]=      0;
   cmd[5]=      0;
   cmd[6]=      0;
   cmd[7]=      nBufSize>>8;
   cmd[8]=      nBufSize & 0xFF;
   cmd[9]=      0;
   cmd[10]=   0;
   cmd[11]=   0;

   memset(pbtBuffer,0x00,nBufSize);

   return IssueScsiCmd( SRB_DIR_IN, cmd, 10, pbtBuffer, nBufSize );
}


BYTE CAspiCD::IsAudioPlaying()
{
   // Create buffer
   BYTE pbtBuffer[48];

   // Read SubChannel information 0
   ReadSubChannel(0x01,pbtBuffer,sizeof(pbtBuffer));

   // Return result
   return pbtBuffer[1];
}


BYTE CAspiCD::CurrentPosition(DWORD& dwRelPos,DWORD& dwAbsPos)
{
   // Create buffer
   BYTE pbtBuffer[48];

   // Read SubChannel information 0
   BYTE btReturn=ReadSubChannel(0x01,pbtBuffer,sizeof(pbtBuffer));

   dwAbsPos=   ((DWORD)pbtBuffer[11]);
   dwAbsPos+=   ((DWORD)pbtBuffer[10])<<8;
   dwAbsPos+=   ((DWORD)pbtBuffer[ 9])<<16;
   dwAbsPos+=   ((DWORD)pbtBuffer[ 8])<<24;

   dwRelPos=   ((DWORD)pbtBuffer[15]);
   dwRelPos+=   ((DWORD)pbtBuffer[14])<<8;
   dwRelPos+=   ((DWORD)pbtBuffer[13])<<16;
   dwRelPos+=   ((DWORD)pbtBuffer[12])<<24;

   // Return result
   return btReturn;
}


BYTE CAspiCD::GetSubChannelTrackInfo(   int&   nReadIndex,
                              int&   nReadTrack,
                              DWORD&   dwReadPos )
{
   // Create buffer
   BYTE pbtBuffer[ 16 ];

   // Read SubChannel information 0
   BYTE btReturn=ReadSubChannelQ( 0x01, pbtBuffer, sizeof(pbtBuffer) );

   nReadTrack = pbtBuffer[ 6 ];
   nReadIndex = pbtBuffer[ 7 ];

   dwReadPos=   ( (DWORD)pbtBuffer[11] );
   dwReadPos+=   ( (DWORD)pbtBuffer[10] )<<8;
   dwReadPos+=   ( (DWORD)pbtBuffer[ 9] )<<16;
   dwReadPos+=   ( (DWORD)pbtBuffer[ 8] )<<24;

   return btReturn;
}


BYTE CAspiCD::Seek(DWORD dwAbsPos)
{
   static BYTE cmd[10] = {0x2B,GetLunID()<<5, 0,0, 0, 0,0,0,0,0};

   cmd[2] =(BYTE)((dwAbsPos>>24) & 0xff);
   cmd[3] =(BYTE)((dwAbsPos>>16) & 0xff);
   cmd[4] =(BYTE)((dwAbsPos>>8 ) & 0xff);
   cmd[5] =(BYTE)(dwAbsPos&0xff);

   return IssueScsiCmd(SRB_DIR_OUT,cmd,sizeof(cmd));
}


void CAspiCD::GetCDRomDevices()
{
   if (GetStatus())
   {
      DWORD dwDevType;
      BYTE btAdapterID=0;
      BYTE btLunID=0;
      BYTE btTargetID=0;

      for ( btAdapterID=0; btAdapterID < m_bNumHA; btAdapterID++ )
      {
         for ( btTargetID = 0; btTargetID < 12; btTargetID++ )
         {
            for ( btLunID = 0; btLunID < 8; btLunID++ )
            {
               // Get device type
               dwDevType=GetDeviceType(btAdapterID,btTargetID,btLunID);
#ifdef DEBUG_VERBOSE
               char lpszTmp[255];
               sprintf(lpszTmp,"Status of %d %d %d
                %d\n",GetTargetID(),GetTargetID(),GetLunID(),dwDevType);
               DebugPrintf(lpszTmp);
#endif

               if (dwDevType==DTC_CDROM || dwDevType==DTC_WORM)
               {
#ifdef DEBUG_VERBOSE
                  strTmp.Format("Found CDROM at %d %d %d
                   %d\n",GetTargetID(),GetTargetID(),GetLunID(),dwDevType);
                  DebugPrintf(strTmp);
#endif
                  InquiryCommand(btAdapterID,btTargetID,btLunID);
               }
            }
         }
      }
   }

#ifndef CDEX_DLL
   // Now all CDRoms have been detected, load the proper settings for the CD-ROM
   m_CDRomSettings.Load();
#endif
}



BOOL CAspiCD::IsMMC(LPSTR lpszInfo)
{ 
   BYTE pbtBuffer[0xFF];
   
   // Clear buffer
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   ModeSense(0x2A,pbtBuffer,sizeof(pbtBuffer));

   SCISMODEHEADER* pBlockHeader=(SCISMODEHEADER*)pbtBuffer;
//   SCSICDMODEPAGE2A*
 pPage=(SCSICDMODEPAGE2A*)(pbtBuffer+sizeof(SCISMODEHEADER)+pBlockHeader->nBlock
Len);
   SCSICDMODEPAGE2A* pPage=(SCSICDMODEPAGE2A*)(&pbtBuffer[4]);

   int n2aSize=sizeof(SCSICDMODEPAGE2A);
   int nBlockSize=sizeof(SCISMODEHEADER);

   int nMaxReadSpeed=((int)pPage->ReadSpeedL+(int)pPage->ReadSpeedL<<8);

    if((pbtBuffer[4]&0x3F)==0x2A)
   {
      // Set mmc variable or so
      // MMC style drive!
      if(pbtBuffer[5]>=4)
      {
         if(pbtBuffer[9]&0x1)
         {
            strcpy(lpszInfo,"Drive is MMC style");
            return TRUE;
         }
      }
      else
      {
         strcpy(lpszInfo,"MMC drive, but reports CDDA incapable");
         return TRUE;
      }
    }
   strcpy(lpszInfo,"Drive does not support MMC");
   return FALSE;
}



void CAspiCD::InquiryCommand(BYTE btAdapterID,BYTE btTargetID,BYTE btLunID)
{
   char lpszTmp[26];

   static BYTE cmd[6] = {0x12,btLunID<<5, 0,0, 0x24, 0};

   static BYTE pbtBuffer[ 0x24];

   // Clear buffer
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,sizeof(pbtBuffer),btAdapter
   ID,btTargetID,btLunID);

   // Clear string
   memset(lpszTmp,0x00,sizeof(lpszTmp));

   // Create a string with CD-ROM Vendor and type information
   strncpy(lpszTmp,(LPSTR)&pbtBuffer[8],24);

   // Add entry to array of CD-ROM Settings
   AddCDRom( lpszTmp, btAdapterID, btTargetID, btLunID );
}

void CAspiCD::GetDeviceName(BYTE btAdapterID,BYTE btTargetID,BYTE btLunID,LPSTR
 lpszDviceName)
{

   static BYTE cmd[6] = {0x12,btLunID<<5, 0,0, 0x24, 0};

   static BYTE pbtBuffer[ 0x24];

   // Clear buffer
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,sizeof(pbtBuffer),btAdapter
   ID,btTargetID,btLunID);

   // Create a string with CD-ROM Vendor and type information
   strcpy(lpszDviceName,(LPSTR)&pbtBuffer[8]);
}


BYTE CAspiCD::ModeSense(int nPage,BYTE* pbtBuffer,int nBufSize)
{
   BYTE   btReturn;

   // Clear result buffer
   memset(pbtBuffer,0x00,nBufSize);

   BYTE cmd[12];

   // Init buffers
   memset(cmd,0x00,sizeof(cmd));

   // Common settings
   cmd[1]=GetLunID()<<5;            // Lun
   cmd[2]=nPage&0x3F;            // Get page values (current value)

   // ATAPI or SCSI sense ?
   if (GetDriveTable().bAtapi==TRUE)
   {
      cmd[0]= 0x5A;               // Operation Code
      cmd[7]=   nBufSize>>8;
      cmd[8]=   nBufSize&0xFF;

      // Call SCSI command
      btReturn=IssueScsiCmd(SRB_DIR_IN,cmd,10,pbtBuffer,nBufSize);
//            
 IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,sizeof(pbtBuffer));

      int nLen=pbtBuffer[1];
      nLen+=((int)pbtBuffer[0])<<8;

      // Convert header to standard SCSI header
      pbtBuffer[0]=(BYTE)(nLen-4);
      pbtBuffer[1]=pbtBuffer[2];
      pbtBuffer[2]=pbtBuffer[3];
      pbtBuffer[3]=pbtBuffer[7];
      memmove(pbtBuffer+4,pbtBuffer+8,nBufSize-4);

   }
   else
   {
      ASSERT(nBufSize<256);

      cmd[0]=0x1A;               // Operation Code
      cmd[8]=   nBufSize;            // Set low word of buffer size

      // Call SCSI command
      btReturn=IssueScsiCmd(SRB_DIR_IN,cmd,6,pbtBuffer,nBufSize);
   }
   return btReturn;
}


BYTE CAspiCD::ModeSelect(int nPageSize,BYTE* pbtBuffer,int nBufSize,BOOL
 bPageFormat)
{
   BYTE   btReturn;
   BYTE   cmd[10];

   // Init cmd memory
   memset(cmd,0x00,sizeof(cmd));

   if (GetAtapi()==TRUE)
   {
      int nBlkSize=8;               // = size of ATAPI block

      cmd[0]=0x55;               // Operation Code
      cmd[1]=GetLunID()<<5;            // Lun 
      
      if (bPageFormat)
         cmd[1]|=0x10;            // Have to select Page Format for vendor
          specific pages

      cmd[8]=nPageSize+nBlkSize;      // sizeof mode

      // Shift the stuff 8 bytes for ATAPI block header
      memmove(&pbtBuffer[nBlkSize],&pbtBuffer[0],nBufSize-nBlkSize);

      // Init first eight bytes
      memset(pbtBuffer,0x00,nBlkSize);

      // Setup ATAPI block descriptor
      pbtBuffer[nBlkSize-1]=8;         // Block Descriptor Length

      // Call SCSI command (10 bytes command)
      btReturn=IssueScsiCmd(SRB_DIR_OUT,cmd,10,pbtBuffer,nPageSize+nBlkSize);
   }
   else
   {
      int nBlkSize=4;                  // = size of SCSI block
      cmd[0]=0x15;                  // Operation Code
      cmd[1]=GetLunID()<<5;            // Lun 
      
      if (bPageFormat)
         cmd[1]|=0x10;            // Have to select Page Format for vendor
          specific pages

      cmd[4]=nPageSize+nBlkSize;         // sizeof mode 

      // Shift the stuff 4 bytes for SCSI block header
      memmove(&pbtBuffer[nBlkSize],&pbtBuffer[0],nBufSize-nBlkSize);

      // Init first four bytes
      memset(pbtBuffer,0x00,nBlkSize);

      // Setup SCSI block descriptor
      pbtBuffer[nBlkSize-1]=8;         // Block Descriptor Length

      // Call SCSI command (6 BYTES command)
      btReturn=IssueScsiCmd(SRB_DIR_OUT,cmd,6,pbtBuffer,nPageSize+nBlkSize);
   }
   return btReturn;
}



// return sector size and sets original density
DWORD CAspiCD::GetSectorSize(BYTE& btDensity)
{
   BYTE pbtBuffer[0xFF];

   DWORD dwSectorSize=CB_CDROMSECTOR;

   // Set default density, is this correct???
//   btDensity=0x81;
   btDensity=0x00;

   // Sense Page 0x01 (error recovery) to get block decriptor
   ModeSense(0x01,pbtBuffer,sizeof(pbtBuffer));
   
   // FIXME: some drives dont deliver block descriptors !!!
   // In that case, use the default values (i.e. secsize=2048, density=0x81)
   SCSIMODEHDR_6* pScsiHeader=(SCSIMODEHDR_6*)pbtBuffer;

   if (pScsiHeader->btBlkDescrLen>0)
   {
      // Get original density???
      SCSIBLOCKDESCRIPTOR* pBlockDescriptor=(SCSIBLOCKDESCRIPTOR*)&pbtBuffer[4];

      btDensity=pBlockDescriptor->btDensity;

      dwSectorSize=pBlockDescriptor->btBlockLenL;
      dwSectorSize+=(DWORD)(pBlockDescriptor->btBlockLenM)<<8;
   }

    return dwSectorSize;
}



void CAspiCD::SetSectorSize(DWORD dwSecSize,BYTE btDensity)
{
   // 8 additional bytes for the speed selection mode pages
   BYTE pbtBuffer[16+8];

   TOSHIBA_MODE_PAGE_20 ToshibaSpeedPage={0x20,0x01,0,8};

   BYTE PhilipsSpeedPage[8]={0x23,0x06,1,1,1,0,0,0};


   // Which speed is selected
   if ( GetSpeed() > 1 )
   {
      ToshibaSpeedPage.bitSpeed=1;

      // Philips speed can be set max to 4 for CD 2660
      PhilipsSpeedPage[4]=PhilipsSpeedPage[2]=min(GetSpeed(),4);
   }

   // Init buffers
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   // Just a sanity check, this should never occur anyways
   if (dwSecSize==2048)
   {
      btDensity=0x81;
   }

   // Create SCSI block desciptor
   pbtBuffer[0]=btDensity;               // Density Code
   pbtBuffer[6]=(BYTE)(dwSecSize >> 8);   // block length "msb"
   pbtBuffer[7]=(BYTE)(dwSecSize & 0xFF);   // block length lsb

   if (GetSpeedMethod()==SPEEDTOSHIBA && dwSecSize!=2048 && GetSpeed()>0 )
   {
      // Add the mode page bits
      memcpy(pbtBuffer+8,&ToshibaSpeedPage,sizeof(ToshibaSpeedPage));

      // Call mode select, with Toshiba speed selection code
      ModeSelect(8+sizeof(ToshibaSpeedPage),pbtBuffer,sizeof(pbtBuffer),TRUE);
   }
   else
   if (GetSpeedMethod()==SPEEDPHILIPS && dwSecSize!=2048 && GetSpeed()>0 )
   {
      // Add the mode page bits
      memcpy(pbtBuffer+8,&PhilipsSpeedPage,sizeof(PhilipsSpeedPage));

      // Call mode select, with Toshiba speed selection code
      ModeSelect(8+sizeof(PhilipsSpeedPage),pbtBuffer,sizeof(pbtBuffer),TRUE);
   }
   else
   {
      // Call mode select, just to set the block desciptor only
      ModeSelect(8,pbtBuffer,16,FALSE);
   }

}


// switch Toshiba/DEC and HP drives from/to cdda density
// reserved, Medium type=0, Dev spec Parm = 0, block descriptor len 0 oder 8,
 Density (cd format) 
// (0=YellowBook, XA Mode 2=81h, XA Mode1=83h and raw audio via SCSI=82h),
// # blks msb, #blks, #blks lsb, reserved, blocksize, blocklen msb, blocklen
 lsb,
// MODE_SELECT, page = SCSI-2  save page disabled, reserved, reserved, parm
 list len, flags

void CAspiCD::EnableCdda(BOOL bAudioMode)
{
   static DWORD   dwSecSize=2048;         // Static variable !!
   static BYTE      btDensity=0;         // Static variable !!


   // Check if we have to do this anyways
   if (GetEnableMode()==ENABLENONE)
      return;

   // Get the original sector size (and denisity code), if we don't have it yet 
   if (dwSecSize==0)
   {
      dwSecSize=2048;
//      dwSecSize=GetSectorSize(btDensity);
   }

   // Switch to audio mode??
   if (bAudioMode)
   {
      // Switch to Audio mode
      SetSectorSize(CB_CDDASECTOR,GetDensity());
   }
   else
   {
      // If dwSector is set, return old values
      if (dwSecSize!=0)
      {
         // Switch to Audio mode
         SetSectorSize(dwSecSize,btDensity);

         // Clear values
         dwSecSize=0;
         btDensity=0;
      }
   }
}


void CAspiCD::SetCDSpeed( int nSpeed )
{
   BYTE btH=0;
   BYTE btL=0;

   if (nSpeed==-1)
   {
      nSpeed= GetSpeed();
   }

   btH=(nSpeed*0xB0)>>8;         // Get speed High byte
   btL=(nSpeed*0xB0)&0xFF;      // Get speed Low Byte

   static BYTE pbtBuffer[16];

   // Init buffers
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   int nCmdSize=10;

   BYTE cmd [12] = {0x00,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

   switch (GetSpeedMethod())
   {
      case SPEEDMMC:   
         cmd [0]=0xBB; 
         nCmdSize=12;
         // Only set this when the user has requested it!
         if (nSpeed>0)
         {
            // Setup CMD structure
            cmd[0] = 0xBB;            // Operation Code
            cmd[1] = GetLunID()<<5;         // Lun
            cmd[2] = btH;            // High word of speed
            cmd[3] = btL;            // Low word of speed
            cmd[4] = btH;            // High word of speed
            cmd[5] = btL;            // Low word of speed
            // Send to ASPI
            IssueScsiCmd(SRB_DIR_OUT,cmd,nCmdSize,NULL,0);
         }
      break;

//      case READ10:   cmd [0]=0x28; nCmdSize=10;break;   // Read Std
//      case SPEEDNEC:   cmd [0]=0xD4; nCmdSize=10;break;   // Read 10
      case SPEEDSONY:   
         if (nSpeed>0)
         {
            nCmdSize=6;
            cmd[0]=0x15;            // MODE_SELECT
            cmd[1]=0x10;            // no save page
            cmd[2]=0;               // reserved
            cmd[3]=0;               // reserved
            cmd[4]=4 + 4;            // sizeof(mode)
            cmd[5]=0;               // reserved

            pbtBuffer[0]=0;
            pbtBuffer[1]=0;
            pbtBuffer[2]=0;
            pbtBuffer[3]=0;
            pbtBuffer[4]=0x31;
            pbtBuffer[5]=2;
            pbtBuffer[6]=0;            // speed
            pbtBuffer[7]=0;            // reserved

            // speed values > 1 are drive dependent
            if (nSpeed > 4)
               nSpeed = 8;

            pbtBuffer[6] = (nSpeed / 2);

            // Send to ASPI
            IssueScsiCmd(SRB_DIR_OUT,cmd,nCmdSize,pbtBuffer,8);
         }
      default:
         return;
   }
}



BOOL CAspiCD::IsUnitReady()
{
//   DebugPrintf("Start of IsUnitReady\n");

   static BYTE cmd[6] = {0x00, 0, 0,0, 0, 0};

   BOOL bReturn=(BOOL)IssueScsiCmd(0,cmd,sizeof(cmd));
//   DebugPrintf("IsUnitReady reports, btResult=%d \n",bReturn);
   return bReturn;
}

BOOL CAspiCD::RequestSense()
{
   static BYTE pbtBuffer[100];

   // Init buffers
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   DebugPrintf("Start of IsUnitReady\n");

   static BYTE cmd[6] = {0x03, 0, 0,0, (BYTE)sizeof(pbtBuffer),0};

   BOOL
    bReturn=(BOOL)IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,sizeof(pbtBu
   ffer));

   DebugPrintf("Results of Request Sense SK=%x ASC=%x: ASCQ=%d
    \n",pbtBuffer[2]&0xf,pbtBuffer[12],pbtBuffer[13]);

   DebugPrintf("IsUnitReady reports, btResult=%d \n",bReturn);
   return bReturn;
}


#ifdef NEWSTUFF
BOOL CAspiCD::IsMediaLoaded()
{
   DebugPrintf("Start of IsMediaLoaded\n");

   static BYTE senseBuffer[SENSE_LEN];

/*
   static BYTE cmd[6] = {0x00, 0, 0,0, 0, 0};

   BYTE
    btResult=IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),NULL,0,senseBuffer,sizeof(s
   enseBuffer));

   if (btResult==4)
      return FALSE;

   return (senseBuffer[12]!=0x3a || senseBuffer[13]!=0x00);
*/
   static BYTE cmd[6] = {0x00, 0, 0,0, 0, 0};

   BYTE
    btResult=IssueScsiCmd(0x00,cmd,sizeof(cmd),NULL,0,senseBuffer,sizeof(senseBu
   ffer));

   if (btResult==4)
   {
      DebugPrintf("IsMediaLoaded returns FALSE, btResult=%d \n",btResult);
      return FALSE;
   }
   DebugPrintf("IsUnitReady Reports OKAY, btResult=%d \n",btResult);

//   BOOL bReturn=(senseBuffer[12]!=0x3a || senseBuffer[13]!=0x00);

//   DebugPrintf("Media Loaded indicates %d \n",bReturn);
//   return bReturn;
   return TRUE;
}
#endif


BOOL CAspiCD::Scan(DWORD dwSector,BOOL bForeWard)
{
   int nCmdSize=0;

   static BYTE cmd[12];

   // clear cmd buffer
   memset(cmd,0x00,sizeof(cmd));

   nCmdSize=10;
   
   cmd[0]= 0xBA;
   cmd[1]= GetLunID()<<5;
   cmd[1]+= (bForeWard)?0x00:0x10;

   cmd[2]= (BYTE)(dwSector >> 24);
   cmd[3]= (BYTE)((dwSector >> 16) & 0xFF);
   cmd[4]= (BYTE)((dwSector >> 8) & 0xFF);
   cmd[5]= (BYTE)(dwSector & 0xFF);

   return (BOOL)IssueScsiCmd(SRB_DIR_OUT,cmd,nCmdSize);
}

BOOL CAspiCD::PlayTrack(DWORD dwStartSector,DWORD dwEndSector)
{
   int nCmdSize=0;

   static BYTE cmd[12];

   // clear cmd buffer
   memset(cmd,0x00,sizeof(cmd));

   DWORD dwSector=dwStartSector;

   // There might be quite a few sectors
   DWORD dwNumSectors=dwEndSector-dwStartSector;

/*
if (g_config.GetPlayMethod()==10)
{
   nCmdSize=10;
   
   cmd[0]= 0x45;
   cmd[1]= GetLunID()<<5;
   cmd[2]= (BYTE)(dwSector >> 24);
   cmd[3]= (BYTE)((dwSector >> 16) & 0xFF);
   cmd[4]= (BYTE)((dwSector >> 8) & 0xFF);
   cmd[5]= (BYTE)(dwSector & 0xFF);

   // RESERVED
   cmd[6]= (BYTE)0x00;

   // Set number of sector to read
   cmd[7]= (BYTE)((dwNumSectors >>8) & 0xFF);
   cmd[8]= (BYTE) (dwNumSectors & 0xFF);
}



if (g_config.GetPlayMethod()==12)
{
*/   nCmdSize=12;
   cmd[0]= 0xA5;
   cmd[1]= GetLunID()<<5;

   // Start sector
   cmd[2]= (BYTE)(dwSector >> 24);
   cmd[3]= (BYTE)((dwSector >> 16) & 0xFF);
   cmd[4]= (BYTE)((dwSector >> 8) & 0xFF);
   cmd[5]= (BYTE)(dwSector & 0xFF);

   // Set Track Length
   cmd[6]= (BYTE)((dwNumSectors >>24) & 0xFF);
   cmd[7]= (BYTE)((dwNumSectors >>16) & 0xFF);
   cmd[8]= (BYTE)((dwNumSectors >>8) & 0xFF);
   cmd[9]= (BYTE) (dwNumSectors & 0xFF);

/*}

// USE MSF frames
if (g_config.GetPlayMethod()==6)
{
   nCmdSize=10;
   cmd[0]= 0x47;
   cmd[1]= GetLunID()<<5;

   // Start sector
   cmd[3]= (BYTE)((dwSector / 75) /60);
   cmd[4]= (BYTE)((dwSector / 75) %60);
   cmd[5]= (BYTE)((dwSector %75 )    );

   cmd[6]= (BYTE)((dwSector+dwNumSectors / 75) /60);
   cmd[7]= (BYTE)((dwSector+dwNumSectors / 75) %60);
   cmd[8]= (BYTE)((dwSector+dwNumSectors %75 )    );
}
*/
   return (BOOL)IssueScsiCmd(SRB_DIR_OUT,cmd,nCmdSize);

}


BOOL CAspiCD::StopPlayTrack()
{
   // Set up SCSI command buffer
   static BYTE cmd[6] = {0x1B,GetLunID()<<5|01,0,0,0,0};
   // Do command
   return (BOOL)IssueScsiCmd(SRB_DIR_OUT,cmd,sizeof(cmd));
}


short SWAPSHORT(short sSwap)
{
   return ((sSwap>>8)&0xFF) + ((sSwap&0xFF) <<8);
}

#ifndef CDEX_DLL
BOOL CAspiCD::TestDrive(DRIVETABLE Test,ENDIAN& Endian,CADebug& dbgFile)
{
   DebugPrintf("Start Of Test Drive \n");

   BYTE* pbtReadBuffer=NULL;

   BOOL bRet=FALSE;
   const int nTestSectors=10;

   // Keep old settings
   DRIVETABLE oldSettings=m_DriveTable;

   m_DriveTable=Test;

   // Okay, init drive
   EnableCdda(TRUE);

   // Log Sense Info to dbgFile
   dbgFile.printf("EnableCDDA(%02X/%02X/%02X)\t",g_SenseKey.SK,g_SenseKey.ASC,g_
   SenseKey.ASCQ);

   // Allocate memory for the two read buffers
   pbtReadBuffer=new BYTE[nTestSectors * CB_CDDASECTOR];

   // Set endian always to Little
   m_DriveTable.Endian=LITTLEENDIAN;   

//   OutputDebugString("Read CD Sector \n");
   // Try to read sector 1500-1600
   ReadCdRomSector( pbtReadBuffer, 1500, nTestSectors, FALSE );

   dbgFile.printf("ReadCDRom(%02X/%02X/%02X)\t",g_SenseKey.SK,g_SenseKey.ASC,g_S
   enseKey.ASCQ);

   // Analyze data
   for (int i=0;i<nTestSectors*CB_CDDASECTOR;i++)
   {
      if (pbtReadBuffer[i]>0)
      {
         bRet=TRUE;
         break;
      }
   }

   int iBigEndian=0;
   int iLitleEndian=0;


   // Two bytes for each sample
   short *psBuffer=(short*)pbtReadBuffer;

   // Init the stuff to calculate delta samples
   short sPrevLitle=psBuffer[0];
   short sPrevBig=SWAPSHORT(sPrevLitle);

   // Test Endian
   for (i=0;i<nTestSectors*CB_CDDASECTOR/2;i++)
   {
      short sLitle=psBuffer[i];
      short sBig=SWAPSHORT(sLitle);

      if ( abs((int)sLitle-(int)sPrevLitle)> abs((int)sBig-(int)sPrevBig))
      {
         iBigEndian++;
      }
      else
      {
         iLitleEndian++;
      }
   }

   if (iBigEndian>iLitleEndian)
   {
      Endian=BIGENDIAN;
   }
   else
   {
      Endian=LITTLEENDIAN;
   }


   // Clean up memory
   delete [] pbtReadBuffer;   
   pbtReadBuffer=NULL;

//   OutputDebugString("Disable CD Drive \n");
   // Stop recording
   EnableCdda(FALSE);

   m_DriveTable=oldSettings;

   DebugPrintf("EndOf Test Drive \n");
   return bRet;
}
#endif

BOOL CAspiCD::EjectCD(BOOL bEject)
{
   BYTE cmd[6] = {0x1B, 0, 0, 0, 0, 0};
   
   LONG lSector = 0;

   WORD wNumSectors= 0;

   cmd [1]=GetLunID()<<5;

   if (bEject)
      cmd [4]=0x02;
   else
      cmd [4]=0x03;

   return (BOOL)IssueScsiCmd(SRB_DIR_OUT,cmd,sizeof(cmd));
}


BOOL CAspiCD::PauseCD(BOOL bPause)
{
   BYTE cmd[10] = {0x4B, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   
   cmd [1]=GetLunID()<<5;

   if (!bPause)
      cmd [8]=0x01;
   else
      cmd [8]=0x00;

   return (BOOL)IssueScsiCmd(SRB_DIR_OUT,cmd,sizeof(cmd));
}


WORD SWAPWORD(WORD nSwap)
{
   return ((nSwap>>8)&0xFF) || ((nSwap&0xFF)<<8);
}


BYTE CAspiCD::IssueScsiCmd(BYTE bFlags,LPBYTE lpcbData,int ncbLen)
{
   return
    IssueScsiCmd(bFlags,lpcbData,ncbLen,NULL,0,GetAdapterID(),GetTargetID(),GetL
   unID());
}

BYTE CAspiCD::IssueScsiCmd(BYTE bFlags,LPBYTE lpcbData,int ncbLen,LPBYTE
 lpBuffer,int nBufLen)
{
   return
    IssueScsiCmd(bFlags,lpcbData,ncbLen,lpBuffer,nBufLen,GetAdapterID(),GetTarge
   tID(),GetLunID());
}


BOOL CAspiCD::ScsiAbort(SRB_EXECSCSICMD* sp,BYTE btAdapterID)
{
   DWORD         dwStatus = 0;
   SRB_Abort      s;

   // Clear SRB_Abort structure
   memset(&s,0x00,sizeof(s));

   // Set structure variables
   s.SRB_Cmd      = SC_ABORT_SRB;         // ASPI command code = SC_ABORT_SRB
   s.SRB_HaID      = btAdapterID;         // ASPI host adapter number
   s.SRB_Flags      = 0;               // Flags
   s.SRB_ToAbort   = (LPSRB)&sp;         // sp

   // Initiate SCSI abort
   dwStatus = SendASPI32Command((LPSRB)&s);

   m_btLastError=s.SRB_Status;

   // Check condition
   if (s.SRB_Status != SS_COMP)
   {
//      printf("Abort ERROR! 0x%08X\n", s.SRB_Status);

      // Indicate that error has occured
      return (FALSE);
   }

   // Everything went OK
   return (TRUE);
}

/*
BOOL CAspiCD::RezeroUnit(SRB_EXECSCSICMD* sp,BYTE btAdapterID)
{
   DWORD         dwStatus = 0;
   SRB_Abort      s;

   // Clear SRB_Abort structure
   memset(&s,0x00,sizeof(s));

   // Set structure variables
   s.SRB_Cmd      = SC_ABORT_SRB;         // ASPI command code = SC_ABORT_SRB
   s.SRB_HaID      = btAdapterID;         // ASPI host adapter number
   s.SRB_Flags      = 0;               // Flags
   s.SRB_ToAbort   = (LPSRB)&sp;         // sp

   // Initiate SCSI abort
   dwStatus = SendASPI32Command((LPSRB)&s);

   m_btLastError=s.SRB_Status;

   // Check condition
   if (s.SRB_Status != SS_COMP)
   {
//      printf("Abort ERROR! 0x%08X\n", s.SRB_Status);

      // Indicate that error has occured
      return (FALSE);
   }

   // Everything went OK
   return (TRUE);
}
*/

void  CAspiCD::SetScsiTimeOut(int nTimeOut)
{
/*
   static SRB_GetSetTimeouts srbTimeOut;
   memset(&srbTimeOut,0x00,sizeof(srbTimeOut));
   srbTimeOut.SRB_Cmd=SC_GETSET_TIMEOUTS;
   srbTimeOut.SRB_HaId=GetAdapterID();;
   srbTimeOut.SRB_Flags=SRB_DIR_OUT;
   srbTimeOut.SRB_Lun=GetLunID();
   srbTimeOut.SRB_Timeout=nTimeOut*2;
   srbTimeOut.SRB_Target=GetTargetID();

   // Send ASPI32 command
   SendASPI32Command((LPSRB)&srbTimeOut);

   switch (srbTimeOut.SRB_Status)
   {
      case SS_COMP:
      break;
      case SS_INVALID_HA:
      case SS_NO_DEVICE:
      case SS_INVALID_SRB:
         assert(FALSE);
      break;
   }
*/
}

int CAspiCD::GetScsiTimeOut()
{
/*
   static SRB_GetSetTimeouts srbTimeOut;
   memset(&srbTimeOut,0x00,sizeof(srbTimeOut));
   srbTimeOut.SRB_Cmd=SC_GETSET_TIMEOUTS;
   srbTimeOut.SRB_HaId=GetAdapterID();;
   srbTimeOut.SRB_Flags=SRB_DIR_IN;
   srbTimeOut.SRB_Lun=GetLunID();
   srbTimeOut.SRB_Target=GetTargetID();
   
   // Send ASPI32 command
   DWORD dwASPIStatus=SendASPI32Command((LPSRB)&srbTimeOut);

   switch (dwASPIStatus)
   {
      case SS_COMP:
         return srbTimeOut.SRB_Timeout/2;
      break;

      case SS_INVALID_HA:
      case SS_NO_DEVICE:
      case SS_INVALID_SRB:
         assert(FALSE);
      break;
   }
*/
   return 10*2;

}



BYTE CAspiCD::IssueScsiCmd(BYTE bFlags,LPBYTE lpcbData,int ncbLen,LPBYTE
 lpBuffer,int nBufLen,BYTE btAdapterID,BYTE btTargetID,BYTE btLunID)
{
   
// added by Andi, scenalyzer@blackbox.net
   SRB_EXECSCSICMD   mySrb;               // Create SRB_EXECSCSICMD header
   HANDLE         hEvent=NULL;         // Handle to event

// end added

    DWORD         dwASPIEventStatus=0;
    DWORD         dwASPIStatus;

   // Clear all fields
   memset(&mySrb,0x00,sizeof(SRB_EXECSCSICMD));

    // **************************************************
    // Create event for MySRB.  Initial state         
    // non-signaled, manual reset.                    
    // **************************************************

   // Make sure it is a manual reset event!
    if ((hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL)
          return SS_ABORTED;

   // Set SRB fields
   mySrb.SRB_Cmd=SC_EXEC_SCSI_CMD;
   mySrb.SRB_HaId=btAdapterID;
   mySrb.SRB_Flags=bFlags|SRB_EVENT_NOTIFY;
   mySrb.SRB_Target=btTargetID;
   mySrb.SRB_Lun=btLunID;
    mySrb.SRB_SenseLen=SENSE_LEN;
   mySrb.SRB_CDBLen=ncbLen;
    mySrb.SRB_BufLen=nBufLen;
    mySrb.SRB_BufPointer=lpBuffer;
    mySrb.SRB_PostProc= (POSTPROCFUNC)hEvent;

   ASSERT( ncbLen<=sizeof(mySrb.CDBByte));


   LPBYTE lpTest=(LPBYTE)&mySrb;

   // Copy CBDByte data, if available
   if (lpcbData!=NULL)
   {
      memcpy(&mySrb.CDBByte,lpcbData,ncbLen);
   }

   // Clear event handle
   ResetEvent(hEvent);

   if ( ncbLen == 6 )
   {
      DebugPrintf("Sending ASPI command flag %02X cmd %02X %02X %02X %02X %02X
       %02X",
            mySrb.SRB_Flags,
            mySrb.CDBByte[0],
            mySrb.CDBByte[1],
            mySrb.CDBByte[2],
            mySrb.CDBByte[3],
            mySrb.CDBByte[4],
            mySrb.CDBByte[5] );
   }
   else
   {
      DebugPrintf("Sending ASPI command flag %02X cmd %02X %02X %02X %02X %02X
       %02X %02X %02X %02X %02X",
            mySrb.SRB_Flags,
            mySrb.CDBByte[0],
            mySrb.CDBByte[1],
            mySrb.CDBByte[2],
            mySrb.CDBByte[3],
            mySrb.CDBByte[4],
            mySrb.CDBByte[5],
            mySrb.CDBByte[6],
            mySrb.CDBByte[7],
            mySrb.CDBByte[8],
            mySrb.CDBByte[9]
            );
   }


   // Send ASPI32 command
   dwASPIStatus=SendASPI32Command((LPSRB)&mySrb);

    // **************************************************
    // Block on event till signaled                     *
    // **************************************************
    if ( dwASPIStatus == SS_PENDING )
   {
      // Wait till time-out
      dwASPIEventStatus = WaitForSingleObject(hEvent,m_nTimeOut);


      // Check if time-out did occur
      if (dwASPIEventStatus==WAIT_TIMEOUT )
       {
         // Clear event, time out
         ResetEvent(hEvent);
      }

      if (mySrb.SRB_Status == SS_PENDING)
      {
         //ASSERT(FALSE);

         // Time out condition
         DebugPrintf("Time Out in issuescsicmd\n");

         // Abort SCSI command
         ScsiAbort(&mySrb,btAdapterID);

         // Close the event handle
         CloseHandle(hEvent);

         // Indicate that command has been aborted
         m_btLastError=mySrb.SRB_Status;
         return SS_ABORTED;
      }

   }

   g_SenseKey.SK   =mySrb.SenseArea[2]&0xf;
   g_SenseKey.ASC   =mySrb.SenseArea[12];
   g_SenseKey.ASCQ   =mySrb.SenseArea[13];
   m_btLastError=mySrb.SRB_Status;

   /*
    * Check ASPI command status
    */
   if (mySrb.SRB_Status != SS_COMP)
   {
      //ASSERT(FALSE);
      DebugPrintf("Error in IssueSCSICommand with Status %02X Sense flags %02X
       %02X %02X ",
               mySrb.SRB_Status,
               g_SenseKey.SK,
               g_SenseKey.ASC,
               g_SenseKey.ASCQ );

      // Clear event, time out
      //ResetEvent(hEvent);
   }

   // Close the event handle
   CloseHandle(hEvent);

   // Check aspi function call
   //dwAspiStatus=IsScsiError((LPSRB)&mySrb);


   DebugPrintf("ASPI command completed with Status %02X Sense flags %02X %02X
    %02X ",
            mySrb.SRB_Status,
            g_SenseKey.SK,
            g_SenseKey.ASC,
            g_SenseKey.ASCQ );


   return mySrb.SRB_Status;
}


/*
void CAspiCD::AllocASPIBuffer(ASPI32BUFF* pASPIBuffer)
{
   memset( pASPIBuffer, 0, sizeof(ASPI32BUFF) );
   pASPIBuffer->AB_BufLen = 1024*512;
   pASPIBuffer->AB_ZeroFill = 1;

   while ( (!GetASPI32Buffer( pASPIBuffer )==FALSE) && (pASPIBuffer->AB_BufLen
    > 1024*32) )
   {
      pASPIBuffer->AB_BufLen /= 2;
   }
   if ( pASPIBuffer->AB_BufLen<(1024*32))
   {
      memset( pASPIBuffer, 0, sizeof(ASPI32BUFF) );
   }

}

void CAspiCD::DeAllocASPIBuffer(ASPI32BUFF* pASPIBuffer)
{
   FreeASPI32Buffer( pASPIBuffer );
   memset( pASPIBuffer, 0, sizeof(ASPI32BUFF) );
}
*/

void CAspiCD::BusDeviceReset()
{
   // Create SRB_GETDISKINFO header
   SRB_BUSDEVICERESET mySrb;

   // Clear all fields
   memset(&mySrb,0x00,sizeof(SRB_BUSDEVICERESET));

   // Set SRB fields
   mySrb.SRB_Cmd=SC_RESET_DEV;

   mySrb.SRB_HaId=GetTargetID();
   mySrb.SRB_Target=GetTargetID();
   mySrb.SRB_Lun=GetLunID();

   // Send command
   DWORD dwReturn=SendASPI32Command((LPSRB)&mySrb);


   // Check aspi function call
   IsScsiError((LPSRB)&mySrb);

   m_btLastError=mySrb.SRB_Status;
}



void CAspiCD::PreventMediaRemoval (BOOL bAudioMode)
{
   if ( GetLockDuringRead() )
   {
      DebugPrintf("Entering CAspiCD::PreventMediaRemoval with value %d",
       bAudioMode );
      BYTE cmd [6] = {0x1E,GetLunID()<<5, 0, 0, 0, 0 };

      // Prevent Media removal by setting flagg
      if (bAudioMode) 
      {
         cmd[4] = 0x1;
      }

      // And issue SCSI command
      IssueScsiCmd( SRB_DIR_OUT, cmd,sizeof(cmd), NULL, 0 );

      // Log Sense data
      DebugPrintf("PreventMediaRemoval sense data
       %02X/%02X/%02X",g_SenseKey.SK,g_SenseKey.ASC,g_SenseKey.ASCQ);

      DebugPrintf("Leaving CAspiCD::PreventMediaRemoval");
   }
}


// Method to get CD table of contents via SCSI interface
CDEX_ERR CAspiCD::ReadToc()
{
   const int nBufferSize=CB_CDROMSECTOR;

   BYTE pbtBuffer[nBufferSize];

   // Clear buffer
   memset(pbtBuffer,0x00,sizeof(pbtBuffer));

   DebugPrintf("Entering CAspiCD::ReadToc");

   BYTE cmd [10] = { 0x43,GetLunID()<<5, 0, 0, 0, 0, 1, CB_CDROMSECTOR >> 8,
    CB_CDROMSECTOR & 0xFF, 0 };

   // Issue Scsi command to read Table Of Contents
   IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,nBufferSize);

   // Calculate total number of TOC entries
   WORD wNumTracks = ((pbtBuffer [0] << 8) + pbtBuffer [1] - 2) / 8 ;


   // Add TOC entries
   GetToc().Clear();

   // Set numbner of TOC entries
   if (wNumTracks>0)
   {
      // Get a pointer to the track information
      BYTE* btTocStream=pbtBuffer + 4;

      // Fill all the track info entries
      // Also add the lead out track (TRACK 0xAA)
      for (int iTrackEntry=0;iTrackEntry<wNumTracks;iTrackEntry++)
      {
         GetToc().FillEntry(btTocStream+iTrackEntry*8);
      }

      // Dump debug information
   //   if (g_config.GetDumpToc())
   //      DumpToc(btTocStream,8*wNumTracks);
   //   DebugPrintf("CDInfo::ReadToc");
   }
   return CDEX_OK;
}


// Method to get CD table of contents via SCSI interface
CDEX_ERR CAspiCD::ReadCDText(BYTE* pbtBuffer,int nBufferSize,LPINT pnCDTextSize)
{
   ASSERT(pbtBuffer!=NULL);
   ASSERT(nBufferSize>4);
   ASSERT(pnCDTextSize!=NULL);

   DebugPrintf("Entering CAspiCD::ReadCDText");

   // Clear buffer
   memset(pbtBuffer,0x00,nBufferSize);

   BYTE cmd [10] = { 0x43,GetLunID()<<5, 5, 0, 0, 0, 1, 0, 4, 0 };

   // Issue Scsi command to read Table Of Contents
   IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,4);

   // Get length of CD_TEXT fields
   *pnCDTextSize = ((pbtBuffer[0] << 8 ) | pbtBuffer[1]) + 2;

   // Check size, return if it is too small
   if ( (*pnCDTextSize>nBufferSize) || (*pnCDTextSize<4) )
      return CDEX_ERROR;

   cmd[7] = (BYTE) (*pnCDTextSize >> 8);
   cmd[8] = (BYTE) (*pnCDTextSize&0xFF);

   // Issue Scsi command to read Table Of Contents
   IssueScsiCmd(SRB_DIR_IN,cmd,sizeof(cmd),pbtBuffer,nBufferSize);

   return CDEX_OK;
}


// added by Andi, scenalyzer@blackbox.net

#include "process.h"

struct ReadParas
{
   BYTE* pbtReadBuffer;
   int  nSectorsPerRead;
   LONG lSector;
   DWORD dwNumSectors;
   CRITICAL_SECTION crit;
   BOOL m_bOk;
   CAspiCD *pCd;
};

void __cdecl ReadThreadProc(LPVOID param)
{
   ReadParas *pRd = (ReadParas*)param;

   SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 

   while(1)
   {
      EnterCriticalSection(&pRd->crit);

      BYTE*   pbtReadBuffer = pRd->pbtReadBuffer;
      LONG   lSector = pRd->lSector;
      DWORD   dwNumSectors = pRd->dwNumSectors;

      if(dwNumSectors > pRd->nSectorsPerRead)
         dwNumSectors = pRd->nSectorsPerRead;

      pRd->pbtReadBuffer += CB_CDDASECTOR * dwNumSectors;

      pRd->dwNumSectors -= dwNumSectors;

      pRd->lSector += dwNumSectors;

      LeaveCriticalSection(&pRd->crit);

      if(!dwNumSectors || !pRd->m_bOk)
         return;
      if( !pRd->pCd->ReadCdRomSector( pbtReadBuffer, lSector, dwNumSectors,
       FALSE ) )
         pRd->m_bOk = FALSE;
   };
};
// end added

BOOL CAspiCD::ReadCdRomSector(
   BYTE*   pbtReadBuffer,
   LONG   lSector,
   DWORD   dwNumSectors,
   BOOL   bGetC2

   )
{
   // added by Andi, scenalyzer@blackbox.net
   const int nMaxSectorsAtOnce = 27;
   
   if( dwNumSectors >= 1000 && dwNumSectors > nMaxSectorsAtOnce )   
   {
      ReadParas rd;

      rd.nSectorsPerRead = nMaxSectorsAtOnce;

      InitializeCriticalSection(&rd.crit);

      rd.dwNumSectors = dwNumSectors;
      rd.lSector = lSector;
      rd.pbtReadBuffer = pbtReadBuffer;
      rd.pCd = this;
      rd.m_bOk = TRUE;
      HANDLE hs[2];

      hs[0] = (HANDLE)_beginthread(ReadThreadProc, 0, &rd);

      hs[1] = (HANDLE)_beginthread(ReadThreadProc, 0, &rd);

      WaitForMultipleObjects(2, hs, TRUE, INFINITE);

      DeleteCriticalSection(&rd.crit);

      return rd.m_bOk;
   }
   // end added

   DebugPrintf("Entering CAspiCD::ReadCdRomSector, lSector=%d
    dwNumSectors=%d",lSector,dwNumSectors);

   int nCmdSize=10;

   BYTE cmd [12] = {0x00,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

   switch (GetReadMethod())
   {
      case READ10:   cmd [0]=0x28; nCmdSize=10;break;   // Read 10 (standard)
      case READC3:   cmd [0]=0xA8; nCmdSize=12;break;   // Read A8_12
      case READNEC:   cmd [0]=0xD4; nCmdSize=10;break;   // Read Nec Specific
      case READC1:   cmd [0]=0xD4; nCmdSize=12;break;   // Read D4_12
      case READC2:   cmd [0]=0xD5; nCmdSize=10;break;   // Read D5_10
      case READSONY:   cmd [0]=0xD8; nCmdSize=12;break;   // Read Sony
      case READMMC:   cmd [0]=0xBE; nCmdSize=12;break;   // Read MMC
      case READMMC2:   cmd [0]=0xBE; nCmdSize=12;break;   // Read MMC12
      case READMMC3:   cmd [0]=0xBE; nCmdSize=12;break;   // Read MMC12
      default: ASSERT(FALSE);
   }

   cmd [1] = GetLunID()<<5;                     // Set LUN
   cmd [2] = (BYTE)(lSector >> 24);            // Set Start Sector 
   cmd [3] = (BYTE)((lSector >> 16) & 0xFF);      // Set Start Sector
   cmd [4] = (BYTE)((lSector >> 8) & 0xFF);      // Set Start Sector
   cmd [5] = (BYTE)(lSector & 0xFF);            // Set Start Sector

   switch (GetReadMethod())
   {
      case READ10:   
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);      // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors to
          read
      break;   // Read 10

      case READC3:   
         cmd[8]=(BYTE)((dwNumSectors >>8) & 0xFF);      // Number of sectors to
          read
         cmd[9]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors to
          read
      break;   // Read A8_12

      case READNEC:
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to read
      break;   // Read Nec Specific

      case READC1:   
         cmd[8]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[9]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to read
      break;   // Read D4_12

      case READC2:   
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to read
      break;   // Read D5_10

      case READSONY:   
         cmd[8]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[9]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to read
      break;   // Read Read Sony

      case READMMC3:   
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to read
         cmd[9]=0x10;                        // Set vendor specific byte
      break;   // Read MMC

      case READMMC:   
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors to
          read
         cmd[9]=0xF8;                        // Set vendor specific byte
      break;   // Read MMC3

      case READMMC2:   
         cmd[1]|=0x04;
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors to
          read
         cmd[9]=0xF8;                        // Set vendor specific byte
      break;   // Read MMC
      case READMMC4:   
         cmd[1]|=0x04;
         cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
          read
         cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors to
          read
         cmd[9]=0x10;                        // Set vendor specific byte
      break;   // Read MMC
   }

   // Clear old memory buffer
   memset(pbtReadBuffer,0x00,dwNumSectors * CB_CDDASECTOR);

   // Issue SCSI command
   if (IssueScsiCmd(SRB_DIR_IN,cmd,nCmdSize,pbtReadBuffer,dwNumSectors *
    CB_CDDASECTOR)!=SS_COMP)
   {
      DebugPrintf("Leaving CAspiCD::ReadCdRomSectorv (ERROR)");
      return FALSE;
   }


   // Do byte swapping if necessary
   if ( GetEndian() == BIGENDIAN )
   {
      // Get pointer to read buffer
      BYTE* pbtBuffer=pbtReadBuffer;

      // Loop trhough the entire read buffer and swap the bytes
      for (DWORD i=0;i<dwNumSectors*CB_CDDASECTOR;i+=2)
      {
         BYTE bTmp=pbtBuffer[1];
         pbtBuffer[1]=pbtBuffer[0];
         pbtBuffer[0]=bTmp;
         pbtBuffer+=2;
      }
   }

   // Write out the last bits and pieces
   if (GetSwapLefRightChannel())
   {
      // Get pointer to read buffer
      short* psBuffer=(short*)pbtReadBuffer;

      // Loop trhough the entire read buffer and swap the bytes
      for (DWORD i=0;i<dwNumSectors*CB_CDDASECTOR/2;i+=2)
      {
         short sTmp=*(psBuffer+1);
         *(psBuffer+1)=*psBuffer;
         *(psBuffer)=sTmp;
         psBuffer+=2;
      }
   }
   return TRUE;
   DebugPrintf("Leaving CAspiCD::ReadCdRomSector");
}





BOOL CAspiCD::GetStatus()
{
   DWORD ASPIStatus;

    ASPIStatus = GetASPI32SupportInfo();

   m_btLastError=HIBYTE(LOWORD(ASPIStatus));

    switch( HIBYTE(LOWORD(ASPIStatus)) )
    {
        case SS_COMP:
            /*
             * ASPI for Win32 is properly initialized
             */
            m_bNumHA = LOBYTE(LOWORD(ASPIStatus));
         return TRUE;
            break;
        default:
         char lpszError[255];
         GetAspiError(m_btLastError,lpszError);
         sprintf(lpszError,"ASPI for Win32 is not installed
          correctly\r\n%s",lpszError);
         MessageBox(NULL,lpszError,"ASPI Error",MB_OK);
            return FALSE;
    }
   return FALSE;
}


void CAspiCD::ScanForC2Errors(   LONG   lStartSector,
                        LONG   lEndSector,
                        INT&   nErrors,
                        INT*   pnErrorSectors,
                        INT      nMaxErrors,
                        BOOL&   bAbort   )
{
   DWORD   dwNumSectors = 10;
   LONG   lSector = lStartSector;
   BYTE*   pDataBuf = new BYTE[ dwNumSectors * ( 2352 + 294 ) ];
   DWORD   i = 0;
   DWORD   j = 0;
   int      nCmdSize = 10;

   DebugPrintf("Entering CAspiCD::ScanForC2Errors from sector %d to sector",
    lStartSector, lEndSector );


   while ( ( lSector < lEndSector ) && ( FALSE == bAbort ) )
   {
      BYTE cmd [12] = {0x00,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

      switch (GetReadMethod())
      {
         case READ10:   cmd [0]=0x28; nCmdSize=10;break;   // Read 10 (standard)
         case READC3:   cmd [0]=0xA8; nCmdSize=12;break;   // Read A8_12
         case READNEC:   cmd [0]=0xD4; nCmdSize=10;break;   // Read Nec Specific
         case READC1:   cmd [0]=0xD4; nCmdSize=12;break;   // Read D4_12
         case READC2:   cmd [0]=0xD5; nCmdSize=10;break;   // Read D5_10
         case READSONY:   cmd [0]=0xD8; nCmdSize=12;break;   // Read Sony
         case READMMC:   cmd [0]=0xBE; nCmdSize=12;break;   // Read MMC
         case READMMC2:   cmd [0]=0xBE; nCmdSize=12;break;   // Read MMC12
         case READMMC3:   cmd [0]=0xBE; nCmdSize=12;break;   // Read MMC12
         default: ASSERT(FALSE);
      }

      cmd [1] = GetLunID()<<5;                     // Set LUN
      cmd [2] = (BYTE)(lSector >> 24);            // Set Start Sector 
      cmd [3] = (BYTE)((lSector >> 16) & 0xFF);      // Set Start Sector
      cmd [4] = (BYTE)((lSector >> 8) & 0xFF);      // Set Start Sector
      cmd [5] = (BYTE)(lSector & 0xFF);            // Set Start Sector


      switch (GetReadMethod())
      {
         case READ10:   
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);      // Number of sectors
             to read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors
             to read
         break;   // Read 10

         case READC3:   
            cmd[8]=(BYTE)((dwNumSectors >>8) & 0xFF);      // Number of sectors
             to read
            cmd[9]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors
             to read
         break;   // Read A8_12

         case READNEC:
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to
             read
         break;   // Read Nec Specific

         case READC1:   
            cmd[8]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[9]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to
             read
         break;   // Read D4_12

         case READC2:   
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to
             read
         break;   // Read D5_10

         case READSONY:   
            cmd[8]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[9]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to
             read
         break;   // Read Read Sony

         case READMMC3:   
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);      // Number of sectors to
             read
            cmd[9]=0x10;                        // Set vendor specific byte
         break;   // Read MMC

         case READMMC:   
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors
             to read
            cmd[9]=0xF8;                        // Set vendor specific byte
         break;   // Read MMC3

         case READMMC2:   
            cmd[1]|=0x04;
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors
             to read
            cmd[9]=0xF8;                        // Set vendor specific byte
         break;   // Read MMC
         case READMMC4:   
            cmd[1]|=0x04;
            cmd[7]=(BYTE)((dwNumSectors >>8) & 0xFF);   // Number of sectors to
             read
            cmd[8]=(BYTE) (dwNumSectors & 0xFF);         // Number of sectors
             to read
            cmd[9]=0x10;                        // Set vendor specific byte
         break;   // Read MMC
      }

      /* Sync + all headers + user data + EDC/ECC + C2 */
      cmd[ 9 ] = (1 << 7 | 3 << 5 | 1 << 4 | 1 << 3 | 1 << 1);

      // Issue SCSI command
      IssueScsiCmd( SRB_DIR_IN, cmd, nCmdSize, pDataBuf, dwNumSectors * ( 2352
       + 294 ) );

      // check if all data in subchannel is zero
      for ( i =0; i< dwNumSectors; i++ )
      {
         BYTE* pTest = &pDataBuf[ ( 2352 + 294 ) * i + 2352 ];
         for ( j=0; j< 294; j++ )
         {
            if ( 0 != pTest[ i ] )
               break;
         }

         // check if there were any non zero values in the subchannel 
         if ( 294 != j )
         {
            // error in sector lSector + i
            DebugPrintf("C2-Error in sector %d", lSector + i );
            if ( nErrors < nMaxErrors )
            {
               pnErrorSectors[ nErrors++ ] = lSector + i;
            }
            else
            {
            }
         }
      }

      // skip to next block
      lSector+= dwNumSectors;
   }

   delete [] pDataBuf;


   // Log Sense data
   DebugPrintf("ScanForC2Errors sense data
    %02X/%02X/%02X",g_SenseKey.SK,g_SenseKey.ASC,g_SenseKey.ASCQ);

   DebugPrintf("Leaving CAspiCD::ScanForC2Errors");
}