12 #include "../stdafx.h"
13 #include "../core/endian_func.hpp"
14 #include "../string_func.h"
15 #include "../strings_type.h"
16 #include "../misc/getoptdata.h"
17 #include "../table/control_codes.h"
24 #if (!defined(WIN32) && !defined(WIN64)) || defined(__CYGWIN__)
29 #if defined WIN32 || defined __WATCOMC__
40 #include "../table/strgen_tables.h"
42 #include "../safeguards.h"
46 # define LINE_NUM_FMT(s) "%s (%d): warning: %s (" s ")\n"
48 # define LINE_NUM_FMT(s) "%s:%d: " s ": %s\n"
51 void CDECL strgen_warning(
const char *s, ...)
62 void CDECL strgen_error(
const char *s, ...)
73 void NORETURN CDECL strgen_fatal(
const char *s, ...)
82 fprintf(stderr, LINE_NUM_FMT(
"warning"),
_file,
_cur_line,
"language is not compiled");
84 throw std::exception();
87 void NORETURN CDECL
error(
const char *s, ...)
96 fprintf(stderr, LINE_NUM_FMT(
"warning"),
_file,
_cur_line,
"language is not compiled");
115 this->
fh = fopen(file,
"rb");
116 if (this->
fh == NULL)
error(
"Could not open %s", file);
127 return fgets(buffer,
ClampToU16(last - buffer + 1), this->
fh);
137 error(
"Language must include ##name, ##ownname and ##isocode");
144 if (!memcmp(str,
"id ", 3)) {
146 }
else if (!memcmp(str,
"name ", 5)) {
148 }
else if (!memcmp(str,
"ownname ", 8)) {
150 }
else if (!memcmp(str,
"isocode ", 8)) {
152 }
else if (!memcmp(str,
"textdir ", 8)) {
153 if (!memcmp(str + 8,
"ltr", 3)) {
155 }
else if (!memcmp(str + 8,
"rtl", 3)) {
158 error(
"Invalid textdir %s", str + 8);
160 }
else if (!memcmp(str,
"digitsep ", 9)) {
163 }
else if (!memcmp(str,
"digitsepcur ", 12)) {
166 }
else if (!memcmp(str,
"decimalsep ", 11)) {
169 }
else if (!memcmp(str,
"winlangid ", 10)) {
170 const char *buf = str + 10;
171 long langid = strtol(buf, NULL, 16);
172 if (langid > (
long)UINT16_MAX || langid < 0) {
173 error(
"Invalid winlangid %s", buf);
176 }
else if (!memcmp(str,
"grflangid ", 10)) {
177 const char *buf = str + 10;
178 long langid = strtol(buf, NULL, 16);
179 if (langid >= 0x7F || langid < 0) {
180 error(
"Invalid grflangid %s", buf);
183 }
else if (!memcmp(str,
"gender ", 7)) {
184 if (this->
master)
error(
"Genders are not allowed in the base translation.");
188 const char *s = ParseWord(&buf);
190 if (s == NULL)
break;
195 }
else if (!memcmp(str,
"case ", 5)) {
196 if (this->
master)
error(
"Cases are not allowed in the base translation.");
200 const char *s = ParseWord(&buf);
202 if (s == NULL)
break;
214 FILE *f2 = fopen(n2,
"rb");
215 if (f2 == NULL)
return false;
217 FILE *f1 = fopen(n1,
"rb");
218 if (f1 == NULL)
error(
"can't open %s", n1);
224 l1 = fread(b1, 1,
sizeof(b1), f1);
225 l2 = fread(b2, 1,
sizeof(b2), f2);
227 if (l1 != l2 || memcmp(b1, b2, l1)) {
250 this->filename =
stredup(filename);
251 this->
fh = fopen(this->filename,
"wb");
253 if (this->
fh == NULL) {
254 error(
"Could not open %s", this->filename);
290 fprintf(this->
fh,
"/* This file is automatically generated. Do not modify */\n\n");
291 fprintf(this->
fh,
"#ifndef TABLE_STRINGS_H\n");
292 fprintf(this->
fh,
"#define TABLE_STRINGS_H\n");
303 if (
prev + 1 != stringid) fprintf(this->
fh,
"\n");
304 fprintf(this->
fh,
"static const StringID %s = 0x%X;\n", name, stringid);
311 int max_plural_forms = 0;
318 "static const uint LANGUAGE_PACK_VERSION = 0x%X;\n"
319 "static const uint LANGUAGE_MAX_PLURAL = %d;\n"
320 "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n\n",
324 fprintf(this->
fh,
"#endif /* TABLE_STRINGS_H */\n");
333 #if defined(WIN32) || defined(WIN64)
353 this->
Write((
const byte *)header,
sizeof(*header));
358 if (fputc(0, this->
fh) == EOF) {
364 void Write(
const byte *buffer,
size_t length)
366 if (fwrite(buffer,
sizeof(*buffer), length, this->
fh) != length) {
377 #if defined(WIN32) || defined(__WATCOMC__)
380 mkdir(directory, 0755);
389 static inline char *
mkpath(
char *buf,
const char *last,
const char *path,
const char *file)
393 char *p = strchr(buf,
'\0');
394 if (p[-1] != PATHSEPCHAR && p != last) *p++ = PATHSEPCHAR;
399 #if defined(__MINGW32__)
405 static inline char *replace_pathsep(
char *s)
407 for (
char *c = s; *c !=
'\0'; c++)
if (*c ==
'/') *c =
'\\';
411 static inline char *replace_pathsep(
char *s) {
return s; }
429 int CDECL
main(
int argc,
char *argv[])
431 char pathbuf[MAX_PATH];
432 const char *src_dir =
".";
433 const char *dest_dir = NULL;
437 int i = mgo.GetOpt();
442 puts(
"$Revision: 26521 $");
446 printf(
"args\tflags\tcommand\treplacement\n");
447 for (
const CmdStruct *cs = _cmd_structs; cs <
endof(_cmd_structs); cs++) {
449 if (cs->proc == EmitGender) {
451 }
else if (cs->proc == EmitPlural) {
458 printf(
"%i\t%c\t\"%s\"\t\"%s\"\n", cs->consumes, flags, cs->cmd, strstr(cs->cmd,
"STRING") ?
"STRING" : cs->cmd);
463 printf(
"count\tdescription\tnames\n");
465 printf(
"%i\t\"%s\"\t%s\n", pf->plural_count, pf->description, pf->names);
470 printf(
"name\tflags\tdefault\tdescription\n");
472 printf(
"\"%s\"\t%s\t\"%s\"\t\"%s\"\n",
487 "strgen - $Revision: 26521 $\n"
488 " -v | --version print version information and exit\n"
489 " -t | --todo replace any untranslated strings with '<TODO>'\n"
490 " -w | --warning print a warning for any untranslated strings\n"
491 " -h | -? | --help print this help message and exit\n"
492 " -s | --source_dir search for english.txt in the specified directory\n"
493 " -d | --dest_dir put output file in the specified directory, create if needed\n"
494 " -export-commands export all commands and exit\n"
495 " -export-plurals export all plural forms and exit\n"
496 " -export-pragmas export all pragmas and exit\n"
497 " Run without parameters and strgen will search for english.txt and parse it,\n"
498 " creating strings.h. Passing an argument, strgen will translate that language\n"
499 " file using english.txt as a reference and output <language>.lng."
504 src_dir = replace_pathsep(mgo.opt);
508 dest_dir = replace_pathsep(mgo.opt);
512 fprintf(stderr,
"Invalid arguments\n");
517 if (dest_dir == NULL) dest_dir = src_dir;
524 if (mgo.numleft == 0) {
525 mkpath(pathbuf,
lastof(pathbuf), src_dir,
"english.txt");
530 master_reader.ParseFile();
531 if (_errors != 0)
return 1;
535 mkpath(pathbuf,
lastof(pathbuf), dest_dir,
"strings.h");
538 writer.WriteHeader(data);
539 writer.Finalise(data);
540 }
else if (mgo.numleft >= 1) {
543 mkpath(pathbuf,
lastof(pathbuf), src_dir,
"english.txt");
548 master_reader.ParseFile();
550 for (
int i = 0; i < mgo.numleft; i++) {
551 data.FreeTranslation();
553 const char *translation = replace_pathsep(mgo.argv[i]);
554 const char *file = strrchr(translation, PATHSEPCHAR);
555 FileStringReader translation_reader(data, translation,
false, file == NULL || strcmp(file + 1,
"english.txt") != 0);
556 translation_reader.ParseFile();
557 if (_errors != 0)
return 1;
560 r = strrchr(mgo.argv[i], PATHSEPCHAR);
561 mkpath(pathbuf,
lastof(pathbuf), dest_dir, (r != NULL) ? &r[1] : mgo.argv[i]);
564 r = strrchr(pathbuf,
'.');
565 if (r == NULL || strcmp(r,
".txt") != 0) r = strchr(pathbuf,
'\0');
569 writer.WriteLang(data);
573 if ((_show_todo & 2) != 0) {
574 fprintf(stdout,
"%d warnings and %d errors for %s\n", _warnings, _errors, pathbuf);