/***************************************************************************
                           generic.c  -  description
                             -------------------
    begin                : Sun Nov 16 2003
    copyright            : (C) 2003 by Pete Bernert
    email                : BlackDove@addcom.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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. See also the license.txt file for *
 *   additional informations.                                              *
 *                                                                         *
 ***************************************************************************/

//*************************************************************************//
// History of changes:
//
// 2004/12/25 - Pete
// - repaired time2addr/addr2time
//
// 2003/11/16 - Pete
// - generic cleanup for the Peops release
//
//*************************************************************************//

/////////////////////////////////////////////////////////

#include "stdafx.h"
#define _IN_GENERIC
#include "externals.h"

/////////////////////////////////////////////////////////

int        iCD_AD=-1;                                  // drive address
int        iCD_TA=-1;               
int        iCD_LU=-1;
int        iRType=0;                                   // read mode
int        iUseSpeedLimit=0;                           // speed limit use
int        iSpeedLimit=2;                              // speed 2x
int        iNoWait=0;                                  // wait
int        iMaxRetry=5;                                // retry on error
int        iShowReadErr=0;                             // show msg on error
HANDLE     hEvent=NULL;                                // global event
HINSTANCE  hASPI=NULL;                                 // aspi lib
READFUNC   pReadFunc=NULL;                             // read func
DEINITFUNC pDeInitFunc=NULL;                           // deinit func
int        iInterfaceMode=1;                           // interface (aspi/ioctrlscsi/ioctrlraw)
int        iWantedBlockSize=2352;
int        iUsedBlockSize=2352;
int        iUsedMode=CDVD_MODE_2352;
int        iBlockDump=0;

DWORD (*pGetASPI32SupportInfo)(void);                  // ptrs to aspi funcs
DWORD (*pSendASPI32Command)(LPSRB);

/////////////////////////////////////////////////////////

void addr2time(unsigned long addr, unsigned char *time)
{
 addr+=150;
 time[3] = (unsigned char)(addr%75);
 addr/=75;
 time[2]=(unsigned char)(addr%60);
 addr/=60;
 time[1]=(unsigned char)(addr%100);
 time[0]=(unsigned char)(addr/100);
}

void addr2timeB(unsigned long addr, unsigned char *time)
{
 time[3] = itob((unsigned char)(addr%75));
 addr/=75;
 time[2]=itob((unsigned char)(addr%60));
 addr/=60;
 time[1]=itob((unsigned char)(addr%100));
 time[0]=itob((unsigned char)(addr/100));
}

unsigned long time2addr(unsigned char *time)
{
 unsigned long addr;

 addr =  time[0]*100;
 addr += time[1];
 addr *= 60;
 
 addr = (addr + time[2])*75;
 addr += time[3];
 addr -= 150;
 return addr;
}

unsigned long time2addrB(unsigned char *time)
{
 unsigned long addr;

 addr =  (btoi(time[0]))*100;
 addr += btoi(time[1]);
 addr *= 60;
 addr = (addr + btoi(time[2]))*75;
 addr += btoi(time[3]);
 addr -= 150;
 return addr;
}

#ifndef _GCC
unsigned long reOrder(unsigned long value)
{
#pragma warning (disable: 4035)
   __asm 
    {
     mov eax,value
     bswap eax
    }
}
#endif

/////////////////////////////////////////////////////////

void CreateGenEvent(void)
{
 hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
}

/////////////////////////////////////////////////////////

void FreeGenEvent(void)
{
 if(hEvent) CloseHandle(hEvent);
 hEvent=0;
}

/////////////////////////////////////////////////////////

DWORD WaitGenEvent(DWORD dwMS)
{
 if(hASPI)                                             // aspi event
  return WaitForSingleObject(hEvent,dwMS);
 else
  {                                                    // ioctl overlapped (always waiting til finished, dwMS not used)
   DWORD dwR=0;
   GetOverlappedResult(hIOCTL,&ovcIOCTL,&dwR,TRUE);
   return 0;
  }
}

/////////////////////////////////////////////////////////

void LockGenCDAccess(void)
{
 if(hReadThread)                                       // thread mode?
  WaitForSingleObject(hThreadMutex[0],INFINITE);       // -> wait until all reading is done
 else                                                  // async prefetch?
 if(bDoWaiting)                                        // -> async operation has to finish first
  {WaitGenEvent(0xFFFFFFFF);bDoWaiting=FALSE;}
}

/////////////////////////////////////////////////////////

void UnlockGenCDAccess(void)
{
 if(hReadThread)                                       // thread mode?
  ReleaseMutex(hThreadMutex[0]);                       // -> we are finished with our special command, now reading can go on
}

/////////////////////////////////////////////////////////

void WaitUntilDriveIsReady(void)
{
 if(iNoWait==0)
  {
   while(TestSCSIUnitReady()==0) Sleep(500);
  } 
}

/////////////////////////////////////////////////////////

void SetGenCDSpeed(int iReset)
{
 if(iUseSpeedLimit)
  {
   if(bDoWaiting)                                      // still a command running? wait 
    {WaitGenEvent(0xFFFFFFFF);bDoWaiting=FALSE;}

   if(iReset) SetSCSISpeed(0xFFFF);
   else
   if(SetSCSISpeed(176*iSpeedLimit)<=0)
    {
     MessageBox(GetActiveWindow(),
                "Failure: cannot change the drive speed!",
                "cdvdPeops... speed limitation",
                MB_OK|MB_ICONEXCLAMATION);
     iUseSpeedLimit=0;
    }
  }
}

/////////////////////////////////////////////////////////
// checks, which direct subchannel reading type is supported

void GetBESubReadFunc(void)
{
 unsigned char * pB=(unsigned char *)malloc(4096);

 pReadFunc  = ReadSCSI_BE_Sub;                         // pre-init read func

 WaitUntilDriveIsReady();                              // wait before first read

 ReadSub_BE_2(0,pB,1);                                 // first (unchecked) read
 if(!ReadSub_BE_2(0,pB,1))                             // read again, and check this time
  {                                                    // -> read failed?
   if(ReadSub_BE_2_1(0,pB,1))                          // -> try different sub reading
    {                                                  // --> success? mmm... let us check the data
     DecodeSub_BE_2_1(pB+2352);                        // --> decode sub
     if(*(pB+2352)==0x41)                              // --> check the first decoded byte
      pReadFunc  = ReadSCSI_BE_Sub_1;                  // ---> wow, byte is ok
    }
  }
 free(pB);                                            
}

/////////////////////////////////////////////////////////

int GetGenReadFunc(int iRM)
{
 switch(iRM)                                           // scsi read mode
  {
   // ------------------------------------------------ //
   case MODE_BE_1:
   case MODE_BE_2:
    {
     if(iUseSubReading==1)
           GetBESubReadFunc();
      else pReadFunc = ReadSCSI_BE;
     } break;
   // ------------------------------------------------ //
   case MODE_28_1:
   case MODE_28_2:
    {
     if(iUseSubReading==1)
          pReadFunc  = ReadSCSI_28_Sub;
     else pReadFunc  = ReadSCSI_28;
    } break;
   // ------------------------------------------------ //
   case MODE_28_2048:
    {
     pReadFunc       = ReadSCSI_28_2048;
    } break;
   // ------------------------------------------------ //
   case MODE_28_2048_Ex:
    {
     pReadFunc       = ReadSCSI_28_2048_Ex;
    } break;
   // ------------------------------------------------ //
   default:
    {
     pReadFunc       = ReadSCSI_Dummy;
    } return -3;
   // ------------------------------------------------ //
  }
 return 1;
}

/////////////////////////////////////////////////////////

int OpenGenCD(int iA,int iT,int iL)
{
 pDeInitFunc = NULL;                                   // init de-init func
 pReadFunc   = ReadSCSI_Dummy;                         // init (dummy) read func

 if(iA==-1) return -1;                                 // not configured properly

 // -------------------------------------------------- //

 if(iInterfaceMode>1)                                  // ioctrl interfaces?
  {
   OpenIOCTLHandle(iA,iT,iL);                          // open w2k/xp ioctrl device
   if(hIOCTL==NULL) return -2;                         // no cdrom available

   if(iInterfaceMode==3)                               // special ioctl RAW mode?
    {                                                  // -> get special reading funcs (non-scsi!)
     if(iUseSubReading==1)                         
          pReadFunc  = ReadIOCTL_Raw_Sub;
     else pReadFunc  = ReadIOCTL_Raw;

     WaitUntilDriveIsReady();
     return 1;
    }
  }
 else                                                  // aspi interface?
  {
   int iDevice=GetSCSIDevice(iA,iT,iL);                // get device type
   if(iDevice!=DTYPE_CDROM) return -2;                 // no cdrom? bye
  }

 if(CheckSCSIReadMode()==SS_COMP)
  WaitUntilDriveIsReady();
 else 
  {
   CloseIOCTLHandle();
   return -3;
  }

 return 1;
}

/////////////////////////////////////////////////////////

void CloseGenCD(void)
{
 iCDROK=0;                                             // no more cd available
 if(pDeInitFunc) pDeInitFunc();                        // deinit, if needed
 pDeInitFunc = NULL;                
 pReadFunc   = ReadSCSI_Dummy;
 CloseIOCTLHandle();                                   // close ioctl drive file (if used)
}

/////////////////////////////////////////////////////////

void OpenGenInterface(void)
{
 hASPI=NULL;

 if(iInterfaceMode==0) return;                         // no interface? no fun
 else
 if(iInterfaceMode==1)                                 // aspi
  {
   hASPI=LoadLibrary("WNASPI32.DLL");
   if(hASPI)
    {
     pGetASPI32SupportInfo =
      (DWORD(*)(void)) GetProcAddress(hASPI,"GetASPI32SupportInfo");
     pSendASPI32Command =
      (DWORD(*)(LPSRB))GetProcAddress(hASPI,"SendASPI32Command");

     if(!pGetASPI32SupportInfo || !pSendASPI32Command)
      {
       iInterfaceMode=0;
       return;
      }
    }
  }
 else                                                  // ioctl
  {
   if(iInterfaceMode<2 || iInterfaceMode>3) iInterfaceMode=2;
   pGetASPI32SupportInfo = NULL;
   pSendASPI32Command    = IOCTLSendASPI32Command;
  }
}

/////////////////////////////////////////////////////////

void CloseGenInterface(void)
{
 pGetASPI32SupportInfo=NULL;                           // clear funcs
 pSendASPI32Command=NULL;

 if(hASPI)                                             // free aspi
  {
   FreeLibrary(hASPI);
   hASPI=NULL;
  }
 else CloseIOCTLHandle();                              // or close ioctl file
}

/////////////////////////////////////////////////////////

int GetGenCDDrives(char * pDList)
{
 if(hASPI) return GetSCSICDDrives(pDList);             // aspi? use it
 return GetIOCTLCDDrives(pDList);                      // or use ioctl
}

/////////////////////////////////////////////////////////
