/*****************************************************************************
*	The Graphics Engine v1.01					     *
*									     *
*	All program code and documentation associated with The Graphics	     *
*	Engine is Copyright (c) 1993 by Matthew Hildebrand.		     *
*									     *
*	Unauthorised usage or modification of any or all of The Graphics     *
*	Engine is strictly prohibited.	     				     *
*****************************************************************************/

/* This notice may NOT be removed and MUST remain intact */
static char copyright[] = "The Graphics Engine -- Copyright (c) 1993 by Matthew Hildebrand";


/* Guess what... this file must be compiled with the large or huge model. */

#ifdef __TINY__
#error This module MUST be compiled with the large or huge model.
#endif
#ifdef __SMALL__
#error This module MUST be compiled with the large or huge model.
#endif
#ifdef M_I86SM
#error This module MUST be compiled with the large or huge model.
#endif
#ifdef __MEDIUM__
#error This module MUST be compiled with the large or huge model.
#endif
#ifdef M_I86MM
#error This module MUST be compiled with the large or huge model.
#endif
#ifdef __COMPACT__
#error This module MUST be compiled with the large or huge model.
#endif
#ifdef M_I86CM
#error This module MUST be compiled with the large or huge model.
#endif


#include <alloc.h>
#include <dos.h>
#include <math.h>
#include <mem.h>
#include <stdio.h>
#include "tge.h"

#define MAXX	(_grSystemDrv->maxx)
#define MAXY	(_grSystemDrv->maxy)

static void *forceOffset(void far *p);
static void setupInfo(struct GraphDrv far *grDrv);
static void huge GE_deInitGraphics(void);
static void huge GE_putImage(int x, int y, void far *image);
static void huge GE_putImageInv(int x, int y, void far *image);
static void huge GE_getImage(int ulx, int uly, int lrx, int lry, void far
	*image);
static void huge GE_putLine(int lineNum, int xOff, int lineLen, void far *buf);
static void huge GE_getLine(int lineNum, int xOff, int lineLen, void far *buf);
static unsigned long huge GE_imageSize(int ulx, int uly, int lrx, int lry);
static void huge GE_putPixel(int x, int y, unsigned colour);
static unsigned huge GE_getPixel(int x, int y);
static void huge GE_line(int x1, int y1, int x2, int y2, unsigned colour);
static void huge GE_horizLine(int y, int x1, int x2, unsigned colour);
static void huge GE_drawRect(int ulx, int uly, int lrx, int lry, unsigned
	colour);
static void huge GE_filledRect(int ulx, int uly, int lrx, int lry, unsigned
	colour);
static void huge GE_setPaletteReg(unsigned palReg, unsigned char red,
	unsigned char blue, unsigned char green);
static void huge GE_getPaletteReg(unsigned palReg, unsigned char *red,
	unsigned char *blue, unsigned char *green);
static void huge GE_setBlockPalette(unsigned firstReg, unsigned numRegs,
	void far *data);
static void huge GE_getBlockPalette(unsigned firstReg, unsigned numRegs,
	void far *data);
static void huge GE_clearGraphics(unsigned colour);

static struct GraphDrv far *drvAddr = NULL;
struct GraphDrv far *_grSystemDrv = NULL;


/******
******* Routines to load a graphics driver from disk.
******/

/* Call this function to load a driver */
int loadGraphDriver(char *filename)
{
  struct GraphDrv far *grDrv = NULL;
  FILE *fp;
  long l;
  char signature[4];

  if ((fp=fopen(filename,"rb")) != NULL) {
    fseek(fp, 0L, SEEK_END);
    l = ftell(fp);
    rewind(fp);

    fread(signature, 1, 4, fp);
    if (!memcmp(signature, "GRAP", 4))
    {
      l -= 4;
      if (l < 0xFFFFl && (grDrv=(struct GraphDrv far *)farmalloc(l+15)) != NULL)
      {
        drvAddr = grDrv;
	(void *)grDrv = forceOffset(drvAddr);	/* force to XXXX:0004 */
        _grSystemDrv = grDrv;

	/* initialize the function pointers */
	if (fread(grDrv, 1, (unsigned)l, fp) == (unsigned)l)
        {
          setupInfo(grDrv);
        }
        else
        {
          farfree(grDrv);
          drvAddr = _grSystemDrv = grDrv = NULL;
        }
      }
    }
    fclose(fp);
  }
  return (grDrv ? 1 : 0);
}

/* Force a pointer to the form XXXX:0004 */
static void *forceOffset(void far *p)
{
  void huge *temp = (void huge *)p;

  if (FP_OFF(temp) == 4)
    return ((void far *)temp);
  else if (FP_OFF(temp) > 4)
    return (MK_FP(FP_SEG(temp)+1, 4));
  else
    return (MK_FP(FP_SEG(temp), 4));
}

/* Set up function pointers once a driver has been loaded */
static void setupInfo(struct GraphDrv far *grDrv)
{
  register unsigned seg = FP_SEG(grDrv);

  /* initGraphics must always be present */
  (void *)grDrv->_initGraphics = MK_FP(seg, FP_OFF(grDrv->_initGraphics));

  /* These functions are optional; fill in the default ones if the
     driver doesn't contain them.  It's tedious, but somebody has
     to do it... */
  if (FP_OFF(grDrv->_deInitGraphics))	/* deInitGraphics */
    (void *)grDrv->_deInitGraphics = MK_FP(seg, FP_OFF(grDrv->_deInitGraphics));
  else
    grDrv->_deInitGraphics = GE_deInitGraphics;

  if (FP_OFF(grDrv->_putImage))		/* putImage */
    (void *)grDrv->_putImage = MK_FP(seg, FP_OFF(grDrv->_putImage));
  else
    grDrv->_putImage = GE_putImage;

  if (FP_OFF(grDrv->_putImageInv))	/* putImageInv */
    (void *)grDrv->_putImageInv = MK_FP(seg, FP_OFF(grDrv->_putImageInv));
  else
    grDrv->_putImageInv = GE_putImageInv;

  if (FP_OFF(grDrv->_getImage))		/* getImage */
    (void *)grDrv->_getImage = MK_FP(seg, FP_OFF(grDrv->_getImage));
  else
    grDrv->_getImage = GE_getImage;

  if (FP_OFF(grDrv->_putLine))		/* putLine */
    (void *)grDrv->_putLine = MK_FP(seg, FP_OFF(grDrv->_putLine));
  else
    grDrv->_putLine = GE_putLine;

  if (FP_OFF(grDrv->_getLine))		/* getLine */
    (void *)grDrv->_getLine = MK_FP(seg, FP_OFF(grDrv->_getLine));
  else
    grDrv->_getLine = GE_getLine;

  if (FP_OFF(grDrv->_imageSize))	/* imageSize */
    (void *)grDrv->_imageSize = MK_FP(seg, FP_OFF(grDrv->_imageSize));
  else
    grDrv->_imageSize = GE_imageSize;

  if (FP_OFF(grDrv->_putPixel))		/* putPixel */
    (void *)grDrv->_putPixel = MK_FP(seg, FP_OFF(grDrv->_putPixel));
  else
    grDrv->_putPixel = GE_putPixel;

  if (FP_OFF(grDrv->_getPixel))		/* getPixel */
    (void *)grDrv->_getPixel = MK_FP(seg, FP_OFF(grDrv->_getPixel));
  else
    grDrv->_getPixel = GE_getPixel;

  if (FP_OFF(grDrv->_line))		/* line */
    (void *)grDrv->_line = MK_FP(seg, FP_OFF(grDrv->_line));
  else
    grDrv->_line = GE_line;

  if (FP_OFF(grDrv->_horizLine))	/* horizLine */
    (void *)grDrv->_horizLine = MK_FP(seg, FP_OFF(grDrv->_horizLine));
  else
    grDrv->_horizLine = GE_horizLine;

  if (FP_OFF(grDrv->_drawRect))		/* drawRect */
    (void *)grDrv->_drawRect = MK_FP(seg, FP_OFF(grDrv->_drawRect));
  else
    grDrv->_drawRect = GE_drawRect;

  if (FP_OFF(grDrv->_filledRect))	/* filledRect */
    (void *)grDrv->_filledRect = MK_FP(seg, FP_OFF(grDrv->_filledRect));
  else
    grDrv->_filledRect = GE_filledRect;

  if (FP_OFF(grDrv->_setPaletteReg))	/* setPaletteReg */
    (void *)grDrv->_setPaletteReg = MK_FP(seg, FP_OFF(grDrv->_setPaletteReg));
  else
    grDrv->_setPaletteReg = GE_setPaletteReg;

  if (FP_OFF(grDrv->_getPaletteReg))	/* getPaletteReg */
    (void *)grDrv->_getPaletteReg = MK_FP(seg, FP_OFF(grDrv->_getPaletteReg));
  else
    grDrv->_getPaletteReg = GE_getPaletteReg;

  if (FP_OFF(grDrv->_setBlockPalette))	/* setBlockPalette */
    (void *)grDrv->_setBlockPalette = MK_FP(seg, FP_OFF(grDrv->_setBlockPalette));
  else
    grDrv->_setBlockPalette = GE_setBlockPalette;

  if (FP_OFF(grDrv->_getBlockPalette))	/* getBlockPalette */
    (void *)grDrv->_getBlockPalette = MK_FP(seg, FP_OFF(grDrv->_getBlockPalette));
  else
    grDrv->_getBlockPalette = GE_getBlockPalette;

  if (FP_OFF(grDrv->_clearGraphics))	/* clearGraphics */
    (void *)grDrv->_clearGraphics = MK_FP(seg, FP_OFF(grDrv->_clearGraphics));
  else
    grDrv->_clearGraphics = GE_clearGraphics;
}

/******
******* Free graphics driver memory
******/

void unloadGraphDriver(void)
{
  if (drvAddr)
    farfree(drvAddr);
}

/******
******* High-level driver routines to be used if they aren't in a driver
******/

static void huge GE_deInitGraphics(void)
{
  _AX = 0x0003;
  geninterrupt(0x10);
}

static void huge GE_putImage(int x, int y, void far *image)
{
  register unsigned ycount;
  unsigned wide, deep;
  register unsigned sadd = 0;
  unsigned char huge *p = (unsigned char *)image;

  wide = *(unsigned *)image;
  deep = *((unsigned *)image+1);
  p += 4;

  if (y < 0)				/* clip y coordinate */
  {
    if (y+deep)
    {
      p += wide * abs(y);
      deep -= abs(y);
      y = 0;
    }
    else
      return;
  }
  else if (y > MAXY)
    return;
  else if (y+deep > MAXY)
    deep = MAXY-y+1;

  if (x < 0)				/* clip x coordinate */
  {
    if (x+wide)
    {
      sadd += abs(x);
      p += abs(x);
      wide -= abs(x);
      x = 0;
    }
    else
      return;
  }
  else if (x > MAXX)
    return;
  else if (x+wide > MAXX)
  {
    sadd += wide-1 - (MAXX-x);
    wide = MAXX-x+1;
  }

  sadd += wide;
  for (ycount=0; ycount<deep; ycount++)
  {
    putLine(y+ycount, x, wide, (void *)p);
    p += sadd;
  }
}

static void huge GE_putImageInv(int x, int y, void far *image)
{
  register unsigned xcount, ycount;
  unsigned wide, deep;
  register unsigned sadd = 0;
  unsigned char huge *p = (unsigned char *)image;

  wide = *(unsigned *)image;
  deep = *((unsigned *)image+1);
  p += 4;

  if (y < 0)				/* clip y coordinate */
  {
    if (y+deep)
    {
      p += wide * abs(y);
      deep -= abs(y);
      y = 0;
    }
    else
      return;
  }
  else if (y > MAXY)
    return;
  else if (y+deep > MAXY)
    deep = MAXY-y+1;

  if (x < 0)				/* clip x coordinate */
  {
    if (x+wide)
    {
      sadd += abs(x);
      p += abs(x);
      wide -= abs(x);
      x = 0;
    }
    else
      return;
  }
  else if (x > MAXX)
    return;
  else if (x+wide > MAXX)
  {
    sadd += wide-1 - (MAXX-x);
    wide = MAXX-x+1;
  }

  for (ycount=0; ycount<deep; ycount++)
  {
    for (xcount=0; xcount<wide; xcount++)
    {
      if (*p)
        putPixel(x+xcount, y+ycount, *p);
      p++;
    }
    p += sadd;
  }
}

static void huge GE_getImage(int ulx, int uly, int lrx, int lry, void far
	*image)
{
  register unsigned ycount, wide;
  int temp;
  unsigned char huge *p = (unsigned char *)image;

  if (lrx < ulx)			/* swap coordinates if necessary */
  {
    temp = ulx;
    lrx = ulx;
    lrx = temp;
  }
  if (lry < uly)
  {
    temp = uly;
    lry = uly;
    lry = temp;
  }

  if (ulx < 0)				/* ensure coords are in bounds */
    ulx = 0;
  else if (ulx > MAXX)
    ulx = MAXX;

  if (uly < 0)
    uly = 0;
  else if (uly > MAXY)
    uly = MAXY;

  if (lrx < 0)
    lrx = 0;
  else if (lrx > MAXX)
    lrx = MAXX;

  if (lry < 0)
    lry = 0;
  else if (lry > MAXY)
    lry = MAXY;

  *(unsigned *)image = lrx-ulx+1;
  *((unsigned *)image+1) = lry-uly+1;
  p += 4;

  wide = lrx-ulx+1;
  for (ycount=uly; ycount<=lry; ycount++)
  {
    getLine(ycount, ulx, wide, (void *)p);
    p += wide;
  }
}

static void huge GE_putLine(int lineNum, int xOff, int lineLen, void far *buf)
{
  register unsigned count, y;

  for (count=0,y=lineNum; count<lineLen; count++)
    putPixel(xOff+count, y, *((unsigned char huge *)buf+count));
}

static void huge GE_getLine(int lineNum, int xOff, int lineLen, void far *buf)
{
  register unsigned count, y;

  for (count=0,y=lineNum; count<lineLen; count++)
    *((unsigned char huge *)buf+count) = getPixel(xOff+count, y);
}

static unsigned long huge GE_imageSize(int ulx, int uly, int lrx, int lry)
{
  unsigned long size;
  int temp;

  if (lrx < ulx)			/* swap coordinates if necessary */
  {
    temp = ulx;
    lrx = ulx;
    lrx = temp;
  }
  if (lry < uly)
  {
    temp = uly;
    lry = uly;
    lry = temp;
  }

  if (ulx < 0)				/* ensure coords are in bounds */
    ulx = 0;
  else if (ulx > MAXX)
    ulx = MAXX;

  if (uly < 0)
    uly = 0;
  else if (uly > MAXY)
    uly = MAXY;

  if (lrx < 0)
    lrx = 0;
  else if (lrx > MAXX)
    lrx = MAXX;

  if (lry < 0)
    lry = 0;
  else if (lry > MAXY)
    lry = MAXY;

  size = (unsigned long)(lrx-ulx+1);
  size *= (unsigned long)(lry-uly+1);

  return (size+4);			/* +4 for dimension information */
}

static void huge GE_putPixel(int x, int y, unsigned colour)
{
  _AH = 0x0C;
  _AL = (unsigned char)colour;
  _BH = 0;
  _CX = x;
  _DX = y;
  geninterrupt(0x10);
}

static unsigned huge GE_getPixel(int x, int y)
{
  _AH = 0x0D;
  _BH = 0;
  _CX = x;
  _DX = y;
  geninterrupt(0x10);

  _AH = 0;
  return (_AX);
}

static void huge GE_line(int x1, int y1, int x2, int y2, unsigned colour)
{
  register int t, distance;
  int xerr=0, yerr=0, deltax, deltay;
  int incx, incy;

  deltax = x2 - x1;			/* compute both distances */
  deltay = y2 - y1;

  if (deltax > 0)			/* compute increments */
    incx = 1;
  else if (deltax == 0)
    incx = 0;
  else
    incx = -1;

  if (deltay > 0)
    incy = 1;
  else if (deltay == 0)
    incy = 0;
  else
    incy = -1;

  deltax = abs(deltax);			/* determine greater distance */
  deltay = abs(deltay);
  if (deltax > deltay)
    distance = deltax;
  else
    distance = deltay;

  for (t=0; t<=distance+1; t++)		/* draw the line */
  {
    putPixel(x1, y1, colour);
    xerr += deltax;
    yerr += deltay;
    if (xerr > distance)
    {
      xerr -= distance;
      x1 += incx;
    }
    if (yerr > distance)
    {
      yerr -= distance;
      y1 += incy;
    }
  }
}

static void huge GE_horizLine(int y, int x1, int x2, unsigned colour)
{
  register unsigned count, max;

  max = x2;
  for (count=x1; count<=max; count++)
    putPixel(count, y, colour);
}

static void huge GE_drawRect(int ulx, int uly, int lrx, int lry, unsigned
	colour)
{
  register int t;

  if (ulx > lrx)			/* swap coords if necessary */
  {
    t = ulx;
    ulx = lrx;
    lrx = t;
  }
  if (uly > lry)
  {
    t = uly;
    uly = lry;
    lry = t;
  }

  horizLine(uly, ulx, lrx, colour);	/* top */
  horizLine(lry, ulx, lrx, colour);	/* bottom */
  line(ulx, uly, ulx, lry, colour);	/* left */
  line(lrx, uly, lrx, lry, colour);	/* bottom */
}

static void huge GE_filledRect(int ulx, int uly, int lrx, int lry, unsigned
	colour)
{
  register unsigned count, max;
  int t;

  if (ulx > lrx)			/* swap coords if necessary */
  {
    t = ulx;
    ulx = lrx;
    lrx = t;
  }
  if (uly > lry)
  {
    t = uly;
    uly = lry;
    lry = t;
  }

  max = lry;
  for (count=uly; count<=max; count++)
    horizLine(count, ulx, lrx, colour);
}

static void huge GE_setPaletteReg(unsigned palReg, unsigned char red, unsigned
	char blue, unsigned char green)
{
  _AX = 0x1010;
  _BX = palReg;
  _DH = red;
  _CH = green;
  _CL = blue;
  geninterrupt(0x10);
}

static void huge GE_getPaletteReg(unsigned palReg, unsigned char *red, unsigned
	char *blue, unsigned char *green)
{
  _AX = 0x1015;
  _BX = palReg;
  geninterrupt(0x10);
  *red = _DH;
  *green = _CH;
  *blue = _CL;
}

static void huge GE_setBlockPalette(unsigned firstReg, unsigned numRegs, void
	far *data)
{
  _ES = FP_SEG(data);
  _DX = FP_OFF(data);
  _AX = 0x1012;
  _BX = firstReg;
  _CX = numRegs;
  geninterrupt(0x10);
}

static void huge GE_getBlockPalette(unsigned firstReg, unsigned numRegs, void
	far *data)
{
  _ES = FP_SEG(data);
  _DX = FP_OFF(data);
  _AX = 0x1017;
  _BX = firstReg;
  _CX = numRegs;
  geninterrupt(0x10);
}

static void huge GE_clearGraphics(unsigned colour)
{
  register unsigned count;

  for (count=0; count<=MAXY; count++)
    horizLine(count, 0, MAXX, colour);
}