thug/Code/Gfx/NGC/NX/chars.cpp

690 lines
18 KiB
C++
Raw Permalink Normal View History

2016-02-13 21:39:12 +00:00
#include <stdlib.h>
#include <string.h>
#include <core/defines.h>
#include <core/debug.h>
#include <core/string/stringutils.h>
#include <core/macros.h>
#include <sys/file/filesys.h>
#include "nx_init.h"
#include "chars.h"
#include <sys/ngc/p_prim.h>
#include <sys/ngc/p_camera.h>
#include <sys/ngc/p_render.h>
#include <sys/ngc/p_display.h>
#include <sys/ngc/p_gx.h>
#include <sys/config/config.h>
#include <gfx/nxfontman.h>
/*
.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