#include "pch.hpp"
//===========================================================================//
// File:	 Debuggui.cpp													 //
// Project:  GameOS					                                         //
// Contents: Debugger user interface										 //
//---------------------------------------------------------------------------//
// 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 "Texture Manager.hpp"
#include "ErrorHandler.hpp"
#include "Profiler.hpp"
#include "DXRasterizer.hpp"
#include "3DRasterizer.hpp"
#include "DirectX.hpp"
#include "Windows.hpp"
#include "Sound Renderer.hpp"
#include "Music.hpp"
#include "Font3D.hpp"
#include "MemoryManager.hpp"
#include "Perf.h"
#include "Net_Header.hpp"
#include "FileIO.hpp"
#include "DirectXDebugging.hpp"
#include "ControlManager.hpp"


FrameGraphMode GraphMode=Graph_Chart;



extern DWORD StartupMemory;
extern DWORD PDBMemory;


// formatted numbers;
char* ElevenDigitNumber( DWORD Value );

extern ControlManagerDataStruct g_CMData;
extern DWORD CurrentRefreshRate;
extern DWORD gMemoryStackWalkLevel;
extern DWORD SystemMemoryAllocs, SystemMemoryAllocated;
extern int AgressiveManager;

// for memory tracking
extern HGOSHEAP DefaultHeap;
DWORD gHeapOnLineIndex = 0;
HGOSHEAP DebugHeapOnLine[32];
signed int DebugHeapStartLine = 0;
DWORD DebugStartLineCounter = 0;
extern LARGEBLOCKHEADER * gMemoryBigAllocList;
extern DWORD MemoryAllocs,MemoryFrees;
extern HGOSHEAP gExaminingHeap;
extern MEMORYPOOL* gExaminingPool;
extern int gExaminingHeapItem;

extern MEMORYPOOL * gMemoryPool[MemoryPools];
void DisplayTextureMemory( DWORD ExamineUsed );
void DebugDisplayHeap(gos_Heap * pHeap,DWORD indent=0);
DWORD PerfCounterSelected=-1;
int PerfYStart=0,PerfYEnd=0;
DWORD DebugBlockHeapStart;

extern char SpewList[];
extern char SpewDefault[];
double spewCursorTimer;
bool spewInterfaceFocus;
extern int HasAntiAlias;
extern int TextureScroll;

TextureHeap* pTextureHeap=0;

extern DWORD VirtualMemoryAllocated;
extern DWORD MemoryUsed;
extern DWORD MemoryAllocations;

extern char DisplayInfoText[128];

int DebugHeapStart,DebugHeapEnd;
int TD_TextureX;
int TD_TextureY;
int TD_Width;
gos_VERTEX tv[4];

#define MAX(x,y,z) (((x)>(y)?(x)-(y):0) > (z) ? ((x)>(y)?(x)-(y):0) : (z))

int MaxPS256 = 0;
int MaxPK256 = 0;
int MaxPA256 = 0;
int MaxPS128 = 0;
int MaxPK128 = 0;
int MaxPA128 = 0;
int MaxPS64 = 0;
int MaxPK64 = 0;
int MaxPA64 = 0;
int MaxPS32 = 0;
int MaxPK32 = 0;
int MaxPA32 = 0;
int MaxPS16 = 0;
int MaxPK16 = 0;
int MaxPA16 = 0;


void ResetTextureStats()
{
	MaxPS256 = 0;
	MaxPK256 = 0;
	MaxPA256 = 0;
	MaxPS128 = 0;
	MaxPK128 = 0;
	MaxPA128 = 0;
	MaxPS64 = 0;
	MaxPK64 = 0;
	MaxPA64 = 0;
	MaxPS32 = 0;
	MaxPK32 = 0;
	MaxPA32 = 0;
	MaxPS16 = 0;
	MaxPK16 = 0;
	MaxPA16 = 0;
}


//
// Vertices for text
//
gos_VERTEX pTextVertices[80];
DWORD TextVertex;
DWORD TextColor=0;
//
// Current debug display
//
EDbgScreen DebugDisplay;
//
// Debugger window variables
//
int DbTopX,DbTopY;
DWORD DoubleChr=0;
//
// Top of statistics window
//
DWORD TopStatistics=0;

//
// Show memory allocations in the heaps
//
bool ShowAllocations=0;
bool ShowBlockInfo=0;

//
// Print Character variables
//
int DbChrX,DbChrY;			// Current character x,y
int CurrentX,CurrentY;		// Current pixel position
int StartX,StartY;			// Current start of line
int DbMinX,DbMaxX,DbMaxY;			// Max X,Y line allowed and DbMinX (nothing printed before it)

// Sound Renderer data
extern SoundRendererData SoundRenderer;
extern LinkedList<SoundResource *> * g_soundResources;
extern HGOSMUSIC g_activeMusicList;
signed long gStartingChannel = 0;
extern float gSoundSpatDebugZoom;

// Control manager data
signed long gWhichCMDevice = 0;
gosJoystick_Info gCMDeviceInfo;



//
// Find total memory committed
//
DWORD GetVMSize()
{
	#ifdef LAB_ONLY
	__try
	{
		Sleep(10);									// Make system settle down a bit
		MEMORY_BASIC_INFORMATION mbi;
		void* BaseAddress=0;
		DWORD Memory=0;

		while( (int)BaseAddress<0x7fff0000 )
		{
			if( 0==VirtualQuery( BaseAddress,&mbi,sizeof(mbi) ) )
				break;

			if( mbi.State==MEM_COMMIT )
			{
				Memory+=mbi.RegionSize;
			}
			
			BaseAddress=(char*)BaseAddress+mbi.RegionSize;
		}
		return Memory;
	}
	__except(1)
	{
		return 0;
	}
	#else
		return 0;
	#endif
}


//
// Find total memory committed
//
void GetnVidiaCacheSize( DWORD &Surfaces, DWORD &Memory )
{
	#ifdef LAB_ONLY
	__try
	{
		Sleep(10);									// Make system settle down a bit
		MEMORY_BASIC_INFORMATION mbi;
		void* BaseAddress=0;
		Surfaces=0;
		Memory=0;

		while( (int)BaseAddress<0x7fff0000 )
		{
			if( 0==VirtualQuery( BaseAddress,&mbi,sizeof(mbi) ) )
				break;

			if( mbi.State==MEM_COMMIT && mbi.Protect==PAGE_READWRITE )
			{
				if( *(DWORD*)BaseAddress==0x78494447 )
				{
					Surfaces++;
					Memory+=mbi.RegionSize;
				}
			}
			
			BaseAddress=(char*)BaseAddress+mbi.RegionSize;
		}
	}
	__except(1)
	{
		return;
	}
	#else
		return;
	#endif
}



extern DWORD DebuggerMouse;


//
// Draw debugger cursor
//
void UpdateDebugMouse()
{
	if( (gHardwareMouse || DebuggerMouse) && ((Compatibility3D&BadMouse) || Environment.fullScreen) )
	{
		gos_SetRenderState( gos_State_Clipping, 0 );
		gos_SetRenderState( gos_State_Texture, FontTexture );
		if( HasAlphaTest )
		{
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendDecal );
			gos_SetRenderState( gos_State_AlphaTest, 1 );
		}
		else
		{
			gos_SetRenderState( gos_State_AlphaTest, 0 );
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
		}

		gos_VERTEX pVertices[4];

		pVertices[0].x		= (float)DBMouseX;
		pVertices[0].y		= (float)DBMouseY;
		pVertices[0].argb	= 0xffffffff;
		pVertices[0].u		= 96.0f/128.0f;
		pVertices[0].v		= 48.0f/128.0f;
		pVertices[0].z		= 0.0f;
		pVertices[0].rhw	= 1.0f;

		pVertices[1].x		= (float)DBMouseX;
		pVertices[1].y		= (float)(DBMouseY+20);
		pVertices[1].argb	= 0xffffffff;
		pVertices[1].u		= 96.0f/128.0f;
		pVertices[1].v		= 68.0f/128.0f;
		pVertices[1].z		= 0.0f;
		pVertices[1].rhw	= 1.0f;

		pVertices[2].x		= (float)(DBMouseX+12);
		pVertices[2].y		= (float)(DBMouseY+20);
		pVertices[2].argb	= 0xffffffff;
		pVertices[2].u		= 108.0f/128.0f;
		pVertices[2].v		= 68.0f/128.0f;
		pVertices[2].z		= 0.0f;
		pVertices[2].rhw	= 1.0f;

		pVertices[3].x		= (float)(DBMouseX+12);
		pVertices[3].y		= (float)DBMouseY;
		pVertices[3].argb	= 0xffffffff;
		pVertices[3].u		= 108.0f/128.0f;
		pVertices[3].v		= 48.0f/128.0f;
		pVertices[3].z		= 0.0f;
		pVertices[3].rhw	= 1.0f;

		gos_ClipDrawQuad( pVertices );
	}
}



#ifdef LAB_ONLY

//
// Take a pathname and return just the filename
char* GetFileTrunc(char * src)
{
	static char gfoBuffer[32];
	if (src == NULL)
		return NULL;
	if( strlen(src)>25 )
	{
		strcpy( gfoBuffer, ".." );
		strcat( gfoBuffer,src+strlen(src)-23 );
	}
	else
	{
		strcpy( gfoBuffer,src );
	}

	return gfoBuffer;
}

//
// Returns an 11 digit number, right justified with commas every 1,000
//
char* ElevenDigitNumber( DWORD Value )
{
	static char Buffer[20];
	DWORD Digits=0,Length=0;
	memset( Buffer, 32, sizeof(Buffer) );
//
// Write out value from lowest digit first
//
	do
	{
		Buffer[Length++]=(char)(Value%10 + '0');
		Digits++;
		if( Digits==3 )
		{
			Buffer[Length++]=',';
			Digits=0;
		}
		Value/=10;
	} while (Value!=0);
//
// If last digit is a comma, remove it.
//
	if( Buffer[Length-1]==',' )
		Buffer[Length-1]=' ';
//
// Reverse buffer and copy it into 11 digit buffer
//
	Buffer[11]=0;
	_strrev( Buffer );
	return Buffer;
}

#ifdef _DEBUG
//
// Display information about the current pool
//
void DisplayPoolItems( MEMORYPOOL* pPool )
{
	char info[256];
	int linePos = 4;
	DWORD counter = 0;

	while(pPool)
	{
		if( pPool->FreeBlockPtr )
		{
			if( pPool->wBlockSize<256 )
			{
				SMALLPOOLBLOCK* pBlock = (SMALLPOOLBLOCK*)pPool->pInfoBlocks;
				BYTE* memory = pPool->pMemoryPool;
				bool Ignore;
							
				for( int t0=0; t0<pPool->wTotalBlocks; t0++ )
				{
					Ignore=0;
					
					if( AgressiveManager )
					{
						DWORD startBlockNumber=( (int)memory - (int)pPool ) / 4096;
						DWORD endBlockNumber=( (int)memory - (int)pPool + pPool->wBlockSize ) / 4096;

						if( 0==pPool->AllocCount[startBlockNumber] || 0==pPool->AllocCount[endBlockNumber] )
							Ignore=1;									// Block is decomitted, don't check it.
					}

					if( !Ignore )
					{
						DWORD Size=pBlock->Size;
						BYTE Heap=*memory;

						if( Heap )
						{
							if( counter >= gExaminingHeapItem )
							{
								sprintf(info, "Bytes: %d / %d  Heap=\"%s\"\n", Size, pPool->wBlockSize, HeapList[Heap]->Name );
								DrawText(0x00ff00, info);
								linePos++;
								if (linePos > 34)
									goto printpoolback;
								sprintf(info, "0x%x: ", (DWORD) memory+1 );
								DrawText(0xffffff, info);
								sprintf(info, "%s\n", DecodeAddress(pBlock->pContext[0], true));
								if (strlen(info) > 64)
								{
									DrawText(0xffffff, "...");
									DrawText(0xffffff, info+strlen(info)-64);
								}
								else
									DrawText(0xffffff, info);
								linePos++;
								if (linePos > 34)
									goto printpoolback;
								for(int i=1; i < Environment.memoryTraceLevel; i++)
								{
									char * decode = DecodeAddress(pBlock->pContext[i], true);
									if (decode)
									{
										DrawText(0x7f7f7f, "..........  ");
										sprintf(info, "%s\n", decode);
										if (strlen(info) > 62)
										{
											DrawText(0x7f7f7f, "...");
											DrawText(0x7f7f7f, info+strlen(info)-62);
										}
										else
											DrawText(0x7f7f7f, info);
									}
									else
									DrawText(0x7f7f7f, "..........\n");
									linePos++;
									if (linePos > 34)
										goto printpoolback;
								}
								DrawText(0xffffff,"\n");
								linePos++;
								if (linePos > 34)
									goto printpoolback;
							}
							counter++;
						}
					}
					pBlock = (SMALLPOOLBLOCK*)((int)pBlock + sizeof(SMALLPOOLBLOCK) + gMemoryStackWalkLevel*4);
					memory += pPool->wBlockSize;
				}
			}
			else
			{
				POOLBLOCK* pBlock = pPool->pInfoBlocks;
				BYTE* memory = pPool->pMemoryPool;
				bool Ignore;

				for( int t0=0; t0<pPool->wTotalBlocks; t0++ )
				{
					Ignore=0;
					
					if( AgressiveManager )
					{
						DWORD startBlockNumber=( (int)memory - (int)pPool ) / 4096;
						DWORD endBlockNumber=( (int)memory - (int)pPool + pPool->wBlockSize ) / 4096;

						if( 0==pPool->AllocCount[startBlockNumber] || 0==pPool->AllocCount[endBlockNumber] )
							Ignore=1;									// Block is decomitted, don't check it.
					}

					if( !Ignore )
					{
						DWORD Size=pBlock->Size;
						BYTE Heap=*memory;
				
						if( Heap )
						{
							if( counter >= gExaminingHeapItem )
							{
								sprintf(info, "Bytes: %d / %d  Heap=\"%s\"\n", Size, pPool->wBlockSize, HeapList[Heap]->Name );
								DrawText(0x00ff00, info);
								linePos++;
								if (linePos > 34)
									goto printpoolback;
								sprintf(info, "0x%x: ", (DWORD) memory+1 );
								DrawText(0xffffff, info);
								sprintf(info, "%s\n", DecodeAddress(pBlock->pContext[0], true));
								if (strlen(info) > 64)
								{
									DrawText(0xffffff, "...");
									DrawText(0xffffff, info+strlen(info)-64);
								}
								else
									DrawText(0xffffff, info);
								linePos++;
								if (linePos > 34)
									goto printpoolback;
								for(int i=1; i < Environment.memoryTraceLevel; i++)
								{
									char * decode = DecodeAddress(pBlock->pContext[i], true);
									if (decode)
									{
										DrawText(0x7f7f7f, "..........  ");
										sprintf(info, "%s\n", decode);
										if (strlen(info) > 62)
										{
											DrawText(0x7f7f7f, "...");
											DrawText(0x7f7f7f, info+strlen(info)-62);
										}
										else
											DrawText(0x7f7f7f, info);
									}
									else
									DrawText(0x7f7f7f, "..........\n");
									linePos++;
									if (linePos > 34)
										goto printpoolback;
								}
								DrawText(0xffffff,"\n");
								linePos++;
								if (linePos > 34)
									goto printpoolback;
							}
							counter++;
						}
					}
					pBlock = (POOLBLOCK*)((int)pBlock + sizeof(POOLBLOCK) + gMemoryStackWalkLevel*4);
					memory += pPool->wBlockSize;
				}
			}
		}
		pPool = pPool->pNext;
	}

printpoolback:
	//
	// Print the "back" button
	//
	if (counter <= gExaminingHeapItem)
		gExaminingHeapItem = counter-1;
	CurrentY = DbTopY+ DbMaxY*8;
	DrawText(0x00ff00, "						(CLICK HERE TO GO TO PREVIOUS SCREEN)");
}




//
// Display information about the current heap
//
void DisplayHeapItems(HGOSHEAP pHeap)
{
	char info[256];
	int linePos = 4;
	DWORD counter = 0;
	LARGEBLOCKHEADER* ba = gMemoryBigAllocList;
//
// Iterate through small block memory pools
//
	for(int j=0; j < MemoryPools; j++)
	{
		MEMORYPOOL* pPool = gMemoryPool[j];
		while(pPool)
		{
			if( pPool->FreeBlockPtr )
			{
				if( pPool->wBlockSize < 256 )
				{
					SMALLPOOLBLOCK* pBlock = (SMALLPOOLBLOCK*)pPool->pInfoBlocks;
					BYTE* memory = pPool->pMemoryPool;
					bool Ignore;
				
					for( int t0=0; t0<pPool->wTotalBlocks; t0++ )
					{
						Ignore=0;
						
						if( AgressiveManager )
						{
							DWORD startBlockNumber=( (int)memory - (int)pPool ) / 4096;
							DWORD endBlockNumber=( (int)memory - (int)pPool + pPool->wBlockSize ) / 4096;

							if( 0==pPool->AllocCount[startBlockNumber] || 0==pPool->AllocCount[endBlockNumber] )
								Ignore=1;									// Block is decomitted, don't check it.
						}

						if( !Ignore )
						{
							DWORD Size=pBlock->Size;
							BYTE Heap=*memory;

							if( HeapList[Heap] == pHeap )
							{
								if( counter >= gExaminingHeapItem )
								{
									sprintf(info, "Bytes: %d / %d\n", Size, pPool->wBlockSize);
									DrawText(0x00ff00, info);
									linePos++;
									if (linePos > 34)
										goto printback;
									sprintf(info, "0x%x: ", (DWORD) memory+1 );
									DrawText(0xffffff, info);
									sprintf(info, "%s\n", DecodeAddress(pBlock->pContext[0], true));
									if (strlen(info) > 64)
									{
										DrawText(0xffffff, "...");
										DrawText(0xffffff, info+strlen(info)-64);
									}
									else
										DrawText(0xffffff, info);
									linePos++;
									if (linePos > 34)
										goto printback;
									for(int i=1; i < Environment.memoryTraceLevel; i++)
									{
										char * decode = DecodeAddress(pBlock->pContext[i], true);
										if (decode)
										{
											DrawText(0x7f7f7f, "..........  ");
											sprintf(info, "%s\n", decode);
											if (strlen(info) > 62)
											{
												DrawText(0x7f7f7f, "...");
												DrawText(0x7f7f7f, info+strlen(info)-62);
											}
											else
												DrawText(0x7f7f7f, info);
										}
										else
										DrawText(0x7f7f7f, "..........\n");
										linePos++;
										if (linePos > 34)
											goto printback;
									}
									DrawText(0xffffff,"\n");
									linePos++;
									if (linePos > 34)
										goto printback;
								}
								counter++;
							}
						}
						pBlock = (SMALLPOOLBLOCK*)((int)pBlock + sizeof(SMALLPOOLBLOCK) + gMemoryStackWalkLevel*4);
						memory += pPool->wBlockSize;
					}
				}
				else
				{
					POOLBLOCK* pBlock = pPool->pInfoBlocks;
					BYTE* memory = pPool->pMemoryPool;
					bool Ignore;
									
					for( int t0=0; t0<pPool->wTotalBlocks; t0++ )
					{
						Ignore=0;
						
						if( AgressiveManager )
						{
							DWORD startBlockNumber=( (int)memory - (int)pPool ) / 4096;
							DWORD endBlockNumber=( (int)memory - (int)pPool + pPool->wBlockSize ) / 4096;

							if( 0==pPool->AllocCount[startBlockNumber] || 0==pPool->AllocCount[endBlockNumber] )
								Ignore=1;									// Block is decomitted, don't check it.
						}

						if( !Ignore )
						{
							DWORD Size=pBlock->Size;
							BYTE Heap=*memory;

							if( HeapList[Heap] == pHeap )
							{
								if( counter >= gExaminingHeapItem )
								{
									sprintf(info, "Bytes: %d / %d\n", Size, pPool->wBlockSize);
									DrawText(0x00ff00, info);
									linePos++;
									if (linePos > 34)
										goto printback;
									sprintf(info, "0x%x: ", (DWORD) memory+1 );
									DrawText(0xffffff, info);
									sprintf(info, "%s\n", DecodeAddress(pBlock->pContext[0], true));
									if (strlen(info) > 64)
									{
										DrawText(0xffffff, "...");
										DrawText(0xffffff, info+strlen(info)-64);
									}
									else
										DrawText(0xffffff, info);
									linePos++;
									if (linePos > 34)
										goto printback;
									for(int i=1; i < Environment.memoryTraceLevel; i++)
									{
										char * decode = DecodeAddress(pBlock->pContext[i], true);
										if (decode)
										{
											DrawText(0x7f7f7f, "..........  ");
											sprintf(info, "%s\n", decode);
											if (strlen(info) > 62)
											{
												DrawText(0x7f7f7f, "...");
												DrawText(0x7f7f7f, info+strlen(info)-62);
											}
											else
												DrawText(0x7f7f7f, info);
										}
										else
										DrawText(0x7f7f7f, "..........\n");
										linePos++;
										if (linePos > 34)
											goto printback;
									}
									DrawText(0xffffff,"\n");
									linePos++;
									if (linePos > 34)
										goto printback;
								}
								counter++;
							}
						}
						pBlock = (POOLBLOCK*)((int)pBlock + sizeof(POOLBLOCK) + gMemoryStackWalkLevel*4);
						memory += pPool->wBlockSize;
					}
				}
			}
			pPool = pPool->pNext;
		}
	}
//
// Iterate through large blocks
//
	while (ba)
	{
		if( HeapList[ba->pLargeBlockInfo->Heap] == pHeap )
		{
			if ( counter >= gExaminingHeapItem )
			{
				sprintf(info, "Bytes: %d\n", ba->pLargeBlockInfo->Size );
				DrawText(0xffffff, info);
				linePos++;
				if (linePos > 34)
					goto printback;
				sprintf(info, "0x%x: ", &ba[1] );
				DrawText(0xffffff, info);
				sprintf(info, "%s\n", DecodeAddress(ba->pLargeBlockInfo->pContext[0], true));
				if (strlen(info) > 64)
				{
					DrawText(0xffffff, "...");
					DrawText(0xffffff, info+strlen(info)-64);
				}
				else
					DrawText(0xffffff, info);
				linePos++;
				if (linePos > 34)
					goto printback;
				for(int i=1; i < Environment.memoryTraceLevel; i++)
				{
					char * decode = DecodeAddress(ba->pLargeBlockInfo->pContext[i], true);
					if (decode)
					{
						DrawText(0x7f7f7f, "..........  ");
						sprintf(info, "%s\n", decode);
						if (strlen(info) > 62)
						{
							DrawText(0x7f7f7f, "...");
							DrawText(0x7f7f7f, info+strlen(info)-62);
						}
						else
							DrawText(0x7f7f7f, info);
					}
					else
						DrawText(0x7f7f7f, "..........\n" );
					linePos++;
					if (linePos > 34)
						goto printback;
				}
				DrawText(0xffffff,"\n");
				linePos++;
				if (linePos > 34)
					goto printback;
			}
			counter++;
		}
		ba = ba->pLargeBlockInfo->pNext;
	}

printback:
	//
	// Print the "back" button
	//
	if (counter <= gExaminingHeapItem)
		gExaminingHeapItem = counter-1;
	CurrentY = DbTopY+ DbMaxY*8;
	DrawText(0x00ff00, "						(CLICK HERE TO GO TO PREVIOUS SCREEN)");
}
#endif

void DebugDisplayHeap(gos_Heap * pHeap, DWORD indent)
{
	if (DebugHeapStartLine > DebugStartLineCounter)
	{
		DebugStartLineCounter++;
		indent++;
		gos_Heap* pChild= pHeap->pChild;
		if (pHeap->bExpanded)
		{
			while(pChild)
			{
				DebugDisplayHeap(pChild, indent);
				pChild = pChild->pNext;
			}
		}
		return;
	}

	if (gHeapOnLineIndex > 32)
		return;
	else
		DebugHeapOnLine[gHeapOnLineIndex] = pHeap;
	// show roll-up data
	char Buffer[256];
	char Name[32];
	memset(Name,32,32);

	DWORD color;
	char specChar[3];
	specChar[2] = 0;

	
	if (pHeap->pParent && pHeap->pParent->pNext)
	{
		for(int i=0; i < indent; i++)
			DrawText(0x808080,"| ");
	}
	else
	{
		for(int i=0; i < indent; i++)
			DrawText(0x808080,"  ");
	}

	if (pHeap->pChild)
	{
		color = 0xffffff;
		if (pHeap->bExpanded)
		{
			color = 0x808080;
			specChar[0] = (unsigned char) 132;		// '-'
		}
		else				 
			specChar[0] = (unsigned char) 131;	   //  '+'
		specChar[1] = (unsigned char) 130;
		DrawText(color, specChar);
	}
	else
	{
		color = 0xffffff;
		if (pHeap->pNext)
			specChar[0] = (unsigned char) 128;   // t-intersection
		else
			specChar[0] = (unsigned char) 129;   // end cap
		specChar[1] = (unsigned char) 130;
		DrawText(color,specChar);
	}

	memset(Name,32,32);
		
	strncpy( Name+(indent*2)+2, pHeap->Name, 20-(indent*2) );
	Name[22] = 0;
	
	CurrentX = StartX;
	DbChrX = 0;

	if( ShowAllocations )
	{
		sprintf( Buffer, "%-22s   % 7d % 9d  % 7d % 9d  % 7d % 9d", 
			Name, 
			pHeap->TotalAllocations-pHeap->LargeAllocations-pHeap->DXAllocations,
			pHeap->BytesAllocated-pHeap->LargeAllocated-pHeap->DXAllocated,
			pHeap->LargeAllocations,
			pHeap->LargeAllocated,
			pHeap->DXAllocations,
			pHeap->DXAllocated );
		DrawText( color, Buffer );
	}
	else
	{
		sprintf( Buffer, "%-22s%8d %8d %9d ", 
			Name, 
			pHeap->BytesAllocatedThisLoop,
			pHeap->BytesFreedThisLoop,
			pHeap->BytesAllocated - pHeap->TotalAllocatedLastLoop );
		DrawText( color, Buffer );
	}
	
	DrawText( color, ElevenDigitNumber(pHeap->BytesAllocated) );
	DrawText( color, " " );
	DrawText( color, ElevenDigitNumber(pHeap->PeakSize) );
	
	if ( pHeap->MaximumSize )
	{
		sprintf( Buffer, "%5.1f\n", 100.0f * ( (float) pHeap->BytesAllocated / (float) pHeap->MaximumSize) );
		DrawText( color, Buffer ); 
	}
	else
		DrawText( color,"\n");

	indent++;
	gHeapOnLineIndex++;

	gos_Heap* pChild= pHeap->pChild;

	if (pHeap->bExpanded)
	{
		while(pChild)
		{
			DebugDisplayHeap(pChild, indent);
			pChild = pChild->pNext;
		}
	}
}

#endif







void DrawChr( char Chr )
{
	static Brighten=0;
//
// Check for brighten next charcter
//
	if( Chr==1 )
	{
		Brighten=1;
		return;
	}
//
// Check for TAB to next four
//
	if( Chr==9 )
	{
		do
		{
			DrawChr( ' ' );
		} while( DbChrX&3 );
		return;
	}
//
// Check for CR
//
	if( Chr=='\n' )
	{
		DbChrX=0;
		DbChrY++;
		CurrentX=StartX;
		if( DbChrY<DbMaxY )
		{
			CurrentY=StartY+(DoubleChr ? 16 : 8);
			StartY+=(DoubleChr ? 16 : 8);
		}
		return;
	}
//
// Disable word wrapping in the debugger (must implement scroll left/right)
//
//	if (DbChrX>=DbMaxX)
//	{
//		DbChrX=0;
//		DbChrY++;
//		CurrentX=StartX;
//		if( DbChrY<DbMaxY )
//		{
//			CurrentY=StartY+(DoubleChr ? 16 : 8);
//			StartY+=(DoubleChr ? 16 : 8);
//		}
//	}

	if( DbChrY<DbMaxY && DbChrX<DbMaxX && DbChrX>=DbMinX )
	{
		if( Chr!=32 )
		{
			if( ((unsigned char)Chr) <32 || ((unsigned char) Chr)>=134 )
				Chr='.';
//
// Work out texture coords for font
//
			float FontU=((Chr&15)*6.0f+1.0f)*(1.0f/128.0f);
			float FontV=(48.0f+8.0f*((Chr-32)>>4))*(1.0f/128.0f);
			float FontU1=FontU+(5.0f/128.0f);
			float FontV1=FontV+(8.0f/128.0f);
//
// Make a quad
//
			pTextVertices[TextVertex+0].rhw = 1;
			pTextVertices[TextVertex+1].rhw = 1;
			pTextVertices[TextVertex+2].rhw = 1;
			pTextVertices[TextVertex+3].rhw = 1;

			pTextVertices[TextVertex+0].argb = TextColor+DebuggerAlpha + (Brighten ? 0x3f3f3f : 0);
			pTextVertices[TextVertex+1].argb = TextColor+DebuggerAlpha + (Brighten ? 0x3f3f3f : 0);
			pTextVertices[TextVertex+2].argb = TextColor+DebuggerAlpha + (Brighten ? 0x3f3f3f : 0);
			pTextVertices[TextVertex+3].argb = TextColor+DebuggerAlpha + (Brighten ? 0x3f3f3f : 0);

			pTextVertices[TextVertex+0].x = (float)(CurrentX);
			pTextVertices[TextVertex+0].y = (float)(CurrentY);
			pTextVertices[TextVertex+0].u = FontU;
			pTextVertices[TextVertex+0].v = FontV;

			pTextVertices[TextVertex+1].x = (float)(CurrentX);
			pTextVertices[TextVertex+1].y = (float)(CurrentY+(DoubleChr ? 16 : 8));
			pTextVertices[TextVertex+1].u = FontU;
			pTextVertices[TextVertex+1].v = FontV1;

			pTextVertices[TextVertex+2].x = (float)(CurrentX+(DoubleChr ? 10 : 5));
			pTextVertices[TextVertex+2].y = (float)(CurrentY+(DoubleChr ? 16 : 8));
			pTextVertices[TextVertex+2].u = FontU1;
			pTextVertices[TextVertex+2].v = FontV1;

			pTextVertices[TextVertex+3].x = (float)(CurrentX+(DoubleChr ? 10 : 5));
			pTextVertices[TextVertex+3].y = (float)(CurrentY);
			pTextVertices[TextVertex+3].u = FontU1;
			pTextVertices[TextVertex+3].v = FontV;
//
// Texel offset?
//
			if( Compatibility3D&TextureOffset )
			{
				pTextVertices[TextVertex+0].u+=DebuggerTexelOffset;
				pTextVertices[TextVertex+0].v+=DebuggerTexelOffset;
				pTextVertices[TextVertex+1].u+=DebuggerTexelOffset;
				pTextVertices[TextVertex+1].v+=DebuggerTexelOffset;
				pTextVertices[TextVertex+2].u+=DebuggerTexelOffset;
				pTextVertices[TextVertex+2].v+=DebuggerTexelOffset;
				pTextVertices[TextVertex+3].u+=DebuggerTexelOffset;
				pTextVertices[TextVertex+3].v+=DebuggerTexelOffset;
			}
			else
			{
				pTextVertices[TextVertex+0].x-=0.4375f;
				pTextVertices[TextVertex+0].y-=0.4375f;
				pTextVertices[TextVertex+1].x-=0.4375f;
				pTextVertices[TextVertex+1].y-=0.4375f;
				pTextVertices[TextVertex+2].x-=0.4375f;
				pTextVertices[TextVertex+2].y-=0.4375f;
				pTextVertices[TextVertex+3].x-=0.4375f;
				pTextVertices[TextVertex+3].y-=0.4375f;
			}
//
// Batch up to 20 characters
//
			TextVertex+=4;
			if( TextVertex==80 )
			{
				gos_DrawQuads( pTextVertices, TextVertex );
				TextVertex=0;
			}
		}
		CurrentX+=(DoubleChr ? 12 : 6);
	}
//
// Always moves X position along
//
	DbChrX++;
//
// If highlighting, set back to normal
//
	Brighten=0;
}











//
// Display a string 1=Brighten next character, 9=Tab to 4, 'n'=Fill to end of line, move to next
//
void DrawText( DWORD Color, char* String )
{
	TextVertex=0;
	TextColor=Color;
	if( (Color&0xffffff)==0xffffff && DebuggerAlpha==0xff000000 && HasAlphaTest )
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendDecal );
	else
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );

	while( *String )
	{
		DrawChr( *String++ );
	}

	if( TextVertex )
		gos_DrawQuads( pTextVertices, TextVertex );
	TextVertex=0;
}




void InitTextDisplay()
{
//
// Make the font the current texture
//
	gos_SetRenderState( gos_State_Texture, FontTexture );
	gos_SetRenderState( gos_State_AlphaTest, 1 );
//
// Init the text printing
//
	DbChrX=0;
	DbChrY=0;
	DbMaxX=80;
	DbMaxY=40;
}



//
// Add a texture to the current heap
//
void AddTextureHeap( DWORD Handle, float X1, float Y1, float X2, float Y2 )
{
	TextureHeap* pThis=(TextureHeap*)gos_Malloc( sizeof(TextureHeap), DefaultHeap );

	pThis->Handle=Handle;
	pThis->X1=(int)X1;
	pThis->X2=(int)X2;
	pThis->Y1=(int)Y1;
	pThis->Y2=(int)Y2;

	pThis->pNext=pTextureHeap;
	pTextureHeap=pThis;
}



//
// Clip and draw texture square
//
void gos_DrawTexQuads( DWORD Handle, long ShowWidth, long ShowHeight )
{
	tv[0].x=(float)(TD_TextureX);
	tv[1].x=(float)(TD_TextureX);
	tv[2].x=(float)(TD_TextureX+ShowWidth);
	tv[3].x=(float)(TD_TextureX+ShowWidth);

	tv[0].y=(float)(TD_TextureY);
	tv[1].y=(float)(TD_TextureY+ShowHeight);
	tv[2].y=(float)(TD_TextureY+ShowHeight);
	tv[3].y=(float)(TD_TextureY);

	if( TEXHANDLE_TO_PTR(Handle)->Description()->ddpfPixelFormat.dwSize == 0 )
		return;	// can't display this texture type on-screen
//
// Check for ON screen
//
	if( (TD_TextureY >= DbTopY+18) && (TD_TextureY+ShowHeight) < DbTopY+332 )
	{
		gos_SetRenderState( gos_State_Texture, Handle );
		gos_ClipDrawQuad( tv );
		AddTextureHeap( Handle, tv[0].x, tv[0].y, tv[2].x, tv[2].y );
		return;
	}
//
// Check for OFF screen
//
	if( (TD_TextureY >= DbTopY+332) || (TD_TextureY+ShowHeight) < DbTopY+18 )
	{
		return;
	}
//
// Clipped with top
//
	if( TD_TextureY < DbTopY+18 )
	{
		float clipped=(float)((DbTopY+18)-TD_TextureY)/(float)ShowHeight;

		tv[0].v=tv[3].v=clipped;
		
		tv[0].y=(float)(DbTopY+18);
		tv[3].y=(float)(DbTopY+18);

		gos_SetRenderState( gos_State_Texture, Handle );
		gos_ClipDrawQuad( tv );
		AddTextureHeap( Handle, tv[0].x, tv[0].y, tv[2].x, tv[2].y );

		tv[0].v=0.0;
		tv[3].v=0.0;
		return;
	}

//
// Clipped with bottom
//
	tv[1].y=(float)(DbTopY+332);
	tv[2].y=(float)(DbTopY+332);

	float clipped=1.0f-(float)((TD_TextureY+ShowHeight)-(DbTopY+332))/(float)ShowHeight;

	tv[1].v=tv[2].v=clipped;
		
	gos_SetRenderState( gos_State_Texture, Handle );
	gos_ClipDrawQuad( tv );
	AddTextureHeap( Handle, tv[0].x, tv[0].y, tv[2].x, tv[2].y );

	tv[1].v=1.0;
	tv[2].v=1.0;
	return;
}










//
// Get valid performance monitor registers
//
HRESULT WINAPI PerfCounterEnum( char *szDescription, DWORD dwFlags, DWORD dwId )
{
	if( dwId==PerfCounterSelected )
	{
		DrawText( 0xffffff, "---> " );
		DrawText( 0xffffff, szDescription );
	}
	else
	{
		DrawText( 0xffffff, "     " );
		DrawText( 0xffffff, szDescription );
	}
	DrawText( 0xffffff, "\n" );
	return 1;
}


//
// Use in realtime displaying the texture heaps
//
void ShowTextureHeaps( DWORD Width, DWORD ExamineUsed )
{
	DWORD Found=0;
	DWORD TotalFound=0;
	DWORD SysBytes;
	DWORD VidBytes;
	DWORD SysBytesTot=0;
	DWORD VidBytesTot=0;
	DWORD SysBytesShown=0;
	DWORD VidBytesShown=0;

	int ShowDetail = (ExamineUsed&0x0C)>>2;
	ExamineUsed &= 3;
	DWORD Height;
	DWORD ShowWidth;
	DWORD ShowHeight;
	CTexInfo *pTexInfo;

//
// Check if there are textures of this size
//

	for( pTexInfo = CTexInfo::FirstTexture(); pTexInfo; pTexInfo = pTexInfo->NextTexture() )
	{
		if( NoDebuggerStats /* && !CTexInfo::SpecialHandle(Handle) */ )
		{
			if( ( pTexInfo->Width()==Width ) && !pTexInfo->SpecialTexture() )
			{
				TotalFound++;

				VidBytes = pTexInfo->MemoryUsage( true, gmt_VidMem );
				SysBytes = pTexInfo->MemoryUsage( true, gmt_All ) - VidBytes;	// i.e. Original & SysMem

				VidBytesTot += VidBytes;
				SysBytesTot += SysBytes;

				if( ( ( ExamineUsed == 1 ) && pTexInfo->Used() ) ||
					( ( ExamineUsed == 0 ) && !pTexInfo->Used() ) ||
					( ( ExamineUsed == 2 ) && pTexInfo->InVidMem() ) ||
					  ( ExamineUsed == 3 ) )
				{
					Found++;
					VidBytesShown += VidBytes;
					SysBytesShown += SysBytes;
				}
			}
		}
	}
	if( 0==Found )
		return;

	// Convert memory stats to kilobytes
	VidBytesTot >>= 10;
	SysBytesTot >>= 10;
	VidBytesShown >>= 10;
	SysBytesShown >>= 10;

//
// Print texture size
//
	if( TD_TextureY <= DbTopY+332 )
	{
		InitTextDisplay();
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
		CurrentX=TD_TextureX;
		CurrentY=TD_TextureY+2;
		char Buffer[128];
		if( ExamineUsed == 1 )
			sprintf( Buffer, "Texture size %dxn, Used %d (/%d), SysMem %dk (/%dk) VidMem %dk (/%dk)",Width,Found,TotalFound,SysBytesShown,SysBytesTot,VidBytesShown,VidBytesTot );
		else if( ExamineUsed == 2 )
			sprintf( Buffer, "Texture size %dxn, Loaded %d (/%d), SysMem %dk (/%dk) VidMem %dk (/%dk)",Width,Found,TotalFound,SysBytesShown,SysBytesTot,VidBytesShown,VidBytesTot );
		else if( ExamineUsed == 3 )
			sprintf( Buffer, "Texture size %dxn, %d , SysMem %dk VidMem %dk",Width,TotalFound,SysBytesTot,VidBytesTot );
		else
			sprintf( Buffer, "Texture size %dxn, Unused %d (/%d), SysMem %dk (/%dk) VidMem %dk (/%dk)",Width,Found,TotalFound,SysBytesShown,SysBytesTot,VidBytesShown,VidBytesTot );
		if( (TD_TextureY >= DbTopY+18) && (TD_TextureY+14) < DbTopY+332 )
		{
			DrawText( 0xf0f0f0, Buffer );
		}
	}
	TD_TextureY+=14;
//
// Show textures
//
	if( TD_TextureY <= DbTopY+332 )
	{
		gos_SetRenderState( gos_State_MipMapBias, 0 );
		gos_SetRenderState( gos_State_Filter, gos_FilterNone );
		gos_SetRenderState( gos_State_Clipping, 2 );
		gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	}

	if( ShowDetail != 0 )
	{
		for( pTexInfo = CTexInfo::FirstTexture(); pTexInfo; pTexInfo = pTexInfo->NextTexture() )
		{
			if( NoDebuggerStats /* && !CTexInfo::SpecialHandle(Handle) */ )
			{
				if( ( pTexInfo->Width()==Width ) && !pTexInfo->SpecialTexture() )
				{
					if( ( ( ExamineUsed == 1 ) && pTexInfo->Used() ) ||
						( ( ExamineUsed == 0 ) && !pTexInfo->Used() ) ||
						( ( ExamineUsed == 2 ) && pTexInfo->InVidMem() ) ||
						  ( ExamineUsed == 3 ) )
					{
						Height = pTexInfo->Height();

						ShowWidth = 64;
						ShowHeight = 64;
						if( Height != Width )
						{
							if( Height > Width )
							{
								ShowWidth = ( ShowWidth * Width ) / Height;
							}
							else
							{
								ShowHeight = ( ShowHeight * Height ) / Width;
							}
						}

						char Buffer[128];

						if( ( ShowDetail == 1 ) || pTexInfo->InVidMem() )
						{
							gos_DrawTexQuads( TEXPTR_TO_HANDLE(pTexInfo),ShowWidth,ShowHeight );
						}

						if( ( TD_TextureY >= DbTopY+10 ) && ( TD_TextureY <= DbTopY+300 ) )
						{

							int OldDbMaxX = DbMaxX;
							DbMaxX -= 22;

							gos_SetRenderState( gos_State_Texture, FontTexture );
							gos_SetRenderState( gos_State_AlphaTest, 1 );
							DbChrX=0;
							DbChrY=0;

							CurrentX=DbTopX+140;
							CurrentY=TD_TextureY+15;

							sprintf( Buffer, "%s\n", pTexInfo->Name() );
							DrawText( 0x00ffffff, Buffer );

							CurrentX=DbTopX+140;
							CurrentY=TD_TextureY+25;

							sprintf( Buffer, " Wd x Ht  ARGB Store  SysK 32/M/16/M VidK 16/M\n");
							DrawText( 0x00808080, Buffer );

							CurrentX=DbTopX+140;
							CurrentY=TD_TextureY+35;

							sprintf( Buffer, "%4d %4d ", Width, Height );
							DrawText( 0x00c0c0c0, Buffer );

							switch( pTexInfo->Description()->ddpfPixelFormat.dwRBitMask )
							{
							case 0x0f00:	// 4444
								DrawText( 0x00c0c0c0, "4444 " );
								break;
							case 0xf800:	// 0565
								DrawText( 0x00c0c0c0, "0565 " );
								break;
							case 0x7c00:	// 1555
								DrawText( 0x00c0c0c0, "1555 " );
								break;
							case 0x00FF0000:	//  ?888
								if( pTexInfo->Description()->ddpfPixelFormat.dwRGBAlphaBitMask == 0 )
									DrawText( 0x00c0c0c0, "0888 " );
								else
									DrawText( 0x00c0c0c0, "8888 " );
								break;
							default:
								DrawText( 0x00c0c0c0, "???? " );
							}

							DWORD sysk = 0;
							DWORD vidk = 0;
							char sysk32 = 'N';
							char sysk32M = 'N';
							char sysk16 = 'N';
							char syskM = 'N';
							char vidk16 = 'N';
							char vidkM = 'N';

							int bytes;

							bytes = pTexInfo->MemoryUsage(true,gmt_Original);
							if( bytes )
							{
								sysk += bytes;
								sysk32 = 'Y';
								if( pTexInfo->Hints() & gosHint_UserMipMaps )
									sysk32M = 'Y';
							}

							bytes = pTexInfo->MemoryUsage(true,gmt_SysMem);
							if( bytes )
							{
								sysk += bytes;
								sysk16 = 'Y';
								if( !(pTexInfo->Hints() & gosHint_DisableMipmap) )
									syskM = 'Y';
							}

							bytes = pTexInfo->MemoryUsage(true,gmt_VidMem);
							if( bytes )
							{
								vidk += bytes;
								vidk16 = 'Y';
								if( !(pTexInfo->Hints() & gosHint_DisableMipmap) )
									vidkM = 'Y';
							}
							// Round down to kilobytes
							sysk >>= 10;
							vidk >>= 10;

							if( pTexInfo->Hints() & gosHint_ReloadFromDisk )
							{
								DrawText( 0x00FFFF00, "Reload " );
							}
							else if( pTexInfo->CanRebuild() )
							{
								DrawText( 0x0000FF00, "Rebuild" );
							}
							else
							{
								DrawText( 0x00FF0000, "Fixed  " );
							}
							// Store  SysK 32/M/16/M VidK 16/M\n");
							sprintf( Buffer, "%4d  %c/%c/ %c/%c %4d  %c/%c %s", sysk,sysk32,sysk32M,sysk16,syskM,vidk,vidk16,vidkM, (pTexInfo->InAGP())?"AGP":""  );
							DrawText( 0x00c0c0c0, Buffer );

							DbMaxX = OldDbMaxX;
						}

						TD_TextureX+=64+5;
						TD_TextureY+=(64/2)+5;
						TD_Width++;
						if( TD_Width == 2 )
						{
							TD_TextureX=DbTopX+4;
							TD_Width=0;
						}
					}
				}
			}
		}

		TD_Width=0;
		TD_TextureX=DbTopX+4;
		TD_TextureY+=(64/2)+5;
	}
	else
	{
		for( pTexInfo = CTexInfo::FirstTexture(); pTexInfo; pTexInfo = pTexInfo->NextTexture() )
		{
			if( NoDebuggerStats /* && !CTexInfo::SpecialHandle(Handle) */ )
			{
				if( ( pTexInfo->Width()==Width ) && !pTexInfo->SpecialTexture() )
				{
					if( ( ( ExamineUsed == 1 ) && pTexInfo->Used() ) ||
						( ( ExamineUsed == 0 ) && !pTexInfo->Used() ) ||
						( ( ExamineUsed == 2 ) && pTexInfo->InVidMem() ) ||
						  ( ExamineUsed == 3 ) )
					{
						Height = pTexInfo->Height();

						ShowWidth = 64;
						ShowHeight = 64;
						if( Height != Width )
						{
							if( Height > Width )
							{
								ShowWidth = ( ShowWidth * Width ) / Height;
							}
							else
							{
								ShowHeight = ( ShowHeight * Height ) / Width;
							}
						}

						gos_DrawTexQuads( TEXPTR_TO_HANDLE(pTexInfo),ShowWidth,ShowHeight );

						TD_TextureX+=64+5;
						TD_Width++;
						if( TD_Width*(64+5) > 462 )
						{
							TD_TextureX=DbTopX+4;
							TD_TextureY+=64+5;
							TD_Width=0;
						}
					}
				}
			}
		}

		if( TD_Width!=0 )
		{
			TD_TextureX=DbTopX+4;
			TD_TextureY+=64+5;
			TD_Width=0;
		}
	}
}



void DisplayTextureMemory( DWORD ExamineUsed )
{
//
// Work out maximum scroll position
//

	while( pTextureHeap )
	{
		TextureHeap* pThis=pTextureHeap;
		pTextureHeap=pTextureHeap->pNext;			// Remove any heaps displayed last frame
		gos_Free( pThis );
	}

	TD_TextureX=DbTopX+4;
	TD_TextureY=DbTopY+332;
	TD_Width=0;

	int viewWidth;

	for( viewWidth=4096; viewWidth >=4; viewWidth>>=1 )
		ShowTextureHeaps( viewWidth, ExamineUsed );

	if( TextureScroll < ((DbTopY+332)-TD_TextureY+8*40) )
		TextureScroll = ((DbTopY+332)-TD_TextureY+8*40);
	if( TextureScroll>0 )
		TextureScroll=0;
//
// Now display textures
//
	TD_TextureX=DbTopX+4;
	TD_TextureY=DbTopY+18+TextureScroll;
	TD_Width=0;

	tv[0].argb=DebuggerAlpha+0xffffff;
	tv[1].argb=DebuggerAlpha+0xffffff;
	tv[2].argb=DebuggerAlpha+0xffffff;
	tv[3].argb=DebuggerAlpha+0xffffff;

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

	tv[0].v=0.0;
	tv[1].v=1.0;
	tv[2].v=1.0;
	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;

	for( viewWidth=4096; viewWidth >=4; viewWidth>>=1 )
		ShowTextureHeaps( viewWidth, ExamineUsed );
}









#ifdef LAB_ONLY

void UpdateDebugWindow()
{
//
// 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, 1 );
	if( (DebuggerAlpha&0xff000000)==0xff000000 )
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	else
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
//
// Draw window surround and title bar
//
	DrawSquare( DbTopX,DbTopY,DbSizeX,DbSizeY, 0x000060 );			// Whole window (note: Font background color should match!)
	DrawSquare( DbTopX+3,DbTopY+3,483,12, 0x0000a0 );		// Title bar
//
// Draw shadow lines around menu (Blade clipping needs to be fixed first)
//
	DWORD OldClip=RenderStates[gos_State_Clipping];
	gos_SetRenderState( gos_State_Clipping, 1 );
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	DrawLines( DbTopX+DbSizeX, DbTopY+2, DbTopX+DbSizeX, DbTopY+DbSizeY, 0x80000000 );
	DrawLines( DbTopX+DbSizeX+1, DbTopY+2, DbTopX+DbSizeX+1, DbTopY+DbSizeY, 0x80000000 );
	DrawLines( DbTopX+2, DbTopY+DbSizeY, DbTopX+DbSizeX+2, DbTopY+DbSizeY, 0x80000000 );
	DrawLines( DbTopX+2, DbTopY+DbSizeY+1, DbTopX+DbSizeX+2, DbTopY+DbSizeY+1, 0x80000000 );
	gos_SetRenderState( gos_State_Clipping, OldClip );
//
// Draw menu bar (always translucent)
//
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	InitTextDisplay();
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	CurrentX=DbTopX+6;
	CurrentY=DbTopY+6;
	DrawText( 0xc0c0c0, "\001Debugger  \001Rasterizer  " );
//
// If Libraries are active, add menu item
//
	if( pLibrariesMenu )
	{
		DrawText( 0xc0c0c0, "\001Libraries  " );
	}
//
// If the game has it's own menu, add it to the end
//
	if( pGameMenu )
	{
		DrawText( 0xc0c0c0, ApplicationName );
	}
//
// Draw current main window text
//
	DbChrX=0;
	DbChrY=0;
	DbMaxX=80;
	DbMaxY=40;
	StartX=CurrentX=DbTopX+4;
	StartY=CurrentY=DbTopY+18;
	pTextVertices[0].argb = 0xffffff+DebuggerAlpha;
	pTextVertices[1].argb = 0xffffff+DebuggerAlpha;
	pTextVertices[2].argb = 0xffffff+DebuggerAlpha;
	pTextVertices[3].argb = 0xffffff+DebuggerAlpha;
//
// Update the debug display
//
	switch( DebugDisplay )
	{
//
// Display statistics
//
		case DbgS_Stat:
		{
			char Buffer[256];
			sprintf( Buffer, "    Current    Average    Minimum    Maximum Graph     Name\n" );
			DrawText( 0xffffff, Buffer );
			Stat* pStat=pStatistics;

			for( int i=0;i<TopStatistics; i++ )
				if( pStat )
					pStat=pStat->pNext;

			while( pStat && DbChrY<40 )
			{
				if( pStat->Flags&Stat_Format )
				{
					DrawText( 0xffffff, pStat->Name );
					DrawText( 0xffffff, "\n" );
				}
				else
				{
					char floatBuffer[2];
					char fBuffer[128];
					floatBuffer[1]=0;

					bool ShowMilliseconds=gShowGraphsAsTime && pStat->Type==gos_timedata;
//
// Current value - or average of last 10 values
//
					double Value=GetHistory(pStat,GraphHead);
					if( gShowAverage && pStat->Count>15 )
					{
						if( pStat->Type==gos_timedata && !ShowMilliseconds )
						{
							Value=Value*PercentHistory[GraphHead] +
							GetHistory(pStat,(GraphHead-1)&511) * PercentHistory[(GraphHead-1)&511] +
							GetHistory(pStat,(GraphHead-2)&511) * PercentHistory[(GraphHead-2)&511] +
							GetHistory(pStat,(GraphHead-3)&511) * PercentHistory[(GraphHead-3)&511] +
							GetHistory(pStat,(GraphHead-4)&511) * PercentHistory[(GraphHead-4)&511] +
							GetHistory(pStat,(GraphHead-5)&511) * PercentHistory[(GraphHead-5)&511] +
							GetHistory(pStat,(GraphHead-6)&511) * PercentHistory[(GraphHead-6)&511] +
							GetHistory(pStat,(GraphHead-7)&511) * PercentHistory[(GraphHead-7)&511] +
							GetHistory(pStat,(GraphHead-8)&511) * PercentHistory[(GraphHead-8)&511] +
							GetHistory(pStat,(GraphHead-9)&511) * PercentHistory[(GraphHead-9)&511] +
							GetHistory(pStat,(GraphHead-10)&511)* PercentHistory[(GraphHead-10)&511] +
							GetHistory(pStat,(GraphHead-11)&511)* PercentHistory[(GraphHead-11)&511] +
							GetHistory(pStat,(GraphHead-12)&511)* PercentHistory[(GraphHead-12)&511] +
							GetHistory(pStat,(GraphHead-13)&511)* PercentHistory[(GraphHead-13)&511] +
							GetHistory(pStat,(GraphHead-14)&511)* PercentHistory[(GraphHead-14)&511] +
							GetHistory(pStat,(GraphHead-15)&511)* PercentHistory[(GraphHead-15)&511];
							Value/=16.0f;
						}
						else
						{
							Value+=
							GetHistory(pStat,(GraphHead-1)&511) +
							GetHistory(pStat,(GraphHead-2)&511) +
							GetHistory(pStat,(GraphHead-3)&511) +
							GetHistory(pStat,(GraphHead-4)&511) +
							GetHistory(pStat,(GraphHead-5)&511) +
							GetHistory(pStat,(GraphHead-6)&511) +
							GetHistory(pStat,(GraphHead-7)&511) +
							GetHistory(pStat,(GraphHead-8)&511) +
							GetHistory(pStat,(GraphHead-9)&511) +
							GetHistory(pStat,(GraphHead-10)&511) +
							GetHistory(pStat,(GraphHead-11)&511) +
							GetHistory(pStat,(GraphHead-12)&511) +
							GetHistory(pStat,(GraphHead-13)&511) +
							GetHistory(pStat,(GraphHead-14)&511) +
							GetHistory(pStat,(GraphHead-15)&511);
							Value/=16.0f;
						}
					}
					else
					{
						if( pStat->Type==gos_timedata && !ShowMilliseconds )
						{
							Value*=PercentHistory[GraphHead];
						}
					}
//
// Clamp number of digits so display never gets corrupted
//
					double MaxValue=9999999.0;
					if( pStat->Flags&Stat_1DP )
					{
						floatBuffer[0]='1';
						MaxValue=99999999.0;
					}
					else if( pStat->Flags&Stat_2DP )
					{
						floatBuffer[0]='2';
						MaxValue=9999999.0;
					}
					else if( pStat->Flags&Stat_3DP )
					{
						floatBuffer[0]='3';
						MaxValue=9999999.0;
					}
					else
					{
						floatBuffer[0]='0';
						MaxValue=9999999999.0;
					}
					if( pStat->Type==gos_timedata && ShowMilliseconds )
					{
						floatBuffer[0]='2';
						MaxValue=99999.0;
					}
//
// Showing the total only?
//
					if( pStat->Flags&Stat_Total )
					{
						if( Value>MaxValue )
							Value=MaxValue;

						sprintf( fBuffer, "%% 11.%sf                                   %%s   %%s\n",floatBuffer );
						sprintf( Buffer, fBuffer,
							Value,
							((pStat->Flags&Stat_Graph) ? "Y" : "n"),
							pStat->Name );
					}
					else
					{
						if( pStat->Count )
						{
							if( pStat->Type==gos_timedata )
							{
								if( ShowMilliseconds )
								{
									Value*=OneOverProcessorSpeed;
									
									if( Value>MaxValue )
										Value=MaxValue;

									double Average=(pStat->Total/pStat->Count)*OneOverProcessorSpeed>MaxValue ? MaxValue : (pStat->Total/pStat->Count)*OneOverProcessorSpeed;
									double Minimum=pStat->Minimum*OneOverProcessorSpeed>MaxValue ? MaxValue : pStat->Minimum*OneOverProcessorSpeed;
									double Maximum=pStat->Maximum*OneOverProcessorSpeed>MaxValue ? MaxValue : pStat->Maximum*OneOverProcessorSpeed;
									if( Average<0 )
										Average=0;

									sprintf( fBuffer, "%% 9.%sfms%% 9.%sfms%% 9.%sfms%% 9.%sfms  %%s   %%s\n",floatBuffer,floatBuffer,floatBuffer,floatBuffer );
									sprintf( Buffer, fBuffer,
										Value,
										Average,
										Minimum,
										Maximum,
										((pStat->Flags&Stat_Graph) ? "Y" : "n"),
										pStat->Name );
								}
								else
								{
									if( Value>MaxValue )
										Value=MaxValue;

									double Average=(pStat->TotalPercentage/pStat->Count)>MaxValue ? MaxValue : (pStat->TotalPercentage/pStat->Count);
									double Minimum=pStat->MinPercentage>MaxValue ? MaxValue : pStat->MinPercentage;
									double Maximum=pStat->MaxPercentage>MaxValue ? MaxValue : pStat->MaxPercentage;

									if( Value>100.0f )
										Value=100.0f;
									else if( Value<0 )
										Value=0;
									if( Average>100.0f )
										Average=100.0f;
									else if( Average<0 )
										Average=0;
									if( Minimum>100.0f )
										Minimum=100.0f;
									else if( Minimum<0 )
										Minimum=0;
									if( Maximum>100.0f )
										Maximum=100.0f;
									else if( Maximum<0 )
										Maximum=0;
									
									sprintf( fBuffer, "%% 11.%sf%% 11.%sf%% 11.%sf%% 11.%sf  %%s   %%s\n",floatBuffer,floatBuffer,floatBuffer,floatBuffer );
									sprintf( Buffer, fBuffer,
										Value,
										Average,
										Minimum,
										Maximum,
										((pStat->Flags&Stat_Graph) ? "Y" : "n"),
										pStat->Name );
								}
							}
							else
							{
								if( Value>MaxValue )
									Value=MaxValue;

								double Average=(pStat->Total/pStat->Count)>MaxValue ? MaxValue : (pStat->Total/pStat->Count);
								double Minimum=pStat->Minimum>MaxValue ? MaxValue : pStat->Minimum;
								double Maximum=pStat->Maximum>MaxValue ? MaxValue : pStat->Maximum;
								
								sprintf( fBuffer, "%% 11.%sf%% 11.%sf%% 11.%sf%% 11.%sf  %%s   %%s\n",floatBuffer,floatBuffer,floatBuffer,floatBuffer );
								sprintf( Buffer, fBuffer,
									Value,
									Average,
									Minimum,
									Maximum,
									((pStat->Flags&Stat_Graph) ? "Y" : "n"),
									pStat->Name );
							}
						}
						else
						{
//
// No values recorded yet
//
							sprintf( fBuffer, "%% 11.%sf%% 11.%sf%% 11.%sf%% 11.%sf  %%s   %%s\n",floatBuffer,floatBuffer,floatBuffer,floatBuffer );
							sprintf( Buffer, fBuffer,
								(double)0.0,
								(double)0.0,
								(double)0.0,
								(double)0.0,
								((pStat->Flags&Stat_Graph) ? "Y" : "n"),
								pStat->Name );
						}
					}
					DrawText( 0xffffff, Buffer );
				}
				pStat=pStat->pNext;
			}
			break;
		}
//
// Display spew log
//
		case DbgS_Spew:
		{

			DrawText( 0xffffff, "Spew Messages: \n");
			DrawText( 0xffffff, "=============\n\n");
			DrawText( 0xffffff, "Common Categories: ");
			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECTDRAW)) )
				DrawText(0x00ff00, "[DirectDraw] ");
			else
				DrawText(0xff0000, "[DirectDraw] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECT3D)) )
				DrawText(0x00ff00, " [ Direct3D ] ");
			else
				DrawText(0xff0000, " [ Direct3D ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECTX)) )
				DrawText(0x00ff00, " [  DirectX ] ");
			else
				DrawText(0xff0000, " [  DirectX ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECTPLAY)) )
				DrawText(0x00ff00, " [DirectPlay]\n");
			else												
				DrawText(0xff0000, " [DirectPlay]\n");

			DrawText(0xffffff, "================= ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_FILES)) )
				DrawText(0x00ff00, " [ File I/O ] ");
			else
				DrawText(0xff0000, " [ File I/O ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECTINPUT)) )
				DrawText(0x00ff00, " [ Controls ] ");
			else
				DrawText(0xff0000, " [ Controls ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_MEMORY)) )
				DrawText(0x00ff00, " [  Memory  ] ");
			else
				DrawText(0xff0000, " [  Memory  ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_NETWORK)) )
				DrawText(0x00ff00, " [   Net    ]\n");
			else
				DrawText(0xff0000, " [   Net    ]\n");

			DrawText(0xffffff, "                  ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECTSOUND)) )
				DrawText(0x00ff00, " [  Audio   ] ");
			else
				DrawText(0xff0000, " [  Audio   ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_TEXTURE)) )
				DrawText(0x00ff00, " [ Textures ] ");
			else
				DrawText(0xff0000, " [ Textures ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_WINDOWSMESSAGES)) )
				DrawText(0x00ff00, " [  Windows ] ");
			else
				DrawText(0xff0000, " [  Windows ] ");

			if (strstr(strlwr((char *)&SpewList), strlwr(GROUP_DIRECTSHOW)) )
				DrawText(0x00ff00, " [   Video  ]");
			else
				DrawText(0xff0000, " [   Video  ]");

			DrawText( 0x00ff00, "\n\nRuntime Filter: ");
			DrawText( 0x00ff00, (char *)&SpewDefault );

			// draw the input cursor if spew line is focused
			if (spewInterfaceFocus)
			{
				if (gos_GetElapsedTime() <= spewCursorTimer + 0.5 )
				{
					DrawText(0xffff00,"O");
				}

				if (gos_GetElapsedTime() > spewCursorTimer + 1.0 )
				{
					spewCursorTimer = gos_GetElapsedTime();
				}
			}

			DrawText( 0xffffff, "\n\n");
			DWORD cpos=CurrentDebugTopOfScreen;

			char Char;
			do
			{
				do
				{
					Char=DebuggerBuffer[cpos];	    

					DrawChr( Char );

					cpos++;
					if( cpos==DebuggerBufferSize )
						cpos=0;
				}
				while( Char!='\n' );
			
			} while(DbChrY<DbMaxY);

			if( TextVertex )
				gos_DrawQuads( pTextVertices, TextVertex );
			TextVertex=0;

			break;
		}
//
// Display renderer information
//
		case DbgS_Texture:
		{
			char Buffer[256];

			char HardwareBuffer[128];
			DWORD Device=Environment.fullScreen ? Environment.FullScreenDevice : 0;
			sprintf( HardwareBuffer, "%s (%s T&L)",DeviceArray[Device].DDid.szDescription,(gos_GetMachineInformation(gos_Info_CanHardwareTandL) ? "Hardware" : "Software") );
            sprintf( Buffer, "\nRenderer          = %s\n",HardwareRenderer ? HardwareBuffer : (Environment.Renderer==2 ? "Software Reference Rasterizer" : (Environment.Renderer==1 ? "Software RGB/MMX" : "Blade Rasterizer" )) );
			DrawText( 0xffffff, Buffer );
			if( HardwareRenderer )
			{
				sprintf( Buffer,"Hardware T&L      = Lights=%d, Blend=%d\n",gos_GetMachineInformation(gos_Info_GetMaximumActiveLights),HasVertexBlending );
			    DrawText( 0xffffff, Buffer );
			}
			if( NumDevices )
            {
                if( -1==DeviceArray[Device].CurrentCard )
			    {
				    if( DeviceArray[Device].CurrentVendor )
				    {
					    sprintf( Buffer, "3D Chipset        = Vendor=%s, Device=0x%x, DriverH,L=0x%x,0x%x\n", DeviceArray[Device].CurrentVendor, DeviceArray[Device].DDid.dwDeviceId, DeviceArray[Device].DDid.liDriverVersion.HighPart, DeviceArray[Device].DDid.liDriverVersion.LowPart );
					    DrawText( 0xffffff, Buffer );
				    }
				    else
				    {
					    sprintf( Buffer, "3D Chipset        = Vendor=0x%x, Device=0x%x, DriverH,L=0x%x,0x%x\n", DeviceArray[Device].DDid.dwVendorId, DeviceArray[Device].DDid.dwDeviceId, DeviceArray[Device].DDid.liDriverVersion.HighPart, DeviceArray[Device].DDid.liDriverVersion.LowPart );
					    DrawText( 0xffffff, Buffer );
				    }
			    }
			    else
			    {
				    char Buffer1[128];
				    sprintf( Buffer, "3D Chipset        = %s %s (Driver version %s)\n", KnownCards[DeviceArray[Device].CurrentCard].VendorName, KnownCards[DeviceArray[Device].CurrentCard].CardName, VersionNumber( Buffer1, DeviceArray[Device].DDid.liDriverVersion.HighPart, DeviceArray[Device].DDid.liDriverVersion.LowPart) );
				    DrawText( 0xffffff, Buffer );

					DWORD DrvH=KnownCards[DeviceArray[Device].CurrentCard].DriverH;
					DWORD DrvL=KnownCards[DeviceArray[Device].CurrentCard].DriverL;
					if( WindowsNT )
					{
						DrvH=KnownCards[DeviceArray[Device].CurrentCard].NTDriverH;
						DrvL=KnownCards[DeviceArray[Device].CurrentCard].NTDriverL;
					}
					if( DrvL != 0 && DrvH != 0 && (
						DrvL != DeviceArray[Device].DDid.liDriverVersion.LowPart ||
						DrvH != DeviceArray[Device].DDid.liDriverVersion.HighPart ))
					{
						char Buffer1[64];
						sprintf( Buffer, "GameOS Driver     = version %s\n", VersionNumber( Buffer1, DrvH, DrvL ) );
						DrawText( 0xff0000, Buffer );
					}
			    }
            }
            else
            {
				sprintf( Buffer, "3D Hardware       = Not Present - Using blade\n" );
				DrawText( 0xff0000, Buffer );
            }
			char rBuf[32];
			if( CurrentRefreshRate )
				sprintf( rBuf, "%dHz refresh rate", CurrentRefreshRate );
			else
				sprintf( rBuf, "default refresh rate" );
            sprintf( Buffer, "Screen Resolution = %dx%d (%s)\n",Environment.screenWidth, Environment.screenHeight, rBuf );
			DrawText( 0xffffff, Buffer );

            sprintf( Buffer, "Solid textures    = %s\n",GetGosPixelFormat(&TextureDesc[gos_Texture_Solid][0][0].ddpfPixelFormat) );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Keyed textures    = %s\n",GetGosPixelFormat(&TextureDesc[gos_Texture_Keyed][0][0].ddpfPixelFormat) );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Alpha textures    = %s\n",GetGosPixelFormat(&TextureDesc[gos_Texture_Alpha][0][0].ddpfPixelFormat) );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Render format     = %s%s%s\n",GetGosPixelFormat( &BBddsd.ddpfPixelFormat ), ((BBddsd.ddsCaps.dwCaps&DDSCAPS_VIDEOMEMORY) ? "(Video Memory)" : "(System Memory)"),(TripleBuffer ? " (Tripple Buffered)" : "") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Z Buffer format   = %s%s\n",(Environment.disableZBuffer ? "Disabled" : GetGosPixelFormat( &ZBddsd.ddpfPixelFormat )), (Environment.disableZBuffer ? "" :	(ZBddsd.ddsCaps.dwCaps&DDSCAPS_VIDEOMEMORY) ? "(Video Memory)" : "(System Memory)") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has linear memory = %s\n", (DeviceArray[Device].LinearMemory ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has Mipmapping    = %s\n", (HasMipMap ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "AGP Memory        = %s\n", (HasAGP ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Guard Band Clip   = %s\n", (HasGuardBandClipping ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has AlphaTest     = %s\n", (HasAlphaTest ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has Clamp mode    = %s\n", (HasClamp ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has ModulateAlpha = %s\n", (HasModulateAlpha ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has Gamma         = %s\n", (GammaControlInterface ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has Brightness    = %s\n", (AllowBrightness ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has Contrast      = %s\n", (AllowContrast ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Has Specular      = %s\n", (HasSpecular ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Can render 32 bit = %s\n", (gos_GetMachineInformation(gos_Info_CanDeviceRender32Bit,Device) ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Anti-Alias        = %s (Has Caps=%c)\n",(Environment.AntiAlias ? "Yes" : "No"),HasAntiAlias ? 'Y' : 'N' );
			DrawText( 0xffffff, Buffer );
			sprintf( Buffer, "Stencil Buffer    = %s (Has Caps=%s)\n", (Environment.Stencil ? "Yes" : "No"), (HardwareRenderer ? (gos_GetMachineInformation(gos_Info_CanDeviceRenderStencil,Device)==1 ? "Y" : (gos_GetMachineInformation(gos_Info_CanDeviceRenderStencil,Device)==2 ? "In 32 Bit Mode" : "N")) : "N") );
			DrawText( 0xffffff, Buffer );
			break;
		}

		case DbgS_TextureLog:
		{
			char Buffer[256];
			if( CTexInfo::LogEnable() )
				sprintf( Buffer, "Texture Manager Log      (Logging Enabled)  (DownScale=/%d)\n===================\n\n",1<<CTexInfo::GlobalScaleShift() );
			else
				sprintf( Buffer, "Texture Manager Log      (Logging Disabled)  (DownScale=/%d)\n===================\n\n",1<<CTexInfo::GlobalScaleShift() );
			DrawText( 0xffffff, Buffer );

			DrawText( 0xffffff, " Frame Texture     Size   Event & Texture Name\n\n");
			for( int evtOffset = 0; evtOffset < 32; evtOffset++ )
			{
				if( CTexInfo::GetTextureEvent( evtOffset ) )
				{
					sprintf( Buffer, "%s\n", CTexInfo::GetTextureEvent( evtOffset ) );
					DrawText( 0xffffff, Buffer );
				}
			}
			break;
		}

//
// Display networking information
//
		case DbgS_Network:
		{
			char Buffer[256];

			DrawText( 0xffffff, "Networking Information\n======================\n\n" );

			if( !Environment.NetworkGame )
			{
				DrawText( 0xffffff, "Not in a networked game\n" );
				break;
			}

			sprintf( Buffer, "TCPIP=%s IPX=%s Serial=%s Modems=%s\n",(hasTCP ? "Yes" : "No "),(hasIPX ? "Yes" : "No "),(SerialPortNames ? SerialPortNames->Name : "No  "), (ModemNames ? ModemNames->Name : "No") );
			DrawText( 0xffffff, Buffer );

			ListOfNames* pSerialPort=SerialPortNames;
			ListOfNames* pModem=ModemNames;
			while( pSerialPort || pModem )
			{
				if( pSerialPort )
					pSerialPort=pSerialPort->pNext;
				if( pModem )
					pModem=pModem->pNext;
				sprintf( Buffer, "                         %s        %s\n",(pSerialPort ? pSerialPort->Name : "    "), (pModem ? pModem->Name : " ") );
				DrawText( 0xffffff, Buffer );
			}

			char* Protocol="Not connected";
			switch( Connected )
			{
				case NP_TCP:
					Protocol="TCPIP";
					break;
				case NP_IPX:
					Protocol="IPX";
					break;
				case NP_SERIAL:
					Protocol="Serial";
					break;
				case NP_MODEM:
					Protocol="Modem";
					break;
			}

			sprintf( Buffer, "Protocol Selected = %s %s\n",Protocol, (gos_NetInformation(gos_Networking) ? "- In a networked game" : "- Not in a networked game") );
			DrawText( 0xffffff, Buffer );

			sprintf( Buffer, "Players in game   = %d (Max=%d)\n", (DWORD)gos_NetInformation(gos_NumberOfPlayers),(Connected==NP_SERIAL || Connected==NP_MODEM) ? 2 : Environment.NetworkMaxPlayers );
			DrawText( 0xffffff, Buffer );

			sprintf( Buffer, "This machine ID   = 0x%x  %s\n", (DWORD)gos_NetInformation(gos_MyID), (gos_NetInformation(gos_AmITheServer) ? "(This machine is the server)" : "") );
			DrawText( 0xffffff, Buffer );

			sprintf( Buffer, "This game name    = \"%s\" %s %s\n", (gos_NetInformation(gos_Networking) ? CurrentSession.lpszSessionNameA : ""), (gos_NetInformation(gos_GameSecure) ? "(Secure)" : ""), (gos_NetInformation(gos_GameSecure) ? "" : (Environment.NetServerMigration ? "(Server Migration Possible)" : "")) );
			DrawText( 0xffffff, Buffer );

			sprintf( Buffer, "This players name = \"%s\"", (char*)gos_NetInformation(gos_PlayerName,(DWORD)gos_NetInformation(gos_PlayerNumber,(DWORD)(gos_NetInformation(gos_MyID)))) );
			DrawText( 0xffffff, Buffer );

			if( Connected==NP_TCP && MyIPAddress )
			{
				sprintf( Buffer, " (Address=%s)", MyIPAddress );
				DrawText( 0xffffff, Buffer );
			}

			char ComputerName[MAX_COMPUTERNAME_LENGTH+1];
			DWORD NameSize=MAX_COMPUTERNAME_LENGTH+1;
			GetComputerName( ComputerName, &NameSize );
			sprintf( Buffer, "\nThis machine name = %s", ComputerName );
			DrawText( 0xffffff, Buffer );
		
			sprintf( Buffer, "\nThe servers ID    = 0x%x\n", (DWORD)gos_NetInformation(gos_ServerID) );
			DrawText( 0xffffff, Buffer );

			sprintf( Buffer, "Everyone's ID     = 0x%x\n", (DWORD)gos_NetInformation(gos_AllID) );
			DrawText( 0xffffff, Buffer );

			sprintf( Buffer, "Game locked       = %s\n", (gos_NetInformation(gos_LockedStatus) ? "Yes" : "No") );
			DrawText( 0xffffff, Buffer );

			if( gos_NetInformation(gos_GamePassword) )
			{
				sprintf( Buffer, "Game Password     = \"%s\"\n", gos_NetInformation(gos_GamePassword) );
				DrawText( 0xffffff, Buffer );
			}

			sprintf( Buffer, "Unsent packets    = %d\n\n", gos_NetInformation(gos_GetMessageSendQueue) );
			DrawText( 0xffffff, Buffer );

			int i,iNumOutboudWindowObjects;

			iNumOutboudWindowObjects = (int)gos_NetInformation(gos_GetNumOutboudWindows);
			for (i=0 ; i<iNumOutboudWindowObjects ; i++)
			{
				sprintf( Buffer, "Outstanding %2d    = %4d, %4d, %4d, %4d\n\n",
					i,
					gos_NetInformation(gos_GetOutboundWindowSize, i),
					gos_NetInformation(gos_GetOB_NxtPkt2Send, i),
					gos_NetInformation(gos_GetOB_LstPktRcvd, i),
					gos_NetInformation(gos_GetOB_LstPktTheyRcvd, i));
				DrawText( 0xffffff, Buffer );
			}

			DrawText( 0xffffff, "Player Name       ID        #To    bps      #From    bps      Received  When\n" );

			DWORD NumTo=0;
			DWORD NumFrom=0;
			DWORD BPSTo=0;
			DWORD BPSFrom=0;
			DWORD Lost=0;
			DWORD Latency=0;

			for( int t0=0; t0<(DWORD)gos_NetInformation(gos_NumberOfPlayers); t0++ )
			{
				char TempName[128];
//
// Truncate player name to fit on display
//
				strncpy( TempName, (char*)gos_NetInformation(gos_PlayerName,t0), 13 );
				for( int t1=0; t1<13; t1++ )
					if( TempName[t1]==0 )
						TempName[t1]=' ';
				TempName[12]=0;
//
// Run through packet log and collect stats for the last second
//
				NumTo=0;
				NumFrom=0;
				BPSTo=0;
				BPSFrom=0;
				Lost=0;
				Latency=0;

				DWORD PlayerID=(DWORD)gos_NetInformation(gos_PlayerID,t0);
				
				PacketLogging* pThis=pDebugPacketLog;
				while( pThis )
				{
					if( NetworkDebugTime-1.0f>pThis->TimeStamp )			// Look only 1 second back
						break;
					if( pThis->ToID==PlayerID )
						NumTo++;
					if( pThis->FromID==PlayerID )
						NumFrom++;
					if( pThis->ToID==PlayerID )
						BPSTo+=(pThis->Size+PACKET_OVERHEAD)*8;
					if( pThis->FromID==PlayerID )
						BPSFrom+=(pThis->Size+PACKET_OVERHEAD)*8;
					pThis=pThis->pNext;
				}

				DWORD NumberPackets=0;
				double When=0.0f;
				ListOfNames* pTemp=CurrentPlayers;
				while( pTemp )
				{
					if( pTemp->Data==PlayerID )
					{
						NumberPackets=pTemp->PacketsReceived;
						When=pTemp->LastTime;				
						break;
					}
					pTemp=pTemp->pNext;
				}
			
				if( NumberPackets )
				{
					DWORD TimeDelta=(int)(gos_GetElapsedTime()-When);
					sprintf( Buffer, "%s 0x%08X    % 4d% 7d       % 4d% 7d        %4d %6ds\n", TempName,PlayerID,NumTo,BPSTo,NumFrom,BPSFrom,NumberPackets,TimeDelta );
				}
				else
					sprintf( Buffer, "%s 0x%08X    % 4d% 7d       % 4d% 7d        None\n", TempName,PlayerID,NumTo,BPSTo,NumFrom,BPSFrom );

				DrawText( 0xffffff, Buffer );
			}

			break;
		}

//
// display sound renderer information
//
		case DbgS_Sound:
		{
			if (!SoundRenderer.m_isInitialized)
			{
				DrawText( 0xffffff, "Sound Renderer is disabled\n" );
				break;
			}

			//
			// Mixer information
			//
			gosAudio_ChannelInfo ci;
			gosAudio_GetChannelInfo( gosAudio_Mixer, &ci );
			char tmp[512];
			DrawText( 0xffffff, "Mixer\n" );
			DrawText( 0xffffff, "=====\n" );
			if (SoundRenderer.m_Mixer->m_dscaps.dwFlags & DSCAPS_CERTIFIED)
			{
				DrawText( 0x00ff00, "This device is certified by Microsoft" );
				if ( Environment.soundMixInHardware == 0 )
					DrawText(0xff0000, " (Hardware Mixing Disabled)");
				DrawText(0xff0000,"\n");
			}
			else
			{
				DrawText( 0xff0000, "This device is not certified by Microsoft" );
				if ( Environment.soundMixInHardware == 0 )
					DrawText(0xff0000, " (Hardware Mixing Disabled)");
				DrawText(0xff0000,"\n");
			}

			sprintf( tmp, "Sound Device               : %s\n", SoundRenderer.m_DeviceArray[Environment.soundDevice].Description );
			DrawText( 0xffffff, tmp );
			sprintf( tmp, "%s", SoundRenderer.m_DeviceArray[Environment.soundDevice].Version );
			DrawText( 0xffffff, tmp );
			sprintf( tmp, "Mixer Frequency / Channels : %d KHz Stereo\n", Environment.soundHiFi == 1 ?  44 : 22 );
			DrawText( 0xffffff, tmp );

			SoundRenderer.m_Mixer->GetCaps();
			sprintf( tmp, "3D Hardware Buffers in use : %d / %d\n", SoundRenderer.m_Mixer->m_dscaps.dwMaxHw3DAllBuffers - SoundRenderer.m_Mixer->m_dscaps.dwFreeHw3DAllBuffers, SoundRenderer.m_Mixer->m_dscaps.dwMaxHw3DAllBuffers );
			DrawText( 0xffffff, tmp );
			sprintf( tmp, "3D Hardware Memory in use  : %d / %d\n", SoundRenderer.m_Mixer->m_dscaps.dwTotalHwMemBytes - SoundRenderer.m_Mixer->m_dscaps.dwFreeHwMemBytes, SoundRenderer.m_Mixer->m_dscaps.dwTotalHwMemBytes );
			DrawText( 0xffffff, tmp );

			sprintf( tmp, "Speaker Configuration      : ");
			DrawText( 0xffffff, tmp );
			if (ci.dwSpeakerConfig == gosAudio_Headphones )
				sprintf( tmp, "Headphones\n" );
			else
			if (ci.dwSpeakerConfig == gosAudio_Monaural )
				sprintf( tmp, "Monaural\n" );
			else
			if (ci.dwSpeakerConfig == gosAudio_Quadraphonic )
				sprintf( tmp, "Quadraphonic\n" );
			else
 			if (ci.dwSpeakerConfig == gosAudio_Surround )
				sprintf( tmp, "Surround\n" );
			else
			if ((ci.dwSpeakerConfig & gosAudio_Stereo) )
			{
				if (ci.dwSpeakerConfig & gosAudio_DegreeArc5 )
					sprintf( tmp, "Stereo ( gosAudio_DegreeArc5)\n" );
				else
				if (ci.dwSpeakerConfig & gosAudio_DegreeArc10 )
					sprintf( tmp, "Stereo ( gosAudio_DegreeArc10)\n" );
				else
				if (ci.dwSpeakerConfig & gosAudio_DegreeArc20 )
					sprintf( tmp, "Stereo ( gosAudio_DegreeArc20)\n" );
				else
				if (ci.dwSpeakerConfig & gosAudio_DegreeArc180 )
					sprintf( tmp, "Stereo ( gosAudio_DegreeArc180)\n" );
				else
					sprintf( tmp, "Stereo\n");
			}
			DrawText( 0xffffff, tmp );


			// volume, panning, freq
			sprintf( tmp, "Volume, Panning, Frequency : %.2f, %.2f, %.2f\n", ci.fVolume, ci.fPanning, ci.fFrequency );
			DrawText( 0xffffff, tmp );

			// distance, doppler, rolloff
			sprintf( tmp, "Doppler, Distance, Rolloff : %.2f, %.2f, %.2f\n", ci.fDoppler, ci.fDistance, ci.fRolloff );
			DrawText( 0xffffff, tmp );				   

			
			// position
			sprintf( tmp, "3D Position                : (%.2f, %.2f, %.2f)\n", ci.fPosX, ci.fPosY, ci.fPosZ );
			DrawText( 0xffffff, tmp );
			sprintf( tmp, "3D Velocity                : (%.2f, %.2f, %.2f)\n", ci.fVelX, ci.fVelY, ci.fVelZ );
			DrawText( 0xffffff, tmp );

			// orientationn
			sprintf( tmp, "3D Front Orientation       : (%.2f, %.2f, %.2f)\n", ci.fFrontX, ci.fFrontY, ci.fFrontZ );
			DrawText( 0xffffff, tmp );
			sprintf( tmp, "3D Top Orientation         : (%.2f, %.2f, %.2f)\n", ci.fTopX, ci.fTopY, ci.fTopZ );
			DrawText( 0xffffff, tmp );

			// reverb, decay
			sprintf( tmp, "Reverberation, Decay       : %.2f, %.2f ", ci.fReverb, ci.fDecay  );
			DrawText( 0xffffff, tmp );
			if ((ci.dwProperties & gosAudio_Decay) )
				DrawText( 0x00ff00, "(Supported)\n");
			else
				DrawText( 0xff0000, "(Not Supported)\n");

			//
			// Channel information
			//
			DrawText( 0xffffff, "\nChannels\n" );
			DrawText( 0xffffff, "=======\n" );
			for( int i=gStartingChannel; i < Environment.soundChannels; i++ )
			{
				DWORD color = 0;
				gosAudio_GetChannelInfo( i, &ci );
				sprintf(tmp, "%02d: ", i );
				DS3DSoundChannel * s = SoundRenderer.m_Channel[i];
				if ( s->m_resourceType != -1 )
				{
					if ((SoundRenderer.m_Channel[i]->m_resourceType == gosAudio_UserMemory || s->m_resourceType == gosAudio_UserMemoryPlayList) && !s->m_resourceName[0])
					{
						strcat( tmp, "<no name>\n" );
					}
					else
					{
 						strcat( tmp, "<");
						strcat( tmp, s->m_resourceName );
						strcat( tmp, ">\n");
					}
					DrawText( 0xffffff, tmp );

					DrawText( 0xffffff, "    (");

					switch(ci.ePlayMode)
					{
					case gosAudio_PlayOnce:
						color = 0x00ff00;
						sprintf( tmp, "Playing  " );
						break;
					case gosAudio_Loop:
						color = 0x00ff00;
						sprintf( tmp, "Looping  " );
						break;
					case gosAudio_Pause:
						color = 0xffff00;
						sprintf( tmp, "Paused   " );
						break;
					case gosAudio_Stop:
						color = 0xff0000;
						sprintf( tmp, "Inactive " );
						break;
					}
					DrawText(color, tmp);

					if ( (ci.dwProperties & gosAudio_Position) ||
						(ci.dwProperties & gosAudio_Velocity) ||
						(ci.dwProperties & gosAudio_MinMaxDistance) )
					{
						if ( SoundRenderer.m_Channel[i]->m_true3D )
							sprintf( tmp, "Hardware 3D " );
						else
							sprintf( tmp, "DX7 Emulated 3D " );
					}
					else
						sprintf( tmp, "2D " );

					if ( s->m_resourceType == gosAudio_CachedFile )
						strcat(tmp, "Cached " );
					else
					if ( s->m_resourceType == gosAudio_StreamedFile || s->m_resourceType == gosAudio_StreamedFP)
						strcat(tmp, "Streamed " );
					else
					if ( s->m_resourceType == gosAudio_UserMemory )
						strcat(tmp, "Memory " );
					else
					if ( s->m_resourceType == gosAudio_UserMemoryPlayList )
						strcat(tmp, "PlayList " );
					DrawText( 0xffffff, tmp );

					if (s->m_resourceType == gosAudio_StreamedMusic)
						sprintf(tmp, "Streamed via DirectShow)");
					else
					{
						if (s->m_Buffer)
						{
							WAVEFORMATEX wf;
							int size = sizeof(wf);
							wGetFormat(s->m_Buffer, &wf, size, NULL);  
							sprintf(tmp, "%2dKHz %2d-bit %s)", wf.nSamplesPerSec/1000, wf.wBitsPerSample,wf.nChannels == 1 ? "Mono" : "Stereo");
						}
					}
 
					DrawText( 0xffffff, tmp );

					CurrentX = StartX + 8*50;

					sprintf(tmp,"%3.0f%c complete\n",ci.fCompletionRatio *100,'%');
					DrawText(0xffffff, tmp);


					// volume, panning, freq
					sprintf( tmp, "    Volume, Panning, Frequency : %.2f, %.2f, %.2f\n", ci.fVolume, ci.fPanning, ci.fFrequency );
					DrawText( 0xffffff, tmp );
		

					if ( (ci.dwProperties & gosAudio_Position) ||
						(ci.dwProperties & gosAudio_Velocity) ||
						(ci.dwProperties & gosAudio_MinMaxDistance) )
					{
						// distance min, max
						float dist = (float) sqrt( pow(ci.fPosX-SoundRenderer.m_Mixer->m_Attributes3D.vPosition.x,2) + pow(ci.fPosY-SoundRenderer.m_Mixer->m_Attributes3D.vPosition.y,2) + pow(ci.fPosZ-SoundRenderer.m_Mixer->m_Attributes3D.vPosition.z,2) );
					    sprintf( tmp, "    Distance Min /  Max        : %.2f / %.2f (Now: %.2f)\n", ci.fMinDistance, ci.fMaxDistance, dist );
						DrawText( 0xffffff, tmp );				   
						
						// position
						sprintf( tmp, "    3D Position                : (%.2f, %.2f, %.2f)\n", ci.fPosX, ci.fPosY, ci.fPosZ );
						DrawText( 0xffffff, tmp );
						sprintf( tmp, "    3D Velocity                : (%.2f, %.2f, %.2f)\n", ci.fVelX, ci.fVelY, ci.fVelZ );
						DrawText( 0xffffff, tmp );

						if (ci.dwProperties & gosAudio_ConeAngles)
						{
							// cone angles
							sprintf( tmp, "    Cone Angles            : (%.2f, %.2f)\n", ci.fConeInner, ci.fConeOuter );
							DrawText( 0xffffff, tmp );
							// cone orientation
							sprintf( tmp, "    Cone Orientation       : (%.2f, %.2f, %.2f)\n", ci.fConeX, ci.fConeY, ci.fConeZ );
							DrawText( 0xffffff, tmp );
						}
					}
				}
				else
				{
					strcat( tmp, "<Empty>\n" );
					DrawText( 0xffffff, tmp );
				}
			}
			break;
		}
//
// Display control manager information
//
		case DbgS_Control:
		{
			char tmp[256];
			DrawText( 0xffffff, "Control Manager\n==============\n\n" );

			//
			// Print System Information
			//

			if (gActive)
				DrawText(0x00ff00, "Application is active, ");
			else
				DrawText(0xff0000, "Application is inactive, ");

			if (gGotFocus)
				DrawText(0x00ff00, "and focused\n\n");
			else
				DrawText(0xff0000, "and has lost focused\n\n");
			
			//
			// Now the mouse information
			//
			float mx,my; 
			DWORD buttons;
			int pWheelDelta;
			float x,y;
			gos_GetMouseInfo( &x, &y, 0, 0, &pWheelDelta, &buttons );
			mx = (x*Environment.screenWidth);
			my = (y*Environment.screenHeight);

			DrawText(0xffffff, "Mouse Informaton:\n");
			char mouseInfo[32];
			sprintf(mouseInfo, "Location: (%.2f, %.2f)\n", mx, my);
			DrawText(0xffffff, mouseInfo);
			sprintf(mouseInfo, "Buttons:  ");
			DrawText(0xffffff, mouseInfo);
			for(int i=0; i < 5; i++)
			{
				if (buttons & (1 << i))
					DrawText(0x00ff00, "o");
				else
					DrawText(0xff0000, "o");
			}
			sprintf(mouseInfo, "\nWheel:    %d\n\n", pWheelDelta);
			DrawText(0xffffff, mouseInfo);





			int num = g_CMData.m_numJoysticks;
			if (num)
			{
				gosJoystick_GetInfo(gWhichCMDevice, &gCMDeviceInfo);
				sprintf( tmp, "Device %d of %d\n\n", gWhichCMDevice+1, num );
				DrawText( 0xffffff, tmp );

				sprintf( tmp, "\"%s\" (", gCMDeviceInfo.lpstrName );
				DrawText( 0xffffff, tmp );

				if (!gCMDeviceInfo.bIsPolled)
				{
					DrawText(0xff0000, "Unpolled");
					DrawText(0xffffff, ")\n\n" );
					break;
				}
				else
				{
					DrawText(0x00ff00, "Polled");
					DrawText(0xffffff, ")\n\n" );
				}


				if (!gCMDeviceInfo.bIsPolled)
					break;

				if (gCMDeviceInfo.nAxes - (gCMDeviceInfo.nSliders + gCMDeviceInfo.nPOVs))
				{
					sprintf(tmp,"Axes (%2d)\n=========\n", gCMDeviceInfo.nAxes - (gCMDeviceInfo.nSliders + gCMDeviceInfo.nPOVs));
					DrawText(0xffffff, tmp );
				}
				for(int i = 0; i < 6; i++)
				{
					if (gCMDeviceInfo.bAxisValid & (1<<i))
					{
						switch(i)
						{
						case 0:
							{
								sprintf( tmp, "X Axis            : %+.3f\n", gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
							}
							break;
						case 1:
							{
								sprintf( tmp, "Y Axis            : %+.3f\n", gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
							}
							break;
						case 2:
							{
								sprintf( tmp, "Z Axis            : %+.3f\n", gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
							}
							break;
						case 3:
							{
								sprintf( tmp, "RX Axis           : %+.3f\n", gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
							}
							break;
						case 4:
							{
								sprintf( tmp, "RY Axis           : %+.3f\n", gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
							}
							break;
						case 5:
							{
								sprintf( tmp, "RZ Axis           : %+.3f\n", gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
							}
							break;
						}
						DrawText( 0xffffff, tmp );
					}
				}
				
				if (gCMDeviceInfo.nSliders) 
				{
					sprintf(tmp, "\nSliders (%2d)\n===========\n", gCMDeviceInfo.nSliders);
					DrawText(0xffffff, tmp);				
				}
				for(i = 6; i < 8; i++)
				{
		 			if (gCMDeviceInfo.bAxisValid & (1<<i))
					{
						sprintf( tmp, "Slider #%d      : %+.3f\n", i - 5, gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i) );
						DrawText( 0xffffff, tmp );
					}
				}

				if (gCMDeviceInfo.nPOVs)
				{
					sprintf(tmp, "\nPOVs (%2d)\n=========\n", gCMDeviceInfo.nPOVs);
					DrawText(0xffffff, tmp);				
				}
				for(i = 8; i < 12; i++)
				{
		 			if (gCMDeviceInfo.bAxisValid & (1<<i))
					{
						sprintf( tmp, "POV #%d            : %+.3f\n", i-7,  gosJoystick_GetAxis(gWhichCMDevice, (enum GOSJoystickAxis) i));
						DrawText( 0xffffff, tmp );
					}
				}
				sprintf( tmp, "\nButtons (%2d - %2d)\n=================\nState             : ", 0, gCMDeviceInfo.nButtons-1);
				DrawText( 0xffffff, tmp );
				for(i = 0; i < (int) gCMDeviceInfo.nButtons; i++)
				{
					if (gosJoystick_ButtonStatus(gWhichCMDevice, i))
						DrawText( 0x00ff00, "o" );
					else
						DrawText( 0xff0000, "o" );
				}
				DrawText( 0xffffff, "\n");
			}
			else
			{
				DrawText( 0xffffff, "No devices detected!\n");
			}

			break;
		}
//
// Realtime display of texture memory for textures that are NOT used in the current frame
//
		case DbgS_UnusedTextures:
		{
			DisplayTextureMemory( 0 );
			break;
		}
//
// Realtime display of USED texture memory
//
		case DbgS_UsedTextures:
		{
			DisplayTextureMemory( 1 );
			break;
		}
//
// Realtime display of textures loaded into video memory
//
		case DbgS_LoadedTextures:
		{
			DisplayTextureMemory( 2 );
			break;
		}
//
// Realtime display of all textures
//
		case DbgS_AllTextures:
		{
			DisplayTextureMemory( 3 );
			break;
		}
//
// Realtime detail-display of texture memory for textures that are NOT used in the current frame
//
		case DbgS_UnusedTextureDetail:
		{
			DisplayTextureMemory( 4 );
			break;
		}
//
// Realtime detail-display of USED texture memory
//
		case DbgS_UsedTextureDetail:
		{
			DisplayTextureMemory( 5 );
			break;
		}
//
// Realtime detail-display of textures loaded into video memory
//
		case DbgS_LoadedTextureDetail:
		{
			DisplayTextureMemory( 6 );
			break;
		}
//
// Realtime detail-display of all textures
//
		case DbgS_AllTextureDetail:
		{
			DisplayTextureMemory( 7 );
			break;
		}
//
// Realtime detail-display of all textures without causing texture uploads
//
		case DbgS_AllTextureNoUpload:
		{
			DisplayTextureMemory( 0xB );
			break;
		}
//
// Display memory manager information
//
		case DbgS_Memory:
		{
			char Buffer[256];
			if( !gExaminingHeap && !gExaminingPool )
			{
				extern MEMORYPOOL* gMemoryPool[MemoryPools];
				DrawText( 0xffffff, "Memory Manager     Show Allocations : " );
				if( ShowAllocations )
					DrawText( 0xffffff, "On " );
				else
					DrawText( 0xffffff, "Off" );
				DrawText( 0xffffff, "    Show Block Info : " );
				if( ShowBlockInfo )
					DrawText( 0xffffff, "On" );
				else
					DrawText( 0xffffff, "Off" );
				DrawText( 0xffffff, "\n==============\n\n" );
			}

			if( Environment.MemoryManager && !gExaminingHeap && !gExaminingPool )
			{
				if( 1==Environment.MemoryManager )
				{
					DrawText( 0xffffff, "Disabled - all allocations going through standard malloc() and free()" );
					break;
				}
				else
				{
					sprintf( Buffer, "Overrun chcking in progress.\n\n%d bytes allocated\n%d allocations\n%dK virtual memory used",MemoryUsed,MemoryAllocations,VirtualMemoryAllocated/1024 );
					DrawText( 0xffffff, Buffer );
					break;
				}
			}

			DWORD bigTotal = 0;
			#ifdef _ARMOR
				DWORD TotalReserved = LargeMemoryAllocations*(gMemoryStackWalkLevel*4+sizeof(LARGEBLOCK)+sizeof(LARGEBLOCKHEADER)+4);
			#else
				DWORD TotalReserved = LargeMemoryAllocations*(gMemoryStackWalkLevel*4+sizeof(LARGEBLOCK)+sizeof(LARGEBLOCKHEADER));
			#endif

			if( gExaminingHeap )
			{
#ifdef _DEBUG
				DrawText( 0xffffff, "Memory Manager" );
				DrawText( 0xffffff, "\n==============\n\n" );
				DrawText( 0xffffff, "Breakdown of heap ");
				DrawText( 0xffffff, "\"");
				DrawText( 0xffffff, gExaminingHeap->Name );
				if( gExaminingHeap->DXAllocations )
				{
					char Buffer[256];
					sprintf( Buffer, "\"    DXAllocated=%dk in %d blocks\n\n", gExaminingHeap->DXAllocated/1024, gExaminingHeap->DXAllocations );
					DrawText( 0xffffff, Buffer );
				}
				else
					DrawText( 0xffffff, "\"\n\n");
				DisplayHeapItems(gExaminingHeap);
#endif
				break;
			}

			if( gExaminingPool )
			{
#ifdef _DEBUG
				DrawText( 0xffffff, "Memory Manager" );
				DrawText( 0xffffff, "\n==============\n\n" );
				DrawText( 0xffffff, "Breakdown of ");
				char Buffer[16];
				sprintf( Buffer, "%d", gExaminingPool->wBlockSize );
				DrawText( 0xffffff, Buffer );
				DrawText( 0xffffff, " byte pool\n\n");
				DisplayPoolItems(gExaminingPool);
#endif
				break;
			}

			DebugBlockHeapStart=DbChrY+2;
			DWORD BlocksAllocated=0;
			DWORD AlignmentCost=0;
			DWORD DecommittedMemory=0;

			for(int i=0; i < MemoryPools; i++)
			{
				WORD blocksize = PoolSizes[i];
				DWORD blocksused = 0;
				DWORD userbytes = 0;
				DWORD bytesreserved = 0;
				DWORD TotalBlocks=0;
				MEMORYPOOL* itr = gMemoryPool[i];
				while(itr)
				{
					if( AgressiveManager )
					{
						for( int t0=0; t0<16; t0++ )
						{
							if( 0==itr->AllocCount[t0] )
							{
								DecommittedMemory+=4;
							}
						}
					}
					TotalBlocks+=itr->wTotalBlocks;
					blocksused += itr->FreeBlockPtr;
					userbytes += itr->wUserBytes;
					bytesreserved += 65536;
					TotalReserved += 65536-itr->wUserBytes;
					itr = itr->pNext;
				}

				AlignmentCost  +=blocksused*blocksize-userbytes;
				BlocksAllocated+=blocksused;
				
				if( ShowBlockInfo )
				{
					sprintf
						(		   
							Buffer, 
							"%7d/%7d blocks of %5dk %4db pool used: %5dk/%5dk average: %4db\n",
							blocksused,
							TotalBlocks,
							bytesreserved / 1024,
							blocksize,
							userbytes / 1024,
							(blocksused * blocksize) /1024,
							blocksused ? (DWORD) (blocksize * ((float) userbytes / (float) (blocksused * blocksize)))  : 0
						);
					DrawText(0xffffff, Buffer);
				}
			}

			gos_Heap* hptr = AllHeaps;
			while(hptr)
			{
				bigTotal += hptr->BytesAllocated;			  
				TotalReserved += hptr->BytesAllocated;
				hptr = hptr->pNext;
			}

			if( ShowBlockInfo )
				DrawText(0xffffff, "\n");

			sprintf(Buffer, "% 6dk in %d blocks larger than %d bytes\n", (LargeMemorySize) /1024, LargeMemoryAllocations, LargestMemoryBlock );
			DrawText(0xffffff, Buffer);
				
			sprintf(Buffer, "% 6dk in %d blocks smaller than %d bytes\n",  (bigTotal-LargeMemorySize-SystemMemoryAllocated)/1024, BlocksAllocated, LargestMemoryBlock );
			DrawText(0xffffff, Buffer);

			sprintf( Buffer, "% 6dk in DirectX sound/textures (%d surfaces)\n", SystemMemoryAllocated/1024, SystemMemoryAllocs );
			DrawText(0xffffff,Buffer);

			DWORD AGPMemory=0;

			CTexInfo *pTexInfo;
			for( pTexInfo = CTexInfo::FirstTexture(); pTexInfo; pTexInfo = pTexInfo->NextTexture() )
			{
				if( pTexInfo->InAGP() )
				{
					AGPMemory+=pTexInfo->MemoryUsage(true,gmt_VidMem);
				}
			}

			sprintf( Buffer, "% 6dk in AGP memory\n", AGPMemory/1024 );
			DrawText(0xffffff,Buffer);

			sprintf( Buffer, "% 6dk in PDB debugging info\n", PDBMemory/1024 );
			DrawText(0xffffff,Buffer);

			DWORD nVSurface=0;
			DWORD nVMem=0;
			GetnVidiaCacheSize( nVSurface, nVMem );			
			sprintf( Buffer, "% 6dk in nVidia cache (%d surfaces)\n", nVMem/1024, nVSurface );
			DrawText(0xffffff,Buffer);

			sprintf( Buffer, "% 6dk in exe / static data and dlls\n", StartupMemory/1024 );
			DrawText(0xffffff,Buffer);

			bigTotal+=StartupMemory + PDBMemory + AGPMemory + nVMem;
			TotalReserved+=StartupMemory + PDBMemory + AGPMemory + nVMem;

			if( AgressiveManager )
			{
				sprintf(Buffer, "-------\n% 6dk Total usage     (Calls to gos_Malloc/gos_Free this frame: %d/%d)\n% 6dk Total reserved  (Decommitted: %dk  Alignment cost: %dk)\n\n", bigTotal/1024, MemoryAllocs, MemoryFrees, TotalReserved/1024, DecommittedMemory, AlignmentCost/1024 );
			}
			else
			{
				sprintf(Buffer, "% 6dk Total usage     (Calls to gos_Malloc/gos_Free this frame: %d/%d)\n% 6dk Total reserved  (Alignment cost: %dk)\n\n", bigTotal/1024, MemoryAllocs, MemoryFrees, TotalReserved/1024, AlignmentCost/1024 );
			}
			DrawText(0xffffff, Buffer);

			if( ShowAllocations )
			{
				DrawText( 0xffffff, "Heap                       Small    SmSize      Big   BigSize  DirectX    DXSize\n" );
				DrawText( 0xffffff, "======================   ======= =========  ======= =========  ======= =========\n" );
			}
			else
			{
				DrawText( 0xffffff, "Heap                    Allocs    Frees     Delta     Current        Peak  %Full\n" );
				DrawText( 0xffffff, "======================  ======   ======  ========    ========    ========  =====\n" );
			}
			gos_Heap* pHeap=AllHeaps;
			gHeapOnLineIndex = 0;
			memset(DebugHeapOnLine,0,sizeof(DebugHeapOnLine));
			DebugStartLineCounter = 0;
			DebugHeapStart=DbChrY+2;
			while(pHeap)
			{
				DebugDisplayHeap(pHeap);
				pHeap = pHeap->pNext;
			}
			DebugHeapEnd=DbChrY+2;
			break;
		}
//
// Display CPU Statistics
//
		case DbgS_CPUStats:
		{
			DrawText( 0xffffff, "Processor Statistics\n====================\n" );
			char Buffer[256];
			sprintf( Buffer, "   Current    Average    Minimum    Maximum Graph     Name\n\n" );
			DrawText( 0xffffff, Buffer );
			Stat* pStat=pStatistics;

			while( pStat && DbChrY<40 )
			{
				if( gos_cycledata==pStat->Type )
				{
					char floatBuffer[2];
					char fBuffer[128];
					floatBuffer[1]=0;

					if( pStat->Flags&Stat_1DP )
						floatBuffer[0]='1';
					else if( pStat->Flags&Stat_2DP )
						floatBuffer[0]='2';
					else if( pStat->Flags&Stat_3DP )
						floatBuffer[0]='3';
					else
						floatBuffer[0]='0';

					if( pStat->Flags&Stat_Total )
					{
						sprintf( fBuffer, "%% 10.%sf                                    %%s   %%s\n",floatBuffer );
						sprintf( Buffer, fBuffer,
							(double)GetHistory(pStat,GraphHead),
							((pStat->Flags&Stat_Graph) ? "Y" : "n"),
							pStat->Name );
					}
					else
					{
						sprintf( fBuffer, "%% 10.%sf %% 10.%sf %% 10.%sf %% 10.%sf   %%s   %%s\n",floatBuffer,floatBuffer,floatBuffer,floatBuffer );
						if( 0.0==pStat->Count )
						{
							sprintf( Buffer, fBuffer,
								(double)0.0,
								(double)0.0,
								(double)0.0,
								(double)0.0,
								((pStat->Flags&Stat_Graph) ? "Y" : "n"),
								pStat->Name );
						}
						else
						{
							sprintf( Buffer, fBuffer,
								(double)GetHistory(pStat,GraphHead),
								(double)pStat->Total/pStat->Count,
								(double)pStat->Minimum,
								(double)pStat->Maximum,
								((pStat->Flags&Stat_Graph) ? "Y" : "n"),
								pStat->Name );
						}
					}
					DrawText( 0xffffff, Buffer );
				}
				pStat=pStat->pNext;
			}

			DrawText( 0xffffff, "\nAvailable Performance Registers - Click to Enable\n\n" );

//
// Code to enum registers
//
			if( PerfCounters )
			{
				PerfYStart=DbChrY;
				EnumeratePerformanceMonitors( PerfCounterEnum );
				PerfYEnd=DbChrY;
			}
			else
			{
				PerfYStart=PerfYEnd=0;
				DrawText( 0xffffff, "None, library could not be initialized\n" );
			}
			break;
		}
//
//	History of last 32 sounds played
//
		case DbgS_PlayHistory:
		{
			DrawText( 0xffffff, "Sound History\n=============\n\nVerbose Debugging:    " );
			if (SoundRenderer.m_verboseDebugger)
			{
				DrawText(0x00ff00, "On\n\n");
			}
			else
			{
				DrawText(0xff0000, "Off\n\n");
			}

			DrawText( 0xffffff, "TimeStamp    Action          Channel    Resource File\n=========    ============    =======    ========================================\n\n" );

			int i = SoundRenderer.m_playHistoryItr-1;
			if (i < 0)
				i = 31;

			LinkedListIterator<SoundResource *> itr 
				= LinkedListIterator<SoundResource *> (g_soundResources);

			do
			{
				if (SoundRenderer.m_playHistory[i])
				{
					if (strstr(SoundRenderer.m_playHistory[i], "Interrupt"))
						DrawText(0xff0000,SoundRenderer.m_playHistory[i]);
					else
						DrawText(0xffffff,SoundRenderer.m_playHistory[i]);
				}
				i--;
				if (i < 0)
					i = 31;
			} while (i != SoundRenderer.m_playHistoryItr);
			break;
		}
//
//	List of currently loaded sound resources
//
		case DbgS_SoundResource:
		{
			char Buffer[128];
			int counter = 0;
			DrawText( 0xffffff, "Available Sound Resources\n=========================\n\n" );
			LinkedListIterator<SoundResource *> itr 
				= LinkedListIterator<SoundResource *> (g_soundResources);
			itr.Head();
			SoundResource * sr;
		
			while((sr=itr.ReadAndNext()) != NULL)
			{
#if 0
				int times_used = 0;
				for( int i=0; i < Environment.soundChannels; i++ )
				{
					if (SoundRenderer.m_Channel[i] && SoundRenderer.m_Channel[i]->m_currentSoundResource == sr)
						times_used++;
				}
#endif
				int times_used = 0;
					times_used = sr->duplicateCount;

				switch(sr->m_Type)
				{
					case gosAudio_CachedFile:
						sprintf(Buffer,"%2d: (Cached   %s, Duped: %2dx) %5.1fsec / %4d kb <%s>\n", counter, sr->m_WaveFormatEx->nChannels == 1 ? "  Mono" : "Stereo", times_used, sr->m_fDuration, sr->masterDesc.dwBufferBytes / 1024, GetFileTrunc(sr->m_FileName));
						break;							   
					case gosAudio_UserMemory:																																													   
						sprintf(Buffer,"%2d: (Memory   %s, Duped: %2dx) %5.1fsec / %4d kb <%s>\n", counter, sr->m_WaveFormatEx->nChannels == 1 ? "  Mono" : "Stereo", times_used, sr->m_fDuration, sr->masterDesc.dwBufferBytes / 1024, sr->m_FileName ? GetFileTrunc(sr->m_FileName) : "<no name>");
						break;									   																	 
					case gosAudio_StreamedFile:
						sprintf(Buffer,"%2d: (Streamed %s)              %5.1fsec / %4d kb <%s>\n", counter, sr->m_WaveFormatEx->nChannels == 1 ? "  Mono" : "Stereo", sr->m_fDuration, sr->m_suggFormat.nAvgBytesPerSec * 3, sr->m_FileName ? GetFileTrunc(sr->m_FileName) : "<no name>");
						break;
					case gosAudio_StreamedFP:
						sprintf(Buffer,"%2d: (Streamed %s)              %5.1fsec / %4d kb <%s>\n", counter, sr->m_WaveFormatEx->nChannels == 1 ? "  Mono" : "Stereo", sr->m_fDuration, sr->m_suggFormat.nAvgBytesPerSec * 3, sr->m_FileName ? GetFileTrunc(sr->m_FileName) : "<no name>");
						break;
					case gosAudio_StreamedMusic:
						sprintf(Buffer,"%2d: (DShow    %s)              %5.1fsec / %4d kb <%s>\n", counter, sr->m_WaveFormatEx->nChannels == 1 ? "  Mono" : "Stereo", ((float)sr->m_hMusic->m_fDurationSec) / 10000000.0f, 32768 / 1024, GetFileTrunc(sr->m_FileName));
						break;
					case gosAudio_UserMemoryPlayList:
						sprintf(Buffer,"%2d: (PlayList %s)              %5.1fsec / %4d kb <%s>\n", counter, sr->m_WaveFormatEx->nChannels == 1 ? "  Mono" : "Stereo", sr->m_fDuration, 32768 / 1024, sr->m_FileName ? GetFileTrunc(sr->m_FileName) : "<no name>");
						break;
				}
				DrawText(0xffffff,Buffer);

				counter++;
			}

			DrawText( 0xffffff, "\nRemnant Resources Left in Channels\n==================================\n\n" );
			for( int i=0; i < Environment.soundChannels; i++ )
			{
				DS3DSoundChannel * s = SoundRenderer.m_Channel[i];
				if ( s && s->m_resourceType != -1 && !(s->m_currentSoundResource))
				{
					WAVEFORMATEX wf;
					int size = sizeof(wf);
					wGetFormat(s->m_Buffer,&wf,size,NULL);
					switch(s->m_resourceType)
					{
						case gosAudio_CachedFile:
							sprintf(Buffer,"%2d: (Cached   %s) %5.1fsec / %4d kb <%s>\n", counter, wf.nChannels == 1 ? "  Mono" : "Stereo", s->m_fDuration, s->m_cbBufSize, GetFileTrunc(s->m_resourceName));
							break;							   
						case gosAudio_UserMemory:
							sprintf(Buffer,"%2d: (Memory   %s) %5.1fsec / %4d kb <%s>\n", counter, wf.nChannels == 1 ? "  Mono" : "Stereo", s->m_fDuration, s->m_cbBufSize, GetFileTrunc(s->m_resourceName));
							break;																											 
					}
					DrawText(0xffffff,Buffer);
					counter++;
				}
			}
			break;
		}

//
//
//
		case DbgS_NetworkX:
		{
			DrawText( 0xffffff, "Network Packet History\n======================\n\n" );

			DrawText( 0xffffff, " Frame   TimeStamp  Type        To/From    Size  Content\n" );

			PacketLogging* pLast=0;
			PacketLogging* pThis=pPacketLog;
			for( int t0=0; t0<35; t0++ )
			{
				if( 0==pThis )
					break;
				char Buffer[128];
				if( pThis->Size )
				{
					sprintf( Buffer, "%07d % 10.4f  %s  %s  %3d  ", pThis->FrameNumber, pThis->TimeStamp, pThis->Type, pThis->Player, pThis->Size );
					BYTE* Data=(BYTE*)&pThis->Data;
					for( int t0=0; t0<pThis->Size; t0++ )
					{
						char Buf1[4];
						sprintf( Buf1, "%02X ",*Data++ );
						strcat( Buffer, Buf1 );
						if( 7==t0 )
							break;
					}
				}
				else
				{
					sprintf( Buffer, "%07d % 10.4f  %s  %s", pThis->FrameNumber, pThis->TimeStamp, pThis->Type, pThis->Player );
				}
				strcat( Buffer, "\n" );				
				DrawText( 0xffffff, Buffer );
				pLast=pThis;
				pThis=pThis->pNext;
			}
			break;
		}
//
// Display files currently open
//
		case DbgS_File:
		{
			DrawText( 0xffffff, "Files currently open" );

			if( Environment.debugLog && *Environment.debugLog!=0 )			// SPEW to a file?
			{
				char Buffer[MAX_PATH];
				sprintf( Buffer, "    - SPEW %d items to %s", SpewWrites, Environment.debugLog );
				DrawText( 0xffffff, Buffer );
			}

			DrawText( 0xffffff, "\n====================\n\n" );

			if( NumberFilesOpen==0 && NumberMemoryMappedFilesOpen==0 )
			{
				DrawText( 0xffffff, "None\n" );
			}		
			else
			{
				char Buffer[256];
				char drive[_MAX_DRIVE];
				char dir[_MAX_DIR];					// Replace extension with .bmp
				char fname[_MAX_FNAME];
				char ext[_MAX_EXT];

				MemoryMappedFiles* pMM=ListofMemoryMappedFiles;
				while( pMM )
				{
	   				_splitpath( pMM->Name, drive, dir, fname, ext );
					sprintf( Buffer, "Memory mapped %s%s - Size %d bytes\n",fname,ext,pMM->Size );
					DrawText( 0xffffff, Buffer );
					pMM=pMM->pNext;
				}

				gosFileStream* pNF=ListOfFiles;
				while( pNF )
				{
	   				_splitpath( pNF->m_Filename, drive, dir, fname, ext );
					sprintf( Buffer, "File %s%s - %s %d bytes\n",fname,ext,(pNF->m_writeEnabled ? "written" : "read"), pNF->BytesTransfered );
					DrawText( 0xffffff, Buffer );
					pNF=pNF->pNext;
				}
			}
//
// Display history of file functions
//
			DrawText( 0xffffff, "\n Frame      Time Function\n============================\n\n" );

			DWORD Index=CurrentFileInfo;
			for( int t0=0; t0<32 && DbChrY<40 ; t0++ )
			{
				Index=(Index-1)&31;
				DrawText( 0xffffff, &FileInfo[Index][0] );
				DrawText( 0xffffff, "\n" );
			}
			break;
		}
//
//
//
		case DbgS_SoundSpatial: // Sound Spatialization Graph
		{
			CurrentX = 4 + DbTopX;
			CurrentY = 18 + DbTopY;
			DrawText( 0xffffff, "Sound Spatialization Graph\n" );
			DrawText( 0xffffff, "==========================\n\n" );
			DrawText( 0xffffff, "Legend:                                                Position:\n");
			DrawText( 0xffffff, "======                                                 ========\n");
			char tmpstr[128];
			sprintf(tmpstr,"                                                    Zoom Level: %.0f%c\n", (float) 1.0f/gSoundSpatDebugZoom*100.0f, '%');
			DrawText( 0xffffff, tmpstr );
			DrawText( 0xffffff, "                                                      PgUp / PgDn\nq" );

//
// 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, 1 );
			if( (DebuggerAlpha&0xff000000)==0xff000000 )
				gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
			else
				gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
//
// Draw window surround and title bar
//
			DrawSquare(DbTopX+(DbSizeX/2),DbTopY+(DbSizeY/4), (DbSizeX/2)-4, (DbSizeX/2)-4,0x0000A0);
			int centerx = DbTopX+(DbSizeX)-(DbSizeX/4);
			int centery	= DbTopY+(DbSizeY/4)+(DbSizeX/4)-3;

			DrawLines( centerx-(DbSizeX/4)-1, centery+3, centerx+(DbSizeX/4)-5, centery+3, 0x00ffffff  + DebuggerAlpha);
			DrawLines( centerx, centery-(DbSizeX/4)+3, centerx, centery+(DbSizeX/4),0x00ffffff + DebuggerAlpha);

			centerx-=4;

			InitTextDisplay();
			CurrentX = centerx;
			CurrentY = centery;

			DrawText(0xff0000,")(\n");

			char chanID[3];
			gosAudio_PlayMode pm;
			gosAudio_ChannelInfo ci;

			float OrientZ = SoundRenderer.m_Mixer->m_Attributes3D.vOrientFront.z;
			float OrientX = -SoundRenderer.m_Mixer->m_Attributes3D.vOrientFront.x;
			for( int i=0; i < Environment.soundChannels && i < 32; i++ )
			{
				DS3DSoundChannel * s = SoundRenderer.m_Channel[i];
				gosAudio_GetChannelInfo(i, &ci);
				pm = gosAudio_GetChannelPlayMode(i);
				if (s && s->m_Buffer3D)
				{
					float x = (s->m_Attributes3D.vPosition.x - SoundRenderer.m_Mixer->m_Attributes3D.vPosition.x) / gSoundSpatDebugZoom;
					float y = (s->m_Attributes3D.vPosition.z - SoundRenderer.m_Mixer->m_Attributes3D.vPosition.z) / gSoundSpatDebugZoom;

					float tmpx = x;
					x = x*OrientZ + y*OrientX;
					y = y*OrientZ - tmpx*OrientX;

					CurrentX = float2long(centerx+x);
					CurrentY = float2long(centery-y);

					if (CurrentX > centerx-(DbSizeX/4)-5 && CurrentX < centerx+(DbSizeX/4)-9 &&
						CurrentY > centery-(DbSizeX/4)-1 && CurrentY < centery+(DbSizeX/4)-4)
					{
						sprintf(chanID,"%02d\n",i);
						switch(pm)
						{
						case gosAudio_Stop:
						case gosAudio_Pause:
							DrawText(0xff0000, chanID);
							break;
						case gosAudio_PlayOnce:
							{
								int c = float2long(255.0f * ci.fCompletionRatio);
								DrawText((c<<16)+((255-c)<<8), chanID);
							}
							break;
						case gosAudio_Loop:
							DrawText(0x00ff00, chanID);
							break;
						}
					}
				}

				// print the channel# / resource name in the proper place
				CurrentX = 4 + DbTopX + ( (i/40)*((DbSizeX/4) + (DbSizeX/2)) );
				CurrentY = 18 + DbTopY + ((i+6)*8);
				if (s && s->m_resourceName)
					sprintf(tmpstr, "%02d: %s\n", i,GetFileTrunc(s->m_resourceName));
				else
					sprintf(tmpstr, "%02d: <none loaded>\n", i);
				
				if (s && s->m_Buffer3D)
				{
					switch(pm)
					{
					case gosAudio_Stop:
					case gosAudio_Pause:
						DrawText(0xff0000, tmpstr);
						break;
					case gosAudio_PlayOnce:
						{
							int c = float2long(255.0f * ci.fCompletionRatio);
							DrawText((c<<16)+((255-c)<<8), tmpstr);
						}
						break;
					case gosAudio_Loop:
						DrawText(0x00ff00, tmpstr);
						break;
					}
				}
				else
				{
					DrawText(0xffffff, tmpstr);
				}
			}

			break;
		}
		default:
			STOP(( "Bad mode" ));
	}
}


#else
void UpdateDebugWindow()
{
}
#endif
