/*
This code is based on cdda.c at cdda980108.diffs ,
xreadvcd.c ,Linux kernel  and tosha(1).
Written by Takanori Watanabe(takawata@shidahara1.planet.kobe-u.ac.jp)
******************************NOTICE**************************************
*Please don't reuse this code for other programs. This Code is very EVIL.*
*We're discussing much more nicer imprmentation than this. Wait for a    *
* while. Thanks.                                                         *
**************************************************************************
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/cdio.h>

#include <stdio.h>
#include <fcntl.h>
#if 0 
#define ATAPI /*if your CDROM Drive is ATAPI*/
#else
#define SCSI /* if your CDROM Drive is SCSI. And you need to 
	      * compile with following command line :
	      * cc rvcdnew.c -lscsi
	      */
#endif

#ifdef SCSI
#include <sys/scsiio.h>
#include <scsi.h>
#include <signal.h>
#endif
#define NB 1
#define	ATAPI_READ_CD		0xbe
#define ATAPI_READ_BIG          0x28
#define	CD_CTL_PREEMPHASIS	1
#define	CD_CTL_DIGITALCOPY	2
#define	CD_CTL_DATATRACK	4
#define	CD_CTL_FOURCHANNEL	8
#define FRAMESIZEMODE2 2328

struct atapires {
  u_char	code;
  u_char	status;
  u_char	error;
};
struct atapireq {
  u_char  cmd[16];
  caddr_t databuf;
  int     datalen;  
  struct	atapires result;
};
#define CDIOCATAPIREQ    _IOWR('c',100,struct atapireq)
int devfd;
typedef struct cdsector{
   char sync[12],
   header[4],
   subheader[8],
   data[2324],
   spare[4];
}cdsector_t;
#ifdef SCSI
struct scsireq *ar;
void get_cap (int fd)
{
  int result;
  char buf[512];

  scsireq_reset(ar);
  bzero(buf,sizeof(buf));
  ar->timeout =10000;
  
  scsireq_build (ar,12, buf,SCCMD_READ, "1A 0 1 0 C 0");
  
  if((result=scsireq_enter(fd,ar))){
    perror("scsireq_enter");
    exit(-1);
  }
#ifdef DEBUG
  scsi_debug(stderr,result,ar);
#endif
  
}
set_mode(int fd,int density,int size)
{
  char buf[256];
  int result;
  bzero(buf,sizeof(buf));
  buf[3] = 8;
  buf[4] = density;
  buf[10] = size >> 8;
  buf[11] = size & 0xff;
  scsireq_reset(ar);
  scsireq_build(ar,12,buf,SCCMD_READ,"15 10 0 0 C 0");
  
  if((result=scsireq_enter(fd,ar))){
    perror("scsireq_enter");
    exit(-1);
  }
#ifdef DEBUG
  scsi_debug(stderr,result,ar);
#endif
  
}

readsector(int fd,int lba,int format,caddr_t sector)
{
  int result;
  /*FOR TOSHIBA CDROM DRIVE ONLY. (I will not support others :-P)*/
  scsireq_reset(ar);
  ar->timeout=10000;
  scsireq_build(ar,2352,sector,SCCMD_READ,"v 0 0 v:i3 0 0 1 0 ",
		ATAPI_READ_BIG,lba);
  if((result=scsireq_enter(fd,ar))){
    perror("scsireq_enter");
    exit(-1);
  }
#ifdef DEBUG
  scsi_debug(stderr,result,ar);
#endif
  return 0;
}
void ending(int dummy)
{
  set_mode(devfd,0,2048);/*RESTORE MODE*/
  exit(0);
}
#endif/*SCSI*/

#ifdef ATAPI
readsector(int fd,int lba,int format,caddr_t sector)
{
  struct atapireq ar;
  bzero(&ar, sizeof ar);
  ar.cmd[0] = ATAPI_READ_CD;/*ATAPI Request Command*/
  ar.cmd[1] = format<<2; /* was 0, In this Command this means format to read
			  */
  ar.cmd[2] = lba >> 24;
  ar.cmd[3] = lba >> 16;
  ar.cmd[4] = lba >> 8;
  ar.cmd[5] = lba;/* These probably means start sector*/
  ar.cmd[6] = 0;
  ar.cmd[7] = 0;
  ar.cmd[8] = 1;/*Sectors to read*/
  ar.cmd[9] = 0xf8;/*Was 0x10(What this paramater means?)*/
  ar.databuf = sector;
  ar.datalen = 2352/*sizeof(buf)*/;
  if (ioctl(fd, CDIOCATAPIREQ, &ar) < 0) {
    perror("CDIOATAPIREQ");
    exit(-1);
  }
  
  /* assert(ar.datalen == 2352); */
#ifdef DEBUG
  fprintf(stderr,"%d %x %x %x\n",ar.datalen,(int)ar.result.code,(int)ar.result.status,(int)ar.result.error);
#endif
  return 0;
}
#endif /*ATAPI*/
main(int argc, char **argv)
{
  int fd,fdout;
  struct ioc_toc_header th;
  struct ioc_read_toc_entry te;
  struct cd_toc_entry *cte;
  char buf[NB*2352],buf2[NB*2352];
  u_long start, end, i;
  int track,format, ntracks;
  cdsector_t sector;
  if (argc < 3) {
    fprintf(stderr, "usage: %s track file \n", argv[0]);
#if 0
    fprintf(stderr, "format:0...raw\n");
    fprintf(stderr, "format:1...CDDA\n");
    fprintf(stderr, "format:2...MODE1\n");
    fprintf(stderr, "format:3...MODE2\n");
#endif
    exit(1);
	}
  track = atoi(argv[1]);
#ifdef ATAPI
  /*This file  must be hard coded.(I don't check type of device)*/
  fd = open("/dev/rwcd0c", O_RDONLY,0644);
#endif
#ifdef SCSI
  /*This file  must be hard coded.(I don't check type of device)*/
  fd= scsi_open("/dev/rcd0c",O_RDWR);
  signal(SIGHUP,ending);
  signal(SIGINT,ending);
  signal(SIGQUIT,ending);
  signal(SIGILL,ending);
  signal(SIGFPE,ending);
  signal(SIGBUS,ending);
  signal(SIGSEGV,ending);
  signal(SIGPIPE,ending);
  ar=scsireq_new();
  set_mode(fd,0,2048);
#endif
  devfd=fd;
  if (fd < 0) {
    perror("open");
    exit(-1);
  }
  if(*argv[2]=='-'){
    if(argv[2][1]=='-'){
      fprintf(stderr,"CDROM SECTOR SIZE Fixing\n");
      exit(0);
    }
    fdout=1;
  }
  else{
    fdout= open(argv[2],O_RDWR|O_CREAT,0644);
  }
  if(fdout < 0){
    perror("open");
    exit (-1);
  }	
  format=0;/*Format:RAW*/
  if (ioctl(fd, CDIOREADTOCHEADER, &th) < 0) {
    perror("CDIOREADTOCHEADER");
    exit(-1);
  }
  
  ntracks = th.ending_track - th.starting_track + 2;
  cte = (struct cd_toc_entry *)malloc(sizeof(*cte) * ntracks);
  
  te.address_format = CD_LBA_FORMAT;
  te.starting_track = 0;
  te.data_len = ntracks * sizeof(struct cd_toc_entry);
  te.data = cte;
  
  if (ioctl(fd, CDIOREADTOCENTRYS, &te) < 0) {
    perror("CDIOREADTOCENTRYS");
    exit(-1);
  }
  
  start = ntohl(cte[track-th.starting_track].addr.lba);
  end = ntohl(cte[track+1-th.starting_track].addr.lba);
  fprintf(stderr, "track %d of %d-%d: sector %d - %d\n",
	  track, th.starting_track, th.ending_track, start, end - 1);
  /*
   *    density= (size>2048) ? 0x81 : 0x83;AUDIO Sucking ... 0x82? 
   */
#ifdef SCSI
#ifdef TOSHIBA_VENDOR_SPECIFIC
  set_mode(fd,0x83,2352);
#else
  set_mode(fd,0,2352);
#endif
#endif
  for (i = start; i < end; i += 1 ) {
    if(readsector(fd,i,format,(caddr_t)&sector)<0){
      perror("readsector");
    }
    if ((sector.subheader[1]==1) &&
	(((sector.subheader[2]==0x62) 
	  &&(sector.subheader[3]==0x0f)) || ((sector.subheader[2]==0x64) &&
					     (sector.subheader[3]==0x7f)))){
      fprintf(stderr,"O");
      write(fdout,sector.data,2324);
    }
    else
      fprintf(stderr,"X");
  }
#ifdef SCSI
  ending(1);
#endif
  close(fd);
}






