/****************************************************************************\
*                                                                            *
*  ENGINE1.CPP - Source Code Module                                          *
*                                                                            *
*  (c)Copyright 2001 Indotek, LLC. All Rights Reserved.                      *
*                                                                            *
\****************************************************************************/

#define GLOBALS_DEFINED
#include "internal.h"

D3DMATRIX R3D_STDCALL
_r3d_IdentityMatrix(void)
{
   return D3DMATRIX(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

D3DMATRIX R3D_STDCALL
_r3d_ZeroMatrix(void)
{
   D3DMATRIX ret;
   for (int i = 0;i < 4;i++)
      for (int j = 0;j < 4;j++)
         ret(i, j) = 0.0f;
   return ret;
}

// near_plane, // distance to near clipping plane
// far_plane, // distance to far clipping plane
// fov_horiz, // horizontal field of view angle, in radians
// float fov_vert, // vertical field of view angle, in radians
D3DMATRIX R3D_STDCALL
_r3d_ProjectionMatrix(double near_plane, double far_plane, double fov)
{
   double fov_horiz, aspect_ratio, x, z;
   float h, w, Q;

#define cot(a) (1.0/tan((a)))

   aspect_ratio = (double)_r3d_dwRenderHeight / (double)_r3d_dwRenderWidth;

   if (_r3d_gbWindowed == TRUE)
   {
      // Constrain to Max Vertical and Horizontal FOV
      if (aspect_ratio <= 0.75)
      {
         fov_horiz = fov * 0.5F;
      }
      else
      {
         // Height is greater than width for a 0.75 aspect ratio.
         // We will base fov_horiz on aspect ratio
         fov_horiz = ((fov * 0.75F) / aspect_ratio) * 0.5F;
      }
   }
   else
      fov_horiz = fov * 0.5F;
   x = sin(fov_horiz);
   z = cos(fov_horiz);
   if (_r3d_gbWindowed == TRUE)
      x *= aspect_ratio;
   else
      x *= 0.75;
   w = (float)cot(fov_horiz);
   h = (float)(z / x);
   Q = (float)(far_plane / (far_plane - near_plane));

   D3DMATRIX ret = _r3d_ZeroMatrix();
   ret(0, 0) = w;
   ret(1, 1) = h;
   ret(2, 2) = Q;
   ret(3, 2) = (float)(-Q * near_plane);
   ret(2, 3) = 1;
   return ret;
}

int R3D_STDCALL
r3d_MessageBox(LPCTSTR lpMessage, LPCTSTR lpTitle, UINT uType)
{
   int returnval;

   // Need to end a potential 3D render state
   if (_r3d_bInsideRenderBeginEnd == TRUE)
      _r3d_Device->EndScene();

   // Neccessary even on 3DFX
   if (_r3d_DirectDraw4 != NULL)
      _r3d_DirectDraw4->FlipToGDISurface();

   //return (MessageBox(_r3d_ghWnd, lpMessage, lpTitle, uType|MB_DEFAULT_DESKTOP_ONLY));
   returnval = MessageBox(_r3d_ghWnd, lpMessage, lpTitle, uType | MB_SYSTEMMODAL);

   if (_r3d_bInsideRenderBeginEnd == TRUE)
      _r3d_Device->BeginScene();

   return returnval;
}

BOOL R3D_STDCALL
r3d_SurfaceDestroyAll(void)
{
   int LoopVar;

   // We loop through all allocated surfaces
   // and delete all.
   for (LoopVar = 0;LoopVar < _R3D_MAX_SURFACES;LoopVar++)
   {
      r3d_SurfaceDestroy(LoopVar); // Ignore errors!
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SurfaceDestroy(int handle)
{
   if (_r3d_bSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceDestroy", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   _R3D_DELETE(_r3d_Surface[handle]->RGBA);
   _R3D_DELETE(_r3d_Surface[handle]);

   _r3d_bSurfaceDoesNotExist[handle] = TRUE;

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SurfaceCreate(int *handle, int Width, int Height)
{
   int LoopVar;

   for (LoopVar = 0;LoopVar < _R3D_MAX_SURFACES;LoopVar++)
   {
      if (_r3d_bSurfaceDoesNotExist[LoopVar] == TRUE)
      {
         // Allocate Surface Structure Memory
         if (NULL == (_r3d_Surface[LoopVar] = new _R3D_SURFACE))
         {
            _r3d_FormulateErrorString("r3d_SurfaceCreate", "Can not allocate memory", DD_OK);
            return FALSE;
         }

         // Allocate Surface Memory
         if (NULL == (_r3d_Surface[LoopVar]->RGBA = new unsigned char[Width * Height*4]))
         {
            //_R3D_DELETE(_r3d_Surface[LoopVar]);
            _r3d_FormulateErrorString("r3d_SurfaceCreate", "Can not allocate memory", DD_OK);
            return FALSE;
         }

         _r3d_bSurfaceDoesNotExist[LoopVar] = FALSE;
         _r3d_Surface[LoopVar]->dwWidth = (DWORD)Width;
         _r3d_Surface[LoopVar]->dwHeight = (DWORD)Height;
         *handle = LoopVar;
         break;
      }
   }

   // Let's initialize the alpha channel to 255 and the
   // set the background color to black.
   unsigned long *SurfaceRGBA, dwColor;
   SurfaceRGBA = (unsigned long *)r3d_SurfaceGet(*handle);
   dwColor = r3d_SurfaceColorMakeRGBA(0, 0, 0, 255);
   //memset(SurfaceRGBA,dwColor,(Width*Height));
   for (LoopVar = 0;LoopVar < (Width * Height);LoopVar++)
      SurfaceRGBA[LoopVar] = dwColor;

   return TRUE;
}

BOOL R3D_STDCALL
_r3d_BMPGetInfo(char *pFilename, char *FunctionNameString, DWORD *BMPWidth, DWORD *BMPHeight, BOOL *bPalette256)
{
   HANDLE hTemp;
   char buffer[512];
   BITMAPFILEHEADER bFileHead;
   BITMAPINFOHEADER bFileInfo;

   if ((hTemp = r3d_FileOpen(pFilename, R3D_READ)) == INVALID_HANDLE_VALUE)
   {
      sprintf(buffer, "Can not load %s", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileRead(hTemp, &bFileHead, sizeof(bFileHead)) == FALSE)
   {
      sprintf(buffer, "%s: Error reading file", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileRead(hTemp, &bFileInfo, sizeof(bFileInfo)) == FALSE)
   {
      sprintf(buffer, "%s: Error reading file", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      return FALSE;
   }
   if (bFileInfo.biBitCount != 8 && bFileInfo.biBitCount != 24) // Stored as B,G, and R, respectively
   {
      // Not 8 or 24 bit
      sprintf(buffer, "%s : Not a 256 color or 24-bit BMP", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      r3d_FileClose(hTemp);
      return FALSE;
   }
   if (bFileInfo.biCompression != BI_RGB)
   {
      // Not an uncompressed bitmap
      sprintf(buffer, "%s : Not an uncompressed BMP", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      r3d_FileClose(hTemp);
      return FALSE;
   }

   r3d_FileClose(hTemp); //hmmmm... why is this not working....

   if (bFileInfo.biBitCount == 8)
      *bPalette256 = TRUE;
   else //if (bFileInfo.biBitCount==24)
      *bPalette256 = FALSE;
   *BMPWidth = (DWORD)bFileInfo.biWidth;
   *BMPHeight = (DWORD)bFileInfo.biHeight;

   return TRUE;
}

BOOL R3D_STDCALL
_r3d_BMPLoad(char *pFilename, char *FunctionNameString, unsigned char *pBGRImage, unsigned char *p256Image)
{
   HANDLE hTemp;
   char buffer[512];
   BITMAPFILEHEADER bFileHead;
   BITMAPINFOHEADER bFileInfo;
   RGBQUAD Palette[256];
   int counter, LoopVar;
   unsigned long dwPaddedBytes;

   // A DIB consists of two parts: a BITMAPCOREINFO structure describing
   // the dimensions and colors of the bitmap, and an array of bytes
   // defining the pixels of the bitmap. The bits in the array are packed
   // together, but each scan line must be padded with zeroes to end on a
   // LONG boundary. The origin of the bitmap is the lower left corner.

   // First, let's load the bitmap to our temporary buffer
   if ((hTemp = r3d_FileOpen(pFilename, R3D_READ)) == INVALID_HANDLE_VALUE)
   {
      sprintf(buffer, "Can not load %s", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileRead(hTemp, &bFileHead, sizeof(bFileHead)) == FALSE)
   {
      sprintf(buffer, "%s: Error reading file", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileRead(hTemp, &bFileInfo, sizeof(bFileInfo)) == FALSE)
   {
      sprintf(buffer, "%s: Error reading file", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      return FALSE;
   }
   if (bFileInfo.biBitCount != 8 && bFileInfo.biBitCount != 24) // Stored as B,G, and R, respectively
   {
      // Not 8 or 24 bit
      sprintf(buffer, "%s : Not a 256 color or 24-bit BMP", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      r3d_FileClose(hTemp);
      return FALSE;
   }
   if (bFileInfo.biCompression != BI_RGB)
   {
      // Not an uncompressed bitmap
      sprintf(buffer, "%s : Not an uncompressed BMP", pFilename);
      _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
      r3d_FileClose(hTemp);
      return FALSE;
   }

   if (bFileInfo.biBitCount == 8)
   {
      // We need to read the palette
      if (r3d_FileRead(hTemp, Palette, (DWORD)(256 * 4)) == FALSE)
      {
         sprintf(buffer, "%s: Error reading file", pFilename);
         _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
         return FALSE;
      }

      // Actual B,G,R Bitmap data may start somewhere other
      // than you think and is specified in bFileHead.bfOffBits
      if (r3d_FileSeek(hTemp, bFileHead.bfOffBits, R3D_BEGINNING) == FALSE)
      {
         sprintf(buffer, "%s: Error seeking RGB data in file", pFilename);
         _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
         return FALSE;
      }

      dwPaddedBytes = ((bFileInfo.biWidth * 1) % 4);
      if (dwPaddedBytes)
         dwPaddedBytes = 4 - dwPaddedBytes;
      for (LoopVar = 0;LoopVar < bFileInfo.biHeight;LoopVar++)
      {
         if (r3d_FileRead(hTemp, &p256Image[LoopVar * bFileInfo.biWidth], (DWORD)(bFileInfo.biWidth)) == FALSE)
         {
            sprintf(buffer, "%s: Error reading file", pFilename);
            _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
            return FALSE;
         }
         if (dwPaddedBytes && LoopVar < bFileInfo.biHeight - 1)
         {
            if (r3d_FileSeek(hTemp, dwPaddedBytes, R3D_CURRENT) == FALSE)
            {
               sprintf(buffer, "%s: Error seeking in file", pFilename);
               _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
               return FALSE;
            }
         }
      }

      // Now convert to BGR and store in pBGRImage
      counter = 0;
      for (LoopVar = 0;LoopVar < bFileInfo.biWidth * bFileInfo.biHeight;LoopVar++)
      {
         // Blue
         pBGRImage[counter] = Palette[p256Image[LoopVar]].rgbBlue;
         counter++;
         // Green
         pBGRImage[counter] = Palette[p256Image[LoopVar]].rgbGreen;
         counter++;
         // Red
         pBGRImage[counter] = Palette[p256Image[LoopVar]].rgbRed;
         counter++;
      }
   }
   else if (bFileInfo.biBitCount == 24)
   {
      // Actual B,G,R Bitmap data may start somewhere other
      // than you think and is specified in bFileHead.bfOffBits
      if (r3d_FileSeek(hTemp, bFileHead.bfOffBits, R3D_BEGINNING) == FALSE)
      {
         sprintf(buffer, "%s: Error seeking RGB data in file", pFilename);
         _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
         return FALSE;
      }

      // Safe to read now
      dwPaddedBytes = ((bFileInfo.biWidth * 3) % 4);
      if (dwPaddedBytes)
         dwPaddedBytes = 4 - dwPaddedBytes;
      for (LoopVar = 0;LoopVar < bFileInfo.biHeight;LoopVar++)
      {
         if (r3d_FileRead(hTemp, &pBGRImage[LoopVar * bFileInfo.biWidth*3], (DWORD)(bFileInfo.biWidth * 3)) == FALSE)
         {
            sprintf(buffer, "%s: Error reading file", pFilename);
            _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
            return FALSE;
         }
         if (dwPaddedBytes && LoopVar < bFileInfo.biHeight - 1)
         {
            if (r3d_FileSeek(hTemp, dwPaddedBytes, R3D_CURRENT) == FALSE)
            {
               sprintf(buffer, "%s: Error seeking in file", pFilename);
               _r3d_FormulateErrorString(FunctionNameString, buffer, DD_OK);
               return FALSE;
            }
         }
      }
   }
   r3d_FileClose(hTemp); //hmmmm... why is this not working....

#ifdef EVALUATIONVERSION
   int x, y, x01, y01, x02, y02;
   // Horizontal lines
   y01 = bFileInfo.biHeight / 3;
   y02 = y01 * 2;
   for (x = 0;x < bFileInfo.biWidth;x++)
   {
      *(pBGRImage + (y01 * bFileInfo.biWidth * 3) + (x * 3) + 0) = 0;
      *(pBGRImage + (y01 * bFileInfo.biWidth * 3) + (x * 3) + 1) = 0;
      *(pBGRImage + (y01 * bFileInfo.biWidth * 3) + (x * 3) + 2) = 0;
      *(pBGRImage + (y02 * bFileInfo.biWidth * 3) + (x * 3) + 0) = 0;
      *(pBGRImage + (y02 * bFileInfo.biWidth * 3) + (x * 3) + 1) = 0;
      *(pBGRImage + (y02 * bFileInfo.biWidth * 3) + (x * 3) + 2) = 0;
   }
   // Vertical lines
   x01 = bFileInfo.biWidth / 3;
   x02 = x01 * 2;
   for (y = 0;y < bFileInfo.biHeight;y++)
   {
      *(pBGRImage + (y * bFileInfo.biWidth * 3) + (x01 * 3) + 0) = 0;
      *(pBGRImage + (y * bFileInfo.biWidth * 3) + (x01 * 3) + 1) = 0;
      *(pBGRImage + (y * bFileInfo.biWidth * 3) + (x01 * 3) + 2) = 0;
      *(pBGRImage + (y * bFileInfo.biWidth * 3) + (x02 * 3) + 0) = 0;
      *(pBGRImage + (y * bFileInfo.biWidth * 3) + (x02 * 3) + 1) = 0;
      *(pBGRImage + (y * bFileInfo.biWidth * 3) + (x02 * 3) + 2) = 0;
   }
#endif

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SurfaceSetColorKey(int handle, unsigned char Red, unsigned char Green, unsigned char Blue)
{
   int LoopVarX, LoopVarY;
   unsigned char *UCptr;
   unsigned char r, g, b, a;

   if (_r3d_bSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceSetColorKey", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   // We loop through the size of the virtual surface since
   // we can avoid pointer underflow and overflow this way.
   UCptr = (unsigned char *)r3d_SurfaceGet(handle);
   //UCptr+=3; // Alpha channel for RGBA
   for (LoopVarY = 0;LoopVarY < (int)_r3d_Surface[handle]->dwHeight;LoopVarY++)
   {
      for (LoopVarX = 0;LoopVarX < (int)_r3d_Surface[handle]->dwWidth;LoopVarX++)
      {
         r = *(UCptr);
         g = *(UCptr + 1);
         b = *(UCptr + 2);
         a = *(UCptr + 3);
         if (r == Red && g == Green && b == Blue)
            *(UCptr + 3) = 0;
         else
            *(UCptr + 3) = 255;
         UCptr += 4;
      }
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SurfaceSetAlphaFromBMP(int handle, char *BitmapFilename)
{
   int LoopVarX, LoopVarY;
   unsigned char *UCptr;
   DWORD dwWidth, dwHeight;
   char buffer[256];
   BOOL bPalette256;

   if (_r3d_bSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceSetAlphaFromBMP", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   // First, we get the BMP width and height
   if (_r3d_BMPGetInfo(BitmapFilename, "r3d_SurfaceSetAlphaFromBMP", &dwWidth, &dwHeight, &bPalette256) == FALSE)
   {
      // Error string already defined
      return FALSE;
   }

   // Lets return with error if sizes are not equal
   if (dwWidth != _r3d_Surface[handle]->dwWidth && dwHeight != _r3d_Surface[handle]->dwHeight)
   {
      sprintf(buffer, "%s : File is not the same size as surface at handle # %d", BitmapFilename, handle);
      _r3d_FormulateErrorString("r3d_SurfaceSetAlphaFromBMP", buffer, DD_OK);
      return FALSE;
   }

   // Now, allocate memory
   if (NULL == (_r3d_A = new unsigned char[dwWidth * dwHeight*3]))
   {
      _r3d_FormulateErrorString("r3d_SurfaceSetAlphaFromBMP", "Can not allocate memory", DD_OK);
      return FALSE;
   }
   if (bPalette256 == TRUE)
   {
      if (NULL == (_r3d_256 = new unsigned char[dwWidth * dwHeight]))
      {
         _r3d_FormulateErrorString("r3d_SurfaceSetAlphaFromBMP", "Can not allocate memory", DD_OK);
         _R3D_DELETE(_r3d_A);
         return FALSE;
      }
   }

   // Now, load the bitmap image
   if (_r3d_BMPLoad(BitmapFilename, "r3d_SurfaceSetAlphaFromBMP", _r3d_A, _r3d_256) == FALSE)
   {
      // Error string already defined
      _R3D_DELETE(_r3d_A);
      _R3D_DELETE(_r3d_256);
      return FALSE;
   }

   // We can delete this after we call _r3d_BMPLoad()
   _R3D_DELETE(_r3d_256);

   // We loop through the size of the virtual surface since
   // we can avoid pointer underflow and overflow this way.
   UCptr = (unsigned char *)r3d_SurfaceGet(handle);
   UCptr += 3; // Alpha channel for RGBA
   //for ( LoopVarY=0; LoopVarY<(int)_r3d_Surface[handle]->dwHeight; LoopVarY++)
   for (LoopVarY = (int)dwHeight - 1;LoopVarY >= 0;LoopVarY--)
   {
      for (LoopVarX = 0;LoopVarX < (int)_r3d_Surface[handle]->dwWidth;LoopVarX++)
      {
         // We take alpha from the second channel... Should be green
         *UCptr = _r3d_A[(LoopVarY * dwWidth*3) + (LoopVarX*3)+1];
         UCptr += 4;
      }
   }
   // We're done, we can delete this now...
   _R3D_DELETE(_r3d_A);

   return TRUE;
}

// If we use a color key and a getpixel function we can get the same
// results and allow for custom colorkeying.
BOOL R3D_STDCALL
r3d_SurfaceCreateFromBMP(int *handle, char *BitmapFilename)
{
   //char buffer[512];
   int LoopVarX, LoopVarY;
   unsigned char *UCptr;
   int hBitmap;
   DWORD dwWidth, dwHeight;
   BOOL bPalette256;

   // First, we get the BMP width and height
   if (_r3d_BMPGetInfo(BitmapFilename, "r3d_SurfaceCreateFromBMP", &dwWidth, &dwHeight, &bPalette256) == FALSE)
   {
      // Error string already defined
      return FALSE;
   }

   // Now, allocate memory
   if (NULL == (_r3d_BGR = new unsigned char[dwWidth * dwHeight*3]))
   {
      _r3d_FormulateErrorString("r3d_SurfaceCreateFromBMP", "Can not allocate memory", DD_OK);
      return FALSE;
   }
   if (bPalette256 == TRUE)
   {
      if (NULL == (_r3d_256 = new unsigned char[dwWidth * dwHeight]))
      {
         _r3d_FormulateErrorString("r3d_SurfaceCreateFromBMP", "Can not allocate memory", DD_OK);
         _R3D_DELETE(_r3d_BGR);
         return FALSE;
      }
   }

   // Now, load the bitmap image
   if (_r3d_BMPLoad(BitmapFilename, "r3d_SurfaceCreateFromBMP", _r3d_BGR, _r3d_256) == FALSE)
   {
      // Error string already defined
      _R3D_DELETE(_r3d_BGR);
      _R3D_DELETE(_r3d_256);
      return FALSE;
   }

   // We can delete this after we call _r3d_BMPLoad()
   _R3D_DELETE(_r3d_256);

   // Now let's create a surface for the bitmap
   if (r3d_SurfaceCreate(&hBitmap, (int)dwWidth, (int)dwHeight) == FALSE)
   {
      // Error string already defined
      _R3D_DELETE(_r3d_BGR);
      return FALSE;
   }
   *handle = hBitmap;

   // Now let's copy it to our nice RGBA surface
   UCptr = (unsigned char *)r3d_SurfaceGet(hBitmap);
   for (LoopVarY = (int)dwHeight - 1;LoopVarY >= 0;LoopVarY--)
      //for ( LoopVarY=0; LoopVarY<(int)dwHeight; LoopVarY++ )
   {
      for (LoopVarX = 0;LoopVarX < (int)dwWidth;LoopVarX++)
      {
         *UCptr = _r3d_BGR[(LoopVarY * dwWidth*3) + (LoopVarX*3)+2]; // Red Component
         UCptr++;
         *UCptr = _r3d_BGR[(LoopVarY * dwWidth*3) + (LoopVarX*3)+1]; // Green Component
         UCptr++;
         *UCptr = _r3d_BGR[(LoopVarY * dwWidth*3) + (LoopVarX*3)+0]; // Blue Component
         UCptr++;
         *UCptr = 255;
         UCptr++;
      }
   }

   // We're done, we can delete this now...
   _R3D_DELETE(_r3d_BGR);

   return TRUE;
}

// Returns a pointer to the base of the virtual surface memory array
DWORD *R3D_STDCALL
r3d_SurfaceGet(int handle)
{
   if (_r3d_bSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceGet", "Invalid Surface Handle", DD_OK);
      return NULL;
   }

   return (DWORD *)(&_r3d_Surface[handle]->RGBA[0]);
}

BOOL R3D_STDCALL
r3d_2DBegin(unsigned char **pMem, int *Pitch, int *PixelFormat)
{
   if (_r3d_bInsideRenderBeginEnd == TRUE)
   {
      _r3d_FormulateErrorString("r3d_2DBegin", "Can not call r3d_2D...() series of functions between an r3d_RenderBegin() and r3d_RenderEnd() pair.", _r3d_hResult);
      return FALSE;
   }
   if (_r3d_2DBeginEnd.bIsLocked == TRUE)
   {
      _r3d_FormulateErrorString("r3d_2DBegin", "r3d_2DBegin() can not be called twice without first calling r3d_2DEnd().", _r3d_hResult);
      return FALSE;
   }

   // Lock back buffer
   //memset(&_r3d_2DBeginEnd.ddsd, 0, sizeof(_r3d_2DBeginEnd.ddsd));
   //_r3d_2DBeginEnd.ddsd.dwSize = sizeof(_r3d_2DBeginEnd.ddsd);
   //_r3d_2DBeginEnd.ddsd.dwFlags = DDSD_PITCH;
   _r3d_ProperlyInitDDSD(&_r3d_2DBeginEnd.ddsd);
   if ((_r3d_hResult = _r3d_BackBuffer4->Lock(NULL, &_r3d_2DBeginEnd.ddsd, DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) != DD_OK)
   {
      _r3d_FormulateErrorString("r3d_2DBegin", "_r3d_BackBuffer4->Lock()", _r3d_hResult);
      return FALSE;
   }
   //_r3d_2DBeginEnd.ddsd.lPitch=_r3d_gpCurrentDriver->pCurrentDevice->pCurrentMode->ddsd.lPitch;

   _r3d_2DBeginEnd.bIsLocked = TRUE;

   // Set this before we leave
   if (pMem != NULL)
      *pMem = (unsigned char *)_r3d_2DBeginEnd.ddsd.lpSurface;
   if (Pitch != NULL)
      *Pitch = (int)_r3d_2DBeginEnd.ddsd.lPitch;
   if (PixelFormat != NULL)
      *PixelFormat = _r3d_PixelFormatCode;

   return TRUE;
}

BOOL R3D_STDCALL
r3d_2DEnd(void)
{
   if (_r3d_bInsideRenderBeginEnd == TRUE)
   {
      _r3d_FormulateErrorString("r3d_2DEnd", "Can not call r3d_2D...() series of functions between an r3d_RenderBegin() and r3d_RenderEnd() pair.", _r3d_hResult);
      return FALSE;
   }
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      _r3d_FormulateErrorString("r3d_2DEnd", "r3d_2DEnd() not preceeded by r3d_2DBegin().", _r3d_hResult);
      return FALSE;
   }

   if ((_r3d_hResult = _r3d_BackBuffer4->Unlock(NULL)) != DD_OK)
   {
      _r3d_FormulateErrorString("r3d_2DEnd", "_r3d_BackBuffer4->Unlock()", _r3d_hResult);
      return FALSE;
   }

   _r3d_2DBeginEnd.bIsLocked = FALSE;

   return TRUE;
}

BOOL R3D_STDCALL
r3d_2DSetPixel(int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
   unsigned char br, bg, bb;
   int DstR, DstG, DstB;

   // Clip
   if (x < _r3d_2DClipMinX)
      return TRUE;
   if (x > _r3d_2DClipMaxX)
      return TRUE;
   if (y < _r3d_2DClipMinY)
      return TRUE;
   if (y > _r3d_2DClipMaxY)
      return TRUE;

   // Lock back buffer, if required
   BOOL bUnlockRequired = FALSE;
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      bUnlockRequired = TRUE;
   }

   if (a == 255)
   {
      switch (_r3d_PixelFormatCode)
      {
      case 0:
         _r3d_Unsupported_SetPixelRGB0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      case 1:
         _r3d_Unsupported_SetPixelRGB1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      case 2:
         _r3d_Unsupported_SetPixelR2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
         _r3d_Unsupported_SetPixelG2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
         _r3d_Unsupported_SetPixelB2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
         break;
      case 3:
         _r3d_Unsupported_SetPixelR3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
         _r3d_Unsupported_SetPixelG3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
         _r3d_Unsupported_SetPixelB3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
         break;
      case 4:
         _r3d_Unsupported_SetPixelRGB4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      case 5:
         _r3d_Unsupported_SetPixelRGB5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      }
   }
   else
   {
      switch (_r3d_PixelFormatCode)
      {
      case 0:
         DstR = _r3d_Unsupported_GetPixelRed0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstG = _r3d_Unsupported_GetPixelGreen0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstB = _r3d_Unsupported_GetPixelBlue0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         br = (unsigned char)(DstR + ((((int)r - DstR) * (int)a) / 255));
         bg = (unsigned char)(DstG + ((((int)g - DstG) * (int)a) / 255));
         bb = (unsigned char)(DstB + ((((int)b - DstB) * (int)a) / 255));
         _r3d_Unsupported_SetPixelRGB0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, br, bg, bb);
         break;
      case 1:
         DstR = _r3d_Unsupported_GetPixelRed1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstG = _r3d_Unsupported_GetPixelGreen1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstB = _r3d_Unsupported_GetPixelBlue1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         br = (unsigned char)(DstR + ((((int)r - DstR) * (int)a) / 255));
         bg = (unsigned char)(DstG + ((((int)g - DstG) * (int)a) / 255));
         bb = (unsigned char)(DstB + ((((int)b - DstB) * (int)a) / 255));
         _r3d_Unsupported_SetPixelRGB1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      case 2:
         DstR = _r3d_Unsupported_GetPixelRed2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstG = _r3d_Unsupported_GetPixelGreen2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstB = _r3d_Unsupported_GetPixelBlue2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         br = (unsigned char)(DstR + ((((int)r - DstR) * (int)a) / 255));
         bg = (unsigned char)(DstG + ((((int)g - DstG) * (int)a) / 255));
         bb = (unsigned char)(DstB + ((((int)b - DstB) * (int)a) / 255));
         _r3d_Unsupported_SetPixelR2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
         _r3d_Unsupported_SetPixelG2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
         _r3d_Unsupported_SetPixelB2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
         break;
      case 3:
         DstR = _r3d_Unsupported_GetPixelRed3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstG = _r3d_Unsupported_GetPixelGreen3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstB = _r3d_Unsupported_GetPixelBlue3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         br = (unsigned char)(DstR + ((((int)r - DstR) * (int)a) / 255));
         bg = (unsigned char)(DstG + ((((int)g - DstG) * (int)a) / 255));
         bb = (unsigned char)(DstB + ((((int)b - DstB) * (int)a) / 255));
         _r3d_Unsupported_SetPixelR3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
         _r3d_Unsupported_SetPixelG3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
         _r3d_Unsupported_SetPixelB3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
         break;
      case 4:
         DstR = _r3d_Unsupported_GetPixelRed4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstG = _r3d_Unsupported_GetPixelGreen4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstB = _r3d_Unsupported_GetPixelBlue4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         br = (unsigned char)(DstR + ((((int)r - DstR) * (int)a) / 255));
         bg = (unsigned char)(DstG + ((((int)g - DstG) * (int)a) / 255));
         bb = (unsigned char)(DstB + ((((int)b - DstB) * (int)a) / 255));
         _r3d_Unsupported_SetPixelRGB4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      case 5:
         DstR = _r3d_Unsupported_GetPixelRed5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstG = _r3d_Unsupported_GetPixelGreen5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         DstB = _r3d_Unsupported_GetPixelBlue5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
         br = (unsigned char)(DstR + ((((int)r - DstR) * (int)a) / 255));
         bg = (unsigned char)(DstG + ((((int)g - DstG) * (int)a) / 255));
         bb = (unsigned char)(DstB + ((((int)b - DstB) * (int)a) / 255));
         _r3d_Unsupported_SetPixelRGB5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
         break;
      }
   }

   if (bUnlockRequired == TRUE)
   {
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return TRUE;
}

unsigned char R3D_STDCALL
r3d_2DGetPixelRed(int x, int y)
{
   unsigned char r;

   // Clip
   if (x < _r3d_2DClipMinX)
      return 0;
   if (x > _r3d_2DClipMaxX)
      return 0;
   if (y < _r3d_2DClipMinY)
      return 0;
   if (y > _r3d_2DClipMaxY)
      return 0;

   // Lock back buffer, if required
   BOOL bUnlockRequired = FALSE;
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      bUnlockRequired = TRUE;
   }

   switch (_r3d_PixelFormatCode)
   {
   case 0:
      r = _r3d_Unsupported_GetPixelRed0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 1:
      r = _r3d_Unsupported_GetPixelRed1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 2:
      r = _r3d_Unsupported_GetPixelRed2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 3:
      r = _r3d_Unsupported_GetPixelRed3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 4:
      r = _r3d_Unsupported_GetPixelRed4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 5:
      r = _r3d_Unsupported_GetPixelRed5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   }

   if (bUnlockRequired == TRUE)
   {
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return r;
}

unsigned char R3D_STDCALL
r3d_2DGetPixelGreen(int x, int y)
{
   unsigned char g;

   // Clip
   if (x < _r3d_2DClipMinX)
      return 0;
   if (x > _r3d_2DClipMaxX)
      return 0;
   if (y < _r3d_2DClipMinY)
      return 0;
   if (y > _r3d_2DClipMaxY)
      return 0;

   // Lock back buffer, if required
   BOOL bUnlockRequired = FALSE;
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      bUnlockRequired = TRUE;
   }

   switch (_r3d_PixelFormatCode)
   {
   case 0:
      g = _r3d_Unsupported_GetPixelGreen0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 1:
      g = _r3d_Unsupported_GetPixelGreen1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 2:
      g = _r3d_Unsupported_GetPixelGreen2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 3:
      g = _r3d_Unsupported_GetPixelGreen3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 4:
      g = _r3d_Unsupported_GetPixelGreen4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 5:
      g = _r3d_Unsupported_GetPixelGreen5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   }

   if (bUnlockRequired == TRUE)
   {
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return g;
}

unsigned char R3D_STDCALL
r3d_2DGetPixelBlue(int x, int y)
{
   unsigned char b;

   // Clip
   if (x < _r3d_2DClipMinX)
      return 0;
   if (x > _r3d_2DClipMaxX)
      return 0;
   if (y < _r3d_2DClipMinY)
      return 0;
   if (y > _r3d_2DClipMaxY)
      return 0;

   // Lock back buffer, if required
   BOOL bUnlockRequired = FALSE;
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      bUnlockRequired = TRUE;
   }

   switch (_r3d_PixelFormatCode)
   {
   case 0:
      b = _r3d_Unsupported_GetPixelBlue0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 1:
      b = _r3d_Unsupported_GetPixelBlue1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 2:
      b = _r3d_Unsupported_GetPixelBlue2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 3:
      b = _r3d_Unsupported_GetPixelBlue3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 4:
      b = _r3d_Unsupported_GetPixelBlue4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   case 5:
      b = _r3d_Unsupported_GetPixelBlue5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
      break;
   }

   if (bUnlockRequired == TRUE)
   {
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return b;
}

int R3D_STDCALL
r3d_SurfaceGetWidth(int handle)
{
   if (_r3d_bSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceGetWidth", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   return (int)_r3d_Surface[handle]->dwWidth;
}

int R3D_STDCALL
r3d_SurfaceGetHeight(int handle)
{
   if (_r3d_bSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceGetHeight", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   return (int)_r3d_Surface[handle]->dwHeight;
}

BOOL R3D_STDCALL
r3d_SurfaceCreateFromBackBuffer(int *hSrc)
{
   unsigned char *pSurface;
   unsigned long x, y;
   int hBitmap;

   if (r3d_SurfaceCreate(&hBitmap, (int)_r3d_dwRenderWidth, (int)_r3d_dwRenderHeight) == FALSE)
   {
      // Error string already defined
      return FALSE;
   }
   *hSrc = hBitmap;

   // Lock back buffer, if required
   BOOL bUnlockRequired = FALSE;
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      bUnlockRequired = TRUE;
   }


   pSurface = (unsigned char *)r3d_SurfaceGet(hBitmap);
   for (y = 0;y < _r3d_dwRenderHeight;y++)
   {
      for (x = 0;x < _r3d_dwRenderWidth;x++)
      {
         switch (_r3d_PixelFormatCode)
         {
         case 0:
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 1:
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 2:
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 3:
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 4:
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 5:
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            pSurface[(y * (_r3d_dwRenderWidth << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         }
      }
   }

   if (bUnlockRequired == TRUE)
   {
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_2DFadeIn(int hSrc, int SrcMinx, int SrcMaxx, int SrcMiny, int SrcMaxy, int DstMinx, int DstMaxx, int DstMiny, int DstMaxy, int StepSize)
{
   unsigned long x, y, SrcY, LookUp;
   unsigned long SrcA, SrcR, SrcG, SrcB;
   unsigned long DstR, DstG, DstB;
   unsigned char r, g, b;
   BOOL bScale;
   unsigned char *SrcRGBA;
   unsigned long DstW, DstH, SrcW, SrcH;
   unsigned long HRatio, WRatio;

   if (_r3d_bSurfaceDoesNotExist[hSrc] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_2DFadeIn", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   _R3D_BLITCLIP((int)(_r3d_Surface[hSrc]->dwWidth), (int)(_r3d_Surface[hSrc]->dwHeight), (int)(_r3d_2DClipMaxX + 1), (int)(_r3d_2DClipMaxY + 1), SrcMinx, SrcMaxx, SrcMiny, SrcMaxy, DstMinx, DstMaxx, DstMiny, DstMaxy);

   // Determine Scale Size
   DstW = (DstMaxx - DstMinx) + 1;
   DstH = (DstMaxy - DstMiny) + 1;
   SrcW = (SrcMaxx - SrcMinx) + 1;
   SrcH = (SrcMaxy - SrcMiny) + 1;
   if (DstW == SrcW && DstH == SrcH)
   {
      bScale = FALSE;
   }
   else
   {
      bScale = TRUE;
      WRatio = (SrcW << 16) / DstW;
      HRatio = (SrcH << 16) / DstH;
   }

   // Lock back buffer, if required
   BOOL bLockStatus;
   bLockStatus = _r3d_2DBeginEnd.bIsLocked;
   if (bLockStatus == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   // We also need to keep a copy of the back buffer somewhere else
   if (NULL == (_r3d_FadeInBackBufferCopy = new unsigned char[DstW * DstH*4]))
   {
      _r3d_FormulateErrorString("r3d_2DFadeIn", "Can not allocate memory", DD_OK);
      return FALSE;
   }
   for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
   {
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
      {
         switch (_r3d_PixelFormatCode)
         {
         case 0:
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 1:
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 2:
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 3:
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 4:
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         case 5:
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)] = _r3d_Unsupported_GetPixelRed5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1] = _r3d_Unsupported_GetPixelGreen5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2] = _r3d_Unsupported_GetPixelBlue5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
            break;
         }
      }
   }

   // Unlock
   if (r3d_2DEnd() == FALSE)
   {
      // Error string already defined...
      return FALSE;
   }

   // Let's build the scanline lookup table
   //
   if (bScale == TRUE)
   {
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         _r3d_ScanLineLookUpTable[x - DstMinx] = (((((x - DstMinx) * WRatio) >> 16) + SrcMinx) << 2);
   }
   else
   {
      // We could actually create a pre built table when we initialize R3D
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         _r3d_ScanLineLookUpTable[x - DstMinx] = (((x - DstMinx) + SrcMinx) << 2);
   }

   // Fade it in
   SrcRGBA = (unsigned char *)r3d_SurfaceGet(hSrc);
   for (SrcA = 0;SrcA <= 255;SrcA += StepSize)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      switch (_r3d_PixelFormatCode)
      {
      case 0:
         for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
         {
            if (bScale == TRUE)
               SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            else
               SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               SrcR = SrcRGBA[LookUp];
               SrcG = SrcRGBA[LookUp+1];
               SrcB = SrcRGBA[LookUp+2];
               DstR = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)];
               DstG = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1];
               DstB = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2];
               r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
               g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
               b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
               _r3d_Unsupported_SetPixelRGB0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
            }
         }
         break;
      case 1:
         for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
         {
            if (bScale == TRUE)
               SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            else
               SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               SrcR = SrcRGBA[LookUp];
               SrcG = SrcRGBA[LookUp+1];
               SrcB = SrcRGBA[LookUp+2];
               DstR = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)];
               DstG = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1];
               DstB = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2];
               r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
               g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
               b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
               _r3d_Unsupported_SetPixelRGB1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
            }
         }
         break;
      case 2:
         for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
         {
            if (bScale == TRUE)
               SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            else
               SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               SrcR = SrcRGBA[LookUp];
               SrcG = SrcRGBA[LookUp+1];
               SrcB = SrcRGBA[LookUp+2];
               DstR = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)];
               DstG = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1];
               DstB = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2];
               r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
               g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
               b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
               _r3d_Unsupported_SetPixelR2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
               _r3d_Unsupported_SetPixelG2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
               _r3d_Unsupported_SetPixelB2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
            }
         }
         break;
      case 3:
         for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
         {
            if (bScale == TRUE)
               SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            else
               SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               SrcR = SrcRGBA[LookUp];
               SrcG = SrcRGBA[LookUp+1];
               SrcB = SrcRGBA[LookUp+2];
               DstR = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)];
               DstG = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1];
               DstB = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2];
               r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
               g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
               b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
               _r3d_Unsupported_SetPixelR3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
               _r3d_Unsupported_SetPixelG3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
               _r3d_Unsupported_SetPixelB3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
            }
         }
         break;
      case 4:
         for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
         {
            if (bScale == TRUE)
               SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            else
               SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               SrcR = SrcRGBA[LookUp];
               SrcG = SrcRGBA[LookUp+1];
               SrcB = SrcRGBA[LookUp+2];
               DstR = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)];
               DstG = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1];
               DstB = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2];
               r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
               g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
               b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
               _r3d_Unsupported_SetPixelRGB4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
            }
         }
         break;
      case 5:
         for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
         {
            if (bScale == TRUE)
               SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            else
               SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               SrcR = SrcRGBA[LookUp];
               SrcG = SrcRGBA[LookUp+1];
               SrcB = SrcRGBA[LookUp+2];
               DstR = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)];
               DstG = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+1];
               DstB = _r3d_FadeInBackBufferCopy[(y * (DstW << 2)) + (x << 2)+2];
               r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
               g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
               b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
               _r3d_Unsupported_SetPixelRGB5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
            }
         }
         break;
      }
      // Unlock and page flip
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      r3d_PageFlip(FALSE, FALSE);
   }

   _R3D_DELETE(_r3d_FadeInBackBufferCopy);

   // We do a final blit and page flip
   r3d_2DBlit(hSrc, SrcMinx, SrcMaxx, SrcMiny, SrcMaxy, DstMinx, DstMaxx, DstMiny, DstMaxy, FALSE);
   r3d_PageFlip(FALSE, FALSE);
   r3d_2DBlit(hSrc, SrcMinx, SrcMaxx, SrcMiny, SrcMaxy, DstMinx, DstMaxx, DstMiny, DstMaxy, FALSE);
   r3d_PageFlip(FALSE, FALSE);

   if (bLockStatus == TRUE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_2DBlit(int hSrc, int SrcMinx, int SrcMaxx, int SrcMiny, int SrcMaxy, int DstMinx, int DstMaxx, int DstMiny, int DstMaxy, BOOL BlendAlphaChannel)
{
   unsigned long x, y, SrcY, LookUp;
   unsigned long SrcA, SrcR, SrcG, SrcB;
   unsigned long DstR, DstG, DstB;
   unsigned char r, g, b;
   BOOL bScale;
   unsigned char *SrcRGBA;
   unsigned long DstW, DstH, SrcW, SrcH;
   unsigned long HRatio, WRatio;

   if (_r3d_bSurfaceDoesNotExist[hSrc] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_2DBlit", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }
   _R3D_BLITCLIP((int)(_r3d_Surface[hSrc]->dwWidth), (int)(_r3d_Surface[hSrc]->dwHeight), (int)(_r3d_2DClipMaxX + 1), (int)(_r3d_2DClipMaxY + 1), SrcMinx, SrcMaxx, SrcMiny, SrcMaxy, DstMinx, DstMaxx, DstMiny, DstMaxy);

   // Determine Scale Size
   DstW = (DstMaxx - DstMinx) + 1;
   DstH = (DstMaxy - DstMiny) + 1;
   SrcW = (SrcMaxx - SrcMinx) + 1;
   SrcH = (SrcMaxy - SrcMiny) + 1;
   if (DstW == SrcW && DstH == SrcH)
   {
      bScale = FALSE;
   }
   else
   {
      bScale = TRUE;
      WRatio = (SrcW << 16) / DstW;
      HRatio = (SrcH << 16) / DstH;
   }

   // Lock back buffer, if required
   BOOL bUnlockRequired = FALSE;
   if (_r3d_2DBeginEnd.bIsLocked == FALSE)
   {
      if (r3d_2DBegin(NULL, NULL, NULL) == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
      bUnlockRequired = TRUE;
   }

   // Let's build the scanline lookup table
   //
   if (bScale == TRUE)
   {
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         _r3d_ScanLineLookUpTable[x - DstMinx] = (((((x - DstMinx) * WRatio) >> 16) + SrcMinx) << 2);
   }
   else
   {
      // We could actually create a pre built table when we initialize R3D
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         _r3d_ScanLineLookUpTable[x - DstMinx] = (((x - DstMinx) + SrcMinx) << 2);
   }

   // Blit it
   SrcRGBA = (unsigned char *)r3d_SurfaceGet(hSrc);
   switch (_r3d_PixelFormatCode)
   {
   case 0:
      for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
      {
         if (bScale == TRUE)
            SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         else
            SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         if (BlendAlphaChannel)
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               //if ( 255==(SrcA=SrcRGBA[LookUp+3]) )
               SrcA = SrcRGBA[LookUp+3];
               if (SrcA == 255)
               {
                  _r3d_Unsupported_SetPixelRGB0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
               }
               else
               {
                  SrcR = SrcRGBA[LookUp];
                  SrcG = SrcRGBA[LookUp+1];
                  SrcB = SrcRGBA[LookUp+2];
                  DstR = _r3d_Unsupported_GetPixelRed0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstG = _r3d_Unsupported_GetPixelGreen0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstB = _r3d_Unsupported_GetPixelBlue0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
                  g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
                  b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
                  _r3d_Unsupported_SetPixelRGB0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
               }
            }
         }
         else
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               _r3d_Unsupported_SetPixelRGB0(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
            }
         }
      }
      break;
   case 1:
      for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
      {
         if (bScale == TRUE)
            SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         else
            SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         if (BlendAlphaChannel)
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               //if ( 255==(SrcA=SrcRGBA[LookUp+3]) )
               SrcA = SrcRGBA[LookUp+3];
               if (SrcA == 255)
               {
                  _r3d_Unsupported_SetPixelRGB1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
               }
               else
               {
                  SrcR = SrcRGBA[LookUp];
                  SrcG = SrcRGBA[LookUp+1];
                  SrcB = SrcRGBA[LookUp+2];
                  DstR = _r3d_Unsupported_GetPixelRed1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstG = _r3d_Unsupported_GetPixelGreen1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstB = _r3d_Unsupported_GetPixelBlue1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
                  g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
                  b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
                  _r3d_Unsupported_SetPixelRGB1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
               }
            }
         }
         else
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               _r3d_Unsupported_SetPixelRGB1(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
            }
         }
      }
      break;
   case 2:
      for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
      {
         if (bScale == TRUE)
            SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         else
            SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         if (BlendAlphaChannel)
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               //if ( 255==(SrcA=SrcRGBA[LookUp+3]) )
               SrcA = SrcRGBA[LookUp+3];
               if (SrcA == 255)
               {
                  _r3d_Unsupported_SetPixelR2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp]);
                  _r3d_Unsupported_SetPixelG2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+1]);
                  _r3d_Unsupported_SetPixelB2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+2]);
               }
               else
               {
                  SrcR = SrcRGBA[LookUp];
                  SrcG = SrcRGBA[LookUp+1];
                  SrcB = SrcRGBA[LookUp+2];
                  DstR = _r3d_Unsupported_GetPixelRed2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstG = _r3d_Unsupported_GetPixelGreen2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstB = _r3d_Unsupported_GetPixelBlue2(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
                  g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
                  b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
                  _r3d_Unsupported_SetPixelR2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
                  _r3d_Unsupported_SetPixelG2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
                  _r3d_Unsupported_SetPixelB2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
               }
            }
         }
         else
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               _r3d_Unsupported_SetPixelR2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp]);
               _r3d_Unsupported_SetPixelG2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+1]);
               _r3d_Unsupported_SetPixelB2Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+2]);
            }
         }
      }
      break;
   case 3:
      for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
      {
         if (bScale == TRUE)
            SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         else
            SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         if (BlendAlphaChannel)
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               //if ( 255==(SrcA=SrcRGBA[LookUp+3]) )
               SrcA = SrcRGBA[LookUp+3];
               if (SrcA == 255)
               {
                  _r3d_Unsupported_SetPixelR3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp]);
                  _r3d_Unsupported_SetPixelG3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+1]);
                  _r3d_Unsupported_SetPixelB3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+2]);
               }
               else
               {
                  SrcR = SrcRGBA[LookUp];
                  SrcG = SrcRGBA[LookUp+1];
                  SrcB = SrcRGBA[LookUp+2];
                  DstR = _r3d_Unsupported_GetPixelRed3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstG = _r3d_Unsupported_GetPixelGreen3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstB = _r3d_Unsupported_GetPixelBlue3(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
                  g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
                  b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
                  _r3d_Unsupported_SetPixelR3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r);
                  _r3d_Unsupported_SetPixelG3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, g);
                  _r3d_Unsupported_SetPixelB3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, b);
               }
            }
         }
         else
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               _r3d_Unsupported_SetPixelR3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp]);
               _r3d_Unsupported_SetPixelG3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+1]);
               _r3d_Unsupported_SetPixelB3Safe(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp+2]);
            }
         }
      }
      break;
   case 4:
      for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
      {
         if (bScale == TRUE)
            SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         else
            SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         if (BlendAlphaChannel)
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               //if ( 255==(SrcA=SrcRGBA[LookUp+3]) )
               SrcA = SrcRGBA[LookUp+3];
               if (SrcA == 255)
               {
                  _r3d_Unsupported_SetPixelRGB4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
               }
               else
               {
                  SrcR = SrcRGBA[LookUp];
                  SrcG = SrcRGBA[LookUp+1];
                  SrcB = SrcRGBA[LookUp+2];
                  DstR = _r3d_Unsupported_GetPixelRed4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstG = _r3d_Unsupported_GetPixelGreen4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstB = _r3d_Unsupported_GetPixelBlue4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
                  g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
                  b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
                  _r3d_Unsupported_SetPixelRGB4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
               }
            }
         }
         else
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               _r3d_Unsupported_SetPixelRGB4(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
            }
         }
      }
      break;
   case 5:
      for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
      {
         if (bScale == TRUE)
            SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         else
            SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
         if (BlendAlphaChannel)
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               //if ( 255==(SrcA=SrcRGBA[LookUp+3]) )
               SrcA = SrcRGBA[LookUp+3];
               if (SrcA == 255)
               {
                  _r3d_Unsupported_SetPixelRGB5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
               }
               else
               {
                  SrcR = SrcRGBA[LookUp];
                  SrcG = SrcRGBA[LookUp+1];
                  SrcB = SrcRGBA[LookUp+2];
                  DstR = _r3d_Unsupported_GetPixelRed5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstG = _r3d_Unsupported_GetPixelGreen5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  DstB = _r3d_Unsupported_GetPixelBlue5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y);
                  r = (unsigned char)(DstR + (((SrcR - DstR) * SrcA) / 255));
                  g = (unsigned char)(DstG + (((SrcG - DstG) * SrcA) / 255));
                  b = (unsigned char)(DstB + (((SrcB - DstB) * SrcA) / 255));
                  _r3d_Unsupported_SetPixelRGB5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, r, g, b);
               }
            }
         }
         else
         {
            for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
            {
               LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
               _r3d_Unsupported_SetPixelRGB5(_r3d_2DBeginEnd.ddsd.lpSurface, _r3d_2DBeginEnd.ddsd.lPitch, x, y, SrcRGBA[LookUp], SrcRGBA[LookUp+1], SrcRGBA[LookUp+2]);
            }
         }
      }
      break;
   }

   if (bUnlockRequired == TRUE)
   {
      if (r3d_2DEnd() == FALSE)
      {
         // Error string already defined...
         return FALSE;
      }
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SurfaceBlit(int hSrc, int hDst, int SrcMinx, int SrcMaxx, int SrcMiny, int SrcMaxy, int DstMinx, int DstMaxx, int DstMiny, int DstMaxy, BOOL BlendAlphaChannel)
{
   unsigned long x, y, SrcY, LookUp, DstY;
   unsigned long SrcA;
   unsigned long DstR, DstG, DstB;
   BOOL bScale;
   unsigned char *SrcRGBA, *DstRGBA;
   unsigned long DstW, DstH, SrcW, SrcH;
   unsigned long HRatio, WRatio;

   if (_r3d_bSurfaceDoesNotExist[hSrc] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceBlit", "hSrc: Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   if (_r3d_bSurfaceDoesNotExist[hDst] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceBlit", "hDst: Invalid Surface Handle", DD_OK);
      return FALSE;
   }
   _R3D_BLITCLIP((int)(_r3d_Surface[hSrc]->dwWidth), (int)(_r3d_Surface[hSrc]->dwHeight), (int)(_r3d_Surface[hDst]->dwWidth), (int)(_r3d_Surface[hDst]->dwHeight), SrcMinx, SrcMaxx, SrcMiny, SrcMaxy, DstMinx, DstMaxx, DstMiny, DstMaxy);

   // Determine Scale Size
   DstW = (DstMaxx - DstMinx) + 1;
   DstH = (DstMaxy - DstMiny) + 1;
   SrcW = (SrcMaxx - SrcMinx) + 1;
   SrcH = (SrcMaxy - SrcMiny) + 1;
   if (DstW == SrcW && DstH == SrcH)
   {
      bScale = FALSE;
   }
   else
   {
      bScale = TRUE;
      WRatio = (SrcW << 16) / DstW;
      HRatio = (SrcH << 16) / DstH;
   }

   // Let's build the scanline lookup table
   //
   if (bScale == TRUE)
   {
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         _r3d_ScanLineLookUpTable[x - DstMinx] = (((((x - DstMinx) * WRatio) >> 16) + SrcMinx) << 2);
   }
   else
   {
      // We could actually create a pre built table when we initialize R3D
      for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         _r3d_ScanLineLookUpTable[x - DstMinx] = (((x - DstMinx) + SrcMinx) << 2);
   }

   // Blit it
   SrcRGBA = (unsigned char *)r3d_SurfaceGet(hSrc);
   DstRGBA = (unsigned char *)r3d_SurfaceGet(hDst);
   for (y = (unsigned long)DstMiny;y <= (unsigned long)DstMaxy;y++)
   {
      if (bScale == TRUE)
         SrcY = ((((y - DstMiny) * HRatio) >> 16) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
      else
         SrcY = ((y - DstMiny) + SrcMiny) * (_r3d_Surface[hSrc]->dwWidth << 2);
      //SrcY*=(_r3d_Surface[hSrc]->dwWidth<<2);
      DstY = ((_r3d_Surface[hDst]->dwWidth << 2) * y);
      if (BlendAlphaChannel)
      {
         for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         {
            LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
            if (255 == (SrcA = SrcRGBA[LookUp+3]))
            {
               DstRGBA[DstY + (x << 2)] = SrcRGBA[LookUp];
               DstRGBA[DstY + (x << 2)+1] = SrcRGBA[LookUp+1];
               DstRGBA[DstY + (x << 2)+2] = SrcRGBA[LookUp+2];
            }
            else
            {
               //SrcR=SrcRGBA[LookUp];
               //SrcG=SrcRGBA[LookUp+1];
               //SrcB=SrcRGBA[LookUp+2];
               DstR = DstRGBA[DstY + (x << 2)];
               DstG = DstRGBA[DstY + (x << 2)+1];
               DstB = DstRGBA[DstY + (x << 2)+2];
               //r=(unsigned char)(DstR+(((SrcR-DstR)*SrcA)/255));
               //g=(unsigned char)(DstG+(((SrcG-DstG)*SrcA)/255));
               //b=(unsigned char)(DstB+(((SrcB-DstB)*SrcA)/255));
               //DstRGBA[DstY+(x<<2)]=r;
               //DstRGBA[DstY+(x<<2)+1]=b;
               //DstRGBA[DstY+(x<<2)+2]=g;
               //DstRGBA[DstY+(x<<2)]=(unsigned char)(DstR+(((SrcR-DstR)*SrcA)/255));
               //DstRGBA[DstY+(x<<2)+1]=(unsigned char)(DstG+(((SrcG-DstG)*SrcA)/255));
               //DstRGBA[DstY+(x<<2)+2]=(unsigned char)(DstB+(((SrcB-DstB)*SrcA)/255));
               DstRGBA[DstY + (x << 2)] = (unsigned char)(DstR + (((SrcRGBA[LookUp] - DstR) * SrcA) / 255));
               DstRGBA[DstY + (x << 2)+1] = (unsigned char)(DstG + (((SrcRGBA[LookUp+1] - DstG) * SrcA) / 255));
               DstRGBA[DstY + (x << 2)+2] = (unsigned char)(DstB + (((SrcRGBA[LookUp+2] - DstB) * SrcA) / 255));
            }
         }
      }
      else
      {
         for (x = (unsigned long)DstMinx;x <= (unsigned long)DstMaxx;x++)
         {
            LookUp = SrcY + _r3d_ScanLineLookUpTable[x - DstMinx];
            DstRGBA[DstY + (x << 2)] = SrcRGBA[LookUp];
            DstRGBA[DstY + (x << 2)+1] = SrcRGBA[LookUp+1];
            DstRGBA[DstY + (x << 2)+2] = SrcRGBA[LookUp+2];
            DstRGBA[DstY + (x << 2)+3] = SrcRGBA[LookUp+3];
         }
      }
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SurfaceExportToBMP(int hSrc, char *lpFilename)
{
   // Header used for 640x480x24 bpp image without compression
   unsigned char bmp_24_hdr[54] =
   {
      66, 77, 54, 16, 14, 0, 0, 0, 0, 0, 54, 0,
      0, 0, 40, 0, 0, 0, 128, 2, 0, 0, 224, 1, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
      0, 16, 14, 0, 206, 14, 0, 0, 216, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
   };
   unsigned long dwPaddedBytes;
   unsigned char UCTemp[4] = {0, 0, 0, 0};
   char ErrorBuffer[256];
   HANDLE hFile;
   long LoopVar, LoopVarX;

   if (_r3d_bSurfaceDoesNotExist[hSrc] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", "hSrc: Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   // Write the header our way
   if ((hFile = r3d_FileOpen(lpFilename, R3D_WRITE | R3D_CREATE | R3D_TRUNCATE)) == INVALID_HANDLE_VALUE)
   {
      sprintf(ErrorBuffer, "%s: Error opening file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, bmp_24_hdr, (DWORD)2) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   LoopVar = ((long)(_r3d_Surface[hSrc]->dwWidth * _r3d_Surface[hSrc]->dwHeight) * 3L) + 54L;
   if (r3d_FileWrite(hFile, &LoopVar, (DWORD)4) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, &bmp_24_hdr[6], (DWORD)12) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, &_r3d_Surface[hSrc]->dwWidth, (DWORD)4) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, &_r3d_Surface[hSrc]->dwHeight, (DWORD)4) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, &bmp_24_hdr[26], (DWORD)12) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   LoopVar = 3930L; // 100x100 dpi
   if (r3d_FileWrite(hFile, &LoopVar, (DWORD)4) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, &LoopVar, (DWORD)4) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }
   if (r3d_FileWrite(hFile, &bmp_24_hdr[46], (DWORD)(54 - 46)) == FALSE)
   {
      sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
      return FALSE;
   }

   // Scanlines in BMP's must be padded with zero bytes on long boundries
   dwPaddedBytes = ((_r3d_Surface[hSrc]->dwWidth * 3) % 4);
   if (dwPaddedBytes)
      dwPaddedBytes = 4 - dwPaddedBytes;

   // Allocate a scanline
   if (NULL == (_r3d_BGR = new unsigned char[(_r3d_Surface[hSrc]->dwWidth*3) + dwPaddedBytes]))
   {
      _r3d_FormulateErrorString("r3d_SurfaceToBMP", "Can not allocate memory", DD_OK);
      return FALSE;
   }

   // Set entire allocated scanline to 0, this also ensures that
   // the padded bytes will be zero
   ZeroMemory(&_r3d_BGR[0], (_r3d_Surface[hSrc]->dwWidth * 3) + dwPaddedBytes);

   // Write one scan line at a time
   unsigned long *SurfaceRGBA, dwColor;
   SurfaceRGBA = (unsigned long *)r3d_SurfaceGet(hSrc);
   for (LoopVar = (int)_r3d_Surface[hSrc]->dwHeight - 1;LoopVar >= 0;LoopVar--)
   {
      for (LoopVarX = 0;LoopVarX < (int)_r3d_Surface[hSrc]->dwWidth;LoopVarX++)
      {
         dwColor = SurfaceRGBA[(LoopVar * _r3d_Surface[hSrc]->dwWidth) + LoopVarX];
         _r3d_BGR[(LoopVarX*3)] = r3d_SurfaceColorGetB(dwColor);
         _r3d_BGR[(LoopVarX*3)+1] = r3d_SurfaceColorGetG(dwColor);
         _r3d_BGR[(LoopVarX*3)+2] = r3d_SurfaceColorGetR(dwColor);
      }
      if (r3d_FileWrite(hFile, &_r3d_BGR[0], (_r3d_Surface[hSrc]->dwWidth * 3) + dwPaddedBytes) == FALSE)
      {
         sprintf(ErrorBuffer, "%s: Error writing file", lpFilename);
         _r3d_FormulateErrorString("r3d_SurfaceToBMP", ErrorBuffer, DD_OK);
         return FALSE;
      }
   }
   r3d_FileClose(hFile);

   _R3D_DELETE(_r3d_BGR);

   return TRUE;
}

LPDIRECTDRAW4 R3D_STDCALL
r3d_GetDirectDraw(void)
{
   return (_r3d_DirectDraw4);
}

LPDIRECTDRAWSURFACE4 R3D_STDCALL
r3d_GetFrontBufferSurface(void)
{
   return (_r3d_FrontBuffer4);
}

LPDIRECTDRAWSURFACE4 R3D_STDCALL
r3d_GetBackBufferSurface(void)
{
   return (_r3d_BackBuffer4);
}

LPDIRECTDRAWSURFACE4 R3D_STDCALL
r3d_GetZBufferSurface(void)
{
   return (_r3d_ZBuffer4);
}

LPDIRECT3D3 R3D_STDCALL
r3d_GetDirect3D(void)
{
   return (_r3d_Direct3D);
}

LPDIRECT3DDEVICE3 R3D_STDCALL
r3d_GetDevice(void)
{
   return (_r3d_Device);
}

LPDIRECT3DLIGHT R3D_STDCALL
r3d_GetLight(void)
{
   return (_r3d_Light);
}

LPDIRECT3DVIEWPORT3 R3D_STDCALL
r3d_GetViewport(void)
{
   return (_r3d_Viewport);
}

BOOL R3D_STDCALL
r3d_SetFogAttributes(float fFogZStart, float fFogZEnd, float r, float g, float b)
{
   //unsigned char a=0;

   _r3d_state.FogStart = fFogZStart;
   _r3d_state.FogEnd = fFogZEnd;
   // The render state values can be any RGB color, specified as an
   // RGBA color (the alpha component is ignored).
   // WRONG! _r3d_state.FogColor=(D3DCOLOR)D3DRGBA(D3DVAL(r),D3DVAL(g),D3DVAL(b),D3DVAL(a));
   //_r3d_state.FogColor=((((unsigned long)(a)) << 24) | (((unsigned long)(r)) << 16) | (((unsigned long)(g)) << 8) | (unsigned long)(b));
   _r3d_state.FogColor = ((((unsigned long)((r) * 255)) << 16) | (((unsigned long)((g) * 255)) << 8) | (unsigned long)((b) * 255));

   if ((_r3d_hResult = _r3d_Device->SetRenderState(D3DRENDERSTATE_FOGENABLE, (DWORD)_r3d_state.bFog)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_SetFogAttributes", "D3DRENDERSTATE_FOGENABLE", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetLightState(D3DLIGHTSTATE_FOGMODE, (DWORD)_r3d_state.bFog ? D3DFOG_LINEAR : D3DFOG_NONE)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_SetFogAttributes", "D3DLIGHTSTATE_FOGMODE", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetLightState(D3DLIGHTSTATE_FOGSTART, *(DWORD *)(&_r3d_state.FogStart))) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_SetFogAttributes", "D3DLIGHTSTATE_FOGSTART", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetLightState(D3DLIGHTSTATE_FOGEND, *(DWORD *)(&_r3d_state.FogEnd))) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_SetFogAttributes", "D3DLIGHTSTATE_FOGEND", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetRenderState(D3DRENDERSTATE_FOGCOLOR, (DWORD)_r3d_state.FogColor)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_SetFogAttributes", "D3DRENDERSTATE_FOGCOLOR", _r3d_hResult);
      return FALSE;
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_EnableFog(BOOL bEnable)
{
   _r3d_state.bFog = bEnable;

   if ((_r3d_hResult = _r3d_Device->SetRenderState(D3DRENDERSTATE_FOGENABLE, (DWORD)_r3d_state.bFog)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_EnableFog", "D3DRENDERSTATE_FOGENABLE", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetLightState(D3DLIGHTSTATE_FOGMODE, (DWORD)_r3d_state.bFog ? D3DFOG_LINEAR : D3DFOG_NONE)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_EnableFog", "D3DLIGHTSTATE_FOGMODE", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetLightState(D3DLIGHTSTATE_FOGSTART, *(DWORD *)(&_r3d_state.FogStart))) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_EnableFog", "D3DLIGHTSTATE_FOGSTART", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetLightState(D3DLIGHTSTATE_FOGEND, *(DWORD *)(&_r3d_state.FogEnd))) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_EnableFog", "D3DLIGHTSTATE_FOGEND", _r3d_hResult);
      return FALSE;
   }
   if ((_r3d_hResult = _r3d_Device->SetRenderState(D3DRENDERSTATE_FOGCOLOR, (DWORD)_r3d_state.FogColor)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_EnableFog", "D3DRENDERSTATE_FOGCOLOR", _r3d_hResult);
      return FALSE;
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_EnableSpecular(BOOL bEnable)
{
   _r3d_state.bSpecular = bEnable;

   if ((_r3d_hResult = _r3d_Device->SetRenderState(D3DRENDERSTATE_SPECULARENABLE, (DWORD)_r3d_state.bSpecular)) != D3D_OK)
   {
      _r3d_FormulateErrorString("r3d_EnableSpecular", "SetRenderState(D3DRENDERSTATE_SPECULARENABLE, (DWORD)bEnable)", _r3d_hResult);
      return FALSE;
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_SetFilteringAndMipMapOptions(int Method)
{
   // Texture-map filtering capabilities. General texture filtering flags
   // reflect which texture filtering modes you can set for the
   // D3DRENDERSTATE_TEXTUREMAG, D3DRENDERSTATE_TEXTUREMIN render states.
   // 
   // D3DPTFILTERCAPS_NEAREST
   // Point sampling. The texel with coordinates nearest to the desired pixel
   // value is used. This applies to both zooming in and zooming out. If
   // either zooming in or zooming out is supported, then both must be
   // supported.
   // 
   // D3DPTFILTERCAPS_LINEAR
   // Bilinear filtering. Chooses the texel that has nearest coordinates,
   // then performs a weighted average with the four surrounding texels to
   // determine the final color. This applies to both zooming in and zooming
   // out. If either zooming in or zooming out is supported, then both must
   // be supported.
   // 
   // D3DPTFILTERCAPS_MIPNEAREST
   // Nearest mipmapping. Chooses the texel from the appropriate mipmap with
   // coordinates nearest to the desired pixel value.
   // 
   // D3DPTFILTERCAPS_MIPLINEAR
   // Nearest mipmapping, with bilinear filtering applied to the result.
   // Chooses the texel from the appropriate mipmap that has nearest
   // coordinates, then performs a weighted average with the four surrounding
   // texels to determine the final color.
   // 
   // D3DPTFILTERCAPS_LINEARMIPNEAREST
   // Linear interpolation between two point sampled mipmaps. Chooses the
   // nearest texel from the two closest mipmap levels, then performs linear
   // interpolation between them.
   // 
   // D3DPTFILTERCAPS_LINEARMIPLINEAR
   // Trilinear interpolation between mipmaps. Performs bilinear filtering on
   // the two nearest mipmaps, then interpolates linearly between the two
   // colors to determine a final color.

   int iSupported[6];
   // Assume FALSE
   iSupported[0] = iSupported[1] = iSupported[2] = iSupported[3] = iSupported[4] = iSupported[5] = 0;
   if (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_NEAREST)
      iSupported[0] = 1;
   if (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR)
      iSupported[1] = 1;
   if (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_MIPNEAREST)
      iSupported[2] = 1;
   if (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_MIPLINEAR)
      iSupported[3] = 1;
   if (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEARMIPNEAREST)
      iSupported[4] = 1;
   if (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEARMIPLINEAR)
      iSupported[5] = 1;

   // If none are supported... return quietly
   if (iSupported[0] + iSupported[1] + iSupported[2] + iSupported[3] + iSupported[4] + iSupported[5] == 0)
      return TRUE;

   // If requested method is >=3 but is not supported, find
   // the highest available texture filtering method...

   // Find next highest method... If none available, select first
   // available
   BOOL bFound = FALSE;
   int LoopVar;
   if (iSupported[Method] == 0)
   {
      // Find the next available texture filtering method...
      for (LoopVar = Method;LoopVar < 6;LoopVar++)
      {
         if (iSupported[LoopVar] == 1)
         {
            bFound = TRUE;
            Method = LoopVar;
            break;
         }
      }
      if (bFound == FALSE)
      {
         // Find the first available texture filtering method...
         for (LoopVar = 0;LoopVar < 6;LoopVar++)
         {
            if (iSupported[LoopVar] == 1)
            {
               Method = LoopVar;
               break;
            }
         }
      }
   }

   // Level 0 is no mipmapping and no texture filtering
   // Level 1 is no mipmapping and texture filtering
   // Level 2 is mipmapping and no texture filtering
   // Level 3 is mipmapping and texture filtering
   // Level 4 is mipmapping+filtering and no texture filtering
   // Level 5 is mipmapping+filtering and texture filtering
   switch (Method)
   {
   case 0:
      // Kill Filtering
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_POINT )", _r3d_hResult);
         return FALSE;
      }
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTFG_POINT )", _r3d_hResult);
         return FALSE;
      }
      // Kill MipMapping : RULE: ALWAYS ALTER MIPMAPPING OPTIONS BEFORE FILTERING OPTIONS!
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_NONE)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTFP_NONE )", _r3d_hResult);
         return FALSE;
      }
      _r3d_state.FilterMethod = Method;
      break;
   case 1:
      // Kill MipMapping : RULE: ALWAYS ALTER MIPMAPPING OPTIONS BEFORE FILTERING OPTIONS!
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_NONE)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTFP_NONE )", _r3d_hResult);
         return FALSE;
      }
      // Enable Filtering, if supported...
      if ((r3d_GetDeviceDesc()->dwFlags & D3DDD_TRICAPS) && (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR))
      {
         if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_LINEAR)) != D3D_OK)
         {
            _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
            return FALSE;
         }
         if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR)) != D3D_OK)
         {
            _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTFG_LINEAR )", _r3d_hResult);
            return FALSE;
         }
      }
      _r3d_state.FilterMethod = Method;
      break;
   case 2:
      // Kill Filtering
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_POINT )", _r3d_hResult);
         return FALSE;
      }
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTFG_POINT )", _r3d_hResult);
         return FALSE;
      }
      // Enable POINT MipMapping : RULE: ALWAYS ALTER MIPMAPPING OPTIONS BEFORE FILTERING OPTIONS!
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
         return FALSE;
      }
      _r3d_state.FilterMethod = Method;
      break;
   case 3:
      // Enable POINT MipMapping : RULE: ALWAYS ALTER MIPMAPPING OPTIONS BEFORE FILTERING OPTIONS!
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
         return FALSE;
      }
      // Enable Filtering, if supported...
      if ((r3d_GetDeviceDesc()->dwFlags & D3DDD_TRICAPS) && (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR))
      {
         if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_LINEAR)) != D3D_OK)
         {
            _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
            return FALSE;
         }
         if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR)) != D3D_OK)
         {
            _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTFG_LINEAR )", _r3d_hResult);
            return FALSE;
         }
      }
      _r3d_state.FilterMethod = Method;
      break;
   case 4:
      // Kill Filtering
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_POINT )", _r3d_hResult);
         return FALSE;
      }
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTFG_POINT )", _r3d_hResult);
         return FALSE;
      }
      // Enable LINEAR MipMapping : RULE: ALWAYS ALTER MIPMAPPING OPTIONS BEFORE FILTERING OPTIONS!
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_LINEAR)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
         return FALSE;
      }
      _r3d_state.FilterMethod = Method;
      break;
   case 5:
      // Enable LINEAR MipMapping : RULE: ALWAYS ALTER MIPMAPPING OPTIONS BEFORE FILTERING OPTIONS!
      if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_LINEAR)) != D3D_OK)
      {
         _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
         return FALSE;
      }
      // Enable Filtering, if supported...
      if ((r3d_GetDeviceDesc()->dwFlags & D3DDD_TRICAPS) && (r3d_GetDeviceDesc()->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR))
      {
         if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_LINEAR)) != D3D_OK)
         {
            _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTFN_LINEAR )", _r3d_hResult);
            return FALSE;
         }
         if ((_r3d_hResult = _r3d_Device->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR)) != D3D_OK)
         {
            _r3d_FormulateErrorString("r3d_SetFilteringAndMipMapOptions", "SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTFG_LINEAR )", _r3d_hResult);
            return FALSE;
         }
      }
      _r3d_state.FilterMethod = Method;
      break;
   }
   return TRUE;
}

BOOL R3D_STDCALL
r3d_SetBackgroundColor(float r, float g, float b)
{
   _r3d_BackGroundRed = r;
   _r3d_BackGroundGreen = g;
   _r3d_BackGroundBlue = b;

   return TRUE;
}

void R3D_STDCALL
r3d_GetVersion(int *major, int *tenths, int *hundredths)
{
   // 3.00 = first beta release
   // 3.01 = final beta release
   // 3.02 = final release
   // 3.03 = bug fixes
   // 3.04 = added support for more than 32 video modes
   *major = 3;
   *tenths = 0;
   *hundredths = 3;
}


LPDIRECTDRAWSURFACE4 R3D_STDCALL
r3d_DDSurfaceGet(int hDDSurface)
{
   return (_r3d_DDSurface[hDDSurface]->lpSurface);
}

BOOL R3D_STDCALL
r3d_DDSurfaceBlit(LPDIRECTDRAWSURFACE4 Src, LPDIRECTDRAWSURFACE4 Dst, int SrcWidth, int SrcHeight, int DstWidth, int DstHeight, int SrcMinx, int SrcMaxx, int SrcMiny, int SrcMaxy, int DstMinx, int DstMaxx, int DstMiny, int DstMaxy, BOOL bSrcColorKey, BOOL bDstColorKey)
{
   RECT rectSrc, rectDst;
   DWORD dwFlags;

   _R3D_BLITCLIP((int)SrcWidth, (int)SrcHeight, (int)DstWidth, (int)DstHeight, SrcMinx, SrcMaxx, SrcMiny, SrcMaxy, DstMinx, DstMaxx, DstMiny, DstMaxy);
   rectSrc.left = SrcMinx;
   rectSrc.top = SrcMiny;
   rectSrc.right = SrcMaxx + 1; // Rect's must be Inclusive
   rectSrc.bottom = SrcMaxy + 1; // Rect's must be Inclusive
   rectDst.left = DstMinx;
   rectDst.top = DstMiny;
   rectDst.right = DstMaxx + 1; // Rect's must be Inclusive
   rectDst.bottom = DstMaxy + 1; // Rect's must be Inclusive

   if ((rectDst.bottom - rectDst.top) == (rectSrc.bottom - rectSrc.top) &&
      (rectDst.right - rectDst.left) == (rectSrc.right - rectSrc.left))
   {
      // BltFast candidate
      dwFlags = DDBLTFAST_NOCOLORKEY;
      if (bSrcColorKey || bDstColorKey)
      {
         dwFlags = 0;
         if (bSrcColorKey)
            dwFlags |= DDBLTFAST_SRCCOLORKEY;
         if (bDstColorKey)
            dwFlags |= DDBLTFAST_DESTCOLORKEY;
      }
      dwFlags |= DDBLTFAST_WAIT;
      if (Dst->BltFast(DstMinx, DstMiny, Src, &rectSrc, dwFlags) == DD_OK)
      {
         //r3d_MessageBox("It worked!","HEY!",MB_OK);
         return TRUE;
      }
   }

   // We can fall through down to here if BltFast fails (both surfaces
   // are not in video memory) or if the blit was never a BltFast
   // candidate
   dwFlags = DDBLT_WAIT;
   if (bSrcColorKey)
      dwFlags |= DDBLT_KEYSRC;
   if (bDstColorKey)
      dwFlags |= DDBLT_KEYDEST;
   if ((_r3d_hResult = Dst->Blt(&rectDst, Src, &rectSrc, dwFlags, NULL)) != DD_OK)
   {
      _r3d_FormulateErrorString("r3d_DDSurfaceBlit", "Dst->Blt()", _r3d_hResult);
      return FALSE;
   }

   return TRUE;
}

// MemoryLocation = 0 = System Memory
// MemoryLocation = 1 = Display Memory
BOOL R3D_STDCALL
r3d_DDSurfaceCreateFromSurface(int *handle, int hSrc, int MemoryType)
{
   DDSURFACEDESC2 ddsd;
   unsigned long *SurfaceRGBA, dwColor, x, y;
   unsigned char r, g, b;
   int LoopVar, TmpHandle;

   for (LoopVar = 0;LoopVar < _R3D_MAX_DDSURFACES;LoopVar++)
   {
      if (_r3d_bDDSurfaceDoesNotExist[LoopVar] == TRUE)
      {
         // Allocate Surface Structure Memory
         TmpHandle = LoopVar;
         if (NULL == (_r3d_DDSurface[TmpHandle] = new _R3D_DDSURFACE))
         {
            _r3d_FormulateErrorString("r3d_DDSurfaceCreateFromSurface", "Can not allocate memory", DD_OK);
            return FALSE;
         }
         _r3d_DDSurface[TmpHandle]->lpSurface = NULL;

         // Allocate DirectDraw Surface
         _r3d_ProperlyInitDDSD(&ddsd);
         ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
         //ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
         if (MemoryType == 0)
            ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
         else if (MemoryType == 1)
            ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
         ddsd.dwWidth = _r3d_Surface[hSrc]->dwWidth;
         ddsd.dwHeight = _r3d_Surface[hSrc]->dwHeight;
         //memcpy( &ddsd.ddpfPixelFormat, &_r3d_gpCurrentDriver->pCurrentDevice->pCurrentMode->ddsd.ddpfPixelFormat, sizeof(_r3d_gpCurrentDriver->pCurrentDevice->pCurrentMode->ddsd.ddpfPixelFormat) );
         if ((_r3d_hResult = _r3d_DirectDraw4->CreateSurface(&ddsd, &_r3d_DDSurface[TmpHandle]->lpSurface, NULL)) != DD_OK)
         {
            _R3D_DELETE(_r3d_DDSurface[TmpHandle]);
            _r3d_FormulateErrorString("r3d_DDSurfaceCreateFromSurface", "_r3d_DirectDraw4->CreateSurface()", _r3d_hResult);
            return FALSE;
         }

         _r3d_bDDSurfaceDoesNotExist[TmpHandle] = FALSE;
         _r3d_DDSurface[TmpHandle]->dwWidth = _r3d_Surface[hSrc]->dwWidth;
         _r3d_DDSurface[TmpHandle]->dwHeight = _r3d_Surface[hSrc]->dwHeight;
         *handle = TmpHandle;
         break;
      }
   }

   // Copy _r3d_Surface to _r3d_DDSurface...
   //
   // Search on "Step 2.2: Set Up DirectDraw Surfaces" in DX docs
   //
   // "As when creating the primary surface, the pixel format for the
   // off-screen surface is assumed to be the same as the display mode
   // when it isn't provided in the surface description."
   //

   // Lock the surface
   _r3d_ProperlyInitDDSD(&ddsd);
   if ((_r3d_hResult = _r3d_DDSurface[TmpHandle]->lpSurface->Lock(NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) != DD_OK)
   {
      _R3D_RELEASE(_r3d_DDSurface[TmpHandle]->lpSurface);
      _R3D_DELETE(_r3d_DDSurface[TmpHandle]);
      _r3d_bDDSurfaceDoesNotExist[TmpHandle] = TRUE;
      _r3d_FormulateErrorString("r3d_DDSurfaceCreateFromSurface", "lpSurface->Lock", _r3d_hResult);
      return FALSE;
   }

   SurfaceRGBA = (unsigned long *)r3d_SurfaceGet(hSrc);
   for (y = 0;y < _r3d_DDSurface[TmpHandle]->dwHeight;y++)
   {
      for (x = 0;x < _r3d_DDSurface[TmpHandle]->dwWidth;x++)
      {
         dwColor = SurfaceRGBA[(y * (int)_r3d_Surface[hSrc]->dwWidth) + x];
         r = r3d_SurfaceColorGetR(dwColor);
         g = r3d_SurfaceColorGetG(dwColor);
         b = r3d_SurfaceColorGetB(dwColor);
         switch (_r3d_PixelFormatCode)
         {
         case 0:
            _r3d_Unsupported_SetPixelRGB0(ddsd.lpSurface, ddsd.lPitch, x, y, r, g, b);
            break;
         case 1:
            _r3d_Unsupported_SetPixelRGB1(ddsd.lpSurface, ddsd.lPitch, x, y, r, g, b);
            break;
         case 2:
            _r3d_Unsupported_SetPixelR2Safe(ddsd.lpSurface, ddsd.lPitch, x, y, r);
            _r3d_Unsupported_SetPixelG2Safe(ddsd.lpSurface, ddsd.lPitch, x, y, g);
            _r3d_Unsupported_SetPixelB2Safe(ddsd.lpSurface, ddsd.lPitch, x, y, b);
            break;
         case 3:
            _r3d_Unsupported_SetPixelR3Safe(ddsd.lpSurface, ddsd.lPitch, x, y, r);
            _r3d_Unsupported_SetPixelG3Safe(ddsd.lpSurface, ddsd.lPitch, x, y, g);
            _r3d_Unsupported_SetPixelB3Safe(ddsd.lpSurface, ddsd.lPitch, x, y, b);
            break;
         case 4:
            _r3d_Unsupported_SetPixelRGB4(ddsd.lpSurface, ddsd.lPitch, x, y, r, g, b);
            break;
         case 5:
            _r3d_Unsupported_SetPixelRGB5(ddsd.lpSurface, ddsd.lPitch, x, y, r, g, b);
            break;
         }
      }
   }

   // Unlock and leave
   _r3d_DDSurface[TmpHandle]->lpSurface->Unlock(NULL);

   return TRUE;
}

int R3D_STDCALL
r3d_DDSurfaceGetWidth(int handle)
{
   if (_r3d_bDDSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_DDSurfaceGetWidth", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   return (int)_r3d_DDSurface[handle]->dwWidth;
}

int R3D_STDCALL
r3d_DDSurfaceGetHeight(int handle)
{
   if (_r3d_bDDSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_DDSurfaceGetHeight", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   return (int)_r3d_DDSurface[handle]->dwHeight;
}

BOOL R3D_STDCALL
r3d_DDSurfaceDestroyAll(void)
{
   int LoopVar;

   // We loop through all allocated surfaces
   // and delete all.
   for (LoopVar = 0;LoopVar < _R3D_MAX_DDSURFACES;LoopVar++)
   {
      r3d_DDSurfaceDestroy(LoopVar); // Ignore errors!
   }

   return TRUE;
}

BOOL R3D_STDCALL
r3d_DDSurfaceDestroy(int handle)
{
   if (_r3d_bDDSurfaceDoesNotExist[handle] == TRUE)
   {
      _r3d_FormulateErrorString("r3d_DDSurfaceDestroy", "Invalid Surface Handle", DD_OK);
      return FALSE;
   }

   _R3D_RELEASE(_r3d_DDSurface[handle]->lpSurface);
   _R3D_DELETE(_r3d_DDSurface[handle]);

   _r3d_bDDSurfaceDoesNotExist[handle] = TRUE;

   return TRUE;
}