/*
* Copyright 2010 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see .
*/
#include "Language.h"
#include
#include
#include "CrossPlatform.h"
#include "Exception.h"
#include "Options.h"
#include "../Interface/TextList.h"
namespace OpenXcom
{
/**
* Initializes an empty language file.
*/
Language::Language() : _name(L""), _strings()
{
}
/**
*
*/
Language::~Language()
{
}
/**
* Takes an 8-bit string encoded in UTF-8 and converts it
* to a wide-character string.
* @note Adapted from http://www.linuxquestions.org/questions/programming-9/wstring-utf8-conversion-in-pure-c-701084/
* @param src UTF-8 string.
* @return Wide-character string.
*/
std::wstring Language::utf8ToWstr(const std::string& src)
{
std::wstring dest;
wchar_t w = 0;
int bytes = 0;
wchar_t err = 0xfffd;
for (size_t i = 0; i < src.size(); ++i)
{
unsigned char c = (unsigned char)src[i];
if (c <= 0x7f) //first byte
{
if (bytes)
{
dest.push_back(err);
bytes = 0;
}
dest.push_back((wchar_t)c);
}
else if (c <= 0xbf) //second/third/etc byte
{
if (bytes)
{
w = ((w << 6)|(c & 0x3f));
bytes--;
if (bytes == 0)
dest.push_back(w);
}
else
dest.push_back(err);
}
else if (c <= 0xdf) //2byte sequence start
{
bytes = 1;
w = c & 0x1f;
}
else if (c <= 0xef) //3byte sequence start
{
bytes = 2;
w = c & 0x0f;
}
else if (c <= 0xf7) //4byte sequence start
{
bytes = 3;
w = c & 0x07;
}
else
{
dest.push_back(err);
bytes = 0;
}
}
if (bytes)
dest.push_back(err);
return dest;
}
/**
* Takes a wide-character string and converts it
* to a 8-bit string encoded in UTF-8.
* @note Adapted from http://www.linuxquestions.org/questions/programming-9/wstring-utf8-conversion-in-pure-c-701084/
* @param src Wide-character string.
* @return UTF-8 string.
*/
std::string Language::wstrToUtf8(const std::wstring& src)
{
std::string dest;
for (size_t i = 0; i < src.size(); ++i)
{
wchar_t w = src[i];
if (w <= 0x7f)
{
dest.push_back((char)w);
}
else if (w <= 0x7ff)
{
dest.push_back(0xc0 | ((w >> 6)& 0x1f));
dest.push_back(0x80 | (w & 0x3f));
}
else if (w <= 0xffff)
{
dest.push_back(0xe0 | ((w >> 12)& 0x0f));
dest.push_back(0x80 | ((w >> 6) & 0x3f));
dest.push_back(0x80 | (w & 0x3f));
}
else if (w <= 0x10ffff)
{
dest.push_back(0xf0 | ((w >> 18)& 0x07));
dest.push_back(0x80 | ((w >> 12) & 0x3f));
dest.push_back(0x80 | ((w >> 6) & 0x3f));
dest.push_back(0x80 | (w & 0x3f));
}
else
dest.push_back('?');
}
return dest;
}
/**
* Replaces every instance of a substring.
* @param str The string to modify.
* @param find The substring to find.
* @param replace The substring to replace it with.
*/
void Language::replace(std::string &str, const std::string &find, const std::string &replace)
{
for (size_t i = str.find(find); i != std::string::npos;)
{
str.replace(i, find.length(), replace);
++i;
i = str.find(find, i);
}
}
/**
* Gets all the languages found in the
* data folder and adds them to a text list.
* @param list Text list.
* @return List of language filenames.
*/
std::vector Language::getList(TextList *list)
{
std::vector langs = CrossPlatform::getFolderContents(Options::getDataFolder() + "Language/", "lng");
for (std::vector::iterator i = langs.begin(); i != langs.end();)
{
std::string file = (*i);
std::string fullname = Options::getDataFolder() + "Language/" + file;
std::ifstream fin(fullname.c_str(), std::ios::in | std::ios::binary);
try
{
if (!fin)
{
throw Exception("Failed to load language");
}
char value;
std::string langname;
while (fin.read(&value, 1))
{
if (value != '\n')
{
langname += value;
}
else
{
break;
}
}
fin.close();
list->addRow(1, Language::utf8ToWstr(langname).c_str());
(*i) = file.substr(0, file.length()-4);
++i;
}
catch (Exception &e)
{
std::cerr << e.what() << std::endl;
i = langs.erase(i);
continue;
}
}
return langs;
}
/**
* Loads pairs of strings separated by linebreaks contained
* in a text file into the Language. Each pair is made of
* an ID and a localized string.
* @param filename Filename of the LNG file.
* @sa Reference Table
*/
void Language::loadLng(const std::string &filename)
{
_strings.clear();
std::ifstream txtFile (filename.c_str(), std::ios::in | std::ios::binary);
if (!txtFile)
{
throw Exception("Failed to load LNG");
}
char value;
std::string buffer, bufid;
std::wstring bufstr;
bool first = true, id = true;
while (txtFile.read(&value, 1))
{
if (value != '\n')
{
buffer += value;
}
else
{
// Get language name
if (first)
{
_name = utf8ToWstr(buffer);
first = false;
}
else
{
// Get ID
if (id)
{
bufid = buffer;
}
// Get string
else
{
replace(buffer, "{NEWLINE}", "\n");
replace(buffer, "{SMALLLINE}", "\x02");
bufstr = utf8ToWstr(buffer);
_strings[bufid] = bufstr;
}
id = !id;
}
buffer.clear();
}
}
if (!txtFile.eof())
{
throw Exception("Invalid data from file");
}
txtFile.close();
}
/**
* Returns the language's name in its native language.
* @return Language name.
*/
std::wstring Language::getName() const
{
return _name;
}
/**
* Returns the localizable string with the specified ID.
* If it's not found, just returns the ID.
* @param id ID of the string.
* @return String with the requested ID.
*/
std::wstring Language::getString(const std::string &id) const
{
std::map::const_iterator s = _strings.find(id);
if (s == _strings.end())
{
std::wcout << "WARNING: " << utf8ToWstr(id) << " not found in " << _name << std::endl;
return utf8ToWstr(id);
}
else
{
return s->second;
}
}
/**
* Outputs all the language IDs and strings
* to an HTML table.
*/
void Language::toHtml() const
{
std::ofstream htmlFile ("lang.html", std::ios::out);
htmlFile << "
" << std::endl;
htmlFile << "
ID String
English String
" << std::endl;
for (std::map::const_iterator i = _strings.begin(); i != _strings.end(); ++i)
{
htmlFile << "