mirror of
https://github.com/Fledge68/WiiFlow_Lite.git
synced 2024-12-26 03:41:55 +01:00
b2aef2cdaa
in the plugin menu -using the newly created sort function for covers too now so we dont need to use two separate ones (covers should be sorted same way still)
460 lines
10 KiB
C++
460 lines
10 KiB
C++
#include <algorithm>
|
|
#include "text.hpp"
|
|
#include "memory/mem2.hpp"
|
|
|
|
static char general_buffer[MAX_MSG_SIZE * 2];
|
|
string sfmt(const char *format, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, format);
|
|
size_t len = vsnprintf(general_buffer, MAX_MSG_SIZE - 1, format, va);
|
|
general_buffer[MAX_MSG_SIZE - 1] = '\0';
|
|
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);
|
|
general_buffer[MAX_MSG_SIZE - 1] = '\0';
|
|
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;
|
|
}
|
|
|
|
void SFont::ClearData(void)
|
|
{
|
|
if(font != NULL)
|
|
delete font;
|
|
font = NULL;
|
|
if(data != NULL)
|
|
free(data);
|
|
data = NULL;
|
|
dataSize = 0;
|
|
}
|
|
|
|
bool SFont::fromBuffer(const u8 *buffer, const u32 bufferSize, u32 size, u32 lspacing, u32 w, u32 idx, const char *)
|
|
{
|
|
if(buffer == NULL)
|
|
return false;
|
|
size = min(max(6u, size), 1000u);
|
|
lineSpacing = min(max(6u, lspacing), 1000u);
|
|
weight = min(w, 32u);
|
|
index = idx;
|
|
|
|
if(data != NULL)
|
|
free(data);
|
|
data = (u8*)MEM2_alloc(bufferSize);
|
|
if(data == NULL) return false;
|
|
dataSize = bufferSize;
|
|
|
|
memcpy(data, buffer, bufferSize);
|
|
DCFlushRange(data, dataSize);
|
|
|
|
font = new FreeTypeGX();
|
|
font->loadFont(data, dataSize, size, weight, index, false);
|
|
return true;
|
|
}
|
|
|
|
bool SFont::fromFile(const char *filename, u32 size, u32 lspacing, u32 w, u32 idx)
|
|
{
|
|
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 != NULL)
|
|
free(data);
|
|
data = (u8*)MEM2_alloc(fileSize);
|
|
if (!data)
|
|
{
|
|
fclose(file);
|
|
return false;
|
|
}
|
|
|
|
fread(data, 1, fileSize, file);
|
|
dataSize = fileSize;
|
|
DCFlushRange(data, dataSize);
|
|
|
|
font = new FreeTypeGX();
|
|
font->loadFont(data, dataSize, size, weight, index, false);
|
|
return true;
|
|
}
|
|
|
|
static const wchar_t *g_whitespaces = L" \f\n\r\t\v";
|
|
void CText::setText(const SFont &font, const wstringEx &t)
|
|
{
|
|
SWord w;
|
|
m_lines.clear();
|
|
if(font.font == NULL)
|
|
return;
|
|
m_font = font;
|
|
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(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(const SFont &font, const wstringEx &t, u32 startline)
|
|
{
|
|
SWord w;
|
|
totalHeight = 0;
|
|
|
|
m_lines.clear();
|
|
if(font.font != NULL)
|
|
m_font = font;
|
|
if(m_font.font == NULL)
|
|
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(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)
|
|
{
|
|
if(m_font.font == NULL)
|
|
return;
|
|
|
|
float shift;
|
|
totalHeight = 0;
|
|
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)
|
|
{
|
|
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 == NULL)
|
|
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;
|
|
}
|
|
|
|
bool wchar_cmp(const wchar_t *first, const wchar_t *second, u32 first_len, u32 second_len)
|
|
{
|
|
u32 i = 0;
|
|
while((i < first_len) && (i < second_len))
|
|
{
|
|
if(tolower(first[i]) < tolower(second[i]))
|
|
return true;
|
|
else if(tolower(first[i]) > tolower(second[i]))
|
|
return false;
|
|
++i;
|
|
}
|
|
return first_len < second_len;
|
|
}
|