2013-04-19 02:52:48 +00:00
// leave this as first line for PCH reasons...
//
# include "../server/exe_headers.h"
# include "../qcommon/sstring.h" // stl string class won't compile in here (MS shite), so use Gil's.
# include "tr_local.h"
# include "tr_font.h"
# include "../qcommon/stringed_ingame.h"
2013-04-23 07:39:09 +00:00
# include <string>
2013-04-19 02:52:48 +00:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This file is shared in the single and multiplayer codebases, so be CAREFUL WHAT YOU ADD/CHANGE!!!!!
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef enum
{
eWestern , // ( I only care about asian languages in here at the moment )
eRussian , // .. but now I need to care about this, since it uses a different TP
ePolish , // ditto
eKorean ,
eTaiwanese , // 15x15 glyphs tucked against BR of 16x16 space
eJapanese , // 15x15 glyphs tucked against TL of 16x16 space
eChinese , // 15x15 glyphs tucked against TL of 16x16 space
eThai , // 16x16 cells with glyphs against left edge, special file (tha_widths.dat) for variable widths
} Language_e ;
// this is to cut down on all the stupid string compares I've been doing, and convert asian stuff to switch-case
//
Language_e GetLanguageEnum ( )
{
static int iSE_Language_ModificationCount = - 1234 ; // any old silly value that won't match the cvar mod count
static Language_e eLanguage = eWestern ;
// only re-strcmp() when language string has changed from what we knew it as...
//
if ( iSE_Language_ModificationCount ! = se_language - > modificationCount )
{
iSE_Language_ModificationCount = se_language - > modificationCount ;
if ( Language_IsRussian ( ) ) eLanguage = eRussian ;
else if ( Language_IsPolish ( ) ) eLanguage = ePolish ;
else if ( Language_IsKorean ( ) ) eLanguage = eKorean ;
else if ( Language_IsTaiwanese ( ) ) eLanguage = eTaiwanese ;
else if ( Language_IsJapanese ( ) ) eLanguage = eJapanese ;
else if ( Language_IsChinese ( ) ) eLanguage = eChinese ;
else if ( Language_IsThai ( ) ) eLanguage = eThai ;
else eLanguage = eWestern ;
}
return eLanguage ;
}
struct SBCSOverrideLanguages_t
{
LPCSTR m_psName ;
Language_e m_eLanguage ;
} ;
// so I can do some stuff with for-next loops when I add polish etc...
//
SBCSOverrideLanguages_t g_SBCSOverrideLanguages [ ] =
{
{ " russian " , eRussian } ,
{ " polish " , ePolish } ,
{ NULL , eWestern }
} ;
//================================================
//
# define sFILENAME_THAI_WIDTHS "fonts / tha_widths.dat"
# define sFILENAME_THAI_CODES "fonts / tha_codes.dat"
struct ThaiCodes_t
{
map < int , int > m_mapValidCodes ;
vector < int > m_viGlyphWidths ;
string m_strInitFailureReason ; // so we don't have to keep retrying to work this out
void Clear ( void )
{
m_mapValidCodes . clear ( ) ;
m_viGlyphWidths . clear ( ) ;
m_strInitFailureReason = " " ; // if blank, never failed, else says don't bother re-trying
}
ThaiCodes_t ( )
{
Clear ( ) ;
}
// convert a supplied 1,2 or 3-byte multiplied-up integer into a valid 0..n index, else -1...
//
int GetValidIndex ( int iCode )
{
map < int , int > : : iterator it = m_mapValidCodes . find ( iCode ) ;
if ( it ! = m_mapValidCodes . end ( ) )
{
return ( * it ) . second ;
}
return - 1 ;
}
int GetWidth ( int iGlyphIndex )
{
if ( iGlyphIndex < m_viGlyphWidths . size ( ) )
{
return m_viGlyphWidths [ iGlyphIndex ] ;
}
assert ( 0 ) ;
return 0 ;
}
// return is error message to display, or NULL for success
const char * Init ( void )
{
if ( m_mapValidCodes . empty ( ) & & m_viGlyphWidths . empty ( ) )
{
if ( m_strInitFailureReason . empty ( ) ) // never tried and failed already?
{
int * piData = NULL ; // note <int>, not <byte>, for []-access
//
// read the valid-codes table in...
//
int iBytesRead = FS_ReadFile ( sFILENAME_THAI_CODES , ( void * * ) & piData ) ;
if ( iBytesRead > 0 & & ! ( iBytesRead & 3 ) ) // valid length and multiple of 4 bytes long
{
int iTableEntries = iBytesRead / sizeof ( int ) ;
for ( int i = 0 ; i < iTableEntries ; i + + )
{
m_mapValidCodes [ piData [ i ] ] = i ; // convert MBCS code to sequential index...
}
FS_FreeFile ( piData ) ; // dispose of original
// now read in the widths... (I'll keep these in a simple STL vector, so they'all disappear when the <map> entries do...
//
iBytesRead = FS_ReadFile ( sFILENAME_THAI_WIDTHS , ( void * * ) & piData ) ;
if ( iBytesRead > 0 & & ! ( iBytesRead & 3 ) & & iBytesRead > > 2 /*sizeof(int)*/ = = iTableEntries )
{
for ( int i = 0 ; i < iTableEntries ; i + + )
{
m_viGlyphWidths . push_back ( piData [ i ] ) ;
}
FS_FreeFile ( piData ) ; // dispose of original
}
else
{
m_strInitFailureReason = va ( " Error with file \" %s \" , size = %d! \n " , sFILENAME_THAI_WIDTHS , iBytesRead ) ;
}
}
else
{
m_strInitFailureReason = va ( " Error with file \" %s \" , size = %d! \n " , sFILENAME_THAI_CODES , iBytesRead ) ;
}
}
}
return m_strInitFailureReason . c_str ( ) ;
}
} ;
# define GLYPH_MAX_KOREAN_SHADERS 3
# define GLYPH_MAX_TAIWANESE_SHADERS 4
# define GLYPH_MAX_JAPANESE_SHADERS 3
# define GLYPH_MAX_CHINESE_SHADERS 3
# define GLYPH_MAX_THAI_SHADERS 3
# define GLYPH_MAX_ASIAN_SHADERS 4 // this MUST equal the larger of the above defines
class CFontInfo
{
private :
// From the fontdat file
glyphInfo_t mGlyphs [ GLYPH_COUNT ] ;
// int mAsianHack; // unused junk from John's fontdat file format.
// end of fontdat data
int mShader ; // handle to the shader with the glyph
int m_hAsianShaders [ GLYPH_MAX_ASIAN_SHADERS ] ; // shaders for Korean glyphs where applicable
glyphInfo_t m_AsianGlyph ; // special glyph containing asian->western scaling info for all glyphs
int m_iAsianGlyphsAcross ; // needed to dynamically calculate S,T coords
int m_iAsianPagesLoaded ;
bool m_bAsianLastPageHalfHeight ;
int m_iLanguageModificationCount ; // doesn't matter what this is, so long as it's comparable as being changed
ThaiCodes_t * m_pThaiData ;
public :
char m_sFontName [ MAX_QPATH ] ; // eg "fonts/lcd" // needed for korean font-hint if we need >1 hangul set
int mPointSize ;
int mHeight ;
int mAscender ;
int mDescender ;
bool mbRoundCalcs ; // trying to make this !@#$%^ thing work with scaling
int m_iThisFont ; // handle to itself
int m_iAltSBCSFont ; // -1 == NULL // alternative single-byte font for languages like russian/polish etc that need to override high characters ?
int m_iOriginalFontWhenSBCSOverriden ;
float m_fAltSBCSFontScaleFactor ; // -1, else amount to adjust returned values by to make them fit the master western font they're substituting for
bool m_bIsFakeAlienLanguage ; // ... if true, don't process as MBCS or override as SBCS etc
CFontInfo ( const char * fontName ) ;
// CFontInfo(int fill) { memset(this, fill, sizeof(*this)); } // wtf?
~ CFontInfo ( void ) { }
const int GetPointSize ( void ) const { return ( mPointSize ) ; }
const int GetHeight ( void ) const { return ( mHeight ) ; }
const int GetAscender ( void ) const { return ( mAscender ) ; }
const int GetDescender ( void ) const { return ( mDescender ) ; }
const glyphInfo_t * GetLetter ( const unsigned int uiLetter , int * piShader = NULL ) ;
const int GetCollapsedAsianCode ( ulong uiLetter ) const ;
const int GetLetterWidth ( const unsigned int uiLetter ) ;
const int GetLetterHorizAdvance ( const unsigned int uiLetter ) ;
const int GetShader ( void ) const { return ( mShader ) ; }
void FlagNoAsianGlyphs ( void ) { m_hAsianShaders [ 0 ] = 0 ; m_iLanguageModificationCount = - 1 ; } // used during constructor
bool AsianGlyphsAvailable ( void ) const { return ! ! ( m_hAsianShaders [ 0 ] ) ; }
void UpdateAsianIfNeeded ( bool bForceReEval = false ) ;
} ;
//================================================
// round float to one decimal place...
//
float RoundTenth ( float fValue )
{
return ( floorf ( ( fValue * 10.0f ) + 0.5f ) ) / 10.0f ;
}
int g_iCurrentFontIndex ; // entry 0 is reserved index for missing/invalid, else ++ with each new font registered
vector < CFontInfo * > g_vFontArray ;
typedef map < sstring_t , int > FontIndexMap_t ;
FontIndexMap_t g_mapFontIndexes ;
int g_iNonScaledCharRange ; // this is used with auto-scaling of asian fonts, anything below this number is preserved in scale, anything above is scaled down by 0.75f
//paletteRGBA_c lastcolour;
// =============================== some korean stuff =======================================
# define KSC5601_HANGUL_HIBYTE_START 0xB0 // range is...
# define KSC5601_HANGUL_HIBYTE_STOP 0xC8 // ... inclusive
# define KSC5601_HANGUL_LOBYTE_LOBOUND 0xA0 // range is...
# define KSC5601_HANGUL_LOBYTE_HIBOUND 0xFF // ...bounding (ie only valid in between these points, but NULLs in charsets for these codes)
# define KSC5601_HANGUL_CODES_PER_ROW 96 // 2 more than the number of glyphs
extern qboolean Language_IsKorean ( void ) ;
static inline bool Korean_ValidKSC5601Hangul ( byte _iHi , byte _iLo )
{
return ( _iHi > = KSC5601_HANGUL_HIBYTE_START & &
_iHi < = KSC5601_HANGUL_HIBYTE_STOP & &
_iLo > KSC5601_HANGUL_LOBYTE_LOBOUND & &
_iLo < KSC5601_HANGUL_LOBYTE_HIBOUND
) ;
}
static inline bool Korean_ValidKSC5601Hangul ( unsigned int uiCode )
{
return Korean_ValidKSC5601Hangul ( uiCode > > 8 , uiCode & 0xFF ) ;
}
// takes a KSC5601 double-byte hangul code and collapses down to a 0..n glyph index...
// Assumes rows are 96 wide (glyph slots), not 94 wide (actual glyphs), so I can ignore boundary markers
//
// (invalid hangul codes will return 0)
//
static int Korean_CollapseKSC5601HangulCode ( unsigned int uiCode )
{
if ( Korean_ValidKSC5601Hangul ( uiCode ) )
{
uiCode - = ( KSC5601_HANGUL_HIBYTE_START * 256 ) + KSC5601_HANGUL_LOBYTE_LOBOUND ; // sneaky maths on both bytes, reduce to 0x0000 onwards
uiCode = ( ( uiCode > > 8 ) * KSC5601_HANGUL_CODES_PER_ROW ) + ( uiCode & 0xFF ) ;
return uiCode ;
}
return 0 ;
}
static int Korean_InitFields ( int & iGlyphTPs , LPCSTR & psLang )
{
psLang = " kor " ;
iGlyphTPs = GLYPH_MAX_KOREAN_SHADERS ;
g_iNonScaledCharRange = 255 ;
return 32 ; // m_iAsianGlyphsAcross
}
// ======================== some taiwanese stuff ==============================
// (all ranges inclusive for Big5)...
//
# define BIG5_HIBYTE_START0 0xA1 // (misc chars + level 1 hanzi)
# define BIG5_HIBYTE_STOP0 0xC6 //
# define BIG5_HIBYTE_START1 0xC9 // (level 2 hanzi)
# define BIG5_HIBYTE_STOP1 0xF9 //
# define BIG5_LOBYTE_LOBOUND0 0x40 //
# define BIG5_LOBYTE_HIBOUND0 0x7E //
# define BIG5_LOBYTE_LOBOUND1 0xA1 //
# define BIG5_LOBYTE_HIBOUND1 0xFE //
# define BIG5_CODES_PER_ROW 160 // 3 more than the number of glyphs
extern qboolean Language_IsTaiwanese ( void ) ;
static bool Taiwanese_ValidBig5Code ( unsigned int uiCode )
{
const byte _iHi = ( uiCode > > 8 ) & 0xFF ;
if ( ( _iHi > = BIG5_HIBYTE_START0 & & _iHi < = BIG5_HIBYTE_STOP0 )
| | ( _iHi > = BIG5_HIBYTE_START1 & & _iHi < = BIG5_HIBYTE_STOP1 )
)
{
const byte _iLo = uiCode & 0xFF ;
if ( ( _iLo > = BIG5_LOBYTE_LOBOUND0 & & _iLo < = BIG5_LOBYTE_HIBOUND0 ) | |
( _iLo > = BIG5_LOBYTE_LOBOUND1 & & _iLo < = BIG5_LOBYTE_HIBOUND1 )
)
{
return true ;
}
}
return false ;
}
// only call this when Taiwanese_ValidBig5Code() has already returned true...
//
static bool Taiwanese_IsTrailingPunctuation ( unsigned int uiCode )
{
// so far I'm just counting the first 21 chars, those seem to be all the basic punctuation...
//
if ( uiCode > = ( ( BIG5_HIBYTE_START0 < < 8 ) | BIG5_LOBYTE_LOBOUND0 ) & &
uiCode < ( ( BIG5_HIBYTE_START0 < < 8 ) | BIG5_LOBYTE_LOBOUND0 + 20 )
)
{
return true ;
}
return false ;
}
// takes a BIG5 double-byte code (including level 2 hanzi) and collapses down to a 0..n glyph index...
// Assumes rows are 160 wide (glyph slots), not 157 wide (actual glyphs), so I can ignore boundary markers
//
// (invalid big5 codes will return 0)
//
static int Taiwanese_CollapseBig5Code ( unsigned int uiCode )
{
if ( Taiwanese_ValidBig5Code ( uiCode ) )
{
uiCode - = ( BIG5_HIBYTE_START0 * 256 ) + BIG5_LOBYTE_LOBOUND0 ; // sneaky maths on both bytes, reduce to 0x0000 onwards
if ( ( uiCode & 0xFF ) > = ( BIG5_LOBYTE_LOBOUND1 - 1 ) - BIG5_LOBYTE_LOBOUND0 )
{
uiCode - = ( ( BIG5_LOBYTE_LOBOUND1 - 1 ) - ( BIG5_LOBYTE_HIBOUND0 + 1 ) ) - 1 ;
}
uiCode = ( ( uiCode > > 8 ) * BIG5_CODES_PER_ROW ) + ( uiCode & 0xFF ) ;
return uiCode ;
}
return 0 ;
}
static int Taiwanese_InitFields ( int & iGlyphTPs , LPCSTR & psLang )
{
psLang = " tai " ;
iGlyphTPs = GLYPH_MAX_TAIWANESE_SHADERS ;
g_iNonScaledCharRange = 255 ;
return 64 ; // m_iAsianGlyphsAcross
}
// ======================== some Japanese stuff ==============================
// ( all ranges inclusive for Shift-JIS )
//
# define SHIFTJIS_HIBYTE_START0 0x81
# define SHIFTJIS_HIBYTE_STOP0 0x9F
# define SHIFTJIS_HIBYTE_START1 0xE0
# define SHIFTJIS_HIBYTE_STOP1 0xEF
//
# define SHIFTJIS_LOBYTE_START0 0x40
# define SHIFTJIS_LOBYTE_STOP0 0x7E
# define SHIFTJIS_LOBYTE_START1 0x80
# define SHIFTJIS_LOBYTE_STOP1 0xFC
# define SHIFTJIS_CODES_PER_ROW (((SHIFTJIS_LOBYTE_STOP0-SHIFTJIS_LOBYTE_START0)+1)+((SHIFTJIS_LOBYTE_STOP1-SHIFTJIS_LOBYTE_START1)+1))
extern qboolean Language_IsJapanese ( void ) ;
static bool Japanese_ValidShiftJISCode ( byte _iHi , byte _iLo )
{
if ( ( _iHi > = SHIFTJIS_HIBYTE_START0 & & _iHi < = SHIFTJIS_HIBYTE_STOP0 )
| | ( _iHi > = SHIFTJIS_HIBYTE_START1 & & _iHi < = SHIFTJIS_HIBYTE_STOP1 )
)
{
if ( ( _iLo > = SHIFTJIS_LOBYTE_START0 & & _iLo < = SHIFTJIS_LOBYTE_STOP0 ) | |
( _iLo > = SHIFTJIS_LOBYTE_START1 & & _iLo < = SHIFTJIS_LOBYTE_STOP1 )
)
{
return true ;
}
}
return false ;
}
static inline bool Japanese_ValidShiftJISCode ( unsigned int uiCode )
{
return Japanese_ValidShiftJISCode ( uiCode > > 8 , uiCode & 0xFF ) ;
}
// only call this when Japanese_ValidShiftJISCode() has already returned true...
//
static bool Japanese_IsTrailingPunctuation ( unsigned int uiCode )
{
// so far I'm just counting the first 18 chars, those seem to be all the basic punctuation...
//
if ( uiCode > = ( ( SHIFTJIS_HIBYTE_START0 < < 8 ) | SHIFTJIS_LOBYTE_START0 ) & &
uiCode < ( ( SHIFTJIS_HIBYTE_START0 < < 8 ) | SHIFTJIS_LOBYTE_START0 + 18 )
)
{
return true ;
}
return false ;
}
// takes a ShiftJIS double-byte code and collapse down to a 0..n glyph index...
//
// (invalid codes will return 0)
//
static int Japanese_CollapseShiftJISCode ( unsigned int uiCode )
{
if ( Japanese_ValidShiftJISCode ( uiCode ) )
{
uiCode - = ( ( SHIFTJIS_HIBYTE_START0 < < 8 ) | SHIFTJIS_LOBYTE_START0 ) ; // sneaky maths on both bytes, reduce to 0x0000 onwards
if ( ( uiCode & 0xFF ) > = ( SHIFTJIS_LOBYTE_START1 ) - SHIFTJIS_LOBYTE_START0 )
{
uiCode - = ( ( SHIFTJIS_LOBYTE_START1 ) - SHIFTJIS_LOBYTE_STOP0 ) - 1 ;
}
if ( ( ( uiCode > > 8 ) & 0xFF ) > = ( SHIFTJIS_HIBYTE_START1 ) - SHIFTJIS_HIBYTE_START0 )
{
uiCode - = ( ( ( SHIFTJIS_HIBYTE_START1 ) - SHIFTJIS_HIBYTE_STOP0 ) - 1 ) < < 8 ;
}
uiCode = ( ( uiCode > > 8 ) * SHIFTJIS_CODES_PER_ROW ) + ( uiCode & 0xFF ) ;
return uiCode ;
}
return 0 ;
}
static int Japanese_InitFields ( int & iGlyphTPs , LPCSTR & psLang )
{
psLang = " jap " ;
iGlyphTPs = GLYPH_MAX_JAPANESE_SHADERS ;
g_iNonScaledCharRange = 255 ;
return 64 ; // m_iAsianGlyphsAcross
}
// ======================== some Chinese stuff ==============================
# define GB_HIBYTE_START 0xA1 // range is...
# define GB_HIBYTE_STOP 0xF7 // ... inclusive
# define GB_LOBYTE_LOBOUND 0xA0 // range is...
# define GB_LOBYTE_HIBOUND 0xFF // ...bounding (ie only valid in between these points, but NULLs in charsets for these codes)
# define GB_CODES_PER_ROW 95 // 1 more than the number of glyphs
extern qboolean Language_IsChinese ( void ) ;
static inline bool Chinese_ValidGBCode ( byte _iHi , byte _iLo )
{
return ( _iHi > = GB_HIBYTE_START & &
_iHi < = GB_HIBYTE_STOP & &
_iLo > GB_LOBYTE_LOBOUND & &
_iLo < GB_LOBYTE_HIBOUND
) ;
}
static inline bool Chinese_ValidGBCode ( unsigned int uiCode )
{
return Chinese_ValidGBCode ( uiCode > > 8 , uiCode & 0xFF ) ;
}
// only call this when Chinese_ValidGBCode() has already returned true...
//
static bool Chinese_IsTrailingPunctuation ( unsigned int uiCode )
{
// so far I'm just counting the first 13 chars, those seem to be all the basic punctuation...
//
if ( uiCode > ( ( GB_HIBYTE_START < < 8 ) | GB_LOBYTE_LOBOUND ) & &
uiCode < ( ( GB_HIBYTE_START < < 8 ) | GB_LOBYTE_LOBOUND + 14 )
)
{
return true ;
}
return false ;
}
// takes a GB double-byte code and collapses down to a 0..n glyph index...
// Assumes rows are 96 wide (glyph slots), not 94 wide (actual glyphs), so I can ignore boundary markers
//
// (invalid GB codes will return 0)
//
static int Chinese_CollapseGBCode ( unsigned int uiCode )
{
if ( Chinese_ValidGBCode ( uiCode ) )
{
uiCode - = ( GB_HIBYTE_START * 256 ) + GB_LOBYTE_LOBOUND ; // sneaky maths on both bytes, reduce to 0x0000 onwards
uiCode = ( ( uiCode > > 8 ) * GB_CODES_PER_ROW ) + ( uiCode & 0xFF ) ;
return uiCode ;
}
return 0 ;
}
static int Chinese_InitFields ( int & iGlyphTPs , LPCSTR & psLang )
{
psLang = " chi " ;
iGlyphTPs = GLYPH_MAX_CHINESE_SHADERS ;
g_iNonScaledCharRange = 255 ;
return 64 ; // m_iAsianGlyphsAcross
}
// ======================== some Thai stuff ==============================
//TIS 620-2533
# define TIS_GLYPHS_START 160
# define TIS_SARA_AM 0xD3 // special case letter, both a new letter and a trailing accent for the prev one
ThaiCodes_t g_ThaiCodes ; // the one and only instance of this object
extern qboolean Language_IsThai ( void ) ;
/*
static int Thai_IsAccentChar ( unsigned int uiCode )
{
switch ( uiCode )
{
case 209 :
case 212 : case 213 : case 214 : case 215 : case 216 : case 217 : case 218 :
case 231 : case 232 : case 233 : case 234 : case 235 : case 236 : case 237 : case 238 :
return true ;
}
return false ;
}
*/
// returns a valid Thai code (or 0), based on taking 1,2 or 3 bytes from the supplied byte stream
// Fills in <iThaiBytes> with 1,2 or 3
static int Thai_ValidTISCode ( const byte * psString , int & iThaiBytes )
{
// try a 1-byte code first...
//
if ( psString [ 0 ] > = 160 ) // so western letters drop through and use normal font
{
// this code is heavily little-endian, so someone else will need to port for Mac etc... (not my problem ;-)
//
union CodeToTry_t
{
char sChars [ 4 ] ;
unsigned int uiCode ;
} ;
CodeToTry_t CodeToTry ;
CodeToTry . uiCode = 0 ; // important that we clear all 4 bytes in sChars here
// thai codes can be up to 3 bytes long, so see how high we can get...
//
for ( int i = 0 ; i < 3 ; i + + )
{
CodeToTry . sChars [ i ] = psString [ i ] ;
int iIndex = g_ThaiCodes . GetValidIndex ( CodeToTry . uiCode ) ;
if ( iIndex = = - 1 )
{
// failed, so return previous-longest code...
//
CodeToTry . sChars [ i ] = 0 ;
break ;
}
}
iThaiBytes = i ;
assert ( i ) ; // if 'i' was 0, then this may be an error, trying to get a thai accent as standalone char?
return CodeToTry . uiCode ;
}
return 0 ;
}
// special case, thai can only break on certain letters, and since the rules are complicated then
// we tell the translators to put an underscore ('_') between each word even though in Thai they're
// all jammed together at final output onscreen...
//
static inline bool Thai_IsTrailingPunctuation ( unsigned int uiCode )
{
return uiCode = = ' _ ' ;
}
// takes a TIS 1,2 or 3 byte code and collapse down to a 0..n glyph index...
//
// (invalid codes will return 0)
//
static int Thai_CollapseTISCode ( unsigned int uiCode )
{
if ( uiCode > = TIS_GLYPHS_START ) // so western letters drop through as invalid
{
int iCollapsedIndex = g_ThaiCodes . GetValidIndex ( uiCode ) ;
if ( iCollapsedIndex ! = - 1 )
{
return iCollapsedIndex ;
}
}
return 0 ;
}
static int Thai_InitFields ( int & iGlyphTPs , LPCSTR & psLang )
{
psLang = " tha " ;
iGlyphTPs = GLYPH_MAX_THAI_SHADERS ;
g_iNonScaledCharRange = INT_MAX ; // in other words, don't scale any thai chars down
return 32 ; // m_iAsianGlyphsAcross
}
// ============================================================================
// takes char *, returns integer char at that point, and advances char * on by enough bytes to move
// past the letter (either western 1 byte or Asian multi-byte)...
//
// looks messy, but the actual execution route is quite short, so it's fast...
//
// Note that I have to have this 3-param form instead of advancing a passed-in "const char **psText" because of VM-crap where you can only change ptr-contents, not ptrs themselves. Bleurgh. Ditto the qtrue:qfalse crap instead of just returning stuff straight through.
//
unsigned int AnyLanguage_ReadCharFromString ( const char * psText , int * piAdvanceCount , qboolean * pbIsTrailingPunctuation /* = NULL */ )
{
const byte * psString = ( const byte * ) psText ; // avoid sign-promote bug
unsigned int uiLetter ;
switch ( GetLanguageEnum ( ) )
{
case eKorean :
{
if ( Korean_ValidKSC5601Hangul ( psString [ 0 ] , psString [ 1 ] ) )
{
uiLetter = ( psString [ 0 ] * 256 ) + psString [ 1 ] ;
* piAdvanceCount = 2 ;
// not going to bother testing for korean punctuation here, since korean already
// uses spaces, and I don't have the punctuation glyphs defined, only the basic 2350 hanguls
//
if ( pbIsTrailingPunctuation )
{
* pbIsTrailingPunctuation = qfalse ;
}
return uiLetter ;
}
}
break ;
case eTaiwanese :
{
if ( Taiwanese_ValidBig5Code ( ( psString [ 0 ] * 256 ) + psString [ 1 ] ) )
{
uiLetter = ( psString [ 0 ] * 256 ) + psString [ 1 ] ;
* piAdvanceCount = 2 ;
// need to ask if this is a trailing (ie like a comma or full-stop) punctuation?...
//
if ( pbIsTrailingPunctuation )
{
* pbIsTrailingPunctuation = Taiwanese_IsTrailingPunctuation ( uiLetter ) ? qtrue : qfalse ;
}
return uiLetter ;
}
}
break ;
case eJapanese :
{
if ( Japanese_ValidShiftJISCode ( psString [ 0 ] , psString [ 1 ] ) )
{
uiLetter = ( psString [ 0 ] * 256 ) + psString [ 1 ] ;
* piAdvanceCount = 2 ;
// need to ask if this is a trailing (ie like a comma or full-stop) punctuation?...
//
if ( pbIsTrailingPunctuation )
{
* pbIsTrailingPunctuation = Japanese_IsTrailingPunctuation ( uiLetter ) ? qtrue : qfalse ;
}
return uiLetter ;
}
}
break ;
case eChinese :
{
if ( Chinese_ValidGBCode ( ( psString [ 0 ] * 256 ) + psString [ 1 ] ) )
{
uiLetter = ( psString [ 0 ] * 256 ) + psString [ 1 ] ;
* piAdvanceCount = 2 ;
// need to ask if this is a trailing (ie like a comma or full-stop) punctuation?...
//
if ( pbIsTrailingPunctuation )
{
* pbIsTrailingPunctuation = Chinese_IsTrailingPunctuation ( uiLetter ) ? qtrue : qfalse ;
}
return uiLetter ;
}
}
break ;
case eThai :
{
int iThaiBytes ;
uiLetter = Thai_ValidTISCode ( psString , iThaiBytes ) ;
if ( uiLetter )
{
* piAdvanceCount = iThaiBytes ;
if ( pbIsTrailingPunctuation )
{
* pbIsTrailingPunctuation = Thai_IsTrailingPunctuation ( uiLetter ) ? qtrue : qfalse ;
}
return uiLetter ;
}
}
break ;
}
// ... must not have been an MBCS code...
//
uiLetter = psString [ 0 ] ;
* piAdvanceCount = 1 ;
if ( pbIsTrailingPunctuation )
{
* pbIsTrailingPunctuation = ( uiLetter = = ' ! ' | |
uiLetter = = ' ? ' | |
uiLetter = = ' , ' | |
uiLetter = = ' . ' | |
uiLetter = = ' ; ' | |
uiLetter = = ' : '
) ? qtrue : qfalse ;
}
return uiLetter ;
}
// needed for subtitle printing since original code no longer worked once camera bar height was changed to 480/10
// rather than refdef height / 10. I now need to bodge the coords to come out right.
//
qboolean Language_IsAsian ( void )
{
switch ( GetLanguageEnum ( ) )
{
case eKorean :
case eTaiwanese :
case eJapanese :
case eChinese :
case eThai : // this is asian, but the query is normally used for scaling
return qtrue ;
}
return qfalse ;
}
qboolean Language_UsesSpaces ( void )
{
// ( korean uses spaces )
switch ( GetLanguageEnum ( ) )
{
case eTaiwanese :
case eJapanese :
case eChinese :
case eThai :
return qfalse ;
}
return qtrue ;
}
// ======================================================================
// name is (eg) "ergo" or "lcd", no extension.
//
// If path present, it's a special language hack for SBCS override languages, eg: "lcd/russian", which means
// just treat the file as "russian", but with the "lcd" part ensuring we don't find a different registered russian font
//
CFontInfo : : CFontInfo ( const char * _fontName )
{
int len , i ;
void * buff ;
dfontdat_t * fontdat ;
// remove any special hack name insertions...
//
char fontName [ MAX_QPATH ] ;
sprintf ( fontName , " fonts/%s.fontdat " , COM_SkipPath ( const_cast < char * > ( _fontName ) ) ) ; // COM_SkipPath should take a const char *, but it's just possible people use it as a char * I guess, so I have to hack around like this <groan>
// clear some general things...
//
m_pThaiData = NULL ;
m_iAltSBCSFont = - 1 ;
m_iThisFont = - 1 ;
m_iOriginalFontWhenSBCSOverriden = - 1 ;
m_fAltSBCSFontScaleFactor = - 1 ;
m_bIsFakeAlienLanguage = ! strcmp ( _fontName , " aurabesh " ) ; // dont try and make SBCS or asian overrides for this
len = FS_ReadFile ( fontName , NULL ) ;
if ( len = = sizeof ( dfontdat_t ) )
{
FS_ReadFile ( fontName , & buff ) ;
fontdat = ( dfontdat_t * ) buff ;
for ( i = 0 ; i < GLYPH_COUNT ; i + + )
{
mGlyphs [ i ] = fontdat - > mGlyphs [ i ] ;
}
mPointSize = fontdat - > mPointSize ;
mHeight = fontdat - > mHeight ;
mAscender = fontdat - > mAscender ;
mDescender = fontdat - > mDescender ;
// mAsianHack = fontdat->mKoreanHack; // ignore this crap, it's some junk in the fontdat file that no-one uses
mbRoundCalcs = ! ! strstr ( fontName , " ergo " ) ;
// cope with bad fontdat headers...
//
if ( mHeight = = 0 )
{
mHeight = mPointSize ;
mAscender = mPointSize - Round ( ( ( float ) mPointSize / 10.0f ) + 2 ) ; // have to completely guess at the baseline... sigh.
mDescender = mHeight - mAscender ;
}
FS_FreeFile ( buff ) ;
}
else
{
mHeight = 0 ;
mShader = 0 ;
}
Q_strncpyz ( m_sFontName , fontName , sizeof ( m_sFontName ) ) ;
COM_StripExtension ( m_sFontName , m_sFontName ) ; // so we get better error printing if failed to load shader (ie lose ".fontdat")
mShader = RE_RegisterShaderNoMip ( m_sFontName ) ;
FlagNoAsianGlyphs ( ) ;
UpdateAsianIfNeeded ( true ) ;
// finished...
g_vFontArray . resize ( g_iCurrentFontIndex + 1 ) ;
g_vFontArray [ g_iCurrentFontIndex + + ] = this ;
extern cvar_t * com_buildScript ;
if ( com_buildScript - > integer = = 2 )
{
Com_Printf ( " com_buildScript(2): Registering foreign fonts... \n " ) ;
static qboolean bDone = qfalse ; // Do this once only (for speed)...
if ( ! bDone )
{
bDone = qtrue ;
char sTemp [ MAX_QPATH ] ;
int iGlyphTPs = 0 ;
const char * psLang = NULL ;
// SBCS override languages...
//
fileHandle_t f ;
for ( int i = 0 ; g_SBCSOverrideLanguages [ i ] . m_psName ; i + + )
{
char sTemp [ MAX_QPATH ] ;
sprintf ( sTemp , " fonts/%s.tga " , g_SBCSOverrideLanguages [ i ] . m_psName ) ;
FS_FOpenFileRead ( sTemp , & f , qfalse ) ;
if ( f ) FS_FCloseFile ( f ) ;
sprintf ( sTemp , " fonts/%s.fontdat " , g_SBCSOverrideLanguages [ i ] . m_psName ) ;
FS_FOpenFileRead ( sTemp , & f , qfalse ) ;
if ( f ) FS_FCloseFile ( f ) ;
}
// asian MBCS override languages...
//
for ( int iLang = 0 ; iLang < 5 ; iLang + + )
{
switch ( iLang )
{
case 0 : m_iAsianGlyphsAcross = Korean_InitFields ( iGlyphTPs , psLang ) ; break ;
case 1 : m_iAsianGlyphsAcross = Taiwanese_InitFields ( iGlyphTPs , psLang ) ; break ;
case 2 : m_iAsianGlyphsAcross = Japanese_InitFields ( iGlyphTPs , psLang ) ; break ;
case 3 : m_iAsianGlyphsAcross = Chinese_InitFields ( iGlyphTPs , psLang ) ; break ;
case 4 : m_iAsianGlyphsAcross = Thai_InitFields ( iGlyphTPs , psLang ) ;
{
// additional files needed for Thai language...
//
FS_FOpenFileRead ( sFILENAME_THAI_WIDTHS , & f , qfalse ) ;
if ( f ) {
FS_FCloseFile ( f ) ;
}
FS_FOpenFileRead ( sFILENAME_THAI_CODES , & f , qfalse ) ;
if ( f ) {
FS_FCloseFile ( f ) ;
}
}
break ;
}
for ( int i = 0 ; i < iGlyphTPs ; i + + )
{
Com_sprintf ( sTemp , sizeof ( sTemp ) , " fonts/%s_%d_1024_%d.tga " , psLang , 1024 / m_iAsianGlyphsAcross , i ) ;
// RE_RegisterShaderNoMip( sTemp ); // don't actually need to load it, so...
FS_FOpenFileRead ( sTemp , & f , qfalse ) ;
if ( f ) {
FS_FCloseFile ( f ) ;
}
}
}
}
}
}
void CFontInfo : : UpdateAsianIfNeeded ( bool bForceReEval /* = false */ )
{
// if asian language, then provide an alternative glyph set and fill in relevant fields...
//
if ( mHeight & & ! m_bIsFakeAlienLanguage ) // western charset exists in first place, and isn't alien rubbish?
{
Language_e eLanguage = GetLanguageEnum ( ) ;
if ( eLanguage = = eKorean | | eLanguage = = eTaiwanese | | eLanguage = = eJapanese | | eLanguage = = eChinese | | eLanguage = = eThai )
{
int iCappedHeight = mHeight < 16 ? 16 : mHeight ; // arbitrary limit on small char sizes because Asian chars don't squash well
if ( m_iLanguageModificationCount ! = se_language - > modificationCount | | ! AsianGlyphsAvailable ( ) | | bForceReEval )
{
m_iLanguageModificationCount = se_language - > modificationCount ;
int iGlyphTPs = 0 ;
const char * psLang = NULL ;
switch ( eLanguage )
{
case eKorean : m_iAsianGlyphsAcross = Korean_InitFields ( iGlyphTPs , psLang ) ; break ;
case eTaiwanese : m_iAsianGlyphsAcross = Taiwanese_InitFields ( iGlyphTPs , psLang ) ; break ;
case eJapanese : m_iAsianGlyphsAcross = Japanese_InitFields ( iGlyphTPs , psLang ) ; break ;
case eChinese : m_iAsianGlyphsAcross = Chinese_InitFields ( iGlyphTPs , psLang ) ; break ;
case eThai :
{
m_iAsianGlyphsAcross = Thai_InitFields ( iGlyphTPs , psLang ) ;
if ( ! m_pThaiData )
{
LPCSTR psFailureReason = g_ThaiCodes . Init ( ) ;
if ( ! psFailureReason [ 0 ] )
{
m_pThaiData = & g_ThaiCodes ;
}
else
{
// failed to load a needed file, reset to English...
//
Cvar_Set ( " se_language " , " english " ) ;
Com_Error ( ERR_DROP , psFailureReason ) ;
}
}
}
break ;
}
// textures need loading...
//
if ( m_sFontName [ 0 ] )
{
// Use this sometime if we need to do logic to load alternate-height glyphs to better fit other fonts.
// (but for now, we just use the one glyph set)
//
}
for ( int i = 0 ; i < iGlyphTPs ; i + + )
{
// (Note!! assumption for S,T calculations: all Asian glyph textures pages are square except for last one)
//
char sTemp [ MAX_QPATH ] ;
Com_sprintf ( sTemp , sizeof ( sTemp ) , " fonts/%s_%d_1024_%d " , psLang , 1024 / m_iAsianGlyphsAcross , i ) ;
//
// returning 0 here will automatically inhibit Asian glyph calculations at runtime...
//
m_hAsianShaders [ i ] = RE_RegisterShaderNoMip ( sTemp ) ;
}
// for now I'm hardwiring these, but if we ever have more than one glyph set per language then they'll be changed...
//
m_iAsianPagesLoaded = iGlyphTPs ; // not necessarily true, but will be safe, and show up obvious if something missing
m_bAsianLastPageHalfHeight = true ;
bForceReEval = true ;
}
if ( bForceReEval )
{
// now init the Asian member glyph fields to make them come out the same size as the western ones
// that they serve as an alternative for...
//
m_AsianGlyph . width = iCappedHeight ; // square Asian chars same size as height of western set
m_AsianGlyph . height = iCappedHeight ; // ""
switch ( eLanguage )
{
default : m_AsianGlyph . horizAdvance = iCappedHeight ; break ;
case eKorean : m_AsianGlyph . horizAdvance = iCappedHeight - 1 ; break ; // korean has a small amount of space at the edge of the glyph
case eTaiwanese :
case eJapanese :
case eChinese : m_AsianGlyph . horizAdvance = iCappedHeight + 3 ; // need to force some spacing for these
// case eThai: // this is done dynamically elsewhere, since Thai glyphs are variable width
}
m_AsianGlyph . horizOffset = 0 ; // ""
m_AsianGlyph . baseline = mAscender + ( ( iCappedHeight - mHeight ) > > 1 ) ;
}
}
else
{
// not using Asian...
//
FlagNoAsianGlyphs ( ) ;
}
}
else
{
// no western glyphs available, so don't attempt to match asian...
//
FlagNoAsianGlyphs ( ) ;
}
}
static CFontInfo * GetFont_Actual ( int index )
{
index & = SET_MASK ;
if ( ( index > = 1 ) & & ( index < g_iCurrentFontIndex ) )
{
CFontInfo * pFont = g_vFontArray [ index ] ;
if ( pFont )
{
pFont - > UpdateAsianIfNeeded ( ) ;
}
return pFont ;
}
return ( NULL ) ;
}
// needed to add *piShader param because of multiple TPs,
// if not passed in, then I also skip S,T calculations for re-usable static asian glyphinfo struct...
//
const glyphInfo_t * CFontInfo : : GetLetter ( const unsigned int uiLetter , int * piShader /* = NULL */ )
{
if ( AsianGlyphsAvailable ( ) )
{
int iCollapsedAsianCode = GetCollapsedAsianCode ( uiLetter ) ;
if ( iCollapsedAsianCode )
{
if ( piShader )
{
// (Note!! assumption for S,T calculations: all asian glyph textures pages are square except for last one
// which may or may not be half height) - but not for Thai
//
int iTexturePageIndex = iCollapsedAsianCode / ( m_iAsianGlyphsAcross * m_iAsianGlyphsAcross ) ;
if ( iTexturePageIndex > m_iAsianPagesLoaded )
{
assert ( 0 ) ; // should never happen
iTexturePageIndex = 0 ;
}
int iOriginalCollapsedAsianCode = iCollapsedAsianCode ; // need to back this up (if Thai) for later
iCollapsedAsianCode - = iTexturePageIndex * ( m_iAsianGlyphsAcross * m_iAsianGlyphsAcross ) ;
const int iColumn = iCollapsedAsianCode % m_iAsianGlyphsAcross ;
const int iRow = iCollapsedAsianCode / m_iAsianGlyphsAcross ;
const bool bHalfT = ( iTexturePageIndex = = ( m_iAsianPagesLoaded - 1 ) & & m_bAsianLastPageHalfHeight ) ;
const int iAsianGlyphsDown = ( bHalfT ) ? m_iAsianGlyphsAcross / 2 : m_iAsianGlyphsAcross ;
switch ( GetLanguageEnum ( ) )
{
case eKorean :
default :
{
m_AsianGlyph . s = ( float ) ( iColumn ) / ( float ) m_iAsianGlyphsAcross ;
m_AsianGlyph . t = ( float ) ( iRow ) / ( float ) iAsianGlyphsDown ;
m_AsianGlyph . s2 = ( float ) ( iColumn + 1 ) / ( float ) m_iAsianGlyphsAcross ;
m_AsianGlyph . t2 = ( float ) ( iRow + 1 ) / ( float ) iAsianGlyphsDown ;
}
break ;
case eTaiwanese :
{
m_AsianGlyph . s = ( float ) ( ( ( 1024 / m_iAsianGlyphsAcross ) * ( iColumn ) ) + 1 ) / 1024.0f ;
m_AsianGlyph . t = ( float ) ( ( ( 1024 / iAsianGlyphsDown ) * ( iRow ) ) + 1 ) / 1024.0f ;
m_AsianGlyph . s2 = ( float ) ( ( ( 1024 / m_iAsianGlyphsAcross ) * ( iColumn + 1 ) ) ) / 1024.0f ;
m_AsianGlyph . t2 = ( float ) ( ( ( 1024 / iAsianGlyphsDown ) * ( iRow + 1 ) ) ) / 1024.0f ;
}
break ;
case eJapanese :
case eChinese :
{
m_AsianGlyph . s = ( float ) ( ( ( 1024 / m_iAsianGlyphsAcross ) * ( iColumn ) ) ) / 1024.0f ;
m_AsianGlyph . t = ( float ) ( ( ( 1024 / iAsianGlyphsDown ) * ( iRow ) ) ) / 1024.0f ;
m_AsianGlyph . s2 = ( float ) ( ( ( 1024 / m_iAsianGlyphsAcross ) * ( iColumn + 1 ) ) - 1 ) / 1024.0f ;
m_AsianGlyph . t2 = ( float ) ( ( ( 1024 / iAsianGlyphsDown ) * ( iRow + 1 ) ) - 1 ) / 1024.0f ;
}
break ;
case eThai :
{
int iGlyphXpos = ( 1024 / m_iAsianGlyphsAcross ) * ( iColumn ) ;
int iGlyphWidth = g_ThaiCodes . GetWidth ( iOriginalCollapsedAsianCode ) ;
// very thai-specific language-code...
//
if ( uiLetter = = TIS_SARA_AM )
{
iGlyphXpos + = 9 ; // these are pixel coords on the source TP, so don't affect scaled output
iGlyphWidth = 20 ; //
}
m_AsianGlyph . s = ( float ) ( iGlyphXpos ) / 1024.0f ;
m_AsianGlyph . t = ( float ) ( ( ( 1024 / iAsianGlyphsDown ) * ( iRow ) ) ) / 1024.0f ;
// technically this .s2 line should be modified to blit only the correct width, but since
// all Thai glyphs are up against the left edge of their cells and have blank to the cell
// boundary then it's better to keep these calculations simpler...
m_AsianGlyph . s2 = ( float ) ( iGlyphXpos + iGlyphWidth ) / 1024.0f ;
m_AsianGlyph . t2 = ( float ) ( ( ( 1024 / iAsianGlyphsDown ) * ( iRow + 1 ) ) - 1 ) / 1024.0f ;
// special addition for Thai, need to bodge up the width and advance fields...
//
m_AsianGlyph . width = iGlyphWidth ;
m_AsianGlyph . horizAdvance = iGlyphWidth + 1 ;
}
break ;
}
* piShader = m_hAsianShaders [ iTexturePageIndex ] ;
}
return & m_AsianGlyph ;
}
}
if ( piShader )
{
* piShader = GetShader ( ) ;
}
const glyphInfo_t * pGlyph = & mGlyphs [ uiLetter & 0xff ] ;
//
// SBCS language substitution?...
//
if ( m_fAltSBCSFontScaleFactor ! = - 1 )
{
// sod it, use the asian glyph, that's fine...
//
memcpy ( & m_AsianGlyph , pGlyph , sizeof ( m_AsianGlyph ) ) ; // *before* changin pGlyph!
// CFontInfo *pOriginalFont = GetFont_Actual( this->m_iOriginalFontWhenSBCSOverriden );
// pGlyph = &pOriginalFont->mGlyphs[ uiLetter & 0xff ];
# define ASSIGN_WITH_ROUNDING(_dst,_src) _dst = mbRoundCalcs ? Round( m_fAltSBCSFontScaleFactor * _src ) : m_fAltSBCSFontScaleFactor * (float)_src;
ASSIGN_WITH_ROUNDING ( m_AsianGlyph . baseline , pGlyph - > baseline ) ;
ASSIGN_WITH_ROUNDING ( m_AsianGlyph . height , pGlyph - > height ) ;
ASSIGN_WITH_ROUNDING ( m_AsianGlyph . horizAdvance , pGlyph - > horizAdvance ) ;
// m_AsianGlyph.horizOffset = /*Round*/( m_fAltSBCSFontScaleFactor * pGlyph->horizOffset );
ASSIGN_WITH_ROUNDING ( m_AsianGlyph . width , pGlyph - > width ) ;
pGlyph = & m_AsianGlyph ;
}
return pGlyph ;
}
const int CFontInfo : : GetCollapsedAsianCode ( ulong uiLetter ) const
{
int iCollapsedAsianCode = 0 ;
if ( AsianGlyphsAvailable ( ) )
{
switch ( GetLanguageEnum ( ) )
{
case eKorean : iCollapsedAsianCode = Korean_CollapseKSC5601HangulCode ( uiLetter ) ; break ;
case eTaiwanese : iCollapsedAsianCode = Taiwanese_CollapseBig5Code ( uiLetter ) ; break ;
case eJapanese : iCollapsedAsianCode = Japanese_CollapseShiftJISCode ( uiLetter ) ; break ;
case eChinese : iCollapsedAsianCode = Chinese_CollapseGBCode ( uiLetter ) ; break ;
case eThai : iCollapsedAsianCode = Thai_CollapseTISCode ( uiLetter ) ; break ;
default : assert ( 0 ) ; /* unhandled asian language */ break ;
}
}
return iCollapsedAsianCode ;
}
const int CFontInfo : : GetLetterWidth ( unsigned int uiLetter )
{
const glyphInfo_t * pGlyph = GetLetter ( uiLetter ) ;
return pGlyph - > width ? pGlyph - > width : mGlyphs [ ' . ' ] . width ;
}
const int CFontInfo : : GetLetterHorizAdvance ( unsigned int uiLetter )
{
const glyphInfo_t * pGlyph = GetLetter ( uiLetter ) ;
return pGlyph - > horizAdvance ? pGlyph - > horizAdvance : mGlyphs [ ' . ' ] . horizAdvance ;
}
// ensure any GetFont calls that need SBCS overriding (such as when playing in Russian) have the appropriate stuff done...
//
static CFontInfo * GetFont_SBCSOverride ( CFontInfo * pFont , Language_e eLanguageSBCS , LPCSTR psLanguageNameSBCS )
{
if ( ! pFont - > m_bIsFakeAlienLanguage )
{
if ( GetLanguageEnum ( ) = = eLanguageSBCS )
{
if ( pFont - > m_iAltSBCSFont = = - 1 ) // no reg attempted yet?
{
// need to register this alternative SBCS font...
//
int iAltFontIndex = RE_RegisterFont ( va ( " %s/%s " , COM_SkipPath ( pFont - > m_sFontName ) , psLanguageNameSBCS ) ) ; // ensure unique name (eg: "lcd/russian")
CFontInfo * pAltFont = GetFont_Actual ( iAltFontIndex ) ;
if ( pAltFont )
{
// work out the scaling factor for this font's glyphs...( round it to 1 decimal place to cut down on silly scale factors like 0.53125 )
//
pAltFont - > m_fAltSBCSFontScaleFactor = RoundTenth ( ( float ) pFont - > GetPointSize ( ) / ( float ) pAltFont - > GetPointSize ( ) ) ;
//
// then override with the main properties of the original font...
//
pAltFont - > mPointSize = pFont - > GetPointSize ( ) ; //(float) pAltFont->GetPointSize() * pAltFont->m_fAltSBCSFontScaleFactor;
pAltFont - > mHeight = pFont - > GetHeight ( ) ; //(float) pAltFont->GetHeight() * pAltFont->m_fAltSBCSFontScaleFactor;
pAltFont - > mAscender = pFont - > GetAscender ( ) ; //(float) pAltFont->GetAscender() * pAltFont->m_fAltSBCSFontScaleFactor;
pAltFont - > mDescender = pFont - > GetDescender ( ) ; //(float) pAltFont->GetDescender() * pAltFont->m_fAltSBCSFontScaleFactor;
// pAltFont->mPointSize = (float) pAltFont->GetPointSize() * pAltFont->m_fAltSBCSFontScaleFactor;
// pAltFont->mHeight = (float) pAltFont->GetHeight() * pAltFont->m_fAltSBCSFontScaleFactor;
// pAltFont->mAscender = (float) pAltFont->GetAscender() * pAltFont->m_fAltSBCSFontScaleFactor;
// pAltFont->mDescender = (float) pAltFont->GetDescender() * pAltFont->m_fAltSBCSFontScaleFactor;
pAltFont - > mbRoundCalcs = true ;
pAltFont - > m_iOriginalFontWhenSBCSOverriden = pFont - > m_iThisFont ;
}
pFont - > m_iAltSBCSFont = iAltFontIndex ;
}
if ( pFont - > m_iAltSBCSFont > 0 )
{
return GetFont_Actual ( pFont - > m_iAltSBCSFont ) ;
}
}
}
return NULL ;
}
CFontInfo * GetFont ( int index )
{
CFontInfo * pFont = GetFont_Actual ( index ) ;
if ( pFont )
{
// any SBCS overrides? (this has to be pretty quick, and is (sort of))...
//
for ( int i = 0 ; g_SBCSOverrideLanguages [ i ] . m_psName ; i + + )
{
CFontInfo * pAltFont = GetFont_SBCSOverride ( pFont , g_SBCSOverrideLanguages [ i ] . m_eLanguage , g_SBCSOverrideLanguages [ i ] . m_psName ) ;
if ( pAltFont )
{
return pAltFont ;
}
}
}
return pFont ;
}
int RE_Font_StrLenPixels ( const char * psText , const int iFontHandle , const float fScale )
{
int iMaxWidth = 0 ;
int iThisWidth = 0 ;
CFontInfo * curfont ;
curfont = GetFont ( iFontHandle ) ;
if ( ! curfont )
{
return ( 0 ) ;
}
float fScaleA = fScale ;
if ( Language_IsAsian ( ) & & fScale > 0.7f )
{
fScaleA = fScale * 0.75f ;
}
while ( * psText )
{
int iAdvanceCount ;
unsigned int uiLetter = AnyLanguage_ReadCharFromString ( psText , & iAdvanceCount , NULL ) ;
psText + = iAdvanceCount ;
if ( uiLetter = = ' ^ ' )
{
if ( * psText > = ' 0 ' & &
* psText < = ' 9 ' )
{
uiLetter = AnyLanguage_ReadCharFromString ( psText , & iAdvanceCount , NULL ) ;
psText + = iAdvanceCount ;
continue ;
}
}
if ( uiLetter = = 0x0A )
{
iThisWidth = 0 ;
}
else
{
int iPixelAdvance = curfont - > GetLetterHorizAdvance ( uiLetter ) ;
float fValue = iPixelAdvance * ( ( uiLetter > g_iNonScaledCharRange ) ? fScaleA : fScale ) ;
iThisWidth + = curfont - > mbRoundCalcs ? Round ( fValue ) : fValue ;
if ( iThisWidth > iMaxWidth )
{
iMaxWidth = iThisWidth ;
}
}
}
return iMaxWidth ;
}
// not really a font function, but keeps naming consistant...
//
int RE_Font_StrLenChars ( const char * psText )
{
// logic for this function's letter counting must be kept same in this function and RE_Font_DrawString()
//
int iCharCount = 0 ;
while ( * psText )
{
// in other words, colour codes and CR/LF don't count as chars, all else does...
//
int iAdvanceCount ;
unsigned int uiLetter = AnyLanguage_ReadCharFromString ( psText , & iAdvanceCount , NULL ) ;
psText + = iAdvanceCount ;
switch ( uiLetter )
{
case ' ^ ' :
if ( * psText > = ' 0 ' & &
* psText < = ' 9 ' )
{
psText + + ;
}
else
{
iCharCount + + ;
}
break ; // colour code (note next-char skip)
case 10 : break ; // linefeed
case 13 : break ; // return
case ' _ ' : iCharCount + = ( GetLanguageEnum ( ) = = eThai & & ( ( ( unsigned char * ) psText ) [ 0 ] > = TIS_GLYPHS_START ) ) ? 0 : 1 ; break ; // special word-break hack
default : iCharCount + + ; break ;
}
}
return iCharCount ;
}
int RE_Font_HeightPixels ( const int iFontHandle , const float fScale )
{
CFontInfo * curfont ;
curfont = GetFont ( iFontHandle ) ;
if ( curfont )
{
float fValue = curfont - > GetPointSize ( ) * fScale ;
return curfont - > mbRoundCalcs ? Round ( fValue ) : fValue ;
}
return ( 0 ) ;
}
// iMaxPixelWidth is -1 for "all of string", else pixel display count...
//
void RE_Font_DrawString ( int ox , int oy , const char * psText , const float * rgba , const int iFontHandle , int iMaxPixelWidth , const float fScale )
{
static qboolean gbInShadow = qfalse ; // MUST default to this
int x , y , colour , offset ;
const glyphInfo_t * pLetter ;
qhandle_t hShader ;
assert ( psText ) ;
if ( iFontHandle & STYLE_BLINK )
{
if ( ( Sys_Milliseconds ( ) > > 7 ) & 1 )
{
return ;
}
}
// // test code only
// if (GetLanguageEnum() == eTaiwanese)
// {
// psText = "Wp:<3A> }<7D> F<EFBFBD> a <20> p<EFBFBD> G<EFBFBD> <47> <EFBFBD> A<EFBFBD> Ʊ<EFBFBD> <C6B1> A<EFBFBD> <41> <EFBFBD> L<EFBFBD> ̻<EFBFBD> <CCBB> <EFBFBD> <EFBFBD> @<40> ˦<EFBFBD> <CBA6> C";
// }
// else
// if (GetLanguageEnum() == eChinese)
// {
// //psText = "Ӷ<> <D3B6> ս <EFBFBD> <D5BD> II Լ<> <D4BC> ?Ī<> <C4AA> ˹ <20> <> <EFBFBD> <EFBFBD> ʧ<EFBFBD> <CAA7> <20> <> Ҫ<EFBFBD> <D2AA> <EFBFBD> û<EFBFBD> <C3BB> <EFBFBD> <EFBFBD> 趨<EFBFBD> ı <EFBFBD> <C4B1> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ԥ<> <D4A4> ,S3 ѹ<> <D1B9> ,DXT1 ѹ<> <D1B9> ,DXT5 ѹ<> <D1B9> ,16 Bit,32 Bit";
// psText = "Ó¶<> <D3B6> Õ½ <EFBFBD> <D5BD> II";
// }
// else
// if (GetLanguageEnum() == eThai)
// {
// //psText = "<22> ҵðҹ<C3B0> <D2B9> Ե<EFBFBD> ѳ<EFBFBD> <D1B3> <EFBFBD> ص<EFBFBD> <D8B5> ˡ<EFBFBD> <CBA1> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ѻ<EFBFBD> ѡ <EFBFBD> <D1A1> <EFBFBD> <EFBFBD> <EFBFBD> ·<EFBFBD> <C2B7> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ѻ<EFBFBD> <D1BA> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ";
// psText = "<22> ҵðҹ<C3B0> <D2B9> Ե";
// psText = "<22> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ѻ";
// psText = "<22> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ѻ <20> <> _<EFBFBD> Թ_<D4B9> <5F> <EFBFBD> <EFBFBD> <EFBFBD> _1415";
// }
// else
// if (GetLanguageEnum() == eKorean)
// {
// psText = "Wp:<3A> <> Ÿ<EFBFBD> <C5B8> <EFBFBD> ̴<EFBFBD> <20> ָ<EFBFBD> . <20> ׵<EFBFBD> <D7B5> <EFBFBD> <20> <> <EFBFBD> Ѵ <EFBFBD> <D1B4> <EFBFBD> <20> װ<EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> ϰڴ<CFB0> .";
// }
// else
// if (GetLanguageEnum() == eJapanese)
// {
// static char sBlah[200];
// sprintf(sBlah,va("%c%c%c%c%c%c%c%c",0x82,0xA9,0x82,0xC8,0x8A,0xBF,0x8E,0x9A));
// psText = &sBlah[0];
// }
// else
// if (GetLanguageEnum() == eRussian)
// {
//// //psText = "<22> <> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <20> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <20> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ."
// psText = "<22> <> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> ";
// }
// else
// if (GetLanguageEnum() == ePolish)
// {
// psText = "za<7A> o<EFBFBD> ony w 1364 roku, jest najstarsz<73> polsk<73> uczelni<6E> i nale<6C> y...";
// psText = "za<7A> o<EFBFBD> ony nale<6C> y";
// }
CFontInfo * curfont = GetFont ( iFontHandle ) ;
if ( ! curfont | | ! psText )
{
return ;
}
float fScaleA = fScale ;
int iAsianYAdjust = 0 ;
if ( Language_IsAsian ( ) & & fScale > 0.7f )
{
fScaleA = fScale * 0.75f ;
iAsianYAdjust = /*Round*/ ( ( ( ( float ) curfont - > GetPointSize ( ) * fScale ) - ( ( float ) curfont - > GetPointSize ( ) * fScaleA ) ) / 2 ) ;
}
// Draw a dropshadow if required
if ( iFontHandle & STYLE_DROPSHADOW )
{
offset = Round ( curfont - > GetPointSize ( ) * fScale * 0.075f ) ;
static const vec4_t v4DKGREY2 = { 0.15f , 0.15f , 0.15f , 1 } ;
gbInShadow = qtrue ;
RE_Font_DrawString ( ox + offset , oy + offset , psText , v4DKGREY2 , iFontHandle & SET_MASK , iMaxPixelWidth , fScale ) ;
gbInShadow = qfalse ;
}
RE_SetColor ( rgba ) ;
x = ox ;
oy + = Round ( ( curfont - > GetHeight ( ) - ( curfont - > GetDescender ( ) > > 1 ) ) * fScale ) ;
qboolean bNextTextWouldOverflow = qfalse ;
while ( * psText & & ! bNextTextWouldOverflow )
{
int iAdvanceCount ;
unsigned int uiLetter = AnyLanguage_ReadCharFromString ( psText , & iAdvanceCount , NULL ) ;
psText + = iAdvanceCount ;
switch ( uiLetter )
{
case 10 : //linefeed
x = ox ;
oy + = Round ( curfont - > GetPointSize ( ) * fScale ) ;
if ( Language_IsAsian ( ) )
{
oy + = 4 ; // this only comes into effect when playing in asian for "A long time ago in a galaxy" etc, all other text is line-broken in feeder functions
}
break ;
case 13 : // Return
break ;
case 32 : // Space
pLetter = curfont - > GetLetter ( ' ' ) ;
x + = Round ( pLetter - > horizAdvance * fScale ) ;
bNextTextWouldOverflow = ( iMaxPixelWidth ! = - 1 & & ( ( x - ox ) > iMaxPixelWidth ) ) ? qtrue : qfalse ; // yeuch
break ;
case ' _ ' : // has a special word-break usage if in Thai (and followed by a thai char), and should not be displayed, else treat as normal
if ( GetLanguageEnum ( ) = = eThai & & ( ( unsigned char * ) psText ) [ 0 ] > = TIS_GLYPHS_START )
{
break ;
}
// else drop through and display as normal...
case ' ^ ' :
if ( uiLetter ! = ' _ ' ) // necessary because of fallthrough above
{
if ( * psText > = ' 0 ' & &
* psText < = ' 9 ' )
{
colour = ColorIndex ( * psText + + ) ;
if ( ! gbInShadow )
{
RE_SetColor ( g_color_table [ colour ] ) ;
}
break ;
}
}
//purposely falls thrugh
default :
pLetter = curfont - > GetLetter ( uiLetter , & hShader ) ; // Description of pLetter
if ( ! pLetter - > width )
{
pLetter = curfont - > GetLetter ( ' . ' ) ;
}
float fThisScale = uiLetter > g_iNonScaledCharRange ? fScaleA : fScale ;
// sigh, super-language-specific hack...
//
if ( uiLetter = = TIS_SARA_AM & & GetLanguageEnum ( ) = = eThai )
{
x - = Round ( 7 * fThisScale ) ;
}
int iAdvancePixels = Round ( pLetter - > horizAdvance * fThisScale ) ;
bNextTextWouldOverflow = ( iMaxPixelWidth ! = - 1 & & ( ( ( x + iAdvancePixels ) - ox ) > iMaxPixelWidth ) ) ? qtrue : qfalse ; // yeuch
if ( ! bNextTextWouldOverflow )
{
// this 'mbRoundCalcs' stuff is crap, but the only way to make the font code work. Sigh...
//
y = oy - ( curfont - > mbRoundCalcs ? Round ( pLetter - > baseline * fThisScale ) : pLetter - > baseline * fThisScale ) ;
if ( curfont - > m_fAltSBCSFontScaleFactor ! = - 1 )
{
y + = 3 ; // I'm sick and tired of going round in circles trying to do this legally, so bollocks to it
}
RE_StretchPic ( x + Round ( pLetter - > horizOffset * fScale ) , // float x
( uiLetter > g_iNonScaledCharRange ) ? y - iAsianYAdjust : y , // float y
curfont - > mbRoundCalcs ? Round ( pLetter - > width * fThisScale ) : pLetter - > width * fThisScale , // float w
curfont - > mbRoundCalcs ? Round ( pLetter - > height * fThisScale ) : pLetter - > height * fThisScale , // float h
pLetter - > s , // float s1
pLetter - > t , // float t1
pLetter - > s2 , // float s2
pLetter - > t2 , // float t2
//lastcolour.c,
hShader // qhandle_t hShader
) ;
x + = iAdvancePixels ;
}
break ;
}
}
//let it remember the old color //RE_SetColor(NULL);;
}
int RE_RegisterFont ( const char * psName )
{
FontIndexMap_t : : iterator it = g_mapFontIndexes . find ( psName ) ;
if ( it ! = g_mapFontIndexes . end ( ) )
{
int iFontIndex = ( * it ) . second ;
return iFontIndex ;
}
// not registered, so...
//
{
CFontInfo * pFont = new CFontInfo ( psName ) ;
if ( pFont - > GetPointSize ( ) > 0 )
{
int iFontIndex = g_iCurrentFontIndex - 1 ;
g_mapFontIndexes [ psName ] = iFontIndex ;
pFont - > m_iThisFont = iFontIndex ;
return iFontIndex ;
}
else
{
g_mapFontIndexes [ psName ] = 0 ; // missing/invalid
}
}
return 0 ;
}
void R_InitFonts ( void )
{
g_iCurrentFontIndex = 1 ; // entry 0 is reserved for "missing/invalid"
g_iNonScaledCharRange = INT_MAX ; // default all chars to have no special scaling (other than user supplied)
}
void R_ShutdownFonts ( void )
{
for ( int i = 1 ; i < g_iCurrentFontIndex ; i + + ) // entry 0 is reserved for "missing/invalid"
{
delete g_vFontArray [ i ] ;
}
g_mapFontIndexes . clear ( ) ;
g_vFontArray . clear ( ) ;
g_iCurrentFontIndex = 1 ; // entry 0 is reserved for "missing/invalid"
g_ThaiCodes . Clear ( ) ;
}
// this is only really for debugging while tinkering with fonts, but harmless to leave in...
//
void R_ReloadFonts_f ( void )
{
// first, grab all the currently-registered fonts IN THE ORDER THEY WERE REGISTERED...
//
vector < sstring_t > vstrFonts ;
for ( int iFontToFind = 1 ; iFontToFind < g_iCurrentFontIndex ; iFontToFind + + )
{
for ( FontIndexMap_t : : iterator it = g_mapFontIndexes . begin ( ) ; it ! = g_mapFontIndexes . end ( ) ; + + it )
{
if ( iFontToFind = = ( * it ) . second )
{
vstrFonts . push_back ( ( * it ) . first ) ;
break ;
}
}
if ( it = = g_mapFontIndexes . end ( ) )
{
break ; // couldn't find this font
}
}
if ( iFontToFind = = g_iCurrentFontIndex ) // found all of them?
{
// now restart the font system...
//
R_ShutdownFonts ( ) ;
R_InitFonts ( ) ;
//
// and re-register our fonts in the same order as before (note that some menu items etc cache the string lengths so really a vid_restart is better, but this is just for my testing)
//
for ( int iFont = 0 ; iFont < vstrFonts . size ( ) ; iFont + + )
{
# ifdef _DEBUG
int iNewFontHandle = RE_RegisterFont ( vstrFonts [ iFont ] . c_str ( ) ) ;
assert ( iNewFontHandle = = iFont + 1 ) ;
# else
RE_RegisterFont ( vstrFonts [ iFont ] . c_str ( ) ) ;
# endif
}
Com_Printf ( " Done. \n " ) ;
}
else
{
Com_Printf ( " Problem encountered finding current fonts, ignoring. \n " ) ; // poo. Oh well, forget it.
}
}
// end