#include "builddefines.h"

#ifdef PRECOMPILEDHEADERS
	#include "TileEngine All.h"
	#include "winfont.h"

#else
	#include "worlddef.h"
	#include "Render Dirty.h"
	#include "sysutil.h"
	#include "vobject_blitters.h"
#endif

#ifdef JA2BETAVERSION
#include "Message.h"
#endif

// Forward Declarations
extern INT16 gsVIEWPORT_START_X;
extern INT16 gsVIEWPORT_START_Y;
extern INT16 gsVIEWPORT_WINDOW_START_Y;
extern INT16 gsVIEWPORT_WINDOW_END_Y;
extern INT16 gsVIEWPORT_END_X;
extern INT16 gsVIEWPORT_END_Y;
extern UINT16* gpZBuffer;
extern BOOLEAN	gfScrollInertia;


#define		DIRTY_QUEUES			200
#define		BACKGROUND_BUFFERS		1500 //was 500
#define		VIDEO_OVERLAYS			100


BACKGROUND_SAVE	gBackSaves[BACKGROUND_BUFFERS];
UINT32 guiNumBackSaves=0;

VIDEO_OVERLAY	gVideoOverlays[ VIDEO_OVERLAYS ];
UINT32 guiNumVideoOverlays=0;


void AllocateVideoOverlayArea( UINT32 uiCount );
void SaveVideoOverlayArea( UINT32 uiSrcBuffer, UINT32 uiCount );

//BACKGROUND_SAVE	gTopmostSaves[BACKGROUND_BUFFERS];
//UINT32 guiNumTopmostSaves=0;

// do zmiany
//SGPRect		gDirtyClipRect = { 0, 0, 2560, 1600 };
SGPRect		gDirtyClipRect = { 0, 0, 0, 0 }; 

BOOLEAN		gfViewportDirty=FALSE;


BOOLEAN InitializeBaseDirtyRectQueue( )
{
	gDirtyClipRect.iLeft = 0;
	gDirtyClipRect.iTop = 0;
	gDirtyClipRect.iRight = SCREEN_WIDTH;
	gDirtyClipRect.iBottom = SCREEN_HEIGHT;

	return( TRUE );
}

void ShutdownBaseDirtyRectQueue( )
{

}

void AddBaseDirtyRect( INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom )
{
	SGPRect aRect;

	if ( iLeft < 0 )
	{
		iLeft = 0;
	}
	if ( iLeft > SCREEN_WIDTH )
	{
		iLeft = SCREEN_WIDTH;
	}

	if ( iTop < 0 )
	{
		iTop = 0;
	}
	if ( iTop > SCREEN_HEIGHT )
	{
		iTop = SCREEN_HEIGHT;
	}

	if ( iRight < 0 )
	{
		iRight = 0;
	}
	if ( iRight > SCREEN_WIDTH )
	{
		iRight = SCREEN_WIDTH;
	}

	if ( iBottom < 0 )
	{
		iBottom = 0;
	}
	if ( iBottom > SCREEN_HEIGHT )
	{
		iBottom = SCREEN_HEIGHT;
	}

	if ( ( iRight - iLeft ) == 0 || ( iBottom - iTop ) == 0 )
	{
		return;
	}


	if( (iLeft==gsVIEWPORT_START_X) &&
		(iRight==gsVIEWPORT_END_X) &&
		(iTop==gsVIEWPORT_WINDOW_START_Y) &&
		(iBottom==gsVIEWPORT_WINDOW_END_Y))
	{
		gfViewportDirty=TRUE;
		return;
	}

	// Add to list
	aRect.iLeft		= iLeft;
	aRect.iTop		= iTop;
	aRect.iRight	= iRight;
	aRect.iBottom	= iBottom;

	InvalidateRegionEx( aRect.iLeft, aRect.iTop, aRect.iRight, aRect.iBottom, 0 );
}

BOOLEAN ExecuteBaseDirtyRectQueue( )
{
	if(gfViewportDirty)
	{
		//InvalidateRegion(gsVIEWPORT_START_X, gsVIEWPORT_START_Y, gsVIEWPORT_END_X, gsVIEWPORT_END_Y);
		InvalidateScreen( );
		EmptyDirtyRectQueue();
		gfViewportDirty=FALSE;
		return(TRUE);
	}

	return( TRUE );
}

BOOLEAN EmptyDirtyRectQueue( )
{
	return( TRUE );
}


INT32 GetFreeBackgroundBuffer(void)
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
	{
		if((gBackSaves[uiCount].fAllocated==FALSE) && (gBackSaves[uiCount].fFilled==FALSE))
			return((INT32)uiCount);
	}

	if(guiNumBackSaves < BACKGROUND_BUFFERS)
		return((INT32)guiNumBackSaves++);
#ifdef JA2BETAVERSION
	else
	{
		//else display an error message
		DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("ERROR! GetFreeBackgroundBuffer(): Trying to allocate more saves then there is room:	guiCurrentScreen = %d", guiCurrentScreen ) );
	}
#endif
	return(-1);
}

void RecountBackgrounds(void)
{
	INT32 uiCount;

	for(uiCount=guiNumBackSaves-1; (uiCount >=0) ; uiCount--)
	{
		if((gBackSaves[uiCount].fAllocated) || (gBackSaves[uiCount].fFilled))
		{
			guiNumBackSaves=(UINT32)(uiCount+1);
			break;
		}
	}
}

#include <map>
extern std::map<UINT32,ClipRectangle> g_SurfaceRectangle;

INT32 RegisterBackgroundRect(UINT32 uiFlags, INT16 *pSaveArea, INT16 sLeft, INT16 sTop, INT16 sRight, INT16 sBottom)
{
	UINT32 uiBufSize;
	INT32 iBackIndex;
	INT32	ClipX1, ClipY1, ClipX2, ClipY2;
	INT32	uiLeftSkip, uiRightSkip, uiTopSkip, uiBottomSkip;
	UINT32 usHeight, usWidth;
	INT32	iTempX, iTempY;

	// Don't register if we are rendering and we are below the viewport
	//if ( sTop >= gsVIEWPORT_WINDOW_END_Y )
	//{
	//	return(-1 );
	//}
	//only time this shoudl be true is tactical..fix for games saved in broken state
	extern UINT32 guiTacticalInterfaceFlags;
	if (guiTacticalInterfaceFlags & 0x00000001)
	{
		if(gDirtyClipRect.iBottom < SCREEN_HEIGHT)
		{
			gDirtyClipRect.iBottom = SCREEN_HEIGHT;
		}
	}
	ClipX1= gDirtyClipRect.iLeft;
	ClipY1= gDirtyClipRect.iTop;
	ClipX2= gDirtyClipRect.iRight;
	ClipY2= gDirtyClipRect.iBottom;

	usHeight = sBottom - sTop;
	usWidth	= sRight -	sLeft;

	//if((sClipLeft >= sClipRight) || (sClipTop >= sClipBottom))
	//	return(-1);
	iTempX = sLeft;
	iTempY = sTop;

	// Clip to rect
	uiLeftSkip=__min( ClipX1 - min(ClipX1, iTempX), (INT32)usWidth);
	uiRightSkip=__min(max(ClipX2, (iTempX+(INT32)usWidth)) - ClipX2, (INT32)usWidth);
	uiTopSkip=__min(ClipY1 - __min(ClipY1, iTempY), (INT32)usHeight);
	uiBottomSkip=__min(__max(ClipY2, (iTempY+(INT32)usHeight)) - ClipY2, (INT32)usHeight);

	// check if whole thing is clipped
	if((uiLeftSkip >=(INT32)usWidth) || (uiRightSkip >=(INT32)usWidth))
		return(-1 );

	// check if whole thing is clipped
	if((uiTopSkip >=(INT32)usHeight) || (uiBottomSkip >=(INT32)usHeight))
		return(-1 );

	// Set re-set values given based on clipping
	sLeft	= sLeft + (INT16)uiLeftSkip;
	sRight = sRight - (INT16)uiRightSkip;
	sTop	= sTop + (INT16)uiTopSkip;
	sBottom = sBottom - (INT16)uiBottomSkip;

	if((iBackIndex=GetFreeBackgroundBuffer())==(-1))
		return(-1);

	memset(&gBackSaves[iBackIndex], 0, sizeof(BACKGROUND_SAVE));

	gBackSaves[iBackIndex].fZBuffer=FALSE;

	if(pSaveArea==NULL)
	{
		uiBufSize=((sRight-sLeft)*2)*(sBottom-sTop);

		if(uiBufSize==0)
			return(-1);

		if ( uiFlags & BGND_FLAG_SAVERECT )
		{
			if((gBackSaves[iBackIndex].pSaveArea = (INT16 *) MemAlloc(uiBufSize))==NULL)
				return(-1);
			BYTE* data = (BYTE*)gBackSaves[iBackIndex].pSaveArea;
			SurfaceData::SetApplicationData(data);
			g_SurfaceRectangle[SurfaceData::GetSurfaceID(data)].SetRect(sRight-sLeft,sBottom-sTop);
		}


		if(uiFlags&BGND_FLAG_SAVE_Z)
		{
			if((gBackSaves[iBackIndex].pZSaveArea = (INT16 *) MemAlloc(uiBufSize))==NULL)
				return(-1);
			gBackSaves[iBackIndex].fZBuffer=TRUE;
			BYTE* data = (BYTE*)gBackSaves[iBackIndex].pZSaveArea;
			SurfaceData::SetApplicationData(data);
			g_SurfaceRectangle[SurfaceData::GetSurfaceID(data)].SetRect(sRight-sLeft,sBottom-sTop);
		}

		gBackSaves[iBackIndex].fFreeMemory=TRUE;
	}
	//else
	//	gBackSaves[iBackIndex].pSaveArea=pSaveArea;

	gBackSaves[iBackIndex].fAllocated=TRUE;
	gBackSaves[iBackIndex].uiFlags=uiFlags;
	gBackSaves[iBackIndex].sLeft=sLeft;
	gBackSaves[iBackIndex].sTop=sTop;
	gBackSaves[iBackIndex].sRight=sRight;
	gBackSaves[iBackIndex].sBottom=sBottom;
	gBackSaves[iBackIndex].sWidth=(sRight-sLeft);
	gBackSaves[iBackIndex].sHeight=(sBottom-sTop);

	gBackSaves[iBackIndex].fFilled=FALSE;

	return(iBackIndex);
}

void SetBackgroundRectFilled( UINT32 uiBackgroundID )
{
	gBackSaves[uiBackgroundID].fFilled=TRUE;

	AddBaseDirtyRect(gBackSaves[uiBackgroundID].sLeft, gBackSaves[uiBackgroundID].sTop,
						gBackSaves[uiBackgroundID].sRight, gBackSaves[uiBackgroundID].sBottom);

}

BOOLEAN RestoreBackgroundRects(void)
{
	UINT32 uiCount, uiDestPitchBYTES, uiSrcPitchBYTES;
	UINT8	*pDestBuf, *pSrcBuf;

	pDestBuf = LockVideoSurface(guiRENDERBUFFER, &uiDestPitchBYTES);
	pSrcBuf = LockVideoSurface(guiSAVEBUFFER, &uiSrcPitchBYTES);

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
	{
		if(gBackSaves[uiCount].fFilled && ( !gBackSaves[uiCount].fDisabled) )
		{

			if ( gBackSaves[uiCount].uiFlags & BGND_FLAG_SAVERECT )
			{
				if ( gBackSaves[uiCount].pSaveArea != NULL )
				{
					Blt16BPPTo16BPP( (UINT16*)pDestBuf, uiDestPitchBYTES, (UINT16 *)gBackSaves[uiCount].pSaveArea, gBackSaves[uiCount].sWidth*2,
								gBackSaves[uiCount].sLeft , gBackSaves[uiCount].sTop,
								0, 0,
								gBackSaves[uiCount].sWidth, gBackSaves[uiCount].sHeight);

					AddBaseDirtyRect(gBackSaves[uiCount].sLeft, gBackSaves[uiCount].sTop,
													gBackSaves[uiCount].sRight, gBackSaves[uiCount].sBottom);
				}
			}
			else if ( gBackSaves[uiCount].uiFlags & BGND_FLAG_SAVE_Z )
			{
				if ( gBackSaves[uiCount].fZBuffer )
				{
					Blt16BPPTo16BPP( (UINT16*)gpZBuffer, uiDestPitchBYTES, (UINT16 *)gBackSaves[uiCount].pZSaveArea, gBackSaves[uiCount].sWidth*2,
								gBackSaves[uiCount].sLeft , gBackSaves[uiCount].sTop,
								0, 0,
								gBackSaves[uiCount].sWidth, gBackSaves[uiCount].sHeight);
				}
			}
			else
			{
				Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
							(UINT16 *)pSrcBuf, uiSrcPitchBYTES,
							gBackSaves[uiCount].sLeft , gBackSaves[uiCount].sTop,
							gBackSaves[uiCount].sLeft , gBackSaves[uiCount].sTop,
							gBackSaves[uiCount].sWidth, gBackSaves[uiCount].sHeight);

				AddBaseDirtyRect(gBackSaves[uiCount].sLeft, gBackSaves[uiCount].sTop,
									gBackSaves[uiCount].sRight, gBackSaves[uiCount].sBottom);
			}
		}
	}

	UnLockVideoSurface(guiRENDERBUFFER);
	UnLockVideoSurface(guiSAVEBUFFER);

	EmptyBackgroundRects( );

	return(TRUE);
}


BOOLEAN EmptyBackgroundRects(void)
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
	{
		if(gBackSaves[uiCount].fFilled)
		{
			gBackSaves[uiCount].fFilled=FALSE;

			if(!(gBackSaves[uiCount].fAllocated) && (gBackSaves[uiCount].fFreeMemory==TRUE))
			{
				if ( gBackSaves[uiCount].uiFlags & BGND_FLAG_SAVERECT )
				{
					if ( gBackSaves[uiCount].pSaveArea != NULL )
					{
						SurfaceData::ReleaseApplicationData((BYTE*)gBackSaves[uiCount].pSaveArea);
						MemFree(gBackSaves[uiCount].pSaveArea);
					}
				}
				if(gBackSaves[uiCount].fZBuffer)
				{
					SurfaceData::ReleaseApplicationData((BYTE*)gBackSaves[uiCount].pZSaveArea);
					MemFree(gBackSaves[uiCount].pZSaveArea);
				}

				gBackSaves[uiCount].fZBuffer=FALSE;
				gBackSaves[uiCount].fAllocated=FALSE;
				gBackSaves[uiCount].fFreeMemory=FALSE;
				gBackSaves[uiCount].fFilled=FALSE;
				gBackSaves[uiCount].pSaveArea=NULL;

				RecountBackgrounds();
			}
		}

		if(gBackSaves[uiCount].uiFlags&BGND_FLAG_SINGLE || gBackSaves[uiCount].fPendingDelete )
		{
			if(gBackSaves[uiCount].fFreeMemory==TRUE)
			{
				if ( gBackSaves[uiCount].uiFlags & BGND_FLAG_SAVERECT )
				{
					if ( gBackSaves[uiCount].pSaveArea != NULL )
					{
						SurfaceData::ReleaseApplicationData((BYTE*)gBackSaves[uiCount].pSaveArea);
						MemFree(gBackSaves[uiCount].pSaveArea);
					}
				}

				if(gBackSaves[uiCount].fZBuffer)
				{
					SurfaceData::ReleaseApplicationData((BYTE*)gBackSaves[uiCount].pZSaveArea);
					MemFree(gBackSaves[uiCount].pZSaveArea);
				}
			}

			gBackSaves[uiCount].fZBuffer=FALSE;
			gBackSaves[uiCount].fAllocated=FALSE;
			gBackSaves[uiCount].fFreeMemory=FALSE;
			gBackSaves[uiCount].fFilled=FALSE;
			gBackSaves[uiCount].pSaveArea=NULL;
			gBackSaves[uiCount].fPendingDelete = FALSE;

			RecountBackgrounds();
		}
	}

	return(TRUE);
}


BOOLEAN SaveBackgroundRects(void)
{
	UINT32 uiCount, uiDestPitchBYTES, uiSrcPitchBYTES;
	UINT8	*pDestBuf, *pSrcBuf;

	pSrcBuf = LockVideoSurface(guiRENDERBUFFER, &uiDestPitchBYTES );
	pDestBuf = LockVideoSurface(guiSAVEBUFFER, &uiSrcPitchBYTES);

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
	{
		if(gBackSaves[uiCount].fAllocated && ( !gBackSaves[uiCount].fDisabled) )
		{
			if ( gBackSaves[uiCount].uiFlags & BGND_FLAG_SAVERECT )
			{
				if ( gBackSaves[uiCount].pSaveArea != NULL )
				{
					Blt16BPPTo16BPP((UINT16 *)gBackSaves[uiCount].pSaveArea, gBackSaves[uiCount].sWidth*2,
							(UINT16 *)pSrcBuf, uiDestPitchBYTES,
							0, 0,
							gBackSaves[uiCount].sLeft , gBackSaves[uiCount].sTop,
							gBackSaves[uiCount].sWidth, gBackSaves[uiCount].sHeight);

				}
			}
			else if(gBackSaves[uiCount].fZBuffer)
			{
				Blt16BPPTo16BPP((UINT16 *)gBackSaves[uiCount].pZSaveArea, gBackSaves[uiCount].sWidth*2,
							(UINT16 *)gpZBuffer, uiDestPitchBYTES,
							0, 0,
							gBackSaves[uiCount].sLeft , gBackSaves[uiCount].sTop,
							gBackSaves[uiCount].sWidth, gBackSaves[uiCount].sHeight);
			}
			else
			{
				AddBaseDirtyRect(gBackSaves[uiCount].sLeft, gBackSaves[uiCount].sTop,
									gBackSaves[uiCount].sRight, gBackSaves[uiCount].sBottom);
			}

			gBackSaves[uiCount].fFilled=TRUE;
		}
	}

	UnLockVideoSurface(guiRENDERBUFFER);
	UnLockVideoSurface(guiSAVEBUFFER);

	return(TRUE);
}


BOOLEAN FreeBackgroundRect(INT32 iIndex)
{
	if ( iIndex != -1 )
	{
		gBackSaves[iIndex].fAllocated=FALSE;

		RecountBackgrounds();
	}

	return(TRUE);
}

BOOLEAN FreeBackgroundRectPending(INT32 iIndex)
{
	if (iIndex != -1)
	{
		gBackSaves[iIndex].fPendingDelete = TRUE;
	}
	return(TRUE);
}


BOOLEAN FreeBackgroundRectNow(INT32 uiCount)
{
	if(gBackSaves[uiCount].fFreeMemory==TRUE)
	{
		//MemFree(gBackSaves[uiCount].pSaveArea);
		if(gBackSaves[uiCount].fZBuffer)
		{
			SurfaceData::ReleaseApplicationData((BYTE*)gBackSaves[uiCount].pZSaveArea);
			MemFree(gBackSaves[uiCount].pZSaveArea);
		}
	}

	gBackSaves[uiCount].fZBuffer=FALSE;
	gBackSaves[uiCount].fAllocated=FALSE;
	gBackSaves[uiCount].fFreeMemory=FALSE;
	gBackSaves[uiCount].fFilled=FALSE;
	gBackSaves[uiCount].pSaveArea=NULL;

	RecountBackgrounds();
	return(TRUE);
}

BOOLEAN FreeBackgroundRectType(UINT32 uiFlags)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
	{
		if(gBackSaves[uiCount].uiFlags&uiFlags)
		{
			if(gBackSaves[uiCount].fFreeMemory==TRUE)
			{
				//MemFree(gBackSaves[uiCount].pSaveArea);
				if(gBackSaves[uiCount].fZBuffer)
				{
					SurfaceData::ReleaseApplicationData((BYTE*)gBackSaves[uiCount].pZSaveArea);
					MemFree(gBackSaves[uiCount].pZSaveArea);
				}
			}

			gBackSaves[uiCount].fZBuffer=FALSE;
			gBackSaves[uiCount].fAllocated=FALSE;
			gBackSaves[uiCount].fFreeMemory=FALSE;
			gBackSaves[uiCount].fFilled=FALSE;
			gBackSaves[uiCount].pSaveArea=NULL;
		}
	}

	RecountBackgrounds();

	return(TRUE);
}


BOOLEAN InitializeBackgroundRects(void)
{
	guiNumBackSaves=0;
	return( TRUE );
}

BOOLEAN InvalidateBackgroundRects(void)
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
		gBackSaves[uiCount].fFilled=FALSE;

	return(TRUE);
}


BOOLEAN ShutdownBackgroundRects(void)
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumBackSaves; uiCount++)
	{
		if(gBackSaves[uiCount].fAllocated)
			FreeBackgroundRectNow((INT32)uiCount);
	}

	return(TRUE);
}

void DisableBackgroundRect( INT32 iIndex, BOOLEAN fDisabled )
{
	gBackSaves[iIndex].fDisabled = fDisabled;
}

BOOLEAN UpdateSaveBuffer(void)
{
	UINT32 uiDestPitchBYTES, uiSrcPitchBYTES;
	UINT8	*pDestBuf, *pSrcBuf;
	UINT16 usWidth, usHeight;
	UINT8	ubBitDepth;

	// Update saved buffer - do for the viewport size ony!
	GetCurrentVideoSettings( &usWidth, &usHeight, &ubBitDepth );

	pSrcBuf = LockVideoSurface(guiRENDERBUFFER, &uiSrcPitchBYTES);
	pDestBuf = LockVideoSurface(guiSAVEBUFFER, &uiDestPitchBYTES);

	if(gbPixelDepth==16)
	{
		// BLIT HERE
		Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
					(UINT16 *)pSrcBuf, uiSrcPitchBYTES,
					0, gsVIEWPORT_WINDOW_START_Y, 0, gsVIEWPORT_WINDOW_START_Y, usWidth, ( gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y )	);
	}
	else if(gbPixelDepth==8)
	{
		// BLIT HERE
		Blt8BPPTo8BPP(pDestBuf, uiDestPitchBYTES,
					pSrcBuf, uiSrcPitchBYTES,
					0, gsVIEWPORT_WINDOW_START_Y, 0, gsVIEWPORT_WINDOW_START_Y, usWidth, gsVIEWPORT_WINDOW_END_Y );
	}

	UnLockVideoSurface(guiRENDERBUFFER);
	UnLockVideoSurface(guiSAVEBUFFER);

	return(TRUE);
}


BOOLEAN RestoreExternBackgroundRect( INT16 sLeft, INT16 sTop, INT16 sWidth, INT16 sHeight )
{
	UINT32 uiDestPitchBYTES, uiSrcPitchBYTES;
	UINT8	*pDestBuf, *pSrcBuf;

//Heinz (18.01.2009): fixed RUNTIME ERROR when user use screen resolution 640x480
	//Assert( ( sLeft >= 0 ) && ( sTop >= 0 ) && ( sLeft + sWidth <= SCREEN_WIDTH ) && ( sTop + sHeight <= SCREEN_HEIGHT ) );
	if(sLeft < 0) sLeft = 0;
	if(sTop <0 ) sTop = 0;
	if(sLeft > SCREEN_WIDTH) sLeft = SCREEN_WIDTH;
	if(sTop > SCREEN_HEIGHT) sTop = SCREEN_HEIGHT;
	if(sLeft + sWidth > SCREEN_WIDTH) sWidth = SCREEN_WIDTH - sLeft;
	if(sTop + sHeight > SCREEN_HEIGHT) sHeight = SCREEN_HEIGHT - sTop;

	pDestBuf = LockVideoSurface(guiRENDERBUFFER, &uiDestPitchBYTES);
	pSrcBuf = LockVideoSurface(guiSAVEBUFFER, &uiSrcPitchBYTES);

	if(gbPixelDepth==16)
	{
		Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
					(UINT16 *)pSrcBuf, uiSrcPitchBYTES,
					sLeft , sTop,
					sLeft , sTop,
					sWidth, sHeight);
	}
	else if(gbPixelDepth==8)
	{
		Blt8BPPTo8BPP(pDestBuf, uiDestPitchBYTES,
					pSrcBuf, uiSrcPitchBYTES,
					sLeft , sTop,
					sLeft , sTop,
					sWidth, sHeight);
	}
	UnLockVideoSurface(guiRENDERBUFFER);
	UnLockVideoSurface(guiSAVEBUFFER);

	// Add rect to frame buffer queue
	InvalidateRegionEx( sLeft, sTop, (sLeft + sWidth), ( sTop + sHeight ), 0 );

	return(TRUE);
}

BOOLEAN RestoreExternBackgroundRect(SGPRectangle rect)
{
	return RestoreExternBackgroundRect(rect.x, rect.y, rect.width, rect.height);
}


BOOLEAN RestoreExternBackgroundRectGivenID( INT32 iBack )
{
	UINT32 uiDestPitchBYTES, uiSrcPitchBYTES;
	INT16 sLeft, sTop, sWidth, sHeight;
	UINT8	*pDestBuf, *pSrcBuf;

	if( !gBackSaves[iBack].fAllocated )
	{
		return( FALSE );
	}

	sLeft	= gBackSaves[iBack].sLeft;
	sTop	= gBackSaves[iBack].sTop;
	sWidth	= gBackSaves[iBack].sWidth;
	sHeight	= gBackSaves[iBack].sHeight;

	Assert( ( sLeft >= 0 ) && ( sTop >= 0 ) && ( sLeft + sWidth <= SCREEN_WIDTH ) && ( sTop + sHeight <= SCREEN_HEIGHT ) );

	pDestBuf = LockVideoSurface(guiRENDERBUFFER, &uiDestPitchBYTES);
	pSrcBuf = LockVideoSurface(guiSAVEBUFFER, &uiSrcPitchBYTES);

	if(gbPixelDepth==16)
	{
		Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
					(UINT16 *)pSrcBuf, uiSrcPitchBYTES,
					sLeft , sTop,
					sLeft , sTop,
					sWidth, sHeight);
	}
	else if(gbPixelDepth==8)
	{
		Blt8BPPTo8BPP(pDestBuf, uiDestPitchBYTES,
					pSrcBuf, uiSrcPitchBYTES,
					sLeft , sTop,
					sLeft , sTop,
					sWidth, sHeight);
	}
	UnLockVideoSurface(guiRENDERBUFFER);
	UnLockVideoSurface(guiSAVEBUFFER);

	// Add rect to frame buffer queue
	InvalidateRegionEx( sLeft, sTop, (sLeft + sWidth), ( sTop + sHeight ), 0 );

	return(TRUE);
}

BOOLEAN CopyExternBackgroundRect( INT16 sLeft, INT16 sTop, INT16 sWidth, INT16 sHeight )
{
	UINT32 uiDestPitchBYTES, uiSrcPitchBYTES;
	UINT8	*pDestBuf, *pSrcBuf;

	Assert( ( sLeft >= 0 ) && ( sTop >= 0 ) && ( sLeft + sWidth <= SCREEN_WIDTH ) && ( sTop + sHeight <= SCREEN_HEIGHT ) );

	pDestBuf = LockVideoSurface(guiSAVEBUFFER, &uiDestPitchBYTES);
	pSrcBuf = LockVideoSurface(guiRENDERBUFFER, &uiSrcPitchBYTES);

	if(gbPixelDepth==16)
	{
		Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
					(UINT16 *)pSrcBuf, uiSrcPitchBYTES,
					sLeft , sTop,
					sLeft , sTop,
					sWidth, sHeight);
	}
	else if(gbPixelDepth==8)
	{
		Blt8BPPTo8BPP(pDestBuf, uiDestPitchBYTES,
					pSrcBuf, uiSrcPitchBYTES,
					sLeft , sTop,
					sLeft , sTop,
					sWidth, sHeight);
	}
	UnLockVideoSurface(guiSAVEBUFFER);
	UnLockVideoSurface(guiRENDERBUFFER);

	return(TRUE);
}

//*****************************************************************************
// gprintfdirty
//
//		Dirties a single-frame rect exactly the size needed to save the
// background for a given call to gprintf. Note that this must be called before
// the backgrounds are saved, and before the actual call to gprintf that writes
// to the video buffer.
//
//*****************************************************************************
UINT16 gprintfdirty(INT16 x, INT16 y, STR16 pFontString, ...)
{
	va_list argptr;
	CHAR16	string[512];
	UINT16 uiStringLength, uiStringHeight;
	INT32 iBack;

	Assert(pFontString!=NULL);

	va_start(argptr, pFontString);			// Set up variable argument pointer
	vswprintf(string, pFontString, argptr);	// process gprintf string (get output str)
	va_end(argptr);

	uiStringLength=StringPixLength(string, FontDefault);
	uiStringHeight=GetFontHeight(FontDefault);

	if ( uiStringLength > 0 )
	{
		iBack = RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, x, y, (INT16)(x + uiStringLength), (INT16)(y + uiStringHeight));

		if ( iBack != -1 )
		{
			SetBackgroundRectFilled( iBack );
		}
	}

	return(uiStringLength);
}

UINT16 gprintfinvalidate(INT16 x, INT16 y, STR16 pFontString, ...)
{
	va_list argptr;
	CHAR16	string[512];
	UINT16 uiStringLength, uiStringHeight;

	Assert(pFontString!=NULL);

	va_start(argptr, pFontString);			// Set up variable argument pointer
	vswprintf(string, pFontString, argptr);	// process gprintf string (get output str)
	va_end(argptr);

	uiStringLength=StringPixLength(string, FontDefault);
	uiStringHeight=GetFontHeight(FontDefault);

	if ( uiStringLength > 0 )
	{
		InvalidateRegionEx( x, y, (INT16)(x + uiStringLength), (INT16)(y + uiStringHeight), 0 );
	}
	return(uiStringLength);
}

UINT16 gprintfRestore(INT16 x, INT16 y, STR16 pFontString, ...)
{
	va_list argptr;
	CHAR16	string[512];
	UINT16 uiStringLength, uiStringHeight;

	Assert(pFontString!=NULL);

	va_start(argptr, pFontString);			// Set up variable argument pointer
	vswprintf(string, pFontString, argptr);	// process gprintf string (get output str)
	va_end(argptr);

	uiStringLength=StringPixLength(string, FontDefault);
	uiStringHeight=GetFontHeight(FontDefault);

	if ( uiStringLength > 0 )
	{
		RestoreExternBackgroundRect( x, y, uiStringLength, uiStringHeight );
	}

	return(uiStringLength);
}

// OVERLAY STUFF
INT32 GetFreeVideoOverlay(void)
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumVideoOverlays; uiCount++)
	{
		if((gVideoOverlays[uiCount].fAllocated==FALSE ) )
			return((INT32)uiCount);
	}

	if( guiNumVideoOverlays < BACKGROUND_BUFFERS )
		return((INT32)guiNumVideoOverlays++);

	return(-1);
}

void RecountVideoOverlays(void)
{
	INT32 uiCount;

	for(uiCount=guiNumVideoOverlays-1; (uiCount >=0) ; uiCount--)
	{
		if((gVideoOverlays[uiCount].fAllocated) )
		{
			guiNumVideoOverlays=(UINT32)(uiCount+1);
			break;
		}
	}
}

INT32 RegisterVideoOverlay( UINT32 uiFlags, VIDEO_OVERLAY_DESC *pTopmostDesc )
{
	UINT32 iBlitterIndex;
	UINT32 iBackIndex;
	UINT16 uiStringLength, uiStringHeight;

	if ( uiFlags & VOVERLAY_DIRTYBYTEXT )
	{
		// Get dims by supplied text
		if ( pTopmostDesc->pzText == NULL )
		{
			return( -1 );
		}

		uiStringLength=StringPixLength( pTopmostDesc->pzText, pTopmostDesc->uiFontID );
		uiStringHeight=GetFontHeight( pTopmostDesc->uiFontID );

		iBackIndex = RegisterBackgroundRect( BGND_FLAG_PERMANENT, NULL, pTopmostDesc->sLeft, pTopmostDesc->sTop, (INT16)(pTopmostDesc->sLeft + uiStringLength), (INT16)(pTopmostDesc->sTop + uiStringHeight) );
	}
	else
	{
		// Register background
		iBackIndex = RegisterBackgroundRect( BGND_FLAG_PERMANENT, NULL, pTopmostDesc->sLeft, pTopmostDesc->sTop, pTopmostDesc->sRight, pTopmostDesc->sBottom );
	}

	if ( iBackIndex == -1 )
	{
		return( -1 );
	}

	// Get next free topmost blitter index
	if( ( iBlitterIndex = GetFreeVideoOverlay())==(-1))
		return(-1);

	// Init new blitter
	memset(&gVideoOverlays[ iBlitterIndex ], 0, sizeof( VIDEO_OVERLAY ) );

	gVideoOverlays[ iBlitterIndex ].uiFlags			= uiFlags;
	gVideoOverlays[ iBlitterIndex ].fAllocated	= 2;
	gVideoOverlays[ iBlitterIndex ].uiBackground = iBackIndex;
	gVideoOverlays[ iBlitterIndex ].pBackground	= &( gBackSaves[ iBackIndex ] );
	gVideoOverlays[ iBlitterIndex ].BltCallback	= pTopmostDesc->BltCallback;

	// Update blitter info
	// Set update flags to zero since we are forcing all updates
	pTopmostDesc->uiFlags = 0;
	UpdateVideoOverlay( pTopmostDesc, iBlitterIndex , TRUE );

	// Set disabled flag to true
	if ( uiFlags & VOVERLAY_STARTDISABLED )
	{
		gVideoOverlays[ iBlitterIndex ].fDisabled		= TRUE;
		DisableBackgroundRect( gVideoOverlays[ iBlitterIndex ].uiBackground, TRUE );
	}

	gVideoOverlays[ iBlitterIndex ].uiDestBuff = FRAME_BUFFER;

	//DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Register Overlay %d %S", iBlitterIndex, gVideoOverlays[ iBlitterIndex ].zText ) );

	return( iBlitterIndex );
}

void SetVideoOverlayPendingDelete( INT32 iVideoOverlay )
{
	if ( iVideoOverlay != -1 )
	{
		gVideoOverlays[ iVideoOverlay ].fDeletionPending = TRUE;
	}
}

void RemoveVideoOverlay( INT32 iVideoOverlay )
{

	if ( iVideoOverlay != -1 && gVideoOverlays[ iVideoOverlay ].fAllocated )
	{
		// Check if we are actively scrolling
		if ( gVideoOverlays[ iVideoOverlay ].fActivelySaving )
		{
	//		DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Overlay Actively saving %d %S", iVideoOverlay, gVideoOverlays[ iVideoOverlay ].zText ) );

			gVideoOverlays[ iVideoOverlay ].fDeletionPending = TRUE;
		}
		else
		{
			//RestoreExternBackgroundRectGivenID( gVideoOverlays[ iVideoOverlay ].uiBackground );

			// Remove background
			FreeBackgroundRect( gVideoOverlays[ iVideoOverlay ].uiBackground );

			//DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Delete Overlay %d %S", iVideoOverlay, gVideoOverlays[ iVideoOverlay ].zText ) );

			// Remove save buffer if not done so
			if ( gVideoOverlays[ iVideoOverlay ].pSaveArea != NULL )
			{
				SurfaceData::ReleaseApplicationData((BYTE*)gVideoOverlays[ iVideoOverlay ].pSaveArea);
				MemFree( gVideoOverlays[ iVideoOverlay ].pSaveArea );
			}
			gVideoOverlays[ iVideoOverlay ].pSaveArea = NULL;

			// Set as not allocated
			gVideoOverlays[ iVideoOverlay ].fAllocated	= FALSE;
		}
	}
}

BOOLEAN UpdateVideoOverlay( VIDEO_OVERLAY_DESC *pTopmostDesc, UINT32 iBlitterIndex, BOOLEAN fForceAll )
{
	UINT32 uiFlags;
	UINT16 uiStringLength, uiStringHeight;

	if ( iBlitterIndex != -1 )
	{

		if ( !gVideoOverlays[ iBlitterIndex ].fAllocated )
		{
			return( FALSE );
		}

		uiFlags = pTopmostDesc->uiFlags;

		if ( fForceAll )
		{
			gVideoOverlays[ iBlitterIndex ].uiFontID	= pTopmostDesc->uiFontID;
			gVideoOverlays[ iBlitterIndex ].sX			= pTopmostDesc->sX;
			gVideoOverlays[ iBlitterIndex ].sY			= pTopmostDesc->sY;
			gVideoOverlays[ iBlitterIndex ].ubFontBack	= pTopmostDesc->ubFontBack;
			gVideoOverlays[ iBlitterIndex ].ubFontFore	= pTopmostDesc->ubFontFore;

			if ( pTopmostDesc->pzText != NULL )
			{
				wcscpy( gVideoOverlays[ iBlitterIndex ].zText, pTopmostDesc->pzText );
			}
		}
		else
		{
			if ( uiFlags & VOVERLAY_DESC_TEXT )
			{
				if ( pTopmostDesc->pzText != NULL )
				{
					wcscpy( gVideoOverlays[ iBlitterIndex ].zText, pTopmostDesc->pzText );
				}
			}


			if ( uiFlags & VOVERLAY_DESC_DISABLED )
			{
				gVideoOverlays[ iBlitterIndex ].fDisabled = pTopmostDesc->fDisabled;
				DisableBackgroundRect( gVideoOverlays[ iBlitterIndex ].uiBackground, pTopmostDesc->fDisabled );
			}

			// If position has changed and flags are of type that use dirty rects, adjust
			if ( ( uiFlags & VOVERLAY_DESC_POSITION ) )
			{

				if ( gVideoOverlays[ iBlitterIndex ].uiFlags & VOVERLAY_DIRTYBYTEXT )
				{
					// Get dims by supplied text
					if ( pTopmostDesc->pzText == NULL )
					{
						return( FALSE );
					}

					uiStringLength=StringPixLength( gVideoOverlays[ iBlitterIndex ].zText,gVideoOverlays[ iBlitterIndex ].uiFontID );
					uiStringHeight=GetFontHeight( gVideoOverlays[ iBlitterIndex ].uiFontID );

					// Delete old rect
					// Remove background
					FreeBackgroundRectPending( gVideoOverlays[ iBlitterIndex ].uiBackground );

					gVideoOverlays[ iBlitterIndex ].uiBackground = RegisterBackgroundRect( BGND_FLAG_PERMANENT, NULL, pTopmostDesc->sLeft, pTopmostDesc->sTop, (INT16)(pTopmostDesc->sLeft + uiStringLength), (INT16)(pTopmostDesc->sTop + uiStringHeight) );
					gVideoOverlays[ iBlitterIndex ].sX					= pTopmostDesc->sX;
					gVideoOverlays[ iBlitterIndex ].sY					= pTopmostDesc->sY;
				}
			}
		}
	}
	return( TRUE );
}

// FUnctions for entrie array of blitters
void ExecuteVideoOverlays( )
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumVideoOverlays; uiCount++)
	{
		if( gVideoOverlays[uiCount].fAllocated )
		{
			if ( !gVideoOverlays[uiCount].fDisabled )
			{
				// If we are scrolling but havn't saved yet, don't!
				if ( !gVideoOverlays[uiCount].fActivelySaving && gfScrollInertia > 0 )
				{
					continue;
				}

				// ATE: Wait a frame before executing!
				if ( gVideoOverlays[uiCount].fAllocated == 1 )
				{
					// Call Blit Function
					(*(gVideoOverlays[uiCount].BltCallback ) ) ( &(gVideoOverlays[uiCount]) );
				}
				else if ( gVideoOverlays[uiCount].fAllocated == 2 )
				{
					gVideoOverlays[uiCount].fAllocated = 1;
				}
			}

			// Remove if pending
			//if ( gVideoOverlays[uiCount].fDeletionPending )
			//{
			//	RemoveVideoOverlay( uiCount );
			//}
		}
	}

}

void ExecuteVideoOverlaysToAlternateBuffer( UINT32 uiNewDestBuffer )
{
	UINT32	uiCount;
	UINT32	uiOldDestBuffer;

	for(uiCount=0; uiCount < guiNumVideoOverlays; uiCount++)
	{
		if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
		{
			if ( gVideoOverlays[uiCount].fActivelySaving )
			{
				uiOldDestBuffer =	gVideoOverlays[uiCount].uiDestBuff;

				gVideoOverlays[uiCount].uiDestBuff = uiNewDestBuffer;

				// Call Blit Function
				(*(gVideoOverlays[uiCount].BltCallback ) ) ( &(gVideoOverlays[uiCount]) );

				gVideoOverlays[uiCount].uiDestBuff = uiOldDestBuffer;
			}
		}
	}
}

void AllocateVideoOverlaysArea( )
{
	UINT32 uiCount;
	UINT32 uiBufSize;
	UINT32 iBackIndex;

	for(uiCount=0; uiCount < guiNumVideoOverlays; uiCount++)
	{
		if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
		{
			iBackIndex = gVideoOverlays[uiCount].uiBackground;

			// Get buffer size
			uiBufSize=(( gBackSaves[ iBackIndex ].sRight-gBackSaves[ iBackIndex ].sLeft)*2)*(gBackSaves[ iBackIndex ].sBottom-gBackSaves[ iBackIndex ].sTop );

			gVideoOverlays[uiCount].fActivelySaving = TRUE;

			//DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Setting Overlay Actively saving %d %S", uiCount, gVideoOverlays[ uiCount ].zText ) );

			// Allocate
			if( ( gVideoOverlays[ uiCount ].pSaveArea = (INT16 *) MemAlloc( uiBufSize ) ) == NULL )
			{
				continue;
			}
			BYTE* data = (BYTE*)gVideoOverlays[ uiCount ].pSaveArea;
			SurfaceData::SetApplicationData(data);
			g_SurfaceRectangle[SurfaceData::GetSurfaceID(data)].SetRect(
				gBackSaves[ iBackIndex ].sWidth,
				gBackSaves[ iBackIndex ].sHeight);
		}
	}
}

void AllocateVideoOverlayArea( UINT32 uiCount )
{
	UINT32 uiBufSize;
	UINT32 iBackIndex;

	if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
	{
		iBackIndex = gVideoOverlays[uiCount].uiBackground;

		// Get buffer size
		uiBufSize=(( gBackSaves[ iBackIndex ].sRight-gBackSaves[ iBackIndex ].sLeft)*2)*(gBackSaves[ iBackIndex ].sBottom-gBackSaves[ iBackIndex ].sTop );

		gVideoOverlays[uiCount].fActivelySaving = TRUE;

		//DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Setting Overlay Actively saving %d %S", uiCount, gVideoOverlays[ uiCount ].zText ) );

		// Allocate
		if( ( gVideoOverlays[ uiCount ].pSaveArea = (INT16 *) MemAlloc( uiBufSize ) ) == NULL )
		{
			SGP_THROW(L"Memory allocation failed");
		}
		BYTE* data = (BYTE*)gVideoOverlays[ uiCount ].pSaveArea;
		SurfaceData::SetApplicationData(data);
		g_SurfaceRectangle[SurfaceData::GetSurfaceID(data)].SetRect(
			gBackSaves[ iBackIndex ].sWidth,
			gBackSaves[ iBackIndex ].sHeight);
	}
}

void SaveVideoOverlaysArea( UINT32 uiSrcBuffer )
{
	UINT32 uiCount;
	UINT32 iBackIndex;
	UINT32 uiSrcPitchBYTES;
	UINT8	*pSrcBuf;

	pSrcBuf = LockVideoSurface( uiSrcBuffer, &uiSrcPitchBYTES );

	for(uiCount=0; uiCount < guiNumVideoOverlays; uiCount++)
	{
		if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
		{
			iBackIndex = gVideoOverlays[uiCount].uiBackground;

			// OK, if our saved area is null, allocate it here!
			if ( gVideoOverlays[uiCount].pSaveArea == NULL )
			{
				AllocateVideoOverlayArea( uiCount );
			}

			if ( gVideoOverlays[uiCount].pSaveArea != NULL && gBackSaves[iBackIndex].sHeight > 0 && gBackSaves[iBackIndex].sWidth > 0)
			{
				// Save data from frame buffer!				
				Blt16BPPTo16BPP(
					(UINT16 *)gVideoOverlays[uiCount].pSaveArea, 
					gBackSaves[iBackIndex].sWidth * 2,
					(UINT16 *)pSrcBuf, 
					uiSrcPitchBYTES,
					0, 
					0,
					gBackSaves[iBackIndex].sLeft, 
					gBackSaves[iBackIndex].sTop,
					gBackSaves[iBackIndex].sWidth, 
					gBackSaves[iBackIndex].sHeight
				);				
			}
		}
	}
	UnLockVideoSurface( uiSrcBuffer );
}

void SaveVideoOverlayArea( UINT32 uiSrcBuffer, UINT32 uiCount )
{
	UINT32 iBackIndex;
	UINT32 uiSrcPitchBYTES;
	UINT8	*pSrcBuf;

	pSrcBuf = LockVideoSurface( uiSrcBuffer, &uiSrcPitchBYTES );

	if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
	{
		// OK, if our saved area is null, allocate it here!
		if ( gVideoOverlays[uiCount].pSaveArea == NULL )
		{
			AllocateVideoOverlayArea( uiCount );
		}

		if ( gVideoOverlays[uiCount].pSaveArea != NULL )
		{

			iBackIndex = gVideoOverlays[uiCount].uiBackground;

			// Save data from frame buffer!
			Blt16BPPTo16BPP((UINT16 *)gVideoOverlays[uiCount].pSaveArea, gBackSaves[ iBackIndex ].sWidth*2,
						(UINT16 *)pSrcBuf, uiSrcPitchBYTES,
						0, 0,
						gBackSaves[ iBackIndex ].sLeft , gBackSaves[ iBackIndex ].sTop,
						gBackSaves[ iBackIndex ].sWidth, gBackSaves[ iBackIndex ].sHeight );
		}
	}
	UnLockVideoSurface( uiSrcBuffer );
}

void DeleteVideoOverlaysArea( )
{
	UINT32 uiCount;

	for(uiCount=0; uiCount < guiNumVideoOverlays; uiCount++)
	{
		if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
		{
			if ( gVideoOverlays[uiCount].pSaveArea != NULL )
			{
				SurfaceData::ReleaseApplicationData((BYTE*)gVideoOverlays[ uiCount ].pSaveArea);
				MemFree( gVideoOverlays[uiCount].pSaveArea );
			}

			gVideoOverlays[uiCount].fActivelySaving = FALSE;

			gVideoOverlays[uiCount].pSaveArea = NULL;

			//DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Removing Overlay Actively saving %d %S", uiCount, gVideoOverlays[ uiCount ].zText ) );

			// Remove if pending
			if ( gVideoOverlays[uiCount].fDeletionPending )
			{
				RemoveVideoOverlay( uiCount );
			}
		}
	}
}

BOOLEAN RestoreShiftedVideoOverlays( INT16 sShiftX, INT16 sShiftY )
{
	UINT32 uiCount, uiDestPitchBYTES;
	UINT8	*pDestBuf;
	UINT32 iBackIndex;

	INT32	ClipX1, ClipY1, ClipX2, ClipY2;
	INT32	uiLeftSkip, uiRightSkip, uiTopSkip, uiBottomSkip;
	UINT32	usHeight, usWidth;
	INT32	iTempX, iTempY;
	INT16	sLeft, sTop, sRight, sBottom;

	ClipX1= 0;
	ClipY1= gsVIEWPORT_WINDOW_START_Y;
	ClipX2= SCREEN_WIDTH;
	ClipY2= gsVIEWPORT_WINDOW_END_Y - 1;


	pDestBuf = LockVideoSurface( BACKBUFFER, &uiDestPitchBYTES);

	for(uiCount=0; uiCount <	guiNumVideoOverlays; uiCount++)
	{
		if( gVideoOverlays[uiCount].fAllocated && !gVideoOverlays[uiCount].fDisabled )
		{
				iBackIndex = gVideoOverlays[uiCount].uiBackground;

				if ( gVideoOverlays[uiCount].pSaveArea != NULL )
				{
					// Get restore background values
					sLeft			= gBackSaves[ iBackIndex ].sLeft;
					sTop		= gBackSaves[ iBackIndex ].sTop;
					sRight		= gBackSaves[ iBackIndex ].sRight;
					sBottom		= gBackSaves[ iBackIndex ].sBottom;
					usHeight	= gBackSaves[ iBackIndex ].sHeight;
					usWidth	= gBackSaves[ iBackIndex ].sWidth;

					// Clip!!
					iTempX = sLeft + sShiftX;
					iTempY = sTop + sShiftY;

					// Clip to rect
					uiLeftSkip=__min( ClipX1 - min(ClipX1, iTempX), (INT32)usWidth);
					uiRightSkip=__min(max(ClipX2, (iTempX+(INT32)usWidth)) - ClipX2, (INT32)usWidth);
					uiTopSkip=__min(ClipY1 - __min(ClipY1, iTempY), (INT32)usHeight);
					uiBottomSkip=__min(__max(ClipY2, (iTempY+(INT32)usHeight)) - ClipY2, (INT32)usHeight);

					// check if whole thing is clipped
					if((uiLeftSkip >=(INT32)usWidth) || (uiRightSkip >=(INT32)usWidth))
						continue;

					// check if whole thing is clipped
					if((uiTopSkip >=(INT32)usHeight) || (uiBottomSkip >=(INT32)usHeight))
						continue;

					// Set re-set values given based on clipping
					sLeft	= iTempX + (INT16)uiLeftSkip;
					sTop	 = iTempY + (INT16)uiTopSkip;
					sRight	= sRight + sShiftX - (INT16)uiRightSkip;
					sBottom	= sBottom + sShiftY - (INT16)uiBottomSkip;

					usHeight = sBottom - sTop;
					usWidth	= sRight -	sLeft;

					if(gbPixelDepth==16)
					{

						Blt16BPPTo16BPP((UINT16 *)(UINT16 *)pDestBuf, uiDestPitchBYTES,
									(UINT16 *)gVideoOverlays[uiCount].pSaveArea, gBackSaves[ iBackIndex ].sWidth*2,
									sLeft, sTop,
									uiLeftSkip, uiTopSkip,
									usWidth, usHeight );
					}
					else if(gbPixelDepth==8)
					{
					}

					// Once done, check for pending deletion
					if ( gVideoOverlays[uiCount].fDeletionPending )
					{
						RemoveVideoOverlay( uiCount );
					}
				}
		}
	}

	UnLockVideoSurface( BACKBUFFER );

	return(TRUE);
}

BOOLEAN SetOverlayUserData( INT32 iVideoOverlay, UINT8 ubNum, UINT32 uiData )
{
	if ( !gVideoOverlays[ iVideoOverlay ].fAllocated )
	{
		return( FALSE );
	}

	if ( ubNum > 4 )
	{
		return( FALSE );
	}

	gVideoOverlays[ iVideoOverlay ].uiUserData[ ubNum ] = uiData;

	return ( TRUE );
}


// Common callbacks for topmost blitters
void BlitMFont( VIDEO_OVERLAY *pBlitter )
{
	UINT8	*pDestBuf;
	UINT32 uiDestPitchBYTES;

	pDestBuf = LockVideoSurface( pBlitter->uiDestBuff, &uiDestPitchBYTES);

	SetFont( pBlitter->uiFontID );
	SetFontBackground( pBlitter->ubFontBack );
	SetFontForeground( pBlitter->ubFontFore );

	mprintf_buffer( pDestBuf, uiDestPitchBYTES, pBlitter->uiFontID, pBlitter->sX, pBlitter->sY, pBlitter->zText );

	UnLockVideoSurface( pBlitter->uiDestBuff );
}

BOOLEAN BlitBufferToBuffer(UINT32 uiSrcBuffer, UINT32 uiDestBuffer, UINT16 usSrcX, UINT16 usSrcY, UINT16 usWidth, UINT16 usHeight)
{
	UINT32 uiDestPitchBYTES, uiSrcPitchBYTES;
	UINT8	*pDestBuf, *pSrcBuf;
	BOOLEAN fRetVal;

	pDestBuf = LockVideoSurface(uiDestBuffer, &uiDestPitchBYTES);
	pSrcBuf = LockVideoSurface(uiSrcBuffer, &uiSrcPitchBYTES);

	fRetVal = Blt16BPPTo16BPP( (UINT16*)pDestBuf, uiDestPitchBYTES, (UINT16*)pSrcBuf, uiSrcPitchBYTES,
			usSrcX, usSrcY,
			usSrcX, usSrcY,
			usWidth, usHeight);

	UnLockVideoSurface(uiDestBuffer);
	UnLockVideoSurface(uiSrcBuffer);

	return( fRetVal );
}

void EnableVideoOverlay( BOOLEAN fEnable, INT32 iOverlayIndex )
{
	VIDEO_OVERLAY_DESC		VideoOverlayDesc;

	memset( &VideoOverlayDesc, 0, sizeof( VideoOverlayDesc ) );

	// enable or disable
	VideoOverlayDesc.fDisabled	= !fEnable;

	// go play with enable/disable state
	VideoOverlayDesc.uiFlags	= VOVERLAY_DESC_DISABLED;

	UpdateVideoOverlay( &VideoOverlayDesc, iOverlayIndex, FALSE );
}

