/* ---------------------------------------------------------------------- */
/*   WorkBook.c (C) Copyright Bill Buckels 1989-1999                      */
/*   All Rights Reserved.                                                 */
/*                                                                        */
/*   Licence Agreement                                                    */
/*   -----------------                                                    */
/*                                                                        */
/*   WorkBook is distributed as ShareWare.                                */
/*   Suggested Registration is $10.00 per family.                         */
/*                                                                        */
/*   You are expected to register with the Author if you use WorkBook     */
/*   beyond a 30-day evaluation period. Send registration in the form of  */
/*   cheque, or money order to:                                           */
/*                                                                        */
/*   Bill Buckels                                                         */
/*   589 Oxford Street                                                    */
/*   Winnipeg, Manitoba, Canada R3M 3J2                                   */
/*                                                                        */
/*   Email: bbuckels@escape.ca                                            */
/*   WebSite: http://www.escape.ca/~bbuckels                              */
/*                                                                        */
/*   Registered users of WorkBook have a royalty-free right to use,       */
/*   modify, reproduce and distribute this source code (and/or any        */
/*   modified version) in any way you find useful, provided you do not    */
/*   compete with Bill Buckels or his agents, and that you agree that     */
/*   Bill Buckels has no warranty obligations or liability whatsoever     */
/*   resulting from any associated loss or damage.                        */
/*                                                                        */
/*   If you do not agree with these terms, remove this source and         */
/*   all associated files from your computer now.                         */
/*                                                                        */
/*   Description                                                          */
/*   -----------                                                          */
/*                                                                        */
/*   WorkBook is written in Large Model Microsoft C Version 6.00a         */
/*   Previous Versions Were Written in Mix Power C                        */
/* ---------------------------------------------------------------------- */

typedef unsigned char     uchar;
typedef unsigned int      uint;
typedef unsigned long     ulong;


#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <dos.h>
#include <bios.h>
#include <io.h>
#include <time.h>
#include <malloc.h>
#include <conio.h>
#include <graph.h>
#include <process.h>

/* ---------------------------------------------------------------------- */
/* Constants and Macros                                                   */
/* ---------------------------------------------------------------------- */
#define TRUE     1
#define FALSE    0
#define SUCCESS  0
#define VALID    SUCCESS
#define FAILURE       -1
#define INVALID       FAILURE
#define TIME_EXPIRED  -2
#define FRAME_ADVANCE -3
#define SHOW_SCORE    -4

#define FRAMESIZE ((unsigned)64000)
#define FRAMEADDR ((uchar *)0xa0000000l)

#define MAX_BUFFSIZE ((unsigned)65000)
#define FONTSIZE     ((unsigned)23040)

#define ASCIIZ  '\x00'

#define ENTERKEY   '\x0d' /* character generated by the Enter Key          */
#define ESCKEY     '\x1b' /* character generated by the Esc key            */
#define FUNCKEY    '\x00' /* first character generated by function keys    */
#define UPARROW    'H'    /* second character generated by up-arrow key    */
#define DOWNARROW  'P'    /* second character generated by down-arrow key  */
#define LTARROW    'K'    /* second character generated by left-arrow key  */
#define RTARROW    'M'    /* second character generated by right-arrow key */
#define PGUP       'I'    /* second character generated by page up key     */
#define PGDOWN     'Q'    /* second character generated by page down key   */
#define HOMEKEY    'G'    /* second character generated by home key        */
#define ENDKEY     'O'    /* second character generated by end key         */

/* second character generated by numerical fkeys */
/* starting at character 59                      */
/* ending at character 68                        */
#define F1         ';'
#define F2         '<'
#define F3         '='
#define F4         '>'
#define F5         '?'
#define F6         '@'
#define F7         'A'
#define F8         'B'
#define F9         'C'
#define F10        'D'

#define CRETURN '\x0d'
#define LFEED   '\x0A'
#define CTRLZ   '\x1a'
#define DELETE  '\x7f'
#define ESCAPE  '\x1b'
#define BACKSPACE   8
#define SPACEBAR    32

#define MCGA     '\x13'
#define TEXT     '\x03'

/* middle of screen */
#define XMIN 0
#define YMIN 0
#define XMOS 159
#define YMOS 99
#define XMAX 319
#define YMAX 199

#define SCREENWIDTH  (XMAX + 1)
#define RASTERWIDTH  SCREENWIDTH
#define SCREENHEIGHT (YMAX + 1)
#define FONTHEIGHT   10

#define vload() memcpy(FRAMEADDR,(uchar *)&rawbuffer[0],FRAMESIZE)
#define vsave() memcpy((uchar *)&rawbuffer[0],FRAMEADDR,FRAMESIZE)

#define zap(x) memset(FRAMEADDR,x,FRAMESIZE)
#define midpoint(x1,x2) (x1+((x2-x1)/2))

#define TRANSPARENT_COLOR 230
#define MAIN_MENU  1   /* Menu Types */
#define MOUSE_MENU 2

/* ---------------------------------------------------------------------- */
/* These Worm Cursor Images are 68 pixels wide (17 bytes)                 */
/*                            x 47 rasters deep                           */
/* ---------------------------------------------------------------------- */
#define CUR_BYTES   17
#define CUR_WIDTH   68
#define CUR_HEIGHT  47
#define CUR_DATA    4    /* offset past header */
#define CUR_MAX     2

/* ---------------------------------------------------------------------- */
/* These Worm Image Fragments are 36 pixels wide (9 bytes)                */
/*                              x 24 rasters deep                         */
/* ---------------------------------------------------------------------- */

#define ANI_DATA    4    /* offset past header */
#define ANI_MAX     4

#define WORM_BYTEWIDTH    7
#define WORM_PIXELWIDTH   28
#define WORM_HEIGHT       16
#define BIRD_BYTEWIDTH    7
#define BIRD_PIXELWIDTH   28
#define BIRD_HEIGHT       16

#define FINGER_BYTEWIDTH  16
#define FINGER_PIXELWIDTH 64
#define FINGER_HEIGHT     33

// directions for mirroring of image buffers
#define TOP_TO_BOTTOM '+'
#define BOTTOM_TO_TOP '-'

/* ---------------------------------------------------------------------- */
/* Global Vars, etc.                                                      */
/* ---------------------------------------------------------------------- */

// index - height - width
uchar WormAnimation[ANI_MAX][WORM_HEIGHT][WORM_PIXELWIDTH];
uchar BirdAnimation[ANI_MAX][BIRD_HEIGHT][BIRD_PIXELWIDTH];
uchar WormCursor[CUR_MAX][CUR_HEIGHT][CUR_WIDTH];
uchar FingerHand[(FINGER_HEIGHT * FINGER_PIXELWIDTH)];

/* a bit field structure for the CGA pixel */
struct cgabit{
       uint x4: 2;
       uint x3: 2;
       uint x2: 2;
       uint x1: 2;
    };

/* we use a union to unmask the CGA pixel bit fields */
union cgapixel{
      struct cgabit pixel;
      uchar byt;
    };

int POMP[];

uchar *MenuTitle[]={
    "WorkBook(C) Version 2.1",
    "Copyright 1989-1999 Bill Buckels",
    "All Rights Reserved.",
    "--------------------------------"};

#define TITLEMAX (sizeof(MenuTitle)/sizeof(uchar*))
#define TITLELAST (TITLEMAX - 1)

uchar *WorkBookHelp[]={
    "Words are presented on a",
    "background of an out of focus",
    "image. If the words are correctly",
    "overtyped within the time limit,",
    "the picture is presented again in",
    "focus, and a musical reward is",
    "played. If the time limit is not",
    "met, a rude noise is made and the",
    "next word and image combination",
    "is presented for another timed",
    "round of play. See ReadMe.Txt for",
    "more details."};

#define HELPMAX (sizeof(WorkBookHelp)/sizeof(uchar*))

enum { CONTINUE_CHOICE = 0,
       SCORE_CHOICE,
       HELP_CHOICE,
       SELECT_CHOICE,
       SOUND_CHOICE,
       EXIT_CHOICE,
       MAINMAX};

#define MENUMAX MAINMAX
#define LAST_MENU_CHOICE (MENUMAX - 1)

int bMickeyMouse = FALSE,
    iMousex = XMOS,
    iMousey = YMOS,
    iMouseMenu[MENUMAX][4];

int iMainMouse[6][4]={
    0,   0,  105,  99,
    106, 0,  212,  99,
    213, 0,  319,  99,

    0,  100, 105, 199,
    106,100, 212, 199,
    213,100, 319, 199};

uint iCursorCords[6][2]={ 13, 34,
                          119,34,
                          226,34,
                           13,117,
                          119,117,
                          226,117};

int iSoundFlag = TRUE;

uchar szCurrentWorkBook[80];

uchar far *rawbuffer;
uchar far *framebuffer;
uchar far *ramfont;

uchar rgbinfo[256][3];           /* the vga palette */
uchar outlinecolor;
uchar drawcolor;


int Frequency[7][12]={

/* C           D           E     F           G           A           B  */
/* 0     1     2     3     4     5     6     7    8      9     10    11 */
/* W     B     W     B     W     W     B     W    B      W     B     W  */
   65,   69,   73,   78,   82,   87,   93,   98,  104,  110,  117,  123,
  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
  262,  278,  294,  312,  330,  349,  371,  392,  416,  440,  467,  494,
  523,  555,  587,  623,  659,  698,  741,  784,  832,  880,  934,  988,
 1046, 1111, 1175, 1247, 1318, 1397, 1482, 1568, 1664, 1760, 1868, 1976,
 2093, 2221, 2349, 2493, 2637, 2794, 2965, 3136, 3328, 3520, 3736, 3951,
 4186, 4442, 4699, 4986, 5274, 5588, 5930, 6272, 6656, 7040, 7471, 7902};

int FNOTES[21][2]={
/* octave note*/
      1,  9,
      1,  11,
      2,  0,
      2,  2,
      2,  4,
      2,  5,
      2,  7,
      2,  9,
      2,  11,
      3,  0,};

/* Score Counters */
int iTotalQuestions = 0,
    iTotalCorrect = 0,
    iTotalSkipped = 0,
    iTotalOverTime = 0;

/* ---------------------------------------------------------------------- */
/* Function Prototypes                                                    */
/* ---------------------------------------------------------------------- */

int HotClick (int iRetVal, int *choice, int iMenuType);
int ClickOnly (void);
int HideMickey (void);
int ShowMickey (int x, int y);
int VerticalMickeyMinMax (int ymin, int ymax);
int HiMickey (void);

/* ---------------------------------------------------------------------- */
/* Sleep for a while                                                      */
/* ---------------------------------------------------------------------- */
int sleep(int seconds)
{
  int x;

  for (x=0; x<seconds; x++) {
    sound(32767, 18);
    if (x%5 == 0)         /* correct for timertick every 5th second */
      sound(32767, 1);
  }
  return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* Trim trailing white space                                              */
/* ---------------------------------------------------------------------- */
void trim(uchar *str)
{
  int len=strlen(str)-1;
  while(str[len]=='\x20' && len > -1) {
    str[len]=ASCIIZ;
    len--;
  }
  return;
}


/* ---------------------------------------------------------------------- */
/* Use the standard bios int10h routine to load a 256 color palette       */
/* ---------------------------------------------------------------------- */
int loadpalette()
{
  union REGS regs;
  struct SREGS segregs;

  regs.h.ah = 0x10;  /* fuction 10h */
  regs.h.al = 0x12;  /* subfunction 12h - set block of color registers */
  regs.x.bx = 0;     /* start with this reg */
  regs.x.cx = 256;   /* do this many */
  regs.x.dx = (uint)rgbinfo;                 /* offset to array */
  segregs.es = (uint)((long)rgbinfo>>16);   /* segment of array */
  int86x(0x10,&regs,&regs,&segregs);    /* dump data to color registers */
  return SUCCESS;

}


/* ---------------------------------------------------------------------- */
/* Determine the lightest and darkest color in the current palette.       */
/* use the darkest color for the drawcolor                                */
/* use the lightest color for the outline color                           */
/* ---------------------------------------------------------------------- */
void hilo()
{
    uint i,temp,temphi=0,templo=63+63+63;

    for(i=0;i<256;i++)
    {
        temp = rgbinfo[i][0]+rgbinfo[i][1]+rgbinfo[i][2];
        if(temp>=temphi)
        {
            temphi=temp;
            outlinecolor=i;
        }
        if(temp<=templo)
        {
            templo=temp;
            drawcolor=i;
        }
    }
}


/* ---------------------------------------------------------------------- */
/*  Mirror the splash screen to display help more clearly, etc.           */
/* ---------------------------------------------------------------------- */
void MirrorFromCentre(uchar *ptr, uchar direction)
{
   uint y1, y;

   // assume that the pointer is to a 64K image buffer
   y1 = YMOS;
   for (y = (YMOS+1); y < SCREENHEIGHT; y++) {
     if (direction == TOP_TO_BOTTOM)     /* copy top to bottom if + */
        memcpy(&ptr[y*RASTERWIDTH], &ptr[y1*RASTERWIDTH], RASTERWIDTH);
     if (direction == BOTTOM_TO_TOP)     /* copy bottom to top if - */
        memcpy(&ptr[y1*RASTERWIDTH], &ptr[y*RASTERWIDTH], RASTERWIDTH);
     y1--;
   }
   return;
}


/* ---------------------------------------------------------------------- */
/*  Get a pixel color at x,y  Return Color                                */
/* ---------------------------------------------------------------------- */
uchar getpixel(uint x, uint y, uchar *framebuffer)
{
    if (x<XMIN || x>XMAX || y<YMIN || y>YMAX)
      return (uchar)0;

    return framebuffer[(y*RASTERWIDTH)+x];
}


/* ---------------------------------------------------------------------- */
/* Write a pixel at x,y using color                                       */
/* ---------------------------------------------------------------------- */
void putpixel(int x, int y,uint pixelvalue, uchar *framebuffer)
{
    if (x<XMIN || x>XMAX || y<YMIN || y>YMAX)
      return;

    framebuffer[(y*RASTERWIDTH)+x]=(uchar)pixelvalue;
}

/* ---------------------------------------------------------------------- */
/* Added BMP support                                                      */
/* ---------------------------------------------------------------------- */

// translate a 16 color line  to an svga line
void Vga2Svga(unsigned char *src, unsigned char *dest,unsigned width)
{
     int i,j;

     j=0;
     for(i=0;i<width;i++)
     {
        dest[j] = src[i]>>4;  j++;
        dest[j] = src[i]&0xf; j++;
     }
}

// translate a 2 color line to an svga line
void Mono2Svga(unsigned char *src, unsigned char *dest,unsigned width)
{
     int i,j,k;
     uchar msk[]={0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1};

     j=0;
     for(i=0;i<width;i++)
     {

        for(k=0;k<8;k++)
        {
            if(src[i]&msk[k])dest[j] = 1;  /* Foreground */
            else dest[j] = 0;              /* BackGround */
            j++;
        }
     }
}

/* Windows BMP structures and constants */

#define SVGA     256
#define VGA      16
#define MONO     2
#define PIXEL    1

typedef struct tagRGBQUAD
{
    uchar    rgbBlue;
    uchar    rgbGreen;
    uchar    rgbRed;
    uchar    rgbReserved;
} RGBQUAD;

typedef struct tagBITMAPINFOHEADER
{
    ulong   biSize;
    ulong   biWidth;
    ulong   biHeight;
    uint    biPlanes;
    uint    biBitCount;
    ulong   biCompression;
    ulong   biSizeImage;
    ulong   biXPelsPerMeter;
    ulong   biYPelsPerMeter;
    ulong   biClrUsed;
    ulong   biClrImportant;
} BITMAPINFOHEADER;

// constants for the biCompression field
#define BI_RGB      0L
#define BI_RLE8     1L
#define BI_RLE4     2L

typedef struct tagBITMAPINFO
{
    BITMAPINFOHEADER bmiHeader;
    RGBQUAD          bmiColors[256];
} BITMAPINFO;

typedef struct tagBITMAPFILEHEADER
{
    uchar   bfType[2];
    ulong   bfSize;
    uint    bfReserved1;
    uint    bfReserved2;
    ulong   bfOffBits;
} BITMAPFILEHEADER;

/* globals required for BMP */
BITMAPFILEHEADER BitMapFileHeader;
BITMAPINFO       bmp;

/* ---------------------------------------------------------------------- */
/* Read a 256 Color 320 x 200 BMP File Into a screen buffer.              */
/* ---------------------------------------------------------------------- */
int BMPREAD(uchar *lpszBmpFile, uchar far *bmpbuffer)
{
    FILE *fp;
    int i;
    ulong dwSeekSize;
    uint wHres,wVres,wXoff,wYoff;
    char szScanLine[360];

    int iFormat,
        iByteBoundary,
        iPalColors,
        iDivisor;

    if((fp=fopen(lpszBmpFile,"rb"))==NULL)return FAILURE;

    // read the header stuff into the appropriate structures
    fread((char *)&BitMapFileHeader.bfType,
	             sizeof(BITMAPFILEHEADER),1,fp);
    fread((char *)&bmp.bmiHeader.biSize,
                 sizeof(BITMAPINFO),1,fp);

    if(bmp.bmiHeader.biCompression!=BI_RGB ||
       bmp.bmiHeader.biWidth != 320L ||
       bmp.bmiHeader.biHeight != 200L)
       {
        fclose(fp); return FAILURE;
       }

    iFormat = FALSE;
    if(bmp.bmiHeader.biPlanes==1 && bmp.bmiHeader.biBitCount==8)
    {
       iFormat        = SVGA;
       iByteBoundary  = 4 * PIXEL;
       iPalColors     = SVGA;
       iDivisor       = PIXEL;
    }
    if(bmp.bmiHeader.biPlanes==1 && bmp.bmiHeader.biBitCount==4)
    {
       iFormat       = VGA;
       iByteBoundary = 8 * PIXEL;
       iPalColors    = VGA;
       iDivisor      = 2 * PIXEL;
    }
    if(bmp.bmiHeader.biPlanes==1 && bmp.bmiHeader.biBitCount==1)
    {
       iFormat       = MONO;
       iByteBoundary = 32 * PIXEL;
       iPalColors    = MONO;
       iDivisor      = 8 * PIXEL;
    }

    if(iFormat == FALSE)
    {
        fclose(fp);
        return FAILURE;
    }

    for(i=0;i<iPalColors;i++)
    {
       rgbinfo[i][0]=bmp.bmiColors[i].rgbRed  >>2;
       rgbinfo[i][1]=bmp.bmiColors[i].rgbGreen>>2;
       rgbinfo[i][2]=bmp.bmiColors[i].rgbBlue >>2;
    }

    wHres = (uint )bmp.bmiHeader.biWidth;
    while(wHres%iByteBoundary!=0)wHres++;
    wVres = (uint )bmp.bmiHeader.biHeight;

    dwSeekSize =  0L;
    dwSeekSize += BitMapFileHeader.bfOffBits;

    // our palette will not always be a full 256 colors...
    // even in some 256 color images

    fseek(fp,dwSeekSize,SEEK_SET);
    for(i=wVres;i>0;i--)
    {
      fread(szScanLine,wHres/iDivisor,1,fp);
      switch(iFormat)
       {
         case MONO:  Mono2Svga(szScanLine, &bmpbuffer[(i-1)*320],
                               wHres/iDivisor);
                               break;
         case VGA:   Vga2Svga(szScanLine, &bmpbuffer[(i-1)*320],
                               wHres/iDivisor);
                               break;
         default:    memcpy(&bmpbuffer[(i-1)*320],szScanLine, 320);
                               break;

        }
    }
    fclose(fp);

return SUCCESS;
}

/* type conversion functions */
uint byteword(uchar a, uchar b){return b<<8|a;}
uchar lsb(uint word){ return word &0xff;}
uchar msb(uint word){ return word >>8;}

/* ---------------------------------------------------------------------- */
/* Check the PCX file header for a valid 256 color MCGA Full Screen       */
/* ---------------------------------------------------------------------- */
int checkforpcx(uchar *pcxheader)
{
    uint zsoft,version,codetype,pixbits;
    uint xmin, ymin, xmax, ymax;
    uint hres, vres;
    uint no_planes, bytesperline;
    int status=VALID;

    /* read the file header */

    zsoft   =pcxheader[0];
    version =pcxheader[1];
    codetype=pcxheader[2];
    pixbits =pcxheader[3];

    if(zsoft!=10)        status = INVALID;
    if(version!=5)       status = INVALID;
    if(codetype!=1)      status = INVALID;
    if(pixbits !=8)      status = INVALID;

    xmin=byteword(pcxheader[4],pcxheader[5]);
    ymin=byteword(pcxheader[6],pcxheader[7]);
    xmax=byteword(pcxheader[8],pcxheader[9]);
    ymax=byteword(pcxheader[10],pcxheader[11]);
    no_planes   =pcxheader[65];
    bytesperline=byteword(pcxheader[66],pcxheader[67]);

    if(xmin != 0  )       status = INVALID;
    if(ymin != 0  )       status = INVALID;
    if(xmax != 319)       status = INVALID;
    if(ymax != 199)       status = INVALID;
    if(no_planes!=1)      status = INVALID;
    if(bytesperline !=RASTERWIDTH)status = INVALID;

    return status;
}


/* ---------------------------------------------------------------------- */
/* Read a 256 Color 320 x 200 PCX File Into a screen buffer.              */
/* ---------------------------------------------------------------------- */
int VCXREAD(uchar *pcxfilename, uchar far *pcxbuffer)
{
    uint byteoff=0,packet,width=0;
    int fh,status;
    uchar *packbuffer;
    uchar byte,bytecount;
    uchar *ptr,*ptr2;
    uint temp;
    uint target;
    int x,y;

    if((packbuffer=_fmalloc(MAX_BUFFSIZE))==NULL)return FAILURE;
    if( (fh=open(pcxfilename,O_RDONLY|O_BINARY))==FAILURE)
    {
        free(packbuffer);
        return FAILURE;
    }

    target=read(fh,packbuffer,MAX_BUFFSIZE);
    close(fh);
    ptr2=(uchar *)&packbuffer[0];

    if((status=checkforpcx(ptr2))!=FAILURE)
    {
      /* read the palette */
      target-=769;
      ptr2=(uchar *)&packbuffer[target];
      while((temp=*ptr2++)!=12);
           for(y=0;y<256;y++)
              for(x=0;x<3;x++)
                  {
                   temp=*ptr2++;
                   temp>>=2;
                   rgbinfo[y][x]=temp;
                  }

      ptr=(uchar *)&pcxbuffer[0];
      ptr2=(uchar *)&packbuffer[128];

      do{ bytecount=1;                          /* start with a seed count */
          byte=*ptr2++;

                                                /* check to see if its raw */
          if(0xC0 == (0xC0 &byte)){             /* if its not, run encoded */
                      bytecount= 0x3f &byte;
                      byte=*ptr2++;
                      }
          for(packet=0;packet<bytecount;packet++){
                       *ptr++=byte;
                       byteoff++;
                       }
        }while(byteoff<FRAMESIZE);

    }
    free(packbuffer);
    return(status);
}


/* ---------------------------------------------------------------------- */
/* Read a BSAVED 256 Color 320 x 200 PCX Screen c/w Palette               */
/* ---------------------------------------------------------------------- */
int BVXREAD(uchar *filename)
{

    uchar buffer[66];
    uchar buffer1[66];
    uchar buffer2[66];
    uchar *wordptr, *bufptr, *ptr;
    int fh, fh2;

    strcpy(buffer,filename);
    wordptr=strtok(buffer,". \n");
    sprintf(buffer1,"%s.MCG",buffer);
    sprintf(buffer2,"%s.RGB",buffer);

    if((fh=open(buffer1,O_RDONLY|O_BINARY))==FAILURE)
    {
        return FAILURE;
    }
    if((fh2=open(buffer2,O_RDONLY|O_BINARY))==FAILURE)
    {
        close(fh);
        return FAILURE;
    }

    read(fh,rawbuffer,7);
    read(fh,rawbuffer,FRAMESIZE);
    close(fh);

    read(fh2,(uchar *)&rgbinfo[0][0],7);
    read(fh2,(uchar *)&rgbinfo[0][0],768);
    close(fh2);

    return SUCCESS;
}


/* ---------------------------------------------------------------------- */
/* Load a blurry image in a working frame. This will clear-up when the    */
/* correct answer is typed within the time-limit.                         */
/* ---------------------------------------------------------------------- */
int blurrload()
{
  uint x,y;
  uchar *ptr=FRAMEADDR;
  uchar *ptr2=(uchar *)&framebuffer[0];
  uchar ptr3[321];

  for(y=0;y<SCREENHEIGHT;y++)
  {

    if(y%4==0 || y< 4)memcpy(&ptr3[0],&rawbuffer[RASTERWIDTH*y],RASTERWIDTH);

    x=0;
    while(x<RASTERWIDTH)
    {
      if(*ptr2==0)
        *ptr=drawcolor;
      else
        if(*ptr2==3)*ptr=outlinecolor;
        else *ptr=ptr3[x];
      x++;
      *ptr++;
      *ptr2++;
    }
  }
  return SUCCESS;
}


/* ---------------------------------------------------------------------- */
/* Set the videomode using the standard bios int10h call                  */
/* ---------------------------------------------------------------------- */
uchar setcrtmode(uchar vidmode)
{
    union REGS inregs, outregs;

    /* set mode */
    inregs.h.ah = 0;
    inregs.h.al = vidmode;
    int86( 0x10, &inregs, &outregs );

    /* get mode */
    inregs.h.ah = 0xf;
    int86( 0x10, &inregs, &outregs );

    /* return mode */
    return outregs.h.al;

}


/* ---------------------------------------------------------------------- */
/* time out functions                                                     */
/* allow the kid ten seconds between key presses                          */
/* also allow an over-ride with a reasonable substitution.                */
/* ---------------------------------------------------------------------- */
time_t start_ticks,stop_ticks;
float elapsed_time=0, time_out=10;
int loops=VALID;

void start_timer()
{
  time(&start_ticks);
  time(&stop_ticks);
  loops=VALID;
}

int check_timer()
{
   time(&stop_ticks);
   if((elapsed_time=(difftime(stop_ticks,start_ticks)))<time_out)
     return VALID;
   loops=INVALID;
   return (loops);
}

void stop_timer()
{
   elapsed_time=0;
   loops=VALID;
}

/* ---------------------------------------------------------------------- */
/* Swap two character values - used to flip font colors.                  */
/* ---------------------------------------------------------------------- */
void SwapChar(uchar *c1, uchar *c2)
{
  uchar c;
  c = c1[0];
  c1[0] = c2[0];
  c2[0] = c;
}

/* ---------------------------------------------------------------------- */
/* Print an outlined font using color or special erase mode using the     */
/* picbuffer contents to remove previously drawn string from screen.      */
/* ---------------------------------------------------------------------- */
int OutlineFont(uchar *str,int x, int y, uchar *picbuffer)
{

  /* note: if picbuffer is NULL, we print the font normally   */
  /* otherwise, we print the pixel from the image buffer that */
  /* picbuffer points to... (used to erase previous text)     */

  /* if the drawcolor and outlinecolor are the same      */
  /* we don't outline the font, and just letter normally */

  uint len = strlen(str);
  uint scanlines,lets,bytes,temp;
  uint offset, xoff, yoff;
  uchar c=0;

  for(scanlines=0;scanlines<FONTHEIGHT;scanlines++)
  {
    for(lets=0;lets<len;lets++)
    {
       c=str[lets];
       offset=(90*c)+(scanlines*9);

      for(bytes=0;bytes<9;bytes++)
      {
        temp=ramfont[offset+bytes];
        if (temp == 1 || ((temp == 2) && (outlinecolor!=drawcolor))) {
          xoff=lets*8+x+bytes;
          yoff=y+scanlines;
          if (temp==2)
            c = outlinecolor;
          if (temp==1)
            c = drawcolor;
          if (picbuffer!=NULL) {
            c = getpixel(xoff, yoff, picbuffer);
            /* special case of erasing over the frame at bottom of screen */
            if (framebuffer == picbuffer) {
              if(c==0)
                c = drawcolor;
              else
                c = outlinecolor;
            }
          }
          putpixel(xoff, yoff, c, FRAMEADDR);
        }
      }
    }
  }
  return (0);
}

/* ---------------------------------------------------------------------- */
/* Sets-Up Menu Array and Returns y1                                      */
/* ---------------------------------------------------------------------- */
int SetMenu(int x1, int y1, int width, int height, int idx)
{
  if (idx == -1) {
    /* clear array */
    for (idx = 0; idx < MENUMAX; idx++) {
       iMouseMenu[idx][0] = -1;  /* x1, y1, x2, y2 */
       iMouseMenu[idx][1] = -1;
       iMouseMenu[idx][2] = -1;
       iMouseMenu[idx][3] = -1;

    }
  }
  else if (idx > -1 && idx < MENUMAX) {
       /* assign coordinates */
       iMouseMenu[idx][0] = x1;
       iMouseMenu[idx][1] = y1;
       iMouseMenu[idx][2] = ((x1 + width) -1);
       iMouseMenu[idx][3] = ((y1 + height) -1);
  }
  return (y1);
}

/* ---------------------------------------------------------------------- */
/* Get The Menu Choice or Return -1 if not in range.                      */
/* ---------------------------------------------------------------------- */
int GetMenuChoice(int x1, int y1, int iMenuType)
{
  int idx;

  switch(iMenuType) {
    case MAIN_MENU:
      // main menu menu mouse position 0-5
      for (idx = 0; idx < MAINMAX; idx++)  {
          if (x1 >= iMainMouse[idx][0] && y1 >= iMainMouse[idx][1] &&
              x1 <= iMainMouse[idx][2] && y1 <= iMainMouse[idx][3])
              return idx;
      }
      break;
    case MOUSE_MENU:

      for (idx = 0; idx < MENUMAX; idx++)  {
          if (x1 >= iMouseMenu[idx][0] && y1 >= iMouseMenu[idx][1] &&
              x1 <= iMouseMenu[idx][2] && y1 <= iMouseMenu[idx][3])
              return idx;
      }
      break;
  }
  // return -1 if not in range
  return INVALID;
}

/* ---------------------------------------------------------------------- */
/* Display a brief help screen - called from the option menu              */
/* ---------------------------------------------------------------------- */
int ShowHelp()
{
  uchar savecolor;
  int yoff, xoff, idx;

  if(VCXREAD("WorkBack.Pcx",rawbuffer))return FAILURE;

  /* build a background behind the scenes */
  MirrorFromCentre(rawbuffer, TOP_TO_BOTTOM);
  vload();

  yoff =   YMIN;
  xoff =   XMIN+20;

  savecolor = outlinecolor;

  for (idx = 0; idx < TITLEMAX; idx++)
    OutlineFont(MenuTitle[idx], xoff, yoff+=10, NULL);

  outlinecolor = drawcolor;
  for (idx = 0; idx < HELPMAX; idx++)
    OutlineFont(WorkBookHelp[idx], xoff, yoff+=10, NULL);
  outlinecolor = savecolor;

  OutlineFont(MenuTitle[TITLELAST], xoff, yoff+=16, NULL);
  OutlineFont("Press [ANY] to Return To Options", xoff, yoff+=10, NULL);

  ShowMickey(iMousex, iMousey);
  MousePause();
  HideMickey();
  return SUCCESS;

}


// helper function to display the score
void PrintScore(uchar *ptr, int yoff, int xoff)
{
  char szWork[20];

  sprintf(szWork, "%d", iTotalQuestions);
  OutlineFont(szWork,xoff, yoff+=16, ptr);
  sprintf(szWork, "%d", iTotalCorrect);
  OutlineFont(szWork,xoff, yoff+=16, ptr);
  sprintf(szWork, "%d", iTotalSkipped);
  OutlineFont(szWork,xoff, yoff+=16, ptr);
  sprintf(szWork, "%d", iTotalOverTime);
  OutlineFont(szWork,xoff, yoff+=16, ptr);
  return;
}

/* ---------------------------------------------------------------------- */
/* Display the current score - called from the option menu                */
/* ---------------------------------------------------------------------- */
int ShowScore()
{
  uchar szBuffer[2][40],
        szWork[80];
  int yoff, xoff, idx, width, height, status, ysave, xsave;
  int iChoice,
      iOldChoice,
      iLastChoice;

  if(VCXREAD("WorkBack.Pcx",rawbuffer))return FAILURE;
  MirrorFromCentre(rawbuffer, BOTTOM_TO_TOP);
  vload();

  iOldChoice = iChoice = 0;
  SetMenu(0, 0, 0, 0, -1);  /* Clear The Menu Array */

  yoff =   YMIN;
  xoff =   XMIN+20;
  width =  XMAX-40;
  height = FONTHEIGHT;    /* height of a font */

  for (idx = 0; idx < TITLEMAX; idx++)
    OutlineFont(MenuTitle[idx], xoff, yoff+=10, NULL);

  strcpy(szBuffer[iChoice],"Press [F1] to Clear Current Score");
  OutlineFont(szBuffer[iChoice], xoff,
    SetMenu(xoff, yoff+=10, width, height, iChoice), NULL);
  iChoice++;
  OutlineFont("Current Score", xoff, yoff+=16, NULL);
  OutlineFont(MenuTitle[TITLELAST], xoff, yoff+=10, NULL);

  ysave = yoff;
  sprintf(szWork, "Total Questions   = ", iTotalQuestions);
  xsave = xoff + strlen(szWork)*8;
  OutlineFont(szWork,xoff, yoff+=16, NULL);
  sprintf(szWork, "Total Correct     = ", iTotalCorrect);
  OutlineFont(szWork,xoff, yoff+=16, NULL);
  sprintf(szWork, "Total Skipped     = ", iTotalSkipped);
  OutlineFont(szWork,xoff, yoff+=16, NULL);
  sprintf(szWork, "Total OverTime    = ", iTotalOverTime);
  OutlineFont(szWork,xoff, yoff+=16, NULL);

  OutlineFont(MenuTitle[TITLELAST], xoff, yoff+=16, NULL);

  strcpy(szBuffer[iChoice], "Press [ESC] to Return To Options");
  OutlineFont(szBuffer[iChoice], xoff,
    SetMenu(xoff, yoff+=10, width, height, iChoice), NULL);

  iOldChoice = iLastChoice = iChoice;
  vsave();  /* save away the finished screen */

  /* print the score now that we have saved the rest */
  PrintScore(NULL, ysave, xsave);

  /* set-up initial mouse coordinates etc. and show cursor*/
  SwapChar(&drawcolor,&outlinecolor);
  OutlineFont(szBuffer[iChoice],
              iMouseMenu[iChoice][0], iMouseMenu[iChoice][1], NULL);
  SwapChar(&drawcolor,&outlinecolor);

  iMousex = midpoint(iMouseMenu[iChoice][0],iMouseMenu[iChoice][2]);
  iMousey = midpoint(iMouseMenu[iChoice][1],iMouseMenu[iChoice][3]);
  VerticalMickeyMinMax(iMouseMenu[0][1], iMouseMenu[iLastChoice][3]);
  ShowGraphicsArray(0,iMouseMenu[iChoice][1], FingerHand,FRAMEADDR,
                      FINGER_PIXELWIDTH, FINGER_HEIGHT, rawbuffer);
  EatKeys();
  ShowMickey(iMousex, iMousey);

  for (;;)  {
     status = FALSE;
     if (kbhit()) {
        status = getch();
        if (status == FUNCKEY) {
           switch(getch()) {
              case F1:
                   iChoice = 0;
                   status = ENTERKEY;
                   break;
              case PGUP:
              case LTARROW:
              case UPARROW:
                  iChoice--; if (iChoice < 0)iChoice=iLastChoice;break;
              case PGDOWN:
              case RTARROW:
              case DOWNARROW:
                  iChoice++; if (iChoice > iLastChoice)iChoice=0;break;
              case ENDKEY:
                  iChoice = iLastChoice;break;
              case HOMEKEY:
                  iChoice = 0;break;
           }
           if (iChoice != iOldChoice) {
              iMousex =
                midpoint(iMouseMenu[iChoice][0],iMouseMenu[iChoice][2]);
              iMousey =
                midpoint(iMouseMenu[iChoice][1],iMouseMenu[iChoice][3]);
           }
        }
     }
     if (iChoice == iOldChoice) {
        status = HotClick(status, &iChoice, MOUSE_MENU);
     }
     if (iChoice != iOldChoice) {
       HideMickey();
       ShowGraphicsArray(0,iMouseMenu[iOldChoice][1], NULL,FRAMEADDR,
                         FINGER_PIXELWIDTH, FINGER_HEIGHT, rawbuffer);
       OutlineFont(szBuffer[iOldChoice],
              iMouseMenu[iOldChoice][0], iMouseMenu[iOldChoice][1], NULL);
       SwapChar(&drawcolor,&outlinecolor);
       OutlineFont(szBuffer[iChoice],
              iMouseMenu[iChoice][0], iMouseMenu[iChoice][1], NULL);
       SwapChar(&drawcolor,&outlinecolor);
       iOldChoice = iChoice;
       ShowGraphicsArray(0,iMouseMenu[iChoice][1], FingerHand,FRAMEADDR,
                         FINGER_PIXELWIDTH, FINGER_HEIGHT, rawbuffer);
       ShowMickey(iMousex, iMousey);
     }
     if (status == ESCKEY || (status == ENTERKEY && iChoice != 0))
       break;

     if (status == ENTERKEY && iChoice == 0) {
        if ((iTotalQuestions+iTotalCorrect+iTotalSkipped+iTotalOverTime) == 0)
          Bronx();
        else {
          HideMickey();
          PrintScore(rawbuffer, ysave, xsave);   // erase old score
          iTotalQuestions = iTotalCorrect = iTotalSkipped = iTotalOverTime = 0;
          PrintScore(NULL, ysave, xsave);        // print cleared score
          ShowMickey(iMousex, iMousey);
          KeyClick();
        }
      }
  }
  HideMickey();
  return SUCCESS;

}


/* ---------------------------------------------------------------------- */
/* Eat Keypresses to avoid run-on from full keyboard buffer...            */
/* ---------------------------------------------------------------------- */
int EatKeys()
{
  if (kbhit())
    while (kbhit())
      if (getch() == FUNCKEY)
         getch();

  return SUCCESS;
}


/* ---------------------------------------------------------------------- */
/* The WorkBook Selection Menu                                            */
/* ---------------------------------------------------------------------- */
int SelectWorkBook()
{
  struct find_t wild_card;
  uchar szBuffer[MENUMAX][40],
       szWork[100];
  int xoff, yoff, width, height, status;
  int iChoice,
      iOldChoice,
      iLastChoice;

  if(VCXREAD("WorkBack.Pcx",rawbuffer))return FAILURE;
  vload();
  SetMenu(0, 0, 0, 0, -1);  /* Clear The Menu Array */

  /* define cursor hotspot and start of text coordinates */
  yoff =   YMIN;
  xoff =   XMIN+20;
  width =  XMAX-40;
  height = FONTHEIGHT;    /* height of a font */

  for (iChoice = 0; iChoice < TITLEMAX; iChoice++)
    OutlineFont(MenuTitle[iChoice], xoff, yoff+=10, NULL);

  OutlineFont("WorkBook Selection Menu", xoff, yoff+=16, NULL);
  sprintf(szWork, "Current: %s", szCurrentWorkBook);
  OutlineFont(szWork, xoff, yoff+=10, NULL);
  OutlineFont(MenuTitle[TITLELAST], xoff, yoff+=10, NULL);

  /* walk through the directory and get up to 5 WorkBooks */
  /* not bothering to sort the workbooks at this time...  */

  iChoice = 0;
  if(_dos_findfirst("*.dbf",_A_NORMAL,&wild_card)==0) {
    for (;;) {
       strcpy(szBuffer[iChoice], wild_card.name);
       OutlineFont(szBuffer[iChoice], FINGER_PIXELWIDTH,
         SetMenu(FINGER_PIXELWIDTH, yoff+=16, width, height, iChoice), NULL);
       iChoice++;
       if (iChoice == LAST_MENU_CHOICE)  /* leave a menu spot for [ESC] */
          break;
       if(_dos_findnext(&wild_card)!=0)
         break;
    }
  }

  OutlineFont(MenuTitle[TITLELAST], xoff, yoff+=16, NULL);
  strcpy(szBuffer[iChoice], "Press [ESC] to Return To Options");
  OutlineFont(szBuffer[iChoice], xoff,
    SetMenu(xoff, yoff+=10, width, height, iChoice), NULL);
  iLastChoice = iOldChoice = iChoice;
  vsave();  /* save away the finished screen */

  /* set-up initial mouse coordinates etc. and show cursor*/
  SwapChar(&drawcolor,&outlinecolor);

  OutlineFont(szBuffer[iChoice],
              iMouseMenu[iChoice][0], iMouseMenu[iChoice][1], NULL);
  SwapChar(&drawcolor,&outlinecolor);
  iMousex = midpoint(iMouseMenu[iChoice][0],iMouseMenu[iChoice][2]);
  iMousey = midpoint(iMouseMenu[iChoice][1],iMouseMenu[iChoice][3]);
  VerticalMickeyMinMax(iMouseMenu[0][1], iMouseMenu[iLastChoice][3]);
  ShowGraphicsArray(0,iMouseMenu[iChoice][1], FingerHand,FRAMEADDR,
                      FINGER_PIXELWIDTH, FINGER_HEIGHT, rawbuffer);
  EatKeys();
  ShowMickey(iMousex, iMousey);

  for (;;)  {
     status = FALSE;
     if (kbhit()) {
        status = getch();
        if (status == FUNCKEY) {
           switch(getch()) {
              case PGUP:
              case LTARROW:
              case UPARROW:
                  iChoice--; if (iChoice < 0)iChoice=iLastChoice;break;
              case PGDOWN:
              case RTARROW:
              case DOWNARROW:
                  iChoice++; if (iChoice > iLastChoice)iChoice=0;break;
              case ENDKEY:
                  iChoice = iLastChoice;break;
              case HOMEKEY:
                  iChoice = 0;break;
           }
           if (iChoice != iOldChoice) {
              iMousex =
                midpoint(iMouseMenu[iChoice][0],iMouseMenu[iChoice][2]);
              iMousey =
                midpoint(iMouseMenu[iChoice][1],iMouseMenu[iChoice][3]);
           }
        }
     }
     if (iChoice == iOldChoice) {
        status = HotClick(status, &iChoice, MOUSE_MENU);
     }
     if (iChoice != iOldChoice) {
       HideMickey();
       ShowGraphicsArray(0,iMouseMenu[iOldChoice][1], NULL,FRAMEADDR,
                         FINGER_PIXELWIDTH, FINGER_HEIGHT, rawbuffer);
       OutlineFont(szBuffer[iOldChoice],
              iMouseMenu[iOldChoice][0], iMouseMenu[iOldChoice][1], NULL);
       SwapChar(&drawcolor,&outlinecolor);
       OutlineFont(szBuffer[iChoice],
              iMouseMenu[iChoice][0], iMouseMenu[iChoice][1], NULL);
       SwapChar(&drawcolor,&outlinecolor);
       iOldChoice = iChoice;
       ShowGraphicsArray(0,iMouseMenu[iChoice][1], FingerHand,FRAMEADDR,
                         FINGER_PIXELWIDTH, FINGER_HEIGHT, rawbuffer);
       ShowMickey(iMousex, iMousey);
     }
     if ((status == ESCKEY) || (status == ENTERKEY))
       break;
  }
  HideMickey();
  if (status == ESCKEY || iChoice == iLastChoice)
    return FAILURE;

  /* load the new workbook */
  if (SUCCESS != strcmpi(szCurrentWorkBook, szBuffer[iChoice])) {
    strcpy(szCurrentWorkBook, szBuffer[iChoice]);
    OpenDataBase(szBuffer[iChoice]);
  }
  return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* The Option Menu                                                        */
/* ---------------------------------------------------------------------- */
int ShowOptions()
{
  int xoff, yoff, width, height, status;
  int iChoice,
      iOldChoice;
  uchar *curptr;

  if(VCXREAD("WorkMenu.Pcx",rawbuffer))
    return FAILURE;

  zap(ASCIIZ);
  loadpalette();
  hilo();

  iOldChoice = 0;
  curptr = (uchar *)&WormCursor[1][0][0];
  for (;;) {
    vload();

    /* set-up initial mouse coordinates etc. and show cursor*/
    iChoice = iOldChoice;
    iMousex = midpoint(iMainMouse[iChoice][0],iMainMouse[iChoice][2]);
    iMousey = midpoint(iMainMouse[iChoice][1],iMainMouse[iChoice][3]);
    VerticalMickeyMinMax(iMainMouse[0][1], iMainMouse[5][3]);
    ShowGraphicsArray(iCursorCords[iChoice][0], iCursorCords[iChoice][1],
                      curptr, FRAMEADDR, CUR_WIDTH, CUR_HEIGHT, rawbuffer);
    Musicon(FRAMEADDR, rawbuffer);
    EatKeys();
    ShowMickey(iMousex, iMousey);

    KeyClick();
    for (;;) {
       status = FALSE;
       if (kbhit()) {
          status = getch();
          if (status == FUNCKEY) {
             switch(getch()) {
                case F1: iChoice = CONTINUE_CHOICE;
                         status = ENTERKEY;
                         break; /* return to game */
                case F2: iChoice = SCORE_CHOICE;
                         status = ENTERKEY;
                         break; /* showscore */
                case F3: iChoice = HELP_CHOICE;
                         status = ENTERKEY;
                         break; /* help */
                case F4: iChoice = SELECT_CHOICE;
                         status = ENTERKEY;
                         break; /* select a different workbook */
                case F5: iChoice = SOUND_CHOICE;
                         status = ENTERKEY;
                         break; /* sound */
                case F6: iChoice = EXIT_CHOICE;
                         status = ENTERKEY;
                         break; /* exit */
                case LTARROW:
                    curptr = (uchar *)&WormCursor[0][0][0];
                    iChoice--;
                    if (iChoice < 0)iChoice=5;break;
                case PGUP:
                case UPARROW:
                    iChoice-=3;
                    if (iChoice < 0)iChoice+=6;break;
                case RTARROW:
                    curptr = (uchar *)&WormCursor[1][0][0];
                    iChoice++;
                    if (iChoice > 5)iChoice=0;break;
                case DOWNARROW:
                case PGDOWN:
                    iChoice+=3;
                    if (iChoice > 5)iChoice-=6;break;
                case ENDKEY:
                    iChoice = 5;break;
                case HOMEKEY:
                    iChoice = 0;break;
             }
             if (iChoice != iOldChoice) {
                iMousex =
                  midpoint(iMainMouse[iChoice][0],iMainMouse[iChoice][2]);
                iMousey =
                  midpoint(iMainMouse[iChoice][1],iMainMouse[iChoice][3]);
             }
          }
       }
       if (iChoice == iOldChoice) {
          status = HotClick(status, &iChoice, MAIN_MENU);
       }
       if (iChoice != iOldChoice) {
         HideMickey();

         ShowGraphicsArray(iCursorCords[iOldChoice][0], iCursorCords[iOldChoice][1],
                           NULL, FRAMEADDR,CUR_WIDTH, CUR_HEIGHT, rawbuffer);



         iOldChoice = iChoice;
         /* We allow a right of left directiron on the orientation of the */
         /* Worm Cursor... si when we hit the left edge we use the right  */
         /* orientation, and Vice-Versa...                                */

         if (iChoice == 0 || iChoice == 3)
             curptr = (uchar *)&WormCursor[1][0][0];
         if (iChoice == 2 || iChoice == 5)
             curptr = (uchar *)&WormCursor[0][0][0];

         ShowGraphicsArray(iCursorCords[iChoice][0], iCursorCords[iChoice][1],
                           curptr, FRAMEADDR, CUR_WIDTH, CUR_HEIGHT, rawbuffer);
         ShowMickey(iMousex, iMousey);
       }

       /* if the right mouse, or enter on choice 4, or the F4 key was pressed */
       /* we are out of here */
       if ((status == ESCKEY) ||
           (status == ENTERKEY && (iChoice != SOUND_CHOICE)))
         break;

       /* Sound Toggle */
       if (status == ENTERKEY && iChoice == SOUND_CHOICE) {
          KeyClick();  // if the soundflag is true this will click...
          iSoundFlag = (FALSE == iSoundFlag);
          KeyClick();  // if the soundflag was just changed this will click.
          Musicon(FRAMEADDR, rawbuffer);
       }
    }
    KeyClick();
    HideMickey();
    if (status == ESCKEY ||
        iChoice == EXIT_CHOICE ||
        iChoice == CONTINUE_CHOICE)
      break;
    switch(iChoice) {
      case SELECT_CHOICE:
        SelectWorkBook(); break;
      case HELP_CHOICE:
        ShowHelp(); break;
      case SCORE_CHOICE:
        ShowScore(); break;
    }
    if(VCXREAD("WorkMenu.Pcx",rawbuffer))
      break;
  }
  if (status == ESCKEY || iChoice == CONTINUE_CHOICE)
    return SUCCESS;
  else
    return FAILURE;
}


// helper function to show elapsed time
// suggested by ZDNET reviewer July 1999...

void ShowElapsedTime(int *temptime)
{
  int testtime;
  static char timebuf[20]="";
  int x = 267,                // absolute co-ordinates
      y = 148;                // puts elapsed time in the framed area

  testtime = (int)time_out - elapsed_time;
  if (testtime != temptime[0]) {
    OutLineFont(timebuf,x,y, framebuffer);
    sprintf(timebuf, "%002d", testtime);
    OutLineFont(timebuf,x,y, NULL);
    temptime[0] = testtime;
   }
}


/* ---------------------------------------------------------------------- */
/* The WorkBook Core Function                                             */
/* ---------------------------------------------------------------------- */
int WordGame(uchar *name, uchar *str,int xorigin,int yorigin)
{
      int  target=strlen(str),counter=0,status=0,woopsflag=0, yoff=0, kb;
      int  temptime = INVALID;
      uchar interbuf[128],
           c=0x20,
           mb;

      uchar tempchar;

      iTotalQuestions++;  /* increment total */


      /* time limit through the loop              */
      /* and 40 character sentence buffer length  */
      strcpy(interbuf,"                                        ");
      xorigin=xorigin-((4*target)+1);

      /* change to dark outline */
      /* light drawcolor        */
      OutlineFont(str,xorigin,yorigin, NULL);
      // seed the elapsed time display...
      ShowElapsedTime(&temptime);

      SwapChar(&outlinecolor, &drawcolor);

      /* a strcmpi with visual feedback */
      start_timer();

      do {
        c=0x20;
        kb = kbhit();
        mb = ClickOnly();

        if (!kb && !mb) {
          if (check_timer()!=VALID) {
            Bronx();
            zap(ASCIIZ);
            stop_timer();
            iTotalOverTime++;
            return TIME_EXPIRED;
          }
          SwapChar(&outlinecolor, &drawcolor);
          ShowElapsedTime(&temptime);
          SwapChar(&outlinecolor, &drawcolor);

        }
        else {
          if (mb == ENTERKEY || mb == ESCKEY)
            c=mb;
          else
            c=getch();

          woopsflag=-1;
          if (c==FUNCKEY || mb == ENTERKEY) {
            woopsflag=0;
            stop_timer();
            if (mb == ENTERKEY) {
              c  = FUNCKEY;
              mb = PGUP;
            }
            else
              mb = getch();

            switch(mb) {
            /* if we get a Function Key press, play a musical note */
            case F1: sound(Frequency[FNOTES[0][0]][FNOTES[0][1]],2);
                     break;
            case F2: sound(Frequency[FNOTES[1][0]][FNOTES[1][1]],2);
                     break;
            case F3: sound(Frequency[FNOTES[2][0]][FNOTES[2][1]],2);
                     break;
            case F4: sound(Frequency[FNOTES[3][0]][FNOTES[3][1]],2);
                     break;
            case F5: sound(Frequency[FNOTES[4][0]][FNOTES[4][1]],2);
                     break;
            case F6: sound(Frequency[FNOTES[5][0]][FNOTES[5][1]],2);
                     break;
            case F7: sound(Frequency[FNOTES[6][0]][FNOTES[6][1]],2);
                     break;
            case F8: sound(Frequency[FNOTES[7][0]][FNOTES[7][1]],2);
                     break;
            case F9: sound(Frequency[FNOTES[8][0]][FNOTES[8][1]],2);
                     break;
            case F10: sound(Frequency[FNOTES[9][0]][FNOTES[9][1]],2);
                      break;
            case UPARROW:
            case LTARROW:
            case PGUP:
              /* if an UP-Key is pressed show the picture */
              vload();
              ShowMickey(iMousex, iMousey);
              MousePause();
              SaveMousePosition();
              HideMickey();

              /* wait for the next keypress, then blurr it again */
              /* and resume where we left-off */

              SwapChar(&outlinecolor, &drawcolor);
              blurrload();
              OutlineFont(str,xorigin,yorigin, NULL);
              temptime = INVALID; // force redisplay
              ShowElapsedTime(&temptime);
              SwapChar(&outlinecolor, &drawcolor);
              OutlineFont(interbuf,xorigin,yorigin, NULL);
              break;
            case DOWNARROW:
            case RTARROW:
            case PGDOWN:
              /* if a DOWN-Key is pressed, just advance */
              iTotalSkipped++;
              return FRAME_ADVANCE;
            }
            start_timer();
            continue;
          }

          if (c==ESCKEY){
            stop_timer();
            /* if an escape key is pressed, show the program options */
            iTotalQuestions--;  /* don't count the last one */
            status = ShowOptions();
            if (status == FAILURE)
              return status;
            if (BMPREAD(name, rawbuffer))
              if(VCXREAD(name,rawbuffer))
                if(BVXREAD(name))return FAILURE;
            zap(ASCIIZ);
            loadpalette();
            hilo();
            blurrload();
            OutlineFont(str,xorigin,yorigin, NULL);
            temptime = INVALID; // force redisplay
            ShowElapsedTime(&temptime);
            SwapChar(&outlinecolor, &drawcolor);
            OutlineFont(interbuf,xorigin,yorigin, NULL);

            status = SUCCESS;
            iTotalQuestions++;  /* reset the last one */
            EatKeys();
            woopsflag=0;
            iMousex = XMOS;
            iMousey = YMOS;
            start_timer();
            continue;

          }
        }

        /* if a standard key is pressed */
        /* then check it to see if it is the next one we are looking for */

        /* this is part of a continous loop */
        /* while we wait for a keypress     */
        /* so the key evaluation is a little different than if we */
        /* are trolling for modal keypresses... */

        if (toupper(str[counter])==toupper(c)) {
          /* success, so advance the buffer pointer */
          stop_timer();
          for (;;)  {
            interbuf[counter]=str[counter];
            OutlineFont(interbuf,xorigin,yorigin, NULL);
            counter++;
            woopsflag=0;
            KeyClick();
            c = toupper(str[counter]);
            if (c >= 'A' && c <= 'Z')
              break;
            if (c >= '0' && c <= '9')
              break;
            if (c==0)
              break;
          }
          start_timer();
        }
        if(woopsflag==-1) {
          woopsflag=0;
          Woops();
        }
      } while (target != counter);

      /* success... reward the kid... */
      stop_timer();
      vload();
      iTotalCorrect++;
      return status;

}


int PauseTime(int seconds)
{
  time_out = 1.0 * seconds;
  start_timer();
  for (;;) {
    if (check_timer() != VALID)break;
    if (kbhit()) {
      while (kbhit())
        if (getch() == FUNCKEY)
          getch();
      break;
    }
    if (check_timer() != VALID)break;
    if (ClickOnly())
      break;
  }
  stop_timer();
  return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* Initially loads the image and calls the WorkBook Core Function         */
/* ---------------------------------------------------------------------- */
int PlayTheGame(uchar *name,uchar *buffer,int rate)
{

    int xmos=XMOS, ybos=YMAX-FONTHEIGHT, status = SUCCESS;
    time_out=1.0*rate;   /* set-up global timeout for this picture */
    if (BMPREAD(name, rawbuffer))
      if(VCXREAD(name,rawbuffer))
        if(BVXREAD(name))return FAILURE;
    zap(ASCIIZ);
    loadpalette();
    hilo();
    blurrload();
    status = WordGame(name, buffer, xmos, ybos);
    return (status);
}


/* ---------------------------------------------------------------------- */
/* Structure to read a field descriptor from a DBASEIII .DBF File         */
/* ---------------------------------------------------------------------- */
struct FIELD
    {
     uchar name[11];
     uchar type[2] ;
     uchar length  ;
     uchar decimal ;
};

unsigned long lDbaseCount,
              lPosition,
              lNumberOfRecords,
              lRecordNumber;
uint uiHeaderLength,
     uiRecordLength;

struct FIELD field[4];

int fhDataBaseFile = INVALID;

/* ---------------------------------------------------------------------- */
/* Open The WorkBook Script                                               */
/* read the script header - dbaseIII format                               */
/* ---------------------------------------------------------------------- */
int OpenDataBase(uchar *pszScript)
{
  if (fhDataBaseFile != INVALID)
    close(fhDataBaseFile);
  if((fhDataBaseFile=open(pszScript,O_RDONLY|O_BINARY))==FAILURE)
   {
      setcrtmode(TEXT);
      perror(pszScript);
      exit(1);
   }

   lseek(fhDataBaseFile,4l,SEEK_SET);
   read(fhDataBaseFile,(uchar *)&lNumberOfRecords,4);
   lseek(fhDataBaseFile,8l,SEEK_SET);
   read(fhDataBaseFile,(uchar *)&uiHeaderLength,2);
   lseek(fhDataBaseFile,10l,SEEK_SET);
   read(fhDataBaseFile,(uchar *)&uiRecordLength,2);
   lPosition=32l;
   lDbaseCount=0;
   do{
     lseek(fhDataBaseFile,lPosition,SEEK_SET);
     read(fhDataBaseFile,field[lDbaseCount].name,10);
     field[lDbaseCount].name[10]=0;
     lseek(fhDataBaseFile,lPosition+11,SEEK_SET);
     read(fhDataBaseFile,field[lDbaseCount].type,1);
     field[lDbaseCount].type[1]=0;
     lseek(fhDataBaseFile,lPosition+16,SEEK_SET);
     read(fhDataBaseFile,(uchar *)&field[lDbaseCount].length,1);
     read(fhDataBaseFile,(uchar *)&field[lDbaseCount].decimal,1);
     lPosition+=32;
     lDbaseCount++;
   } while (lDbaseCount <4);

   lRecordNumber=1;
   return (fhDataBaseFile);

}

/* ---------------------------------------------------------------------- */
/* Main Program                                                           */
/* ---------------------------------------------------------------------- */
main(int argc, char **argv)
{

  /* database stuff */
  uchar dummy[513];

  /* other stuff */
  uchar pic[66] ;
  uchar text[66];
  uchar score[66];
  int picstat, status;
  int fh,i;
  int limit;
  int DONE = FALSE;


  if(argc==1)strcpy(szCurrentWorkBook,"WORKMAKE.DBF");
  else strcpy(szCurrentWorkBook,argv[1]);

  if((fh=open("WORKBOOK.FNT",O_RDONLY|O_BINARY))==FAILURE)
  {
    puts("Sorry. WORKBOOK.FNT font not found...");
    exit(0);
    }

  if ((ramfont= malloc(FONTSIZE))==NULL)
   {
    puts("Sorry... Insufficient Memory.");
    close(fh);
    exit(0);
    }

  read(fh,ramfont,FONTSIZE);
  close(fh);

  /* open script and read header */
  OpenDataBase(szCurrentWorkBook);

  memset((uchar*)&rgbinfo[0][0],0,(256*3));

  if (((framebuffer= malloc(MAX_BUFFSIZE))==NULL) ||
      ((rawbuffer= malloc(MAX_BUFFSIZE))==NULL)) {
    puts("Sorry... Insufficient Memory.");
    close(fhDataBaseFile);
    exit(0);
  }

  if (setcrtmode(MCGA)!=MCGA) {
    puts("Sorry... VGA adapter required.");
    close(fhDataBaseFile);
    exit(0);
  }

  /* opening routine */
  DONE = TRUE;
  for (;;) {
    if (VCXREAD("WorkFram.pcx",framebuffer))break;
    MakeGraphicsArrays();
    HiMickey();
    if (ShowTitle()) break;
    DONE = ShowOptions();
    if (DONE)ShowTitle();
    break;
  }

  /* end of opener */
  while( !(DONE))
  {
    /* read records */
    /* 1. read picture name */
    lPosition= 1l*(uiHeaderLength+1) + (lRecordNumber-1)*uiRecordLength;
    lseek(fhDataBaseFile,lPosition,SEEK_SET);
    read(fhDataBaseFile,dummy,field[0].length);
    dummy[field[0].length]= ASCIIZ;
    trim(dummy);
    strcpy(pic,dummy);
    lPosition+=field[0].length;
    /* 2. read caption */
    lseek(fhDataBaseFile,lPosition,SEEK_SET);
    read(fhDataBaseFile,dummy,field[1].length);
    dummy[field[1].length]= ASCIIZ;
    trim(dummy);
    strcpy(text,dummy);
    lPosition+=field[1].length;
    /* 3. read time limit */
    lseek(fhDataBaseFile,lPosition,SEEK_SET);
    read(fhDataBaseFile,dummy,field[2].length);
    dummy[field[2].length]= ASCIIZ;
    limit=atoi(dummy);
    if(limit<5||limit>30)limit=10;
    lPosition+=field[2].length;
    /* 4. Read sound file name */
    lseek(fhDataBaseFile,lPosition,SEEK_SET);
    read(fhDataBaseFile,dummy,field[3].length);
    dummy[field[3].length]= ASCIIZ;
    trim(dummy);
    strcpy(score,dummy);
    /* update record counter */
    lRecordNumber++;
    if(lRecordNumber>lNumberOfRecords)lRecordNumber=1;

    picstat= PlayTheGame(pic,text,limit);

    if(picstat==FAILURE) {
      ShowTitle();
      DONE=TRUE;
    }
    else{
      if(picstat==SUCCESS) {
         ShowMickey(iMousex, iMousey);
         picstat=csong(score);
         HideMickey();
      }
    }
  }

  close(fhDataBaseFile);
  setcrtmode(TEXT);
  puts(" Have a Nice Dos!");
  exit(0);

}

/* ----------------------------------------------------------------------- */
/* Sound Functions                                                         */
/* ----------------------------------------------------------------------- */
#define TIMEOUT 0x2c00

int sound(freq,tlen)
int freq;         /* freq of sound in Hertz                  */
int tlen;         /* duration of sound 18.2 ticks per second */
{
    union REGS inregs,outregs;
    uint frdiv = 1331000L/freq;   /* timer divisor */
    int seed,hiseed,hold=0,setting;

    if(!iSoundFlag || !tlen)return FALSE;

    if(freq!=32767)
    {
      outp(0x43,0xB6) ;            /* write timer mode register */
      outp(0x42,frdiv & 0x00FF);   /* write divisor LSB */
      outp(0x42,frdiv >> 8);       /* write divisor MSB */
      setting= inp(0x61);          /* get current port B setting */
      outp(0x61,setting | 0x03);   /* turn speaker on */
    }

    if (tlen>0) {
      tlen=((tlen*1000)/182); /* convert from 18.2 ticks to 100ths */
      inregs.x.ax= TIMEOUT;
      int86(0x21,&inregs,&outregs);
      seed=(outregs.x.dx)&0xff;
 
      while(hold<tlen)
      {

        inregs.x.ax = TIMEOUT;
        int86(0x21,&inregs,&outregs);
        if(seed!=(outregs.x.dx&0xff))
        {
          hiseed= (outregs.x.dx)&0xff;
          if(hiseed<seed)hiseed+=100;
          hold+=(hiseed-seed);
          seed =(outregs.x.dx)&0xff;
        }
      }
    }

    if(freq!=32767)
    {
      /* restore port B setting */
      outp(0x61,setting);
    }
    return SUCCESS;
}


int KeyClick(void)
{
  sound(1046,1);
  sound(1318,1);
  sound(1568,1);
  return (0);
}

int Bronx(void)
{
  sound(60,4);
  sound(40,8);
  return (0);
}

int Woops()
{
   sound(165,1);
   sound(147,1);
   sound(131,1);
   return 0;
}

/* ---------------------------------------------------------------------- */
/* Opening Song                                                           */
/* ---------------------------------------------------------------------- */
int prelude(int sndarry[])
{
    int i=0, freq, duration;

    EatKeys();
    /* array structure is frequency,duration */
    while((freq=sndarry[i])!=-1)
    {
        if (kbhit())break;
        if(clickonly())break;

        i++;
        duration=sndarry[i];
        sound(freq,duration);
        i++;
    }
    EatKeys();
    return (0);

}

/* ---------------------------------------------------------------------- */
/* Play a sound file through the PC Speaker.                              */
/* ---------------------------------------------------------------------- */
int csong(uchar *sndfile)
{

  FILE *fp;
  int frequency, duration, status = SUCCESS;
  uchar c;

  EatKeys();
  if (iSoundFlag == FALSE) {
    PauseTime(10);
  }
  else {
    if((fp=fopen(sndfile,"rb"))==NULL)return -2;
    while((frequency=getw(fp))!=FAILURE)
     {
      if(kbhit()){
        c=getch() ;
        if(c==FUNCKEY)getch();
        if(c==ESCKEY)status = FAILURE;
        break;
      }
      c = ClickOnly();
      if (c) {
        if(c==ESCKEY)status = FAILURE;
        break;
      }
      duration=fgetc(fp);
      sound(frequency,duration);
    }
    fclose(fp);
  }
  EatKeys();
  return (status);
}

/* ---------------------------------------------------------------------- */
/* Mouse Functions and Mouse Navigation Helper Functions                  */
/* ---------------------------------------------------------------------- */

int SaveMousePosition()
{
  union REGS inregs, outregs; /* mouse registers */

  if (bMickeyMouse == TRUE) {
    inregs.x.ax = 3;
    int86(0x33,&inregs,&outregs);
    iMousex=outregs.x.cx/2;
    iMousey=outregs.x.dx;
  }
  return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* Hotclick is called by HotMain to return mouse status and cursor        */
/* position. Mouse status is mapped to equivalent key code returns.       */
/* ---------------------------------------------------------------------- */
int HotClick(int iRetVal, int *choice, int iMenuType)
{
  union REGS inregs, outregs; /* mouse registers */
  int x;

  if (bMickeyMouse == TRUE) {

    inregs.x.ax = 3;
    int86(0x33,&inregs,&outregs);
    iMousex=outregs.x.cx/2;
    iMousey=outregs.x.dx;

    if(outregs.x.bx)
     {
        if (outregs.x.bx == 1)
          iRetVal = ENTERKEY;
        else if (outregs.x.bx == 2)
          iRetVal = ESCKEY;
        while(outregs.x.bx)int86(0x33,&inregs,&outregs);
      }
      else {
         /* we only want valid mouse positions */
         x = GetMenuChoice(iMousex, iMousey, iMenuType);
         if (x!=INVALID)
           choice[0] = x;
      }
   }
   return (iRetVal);
}


/* ---------------------------------------------------------------------- */
/* Clickonly - returns a left or a right mouse click as a key value       */
/*             if the mouse is installed.                                 */
/* ---------------------------------------------------------------------- */
int ClickOnly()
{
  union REGS inregs, outregs; /* mouse registers */
  int iRetVal = 0;

  if (bMickeyMouse == TRUE) {

    inregs.x.ax = 3;
    int86(0x33,&inregs,&outregs);

    if(outregs.x.bx)
     {
        if (outregs.x.bx == 1)
          iRetVal = ENTERKEY;            /* left button */
        else if (outregs.x.bx == 2)
          iRetVal = ESCKEY;              /* right button */

        while(outregs.x.bx)int86(0x33,&inregs,&outregs);
      }
   }
   return (iRetVal);
}


/* ---------------------------------------------------------------------- */
/* MousePause wait for a mouse button or a key press...                   */
/* ---------------------------------------------------------------------- */
int MousePause()
{
  for (;;) {
    if (kbhit()) {
      while (kbhit())
        if (getch() == FUNCKEY)
          getch();
      break;
    }
    if (ClickOnly())
      break;
  }
  return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* HideMickey                                                             */
/* Hide the mouse cursor.                                                 */
/* ---------------------------------------------------------------------- */
int HideMickey()
{
  union REGS inregs, outregs; /* mouse registers */
  if (bMickeyMouse == TRUE)
    {
      inregs.x.ax = 2;
      int86(0x33,&inregs,&outregs);
    }
  return bMickeyMouse;
}


/* ---------------------------------------------------------------------- */
/* ShowMickey                                                             */
/* Show (Unhide) The Mouse Cursor.                                        */
/* ---------------------------------------------------------------------- */
int ShowMickey(int ix, int iy)
{
  union REGS inregs, outregs; /* mouse registers */

  if (bMickeyMouse == FALSE)
     return FALSE;

   iMousex = ix;
   iMousey = iy;

   inregs.x.ax = 4;
   inregs.x.cx = iMousex*2;
   inregs.x.dx = iMousey;
   int86(0x33,&inregs,&outregs);   /* start */

   inregs.x.ax = 1;
   int86(0x33,&inregs,&outregs);   /* show  cursor*/
   return TRUE;
}


/* ---------------------------------------------------------------------- */
/* VerticalMickeyMinMax                                                   */
/* Set vertical limits of virtual mouse screen.                           */
/* Required to limit pasting of stickers in viewport.                     */
/* ---------------------------------------------------------------------- */
int VerticalMickeyMinMax(int ymin, int ymax)
{
  union REGS inregs, outregs; /* mouse registers */

  if (bMickeyMouse == FALSE)
     return FALSE;

   inregs.x.ax = 8;
   inregs.x.cx = ymin;
   inregs.x.dx = ymax;
   int86(0x33,&inregs,&outregs);   /* start */
   return TRUE;
}


/* ---------------------------------------------------------------------- */
/* HiMickey                                                               */
/* Check for a valid mouse driver and mouse.                              */
/* ---------------------------------------------------------------------- */
int HiMickey()
{
    union REGS inregs, outregs;
    struct SREGS segregs;
    long address;
    uchar first_byte;

    /* check for microsoft compatible mouse driver */
    inregs.x.ax = 0x3533;
    intdosx ( &inregs, &outregs, &segregs );
    address = (((long) segregs.es) << 16) + (long) outregs.x.bx;
    first_byte = * (long far *) address;
    if ((address == 0) || (first_byte == 0xcf)) return FALSE;

    /* check to see if the mouse is responding */
    inregs.x.ax=0;
    int86(0x33,&inregs,&outregs);
    if(outregs.x.ax==0)return FALSE;

    bMickeyMouse = TRUE;
    return bMickeyMouse;

}

/* ---------------------------------------------------------------------- */
/* Various Routines for Dealing With Graphics Arrays (sprites) etc.       */
/* ---------------------------------------------------------------------- */

uchar MusicalNote[20][12]={
0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
0,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
0,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
0,  158,  158,  198,  198,  198,  198,  198,  198,  198,  158,  158,
0,  158,  158,  198,    1,    1,    1,    1,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  198,  198,    1,  198,  158,  158,
0,  158,  158,  198,    1,    1,    1,    1,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  198,  198,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  158,  198,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  158,  198,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  158,  198,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  158,  198,    1,  198,  158,  158,
0,  158,  158,  198,    1,  198,  198,  198,    1,  198,  158,  158,
0,  158,  198,  198,    1,  198,  198,    1,    1,  198,  158,  158,
0,  158,  198,    1,    1,  198,  198,    1,    1,  198,  158,  158,
0,  158,  198,    1,    1,  198,  198,    1,    1,  198,  158,  158,
0,  158,  198,    1,    1,  198,  198,  198,  198,  198,  158,  158,
0,  158,  198,  198,  198,  198,  158,  158,  158,  158,  158,  158,
0,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,
0,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158,  158};

/* ---------------------------------------------------------------------- */
/* Musicon                                                                */
/* Plot a musical note if the sound is on.                                */
/* ---------------------------------------------------------------------- */
int Musicon(uchar *crtptr, uchar *framebuffer)
{
  uchar *dest, *src;
  uint y,x,temp;

  temp=(180*RASTERWIDTH)+308;

  for(y=0;y<20;y++)
  {
    dest=(uchar *)&crtptr[temp];
    if(iSoundFlag==TRUE)src=(uchar *)&MusicalNote[y][0];
    else src=(uchar *)&framebuffer[temp];
    for(x=0;x<12;x++)dest[x]=src[x];
    temp+=RASTERWIDTH;
  }
  return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* various graphics arrays etc                                            */
/* Note: These are in the c _putimage/_getimage PIC format for the CGA    */
/*       They are remapped to the VGA in the function MakeGraphicsArray   */
/* ---------------------------------------------------------------------- */

/* ---------------------------------------------------------------------- */
/* worm cursor and worm and bird animation etc.                           */
/* ---------------------------------------------------------------------- */

/* character array created from binary image fragment WORM1.PIC */
unsigned char far WORM1[116] = {
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85,127,213, 85, 85,255,213, 85,253,117, 95,255,255,
255,255,255,245,127,255,255,255,255,255,213,127,255,255,
255,255,255, 85, 95,255,213,127,255,213, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment WORM2.PIC */
unsigned char far WORM2[116] = {
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,255,
213, 85, 85, 85, 85,127,255,253, 85,127,213, 87,255,255,
255,253,253,117, 95,255,255,255,255,255,245, 95,255,213,
127,255,255,213, 87,213, 85, 87,255,255, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment WORM3.PIC */
unsigned char far WORM3[116] = {
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85,255,213, 85, 85, 85, 85, 87,255,
253, 85, 85, 85, 85, 95,255,253, 85,127,213, 85,255,255,
255,253,253,117, 87,255,213,127,255,255,245, 87,253, 85,
 87,255,255,213, 85,253, 85, 85,127,255, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment WORM4.PIC */
unsigned char far WORM4[116] = {
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 87,253, 85, 85, 85, 85, 85, 95,255,
213, 85, 85, 85, 85,127,255,245, 85, 85, 85, 85,127,255,
253, 85,127,213, 85,255,215,255, 85,253,117, 85,255, 85,
127,255,255,245, 85,255, 85,127,255,255,213, 85,125, 85,
 87,255,255, 85, 85, 85, 85, 85, 95,213, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};


/* character array created from binary image fragment BIRD1.PIC */
unsigned char far BIRD1[116]={
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85,105, 85, 85, 85, 85, 85, 85, 90,169, 85,
 85, 86,165, 85, 86,170,170,170,106,152, 85, 85,170,170,
170,170,168,  5, 85,106,170,170,170,165, 85, 85, 90,154,
170,170,149, 85, 85, 86,166,170,170,149, 85, 85, 85,169,
 85,169, 85, 85, 85, 85, 90,170,149, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment BIRD2.PIC */
unsigned char far BIRD2[116]={
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 90,169, 85,
 85, 86,165, 85, 86,170,170,170,106,152, 85, 85,170,170,
170,170,168,  5, 85,106,170,170,170,165, 85, 85, 90,154,
170,170,149, 85, 85, 86,106,170,170,149, 85, 85, 85,106,
169,105, 85, 85, 85, 85,106,169,149, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment BIRD3.PIC */
unsigned char far BIRD3[116]={
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 86,165, 85, 85, 85, 85,
 85,106,152, 85, 90,170,170,170,170,168,  5,106,170,170,
170,170,165, 85, 85,106,170,170,170,149, 85, 85, 90,154,
170,170,149, 85, 85, 86,106,170, 90, 85, 85, 85, 85,106,
169,105, 85, 85, 85, 85,106,169,149, 85, 85, 85, 85,170,
165, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment BIRD4.PIC */
unsigned char far BIRD4[116]={
 56,  0, 16,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 86,165, 85, 85, 85, 85,
 85,106,152, 85, 85, 85,106,170,170,168,  5, 86,170,170,
170,170,165, 85, 90,170,170,170,170,149, 85, 85, 90,154,
170,170,149, 85, 85, 86,106,170, 90, 85, 85, 85, 85,106,
169,105, 85, 85, 85, 85,106,169,149, 85, 85, 85, 85,170,
165, 85, 85, 85, 85, 86,170,149, 85, 85, 85, 85, 86,170,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85};

/* character array created from binary image fragment LWORM.PIC */
uchar far LWORM[804]={
136,  0, 47,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 64, 85, 85, 84,  5, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 64, 85, 85, 84,
  5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 64,
 85, 85, 84,  5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 84, 85, 85, 84, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 21, 85, 81, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 69, 85, 69, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 80,  0,
 21, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 76,252,197, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 63,255,241, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 84,240,252, 60, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 84,207, 51,204, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 84,207,
 51,204, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 84,195, 48,204, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 80,192,  0, 12, 21, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 67,255,255,255,  5, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 15,252,252,255,
193, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 63,
255,255,255,241, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 63, 63,255,243,241, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 48,255,255,252, 49, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 63, 63,255,243,241, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 63,192,  0,
 15,241, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 15,240,  0, 63,241, 85, 85, 85, 85,  0,  0,  0, 21, 85,
 85, 85, 85, 67,252,  0,255,193, 85, 85, 85, 84,  0, 63,
240,  5, 85, 85, 85, 85, 80,255,255,255,  5, 85, 85, 85,
 80,  0, 63,240,  1, 85, 85, 85, 85, 84, 63,  0,252, 21,
 85, 85, 85, 64,  0, 63,240,  0, 85, 85, 85, 85, 85, 15,
255,240, 85, 85, 85, 85,  0,  0, 63,240,  0, 21, 85, 85,
 85, 85, 64,  0,  1, 85, 85, 85, 84, 48,  0, 12,192,  0,
  5, 85, 85, 85, 85, 64,  0,  1, 85, 85, 85, 80,252,  0,
 51, 48,  0,197, 85, 84,  1, 85, 64,  0,  1, 85, 85, 85,
 83,255,  0,  0,  0,  3,197, 85, 80,  0, 85, 64,  0,  1,
 85, 85, 85, 83,255,192, 21, 80, 15,197, 85, 64,  0, 85,
 64,  0,  1, 85, 85, 85, 83,255, 48, 85, 84, 63,197, 85,
 12,  0, 85, 79,255, 49, 85, 85, 85, 83,255,204, 85, 85,
 15,193, 84, 63,  0, 85, 79,252,193, 85, 85, 85, 80,  0,
  0, 85, 85, 51,240,  0,252,193, 85, 79,255, 49, 85, 85,
 85, 80,  0,  0, 85, 85, 12,252,  3,243,  5, 85, 79,252,
193, 85, 85, 85, 80,  0,  0, 85, 85, 51, 48,  0,204, 21,
 85, 79,255, 49, 85, 85, 85, 80,  0,  0, 85, 85, 12,192,
  0, 48, 85, 85, 64,  0,  0, 85, 85, 85, 80,  0,  0, 85,
 85, 67,  0,  0,  1, 85, 85, 64,  0,  0,  0,  0,  0,  3,
252,204, 85, 85, 80,  0,  0,  5, 85, 85, 64,  0,  3,252,
  0, 15,192, 63, 48, 85, 85, 84,  0,  0, 21, 85, 85, 64,
  0, 15,252,  0, 15,240,  3,204, 85, 85, 85, 85, 85, 85,
 85, 85, 64,  0, 63,252,  0, 15,252,  0, 48, 85, 85, 85,
 85, 85, 85, 85, 85, 64,  0,204,204,  0, 12,207,  0,  0,
 85, 85, 85, 85, 85, 85, 85, 85, 80,  3, 51, 48,  0,  3,
 51,  0,  1, 85, 85, 85, 85, 85, 85, 85, 85, 84, 12,204,
204,  0, 12,204,192,  5, 85, 85, 85, 85, 85, 85, 85, 85,
 85,  0,  0,  0,  0,  0,  0,  0, 21, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85};

/* character array created from binary image fragment RWORM.PIC */
uchar far RWORM[804]={
136,  0, 47,  0, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 80, 21, 85, 85,  1, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 80, 21, 85, 85,  1, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 80, 21, 85,
 85,  1, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 21, 85, 85, 21, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 69, 85, 84, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 81, 85, 81, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 84,  0,  5, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 83,
 63, 49, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 79,255,252, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 60, 63, 15, 21, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 51,204,243, 21, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 51,204,243,
 21, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 51, 12,195, 21, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 84, 48,  0,  3,  5, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 80,255,255,255,193, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 67,255, 63, 63,240, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 79,255,255,
255,252, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 79,207,255,252,252, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 76, 63,255,255, 12, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 79,207,255,252,252, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 79,240,  0,  3,252,
 85, 85, 85, 85, 84,  0,  0,  0, 85, 85, 85, 85, 79,252,
  0, 15,240, 85, 85, 85, 85, 80, 15,252,  0, 21, 85, 85,
 85, 67,255,  0, 63,193, 85, 85, 85, 85, 64, 15,252,  0,
  5, 85, 85, 85, 80,255,255,255,  5, 85, 85, 85, 85,  0,
 15,252,  0,  1, 85, 85, 85, 84, 63,  0,252, 21, 85, 85,
 85, 84,  0, 15,252,  0,  0, 85, 85, 85, 85, 15,255,240,
 85, 85, 85, 85, 80,  0,  3, 48,  0, 12, 21, 85, 85, 85,
 64,  0,  1, 85, 64, 21, 85, 83,  0, 12,204,  0, 63,  5,
 85, 85, 85, 64,  0,  1, 85,  0,  5, 85, 83,192,  0,  0,
  0,255,197, 85, 85, 85, 64,  0,  1, 85,  0,  1, 85, 83,
240,  5, 84,  3,255,197, 85, 85, 85, 64,  0,  1, 85,  0,
 48, 85, 83,252, 21, 85, 12,255,197, 85, 85, 85, 64,  0,
  1, 85,  0,252, 21, 67,240, 85, 85, 51,255,197, 85, 85,
 85, 76,255,241, 85, 67, 63,  0, 15,204, 85, 85,  0,  0,
  5, 85, 85, 85, 67, 63,241, 85, 80,207,192, 63, 48, 85,
 85,  0,  0,  5, 85, 85, 85, 76,255,241, 85, 84, 51,  0,
 12,204, 85, 85,  0,  0,  5, 85, 85, 85, 67, 63,241, 85,
 85, 12,  0,  3, 48, 85, 85,  0,  0,  5, 85, 85, 85, 76,
255,241, 85, 85, 64,  0,  0,193, 85, 85,  0,  0,  5, 85,
 85, 85,  0,  0,  1, 85, 85, 80,  0,  0,  5, 85, 85, 51,
 63,192,  0,  0,  0,  0,  0,  1, 85, 85, 84,  0,  0, 21,
 85, 85, 12,252,  3,240,  0, 63,192,  0,  1, 85, 85, 85,
 85, 85, 85, 85, 85, 51,192, 15,240,  0, 63,240,  0,  1,
 85, 85, 85, 85, 85, 85, 85, 85, 12,  0, 63,240,  0, 63,
252,  0,  1, 85, 85, 85, 85, 85, 85, 85, 85,  0,  0,243,
 48,  0, 51, 51,  0,  1, 85, 85, 85, 85, 85, 85, 85, 85,
 64,  0,204,192,  0, 12,204,192,  5, 85, 85, 85, 85, 85,
 85, 85, 85, 80,  3, 51, 48,  0, 51, 51, 48, 21, 85, 85,
 85, 85, 85, 85, 85, 85, 84,  0,  0,  0,  0,  0,  0,  0,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
 85, 85, 85, 85, 85, 85};


/* character array created from binary image fragment FINGER.PIC */
unsigned char far FINGER[532]={
128,  0, 33,  0, 85, 85, 85, 85, 85, 84,  0, 85, 85, 85,
 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,  0,255,  0,
  1, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 64, 63,
252, 15,240, 85, 85, 85, 85, 85, 64, 21, 85, 85, 85, 85,
 15,255,240,255,252, 85, 85, 85, 85, 80, 15,  5, 85, 85,
 85, 64,255,255,195,255,252,  0, 85, 85, 80,  3,255,197,
 85, 85, 85, 15,255,255,255,255,192,252, 21, 84,  3,255,
255,  5, 85, 85, 84, 63,255,255,255,252, 15,255, 21,  0,
255,255,252, 21, 85, 85, 80,255,255,255,255,240,255,255,
  0, 63,255,255,  0, 85, 85, 85, 67,255,255,255,255,195,
255,252, 15,255,255,240, 21, 85, 85, 85, 15,255,255,255,
255,255,255,192,255,255,255,  1, 85, 85, 85, 84, 63,255,
255,255,255,255,252, 15,255,255,240, 21, 85, 85, 85, 80,
255,255,255,255,255,255,240,255,255,255,  1, 85, 85, 85,
 85, 67,255,255,255,255,255,255,195,255,255,240,  0, 85,
 85, 85, 85, 79,255,255,255,255,255,255,255,255,255,  3,
252, 21, 85, 85, 85, 15,255,255,255,255,255,255,255,255,
240, 63,255, 21, 85, 85, 84, 63,255,255,255,255,255,255,
255,255,  3,255,240, 21, 85, 85, 80,255,255,255,255,255,
255,255,255,240, 63,255,  1, 85, 85, 85, 67,255,255,255,
255,255,255,255,255,195,255,240, 21, 85, 85, 85, 15,255,
255,255,255,255,255,255,255,207,255,193, 85, 85, 85, 85,
255,255,255,255,255,255,255,255,255,255,255,  5, 85, 85,
 85, 85,255,255,255,255,255,255,255,255,255,255,252, 21,
 85, 85, 85, 85,255,255,255,255,255,255,255,255,255,255,
240, 85, 85, 85, 85, 85,255,255,255,255,255,255,255,255,
255,255,193, 85, 85, 85, 85, 85,255,255,255,255,255,255,
255,255,255,255,  5, 85, 85, 85, 85, 85,255,255,255,255,
255,255,255,255,255,240, 21, 85, 85, 85, 85, 85,255,255,
255,255,255,255,255,255,255,193, 85, 85, 85, 85, 85, 85,
127,255,255,255,255,255,255,255,252,  5, 85, 85, 85, 85,
 85, 85,127,255,255,255,255,255,255,255,192, 85, 85, 85,
 85, 85, 85, 85, 95,255,255,255,255,255,255,192,  5, 85,
 85, 85, 85, 85, 85, 85, 95,255,255,255,255,255,192,  5,
 85, 85, 85, 85, 85, 85, 85, 85, 87,255,255,255,255,240,
  5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,255,192,  0,
  0,  1, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,124,
  5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85};


/* ---------------------------------------------------------------------- */
/* MakeSprite - Called from MakeGraphicsArray                             */
/* Remaps the c _putimage/_getimage PIC format for the CGA to the VGA.    */
/* ---------------------------------------------------------------------- */
int MakeSprite(uchar *src, uchar *dest,
               uint bytewidth, uint height,
               uchar *cmap)
{
     uint x, y, idx;
     union cgapixel Pic;

     idx = 0;
     for(y=0;y<height;y++)
     {
       for(x=0;x<bytewidth;x++)
       {
         Pic.byt = src[(y*bytewidth)+x];
         dest[idx]=cmap[Pic.pixel.x1];idx++;
         dest[idx]=cmap[Pic.pixel.x2];idx++;
         dest[idx]=cmap[Pic.pixel.x3];idx++;
         dest[idx]=cmap[Pic.pixel.x4];idx++;
       }
     }
     return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* MakeGraphicsArray                                                      */
/* Called on startup to build the cursor and animation sprites.           */
/* Remaps the c _putimage/_getimage PIC format for the CGA to the VGA.    */
/* ---------------------------------------------------------------------- */
int MakeGraphicsArrays()
{
   uint x,y,i,j;

   uchar *pSrc, *pDest;

   /* hard coded color values */
   /* These are based purely on the palette that I have chosen for */
   /* the splash screens for the title and main menu, etc. */

   /* 230 is the transparent color (for the purposes used in this program) */
   /* 200 is RED    */
   /* 235 is BLUE   */
   /* 220 is PURPLE */
   /* 255 is GREEN  */
   /* 158 is WHITE  */

   /* in the arrays below, the CGA colors BLACK, CYAN, MAGENTA, WHITE */
   /* map to an ordinal index for the RGB LUT used by our VGA palette */

   uchar anicolor[4]={1,230,200,158};
   uchar curcolor[4]={1,230,200,158};
   uchar temp;

   // translate the Worm Animation from the CGA Palette
   for(i=0; i<ANI_MAX; i++)
   {
     switch(i) {
        case 0: pSrc = (uchar *)&WORM1[ANI_DATA];
                break;
        case 1: pSrc = (uchar *)&WORM2[ANI_DATA];
                break;
        case 2: pSrc = (uchar *)&WORM3[ANI_DATA];
                break;
        case 3: pSrc = (uchar *)&WORM4[ANI_DATA];
                break;
     }
     pDest = (uchar *)&WormAnimation[i][0][0],
     MakeSprite(pSrc, pDest, WORM_BYTEWIDTH, WORM_HEIGHT, anicolor);
   }
   // translate the Bird Animation from the CGA Palette
   for(i=0; i<ANI_MAX; i++)
   {
     switch(i) {
        case 0: pSrc = (uchar *)&BIRD1[ANI_DATA];
                break;
        case 1: pSrc = (uchar *)&BIRD2[ANI_DATA];
                break;
        case 2: pSrc = (uchar *)&BIRD3[ANI_DATA];
                break;
        case 3: pSrc = (uchar *)&BIRD4[ANI_DATA];
                break;
     }
     pDest = (uchar *)&BirdAnimation[i][0][0],
     MakeSprite(pSrc, pDest, BIRD_BYTEWIDTH, BIRD_HEIGHT, anicolor);
   }

   // translate the Worm Cursor for the main menu from the CGA Palette
   for(i=0; i<CUR_MAX; i++)
   {
     switch(i) {
        case 0: pSrc = (uchar *)&LWORM[CUR_DATA];break;
        case 1: pSrc = (uchar *)&RWORM[CUR_DATA];break;
     }
     pDest = (uchar *)&WormCursor[i][0][0];
     MakeSprite(pSrc, pDest, CUR_BYTES, CUR_HEIGHT, curcolor);
   }

   // translate other arrays from the CGA Palette
   MakeSprite((uchar *)&FINGER[CUR_DATA], FingerHand,
              FINGER_BYTEWIDTH, FINGER_HEIGHT, curcolor);

   return SUCCESS;
}


/* ---------------------------------------------------------------------- */
/* ShowGraphicsArray                                                      */
/* Called throughout the program to display cursor and animation sprites. */
/* ---------------------------------------------------------------------- */
int ShowGraphicsArray(int x, int y, uchar *src, uchar *dest,
                      int width, int height, uchar *framebuffer)
{
  int idx = 0, jdx = 0, y1, x1, xorg;
  uint offset = (unsigned) ((y*RASTERWIDTH) + x);

  xorg = x;
  for(y1=0;y1<height;y1++)
   {

     if (y >= SCREENHEIGHT)
       break;

     jdx = offset;
     x = xorg;
     for(x1=0;x1<width;x1++)
       {
         if (x < SCREENWIDTH && y < SCREENHEIGHT)
          {
             if (NULL == src) {
               dest[jdx] = framebuffer[jdx];
             }
             else {
               if (src[idx] == TRANSPARENT_COLOR) {
                  dest[jdx] = framebuffer[jdx];
                }
               else {
                  dest[jdx] = src[idx];
                }
             }
          }
          idx++;
          jdx++;
          x++;
       }
       offset+=RASTERWIDTH;
       y++;

   }
   return SUCCESS;
}

/* musical array created from file POMP&.SND */
/* array structure is frequency,duration */
int POMP[]={
  392, 14,  370,  3,  392,  3,
  440,  7,  330, 14,  294, 14,
  262, 14,  247,  3,  262,  3,
  294,  7,  220, 28,  247, 14,
  277,  3,  294,  7,  330,  3,
  440, 14,  294, 14,  392, 14,
  392,  3,  370,  7,  330,  3,
  294, 28,  392, 14,  370,  3,
  392,  3,  440,  7,  330, 14,
  294, 14,  262, 14,  247,  3,
  262,  3,  294,  7,  220, 28,
  247, 14,  277,  3,  294,  7,
  330,  3,  440, 14,  294, 14,
  523, 14,  523,  3,  494,  7,
  440,  3,  494,  7,  392,  7,
  370,  7,  349,  7,  330, 14,
  370,  3,  392,  7,  440,  3,
  294, 14,  392, 14,  196, 14,
  262,  3,  247,  7,  220,  3,
  196, 28, -1,-1};

/* ---------------------------------------------------------------------- */
/* Opening Animation Scene                                                */
/* I have kept this seperate from the game routines in order to make it   */
/* easier to read. I would have put this in a different source module     */
/* but I want to keep this altogether. (Less chance of something getting  */
/* lost over time.)                                                       */
/* ---------------------------------------------------------------------- */

int anigrid[2][2]={0,176,    /* the worm */
                   0,10};    /* the bird */

int aniwidth[2]  = {WORM_PIXELWIDTH, BIRD_PIXELWIDTH};
int aniheight[2] = {WORM_HEIGHT, BIRD_HEIGHT};
int anirate   = 8;  // rate of travel - 8 pixels

/* a place to keep the old timertick vector */
void (interrupt _far *int1C)();

/* Vector Sound Stuff */

int *tune;
uint  V_done = FALSE;
uint  V_ticker=0;
uint  V_tocker=0;
uint  V_ImageIdx=0;
int   V_soundcounter=0;

float     V_soundticker=(float)0;
float     V_duration;

#define   V_OFF 0
#define   V_ON  1
int       V_portB;
int       V_soundflag =V_OFF;

/* ---------------------------------------------------------------------- */
/* Set to initial values and plot first frame in the animation sequence   */
/* ---------------------------------------------------------------------- */
int InitAnimation()
{
    V_tocker=0;
    V_ImageIdx=1;
    ShowGraphicsArray(anigrid[0][0]+V_tocker,anigrid[0][1],
                      (uchar *)&WormAnimation[0][0][0],
                      FRAMEADDR, aniwidth[0], aniheight[0], rawbuffer);
    ShowGraphicsArray(anigrid[1][0]+V_tocker,anigrid[1][1],
                      (uchar *)&BirdAnimation[0][0][0],
                      FRAMEADDR, aniwidth[1], aniheight[1], rawbuffer);
    return SUCCESS;
}

/* ---------------------------------------------------------------------- */
/* our replacement for whatever was hooked to the DOS timer tick          */
/* ---------------------------------------------------------------------- */
void interrupt TimerTick()
{
    int frequency,temp;
    uint frdiv;

    _disable();

    /* if we have hit the target turn the speaker on */
    /* when we have finished turn it off again */
    /* and reset our counter to allow for the length of the sound */

    V_ticker++;
    V_soundticker++;
    if(V_soundticker >= V_duration && V_soundflag==V_ON)
    {
      if (iSoundFlag)
        outp(0x61,V_portB);       /* restore port B setting     */
                                  /* this turns the speaker off */
                                  /* reset the flags            */
      V_soundflag = V_OFF;
      V_soundticker = 0;
    }

    if(V_soundflag==V_OFF)
    {
      frequency = tune[V_soundcounter];
      V_soundcounter++;
      temp = tune[V_soundcounter];
      V_soundcounter++;

      /* safety play */
      while(frequency==0)
      {
        frequency = tune[V_soundcounter];
        V_soundcounter++;
        temp = tune[V_soundcounter];
        V_soundcounter++;
      }

      switch(frequency)
      {
        case -1:
          V_done++;
          V_soundcounter=0;
          V_soundticker =0;
          V_ticker=0;
          break;

        default:
          V_duration  = (float)temp;
          if (iSoundFlag) {
            frdiv = 1193180L/frequency; /* timer divisor              */
            outp(0x43,0xB6);            /* write timer mode register  */
            outp(0x42,frdiv & 0xFF);    /* write divisor LSB          */
            outp(0x42,frdiv >> 8);      /* write divisor MSB          */
            V_portB = inp(0x61);        /* get current port B setting */
            outp(0x61,V_portB | 0x03);  /* turn speaker on            */
          }
          V_soundflag = V_ON ;          /* set the sound flag ON      */
      }
    }

    _enable();
    int1C();
}

/* ---------------------------------------------------------------------- */
/* ShowTitle - Shows the title screen and plays the animation sequence    */
/* ---------------------------------------------------------------------- */
int ShowTitle()
{
  uchar c;
  int i=0,bFirstScreen = FALSE;
  union REGS inregs, outregs; /* mouse registers */
  uchar *aniptr;

  /* load the title screen */
  if(VCXREAD("WorkWise.pcx",rawbuffer))return INVALID;
  loadpalette();
  hilo();
  vload();

  /* prepare the animated graphics */
  InitAnimation();

  /* play the tune */
  tune=(uint *)&POMP[0];

  int1C=  _dos_getvect(0x1C);
  _dos_setvect(0x1C, TimerTick);

  /* waits here */

  if(kbhit())
  {
   c=getch();
   if(c == FUNCKEY)c=getch();
   }

  /* have a bird and worm merrygoround    */
  V_ticker=0;
  V_done = FALSE;

  while(!V_done)
  {

    if(bMickeyMouse == TRUE)
    {
      inregs.x.ax=3;
      int86(0x33,&inregs,&outregs);
      if(outregs.x.bx!=0)
      {
        V_done = TRUE;
        if(!bFirstScreen)V_ticker=10;
        while(outregs.x.bx!=0)int86(0x33,&inregs,&outregs);
      }
    }
    if(kbhit())
    {
       V_done = TRUE;
       if(!bFirstScreen)V_ticker=10; /* force the second screen display */
       c=getch();
       if(c == FUNCKEY)c=getch();
    }

    /* every 1/2 second (nine ticks) move the animation along */
    if(V_ticker>9)
    {
      V_ticker=0;
      // 1. restore the old screens...
      ShowGraphicsArray(anigrid[0][0]+V_tocker,anigrid[0][1],
                        NULL, FRAMEADDR, aniwidth[0], aniheight[0], rawbuffer);
      ShowGraphicsArray(anigrid[1][0]+V_tocker,anigrid[1][1],
                        NULL, FRAMEADDR, aniwidth[1], aniheight[1], rawbuffer);

      V_tocker+=anirate;     /* move ahead 8 pixels */
      if(((V_tocker+aniwidth[0]) >XMAX && (V_tocker+aniwidth[1]) >XMAX)||
         V_done)
      {                     /* we have walked off the screen...          */
         V_tocker=0;        /* setting the animation back to the left... */
         if(!bFirstScreen)
         {
           V_done = FALSE;
           bFirstScreen = TRUE;
           VCXREAD("WorkTit.pcx",rawbuffer);
           vload();
           EatKeys();
         }
       }
       // 2. put the next 2 images in the sequence in place
       if (V_ImageIdx > 3 || V_ImageIdx < 0) V_ImageIdx = 0;
       aniptr = (uchar *)&WormAnimation[V_ImageIdx][0][0];
       ShowGraphicsArray(anigrid[0][0]+V_tocker,anigrid[0][1],
                       aniptr,FRAMEADDR, aniwidth[0], aniheight[0], rawbuffer);
       aniptr = (uchar *)&BirdAnimation[V_ImageIdx][0][0];
       ShowGraphicsArray(anigrid[1][0]+V_tocker,anigrid[1][1],
                       aniptr,FRAMEADDR, aniwidth[1], aniheight[1], rawbuffer);

       V_ImageIdx++;
       if (V_ImageIdx > 3)V_ImageIdx=0;
     }

   }
  /* reset defaults */
  _dos_setvect(0x1c, int1C);

  /* restore initial values on the way into the program */
  /* so this will work properly on the way out. */
  /* especially we must reprogram the speaker back to its default */
  /* and turn the thing off... */

  if(V_soundflag==V_ON && iSoundFlag == TRUE)outp(0x61,V_portB);
  V_done = FALSE;
  V_soundticker=0;
  V_ticker=0;
  V_tocker=0;
  V_ImageIdx=0;
  V_soundcounter=0;
  V_soundflag=V_OFF;

  EatKeys();
  return SUCCESS;
}
