/* ************************************************************************* ArmageTron -- Just another Tron Lightcycle Game in 3D. Copyright (C) 2000 Manuel Moos (manuel@moosnet.de) ************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *************************************************************************** */ #include "tMemManager.h" #include "tLocale.h" #include "tConsole.h" #include "tDirectories.h" #include #include class tLocaleSubItem; // identifies a single string in a single language static tArray st_TemplateParameters; static tString s_gameName("Armagetron"); // the official name of this game class tLocaleItem: public tListItem // idendifies a string in all languages { friend class tLocaleSubItem; tString identifier; // the cross-language identifier of this string tLocaleSubItem *items; // the versions of various languages bool istemplate; // does it contain \i-directives? public: static void Check(); // display warnings for all strings not defined in // the favorite language // operator tString() const; // return the version of this string in the favorite language operator const char *() const; tLocaleItem(const char *identifier); // constructor taking the string identifier ~tLocaleItem(); static void Load(const char *file); // load the language definitions from a file static void Clear(); // clear all locale data on program exit static tLocaleItem& Find(const char *identifier); // find identifier }; static const tLanguage* st_firstLanguage = NULL; static const tLanguage* st_secondLanguage = NULL; static tLanguage* st_languageAnchor = NULL; static tLocaleItem* st_localeAnchor = NULL; static tLanguage *currentLanguage = NULL; class tLocaleSubItem: public tListItem { public: const tLanguage *language; // the language this string is in tString translation; // the string itself tLocaleSubItem(tLocaleItem *item) // adds this SubItem to item : tListItem(item->items){} }; tLanguage::tLanguage(const tString& n) :tListItem(st_languageAnchor), name(n) { } tLanguage* tLanguage::FirstLanguage() { return st_languageAnchor; } void tLanguage::SetFirstLanguage() const { st_firstLanguage = this; } void tLanguage::SetSecondLanguage() const { st_secondLanguage = this; } tLanguage* tLanguage::Find(tString& n) { tLanguage *ret = st_languageAnchor; while (ret) { if (ret->name == n) return ret; ret = ret->Next(); } return tNEW(tLanguage(n)); } void tLocaleItem::Check() { tLocaleItem *run = st_localeAnchor; while (run) { tLocaleSubItem *r = run->items; bool ok = false; while (r) { if (r->language == currentLanguage) ok = true; r = r->Next(); } if (!ok) { // con << "Identifier " << run->identifier << " not translated.\n"; con << run->identifier << "\n"; } run = run->Next(); } } tLocaleItem::operator const char *() const// return the version of this string in the favorite language { tString *first = NULL, *second = NULL, *third = NULL; const tString *ret = NULL; tLocaleSubItem *run = items; while (run) { if (run->language == st_firstLanguage) first = &run->translation; if (run->language == st_secondLanguage) second = &run->translation; third = &run->translation; run = run->Next(); } if (first) ret = first; else if (second) ret = second; else if (third) ret = third; else ret = &identifier; if (!istemplate) return *ret; // no template replacements need to be made else { const tString& temp = *ret; static tString replaced; replaced.Clear(); for(int i = 0; i < temp.Len(); i++) { char c = temp(i); if (c != '\\') replaced += c; else if (i < temp.Len() - 1) { c = temp(i+1); if (c == 'g') replaced << s_gameName; else { int index = c-'0'; if (index > 0 && index < 10) replaced << st_TemplateParameters[index]; else if (temp(i+1) == '\\') replaced << '\n'; } i++; } } return replaced; } } tLocaleItem::tLocaleItem(const char *id) // constructor taking the string identifier :tListItem(st_localeAnchor), identifier(id), items(NULL), istemplate(false) { } tLocaleItem::~tLocaleItem() { while (items) delete (items); } void tLocaleItem::Clear() { while (st_localeAnchor) delete (st_localeAnchor); while (st_languageAnchor) delete (st_languageAnchor); } tLocaleItem& tLocaleItem::Find(const char *nn) { tString n(nn); tLocaleItem *ret = st_localeAnchor; while (ret) { if (ret->identifier == n) return *ret; ret = ret->Next(); } return *tNEW(tLocaleItem(n)); } static const tString LANGUAGE("language"); static const tString INCLUDE("include"); static const tString CHECK("check"); void tLocaleItem::Load(const char *file) // load the language definitions from a file { bool check = false; { tString f; f << "language/" << file; std::ifstream s; if ( tDirectories::Data().Open( s, f ) ) { while (!s.eof() && s.good()) { tString id; s >> id; if (id.Len() <= 1 ) { continue; } if(id[0] == '#') { tString dummy; dummy.ReadLine(s); continue; } if (LANGUAGE == id) { tString lang; lang.ReadLine(s); currentLanguage = tLanguage::Find(lang); continue; } if (INCLUDE == id) { tString inc; inc.ReadLine(s); Load(inc); continue; } if (CHECK == id) { check = true; tString dummy; dummy.ReadLine(s); continue; } // id is a true string identifier. tLocaleItem &li = Find(id); tLocaleSubItem *r = li.items; bool done = false; while (r && !done) { if (r->language == currentLanguage) { // r->translation.ReadLine(s); done = true; continue; } r = r->Next(); } if (!done) r = tNEW(tLocaleSubItem)(&li); else { st_Breakpoint(); con << "Locale item " << id << " defined twice in language " << currentLanguage->Name() << ".\n"; } tString pre; pre.ReadLine(s); r->translation.Clear(); for (int i=0; i< pre.Len(); i++) { char c = pre(i); if (c != '\\') r->translation += c; else if (i < pre.Len()-1) switch (pre(i+1)) { case 'n': r->translation += '\n'; i++; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'g': r->translation += '\\'; li.istemplate = true; break; /* case '\\': r->translation << "\\\\"; i++; */ default: r->translation += '\\'; break; } } r->language = currentLanguage; // r->translation.ReadLine(s); } } } if (check) Check(); // determine the name of the game s_gameName.Clear(); s_gameName << tOutput("$game_name"); } class tOutputItemLocale: public tOutputItemBase { const tLocaleItem *element; public: tOutputItemLocale(tOutput& o, const tLocaleItem& e); virtual void Print(tString& target) const; virtual void Clone(tOutput& o) const; }; class tOutputItemSpace: public tOutputItemBase { public: tOutputItemSpace(tOutput& o); virtual void Print(tString& target) const; virtual void Clone(tOutput& o) const; }; class tOutputItemTemplate: public tOutputItemBase { int num; tString parameter; public: tOutputItemTemplate(tOutput& o, int n, const char *p); virtual void Print(tString& target) const; virtual void Clone(tOutput& o) const; }; tOutput::tOutput(): anchor(0){} tOutput::~tOutput() { while (anchor) delete anchor; } void tOutput::Clear() { while (anchor) delete anchor; } static void getstring(tString &target, tOutputItemBase *item) { if (!item) return; getstring(target, item->Next()); item->Print(target); } /* tOutput::operator tString() const { #ifdef DEBUG #ifndef WIN32 static bool recursion = false; if (recursion) { tERR_ERROR_INT("Locale Recursion!"); } recursion = true; #endif #endif static tString x; x.Clear(); getstring(x, anchor); #ifdef DEBUG #ifndef WIN32 recursion = false; #endif #endif return x; } */ tOutput::operator const char *() const { #ifdef DEBUG #ifndef WIN32 static bool recursion = false; if (recursion) { tERR_ERROR_INT("Locale Recursion!"); } recursion = true; #endif #endif static tString x; x.Clear(); getstring(x, anchor); #ifdef DEBUG #ifndef WIN32 recursion = false; #endif #endif return x; } void tOutput::AddLiteral(const char *x) { tNEW(tOutputItem)(*this, tString(x)); } void tOutput::AddSpace() { tNEW(tOutputItemSpace)(*this); } void tOutput::AddLocale(const char *x) { tNEW(tOutputItemLocale)(*this, tLocale::Find(x)); } void tOutput::SetTemplateParameter(int num, const char *parameter) { tNEW(tOutputItemTemplate)(*this, num, parameter); } void tOutput::SetTemplateParameter(int num, int parameter) { tString p; p << parameter; tNEW(tOutputItemTemplate)(*this, num, p); } void tOutput::SetTemplateParameter(int num, float parameter) { tString p; p << parameter; tNEW(tOutputItemTemplate)(*this, num, p); } tOutput::tOutput(const tString& x) :anchor(NULL) { tNEW(tOutputItem)(*this, x); } tOutput::tOutput(const char * x) :anchor(NULL) { *this << x; } tOutput::tOutput(const tLocaleItem &locale) :anchor(NULL) { tNEW(tOutputItemLocale)(*this, locale); } static void copyrec(tOutput &targ, tOutputItemBase *it) { if (!it) return; copyrec(targ, it->Next()); it->Clone(targ); } tOutput::tOutput(const tOutput &o) :anchor(NULL) { copyrec(*this, o.anchor); } tOutput& tOutput::operator=(const tOutput &o) { Clear(); copyrec(*this, o.anchor); return *this; } void tOutput::Append(const tOutput &o) { copyrec(*this, o.anchor); } tOutputItemBase::tOutputItemBase(tOutput& o): tListItem(o.anchor){}; tOutputItemBase::~tOutputItemBase(){} tOutputItemLocale::tOutputItemLocale(tOutput& o, const tLocaleItem& e) :tOutputItemBase(o), element(&e) { } void tOutputItemLocale::Print(tString& target) const { target << *element; } void tOutputItemLocale::Clone(tOutput& o) const { tNEW(tOutputItemLocale)(o, *element); } tOutputItemSpace::tOutputItemSpace(tOutput& o) :tOutputItemBase(o) {} void tOutputItemSpace::Print(tString& target) const { target << ' '; } void tOutputItemSpace::Clone(tOutput& o) const { tNEW(tOutputItemSpace)(o); } tOutputItemTemplate::tOutputItemTemplate(tOutput& o, int n, const char *p) :tOutputItemBase(o), num(n), parameter(p) {} void tOutputItemTemplate::Print(tString& target) const { st_TemplateParameters[num] = parameter; } void tOutputItemTemplate::Clone(tOutput& o) const { tNEW(tOutputItemTemplate)(o, num, parameter); } tOutput& operator << (tOutput &o, char *locale) { return operator<<(o, static_cast(locale)); } // and a special implementation for the locales and strings: tOutput& operator << (tOutput &o, const char *locale){ int len = strlen(locale); if (len == 0) return o; if (len == 1 && locale[0] == ' ') tNEW(tOutputItemSpace)(o); else if (locale[0] == '$') tNEW(tOutputItemLocale)(o, tLocale::Find(locale+1)); else tNEW(tOutputItem)(o, tString(locale)); return o; } // output operators std::ostream& operator<< (std::ostream& s, const tOutput& o) { return s << static_cast(o); } /* std::stringstream& operator<< (std::stringstream& s, const tOutput& o) { static_cast(s) << static_cast(o); return s; } */ tString& operator<< (tString& s, const tOutput& o) { s << static_cast(o); return s; } // interface class for locale items const tLocaleItem& tLocale::Find(const char* id) { return tLocaleItem::Find(id); } void tLocale::Load(const char* filename) { tLocaleItem::Load(filename); } void tLocale::Clear() { tLocaleItem::Clear(); } /* std::stringstream& operator<<(std::stringstream& s, const tLocaleItem &t) { static_cast(s) << static_cast(t); return s; } */ std::ostream& operator<<(std::ostream& s, const tLocaleItem &t) { return s << static_cast(t); } tString& operator<< (tString& s, const tLocaleItem& o) { s << static_cast(o); return s; }