#ifndef NDEBUG

/* ********************************************************************	*
 *																		*
 *	DB.C - Debugging functions.											*
 *																		*
 *	By: Garry Lancaster									Version: 2.0	*
 *																		*
 * ******************************************************************** */

/* N O T E S ********************************************************** */

/* A lot of these functions should be called via macros defined in db.h */

/* If you don't want to link this file with Windows OS files set the 
 * define DB_NOWINDOWS. This will also stop linking with the Direct Draw
 * stuff, which is, after all, a part of Windows. If you want Windows 
 * stuff, but NOT Direct Draw, define DB_NODIRECTDRAW.
 */
 
/* ******************************************************************** *
 * 																		*
 *	I N T E R F A C E - both internal and external.						*
 * 																		*
 * ******************************************************************** */

/* I N C L U D E S **************************************************** */

/* Windows includes. Actually internal, but here to allow pre-compilation. */
#include "advwin32.h"
#ifndef DB_NOWINDOWS
	#include <windows.h>
	#include "advwin32.h"
#endif	
#ifndef DB_NODIRECTDRAW
	#include <ddraw.h>
#endif
#include "db.h"	 /* Contains most off the interface. */

/* G L O B A L S ****************************************************** */
/* Have external linkage. */
volatile BOOL DZ_NULL;

/* This variable dictates whether macros ending _opt get executed. */
int db_option = 0; /* Default is off. */

/* ******************************************************************** *
 * 																		*
 *	I N T E R N A L 													*
 * 																		*
 * ******************************************************************** */

/* I N C L U D E S **************************************************** */

/* Defining DB_NOWINDOWS implies DB_NODIRECTDRAW should	also be defined. */
#ifdef DB_NOWINDOWS
	#ifndef DB_NODIRECTDRAW
		#define DB_NODIRECTDRAW
	#endif
#endif
		
/* ANSI includes. */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>
#include <direct.h>	/* For getcwd() */
#include <stdarg.h>	/* For variable arguments. */

/* C O N S T A N T S ************************************************** */

/* Possible return value for MessageBox() */
#define NO_MEMORY	0

/* Possible value for the width field of a font_struct. */
#define PROP_WIDTH 0

/* Logfile name */
#define LOGFILE_NAME "LOGFILE.TXT"

/* Set this to 1 if the logfile name is an absolute path. Otherwise the
 * logfile will go in the directory that is current when db_log_init() 
 * is called.
 */
#define ABSOLUTE_PATH	0

/* M A C R O S ******************************************************** */

/* Causes a division by zero exception. */
#define DB_FORCE_EXCEPTION()	( db_vol_zero = 1 / db_vol_zero )

/* Cause a brakepoint. */
#define DB_FORCE_BRAKEPOINT()	do {__asm int 3} while(0)

/* T Y P E S ********************************************************** */


typedef struct font_struct *fontPtr;

struct font_struct {
	void *dummy1;
	unsigned short dummy2, dummy3;
	void *dummy4;
	char filename[16];
	unsigned short width, height;
	unsigned short ascent, avgwidth;
	unsigned char byte_width;
	unsigned char filler1;
	short filler2;
	char facename[28];
	unsigned short *prop_width_dataP;
	unsigned char *bitmapP;
};

union PtrPackTag
{
	unsigned char  *cP;
	unsigned short *wP;
	unsigned long  *lP;
};

/* G L O B A L ******************************************************** */
/* Should all be static. */
static BOOL	db_use_brakepoints = FALSE;

/* Name of file to output log messages to. */
static char LogFileNameP[255];

/* Have we initialized the log file? */
static int InitialisedLog = 0;

/* Type of display mode we are in. */
static int db_display_type = DB_DOS;

/* For DirectDraw mode. */
static struct db_dd_mode_tag dd_mode = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0};
static fontPtr FontP = NULL;

/* Volatile zero. */
static volatile int db_vol_zero = 0;

/* Text strings for output. */
static const char* db_assert_textA[ 3 ] =
{
	"ASSERTION FAILED!",
	"Expression: %s",
	"File: %s Line: %d"
};
static const char* db_prompt_std =
	"Quit program/force e(x)ception? [y/n/x]";
static const char* db_prompt_windows =
	"Quit program? [Yes/No]/force exception? [Cancel]";
static const char* db_assert_log_begin_text =
	"DB: FAILED ASSERTION BEGINS";
static const char* db_assert_log_end_text =
	"DB: FAILED ASSERTION ENDS";
static const char* db_msg_log_begin_text =
	"DB: MSG BEGINS";
static const char* db_msg_log_end_text =
	"DB: MSG ENDS";
	
#ifdef DB_NOWINDOWS
static char db_log_file_name[261] = LOGFILE_NAME;
#else
static char db_log_file_name[MAX_PATH+1] = LOGFILE_NAME;
#endif


/* P R O T O S ******************************************************** */
/* Should all be static. */

static void db_do_std_prompt(unsigned yOffset);

#ifndef DB_NOWINDOWS
static void db_do_win_prompt(const char* titleStrP, const char* bodyStrP);
#endif /* ifndef DB_NOWINDOWS */

#ifndef DB_NODIRECTDRAW

/* Load debugging font, return NULL if we fail. */
static fontPtr guiload_font(char *new_fname);

/* Cleanup function for the above. */
static fontPtr CleanupFontLoadFail(HANDLE fH, fontPtr fontP);

/* Outputs debugging text. */
static void out_text(LPDIRECTDRAWSURFACE surfP, int xc, int yc, 
	const char *text, short x_limit, fontPtr fP);

/* Debounce all the keys the Direct Draw stuff uses. */	
static void Debounce(void);

/* Wait for any outstanding flip operations to be completed, provided
 * that the user specified DB_FLIP in the bltOrFlip field of the 
 * db_dd_mode_tag function. If they specified DB_BLIT this function returns
 * immediately.
 */
static void DbWaitForHw(void);
	
/* Flips between the draw and visible surfaces. */	
static void DbFlip(void);

/* Blits the contents of the draw surface to the visible surface. */
static void DbBlt(void);
	
#endif /* ifndef DB_NODIRECTDRAW */

/* F U N C T I O N S **************************************************	*/

/* ******************************************************************** *
 * 																		*
 *	I N T E R F A C E - both internal and external.						*
 * 																		*
 * ******************************************************************** */

/* NEW FNS for formatted debug strings. */
void __cdecl db_logf_fired(const char *fmtStrP, ...)
{
	char msg[ 1024 ];
	va_list varArgList;
	
	va_start( varArgList, fmtStrP );
	vsprintf( msg, fmtStrP, varArgList );
	va_end( varArgList );

	db_log_fired( msg );
}

void __cdecl db_printf_fired(int x, int y, const char *fmtStrP, ...)
{
	char msg[ 256 ];
	va_list varArgList;
	
	va_start( varArgList, fmtStrP );
	vsprintf( msg, fmtStrP, varArgList );
	va_end( varArgList );

	db_print_fired( x, y, msg );
}

void __cdecl db_msgf_fired(const char *fmtStrP, ...)
{
	char msg[ 256 ];
	va_list varArgList;
	
	va_start( varArgList, fmtStrP );
	vsprintf( msg, fmtStrP, varArgList );
	va_end( varArgList );

	db_msg_fired( (const char *) msg );
}

/* Called whenever an assertion fails. */
void db_assert_fail(const char *exprP, const char *fileP, int line)
{
	db_log_fired( db_assert_log_begin_text );
	db_log_fired( db_assert_textA[ 0 ] );
	db_logf_fired( db_assert_textA[ 1 ], exprP );
	db_logf_fired( db_assert_textA[ 2 ], fileP, line );
	db_log_fired( db_assert_log_end_text );

	switch(db_display_type)
	{
		case DB_DOS:
			printf( db_assert_textA[ 0 ] );
			printf("\n");
			printf( db_assert_textA[ 1 ], exprP	);
			printf("\n");
			printf( db_assert_textA[ 2 ], fileP, line );
			printf("\n");
			db_do_std_prompt( 0 );
			break;
#ifndef DB_NODIRECTDRAW			
		case DB_DIRECTDRAW:
			{
				char msg[256];
				unsigned short xLimit = (unsigned short) dd_mode.width;
				
				/* Wait for any hardware to finish flipping. */
				DbWaitForHw();
				
				out_text((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP,
					0, 0, db_assert_textA[ 0 ], xLimit, FontP);
				wsprintf(msg, db_assert_textA[ 1 ], exprP);
				out_text((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP,
					0, 16, msg, xLimit, FontP);
				wsprintf(msg, db_assert_textA[ 2 ], fileP, line);
				out_text((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP,
					0, 32, msg, xLimit, FontP);
				db_do_std_prompt( 48 );
			}	
			break;
#endif	
#ifndef DB_NOWINDOWS		
		case DB_WINDOWS:
			{
				char fmtMsg[ 256 ];
				char msg[256];
				
				strcpy( fmtMsg, db_assert_textA[ 1 ] );
				strcat( fmtMsg, db_assert_textA[ 2 ] );
				strcat( fmtMsg, db_prompt_windows );
				sprintf(msg, fmtMsg, exprP, fileP, line);
				
				db_do_win_prompt( db_assert_textA[ 0 ], msg );
			}
			break;
#endif			
		default:
			break;
	}
}

/* Displays a message and has the program pause until the user responds
 * to it.
 */
void db_msg_fired(const char *strP)
{
	db_log_fired( db_msg_log_begin_text );
	db_log_fired( strP );
	db_log_fired( db_msg_log_end_text );

	switch(db_display_type)
	{
		case DB_DOS:
			printf("%s\n", strP);
			db_do_std_prompt( 0 );
			break;
#ifndef DB_NOWINDOWS			
		case DB_WINDOWS:
			db_do_win_prompt( "Debugging Message", strP );
			break;
#endif
#ifndef DB_NODIRECTDRAW			
		case DB_DIRECTDRAW:
			{
				unsigned short xLimit = (unsigned short) dd_mode.width;
				
				/* Wait for any flip hardware to be ready. */
				DbWaitForHw();
				
				out_text((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP,
					0, 0, strP, xLimit, FontP);
				db_do_std_prompt( 16 );
			}	
			break;	
#endif
		default:
			break;				
	}
}

/* Displays a message and continues program execution immediately. */
void db_print_fired(int x, int y, const char *strP)
{
	switch(db_display_type)
	{
		case DB_DOS:
			printf("%s\n", strP);
			break;
#ifndef DB_NOWINDOWS			
		case DB_WINDOWS:
			break;
#endif
#ifndef DB_NODIRECTDRAW		
		case DB_DIRECTDRAW:
		{
			unsigned short xLimit = (unsigned short) (dd_mode.width - x);
			out_text((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP, x, y, 
				strP, xLimit, FontP);
			break;
		}	
#endif
		default:
			break;			
	}		
}

/* Writes a message to a log file. */
/* At least files can be output in the same way under DOS, Windows and 
 * Direct Draw!
 */
void db_log_fired(const char *strP)
{
	/* Have we intialised the file?	*/
	if(!InitialisedLog) db_log_init();
	{
		/* Open a file for appending, creating one if it doesn't yet exist. */
		FILE *fP = fopen(LogFileNameP, "a+");

		if(!fP) return;

		fprintf(fP, "%s\n", strP);
		fclose(fP);
	}
}

void db_log_init(void)
{
	#if ABSOLUTE_PATH
	sprintf( LogFileNameP, "%s", db_log_file_name ); 
	#else
	/* Append the log file name to the current working directory. */
	sprintf( LogFileNameP, "%s\\%s", getcwd( LogFileNameP, 240 ),
		db_log_file_name );
	#endif
	
	/* Delete old log file. */
	remove(LogFileNameP);
	
	/* Flag that we have initialised the log file. */
	InitialisedLog = 1;
}

extern void db_set_log_file_ex(const char *strP)
{
	InitialisedLog = 0;
	
	strcpy(db_log_file_name, strP);
}

void db_set_mode_ex(int mode, void *modeInfoP, void *newFontP)
{
	db_display_type = mode;	
#ifndef DB_NODIRECTDRAW	
	if(dd_mode.visibleSurfaceP)
	{
		IDirectDrawSurface_Release((LPDIRECTDRAWSURFACE) dd_mode.visibleSurfaceP);
		dd_mode.visibleSurfaceP = NULL;
	}
	
	if(dd_mode.drawSurfaceP)
	{
		IDirectDrawSurface_Release((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP);
		dd_mode.drawSurfaceP = NULL;
	}

	if(mode == DB_DIRECTDRAW) 
	{
		dd_mode = *((struct db_dd_mode_tag *) modeInfoP);

		if(dd_mode.visibleSurfaceP)
		{
			IDirectDrawSurface_AddRef((LPDIRECTDRAWSURFACE) dd_mode.visibleSurfaceP);
		}

		if(dd_mode.drawSurfaceP)
		{
			IDirectDrawSurface_AddRef((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP);
		}

		if(!FontP)
		{
			if(!newFontP)
				FontP = guiload_font("DIALOG.FNT");	
			else
				FontP = (fontPtr) newFontP;
		}
		if(!FontP) 
		{
			db_log_fired("DB ERROR: Font load failed. Exiting...");
			exit(0);
		}	
	}	
#endif	
}

/* Called to set whether exceptions or brakepoints are called. */
void DbUseBrakepoints(BOOL use_brakepoints)
{
	db_use_brakepoints = use_brakepoints;
}

int db_get_mode(void** modeInfoPP, void **FontPP)
{
	// blank return areas
	*FontPP = NULL;
	*modeInfoPP = NULL;

#ifndef DB_NODIRECTDRAW
	if(db_display_type == DB_DIRECTDRAW)
	{
		// copy font data
		*FontPP = (void *) FontP;

		// copy surface data
		*modeInfoPP = (void *) &dd_mode;
	}
#endif
	return db_display_type;
}

void db_uninit(void)
{
	#ifndef DB_NODIRECTDRAW
	if(FontP)
	{
		if(FontP->bitmapP)
		{
			GlobalFree(FontP->bitmapP);
		}
		if(FontP->prop_width_dataP)
		{
			GlobalFree(FontP->prop_width_dataP);
		}
		
		GlobalFree(FontP);
		FontP = NULL;
	}

	if(dd_mode.visibleSurfaceP)
	{
		IDirectDrawSurface_Release((LPDIRECTDRAWSURFACE) dd_mode.visibleSurfaceP);
	}

	if(dd_mode.drawSurfaceP)
	{
		IDirectDrawSurface_Release((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP);
	}

	#endif
}

/* ******************************************************************** *
 * 																		*
 *	I N T E R N A L 													*
 * 																		*
 * ******************************************************************** */

static void db_do_std_prompt(unsigned yOffset)
{
	int ch = 0;

	switch(db_display_type)
	{
		case DB_DOS:
			printf( db_prompt_std );
			printf("\n");
			do
			{
				ch = toupper(getch());
			}
			while((ch != 'N') && (ch != 'Y') && (ch != 'X'));
			break;
#ifndef DB_NODIRECTDRAW
		case DB_DIRECTDRAW:
		{
			SHORT response;
			BOOL done = FALSE;
			unsigned short xLimit = (unsigned short) dd_mode.width;
			
			out_text((LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP,
				0, yOffset, db_prompt_std, xLimit, FontP);
			
			/* Show the message. */
			if(dd_mode.bltOrFlip == DB_FLIP) 
			{
				DbFlip();
			}
			else 
			{
				DbBlt();
			}
			
			/* Wait for a valid key press. */
			do
			{
				response = GetAsyncKeyState('Y');
				if(response & 0x8000) 
				{
					ch = 'Y';
					done = TRUE;
				}	
				response = GetAsyncKeyState('N');
				if(response & 0x8000) 
				{
					ch = 'N';
					done = TRUE;
				}	
				response = GetAsyncKeyState('X');
				if(response & 0x8000)
				{
					ch = 'X';
					done = TRUE;
				}
			}
			while(!done);
			
			Debounce();		
			
			/* Return the flip surfaces to their pre-message state. */
			if(dd_mode.bltOrFlip == DB_FLIP) 
			{
				DbFlip();
			}			
			break;
		}
#endif /* ifndef DB_NODIRECTDRAW */
	}/* switch(db_display_type) */

	if(ch == 'Y')
	{
		exit(-10);
	}
	else if(ch == 'X')
	{
		if(db_use_brakepoints)
		{
			DB_FORCE_BRAKEPOINT();
		}
		else
		{
			DB_FORCE_EXCEPTION();
		}
	}

}/* db_do_std_prompt() */

#ifndef DB_NOWINDOWS
static void db_do_win_prompt(const char* titleStrP, const char* bodyStrP)
{
	int response;

	response = MessageBox
	(
		NULL, 				/* Dialog has no KNOWN parent window. 			*/
		bodyStrP,  			/* Message to go in box. 						*/
		titleStrP,			/* Box title. 									*/
		MB_YESNOCANCEL| 	/* Put up a 'Yes' and a 'No' button. 			*/
		MB_SETFOREGROUND| 	/* Shove message box to front of display. 		*/
		MB_ICONEXCLAMATION| /* Use an exclamation mark to decorate box. 	*/
		MB_TASKMODAL 		/* Suspend current task until box is closed.	*/
	);
	
	if((response == IDYES) || (response == NO_MEMORY)) 
	{
		exit( -10 );
	}
	else if(response == IDCANCEL) 
	{
		if(db_use_brakepoints)
		{
			DB_FORCE_BRAKEPOINT();
		}
		else
		{
			DB_FORCE_EXCEPTION();
		}
	}
}/* db_do_win_prompt() */
#endif /* ifndef DB_NOWINDOWS */

#ifndef DB_NODIRECTDRAW

static fontPtr guiload_font(char *new_fname)
{
 	HANDLE *fH;
	BOOL status;
	unsigned short c, byte, y;
	union PtrPackTag dest; 
	unsigned char inByte;
	fontPtr fntSP;
	DWORD bytesRead;

	/* Open file for reading */
	fH = CreateFile(new_fname, GENERIC_READ, 0, NULL, OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if(!fH) 
	{
		db_log_fired("DB ERROR: Couldn't open font file.");
		return CleanupFontLoadFail(NULL, NULL);
	}
	

	/* Allocate memory for our font structure */
	fntSP = (fontPtr) 
		GlobalAlloc(GMEM_FIXED, sizeof(struct font_struct));
	if(!fntSP) 
	{
		db_log_fired("DB ERROR: Not enough memory for font structure.");
		return CleanupFontLoadFail(fH, NULL);
	}	
	
	/* Nullify pointers inside the font structure. */
	fntSP->bitmapP = NULL;
	fntSP->prop_width_dataP = NULL;
	
	/* Load up font structure */
	status = ReadFile(fH, fntSP, sizeof(struct font_struct), &bytesRead, 
		NULL);
	if((!status) || (bytesRead != sizeof(struct font_struct)))
	{
		db_log_fired("DB ERROR: Error reading structure from font file.");
		return CleanupFontLoadFail(fH, fntSP);
	}
		
	/* Allocate memory for font bitmap */
	fntSP->bitmapP = (unsigned char *) GlobalAlloc(GMEM_FIXED, 
		fntSP->byte_width * dd_mode.bitsPerPixel * fntSP->height * 128);
	if(!fntSP->bitmapP)	
	{
		db_log_fired("DB ERROR: Not enough memory for font bitmap.");
		return CleanupFontLoadFail(fH, fntSP);
	}	
	
	/* Work out proportional text widths for this font
 	 * (if the font is proportional).
	 */
	if(fntSP->width == PROP_WIDTH)
	{
		/* Allocate memory for proportional width data */
		fntSP->prop_width_dataP = (unsigned short *) 
			GlobalAlloc(GMEM_FIXED, 256);
		if(!fntSP->prop_width_dataP) 
		{
			db_log_fired("DB ERROR: Not enough memory for proportional "
				"font data");
			return CleanupFontLoadFail(fH, fntSP);
		}	

		/* Read proportional width data */
		status = ReadFile(fH, fntSP->prop_width_dataP, 256, &bytesRead,
			NULL);
		if((!status) || (bytesRead != 256))
		{
			db_log_fired("DB ERROR: Error reading proportional font data "
				"from file.");
			return CleanupFontLoadFail(fH, fntSP);
		}
			
		/* Round width of font to nearest long word. */
		{
			int i;
			unsigned short *propP = fntSP->prop_width_dataP;
			
			if(dd_mode.bitsPerPixel == 8)
			{
				for(i = 0; i < 128; i ++)
				{
					/* Round up to 4 pixels. */
					*propP = (unsigned short) ((*propP + 3U) & (~3U));
				}
				propP++;
			}
			else
			{
				for(i = 0; i < 128; i ++)
				{
					/* Round up to 2 pixels. */
					*propP = (unsigned short) ((*propP + 1U) & (~1U));
				}
				propP++;
			}
		}	
	}
	else
	{
		/* Round width to nearest long word. */
		if(dd_mode.bitsPerPixel == 8)
		{
			/* Round up to 4 pixels. */
			fntSP->width = (unsigned char) ((fntSP->width + 2) & (~3));
		}
		else
		{
			/* Round up to 2 pixels. */
			fntSP->width = (unsigned char) ((fntSP->width + 1) & (~1));
		}
	}
	
	/* Load up bitmap data */
	dest.cP = fntSP->bitmapP;
	for(c = 0; c < 128; c ++)
	{
		for(y = 0; y < fntSP->height; y ++)
		{
			for(byte = 0; byte < fntSP->byte_width; byte ++)
			{
				unsigned char bitMask = 0x80;
				
				/* Read a byte of data */
				status = ReadFile(fH, &inByte, 1, &bytesRead, NULL);
				if((!status) || (bytesRead != 1))
				{
					db_log_fired("DB ERROR: Error reading font bitmap from "
						"file.");
					return CleanupFontLoadFail(fH, fntSP);
				}
				
				/* Translate 1 bit per pixel data into current Direct Draw
				 * screen mode bit depth.
				 */
				if(dd_mode.bitsPerPixel == 8)
				{ 
					do 
					{
						if(inByte & bitMask) *(dest.cP) = 
							(unsigned char) dd_mode.foreCol;
						else *(dest.cP) = (unsigned char) dd_mode.backCol;
						dest.cP++;
						
						/* Shift bitMask 1 bit to the right */
						bitMask >>= 1;
					}
					while(bitMask);
				}
				else
				{
					do 
					{
						if(inByte & bitMask) *(dest.wP) = dd_mode.foreCol;
						else *(dest.wP) = dd_mode.backCol;
						dest.wP++;
						
						/* Shift bitMask 1 bit to the right */
						bitMask >>= 1;
					}
					while(bitMask);
				}
			}
		}
	}

	/* Close the font file */
	CloseHandle(fH);

	return fntSP;
}

static fontPtr CleanupFontLoadFail(HANDLE fH, fontPtr fontP)
{
	/* Close file if necessary */
	if(fH) CloseHandle(fH);
	
	/* Is the font struct allocated? */
	if(fontP)
	{
		/* Yes. Is the bitmap allocated? If so, free it. */
		if(fontP->bitmapP) GlobalFree(fontP->bitmapP);
		
		/* Is the proportional width data allocated. If so, free it. */
		if(fontP->prop_width_dataP) GlobalFree(fontP->prop_width_dataP);
		
		/* Free the font structure. */
		GlobalFree(fontP);
	}
	
	return NULL;
}

static void out_text(LPDIRECTDRAWSURFACE surfP, int xc, int yc, 
	const char *text, short x_limit, fontPtr fP)
{
	register unsigned long *srcP, *destP;
	register unsigned int x, y; 
	unsigned long heightTimesPitch, charOffset;
	unsigned int prop_width;
	int srcIncr, longsPerLine;
	unsigned int bitShift;
	DDSURFACEDESC surfaceDesc;
	
	/* Lock the surface. */
	{
		HRESULT res;
		
		surfaceDesc.dwSize = sizeof surfaceDesc;
		res = IDirectDrawSurface_Lock(surfP, NULL, &surfaceDesc, 
			DDLOCK_WAIT, NULL);
		if(res != DD_OK) 
		{
			db_log3("Couldn't lock surface.");
			return;
		}
	}
	
	/* Round xc to nearest long word. */
	if(dd_mode.bitsPerPixel == 8)
	{
		xc = (xc + 2) & (~3);
		bitShift = 2;
	}
	else
	{
		xc = (xc + 1) & (~1);
		bitShift = 1;
	}
	
	/* Point to DRAM buffer co-ordinate where the top left of the
	 * first character should be written.
	 */
	destP = (unsigned long *) surfaceDesc.lpSurface + 
		((yc * surfaceDesc.lPitch) >> 2) + (xc >> bitShift);
	heightTimesPitch = (fP->height * surfaceDesc.lPitch) >> 2;
	longsPerLine = (fP->byte_width * dd_mode.bitsPerPixel) >> 2;
	charOffset = longsPerLine * fP->height;

	/* Write our text string */
	while(*text != '\0')
	{
		/* Blit a single character */
		/* Point srcP to first byte of the bitmap for the current
		 * character.
		 */
		srcP = ((unsigned long *) fP->bitmapP) + (*text) * charOffset;

		/* Get width of this character (in pixels). */
		if(fP->width == PROP_WIDTH)
			prop_width = *(fP->prop_width_dataP + (*text));
		else prop_width = fP->width;
		
		/* Check we will not exceed our original x_limit if we blit
		 * this character. If we will, we should stop writing.
		 */
		x_limit = (short) (x_limit - prop_width);
		if(x_limit < 0) break;
		
		/* Convert prop_width from pixels to longs. */
		prop_width >>= bitShift;
		
		srcIncr = longsPerLine - prop_width;
		
		y = fP->height;
		do
		{
			x = prop_width;
			do
			{
				/* Move 1 long word. */
				*destP++ = *srcP++;
			}
			while(--x != 0);
			
			/* Point to start of next horizontal line of character square
			 * in DRAM buffer.
			 */
			destP += (surfaceDesc.lPitch >> 2) - prop_width;
			srcP += srcIncr;
		}
		while(--y != 0);
		
		/* Point to start of next character position in DRAM buffer */
		destP -= heightTimesPitch - prop_width;

		/* Advance one character in text string */
		text++;
	}
	
	/* Unlock surface. */
	{
		HRESULT res;
		
		res = IDirectDrawSurface_Unlock(surfP, 
			(LPVOID) surfaceDesc.lpSurface);
		if(res != DD_OK) db_log_fired("Couldn't unlock surface.");
	}
	return;
}

static void Debounce(void)
{
	BOOL bouncing;
	
	/* Debounce all the keys we use - that is Y, N, and RETURN. */
	do
	{
		bouncing = FALSE;
		if(GetAsyncKeyState('Y') & 0x8000) bouncing = TRUE;
		if(GetAsyncKeyState('N') & 0x8000) bouncing = TRUE;
		if(GetAsyncKeyState('X') & 0x8000) bouncing = TRUE;
		if(GetAsyncKeyState(VK_RETURN) & 0x8000) bouncing = TRUE;
	}
	while(bouncing);
}

static void DbWaitForHw(void)
{
	/* Wait until the last flip is finished and the last blt done */
	BOOL finished;

	/* Stay in this loop until the hardware is free. */
	do
	{
		finished = TRUE;   

		/* Make sure all flips are done. */
		if(dd_mode.bltOrFlip == DB_FLIP)
		{
			if(IDirectDrawSurface_GetFlipStatus(
				(LPDIRECTDRAWSURFACE) dd_mode.visibleSurfaceP, 
				DDGFS_ISFLIPDONE) != DD_OK)
				finished = FALSE;
		}
	}
	while(!finished);
}

static void DbFlip(void)
{
	LPDIRECTDRAWSURFACE	fromSurfP = 
		(LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP;
	LPDIRECTDRAWSURFACE	toSurfP = 
		(LPDIRECTDRAWSURFACE) dd_mode.visibleSurfaceP;
	HRESULT res;
	
	/* Try to flip the screen. */
	res = IDirectDrawSurface_Flip(toSurfP, fromSurfP, DDFLIP_WAIT);
	
	if(res != DD_OK)
		db_log_fired("Internal debug flip failed - message lost!");
}

static void DbBlt(void)
{
	LPDIRECTDRAWSURFACE	fromSurfP = 
		(LPDIRECTDRAWSURFACE) dd_mode.drawSurfaceP;
	LPDIRECTDRAWSURFACE	toSurfP = 
		(LPDIRECTDRAWSURFACE) dd_mode.visibleSurfaceP;
	HRESULT res;
	RECT toRect;
	
	/* Initialise to Rect. */
	toRect.left = dd_mode.bltXOffset;
	toRect.top = dd_mode.bltYOffset;
	toRect.right = toRect.left + dd_mode.width;
	toRect.bottom = toRect.top + dd_mode.height;
	
	/* Try to blit from the draw to the visible surface. */
	res = IDirectDrawSurface_Blt(toSurfP, &toRect, fromSurfP, NULL,
		DDBLT_WAIT, NULL);	
		
	if(res != DD_OK) 
		db_log_fired("Internal debug blit failed - message lost.");	
}

#endif

#else
	;
#endif /* ! NDEBUG */
