mirror of
https://github.com/Fledge68/WiiFlow_Lite.git
synced 2024-12-26 03:41:55 +01:00
550ac3356b
-set down wiiflows entry point from 0x80A00000 to 0x8062000 (saves ALOT of MEM1 for lists etc, so wiiflow should be able to read in alot of more games in for example plugin coverflow) -recompiled libjpeg -removed alot of annoying debug prints -made the trailer code faster
487 lines
10 KiB
C++
487 lines
10 KiB
C++
#include "text.hpp"
|
|
|
|
int currentStr = 0;
|
|
char fmt_buffer[MAX_USES][MAX_MSG_SIZE];
|
|
char general_buffer[MAX_MSG_SIZE];
|
|
|
|
// Simplified use of sprintf
|
|
const char *fmt(const char *format, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, format);
|
|
currentStr = (currentStr + 1) % MAX_USES;
|
|
vsnprintf(fmt_buffer[currentStr], MAX_MSG_SIZE - 1, format, va);
|
|
va_end(va);
|
|
return fmt_buffer[currentStr];
|
|
}
|
|
|
|
string sfmt(const char *format, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, format);
|
|
int len = vsnprintf(general_buffer, MAX_MSG_SIZE - 1, format, va);
|
|
va_end(va);
|
|
return string(general_buffer, len);
|
|
}
|
|
|
|
static inline bool fmtCount(const wstringEx &format, int &i, int &s)
|
|
{
|
|
int state = 0;
|
|
|
|
i = 0;
|
|
s = 0;
|
|
for (u32 k = 0; k < format.size(); ++k)
|
|
{
|
|
if (state == 0)
|
|
{
|
|
if (format[k] == L'%')
|
|
state = 1;
|
|
}
|
|
else if (state == 1)
|
|
{
|
|
switch (format[k])
|
|
{
|
|
case L'%':
|
|
state = 0;
|
|
break;
|
|
case L'i':
|
|
case L'd':
|
|
state = 0;
|
|
++i;
|
|
break;
|
|
case L's':
|
|
state = 0;
|
|
++s;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Only handles the cases i need for translations : plain %i and %s
|
|
bool checkFmt(const wstringEx &ref, const wstringEx &format)
|
|
{
|
|
int s;
|
|
int i;
|
|
int refs;
|
|
int refi;
|
|
if (!fmtCount(ref, refi, refs))
|
|
return false;
|
|
if (!fmtCount(format, i, s))
|
|
return false;
|
|
return i == refi && s == refs;
|
|
}
|
|
|
|
wstringEx wfmt(const wstringEx &format, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, format);
|
|
vsnprintf(general_buffer, MAX_MSG_SIZE - 1, format.toUTF8().c_str(), va);
|
|
va_end(va);
|
|
wstringEx wide_buffer;
|
|
wide_buffer.fromUTF8(general_buffer);
|
|
return wide_buffer;
|
|
}
|
|
|
|
string vectorToString(const vector<string> &vect, string sep)
|
|
{
|
|
string s;
|
|
for (u32 i = 0; i < vect.size(); ++i)
|
|
{
|
|
if (i > 0)
|
|
s.append(sep);
|
|
s.append(vect[i]);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
wstringEx vectorToString(const vector<wstringEx> &vect, char sep)
|
|
{
|
|
wstringEx s;
|
|
for (u32 i = 0; i < vect.size(); ++i)
|
|
{
|
|
if (i > 0)
|
|
s.push_back(sep);
|
|
s.append(vect[i]);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
vector<string> stringToVector(const string &text, char sep)
|
|
{
|
|
vector<string> v;
|
|
if (text.empty()) return v;
|
|
u32 count = 1;
|
|
for (u32 i = 0; i < text.size(); ++i)
|
|
if (text[i] == sep)
|
|
++count;
|
|
v.reserve(count);
|
|
string::size_type off = 0;
|
|
string::size_type i = 0;
|
|
do
|
|
{
|
|
i = text.find_first_of(sep, off);
|
|
if (i != string::npos)
|
|
{
|
|
string ws(text.substr(off, i - off));
|
|
v.push_back(ws);
|
|
off = i + 1;
|
|
}
|
|
else
|
|
v.push_back(text.substr(off));
|
|
} while (i != string::npos);
|
|
return v;
|
|
}
|
|
|
|
vector<wstringEx> stringToVector(const wstringEx &text, char sep)
|
|
{
|
|
vector<wstringEx> v;
|
|
if (text.empty()) return v;
|
|
u32 count = 1;
|
|
for (u32 i = 0; i < text.size(); ++i)
|
|
if (text[i] == sep)
|
|
++count;
|
|
v.reserve(count);
|
|
wstringEx::size_type off = 0;
|
|
wstringEx::size_type i = 0;
|
|
do
|
|
{
|
|
i = text.find_first_of(sep, off);
|
|
if (i != wstringEx::npos)
|
|
{
|
|
wstringEx ws(text.substr(off, i - off));
|
|
v.push_back(ws);
|
|
off = i + 1;
|
|
}
|
|
else
|
|
v.push_back(text.substr(off));
|
|
} while (i != wstringEx::npos);
|
|
return v;
|
|
}
|
|
|
|
bool SFont::fromBuffer(u8 *buffer, u32 bufferSize, u32 size, u32 lspacing, u32 w, u32 idx, const char *)
|
|
{
|
|
if (!buffer || !font) return false;
|
|
|
|
size = min(max(6u, size), 1000u);
|
|
lineSpacing = min(max(6u, lspacing), 1000u);
|
|
weight = min(w, 32u);
|
|
index = idx;
|
|
|
|
if(data.get())
|
|
data.release();
|
|
data = smartMem2Alloc(bufferSize);
|
|
if(!data) return false;
|
|
|
|
memcpy(data.get(), buffer, bufferSize);
|
|
dataSize = bufferSize;
|
|
|
|
font->loadFont(data.get(), dataSize, size, weight, index, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SFont::fromFile(const char *filename, u32 size, u32 lspacing, u32 w, u32 idx)
|
|
{
|
|
if (!font) return false;
|
|
size = min(max(6u, size), 1000u);
|
|
weight = min(w, 32u);
|
|
index = idx = 0;
|
|
|
|
lineSpacing = min(max(6u, lspacing), 1000u);
|
|
|
|
FILE *file = fopen(filename, "rb");
|
|
if (file == NULL) return false;
|
|
fseek(file, 0, SEEK_END);
|
|
u32 fileSize = ftell(file);
|
|
fseek(file, 0, SEEK_SET);
|
|
if (fileSize == 0) return false;
|
|
|
|
if(data.get())
|
|
data.release();
|
|
data = smartMem2Alloc(fileSize);
|
|
if (!data)
|
|
{
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
|
|
fread(data.get(), 1, fileSize, file);
|
|
|
|
dataSize = fileSize;
|
|
|
|
font->loadFont(data.get(), dataSize, size, weight, index, false);
|
|
return true;
|
|
}
|
|
|
|
static const wchar_t *g_whitespaces = L" \f\n\r\t\v";
|
|
void CText::setText(SFont font, const wstringEx &t)
|
|
{
|
|
CText::SWord w;
|
|
m_lines.clear();
|
|
if (!!font.font) m_font = font;
|
|
if (!m_font.font) return;
|
|
|
|
firstLine = 0;
|
|
// Don't care about performance
|
|
vector<wstringEx> lines = stringToVector(t, L'\n');
|
|
m_lines.reserve(lines.size());
|
|
//
|
|
for (u32 k = 0; k < lines.size(); ++k)
|
|
{
|
|
wstringEx &l = lines[k];
|
|
m_lines.push_back(CText::CLine());
|
|
m_lines.back().reserve(32);
|
|
wstringEx::size_type i = l.find_first_not_of(g_whitespaces);
|
|
wstringEx::size_type j;
|
|
while (i != wstringEx::npos)
|
|
{
|
|
j = l.find_first_of(g_whitespaces, i);
|
|
if (j != wstringEx::npos && j > i)
|
|
{
|
|
w.text.assign(l, i, j - i);
|
|
m_lines.back().push_back(w);
|
|
i = l.find_first_not_of(g_whitespaces, j);
|
|
}
|
|
else if (j == wstringEx::npos)
|
|
{
|
|
w.text.assign(l, i, l.size() - i);
|
|
m_lines.back().push_back(w);
|
|
i = wstringEx::npos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CText::setText(SFont font, const wstringEx &t, u32 startline)
|
|
{
|
|
CText::SWord w;
|
|
totalHeight = 0;
|
|
|
|
m_lines.clear();
|
|
if (!!font.font) m_font = font;
|
|
if (!m_font.font) return;
|
|
|
|
firstLine = startline;
|
|
// Don't care about performance
|
|
vector<wstringEx> lines = stringToVector(t, L'\n');
|
|
m_lines.reserve(lines.size());
|
|
//
|
|
for (u32 k = 0; k < lines.size(); ++k)
|
|
{
|
|
wstringEx &l = lines[k];
|
|
m_lines.push_back(CText::CLine());
|
|
m_lines.back().reserve(32);
|
|
wstringEx::size_type i = l.find_first_not_of(g_whitespaces);
|
|
wstringEx::size_type j;
|
|
while (i != wstringEx::npos)
|
|
{
|
|
j = l.find_first_of(g_whitespaces, i);
|
|
if (j != wstringEx::npos && j > i)
|
|
{
|
|
w.text.assign(l, i, j - i);
|
|
m_lines.back().push_back(w);
|
|
i = l.find_first_not_of(g_whitespaces, j);
|
|
}
|
|
else if (j == wstringEx::npos)
|
|
{
|
|
w.text.assign(l, i, l.size() - i);
|
|
m_lines.back().push_back(w);
|
|
i = wstringEx::npos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CText::setFrame(float width, u16 style, bool ignoreNewlines, bool instant)
|
|
{
|
|
float shift;
|
|
|
|
totalHeight = 0;
|
|
|
|
if (!m_font.font) return;
|
|
|
|
float space = m_font.font->getWidth(L" ");
|
|
float posX = 0.f;
|
|
float posY = 0.f;
|
|
u32 lineBeg = 0;
|
|
|
|
if(firstLine > m_lines.size()) firstLine = 0;
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
{
|
|
CText::CLine &words = m_lines[k];
|
|
if (words.empty())
|
|
{
|
|
posY += (float)m_font.lineSpacing;
|
|
continue;
|
|
}
|
|
|
|
for (u32 i = 0; i < words.size(); ++i)
|
|
{
|
|
float wordWidth = m_font.font->getWidth(words[i].text.c_str());
|
|
if (posX == 0.f || posX + (float)wordWidth + space * 2 <= width)
|
|
{
|
|
words[i].targetPos = Vector3D(posX, posY, 0.f);
|
|
posX += wordWidth + space;
|
|
}
|
|
else
|
|
{
|
|
posY += (float)m_font.lineSpacing;
|
|
words[i].targetPos = Vector3D(0.f, posY, 0.f);
|
|
if ((style & (FTGX_JUSTIFY_CENTER | FTGX_JUSTIFY_RIGHT)) != 0)
|
|
{
|
|
posX -= space;
|
|
shift = (style & FTGX_JUSTIFY_CENTER) != 0 ? -posX * 0.5f : -posX;
|
|
for (u32 j = lineBeg; j < i; ++j)
|
|
words[j].targetPos.x += shift;
|
|
}
|
|
posX = wordWidth + space;
|
|
lineBeg = i;
|
|
}
|
|
}
|
|
// Quick patch for newline support
|
|
if (!ignoreNewlines && k + 1 < m_lines.size())
|
|
posX = 9999999.f;
|
|
}
|
|
totalHeight = posY + m_font.lineSpacing;
|
|
|
|
if ((style & (FTGX_JUSTIFY_CENTER | FTGX_JUSTIFY_RIGHT)) != 0)
|
|
{
|
|
posX -= space;
|
|
shift = (style & FTGX_JUSTIFY_CENTER) != 0 ? -posX * 0.5f : -posX;
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
for (u32 j = lineBeg; j < m_lines[k].size(); ++j)
|
|
m_lines[k][j].targetPos.x += shift;
|
|
}
|
|
if ((style & (FTGX_ALIGN_MIDDLE | FTGX_ALIGN_BOTTOM)) != 0)
|
|
{
|
|
posY += (float)m_font.lineSpacing;
|
|
shift = (style & FTGX_ALIGN_MIDDLE) != 0 ? -posY * 0.5f : -posY;
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
for (u32 j = 0; j < m_lines[k].size(); ++j)
|
|
m_lines[k][j].targetPos.y += shift;
|
|
}
|
|
if (instant)
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
for (u32 i = 0; i < m_lines[k].size(); ++i)
|
|
m_lines[k][i].pos = m_lines[k][i].targetPos;
|
|
}
|
|
|
|
void CText::setColor(const CColor &c)
|
|
{
|
|
m_color = c;
|
|
}
|
|
|
|
void CText::tick(void)
|
|
{
|
|
for (u32 k = 0; k < m_lines.size(); ++k)
|
|
for (u32 i = 0; i < m_lines[k].size(); ++i)
|
|
m_lines[k][i].pos += (m_lines[k][i].targetPos - m_lines[k][i].pos) * 0.05f;
|
|
}
|
|
|
|
void CText::draw(void)
|
|
{
|
|
if (!m_font.font) return;
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
for (u32 i = 0; i < m_lines[k].size(); ++i)
|
|
{
|
|
m_font.font->setX(m_lines[k][i].pos.x);
|
|
m_font.font->setY(m_lines[k][i].pos.y);
|
|
m_font.font->drawText(0, m_font.lineSpacing, m_lines[k][i].text.c_str(), m_color);
|
|
}
|
|
}
|
|
|
|
int CText::getTotalHeight(void)
|
|
{
|
|
return totalHeight;
|
|
}
|
|
|
|
string upperCase(string text)
|
|
{
|
|
char c;
|
|
for (string::size_type i = 0; i < text.size(); ++i)
|
|
{
|
|
c = text[i];
|
|
if (c >= 'a' && c <= 'z')
|
|
text[i] = c & 0xDF;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
|
|
string lowerCase(string text)
|
|
{
|
|
char c;
|
|
for (string::size_type i = 0; i < text.size(); ++i)
|
|
{
|
|
c = text[i];
|
|
if (c >= 'A' && c <= 'Z')
|
|
text[i] = c | 0x20;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
// trim from start
|
|
string ltrim(string s)
|
|
{
|
|
s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun<int, int>(isspace))));
|
|
return s;
|
|
}
|
|
|
|
// trim from end
|
|
string rtrim(string s)
|
|
{
|
|
s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun<int, int>(isspace))).base(), s.end());
|
|
return s;
|
|
}
|
|
|
|
void Asciify( wchar_t *str )
|
|
{
|
|
const wchar_t *ptr = str;
|
|
wchar_t *ctr = str;
|
|
|
|
while(*ptr != '\0')
|
|
{
|
|
switch(*ptr)
|
|
{
|
|
case 0x14c:
|
|
*ctr = 0x4f;
|
|
break;
|
|
}
|
|
*ctr = *ptr;
|
|
++ptr;
|
|
++ctr;
|
|
}
|
|
*ctr = '\0';
|
|
}
|
|
|
|
void Asciify2( char *str )
|
|
{
|
|
u8 i=0;
|
|
for( i=0; i < strlen(str); ++i )
|
|
{
|
|
if( str[i] < 0x20 || str[i] > 0x7F )
|
|
str[i] = '_';
|
|
else {
|
|
switch( str[i] )
|
|
{
|
|
case '*':
|
|
case '\"':
|
|
case '|':
|
|
case '<':
|
|
case '>':
|
|
case '?':
|
|
case ':':
|
|
str[i] = '_';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|