/*
**	Command & Conquer Red Alert(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	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, see <http://www.gnu.org/licenses/>.
*/

/****************************************************************************
*
*        C O N F I D E N T I A L -- W E S T W O O D  S T U D I O S
*
*----------------------------------------------------------------------------
*
* PROJECT
*     VQAPlay32 library. (32-Bit protected mode)
*
* FILE
*     drawer.c
*
* DESCRIPTION
*     Frame drawing and page flip control.
*
* PROGRAMMER
*     Bill Randolph
*     Denzil E. Long, Jr.
*
* DATE
*     February 8, 1995
*
*----------------------------------------------------------------------------
*
* PUBLIC
*     VQA_Configure_Drawer - Configure the drawer routines.
*
* PRIVATE
*     Select_Frame             - Selects frame to draw and preforms frame
*                                skip.
*     Prepare_Frame            - Process/Decompress frame information.
*     DrawFrame_Xmode          - Draws a frame directly to Xmode screen.
*     DrawFrame_XmodeBuf       - Draws a frame in Xmode format to a buffer.
*     DrawFrame_XmodeVRAM      - Draws a frame in Xmode with resident
*                                Codebook.
*     PageFlip_Xmode           - Page flip Xmode display.
*     DrawFrame_MCGA           - Draws a frame directly to MCGA screen.
*     PageFlip_MCGA            - Page flip MCGA display.
*     DrawFrame_MCGABuf        - Draws a frame in MCGA format to a buffer.
*     PageFlip_MCGABuf         - Page flip a buffered MCGA display.
*     DrawFrame_VESA640        - Draws a frame in VESA640 format.
*     DrawFrame_VESA320_32K    - Draws a frame to VESA320_32K screen.
*     DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a
*                                buffer.
*     PageFlip_VESA            - Page flip VESA display.
*     DrawFrame_Buffer         - Draw a frame to a buffer.
*     PageFlip_Nop             - Do nothing page flip.
*     UnVQ_Nop                 - Do nothing UnVQ.
*     Mask_Rect                - Sets non-drawable rectangle in image.
*     Mask_Pointers            - Mask vector pointer that are in the mask
*                                rectangle.
*
****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <conio.h>
#include <dos.h>
#include <mem.h>
#include "vq.h"
#include "unvq.h"
#include "vqaplayp.h"
#include <vqm32\all.h>

/*---------------------------------------------------------------------------
 * PRIVATE DECLARATIONS
 *-------------------------------------------------------------------------*/
static long Select_Frame(VQAHandleP *vqap);
static void Prepare_Frame(VQAData *vqabuf);

#if(VQAMCGA_ON)
static long DrawFrame_MCGABuf(VQAHandle *vqa);
static void PageFlip_MCGABuf(VQAHandle *vqa);
static long DrawFrame_MCGA(VQAHandle *vqa);
static void PageFlip_MCGA(VQAHandle *vqa);
#endif /* VQAMCGA_ON */

#if(VQAXMODE_ON)
static long DrawFrame_XmodeBuf(VQAHandle *vqa);
static long DrawFrame_Xmode(VQAHandle *vqa);
static long DrawFrame_XmodeVRAM(VQAHandle *vqa);
static void PageFlip_Xmode(VQAHandle *vqabuf);
#endif /* VQAXMODE_ON */

#if(VQAVESA_ON)
static long DrawFrame_VESA640(VQAHandle *vqa);
static long DrawFrame_VESA320_32K(VQAHandle *vqa);
static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa);
static void PageFlip_VESA(VQAHandle *vqabuf);
#endif /* VQAVESA_ON */

static long DrawFrame_Buffer(VQAHandle *vqa);
static void PageFlip_Nop(VQAHandle *vqa);

#ifndef PHARLAP_TNT
static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers,
		unsigned char *buffer, unsigned long blocksperrow,
		unsigned long numrows, unsigned long bufwidth);
#else
static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers,
		FARPTR buffer, unsigned long blocksperrow, unsigned long numrows,
		unsigned long bufwidth);
#endif

#if(0)
static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1,
		unsigned long x2, unsigned long y2);
static void Mask_Pointers(VQAData *vqabuf);
#endif


/****************************************************************************
*
* NAME
*     VQA_Configure_Drawer - Configure the drawer routines.
*
* SYNOPSIS
*     VQA_Configure_Drawer(VQA)
*
*     void VQA_Configure_Drawer(VQAHandleP *);
*
* FUNCTION
*     Configure the drawing system for the current movie and configuration
*     options.
*
* INPUTS
*     VQA - Pointer to private VQAHandle.
*
* RESULT
*     NONE
*
****************************************************************************/

void VQA_Configure_Drawer(VQAHandleP *vqap)
{
	VQAData   *vqabuf;
	VQAConfig *config;
	VQAHeader *header;
	VQADrawer *drawer;
	long      origin;
	long      blkdim;

	/* Dereference commonly used data members for quicker access. */
	vqabuf = vqap->VQABuf;
	drawer = &vqabuf->Drawer;
	header = &vqap->Header;
	config = &vqap->Config;
	origin = (config->DrawFlags & VQACFGF_ORIGIN);

	/*-------------------------------------------------------------------------
	 * SET THE DRAW POSITION OF THE MOVIE.
	 *
	 * X1 = -1 -- Center image of the X axis, otherwise use X1 value.
	 * Y1 = -1 -- Center image of the Y axis, otherwise use Y1 value.
	 *-----------------------------------------------------------------------*/
	if ((config->X1 == -1) && (config->Y1 == -1)) {

		#if(VQAVIDEO_ON)
		drawer->X1 = ((drawer->Display->XRes - header->image_width) / 2);
		drawer->Y1 = ((drawer->Display->YRes - header->image_height) / 2);
		#else  /* VQAVIDEO_ON */
		drawer->X1 = ((drawer->ImageWidth - header->ImageWidth) / 2);
		drawer->Y1 = ((drawer->ImageHeight - header->ImageHeight) / 2);
		#endif /* VQAVIDEO_ON */

		drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1);
		drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1);
	} else {
		switch (origin) {
			default:
			case VQACFGF_TOPLEFT:
				drawer->X1 = config->X1;
				drawer->Y1 = config->Y1;
				drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1);
				drawer->Y2 = ((drawer->Y1 + header->ImageHeight) - 1);
				break;

			case VQACFGF_BOTLEFT:
				drawer->X1 = config->X1;
				drawer->Y1 = (drawer->ImageHeight - config->Y1);
				drawer->X2 = ((drawer->X1 + header->ImageWidth) - 1);
				drawer->Y2 = ((drawer->Y2 - header->ImageHeight) - 1);
				break;

			case VQACFGF_BOTRIGHT:
				drawer->X1 = (drawer->ImageWidth - config->X1);
				drawer->Y1 = (drawer->ImageHeight - config->Y1);
				drawer->X2 = (drawer->X1 - header->ImageWidth);
				drawer->Y2 = (drawer->Y1 - header->ImageHeight);
				break;
		}
	}

	/*-------------------------------------------------------------------------
	 * INITIALIZE THE UNVQ ROUTINE FOR THE SPECIFIED VIDEO MODE AND BLOCK SIZE.
	 *-----------------------------------------------------------------------*/

	/* Pre-compute commonly used values for speed. */
	drawer->BlocksPerRow = header->ImageWidth / header->BlockWidth;
	drawer->NumRows = header->ImageHeight / header->BlockHeight;
	drawer->NumBlocks = drawer->BlocksPerRow * drawer->NumRows;
	blkdim = BLOCK_DIM(header->BlockWidth, header->BlockHeight);

	/* Initialize draw routine vectors to a NOP routine in order to prevent
	 * a crash.
	 */
	vqabuf->UnVQ = UnVQ_Nop;
	vqabuf->Page_Flip = PageFlip_Nop;

	/* If the client specifies buffering then go ahead an set the unvq
	 * vector. All of the buffered modes use the same unvq routines.
	 */
	if (config->DrawFlags & VQACFGF_BUFFER) {
		switch (blkdim) {
			#if(VQABLOCK_2X2)
			case BLOCK_2X2:
				vqabuf->UnVQ = UnVQ_2x2;
				break;
			#endif

			#if(VQABLOCK_2X3)
			case BLOCK_2X3:
				vqabuf->UnVQ = UnVQ_2x3;
				break;
			#endif

			#if(VQABLOCK_4X2)
			case BLOCK_4X2:
				vqabuf->UnVQ = UnVQ_4x2;
				break;
			#endif

			#if(VQABLOCK_4X4)
			case BLOCK_4X4:
				vqabuf->UnVQ = UnVQ_4x4;
				break;
			#endif

			default:
				break;
 		}
 	}

	/* Initialize the draw vectors for the specified video mode. */
	switch (config->Vmode) {

		/* MCGA */
		#if(VQAMCGA_ON)
		case MCGA:
			if (config->DrawFlags & VQACFGF_BUFFER) {
				vqabuf->Draw_Frame = DrawFrame_MCGABuf;
				vqabuf->Page_Flip = PageFlip_MCGABuf;
			} else {
				vqabuf->Draw_Frame = DrawFrame_MCGA;
				vqabuf->Page_Flip = PageFlip_MCGA;

				/* MCGA uses the same unvq routines that are used for unvqing
				 * to a buffer because MCGA mode is just another buffer. However,
				 * instead of drawing to an allocated RAM buffer we are drawing
				 * directly to the VRAM video buffer.
				 */
				switch (blkdim) {
					#if(VQABLOCK_2X2)
					case BLOCK_2X2:
						vqabuf->UnVQ = UnVQ_2x2;
						break;
					#endif

					#if(VQABLOCK_2X3)
					case BLOCK_2X3:
						vqabuf->UnVQ = UnVQ_2x3;
						break;
					#endif

					#if(VQABLOCK_4X2)
					case BLOCK_4X2:
						#if(0)
						vqabuf->UnVQ = UnVQ_4x2;
						#else
						if (config->DrawFlags & VQACFGF_WOOFER) {
							vqabuf->UnVQ = UnVQ_4x2_Woofer;
						} else {
							vqabuf->UnVQ = UnVQ_4x2;
						}
						#endif
						break;
					#endif

					#if(VQABLOCK_4X4)
					case BLOCK_4X4:
						vqabuf->UnVQ = UnVQ_4x4;
						break;
					#endif
				}
			}

			/* Pre-compute the draw offset for speed. */
			drawer->ScreenOffset = ((320 * drawer->Y1) + drawer->X1);
			break;
		#endif /* VQAMCGA_ON */

		/* XMODE */
		#if(VQAXMODE_ON)
		case XMODE_320X200:
		case XMODE_320X240:
			if (config->DrawFlags & VQACFGF_BUFFER) {
				vqabuf->Draw_Frame = DrawFrame_XmodeBuf;
				vqabuf->Page_Flip = PageFlip_Xmode;
			} else {
				vqabuf->Page_Flip = PageFlip_Xmode;

				if (config->DrawFlags & VQACFGF_VRAMCB) {
					vqabuf->Draw_Frame = DrawFrame_XmodeVRAM;

					switch (blkdim) {
						#if(VQABLOCK_4X2)
						case BLOCK_4X2:
							vqabuf->UnVQ = UnVQ_4x2_XmodeCB;
							break;
						#endif /* VQABLOCK_4X2 */
					}
				} else {
					vqabuf->Draw_Frame = DrawFrame_Xmode;

					switch (blkdim) {
						#if(VQABLOCK_4X2)
						case BLOCK_4X2:
							vqabuf->UnVQ = UnVQ_4x2_Xmode;
							break;
						#endif /* VQABLOCK_4X2 */
					}
				}
			}

			/* Pre-compute the draw offset for speed. */
			drawer->ScreenOffset = (((320 / 4) * drawer->Y1) + (drawer->X1 / 4));
			break;
		#endif /* VQAXMODE_ON */

		/* VESA */
		#if(VQAVESA_ON)

		/* Currently this is a buffered mode, but should be optimized for
		 * for screen direct.
		 */
		case VESA_640X480_256:
			vqabuf->Draw_Frame = DrawFrame_VESA640;
			vqabuf->Page_Flip = PageFlip_VESA;
			break;

		case VESA_320X200_32K_1:
			if (config->DrawFlags & VQACFGF_BUFFER) {
				vqabuf->Draw_Frame = DrawFrame_VESA320_32KBuf;
			} else {
				vqabuf->Draw_Frame = DrawFrame_VESA320_32K;

				switch (blkdim) {
					#if(VQABLOCK_4X2)
					case BLOCK_4X2:
						vqabuf->UnVQ = UnVQ_4x2_VESA320_32K;
						break;
					#endif
				}
			}
			break;
		#endif /* VQAVESA_ON */

		/* Purely buffered (Video refresh is up to the client. */
		default:
			vqabuf->Draw_Frame = DrawFrame_Buffer;

			/* Pre-compute the draw offset for speed. */
			drawer->ScreenOffset = ((drawer->ImageWidth * drawer->Y1)+drawer->X1);
			break;
	}
}


/****************************************************************************
*
* NAME
*     Select_Frame - Selects frame to draw and preforms frame skip.
*
* SYNOPSIS
*     Error = Select_Frame(VQA)
*
*     long Select_Frame(VQAHandleP *);
*
* FUNCTION
*     Select a frame to draw. This is were the frame skipping/delay is
*     performed.
*
* INPUTS
*     VQA - Pointer to private VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long Select_Frame(VQAHandleP *vqap)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAConfig    *config;
	VQAFrameNode *curframe;
	long         curtime;
	long         desiredframe;

	/* Dereference commonly used data members for quicker access. */
	config = &vqap->Config;
	vqabuf = vqap->VQABuf;
	drawer = &vqabuf->Drawer;
	curframe = drawer->CurFrame;

	/* Make sure the current frame is drawable. If the frame is not ready
	 * then we must wait for the loader to catch up.
	 */
	if ((curframe->Flags & VQAFRMF_LOADED) == 0) {
		drawer->WaitsOnLoader++;
		return (VQAERR_NOBUFFER);
	}

	/* If single stepping then return with the next frame.*/
	if (config->OptionFlags & VQAOPTF_STEP) {
		drawer->LastFrame = curframe->FrameNum;
		return (0);
	}

	/* Find the frame # we should play (rounded to nearest frame): */
	curtime = VQA_GetTime();
	desiredframe = ((curtime * config->FrameRate) / VQA_TIMETICKS);

	#if(VQAMONO_ON)
	drawer->DesiredFrame = desiredframe;
	#endif

	/* Handle the cases where the player is going so fast that it's not time 
	 * to draw this frame yet.
	 *
	 * - If the Drawer is using a slower frame rate than the Loader, use a
	 *   delta-time-based wait; otherwise, use the frame number as the wait.
	 */
	if (config->DrawRate != config->FrameRate) {
		if (curtime - drawer->LastTime < (VQA_TIMETICKS / config->DrawRate)) {
			return (VQAERR_NOT_TIME);
		}
	} else {
		if (curframe->FrameNum > desiredframe) {
			return (VQAERR_NOT_TIME);
		}
	}

	/* Make sure we draw at least 5 frames per second */
	if ((curframe->FrameNum - drawer->LastFrame) >= (config->FrameRate / 5)) {
		drawer->LastFrame = curframe->FrameNum;
		return (0);
	}

	/* If frame skipping is disabled then draw every frame. */
	if (config->DrawFlags & VQACFGF_NOSKIP) {
		drawer->LastFrame = curframe->FrameNum;
		return (0);
	}

	/* Handle the case where the player is going too slow, so we have to skip 
	 * some frames:
	 *
	 * - If this is a Key Frame, draw it
	 * - If this frame's # is less than what we're supposed to draw, skip it
	 *   (Because the 1st 'desiredframe' will be 0, FrameNum MUST be typecast 
	 *   to signed WORD for the comparison; otherwise, the comparison uses 
	 *   UWORDs, and the first frame is always skipped.)
	 * - If this is a palette-set frame, set the palette before skipping it
	 * - Loop until we get the frame we need, or there's no frames available
	 */
	while (1) {

		/* No frame available; return */
		if ((curframe->Flags & VQAFRMF_LOADED) == 0) {
			return (VQAERR_NOBUFFER);
		}

		/* Force drawing of a Key Frame */
		if (curframe->Flags & VQAFRMF_KEY) {
			break;
		}

		/* Skip the frame */
		if (curframe->FrameNum < desiredframe) {

			/* Handle a palette in a skipped frame:
			 *
			 * - Stash the palette in Drawer.Palette_24
			 * - Set the Drawer.Flags VQADRWF_SETPAL bit, to tell the page-flip
			 *   routines that this palette must be set
			 */
			if (curframe->Flags & VQAFRMF_PALETTE) {

				/* Un-LCW if needed */
				if (curframe->Flags & VQAFRMF_PALCOMP) {
					curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette
							+ curframe->PalOffset, (char *)curframe->Palette,
							vqabuf->Max_Pal_Size);

					curframe->Flags &= ~VQAFRMF_PALCOMP;
				}

				/* Stash the palette */
				memcpy(drawer->Palette_24, curframe->Palette, curframe->PaletteSize);
				drawer->CurPalSize = curframe->PaletteSize;
				drawer->Flags |= VQADRWF_SETPAL;
			}

			/* Invoke callback with NULL screen ptr */
			if (config->DrawerCallback != NULL) {
				if (config->DrawerCallback(NULL, curframe->FrameNum)) {
					return VQAERR_READ;
				}
			}

			/* Skip the frame */
			curframe->Flags = 0L;
			curframe = curframe->Next;
			drawer->CurFrame = curframe;
			drawer->NumSkipped++;
		} else {
			break;
		}
	}
	
	drawer->LastFrame = curframe->FrameNum;
	drawer->LastTime = curtime;

	return (0);
}


/****************************************************************************
*
* NAME
*     Prepare_Frame - Process/Decompress frame information.
*
* SYNOPSIS
*     Prepare_Frame(VQAData)
*
*     void Prepare_Frame(VQAData *);
*
* FUNCTION
*     Decompress and preprocess the various frame elements (codebook,
*     pointers, palette, etc...)
*
* INPUTS
*     VQAData - Pointer to VQAData structure.
*
* RESULT
*     NONE
*
****************************************************************************/

static void Prepare_Frame(VQAData *vqabuf)
{
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	VQACBNode    *codebook;

	/* Dereference commonly used data members for quicker access. */
	drawer = &vqabuf->Drawer;
	curframe = drawer->CurFrame;
	codebook = curframe->Codebook;

	/* Decompress the codebook, if needed */
	if (codebook->Flags & VQACBF_CBCOMP) {

		/* Decompress the codebook. */
		LCW_Uncompress((char *)codebook->Buffer + codebook->CBOffset,
				(char *)codebook->Buffer, vqabuf->Max_CB_Size);

		/* Mark as uncompressed for the next time we use it */
		codebook->Flags &= (~VQACBF_CBCOMP);
	}

	/* Decompress the palette, if needed */
	if (curframe->Flags & VQAFRMF_PALCOMP) {
		curframe->PaletteSize = LCW_Uncompress((char *)curframe->Palette +
				curframe->PalOffset,(char *)curframe->Palette,vqabuf->Max_Pal_Size);

		/* Mark as uncompressed */
		curframe->Flags &= ~VQAFRMF_PALCOMP;
	}

	/* Decompress the pointer data, if needed */
	if (curframe->Flags & VQAFRMF_PTRCOMP) {
		LCW_Uncompress((char *)curframe->Pointers + curframe->PtrOffset,
				(char *)curframe->Pointers, vqabuf->Max_Ptr_Size);

		/* Mark as uncompressed */
		curframe->Flags &= ~VQAFRMF_PTRCOMP;
	}

	/* Mask the pointers */
	#if(0)
	Mask_Pointers(vqabuf);
	#endif
}


#if(VQAXMODE_ON)
/****************************************************************************
*
* NAME
*     DrawFrame_Xmode - Draws a frame in Xmode format (Screen direct).
*
* SYNOPSIS
*     Error = DrawFrame_Xmode(VQA)
*
*     long DrawFrame_Xmode(VQAHandle *);
*
* FUNCTION
*     Algorithm:
*       - Skip frames
*       - UnLCW frame
*       - Un-VQ into video RAM
*       - Wait on Update_Enabled
*       - Set the Flipper's CurFrame
*       - Set Update_Enabled
*       - Go to next frame
*       - User_Update:
*         Flip X-Page
*         Set Palette from Flipper.CurFrame
*
*     This function implements a sort of cooperative multitasking.  If the
*     Drawer hits a "wait state", where it has to wait for Update_Enabled
*     to toggle, it sets a flag and returns.  This flag is checked on entry
*     to see if we need to jump to the proper execution point. This should
*     improve performance on some platforms.
*
*     This routine handles small images by UnVQ'ing into the correct spot on
*     the Xmode screen.
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_Xmode(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	VQAConfig     *config;
	unsigned char *scrn;
	long          rc;

	/* Dereference data members for quick access */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;
	config = &((VQAHandleP *)vqa)->Config;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
 		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);

		/* Dereference current frame for quicker access */
		curframe = drawer->CurFrame;

		/* Un-VQ the image */
		scrn = GetXHidPage();
		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers,
				scrn + drawer->ScreenOffset, drawer->BlocksPerRow,
				drawer->NumRows, 320);

		/* Invoke user's callback routine */
		if (config->DrawerCallback != NULL) {
			config->DrawerCallback(scrn, curframe->FrameNum);
		}
	}

	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Dereference current frame for quicker access */
	curframe = drawer->CurFrame;

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     DrawFrame_XmodeBuf - Draws a frame in Xmode format to a buffer.
*
* SYNOPSIS
*     Error = DrawFrame_XmodeBuf(VQA)
*
*     long DrawFrame_XmodeBuf(VQAHandle *);
*
* FUNCTION
*     Algorithm:
*       - Skip frames
*       - UnLCW frame
*       - Un-VQ into ImageBuf
*       - Copy ImageBuf to video RAM
*       - Wait on Update_Enabled
*       - Set the Flipper's CurFrame
*       - Set Update_Enabled
*       - Go to next frame
*	      - User_Update:
*         Flip X-Page
*         Set Palette from Flipper.CurFrame
*
*     This function implements a sort of cooperative multitasking.  If the
*     Drawer hits a "wait state", where it has to wait for Update_Enabled
*     to toggle, it sets a flag and returns.  This flag is checked on entry
*     to see if we need to jump to the proper execution point. This should
*     improve performance on some platforms.
*
*     This routine handles small images by UnVQ'ing into the upper-left
*     corner of ImageBuf, then copying ImageBuf into the correct part of the
*     Xmode screen.
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_XmodeBuf(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	VQAHeader     *header;
	VQAConfig     *config;
	unsigned char *scrn;
	long          rc;

	/* Dereference data members for quicker access */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;
	header = &((VQAHandleP *)vqa)->Header;
	config = &((VQAHandleP *)vqa)->Config;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);

		/* Dereference current frame for quicker access. */
		curframe = drawer->CurFrame;

		/* Un-VQ the image */
		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers,
				drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows,
				header->ImageWidth);

		/* Copy the buffer */
		scrn = GetXHidPage();

		if ((header->ImageWidth < 320) || (header->ImageHeight < 200)) {
			Xmode_Blit(drawer->ImageBuf, scrn + drawer->ScreenOffset,
					header->ImageWidth, header->ImageHeight);
		} else {
			Xmode_BufferCopy_320x200(drawer->ImageBuf,scrn + drawer->ScreenOffset);
		}

		/* Invoke user's callback routine */
		if (config->DrawerCallback != NULL) {
			config->DrawerCallback(scrn, curframe->FrameNum);
		}
	}

	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Dereference current frame for quicker access. */
	curframe = drawer->CurFrame;

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     DrawFrame_XmodeVRAM - Draws a frame in Xmode with resident Codebook.
*
* SYNOPSIS
*     Error = DrawFrame_XmodeVRAM(VQA)
*
*     long DrawFrame_XmodeVRAM(VQAHandle *);
*
* FUNCTION
*     Algorithm:
*       - Download Codebook if it isn't already (do this before skipping
*         frames, so Select_Frame will smooth out the delay)
*       - Skip frames
*       - UnLCW frame
*       - Convert Pointers to VRAM format
*       - Un-VQ by copying codebook blocks within VRAM
*       - Wait on Update_Enabled
*       - Set the Flipper's CurFrame
*       - Set Update_Enabled
*       - Go to next frame
*       - User_Update:
*         Flip X-Page
*         Set Palette from Flipper.CurFrame
*
*     This function implements a sort of cooperative multitasking.  If the
*     Drawer hits a "wait state", where it has to wait for Update_Enabled
*     to toggle, it sets a flag and returns.  This flag is checked on entry
*     to see if we need to jump to the proper execution point. This should
*     improve performance on some platforms.
*
*     This routine handles small images by UnVQ'ing into the correct spot on
*     the Xmode screen.
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_XmodeVRAM(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	VQAConfig     *config;
	unsigned char *scrn;
	long          rc;

	/* Dereference data members for quicker access */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;
	config = &((VQAHandleP *)vqa)->Config;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);

		/* Dereference current frame for quicker access. */
		curframe = drawer->CurFrame;

		/* Download codebook to VRAM */
		if ((curframe->Codebook->Flags & VQACBF_DOWNLOADED) == 0) {
			Upload_4x2CB(curframe->Codebook->Buffer,
					((VQAHandleP *)vqa)->Header.CBentries
					+ ((VQAHandleP *)vqa)->Header.Num1Colors);

			curframe->Codebook->Flags |= VQACBF_DOWNLOADED;
		}

		/* Convert pointers to VRAM format */
		XlatePointers(curframe->Pointers, drawer->NumBlocks);

		/* Un-VQ the image */
		scrn = GetXHidPage();
		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers,
				scrn + drawer->ScreenOffset, drawer->BlocksPerRow,
				drawer->NumRows, 320);

		/* Invoke user's callback routine */
		if (config->DrawerCallback != NULL) {
			config->DrawerCallback(scrn, curframe->FrameNum);
		}
	}

	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Dereference current frame for quicker access. */
	curframe = drawer->CurFrame;

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     PageFlip_Xmode - Page flip Xmode display.
*
* SYNOPSIS
*     PageFlip_Xmode(VQA)
*
*     void PageFlip_Xmode(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     NONE
*
****************************************************************************/

static void PageFlip_Xmode(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	unsigned char *pal;
	VQAConfig     *config;
	long          palsize;
	long          slowpal;

	/* Dereference commonly used data members for quicker access. */
	config = &((VQAHandleP *)vqa)->Config;
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;
	curframe = vqabuf->Flipper.CurFrame;

	/* Pre-decode 'vqa' for speed */
	pal = curframe->Palette;
	palsize = curframe->PaletteSize;
	slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0;

	/* - If this is a palette-set frame:
	 * - Wait for NoVB (active scan)
	 * - Flip the page (doesn't take effect until next active scan
	 * - Wait for VB
	 * - Set the palette
	 * - Otherwise, just flip the page
	 */
	if (curframe->Flags & VQAFRMF_PALETTE) {
		WaitNoVB(vqabuf->VBIBit);
		FlipXPage();
		WaitVB(vqabuf->VBIBit);

		if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
			SetPalette(pal, palsize, slowpal);
		}

		/* Notify the client of the palette change. */
		if ((config->NotifyFlags & VQAEVENT_PALETTE)
				&& (config->EventHandler != NULL)) {
			config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize);
		}
	}
	else if (drawer->Flags & VQADRWF_SETPAL) {
		drawer->Flags &= (~VQADRWF_SETPAL);
		WaitNoVB(vqabuf->VBIBit);
		FlipXPage();
		WaitVB(vqabuf->VBIBit);

		if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
			SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal);
		}

		/* Notify the client of the palette change. */
		if ((config->NotifyFlags & VQAEVENT_PALETTE)
				&& (config->EventHandler != NULL)) {

			config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24,
					(long)drawer->CurPalSize);
		}
	} else {
		FlipXPage();
	}
}
#endif /* VQAXMODE_ON */


#if(VQAMCGA_ON)
/****************************************************************************
*
* NAME
*     DrawFrame_MCGA - Draws a frame directly to MCGA screen.
*
* SYNOPSIS
*     Error = DrawFrame_MCGA(VQA)
*
*     long DrawFrame_MCGA(VQAHandle *);
*
* FUNCTION
*     Algorithm:
*       - Skip frames
*       - UnLCW frame
*       - Wait on Update_Enabled
*       - Set Update_Enabled
*       - Go to next frame
*       - User_Update:
*         set palette
*         UnVQ to screen
*
*     This function implements a sort of cooperative multitasking.  If the
*     Drawer hits a "wait state", where it has to wait for Update_Enabled
*     to toggle, it sets a flag and returns.  This flag is checked on entry
*     to see if we need to jump to the proper execution point. This should
*     improve performance on some platforms.
*
*     This routine handles small images by UnVQ'ing into the correct spot on
*     the screen.
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_MCGA(VQAHandle *vqa)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	long         rc;

	/* Dereference commonly used data members for quicker access */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Select the frame to draw. */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);
	}

	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Dereference current frame for quicker access. */
	curframe = drawer->CurFrame;

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     PageFlip_MCGA - Page flip MCGA display.
*
* SYNOPSIS
*     PageFlip_MCGA(VQA)
*
*     void PageFlip_MCGA(VQAHandle *);
*
* FUNCTION
*     Since the MCGA mode only has one buffer, the drawing is actually done
*     at this point.
*
* INPUTS
*     VQA - Pointer to VQAHandle structure.
*
* RESULT
*     NONE
*
****************************************************************************/

static void PageFlip_MCGA(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	VQAConfig     *config;
	unsigned char *pal;
	unsigned char *ptrs;
	unsigned char *cb;
	long          palsize;
	long          slowpal;
	long          blocksperrow;
	long          numrows;

	#ifndef PHARLAP_TNT
	unsigned char *scrn;
	#else
	FARPTR        scrn;
	#endif

	/* Dereference commonly used data members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	config = &((VQAHandleP *)vqa)->Config;
	drawer = &vqabuf->Drawer;
	curframe = vqabuf->Flipper.CurFrame;

	/*-------------------------------------------------------------------------
	 * WAIT FOR THE VERTICAL BLANK TO SET THE PALETTE.
	 *-----------------------------------------------------------------------*/
	if ((curframe->Flags & VQAFRMF_PALETTE)
			|| (drawer->Flags & VQADRWF_SETPAL)) {

		pal = curframe->Palette;
		palsize = curframe->PaletteSize;
		slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0;

		/* Wait for the VBlank. */
	 	WaitNoVB(vqabuf->VBIBit);
	 	WaitVB(vqabuf->VBIBit);

		/* Set the palette. */
		if (curframe->Flags & VQAFRMF_PALETTE) {
			if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
				SetPalette(pal, palsize, slowpal);
			}

			/* Notify the client of the palette change. */
			if ((config->NotifyFlags & VQAEVENT_PALETTE)
					&& (config->EventHandler != NULL)) {

				config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize);
			}
		}
		else if (drawer->Flags & VQADRWF_SETPAL) {
			drawer->Flags &= (~VQADRWF_SETPAL);

			if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
				SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal);
			}

			/* Notify the client of the palette change. */
			if ((config->NotifyFlags & VQAEVENT_PALETTE)
					&& (config->EventHandler != NULL)) {

				config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24,
						(long)drawer->CurPalSize);
			}
		}
	}

	/*-------------------------------------------------------------------------
	 * UNVQ THE FRAME DIRECTLY TO THE MCGA SCREEN.
	 *-----------------------------------------------------------------------*/

	/* Get screen address, blocks per row and number of rows. */
	#ifndef PHARLAP_TNT
	scrn = (unsigned char *)(0xA0000 + (unsigned long)drawer->ScreenOffset);
	#else
	FP_SET(scrn, drawer->ScreenOffset, 0x1C);
	#endif

	blocksperrow = drawer->BlocksPerRow;
	numrows = drawer->NumRows;
	ptrs = curframe->Pointers;
	cb = curframe->Codebook->Buffer;
	vqabuf->UnVQ(cb, ptrs, scrn, blocksperrow, numrows, 320);

	/* Process captions. */
	if ((((VQAHandleP *)vqa)->Caption != NULL)
			&& (config->OptionFlags & VQAOPTF_CAPTIONS)) {
		DoCaptions(((VQAHandleP *)vqa)->Caption, curframe->FrameNum);
	}

	if ((((VQAHandleP *)vqa)->EVA != NULL)
			&& (config->OptionFlags & VQAOPTF_EVA)) {
		DoCaptions(((VQAHandleP *)vqa)->EVA, curframe->FrameNum);
	}

	/* Invoke user's callback routine */
	if (config->DrawerCallback != NULL) {
		if (config->DrawerCallback(NULL, curframe->FrameNum)) {
			vqabuf->Flags |= VQADATF_DDONE;
		}
	}
}


/****************************************************************************
*
* NAME
*     DrawFrame_MCGABuf - Draws a frame in MCGA format to a buffer.
*
* SYNOPSIS
*     Error = DrawFrame_MCGABuf(VQA)
*
*     long DrawFrame_MCGABuf(VQAHandle *);
*
* FUNCTION
*     Algorithm:
*       - Skip frames
*       - UnLCW frame
*       - Wait on Update_Enabled (can't use imgbuf til User_Update's done)
*       - Un-VQ into ImageBuf
*       - Set the Flipper's CurFrame
*       - Set Update_Enabled
*       - Go to next frame
*       - User_Update:
*         set Palette from Flipper.CurFrame
*         copy ImageBuf to screen
*
*     This function implements a sort of cooperative multitasking.  If the
*     Drawer hits a "wait state", where it has to wait for Update_Enabled
*     to toggle, it sets a flag and returns.  This flag is checked on entry
*     to see if we need to jump to the proper execution point. This should
*     improve performance on some platforms.
*
*     This routine handles small images by UnVQ'ing into the upper-left
*     corner of ImageBuf, then copying ImageBuf onto the screen.
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_MCGABuf(VQAHandle *vqa)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	long         rc;

	#ifndef PHARLAP_TNT
	unsigned char *scrn;
	#else
	FARPTR        scrn;
	#endif

	/* Dereference commonly used data members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);
	}

	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Dereference current frame for quicker access. */
	curframe = drawer->CurFrame;

	#ifndef PHARLAP_TNT
	scrn = drawer->ImageBuf;
	#else
	FP_SET(scrn, drawer->ImageBuf, 0x14);
	#endif

	/* Un-VQ the image */
	vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn,
			drawer->BlocksPerRow, drawer->NumRows,
			((VQAHandleP *)vqa)->Header.ImageWidth);

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     PageFlip_MCGABuf - Page flip a buffered MCGA display.
*
* SYNOPSIS
*     PageFlip_MCGABuf(VQA)
*
*     void PageFlip_MCGABuf(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     NONE
*
****************************************************************************/

static void PageFlip_MCGABuf(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	VQAConfig     *config;
	unsigned char *pal;
	unsigned char *buf;
	unsigned char *scrn;
	long          palsize;
	long          slowpal;
	long          imgwidth;
	long          imgheight;

	/* Derefernce commonly used data members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	config = &((VQAHandleP *)vqa)->Config;
	drawer = &vqabuf->Drawer;
	curframe = vqabuf->Flipper.CurFrame;

	/* Pre-decode 'vqa' for speed */
	pal = curframe->Palette;
	palsize = curframe->PaletteSize;
	slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0;

	/* - If this is a palette-set frame:
	 * - Wait for the vertical blank
	 * - Set the palette
	 * - Copy ImageBuf to SEENPAGE:
	 * - use blit routine if image is smaller than full-screen, since the
	 *   buffer copy assumes a full-screen image
	 */
	if (curframe->Flags & VQAFRMF_PALETTE) {
		WaitNoVB(vqabuf->VBIBit);
		WaitVB(vqabuf->VBIBit);

		if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
			SetPalette(pal, palsize, slowpal);
		}

		/* Notify the client of the palette change. */
		if ((config->NotifyFlags & VQAEVENT_PALETTE)
				&& (config->EventHandler != NULL)) {

			config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize);
		}
	}
	else if (drawer->Flags & VQADRWF_SETPAL) {
		drawer->Flags &= (~VQADRWF_SETPAL);
		WaitNoVB(vqabuf->VBIBit);
		WaitVB(vqabuf->VBIBit);

		if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
			SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal);
		}

		/* Notify the client of the palette change. */
		if ((config->NotifyFlags & VQAEVENT_PALETTE)
				&& (config->EventHandler != NULL)) {

			config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24,
					(long)drawer->CurPalSize);
		}
	}

	/* Draw image to the screen. */
	imgwidth = ((VQAHandleP *)vqa)->Header.ImageWidth;
	imgheight = ((VQAHandleP *)vqa)->Header.ImageHeight;
	buf = drawer->ImageBuf;

	#ifndef PHARLAP_TNT
	scrn = (unsigned char *)0xA0000;
	#endif

	if ((imgwidth < 320) || (imgheight < 200)) {
		#ifndef PHARLAP_TNT
		scrn += drawer->ScreenOffset;
		#else
		scrn = (unsigned char *)drawer->ScreenOffset;
		#endif

		MCGA_Blit(buf, scrn, imgwidth, imgheight);
	} else {
		MCGA_BufferCopy(buf, NULL);
	}

	/* Invoke user's callback routine */
	if (config->DrawerCallback != NULL) {
		config->DrawerCallback(NULL, curframe->FrameNum);
	}
}
#endif /* VQAMCGA_ON */


#if(VQAVESA_ON)
/****************************************************************************
*
* NAME
*     DrawFrame_VESA640 - Draws a frame in VESA 640 format.
*
* SYNOPSIS
*     Error = DrawFrame_VESA640(VQA)
*
*     long DrawFrame_VESA640(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_VESA640(VQAHandle *vqa)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	long         rc;

	#ifndef PHARLAP_TNT
	unsigned char *scrn;
	#else
	FARPTR        scrn;
	#endif

	/* Dereference data members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);
	}

	/* Dereference current frame. */
	curframe = drawer->CurFrame;

	/* Wait for Update_Enabled to be set low */
 	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		vqabuf->Drawer.WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	#ifndef PHARLAP_TNT
	scrn = drawer->ImageBuf + drawer->ScreenOffset;
	#else
	FP_SET(scrn, drawer->ImageBuf + drawer->ScreenOffset, 0x14);
	#endif

	/* Un-VQ the image */
	vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn,
			drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth);

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     DrawFrame_VESA320_32K - Draws a frame in VESA 320 format.
*
* SYNOPSIS
*     Error = DrawFrame_VESA320_32K(VQA)
*
*     long DrawFrame_VESA320_32K(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_VESA320_32K(VQAHandle *vqa)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	VQAConfig    *config;
	long         rc;
	long         grain;

	/* Dereference data members for quicker access. */
	config = &((VQAHandleP *)vqa)->Config;
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);

		/* Dereference current frame. */
		curframe = drawer->CurFrame;

		/* Translate palette to 15-bit */
		if (curframe->Flags & VQAFRMF_PALETTE) {
			TranslatePalette(curframe->Palette, drawer->Palette_15,
					curframe->PaletteSize);
		}
		else if (drawer->Flags & VQADRWF_SETPAL) {
			TranslatePalette(drawer->Palette_24, drawer->Palette_15,
					drawer->CurPalSize);
		}

		/* UnVQ directly to screen */
		grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize
				/ ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity;

		#ifndef PHARLAP_TNT
		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers,
				drawer->Palette_15, grain, 0, 0);
		#else
		{
		FARPTR temp;

		FP_SET(temp, drawer->Palette_15, 0x14);

		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, temp,
				grain, 0, 0);
		}
		#endif

		/* Invoke user's callback routine */
		if (config->DrawerCallback != NULL) {
			config->DrawerCallback(0, curframe->FrameNum);
		}
	}

	/* Wait for VQ_Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     DrawFrame_VESA320_32KBuf - Draws a frame in VESA320_32K format to a
*                                buffer.
*
* SYNOPSIS
*     Error = DrawFrame_VESA320_32KBuf(VQA)
*
*     long DrawFrame_VESA320_32KBuf(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     Error - 0 if successful, or VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_VESA320_32KBuf(VQAHandle *vqa)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	VQAConfig    *config;
	long         rc;
	long         grain;

	/* Dereference data members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	config = &((VQAHandleP *)vqa)->Config;
	drawer = &vqabuf->Drawer;
	curframe = drawer->CurFrame;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);

		/* UnVQ buffered mode */
		#ifndef PHARLAP_TNT
		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers,
				drawer->ImageBuf, drawer->BlocksPerRow, drawer->NumRows,
				((VQAHandleP *)vqa)->Header.image_width);
		#else
		{
		FARPTR scrn;

		FP_SET(scrn, drawer->ImageBuf, 0x14);

		vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, scrn,
				drawer->BlocksPerRow, drawer->NumRows,
				((VQAHandleP *)vqa)->Header.image_width);
		}
		#endif

		/* Translate the palette to 15-bit */
		if (curframe->Flags & VQAFRMF_PALETTE) {
			TranslatePalette(curframe->Palette, drawer->Palette_15,
					curframe->PaletteSize);
		}
		else if (drawer->Flags & VQADRWF_SETPAL) {
			TranslatePalette(drawer->Palette_24, drawer->Palette_15,
					drawer->CurPalSize);
		}

		/* Copy the buffer */
		grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize
				/ ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity;

		Buf_320x200_To_VESA_32K(drawer->ImageBuf, drawer->Palette_15, grain);

 		/* Invoke user's callback routine */
		if (config->DrawerCallback != NULL) {
			config->DrawerCallback(0, curframe->FrameNum);
		}
	}

	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		vqabuf->Flags &= (~VQADATF_DSLEEP);
		drawer->WaitsOnFlipper++;
	}

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     PageFlip_VESA - Page flip VESA display.
*
* SYNOPSIS
*     PageFlip_VESA(VQA)
*
*     void PageFlip_VESA(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQAHandle.
*
* RESULT
*     NONE
*
****************************************************************************/

static void PageFlip_VESA(VQAHandle *vqa)
{
	VQAData       *vqabuf;
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	VQAConfig     *config;
	unsigned char *pal;
	long          palsize;
	long          slowpal;
	long          grain;

	/* Dereference date members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	config = &((VQAHandleP *)vqa)->Config;
	drawer = &vqabuf->Drawer;
	curframe = vqabuf->Flipper.CurFrame;

	/* Pre-decode 'vqa' for speed */
	pal = curframe->Palette;
	palsize = curframe->PaletteSize;
	slowpal = (config->OptionFlags & VQAOPTF_SLOWPAL) ? 1 : 0;

	/* Set the palette */
	if (curframe->Flags & VQAFRMF_PALETTE) {
		WaitNoVB(vqabuf->VBIBit);
		WaitVB(vqabuf->VBIBit);

		if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
			SetPalette(pal, palsize, slowpal);
		}

		/* Notify the client of the palette change. */
		if ((config->NotifyFlags & VQAEVENT_PALETTE)
				&& (config->EventHandler != NULL)) {

			config->EventHandler(VQAEVENT_PALETTE, (void *)pal, (long)palsize);
		}
	}
	else if (drawer->Flags & VQADRWF_SETPAL) {
		drawer->Flags &= (~VQADRWF_SETPAL);
		WaitNoVB(vqabuf->VBIBit);
		WaitVB(vqabuf->VBIBit);

		if (!(config->OptionFlags & VQAOPTF_PALOFF)) {
			SetPalette(drawer->Palette_24, drawer->CurPalSize, slowpal);
		}

		/* Notify the client of the palette change. */
		if ((config->NotifyFlags & VQAEVENT_PALETTE)
				&& (config->EventHandler != NULL)) {

			config->EventHandler(VQAEVENT_PALETTE, (void *)drawer->Palette_24,
					(long)drawer->CurPalSize);
		}
	}

	/* Copy the buffer */
	grain = ((VESAModeInfo *)(drawer->Display->Extended))->WinSize
			/ ((VESAModeInfo *)(drawer->Display->Extended))->WinGranularity;

	switch (((VQAHandleP *)vqa)->Header.image_width) {
		case 320:
			if (config->DrawFlags & VQACFGF_SCALEX2) {
				Buf_320x200_To_VESA_640x400(drawer->ImageBuf, grain);
			} else {
				Buf_320x200_To_VESA_320x200(drawer->ImageBuf, grain);
			}
			break;

		default:
			VESA_Blit_640x480(drawer->Display, drawer->ImageBuf, drawer->X1,
					drawer->Y1, ((VQAHandleP *)vqa)->Header.image_width,
					((VQAHandleP *)vqa)->Header.image_height);
			break;
	}

	/* Invoke user's callback routine */
	if (config->DrawerCallback != NULL) {
		config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum);
	}
}
#endif /* VQAVESA_ON */


/****************************************************************************
*
* NAME
*     DrawFrame_Buffer - Draw a frame to a buffer.
*
* SYNOPSIS
*     Error = DrawFrame_Buffer(VQA)
*
*     long DrawFrame_Buffere(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQA handle.
*
* RESULT
*     Error - 0 if successful, otherwise VQAERR_??? error code.
*
****************************************************************************/

static long DrawFrame_Buffer(VQAHandle *vqa)
{
	VQAData      *vqabuf;
	VQADrawer    *drawer;
	VQAFrameNode *curframe;
	VQAConfig    *config;
	long         rc;

	#ifndef PHARLAP_TNT
	unsigned char *buff;
	#else
	FARPTR        buff;
	#endif

	/* Dereference data members for quicker access. */
	config = &((VQAHandleP *)vqa)->Config;
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	drawer = &vqabuf->Drawer;

	/* Check our "sleep" state */
	if (!(vqabuf->Flags & VQADATF_DSLEEP)) {

		/* Find the frame to draw */
		if ((rc = Select_Frame((VQAHandleP *)vqa)) != 0) {
			return (rc);
		}

		/* Uncompress the frame data */
		Prepare_Frame(vqabuf);
	}
 
	/* Wait for Update_Enabled to be set low */
	if (vqabuf->Flags & VQADATF_UPDATE) {
		vqabuf->Flags |= VQADATF_DSLEEP;
		return (VQAERR_SLEEPING);
	}

	if (vqabuf->Flags & VQADATF_DSLEEP) {
		drawer->WaitsOnFlipper++;
		vqabuf->Flags &= (~VQADATF_DSLEEP);
	}

	/* Dereference current frame for quicker access. */
	curframe = drawer->CurFrame;

	#ifndef PHARLAP_TNT
	buff = (unsigned char *)(drawer->ImageBuf + drawer->ScreenOffset);
	#else
	FP_SET(buff, drawer->ImageBuf + drawer->ScreenOffset, 0x14);
	#endif

	/* Un-VQ the image */
	vqabuf->UnVQ(curframe->Codebook->Buffer, curframe->Pointers, buff,
			drawer->BlocksPerRow, drawer->NumRows, drawer->ImageWidth);

	/* Update data for mono output */
	drawer->LastFrameNum = curframe->FrameNum;

	/* Tell the flipper which frame to use */
	vqabuf->Flipper.CurFrame = curframe;

	/* Set the page-avail flag for the flipper */
	vqabuf->Flags |= VQADATF_UPDATE;

	/* Invoke user's callback routine */
	if (config->DrawerCallback != NULL) {
		config->DrawerCallback(drawer->ImageBuf, curframe->FrameNum);
	}

	/* Move to the next frame */
	drawer->CurFrame = curframe->Next;

	return (0);
}


/****************************************************************************
*
* NAME
*     UnVQ_Nop - Do nothing UnVQ.
*
* SYNOPSIS
*     UnVQ_Nop(Codebook, Pointers, Buffer, BPR, Rows, BufWidth)
*
*     void UnVQ_Nop(unsigned char *, unsigned char *, unsigned char *,
*                   unsigned long, unsigned long, unsigned long);
* FUNCTION
*
* INPUTS
*     Codebook - Not used. (Prototype placeholder)
*     Pointers - Not used. (Prototype placeholder)
*     Buffer   - Not used. (Prototype placeholder)
*     BPR      - Not used. (Prototype placeholder)
*     Rows     - Not used. (Prototype placeholder)
*     BufWidth - Not used. (Prototype placeholder)
*
* RESULT
*     NONE
*
****************************************************************************/

#ifndef PHARLAP_TNT
static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers,
		unsigned char *buffer, unsigned long blocksperrow,
		unsigned long numrows, unsigned long bufwidth)
#else
static void cdecl UnVQ_Nop(unsigned char *codebook, unsigned char *pointers,
		FARPTR buffer, unsigned long blocksperrow, unsigned long numrows,
		unsigned long bufwidth)
#endif
{
	/* Suppress compiler warnings */
	codebook = codebook;
	pointers = pointers;
	buffer = buffer;
	blocksperrow = blocksperrow;
	numrows = numrows;
	bufwidth = bufwidth;
}


/****************************************************************************
*
* NAME
*     PageFlip_Nop - Do nothing page flip.
*
* SYNOPSIS
*     PageFlip_Nop(VQA)
*
*     void PageFlip_Nop(VQAHandle *);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQA handle.
*
* RESULT
*     NONE
*
****************************************************************************/

static void PageFlip_Nop(VQAHandle *vqa)
{
	vqa = vqa;
}


#if(0)
/****************************************************************************
*
* NAME
*     Mask_Rect - Sets non-drawable rectangle in image.
*
* SYNOPSIS
*     Mask_Rect(VQA, X1, Y1, X2, Y2)
*
*     void Mask_Rect(VQAHandle *, unsigned long, unsigned long,
*                    unsigned long, unsigned long);
*
* FUNCTION
*
* INPUTS
*     VQA - Pointer to VQA handle.
*     X1  - X coordinate of upper-left corner
*     Y1  - Y coordinate of upper-left corner
*     X2  - X coordinate of lower-right corner
*     Y2  - Y coordinate of lower-right corner
*
* RESULT
*     NONE
*
****************************************************************************/

static void Mask_Rect(VQAHandle *vqa, unsigned long x1, unsigned long y1,
		unsigned long x2, unsigned long y2)
{
	VQAData   *vqabuf;
	VQADrawer *drawer;
	VQAHeader *header;
	long      blocks_per_row;

	/* Dereference data members for quicker access. */
	vqabuf = ((VQAHandleP *)vqa)->VQABuf;
	header = &((VQAHandleP *)vqa)->Header;
	drawer =  &vqabuf->Drawer;

	/* Clip coords to image size */
	if (x1 < vqabuf->Drawer.X1) {
		x1 = vqabuf->Drawer.X1;
	}

	if (y1 < vqabuf->Drawer.Y1) {
		y1 = vqabuf->Drawer.Y1;
	}

	if (x2 > vqabuf->Drawer.X2) {
		x2 = vqabuf->Drawer.X2;
	}

	if (y2 > vqabuf->Drawer.Y2) {
		y2 = vqabuf->Drawer.Y2;
	}

	/* Convert pixel coords to block coords */
	x1 /= header->block_width;
	x2 /= header->block_width;
	y1 /= header->block_height;
	y2 /= header->block_height;

	/* Compute the mask values */
	blocks_per_row = (header->image_width / header->block_width);
	vqabuf->Drawer.MaskStart = blocks_per_row * y1 + x1;

	if (x1 == x2) {
		drawer->MaskWidth = 0;
	} else {
		drawer->MaskWidth = x2 - x1 + 1;
	}

	if (y1 == y2) {
		drawer->MaskHeight = 0;
	} else {
		drawer->MaskHeight = y2 - y1 + 1;
	}
}


/****************************************************************************
*
* NAME
*     Mask_Pointers - Mask vector pointer that are in the mask rectangle.
*
* SYNOPSIS
*     Mask_Pointers(VQAData)
*
*     void Mask_Pointers(VQAData *);
*
* FUNCTION
*
* INPUTS
*     VQAData - Pointer to VQAData structure.
*
* RESULT
*     NONE
*
****************************************************************************/

static void Mask_Pointers(VQAData *vqabuf)
{
	VQADrawer     *drawer;
	VQAFrameNode  *curframe;
	unsigned long *ptr;
	unsigned long i,j;
	unsigned long start;

	/* Dereference data members for quicker access. */
	drawer = &vqabuf->Drawer;
	curframe = drawer->CurFrame;
	start = vqabuf->Drawer.MaskStart;

	for (i = 0; i < drawer->MaskHeight; i++) {
		ptr = (unsigned long *)(curframe->Pointers) + start;

		for (j = 0; j < drawer->MaskWidth; j++) {
			ptr[j] = VQA_MASK_POINTER;
		}

		start += drawer->BlocksPerRow;
	}
}
#endif

