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
00064 LanguageStrings::LanguageStrings(const char *language)
00065 {
00066 const char *p = strrchr(language, PATHSEPCHAR);
00067 if (p == NULL) {
00068 p = language;
00069 } else {
00070 p++;
00071 }
00072
00073 const char *e = strchr(p, '.');
00074 this->language = e == NULL ? strdup(p) : strndup(p, e - p);
00075 }
00076
00078 LanguageStrings::~LanguageStrings()
00079 {
00080 free(this->language);
00081 }
00082
00088 LanguageStrings *ReadRawLanguageStrings(const char *file)
00089 {
00090 LanguageStrings *ret = NULL;
00091 try {
00092 size_t to_read;
00093 FILE *fh = FioFOpenFile(file, "rb", GAME_DIR, &to_read);
00094 if (fh == NULL) {
00095 return NULL;
00096 }
00097
00098 ret = new LanguageStrings(file);
00099
00100 char buffer[2048];
00101 while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != NULL) {
00102 size_t len = strlen(buffer);
00103
00104
00105 size_t i = len;
00106 while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--;
00107 buffer[i] = '\0';
00108
00109 *ret->lines.Append() = strndup(buffer, to_read);
00110
00111 if (len > to_read) {
00112 to_read = 0;
00113 } else {
00114 to_read -= len;
00115 }
00116 }
00117
00118 return ret;
00119 } catch (...) {
00120 delete ret;
00121 return NULL;
00122 }
00123 }
00124
00125
00127 struct StringListReader : StringReader {
00128 const char * const *p;
00129 const char * const *end;
00130
00138 StringListReader(StringData &data, const LanguageStrings *strings, bool master, bool translation) :
00139 StringReader(data, strings->language, master, translation), p(strings->lines.Begin()), end(strings->lines.End())
00140 {
00141 }
00142
00143 char *ReadLine(char *buffer, size_t size)
00144 {
00145 if (this->p == this->end) return NULL;
00146
00147 strncpy(buffer, *this->p, size);
00148 this->p++;
00149
00150 return buffer;
00151 }
00152 };
00153
00155 struct TranslationWriter : LanguageWriter {
00156 StringList *strings;
00157
00162 TranslationWriter(StringList *strings) : strings(strings)
00163 {
00164 }
00165
00166 void WriteHeader(const LanguagePackHeader *header)
00167 {
00168
00169 }
00170
00171 void Finalise()
00172 {
00173
00174 }
00175
00176 void WriteLength(uint length)
00177 {
00178
00179 }
00180
00181 void Write(const byte *buffer, size_t length)
00182 {
00183 char *dest = MallocT<char>(length + 1);
00184 memcpy(dest, buffer, length);
00185 dest[length] = '\0';
00186 *this->strings->Append() = dest;
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
00216 class LanguageScanner : protected FileScanner {
00217 private:
00218 GameStrings *gs;
00219 char *exclude;
00220
00221 public:
00223 LanguageScanner(GameStrings *gs, const char *exclude) : gs(gs), exclude(strdup(exclude)) {}
00224 ~LanguageScanner() { free(exclude); }
00225
00229 void Scan(const char *directory)
00230 {
00231 this->FileScanner::Scan(".txt", directory, false);
00232 }
00233
00234 bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00235 {
00236 if (strcmp(filename, exclude) == 0) return true;
00237
00238 *gs->raw_strings.Append() = ReadRawLanguageStrings(filename);
00239 return true;
00240 }
00241 };
00242
00247 GameStrings *LoadTranslations()
00248 {
00249 const GameInfo *info = Game::GetInfo();
00250 char filename[512];
00251 strecpy(filename, info->GetMainScript(), lastof(filename));
00252 char *e = strrchr(filename, PATHSEPCHAR);
00253 if (e == NULL) return NULL;
00254 e++;
00255
00256 strecpy(e, "lang" PATHSEP "english.txt", lastof(filename));
00257 if (!FioCheckFileExists(filename, GAME_DIR)) return NULL;
00258
00259 GameStrings *gs = new GameStrings();
00260 try {
00261 *gs->raw_strings.Append() = ReadRawLanguageStrings(filename);
00262
00263
00264 LanguageScanner scanner(gs, filename);
00265 strecpy(e, "lang" PATHSEP, lastof(filename));
00266 size_t len = strlen(filename);
00267
00268 const char *tar_filename = info->GetTarFile();
00269 TarList::iterator iter;
00270 if (tar_filename != NULL && (iter = _tar_list[GAME_DIR].find(tar_filename)) != _tar_list[GAME_DIR].end()) {
00271
00272
00273 TarFileList::iterator tar;
00274 FOR_ALL_TARS(tar, GAME_DIR) {
00275
00276 if (tar->second.tar_filename != iter->first) continue;
00277
00278
00279 if (tar->first.size() <= len || tar->first.compare(0, len, filename) != 0) continue;
00280 if (tar->first.compare(tar->first.size() - 4, 4, ".txt") != 0) continue;
00281
00282 scanner.AddFile(tar->first.c_str(), 0, tar_filename);
00283 }
00284 } else {
00285
00286 scanner.Scan(filename);
00287 }
00288
00289 gs->Compile();
00290 return gs;
00291 } catch (...) {
00292 delete gs;
00293 return NULL;
00294 }
00295 }
00296
00298 void GameStrings::Compile()
00299 {
00300 StringData data(1);
00301 StringListReader master_reader(data, this->raw_strings[0], true, false);
00302 master_reader.ParseFile();
00303 if (_errors != 0) throw std::exception();
00304
00305 this->version = data.Version();
00306
00307 StringNameWriter id_writer(&this->string_names);
00308 id_writer.WriteHeader(data);
00309
00310 for (LanguageStrings **p = this->raw_strings.Begin(); p != this->raw_strings.End(); p++) {
00311 data.FreeTranslation();
00312 StringListReader translation_reader(data, *p, false, strcmp((*p)->language, "english") != 0);
00313 translation_reader.ParseFile();
00314 if (_errors != 0) throw std::exception();
00315
00316 LanguageStrings *compiled = *this->compiled_strings.Append() = new LanguageStrings((*p)->language);
00317 TranslationWriter writer(&compiled->lines);
00318 writer.WriteLang(data);
00319 }
00320 }
00321
00323 GameStrings *_current_data = NULL;
00324
00330 const char *GetGameStringPtr(uint id)
00331 {
00332 if (id >= _current_data->cur_language->lines.Length()) return GetStringPtr(STR_UNDEFINED);
00333 return _current_data->cur_language->lines[id];
00334 }
00335
00340 void RegisterGameTranslation(Squirrel *engine)
00341 {
00342 delete _current_data;
00343 _current_data = LoadTranslations();
00344 if (_current_data == NULL) return;
00345
00346 HSQUIRRELVM vm = engine->GetVM();
00347 sq_pushroottable(vm);
00348 sq_pushstring(vm, _SC("GSText"), -1);
00349 if (SQ_FAILED(sq_get(vm, -2))) return;
00350
00351 int idx = 0;
00352 for (const char * const *p = _current_data->string_names.Begin(); p != _current_data->string_names.End(); p++, idx++) {
00353 sq_pushstring(vm, OTTD2SQ(*p), -1);
00354 sq_pushinteger(vm, idx);
00355 sq_rawset(vm, -3);
00356 }
00357
00358 sq_pop(vm, 2);
00359
00360 ReconsiderGameScriptLanguage();
00361 }
00362
00366 void ReconsiderGameScriptLanguage()
00367 {
00368 if (_current_data == NULL) return;
00369
00370 char temp[MAX_PATH];
00371 strecpy(temp, _current_language->file, temp + sizeof(temp));
00372
00373
00374 char *l = strrchr(temp, '.');
00375 assert(l != NULL);
00376 *l = '\0';
00377
00378
00379 char *language = strrchr(temp, PATHSEPCHAR);
00380 assert(language != NULL);
00381 language++;
00382
00383 for (LanguageStrings **p = _current_data->compiled_strings.Begin(); p != _current_data->compiled_strings.End(); p++) {
00384 if (strcmp((*p)->language, language) == 0) {
00385 _current_data->cur_language = *p;
00386 return;
00387 }
00388 }
00389
00390 _current_data->cur_language = _current_data->compiled_strings[0];
00391 }