////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
///////////////////////////////////////////////////////////////////////////////
//
//	bdisp.cpp
// 
// History:
//		06/04/04 RCG	Started.
//
//////////////////////////////////////////////////////////////////////////////
//
//
//////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include "Blue.h"
#include "ORANGE/CDT/slist.h"

#include <ctype.h>

//todo:
#include <kos.h>
#include <dc/sq.h>
#include <dc/flashrom.h>
#include <dc/video.h>
#include "../../../../PVR/pvr.h"

static int RequestedWidth = 0;
static int RequestedHeight = 0;
static int FramebufferWidth = 0;
static int FramebufferHeight = 0;
static bool bIs50Hz = false;
static bool bIsVga = false;

typedef struct		// Stores information on usable video modes.
	{
	int16_t				sWidth;
	int16_t				sHeight;
	int16_t				sColorDepth;
	int16_t				sPages;
	} VIDEO_MODE, *PVIDEO_MODE;

static RSList<VIDEO_MODE, int16_t>	slvmModes;	// List of available video modes.

typedef union { struct { Uint8 b; Uint8 g; Uint8 r; Uint8 a; }; Uint32 argb; } ArgbColor;
static ArgbColor	apeApp[256];				// App's palette.  The palette
														// entries the App actually set.
//Unused! Why?
//static ArgbColor	apeMapped[256];			// Tweaked palette.
														// This is the palette updated to
														// the hardware.  apeApp is trans-
														// lated through au8MapRed, Green,
														// and Blue and stored here for
														// updating to the hardware on
														// rspUpdatePalette().

static U8					au8MapRed[256];			// Map of red intensities to hardware
														// values.  Initially an identity
														// mapping.
static U8					au8MapGreen[256];			// Map of green intensities to
														// hardware values.  Initially an 
														// identity mapping.
static U8					au8MapBlue[256];			// Map of blue intensities to hardware
														// values.  Initially an identity
														// mapping.

static int16_t				asPalEntryLocks[256];	// TRUE, if an indexed entry is locked.
														// FALSE, if not.  Implemented as 
														// shorts in case we ever do levels of
														// locking.

//////////////////////////////////////////////////////////////////////////////
// Module specific macros.
//////////////////////////////////////////////////////////////////////////////

// Only set value if not NULL.
#define SET(ptr, val)		( ((ptr) != NULL) ? *(ptr) = (val) : 0)


//////////////////////////////////////////////////////////////////////////////
//
// Clips [(*px,*py),(*pw,*ph)] into [(sx,sy),(sw, sh)].
//
//////////////////////////////////////////////////////////////////////////////
extern int16_t Clip(	// Returns non-zero if image entirely clipped out.
	int16_t*	px,		// Rectangle to be clipped.
	int16_t*	py, 
	int16_t*	pw, 
	int16_t*	ph,
	int16_t		sx,		// Bounding rectangle.
	int16_t		sy, 
	int16_t		sw, 
	int16_t		sh)
	{
	if (*px < sx)
		{
		TRACE("Clip(): x too small.\n");
		// Adjust width.
		*pw -= sx - *px;
		// Adjust x.
		*px = sx;
		}

	if (*py < sy)
		{
		TRACE("Clip(): y too small.\n");
		// Adjust height.
		*ph -= sy - *py;
		// Adjust y.
		*py = sy;
		}

	if (*px + *pw > sw)
		{
		TRACE("Clip(): Width or x too large.\n");
		// Adjust width.
		*pw -= ((*px + *pw) - sw);
		}

	if (*py + *ph > sh)
		{
		TRACE("Clip(): Height or y too large.\n");
		// Adjust height.
		*ph -= ((*py + *ph) - sh);
		}
	
	// Return 0 (success) if there's a width and a height left.	
	return (int16_t)(*pw > 0 && *ph > 0 ? 0 : -1);
	}

//////////////////////////////////////////////////////////////////////////////
//
// Clips [(*px,*py),(*pw,*ph)] into [(sx,sy),(sw, sh)].
//
//////////////////////////////////////////////////////////////////////////////
extern int16_t ClipQuiet(	// Returns non-zero if image entirely clipped out.
	int16_t*	px,			// Rectangle to be clipped.
	int16_t*	py, 
	int16_t*	pw, 
	int16_t*	ph,
	int16_t		sx,			// Bounding rectangle.
	int16_t		sy, 
	int16_t		sw, 
	int16_t		sh)
	{
	if (*px < sx)
		{
		// Adjust width.
		*pw -= sx - *px;
		// Adjust x.
		*px = sx;
		}

	if (*py < sy)
		{
		// Adjust height.
		*ph -= sy - *py;
		// Adjust y.
		*py = sy;
		}

	if (*px + *pw > sw)
		{
		// Adjust width.
		*pw -= ((*px + *pw) - sw);
		}

	if (*py + *ph > sh)
		{
		// Adjust height.
		*ph -= ((*py + *ph) - sh);
		}
	
	// Return 0 (success) if there's a width and a height left.	
	return (int16_t)(*pw > 0 && *ph > 0 ? 0 : -1);
	}

//////////////////////////////////////////////////////////////////////////////
//
// Clips [(*px,*py),(*pw,*ph)] into [(sx,sy),(sw, sh)].
// Uses short version of function ClipQuiet(...).
//
//////////////////////////////////////////////////////////////////////////////
extern int16_t ClipQuiet(	// Returns non-zero if image entirely clipped out.
	int32_t* px,				// Rectangle to be clipped.
	int32_t* py, 
	int32_t*	pw, 
	int32_t*	ph,
	int32_t	sx,			// Bounding rectangle.
	int32_t	sy, 
	int32_t	sw, 
	int32_t	sh)
	{
	int16_t	sX	= (int16_t)(*px);
	int16_t sY	= (int16_t)(*py);
	int16_t	sW	= (int16_t)(*pw);
	int16_t	sH	= (int16_t)(*ph);

	int16_t	sRes	= ClipQuiet(&sX, &sY, &sW, &sH, 
									(int16_t)sx, (int16_t)sy, (int16_t)sw, (int16_t)sh);

	*px	= sX;
	*py	= sY;
	*pw	= sW;
	*ph	= sH;

	return sRes;
	}


int16_t CompareModes(PVIDEO_MODE pvm1, PVIDEO_MODE pvm2);

extern void Disp_Init(void)	// Returns nothing.
{   
	// Initialize maps to indentities.
	int16_t i;
	for (i = 0; i < 256; i++)
		{
		au8MapRed[i]	= i;
		au8MapGreen[i]	= i;
		au8MapBlue[i]	= i;
		}

	// Never ever ever unlock these.
	asPalEntryLocks[0]	= TRUE;
	asPalEntryLocks[255]	= TRUE;

	slvmModes.SetCompareFunc(CompareModes);
}

//////////////////////////////////////////////////////////////////////////////
//
// Compares two video modes in order of sColorDepth, sWidth, sHeight,
// sPageFlippage.
// Returns 0			if *pvm1 == *pvm2.
// Returns negative	if *pvm1 < *pvm2.
// Returns positive	if *pvm1 > *pvm2.
//
//////////////////////////////////////////////////////////////////////////////
extern int16_t CompareModes(	// Returns as described above.
		PVIDEO_MODE	pvm1,		// First video mode to compare.
		PVIDEO_MODE	pvm2)		// Second video mode to compare.
	{
	int16_t	sRes	= 1;	// Assume *pvm1 > *pvm2.

	if (pvm1->sColorDepth == pvm2->sColorDepth)
		{
		if (pvm1->sWidth == pvm2->sWidth)
			{
			if (pvm1->sHeight == pvm2->sHeight)
				{
				if (pvm1->sPages == pvm2->sPages)
					{
					sRes = 0;
					}
				else
					{
					if (pvm1->sPages < pvm2->sPages)
						{
						sRes = -1;
						}
					}
				}
			else
				{
				if (pvm1->sHeight < pvm2->sHeight)
					{
					sRes = -1;
					}
				}
			}
		else
			{
			if (pvm1->sWidth < pvm2->sWidth)
				{
				sRes = -1;
				}
			}
		}
	else
		{
		if (pvm1->sColorDepth < pvm2->sColorDepth)
			{
			sRes = -1;
			}
		}

	return sRes;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Reset the query process.  Must be called before calling QueryVideoModes()
// for the first time.  See QueryVideoModes() for more.
//
//////////////////////////////////////////////////////////////////////////////

static void addMode(int w, int h, int depth)
{
    PVIDEO_MODE pvm;

    if (depth >= 8)
    {
        pvm = new VIDEO_MODE;
        pvm->sWidth = w;
        pvm->sHeight = h;
        pvm->sColorDepth = 8;
        pvm->sPages = 1;
        slvmModes.Insert(pvm);
    }

    if (depth >= 16)
    {
        pvm = new VIDEO_MODE;
        pvm->sWidth = w;
        pvm->sHeight = h;
        pvm->sColorDepth = 16;
        pvm->sPages = 1;
        slvmModes.Insert(pvm);
    }

    if (depth >= 24)
    {
        pvm = new VIDEO_MODE;
        pvm->sWidth = w;
        pvm->sHeight = h;
        pvm->sColorDepth = 32;
        pvm->sPages = 1;
        slvmModes.Insert(pvm);
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Puts parameters about the hardware video mode into your shorts.
// You may call this function even when in "no mode" (e.g., before 
// rspSetVideoMode is first called, after it fails, or after rspKillVideMode
// is called).  This way you can get information on the user's current mode.
// If in "no mode", psWidth, psHeight, and psPages will receive 0, if not NULL.
//
//////////////////////////////////////////////////////////////////////////////
extern int16_t rspGetVideoMode(
	int16_t* psDeviceDepth,				// Hardware display color depth returned here 
												// (unless NULL).
	int16_t* psDeviceWidth,				// Hardware display width returned here 
												// (unless NULL).
	int16_t* psDeviceHeight,			// Hardware display height returned here 
												// (unless NULL).
	int16_t* psDevicePages,				// Hardware display back buffers returned here
												// (unless NULL).
	int16_t* psWidth,						// Display area width returned here 
												// (unless NULL).
	int16_t* psHeight,					// Display area height returned here
												// (unless NULL).
	int16_t* psPages/*= NULL*/,			// Number of pages (1 to n) returned here 
												// (unless NULL).  More than 1 indicates a 
												// page flipping scenario.
	int16_t* psPixelScaling/*= NULL*/)	// Pixel scaling in effect (1) or not (0)
													// (unless NULL).
{
	// lie about everything.
	SET(psPixelScaling, 0);
	SET(psDevicePages, 0);
	SET(psPages, 1);
	SET(psWidth, FramebufferWidth);
	SET(psHeight, FramebufferHeight);
	SET(psDeviceDepth, 8);
	SET(psDeviceHeight, FramebufferWidth);
	SET(psDeviceWidth, FramebufferHeight);

	return 0;
}


//////////////////////////////////////////////////////////////////////////////
//
// Set a new video mode.  Specified color depth, width, and height for the
// device is absolute.  If these parameters cannot be met, the function will
// fail. If the requested resolution is higher than the requested display area,
// it will be centered with a black border around it.  This function can be
// called multiple times to change modes, but it does not create new
// display areas; instead, the previous buffer is destroyed and a new buffer
// is created to take it's place.
// If pixel doubling is allowed (with rspAllowPixelDoubling) and the requested
// display area is less than or equal to half the requested hardware resolution, 
// pixel doubling will be activated.
// If this function fails, you will be in no mode; meaning you may not access
// the display (i.e., call display/palette functions) even if you were in a mode
// before calling this function.  See rspKillVideoMode.
// Before this function is called, you may not call functions that manipulate
// the display.
//
//////////////////////////////////////////////////////////////////////////////
extern int16_t rspSetVideoMode(	// Returns 0 if successfull, non-zero otherwise
	int16_t	sDeviceDepth,			// Specify required device video depth here.
	int16_t	sDeviceWidth,			// Specify required device resolution width here.
	int16_t	sDeviceHeight,			// Specify required device resolution height here.
	int16_t sWidth,					// Specify width of display area on screen.
	int16_t sHeight,					// Specify height of display area on screen.
	int16_t	sPal60,					// indicates a page flipping scenario.
	int16_t	sPixelDoubling	/*= FALSE*/)
										// TRUE indicates to set the video mode
										// to twice that indicated by sDeviceWidth,
										// sDeviceHeight and double the coordinate
										// system and blts.
										// FALSE indicates not to use this garbage.
	{
		TRACE("rspSetVideoMode(%i, %i, %i, %i, %i, %i, %i)\n", sDeviceDepth, sDeviceWidth, sDeviceHeight, sWidth, sHeight, sPal60, sPixelDoubling);		
		ASSERT(sDeviceDepth == 8);        
        ASSERT(sWidth == 640);
        ASSERT(sHeight == 480);

        for (size_t i = 0; i < 256; i++)
            apeApp[i].a = 0xFF;
    
        if (sPixelDoubling)
        {
            fprintf(stderr, "STUBBED: pixel doubling? %s:%d\n", __FILE__, __LINE__);
            return -1;
        }

        FramebufferWidth = sWidth;
        FramebufferHeight = sHeight;
   	
		int32_t lCableType = vid_check_cable();
		if (lCableType == CT_VGA)
		{
			bIs50Hz = false;
			bIsVga = true;
		}
		else
		{
			if (flashrom_get_region() == FLASHROM_REGION_EUROPE)
			{			
				switch (vid_check_cable())
				{
					case CT_RGB:
						// Offet picture slightly for left shift ?
					case CT_COMPOSITE:
						// Tweak borders
						if (!sPal60)
						{
							bIs50Hz = true;
							vid_set_mode(DM_640x480_PAL_IL, PM_RGB565);
						}
						else
						{
							bIs50Hz = false;
							vid_set_mode(DM_640x480_NTSC_IL, PM_RGB565);
						}						
						break;
				}		
			}
			else
			{
				bIs50Hz = false;
			}
		}
						
        return 0;
	}

extern bool rspIs50Hz(void)
{
	return bIs50Hz;
}

extern bool rspIsVGA(void)
{
	return bIsVga;
}

//////////////////////////////////////////////////////////////////////////////
//
// Puts you in a state of not having display access.  After this function is
// called (similar to before rspSetVideoMode is called) you may not call
// functions that manipulate the display.  This is similar to the situation
// achieved if rspSetVideoMode fails.
//
//////////////////////////////////////////////////////////////////////////////
extern void rspKillVideoMode(void)
	{
    /* no-op ... SDL_Quit() will catch this. */
	}

//////////////////////////////////////////////////////////////////////////////
//
// Frees the memory (and, perhaps, structure(s)) associated with the memory
// stored in system RAM that is allocate rspSetVideoMode.  If you are not 
// using the system buffer (i.e., you are not calling rspLockVideoBuffer), 
// you can call this to free up some additional memory.  Calls to 
// rspLockVideoBuffer will fail after a call to this function without a 
// subsequent call to rspSetVideoMode.
//
//////////////////////////////////////////////////////////////////////////////
extern void rspKillVideoBuffer(void)
	{
    /* no-op ... SDL_Quit() will catch this. */
	}

//////////////////////////////////////////////////////////////////////////////
//
// Variation #1: Update the entire display from the buffer.  Includes
// pixel doubling as appropriate - see elsewhere for details.
//
//////////////////////////////////////////////////////////////////////////////

extern void rspUpdateDisplayRects(void)
{
    // no-op, just blast it all to the GPU.
}

extern void rspCacheDirtyRect(
	int16_t sX,					// x coord of upper-left corner of area to update
	int16_t sY,					// y coord of upper-left corner of area to update
	int16_t sWidth,				// Width of area to update
	int16_t sHeight)				// Height of area to update
{
}


extern void rspPresentFrame(void)
{			
	static Uint32 lastframeticks = 0;
	const Uint32 now = timer_ms_gettime64();

	if ((lastframeticks) && (lastframeticks <= now))
	{
		const Uint32 elapsed = (now - lastframeticks);		
		if (elapsed <= 5)  // going WAY too fast, maybe OpenGL (and/or no vsync)?		
		{		
			timer_spin_sleep(16 - elapsed); // try to get closer to 60fps.			
		}
	}

	lastframeticks = now;
}

extern void rspUpdateDisplay(void)
{
}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern void rspUpdateDisplay(
	int16_t sX,					// x coord of upper-left corner of area to update
	int16_t sY,					// y coord of upper-left corner of area to update
	int16_t sWidth,				// Width of area to update
	int16_t sHeight)				// Height of area to update
{
}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern int16_t rspLockVideoPage(	// Returns 0 if screen memory could be locked.
											// Returns non-zero otherwise.
	void**	ppvMemory,				// Pointer to display memory returned here.
											// NULL returned if not supported.
	int32_t*		plPitch)					// Pitch of display memory returned here.
	{
	/* no-op. */
	return 1;
	}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern void rspUnlockVideoPage(void)	// Returns nothing.
	{
	/* no-op. */
	}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern int16_t rspLockVideoFlipPage(	// Returns 0 if flip screen memory could be 
											// locked.  Returns non-zero otherwise.
	void**	ppvMemory,				// Pointer to flip screen memory returned here.
											// NULL returned on failure.
	int32_t*		plPitch)					// Pitch of flip screen memory returned here.
	{
	return -1;
	}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern void rspUnlockVideoFlipPage(void)	// Returns nothing.
	{
	}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern int16_t rspLockVideoBuffer(	// Returns 0 if system buffer could be locked.
												// Returns non-zero otherwise.
	void**	ppvBuffer,					// Pointer to system buffer returned here.
												// NULL returned on failure.
	int32_t*		plPitch)						// Pitch of system buffer returned here.
	{
    //if (!sdlWindow)
    //    return -1;

    *plPitch = FramebufferWidth;

    return(0);
	}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern void rspUnlockVideoBuffer(void)	// Returns nothing.
	{
	}

///////////////////////////////////////////////////////////////////////////////
//
// Maps calls to API that matches display type.
// See function comment in appropriate CPP (BGDisp/BXDisp).
//
///////////////////////////////////////////////////////////////////////////////
extern int16_t rspAllowPageFlip(void)	// Returns 0 on success.
	{
	return 0;
	}

//////////////////////////////////////////////////////////////////////////////
//	External Palette module functions.
//////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// Set several palette entries.  Separate pointers to each component
// combined with caller-specified increment for all pointers allows
// use of this function with any (non-packed) arrangement of RGB data.
// Hardware palette is not updated until UpdatePalette() is called.
//
///////////////////////////////////////////////////////////////////////////////
extern void rspSetPaletteEntries(
	int16_t sStartIndex,			// Palette entry to start copying to (has no effect on source!)
	int16_t sCount,					// Number of palette entries to do
	uint8_t* pucRed,		// Pointer to first red component to copy from
	uint8_t* pucGreen,	// Pointer to first green component to copy from
	uint8_t* pucBlue,		// Pointer to first blue component to copy from
	int32_t lIncBytes)				// Number of bytes by which to increment pointers after each copy
	{
	// Set up destination pointers.
	uint8_t*	pucDstRed	= &(apeApp[sStartIndex].r);
	uint8_t*	pucDstGreen	= &(apeApp[sStartIndex].g);
	uint8_t*	pucDstBlue	= &(apeApp[sStartIndex].b);

	// Set up lock pointer.
	int16_t*	psLock		= &(asPalEntryLocks[sStartIndex]);

	while (sCount-- > 0)
		{
		if (*psLock++ == 0)
			{
			*pucDstRed		= *pucRed;
			*pucDstGreen	= *pucGreen;
			*pucDstBlue		= *pucBlue;
			}

		// Increment source.
		pucRed			+= lIncBytes;
		pucGreen		+= lIncBytes;
		pucBlue			+= lIncBytes;

		// Increment destination.
		pucDstRed		+= sizeof(apeApp[0]);
		pucDstGreen		+= sizeof(apeApp[0]);
		pucDstBlue		+= sizeof(apeApp[0]);	
		}
	}

///////////////////////////////////////////////////////////////////////////////
//
// Set palette entry.  Hardware palette is not updated until
// UpdatePalette() is called.
//
///////////////////////////////////////////////////////////////////////////////
void rspSetPaletteEntry(
	int16_t sEntry,				// Palette entry (0 to 255)
	uint8_t ucRed,				// Red component (0 to 255)
	uint8_t ucGreen,				// Green component (0 to 255)
	uint8_t ucBlue)				// Blue component (0 to 255)
	{
	ASSERT(sEntry >= 0 && sEntry < 256);

	if (asPalEntryLocks[sEntry] == 0)
		{
		apeApp[sEntry].r	= ucRed;
		apeApp[sEntry].g	= ucGreen;
		apeApp[sEntry].b	= ucBlue;
		}
	}

///////////////////////////////////////////////////////////////////////////////
//
// Get palette entry.  This reflects the palette that may or may NOT have
// been set by calling rspUpdatePalette.
//
///////////////////////////////////////////////////////////////////////////////
void rspGetPaletteEntry(
	int16_t sEntry,				// Palette entry (0 to 255)
	int16_t* psRed,				// Red component (0 to 255) returned if not NULL.
	int16_t* psGreen,			// Green component (0 to 255) returned if not NULL.
	int16_t* psBlue)				// Blue component (0 to 255) returned if not NULL.
	{
	ASSERT(sEntry >= 0 && sEntry < 256);

	SET(psRed,		apeApp[sEntry].r);
	SET(psGreen,	apeApp[sEntry].g);
	SET(psBlue,		apeApp[sEntry].b);
	}

///////////////////////////////////////////////////////////////////////////////
//
// Get several palette entries.  Separate pointers to each component
// combined with caller-specified increment for all pointers allows
// use of this function with any (non-packed) arrangement of RGB data.
// This is the palette that may not necessarily be updated yet in the hardware
// (i.e., some may have set the palette and not called UpdatePalette()).
//
///////////////////////////////////////////////////////////////////////////////
extern void rspGetPaletteEntries(
	int16_t sStartIndex,			// Palette entry to start copying from
	int16_t sCount,					// Number of palette entries to do
	uint8_t* pucRed,		// Pointer to first red component to copy to
	uint8_t* pucGreen,	// Pointer to first green component to copy to
	uint8_t* pucBlue,		// Pointer to first blue component to copy to
	int32_t lIncBytes)				// Number of bytes by which to increment pointers after each copy
	{
	// Set up source pointers.
	uint8_t*	pucSrcRed	= &(apeApp[sStartIndex].r);
	uint8_t*	pucSrcGreen	= &(apeApp[sStartIndex].g);
	uint8_t*	pucSrcBlue	= &(apeApp[sStartIndex].b);

	while (sCount-- > 0)
		{
		*pucRed		= *pucSrcRed;
		*pucGreen	= *pucSrcGreen;
		*pucBlue		= *pucSrcBlue;
		// Increment destination.
		pucRed			+= lIncBytes;
		pucGreen			+= lIncBytes;
		pucBlue			+= lIncBytes;

		// Increment source.
		pucSrcRed		+= sizeof(apeApp[0]);
		pucSrcGreen		+= sizeof(apeApp[0]);
		pucSrcBlue		+= sizeof(apeApp[0]);
		}
	}

///////////////////////////////////////////////////////////////////////////////
//
// Update hardware palette using information that has been set
// using SetPaletteEntry() and SetPaletteEntries().
// In the future, this may support optional VBLANK-synced updates.
//
///////////////////////////////////////////////////////////////////////////////

extern void rspUpdatePalette(uint8_t palnum = 0)
	{
		// palette stuff todo
	
		for (int i = (palnum *256); i < (palnum * 256)+256; i++)
		{
			U8* r;
			U8* g;
			U8* b;

			rspGetPaletteEntry(i-(palnum *256), r, g, b);
									
			pvr_set_pal_entry(i, PACK_ARGB8888(255, *r, *g, *b));
		}	
    }
///////////////////////////////////////////////////////////////////////////////
//
// Set entries in the color map used to tweak values set via 
// rspSetPaletteEntries().  Those colors' values will be used as indices
// into this map when rspUpdatePalette() is called.  The resulting values
// will be updated to the hardware.
// rspGetPaletteEntries/Entry() will still return the original values set 
// (not mapped values).
// 
///////////////////////////////////////////////////////////////////////////////
extern void rspSetPaletteMaps(
	int16_t sStartIndex,			// Map entry to start copying to (has no effect on source!)
	int16_t sCount,					// Number of map entries to do
	uint8_t* pucRed,		// Pointer to first red component to copy from
	uint8_t* pucGreen,	// Pointer to first green component to copy from
	uint8_t* pucBlue,		// Pointer to first blue component to copy from
	int32_t lIncBytes)				// Number of bytes by which to increment pointers after each copy
	{
	// Set up destination pointers.
	uint8_t*	pucDstRed	= &(au8MapRed[sStartIndex]);
	uint8_t*	pucDstGreen	= &(au8MapGreen[sStartIndex]);
	uint8_t*	pucDstBlue	= &(au8MapBlue[sStartIndex]);

	while (sCount-- > 0)
		{
		*pucDstRed++		= *pucRed;
		*pucDstGreen++		= *pucGreen;
		*pucDstBlue++		= *pucBlue;
		// Increment source.
		pucRed				+= lIncBytes;
		pucGreen				+= lIncBytes;
		pucBlue				+= lIncBytes;
		}
	}

///////////////////////////////////////////////////////////////////////////////
//
// Get entries in the color map used to tweak values set via 
// rspSetPaletteEntries().  Those colors' values will be used as indices
// into this map when rspUpdatePalette() is called.  The resulting values
// will be updated to the hardware.
// rspGetPaletteEntries/Entry() will still return the original values set 
// (not mapped values).
// 
///////////////////////////////////////////////////////////////////////////////
extern void rspGetPaletteMaps(
	int16_t sStartIndex,			// Map entry to start copying from (has no effect on dest!)
	int16_t sCount,					// Number of map entries to do
	uint8_t* pucRed,		// Pointer to first red component to copy to
	uint8_t* pucGreen,	// Pointer to first green component to copy to
	uint8_t* pucBlue,		// Pointer to first blue component to copy to
	int32_t lIncBytes)				// Number of bytes by which to increment pointers after each copy
	{
	// Set up source pointers.
	uint8_t*	pucSrcRed	= &(au8MapRed[sStartIndex]);
	uint8_t*	pucSrcGreen	= &(au8MapGreen[sStartIndex]);
	uint8_t*	pucSrcBlue	= &(au8MapBlue[sStartIndex]);

	while (sCount-- > 0)
		{
		*pucRed		= *pucSrcRed++;
		*pucGreen	= *pucSrcGreen++;
		*pucBlue		= *pucSrcBlue++;
		// Increment destination.
		pucRed				+= lIncBytes;
		pucGreen				+= lIncBytes;
		pucBlue				+= lIncBytes;
		}
	}

///////////////////////////////////////////////////////////////////////////////
// Lock several palette entries.  Locking an entry keeps it from
// being updated by rspSetPaletteEntries() (until it is unlocked
// with rspUnlockPaletteEntries() ).
///////////////////////////////////////////////////////////////////////////////
extern void rspLockPaletteEntries(
	int16_t	sStartIndex,			// Palette entry at which to start locking.
	int16_t	sCount)					// Number of palette entries to lock.
	{
	// Set up iterator pointer.
	int16_t*	psLock	= &(asPalEntryLocks[sStartIndex]);

	while (sCount-- > 0)
		{
		*psLock	= TRUE;
		}
	}

///////////////////////////////////////////////////////////////////////////////
// Unlock several palette entries previously locked by rspLockPaletteEntries().
///////////////////////////////////////////////////////////////////////////////
extern void rspUnlockPaletteEntries(
	int16_t	sStartIndex,			// Palette entry at which to start locking.
	int16_t	sCount)					// Number of palette entries to lock.
	{
	// Set up iterator pointer.
	int16_t*	psLock	= &(asPalEntryLocks[sStartIndex]);

	while (sCount-- > 0)
		{
		*psLock	= FALSE;
		}

	// Never ever ever unlock these.
	asPalEntryLocks[0]	= TRUE;
	asPalEntryLocks[255]	= TRUE;
	}

///////////////////////////////////////////////////////////////////////////////
// Dyna schtuff.
///////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
//	External Background module functions.
//////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// Set a callback to be called when the application moves into the background.
//
///////////////////////////////////////////////////////////////////////////////
extern void rspSetBackgroundCallback(	// Returns nothing.
	void (BackgroundCall)(void))			// Callback when app processing becomes
													// background.  NULL to clear.
	{
        /* no-op. */
	}

///////////////////////////////////////////////////////////////////////////////
//
// Set a callback to be called when the application moves into the foreground.
//
///////////////////////////////////////////////////////////////////////////////
extern void rspSetForegroundCallback(	// Returns nothing.
	void (ForegroundCall)(void))			// Callback when app processing becomes
													// foreground.  NULL to clear.
	{
        /* no-op. */
	}

extern int16_t rspIsBackground(void)			// Returns TRUE if in background, FALSE otherwise
    {
        extern bool GSDLAppIsActive;
        return (int16_t) (!GSDLAppIsActive);
    }
//////////////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////////////


// Dan's stuff

extern pvr_ptr_t rspSetPvrTexture(int16_t *data, int width, int height, int depth)
{



}