#ifdef JA2_PRECOMPILED_HEADERS
	#include "JA2 SGP ALL.H"
#elif defined( WIZ8_PRECOMPILED_HEADERS )
	#include "WIZ8 SGP ALL.H"
#else
	#include "DirectDraw Calls.h"
	#include <stdio.h>
	#include <stdlib.h>
	#include "debug.h"
	#if defined( JA2 ) || defined( UTIL )
		#include "video.h"
	#else
		#include "video2.h"
	#endif
	#include "himage.h"
	#include "vsurface.h"
	#include "vsurface_private.h"
	#include "video_private.h"
	#include "wcheck.h"
	#include "vobject_blitters.h"
#endif

extern void SetClippingRect(SGPRect *clip);
extern void GetClippingRect(SGPRect *clip);

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Video Surface SGP Module
//
// Second Revision: Dec 10, 1996, Andrew Emmons
//
///////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines
//
///////////////////////////////////////////////////////////////////////////////////////////////////

//
// This define is sent to CreateList SGP function. It dynamically re-sizes if
// the list gets larger
//

#define DEFAULT_NUM_REGIONS		5
#define DEFAULT_VIDEO_SURFACE_LIST_SIZE	10

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// LOCAL functions
//
///////////////////////////////////////////////////////////////////////////////////////////////////

BOOLEAN UpdateBackupSurface( HVSURFACE hVSurface );
BOOLEAN ClipReleatedSrcAndDestRectangles( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, RECT *DestRect, RECT *SrcRect );
BOOLEAN FillSurface( HVSURFACE hDestVSurface, blt_vs_fx *pBltFx );
BOOLEAN FillSurfaceRect( HVSURFACE hDestVSurface, blt_vs_fx *pBltFx );
BOOLEAN BltVSurfaceUsingDD( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, UINT32 fBltFlags, INT32 iDestX, INT32 iDestY, RECT *SrcRect );
BOOLEAN BltVSurfaceUsingDDBlt( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, UINT32 fBltFlags, INT32 iDestX, INT32 iDestY, RECT *SrcRect, RECT *DestRect );
BOOLEAN GetVSurfaceRect( HVSURFACE hVSurface, RECT *pRect);

void DeletePrimaryVideoSurfaces( );

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// LOCAL global variables
//
///////////////////////////////////////////////////////////////////////////////////////////////////

typedef struct VSURFACE_NODE
{
	HVSURFACE hVSurface;
	UINT32 uiIndex;
  struct VSURFACE_NODE *next, *prev;

	#ifdef SGP_VIDEO_DEBUGGING
		UINT8									*pName;
		UINT8									*pCode;
	#endif

}VSURFACE_NODE;

VSURFACE_NODE  *gpVSurfaceHead = NULL;
VSURFACE_NODE  *gpVSurfaceTail = NULL;
UINT32				guiVSurfaceIndex = 0;
UINT32				guiVSurfaceSize = 0;
UINT32				guiVSurfaceTotalAdded = 0;

#ifdef _DEBUG
enum
{
	DEBUGSTR_NONE,
	DEBUGSTR_SETVIDEOSURFACETRANSPARENCY,
	DEBUGSTR_ADDVIDEOSURFACEREGION,
	DEBUGSTR_GETVIDEOSURFACEDESCRIPTION,
	DEBUGSTR_BLTVIDEOSURFACE_DST,
	DEBUGSTR_BLTVIDEOSURFACE_SRC,
	DEBUGSTR_COLORFILLVIDEOSURFACEAREA,
	DEBUGSTR_SHADOWVIDEOSURFACERECT,
	DEBUGSTR_BLTSTRETCHVIDEOSURFACE_DST,
	DEBUGSTR_BLTSTRETCHVIDEOSURFACE_SRC,
	DEBUGSTR_DELETEVIDEOSURFACEFROMINDEX
};

UINT8 gubVSDebugCode = 0;

void CheckValidVSurfaceIndex( UINT32 uiIndex );
#endif

INT32				giMemUsedInSurfaces;


//OBSOLETE!!!!!!!!!
HLIST				ghVideoSurfaces = NULL;
//OBSOLETE!!!!!!!!!


HVSURFACE		ghPrimary = NULL;
HVSURFACE		ghBackBuffer = NULL;
HVSURFACE   ghFrameBuffer = NULL;
HVSURFACE   ghMouseBuffer = NULL;

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Video Surface Manager functions
//
///////////////////////////////////////////////////////////////////////////////////////////////////

BOOLEAN InitializeVideoSurfaceManager( )
{
	//Shouldn't be calling this if the video surface manager already exists.
	//Call shutdown first...
	Assert( !gpVSurfaceHead );
	Assert( !gpVSurfaceTail );
	RegisterDebugTopic(TOPIC_VIDEOSURFACE, "Video Surface Manager");
	gpVSurfaceHead = gpVSurfaceTail = NULL;

	giMemUsedInSurfaces = 0;

	// Create primary and backbuffer from globals
	if ( !SetPrimaryVideoSurfaces( ) )
	{
    DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_1, String( "Could not create primary surfaces" ) );
		return FALSE;
	}

	return TRUE ;
}

BOOLEAN ShutdownVideoSurfaceManager( )
{
	VSURFACE_NODE *curr;

  DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_0, "Shutting down the Video Surface manager");

	// Delete primary viedeo surfaces
	DeletePrimaryVideoSurfaces( );

	while( gpVSurfaceHead )
	{
		curr = gpVSurfaceHead;
		gpVSurfaceHead = gpVSurfaceHead->next;
		DeleteVideoSurface( curr->hVSurface );
		#ifdef SGP_VIDEO_DEBUGGING
			if( curr->pName )
				MemFree( curr->pName );
			if( curr->pCode )
				MemFree( curr->pCode );
		#endif
		MemFree( curr );
	}
	gpVSurfaceHead = NULL;
	gpVSurfaceTail = NULL;
	guiVSurfaceIndex = 0;
	guiVSurfaceSize = 0;
	guiVSurfaceTotalAdded = 0;
	UnRegisterDebugTopic(TOPIC_VIDEOSURFACE, "Video Objects");
	return TRUE;
}


BOOLEAN RestoreVideoSurfaces( )
{
	VSURFACE_NODE *curr;

  //
	// Loop through Video Surfaces and Restore
  //
	curr = gpVSurfaceTail;
	while( curr )
	{
		if( !RestoreVideoSurface( curr->hVSurface ) )
		{
			return FALSE;
		}
		curr = curr->prev;
	}
	return TRUE;
}


BOOLEAN AddStandardVideoSurface( VSURFACE_DESC *pVSurfaceDesc, UINT32 *puiIndex )
{

	HVSURFACE hVSurface;

	// Assertions
	Assert( puiIndex );
	Assert( pVSurfaceDesc );

	// Create video object
	hVSurface = CreateVideoSurface( pVSurfaceDesc );

	if( !hVSurface )
	{
		// Video Object will set error condition.
		return FALSE ;
	}

	// Set transparency to default
	SetVideoSurfaceTransparencyColor( hVSurface, FROMRGB( 0, 0, 0 ) );

	// Set into video object list
	if( gpVSurfaceHead )
	{ //Add node after tail
		gpVSurfaceTail->next = (VSURFACE_NODE*)MemAlloc( sizeof( VSURFACE_NODE ) );
		Assert( gpVSurfaceTail->next ); //out of memory?
		gpVSurfaceTail->next->prev = gpVSurfaceTail;
		gpVSurfaceTail->next->next = NULL;
		gpVSurfaceTail = gpVSurfaceTail->next;
	}
	else
	{ //new list
		gpVSurfaceHead = (VSURFACE_NODE*)MemAlloc( sizeof( VSURFACE_NODE ) );
		Assert( gpVSurfaceHead ); //out of memory?
		gpVSurfaceHead->prev = gpVSurfaceHead->next = NULL;
		gpVSurfaceTail = gpVSurfaceHead;
	}
	#ifdef SGP_VIDEO_DEBUGGING
		gpVSurfaceTail->pName = NULL;
		gpVSurfaceTail->pCode = NULL;
	#endif
	//Set the hVSurface into the node.
	gpVSurfaceTail->hVSurface = hVSurface;
	gpVSurfaceTail->uiIndex = guiVSurfaceIndex+=2;
	*puiIndex = gpVSurfaceTail->uiIndex;
	Assert( guiVSurfaceIndex < 0xfffffff0 ); //unlikely that we will ever use 2 billion VSurfaces!
	//We would have to create about 70 VSurfaces per second for 1 year straight to achieve this...
	guiVSurfaceSize++;
	guiVSurfaceTotalAdded++;

	return TRUE ;
}


BYTE *LockVideoSurface( UINT32 uiVSurface, UINT32 *puiPitch )
{
	VSURFACE_NODE *curr;

  //
  // Check if given backbuffer or primary buffer
  //
#ifdef JA2
  if ( uiVSurface == PRIMARY_SURFACE )
  {
    return LockPrimarySurface( puiPitch );
  }

  if ( uiVSurface == BACKBUFFER )
  {
    return LockBackBuffer( puiPitch );
  }
#endif

  if ( uiVSurface == FRAME_BUFFER )
  {
    return LockFrameBuffer( puiPitch );
  }

  if ( uiVSurface == MOUSE_BUFFER )
  {
    return LockMouseBuffer( puiPitch );
  }

  //
  // Otherwise, use list
  //

	curr = gpVSurfaceHead;
	while( curr )
	{
		if( curr->uiIndex == uiVSurface )
		{
			break;
		}
		curr = curr->next;
	}
	if( !curr )
	{
		return FALSE;
	}

  //
	// Lock buffer
  //

	return LockVideoSurfaceBuffer( curr->hVSurface, puiPitch );

}

void UnLockVideoSurface( UINT32 uiVSurface )
{
	VSURFACE_NODE *curr;

  //
  // Check if given backbuffer or primary buffer
  //
#ifdef JA2
  if ( uiVSurface == PRIMARY_SURFACE )
  {
    UnlockPrimarySurface();
    return;
  }

  if ( uiVSurface == BACKBUFFER )
  {
    UnlockBackBuffer();
    return;
  }
#endif

  if ( uiVSurface == FRAME_BUFFER )
  {
    UnlockFrameBuffer();
    return;
  }

  if ( uiVSurface == MOUSE_BUFFER )
  {
    UnlockMouseBuffer();
    return;
  }

	curr = gpVSurfaceHead;
	while( curr )
	{
		if( curr->uiIndex == uiVSurface )
		{
			break;
		}
		curr = curr->next;
	}
	if( !curr )
	{
    return;
  }

  //
	// unlock buffer
  //

	UnLockVideoSurfaceBuffer( curr->hVSurface );
}

BOOLEAN SetVideoSurfaceTransparency( UINT32 uiIndex, COLORVAL TransColor )
{
	HVSURFACE hVSurface;

  //
	// Get Video Surface
  //

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_SETVIDEOSURFACETRANSPARENCY;
	#endif
	CHECKF( GetVideoSurface( &hVSurface, uiIndex ) );

  //
	// Set transparency
  //

	SetVideoSurfaceTransparencyColor( hVSurface, TransColor );

	return( TRUE );
}

BOOLEAN AddVideoSurfaceRegion( UINT32 uiIndex, VSURFACE_REGION *pNewRegion )
{
	HVSURFACE hVSurface;

  //
	// Get Video Surface
  //

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_ADDVIDEOSURFACEREGION;
	#endif
	CHECKF( GetVideoSurface( &hVSurface, uiIndex ) );

  //
	// Add Region
  //

	CHECKF( AddVSurfaceRegion( hVSurface, pNewRegion ) );

	return( TRUE );

}

BOOLEAN GetVideoSurfaceDescription( UINT32 uiIndex, UINT16 *usWidth, UINT16 *usHeight, UINT8 *ubBitDepth )
{
	HVSURFACE hVSurface;

	Assert( usWidth != NULL );
	Assert( usHeight != NULL );
	Assert( ubBitDepth != NULL );

  //
	// Get Video Surface
  //

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_GETVIDEOSURFACEDESCRIPTION;
	#endif
	CHECKF( GetVideoSurface( &hVSurface, uiIndex ) );

	*usWidth = hVSurface->usWidth;
	*usHeight = hVSurface->usHeight;
	*ubBitDepth = hVSurface->ubBitDepth;

	return TRUE;
}

BOOLEAN GetVideoSurface( HVSURFACE *hVSurface, UINT32 uiIndex )
{
	VSURFACE_NODE *curr;

	#ifdef _DEBUG
		CheckValidVSurfaceIndex( uiIndex );
	#endif

  if ( uiIndex == PRIMARY_SURFACE )
  {
    *hVSurface = ghPrimary;
		return TRUE;
  }

  if ( uiIndex == BACKBUFFER )
  {
    *hVSurface = ghBackBuffer;
		return TRUE;
  }

  if ( uiIndex == FRAME_BUFFER )
  {
    *hVSurface = ghFrameBuffer;
		return TRUE;
  }

  if ( uiIndex == MOUSE_BUFFER )
  {
    *hVSurface = ghMouseBuffer;
		return TRUE;
  }

	curr = gpVSurfaceHead;
	while( curr )
	{
		if( curr->uiIndex == uiIndex )
		{
			*hVSurface = curr->hVSurface;
			return TRUE;
		}
		curr = curr->next;
	}
  return FALSE;
}

BOOLEAN SetPrimaryVideoSurfaces( )
{
	LPDIRECTDRAWSURFACE2 pSurface;

	// Delete surfaces if they exist
	DeletePrimaryVideoSurfaces( );

  //
	// Get Primary surface
  //
#ifdef JA2
	pSurface = GetPrimarySurfaceObject();
	CHECKF( pSurface != NULL );

	ghPrimary = CreateVideoSurfaceFromDDSurface( pSurface );
	CHECKF( ghPrimary != NULL );

  //
	// Get Backbuffer surface
  //

	pSurface = GetBackBufferObject( );
	CHECKF( pSurface != NULL );

	ghBackBuffer = CreateVideoSurfaceFromDDSurface( pSurface );
	CHECKF( ghBackBuffer != NULL );

  //
	// Get mouse buffer surface
  //
	pSurface = GetMouseBufferObject( );
	CHECKF( pSurface != NULL );

	ghMouseBuffer = CreateVideoSurfaceFromDDSurface( pSurface );
	CHECKF( ghMouseBuffer != NULL );

#endif

  //
	// Get frame buffer surface
  //

	pSurface = GetFrameBufferObject( );
	CHECKF( pSurface != NULL );

	ghFrameBuffer = CreateVideoSurfaceFromDDSurface( pSurface );
	CHECKF( ghFrameBuffer != NULL );


	return( TRUE );
}

void DeletePrimaryVideoSurfaces( )
{
  //
	// If globals are not null, delete them
  //

	if ( ghPrimary != NULL )
	{
		DeleteVideoSurface( ghPrimary );
		ghPrimary = NULL;
	}

	if ( ghBackBuffer != NULL )
	{
		DeleteVideoSurface( ghBackBuffer );
		ghBackBuffer = NULL;
	}

  if ( ghFrameBuffer != NULL )
	{
		DeleteVideoSurface( ghFrameBuffer );
		ghFrameBuffer = NULL;
	}

	if ( ghMouseBuffer != NULL )
	{
		DeleteVideoSurface( ghMouseBuffer );
		ghMouseBuffer = NULL;
	}

}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Given an index to the dest and src vobject contained in our private VSurface list
// Based on flags, blit accordingly
// There are two types, a BltFast and a Blt. BltFast is 10% faster, uses no
// clipping lists
//
///////////////////////////////////////////////////////////////////////////////////////////////////

BOOLEAN BltVideoSurface(UINT32 uiDestVSurface, UINT32 uiSrcVSurface, UINT16 usRegionIndex, INT32 iDestX, INT32 iDestY, UINT32 fBltFlags, blt_vs_fx *pBltFx )
{

	HVSURFACE	hDestVSurface;
	HVSURFACE	hSrcVSurface;

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_BLTVIDEOSURFACE_DST;
	#endif
	if( !GetVideoSurface( &hDestVSurface, uiDestVSurface ) )
	{
		return FALSE;
	}
	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_BLTVIDEOSURFACE_SRC;
	#endif
	if( !GetVideoSurface( &hSrcVSurface, uiSrcVSurface ) )
	{
		return FALSE;
	}
	if ( !BltVideoSurfaceToVideoSurface( hDestVSurface, hSrcVSurface, usRegionIndex, iDestX, iDestY, fBltFlags, pBltFx ) )
	{ // VO Blitter will set debug messages for error conditions
		return FALSE ;
	}
	return TRUE ;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Fills an rectangular area with a specified color value.
//
///////////////////////////////////////////////////////////////////////////////////////////////////

BOOLEAN ColorFillVideoSurfaceArea(UINT32 uiDestVSurface, INT32 iDestX1, INT32 iDestY1, INT32 iDestX2, INT32 iDestY2, UINT16 Color16BPP)
{
	blt_vs_fx BltFx;
	HVSURFACE	hDestVSurface;
	SGPRect Clip;

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_COLORFILLVIDEOSURFACEAREA;
	#endif
	if( !GetVideoSurface( &hDestVSurface, uiDestVSurface ) )
	{
		return FALSE;
	}

	BltFx.ColorFill = Color16BPP;
	BltFx.DestRegion = 0;

  //
	// Clip fill region coords
  //

	GetClippingRect(&Clip);

	if(iDestX1 < Clip.iLeft)
		iDestX1 = Clip.iLeft;

	if(iDestX1 > Clip.iRight)
		return(FALSE);

	if(iDestX2 > Clip.iRight)
		iDestX2 = Clip.iRight;

	if(iDestX2 < Clip.iLeft)
		return(FALSE);

	if(iDestY1 < Clip.iTop)
		iDestY1 = Clip.iTop;

	if(iDestY1 > Clip.iBottom)
		return(FALSE);

	if(iDestY2 > Clip.iBottom)
		iDestY2 = Clip.iBottom;

	if(iDestY2 < Clip.iTop)
		return(FALSE);

	if((iDestX2 <= iDestX1) || (iDestY2 <= iDestY1))
		return(FALSE);

	BltFx.SrcRect.iLeft =	BltFx.FillRect.iLeft = iDestX1;
	BltFx.SrcRect.iTop = BltFx.FillRect.iTop = iDestY1;
	BltFx.SrcRect.iRight = BltFx.FillRect.iRight = iDestX2;
	BltFx.SrcRect.iBottom = BltFx.FillRect.iBottom = iDestY2;

	return( FillSurfaceRect( hDestVSurface, &BltFx ) );
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Fills an rectangular area with a specified image value.
//
///////////////////////////////////////////////////////////////////////////////////////////////////

BOOLEAN ImageFillVideoSurfaceArea(UINT32 uiDestVSurface, INT32 iDestX1, INT32 iDestY1, INT32 iDestX2, INT32 iDestY2, HVOBJECT BkgrndImg, UINT16 Index, INT16 Ox, INT16 Oy)
{
	INT16 xc,yc,hblits,wblits,aw,pw,ah,ph,w,h,xo,yo;
	ETRLEObject		*pTrav;
	SGPRect NewClip,OldClip;

	pTrav = &(BkgrndImg->pETRLEObject[Index]);
	ph = (INT16)(pTrav->usHeight+pTrav->sOffsetY);
	pw = (INT16)(pTrav->usWidth+pTrav->sOffsetX);

	ah = (INT16)(iDestY2-iDestY1);
	aw = (INT16)(iDestX2-iDestX1);

	Ox %= pw;
	Oy %= ph;

	if(Ox>0)
		Ox -= pw;
	xo = (-Ox)%pw;

	if(Oy>0)
		Oy -= ph;
	yo = (-Oy)%ph;

	if(Ox<0)
		xo = (-Ox)%pw;
	else
	{
		xo = pw-(Ox%pw);
		Ox -= pw;
	}

	if(Oy<0)
		yo = (-Oy)%ph;
	else
	{
		yo = ph-(Oy%pw);
		Oy -= ph;
	}

	hblits = ((ah+yo)/ph) + (((ah+yo)%ph)?1:0);
	wblits = ((aw+xo)/pw) + (((aw+xo)%pw)?1:0);

	if((hblits==0) || (wblits==0))
		return(FALSE);

  //
	// Clip fill region coords
  //

	GetClippingRect(&OldClip);

	NewClip.iLeft=iDestX1;
	NewClip.iTop=iDestY1;
	NewClip.iRight=iDestX2;
	NewClip.iBottom=iDestY2;

	if(NewClip.iLeft < OldClip.iLeft)
		NewClip.iLeft = OldClip.iLeft;

	if(NewClip.iLeft > OldClip.iRight)
		return(FALSE);

	if(NewClip.iRight > OldClip.iRight)
		NewClip.iRight = OldClip.iRight;

	if(NewClip.iRight < OldClip.iLeft)
		return(FALSE);

	if(NewClip.iTop < OldClip.iTop)
		NewClip.iTop = OldClip.iTop;

	if(NewClip.iTop > OldClip.iBottom)
		return(FALSE);

	if(NewClip.iBottom > OldClip.iBottom)
		NewClip.iBottom = OldClip.iBottom;

	if(NewClip.iBottom < OldClip.iTop)
		return(FALSE);

	if((NewClip.iRight <= NewClip.iLeft) || (NewClip.iBottom <= NewClip.iTop))
		return(FALSE);

	SetClippingRect(&NewClip);

	yc = (INT16)iDestY1;
	for(h=0;h<hblits;h++)
	{
		xc = (INT16)iDestX1;
		for(w=0;w<wblits;w++)
		{
			BltVideoObject(uiDestVSurface, BkgrndImg, Index, xc+Ox, yc+Oy, VO_BLT_SRCTRANSPARENCY, NULL);
			xc += pw;
		}
		yc += ph;
	}

	SetClippingRect(&OldClip);
	return(TRUE);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Video Surface Manipulation Functions
//
///////////////////////////////////////////////////////////////////////////////////////////////////

HVSURFACE CreateVideoSurface( VSURFACE_DESC *VSurfaceDesc )
{
	LPDIRECTDRAW2				lpDD2Object;
  DDSURFACEDESC				SurfaceDescription;
	DDPIXELFORMAT			  PixelFormat;
	LPDIRECTDRAWSURFACE  lpDDS;
	LPDIRECTDRAWSURFACE2  lpDDS2;
	HVSURFACE						hVSurface;
	HIMAGE							hImage;
	SGPRect							tempRect;
	UINT16							usHeight;
	UINT16							usWidth;
	UINT8								ubBitDepth;
	UINT32							fMemUsage;

//#ifdef JA2
	UINT32							uiRBitMask;
	UINT32							uiGBitMask;
	UINT32							uiBBitMask;
//#endif

	//Clear the memory
	memset( &SurfaceDescription, 0, sizeof( DDSURFACEDESC ) );

  //
	// Get Direct Draw Object
  //

	lpDD2Object = GetDirectDraw2Object( );

  //
	// The description structure contains memory usage flag
  //
#ifdef JA2
	fMemUsage = VSurfaceDesc->fCreateFlags;
#else
	fMemUsage = VSURFACE_SYSTEM_MEM_USAGE;
#endif

  //
	// Check creation options
  //

	do
	{
    //
		// Check if creating from file
    //

		if ( VSurfaceDesc->fCreateFlags & VSURFACE_CREATE_FROMFILE )
		{
      //
			// Create himage object from file
      //

			hImage = CreateImage( VSurfaceDesc->ImageFile, IMAGE_ALLIMAGEDATA );

			if ( hImage == NULL )
			{
					DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_2, "Invalid Image Filename given" );
					return( NULL );
			}

      //
			// Set values from himage
      //
			usHeight = hImage->usHeight;
			usWidth = hImage->usWidth;
			ubBitDepth = hImage->ubBitDepth;
			break;
		}


    //
		// If here, no special options given,
		// Set values from given description structure
    //

		usHeight = VSurfaceDesc->usHeight;
		usWidth = VSurfaceDesc->usWidth;
		ubBitDepth = VSurfaceDesc->ubBitDepth;

	}	while( FALSE );

  //
	// Assertions
  //

	Assert ( usHeight > 0 );
	Assert ( usWidth > 0 );

  //
	// Setup Direct Draw Description
	// First do Pixel Format
  //

	memset( &PixelFormat, 0, sizeof( PixelFormat ) );
	PixelFormat.dwSize = sizeof( DDPIXELFORMAT );

	switch( ubBitDepth )
	{

		case 8:

			PixelFormat.dwFlags = DDPF_RGB | DDPF_PALETTEINDEXED8;
			PixelFormat.dwRGBBitCount = 8;
			break;

		case 16:

			PixelFormat.dwFlags = DDPF_RGB;
			PixelFormat.dwRGBBitCount = 16;

      //
			// Get current Pixel Format from DirectDraw
      //

// We're using pixel formats too -- DB/Wiz

//#ifdef JA2
			CHECKF( GetPrimaryRGBDistributionMasks( &uiRBitMask, &uiGBitMask, &uiBBitMask ) );
			PixelFormat.dwRBitMask = uiRBitMask;
			PixelFormat.dwGBitMask = uiGBitMask;
			PixelFormat.dwBBitMask = uiBBitMask;
//#else
//			PixelFormat.dwRBitMask = 0xf800;
//			PixelFormat.dwGBitMask = 0x7e0;
//			PixelFormat.dwBBitMask = 0x1f;
//#endif
			break;

		default:

      //
			// If Here, an invalid format was given
      //

			DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_2, "Invalid BPP value, can only be 8 or 16." );
			return( FALSE );
	}

	 SurfaceDescription.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;

  //
	// Do memory description, based on specified flags
  //

	do
	{
		if ( fMemUsage & VSURFACE_DEFAULT_MEM_USAGE )
		{
			SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
			break;
		}
		if ( fMemUsage & VSURFACE_VIDEO_MEM_USAGE )
		{
			SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
			break;
		}

		if ( fMemUsage & VSURFACE_SYSTEM_MEM_USAGE )
		{
			SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
			break;
		}

    //
		// Once here, no mem flags were given, use default
    //

		SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

	} while( FALSE );

  //
	// Set other, common structure elements
  //

  SurfaceDescription.dwSize = sizeof(DDSURFACEDESC);
  SurfaceDescription.dwWidth = usWidth;
  SurfaceDescription.dwHeight = usHeight;
	SurfaceDescription.ddpfPixelFormat = PixelFormat;

  //
	// Create Surface
  //

	DDCreateSurface (	lpDD2Object, &SurfaceDescription, &lpDDS, &lpDDS2 );

  //
	// Allocate memory for Video Surface data and initialize
  //

	hVSurface = MemAlloc( sizeof( SGPVSurface ) );
	memset( hVSurface, 0, sizeof( SGPVSurface ) );
	CHECKF( hVSurface != NULL );

	hVSurface->usHeight					= usHeight;
	hVSurface->usWidth						= usWidth;
	hVSurface->ubBitDepth				= ubBitDepth;
	hVSurface->pSurfaceData1			= (PTR)lpDDS;
	hVSurface->pSurfaceData			= (PTR)lpDDS2;
	hVSurface->pSavedSurfaceData1 = NULL;
	hVSurface->pSavedSurfaceData = NULL;
	hVSurface->pPalette					= NULL;
	hVSurface->p16BPPPalette			= NULL;
	hVSurface->TransparentColor	= FROMRGB( 0, 0, 0 );
	hVSurface->RegionList				= CreateList( DEFAULT_NUM_REGIONS, sizeof( VSURFACE_REGION ) );
	hVSurface->fFlags						= 0;
	hVSurface->pClipper					= NULL;

  //
	// Determine memory and other attributes of newly created surface
  //

	DDGetSurfaceDescription ( lpDDS2, &SurfaceDescription );

  //
	// Fail if create tried for video but it's in system
  //

	if ( VSurfaceDesc->fCreateFlags & VSURFACE_VIDEO_MEM_USAGE && SurfaceDescription.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY )
	{
    //
		// Return failure due to not in video
    //

		DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed to create Video Surface in video memory" ) );
		DDReleaseSurface ( &lpDDS, &lpDDS2 );
		MemFree( hVSurface );
		return( NULL );
	}

  //
	// Look for system memory
  //

	if ( SurfaceDescription.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY )
	{
		hVSurface->fFlags |= VSURFACE_SYSTEM_MEM_USAGE;
	}

  //
	// Look for video memory
  //

	if ( SurfaceDescription.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY )
	{
		hVSurface->fFlags |= VSURFACE_VIDEO_MEM_USAGE;
	}

  //
	// If in video memory, create backup surface
  //

	if ( hVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE )
	{
		SurfaceDescription.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
		SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
		SurfaceDescription.dwSize = sizeof(DDSURFACEDESC);
		SurfaceDescription.dwWidth = usWidth;
		SurfaceDescription.dwHeight = usHeight;
		SurfaceDescription.ddpfPixelFormat = PixelFormat;

    //
		// Create Surface
    //

		DDCreateSurface (	lpDD2Object, &SurfaceDescription, &lpDDS, &lpDDS2 );

    //
		// Save surface to backup
    //

		hVSurface->pSavedSurfaceData1 = lpDDS;
		hVSurface->pSavedSurfaceData = lpDDS2;
	}

  //
	// Initialize surface with hImage , if given
  //

	if ( VSurfaceDesc->fCreateFlags & VSURFACE_CREATE_FROMFILE )
	{
		tempRect.iLeft = 0;
		tempRect.iTop = 0;
		tempRect.iRight = hImage->usWidth-1;
		tempRect.iBottom = hImage->usHeight-1;
		SetVideoSurfaceDataFromHImage( hVSurface, hImage, 0, 0, &tempRect );

    //
		// Set palette from himage
    //

		if ( hImage->ubBitDepth == 8 )
		{
			SetVideoSurfacePalette( hVSurface, hImage->pPalette );
		}

    //
		// Delete himage object
    //

		DestroyImage( hImage );
	}

  //
	// All is well
  //

	hVSurface->usHeight					= usHeight;
	hVSurface->usWidth						= usWidth;
	hVSurface->ubBitDepth				= ubBitDepth;

	giMemUsedInSurfaces += ( hVSurface->usHeight * hVSurface->usWidth * ( hVSurface->ubBitDepth / 8 ) );

  DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_3, String("Success in Creating Video Surface" ) );

	return( hVSurface );
}

///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Called when surface is lost, for the most part called by utility functions
//
///////////////////////////////////////////////////////////////////////////////////////////////////

BOOLEAN RestoreVideoSurface( HVSURFACE hVSurface )
{
	LPDIRECTDRAWSURFACE2		lpDDSurface;
	LPDIRECTDRAWSURFACE2		lpBackupDDSurface;
	RECT										aRect;

	Assert( hVSurface != NULL );

  //
	// Restore is only for VIDEO MEMORY - should check if VIDEO and QUIT if not
  //

	if ( !( hVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE ) )
	{
    //
		// No second surfaace has been allocated, return failure
    //

	  DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String("Failed to restore Video Surface surface" ) );
		return( FALSE );
	}

  //
	// Check for valid secondary surface
  //

	if ( hVSurface->pSavedSurfaceData1 == NULL )
	{
    //
		// No secondary surface available
    //

	  DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String("Failure in retoring- no secondary surface found" ) );
		return( FALSE );
	}

	// Restore primary surface
	lpDDSurface = ( LPDIRECTDRAWSURFACE2 )hVSurface->pSurfaceData;
	DDRestoreSurface( lpDDSurface );

	// Blit backup surface data into primary
	lpBackupDDSurface = ( LPDIRECTDRAWSURFACE2 )hVSurface->pSavedSurfaceData;

	aRect.top = (int)0;
	aRect.left = (int)0;
	aRect.bottom = (int)hVSurface->usHeight;
	aRect.right = (int)hVSurface->usWidth;

	DDBltFastSurface( (LPDIRECTDRAWSURFACE2)hVSurface->pSavedSurfaceData, 0, 0, (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData, &aRect, DDBLTFAST_NOCOLORKEY );

	return( TRUE );
}


// Lock must be followed by release
// Pitch MUST be used for all width calculations ( Pitch is in bytes )
// The time between Locking and unlocking must be minimal
BYTE *LockVideoSurfaceBuffer( HVSURFACE hVSurface, UINT32 *pPitch )
{
	DDSURFACEDESC SurfaceDescription;

	// Assertions
	if ( hVSurface == NULL )
	{
		int i = 0;
	}


	Assert( hVSurface != NULL );
	Assert( pPitch != NULL );

#ifndef JA2
	if(hVSurface==ghFrameBuffer)
		return(LockFrameBuffer(pPitch));
#endif

	DDLockSurface( (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData, NULL, &SurfaceDescription, 0, NULL);

	*pPitch = SurfaceDescription.lPitch;

	return SurfaceDescription.lpSurface;
}

void UnLockVideoSurfaceBuffer( HVSURFACE hVSurface )
{
	Assert( hVSurface != NULL );

#ifndef JA2
	if(hVSurface==ghFrameBuffer)
	{
		UnlockFrameBuffer();
		return;
	}
#endif

	DDUnlockSurface( (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData, NULL );

	// Copy contents if surface is in video
	if ( hVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE && !hVSurface->fFlags & VSURFACE_RESERVED_SURFACE )
	{
		UpdateBackupSurface( hVSurface );
	}
}

// Given an HIMAGE object, blit imagery into existing Video Surface. Can be from 8->16 BPP
BOOLEAN SetVideoSurfaceDataFromHImage( HVSURFACE hVSurface, HIMAGE hImage, UINT16 usX, UINT16 usY, SGPRect *pSrcRect )
{
	BYTE		*pDest;
	UINT32	fBufferBPP = 0;
	UINT32  uiPitch;
	UINT16  usEffectiveWidth;
	SGPRect	aRect;

	// Assertions
	Assert( hVSurface != NULL );
	Assert( hImage != NULL );

	// Get Size of hImage and determine if it can fit
	CHECKF( hImage->usWidth  >= hVSurface->usWidth );
	CHECKF( hImage->usHeight >= hVSurface->usHeight );

	// Check BPP and see if they are the same
	if ( hImage->ubBitDepth != hVSurface->ubBitDepth )
	{
		// They are not the same, but we can go from 8->16 without much cost
		if ( hImage->ubBitDepth == 8 && hVSurface->ubBitDepth == 16 )
		{
			fBufferBPP = BUFFER_16BPP;
		}
	}
	else
	{
		// Set buffer BPP
		switch ( hImage->ubBitDepth )
		{
			case 8:

				fBufferBPP = BUFFER_8BPP;
				break;

			case 16:

				fBufferBPP = BUFFER_16BPP;
				break;
		}

	}

	Assert( fBufferBPP != 0 );

	// Get surface buffer data
	pDest = LockVideoSurfaceBuffer( hVSurface, &uiPitch );

	// Effective width ( in PIXELS ) is Pitch ( in bytes ) converted to pitch ( IN PIXELS )
	usEffectiveWidth = (UINT16)( uiPitch / ( hVSurface->ubBitDepth / 8 ) );

	CHECKF( pDest != NULL );

	// Blit Surface
	// If rect is NULL, use entrie image size
	if ( pSrcRect == NULL )
	{
		aRect.iLeft = 0;
		aRect.iTop = 0;
		aRect.iRight = hImage->usWidth;
		aRect.iBottom = hImage->usHeight;
	}
	else
	{
		aRect.iLeft = pSrcRect->iLeft;
		aRect.iTop = pSrcRect->iTop;
		aRect.iRight = pSrcRect->iRight;
		aRect.iBottom = pSrcRect->iBottom;
	}

	// This HIMAGE function will transparently copy buffer
	if ( !CopyImageToBuffer( hImage, fBufferBPP, pDest, usEffectiveWidth, hVSurface->usHeight, usX, usY, &aRect ) )
	{
    DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Error Occured Copying HIMAGE to HVSURFACE" ));
		UnLockVideoSurfaceBuffer( hVSurface );
		return( FALSE );
	}

	// All is OK
	UnLockVideoSurfaceBuffer( hVSurface );

	return( TRUE );

}

// Palette setting is expensive, need to set both DDPalette and create 16BPP palette
BOOLEAN SetVideoSurfacePalette( HVSURFACE hVSurface, SGPPaletteEntry *pSrcPalette )
{

	Assert( hVSurface != NULL );

	// Create palette object if not already done so
	if ( hVSurface->pPalette == NULL )
	{
		DDCreatePalette( GetDirectDraw2Object(), (DDPCAPS_8BIT | DDPCAPS_ALLOW256), (LPPALETTEENTRY)(&pSrcPalette[0]), &((LPDIRECTDRAWPALETTE)hVSurface->pPalette), NULL);

		// Set into surface
		//DDSetSurfacePalette( (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData, (LPDIRECTDRAWPALETTE)hVSurface->pPalette );

	}
	else
	{
		// Just Change entries
		DDSetPaletteEntries( (LPDIRECTDRAWPALETTE)hVSurface->pPalette, 0, 0, 256, ( PALETTEENTRY*)pSrcPalette );
	}

	// Delete 16BPP Palette if one exists
	if ( hVSurface->p16BPPPalette != NULL )
	{
		MemFree( hVSurface->p16BPPPalette );
		hVSurface->p16BPPPalette = NULL;
	}

	// Create 16BPP Palette
	hVSurface->p16BPPPalette = Create16BPPPalette( pSrcPalette );

  DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_3, String("Video Surface Palette change successfull" ));
	return( TRUE );
}

// Transparency needs to take RGB value and find best fit and place it into DD Surface
// colorkey value.
BOOLEAN SetVideoSurfaceTransparencyColor( HVSURFACE hVSurface, COLORVAL TransColor )
{
  DDCOLORKEY		ColorKey;
  DWORD					fFlags = CLR_INVALID;
	LPDIRECTDRAWSURFACE2	lpDDSurface;

	// Assertions
	Assert( hVSurface != NULL );

	//Set trans color into Video Surface
	hVSurface->TransparentColor = TransColor;

	// Get surface pointer
	lpDDSurface = (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData;
	CHECKF( lpDDSurface != NULL );

	// Get right pixel format, based on bit depth

	switch( hVSurface->ubBitDepth )
	{
			case 8:

				// Use color directly
				ColorKey.dwColorSpaceLowValue  = TransColor;
				ColorKey.dwColorSpaceHighValue = TransColor;
				break;

			case 16:

				fFlags = Get16BPPColor( TransColor );

				//fFlags now contains our closest match
				ColorKey.dwColorSpaceLowValue  = fFlags;
				ColorKey.dwColorSpaceHighValue = ColorKey.dwColorSpaceLowValue;
				break;

	}

	DDSetSurfaceColorKey( lpDDSurface, DDCKEY_SRCBLT, &ColorKey);

	return( TRUE );
}


BOOLEAN GetVSurfacePaletteEntries( HVSURFACE hVSurface, SGPPaletteEntry *pPalette )
{
	CHECKF( hVSurface->pPalette != NULL );

	DDGetPaletteEntries( (LPDIRECTDRAWPALETTE)hVSurface->pPalette, 0, 0, 256, (PALETTEENTRY*)pPalette );

	return( TRUE );
}


BOOLEAN DeleteVideoSurfaceFromIndex( UINT32 uiIndex )
{
	VSURFACE_NODE *curr;

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_DELETEVIDEOSURFACEFROMINDEX;
		CheckValidVSurfaceIndex( uiIndex );
	#endif

	curr = gpVSurfaceHead;
	while( curr )
	{
		if( curr->uiIndex == uiIndex )
		{ //Found the node, so detach it and delete it.

			//Deallocate the memory for the video surface
			DeleteVideoSurface( curr->hVSurface );

			if( curr == gpVSurfaceHead )
			{ //Advance the head, because we are going to remove the head node.
				gpVSurfaceHead = gpVSurfaceHead->next;
			}
			if( curr == gpVSurfaceTail )
			{ //Back up the tail, because we are going to remove the tail node.
				gpVSurfaceTail = gpVSurfaceTail->prev;
			}
			//Detach the node from the vsurface list
			if( curr->next )
			{ //Make the prev node point to the next
				curr->next->prev = curr->prev;
			}
			if( curr->prev )
			{ //Make the next node point to the prev
				curr->prev->next = curr->next;
			}
			//The node is now detached.  Now deallocate it.

			#ifdef SGP_VIDEO_DEBUGGING
				if( curr->pName )
				{
					MemFree( curr->pName );
				}
				if( curr->pCode )
				{
					MemFree( curr->pCode );
				}
			#endif

			MemFree( curr );
			guiVSurfaceSize--;
			return TRUE;
		}
		curr = curr->next;
	}
	return FALSE;
}


// Deletes all palettes, surfaces and region data
BOOLEAN DeleteVideoSurface( HVSURFACE hVSurface )
{
	LPDIRECTDRAWSURFACE2	lpDDSurface;

	// Assertions
	CHECKF( hVSurface != NULL );

	// Release palette
	if ( hVSurface->pPalette != NULL )
	{
		DDReleasePalette( (LPDIRECTDRAWPALETTE)hVSurface->pPalette );
		hVSurface->pPalette = NULL;
	}

	//if ( hVSurface->pClipper != NULL )
	//{
		// Release Clipper
	//	DDReleaseClipper( (LPDIRECTDRAWCLIPPER)hVSurface->pClipper );
	//}

	// Get surface pointer
	lpDDSurface = (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData;

	// Release surface
	if ( hVSurface->pSurfaceData1 != NULL )
	{
		DDReleaseSurface( &(LPDIRECTDRAWSURFACE)hVSurface->pSurfaceData1, &lpDDSurface );
	}

	// Release backup surface
	if ( hVSurface->pSavedSurfaceData != NULL )
	{
		DDReleaseSurface( &(LPDIRECTDRAWSURFACE)hVSurface->pSavedSurfaceData1,
								&(LPDIRECTDRAWSURFACE2)hVSurface->pSavedSurfaceData );
	}

	// Release region data
	DeleteList( hVSurface->RegionList );

	//If there is a 16bpp palette, free it
	if( hVSurface->p16BPPPalette != NULL )
	{
		MemFree( hVSurface->p16BPPPalette );
		hVSurface->p16BPPPalette = NULL;
	}

	giMemUsedInSurfaces -= ( hVSurface->usHeight * hVSurface->usWidth * ( hVSurface->ubBitDepth / 8 ) );

	// Release object
	MemFree( hVSurface );

	return( TRUE );
}

// ********************************************************
//
// Clipper manipulation functions
//
// ********************************************************

BOOLEAN SetClipList( HVSURFACE hVSurface, SGPRect *RegionData, UINT16 usNumRegions )
{
	RGNDATA							*pRgnData;
	UINT16							cnt;
	RECT								aRect;
	LPDIRECTDRAW2				lpDD2Object;

	// Get Direct Draw Object
	lpDD2Object = GetDirectDraw2Object( );

	// Assertions
	Assert( hVSurface != NULL );
	Assert( RegionData != NULL );

	// Varifications
	CHECKF( usNumRegions > 0 );

	// If Clipper already created, release
	if ( hVSurface->pClipper != NULL )
	{
		// Release Clipper
		DDReleaseClipper( (LPDIRECTDRAWCLIPPER)hVSurface->pClipper );
	}

	// Create Clipper Object
	DDCreateClipper( lpDD2Object, 0, &((LPDIRECTDRAWCLIPPER)hVSurface->pClipper) );

	// Allocate region data
	pRgnData = ( LPRGNDATA )MemAlloc( sizeof( RGNDATAHEADER) + ( usNumRegions * sizeof( RECT ) ) );
	CHECKF( pRgnData );

	// Setup header
	pRgnData->rdh.dwSize = sizeof( RGNDATA );
	pRgnData->rdh.iType = RDH_RECTANGLES;
	pRgnData->rdh.nCount = usNumRegions;
	pRgnData->rdh.nRgnSize = usNumRegions * sizeof( RECT );
	pRgnData->rdh.rcBound.top  = 0;
	pRgnData->rdh.rcBound.left  = 0;
	pRgnData->rdh.rcBound.bottom  = (int)hVSurface->usHeight;
	pRgnData->rdh.rcBound.right = (int)hVSurface->usWidth;

	// Copy rectangles into region
	for ( cnt = 0; cnt < usNumRegions; cnt++ )
	{
		aRect.top = (UINT32)RegionData[ cnt ].iTop;
		aRect.left = (UINT32)RegionData[ cnt ].iLeft;
		aRect.bottom = (UINT32)RegionData[ cnt ].iBottom;
		aRect.right = (UINT32)RegionData[ cnt ].iRight;

		memcpy( pRgnData + sizeof( RGNDATAHEADER) + ( cnt * sizeof( RECT ) ), &aRect, sizeof( RECT ) );
	}

	// Set items into clipper
	DDSetClipperList( (LPDIRECTDRAWCLIPPER)hVSurface->pClipper, pRgnData, 0 );

	// Set Clipper into Surface
	DDSetClipper( (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData, (LPDIRECTDRAWCLIPPER)hVSurface->pClipper );

	// Delete region data
	MemFree( pRgnData );

	return( TRUE );
}


// ********************************************************
//
// Region manipulation functions
//
// ********************************************************

BOOLEAN GetNumRegions( HVSURFACE hVSurface , UINT32 *puiNumRegions )
{
	Assert( hVSurface );

	*puiNumRegions = ListSize( hVSurface->RegionList );

	return( TRUE );

}

BOOLEAN AddVSurfaceRegion( HVSURFACE hVSurface, VSURFACE_REGION *pNewRegion )
{
	Assert( hVSurface != NULL );
	Assert( pNewRegion != NULL );

	// Add new region to list
	hVSurface->RegionList = AddtoList( hVSurface->RegionList, pNewRegion, ListSize( hVSurface->RegionList ) );

	return( TRUE );
}

// Add a group of regions
BOOLEAN AddVSurfaceRegions( HVSURFACE hVSurface, VSURFACE_REGION **ppNewRegions, UINT16 uiNumRegions )
{
	UINT16 cnt;

	Assert( hVSurface != NULL );
	Assert( ppNewRegions != NULL );

	for ( cnt = 0; cnt < uiNumRegions; cnt++ )
	{
		AddVSurfaceRegion( hVSurface, ppNewRegions[ cnt ] );
	}

	return( TRUE );
}

BOOLEAN RemoveVSurfaceRegion( HVSURFACE hVSurface, UINT16 usIndex )
{
	VSURFACE_REGION	aRegion;

	Assert( hVSurface != NULL );

	return( RemfromList( hVSurface->RegionList, &aRegion, usIndex ) );

}

BOOLEAN ClearAllVSurfaceRegions( HVSURFACE hVSurface )
{
	UINT32 uiListSize, cnt;

	Assert( hVSurface != NULL );

	uiListSize = ListSize( hVSurface->RegionList );

	for ( cnt = uiListSize - 1; cnt >= 0; cnt-- )
	{
		RemoveVSurfaceRegion( hVSurface, (UINT16)cnt );
	}

	return( TRUE );
}

BOOLEAN GetVSurfaceRegion( HVSURFACE hVSurface, UINT16 usIndex, VSURFACE_REGION *aRegion )
{
	Assert( hVSurface != NULL );

	if ( !PeekList( hVSurface->RegionList, aRegion, usIndex ) )
	{
		return( FALSE );
	}

	return( TRUE );
}

BOOLEAN GetVSurfaceRect( HVSURFACE hVSurface, RECT *pRect)
{
	Assert( hVSurface != NULL );
	Assert( pRect != NULL );

	pRect->left=0;
	pRect->top=0;
	pRect->right=hVSurface->usWidth;
	pRect->bottom=hVSurface->usHeight;

	return( TRUE );
}

BOOLEAN ReplaceVSurfaceRegion( HVSURFACE hVSurface , UINT16 usIndex, VSURFACE_REGION *aRegion )
{
	VSURFACE_REGION OldRegion;

	Assert( hVSurface != NULL );

	// Validate index given
	if ( !PeekList( hVSurface->RegionList, &OldRegion, usIndex ) )
	{
		return( FALSE );
	}

	// Replace information
	hVSurface->RegionList = AddtoList( hVSurface->RegionList, aRegion, usIndex );

	return( TRUE );
}

BOOLEAN AddVSurfaceRegionAtIndex( HVSURFACE hVSurface, UINT16 usIndex, VSURFACE_REGION *pNewRegion )
{
	Assert( hVSurface != NULL );
	Assert( pNewRegion != NULL );

	// Add new region to list
	hVSurface->RegionList = AddtoList( hVSurface->RegionList, pNewRegion, usIndex );

	return( TRUE );

}

// *******************************************************************
//
// Blitting Functions
//
// *******************************************************************

// Blt  will use DD Blt or BltFast depending on flags.
// Will drop down into user-defined blitter if 8->16 BPP blitting is being done

BOOLEAN BltVideoSurfaceToVideoSurface( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, UINT16 usIndex, INT32 iDestX, INT32 iDestY, INT32 fBltFlags, blt_vs_fx *pBltFx )
{
	VSURFACE_REGION aRegion;
	RECT					 SrcRect, DestRect;
	UINT8					*pSrcSurface8, *pDestSurface8;
	UINT16				*pDestSurface16, *pSrcSurface16;
	UINT32				uiSrcPitch, uiDestPitch, uiWidth, uiHeight;

	// Assertions
	Assert( hDestVSurface != NULL );

	// Check that both region and subrect are not given
	if ((fBltFlags&VS_BLT_SRCREGION) && (fBltFlags&VS_BLT_SRCSUBRECT))
	{
			DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Inconsistant blit flags given" ));
			return( FALSE );
	}

	// Check for dest src options
	if ( fBltFlags & VS_BLT_DESTREGION )
	{
			CHECKF( pBltFx != NULL );
			CHECKF( GetVSurfaceRegion( hDestVSurface, pBltFx->DestRegion, &aRegion ) );

			// Set starting coordinates from destination region
			iDestY = aRegion.RegionCoords.iTop;
			iDestX = aRegion.RegionCoords.iLeft;
	}

	// Check for fill, if true, fill entire region with color
	if ( fBltFlags & VS_BLT_COLORFILL )
	{
		return( FillSurface( hDestVSurface, pBltFx ) );
	}

	// Check for colorfill rectangle
	if ( fBltFlags & VS_BLT_COLORFILLRECT )
	{
		return( FillSurfaceRect( hDestVSurface, pBltFx ) );
	}

	// Check for source coordinate options - from region, specific rect or full src dimensions
	do
	{
		// Get Region from index, if specified
		if ( fBltFlags & VS_BLT_SRCREGION )
		{
			CHECKF( GetVSurfaceRegion( hSrcVSurface, usIndex, &aRegion ) )

			SrcRect.top = (int)aRegion.RegionCoords.iTop;
			SrcRect.left = (int)aRegion.RegionCoords.iLeft;
			SrcRect.bottom = (int)aRegion.RegionCoords.iBottom;
			SrcRect.right = (int)aRegion.RegionCoords.iRight;
			break;
		}

		// Use SUBRECT if specified
		if ( fBltFlags & VS_BLT_SRCSUBRECT )
		{
			SGPRect aSubRect;

			CHECKF( pBltFx != NULL );

			aSubRect = pBltFx->SrcRect;

			SrcRect.top = (int)aSubRect.iTop;
			SrcRect.left = (int)aSubRect.iLeft;
			SrcRect.bottom = (int)aSubRect.iBottom;
			SrcRect.right = (int)aSubRect.iRight;

			break;
		}

		// Here, use default, which is entire Video Surface
		// Check Sizes, SRC size MUST be <= DEST size
		if ( hDestVSurface->usHeight < hSrcVSurface->usHeight )
		{
				DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Incompatible height size given in Video Surface blit" ));
				return( FALSE );
		}
		if ( hDestVSurface->usWidth < hSrcVSurface->usWidth )
		{
				DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Incompatible height size given in Video Surface blit" ));
				return( FALSE );
		}

		SrcRect.top = (int)0;
		SrcRect.left = (int)0;
		SrcRect.bottom = (int)hSrcVSurface->usHeight;
		SrcRect.right = (int)hSrcVSurface->usWidth;

	} while( FALSE );

	// Once here, assert valid Src
	Assert( hSrcVSurface != NULL );

	// clipping -- added by DB
	GetVSurfaceRect( hDestVSurface, &DestRect);
	uiWidth=SrcRect.right-SrcRect.left;
	uiHeight=SrcRect.bottom-SrcRect.top;

	// check for position entirely off the screen
	if(iDestX >= DestRect.right)
		return(FALSE);
	if(iDestY >= DestRect.bottom)
		return(FALSE);
	if((iDestX+(INT32)uiWidth) < (INT32)DestRect.left)
		return(FALSE);
	if((iDestY+(INT32)uiHeight) < (INT32)DestRect.top)
		return(FALSE);

	// DB The mirroring stuff has to do it's own clipping because
	// it needs to invert some of the numbers
	if(!(fBltFlags & VS_BLT_MIRROR_Y))
	{
		if((iDestX+(INT32)uiWidth) >= (INT32)DestRect.right)
		{
			SrcRect.right-=((iDestX+uiWidth)-DestRect.right);
			uiWidth-=((iDestX+uiWidth)-DestRect.right);
		}
		if((iDestY+(INT32)uiHeight) >= (INT32)DestRect.bottom)
		{
			SrcRect.bottom-=((iDestY+uiHeight)-DestRect.bottom);
			uiHeight-=((iDestY+uiHeight)-DestRect.bottom);
		}
		if(iDestX < DestRect.left)
		{
			SrcRect.left+=(DestRect.left-iDestX);
			uiWidth-=(DestRect.left-iDestX);
			iDestX=DestRect.left;
		}
		if(iDestY < (INT32)DestRect.top)
		{
			SrcRect.top+=(DestRect.top-iDestY);
			uiHeight-=(DestRect.top-iDestY);
			iDestY=DestRect.top;
		}
	}

	// Send dest position, rectangle, etc to DD bltfast function
	// First check BPP values for compatibility
	if ( hDestVSurface->ubBitDepth == 16 && hSrcVSurface->ubBitDepth == 16 )
	{
		if(fBltFlags & VS_BLT_MIRROR_Y)
		{
			if((pSrcSurface16=(UINT16 *)LockVideoSurfaceBuffer(hSrcVSurface, &uiSrcPitch))==NULL)
			{
				DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed on lock of 16BPP surface for blitting" ));
				return(FALSE);
			}

			if((pDestSurface16=(UINT16 *)LockVideoSurfaceBuffer(hDestVSurface, &uiDestPitch))==NULL)
			{
				UnLockVideoSurfaceBuffer(hSrcVSurface);
				DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed on lock of 16BPP dest surface for blitting" ));
				return(FALSE);
			}

			Blt16BPPTo16BPPMirror(pDestSurface16, uiDestPitch, pSrcSurface16, uiSrcPitch, iDestX, iDestY, SrcRect.left, SrcRect.top, uiWidth, uiHeight);
			UnLockVideoSurfaceBuffer(hSrcVSurface);
			UnLockVideoSurfaceBuffer(hDestVSurface);
			return(TRUE);
		}
// For testing with non-DDraw blitting, uncomment to test -- DB
#ifndef JA2
		else
		{
			if((pSrcSurface16=(UINT16 *)LockVideoSurfaceBuffer(hSrcVSurface, &uiSrcPitch))==NULL)
			{
				DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed on lock of 16BPP surface for blitting" ));
				return(FALSE);
			}

			if((pDestSurface16=(UINT16 *)LockVideoSurfaceBuffer(hDestVSurface, &uiDestPitch))==NULL)
			{
				UnLockVideoSurfaceBuffer(hSrcVSurface);
				DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed on lock of 16BPP dest surface for blitting" ));
				return(FALSE);
			}

			Blt16BPPTo16BPP(pDestSurface16, uiDestPitch, pSrcSurface16, uiSrcPitch, iDestX, iDestY, SrcRect.left, SrcRect.top, uiWidth, uiHeight);
			UnLockVideoSurfaceBuffer(hSrcVSurface);
			UnLockVideoSurfaceBuffer(hDestVSurface);
			return(TRUE);
		}
#endif

		CHECKF( BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, fBltFlags, iDestX, iDestY, &SrcRect ) );

	}
	else if ( hDestVSurface->ubBitDepth == 8 && hSrcVSurface->ubBitDepth == 8 )
	{
		if((pSrcSurface8=(UINT8 *)LockVideoSurfaceBuffer(hSrcVSurface, &uiSrcPitch))==NULL)
		{
			DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed on lock of 8BPP surface for blitting" ));
			return(FALSE);
		}

		if((pDestSurface8=(UINT8 *)LockVideoSurfaceBuffer(hDestVSurface, &uiDestPitch))==NULL)
		{
			UnLockVideoSurfaceBuffer(hSrcVSurface);
			DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Failed on lock of 8BPP dest surface for blitting" ));
			return(FALSE);
		}

		//Blt8BPPDataTo8BPPBuffer( UINT8 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex );
		Blt8BPPTo8BPP(pDestSurface8, uiDestPitch, pSrcSurface8, uiSrcPitch, iDestX, iDestY, SrcRect.left, SrcRect.top, uiWidth, uiHeight);
		UnLockVideoSurfaceBuffer(hSrcVSurface);
		UnLockVideoSurfaceBuffer(hDestVSurface);
		return(TRUE);
	}
	else
	{
		DbgMessage(TOPIC_VIDEOSURFACE, DBG_LEVEL_2, String( "Incompatible BPP values with src and dest Video Surfaces for blitting" ));
		return( FALSE );
	}

	return( TRUE );
}



// ******************************************************************************************
//
// UTILITY FUNCTIONS
//
// ******************************************************************************************

// Blt to backup buffer
BOOLEAN UpdateBackupSurface( HVSURFACE hVSurface )
{
	RECT		aRect;

	// Assertions
	Assert( hVSurface != NULL );

	// Validations
	CHECKF( hVSurface->pSavedSurfaceData != NULL );

	aRect.top = (int)0;
	aRect.left = (int)0;
	aRect.bottom = (int)hVSurface->usHeight;
	aRect.right = (int)hVSurface->usWidth;

	// Copy all contents into backup buffer
	DDBltFastSurface( (LPDIRECTDRAWSURFACE2)hVSurface->pSurfaceData, 0, 0, (LPDIRECTDRAWSURFACE2)hVSurface->pSavedSurfaceData, &aRect, DDBLTFAST_NOCOLORKEY );

	return( TRUE );

}


// *****************************************************************************
//
// Private DirectDraw manipulation functions
//
// *****************************************************************************

LPDIRECTDRAWSURFACE2 GetVideoSurfaceDDSurface( HVSURFACE hVSurface )
{
	Assert( hVSurface != NULL );

	return( (LPDIRECTDRAWSURFACE2) hVSurface->pSurfaceData );
}

LPDIRECTDRAWSURFACE GetVideoSurfaceDDSurfaceOne( HVSURFACE hVSurface )
{
	Assert( hVSurface != NULL );

	return( (LPDIRECTDRAWSURFACE) hVSurface->pSurfaceData1 );
}


LPDIRECTDRAWPALETTE  GetVideoSurfaceDDPalette( HVSURFACE hVSurface )
{
	Assert( hVSurface != NULL );

	return( (LPDIRECTDRAWPALETTE) hVSurface->pPalette );
}

HVSURFACE CreateVideoSurfaceFromDDSurface( LPDIRECTDRAWSURFACE2 lpDDSurface )
{
	// Create Video Surface
	DDPIXELFORMAT			  PixelFormat;
	HVSURFACE						hVSurface;
	DDSURFACEDESC			  DDSurfaceDesc;
	LPDIRECTDRAWPALETTE	pDDPalette;
	SGPPaletteEntry			SGPPalette[ 256 ];
	HRESULT							ReturnCode;


	// Allocate Video Surface struct
	hVSurface = MemAlloc( sizeof( SGPVSurface ) );

	// Set values based on DD Surface given
	DDGetSurfaceDescription ( lpDDSurface, &DDSurfaceDesc );
	PixelFormat = DDSurfaceDesc.ddpfPixelFormat;

	hVSurface->usHeight					= (UINT16)DDSurfaceDesc.dwHeight;
	hVSurface->usWidth						= (UINT16)DDSurfaceDesc.dwWidth;
	hVSurface->ubBitDepth				= (UINT8)PixelFormat.dwRGBBitCount;
	hVSurface->pSurfaceData			= (PTR)lpDDSurface;
	hVSurface->pSurfaceData1		= NULL;
	hVSurface->pSavedSurfaceData = NULL;
	hVSurface->RegionList				= CreateList( DEFAULT_NUM_REGIONS, sizeof( VSURFACE_REGION ) );
	hVSurface->fFlags						= 0;

	// Get and Set palette, if attached, allow to fail
	ReturnCode = IDirectDrawSurface2_GetPalette( lpDDSurface, &pDDPalette );

	if ( ReturnCode == DD_OK )
	{
		// Set 8-bit Palette and 16 BPP palette
		hVSurface->pPalette = pDDPalette;

		// Create 16-BPP Palette
		DDGetPaletteEntries( pDDPalette, 0, 0, 256, ( LPPALETTEENTRY )SGPPalette );
		hVSurface->p16BPPPalette = Create16BPPPalette( SGPPalette );
	}
	else
	{
		hVSurface->pPalette = NULL;
		hVSurface->p16BPPPalette = NULL;
	}
	// Set meory flags
	if ( DDSurfaceDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY )
	{
		hVSurface->fFlags |= VSURFACE_SYSTEM_MEM_USAGE;
	}

	if ( DDSurfaceDesc.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY )
	{
		hVSurface->fFlags |= VSURFACE_VIDEO_MEM_USAGE;
	}

	// All is well
  DbgMessage( TOPIC_VIDEOSURFACE, DBG_LEVEL_0, String("Success in Creating Video Surface from DD Surface" ) );

	return( hVSurface );
}

HVSURFACE GetPrimaryVideoSurface( )
{
	return( ghPrimary );
}

HVSURFACE GetBackBufferVideoSurface( )
{
	return( ghBackBuffer );
}

HVSURFACE GetFrameBufferVideoSurface( )
{
	return( ghFrameBuffer );
}

HVSURFACE GetMouseBufferVideoSurface( )
{
	return( ghMouseBuffer );
}


// UTILITY FUNCTIONS FOR BLITTING

BOOLEAN ClipReleatedSrcAndDestRectangles( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, RECT *DestRect, RECT *SrcRect )
{

	Assert( hDestVSurface != NULL );
	Assert( hSrcVSurface != NULL );

	// Check for invalid start positions and clip by ignoring blit
	if ( DestRect->left >= hDestVSurface->usWidth || DestRect->top >= hDestVSurface->usHeight )
	{
		return( FALSE );
	}

	if ( SrcRect->left >= hSrcVSurface->usWidth || SrcRect->top >= hSrcVSurface->usHeight )
	{
		return( FALSE );
	}

	// For overruns
	// Clip destination rectangles
	if ( DestRect->right > hDestVSurface->usWidth )
	{
		// Both have to be modified or by default streching occurs
		DestRect->right = hDestVSurface->usWidth;
		SrcRect->right = SrcRect->left + ( DestRect->right - DestRect->left );
	}
	if ( DestRect->bottom > hDestVSurface->usHeight )
	{
		// Both have to be modified or by default streching occurs
		DestRect->bottom = hDestVSurface->usHeight;
		SrcRect->bottom = SrcRect->top + ( DestRect->bottom - DestRect->top );
	}

	// Clip src rectangles
	if ( SrcRect->right > hSrcVSurface->usWidth )
	{
		// Both have to be modified or by default streching occurs
		SrcRect->right = hSrcVSurface->usWidth;
		DestRect->right = DestRect->left  + ( SrcRect->right - SrcRect->left );
	}
	if ( SrcRect->bottom > hSrcVSurface->usHeight )
	{
		// Both have to be modified or by default streching occurs
		SrcRect->bottom = hSrcVSurface->usHeight;
		DestRect->bottom = DestRect->top + ( SrcRect->bottom - SrcRect->top );
	}

	// For underruns
	// Clip destination rectangles
	if ( DestRect->left < 0 )
	{
		// Both have to be modified or by default streching occurs
		DestRect->left = 0;
		SrcRect->left = SrcRect->right - ( DestRect->right - DestRect->left );
	}
	if ( DestRect->top < 0 )
	{
		// Both have to be modified or by default streching occurs
		DestRect->top = 0;
		SrcRect->top = SrcRect->bottom - ( DestRect->bottom - DestRect->top );
	}

	// Clip src rectangles
	if ( SrcRect->left < 0 )
	{
		// Both have to be modified or by default streching occurs
		SrcRect->left = 0;
		DestRect->left = DestRect->right  - ( SrcRect->right - SrcRect->left );
	}
	if ( SrcRect->top < 0 )
	{
		// Both have to be modified or by default streching occurs
		SrcRect->top = 0;
		DestRect->top = DestRect->bottom - ( SrcRect->bottom - SrcRect->top );
	}

	return( TRUE );
}


BOOLEAN FillSurface( HVSURFACE hDestVSurface, blt_vs_fx *pBltFx )
{
	DDBLTFX				 BlitterFX;

	Assert( hDestVSurface != NULL );
	CHECKF( pBltFx != NULL );

	BlitterFX.dwSize = sizeof( DDBLTFX );
	BlitterFX.dwFillColor = pBltFx->ColorFill;

	DDBltSurface( (LPDIRECTDRAWSURFACE2)hDestVSurface->pSurfaceData, NULL, NULL, NULL, DDBLT_COLORFILL, &BlitterFX );

	if ( hDestVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE && !hDestVSurface->fFlags & VSURFACE_RESERVED_SURFACE )
	{
		UpdateBackupSurface( hDestVSurface );
	}

	return( TRUE );
}

BOOLEAN FillSurfaceRect( HVSURFACE hDestVSurface, blt_vs_fx *pBltFx )
{
	DDBLTFX				 BlitterFX;

	Assert( hDestVSurface != NULL );
	CHECKF( pBltFx != NULL );

	BlitterFX.dwSize = sizeof( DDBLTFX );
	BlitterFX.dwFillColor = pBltFx->ColorFill;

	DDBltSurface( (LPDIRECTDRAWSURFACE2)hDestVSurface->pSurfaceData, (LPRECT)&(pBltFx->FillRect), NULL, NULL, DDBLT_COLORFILL, &BlitterFX );

	if ( hDestVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE && !hDestVSurface->fFlags & VSURFACE_RESERVED_SURFACE )
	{
		UpdateBackupSurface( hDestVSurface );
	}

	return( TRUE );
}


BOOLEAN BltVSurfaceUsingDD( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, UINT32 fBltFlags, INT32 iDestX, INT32 iDestY, RECT *SrcRect )
{
	UINT32		uiDDFlags;
	RECT			DestRect;

  // Blit using the correct blitter
	if ( fBltFlags & VS_BLT_FAST )
	{

		// Validations
		CHECKF( iDestX >= 0 );
		CHECKF( iDestY >= 0 );

		// Default flags
		uiDDFlags = 0;

		// Convert flags into DD flags, ( for transparency use, etc )
		if ( fBltFlags & VS_BLT_USECOLORKEY )
		{
			uiDDFlags |= DDBLTFAST_SRCCOLORKEY;
		}

		// Convert flags into DD flags, ( for transparency use, etc )
		if ( fBltFlags & VS_BLT_USEDESTCOLORKEY )
		{
			uiDDFlags |= DDBLTFAST_DESTCOLORKEY;
		}

		if ( uiDDFlags == 0 )
		{
			// Default here is no colorkey
			uiDDFlags = DDBLTFAST_NOCOLORKEY;
		}

		DDBltFastSurface( (LPDIRECTDRAWSURFACE2)hDestVSurface->pSurfaceData, iDestX, iDestY, (LPDIRECTDRAWSURFACE2)hSrcVSurface->pSurfaceData, SrcRect, uiDDFlags );

	}
	else
	{
		// Normal, specialized blit for clipping, etc

		// Default flags
		uiDDFlags = DDBLT_WAIT;

		// Convert flags into DD flags, ( for transparency use, etc )
		if ( fBltFlags & VS_BLT_USECOLORKEY )
		{
			uiDDFlags |= DDBLT_KEYSRC;
		}

		// Setup dest rectangle
		DestRect.top =  (int)iDestY;
		DestRect.left = (int)iDestX;
		DestRect.bottom = (int)iDestY + ( SrcRect->bottom - SrcRect->top );
		DestRect.right = (int)iDestX + ( SrcRect->right - SrcRect->left );

		// Do Clipping of rectangles
		if ( !ClipReleatedSrcAndDestRectangles( hDestVSurface, hSrcVSurface, &DestRect, SrcRect ) )
		{
			// Returns false because dest start is > dest size
			return( TRUE );
		}

		// Check values for 0 size
		if ( DestRect.top == DestRect.bottom || DestRect.right == DestRect.left )
		{
			return( TRUE );
		}

		// Check for -ve values



		DDBltSurface( (LPDIRECTDRAWSURFACE2)hDestVSurface->pSurfaceData, &DestRect, (LPDIRECTDRAWSURFACE2)hSrcVSurface->pSurfaceData,
							SrcRect, uiDDFlags, NULL );

	}

	// Update backup surface with new data
	if ( hDestVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE && !hDestVSurface->fFlags & VSURFACE_RESERVED_SURFACE )
	{
		UpdateBackupSurface( hDestVSurface );
	}

	return( TRUE );
}

BOOLEAN Blt16BPPBufferShadowRectAlternateTable(UINT16 *pBuffer, UINT32 uiDestPitchBYTES, SGPRect *area);



BOOLEAN InternalShadowVideoSurfaceRect(  UINT32	uiDestVSurface, INT32 X1, INT32 Y1, INT32 X2, INT32 Y2, BOOLEAN fLowPercentShadeTable )
{
	UINT16 *pBuffer;
	UINT32 uiPitch;
	SGPRect   area;
	HVSURFACE hVSurface;


	// CLIP IT!
	// FIRST GET SURFACE

  //
	// Get Video Surface
  //
	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_SHADOWVIDEOSURFACERECT;
	#endif
	CHECKF( GetVideoSurface( &hVSurface, uiDestVSurface ) );

	if ( X1 < 0 )
			X1 = 0;

	if ( X2 < 0 )
			return( FALSE );

	if ( Y2 < 0 )
			return( FALSE );

	if ( Y1 < 0 )
			Y1 = 0;

	if ( X2 >= hVSurface->usWidth )
			X2 = hVSurface->usWidth-1;

	if ( Y2 >= hVSurface->usHeight )
			Y2 = hVSurface->usHeight-1;

	if ( X1 >= hVSurface->usWidth )
			return( FALSE );

	if ( Y1 >= hVSurface->usHeight )
			return( FALSE );

	if (  ( X2 - X1 ) <= 0 )
			return( FALSE );

	if (  ( Y2 - Y1 ) <= 0 )
			return( FALSE );


	area.iTop=Y1;
	area.iBottom=Y2;
	area.iLeft=X1;
	area.iRight=X2;


	// Lock video surface
	pBuffer = (UINT16*)LockVideoSurface( uiDestVSurface, &uiPitch );
 	//UnLockVideoSurface( uiDestVSurface );

	if ( pBuffer == NULL )
	{
		return( FALSE );
	}

	if ( !fLowPercentShadeTable )
	{
		// Now we have the video object and surface, call the shadow function
		if(!Blt16BPPBufferShadowRect(pBuffer, uiPitch, &area))
		{
			// Blit has failed if false returned
			return( FALSE );
		}
	}
	else
	{
		// Now we have the video object and surface, call the shadow function
		if(!Blt16BPPBufferShadowRectAlternateTable(pBuffer, uiPitch, &area))
		{
			// Blit has failed if false returned
			return( FALSE );
		}
	}

	// Mark as dirty if it's the backbuffer
	//if ( uiDestVSurface == BACKBUFFER )
	//{
	//	InvalidateBackbuffer( );
	//}

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}


BOOLEAN ShadowVideoSurfaceRect(  UINT32	uiDestVSurface, INT32 X1, INT32 Y1, INT32 X2, INT32 Y2)
{
 return( InternalShadowVideoSurfaceRect( uiDestVSurface, X1, Y1, X2, Y2, FALSE ) );
}


BOOLEAN ShadowVideoSurfaceRectUsingLowPercentTable(  UINT32	uiDestVSurface, INT32 X1, INT32 Y1, INT32 X2, INT32 Y2)
{
 return( InternalShadowVideoSurfaceRect( uiDestVSurface, X1, Y1, X2, Y2, TRUE ) );
}


//
// BltVSurfaceUsingDDBlt will always use Direct Draw Blt,NOT BltFast
BOOLEAN BltVSurfaceUsingDDBlt( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, UINT32 fBltFlags, INT32 iDestX, INT32 iDestY, RECT *SrcRect, RECT *DestRect )
{
	UINT32		uiDDFlags;

	// Default flags
	uiDDFlags = DDBLT_WAIT;

	// Convert flags into DD flags, ( for transparency use, etc )
	if ( fBltFlags & VS_BLT_USECOLORKEY )
	{
		uiDDFlags |= DDBLT_KEYSRC;
	}

	DDBltSurface( (LPDIRECTDRAWSURFACE2)hDestVSurface->pSurfaceData, DestRect, (LPDIRECTDRAWSURFACE2)hSrcVSurface->pSurfaceData,
						SrcRect, uiDDFlags, NULL );

	// Update backup surface with new data
	if ( hDestVSurface->fFlags & VSURFACE_VIDEO_MEM_USAGE && !hDestVSurface->fFlags & VSURFACE_RESERVED_SURFACE )
	{
		UpdateBackupSurface( hDestVSurface );
	}

	return( TRUE );
}


//
// This function will stretch the source image to the size of the dest rect.
//
// If the 2 images are not 16 Bpp, it returns false.
//
BOOLEAN BltStretchVideoSurface(UINT32 uiDestVSurface, UINT32 uiSrcVSurface, INT32 iDestX, INT32 iDestY, UINT32 fBltFlags, SGPRect *SrcRect, SGPRect *DestRect )
{
	HVSURFACE	hDestVSurface;
	HVSURFACE	hSrcVSurface;

	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_BLTSTRETCHVIDEOSURFACE_DST;
	#endif
	if( !GetVideoSurface( &hDestVSurface, uiDestVSurface ) )
	{
		return FALSE;
	}
	#ifdef _DEBUG
		gubVSDebugCode = DEBUGSTR_BLTSTRETCHVIDEOSURFACE_SRC;
	#endif
	if( !GetVideoSurface( &hSrcVSurface, uiSrcVSurface ) )
	{
		return FALSE;
	}

	//if the 2 images are not both 16bpp, return FALSE
	if( (hDestVSurface->ubBitDepth != 16) && (hSrcVSurface->ubBitDepth != 16) )
		return(FALSE);

	if(!BltVSurfaceUsingDDBlt( hDestVSurface, hSrcVSurface, fBltFlags, iDestX, iDestY, (RECT*)SrcRect, (RECT*)DestRect ) )
	{
    //
		// VO Blitter will set debug messages for error conditions
    //

		return( FALSE );
	}

	return( TRUE );
}


BOOLEAN ShadowVideoSurfaceImage( UINT32	uiDestVSurface, HVOBJECT hImageHandle, INT32 iPosX, INT32 iPosY)
{
	//Horizontal shadow
	ShadowVideoSurfaceRect( uiDestVSurface, iPosX+3, iPosY+hImageHandle->pETRLEObject->usHeight, iPosX+hImageHandle->pETRLEObject->usWidth, iPosY+	hImageHandle->pETRLEObject->usHeight+3);

	//vertical shadow
	ShadowVideoSurfaceRect( uiDestVSurface, iPosX+hImageHandle->pETRLEObject->usWidth, iPosY+3, iPosX+hImageHandle->pETRLEObject->usWidth+3, iPosY+	hImageHandle->pETRLEObject->usHeight);
	return( TRUE );
}

BOOLEAN MakeVSurfaceFromVObject(UINT32 uiVObject, UINT16 usSubIndex, UINT32 *puiVSurface)
{
HVOBJECT hSrcVObject;
UINT32 uiVSurface;
VSURFACE_DESC hDesc;

	if(GetVideoObject(&hSrcVObject, uiVObject))
	{
		// ATE: Memset
		memset( &hDesc, 0, sizeof( VSURFACE_DESC ) );
		hDesc.fCreateFlags=VSURFACE_CREATE_DEFAULT;
		hDesc.usWidth=hSrcVObject->pETRLEObject[ usSubIndex ].usWidth;
		hDesc.usHeight=hSrcVObject->pETRLEObject[ usSubIndex ].usHeight;
		hDesc.ubBitDepth=PIXEL_DEPTH;

		if(AddVideoSurface(&hDesc, &uiVSurface))
		{
			if(BltVideoObjectFromIndex(uiVSurface, uiVObject, usSubIndex, 0, 0, VO_BLT_SRCTRANSPARENCY, NULL))
			{
				*puiVSurface=uiVSurface;
				return(TRUE);
			}
			else
				DeleteVideoSurfaceFromIndex(uiVSurface);
		}
	}		
	
	return(FALSE);
}

#ifdef _DEBUG
void CheckValidVSurfaceIndex( UINT32 uiIndex )
{
	BOOLEAN fAssertError = FALSE;
	if( uiIndex == 0xffffffff )
	{ //-1 index -- deleted
		fAssertError = TRUE;
	}
	else if( uiIndex % 2 && uiIndex < 0xfffffff0  )
	{ //odd numbers are reserved for vobjects
		fAssertError = TRUE;
	}

	if( fAssertError )
	{
		UINT8 str[60];
		switch( gubVSDebugCode )
		{
			case DEBUGSTR_SETVIDEOSURFACETRANSPARENCY:
				sprintf( str, "SetVideoSurfaceTransparency" );
				break;
			case DEBUGSTR_ADDVIDEOSURFACEREGION:
				sprintf( str, "AddVideoSurfaceRegion" );
				break;
			case DEBUGSTR_GETVIDEOSURFACEDESCRIPTION:
				sprintf( str, "GetVideoSurfaceDescription" );
				break;
			case DEBUGSTR_BLTVIDEOSURFACE_DST:
				sprintf( str, "BltVideoSurface (dest)" );
				break;
			case DEBUGSTR_BLTVIDEOSURFACE_SRC:
				sprintf( str, "BltVideoSurface (src)" );
				break;
			case DEBUGSTR_COLORFILLVIDEOSURFACEAREA:
				sprintf( str, "ColorFillVideoSurfaceArea" );
				break;
			case DEBUGSTR_SHADOWVIDEOSURFACERECT:
				sprintf( str, "ShadowVideoSurfaceRect" );
				break;
			case DEBUGSTR_BLTSTRETCHVIDEOSURFACE_DST:
				sprintf( str, "BltStretchVideoSurface (dest)" );
				break;
			case DEBUGSTR_BLTSTRETCHVIDEOSURFACE_SRC:
				sprintf( str, "BltStretchVideoSurface (src)" );
				break;
			case DEBUGSTR_DELETEVIDEOSURFACEFROMINDEX:
				sprintf( str, "DeleteVideoSurfaceFromIndex" );
				break;
			case DEBUGSTR_NONE:
			default:
				sprintf( str, "GetVideoSurface" );
				break;
		}
		if( uiIndex == 0xffffffff )
		{
			AssertMsg( 0, String( "Trying to %s with deleted index -1." , str ) );
		}
		else
		{
			AssertMsg( 0, String( "Trying to %s using a VOBJECT ID %d!", str, uiIndex ) );
		}
	}
}
#endif

#ifdef SGP_VIDEO_DEBUGGING
typedef struct DUMPFILENAME
{
	UINT8 str[256];
}DUMPFILENAME;
void DumpVSurfaceInfoIntoFile( UINT8 *filename, BOOLEAN fAppend )
{
	VSURFACE_NODE *curr;
	FILE *fp;
	DUMPFILENAME *pName, *pCode;
	UINT32 *puiCounter;
	UINT8 tempName[ 256 ];
	UINT8 tempCode[ 256 ];
	UINT32 i, uiUniqueID;
	BOOLEAN fFound;
	if( !guiVSurfaceSize )
	{
		return;
	}

	if( fAppend )
	{
		fp = fopen( filename, "a" );
	}
	else
	{
		fp = fopen( filename, "w" );
	}
	Assert( fp );

	//Allocate enough strings and counters for each node.
	pName = (DUMPFILENAME*)MemAlloc( sizeof( DUMPFILENAME ) * guiVSurfaceSize );
	pCode = (DUMPFILENAME*)MemAlloc( sizeof( DUMPFILENAME ) * guiVSurfaceSize );
	memset( pName, 0, sizeof( DUMPFILENAME ) * guiVSurfaceSize );
	memset( pCode, 0, sizeof( DUMPFILENAME ) * guiVSurfaceSize );
	puiCounter = (UINT32*)MemAlloc( 4 * guiVSurfaceSize );
	memset( puiCounter, 0, 4 * guiVSurfaceSize );

	//Loop through the list and record every unique filename and count them
	uiUniqueID = 0;
	curr = gpVSurfaceHead;
	while( curr )
	{
		strcpy( tempName, curr->pName );
		strcpy( tempCode, curr->pCode );
		fFound = FALSE;
		for( i = 0; i < uiUniqueID; i++ )
		{
			if( !_stricmp( tempName, pName[i].str ) && !_stricmp( tempCode, pCode[i].str ) )
			{ //same string
				fFound = TRUE;
				(puiCounter[ i ])++;
				break;
			}
		}
		if( !fFound )
		{
			strcpy( pName[i].str, tempName );
			strcpy( pCode[i].str, tempCode );
			(puiCounter[ i ])++;
			uiUniqueID++;
		}
		curr = curr->next;
	}

	//Now dump the info.
	fprintf( fp, "-----------------------------------------------\n" );
	fprintf( fp, "%d unique vSurface names exist in %d VSurfaces\n", uiUniqueID, guiVSurfaceSize );
	fprintf( fp, "-----------------------------------------------\n\n" );
	for( i = 0; i < uiUniqueID; i++ )
	{
		fprintf( fp, "%d occurrences of %s\n", puiCounter[i], pName[i].str );
		fprintf( fp, "%s\n\n", pCode[i].str );
	}
	fprintf( fp, "\n-----------------------------------------------\n\n" );

	//Free all memory associated with this operation.
	MemFree( pName );
	MemFree( pCode );
	MemFree( puiCounter );
	fclose( fp );
}

//Debug wrapper for adding vsurfaces
BOOLEAN _AddAndRecordVSurface( VSURFACE_DESC *VSurfaceDesc, UINT32 *uiIndex, UINT32 uiLineNum, UINT8 *pSourceFile )
{
	UINT16 usLength;
	UINT8 str[256];
	if( !AddStandardVideoSurface( VSurfaceDesc, uiIndex ) )
	{
		return FALSE;
	}

	//record the filename of the vsurface (some are created via memory though)
	usLength = strlen( VSurfaceDesc->ImageFile ) + 1;
	gpVSurfaceTail->pName = (UINT8*)MemAlloc( usLength );
	memset( gpVSurfaceTail->pName, 0, usLength );
	strcpy( gpVSurfaceTail->pName, VSurfaceDesc->ImageFile );

	//record the code location of the calling creating function.
	sprintf( str, "%s -- line(%d)", pSourceFile, uiLineNum );
	usLength = strlen( str ) + 1;
	gpVSurfaceTail->pCode = (UINT8*)MemAlloc( usLength );
	memset( gpVSurfaceTail->pCode, 0, usLength );
	strcpy( gpVSurfaceTail->pCode, str );

	return TRUE;
}

#endif

