#include <conio.h>
#include <string.h>
#include <process.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <dos.h>
#include "types.h"
#include "sndcards.h"
#include "FX_man.h"
#include "music.h"
#include "develop.h"
#include "menudefs.h"
#include "menu_lib.h"
#include "common.h"
#include "gamedefs.h"
#include "menus.h"
#include "config.h"
#include "keyboard.h"
#include "control.h"
#include "_menus.h"
#include "util_lib.h"
#include "text_lib.h"
#include "scriplib.h"
#include "tokenlib.h"
#include "linklist.h"
#include "file_lib.h"
#include "wndo_lib.h"
#include "malloc.h"
#include "mnu_inpt.h"

#include "function.h"

#define LAUNCHPROG "launch.tmp"


int32 SaveSetup = -1;
static KeyEntryType * keydefs;



/*
===================
=
= DrawBackground
=
===================
*/

void DrawBackground( void )
   {
   char string[80];

   TextBox
      (
      0,
      1,
      maxtextwidth - 1,
      maxtextheight - 2,
      '°',
      MENUBACK_FOREGROUND,
      MENUBACK_BACKGROUND
      );
   DrawBorder( 0 );
   DrawBorder( maxtextheight - 1 );

   strcpy (string, SETUPPROGRAMNAME);
   strcat (string, " Version ");
   strcat (string, SETUPPROGRAMVERSION);
   DrawString
      (
      1,
      0,
      string,
      MENUBACKBORDER_FOREGROUND,
      MENUBACKBORDER_BACKGROUND
      );

   strcpy (string, "(c) 1995 3D Realms Entertainment");
   DrawString
      (
      maxtextwidth-1-strlen(string),
      0,
      string,
      MENUBACKBORDER_FOREGROUND,
      MENUBACKBORDER_BACKGROUND
      );
   }


//=================
//
// DoMainMenu
//
//=================

void DoMainMenu( void )
   {
   GetSetupFilename();
   DrawBackground();
   InitializeKeyDefList();
   ReadSetup();
   while (SaveSetup == -1)
      {
      HandleMenu( &MainMenu );
      HandleMenu( &QuitMenu );
      if (SaveSetup == 1)
         {
         WriteSetup(false);
         }
      }
   FreeKeyDefList();
   }

//=================
//
// SoundCardName
//
//=================

char * SoundCardName( int32 card )
   {
   switch (card)
      {
      case SoundBlaster:
         return "Sound Blaster\0";
         break;
      case ProAudioSpectrum:
         return "Pro Audio Spectrum\0";
         break;
      case SoundMan16:
         return "Sound Man 16\0";
         break;
      case Adlib:
         return "Adlib\0";
         break;
      case GenMidi:
         return "General Midi\0";
         break;
      case SoundCanvas:
         return "Sound Canvas\0";
         break;
      case Awe32:
         return "Awe 32\0";
         break;
      case WaveBlaster:
         return "Wave Blaster\0";
         break;
      case SoundScape:
         return "Sound Scape\0";
         break;
      case UltraSound:
         return "Gravis Ultrasound\0";
         break;
      case SoundSource:
         return "Sound Source\0";
         break;
      case TandySoundSource:
         return "Tandy Sound Source\0";
         break;
      case PC:
         return "PC Speaker\0";
         break;
      case NumSoundCards:
         return "None\0";
         break;
      default:
         return "\0";
         break;
      }
   }

//=================
//
// BlasterTypeName
//
//=================

char * BlasterTypeName( int32 type )
   {
   switch (type)
      {
      case fx_SB:
         return "Sound Blaster or Compatible\0";
         break;
      case fx_SBPro:
         return "Sound Blaster Pro\0";
         break;
      case fx_SB20:
         return "Sound Blaster 2.0\0";
         break;
      case fx_SBPro2:
         return "Sound Blaster Pro 2.0\0";
         break;
      case fx_SB16:
         return "Sound Blaster 16 or AWE32\0";
         break;
      default:
         return "\0";
         break;
      }
   }

//=================
//
// ControllerTypeName
//
//=================

char * ControllerTypeName( int32 type )
   {
   switch (type)
      {
      case controltype_keyboard:
         return "Keyboard\0";
         break;
      case controltype_keyboardandmouse:
         return "Keyboard and Mouse\0";
         break;
      case controltype_keyboardandjoystick:
         return "Keyboard and Joystick\0";
         break;
      case controltype_keyboardandgamepad:
         return "Keyboard and Gamepad\0";
         break;
      case controltype_keyboardandexternal:
         return "Keyboard and External\0";
         break;
      default:
         return "\0";
         break;
      }
   }

//******************************************************************************
//
// LaunchProgram ()
//
//******************************************************************************

void ShutDown( void );

void LaunchProgram (char * programname, boolean wait)
{
   byte * background;
   char    *newargs[40];

   if (wait == false)
      {
      int32   argnum=1;
      int32   i;
      int32 handle;

      for (i=1;i<_argc;i++)
         newargs [argnum++] = _argv[i];
       if (CheckParm (SETUPNAMEPARM) == 0)
          {
          newargs[argnum++] = "-SETUPFILE";
          newargs[argnum++] = setupfilename;
          }


      newargs[0] = programname;
      ShutDown();
      handle=SafeOpenWrite (LAUNCHPROG, filetype_text );
      for (i=0;i<argnum;i++)
         {
         SafeWrite(handle,newargs[i],strlen(newargs[i]));
         SafeWrite(handle," ",1);
         }
      SafeClose(handle);
      exit(0);
      }
   else
      {
      background = SafeMalloc(maxtextheight * maxtextwidth * 2);
      SaveTextBackground (0, 0, maxtextwidth, maxtextheight, background );
      ShutdownTextMode ();
      KB_Shutdown();
      printf("launching %s\n",programname);
      newargs[0] = NULL;
      if (spawnvp ( P_WAIT, programname , newargs)!=0)
         {
         printf("Spawn Error: %s\n",strerror(errno));
         printf("\n< Press a key to continue >\n");
         getch();
         }
      StartTextMode ();
      KB_Startup();
      RestoreTextBackground (0, 0, maxtextwidth, maxtextheight, background );
      SafeFree(background);
      }
}

//=================
//
// LaunchMultiPlayer
//
//=================

void LaunchMultiPlayer( int32 gametype )
   {
   WriteSetup(false);
   WriteCommitFile( gametype );
   LaunchProgram (COMMITLAUNCHER, false);
   }

//=================
//
// LaunchGame
//
//=================

void LaunchGame( int32 dummy )
   {
   //
   // Satisfy compiler
   //
   dummy=dummy+1;
   WriteSetup(false);
   LaunchProgram (GAMELAUNCHER, false);
   }

//=================
//
// DoChainedModeSetup
//
//=================

/*
void DoChainedModeSetup( void )
   {
   ScreenMode = screenmode_chained;
   if (!HandleMenu( &ScreenWidthSetupMenu ))
      HandleMenu( &ScreenHeightSetupMenu );
   }
*/


//=================
//
// DoScreenBufferModeSetup
//
//=================

void DoScreenBufferModeSetup( void )
   {
   ScreenMode = screenmode_buffered;
   ScreenWidth = 320;
   ScreenHeight = 200;
/*
   HandleMenu( &ScreenBufferSetupMenu );
   switch (ScreenBufferMode)
      {
      case screenbuffer_320x200:
         ScreenWidth = 320;
         ScreenHeight = 200;
         break;
      case screenbuffer_640x400:
         ScreenWidth = 640;
         ScreenHeight = 400;
         break;
      case screenbuffer_640x480:
         ScreenWidth = 640;
         ScreenHeight = 480;
         break;
      case screenbuffer_800x600:
         ScreenWidth = 800;
         ScreenHeight = 600;
         break;
      case screenbuffer_1024x768:
         ScreenWidth = 1024;
         ScreenHeight = 768;
         break;
      case screenbuffer_1280x1024:
         ScreenWidth = 1280;
         ScreenHeight = 1024;
         break;
      case screenbuffer_1600x1200:
         ScreenWidth = 1600;
         ScreenHeight = 1200;
         break;
      }
*/
   }

//=================
//
// DoVesaBufferModeSetup
//
//=================

void DoVesaBufferModeSetup( void )
   {
   char temp[200];
   sprintf(temp,"To use these modes you need a VESA 2.0 driver loaded\n"
                "We suggest the UNIVBE driver.\n");
   ErrorWindow( temp );
   ScreenMode = screenmode_vesa;
   HandleMenu( &VesaSetupMenu );
   switch (VesaBufferMode)
      {
      case vesa_320x200:
         ScreenWidth = 320;
         ScreenHeight = 200;
         break;
      case vesa_360x200:
         ScreenWidth = 360;
         ScreenHeight = 200;
         break;
      case vesa_320x240:
         ScreenWidth = 320;
         ScreenHeight = 240;
         break;
      case vesa_360x240:
         ScreenWidth = 360;
         ScreenHeight = 240;
         break;
      case vesa_320x400:
         ScreenWidth = 320;
         ScreenHeight = 400;
         break;
      case vesa_360x400:
         ScreenWidth = 360;
         ScreenHeight = 400;
         break;
      case vesa_640x350:
         ScreenWidth = 640;
         ScreenHeight = 350;
         break;
      case vesa_640x400:
         ScreenWidth = 640;
         ScreenHeight = 400;
         break;
      case vesa_640x480:
         ScreenWidth = 640;
         ScreenHeight = 480;
         break;
      case vesa_800x600:
         ScreenWidth = 800;
         ScreenHeight = 600;
         break;
      case vesa_1024x768:
         ScreenWidth = 1024;
         ScreenHeight = 768;
         break;
      case vesa_1280x1024:
         ScreenWidth = 1280;
         ScreenHeight = 1024;
         break;
      case vesa_1600x1200:
         ScreenWidth = 1600;
         ScreenHeight = 1200;
         break;
      }
   }


/*
===================
=
= FromFileFunctions
=
===================
*/

#define MAXFIELDS 4

#define ENTRYOFFSET(entrynum) (numfields*(entrynum))

/*
===================
=
= GrabFileEntries
=
===================
*/
void GrabFileEntries
   (
   int32 handle,
   char *** info,
   int32 numentries,
   int32 numfields
   )
   {
   char ** array;
   int32 entry,i;

   // malloc the space for the entire list
   *info = ( char ** )SafeMalloc( sizeof( char ** ) * ENTRYOFFSET(numentries) );

   for (entry=0;entry<numentries;entry++)
      {
      array = *info + ENTRYOFFSET(entry);

      for (i=0;i<numfields;i++)
         {
         int32 length;

         TOKEN_GetLine(handle,true);
         length = strlen(token);
         while (token[length]<32)
            length--;
         length+=2;
         array[ i ] = SafeMalloc(length);
         memset (array[ i ],0,length);
         memcpy (array[ i ],&token[0],length-1);
         }
      }
   }

/*
===================
=
= FreeFileEntries
=
===================
*/

void FreeFileEntries( char *** info, int32 numentries, int32 numfields )
   {
   char ** array;
   int32 entry,i;

   // clean up and free stuff
   for (entry=0;entry<numentries;entry++)
      {
      array = *info + ENTRYOFFSET(entry);

      for (i=0;i<numfields;i++)
         {
         SafeFree(array[ i ]);
         }
      }
   SafeFree(*info);
   }

/*
===================
=
= GetFileEntriesWidth
=
===================
*/
int32 GetFileEntriesWidth( char ** info, int32 numentries, int32 numfields )
   {
   char ** array;
   int32 width;
   int32 entry;

   width = 0;
   for (entry=0;entry<numentries;entry++)
      {
      array = info + ENTRYOFFSET(entry);

      if (strlen(array[ 0 ]) > width)
         {
         width = strlen(array[ 0 ]);
         }
      }
   return width;
   }

/*
===================
=
= OpenFromFileWindow
=
===================
*/

#define FROMFILEWINDOWHEIGHT (19)
#define FROMFILELISTHEIGHT (FROMFILEWINDOWHEIGHT-3)

int32 OpenFromFileWindow( int32 windowwidth )
   {
   char footer[80];
   char header[80];
   int32 windowhandle;

   strcpy (footer,MENUFOOTER);
   strcpy (header,"Choose an entry from the list");
   if ( strlen(header)-1 > windowwidth )
      {
      windowwidth = strlen(header)-1;
      }

   if ( strlen(footer)-1 > windowwidth )
      {
      windowwidth = strlen(footer)-1;
      }
   windowhandle = OpenCenteredWindow
                     (
                     windowwidth,
                     FROMFILEWINDOWHEIGHT,
                     COLOR_LIGHTCYAN,
                     COLOR_BLUE
                     );
   DrawWindowCenteredString
      (
      windowhandle,
      0,
      header,
      WindowForegroundColor( windowhandle ),
      WindowBackgroundColor( windowhandle )
      );
   DrawWindowDivider( windowhandle, 1 );

   DrawWindowCenteredString
      (
      windowhandle,
      FROMFILEWINDOWHEIGHT,
      footer,
      WindowForegroundColor( windowhandle ),
      WindowBackgroundColor( windowhandle )
      );
   DrawWindowDivider( windowhandle, FROMFILEWINDOWHEIGHT-1 );

   return windowhandle;
   }

/*
===================
=
= DrawFromFile
=
===================
*/
void DrawFromFile
   (
   int32 windowhandle,
   int32 current,
   char ** info,
   int32 numentries,
   int32 numfields
   )
   {
   char ** array;
   int32 entry,i;
   int32 endentry;
   int32 position;

   // clear the window
   for (i=2;i<WindowHeight(windowhandle)-1;i++)
      {
      ClearWindowLine( windowhandle, i );
      }

   entry = current-(FROMFILELISTHEIGHT>>1);
   if (entry<0)
      {
      entry = 0;
      }
   endentry = entry+FROMFILELISTHEIGHT;
   if (endentry > numentries)
      {
      endentry = numentries;
      entry = endentry-FROMFILELISTHEIGHT;
      if (entry<0)
         {
         entry = 0;
         }
      }
   i = 0;
   for (;entry<endentry;entry++,i++)
      {
      array = info + ENTRYOFFSET(entry);

      if (entry == current)
         {
         DrawWindowString
            (
            windowhandle,
            2,
            i+2,
            array[0],
            WindowBackgroundColor( windowhandle ),
            WindowForegroundColor( windowhandle )
            );
         }
      else
         {
         DrawWindowString
            (
            windowhandle,
            2,
            i+2,
            array[0],
            WindowForegroundColor( windowhandle ),
            WindowBackgroundColor( windowhandle )
            );
         }
      }
   position = (current*(FROMFILELISTHEIGHT-2)/(numentries-1));
   if (position >= (FROMFILELISTHEIGHT-2))
      position--;
   DrawSlider
      (
      windowhandle,
      WindowWidth(windowhandle),
      2,
      position,
      FROMFILELISTHEIGHT-2,
      true
      );
   }


/*
===================
=
= DoFromList
=
===================
*/

void DoFromList( FromFileType * fromfile, char ** fields, int32 numentries )
   {
   int32 windowwidth;
   int32 windowhandle;
   int32 index;
   boolean done;
   boolean redraw;

   windowwidth = GetFileEntriesWidth( fields, numentries, fromfile->numfields );
   windowwidth += 4;
   // open up window
   windowhandle = OpenFromFileWindow( windowwidth );
   // start up selection process
   index = 0;
   done  = false;
   redraw = true;
   KB_FlushKeyboardQueue();
   KB_ClearKeysDown();
   while (done == false)
      {
      if (redraw == true)
         {
         redraw = false;
         DrawFromFile
            (
            windowhandle,
            index,
            fields,
            numentries,
            fromfile->numfields
            );
         }
      if (KB_KeyWaiting() == true)
         {
         int32 keypress;

         keypress = KB_Getch();
         if (keypress == 0)
            {
            // Must be a scan code
            keypress = KB_Getch();
            switch (keypress)
               {
               case sc_UpArrow:
               case sc_kpad_8:
                  if (index > 0)
                     {
                     redraw = true;
                     index--;
                     }
                  break;
               case sc_DownArrow:
               case sc_kpad_2:
                  if (index < numentries-1)
                     {
                     redraw = true;
                     index++;
                     }
                  break;
               case sc_PgUp:
               case sc_kpad_9:
                  if (index > FROMFILELISTHEIGHT)
                     {
                     redraw = true;
                     index-=FROMFILELISTHEIGHT;
                     }
                  else
                     {
                     redraw = true;
                     index=0;
                     }
                  break;
               case sc_PgDn:
               case sc_kpad_3:
                  if (index < numentries-1-FROMFILELISTHEIGHT)
                     {
                     redraw = true;
                     index+=FROMFILELISTHEIGHT;
                     }
                  else
                     {
                     redraw = true;
                     index=numentries-1;
                     }
                  break;
               case sc_Home:
               case sc_kpad_7:
                  index = 0;
                  redraw = true;
                  break;
               case sc_End:
               case sc_kpad_1:
                  index = numentries-1;
                  redraw = true;
                  break;
               }
            }
            else
            {
            switch (keypress)
               {
               case asc_Enter:
                  {
                  int32 numfields;
                  char ** array;

                  numfields = fromfile->numfields;
                  done = true;
                  array = fields + ENTRYOFFSET(index);
                  if (fromfile->field1)
                     strcpy(fromfile->field1,array[0]);
                  if ( (fromfile->field2) && (numfields > 1 ) )
                     strcpy(fromfile->field2,array[1]);
                  if ( (fromfile->field3) && (numfields > 2 ) )
                     strcpy(fromfile->field3,array[2]);
                  if ( (fromfile->field4) && (numfields > 3 ) )
                     strcpy(fromfile->field4,array[3]);
                  }
                  break;
               case asc_Escape:
                  done = true;
                  break;
               }
            }
         }
      }
   // clean up and free stuff
   CloseWindow( windowhandle );
   }

/*
===================
=
= DoFromFile
=
===================
*/

void DoFromFile( FromFileType * fromfile )
   {
   if (SafeFileExists(fromfile->filename))
      {
      char ** fields;
      int32 numentries;
      int32 handle;

      handle = TOKEN_Load(fromfile->filename);
      // count entries in file
      numentries = TOKEN_LinesInFile(handle) / fromfile->numfields;
      // malloc the space for the entire list
      GrabFileEntries( handle, &fields, numentries, fromfile->numfields );
      DoFromList( fromfile, fields, numentries );

      FreeFileEntries( &fields, numentries, fromfile->numfields );
      TOKEN_Free(handle);
      }
   else
      {
      char temp[100];

      sprintf(temp,"%s does not exist, cannot continue.",fromfile->filename);
      ErrorWindow( temp );
      }
   }

/*
===================
=
= DoFromFunctions
=
===================
*/

void DoFromFunctions ( FromFunctionsType * fromfunctions )
   {
   FromFileType fromfile;
   char ** fields;
   int32 numentries;
   int32 entry;
   int32 numfields;
   int32 length;
   char ** array;

   fromfile.filename = "functions.ini";
   fromfile.numfields = 1;
   fromfile.field1 = fromfunctions->ptr;

   numentries = NUMGAMEFUNCTIONS;
   numfields = 1;

   fields = ( char ** )SafeMalloc( sizeof( char ** ) * ENTRYOFFSET(numentries) );

   for (entry=0;entry<numentries;entry++)
      {
      array = fields + ENTRYOFFSET(entry);

      length = strlen(gamefunctions[entry])+1;
      array[ 0 ] = SafeMalloc(length);
      memset (array[ 0 ],0,length);
      memcpy (array[ 0 ],gamefunctions[entry],length);
      }
   DoFromList( &fromfile, fields, numentries );
   FreeFileEntries( &fields, numentries, fromfile.numfields );
   }


/*
===================
=
= DoChooseFile
=
===================
*/

#define MAXFILES 40
void DoChooseFile ( ChooseFileType * choosefile )
   {
   FromFileType fromfile;
   char ** fields;
   int32 entry;
   int32 length;
   char ** array;
   int32 numfiles;
   int32 numfields;
   struct find_t fblock;
   char * filenames[MAXFILES];
   int32 i;

   numfiles=0;
   if (_dos_findfirst(choosefile->searchstring,0,&fblock)==0)
      {
      do
         {
         filenames[numfiles]=SafeMalloc(128);
         strcpy(filenames[numfiles],fblock.name);
         numfiles++;
         if (numfiles == MAXFILES)
            break;
         }
      while(!_dos_findnext(&fblock));
      }

   if (!numfiles)
      {
      char temp[100];
      sprintf(temp,"No files matching %s found.\n",choosefile->searchstring);
      ErrorWindow(temp);
      return;
      }

   fromfile.filename = choosefile->searchstring;
   fromfile.numfields = 1;
   fromfile.field1 = choosefile->destination;
   numfields = 1;

   fields = ( char ** )SafeMalloc( sizeof( char ** ) * ENTRYOFFSET(numfiles) );

   for (entry=0;entry<numfiles;entry++)
      {
      array = fields + ENTRYOFFSET(entry);
      length = strlen(filenames[entry])+1;
      array[ 0 ] = SafeMalloc(length);
      memset (array[ 0 ],0,length);
      memcpy (array[ 0 ],filenames[entry],length);
      }
   for (i=0;i<numfiles;i++)
      {
      SafeFree(filenames[i]);
      }
   DoFromList( &fromfile, fields, numfiles );
   FreeFileEntries( &fields, numfiles, fromfile.numfields );
   }
/*
===================
=
= KeyboardControlFunctions
=
===================
*/

/*
===================
=
= InitializeKeyDefinitions
=
===================
*/
void InitializeKeyDefinitions( int32 scripthandle )
   {
   int32 numkeyentries;
   int32 i;
   char * functionname;
   char keyname1[80];
   char keyname2[80];

   SetDefaultKeyDefinitions();

   numkeyentries = SCRIPT_NumberEntries( scripthandle, "KeyDefinitions" );

   for (i=0;i<numkeyentries;i++)
      {
      functionname = SCRIPT_Entry(scripthandle, "KeyDefinitions", i );
      memset(keyname1,0,sizeof(keyname1));
      memset(keyname2,0,sizeof(keyname2));
      SCRIPT_GetDoubleString(scripthandle, "KeyDefinitions", functionname, keyname1, keyname2);
      if (keyname1[0])
         {
//         AddKeyFunction( functionname );
         AddKeyDef( functionname, keyname1, 0 );
         }
      else
         ClearKeyDef( functionname, 0);
      if (keyname2[0])
         {
//         AddKeyFunction( functionname );
         AddKeyDef( functionname, keyname2, 1 );
         }
      else
         ClearKeyDef( functionname, 1);
      }
   for (i=0;i<NUMGAMEFUNCTIONS;i++)
      {
      functionname = gamefunctions[i];
      AddKeyFunction( functionname );
      }
   }


#define KEYCODESIZE 6
/*
===================
=
= AddKeyFunction
=
===================
*/
void AddKeyFunction( char * functionname )
   {
   KeyEntryType * key;
   KeyEntryType * newkeydef;
   int32 length;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (!strcmpi( functionname,key->name ))
         {
         return;
         }
      else
         {
         key = key->next;
         }
      }
   newkeydef = (KeyEntryType *)SafeMalloc( sizeof( KeyEntryType ) );
   LL_AddNode( keydefs, newkeydef, next, prev );
   length = strlen(functionname)+1;
   newkeydef->name = SafeMalloc(length);
   strcpy( newkeydef->name, functionname );
   newkeydef->key1 = NULL;
   newkeydef->key2 = NULL;
   }



/*
===================
=
= AddKeyDef
=
===================
*/
void AddKeyDef( char * functionname, char * keyname, int32 whichkey )
   {
   KeyEntryType * key;
   int32 length;

   key = keydefs->next;
   length = strlen(keyname)+1;

   while ( key != keydefs )
      {
      if (!strcmpi( functionname,key->name ))
         {
         if (whichkey == 0)
            {
            if (key->key1)
               {
               SafeFree(key->key1);
               }
            key->key1 = SafeMalloc(length);
            strcpy(key->key1,keyname);
            }
         else if (whichkey == 1)
            {
            if (key->key2)
               {
               SafeFree(key->key2);
               }
            key->key2 = SafeMalloc(length);
            strcpy(key->key2,keyname);
            }
         return;
         }
      else
         {
         key = key->next;
         }
      }
   }

/*
===================
=
= ClearKeyDef
=
===================
*/
void ClearKeyDef( char * functionname, int32 whichkey )
   {
   KeyEntryType * key;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (!strcmpi( functionname,key->name ))
         {
         if (whichkey == 0)
            {
            if (key->key1)
               {
               SafeFree(key->key1);
               key->key1 = NULL;
               }
            }
         else if (whichkey == 1)
            {
            if (key->key2)
               {
               SafeFree(key->key2);
               key->key2 = NULL;
               }
            }
         return;
         }
      else
         {
         key = key->next;
         }
      }
   }

/*
===================
=
= InitializeKeyDefList
=
===================
*/
void InitializeKeyDefList( void )
   {
   LL_CreateNewLinkedList(keydefs,KeyEntryType,next,prev);
   }

/*
===================
=
= FreeKeyDefList
=
===================
*/
void FreeKeyDefList( void )
   {
   KeyEntryType * key;
   KeyEntryType * nextkey;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (key->key1)
         {
         SafeFree(key->key1);
         }
      if (key->key2)
         {
         SafeFree(key->key2);
         }
      nextkey = key->next;
      SafeFree(key);
      key = nextkey;
      }
   }

/*
===================
=
= ClearKeyDefintions
=
===================
*/
void ClearKeyDefionitions( void )
   {
   KeyEntryType * key;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (key->key1)
         {
         SafeFree(key->key1);
         key->key1 = NULL;
         }
      if (key->key2)
         {
         SafeFree(key->key2);
         key->key2 = NULL;
         }
      key = key->next;
      }
   }

/*
===================
=
= WriteKeyDefinitions
=
===================
*/
void WriteKeyDefinitions( int32 scripthandle )
   {
   KeyEntryType * key;

   key = keydefs->next;

   while ( key != keydefs )
      {
      SCRIPT_PutDoubleString( scripthandle,"KeyDefinitions",key->name, key->key1, key->key2 );
      key = key->next;
      }
   }

/*
===================
=
= IsKeyUsed
=
===================
*/
KeyEntryType * IsKeyUsed( KeyEntryType * currentkey, char * keystring, int32 whichkey )
   {
   KeyEntryType * key;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (!strcmpi(key->key1,keystring))
         {
         if ( (key != currentkey) || (whichkey != 0))
            return key;
         }
      if (!strcmpi(key->key2,keystring))
         {
         if ( (key != currentkey) || (whichkey != 1))
            return key;
         }
      key = key->next;
      }
   return NULL;
   }


/*
===================
=
= GetNewKey
=
===================
*/
char * GetNewKey( void )
   {
   int32 windowhandle;
   int32 windowwidth;
   int32 lastscan;
   char msg[80];
   char * newkey;

   strcpy(msg,"Press a Key");
   windowwidth = strlen(msg)+2;
   windowhandle = OpenCenteredWindow
                     (
                     windowwidth,
                     0,
                     COLOR_YELLOW,
                     COLOR_RED
                     );
   DrawWindowCenteredString
      (
      windowhandle,
      0,
      msg,
      WindowForegroundColor( windowhandle ),
      WindowBackgroundColor( windowhandle )
      );
   KB_FlushKeyboardQueue();
   KB_ClearKeysDown();
   KB_SetLastScanCode( 0 );
   while (1)
      {
      if ((KB_GetLastScanCode() != 0) && (KB_GetLastScanCode() != sc_Paused))
         break;
      }
   if (KB_KeyWaiting())
      {
      KB_Getch();
      }
   CloseWindow(windowhandle);
   lastscan = KB_GetLastScanCode();
   newkey = KB_ScanCodeToString(lastscan);
   KB_FlushKeyboardQueue();
   KB_ClearKeysDown();
   return newkey;
   }

/*
===================
=
= KeyDefinition
=
===================
*/
KeyEntryType * KeyDefinition( int32 num )
   {
   int32 i=0;
   KeyEntryType * key;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (i == num)
         return key;
      key = key->next;
      i++;
      }
   return NULL;
   }

/*
===================
=
= NumberKeyDefinitions
=
===================
*/
int32 NumberKeyDefinitions( void )
   {
   int32 i=0;
   KeyEntryType * key;

   key = keydefs->next;

   while ( key != keydefs )
      {
      key = key->next;
      i++;
      }

   return i;
   }

/*
===================
=
= GetWidestKeyDefinition
=
===================
*/
int32 GetWidestKeyDefinition( void )
   {
   KeyEntryType * key;
   int32 width = 0;

   key = keydefs->next;

   while ( key != keydefs )
      {
      int32 tempwidth;

      tempwidth = strlen(key->name) + 1;
      tempwidth += (KEYCODESIZE*2) + 2;
      if (tempwidth > width)
         width = tempwidth;
      key = key->next;
      }
   return width;
   }


/*
===================
=
= GetWidestKeyName
=
===================
*/
int32 GetWidestKeyName( void )
   {
   KeyEntryType * key;
   int32 width = 0;

   key = keydefs->next;

   while ( key != keydefs )
      {
      if (strlen(key->name) > width)
         width = strlen(key->name);
      key = key->next;
      }
   return width+1;
   }

/*
===================
=
= OpenKeyDefinitionWindow
=
===================
*/

#define KEYDEFWINDOWHEIGHT (20)
#define KEYDEFLISTHEIGHT (KEYDEFWINDOWHEIGHT-4)
int32 OpenKeyDefinitionWindow( int32 windowwidth )
   {
   char footer1[80];
   char footer2[80];
   char header[80];
   int32 windowhandle;

   strcpy (footer1,"Esc Exits  F1 Set All Key Defaults\0");
   strcpy (footer2," Move  Del Deletes  ΔΩ Selects\0");
   strcpy (header,"Choose a key definition to modify");
   if ( strlen(header)-1 > windowwidth )
      {
      windowwidth = strlen(header)-1;
      }

   if ( strlen(footer1)-1 > windowwidth )
      {
      windowwidth = strlen(footer1)-1;
      }

   if ( strlen(footer2)-1 > windowwidth )
      {
      windowwidth = strlen(footer2)-1;
      }

   windowhandle = OpenCenteredWindow
                     (
                     windowwidth,
                     KEYDEFWINDOWHEIGHT,
                     COLOR_LIGHTCYAN,
                     COLOR_BLUE
                     );
   DrawWindowCenteredString
      (
      windowhandle,
      0,
      header,
      WindowForegroundColor( windowhandle ),
      WindowBackgroundColor( windowhandle )
      );
   DrawWindowDivider( windowhandle, 1 );

   DrawWindowCenteredString
      (
      windowhandle,
      KEYDEFWINDOWHEIGHT-1,
      footer1,
      WindowForegroundColor( windowhandle ),
      WindowBackgroundColor( windowhandle )
      );
   DrawWindowCenteredString
      (
      windowhandle,
      KEYDEFWINDOWHEIGHT,
      footer2,
      WindowForegroundColor( windowhandle ),
      WindowBackgroundColor( windowhandle )
      );
   DrawWindowDivider( windowhandle, KEYDEFWINDOWHEIGHT-2 );

   return windowhandle;
   }

/*
===================
=
= DrawKeyDefs
=
===================
*/
void DrawKeyDefs
   (
   int32 windowhandle,
   int32 current,
   int32 whichkey,
   int32 numkeydefs
   )
   {
   int32 entry,i;
   int32 endentry;
   int32 position;
   int32 namewidth;

   // clear the window
   for (i=2;i<WindowHeight(windowhandle)-2;i++)
      {
      ClearWindowLine( windowhandle, i );
      }
   namewidth = GetWidestKeyName();

   entry = current-(KEYDEFLISTHEIGHT>>1);
   if (entry<0)
      {
      entry = 0;
      }
   endentry = entry+KEYDEFLISTHEIGHT;
   if (endentry > numkeydefs)
      {
      endentry = numkeydefs;
      entry = endentry-KEYDEFLISTHEIGHT;
      if (entry<0)
         {
         entry = 0;
         }
      }
   i = 0;
   for (;entry<endentry;entry++,i++)
      {
      KeyEntryType * key;
      char str[80];

      key = KeyDefinition(entry);
      memset(str,'.',sizeof(str));
      str[KEYCODESIZE] = '\0';
      if ((current == entry) && (whichkey == 0))
         {
         DrawWindowString
            (
            windowhandle,
            2+namewidth+1,
            i+2,
            str,
            COLOR_GRAY,
            WindowBackgroundColor( windowhandle )
            );
         if (key->key1)
            {
            DrawWindowString
               (
               windowhandle,
               2+namewidth+1,
               i+2,
               key->key1,
               WindowBackgroundColor( windowhandle ),
               WindowForegroundColor( windowhandle )
               );
            }
         }
      else
         {
         DrawWindowString
            (
            windowhandle,
            2+namewidth+1,
            i+2,
            str,
            COLOR_DARKGRAY,
            WindowBackgroundColor( windowhandle )
            );
         if (key->key1)
            {
            DrawWindowString
               (
               windowhandle,
               2+namewidth+1,
               i+2,
               key->key1,
               WindowForegroundColor( windowhandle ),
               WindowBackgroundColor( windowhandle )
               );
            }
         }
      if ((current == entry) && (whichkey == 1))
         {
         DrawWindowString
            (
            windowhandle,
            2+namewidth+KEYCODESIZE+2,
            i+2,
            str,
            COLOR_GRAY,
            WindowBackgroundColor( windowhandle )
            );
         if (key->key2)
            {
            DrawWindowString
               (
               windowhandle,
               2+namewidth+KEYCODESIZE+2,
               i+2,
               key->key2,
               WindowBackgroundColor( windowhandle ),
               WindowForegroundColor( windowhandle )
               );
            }
         }
      else
         {
         DrawWindowString
            (
            windowhandle,
            2+namewidth+KEYCODESIZE+2,
            i+2,
            str,
            COLOR_DARKGRAY,
            WindowBackgroundColor( windowhandle )
            );
         if (key->key2)
            {
            DrawWindowString
               (
               windowhandle,
               2+namewidth+KEYCODESIZE+2,
               i+2,
               key->key2,
               WindowForegroundColor( windowhandle ),
               WindowBackgroundColor( windowhandle )
               );
            }
         }
      if (entry == current)
         {
         DrawWindowString
            (
            windowhandle,
            2,
            i+2,
            key->name,
            WindowBackgroundColor( windowhandle ),
            WindowForegroundColor( windowhandle )
            );
         }
      else
         {
         DrawWindowString
            (
            windowhandle,
            2,
            i+2,
            key->name,
            WindowForegroundColor( windowhandle ),
            WindowBackgroundColor( windowhandle )
            );
         }
      }
   position = (current*(KEYDEFLISTHEIGHT-2)/(numkeydefs-1));
   if (position >= (KEYDEFLISTHEIGHT-2))
      position--;
   DrawSlider
      (
      windowhandle,
      WindowWidth(windowhandle),
      2,
      position,
      KEYDEFLISTHEIGHT-2,
      true
      );
   }

/*
===================
=
= DoKeyDefs
=
===================
*/

void DoKeyDefs( void )
   {
   int32 windowwidth;
   int32 windowhandle;
   int32 numkeydefs;
   int32 index;
   int32 whichkey;
   boolean done;
   boolean redraw;

   // get widest keydef
   windowwidth = GetWidestKeyDefinition();
   windowwidth += 4;
   // open up window
   windowhandle = OpenKeyDefinitionWindow( windowwidth );
   // start up selection process
   index = 0;
   whichkey = 0;
   done  = false;
   redraw = true;
   KB_FlushKeyboardQueue();
   KB_ClearKeysDown();
   numkeydefs = NumberKeyDefinitions();

   while (done == false)
      {
      if (redraw == true)
         {
         redraw = false;
         DrawKeyDefs
            (
            windowhandle,
            index,
            whichkey,
            numkeydefs
            );
         }
      if (KB_KeyWaiting() == true)
         {
         int32 keypress;

         keypress = KB_Getch();
         if (keypress == 0)
            {
            // Must be a scan code
            keypress = KB_Getch();
            switch (keypress)
               {
               case sc_UpArrow:
               case sc_kpad_8:
                  if (index > 0)
                     {
                     redraw = true;
                     index--;
                     }
                  break;
               case sc_DownArrow:
               case sc_kpad_2:
                  if (index < numkeydefs-1)
                     {
                     redraw = true;
                     index++;
                     }
                  break;
               case sc_LeftArrow:
               case sc_RightArrow:
               case sc_kpad_4:
               case sc_kpad_6:
                  whichkey ^= 1;
                  redraw = true;
                  break;
               case sc_PgUp:
               case sc_kpad_9:
                  if (index > KEYDEFLISTHEIGHT)
                     {
                     redraw = true;
                     index-=KEYDEFLISTHEIGHT;
                     }
                  else
                     {
                     redraw = true;
                     index=0;
                     }
                  break;
               case sc_PgDn:
               case sc_kpad_3:
                  if (index < numkeydefs-1-KEYDEFLISTHEIGHT)
                     {
                     redraw = true;
                     index+=KEYDEFLISTHEIGHT;
                     }
                  else
                     {
                     redraw = true;
                     index=numkeydefs-1;
                     }
                  break;
               case sc_Home:
               case sc_kpad_7:
                  index = 0;
                  redraw = true;
                  break;
               case sc_End:
               case sc_kpad_1:
                  index = numkeydefs-1;
                  redraw = true;
                  break;
               case sc_F1:
                  ClearKeyDefionitions();
                  SetDefaultKeyDefinitions();
                  redraw = true;
                  break;
               case sc_Delete:
                  {
                  KeyEntryType * key;

                  key = KeyDefinition(index);
                  ClearKeyDef( key->name, whichkey );
                  redraw = true;
                  }
                  break;
               }
           }
           else
           {
           switch (keypress)
               {
               case asc_Enter:
                   {
                   KeyEntryType * key;
                   char * newkey;
                   KeyEntryType * checkkey;
                   boolean done;

                   done = false;
                   key = KeyDefinition(index);
                   while (done == false)
                      {
                      done = true;
                      newkey = GetNewKey();
                      checkkey = IsKeyUsed( key, newkey, whichkey );
                      if (checkkey)
                         {
                         char temp[100];
                         if (checkkey != key)
                            {
                            sprintf(temp,"WARNING: %s has already been assigned to %s\n",newkey,checkkey->name);
                            ErrorWindow( temp );
                            }
                         else
                            {
                            sprintf(temp,"ERROR: %s cannot be assigned to %s twice.\n",newkey,checkkey->name);
                            ErrorWindow( temp );
                            done = false;
                            }
                         }
                       if (done)
                          AddKeyDef( key->name, newkey, whichkey );
                       redraw = true;
                       }
                    }
                   break;
               case asc_Escape:
                   done = true;
                   break;
               }
           }
           }
       }

   // clean up and free stuff
   CloseWindow( windowhandle );
   }

/*
===================
=
= SetupSound
=
===================
*/

void SetupSound( int32 cardname )
   {
   int MaxVoices;
   int MaxBits;
   int MaxChannels;
   int32 status;

   FXDevice = cardname;

   // if they chose None lets return;
   if (FXDevice == NumSoundCards)
      return;

   // Do special Sound Blaster, AWE32 stuff
   if (
         ( FXDevice == SoundBlaster ) ||
         ( FXDevice == Awe32 )
      )
      {
#ifdef __FLAT__
      fx_blaster_config blaster;

      status = FX_GetBlasterSettings( &blaster );
      if ( status == FX_Ok )
         {
         BlasterConfig.Type      = blaster.Type;
         BlasterConfig.Address   = blaster.Address;
         BlasterConfig.Interrupt = blaster.Interrupt;
         BlasterConfig.Dma8      = blaster.Dma8;
         BlasterConfig.Dma16     = blaster.Dma16;
         BlasterConfig.Midi      = blaster.Midi;
         BlasterConfig.Emu       = blaster.Emu;
         MidiPort = BlasterConfig.Midi;
         }
#endif
      if (HandleMenu(&BlasterSetupMenu))
         return;
#ifdef __FLAT__
      status = FX_SetupSoundBlaster
                  (
                  BlasterConfig, &MaxVoices, &MaxBits, &MaxChannels
                  );
#else
      MaxVoices = MAXVOICES;
      MaxBits = 16;
      MaxChannels = 2;
      status = FX_Ok;
#endif
      }
   else
      {
#ifdef __FLAT__
      fx_device device;
      status = FX_SetupCard( FXDevice, &device );
      MaxVoices = device.MaxVoices;
      MaxBits = device.MaxSampleBits;
      MaxChannels = device.MaxChannels;
#else
      MaxVoices = MAXVOICES;
      MaxBits = 16;
      MaxChannels = 2;
      status = FX_Ok;
#endif
      }
   if (MaxVoices>MAXVOICES)
      MaxVoices = MAXVOICES;

   // modify number of entries for Voice selection menu
   NumVoicesMenu.sections[0]->numentries=MaxVoices;
   if ( status == FX_Ok )
      {
      if (!HandleMenu(&NumVoicesMenu))
         {
         if (!HandleMenu(&NumBitsMenu))
            {
            if (!HandleMenu(&NumChannelsMenu))
               {
               HandleMenu(&MixRateMenu);
               }
            }
         }
      }
#ifdef __FLAT__
   if ( status != FX_Ok )
      {
      ErrorWindow( FX_ErrorString( FX_Error ));
      FXDevice = NumSoundCards;
      }
#endif
   }

/*
===================
=
= SetupMusic
=
===================
*/

void SetupMusic( int32 cardname )
   {

   MusicDevice = cardname;

   // if they chose None lets return
   if (MusicDevice == NumSoundCards)
      return;

   // Do AWE32 stuff
   if ( MusicDevice == Awe32 )
      {
      if (HandleMenu(&BlasterEmuAddressMenu))
         return;
      }
   if (
         ( MusicDevice == SoundCanvas ) ||
         ( MusicDevice == GenMidi ) ||
         ( MusicDevice == WaveBlaster ) ||
         ( MusicDevice == Awe32 )
      )
      {
      HandleMenu(&MidiPortMenu);
      }
   }

/*
===================
=
= TestMusic
=
===================
*/
extern byte theme;

void TestMusic( void )
   {
#ifdef __FLAT__
   int32 status;
   int32 handle;

   // if they chose None lets return
   if (MusicDevice == NumSoundCards)
      return;

   BlasterConfig.Midi = MidiPort;

   handle = MessageWindow
               (
               "Intializing Music"
               );
   status = MUSIC_Init( MusicDevice, MidiPort );
   CloseWindow(handle);
   if ( status == MUSIC_Ok )
      {
      MUSIC_SetVolume( MusicVolume );
      status = MUSIC_PlaySong( &theme, MUSIC_PlayOnce );

      if ( status == MUSIC_Ok )
         {
         char temp[200];
         sprintf(temp,"You should hear the %s\n"
                      "     Press any key to end testing",SONGNAME);
         handle = MessageWindow( temp );
         while (1)
            {
            if (KB_KeyWaiting() == true)
               {
               KB_Getch();
               break;
               }
            if (!MUSIC_SongPlaying())
               {
               break;
               }
            }
         CloseWindow(handle);
         status = MUSIC_StopSong();
         }
      status = MUSIC_Shutdown();
      }
   if ( status != MUSIC_Ok )
      {
      ErrorWindow( MUSIC_ErrorString( MUSIC_ErrorCode ));
      }
#endif
   }

/*
===================
=
= TestSound
=
===================
*/
extern byte testvoc;

void TestSound( void )
   {
#ifdef __FLAT__
   int32 status;
   int32 handle;
   int32 windowhandle;
   boolean test;

   // if they chose None lets return
   if (FXDevice == NumSoundCards)
      return;

   // Do special Sound Blaster, AWE32 stuff
   if (
         ( FXDevice == SoundBlaster ) ||
         ( FXDevice == Awe32 )
      )
      {
      int MaxVoices;
      int MaxBits;
      int MaxChannels;

      status = FX_SetupSoundBlaster
                  (
                  BlasterConfig, &MaxVoices, &MaxBits, &MaxChannels
                  );
      }

   windowhandle = MessageWindow
                     (
                     "Intializing Sound FX"
                     );
   status = FX_Init( FXDevice, NumVoices, NumChannels, NumBits, MixRate );

   if (ReverseStereo == 1)
      {
      FX_SetReverseStereo(!FX_GetReverseStereo());
      }

   CloseWindow(windowhandle);
   if ( status == FX_Ok )
      {
      test = true;
      FX_SetVolume( FXVolume );
      windowhandle = MessageWindow
                  (
                  "       Center Channel\n"
                  "Press any key to end testing"
                  );
      handle = FX_PlayVOC( &testvoc, 0, 255, 255, 255, 255, 0 );
      while (1)
         {
         if (KB_KeyWaiting() == true)
            {
            KB_Getch();
            test = false;
            break;
            }
         if (!FX_SoundActive( handle ))
            {
            break;
            }
         }
      CloseWindow(windowhandle);
      if (test && (NumChannels > 1))
         {
         windowhandle = MessageWindow
                           (
                           "       Left Channel\n"
                           "Press any key to end testing"
                           );
         handle = FX_PlayVOC( &testvoc, 0, 0, 255, 0, 255, 0 );
         while (1)
            {
            if (KB_KeyWaiting() == true)
               {
               KB_Getch();
               test = false;
               break;
               }
            if (!FX_SoundActive( handle ))
               {
               break;
               }
            }
         CloseWindow(windowhandle);
         }
      if (test && (NumChannels > 1))
         {
         windowhandle = MessageWindow
                           (
                           "       Right Channel\n"
                           "Press any key to end testing"
                           );
         handle = FX_PlayVOC( &testvoc, 0, 0, 0, 255, 255, 0 );
         while (1)
            {
            if (KB_KeyWaiting() == true)
               {
               KB_Getch();
               test = false;
               break;
               }
            if (!FX_SoundActive( handle ))
               {
               break;
               }
            }
         CloseWindow(windowhandle);
         }
      status = FX_Shutdown();
      }
   if ( status != FX_Ok )
      {
      ErrorWindow( FX_ErrorString( FX_Error ));
      }
#endif
   }

