/****************************************************************************
*
*                   Digital Sound Interface Kit (DSIK)
*                            Version 2.00
*
*                           by Carlos Hasan
*
* Filename:     setup.c
* Version:      Revision 1.0
*
* Language:     WATCOM C
* Environment:  IBM PC (DOS/4GW)
*
* Description:  Standalone Soundcard setup program.
*
* Revision History:
* ----------------
*
* Revision 1.0  94/12/01  20:35:50  chv
* Initial revision
*
****************************************************************************/

#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <i86.h>
#include "audio.h"

/*------------------- Soundcard Configurations ----------------------------*/

#define NUMDRIVERS  (sizeof(DriversTable)/sizeof(DRIVER))

typedef struct {
    char    *FileName;
    char    *DriverName;
    char    *HelpText;
    int     DevId;
    int     Modes;
    int     Port;
    int     IrqLine;
    int     DmaChannel;
    int     SampleRate;
    int     PortTable[8],IrqTable[16],DmaTable[8];
} DRIVER;

DRIVER DriversTable[] = {
      { "NONE.DRV", "Sin Sonido",
        "Jugar sin sonido :-(",
        ID_NONE, AF_8BITS | AF_MONO, 0x000, 0, 0, 0,
        { 0, 0xFFFF },
        { 0, 0xFFFF },
        { 0, 0xFFFF } },

      { "SB.DRV", "Sound Blaster",
        "Sound Blaster o compatible",
        ID_SB, AF_8BITS | AF_MONO, 0x220, 7, 1, 22050,
        { 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0xFFFF },
        { 2, 3, 5, 7, 0xFFFF },
        { 1, 0xFFFF } },

      { "SB.DRV", "Sound Blaster 2.01",
        "Sound Blaster 2.01 o compatible",
        ID_SB201, AF_8BITS | AF_MONO, 0x220, 7, 1, 44100,
        { 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0xFFFF },
        { 2, 3, 5, 7, 0xFFFF },
        { 1, 0xFFFF } },

      { "SB.DRV", "Sound Blaster Pro",
        "Sound Blaster Pro, Sound Galaxy Pro o compatible",
        ID_SBPRO, AF_8BITS | AF_STEREO, 0x220, 5, 1, 22050,
        { 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0xFFFF },
        { 2, 3, 5, 7, 0xFFFF },
        { 0, 1, 3, 0xFFFF } },

      { "SB.DRV", "Sound Blaster 16/16ASP",
        "Sound Blaster 16/16ASP, Wave Blaster o Sound Blaster AWE32",
        ID_SB16, AF_16BITS | AF_STEREO, 0x220, 5, 5, 44100,
        { 0x220, 0x240, 0x260, 0x280, 0xFFFF },
        { 2, 5, 7, 10, 0xFFFF },
        { 0, 1, 3, 5, 6, 7, 0xFFFF } },

      { "PAS.DRV", "Pro Audio Spectrum",
        "Media Vision Pro Audio Spectrum o compatible",
        ID_PAS, AF_8BITS | AF_STEREO, 0x388, 7, 1, 44100,
        { 0x388, 0x384, 0x38C, 0x288, 0xFFFF },
        { 2, 3, 5, 7, 10, 11, 12, 13, 15, 0xFFFF },
        { 0, 1, 3, 5, 6, 7, 0xFFFF } },

      { "PAS.DRV", "Pro Audio Spectrum+",
        "Media Vision Pro Audio Spectrum Plus o compatible",
        ID_PASPLUS, AF_8BITS | AF_STEREO, 0x388, 7, 1, 44100,
        { 0x388, 0x384, 0x38C, 0x288, 0xFFFF },
        { 2, 3, 5, 7, 10, 11, 12, 13, 15, 0xFFFF },
        { 0, 1, 3, 5, 6, 7, 0xFFFF } },

      { "PAS.DRV", "Pro Audio Spectrum 16",
        "Pro Audio Spectrum 16, Logitech SoundMan 16 o compatible",
        ID_PAS16, AF_16BITS | AF_STEREO, 0x388, 7, 1, 44100,
        { 0x388, 0x384, 0x38C, 0x288, 0xFFFF },
        { 2, 3, 5, 7, 10, 11, 12, 13, 15, 0xFFFF },
        { 0, 1, 3, 5, 6, 7, 0xFFFF } },

      { "WSS.DRV", "Windows Sound System",
        "Windows Sound System, Audiotrix Pro o compatible",
        ID_WSS, AF_16BITS | AF_STEREO, 0x530, 7, 1, 44100,
        { 0x530, 0x604, 0xE80, 0xF40, 0xFFFF },
        { 7, 9, 10, 11, 0xFFFF },
        { 0, 1, 3, 0xFFFF } },

      { "GUS.DRV", "Gravis UltraSound",
        "Advanced Gravis UltraSound, UltraSound Max o compatible",
        ID_GUS, AF_16BITS | AF_STEREO, 0x220, 11, 1, 0,
        { 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0xFFFF },
        { 2, 3, 5, 7, 11, 12, 15, 0xFFFF },
        { 1, 3, 5, 6, 7, 0xFFFF } }
};

/*-------------------- Screen Menu Configurations -------------------------*/

#define MENU_SELECTCARD     0
#define MENU_SAVEEXIT       1

#define TITLELEN            32
#define MAXITEMS            16
#define ITEMLEN             32
#define HELPLEN             80

typedef struct {
    int     Line;
    char    Title[TITLELEN];
    char    ItemsText[MAXITEMS][ITEMLEN];
    char    HelpText[MAXITEMS][HELPLEN];
} MENU;

MENU MainMenu = {
    0, "Menu Principal",
    { "Selecciona Tarjeta de Sonido", "Salvar y Salir" },
    { "Selecciona Tarjeta para Musica y FX",
      "Salva SETUP y salida al D.iO.S." } };

MENU QuaMenu = {
    2, "Calidad de Sonido",
    { "BAJA calidad", "MEDIA calidad", "NORMAL calidad",
      "ALTA calidad", "MUY ALTA calidad" },
    { "BAJA calidad (386/20Hz)",
      "MEDIA calidad (386/33Hz)",
      "NORMAL calidad (486/33Hz) recomendado",
      "ALTA calidad (486/66Hz)",
      "MUY ALTA calidad (Pentium y aberraciones)" } };

/* This table of strings should be customized by the user */
char *TextTable[] = {
    "SETUP.CFG",
    "ARS MAGICA: configuracion de sonido",
    "por: DIEGO MARTINEZ",
    "ACEPTAR",
    "SALIR",
    "Elige Playback Device",
    "Elige Base I/O Port",
    "Elige Base I/O Port %03X hex",
    "Elige IRQ Interrupt Line",
    "Elige IRQ Interrupt Line %d",
    "Elige DMA Channel",
    "Elige DMA Channel %d",
    "Setup file saved. Ejecuta ARS para comenzar",
    "Error saving Setup file!",
    "Setup aborted."
};

/* This table of sampling rates should be modified too */
int RatesTable[] = {
    16000, 19025, 22050, 33075, 44100 };

/*------------------------- Keyboard Routines -----------------------------*/

#define KB_ESC      0x001B
#define KB_ENTER    0x000D
#define KB_UP       0x4800
#define KB_DOWN     0x5000

int ReadKey(void)
{
    int c;
    if (!(c = getch())) c = getch()<<8;
    return c;
}

/*---------------------- Setup I/O File Routines --------------------------*/

/* NOTE: I do not want to link all the sound system here, so I must
         rewrite the setup file routines again. There is no problem
         with the detection routine.
 */

/****************************************************************************
*
* Function:     dLoadSetup
* Parameters:   SC          - soundcard structure
*               Filename    - full config path filename
*
* Returns:      Zero value on success.
*
* Description:  Load the soundcard configuration parameters.
*
****************************************************************************/

int dLoadSetup(SoundCard *SC, char *Filename)
{
    int Handle;
    if ((Handle = open(Filename,O_RDONLY|O_BINARY)) < 0)
        return 1;
    if (read(Handle,SC,sizeof(SoundCard)) != sizeof(SoundCard)) {
        close(Handle);
        return 1;
    }
    close(Handle);
    return 0;
}

/****************************************************************************
*
* Function:     dSaveSetup
* Parameters:   SC          - soundcard structure
*               Filename    - full config path filename
*
* Returns:      Zero value on success.
*
* Description:  Save the soundcard configuration parameters.
*
****************************************************************************/

int dSaveSetup(SoundCard *SC, char *Filename)
{
    int Handle;
    if ((Handle = open(Filename,O_CREAT|O_WRONLY|O_BINARY,S_IRWXU)) < 0)
        return 1;
    if (write(Handle,SC,sizeof(SoundCard)) != sizeof(SoundCard)) {
        close(Handle);
        return 1;
    }
    close(Handle);
    return 0;
}

/*-------------------- Text Screen Mode Routines --------------------------*/

void SetTextMode(void)
{
    union REGS r;
    r.h.ah = 0x00;
    r.h.al = 0x03;
    int386(0x10,&r,&r);
}

void SetPalette(int index, int red, int green, int blue)
{
    outp(0x3c8,index);
    outp(0x3c9,red);
    outp(0x3c9,green);
    outp(0x3c9,blue);
}

void WaitVertRetrace(void)
{
    while (inp(0x3da) & 0x08) ;
    while (!(inp(0x3da) & 0x08)) ;
}

void HideCursor(void)
{
    outpw(0x3d4,0x100a);
    outpw(0x3d4,0x100b);
}

void DrawRect(int x, int y, int width, int height, int c, int color)
{
    char *ptr = (char*)(0xb8000+((x+80*y)<<1));
    int i,j;
    for (j = 0; j < height; j++) {
        for (i = 0; i < width; i++) {
            *ptr++ = c;
            *ptr++ = color;
        }
        ptr += (80-width)<<1;
    }
}

void DrawFrame(int x, int y, int width, int height, int color)
{
    static char frame[] = { 0xda,0xbf,0xc0,0xd9,0xc4,0xb3,0xc3,0xb4,0x20 };
    char *ptr = (char*)(0xb8000+((x+80*y)<<1));
    int i,j;

    *ptr++ = frame[0];
    *ptr++ = color;
    for (i = 0; i < width; i++) {
        *ptr++ = frame[4];
        *ptr++ = color;
    }
    *ptr++ = frame[1];
    *ptr++ = color;
    ptr += (78-width)<<1;

    *ptr++ = frame[5];
    *ptr++ = color;
    for (i = 0; i < width; i++) {
        *ptr++ = frame[8];
        *ptr++ = color;
    }
    *ptr++ = frame[5];
    *ptr++ = color;
    ptr += (78-width)<<1;

    *ptr++ = frame[6];
    *ptr++ = color;
    for (i = 0; i < width; i++) {
        *ptr++ = frame[4];
        *ptr++ = color;
    }
    *ptr++ = frame[7];
    *ptr++ = color;
    ptr += (78-width)<<1;

    for (j = 0; j < height; j++) {
        *ptr++ = frame[5];
        *ptr++ = color;
        for (i = 0; i < width; i++) {
            *ptr++ = frame[8];
            *ptr++ = color;
        }
        *ptr++ = frame[5];
        *ptr++ = color;
        ptr += (78-width)<<1;
    }

    *ptr++ = frame[6];
    *ptr++ = color;
    for (i = 0; i < width; i++) {
        *ptr++ = frame[4];
        *ptr++ = color;
    }
    *ptr++ = frame[7];
    *ptr++ = color;
    ptr += (78-width)<<1;

    *ptr++ = frame[5];
    *ptr++ = color;
    for (i = 0; i < width; i++) {
        *ptr++ = frame[8];
        *ptr++ = color;
    }
    *ptr++ = frame[5];
    *ptr++ = color;
    ptr += (78-width)<<1;

    *ptr++ = frame[2];
    *ptr++ = color;
    for (i = 0; i < width; i++) {
        *ptr++ = frame[4];
        *ptr++ = color;
    }
    *ptr++ = frame[3];
    *ptr++ = color;
    ptr += (78-width)<<1;
}

void DrawText(int x, int y, char *text, int color)
{
    char *ptr = (char*)(0xb8000+((x+80*y)<<1));
    while (*text) {
        *ptr++ = *text++;
        *ptr++ = color;
    }
}

void DrawCenterText(int y, char *text, int color)
{
    DrawText((80-strlen(text))>>1,y,text,color);
}

/*--------------------------- Menu Routines -------------------------------*/

void DrawMenu(MENU *menu)
{
    int x,y,width,height;
    int i,j;
    width = strlen(menu->Title);
    if ((j = 10+strlen(TextTable[3])+strlen(TextTable[4])) > width)
        width = j;
    for (i = height = 0; i < MAXITEMS; i++, height++) {
        if (!(j = strlen(menu->ItemsText[i]))) break;
        if (j > width) width = j;
    }
    width += 4;
    x = (78-width)>>1;
    y = 2+((14-height)>>1);
    DrawRect(x+1,y+1,width+3,height+6,0xb0,0x37);
    DrawFrame(x,y,width,height,0x1b);
    DrawRect(x+1,y+1,width,1,0x20,0x71);
    DrawText(x+1+((width-strlen(menu->Title))>>1),y+1,menu->Title,0x71);
    DrawText(x+2,y+4+height,"ENTER",0x1a);
    DrawText(x+7,y+4+height,"=",0x1b);
    DrawText(x+8,y+4+height,TextTable[3],0x1f);
    DrawText(x-4+width-strlen(TextTable[4]),y+4+height,"ESC",0x1a);
    DrawText(x-1+width-strlen(TextTable[4]),y+4+height,"=",0x1b);
    DrawText(x+width-strlen(TextTable[4]),y+4+height,TextTable[4],0x1f);
    for (i = 0; i < height; i++) {
        if (menu->Line == i) DrawRect(x+1,y+i+3,width,1,0x20,0x71);
        DrawText(x+3,y+i+3,menu->ItemsText[i],(menu->Line == i)?0x71:0x1f);
    }
    DrawRect(0,24,80,1,0x20,0x3b);
    DrawText(1,24,menu->HelpText[menu->Line],0x3b);
}

int ExecMenu(MENU *menu)
{
    int n,key;
    for (n = 0; n < MAXITEMS; n++)
        if (!strlen(menu->ItemsText[n])) break;
    if (n <= 1) return 0;
    for (;;) {
        WaitVertRetrace();
        DrawRect(0,2,80,22,0xb1,0x37);
        DrawMenu(menu);
        if ((key = ReadKey()) == KB_ESC) break;
        switch (key) {
            case KB_ENTER:
                return menu->Line;
                break;
            case KB_UP:
                (menu->Line)--;
                if (menu->Line < 0) (menu->Line)++;
                break;
            case KB_DOWN:
                (menu->Line)++;
                if (menu->Line >= n) (menu->Line)--;
                break;
        }
    }
    return -1;
}

/*------------------- Select Soundcard Menu Routines ----------------------*/

#define MAXDRIVERS ((MAXITEMS>NUMDRIVERS)?NUMDRIVERS:MAXITEMS)

MENU *MakeDeviceMenu(MENU *menu, SoundCard *SC)
{
    DRIVER *drv;
    int i;
    memset(menu,0,sizeof(MENU));
    strncpy(menu->Title,TextTable[5],TITLELEN);
    for (drv = DriversTable, i = 0; i < MAXDRIVERS; drv++, i++) {
        strncpy(menu->ItemsText[i],drv->DriverName,ITEMLEN);
        strncpy(menu->HelpText[i],drv->HelpText,HELPLEN);
        if (SC->ID == drv->DevId) menu->Line = i;
    }
    return menu;
}

MENU *MakePortMenu(MENU *menu, SoundCard *SC)
{
    DRIVER *drv;
    int i,j;
    memset(menu,0,sizeof(MENU));
    strncpy(menu->Title,TextTable[6],TITLELEN);
    for (drv = DriversTable, i = 0; i < MAXDRIVERS; drv++, i++)
        if (SC->ID == drv->DevId) break;
    for (i = 0; (i < MAXITEMS) && ((j = drv->PortTable[i]) != 0xFFFF); i++) {
        _bprintf(menu->ItemsText[i],ITEMLEN,"%03X",j);
        _bprintf(menu->HelpText[i],HELPLEN,TextTable[7],j);
        if (SC->Port == j) menu->Line = i;
    }
    return menu;
}

MENU *MakeIrqMenu(MENU *menu, SoundCard *SC)
{
    DRIVER *drv;
    int i,j;
    memset(menu,0,sizeof(MENU));
    strncpy(menu->Title,TextTable[8],TITLELEN);
    for (drv = DriversTable, i = 0; i < MAXDRIVERS; drv++, i++)
        if (SC->ID == drv->DevId) break;
    for (i = 0; (i < MAXITEMS) && ((j = drv->IrqTable[i]) != 0xFFFF); i++) {
        _bprintf(menu->ItemsText[i],ITEMLEN,"IRQ %d",j);
        _bprintf(menu->HelpText[i],HELPLEN,TextTable[9],j);
        if (SC->IrqLine == j) menu->Line = i;
    }
    return menu;
}

MENU *MakeDmaMenu(MENU *menu, SoundCard *SC)
{
    DRIVER *drv;
    int i,j;
    memset(menu,0,sizeof(MENU));
    strncpy(menu->Title,TextTable[10],TITLELEN);
    for (drv = DriversTable, i = 0; i < MAXDRIVERS; drv++, i++)
        if (SC->ID == drv->DevId) break;
    for (i = 0; (i < MAXITEMS) && ((j = drv->DmaTable[i]) != 0xFFFF); i++) {
        _bprintf(menu->ItemsText[i],ITEMLEN,"DMA %d",j);
        _bprintf(menu->HelpText[i],HELPLEN,TextTable[11],j);
        if (SC->DmaChannel == j) menu->Line = i;
    }
    return menu;
}

int SelectSoundCard(SoundCard *SC)
{
    MENU menu;
    DRIVER *drv;

    if (ExecMenu(MakeDeviceMenu(&menu,SC)) < 0) return 1;
    drv = &DriversTable[menu.Line];
    if (SC->ID != drv->DevId) {
        SC->Port = drv->Port;
        SC->IrqLine = drv->IrqLine;
        SC->DmaChannel = drv->DmaChannel;
    }
    SC->ID = drv->DevId;
    SC->Modes = drv->Modes;
    if (ExecMenu(MakePortMenu(&menu,SC)) < 0) return 1;
    SC->Port = drv->Port = drv->PortTable[menu.Line];
    if (ExecMenu(MakeIrqMenu(&menu,SC)) < 0) return 1;
    SC->IrqLine = drv->IrqLine = drv->IrqTable[menu.Line];
    if (ExecMenu(MakeDmaMenu(&menu,SC)) < 0) return 1;
    SC->DmaChannel = drv->DmaChannel = drv->DmaTable[menu.Line];
    if (drv->SampleRate) {
        if (ExecMenu(&QuaMenu) < 0) return 1;
        if ((SC->SampleRate = RatesTable[QuaMenu.Line]) > drv->SampleRate)
            SC->SampleRate = drv->SampleRate;
    }
    else {
        SC->SampleRate = 44100;
    }
    return 0;
}

/*-------------------- Save and Exit Menu Routines ------------------------*/

int SaveAndExit(SoundCard *SC, char *Filename)
{
    int I;
    for (I = 0; I < NUMDRIVERS; I++)
        if (SC->ID == DriversTable[I].DevId)
            strncpy(SC->DriverName,DriversTable[I].FileName,
                    sizeof(SC->DriverName));
    return dSaveSetup(SC,Filename);
}

/*----------------------- Setup Main Program ------------------------------*/

void SetupProgram(SoundCard *SC)
{
    SetTextMode();
    HideCursor();
    SetPalette(1,1,4,16);
    SetPalette(3,4,6,26);
    DrawRect(0,2,80,22,0xb1,0x37);
    DrawRect(0,0,80,4,0x20,0x3b);
    DrawCenterText(0,TextTable[1],0x3b);
    DrawCenterText(1,TextTable[2],0x3b);
    if (dLoadSetup(SC,TextTable[0])) {
        memset(SC,0,sizeof(SoundCard));
        dAutoDetect(SC);
    }
    for (;;) {
        if (ExecMenu(&MainMenu) < 0) {
            MainMenu.Line = MENU_SELECTCARD;
            break;
        }
        if (MainMenu.Line == MENU_SELECTCARD) {
            if (SelectSoundCard(SC)) break;
        }
        else if (MainMenu.Line == MENU_SAVEEXIT) {
            break;
        }
        MainMenu.Line = MENU_SAVEEXIT;
    }
    SetTextMode();
    if (MainMenu.Line == MENU_SAVEEXIT) {
        printf("%s\n", SaveAndExit(SC,TextTable[0]) ?
            TextTable[13] : TextTable[12]);
    }
    else {
        printf("%s\n",TextTable[14]);
    }
}


int main(int argc, char *argv[])
{
    SoundCard SC;
    SetupProgram(&SC);
    return EXIT_SUCCESS;
}
