/****************************************************************************/
/*  video  version 0.4                                                      */
/*                                                                          */
/* program to access the video-blaster and compatible cards                 */
/* for the GNU djgpp/gcc compiler                                           */
/*                                                                          */
/* (C)1994 Bernhard Schwall                                                 */
/*                                                                          */
/* version 0.1                                                              */
/*    show the video under dos and linux and control the sound              */
/*    (on Media Pro Plus)                                                   */
/*                                                                          */
/* version 0.2                                                              */
/*   save and load single pictures in .TGA format                           */
/*                                                                          */
/* version 0.3                                                              */
/*   videomode 680x512 for maximal resolution                               */
/*   videomode 640x480 with 60Hz even if your VGA card has a higher         */
/*     refresh rate                                                         */
/*                                                                          */
/* version 0.4                                                              */
/*   removed the standard path for VIDEO.CFG                                */
/*   bugfixes for DOS                                                       */
/*                                                                          */
/****************************************************************************/

#include "v_access.h"
#include <stdio.h>
#include <string.h>
#ifdef linux
#include <vga.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#else
#include <dos.h>
#include <pc.h>
#endif

/****************************************************************************/

void Set680x512(){
/* set mode 680x512x16 with 61Hz which is the maximum resolution for the VB */
/* only usefull if input is PAL                                             */
  BYTE CRTCdata[25] = {99,84,85,0x80+11,87,0x86,42,0xb2,
                0,0x60,0,0,0,0,0,0,10,0xa0+34,0xff,7,0x1b,40,
                4,0xe3,0xff};
 
  INT i;

#ifdef linux
  vga_setmode(G640x480x16);
#else
  union REGS ireg, oreg;

  /* use EGA and expand to 512 lines to get a mode with 60Hz even if the */
  /* VGA card has 640x480x16 with 72Hz which can't be used with the VB   */
  ireg.x.ax = 0x10;
  int86(0x10, &ireg, &oreg);
#endif

  port_out(0,0x3c4);
  port_out(1,0x3c5);
  /* auf 67 Hz umschalten */
  port_out((port_in(0x3cc) & 0xf3) | 4,0x3c2);
  port_out(3,0x3c5);
  port_out(0x11,0x3d4);
  port_out(port_in(0x3d5) & 0x7f,0x3d5);
  for (i=0; i<=24; i++){
    port_out(i,0x3d4);
    port_out(CRTCdata[i],0x3d5);
  }
#ifdef linux
  /* HSync+, VSync-  to get the same as used in DOS */
/*  port_out((port_in(0x3cc) & 0x33) | 0x84 , 0x3c2);*/
#endif
}

/****************************************************************************/

void Set640x480(){
/* set mode 640x480x16 with 60Hz */
#ifdef linux
  vga_disabledriverreport();
  vga_init();
  vga_setmode(G640x480x16);
  /* HSync+, VSync-  to get the same as used in DOS */
/*  port_out((port_in(0x3cc) & 0x33) | 0x80 , 0x3c2);*/
#else
  unsigned char CRTCdata[25] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0x0B,0x3E,0x00,0x40,0x00,0x00,
  0x00,0x00,0x00,0x00,0xEA,0x8C,0xDF,0x28,0x00,0xE7,0x04,0xE3,
  0x00};
  union REGS ireg, oreg;
  int i;
  /* use EGA and expand to 480 lines to get a mode with 60Hz even if the */
  /* VGA card has 640x480x16 with 72Hz which can't be used with the VB   */
  ireg.x.ax = 0x10;  
  int86(0x10, &ireg, &oreg);

  port_out(0,0x3c4);
  port_out(1,0x3c5);
  port_out(3,0x3c5);
  port_out(0x11,0x3d4);
  port_out(port_in(0x3d5) & 0x7f,0x3d5);
  for (i=0; i<=24; i++){
    port_out(i,0x3d4);
    port_out(CRTCdata[i],0x3d5);
  }
#endif
}

/****************************************************************************/

void SetTextMode(){
#ifdef linux
  vga_setmode(TEXT);
#else
  union REGS ireg, oreg;

  ireg.x.ax = 0x03;
  int86(0x10, &ireg, &oreg);
#endif
}

/****************************************************************************/

void init(char *Path, INT clear){
char Name[200];

  strcpy(Name, Path);
  strcat(Name,"video.cfg");
  Set640x480();
  V_Initialize(Name, clear);
  V_CreateWindow(136,97,367,286);
  V_wr_i2c(0x8a, 6, 0x1c);
}

/****************************************************************************/

void done(){
  V_Exit();
  SetTextMode();
}

/***************************************************************************/

void itoa(INT nr, char *dest)
{ INT length=0;
  char tmp[10];
  char *src;
  
  src=tmp;
  if (nr == 0) { *dest++ = '0';
                 *dest++ = '0';
                 *dest++ = '0';
		 *dest = 0;
  		 return;
  }
  while (nr > 0)
  { *src++ = (0x30+ (char)(nr % 10));
    length++;
    nr /= 10;
  }
  while (length<3){
    *src++ = '0';
    length++;
  }
  while (length>0)
  { *dest++ = *--src;
    length--;
  }
  *dest = 0;
}

/****************************************************************************/

int ReadLineBGR(char *Buffer, int LineNo, int Pixel)
/* read a line from the card and convert it to RGB 24 bit (to BGR) */
{
  BYTE *src;
  INT  z, r, g, b, u, v, y[4], count=1;
  signed char uBild, vBild;

  if ((Pixel % 4) > 0) Pixel = (Pixel/4)*4;
  src = (BYTE *)(VidMem+2048L*LineNo);
  do{
    y[0] = (*src++ & 0xfe) | 1;
    uBild = *src & 0xc0;
    vBild = (*src++ & 0x30) << 2;
    y[1] = (*src++ & 0xfe) | 1;
    uBild |= (*src & 0xc0) >> 2;
    vBild |= (*src++ & 0x30);
    y[2] = (*src++ & 0xfe) | 1;
    uBild |= (*src & 0xc0) >> 4;
    vBild |= (*src++ &0x30) >> 2;
    y[3] = (*src++ &0xfe) | 1;
    uBild |= (*src & 0xc0) >> 6;
    vBild |= (*src++ & 0x30) >> 4;

    u = (uBild * 226) / 127;
    v = (vBild * 179) / 127;

    for (z=0; z<4; z++){
      if ((r=y[z]+v) < 0) r = 0;
      else
        if (r>255) r = 255;
      if ((b=y[z]+u) < 0) b = 0;
      else
        if (b>255) b = 255;
      g = ((y[z]*436L) >> 8) - ((r*130U) >> 8) - ((b*50U) >> 8);
      if (g<0) g = 0;
      else
        if (g>255) g = 255;
      *Buffer++ = (char)b;
      *Buffer++ = (char)g;
      *Buffer++ = (char)r;
    }
  } while ((count += 4) < Pixel);
  return count;
}

/***************************************************************************/

int WriteLineBGR(char *Buffer, int LineNo, int Pixel)
{
  BYTE *dest, *src=Buffer;
  INT  y[4], uMit, vMit;
  signed long r, g, b;
  signed char uBild, vBild;
  WORD len=1, z;

  if ((Pixel % 4) > 0) Pixel = (Pixel | 3)+1;
  dest = (BYTE *)(VidMem+2048L*LineNo);
  do{
    uMit = 0;
    vMit = 0;
    for (z=0; z<4; z++){
      if (len > Pixel){
        r = g = b = 0;
      }
      else{
        b = *src++;
        g = *src++;
        r = *src++;
      }
      y[z] = (g*256+r*130+b*50)/438;
      uMit += b-y[z];
      vMit += r-y[z];
      len++;
    }

    uBild = (uMit/4*127)/226;
    vBild = (vMit/4*127)/179;

    for (z=0; z<4; z++){
      *dest++ = y[z];
      *dest++ = (uBild & 0xc0) + ((vBild >> 2) & 0x30);
      uBild <<= 2;
      vBild <<= 2;
    }
  } while (len < Pixel);
  return 0;
}

/**************************************************************************/

void WriteFileTGA(char *Name, int XRes, int YRes){
FILE *f;
char *Buffer;
int  y;
char Header[18];

  if ((f = fopen(Name, "wb")) == NULL) return;

  V_FreezeVideo();
  Buffer = malloc(3*(XRes+3));

  memset(Header, 0, 18);
  Header[2] = 2;            /* uncompressed 24 bit */
  Header[12] = XRes & 0xff;
  Header[13] = XRes >> 8;
  Header[14] = YRes & 0xff;
  Header[15] = YRes >> 8;
  Header[16] = 24;          /* 24 bit/pixel */
  Header[17] = 0x20;        /* top-down, non interlaced */

  fwrite(Header, 18, 1, f);
  
  for (y=1; y<=YRes; y++){
    ReadLineBGR(Buffer, y, XRes);
    fwrite(Buffer, 3, XRes, f);
  }
  
  fclose(f);
  free(Buffer);
  V_UnfreezeVideo();
}

/**************************************************************************/

void ReadFileTGA(char *Name){
BYTE Header[18];
char *Buffer;
WORD XRes, YRes, y;
FILE *f;

  if ((f = fopen(Name, "rb")) == NULL) return;
  
  fread(Header, 1, 18, f);
  XRes = Header[12] + 256*(WORD)(Header[13]);
  YRes = Header[14] + 256*(WORD)(Header[15]);
  if ((Header[16] != 24) || (Header[2] != 2) ||
      (XRes > 1024) || (YRes > 512)){
    fclose(f);
    return;
  }
  Buffer = malloc(3*(XRes+3));
  
  V_FreezeVideo();
  if ((Header[17] & 0x20) == 0){ /* upside-down */
    for (y=YRes; y>=1; y--){
      fread(Buffer, 3, XRes, f);
      WriteLineBGR(Buffer, y, XRes);
    }
  }
  else{ /* top-down */
    for (y=1; y<=YRes; y++){
      fread(Buffer, 3, XRes, f);
      WriteLineBGR(Buffer, y, XRes);
    }
  }
  fclose(f);
  free(Buffer);
}  

/**************************************************************************/

int FindFreeNumber(){
/* find first unused number */
  FILE *f;
  char Name[20];
  char Num[5];
  int Number;
  
  Number = 0;
  do{
    itoa(Number, Num);
    strcpy(Name, "pic");
    strcat(Name, Num);
    strcat(Name, ".tga");
    if ((f = fopen(Name, "rb")) == NULL) break;
    fclose(f);
    Number++;
  } while (Number < 1000);
  return Number;
}

/**************************************************************************/

int FindUsedNumber(int Number){
/* find last used number */
  FILE *f;
  char Name[20];
  char Num[5];
  int Start;
  
  Start = Number;
  Number--;
  if (Number == -1) Number = 999;
  do{
    itoa(Number, Num);
    strcpy(Name, "pic");
    strcat(Name, Num);
    strcat(Name, ".tga");
    if ((f = fopen(Name, "rb")) != NULL){
      fclose(f);
      break;
    }
    if (--Number == -1) Number = 999;
  } while (Number != Start);
  if (Number == Start) return -1;
  else return Number;
}

/**************************************************************************/

int main(int argc, char *argv[])
{
  char ch;
  char Number[5];
  char Name[200];
  int Mute = 0;
  int Volume;
  int XRes, YRes;
  int PicNumber;
  int clear = 0;
  int HSync = 0;
  int VSync = 0;
#ifndef linux
  char *tmp;
#endif

#ifdef linux
  /* set the path where to find the file VIDEO.CFG */
  strcpy(Name, "./");
#else
  strcpy(Name, argv[0]);
  tmp = Name;
  while (*tmp != 0) tmp++;
  while ((*tmp != '/') && (tmp != Name)) tmp--;
  if (tmp != Name) tmp++;
  *tmp = 0;
#endif
  if (argc > 1){
    if (!strcmp(argv[1],"-d")) clear = 1;
    else{
      printf("unknown option\n");
      return -1;
    }
  }
  init(Name, clear);
  Volume = V_GetVolume();
  XRes = 368;
  YRes = 286;
  PicNumber = 0;

  do {
#ifdef linux
    ch = getchar();
#else
    ch = getkey();
#endif
    switch (ch){
    case '2': if (YRes <= 480) Set680x512();
              V_CreateWindow(-4,0,672,508);
              XRes = 672;
              YRes = 508;
              break;
    case '3': if (YRes > 480) Set640x480();
              V_CreateWindow(0,0,640,480);
              XRes = 640;
              YRes = 480;
              break;
    case '4': if (YRes > 480) Set640x480();
              V_CreateWindow(136,97,367,286);
              XRes = 368;
              YRes = 286;
              break;
    case '5': if (YRes > 480) Set640x480();
              V_CreateWindow(160,120,320,240);
              XRes = 320;
              YRes = 240;
              break;
    case 'l': Volume += 8;
              if (Volume > 255) Volume = 255;
              V_SetVolume(Volume);
              break;
    case 'q': Volume -= 8;
              if (Volume < 0) Volume = 0;
              V_SetVolume(Volume);
              break;
    case '+': Volume += 2;
              if (Volume > 255) Volume = 255;
              V_SetVolume(Volume);
              break;
    case '-': Volume -= 2;
              if (Volume < 0) Volume = 0;
              V_SetVolume(Volume);
              break;
    case 'm': if (Mute == 0){
                Mute = Volume;
                V_SetVolume(0);
              }
              else {
                V_SetVolume(Mute);
                Mute = 0;
              }
              break;
    case 's': if (V_Freeze()) V_UnfreezeVideo();
              else          V_FreezeVideo();
              break;
    case 'w': itoa(PicNumber = FindFreeNumber(), Number);
              PicNumber++;
              strcpy(Name, "pic");
              strcat(Name,Number);
              strcat(Name,".tga");
              WriteFileTGA(Name, XRes, YRes);
              break;
    case 'g': PicNumber = FindUsedNumber(PicNumber);
              if (PicNumber != -1){
                itoa(PicNumber, Number);
                strcpy(Name, "pic");
                strcat(Name,Number);
                strcat(Name,".tga");
                ReadFileTGA(Name);
              }
              break;
    case 'n': V_wr_i2c(0x8a, 6, 0x32);
              break;
    case 'b': V_wr_i2c(0x8a, 6, 0x1c);
              break;
    case 'h': V_SetSkewFactor(7, HSync ^= 1);
              break;
    case 'v': V_SetSkewFactor(8, VSync ^= 1);
              break;
    }
  } while (ch != 27);
  done();
  return 0;
}
