#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define DEBUG 0
#define CHECK 0 /* don't bother checking bin for validity... */

//----------------Wave Stuff---------------------/
typedef unsigned char BYTE1 ;
typedef unsigned short int BYTE2 ;
typedef unsigned long int  BYTE4 ;

typedef struct wavHdr {
   BYTE1 riff[4];
   BYTE4 bytestoend;
   BYTE1 wavetxt[4];
   BYTE1 fmttxt[4];
   BYTE4 formatsize;  // 16 byte format specifier
   BYTE2 format;          // Windows PCM
   BYTE2 channels;             // 2 channels 
   BYTE4 samplerate;       // 44,100 Samples/sec
   BYTE4 avgbyterate;     // 176,400 Bytes/sec
   BYTE2 samplebytes;          // 4 bytes/sample
   BYTE2 channelbits;         // 16 bits/channel
   BYTE1 datatxt[4];
   BYTE4 blocksize;
} tWavHead;

#define HEADBYTES 36
#define WINDOWS_PCM 0x0001
//-------------------------------------------------/

/*  G L O B A L   D E F I N E S  */
#define byte    unsigned char
#define SIZERAW 2352
#define SIZEISO_MODE1 2048
#define SIZEISO_MODE2_RAW 2352
#define SIZEISO_MODE2_FORM1 2048
#define SIZEISO_MODE2_FORM2 2336
#define AUDIO 0
#define MODE1 1
#define MODE2 2
#define MODE1_2352 10
#define MODE2_2352 20
#define MODE1_2048 30
#define MODE2_2336 40

#define RAWDATA FF       // using this for leftover data when truncating for non-overburn

#define PROG_INTERVAL 1024
#define UNKNOWN -1
#define OFFSET 150                    
// got this from easycd pro by looking at a blank disk so it may be off...
#define CD74_MAX_SECTORS 334873 // 653.75 Mb 
         

unsigned long int Index(char m, char s, char f)
{
  unsigned long int temp;


  temp =  (((m>>4)*10) + (m&0xf)) * 60;
  temp = ( temp + (((s>>4)*10) + (s&0xf))) * 75;
  temp =  temp + (((f>>4)*10) + (f&0xf));
  
//  printf("\n%d%d %d%d %d%d = %06d", m>>4, m&f, s>>4, s&f, f>>4, f&f, temp);
  
  return temp;
}

void unIndex(unsigned long int index, char *ptr)
{
  char m, s, f;

  f = (char) (index % 75);
  s = (char) ((index/75) % 60);
  m = (char) (index/(75*60));
  sprintf(ptr, "%d%d:%d%d:%d%d", m/10, m%10, s/10, s%10, f/10, f%10);

}

// global variables
FILE* fdBinFile;
FILE* fdCueFile;
FILE* fdOutFile;
char sBinFilename[256];
char sOutFilename[256];

unsigned long int writepos = 0; // for inplace conversions...

   
#define OUTBUF_SIZE 4*1024*1024
#define INBUF_SIZE 4*1024*1024
unsigned char OUTBUF[OUTBUF_SIZE]; 
unsigned int OUTBUF_IDX = 0;
unsigned char INBUF[INBUF_SIZE]; 
unsigned int INBUF_RIDX = 0;
unsigned int INBUF_WIDX = 0;

int mode2to1 = 0;

typedef struct track
{
   unsigned short mode;
   unsigned long idx0;
   unsigned long idx1;
   unsigned char num[3];
   unsigned char name[80];
   unsigned long offset0;
   unsigned long offset1;
   unsigned long size; /* track size in bytes */
} tTrack;

buffered_fread(unsigned char *array, unsigned int size) {
   unsigned int i;
   
   if(INBUF_WIDX == 0) {    
      INBUF_WIDX += fread( INBUF, 1, (INBUF_SIZE/size)*size, fdBinFile );
   }
   if(INBUF_WIDX == 0) return 0; // read failed.
   
   for(i = 0; i< size; i++) 
   {

      array[i] = INBUF[INBUF_RIDX++];
      if((INBUF_RIDX == INBUF_WIDX) && (i < (size -1))) {
         printf("   Warning: Premature EOF\n");
         while(i++ < size) { array[i] == 0; }/* zero fill the rest */
         break;
      }
   }

   if(INBUF_RIDX == INBUF_WIDX) {
      INBUF_RIDX = 0;   
      INBUF_WIDX = 0;   
   }


   return 1; // read passed
     
}

void buffered_fwrite(unsigned char *array, unsigned int size) {
   unsigned int idx;
   unsigned long int readpos;
     
   if(OUTBUF_IDX+size >= OUTBUF_SIZE) {     

      if(fdOutFile == fdBinFile) {
         readpos = ftell(fdOutFile);
         if(0 != fseek(fdOutFile, writepos, SEEK_SET)) { 
            perror("\nbin2iso(fseek)"); exit(1);
         }         
      }

      //      printf("\nWriting            \n");
      if( 1 != fwrite( OUTBUF, OUTBUF_IDX, 1, fdOutFile )) {
         perror("\nbin2iso(fwrite)");
         fclose(fdOutFile);
         // remove(sOutFilename);
         exit(1);
      }
      if( 1 != fwrite( array, size, 1, fdOutFile )) {
         perror("\nbin2iso(fwrite)");
         fclose(fdOutFile);
         // remove(sOutFilename);
         exit(1);
      }
//      printf("\nWrote %d bytes            \n", OUTBUF_IDX+size);
      OUTBUF_IDX = 0;

      if(fdOutFile == fdBinFile) {
         writepos = ftell(fdOutFile);
         if(0 != fseek(fdOutFile, readpos, SEEK_SET)) { 
            perror("\nbin2iso(fseek)"); exit(1);
         }         
     }


   } else {
      for(idx = 0; idx < size; idx++) {
         OUTBUF[OUTBUF_IDX + idx] = array[idx];
      }
      OUTBUF_IDX+=size;
   }
     
}


void flush_buffers(void)
{
   unsigned long int readpos;

   if(fdOutFile == fdBinFile) {
      readpos = ftell(fdOutFile);
      if(0 != fseek(fdOutFile, writepos, SEEK_SET)) { 
         perror("\nbin2iso(fseek)"); exit(1);
      }         
   }

   if( 1 != fwrite( OUTBUF, OUTBUF_IDX, 1, fdOutFile )) {
      perror("\nbin2iso(fwrite)");
      fclose(fdOutFile);
      // remove(sOutFilename);
      exit(1);
   }

//   printf("\nWrote %d bytes          \n", OUTBUF_IDX);
   OUTBUF_IDX = 0;
   INBUF_RIDX = 0;
   INBUF_WIDX = 0;

   if(fdOutFile == fdBinFile) {
      writepos = ftell(fdOutFile);
      if(0 != fseek(fdOutFile, readpos, SEEK_SET)) { 
         perror("\nbin2iso(fseek)"); exit(1);
      }         
   }


}



// presumes Line is preloaded with the "current" line of the file
int getTrackinfo(char *Line, tTrack *track)
{
//   char tnum[3];
   char inum[3];
   char min;
   char sec;
   char block;
   
   track->idx0 = -1;
   track->idx1 = -1;
   
   // Get the 'mode'
   if (strncmp(&Line[2], "TRACK ", 6)==0) 
   {
      strncpy(track->num, &Line[8], 2); track->num[2] = '\0';

      track->mode = UNKNOWN;
      if(strncmp(&Line[11], "AUDIO", 5)==0) track->mode = AUDIO;
      if(strncmp(&Line[11], "MODE1/2352", 10)==0) track->mode = MODE1_2352;
      if(strncmp(&Line[11], "MODE1/2048", 10)==0) track->mode = MODE1_2048;
      if(strncmp(&Line[11], "MODE2/2352", 10)==0) track->mode = MODE2_2352;
      if(strncmp(&Line[11], "MODE2/2336", 10)==0) track->mode = MODE2_2336;
   }
   else return(1);
   
   // Set the name
   strcpy(track->name, sBinFilename);
   track->name[strlen(sBinFilename)-4] = '\0';
   strcat(track->name, "-");
   strcat(track->name, track->num);

   if( (track->mode == MODE1_2352) || 
       (track->mode == MODE1_2048) || 
       (track->mode == MODE2_2352) || 
       (track->mode == MODE2_2336)    )
   {
      strcat(track->name, ".iso");
   } else if(track->mode == AUDIO) {
      strcat(track->name, ".wav");
   } else {
      printf("Track %d Unsupported mode\n", track->num);
      return(1);
   }

   // Get the track indexes
   while(1) {
      if(! fgets( Line, 256, fdCueFile ) ) { break; }

      if (strncmp(&Line[2], "TRACK ", 6)==0) 
      {  
         break; // next track starting
      }

      if (strncmp(&Line[4], "INDEX ", 6)==0) 
      {
         strncpy(inum, &Line[10], 2); inum[2] = '\0';
         min = ((Line[13]-'0')<<4) | Line[14]-'0';
         sec = ((Line[16]-'0')<<4) | Line[17]-'0';
         block = ((Line[19]-'0')<<4) | Line[20]-'0';
         
         
         if(strcmp(inum, "00")==0) track->idx0 = Index(min, sec, block);
         else if(strcmp(inum, "01")==0) track->idx1 = Index(min, sec, block);
         else { printf("Unexpected Index number: %s\n", inum); exit(1); } 
           
      }
      else if (strncmp(&Line[4], "PREGAP ", 7)==0) { ; /* ignore, handled below */ }
      else if (strncmp(&Line[4], "FLAGS ", 6)==0)  { ; /* ignore */ }
      else { printf("Unexpected cuefile line: %s\n", Line); }
   }
   if(track->idx0 == -1) track->idx0 = track->idx1;
   if(track->idx1 == -1) track->idx1 = track->idx0;
   return(0);
} 


void dotrack(short mode, long preidx, long startidx, long endidx, unsigned long offset) 
{
   unsigned char buf[SIZERAW+100];
   unsigned long blockswritten = 0;
   unsigned int uiLastIndex;
#if CHECK
   unsigned int uiCurrentIndex;
#endif
   unsigned int write = 1;
   
   tWavHead wavhead = { "RIFF", 
                             0,      
                        "WAVE", 
                        "fmt ", 
                            16,       // 16 byte format specifier
                   WINDOWS_PCM,       // format
                             2,       // 2 Channels 
                         44100,       // 44,100 Samples/sec    
                        176400,       // 176,400 Bytes/sec
                             4,       // 4 bytes/sample
                            16,       // 16 bits/channel
                        "data",  
                             0 };
                             
   
   uiLastIndex = startidx-1;
   // Input -- process -- Output 
   if(startidx != 0) printf("\nNote: PreGap = %d frames\n", startidx-preidx);
   else printf("\nNote: PreGap = %d frames\n", OFFSET); // cd standard: starting offset
                                                       // - of course this isn't true for bootable cd's...

   if(sOutFilename[0] != '\0') {
      printf("Creating %s (%06d,%06d) ", sOutFilename, startidx, endidx-1);
   } else {
      printf("Converting (%06d,%06d) ", startidx, endidx-1);
   }
   switch(mode)
   {
      case AUDIO:
         printf("Audio");
         break;            
      case MODE1_2352:
         printf("Mode1/2048");
         break;
      case MODE2_2336:
         printf("Mode2/2352");
         break;
      case MODE2_2352:
         if(mode2to1 != 1) 
            printf("Mode2/2352");
         else 
            printf("Mode1/2048");
         break;
      case MODE1_2048:
         printf("Mode1/2048");
         break;
      default:
           printf("Huh? What's going on?");
           exit(1);
   }
   printf(" :       ");
   
   if(sOutFilename[0] != '\0') {
      if(NULL == (fdOutFile = fopen (sOutFilename, "wb"))) {
         perror("bin2iso(fopen)");
      }
// printf("\nOpened File %s: %d\n", sOutFilename, fdOutFile);

   } else {
      fdOutFile = fdBinFile;
   }
   if (fdOutFile == NULL)   { printf ("    Unable to create %s\n", sOutFilename); exit (1); }
   
   if(0 != fseek(fdBinFile, offset, SEEK_SET)) { 
      perror("\nbin2iso(fseek)"); exit(1);
   }         

#if (DEBUG == 0)
   if(mode == AUDIO) {
      if( 1 != fwrite( &wavhead, sizeof(wavhead), 1, fdOutFile ) ) { // write placeholder
         perror("\nbin2iso(fwrite)");
         fclose(fdOutFile);
         // remove(sOutFilename);
         exit(1);
      }
   }
#endif 
         
   memset( &buf[0], '\0', sizeof( buf ) );
   if(mode == MODE2_2336) {
      unsigned int M = 0, S = 2, F = 0;
      while( buffered_fread( &buf[16], SIZEISO_MODE2_FORM2) ) {
         //setup headed area (probably not necessary though...
         //buf[0] = 0;
         memset( &buf[1], 0xFF, sizeof(buf[0])*10 );
         //buf[11] = 0;
         buf[12] = M;
         buf[13] = S;
         buf[14] = F;
         buf[15] = MODE2;
         
         if((++F&0xF) == 0xA) F += 6;

         if(F == 0x75) { S++; F = 0; } 
         if((S&0xF) == 0xA) S += 6;
  
         if(S == 0x60) { M++; S = 0; }
         if((M&0xF) == 0xA) M += 6;
//         printf("\n%x:%x:%x", M, S, F);
         
         buffered_fwrite( buf, SIZERAW );   
         uiLastIndex++;
         memset( &buf[0], '\0', sizeof( buf ) );
         if (startidx%PROG_INTERVAL == 0) { printf("\b\b\b\b\b\b%06d", startidx); }
         if (++startidx == endidx) { printf("\b\b\b\b\b\bComplete\n"); break; }
      }
   } else if (mode == MODE1_2048) {
      while( buffered_fread( buf, SIZEISO_MODE1) ) {         
         buffered_fwrite( buf, SIZEISO_MODE1 );   
         uiLastIndex++;
         if (startidx%PROG_INTERVAL == 0) { printf("\b\b\b\b\b\b%06d", startidx); }
         if (++startidx == endidx) { printf("\b\b\b\b\b\bComplete\n"); break; }
      }
   } else {
      while( buffered_fread( buf, SIZERAW) ) {
         switch(mode) {
            case AUDIO:
#if (DEBUG == 0)
               buffered_fwrite( buf, SIZERAW );
#endif        
               uiLastIndex++;
               blockswritten++;
               break;
            case MODE1_2352:
               // should put a crc check in here...
#if CHECK
               if( buf[15] != MODE1) 
               { 
                  printf("\nWarning: Mode Error in bin file!\n"); 
                  printf("   %02x:%02x:%02x : mode %02x\n", buf[12], buf[13], buf[14], buf[15] ); 
                  //exit(1);
               }
             
               uiCurrentIndex = Index(buf[12], buf[13], buf[14]) - OFFSET;
              
               if(uiCurrentIndex != uiLastIndex+1)
               { 
                  printf("\nWarning: Frame Error in bin file!\n"); 
                  printf("Last      %02d:%02d:%02d (%d)\n", ((uiLastIndex+OFFSET)/75)/60, ((uiLastIndex+OFFSET)/75)%60, (uiLastIndex+OFFSET)%75, uiLastIndex ); 
                  printf("Current   %02x:%02x:%02x (%d)\n", buf[12], buf[13], buf[14], uiCurrentIndex ); 
                  printf("Expecting %02d:%02d:%02d (%d)\n", ((uiLastIndex+OFFSET+1)/75)/60, ((uiLastIndex+OFFSET+1)/75)%60, (uiLastIndex+OFFSET+1)%75, uiLastIndex+1 ); 
                
               }
#endif
#if (DEBUG == 0)
               buffered_fwrite( &buf[16], SIZEISO_MODE1 );
#endif
#if CHECK
               uiLastIndex = uiCurrentIndex;
#endif
               break;
            case MODE2_2352:
#if CHECK
               if( (buf[15]&0xf) != MODE2) 
               { 
                  printf("\nWarning: Mode Error in bin file!\n"); 
                  printf("   %02x:%02x:%02x : mode %02x\n", buf[12], buf[13], buf[14], buf[15] ); 
                  //exit(1);
               }

               uiCurrentIndex = Index(buf[12], buf[13], buf[14]) - OFFSET;

               if(uiCurrentIndex != uiLastIndex+1)
               { 
                  printf("\nWarning: Frame Error in bin file!\n"); 
                  printf("Last      %02d:%02d:%02d (%d)\n", ((uiLastIndex+OFFSET)/75)/60, ((uiLastIndex+OFFSET)/75)%60, (uiLastIndex+OFFSET)%75, uiLastIndex ); 
                  printf("Current   %02x:%02x:%02x (%d)\n", buf[12], buf[13], buf[14], uiCurrentIndex ); 
                  printf("Expecting %02d:%02d:%02d (%d)\n", ((uiLastIndex+OFFSET+1)/75)/60, ((uiLastIndex+OFFSET+1)/75)%60, (uiLastIndex+OFFSET+1)%75, uiLastIndex+1 ); 
               }
#endif
#if (DEBUG == 0)
               if(mode2to1) buffered_fwrite( &buf[16+8], SIZEISO_MODE1 );
               else if(write) buffered_fwrite( &buf[0], SIZEISO_MODE2_RAW );
#endif
#if CHECK
               uiLastIndex = uiCurrentIndex;
#endif
               break;
            default:
               printf("Unkown Mode\n"); exit(1);
               break;
         }         
            
         memset( &buf[0], '\0', sizeof( buf ) );
         if (startidx%PROG_INTERVAL == 0) { printf("\b\b\b\b\b\b%06d", startidx); }
         if (++startidx == endidx) { printf("\b\b\b\b\b\bComplete\n"); break; }
      }
   }
   flush_buffers(); // flushes write buffer
                    // and clears read buffer.
   if(mode == AUDIO) {
      wavhead.blocksize = blockswritten*SIZERAW;
      wavhead.bytestoend = wavhead.blocksize + HEADBYTES;
      // rewind to the beginning
      if(0 != fseek(fdOutFile, 0, SEEK_SET)) { 
         perror("\nbin2iso(fseek)"); exit(1);
      }         

#if (DEBUG == 0)
      fwrite( &wavhead, sizeof(wavhead), 1, fdOutFile );
#endif
   }      
   fclose(fdOutFile);
}


void doCueFile(void) {
   int track = 1;
   unsigned long int binIndex = 0;
   unsigned long int trackIndex = 0;
   const int gapThreshold = 20; // look for 0.266 sec gap
   const int valueThreshold = 800; // look for samples < 700
   int count = 0;
   int i, blank;
   int gapon = 0;
   short value;
      
   char mode[12] = "AUDIO";
   char index0[9] = "00:00:00";
   char index1[9] = "00:00:00";
   unsigned char buf[SIZERAW+100];
    
   printf(            "FILE %s BINARY\n", sBinFilename);
   fprintf(fdCueFile, "FILE %s BINARY\n", sBinFilename);
   memset( buf, '\0', sizeof( buf ) );
   while( fread( buf, 1, SIZERAW, fdBinFile ) ) {
      if(trackIndex == 0) {
         if ( (buf[0] == 0x00) &&
              (buf[1] == 0xFF) &&
              (buf[2] == 0xFF) &&
              (buf[3] == 0xFF) &&
              (buf[4] == 0xFF) &&
              (buf[5] == 0xFF) &&
              (buf[6] == 0xFF) &&
              (buf[7] == 0xFF) &&
              (buf[8] == 0xFF) &&
              (buf[9] == 0xFF) &&
              (buf[10] == 0xFF) &&
              (buf[11] == 0x00) 
            ) {
            sprintf(mode, "MODE%d/2352", buf[15]);
         } else { 
            sprintf(mode, "AUDIO"); 
         }
      } 
      if(binIndex == 0) {
         printf(            "  TRACK %02d %s\n", track, mode);
         fprintf(fdCueFile, "  TRACK %02d %s\n", track, mode);
         printf(            "    INDEX 01 %s\n", index0);
         fprintf(fdCueFile, "    INDEX 01 %s\n", index0);
      }
      blank = 1;
      for(i = 0; i < SIZERAW; i+=2) {
         value = buf[i+1];
         value = ((value << 8) | buf[i]);
//         printf("%f %i\n",(1.0/75)*binIndex, value);
         if(abs(value) > valueThreshold) {
            blank = 0;
            break;
         }
      }
//      if(i == SIZERAW) printf("%f ~blank~\n", (1.0/75)*binIndex);
      if(blank == 1) count++;
      else if (gapon == 1) {
         gapon = 0; 
         unIndex(binIndex-count, index0);
         count = 0;
         unIndex(binIndex, index1);
         printf(            "  TRACK %02d %s\n", track, mode);
         fprintf(fdCueFile, "  TRACK %02d %s\n", track, mode);
         printf(            "    INDEX 00 %s\n", index0);
         fprintf(fdCueFile, "    INDEX 00 %s\n", index0);
         printf(            "    INDEX 01 %s\n", index1);
         fprintf(fdCueFile, "    INDEX 01 %s\n", index1);
      }
      
      if((count > gapThreshold) && (gapon == 0)) {
         gapon = 1; track++;
         trackIndex = -1;
      } 
      
      memset( buf, '\0', sizeof( buf ) );
      binIndex++;      
      trackIndex++;
   }
}

// return 0 to when no data found, 1 when there is.
int checkGaps(FILE *fdBinFile, tTrack tracks[], int nTracks) {
   int i, k;
   unsigned long int j;
   unsigned char buf[SIZERAW];
   int c = 0;
   int writegap = 0;
   short value;
   int count;

   if(nTracks == 2) { return 0; }; // don't need to bother with single track images

   printf("Checking gap data:\n");

   for (i = 0; i < nTracks; i++) {
      if((tracks[i].offset0 != tracks[i].offset1) && (tracks[i-1].mode == AUDIO)) {
         if(0 != fseek(fdBinFile, tracks[i].offset0, SEEK_SET)) { 
            perror("\nbin2iso(fseek)"); exit(1);
         }
         count = 0;
         for(j = tracks[i].idx0; j < tracks[i].idx1; j++) {
            if(0 == fread( buf, SIZERAW, 1, fdBinFile ) ) {
               perror("bin2iso(fread)");
               exit(1);
            }
            for(k = 0; k < SIZERAW; k+=2) {
               value = buf[k+1];
               value = ((value << 8) | buf[k]);
               if(value != 0) { 
                  count++;

                // printf("%10d: %2x\n", count ,value );
               }
            }
         }
         if(count != 0) {
            printf("   Track%02d - %d values of Non-Zero gap data encountered\n", i-1, count);
            if((count > SIZERAW/2/2) && (writegap == 0)) {
               printf("   -->Threashold reached\n"); writegap = 1;
            }
         }
      }
   }
   return writegap; 
}   

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ */

int   main(int argc, char **argv) {
   unsigned long int count = 0;
//   int printon = 0;

   char sLine[256];
   int i,j,q;
   
//   int writegap = -1;   // auto detect pregap data action by default. 
   int writegap = 1;   // keep pregap data by default. 
   int no_overburn = 0; 
   int createCue = 0; 
   char sTrack[3] = "00"; 
   int doOneTrack = 0;
   int doInPlace = 0;
         
   tTrack trackA;
   tTrack trackB;
   
   tTrack tracks[100];
   int nTracks = 0;
   
   char sOutdir[192];

   sOutFilename[0] = '\0';

   /* Tell them what I am. */
   printf ("\n%s, %s", __DATE__, __TIME__);
   printf ("\nbin2iso V1.9b - Converts RAW format (.bin) files to ISO/WAV format"); 
   printf ("\n               Bob Doiron, ICQ#280251                     \n");
   printf ("\nCheck for updates at http://users.andara.com/~doiron\n\n");
   if(argc < 2) {
      printf("Usage: bin2iso <cuefile> [<output dir>] [-[a]wg] [-t XX] [-i] [-nob]\n");
      printf("or   : bin2iso <cuefile> -c <binfile>\n");
      printf("\n");
      printf("Where:\n");
      printf("   <cuefile>    - the .cue file that belongs to the .bin file to \n");
      printf("                  be converted\n");
      printf("   <output dir> - the output directory (defaults to current dir) \n");
      printf("   -nwg         - indicates that audio data found in the track   \n");
      printf("                  'gaps' shouldn't be appended to the audio tracks\n");
      printf("   -awg         - looks for non-zero data in the 'gaps', if found\n");
      printf("                  then gaps are appended to audio tracks. Looks  \n");
      printf("                  for more than 1/2 of a sector of non-zero values\n");
      printf("                  (%d values),                                   \n", SIZERAW/2/2);
      printf("   -t XX        - Extracts the XX'th track.                      \n");
      printf("   -i           - Performs the conversion 'in place'. Meaning it \n");
      printf("                  truncates the binfile after each track is      \n");
      printf("                  created to minimize diskspace requirements.    \n");
      printf("                  [not valid with -t]                            \n");
      printf("   -nob         - Doesn't use overburn data past %ld sectors.    \n", CD74_MAX_SECTORS);
      printf("                  This of course presumes that the data is not   \n");
      printf("                  useful.                                        \n");
      printf("   -c           - Attempts to create a <cuefile> from an existing\n");
      printf("                  <binfile>                                      \n");
      exit (1);
   }

   strcpy(sOutdir, "./"); // default path

   printf("\n");
   for (i=2; i < argc; i++) {
      if (argv[i][0] == '-') {
         /* if (strncmp(&(argv[i][1]), "wg", 2)==0) {
            writegap = 1; 
         } else */ 
         
         if (strncmp(&(argv[i][1]), "awg", 3)==0) {
            writegap = -1; 
            printf("Note: Auto-detecting pregap data\n");         
         } else if (strncmp(&(argv[i][1]), "nwg", 3)==0) {
            writegap = 0;          
         } else if (strncmp(&(argv[i][1]), "m2to1", 5)==0) {
            mode2to1 = 1;
            printf("Note: Converting Mode2 ISO to Mode1\n");
         } else if (strncmp(&(argv[i][1]), "t", 1)==0) {
            strcpy(sTrack, argv[i+1]);
            doOneTrack = 1;
            i++;
         } else if (strncmp(&(argv[i][1]), "i", 1)==0) {
            if(doOneTrack == 1) { printf("Invalid combination of options...\n"); exit(1); }
            printf("Bin file will be truncated after each track created\n");
            doInPlace = 1;
         } else if (strncmp(&(argv[i][1]), "c", 1)==0) {
            createCue = 1;
            strcpy(sBinFilename, argv[i+1]);
            i++;
         } else if (strncmp(&(argv[i][1]), "nob", 3)==0) {
            no_overburn = 1;
         }
      } else {
         strcpy(sOutdir, argv[2]);
      }
   }
   
   if(createCue == 1) {
      fdBinFile = fopen (sBinFilename, "rb");
      if (fdBinFile == NULL) {
         printf ("Unable to open %s\n", sBinFilename);
         exit (1);
      } 
      fdCueFile = fopen (argv[1], "w");
      if (fdCueFile == NULL) {
         printf ("Unable to create %s\n", argv[1]);
         exit (1);
      } 

      if((strcmp(&sBinFilename[strlen(sBinFilename)-4], ".wav")==0) ||
         (strcmp(&sBinFilename[strlen(sBinFilename)-4], ".WAV")==0) ) {
         printf(".wav binfile - Skipping wav header\n");
         fread( sLine, 1, sizeof(tWavHead), fdBinFile );
      }

      doCueFile();

   } else {   
      fdCueFile = fopen (argv[1], "r");      
      if (fdCueFile == NULL) {
         printf ("Unable to open %s\n", argv[1]);
         exit (1);
      } 

      // get bin filename from cuefile... why? why not.
      if(! fgets( sLine, 256, fdCueFile ) ) {
         printf ("Error Reading Cuefile\n");
         exit (1);
      }
      if (strncmp(sLine, "FILE ", 5)==0) {
         i = 0;
         j = 0;
         q = 0; // track open and closed quotes
         do { 
            sBinFilename[j] = sLine[5+i]; 
            i++;
            j++;
            if ((sBinFilename[j-1] == '\\') || (sBinFilename[j-1] == '/')) { j = 0; } //strip out path info
            if (sBinFilename[j-1] == '"') { j--; q++;} // strip out quotes
         } while ((sLine[5+i-1] != ' ') || (q == 1));
         sBinFilename[j] = '\0';
         //bug?? Why did a trailing space show up??
         while(sBinFilename[--j] == ' ') sBinFilename[j] = '\0';

// do not need to convert to lower case on unix system
//         strlwr(sBinFilename);

      } else {
         printf ("Error: Filename not found on first line of cuefile.\n", argv[1]);
         exit (1);
      }
   
      // Open the bin file
      if(doInPlace == 1) {
         fdBinFile = fopen (sBinFilename, "rb+");
      } else {
         fdBinFile = fopen (sBinFilename, "rb");
      }
      if (fdBinFile == NULL) {
         printf ("Unable to open %s\n", sBinFilename);
         perror("\nbin2iso(fopen)");
         exit(1);
      }

      // Get next line
      if(! fgets( sLine, 256, fdCueFile ) ) {
         printf ("Error Reading Cuefile\n");
         exit (1);
      } 
   
      if(strlen(sOutdir) > 0) {
         if((sOutdir[strlen(sOutdir)-1] != '/' ) && (sOutdir[strlen(sOutdir)-1] != ':' ) ) {
            strcat(sOutdir, "/");
         }
      }
   
      while(!feof(fdCueFile)) {
         getTrackinfo(sLine, &tracks[nTracks++]);
      }
      tracks[nTracks].idx0 = tracks[nTracks].idx1 = -1;

      switch (tracks[0].mode) {
         case MODE1_2048:
            tracks[0].offset0 = tracks[0].idx0*SIZEISO_MODE1;
            break;
         case MODE2_2336:
            tracks[0].offset0 = tracks[0].idx0*SIZEISO_MODE2_FORM2;
            break;
         default:  // AUDIO, MODE1_2352, MODE2_2352:
            tracks[0].offset0 = tracks[0].idx0*SIZERAW;
            break;
      }               
      /* set offsets */

      
      if(0 != fseek(fdBinFile, 0, SEEK_END)) { 
         perror("\nbin2iso(fseek)"); exit(1);
      }

      tracks[nTracks].offset0 = tracks[nTracks].offset1 = ftell(fdBinFile);

      for(i = 0; i < nTracks; i++) {
         switch (tracks[i].mode) {
            case MODE1_2048:
               tracks[i].offset1 = tracks[i].offset0   + (tracks[i].idx1-tracks[i].idx0)*SIZEISO_MODE1;
               if(tracks[i+1].idx0 != -1)
                  tracks[i+1].offset0 = tracks[i].offset1 + (tracks[i+1].idx0 - tracks[i].idx1)*SIZEISO_MODE1;
               else {
                  tracks[i+1].idx0 = tracks[i+1].idx1 = (tracks[i+1].offset0 - tracks[i].offset1)/SIZEISO_MODE1 + tracks[i].idx1;
                  if(((tracks[i+1].offset0 - tracks[i].offset1)%SIZEISO_MODE1) != 0) printf("Warning: Bin file has invalid byte count for cuefile.\n");
               }
               break;
            case MODE2_2336:
               tracks[i].offset1 = tracks[i].offset0   + (tracks[i].idx1-tracks[i].idx0)*SIZEISO_MODE2_FORM2;
               if(tracks[i+1].idx0 != -1)
                  tracks[i+1].offset0 = tracks[i].offset1 + (tracks[i+1].idx0 - tracks[i].idx1)*SIZEISO_MODE2_FORM2;
               else {
                  tracks[i+1].idx0 = tracks[i+1].idx1 = (tracks[i+1].offset0 - tracks[i].offset1)/SIZEISO_MODE2_FORM2 + tracks[i].idx1;
                  if(((tracks[i+1].offset0 - tracks[i].offset1)%SIZEISO_MODE2_FORM2) != 0) printf("Warning: Bin file has invalid byte count for cuefile.\n");
               }
               break;
            default:  // AUDIO, MODE1_2352, MODE2_2352:
               tracks[i].offset1 = tracks[i].offset0   + (tracks[i].idx1-tracks[i].idx0)*SIZERAW;
               if(tracks[i+1].idx0 != -1)
                  tracks[i+1].offset0 = tracks[i].offset1 + (tracks[i+1].idx0 - tracks[i].idx1)*SIZERAW;
               else {
                  tracks[i+1].idx0 = tracks[i+1].idx1 = (tracks[i+1].offset0 - tracks[i].offset1)/SIZERAW + tracks[i].idx1;
                  if(((tracks[i+1].offset0 - tracks[i].offset1)%SIZERAW) != 0) printf("Warning: Bin file has invalid byte count for cuefile.\n");
               }
               break;
         }
      }

      // if not allowing overburn, then create a new track to hold extra data...
      if(no_overburn == 1) {
         i = nTracks;
         if(tracks[i].idx0 > CD74_MAX_SECTORS) {
            tracks[i+1] = tracks[nTracks];
            strcpy(tracks[i].name, "obdatatemp.bin");
            tracks[i].idx0 = CD74_MAX_SECTORS;
            tracks[i].idx1 = CD74_MAX_SECTORS;
            switch (tracks[i-1].mode) {
               case MODE1_2048:
                  tracks[i].offset0 = tracks[i-1].offset1 + (tracks[i].idx0 - tracks[i-1].idx1)*SIZEISO_MODE1;
                  break;
               case MODE2_2336:
                  tracks[i].offset0 = tracks[i-1].offset1 + (tracks[i].idx0 - tracks[i-1].idx1)*SIZEISO_MODE2_FORM2;
                  break;
               default:  // AUDIO, MODE1_2352, MODE2_2352:
                  tracks[i].offset0 = tracks[i-1].offset1 + (tracks[i].idx0 - tracks[i-1].idx1)*SIZERAW;
                  break;
            }
            tracks[i].offset1 = tracks[i].offset0;
            tracks[i].mode = tracks[i-1].mode;
            nTracks++;
         }
      }

      
      /* set sizes */
      for(i = 0; i < nTracks; i++) {
         switch (tracks[i].mode) {
            case MODE1_2352:
               tracks[i].size = ((tracks[i+1].offset1 - tracks[i].offset1) / SIZERAW ) * SIZEISO_MODE1;
               break;
            case MODE2_2336:
               tracks[i].size = ((tracks[i+1].offset1 - tracks[i].offset1) / SIZEISO_MODE2_FORM2 ) * SIZERAW;
               break;
            default: // MODE1_2048, MODE2_2352, AUDIO
              tracks[i].size = tracks[i+1].offset1 - tracks[i].offset1;
              break;
         }
      }

      if(writegap == -1)  { writegap = checkGaps(fdBinFile, tracks, nTracks); }

      if(writegap == 1) 
         printf("Note: Appending pregap data to end of audio tracks\n");
      else 
         printf("Note: Discarding pregap data\n");

      printf("\n");
      for(i = 0; i <= nTracks-1; i++) {
         printf("%s (%3d Mb) - sectors %06ld:%06ld (offset %09ld:%09ld)\n", 
            tracks[i].name, 
            tracks[i].size/(1024*1024), 
            tracks[i].idx1, 
            ( ((writegap == 0) || (tracks[i].mode != AUDIO)) ? tracks[i+1].idx0 : tracks[i+1].idx1)-1, 
            tracks[i].offset1, 
            ( ((writegap == 0) || (tracks[i].mode != AUDIO)) ? tracks[i+1].offset0 : tracks[i+1].offset1)-1 
         );
      }
      printf("\n");

      if( (((mode2to1 != 1) && (tracks[0].mode == MODE2_2352)) || (tracks[0].mode == MODE1_2048)) && (nTracks == 1) ) {
         if(tracks[0].mode == MODE2_2352) { printf("Mode2/2352"); }
         if(tracks[0].mode == MODE1_2048) { printf("Mode1/2048"); }
         printf(" single track bin file indicated by cue file\n");   
         fclose(fdBinFile);
         if( 0 != rename(sBinFilename, tracks[0].name) ) {
            perror("\nbin2iso(rename)");
            exit(1);
         }
         printf("%s renamed to %s\n", sBinFilename, tracks[0].name);
         fclose(fdCueFile);
         return(0);
      }

      for(i=nTracks-1; i>=0; i--) {
         trackA = tracks[i];         
         trackB = tracks[i+1];
         // audio can't be done in the bin file due to header.
         // 2336 can't either because it's expanded to 2352
         if((doInPlace == 1) && (i == 0) && (trackA.mode != AUDIO) && (trackA.mode != MODE2_2336) ) {
            sOutFilename[0] = '\0';
         } else {
            strcpy(sOutFilename, sOutdir); 
            strcat(sOutFilename, trackA.name);
         }
         if ( ((doOneTrack == 1) && strcmp(trackA.num, sTrack)==0) || (doOneTrack == 0) ) {

            if(!((i == 0) && ((trackA.mode == MODE2_2352)||(trackA.mode == MODE1_2048)) && (doInPlace == 1) )){
               if (!writegap || (trackA.mode != AUDIO)) { // when not Audio, don't append.
                  dotrack(trackA.mode, trackA.idx0, trackA.idx1, trackB.idx0, trackA.offset1);
               } else {
                  /* if(trackA.idx0 == 0) // handles first track with pregap.
                     dotrack(trackA.mode,           0, trackA.idx1, trackB.idx1, trackA.offset1);
                  else
                  */
                  dotrack(trackA.mode, trackA.idx1, trackA.idx1, trackB.idx1, trackA.offset1);
               }
            }
         } /*else {
            fclose(fdBinFile); // just close bin file. Already MODE1_2048 or MODE2_2352
         }*/
         if( (doOneTrack == 0) && (doInPlace == 1) ) {
            if( (i != 0) || ( (i == 0) && ((trackA.mode == AUDIO)||(trackA.mode == MODE2_2336)) ) ) {
               printf("Truncating bin file to %ld bytes\n", trackA.offset1);
               if( -1 == ftruncate(fileno(fdBinFile), trackA.offset1) ) {
                  perror("\nbin2iso(_chsize)");
                  exit(1);
               }
            } else {
               printf("Renaming %s to %s\n", sBinFilename, trackA.name);
               fclose(fdBinFile);
               if( 0 != rename(sBinFilename, trackA.name) ) {
                  perror("\nbin2iso(rename)");
                  exit(1);
               }
            
               // fix writepos for case when simply truncating...
               if((trackA.mode == MODE2_2352) || (trackA.mode == MODE1_2048)){ writepos = trackB.offset0; }

               printf("Truncating to %ld bytes\n", writepos);
            
               fdBinFile = fopen(trackA.name, "rb+"); // gets closed in doTrack...
               if(fdBinFile == NULL) { perror("bin2iso(fopen)"); exit(1); }

               if( -1 == ftruncate(fileno(fdBinFile), writepos) ) {
                  perror("\nbin2iso(_chsize)");
                  exit(1);
               }
            }
         }
      }   
   }
   fclose(fdCueFile);
   fclose(fdBinFile);
   return(0);  
}



