mirror of
https://github.com/Fledge68/WiiFlow_Lite.git
synced 2024-11-01 09:05:06 +01:00
706e18c6c0
progress, thanks alot to dimok and the whole usb loader gx team for help and the code for it! To zoom the banner to fullscreen click the back button, to zoom out the back button again, known bugs: -some games fuck up the textures of buttons (e.g. wii play motion) -if no wii game or channel was selected and you try to launch a game or exit wiiflow it will most likely codedump
388 lines
9.3 KiB
C++
388 lines
9.3 KiB
C++
/*
|
|
Copyright (c) 2012 - giantpune
|
|
Copyright (c) 2012 - Dimok
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
distribution.
|
|
*/
|
|
|
|
#include <malloc.h>
|
|
#include "WiiFont.h"
|
|
|
|
WiiFont::WiiFont()
|
|
: header(NULL)
|
|
, finf(NULL)
|
|
, tglp(NULL)
|
|
, cwdh(NULL)
|
|
, font_loaded(false)
|
|
{
|
|
}
|
|
|
|
WiiFont::~WiiFont()
|
|
{
|
|
std::map<u16, TextureCache>::iterator itr;
|
|
|
|
for (itr = textureMap.begin(); itr != textureMap.end(); itr++)
|
|
{
|
|
if(itr->second.allocated && itr->second.texture_data)
|
|
free(itr->second.texture_data);
|
|
}
|
|
}
|
|
|
|
bool WiiFont::Load(const u8 *file)
|
|
{
|
|
if(!file)
|
|
return false;
|
|
|
|
header = (WiiFont::Header *) file;
|
|
|
|
if((header->magic != MAGIC_FONT && header->magic != MAGIC_FONT_ARCHIVE)
|
|
|| header->version != MAGIC_VERSION)
|
|
{
|
|
header = NULL;
|
|
return false;
|
|
}
|
|
|
|
const u8 *position = ((const u8 *)header) + header->header_len;
|
|
|
|
for(u32 i = 0; i < header->section_count; ++i)
|
|
{
|
|
section_t *section = (section_t *) position;
|
|
position += section->size;
|
|
|
|
switch( section->magic )
|
|
{
|
|
case MAGIC_GLYPH_GROUP:
|
|
glgr = (GlgrHeader *) section;
|
|
break;
|
|
case MAGIC_FONT_INFORMATION:
|
|
finf = (FinfHeader *) section;
|
|
break;
|
|
case MAGIC_TEXTURE_GLYPH:
|
|
tglp = (TglpHeader *) section;
|
|
break;
|
|
case MAGIC_CHARACTER_WIDTH:
|
|
cwdh = (CwdhHeader *) section;
|
|
break;
|
|
case MAGIC_CHARACTER_CODE_MAP:
|
|
ParseCmap((CmapEntry *) (section + 1));
|
|
break;
|
|
default:
|
|
// ignore
|
|
gprintf("Uknown section %.4s\n", (char *) §ion->magic);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Some sanity checks
|
|
if(!finf || !tglp || !cwdh)
|
|
return false;
|
|
|
|
if(finf->tglpOffset > header->filesize || finf->cwdhOffset > header->filesize
|
|
|| finf->cmapOffset > header->filesize)
|
|
return false;
|
|
|
|
font_loaded = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
inline bool WiiFont::CheckCmap(u16 charCode, u16 mapValue)
|
|
{
|
|
std::map<u16, u16>::iterator it = cmap.find(charCode);
|
|
if(it != cmap.end())
|
|
{
|
|
if((*it).second != mapValue)
|
|
{
|
|
gprintf("Duplicate characters\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool WiiFont::ParseCmap(CmapEntry *cmapEntry)
|
|
{
|
|
if(!cmapEntry)
|
|
return false;
|
|
|
|
while(true)
|
|
{
|
|
switch(cmapEntry->type)
|
|
{
|
|
case 0:
|
|
{
|
|
for(u16 i = cmapEntry->charCode, j = cmapEntry->start; j < cmapEntry->end; j++, i++)
|
|
{
|
|
if(!CheckCmap(j, i))
|
|
return false;
|
|
cmap[j] = i;
|
|
}
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
u16 idx = 0;
|
|
u16 *idxPointer = &cmapEntry->charCode;
|
|
for(u32 i = cmapEntry->start; i < cmapEntry->end; i++)
|
|
{
|
|
u16 m_idx = idxPointer[idx++];
|
|
if(m_idx == 0xffff)
|
|
continue;
|
|
|
|
if(!CheckCmap(i, m_idx))
|
|
return false;
|
|
|
|
cmap[i] = m_idx;
|
|
}
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
u16 ind, character;
|
|
u16 *charData = (u16 *) (cmapEntry + 1);
|
|
for(u32 i = 0; i < cmapEntry->charCode; i++)
|
|
{
|
|
character = charData[0];
|
|
ind = charData[1];
|
|
charData += 2;
|
|
|
|
if(!CheckCmap(character, ind))
|
|
return false;
|
|
|
|
cmap[character] = ind;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
gprintf( "unknown cmap type\n" );
|
|
return false;
|
|
}
|
|
|
|
if(cmapEntry->pos == 0)
|
|
return true;
|
|
|
|
cmapEntry = (CmapEntry *)(((u8 *)header) + cmapEntry->pos);
|
|
}
|
|
}
|
|
|
|
const WiiFont::CharInfo *WiiFont::GetCharInfo(u16 charCode)
|
|
{
|
|
if(!finf || !tglp || !cwdh)
|
|
return NULL;
|
|
|
|
// see if the character already exists in our cache
|
|
std::map<u16, CharInfo>::iterator itr = charInfoMap.find(charCode);
|
|
if(itr != charInfoMap.end())
|
|
return &itr->second;
|
|
|
|
u16 idx = CharToIdx(charCode);
|
|
if(idx > cwdh->endIdx)
|
|
{
|
|
gprintf( "idx > cwdh->endIdx" );
|
|
return NULL;
|
|
}
|
|
|
|
Cwdh *cwdh2 = (Cwdh*) (((u8 *)header) + finf->cwdhOffset + 8);
|
|
|
|
u32 chars_per_texture = (tglp->charColumns * tglp->charRows);
|
|
u32 tex_idx = idx / chars_per_texture;
|
|
u32 row = (idx - chars_per_texture * tex_idx) / tglp->charColumns;
|
|
u32 col = (idx - chars_per_texture * tex_idx) - ( tglp->charColumns * row );
|
|
|
|
f32 _s1 = ((tglp->cellWidth + 1) * col) / (f32)tglp->width;
|
|
f32 _s2 = _s1 + CharacterWidth() / (f32)tglp->width;
|
|
|
|
// this is good vertically but horizontal without it it looks clearer
|
|
f32 _t1 = ((tglp->cellHeight + 1) * row + 0.5f * (CharacterHeight() - (tglp->cellHeight + 1))) / (f32)tglp->height;
|
|
f32 _t2 = _t1 + CharacterHeight() / (f32)tglp->height;
|
|
|
|
CharInfo charInfo;
|
|
charInfo.sheetIdx = tex_idx;
|
|
charInfo.s1 = _s1;
|
|
charInfo.s2 = _s2;
|
|
charInfo.t1 = _t1;
|
|
charInfo.t2 = _t2;
|
|
charInfo.advanceGlyphX = cwdh2[idx].advanceGlyphX;
|
|
charInfo.unk = cwdh2[idx].unk;
|
|
charInfo.advanceKerning = cwdh2[idx].advanceKerning;
|
|
|
|
charInfoMap[charCode] = charInfo;
|
|
return &charInfoMap[charCode];
|
|
}
|
|
|
|
GXTexObj *WiiFont::LoadTextureObj(u16 tex_idx)
|
|
{
|
|
if(!font_loaded || tex_idx >= tglp->texCnt)
|
|
return NULL;
|
|
|
|
// init texture and add it to the list
|
|
TextureCache chacheStruct;
|
|
|
|
if(header->magic == MAGIC_FONT_ARCHIVE)
|
|
{
|
|
chacheStruct.texture_data = GetUnpackedTexture(tex_idx);
|
|
chacheStruct.allocated = true;
|
|
}
|
|
else
|
|
{
|
|
chacheStruct.texture_data = ((u8 *) header) + tglp->dataOffset + tex_idx * tglp->texSize;
|
|
chacheStruct.allocated = false;
|
|
}
|
|
|
|
if(!chacheStruct.texture_data)
|
|
return NULL;
|
|
|
|
GX_InitTexObj( &chacheStruct.texObj, chacheStruct.texture_data, tglp->width, tglp->height, 0, 0, 0, true );
|
|
GX_InitTexObjLOD( &chacheStruct.texObj, GX_LINEAR,GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_DISABLE, GX_FALSE, GX_ANISO_1 );
|
|
|
|
textureMap[tex_idx] = chacheStruct;
|
|
return &textureMap[tex_idx].texObj;
|
|
}
|
|
|
|
bool WiiFont::Apply(u16 tex_indx)
|
|
{
|
|
if(!font_loaded)
|
|
return false;
|
|
|
|
if(tex_indx >= tglp->texCnt)
|
|
return false;
|
|
|
|
GXTexObj *tex_obj;
|
|
|
|
// Load character texture from cache if available otherwise create cache
|
|
std::map<u16, TextureCache>::iterator itr = textureMap.find(tex_indx);
|
|
if(itr != textureMap.end())
|
|
tex_obj = &itr->second.texObj;
|
|
else
|
|
tex_obj = LoadTextureObj(tex_indx);
|
|
|
|
// no texture for this character found
|
|
if(!tex_obj)
|
|
return false;
|
|
|
|
GX_LoadTexObj(tex_obj, GX_TEXMAP0);
|
|
GX_InvalidateTexAll();
|
|
return true;
|
|
}
|
|
|
|
// giantpunes little magic function
|
|
bool WiiFont::Decompress_0x28( unsigned char *outBuf, u32 outLen, const unsigned char *inBuf, u32 inLen )
|
|
{
|
|
if( outLen & 3 )// this copies 32 bits at a time, so it probably needs to be aligned
|
|
{
|
|
gprintf( "length not aligned to 32 bits\n" );
|
|
return false;
|
|
}
|
|
const u32 root_offset = 5;
|
|
u32 symbol = 0;
|
|
u32 counter = 0;
|
|
u32 inIdx = 0;
|
|
u32 outIdx = 0;
|
|
u32 *in32 = (u32*)( ( inBuf + 6 ) + ( ( inBuf[ 4 ] << 1 ) ) );
|
|
u32 *out32 = (u32*)outBuf;
|
|
u8 *p = (u8*)inBuf + root_offset;
|
|
|
|
outLen >>= 2; //we are copying 4 bytes at a time
|
|
while( outLen )
|
|
{
|
|
for( u32 i = 0, leaf = p[ 0 ], bits = __builtin_bswap32( in32[ inIdx ] )
|
|
; i < 0x20 && outLen
|
|
; i++, leaf = p[ 0 ], bits <<= 1 )
|
|
{
|
|
u32 topBit = bits >> 31;
|
|
u8 d = 2 - ((u64)p & 1);
|
|
p += topBit + ( ( leaf << 1 ) & 0x7e ) + d;
|
|
|
|
if( !( p > (u8*)inBuf ) && ( p < (u8*)inBuf + inLen ) )
|
|
{
|
|
gprintf( "out of range 1\n" );
|
|
return false;
|
|
}
|
|
|
|
if( ( ( leaf << ( topBit ) ) & ( 1 << 7 ) ) ) // p->isLeaf
|
|
{
|
|
symbol = ( ( p[ 0 ] << 0x18 ) | ( symbol >> 8 ) );
|
|
p = (u8*)inBuf + root_offset; // p = root
|
|
if( counter++ > 2 )
|
|
{
|
|
out32[ outIdx++ ] = __builtin_bswap32( symbol );// buf[bufcur++] = p->symbol
|
|
counter = 0; // reset counter
|
|
outLen--; // decrease amount to copy
|
|
}
|
|
}
|
|
}
|
|
if( inIdx++ >= ( inLen >> 2 ) )
|
|
{
|
|
gprintf( "out of range 2\n" );
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
u8 *WiiFont::GetUnpackedTexture(u16 sheetNo)
|
|
{
|
|
u8 *data = (u8 *) header;
|
|
u32 compressedSize;
|
|
u32 off = 0;
|
|
|
|
// skip over all the sheets till we get the one we want
|
|
for( u32 i = 0; i < sheetNo; i++ )
|
|
{
|
|
compressedSize = *(u32*)( data + tglp->dataOffset + off );
|
|
off += compressedSize + 4;
|
|
}
|
|
|
|
compressedSize = *(u32*)( data + tglp->dataOffset + off );
|
|
|
|
u32 uncompressedSize = *(u32*)( data + tglp->dataOffset + off + 4 );
|
|
if( (uncompressedSize & 0xff000000) != 0x28000000 )// looks like all the sheets in wbf1 and wbf2.brfna are 0x28
|
|
{
|
|
gprintf( "Brfna::LoadSheets(): unknown data type\n" );
|
|
return NULL;
|
|
}
|
|
uncompressedSize = ( __builtin_bswap32( uncompressedSize ) >> 8 );
|
|
if( !uncompressedSize )// is this right? it looks like it is. but it SHOULD only happen for files over 0xffffff bytes
|
|
{
|
|
uncompressedSize = __builtin_bswap32(*(u32*)( data + tglp->dataOffset + off + 8 ));
|
|
}
|
|
if( uncompressedSize != glgr->sheet_size )
|
|
{
|
|
gprintf( "uncompressedSize != glgr->sheet_size %08x\n", uncompressedSize );
|
|
return NULL;
|
|
}
|
|
|
|
// decompress
|
|
u8* sheetData = (u8*)memalign( 32, uncompressedSize );// buffer needs to be 32bit aligned
|
|
if( !sheetData )
|
|
return NULL;
|
|
|
|
if( !Decompress_0x28( sheetData, uncompressedSize, ( data + tglp->dataOffset + off + 4 ), compressedSize ) )
|
|
{
|
|
free( sheetData );
|
|
return NULL;
|
|
}
|
|
|
|
// Flush cache
|
|
DCFlushRange(sheetData, uncompressedSize);
|
|
|
|
return sheetData;
|
|
}
|