00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../strgen/strgen.h"
00014 #include "../debug.h"
00015 #include "../fileio_func.h"
00016 #include "../script/squirrel_class.hpp"
00017 #include "../strings_func.h"
00018 #include "game_text.hpp"
00019 #include "game.hpp"
00020
00021 #include "table/strings.h"
00022
00023 #include <stdarg.h>
00024
00025 void CDECL strgen_warning(const char *s, ...)
00026 {
00027 char buf[1024];
00028 va_list va;
00029 va_start(va, s);
00030 vsnprintf(buf, lengthof(buf), s, va);
00031 va_end(va);
00032 DEBUG(script, 0, "%s:%d: warning: %s", _file, _cur_line, buf);
00033 _warnings++;
00034 }
00035
00036 void CDECL strgen_error(const char *s, ...)
00037 {
00038 char buf[1024];
00039 va_list va;
00040 va_start(va, s);
00041 vsnprintf(buf, lengthof(buf), s, va);
00042 va_end(va);
00043 DEBUG(script, 0, "%s:%d: error: %s", _file, _cur_line, buf);
00044 _errors++;
00045 }
00046
00047 void NORETURN CDECL strgen_fatal(const char *s, ...)
00048 {
00049 char buf[1024];
00050 va_list va;
00051 va_start(va, s);
00052 vsnprintf(buf, lengthof(buf), s, va);
00053 va_end(va);
00054 DEBUG(script, 0, "%s:%d: FATAL: %s", _file, _cur_line, buf);
00055 throw std::exception();
00056 }
00057
00062 LanguageStrings::LanguageStrings(const char *language)
00063 {
00064 const char *p = strrchr(language, PATHSEPCHAR);
00065 if (p == NULL) {
00066 p = language;
00067 } else {
00068 p++;
00069 }
00070
00071 const char *e = strchr(p, '.');
00072 this->language = e == NULL ? strdup(p) : strndup(p, e - p);
00073 }
00074
00076 LanguageStrings::~LanguageStrings()
00077 {
00078 free(this->language);
00079 }
00080
00086 LanguageStrings *ReadRawLanguageStrings(const char *file)
00087 {
00088 LanguageStrings *ret = NULL;
00089 try {
00090 size_t to_read;
00091 FILE *fh = FioFOpenFile(file, "rb", GAME_DIR, &to_read);
00092 if (fh == NULL) {
00093 return NULL;
00094 }
00095
00096 ret = new LanguageStrings(file);
00097
00098 char buffer[2048];
00099 while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != NULL) {
00100 size_t len = strlen(buffer);
00101
00102
00103 size_t i = len;
00104 while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--;
00105 buffer[i] = '\0';
00106
00107 *ret->lines.Append() = strndup(buffer, to_read);
00108
00109 if (len > to_read) {
00110 to_read = 0;
00111 } else {
00112 to_read -= len;
00113 }
00114 }
00115
00116 return ret;
00117 } catch (...) {
00118 delete ret;
00119 return NULL;
00120 }
00121 }
00122
00123
00125 struct StringListReader : StringReader {
00126 const char * const *p;
00127 const char * const *end;
00128
00136 StringListReader(StringData &data, const LanguageStrings *strings, bool master, bool translation) :
00137 StringReader(data, strings->language, master, translation), p(strings->lines.Begin()), end(strings->lines.End())
00138 {
00139 }
00140
00141 char *ReadLine(char *buffer, size_t size)
00142 {
00143 if (this->p == this->end) return NULL;
00144
00145 strncpy(buffer, *this->p, size);
00146 this->p++;
00147
00148 return buffer;
00149 }
00150
00151 void HandlePragma(char *str)
00152 {
00153 strgen_fatal("unknown pragma '%s'", str);
00154 }
00155 };
00156
00158 struct TranslationWriter : LanguageWriter {
00159 StringList *strings;
00160
00165 TranslationWriter(StringList *strings) : strings(strings)
00166 {
00167 }
00168
00169 void WriteHeader(const LanguagePackHeader *header)
00170 {
00171
00172 }
00173
00174 void Finalise()
00175 {
00176
00177 }
00178
00179 void WriteLength(uint length)
00180 {
00181
00182 }
00183
00184 void Write(const byte *buffer, size_t length)
00185 {
00186 *this->strings->Append() = strndup((const char*)buffer, length);
00187 }
00188 };
00189
00191 struct StringNameWriter : HeaderWriter {
00192 StringList *strings;
00193
00198 StringNameWriter(StringList *strings) : strings(strings)
00199 {
00200 }
00201
00202 void WriteStringID(const char *name, int stringid)
00203 {
00204 if (stringid == (int)this->strings->Length()) *this->strings->Append() = strdup(name);
00205 }
00206
00207 void Finalise(const StringData &data)
00208 {
00209
00210 }
00211 };
00212
00213 static void GetBasePath(char *buffer, size_t length)
00214 {
00215 strecpy(buffer, Game::GetMainScript(), buffer + length);
00216 char *s = strrchr(buffer, PATHSEPCHAR);
00217 if (s != NULL) {
00218
00219 s++;
00220 *s = '\0';
00221 }
00222
00223
00224 #if (PATHSEPCHAR != '/')
00225 for (char *n = buffer; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00226 #endif
00227 }
00228
00232 class LanguageScanner : protected FileScanner {
00233 private:
00234 GameStrings *gs;
00235 char *exclude;
00236
00237 public:
00239 LanguageScanner(GameStrings *gs, const char *exclude) : gs(gs), exclude(strdup(exclude)) {}
00240 ~LanguageScanner() { free(exclude); }
00241
00245 void Scan(const char *directory)
00246 {
00247 this->FileScanner::Scan(".txt", directory, false);
00248 }
00249
00250 bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00251 {
00252 if (strcmp(filename, exclude) == 0) return true;
00253
00254 *gs->raw_strings.Append() = ReadRawLanguageStrings(filename);
00255 return true;
00256 }
00257 };
00258
00263 GameStrings *LoadTranslations()
00264 {
00265 GameStrings *gs = new GameStrings();
00266 try {
00267 char filename[512];
00268 GetBasePath(filename, sizeof(filename));
00269 char *e = filename + strlen(filename);
00270
00271 seprintf(e, filename + sizeof(filename), "lang" PATHSEP "english.txt");
00272 if (!FioCheckFileExists(filename, GAME_DIR)) throw std::exception();
00273 *gs->raw_strings.Append() = ReadRawLanguageStrings(filename);
00274
00275
00276 LanguageScanner scanner(gs, filename);
00277 strecpy(e, "lang" PATHSEP, filename + sizeof(filename));
00278 scanner.Scan(filename);
00279
00280 gs->Compile();
00281 return gs;
00282 } catch (...) {
00283 delete gs;
00284 return NULL;
00285 }
00286 }
00287
00289 void GameStrings::Compile()
00290 {
00291 StringData data(1);
00292 StringListReader master_reader(data, this->raw_strings[0], true, false);
00293 master_reader.ParseFile();
00294 if (_errors != 0) throw std::exception();
00295
00296 this->version = data.Version();
00297
00298 StringNameWriter id_writer(&this->string_names);
00299 id_writer.WriteHeader(data);
00300
00301 for (LanguageStrings **p = this->raw_strings.Begin(); p != this->raw_strings.End(); p++) {
00302 data.FreeTranslation();
00303 StringListReader translation_reader(data, *p, false, strcmp((*p)->language, "english") != 0);
00304 translation_reader.ParseFile();
00305 if (_errors != 0) throw std::exception();
00306
00307 LanguageStrings *compiled = *this->compiled_strings.Append() = new LanguageStrings((*p)->language);
00308 TranslationWriter writer(&compiled->lines);
00309 writer.WriteLang(data);
00310 }
00311 }
00312
00314 GameStrings *_current_data = NULL;
00315
00321 const char *GetGameStringPtr(uint id)
00322 {
00323 if (id >= _current_data->cur_language->lines.Length()) return GetStringPtr(STR_UNDEFINED);
00324 return _current_data->cur_language->lines[id];
00325 }
00326
00331 void RegisterGameTranslation(Squirrel *engine)
00332 {
00333 delete _current_data;
00334 _current_data = LoadTranslations();
00335 if (_current_data == NULL) return;
00336
00337 HSQUIRRELVM vm = engine->GetVM();
00338 sq_pushroottable(vm);
00339 sq_pushstring(vm, _SC("GSText"), -1);
00340 if (SQ_FAILED(sq_get(vm, -2))) return;
00341
00342 int idx = 0;
00343 for (const char * const *p = _current_data->string_names.Begin(); p != _current_data->string_names.End(); p++, idx++) {
00344 sq_pushstring(vm, OTTD2SQ(*p), -1);
00345 sq_pushinteger(vm, idx);
00346 sq_rawset(vm, -3);
00347 }
00348
00349 sq_pop(vm, 2);
00350
00351 ReconsiderGameScriptLanguage();
00352 }
00353
00357 void ReconsiderGameScriptLanguage()
00358 {
00359 if (_current_data == NULL) return;
00360
00361 char temp[MAX_PATH];
00362 strecpy(temp, _current_language->file, temp + sizeof(temp));
00363
00364
00365 char *l = strrchr(temp, '.');
00366 assert(l != NULL);
00367 *l = '\0';
00368
00369
00370 char *language = strrchr(temp, PATHSEPCHAR);
00371 assert(language != NULL);
00372 language++;
00373
00374 for (LanguageStrings **p = _current_data->compiled_strings.Begin(); p != _current_data->compiled_strings.End(); p++) {
00375 if (strcmp((*p)->language, language) == 0) {
00376 _current_data->cur_language = *p;
00377 return;
00378 }
00379 }
00380
00381 _current_data->cur_language = _current_data->compiled_strings[0];
00382 }