OpenTTD Source  12.0-beta2
game_text.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD 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, version 2.
4  * OpenTTD 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.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "../stdafx.h"
11 #include "../strgen/strgen.h"
12 #include "../debug.h"
13 #include "../fileio_func.h"
14 #include "../tar_type.h"
15 #include "../script/squirrel_class.hpp"
16 #include "../strings_func.h"
17 #include "game_text.hpp"
18 #include "game.hpp"
19 #include "game_info.hpp"
20 
21 #include "table/strings.h"
22 
23 #include <stdarg.h>
24 #include <memory>
25 
26 #include "../safeguards.h"
27 
28 void CDECL strgen_warning(const char *s, ...)
29 {
30  char buf[1024];
31  va_list va;
32  va_start(va, s);
33  vseprintf(buf, lastof(buf), s, va);
34  va_end(va);
35  Debug(script, 0, "{}:{}: warning: {}", _file, _cur_line, buf);
36  _warnings++;
37 }
38 
39 void CDECL strgen_error(const char *s, ...)
40 {
41  char buf[1024];
42  va_list va;
43  va_start(va, s);
44  vseprintf(buf, lastof(buf), s, va);
45  va_end(va);
46  Debug(script, 0, "{}:{}: error: {}", _file, _cur_line, buf);
47  _errors++;
48 }
49 
50 void NORETURN CDECL strgen_fatal(const char *s, ...)
51 {
52  char buf[1024];
53  va_list va;
54  va_start(va, s);
55  vseprintf(buf, lastof(buf), s, va);
56  va_end(va);
57  Debug(script, 0, "{}:{}: FATAL: {}", _file, _cur_line, buf);
58  throw std::exception();
59 }
60 
66 LanguageStrings ReadRawLanguageStrings(const std::string &file)
67 {
68  size_t to_read;
69  FILE *fh = FioFOpenFile(file, "rb", GAME_DIR, &to_read);
70  if (fh == nullptr) return LanguageStrings();
71 
72  FileCloser fhClose(fh);
73 
74  auto pos = file.rfind(PATHSEPCHAR);
75  if (pos == std::string::npos) return LanguageStrings();
76  std::string langname = file.substr(pos + 1);
77 
78  /* Check for invalid empty filename */
79  if (langname.empty() || langname.front() == '.') return LanguageStrings();
80 
81  LanguageStrings ret(langname.substr(0, langname.find('.')));
82 
83  char buffer[2048];
84  while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != nullptr) {
85  size_t len = strlen(buffer);
86 
87  /* Remove trailing spaces/newlines from the string. */
88  size_t i = len;
89  while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--;
90  buffer[i] = '\0';
91 
92  ret.lines.emplace_back(buffer, i);
93 
94  if (len > to_read) {
95  to_read = 0;
96  } else {
97  to_read -= len;
98  }
99  }
100 
101  return ret;
102 }
103 
104 
107  StringList::const_iterator p;
108  StringList::const_iterator end;
109 
118  StringReader(data, strings.language.c_str(), master, translation), p(strings.lines.begin()), end(strings.lines.end())
119  {
120  }
121 
122  char *ReadLine(char *buffer, const char *last) override
123  {
124  if (this->p == this->end) return nullptr;
125 
126  strecpy(buffer, this->p->c_str(), last);
127  this->p++;
128 
129  return buffer;
130  }
131 };
132 
136 
142  {
143  }
144 
145  void WriteHeader(const LanguagePackHeader *header)
146  {
147  /* We don't use the header. */
148  }
149 
150  void Finalise()
151  {
152  /* Nothing to do. */
153  }
154 
155  void WriteLength(uint length)
156  {
157  /* We don't write the length. */
158  }
159 
160  void Write(const byte *buffer, size_t length)
161  {
162  this->strings.emplace_back((const char *)buffer, length);
163  }
164 };
165 
169 
175  {
176  }
177 
178  void WriteStringID(const char *name, int stringid)
179  {
180  if (stringid == (int)this->strings.size()) this->strings.emplace_back(name);
181  }
182 
183  void Finalise(const StringData &data)
184  {
185  /* Nothing to do. */
186  }
187 };
188 
192 class LanguageScanner : protected FileScanner {
193 private:
194  GameStrings *gs;
195  std::string exclude;
196 
197 public:
199  LanguageScanner(GameStrings *gs, const std::string &exclude) : gs(gs), exclude(exclude) {}
200 
204  void Scan(const char *directory)
205  {
206  this->FileScanner::Scan(".txt", directory, false);
207  }
208 
209  bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override
210  {
211  if (exclude == filename) return true;
212 
213  auto ls = ReadRawLanguageStrings(filename);
214  if (!ls.IsValid()) return false;
215 
216  gs->raw_strings.push_back(std::move(ls));
217  return true;
218  }
219 };
220 
226 {
227  const GameInfo *info = Game::GetInfo();
228  std::string basename(info->GetMainScript());
229  auto e = basename.rfind(PATHSEPCHAR);
230  if (e == std::string::npos) return nullptr;
231  basename.erase(e + 1);
232 
233  std::string filename = basename + "lang" PATHSEP "english.txt";
234  if (!FioCheckFileExists(filename, GAME_DIR)) return nullptr;
235 
236  auto ls = ReadRawLanguageStrings(filename);
237  if (!ls.IsValid()) return nullptr;
238 
239  GameStrings *gs = new GameStrings();
240  try {
241  gs->raw_strings.push_back(std::move(ls));
242 
243  /* Scan for other language files */
244  LanguageScanner scanner(gs, filename);
245  std::string ldir = basename + "lang" PATHSEP;
246 
247  const std::string tar_filename = info->GetTarFile();
248  TarList::iterator iter;
249  if (!tar_filename.empty() && (iter = _tar_list[GAME_DIR].find(tar_filename)) != _tar_list[GAME_DIR].end()) {
250  /* The main script is in a tar file, so find all files that
251  * are in the same tar and add them to the langfile scanner. */
252  for (const auto &tar : _tar_filelist[GAME_DIR]) {
253  /* Not in the same tar. */
254  if (tar.second.tar_filename != iter->first) continue;
255 
256  /* Check the path and extension. */
257  if (tar.first.size() <= ldir.size() || tar.first.compare(0, ldir.size(), ldir) != 0) continue;
258  if (tar.first.compare(tar.first.size() - 4, 4, ".txt") != 0) continue;
259 
260  scanner.AddFile(tar.first, 0, tar_filename);
261  }
262  } else {
263  /* Scan filesystem */
264  scanner.Scan(ldir.c_str());
265  }
266 
267  gs->Compile();
268  return gs;
269  } catch (...) {
270  delete gs;
271  return nullptr;
272  }
273 }
274 
277 {
278  StringData data(32);
279  StringListReader master_reader(data, this->raw_strings[0], true, false);
280  master_reader.ParseFile();
281  if (_errors != 0) throw std::exception();
282 
283  this->version = data.Version();
284 
285  StringNameWriter id_writer(this->string_names);
286  id_writer.WriteHeader(data);
287 
288  for (const auto &p : this->raw_strings) {
289  data.FreeTranslation();
290  StringListReader translation_reader(data, p, false, p.language != "english");
291  translation_reader.ParseFile();
292  if (_errors != 0) throw std::exception();
293 
294  this->compiled_strings.emplace_back(p.language);
295  TranslationWriter writer(this->compiled_strings.back().lines);
296  writer.WriteLang(data);
297  }
298 }
299 
302 
308 const char *GetGameStringPtr(uint id)
309 {
310  if (id >= _current_data->cur_language->lines.size()) return GetStringPtr(STR_UNDEFINED);
311  return _current_data->cur_language->lines[id].c_str();
312 }
313 
319 {
320  delete _current_data;
322  if (_current_data == nullptr) return;
323 
324  HSQUIRRELVM vm = engine->GetVM();
325  sq_pushroottable(vm);
326  sq_pushstring(vm, "GSText", -1);
327  if (SQ_FAILED(sq_get(vm, -2))) return;
328 
329  int idx = 0;
330  for (const auto &p : _current_data->string_names) {
331  sq_pushstring(vm, p.c_str(), -1);
332  sq_pushinteger(vm, idx);
333  sq_rawset(vm, -3);
334  idx++;
335  }
336 
337  sq_pop(vm, 2);
338 
340 }
341 
346 {
347  if (_current_data == nullptr) return;
348 
349  char temp[MAX_PATH];
350  strecpy(temp, _current_language->file, lastof(temp));
351 
352  /* Remove the extension */
353  char *l = strrchr(temp, '.');
354  assert(l != nullptr);
355  *l = '\0';
356 
357  /* Skip the path */
358  char *language = strrchr(temp, PATHSEPCHAR);
359  assert(language != nullptr);
360  language++;
361 
362  for (auto &p : _current_data->compiled_strings) {
363  if (p.language == language) {
365  return;
366  }
367  }
368 
370 }
game.hpp
_file
const char * _file
The filename of the input, so we can refer to it in errors/warnings.
Definition: strgen_base.cpp:26
StringNameWriter::WriteStringID
void WriteStringID(const char *name, int stringid)
Write the string ID.
Definition: game_text.cpp:178
TranslationWriter::WriteHeader
void WriteHeader(const LanguagePackHeader *header)
Write the header metadata.
Definition: game_text.cpp:145
ScriptInfo::GetMainScript
const char * GetMainScript() const
Get the filename of the main.nut script.
Definition: script_info.hpp:90
ReconsiderGameScriptLanguage
void ReconsiderGameScriptLanguage()
Reconsider the game script language, so we use the right one.
Definition: game_text.cpp:345
GameStrings::version
uint version
The version of the language strings.
Definition: game_text.hpp:34
game_info.hpp
LanguageScanner::AddFile
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override
Add a file with the given filename.
Definition: game_text.cpp:209
TranslationWriter::Write
void Write(const byte *buffer, size_t length)
Write a number of bytes.
Definition: game_text.cpp:160
Game::GetInfo
static class GameInfo * GetInfo()
Get the current GameInfo.
Definition: game.hpp:80
FileScanner::Scan
uint Scan(const char *extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
Definition: fileio.cpp:1216
TranslationWriter
Class for writing an encoded language.
Definition: game_text.cpp:134
HeaderWriter
Base class for writing the header, i.e.
Definition: strgen.h:91
Squirrel::GetVM
HSQUIRRELVM GetVM()
Get the squirrel VM.
Definition: squirrel.hpp:80
LanguageScanner::LanguageScanner
LanguageScanner(GameStrings *gs, const std::string &exclude)
Initialise.
Definition: game_text.cpp:199
_cur_line
int _cur_line
The current line we're parsing in the input file.
Definition: strgen_base.cpp:27
ScriptInfo::GetTarFile
std::string GetTarFile() const
Get the filename of the tar the script is in.
Definition: script_info.hpp:95
LanguageWriter::WriteLang
virtual void WriteLang(const StringData &data)
Actually write the language.
Definition: strgen_base.cpp:959
StringNameWriter
Class for writing the string IDs.
Definition: game_text.cpp:167
StringReader::master
bool master
Are we reading the master file?
Definition: strgen.h:63
GameStrings::string_names
StringList string_names
The names of the compiled strings.
Definition: game_text.hpp:39
StringData::Version
uint Version() const
Make a hash of the file to get a unique "version number".
Definition: strgen_base.cpp:175
Squirrel
Definition: squirrel.hpp:23
FileCloser
Auto-close a file upon scope exit.
Definition: fileio_func.h:120
StringData
Information about the currently known strings.
Definition: strgen.h:41
StringListReader::p
StringList::const_iterator p
The current location of the iteration.
Definition: game_text.cpp:107
FioFOpenFile
FILE * FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:245
GetGameStringPtr
const char * GetGameStringPtr(uint id)
Get the string pointer of a particular game string.
Definition: game_text.cpp:308
StringListReader::ReadLine
char * ReadLine(char *buffer, const char *last) override
Read a single line from the source of strings.
Definition: game_text.cpp:122
TranslationWriter::Finalise
void Finalise()
Finalise writing the file.
Definition: game_text.cpp:150
LanguageScanner
Scanner to find language files in a GameScript directory.
Definition: game_text.cpp:192
RegisterGameTranslation
void RegisterGameTranslation(Squirrel *engine)
Register the current translation to the Squirrel engine.
Definition: game_text.cpp:318
StringList
std::vector< std::string > StringList
Type for a list of strings.
Definition: string_type.h:58
_current_language
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:46
GAME_DIR
@ GAME_DIR
Subdirectory for all game scripts.
Definition: fileio_type.h:121
StringData::FreeTranslation
void FreeTranslation()
Free all data related to the translation.
Definition: strgen_base.cpp:104
GameStrings::raw_strings
std::vector< LanguageStrings > raw_strings
The raw strings per language, first must be English/the master language!.
Definition: game_text.hpp:37
LanguageMetadata::file
char file[MAX_PATH]
Name of the file we read this data from.
Definition: language.h:94
vseprintf
int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap)
Safer implementation of vsnprintf; same as vsnprintf except:
Definition: string.cpp:61
StringListReader
A reader that simply reads using fopen.
Definition: game_text.cpp:106
LanguageStrings
Container for the raw (unencoded) language strings of a language.
Definition: game_text.hpp:20
FioCheckFileExists
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:108
game_text.hpp
StringReader::data
StringData & data
The data to fill during reading.
Definition: strgen.h:61
TranslationWriter::TranslationWriter
TranslationWriter(StringList &strings)
Writer for the encoded data.
Definition: game_text.cpp:141
GameInfo
All static information from an Game like name, version, etc.
Definition: game_info.hpp:16
TranslationWriter::WriteLength
void WriteLength(uint length)
Write the length as a simple gamma.
Definition: game_text.cpp:155
StringReader::translation
bool translation
Are we reading a translation, implies !master. However, the base translation will have this false.
Definition: strgen.h:64
StringReader::ParseFile
virtual void ParseFile()
Start parsing the file.
Definition: strgen_base.cpp:816
GameStrings::cur_language
LanguageStrings * cur_language
The current (compiled) language.
Definition: game_text.hpp:35
StringListReader::end
StringList::const_iterator end
The end of the iteration.
Definition: game_text.cpp:108
StringNameWriter::strings
StringList & strings
The string names.
Definition: game_text.cpp:168
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
StringNameWriter::Finalise
void Finalise(const StringData &data)
Finalise writing the file.
Definition: game_text.cpp:183
LanguageWriter
Base class for all language writers.
Definition: strgen.h:112
_current_data
GameStrings * _current_data
The currently loaded game strings.
Definition: game_text.cpp:301
FileScanner
Helper for scanning for files with a given name.
Definition: fileio_func.h:39
HeaderWriter::WriteHeader
void WriteHeader(const StringData &data)
Write the header information.
Definition: strgen_base.cpp:850
TranslationWriter::strings
StringList & strings
The encoded strings.
Definition: game_text.cpp:135
GameStrings::compiled_strings
std::vector< LanguageStrings > compiled_strings
The compiled strings per language, first must be English/the master language!.
Definition: game_text.hpp:38
StringNameWriter::StringNameWriter
StringNameWriter(StringList &strings)
Writer for the string names.
Definition: game_text.cpp:174
LanguageStrings::lines
StringList lines
The lines of the file to pass into the parser/encoder.
Definition: game_text.hpp:22
StringListReader::StringListReader
StringListReader(StringData &data, const LanguageStrings &strings, bool master, bool translation)
Create the reader.
Definition: game_text.cpp:117
GameStrings::Compile
void Compile()
Compile the language.
Definition: game_text.cpp:276
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
LanguageScanner::Scan
void Scan(const char *directory)
Scan.
Definition: game_text.cpp:204
LoadTranslations
GameStrings * LoadTranslations()
Load all translations that we know of.
Definition: game_text.cpp:225
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:394
StringReader
Helper for reading strings.
Definition: strgen.h:60
GameStrings
Container for all the game strings.
Definition: game_text.hpp:33
ReadRawLanguageStrings
LanguageStrings ReadRawLanguageStrings(const std::string &file)
Read all the raw language strings from the given file.
Definition: game_text.cpp:66
LanguagePackHeader
Header of a language file.
Definition: language.h:24