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 "../tar_type.h"
00017 #include "../script/squirrel_class.hpp"
00018 #include "../strings_func.h"
00019 #include "game_text.hpp"
00020 #include "game.hpp"
00021 #include "game_info.hpp"
00022
00023 #include "table/strings.h"
00024
00025 #include <stdarg.h>
00026
00027 void CDECL strgen_warning(const char *s, ...)
00028 {
00029 char buf[1024];
00030 va_list va;
00031 va_start(va, s);
00032 vsnprintf(buf, lengthof(buf), s, va);
00033 va_end(va);
00034 DEBUG(script, 0, "%s:%d: warning: %s", _file, _cur_line, buf);
00035 _warnings++;
00036 }
00037
00038 void CDECL strgen_error(const char *s, ...)
00039 {
00040 char buf[1024];
00041 va_list va;
00042 va_start(va, s);
00043 vsnprintf(buf, lengthof(buf), s, va);
00044 va_end(va);
00045 DEBUG(script, 0, "%s:%d: error: %s", _file, _cur_line, buf);
00046 _errors++;
00047 }
00048
00049 void NORETURN CDECL strgen_fatal(const char *s, ...)
00050 {
00051 char buf[1024];
00052 va_list va;
00053 va_start(va, s);
00054 vsnprintf(buf, lengthof(buf), s, va);
00055 va_end(va);
00056 DEBUG(script, 0, "%s:%d: FATAL: %s", _file, _cur_line, buf);
00057 throw std::exception();
00058 }
00059
00065 LanguageStrings::LanguageStrings(const char *language, const char *end)
00066 {
00067 this->language = end == NULL ? strdup(language) : strndup(language, end - language);
00068 }
00069
00071 LanguageStrings::~LanguageStrings()
00072 {
00073 free(this->language);
00074 }
00075
00081 LanguageStrings *ReadRawLanguageStrings(const char *file)
00082 {
00083 LanguageStrings *ret = NULL;
00084 FILE *fh = NULL;
00085 try {
00086 size_t to_read;
00087 fh = FioFOpenFile(file, "rb", GAME_DIR, &to_read);
00088 if (fh == NULL) {
00089 return NULL;
00090 }
00091
00092 const char *langname = strrchr(file, PATHSEPCHAR);
00093 if (langname == NULL) {
00094 langname = file;
00095 } else {
00096 langname++;
00097 }
00098
00099
00100 if (*langname == '.' || *langname == 0) {
00101 fclose(fh);
00102 return NULL;
00103 }
00104
00105 ret = new LanguageStrings(langname, strchr(langname, '.'));
00106
00107 char buffer[2048];
00108 while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != NULL) {
00109 size_t len = strlen(buffer);
00110
00111
00112 size_t i = len;
00113 while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--;
00114 buffer[i] = '\0';
00115
00116 *ret->lines.Append() = strndup(buffer, to_read);
00117
00118 if (len > to_read) {
00119 to_read = 0;
00120 } else {
00121 to_read -= len;
00122 }
00123 }
00124
00125 fclose(fh);
00126 return ret;
00127 } catch (...) {
00128 if (fh != NULL) fclose(fh);
00129 delete ret;
00130 return NULL;
00131 }
00132 }
00133
00134
00136 struct StringListReader : StringReader {
00137 const char * const *p;
00138 const char * const *end;
00139
00147 StringListReader(StringData &data, const LanguageStrings *strings, bool master, bool translation) :
00148 StringReader(data, strings->language, master, translation), p(strings->lines.Begin()), end(strings->lines.End())
00149 {
00150 }
00151
00152 char *ReadLine(char *buffer, size_t size)
00153 {
00154 if (this->p == this->end) return NULL;
00155
00156 strncpy(buffer, *this->p, size);
00157 this->p++;
00158
00159 return buffer;
00160 }
00161 };
00162
00164 struct TranslationWriter : LanguageWriter {
00165 StringList *strings;
00166
00171 TranslationWriter(StringList *strings) : strings(strings)
00172 {
00173 }
00174
00175 void WriteHeader(const LanguagePackHeader *header)
00176 {
00177
00178 }
00179
00180 void Finalise()
00181 {
00182
00183 }
00184
00185 void WriteLength(uint length)
00186 {
00187
00188 }
00189
00190 void Write(const byte *buffer, size_t length)
00191 {
00192 char *dest = MallocT<char>(length + 1);
00193 memcpy(dest, buffer, length);
00194 dest[length] = '\0';
00195 *this->strings->Append() = dest;
00196 }
00197 };
00198
00200 struct StringNameWriter : HeaderWriter {
00201 StringList *strings;
00202
00207 StringNameWriter(StringList *strings) : strings(strings)
00208 {
00209 }
00210
00211 void WriteStringID(const char *name, int stringid)
00212 {
00213 if (stringid == (int)this->strings->Length()) *this->strings->Append() = strdup(name);
00214 }
00215
00216 void Finalise(const StringData &data)
00217 {
00218
00219 }
00220 };
00221
00225 class LanguageScanner : protected FileScanner {
00226 private:
00227 GameStrings *gs;
00228 char *exclude;
00229
00230 public:
00232 LanguageScanner(GameStrings *gs, const char *exclude) : gs(gs), exclude(strdup(exclude)) {}
00233 ~LanguageScanner() { free(exclude); }
00234
00238 void Scan(const char *directory)
00239 {
00240 this->FileScanner::Scan(".txt", directory, false);
00241 }
00242
00243 bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00244 {
00245 if (strcmp(filename, exclude) == 0) return true;
00246
00247 *gs->raw_strings.Append() = ReadRawLanguageStrings(filename);
00248 return true;
00249 }
00250 };
00251
00256 GameStrings *LoadTranslations()
00257 {
00258 const GameInfo *info = Game::GetInfo();
00259 char filename[512];
00260 strecpy(filename, info->GetMainScript(), lastof(filename));
00261 char *e = strrchr(filename, PATHSEPCHAR);
00262 if (e == NULL) return NULL;
00263 e++;
00264
00265 strecpy(e, "lang" PATHSEP "english.txt", lastof(filename));
00266 if (!FioCheckFileExists(filename, GAME_DIR)) return NULL;
00267
00268 GameStrings *gs = new GameStrings();
00269 try {
00270 *gs->raw_strings.Append() = ReadRawLanguageStrings(filename);
00271
00272
00273 LanguageScanner scanner(gs, filename);
00274 strecpy(e, "lang" PATHSEP, lastof(filename));
00275 size_t len = strlen(filename);
00276
00277 const char *tar_filename = info->GetTarFile();
00278 TarList::iterator iter;
00279 if (tar_filename != NULL && (iter = _tar_list[GAME_DIR].find(tar_filename)) != _tar_list[GAME_DIR].end()) {
00280
00281
00282 TarFileList::iterator tar;
00283 FOR_ALL_TARS(tar, GAME_DIR) {
00284
00285 if (tar->second.tar_filename != iter->first) continue;
00286
00287
00288 if (tar->first.size() <= len || tar->first.compare(0, len, filename) != 0) continue;
00289 if (tar->first.compare(tar->first.size() - 4, 4, ".txt") != 0) continue;
00290
00291 scanner.AddFile(tar->first.c_str(), 0, tar_filename);
00292 }
00293 } else {
00294
00295 scanner.Scan(filename);
00296 }
00297
00298 gs->Compile();
00299 return gs;
00300 } catch (...) {
00301 delete gs;
00302 return NULL;
00303 }
00304 }
00305
00307 void GameStrings::Compile()
00308 {
00309 StringData data(1);
00310 StringListReader master_reader(data, this->raw_strings[0], true, false);
00311 master_reader.ParseFile();
00312 if (_errors != 0) throw std::exception();
00313
00314 this->version = data.Version();
00315
00316 StringNameWriter id_writer(&this->string_names);
00317 id_writer.WriteHeader(data);
00318
00319 for (LanguageStrings **p = this->raw_strings.Begin(); p != this->raw_strings.End(); p++) {
00320 data.FreeTranslation();
00321 StringListReader translation_reader(data, *p, false, strcmp((*p)->language, "english") != 0);
00322 translation_reader.ParseFile();
00323 if (_errors != 0) throw std::exception();
00324
00325 LanguageStrings *compiled = *this->compiled_strings.Append() = new LanguageStrings((*p)->language);
00326 TranslationWriter writer(&compiled->lines);
00327 writer.WriteLang(data);
00328 }
00329 }
00330
00332 GameStrings *_current_data = NULL;
00333
00339 const char *GetGameStringPtr(uint id)
00340 {
00341 if (id >= _current_data->cur_language->lines.Length()) return GetStringPtr(STR_UNDEFINED);
00342 return _current_data->cur_language->lines[id];
00343 }
00344
00349 void RegisterGameTranslation(Squirrel *engine)
00350 {
00351 delete _current_data;
00352 _current_data = LoadTranslations();
00353 if (_current_data == NULL) return;
00354
00355 HSQUIRRELVM vm = engine->GetVM();
00356 sq_pushroottable(vm);
00357 sq_pushstring(vm, _SC("GSText"), -1);
00358 if (SQ_FAILED(sq_get(vm, -2))) return;
00359
00360 int idx = 0;
00361 for (const char * const *p = _current_data->string_names.Begin(); p != _current_data->string_names.End(); p++, idx++) {
00362 sq_pushstring(vm, OTTD2SQ(*p), -1);
00363 sq_pushinteger(vm, idx);
00364 sq_rawset(vm, -3);
00365 }
00366
00367 sq_pop(vm, 2);
00368
00369 ReconsiderGameScriptLanguage();
00370 }
00371
00375 void ReconsiderGameScriptLanguage()
00376 {
00377 if (_current_data == NULL) return;
00378
00379 char temp[MAX_PATH];
00380 strecpy(temp, _current_language->file, lastof(temp));
00381
00382
00383 char *l = strrchr(temp, '.');
00384 assert(l != NULL);
00385 *l = '\0';
00386
00387
00388 char *language = strrchr(temp, PATHSEPCHAR);
00389 assert(language != NULL);
00390 language++;
00391
00392 for (LanguageStrings **p = _current_data->compiled_strings.Begin(); p != _current_data->compiled_strings.End(); p++) {
00393 if (strcmp((*p)->language, language) == 0) {
00394 _current_data->cur_language = *p;
00395 return;
00396 }
00397 }
00398
00399 _current_data->cur_language = _current_data->compiled_strings[0];
00400 }