#include "pch.hpp"
//===========================================================================//
// File:	 Debugger.cpp													 //
// Project:  GameOS					                                         //
// Contents: Debugger														 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 06/07/98  AG     Created													 //
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Debugger.hpp"
#include "3DRasterizer.hpp"
#include "ErrorHandler.hpp"
#include "WinProc.hpp"
#include "Profiler.hpp"
#include "Texture Manager.hpp"
#include "ControlManager.hpp"
#include "DXRasterizer.hpp"
#include "3DRasterizer.hpp"
#include "DirectX.hpp"
#include "Windows.hpp"
#include "perf.h"
#include "MemoryManager.hpp"
#include "Sound Renderer.hpp"
#include "DirectXDebugging.hpp"
#include "ijl.h"


extern bool InternalDoesFileExist( const char* FileName );
extern SoundRendererData SoundRenderer;
extern bool ShowBlockInfo;
extern int DebugHeapStart,DebugHeapEnd;
float gSoundSpatDebugZoom = 1.0f;
extern bool ShowAllocations;
extern HGOSHEAP gExaminingHeap;
extern MEMORYPOOL* gExaminingPool;
extern BYTE* _stdcall GrabScreenImage(IDirectDrawSurface7* pBBSurface, int nScrW, int nScrH);
extern DWORD DebugBlockHeapStart;
//
// Memory debugger
//
void DoExamineHeaps(DWORD keyPress);
int gExaminingHeapItem = 0;

void DoSpewInterface(char ascii);
void UpdateCursor();
bool CheckSpewButton(DWORD x, DWORD y);
extern HGOSHEAP DebugHeapOnLine[];
extern signed int DebugHeapStartLine;
extern DWORD BGMask;
DWORD DebuggerMouse;
extern DWORD gCaptureDevice;
extern DWORD gCaptureScreen;

// for spew debugger
extern char SpewList[];
extern char SpewDefault[];
extern double spewCursorTimer;
extern bool	spewInterfaceFocus;

extern DWORD CurrentBGCol;
extern DWORD CurrentBGFill;
extern DWORD CurrentFog;
extern DWORD CurrentFogCol;
DWORD PixelAreaBucket[171];
DWORD PixelAreaResults[171];
DWORD TotalAreaTriangles=0;
DWORD DebuggerMipLevel=0;
DWORD DebounceAlt;
int TextureScroll=0;
DWORD SelectedHandle=0;
DWORD TextureToUnload=0;
DWORD ButtonDown=0;
extern DWORD DBPixelColor;
bool DebuggerUsed=0;
//
// Render mode used outside debugger
//
RenderModeType OldRenderMode;

void DoRenderState( gos_RenderState RenderState, int Value );
float DebuggerTexelOffset;
//
//
extern DWORD PickStates[gos_MaxState];			// Used to save all render states
extern gos_VERTEX Pickv1,Pickv2,Pickv3;
extern DWORD PickFound;							// 1=Quad, 2=Triangle, 3=Indexed array, 4=Strip, 5=Fan
extern DWORD PickDepth;							// Number of triangles found
extern DWORD PickNumber;						// Which of the above to return


DWORD		SubMenuWidth;
DWORD		SubMenuHeight;
int			SubMenuX;
int			SubMenuY;
int			MenuEndX;
int			MenuEndY;
bool		SubMenuActive=0;
MenuItem*	SubMenuContent;


DWORD		MenuWidth;
DWORD		MenuHeight;
int			MenuX;
int			MenuY;
bool		MenuActive=0;
MenuItem*	MenuContent;
MenuItem*	BaseMenu;
MenuItem*	MenuHighlight;
MenuItem*	OldMenuHighlight;
MenuItem*	SaveLastMenu=0;
MenuItem*	SaveNextMenu=0;;
double		MenuTimer;
bool		DebounceMenu=0;




extern IJLERR (WINAPI* _ijlInit)( JPEG_CORE_PROPERTIES* jcprops );
extern IJLERR (WINAPI* _ijlFree)( JPEG_CORE_PROPERTIES* jcprops );
extern IJLERR (WINAPI* _ijlRead)( JPEG_CORE_PROPERTIES* jcprops, IJLIOTYPE iotype );
extern IJLERR (WINAPI* _ijlWrite)( JPEG_CORE_PROPERTIES* jcprops, IJLIOTYPE iotype );
extern HINSTANCE LibJpeg;








#ifdef LAB_ONLY
//
// Pie chart data
//
	gos_VERTEX PieData[103];	
#endif


//
// Examine area mode
//
int AreaL=0, AreaR=0, AreaT=0, AreaB=0;
DWORD PolygonsInArea=0;

//
//
//
DWORD PickFlash=0;
int ZoomMode=0;								// Zoom screen around cursor
DWORD gForceNoFlip=1;						// Force blt instead of flip in fullscreen
extern char DisplayInfoText[128];
extern DWORD EndDisplayInfoTime;
extern DWORD EnableDisplayInfo;
extern DWORD DoubleChr;
extern double TotalPixelsDrawn;
extern signed long gStartingChannel;
extern signed long gWhichCMDevice;
extern gosJoystick_Info gCMDeviceInfo;


bool ProcessMemorySize=1;			// When true will calculate each processes memory size in the exception handler (can take about 1 second!)
bool DebuggerActive=0;
bool InDebugger=0;				// During debugger rendering
bool NoDebuggerStats=1;			// When 0 Stats and spews are active during GameOS functions like debugger
//
// Debugger mouse position and buttons
//
int DBMouseX,DBMouseY;
int OldDBMouseX,OldDBMouseY;
DWORD DBMouseMoved;
int ExMouseX=0,ExMouseY=0;
int EAreaX,EAreaY;
DWORD DBButtons;
int DBWheelDelta;
int BaseZoom=0;
DWORD DebounceMouse=0;
//
// Alpha added to the debugger colors
//
DWORD DebuggerAlpha=0;
DWORD AlphaIndex=0;
DWORD DebuggerAlphaColors[]={0xff000000,0xd0000000,0xa0000000,0x78000000,0x48000000,0x38000000};

//
// Scroll back buffer for Dumplog (128K)
//
DWORD CurrentDebugSpewPos;
DWORD CurrentDebugTopOfScreen;
char DebuggerBuffer[128*1024];

//
// Examine scene variables
//
DWORD ExTopX=2,ExTopY=4;

//
// Used in moving debugger window
//
DWORD DraggingWindow=0;
DWORD DragLastX=0;
DWORD DragLastY=0;

char* ConditionString[]={
"bad",
"CmpNever",
"Less",
"Equal",
"LessEqual",
"Greater",
"NotEqual",
"GreaterEqual",
"Always",
};

void DrawLines( int X1, int Y1, int X2, int Y2, DWORD Color );




//
// Return the state of a menu item or trigger it
//
DWORD __stdcall CallDebuggerMenuItem( const char* Name, DWORD MenuFunction )
{
//
// Check this menu item is not already being used
//
	bool found=0;
	MenuItem* pMenu=*pCurrentMenu;

	if( 0==strnicmp( Name, "Debugger\\", strlen("Debugger\\")) )
	{
		Name+=strlen("Debugger\\");
		pMenu=pDebuggerMenu;
	} else
	if( 0==strnicmp( Name, "Rasterizer\\", strlen("Rasterizer\\")) )
	{
		Name+=strlen("Rasterizer\\");
		pMenu=pRasterizerMenu;
	} else
	if( 0==strnicmp( Name, "Libraries\\", strlen("Libraries\\")) )
	{
		Name+=strlen("Libraries\\");
		pMenu=pLibrariesMenu;
	}

	while( pMenu && !found )
	{
		if( pMenu->pSubMenu )
		{
			MenuItem* pSub=pMenu->pSubMenu;
			while( pSub && !found )
			{
				if( 0==stricmp(Name, pSub->FullName) )
				{
					pMenu=pSub;
					found=1;
				}
				if( !found )
					pSub=pSub->pNext;
			}
		}
		else
		{
			if( 0==stricmp(Name, pMenu->FullName) )
			{
				found=1;
			}
		}
		if( !found )
			pMenu=pMenu->pNext;
	}

	if( found )
	{
		switch( MenuFunction )
		{
			case gosMenu_Name:
				return 1;
			case gosMenu_CheckMark:
				return (pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_CheckMark) ) || (pMenu->CheckMark && pMenu->CheckMark());
			case gosMenu_Greyed:
				return (pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_Greyed) ) || (pMenu->Greyed && pMenu->Greyed());
			case gosMenu_Activated:
				if( pMenu->Routine )
				{
					pMenu->Routine();
				}
				else
				{
					if( pMenu->Callback )
						return pMenu->Callback( pMenu->FullName,gosMenu_Activated );
				}
			default:
				return 0;
		}
	}
//
// Menu not found - ignore.
//
	return 0;
}





//
// Deletes a menu item
//
void __stdcall DelDebuggerMenuItem( char* Name )
{
	#ifdef LAB_ONLY
	bool ResetMenu=0;
//
// If name starts with Libraries\\ the menu entry is put on the Libraries main menu
//
	if( 0==strnicmp( "Libraries\\", Name, strlen("Libraries\\")) )
	{
		ResetMenu=1;
		pCurrentMenu=&pLibrariesMenu;
	}
//
// Find in list and remember the previous entry
//
	MenuItem* pLast=0;
	MenuItem* pMenu=*pCurrentMenu;
	while( pMenu )
	{
		if( pMenu->pSubMenu )
		{
			MenuItem* pSub=pMenu->pSubMenu;
			MenuItem* pSubLast=0;
			while( pSub )
			{
//
// Delete sub menu?
//
				if( 0==stricmp(Name, pSub->FullName) )
				{
					if( pSubLast )
					{
						pSubLast->pNext=pSub->pNext;
						gos_Free( pSub );
						if(	ResetMenu )							// Kill an item on sub menu
						{
							pCurrentMenu=&pGameMenu;
						}
						return;
					}
					else
					{
						if( pSub->pNext )
						{
							pMenu->pSubMenu=pSub->pNext;
							gos_Free( pSub );
							if(	ResetMenu )						// Kill first item on sub menu
							{
								pCurrentMenu=&pGameMenu;
							}
							return;
						}
						else
						{
							gos_Free( pSub );
							if( pLast )
							{
								pLast->pNext=pMenu->pNext;		// Kill whole sub menu
								gos_Free( pMenu );
								if(	ResetMenu )
								{
									pCurrentMenu=&pGameMenu;
								}
								return;
							}
							else
							{
								if(	ResetMenu )					// Kill main menu if needed
								{
									pLibrariesMenu=pMenu->pNext;							
								}
								else
								{
									pGameMenu=pMenu->pNext;
								}
								gos_Free( pMenu );
								pCurrentMenu=&pGameMenu;
								return;
							}
						}
					}
				}
				pSubLast=pSub;
				pSub=pSub->pNext;
			}
		}
		else
		{
			if( 0==stricmp(Name, pMenu->FullName) )
				break;
		}
		pLast=pMenu;
		pMenu=pMenu->pNext;
	}
//
// Cannot find menu item?
//
	if( !pMenu )
		STOP(( "Tried to delete a menu that does not exist '%s'",Name ));
//
// Delete menu item
//
	if( pLast )
	{
		pLast->pNext=pMenu->pNext;
		gos_Free( pMenu );
	}
	else
	{
		gos_Free( pMenu );
		*pCurrentMenu=0;
	}
	if(	ResetMenu )
	{
		pCurrentMenu=&pGameMenu;
	}
	#endif
}



//
// CLIENT VISIBLE
//
// Adds a menu item to the ^Break debugger.
//
// This allows games to enable or disable game features using the debugger.
//
// If no items have been added to this menu it will not appear. Otherwise the
// menu item will appear under the last drop down menu with the same title
// as the game. ie: Assassin.
//
// Name			is the name of the menu entry (if NULL a blank line is added to the menu)
// CheckMark	is a routine that should return true if the menu should have a check next to it (active)
// Activated	is a routine that is called when the user select this menu item
//
void __stdcall AddDebuggerMenuItem( char* Name, bool(__stdcall *CheckMark)(), void(__stdcall *Activated)(), bool(__stdcall *Greyed)(), DWORD(__stdcall *Callback)( char* Name, DWORD MenuFunction) )
{
	#ifdef LAB_ONLY
//
// Not on DLL platform - there is no debugger
//
	if( Platform==Platform_DLL )
		return;
//
// Some sanity checks
//
	bool ResetMenu=0;
	if( Callback )
	{
		CheckMark=0;				// When using new callback, DO NOT allow old syntax (Activated cannot work like this for example)
		Activated=0;
		Greyed=0;
	}
	gosASSERT( Name && *Name );
//
// Save the full name
//
	char* FullName=Name;
//
// If name starts with Libraries\\ the menu entry is put on the Libraries main menu
//
	if( 0==strnicmp( "Libraries\\", Name, strlen("Libraries\\")) )
	{
		ResetMenu=1;
		pCurrentMenu=&pLibrariesMenu;
		Name=&Name[strlen("Libraries\\")];
	}
//
// Check this menu item is not already being used
//
	MenuItem* pNew;
	MenuItem* pLast=0;
	MenuItem* pMenu=*pCurrentMenu;
	while( pMenu )
	{
		if( 0==stricmp(FullName, pMenu->FullName) )
		{
			if( 0!=strcmp(Name," " ) && strlen(Name)>2 && (0!=strcmp(&Name[strlen(Name)-2], "\\ ")) )
				STOP(( "AddDebuggerMenuItem called more than once for same menu - '%s'",FullName ));
		}
		pLast=pMenu;
		pMenu=pMenu->pNext;
	}
//
// See if it's a sub menu
//
	char* pSubMenu=Name;
	while( *pSubMenu )
	{
		if( *pSubMenu=='\\' )
		{
			break;
		}
		pSubMenu++;
	}
//
// No sub menus - Create a new menu block and link it into the list
//
	if( *pSubMenu==0 )
	{
		DWORD NameLen=strlen(Name);
		pNew=(MenuItem*)gos_Malloc( sizeof(MenuItem)+NameLen+1+strlen(FullName)+1, DefaultHeap );
		pNew->pNext=0;
		pNew->pSubMenu=0;
		pNew->Greyed=Greyed;
		pNew->CheckMark=CheckMark;
		pNew->Routine=Activated;
		pNew->Callback=Callback;
		pNew->FullName=(char*)(pNew+1)+NameLen+1;
		pNew->Name=(char*)(pNew+1);
		strcpy( pNew->FullName, FullName);
		strcpy( pNew->Name, Name);
		if( pLast )
			pLast->pNext=pNew;
		else
			*pCurrentMenu=pNew;
		if(	ResetMenu )
		{
			pCurrentMenu=&pGameMenu;
		}
		return;
	}
//
// Get main menu name
//
	char SubMenu[256];
	memcpy( &SubMenu, Name, pSubMenu-Name );
	SubMenu[pSubMenu-Name]=0;
//
// See if main menu already exists
//
	MenuItem* pSearch=*pCurrentMenu;
	while( pSearch )
	{
		if( 0==stricmp(SubMenu, pSearch->Name) )
			break;
		pSearch=pSearch->pNext;
	}
//
// Now chain new menu item onto end of sub menu.
//	
	DWORD NameLen=strlen(Name)-strlen(SubMenu)-1;
	MenuItem* pNew1=(MenuItem*)gos_Malloc( sizeof(MenuItem)+NameLen+1+strlen(FullName)+1, DefaultHeap );
	pNew1->Greyed=Greyed;
	pNew1->pNext=0;
	pNew1->pSubMenu=0;
	pNew1->CheckMark=CheckMark;
	pNew1->Routine=Activated;
	pNew1->Callback=Callback;
	pNew1->FullName=(char*)(pNew1+1)+NameLen+1;
	pNew1->Name=(char*)(pNew1+1);
	strcpy( pNew1->FullName, FullName);
	strcpy( pNew1->Name, &Name[strlen(SubMenu)+1]);
//
// If doesn't, create new main menu
//
	if( !pSearch )
	{
		DWORD NameLen=strlen(SubMenu);
		pNew=(MenuItem*)gos_Malloc( sizeof(MenuItem)+NameLen+1+strlen(FullName)+1, DefaultHeap );
		pNew->pSubMenu=pNew1;
		pNew->Greyed=0;
		pNew->CheckMark=0;
		pNew->FullName=(char*)(pNew+1)+NameLen+1;
		pNew->Callback=0;
		pNew->Name=(char*)(pNew+1);
		pNew->pNext=0;
		strcpy( pNew->Name, SubMenu);
		strcpy( pNew->FullName, FullName);
		if( pLast )
			pLast->pNext=pNew;
		else
			*pCurrentMenu=pNew;
	}
	else
	{
//
// Chain it onto main or sub menu
//
		pLast=pSearch->pSubMenu;
		while( pLast->pNext )
		{
			pLast=pLast->pNext;
		}
		pLast->pNext=pNew1;
	}
//
//
//
	if(	ResetMenu )
	{
		pCurrentMenu=&pGameMenu;
	}
	#endif
}









DWORD OldStop,OldRendering,WhichImage;
//
// Stop any render modes
//
void EndRenderMode()
{
	if( RenderMode==ExamineRenderer || RenderMode==ExamineDepth )
	{
		if( g_RefBackBuffer )
		{
			wRelease( g_RefBackBuffer );
			g_RefBackBuffer=0;
			wRelease( g_CopyBackBuffer );
			g_CopyBackBuffer=0;
		}
		gStopSystem=OldStop;
		gStopRendering=OldRendering;
	}
	
	if( RenderMode==ExamineScene	||
		RenderMode==ExamineColor	||
		RenderMode==ExamineArea		||
		RenderMode==ExamineDrawing	||
		RenderMode==ExamineMipmaps	||
		RenderMode==ExamineMipmaps2	||
		RenderMode==ExamineRenderer ||
		RenderMode==ExamineDepth	)
	{
		DebuggerActive=1;
		gStopSystem=OldStop;
		RenderMode=Normal;
	}

	EnableDisplayInfo=0;
	MenuActive=0;
	SubMenuActive=0;
	MenuHighlight=0;
	BaseZoom=0;
	ZoomMode=0;
	PickNumber=1;
}











//
// Draw a menu or sub menu
//
void RenderMenu( int MenuX, int MenuY, DWORD MenuWidth, DWORD MenuHeight, MenuItem* MenuContent, int Offset )
{
//
// Fast drawing render states
//
	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	if( (DebuggerAlpha&0xff000000)==0xff000000 )
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	else
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
//
// Draw background
//
	DrawSquare( MenuX,MenuY,MenuWidth,MenuHeight, 0x0000a0 );
//
// Draw edge (make it look more 3D)
//
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	DrawLines( MenuX, MenuY, MenuX+MenuWidth, MenuY, DebuggerAlpha+0x000000d0 );
	DrawLines( MenuX, MenuY, MenuX, MenuY+MenuHeight, DebuggerAlpha+0x000000d0 );
	DrawLines( MenuX+MenuWidth, MenuY, MenuX+MenuWidth, MenuY+MenuHeight+1, DebuggerAlpha+0x00000040 );
	DrawLines( MenuX, MenuY+MenuHeight, MenuX+MenuWidth, MenuY+MenuHeight, DebuggerAlpha+0x00000040 );
//
// Draw shadow lines around menu
//
	DrawLines( MenuX+MenuWidth+1, MenuY+2, MenuX+MenuWidth+1, MenuY+MenuHeight+1, 0x80000000 );
	DrawLines( MenuX+MenuWidth+2, MenuY+2, MenuX+MenuWidth+2, MenuY+MenuHeight+1, 0x80000000 );
	DrawLines( MenuX+2, MenuY+MenuHeight+1, MenuX+MenuWidth+3, MenuY+MenuHeight+1, 0x80000000 );
	DrawLines( MenuX+2, MenuY+MenuHeight+2, MenuX+MenuWidth+3, MenuY+MenuHeight+2, 0x80000000 );
//
// Fill menu text
//
	InitTextDisplay();
	int tempX=MenuX;
	int tempY=MenuY+Offset;

	MenuItem* pMenu=MenuContent;
	MenuItem* OldMenu=0;
	bool SkipMenu=0;
	bool BlankLine=0;
	while( pMenu && *pMenu->Name!=0 )
	{
		char* Text;
		if( pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_Name))
		{
			Text=(char*)pMenu->Callback(pMenu->FullName,gosMenu_Name );
		}
		else
		{
			Text=pMenu->Name;
		}
		if( strlen(Text)==1 && Text[0]==' ' )
		{
			if( BlankLine )
				SkipMenu=1;
			BlankLine=1;
		}
		else
		{
			SkipMenu=0;
			BlankLine=0;
		}
		if( !SkipMenu )
		{
			CurrentMenu=pMenu;
			CurrentX=tempX+6;
			CurrentY=tempY+6;
			if( DBMouseMoved )
			{
				if( !DebounceMenu &&
					 DBMouseX>(CurrentX-6) && DBMouseX<(CurrentX+MenuWidth-6) && 
					 DBMouseY>(CurrentY-3) && DBMouseY<(CurrentY+8) )
				{
					if( !(Offset==2 && SubMenuActive && DBMouseX>SubMenuX && DBMouseX<(SubMenuX+SubMenuWidth) && DBMouseY>SubMenuY && DBMouseY<(SubMenuY+SubMenuHeight)) )
						MenuHighlight=pMenu;
				}
			}
			else
			{
				if( (MenuItem*)-1==SaveNextMenu )
				{
					SaveNextMenu=pMenu;
				}
				if( MenuHighlight==pMenu )
				{
					SaveLastMenu=OldMenu;
					SaveNextMenu=(MenuItem*)-1;
				}
			}
			if( pMenu==MenuHighlight )
			{
				gos_SetRenderState( gos_State_Texture, 0 );
				DrawSquare( CurrentX-4,CurrentY-1,MenuWidth-4,10, 0x800000 );	// Menu item mouse is over
				gos_SetRenderState( gos_State_Texture, FontTexture );
				MenuEndX=tempX+MenuWidth+1;
				MenuEndY=tempY+6;
			}
			tempY+=10;
			DbChrX=0;
			DbMaxX=80;
//
// In non debug mode some items are not available
//
			DWORD MColor=0xffffff;
			if( (pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_Greyed) ) || (pMenu->Greyed && pMenu->Greyed()) )
			{
				MColor=0x808080;
			}
//
// See if menu is active or not
//
			if( (pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_CheckMark) ) || (pMenu->CheckMark && pMenu->CheckMark()) )
				DrawText( MColor, "*" );
			else
				DrawText( MColor, " " );
			DrawText( MColor, Text );
			if( pMenu->pSubMenu )
			{
				CurrentX=tempX+MenuWidth-10;
				DrawText( MColor, ">" );
			}
			DrawChr( '\n' );
			OldMenu=pMenu;
		}
		pMenu=pMenu->pNext;
	}
	if( (MenuItem*)-1==SaveNextMenu )
		SaveNextMenu=(MenuItem*)0;
}


//
// If a menu is on the display, draw it
//
void DrawMenu()
{
	if( DBMouseMoved )
	{
		MenuHighlight=0;
	}
	else
	{
		SaveLastMenu=0;
		SaveNextMenu=0;
	}
	if( MenuActive )
	{
		RenderMenu( MenuX, MenuY, MenuWidth, MenuHeight, MenuContent, 2 );
	}
	if( SubMenuActive )
	{
		RenderMenu( SubMenuX, SubMenuY, SubMenuWidth, SubMenuHeight, SubMenuContent, 0 );
	}
}




//
// Find widest menu entry, size menu box to fit it
//
// If MenuText is 0 assumed to be user defined list.
//
void OpenMenu( MenuItem* MenuText, int x, int y )
{
	MenuWidth=20;
	MenuHeight=14;
	MenuItem* pMenu=MenuText;
	bool FoundSubMenu=0;
	bool BlankLine=0;
	bool SkipMenu=0;
	while( pMenu )
	{
		if( pMenu->pSubMenu )
			FoundSubMenu=1;
		char* Text;
		if( pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_Name))
		{
			Text=(char*)pMenu->Callback(pMenu->FullName,gosMenu_Name );
		}
		else
		{
			Text=pMenu->Name;
		}
		if( strlen(Text)==1 && Text[0]==' ' )
		{
			if( BlankLine )
				SkipMenu=1;
			BlankLine=1;
		}
		else
		{
			BlankLine=0;
			SkipMenu=0;
		}
		if( !SkipMenu )
		{
			if( 18+6*strlen(Text)>MenuWidth )
				MenuWidth=20+6*strlen(Text);
			MenuHeight+=10;
		}
		pMenu=pMenu->pNext;
	}
	if( FoundSubMenu )
		MenuWidth+=10;
	MenuX=DbTopX+x;
	MenuY=DbTopY+y;
	MenuContent=MenuText;
	MenuActive=1;
	SubMenuActive=0;
	SubMenuContent=0;
//
// If off screen, move it on screen
//
	if( MenuX<4 )
		MenuX=4;
	if( MenuY<4 )
		MenuY=4;
	if( MenuX+MenuWidth>Environment.screenWidth-4 )
		MenuX=Environment.screenWidth-MenuWidth-4;
	if( MenuY+MenuHeight>Environment.screenHeight-4 )
		MenuY=Environment.screenHeight-MenuHeight-4;
}

//
// Find widest menu entry, size menu box to fit it
//
// If MenuText is 0 assumed to be user defined list.
//
void OpenSubMenu( MenuItem* MenuText, int x, int y )
{
	SubMenuWidth=20;
	SubMenuHeight=12;
	MenuItem* pMenu=MenuText;
	bool BlankLine=0;
	bool SkipMenu=0;
	while( pMenu )
	{
		char* Text;
		if( pMenu->Callback && pMenu->Callback(pMenu->FullName,gosMenu_Name))
		{
			Text=(char*)pMenu->Callback(pMenu->FullName,gosMenu_Name );
		}
		else
		{
			Text=pMenu->Name;
		}
		if( strlen(Text)==1 && Text[0]==' ' )
		{
			if( BlankLine )
				SkipMenu=1;
			BlankLine=1;
		}
		else
		{
			BlankLine=0;
			SkipMenu=0;
		}
		if( !SkipMenu )
		{
			if( 18+6*strlen(Text)>SubMenuWidth )
				SubMenuWidth=20+6*strlen(Text);
			SubMenuHeight+=10;
		}
		pMenu=pMenu->pNext;
	}
	SubMenuX=x-2;
	SubMenuY=y-6;
	SubMenuContent=MenuText;
	SubMenuActive=1;
//
// If off screen, move it on screen
//
	if( SubMenuX<4 )
		SubMenuX=4;
	if( SubMenuY<4 )
		SubMenuY=4;
	if( SubMenuX+SubMenuWidth>Environment.screenWidth-4 )
		SubMenuX=Environment.screenWidth-SubMenuWidth-4;
	if( SubMenuY+SubMenuHeight>Environment.screenHeight-4 )
		SubMenuY=Environment.screenHeight-SubMenuHeight-4;
}










//
// Called once to init the debugger
//
void InitDebugger()
{
	#ifdef LAB_ONLY
	memset( DebuggerBuffer, '\n', DebuggerBufferSize );
	CurrentDebugSpewPos=40;
	CurrentDebugTopOfScreen=9;

	DoingAdvance=0;
	DebuggerActive=0;
	DbTopX=60;
	DbTopY=60;
//
// Setup pie chart vertices
//
	#define SizeOfPie 96.0f

	memset( &PieData[0], 0, sizeof(PieData) );
	PieData[0].x=SizeOfPie/2+4.0f;
	PieData[0].y=SizeOfPie/2+4.0f;
	PieData[0].rhw=1.0f;

	for( int t1=1; t1<102; t1++ )
	{
		PieData[t1].x=(float)(PieData[0].x+SizeOfPie/2*sin( (2.0f*3.1415926f*(t1-1))/100.0f ));
		PieData[t1].y=(float)(PieData[0].y-SizeOfPie/2*cos( (2.0f*3.1415926f*(t1-1))/100.0f ));
		PieData[t1].rhw=1.0f;
	}
	PieData[t1].x=(float)(PieData[0].x+SizeOfPie/2*sin( 0 ));
	PieData[t1].y=(float)(PieData[0].y-SizeOfPie/2*cos( 0 ));
	PieData[t1].rhw=1.0f;
//
// Setup pixel area ranges
//
	float Scale=10.0f;
	DWORD t0=0;
	DWORD BarNum=0;
	while( BarNum<170 )
	{
		DWORD Size=float2long((float)pow( Scale, (log(10000.0f)*t0)/(170.0f*log(Scale))));
		if( t0!=0 && PixelAreaBucket[BarNum-1]==Size )
		{
			t0++;
			continue;
		}
		t0++;
		PixelAreaBucket[BarNum++]=Size;
	};
//
// Setup framegraphs
//
	memset( Graph,0,sizeof(Graph) );
	for( int i=0; i<512; i++ )
	{
		Graph[i].rhw=1.0;
		Graph[i].argb=0xffffffff;
	}
//
// Real-time spew editor setup
//
	spewCursorTimer = 0.0;
	spewInterfaceFocus = false;
//
//
//
	memset( pTextVertices, 0, sizeof(pTextVertices) );
	for( t0=0; t0<80; t0++ )
	{
		pTextVertices[t0].rhw = 1.0;
	}
	#endif
}


//
// Clean up menus and any sub menus
//
void CleanMenu( MenuItem* Menu )
{
	MenuItem* pLast;
	MenuItem* pMenu=Menu;
	while( pMenu )
	{
		pLast=pMenu;
		if( pMenu->pSubMenu )		// Points to a sub menu?
		{
			MenuItem* psLast;
			MenuItem* psMenu=pMenu->pSubMenu;
			while( psMenu )
			{
				psLast=psMenu;
				psMenu=psMenu->pNext;
				gos_Free(psLast);
			}
		}
		pMenu=pMenu->pNext;
		gos_Free(pLast);
	}
}


//
// Free all debugger memory allocated during memory manager
//
void CleanUpDebugger()
{
	#ifdef LAB_ONLY
	SPEW(( GROUP_GAMEOS, "CleanUpDebugger()" ));
	CleanMenu( pLibrariesMenu );
	pLibrariesMenu=0;

	CleanMenu( pGameMenu );
	pGameMenu=0;

	MenuActive=0;
	SubMenuActive=0;
	if( g_RefBackBuffer )
	{
		wRelease( g_RefBackBuffer );
		g_RefBackBuffer=0;
		wRelease( g_CopyBackBuffer );
		g_CopyBackBuffer=0;
	}
	while( pTextureHeap )
	{
		TextureHeap* pThis=pTextureHeap;
		pTextureHeap=pTextureHeap->pNext;			// Remove any heaps displayed last frame
		gos_Free( pThis );
	}
	SPEW(( GROUP_GAMEOS, "CleanUpDebugger() - Finished" ));
	#endif
	gStopRendering=0;
}


//
// Called to free all debugger memory (after MM closed down)
//
void DestroyDebugger()
{
	#ifdef LAB_ONLY
	SPEW(( GROUP_GAMEOS, "DestroyDebugger()" ));
	CleanMenu( pRasterizerMenu );
	pRasterizerMenu=0;
	CleanMenu( pDebuggerMenu );
	pDebuggerMenu=0;
	SPEW(( GROUP_GAMEOS, "DestroyDebugger() - Finished" ));
	#endif
}






//
// Scroll the spew window up a line
//
void SpewNextLine()
{
	char Chr;
	do
	{
		Chr=DebuggerBuffer[CurrentDebugTopOfScreen];

		CurrentDebugTopOfScreen++;
		if( CurrentDebugTopOfScreen==DebuggerBufferSize )
			CurrentDebugTopOfScreen=0;

	} while( Chr!='\n' );
}

//
// Send a message to the spew window (scroll display up a line)
//
void SpewToDebugger( char* Message )
{
	while( *Message )
	{
		if( *Message=='\n' )
		{
			SpewNextLine();
		}

		DebuggerBuffer[CurrentDebugSpewPos++]=*Message;
		if( CurrentDebugSpewPos==DebuggerBufferSize )
			CurrentDebugSpewPos=0;
		Message++;
	}
}







#define MIN3(x,y,z) (x<y ? (x<z ? x : z) : (y<z ? y : z))
#define MAX3(x,y,z) (x>y ? (x>z ? x : z) : (y>z ? y : z))

//
// Used in calculating texture repeating in examine mode
//
float DoUV( float x )
{
	if( x >=0 )
	{
		if( x-(int)x==0.0 && x!=0.0 )
			return 1.0;
		return x-(int)x;
	}
	else
	{
		if( x-(int)x==-1.0 && x!=-1.0 )
			return 0.0;
		return 1.0f+x-(int)x;
	}
}



//
// Return the mipmap level a triangle used
//
int GetMipmapUsed( DWORD Handle, gos_VERTEX* v1, gos_VERTEX* v2, gos_VERTEX* v3 )
{				
	if( Handle==-1 )
		return 0;
//
// Find mipmap level
//
	int MipMapUsed=0;
	DWORD  ScreenArea= (DWORD)(0.5f * fabs( v2->x*v3->y + v1->x*v2->y + v3->x*v1->y - v2->x*v1->y - v1->x*v3->y - v3->x*v2->y ));
	DWORD TextureArea=(DWORD)( 0.5f * fabs( v2->u*v3->v + v1->u*v2->v + v3->u*v1->v - v2->u*v1->v - v1->u*v3->v - v3->u*v2->v ) * TEXHANDLE_TO_PTR(Handle)->Area() );

	if( 0==TextureArea || 0==ScreenArea )
		return 0;
	
	if( MipMapUsed>-10 && (ScreenArea > TextureArea) )
	{
		do
		{
			MipMapUsed--;
			ScreenArea/=4;
		} while(ScreenArea>TextureArea);
	}
	else
	{
		while( MipMapUsed<9 && ((ScreenArea*4) <= TextureArea) )
		{
			MipMapUsed++;
			ScreenArea*=4;
		}
	}
	return MipMapUsed;
}





double mppsLastTime=0.0;
//
// Show triangle size information graphs
//
void ShowTriangleSizeInfo()
{
	char Buffer[256];
	DWORD StartX=Environment.screenWidth/2-(512+8)/2;
	DWORD StartY=22;

	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	DrawSquare( StartX, StartY-10, (512+8), 122, 0xff606060 );

	InitTextDisplay();
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
//
// Now display scale, show 10, 100, 1000, 10,000 pixels
//
	DbChrX=0;
	DbMaxX=80;
	CurrentY=17;
	DWORD Search=10;
	for( int t0=0; t0<155; t0++ )
	{
		CurrentX=Environment.screenWidth/2-(512+8)/2+4+1+3*t0;
		if( PixelAreaBucket[t0]>Search )
		{
			_itoa( Search, Buffer, 10);
			DrawText( 0xffffff, Buffer );
			Search*=10;
		}
	}
//
// Find largest bucket entry
//
	DWORD LargestBucket=0;
	for( t0=0; t0<170; t0++ )
	{
		if( LargestBucket<PixelAreaResults[t0] )
			LargestBucket=PixelAreaResults[t0];
	}

	float GraphScale=1.0;
	if( LargestBucket )
		GraphScale=80.0f/LargestBucket;
//
//
//
	double SmallTriangles = 0.0;

	if(TotalAreaTriangles>0)
		SmallTriangles=(PixelAreaResults[0]+PixelAreaResults[1])*100.0/TotalAreaTriangles;

	DWORD iTotalPixelsDrawn=(DWORD)TotalPixelsDrawn;
	
	sprintf( Buffer, "% 9d=Total pixels rendered  Graph Peak=%-8d Small Triangles=%d (%5.2f%%)",iTotalPixelsDrawn,LargestBucket,PixelAreaResults[0]+PixelAreaResults[1],SmallTriangles );

	CurrentY=118;
	CurrentX=Environment.screenWidth/2-(512+8)/2+4;
	DbChrX=0;
	DbMaxX=85;
	DrawText( 0xffffff, Buffer );
//
// Draw lines around graph
//
	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );

	CurrentX=Environment.screenWidth/2-(512+8)/2+4;

	DrawLines( CurrentX, 29, CurrentX+512, 29, 0xffffff );
	DrawLines( CurrentX, 111, CurrentX+512, 111, 0xffffff );
	DrawLines( CurrentX, 30, CurrentX, 111, 0xffffff );
	DrawLines( CurrentX+511, 30, CurrentX+511, 111, 0xffffff );
//
// Now draw graph
//
	for( t0=0; t0<170; t0++ )
	{
		DWORD heightofBar=(DWORD)(PixelAreaResults[t0]*GraphScale);
		
		if( heightofBar )
		{
			DWORD BarColor=0xff00b000;

			if( PixelAreaBucket[t0]<3 )
				BarColor=0xffff0000;
			else if( PixelAreaBucket[t0]<10 )
				BarColor=0xffb00000;
			else if( PixelAreaBucket[t0]<100 )
				BarColor=0xff700000;

			DrawSquare( CurrentX+1, 110-heightofBar, 3, heightofBar, BarColor );
		}

		PixelAreaResults[t0]=0;
		CurrentX+=3;
	}

	TotalAreaTriangles=0;
	TotalPixelsDrawn=0.0;
}




//
// Examine Mipmaps
//
void DoExamineMipmaps()
{
//
// End the 3D Scene
//
	End3DScene();

	if( (gos_GetKey()&255)==' ' )
	{
		if( WhichImage )
		{
			strcpy( DisplayInfoText, "Mipmap Levels (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=0;
		}
		else
		{
			strcpy( DisplayInfoText, "Original Image (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=1;
		}
	}

//
// Now allow rendering again
//
	gos_SetupViewport( 0,0,0,0, 0.0, 0.0, 1.0, 1.0 );

	if( EnableDisplayInfo && 0==WhichImage )
	{
		DWORD StartX=Environment.screenWidth/2-128;
		DWORD StartY=Environment.screenHeight-80;

		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		DrawSquare( StartX-6, StartY, 270, 44, 0xff606060 );

		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );

		char String[4];
		
		for( DWORD t1=0; t1<15; t1++ )
		{
//
// Display mipmap color number
//
			CurrentX=StartX+t1*18;
			CurrentY=StartY+8;
			if( t1<7 )
			{
				String[0]='-';
				String[1]=(char)(7-t1+'0');
				String[2]=0;
			}
			else
			{
				CurrentX-=10;
				String[0]='+';
				String[1]=(char)(t1+'0'-7);
				String[2]=0;
			}
			if( t1!=7 )
			{
				DrawText( 0xffffff, String );
			}
		}
//
// Display squares of color
//
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		for( DWORD t0=0; t0<15; t0++ )
		{
			if( t0!=7 )
			{
				DrawSquare( StartX, StartY+20, 14, 14, GetMipmapColor( t0-7 ) );
				StartX+=18;
			}
			else
				StartX+=8;
		}
	}
}

//
// Examine Mipmaps
//
void DoExamineMipmaps2()
{
//
// End the 3D Scene
//
	End3DScene();

	if( (gos_GetKey()&255)==' ' )
	{
		if( WhichImage )
		{
			strcpy( DisplayInfoText, "Mipmap Levels (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=0;
		}
		else
		{
			strcpy( DisplayInfoText, "Original Image (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=1;
		}
	}

//
// Now allow rendering again
//
	gos_SetupViewport( 0,0,0,0, 0.0, 0.0, 1.0, 1.0 );

	if( EnableDisplayInfo && 0==WhichImage )
	{
		DWORD StartX=Environment.screenWidth/2-128;
		DWORD StartY=Environment.screenHeight-80;

		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		DrawSquare( StartX-6, StartY, 270, 44, 0xff606060 );

		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );

		char String[4];
		
		for( DWORD t1=0; t1<15; t1++ )
		{
//
// Display mipmap color number
//
			CurrentX=StartX+t1*18;
			CurrentY=StartY+8;
			if( t1<7 )
			{
				String[0]='-';
				String[1]=(char)(7-t1+'0');
				String[2]=0;
			}
			else
			{
				CurrentX-=10;
				String[0]='+';
				String[1]=(char)(t1+'0'-7);
				String[2]=0;
			}
			if( t1!=7 )
			{
				DrawText( 0xffffff, String );
			}
		}
//
// Display squares of color
//
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		for( DWORD t0=0; t0<15; t0++ )
		{
			if( t0!=7 )
			{
				DrawSquare( StartX, StartY+20, 14, 14, GetMipmapColor( t0-7 ) );
				StartX+=18;
			}
			else
				StartX+=8;
		}
	}
}

//
// Examine renderer - swap between two bitmaps
//
void DoExamineRenderer()
{
//
// End the 3D Scene
//
	End3DScene();

	if( (gos_GetKey()&255)==' ' )
	{
		if( WhichImage )
		{
			strcpy( DisplayInfoText, "Reference Rasterizer (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=0;
		}
		else
		{
			char HardwareBuffer[128];
			sprintf( HardwareBuffer, "%s",DeviceArray[Environment.fullScreen ? Environment.FullScreenDevice : 0].DDid.szDescription );
            sprintf( DisplayInfoText, "%s (hit space)",(HardwareRenderer ? HardwareBuffer : (Environment.Renderer==2 ? "Software Reference Rasterizer" : (Environment.Renderer==1 ? "Software RGB/MMX" : "Blade Rasterizer" ))) );
			EnableDisplayInfo=1;
			WhichImage=1;
		}
	}

	if( WhichImage )
		CopyBackBuffer( BackBufferSurface, g_CopyBackBuffer );
	else
		CopyBackBuffer( BackBufferSurface, g_RefBackBuffer );
//
// Now allow rendering again
//
	gos_SetupViewport( 0,0,0,0, 0.0, 0.0, 1.0, 1.0 );
}



//
// Draw a square region using quads in a given color
//
void DrawSquare( int TopX, int TopY, int Width, int Height, DWORD Color )
{
	gos_VERTEX pVertices[4];

	memset( pVertices, 0, sizeof(pVertices) );
	pVertices[0].rhw	= 1.0;
	pVertices[1].rhw	= 1.0;
	pVertices[2].rhw	= 1.0;
	pVertices[3].rhw	= 1.0;

	pVertices[0].x		= (float)TopX;
	pVertices[0].y		= (float)TopY;
	pVertices[0].argb	= DebuggerAlpha+Color;

	pVertices[1].x		= (float)TopX;
	pVertices[1].y		= (float)(TopY+Height);
	pVertices[1].argb	= DebuggerAlpha+Color;

	pVertices[2].x		= (float)(TopX+Width);
	pVertices[2].y		= (float)(TopY+Height);
	pVertices[2].argb	= DebuggerAlpha+Color;

	pVertices[3].x		= (float)(TopX+Width);
	pVertices[3].y		= (float)(TopY);
	pVertices[3].argb	= DebuggerAlpha+Color;

	gos_ClipDrawQuad( pVertices );
}
//
// Draw a line in a given color
//
void DrawLines( int X1, int Y1, int X2, int Y2, DWORD Color )
{
	gos_VERTEX pVertices[2];

	memset( pVertices, 0, sizeof(pVertices) );
	pVertices[0].rhw	= 1.0;
	pVertices[1].rhw	= 1.0;

	pVertices[0].x		= (float)X1;
	pVertices[0].y		= (float)Y1;
	pVertices[0].argb	= Color;

	pVertices[1].x		= (float)X2;
	pVertices[1].y		= (float)Y2;
	pVertices[1].argb	= Color;

	gos_DrawLines( pVertices, 2 );
}

//
// Examine depth complexity - swap between two bitmaps
//
void DoExamineDepth()
{
//
// End the 3D Scene
//
	End3DScene();

	if( (gos_GetKey()&255)==' ' )
	{
		if( WhichImage )
		{
			strcpy( DisplayInfoText, "Depth Complexity (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=0;
		}
		else
		{
			strcpy( DisplayInfoText, "Original Image (hit space)" );
			EnableDisplayInfo=1;
			WhichImage=1;
		}
	}

	if( WhichImage )
		CopyBackBuffer( BackBufferSurface, g_CopyBackBuffer );
	else
		CopyBackBuffer( BackBufferSurface, g_RefBackBuffer );
//
// Now allow rendering again
//
	gos_SetupViewport( 0,0,0,0, 0.0, 0.0, 1.0, 1.0 );
//
// Now show information on depth complexity
//
	if( EnableDisplayInfo && 0==WhichImage )
	{
		DWORD StartX=Environment.screenWidth/2-128;
		DWORD StartY=Environment.screenHeight-80;

		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		DrawSquare( StartX-4, StartY, 264, 60, 0xff606060 );

		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );

		char String[4];
		
		for( DWORD t1=0; t1<8; t1++ )
		{
//
// Display color depth number
//
			CurrentX=StartX+14+t1*32;
			CurrentY=StartY+8;
			String[0]=(char)(t1+'0');
			String[1]=0;
			DrawText( 0xffffff, String );

			CurrentX=StartX+7+t1*32;
			CurrentY=StartY+48;
//
// Display percentage of scene in depth
//
			DWORD Number=(DepthResults[t1]*100)/DepthPixels;
			
			if( Number>99 )
			{
				CurrentX-=3;
				DrawText( 0xffffff, "100%" );
			}
			else if( Number==0 && DepthResults[t1] )
			{
				CurrentX+=3;
				DrawText( 0xffffff, "1%" );
			}
			else if( Number>0 && Number <10 )
			{
				CurrentX+=3;
				String[0]=(char)('0'+Number%10);
				String[1]='%';
				String[2]=0;
				DrawText( 0xffffff, String );
			}
			else if( Number>0 )
			{
				String[0]=(char)('0'+Number/10);
				String[1]=(char)('0'+Number%10);
				String[2]='%';
				String[3]=0;
				DrawText( 0xffffff, String );
			}
		}

		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_Texture, 0 );
		for( DWORD t0=0; t0<8; t0++ )
		{
			DrawSquare( StartX+4, StartY+20, 24, 24, gosColorTable[t0] );
			StartX+=32;
		}
	}
}



//
// Show information about triangles within an area of the screen
//
void DoExamineArea()
{
	if( DebuggerActive )
	{
//
// Has the user clicked on an area?
//
		if( DBButtons&1 )
		{
			if( ButtonDown )
			{
				AreaL=DBMouseX>EAreaX ? EAreaX : DBMouseX;
				AreaR=DBMouseX>EAreaX ? DBMouseX : EAreaX;
				AreaT=DBMouseY>EAreaY ? EAreaY : DBMouseY;
				AreaB=DBMouseY>EAreaY ? DBMouseY : EAreaY;
			}
			else
			{
				ButtonDown=1;
				AreaL=AreaR=EAreaX=DBMouseX;
				AreaT=AreaB=EAreaY=DBMouseY;
			}
		}
		else
		{
			ButtonDown=0;
		}
//
// Draw area that is being examined
//
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		DrawSquare( AreaL, AreaT, AreaR-AreaL, AreaB-AreaT, 0xc0404080 );
//
// Show triangle information about things within the area
//
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		DrawSquare( 4, 4, 132, 32, 0xff606060 );

		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		CurrentX=16;
		CurrentY=16;

		char Buffer[64];
		sprintf( Buffer, "Triangles: %d",PolygonsInArea );
		PolygonsInArea=0;
		DrawText( 0xffffff, Buffer );
	}
}


//
// Show information about triangles within an area of the screen
//
void DoExamineDrawing()
{
	if( DebuggerActive )
	{
//
// First frame? - Record max poly number
//
		if( DrawingMax==999999 )
		{
			DrawingMax=DrawingHighest=DrawingPolys;
			CulledMax=gCulledTriangles;
		}
//
// Check for keyboard keys
//
		DWORD KeyPress=(gos_GetKey()>>8)&~0xff00;

		if( (KeyPress==KEY_LEFT || KeyPress==KEY_DOWN) && DrawingMax!=0 )
			DrawingMax--;
		if( KeyPress==KEY_NEXT )
		{
			if( DrawingMax>50 )
				DrawingMax-=50;
			else
				DrawingMax=0;
		}
		if( (KeyPress==KEY_RIGHT || KeyPress==KEY_UP) && DrawingMax<DrawingHighest )
			DrawingMax++;
		if( KeyPress==KEY_PRIOR )
		{
			if( DrawingMax<DrawingHighest-50 )
				DrawingMax+=50;
			else
				DrawingMax=DrawingHighest;
		}
		if( KeyPress==KEY_HOME )
			DrawingMax=0;
		if( KeyPress==KEY_END )
			DrawingMax=DrawingHighest;
//
// Show triangle information about things within the area
//
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
		DrawSquare( 4, 4, 214, 38, 0xff606060 );
		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		CurrentX=35;
		CurrentY=6;
		DbChrX=0;
		DbChrY=0;
		DbMaxX=64;
		DbMaxY=24;
		char Buffer[64];
		sprintf( Buffer, "Triangles drawn %d/%d",DrawingMax,DrawingHighest );
		PolygonsInArea=0;
		DrawText( 0xffffff, Buffer );
		sprintf( Buffer, "(Triangles Culled %d)",CulledMax );
		DbChrX=0;
		CurrentX=42;
		CurrentY=16;
		DrawText( 0xffffff, Buffer );
		DbChrX=0;
		CurrentX=8;
		CurrentY=30;
		DrawText( 0xffffff, "Use cursor keys/PgUp/PgDn/Home/End" );
//
// Reset polygon counter
//
		DrawingPolys=0;
		gCulledTriangles=0;
	}
}



//
// Given a pixel and mask, returns the color byte
//
DWORD GetColor( DWORD Pixel, DWORD Mask )
{
	DWORD Shift=0;
	DWORD MaskBit=1;
	DWORD LeftShift=0;
	while( 0==(Mask&MaskBit) )
	{
		Shift++;
		MaskBit<<=1;
	}
	for( int t0=0; t0<32; t0++ )
		if( Mask&(1<<t0) )
			LeftShift++;
	LeftShift=8-LeftShift;
	return ((Pixel&Mask)>>Shift)<<LeftShift;
}


char* ShowColor( DWORD Pixel )
{
	static char Buffer[64];
	sprintf( Buffer, "%3d,%3d,%3d  ",((Pixel>>16)&255),((Pixel>>8)&255),Pixel&255 );

	char* pBit=&Buffer[13];
	for( int t0=23; t0>=0; t0-- )
	{
		if( t0==15 || t0==7 )
			*pBit++=' ';
		*pBit++=((Pixel&(1<<t0)) ? '1' : '0');
	}
	*pBit++=0;
	return Buffer;
}




//
//
// Do all the examine color processing
//
//
void DoExamineColor()
{
//
// Get any keys pressed
//
	DWORD KeyPress=gos_GetKey();
	if( (((KeyPress>>8)&~0xff00)==KEY_UP && gos_GetKeyStatus(KEY_LSHIFT)) && AlphaIndex>0 )
		AlphaIndex-=1;
	if( (((KeyPress>>8)&~0xff00)==KEY_DOWN && gos_GetKeyStatus(KEY_LSHIFT)) && AlphaIndex<5 )
		AlphaIndex+=1;
	KeyPress=KeyPress&255;
//
// Draw examine dialog box
//
	if( DebuggerActive )
	{
//
// Decode background color and current pixel color
//
		DWORD Pixel=(GetColor( DBPixelColor, BBddsd.ddpfPixelFormat.dwRBitMask )<<16)+
					(GetColor( DBPixelColor, BBddsd.ddpfPixelFormat.dwGBitMask )<<8)+
					 GetColor( DBPixelColor, BBddsd.ddpfPixelFormat.dwBBitMask );
//
// Check to see if user dimmed window (all modes of debugger)
//
		DebuggerAlpha=DebuggerAlphaColors[AlphaIndex];
//
// Does window have to move out of the cursors way?
//
		if( ExTopX==4 && (DBMouseX<=308 && DBMouseY<90) )
			ExTopX=Environment.screenWidth-306;
		if( ExTopX!=4 && ((ExTopX!=Environment.screenWidth-306) || (DBMouseX>=(Environment.screenWidth-310) && DBMouseY<90)) )
			ExTopX=4;
//
// Draw background
//
		gos_SetRenderState( gos_State_Texture, 0 );
		if( (DebuggerAlpha&0xff000000)==0xff000000 )
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		else
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
		DrawSquare( ExTopX,ExTopY,302,80, 0x000060 );
//
// Draw a color square
//	
		DrawSquare( ExTopX+30,ExTopY+42,32,32, Pixel );
//
// Display color information
//
		char Buffer[256];
		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
		StartX=CurrentX=ExTopX+6;
		StartY=CurrentY=ExTopY+5;
		DbChrX=0;
		DbChrY=0;
		DbMaxX=50;
		DbMaxY=24;

		sprintf( Buffer, "Color  = %s\n", ShowColor(Pixel) );
		DrawText( 0xc0c0c0, Buffer );

		if( CurrentBGFill )
		{
			sprintf( Buffer, "Fill   = %s\n", ShowColor(CurrentBGCol) );
			DrawText( 0xc0c0c0, Buffer );
		}
		else
		{
			sprintf( Buffer, "Fill   = %s\n", ShowColor(0) );
			DrawText( 0x808080, Buffer );
		}
		CurrentBGFill=0;

		if( CurrentFog )
		{
			sprintf( Buffer, "Fog    = %s\n", ShowColor(CurrentFogCol) );
			DrawText( 0xc0c0c0, Buffer );
		}
		else
		{
			sprintf( Buffer, "Fog    = %s\n", ShowColor(0) );
			DrawText( 0x808080, Buffer );
		}
		CurrentFog=0;

		sprintf( Buffer, "Target = %s\n", ShowColor(BGMask) );
		DrawText( 0xc0c0c0, Buffer );
//
//
//
		StartX=CurrentX=ExTopX+88;
		StartY=CurrentY=ExTopY+44;

		if( BBddsd.ddpfPixelFormat.dwRGBBitCount==16 )
		{
			sprintf( Buffer, "Actual pixel value   = 0x%04X\n", DBPixelColor&0xffff );
		}
		else
		{
			sprintf( Buffer, "Actual pixel value   = 0x%06X\n", DBPixelColor&0xffffff );
		}
		DrawText( 0xc0c0c0, Buffer );

		sprintf( Buffer, "Render target format = %s\n", GetGosPixelFormat( &BBddsd.ddpfPixelFormat ) );
		DrawText( 0xc0c0c0, Buffer );

		sprintf( Buffer, "Mouse position       = %d,%d\n", DBMouseX, DBMouseY );
		DrawText( 0xc0c0c0, Buffer );
//
// If left button held enter zoom mode (center mouse wheel zooms in and out)
//
		if( DBWheelDelta )
		{
			BaseZoom-=DBWheelDelta;
			if( BaseZoom>12 )
				BaseZoom=12;
			if( BaseZoom<0 )
				BaseZoom=0;
		}
		if( DBButtons&1 )
		{
			if( !DebounceMouse )
			{	BaseZoom=0;
				ZoomMode+=1;
			}
			if( ZoomMode>12 )
				ZoomMode=12;
		}
		else
		{
			DebounceMouse=0;
			if( ZoomMode>BaseZoom )
				ZoomMode-=1;
			if( ZoomMode<BaseZoom )
				ZoomMode+=1;
		}
		ExMouseX=DBMouseX;
		ExMouseY=DBMouseY;
		PickFound=0;
		PickDepth=0;
	}
}






void DisplayUV( float U, float V )
{
	char Buffer[128];

	if( U >= 10.0 )
		sprintf( Buffer, "   u=% 8.4f\n",(double)U );
	else
		if( U>0.0 )
			sprintf( Buffer, "   u=% 8.5f\n",(double)U );
		else
			if( U<=-10.0 )
				sprintf( Buffer, "   u=% 8.3f\n",(double)U );
			else
				sprintf( Buffer, "   u=% 8.4f\n",(double)U );
	DrawText( 0xc0c0c0, Buffer );
	if( V >= 10.0 )
		sprintf( Buffer, "   v=% 8.4f\n",(double)V );
	else
		if( V>0.0 )
			sprintf( Buffer, "   v=% 8.5f\n",(double)V );
		else
			if( V<=-10.0 )
				sprintf( Buffer, "   v=% 8.3f\n",(double)V );
			else
				sprintf( Buffer, "   v=% 8.4f\n",(double)V );
	DrawText( 0xc0c0c0, Buffer );
}







//
//
// Do all the examine scene processing (Show Triangle information)
//
//
void DoExamineScene()
{
//
// Get any keys pressed
//
	DWORD KeyPress=gos_GetKey();
	if( (((KeyPress>>8)&~0xff00)==KEY_UP && gos_GetKeyStatus(KEY_LSHIFT)) && AlphaIndex>0 )
		AlphaIndex-=1;
	if( (((KeyPress>>8)&~0xff00)==KEY_DOWN && gos_GetKeyStatus(KEY_LSHIFT)) && AlphaIndex<5 )
		AlphaIndex+=1;
	KeyPress=KeyPress&255;
//
// Show triangle found behind debugger
//
	if( PickFound && DebuggerActive)
	{
//
// Flash current triangle unless right button pressed
//
		if( (DBButtons&2)==0 )
		{
			PickZoom[0].argb=PickFlash==0x80ffffff ? 0x40ffffff : PickFlash;		// Do not use bright flash on main screen
			PickZoom[1].argb=PickFlash==0x80ffffff ? 0x40ffffff : PickFlash;
			PickZoom[2].argb=PickFlash==0x80ffffff ? 0x40ffffff : PickFlash;
			PickZoom[0].z=0.0;
			PickZoom[1].z=0.0;
			PickZoom[2].z=0.0;
			PickZoom[0].rhw=1.0;
			PickZoom[1].rhw=1.0;
			PickZoom[2].rhw=1.0;

			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
			gos_SetRenderState( gos_State_Texture, 0 );
			gos_SetRenderState( gos_State_Clipping, 1 );							// Don't use guard band in case too large
			if( Environment.Renderer==3 && (PickStates[gos_State_MonoEnable]&256) )
				gos_SetRenderState( gos_State_MonoEnable, 256 );					// Use pixel double mode in blade

			DWORD OldZoom=ZoomMode;
			ZoomMode=0;
			gos_DrawTriangles( PickZoom, 3 );
			ZoomMode=OldZoom;

			gos_SetRenderState( gos_State_MonoEnable, 0 );
			gos_SetRenderState( gos_State_Clipping, 0 );
		}
//
// Change flashing color
//
		if( PickFlash==0x80ffffff )
			PickFlash=0x40ff0000;
		else if( PickFlash==0x40ff0000 )
			PickFlash=0x4000ff00;
		else if( PickFlash==0x4000ff00 )
			PickFlash=0x400000ff;
		else if( PickFlash==0x400000ff )
			PickFlash=0;
		else
			PickFlash=0x80ffffff;
	}
//
// Draw examine dialog box
//
	if( DebuggerActive )
	{
//
// Check to see if user dimmed window (all modes of debugger)
//
		DebuggerAlpha=DebuggerAlphaColors[AlphaIndex];
//
// Does window have to move out of the cursors way?
//
		if( ExTopX==2 && DBMouseX<=285 )
			ExTopX=Environment.screenWidth-288;
		if( ExTopX!=2 && DBMouseX>=(Environment.screenWidth-290) )
			ExTopX=2;
//
// Draw background
//
		gos_SetRenderState( gos_State_Texture, 0 );
		if( (DebuggerAlpha&0xff000000)==0xff000000 )
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		else
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
		DrawSquare( ExTopX,ExTopY,284,472, 0x000060 );
//
//
//
		float RepeatX=1.0;
		float RepeatY=1.0;
		int MipMapUsed=0;

		if( PickFound )
		{
//
// Display information about texture under cursor
//
			DWORD Handle=PickStates[gos_State_Texture];

			if( PickStates[gos_State_Texture]==0 )
			{
				InitTextDisplay();
				gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
				CurrentX=ExTopX+14;
				CurrentY=ExTopY+16;
				DrawText( 0xc0c0c0, "No Texture" );
			}
			else
			{
				DWORD Scale=256;
				if( Environment.screenWidth==512 )
				{
					Scale=128;
				}

				if( (DBButtons&2)==0 )
				{
					gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
					gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
				}
				else
				{
					gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
					gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendDecal );
					gos_SetRenderState( gos_State_AlphaTest, 0 );
				}
				gos_SetRenderState( gos_State_MipMapBias, DebuggerMipLevel );
				gos_SetRenderState( gos_State_Texture, PickStates[gos_State_Texture] );
				gos_SetRenderState( gos_State_TextureAddress, PickStates[gos_State_TextureAddress] );

				if( PickStates[gos_State_TextureAddress]==gos_TextureClamp )
				{
//
// Clamped textures
//
					float LowestU=(float)floor(MIN3(Pickv1.u,Pickv2.u,Pickv3.u));
					float LowestV=(float)floor(MIN3(Pickv1.v,Pickv2.v,Pickv3.v));
					
					if( (DBButtons&2)==0 )							// Show non repeated when button 2 down
					{
//
// Work out how often a texture repeats (complex due to special cases!)
//
						RepeatX=1.0f+(float)floor(MAX3(Pickv1.u,Pickv2.u,Pickv3.u))-LowestU;
						if( (int)MAX3(Pickv1.u,Pickv2.u,Pickv3.u)==MAX3(Pickv1.u,Pickv2.u,Pickv3.u) && RepeatX>1.0 )
							RepeatX-=1.0;

						RepeatY=1.0f+(float)floor(MAX3(Pickv1.v,Pickv2.v,Pickv3.v))-LowestV;
						if( (int)MAX3(Pickv1.v,Pickv2.v,Pickv3.v)==MAX3(Pickv1.v,Pickv2.v,Pickv3.v) && RepeatY>1.0 )
							RepeatY-=1.0;
					}

					gos_VERTEX tv[4];
					tv[0].x=(float)(ExTopX+14);
					tv[1].x=(float)(ExTopX+14);
					tv[2].x=(float)(ExTopX+14+Scale);
					tv[3].x=(float)(ExTopX+14+Scale);

					tv[0].y=(float)(ExTopY+14);
					tv[1].y=(float)(ExTopY+14+Scale);
					tv[2].y=(float)(ExTopY+14+Scale);
					tv[3].y=(float)(ExTopY+14);

					tv[0].argb=DebuggerAlpha+0xffffff;
					tv[1].argb=DebuggerAlpha+0xffffff;
					tv[2].argb=DebuggerAlpha+0xffffff;				// Draw texture used for triangle
					tv[3].argb=DebuggerAlpha+0xffffff;

					tv[0].u=0.0;
					tv[1].u=0.0;
					tv[2].u=RepeatX;
					tv[3].u=RepeatX;

					tv[0].v=0.0;
					tv[1].v=RepeatY;
					tv[2].v=RepeatY;
					tv[3].v=0.0;

					tv[0].z=0.0;
					tv[1].z=0.0;
					tv[2].z=0.0;
					tv[3].z=0.0;

					tv[0].rhw=1.0;
					tv[1].rhw=1.0;
					tv[2].rhw=1.0;
					tv[3].rhw=1.0;

					gos_ClipDrawQuad( tv );							// Draw actual size (unless 512*512 - then half it)
//
// Draw area of texture used in triangle
//
					tv[0]=Pickv1;
					tv[1]=Pickv2;
					tv[2]=Pickv3;
//
// If right button down, do not repeat texture
//
					if( (DBButtons&2)==0 )
					{
						tv[0].x=ExTopX+14+((tv[0].u-LowestU)/RepeatX)*Scale;
						tv[0].y=ExTopY+14+((tv[0].v-LowestV)/RepeatY)*Scale;

						tv[1].x=ExTopX+14+((tv[1].u-LowestU)/RepeatX)*Scale;
						tv[1].y=ExTopY+14+((tv[1].v-LowestV)/RepeatY)*Scale;

						tv[2].x=ExTopX+14+((tv[2].u-LowestU)/RepeatX)*Scale;
						tv[2].y=ExTopY+14+((tv[2].v-LowestV)/RepeatY)*Scale;
					}
					else
					{
						tv[0].x=ExTopX+14+DoUV(tv[0].u)*Scale;
						tv[0].y=ExTopY+14+DoUV(tv[0].v)*Scale;

						tv[1].x=ExTopX+14+DoUV(tv[1].u)*Scale;
						tv[1].y=ExTopY+14+DoUV(tv[1].v)*Scale;

						tv[2].x=ExTopX+14+DoUV(tv[2].u)*Scale;
						tv[2].y=ExTopY+14+DoUV(tv[2].v)*Scale;
					}

					tv[0].argb=DebuggerAlpha==0xff000000 ? PickFlash : DebuggerAlpha+(PickFlash&0xffffff);
					tv[1].argb=DebuggerAlpha==0xff000000 ? PickFlash : DebuggerAlpha+(PickFlash&0xffffff);
					tv[2].argb=DebuggerAlpha==0xff000000 ? PickFlash : DebuggerAlpha+(PickFlash&0xffffff);
					tv[0].z=0.0;
					tv[1].z=0.0;
					tv[2].z=0.0;
					tv[0].rhw=1.0;
					tv[1].rhw=1.0;
					tv[2].rhw=1.0;
//
// Reset states
//
					gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
					gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
//
// If right button down, do not flash triangle
//
					if( (DBButtons&2)==0 )
					{
						gos_SetRenderState( gos_State_MipMapBias, 0 );
						gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
						gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
						gos_SetRenderState( gos_State_Texture, 0 );

						gos_DrawTriangles( tv, 3 );
					}
				}
				else
				{
//
// Wrapped textures
//
					float LowestU=(float)floor(MIN3(Pickv1.u,Pickv2.u,Pickv3.u));
					float LowestV=(float)floor(MIN3(Pickv1.v,Pickv2.v,Pickv3.v));
					
					if( (DBButtons&2)==0 )							// Show non repeated when button 2 down
					{
//
// Work out how often a texture repeats (complex due to special cases!)
//
						RepeatX=1.0f+(float)floor(MAX3(Pickv1.u,Pickv2.u,Pickv3.u))-LowestU;
						if( (int)MAX3(Pickv1.u,Pickv2.u,Pickv3.u)==MAX3(Pickv1.u,Pickv2.u,Pickv3.u) && RepeatX>1.0 )
							RepeatX-=1.0;

						RepeatY=1.0f+(float)floor(MAX3(Pickv1.v,Pickv2.v,Pickv3.v))-LowestV;
						if( (int)MAX3(Pickv1.v,Pickv2.v,Pickv3.v)==MAX3(Pickv1.v,Pickv2.v,Pickv3.v) && RepeatY>1.0 )
							RepeatY-=1.0;
					}

					gos_VERTEX tv[4];
					tv[0].x=(float)(ExTopX+14);
					tv[1].x=(float)(ExTopX+14);
					tv[2].x=(float)(ExTopX+14+Scale);
					tv[3].x=(float)(ExTopX+14+Scale);

					tv[0].y=(float)(ExTopY+14);
					tv[1].y=(float)(ExTopY+14+Scale);
					tv[2].y=(float)(ExTopY+14+Scale);
					tv[3].y=(float)(ExTopY+14);

					tv[0].argb=DebuggerAlpha+0xffffff;
					tv[1].argb=DebuggerAlpha+0xffffff;
					tv[2].argb=DebuggerAlpha+0xffffff;				// Draw texture used for triangle
					tv[3].argb=DebuggerAlpha+0xffffff;

					tv[0].u=0.0;
					tv[1].u=0.0;
					tv[2].u=RepeatX;
					tv[3].u=RepeatX;

					tv[0].v=0.0;
					tv[1].v=RepeatY;
					tv[2].v=RepeatY;
					tv[3].v=0.0;

					tv[0].z=0.0;
					tv[1].z=0.0;
					tv[2].z=0.0;
					tv[3].z=0.0;

					tv[0].rhw=1.0;
					tv[1].rhw=1.0;
					tv[2].rhw=1.0;
					tv[3].rhw=1.0;

					gos_ClipDrawQuad( tv );							// Draw actual size (unless 512*512 - then half it)
//
// Draw area of texture used in triangle
//
					tv[0]=Pickv1;
					tv[1]=Pickv2;
					tv[2]=Pickv3;
//
// If right button down, do not repeat texture
//
					if( (DBButtons&2)==0 )
					{
						tv[0].x=ExTopX+14+((tv[0].u-LowestU)/RepeatX)*Scale;
						tv[0].y=ExTopY+14+((tv[0].v-LowestV)/RepeatY)*Scale;

						tv[1].x=ExTopX+14+((tv[1].u-LowestU)/RepeatX)*Scale;
						tv[1].y=ExTopY+14+((tv[1].v-LowestV)/RepeatY)*Scale;

						tv[2].x=ExTopX+14+((tv[2].u-LowestU)/RepeatX)*Scale;
						tv[2].y=ExTopY+14+((tv[2].v-LowestV)/RepeatY)*Scale;
					}
					else
					{
						tv[0].x=ExTopX+14+DoUV(tv[0].u)*Scale;
						tv[0].y=ExTopY+14+DoUV(tv[0].v)*Scale;

						tv[1].x=ExTopX+14+DoUV(tv[1].u)*Scale;
						tv[1].y=ExTopY+14+DoUV(tv[1].v)*Scale;

						tv[2].x=ExTopX+14+DoUV(tv[2].u)*Scale;
						tv[2].y=ExTopY+14+DoUV(tv[2].v)*Scale;
					}

					tv[0].argb=DebuggerAlpha==0xff000000 ? PickFlash : DebuggerAlpha+(PickFlash&0xffffff);
					tv[1].argb=DebuggerAlpha==0xff000000 ? PickFlash : DebuggerAlpha+(PickFlash&0xffffff);
					tv[2].argb=DebuggerAlpha==0xff000000 ? PickFlash : DebuggerAlpha+(PickFlash&0xffffff);
					tv[0].z=0.0;
					tv[1].z=0.0;
					tv[2].z=0.0;
					tv[0].rhw=1.0;
					tv[1].rhw=1.0;
					tv[2].rhw=1.0;
//
// Reset states
//
					gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
					gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
					gos_SetRenderState( gos_State_AlphaTest, 1 );
//
// If right button down, do not flash triangle
//
					if( (DBButtons&2)==0 )
					{
						gos_SetRenderState( gos_State_MipMapBias, 0 );
						gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
						gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
						gos_SetRenderState( gos_State_Texture, 0 );

						gos_DrawTriangles( tv, 3 );
					}
				}
//
// Calculate pixel vs. texel coverage
//
				MipMapUsed=GetMipmapUsed( Handle, &Pickv1, &Pickv2, &Pickv3 );
			}
//
// Display renderstate information
//
			char Buffer[256];

			gos_SetRenderState( gos_State_MipMapBias, 0 );
			InitTextDisplay();
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );

			StartX=CurrentX=ExTopX+8;
			StartY=CurrentY=ExTopY+3;
//
// If there is more than one triangle under the cursor display the numbers of layers
//			
			if( PickDepth>1 )
			{
				DrawText( 0xc0c0c0, "Layers " );
				
				if( PickDepth>9 )
					PickDepth=9;

				for( int t0=1; t0<=PickDepth; t0++ )
				{
					if( t0==PickNumber )
					{
						DrawChr( 1 );
						DrawChr( (char)(t0+'0') );
						DrawChr( (char)' ' );
					}
					else
					{
						DrawChr( (char)(t0+'0') );
						DrawChr( (char)' ' );
					}
					if( TextVertex )
						gos_DrawQuads( pTextVertices, TextVertex );
					TextVertex=0;
				}
			}
//
// Show Mip Level being displayed (if Pg Dn or Pg Up have been used)
//
			if( PickStates[gos_State_Texture]!=0  )
			{
				StartX=CurrentX=ExTopX+156;
				if( DebuggerMipLevel && !TEXHANDLE_TO_PTR(Handle)->MipmapDisabled() )
				{
					char temp[32];
					DWORD Width=TEXHANDLE_TO_PTR(Handle)->Width() >> DebuggerMipLevel;
					DWORD Height=TEXHANDLE_TO_PTR(Handle)->Height() >> DebuggerMipLevel;
					sprintf( temp, "MipLevel %d (%dx%d)",DebuggerMipLevel,Width,Height );
					DrawText( 0xc0c0c0, temp );
				}
				else
				{
					char temp[32];
					DWORD Width=TEXHANDLE_TO_PTR(Handle)->Width();
					DWORD Height=TEXHANDLE_TO_PTR(Handle)->Height();
					sprintf( temp, "MipLevel 0 (%dx%d)",Width,Height );
					DrawText( 0xc0c0c0, temp );
				}
			}
//
// If there is a texture describe it
//
			StartX=CurrentX=ExTopX+8;
			StartY=CurrentY=(Environment.screenWidth!=512) ? (ExTopY+274) : (ExTopY+274-96);
			DbChrX=0;
			DbChrY=0;
			DbMaxX=30;
			DbMaxY=24;
			char tempBuffer[32];

			if( PickStates[gos_State_Texture]!=0  )
			{
				if( strlen(TEXHANDLE_TO_PTR(Handle)->Name())>30 )
				{
					strcpy( tempBuffer, ".." );
					strcat( tempBuffer,TEXHANDLE_TO_PTR(Handle)->Name()+strlen(TEXHANDLE_TO_PTR(Handle)->Name())-28 );
				}
				else
				{
					strcpy( tempBuffer,TEXHANDLE_TO_PTR(Handle)->Name() );
				}

				sprintf( Buffer, tempBuffer);
				DrawText( 0xc0c0c0, Buffer );				// Path name of texture
				DrawText( 0xc0c0c0, "\n" );

				sprintf( Buffer, "Format %s = %s ( %s)\n", (TEXHANDLE_TO_PTR(Handle)->Detected() ? "found" : "force"),
					 TEXHANDLE_TO_PTR(Handle)->Format()==gos_Texture_Solid ? "Solid" :
					(TEXHANDLE_TO_PTR(Handle)->Format()==gos_Texture_Keyed ? "Keyed" :
					(TEXHANDLE_TO_PTR(Handle)->Format()==gos_Texture_Alpha ? "Alpha" :
					(TEXHANDLE_TO_PTR(Handle)->Format()==gos_Texture_Bump ? "Bump" : "Norm" ) ) ),
					 GetGosPixelFormat(&TEXHANDLE_TO_PTR(Handle)->Description()->ddpfPixelFormat) );
				DrawText( 0xc0c0c0, Buffer );

				sprintf( Buffer, "Addressing   = %s\n", (PickStates[gos_State_TextureAddress]==gos_TextureWrap ? "WRAP" : "CLAMP" ));
				DrawText( 0xc0c0c0, Buffer );

				sprintf( Buffer, "Handle       = 0x%x\n",Handle);
				DrawText( 0xc0c0c0, Buffer );

				sprintf( Buffer, "Mipmap Level = %d\n",MipMapUsed);
				DrawText( 0xc0c0c0, Buffer );

				char* Filter="Unknown";
				switch (TEXHANDLE_TO_PTR(Handle)->MipFilter() )
				{
					case 0:
						Filter="Box";
						break;
					case 1:
						Filter="Point";
						break;
				}
				
				sprintf( Buffer, "Filter       = %s\n",Filter );
				DrawText( 0xc0c0c0, Buffer );

				if( gosHint_DisableMipmap&TEXHANDLE_TO_PTR(Handle)->Hints() )
				{
					sprintf( Buffer, "Hint         = Disable Mipmap\n" );
					DrawText( 0xc0c0c0, Buffer );
				}
				if( gosHint_DontShrink&TEXHANDLE_TO_PTR(Handle)->Hints() )
				{
					sprintf( Buffer, "Hint         = Dont Shrink\n" );
					DrawText( 0xc0c0c0, Buffer );
				}
				if( gosHint_AGPMemory&TEXHANDLE_TO_PTR(Handle)->Hints() )
				{
					sprintf( Buffer, "Hint         = AGP\n" );
					DrawText( 0xc0c0c0, Buffer );
				}
				if( gosHint_VideoMemory&TEXHANDLE_TO_PTR(Handle)->Hints() )
				{
					sprintf( Buffer, "Hint         = Video\n" );
					DrawText( 0xc0c0c0, Buffer );
				}
				if( gosHint_UserMipMaps&TEXHANDLE_TO_PTR(Handle)->Hints() )
				{
					sprintf( Buffer, "Hint         = User Mipmaps\n" );
					DrawText( 0xc0c0c0, Buffer );
				}
				if( gosHint_ReloadFromDisk&TEXHANDLE_TO_PTR(Handle)->Hints() )
				{
					sprintf( Buffer, "Hint         = ReloadFromDisk\n" );
					DrawText( 0xc0c0c0, Buffer );
				}
				if( TEXHANDLE_TO_PTR(Handle)->pRebuildFunc() )
				{
					sprintf( Buffer, "Rebuild function %08x, Instance %08x\n", TEXHANDLE_TO_PTR(Handle)->pRebuildFunc(), TEXHANDLE_TO_PTR(Handle)->pInstance() );
					DrawText( 0xc0c0c0, Buffer );
				}
			}
//
// Show current renderstate settings
//
			StartY=CurrentY=(Environment.screenWidth!=512) ? (ExTopY+290-12+9*8) : (ExTopY+290-12+9*8-96);
			DbChrX=0;
			DbChrY=0;
			DbMaxX=30;
			DbMaxY=24;

			sprintf( Buffer, "Filter       = %s\n", (PickStates[gos_State_Filter]==gos_FilterNone) ? "None" : 
				(PickStates[gos_State_Filter]==gos_FilterBiLinear) ? "BiLinear" : "TriLinear" );
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "AlphaMode    = %s\n",(PickStates[gos_State_AlphaMode]==gos_Alpha_OneZero) ? "OneZero" :
			(PickStates[gos_State_AlphaMode]==gos_Alpha_OneOne) ? "OneOne" :
			(PickStates[gos_State_AlphaMode]==gos_Alpha_AlphaOne) ? "AlphaOne" :
			(PickStates[gos_State_AlphaMode]==gos_Alpha_OneInvAlpha) ? "OneInvAlpha" : "AlphaInvAlpha" );
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Shade Mode   = %s\n", gos_ShadeGouraud==PickStates[gos_State_ShadeMode] ? "Gouraud" : "Flat");
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "TextureBlend = %s\n", gos_BlendModulateAlpha==PickStates[gos_State_TextureMapBlend] ? "ModulateAlpha" : (gos_BlendModulate==PickStates[gos_State_TextureMapBlend] ? "Modulate" : "Decal") );
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Mipmap Bias  = %d\n", PickStates[gos_State_MipMapBias]);
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "ZCompare     = %s\n", PickStates[gos_State_ZCompare]==1 ? "LessEqual" : PickStates[gos_State_ZCompare]==2 ? "Less" : "False");
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "ZWrite       = %s\n", PickStates[gos_State_ZWrite] ? "True" : "False");
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Perspective  = %s\n", PickStates[gos_State_Perspective] ? "True" : "False");
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Dither       = %s\n", PickStates[gos_State_Dither] ? "True" : "False");
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "AlphaTest    = %s\n", PickStates[gos_State_AlphaTest] ? "True" : "False");
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Mono Enable  = %s\n", (Environment.Renderer==3 && (PickStates[gos_State_MonoEnable]&256)) ? ((PickStates[gos_State_MonoEnable]&1) ? "True+Doubled" : "False+Doubled") : ((PickStates[gos_State_MonoEnable]&1) ? "True" : "False") );
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Specular     = %s\n", PickStates[gos_State_Specular] ? "True" : "False");
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Clipping     = %s\n", 0==PickStates[gos_State_Clipping] ? "False" : (1==PickStates[gos_State_Clipping] ? "True" : "Guard") );
			DrawText( 0xc0c0c0, Buffer );

			sprintf( Buffer, "Wireframe    = %s\n", PickStates[gos_State_WireframeMode] ? "True" : "False");
			DrawText( 0xc0c0c0, Buffer );

			if( 0==PickStates[gos_State_Fog] )
			{
				sprintf( Buffer, "Fog          = Disabled\n");
			}
			else
			{
				sprintf( Buffer, "Fog Color    = 0x%06x\n", PickStates[gos_State_Fog]&0xffffff);
			}
			DrawText( 0xc0c0c0, Buffer );

			StartX=CurrentX=ExTopX+8+31*6+4;
			StartY=CurrentY=(Environment.screenWidth!=512) ? (ExTopY+290-16) : (ExTopY+290-16-96);
			DbChrX=0;
			DbChrY=0;
			DbMaxX=13;
			DbMaxY=24;

			sprintf( Buffer, "v1 " );
			DrawText( 0xf0f0f0, Buffer );
			sprintf( Buffer, "x=% 8.2f\n",(double)Pickv1.x );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "   y=% 8.2f\n",(double)Pickv1.y );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "   z=% 8.5f\n",(double)Pickv1.z );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, " rhw=% 8.5f\n",(double)Pickv1.rhw );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "argb=%08x\n",Pickv1.argb );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "frgb=%08x\n",Pickv1.frgb );
			DrawText( 0xc0c0c0, Buffer );
			if( PickStates[gos_State_Texture]!=0 )
			{
				DisplayUV( Pickv1.u, Pickv1.v );
			}
			else
			{
				DrawText( 0xc0c0c0, "   u=\n" );
				DrawText( 0xc0c0c0, "   v=\n" );
			}

			StartY=CurrentY=CurrentY+2;
			
			sprintf( Buffer, "v2 " );
			DrawText( 0xf0f0f0, Buffer );
			sprintf( Buffer, "x=% 8.2f\n",(double)Pickv2.x );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "   y=% 8.2f\n",(double)Pickv2.y );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "   z=% 8.5f\n",(double)Pickv2.z );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, " rhw=% 8.5f\n",(double)Pickv2.rhw );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "argb=%08x\n",Pickv2.argb );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "frgb=%08x\n",Pickv2.frgb );
			DrawText( 0xc0c0c0, Buffer );
			if( PickStates[gos_State_Texture]!=0 )
			{
				DisplayUV( Pickv2.u, Pickv2.v );
			}
			else
			{
				DrawText( 0xc0c0c0, "   u=\n" );
				DrawText( 0xc0c0c0, "   v=\n" );
			}

			StartY=CurrentY=CurrentY+2;

			sprintf( Buffer, "v3 " );
			DrawText( 0xf0f0f0, Buffer );
			sprintf( Buffer, "x=% 8.2f\n",(double)Pickv3.x );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "   y=% 8.2f\n",(double)Pickv3.y );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "   z=% 8.5f\n",(double)Pickv3.z );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, " rhw=% 8.5f\n",(double)Pickv3.rhw );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "argb=%08x\n",Pickv3.argb );
			DrawText( 0xc0c0c0, Buffer );
			sprintf( Buffer, "frgb=%08x\n",Pickv3.frgb );
			DrawText( 0xc0c0c0, Buffer );
			if( PickStates[gos_State_Texture]!=0 )
			{
				DisplayUV( Pickv3.u, Pickv3.v );
			}
			else
			{
				DrawText( 0xc0c0c0, "   u=\n" );
				DrawText( 0xc0c0c0, "   v=\n" );
			}
		}
//
// Clear current triangle found
//		
		if( KeyPress=='-' && DebuggerMipLevel )
		{
			DebuggerMipLevel--;
		}
		if( (KeyPress=='+' || KeyPress=='=') && DebuggerMipLevel<4 )
		{
			DebuggerMipLevel++;
		}

		if( KeyPress=='1' )
			PickNumber=1;
		if( KeyPress=='2' )
			PickNumber=2;
		if( KeyPress=='3' )
			PickNumber=3;
		if( KeyPress=='4' )
			PickNumber=4;
		if( KeyPress=='5' )
			PickNumber=5;
		if( KeyPress=='6' )
			PickNumber=6;
		if( KeyPress=='7' )
			PickNumber=7;
		if( KeyPress=='8' )
			PickNumber=8;
		if( KeyPress=='9' )
			PickNumber=9;
		if( KeyPress=='0' )
			PickNumber=10;
//
// If left button held enter zoom mode (center mouse wheel zooms in and out)
//
		if( DBWheelDelta )
		{
			BaseZoom-=DBWheelDelta;
			if( BaseZoom>12 )
				BaseZoom=12;
			if( BaseZoom<0 )
				BaseZoom=0;
		}
		if( DBButtons&1 )
		{
			if( !DebounceMouse )
			{	BaseZoom=0;
				ZoomMode+=1;
			}
			if( ZoomMode>12 )
				ZoomMode=12;
		}
		else
		{
			DebounceMouse=0;
			if( ZoomMode>BaseZoom )
				ZoomMode-=1;
			if( ZoomMode<BaseZoom )
				ZoomMode+=1;
		}
//
//
//
		ExMouseX=DBMouseX;
		ExMouseY=DBMouseY;
		PickFound=0;
		PickDepth=0;
	}
}

























#ifdef LAB_ONLY
//
//
// Do all the debugger processing
//
//
void DoDebugger()
{
//
// If debugger off the display, bring it back
//
	if( ((int)Environment.screenWidth < DbTopX) || ((int)Environment.screenHeight < DbTopY) )
	{
		DbTopX=60;
		DbTopY=60;
	}
//
// Check for debugger keys and mouse
//
	if( MenuActive || SubMenuActive )
	{
		DWORD Key=(gos_GetKey()>>8)&~0xff00;
//
// If enter pressed on a menu item, do it.
//
		if( Key==KEY_RETURN )
		{
			if( MenuHighlight && 
			   ((MenuHighlight->Routine || MenuHighlight->Callback) || MenuHighlight->pSubMenu) &&
			 !((MenuHighlight->Callback && MenuHighlight->Callback(MenuHighlight->FullName,gosMenu_Greyed) ) ||
			   (MenuHighlight->Greyed && MenuHighlight->Greyed())) )
			{
				if( MenuHighlight->pSubMenu )
				{
					DebounceMenu=1;
					BaseMenu=MenuHighlight;
					OpenSubMenu( MenuHighlight->pSubMenu, MenuEndX, MenuEndY );
					MenuHighlight=MenuHighlight->pSubMenu;
				}
				else
				{
					bool Shift=KEY_FREE!=gos_GetKeyStatus(KEY_LSHIFT);
					
					if( MenuHighlight->Routine )
					{
						if( !Shift )
						{
							MenuActive=0;
							SubMenuActive=0;
						}
						MenuHighlight->Routine();
					}
					else
					{
						if( MenuHighlight->Callback( MenuHighlight->FullName,gosMenu_Activated ))
						{
							DebounceMenu=1;
						}
						else
						{
							if( !Shift )
							{
								MenuActive=0;
								SubMenuActive=0;
							}
							else
								DebounceMenu=1;
						}
					}
				}
			}
		}
//
// If the mouse was not moved, keyboard controls work
//
		if( !DBMouseMoved )
		{
			if( Key==KEY_DOWN )
			{
				if( 0==MenuHighlight )
				{
					MenuHighlight=MenuContent;				
				}
				else
				{
					if( SaveNextMenu )
					{
						MenuHighlight=SaveNextMenu;
						OldMenuHighlight=MenuHighlight;
						MenuTimer=gos_GetHiResTime()+3600.0;
					}
				}
			}
			if( Key==KEY_UP )
			{
				if( 0==MenuHighlight )
				{
					MenuHighlight=MenuContent;				
				}
				else
				{
					if( SaveLastMenu )
					{
						MenuHighlight=SaveLastMenu;
						OldMenuHighlight=MenuHighlight;
						MenuTimer=gos_GetHiResTime()+3600.0;
					}
				}
			}
			if( Key==KEY_RIGHT )
			{
				if( !SubMenuActive && MenuHighlight && MenuHighlight->pSubMenu )
				{
					MenuActive=1;
					BaseMenu=MenuHighlight;
					OpenSubMenu( MenuHighlight->pSubMenu, MenuEndX, MenuEndY );
					MenuHighlight=MenuHighlight->pSubMenu;
				}
				else
				{
					if( MenuContent==pDebuggerMenu )
					{
						OpenMenu( pRasterizerMenu, 8+6*10, 16 );
						if( MenuHighlight )
							MenuHighlight=pRasterizerMenu;
					}
					else
					{
						if( MenuContent==pRasterizerMenu )
						{
							if( pLibrariesMenu )
							{
								OpenMenu( pLibrariesMenu, 8+6*22, 16 );
								if( MenuHighlight )
									MenuHighlight=pLibrariesMenu;
							}
							else
							{
								if( pGameMenu )
								{
									OpenMenu( pGameMenu, pLibrariesMenu ? 8+6*33 : 8+6*22, 16 );
									if( MenuHighlight )
										MenuHighlight=pGameMenu;
								}
							}
						}
						else
						{
							if( MenuContent==pLibrariesMenu )
							{
								if( pGameMenu )
								{
									OpenMenu( pGameMenu, pLibrariesMenu ? 8+6*33 : 8+6*22, 16 );
									if( MenuHighlight )
										MenuHighlight=pGameMenu;
								}
							}
						}
					}
					OldMenuHighlight=MenuHighlight;
					MenuTimer=gos_GetHiResTime()+3600.0;
				}
			}
			if( Key==KEY_LEFT )
			{
				if( SubMenuActive )
				{
					SubMenuActive=0;
					OldMenuHighlight=MenuHighlight=BaseMenu;
					MenuTimer=gos_GetHiResTime()+3600.0;
				}
				else
				{
					if( MenuContent==pRasterizerMenu )
					{
						OpenMenu( pDebuggerMenu, 8+6*0, 16 );
						if( MenuHighlight )
							MenuHighlight=pDebuggerMenu;
					}
					else
					{
						if( MenuContent==pLibrariesMenu )
						{
							OpenMenu( pRasterizerMenu, 8+6*10, 16 );
							if( MenuHighlight )
								MenuHighlight=pRasterizerMenu;
						}
						if( MenuContent==pGameMenu )
						{
							if( pLibrariesMenu )
							{
								OpenMenu( pLibrariesMenu, 8+6*22, 16 );
								if( MenuHighlight )
									MenuHighlight=pLibrariesMenu;
							}
							else
							{
								OpenMenu( pRasterizerMenu, 8+6*10, 16 );
								if( MenuHighlight )
									MenuHighlight=pRasterizerMenu;
							}
						}
					}
					OldMenuHighlight=MenuHighlight;
					MenuTimer=gos_GetHiResTime()+3600.0;
				}
			}
		}
//
// ALT key will close the current menu
//
		if( gos_GetKeyStatus(KEY_LMENU) )
		{
			if( DebounceAlt==0 )
			{
				DebounceAlt=1;
				MenuActive=0;
				SubMenuActive=0;
			}
		}
		else
		{
			DebounceAlt=0;
		}
	}
	else
	{
//
// ALT key will open the first menu
//
		if( gos_GetKeyStatus(KEY_LMENU) )
		{
			if( DebounceAlt==0 )
			{
				DebounceAlt=1;
				OpenMenu( pDebuggerMenu, 8+6*0, 16 );
				MenuHighlight=pDebuggerMenu;
			}
		}
		else
		{
			DebounceAlt=0;
		}
//
// Check to see if user dimmed window (all modes of debugger)
//
		DWORD Key = gos_GetKey();
		char ascii = (char) (Key & 0xff);
		Key=(Key>>8)&~0xff00;

		if( (Key==KEY_UP && gos_GetKeyStatus(KEY_LSHIFT)) && AlphaIndex>0 )
			AlphaIndex-=1;
		if( (Key==KEY_DOWN && gos_GetKeyStatus(KEY_LSHIFT)) && AlphaIndex<5 )
			AlphaIndex+=1;
		DebuggerAlpha=DebuggerAlphaColors[AlphaIndex];
//
// Check keys when menus not open
//
		switch( DebugDisplay )
		{
			case DbgS_Stat:						// Statistics
			{
				if( Key==KEY_DOWN && !gos_GetKeyStatus(KEY_LSHIFT) )
				{
					if( TopStatistics < (NumberStatistics-1) )	
						TopStatistics++;
				}
				if( Key==KEY_UP && !gos_GetKeyStatus(KEY_LSHIFT) )
				{
					if( TopStatistics >= 1 )	
						TopStatistics--;
				}
				if( Key==KEY_HOME )
				{
					TopStatistics=0;
				}
				if( Key==KEY_END )
				{
					if( NumberStatistics>=39 )
						TopStatistics=NumberStatistics-39;
					else
						TopStatistics=0;
				}
				if( Key==KEY_NEXT )
				{
					if( TopStatistics < (NumberStatistics-39) )
						TopStatistics+=39;
				}
				if( Key==KEY_PRIOR )
				{
					if( TopStatistics > 39 )	
						TopStatistics-=39;
					else
						TopStatistics=0;
				}
				break;
			}
			
			case DbgS_Spew:						// Spew output
			{
				// add scrolling support to spew output
				DoSpewInterface(ascii);
				break;
			}
			
			case DbgS_Sound:						// Sound display
			{
				if( Key==KEY_DOWN && !gos_GetKeyStatus(KEY_LSHIFT) )
					gStartingChannel++;

				if( Key==KEY_UP && !gos_GetKeyStatus(KEY_LSHIFT) )
					gStartingChannel--;

				if( Key==KEY_HOME )
					gStartingChannel = 0;

				if( Key==KEY_END )
					gStartingChannel = Environment.soundChannels-1;

				if( Key==KEY_NEXT )
					gStartingChannel += 4;

				if( Key==KEY_PRIOR )
					gStartingChannel -= 4;

				if ( gStartingChannel < 0 )
					gStartingChannel = 0;

				if ( gStartingChannel >= Environment.soundChannels )
					gStartingChannel = Environment.soundChannels-1;
				break;
			}
			
			case DbgS_Control:						// Input devices
			{
				int num = g_CMData.m_numJoysticks;
				if (num)
				{
					if( Key==KEY_DOWN && !gos_GetKeyStatus(KEY_LSHIFT) )
					{
						gWhichCMDevice++;
						if (gWhichCMDevice >= num)
							gWhichCMDevice = 0;
					}
					
					if( Key==KEY_UP && !gos_GetKeyStatus(KEY_LSHIFT) )
					{
						gWhichCMDevice--;
						if (gWhichCMDevice < 0)
							gWhichCMDevice = 0;
					}
				}
				break;
			}

			case DbgS_UsedTextures:						// Texture heaps
			case DbgS_UnusedTextures:
			case DbgS_LoadedTextures:
			case DbgS_AllTextures:
			case DbgS_UsedTextureDetail:
			case DbgS_UnusedTextureDetail:
			case DbgS_LoadedTextureDetail:
			case DbgS_AllTextureDetail:
			case DbgS_AllTextureNoUpload:
			{
				if( Key==KEY_DOWN && !gos_GetKeyStatus(KEY_LSHIFT) )
				{
					TextureScroll-=8;
				}

				if( Key==KEY_UP && !gos_GetKeyStatus(KEY_LSHIFT) )
				{
					TextureScroll+=8;
					if( TextureScroll>0 )
						TextureScroll=0;
				}

				if( Key==KEY_HOME )
				{
					TextureScroll=0;
				}

				if( Key==KEY_END )
				{
					TextureScroll=-100000;
				}
				
				if( Key==KEY_PRIOR )
				{
					TextureScroll+=8*40;
					if( TextureScroll>0 )
						TextureScroll=0;
				}

				if( Key==KEY_NEXT )
				{
					TextureScroll-=8*40;
				}

				SelectedHandle=0;
				TextureToUnload=0;
				if( DBButtons && pTextureHeap )
				{
					TextureHeap* pThis=pTextureHeap;
					while( pThis )
					{
						if( DBMouseX>=pThis->X1 && DBMouseX<=pThis->X2 &&
							DBMouseY>=pThis->Y1 && DBMouseY<=pThis->Y2 )
						{
							if( ( DBButtons & 1 ) &&
								( ( DebugDisplay == DbgS_UsedTextures ) ||
								  ( DebugDisplay == DbgS_LoadedTextures ) ||
								  ( DebugDisplay == DbgS_UsedTextureDetail ) ||
								  ( DebugDisplay == DbgS_LoadedTextureDetail ) ))
							{
								// left-click, flash the selected texture
								SelectedHandle=pThis->Handle;
							}
							if( ( DBButtons & 4 ) && ( ( DebugDisplay == DbgS_LoadedTextures ) || ( DebugDisplay == DbgS_LoadedTextureDetail ) ) )
							{
								// middle-click, unload the selected texture
								TextureToUnload = pThis->Handle;
							}
							break;
						}
						pThis=pThis->pNext;
					}
				}
				break;
			}

			case DbgS_TextureLog:
			{
				break;
			}

		
			case DbgS_Memory:						// Memory heaps
			{
				if (DBMouseX- DbTopX > 228 && DBMouseX - DbTopX < 252 && 
					DBMouseY- DbTopY > 16 && DBMouseY- DbTopY < 24 )
				{
					if (!DebounceMouse && (DBButtons&1))
					{
						ShowAllocations = !ShowAllocations;
						DebounceMouse = 1;
					}
				}
				if (DBMouseX- DbTopX > 374 && DBMouseX - DbTopX < 402 && 
					DBMouseY- DbTopY > 16 && DBMouseY- DbTopY < 24 )
				{
					if (!DebounceMouse && (DBButtons&1))
					{
						ShowBlockInfo = !ShowBlockInfo;
						DebounceMouse = 1;
					}
				}
				DoExamineHeaps(Key);
				break;
			}
			case DbgS_PlayHistory:
			{
				if (DBMouseX- DbTopX > 132 && DBMouseX - DbTopX < 152 && 
					DBMouseY- DbTopY > 40 && DBMouseY- DbTopY < 48)
				{
					if (!DebounceMouse && (DBButtons&1))
					{
						SoundRenderer.m_verboseDebugger = !SoundRenderer.m_verboseDebugger;
						DebounceMouse = 0;
					}
				}
				break;
			}
			case DbgS_SoundSpatial:
			{
				if (Key == KEY_PRIOR)
				{
					if (gSoundSpatDebugZoom > 0.25f)
						gSoundSpatDebugZoom -= .25f;
				}
				else
				if (Key == KEY_NEXT)
				{
					if (gSoundSpatDebugZoom < 3.0f)
						gSoundSpatDebugZoom += .25f;
				}

				break;
			}
		}
	}
//
// Has cursor sat above a submenu for .25 seconds?
//
	if( MenuHighlight!=OldMenuHighlight )
	{
		OldMenuHighlight=MenuHighlight;
		MenuTimer=gos_GetHiResTime();					// If new menu item, reset timer
	}
	if( MenuHighlight && (gos_GetHiResTime()-MenuTimer>0.25f) )
	{
		if( 0==(DBButtons&1) )
		{
			if( MenuHighlight->pSubMenu )
			{
				if( !SubMenuActive || SubMenuContent!=MenuHighlight->pSubMenu )
				{
					BaseMenu=MenuHighlight;
					MenuActive=1;
					OpenSubMenu( MenuHighlight->pSubMenu, MenuEndX, MenuEndY );
				}	
			}
			else
			{
				if( SubMenuActive )
				{
					MenuItem* pTemp=SubMenuContent;
					bool OnSubMenu=0;
					while( pTemp )
					{
						if( pTemp==MenuHighlight )
							OnSubMenu=1;
						pTemp=pTemp->pNext;
					}
					if( !OnSubMenu )
						SubMenuActive=0;
				}
			}
		}
	}
//
// Menu Open?
//
	bool Shift=KEY_FREE!=gos_GetKeyStatus(KEY_LSHIFT);
	if( MenuActive || SubMenuActive )
	{
		if( DBButtons&1 )
		{
			if( !DebounceMenu )
			{
				DebounceMouse=1;
				if( !SubMenuActive )
				{
					if( MenuHighlight && (MenuHighlight->Routine || MenuHighlight->Callback || MenuHighlight->pSubMenu) && !( (MenuHighlight->Callback && MenuHighlight->Callback(MenuHighlight->FullName,gosMenu_Greyed) ) || (MenuHighlight->Greyed && MenuHighlight->Greyed())) )
					{
						if( MenuHighlight->pSubMenu )
						{
							DebounceMenu=1;
							BaseMenu=MenuHighlight;
							OpenSubMenu( MenuHighlight->pSubMenu, MenuEndX, MenuEndY );
						}
						else
						{
							if( MenuHighlight->Routine )
							{
								if( !Shift )
									MenuActive=0;
								else
									DebounceMenu=1;
								MenuHighlight->Routine();
							}
							else
							{
								if( MenuHighlight->Callback( MenuHighlight->FullName,gosMenu_Activated ))
								{
									DebounceMenu=1;
								}
								else
								{
									if( !Shift )
										MenuActive=0;
									else
										DebounceMenu=1;
								}
							}
						}
					}
					else
					{
						if( !MenuHighlight )
						{
							if( !Shift )
								MenuActive=0;
							else
								DebounceMenu=1;
						}
					}
				}
				else
				{
					DebounceMenu=1;
					if( MenuHighlight )
					{
						if( !( (MenuHighlight->Callback && MenuHighlight->Callback(MenuHighlight->FullName,gosMenu_Greyed) ) || (MenuHighlight->Greyed && MenuHighlight->Greyed())) )
						{
							if( MenuHighlight->pSubMenu )
							{
								BaseMenu=MenuHighlight;
								OpenSubMenu( MenuHighlight->pSubMenu, MenuEndX, MenuEndY );
							}
							else
							{
								if( MenuHighlight->Routine )
								{
									if( !Shift )
									{
										MenuActive=0;
										SubMenuActive=0;
									}
									else
										DebounceMenu=1;
									MenuHighlight->Routine();
								}
								else
								{
									if( MenuHighlight->Callback && MenuHighlight->Callback( MenuHighlight->FullName,gosMenu_Activated ))
									{
										DebounceMenu=1;
									}
									else
									{
										if( !Shift )
										{
											MenuActive=0;
											SubMenuActive=0;
										}
										else
											DebounceMenu=1;
									}
								}
							}
						}
					}
					else
					{
						if( !Shift )
						{
							MenuActive=0;
							SubMenuActive=0;
						}
						else
							DebounceMenu=1;
					}
				}
			}
		}
		else
		{
			DebounceMenu=0;
		}
	}
//
// Clicked above title bar?
//
	if( DraggingWindow )
	{
		if( 0==(DBButtons&1) )
			DraggingWindow=0;
	}
	else
	{
		if( !MenuActive && !SubMenuActive && (DBButtons&1) && (DBMouseX>DbTopX-2) && DBMouseX<(DbTopX+DbSizeX) && DBMouseY>DbTopY &&			// In window
			((DBMouseY<(DbTopY+4)) ||																					// Above menu bar
			 (DBMouseY<=(DbTopY+13) && ((DBMouseX-DbTopX<4) || DBMouseX>(pGameMenu ? (DbTopX+((pLibrariesMenu ? 33 : 22)+strlen(ApplicationName))*6+7) : (DbTopX+(pLibrariesMenu ? 31 : 20)*6+7))) )	// Or past last menu entry
			)
		  )
		{
			DragLastX=DBMouseX;
			DragLastY=DBMouseY;
			DraggingWindow=1;
		}
		else
			DraggingWindow=0;
	}
//
// Dragging window?
//
	if( (DBButtons&1) && DraggingWindow )
	{
		DbTopX+=DBMouseX-DragLastX;
		DbTopY+=DBMouseY-DragLastY;
		DragLastX=DBMouseX;
		DragLastY=DBMouseY;
	}
//
// Check for automatically opening a top level menu (only if not obscured by a menu or sub menu)
//
	if( MenuActive && DBMouseX>DbTopX && DBMouseX<(DbTopX+DbSizeX) && DBMouseY>DbTopY && DBMouseY<(DbTopY+DbSizeY) )
	{
		if( !(DBMouseX>MenuX && DBMouseX<MenuX+MenuWidth && DBMouseY>MenuY && DBMouseY<MenuY+MenuHeight) &&
			!(SubMenuActive && DBMouseX>SubMenuX && DBMouseX<SubMenuX+SubMenuWidth && DBMouseY>SubMenuY && DBMouseY<SubMenuY+SubMenuHeight) )
		{
			DWORD MenuX=DBMouseX-DbTopX;
			DWORD MenuY=DBMouseY-DbTopY;

			if( MenuY<=13 && MenuY>=4 && MenuX>=4 && MenuX<(80*6) )
			{
				MenuX=(MenuX-7)/6;

				if( MenuX<8 )
				{
					if( MenuContent!=pDebuggerMenu )
					{
						DebounceMenu=1;
						OpenMenu( pDebuggerMenu, 8+6*0, 16 );
					}
				}

				if( MenuX>=10 && MenuX<20 )
				{
					if( MenuContent!=pRasterizerMenu )
					{
						DebounceMenu=1;
						OpenMenu( pRasterizerMenu, 8+6*10, 16 );
					}
				}
				if( pLibrariesMenu && MenuX>=22 && MenuX<31 )
				{
					if( MenuContent!=pLibrariesMenu )
					{
						DebounceMenu=1;
						OpenMenu( pLibrariesMenu, 8+6*22, 16 );
					}
				}
				if( pGameMenu && (pLibrariesMenu ? (MenuX>=33 && MenuX<(33+strlen(ApplicationName))) : (MenuX>=22 && MenuX<(22+strlen(ApplicationName)))) )
				{
					if( MenuContent!=pGameMenu )
					{
						DebounceMenu=1;
						OpenMenu( pGameMenu, pLibrariesMenu ? 8+6*33 : 8+6*22, 16 );
					}
				}
			}
		}
	}
//
// Check for mouse clicks
//
	if( DBButtons&1 && !DraggingWindow )
	{
//
// Mouse clicked inside debug window?
//
		if( DBMouseX>DbTopX && DBMouseX<(DbTopX+DbSizeX) && DBMouseY>DbTopY && DBMouseY<(DbTopY+DbSizeY) )
		{
			DWORD MenuX=DBMouseX-DbTopX;
			DWORD MenuY=DBMouseY-DbTopY;
//
// On menu bar?
//
			if( MenuY<=13 && MenuY>=4 && MenuX>=4 && MenuX<(80*6) )
			{
				MenuX=(MenuX-7)/6;	// Convert mouse to characters

				if( MenuX<8 )
				{
					DebounceMenu=1;
					OpenMenu( pDebuggerMenu, 8+6*0, 16 );
				}

				if( MenuX>=10 && MenuX<20 )
				{
					DebounceMenu=1;
					OpenMenu( pRasterizerMenu, 8+6*10, 16 );
				}
				if( pLibrariesMenu && MenuX>=22 && MenuX<31 )
				{
					DebounceMenu=1;
					OpenMenu( pLibrariesMenu, 8+6*22, 16 );
				}
				if( pGameMenu && (pLibrariesMenu ? (MenuX>=33 && MenuX<(33+strlen(ApplicationName))) : (MenuX>=22 && MenuX<(22+strlen(ApplicationName)))) )
				{
					DebounceMenu=1;
					OpenMenu( pGameMenu, pLibrariesMenu ? 8+6*33 : 8+6*22, 16 );
				}
			}
//
// Inside scrolling information?
//
			else
			{
				if( DebounceMouse==0 )
				{
					DebounceMouse=1;

					if( MenuY>=24 && MenuX>=4 && MenuX<(80*6) )
					{
						MenuY=(MenuY-16)/8;
						MenuX=(MenuX-4)/6;	// Convert mouse to characters
//
// On 'graph' column in statistics?
//
						if( DebugDisplay==DbgS_Stat )
						{
							if( MenuX>=44 && MenuX<49 )
							{
								Stat* pStat=pStatistics;
								for( int i=0;i<TopStatistics; i++ )
									if( pStat )
										pStat=pStat->pNext;

								while( pStat && MenuY>1 )
								{
									pStat=pStat->pNext;
									MenuY-=1;
								}

								if( pStat && !(pStat->Flags&Stat_Format) )
								{
									pStat->Flags^=Stat_Graph;
									UpdateGraphs();
								}							
							}
						}
//
// On 'graph' column in Processor Statistics?
//
						if( DebugDisplay==DbgS_CPUStats )
						{
							if( MenuX>=44 && MenuX<49 )
							{
								Stat* pStat=pStatistics;
								while( pStat && MenuY>3 )
								{
									pStat=pStat->pNext;
									if( pStat && gos_cycledata==pStat->Type )
										MenuY-=1;
								}

								if( pStat && !(pStat->Flags&Stat_Format) )
								{
									pStat->Flags^=Stat_Graph;
								}							
								
							}
							if( MenuY>=PerfYStart && MenuY<=PerfYEnd )
							{
								if( MenuY-PerfYStart==PerfCounterSelected )
									PerfCounterSelected=-1;
								else
								{
									PerfCounterSelected=MenuY-PerfYStart;									
									SetPerformanceMonitor( PerfCounterSelected );
									NewPerformanceRegister=1;
								}
							}	
						}
					}
				}
			}
		}
	}
	else
	{
		DebounceMouse=0;
	}
//
// Debugger may have been disabled by a menu option
//
	if( OldRenderMode!=ExamineDepth && OldRenderMode!=ExamineRenderer && OldRenderMode!=ExamineMipmaps && OldRenderMode!=ExamineMipmaps2 )
	{
//
// If any part of window off display, enable clipping
//
		if( DbTopX>0 && (DbTopX+DbSizeX)<Environment.screenWidth && DbTopY>0 && (DbTopY+DbSizeY)<Environment.screenHeight )
			gos_SetRenderState( gos_State_Clipping, 0 );
		else
			gos_SetRenderState( gos_State_Clipping, 2 );
//
// Show the debug window
//
		UpdateDebugWindow();
//
// Draw any floating menus
//
		DrawMenu();
	}
}
#endif












//
// Display any information about current display mode for 4 seconds
//
void UpdateDisplayInfo()
{
	if( EnableDisplayInfo==1 )
	{
		EndDisplayInfoTime=timeGetTime();
		EnableDisplayInfo=2;
	}
	if( EnableDisplayInfo==2 )
	{
		if( timeGetTime() < EndDisplayInfoTime+4000 )
		{
			InitTextDisplay();
			gos_SetRenderState( gos_State_Clipping, 2 );							// Enable clipping for long names
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
			DWORD oldAlpha=DebuggerAlpha;
			DebuggerAlpha=0xD0000000;
			CurrentX=(Environment.screenWidth/2)-strlen(DisplayInfoText)*6;
			CurrentY=6;
			DoubleChr=1;
			DrawText( 0xc0c0f0, DisplayInfoText );
			DoubleChr=0;
			gos_SetRenderState( gos_State_Clipping, 0 );
			DebuggerAlpha=oldAlpha;
		}
		else
		{
			EnableDisplayInfo=0;
		}
	}
}



bool _stdcall WriteImageAfterGrab(BYTE* Image, const char* pcszFilePrefix)
{
    bool bRet = false;
		if( Image )
		{
//
// Find a unique file
//
			char FileName[32];
			DWORD Number=0;
			while(1)
			{
				sprintf( FileName, pcszFilePrefix, Number, (gScreenBMP ? "bmp" : "jpg" ));
				if( !InternalDoesFileExist( FileName ) )
				{
					break;
				}
				Number++;
				if( Number>9999 )
				{
					Number=0;
					break;
				}
			}
//
// Save as .bmp
//
			if( gScreenBMP || !LibJpeg )
			{
				HANDLE hFile = CreateFile( FileName, GENERIC_WRITE, 0,NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL );
				if( hFile!=INVALID_HANDLE_VALUE )
				{
					DWORD temp;
					WriteFile( hFile, Image, ScreenImageSize, &temp, NULL );
					CloseHandle( hFile );

                    bRet = true; // ok?
				}
			}
			else
//
// Save as .jpg
//
			{
				JPEG_CORE_PROPERTIES jcprops;
				if( IJL_OK == _ijlInit(&jcprops) )
				{
					jcprops.DIBWidth       = Environment.screenWidth;
					jcprops.DIBHeight      = Environment.screenHeight;
					jcprops.DIBBytes       = Image;
					jcprops.DIBPadBytes    = 0;
					jcprops.DIBChannels    = 3;
					jcprops.DIBColor       = IJL_BGR;

					jcprops.JPGWidth       = Environment.screenWidth;
					jcprops.JPGHeight      = Environment.screenHeight;
					jcprops.JPGFile        = FileName;
					jcprops.JPGBytes       = NULL;
					jcprops.JPGSizeBytes   = NULL;
					jcprops.JPGChannels    = 3;
					jcprops.JPGColor       = IJL_YCBCR;
					jcprops.JPGSubsampling = IJL_411;		// 4:1:1 subsampling.
					jcprops.jquality       = 90;			// Select "good" image quality

					_ijlWrite( &jcprops, IJL_JFILE_WRITEWHOLEIMAGE) ;

					_ijlFree(&jcprops);

                    bRet = true; // ok?
				}
			}
//
// Remove temp image
//
			free(Image);
		}

    return bRet;
}

//
//
// This the where the control break debugger is updated on the screen
//
//
void UpdateDebugger()
{
	static bool OldDebuggerActive=0;
	static DWORD OldDebuggerMouse=0;
//
// Check for reaching the end of log files
//
	CheckLogFile();
//
// Debugger does not display if you have not updated the display	
//
	if( !InsideBeginScene )
		return;
//
// Do a screen capture?
//
	if( gCaptureScreen && (gCaptureDevice == 0))
	{
		End3DScene();
		BYTE* Image=GrabScreenImage(BackBufferSurface, 0, 0);
        WriteImageAfterGrab(Image, "screen%04d.%s");
		return;
	}
//
// Do not count debugger in statistics? - If not, save them
//
	#ifdef LAB_ONLY
	DWORD DidSave=0;
	if( NoDebuggerStats )
	{
		SaveStatistics();							// Clear statistics after debugger
		DidSave=1;
	}
	#endif
//
// If nothing on display, debugger does not use textures
//
	DebuggerTextures=0;
	DebuggerUsed=0;
//
// Or debugger mode?
//
	if( OldDebuggerMouse || OldDebuggerActive || EnableDisplayInfo || DebuggerActive || gFrameGraph || RenderMode==ExamineTriangleSize || RenderMode==ExamineTriangleSize1 || (gShowLFControls && 1==LogMode) )
	{
//
// If debugger puts anything on the display, ignore regions, update the whole screen in dirty rectangle mode
//
		DebuggerUsed=1;
//
// Save current rendermode
//
		OldRenderMode=RenderMode;
		RenderMode=Normal;
//
// No more spews during debugger
//
		if( NoDebuggerStats )
			SpewSilence=1;
//
// Setup a viewport just in case app has not
//
		DebuggerTextures=1;							// Debugger uses font texture
		gos_SetupViewport( 0,0,0,0, 0.0, 0.0, 1.0, 1.0 );
//
// Make sure whole screen is viewport
//
		if( ViewPortChanged )
		{
			gos_SetViewport( 0, 0, Environment.screenWidth, Environment.screenHeight );
			ViewPortChanged=0;
		}
//
// Setup renderstates for maximum performance
//
		InDebugger=1;
		gos_SetRenderState( gos_State_Texture, 0 );
		gos_SetRenderState( gos_State_Filter, gos_FilterNone );
		gos_SetRenderState( gos_State_ZCompare, 0 );
		gos_SetRenderState( gos_State_ZWrite, 0 );
		gos_SetRenderState( gos_State_AlphaTest, 0 );
		gos_SetRenderState( gos_State_Perspective, 0 );
		gos_SetRenderState( gos_State_Specular, 0 );
		gos_SetRenderState( gos_State_Dither, 0 );
		gos_SetRenderState( gos_State_Clipping, 0 );
		gos_SetRenderState( gos_State_WireframeMode, 0 );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		gos_SetRenderState( gos_State_TextureAddress, gos_TextureWrap );
		gos_SetRenderState( gos_State_Culling, gos_Cull_None );
		gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
		gos_SetRenderState( gos_State_MipMapBias, 0 );
		gos_SetRenderState( gos_State_Fog, 0 );
		gos_SetRenderState( gos_State_MonoEnable, 0 );
		gos_SetRenderState( gos_State_StencilEnable, 0 );
		gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_None );
//
// Setup any texture offset (Font.tga is 128*128)
//
		DebuggerTexelOffset=0.0625f/128.0f;
//
// Read mouse position and buttons
//
		float tempX,tempY;
		gos_GetMouseInfo( &tempX, &tempY, 0,0,&DBWheelDelta, &DBButtons );
		DBMouseX=float2long(tempX*(Environment.screenWidth-1)-0.5f);
		DBMouseY=float2long(tempY*(Environment.screenHeight-1)-0.5f);
		DBMouseMoved=(OldDBMouseX!=DBMouseX || OldDBMouseY!=DBMouseY);
		OldDBMouseX=DBMouseX;
		OldDBMouseY=DBMouseY;
		SelectedHandle=0;
		DBButtons=gos_GetKeyStatus(KEY_LMOUSE) ? 1 : 0;
		DBButtons|=gos_GetKeyStatus(KEY_RMOUSE) ? 2 : 0;
		DBButtons|=gos_GetKeyStatus(KEY_MMOUSE) ? 4 : 0;
//
// Display depends upon mode of debugger
//
		#ifdef LAB_ONLY
		switch( OldRenderMode )
		{
			case ExamineRenderer:
				DoExamineRenderer();					// Show reference rasterizer
				break;

			case ExamineDepth:
				DoExamineDepth();						// Show depth complexity
				break;

			case ExamineScene:
				DoExamineScene();						// Show triangle information
				break;

			case ExamineColor:
				DoExamineColor();						// Show color information
				break;

			case ExamineArea:
				DoExamineArea();						// Show examine area in cursor defined box
				break;

			case ExamineDrawing:
				DoExamineDrawing();						// Show examine drawing scene
				break;

			case ExamineMipmaps:						// Show MipMaps - do not show debugger
				DoExamineMipmaps();
				break;

			case ExamineMipmaps2:						// Show MipMaps - do not show debugger
				DoExamineMipmaps2();
				break;

			default:
				if( gShowLFControls && 1==LogMode )		// If playing back log files, show controls
					ShowLogFileControls();
				if( OldRenderMode==ExamineTriangleSize || OldRenderMode==ExamineTriangleSize1 )
				{
					ShowTriangleSizeInfo();				// Show triangle size graphs
				}
				if( gFrameGraph )
					ShowFrameGraphs();					// If graphs being displayed, show them first
				if( DebuggerActive )
					DoDebugger();						// Now show debugger screen
		}
		#endif
//
// Show mouse cursor
//
		DebuggerMouse=0;
		if((OldRenderMode!=ExamineDepth &&
			OldRenderMode!=ExamineRenderer &&			// If not in these modes
			OldRenderMode!=ExamineDrawing &&
			OldRenderMode!=ExamineMipmaps &&
			OldRenderMode!=ExamineMipmaps2 ) &&
			(DebuggerActive ||							// If debugger active
			(gShowLFControls && 1==LogMode)) )			// Or playing back a log file with controls visible
		{
			DebuggerMouse=1;
		}
//
//
//
		if( DebuggerActive!=OldDebuggerActive || OldDebuggerMouse!=DebuggerMouse )
		{
			UpdateCursor();
			OldDebuggerActive=DebuggerActive;
			OldDebuggerMouse=DebuggerMouse;
		}
//
// Show a mouse cursor (or use windows hardware mouse)
//
		UpdateDebugMouse();
//
// Show any change display mode info
//
		if( InsideBeginScene )
			UpdateDisplayInfo();
//
// Reset stats here to not include debugger in statistics
//
		#ifdef LAB_ONLY
		if( DidSave )
			RestoreStatistics();
		#endif
//
// If requested by the user (with a middle-mouse-click), unload a texture from vram
//
		if( TextureToUnload )
		{
			if( TEXHANDLE_TO_PTR(TextureToUnload)->InVidMem() )
			{
				TEXHANDLE_TO_PTR(TextureToUnload)->FreeVidMem();
			}
			TextureToUnload = 0;
		}

//
// Restore the users renderstates
//
		InDebugger=0;
		RenderMode=OldRenderMode;
//
// Allow spews to happen again
//
		SpewSilence=0;
	}
}




static DWORD* OldEBP;
static DWORD* CurrentEBP;
//
// Walk up the stack (only in debug builds - with stack frames)
//
DWORD WalkUp()
{
	DWORD result=0;
	#ifdef _DEBUG
		if( CurrentEBP )
		{
			result=CurrentEBP[1];
			CurrentEBP=(DWORD*)CurrentEBP[0];
			if( 0!=((DWORD)CurrentEBP&3) || CurrentEBP<OldEBP || (DWORD)CurrentEBP>0x80000000 )
				CurrentEBP=0;
			OldEBP=CurrentEBP;
		}
	#endif
	return result;
}


//
// Walk up a stack, filling an array with addresses of the routines
//
// NumberOfLevels is the size of the array (in DWORDS)
// IgnoreLevels is the number of levels to ignore (so the WalkStack routine does not show up!)
//
void WalkStack( DWORD* RoutineAddresses, DWORD NumberOfLevels, DWORD IgnoreLevels )
{
	_asm	mov CurrentEBP,ebp
	_asm	mov OldEBP,esp

	if( 0!=((DWORD)CurrentEBP&3) || CurrentEBP<OldEBP || (DWORD)CurrentEBP>0x80000000 )
		CurrentEBP=0;

	for( int t0=0; t0<IgnoreLevels; t0++ )
		WalkUp();

	for( t0=0; t0<NumberOfLevels; t0++ )
		RoutineAddresses[t0]=WalkUp();
}

extern char* GetLocationFromAddress( IMAGEHLP_LINE* ImageHlp_pline, char* Buffer, int Address );
//
// Returns an ASCII string describing the address passed
//
char* DecodeAddress( DWORD Address, bool brief )
{
	if( 0==Address )
		return 0;
	static char LineBuffer[256];

	LineBuffer[0]=0;
	
	char BufferSym[MAX_PATH];
	char BufferLoc[MAX_PATH];
	IMAGEHLP_LINE pline;

	char* File=GetSymbolFromAddress( BufferSym, Address );
	char* Line=GetLocationFromAddress( &pline, BufferLoc, Address );

	if( 0==Line )
		return 0;
	if (brief)
		sprintf( LineBuffer, "%s", Line );
	else
		sprintf( LineBuffer, "%s : %s",Line,File );
	return LineBuffer;
}

#ifdef LAB_ONLY
void DoExamineHeaps(DWORD Keypress)
{
	if( !gExaminingHeap && !gExaminingPool )
	{
		// 1. handle wheel change or up/down arrow or pageup/pagedown
   		if (Keypress == KEY_UP)
			DebugHeapStartLine -= 1;
		else
		if (Keypress == KEY_DOWN)
			DebugHeapStartLine += 1;
		else
		if (Keypress == KEY_PRIOR)
			DebugHeapStartLine -= 20;
		else
		if (Keypress == KEY_NEXT)
			DebugHeapStartLine += 20;

		DebugHeapStartLine -= DBWheelDelta;
		if( DebugHeapStartLine<0 )
			DebugHeapStartLine=0;

		// 2. handle left button click
		if (!DebounceMouse && DBButtons&1)
		{
			// 1. which line did we click on
			int lineNumber = ((DBMouseY- DbTopY)/ 8)-DebugHeapStart;

			// 2. expand/contract heap, if valid line
			if (lineNumber >=0 && lineNumber < (DebugHeapEnd-DebugHeapStart) && DebugHeapOnLine[lineNumber])
			{
				if (DebugHeapOnLine[lineNumber]->pChild == NULL)
				{
					if (DebugHeapOnLine[lineNumber]->bExamined)
					{
						DebugHeapOnLine[lineNumber]->bExamined = false;
					}
					else
					{
						DebugHeapOnLine[lineNumber]->bExamined = true;
						if (Environment.memoryTraceLevel != 0)
						{
#ifdef _DEBUG
							gExaminingHeap = DebugHeapOnLine[lineNumber];
							gExaminingHeapItem = 0;
#endif
						}
					}
				}
				else
					DebugHeapOnLine[lineNumber]->bExpanded = !DebugHeapOnLine[lineNumber]->bExpanded;
			}
			// 1. which line did we click on
			lineNumber = ((DBMouseY- DbTopY)/ 8)-DebugBlockHeapStart;

			// 2. expand/contract heap, if valid line
			if( ShowBlockInfo && lineNumber >=0 && lineNumber<MemoryPools )
			{
				if (Environment.memoryTraceLevel != 0)
				{
#ifdef _DEBUG
					gExaminingPool = gMemoryPool[lineNumber];
					gExaminingHeapItem = 0;
#endif
				}
			}
			DebounceMouse = 0;
		}
	}
	else
	{
		if (!DebounceMouse && DBButtons&1)
		{
			// 1. which line did we click on
			int lineNumber = ((DBMouseY- DbTopY)/ 8);

			if (lineNumber >= 38 && lineNumber <= 40)
			{
				gExaminingHeap = 0;
				gExaminingPool = 0;
			}
			DebounceMouse = 0;
		}

		gExaminingHeapItem -= DBWheelDelta;

		if (Keypress == KEY_PRIOR)
			gExaminingHeapItem -= (34/Environment.memoryTraceLevel);
		else
		if (Keypress == KEY_NEXT)
			gExaminingHeapItem += 34/Environment.memoryTraceLevel;
		else
   		if (Keypress == KEY_UP)
			gExaminingHeapItem -= 1;
		else
		if (Keypress == KEY_DOWN)
			gExaminingHeapItem += 1;

		if (gExaminingHeapItem < 0)
			gExaminingHeapItem = 0;
	}
}

void DoSpewInterface(char ascii)
{

	if (!DebounceMouse && DBButtons&1)
	{
		// 1. which line did we click on
		int lineNumber = ((DBMouseY- DbTopY)/ 8);
		DebounceMouse = 0;

		// a click occurred on the filter line. if filter doesn't have focus
		// give it focus. if it does already, treat the click as a KEY_RETURN
		// (see below).
		if (lineNumber > 8 && lineNumber < 11)
			spewInterfaceFocus = true;
		else
		{
			if (spewInterfaceFocus == true)
				ascii = KEY_RETURN;
			// now, we've also got to check if they've hit a spew 'button'
			if (CheckSpewButton((DBMouseX-DbTopX) / 8, (DBMouseY-DbTopY)/8))
			{
				ascii = KEY_RETURN;
				spewInterfaceFocus = true;
			}
		}
	}

	if (spewInterfaceFocus)
	{
		if (ascii)
		{
			// keep the cursor lit when someone's entering data....
			spewCursorTimer = gos_GetElapsedTime();

			if (ascii >= 32 && ascii < 128)
			{
				// don't let this get to big for the static buffer.
				if (strlen((char *) &SpewDefault) < 256)
					((char*)(&SpewDefault))[strlen((char *) &SpewDefault)] = (char) ascii;
			}
		
			if (ascii == KEY_BACK)
			{
				// removing characters; limit at no characters so memory
				// is corrupted.
				if (strlen((char *) &SpewDefault))
					((char*)(&SpewDefault))[strlen((char *) &SpewDefault)-1 ] = '\0';
			}
			if (ascii == KEY_RETURN)
			{
				// the user hit return. recompose the SpewList with any changes they
				// may have made and relinquish focus.
				char UserName[32+1];
				strncpy( UserName, gosGetUserName(), 31 );
				strcpy( SpewList, UserName );
				strcat( SpewList, " " );
				strcat( SpewList, SpewDefault );
				strcat( SpewList, " " );
				if( Environment.spew )
					strcat( SpewList, Environment.spew);
				gosASSERT( strlen(SpewList) < 512 );
				spewInterfaceFocus = false;
			}
		}
	}
}

bool CheckSpewButton(DWORD x, DWORD y)
{
	char tmp[32];
	// trivially reject anything outside main panel...
	if (x < 13 || x > 55 || y < 5 || y > 7 )
		return false;

	if ( x > 14 && x < 13+10)
	{
		// we're in the 1st column
		switch(y)
		{
		case 5:
			strcpy(tmp,GROUP_DIRECTDRAW);
			break;
		case 6:
			strcpy(tmp,GROUP_FILES);
			break;
		case 7:
			strcpy(tmp,GROUP_DIRECTSOUND);
			break;
		}
	}
	else
	if ( x > 24 && x < 24+10)
	{
		// we're in the 2nd column
		switch(y)
		{
		case 5:
			strcpy(tmp,GROUP_DIRECT3D);
			break;
		case 6:
			strcpy(tmp,GROUP_DIRECTINPUT);
			break;
		case 7:
			strcpy(tmp,GROUP_TEXTURE);
			break;
		}
	}
	else
	if ( x > 35 && x < 35+10)
	{
		// we're in the 3rd column
		switch(y)
		{
		case 5:
			strcpy(tmp,GROUP_DIRECTX);
			break;
		case 6:
			strcpy(tmp,GROUP_MEMORY);
			break;
		case 7:
			strcpy(tmp,GROUP_WINDOWSMESSAGES);
			break;
		}
	}
	else
	if ( x > 45 && x < 45+10)
	{
		// we're in the 4th column
		switch(y)
		{
		case 5:
			strcpy(tmp,GROUP_DIRECTPLAY);
			break;
		case 6:
			strcpy(tmp,GROUP_NETWORK);
			break;
		case 7:
			strcpy(tmp,GROUP_DIRECTSHOW);
			break;
		}
	}
	char tmp2[512];
	char tmp3[32];
//	strncpy( tmp2, gosGetUserName(), 31 );
	strcpy(tmp2, (char*)&SpewDefault);
	strcpy(tmp3, tmp);
	char * where = strstr(strlwr(tmp2),strlwr(tmp3));
	if (where)
	{
		// it's in the list, let's extricate it.
		if (where != tmp2 && *(where-1) == ' ')
		{
			strcpy((char*)&SpewDefault + ((where-1)-tmp2),(char*)&SpewDefault + (where-tmp2)+strlen(tmp3));
		}
		else
			strcpy((char*)&SpewDefault + (where-tmp2),(char*)&SpewDefault + (where-tmp2)+strlen(tmp3));
	}
	else
	{
		// it's not in the list, go ahead and add it...
		strcat((char *)&SpewDefault, " ");
		strcat((char *)&SpewDefault, tmp);
	}
	return true;
}
#endif











