#include #include #include #include #include #include #include #include "nx_init.h" #include "chars.h" #include #include #include #include #include #include #include /* .fnt file format (by Ryan) -------------------------- 4 File size in bytes 4 Number of characters 4 Default height 4 Default base ? Character table (see below) ? Texture (see below) Character 2 Baseline (how many pixels down relative to top of image) 2 Ascii value Texture 4 Size of texture 2 Width 2 Height 2 Bit depth 6 Padding W*H Raw data 0-3 Padding for uint32 alignment 1K Palette data 4 Number of subtextures ? Subtexture table (see below) Subtexture 2 X 2 Y 2 W 2 H */ // This is the function generated by the makefile when building permanent font files. extern void * get_font_file_address( const char * p_filename ); namespace NxNgc { float CurrentScale; uint32 FontVramBase; SFont *pFontList; SFont *pButtonsFont = NULL; float wx0 = 0.0f; float wy0 = 0.0f; float wx1 = 640.0f; float wy1 = 448.0f; SFont *SText::spOverrideFont = NULL; SFont *LoadFont( const char *Filename ) { SFont *pFont; SChar *pChar; uint8 *pData; // int i,Len,NumChars,Width,Height,Depth,NumBytes; int i,NumChars,Width,Height,Depth,NumBytes; char * p8 = (char *)Filename; while ( *p8 != '\0' ) { if ( ( *p8 >= 'A' ) && ( *p8 <= 'Z' ) ) { *p8 = *p8 - ( 'A' - 'a' ); } p8++; } unsigned char * p_fnt = (unsigned char *)get_font_file_address( Filename ); Dbg_MsgAssert( p_fnt, ( "Font file '%s' not found in permanent data.", Filename )); #define get_bytes(p,n) { memcpy( p, p_fnt, n ); p_fnt += n; } // Allocate memory for the font structure. pFont = new SFont(); // Allocate a temporary buffer. uint8 FontBuf[2048]; // Load file header. get_bytes( FontBuf, 16 ); // Len = File::Read( FontBuf, 16, 1, p_FH ); // Dbg_MsgAssert( Len==16, ( "couldn't read file header from font file %s", Filename )); NumChars = ((uint32 *)FontBuf)[1]; pFont->DefaultHeight = ((uint32 *)FontBuf)[2]; pFont->DefaultBase = ((uint32 *)FontBuf)[3]; #ifdef __PLAT_NGC__ // PJR - This needs to disappear once we start converting .ska to .ska.ngc etc. NumChars = nReverse32( NumChars ); pFont->DefaultHeight = nReverse32( pFont->DefaultHeight ); pFont->DefaultBase = nReverse32( pFont->DefaultBase ); #endif // __PLAT_NGC__ // Clear character map to zero. memset( pFont->Map, 0, 256 ); memset(pFont->SpecialMap, 0, 32); // Allocate memory for character table. pFont->pChars = new SChar[NumChars]; // Load character map and character table. get_bytes( FontBuf, NumChars << 2 ); // Len = File::Read( FontBuf, NumChars << 2, 1, p_FH ); // Dbg_MsgAssert( Len == ( NumChars << 2 ), ( "couldn't read character table in font file %s", Filename )); for( i = 0, pChar = pFont->pChars, pData = FontBuf; i < NumChars; i++,pChar++,pData += 4 ) { #ifdef __PLAT_NGC__ // PJR - This needs to disappear once we start converting .ska to .ska.ngc etc. pChar->Baseline = nReverse32( pChar->Baseline ); pChar->Baseline = nReverse16( ((uint16 *)pData)[0] ); sint16 ascii_val = nReverse16(((sint16*) pData)[1]); if (ascii_val >= 0) pFont->Map[(uint8) ascii_val] = i; else { Dbg_Assert(ascii_val >= -32) pFont->SpecialMap[(uint8) (-ascii_val - 1)] = i; } #else pChar->Baseline = ((uint16 *)pData)[0]; pFont->Map[(uint8)((uint16*)pData)[1]] = i; #endif // __PLAT_NGC__ } // now, if there is a null character in the font, make characters that could not be found // in the font display that instead of 'A' if (pFont->SpecialMap[31] != 0) { for (i = 0; i < 256; i++) { if (pFont->Map[i] == 0 && i != 'A' && i != 'a') pFont->Map[i] = pFont->SpecialMap[31]; if (i < 31 && pFont->SpecialMap[i] == 0) pFont->SpecialMap[i] = pFont->SpecialMap[31]; } } // Load texture header. get_bytes( FontBuf, 16 ); // Len = File::Read( FontBuf, 16, 1, p_FH ); // Dbg_MsgAssert( Len == 16, ( "couldn't read texture header from font file %s", Filename )); Width = ((uint16 *)FontBuf)[2]; Height = ((uint16 *)FontBuf)[3]; Depth = ((uint16 *)FontBuf)[4]; #ifdef __PLAT_NGC__ // PJR - This needs to disappear once we start converting .ska to .ska.ngc etc. Width = nReverse16( Width ); Height = nReverse16( Height ); Depth = nReverse16( Depth ); #endif // __PLAT_NGC__ // Create texture. Dbg_Assert( Depth == 8 ); NumBytes = ( Width * Height + 3 ) & 0xFFFFFFFC; // Align texture to 32 bytes & point up. int size = ( Width * Height ) + ( 256 * 2 ); unsigned char * p_align = (unsigned char*)OSRoundUp32B( p_fnt ); for ( int lp = ( size - 1 ); lp >= 0; lp-- ) p_align[lp] = p_fnt[lp]; DCFlushRange( p_align, size ); pFont->mp_texture = p_align; pFont->mp_palette = &p_align[( Width * Height )]; p_fnt += ( Width * Height ); // Texture p_fnt += ( 256 * 2 ); // Palette p_fnt += 32; // Padding // pFont->mp_texture = new char[ ( Width * Height ) ]; // Len = File::Read( pFont->mp_texture, ( Width * Height ), 1, p_FH ); // Dbg_MsgAssert( Len == ( Width * Height ), ( "couldn't read texture data from font file %s", Filename )); // // pFont->mp_palette = new char[ ( 256 * 4 ) ]; // Len = File::Read( pFont->mp_palette, ( 256 * 4 ), 1, p_FH ); // Dbg_MsgAssert( Len == ( 256 * 4 ), ( "couldn't read palette data from font file %s", Filename )); pFont->m_width = Width; pFont->m_height = Height; // Skip numsubtextures, and load subtextures. get_bytes( FontBuf, ( NumChars << 3 ) + 4 ); // Len = File::Read( FontBuf, ( NumChars << 3 ) + 4, 1, p_FH ); // Dbg_MsgAssert( Len == ( NumChars << 3 ) + 4, ( "couldn't read subtexture table from font file %s", Filename )); for( i = 0, pChar = pFont->pChars, pData = FontBuf + 4; i < NumChars; i++, pChar++, pData += 8 ) { uint16 x = ((uint16 *)pData )[0]; uint16 y = ((uint16 *)pData )[1]; uint16 w = ((uint16 *)pData )[2]; uint16 h = ((uint16 *)pData )[3]; #ifdef __PLAT_NGC__ x = nReverse16( x ); y = nReverse16( y ); w = nReverse16( w ); h = nReverse16( h ); #endif // __PLAT_NGC__ pChar->w = w; pChar->h = h; pChar->u0 = ( ((float)x + 0.5f) / (float)Width ); pChar->v0 = ( ((float)y + 0.5f) / (float)Height ); pChar->u1 = pChar->u0 + ((float)w / (float)Width ); pChar->v1 = pChar->v0 + ((float)h / (float)Height ); } // Add font to font list. pFont->pNext = pFontList; pFontList = pFont; // We're done with the font file now. // File::Close( p_FH ); // this will serve as the default spacing pFont->mSpaceSpacing = pFont->pChars[pFont->Map['I']].w; return pFont; } void UnloadFont(SFont *pFont) { SFont *pPrevFont; int found=0; // find font and unchain from list if (pFontList == pFont) { found=1; pFontList = pFontList->pNext; } else { for (pPrevFont=pFontList; pPrevFont->pNext; pPrevFont=pPrevFont->pNext) { if (pPrevFont->pNext == pFont) { found=1; pPrevFont->pNext = pFont->pNext; break; } } } Dbg_MsgAssert(found, ("attempt to unload font which has not been loaded")); // free memory free( pFont->pChars ); free( pFont ); } uint32 SFont::GetDefaultHeight() const { return DefaultHeight << 4; } uint32 SFont::GetDefaultBase() const { return DefaultBase << 4; } void SFont::QueryString(char *String, float &width, float &height) { SChar *pChar; char *pLetter; float u0,v0,x0,u1,v1,x1; x0 = 0; //sint16 char_spacing = 16 + (mCharSpacing<<4); float char_spacing = mCharSpacing; float space_spacing = mSpaceSpacing; for (pLetter=String;; pLetter++) { pChar = NULL; // may be overridden by the '\b' tag SFont *p_font = this; // acount for tags (might be multiple ones in a row) bool got_char_tag = false; // tag resulting in output of character while (*pLetter == '\\' && !got_char_tag) { pLetter++; if (*pLetter == '\\') break; switch(*pLetter) { case '\\': got_char_tag = true; break; case 'c': case 'C': pLetter += 2; // skip over "c#" break; case 's': case 'S': { pLetter++; // skip "s" uint digit = Str::DehexifyDigit(pLetter); pChar = pChars + SpecialMap[digit]; got_char_tag = true; break; } case 'b': case 'B': { pLetter++; // skip "b" uint digit = Str::DehexifyDigit(pLetter); // switch over to buttons font, the regular font will be used again on the next character p_font = pButtonsFont; Dbg_Assert(p_font); pChar = p_font->pChars + p_font->SpecialMap[digit]; got_char_tag = true; break; } case 'm': case 'M': { pLetter++; // skip "m" char button_char = Nx::CFontManager::sMapMetaCharacterToButton(pLetter); uint digit = Str::DehexifyDigit(&button_char); p_font = pButtonsFont; Dbg_Assert(p_font); pChar = p_font->pChars + p_font->SpecialMap[digit]; got_char_tag = true; break; } default: Dbg_MsgAssert(0, ("unknown tag %c", *pLetter)); break; } } // end while if (*pLetter == '\0') break; if (*pLetter != ' ' || pChar) { if (!pChar) pChar = p_font->pChars + p_font->Map[(uint8)*pLetter]; u0 = pChar->u0; v0 = pChar->v0; u1 = pChar->u1; v1 = pChar->v1; x1 = x0+((u1-u0)*m_width); } else { x1 = x0 + space_spacing; } x0 = x1+char_spacing; } width = (float)x0; height = (float)DefaultHeight; } ///////////////////////////////////////////////////////////////////////////// // SText::SText( float pri ) : SDraw2D( pri, true ) { } SText::~SText( void ) { } void SText::BeginDraw( void ) { } ///////////////////////////////////////////////////////////////////////////// // void SText::Draw( void ) { bool uploaded = false; bool new_color = true; SChar *pChar = NULL; char *pLetter; float u0,v0,x0,y0,u1,v1,x1,y1,yt; x0 = m_xpos + wx0; y0 = m_ypos + wy0; //sint16 char_spacing = 16 + (mp_font->mCharSpacing<<4); float char_spacing = (float) mp_font->mCharSpacing * m_xscale; float space_spacing = (float) mp_font->mSpaceSpacing * m_xscale; GXColor current_color; current_color.a = (m_rgba&0xff); current_color.b = ((m_rgba&0xff00)>>8); current_color.g = ((m_rgba&0xff0000)>>16); current_color.r = ((m_rgba&0xff000000)>>24); for (pLetter=mp_string;; pLetter++) { pChar = NULL; SFont *p_font = (spOverrideFont) ? spOverrideFont : mp_font; // acount for tags (might be multiple ones in a row) bool got_char_tag = false; // tag resulting in output of character while (*pLetter == '\\' && !got_char_tag) { pLetter++; switch(*pLetter) { case '\\': got_char_tag = true; break; case 'c': case 'C': { pLetter++; // skip "c" uint digit = Str::DehexifyDigit(pLetter); pLetter++; // skip "#" // set active color from font uint32 trgba; if (digit != 0 && !m_color_override) { trgba = mp_font->mRGBATab[digit-1]; current_color.r = (trgba&0xff); current_color.g = ((trgba&0xff00)>>8); current_color.b = ((trgba&0xff0000)>>16); current_color.a = ((trgba&0xff000000)>>24); } else { trgba = m_rgba; current_color.a = (trgba&0xff); current_color.b = ((trgba&0xff00)>>8); current_color.g = ((trgba&0xff0000)>>16); current_color.r = ((trgba&0xff000000)>>24); } //GX::SetChanAmbColor( GX_COLOR0A0, current_color ); new_color = true; break; } case 's': case 'S': { pLetter++; // skip "s" uint digit = Str::DehexifyDigit(pLetter); pChar = mp_font->pChars + mp_font->SpecialMap[digit]; got_char_tag = true; break; } case 'b': case 'B': { // 'B' stands for button, accesses the button font pLetter++; // skip "b" uint digit = Str::DehexifyDigit(pLetter); // switch to the buttons font! p_font = pButtonsFont; Dbg_Assert(p_font); pChar = p_font->pChars + p_font->SpecialMap[digit]; got_char_tag = true; spOverrideFont = p_font; //EndDraw(); //BeginDraw(); uploaded = false; // we can now return to using the regular font (no override) spOverrideFont = NULL; break; } default: Dbg_MsgAssert(0, ("unknown tag %c", *pLetter)); break; } } // end while if (*pLetter == '\0') break; if (*pLetter != ' ' || pChar) { if (!pChar) pChar = p_font->pChars + p_font->Map[(uint8) *pLetter]; yt = y0 + (float)(p_font->DefaultBase - pChar->Baseline) * m_yscale; u0 = pChar->u0; v0 = pChar->v0; u1 = pChar->u1; v1 = pChar->v1; x1 = x0+(((u1-u0)*p_font->m_width)*m_xscale); y1 = yt+(((v1-v0)*p_font->m_height)*m_yscale); } else { x0 += (space_spacing + char_spacing); continue; } if ( !uploaded && current_color.a ) { uploaded = true; // Upload the texture & alpha map. GX::UploadTexture( p_font->mp_texture, p_font->m_width, p_font->m_height, GX_TF_C8, GX_CLAMP, GX_CLAMP, GX_FALSE, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1, GX_TEXMAP0 ); GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, p_font->m_width, p_font->m_height ); GX::UploadPalette( p_font->mp_palette, GX_TL_RGB5A3, GX_TLUT_256, GX_TEXMAP0 ); // GXTexObj texObj; // GXInitTexObjCI( // &texObj, // p_font->mp_texture, // p_font->m_width, // p_font->m_height, // GX_TF_C8, // GX_CLAMP, // GX_CLAMP, // GX_FALSE, // GX_TLUT0 ); // GXInitTexObjLOD(&texObj, GX_LINEAR, GX_LINEAR, 0, 0, 0, 0, 0, GX_ANISO_1); // GXLoadTexObj( &texObj, GX_TEXMAP0 ); // // GXTlutObj palObj; // GXInitTlutObj( &palObj, p_font->mp_palette, GX_TL_RGB5A3, 16 ); // GXLoadTlut ( &palObj, GX_TLUT0 ); // GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP1 ); // Alpha map is in Green channel, so use this to swap it to the alpha channel. // GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0 ); // GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV ); // GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV ); // GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO ); // GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO ); GX::SetTexChanTevIndCull( 1, 1, 1, 0, GX_CULL_NONE ); GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 ); GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY ); GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY ); GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL); GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_RASA, GX_CA_TEXA, GX_CA_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV, GX_TEV_SWAP0, GX_TEV_SWAP0 ); GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_RASC, GX_CC_TEXC, GX_CC_ZERO, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV ); // GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE ); // Set current vertex descriptor to enable position and color0. // Both use 8b index to access their data arrays. GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT ); // Set material color. GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} ); GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); } if ( current_color.a ) { if ( new_color ) { GX::SetChanAmbColor( GX_COLOR0A0, current_color ); new_color = false; } // Send coordinates. GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); GX::Position3f32(x0, yt, -1.0f); GX::TexCoord2f32(u0, v0); GX::Position3f32(x1, yt, -1.0f); GX::TexCoord2f32(u1, v0); GX::Position3f32(x1, y1, -1.0f); GX::TexCoord2f32(u1, v1); GX::Position3f32(x0, y1, -1.0f); GX::TexCoord2f32(u0, v1); GX::End(); } x0 = x1 + char_spacing; if (p_font != mp_font) { // we just used the button font, so return to the regular one uploaded = false; //EndDraw(); //BeginDraw(); } } // end for } ///////////////////////////////////////////////////////////////////////////// // void SText::EndDraw( void ) { } void SetTextWindow( uint16 x0, uint16 x1, uint16 y0, uint16 y1 ) { wx0 = (float)x0; wy0 = (float)y0; wx1 = (float)x1; wy1 = (float)y1; // TextRegSCISSOR = (uint64)x0 | (uint64)x1<<16 | (uint64)y0<<32 | (uint64)y1<<48; } void SText::DrawSingle( void ) { BeginDraw(); Draw(); EndDraw(); } } // namespace Ngc