1
0
forked from 0ad/0ad

Initial i18n integration

This was SVN commit r1029.
This commit is contained in:
Ykkrosh 2004-08-21 11:45:01 +00:00
parent b981542a51
commit 0e55379b36
27 changed files with 2043 additions and 123 deletions

View File

@ -169,6 +169,6 @@ sub cpp_files {
opendir my $d, $_[0] or die "Error opening directory '$_[0]' ($!)";
my @f = readdir $d;
my @files = map "$_[0]/$_", grep /\.(?:cpp|h)$/, @f;
push @files, cpp_files("$_[0]/$_") for grep { /^[a-zA-Z0-9]+$/ and -d "$_[0]/$_" } @f;
push @files, cpp_files("$_[0]/$_") for grep { !/^(?:workspaces|tools|ape|sced)$/ and /^[a-zA-Z0-9]+$/ and -d "$_[0]/$_" } @f;
return @files;
}

133
source/i18n/BufferVariable.cpp Executable file
View File

@ -0,0 +1,133 @@
#include "precompiled.h"
#include "BufferVariable.h"
#include "DataTypes.h"
#include "CLocale.h"
// Hashing functions are currently 32-bit only
cassert(sizeof(int) == 4);
cassert(sizeof(double) == 8);
using namespace I18n;
namespace I18n {
// These bits don't seem to work without the explicit namespace{}
BufferVariable* NewBufferVariable(int v) { return new BufferVariable_int(v); }
BufferVariable* NewBufferVariable(double v) { return new BufferVariable_double(v); }
BufferVariable* NewBufferVariable(const wchar_t* v) { return new BufferVariable_string(v); }
BufferVariable* NewBufferVariable(const char* v) { return new BufferVariable_string(v); }
BufferVariable* NewBufferVariable(I18n::Name v) { return new BufferVariable_rawstring(v.value); }
}
StrImW BufferVariable_int::ToString(CLocale*)
{
wchar_t buffer[16];
swprintf(buffer, 16, L"%d", value);
return buffer;
}
u32 BufferVariable_int::Hash()
{
return (u32)value;
}
StrImW BufferVariable_double::ToString(CLocale*)
{
// TODO: Work out how big the buffer should be
wchar_t buffer[256];
swprintf(buffer, 256, L"%f", value);
return buffer;
}
u32 BufferVariable_double::Hash()
{
// Add the two four-bytes of the double
return *((u32*)&value) + *((u32*)&value + 1);
}
StrImW BufferVariable_string::ToString(CLocale* locale)
{
// Attempt noun translations
const Str nouns = L"nouns";
const Str wordname (value.str());
const CLocale::LookupType* word = locale->LookupWord(nouns, wordname);
// Couldn't find; just return the original word
if (! word)
return value;
const Str propname = L"singular";
Str ret;
if (! locale->LookupProperty(word, propname, ret))
{
delete word;
return value;
}
delete word;
return ret.c_str();
}
u32 BufferVariable_string::Hash()
{
// Do nothing very clever
int hash = 0;
const wchar_t* str = value.str();
size_t len = wcslen(str);
for (size_t i = 0; i < len; ++i)
hash += str[i];
return hash;
}
StrImW BufferVariable_rawstring::ToString(CLocale*)
{
return value;
}
u32 BufferVariable_rawstring::Hash()
{
int hash = 0;
const wchar_t* str = value.str();
size_t len = wcslen(str);
for (size_t i = 0; i < len; ++i)
hash += str[i];
return hash;
}
bool I18n::operator== (BufferVariable& a, BufferVariable& b)
{
if (a.Type != b.Type)
return false;
switch (a.Type)
{
case vartype_int:
return *static_cast<BufferVariable_int*>(&a) == *static_cast<BufferVariable_int*>(&b);
case vartype_double:
return *static_cast<BufferVariable_double*>(&a) == *static_cast<BufferVariable_double*>(&b);
case vartype_string:
return *static_cast<BufferVariable_string*>(&a) == *static_cast<BufferVariable_string*>(&b);
case vartype_rawstring:
return *static_cast<BufferVariable_rawstring*>(&a) == *static_cast<BufferVariable_rawstring*>(&b);
}
debug_warn("Invalid buffer variable vartype");
return false;
}
bool BufferVariable_int::operator== (BufferVariable_int& a)
{
return a.value == this->value;
}
bool BufferVariable_double::operator== (BufferVariable_double& a)
{
return a.value == this->value;
}
bool BufferVariable_string::operator== (BufferVariable_string& a)
{
return a.value == this->value;
}
bool BufferVariable_rawstring::operator== (BufferVariable_rawstring& a)
{
return a.value == this->value;
}

87
source/i18n/BufferVariable.h Executable file
View File

@ -0,0 +1,87 @@
/*
BufferVariable stores the parameters that have been passed to a StringBuffer,
providing hashes and strings on request.
*/
#ifndef I18N_BUFFERVARIABLE_H
#define I18N_BUFFERVARIABLE_H
#include "Common.h"
#include "StrImmutable.h"
#include <assert.h>
namespace I18n
{
class CLocale;
enum {
vartype_int,
vartype_double,
vartype_string,
vartype_rawstring, // won't be translated automatically
};
class BufferVariable
{
public:
char Type;
virtual StrImW ToString(CLocale*) = 0;
virtual u32 Hash() = 0;
virtual ~BufferVariable() {};
};
// Factory constructor type thing, sort of
template<typename T>
BufferVariable* NewBufferVariable(T v);
class BufferVariable_int : public BufferVariable
{
public:
int value;
BufferVariable_int(int v) : value(v) { Type = vartype_int; }
StrImW ToString(CLocale*);
u32 Hash();
// Equality testing is required by the cache
bool operator== (BufferVariable_int&);
};
class BufferVariable_double : public BufferVariable
{
public:
double value;
BufferVariable_double(double v) : value(v) { Type = vartype_double; }
StrImW ToString(CLocale*);
u32 Hash();
bool operator== (BufferVariable_double&);
};
class BufferVariable_string : public BufferVariable
{
public:
StrImW value;
BufferVariable_string(StrImW v) : value(v) { Type = vartype_string; }
StrImW ToString(CLocale*);
u32 Hash();
bool operator== (BufferVariable_string&);
};
class BufferVariable_rawstring : public BufferVariable
{
public:
StrImW value;
BufferVariable_rawstring(StrImW v) : value(v) { Type = vartype_rawstring; }
StrImW ToString(CLocale*);
u32 Hash();
bool operator== (BufferVariable_rawstring&);
};
// BufferVariable==BufferVariable compares their Types, and then
// uses the appropriate operator== if they're the same
bool operator== (BufferVariable&, BufferVariable&);
}
#endif // I18N_BUFFERVARIABLE_H

431
source/i18n/CLocale.cpp Executable file
View File

@ -0,0 +1,431 @@
#include "precompiled.h"
#include "CLocale.h"
#include "TSComponent.h"
#include "StringConvert.h"
#include <assert.h>
#include <algorithm>
#include "ps/CLogger.h"
#define LOG_CATEGORY "i18n"
using namespace I18n;
// Vaguely useful utility function for deleting stuff
template<typename T> void delete_fn(T* v) { delete v; }
// These could be optimised for little-endian sizeof(wchar_t)==2 systems:
static inline void ReadWString8_(const char*& data, Str& str)
{
u8 length = *(u8*)data;
data += 1;
StringConvert::ucs2le_to_wstring(data, data+length, str);
data += length;
}
static inline void ReadWString16_(const char*& data, Str& str)
{
u16 length = *(u16*)data;
data += 2;
StringConvert::ucs2le_to_wstring(data, data+length, str);
data += length;
}
#define ReadWString8(s) Str s; ReadWString8_(data, s);
#define ReadWString16(s) Str s; ReadWString16_(data, s);
bool CLocale::LoadStrings(const char* data)
{
// TODO: More robust file format (so errors can be detected in a
// nicer way than watching for access violations)
u16 PhraseCount = *(u16*)data;
data += 2;
for (int i = 0; i < PhraseCount; ++i)
{
ReadWString16(Key);
u8 VarCount = *(u8*)data;
data += 1;
// Get the relevant entry in the string hash, creating it if it doesn't exist
TranslatedString* String = Strings[Key];
if (! String)
String = Strings[Key] = new TranslatedString;
// If this is a redefined string, make sure it's empty
String->Parts.clear();
// Store the number of variables, so translate(x)<<y<<z can check for validity
String->VarCount = VarCount;
u8 SectionCount = *(u8*)data;
data += 1;
for (int j = 0; j < SectionCount; ++j)
{
u8 SectionType = *(u8*)data;
data += 1;
switch (SectionType)
{
case 0: // Constant string
{
ReadWString16(StringText);
String->Parts.push_back(new TSComponentString(StringText.c_str()));
break;
}
case 1: // Variable
{
u8 VarID = *(u8*)data;
data += 1;
String->Parts.push_back(new TSComponentVariable(VarID));
break;
}
case 2: // Function
{
u8 NameLength = *(u8*)data;
data += 1;
std::string NameText ((char*)data, (char*)(data + NameLength));
data += NameLength;
u8 ParamCount = *(u8*)data;
data += 1;
TSComponentFunction* Func = new TSComponentFunction(NameText.c_str());
for (int k = 0; k < ParamCount; ++k)
{
u8 ParamType = *(u8*)data;
data += 1;
switch (ParamType)
{
case 0: // String
{
ReadWString8(StrText);
Func->AddParam(new ScriptValueString(Script, StrText.c_str()));
break;
}
case 1: // Variable
{
u8 ID = *(u8*)data;
data += 1;
Func->AddParam(new ScriptValueVariable(Script, ID));
break;
}
case 2: // Integer
{
u32 Num = *(u32*)data;
data += 4;
Func->AddParam(new ScriptValueInteger(Script, Num));
break;
}
default: // Argh!
debug_warn("Invalid function parameter type");
}
}
String->Parts.push_back(Func);
break;
}
default: // Argh!
debug_warn("Invalid translation string section type");
}
}
}
return true;
}
bool CLocale::LoadFunctions(const char* data, size_t len, const char* filename)
{
// Insist on little-endian UTF16 files containing a BOM (e.g. as generated
// by Notepad when saving in Unicode format)
// TODO: Support more Unicode file formats
if (len < 2)
{
LOG(ERROR, LOG_CATEGORY, "I18n: Functions file '%s' is too short", filename);
return false;
}
if (*(jschar*)data != 0xFEFF)
{
LOG(ERROR, LOG_CATEGORY, "I18n: Functions file '%s' has invalid Unicode format (lacking little-endian BOM)", filename);
return false;
}
if (! Script.ExecuteCode((jschar*)(data+2), len/2, filename))
{
LOG(ERROR, LOG_CATEGORY, "I18n: JS errors in functions file '%s'", filename);
return false;
}
return true;
}
bool CLocale::LoadDictionary(const char* data, const wchar_t* name)
{
u8 PropertyCount = *(u8*)data;
data += 1;
DictData& dict = Dictionaries[name];
// Read the names of the properties
for (int i = 0; i < PropertyCount; ++i)
{
ReadWString8(Property);
dict.DictProperties[Property] = i;
}
u16 ValueCount = *(u16*)data;
data += 2;
// Read each 'value' (word + properties)
for (int i = 0; i < ValueCount; ++i)
{
ReadWString8(Word);
std::vector<std::wstring>& props = dict.DictWords[Word];
for (int j = 0; j < PropertyCount; ++j)
{
ReadWString8(Value);
props.push_back(Value);
}
}
return true;
}
const CLocale::LookupType* CLocale::LookupWord(const Str& dictname, const Str& word)
{
std::map<Str, DictData>::const_iterator dictit = Dictionaries.find(dictname);
if (dictit == Dictionaries.end())
{
assert(! "invalid dictionary");
return NULL;
}
std::map<Str, std::vector<Str> >::const_iterator wordit = dictit->second.DictWords.find(word);
if (wordit == dictit->second.DictWords.end())
{
// Word not found. Respond quietly, so JS code can handle missing
// words in a more appropriate way.
return NULL;
}
// Return some data that can later be passed to LookupProperty
return new LookupType(&dictit->second, &wordit->second);
}
bool CLocale::LookupProperty(const LookupType* data, const Str& property, Str& result)
{
std::map<Str, int>::const_iterator propit = data->first->DictProperties.find(property);
if (propit == data->first->DictProperties.end())
return false;
// Return the appropriate string
result = (*data->second)[propit->second];
return true;
}
const StrImW CLocale::CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params)
{
return Script.CallFunction(name, vars, params);
}
StringBuffer CLocale::Translate(const wchar_t* id)
{
if (++CacheAge > CacheAgeLimit)
{
CacheAge = 0;
ClearCache();
}
StringsType::iterator TransStr = Strings.find(Str(id));
if (TransStr == Strings.end())
{
LOG(WARNING, LOG_CATEGORY, "I18n: No translation found for string '%ls'", id);
// Just use the ID string directly, and remember it for the future
return StringBuffer(AddDefaultString(id), this);
}
return StringBuffer(*(*TransStr).second, this);
}
void CLocale::AddToCache(StringBuffer* sb, Str& str)
{
CacheData& d = TranslationCache[&sb->String];
// Clean up any earlier cache entry
std::for_each(d.vars.begin(), d.vars.end(), delete_fn<BufferVariable>);
// Set the data for the new cache entry
d.hash = sb->Hash();
d.vars = sb->Variables;
d.output = str;
}
bool CLocale::ReadCached(StringBuffer* sb, Str& str)
{
// Look for a string with the right key in the cache
std::map<TranslatedString*, CacheData>::iterator it =
TranslationCache.find(&sb->String);
// See if it actually exists
if (it == TranslationCache.end())
return false;
// Check quickly whether the hashes match
if (sb->Hash() != (*it).second.hash)
return false;
// Check every variable to see whether they're identical
assert(sb->Variables.size() == (*it).second.vars.size()); // this should always be true
size_t count = sb->Variables.size();
for (size_t i = 0; i < count; ++i)
if (! ( *sb->Variables[i] == *(*it).second.vars[i] ) )
return false;
str = (*it).second.output;
return true;
}
void CLocale::ClearCache()
{
// Deallocate cached data
for (std::map<TranslatedString*, CacheData>::iterator it = TranslationCache.begin(); it != TranslationCache.end(); ++it)
std::for_each((*it).second.vars.begin(), (*it).second.vars.end(), delete_fn<BufferVariable>);
TranslationCache.clear();
}
bool is_valid_variable_char(wchar_t c)
{
// c =~ /[a-zA-Z0-9_]/
// (Hurrah for internationalisation.)
return
(c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| (c == '_');
}
TranslatedString& CLocale::AddDefaultString(const wchar_t* id)
{
// Parse a string involving $variables and $$ (=$)
enum ParseState {
st_default,
st_afterdollar,
st_variable
};
ParseState state = st_default;
TranslatedString* str = new TranslatedString;
str->VarCount = 0;
std::wstring tempstr;
for (const wchar_t* ch = id; *ch != '\0'; ++ch)
{
switch (state)
{
case st_default:
if (*ch == '$')
{
state = st_afterdollar;
}
else
{
tempstr += *ch;
}
break;
case st_afterdollar:
if (*ch == '$')
{
tempstr += '$';
state = st_default;
}
else
{
// Start of a variable name.
// Push the old string onto the component stack
if (tempstr.length())
{
str->Parts.push_back(new TSComponentString(tempstr.c_str()));
tempstr.clear();
}
// Set the ID (starting at 0) and increment the count
str->Parts.push_back(new TSComponentVariable(str->VarCount++));
state = st_variable;
}
break;
case st_variable:
if (*ch == '$')
{
state = st_afterdollar;
}
else if (! is_valid_variable_char(*ch))
{
state = st_default;
tempstr = *ch;
}
// We don't care about the actual name of the variable, so just ignore it.
break;
}
}
// Make sure the last string is added to the parts list
if (tempstr.length())
str->Parts.push_back(new TSComponentString(tempstr.c_str()));
Strings[id] = str;
return *str;
}
CLocale::~CLocale()
{
// Clean up the list of strings
for (StringsType::iterator it = Strings.begin(); it != Strings.end(); ++it)
delete (*it).second;
ClearCache();
}

106
source/i18n/CLocale.h Executable file
View File

@ -0,0 +1,106 @@
/*
CLocale contains all the data about a locale (/language).
LoadFunctions/LoadStrings/LoadDictionary are used to specify data to be used
when translating, and Translate is used to perform translations.
All other methods are used internally by other I18n components.
*/
#ifndef I18N_CLOCALE_H
#define I18N_CLOCALE_H
#include "Common.h"
#include "Interface.h"
#include "StringBuffer.h"
#include "StrImmutable.h"
#include "ScriptInterface.h"
#include "lib/sysdep/sysdep.h" // STL_HASH_MAP
#include <map>
#include <algorithm>
struct JSContext;
namespace I18n
{
class CLocale : public CLocale_interface
{
friend StringBuffer;
private:
typedef STL_HASH_MAP<Str, TranslatedString*> StringsType;
public:
StringBuffer Translate(const wchar_t* id);
bool LoadFunctions(const char* filedata, size_t len, const char* filename);
bool LoadStrings(const char* filedata);
bool LoadDictionary(const char* filedata, const wchar_t* name);
const StrImW CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params);
private:
struct DictData;
public:
typedef std::pair<const DictData*, const std::vector<Str>*> LookupType;
// Returns a new'ed structure. Please remember to delete it.
const LookupType* LookupWord(const Str& dictname, const Str& word);
bool LookupProperty(const LookupType* data, const Str& property, Str& result);
// Disable the "'this' used in base member initializer list" warning: only the
// pointer (and not the data it points to) is accessed by ScriptObject's
// constructor, so it shouldn't cause any problems.
#ifdef _MSC_VER
# pragma warning (disable: 4355)
#endif
CLocale(JSContext* context) : Script(this, context), CacheAge(0) {}
#ifdef _MSC_VER
# pragma warning (default: 4355)
#endif
~CLocale();
void AddToCache(StringBuffer*, Str&);
bool ReadCached(StringBuffer*, Str&);
void ClearCache();
private:
TranslatedString& AddDefaultString(const wchar_t* id);
StringsType Strings;
// Incremented by every call to Translate(), with
// ClearCache() being run after a certain number of calls.
// TODO: Replace with a better caching system.
int CacheAge;
static const int CacheAgeLimit = 1024;
struct CacheData
{
u32 hash;
std::vector<BufferVariable*> vars;
Str output;
};
std::map<TranslatedString*, CacheData> TranslationCache;
struct DictData
{
std::map< Str /* property name */, int /* property id */ > DictProperties;
std::map< Str /* word */, std::vector<Str> /* properties */ > DictWords;
};
std::map<Str /* dictionary name */, DictData> Dictionaries;
ScriptObject Script;
};
}
#endif // I18N_CLOCALE_H

20
source/i18n/Common.h Executable file
View File

@ -0,0 +1,20 @@
// Things that are used by most I18n code:
#ifndef I18N_COMMON_H
#define I18N_COMMON_H
#include <string>
#include "Errors.h"
namespace I18n
{
// Define an 'internal' string type, for no particular reason
typedef std::wstring Str;
}
ERROR_GROUP(I18n);
// That was exciting.
#endif // I18N_COMMON_H

19
source/i18n/DataTypes.h Executable file
View File

@ -0,0 +1,19 @@
#ifndef I18N_DATATYPES_H
#define I18N_DATATYPES_H
#include "StrImmutable.h"
namespace I18n
{
// Allow translate("Hello $you")<<I18n::Name(playername), which
// won't attempt to automatically translate the player's name.
// Templated to allow char* and wchar_t*
struct Name
{
template<typename T> Name(T d) : value(d) {}
StrImW value;
};
}
#endif // I18N_DATATYPES_H

76
source/i18n/Interface.cpp Executable file
View File

@ -0,0 +1,76 @@
/*
Vague overview of the i18n code:
(TODO: Improve the documentation)
CLocale stores all locale-specific (locale==language, roughly) data.
Usually there's only going to be one in existence.
A language needs to define:
* String files that define translations for phrases.
* Dictionary files that translate individual words (usually names of objects),
including extra information that the grammar requires (whether to use 'a' or
'an' in English, gender in lots of European languages, etc)
* .js files containing functions that apply grammatical rules
(e.g. choosing singular vs plural depending on a number)
CLocale::LoadStrings / LoadDictionary / LoadFunctions are used to input the
data from the appropriate files. Call multiple times if desired.
CLocale::Translate is the primary interface. Pass it a unique identifier
string, and it'll read the appropriate translated data from the
loaded data files. A StringBuffer is returned.
To allow variables embedded in text, StringBuffer::operator<< is used
in a similar way to in cout, storing the variable in the StringBuffer and
returning the StringBuffer again.
StringBuffer::operator Str() does the final insertion of variables into
the translated phrase, either grabbing the final result from a cache or
doing all the variable-to-string conversions.
The strings read from disk are each stored as a TranslatedString,
containing several TSComponents, which are either static strings or
default-formatted variables or functions.
TSComponentFunction is the most complex of the TSComponents, containing a
list of ScriptValues -- these allow numbers, strings and variables to be
passed as parameters into a JS function.
StringBuffer::operator<< stores BufferVariable*s in the StringBuffer.
These provide access to a hash of the variable (for caching), and can
be converted into a string (for display).
StrImW is used in various places, just as a more efficient alternative
to std::wstring.
*/
#include "precompiled.h"
#include "Interface.h"
#include "CLocale.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY "i18n"
using namespace I18n;
struct JSContext;
CLocale_interface* I18n::NewLocale(JSContext* cx)
{
try
{
return new CLocale(cx);
}
catch (PSERROR_I18n& e)
{
LOG(ERROR, LOG_CATEGORY, "Error creating locale object ('%ls')", GetErrorString(e.code));
return NULL;
}
}

43
source/i18n/Interface.h Executable file
View File

@ -0,0 +1,43 @@
/*
The only file that external code should need to include.
*/
#ifndef I18N_INTERFACE_H
#define I18N_INTERFACE_H
#include "StringBuffer.h"
#include "DataTypes.h"
struct JSContext;
namespace I18n
{
// Use an interface class, so minimal headers are required by
// anybody who only wants to make use of Translate()
class CLocale_interface
{
public:
virtual StringBuffer Translate(const wchar_t* id) = 0;
// Load* functions return true for success, false for failure
// Pass the contents of a UTF-16LE BOMmed .js file.
// The filename is just used to give more useful error messages from JS.
virtual bool LoadFunctions(const char* filedata, size_t len, const char* filename) = 0;
// Desires a .lng file, as produced by convert.pl
virtual bool LoadStrings(const char* filedata) = 0;
// Needs .wrd files generated through tables.pl
virtual bool LoadDictionary(const char* filedata, const wchar_t* name) = 0;
virtual ~CLocale_interface() {}
};
// Build a CLocale. Returns NULL on failure.
CLocale_interface* NewLocale(JSContext* cx);
}
#endif // I18N_INTERFACE_H

345
source/i18n/ScriptInterface.cpp Executable file
View File

@ -0,0 +1,345 @@
#include "precompiled.h"
#include "ScriptInterface.h"
#include "CLocale.h"
#include "StringConvert.h"
#include "jsapi.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY "i18n"
using namespace I18n;
namespace LookedupWord {
JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj);
assert(lookedup);
JSObject* parent = JS_GetParent(cx, obj);
assert(parent);
CLocale* locale = (CLocale*)JS_GetPrivate(cx, parent);
assert(locale);
JSString* prop = JS_ValueToString(cx, id);
if (!prop)
{
JS_ReportError(cx, "lookup() property failed to convert to string");
return JS_FALSE;
}
jschar* prop_chars = JS_GetStringChars(prop);
if (!prop_chars)
{
JS_ReportError(cx, "lookup() property failed to convert string to chars");
return JS_FALSE;
}
Str prop_str;
StringConvert::jschars_to_wstring(prop_chars, JS_GetStringLength(prop), prop_str);
Str result;
if (! locale->LookupProperty(lookedup, prop_str, result))
result = L"(unrecognised property)";
JSString* result_str = StringConvert::wstring_to_jsstring(cx, result);
if (!result_str)
{
JS_ReportError(cx, "lookup() property failed to create string");
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(result_str);
return JS_TRUE;
}
void Finalize(JSContext *cx, JSObject *obj)
{
// Free the LookupType that was allocated when building this object
CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj);
assert(lookedup);
delete lookedup;
}
static JSClass JSI_class = {
"LookedupWord", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
GetProperty, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, Finalize,
NULL, NULL, NULL, NULL
};
}
JSBool JSFunc_LookupWord(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if (argc != 2)
{
JS_ReportError(cx, "Incorrect number of parameters to lookup(dictionary, word) function");
return JS_FALSE;
}
// Get the strings
JSString* dictname = JS_ValueToString(cx, argv[0]);
JSString* word = JS_ValueToString(cx, argv[1]);
if (!dictname || !word)
{
JS_ReportError(cx, "lookup() failed to convert parameters to strings");
return JS_FALSE;
}
// and the characters from the strings
jschar* dictname_chars = JS_GetStringChars(dictname);
jschar* word_chars = JS_GetStringChars(word);
if (!dictname_chars || !word_chars)
{
JS_ReportError(cx, "lookup() failed to get parameter string data");
return JS_FALSE;
}
// and convert those characters into to wstrings
Str dictname_str, word_str;
StringConvert::jschars_to_wstring(dictname_chars, JS_GetStringLength(dictname), dictname_str);
StringConvert::jschars_to_wstring(word_chars, JS_GetStringLength(word), word_str);
// Extract the CLocale* from the 'global' object
CLocale* locale = (CLocale*)JS_GetPrivate(cx, obj);
const CLocale::LookupType* lookedup = locale->LookupWord(dictname_str, word_str);
if (! lookedup)
{
// Couldn't find the word in the table
*rval = JSVAL_NULL;
return JS_TRUE;
}
// Create an object to be returned, containing enough data to access properties of the found word
JSObject* wordobj = JS_NewObject(cx, &LookedupWord::JSI_class, NULL, obj);
if (!wordobj)
{
JS_ReportError(cx, "lookup() failed to create object");
return JS_FALSE;
}
// Associate the looked-up word data with the JS object
JS_SetPrivate(cx, wordobj, (void*)lookedup);
*rval = OBJECT_TO_JSVAL(wordobj);
return JS_TRUE;
}
static JSFunctionSpec JSFunc_list[] = {
{"lookup", JSFunc_LookupWord, 2, 0, 0},
{0,0,0,0,0},
};
static JSClass JSI_class_scriptglobal = {
"", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, NULL
};
ScriptObject::ScriptObject(CLocale* locale, JSContext* cx)
: Context(cx)
{
// This should, in theory, never fail
Object = JS_NewObject(Context, &JSI_class_scriptglobal, NULL, NULL);
if (! Object)
{
debug_warn("Object creation failed");
throw PSERROR_I18n_Script_SetupFailed();
}
JS_AddRoot(Context, &Object);
// Register the 'global' functions
if (! JS_DefineFunctions(Context, Object, JSFunc_list))
{
debug_warn("JS_DefineFunctions failed");
throw PSERROR_I18n_Script_SetupFailed();
}
// Store the CLocale* ins in the script-object's private area
JS_SetPrivate(Context, Object, locale);
}
ScriptObject::~ScriptObject()
{
JS_RemoveRoot(Context, &Object);
}
bool ScriptObject::ExecuteCode(const jschar* data, size_t len, const char* filename)
{
jsval ret;
return (JS_EvaluateUCScript(Context, Object, data, (int)len-1, filename, 1, &ret) ? true : false);
// use len-1 to stop JS crashing. *shrug* ^^^^^
}
const StrImW ScriptObject::CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params)
{
int argc = (int)params.size();
// Construct argv, converting each parameter into a jsval
jsval* argv = new jsval[argc];
for (int i = 0; i < argc; ++i)
argv[i] = params[i]->GetJsval(vars);
jsval rval;
bool called = JS_CallFunctionName(Context, Object, name, argc, argv, &rval) ? true : false;
delete[] argv;
if (! called)
{
LOG(ERROR, LOG_CATEGORY, "I18n: Error executing JS function '%s'", name);
return L"(JS error)";
}
// Convert rval to a string and return
JSString* str = JS_ValueToString(Context, rval);
if (! str)
{
debug_warn("Conversion to string failed");
return L"(JS error)";
}
jschar* chars = JS_GetStringChars(str);
if (! chars)
{
debug_warn("GetStringChars failed");
return L"(JS error)";
}
return StrImW(chars, JS_GetStringLength(str));
}
ScriptValueString::ScriptValueString(ScriptObject& script, const wchar_t* s)
{
Context = script.Context;
JSString* str = StringConvert::wchars_to_jsstring(Context, s);
if (!str)
{
debug_warn("Error creating JS string");
Value = JSVAL_NULL;
}
else
{
JS_AddRoot(Context, str);
Value = STRING_TO_JSVAL(str);
}
}
jsval ScriptValueString::GetJsval(const std::vector<BufferVariable*>& vars)
{
return Value;
}
ScriptValueString::~ScriptValueString()
{
if (! JSVAL_IS_NULL(Value))
{
JSString* str = JSVAL_TO_STRING(Value);
JS_RemoveRoot(Context, str);
}
}
/************/
ScriptValueInteger::ScriptValueInteger(ScriptObject& script, const int v)
{
Context = script.Context;
Value = INT_TO_JSVAL(v);
}
jsval ScriptValueInteger::GetJsval(const std::vector<BufferVariable*>& vars)
{
return Value;
}
ScriptValueInteger::~ScriptValueInteger()
{
}
/************/
ScriptValueVariable::ScriptValueVariable(ScriptObject& script, const unsigned char id)
{
Context = script.Context;
ID = id;
GCVal = NULL;
}
jsval ScriptValueVariable::GetJsval(const std::vector<BufferVariable*>& vars)
{
// Clean up from earlier invocations
if (GCVal)
{
JS_RemoveRoot(Context, GCVal);
GCVal = NULL;
}
switch (vars[ID]->Type)
{
case vartype_int:
{
int val = ((BufferVariable_int*)vars[ID])->value;
return INT_TO_JSVAL(val);
}
case vartype_double:
{
jsdouble* val = JS_NewDouble(Context, ((BufferVariable_double*)vars[ID])->value);
if (!val)
{
debug_warn("Error creating JS double");
return JSVAL_NULL;
}
GCVal = (void*)val;
JS_AddRoot(Context, val);
return DOUBLE_TO_JSVAL(val);
}
case vartype_string:
{
JSString* val = StringConvert::wchars_to_jsstring(Context, ((BufferVariable_string*)vars[ID])->value.str());
if (!val)
{
debug_warn("Error creating JS string");
return JSVAL_NULL;
}
GCVal = (void*)val;
JS_AddRoot(Context, val);
return STRING_TO_JSVAL(val);
}
case vartype_rawstring:
{
JSString* val = StringConvert::wchars_to_jsstring(Context, ((BufferVariable_rawstring*)vars[ID])->value.str());
if (!val)
{
debug_warn("Error creating JS string");
return JSVAL_NULL;
}
GCVal = (void*)val;
JS_AddRoot(Context, val);
return STRING_TO_JSVAL(val);
}
default:
debug_warn("Invalid type");
return JSVAL_NULL;
}
}
ScriptValueVariable::~ScriptValueVariable()
{
if (GCVal)
JS_RemoveRoot(Context, GCVal);
}

94
source/i18n/ScriptInterface.h Executable file
View File

@ -0,0 +1,94 @@
/*
ScriptObject provides an interface to the JavaScript engine.
ScriptValues are used to generate jsvals from 'constants' (strings, ints)
and 'variables' (dependent on the type passed with "translate(...) <<").
*/
#ifndef I18N_SCRIPTINTERFACE_H
#define I18N_SCRIPTINTERFACE_H
#include "Common.h"
#include "BufferVariable.h"
#include <vector>
ERROR_SUBGROUP(I18n, Script);
ERROR_TYPE(I18n_Script, SetupFailed);
typedef _W64 long jsval;
typedef unsigned short jschar;
struct JSContext;
struct JSObject;
namespace I18n
{
class ScriptValue;
class CLocale;
class ScriptObject
{
public:
ScriptObject(CLocale* locale, JSContext* cx);
~ScriptObject();
bool ExecuteCode(const jschar* data, size_t len, const char* filename);
const StrImW CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params);
JSContext* Context;
JSObject* Object;
};
// Virtual base class for script values
class ScriptValue
{
public:
virtual jsval GetJsval(const std::vector<BufferVariable*>& vars) = 0;
virtual ~ScriptValue() {};
protected:
JSContext* Context;
};
// Particular types of script value:
class ScriptValueString : public ScriptValue
{
public:
ScriptValueString(ScriptObject& script, const wchar_t* s);
~ScriptValueString();
jsval GetJsval(const std::vector<BufferVariable*>& vars);
private:
jsval Value;
};
class ScriptValueInteger : public ScriptValue
{
public:
ScriptValueInteger(ScriptObject& script, const int v);
~ScriptValueInteger();
jsval GetJsval(const std::vector<BufferVariable*>& vars);
private:
jsval Value;
};
class ScriptValueVariable : public ScriptValue
{
public:
ScriptValueVariable(ScriptObject& script, const unsigned char id);
~ScriptValueVariable();
jsval GetJsval(const std::vector<BufferVariable*>& vars);
private:
unsigned char ID;
void* GCVal; // something that's being garbage-collected
};
}
#endif // I18N_SCRIPTINTERFACE_H

104
source/i18n/StrImmutable.h Executable file
View File

@ -0,0 +1,104 @@
/*
A reference-counted immutable string; more efficient than std::wstring
when returning strings from functions. (My i18n test program went ~20% faster,
which is not insignificant).
*/
#ifndef I18N_STRIMMUTABLE_H
#define I18N_STRIMMUTABLE_H
#include <wchar.h>
#include <string.h>
typedef unsigned short jschar;
namespace I18n
{
struct strImW_data
{
int refs;
wchar_t* data;
strImW_data() : refs(1) {}
};
class StrImW
{
public:
// I'm lazy (elsewhere), so allow construction from a variety
// of data types:
const StrImW(const wchar_t* s)
{
ref = new strImW_data;
size_t len = wcslen(s)+1;
ref->data = new wchar_t[len];
memcpy((void*)ref->data, s, len*sizeof(wchar_t));
}
const StrImW(const char* s)
{
ref = new strImW_data;
size_t len = strlen(s)+1;
ref->data = new wchar_t[len];
for (size_t i=0; i<len; ++i)
ref->data[i] = s[i];
}
// On non-MSVC, or on MSVC with a native wchar_t type, define jschar separately
#if !defined(_MSC_VER) || !defined(_WCHAR_T_DEFINED)
const StrImW(const jschar* s)
{
ref = new strImW_data;
size_t len = 0;
while (s[len] != '\0') ++len;
++len; // include the \0
ref->data = new wchar_t[len];
for (size_t i=0; i<len; ++i)
ref->data[i] = s[i];
}
#endif
const StrImW(const jschar* s, size_t len)
{
ref = new strImW_data;
ref->data = new wchar_t[len+1];
for (size_t i=0; i<len; ++i)
ref->data[i] = s[i];
ref->data[len] = 0;
}
~StrImW()
{
if (--ref->refs == 0)
{
delete[] ref->data;
delete ref;
}
}
// Copy constructor
const StrImW(const StrImW& s)
{
ref = s.ref;
++ref->refs;
}
const wchar_t* str() const
{
return ref->data;
}
bool operator== (const StrImW& s)
{
return s.ref==this->ref || wcscmp(s.ref->data, this->ref->data)==0;
}
private:
strImW_data* ref;
};
}
#endif // I18N_STRIMMUTABLE_H

70
source/i18n/StringBuffer.cpp Executable file
View File

@ -0,0 +1,70 @@
#include "precompiled.h"
#include "CLocale.h"
#include "TSComponent.h"
#include "StringBuffer.h"
// Vaguely useful utility function for deleting stuff
template<typename T> void delete_fn(T* v) { delete v; }
#include <assert.h>
#include <iostream>
#include "ps/CLogger.h"
#define LOG_CATEGORY "i18n"
using namespace I18n;
I18n::StringBuffer::operator Str()
{
if (Variables.size() != String.VarCount)
{
LOG(ERROR, LOG_CATEGORY, "I18n: Incorrect number of parameters passed to Translate");
// Tidy up
std::for_each(Variables.begin(), Variables.end(), delete_fn<BufferVariable>);
return L"(translation error)";
}
if (String.VarCount == 0)
return String.Parts[0]->ToString(Locale, Variables).str();
Str ret;
if (Locale->ReadCached(this, ret))
{
// Found in cache
// Clean up the current allocated data
std::for_each(Variables.begin(), Variables.end(), delete_fn<BufferVariable>);
// Return the cached string
return ret;
}
// Not in cache - construct the string
for (std::vector<const TSComponent*>::iterator it = String.Parts.begin(),
end = String.Parts.end();
it != end; ++it)
{
ret += (*it)->ToString(Locale, Variables).str();
}
Locale->AddToCache(this, ret);
return ret;
}
I18n::StringBuffer::StringBuffer(TranslatedString& str, CLocale* loc)
: String(str), Locale(loc)
{
}
u32 I18n::StringBuffer::Hash()
{
u32 hash = 0;
size_t max = Variables.size();
for (size_t i = 0; i < max; ++i)
hash += Variables[i]->Hash();
return hash;
}

49
source/i18n/StringBuffer.h Executable file
View File

@ -0,0 +1,49 @@
/*
Returned by Translate(), and used to collect variables from the user
before converting into a string.
*/
#ifndef I18N_STRINGBUF_H
#define I18N_STRINGBUF_H
#include "Common.h"
#include "TranslatedString.h"
namespace I18n
{
class CLocale;
class BufferVariable;
template<typename T> BufferVariable* NewBufferVariable(T v);
class StringBuffer
{
friend CLocale;
public:
// Builds and returns the finished string
operator Str();
// Stores the variable inside the StringBuffer,
// for later conversion into a string
template<typename T> StringBuffer& operator<< (T v)
{
// This BufferVariable is freed by the caching system (yuck)
Variables.push_back(NewBufferVariable(v));
return *this;
}
// Returns a simple hash of the contents of Variables
u32 Hash();
private:
StringBuffer(TranslatedString&, CLocale*);
TranslatedString& String;
std::vector<BufferVariable*> Variables;
CLocale* Locale;
};
}
#endif I18N_STRINGBUF_H

41
source/i18n/TSComponent.cpp Executable file
View File

@ -0,0 +1,41 @@
#include "precompiled.h"
#include "TSComponent.h"
#include "CLocale.h"
using namespace I18n;
// Vaguely useful utility function for deleting stuff
template<typename T> void delete_fn(T* v) { delete v; }
const StrImW TSComponentString::ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const
{
return String;
}
/**************/
const StrImW TSComponentVariable::ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const
{
// This will never be out-of-bounds -- the number
// of parameters has been checked earlier
return vars[ID]->ToString(locale);
}
/**************/
const StrImW TSComponentFunction::ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const
{
return locale->CallFunction(Name.c_str(), vars, Params);
}
void TSComponentFunction::AddParam(ScriptValue* p)
{
Params.push_back(p);
}
TSComponentFunction::~TSComponentFunction()
{
for_each(Params.begin(), Params.end(), delete_fn<ScriptValue>);
}

73
source/i18n/TSComponent.h Executable file
View File

@ -0,0 +1,73 @@
/*
Stores sections of a translated string: constant strings, simple variables,
and JS function calls, allowing conversion to strings.
*/
#ifndef I18N_TSCOMPONENT_H
#define I18N_TSCOMPONENT_H
#include "StrImmutable.h"
#include "BufferVariable.h"
#include "ScriptInterface.h"
#include <algorithm>
#include <vector>
namespace I18n
{
class CLocale;
class TSComponent
{
public:
virtual const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const = 0;
virtual ~TSComponent() {}
};
class TSComponentString : public TSComponent {
public:
TSComponentString(const wchar_t* s) : String(s) {};
const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const;
private:
const StrImW String;
};
class TSComponentVariable : public TSComponent {
public:
TSComponentVariable(unsigned char id) : ID(id) {};
const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const;
private:
unsigned char ID;
};
class TSComponentFunction : public TSComponent {
public:
TSComponentFunction(const char* name) : Name(name) {};
~TSComponentFunction();
const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const;
// AddParam is called when loading the function, building up the
// internal list of parameters (strings / ints / variables)
void AddParam(ScriptValue* p);
private:
const std::string Name;
std::vector<ScriptValue*> Params;
};
}
#endif // I18N_TSCOMPONENT_H

View File

@ -0,0 +1,12 @@
#include "precompiled.h"
#include "TranslatedString.h"
#include "TSComponent.h"
using namespace I18n;
TranslatedString::~TranslatedString()
{
for (std::vector<const TSComponent*>::iterator it = Parts.begin(); it != Parts.end(); ++it)
delete *it;
}

28
source/i18n/TranslatedString.h Executable file
View File

@ -0,0 +1,28 @@
/*
Simple storage for translated phrases, made up of lots of TSComponents
(strings / variables / functions)
*/
#ifndef I18N_TSTRING_H
#define I18N_TSTRING_H
#include <vector>
namespace I18n
{
class TSComponent;
class TranslatedString
{
public:
std::vector<const TSComponent*> Parts;
unsigned char VarCount;
~TranslatedString();
};
}
#endif // I18N_TSTRING_H

View File

@ -56,6 +56,8 @@
#include "ConfigDB.h"
#include "CLogger.h"
#include "ps/i18n.h"
#define LOG_CATEGORY "main"
#ifndef NO_GUI
@ -485,7 +487,9 @@ static void Render()
glTranslatef(10.0f, 10.0f, 0.0f);
glScalef(1.0, -1.0, 1.0);
glwprintf( L"%d FPS", fps);
CStrW fps_display = translate(L"$num FPS") << fps;
glwprintf(fps_display);
oglCheck();
@ -666,6 +670,8 @@ static void psInit()
g_Console->m_iFontHeight = unifont_linespacing(g_Font_Console);
g_Console->m_iFontOffset = 9;
I18n::LoadLanguage("pseudogreek");
loadHotkeys();
#ifndef NO_GUI
@ -694,6 +700,8 @@ static void psShutdown()
CXeromyces::Terminate();
MusicPlayer.release();
I18n::Shutdown();
}
@ -705,7 +713,7 @@ static void Shutdown()
if (g_Game)
delete g_Game;
delete &g_Scheduler;
delete &g_SessionManager;

View File

@ -7,12 +7,14 @@
class PSERROR_CVFSFile : public PSERROR {};
class PSERROR_GUI : public PSERROR {};
class PSERROR_Game : public PSERROR {};
class PSERROR_I18n : public PSERROR {};
class PSERROR_Renderer : public PSERROR {};
class PSERROR_Scripting : public PSERROR {};
class PSERROR_System : public PSERROR {};
class PSERROR_Xeromyces : public PSERROR {};
class PSERROR_Game_World : public PSERROR_Game {};
class PSERROR_I18n_Script : public PSERROR_I18n {};
class PSERROR_Scripting_DefineType : public PSERROR_Scripting {};
class PSERROR_Scripting_LoadFile : public PSERROR_Scripting {};
@ -22,6 +24,7 @@ class PSERROR_CVFSFile_LoadFailed : public PSERROR_CVFSFile { public: PSERROR_CV
class PSERROR_GUI_JSOpenFailed : public PSERROR_GUI { public: PSERROR_GUI_JSOpenFailed(); };
class PSERROR_GUI_TextureLoadFailed : public PSERROR_GUI { public: PSERROR_GUI_TextureLoadFailed(); };
class PSERROR_Game_World_MapLoadFailed : public PSERROR_Game_World { public: PSERROR_Game_World_MapLoadFailed(); };
class PSERROR_I18n_Script_SetupFailed : public PSERROR_I18n_Script { public: PSERROR_I18n_Script_SetupFailed(); };
class PSERROR_Renderer_VBOFailed : public PSERROR_Renderer { public: PSERROR_Renderer_VBOFailed(); };
class PSERROR_Scripting_CallFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CallFunctionFailed(); };
class PSERROR_Scripting_ContextCreationFailed : public PSERROR_Scripting { public: PSERROR_Scripting_ContextCreationFailed(); };
@ -49,28 +52,29 @@ extern const PSRETURN PSRETURN_CVFSFile_InvalidBufferAccess = 0x01000002;
extern const PSRETURN PSRETURN_CVFSFile_LoadFailed = 0x01000003;
extern const PSRETURN PSRETURN_GUI_JSOpenFailed = 0x02000001;
extern const PSRETURN PSRETURN_GUI_TextureLoadFailed = 0x02000002;
extern const PSRETURN PSRETURN_Game_World_MapLoadFailed = 0x03030001;
extern const PSRETURN PSRETURN_Renderer_VBOFailed = 0x04000001;
extern const PSRETURN PSRETURN_Scripting_DefineType_AlreadyExists = 0x05010001;
extern const PSRETURN PSRETURN_Scripting_DefineType_CreationFailed = 0x05010002;
extern const PSRETURN PSRETURN_Scripting_LoadFile_EvalErrors = 0x05020001;
extern const PSRETURN PSRETURN_Scripting_LoadFile_OpenFailed = 0x05020002;
extern const PSRETURN PSRETURN_Scripting_CallFunctionFailed = 0x05000001;
extern const PSRETURN PSRETURN_Scripting_ContextCreationFailed = 0x05000002;
extern const PSRETURN PSRETURN_Scripting_ConversionFailed = 0x05000003;
extern const PSRETURN PSRETURN_Scripting_CreateObjectFailed = 0x05000004;
extern const PSRETURN PSRETURN_Scripting_DefineConstantFailed = 0x05000005;
extern const PSRETURN PSRETURN_Scripting_GlobalObjectCreationFailed = 0x05000006;
extern const PSRETURN PSRETURN_Scripting_NativeFunctionSetupFailed = 0x05000007;
extern const PSRETURN PSRETURN_Scripting_RegisterFunctionFailed = 0x05000008;
extern const PSRETURN PSRETURN_Scripting_RuntimeCreationFailed = 0x05000009;
extern const PSRETURN PSRETURN_Scripting_StandardClassSetupFailed = 0x0500000a;
extern const PSRETURN PSRETURN_Scripting_TypeDoesNotExist = 0x0500000b;
extern const PSRETURN PSRETURN_System_RequiredExtensionsMissing = 0x06000001;
extern const PSRETURN PSRETURN_System_SDLInitFailed = 0x06000002;
extern const PSRETURN PSRETURN_System_VmodeFailed = 0x06000003;
extern const PSRETURN PSRETURN_Xeromyces_XMLOpenFailed = 0x07000001;
extern const PSRETURN PSRETURN_Xeromyces_XMLParseError = 0x07000002;
extern const PSRETURN PSRETURN_Game_World_MapLoadFailed = 0x03040001;
extern const PSRETURN PSRETURN_I18n_Script_SetupFailed = 0x04030001;
extern const PSRETURN PSRETURN_Renderer_VBOFailed = 0x05000001;
extern const PSRETURN PSRETURN_Scripting_DefineType_AlreadyExists = 0x06010001;
extern const PSRETURN PSRETURN_Scripting_DefineType_CreationFailed = 0x06010002;
extern const PSRETURN PSRETURN_Scripting_LoadFile_EvalErrors = 0x06020001;
extern const PSRETURN PSRETURN_Scripting_LoadFile_OpenFailed = 0x06020002;
extern const PSRETURN PSRETURN_Scripting_CallFunctionFailed = 0x06000001;
extern const PSRETURN PSRETURN_Scripting_ContextCreationFailed = 0x06000002;
extern const PSRETURN PSRETURN_Scripting_ConversionFailed = 0x06000003;
extern const PSRETURN PSRETURN_Scripting_CreateObjectFailed = 0x06000004;
extern const PSRETURN PSRETURN_Scripting_DefineConstantFailed = 0x06000005;
extern const PSRETURN PSRETURN_Scripting_GlobalObjectCreationFailed = 0x06000006;
extern const PSRETURN PSRETURN_Scripting_NativeFunctionSetupFailed = 0x06000007;
extern const PSRETURN PSRETURN_Scripting_RegisterFunctionFailed = 0x06000008;
extern const PSRETURN PSRETURN_Scripting_RuntimeCreationFailed = 0x06000009;
extern const PSRETURN PSRETURN_Scripting_StandardClassSetupFailed = 0x0600000a;
extern const PSRETURN PSRETURN_Scripting_TypeDoesNotExist = 0x0600000b;
extern const PSRETURN PSRETURN_System_RequiredExtensionsMissing = 0x07000001;
extern const PSRETURN PSRETURN_System_SDLInitFailed = 0x07000002;
extern const PSRETURN PSRETURN_System_VmodeFailed = 0x07000003;
extern const PSRETURN PSRETURN_Xeromyces_XMLOpenFailed = 0x08000001;
extern const PSRETURN PSRETURN_Xeromyces_XMLParseError = 0x08000002;
extern const PSRETURN MASK__PSRETURN_CVFSFile = 0xff000000;
extern const PSRETURN CODE__PSRETURN_CVFSFile = 0x01000000;
@ -78,20 +82,24 @@ extern const PSRETURN MASK__PSRETURN_GUI = 0xff000000;
extern const PSRETURN CODE__PSRETURN_GUI = 0x02000000;
extern const PSRETURN MASK__PSRETURN_Game = 0xff000000;
extern const PSRETURN CODE__PSRETURN_Game = 0x03000000;
extern const PSRETURN MASK__PSRETURN_I18n = 0xff000000;
extern const PSRETURN CODE__PSRETURN_I18n = 0x04000000;
extern const PSRETURN MASK__PSRETURN_Renderer = 0xff000000;
extern const PSRETURN CODE__PSRETURN_Renderer = 0x04000000;
extern const PSRETURN CODE__PSRETURN_Renderer = 0x05000000;
extern const PSRETURN MASK__PSRETURN_Scripting = 0xff000000;
extern const PSRETURN CODE__PSRETURN_Scripting = 0x05000000;
extern const PSRETURN CODE__PSRETURN_Scripting = 0x06000000;
extern const PSRETURN MASK__PSRETURN_System = 0xff000000;
extern const PSRETURN CODE__PSRETURN_System = 0x06000000;
extern const PSRETURN CODE__PSRETURN_System = 0x07000000;
extern const PSRETURN MASK__PSRETURN_Xeromyces = 0xff000000;
extern const PSRETURN CODE__PSRETURN_Xeromyces = 0x07000000;
extern const PSRETURN CODE__PSRETURN_Xeromyces = 0x08000000;
extern const PSRETURN MASK__PSRETURN_Game_World = 0xffff0000;
extern const PSRETURN CODE__PSRETURN_Game_World = 0x03030000;
extern const PSRETURN CODE__PSRETURN_Game_World = 0x03040000;
extern const PSRETURN MASK__PSRETURN_I18n_Script = 0xffff0000;
extern const PSRETURN CODE__PSRETURN_I18n_Script = 0x04030000;
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType = 0xffff0000;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType = 0x05010000;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType = 0x06010000;
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile = 0xffff0000;
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile = 0x05020000;
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile = 0x06020000;
extern const PSRETURN MASK__PSRETURN_CVFSFile_AlreadyLoaded = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_CVFSFile_AlreadyLoaded = 0x01000001;
@ -104,77 +112,80 @@ extern const PSRETURN CODE__PSRETURN_GUI_JSOpenFailed = 0x02000001;
extern const PSRETURN MASK__PSRETURN_GUI_TextureLoadFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_GUI_TextureLoadFailed = 0x02000002;
extern const PSRETURN MASK__PSRETURN_Game_World_MapLoadFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Game_World_MapLoadFailed = 0x03030001;
extern const PSRETURN CODE__PSRETURN_Game_World_MapLoadFailed = 0x03040001;
extern const PSRETURN MASK__PSRETURN_I18n_Script_SetupFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_I18n_Script_SetupFailed = 0x04030001;
extern const PSRETURN MASK__PSRETURN_Renderer_VBOFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Renderer_VBOFailed = 0x04000001;
extern const PSRETURN CODE__PSRETURN_Renderer_VBOFailed = 0x05000001;
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_AlreadyExists = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_AlreadyExists = 0x05010001;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_AlreadyExists = 0x06010001;
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_CreationFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_CreationFailed = 0x05010002;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_CreationFailed = 0x06010002;
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_EvalErrors = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_EvalErrors = 0x05020001;
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_EvalErrors = 0x06020001;
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_OpenFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_OpenFailed = 0x05020002;
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_OpenFailed = 0x06020002;
extern const PSRETURN MASK__PSRETURN_Scripting_CallFunctionFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_CallFunctionFailed = 0x05000001;
extern const PSRETURN CODE__PSRETURN_Scripting_CallFunctionFailed = 0x06000001;
extern const PSRETURN MASK__PSRETURN_Scripting_ContextCreationFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_ContextCreationFailed = 0x05000002;
extern const PSRETURN CODE__PSRETURN_Scripting_ContextCreationFailed = 0x06000002;
extern const PSRETURN MASK__PSRETURN_Scripting_ConversionFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_ConversionFailed = 0x05000003;
extern const PSRETURN CODE__PSRETURN_Scripting_ConversionFailed = 0x06000003;
extern const PSRETURN MASK__PSRETURN_Scripting_CreateObjectFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_CreateObjectFailed = 0x05000004;
extern const PSRETURN CODE__PSRETURN_Scripting_CreateObjectFailed = 0x06000004;
extern const PSRETURN MASK__PSRETURN_Scripting_DefineConstantFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineConstantFailed = 0x05000005;
extern const PSRETURN CODE__PSRETURN_Scripting_DefineConstantFailed = 0x06000005;
extern const PSRETURN MASK__PSRETURN_Scripting_GlobalObjectCreationFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_GlobalObjectCreationFailed = 0x05000006;
extern const PSRETURN CODE__PSRETURN_Scripting_GlobalObjectCreationFailed = 0x06000006;
extern const PSRETURN MASK__PSRETURN_Scripting_NativeFunctionSetupFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_NativeFunctionSetupFailed = 0x05000007;
extern const PSRETURN CODE__PSRETURN_Scripting_NativeFunctionSetupFailed = 0x06000007;
extern const PSRETURN MASK__PSRETURN_Scripting_RegisterFunctionFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_RegisterFunctionFailed = 0x05000008;
extern const PSRETURN CODE__PSRETURN_Scripting_RegisterFunctionFailed = 0x06000008;
extern const PSRETURN MASK__PSRETURN_Scripting_RuntimeCreationFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_RuntimeCreationFailed = 0x05000009;
extern const PSRETURN CODE__PSRETURN_Scripting_RuntimeCreationFailed = 0x06000009;
extern const PSRETURN MASK__PSRETURN_Scripting_StandardClassSetupFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_StandardClassSetupFailed = 0x0500000a;
extern const PSRETURN CODE__PSRETURN_Scripting_StandardClassSetupFailed = 0x0600000a;
extern const PSRETURN MASK__PSRETURN_Scripting_TypeDoesNotExist = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Scripting_TypeDoesNotExist = 0x0500000b;
extern const PSRETURN CODE__PSRETURN_Scripting_TypeDoesNotExist = 0x0600000b;
extern const PSRETURN MASK__PSRETURN_System_RequiredExtensionsMissing = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_System_RequiredExtensionsMissing = 0x06000001;
extern const PSRETURN CODE__PSRETURN_System_RequiredExtensionsMissing = 0x07000001;
extern const PSRETURN MASK__PSRETURN_System_SDLInitFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_System_SDLInitFailed = 0x06000002;
extern const PSRETURN CODE__PSRETURN_System_SDLInitFailed = 0x07000002;
extern const PSRETURN MASK__PSRETURN_System_VmodeFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_System_VmodeFailed = 0x06000003;
extern const PSRETURN CODE__PSRETURN_System_VmodeFailed = 0x07000003;
extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLOpenFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLOpenFailed = 0x07000001;
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLOpenFailed = 0x08000001;
extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLParseError = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLParseError = 0x07000002;
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLParseError = 0x08000002;
PSERROR_CVFSFile_AlreadyLoaded::PSERROR_CVFSFile_AlreadyLoaded() { magic=0x45725221; code=0x01000001; }
PSERROR_CVFSFile_InvalidBufferAccess::PSERROR_CVFSFile_InvalidBufferAccess() { magic=0x45725221; code=0x01000002; }
PSERROR_CVFSFile_LoadFailed::PSERROR_CVFSFile_LoadFailed() { magic=0x45725221; code=0x01000003; }
PSERROR_GUI_JSOpenFailed::PSERROR_GUI_JSOpenFailed() { magic=0x45725221; code=0x02000001; }
PSERROR_GUI_TextureLoadFailed::PSERROR_GUI_TextureLoadFailed() { magic=0x45725221; code=0x02000002; }
PSERROR_Game_World_MapLoadFailed::PSERROR_Game_World_MapLoadFailed() { magic=0x45725221; code=0x03030001; }
PSERROR_Renderer_VBOFailed::PSERROR_Renderer_VBOFailed() { magic=0x45725221; code=0x04000001; }
PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists() { magic=0x45725221; code=0x05010001; }
PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed() { magic=0x45725221; code=0x05010002; }
PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors() { magic=0x45725221; code=0x05020001; }
PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed() { magic=0x45725221; code=0x05020002; }
PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed() { magic=0x45725221; code=0x05000001; }
PSERROR_Scripting_ContextCreationFailed::PSERROR_Scripting_ContextCreationFailed() { magic=0x45725221; code=0x05000002; }
PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed() { magic=0x45725221; code=0x05000003; }
PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed() { magic=0x45725221; code=0x05000004; }
PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed() { magic=0x45725221; code=0x05000005; }
PSERROR_Scripting_GlobalObjectCreationFailed::PSERROR_Scripting_GlobalObjectCreationFailed() { magic=0x45725221; code=0x05000006; }
PSERROR_Scripting_NativeFunctionSetupFailed::PSERROR_Scripting_NativeFunctionSetupFailed() { magic=0x45725221; code=0x05000007; }
PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed() { magic=0x45725221; code=0x05000008; }
PSERROR_Scripting_RuntimeCreationFailed::PSERROR_Scripting_RuntimeCreationFailed() { magic=0x45725221; code=0x05000009; }
PSERROR_Scripting_StandardClassSetupFailed::PSERROR_Scripting_StandardClassSetupFailed() { magic=0x45725221; code=0x0500000a; }
PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist() { magic=0x45725221; code=0x0500000b; }
PSERROR_System_RequiredExtensionsMissing::PSERROR_System_RequiredExtensionsMissing() { magic=0x45725221; code=0x06000001; }
PSERROR_System_SDLInitFailed::PSERROR_System_SDLInitFailed() { magic=0x45725221; code=0x06000002; }
PSERROR_System_VmodeFailed::PSERROR_System_VmodeFailed() { magic=0x45725221; code=0x06000003; }
PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() { magic=0x45725221; code=0x07000001; }
PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() { magic=0x45725221; code=0x07000002; }
PSERROR_Game_World_MapLoadFailed::PSERROR_Game_World_MapLoadFailed() { magic=0x45725221; code=0x03040001; }
PSERROR_I18n_Script_SetupFailed::PSERROR_I18n_Script_SetupFailed() { magic=0x45725221; code=0x04030001; }
PSERROR_Renderer_VBOFailed::PSERROR_Renderer_VBOFailed() { magic=0x45725221; code=0x05000001; }
PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists() { magic=0x45725221; code=0x06010001; }
PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed() { magic=0x45725221; code=0x06010002; }
PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors() { magic=0x45725221; code=0x06020001; }
PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed() { magic=0x45725221; code=0x06020002; }
PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed() { magic=0x45725221; code=0x06000001; }
PSERROR_Scripting_ContextCreationFailed::PSERROR_Scripting_ContextCreationFailed() { magic=0x45725221; code=0x06000002; }
PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed() { magic=0x45725221; code=0x06000003; }
PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed() { magic=0x45725221; code=0x06000004; }
PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed() { magic=0x45725221; code=0x06000005; }
PSERROR_Scripting_GlobalObjectCreationFailed::PSERROR_Scripting_GlobalObjectCreationFailed() { magic=0x45725221; code=0x06000006; }
PSERROR_Scripting_NativeFunctionSetupFailed::PSERROR_Scripting_NativeFunctionSetupFailed() { magic=0x45725221; code=0x06000007; }
PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed() { magic=0x45725221; code=0x06000008; }
PSERROR_Scripting_RuntimeCreationFailed::PSERROR_Scripting_RuntimeCreationFailed() { magic=0x45725221; code=0x06000009; }
PSERROR_Scripting_StandardClassSetupFailed::PSERROR_Scripting_StandardClassSetupFailed() { magic=0x45725221; code=0x0600000a; }
PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist() { magic=0x45725221; code=0x0600000b; }
PSERROR_System_RequiredExtensionsMissing::PSERROR_System_RequiredExtensionsMissing() { magic=0x45725221; code=0x07000001; }
PSERROR_System_SDLInitFailed::PSERROR_System_SDLInitFailed() { magic=0x45725221; code=0x07000002; }
PSERROR_System_VmodeFailed::PSERROR_System_VmodeFailed() { magic=0x45725221; code=0x07000003; }
PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() { magic=0x45725221; code=0x08000001; }
PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() { magic=0x45725221; code=0x08000002; }
const wchar_t* GetErrorString(PSRETURN code)
{
@ -185,28 +196,29 @@ const wchar_t* GetErrorString(PSRETURN code)
case 0x01000003: return L"CVFSFile_LoadFailed"; break;
case 0x02000001: return L"GUI_JSOpenFailed"; break;
case 0x02000002: return L"GUI_TextureLoadFailed"; break;
case 0x03030001: return L"Game_World_MapLoadFailed"; break;
case 0x04000001: return L"Renderer_VBOFailed"; break;
case 0x05010001: return L"Scripting_DefineType_AlreadyExists"; break;
case 0x05010002: return L"Scripting_DefineType_CreationFailed"; break;
case 0x05020001: return L"Scripting_LoadFile_EvalErrors"; break;
case 0x05020002: return L"Scripting_LoadFile_OpenFailed"; break;
case 0x05000001: return L"Scripting_CallFunctionFailed"; break;
case 0x05000002: return L"Scripting_ContextCreationFailed"; break;
case 0x05000003: return L"Scripting_ConversionFailed"; break;
case 0x05000004: return L"Scripting_CreateObjectFailed"; break;
case 0x05000005: return L"Scripting_DefineConstantFailed"; break;
case 0x05000006: return L"Scripting_GlobalObjectCreationFailed"; break;
case 0x05000007: return L"Scripting_NativeFunctionSetupFailed"; break;
case 0x05000008: return L"Scripting_RegisterFunctionFailed"; break;
case 0x05000009: return L"Scripting_RuntimeCreationFailed"; break;
case 0x0500000a: return L"Scripting_StandardClassSetupFailed"; break;
case 0x0500000b: return L"Scripting_TypeDoesNotExist"; break;
case 0x06000001: return L"System_RequiredExtensionsMissing"; break;
case 0x06000002: return L"System_SDLInitFailed"; break;
case 0x06000003: return L"System_VmodeFailed"; break;
case 0x07000001: return L"Xeromyces_XMLOpenFailed"; break;
case 0x07000002: return L"Xeromyces_XMLParseError"; break;
case 0x03040001: return L"Game_World_MapLoadFailed"; break;
case 0x04030001: return L"I18n_Script_SetupFailed"; break;
case 0x05000001: return L"Renderer_VBOFailed"; break;
case 0x06010001: return L"Scripting_DefineType_AlreadyExists"; break;
case 0x06010002: return L"Scripting_DefineType_CreationFailed"; break;
case 0x06020001: return L"Scripting_LoadFile_EvalErrors"; break;
case 0x06020002: return L"Scripting_LoadFile_OpenFailed"; break;
case 0x06000001: return L"Scripting_CallFunctionFailed"; break;
case 0x06000002: return L"Scripting_ContextCreationFailed"; break;
case 0x06000003: return L"Scripting_ConversionFailed"; break;
case 0x06000004: return L"Scripting_CreateObjectFailed"; break;
case 0x06000005: return L"Scripting_DefineConstantFailed"; break;
case 0x06000006: return L"Scripting_GlobalObjectCreationFailed"; break;
case 0x06000007: return L"Scripting_NativeFunctionSetupFailed"; break;
case 0x06000008: return L"Scripting_RegisterFunctionFailed"; break;
case 0x06000009: return L"Scripting_RuntimeCreationFailed"; break;
case 0x0600000a: return L"Scripting_StandardClassSetupFailed"; break;
case 0x0600000b: return L"Scripting_TypeDoesNotExist"; break;
case 0x07000001: return L"System_RequiredExtensionsMissing"; break;
case 0x07000002: return L"System_SDLInitFailed"; break;
case 0x07000003: return L"System_VmodeFailed"; break;
case 0x08000001: return L"Xeromyces_XMLOpenFailed"; break;
case 0x08000002: return L"Xeromyces_XMLParseError"; break;
}
return L"Unrecognised error";
}

53
source/ps/StringConvert.cpp Executable file
View File

@ -0,0 +1,53 @@
#include "precompiled.h"
#include "StringConvert.h"
#include <assert.h>
#include "jsapi.h"
#if SDL_BYTE_ORDER == SDL_BIG_ENDIAN
#define ucs2le_to_wchart(ptr) (wchar_t)( (u16) ((u8*)ptr)[0] | (u16) ( ((u8*)ptr)[1] << 8) )
#else
#define ucs2le_to_wchart(ptr) (wchar_t)(*ptr);
#endif
JSString* StringConvert::wstring_to_jsstring(JSContext* cx, const std::wstring& str)
{
size_t len = str.length();
jschar* data = (jschar*)JS_malloc(cx, len*sizeof(jschar));
if (!data)
return NULL;
for (size_t i=0; i<len; ++i)
data[i] = str[i];
return JS_NewUCString(cx, data, len);
}
JSString* StringConvert::wchars_to_jsstring(JSContext* cx, const wchar_t* chars)
{
size_t len = wcslen(chars);
jschar* data = (jschar*)JS_malloc(cx, len*sizeof(jschar));
if (!data)
return NULL;
for (size_t i=0; i<len; ++i)
data[i] = chars[i];
return JS_NewUCString(cx, data, len);
}
void StringConvert::jschars_to_wstring(const jschar* chars, size_t len, std::wstring& result)
{
assert(result.empty());
result.reserve(len);
for (size_t i = 0; i < len; ++i)
result += chars[i];
}
void StringConvert::ucs2le_to_wstring(const char* start, const char* end, std::wstring& result)
{
assert(result.empty());
result.reserve(end-start);
for (const char* pos = start; pos < end; pos += 2)
result += ucs2le_to_wchart(pos);
}

19
source/ps/StringConvert.h Executable file
View File

@ -0,0 +1,19 @@
typedef unsigned short jschar;
typedef unsigned short ucs2char;
struct JSString;
struct JSContext;
#include <string>
namespace StringConvert
{
// A random collection of conversion functions:
JSString* wstring_to_jsstring(JSContext* cx, const std::wstring& str);
JSString* wchars_to_jsstring(JSContext* cx, const wchar_t* chars);
void jschars_to_wstring(const jschar* chars, size_t len, std::wstring& result);
void ucs2le_to_wstring(const char* start, const char* end, std::wstring& result);
}

52
source/ps/i18n.cpp Executable file
View File

@ -0,0 +1,52 @@
#include "precompiled.h"
#include "ps/i18n.h"
#include "ps/CVFSFile.h"
#include "scripting/ScriptingHost.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY "i18n"
I18n::CLocale_interface* g_CurrentLocale = NULL;
bool I18n::LoadLanguage(const char* name)
{
CLocale_interface* locale_ptr = I18n::NewLocale(g_ScriptingHost.getContext());
if (! locale_ptr)
{
debug_warn("Failed to create locale");
return false;
}
std::auto_ptr<CLocale_interface> locale (locale_ptr);
{
CVFSFile strings;
CStr filename = CStr("language/")+name+"/phrases.lng";
if (strings.Load(filename) != PSRETURN_OK)
{
LOG(ERROR, LOG_CATEGORY, "Error opening language string file '%s'", filename.c_str());
return false;
}
if (! locale->LoadStrings( (const char*) strings.GetBuffer() ))
{
LOG(ERROR, LOG_CATEGORY, "Error loading language string file '%s'", filename.c_str());
return false;
}
}
// Free any previously loaded data
delete g_CurrentLocale;
g_CurrentLocale = locale.release();
return true;
}
void I18n::Shutdown()
{
delete g_CurrentLocale;
}

11
source/ps/i18n.h Executable file
View File

@ -0,0 +1,11 @@
#include "i18n/Interface.h"
extern I18n::CLocale_interface* g_CurrentLocale;
#define translate(s) g_CurrentLocale->Translate(s)
namespace I18n
{
bool LoadLanguage(const char* name);
void Shutdown();
}

View File

@ -14,6 +14,8 @@
#include "Network/Server.h"
#include "Network/Client.h"
#include "ps/i18n.h"
#include "scripting/JSInterface_Entity.h"
#include "scripting/JSInterface_BaseEntity.h"
#include "scripting/JSInterface_Vector3D.h"
@ -49,6 +51,7 @@ JSFunctionSpec ScriptFunctionTable[] =
{"endGame", endGame, 0, 0, 0 },
{"joinGame", joinGame, 0, 0, 0 },
{"startServer", startServer, 0, 0, 0 },
{"loadLanguage", loadLanguage, 1, 0, 0 },
{"exit", exitProgram, 0, 0, 0 },
{"crash", crash, 0, 0, 0 },
@ -357,6 +360,21 @@ JSBool endGame(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObje
return JS_TRUE;
}
JSBool loadLanguage(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
{
if (argc != 1)
{
JS_ReportError(cx, "loadLanguage: needs 1 parameter");
return JS_FALSE;
}
CStr lang = g_ScriptingHost.ValueToString(argv[0]);
I18n::LoadLanguage(lang);
return JS_TRUE;
}
extern void kill_mainloop(); // from main.cpp
JSBool exitProgram(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* UNUSEDPARAM(rval))
@ -368,7 +386,7 @@ JSBool exitProgram(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(global
JSBool crash(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* UNUSEDPARAM(rval))
{
MICROLOG(L"Crashing at user's request.");
uintptr_t ptr = 0xDEADC0DE;
uintptr_t ptr = 0xDEADC0DE; // oh dear, might this be an invalid pointer?
return *(JSBool*) ptr;
}

View File

@ -27,13 +27,16 @@ JSBool joinGame(JSContext* context, JSObject* globalObject, unsigned int argc, j
JSBool startGame(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
JSBool endGame(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
// Replaces the current language (locale) with a new one
JSBool loadLanguage(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
// Tells the main loop to stop looping
JSBool exitProgram(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
// Crashes.
JSBool crash(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
// Tries to print the amount of remaining video memory
// Tries to print the amount of remaining video memory. (I don't like starting functions with underscores).
JSBool js_mem(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
extern JSFunctionSpec ScriptFunctionTable[];

View File

@ -22,52 +22,65 @@ package.config["Testing"].target = "ps_test"
-- Files
package.files = {
-- ps/
{ sourcesfromdirs("../../ps") },
{ sourcesfromdirs("../../ps/scripting") },
{ sourcesfromdirs("../../ps") },
{ sourcesfromdirs("../../ps/scripting") },
{ sourcesfromdirs("../../ps/Network") },
-- simulation/
{ sourcesfromdirs("../../simulation") },
{ sourcesfromdirs("../../simulation/scripting") },
{ sourcesfromdirs("../../simulation/scripting") },
-- lib/
{ sourcesfromdirs(
"../../lib",
"../../lib/sysdep",
"../../lib/res") },
"../../lib",
"../../lib/sysdep",
"../../lib/res") },
-- graphics/
{ sourcesfromdirs(
"../../graphics") },
{ sourcesfromdirs( "../../graphics/scripting" ) },
"../../graphics") },
{ sourcesfromdirs( "../../graphics/scripting" ) },
-- maths/
{ sourcesfromdirs(
"../../maths") },
{ sourcesfromdirs( "../../maths/scripting" ) },
"../../maths") },
{ sourcesfromdirs( "../../maths/scripting" ) },
-- renderer/
{ sourcesfromdirs(
"../../renderer") },
"../../renderer") },
-- gui/
{ sourcesfromdirs(
"../../gui") },
{ sourcesfromdirs( "../../gui/scripting" ) },
"../../gui") },
{ sourcesfromdirs( "../../gui/scripting" ) },
-- terrain/
{ sourcesfromdirs(
"../../terrain") },
"../../terrain") },
-- sound/
{ sourcesfromdirs(
"../../sound") },
"../../sound") },
-- main
{ "../../main.cpp" },
-- scripting
{ sourcesfromdirs("../../scripting") }
{ sourcesfromdirs("../../scripting") },
-- i18n
{ sourcesfromdirs("../../i18n") }
}
package.includepaths = {
"../../ps",
"../../simulation",
"../../lib",
"../../graphics",
"../../maths",
"../../renderer",
"../../terrain",
"../../ps",
"../../simulation",
"../../lib",
"../../graphics",
"../../maths",
"../../renderer",
"../../terrain",
"../.." }
package.libpaths = {