/*
 *  This program is based on code extracted from Shawn Hargreaves'
 *  vesainfo.c program. We have modified it to output video configuration 
 *  information needed by our fractint program. This program will help you 
 *  use available VESA video modes with Fractint.
 *
 *  Obviously Shawn is not responsible for any mistakes we have made in our
 *  hack. Let me add that we really appreciate his open-source Allegro 
 *  package, which made this program possible.
 *
 *  Compile using djgpp with
 *  gcc -o makefcfg makefcfg.c
 * 
 *  Tim Wegner
 *  The Stone Soup Group
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dpmi.h>
#include <go32.h>
#include <sys/movedata.h>
#include <sys/farptr.h>
#include <sys/segments.h>

/* 
 * The following stuff is what is needed from allegro.h.
 * Since it is not much, I have eliminated the allegro.h include.
 */
 
#ifndef TRUE 
#define TRUE         -1
#define FALSE        0
#endif

typedef struct FRACT_INFO
{
   unsigned short mode;
   unsigned char  BitsPerPixel;
   unsigned short XResolution;
   unsigned short YResolution;
   char color_desc[80];
   char oem_string[256];
   char oemvendor_string[256];
   char oemproductname_string[256];
} FRACT_INFO;

#define MASK_LINEAR(addr)     (addr & 0x000FFFFF)
#define RM_TO_LINEAR(addr)    (((addr & 0xFFFF0000) >> 12) + (addr & 0xFFFF))
#define RM_OFFSET(addr)       (addr & 0xF)
#define RM_SEGMENT(addr)      ((addr >> 4) & 0xFFFF)

typedef struct VESA_INFO 
{ 
   unsigned char  VESASignature[4]     __attribute__ ((packed));
   unsigned short VESAVersion          __attribute__ ((packed));
   unsigned long  OEMStringPtr         __attribute__ ((packed));
   unsigned char  Capabilities[4]      __attribute__ ((packed));
   unsigned long  VideoModePtr         __attribute__ ((packed)); 
   unsigned short TotalMemory          __attribute__ ((packed)); 
   unsigned short OemSoftwareRev       __attribute__ ((packed)); 
   unsigned long  OemVendorNamePtr     __attribute__ ((packed)); 
   unsigned long  OemProductNamePtr    __attribute__ ((packed)); 
   unsigned long  OemProductRevPtr     __attribute__ ((packed)); 
   unsigned char  Reserved[222]        __attribute__ ((packed)); 
   unsigned char  OemData[256]         __attribute__ ((packed)); 
} VESA_INFO;

typedef struct MODE_INFO 
{
   unsigned short ModeAttributes       __attribute__ ((packed)); 
   unsigned char  WinAAttributes       __attribute__ ((packed)); 
   unsigned char  WinBAttributes       __attribute__ ((packed)); 
   unsigned short WinGranularity       __attribute__ ((packed)); 
   unsigned short WinSize              __attribute__ ((packed)); 
   unsigned short WinASegment          __attribute__ ((packed)); 
   unsigned short WinBSegment          __attribute__ ((packed)); 
   unsigned long  WinFuncPtr           __attribute__ ((packed)); 
   unsigned short BytesPerScanLine     __attribute__ ((packed)); 
   unsigned short XResolution          __attribute__ ((packed)); 
   unsigned short YResolution          __attribute__ ((packed)); 
   unsigned char  XCharSize            __attribute__ ((packed)); 
   unsigned char  YCharSize            __attribute__ ((packed)); 
   unsigned char  NumberOfPlanes       __attribute__ ((packed)); 
   unsigned char  BitsPerPixel         __attribute__ ((packed)); 
   unsigned char  NumberOfBanks        __attribute__ ((packed)); 
   unsigned char  MemoryModel          __attribute__ ((packed)); 
   unsigned char  BankSize             __attribute__ ((packed)); 
   unsigned char  NumberOfImagePages   __attribute__ ((packed));
   unsigned char  Reserved_page        __attribute__ ((packed)); 
   unsigned char  RedMaskSize          __attribute__ ((packed)); 
   unsigned char  RedMaskPos           __attribute__ ((packed)); 
   unsigned char  GreenMaskSize        __attribute__ ((packed)); 
   unsigned char  GreenMaskPos         __attribute__ ((packed));
   unsigned char  BlueMaskSize         __attribute__ ((packed)); 
   unsigned char  BlueMaskPos          __attribute__ ((packed)); 
   unsigned char  ReservedMaskSize     __attribute__ ((packed)); 
   unsigned char  ReservedMaskPos      __attribute__ ((packed)); 
   unsigned char  DirectColorModeInfo  __attribute__ ((packed));
   unsigned long  PhysBasePtr          __attribute__ ((packed)); 
   unsigned long  OffScreenMemOffset   __attribute__ ((packed)); 
   unsigned short OffScreenMemSize     __attribute__ ((packed)); 
   unsigned short LinBytesPerScanLine  __attribute__ ((packed));
   unsigned char  BnkNumberOfPages     __attribute__ ((packed));
   unsigned char  LinNumberOfPages     __attribute__ ((packed));
   unsigned char  LinRedMaskSize       __attribute__ ((packed));
   unsigned char  LinRedFieldPos       __attribute__ ((packed));
   unsigned char  LinGreenMaskSize     __attribute__ ((packed));
   unsigned char  LinGreenFieldPos     __attribute__ ((packed));
   unsigned char  LinBlueMaskSize      __attribute__ ((packed));
   unsigned char  LinBlueFieldPos      __attribute__ ((packed));
   unsigned char  LinRsvdMaskSize      __attribute__ ((packed));
   unsigned char  LinRsvdFieldPos      __attribute__ ((packed));
   unsigned long  MaxPixelClock        __attribute__ ((packed));
   unsigned char  Reserved[190]        __attribute__ ((packed));
} MODE_INFO;

VESA_INFO vesa_info;
MODE_INFO mode_info;

#define MAX_VESA_MODES 1024

FRACT_INFO fract_info[MAX_VESA_MODES];

char *fkeys[] = 
{
   "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10",
   "SF1","SF2","SF3","SF4","SF5","SF6","SF7","SF8","SF9","SF10",
   "CF1","CF2","CF3","CF4","CF5","CF6","CF7","CF8",
   "AF1","AF2","AF3","AF4","AF5","AF6","AF7","AF8"
};

int maxkey = sizeof(fkeys)/sizeof(char *);

/* program options */

int show_true_color = 0;   /* if true show truecolor modes */
int show_16_color   = 0;   /* if true show 16 color modes */
int start_key       = 0;   /* starting Fkey */
int overwrite       = 0;   /* file trample flag */

unsigned short mode[MAX_VESA_MODES];
int modes;

char oem_string[256];
char oemvendor_string[256];
char oemproductname_string[256];
char oemproductrev_string[256];

void get_string(char *s, unsigned long addr)
{
   if (addr) 
   {
      addr = RM_TO_LINEAR(addr);
      while (_farnspeekb(addr) != 0)
	 *(s++) = _farnspeekb(addr++);
      *s = 0;
   }
   else
      strcpy(s, "(null)");
}

int get_vesa_info()
{
   __dpmi_regs r;
   unsigned long mode_ptr;
   int c;

   for (c=0; c<(int)sizeof(VESA_INFO); c++)
      _farpokeb(_dos_ds, MASK_LINEAR(__tb)+c, 0);

   dosmemput("VBE2", 4, MASK_LINEAR(__tb));

   r.x.ax = 0x4F00;
   r.x.di = RM_OFFSET(__tb);
   r.x.es = RM_SEGMENT(__tb);
   __dpmi_int(0x10, &r);
   if (r.h.ah) 
   {
      printf("Int 0x10, ax=0x4F00 failed! ax=0x%02X\n", r.x.ax);
      return -1;
   }

   dosmemget(MASK_LINEAR(__tb), sizeof(VESA_INFO), &vesa_info);

   if (strncmp(vesa_info.VESASignature, "VESA", 4) != 0) 
   {
      printf("Bad VESA signature! (%4.4s)\n", vesa_info.VESASignature);
      return -1;
   }

   _farsetsel(_dos_ds);

   modes = 0;
   mode_ptr = RM_TO_LINEAR(vesa_info.VideoModePtr);
   mode[modes] = _farnspeekw(mode_ptr);

   while (mode[modes] != 0xFFFF) 
   {
      modes++;
      mode_ptr += 2;
      mode[modes] = _farnspeekw(mode_ptr);
   }

   get_string(oem_string, vesa_info.OEMStringPtr);
   get_string(oemvendor_string, vesa_info.OemVendorNamePtr);
   get_string(oemproductname_string, vesa_info.OemProductNamePtr);
   get_string(oemproductrev_string, vesa_info.OemProductRevPtr);

   return 0;
}

typedef struct COLORINFO
{
   int size;
   int pos;
   char c;
} COLORINFO;

COLORINFO colorinfo[4];

int colorcount = 0;

int color_cmp(const void *e1, const void *e2)
{
   COLORINFO *c1 = (COLORINFO *)e1;
   COLORINFO *c2 = (COLORINFO *)e2;

   return c2->pos - c1->pos;
}

void add_color(int size, int pos, char c)
{
   if ((size > 0) || (pos > 0)) 
   {
      colorinfo[colorcount].size = size;
      colorinfo[colorcount].pos = pos;
      colorinfo[colorcount].c = c;
      colorcount++;
   }
}

void get_color_desc(char *s)
{
   int c;

   if (colorcount > 0) 
   {
      qsort(colorinfo, colorcount, sizeof(COLORINFO), color_cmp);

      for (c=0; c<colorcount; c++)
	 *(s++) = colorinfo[c].c;

      *(s++) = ' ';

      for (c=0; c<colorcount; c++)
	 *(s++) = '0' + colorinfo[c].size;

      colorcount = 0;
   }

   *s = 0;
}

int get_mode_info(int mode, FRACT_INFO *f_info)
{
   __dpmi_regs r;
   char color_desc[80];
   int c;

   for (c=0; c<(int)sizeof(MODE_INFO); c++)
      _farpokeb(_dos_ds, MASK_LINEAR(__tb)+c, 0);

   r.x.ax = 0x4F01;
   r.x.di = RM_OFFSET(__tb);
   r.x.es = RM_SEGMENT(__tb);
   r.x.cx = mode;
   __dpmi_int(0x10, &r);
   if (r.h.ah) 
   {
      printf("Int 0x10, ax=0x4F01 failed! ax=0x%02X\n", r.x.ax);
      exit(1);
   }

   dosmemget(MASK_LINEAR(__tb), sizeof(MODE_INFO), &mode_info);

   add_color(mode_info.RedMaskSize,      mode_info.RedMaskPos,      'R');
   add_color(mode_info.GreenMaskSize,    mode_info.GreenMaskPos,    'G');
   add_color(mode_info.BlueMaskSize,     mode_info.BlueMaskPos,     'B');
   add_color(mode_info.ReservedMaskSize, mode_info.ReservedMaskPos, 'x');
   get_color_desc(color_desc);

   /* no text modes */
   if (!(mode_info.ModeAttributes & 16))
      return(0);
      
   /* no 16 color modes */ 
   if(!show_16_color && mode_info.BitsPerPixel < 8)
      return(0);

   /* no true color modes */ 
   if(!show_true_color && mode_info.BitsPerPixel > 8)
      return(0);

   /*
    * Copy stuff needed to build fractint mode information 
    */
   f_info->mode                         = mode;
   f_info->BitsPerPixel                 = mode_info.BitsPerPixel;
   f_info->XResolution                  = mode_info.XResolution;
   f_info->YResolution                  = mode_info.YResolution;
   strncpy(f_info->color_desc,            color_desc,            80);
   strncpy(f_info->oem_string,            oem_string,            256);
   strncpy(f_info->oemvendor_string,      oemvendor_string,      256);
   strncpy(f_info->oemproductname_string, oemproductname_string, 256);

   return 1;
}

int fract_cmp(const void *e1, const void *e2)
{
   FRACT_INFO *c1 = (FRACT_INFO *)e1;
   FRACT_INFO *c2 = (FRACT_INFO *)e2;

   /* primary key */
   if(c1->BitsPerPixel != c2->BitsPerPixel)
      return(c1->BitsPerPixel - c2->BitsPerPixel);

   /* secondary key */
   if(c1->XResolution != c2->XResolution)
      return(c1->XResolution - c2->XResolution);

   /* tertiary key */
   if(c1->YResolution != c2->YResolution)
      return(c1->YResolution - c2->YResolution);

   return(0);      
}

void write_fractint_cfg(int modes, FRACT_INFO *f_info)
{
    FILE *fract_cfg;
    int i;
    int keynum;


    if((fract_cfg = fopen("fractint.cfg","w")) == NULL)
    {
       printf("Can't open fractint.cfg \n");
       return;
    }
    fprintf(fract_cfg,
"; FRACTINT.CFG File\n"
";key  name of adapter/mode	| AX | BX | CX | DX |mode|  x |  y |clr| comments\n"
";=================================================================================\n");
    keynum = start_key;
    for(i=0;i<modes;i++)
    {
        fprintf(fract_cfg,"%-4s,",keynum < maxkey?fkeys[keynum++]:"");
        fprintf(fract_cfg,"VESA %-22s,4f02,",
           f_info[i].color_desc[0]?f_info[i].color_desc:
           f_info[i].BitsPerPixel <= 8?"Color Palette Mode":"no description");
        fprintf(fract_cfg,"%-3X,   0,   0,  28,%4d,%4d,",
           f_info[i].mode,
           f_info[i].XResolution,
           f_info[i].YResolution);
        switch(f_info[i].BitsPerPixel)
        {
        case 1:
        case 2:
        case 4:
        case 8:
            fprintf(fract_cfg,"%4u,",1<<(f_info[i].BitsPerPixel));
            break;
        case 15:
            fprintf(fract_cfg," 32k,");
            break;
        case 16:
            fprintf(fract_cfg," 64k,");
            break;
        case 24:
        case 32:
            fprintf(fract_cfg," 16m,");
            break;
        default:
            fprintf(fract_cfg,"unknown");
            break;
        }
        fprintf(fract_cfg,"VESA mode from OEM %s",f_info[i].oem_string);
        fprintf(fract_cfg,"\n");
    }
}

int main(int argc, char *argv[])
{
   int c;
   int i;

   printf("\n");
   printf("Makefcfg 1.0  -- A Fractint video utility by The Stone Soup Group \n");

   for (c=1; c<argc; c++)
   {
      char *opt;
      
      strlwr(argv[c]);
      for(i=0; i<maxkey; i++)
      {
         char this_key[5];
         strcpy(this_key,fkeys[i]);
         strlwr(this_key);
         if(!strcmp(argv[c],this_key))
         {
            start_key = i;
            break;
         }
      }

      if(i < maxkey) /* means that start_key was set */
         continue;

      if(!strcmp(argv[c],"16"))
      {
         show_16_color = 1;
         continue;
      }
      
      if(*argv[c] == 't')
      {
         show_true_color = 1;
         continue;
      }

      if(*argv[c] == 'o')
      {
         overwrite = 1;
         continue;
      }

      printf(
"\n"
"Makefcfg reads your VESA Video BIOS and writes a Fractint \n"
"   fractint.cfg file with VESA video modes set up.\n\n"
"Syntax: makefcfg [commands ...]\n"
"   optional commands (space separated) include:\n"
"      16        -- causes 16 color modes to be included\n"
"      true      -- causes true color modes to be included\n"
"      <fkey>    -- starting Fractint video mode key. <fkey> is \n"
"                   one of F1, F2, ..., AF1, AF2, ..., CF1, CF1 ...\n"
"      overwrite -- program will overwrite fractcfg.old if it exists\n"
"                   (note: fractint.cfg is always saved to fractcfg.old)\n"
"\n");
      return(1);
   }
   printf("   Option settings (try makefcfg -h to see how to set):\n");
   printf("      Showing 256 color modes [%-3s]  (always)\n","yes");
   printf("      Showing truecolor modes [%-3s]  (default no)\n",show_true_color?"yes":"no");
   printf("      Showing 16 color  modes [%-3s]  (default no)\n",show_16_color?  "yes":"no");
   printf("      Starting FKEY           [%-3s]  (default F2)\n",fkeys[start_key]);
   printf("      Overwrite fractfg.old   [%-3s]  (default no)\n\n",overwrite?"yes":"no");

   if(access("fractint.cfg",0) == 0)
   {
      if(access("fractcfg.old",0))
      {
         printf("fractint.cfg exists - renaming to fractcfg.old\n");
         rename("fractint.cfg","fractcfg.old"); 
      }
      else if(!overwrite)
      {
         printf("Both fractint.cfg and fractcfg.old exist\n");
         printf("Please delete or rename one of them\n");
         exit(0);
      }
   }

   printf("Reading your VESA Video BIOS ...\n");
   if (get_vesa_info() != 0)
      return -1;

   for (i=0,c=0; c<modes; c++)
      if(get_mode_info(mode[c], &fract_info[i]))
         i++;

   qsort(fract_info, i, sizeof(FRACT_INFO),  fract_cmp);

   printf("Begin writing fractint.cfg\n");
   write_fractint_cfg(i, fract_info);
   printf("Done writing fractint.cfg\n");
   return 0;
}
