/****************************************************************************
*
*                         The Universal VESA VBE
*
*                   Copyright (C) 1993 Kendall Bennett.
*                           All rights reserved.
*
* Filename:     $RCSfile: main.c $
* Version:      $Revision: 1.2 $
*
* Language:     ANSI C
* Environment:  IBM PC (MS DOS)
*
* Description:  Main startup module for the Universal VESA VBE. Performs
*               all of the command line processing, card detection and
*               installation of the VESA VBE as a TSR.
*
*               Note that this VBE does _not_ attempt to provide support
*               for extended text modes. VESA VBE compliant text modes
*               supported by the old Video BIOS will still be supported
*               by pass-through. Thus if you _really_ need extended text
*               modes, install the TSR that came with your card _before_
*               installing the Universal VESA VBE.
*
*               MUST be compiled in the large memory model.
*
* $Id: main.c 1.2 1993/09/23 03:29:51 kjb release $
*
* Revision History:
* -----------------
*
* $Log: main.c $
* Revision 1.2  1993/09/23  03:29:51  kjb
* Added __DATE__ to logon message.
*
* Revision 1.1  1993/09/19  01:26:00  kjb
* Initial revision
*
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <ctype.h>
#include <string.h>
#include "univbe.h"
#include "getopt.h"

/*---------------------------- Global Variables ---------------------------*/

int     superVGA        = -1;       /* Type of installed SuperVGA       */
int     chipID          = -1;       /* Type of chipset installed        */
int     memory          = -1;       /* Amount of video memory           */
int     dac             = -1;       /* Video DAC installed              */
int     pageFlip        = false;    /* True if page flipping supported  */
int     VESAcompliant   = false;    /* Flags if BIOS is already VESA    */
bool    skipDetect      = false;    /* Flags skipping of detection code */
bool    quiet           = false;    /* Flags quiet mode of operation    */
bool    installTSR      = true;     /* Flags if TSR should be installed */
char    *release        = "4.0";
short   removedModes[31];           /* Table of removed video modes     */

/*----------------------------- Implementation ----------------------------*/

void help(void)
/****************************************************************************
*
* Function:     help
*
* Description:  Provide command line usage information.
*
****************************************************************************/
{
    printf("Options are:\n");
    printf("    -s<name> - Force detection of SuperVGA 'name'\n");
    printf("    -c<x>    - Force detection of SuperVGA chipset (numerical id)\n");
    printf("    -m<size> - Force memory size to 'size'\n");
    printf("    -d<x>    - Force DAC type (0:VGA, 1:15 bit, 2:16 bit, 3:24 bit)\n");
    printf("    -i       - Do not perform SuperVGA detection\n");
    printf("    -q       - Quiet mode\n");
    printf("    -g       - Regenerate default .ini file\n");
    printf("    -u       - Unload the Universal VESA VBE from memory\n");
    printf("    -t       - Don't go TSR, just return the detected SuperVGA id\n");
    printf("\n");
    printf("It is possible the SuperVGA detection code will hang the machine on old\n");
    printf("VGA/SVGA cards. You can optionally force the program to work with any\n");
    printf("combination of card, chipset, memory size and DAC, but unless you specify\n");
    printf("the -i option, unspecified values will be filled in automatically for you.\n");
    exit(1);
}

void generateINI(char *argv0)
/****************************************************************************
*
* Function:     generateINI
* Parameters:   argv0   - Value for argv[0] from main.
*
* Description:  Generates the default .ini file for the installed video
*               card. This .ini file is used to remove support for specific
*               video modes from the TSR. In order to generate the default
*               .ini file,
*
****************************************************************************/
{
    union REGS      regs;
    struct SREGS    sregs;
    VgaInfoBlock    vgaInfo;
    char            *p,buf[255];
    int             i,oldMode;
    bool            old50Lines;
    short           *modeTable;
    FILE            *inifile;

    sregs.es = FP_SEG(&vgaInfo);
    regs.x.di = FP_OFF(&vgaInfo);
    regs.x.ax = 0x4F00;
    int86x(0x10,&regs,&regs,&sregs);
    if (regs.x.ax != 0x004F) {
        printf("VESA BIOS call 00h failed!\n");
        exit(1);
        }
    modeTable = vgaInfo.VideoModePtr;

    p = strrchr(argv0,'\\') + 1;
    i = p-argv0;
    strncpy(buf,argv0,i);
    buf[i] = '\0';
    strcat(buf,INIFILE_NAME);
    if ((inifile = fopen(buf,"wt")) != NULL) {
        fprintf(inifile,REMOVE);
        fprintf(inifile,"\n\n");

        regs.x.ax = 0x4F03;
        int86(0x10,&regs,&regs);
        oldMode = regs.x.bx;
        old50Lines = false;             /* Default to 25 line mode      */
        if (oldMode == 0x3) {
            regs.x.ax = 0x1130;
            regs.x.bx = 0;
            regs.x.dx = 0;
            int86(0x10,&regs,&regs);
            old50Lines = (regs.h.dl == 49);
            }

        while (*modeTable != -1) {
            regs.x.ax = 0x4F02;
            regs.x.bx = *modeTable;
            int86(0x10,&regs,&regs);
            if (regs.x.ax != 0x004F) {
                /* The video mode did not set, so remove support for it */

                fprintf(inifile,"%03X",*modeTable);
                }
            regs.x.ax = 0x4F02;
            regs.x.bx = oldMode;
            int86(0x10,&regs,&regs);
            modeTable++;
            }

        regs.x.ax = 0x4F02;
        regs.x.bx = oldMode;
        int86(0x10,&regs,&regs);
        if (old50Lines) {
            regs.x.ax = 0x1112;
            regs.x.bx = 0;
            int86(0x10,&regs,&regs);    /* Restore 50 line mode         */
            }
        fclose(inifile);
        }
    else {
        printf("Could not create .ini file!\n");
        exit(1);
        }
}

void parseArguments(int argc,char *argv[])
/****************************************************************************
*
* Function:     parseArguments
* Parameters:   argc    - Number of command line arguments
*               argv    - Array of command line arguments
*
* Description:  Parses the command line and forces detection of specific
*               SuperVGA's if specified.
*
****************************************************************************/
{
    int     i,option;
    char    *argument,*argv0 = argv[0];

    /* Parse command line options */

    do {
        option = getopt(argc,argv,"S:s:C:c:M:m:D:d:UuIihQqGgTt",&argument);
        if (isascii(option))
            option = tolower(option);
        switch(option) {
            case 'i':
                skipDetect = true;
                break;
            case 's':
                i = FIRST_SVGA;
                while (1) {
                    if (stricmp(getSuperVGAName(i),argument) == 0) {
                        superVGA = i;
                        break;
                        }
                    if (++i > LAST_SVGA) {
                        printf("Unknown SuperVGA chip. Valid values are:\n\n");
                        for (i = FIRST_SVGA; i <= LAST_SVGA; i++)
                            printf("    \"%s\"\n", getSuperVGAName(i));
                        exit(1);
                        }
                    }
                break;
            case 'c':
                chipID = atoi(argument);
                break;
            case 'm':
                memory = atoi(argument);
                break;
            case 'd':
                dac = atoi(argument);
                break;
            case 'q':
                quiet = true;
                break;
            case 'g':
                if (isLoaded()) {
                    generateINI(argv0);
                    printf("Default .ini file generated.\n");
                    exit(1);
                    }
                else {
                    printf("Universal VESA VBE is not loaded - cannot generate .ini file.\n");
                    }
                exit(1);
                break;
            case 'u':
                if (isLoaded()) {
                    if (!quiet)
                        printf("Removing Universal VESA VBE from memory.\n");
                    unload();
                    }
                else printf("Universal VESA VBE is not loaded - cannot remove.\n");
                exit(1);
                break;
            case 't':
                installTSR = false;
                break;
            case 'h':
            case INVALID:
                help();
            }
        } while (option != ALLDONE);
}

void readINIFile(char *argv0)
/****************************************************************************
*
* Function:     readINIFile
* Parameters:   argv0   - Value for argv[0] from main.
*
* Description:  Reads the 'univbe.ini' file from the same directory that the
*               univbe program was started from, and reads in the values
*               of the video modes to remove support for. If the .ini
*               file cannot be found, a default ini file will be created
*               before going TSR.
*
****************************************************************************/
{
    char    *p,buf[255];
    int     i,mode;
    FILE    *inifile;

    removedModes[0] = -1;       /* Terminate the list of removed modes  */

    p = strrchr(argv0,'\\') + 1;
    i = p-argv0;
    strncpy(buf,argv0,i);
    buf[i] = '\0';
    strcat(buf,INIFILE_NAME);
    if ((inifile = fopen(buf,"rt")) != NULL) {
        /* The .ini file was found, so read in the value of modes we
         * are to remove
         */

        while (fgets(buf,255,inifile)) {
            if (strnicmp(buf,REMOVE,sizeof(REMOVE)-1) == 0)
                break;
            }

        i = 0;
        while (fgets(buf,255,inifile)) {
            if (sscanf(buf,"%x",&mode) == 1) {
                removedModes[i++] = mode;
                }
            }
        removedModes[i] = -1;   /* Terminate the list of removed modes  */
        fclose(inifile);
        }
}

void main(int argc,char *argv[])
{
    readINIFile(argv[0]);
    parseArguments(argc,argv);

    if (!skipDetect)
        detect(&superVGA,&chipID,&memory,&dac,&pageFlip,&VESAcompliant);

    if (!installTSR)
        exit(superVGA);     /* Return SuperVGA id as return code        */

    if (!quiet) {
        printf("UNIVBE - Universal VESA Video BIOS Extension (VBE) Version 1.2\n");
		printf("         Release %s (%s)\n\n",release, __DATE__);
        printf("Copyright (C) 1993 Kendall Bennett\n\n");
        }
    if (isLoaded()) {
        printf("Universal VESA VBE is already loaded - installation aborted.\n");
        exit(-1);
        }
    if (superVGA == grSVGA_NONE) {
        printf("Supported video card not detected - installation aborted.\n");
        exit(-2);
        }

    if (memory == 0) {
        printf("Unable to detect video memory size. Please consult your video card manual\n");
        printf("and use the -m command line switch to specify the amount of video memory\n");
        printf("that your video card has on board. If your card has 1Mb of video memory,\n");
        printf("you would use the following command to start the Universal VESA VBE:\n\n");
        printf("    univbe -m1024\n");
        exit(-3);
        }

    if (!quiet) {
        printf("Installing for: %s",getSuperVGAName(superVGA));
        if (getChipsetName(superVGA,chipID) != NULL)
            printf(" (%s)", getChipsetName(superVGA,chipID));
        printf("\nOld BIOS:       ");
        if (VESAcompliant) {
            printf("VESA VBE %d.%d compliant\n",
                (VESAcompliant & 0xFF00) >> 8, VESAcompliant & 0xFF);
            }
        else
            printf("Normal VGA\n");
        printf("DAC:            %s\n", getDACName(dac));
        if (memory < 1024)
            printf("Memory:         %d Kb\n",memory);
        else
            printf("Memory:         %d Mb\n",memory / 1024);
        }

    if (!install(superVGA,chipID,memory,dac,pageFlip,VESAcompliant)) {
        printf("ERROR: Could not install correctly - internal buffers too small!\n");
        printf("       Please report this problem to have it rectified promptly.\n");
        exit(-4);
        }
    goTSR();
}
