/*-------------------------------------------------------*/
/* Originally part of SOUNDHAX v1 by John M. Trindle     */
/*                 FREEWARE 12/19/91                     */
/*-------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>

#define MULTIPLEX_INT   0x2f
#define INIT_MP_INT     0x1500;
#define CDREQ_MP_INT    0x1510;

#define STAT_HAS_AN_ERROR 0x8000
#define STAT_BUSY         0x0200
#define STAT_DONE         0x0100

int CDRomLoaded,CDRomDrive;

#define CD_EJECT_COMMAND      0
#define CD_SEEK_COMMAND       131
#define CD_PLAY_COMMAND       132
#define CD_STOP_COMMAND       133
#define CD_RESUME_COMMAND     136

#define READ_IOCTL_COMMAND   3
#define WRITE_IOCTL_COMMAND  12

#define CD_STATUS_COMMAND     6

#define GET_AUDIO_DISKINFO    10
#define GET_AUDIO_TRACKINFO   11


#define        ADDR_HSG        0
#define        ADDR_RED        1

#define  CMS_AVAILABLE  1
#define  FM_AVAILABLE   2
#define  CV_AVAILABLE   4

#define  OFF            0
#define  ON             1

#define  FALSE          0
#define  TRUE           1

extern _ct_io_addx;
extern _ct_int_num;
extern _ct_music_status;
extern _ct_voice_status;

static int StatusWord;

typedef struct
{
   unsigned char ParamLength;
   unsigned char SubUnit;
   unsigned char CommandCode;
   int           Status;
   unsigned long Reserved1;
   unsigned long Reserved2;
   unsigned char AddressMode;
   void     *BeginPointer;
   unsigned long Length;
   unsigned long Reserved3;
} CDCmdStru;

typedef struct
{
   unsigned char CommandCode;
   unsigned char TrackNumber;
   unsigned long StartAddress;
   unsigned char Control;
} CDTrackInfoStru;

typedef struct
{
  unsigned char CommandCode;
} CDEjectStru;

typedef struct
{
  unsigned char CommandCode;
  unsigned long Status;
} CDStatusStru;

typedef struct
{
   unsigned char AddrMode;
   unsigned long FrameOffset;
   unsigned long NumberFrames;
} CDPlayStru;

typedef struct DiskInfo_Rec {
   unsigned char CommandCode;
   unsigned char LoTrack;
   unsigned char HiTrack;
   unsigned long LeadOut;
} CDDiskInfoStru;


CDCmdStru CDCmdBlock;
CDTrackInfoStru TrackInfo[100];
CDDiskInfoStru DiskInfo;

SendCDCommand(int CommandCode, void *BeginPtr, long Length);
unsigned long CDTrackInfo(int TrackNumber, int Minutes,
                          int Seconds, int Frames);
CDPlay(long Offset,long NumFrames);
unsigned int CDStatus();
PrintTimeFrames(long NumFrames);
unsigned long Red2HSG(unsigned long RedValue);

long LastSector;
int NumTracks;

main(int argc, char *argv[])
{
   int i,j,DriverFeatures=0;

   int Track,StartMin,StartSec,EndMin,EndSec,StartFrame,EndFrame;
   long StartSector,EndSector;

   FILE *outfp;

   float TempFloat;
   char LocalBuffer[132];

   if (!CheckCD2F())
   {
      printf("MSCDEX NOT LOADED\n");
      exit(1);
   }

   CDGetDiskInfo();
   LastSector = DiskInfo.LeadOut;
   NumTracks = DiskInfo.HiTrack-DiskInfo.LoTrack+1;

   printf("Reading Drive %c\n",CDRomDrive+'A');

   if (argc > 1)
   {
      Track = atoi(argv[1]);
      printf("Starting Track %d, %d tracks on disk\n",Track,NumTracks);
   }
   else
   {
      for (i = DiskInfo.LoTrack; i <= DiskInfo.HiTrack; i++)
      {
         CDTrackInfo(i,0,0,0);
      }

      TrackInfo[DiskInfo.HiTrack+1].StartAddress = DiskInfo.LeadOut;

      for (i = DiskInfo.LoTrack; i <= DiskInfo.HiTrack; i++)
      {
         printf("Track %2d ",i);
         PrintTimeFrames(Red2HSG(TrackInfo[i+1].StartAddress)-Red2HSG(TrackInfo[i].StartAddress));
         printf("\n");
      }
   }

   if (argc < 2)
   {
      exit(0);
   }

   outfp = NULL;

   EndSector = Red2HSG(DiskInfo.LeadOut)-150L;  /* 150L Fudge Factor */

   StartMin = 0;
   StartSec = 0;
   StartFrame = 0;

   EndMin = 0;
   EndSec = 0;
   EndFrame = 0;

   if (argc > 2)
   {
      strcpy(LocalBuffer,argv[2]);

      StartMin = atoi(strtok(LocalBuffer,":"));
      TempFloat = atof(strtok(NULL,":"));

      StartSec = (int) TempFloat;
      StartFrame = ((int)(TempFloat*75))-StartSec*75;
   } 
   StartSector = CDTrackInfo(Track,StartMin,StartSec,StartFrame);

   if (argc > 3)
   {
      strcpy(LocalBuffer,argv[3]);

      EndMin = atoi(strtok(LocalBuffer,":"));
      TempFloat = atof(strtok(NULL,":"));

      EndSec = (int) TempFloat;
      EndFrame = ((int)(TempFloat*75))-EndSec*75;

      EndSector   = CDTrackInfo(Track,EndMin,EndSec,EndFrame);
   } 

   CDStop();

   if (argc > 4)
   {
      _ct_io_addx = 0x220;    /* I/O Base */
      DriverFeatures = _ct_card_here();
      if (DriverFeatures & CV_AVAILABLE)
      {
         _sbc_scan_int();
         _ctvd_init(6);

         printf("opening file %s\n",argv[4]);
         outfp = fopen(argv[4],"wb");

         _ctvd_speaker(OFF);
         _ctvd_input(outfp->_file,250);
      }

   }

   CDPlay(StartSector,EndSector-StartSector);

   if (outfp != NULL)
   {
      printf("Press any key to abort\n");

      /* This could be keyed on CDStatus BUSY, but corrupts sample */

      while(TRUE)
      {
         if (kbhit())
         {
            getch();
            CDStop();
            break;
         }
      }
   } 

   printf("\n");

   if (outfp != NULL)
      fclose(outfp);

   if (DriverFeatures & CV_AVAILABLE && argc > 4)
      _ctvd_terminate();
   
}


CheckCD2F()
{
   union REGS regs;

   regs.x.ax = INIT_MP_INT;
   int86(MULTIPLEX_INT,&regs,&regs);
   CDRomLoaded = regs.x.bx;
   CDRomDrive = regs.x.cx;
}

CDEject()
{
   CDEjectStru EjectRequestBlock;

   EjectRequestBlock.CommandCode = CD_EJECT_COMMAND;

   SendCDCommand(WRITE_IOCTL_COMMAND,&EjectRequestBlock,sizeof(CDEjectStru));
}

unsigned int CDStatus()
{
   unsigned int RetVal;
   CDStatusStru StatusRequestBlock;

   StatusRequestBlock.CommandCode = CD_STATUS_COMMAND;

   RetVal = SendCDCommand(READ_IOCTL_COMMAND,&StatusRequestBlock,sizeof(CDStatusStru));
   return(RetVal);
}

CDStop()
{
   return(SendCDCommand(CD_STOP_COMMAND,NULL,0));
}


CDResume()
{
   return(SendCDCommand(CD_RESUME_COMMAND,NULL,0));
}

CDPlay(long FrameOffset, long NumFrames)
{
   SendCDCommand(CD_PLAY_COMMAND,(void *)FrameOffset,NumFrames);
}

CDGetDiskInfo()
{
   DiskInfo.CommandCode = GET_AUDIO_DISKINFO;
   SendCDCommand(READ_IOCTL_COMMAND,&DiskInfo,sizeof(CDDiskInfoStru));
}

PrintTimeFrames(long NumFrames)
{
   long Mins;
   long Secs;
   long PlusFrames;
   PlusFrames = NumFrames;
   Secs = PlusFrames/75;
   PlusFrames -= Secs*75;
   Mins = Secs/60;
   Secs -= Mins*60;

   printf("%2ld:%02ld:%02ld",Mins,Secs,PlusFrames);
}

SendCDCommand(int CommandCode, void *BeginPtr, long Length)
{
   union REGS regs;
   struct SREGS sregs;

   CDCmdBlock.ParamLength = 13;
   CDCmdBlock.SubUnit     = 0;
   CDCmdBlock.CommandCode = CommandCode;
   CDCmdBlock.Status      = 0;
   CDCmdBlock.AddressMode = ADDR_HSG;
   CDCmdBlock.BeginPointer = BeginPtr;
   CDCmdBlock.Length      = Length;
   CDCmdBlock.Reserved3   = 0;

   regs.x.ax = CDREQ_MP_INT;
   regs.x.bx = FP_OFF(&CDCmdBlock);
   regs.x.cx = CDRomDrive;
   sregs.es  = FP_SEG(&CDCmdBlock);

   int86x(MULTIPLEX_INT,&regs,&regs,&sregs);

   return(CDCmdBlock.Status);
}

unsigned long CDTrackInfo(int TrackNumber, int Minutes, int Seconds, int Frames)
{

   TrackInfo[TrackNumber].CommandCode = GET_AUDIO_TRACKINFO;

   TrackInfo[TrackNumber].TrackNumber = TrackNumber;

   SendCDCommand(READ_IOCTL_COMMAND,&(TrackInfo[TrackNumber]),sizeof(CDTrackInfoStru));

   return(Red2HSG(TrackInfo[TrackNumber].StartAddress)+(Minutes*60L+((long)Seconds))*75L+((long)Frames));
}

unsigned long Red2HSG(unsigned long RedValue)
{

   unsigned long Mins;
   unsigned long Secs;
   unsigned long PlusFrames;

   PlusFrames = RedValue & 0x000000ffL;

   Secs = ((RedValue & 0x0000ff00L) >> 8);

   Mins = ((RedValue & 0x00ff0000L) >> 16);

   return((Mins*60+Secs)*75+PlusFrames);
}


