#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 "debug.h"
	#if defined( JA2 ) || defined( UTIL )
		#include "video.h"
	#else
		#include "video2.h"
	#endif
	#include "himage.h"
	#include "vobject.h"
	#include "wcheck.h"
	#include "vobject_blitters.h"
	#include "sgp.h"
#endif

#include <unordered_map>

// ******************************************************************************
//
// Video Object SGP Module
//
// Video Objects are used to contain any imagery which requires blitting. The data
// is contained within a Direct Draw surface. Palette information is in both
// a Direct Draw Palette and a 16BPP palette structure for 8->16 BPP Blits.
// Blitting is done via Direct Draw as well as custum blitters. Regions are
// used to define local coordinates within the surface
//
// 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_VIDEO_OBJECT_LIST_SIZE	10

#define COMPRESS_TRANSPARENT				0x80
#define COMPRESS_RUN_MASK						0x7F

// *******************************************************************************
// External Functions and variables
// *******************************************************************************

// *******************************************************************************
// LOCAL functions
// *******************************************************************************


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



HLIST		ghVideoObjects = NULL;
BOOLEAN	gfVideoObjectsInit=FALSE;

#ifndef SGP_VIDEO_DEBUGGING
	#define USE_HASHMAP_FOR_VOBJECTS 
#endif

#ifdef USE_HASHMAP_FOR_VOBJECTS 
typedef struct VOBJECT_NODE
{
	HVOBJECT hVObject;
	UINT32 uiIndex;
#ifdef SGP_VIDEO_DEBUGGING
	STR8									pName;
	STR8									pCode;
#endif
} VOBJECT_NODE, *VOBJECT_NODE_PTR;

typedef std::unordered_map<UINT32, VOBJECT_NODE_PTR> VOBJECT_MAP;
static VOBJECT_MAP gpVObjectMap;

#else
typedef struct VOBJECT_NODE
{
	HVOBJECT hVObject;
	UINT32 uiIndex;
	struct VOBJECT_NODE *next, *prev;

	#ifdef SGP_VIDEO_DEBUGGING
		STR8									pName;
		STR8									pCode;
	#endif

}VOBJECT_NODE;

VOBJECT_NODE	*gpVObjectHead = NULL;
VOBJECT_NODE	*gpVObjectTail = NULL;
#endif
UINT32				guiVObjectIndex = 1;
UINT32				guiVObjectSize = 0;
UINT32				guiVObjectTotalAdded = 0;

#ifdef _DEBUG
enum
{
	DEBUGSTR_NONE,
	DEBUGSTR_SETVIDEOOBJECTTRANSPARENCY,
	DEBUGSTR_BLTVIDEOOBJECTFROMINDEX,
	DEBUGSTR_SETOBJECTHANDLESHADE,
	DEBUGSTR_GETVIDEOOBJECTETRLESUBREGIONPROPERTIES,
	DEBUGSTR_GETVIDEOOBJECTETRLEPROPERTIESFROMINDEX,
	DEBUGSTR_SETVIDEOOBJECTPALETTE8BPP,
	DEBUGSTR_GETVIDEOOBJECTPALETTE16BPP,
	DEBUGSTR_COPYVIDEOOBJECTPALETTE16BPP,
	DEBUGSTR_BLTVIDEOOBJECTOUTLINEFROMINDEX,
	DEBUGSTR_BLTVIDEOOBJECTOUTLINESHADOWFROMINDEX,
	DEBUGSTR_DELETEVIDEOOBJECTFROMINDEX
};

UINT8 gubVODebugCode = 0;

void CheckValidVObjectIndex( UINT32 uiIndex );
#endif

// **************************************************************
//
// Video Object Manager functions
//
// **************************************************************

int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
	puts("in filter.");
	if (code == EXCEPTION_ACCESS_VIOLATION) {
		puts("caught AV as expected.");
		return EXCEPTION_EXECUTE_HANDLER;
	}

	else
	{
		puts("didn't catch AV, unexpected.");
		return EXCEPTION_CONTINUE_SEARCH;
	};
}

BOOLEAN InitializeVideoObjectManager( )
{
	//Shouldn't be calling this if the video object manager already exists.
	//Call shutdown first...
#ifdef USE_HASHMAP_FOR_VOBJECTS
	Assert( gpVObjectMap.empty() );
	RegisterDebugTopic(TOPIC_VIDEOOBJECT, "Video Object Manager");
#else
	Assert( !gpVObjectHead );
	Assert( !gpVObjectTail );
	RegisterDebugTopic(TOPIC_VIDEOOBJECT, "Video Object Manager");
	gpVObjectHead = gpVObjectTail = NULL;
#endif
	gfVideoObjectsInit=TRUE;
	return TRUE ;
}

BOOLEAN ShutdownVideoObjectManager( )
{
	VOBJECT_NODE *curr;
#ifdef USE_HASHMAP_FOR_VOBJECTS
	for(VOBJECT_MAP::iterator itr=gpVObjectMap.begin(); itr != gpVObjectMap.end(); ++itr )
	{
		curr = (*itr).second;
		DeleteVideoObject( curr->hVObject );
#ifdef SGP_VIDEO_DEBUGGING
		if( curr->pName )
			MemFree( curr->pName );
		if( curr->pCode )
			MemFree( curr->pCode );
#endif
		MemFree( curr );
	}
	gpVObjectMap.clear();
#else
	while( gpVObjectHead )
	{
		curr = gpVObjectHead;
		gpVObjectHead = gpVObjectHead->next;
		DeleteVideoObject( curr->hVObject );
		#ifdef SGP_VIDEO_DEBUGGING
			if( curr->pName )
				MemFree( curr->pName );
			if( curr->pCode )
				MemFree( curr->pCode );
		#endif
		MemFree( curr );
	}
	gpVObjectHead = NULL;
	gpVObjectTail = NULL;
#endif
	guiVObjectIndex = 1;
	guiVObjectSize = 0;
	guiVObjectTotalAdded = 0;
	UnRegisterDebugTopic(TOPIC_VIDEOOBJECT, "Video Objects");
	gfVideoObjectsInit=FALSE;
	return TRUE;
}

UINT32 CountVideoObjectNodes()
{
#ifdef USE_HASHMAP_FOR_VOBJECTS
	return gpVObjectMap.size();
#else
	VOBJECT_NODE *curr;
	UINT32 i = 0;
	curr = gpVObjectHead;
	while( curr )
	{
		i++;
		curr = curr->next;
	}
	return i;
#endif
}

BOOLEAN AddStandardVideoObject( VOBJECT_DESC *pVObjectDesc, UINT32 *puiIndex )
{

	HVOBJECT hVObject;

	// Assertions
	Assert( puiIndex );
	Assert( pVObjectDesc );

	// Create video object
	hVObject = CreateVideoObject( pVObjectDesc );

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

	// Set transparency to default
	SetVideoObjectTransparencyColor( hVObject, FROMRGB( 0, 0, 0 ) );

#ifdef USE_HASHMAP_FOR_VOBJECTS
	{
		VOBJECT_NODE_PTR curr = (VOBJECT_NODE*)MemAlloc( sizeof( VOBJECT_NODE ) );
		memset(curr, 0, sizeof(VOBJECT_NODE));
		curr->hVObject = hVObject;
		curr->uiIndex = guiVObjectIndex++;
		*puiIndex = curr->uiIndex;

		gpVObjectMap[curr->uiIndex] = curr;
	}
#else
	// Set into video object list
	if( gpVObjectHead )
	{ //Add node after tail
		gpVObjectTail->next = (VOBJECT_NODE*)MemAlloc( sizeof( VOBJECT_NODE ) );
		Assert( gpVObjectTail->next ); //out of memory?
		gpVObjectTail->next->prev = gpVObjectTail;
		gpVObjectTail->next->next = NULL;
		gpVObjectTail = gpVObjectTail->next;
	}
	else
	{ //new list
		gpVObjectHead = (VOBJECT_NODE*)MemAlloc( sizeof( VOBJECT_NODE ) );
		Assert( gpVObjectHead ); //out of memory?
		gpVObjectHead->prev = gpVObjectHead->next = NULL;
		gpVObjectTail = gpVObjectHead;
	}
	#ifdef SGP_VIDEO_DEBUGGING
		gpVObjectTail->pName = NULL;
		gpVObjectTail->pCode = NULL;
	#endif
	//Set the hVObject into the node.
	gpVObjectTail->hVObject = hVObject;
	gpVObjectTail->uiIndex = guiVObjectIndex+=2;
	*puiIndex = gpVObjectTail->uiIndex;
#endif
	Assert( guiVObjectIndex < 0xfffffff0 ); //unlikely that we will ever use 2 billion vobjects!
	//We would have to create about 70 vobjects per second for 1 year straight to achieve this...
	guiVObjectSize++;
	guiVObjectTotalAdded++;

	#ifdef JA2TESTVERSION
		if( CountVideoObjectNodes() != guiVObjectSize )
		{
			guiVObjectSize = guiVObjectSize;
		}
	#endif

	return TRUE ;
}


BOOLEAN SetVideoObjectTransparency( UINT32 uiIndex, COLORVAL TransColor )
{
	HVOBJECT hVObject;

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_SETVIDEOOBJECTTRANSPARENCY;
	#endif
	CHECKF( GetVideoObject( &hVObject, uiIndex ) );

	// Set transparency
	SetVideoObjectTransparencyColor( hVObject, TransColor );

	return( TRUE );
}


BOOLEAN GetVideoObject( HVOBJECT *hVObject, UINT32 uiIndex )
{

	#ifdef _DEBUG
		CheckValidVObjectIndex( uiIndex );
	#endif

#ifdef USE_HASHMAP_FOR_VOBJECTS
	VOBJECT_MAP::iterator itr = gpVObjectMap.find(uiIndex);
	if (itr != gpVObjectMap.end())
	{
		*hVObject = (*itr).second->hVObject;
		return TRUE;
	}
#else
	VOBJECT_NODE *curr;
	curr = gpVObjectHead;
	while( curr )
	{
		if( curr->uiIndex == uiIndex )
		{
			*hVObject = curr->hVObject;
			return TRUE;
		}
		curr = curr->next;
	}
#endif
	*hVObject = NULL;
	return FALSE;
}

BOOLEAN BltVideoObjectFromIndex(UINT32 uiDestVSurface, UINT32 uiSrcVObject, UINT16 usRegionIndex, INT32 iDestX, INT32 iDestY, UINT32 fBltFlags, blt_fx *pBltFx )
{
	UINT16				*pBuffer;
	UINT32								uiPitch;
	HVOBJECT							hSrcVObject;

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

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

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_BLTVIDEOOBJECTFROMINDEX;
	#endif
	if( !GetVideoObject( &hSrcVObject, uiSrcVObject ) )
	{
		UnLockVideoSurface( uiDestVSurface );
		return FALSE;
	}

	// Now we have the video object and surface, call the VO blitter function
	if ( !BltVideoObjectToBuffer( pBuffer, uiPitch, hSrcVObject, usRegionIndex, iDestX, iDestY, fBltFlags, pBltFx ) )
	{
		UnLockVideoSurface( uiDestVSurface );
		// VO Blitter will set debug messages for error conditions
		return FALSE;
	}

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}


BOOLEAN DeleteVideoObjectFromIndex( UINT32 uiVObject	)
{
	VOBJECT_NODE *curr;

	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_DELETEVIDEOOBJECTFROMINDEX;
		CheckValidVObjectIndex( uiVObject );
	#endif

#ifdef USE_HASHMAP_FOR_VOBJECTS
		VOBJECT_MAP::iterator itr = gpVObjectMap.find(uiVObject);
		if (itr != gpVObjectMap.end())
		{
			curr = (*itr).second;

			DeleteVideoObject( curr->hVObject );

			//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 );
			curr = NULL;
			guiVObjectSize--;

			gpVObjectMap.erase(itr);
#ifdef JA2TESTVERSION
			if( CountVideoObjectNodes() != guiVObjectSize )
			{
				guiVObjectSize = guiVObjectSize;
				DebugBreakpoint();
			}
#endif
			return TRUE;
		}
#else
	curr = gpVObjectHead;
	while( curr )
	{
		if( curr->uiIndex == uiVObject )
		{ //Found the node, so detach it and delete it.

			//Deallocate the memory for the video object
			DeleteVideoObject( curr->hVObject );

			if( curr == gpVObjectHead )
			{ //Advance the head, because we are going to remove the head node.
				gpVObjectHead = gpVObjectHead->next;
			}
			if( curr == gpVObjectTail )
			{ //Back up the tail, because we are going to remove the tail node.
				gpVObjectTail = gpVObjectTail->prev;
			}
			//Detach the node from the vobject 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 );
			curr = NULL;
			guiVObjectSize--;
			#ifdef JA2TESTVERSION
				if( CountVideoObjectNodes() != guiVObjectSize )
				{
					guiVObjectSize = guiVObjectSize;
				}
			#endif
			return TRUE;
		}
		curr = curr->next;
	}
#endif
	return FALSE;
}



// Given an index to the dest and src vobject contained in ghVideoObjects
// Based on flags, blit accordingly
// There are two types, a BltFast and a Blt. BltFast is 10% faster, uses no
// clipping lists
BOOLEAN BltVideoObject(	UINT32	uiDestVSurface,
												HVOBJECT hSrcVObject,
												UINT16 usRegionIndex,
												INT32	iDestX,
												INT32	iDestY,
												UINT32 fBltFlags,
												blt_fx *pBltFx )
{

	UINT16								*pBuffer;
	UINT32								uiPitch;

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

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

	// Now we have the video object and surface, call the VO blitter function
	if ( !BltVideoObjectToBuffer( pBuffer, uiPitch, hSrcVObject, usRegionIndex, iDestX, iDestY, fBltFlags, pBltFx ) )
	{
		UnLockVideoSurface( uiDestVSurface );
		// VO Blitter will set debug messages for error conditions
		return( FALSE );
	}

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}

BOOLEAN BltVideoObject(UINT32 uiDestVSurface, HVOBJECT hSrcVObject, UINT16 usRegionIndex, SGPRectangle Region, UINT32 fBltFlags, blt_fx* pBltFx)
{
	return BltVideoObject(uiDestVSurface, hSrcVObject, usRegionIndex, Region.x, Region.y, fBltFlags, pBltFx);
}
// *******************************************************************************
// Video Object Manipulation Functions
// *******************************************************************************


HVOBJECT CreateVideoObject( VOBJECT_DESC *VObjectDesc )
{
	HVOBJECT						hVObject;
	HIMAGE							hImage;
	ETRLEData						TempETRLEData;
//	UINT32							count;

	// Allocate memory for video object data and initialize
	hVObject = (HVOBJECT) MemAlloc( sizeof( SGPVObject ) );
	CHECKF( hVObject != NULL );
	memset( hVObject, 0, sizeof( SGPVObject ) );

	// default of all members of the vobject is 0

	// Check creation options
//	do
//	{
		if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE || VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMHIMAGE )
		{
			if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE )
			{
				ImageFileType::TestOrder order = ImageFileType::JPC_FALLBACK;

				if(VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMJPC) {
					order = ImageFileType::JPC;
				} else if(VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMJPC_FALLBACK) {
					order = ImageFileType::JPC_FALLBACK;
				} else if(VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMPNG) {
					order = ImageFileType::PNG_FALLBACK;
				} else if(VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMPNG_FALLBACK) {
					order = ImageFileType::PNG_FALLBACK;
				}
				// Create himage object from file
				hImage = CreateImage( VObjectDesc->ImageFile, IMAGE_ALLIMAGEDATA, order );

				if ( hImage == NULL )
				{
						MemFree( hVObject );
						DbgMessage( TOPIC_VIDEOOBJECT, DBG_LEVEL_2, "Invalid Image Filename given" );
						return( NULL );
				}
			}
			else
			{ // create video object from provided hImage
				hImage = VObjectDesc->hImage;
				if ( hImage == NULL )
				{
						MemFree( hVObject );
						DbgMessage( TOPIC_VIDEOOBJECT, DBG_LEVEL_2, "Invalid hImage pointer given" );
						return( NULL );
				}
			}

			if(hImage->ubBitDepth == 32)
			{
				SGP_THROW_IFFALSE(hImage->usNumberOfObjects > 0, L"bad himage");
				
				// create one 16bpp object (that contains 32bpp data)
				hVObject->p16BPPObject = (SixteenBPPObjectInfo*)MemAlloc(sizeof(SixteenBPPObjectInfo));
				if(!hVObject->p16BPPObject)
				{
					SGP_THROW(L"bad alloc");
				}
				memset(hVObject->p16BPPObject, 0, sizeof(SixteenBPPObjectInfo));

				int SIZE = hImage->pETRLEObject[0].usHeight * hImage->pETRLEObject[0].usWidth * sizeof(UINT32);
				hVObject->p16BPPObject->p16BPPData = (UINT16*)MemAlloc(SIZE); // UINT32*
				if(!hVObject->p16BPPObject->p16BPPData)
				{
					MemFree(hVObject->p16BPPObject);
					SGP_THROW(L"bad alloc");
				}
				memcpy(hVObject->p16BPPObject->p16BPPData, hImage->p32BPPData, SIZE);

				hVObject->p16BPPObject->sOffsetX		= hImage->pETRLEObject[0].sOffsetX;
				hVObject->p16BPPObject->sOffsetY		= hImage->pETRLEObject[0].sOffsetY;
				hVObject->p16BPPObject->ubShadeLevel	= 0;
				hVObject->p16BPPObject->usHeight		= hImage->pETRLEObject[0].usHeight;
				hVObject->p16BPPObject->usWidth			= hImage->pETRLEObject[0].usWidth;
				hVObject->p16BPPObject->usRegionIndex	= 0;

				hVObject->usNumberOf16BPPObjects = 1;
				hVObject->ubBitDepth = hImage->ubBitDepth;

				if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE )
				{
					DestroyImage( hImage );
				}

				return hVObject;
			}
			else if(hImage->ubBitDepth == 16)
			{
				SGP_THROW_IFFALSE(hImage->usNumberOfObjects > 0, L"bad himage");

				// create one 16bpp object (that contains 32bpp data)
				hVObject->p16BPPObject = (SixteenBPPObjectInfo*)MemAlloc(sizeof(SixteenBPPObjectInfo));
				if(!hVObject->p16BPPObject)
				{
					SGP_THROW(L"bad alloc");
				}
				memset(hVObject->p16BPPObject, 0, sizeof(SixteenBPPObjectInfo));

				int SIZE = hImage->pETRLEObject[0].usHeight * hImage->pETRLEObject[0].usWidth * sizeof(UINT16);
				hVObject->p16BPPObject->p16BPPData = (UINT16*)MemAlloc(SIZE);
				if(!hVObject->p16BPPObject->p16BPPData)
				{
					MemFree(hVObject->p16BPPObject);
					SGP_THROW(L"bad alloc");
				}
				memcpy(hVObject->p16BPPObject->p16BPPData, hImage->p16BPPData, SIZE);

				hVObject->p16BPPObject->sOffsetX		= hImage->pETRLEObject[0].sOffsetX;
				hVObject->p16BPPObject->sOffsetY		= hImage->pETRLEObject[0].sOffsetY;
				hVObject->p16BPPObject->ubShadeLevel	= 0;
				hVObject->p16BPPObject->usHeight		= hImage->pETRLEObject[0].usHeight;
				hVObject->p16BPPObject->usWidth			= hImage->pETRLEObject[0].usWidth;
				hVObject->p16BPPObject->usRegionIndex	= 0;

				hVObject->usNumberOf16BPPObjects = 1;
				hVObject->ubBitDepth = hImage->ubBitDepth;

				if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE )
				{
					DestroyImage( hImage );
				}

				return hVObject;
			}

			// Check if returned himage is TRLE compressed - return error if not
			if ( ! (hImage->fFlags & IMAGE_TRLECOMPRESSED ) )
			{
					MemFree( hVObject );
					DbgMessage( TOPIC_VIDEOOBJECT, DBG_LEVEL_2, "Invalid Image format given." );
					DestroyImage( hImage );
					return( NULL );
			}

			// Set values from himage
			hVObject->ubBitDepth				= hImage->ubBitDepth;

			// Get TRLE data
			CHECKF( GetETRLEImageData( hImage, &TempETRLEData ) );

			// Set values
			hVObject->usNumberOfObjects	= TempETRLEData.usNumberOfObjects;
			hVObject->pETRLEObject		= TempETRLEData.pETRLEObject;
			hVObject->pPixData			= TempETRLEData.pPixData;
			hVObject->uiSizePixData		= TempETRLEData.uiSizePixData;

			// Set palette from himage
			if ( hImage->ubBitDepth == 8 )
			{
				hVObject->pShade8=ubColorTables[DEFAULT_SHADE_LEVEL];
				hVObject->pGlow8=ubColorTables[0];

				SetVideoObjectPalette( hVObject, hImage->pPalette );

			}

			if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE )
			{
				// Delete himage object
				DestroyImage( hImage );
			}
	//		break;
		}
		else
		{
			MemFree( hVObject );
			DbgMessage( TOPIC_VIDEOOBJECT, DBG_LEVEL_2, "Invalid VObject creation flags given." );
			return( NULL );
		}

		// If here, no special options given, use structure given in paraneters
		// TO DO:

//	}
//	while( FALSE );

	// All is well
//	DbgMessage( TOPIC_VIDEOOBJECT, DBG_LEVEL_3, String("Success in Creating Video Object" ) );

	return( hVObject );
}


// Palette setting is expensive, need to set both DDPalette and create 16BPP palette
BOOLEAN SetVideoObjectPalette( HVOBJECT hVObject, SGPPaletteEntry *pSrcPalette )
{

	Assert( hVObject != NULL );
	Assert( pSrcPalette != NULL );

	// Create palette object if not already done so
	if ( hVObject->pPaletteEntry == NULL )
	{
		// Create palette
		hVObject->pPaletteEntry = (SGPPaletteEntry *) MemAlloc( sizeof( SGPPaletteEntry ) * 256 );
		CHECKF( hVObject->pPaletteEntry != NULL );

		// Copy src into palette
		memcpy( hVObject->pPaletteEntry, pSrcPalette, sizeof( SGPPaletteEntry ) * 256 );

	}
	else
	{
		// Just Change entries
		memcpy( hVObject->pPaletteEntry, pSrcPalette, sizeof( SGPPaletteEntry ) * 256 );
	}

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

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

//	DbgMessage(TOPIC_VIDEOOBJECT, DBG_LEVEL_3, String("Video Object Palette change successfull" ));
	return( TRUE );
}

// Transparency needs to take RGB value and find best fit and place it into DD Surface
// colorkey value.
BOOLEAN SetVideoObjectTransparencyColor( HVOBJECT hVObject, COLORVAL TransColor )
{

	// Assertions
	Assert( hVObject != NULL );

	//Set trans color into video object
	hVObject->TransparentColor = TransColor;

	return( TRUE );
}


// Deletes all palettes, surfaces and region data
BOOLEAN DeleteVideoObject( HVOBJECT hVObject )
{
	UINT16			usLoop;

	// Assertions
	CHECKF( hVObject != NULL );

	DestroyObjectPaletteTables(hVObject);

	// Release palette
	if ( hVObject->pPaletteEntry != NULL )
	{
		MemFree( hVObject->pPaletteEntry );
//		hVObject->pPaletteEntry = NULL;
	}


	if ( hVObject->pPixData != NULL )
	{
		MemFree( hVObject->pPixData );
//		hVObject->pPixData = NULL;
	}

	if ( hVObject->pETRLEObject != NULL )
	{
		MemFree( hVObject->pETRLEObject );
//		hVObject->pETRLEObject = NULL;
	}

	if ( hVObject->ppZStripInfo != NULL )
	{
		for (usLoop = 0; usLoop < hVObject->usNumberOfObjects; usLoop++)
		{
			if (hVObject->ppZStripInfo[usLoop] != NULL)
			{
				MemFree( hVObject->ppZStripInfo[usLoop]->pbZChange );
				MemFree( hVObject->ppZStripInfo[usLoop] );
			}
		}
		MemFree( hVObject->ppZStripInfo );
//		hVObject->ppZStripInfo = NULL;
	}

	if ( hVObject->usNumberOf16BPPObjects > 0)
	{
		for( usLoop = 0; usLoop < hVObject->usNumberOf16BPPObjects; usLoop++)
		{
			MemFree( hVObject->p16BPPObject[usLoop].p16BPPData );
		}
		MemFree( hVObject->p16BPPObject );
	}

	// Release object
	MemFree( hVObject );

	return( TRUE );
}

/**********************************************************************************************
 CreateObjectPaletteTables

		Creates the shading tables for 8-bit brushes. One highlight table is created, based on
	the object-type, 3 brightening tables, 1 normal, and 11 darkening tables. The entries are
	created iteratively, rather than in a loop to allow hand-tweaking of the values. If you
	change the HVOBJECT_SHADE_TABLES symbol, remember to add/delete entries here, it won't
	adjust automagically.

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

UINT16 CreateObjectPaletteTables(HVOBJECT pObj, UINT32 uiType)
{
UINT32 count;

		// this creates the highlight table. Specify the glow-type when creating the tables
		// through uiType, symbols are from VOBJECT.H
	for( count = 0; count < 16; count++ )
	{
		if ( (count == 4) && (pObj->p16BPPPalette == pObj->pShades[ count ]) )
			pObj->pShades[ count ] = NULL;
		else if ( pObj->pShades[ count ] != NULL )
		{
			MemFree( pObj->pShades[ count ] );
			pObj->pShades[ count ] = NULL;
		}
	}

		switch(uiType)
		{
			case HVOBJECT_GLOW_GREEN:	// green glow
				pObj->pShades[0]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 0, 255, 0, TRUE);
				break;
			case HVOBJECT_GLOW_BLUE:	// blue glow
				pObj->pShades[0]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 0, 0, 255, TRUE);
				break;
			case HVOBJECT_GLOW_YELLOW:	// yellow glow
				pObj->pShades[0]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 255, 255, 0, TRUE);
				break;
			case HVOBJECT_GLOW_RED:	// red glow
				pObj->pShades[0]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 255, 0, 0, TRUE);
				break;
		}

		// these are the brightening tables, 115%-150% brighter than original
		pObj->pShades[1]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 293, 293, 293, FALSE);
		pObj->pShades[2]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 281, 281, 281, FALSE);
		pObj->pShades[3]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 268, 268, 268, FALSE);

		// palette 4 is the non-modified palette.
		// if the standard one has already been made, we'll use it
		if(pObj->p16BPPPalette!=NULL)
			pObj->pShades[4]=pObj->p16BPPPalette;
		else
		{
			// or create our own, and assign it to the standard one
			pObj->pShades[4]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 255, 255, 255, FALSE);
			pObj->p16BPPPalette=pObj->pShades[4];
		}

		// the rest are darkening tables, right down to all-black.
		pObj->pShades[5]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 195, 195, 195, FALSE);
		pObj->pShades[6]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 165, 165, 165, FALSE);
		pObj->pShades[7]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 135, 135, 135, FALSE);
		pObj->pShades[8]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 105, 105, 105, FALSE);
		pObj->pShades[9]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 75, 75, 75, FALSE);
		pObj->pShades[10]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 45, 45, 45, FALSE);
		pObj->pShades[11]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 36, 36, 36, FALSE);
		pObj->pShades[12]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 27, 27, 27, FALSE);
		pObj->pShades[13]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 18, 18, 18, FALSE);
		pObj->pShades[14]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 9, 9, 9, FALSE);
		pObj->pShades[15]=Create16BPPPaletteShaded( pObj->pPaletteEntry, 0, 0, 0, FALSE);

		// Set current shade table to neutral color
		pObj->pShadeCurrent=pObj->pShades[4];

		// check to make sure every table got a palette
		for(count=0; (count < HVOBJECT_SHADE_TABLES) && (pObj->pShades[count]!=NULL); count++);


		// return the result of the check
		return(count==HVOBJECT_SHADE_TABLES);
}

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

// High level blit function encapsolates ALL effects and BPP
BOOLEAN BltVideoObjectToBuffer( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJECT hSrcVObject, UINT16 usIndex, INT32 iDestX, INT32 iDestY, INT32 fBltFlags, blt_fx *pBltFx )
{
	// Sometimes an exception is thrown in that method.
//BF	__try
	{
	// Assertions
	//Assert( pBuffer != NULL );
	SGP_THROW_IFFALSE( pBuffer != NULL, L"No Destination Buffer" );

	if ( hSrcVObject == NULL )
	{
		//int breakpoint=0;
	}

	//Assert( hSrcVObject != NULL );
	SGP_THROW_IFFALSE( hSrcVObject != NULL, L"No Source Object" )

	SixteenBPPObjectInfo *image = NULL;
	// Check For Flags and bit depths
	switch( hSrcVObject->ubBitDepth )
	{
			case 32:
				SGP_THROW_IFFALSE(usIndex < hSrcVObject->usNumberOf16BPPObjects, L"index larger that number images");
				image = &hSrcVObject->p16BPPObject[usIndex];
#if 0
				Blt16BPPTo16BPPTrans( pBuffer, uiDestPitchBYTES, 
					image->p16BPPData, image->usWidth * sizeof(UINT16),
					iDestX, iDestY, 
					0, 0, image->usWidth, image->usHeight,
					0 ); 
#else
				Blt32BPPTo16BPPTrans( pBuffer, uiDestPitchBYTES, 
					(UINT32*)image->p16BPPData, image->usWidth * sizeof(UINT32),
					iDestX, iDestY, 
					0, 0, image->usWidth, image->usHeight); 
#endif
				break;

			case 16:
				SGP_THROW_IFFALSE(usIndex < hSrcVObject->usNumberOf16BPPObjects, L"index larger that number images");
				image = &hSrcVObject->p16BPPObject[usIndex];
				if ( fBltFlags & VO_BLT_SRCTRANSPARENCY	)
				{
					Blt16BPPTo16BPPTrans( pBuffer, uiDestPitchBYTES, 
						image->p16BPPData, image->usWidth * sizeof(UINT16),
						iDestX, iDestY, 
						0, 0, image->usWidth, image->usHeight, 0x1F ); // 0x1f = 5 bits of blue
				}
				else
				{
					Blt16BPPTo16BPP( pBuffer, uiDestPitchBYTES, 
						image->p16BPPData, image->usWidth * sizeof(UINT16),
						iDestX, iDestY, 
						0, 0, image->usWidth, image->usHeight );
				}

				break;

			case 8:

				SGP_THROW_IFFALSE( hSrcVObject->usNumberOfObjects > usIndex, L"Video object index is larger than the number of subimages");
				// Switch based on flags given
				do
				{
					if(gbPixelDepth==16)
					{
#ifndef JA2
						if ( fBltFlags & VO_BLT_MIRROR_Y)
						{
							if(!BltIsClipped(hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect))
								Blt8BPPDataTo16BPPBufferTransMirror( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex );
// CLipping version not done -- DB
//								Blt8BPPDataTo16BPPBufferTransMirrorClip( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect);
							break;
						}
						else
#endif
						if ( fBltFlags & VO_BLT_SRCTRANSPARENCY	)
						{
							if(BltIsClipped(hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect))
								Blt8BPPDataTo16BPPBufferTransparentClip( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect);
							else
								Blt8BPPDataTo16BPPBufferTransparent( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex );
							break;
						}
						else if ( fBltFlags & VO_BLT_SHADOW	)
						{
							if(BltIsClipped(hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect))
								Blt8BPPDataTo16BPPBufferShadowClip( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect);
							else
								Blt8BPPDataTo16BPPBufferShadow( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex );
							break;
						}

					}
					else if(gbPixelDepth==8)
					{
						if ( fBltFlags & VO_BLT_SRCTRANSPARENCY	)
						{
							if(BltIsClipped(hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect))
								Blt8BPPDataTo8BPPBufferTransparentClip( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect);
							else
								Blt8BPPDataTo8BPPBufferTransparent( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex );
							break;
						}
						else if ( fBltFlags & VO_BLT_SHADOW	)
						{
							if(BltIsClipped(hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect))
								Blt8BPPDataTo8BPPBufferShadowClip( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect);
							else
								Blt8BPPDataTo8BPPBufferShadow( pBuffer, uiDestPitchBYTES, hSrcVObject, iDestX, iDestY, usIndex );
							break;
						}
					}
					// Use default blitter here
					//Blt8BPPDataTo16BPPBuffer( hDestVObject, hSrcVObject, (UINT16)iDestX, (UINT16)iDestY, (SGPRect*)&SrcRect );

				} while( FALSE );

				break;
	}

	return( TRUE );
	}
	//BF
	//__except(filter(GetExceptionCode(), GetExceptionInformation()))
	//{
	//	return ( TRUE );
	//}
}

BOOLEAN PixelateVideoObjectRect(	UINT32	uiDestVSurface, INT32 X1, INT32 Y1, INT32 X2, INT32 Y2)
{
	UINT16 *pBuffer;
	UINT32 uiPitch;
	SGPRect	area;
	UINT8 uiPattern[8][8]={	{0,1,0,1,0,1,0,1},
													{1,0,1,0,1,0,1,0},
													{0,1,0,1,0,1,0,1},
													{1,0,1,0,1,0,1,0},
													{0,1,0,1,0,1,0,1},
													{1,0,1,0,1,0,1,0},
													{0,1,0,1,0,1,0,1},
													{1,0,1,0,1,0,1,0}};

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

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

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

	// Now we have the video object and surface, call the shadow function
	if(!Blt16BPPBufferPixelateRect(pBuffer, uiPitch, &area, uiPattern))
	{
		UnLockVideoSurface( uiDestVSurface );
		// Blit has failed if false returned
		return( FALSE );
	}

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

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}


/**********************************************************************************************
 DestroyObjectPaletteTables

	Destroys the palette tables of a video object. All memory is deallocated, and
	the pointers set to NULL. Be careful not to try and blit this object until new
	tables are calculated, or things WILL go boom.

**********************************************************************************************/
BOOLEAN DestroyObjectPaletteTables(HVOBJECT hVObject)
{
UINT32 x;
BOOLEAN f16BitPal;

	for ( x = 0; x < HVOBJECT_SHADE_TABLES; x++ )
	{
		if ( !( hVObject->fFlags & VOBJECT_FLAG_SHADETABLE_SHARED ) )
		{
			if ( hVObject->pShades[x] != NULL )
			{
				if ( hVObject->pShades[x] == hVObject->p16BPPPalette )
					f16BitPal = TRUE;
				else
					f16BitPal = FALSE;

				MemFree( hVObject->pShades[x] );
				hVObject->pShades[x] = NULL;

				if ( f16BitPal )
					hVObject->p16BPPPalette = NULL;
			}
		}
	}

	if ( hVObject->p16BPPPalette != NULL )
	{
		MemFree( hVObject->p16BPPPalette );
		hVObject->p16BPPPalette = NULL;
	}

	hVObject->pShadeCurrent=NULL;
	hVObject->pGlow=NULL;

	return(TRUE);

}



UINT16 SetObjectShade(HVOBJECT pObj, UINT32 uiShade)
{
	Assert(pObj!=NULL);
	Assert(uiShade >= 0);
	Assert(uiShade < HVOBJECT_SHADE_TABLES);

	if(pObj->pShades[uiShade]==NULL)
	{
	DbgMessage(TOPIC_VIDEOOBJECT, DBG_LEVEL_2, String("Attempt to set shade level to NULL table"));
		return(FALSE);
	}

	pObj->pShadeCurrent=pObj->pShades[uiShade];
	return(TRUE);
}

UINT16 SetObjectHandleShade(UINT32 uiHandle, UINT32 uiShade)
{
	HVOBJECT hObj;

	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_SETOBJECTHANDLESHADE;
	#endif
	if(!GetVideoObject(&hObj, uiHandle))
	{
	DbgMessage(TOPIC_VIDEOOBJECT, DBG_LEVEL_2, String("Invalid object handle for setting shade level"));
		return(FALSE);
	}
	return(SetObjectShade(hObj, uiShade));
}

/*
UINT16 FillObjectRect(UINT32 iObj, INT32 x1, INT32 y1, INT32 x2, INT32 y2, COLORVAL color32)
{
UINT16	*pBuffer;
UINT32	uiPitch;
//HVSURFACE pSurface;

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


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

	FillRect16BPP(pBuffer, uiPitch, x1, y1, x2, y2, Get16BPPColor(color32));

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

	UnLockVideoSurface(iObj);
}

*/


/********************************************************************************************
	GetETRLEPixelValue

	Given a VOBJECT and ETRLE image index, retrieves the value of the pixel located at the
	given image coordinates. The value returned is an 8-bit palette index
********************************************************************************************/
BOOLEAN GetETRLEPixelValue( UINT8 * pDest, HVOBJECT hVObject, UINT16 usETRLEIndex, UINT16 usX, UINT16 usY )
{
	UINT8 *					pCurrent;
	UINT16					usLoopX = 0;
	UINT16					usLoopY = 0;
	UINT16					ubRunLength;
	ETRLEObject *		pETRLEObject;

	// Do a bunch of checks
	CHECKF( hVObject != NULL );
	CHECKF( usETRLEIndex < hVObject->usNumberOfObjects );

	pETRLEObject = &(hVObject->pETRLEObject[usETRLEIndex]);

	CHECKF( usX < pETRLEObject->usWidth );
	CHECKF( usY < pETRLEObject->usHeight );

	// Assuming everything's okay, go ahead and look...
	pCurrent = &((UINT8 *)hVObject->pPixData)[pETRLEObject->uiDataOffset];

	// Skip past all uninteresting scanlines
	while( usLoopY < usY )
	{
		while( *pCurrent != 0 )
		{
			if (*pCurrent & COMPRESS_TRANSPARENT)
			{
				pCurrent++;
			}
			else
			{
				pCurrent += *pCurrent & COMPRESS_RUN_MASK;
			}
		}
		usLoopY++;
	}

	// Now look in this scanline for the appropriate byte
	do
	{
		ubRunLength = *pCurrent & COMPRESS_RUN_MASK;

		if (*pCurrent & COMPRESS_TRANSPARENT)
		{
			if (usLoopX + ubRunLength >= usX)
			{
				*pDest = 0;
				return( TRUE );
			}
			else
			{
				pCurrent++;
			}
		}
		else
		{
			if (usLoopX + ubRunLength >= usX)
			{
				// skip to the correct byte; skip at least 1 to get past the byte defining the run
				pCurrent += (usX - usLoopX) + 1;
				*pDest = *pCurrent;
				return( TRUE );
			}
			else
			{
				pCurrent += ubRunLength + 1;
			}
		}
		usLoopX += ubRunLength;
	}
	while( usLoopX < usX );
	// huh???
	return( FALSE );
}

BOOLEAN GetVideoObjectETRLEProperties( HVOBJECT hVObject, ETRLEObject *pETRLEObject, UINT16 usIndex )
{
	CHECKF( usIndex >= 0 );
	CHECKF( usIndex < hVObject->usNumberOfObjects );

	memcpy( pETRLEObject, &( hVObject->pETRLEObject[ usIndex ] ), sizeof( ETRLEObject ) );

	return( TRUE );

}

BOOLEAN GetVideoObjectETRLESubregionProperties( UINT32 uiVideoObject, UINT16 usIndex, UINT16 *pusWidth, UINT16 *pusHeight )
{
	HVOBJECT							hVObject;
	ETRLEObject						ETRLEObject;

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_GETVIDEOOBJECTETRLESUBREGIONPROPERTIES;
	#endif
	CHECKF( GetVideoObject( &hVObject, uiVideoObject ) );

	CHECKF( GetVideoObjectETRLEProperties( hVObject, &ETRLEObject, usIndex ) );

	*pusWidth = ETRLEObject.usWidth;
	*pusHeight = ETRLEObject.usHeight;

	return( TRUE );
}


BOOLEAN GetVideoObjectETRLEPropertiesFromIndex( UINT32 uiVideoObject, ETRLEObject *pETRLEObject, UINT16 usIndex )
{
	HVOBJECT							hVObject;

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_GETVIDEOOBJECTETRLEPROPERTIESFROMINDEX;
	#endif
	CHECKF( GetVideoObject( &hVObject, uiVideoObject ) );

	CHECKF( GetVideoObjectETRLEProperties( hVObject, pETRLEObject, usIndex ) );

	return( TRUE );
}

BOOLEAN SetVideoObjectPalette8BPP(INT32 uiVideoObject, SGPPaletteEntry *pPal8)
{
	HVOBJECT							hVObject;

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_SETVIDEOOBJECTPALETTE8BPP;
	#endif
	CHECKF( GetVideoObject( &hVObject, uiVideoObject ) );

	return( SetVideoObjectPalette( hVObject, pPal8 ) );
}


BOOLEAN GetVideoObjectPalette16BPP(INT32 uiVideoObject, UINT16 **ppPal16)
{
	HVOBJECT							hVObject;

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_GETVIDEOOBJECTPALETTE16BPP;
	#endif
	CHECKF( GetVideoObject( &hVObject, uiVideoObject ) );

	*ppPal16 = hVObject->p16BPPPalette;

	return( TRUE );
}

BOOLEAN CopyVideoObjectPalette16BPP(INT32 uiVideoObject, UINT16 *ppPal16)
{
	HVOBJECT							hVObject;

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_COPYVIDEOOBJECTPALETTE16BPP;
	#endif
	CHECKF( GetVideoObject( &hVObject, uiVideoObject ) );

	memcpy(ppPal16, hVObject->p16BPPPalette, 256*2);

	return( TRUE );
}

BOOLEAN CheckFor16BPPRegion( HVOBJECT hVObject, UINT16 usRegionIndex, UINT8 ubShadeLevel, UINT16 * pusIndex )
{
	UINT16					usLoop;
	SixteenBPPObjectInfo *	p16BPPObject;

	if (hVObject->usNumberOf16BPPObjects > 0)
	{
		for (usLoop = 0; usLoop < hVObject->usNumberOf16BPPObjects; usLoop++)
		{
			p16BPPObject = &(hVObject->p16BPPObject[usLoop]);
			if (p16BPPObject->usRegionIndex == usRegionIndex && p16BPPObject->ubShadeLevel == ubShadeLevel)
			{
				if (pusIndex != NULL)
				{
					*pusIndex = usLoop;
				}
				return( TRUE );
			}
		}
	}
	return( FALSE );
}

BOOLEAN ConvertVObjectRegionTo16BPP( HVOBJECT hVObject, UINT16 usRegionIndex, UINT8 ubShadeLevel )
{
	SixteenBPPObjectInfo *	p16BPPObject;
	UINT8 *					pInput;
	UINT8 *					pOutput;
	UINT16 *				p16BPPPalette;
	UINT32					uiDataLoop;
	UINT32					uiDataLength;
	UINT8					ubRunLoop;
	//UINT8					ubRunLength;
	INT8					bData;
	UINT32					uiLen;

	// check for existing 16BPP region and then allocate memory
	if (usRegionIndex >= hVObject->usNumberOfObjects || ubShadeLevel >= HVOBJECT_SHADE_TABLES)
	{
		return( FALSE );
	}
	if (CheckFor16BPPRegion( hVObject, usRegionIndex, ubShadeLevel, NULL) == TRUE)
	{
		// it already exists; no need to do anything!
		return( TRUE );
	}

	if (hVObject->usNumberOf16BPPObjects > 0)
	{
		// have to reallocate memory
		hVObject->p16BPPObject = (SixteenBPPObjectInfo *) MemRealloc( hVObject->p16BPPObject, sizeof( SixteenBPPObjectInfo ) * (hVObject->usNumberOf16BPPObjects + 1) );
	}
	else
	{
		// allocate memory for the first 16BPPObject
		hVObject->p16BPPObject = (SixteenBPPObjectInfo *) MemAlloc( sizeof( SixteenBPPObjectInfo ) );
	}
	if (hVObject->p16BPPObject == NULL)
	{
		hVObject->usNumberOf16BPPObjects = 0;
		return( FALSE );
	}

	// the new object is the last one in the array
	p16BPPObject = &(hVObject->p16BPPObject[hVObject->usNumberOf16BPPObjects]);

	// need twice as much memory because of going from 8 to 16 bits
	p16BPPObject->p16BPPData = (UINT16 *) MemAlloc( hVObject->pETRLEObject[usRegionIndex].uiDataLength * 2 );
	if (p16BPPObject->p16BPPData == NULL)
	{
		return( FALSE );
	}

	p16BPPObject->usRegionIndex = usRegionIndex;
	p16BPPObject->ubShadeLevel = ubShadeLevel;
	p16BPPObject->usHeight = hVObject->pETRLEObject[ usRegionIndex ].usHeight;
	p16BPPObject->usWidth = hVObject->pETRLEObject[ usRegionIndex ].usWidth;
	p16BPPObject->sOffsetX=hVObject->pETRLEObject[ usRegionIndex ].sOffsetX;
	p16BPPObject->sOffsetY=hVObject->pETRLEObject[ usRegionIndex ].sOffsetY;

	// get the palette
	p16BPPPalette = hVObject->pShades[ubShadeLevel];
	pInput = (UINT8 *) hVObject->pPixData + hVObject->pETRLEObject[ usRegionIndex ].uiDataOffset;

	uiDataLength=hVObject->pETRLEObject[usRegionIndex].uiDataLength;

	// now actually do the conversion

	uiLen=0;
	pOutput = (UINT8 *)p16BPPObject->p16BPPData;
	for (uiDataLoop = 0; uiDataLoop < uiDataLength; uiDataLoop++)
	{
		bData= *pInput;
		if(bData&0x80)
		{
			// transparent
			*pOutput = *pInput;
			pOutput++;
			pInput++;
			//uiDataLoop++;
			uiLen+=(UINT32)(bData&0x7f);
		}
		else if(bData > 0)
		{
			// nontransparent
			*pOutput = *pInput;
			pOutput++;
			pInput++;
			//uiDataLoop++;
			for(ubRunLoop=0; ubRunLoop < bData; ubRunLoop++)
			{
				*((UINT16 *)pOutput) = p16BPPPalette[*pInput];
				pOutput++;
				pOutput++;
				pInput++;
				uiDataLoop++;
			}
			uiLen+=(UINT32)bData;
		}
		else
		{
			// eol
			*pOutput = *pInput;
			pOutput++;
			pInput++;
			//uiDataLoop++;
			if(uiLen!=p16BPPObject->usWidth)
		 DbgMessage(TOPIC_VIDEOOBJECT, DBG_LEVEL_1, String( "Actual pixel width different from header width" ));
			uiLen=0;
		}

		// copy the run-length byte
	/*	*pOutput = *pInput;
		pOutput++;
		if (((*pInput) & COMPRESS_TRANSPARENT) == 0 && *pInput > 0)
		{
			// non-transparent run; deal with the pixel data
			ubRunLoop = 0;
			ubRunLength = ((*pInput) & COMPRESS_RUN_LIMIT);
			// skip to the next input byte
			pInput++;
			for (ubRunLoop = 0; ubRunLoop < ubRunLength; ubRunLoop++)
			{
				*((UINT16 *)pOutput) = p16BPPPalette[*pInput];
				// advance two bytes in output, one in input
				pOutput++;
				pOutput++;
				pInput++;
				uiDataLoop++;
			}
		}
		else
		{
			// transparent run or end of scanline; skip to the next input byte
			pInput++;
		} */
	}
	hVObject->usNumberOf16BPPObjects++;
	return( TRUE );
}


BOOLEAN BltVideoObjectOutlineFromIndex(UINT32 uiDestVSurface, UINT32 uiSrcVObject, UINT16 usIndex, INT32 iDestX, INT32 iDestY, INT16 s16BPPColor, BOOLEAN fDoOutline )
{
	UINT16				*pBuffer;
	UINT32								uiPitch;
	HVOBJECT							hSrcVObject;

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

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

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_BLTVIDEOOBJECTOUTLINEFROMINDEX;
	#endif
	CHECKF( GetVideoObject( &hSrcVObject, uiSrcVObject ) );

	if( BltIsClipped( hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect) )
	{
		Blt8BPPDataTo16BPPBufferOutlineClip((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, s16BPPColor, fDoOutline, &ClippingRect );
	}
	else
	{
		Blt8BPPDataTo16BPPBufferOutline((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, s16BPPColor, fDoOutline );
	}

	// Now we have the video object and surface, call the VO blitter function

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}

BOOLEAN BltVideoObjectOutline(UINT32 uiDestVSurface, HVOBJECT hSrcVObject, UINT16 usIndex, INT32 iDestX, INT32 iDestY, INT16 s16BPPColor, BOOLEAN fDoOutline )
{
	UINT16				*pBuffer;
	UINT32								uiPitch;
	// Lock video surface
	pBuffer = (UINT16*)LockVideoSurface( uiDestVSurface, &uiPitch );

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

	if( BltIsClipped( hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect) )
	{
		Blt8BPPDataTo16BPPBufferOutlineClip((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, s16BPPColor, fDoOutline, &ClippingRect );
	}
	else
	{
		Blt8BPPDataTo16BPPBufferOutline((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, s16BPPColor, fDoOutline );
	}

	// Now we have the video object and surface, call the VO blitter function

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}



BOOLEAN BltVideoObjectOutlineShadowFromIndex(UINT32 uiDestVSurface, UINT32 uiSrcVObject, UINT16 usIndex, INT32 iDestX, INT32 iDestY )
{
	UINT16				*pBuffer;
	UINT32								uiPitch;
	HVOBJECT							hSrcVObject;

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

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

	// Get video object
	#ifdef _DEBUG
		gubVODebugCode = DEBUGSTR_BLTVIDEOOBJECTOUTLINESHADOWFROMINDEX;
	#endif
	CHECKF( GetVideoObject( &hSrcVObject, uiSrcVObject ) );

	if(hSrcVObject->ubBitDepth == 8)
	{
		if( BltIsClipped( hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect) )
		{
			Blt8BPPDataTo16BPPBufferOutlineShadowClip((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect );
		}
		else
		{
			Blt8BPPDataTo16BPPBufferOutlineShadow((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex );
		}
	}
	else if(hSrcVObject->ubBitDepth == 16)
	{
		SGP_THROW_IFFALSE(usIndex < hSrcVObject->usNumberOf16BPPObjects, L"index larger that number images");
		SixteenBPPObjectInfo &image = hSrcVObject->p16BPPObject[0];
		Blt16BPPTo16BPPTransShadow(pBuffer, uiPitch, image.p16BPPData, image.usWidth * sizeof(UINT16),
			iDestX, iDestY, 0, 0, image.usWidth, image.usHeight, 0x1F);
	}
	else if(hSrcVObject->ubBitDepth == 32)
	{
		SGP_THROW_IFFALSE(usIndex < hSrcVObject->usNumberOf16BPPObjects, L"index larger that number images");
		SixteenBPPObjectInfo &image = hSrcVObject->p16BPPObject[0];
		Blt32BPPTo16BPPTransShadow(pBuffer, uiPitch, (UINT32*)image.p16BPPData, image.usWidth * sizeof(UINT32),
			iDestX, iDestY, 0, 0, image.usWidth, image.usHeight);
	}

	// Now we have the video object and surface, call the VO blitter function

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}

BOOLEAN BltVideoObjectOutlineShadow(UINT32 uiDestVSurface, HVOBJECT hSrcVObject, UINT16 usIndex, INT32 iDestX, INT32 iDestY )
{
	UINT16				*pBuffer;
	UINT32								uiPitch;
	// Lock video surface
	pBuffer = (UINT16*)LockVideoSurface( uiDestVSurface, &uiPitch );

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

	if( BltIsClipped( hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect) )
	{
		Blt8BPPDataTo16BPPBufferOutlineShadowClip((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect );
	}
	else
	{
		Blt8BPPDataTo16BPPBufferOutlineShadow((UINT16*)pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex );
	}

	// Now we have the video object and surface, call the VO blitter function

	UnLockVideoSurface( uiDestVSurface );
	return( TRUE );
}

#ifdef _DEBUG
void CheckValidVObjectIndex( UINT32 uiIndex )
{
	BOOLEAN fAssertError = FALSE;
	if( uiIndex == 0xffffffff )
	{ //-1 index -- deleted
		fAssertError = TRUE;
	}
	if( !(uiIndex % 2) && uiIndex < 0xfffffff0 || uiIndex >= 0xfffffff0 )
	{ //even numbers are reserved for vsurfaces as well as the 0xfffffff0+ values
		fAssertError = TRUE;
	}

	if( fAssertError )
	{
		CHAR8 str[60];
		switch( gubVODebugCode )
		{
			case DEBUGSTR_SETVIDEOOBJECTTRANSPARENCY:
				sprintf( str, "SetVideoObjectTransparency" );
				break;
			case DEBUGSTR_BLTVIDEOOBJECTFROMINDEX:
				sprintf( str, "BltVideoObjectFromIndex" );
				break;
			case DEBUGSTR_SETOBJECTHANDLESHADE:
				sprintf( str, "SetObjectHandleShade" );
				break;
			case DEBUGSTR_GETVIDEOOBJECTETRLESUBREGIONPROPERTIES:
				sprintf( str, "GetVideoObjectETRLESubRegionProperties" );
				break;
			case DEBUGSTR_GETVIDEOOBJECTETRLEPROPERTIESFROMINDEX:
				sprintf( str, "GetVideoObjectETRLEPropertiesFromIndex" );
				break;
			case DEBUGSTR_SETVIDEOOBJECTPALETTE8BPP:
				sprintf( str, "SetVideoObjectPalette8BPP" );
				break;
			case DEBUGSTR_GETVIDEOOBJECTPALETTE16BPP:
				sprintf( str, "GetVideoObjectPalette16BPP" );
				break;
			case DEBUGSTR_COPYVIDEOOBJECTPALETTE16BPP:
				sprintf( str, "CopyVideoObjectPalette16BPP" );
				break;
			case DEBUGSTR_BLTVIDEOOBJECTOUTLINEFROMINDEX:
				sprintf( str, "BltVideoObjectOutlineFromIndex" );
				break;
			case DEBUGSTR_BLTVIDEOOBJECTOUTLINESHADOWFROMINDEX:
				sprintf( str, "BltVideoObjectOutlineShadowFromIndex" );
				break;
			case DEBUGSTR_DELETEVIDEOOBJECTFROMINDEX:
				sprintf( str, "DeleteVideoObjectFromIndex" );
				break;
			case DEBUGSTR_NONE:
			default:
				sprintf( str, "GetVideoObject" );
				break;
		}
	}
}
#endif

#ifdef SGP_VIDEO_DEBUGGING

typedef struct DUMPFILENAME
{
	CHAR8 str[256];
}DUMPFILENAME;

void DumpVObjectInfoIntoFile( const STR8 filename, BOOLEAN fAppend )
{
	VOBJECT_NODE *curr;
	FILE *fp;
	DUMPFILENAME *pName, *pCode;
	UINT32 *puiCounter;
	CHAR8 tempName[ 256 ];
	CHAR8 tempCode[ 256 ];
	UINT32 i, uiUniqueID;
	BOOLEAN fFound;
	if( !guiVObjectSize )
	{
		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 ) * guiVObjectSize );
	pCode = (DUMPFILENAME*)MemAlloc( sizeof( DUMPFILENAME ) * guiVObjectSize );
	memset( pName, 0, sizeof( DUMPFILENAME ) * guiVObjectSize );
	memset( pCode, 0, sizeof( DUMPFILENAME ) * guiVObjectSize );
	puiCounter = (UINT32*)MemAlloc( 4 * guiVObjectSize );
	memset( puiCounter, 0, 4 * guiVObjectSize );

	//Loop through the list and record every unique filename and count them
	uiUniqueID = 0;
	curr = gpVObjectHead;
	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 vObject names exist in %d VObjects\n", uiUniqueID, guiVObjectSize );
	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 vObjects
BOOLEAN _AddAndRecordVObject( VOBJECT_DESC *VObjectDesc, UINT32 *uiIndex, UINT32 uiLineNum, const STR8 pSourceFile )
{
	UINT16 usLength;
	CHAR8 str[256];
	if( !AddStandardVideoObject( VObjectDesc, uiIndex ) )
	{
		return FALSE;
	}

	//record the filename of the vObject (some are created via memory though)
	usLength = strlen( VObjectDesc->ImageFile ) + 1;
	gpVObjectTail->pName = (STR8)MemAlloc( usLength );
	memset( gpVObjectTail->pName, 0, usLength );
	strcpy( gpVObjectTail->pName, VObjectDesc->ImageFile );

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

	return TRUE;
}

void PerformVideoInfoDumpIntoFile( const STR8 filename, BOOLEAN fAppend )
{
	DumpVObjectInfoIntoFile( filename, fAppend );
	DumpVSurfaceInfoIntoFile( filename, TRUE );
}

#endif

// Flugente: retrieve width and height of video object
void GetVideoObjectDimensions( HVOBJECT hSrcVObject, UINT16 usIndex, UINT16& rusWidth, UINT16& rusHeight )
{
	rusWidth = 0;
	rusHeight = 0;

	if ( hSrcVObject == NULL )
		return;

	switch ( hSrcVObject->ubBitDepth )
	{
	case 32:
	case 16:
		if ( usIndex < hSrcVObject->usNumberOf16BPPObjects )
		{
			rusWidth = hSrcVObject->p16BPPObject[usIndex].usWidth;
			rusHeight = hSrcVObject->p16BPPObject[usIndex].usHeight;
		}
		break;

	case 8:
		if ( usIndex < hSrcVObject->usNumberOfObjects )
		{
			rusWidth = hSrcVObject->pETRLEObject[usIndex].usWidth;
			rusHeight = hSrcVObject->pETRLEObject[usIndex].usHeight;
		}
		break;
	}
}
