#include "pch.hpp"
//==========================================================================//
// File:	 Font3D.cpp														//
// Project:  GameOS					                                        //
// Contents: Font3D drawing routines										//
//--------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                            //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL              //
//==========================================================================//
#include "Font3D.hpp"
#include "DXRasterizer.hpp"
#include "3DRasterizer.hpp"
#include "Texture Manager.hpp"
#include "mbstring.h"

// for memory tracking
extern HGOSHEAP DefaultHeap;
extern volatile __int64 TimeFont3D;
extern DWORD gDisableText;
extern char gDefaultGDIFont[];
extern BYTE LeadByteTable[256];
extern BYTE UppertoLowerTable[256];
float MagicOffset=0.5f-(1.0f/16.0f);

bool FontNoCache=0;
bool HasDBCS(char *);
void GetChrSize( int* Width, int* Height, BYTE Chr );
DWORD GetNextLineLen( BYTE* pText, BYTE** pNextLine );

DWORD GetNextWordLen( BYTE* pText );
void HandleTags(char ** lplpMessage);

FontInfo*	Fonts=0;			// Linked list of fonts loaded

//
// Current settings for print routines
//
HGOSFONT3D	FontHandle=0;
DWORD		FontColor=0;
bool		FontStartOfLine=0;
float		FontSize=0;
bool		OddSize=0;					// OddSize is true when FontSize is not 1.0f (optimization)
bool		FontWordWrap=0;
DWORD		FontWrapType=0;
bool		FontProportional=0;
bool		FontBold=0;
bool		FontItalic=0;
bool		FontDisableCodes=0;

int FontX,FontY;
float FontMinX,FontMinY,FontMaxX,FontMaxY;
float TexOffset;
DWORD MarginBuffer=0;
DWORD CurrentFontTexture=-1;

int LargestHeight = 0;

float FontScale=1.0f;
//
// Vertices used for text
//
gos_VERTEX FontVertices[128];
DWORD FontIndex;
DWORD FontClipping;

extern void TextDrawDBCS(char * Message);
extern void GDIGetStringLen (char * msg, DWORD * w, DWORD * h);






//
// FontHandle	- This must be a previously loaded font (0=default GOS 6*8 fixed space font)
// Foreground	- Foreground color (Background color should be done by drawing a quad)
// Size			- Size to scale font by, 1.0 is normal, 2.0 would be double size. Negative number translate to "point size"
// WordWrap		- If true words will be wraped within region, otherwise clipped
// Proportional	- If false words are displayed fixed pitch
// Bold         - If true text is bolded (This displays the text twice, shifted a pixel so is half the speed as normal text) - To display a lot of text use a bold font.
// Italic		- If true text is italiced
// WrapType		- 0=Left aligned, 1=Right aligned, 2=Centered
// DisableCodes - Disable all embedded codes
//
void __stdcall gos_TextSetAttributes( HGOSFONT3D Handle, DWORD Foreground, float Size, bool WordWrap, bool Proportional, bool Bold, bool Italic, DWORD WrapType, bool DisableEmbeddedCodes )
{
	FontHandle=Handle;
	#ifdef _DEBUG
	if( 0==FontHandle || FontHandle->MagicNumber!=0x4587abba )
		STOP(( "Invalid font handle" ));
	#endif
	FontColor=Foreground;

	if (Size < 0)
	{
/*
		if (!FontHandle->hFontTTF)
		{
			//jcem: below is original code
			//FontSize = FontHandle->Height / ((FontHandle->Height * (2.0f/3.0f)) / -Size);

			//jcem: above line is same as below lines
			//FontSize = (FontHandle->Height * -Size) / (FontHandle->Height * (2.0f/3.0f));
			//FontSize = -Size / (2.0f/3.0f);
			//FontSize = -Size * 3.0f / 2.0f;
			//FontSize = -Size * 1.5f;

			//jcem: so commented out...
		}
		else
*/
		if (Size < -1000) {
			FontSize = -Size - 1000; // i.e. -1050 means 50pixel height...
		} else
			// jcem: in below line, if -Size means point size then screen DPI is 108(==72 * 1.5)
			FontSize = -Size * 1.5f;
	}
	else
	{
/*
		if (!FontHandle->hFontTTF)
			FontSize = (float)  FontHandle->Height * Size;
		else
*/
			FontSize = FontHandle->Height * Size;
	}

	FontScale = FontSize / (FontHandle->Height);

	if (FontScale != 1.0f)
		OddSize = true;
	else
		OddSize = false;

	FontDisableCodes=DisableEmbeddedCodes;
	FontWordWrap=WordWrap;
	FontWrapType=WrapType;
	FontProportional=Proportional;
	FontBold=Bold;
	FontItalic=Italic;
	MarginBuffer=0;
	if( FontItalic )
		MarginBuffer=float2long((FontSize/((float)FontHandle->Height))*FontHandle->Width*0.25f);
}


//
// Set the current position of the cursor. (Screen pixels)
//
void __stdcall gos_TextSetPosition( int XPosition, int YPosition )
{
	FontX=XPosition;
	FontY=YPosition;
}

//
// Returns the current print position
//
void __stdcall gos_TextGetPrintPosition( int* XPosition, int* YPosition )
{
	*XPosition=FontX;
	*YPosition=FontY;
}


//
// Set the region that the cursor will be clipped to (in screen pixels)
// The text will be clipped or wrap when the right edge is reached
//
void __stdcall gos_TextSetRegion( int Left, int Top, int Right, int Bottom )
{
	gosASSERT( Right>Left && Bottom>Top );
	FontMinX=(float)Left;
	FontMinY=(float)Top;
	FontMaxX=(float)Right;
	FontMaxY=(float)Bottom;
}


//
// Returns pixel height and width of string (Height=Height of charcters * number of /n found, Width=Width of longest line)
//
void gos_TextStringLength( DWORD* Width, DWORD* Height, const char *Message, ... )
{
	BYTE textBufferArray[8192];			// Buffer for text display
	char * textbuffer = (char *)textBufferArray;
	FontInfo * origFont = FontHandle;
	float origSize = FontSize;
//
// Default message empty
//
	*Height=0;
	*Width=0;
//
// Empty message?
//
	if( !Message || 0==*Message )
		return;
//
// Fill buffer with text (check for overrun)
//
	textbuffer[sizeof(textBufferArray)-1]=(char)4;
    _vsnprintf( textbuffer, sizeof(textBufferArray), Message, (char*)(&Message+1) );
	if( textBufferArray[sizeof(textBufferArray)-1]!=(char)4 )
		STOP(( "String too long" ));
//
// Look for tags (Only valid at start of text)
//
	if( textbuffer[0]=='/' )
		HandleTags(&textbuffer);

#ifdef _DEBUG
	if (!FontHandle)
		STOP(( "No font specified - cannot compute print dimensions" ));
#endif

	if (OddSize)
		FontScale = FontSize / (FontHandle->Height);

	if( FontHandle->hFontTTF )
	{
		GDIGetStringLen((char*) textbuffer, Width, Height);
		return;
	}

//
// Find length of all lines
//
	*Height=0;
	DWORD LongestLine=0;

	BYTE* pText=(BYTE*)textbuffer;
	BYTE* pNext;

	do
	{
		DWORD CurrentLine=GetNextLineLen( pText, &pNext );
		
		*Height+=LargestHeight;

		if( CurrentLine>LongestLine )
			LongestLine=CurrentLine;

		if (pText == pNext)
			break;
		else
			pText=pNext;
	
	} while( *pText );

	*Width=LongestLine;
//
// Allow for overhang on last italic character
//
	if( *Width )
	{
		//if( FontProportional )	 // Andy, the math hear is somewhat suspect...
		//	*Width-=1;				 // What is the reasoning behind this? It causes problems....
		if( FontItalic )
			*Width+=float2long((FontSize/(float)FontHandle->Height)*FontHandle->Width*0.25f);
	}
//
// Check message did not overwrite buffer
//
	if( pText-((BYTE*)textbuffer) >= sizeof(textBufferArray) )
		STOP(( "Message too long" ));
	FontHandle = origFont;
	FontSize = origSize;
}




//
// Normally this is a rect, but 0 and 3 can be shifted in the X (1,3 are always extreems)
//
// Clip this text character to the draw region and draw it
//
void FontDrawQuad( bool isBold ) 
{
//
// Quick check for whole character inside region
//
	if(	FontVertices[FontIndex+1].x >= FontMinX &&
		FontVertices[FontIndex+3].x <= FontMaxX &&
		FontVertices[FontIndex+3].y >= FontMinY &&
		FontVertices[FontIndex+1].y <= FontMaxY )
	{
		if( Compatibility3D&TextureOffset )
		{
			FontVertices[FontIndex+0].u+=TexOffset;
			FontVertices[FontIndex+0].v+=TexOffset;
			FontVertices[FontIndex+1].u+=TexOffset;
			FontVertices[FontIndex+1].v+=TexOffset;
			FontVertices[FontIndex+2].u+=TexOffset;
			FontVertices[FontIndex+2].v+=TexOffset;
			FontVertices[FontIndex+3].u+=TexOffset;
			FontVertices[FontIndex+3].v+=TexOffset;
		}
		else
		{
			FontVertices[FontIndex+0].x-=MagicOffset;
			FontVertices[FontIndex+0].y-=MagicOffset;
			FontVertices[FontIndex+1].x-=MagicOffset;
			FontVertices[FontIndex+1].y-=MagicOffset;
			FontVertices[FontIndex+2].x-=MagicOffset;
			FontVertices[FontIndex+2].y-=MagicOffset;
			FontVertices[FontIndex+3].x-=MagicOffset;
			FontVertices[FontIndex+3].y-=MagicOffset;
		}
//
// Cater for blade pixel doubled mode
//
		float maxX=(float)Environment.screenWidth;
		float maxY=(float)Environment.screenHeight;

		if( Environment.Renderer==3 && RenderStates[gos_State_MonoEnable]&256 )
		{
			maxX/=2.0f;
			maxY/=2.0f;
		}	
//
// Quick check for whole quad off screen
//
		if( FontVertices[FontIndex+1].x+1.0f >= maxX ||
			FontVertices[FontIndex+3].x <= 0.0f ||
			FontVertices[FontIndex+3].y >= maxY ||
			FontVertices[FontIndex+1].y <= 0.0f )
			return;
//
// Quick check for whole quad on screen
//
		if(	FontVertices[FontIndex+1].x < 0.0f ||
			FontVertices[FontIndex+3].x+1.0f > maxX ||
			FontVertices[FontIndex+3].y < 0.0f ||
			FontVertices[FontIndex+1].y > maxY )
		{
			FontClipping=1;
		}
		FontIndex+=4;

		if (isBold)
		{
			FontVertices[FontIndex] = FontVertices[FontIndex-4];
			FontVertices[FontIndex+1] = FontVertices[FontIndex-3];
			FontVertices[FontIndex+2] = FontVertices[FontIndex-2];
			FontVertices[FontIndex+3] = FontVertices[FontIndex-1];
			FontVertices[FontIndex+0].x += 1.0f;
			FontVertices[FontIndex+1].x += 1.0f;
			FontVertices[FontIndex+2].x += 1.0f;
			FontVertices[FontIndex+3].x += 1.0f;
			FontIndex+=4;
		}
	}
	else
	{
		// the character is at least partially outside its region, see if it is completely off
		if(	FontVertices[FontIndex+1].x > FontMaxX ||
			FontVertices[FontIndex+3].x < FontMinX ||
			FontVertices[FontIndex+3].y > FontMaxY ||
			FontVertices[FontIndex+1].y < FontMinY )
			return;

		// need to clip the character to the region
		float du = FontVertices[FontIndex+3].u - FontVertices[FontIndex+1].u;
		float dv = FontVertices[FontIndex+3].v - FontVertices[FontIndex+1].v;
		float dx = FontVertices[FontIndex+3].x - FontVertices[FontIndex+1].x;
		float dy = FontVertices[FontIndex+3].y - FontVertices[FontIndex+1].y;

		if( FontVertices[FontIndex+1].x < FontMinX )
		{
			float cu = du * (FontMinX-FontVertices[FontIndex+1].x) / dx;
			FontVertices[FontIndex+1].u += cu;
			FontVertices[FontIndex+0].u += cu;
			FontVertices[FontIndex+1].x = FontMinX;
			FontVertices[FontIndex+0].x = FontMinX;
		}
		if( FontVertices[FontIndex+3].y < FontMinY )
		{
			float cv = dv * (FontMinY-FontVertices[FontIndex+3].y) / dy;
			FontVertices[FontIndex+3].v += cv;
			FontVertices[FontIndex+0].v += cv;
			FontVertices[FontIndex+3].y = FontMinY;
			FontVertices[FontIndex+0].y = FontMinY;
		}
		if( FontVertices[FontIndex+3].x > FontMaxX )
		{
			float cu = du * (FontVertices[FontIndex+3].x-FontMaxX) / dx;
			FontVertices[FontIndex+3].u -= cu;
			FontVertices[FontIndex+2].u -= cu;
			FontVertices[FontIndex+3].x = FontMaxX;
			FontVertices[FontIndex+2].x = FontMaxX;
		}
		if( FontVertices[FontIndex+1].y > FontMaxY )
		{
			float cv = dv * (FontVertices[FontIndex+1].y-FontMaxY) / dy;
			FontVertices[FontIndex+1].v -= cv;
			FontVertices[FontIndex+2].v -= cv;
			FontVertices[FontIndex+1].y = FontMaxY;
			FontVertices[FontIndex+2].y = FontMaxY;
		}
		// recurse *once* into this function to display the clipped character
		FontDrawQuad(isBold);
	}
}



//
// Draws a solid rectangle in the region specified (will be clipped to the TextSetRegion)
//  This is used to draw the 'background' of any text.
//
void __stdcall gos_TextDrawBackground( int Left, int Top, int Right, int Bottom, DWORD Color )
{
	#ifdef LAB_ONLY
	__int64 temp=GetCycles();
	if( gDisableText )
		return;
	#endif
//
// Save renderstates
//
	gos_PushRenderStates();
//
// Setup text font states - fastest performance, reading but not updating Z Buffer
//
	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_Culling, gos_Cull_None );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_WireframeMode, 0 );
	if( (Color&0xff000000)==0xff000000 )
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	else
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	gos_SetRenderState( gos_State_TextureAddress, gos_TextureWrap );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_MipMapBias, 0 );
	gos_SetRenderState( gos_State_Fog, 0 );
	gos_SetRenderState( gos_State_StencilEnable, 0 );
	gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_None );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
//
// Set vertex colors
//
	FontIndex=0;
	FontVertices[0].argb = Color;
	FontVertices[1].argb = Color;
	FontVertices[2].argb = Color;
	FontVertices[3].argb = Color;
//
// Setup vertex coords
//
	FontVertices[0].x = (float)Left;
	FontVertices[0].y = (float)Top;

	FontVertices[1].x = (float)Left;
	FontVertices[1].y = (float)Bottom;

	FontVertices[2].x = (float)Right;
	FontVertices[2].y = (float)Bottom;

	FontVertices[3].x = (float)Right;
	FontVertices[3].y = (float)Top;
//
// Clip and draw it
//
	FontDrawQuad(false);
//
// If valid, draw
//
	if( FontIndex )
	{
		if( FontClipping )
			gos_SetRenderState( gos_State_Clipping, 2 );
		gos_DrawQuads( FontVertices, FontIndex );
	}
//
// Restore renderstates
//
	gos_PopRenderStates();
//
//
//
	#ifdef LAB_ONLY
	TimeFont3D+=GetCycles()-temp;
	#endif
}



//
// Returns size of character in current attributes
//
void GetChrSize( int* Width, int* Height, BYTE Chr )
{
	if (Chr < 32 || Chr >=FontHandle->CharCount)
		Chr = '@';

	if( OddSize )
	{
//
// Height only effected by scale
//
		*Height=float2long(FontSize);
//
// Space character special
//
		if( Chr==32 )
		{
			if( FontProportional )
				*Width=float2long(FontScale*FontHandle->Width/2);
			else
				*Width=float2long(FontScale*FontHandle->Width);
			return;
		}
//
// Work out width of character
//
		if( FontProportional )
			*Width=float2long((FontHandle->UsedPixels[Chr-32]+1)*FontScale);
		else
			*Width=float2long(FontHandle->Width*FontScale);
		if( FontBold )
			*Width+=1;
		return;
	}
	else
	{
//
// Height only effected by scale
//
		*Height=FontHandle->Height;
//
// Space charcter special
//
		if( Chr==32 )
		{
			if( FontProportional )
				*Width=FontHandle->Width>>1;
			else
				*Width=FontHandle->Width;
			return;
		}
//
// Work out width of character
//
		if( FontProportional )
			*Width=FontHandle->UsedPixels[Chr-32]+1;
		else
			*Width=FontHandle->Width;
		if( FontBold )
			*Width+=1;
		return;
	}
}


//
// Am I sitting on a special code? If so, how many characters should I move ahead?
//
int IgnoreEmbedded( BYTE* pText )
{
	if( FontDisableCodes )
		return 0;

	if( *pText=='/' )
	{
		if( *(pText+1)=='/' )
			return 1;
		if( 0==strnicmp( "color=", (char*)pText+1, 6 ) ) 
			return 15;
		if( 0==strnicmp( "bold=", (char*)pText+1, 5 ) )
			return 7;
		if( 0==strnicmp( "proportional=", (char*)pText+1, 13 ) )
			return 15;
		if( 0==strnicmp( "italic=", (char*)pText+1, 7 ) )
			return 9;
		if( 0==strnicmp( "size=", (char*)pText+1, 5 ) )
		{
			int toReturn = 6 ;
			pText+=6;
			bool usedDelimiter = false;
			if (*pText == '<')
			{
				usedDelimiter = true;
				pText++;
				toReturn++;
			}
			bool negate = false;
			if (*pText == '-')
			{
				negate = true;
				pText++;
				toReturn++;
			}

			FontSize = (float) ((*pText)-'0');
			pText+=1;
			if (((*pText)-'0') >= 0 && ((*pText)-'0') <= 9 )
			{
				FontSize = FontSize*10.0f+ (float) ((*pText)-'0');
				pText+=1;
				toReturn++;
			}

			if (usedDelimiter)
			{
				if (*pText !='>')
					STOP(("No matching '>' delimiter on size parameter"));
				else
				{
					pText++;
					toReturn++;
				}
			}

			if (!FontHandle)
				STOP(("A font must be specified before setting it's size"));

			if (negate)
			{
				if (!FontHandle->hFontTTF)
				{
					FontSize = FontHandle->Height / ((FontHandle->Height * (2.0f/3.0f)) / FontSize);
					if (FontSize != 1.0f && FontSize != FontHandle->Height)
						OddSize = 1;
				}
				else
					FontSize = FontSize * 1.5f;
			}
			else
			{
				if (!FontHandle->hFontTTF)
				{
					FontSize = (float)  FontHandle->Height * FontSize;
					if (FontSize != 1.0f && FontSize != FontHandle->Height)
						OddSize = 1;
				}
				else
					FontSize = FontHandle->Height * FontSize;
			}

			FontScale = FontSize / (FontHandle->Height);

			if (FontScale != 1.0f)
				OddSize = true;
			else
				OddSize = false;

			return toReturn;

		}
		if( 0==strnicmp( "font=", (char*)pText+1, 5 ) )
		{
			//
			// String specifies the use of another font. Find and set it.
			//
			pText+=6;
			char path[256];
			char fullpath[256];
			char * p = path;
			if (*pText != '<')
				STOP(("Syntax error: /font=\"<path>\""));
			else
				pText++;
			while(*pText && *pText != '>')
			{
				*p= *pText;
				pText++;
				p++;
			}
			*p = 0;

			if (*pText != '>')
				STOP(("Syntax error: /font=<path>"));
			else
				pText++;

			gos_GetFullPathName(fullpath, path);
			int len = strlen(fullpath);
			
			FontInfo* pFont=Fonts;
			while( pFont )
			{
				if(	strnicmp(pFont->FontFile,fullpath,len)==0 )
				{
					FontHandle = pFont;
					break;
				}
				else
				if (strnicmp(pFont->FontFile,path,strlen(path))==0)
				{
					FontHandle = pFont;
					break;
				}
				pFont=pFont->pNext;
			}
			if (pFont == NULL)
				STOP(("//font= specified a unknown font"));
			return 7 + strlen(pFont->FontFile);
		}
	}
	return 0;
}


//
// Returns the pixel length of the next word
//
DWORD GetNextWordLen( BYTE* pText )
{
	int Width,Height,Len;
	GetChrSize( &Len, &Height, ' ' );

	float FontSizeTmp = FontSize;
	bool OddSizeTmp = OddSize;
	FontInfo * FontHandleTmp = FontHandle;
	LargestHeight = 0;
	while( *pText!=' ' && !(*pText==0x0d || *pText==0x0a) && *pText!=0 )
	{
		int add = 0;
		while( (add = IgnoreEmbedded( pText)) != 0 )
			pText+=add;
		if (*pText == 0 || *pText==0x0d || *pText==0x0a)
			break;
		GetChrSize( &Width, &Height, *pText++ );
		if (Height > LargestHeight)
			LargestHeight = Height;
		Len+=Width;
	}
	FontHandle = FontHandleTmp;
	FontSize = FontSizeTmp;
	OddSize = OddSizeTmp;
	return Len;
}

//
// Returns the pixel length of the next line  (0 if line does not fit within region)
//
DWORD GetNextLineLen( BYTE* pText, BYTE** pNextLine )
{
	int Width,Height,Len=float2long(FontMinX),LastWordLen=float2long(FontMaxX);
	BYTE* PLastWord=0;

	LargestHeight = 0;

	if ((*pText==0x0d))
	{
		pText++;
		if (*pText== 0x0a)
			pText++;
		*pNextLine = pText;
		int dummy;
		GetChrSize(&dummy, &LargestHeight, ' ');  
		return 1;
	}
	
	while( !(*pText==0x0d || *pText == 0x0a) && *pText!=0 )
	{
		int add = 0;
		while( (add = IgnoreEmbedded( pText)) != 0 )
			pText+=add;
		
		if (*pText==0x0d || *pText == 0x0a || *pText == 0)
			break;

		if( *pText==' ' && FontWordWrap )
		{
			if( Len+MarginBuffer>FontMaxX )
			{
				Len=LastWordLen;
				if( PLastWord )
					pText=PLastWord;
				break;
			}
			else
			{
				PLastWord=pText+1;
				LastWordLen=Len;
			}
		}
		GetChrSize( &Width, &Height, *pText++ );
		if (Height > LargestHeight)
			LargestHeight = Height;
		if( Len+Width+MarginBuffer>FontMaxX && FontWordWrap )
		{
			Len=LastWordLen;
			if( PLastWord )
				pText=PLastWord;
			break;
		}
		Len+=Width;
	}
	if( Len+MarginBuffer>FontMaxX && FontWordWrap )
	{
		Len=LastWordLen;
		if( PLastWord )
			pText=PLastWord;
	}
	if( *pText==0x0d || *pText == 0x0a)
		pText++;
	if( pNextLine )
		*pNextLine=pText;

	return float2long(Len-FontMinX);
}


//
// Draw a single character
//
void gos_ChrDraw( BYTE Chr )
{
//
// End of line?
//
	int Width;
	int OffsetToNext;
	float FontU=0.0f;
	float FontV=0.0f;
	float FontU1=0.0f;
	float FontV1=0.0f;
	float TopBlankPixels;

	float LeftX = 0.0f;
	float RightX = 0.0f;
	float Y2 = 0.0f;
	float X2 = 0.0f;

	if (OddSize)
	{

		if( Chr==0x0d || Chr== 0x0a)
		{
			FontX=float2long(FontMinX);
			FontY+=float2long(FontSize);
			return;
		}
//
// The assert that used to be here was probably not the best way to handle
// handle an out-of-range character. Instead, we'll substitute with an '@'
// symbol.
//
		if (Chr < 32 || Chr >= FontHandle->CharCount)
			Chr = '@';
//
// Check for space
//
		if( Chr==32 )
		{
			if( FontProportional )
				FontX+=float2long(FontScale*FontHandle->Width/2.0f);
			else
				FontX+=float2long(FontScale*FontHandle->Width);
			return;
		}
//
// Work out texture coords for font
//
		Chr-=32;
		FontU=FontHandle->TopU[Chr];
		FontV=(float)(FontHandle->TopV[Chr]+FontHandle->TopOffset[Chr]);
		FontU1=FontU;
		FontV1=FontV+FontHandle->RealHeight[Chr];

		if( FontProportional )
			OffsetToNext=float2long((FontHandle->UsedPixels[Chr]+1)*FontScale);
		else
			OffsetToNext=float2long(FontHandle->Width*FontScale);
//
// Work out width of character
//
		Width=FontHandle->UsedPixels[Chr];
		FontU+=FontHandle->BlankPixels[Chr];
		FontU1+=FontHandle->BlankPixels[Chr]+Width;
//
// Convert to texture 0->1.0 coords
//
		FontU*=FontHandle->rhSize;
		FontV*=FontHandle->rhSize;
		FontU1*=FontHandle->rhSize;
		FontV1*=FontHandle->rhSize;
//
// Work out screen potision of vertices
//
		LeftX=(float)FontX;
		RightX=FontX+Width*FontScale;
		Y2=FontY+(FontHandle->TopOffset[Chr]+FontHandle->RealHeight[Chr])*FontScale;
		X2=RightX;
//
// Italic? (25% slant)
//
		if( FontItalic )
		{
			float ItalicOffset=(FontScale*FontHandle->Width*0.25f)*((float)(FontHandle->Height-FontHandle->TopOffset[Chr])/FontHandle->Height);
			LeftX+=ItalicOffset;
			RightX+=ItalicOffset;
		}
//
// New font selected?
//
	if( CurrentFontTexture!=FontHandle->Texture[ FontHandle->TextureHandle[Chr] ] )
	{
		if( FontIndex )
		{
			if( FontClipping )
				gos_SetRenderState( gos_State_Clipping, 2 );
			gos_DrawQuads( FontVertices, FontIndex );
			if( FontClipping )
				gos_SetRenderState( gos_State_Clipping, 0 );
		}
		FontClipping=0;
		FontIndex=0;

		CurrentFontTexture=FontHandle->Texture[ FontHandle->TextureHandle[Chr] ];
		gos_SetRenderState( gos_State_Texture, CurrentFontTexture );
		FontNoCache=1;
	}
//
// Make a quad, draw it
//
		TopBlankPixels=(float)FontY+(FontHandle->TopOffset[Chr])*FontScale;
	}
	else
	{

		if( Chr=='\n' || Chr == 0x0a || Chr == 0x0d)
		{
			FontX=float2long(FontMinX);
			FontY+=float2long(FontSize);
			return;
		}
// The assert that used to be here was probably not the best way to handle
// handle an out-of-range character. Instead, we'll substitute with an '@'
// symbol.
//
		if (Chr < 32 || Chr >= FontHandle->CharCount)
			Chr = '@';
//
// Check for space
//
		if( Chr==32 )
		{
			if( FontProportional )
				FontX+=float2long( FontScale *(FontHandle->Width>>1) );
			else
				FontX+=float2long( FontScale *(FontHandle->Width) );
			return;
		}
//
// Work out texture coords for font
//
		Chr-=32;
		FontU=FontHandle->TopU[Chr];
		FontV=(float)(FontHandle->TopV[Chr]+FontHandle->TopOffset[Chr]);
		FontU1=FontU;
		FontV1=FontV+FontHandle->RealHeight[Chr];

		if( FontProportional )
			OffsetToNext=float2long(FontScale * (FontHandle->UsedPixels[Chr]+1));
		else
			OffsetToNext=float2long(FontScale * FontHandle->Width);
//
// Work out width of character
//
		Width=FontHandle->UsedPixels[Chr];
		FontU+=FontHandle->BlankPixels[Chr];
		FontU1+=FontHandle->BlankPixels[Chr]+Width;
//
// Convert to texture 0->1.0 coords
//
		FontU*=FontHandle->rhSize;
		FontV*=FontHandle->rhSize;
		FontU1*=FontHandle->rhSize;
		FontV1*=FontHandle->rhSize;
//
// Work out screen potision of vertices
//
		LeftX=(float)FontX;
		RightX;
		Y2;
		RightX=(float)(FontX+(Width*FontScale));
		Y2=(float)(FontY+(FontHandle->TopOffset[Chr]+FontHandle->RealHeight[Chr]*FontScale));
		X2=RightX;
//
// Italic? (25% slant)
//
		if( FontItalic )
		{
			float ItalicOffset=(FontScale*FontHandle->Width*0.25f)*((float)(FontHandle->Height-FontHandle->TopOffset[Chr])/FontHandle->Height);
			LeftX+=ItalicOffset;
			RightX+=ItalicOffset;
		}
//
// New font selected?
//
		if( CurrentFontTexture!=FontHandle->Texture[ FontHandle->TextureHandle[Chr] ] )
		{
			if( FontIndex )
			{
				if( FontClipping )
					gos_SetRenderState( gos_State_Clipping, 2 );
				gos_DrawQuads( FontVertices, FontIndex );
				if( FontClipping )
					gos_SetRenderState( gos_State_Clipping, 0 );
			}
			FontClipping=0;
			FontIndex=0;

			CurrentFontTexture=FontHandle->Texture[ FontHandle->TextureHandle[Chr] ];
			gos_SetRenderState( gos_State_Texture, CurrentFontTexture );
			FontNoCache=1;
		}

//
// Make a quad, draw it
//
		TopBlankPixels=(float)(FontY+FontHandle->TopOffset[Chr]);
	}


	FontVertices[FontIndex+0].argb = FontColor;
	FontVertices[FontIndex+1].argb = FontColor;
	FontVertices[FontIndex+2].argb = FontColor;
	FontVertices[FontIndex+3].argb = FontColor;

	FontVertices[FontIndex+0].x = LeftX;
	FontVertices[FontIndex+0].y = TopBlankPixels;
	FontVertices[FontIndex+0].u = FontU;
	FontVertices[FontIndex+0].v = FontV;

	FontVertices[FontIndex+1].x = (float)FontX;
	FontVertices[FontIndex+1].y = Y2;
	FontVertices[FontIndex+1].u = FontU;
	FontVertices[FontIndex+1].v = FontV1;

	FontVertices[FontIndex+2].x = X2;
	FontVertices[FontIndex+2].y = Y2;
	FontVertices[FontIndex+2].u = FontU1;
	FontVertices[FontIndex+2].v = FontV1;

	FontVertices[FontIndex+3].x = RightX;
	FontVertices[FontIndex+3].y = TopBlankPixels;
	FontVertices[FontIndex+3].u = FontU1;
	FontVertices[FontIndex+3].v = FontV;

	FontDrawQuad(FontBold);
//
// Bold? (draw 1 pixel doubled)
//
	if( FontBold )
	{
		OffsetToNext++;
	}
//
// Flush any vertices
//
	if( FontIndex>=124 )								// (Room for bold characters)
	{
		if( FontClipping )
			gos_SetRenderState( gos_State_Clipping, 2 );
		gos_DrawQuads( FontVertices, FontIndex );
		if( FontClipping )
			gos_SetRenderState( gos_State_Clipping, 0 );
		FontClipping=0;
		FontIndex=0;
		FontNoCache=1;
	}
//
// Move current cursor position to next space
//
	FontX+=OffsetToNext;
}





void SetupFontRenderstates()
{
//
// Setup text font states - fastest performance, reading but not updating Z Buffer
//
	if (FontHandle)
	{
		CurrentFontTexture=FontHandle->Texture[0];
		gos_SetRenderState( gos_State_Texture, CurrentFontTexture );
	}
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 1 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_WireframeMode, 0 );
	gos_SetRenderState( gos_State_Culling, gos_Cull_None );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	gos_SetRenderState( gos_State_TextureAddress, gos_TextureWrap );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_Fog, 0 );
	gos_SetRenderState( gos_State_StencilEnable, 0 );
	gos_SetRenderState( gos_State_Multitexture, gos_Multitexture_None );
	if( HasAlphaTest )
	{
		if( 0xffffffff==FontColor )
		{
			gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendDecal );
		}
		else
		{
			if( 0xff000000==(FontColor&0xff000000) )
				gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
			else
				gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
		}
//
// Alpha mode depends on vertex alpha - because of alpha test ONEZERO can be used for transparent text
//
		if( (FontColor&0xff000000)==0xff000000 && !FontHandle->Aliased )
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
		else
			gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	}
	else
	{
		gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
	}
}





//
// Same as above, but an arglist can be passed
//
void __cdecl gos_TextDraw( const char *Message, ... )
{
    va_list arglist;
    va_start( arglist, Message );
    gos_TextDrawV( Message, arglist );
    va_end( arglist );
}


//
// Draws string using current attributes and position
//
void __stdcall gos_TextDrawV( const char *Message, char* arglist )
{
//
// Empty message?
//
	if (!Message || *Message==0 )
		return;
//
// Fill a temp buffer with text
//
	static char textBufferArray[8192];
	char *textbuffer = (char *) textBufferArray;

	textBufferArray[sizeof(textBufferArray)-1]=(char)4;
    _vsnprintf( textBufferArray, sizeof(textBufferArray), Message, arglist );
	if( textBufferArray[sizeof(textBufferArray)-1]!=(char)4 )
		STOP(( "String too long" ));
//
// Look for tags (Only valid at start of text)
//
	if( textbuffer[0]=='/' )
		HandleTags(&textbuffer);
//
//
//
	#ifdef LAB_ONLY
	__int64 temp=GetCycles();
	if( gDisableText )
		return;
	#endif

#ifdef _ARMOR
	if( 0==FontHandle )
		STOP(( "No font selected" ));

	FontInfo* pFont=Fonts;
	while( pFont )
	{
		if(	pFont == FontHandle )
			break;
		pFont=pFont->pNext;
	}

	if (pFont == NULL)
		STOP(( "Invalid font handle" ));	
#endif

	if (OddSize)
		FontScale = FontSize / (FontHandle->Height);

	if( FontHandle->hFontTTF )								// TTF fonts are always printed with GDI, if they have DBCS or not. .D3F fonts cannot print DBCS
	{
		TextDrawDBCS( (char*) textbuffer );
		return;
	}

//
// Save renderstates
//
	gos_PushRenderStates();
//
// Setup renderstates
//
	SetupFontRenderstates();
//
// Setup any texture offset
//
	if( Compatibility3D&TextureOffset )
		TexOffset=0.0625f/TEXHANDLE_TO_PTR(RenderStates[gos_State_Texture])->Width();
//
// If center in region, calc size and move down
//
	DWORD OldFontWrapType=FontWrapType;
	if( FontWrapType==3 )
	{
		FontWrapType=2;											// Enable centering
		int Width,Height;
		gos_TextStringLength( (DWORD*)&Width, (DWORD*)&Height, (const char*)&textbuffer[0] );
		if( (FontMaxY-FontMinY)>=Height )
		{
			FontY+=(float2long(FontMaxY-FontMinY)-Height)/2;
		}
	}
//
// Step though message display each character
//
	FontIndex=0;
	FontNoCache=0;
	BYTE* pText=(unsigned char *)textbuffer;
	FontStartOfLine=1;
	while( *pText )
	{
//
// Check for right align or centered
//
		if( FontStartOfLine && FontWrapType )
		{
			FontStartOfLine=0;
			int Padding=float2long(FontMaxX-FontMinX)-GetNextLineLen(pText,0);

			if( FontWrapType==1 )
			{
				FontX+=Padding-MarginBuffer;	// Right align
			}
			else
			{
				FontX+=(Padding+1)>>1;		// Center text
			}
		}
//
// Check for 'escape codes'
//
		if( 0==FontDisableCodes && *pText=='/' )
		{
			pText++;
			if( *pText=='/' )
			{
				gos_ChrDraw( *pText++ );
			}
			else
			{
				if( 0==strnicmp( "color=", (char*)pText, 6 ) ) 
				{
					pText+=6;
					DWORD Color=0;
					for( int t0=0; t0<8; t0++ )
					{
						Color<<=4;
						DWORD Tchr=UppertoLowerTable[*pText++];
						if( Tchr>='0' && Tchr<='9' || Tchr>='a' && Tchr<='f' )
						{
							Tchr-='0';
							if( Tchr>9 )
								Tchr-=('a'-'9'-1);
							Color+=Tchr;
						}
						else
						{
#ifdef _DEBUG
							STOP(( "Bad color - /color=AARRGGBB in hex" ));
#endif
						}
					}
					FontColor=Color;
//
// Flush any vertices with old renderstates
//
					if( FontIndex)
					{
						if( FontClipping )
							gos_SetRenderState( gos_State_Clipping, 2 );
						gos_DrawQuads( FontVertices, FontIndex );
						if( FontClipping )
							gos_SetRenderState( gos_State_Clipping, 0 );
						FontClipping=0;
						FontIndex=0;
						FontNoCache=1;
					}
					if( HasAlphaTest )
					{
						if( 0xffffffff==FontColor )
						{
							gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendDecal );
						}
						else
						{
							if( 0xff000000==(FontColor&0xff000000) )
								gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
							else
								gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
						}
						if( (FontColor&0xff000000)==0xff000000 && !FontHandle->Aliased )
							gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
						else
							gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
					}
					else
					{
						gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulateAlpha );
						gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );
					}
				}
				else if( 0==strnicmp( "proportional=0", (char*)pText, 14 ) )
				{
					pText+=14;
					FontProportional=0;
				}
				else if( 0==strnicmp( "proportional=1", (char*)pText, 14 ) )
				{
					pText+=14;
					FontProportional=1;
				}
				else if( 0==strnicmp( "bold=1", (char*)pText, 6 ) )
				{
					pText+=6;
					FontBold=1;
				}
				else if( 0==strnicmp( "bold=0", (char*)pText, 6 ) )
				{
					pText+=6;
					FontBold=0;
				}
				else if( 0==strnicmp( "italic=0", (char*)pText, 8 ) )
				{
					pText+=8;
					FontItalic=0;
				}
				else if( 0==strnicmp( "italic=1", (char*)pText, 8 ) )
				{
					pText+=8;
					FontItalic=1;
				}
				else
					gos_ChrDraw( '/' );
			}
		}
		else
		{
//
// Check for word wrap (left align)
//			
			if( FontWordWrap )
			{
				if (*pText == ' ')
				{
					if( FontX+GetNextWordLen(pText+1)+MarginBuffer > FontMaxX )
					{
						gos_ChrDraw( '\n' );
						FontStartOfLine=1;
						pText++;
					}
					else
					{
						gos_ChrDraw( *pText++ );
					}
				}
				else
				{
					int wordlen = GetNextWordLen(pText+1);
					int w, h;
					GetChrSize(&w,&h,*pText); 
					if (wordlen > (FontMaxX-FontMinX))
					{
						do
						{
							if (w+FontX < FontMaxX)
							{
								gos_ChrDraw( *pText++ );
							}
							else
							{
								*pText--;
								break;
							}
						}while(1);

					}
					if( FontX+w+MarginBuffer > FontMaxX )
					{
						gos_ChrDraw( '\n' );
						FontStartOfLine=1;
					}
					else
					{
						gos_ChrDraw( *pText++ );
					}
				}
			}
			else
			{
				if( *pText=='\n' || *pText == 0x0d )
					FontStartOfLine=1;
				gos_ChrDraw( *pText++ );
			}
		}
	}
//
// Check message did not overwrite buffer
//
	if( pText-((unsigned char *)textbuffer) >= sizeof(textBufferArray) )
		STOP(( "Message too long" ));
//
// Flush any vertices
//
	if( FontIndex )
	{
		if( FontClipping )
			gos_SetRenderState( gos_State_Clipping, 2 );
		gos_DrawQuads( FontVertices, FontIndex );
	}
//
// Restore renderstates
//
	FontWrapType=OldFontWrapType;
	gos_PopRenderStates();
//
//
//
	#ifdef LAB_ONLY
	TimeFont3D+=GetCycles()-temp;
	#endif
}





void HandleTags(char ** lplpMessage)
{
	if( FontDisableCodes )
		return;
	//
	// Look for start-only tags
	//
	char * Message = *lplpMessage;
	bool foundTag = true;
	while( foundTag && *Message=='/' )
	{
		foundTag = false;
		if( 0==strnicmp( "/font=", (char*)Message, 6 ) )
		{
			//
			// String specifies the use of another font. Find and set it.
			//
			Message+=6;
			char path[256];
			char fullpath[256];
			char * p = path;
			if ((*Message) != '<')
				STOP(("Syntax error: /font=<path>"));
			else
				Message++;
			while((*Message) && (*Message) != '>')
			{
				*p= (*Message);
				Message++;
				p++;
			}
			*p = 0;

			if ((*Message) != '>')
				STOP(("Syntax error: /font=<path>"));
			else
				Message++;

			gos_GetFullPathName(fullpath, path);
			int len = strlen(fullpath);
			
			FontInfo* pFont=Fonts;
			while( pFont )
			{
				if(	strnicmp(pFont->FontFile,fullpath,len)==0 )
				{
					FontHandle = pFont;
					break;
				}
				else
				if (strnicmp(pFont->FontFile,path,strlen(path))==0)
				{
					FontHandle = pFont;
					break;
				}
				pFont=pFont->pNext;
			}
			if (pFont == NULL)
				STOP(("//font= specified a unknown font"));
			//
			// MJL 03/24/00
			// Bugfix: without this, after processing a /font
			// tag in text, all following tags are ignored. It looks
			// like it's just a simple honest bug
			//
			foundTag = true;
		}
		else
		if( 0==strnicmp( "/color=", (char*)Message, 7 ) ) 
		{
			Message+=7;
			DWORD Color=0;
			for( int t0=0; t0<8; t0++ )
			{
				Color<<=4;
				DWORD Tchr=UppertoLowerTable[*Message++];
				if( Tchr>='0' && Tchr<='9' || Tchr>='a' && Tchr<='f' )
				{
					Tchr-='0';
					if( Tchr>9 )
						Tchr-=('a'-'9'-1);
					Color+=Tchr;
				}
				else
				{
#ifdef _DEBUG
					STOP(( "Bad color - /color=AARRGGBB in hex" ));
#endif
				}
			}
			FontColor=Color;
			foundTag = true;
		}
		else
		if( 0==strnicmp( "/size=", (char*)Message, 6 ) )
		{
			Message+=6;
			bool usedDelimiter = false;
			if (*Message == '<')
			{
				usedDelimiter = true;
				Message++;
			}
			bool negate = false;
			if (*Message == '-')
			{
				negate = true;
				Message++;
			}

			FontSize = (float) ((*Message)-'0');
			Message+=1;
			if (((*Message)-'0') >= 0 && ((*Message)-'0') <= 9 )
			{
				FontSize = FontSize*10.0f+ (float) ((*Message)-'0');
				Message+=1;
			}

			if (usedDelimiter)
			{
				if (*Message !='>')
					STOP(("No matching '>' delimiter on size parameter"));
				else
					Message++;
			}

			if (!FontHandle)
				STOP(("A font must be specified before setting it's size"));

			if (negate)
			{
				if (!FontHandle->hFontTTF)
				{
					FontSize = FontHandle->Height / ((FontHandle->Height * (2.0f/3.0f)) / FontSize);
					if (FontSize != 1.0f && FontSize != FontHandle->Height)
						OddSize = 1;
				}
				else
					FontSize = FontSize * 1.5f;
			}
			else
			{
				if (!FontHandle->hFontTTF)
				{
					FontSize = (float)  FontHandle->Height * FontSize;
					if (FontSize != 1.0f && FontSize != FontHandle->Height)
						OddSize = 1;
				}
				else
					FontSize = FontHandle->Height * FontSize;
			}

			FontScale = FontSize / (FontHandle->Height);

			if (FontScale != 1.0f)
				OddSize = true;
			else
				OddSize = false;

			foundTag = true;
		}
		else if( 0==strnicmp( "/proportional=0", (char*)Message, 15 ) )
		{
			Message+=15;
			FontProportional=0;
			foundTag = true;
		}
		else if( 0==strnicmp( "/proportional=1", (char*)Message, 15 ) )
		{
			Message+=15;
			FontProportional=1;
			foundTag = true;
		}
		else if( 0==strnicmp( "/wrap=1", (char*)Message, 7 ) )
		{
			Message+=7;
			FontWordWrap=1;
			foundTag = true;
		}
		else if( 0==strnicmp( "/wrap=0", (char*)Message, 7 ) )
		{
			Message+=7;
			FontWordWrap=0;
			foundTag = true;
		}
		else if( 0==strnicmp( "/align=left", (char*)Message, 11 ) )
		{
			Message+=11;
			FontWrapType=0;
			foundTag = true;
		}
		else if( 0==strnicmp( "/align=right", (char*)Message, 12 ) )
		{
			Message+=12;
			FontWrapType=1;
			foundTag = true;
		}
		else if( 0==strnicmp( "/align=center", (char*)Message, 13 ) )
		{
			Message+=13;
			FontWrapType=2;
			foundTag = true;
		}
		else if( 0==strnicmp( "/align=middle", (char*)Message, 13 ) )
		{
			Message+=13;
			FontWrapType=3;
			foundTag = true;
		}
		else if( 0==strnicmp( "/bold=1", (char*)Message, 7 ) )
		{
			Message+=7;
			FontBold=1;
			foundTag = true;
		}
		else if( 0==strnicmp( "/bold=0", (char*)Message, 7 ) )
		{
			Message+=7;
			FontBold=0;
			foundTag = true;
		}
		else if( 0==strnicmp( "/italic=0", (char*)Message, 9 ) )
		{
			Message+=9;
			FontItalic=0;
			foundTag = true;
		}
		else if( 0==strnicmp( "/italic=1", (char*)Message, 9 ) )
		{
			Message+=9;
			FontItalic=1;
			foundTag = true;
		}

	}
	*lplpMessage = Message;
}























