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);