/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = MODEINFO                                       */
/*                                                                    */
/*   Description     = VIDEOPMI interface to obtain the modes         */
/*                     available from the base video subsystem        */
/*                                                                    */
/*   Function        = SetObtainableModes                             */
/*                                                                    */
/*                                                                    */
/* CHANGE ACTIVITY =                                                  */
/*   DATE      FLAG        APAR   CHANGE DESCRIPTION                  */
/*   --------  ----------  -----  ------------------------------------*/
/*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx                             */
/*   08/09/94              91949  Use VIDEOPMI int. to obtain modes   */
/*   08/10/94  Senja              Made all PMI string comparisons case*/
/*   09/02/94  @V3.0YEE01  94321  Fix up the string compare routine   */
/*   01/09/95  N/A        108935  Remove Defect 105730                */
/*                                                                    */
/**********************************************************************/
#ifdef S3
#define  MAXCHARLEN   256

#define INCL_BASE               /*            */
#define  INCL_DOSFILEMGR
#define  INCL_DOSMEMMGR
#define  INCL_DOSMISC

#define INCL_GPIBITMAPS
#define INCL_GPIDISPPRF
#include <eddinclt.h>
#include "edddtypt.h"
#include "eddesres.h"
#include <svgadefs.h>           /*            */
BOOL    fNeedExtraModeSwitch;

extern PSCREENRESOLUTION asrScreenResolution;
extern USHORT            usNumScreenResolutions;
// why bother ifdef'ing when BPP24 is enabled all the time.
#ifdef   BPP24
extern DDTType           DDT;
#endif

ULONG ulFilePos, ulFileSize;

PVOID MyAlloc(HFILE hf)
{
   FILESTATUS3  fib;
   ULONG        ulcbread;
   PVOID        pbuf;

   if(!DosQueryFileInfo(hf, FIL_STANDARD, &fib, sizeof(FILESTATUS3)))
   {
      if(!DosAllocMem(&pbuf, fib.cbFile, fALLOC))
      {
         if(!DosRead(hf, pbuf, fib.cbFile, &ulcbread))
         {
            ulFileSize = ulcbread;
            ulFilePos  = 0;
            return(pbuf);
         }
      }
   }

   return(NULL);
}

int MyGetString(PSZ pszStr, ULONG ulsize, PPVOID ppbuf)
{
   ULONG      cnt;

   if(ulFilePos >= ulFileSize) return(0);

   for (cnt=0;cnt < ulsize;cnt++)
   {
      if (((PSZ)*ppbuf)[cnt] == 0x0a)
      {
         pszStr[cnt] = 0;
         (*((PULONG)ppbuf)) += cnt+1;
         ulFilePos += cnt+1;
         break;
      }
      else
      {
         if(ulFilePos + cnt > ulFileSize) return(0);
         pszStr[cnt] = ((PSZ)*ppbuf)[cnt];
      }
   }

   return (1);
}

ULONG Myatoi(PSZ psz)
{

  PSZ    p1;
  ULONG  ulvalue = 0;

  for(p1 = psz; *p1 == 0x20; p1++);   /* Strip leading blanks */

  for(;*p1>='0' && *p1<='9';p1++)     /* Convert ASCII to integer */
  {
     ulvalue = ulvalue * 10;
     ulvalue += *p1 - '0';
  }

  return(ulvalue);
}

int MySscanf(PSZ pszBuffer, PULONG pulid)
{
   PSZ        p1;

   for (p1=pszBuffer;*p1 != '=';p1++);
   *pulid = Myatoi(&p1[1]);
   return(1);

}

//---------------------------------------------------------------------
// MySkipBlanks
//
// Returns a pointer to the first non-blank character in the string.
// Blank characters are defined as spaces, tabs, or the C style comment
// delimiters used in the .PMI file.
//---------------------------------------------------------------------
PSZ MySkipBlanks(PSZ pszBuffer)
{
    PSZ pStr;

    for (pStr=pszBuffer; *pStr != NULL; pStr++)
    {
        if ((*pStr != ' ' ) &&
            (*pStr != '/' ) &&
            (*pStr != '*' ) &&
            (*pStr != '\t'))
            break;
    }
    return(pStr);
}

//---------------------------------------------------------------------
// MyStrNCmp
//
// Compares the first N characters in both strings.  If all characters
// are equal, returns TRUE.  Otherwise, returns FALSE.  Note: No special
// check are provided to ensure either or both strings ends prematurely.
//---------------------------------------------------------------------
BOOL MyStrNCmp(PSZ str1, PSZ str2, ULONG cnt)
{
    ULONG i;

    for (i=0; i<cnt; i++)
    {
        if (str1[i] != str2[i])
            return(FALSE);
    }
    return(TRUE);
}

//---------------------------------------------------------------------
// MyStrIStr
//           
// Searches thru the string 1 for occurance of string 2, ignoring the case.
// cnt specifies the length of string 2 (including zero) so that we
// don't overrun the string 1. If string 2 contained in string 1, returnes TRUE
//---------------------------------------------------------------------
#define _toupper(_c)   (UCHAR)  ( (_c)-'a'+'A' )
#define _islower(_c)   ((_c >= 'a') && (_c <= 'z'))
BOOL MyStrIStr(PSZ str1, PSZ str2, ULONG cnt)
{
  ULONG i, len1;

  // convert both to upper case. determine the length of first
  for(i=0;str1[i];i++)
  {
     str1[i] = _islower(str1[i]) ? _toupper(str1[i]) : str1[i];
  }

  len1 = i;             /*don't allow the search pass end of string1 @V3.0YEE01*/

  if (len1 < cnt)                                                  /*@V3.0YEE01*/
    return FALSE;       /*string1 less than string length to compare @V3.0YEE01*/

  for(i=0;i<cnt;i++)
     str2[i] = _islower(str2[i]) ? _toupper(str2[i]) : str2[i];

  for(i=0;i<len1;i++)
  {
     if (str1[i] == str2[0])    //first character is the same, try comparing
     {
       if (MyStrNCmp(&str1[i],str2,cnt))                           /*@V3.0YEE01*/
         return TRUE;
     }
  }
  return FALSE;
}
HFILE Myfopen(PSZ pszFilename)
{
   HFILE   hfile;
   ULONG   ulact;
   ULONG   rc;

   rc = DosOpen(pszFilename, &hfile, &ulact, 0L, 0L, 1L, 0x40L, NULL);
   return (rc ? (HFILE)0 : hfile);
}

/*
** Use VIDEOPMI interface if available to obtain the adapter description and the video
** modes obtainable. Returns 0 and modifies the global mode table and flags
** if the interface is available and successful.                          
*/
APIRET GetModeTable(char* szPMIFileName)
{
  APIRET rc;
  ULONG  cnt, i;
  HMODULE hmodVIDEOPMI;
  ULONG ulTotalModes;
  VIDEOMODEINFO *pModes;
  VIDEO_ADAPTER sAdapterInstance;
  PFNVIDEOPMIREQUEST pfnPMIRequest;     /* PMI request entry point into VIDEOPMI */
  char sFail[256] = {0};
  if (!(rc = DosLoadModule(sFail, 256, "VIDEOPMI", &hmodVIDEOPMI)))
    rc = DosQueryProcAddr(hmodVIDEOPMI, 0L,"VIDEOPMI32Request", (PFN *) &pfnPMIRequest);
  if(!rc && (!(rc = pfnPMIRequest(&sAdapterInstance,
                           PMIREQUEST_LOADPMIFILE,
                           szPMIFileName,
                           NULL))))
  {
     /*
     ** Query number of mode entries
     */
     if (!(rc = pfnPMIRequest(&sAdapterInstance,
                        PMIREQUEST_QUERYMAXMODEENTRIES,
                        NULL,
                        &ulTotalModes)))
     {
        if (!(rc=DosAllocMem(&pModes,
                           ulTotalModes*sizeof(VIDEOMODEINFO),
                           fALLOC)))
        {
          /*
          ** Obtain all ModeInfoData
          */
          rc=pfnPMIRequest(&sAdapterInstance,
                         PMIREQUEST_QUERYMODEINFODATA,
                         NULL,
                         pModes);
          if (rc)
          {
             DosFreeMem(pModes);
             DosFreeModule(hmodVIDEOPMI);
          }
        }
        else
          DosFreeModule(hmodVIDEOPMI);
     }
     else
        DosFreeModule(hmodVIDEOPMI);
  }
  else
     DosFreeModule(hmodVIDEOPMI);
  if (!rc)
  {
      /*
      **           
      ** The old way is:
      ** asr is a hardcoded mode list in EDDESRES.C
      ** asrScreenResolution is a pointer to asr.
      ** SetObtainable modes would go thru the mode table and set the
      ** OBTAINABLE flags.
      ** Problem: supported modes are limited to the hardcoded asr list
      **
      ** The new way should be:
      ** asrScreenResolution is allocated depending on how many >= 8bpp
      ** modes are supported by the VIDEOPMI. ModeTable from PMI is
      ** copied into the asrScreenResolution table.
      **
      ** I've implemented both ways. SENJA_RESOLUTION_SCHEME macro will
      ** enable the new way as soon as it is desirable.
      */
      #ifndef SENJA_RESOLUTION_SCHEME
      // old way
      for (i=0;i < ulTotalModes;i++)
      {
        if((pModes[i].usType & MODE_FLAG_GRAPHICS) && (pModes[i].bBitsPerPixel >= 8))
        {
          for(cnt=0;cnt<usNumScreenResolutions;cnt++)
          {
            if ( asrScreenResolution[cnt].width  == (ULONG) pModes[i].usXResolution &&
                 asrScreenResolution[cnt].height == (ULONG) pModes[i].usYResolution &&
                 (asrScreenResolution[cnt].colors == (ULONG) (1 << pModes[i].bBitsPerPixel)) &&
                 asrScreenResolution[cnt].planes == (ULONG) pModes[i].bBitPlanes)
            {
              asrScreenResolution[cnt].floptions |= DSP_RESOLUTION_OBTAINABLE;
            }
          }
        }
      }
      #else

      // new way
      for(i=0,usNumScreenResolutions=0;i<ulTotalModes;i++)
      {
        if ((pModes[i].usType & MODE_FLAG_GRAPHICS) && (pModes[i].bBitsPerPixel >= 8))
          usNumScreenResolutions++;
      }
      if (!usNumScreenResolutions &&
          !(rc=DosAllocMem(&pModes,
                           usNumScreenResolutions*sizeof(SCREENRESOLUTIONS),
                           fALLOC)))
      {
        for(i=0;j=0;i<usNumScreenResolutions;i++)
        {
          if ((pModes[i].usType & MODE_FLAG_GRAPHICS) && (pModes[i].bBitsPerPixel >= 8))
          {
            //add the resolution to the table
            asrScreenResolution[j].width  = (ULONG) pModes[i].usXResolution;
            asrScreenResolution[j].height = (ULONG) pModes[i].usYResolution;
            asrScreenResolution[j].colors = (ULONG) (1L << pModes[i].bBitPsPerPixel);
            asrScreenResolution[j].planes = (ULONG) pModes[i].bBitPlanes;
            asrScreenResolution[j].floptions |= DSP_RESOLUTION_OBTAINABLE;
          }
        }
      }
      asrScreenResolution[0].floptions |= DSP_RESOLUTION_DEFAULT;
      #endif
      /*
      ** The format of the DAC string is "DACNAME Company, custom string"
      ** Try to identify company names in order to special case.
      */
      if (i<sizeof(sAdapterInstance.Adapter.szDACString))
      {
        if (MyStrIStr(sAdapterInstance.Adapter.szDACString,"MUSIC",5)) /*@V3.0YEE01*/
           DDT.fScreenFlags &= ~USE_ATTDAC;
        else if (MyStrIStr(sAdapterInstance.Adapter.szDACString,"BROOKTREE",9))/*@V3.0YEE01*/
           DDT.fScreenFlags |= (USE_BROOKDAC | USE_8BIT_DAC);
        else if (MyStrIStr(sAdapterInstance.Adapter.szDACString,"AT&T",4))
           DDT.fScreenFlags |= USE_8BIT_DAC;
      }
      if (MyStrIStr(sAdapterInstance.Adapter.szRevision,"Special Case Mode Assist",24))
      {
          fNeedExtraModeSwitch = TRUE;
      }
      DosFreeMem(pModes);
      DosFreeModule(hmodVIDEOPMI);
  }
  return rc;
}
VOID SetObtainableModes(VOID)
{
   ULONG      ulDrive;
   CHAR       pszPMIFileName[20] = "#:\\OS2\\SVGADATA.PMI";
   HFILE      hfpmi;
   ULONG      cnt;
   PVOID      pBuf;
   PSZ        pNonBlank;
   char       pszPMIStr[MAXCHARLEN];
   ULONG      ul_xres, ul_yres, ul_clr, ul_planes;

   #ifdef   BPP24
   DDT.fScreenFlags |= USE_ATTDAC;
   #endif

   fNeedExtraModeSwitch = FALSE;

   /* Search for the correct PMI File           */
   /*   We will append the system boot drive    */
   /*   to the OS2 dir and try to open the      */
   /*   file.                                   */

   DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulDrive, sizeof(ULONG));
   pszPMIFileName[0] = (UCHAR)(( (ULONG)'A' + ulDrive ) - 1);

   /*
   **           
   ** Use videopmi interface to obtain the mode table and adapter info.
   */
   if (!GetModeTable(pszPMIFileName))
     return;

   /* Open and parse for a match                            */
   /*   Search will be base on the [comment] key word in    */
   /*   the appropriate pmi file                            */

   if((hfpmi = Myfopen(pszPMIFileName)) && (pBuf = MyAlloc(hfpmi)))
   {

      while (MyGetString(pszPMIStr, MAXCHARLEN, &pBuf))
      {
         if (MyStrNCmp(pszPMIStr,"[ModeInfo]",10))
         {
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* Mode Attributes      */
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* Bytes Per Scan Line  */
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* X Resolution         */
            MySscanf(pszPMIStr, &ul_xres);
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* Y Resolution         */
            MySscanf(pszPMIStr, &ul_yres);
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* Text Rows            */
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* Bits Per Pixel       */
            MySscanf(pszPMIStr, &ul_clr);
            ul_clr = 1 << ul_clr;                          /* Convert BPP to #clrs */
            MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);     /* Number of Planes     */
            MySscanf(pszPMIStr, &ul_planes);

            for(cnt=0;cnt<usNumScreenResolutions;cnt++)
            {
               if ( asrScreenResolution[cnt].width  == ul_xres   &&
                    asrScreenResolution[cnt].height == ul_yres   &&
                    asrScreenResolution[cnt].colors == ul_clr    &&
                    asrScreenResolution[cnt].planes == ul_planes)
               {
                  asrScreenResolution[cnt].floptions |= DSP_RESOLUTION_OBTAINABLE;
               }
            }

         }
         else if (MyStrNCmp(pszPMIStr,"[AdapterType]",13))
              {
                  MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);
                  MyGetString(pszPMIStr, MAXCHARLEN, &pBuf);

                  //---------------------------------------------------
                  // Although in most cases we can skip the extra mode
                  // set during death, we make a special case for the
                  // Lacuna machines whose BIOS doesn't deal with it
                  // properly.  We originally took out the extra mode
                  // switch after receiving complaint of monitors
                  // getting 'cooked' in Europe.  @RCW
                  //---------------------------------------------------
                  pNonBlank = MySkipBlanks(pszPMIStr);
                  if (MyStrNCmp(pNonBlank,"Special Case Mode Assist",24))
                  {
                      fNeedExtraModeSwitch = TRUE;
                  }

              }
              else
              {
                  //---------------------------------------------------
                  // Fetch the DAC type to determine what method of
                  // DAC interface we'll use and to determine if we
                  // can use 256 grey scales rather than 64.
                  //---------------------------------------------------
                  pNonBlank = MySkipBlanks(pszPMIStr);

                  if (MyStrNCmp(pNonBlank,"HI-COLOR DAC by ",16))
                  {
                      pNonBlank += 16;

                      if (MyStrNCmp(pNonBlank,"Music",5))
                          DDT.fScreenFlags &= ~USE_ATTDAC;

                      else if (MyStrNCmp(pNonBlank,"Brook",5))
                               DDT.fScreenFlags |= (USE_BROOKDAC | USE_8BIT_DAC);

                           else if (MyStrNCmp(pNonBlank,"AT&T",4))
                                    DDT.fScreenFlags |= USE_8BIT_DAC;
                  }

              }

      } // End While

      DosFreeMem(pBuf);
      DosClose(hfpmi);
   }

   return;
}
#endif
