/*  DAGRABX grabs 16 bit 44.1 KHz CD Audio digitally to a file */
/*  This version uses XMS as an intermediate buffer to avoid   */
/*  the "glitches" that otherwise occur                        */


#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <alloc.h>
#include <xmslib.h>
#include <cdrom.h>

#define FALSE  0
#define TRUE   1

int CDRomLoaded,CDRomDrive;

struct EMMMoveStruct moverec;
unsigned int xmshandle, xmsfree,xmstot;

struct ReadLongStruc ReadLongBlock;
struct IOCTLStruc IOCTLBlock;
struct DiskInfoStruc DiskInfoBlock;
struct TrackInfoStruc TrackInfoBlock[100];

unsigned long Red2HSG(unsigned long RedValue);

int *buffer;

main(int argc, char *argv[])
{
struct dfree diskfree;
long avail;
int drive;

unsigned long ssec,esec,ssec2,nsec,nsec2;
unsigned int secstograb,secstowrite,i,j,k,offs;
unsigned char filename[80];
FILE *outfp;

int Track,StartMin,StartSec,EndMin,EndSec,StartFrame,EndFrame;
float TempFloat;
char LocalBuffer[132];
char c = ':';

       if(argc > 3) {
               if (!CheckCD2F()){
                       printf("MSCDEX NOT LOADED\n");
                       exit(1);
               }

               if(!XMS_Setup()) {
                       printf("No XMS handler installed!\n");
                       exit(1);
               }

               while(GetDiskInfo() != STAT_DONE);

               for(i = DiskInfoBlock.LowTrack; i <= DiskInfoBlock.HighTrack; i++)
                       GetTrackInfo(i);

               Track = atoi(argv[1]);
               if( (Track < 1) || (Track > DiskInfoBlock.HighTrack)) {
                       printf("Error: Track must be between %d and %d\n",DiskInfoBlock.LowTrack,DiskInfoBlock.HighTrack);
                       errexit();
               }

               StartMin = 0;
               StartSec = 0;
               StartFrame = 0;
               EndMin = 0;
               EndSec = 0;
               EndFrame = 0;

               strcpy(LocalBuffer,argv[2]);
               StartMin = atoi(strtok(LocalBuffer,":"));
               StartSec = atoi(strtok(NULL,":"));
               StartFrame = atoi(&LocalBuffer[strrchr(argv[2],c)+1-LocalBuffer]);

               strcpy(LocalBuffer,argv[3]);
               EndMin = atoi(strtok(LocalBuffer,":"));
               EndSec = atoi(strtok(NULL,":"));
               EndFrame = atoi(&LocalBuffer[strrchr(argv[3],c)+1-LocalBuffer]);

               ssec = Red2HSG(TrackInfoBlock[Track].Startpoint)+(StartMin*60L+((long)StartSec))*75L+((long)StartFrame) -150L;
               esec = Red2HSG(TrackInfoBlock[Track].Startpoint)+(EndMin*60L+((long)EndSec))*75L+((long)EndFrame) -150L;
               nsec = esec-ssec;
               nsec2 = nsec;
               ssec2 = ssec;

               printf("Start sector     : %ld\n",ssec);
               printf("End   sector     : %ld\n",esec);
               printf("Number of sectors: %ld\n",nsec);
               printf("CDDA data size   : %ld bytes\n",nsec*2352l);

               if(argc > 4) {
                       if((buffer = malloc(59000)) == NULL){
                               printf("Malloc error!\n");
                               errexit();
                       }

                       XMS_FreeMem(&xmsfree,&xmstot);
                       XMS_AllocEMB(xmsfree,&xmshandle);

                       if(xmsfree < (unsigned int)(nsec*2352l/1024l)) {
                               printf("Not enough XMS memory!\n");
                               errexit();
                       }

                       drive = getdisk();
                       getdfree(drive+1,&diskfree);
                       avail =  (long) diskfree.df_avail *
                                (long) diskfree.df_bsec *
                                (long) diskfree.df_sclus;
                       if(avail < (nsec*2352l)) {
                               printf("Not enough disk space!\n");
                               errexit();
                       }

                       strcpy(filename,argv[4]);
                       outfp = fopen(filename,"wb");


                       moverec.TLen = 58800l;          /*  2352 bytes * 25 sectors */
                       moverec.SHand = 0;
                       moverec.SOff = (unsigned long)buffer;
                       moverec.DHand = xmshandle;
                       moverec.DOff =  0l;

                       do{
                               secstograb = 25;
                               if(nsec2 < 25l) {
                                       secstograb = (unsigned int)nsec2;
                                       moverec.TLen = (unsigned long)(secstograb*2352);
                               }

                               ReadLong(ssec2,secstograb);

                               while(ReadLongBlock.ReqHdr.Status & STAT_HAS_AN_ERROR)
                                       ReadLong(ssec2,secstograb);

                               XMS_MoveEMB(&moverec);

                               ssec2+=25l;
                               nsec2-=25l;
                               moverec.DOff += 58800l;

                       } while ((long)nsec2 > 0l);


                       moverec.TLen = 58800;
                       moverec.SHand = xmshandle;
                       moverec.SOff = 0l;
                       moverec.DHand = 0;
                       moverec.DOff = (unsigned long)buffer;

                       do {
                               secstowrite = 25;
                               if(nsec < 25l) {
                                       secstowrite = (unsigned int)nsec;
                                       moverec.TLen = (unsigned long)(secstowrite*2352);
                               }

                               XMS_MoveEMB(&moverec);
                               fwrite(buffer,2,1176*secstowrite,outfp);

                               moverec.SOff += 58800l;
                               nsec -= 25l;
                       } while ((long)nsec > 0l);

                       fclose(outfp);
                       free(buffer);
                       XMS_FreeEMB(xmshandle);
               }
               else
               {
                       printf("\nPlaying only...\n");
                       Play(ssec,nsec);
               }
       }
       else {
               printf("DAGRAB CD-DA Audio grabber v0.1\n");
               printf("usage: dagrab <track> <start MM:SS:FF> <end MM:SS:FF> [<filename>]\n");
       }
}


CheckCD2F()
{
union REGS regs;

       regs.x.ax = INIT_MP_INT;
       int86(MULTIPLEX_INT,&regs,&regs);
       CDRomLoaded = regs.x.bx;
       CDRomDrive = regs.x.cx;
}

GetDiskInfo()
{
union REGS regs;
struct SREGS sregs;

       DiskInfoBlock.CntrlCode = GET_AUDIO_DISKINFO;

       IOCTLBlock.ReqHdr.ParamLength = 13;
       IOCTLBlock.ReqHdr.SubUnit     = 0;
       IOCTLBlock.ReqHdr.CommandCode = READ_IOCTL_COMMAND;
       IOCTLBlock.ReqHdr.Status      = 0;
       IOCTLBlock.Meddescr = 0;
       IOCTLBlock.Transfaddr =  (unsigned long)&DiskInfoBlock;
       IOCTLBlock.Numbytes = 7;
       IOCTLBlock.Startsec = 0;
       IOCTLBlock.volID = 0;

       regs.x.ax = CDREQ_MP_INT;
       regs.x.bx = FP_OFF(&IOCTLBlock);
       regs.x.cx = CDRomDrive;
       sregs.es  = FP_SEG(&IOCTLBlock);

       int86x(MULTIPLEX_INT,&regs,&regs,&sregs);

       return(IOCTLBlock.ReqHdr.Status);
}

GetTrackInfo(unsigned char trk)
{
union REGS regs;
struct SREGS sregs;

       TrackInfoBlock[trk].CntrlCode = GET_AUDIO_TRACKINFO;
       TrackInfoBlock[trk].Tracknum = trk;

       IOCTLBlock.ReqHdr.ParamLength = 13;
       IOCTLBlock.ReqHdr.SubUnit     = 0;
       IOCTLBlock.ReqHdr.CommandCode = READ_IOCTL_COMMAND;
       IOCTLBlock.ReqHdr.Status      = 0;
       IOCTLBlock.Meddescr = 0;
       IOCTLBlock.Transfaddr =  (unsigned long)&(TrackInfoBlock[trk]);
       IOCTLBlock.Numbytes = 7;
       IOCTLBlock.Startsec = 0;
       IOCTLBlock.volID = 0;

       regs.x.ax = CDREQ_MP_INT;
       regs.x.bx = FP_OFF(&IOCTLBlock);
       regs.x.cx = CDRomDrive;
       sregs.es  = FP_SEG(&IOCTLBlock);

       int86x(MULTIPLEX_INT,&regs,&regs,&sregs);

       return(IOCTLBlock.ReqHdr.Status);
}

Play(unsigned long SSec,unsigned long numsecs)
{
union REGS regs;
struct SREGS sregs;

       struct PlayStruc PlayBlock;
       struct QInfoStruc QInfoBlock;

       PlayBlock.ReqHdr.ParamLength = 13;
       PlayBlock.ReqHdr.SubUnit     = 0;
       PlayBlock.ReqHdr.CommandCode = CD_PLAY_AUDIO;
       PlayBlock.ReqHdr.Status      = 0;

       PlayBlock.AddressMode = ADDR_HSG;
       PlayBlock.Startsec = SSec;
       PlayBlock.Numsec = numsecs;

       regs.x.ax = CDREQ_MP_INT;
       regs.x.bx = FP_OFF(&PlayBlock);
       regs.x.cx = CDRomDrive;
       sregs.es  = FP_SEG(&PlayBlock);

       int86x(MULTIPLEX_INT,&regs,&regs,&sregs);

       do {
               QInfoBlock.CntrlCode = GET_QCHAN_INFO;

               IOCTLBlock.ReqHdr.ParamLength = 13;
               IOCTLBlock.ReqHdr.SubUnit     = 0;
               IOCTLBlock.ReqHdr.CommandCode = READ_IOCTL_COMMAND;
               IOCTLBlock.ReqHdr.Status      = 0;
               IOCTLBlock.Meddescr = 0;
               IOCTLBlock.Transfaddr = (unsigned long)&QInfoBlock;
               IOCTLBlock.Numbytes = 11;
               IOCTLBlock.Startsec = 0;
               IOCTLBlock.volID = 0;

               regs.x.ax = CDREQ_MP_INT;
               regs.x.bx = FP_OFF(&IOCTLBlock);
               regs.x.cx = CDRomDrive;
               sregs.es  = FP_SEG(&IOCTLBlock);
               int86x(MULTIPLEX_INT,&regs,&regs,&sregs);

               if(kbhit()){
                       getch();
                       Stop();
                       break;
               }
       } while (IOCTLBlock.ReqHdr.Status & STAT_BUSY);
}

Stop()
{
union REGS regs;
struct SREGS sregs;

       struct ReqHdrStruc StopBlock;

       StopBlock.ParamLength = 13;
       StopBlock.SubUnit     = 0;
       StopBlock.CommandCode = CD_STOP_AUDIO;
       StopBlock.Status      = 0;

       regs.x.ax = CDREQ_MP_INT;
       regs.x.bx = FP_OFF(&StopBlock);
       regs.x.cx = CDRomDrive;
       sregs.es  = FP_SEG(&StopBlock);

       int86x(MULTIPLEX_INT,&regs,&regs,&sregs);
       return(StopBlock.Status);
}

ReadLong(unsigned long SSec,unsigned int numsecs)
{
union REGS regs;
struct SREGS sregs;

       ReadLongBlock.ReqHdr.ParamLength = 13;
       ReadLongBlock.ReqHdr.SubUnit     = 0;
       ReadLongBlock.ReqHdr.CommandCode = CD_READ_LONG;
       ReadLongBlock.ReqHdr.Status      = 0;
       ReadLongBlock.AddressMode = ADDR_HSG;
       ReadLongBlock.Transfaddr = (int far*)buffer;
       ReadLongBlock.Numsec     = numsecs;
       ReadLongBlock.Startsec   = SSec;
       ReadLongBlock.Readmode   = RAW;
       ReadLongBlock.Interlsiz  = 0;
       ReadLongBlock.Interlskip = 0;

       regs.x.ax = CDREQ_MP_INT;
       regs.x.bx = FP_OFF(&ReadLongBlock);
       regs.x.cx = CDRomDrive;
       sregs.es  = FP_SEG(&ReadLongBlock);

       int86x(MULTIPLEX_INT,&regs,&regs,&sregs);

       return(ReadLongBlock.ReqHdr.Status);
}


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);
}


errexit()
{
       XMS_FreeEMB(xmshandle);
       exit(1);
}