Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

763 lines
22 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#define SUPPORT_CUSTOM_FONT_FORMAT
#ifdef SUPPORT_CUSTOM_FONT_FORMAT
#define _WIN32_WINNT 0x0500
#endif
#include "gameuisystemsurface.h"
#include "gameuisystemmgr.h"
#include "vgui_surfacelib/fontmanager.h"
#include "vgui_surfacelib/texturedictionary.h"
#include "vgui_surfacelib/fonttexturecache.h"
#include "vgui/isystem.h"
#include "tier1/utlbuffer.h"
#include "tier1/keyvalues.h"
#include "materialsystem/imesh.h"
#include "gameuischeme.h"
#include "vgui_controls/Controls.h"
#include "rendersystem/irendercontext.h"
#include "rendersystem/vertexdata.h"
#include "rendersystem/indexdata.h"
#include "GameLayer.h"
#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#endif
#include "xbox/xboxstubs.h"
#include "valvefont.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Helper function for getting the language the game ui should use.
//-----------------------------------------------------------------------------
static void HelperGetLanguage( char *pLanguageBuf, int bufSize )
{
bool bValid = false;
if ( IsPC() )
{
bValid = vgui::system()->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\Language", pLanguageBuf, bufSize - 1 );
}
else
{
Q_strncpy( pLanguageBuf, XBX_GetLanguageString(), bufSize );
bValid = true;
}
if ( !bValid )
{
Q_strncpy( pLanguageBuf, "english", bufSize );
}
}
//-----------------------------------------------------------------------------
// Globals...
//-----------------------------------------------------------------------------
static CFontTextureCache g_FontTextureCache;
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
CGameUISystemSurface g_GameUISystemSurface;
CGameUISystemSurface *g_pGameUISystemSurface = &g_GameUISystemSurface;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CGameUISystemSurface::CGameUISystemSurface()
{
m_bIsInitialized = false;
g_FontTextureCache.SetPrefix( "ingameui" );
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CGameUISystemSurface::~CGameUISystemSurface()
{
m_CustomFontFileNames.RemoveAll();
m_BitmapFontFileNames.RemoveAll();
m_BitmapFontFileMapping.RemoveAll();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
InitReturnVal_t CGameUISystemSurface::Init()
{
if ( m_bIsInitialized )
return INIT_OK;
// fonts initialization
char language[64];
HelperGetLanguage( language, 64 );
FontManager().SetLanguage( language );
m_bIsInitialized = true;
return INIT_OK;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameUISystemSurface::Shutdown( void )
{
// Release all textures
TextureDictionary()->DestroyAllTextures();
#if !defined( _GAMECONSOLE )
HMODULE gdiModule = NULL;
#ifdef SUPPORT_CUSTOM_FONT_FORMAT
; // on custom font format Windows takes care of cleaning up the font when the process quits
#else
// release any custom font files
// use newer function if possible
gdiModule = ::LoadLibrary( "gdi32.dll" );
typedef int (WINAPI *RemoveFontResourceExProc)(LPCTSTR, DWORD, PVOID);
RemoveFontResourceExProc pRemoveFontResourceEx = NULL;
if ( gdiModule )
{
pRemoveFontResourceEx = (RemoveFontResourceExProc)::GetProcAddress(gdiModule, "RemoveFontResourceExA");
}
for (int i = 0; i < m_CustomFontFileNames.Count(); i++)
{
if ( pRemoveFontResourceEx )
{
// dvs: Keep removing the font until we get an error back. After consulting with Microsoft, it appears
// that RemoveFontResourceEx must sometimes be called multiple times to work. Doing this insures that
// when we load the font next time we get the real font instead of Ariel.
int nRetries = 0;
while ( (*pRemoveFontResourceEx)(m_CustomFontFileNames[i].String(), 0x10, NULL) && ( nRetries < 10 ) )
{
nRetries++;
Msg( "Removed font resource %s on attempt %d.\n", m_CustomFontFileNames[i].String(), nRetries );
}
}
else
{
// dvs: Keep removing the font until we get an error back. After consulting with Microsoft, it appears
// that RemoveFontResourceEx must sometimes be called multiple times to work. Doing this insures that
// when we load the font next time we get the real font instead of Ariel.
int nRetries = 0;
while ( ::RemoveFontResource(m_CustomFontFileNames[i].String()) && ( nRetries < 10 ) )
{
nRetries++;
Msg( "Removed font resource %s on attempt %d.\n", m_CustomFontFileNames[i].String(), nRetries );
}
}
}
#endif // SUPPORT_CUSTOM_FONT_FORMAT
#endif
m_CustomFontFileNames.RemoveAll();
m_BitmapFontFileNames.RemoveAll();
m_BitmapFontFileMapping.RemoveAll();
#if !defined( _GAMECONSOLE )
if ( gdiModule )
{
::FreeLibrary(gdiModule);
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: adds a custom font file (supports valve .vfont files)
//-----------------------------------------------------------------------------
bool CGameUISystemSurface::AddCustomFontFile( const char *fontFileName )
{
if ( IsGameConsole() )
{
// custom fonts are not supported (not needed) on xbox, all .vfonts are offline converted to ttfs
// ttfs are mounted/handled elsewhere
return true;
}
char fullPath[MAX_PATH];
bool bFound = false;
// windows needs an absolute path for ttf
bFound = g_pFullFileSystem->GetLocalPath( fontFileName, fullPath, sizeof( fullPath ) ) ? true : false;
if ( !bFound )
{
Warning( "Couldn't find custom font file '%s'\n", fontFileName );
return false;
}
// only add if it's not already in the list
Q_strlower( fullPath );
CUtlSymbol sym( fullPath );
int i;
for ( i = 0; i < m_CustomFontFileNames.Count(); i++ )
{
if ( m_CustomFontFileNames[i] == sym )
break;
}
if ( !m_CustomFontFileNames.IsValidIndex( i ) )
{
m_CustomFontFileNames.AddToTail( fullPath );
if ( IsPC() )
{
#ifdef SUPPORT_CUSTOM_FONT_FORMAT
// We don't need the actual file on disk
#else
// make sure it's on disk
// only do this once for each font since in steam it will overwrite the
// registered font file, causing windows to invalidate the font
g_pFullFileSystem->GetLocalCopy( fullPath );
#endif
}
}
#if !defined( _GAMECONSOLE )
#ifdef SUPPORT_CUSTOM_FONT_FORMAT
// Just load the font data, decrypt in memory and register for this process
CUtlBuffer buf;
if ( !g_pFullFileSystem->ReadFile( fontFileName, NULL, buf ) )
{
Msg( "Failed to load custom font file '%s'\n", fontFileName );
return false;
}
if ( !ValveFont::DecodeFont( buf ) )
{
Msg( "Failed to parse custom font file '%s'\n", fontFileName );
return false;
}
DWORD dwNumFontsRegistered = 0;
HANDLE hRegistered = NULL;
hRegistered = ::AddFontMemResourceEx( buf.Base(), buf.TellPut(), NULL, &dwNumFontsRegistered );
if ( !hRegistered )
{
Msg( "Failed to register custom font file '%s'\n", fontFileName );
return false;
}
return hRegistered != NULL;
#else
// try and use the optimal custom font loader, will makes sure fonts are unloaded properly
// this function is in a newer version of the gdi library (win2k+), so need to try get it directly
bool successfullyAdded = false;
HMODULE gdiModule = ::LoadLibrary( "gdi32.dll" );
if (gdiModule)
{
typedef int (WINAPI *AddFontResourceExProc)(LPCTSTR, DWORD, PVOID);
AddFontResourceExProc pAddFontResourceEx = (AddFontResourceExProc)::GetProcAddress( gdiModule, "AddFontResourceExA" );
if (pAddFontResourceEx)
{
int result = (*pAddFontResourceEx)(fullPath, 0x10, NULL);
if (result > 0)
{
successfullyAdded = true;
}
}
::FreeLibrary(gdiModule);
}
// add to windows
bool success = successfullyAdded || (::AddFontResource(fullPath) > 0);
if ( !success )
{
Msg( "Failed to load custom font file '%s'\n", fullPath );
}
Assert( success );
return success;
#endif // SUPPORT_CUSTOM_FONT_FORMAT
#else
return true;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: adds a bitmap font file
//-----------------------------------------------------------------------------
bool CGameUISystemSurface::AddBitmapFontFile( const char *fontFileName )
{
bool bFound = false;
bFound = ( ( g_pFullFileSystem->GetDVDMode() == DVDMODE_STRICT ) || g_pFullFileSystem->FileExists( fontFileName, IsGameConsole() ? "GAME" : NULL ) );
if ( !bFound )
{
Msg( "Couldn't find bitmap font file '%s'\n", fontFileName );
return false;
}
char path[MAX_PATH];
Q_strncpy( path, fontFileName, MAX_PATH );
// only add if it's not already in the list
Q_strlower( path );
CUtlSymbol sym( path );
int i;
for ( i = 0; i < m_BitmapFontFileNames.Count(); i++ )
{
if ( m_BitmapFontFileNames[i] == sym )
break;
}
if ( !m_BitmapFontFileNames.IsValidIndex( i ) )
{
m_BitmapFontFileNames.AddToTail( path );
if ( IsPC() )
{
// make sure it's on disk
// only do this once for each font since in steam it will overwrite the
// registered font file, causing windows to invalidate the font
g_pFullFileSystem->GetLocalCopy( path );
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameUISystemSurface::SetBitmapFontName( const char *pName, const char *pFontFilename )
{
char fontPath[MAX_PATH];
Q_strncpy( fontPath, pFontFilename, MAX_PATH );
Q_strlower( fontPath );
CUtlSymbol sym( fontPath );
for ( int i = 0; i < m_BitmapFontFileNames.Count(); i++ )
{
if ( m_BitmapFontFileNames[i] == sym )
{
// found it, update the mapping
int index = m_BitmapFontFileMapping.Find( pName );
if ( !m_BitmapFontFileMapping.IsValidIndex( index ) )
{
index = m_BitmapFontFileMapping.Insert( pName );
}
m_BitmapFontFileMapping.Element( index ) = i;
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CGameUISystemSurface::GetBitmapFontName( const char *pName )
{
// find it in the mapping symbol table
int index = m_BitmapFontFileMapping.Find( pName );
if ( index == m_BitmapFontFileMapping.InvalidIndex() )
{
return "";
}
return m_BitmapFontFileNames[m_BitmapFontFileMapping.Element( index )].String();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameUISystemSurface::ClearTemporaryFontCache( void )
{
FontManager().ClearTemporaryFontCache();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CGameUISystemSurface::GetFontName( FontHandle_t font )
{
return FontManager().GetFontName( font );
}
//-----------------------------------------------------------------------------
// Causes fonts to get reloaded, etc.
//-----------------------------------------------------------------------------
void CGameUISystemSurface::ResetFontCaches()
{
// Don't do this on x360!!!
if ( IsGameConsole() )
return;
// clear font texture cache
g_FontTextureCache.Clear();
g_pGameUISchemeManager->ReloadFonts();
}
//-----------------------------------------------------------------------------
// Purpose: cap bits
// Warning: if you change this, make sure the SurfaceV28 wrapper above reports
// the correct capabilities.
//-----------------------------------------------------------------------------
bool CGameUISystemSurface::SupportsFontFeature( FontFeature_t feature )
{
switch ( feature )
{
case FONT_FEATURE_ANTIALIASED_FONTS:
case FONT_FEATURE_DROPSHADOW_FONTS:
return true;
case FONT_FEATURE_OUTLINE_FONTS:
if ( IsGameConsole() )
return false;
return true;
default:
return false;
};
}
//-----------------------------------------------------------------------------
// Purpose: Force a set of characters to be rendered into the font page.
//-----------------------------------------------------------------------------
void CGameUISystemSurface::PrecacheFontCharacters( FontHandle_t font, wchar_t *pCharacterString )
{
wchar_t *pCommonChars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.!:-/%";
if ( !pCharacterString || !pCharacterString[0] )
{
// use the common chars, alternate languages are not handled
pCharacterString = pCommonChars;
}
int numChars = 0;
while( pCharacterString[ numChars ] )
{
numChars++;
}
int *pTextureIDs_ignored = (int *)_alloca( numChars*sizeof( int ) );
float **pTexCoords_ignored = (float **)_alloca( numChars*sizeof( float * ) );
g_FontTextureCache.GetTextureForChars( font, FONT_DRAW_DEFAULT, pCharacterString, pTextureIDs_ignored, pTexCoords_ignored, numChars );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameUISystemSurface::DrawFontTexture( int textureId, int xPos, int yPos )
{
Assert( g_pMaterialSystem );
IMaterial *pMaterial = GetMaterial( textureId );
if (!pMaterial)
{
return;
}
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
if ( !pMesh )
return;
unsigned char textColor[4];
textColor[0] = 255;
textColor[1] = 255;
textColor[2] = 255;
textColor[3] = 255;
int x = xPos;
int y = yPos;
int wide;
int tall;
TextureDictionary()->GetTextureSize( textureId, wide, tall );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
meshBuilder.Position3f( x, y, 0 );
meshBuilder.Color4ubv( textColor );
meshBuilder.TexCoord2f( 0, 0, 0 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.Position3f( x + wide, y, 0 );
meshBuilder.Color4ubv( textColor );
meshBuilder.TexCoord2f( 0, 1, 0 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.Position3f( x + wide, y + tall, 0 );
meshBuilder.Color4ubv( textColor );
meshBuilder.TexCoord2f( 0, 1, 1 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.Position3f( x, y + tall, 0 );
meshBuilder.Color4ubv( textColor );
meshBuilder.TexCoord2f( 0, 0, 1 );
meshBuilder.AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 1>();
meshBuilder.End();
pMesh->Draw();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameUISystemSurface::DrawFontTexture( IRenderContext *pRenderContext, int textureId, int xPos, int yPos )
{
HRenderTexture fontTextureHandle = g_pGameUISystemSurface->GetTextureHandle( textureId );
pRenderContext->BindTexture( 0, fontTextureHandle );
VertexColor_t textColor( 255, 255, 255, 255);
int x = xPos;
int y = yPos;
int wide;
int tall;
TextureDictionary()->GetTextureSize( textureId, wide, tall );
CDynamicVertexData< GameUIVertex_t > vb( pRenderContext, 4, "gamelayer2", "game_controls2" );
vb.Lock();
vb->m_vecPosition.Init( x, y, 0.0f );
vb->m_color = textColor;
vb->m_vecTexCoord.Init( 0, 0 );
vb.AdvanceVertex();
vb->m_vecPosition.Init( x + wide, y, 0.0f );
vb->m_color = textColor;
vb->m_vecTexCoord.Init( 1, 0 );
vb.AdvanceVertex();
vb->m_vecPosition.Init( x+ wide, y + tall, 0.0f );
vb->m_color = textColor;
vb->m_vecTexCoord.Init( 1, 1 );
vb.AdvanceVertex();
vb->m_vecPosition.Init( x, y + tall, 0.0f );
vb->m_color = textColor;
vb->m_vecTexCoord.Init( 0, 1 );
vb.AdvanceVertex();
vb.Unlock();
vb.Bind( 0, 0 );
CDynamicIndexData< uint16 > ib( pRenderContext, 6, "gamelayer", "game_controls" );
ib.Lock();
ib.Index( 0 );
ib.Index( 1 );
ib.Index( 2 );
ib.Index( 0);
ib.Index( 2 );
ib.Index( 3 );
ib.Unlock();
ib.Bind( 0 );
pRenderContext->DrawIndexed( RENDER_PRIM_TRIANGLES, 0, 6 );
}
//-----------------------------------------------------------------------------
// Purpose: Gets the material and texture coords for this char.
// Set up info.drawtype and set the font first.
//-----------------------------------------------------------------------------
IMaterial *CGameUISystemSurface::GetTextureForChar( FontCharRenderInfo &info, float **texCoords )
{
bool bSuccess = g_FontTextureCache.GetTextureForChar( info.currentFont, info.drawType, info.ch, &info.textureId, texCoords );
if ( !bSuccess )
{
return NULL;
}
return TextureDictionary()->GetTextureMaterial( info.textureId );
}
//-----------------------------------------------------------------------------
// Purpose: Gets the material and texture coords for this char.
// Set up info.drawtype and set the font first.
// This call doesnt use the static in the font cache to store the texcoords.
//-----------------------------------------------------------------------------
IMaterial *CGameUISystemSurface::GetTextureAndCoordsForChar( FontCharRenderInfo &info, float *texCoords )
{
bool bSuccess = g_FontTextureCache.GetTextureAndCoordsForChar( info.currentFont, info.drawType, info.ch, &info.textureId, texCoords );
if ( !bSuccess )
{
return NULL;
}
return TextureDictionary()->GetTextureMaterial( info.textureId );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CGameUISystemSurface::GetUnicodeCharRenderPositions( FontCharRenderInfo& info, Vector2D *pPositions )
{
info.valid = false;
if ( !info.currentFont )
{
return info.valid;
}
info.valid = true;
info.fontTall = GetFontTall( info.currentFont );
GetCharABCwide( info.currentFont, info.ch, info.abcA, info.abcB, info.abcC );
bool bUnderlined = FontManager().GetFontUnderlined( info.currentFont );
// Do prestep before generating texture coordinates, etc.
if ( !bUnderlined )
{
info.x += info.abcA;
}
// get the character texture from the cache
info.textureId = 0;
float *texCoords = NULL;
if ( !g_FontTextureCache.GetTextureForChar( info.currentFont, info.drawType, info.ch, &info.textureId, &texCoords ) )
{
info.valid = false;
return info.valid;
}
int fontWide = info.abcB;
if ( bUnderlined )
{
fontWide += ( info.abcA + info.abcC );
info.x-= info.abcA;
}
pPositions[0].x = info.x;
pPositions[0].y = info.y;
pPositions[1].x = info.x + fontWide;
pPositions[1].y = info.y;
pPositions[2].x = info.x + fontWide;
pPositions[2].y = info.y + info.fontTall;
pPositions[3].x = info.x;
pPositions[3].y = info.y + info.fontTall;
return info.valid;
}
//-----------------------------------------------------------------------------
// Purpose: adds glyphs to a font created by CreateFont()
//-----------------------------------------------------------------------------
bool CGameUISystemSurface::SetBitmapFontGlyphSet( FontHandle_t font, const char *windowsFontName, float scalex, float scaley, int flags)
{
return FontManager().SetBitmapFontGlyphSet( font, windowsFontName, scalex, scaley, flags );
}
//-----------------------------------------------------------------------------
// Purpose: creates a new empty font
//-----------------------------------------------------------------------------
FontHandle_t CGameUISystemSurface::CreateFont()
{
return FontManager().CreateFont();
}
//-----------------------------------------------------------------------------
// Purpose: adds glyphs to a font created by CreateFont()
//-----------------------------------------------------------------------------
bool CGameUISystemSurface::SetFontGlyphSet( FontHandle_t font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax)
{
return FontManager().SetFontGlyphSet( font, windowsFontName, tall, weight, blur, scanlines,
flags, nRangeMin, nRangeMax );
}
//-----------------------------------------------------------------------------
// Purpose: returns the max height of a font
//-----------------------------------------------------------------------------
int CGameUISystemSurface::GetFontTall( FontHandle_t font )
{
return FontManager().GetFontTall( font );
}
//-----------------------------------------------------------------------------
// Purpose: returns the abc widths of a single character
// This is used by text classes to handle kerning.
//-----------------------------------------------------------------------------
void CGameUISystemSurface::GetCharABCwide( FontHandle_t font, int ch, int &a, int &b, int &c )
{
FontManager().GetCharABCwide( font, ch, a, b, c );
}
//-----------------------------------------------------------------------------
// Purpose: returns the pixel width of a single character
//-----------------------------------------------------------------------------
int CGameUISystemSurface::GetCharacterWidth( FontHandle_t font, int ch )
{
return FontManager().GetCharacterWidth( font, ch );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
IMaterial *CGameUISystemSurface::GetMaterial( int textureId )
{
return TextureDictionary()->GetTextureMaterial( textureId );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
HRenderTexture CGameUISystemSurface::GetTextureHandle( int textureId )
{
return TextureDictionary()->GetTextureHandle( textureId );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameUISystemSurface::SetLanguage( const char *pLanguage )
{
FontManager().SetLanguage( pLanguage );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CGameUISystemSurface::GetLanguage()
{
return FontManager().GetLanguage();
}