/* Warning! This C source contains extended characters! */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <conio.h>
#include <bios.h>
#include <dir.h>
#include <musique.h>
#include <menu.e>

int getmode(void)
/* get the current video mode since Turbo-C may be out of sync */
{
  union REGS regs;

  regs.h.ah=(unsigned)'\xF';
  int86(0x10,&regs,&regs);
  return((int)regs.h.al);
}

int getcolumn(void)
/* get the current number of column since Turbo-C may be out of sync */
{
  union REGS regs;

  regs.h.ah=(unsigned)'\xF';
  int86(0x10,&regs,&regs);
  return((int)regs.h.ah);
}

void findports(PARAMETERTYPE *parameter, int verbose)
/* plays STEREO iff two parallel ports found */
/* plays SINGLE iff one parallel port found */
/* plays SPEAKER iff no parallel port found */
/* shuts off initially any parallel port to be used */
{
  if (parameter->A)
    if (parameter->B) {
      if (verbose) message(8);
      strcpy(parameter->ports,STEREO);
      strcat(parameter->ports,PORT1S);
      shutup(PORT1);
      strcat(parameter->ports,PORT2S);
      shutup(PORT2);
      parameter->C=FALSE;
    }
    else
      if (parameter->C) {
        if (verbose) message(8);
        strcpy(parameter->ports,STEREO);
        strcat(parameter->ports,PORT1S);
        shutup(PORT1);
        strcat(parameter->ports,PORT3S);
        shutup(PORT3);
      } else {
        if (verbose) message(26);
        strcpy(parameter->ports,SINGLE);
        strcat(parameter->ports,PORT1S);
        shutup(PORT1);
      }
  else
    if (parameter->B)
      if (parameter->C) {
        if (verbose) message(8);
        strcpy(parameter->ports,STEREO);
        strcat(parameter->ports,PORT2S);
        shutup(PORT2);
        strcat(parameter->ports,PORT3S);
        shutup(PORT3);
      } else {
        if (verbose) message(26);
        strcpy(parameter->ports,SINGLE);
        strcat(parameter->ports,PORT2S);
        shutup(PORT2);
      }
    else
      if (parameter->C) {
        if (verbose) message(26);
        strcpy(parameter->ports,SINGLE);
        strcat(parameter->ports,PORT3S);
        shutup(PORT3);
      } else {
        if (verbose) message(9);
        strcpy(parameter->ports,SPEAKER);
      }
}

int scannumber(char source[])
/* scans a strings for a positive decimal integer */
/* the source must contain the number only */
{
  int number,ptr;
  char c;
  
  if (source[0]=='\0') error(23);
  number=ptr=0;
  while ((c=source[ptr++])!='\0')
    if ((c>='0') && (c<='9')) number=10*number+(int)(c-'0');
    else error(23);
  return(number);
}

void scanarguments(int argc, char *argv[], char menuname[], 
                   PARAMETERTYPE *parameter)
/* scans command line parameters */
{
  int argptr;

  for (argptr=1 ; argptr<argc; argptr++) {
    if ((argv[argptr][0]==FLAG1) || (argv[argptr][0]==FLAG2))
      /* parameter */
      if (argv[argptr][2]!='\0') {
        /* long parameter */
        if ((char)toupper((int)argv[argptr][1])==TEMPFLAG) {
          parameter->tempdrive=(int)((char)toupper((int)argv[argptr][2])-'A');
          if (argv[argptr][3]!='\0') error(23);
        }
        else
        if ((char)toupper((int)argv[argptr][1])==MODEFLAG) 
          parameter->videomode=scannumber(&(argv[argptr][2]));
        else
        if ((char)toupper((int)argv[argptr][1])==COLORFLAG) 
          switch (argv[argptr][2]) {
            case '1':coloroutline=scannumber(&(argv[argptr][3]));
                     break;
            case '2':colortitle=scannumber(&(argv[argptr][3]));
                     break;
            case '3':coloritem=scannumber(&(argv[argptr][3]));
                     break;
            case '4':colorpicked=scannumber(&(argv[argptr][3]));
                     break;
            case '5':colorcurrent=scannumber(&(argv[argptr][3]));
                     break;
            case '6':colorcursor=scannumber(&(argv[argptr][3]));
                     break;
            case '7':colorborder=scannumber(&(argv[argptr][3]));
                     break;
            default:error(23);
          }
        else error(23);
      }
      else
      /* short parameter */
      if ((char)toupper((int)argv[argptr][1])==SPEED1FLAG) 
        parameter->speed[1]='a';
      else
      if ((char)toupper((int)argv[argptr][1])==SPEED2FLAG) 
        parameter->speed[1]='b';
      else
      if ((char)toupper((int)argv[argptr][1])==SPEED3FLAG) 
        parameter->speed[1]='c';
      else
      if ((char)toupper((int)argv[argptr][1])==FRENCHFLAG) language=FRENCH;
      else
      if ((char)toupper((int)argv[argptr][1])==ENGLISHFLAG) language=ENGLISH;
      else
      if ((char)toupper((int)argv[argptr][1])==GERMANFLAG) language=GERMAN;
      else
      if ((char)toupper((int)argv[argptr][1])==HIGHFLAG) {
        coloroutline|=HIGHINT;
        colortitle|=HIGHINT;
        coloritem|=HIGHINT;
        colorpicked|=HIGHINT;
        colorcurrent|=HIGHINT;
        colorcursor|=HIGHINT;
        colorborder|=HIGHINT;
      }
      else
      if ((char)toupper((int)argv[argptr][1])==MOUSEFLAG) 
        parameter->mouse=TRUE;
      else
      if ((char)toupper((int)argv[argptr][1])==SONGFLAG) {
        parameter->verbose=FALSE;
        parameter->DOS=FALSE;
      }
      else
      if ((char)toupper((int)argv[argptr][1])==VERBOSEFLAG) {
        parameter->verbose=TRUE;
        parameter->DOS=FALSE;
      }
      else
      if ((char)toupper((int)argv[argptr][1])==DOSFLAG) {
        parameter->verbose=FALSE;
        parameter->DOS=TRUE;
      }
      else
      if ((char)toupper((int)argv[argptr][1])==PAUSEFLAG) 
        parameter->pause=TRUE;
      else
      if ((char)toupper((int)argv[argptr][1])==BIOSFLAG) 
        parameter->rawvideo=FALSE;
      else
      if (argv[argptr][1]==NOPORTFLAG) {
        parameter->A=parameter->B=parameter->C=FALSE;
        strcpy(parameter->ports,SPEAKER);
        }
      else
      if (argv[argptr][1]==PORT1FLAG) {
        parameter->A=TRUE;
        parameter->ports[0]=ODDPORTS;
      }
      else
      if (argv[argptr][1]==PORT2FLAG) {
        parameter->B=TRUE;
        parameter->ports[0]=ODDPORTS;
      }
      else
      if (argv[argptr][1]==PORT3FLAG) {
        parameter->C=TRUE;
        parameter->ports[0]=ODDPORTS;
      }
      else
      if ((char)toupper((int)argv[argptr][1])==QUESTIONFLAG) error(1);
      else error(23);
    else 
      /* must be a menu name */
      if (strlen(argv[argptr])>=(LINELENGTH-strlen(MENUENDING))) error(22);
      else strcpy(menuname,argv[argptr]);
  }
  colorcurrent<<=4;
  colorcursor<<=4;
  colorborder<<=4;
  /* no harm done if the user specifies three parallel ports */
  if (parameter->A || parameter->B || parameter->C) findports(parameter,FALSE);
}

void extractstring(char **scannerptr, char target[], int targetlength, 
                   int spacesin)
/* copies "intelligently" a token from a string into another string */
/* target has a fixed width */
{
  int scanner;

  /* skip leading spaces */
  while ((**scannerptr==' ') && (**scannerptr!='\0') && (**scannerptr!='\n'))
    (*scannerptr)++;
  /* there should be something left to read */
  if ((**scannerptr=='\0') || (**scannerptr=='\n')) error(10);
  if (spacesin) {
    /* read until EOL or nothing left */
    for (scanner=0 ; (**scannerptr!='\0') && (**scannerptr!='\n') ;
         (*scannerptr)++) target[scanner++]=**scannerptr;
    /* discard pending spaces for now */
    while (target[scanner-1]==' ') scanner--;
  }
  else
    /* read until space, EOL, or nothing left */
    for (scanner=0 ; (**scannerptr!=' ') && (**scannerptr!='\0') && 
         (**scannerptr!='\n') ; (*scannerptr)++) target[scanner++]=**scannerptr;
  /* check if not too long */
  if (scanner>targetlength) error(11);
  /* fill with space until full length */
  while (scanner<targetlength) target[scanner++]=' ';
  /* put EOS character */
  target[targetlength]='\0';
}

void flushright(char target[], int targetlength)
/* inserts spaces until the content is flush right */
{
  int ptr,endptr;

  ptr=endptr=targetlength-1;
  while (target[ptr]==' ') ptr--;
  while (ptr>=0) target[endptr--]=target[ptr--];
  while (endptr>=0) target[endptr--]=' ';
}

void centerstring(char target[], int targetlength)
/* inserts spaces until the content is centered */
{
  int ptr,endptr;

  ptr=targetlength-1;
  while (target[ptr]==' ') ptr--;
  endptr=(ptr+targetlength)/2;
  while (ptr>=0) target[endptr--]=target[ptr--];
  while (endptr>=0) target[endptr--]=' ';
}

void pathcopy(SONGSTYPE *songs, char newDOSpath[])
/* copies a paths to the pool and updates the last song */
{
  char *poolptr;
  int scanner;

  scanner=LINELENGTH-1; /* length of newDOSpath */
  while ((newDOSpath[--scanner]==' ') && (scanner>=0)) newDOSpath[scanner]='\0';
  poolptr=songs->songpath+songs->songpathoffset;
  songs->song[songs->songnumber].DOSpath=poolptr;
  songs->songpathoffset+=strlen(newDOSpath);
  if (songs->songpathoffset++>PATHLENGTH) error(24);
  strcpy(poolptr,newDOSpath);
}

void readmenu(FILE *menufile, SONGSTYPE *songs)
/* each line contains diskette name, path name, DOS file name, and song name */
/* these subitems are surrounded by spaces */
/* the song name may contain spaces */
{
  char line[LINELENGTH];
  char newdiskname[LINELENGTH];
  char newscore[LINELENGTH];
  char newDOSpath[LINELENGTH];
  char newDOSname[LINELENGTH];
  char newsongname[LINELENGTH];
  char *errorchar;
  char *scannerptr;

  errorchar=fgets(line,LINELENGTH,menufile); /* let fgets handle EOL */
  while (errorchar!=NULL) {
    if (strlen(line)==(LINELENGTH-1)) error(4);
    if (line[0]!=COMMENTFLAG) {
      scannerptr=line;
      extractstring(&scannerptr,newdiskname,DISKNAMELENGTH,FALSE);
      flushright(newdiskname,DISKNAMELENGTH);
      extractstring(&scannerptr,newscore,SCORELENGTH,FALSE);
      flushright(newscore,SCORELENGTH);
      extractstring(&scannerptr,newDOSpath,(LINELENGTH-1),FALSE);
      extractstring(&scannerptr,newDOSname,DOSNAMELENGTH,FALSE);
      extractstring(&scannerptr,newsongname,SONGNAMELENGTH,TRUE);
      centerstring(newsongname,SONGNAMELENGTH);
      songs->songnumber++;
      if (songs->songnumber>MAXSONGS) error(5);
      strcpy(songs->song[songs->songnumber].diskname,newdiskname);
      strcpy(songs->song[songs->songnumber].score,newscore);
      pathcopy(songs,newDOSpath);
      strcpy(songs->song[songs->songnumber].DOSname,newDOSname);
      strcpy(songs->song[songs->songnumber].songname,newsongname);
      songs->song[songs->songnumber].picked=0;
    }
    errorchar=fgets(line,LINELENGTH,menufile);
  }
  if (songs->songnumber==0) error(6);
  printf(" %d",songs->songnumber);
  message(7);
  printf(" %d",(int)(((long)100*(long)songs->songpathoffset)/(long)PATHLENGTH));
  message(0);
}

int findport(int port)
/* port deemed valid iff data can be written and read back */
/* returns TRUE iff parallel port found */
/* bus remanence (?!) calls for the delay used between write and read */
/* this delay also makes for a not so loud speaker noise */
/* warning: may not work on some parallel ports */
{
  int data;

  shutup(port);
  data=0x0;
  do {
    data++;
    outportb(port,(unsigned char)data);
    delay(SHUTUPDELAY);
    if ((unsigned char)data!=inportb(port)) return(FALSE);
  } while (data!=0xFF);
  shutup(port);
  return(TRUE);
}

int findprogram(char program[], int tempdrive, int currentdrive)
/* searches temporary drive(RAM?), PATH(HD?), and current drive */
/* returns drive number or -1 */
{
  struct ffblk fff;

  setdisk(tempdrive);
  if (searchpath(program)==NULL) {
    setdisk(currentdrive);
    /* normal, read-only, hidden, system, archive */
    if (findfirst(program,&fff,39)) return(-1);
    else return(currentdrive);
  }
  else {
    setdisk(currentdrive);
    return(tempdrive);
  }
}

int moveprogram(PARAMETERTYPE *parameter, char program[], int *programcopied) 
/* the program is on the temporary drive (or path), or on the current drive */
/* if necessary, the program is moved temporarily onto the temporary drive */
/* this will most likely optimize the speed of operation of system() calls */
/* returns TRUE iff the program has been found */
{
  char buffer[BUFFERSIZE];
  char filename[LINELENGTH];
  FILE *source,*target;
  int programdrive,errorint,tomove,moved;
  char temporarytext[3];
  char programtext[3];

  *programcopied=FALSE;
  programdrive=findprogram(program,parameter->tempdrive,
                           parameter->currentdrive);
  if (programdrive==-1) return(FALSE);
  if (programdrive!=parameter->tempdrive) {
    strcpy(temporarytext,"A:");
    strcpy(programtext,"A:");
    temporarytext[0]+=(char)parameter->tempdrive;
    programtext[0]+=(char)programdrive;
    message(10);
    strcpy(filename,programtext);
    strcat(filename,program);
    source=fopen(filename,"rb");
    if (source==NULL) error(13);
    strcpy(filename,temporarytext);
    strcat(filename,program);
    target=fopen(filename,"wb");
    if (target==NULL) error(14);
    tomove=(int)fread(buffer,(unsigned)1,(unsigned)BUFFERSIZE,source);
    while (tomove!=0) {
      moved=(int)fwrite(buffer,(unsigned)1,(unsigned)tomove,target);
      if (moved!=tomove) error(15);
      tomove=(int)fread(buffer,(unsigned)1,(unsigned)BUFFERSIZE,source);
    }
    errorint=fclose(source);
    if (errorint==EOF) error(16);
    errorint=fclose(target);
    if (errorint==EOF) error(17);
    *programcopied=TRUE;
    message(11);
  }                                                            
  else message(12);
  return(TRUE);
}

void readparameter(PARAMETERTYPE *parameter, MOUSETYPE *mousedata,
                   int songnumber)
/* collects information about drives, program location, ports and screen */
{
  int height;
  int found;
  int lastdrive;
  int bigtotal;

  message(28);
  printf("%c\n",(char)toupper((int)parameter->speed[1]));
  if (parameter->verbose) {
    message(41);
    parameter->itemlength=1+DISKNAMELENGTH+1+SCORELENGTH+1+DOSNAMELENGTH+1+
                            SONGNAMELENGTH+1;
  }
  else 
    if (parameter->DOS) {
      message(46);
      parameter->itemlength=1+DOSNAMELENGTH+1;
    }
    else {
      message(42);
      parameter->itemlength=1+SONGNAMELENGTH+1;
    }
  message(13);
  parameter->currentdrive=getdisk();
  printf("%c\n",(char)((int)'A'+parameter->currentdrive));
  /* works iff LASTDRIVE statement found in CONFIG */
  lastdrive=setdisk(parameter->currentdrive)-1;
  if (parameter->tempdrive==ODDDRIVE) {
    if (parameter->currentdrive<=1)
      /* from floppy */
      parameter->tempdrive=lastdrive;
    else
      /* from hard-disk or RAM-disk */
      parameter->tempdrive=parameter->currentdrive;
  }
  message(14);
  printf("%c\n",(char)((int)'A'+parameter->tempdrive));
  if ((parameter->tempdrive<0) || (parameter->tempdrive>lastdrive)) error(30);
  message(15);
  printf("%s:\n",MODPLAY1);
  found=moveprogram(parameter,MODPLAY1,&(parameter->modplaycopied));
  if (!found) {
    message(15);
    printf("%s:\n",MODPLAY2);
    found=moveprogram(parameter,MODPLAY2,&(parameter->modplaycopied));
    if (!found) {
      message(15);
      printf("%s:\n",MODPLAY3);
      found=moveprogram(parameter,MODPLAY3,&(parameter->modplaycopied));
      if (!found) error(12);
    }
  }
  message(15);
  printf("%s:\n",PKUNZIP1);
  found=moveprogram(parameter,PKUNZIP1,&(parameter->pkunzipcopied));
  if (!found) message(29);
  parameter->pkunzipfound=found;
  if (parameter->ports[0]==ODDPORTS) {
    message(16);
    parameter->A=findport(PORT1);
    parameter->B=findport(PORT2);
    if (!parameter->A || !parameter->B) parameter->C=findport(PORT3);
    findports(parameter,TRUE);
  }
  else message(27);
  message(17);
  if (parameter->rawvideo) message(48);
  else message(49);
  if (parameter->videomode==ODDMODE) {
    message(18);
    parameter->videomode=getmode();
    printf("%d\n",parameter->videomode);
  }
  else {
    message(47);
    printf("%d\n",parameter->videomode);
  }
  parameter->maxcolumn=getcolumn();
  printf("  %d ",parameter->maxcolumn);
  message(19);
  parameter->maxline=25;
  printf("  %d ",parameter->maxline);
  message(20);
  height=(int)peekb((unsigned)0x40,(unsigned)0x84)+1;
  /* guess if this information makes sense */
  if ((height>25) && (height<=60)) {
    parameter->maxline=height;
    printf("  %d ",parameter->maxline);
    message(21);
  }
  /* number of item lines in menu */
  parameter->bigline=parameter->maxline-4;
  /* number of item columns in menu */
  parameter->bigcolumn=(parameter->maxcolumn-4)/parameter->itemlength;
  /* total number of items on menu */
  bigtotal=parameter->bigcolumn*parameter->bigline;
  /* adjust if screen not fully used */
  if (bigtotal>songnumber) {
    /* number of item columns in menu */
    parameter->bigcolumn=(songnumber-1)/parameter->bigline+1;
    /* number of item lines in menu */
    parameter->bigline=(songnumber-1)/parameter->bigcolumn+1;
  }
  if (parameter->bigcolumn<1) error(7);
  if (parameter->bigline<1) error(8);
  /* total number of item lines, displayed and not displayed */
  parameter->hugeline=(songnumber-1)/parameter->bigcolumn+1;
  /* left out column space */
  parameter->leftcolumn=parameter->maxcolumn-4-
                        parameter->bigcolumn*parameter->itemlength;
  if (parameter->mouse) {
    message(44);
    if (setupmouse(parameter,mousedata)) message(45);
    else error(31);
  }
  if (parameter->pause) {
    bellit();
    message(22);
    switch (bioskey(0)) {
      case KEYESC:error(9);
      default:break;
    }
  }
}
