mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-24 05:49:58 +00:00
Basic internationalization (i18n) framework.
This commit is contained in:
parent
909b67743e
commit
b25cfe209b
@ -1,8 +1,19 @@
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include "base/buffer.h"
|
||||
#include "base/stringutil.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// Function Cross-Compatibility
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
unsigned int parseHex(const char *_szValue)
|
||||
{
|
||||
int Value = 0;
|
||||
@ -51,3 +62,144 @@ void DataToHexString(const uint8 *data, size_t size, std::string *output) {
|
||||
}
|
||||
buffer.TakeAll(output);
|
||||
}
|
||||
|
||||
std::string StringFromFormat(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *buf = NULL;
|
||||
std::string temp = "";
|
||||
#ifdef _WIN32
|
||||
int required = 0;
|
||||
|
||||
va_start(args, format);
|
||||
required = _vscprintf(format, args);
|
||||
buf = new char[required + 1];
|
||||
if(vsnprintf(buf, required, format, args) < 0)
|
||||
buf[0] = '\0';
|
||||
va_end(args);
|
||||
|
||||
buf[required] = '\0';
|
||||
temp = buf;
|
||||
delete[] buf;
|
||||
#else
|
||||
va_start(args, format);
|
||||
if(vasprintf(&buf, format, args) < 0)
|
||||
buf = NULL;
|
||||
va_end(args);
|
||||
|
||||
if(buf != NULL) {
|
||||
temp = buf;
|
||||
free(buf);
|
||||
}
|
||||
#endif
|
||||
return temp;
|
||||
}
|
||||
|
||||
std::string StringFromInt(int value)
|
||||
{
|
||||
char temp[16];
|
||||
sprintf(temp, "%i", value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value)
|
||||
{
|
||||
return value ? "True" : "False";
|
||||
}
|
||||
|
||||
// Turns " hej " into "hej". Also handles tabs.
|
||||
std::string StripSpaces(const std::string &str)
|
||||
{
|
||||
const size_t s = str.find_first_not_of(" \t\r\n");
|
||||
|
||||
if (str.npos != s)
|
||||
return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
// "\"hello\"" is turned to "hello"
|
||||
// This one assumes that the string has already been space stripped in both
|
||||
// ends, as done by StripSpaces above, for example.
|
||||
std::string StripQuotes(const std::string& s)
|
||||
{
|
||||
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
||||
return s.substr(1, s.size() - 2);
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const uint8_t *data, uint32_t size, int line_len, bool spaces)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::hex;
|
||||
|
||||
for (int line = 0; size; ++data, --size)
|
||||
{
|
||||
oss << std::setw(2) << (int)*data;
|
||||
if (line_len == ++line)
|
||||
{
|
||||
oss << '\n';
|
||||
line = 0;
|
||||
}
|
||||
else if (spaces)
|
||||
oss << ' ';
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool TryParse(const std::string &str, uint32_t *const output)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
|
||||
// Holy crap this is ugly.
|
||||
|
||||
// Reset errno to a value other than ERANGE
|
||||
errno = 0;
|
||||
|
||||
unsigned long value = strtoul(str.c_str(), &endptr, 0);
|
||||
|
||||
if (!endptr || *endptr)
|
||||
return false;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
if (ULONG_MAX > UINT_MAX) {
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4309)
|
||||
#endif
|
||||
// Note: The typecasts avoid GCC warnings when long is 32 bits wide.
|
||||
if (value >= static_cast<unsigned long>(0x100000000ull)
|
||||
&& value <= static_cast<unsigned long>(0xFFFFFFFF00000000ull))
|
||||
return false;
|
||||
}
|
||||
|
||||
*output = static_cast<uint32_t>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string &str, bool *const output)
|
||||
{
|
||||
if ("1" == str || !strcasecmp("true", str.c_str()))
|
||||
*output = true;
|
||||
else if ("0" == str || !strcasecmp("false", str.c_str()))
|
||||
*output = false;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output)
|
||||
{
|
||||
std::istringstream iss(str);
|
||||
output.resize(1);
|
||||
|
||||
while (std::getline(iss, *output.rbegin(), delim))
|
||||
output.push_back("");
|
||||
|
||||
output.pop_back();
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
@ -55,6 +57,45 @@ public:
|
||||
bool operator ==(const ConstString &other) const {
|
||||
return ptr_ == other.ptr_ || !strcmp(ptr_, other.ptr_);
|
||||
}
|
||||
const char *get() const { return ptr_; }
|
||||
private:
|
||||
const char *ptr_;
|
||||
};
|
||||
|
||||
std::string StringFromFormat(const char* format, ...);
|
||||
std::string StringFromInt(int value);
|
||||
std::string StringFromBool(bool value);
|
||||
|
||||
std::string ArrayToString(const uint8_t *data, uint32_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StripSpaces(const std::string &s);
|
||||
std::string StripQuotes(const std::string &s);
|
||||
|
||||
bool TryParse(const std::string &str, bool *const output);
|
||||
bool TryParse(const std::string &str, uint32_t *const output);
|
||||
|
||||
template <typename N>
|
||||
static bool TryParse(const std::string &str, N *const output)
|
||||
{
|
||||
std::istringstream iss(str);
|
||||
|
||||
N tmp = 0;
|
||||
if (iss >> tmp)
|
||||
{
|
||||
*output = tmp;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output);
|
||||
|
||||
|
||||
template <typename N>
|
||||
static std::string ValueToString(const N value)
|
||||
{
|
||||
std::stringstream string;
|
||||
string << value;
|
||||
return string.str();
|
||||
}
|
||||
|
||||
|
580
file/ini_file.cpp
Normal file
580
file/ini_file.cpp
Normal file
@ -0,0 +1,580 @@
|
||||
// IniFile
|
||||
// Taken from Dolphin but relicensed by me, Henrik Rydgard, under the MIT
|
||||
// license as I wrote the whole thing originally and it has barely changed.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/stringutil.h"
|
||||
#include "file/ini_file.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef _WIN32
|
||||
// Function Cross-Compatibility
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
// Ugh, this is ugly.
|
||||
static bool ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut)
|
||||
{
|
||||
int FirstEquals = (int)line.find("=", 0);
|
||||
int FirstCommentChar = -1;
|
||||
|
||||
// Comments
|
||||
if (FirstCommentChar < 0)
|
||||
FirstCommentChar =
|
||||
(int)line.find("#", FirstEquals > 0 ? FirstEquals : 0);
|
||||
if (FirstCommentChar < 0 && line[0] == ';')
|
||||
FirstCommentChar = 0;
|
||||
|
||||
// Allow preservation of spacing before comment
|
||||
if (FirstCommentChar > 0)
|
||||
{
|
||||
while (line[FirstCommentChar - 1] == ' ' || line[FirstCommentChar - 1] == 9) // 9 == tab
|
||||
{
|
||||
FirstCommentChar--;
|
||||
}
|
||||
}
|
||||
|
||||
if ((FirstEquals >= 0) && ((FirstCommentChar < 0) || (FirstEquals < FirstCommentChar)))
|
||||
{
|
||||
// Yes, a valid key/value line!
|
||||
*keyOut = StripSpaces(line.substr(0, FirstEquals));
|
||||
if (commentOut) *commentOut = FirstCommentChar > 0 ? line.substr(FirstCommentChar) : std::string("");
|
||||
if (valueOut) *valueOut = StripQuotes(StripSpaces(line.substr(FirstEquals + 1, FirstCommentChar - FirstEquals - 1)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string* IniFile::Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut)
|
||||
{
|
||||
for (std::vector<std::string>::iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string& line = *iter;
|
||||
std::string lineKey;
|
||||
ParseLine(line, &lineKey, valueOut, commentOut);
|
||||
if (!strcasecmp(lineKey.c_str(), key))
|
||||
return &line;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IniFile::Section::Set(const char* key, const char* newValue)
|
||||
{
|
||||
std::string value, commented;
|
||||
std::string* line = GetLine(key, &value, &commented);
|
||||
if (line)
|
||||
{
|
||||
// Change the value - keep the key and comment
|
||||
*line = StripSpaces(key) + " = " + newValue + commented;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The key did not already exist in this section - let's add it.
|
||||
lines.push_back(std::string(key) + " = " + newValue);
|
||||
}
|
||||
}
|
||||
|
||||
void IniFile::Section::Set(const char* key, const std::string& newValue, const std::string& defaultValue)
|
||||
{
|
||||
if (newValue != defaultValue)
|
||||
Set(key, newValue);
|
||||
else
|
||||
Delete(key);
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, std::string* value, const char* defaultValue)
|
||||
{
|
||||
std::string* line = GetLine(key, value, 0);
|
||||
if (!line)
|
||||
{
|
||||
if (defaultValue)
|
||||
{
|
||||
*value = defaultValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IniFile::Section::Set(const char* key, const float newValue, const float defaultValue)
|
||||
{
|
||||
if (newValue != defaultValue)
|
||||
Set(key, newValue);
|
||||
else
|
||||
Delete(key);
|
||||
}
|
||||
|
||||
void IniFile::Section::Set(const char* key, int newValue, int defaultValue)
|
||||
{
|
||||
if (newValue != defaultValue)
|
||||
Set(key, newValue);
|
||||
else
|
||||
Delete(key);
|
||||
}
|
||||
|
||||
void IniFile::Section::Set(const char* key, bool newValue, bool defaultValue)
|
||||
{
|
||||
if (newValue != defaultValue)
|
||||
Set(key, newValue);
|
||||
else
|
||||
Delete(key);
|
||||
}
|
||||
|
||||
void IniFile::Section::Set(const char* key, const std::vector<std::string>& newValues)
|
||||
{
|
||||
std::string temp;
|
||||
// Join the strings with ,
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for (it = newValues.begin(); it != newValues.end(); ++it)
|
||||
{
|
||||
temp += (*it) + ",";
|
||||
}
|
||||
// remove last ,
|
||||
if (temp.length())
|
||||
temp.resize(temp.length() - 1);
|
||||
Set(key, temp.c_str());
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, std::vector<std::string>& values)
|
||||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (!retval || temp.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// ignore starting , if any
|
||||
size_t subStart = temp.find_first_not_of(",");
|
||||
size_t subEnd;
|
||||
|
||||
// split by ,
|
||||
while (subStart != std::string::npos) {
|
||||
|
||||
// Find next ,
|
||||
subEnd = temp.find_first_of(",", subStart);
|
||||
if (subStart != subEnd)
|
||||
// take from first char until next ,
|
||||
values.push_back(StripSpaces(temp.substr(subStart, subEnd - subStart)));
|
||||
|
||||
// Find the next non , char
|
||||
subStart = temp.find_first_not_of(",", subEnd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, int* value, int defaultValue)
|
||||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, uint32_t* value, uint32_t defaultValue)
|
||||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp, value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, bool* value, bool defaultValue)
|
||||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, float* value, float defaultValue)
|
||||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Section::Get(const char* key, double* value, double defaultValue)
|
||||
{
|
||||
std::string temp;
|
||||
bool retval = Get(key, &temp, 0);
|
||||
if (retval && TryParse(temp.c_str(), value))
|
||||
return true;
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Section::Exists(const char *key) const
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string lineKey;
|
||||
ParseLine(*iter, &lineKey, NULL, NULL);
|
||||
if (!strcasecmp(lineKey.c_str(), key))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> IniFile::Section::ToMap() const
|
||||
{
|
||||
std::map<std::string, std::string> outMap;
|
||||
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
std::string lineKey, lineValue;
|
||||
if (ParseLine(*iter, &lineKey, &lineValue, NULL)) {
|
||||
outMap[lineKey] = lineValue;
|
||||
}
|
||||
}
|
||||
return outMap;
|
||||
}
|
||||
|
||||
|
||||
bool IniFile::Section::Delete(const char *key)
|
||||
{
|
||||
std::string* line = GetLine(key, 0, 0);
|
||||
for (std::vector<std::string>::iterator liter = lines.begin(); liter != lines.end(); ++liter)
|
||||
{
|
||||
if (line == &*liter)
|
||||
{
|
||||
lines.erase(liter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// IniFile
|
||||
|
||||
const IniFile::Section* IniFile::GetSection(const char* sectionName) const
|
||||
{
|
||||
for (std::vector<Section>::const_iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
if (!strcasecmp(iter->name().c_str(), sectionName))
|
||||
return (&(*iter));
|
||||
return 0;
|
||||
}
|
||||
|
||||
IniFile::Section* IniFile::GetSection(const char* sectionName)
|
||||
{
|
||||
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
if (!strcasecmp(iter->name().c_str(), sectionName))
|
||||
return (&(*iter));
|
||||
return 0;
|
||||
}
|
||||
|
||||
IniFile::Section* IniFile::GetOrCreateSection(const char* sectionName)
|
||||
{
|
||||
Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
{
|
||||
sections.push_back(Section(sectionName));
|
||||
section = §ions[sections.size() - 1];
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
bool IniFile::DeleteSection(const char* sectionName)
|
||||
{
|
||||
Section* s = GetSection(sectionName);
|
||||
if (!s)
|
||||
return false;
|
||||
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
{
|
||||
if (&(*iter) == s)
|
||||
{
|
||||
sections.erase(iter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::Exists(const char* sectionName, const char* key) const
|
||||
{
|
||||
const Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
return section->Exists(key);
|
||||
}
|
||||
|
||||
void IniFile::SetLines(const char* sectionName, const std::vector<std::string> &lines)
|
||||
{
|
||||
Section* section = GetOrCreateSection(sectionName);
|
||||
section->lines.clear();
|
||||
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
|
||||
{
|
||||
section->lines.push_back(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniFile::DeleteKey(const char* sectionName, const char* key)
|
||||
{
|
||||
Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
std::string* line = section->GetLine(key, 0, 0);
|
||||
for (std::vector<std::string>::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
|
||||
{
|
||||
if (line == &(*liter))
|
||||
{
|
||||
section->lines.erase(liter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; //shouldn't happen
|
||||
}
|
||||
|
||||
// Return a list of all keys in a section
|
||||
bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) const
|
||||
{
|
||||
const Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
keys.clear();
|
||||
for (std::vector<std::string>::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
|
||||
{
|
||||
std::string key;
|
||||
ParseLine(*liter, &key, 0, 0);
|
||||
keys.push_back(key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return a list of all lines in a section
|
||||
bool IniFile::GetLines(const char* sectionName, std::vector<std::string>& lines, const bool remove_comments) const
|
||||
{
|
||||
const Section* section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
|
||||
lines.clear();
|
||||
for (std::vector<std::string>::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter)
|
||||
{
|
||||
std::string line = StripSpaces(*iter);
|
||||
|
||||
if (remove_comments)
|
||||
{
|
||||
int commentPos = (int)line.find('#');
|
||||
if (commentPos == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (commentPos != (int)std::string::npos)
|
||||
{
|
||||
line = StripSpaces(line.substr(0, commentPos));
|
||||
}
|
||||
}
|
||||
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void IniFile::SortSections()
|
||||
{
|
||||
std::sort(sections.begin(), sections.end());
|
||||
}
|
||||
|
||||
bool IniFile::Load(const char* filename)
|
||||
{
|
||||
// Maximum number of letters in a line
|
||||
static const int MAX_BYTES = 1024*32;
|
||||
|
||||
sections.clear();
|
||||
sections.push_back(Section(""));
|
||||
// first section consists of the comments before the first real section
|
||||
|
||||
// Open file
|
||||
std::ifstream in;
|
||||
in.open(filename, std::ios::in);
|
||||
|
||||
if (in.fail()) return false;
|
||||
|
||||
while (!in.eof())
|
||||
{
|
||||
char templine[MAX_BYTES];
|
||||
in.getline(templine, MAX_BYTES);
|
||||
std::string line = templine;
|
||||
|
||||
// Remove UTF-8 byte order marks.
|
||||
if (line.substr(0, 3) == "\xEF\xBB\xBF")
|
||||
line = line.substr(3);
|
||||
|
||||
#ifndef _WIN32
|
||||
// Check for CRLF eol and convert it to LF
|
||||
if (!line.empty() && line.at(line.size()-1) == '\r')
|
||||
{
|
||||
line.erase(line.size()-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (in.eof()) break;
|
||||
|
||||
if (line.size() > 0)
|
||||
{
|
||||
if (line[0] == '[')
|
||||
{
|
||||
size_t endpos = line.find("]");
|
||||
|
||||
if (endpos != std::string::npos)
|
||||
{
|
||||
// New section!
|
||||
std::string sub = line.substr(1, endpos - 1);
|
||||
sections.push_back(Section(sub));
|
||||
|
||||
if (endpos + 1 < line.size())
|
||||
{
|
||||
sections[sections.size() - 1].comment = line.substr(endpos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sections[sections.size() - 1].lines.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::Save(const char* filename)
|
||||
{
|
||||
std::ofstream out;
|
||||
out.open(filename, std::ios::out);
|
||||
|
||||
if (out.fail())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// UTF-8 byte order mark. To make sure notepad doesn't go nuts.
|
||||
out << "\xEF\xBB\xBF";
|
||||
|
||||
// Currently testing if dolphin community can handle the requirements of C++11 compilation
|
||||
// If you get a compiler error on this line, your compiler is probably old.
|
||||
// Update to g++ 4.4 or a recent version of clang (XCode 4.2 on OS X).
|
||||
// If you don't want to update, complain in a google code issue, the dolphin forums or #dolphin-emu.
|
||||
for (std::vector<Section>::iterator iter = sections.begin(); iter != sections.end(); ++iter)
|
||||
{
|
||||
const Section& section = *iter;
|
||||
|
||||
if (section.name() != "")
|
||||
{
|
||||
out << "[" << section.name() << "]" << section.comment << std::endl;
|
||||
}
|
||||
|
||||
for (std::vector<std::string>::const_iterator liter = section.lines.begin(); liter != section.lines.end(); ++liter)
|
||||
{
|
||||
std::string s = *liter;
|
||||
out << s << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IniFile::Get(const char* sectionName, const char* key, std::string* value, const char* defaultValue)
|
||||
{
|
||||
Section* section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
if (defaultValue) {
|
||||
*value = defaultValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return section->Get(key, value, defaultValue);
|
||||
}
|
||||
|
||||
bool IniFile::Get(const char *sectionName, const char* key, std::vector<std::string>& values)
|
||||
{
|
||||
Section *section = GetSection(sectionName);
|
||||
if (!section)
|
||||
return false;
|
||||
return section->Get(key, values);
|
||||
}
|
||||
|
||||
bool IniFile::Get(const char* sectionName, const char* key, int* value, int defaultValue)
|
||||
{
|
||||
Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniFile::Get(const char* sectionName, const char* key, uint32_t* value, uint32_t defaultValue)
|
||||
{
|
||||
Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniFile::Get(const char* sectionName, const char* key, bool* value, bool defaultValue)
|
||||
{
|
||||
Section *section = GetSection(sectionName);
|
||||
if (!section) {
|
||||
*value = defaultValue;
|
||||
return false;
|
||||
} else {
|
||||
return section->Get(key, value, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Unit test. TODO: Move to the real unit test framework.
|
||||
/*
|
||||
int main()
|
||||
{
|
||||
IniFile ini;
|
||||
ini.Load("my.ini");
|
||||
ini.Set("Hej", "A", "amaskdfl");
|
||||
ini.Set("Mossa", "A", "amaskdfl");
|
||||
ini.Set("Aissa", "A", "amaskdfl");
|
||||
//ini.Read("my.ini");
|
||||
std::string x;
|
||||
ini.Get("Hej", "B", &x, "boo");
|
||||
ini.DeleteKey("Mossa", "A");
|
||||
ini.DeleteSection("Mossa");
|
||||
ini.SortSections();
|
||||
ini.Save("my.ini");
|
||||
//UpdateVars(ini);
|
||||
return 0;
|
||||
}
|
||||
*/
|
179
file/ini_file.h
Normal file
179
file/ini_file.h
Normal file
@ -0,0 +1,179 @@
|
||||
// IniFile
|
||||
// Taken from Dolphin but relicensed by me, Henrik Rydgard, under the MIT
|
||||
// license as I wrote the whole thing originally and it has barely changed.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "base/stringutil.h"
|
||||
|
||||
class IniFile
|
||||
{
|
||||
public:
|
||||
class Section
|
||||
{
|
||||
friend class IniFile;
|
||||
|
||||
public:
|
||||
Section() {}
|
||||
Section(const std::string& name) : name_(name) {}
|
||||
|
||||
bool Exists(const char *key) const;
|
||||
bool Delete(const char *key);
|
||||
|
||||
std::map<std::string, std::string> ToMap() const;
|
||||
|
||||
std::string* GetLine(const char* key, std::string* valueOut, std::string* commentOut);
|
||||
void Set(const char* key, const char* newValue);
|
||||
void Set(const char* key, const std::string& newValue, const std::string& defaultValue);
|
||||
|
||||
void Set(const std::string &key, const std::string &value) {
|
||||
Set(key.c_str(), value.c_str());
|
||||
}
|
||||
bool Get(const char* key, std::string* value, const char* defaultValue);
|
||||
|
||||
void Set(const char* key, uint32_t newValue) {
|
||||
Set(key, StringFromFormat("0x%08x", newValue).c_str());
|
||||
}
|
||||
void Set(const char* key, float newValue) {
|
||||
Set(key, StringFromFormat("%f", newValue).c_str());
|
||||
}
|
||||
void Set(const char* key, const float newValue, const float defaultValue);
|
||||
void Set(const char* key, double newValue) {
|
||||
Set(key, StringFromFormat("%f", newValue).c_str());
|
||||
}
|
||||
|
||||
void Set(const char* key, int newValue, int defaultValue);
|
||||
void Set(const char* key, int newValue) {
|
||||
Set(key, StringFromInt(newValue).c_str());
|
||||
}
|
||||
|
||||
void Set(const char* key, bool newValue, bool defaultValue);
|
||||
void Set(const char* key, bool newValue) {
|
||||
Set(key, StringFromBool(newValue).c_str());
|
||||
}
|
||||
void Set(const char* key, const std::vector<std::string>& newValues);
|
||||
|
||||
template<typename U, typename V>
|
||||
void Set(const char* key, const std::map<U,V>& newValues)
|
||||
{
|
||||
std::vector<std::string> temp;
|
||||
for(typename std::map<U,V>::const_iterator it = newValues.begin(); it != newValues.end(); it++)
|
||||
{
|
||||
temp.push_back(ValueToString<U>(it->first)+"_"+ValueToString<V>(it->second));
|
||||
}
|
||||
Set(key,temp);
|
||||
}
|
||||
|
||||
bool Get(const char* key, int* value, int defaultValue = 0);
|
||||
bool Get(const char* key, uint32_t* value, uint32_t defaultValue = 0);
|
||||
bool Get(const char* key, bool* value, bool defaultValue = false);
|
||||
bool Get(const char* key, float* value, float defaultValue = false);
|
||||
bool Get(const char* key, double* value, double defaultValue = false);
|
||||
bool Get(const char* key, std::vector<std::string>& values);
|
||||
template<typename U, typename V>
|
||||
bool Get(const char* key, std::map<U,V>& values)
|
||||
{
|
||||
std::vector<std::string> temp;
|
||||
if(!Get(key,temp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
values.clear();
|
||||
for(size_t i = 0; i < temp.size(); i++)
|
||||
{
|
||||
std::vector<std::string> key_val;
|
||||
SplitString(temp[i],'_',key_val);
|
||||
if(key_val.size() < 2)
|
||||
continue;
|
||||
U mapKey;
|
||||
V mapValue;
|
||||
if(!TryParse<U>(key_val[0],&mapKey))
|
||||
continue;
|
||||
if(!TryParse<V>(key_val[1],&mapValue))
|
||||
continue;
|
||||
values[mapKey] = mapValue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator < (const Section& other) const {
|
||||
return name_ < other.name_;
|
||||
}
|
||||
|
||||
const std::string &name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::string> lines;
|
||||
std::string name_;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
bool Load(const char* filename);
|
||||
bool Load(const std::string &filename) { return Load(filename.c_str()); }
|
||||
bool Save(const char* filename);
|
||||
bool Save(const std::string &filename) { return Save(filename.c_str()); }
|
||||
|
||||
// Returns true if key exists in section
|
||||
bool Exists(const char* sectionName, const char* key) const;
|
||||
|
||||
// TODO: Get rid of these, in favor of the Section ones.
|
||||
void Set(const char* sectionName, const char* key, const char* newValue) {
|
||||
GetOrCreateSection(sectionName)->Set(key, newValue);
|
||||
}
|
||||
void Set(const char* sectionName, const char* key, const std::string& newValue) {
|
||||
GetOrCreateSection(sectionName)->Set(key, newValue.c_str());
|
||||
}
|
||||
void Set(const char* sectionName, const char* key, int newValue) {
|
||||
GetOrCreateSection(sectionName)->Set(key, newValue);
|
||||
}
|
||||
void Set(const char* sectionName, const char* key, uint32_t newValue) {
|
||||
GetOrCreateSection(sectionName)->Set(key, newValue);
|
||||
}
|
||||
void Set(const char* sectionName, const char* key, bool newValue) {
|
||||
GetOrCreateSection(sectionName)->Set(key, newValue);
|
||||
}
|
||||
void Set(const char* sectionName, const char* key, const std::vector<std::string>& newValues) {
|
||||
GetOrCreateSection(sectionName)->Set(key, newValues);
|
||||
}
|
||||
|
||||
// TODO: Get rid of these, in favor of the Section ones.
|
||||
bool Get(const char* sectionName, const char* key, std::string* value, const char* defaultValue = "");
|
||||
bool Get(const char* sectionName, const char* key, int* value, int defaultValue = 0);
|
||||
bool Get(const char* sectionName, const char* key, uint32_t* value, uint32_t defaultValue = 0);
|
||||
bool Get(const char* sectionName, const char* key, bool* value, bool defaultValue = false);
|
||||
bool Get(const char* sectionName, const char* key, std::vector<std::string>& values);
|
||||
|
||||
template<typename T> bool GetIfExists(const char* sectionName, const char* key, T value)
|
||||
{
|
||||
if (Exists(sectionName, key))
|
||||
return Get(sectionName, key, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetKeys(const char* sectionName, std::vector<std::string>& keys) const;
|
||||
|
||||
void SetLines(const char* sectionName, const std::vector<std::string> &lines);
|
||||
bool GetLines(const char* sectionName, std::vector<std::string>& lines, const bool remove_comments = true) const;
|
||||
|
||||
bool DeleteKey(const char* sectionName, const char* key);
|
||||
bool DeleteSection(const char* sectionName);
|
||||
|
||||
void SortSections();
|
||||
const std::vector<Section> &Sections() { return sections; }
|
||||
|
||||
Section* GetOrCreateSection(const char* section);
|
||||
|
||||
private:
|
||||
std::vector<Section> sections;
|
||||
|
||||
const Section* GetSection(const char* section) const;
|
||||
Section* GetSection(const char* section);
|
||||
std::string* GetLine(const char* section, const char* key);
|
||||
void CreateSection(const char* section);
|
||||
};
|
@ -397,6 +397,8 @@ void DrawBuffer::DrawText(int font, const char *text, float x, float y, Color co
|
||||
continue;
|
||||
}
|
||||
const AtlasChar *ch = atlasfont.getChar(cval);
|
||||
if (!ch)
|
||||
ch = atlasfont.getChar('?');
|
||||
if (ch) {
|
||||
const AtlasChar &c = *ch;
|
||||
float cx1, cy1, cx2, cy2;
|
||||
|
105
i18n/i18n.cpp
Normal file
105
i18n/i18n.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "i18n/i18n.h"
|
||||
#include "file/ini_file.h"
|
||||
|
||||
I18NRepo i18nrepo;
|
||||
|
||||
I18NRepo::~I18NRepo() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void I18NRepo::Clear() {
|
||||
for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
|
||||
delete iter->second;
|
||||
}
|
||||
cats_.clear();
|
||||
}
|
||||
|
||||
const char *I18NCategory::T(const char *key, const char *def) {
|
||||
auto iter = map_.find(key);
|
||||
if (iter != map_.end()) {
|
||||
return iter->second.text.c_str();
|
||||
} else {
|
||||
if (def)
|
||||
missedKeyLog_[key] = def;
|
||||
else
|
||||
missedKeyLog_[key] = key;
|
||||
return def ? def : key;
|
||||
}
|
||||
}
|
||||
|
||||
void I18NCategory::SetMap(const std::map<std::string, std::string> &m) {
|
||||
for (auto iter = m.begin(); iter != m.end(); ++iter) {
|
||||
if (map_.find(iter->first) == map_.end()) {
|
||||
map_[iter->first] = I18NEntry(iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I18NCategory *I18NRepo::GetCategory(const char *category) {
|
||||
auto iter = cats_.find(category);
|
||||
if (iter != cats_.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
I18NCategory *c = new I18NCategory(this);
|
||||
cats_[category] = c;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string I18NRepo::GetIniPath(const std::string &languageID) const {
|
||||
return "lang/" + languageID + ".ini";
|
||||
}
|
||||
|
||||
void I18NRepo::LoadIni(const std::string &languageID) {
|
||||
IniFile ini;
|
||||
if (!ini.Load(GetIniPath(languageID))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Clear();
|
||||
|
||||
const std::vector<IniFile::Section> §ions = ini.Sections();
|
||||
|
||||
for (auto iter = sections.begin(); iter != sections.end(); ++iter) {
|
||||
if (iter->name() != "") {
|
||||
cats_[iter->name()] = LoadSection(&(*iter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I18NCategory *I18NRepo::LoadSection(const IniFile::Section *section) {
|
||||
I18NCategory *cat = new I18NCategory(this);
|
||||
std::map<std::string, std::string> sectionMap = section->ToMap();
|
||||
cat->SetMap(sectionMap);
|
||||
return cat;
|
||||
}
|
||||
|
||||
// This is a very light touched save variant - it won't overwrite
|
||||
// anything, only create new entries.
|
||||
void I18NRepo::SaveIni(const std::string &languageID) {
|
||||
IniFile ini;
|
||||
ini.Load(GetIniPath(languageID));
|
||||
for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
|
||||
std::string categoryName = iter->first;
|
||||
IniFile::Section *section = ini.GetOrCreateSection(categoryName.c_str());
|
||||
SaveSection(ini, section, iter->second);
|
||||
}
|
||||
ini.Save(GetIniPath(languageID));
|
||||
}
|
||||
|
||||
void I18NRepo::SaveSection(IniFile &ini, IniFile::Section *section, I18NCategory *cat) {
|
||||
const std::map<std::string, std::string> &missed = cat->Missed();
|
||||
|
||||
for (auto iter = missed.begin(); iter != missed.end(); ++iter) {
|
||||
if (!section->Exists(iter->first.c_str())) {
|
||||
section->Set(iter->first, iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<std::string, I18NEntry> &entries = cat->GetMap();
|
||||
for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
|
||||
section->Set(iter->first, iter->second.text);
|
||||
}
|
||||
|
||||
cat->ClearMissed();
|
||||
}
|
95
i18n/i18n.h
Normal file
95
i18n/i18n.h
Normal file
@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
// I18N = I....18..dots.....N = INTERNATIONALIZATION
|
||||
|
||||
// Super simple I18N library.
|
||||
// Just enough to be useful and usable.
|
||||
// Spits out easy-to-edit utf-8 .INI files.
|
||||
|
||||
// As usual, everything is UTF-8. Nothing else allowed.
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/stringutil.h"
|
||||
#include "file/ini_file.h"
|
||||
|
||||
// Reasonably thread safe.
|
||||
|
||||
class I18NRepo;
|
||||
|
||||
struct I18NEntry {
|
||||
I18NEntry(const std::string &t) : text(t), readFlag(false) {}
|
||||
I18NEntry() : readFlag(false) {}
|
||||
std::string text;
|
||||
bool readFlag;
|
||||
};
|
||||
|
||||
struct I18NCandidate {
|
||||
I18NCandidate() : key(0), defVal(0) {}
|
||||
I18NCandidate(const char *k, const char *d) : key(k), defVal(d) {}
|
||||
const char *key;
|
||||
const char *defVal;
|
||||
};
|
||||
|
||||
class I18NCategory {
|
||||
public:
|
||||
const char *T(const char *key, const char *def = 0);
|
||||
|
||||
const std::map<std::string, std::string> &Missed() const {
|
||||
return missedKeyLog_;
|
||||
}
|
||||
|
||||
void SetMap(const std::map<std::string, std::string> &m);
|
||||
const std::map<std::string, I18NEntry> &GetMap() { return map_; }
|
||||
void ClearMissed() { missedKeyLog_.clear(); }
|
||||
|
||||
private:
|
||||
I18NCategory(I18NRepo *repo) {}
|
||||
|
||||
std::map<std::string, I18NEntry> map_;
|
||||
std::map<std::string, std::string> missedKeyLog_;
|
||||
|
||||
// Noone else can create these.
|
||||
friend class I18NRepo;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(I18NCategory);
|
||||
};
|
||||
|
||||
class I18NRepo {
|
||||
public:
|
||||
I18NRepo() {}
|
||||
~I18NRepo();
|
||||
|
||||
void LoadIni(const std::string &languageID); // NOT the filename!
|
||||
void SaveIni(const std::string &languageID);
|
||||
|
||||
I18NCategory *GetCategory(const char *categoryName);
|
||||
const char *T(const char *category, const char *key, const char *def = 0);
|
||||
|
||||
private:
|
||||
std::string GetIniPath(const std::string &languageID) const;
|
||||
void Clear();
|
||||
I18NCategory *LoadSection(const IniFile::Section *section);
|
||||
void SaveSection(IniFile &ini, IniFile::Section *section, I18NCategory *cat);
|
||||
|
||||
std::map<std::string, I18NCategory *> cats_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(I18NRepo);
|
||||
};
|
||||
|
||||
extern I18NRepo i18nrepo;
|
||||
|
||||
// These are simply talking to the one global instance of I18NRepo.
|
||||
|
||||
inline I18NCategory *GetI18NCategory(const char *categoryName) {
|
||||
return i18nrepo.GetCategory(categoryName);
|
||||
}
|
||||
|
||||
inline const char *T(const char *category, const char *key, const char *def = 0) {
|
||||
return i18nrepo.T(category, key, def);
|
||||
}
|
||||
|
||||
|
||||
|
@ -211,6 +211,7 @@
|
||||
<ClInclude Include="file\easy_file.h" />
|
||||
<ClInclude Include="file\fd_util.h" />
|
||||
<ClInclude Include="file\file_util.h" />
|
||||
<ClInclude Include="file\ini_file.h" />
|
||||
<ClInclude Include="file\vfs.h" />
|
||||
<ClInclude Include="file\zip_read.h" />
|
||||
<ClInclude Include="gfx\gl_debug_log.h" />
|
||||
@ -223,6 +224,7 @@
|
||||
<ClInclude Include="gfx_es2\glsl_program.h" />
|
||||
<ClInclude Include="gfx_es2\gl_state.h" />
|
||||
<ClInclude Include="gfx_es2\vertex_format.h" />
|
||||
<ClInclude Include="i18n\i18n.h" />
|
||||
<ClInclude Include="image\png_load.h" />
|
||||
<ClInclude Include="image\zim_load.h" />
|
||||
<ClInclude Include="image\zim_save.h" />
|
||||
@ -305,6 +307,7 @@
|
||||
<ClCompile Include="file\easy_file.cpp" />
|
||||
<ClCompile Include="file\fd_util.cpp" />
|
||||
<ClCompile Include="file\file_util.cpp" />
|
||||
<ClCompile Include="file\ini_file.cpp" />
|
||||
<ClCompile Include="file\zip_read.cpp" />
|
||||
<ClCompile Include="gfx\gl_debug_log.cpp" />
|
||||
<ClCompile Include="gfx\gl_lost_manager.cpp" />
|
||||
@ -316,6 +319,7 @@
|
||||
<ClCompile Include="gfx_es2\glsl_program.cpp" />
|
||||
<ClCompile Include="gfx_es2\gl_state.cpp" />
|
||||
<ClCompile Include="gfx_es2\vertex_format.cpp" />
|
||||
<ClCompile Include="i18n\i18n.cpp" />
|
||||
<ClCompile Include="image\png_load.cpp" />
|
||||
<ClCompile Include="image\zim_load.cpp" />
|
||||
<ClCompile Include="image\zim_save.cpp" />
|
||||
|
@ -245,6 +245,12 @@
|
||||
<ClInclude Include="thread\threadutil.h">
|
||||
<Filter>thread</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="i18n\i18n.h">
|
||||
<Filter>i18n</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="file\ini_file.h">
|
||||
<Filter>file</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="gfx\gl_debug_log.cpp">
|
||||
@ -432,6 +438,12 @@
|
||||
<ClCompile Include="thread\threadutil.cpp">
|
||||
<Filter>thread</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="i18n\i18n.cpp">
|
||||
<Filter>i18n</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="file\ini_file.cpp">
|
||||
<Filter>file</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="gfx">
|
||||
@ -494,5 +506,8 @@
|
||||
<Filter Include="thread">
|
||||
<UniqueIdentifier>{caa41117-1d90-47cc-9fba-f7e670e315a3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="i18n">
|
||||
<UniqueIdentifier>{02e8ef95-82c7-4420-b029-a189a5e0fcbd}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -286,9 +286,13 @@ int UITextureButton(UIContext *ctx, int id, const LayoutManager &layout, float w
|
||||
}
|
||||
|
||||
// Render button
|
||||
const int dropsize = 10;
|
||||
int dropsize = 10;
|
||||
if (drop_shadow && texture)
|
||||
{
|
||||
if (txOffset) {
|
||||
dropsize = 3;
|
||||
y += txOffset * 2;
|
||||
}
|
||||
ui_draw2d.DrawImage4Grid(drop_shadow, x - dropsize, y, x+w + dropsize, y+h+dropsize*1.5,
|
||||
alphaMul(color,0.5f), 1.0f);
|
||||
ui_draw2d.Flush(true);
|
||||
|
Loading…
Reference in New Issue
Block a user